From b7cc05ba8564ca22a17fef25f1fbd7338e09ab9f Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Tue, 24 Oct 2023 18:54:57 -0400 Subject: [PATCH 001/271] (test): Add Functions test harnesses & additional foundry unit tests (#11011) * (test): Add Functions test harnesses & additional unit tests * (test): Add transmitter balance helpers * Update gas snapshot from helper functions --- contracts/foundry.toml | 6 +- .../gas-snapshots/functions.gas-snapshot | 318 +++++++++--------- .../tests/v1_X/FunctionsBilling.t.sol | 242 ++++++++++--- .../tests/v1_X/FunctionsClient.t.sol | 44 ++- .../tests/v1_X/FunctionsCoordinator.t.sol | 216 ++++++++++-- .../tests/v1_X/FunctionsRouter.t.sol | 4 +- .../tests/v1_X/FunctionsSubscriptions.t.sol | 60 +--- .../src/v0.8/functions/tests/v1_X/README.md | 15 +- .../src/v0.8/functions/tests/v1_X/Setup.t.sol | 49 ++- .../testhelpers/FunctionsClientHarness.sol | 24 ++ .../testhelpers/FunctionsClientTestHelper.sol | 2 +- .../FunctionsClientWithEmptyCallback.sol | 2 +- .../FunctionsCoordinatorHarness.sol | 119 +++++++ .../FunctionsCoordinatorTestHelper.sol | 2 +- .../testhelpers/FunctionsRouterHarness.sol | 30 ++ .../FunctionsSubscriptionsHarness.sol | 54 +++ .../v1_X/testhelpers/FunctionsTestHelper.sol | 2 +- 17 files changed, 884 insertions(+), 305 deletions(-) create mode 100644 contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol create mode 100644 contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol create mode 100644 contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsRouterHarness.sol create mode 100644 contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsSubscriptionsHarness.sol diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 1228ce3ec03..cf27c0f2a8b 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -18,9 +18,9 @@ block_number = 12345 [profile.functions] solc_version = '0.8.19' -src = 'src/v0.8/functions' -test = 'src/v0.8/functions/tests' -gas_price = 3000000000 +src = 'src/v0.8/functions/dev/v1_X' +test = 'src/v0.8/functions/tests/v1_X' +gas_price = 3_000_000_000 # 3 gwei [profile.vrf] optimizer_runs = 1000 diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index b6298350604..d575c8ca196 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,177 +1,195 @@ -FunctionsBilling_EstimateCost:test_EstimateCost_RevertsIfGasPriceAboveCeiling() (gas: 32391) -FunctionsBilling_EstimateCost:test_EstimateCost_Success() (gas: 53182) -FunctionsBilling_EstimateCost:test_EstimateCost_SuccessLowGasPrice() (gas: 53285) +FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) +FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) +FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) +FunctionsBilling_EstimateCost:test_EstimateCost_RevertsIfGasPriceAboveCeiling() (gas: 32458) +FunctionsBilling_EstimateCost:test_EstimateCost_Success() (gas: 53227) +FunctionsBilling_EstimateCost:test_EstimateCost_SuccessLowGasPrice() (gas: 53330) +FunctionsBilling_GetAdminFee:test_GetAdminFee_Success() (gas: 18226) FunctionsBilling_GetConfig:test_GetConfig_Success() (gas: 23671) +FunctionsBilling_GetDONFee:test_GetDONFee_Success() (gas: 15792) +FunctionsBilling_GetWeiPerUnitLink:test_GetWeiPerUnitLink_Success() (gas: 31773) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_RevertIfInsufficientBalance() (gas: 70138) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_RevertWithNoBalance() (gas: 106295) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_SuccessTransmitterWithBalanceNoAmountGiven() (gas: 140174) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_SuccessTransmitterWithBalanceValidAmountGiven() (gas: 142502) FunctionsBilling_OracleWithdrawAll:test_OracleWithdrawAll_RevertIfNotOwner() (gas: 13296) -FunctionsBilling_OracleWithdrawAll:test_OracleWithdrawAll_SuccessPaysTransmittersWithBalance() (gas: 146657) -FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 498113) -FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 199261) -FunctionsClient__SendRequest:test__SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 54991) -FunctionsCoordinator_Constructor:test_Constructor_Success() (gas: 11933) -FunctionsOracle_sendRequest:testEmptyRequestDataReverts() (gas: 13452) -FunctionsOracle_setDONPublicKey:testEmptyPublicKeyReverts() (gas: 10974) -FunctionsOracle_setDONPublicKey:testOnlyOwnerReverts() (gas: 11255) -FunctionsOracle_setDONPublicKey:testSetDONPublicKeySuccess() (gas: 126453) -FunctionsOracle_setDONPublicKey:testSetDONPublicKey_gas() (gas: 97580) -FunctionsOracle_setRegistry:testEmptyPublicKeyReverts() (gas: 10635) -FunctionsOracle_setRegistry:testOnlyOwnerReverts() (gas: 10927) -FunctionsOracle_setRegistry:testSetRegistrySuccess() (gas: 35791) -FunctionsOracle_setRegistry:testSetRegistry_gas() (gas: 31987) -FunctionsOracle_typeAndVersion:testTypeAndVersionSuccess() (gas: 6905) +FunctionsBilling_OracleWithdrawAll:test_OracleWithdrawAll_SuccessPaysTransmittersWithBalance() (gas: 147278) +FunctionsBilling_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 18974) +FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 38251) +FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8801) +FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) +FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 498114) +FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 199285) +FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_RevertIfNotRouter() (gas: 14623) +FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_Success() (gas: 22923) +FunctionsClient__SendRequest:test__SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 55059) +FunctionsCoordinator_Constructor:test_Constructor_Success() (gas: 11984) +FunctionsCoordinator_GetDONPublicKey:test_GetDONPublicKey_RevertIfEmpty() (gas: 15334) +FunctionsCoordinator_GetDONPublicKey:test_GetDONPublicKey_Success() (gas: 106496) +FunctionsCoordinator_GetThresholdPublicKey:test_GetThresholdPublicKey_RevertIfEmpty() (gas: 15313) +FunctionsCoordinator_GetThresholdPublicKey:test_GetThresholdPublicKey_Success() (gas: 656556) +FunctionsCoordinator_SetDONPublicKey:test_SetDONPublicKey_RevertNotOwner() (gas: 20364) +FunctionsCoordinator_SetDONPublicKey:test_SetDONPublicKey_Success() (gas: 101275) +FunctionsCoordinator_SetThresholdPublicKey:test_SetThresholdPublicKey_RevertNotOwner() (gas: 13892) +FunctionsCoordinator_SetThresholdPublicKey:test_SetThresholdPublicKey_Success() (gas: 651248) +FunctionsCoordinator_StartRequest:test_StartRequest_RevertIfNotRouter() (gas: 22703) +FunctionsCoordinator_StartRequest:test_StartRequest_Success() (gas: 107681) +FunctionsCoordinator__IsTransmitter:test__IsTransmitter_SuccessFound() (gas: 18957) +FunctionsCoordinator__IsTransmitter:test__IsTransmitter_SuccessNotFound() (gas: 19690) FunctionsRequest_DEFAULT_BUFFER_SIZE:test_DEFAULT_BUFFER_SIZE() (gas: 246) FunctionsRequest_EncodeCBOR:test_EncodeCBOR_Success() (gas: 223) FunctionsRequest_REQUEST_DATA_VERSION:test_REQUEST_DATA_VERSION() (gas: 225) -FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12073) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 169899) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 160226) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38092) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35224) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 178394) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28063) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 153900) -FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 296711) -FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 310303) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2484943) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 515428) -FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 18005) -FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12926) -FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37136) -FunctionsRouter_GetContractById:test_GetContractById_RevertIfRouteDoesNotExist() (gas: 13871) -FunctionsRouter_GetContractById:test_GetContractById_SuccessIfRouteExists() (gas: 17395) -FunctionsRouter_GetProposedContractById:test_GetProposedContractById_RevertIfRouteDoesNotExist() (gas: 16382) -FunctionsRouter_GetProposedContractById:test_GetProposedContractById_SuccessIfRouteExists() (gas: 23934) -FunctionsRouter_GetProposedContractSet:test_GetProposedContractSet_Success() (gas: 25958) -FunctionsRouter_IsValidCallbackGasLimit:test_IsValidCallbackGasLimit_RevertGasLimitTooBig() (gas: 28034) -FunctionsRouter_IsValidCallbackGasLimit:test_IsValidCallbackGasLimit_RevertInvalidConfig() (gas: 41004) -FunctionsRouter_IsValidCallbackGasLimit:test_IsValidCallbackGasLimit_Success() (gas: 24551) -FunctionsRouter_Pause:test_Pause_RevertIfNotOwner() (gas: 13315) -FunctionsRouter_Pause:test_Pause_Success() (gas: 20298) -FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfEmptyAddress() (gas: 14768) -FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfExceedsMaxProposal() (gas: 21670) -FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfLengthMismatch() (gas: 14647) -FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotNewContract() (gas: 19025) -FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotOwner() (gas: 23369) -FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_Success() (gas: 118456) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfConsumerNotAllowed() (gas: 59304) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfDuplicateRequestId() (gas: 192796) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfEmptyData() (gas: 29405) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfIncorrectDonId() (gas: 57926) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 186209) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 50902) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidDonId() (gas: 25061) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfNoSubscription() (gas: 29111) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfPaused() (gas: 34247) -FunctionsRouter_SendRequest:test_SendRequest_Success() (gas: 284999) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfConsumerNotAllowed() (gas: 65800) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfEmptyData() (gas: 35991) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfIncorrectDonId() (gas: 29897) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalidCallbackGasLimit() (gas: 57488) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalidDonId() (gas: 27482) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfNoSubscription() (gas: 35696) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfPaused() (gas: 40766) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_Success() (gas: 291551) -FunctionsRouter_SendRequestToProposed:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 192728) -FunctionsRouter_SetAllowListId:test_SetAllowListId_Success() (gas: 30687) -FunctionsRouter_SetAllowListId:test_UpdateConfig_RevertIfNotOwner() (gas: 13380) -FunctionsRouter_Unpause:test_Unpause_RevertIfNotOwner() (gas: 13337) -FunctionsRouter_Unpause:test_Unpause_Success() (gas: 77421) +FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12007) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 169900) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 160227) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38115) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35238) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 178373) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28086) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 153924) +FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 296712) +FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 310327) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2484946) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 515433) +FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) +FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) +FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37159) +FunctionsRouter_GetContractById:test_GetContractById_RevertIfRouteDoesNotExist() (gas: 13849) +FunctionsRouter_GetContractById:test_GetContractById_SuccessIfRouteExists() (gas: 17373) +FunctionsRouter_GetProposedContractById:test_GetProposedContractById_RevertIfRouteDoesNotExist() (gas: 16383) +FunctionsRouter_GetProposedContractById:test_GetProposedContractById_SuccessIfRouteExists() (gas: 23935) +FunctionsRouter_GetProposedContractSet:test_GetProposedContractSet_Success() (gas: 25936) +FunctionsRouter_IsValidCallbackGasLimit:test_IsValidCallbackGasLimit_RevertGasLimitTooBig() (gas: 28103) +FunctionsRouter_IsValidCallbackGasLimit:test_IsValidCallbackGasLimit_RevertInvalidConfig() (gas: 41093) +FunctionsRouter_IsValidCallbackGasLimit:test_IsValidCallbackGasLimit_Success() (gas: 24626) +FunctionsRouter_Pause:test_Pause_RevertIfNotOwner() (gas: 13338) +FunctionsRouter_Pause:test_Pause_Success() (gas: 20344) +FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfEmptyAddress() (gas: 14791) +FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfExceedsMaxProposal() (gas: 21693) +FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfLengthMismatch() (gas: 14670) +FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotNewContract() (gas: 19048) +FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotOwner() (gas: 23392) +FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_Success() (gas: 118479) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfConsumerNotAllowed() (gas: 59347) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfDuplicateRequestId() (gas: 192799) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfEmptyData() (gas: 29426) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfIncorrectDonId() (gas: 57925) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 186299) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 50947) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidDonId() (gas: 25082) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfNoSubscription() (gas: 29132) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfPaused() (gas: 34291) +FunctionsRouter_SendRequest:test_SendRequest_Success() (gas: 285026) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfConsumerNotAllowed() (gas: 65843) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfEmptyData() (gas: 36012) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfIncorrectDonId() (gas: 29896) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalidCallbackGasLimit() (gas: 57533) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalidDonId() (gas: 27503) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfNoSubscription() (gas: 35717) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfPaused() (gas: 40810) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_Success() (gas: 291595) +FunctionsRouter_SendRequestToProposed:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 192791) +FunctionsRouter_SetAllowListId:test_SetAllowListId_Success() (gas: 30688) +FunctionsRouter_SetAllowListId:test_UpdateConfig_RevertIfNotOwner() (gas: 13403) +FunctionsRouter_Unpause:test_Unpause_RevertIfNotOwner() (gas: 13293) +FunctionsRouter_Unpause:test_Unpause_Success() (gas: 77400) FunctionsRouter_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 24437) -FunctionsRouter_UpdateConfig:test_UpdateConfig_Success() (gas: 60653) -FunctionsRouter_UpdateContracts:test_UpdateContracts_RevertIfNotOwner() (gas: 13293) -FunctionsRouter_UpdateContracts:test_UpdateContracts_Success() (gas: 38716) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 60324) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfPaused() (gas: 60962) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderBecomesBlocked() (gas: 94675) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderIsNotNewOwner() (gas: 62691) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_Success() (gas: 214576) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumers() (gas: 137833) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumersAfterConfigUpdate() (gas: 164777) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNoSubscription() (gas: 12926) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotAllowedSender() (gas: 57789) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotSubscriptionOwner() (gas: 87142) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfPaused() (gas: 18051) -FunctionsSubscriptions_AddConsumer:test_AddConsumer_Success() (gas: 95481) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNoSubscription() (gas: 15085) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotAllowedSender() (gas: 57929) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89316) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPaused() (gas: 20191) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 193763) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitAllBalanceAsDeposit() (gas: 114636) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitSomeBalanceAsDeposit() (gas: 125891) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessRecieveDeposit() (gas: 311486) +FunctionsRouter_UpdateConfig:test_UpdateConfig_Success() (gas: 60676) +FunctionsRouter_UpdateContracts:test_UpdateContracts_RevertIfNotOwner() (gas: 13336) +FunctionsRouter_UpdateContracts:test_UpdateContracts_Success() (gas: 38732) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 60326) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfPaused() (gas: 60987) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderBecomesBlocked() (gas: 94677) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderIsNotNewOwner() (gas: 62693) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_Success() (gas: 214560) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumers() (gas: 137893) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumersAfterConfigUpdate() (gas: 164837) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNoSubscription() (gas: 12946) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotAllowedSender() (gas: 57809) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNotSubscriptionOwner() (gas: 87162) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfPaused() (gas: 18094) +FunctionsSubscriptions_AddConsumer:test_AddConsumer_Success() (gas: 95480) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNoSubscription() (gas: 15041) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotAllowedSender() (gas: 57885) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89272) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPaused() (gas: 20148) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 193688) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitAllBalanceAsDeposit() (gas: 114506) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitSomeBalanceAsDeposit() (gas: 125832) +FunctionsSubscriptions_CancelSubscription_ReceiveDeposit:test_CancelSubscription_SuccessRecieveDeposit() (gas: 74973) FunctionsSubscriptions_Constructor:test_Constructor_Success() (gas: 7654) -FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfNotAllowedSender() (gas: 28637) -FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfPaused() (gas: 17948) -FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_Success() (gas: 351723) -FunctionsSubscriptions_GetConsumer:test_GetConsumer_Success() (gas: 16225) -FunctionsSubscriptions_GetFlags:test_GetFlags_SuccessInvalidSubscription() (gas: 13100) -FunctionsSubscriptions_GetFlags:test_GetFlags_SuccessValidSubscription() (gas: 40858) -FunctionsSubscriptions_GetSubscription:test_GetSubscription_Success() (gas: 30959) -FunctionsSubscriptions_GetSubscriptionCount:test_GetSubscriptionCount_Success() (gas: 12967) -FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfEndIsAfterLastSubscription() (gas: 16523) -FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13436) -FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59568) -FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15032) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 28401, ~: 28401) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 30913, ~: 30913) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14248, ~: 14248) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 35870, ~: 35870) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 59685, ~: 59685) +FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfNotAllowedSender() (gas: 28660) +FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_RevertIfPaused() (gas: 17994) +FunctionsSubscriptions_CreateSubscriptionWithConsumer:test_CreateSubscriptionWithConsumer_Success() (gas: 351726) +FunctionsSubscriptions_GetConsumer:test_GetConsumer_Success() (gas: 16226) +FunctionsSubscriptions_GetFlags:test_GetFlags_SuccessInvalidSubscription() (gas: 13101) +FunctionsSubscriptions_GetFlags:test_GetFlags_SuccessValidSubscription() (gas: 40903) +FunctionsSubscriptions_GetSubscription:test_GetSubscription_Success() (gas: 30937) +FunctionsSubscriptions_GetSubscriptionCount:test_GetSubscriptionCount_Success() (gas: 12968) +FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfEndIsAfterLastSubscription() (gas: 16547) +FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13459) +FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59592) +FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15010) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 28446, ~: 28446) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 30958, ~: 30958) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14293, ~: 14293) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 35938, ~: 35938) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 59686, ~: 59686) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) -FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfPaused() (gas: 20833) +FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfPaused() (gas: 20856) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_SuccessPaysRecipient() (gas: 59732) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_SuccessSetsBalanceToZero() (gas: 57701) FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_RevertIfNoSubscription() (gas: 12818) FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_RevertIfNotOwner() (gas: 15549) FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_Success() (gas: 54867) -FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessDeletesSubscription() (gas: 49624) +FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessDeletesSubscription() (gas: 49607) FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessSubOwnerRefunded() (gas: 50896) -FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessWhenRequestInFlight() (gas: 164300) +FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessWhenRequestInFlight() (gas: 164303) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfAmountMoreThanBalance() (gas: 17924) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfBalanceInvariant() (gas: 210) -FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfNotOwner() (gas: 15533) +FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfNotOwner() (gas: 15555) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessIfNoAmount() (gas: 37396) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessIfRecipientAddressZero() (gas: 52130) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessPaysRecipient() (gas: 54413) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessSetsBalanceToZero() (gas: 37790) -FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessFalseIfNoPendingRequests() (gas: 15025) -FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 175897) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfEmptyNewOwner() (gas: 27610) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfInvalidNewOwner() (gas: 57707) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNoSubscription() (gas: 15000) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 75130) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotSubscriptionOwner() (gas: 17959) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfPaused() (gas: 20104) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_Success() (gas: 68217) -FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_SuccessChangeProposedOwner() (gas: 82791) -FunctionsSubscriptions_RecoverFunds:test_OwnerCancelSubscription_RevertIfNotOwner() (gas: 15532) -FunctionsSubscriptions_RecoverFunds:test_RecoverFunds_Success(uint64) (runs: 256, μ: 41699, ~: 41704) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfInvalidConsumer() (gas: 30238) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNoSubscription() (gas: 14997) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 57778) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87186) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPaused() (gas: 18004) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191195) +FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessFalseIfNoPendingRequests() (gas: 14981) +FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 175857) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfEmptyNewOwner() (gas: 27611) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfInvalidNewOwner() (gas: 57709) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNoSubscription() (gas: 15001) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotAllowedSender() (gas: 75131) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNotSubscriptionOwner() (gas: 17960) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfPaused() (gas: 20128) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_Success() (gas: 68196) +FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_SuccessChangeProposedOwner() (gas: 82749) +FunctionsSubscriptions_RecoverFunds:test_OwnerCancelSubscription_RevertIfNotOwner() (gas: 15554) +FunctionsSubscriptions_RecoverFunds:test_RecoverFunds_Success(uint64) (runs: 256, μ: 41717, ~: 41721) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfInvalidConsumer() (gas: 30260) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNoSubscription() (gas: 15019) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 57800) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87208) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPaused() (gas: 18049) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191221) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_Success() (gas: 41979) -FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNoSubscription() (gas: 12847) -FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNotOwner() (gas: 15640) -FunctionsSubscriptions_SetFlags:test_SetFlags_Success() (gas: 35549) -FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertIfPaused() (gas: 25910) -FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertIfTimeoutNotExceeded() (gas: 25239) -FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertInvalidRequest() (gas: 28220) -FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_Success() (gas: 57752) -FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfNotAllowedSender() (gas: 26368) -FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfPaused() (gas: 15714) -FunctionsSubscriptions_createSubscription:test_CreateSubscription_Success() (gas: 152510) +FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNoSubscription() (gas: 12891) +FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNotOwner() (gas: 15684) +FunctionsSubscriptions_SetFlags:test_SetFlags_Success() (gas: 35594) +FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertIfPaused() (gas: 25955) +FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertIfTimeoutNotExceeded() (gas: 25261) +FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_RevertInvalidRequest() (gas: 28242) +FunctionsSubscriptions_TimeoutRequests:test_TimeoutRequests_Success() (gas: 57754) +FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfNotAllowedSender() (gas: 26390) +FunctionsSubscriptions_createSubscription:test_CreateSubscription_RevertIfPaused() (gas: 15759) +FunctionsSubscriptions_createSubscription:test_CreateSubscription_Success() (gas: 152576) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:testAcceptTermsOfService_InvalidSigner_vuln() (gas: 94815) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfAcceptorIsNotSender() (gas: 25837) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfBlockedSender() (gas: 44348) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfInvalidSigner() (gas: 23597) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientContractIsNotSender() (gas: 1866530) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_RevertIfRecipientIsNotSender() (gas: 26003) -FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForContract() (gas: 1946591) +FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForContract() (gas: 1946547) FunctionsTermsOfServiceAllowList_AcceptTermsOfService:test_AcceptTermsOfService_SuccessIfAcceptingForSelf() (gas: 104851) FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_RevertIfNotOwner() (gas: 15469) FunctionsTermsOfServiceAllowList_BlockSender:test_BlockSender_Success() (gas: 51794) @@ -188,8 +206,8 @@ FunctionsTermsOfServiceAllowList_UnblockSender:test_UnblockSender_Success() (gas FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 13727) FunctionsTermsOfServiceAllowList_UpdateConfig:test_UpdateConfig_Success() (gas: 22073) Gas_AcceptTermsOfService:test_AcceptTermsOfService_Gas() (gas: 84675) -Gas_AddConsumer:test_AddConsumer_Gas() (gas: 79067) -Gas_CreateSubscription:test_CreateSubscription_Gas() (gas: 73353) -Gas_FundSubscription:test_FundSubscription_Gas() (gas: 38501) -Gas_SendRequest:test_SendRequest_MaximumGas() (gas: 964209) -Gas_SendRequest:test_SendRequest_MinimumGas() (gas: 156929) \ No newline at end of file +Gas_AddConsumer:test_AddConsumer_Gas() (gas: 79087) +Gas_CreateSubscription:test_CreateSubscription_Gas() (gas: 73375) +Gas_FundSubscription:test_FundSubscription_Gas() (gas: 38546) +Gas_SendRequest:test_SendRequest_MaximumGas() (gas: 964214) +Gas_SendRequest:test_SendRequest_MinimumGas() (gas: 156934) \ No newline at end of file diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol index 14188fdc04a..82dea8672c8 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol @@ -4,12 +4,17 @@ pragma solidity ^0.8.19; import {FunctionsCoordinator} from "../../dev/v1_X/FunctionsCoordinator.sol"; import {FunctionsBilling} from "../../dev/v1_X/FunctionsBilling.sol"; import {FunctionsRequest} from "../../dev/v1_X/libraries/FunctionsRequest.sol"; +import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; +import {Routable} from "../../dev/v1_X/Routable.sol"; -import {FunctionsRouterSetup, FunctionsSubscriptionSetup, FunctionsMultipleFulfillmentsSetup} from "./Setup.t.sol"; +import {FunctionsRouterSetup, FunctionsSubscriptionSetup, FunctionsClientRequestSetup, FunctionsMultipleFulfillmentsSetup} from "./Setup.t.sol"; /// @notice #constructor -contract FunctionsBilling_Constructor { - +contract FunctionsBilling_Constructor is FunctionsSubscriptionSetup { + function test_Constructor_Success() public { + assertEq(address(s_functionsRouter), s_functionsCoordinator.getRouter_HARNESS()); + assertEq(address(s_linkEthFeed), s_functionsCoordinator.getLinkToNativeFeed_HARNESS()); + } } /// @notice #getConfig @@ -32,28 +37,94 @@ contract FunctionsBilling_GetConfig is FunctionsRouterSetup { } /// @notice #updateConfig -contract FunctionsBilling_UpdateConfig { +contract FunctionsBilling_UpdateConfig is FunctionsRouterSetup { + FunctionsBilling.Config internal configToSet; + + function setUp() public virtual override { + FunctionsRouterSetup.setUp(); + + configToSet = FunctionsBilling.Config({ + feedStalenessSeconds: getCoordinatorConfig().feedStalenessSeconds * 2, + gasOverheadAfterCallback: getCoordinatorConfig().gasOverheadAfterCallback * 2, + gasOverheadBeforeCallback: getCoordinatorConfig().gasOverheadBeforeCallback * 2, + requestTimeoutSeconds: getCoordinatorConfig().requestTimeoutSeconds * 2, + donFee: getCoordinatorConfig().donFee * 2, + maxSupportedRequestDataVersion: getCoordinatorConfig().maxSupportedRequestDataVersion * 2, + fulfillmentGasPriceOverEstimationBP: getCoordinatorConfig().fulfillmentGasPriceOverEstimationBP * 2, + fallbackNativePerUnitLink: getCoordinatorConfig().fallbackNativePerUnitLink * 2, + minimumEstimateGasPriceWei: getCoordinatorConfig().minimumEstimateGasPriceWei * 2 + }); + } + function test_UpdateConfig_RevertIfNotOwner() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert("Only callable by owner"); + s_functionsCoordinator.updateConfig(configToSet); + } + + event ConfigUpdated(FunctionsBilling.Config config); + + function test_UpdateConfig_Success() public { + // topic0 (function signature, always checked), NOT topic1 (false), NOT topic2 (false), NOT topic3 (false), and data (true). + bool checkTopic1 = false; + bool checkTopic2 = false; + bool checkTopic3 = false; + bool checkData = true; + vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData); + emit ConfigUpdated(configToSet); + + s_functionsCoordinator.updateConfig(configToSet); + + FunctionsBilling.Config memory config = s_functionsCoordinator.getConfig(); + assertEq(config.feedStalenessSeconds, configToSet.feedStalenessSeconds); + assertEq(config.gasOverheadAfterCallback, configToSet.gasOverheadAfterCallback); + assertEq(config.gasOverheadBeforeCallback, configToSet.gasOverheadBeforeCallback); + assertEq(config.requestTimeoutSeconds, configToSet.requestTimeoutSeconds); + assertEq(config.donFee, configToSet.donFee); + assertEq(config.maxSupportedRequestDataVersion, configToSet.maxSupportedRequestDataVersion); + assertEq(config.fulfillmentGasPriceOverEstimationBP, configToSet.fulfillmentGasPriceOverEstimationBP); + assertEq(config.fallbackNativePerUnitLink, configToSet.fallbackNativePerUnitLink); + assertEq(config.minimumEstimateGasPriceWei, configToSet.minimumEstimateGasPriceWei); + } } /// @notice #getDONFee -contract FunctionsBilling_GetDONFee { +contract FunctionsBilling_GetDONFee is FunctionsRouterSetup { + function test_GetDONFee_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + uint72 donFee = s_functionsCoordinator.getDONFee(new bytes(0)); + assertEq(donFee, s_donFee); + } } /// @notice #getAdminFee -contract FunctionsBilling_GetAdminFee { +contract FunctionsBilling_GetAdminFee is FunctionsRouterSetup { + function test_GetAdminFee_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + uint72 adminFee = s_functionsCoordinator.getAdminFee(); + assertEq(adminFee, s_adminFee); + } } /// @notice #getWeiPerUnitLink -contract FunctionsBilling_GetWeiPerUnitLink { - -} +contract FunctionsBilling_GetWeiPerUnitLink is FunctionsRouterSetup { + function test_GetWeiPerUnitLink_Success() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); -/// @notice #_getJuelsPerGas -contract FunctionsBilling__GetJuelsPerGas { - // TODO: make contract internal function helper + uint256 weiPerUnitLink = s_functionsCoordinator.getWeiPerUnitLink(); + assertEq(weiPerUnitLink, uint256(LINK_ETH_RATE)); + } } /// @notice #estimateCost @@ -109,7 +180,7 @@ contract FunctionsBilling_EstimateCost is FunctionsSubscriptionSetup { callbackGasLimit, gasPriceWei ); - uint96 expectedCostEstimate = 16375000000000200; + uint96 expectedCostEstimate = 51110500000000200; assertEq(costEstimate, expectedCostEstimate); } @@ -134,7 +205,7 @@ contract FunctionsBilling_EstimateCost is FunctionsSubscriptionSetup { callbackGasLimit, gasPriceWei ); - uint96 expectedCostEstimate = 81875000000000200; + uint96 expectedCostEstimate = 255552500000000200; assertEq(costEstimate, expectedCostEstimate); } } @@ -149,24 +220,115 @@ contract FunctionsBilling__StartBilling { // TODO: make contract internal function helper } -/// @notice #_computeRequestId -contract FunctionsBilling__ComputeRequestId { - // TODO: make contract internal function helper -} - /// @notice #_fulfillAndBill contract FunctionsBilling__FulfillAndBill { // TODO: make contract internal function helper } /// @notice #deleteCommitment -contract FunctionsBilling_DeleteCommitment { +contract FunctionsBilling_DeleteCommitment is FunctionsClientRequestSetup { + function test_DeleteCommitment_RevertIfNotRouter() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert(Routable.OnlyCallableByRouter.selector); + s_functionsCoordinator.deleteCommitment(s_requests[1].requestId); + } + + event CommitmentDeleted(bytes32 requestId); + + function test_DeleteCommitment_Success() public { + // Send as Router + vm.stopPrank(); + vm.startPrank(address(s_functionsRouter)); + + // topic0 (function signature, always checked), NOT topic1 (false), NOT topic2 (false), NOT topic3 (false), and data (true). + bool checkTopic1 = false; + bool checkTopic2 = false; + bool checkTopic3 = false; + bool checkData = true; + vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData); + emit CommitmentDeleted(s_requests[1].requestId); + s_functionsCoordinator.deleteCommitment(s_requests[1].requestId); + } } /// @notice #oracleWithdraw -contract FunctionsBilling_OracleWithdraw { +contract FunctionsBilling_OracleWithdraw is FunctionsMultipleFulfillmentsSetup { + function test_OracleWithdraw_RevertWithNoBalance() public { + uint256[4] memory transmitterBalancesBefore = _getTransmitterBalances(); + _assertTransmittersAllHaveBalance(transmitterBalancesBefore, 0); + // Send as stranger, which has no balance + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert(FunctionsSubscriptions.InvalidCalldata.selector); + + // Attempt to withdraw with no amount, which would withdraw the full balance + s_functionsCoordinator.oracleWithdraw(STRANGER_ADDRESS, 0); + + uint256[4] memory transmitterBalancesAfter = _getTransmitterBalances(); + _assertTransmittersAllHaveBalance(transmitterBalancesAfter, 0); + } + + function test_OracleWithdraw_RevertIfInsufficientBalance() public { + // Send as transmitter 1, which has transmitted 1 report + vm.stopPrank(); + vm.startPrank(NOP_TRANSMITTER_ADDRESS_1); + + vm.expectRevert(FunctionsBilling.InsufficientBalance.selector); + + // Attempt to withdraw more than the Coordinator has assigned + s_functionsCoordinator.oracleWithdraw(NOP_TRANSMITTER_ADDRESS_1, s_fulfillmentCoordinatorBalance + 1); + } + + function test_OracleWithdraw_SuccessTransmitterWithBalanceValidAmountGiven() public { + uint256[4] memory transmitterBalancesBefore = _getTransmitterBalances(); + _assertTransmittersAllHaveBalance(transmitterBalancesBefore, 0); + + // Send as transmitter 1, which has transmitted 1 report + vm.stopPrank(); + vm.startPrank(NOP_TRANSMITTER_ADDRESS_1); + + uint96 expectedTransmitterBalance = s_fulfillmentCoordinatorBalance / 3; + + // Attempt to withdraw half of balance + uint96 halfBalance = expectedTransmitterBalance / 2; + s_functionsCoordinator.oracleWithdraw(NOP_TRANSMITTER_ADDRESS_1, halfBalance); + + uint256[4] memory transmitterBalancesAfter = _getTransmitterBalances(); + assertEq(transmitterBalancesAfter[0], halfBalance); + assertEq(transmitterBalancesAfter[1], 0); + assertEq(transmitterBalancesAfter[2], 0); + assertEq(transmitterBalancesAfter[3], 0); + } + + function test_OracleWithdraw_SuccessTransmitterWithBalanceNoAmountGiven() public { + uint256[4] memory transmitterBalancesBefore = _getTransmitterBalances(); + _assertTransmittersAllHaveBalance(transmitterBalancesBefore, 0); + + // Send as transmitter 1, which has transmitted 1 report + vm.stopPrank(); + vm.startPrank(NOP_TRANSMITTER_ADDRESS_1); + + // Attempt to withdraw with no amount, which will withdraw the full balance + s_functionsCoordinator.oracleWithdraw(NOP_TRANSMITTER_ADDRESS_1, 0); + + // 3 report transmissions have been made + uint96 totalDonFees = s_donFee * 3; + // 4 transmitters will share the DON fees + uint96 donFeeShare = totalDonFees / 4; + uint96 expectedTransmitterBalance = ((s_fulfillmentCoordinatorBalance - totalDonFees) / 3) + donFeeShare; + + uint256[4] memory transmitterBalancesAfter = _getTransmitterBalances(); + assertEq(transmitterBalancesAfter[0], expectedTransmitterBalance); + assertEq(transmitterBalancesAfter[1], 0); + assertEq(transmitterBalancesAfter[2], 0); + assertEq(transmitterBalancesAfter[3], 0); + } } /// @notice #oracleWithdrawAll @@ -188,37 +350,29 @@ contract FunctionsBilling_OracleWithdrawAll is FunctionsMultipleFulfillmentsSetu } function test_OracleWithdrawAll_SuccessPaysTransmittersWithBalance() public { - uint256 transmitter1BalanceBefore = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_1); - assertEq(transmitter1BalanceBefore, 0); - uint256 transmitter2BalanceBefore = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_2); - assertEq(transmitter2BalanceBefore, 0); - uint256 transmitter3BalanceBefore = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_3); - assertEq(transmitter3BalanceBefore, 0); - uint256 transmitter4BalanceBefore = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_4); - assertEq(transmitter4BalanceBefore, 0); + uint256[4] memory transmitterBalancesBefore = _getTransmitterBalances(); + _assertTransmittersAllHaveBalance(transmitterBalancesBefore, 0); s_functionsCoordinator.oracleWithdrawAll(); uint96 expectedTransmitterBalance = s_fulfillmentCoordinatorBalance / 3; - uint256 transmitter1BalanceAfter = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_1); - assertEq(transmitter1BalanceAfter, expectedTransmitterBalance); - uint256 transmitter2BalanceAfter = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_2); - assertEq(transmitter2BalanceAfter, expectedTransmitterBalance); - uint256 transmitter3BalanceAfter = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_3); - assertEq(transmitter3BalanceAfter, expectedTransmitterBalance); + uint256[4] memory transmitterBalancesAfter = _getTransmitterBalances(); + assertEq(transmitterBalancesAfter[0], expectedTransmitterBalance); + assertEq(transmitterBalancesAfter[1], expectedTransmitterBalance); + assertEq(transmitterBalancesAfter[2], expectedTransmitterBalance); // Transmitter 4 has no balance - uint256 transmitter4BalanceAfter = s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_4); - assertEq(transmitter4BalanceAfter, 0); + assertEq(transmitterBalancesAfter[3], 0); } } -/// @notice #_getTransmitters -contract FunctionsBilling__GetTransmitters { - // TODO: make contract internal function helper -} - /// @notice #_disperseFeePool -contract FunctionsBilling__DisperseFeePool { - // TODO: make contract internal function helper +contract FunctionsBilling__DisperseFeePool is FunctionsRouterSetup { + function test__DisperseFeePool_RevertIfNotSet() public { + // Manually set s_feePool (at slot 11) to 1 to get past first check in _disperseFeePool + vm.store(address(s_functionsCoordinator), bytes32(uint256(11)), bytes32(uint256(1))); + + vm.expectRevert(FunctionsBilling.NoTransmittersSet.selector); + s_functionsCoordinator.disperseFeePool_HARNESS(); + } } diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsClient.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsClient.t.sol index d6a3be16847..363827645c0 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsClient.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsClient.t.sol @@ -2,22 +2,23 @@ pragma solidity ^0.8.19; import {BaseTest} from "./BaseTest.t.sol"; +import {FunctionsClient} from "../../dev/v1_X/FunctionsClient.sol"; import {FunctionsRouter} from "../../dev/v1_X/FunctionsRouter.sol"; import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; import {FunctionsRequest} from "../../dev/v1_X/libraries/FunctionsRequest.sol"; import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; -import {FunctionsSubscriptionSetup} from "./Setup.t.sol"; +import {FunctionsClientSetup, FunctionsSubscriptionSetup, FunctionsClientRequestSetup} from "./Setup.t.sol"; /// @notice #constructor -contract FunctionsClient_Constructor { - +contract FunctionsClient_Constructor is FunctionsClientSetup { + function test_Constructor_Success() public { + assertEq(address(s_functionsRouter), s_functionsClient.getRouter_HARNESS()); + } } /// @notice #_sendRequest contract FunctionsClient__SendRequest is FunctionsSubscriptionSetup { - // TODO: make contract internal function helper - function test__SendRequest_RevertIfInvalidCallbackGasLimit() public { // Build minimal valid request data string memory sourceCode = "return 'hello world';"; @@ -43,12 +44,35 @@ contract FunctionsClient__SendRequest is FunctionsSubscriptionSetup { } } -/// @notice #fulfillRequest -contract FunctionsClient_FulfillRequest { +/// @notice #handleOracleFulfillment +contract FunctionsClient_HandleOracleFulfillment is FunctionsClientRequestSetup { + function test_HandleOracleFulfillment_RevertIfNotRouter() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); -} + vm.expectRevert(FunctionsClient.OnlyRouterCanFulfill.selector); + s_functionsClient.handleOracleFulfillment(s_requests[1].requestId, new bytes(0), new bytes(0)); + } -/// @notice #handleOracleFulfillment -contract FunctionsClient_HandleOracleFulfillment { + event RequestFulfilled(bytes32 indexed id); + event ResponseReceived(bytes32 indexed requestId, bytes result, bytes err); + + function test_HandleOracleFulfillment_Success() public { + // Send as Router + vm.stopPrank(); + vm.startPrank(address(s_functionsRouter)); + // topic0 (function signature, always checked), NOT topic1 (false), NOT topic2 (false), NOT topic3 (false), and data (true). + bool checkTopic1 = false; + bool checkTopic2 = false; + bool checkTopic3 = false; + bool checkData = true; + vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData); + emit ResponseReceived(s_requests[1].requestId, new bytes(0), new bytes(0)); + vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData); + emit RequestFulfilled(s_requests[1].requestId); + + s_functionsClient.handleOracleFulfillment(s_requests[1].requestId, new bytes(0), new bytes(0)); + } } diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol index 893aa6408b6..7166add19fe 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol @@ -4,9 +4,13 @@ pragma solidity ^0.8.19; import {FunctionsCoordinator} from "../../dev/v1_X/FunctionsCoordinator.sol"; import {FunctionsBilling} from "../../dev/v1_X/FunctionsBilling.sol"; import {FunctionsRequest} from "../../dev/v1_X/libraries/FunctionsRequest.sol"; +import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; import {FunctionsRouter} from "../../dev/v1_X/FunctionsRouter.sol"; +import {Routable} from "../../dev/v1_X/Routable.sol"; -import {FunctionsRouterSetup} from "./Setup.t.sol"; +import {BaseTest} from "./BaseTest.t.sol"; +import {FunctionsRouterSetup, FunctionsDONSetup, FunctionsSubscriptionSetup} from "./Setup.t.sol"; +import "forge-std/console.sol"; /// @notice #constructor contract FunctionsCoordinator_Constructor is FunctionsRouterSetup { @@ -17,48 +21,220 @@ contract FunctionsCoordinator_Constructor is FunctionsRouterSetup { } /// @notice #getThresholdPublicKey -contract FunctionsCoordinator_GetThresholdPublicKey { +contract FunctionsCoordinator_GetThresholdPublicKey is FunctionsDONSetup { + function test_GetThresholdPublicKey_RevertIfEmpty() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + // Reverts when empty + vm.expectRevert(FunctionsCoordinator.EmptyPublicKey.selector); + s_functionsCoordinator.getThresholdPublicKey(); + } + + function test_GetThresholdPublicKey_Success() public { + s_functionsCoordinator.setThresholdPublicKey(s_thresholdKey); + + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + bytes memory thresholdKey = s_functionsCoordinator.getThresholdPublicKey(); + assertEq(thresholdKey, s_thresholdKey); + } } /// @notice #setThresholdPublicKey -contract FunctionsCoordinator_SetThresholdPublicKey { +contract FunctionsCoordinator_SetThresholdPublicKey is FunctionsDONSetup { + function test_SetThresholdPublicKey_RevertNotOwner() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert("Only callable by owner"); + bytes memory newThresholdKey = new bytes(0); + s_functionsCoordinator.setThresholdPublicKey(newThresholdKey); + } + + function test_SetThresholdPublicKey_Success() public { + s_functionsCoordinator.setThresholdPublicKey(s_thresholdKey); + + bytes memory thresholdKey = s_functionsCoordinator.getThresholdPublicKey(); + assertEq(thresholdKey, s_thresholdKey); + } } /// @notice #getDONPublicKey -contract FunctionsCoordinator_GetDONPublicKey { +contract FunctionsCoordinator_GetDONPublicKey is FunctionsDONSetup { + function test_GetDONPublicKey_RevertIfEmpty() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + // Reverts when empty + vm.expectRevert(FunctionsCoordinator.EmptyPublicKey.selector); + s_functionsCoordinator.getDONPublicKey(); + } + + function test_GetDONPublicKey_Success() public { + s_functionsCoordinator.setDONPublicKey(s_donKey); + + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + bytes memory donKey = s_functionsCoordinator.getDONPublicKey(); + assertEq(donKey, s_donKey); + } } /// @notice #setDONPublicKey -contract FunctionsCoordinator__SetDONPublicKey { +contract FunctionsCoordinator_SetDONPublicKey is FunctionsDONSetup { + function test_SetDONPublicKey_RevertNotOwner() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); + + vm.expectRevert("Only callable by owner"); + s_functionsCoordinator.setDONPublicKey(s_donKey); + } + + function test_SetDONPublicKey_Success() public { + s_functionsCoordinator.setDONPublicKey(s_donKey); + bytes memory donKey = s_functionsCoordinator.getDONPublicKey(); + assertEq(donKey, s_donKey); + } } /// @notice #_isTransmitter -contract FunctionsCoordinator_IsTransmitter { - // TODO: make contract internal function helper +contract FunctionsCoordinator__IsTransmitter is FunctionsDONSetup { + function test__IsTransmitter_SuccessFound() public { + bool isTransmitter = s_functionsCoordinator.isTransmitter_HARNESS(NOP_TRANSMITTER_ADDRESS_1); + assertEq(isTransmitter, true); + } + + function test__IsTransmitter_SuccessNotFound() public { + bool isTransmitter = s_functionsCoordinator.isTransmitter_HARNESS(STRANGER_ADDRESS); + assertEq(isTransmitter, false); + } } -/// @notice #setNodePublicKey -contract FunctionsCoordinator_SetNodePublicKey { +/// @notice #startRequest +contract FunctionsCoordinator_StartRequest is FunctionsSubscriptionSetup { + function test_StartRequest_RevertIfNotRouter() public { + // Send as stranger + vm.stopPrank(); + vm.startPrank(STRANGER_ADDRESS); -} + vm.expectRevert(Routable.OnlyCallableByRouter.selector); -/// @notice #deleteNodePublicKey -contract FunctionsCoordinator_DeleteNodePublicKey { + s_functionsCoordinator.startRequest( + FunctionsResponse.RequestMeta({ + requestingContract: address(s_functionsClient), + data: new bytes(0), + subscriptionId: s_subscriptionId, + dataVersion: FunctionsRequest.REQUEST_DATA_VERSION, + flags: bytes32(0), + callbackGasLimit: 5_500, + adminFee: s_adminFee, + initiatedRequests: 0, + completedRequests: 0, + availableBalance: s_subscriptionInitialFunding, + subscriptionOwner: OWNER_ADDRESS + }) + ); + } -} + event OracleRequest( + bytes32 indexed requestId, + address indexed requestingContract, + address requestInitiator, + uint64 subscriptionId, + address subscriptionOwner, + bytes data, + uint16 dataVersion, + bytes32 flags, + uint64 callbackGasLimit, + FunctionsResponse.Commitment commitment + ); -/// @notice #getAllNodePublicKeys -contract FunctionsCoordinator_GetAllNodePublicKeys { + function test_StartRequest_Success() public { + // Send as Router + vm.stopPrank(); + vm.startPrank(address(s_functionsRouter)); + (, , address txOrigin) = vm.readCallers(); -} + bytes memory _requestData = new bytes(0); + uint32 _callbackGasLimit = 5_500; + uint96 costEstimate = s_functionsCoordinator.estimateCost( + s_subscriptionId, + _requestData, + _callbackGasLimit, + tx.gasprice + ); + uint32 timeoutTimestamp = uint32(block.timestamp + getCoordinatorConfig().requestTimeoutSeconds); + bytes32 expectedRequestId = keccak256( + abi.encode( + address(s_functionsCoordinator), + address(s_functionsClient), + s_subscriptionId, + 1, + keccak256(_requestData), + FunctionsRequest.REQUEST_DATA_VERSION, + _callbackGasLimit, + costEstimate, + timeoutTimestamp, + txOrigin + ) + ); -/// @notice #startRequest -contract FunctionsCoordinator_StartRequest { + FunctionsResponse.Commitment memory expectedComittment = FunctionsResponse.Commitment({ + adminFee: s_adminFee, + coordinator: address(s_functionsCoordinator), + client: address(s_functionsClient), + subscriptionId: s_subscriptionId, + callbackGasLimit: _callbackGasLimit, + estimatedTotalCostJuels: costEstimate, + timeoutTimestamp: timeoutTimestamp, + requestId: expectedRequestId, + donFee: s_donFee, + gasOverheadBeforeCallback: getCoordinatorConfig().gasOverheadBeforeCallback, + gasOverheadAfterCallback: getCoordinatorConfig().gasOverheadAfterCallback + }); + // topic0 (function signature, always checked), topic1 (true), topic2 (true), NOT topic3 (false), and data (true). + vm.expectEmit(true, true, false, true); + emit OracleRequest({ + requestId: expectedRequestId, + requestingContract: address(s_functionsClient), + requestInitiator: txOrigin, + subscriptionId: s_subscriptionId, + subscriptionOwner: OWNER_ADDRESS, + data: _requestData, + dataVersion: FunctionsRequest.REQUEST_DATA_VERSION, + flags: bytes32(0), + callbackGasLimit: _callbackGasLimit, + commitment: expectedComittment + }); + + s_functionsCoordinator.startRequest( + FunctionsResponse.RequestMeta({ + requestingContract: address(s_functionsClient), + data: _requestData, + subscriptionId: s_subscriptionId, + dataVersion: FunctionsRequest.REQUEST_DATA_VERSION, + flags: bytes32(0), + callbackGasLimit: 5_500, + adminFee: s_adminFee, + initiatedRequests: 0, + completedRequests: 0, + availableBalance: s_subscriptionInitialFunding, + subscriptionOwner: OWNER_ADDRESS + }) + ); + } } /// @notice #_beforeSetConfig @@ -73,10 +249,10 @@ contract FunctionsCoordinator__GetTransmitters { /// @notice #_report contract FunctionsCoordinator__Report { - + // TODO: make contract internal function helper } /// @notice #_onlyOwner contract FunctionsCoordinator__OnlyOwner { - + // TODO: make contract internal function helper } diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol index b9b6e1d5746..081fe2f6649 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsRouter.t.sol @@ -938,7 +938,7 @@ contract FunctionsRouter_Fulfill is FunctionsClientRequestSetup { uint32 callbackGasLimit = s_requests[requestToFulfill].requestData.callbackGasLimit; // Coordinator sends enough gas that would get through callback and payment, but fail after - uint256 gasToUse = getCoordinatorConfig().gasOverheadBeforeCallback + callbackGasLimit + 100000; + uint256 gasToUse = getCoordinatorConfig().gasOverheadBeforeCallback + callbackGasLimit + 10_000; // topic0 (function signature, always checked), topic1 (true), NOT topic2 (false), NOT topic3 (false), and data (true). bool checkTopic1RequestId = true; @@ -1221,7 +1221,7 @@ contract FunctionsRouter_Fulfill is FunctionsClientRequestSetup { emit RequestProcessed({ requestId: s_requests[requestToFulfill].requestId, subscriptionId: s_subscriptionId, - totalCostJuels: _getExpectedCost(5393), // gasUsed is manually taken + totalCostJuels: _getExpectedCost(5416), // gasUsed is manually taken transmitter: NOP_TRANSMITTER_ADDRESS_1, resultCode: FunctionsResponse.FulfillResult.FULFILLED, response: bytes(response), diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol index 8046bf7d939..df905fb8bee 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol @@ -1122,62 +1122,14 @@ contract FunctionsSubscriptions_CancelSubscription is FunctionsSubscriptionSetup uint256 balanceAfterWithdraw = s_linkToken.balanceOf(STRANGER_ADDRESS); assertEq(balanceBeforeWithdraw + expectedDepositWithheld, balanceAfterWithdraw); } +} - function test_CancelSubscription_SuccessRecieveDeposit() public { - // Complete 1 request = subscriptionDepositMinimumRequests - vm.recordLogs(); - bytes32 requestId = s_functionsClient.sendRequest( - s_donId, - "return 'hello world';", - new bytes(0), - new string[](0), - new bytes[](0), - s_subscriptionId, - 5500 - ); - - // Get commitment data from OracleRequest event log - Vm.Log[] memory entries = vm.getRecordedLogs(); - (, , , , , , , FunctionsResponse.Commitment memory commitment) = abi.decode( - entries[0].data, - (address, uint64, address, bytes, uint16, bytes32, uint64, FunctionsResponse.Commitment) - ); - - // Send as transmitter 1 - vm.stopPrank(); - vm.startPrank(NOP_TRANSMITTER_ADDRESS_1); - - // Build report - bytes32[] memory requestIds = new bytes32[](1); - requestIds[0] = requestId; - bytes[] memory results = new bytes[](1); - results[0] = bytes("hello world!"); - bytes[] memory errors = new bytes[](1); - // No error - bytes[] memory onchainMetadata = new bytes[](1); - onchainMetadata[0] = abi.encode(commitment); - bytes[] memory offchainMetadata = new bytes[](1); - // No offchain metadata - bytes memory report = abi.encode(requestIds, results, errors, onchainMetadata, offchainMetadata); - - // Build signers - address[31] memory signers; - signers[0] = NOP_SIGNER_ADDRESS_1; - - // Send report - vm.recordLogs(); - s_functionsCoordinator.callReportWithSigners(report, signers); - - // Get actual cost from RequestProcessed event log - Vm.Log[] memory entries2 = vm.getRecordedLogs(); - (uint96 totalCostJuels, , , , , ) = abi.decode( - entries2[2].data, - (uint96, address, FunctionsResponse.FulfillResult, bytes, bytes, bytes) - ); +/// @notice #cancelSubscription +contract FunctionsSubscriptions_CancelSubscription_ReceiveDeposit is FunctionsFulfillmentSetup { + event SubscriptionCanceled(uint64 indexed subscriptionId, address fundsRecipient, uint256 fundsAmount); - // Return to sending as owner - vm.stopPrank(); - vm.startPrank(OWNER_ADDRESS); + function test_CancelSubscription_SuccessRecieveDeposit() public { + uint96 totalCostJuels = s_fulfillmentRouterOwnerBalance + s_fulfillmentCoordinatorBalance; uint256 subscriptionOwnerBalanceBefore = s_linkToken.balanceOf(OWNER_ADDRESS); diff --git a/contracts/src/v0.8/functions/tests/v1_X/README.md b/contracts/src/v0.8/functions/tests/v1_X/README.md index 6400a28dc79..5f96532fb43 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/README.md +++ b/contracts/src/v0.8/functions/tests/v1_X/README.md @@ -5,18 +5,21 @@ First set the foundry profile to Functions: export FOUNDRY_PROFILE=functions ``` -To run all test files use: +**To run tests use**: + +All Functions test files: ``` -forge test -vv +forge test -vvv ``` To run a specific file use: ``` -forge test -vv --mp src/v0.8/functions/tests/v1_X/[File Name].t.sol +forge test -vvv --mp src/v0.8/functions/tests/v1_X/[File Name].t.sol ``` -To see coverage: -First ensure that the correct files are being evaluated. For example, if only v1 contracts are, then temporarily change the Functions profile in `./foundry.toml`. +**To see coverage**: +First ensure that the correct files are being evaluated. For example, if only v0 contracts are, then temporarily change the Functions profile in `./foundry.toml`. + ``` -forge coverage +forge coverage --ir-minimum ``` \ No newline at end of file diff --git a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol index f603e83281c..0c08fd20cd3 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol @@ -2,21 +2,21 @@ pragma solidity ^0.8.19; import {BaseTest} from "./BaseTest.t.sol"; -import {FunctionsRouter} from "../../dev/v1_X/FunctionsRouter.sol"; -import {FunctionsCoordinatorTestHelper} from "./testhelpers/FunctionsCoordinatorTestHelper.sol"; +import {FunctionsClientHarness} from "./testhelpers/FunctionsClientHarness.sol"; +import {FunctionsRouterHarness, FunctionsRouter} from "./testhelpers/FunctionsRouterHarness.sol"; +import {FunctionsCoordinatorHarness} from "./testhelpers/FunctionsCoordinatorHarness.sol"; import {FunctionsBilling} from "../../dev/v1_X/FunctionsBilling.sol"; import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol"; import {TermsOfServiceAllowList} from "../../dev/v1_X/accessControl/TermsOfServiceAllowList.sol"; -import {FunctionsClientUpgradeHelper} from "./testhelpers/FunctionsClientUpgradeHelper.sol"; import {MockLinkToken} from "../../../mocks/MockLinkToken.sol"; import "forge-std/Vm.sol"; /// @notice Set up to deploy the following contracts: FunctionsRouter, FunctionsCoordinator, LINK/ETH Feed, ToS Allow List, and LINK token contract FunctionsRouterSetup is BaseTest { - FunctionsRouter internal s_functionsRouter; - FunctionsCoordinatorTestHelper internal s_functionsCoordinator; // TODO: use actual FunctionsCoordinator instead of helper + FunctionsRouterHarness internal s_functionsRouter; + FunctionsCoordinatorHarness internal s_functionsCoordinator; MockV3Aggregator internal s_linkEthFeed; TermsOfServiceAllowList internal s_termsOfServiceAllowList; MockLinkToken internal s_linkToken; @@ -36,9 +36,9 @@ contract FunctionsRouterSetup is BaseTest { function setUp() public virtual override { BaseTest.setUp(); s_linkToken = new MockLinkToken(); - s_functionsRouter = new FunctionsRouter(address(s_linkToken), getRouterConfig()); + s_functionsRouter = new FunctionsRouterHarness(address(s_linkToken), getRouterConfig()); s_linkEthFeed = new MockV3Aggregator(0, LINK_ETH_RATE); - s_functionsCoordinator = new FunctionsCoordinatorTestHelper( + s_functionsCoordinator = new FunctionsCoordinatorHarness( address(s_functionsRouter), getCoordinatorConfig(), address(s_linkEthFeed) @@ -68,8 +68,8 @@ contract FunctionsRouterSetup is BaseTest { return FunctionsBilling.Config({ feedStalenessSeconds: 24 * 60 * 60, // 1 day - gasOverheadAfterCallback: 50_000, // TODO: update - gasOverheadBeforeCallback: 100_00, // TODO: update + gasOverheadAfterCallback: 93_942, + gasOverheadBeforeCallback: 105_000, requestTimeoutSeconds: 60 * 5, // 5 minutes donFee: s_donFee, maxSupportedRequestDataVersion: 1, @@ -111,6 +111,15 @@ contract FunctionsDONSetup is FunctionsRouterSetup { uint64 internal s_offchainConfigVersion = 1; bytes internal s_offchainConfig = new bytes(0); + bytes s_thresholdKey = + vm.parseBytes( + "0x7b2247726f7570223a2250323536222c22475f626172223a22424f2f344358424575792f64547a436a612b614e774d666c2b645a77346d325036533246536b4966472f6633527547327337392b494e79642b4639326a346f586e67433657427561556a752b4a637a32377834484251343d222c2248223a224250532f72485065377941467232416c447a79395549466258776d46384666756632596d514177666e3342373844336f474845643247474536466e616f34552b4c6a4d4d5756792b464f7075686e77554f6a75427a64773d222c22484172726179223a5b22424d75546862414473337768316e67764e56792f6e3841316d42674b5a4b4c475259385937796a39695769337242502f316a32347571695869534531437554384c6f51446a386248466d384345477667517158494e62383d222c224248687974716d6e34314373322f4658416f43737548687151486236382f597930524b2b41354c6647654f645a78466f4e386c442b45656e4b587a544943784f6d3231636d535447364864484a6e336342645663714c673d222c22424d794e7a4534616e596258474d72694f52664c52634e7239766c347878654279316432452f4464335a744630546372386267567435582b2b42355967552b4b7875726e512f4d656b6857335845782b79506e4e4f584d3d222c22424d6a753272375a657a4a45545539413938746a6b6d547966796a79493735345742555835505174724a6578346d6766366130787373426d50325a7472412b55576d504e592b6d4664526b46674f7944694c53614e59453d225d7d" + ); + bytes s_donKey = + vm.parseBytes( + "0xf2f9c47363202d89aa9fa70baf783d70006fe493471ac8cfa82f1426fd09f16a5f6b32b7c4b5d5165cd147a6e513ba4c0efd39d969d6b20a8a21126f0411b9c6" + ); + function setUp() public virtual override { FunctionsRouterSetup.setUp(); @@ -136,6 +145,22 @@ contract FunctionsDONSetup is FunctionsRouterSetup { s_offchainConfig ); } + + function _getTransmitterBalances() internal view returns (uint256[4] memory balances) { + return [ + s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_1), + s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_2), + s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_3), + s_linkToken.balanceOf(NOP_TRANSMITTER_ADDRESS_4) + ]; + } + + function _assertTransmittersAllHaveBalance(uint256[4] memory balances, uint256 expectedBalance) internal { + assertEq(balances[0], expectedBalance); + assertEq(balances[1], expectedBalance); + assertEq(balances[2], expectedBalance); + assertEq(balances[3], expectedBalance); + } } /// @notice Set up to add the Coordinator and ToS Allow Contract as routes on the Router contract @@ -172,12 +197,12 @@ contract FunctionsOwnerAcceptTermsOfServiceSetup is FunctionsRoutesSetup { /// @notice Set up to deploy a consumer contract contract FunctionsClientSetup is FunctionsOwnerAcceptTermsOfServiceSetup { - FunctionsClientUpgradeHelper internal s_functionsClient; + FunctionsClientHarness internal s_functionsClient; function setUp() public virtual override { FunctionsOwnerAcceptTermsOfServiceSetup.setUp(); - s_functionsClient = new FunctionsClientUpgradeHelper(address(s_functionsRouter)); + s_functionsClient = new FunctionsClientHarness(address(s_functionsRouter)); } } @@ -261,7 +286,7 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { vm.recordLogs(); - bytes32 requestId = FunctionsClientUpgradeHelper(client).sendRequest( + bytes32 requestId = FunctionsClientHarness(client).sendRequest( s_donId, sourceCode, secrets, diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol new file mode 100644 index 00000000000..ec3b5a65fea --- /dev/null +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {FunctionsClientUpgradeHelper} from "./FunctionsClientUpgradeHelper.sol"; +import {FunctionsResponse} from "../../../dev/v1_X/libraries/FunctionsResponse.sol"; + +/// @title Functions Client Test Harness +/// @notice Contract to expose internal functions for testing purposes +contract FunctionsClientHarness is FunctionsClientUpgradeHelper { + constructor(address router) FunctionsClientUpgradeHelper(router) {} + + function getRouter_HARNESS() external view returns (address) { + return address(i_router); + } + + function sendRequest_HARNESS( + bytes memory data, + uint64 subscriptionId, + uint32 callbackGasLimit, + bytes32 donId + ) external returns (bytes32) { + return super._sendRequest(data, subscriptionId, callbackGasLimit, donId); + } +} diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol index bca0f0a3fa2..bc73544205e 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; +pragma solidity ^0.8.19; import {ITermsOfServiceAllowList} from "../../../dev/v1_X/accessControl/interfaces/ITermsOfServiceAllowList.sol"; import {IFunctionsSubscriptions} from "../../../dev/v1_X/interfaces/IFunctionsSubscriptions.sol"; diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientWithEmptyCallback.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientWithEmptyCallback.sol index 362b21d89ba..e5674717730 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientWithEmptyCallback.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientWithEmptyCallback.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; +pragma solidity ^0.8.19; import {FunctionsRequest} from "../../../dev/v1_X/libraries/FunctionsRequest.sol"; import {FunctionsClient} from "../../../dev/v1_X/FunctionsClient.sol"; diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol new file mode 100644 index 00000000000..bc103fc3561 --- /dev/null +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {FunctionsCoordinator} from "../../../dev/v1_X/FunctionsCoordinator.sol"; +import {FunctionsBilling} from "../../../dev/v1_X/FunctionsBilling.sol"; +import {FunctionsResponse} from "../../../dev/v1_X/libraries/FunctionsResponse.sol"; + +/// @title Functions Coordinator Test Harness +/// @notice Contract to expose internal functions for testing purposes +contract FunctionsCoordinatorHarness is FunctionsCoordinator { + address s_linkToNativeFeed_HARNESS; + address s_router_HARNESS; + + constructor( + address router, + FunctionsBilling.Config memory config, + address linkToNativeFeed + ) FunctionsCoordinator(router, config, linkToNativeFeed) { + s_linkToNativeFeed_HARNESS = linkToNativeFeed; + s_router_HARNESS = router; + } + + function isTransmitter_HARNESS(address node) external view returns (bool) { + return super._isTransmitter(node); + } + + function beforeSetConfig_HARNESS(uint8 _f, bytes memory _onchainConfig) external { + return super._beforeSetConfig(_f, _onchainConfig); + } + + /// @dev Used by FunctionsBilling.sol + function getTransmitters_HARNESS() external view returns (address[] memory) { + return super._getTransmitters(); + } + + function report_HARNESS( + uint256 initialGas, + address transmitter, + uint8 signerCount, + address[MAX_NUM_ORACLES] memory signers, + bytes calldata report + ) external { + return super._report(initialGas, transmitter, signerCount, signers, report); + } + + function onlyOwner_HARNESS() external view { + return super._onlyOwner(); + } + + // ================================================================ + // | Functions Billing | + // ================================================================ + + function getLinkToNativeFeed_HARNESS() external view returns (address) { + return s_linkToNativeFeed_HARNESS; + } + + function getRouter_HARNESS() external view returns (address) { + return s_router_HARNESS; + } + + function calculateCostEstimate_HARNESS( + uint32 callbackGasLimit, + uint256 gasPriceWei, + uint72 donFee, + uint72 adminFee + ) external view returns (uint96) { + return super._calculateCostEstimate(callbackGasLimit, gasPriceWei, donFee, adminFee); + } + + function startBilling_HARNESS( + FunctionsResponse.RequestMeta memory request + ) external returns (FunctionsResponse.Commitment memory commitment) { + return super._startBilling(request); + } + + function fulfillAndBill_HARNESS( + bytes32 requestId, + bytes memory response, + bytes memory err, + bytes memory onchainMetadata, + bytes memory offchainMetadata + ) external returns (FunctionsResponse.FulfillResult) { + return super._fulfillAndBill(requestId, response, err, onchainMetadata, offchainMetadata); + } + + function disperseFeePool_HARNESS() external { + return super._disperseFeePool(); + } + + // ================================================================ + // | OCR2 | + // ================================================================ + + function configDigestFromConfigData_HARNESS( + uint256 _chainId, + address _contractAddress, + uint64 _configCount, + address[] memory _signers, + address[] memory _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _encodedConfigVersion, + bytes memory _encodedConfig + ) internal pure returns (bytes32) { + return + super._configDigestFromConfigData( + _chainId, + _contractAddress, + _configCount, + _signers, + _transmitters, + _f, + _onchainConfig, + _encodedConfigVersion, + _encodedConfig + ); + } +} diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol index 1d883b3b29a..5e57e62e599 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorTestHelper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; +pragma solidity ^0.8.19; import {FunctionsCoordinator} from "../../../dev/v1_X/FunctionsCoordinator.sol"; import {FunctionsBilling} from "../../../dev/v1_X/FunctionsBilling.sol"; diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsRouterHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsRouterHarness.sol new file mode 100644 index 00000000000..7caeff498a3 --- /dev/null +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsRouterHarness.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {FunctionsRouter} from "../../../dev/v1_X/FunctionsRouter.sol"; + +/// @title Functions Router Test Harness +/// @notice Contract to expose internal functions for testing purposes +contract FunctionsRouterHarness is FunctionsRouter { + constructor(address linkToken, Config memory config) FunctionsRouter(linkToken, config) {} + + function getMaxConsumers_HARNESS() external view returns (uint16) { + return super._getMaxConsumers(); + } + + function getSubscriptionDepositDetails_HARNESS() external view returns (uint16, uint72) { + return super._getSubscriptionDepositDetails(); + } + + function whenNotPaused_HARNESS() external view { + return super._whenNotPaused(); + } + + function onlyRouterOwner_HARNESS() external view { + return super._onlyRouterOwner(); + } + + function onlySenderThatAcceptedToS_HARNESS() external view { + return super._onlySenderThatAcceptedToS(); + } +} diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsSubscriptionsHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsSubscriptionsHarness.sol new file mode 100644 index 00000000000..2e2427f6e13 --- /dev/null +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsSubscriptionsHarness.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {FunctionsSubscriptions} from "../../../dev/v1_X/FunctionsSubscriptions.sol"; + +/// @title Functions Subscriptions Test Harness +/// @notice Contract to expose internal functions for testing purposes +contract FunctionsSubscriptionsHarness is FunctionsSubscriptions { + constructor(address link) FunctionsSubscriptions(link) {} + + function markRequestInFlight_HARNESS(address client, uint64 subscriptionId, uint96 estimatedTotalCostJuels) external { + return super._markRequestInFlight(client, subscriptionId, estimatedTotalCostJuels); + } + + function pay_HARNESS( + uint64 subscriptionId, + uint96 estimatedTotalCostJuels, + address client, + uint96 adminFee, + uint96 juelsPerGas, + uint96 gasUsed, + uint96 costWithoutCallbackJuels + ) external returns (Receipt memory) { + return + super._pay( + subscriptionId, + estimatedTotalCostJuels, + client, + adminFee, + juelsPerGas, + gasUsed, + costWithoutCallbackJuels + ); + } + + function isExistingSubscription_HARNESS(uint64 subscriptionId) external view { + return super._isExistingSubscription(subscriptionId); + } + + function isAllowedConsumer_HARNESS(address client, uint64 subscriptionId) external view { + return super._isAllowedConsumer(client, subscriptionId); + } + + // Overrides + function _getMaxConsumers() internal view override returns (uint16) {} + + function _getSubscriptionDepositDetails() internal override returns (uint16, uint72) {} + + function _onlySenderThatAcceptedToS() internal override {} + + function _onlyRouterOwner() internal override {} + + function _whenNotPaused() internal override {} +} diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsTestHelper.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsTestHelper.sol index e8e74e3ed74..50e90c44953 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsTestHelper.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsTestHelper.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; +pragma solidity ^0.8.19; import {FunctionsRequest} from "../../../dev/v1_X/libraries/FunctionsRequest.sol"; From e9c820632bfd2cf7da0c40ce547af13cd53748de Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Wed, 25 Oct 2023 02:47:50 -0400 Subject: [PATCH 002/271] (chore): Reduce contracts solhint warnings by ignoring Functions v1.0.0 production code (#11069) --- contracts/.solhintignore | 4 ++-- contracts/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/.solhintignore b/contracts/.solhintignore index 1a308af94b1..53d52657ecb 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -1,8 +1,8 @@ # 351 warnings #./src/v0.8/automation -# 27 warnings -#./src/v0.8/functions +# Ignore Functions v1.0.0 code that was frozen after audit +./src/v0.8/functions/v1_0_0 # Ignore tests, this should not be the long term plan but is OK in the short term ./src/v0.8/**/*.t.sol diff --git a/contracts/package.json b/contracts/package.json index 6c902926c2b..75b1ddfa973 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 442 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 351 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", From 5e75873305b79b6a313813a29e604678953c291f Mon Sep 17 00:00:00 2001 From: Mohamed Mehany <7327188+mohamed-mehany@users.noreply.github.com> Date: Wed, 25 Oct 2023 10:57:56 +0200 Subject: [PATCH 003/271] Fix redundant "replacing abandoned tx" logs (#11019) This fixes the issue of incorrectly logging the "replacing abandoned tx" log. The DELETE query doesn't return a sql.ErrNoRows on failing to find rows to be deleted. Instead we are checking on the count of RowsAffected. Another minor fix is continuing to execute the function if no attempts were deleted, instead of throwing an error, since at this point there is no need to break the execution flow. Co-authored-by: Simson --- core/chains/evm/txmgr/evm_tx_store.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 7b1ef8948c1..96963e78d75 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1486,14 +1486,20 @@ func (o *evmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx, // Note: the record of the original abandoned transaction will remain in evm.txes, only the attempt is replaced. (Any receipt // associated with the abandoned attempt would also be lost, although this shouldn't happen since only unconfirmed transactions // can be abandoned.) - _, err := tx.Exec(`DELETE FROM evm.tx_attempts a USING evm.txes t + result, err := tx.Exec(`DELETE FROM evm.tx_attempts a USING evm.txes t WHERE t.id = a.eth_tx_id AND a.hash = $1 AND t.state = $2 AND t.error = 'abandoned'`, attempt.Hash, txmgr.TxFatalError, ) if err == nil { - o.logger.Debugf("Replacing abandoned tx with tx hash %s with tx_id=%d with identical tx hash", attempt.Hash, attempt.TxID) - } else if errors.Is(err, sql.ErrNoRows) { - return err + count, err := result.RowsAffected() + if err != nil { + return pkgerrors.Wrap(err, "UpdateTxUnstartedToInProgress failed to get rows affected") + } + if count > 0 { + o.logger.Debugf("Replacing abandoned tx with tx hash %s with tx_id=%d with identical tx hash", attempt.Hash, attempt.TxID) + } + } else { + return pkgerrors.Wrap(err, "UpdateTxUnstartedToInProgress failed to delete abandoned transactions") } var dbAttempt DbEthTxAttempt From 8743742d827a4717d78ee5c0276b2cf3d88d356e Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Oct 2023 12:17:04 +0200 Subject: [PATCH 004/271] Bump breaking NPM deps (#11003) * bump typechain/hardhat * bump typechain/hardhat * bump nomiclabs/hardhat-etherscan * Pin cbor v5.2.0 dependency * bump hardhat-abi-exporter * bump hardhat-contract-sizer * bump typechain/ethers-v5 * Revert "bump typechain/ethers-v5" This reverts commit ea695e7138115c61ddfd7aa2f6030614e7b16a61. * solhint wrong issue number --------- Co-authored-by: Justin Kaseman --- contracts/.solhintignore | 2 +- contracts/package.json | 11 ++-- contracts/pnpm-lock.yaml | 105 +++++++++++++++++++++++---------------- 3 files changed, 69 insertions(+), 49 deletions(-) diff --git a/contracts/.solhintignore b/contracts/.solhintignore index 53d52657ecb..bc7be4fbfee 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -1,4 +1,4 @@ -# 351 warnings +# 377 warnings #./src/v0.8/automation # Ignore Functions v1.0.0 code that was frozen after audit diff --git a/contracts/package.json b/contracts/package.json index 75b1ddfa973..46b47440a6e 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 351 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 377 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", @@ -40,12 +40,12 @@ "@ethersproject/random": "~5.7.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.9", "@nomiclabs/hardhat-ethers": "^2.2.3", - "@nomiclabs/hardhat-etherscan": "^3.1.0", + "@nomiclabs/hardhat-etherscan": "^3.1.7", "@nomiclabs/hardhat-waffle": "2.0.6", "@openzeppelin/hardhat-upgrades": "1.28.0", "@openzeppelin/test-helpers": "^0.5.16", "@typechain/ethers-v5": "^7.2.0", - "@typechain/hardhat": "^5.0.0", + "@typechain/hardhat": "^7.0.0", "@types/cbor": "5.0.1", "@types/chai": "^4.3.9", "@types/debug": "^4.1.10", @@ -55,6 +55,7 @@ "@typescript-eslint/eslint-plugin": "^6.8.0", "@typescript-eslint/parser": "^6.8.0", "abi-to-sol": "^0.6.6", + "cbor": "^5.2.0", "chai": "^4.3.10", "debug": "^4.3.4", "eslint": "^8.51.0", @@ -64,8 +65,8 @@ "ethereum-waffle": "^3.4.4", "ethers": "~5.7.2", "hardhat": "~2.18.1", - "hardhat-abi-exporter": "^2.2.1", - "hardhat-contract-sizer": "^2.5.1", + "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", diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index 9d54cfa7743..65865f2adf6 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -44,14 +44,14 @@ devDependencies: specifier: ^2.2.3 version: 2.2.3(ethers@5.7.2)(hardhat@2.18.1) '@nomiclabs/hardhat-etherscan': - specifier: ^3.1.0 - version: 3.1.0(hardhat@2.18.1) + specifier: ^3.1.7 + version: 3.1.7(hardhat@2.18.1) '@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.18.1) '@openzeppelin/hardhat-upgrades': specifier: 1.28.0 - version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.0)(ethers@5.7.2)(hardhat@2.18.1) + version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.18.1) '@openzeppelin/test-helpers': specifier: ^0.5.16 version: 0.5.16(bn.js@4.12.0) @@ -59,8 +59,8 @@ devDependencies: 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.2.0)(typescript@5.2.2) '@typechain/hardhat': - specifier: ^5.0.0 - version: 5.0.0(hardhat@2.18.1)(lodash@4.17.21)(typechain@8.2.0) + 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.18.1)(typechain@8.2.0) '@types/cbor': specifier: 5.0.1 version: 5.0.1 @@ -88,6 +88,9 @@ devDependencies: abi-to-sol: specifier: ^0.6.6 version: 0.6.6 + cbor: + specifier: ^5.2.0 + version: 5.2.0 chai: specifier: ^4.3.10 version: 4.3.10 @@ -116,11 +119,11 @@ devDependencies: specifier: ~2.18.1 version: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) hardhat-abi-exporter: - specifier: ^2.2.1 - version: 2.2.1(hardhat@2.18.1) + specifier: ^2.10.1 + version: 2.10.1(hardhat@2.18.1) hardhat-contract-sizer: - specifier: ^2.5.1 - version: 2.5.1(hardhat@2.18.1) + specifier: ^2.10.0 + version: 2.10.0(hardhat@2.18.1) hardhat-gas-reporter: specifier: ^1.0.9 version: 1.0.9(hardhat@2.18.1) @@ -1159,22 +1162,22 @@ packages: hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) dev: true - /@nomiclabs/hardhat-etherscan@3.1.0(hardhat@2.18.1): - resolution: {integrity: sha512-JroYgfN1AlYFkQTQ3nRwFi4o8NtZF7K/qFR2dxDUgHbCtIagkUseca9L4E/D2ScUm4XT40+8PbCdqZi+XmHyQA==} + /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.18.1): + resolution: {integrity: sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==} peerDependencies: hardhat: ^2.0.4 dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/address': 5.7.0 - cbor: 5.2.0 + cbor: 8.1.0 chalk: 2.4.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 7.0.1 hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) lodash: 4.17.21 semver: 6.3.0 - table: 6.8.0 - undici: 5.10.0 + table: 6.8.1 + undici: 5.19.1 transitivePeerDependencies: - supports-color dev: true @@ -1223,7 +1226,7 @@ packages: - encoding dev: true - /@openzeppelin/hardhat-upgrades@1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.0)(ethers@5.7.2)(hardhat@2.18.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.18.1): resolution: {integrity: sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ==} hasBin: true peerDependencies: @@ -1237,7 +1240,7 @@ packages: optional: true dependencies: '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.18.1) - '@nomiclabs/hardhat-etherscan': 3.1.0(hardhat@2.18.1) + '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.18.1) '@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 @@ -1645,16 +1648,22 @@ packages: typescript: 5.2.2 dev: true - /@typechain/hardhat@5.0.0(hardhat@2.18.1)(lodash@4.17.21)(typechain@8.2.0): - resolution: {integrity: sha512-Pqk+KdREbU6Uk3en1Z5caQpWt2bKU+KTOi+6dZwcIXJpF1wKoAwF1cbaYSQEzrG4BSUTM1rHQhW5JHSfeqpsAg==} + /@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.18.1)(typechain@8.2.0): + resolution: {integrity: sha512-XB79i5ewg9Met7gMVGfgVkmypicbnI25T5clJBEooMoW2161p4zvKFpoS2O+lBppQyMrPIZkdvl2M3LMDayVcA==} peerDependencies: - hardhat: ^2.0.10 - lodash: ^4.17.15 - typechain: ^7.0.0 + '@ethersproject/abi': ^5.4.7 + '@ethersproject/providers': ^5.4.7 + '@typechain/ethers-v5': ^11.0.0 + ethers: ^5.4.7 + hardhat: ^2.9.9 + typechain: ^8.2.0 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.2.0)(typescript@5.2.2) + ethers: 5.7.2 fs-extra: 9.1.0 hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) - lodash: 4.17.21 typechain: 8.2.0(typescript@5.2.2) dev: true @@ -3487,6 +3496,13 @@ packages: nofilter: 1.0.4 dev: true + /cbor@8.1.0: + resolution: {integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==} + engines: {node: '>=12.19'} + dependencies: + nofilter: 3.1.0 + dev: true + /cbor@9.0.1: resolution: {integrity: sha512-/TQOWyamDxvVIv+DY9cOLNuABkoyz8K/F3QE56539pGVYohx0+MEA1f4lChFTX79dBTBS7R1PF6ovH7G+VtBfQ==} engines: {node: '>=16'} @@ -4324,6 +4340,17 @@ packages: engines: {node: '>=0.4.0'} dev: true + /delete-empty@3.0.0: + resolution: {integrity: sha512-ZUyiwo76W+DYnKsL3Kim6M/UOavPdBJgDYWOmuQhYaZvJH0AXAHbUNyEDtRbBra8wqqr686+63/0azfEk1ebUQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + ansi-colors: 4.1.3 + minimist: 1.2.8 + path-starts-with: 2.0.1 + rimraf: 2.7.1 + dev: true + /depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -6273,23 +6300,26 @@ packages: har-schema: 2.0.0 dev: true - /hardhat-abi-exporter@2.2.1(hardhat@2.18.1): - resolution: {integrity: sha512-Um7+RPvJEj+OqWjPoPKlTTkO1Akr10pqpgMk8Pw2jz2wrGv5XQBGNW5aQgGVDUosYktUIWDaEhcwwFKbFsir9A==} - engines: {node: '>=12.10.0'} + /hardhat-abi-exporter@2.10.1(hardhat@2.18.1): + resolution: {integrity: sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ==} + engines: {node: '>=14.14.0'} peerDependencies: hardhat: ^2.0.0 dependencies: + '@ethersproject/abi': 5.7.0 + delete-empty: 3.0.0 hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) dev: true - /hardhat-contract-sizer@2.5.1(hardhat@2.18.1): - resolution: {integrity: sha512-28yRb73e30aBVaZOOHTlHZFIdIasA/iFunIehrUviIJTubvdQjtSiQUo2wexHFtt71mQeMPP8qjw2sdbgatDnQ==} + /hardhat-contract-sizer@2.10.0(hardhat@2.18.1): + 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.18.1(ts-node@10.9.1)(typescript@5.2.2) + strip-ansi: 6.0.1 dev: true /hardhat-gas-reporter@1.0.9(hardhat@2.18.1): @@ -8916,6 +8946,11 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-starts-with@2.0.1: + resolution: {integrity: sha512-wZ3AeiRBRlNwkdUxvBANh0+esnt38DLffHDujZyRHkqkaKHTglnY2EP5UX3b8rdeiSutgO4y9NEJwXezNP5vHg==} + engines: {node: '>=8'} + dev: true + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} requiresBuild: true @@ -10730,17 +10765,6 @@ packages: wordwrapjs: 4.0.1 dev: true - /table@6.8.0: - resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==} - engines: {node: '>=10.0.0'} - dependencies: - ajv: 8.11.0 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - /table@6.8.1: resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} engines: {node: '>=10.0.0'} @@ -11244,11 +11268,6 @@ packages: dev: true optional: true - /undici@5.10.0: - resolution: {integrity: sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==} - engines: {node: '>=12.18'} - dev: true - /undici@5.19.1: resolution: {integrity: sha512-YiZ61LPIgY73E7syxCDxxa3LV2yl3sN8spnIuTct60boiiRaE1J8mNWHO8Im2Zi/sFrPusjLlmRPrsyraSqX6A==} engines: {node: '>=12.18'} From 8dd240f7f69804c5103dbdfc8d5008d86139868b Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 25 Oct 2023 12:52:03 +0100 Subject: [PATCH 005/271] [BCF-2725] Add the repository name to the flakey test runner output (#11078) * [BCF-2725] Surface the repo in flakeytestrunner * Use back-quoted strings --- .github/workflows/ci-core.yml | 4 ++++ tools/flakeytests/cmd/runner/main.go | 4 +++- tools/flakeytests/reporter.go | 4 +++- tools/flakeytests/reporter_test.go | 14 +++++++------- tools/flakeytests/utils.go | 24 ++++++++++++++---------- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index d6991bb4e67..74ca1dae9ad 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -156,12 +156,16 @@ jobs: GRAFANA_CLOUD_BASIC_AUTH: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} GITHUB_EVENT_PATH: ${{ github.event_path }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_REPO: ${{ github.repository }} run: | ./runner \ -grafana_auth=$GRAFANA_CLOUD_BASIC_AUTH \ -grafana_host=$GRAFANA_CLOUD_HOST \ -gh_sha=$GITHUB_SHA \ -gh_event_path=$GITHUB_EVENT_PATH \ + -gh_event_name=$GITHUB_EVENT_NAME \ + -gh_repo=$GITHUB_REPO \ -command=./tools/bin/go_core_tests \ `ls -R ./artifacts/go_core_tests*/output.txt` - name: Store logs artifacts diff --git a/tools/flakeytests/cmd/runner/main.go b/tools/flakeytests/cmd/runner/main.go index aea69a134b0..601832a8375 100644 --- a/tools/flakeytests/cmd/runner/main.go +++ b/tools/flakeytests/cmd/runner/main.go @@ -18,6 +18,8 @@ func main() { command := flag.String("command", "", "test command being rerun; used to tag metrics") ghSHA := flag.String("gh_sha", "", "commit sha for which we're rerunning tests") ghEventPath := flag.String("gh_event_path", "", "path to associated gh event") + ghEventName := flag.String("gh_event_name", "", "type of associated gh event") + ghRepo := flag.String("gh_repo", "", "name of gh repository") flag.Parse() if *grafanaHost == "" { @@ -45,7 +47,7 @@ func main() { readers = append(readers, r) } - ctx := flakeytests.GetGithubMetadata(*ghSHA, *ghEventPath) + ctx := flakeytests.GetGithubMetadata(*ghRepo, *ghEventName, *ghSHA, *ghEventPath) rep := flakeytests.NewLokiReporter(*grafanaHost, *grafanaAuth, *command, ctx) r := flakeytests.NewRunner(readers, rep, numReruns) err := r.Run() diff --git a/tools/flakeytests/reporter.go b/tools/flakeytests/reporter.go index beecd8b3e4f..db3890b5c7b 100644 --- a/tools/flakeytests/reporter.go +++ b/tools/flakeytests/reporter.go @@ -33,8 +33,10 @@ type numFlakes struct { } type Context struct { - CommitSHA string `json:"commit_sha,omitempty"` + CommitSHA string `json:"commit_sha"` PullRequestURL string `json:"pull_request_url,omitempty"` + Repository string `json:"repository"` + Type string `json:"event_type"` } type LokiReporter struct { diff --git a/tools/flakeytests/reporter_test.go b/tools/flakeytests/reporter_test.go index f63b89273c9..9cb2c8e9f7d 100644 --- a/tools/flakeytests/reporter_test.go +++ b/tools/flakeytests/reporter_test.go @@ -23,8 +23,8 @@ func TestMakeRequest_SingleTest(t *testing.T) { assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, "{\"package\":\"core/assets\",\"test_name\":\"TestLink\",\"fq_test_name\":\"core/assets:TestLink\"}"}, - {ts, "{\"num_flakes\":1}"}, + {ts, `{"package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink","commit_sha":"","repository":"","event_type":""}`}, + {ts, `{"num_flakes":1,"commit_sha":"","repository":"","event_type":""}`}, }) } @@ -44,9 +44,9 @@ func TestMakeRequest_MultipleTests(t *testing.T) { assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, "{\"package\":\"core/assets\",\"test_name\":\"TestLink\",\"fq_test_name\":\"core/assets:TestLink\"}"}, - {ts, "{\"package\":\"core/assets\",\"test_name\":\"TestCore\",\"fq_test_name\":\"core/assets:TestCore\"}"}, - {ts, "{\"num_flakes\":2}"}, + {ts, `{"package":"core/assets","test_name":"TestLink","fq_test_name":"core/assets:TestLink","commit_sha":"","repository":"","event_type":""}`}, + {ts, `{"package":"core/assets","test_name":"TestCore","fq_test_name":"core/assets:TestCore","commit_sha":"","repository":"","event_type":""}`}, + {ts, `{"num_flakes":2,"commit_sha":"","repository":"","event_type":""}`}, }) } @@ -60,7 +60,7 @@ func TestMakeRequest_NoTests(t *testing.T) { assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, "{\"num_flakes\":0}"}, + {ts, `{"num_flakes":0,"commit_sha":"","repository":"","event_type":""}`}, }) } @@ -74,6 +74,6 @@ func TestMakeRequest_WithContext(t *testing.T) { assert.Len(t, pr.Streams, 1) assert.Equal(t, pr.Streams[0].Stream, map[string]string{"command": "go_core_tests", "app": "flakey-test-reporter"}) assert.ElementsMatch(t, pr.Streams[0].Values, [][]string{ - {ts, "{\"num_flakes\":0,\"commit_sha\":\"42\"}"}, + {ts, `{"num_flakes":0,"commit_sha":"42","repository":"","event_type":""}`}, }) } diff --git a/tools/flakeytests/utils.go b/tools/flakeytests/utils.go index 0b95193b2b5..18ab43980b3 100644 --- a/tools/flakeytests/utils.go +++ b/tools/flakeytests/utils.go @@ -29,7 +29,7 @@ func DigString(mp map[string]interface{}, path []string) (string, error) { return vs, nil } -func GetGithubMetadata(sha string, path string) Context { +func GetGithubMetadata(repo string, eventName string, sha string, path string) Context { event := map[string]interface{}{} if path != "" { r, err := os.Open(path) @@ -48,14 +48,18 @@ func GetGithubMetadata(sha string, path string) Context { } } - prURL := "" - url, err := DigString(event, []string{"pull_request", "_links", "html", "href"}) - if err == nil { - prURL = url - } - ctx := Context{ - CommitSHA: sha, - PullRequestURL: prURL, + basicCtx := &Context{Repository: repo, CommitSHA: sha, Type: eventName} + switch eventName { + case "pull_request": + prURL := "" + url, err := DigString(event, []string{"pull_request", "_links", "html", "href"}) + if err == nil { + prURL = url + } + + basicCtx.PullRequestURL = prURL + return *basicCtx + default: + return *basicCtx } - return ctx } From d8463be40baaf4f75113c51d883b355c8541accf Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 25 Oct 2023 08:07:31 -0500 Subject: [PATCH 006/271] core/internal/testutils/configtest: consolidate v2 and toml packages in to configtest (#11081) --- core/bridges/orm_test.go | 5 +- core/chains/evm/chain_test.go | 5 +- core/chains/evm/config/config_test.go | 2 +- .../evm/forwarders/forwarder_manager_test.go | 2 +- .../evm/headtracker/head_broadcaster_test.go | 2 +- .../evm/headtracker/head_listener_test.go | 2 +- .../chains/evm/headtracker/head_saver_test.go | 2 +- .../evm/headtracker/head_tracker_test.go | 2 +- core/chains/evm/headtracker/orm_test.go | 2 +- core/chains/evm/log/helpers_test.go | 2 +- core/chains/evm/log/orm_test.go | 2 +- core/chains/evm/monitor/balance_test.go | 2 +- core/chains/evm/txmgr/attempts_test.go | 2 +- core/chains/evm/txmgr/broadcaster_test.go | 2 +- core/chains/evm/txmgr/confirmer_test.go | 2 +- core/chains/evm/txmgr/evm_tx_store_test.go | 2 +- core/chains/evm/txmgr/nonce_syncer_test.go | 2 +- core/chains/evm/txmgr/reaper_test.go | 2 +- core/chains/evm/txmgr/resender_test.go | 2 +- core/chains/evm/txmgr/strategies_test.go | 2 +- core/chains/evm/txmgr/txmgr_test.go | 2 +- core/chains/evm/types/models_test.go | 2 +- core/cmd/app_test.go | 26 ++-- core/cmd/p2p_keys_commands_test.go | 3 +- core/cmd/shell_local_test.go | 2 +- core/cmd/shell_remote_test.go | 4 +- core/cmd/shell_test.go | 2 +- core/internal/cltest/cltest.go | 11 +- core/internal/cltest/factories.go | 2 +- core/internal/cltest/heavyweight/orm.go | 9 +- core/internal/cltest/job_factories.go | 5 +- core/internal/features/features_test.go | 10 +- .../testutils/configtest/general_config.go | 122 +++++++++++++++++- .../configtest/{v2/toml => }/toml.go | 2 +- .../testutils/configtest/v2/general_config.go | 120 ----------------- core/internal/testutils/evmtest/v2/evmtest.go | 2 +- core/services/blockhashstore/bhs_test.go | 2 +- core/services/blockhashstore/delegate_test.go | 2 +- .../relayer_chain_interoperators_test.go | 2 +- core/services/cron/cron_test.go | 2 +- core/services/directrequest/delegate_test.go | 2 +- core/services/feeds/orm_test.go | 2 +- core/services/feeds/service_test.go | 2 +- .../fluxmonitorv2/flux_monitor_test.go | 2 +- core/services/fluxmonitorv2/orm_test.go | 2 +- core/services/functions/listener_test.go | 2 +- core/services/job/job_orm_test.go | 2 +- .../job/job_pipeline_orm_integration_test.go | 9 +- core/services/job/orm_test.go | 5 +- core/services/job/runner_integration_test.go | 39 +++--- core/services/job/spawner_test.go | 6 +- core/services/keeper/orm_test.go | 5 +- .../keeper/registry1_1_synchronizer_test.go | 2 +- .../keeper/registry1_2_synchronizer_test.go | 2 +- .../keeper/registry1_3_synchronizer_test.go | 2 +- .../registry_synchronizer_helper_test.go | 2 +- core/services/keeper/upkeep_executer_test.go | 5 +- core/services/keystore/cosmos_test.go | 2 +- core/services/keystore/csa_test.go | 2 +- core/services/keystore/dkgencrypt_test.go | 2 +- core/services/keystore/dkgsign_test.go | 2 +- core/services/keystore/eth_test.go | 2 +- core/services/keystore/master_test.go | 2 +- core/services/keystore/ocr2_test.go | 2 +- core/services/keystore/ocr_test.go | 2 +- core/services/keystore/p2p_test.go | 2 +- core/services/keystore/solana_test.go | 2 +- core/services/keystore/starknet_test.go | 2 +- core/services/keystore/vrf_test.go | 2 +- core/services/ocr/contract_tracker_test.go | 2 +- core/services/ocr/database_test.go | 7 +- core/services/ocr/validate_test.go | 4 +- core/services/ocr2/database_test.go | 7 +- core/services/ocr2/delegate_test.go | 2 +- .../ocr2/plugins/dkg/config/config_test.go | 2 +- core/services/ocr2/validate/validate_test.go | 4 +- core/services/ocrcommon/peer_wrapper_test.go | 4 +- core/services/ocrcommon/peerstore_test.go | 6 +- .../ocrcommon/transmitter_pipeline_test.go | 2 +- core/services/ocrcommon/transmitter_test.go | 2 +- core/services/pg/locked_db_test.go | 2 +- core/services/pipeline/common_test.go | 4 +- core/services/pipeline/orm_test.go | 11 +- core/services/pipeline/runner_test.go | 2 +- core/services/pipeline/task.bridge_test.go | 9 +- core/services/pipeline/task.eth_call_test.go | 2 +- core/services/pipeline/task.eth_tx_test.go | 2 +- core/services/pipeline/task.http_test.go | 2 +- .../promreporter/prom_reporter_test.go | 2 +- core/services/relay/evm/evm_test.go | 5 +- .../relay/evm/relayer_extender_test.go | 2 +- .../relay/evm/request_round_tracker_test.go | 7 +- core/services/vrf/delegate_test.go | 2 +- .../services/vrf/proof/proof_response_test.go | 2 +- .../vrf/v2/integration_v2_plus_test.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 2 +- core/store/migrate/migrate_test.go | 2 +- core/web/cors_test.go | 2 +- core/web/cosmos_chains_controller_test.go | 2 +- core/web/eth_keys_controller_test.go | 2 +- core/web/evm_chains_controller_test.go | 2 +- core/web/evm_forwarders_controller_test.go | 2 +- core/web/evm_transfer_controller_test.go | 6 +- .../external_initiators_controller_test.go | 14 +- core/web/features_controller_test.go | 2 +- core/web/gui_assets_test.go | 2 +- core/web/jobs_controller_test.go | 4 +- core/web/log_controller_test.go | 2 +- core/web/loop_registry_test.go | 2 +- core/web/pipeline_runs_controller_test.go | 2 +- core/web/resolver/eth_key_test.go | 2 +- core/web/resolver/features_test.go | 4 +- core/web/solana_chains_controller_test.go | 2 +- 113 files changed, 327 insertions(+), 320 deletions(-) rename core/internal/testutils/configtest/{v2/toml => }/toml.go (95%) delete mode 100644 core/internal/testutils/configtest/v2/general_config.go diff --git a/core/bridges/orm_test.go b/core/bridges/orm_test.go index db043393350..b110b4f519d 100644 --- a/core/bridges/orm_test.go +++ b/core/bridges/orm_test.go @@ -5,15 +5,16 @@ import ( "time" "github.com/google/uuid" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/chains/evm/chain_test.go b/core/chains/evm/chain_test.go index 09395ff4c97..ba24598ef73 100644 --- a/core/chains/evm/chain_test.go +++ b/core/chains/evm/chain_test.go @@ -4,12 +4,13 @@ import ( "math/big" "testing" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index 10984d45d1e..d0f9e846e37 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 0117c2f2c07..082d329e385 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index c478920e00e..5c2423f328a 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -17,7 +17,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index 63d22233836..dff97f58436 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -17,7 +17,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/headtracker/head_saver_test.go b/core/chains/evm/headtracker/head_saver_test.go index 5ab43679f4b..5ed85adc598 100644 --- a/core/chains/evm/headtracker/head_saver_test.go +++ b/core/chains/evm/headtracker/head_saver_test.go @@ -10,7 +10,7 @@ import ( httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index d30571f331d..502aa4ae6db 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -29,7 +29,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/headtracker/orm_test.go b/core/chains/evm/headtracker/orm_test.go index 5b106ac1018..123478ff902 100644 --- a/core/chains/evm/headtracker/orm_test.go +++ b/core/chains/evm/headtracker/orm_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/ethereum/go-ethereum/common" diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index 58e81132b0f..688757a3e96 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -31,7 +31,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/log/orm_test.go b/core/chains/evm/log/orm_test.go index 365bb354338..48524896cf4 100644 --- a/core/chains/evm/log/orm_test.go +++ b/core/chains/evm/log/orm_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/chains/evm/monitor/balance_test.go b/core/chains/evm/monitor/balance_test.go index dbb2003b695..c80d64e7ef7 100644 --- a/core/chains/evm/monitor/balance_test.go +++ b/core/chains/evm/monitor/balance_test.go @@ -18,7 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index 099143f0ce4..863eae47236 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 6f9308548b3..3901da59eeb 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -37,7 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 32246b06cea..8fbdb7696d9 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -33,7 +33,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index e5b47c457b4..ba02f118cf5 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -15,7 +15,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/txmgr/nonce_syncer_test.go b/core/chains/evm/txmgr/nonce_syncer_test.go index 13e5fd02e8c..f6480b4c305 100644 --- a/core/chains/evm/txmgr/nonce_syncer_test.go +++ b/core/chains/evm/txmgr/nonce_syncer_test.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/txmgr/reaper_test.go b/core/chains/evm/txmgr/reaper_test.go index 830ed1ac17f..20cc27a675f 100644 --- a/core/chains/evm/txmgr/reaper_test.go +++ b/core/chains/evm/txmgr/reaper_test.go @@ -12,7 +12,7 @@ import ( txmgrmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/chains/evm/txmgr/resender_test.go b/core/chains/evm/txmgr/resender_test.go index d17156f4525..cc94511e3b0 100644 --- a/core/chains/evm/txmgr/resender_test.go +++ b/core/chains/evm/txmgr/resender_test.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/txmgr/strategies_test.go b/core/chains/evm/txmgr/strategies_test.go index 9c5a1be37f1..765b43e78f2 100644 --- a/core/chains/evm/txmgr/strategies_test.go +++ b/core/chains/evm/txmgr/strategies_test.go @@ -11,7 +11,7 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" ) func Test_SendEveryStrategy(t *testing.T) { diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index de8c6ff4ef8..6cb43b27716 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -30,7 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index 683a49692b6..2f9dc7dd7c3 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -22,7 +22,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/cmd/app_test.go b/core/cmd/app_test.go index 78331bf7063..bbb00bff3ec 100644 --- a/core/cmd/app_test.go +++ b/core/cmd/app_test.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/config/toml" - testtomlutils "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2/toml" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -88,7 +88,7 @@ func Test_initServerConfig(t *testing.T) { name: "files only", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, }, wantCfg: withDefaults(t, testConfigFileContents, chainlink.Secrets{}), }, @@ -104,7 +104,7 @@ func Test_initServerConfig(t *testing.T) { name: "env overlay of file", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, envVar: testEnvContents, }, wantCfg: withDefaults(t, chainlink.Config{ @@ -124,7 +124,7 @@ func Test_initServerConfig(t *testing.T) { name: "failed to read secrets", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{"/doesnt-exist"}, }, wantErr: true, @@ -133,8 +133,8 @@ func Test_initServerConfig(t *testing.T) { name: "reading secrets", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, - secretsFiles: []string{testtomlutils.WriteTOMLFile(t, testSecretsFileContents, "test_secrets.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + secretsFiles: []string{configtest.WriteTOMLFile(t, testSecretsFileContents, "test_secrets.toml")}, }, wantCfg: withDefaults(t, testConfigFileContents, testSecretsRedactedContents), }, @@ -142,7 +142,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../services/chainlink/testdata/mergingsecretsdata/secrets-database.toml", "../services/chainlink/testdata/mergingsecretsdata/secrets-password.toml", @@ -159,7 +159,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets with overrides: Database", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../testdata/mergingsecretsdata/secrets-database.toml", "../testdata/mergingsecretsdata/secrets-database.toml", @@ -171,7 +171,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets with overrides: Password", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../testdata/mergingsecretsdata/secrets-password.toml", "../testdata/mergingsecretsdata/secrets-password.toml", @@ -183,7 +183,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets with overrides: Pyroscope", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../testdata/mergingsecretsdata/secrets-pyroscope.toml", "../testdata/mergingsecretsdata/secrets-pyroscope.toml", @@ -195,7 +195,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets with overrides: Prometheus", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../testdata/mergingsecretsdata/secrets-prometheus.toml", "../testdata/mergingsecretsdata/secrets-prometheus.toml", @@ -207,7 +207,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets with overrides: Mercury", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../testdata/mergingsecretsdata/secrets-mercury-split-one.toml", "../testdata/mergingsecretsdata/secrets-mercury-split-one.toml", @@ -219,7 +219,7 @@ func Test_initServerConfig(t *testing.T) { name: "reading multiple secrets with overrides: Threshold", args: args{ opts: new(chainlink.GeneralConfigOpts), - fileNames: []string{testtomlutils.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, + fileNames: []string{configtest.WriteTOMLFile(t, testConfigFileContents, "test.toml")}, secretsFiles: []string{ "../testdata/mergingsecretsdata/secrets-threshold.toml", "../testdata/mergingsecretsdata/secrets-threshold.toml", diff --git a/core/cmd/p2p_keys_commands_test.go b/core/cmd/p2p_keys_commands_test.go index cf107ba5071..0407c924575 100644 --- a/core/cmd/p2p_keys_commands_test.go +++ b/core/cmd/p2p_keys_commands_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -24,7 +25,7 @@ func TestP2PKeyPresenter_RenderTable(t *testing.T) { var ( id = "1" - peerID = "12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X" + peerID = configtest.DefaultPeerID pubKey = "somepubkey" buffer = bytes.NewBufferString("") r = cmd.RendererTable{Writer: buffer} diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index be8d5c94056..d70b06f5a98 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/cmd/shell_remote_test.go b/core/cmd/shell_remote_test.go index c1d15df9ec8..7f998225f63 100644 --- a/core/cmd/shell_remote_test.go +++ b/core/cmd/shell_remote_test.go @@ -25,7 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -58,7 +58,7 @@ func startNewApplicationV2(t *testing.T, overrideFn func(c *chainlink.Config, s fn(sopts) } - config := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(30 * time.Millisecond) f := false c.EVM[0].Enabled = &f diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index 74768a21928..9b87e8fb1da 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index a9ff8144b3c..3fa00901779 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -61,7 +61,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/keystest" @@ -108,7 +107,7 @@ const ( // SessionSecret is the hardcoded secret solely used for test SessionSecret = "clsession_test_secret" // DefaultPeerID is the peer ID of the default p2p key - DefaultPeerID = "12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X" + DefaultPeerID = configtest.DefaultPeerID // DefaultOCRKeyBundleID is the ID of the default ocr key bundle DefaultOCRKeyBundleID = "f5bf259689b26f1374efb3c9a9868796953a0f814bb2d39b968d0e61b58620a5" // DefaultOCR2KeyBundleID is the ID of the fixture ocr2 key bundle @@ -240,7 +239,7 @@ func NewWSServer(t *testing.T, chainID *big.Int, callback testutils.JSONRPCHandl func NewApplicationEVMDisabled(t *testing.T) *TestApplication { t.Helper() - c := configtest2.NewGeneralConfig(t, nil) + c := configtest.NewGeneralConfig(t, nil) return NewApplicationWithConfig(t, c) } @@ -250,7 +249,7 @@ func NewApplicationEVMDisabled(t *testing.T) *TestApplication { func NewApplication(t testing.TB, flagsAndDeps ...interface{}) *TestApplication { t.Helper() - c := configtest2.NewGeneralConfig(t, nil) + c := configtest.NewGeneralConfig(t, nil) return NewApplicationWithConfig(t, c, flagsAndDeps...) } @@ -260,7 +259,7 @@ func NewApplication(t testing.TB, flagsAndDeps ...interface{}) *TestApplication func NewApplicationWithKey(t *testing.T, flagsAndDeps ...interface{}) *TestApplication { t.Helper() - config := configtest2.NewGeneralConfig(t, nil) + config := configtest.NewGeneralConfig(t, nil) return NewApplicationWithConfigAndKey(t, config, flagsAndDeps...) } @@ -1617,7 +1616,7 @@ func AssertPipelineTaskRunsErrored(t testing.TB, runs []pipeline.TaskRun) { } func NewTestChainScopedConfig(t testing.TB) evmconfig.ChainScopedConfig { - cfg := configtest2.NewGeneralConfig(t, nil) + cfg := configtest.NewGeneralConfig(t, nil) return evmtest.NewChainScopedConfig(t, cfg) } diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index a1a5d9db6be..85ffc6b02bd 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -34,7 +34,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" diff --git a/core/internal/cltest/heavyweight/orm.go b/core/internal/cltest/heavyweight/orm.go index 841901c25aa..2f9370f35a6 100644 --- a/core/internal/cltest/heavyweight/orm.go +++ b/core/internal/cltest/heavyweight/orm.go @@ -14,13 +14,14 @@ import ( "runtime" "testing" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" @@ -50,7 +51,7 @@ func prepareFullTestDBV2(t testing.TB, name string, empty bool, loadFixtures boo t.Fatal("could not load fixtures into an empty DB") } - gcfg := configtest2.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { + gcfg := configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres if overrideFn != nil { overrideFn(c, s) @@ -68,7 +69,7 @@ func prepareFullTestDBV2(t testing.TB, name string, empty bool, loadFixtures boo os.RemoveAll(gcfg.RootDir()) }) - gcfg = configtest2.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { + gcfg = configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres s.Database.URL = models.MustSecretURL(migrationTestDBURL) if overrideFn != nil { diff --git a/core/internal/cltest/job_factories.go b/core/internal/cltest/job_factories.go index 910ffe79e38..77fee125e21 100644 --- a/core/internal/cltest/job_factories.go +++ b/core/internal/cltest/job_factories.go @@ -5,11 +5,12 @@ import ( "testing" "github.com/google/uuid" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/bridges" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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/job" diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index c5d16590d52..058c8325b9a 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -56,7 +56,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" @@ -83,7 +83,7 @@ func TestIntegration_ExternalInitiatorV2(t *testing.T) { ethClient := cltest.NewEthMocksWithStartupAssertions(t) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) }) @@ -362,7 +362,7 @@ func TestIntegration_DirectRequest(t *testing.T) { t.Run(test.name, func(t *testing.T) { // Simulate a consumer contract calling to obtain ETH quotes in 3 different currencies // in a single callback. - config := configtest2.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config := configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) }) @@ -467,7 +467,7 @@ func setupAppForEthTx(t *testing.T, operatorContracts OperatorContracts) (app *c b := operatorContracts.sim lggr, o := logger.TestLoggerObserved(t, zapcore.DebugLevel) - cfg := configtest2.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfigSimulated(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Listener.FallbackPollInterval = models.MustNewDuration(100 * time.Millisecond) }) app = cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, cfg, b, lggr) @@ -1320,7 +1320,7 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { var initialDefaultGasPrice int64 = 5_000_000_000 maxGasPrice := assets.NewWeiI(10 * initialDefaultGasPrice) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].BalanceMonitor.Enabled = ptr(false) c.EVM[0].GasEstimator.BlockHistory.CheckInclusionBlocks = ptr[uint16](0) c.EVM[0].GasEstimator.PriceDefault = assets.NewWeiI(initialDefaultGasPrice) diff --git a/core/internal/testutils/configtest/general_config.go b/core/internal/testutils/configtest/general_config.go index 1b652eeecdd..93d388b2d30 100644 --- a/core/internal/testutils/configtest/general_config.go +++ b/core/internal/testutils/configtest/general_config.go @@ -1,6 +1,122 @@ package configtest -// TODO move? -const ( - DefaultPeerID = "12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X" +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + "github.com/smartcontractkit/chainlink/v2/core/store/dialects" + "github.com/smartcontractkit/chainlink/v2/core/store/models" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) + +const DefaultPeerID = "12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X" + +// NewTestGeneralConfig returns a new chainlink.GeneralConfig with default test overrides and one chain with evmclient.NullClientChainID. +func NewTestGeneralConfig(t testing.TB) chainlink.GeneralConfig { return NewGeneralConfig(t, nil) } + +// NewGeneralConfig returns a new chainlink.GeneralConfig with overrides. +// The default test overrides are applied before overrideFn, and include one chain with evmclient.NullClientChainID. +func NewGeneralConfig(t testing.TB, overrideFn func(*chainlink.Config, *chainlink.Secrets)) chainlink.GeneralConfig { + tempDir := t.TempDir() + g, err := chainlink.GeneralConfigOpts{ + OverrideFn: func(c *chainlink.Config, s *chainlink.Secrets) { + overrides(c, s) + c.RootDir = &tempDir + if fn := overrideFn; fn != nil { + fn(c, s) + } + }, + }.New() + require.NoError(t, err) + return g +} + +// overrides applies some test config settings and adds a default chain with evmclient.NullClientChainID. +func overrides(c *chainlink.Config, s *chainlink.Secrets) { + s.Password.Keystore = models.NewSecret("dummy-to-pass-validation") + + c.Insecure.OCRDevelopmentMode = ptr(true) + c.InsecureFastScrypt = ptr(true) + c.ShutdownGracePeriod = models.MustNewDuration(testutils.DefaultWaitTimeout) + + c.Database.Dialect = dialects.TransactionWrappedPostgres + c.Database.Lock.Enabled = ptr(false) + c.Database.MaxIdleConns = ptr[int64](20) + c.Database.MaxOpenConns = ptr[int64](20) + c.Database.MigrateOnStartup = ptr(false) + c.Database.DefaultLockTimeout = models.MustNewDuration(1 * time.Minute) + + c.JobPipeline.ReaperInterval = models.MustNewDuration(0) + + c.P2P.V1.Enabled = ptr(false) + c.P2P.V2.Enabled = ptr(false) + + c.WebServer.SessionTimeout = models.MustNewDuration(2 * time.Minute) + c.WebServer.BridgeResponseURL = models.MustParseURL("http://localhost:6688") + testIP := net.ParseIP("127.0.0.1") + c.WebServer.ListenIP = &testIP + c.WebServer.TLS.ListenIP = &testIP + + chainID := utils.NewBigI(evmclient.NullClientChainID) + c.EVM = append(c.EVM, &evmcfg.EVMConfig{ + ChainID: chainID, + Chain: evmcfg.Defaults(chainID), + Nodes: evmcfg.EVMNodes{ + &evmcfg.Node{ + Name: ptr("test"), + WSURL: &models.URL{}, + HTTPURL: &models.URL{}, + SendOnly: new(bool), + Order: ptr[int32](100), + }, + }, + }) +} + +// NewGeneralConfigSimulated returns a new chainlink.GeneralConfig with overrides, including the simulated EVM chain. +// The default test overrides are applied before overrideFn. +// The simulated chain (testutils.SimulatedChainID) replaces the null chain (evmclient.NullClientChainID). +func NewGeneralConfigSimulated(t testing.TB, overrideFn func(*chainlink.Config, *chainlink.Secrets)) chainlink.GeneralConfig { + return NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + simulated(c, s) + if fn := overrideFn; fn != nil { + fn(c, s) + } + }) +} + +// simulated is a config override func that appends the simulated EVM chain (testutils.SimulatedChainID), +// or replaces the null chain (client.NullClientChainID) if that is the only entry. +func simulated(c *chainlink.Config, s *chainlink.Secrets) { + chainID := utils.NewBig(testutils.SimulatedChainID) + enabled := true + cfg := evmcfg.EVMConfig{ + ChainID: chainID, + Chain: evmcfg.Defaults(chainID), + Enabled: &enabled, + Nodes: evmcfg.EVMNodes{&validTestNode}, + } + if len(c.EVM) == 1 && c.EVM[0].ChainID.Cmp(utils.NewBigI(client.NullClientChainID)) == 0 { + c.EVM[0] = &cfg // replace null, if only entry + } else { + c.EVM = append(c.EVM, &cfg) + } +} + +var validTestNode = evmcfg.Node{ + Name: ptr("simulated-node"), + WSURL: models.MustParseURL("WSS://simulated-wss.com/ws"), + HTTPURL: models.MustParseURL("http://simulated.com"), + SendOnly: nil, + Order: ptr(int32(1)), +} + +func ptr[T any](v T) *T { return &v } diff --git a/core/internal/testutils/configtest/v2/toml/toml.go b/core/internal/testutils/configtest/toml.go similarity index 95% rename from core/internal/testutils/configtest/v2/toml/toml.go rename to core/internal/testutils/configtest/toml.go index 0cb89010f25..78db05f9c3c 100644 --- a/core/internal/testutils/configtest/v2/toml/toml.go +++ b/core/internal/testutils/configtest/toml.go @@ -1,4 +1,4 @@ -package testtomlutils +package configtest import ( "os" diff --git a/core/internal/testutils/configtest/v2/general_config.go b/core/internal/testutils/configtest/v2/general_config.go deleted file mode 100644 index febbb367bd5..00000000000 --- a/core/internal/testutils/configtest/v2/general_config.go +++ /dev/null @@ -1,120 +0,0 @@ -package v2 - -import ( - "net" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/store/dialects" - "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -// NewTestGeneralConfig returns a new chainlink.GeneralConfig with default test overrides and one chain with evmclient.NullClientChainID. -func NewTestGeneralConfig(t testing.TB) chainlink.GeneralConfig { return NewGeneralConfig(t, nil) } - -// NewGeneralConfig returns a new chainlink.GeneralConfig with overrides. -// The default test overrides are applied before overrideFn, and include one chain with evmclient.NullClientChainID. -func NewGeneralConfig(t testing.TB, overrideFn func(*chainlink.Config, *chainlink.Secrets)) chainlink.GeneralConfig { - tempDir := t.TempDir() - g, err := chainlink.GeneralConfigOpts{ - OverrideFn: func(c *chainlink.Config, s *chainlink.Secrets) { - overrides(c, s) - c.RootDir = &tempDir - if fn := overrideFn; fn != nil { - fn(c, s) - } - }, - }.New() - require.NoError(t, err) - return g -} - -// overrides applies some test config settings and adds a default chain with evmclient.NullClientChainID. -func overrides(c *chainlink.Config, s *chainlink.Secrets) { - s.Password.Keystore = models.NewSecret("dummy-to-pass-validation") - - c.Insecure.OCRDevelopmentMode = ptr(true) - c.InsecureFastScrypt = ptr(true) - c.ShutdownGracePeriod = models.MustNewDuration(testutils.DefaultWaitTimeout) - - c.Database.Dialect = dialects.TransactionWrappedPostgres - c.Database.Lock.Enabled = ptr(false) - c.Database.MaxIdleConns = ptr[int64](20) - c.Database.MaxOpenConns = ptr[int64](20) - c.Database.MigrateOnStartup = ptr(false) - c.Database.DefaultLockTimeout = models.MustNewDuration(1 * time.Minute) - - c.JobPipeline.ReaperInterval = models.MustNewDuration(0) - - c.P2P.V1.Enabled = ptr(false) - c.P2P.V2.Enabled = ptr(false) - - c.WebServer.SessionTimeout = models.MustNewDuration(2 * time.Minute) - c.WebServer.BridgeResponseURL = models.MustParseURL("http://localhost:6688") - testIP := net.ParseIP("127.0.0.1") - c.WebServer.ListenIP = &testIP - c.WebServer.TLS.ListenIP = &testIP - - chainID := utils.NewBigI(evmclient.NullClientChainID) - c.EVM = append(c.EVM, &evmcfg.EVMConfig{ - ChainID: chainID, - Chain: evmcfg.Defaults(chainID), - Nodes: evmcfg.EVMNodes{ - &evmcfg.Node{ - Name: ptr("test"), - WSURL: &models.URL{}, - HTTPURL: &models.URL{}, - SendOnly: new(bool), - Order: ptr[int32](100), - }, - }, - }) -} - -// NewGeneralConfigSimulated returns a new chainlink.GeneralConfig with overrides, including the simulated EVM chain. -// The default test overrides are applied before overrideFn. -// The simulated chain (testutils.SimulatedChainID) replaces the null chain (evmclient.NullClientChainID). -func NewGeneralConfigSimulated(t testing.TB, overrideFn func(*chainlink.Config, *chainlink.Secrets)) chainlink.GeneralConfig { - return NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - simulated(c, s) - if fn := overrideFn; fn != nil { - fn(c, s) - } - }) -} - -// simulated is a config override func that appends the simulated EVM chain (testutils.SimulatedChainID), -// or replaces the null chain (client.NullClientChainID) if that is the only entry. -func simulated(c *chainlink.Config, s *chainlink.Secrets) { - chainID := utils.NewBig(testutils.SimulatedChainID) - enabled := true - cfg := evmcfg.EVMConfig{ - ChainID: chainID, - Chain: evmcfg.Defaults(chainID), - Enabled: &enabled, - Nodes: evmcfg.EVMNodes{&validTestNode}, - } - if len(c.EVM) == 1 && c.EVM[0].ChainID.Cmp(utils.NewBigI(client.NullClientChainID)) == 0 { - c.EVM[0] = &cfg // replace null, if only entry - } else { - c.EVM = append(c.EVM, &cfg) - } -} - -var validTestNode = evmcfg.Node{ - Name: ptr("simulated-node"), - WSURL: models.MustParseURL("WSS://simulated-wss.com/ws"), - HTTPURL: models.MustParseURL("http://simulated.com"), - SendOnly: nil, - Order: ptr(int32(1)), -} - -func ptr[T any](v T) *T { return &v } diff --git a/core/internal/testutils/evmtest/v2/evmtest.go b/core/internal/testutils/evmtest/v2/evmtest.go index 400690480d7..fa22588c8fb 100644 --- a/core/internal/testutils/evmtest/v2/evmtest.go +++ b/core/internal/testutils/evmtest/v2/evmtest.go @@ -5,7 +5,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/blockhashstore/bhs_test.go b/core/services/blockhashstore/bhs_test.go index ed32b3ab49f..5c501a62ac9 100644 --- a/core/services/blockhashstore/bhs_test.go +++ b/core/services/blockhashstore/bhs_test.go @@ -12,7 +12,7 @@ import ( txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/blockhashstore/delegate_test.go b/core/services/blockhashstore/delegate_test.go index 582492105e0..089e9544af5 100644 --- a/core/services/blockhashstore/delegate_test.go +++ b/core/services/blockhashstore/delegate_test.go @@ -15,7 +15,7 @@ import ( mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 5b17e2e4232..b7291e7dc74 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -29,7 +29,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/plugins" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/cron/cron_test.go b/core/services/cron/cron_test.go index 174f80586ae..19a51a30650 100644 --- a/core/services/cron/cron_test.go +++ b/core/services/cron/cron_test.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index ffd78443cc2..e58dbaeb50c 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/feeds/orm_test.go b/core/services/feeds/orm_test.go index fab9d39a265..746956bbfcd 100644 --- a/core/services/feeds/orm_test.go +++ b/core/services/feeds/orm_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index 744c5d14702..c94a75b3dd5 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index 83ffee8ac57..0d1eb085a84 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -32,7 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" corenull "github.com/smartcontractkit/chainlink/v2/core/null" diff --git a/core/services/fluxmonitorv2/orm_test.go b/core/services/fluxmonitorv2/orm_test.go index 3bebc150c82..0bb08032617 100644 --- a/core/services/fluxmonitorv2/orm_test.go +++ b/core/services/fluxmonitorv2/orm_test.go @@ -16,7 +16,7 @@ import ( txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 6f0badd6e11..007a2a91688 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -21,7 +21,7 @@ import ( log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 20f569a893a..74416e68dce 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -23,7 +23,7 @@ import ( evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/job/job_pipeline_orm_integration_test.go b/core/services/job/job_pipeline_orm_integration_test.go index a5d3ee984da..1158fc46260 100644 --- a/core/services/job/job_pipeline_orm_integration_test.go +++ b/core/services/job/job_pipeline_orm_integration_test.go @@ -4,14 +4,15 @@ import ( "testing" "time" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" @@ -44,7 +45,7 @@ func TestPipelineORM_Integration(t *testing.T) { answer2 [type=bridge name=election_winner index=1]; ` - config := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.HTTPRequest.DefaultTimeout = models.MustNewDuration(30 * time.Millisecond) }) db := pgtest.NewSqlxDB(t) @@ -147,7 +148,7 @@ func TestPipelineORM_Integration(t *testing.T) { t.Run("creates runs", func(t *testing.T) { lggr := logger.TestLogger(t) - cfg := configtest2.NewTestGeneralConfig(t) + cfg := configtest.NewTestGeneralConfig(t) clearJobsDb(t, db) orm := pipeline.NewORM(db, logger.TestLogger(t), cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) btORM := bridges.NewORM(db, logger.TestLogger(t), cfg.Database()) diff --git a/core/services/job/orm_test.go b/core/services/job/orm_test.go index 15a2432f9e9..a6986d7fb32 100644 --- a/core/services/job/orm_test.go +++ b/core/services/job/orm_test.go @@ -3,13 +3,14 @@ package job_test import ( "testing" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" clnull "github.com/smartcontractkit/chainlink/v2/core/null" diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index 05782f95ce8..c0fff1e560a 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -13,39 +13,38 @@ import ( "testing" "time" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" - "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" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/google/uuid" + "github.com/pelletier/go-toml" + "github.com/pkg/errors" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "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" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "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/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/store/models" + "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" - - "github.com/google/uuid" - "github.com/pelletier/go-toml" - "github.com/pkg/errors" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "gopkg.in/guregu/null.v4" ) var monitoringEndpoint = telemetry.MonitoringEndpointGenerator(&telemetry.NoopAgent{}) @@ -58,7 +57,7 @@ func TestRunner(t *testing.T) { _, transmitterAddress := cltest.MustInsertRandomKey(t, ethKeyStore) require.NoError(t, keyStore.OCR().Add(cltest.DefaultOCRKey)) - config := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.P2P.V1.Enabled = ptr(true) c.P2P.V1.DefaultBootstrapPeers = &[]string{ "/dns4/chain.link/tcp/1234/p2p/16Uiu2HAm58SP7UL8zsnpeuwHfytLocaqgnyaYKP8wu7qRdrixLju", @@ -759,7 +758,7 @@ func TestRunner_Success_Callback_AsyncJob(t *testing.T) { ethClient := cltest.NewEthMocksWithStartupAssertions(t) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { t := true c.JobPipeline.ExternalInitiatorsEnabled = &t c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) @@ -941,7 +940,7 @@ func TestRunner_Error_Callback_AsyncJob(t *testing.T) { ethClient := cltest.NewEthMocksWithStartupAssertions(t) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { t := true c.JobPipeline.ExternalInitiatorsEnabled = &t c.Database.Listener.FallbackPollInterval = models.MustNewDuration(10 * time.Millisecond) diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 4e28dcdf062..be4a480a6c9 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -18,7 +18,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" @@ -73,7 +73,7 @@ func (g *relayGetter) Get(id relay.ID) (loop.Relayer, error) { func TestSpawner_CreateJobDeleteJob(t *testing.T) { t.Parallel() - config := configtest2.NewTestGeneralConfig(t) + config := configtest.NewTestGeneralConfig(t) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, config.Database()) ethKeyStore := keyStore.Eth() @@ -264,7 +264,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { }) t.Run("Unregisters filters on 'DeleteJob()'", func(t *testing.T) { - config = configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = func(b bool) *bool { return &b }(true) }) lp := &mocklp.LogPoller{} diff --git a/core/services/keeper/orm_test.go b/core/services/keeper/orm_test.go index 972437eda1d..d990effa103 100644 --- a/core/services/keeper/orm_test.go +++ b/core/services/keeper/orm_test.go @@ -9,13 +9,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/keeper/registry1_1_synchronizer_test.go b/core/services/keeper/registry1_1_synchronizer_test.go index 8444aa50dd4..031b7a59074 100644 --- a/core/services/keeper/registry1_1_synchronizer_test.go +++ b/core/services/keeper/registry1_1_synchronizer_test.go @@ -18,7 +18,7 @@ import ( registry1_1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/keeper/registry1_2_synchronizer_test.go b/core/services/keeper/registry1_2_synchronizer_test.go index f593a638f99..e7d8d6a48a2 100644 --- a/core/services/keeper/registry1_2_synchronizer_test.go +++ b/core/services/keeper/registry1_2_synchronizer_test.go @@ -18,7 +18,7 @@ import ( registry1_2 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_2" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/keeper/registry1_3_synchronizer_test.go b/core/services/keeper/registry1_3_synchronizer_test.go index 53b5cbf983f..a0522fd717e 100644 --- a/core/services/keeper/registry1_3_synchronizer_test.go +++ b/core/services/keeper/registry1_3_synchronizer_test.go @@ -14,7 +14,7 @@ import ( evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" registry1_3 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_3" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/utils" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" diff --git a/core/services/keeper/registry_synchronizer_helper_test.go b/core/services/keeper/registry_synchronizer_helper_test.go index 99fb54eba4d..63dc6343535 100644 --- a/core/services/keeper/registry_synchronizer_helper_test.go +++ b/core/services/keeper/registry_synchronizer_helper_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 39f85aedcd9..7f9698435f8 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -9,11 +9,12 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -24,7 +25,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/keystore/cosmos_test.go b/core/services/keystore/cosmos_test.go index ada7c25a2c4..3c33f16282d 100644 --- a/core/services/keystore/cosmos_test.go +++ b/core/services/keystore/cosmos_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" diff --git a/core/services/keystore/csa_test.go b/core/services/keystore/csa_test.go index ef2dd1f6893..b6dfb009593 100644 --- a/core/services/keystore/csa_test.go +++ b/core/services/keystore/csa_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" diff --git a/core/services/keystore/dkgencrypt_test.go b/core/services/keystore/dkgencrypt_test.go index 1edbc2120fa..36f48f9c2b4 100644 --- a/core/services/keystore/dkgencrypt_test.go +++ b/core/services/keystore/dkgencrypt_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgencryptkey" diff --git a/core/services/keystore/dkgsign_test.go b/core/services/keystore/dkgsign_test.go index d699800b509..5ea23a516be 100644 --- a/core/services/keystore/dkgsign_test.go +++ b/core/services/keystore/dkgsign_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/dkgsignkey" diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index 0fec509a325..4165350300f 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -16,7 +16,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" diff --git a/core/services/keystore/master_test.go b/core/services/keystore/master_test.go index 7a280cc6756..73f636c6625 100644 --- a/core/services/keystore/master_test.go +++ b/core/services/keystore/master_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ) diff --git a/core/services/keystore/ocr2_test.go b/core/services/keystore/ocr2_test.go index b4feb33b5f5..9223538a766 100644 --- a/core/services/keystore/ocr2_test.go +++ b/core/services/keystore/ocr2_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" diff --git a/core/services/keystore/ocr_test.go b/core/services/keystore/ocr_test.go index 5698352ec30..200d62415eb 100644 --- a/core/services/keystore/ocr_test.go +++ b/core/services/keystore/ocr_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ocrkey" diff --git a/core/services/keystore/p2p_test.go b/core/services/keystore/p2p_test.go index 63654786fd1..89cab3e1621 100644 --- a/core/services/keystore/p2p_test.go +++ b/core/services/keystore/p2p_test.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" diff --git a/core/services/keystore/solana_test.go b/core/services/keystore/solana_test.go index 8d7d90a9da8..6e895a56117 100644 --- a/core/services/keystore/solana_test.go +++ b/core/services/keystore/solana_test.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/solkey" diff --git a/core/services/keystore/starknet_test.go b/core/services/keystore/starknet_test.go index 571a809a55a..df9516f8710 100644 --- a/core/services/keystore/starknet_test.go +++ b/core/services/keystore/starknet_test.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/caigo" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" diff --git a/core/services/keystore/vrf_test.go b/core/services/keystore/vrf_test.go index f0c6949bbea..7a2e91ffec3 100644 --- a/core/services/keystore/vrf_test.go +++ b/core/services/keystore/vrf_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" diff --git a/core/services/ocr/contract_tracker_test.go b/core/services/ocr/contract_tracker_test.go index d8fe45e8b33..5684219cf16 100644 --- a/core/services/ocr/contract_tracker_test.go +++ b/core/services/ocr/contract_tracker_test.go @@ -23,7 +23,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/offchain_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/ocr/database_test.go b/core/services/ocr/database_test.go index 8b345df8597..6a72c27aa65 100644 --- a/core/services/ocr/database_test.go +++ b/core/services/ocr/database_test.go @@ -7,14 +7,15 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/ocr" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/services/ocr/validate_test.go b/core/services/ocr/validate_test.go index 3efd2d8381e..0164fd82c54 100644 --- a/core/services/ocr/validate_test.go +++ b/core/services/ocr/validate_test.go @@ -12,7 +12,7 @@ import ( "gopkg.in/guregu/null.v4" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -399,7 +399,7 @@ answer1 [type=median index=0]; for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - c := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = null.BoolFrom(false).Ptr() if tc.overrides != nil { tc.overrides(c, s) diff --git a/core/services/ocr2/database_test.go b/core/services/ocr2/database_test.go index 18c0ec85194..aabb2b33a79 100644 --- a/core/services/ocr2/database_test.go +++ b/core/services/ocr2/database_test.go @@ -7,14 +7,15 @@ import ( medianconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median/config" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/ocr2/delegate_test.go b/core/services/ocr2/delegate_test.go index 3973774c56a..daffac3f96b 100644 --- a/core/services/ocr2/delegate_test.go +++ b/core/services/ocr2/delegate_test.go @@ -13,7 +13,7 @@ import ( txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/ocr2/plugins/dkg/config/config_test.go b/core/services/ocr2/plugins/dkg/config/config_test.go index b49a5277d9c..fe796a9ad6c 100644 --- a/core/services/ocr2/plugins/dkg/config/config_test.go +++ b/core/services/ocr2/plugins/dkg/config/config_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/config" ) diff --git a/core/services/ocr2/validate/validate_test.go b/core/services/ocr2/validate/validate_test.go index 98a13ebf8a7..4685ed745dd 100644 --- a/core/services/ocr2/validate/validate_test.go +++ b/core/services/ocr2/validate/validate_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" medianconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median/config" @@ -584,7 +584,7 @@ KeyID = "6f3b82406688b8ddb944c6f2e6d808f014c8fa8d568d639c25019568c for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - c := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = testutils.Ptr(false) // tests run with OCRDevelopmentMode by default. if tc.overrides != nil { tc.overrides(c, s) diff --git a/core/services/ocrcommon/peer_wrapper_test.go b/core/services/ocrcommon/peer_wrapper_test.go index 45edc64e9db..209bc6b969b 100644 --- a/core/services/ocrcommon/peer_wrapper_test.go +++ b/core/services/ocrcommon/peer_wrapper_test.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -25,7 +25,7 @@ func Test_SingletonPeerWrapper_Start(t *testing.T) { db := pgtest.NewSqlxDB(t) - peerID, err := p2ppeer.Decode("12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X") + peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) require.NoError(t, err) t.Run("with no p2p keys returns error", func(t *testing.T) { diff --git a/core/services/ocrcommon/peerstore_test.go b/core/services/ocrcommon/peerstore_test.go index ba55e0767ab..6e692153564 100644 --- a/core/services/ocrcommon/peerstore_test.go +++ b/core/services/ocrcommon/peerstore_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" @@ -21,7 +21,7 @@ import ( func Test_Peerstore_Start(t *testing.T) { db := pgtest.NewSqlxDB(t) - peerID, err := p2ppeer.Decode("12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X") + peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) require.NoError(t, err) nonExistentP2PPeerID, err := p2ppeer.Decode("12D3KooWAdCzaesXyezatDzgGvCngqsBqoUqnV9PnVc46jsVt2i9") @@ -72,7 +72,7 @@ func Test_Peerstore_Start(t *testing.T) { func Test_Peerstore_WriteToDB(t *testing.T) { db := pgtest.NewSqlxDB(t) - peerID, err := p2ppeer.Decode("12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X") + peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) require.NoError(t, err) cfg := configtest.NewTestGeneralConfig(t) diff --git a/core/services/ocrcommon/transmitter_pipeline_test.go b/core/services/ocrcommon/transmitter_pipeline_test.go index 8a1f2f2a922..e0114d0aa0d 100644 --- a/core/services/ocrcommon/transmitter_pipeline_test.go +++ b/core/services/ocrcommon/transmitter_pipeline_test.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/ocrcommon/transmitter_test.go b/core/services/ocrcommon/transmitter_test.go index ac5d120eb0d..d954da869bc 100644 --- a/core/services/ocrcommon/transmitter_test.go +++ b/core/services/ocrcommon/transmitter_test.go @@ -13,7 +13,7 @@ import ( txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" ) diff --git a/core/services/pg/locked_db_test.go b/core/services/pg/locked_db_test.go index aaf7ebf32c4..a2aebcd57f4 100644 --- a/core/services/pg/locked_db_test.go +++ b/core/services/pg/locked_db_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/pipeline/common_test.go b/core/services/pipeline/common_test.go index 37cc60f27e9..7da80d3af47 100644 --- a/core/services/pipeline/common_test.go +++ b/core/services/pipeline/common_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -323,7 +323,7 @@ func TestTaskRunResult_IsPending(t *testing.T) { func TestSelectGasLimit(t *testing.T) { t.Parallel() - gcfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + gcfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(999)) c.EVM[0].GasEstimator.LimitJobType = toml.GasLimitJobType{ DR: ptr(uint32(100)), diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index 4c03ce16ef8..a487c231fb8 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -5,17 +5,18 @@ import ( "time" "github.com/google/uuid" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" @@ -514,7 +515,7 @@ func Test_GetUnfinishedRuns_Keepers(t *testing.T) { // The test configures single Keeper job with two running tasks. // GetUnfinishedRuns() expects to catch both running tasks. - config := configtest2.NewTestGeneralConfig(t) + config := configtest.NewTestGeneralConfig(t) lggr := logger.TestLogger(t) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, config.Database()) @@ -616,7 +617,7 @@ func Test_GetUnfinishedRuns_DirectRequest(t *testing.T) { // The test configures single DR job with two task runs: one is running and one is suspended. // GetUnfinishedRuns() expects to catch the one that is running. - config := configtest2.NewTestGeneralConfig(t) + config := configtest.NewTestGeneralConfig(t) lggr := logger.TestLogger(t) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, config.Database()) @@ -709,7 +710,7 @@ func Test_Prune(t *testing.T) { n := uint64(2) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.MaxSuccessfulRuns = &n }) lggr, observed := logger.TestLoggerObserved(t, zapcore.DebugLevel) diff --git a/core/services/pipeline/runner_test.go b/core/services/pipeline/runner_test.go index 22b70829ba5..3abcdbe0abe 100644 --- a/core/services/pipeline/runner_test.go +++ b/core/services/pipeline/runner_test.go @@ -26,7 +26,7 @@ import ( bridgesMocks "github.com/smartcontractkit/chainlink/v2/core/bridges/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" diff --git a/core/services/pipeline/task.bridge_test.go b/core/services/pipeline/task.bridge_test.go index 6f542d485e0..03a804c9c12 100644 --- a/core/services/pipeline/task.bridge_test.go +++ b/core/services/pipeline/task.bridge_test.go @@ -24,8 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -208,7 +207,7 @@ func TestBridgeTask_HandlesIntermittentFailure(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {}) + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {}) s1 := httptest.NewServer(fakeIntermittentlyFailingPriceResponder(t, utils.MustUnmarshalToMap(btcUSDPairing), decimal.NewFromInt(9700), "", nil)) defer s1.Close() @@ -270,7 +269,7 @@ func TestBridgeTask_DoesNotReturnStaleResults(t *testing.T) { db := pgtest.NewSqlxDB(t) - cfg := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.WebServer.BridgeCacheTTL = models.MustNewDuration(30 * time.Second) }) queryer := pg.NewQ(db, logger.TestLogger(t), cfg.Database()) @@ -336,7 +335,7 @@ func TestBridgeTask_DoesNotReturnStaleResults(t *testing.T) { require.NoError(t, result2.Error) require.Equal(t, string(big.NewInt(9700).Bytes()), result2.Value) - cfg2 := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + cfg2 := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.WebServer.BridgeCacheTTL = models.MustNewDuration(0 * time.Second) }) task.HelperSetDependencies(cfg2.JobPipeline(), cfg2.WebServer(), orm, specID, uuid.UUID{}, c) diff --git a/core/services/pipeline/task.eth_call_test.go b/core/services/pipeline/task.eth_call_test.go index 77a10681fb4..8fe8bec16c0 100644 --- a/core/services/pipeline/task.eth_call_test.go +++ b/core/services/pipeline/task.eth_call_test.go @@ -17,7 +17,7 @@ import ( txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/pipeline/task.eth_tx_test.go b/core/services/pipeline/task.eth_tx_test.go index af09d793852..e5f50bc29e5 100644 --- a/core/services/pipeline/task.eth_tx_test.go +++ b/core/services/pipeline/task.eth_tx_test.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/pipeline/task.http_test.go b/core/services/pipeline/task.http_test.go index eee3a9aa783..c0dd93df430 100644 --- a/core/services/pipeline/task.http_test.go +++ b/core/services/pipeline/task.http_test.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index f57cf8f45a8..1f15d94418d 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" diff --git a/core/services/relay/evm/evm_test.go b/core/services/relay/evm/evm_test.go index df7cd8eb818..4e9c44a7b93 100644 --- a/core/services/relay/evm/evm_test.go +++ b/core/services/relay/evm/evm_test.go @@ -3,10 +3,11 @@ package evm_test import ( "testing" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) diff --git a/core/services/relay/evm/relayer_extender_test.go b/core/services/relay/evm/relayer_extender_test.go index 361a7468f30..3f4a3749ac8 100644 --- a/core/services/relay/evm/relayer_extender_test.go +++ b/core/services/relay/evm/relayer_extender_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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/services/chainlink" diff --git a/core/services/relay/evm/request_round_tracker_test.go b/core/services/relay/evm/request_round_tracker_test.go index b9f38d54ba3..cb2ee2a8d72 100644 --- a/core/services/relay/evm/request_round_tracker_test.go +++ b/core/services/relay/evm/request_round_tracker_test.go @@ -7,12 +7,13 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -21,7 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 8c522520faf..38b361716b6 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -21,7 +21,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "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" diff --git a/core/services/vrf/proof/proof_response_test.go b/core/services/vrf/proof/proof_response_test.go index 191c18b60d3..24df77d4b32 100644 --- a/core/services/vrf/proof/proof_response_test.go +++ b/core/services/vrf/proof/proof_response_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_verifier_wrapper" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" proof2 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/proof" "github.com/ethereum/go-ethereum/accounts/abi/bind" diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 33eb9f74839..9adf47f256b 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -40,7 +40,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 6dad3173072..093adc8eaaf 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -57,7 +57,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index 7a1e38fb030..fe218589d2d 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" diff --git a/core/web/cors_test.go b/core/web/cors_test.go index cfd82dd8b71..fcd5d9b3874 100644 --- a/core/web/cors_test.go +++ b/core/web/cors_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) diff --git a/core/web/cosmos_chains_controller_test.go b/core/web/cosmos_chains_controller_test.go index f3f5909940f..475ef413528 100644 --- a/core/web/cosmos_chains_controller_test.go +++ b/core/web/cosmos_chains_controller_test.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/web" diff --git a/core/web/eth_keys_controller_test.go b/core/web/eth_keys_controller_test.go index 98641721737..e3a39d541a4 100644 --- a/core/web/eth_keys_controller_test.go +++ b/core/web/eth_keys_controller_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" diff --git a/core/web/evm_chains_controller_test.go b/core/web/evm_chains_controller_test.go index 4ebf06f2b6d..3d5a4e3eedd 100644 --- a/core/web/evm_chains_controller_test.go +++ b/core/web/evm_chains_controller_test.go @@ -14,7 +14,7 @@ import ( evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/web/evm_forwarders_controller_test.go b/core/web/evm_forwarders_controller_test.go index 46820b42337..31e49f20ecc 100644 --- a/core/web/evm_forwarders_controller_test.go +++ b/core/web/evm_forwarders_controller_test.go @@ -13,7 +13,7 @@ import ( evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/web" diff --git a/core/web/evm_transfer_controller_test.go b/core/web/evm_transfer_controller_test.go index 0f3cdc8bb69..14259637b4f 100644 --- a/core/web/evm_transfer_controller_test.go +++ b/core/web/evm_transfer_controller_test.go @@ -15,7 +15,7 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -125,7 +125,7 @@ func TestTransfersController_CreateSuccess_From_BalanceMonitorDisabled(t *testin ethClient.On("PendingNonceAt", mock.Anything, key.Address).Return(uint64(1), nil) ethClient.On("BalanceAt", mock.Anything, key.Address, (*big.Int)(nil)).Return(balance.ToInt(), nil) - config := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].BalanceMonitor.Enabled = ptr(false) }) @@ -285,7 +285,7 @@ func TestTransfersController_CreateSuccess_eip1559(t *testing.T) { ethClient.On("BalanceAt", mock.Anything, key.Address, (*big.Int)(nil)).Return(balance.ToInt(), nil) ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(0), nil).Maybe() - config := configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) c.EVM[0].GasEstimator.Mode = ptr("FixedPrice") c.EVM[0].ChainID = (*utils.Big)(testutils.FixtureChainID) diff --git a/core/web/external_initiators_controller_test.go b/core/web/external_initiators_controller_test.go index 2229b40b7ef..6a1b715b728 100644 --- a/core/web/external_initiators_controller_test.go +++ b/core/web/external_initiators_controller_test.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -69,7 +69,7 @@ func TestExternalInitiatorsController_Index(t *testing.T) { t.Parallel() app := cltest.NewApplicationWithConfig(t, - configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) })) require.NoError(t, app.Start(testutils.Context(t))) @@ -135,7 +135,7 @@ func TestExternalInitiatorsController_Create_success(t *testing.T) { t.Parallel() app := cltest.NewApplicationWithConfig(t, - configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) })) require.NoError(t, app.Start(testutils.Context(t))) @@ -163,7 +163,7 @@ func TestExternalInitiatorsController_Create_without_URL(t *testing.T) { t.Parallel() app := cltest.NewApplicationWithConfig(t, - configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) })) require.NoError(t, app.Start(testutils.Context(t))) @@ -191,7 +191,7 @@ func TestExternalInitiatorsController_Create_invalid(t *testing.T) { t.Parallel() app := cltest.NewApplicationWithConfig(t, - configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) })) require.NoError(t, app.Start(testutils.Context(t))) @@ -209,7 +209,7 @@ func TestExternalInitiatorsController_Delete(t *testing.T) { t.Parallel() app := cltest.NewApplicationWithConfig(t, - configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) })) require.NoError(t, app.Start(testutils.Context(t))) @@ -231,7 +231,7 @@ func TestExternalInitiatorsController_DeleteNotFound(t *testing.T) { t.Parallel() app := cltest.NewApplicationWithConfig(t, - configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.JobPipeline.ExternalInitiatorsEnabled = ptr(true) })) require.NoError(t, app.Start(testutils.Context(t))) diff --git a/core/web/features_controller_test.go b/core/web/features_controller_test.go index 8ef2e08d394..727d7db5476 100644 --- a/core/web/features_controller_test.go +++ b/core/web/features_controller_test.go @@ -6,7 +6,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/gui_assets_test.go b/core/web/gui_assets_test.go index 784b0958f59..137b1231984 100644 --- a/core/web/gui_assets_test.go +++ b/core/web/gui_assets_test.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" clhttptest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/httptest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/web" diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 345662909a4..fc2e8d7a30e 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -25,7 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/directrequest" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -44,7 +44,7 @@ func TestJobsController_Create_ValidationFailure_OffchainReportingSpec(t *testin contractAddress = cltest.NewEIP55Address() ) - peerID, err := p2ppeer.Decode("12D3KooWPjceQrSwdWXPyLLeABRXmuqt69Rg3sBYbU1Nft9HyQ6X") + peerID, err := p2ppeer.Decode(configtest.DefaultPeerID) require.NoError(t, err) randomBytes := testutils.Random32Byte() diff --git a/core/web/log_controller_test.go b/core/web/log_controller_test.go index e4cd1768cef..dbb95361b98 100644 --- a/core/web/log_controller_test.go +++ b/core/web/log_controller_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" diff --git a/core/web/loop_registry_test.go b/core/web/loop_registry_test.go index 59a4d0df686..ea766725641 100644 --- a/core/web/loop_registry_test.go +++ b/core/web/loop_registry_test.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) diff --git a/core/web/pipeline_runs_controller_test.go b/core/web/pipeline_runs_controller_test.go index 5b17fcb007e..c44ee9ae8da 100644 --- a/core/web/pipeline_runs_controller_test.go +++ b/core/web/pipeline_runs_controller_test.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index 26e061b164f..6cac2f4ac4f 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" diff --git a/core/web/resolver/features_test.go b/core/web/resolver/features_test.go index 1d3f5b8ddaf..f14f71abc90 100644 --- a/core/web/resolver/features_test.go +++ b/core/web/resolver/features_test.go @@ -3,7 +3,7 @@ package resolver import ( "testing" - configtest2 "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) @@ -24,7 +24,7 @@ func Test_ToFeatures(t *testing.T) { name: "success", authenticated: true, before: func(f *gqlTestFramework) { - f.App.On("GetConfig").Return(configtest2.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + f.App.On("GetConfig").Return(configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { t, f := true, false c.Feature.UICSAKeys = &f c.Feature.FeedsManager = &t diff --git a/core/web/solana_chains_controller_test.go b/core/web/solana_chains_controller_test.go index 5d6dc7424a2..724d5cd2c3d 100644 --- a/core/web/solana_chains_controller_test.go +++ b/core/web/solana_chains_controller_test.go @@ -18,7 +18,7 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - configtest "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest/v2" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/web" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" From a3817b5d5a9a3686dfcfca54d4859ac926bef680 Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:10:22 +0200 Subject: [PATCH 007/271] Pass docker image and version to ClNode in E2E tests (#11082) * Pass docker image and version to ClNode * Update logging * Fix --- integration-tests/docker/test_env/cl_node.go | 46 +++++++++---------- integration-tests/docker/test_env/test_env.go | 2 +- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index bf4d3285dcf..4c40e641210 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -49,6 +49,8 @@ type ClNode struct { NodeConfig *chainlink.Config `json:"-"` NodeSecretsConfigTOML string `json:"-"` PostgresDb *test_env.PostgresDb `json:"postgresDb"` + UserEmail string `json:"userEmail"` + UserPassword string `json:"userPassword"` t *testing.T l zerolog.Logger lw *logwatch.LogWatch @@ -86,18 +88,22 @@ func WithLogWatch(lw *logwatch.LogWatch) ClNodeOption { } } -func NewClNode(networks []string, nodeConfig *chainlink.Config, opts ...ClNodeOption) *ClNode { +func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *chainlink.Config, opts ...ClNodeOption) *ClNode { nodeDefaultCName := fmt.Sprintf("%s-%s", "cl-node", uuid.NewString()[0:8]) pgDefaultCName := fmt.Sprintf("pg-%s", nodeDefaultCName) pgDb := test_env.NewPostgresDb(networks, test_env.WithPostgresDbContainerName(pgDefaultCName)) n := &ClNode{ EnvComponent: test_env.EnvComponent{ - ContainerName: nodeDefaultCName, - Networks: networks, + ContainerName: nodeDefaultCName, + ContainerImage: imageName, + ContainerVersion: imageVersion, + Networks: networks, }, - NodeConfig: nodeConfig, - PostgresDb: pgDb, - l: log.Logger, + UserEmail: "local@local.com", + UserPassword: "localdevpassword", + NodeConfig: nodeConfig, + PostgresDb: pgDb, + l: log.Logger, } for _, opt := range opts { opt(n) @@ -126,7 +132,7 @@ func (n *ClNode) UpgradeVersion(cfg *chainlink.Config, newImage, newVersion stri return fmt.Errorf("new version is empty") } if newImage == "" { - newImage = os.Getenv("CHAINLINK_IMAGE") + return fmt.Errorf("new image name is empty") } n.ContainerImage = newImage n.ContainerVersion = newVersion @@ -291,14 +297,19 @@ func (n *ClNode) StartContainer() error { if err != nil { return err } - n.l.Info().Str("containerName", n.ContainerName). + n.l.Info(). + Str("containerName", n.ContainerName). + Str("containerImage", n.ContainerImage). + Str("containerVersion", n.ContainerVersion). Str("clEndpoint", clEndpoint). Str("clInternalIP", ip). + Str("userEmail", n.UserEmail). + Str("userPassword", n.UserPassword). Msg("Started Chainlink Node container") clClient, err := client.NewChainlinkClient(&client.ChainlinkConfig{ URL: clEndpoint, - Email: "local@local.com", - Password: "localdevpassword", + Email: n.UserEmail, + Password: n.UserPassword, InternalIP: ip, }, n.l) @@ -360,21 +371,6 @@ func (n *ClNode) getContainerRequest(secrets string) ( adminCredsPath := "/home/admin-credentials.txt" apiCredsPath := "/home/api-credentials.txt" - if n.ContainerImage == "" { - image, ok := os.LookupEnv("CHAINLINK_IMAGE") - if !ok { - return nil, errors.New("CHAINLINK_IMAGE env must be set") - } - n.ContainerImage = image - } - if n.ContainerVersion == "" { - version, ok := os.LookupEnv("CHAINLINK_VERSION") - if !ok { - return nil, errors.New("CHAINLINK_VERSION env must be set") - } - n.ContainerVersion = version - } - return &tc.ContainerRequest{ Name: n.ContainerName, Image: fmt.Sprintf("%s:%s", n.ContainerImage, n.ContainerVersion), diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 9eb9ed9f39b..07b193f102f 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -142,7 +142,7 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i } else { te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { - ocrNode := NewClNode([]string{te.Network.Name}, nodeConfig, + ocrNode := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, WithSecrets(secretsConfig), ) te.ClCluster.Nodes = append(te.ClCluster.Nodes, ocrNode) From 93a4ef9de2046510cb634513e3e97074c89a8386 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 25 Oct 2023 16:08:53 +0200 Subject: [PATCH 008/271] fix several npm audit issues (#11080) * fix: json-schema * bump eth-gas-reporter * fix shelljs * fix minimatch & lodash * run hh on lockfile changes --- .github/workflows/solidity-hardhat.yml | 1 + contracts/pnpm-lock.yaml | 457 ++----------------------- 2 files changed, 32 insertions(+), 426 deletions(-) diff --git a/.github/workflows/solidity-hardhat.yml b/.github/workflows/solidity-hardhat.yml index 2e25e52753d..334ce4d1bae 100644 --- a/.github/workflows/solidity-hardhat.yml +++ b/.github/workflows/solidity-hardhat.yml @@ -28,6 +28,7 @@ jobs: - 'contracts/src/!(v0.8/(llo-feeds|ccip)/**)/**/*' - 'contracts/test/**/*' - 'contracts/package.json' + - 'contracts/pnpm-lock.yaml' - 'contracts/hardhat.config.ts' - 'contracts/ci.json' - '.github/workflows/solidity-hardhat.yml' diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index 65865f2adf6..2b6082d656a 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -126,7 +126,7 @@ devDependencies: version: 2.10.0(hardhat@2.18.1) hardhat-gas-reporter: specifier: ^1.0.9 - version: 1.0.9(hardhat@2.18.1) + version: 1.0.9(debug@4.3.4)(hardhat@2.18.1) hardhat-ignore-warnings: specifier: ^0.2.6 version: 0.2.9 @@ -2156,11 +2156,6 @@ packages: dev: true optional: true - /ansi-colors@3.2.3: - resolution: {integrity: sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==} - engines: {node: '>=6'} - dev: true - /ansi-colors@3.2.4: resolution: {integrity: sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==} engines: {node: '>=6'} @@ -2193,11 +2188,6 @@ packages: engines: {node: '>=4'} dev: true - /ansi-regex@4.1.1: - resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} - engines: {node: '>=6'} - dev: true - /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2330,17 +2320,6 @@ packages: get-intrinsic: 1.2.1 dev: true - /array.prototype.reduce@1.0.4: - resolution: {integrity: sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.3 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 - dev: true - /array.prototype.reduce@1.0.6: resolution: {integrity: sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==} engines: {node: '>= 0.4'} @@ -3460,11 +3439,6 @@ packages: engines: {node: '>=4'} dev: true - /camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: true - /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} @@ -3622,21 +3596,6 @@ packages: parse5-htmlparser2-tree-adapter: 7.0.0 dev: true - /chokidar@3.3.0: - resolution: {integrity: sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.2 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.2.0 - optionalDependencies: - fsevents: 2.1.3 - dev: true - /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -3740,14 +3699,6 @@ packages: wrap-ansi: 2.1.0 dev: true - /cliui@5.0.0: - resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} - dependencies: - string-width: 3.1.0 - strip-ansi: 5.2.0 - wrap-ansi: 5.1.0 - dev: true - /cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: @@ -4136,7 +4087,7 @@ packages: ms: 2.0.0 dev: true - /debug@3.2.6(supports-color@6.0.0): + /debug@3.2.6: resolution: {integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==} deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797) peerDependencies: @@ -4146,7 +4097,6 @@ packages: optional: true dependencies: ms: 2.1.3 - supports-color: 6.0.0 dev: true /debug@3.2.7: @@ -4398,11 +4348,6 @@ packages: - supports-color dev: true - /diff@3.5.0: - resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} - engines: {node: '>=0.3.1'} - dev: true - /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -4518,10 +4463,6 @@ packages: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - /emoji-regex@7.0.3: - resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} - dev: true - /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -4585,36 +4526,6 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract@1.20.3: - resolution: {integrity: sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - es-to-primitive: 1.2.1 - function-bind: 1.1.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.1.3 - get-symbol-description: 1.0.0 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-symbols: 1.0.3 - internal-slot: 1.0.3 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-weakref: 1.0.2 - object-inspect: 1.12.2 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - safe-regex-test: 1.0.0 - string.prototype.trimend: 1.0.5 - string.prototype.trimstart: 1.0.5 - unbox-primitive: 1.0.2 - dev: true - /es-abstract@1.22.2: resolution: {integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==} engines: {node: '>= 0.4'} @@ -4916,29 +4827,31 @@ packages: js-sha3: 0.5.7 dev: true - /eth-gas-reporter@0.2.25: - resolution: {integrity: sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==} + /eth-gas-reporter@0.2.27(debug@4.3.4): + resolution: {integrity: sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==} peerDependencies: '@codechecks/client': ^0.1.0 peerDependenciesMeta: '@codechecks/client': optional: true dependencies: - '@ethersproject/abi': 5.7.0 '@solidity-parser/parser': 0.14.3 + axios: 1.5.1(debug@4.3.4) cli-table3: 0.5.1 colors: 1.4.0 ethereum-cryptography: 1.1.2 - ethers: 4.0.49 + ethers: 5.7.2 fs-readdir-recursive: 1.1.0 lodash: 4.17.21 markdown-table: 1.1.3 - mocha: 7.2.0 + mocha: 10.2.0 req-cwd: 2.0.0 - request: 2.88.2 - request-promise-native: 1.0.9(request@2.88.2) sha1: 1.1.1 sync-request: 6.1.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate dev: true /eth-json-rpc-infura@3.2.1: @@ -5654,13 +5567,6 @@ packages: locate-path: 2.0.0 dev: true - /find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - dependencies: - locate-path: 3.0.0 - dev: true - /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -5700,13 +5606,6 @@ packages: rimraf: 3.0.2 dev: true - /flat@4.1.1: - resolution: {integrity: sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==} - hasBin: true - dependencies: - is-buffer: 2.0.5 - dev: true - /flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true @@ -5862,15 +5761,6 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents@2.1.3: - resolution: {integrity: sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - deprecated: '"Please update to latest v2.3 or v2.2"' - requiresBuild: true - dev: true - optional: true - /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5883,16 +5773,6 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.3 - functions-have-names: 1.2.3 - dev: true - /function.prototype.name@1.1.6: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} @@ -5921,7 +5801,7 @@ packages: bip39: 2.5.0 cachedown: 1.0.0 clone: 2.1.2 - debug: 3.2.6(supports-color@6.0.0) + debug: 3.2.6 encoding-down: 5.0.4 eth-sig-util: 3.0.0 ethereumjs-abi: 0.6.8 @@ -5934,7 +5814,7 @@ packages: heap: 0.2.6 level-sublevel: 6.6.4 levelup: 3.1.1 - lodash: 4.17.20 + lodash: 4.17.21 lru-cache: 5.1.1 merkle-patricia-tree: 3.0.0 patch-package: 6.2.2 @@ -6066,17 +5946,6 @@ packages: path-is-absolute: 1.0.1 dev: true - /glob@7.1.3: - resolution: {integrity: sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - /glob@7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} dependencies: @@ -6268,11 +6137,6 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true - /growl@1.10.5: - resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} - engines: {node: '>=4.x'} - dev: true - /handlebars@4.7.7: resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} engines: {node: '>=0.4.7'} @@ -6322,17 +6186,20 @@ packages: strip-ansi: 6.0.1 dev: true - /hardhat-gas-reporter@1.0.9(hardhat@2.18.1): + /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.18.1): resolution: {integrity: sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==} peerDependencies: hardhat: ^2.0.2 dependencies: array-uniq: 1.0.3 - eth-gas-reporter: 0.2.25 + eth-gas-reporter: 0.2.27(debug@4.3.4) hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' + - bufferutil + - debug + - utf-8-validate dev: true /hardhat-ignore-warnings@0.2.9: @@ -6648,7 +6515,7 @@ packages: engines: {node: '>=0.8', npm: '>=1.3.7'} dependencies: assert-plus: 1.0.0 - jsprim: 1.4.1 + jsprim: 1.4.2 sshpk: 1.16.1 dev: true @@ -6759,15 +6626,6 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true - /internal-slot@1.0.3: - resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.1.3 - has: 1.0.3 - side-channel: 1.0.4 - dev: true - /internal-slot@1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} @@ -6878,12 +6736,6 @@ packages: ci-info: 2.0.0 dev: true - /is-core-module@2.10.0: - resolution: {integrity: sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==} - dependencies: - has: 1.0.3 - dev: true - /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: @@ -7268,14 +7120,6 @@ packages: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true - /js-yaml@3.13.1: - resolution: {integrity: sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - dev: true - /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -7349,8 +7193,8 @@ packages: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} dev: true - /json-schema@0.2.3: - resolution: {integrity: sha512-a3xHnILGMtk+hDOqNwHzF6e2fNbiMrXZvxKQiEv2MlgQP+pjIOzqAmKYD2mDpXYE/44M7g+n9p2bKkYWDUcXCQ==} + /json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} dev: true /json-stable-stringify-without-jsonify@1.0.1: @@ -7413,13 +7257,13 @@ packages: resolution: {integrity: sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==} dev: true - /jsprim@1.4.1: - resolution: {integrity: sha512-4Dj8Rf+fQ+/Pn7C5qeEX02op1WfOss3PKTE9Nsop3Dx+6UPxlm1dr/og7o2cRa5hNN07CACr4NFzRLtj/rjWog==} - engines: {'0': node >=0.6.0} + /jsprim@1.4.2: + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} dependencies: assert-plus: 1.0.0 extsprintf: 1.3.0 - json-schema: 0.2.3 + json-schema: 0.4.0 verror: 1.10.0 dev: true @@ -7680,14 +7524,6 @@ packages: path-exists: 3.0.0 dev: true - /locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - dev: true - /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -7734,13 +7570,6 @@ packages: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /log-symbols@3.0.0: - resolution: {integrity: sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==} - engines: {node: '>=8'} - dependencies: - chalk: 2.4.2 - dev: true - /log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} @@ -8036,12 +7865,6 @@ packages: /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - /minimatch@3.0.4: - resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} - dependencies: - brace-expansion: 1.1.11 - dev: true - /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -8102,13 +7925,6 @@ packages: mkdirp: 1.0.4 dev: true - /mkdirp@0.5.5: - resolution: {integrity: sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==} - hasBin: true - dependencies: - minimist: 1.2.6 - dev: true - /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -8156,37 +7972,6 @@ packages: yargs-unparser: 2.0.0 dev: true - /mocha@7.2.0: - resolution: {integrity: sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==} - engines: {node: '>= 8.10.0'} - hasBin: true - dependencies: - ansi-colors: 3.2.3 - browser-stdout: 1.3.1 - chokidar: 3.3.0 - debug: 3.2.6(supports-color@6.0.0) - diff: 3.5.0 - escape-string-regexp: 1.0.5 - find-up: 3.0.0 - glob: 7.1.3 - growl: 1.10.5 - he: 1.2.0 - js-yaml: 3.13.1 - log-symbols: 3.0.0 - minimatch: 3.0.4 - mkdirp: 0.5.5 - ms: 2.1.1 - node-environment-flags: 1.0.6 - object.assign: 4.1.0 - strip-json-comments: 2.0.1 - supports-color: 6.0.0 - which: 1.3.1 - wide-align: 1.1.3 - yargs: 13.3.2 - yargs-parser: 13.1.2 - yargs-unparser: 1.6.0 - dev: true - /mock-fs@4.12.0: resolution: {integrity: sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ==} requiresBuild: true @@ -8355,13 +8140,6 @@ packages: lodash: 4.17.21 dev: true - /node-environment-flags@1.0.6: - resolution: {integrity: sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==} - dependencies: - object.getownpropertydescriptors: 2.1.4 - semver: 5.7.1 - dev: true - /node-fetch@1.7.3: resolution: {integrity: sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==} dependencies: @@ -8414,7 +8192,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.1 + resolve: 1.22.8 semver: 5.7.1 validate-npm-package-license: 3.0.4 dev: true @@ -8522,16 +8300,6 @@ packages: isobject: 3.0.1 dev: true - /object.assign@4.1.0: - resolution: {integrity: sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==} - engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.1.4 - function-bind: 1.1.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - dev: true - /object.assign@4.1.4: resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} engines: {node: '>= 0.4'} @@ -8542,16 +8310,6 @@ packages: object-keys: 1.1.1 dev: true - /object.getownpropertydescriptors@2.1.4: - resolution: {integrity: sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==} - engines: {node: '>= 0.8'} - dependencies: - array.prototype.reduce: 1.0.4 - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.3 - dev: true - /object.getownpropertydescriptors@2.1.7: resolution: {integrity: sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==} engines: {node: '>= 0.8'} @@ -8726,13 +8484,6 @@ packages: p-limit: 1.3.0 dev: true - /p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - dependencies: - p-limit: 2.3.0 - dev: true - /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -9385,13 +9136,6 @@ packages: util-deprecate: 1.0.2 dev: true - /readdirp@3.2.0: - resolution: {integrity: sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==} - engines: {node: '>= 8'} - dependencies: - picomatch: 2.3.1 - dev: true - /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -9410,7 +9154,7 @@ packages: resolution: {integrity: sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==} engines: {node: '>=0.10.0'} dependencies: - minimatch: 3.0.4 + minimatch: 3.1.2 dev: true /reduce-flatten@2.0.0: @@ -9514,29 +9258,6 @@ packages: resolve-from: 3.0.0 dev: true - /request-promise-core@1.1.4(request@2.88.2): - resolution: {integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==} - engines: {node: '>=0.10.0'} - peerDependencies: - request: ^2.34 - dependencies: - lodash: 4.17.21 - request: 2.88.2 - dev: true - - /request-promise-native@1.0.9(request@2.88.2): - resolution: {integrity: sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==} - engines: {node: '>=0.12.0'} - deprecated: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142 - peerDependencies: - request: ^2.34 - dependencies: - request: 2.88.2 - request-promise-core: 1.1.4(request@2.88.2) - stealthy-require: 1.1.1 - tough-cookie: 2.5.0 - dev: true - /request@2.88.2: resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} engines: {node: '>= 6'} @@ -9583,10 +9304,6 @@ packages: resolution: {integrity: sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==} dev: true - /require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - dev: true - /resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} dev: true @@ -9616,15 +9333,6 @@ packages: path-parse: 1.0.7 dev: true - /resolve@1.22.1: - resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} - hasBin: true - dependencies: - is-core-module: 2.10.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -10013,8 +9721,8 @@ packages: engines: {node: '>=8'} dev: true - /shelljs@0.8.3: - resolution: {integrity: sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==} + /shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} engines: {node: '>=4'} hasBin: true dependencies: @@ -10332,7 +10040,7 @@ packages: recursive-readdir: 2.2.2 sc-istanbul: 0.4.6 semver: 7.5.4 - shelljs: 0.8.3 + shelljs: 0.8.5 web3-utils: 1.8.0 transitivePeerDependencies: - supports-color @@ -10474,11 +10182,6 @@ packages: engines: {node: '>= 0.8'} dev: true - /stealthy-require@1.1.1: - resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} - engines: {node: '>=0.10.0'} - dev: true - /stream-to-pull-stream@1.7.3: resolution: {integrity: sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg==} dependencies: @@ -10518,15 +10221,6 @@ packages: strip-ansi: 4.0.0 dev: true - /string-width@3.1.0: - resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} - engines: {node: '>=6'} - dependencies: - emoji-regex: 7.0.3 - is-fullwidth-code-point: 2.0.0 - strip-ansi: 5.2.0 - dev: true - /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -10545,14 +10239,6 @@ packages: es-abstract: 1.22.2 dev: true - /string.prototype.trimend@1.0.5: - resolution: {integrity: sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.3 - dev: true - /string.prototype.trimend@1.0.7: resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} dependencies: @@ -10561,14 +10247,6 @@ packages: es-abstract: 1.22.2 dev: true - /string.prototype.trimstart@1.0.5: - resolution: {integrity: sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.3 - dev: true - /string.prototype.trimstart@1.0.7: resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} dependencies: @@ -10607,13 +10285,6 @@ packages: ansi-regex: 3.0.1 dev: true - /strip-ansi@5.2.0: - resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} - engines: {node: '>=6'} - dependencies: - ansi-regex: 4.1.1 - dev: true - /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -10650,11 +10321,6 @@ packages: engines: {node: '>=4'} dev: true - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: true - /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -10679,13 +10345,6 @@ packages: has-flag: 3.0.0 dev: true - /supports-color@6.0.0: - resolution: {integrity: sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==} - engines: {node: '>=6'} - dependencies: - has-flag: 3.0.0 - dev: true - /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -11006,7 +10665,7 @@ packages: glob: 7.2.3 mkdirp: 0.5.6 prettier: 2.8.8 - resolve: 1.22.1 + resolve: 1.22.8 ts-essentials: 1.0.4 dev: true @@ -12408,10 +12067,6 @@ packages: resolution: {integrity: sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==} dev: true - /which-module@2.0.0: - resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} - dev: true - /which-typed-array@1.1.11: resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} engines: {node: '>= 0.4'} @@ -12438,12 +12093,6 @@ packages: isexe: 2.0.0 dev: true - /wide-align@1.1.3: - resolution: {integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==} - dependencies: - string-width: 2.1.1 - dev: true - /window-size@0.2.0: resolution: {integrity: sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw==} engines: {node: '>= 0.10.0'} @@ -12479,15 +12128,6 @@ packages: strip-ansi: 3.0.1 dev: true - /wrap-ansi@5.1.0: - resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} - engines: {node: '>=6'} - dependencies: - ansi-styles: 3.2.1 - string-width: 3.1.0 - strip-ansi: 5.2.0 - dev: true - /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -12613,10 +12253,6 @@ packages: resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} dev: true - /y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - dev: true - /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -12640,13 +12276,6 @@ packages: engines: {node: '>= 6'} dev: true - /yargs-parser@13.1.2: - resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: true - /yargs-parser@2.4.1: resolution: {integrity: sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==} dependencies: @@ -12659,15 +12288,6 @@ packages: engines: {node: '>=10'} dev: true - /yargs-unparser@1.6.0: - resolution: {integrity: sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==} - engines: {node: '>=6'} - dependencies: - flat: 4.1.1 - lodash: 4.17.21 - yargs: 13.3.2 - dev: true - /yargs-unparser@2.0.0: resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} engines: {node: '>=10'} @@ -12678,21 +12298,6 @@ packages: is-plain-obj: 2.1.0 dev: true - /yargs@13.3.2: - resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} - dependencies: - cliui: 5.0.0 - find-up: 3.0.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 3.1.0 - which-module: 2.0.0 - y18n: 4.0.3 - yargs-parser: 13.1.2 - dev: true - /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} From d31223c6f9767ef775932d7622dd709f3b7fafdb Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Wed, 25 Oct 2023 17:34:02 +0300 Subject: [PATCH 009/271] VRF-664:add Slack notification for VRF v2 Plus WASP test (#11063) * VRF-664:add Slack notification for VRF v2 Plus WASP test * VRF-664:fixing import cycle * VRF-664:cleanup * VRF-664:adding slack channel secret --- ... on-demand-vrfv2plus-performance-test.yml} | 23 ++-- .../vrfv2plus/vrfv2plus_config/config.go | 2 + integration-tests/load/vrfv2plus/config.go | 23 ++-- integration-tests/load/vrfv2plus/config.toml | 8 +- .../load/vrfv2plus/onchain_monitoring.go | 22 ++-- .../load/vrfv2plus/vrfv2plus_test.go | 117 +++++++++++++----- integration-tests/testreporters/vrfv2plus.go | 91 ++++++++++++++ 7 files changed, 228 insertions(+), 58 deletions(-) rename .github/workflows/{on-demand-vrfv2plus-load-test.yml => on-demand-vrfv2plus-performance-test.yml} (84%) create mode 100644 integration-tests/testreporters/vrfv2plus.go diff --git a/.github/workflows/on-demand-vrfv2plus-load-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml similarity index 84% rename from .github/workflows/on-demand-vrfv2plus-load-test.yml rename to .github/workflows/on-demand-vrfv2plus-performance-test.yml index f4ca096d5ce..b33c6f83133 100644 --- a/.github/workflows/on-demand-vrfv2plus-load-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -1,4 +1,4 @@ -name: On Demand VRFV2 Plus Load Test +name: On Demand VRFV2 Plus Performance Test on: workflow_dispatch: inputs: @@ -49,8 +49,8 @@ on: - "Spike" testDuration: description: Duration of the test (time string) - required: false - default: 1m + required: true + default: 5m useExistingEnv: description: Set `true` to use existing environment or `false` to deploy CL node and all contracts required: false @@ -59,8 +59,8 @@ on: description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) required: false jobs: - vrfv2plus_load_test: - name: ${{ inputs.network }} VRFV2 Plus Load Test + vrfv2plus_performance_test: + name: ${{ inputs.network }} VRFV2 Plus Performance Test environment: integration runs-on: ubuntu20.04-8cores-32GB permissions: @@ -80,6 +80,8 @@ jobs: REF_NAME: ${{ github.head_ref || github.ref_name }} CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} + SLACK_CHANNEL: ${{ secrets.QA_VRF_SLACK_CHANNEL }} WASP_LOG_LEVEL: info steps: - name: Collect Metrics @@ -88,8 +90,15 @@ jobs: with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ${{ inputs.network }} VRFV2 Plus Load Test + this-job-name: ${{ inputs.network }} VRFV2 Plus Performance Test continue-on-error: true + - name: Setup Push Tag + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY - name: Get Inputs run: | EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) @@ -111,7 +120,7 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 with: - test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 6h -run TestVRFV2PlusLoad/vrfv2plus_soak_test ./load/vrfv2plus + test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 6h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ inputs.chainlinkImage }} cl_image_tag: ${{ inputs.chainlinkVersion }} diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go index 7a1221eaf8b..caee353294f 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go @@ -18,6 +18,8 @@ type VRFV2PlusConfig struct { FulfillmentFlatFeeLinkPPM uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM" default:"500"` // Flat fee in ppm for LINK for the VRF Coordinator config FulfillmentFlatFeeNativePPM uint32 `envconfig:"FULFILLMENT_FLAT_FEE_NATIVE_PPM" default:"500"` // Flat fee in ppm for native currency for the VRF Coordinator config + NumberOfSubToCreate int `envconfig:"NUMBER_OF_SUB_TO_CREATE" default:"1"` // Number of subscriptions to create + RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request RandomnessRequestCountPerRequestDeviation uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST_DEVIATION" default:"0"` // How many randomness requests to send per request diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index 5f3babfeab0..329e4abf135 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -12,6 +12,10 @@ import ( const ( DefaultConfigFilename = "config.toml" + SoakTestType = "Soak" + LoadTestType = "Load" + StressTestType = "Stress" + SpikeTestType = "Spike" ErrReadPerfConfig = "failed to read TOML config for performance tests" ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" @@ -38,7 +42,6 @@ type ExistingEnvConfig struct { type NewEnvConfig struct { Funding - NumberOfSubToCreate int `toml:"number_of_sub_to_create"` } type Common struct { @@ -68,6 +71,8 @@ type Spike struct { } type PerformanceTestConfig struct { + NumberOfSubToCreate int `toml:"number_of_sub_to_create"` + RPS int64 `toml:"rps"` //Duration *models.Duration `toml:"duration"` RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` @@ -101,24 +106,28 @@ func ReadConfig() (*PerformanceConfig, error) { return cfg, nil } -func SetPerformanceTestConfig(vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, cfg *PerformanceConfig) { - switch os.Getenv("TEST_TYPE") { - case "Soak": +func SetPerformanceTestConfig(testType string, vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, cfg *PerformanceConfig) { + switch testType { + case SoakTestType: + vrfv2PlusConfig.NumberOfSubToCreate = cfg.Soak.NumberOfSubToCreate vrfv2PlusConfig.RPS = cfg.Soak.RPS vrfv2PlusConfig.RateLimitUnitDuration = cfg.Soak.RateLimitUnitDuration.Duration() vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Soak.RandomnessRequestCountPerRequest vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Soak.RandomnessRequestCountPerRequestDeviation - case "Load": + case LoadTestType: + vrfv2PlusConfig.NumberOfSubToCreate = cfg.Load.NumberOfSubToCreate vrfv2PlusConfig.RPS = cfg.Load.RPS vrfv2PlusConfig.RateLimitUnitDuration = cfg.Load.RateLimitUnitDuration.Duration() vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Load.RandomnessRequestCountPerRequest vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Load.RandomnessRequestCountPerRequestDeviation - case "Stress": + case StressTestType: + vrfv2PlusConfig.NumberOfSubToCreate = cfg.Stress.NumberOfSubToCreate vrfv2PlusConfig.RPS = cfg.Stress.RPS vrfv2PlusConfig.RateLimitUnitDuration = cfg.Stress.RateLimitUnitDuration.Duration() vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Stress.RandomnessRequestCountPerRequest vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation = cfg.Stress.RandomnessRequestCountPerRequestDeviation - case "Spike": + case SpikeTestType: + vrfv2PlusConfig.NumberOfSubToCreate = cfg.Spike.NumberOfSubToCreate vrfv2PlusConfig.RPS = cfg.Spike.RPS vrfv2PlusConfig.RateLimitUnitDuration = cfg.Spike.RateLimitUnitDuration.Duration() vrfv2PlusConfig.RandomnessRequestCountPerRequest = cfg.Spike.RandomnessRequestCountPerRequest diff --git a/integration-tests/load/vrfv2plus/config.toml b/integration-tests/load/vrfv2plus/config.toml index 1208423dc0d..31a0bf56652 100644 --- a/integration-tests/load/vrfv2plus/config.toml +++ b/integration-tests/load/vrfv2plus/config.toml @@ -6,7 +6,7 @@ minimum_confirmations = 3 sub_funds_link = 1000 sub_funds_native = 1000 node_funds = 10 -number_of_sub_to_create = 10 + [ExistingEnvConfig] coordinator_address = "0x4931Ce2e341398c8eD8A5D0F6ADb920476D6DaBb" @@ -21,6 +21,7 @@ rate_limit_unit_duration = "6s" rps = 1 randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 # approx 60 RPM - 1 tx request with 4 rand requests in each tx every 3 seconds [Load] @@ -28,13 +29,15 @@ rate_limit_unit_duration = "3s" rps = 1 randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 10 # approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx [Stress] -rate_limit_unit_duration = "0" +rate_limit_unit_duration = "1s" rps = 3 randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 20 # approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds [Spike] @@ -42,3 +45,4 @@ rate_limit_unit_duration = "1m" rps = 1 randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 \ No newline at end of file diff --git a/integration-tests/load/vrfv2plus/onchain_monitoring.go b/integration-tests/load/vrfv2plus/onchain_monitoring.go index 0ae27fe6be0..c56d835234e 100644 --- a/integration-tests/load/vrfv2plus/onchain_monitoring.go +++ b/integration-tests/load/vrfv2plus/onchain_monitoring.go @@ -3,7 +3,7 @@ package loadvrfv2plus import ( "context" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/wasp" "testing" "time" @@ -18,11 +18,12 @@ const ( ErrLokiPush = "failed to push monitoring metrics to Loki" ) -func MonitorLoadStats(lc *wasp.LokiClient, vrfv2PlusContracts *vrfv2plus.VRFV2_5Contracts, labels map[string]string) { +func MonitorLoadStats(lc *wasp.LokiClient, consumer contracts.VRFv2PlusLoadTestConsumer, labels map[string]string) { go func() { for { time.Sleep(1 * time.Second) - SendLoadTestMetricsToLoki(vrfv2PlusContracts, lc, labels) + metrics := GetLoadTestMetrics(consumer) + SendMetricsToLoki(metrics, lc, labels) } }() } @@ -38,13 +39,16 @@ func UpdateLabels(labels map[string]string, t *testing.T) map[string]string { return updatedLabels } -func SendLoadTestMetricsToLoki(vrfv2PlusContracts *vrfv2plus.VRFV2_5Contracts, lc *wasp.LokiClient, updatedLabels map[string]string) { - //todo - should work with multiple consumers and consumers having different keyhashes and wallets - metrics, err := vrfv2PlusContracts.LoadTestConsumers[0].GetLoadTestMetrics(context.Background()) - if err != nil { - log.Error().Err(err).Msg(ErrMetrics) - } +func SendMetricsToLoki(metrics *contracts.VRFLoadTestMetrics, lc *wasp.LokiClient, updatedLabels map[string]string) { if err := lc.HandleStruct(wasp.LabelsMapToModel(updatedLabels), time.Now(), metrics); err != nil { log.Error().Err(err).Msg(ErrLokiPush) } } + +func GetLoadTestMetrics(consumer contracts.VRFv2PlusLoadTestConsumer) *contracts.VRFLoadTestMetrics { + metrics, err := consumer.GetLoadTestMetrics(context.Background()) + if err != nil { + log.Error().Err(err).Msg(ErrMetrics) + } + return metrics +} diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 5c3ea6e8c6d..a3cb4d4b7f5 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -4,7 +4,9 @@ import ( "context" "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" + "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" "math/big" @@ -20,21 +22,46 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) -func TestVRFV2PlusLoad(t *testing.T) { +var ( + env *test_env.CLClusterTestEnv + vrfv2PlusContracts *vrfv2plus.VRFV2_5Contracts + vrfv2PlusData *vrfv2plus.VRFV2PlusData + subIDs []*big.Int + + labels = map[string]string{ + "branch": "vrfv2Plus_healthcheck", + "commit": "vrfv2Plus_healthcheck", + } + + testType = os.Getenv("TEST_TYPE") +) + +func TestVRFV2PlusPerformance(t *testing.T) { cfg, err := ReadConfig() require.NoError(t, err) var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig err = envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) require.NoError(t, err) - SetPerformanceTestConfig(&vrfv2PlusConfig, cfg) + testReporter := &testreporters.VRFV2PlusTestReporter{} + + SetPerformanceTestConfig(testType, &vrfv2PlusConfig, cfg) l := logging.GetTestLogger(t) //todo: temporary solution with envconfig and toml config until VRF-662 is implemented vrfv2PlusConfig.MinimumConfirmations = cfg.Common.MinimumConfirmations + lokiConfig := wasp.NewEnvLokiConfig() + lc, err := wasp.NewLokiClient(lokiConfig) + if err != nil { + l.Error().Err(err).Msg(ErrLokiClient) + return + } + + updatedLabels := UpdateLabels(labels, t) + l.Info(). - Str("Test Type", os.Getenv("TEST_TYPE")). + Str("Test Type", testType). Str("Test Duration", vrfv2PlusConfig.TestDuration.Truncate(time.Second).String()). Int64("RPS", vrfv2PlusConfig.RPS). Str("RateLimitUnitDuration", vrfv2PlusConfig.RateLimitUnitDuration.String()). @@ -43,11 +70,6 @@ func TestVRFV2PlusLoad(t *testing.T) { Bool("UseExistingEnv", vrfv2PlusConfig.UseExistingEnv). Msg("Performance Test Configuration") - var env *test_env.CLClusterTestEnv - var vrfv2PlusContracts *vrfv2plus.VRFV2_5Contracts - var vrfv2PlusData *vrfv2plus.VRFV2PlusData - var subIDs []*big.Int - if vrfv2PlusConfig.UseExistingEnv { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented vrfv2PlusConfig.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress @@ -57,7 +79,10 @@ func TestVRFV2PlusLoad(t *testing.T) { env, err = test_env.NewCLTestEnvBuilder(). WithTestLogger(t). - WithoutCleanup(). + WithCustomCleanup( + func() { + teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) + }). Build() require.NoError(t, err, "error creating test env") @@ -93,13 +118,18 @@ func TestVRFV2PlusLoad(t *testing.T) { vrfv2PlusConfig.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeFunds vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.NewEnvConfig.Funding.SubFundsNative - numberOfSubToCreate := cfg.NewEnvConfig.NumberOfSubToCreate env, err = test_env.NewCLTestEnvBuilder(). WithTestLogger(t). WithGeth(). WithCLNodes(1). WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). - WithStandardCleanup(). + WithCustomCleanup( + func() { + teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + }). WithLogWatcher(). Build() @@ -113,29 +143,24 @@ func TestVRFV2PlusLoad(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err = vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkToken, mockETHLinkFeed, 1, numberOfSubToCreate) + vrfv2PlusContracts, subIDs, vrfv2PlusData, err = vrfv2plus.SetupVRFV2_5Environment( + env, + &vrfv2PlusConfig, + linkToken, + mockETHLinkFeed, + 1, + vrfv2PlusConfig.NumberOfSubToCreate, + ) require.NoError(t, err, "error setting up VRF v2_5 env") } - l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs Involved in Load Test") + l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) require.NoError(t, err, "error getting subscription information for subscription %s", subID.String()) vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) } - labels := map[string]string{ - "branch": "vrfv2Plus_healthcheck", - "commit": "vrfv2Plus_healthcheck", - } - - lokiConfig := wasp.NewEnvLokiConfig() - lc, err := wasp.NewLokiClient(lokiConfig) - if err != nil { - l.Error().Err(err).Msg(ErrLokiClient) - return - } - singleFeedConfig := &wasp.Config{ T: t, LoadType: wasp.RPS, @@ -156,11 +181,10 @@ func TestVRFV2PlusLoad(t *testing.T) { consumer := vrfv2PlusContracts.LoadTestConsumers[0] err = consumer.ResetMetrics() require.NoError(t, err) - updatedLabels := UpdateLabels(labels, t) - MonitorLoadStats(lc, vrfv2PlusContracts, updatedLabels) + MonitorLoadStats(lc, consumer, updatedLabels) // is our "job" stable at all, no memory leaks, no flaking performance under some RPS? - t.Run("vrfv2plus soak test", func(t *testing.T) { + t.Run("vrfv2plus performance test", func(t *testing.T) { singleFeedConfig.Schedule = wasp.Plain( vrfv2PlusConfig.RPS, @@ -172,17 +196,44 @@ func TestVRFV2PlusLoad(t *testing.T) { require.NoError(t, err) var wg sync.WaitGroup - wg.Add(1) + //todo - timeout should be configurable depending on the perf test type requestCount, fulfilmentCount, err := vrfv2plus.WaitForRequestCountEqualToFulfilmentCount(consumer, 30*time.Second, &wg) + require.NoError(t, err) + wg.Wait() + l.Info(). Interface("Request Count", requestCount). Interface("Fulfilment Count", fulfilmentCount). Msg("Final Request/Fulfilment Stats") - require.NoError(t, err) - wg.Wait() - //send final results - SendLoadTestMetricsToLoki(vrfv2PlusContracts, lc, updatedLabels) }) +} +func teardown( + t *testing.T, + consumer contracts.VRFv2PlusLoadTestConsumer, + lc *wasp.LokiClient, updatedLabels map[string]string, + testReporter *testreporters.VRFV2PlusTestReporter, + testType string, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, +) { + //send final results to Loki + metrics := GetLoadTestMetrics(consumer) + SendMetricsToLoki(metrics, lc, updatedLabels) + //set report data for Slack notification + testReporter.SetReportData( + testType, + metrics.RequestCount, + metrics.FulfilmentCount, + metrics.AverageFulfillmentInMillions, + metrics.SlowestFulfillment, + metrics.FastestFulfillment, + vrfv2PlusConfig, + ) + + // send Slack notification + err := testReporter.SendSlackNotification(t, nil) + if err != nil { + log.Warn().Err(err).Msg("Error sending Slack notification") + } } diff --git a/integration-tests/testreporters/vrfv2plus.go b/integration-tests/testreporters/vrfv2plus.go new file mode 100644 index 00000000000..83d4678dfdd --- /dev/null +++ b/integration-tests/testreporters/vrfv2plus.go @@ -0,0 +1,91 @@ +package testreporters + +import ( + "fmt" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" + "math/big" + "os" + "testing" + "time" + + "github.com/slack-go/slack" + + "github.com/smartcontractkit/chainlink-testing-framework/testreporters" +) + +type VRFV2PlusTestReporter struct { + TestType string + RequestCount *big.Int + FulfilmentCount *big.Int + AverageFulfillmentInMillions *big.Int + SlowestFulfillment *big.Int + FastestFulfillment *big.Int + Vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig +} + +func (o *VRFV2PlusTestReporter) SetReportData( + testType string, + RequestCount *big.Int, + FulfilmentCount *big.Int, + AverageFulfillmentInMillions *big.Int, + SlowestFulfillment *big.Int, + FastestFulfillment *big.Int, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, +) { + o.TestType = testType + o.RequestCount = RequestCount + o.FulfilmentCount = FulfilmentCount + o.AverageFulfillmentInMillions = AverageFulfillmentInMillions + o.SlowestFulfillment = SlowestFulfillment + o.FastestFulfillment = FastestFulfillment + o.Vrfv2PlusConfig = &vrfv2PlusConfig +} + +// SendSlackNotification sends a slack message to a slack webhook +func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { + if slackClient == nil { + slackClient = slack.New(testreporters.SlackAPIKey) + } + + testFailed := t.Failed() + headerText := fmt.Sprintf(":white_check_mark: VRF %s Test PASSED :white_check_mark:", o.TestType) + if testFailed { + headerText = fmt.Sprintf(":x: VRF %s Test FAILED :x:", o.TestType) + } + + messageBlocks := testreporters.SlackNotifyBlocks(headerText, fmt.Sprintf("%s", os.Getenv("SELECTED_NETWORKS")), []string{ + fmt.Sprintf( + "Summary\n"+ + "Perf Test Type: %s\n"+ + "Test Duration set in parameters: %s\n"+ + "Use Existing Env: %t\n"+ + "Request Count: %s\n"+ + "Fulfilment Count: %s\n"+ + "AverageFulfillmentInMillions: %s\n"+ + "Slowest Fulfillment: %s\n"+ + "Fastest Fulfillment: %s \n"+ + "RPS: %d\n"+ + "RateLimitUnitDuration: %s\n"+ + "RandomnessRequestCountPerRequest: %d\n"+ + "RandomnessRequestCountPerRequestDeviation: %d\n", + o.TestType, + o.Vrfv2PlusConfig.TestDuration.Truncate(time.Second).String(), + o.Vrfv2PlusConfig.UseExistingEnv, + o.RequestCount.String(), + o.FulfilmentCount.String(), + o.AverageFulfillmentInMillions.String(), + o.SlowestFulfillment.String(), + o.FastestFulfillment.String(), + o.Vrfv2PlusConfig.RPS, + o.Vrfv2PlusConfig.RateLimitUnitDuration.String(), + o.Vrfv2PlusConfig.RandomnessRequestCountPerRequest, + o.Vrfv2PlusConfig.RandomnessRequestCountPerRequestDeviation, + ), + }) + + _, err := testreporters.SendSlackMessage(slackClient, slack.MsgOptionBlocks(messageBlocks...)) + if err != nil { + return err + } + return nil +} From 3e75b262f11603383609e2a5757d5170adb07e88 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Wed, 25 Oct 2023 08:20:04 -0700 Subject: [PATCH 010/271] Bump version and update CHANGELOG for core v2.7.0 (#11072) --- VERSION | 2 +- docs/CHANGELOG.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index e70b4523ae7..24ba9a38de6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.0 +2.7.0 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 469cd0d1686..44d018769ec 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] +... + +## 2.7.0 - UNRELEASED + ### Added - Added new configuration field named `LeaseDuration` for `EVM.NodePool` that will periodically check if internal subscriptions are connected to the "best" (as defined by the `SelectionMode`) node and switch to it if necessary. Setting this value to `0s` will disable this feature. From 4464dffc90686c05868f26f52d669ae8192aa18e Mon Sep 17 00:00:00 2001 From: Erik Burton Date: Wed, 25 Oct 2023 11:05:09 -0700 Subject: [PATCH 011/271] [RE-2009] Bump action references (#11067) * Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...3df4ab11eba7bda6032a0b82a6bb43b11571feac) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * manual: Bump actions/checkout from 3 to 4 * manual: Bump actions/setup-node from 3 to 4 * manual: Bump aws-actions/amazon-ecr-login from 1 to 2 * manual: Bump aws-actions/configure-aws-credentials from 2 to 4 * fix: Solana Build Artifacts job --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../build-sign-publish-chainlink/action.yml | 2 +- .github/actions/delete-deployments/action.yml | 4 +-- .github/actions/golangci-lint/action.yml | 2 +- .../goreleaser-build-sign-publish/README.md | 4 +-- .github/actions/setup-nodejs/action.yaml | 4 +-- .github/actions/split-tests/action.yaml | 2 +- .../workflows/automation-benchmark-tests.yml | 2 +- .../workflows/automation-ondemand-tests.yml | 6 ++-- .github/workflows/build-publish-develop.yml | 2 +- .github/workflows/build-publish.yml | 4 +-- .github/workflows/build.yml | 2 +- .github/workflows/ci-chaincli.yml | 2 +- .github/workflows/ci-core.yml | 14 ++++---- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/delete-deployments.yml | 2 +- .github/workflows/dependency-check.yml | 4 +-- .../goreleaser-build-publish-develop.yml | 8 ++--- .github/workflows/helm-publish.yml | 2 +- .github/workflows/integration-chaos-tests.yml | 6 ++-- .../workflows/integration-staging-tests.yml | 2 +- .../workflows/integration-tests-publish.yml | 2 +- .github/workflows/integration-tests.yml | 32 ++++++++++--------- .github/workflows/lint-gh-workflows.yml | 2 +- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- .../on-demand-vrfv2plus-performance-test.yml | 2 +- .github/workflows/operator-ui-cd.yml | 4 +-- .github/workflows/operator-ui-ci.yml | 2 +- .github/workflows/performance-tests.yml | 8 ++--- .github/workflows/solidity-foundry.yml | 4 +-- .github/workflows/solidity-hardhat.yml | 10 +++--- .github/workflows/solidity.yml | 12 +++---- ...evelop-from-smartcontractkit-chainlink.yml | 2 +- 32 files changed, 80 insertions(+), 78 deletions(-) diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml index 853702045e8..bd633bced74 100644 --- a/.github/actions/build-sign-publish-chainlink/action.yml +++ b/.github/actions/build-sign-publish-chainlink/action.yml @@ -99,7 +99,7 @@ runs: - if: inputs.publish == 'true' # Log in to AWS for publish to ECR name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ inputs.aws-role-to-assume }} role-duration-seconds: ${{ inputs.aws-role-duration-seconds }} diff --git a/.github/actions/delete-deployments/action.yml b/.github/actions/delete-deployments/action.yml index 20b7d0eefe5..5fc7ef0287b 100644 --- a/.github/actions/delete-deployments/action.yml +++ b/.github/actions/delete-deployments/action.yml @@ -29,11 +29,11 @@ inputs: runs: using: composite steps: - - uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd #v2.2.4 + - uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd # v2.2.4 with: version: ^8.0.0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 with: node-version: "18" cache: "pnpm" diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index 5c4882e2684..c0aeb529c1e 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -30,7 +30,7 @@ inputs: runs: using: composite steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Go uses: ./.github/actions/setup-go with: diff --git a/.github/actions/goreleaser-build-sign-publish/README.md b/.github/actions/goreleaser-build-sign-publish/README.md index 49edfb25d51..d6bf7e6fd4a 100644 --- a/.github/actions/goreleaser-build-sign-publish/README.md +++ b/.github/actions/goreleaser-build-sign-publish/README.md @@ -25,9 +25,9 @@ jobs: MACOS_SDK_VERSION: 12.3 steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure aws credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ secrets.aws-role-arn }} role-duration-seconds: ${{ secrets.aws-role-dur-sec }} diff --git a/.github/actions/setup-nodejs/action.yaml b/.github/actions/setup-nodejs/action.yaml index 4e1740032b6..1bb529b421c 100644 --- a/.github/actions/setup-nodejs/action.yaml +++ b/.github/actions/setup-nodejs/action.yaml @@ -7,11 +7,11 @@ description: Setup pnpm for contracts runs: using: composite steps: - - uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd #v2.2.4 + - uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd # v2.2.4 with: version: ^7.0.0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 with: node-version: "16" cache: "pnpm" diff --git a/.github/actions/split-tests/action.yaml b/.github/actions/split-tests/action.yaml index fc96c4da25f..684fd6a2bd7 100644 --- a/.github/actions/split-tests/action.yaml +++ b/.github/actions/split-tests/action.yaml @@ -15,7 +15,7 @@ runs: with: version: ^7.0.0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 with: node-version: "16" cache: "pnpm" diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 3d7466faedd..45491af0264 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -97,7 +97,7 @@ jobs: done done <<< "$EVM_HTTP_URLS" - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ env.REF_NAME }} - name: Build Test Image diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index 032670c39a3..20415b599ed 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -53,7 +53,7 @@ jobs: this-job-name: Build Chainlink Image ${{ matrix.image.name }} continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.head_ref || github.ref_name }} - name: Check if image exists @@ -98,7 +98,7 @@ jobs: this-job-name: Build Test Image continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.head_ref || github.ref_name }} - name: Build Test Image @@ -149,7 +149,7 @@ jobs: name: Automation On Demand ${{ matrix.tests.name }} Test steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.head_ref || github.ref_name }} - name: Determine build to use diff --git a/.github/workflows/build-publish-develop.yml b/.github/workflows/build-publish-develop.yml index cba8edba3e2..54ccaad5810 100644 --- a/.github/workflows/build-publish-develop.yml +++ b/.github/workflows/build-publish-develop.yml @@ -31,7 +31,7 @@ jobs: name: push-chainlink-develop ${{ matrix.image.name }} steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ env.GIT_REF }} # When this is ran from manual workflow_dispatch, the github.sha may be diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 9aa7b9accc5..5db70576b37 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Check for VERSION file bump on tags if: ${{ startsWith(github.ref, 'refs/tags/v') }} uses: ./.github/actions/version-file-bump @@ -32,7 +32,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Build, sign and publish chainlink image uses: ./.github/actions/build-sign-publish-chainlink diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9931137d637..7cdf5e46b9e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Build chainlink image uses: ./.github/actions/build-sign-publish-chainlink diff --git a/.github/workflows/ci-chaincli.yml b/.github/workflows/ci-chaincli.yml index 97225e46557..fd58d08005c 100644 --- a/.github/workflows/ci-chaincli.yml +++ b/.github/workflows/ci-chaincli.yml @@ -14,7 +14,7 @@ jobs: name: chaincli-lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Golang Lint uses: ./.github/actions/golangci-lint with: diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 74ca1dae9ad..7bc91da3abb 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -22,7 +22,7 @@ jobs: name: lint runs-on: ubuntu20.04-8cores-32GB steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Golang Lint uses: ./.github/actions/golangci-lint with: @@ -40,9 +40,9 @@ jobs: CL_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/chainlink_test?sslmode=disable steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs with: @@ -126,9 +126,9 @@ jobs: CL_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/chainlink_test?sslmode=disable steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs with: @@ -183,7 +183,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 # fetches all history for all tags and branches to provide more metadata for sonar reports - name: Download all workflow run artifacts @@ -227,7 +227,7 @@ jobs: run: | echo "## \`skip-smoke-tests\` label is active, skipping E2E smoke tests" >>$GITHUB_STEP_SUMMARY exit 0 - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Setup Go diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 86f2515e26d..822bf259f92 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Go if: ${{ matrix.language == 'go' }} diff --git a/.github/workflows/delete-deployments.yml b/.github/workflows/delete-deployments.yml index a3d3100516d..3ec5fb35c97 100644 --- a/.github/workflows/delete-deployments.yml +++ b/.github/workflows/delete-deployments.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Clean up integration environment uses: ./.github/actions/delete-deployments diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index ebfe1b947a9..42729a8cf1c 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -11,7 +11,7 @@ jobs: changes: ${{ steps.changes.outputs.src }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: changes with: @@ -25,7 +25,7 @@ jobs: needs: [changes] steps: - name: Check out code - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set up Go if: needs.changes.outputs.src == 'true' diff --git a/.github/workflows/goreleaser-build-publish-develop.yml b/.github/workflows/goreleaser-build-publish-develop.yml index 9e9b088033e..1edfdedd706 100644 --- a/.github/workflows/goreleaser-build-publish-develop.yml +++ b/.github/workflows/goreleaser-build-publish-develop.yml @@ -18,9 +18,9 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure aws credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ secrets.AWS_OIDC_IAM_ROLE_ARN }} role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS }} @@ -55,9 +55,9 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure aws credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_GATI }} role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS }} diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-publish.yml index e80d758d810..48a7060fc77 100644 --- a/.github/workflows/helm-publish.yml +++ b/.github/workflows/helm-publish.yml @@ -12,7 +12,7 @@ jobs: contents: read steps: - name: Checkout repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure aws credentials uses: aws-actions/configure-aws-credentials@50ac8dd1e1b10d09dac7b8727528b91bed831ac0 # v3.0.2 diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index a9aa2c35df1..503e5ec58a4 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Check if image exists id: check-image uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 @@ -69,7 +69,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Build Test Image uses: ./.github/actions/build-test-image with: @@ -107,7 +107,7 @@ jobs: test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 with: diff --git a/.github/workflows/integration-staging-tests.yml b/.github/workflows/integration-staging-tests.yml index a6fda178d50..2abb9a3cfae 100644 --- a/.github/workflows/integration-staging-tests.yml +++ b/.github/workflows/integration-staging-tests.yml @@ -50,7 +50,7 @@ jobs: WASP_LOG_LEVEL: info steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Run E2E soak tests diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index c71b83d1c4d..06f7bb1b817 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -27,7 +27,7 @@ jobs: this-job-name: Publish Integration Test Image continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha }} - name: Build Image diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index aadb14f1284..edd7755e8b9 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: changes with: @@ -81,7 +81,7 @@ jobs: this-job-name: Build Chainlink Image ${{ matrix.image.name }} continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Check if image exists @@ -129,7 +129,7 @@ jobs: this-job-name: Build Test Image continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Test Image @@ -153,7 +153,7 @@ jobs: echo "## \`skip-smoke-tests\` label is active, skipping E2E smoke tests" >>$GITHUB_STEP_SUMMARY exit 0 - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Compare Test Lists run: | cd ./integration-tests @@ -190,7 +190,7 @@ jobs: name: ETH Smoke Tests ${{ matrix.product.name }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Go Test Command @@ -303,7 +303,7 @@ jobs: name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Go Test Command @@ -484,7 +484,7 @@ jobs: steps: - name: Checkout repo if: ${{ github.event_name == 'pull_request' }} - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: 🧼 Clean up Environment if: ${{ github.event_name == 'pull_request' }} @@ -513,7 +513,7 @@ jobs: continue-on-error: true steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Run Setup @@ -552,7 +552,7 @@ jobs: TEST_SUITE: migration steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Get Latest Version @@ -608,7 +608,7 @@ jobs: sha: ${{ steps.getsha.outputs.sha }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Get the sha from go mod @@ -619,7 +619,7 @@ jobs: echo "short sha is: ${short_sha}" echo "short_sha=${short_sha}" >> "$GITHUB_OUTPUT" - name: Checkout solana - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana ref: develop @@ -642,7 +642,7 @@ jobs: projectserum_version: ${{ steps.psversion.outputs.projectserum_version }} steps: - name: Checkout the solana repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana ref: ${{ needs.get_solana_sha.outputs.sha }} @@ -704,6 +704,8 @@ jobs: this-job-name: Solana Build Artifacts continue-on-error: true - name: Checkout the solana repo + # Use v3.6.0 because the custom runner (container configured above) + # doesn't have node20 installed which is required for versions >=4 uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: repository: smartcontractkit/chainlink-solana @@ -744,7 +746,7 @@ jobs: continue-on-error: true - name: Checkout the repo if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana ref: 23816fcf7d380a30c87b6d87e4fb0ca94419b259 # swtich back to this after the next solana release${{ needs.get_solana_sha.outputs.sha }} @@ -802,7 +804,7 @@ jobs: test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana ref: ${{ needs.get_solana_sha.outputs.sha }} @@ -915,7 +917,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} ## Only run OCR smoke test for now diff --git a/.github/workflows/lint-gh-workflows.yml b/.github/workflows/lint-gh-workflows.yml index 1220f3a745f..8facdc038c1 100644 --- a/.github/workflows/lint-gh-workflows.yml +++ b/.github/workflows/lint-gh-workflows.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out Code - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run actionlint uses: reviewdog/action-actionlint@82693e9e3b239f213108d6e412506f8b54003586 # v1.39.1 - name: Collect Metrics diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index cd141142268..7dc144264da 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -110,7 +110,7 @@ jobs: echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ env.REF_NAME }} - name: Setup Push Tag diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index b33c6f83133..41b6618ba6b 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -114,7 +114,7 @@ jobs: echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 - name: Run Tests diff --git a/.github/workflows/operator-ui-cd.yml b/.github/workflows/operator-ui-cd.yml index 4f6e82e07b3..36a9d8b715a 100644 --- a/.github/workflows/operator-ui-cd.yml +++ b/.github/workflows/operator-ui-cd.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Update version id: update @@ -25,7 +25,7 @@ jobs: run: ./operator_ui/check.sh - name: Assume role capable of dispatching action - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ secrets.AWS_OIDC_CHAINLINK_CI_AUTO_PR_TOKEN_ISSUER_ROLE_ARN }} role-duration-seconds: ${{ secrets.aws-role-duration-seconds }} diff --git a/.github/workflows/operator-ui-ci.yml b/.github/workflows/operator-ui-ci.yml index 8ced3b222cf..2ce85bc1322 100644 --- a/.github/workflows/operator-ui-ci.yml +++ b/.github/workflows/operator-ui-ci.yml @@ -23,7 +23,7 @@ jobs: continue-on-error: true - name: Assume role capable of dispatching action - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ secrets.AWS_OIDC_CHAINLINK_CI_OPERATOR_UI_ACCESS_TOKEN_ISSUER_ROLE_ARN }} role-duration-seconds: 3600 diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index b79d8dfea24..43ed80cb3ff 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -18,16 +18,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: aws-region: ${{ secrets.QA_AWS_REGION }} role-to-assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} role-duration-seconds: 3600 - name: Login to Amazon ECR id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 + uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # v2.7.0 - name: Build and Push @@ -55,7 +55,7 @@ jobs: needs: build-chainlink steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 with: diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index 590165c8e7a..f6c515b5b86 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -12,7 +12,7 @@ jobs: changes: ${{ steps.changes.outputs.src }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: changes with: @@ -41,7 +41,7 @@ jobs: steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: submodules: recursive diff --git a/.github/workflows/solidity-hardhat.yml b/.github/workflows/solidity-hardhat.yml index 334ce4d1bae..6e7de2eba19 100644 --- a/.github/workflows/solidity-hardhat.yml +++ b/.github/workflows/solidity-hardhat.yml @@ -19,7 +19,7 @@ jobs: changes: ${{ steps.changes.outputs.src }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: changes with: @@ -40,7 +40,7 @@ jobs: splits: ${{ steps.split.outputs.splits }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Generate splits id: split uses: ./.github/actions/split-tests @@ -66,7 +66,7 @@ jobs: runs-on: ubuntu20.04-4cores-16GB steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs - name: Setup Hardhat @@ -104,7 +104,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs - name: Make coverage directory @@ -131,7 +131,7 @@ jobs: runs-on: ubuntu20.04-4cores-16GB steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs - name: Setup Hardhat diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index b2537a5c9a7..f46b13191d2 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -16,7 +16,7 @@ jobs: changes: ${{ steps.changes.outputs.src }} steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: changes with: @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs - name: Run Prepublish test @@ -54,9 +54,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Checkout diff-so-fancy - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: so-fancy/diff-so-fancy ref: a673cb4d2707f64d92b86498a2f5f71c8e2643d5 # v1.4.3 @@ -101,7 +101,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS uses: ./.github/actions/setup-nodejs - name: Run pnpm lint @@ -126,7 +126,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout the repo - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS if: needs.changes.outputs.changes == 'true' uses: ./.github/actions/setup-nodejs diff --git a/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml b/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml index 97037652034..7fe9ffd526b 100644 --- a/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml +++ b/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml @@ -10,7 +10,7 @@ jobs: name: Sync runs-on: ubuntu-latest steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: develop if: env.GITHUB_REPOSITORY != 'smartcontractkit/chainlink' From 45b59578f369f221465dea339d368bd3beec175c Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 26 Oct 2023 06:11:53 -0500 Subject: [PATCH 012/271] .github/workflows: add slack notification to nightly golangci-lint (#11088) --- .github/workflows/ci-core.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 7bc91da3abb..4037dc0cd90 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -28,6 +28,14 @@ jobs: with: gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} + - name: Notify Slack + if: ${{ failure() && github.event.schedule != '' }} + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + with: + channel-id: "#team-core" + slack-message: "golangci-lint failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" core: strategy: From b31284ba5a12c7b675bd942cc8f4b2598232f5a7 Mon Sep 17 00:00:00 2001 From: Francisco de Borja Aranda Castillejo Date: Thu, 26 Oct 2023 15:26:50 +0200 Subject: [PATCH 013/271] make maxCheck and maxPerform configurable (#11083) * Make maxCheck and maxPerform configurable - maxCheck and maxPerform can be configured. - Values for these have to be provided in the constructor. - Adhere to Solidity Style Guide in imports, varnames, etc. * add tests * add custom errors on require methods * add proposed fixes * make LINK_TOKEN private --- .../upkeeps/LinkAvailableBalanceMonitor.sol | 240 ++++---- .../contracts/utils/structs/EnumerableMap.sol | 530 ++++++++++++++++++ .../LinkAvailableBalanceMonitor.test.ts | 15 +- 3 files changed, 659 insertions(+), 126 deletions(-) create mode 100644 contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index cff09aaebbd..028579397aa 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -2,11 +2,11 @@ pragma solidity 0.8.6; -import "../../shared/access/ConfirmedOwner.sol"; -import "../interfaces/KeeperCompatibleInterface.sol"; -import "../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol"; -import "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; -import "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableMap.sol"; +import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; +import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol"; interface IAggregatorProxy { function aggregator() external view returns (address); @@ -16,60 +16,63 @@ interface ILinkAvailable { function linkAvailableForPayment() external view returns (int256 availableBalance); } -/** - * @title The LinkAvailableBalanceMonitor contract. - * @notice A keeper-compatible contract that monitors target contracts for balance from a custom - * function linkAvailableForPayment() and funds them with LINK if it falls below a defined - * threshold. Also supports aggregator proxy contracts monitoring which require fetching the actual - * target contract through a predefined interface. - * @dev with 30 addresses as the MAX_PERFORM, the measured max gas usage of performUpkeep is around 2M - * therefore, we recommend an upkeep gas limit of 3M (this has a 33% margin of safety). Although, nothing - * prevents us from using 5M gas and increasing MAX_PERFORM, 30 seems like a reasonable batch size that - * is probably plenty for most needs. - * @dev with 130 addresses as the MAX_CHECK, the measured max gas usage of checkUpkeep is around 3.5M, - * which is 30% below the 5M limit. - * Note that testing conditions DO NOT match live chain gas usage, hence the margins. Change - * at your own risk!!! - * @dev some areas for improvement / acknowledgement of limitations: - * * validate that all addresses conform to interface when adding them to the watchlist - * * this is a "trusless" upkeep, meaning it does not trust the caller of performUpkeep; - we could save a fair amount of gas and re-write this upkeep for use with Automation v2.0+, - which has significantly different trust assumptions - */ -contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatibleInterface { +/// @title The LinkAvailableBalanceMonitor contract. +/// @notice A keeper-compatible contract that monitors target contracts for balance from a custom +/// function linkAvailableForPayment() and funds them with LINK if it falls below a defined +/// threshold. Also supports aggregator proxy contracts monitoring which require fetching the actual +/// target contract through a predefined interface. +/// @dev with 30 addresses as the s_maxPerform, the measured max gas usage of performUpkeep is around 2M +/// therefore, we recommend an upkeep gas limit of 3M (this has a 33% margin of safety). Although, nothing +/// prevents us from using 5M gas and increasing s_maxPerform, 30 seems like a reasonable batch size that +/// is probably plenty for most needs. +/// @dev with 130 addresses as the s_maxCheck, the measured max gas usage of checkUpkeep is around 3.5M, +/// which is 30% below the 5M limit. +/// Note that testing conditions DO NOT match live chain gas usage, hence the margins. Change +/// at your own risk!!! +/// @dev some areas for improvement / acknowledgement of limitations: +/// validate that all addresses conform to interface when adding them to the watchlist +/// this is a "trusless" upkeep, meaning it does not trust the caller of performUpkeep; +/// we could save a fair amount of gas and re-write this upkeep for use with Automation v2.0+, +/// which has significantly different trust assumptions +contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationCompatibleInterface { using EnumerableMap for EnumerableMap.AddressToUintMap; event FundsWithdrawn(uint256 amountWithdrawn, address payee); event TopUpSucceeded(address indexed topUpAddress); event TopUpBlocked(address indexed topUpAddress); event WatchlistUpdated(); + event MaxPerformUpdated(uint256 oldMaxPerform, uint256 newMaxPerform); + event MaxCheckUpdated(uint256 oldMaxCheck, uint256 newMaxCheck); error InvalidWatchList(); error DuplicateAddress(address duplicate); - uint256 public constant MAX_PERFORM = 5; // max number to addresses to top up in a single batch - uint256 public constant MAX_CHECK = 20; // max number of upkeeps to check (need to fit in 5M gas limit) - IERC20 public immutable LINK_TOKEN; - + IERC20 private immutable LINK_TOKEN; EnumerableMap.AddressToUintMap private s_watchList; uint256 private s_topUpAmount; + uint32 private s_minWaitPeriodSeconds; + uint16 private s_maxPerform; + uint16 private s_maxCheck; - /** - * @param linkTokenAddress the LINK token address - * @param topUpAmount the amount of LINK to top up an aggregator with at once - */ - constructor(address linkTokenAddress, uint256 topUpAmount) ConfirmedOwner(msg.sender) { - require(linkTokenAddress != address(0)); - require(topUpAmount > 0); + /// @param linkTokenAddress the LINK token address + /// @param topUpAmount the amount of LINK to top up an aggregator with at once + constructor( + address linkTokenAddress, + uint256 topUpAmount, + uint16 maxPerform, + uint16 maxCheck + ) ConfirmedOwner(msg.sender) { + require(linkTokenAddress != address(0), "LinkAvailableBalanceMonitor: invalid linkTokenAddress"); + require(topUpAmount > 0, "LinkAvailableBalanceMonitor: invalid topUpAmount"); LINK_TOKEN = IERC20(linkTokenAddress); s_topUpAmount = topUpAmount; + s_maxPerform = maxPerform; + s_maxCheck = maxCheck; } - /** - * @notice Sets the list of subscriptions to watch and their funding parameters - * @param addresses the list of target addresses to watch (could be direct target or IAggregatorProxy) - * @param minBalances the list of corresponding minBalance for the target address - */ + /// @notice Sets the list of subscriptions to watch and their funding parameters + /// @param addresses the list of target addresses to watch (could be direct target or IAggregatorProxy) + /// @param minBalances the list of corresponding minBalance for the target address function setWatchList(address[] calldata addresses, uint256[] calldata minBalances) external onlyOwner { if (addresses.length != minBalances.length) { revert InvalidWatchList(); @@ -77,7 +80,7 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib // first, remove all existing addresses from list for (uint256 idx = s_watchList.length(); idx > 0; idx--) { (address target, ) = s_watchList.at(idx - 1); - require(s_watchList.remove(target)); + require(s_watchList.remove(target), "LinkAvailableBalanceMonitor: unable to setWatchlist"); } // then set new addresses for (uint256 idx = 0; idx < addresses.length; idx++) { @@ -92,11 +95,9 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib emit WatchlistUpdated(); } - /** - * @notice Adds addresses to the watchlist without overwriting existing members - * @param addresses the list of target addresses to watch (could be direct target or IAggregatorProxy) - * @param minBalances the list of corresponding minBalance for the target address - */ + /// @notice Adds addresses to the watchlist without overwriting existing members + /// @param addresses the list of target addresses to watch (could be direct target or IAggregatorProxy) + /// @param minBalances the list of corresponding minBalance for the target address function addToWatchList(address[] calldata addresses, uint256[] calldata minBalances) external onlyOwner { if (addresses.length != minBalances.length) { revert InvalidWatchList(); @@ -113,10 +114,8 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib emit WatchlistUpdated(); } - /** - * @notice Removes addresses from the watchlist - * @param addresses the list of target addresses to remove from the watchlist - */ + /// @notice Removes addresses from the watchlist + /// @param addresses the list of target addresses to remove from the watchlist function removeFromWatchlist(address[] calldata addresses) external onlyOwner { for (uint256 idx = 0; idx < addresses.length; idx++) { if (!s_watchList.contains(addresses[idx])) { @@ -127,33 +126,36 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib emit WatchlistUpdated(); } - /** - * @notice Gets a list of proxies that are underfunded, up to the MAX_PERFORM size - * @dev the function starts at a random index in the list to avoid biasing the first - * addresses in the list over latter ones. - * @dev the function will check at most MAX_CHECK proxies in a single call - * @dev the function returns a list with a max length of MAX_PERFORM - * @return list of target addresses which are underfunded - */ + /// @notice Gets a list of proxies that are underfunded, up to the s_maxPerform size + /// @dev the function starts at a random index in the list to avoid biasing the first + /// addresses in the list over latter ones. + /// @dev the function will check at most s_maxCheck proxies in a single call + /// @dev the function returns a list with a max length of s_maxPerform + /// @return list of target addresses which are underfunded function sampleUnderfundedAddresses() public view returns (address[] memory) { + uint16 maxPerform = s_maxPerform; + uint16 maxCheck = s_maxCheck; uint256 numTargets = s_watchList.length(); - uint256 numChecked = 0; uint256 idx = uint256(blockhash(block.number - 1)) % numTargets; // start at random index, to distribute load - uint256 numToCheck = numTargets < MAX_CHECK ? numTargets : MAX_CHECK; + uint256 numToCheck = numTargets < maxCheck ? numTargets : maxCheck; uint256 numFound = 0; - address[] memory targetsToFund = new address[](MAX_PERFORM); - for (; numChecked < numToCheck; (idx, numChecked) = ((idx + 1) % numTargets, numChecked + 1)) { + address[] memory targetsToFund = new address[](maxPerform); + for ( + uint256 numChecked = 0; + numChecked < numToCheck; + (idx, numChecked) = ((idx + 1) % numTargets, numChecked + 1) + ) { (address target, uint256 minBalance) = s_watchList.at(idx); (bool needsFunding, ) = _needsFunding(target, minBalance); if (needsFunding) { targetsToFund[numFound] = target; numFound++; - if (numFound == MAX_PERFORM) { + if (numFound == maxPerform) { break; // max number of addresses in batch reached } } } - if (numFound != MAX_PERFORM) { + if (numFound != maxPerform) { assembly { mstore(targetsToFund, numFound) // resize array to number of valid targets } @@ -161,10 +163,8 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib return targetsToFund; } - /** - * @notice Send funds to the targets provided. - * @param targetAddresses the list of targets to fund - */ + /// @notice Send funds to the targets provided. + /// @param targetAddresses the list of targets to fund function topUp(address[] memory targetAddresses) public whenNotPaused { uint256 topUpAmount = s_topUpAmount; uint256 stopIdx = targetAddresses.length; @@ -186,11 +186,9 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib } } - /** - * @notice Gets list of subscription ids that are underfunded and returns a keeper-compatible payload. - * @return upkeepNeeded signals if upkeep is needed - * @return performData is an abi encoded list of subscription ids that need funds - */ + /// @notice Gets list of subscription ids that are underfunded and returns a keeper-compatible payload. + /// @return upkeepNeeded signals if upkeep is needed + /// @return performData is an abi encoded list of subscription ids that need funds function checkUpkeep( bytes calldata ) external view override whenNotPaused returns (bool upkeepNeeded, bytes memory performData) { @@ -206,39 +204,31 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib return (upkeepNeeded, performData); } - /** - * @notice Called by the keeper to send funds to underfunded addresses. - * @param performData the abi encoded list of addresses to fund - */ + /// @notice Called by the keeper to send funds to underfunded addresses. + /// @param performData the abi encoded list of addresses to fund function performUpkeep(bytes calldata performData) external override { address[] memory needsFunding = abi.decode(performData, (address[])); topUp(needsFunding); } - /** - * @notice Withdraws the contract balance in the LINK token. - * @param amount the amount of the LINK to withdraw - * @param payee the address to pay - */ + /// @notice Withdraws the contract balance in the LINK token. + /// @param amount the amount of the LINK to withdraw + /// @param payee the address to pay function withdraw(uint256 amount, address payable payee) external onlyOwner { - require(payee != address(0)); + require(payee != address(0), "LinkAvailableBalanceMonitor: invalid payee address"); LINK_TOKEN.transfer(payee, amount); emit FundsWithdrawn(amount, payee); } - /** - * @notice Sets the top up amount - */ + /// @notice Sets the top up amount function setTopUpAmount(uint256 topUpAmount) external onlyOwner returns (uint256) { - require(topUpAmount > 0); + require(topUpAmount > 0, "LinkAvailableBalanceMonitor: invalid linkTokenAddress"); return s_topUpAmount = topUpAmount; } - /** - * @notice Sets the minimum balance for the given target address - */ + /// @notice Sets the minimum balance for the given target address function setMinBalance(address target, uint256 minBalance) external onlyOwner returns (uint256) { - require(minBalance > 0); + require(minBalance > 0, "LinkAvailableBalanceMonitor: invalid minBalance"); (bool exists, uint256 prevMinBalance) = s_watchList.tryGet(target); if (!exists) { revert InvalidWatchList(); @@ -247,23 +237,29 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib return prevMinBalance; } - /** - * @notice Pause the contract, which prevents executing performUpkeep - */ - function pause() external onlyOwner { - _pause(); + /// @notice Update s_maxPerform + function setMaxPerform(uint16 maxPerform) external onlyOwner { + emit MaxPerformUpdated(s_maxPerform, maxPerform); + s_maxPerform = maxPerform; } - /** - * @notice Unpause the contract - */ - function unpause() external onlyOwner { - _unpause(); + /// @notice Update s_maxCheck + function setMaxCheck(uint16 maxCheck) external onlyOwner { + emit MaxCheckUpdated(s_maxCheck, maxCheck); + s_maxCheck = maxCheck; + } + + /// @notice Gets maxPerform + function getMaxPerform() external view returns (uint16) { + return s_maxPerform; + } + + /// @notice Gets maxCheck + function getMaxCheck() external view returns (uint16) { + return s_maxCheck; } - /** - * @notice Gets the list of subscription ids being watched - */ + /// @notice Gets the list of subscription ids being watched function getWatchList() external view returns (address[] memory, uint256[] memory) { uint256 len = s_watchList.length(); address[] memory targets = new address[](len); @@ -276,16 +272,12 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib return (targets, minBalances); } - /** - * @notice Gets the configured top up amount - */ + /// @notice Gets the configured top up amount function getTopUpAmount() external view returns (uint256) { return s_topUpAmount; } - /** - * @notice Gets the configured minimum balance for the given target - */ + /// @notice Gets the configured minimum balance for the given target function getMinBalance(address target) external view returns (uint256) { (bool exists, uint256 minBalance) = s_watchList.tryGet(target); if (!exists) { @@ -294,14 +286,22 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, KeeperCompatib return minBalance; } - /** - * @notice checks the target (could be direct target or IAggregatorProxy), and determines - * if it is elligible for funding - * @param targetAddress the target to check - * @param minBalance minimum balance required for the target - * @return bool whether the target needs funding or not - * @return address the address of the contract needing funding - */ + /// @notice Pause the contract, which prevents executing performUpkeep + function pause() external onlyOwner { + _pause(); + } + + /// @notice Unpause the contract + function unpause() external onlyOwner { + _unpause(); + } + + /// @notice checks the target (could be direct target or IAggregatorProxy), and determines + /// if it is elligible for funding + /// @param targetAddress the target to check + /// @param minBalance minimum balance required for the target + /// @return bool whether the target needs funding or not + /// @return address the address of the contract needing funding function _needsFunding(address targetAddress, uint256 minBalance) private view returns (bool, address) { ILinkAvailable target; IAggregatorProxy proxy = IAggregatorProxy(targetAddress); diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol new file mode 100644 index 00000000000..7f4e9115b19 --- /dev/null +++ b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableMap.sol) +// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js. + +pragma solidity ^0.8.0; + +import "./EnumerableSet.sol"; + +/** + * @dev Library for managing an enumerable variant of Solidity's + * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] + * type. + * + * Maps have the following properties: + * + * - Entries are added, removed, and checked for existence in constant time + * (O(1)). + * - Entries are enumerated in O(n). No guarantees are made on the ordering. + * + * ``` + * contract Example { + * // Add the library methods + * using EnumerableMap for EnumerableMap.UintToAddressMap; + * + * // Declare a set state variable + * EnumerableMap.UintToAddressMap private myMap; + * } + * ``` + * + * The following map types are supported: + * + * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0 + * - `address -> uint256` (`AddressToUintMap`) since v4.6.0 + * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0 + * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0 + * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0 + * + * [WARNING] + * ==== + * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure + * unusable. + * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. + * + * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an + * array of EnumerableMap. + * ==== + */ +library EnumerableMap { + using EnumerableSet for EnumerableSet.Bytes32Set; + + // To implement this library for multiple types with as little code + // repetition as possible, we write it in terms of a generic Map type with + // bytes32 keys and values. + // The Map implementation uses private functions, and user-facing + // implementations (such as Uint256ToAddressMap) are just wrappers around + // the underlying Map. + // This means that we can only create new EnumerableMaps for types that fit + // in bytes32. + + struct Bytes32ToBytes32Map { + // Storage of keys + EnumerableSet.Bytes32Set _keys; + mapping(bytes32 => bytes32) _values; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + Bytes32ToBytes32Map storage map, + bytes32 key, + bytes32 value + ) internal returns (bool) { + map._values[key] = value; + return map._keys.add(key); + } + + /** + * @dev Removes a key-value pair from a map. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) { + delete map._values[key]; + return map._keys.remove(key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) { + return map._keys.contains(key); + } + + /** + * @dev Returns the number of key-value pairs in the map. O(1). + */ + function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) { + return map._keys.length(); + } + + /** + * @dev Returns the key-value pair stored at position `index` in the map. O(1). + * + * Note that there are no guarantees on the ordering of entries inside the + * array, and it may change when more entries are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) { + bytes32 key = map._keys.at(index); + return (key, map._values[key]); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) { + bytes32 value = map._values[key]; + if (value == bytes32(0)) { + return (contains(map, key), bytes32(0)); + } else { + return (true, value); + } + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || contains(map, key), "EnumerableMap: nonexistent key"); + return value; + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + Bytes32ToBytes32Map storage map, + bytes32 key, + string memory errorMessage + ) internal view returns (bytes32) { + bytes32 value = map._values[key]; + require(value != 0 || contains(map, key), errorMessage); + return value; + } + + // UintToUintMap + + struct UintToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + UintToUintMap storage map, + uint256 key, + uint256 value + ) internal returns (bool) { + return set(map._inner, bytes32(key), bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToUintMap storage map, uint256 key) internal returns (bool) { + return remove(map._inner, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) { + return contains(map._inner, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToUintMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (uint256(key), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, bytes32(key)); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(key))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + UintToUintMap storage map, + uint256 key, + string memory errorMessage + ) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(key), errorMessage)); + } + + // UintToAddressMap + + struct UintToAddressMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + UintToAddressMap storage map, + uint256 key, + address value + ) internal returns (bool) { + return set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { + return remove(map._inner, bytes32(key)); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { + return contains(map._inner, bytes32(key)); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(UintToAddressMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (uint256(key), address(uint160(uint256(value)))); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { + (bool success, bytes32 value) = tryGet(map._inner, bytes32(key)); + return (success, address(uint160(uint256(value)))); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { + return address(uint160(uint256(get(map._inner, bytes32(key))))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + UintToAddressMap storage map, + uint256 key, + string memory errorMessage + ) internal view returns (address) { + return address(uint160(uint256(get(map._inner, bytes32(key), errorMessage)))); + } + + // AddressToUintMap + + struct AddressToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + AddressToUintMap storage map, + address key, + uint256 value + ) internal returns (bool) { + return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(AddressToUintMap storage map, address key) internal returns (bool) { + return remove(map._inner, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(AddressToUintMap storage map, address key) internal view returns (bool) { + return contains(map._inner, bytes32(uint256(uint160(key)))); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(AddressToUintMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (address(uint160(uint256(key))), uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key)))); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(AddressToUintMap storage map, address key) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(uint256(uint160(key))))); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + AddressToUintMap storage map, + address key, + string memory errorMessage + ) internal view returns (uint256) { + return uint256(get(map._inner, bytes32(uint256(uint160(key))), errorMessage)); + } + + // Bytes32ToUintMap + + struct Bytes32ToUintMap { + Bytes32ToBytes32Map _inner; + } + + /** + * @dev Adds a key-value pair to a map, or updates the value for an existing + * key. O(1). + * + * Returns true if the key was added to the map, that is if it was not + * already present. + */ + function set( + Bytes32ToUintMap storage map, + bytes32 key, + uint256 value + ) internal returns (bool) { + return set(map._inner, key, bytes32(value)); + } + + /** + * @dev Removes a value from a set. O(1). + * + * Returns true if the key was removed from the map, that is if it was present. + */ + function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) { + return remove(map._inner, key); + } + + /** + * @dev Returns true if the key is in the map. O(1). + */ + function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) { + return contains(map._inner, key); + } + + /** + * @dev Returns the number of elements in the map. O(1). + */ + function length(Bytes32ToUintMap storage map) internal view returns (uint256) { + return length(map._inner); + } + + /** + * @dev Returns the element stored at position `index` in the set. O(1). + * Note that there are no guarantees on the ordering of values inside the + * array, and it may change when more values are added or removed. + * + * Requirements: + * + * - `index` must be strictly less than {length}. + */ + function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) { + (bytes32 key, bytes32 value) = at(map._inner, index); + return (key, uint256(value)); + } + + /** + * @dev Tries to returns the value associated with `key`. O(1). + * Does not revert if `key` is not in the map. + */ + function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) { + (bool success, bytes32 value) = tryGet(map._inner, key); + return (success, uint256(value)); + } + + /** + * @dev Returns the value associated with `key`. O(1). + * + * Requirements: + * + * - `key` must be in the map. + */ + function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) { + return uint256(get(map._inner, key)); + } + + /** + * @dev Same as {get}, with a custom error message when `key` is not in the map. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryGet}. + */ + function get( + Bytes32ToUintMap storage map, + bytes32 key, + string memory errorMessage + ) internal view returns (uint256) { + return uint256(get(map._inner, key, errorMessage)); + } +} \ No newline at end of file diff --git a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts index 4efd98039f5..af0063fb503 100644 --- a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts +++ b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts @@ -35,7 +35,6 @@ const zeroLINK = ethers.utils.parseEther('0') const oneLINK = ethers.utils.parseEther('1') const twoLINK = ethers.utils.parseEther('2') const fiveLINK = ethers.utils.parseEther('5') -const sixLINK = ethers.utils.parseEther('6') const tenLINK = ethers.utils.parseEther('10') const oneHundredLINK = ethers.utils.parseEther('100') @@ -142,8 +141,12 @@ const setup = async () => { owner, ) - lt = await ltFactory.deploy() - labm = await labmFactory.deploy(lt.address, twoLINK) + // New parameters needed by the constructor + const maxPerform = 5 + const maxCheck = 20 + + lt = (await ltFactory.deploy()) as LinkToken + labm = await labmFactory.deploy(lt.address, twoLINK, maxPerform, maxCheck) await labm.deployed() for (let i = 1; i <= 4; i++) { @@ -471,8 +474,8 @@ describe('LinkAvailableBalanceMonitor', () => { let aggregators: MockContract[] beforeEach(async () => { - MAX_PERFORM = (await labm.MAX_PERFORM()).toNumber() - MAX_CHECK = (await labm.MAX_CHECK()).toNumber() + MAX_PERFORM = await labm.getMaxPerform() + MAX_CHECK = await labm.getMaxCheck() proxyAddresses = [] minBalances = [] aggregators = [] @@ -577,7 +580,7 @@ describe('LinkAvailableBalanceMonitor', () => { it('Can handle MAX_PERFORM proxies within gas limit', async () => { // add MAX_PERFORM number of proxies - const MAX_PERFORM = (await labm.MAX_PERFORM()).toNumber() + const MAX_PERFORM = await labm.getMaxPerform() const proxyAddresses = [] const minBalances = [] for (let idx = 0; idx < MAX_PERFORM; idx++) { From 09f0afdef4ec46909d6d3cdc2f3a12f4fcec9ca8 Mon Sep 17 00:00:00 2001 From: Jean Arnaud Date: Thu, 26 Oct 2023 15:58:27 +0200 Subject: [PATCH 014/271] Add missing fields to generated Deploy methods (#11061) * Add missing fields to generated Deploy methods * Regenerate wrappers * Abigen test * Error handling * Test Address method returning the proper address * Update generated wrappers * Refactor method and check type assertion * Format --- core/gethwrappers/abigen.go | 61 ++++++++++++++++--- core/gethwrappers/abigen_test.go | 29 +++++++++ .../generated/functions/functions.go | 2 +- .../functions_allow_list.go | 2 +- .../functions_client_example.go | 2 +- .../functions_coordinator.go | 2 +- .../functions_load_test_client.go | 2 +- .../functions_router/functions_router.go | 2 +- .../functions_v1_events_mock.go | 2 +- .../functions/generated/ocr2dr/ocr2dr.go | 2 +- .../ocr2dr_client_example.go | 2 +- .../generated/ocr2dr_oracle/ocr2dr_oracle.go | 2 +- .../ocr2dr_registry/ocr2dr_registry.go | 2 +- .../authorized_forwarder.go | 2 +- .../automation_consumer_benchmark.go | 2 +- .../automation_forwarder_logic.go | 2 +- .../automation_registrar_wrapper2_1.go | 2 +- .../automation_utils_2_1.go | 2 +- .../batch_blockhash_store.go | 2 +- .../batch_vrf_coordinator_v2.go | 2 +- .../batch_vrf_coordinator_v2plus.go | 2 +- .../blockhash_store/blockhash_store.go | 2 +- .../chain_specific_util_helper.go | 2 +- .../consumer_wrapper/consumer_wrapper.go | 2 +- .../dummy_protocol_wrapper.go | 2 +- .../generated/flags_wrapper/flags_wrapper.go | 2 +- .../flux_aggregator_wrapper.go | 2 +- .../functions_billing_registry_events_mock.go | 2 +- .../functions_oracle_events_mock.go | 2 +- .../generated/gas_wrapper/gas_wrapper.go | 2 +- .../gas_wrapper_mock/gas_wrapper_mock.go | 2 +- .../keeper_consumer_performance_wrapper.go | 2 +- .../keeper_consumer_wrapper.go | 2 +- .../keeper_registrar_wrapper1_2.go | 2 +- .../keeper_registrar_wrapper1_2_mock.go | 2 +- .../keeper_registrar_wrapper2_0.go | 2 +- .../keeper_registry_logic1_3.go | 2 +- .../keeper_registry_logic2_0.go | 2 +- .../keeper_registry_logic_a_wrapper_2_1.go | 2 +- .../keeper_registry_logic_b_wrapper_2_1.go | 2 +- .../keeper_registry_wrapper1_1.go | 2 +- .../keeper_registry_wrapper1_1_mock.go | 2 +- .../keeper_registry_wrapper1_2.go | 2 +- .../keeper_registry_wrapper1_3.go | 2 +- .../keeper_registry_wrapper2_0.go | 2 +- .../keeper_registry_wrapper_2_1.go | 2 +- .../keepers_vrf_consumer.go | 2 +- .../link_token_interface.go | 2 +- .../generated/log_emitter/log_emitter.go | 2 +- .../log_triggered_streams_lookup_wrapper.go | 2 +- .../log_upkeep_counter_wrapper.go | 2 +- .../mock_aggregator_proxy.go | 2 +- .../mock_ethlink_aggregator_wrapper.go | 2 +- .../mock_gas_aggregator_wrapper.go | 2 +- .../multiwordconsumer_wrapper.go | 2 +- .../operator_factory/operator_factory.go | 2 +- .../operator_wrapper/operator_wrapper.go | 2 +- .../oracle_wrapper/oracle_wrapper.go | 2 +- .../perform_data_checker_wrapper.go | 2 +- .../solidity_vrf_consumer_interface.go | 2 +- .../solidity_vrf_consumer_interface_v08.go | 2 +- .../solidity_vrf_coordinator_interface.go | 2 +- .../solidity_vrf_request_id.go | 2 +- .../solidity_vrf_request_id_v08.go | 2 +- .../solidity_vrf_v08_verifier_wrapper.go | 2 +- .../solidity_vrf_verifier_wrapper.go | 2 +- .../solidity_vrf_wrapper.go | 2 +- .../streams_lookup_upkeep_wrapper.go | 2 +- .../test_api_consumer_wrapper.go | 2 +- .../trusted_blockhash_store.go | 2 +- .../upkeep_counter_wrapper.go | 2 +- ...eep_perform_counter_restrictive_wrapper.go | 2 +- .../upkeep_transcoder/upkeep_transcoder.go | 2 +- ...ifiable_load_log_trigger_upkeep_wrapper.go | 2 +- ...able_load_streams_lookup_upkeep_wrapper.go | 2 +- .../verifiable_load_upkeep_wrapper.go | 2 +- .../vrf_consumer_v2/vrf_consumer_v2.go | 2 +- ...rf_consumer_v2_plus_upgradeable_example.go | 2 +- .../vrf_consumer_v2_upgradeable_example.go | 2 +- .../vrf_coordinator_mock.go | 2 +- .../vrf_coordinator_v2/vrf_coordinator_v2.go | 2 +- .../vrf_coordinator_v2_5.go | 2 +- .../vrf_coordinator_v2_plus_v2_example.go | 2 +- .../vrf_external_sub_owner_example.go | 2 +- .../vrf_load_test_external_sub_owner.go | 2 +- .../vrf_load_test_ownerless_consumer.go | 2 +- .../vrf_load_test_with_metrics.go | 2 +- .../vrf_malicious_consumer_v2.go | 2 +- .../vrf_malicious_consumer_v2_plus.go | 2 +- .../generated/vrf_owner/vrf_owner.go | 2 +- .../vrf_owner_test_consumer.go | 2 +- .../vrf_ownerless_consumer_example.go | 2 +- .../vrf_single_consumer_example.go | 2 +- .../vrf_v2_consumer_wrapper.go | 2 +- .../vrf_v2plus_load_test_with_metrics.go | 2 +- .../vrf_v2plus_single_consumer.go | 2 +- .../vrf_v2plus_sub_owner.go | 2 +- .../vrf_v2plus_upgraded_version.go | 2 +- .../vrfv2_proxy_admin/vrfv2_proxy_admin.go | 2 +- .../vrfv2_reverting_example.go | 2 +- .../vrfv2_transparent_upgradeable_proxy.go | 2 +- .../generated/vrfv2_wrapper/vrfv2_wrapper.go | 2 +- .../vrfv2_wrapper_consumer_example.go | 2 +- .../vrfv2plus_client/vrfv2plus_client.go | 2 +- .../vrfv2plus_consumer_example.go | 2 +- .../vrfv2plus_malicious_migrator.go | 2 +- .../vrfv2plus_reverting_example.go | 2 +- .../vrfv2plus_wrapper/vrfv2plus_wrapper.go | 2 +- .../vrfv2plus_wrapper_consumer_example.go | 2 +- .../vrfv2plus_wrapper_load_test_consumer.go | 2 +- core/gethwrappers/go_generate.go | 2 +- .../errored_verifier/errored_verifier.go | 2 +- .../exposed_verifier/exposed_verifier.go | 2 +- .../generated/fee_manager/fee_manager.go | 2 +- .../reward_manager/reward_manager.go | 2 +- .../llo-feeds/generated/verifier/verifier.go | 2 +- .../verifier_proxy/verifier_proxy.go | 2 +- .../burn_mint_erc677/burn_mint_erc677.go | 2 +- .../shared/generated/erc20/erc20.go | 2 +- .../shared/generated/link_token/link_token.go | 2 +- .../generated/werc20_mock/werc20_mock.go | 2 +- .../generated/entry_point/entry_point.go | 2 +- .../greeter_wrapper/greeter_wrapper.go | 2 +- .../paymaster_wrapper/paymaster_wrapper.go | 2 +- .../generated/sca_wrapper/sca_wrapper.go | 2 +- .../smart_contract_account_factory.go | 2 +- .../smart_contract_account_helper.go | 2 +- 127 files changed, 207 insertions(+), 133 deletions(-) create mode 100644 core/gethwrappers/abigen_test.go diff --git a/core/gethwrappers/abigen.go b/core/gethwrappers/abigen.go index 5affe546576..d96d9f8c306 100644 --- a/core/gethwrappers/abigen.go +++ b/core/gethwrappers/abigen.go @@ -159,9 +159,17 @@ func getContractName(fileNode *ast.File) string { return contractName } +// Add the `.address` and `.abi` fields to the contract struct. func addContractStructFields(contractName string, fileNode *ast.File) *ast.File { - // Add the `.address` and `.abi` fields to the contract struct - fileNode = astutil.Apply(fileNode, func(cursor *astutil.Cursor) bool { + fileNode = addContractStructFieldsToStruct(contractName, fileNode) + fileNode = addContractStructFieldsToConstructor(contractName, fileNode) + fileNode = addContractStructFieldsToDeployMethod(contractName, fileNode) + return fileNode +} + +// Add the fields to the contract struct. +func addContractStructFieldsToStruct(contractName string, fileNode *ast.File) *ast.File { + return astutil.Apply(fileNode, func(cursor *astutil.Cursor) bool { x, is := cursor.Node().(*ast.StructType) if !is { return true @@ -188,14 +196,14 @@ func addContractStructFields(contractName string, fileNode *ast.File) *ast.File Sel: ast.NewIdent("ABI"), }, } - x.Fields.List = append([]*ast.Field{addrField, abiField}, x.Fields.List...) - return false }, nil).(*ast.File) +} - // Add the fields to the return value of the constructor - fileNode = astutil.Apply(fileNode, func(cursor *astutil.Cursor) bool { +// Add the fields to the return value of the constructor. +func addContractStructFieldsToConstructor(contractName string, fileNode *ast.File) *ast.File { + return astutil.Apply(fileNode, func(cursor *astutil.Cursor) bool { x, is := cursor.Node().(*ast.FuncDecl) if !is { return true @@ -260,11 +268,48 @@ func addContractStructFields(contractName string, fileNode *ast.File) *ast.File } x.Body.List = append([]ast.Stmt{parseABIStmt, checkParseABIErrStmt}, x.Body.List...) - return false }, nil).(*ast.File) +} - return fileNode +// Add the fields to the returned struct in the 'Deploy' method. +func addContractStructFieldsToDeployMethod(contractName string, fileNode *ast.File) *ast.File { + return astutil.Apply(fileNode, func(cursor *astutil.Cursor) bool { + x, is := cursor.Node().(*ast.FuncDecl) + if !is { + return true + } else if x.Name.Name != "Deploy"+contractName { + return false + } + + for _, stmt := range x.Body.List { + returnStmt, is := stmt.(*ast.ReturnStmt) + if !is { + continue + } + if len(returnStmt.Results) < 3 { + continue + } + rs, is := returnStmt.Results[2].(*ast.UnaryExpr) + if !is { + return true + } + lit, is := rs.X.(*ast.CompositeLit) + if !is { + continue + } + addressExpr := &ast.KeyValueExpr{ + Key: ast.NewIdent("address"), + Value: ast.NewIdent("address"), + } + abiExpr := &ast.KeyValueExpr{ + Key: ast.NewIdent("abi"), + Value: ast.NewIdent("*parsed"), + } + lit.Elts = append([]ast.Expr{addressExpr, abiExpr}, lit.Elts...) + } + return false + }, nil).(*ast.File) } func getLogNames(fileNode *ast.File) []string { diff --git a/core/gethwrappers/abigen_test.go b/core/gethwrappers/abigen_test.go new file mode 100644 index 00000000000..7c206f59dcd --- /dev/null +++ b/core/gethwrappers/abigen_test.go @@ -0,0 +1,29 @@ +package gethwrappers + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" +) + +// Test that the generated Deploy method fill all the required fields and returns the correct address. +// We perform this test using the generated LogEmitter wrapper. +func TestGeneratedDeployMethodAddressField(t *testing.T) { + owner := testutils.MustNewSimTransactor(t) + ec := backends.NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), map[common.Address]core.GenesisAccount{ + owner.From: { + Balance: big.NewInt(0).Mul(big.NewInt(10), big.NewInt(1e18)), + }, + }, 10e6) + emitterAddr, _, emitter, err := log_emitter.DeployLogEmitter(owner, ec) + require.NoError(t, err) + require.Equal(t, emitterAddr, emitter.Address()) +} diff --git a/core/gethwrappers/functions/generated/functions/functions.go b/core/gethwrappers/functions/generated/functions/functions.go index 419c21ca058..0c35806b66f 100644 --- a/core/gethwrappers/functions/generated/functions/functions.go +++ b/core/gethwrappers/functions/generated/functions/functions.go @@ -50,7 +50,7 @@ func DeployFunctions(auth *bind.TransactOpts, backend bind.ContractBackend) (com if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Functions{FunctionsCaller: FunctionsCaller{contract: contract}, FunctionsTransactor: FunctionsTransactor{contract: contract}, FunctionsFilterer: FunctionsFilterer{contract: contract}}, nil + return address, tx, &Functions{address: address, abi: *parsed, FunctionsCaller: FunctionsCaller{contract: contract}, FunctionsTransactor: FunctionsTransactor{contract: contract}, FunctionsFilterer: FunctionsFilterer{contract: contract}}, nil } type Functions struct { diff --git a/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go b/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go index d86c07f1705..0ccb08cdaa9 100644 --- a/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go +++ b/core/gethwrappers/functions/generated/functions_allow_list/functions_allow_list.go @@ -57,7 +57,7 @@ func DeployTermsOfServiceAllowList(auth *bind.TransactOpts, backend bind.Contrac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &TermsOfServiceAllowList{TermsOfServiceAllowListCaller: TermsOfServiceAllowListCaller{contract: contract}, TermsOfServiceAllowListTransactor: TermsOfServiceAllowListTransactor{contract: contract}, TermsOfServiceAllowListFilterer: TermsOfServiceAllowListFilterer{contract: contract}}, nil + return address, tx, &TermsOfServiceAllowList{address: address, abi: *parsed, TermsOfServiceAllowListCaller: TermsOfServiceAllowListCaller{contract: contract}, TermsOfServiceAllowListTransactor: TermsOfServiceAllowListTransactor{contract: contract}, TermsOfServiceAllowListFilterer: TermsOfServiceAllowListFilterer{contract: contract}}, nil } type TermsOfServiceAllowList struct { diff --git a/core/gethwrappers/functions/generated/functions_client_example/functions_client_example.go b/core/gethwrappers/functions/generated/functions_client_example/functions_client_example.go index 6ae90b45edb..00bf6a438fc 100644 --- a/core/gethwrappers/functions/generated/functions_client_example/functions_client_example.go +++ b/core/gethwrappers/functions/generated/functions_client_example/functions_client_example.go @@ -52,7 +52,7 @@ func DeployFunctionsClientExample(auth *bind.TransactOpts, backend bind.Contract if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsClientExample{FunctionsClientExampleCaller: FunctionsClientExampleCaller{contract: contract}, FunctionsClientExampleTransactor: FunctionsClientExampleTransactor{contract: contract}, FunctionsClientExampleFilterer: FunctionsClientExampleFilterer{contract: contract}}, nil + return address, tx, &FunctionsClientExample{address: address, abi: *parsed, FunctionsClientExampleCaller: FunctionsClientExampleCaller{contract: contract}, FunctionsClientExampleTransactor: FunctionsClientExampleTransactor{contract: contract}, FunctionsClientExampleFilterer: FunctionsClientExampleFilterer{contract: contract}}, nil } type FunctionsClientExample struct { diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index 32db52a029f..ffe072fc657 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -92,7 +92,7 @@ func DeployFunctionsCoordinator(auth *bind.TransactOpts, backend bind.ContractBa if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsCoordinator{FunctionsCoordinatorCaller: FunctionsCoordinatorCaller{contract: contract}, FunctionsCoordinatorTransactor: FunctionsCoordinatorTransactor{contract: contract}, FunctionsCoordinatorFilterer: FunctionsCoordinatorFilterer{contract: contract}}, nil + return address, tx, &FunctionsCoordinator{address: address, abi: *parsed, FunctionsCoordinatorCaller: FunctionsCoordinatorCaller{contract: contract}, FunctionsCoordinatorTransactor: FunctionsCoordinatorTransactor{contract: contract}, FunctionsCoordinatorFilterer: FunctionsCoordinatorFilterer{contract: contract}}, nil } type FunctionsCoordinator struct { diff --git a/core/gethwrappers/functions/generated/functions_load_test_client/functions_load_test_client.go b/core/gethwrappers/functions/generated/functions_load_test_client/functions_load_test_client.go index 37a895fe8cd..a03d6a5b144 100644 --- a/core/gethwrappers/functions/generated/functions_load_test_client/functions_load_test_client.go +++ b/core/gethwrappers/functions/generated/functions_load_test_client/functions_load_test_client.go @@ -52,7 +52,7 @@ func DeployFunctionsLoadTestClient(auth *bind.TransactOpts, backend bind.Contrac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsLoadTestClient{FunctionsLoadTestClientCaller: FunctionsLoadTestClientCaller{contract: contract}, FunctionsLoadTestClientTransactor: FunctionsLoadTestClientTransactor{contract: contract}, FunctionsLoadTestClientFilterer: FunctionsLoadTestClientFilterer{contract: contract}}, nil + return address, tx, &FunctionsLoadTestClient{address: address, abi: *parsed, FunctionsLoadTestClientCaller: FunctionsLoadTestClientCaller{contract: contract}, FunctionsLoadTestClientTransactor: FunctionsLoadTestClientTransactor{contract: contract}, FunctionsLoadTestClientFilterer: FunctionsLoadTestClientFilterer{contract: contract}}, nil } type FunctionsLoadTestClient struct { diff --git a/core/gethwrappers/functions/generated/functions_router/functions_router.go b/core/gethwrappers/functions/generated/functions_router/functions_router.go index 2c482048c49..592f95b568f 100644 --- a/core/gethwrappers/functions/generated/functions_router/functions_router.go +++ b/core/gethwrappers/functions/generated/functions_router/functions_router.go @@ -91,7 +91,7 @@ func DeployFunctionsRouter(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsRouter{FunctionsRouterCaller: FunctionsRouterCaller{contract: contract}, FunctionsRouterTransactor: FunctionsRouterTransactor{contract: contract}, FunctionsRouterFilterer: FunctionsRouterFilterer{contract: contract}}, nil + return address, tx, &FunctionsRouter{address: address, abi: *parsed, FunctionsRouterCaller: FunctionsRouterCaller{contract: contract}, FunctionsRouterTransactor: FunctionsRouterTransactor{contract: contract}, FunctionsRouterFilterer: FunctionsRouterFilterer{contract: contract}}, nil } type FunctionsRouter struct { diff --git a/core/gethwrappers/functions/generated/functions_v1_events_mock/functions_v1_events_mock.go b/core/gethwrappers/functions/generated/functions_v1_events_mock/functions_v1_events_mock.go index ae1fe20401c..422d2e8b2ae 100644 --- a/core/gethwrappers/functions/generated/functions_v1_events_mock/functions_v1_events_mock.go +++ b/core/gethwrappers/functions/generated/functions_v1_events_mock/functions_v1_events_mock.go @@ -60,7 +60,7 @@ func DeployFunctionsV1EventsMock(auth *bind.TransactOpts, backend bind.ContractB if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsV1EventsMock{FunctionsV1EventsMockCaller: FunctionsV1EventsMockCaller{contract: contract}, FunctionsV1EventsMockTransactor: FunctionsV1EventsMockTransactor{contract: contract}, FunctionsV1EventsMockFilterer: FunctionsV1EventsMockFilterer{contract: contract}}, nil + return address, tx, &FunctionsV1EventsMock{address: address, abi: *parsed, FunctionsV1EventsMockCaller: FunctionsV1EventsMockCaller{contract: contract}, FunctionsV1EventsMockTransactor: FunctionsV1EventsMockTransactor{contract: contract}, FunctionsV1EventsMockFilterer: FunctionsV1EventsMockFilterer{contract: contract}}, nil } type FunctionsV1EventsMock struct { diff --git a/core/gethwrappers/functions/generated/ocr2dr/ocr2dr.go b/core/gethwrappers/functions/generated/ocr2dr/ocr2dr.go index 8cc65942168..c2b3c3a5e91 100644 --- a/core/gethwrappers/functions/generated/ocr2dr/ocr2dr.go +++ b/core/gethwrappers/functions/generated/ocr2dr/ocr2dr.go @@ -50,7 +50,7 @@ func DeployOCR2DR(auth *bind.TransactOpts, backend bind.ContractBackend) (common if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &OCR2DR{OCR2DRCaller: OCR2DRCaller{contract: contract}, OCR2DRTransactor: OCR2DRTransactor{contract: contract}, OCR2DRFilterer: OCR2DRFilterer{contract: contract}}, nil + return address, tx, &OCR2DR{address: address, abi: *parsed, OCR2DRCaller: OCR2DRCaller{contract: contract}, OCR2DRTransactor: OCR2DRTransactor{contract: contract}, OCR2DRFilterer: OCR2DRFilterer{contract: contract}}, nil } type OCR2DR struct { diff --git a/core/gethwrappers/functions/generated/ocr2dr_client_example/ocr2dr_client_example.go b/core/gethwrappers/functions/generated/ocr2dr_client_example/ocr2dr_client_example.go index a4b94f7e927..c37ba64b8d9 100644 --- a/core/gethwrappers/functions/generated/ocr2dr_client_example/ocr2dr_client_example.go +++ b/core/gethwrappers/functions/generated/ocr2dr_client_example/ocr2dr_client_example.go @@ -61,7 +61,7 @@ func DeployOCR2DRClientExample(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &OCR2DRClientExample{OCR2DRClientExampleCaller: OCR2DRClientExampleCaller{contract: contract}, OCR2DRClientExampleTransactor: OCR2DRClientExampleTransactor{contract: contract}, OCR2DRClientExampleFilterer: OCR2DRClientExampleFilterer{contract: contract}}, nil + return address, tx, &OCR2DRClientExample{address: address, abi: *parsed, OCR2DRClientExampleCaller: OCR2DRClientExampleCaller{contract: contract}, OCR2DRClientExampleTransactor: OCR2DRClientExampleTransactor{contract: contract}, OCR2DRClientExampleFilterer: OCR2DRClientExampleFilterer{contract: contract}}, nil } type OCR2DRClientExample struct { diff --git a/core/gethwrappers/functions/generated/ocr2dr_oracle/ocr2dr_oracle.go b/core/gethwrappers/functions/generated/ocr2dr_oracle/ocr2dr_oracle.go index 509a3c83883..a2f81cfb30c 100644 --- a/core/gethwrappers/functions/generated/ocr2dr_oracle/ocr2dr_oracle.go +++ b/core/gethwrappers/functions/generated/ocr2dr_oracle/ocr2dr_oracle.go @@ -59,7 +59,7 @@ func DeployOCR2DROracle(auth *bind.TransactOpts, backend bind.ContractBackend) ( if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &OCR2DROracle{OCR2DROracleCaller: OCR2DROracleCaller{contract: contract}, OCR2DROracleTransactor: OCR2DROracleTransactor{contract: contract}, OCR2DROracleFilterer: OCR2DROracleFilterer{contract: contract}}, nil + return address, tx, &OCR2DROracle{address: address, abi: *parsed, OCR2DROracleCaller: OCR2DROracleCaller{contract: contract}, OCR2DROracleTransactor: OCR2DROracleTransactor{contract: contract}, OCR2DROracleFilterer: OCR2DROracleFilterer{contract: contract}}, nil } type OCR2DROracle struct { diff --git a/core/gethwrappers/functions/generated/ocr2dr_registry/ocr2dr_registry.go b/core/gethwrappers/functions/generated/ocr2dr_registry/ocr2dr_registry.go index eb825b5eaa6..f81a4316203 100644 --- a/core/gethwrappers/functions/generated/ocr2dr_registry/ocr2dr_registry.go +++ b/core/gethwrappers/functions/generated/ocr2dr_registry/ocr2dr_registry.go @@ -71,7 +71,7 @@ func DeployOCR2DRRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &OCR2DRRegistry{OCR2DRRegistryCaller: OCR2DRRegistryCaller{contract: contract}, OCR2DRRegistryTransactor: OCR2DRRegistryTransactor{contract: contract}, OCR2DRRegistryFilterer: OCR2DRRegistryFilterer{contract: contract}}, nil + return address, tx, &OCR2DRRegistry{address: address, abi: *parsed, OCR2DRRegistryCaller: OCR2DRRegistryCaller{contract: contract}, OCR2DRRegistryTransactor: OCR2DRRegistryTransactor{contract: contract}, OCR2DRRegistryFilterer: OCR2DRRegistryFilterer{contract: contract}}, nil } type OCR2DRRegistry struct { diff --git a/core/gethwrappers/generated/authorized_forwarder/authorized_forwarder.go b/core/gethwrappers/generated/authorized_forwarder/authorized_forwarder.go index 6417a9c6782..03643c9154b 100644 --- a/core/gethwrappers/generated/authorized_forwarder/authorized_forwarder.go +++ b/core/gethwrappers/generated/authorized_forwarder/authorized_forwarder.go @@ -52,7 +52,7 @@ func DeployAuthorizedForwarder(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &AuthorizedForwarder{AuthorizedForwarderCaller: AuthorizedForwarderCaller{contract: contract}, AuthorizedForwarderTransactor: AuthorizedForwarderTransactor{contract: contract}, AuthorizedForwarderFilterer: AuthorizedForwarderFilterer{contract: contract}}, nil + return address, tx, &AuthorizedForwarder{address: address, abi: *parsed, AuthorizedForwarderCaller: AuthorizedForwarderCaller{contract: contract}, AuthorizedForwarderTransactor: AuthorizedForwarderTransactor{contract: contract}, AuthorizedForwarderFilterer: AuthorizedForwarderFilterer{contract: contract}}, nil } type AuthorizedForwarder struct { diff --git a/core/gethwrappers/generated/automation_consumer_benchmark/automation_consumer_benchmark.go b/core/gethwrappers/generated/automation_consumer_benchmark/automation_consumer_benchmark.go index f4d59e7e7de..3dafe68921f 100644 --- a/core/gethwrappers/generated/automation_consumer_benchmark/automation_consumer_benchmark.go +++ b/core/gethwrappers/generated/automation_consumer_benchmark/automation_consumer_benchmark.go @@ -52,7 +52,7 @@ func DeployAutomationConsumerBenchmark(auth *bind.TransactOpts, backend bind.Con if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &AutomationConsumerBenchmark{AutomationConsumerBenchmarkCaller: AutomationConsumerBenchmarkCaller{contract: contract}, AutomationConsumerBenchmarkTransactor: AutomationConsumerBenchmarkTransactor{contract: contract}, AutomationConsumerBenchmarkFilterer: AutomationConsumerBenchmarkFilterer{contract: contract}}, nil + return address, tx, &AutomationConsumerBenchmark{address: address, abi: *parsed, AutomationConsumerBenchmarkCaller: AutomationConsumerBenchmarkCaller{contract: contract}, AutomationConsumerBenchmarkTransactor: AutomationConsumerBenchmarkTransactor{contract: contract}, AutomationConsumerBenchmarkFilterer: AutomationConsumerBenchmarkFilterer{contract: contract}}, nil } type AutomationConsumerBenchmark struct { diff --git a/core/gethwrappers/generated/automation_forwarder_logic/automation_forwarder_logic.go b/core/gethwrappers/generated/automation_forwarder_logic/automation_forwarder_logic.go index b26f5e5692c..8b35a68c826 100644 --- a/core/gethwrappers/generated/automation_forwarder_logic/automation_forwarder_logic.go +++ b/core/gethwrappers/generated/automation_forwarder_logic/automation_forwarder_logic.go @@ -50,7 +50,7 @@ func DeployAutomationForwarderLogic(auth *bind.TransactOpts, backend bind.Contra if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &AutomationForwarderLogic{AutomationForwarderLogicCaller: AutomationForwarderLogicCaller{contract: contract}, AutomationForwarderLogicTransactor: AutomationForwarderLogicTransactor{contract: contract}, AutomationForwarderLogicFilterer: AutomationForwarderLogicFilterer{contract: contract}}, nil + return address, tx, &AutomationForwarderLogic{address: address, abi: *parsed, AutomationForwarderLogicCaller: AutomationForwarderLogicCaller{contract: contract}, AutomationForwarderLogicTransactor: AutomationForwarderLogicTransactor{contract: contract}, AutomationForwarderLogicFilterer: AutomationForwarderLogicFilterer{contract: contract}}, nil } type AutomationForwarderLogic struct { diff --git a/core/gethwrappers/generated/automation_registrar_wrapper2_1/automation_registrar_wrapper2_1.go b/core/gethwrappers/generated/automation_registrar_wrapper2_1/automation_registrar_wrapper2_1.go index d2b8054436a..311ce75c108 100644 --- a/core/gethwrappers/generated/automation_registrar_wrapper2_1/automation_registrar_wrapper2_1.go +++ b/core/gethwrappers/generated/automation_registrar_wrapper2_1/automation_registrar_wrapper2_1.go @@ -77,7 +77,7 @@ func DeployAutomationRegistrar(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &AutomationRegistrar{AutomationRegistrarCaller: AutomationRegistrarCaller{contract: contract}, AutomationRegistrarTransactor: AutomationRegistrarTransactor{contract: contract}, AutomationRegistrarFilterer: AutomationRegistrarFilterer{contract: contract}}, nil + return address, tx, &AutomationRegistrar{address: address, abi: *parsed, AutomationRegistrarCaller: AutomationRegistrarCaller{contract: contract}, AutomationRegistrarTransactor: AutomationRegistrarTransactor{contract: contract}, AutomationRegistrarFilterer: AutomationRegistrarFilterer{contract: contract}}, nil } type AutomationRegistrar struct { diff --git a/core/gethwrappers/generated/automation_utils_2_1/automation_utils_2_1.go b/core/gethwrappers/generated/automation_utils_2_1/automation_utils_2_1.go index 1eaa4ea03fe..5d6dc1e41ca 100644 --- a/core/gethwrappers/generated/automation_utils_2_1/automation_utils_2_1.go +++ b/core/gethwrappers/generated/automation_utils_2_1/automation_utils_2_1.go @@ -110,7 +110,7 @@ func DeployAutomationUtils(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &AutomationUtils{AutomationUtilsCaller: AutomationUtilsCaller{contract: contract}, AutomationUtilsTransactor: AutomationUtilsTransactor{contract: contract}, AutomationUtilsFilterer: AutomationUtilsFilterer{contract: contract}}, nil + return address, tx, &AutomationUtils{address: address, abi: *parsed, AutomationUtilsCaller: AutomationUtilsCaller{contract: contract}, AutomationUtilsTransactor: AutomationUtilsTransactor{contract: contract}, AutomationUtilsFilterer: AutomationUtilsFilterer{contract: contract}}, nil } type AutomationUtils struct { diff --git a/core/gethwrappers/generated/batch_blockhash_store/batch_blockhash_store.go b/core/gethwrappers/generated/batch_blockhash_store/batch_blockhash_store.go index 426c5a2c79d..1a43287d748 100644 --- a/core/gethwrappers/generated/batch_blockhash_store/batch_blockhash_store.go +++ b/core/gethwrappers/generated/batch_blockhash_store/batch_blockhash_store.go @@ -50,7 +50,7 @@ func DeployBatchBlockhashStore(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &BatchBlockhashStore{BatchBlockhashStoreCaller: BatchBlockhashStoreCaller{contract: contract}, BatchBlockhashStoreTransactor: BatchBlockhashStoreTransactor{contract: contract}, BatchBlockhashStoreFilterer: BatchBlockhashStoreFilterer{contract: contract}}, nil + return address, tx, &BatchBlockhashStore{address: address, abi: *parsed, BatchBlockhashStoreCaller: BatchBlockhashStoreCaller{contract: contract}, BatchBlockhashStoreTransactor: BatchBlockhashStoreTransactor{contract: contract}, BatchBlockhashStoreFilterer: BatchBlockhashStoreFilterer{contract: contract}}, nil } type BatchBlockhashStore struct { diff --git a/core/gethwrappers/generated/batch_vrf_coordinator_v2/batch_vrf_coordinator_v2.go b/core/gethwrappers/generated/batch_vrf_coordinator_v2/batch_vrf_coordinator_v2.go index 4cb6e76b964..3a1ec957b93 100644 --- a/core/gethwrappers/generated/batch_vrf_coordinator_v2/batch_vrf_coordinator_v2.go +++ b/core/gethwrappers/generated/batch_vrf_coordinator_v2/batch_vrf_coordinator_v2.go @@ -72,7 +72,7 @@ func DeployBatchVRFCoordinatorV2(auth *bind.TransactOpts, backend bind.ContractB if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &BatchVRFCoordinatorV2{BatchVRFCoordinatorV2Caller: BatchVRFCoordinatorV2Caller{contract: contract}, BatchVRFCoordinatorV2Transactor: BatchVRFCoordinatorV2Transactor{contract: contract}, BatchVRFCoordinatorV2Filterer: BatchVRFCoordinatorV2Filterer{contract: contract}}, nil + return address, tx, &BatchVRFCoordinatorV2{address: address, abi: *parsed, BatchVRFCoordinatorV2Caller: BatchVRFCoordinatorV2Caller{contract: contract}, BatchVRFCoordinatorV2Transactor: BatchVRFCoordinatorV2Transactor{contract: contract}, BatchVRFCoordinatorV2Filterer: BatchVRFCoordinatorV2Filterer{contract: contract}}, nil } type BatchVRFCoordinatorV2 struct { diff --git a/core/gethwrappers/generated/batch_vrf_coordinator_v2plus/batch_vrf_coordinator_v2plus.go b/core/gethwrappers/generated/batch_vrf_coordinator_v2plus/batch_vrf_coordinator_v2plus.go index 610f3a27006..58f134fe2a6 100644 --- a/core/gethwrappers/generated/batch_vrf_coordinator_v2plus/batch_vrf_coordinator_v2plus.go +++ b/core/gethwrappers/generated/batch_vrf_coordinator_v2plus/batch_vrf_coordinator_v2plus.go @@ -73,7 +73,7 @@ func DeployBatchVRFCoordinatorV2Plus(auth *bind.TransactOpts, backend bind.Contr if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &BatchVRFCoordinatorV2Plus{BatchVRFCoordinatorV2PlusCaller: BatchVRFCoordinatorV2PlusCaller{contract: contract}, BatchVRFCoordinatorV2PlusTransactor: BatchVRFCoordinatorV2PlusTransactor{contract: contract}, BatchVRFCoordinatorV2PlusFilterer: BatchVRFCoordinatorV2PlusFilterer{contract: contract}}, nil + return address, tx, &BatchVRFCoordinatorV2Plus{address: address, abi: *parsed, BatchVRFCoordinatorV2PlusCaller: BatchVRFCoordinatorV2PlusCaller{contract: contract}, BatchVRFCoordinatorV2PlusTransactor: BatchVRFCoordinatorV2PlusTransactor{contract: contract}, BatchVRFCoordinatorV2PlusFilterer: BatchVRFCoordinatorV2PlusFilterer{contract: contract}}, nil } type BatchVRFCoordinatorV2Plus struct { diff --git a/core/gethwrappers/generated/blockhash_store/blockhash_store.go b/core/gethwrappers/generated/blockhash_store/blockhash_store.go index 8711f13b2d6..e43f9f450eb 100644 --- a/core/gethwrappers/generated/blockhash_store/blockhash_store.go +++ b/core/gethwrappers/generated/blockhash_store/blockhash_store.go @@ -50,7 +50,7 @@ func DeployBlockhashStore(auth *bind.TransactOpts, backend bind.ContractBackend) if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &BlockhashStore{BlockhashStoreCaller: BlockhashStoreCaller{contract: contract}, BlockhashStoreTransactor: BlockhashStoreTransactor{contract: contract}, BlockhashStoreFilterer: BlockhashStoreFilterer{contract: contract}}, nil + return address, tx, &BlockhashStore{address: address, abi: *parsed, BlockhashStoreCaller: BlockhashStoreCaller{contract: contract}, BlockhashStoreTransactor: BlockhashStoreTransactor{contract: contract}, BlockhashStoreFilterer: BlockhashStoreFilterer{contract: contract}}, nil } type BlockhashStore struct { diff --git a/core/gethwrappers/generated/chain_specific_util_helper/chain_specific_util_helper.go b/core/gethwrappers/generated/chain_specific_util_helper/chain_specific_util_helper.go index aa4dc0f88d9..b32c3d18d55 100644 --- a/core/gethwrappers/generated/chain_specific_util_helper/chain_specific_util_helper.go +++ b/core/gethwrappers/generated/chain_specific_util_helper/chain_specific_util_helper.go @@ -50,7 +50,7 @@ func DeployChainSpecificUtilHelper(auth *bind.TransactOpts, backend bind.Contrac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &ChainSpecificUtilHelper{ChainSpecificUtilHelperCaller: ChainSpecificUtilHelperCaller{contract: contract}, ChainSpecificUtilHelperTransactor: ChainSpecificUtilHelperTransactor{contract: contract}, ChainSpecificUtilHelperFilterer: ChainSpecificUtilHelperFilterer{contract: contract}}, nil + return address, tx, &ChainSpecificUtilHelper{address: address, abi: *parsed, ChainSpecificUtilHelperCaller: ChainSpecificUtilHelperCaller{contract: contract}, ChainSpecificUtilHelperTransactor: ChainSpecificUtilHelperTransactor{contract: contract}, ChainSpecificUtilHelperFilterer: ChainSpecificUtilHelperFilterer{contract: contract}}, nil } type ChainSpecificUtilHelper struct { diff --git a/core/gethwrappers/generated/consumer_wrapper/consumer_wrapper.go b/core/gethwrappers/generated/consumer_wrapper/consumer_wrapper.go index 954f66a08ef..0c0386b085f 100644 --- a/core/gethwrappers/generated/consumer_wrapper/consumer_wrapper.go +++ b/core/gethwrappers/generated/consumer_wrapper/consumer_wrapper.go @@ -52,7 +52,7 @@ func DeployConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, _link if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Consumer{ConsumerCaller: ConsumerCaller{contract: contract}, ConsumerTransactor: ConsumerTransactor{contract: contract}, ConsumerFilterer: ConsumerFilterer{contract: contract}}, nil + return address, tx, &Consumer{address: address, abi: *parsed, ConsumerCaller: ConsumerCaller{contract: contract}, ConsumerTransactor: ConsumerTransactor{contract: contract}, ConsumerFilterer: ConsumerFilterer{contract: contract}}, nil } type Consumer struct { diff --git a/core/gethwrappers/generated/dummy_protocol_wrapper/dummy_protocol_wrapper.go b/core/gethwrappers/generated/dummy_protocol_wrapper/dummy_protocol_wrapper.go index e7a88fdd06e..e2838b3f6fb 100644 --- a/core/gethwrappers/generated/dummy_protocol_wrapper/dummy_protocol_wrapper.go +++ b/core/gethwrappers/generated/dummy_protocol_wrapper/dummy_protocol_wrapper.go @@ -52,7 +52,7 @@ func DeployDummyProtocol(auth *bind.TransactOpts, backend bind.ContractBackend) if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &DummyProtocol{DummyProtocolCaller: DummyProtocolCaller{contract: contract}, DummyProtocolTransactor: DummyProtocolTransactor{contract: contract}, DummyProtocolFilterer: DummyProtocolFilterer{contract: contract}}, nil + return address, tx, &DummyProtocol{address: address, abi: *parsed, DummyProtocolCaller: DummyProtocolCaller{contract: contract}, DummyProtocolTransactor: DummyProtocolTransactor{contract: contract}, DummyProtocolFilterer: DummyProtocolFilterer{contract: contract}}, nil } type DummyProtocol struct { diff --git a/core/gethwrappers/generated/flags_wrapper/flags_wrapper.go b/core/gethwrappers/generated/flags_wrapper/flags_wrapper.go index f8d3c21b54f..590f9e2af96 100644 --- a/core/gethwrappers/generated/flags_wrapper/flags_wrapper.go +++ b/core/gethwrappers/generated/flags_wrapper/flags_wrapper.go @@ -52,7 +52,7 @@ func DeployFlags(auth *bind.TransactOpts, backend bind.ContractBackend, racAddre if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Flags{FlagsCaller: FlagsCaller{contract: contract}, FlagsTransactor: FlagsTransactor{contract: contract}, FlagsFilterer: FlagsFilterer{contract: contract}}, nil + return address, tx, &Flags{address: address, abi: *parsed, FlagsCaller: FlagsCaller{contract: contract}, FlagsTransactor: FlagsTransactor{contract: contract}, FlagsFilterer: FlagsFilterer{contract: contract}}, nil } type Flags struct { diff --git a/core/gethwrappers/generated/flux_aggregator_wrapper/flux_aggregator_wrapper.go b/core/gethwrappers/generated/flux_aggregator_wrapper/flux_aggregator_wrapper.go index 6c4cb73af7a..876ef73487c 100644 --- a/core/gethwrappers/generated/flux_aggregator_wrapper/flux_aggregator_wrapper.go +++ b/core/gethwrappers/generated/flux_aggregator_wrapper/flux_aggregator_wrapper.go @@ -52,7 +52,7 @@ func DeployFluxAggregator(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FluxAggregator{FluxAggregatorCaller: FluxAggregatorCaller{contract: contract}, FluxAggregatorTransactor: FluxAggregatorTransactor{contract: contract}, FluxAggregatorFilterer: FluxAggregatorFilterer{contract: contract}}, nil + return address, tx, &FluxAggregator{address: address, abi: *parsed, FluxAggregatorCaller: FluxAggregatorCaller{contract: contract}, FluxAggregatorTransactor: FluxAggregatorTransactor{contract: contract}, FluxAggregatorFilterer: FluxAggregatorFilterer{contract: contract}}, nil } type FluxAggregator struct { diff --git a/core/gethwrappers/generated/functions_billing_registry_events_mock/functions_billing_registry_events_mock.go b/core/gethwrappers/generated/functions_billing_registry_events_mock/functions_billing_registry_events_mock.go index ec87fb9e740..c86c4feaa4b 100644 --- a/core/gethwrappers/generated/functions_billing_registry_events_mock/functions_billing_registry_events_mock.go +++ b/core/gethwrappers/generated/functions_billing_registry_events_mock/functions_billing_registry_events_mock.go @@ -64,7 +64,7 @@ func DeployFunctionsBillingRegistryEventsMock(auth *bind.TransactOpts, backend b if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsBillingRegistryEventsMock{FunctionsBillingRegistryEventsMockCaller: FunctionsBillingRegistryEventsMockCaller{contract: contract}, FunctionsBillingRegistryEventsMockTransactor: FunctionsBillingRegistryEventsMockTransactor{contract: contract}, FunctionsBillingRegistryEventsMockFilterer: FunctionsBillingRegistryEventsMockFilterer{contract: contract}}, nil + return address, tx, &FunctionsBillingRegistryEventsMock{address: address, abi: *parsed, FunctionsBillingRegistryEventsMockCaller: FunctionsBillingRegistryEventsMockCaller{contract: contract}, FunctionsBillingRegistryEventsMockTransactor: FunctionsBillingRegistryEventsMockTransactor{contract: contract}, FunctionsBillingRegistryEventsMockFilterer: FunctionsBillingRegistryEventsMockFilterer{contract: contract}}, nil } type FunctionsBillingRegistryEventsMock struct { diff --git a/core/gethwrappers/generated/functions_oracle_events_mock/functions_oracle_events_mock.go b/core/gethwrappers/generated/functions_oracle_events_mock/functions_oracle_events_mock.go index 3b8238c00e0..4e97e082394 100644 --- a/core/gethwrappers/generated/functions_oracle_events_mock/functions_oracle_events_mock.go +++ b/core/gethwrappers/generated/functions_oracle_events_mock/functions_oracle_events_mock.go @@ -52,7 +52,7 @@ func DeployFunctionsOracleEventsMock(auth *bind.TransactOpts, backend bind.Contr if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FunctionsOracleEventsMock{FunctionsOracleEventsMockCaller: FunctionsOracleEventsMockCaller{contract: contract}, FunctionsOracleEventsMockTransactor: FunctionsOracleEventsMockTransactor{contract: contract}, FunctionsOracleEventsMockFilterer: FunctionsOracleEventsMockFilterer{contract: contract}}, nil + return address, tx, &FunctionsOracleEventsMock{address: address, abi: *parsed, FunctionsOracleEventsMockCaller: FunctionsOracleEventsMockCaller{contract: contract}, FunctionsOracleEventsMockTransactor: FunctionsOracleEventsMockTransactor{contract: contract}, FunctionsOracleEventsMockFilterer: FunctionsOracleEventsMockFilterer{contract: contract}}, nil } type FunctionsOracleEventsMock struct { diff --git a/core/gethwrappers/generated/gas_wrapper/gas_wrapper.go b/core/gethwrappers/generated/gas_wrapper/gas_wrapper.go index f2e25e36a7d..eec0cf855f3 100644 --- a/core/gethwrappers/generated/gas_wrapper/gas_wrapper.go +++ b/core/gethwrappers/generated/gas_wrapper/gas_wrapper.go @@ -52,7 +52,7 @@ func DeployKeeperRegistryCheckUpkeepGasUsageWrapper(auth *bind.TransactOpts, bac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryCheckUpkeepGasUsageWrapper{KeeperRegistryCheckUpkeepGasUsageWrapperCaller: KeeperRegistryCheckUpkeepGasUsageWrapperCaller{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperTransactor: KeeperRegistryCheckUpkeepGasUsageWrapperTransactor{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperFilterer: KeeperRegistryCheckUpkeepGasUsageWrapperFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryCheckUpkeepGasUsageWrapper{address: address, abi: *parsed, KeeperRegistryCheckUpkeepGasUsageWrapperCaller: KeeperRegistryCheckUpkeepGasUsageWrapperCaller{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperTransactor: KeeperRegistryCheckUpkeepGasUsageWrapperTransactor{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperFilterer: KeeperRegistryCheckUpkeepGasUsageWrapperFilterer{contract: contract}}, nil } type KeeperRegistryCheckUpkeepGasUsageWrapper struct { diff --git a/core/gethwrappers/generated/gas_wrapper_mock/gas_wrapper_mock.go b/core/gethwrappers/generated/gas_wrapper_mock/gas_wrapper_mock.go index b0420097b43..93803ce9938 100644 --- a/core/gethwrappers/generated/gas_wrapper_mock/gas_wrapper_mock.go +++ b/core/gethwrappers/generated/gas_wrapper_mock/gas_wrapper_mock.go @@ -52,7 +52,7 @@ func DeployKeeperRegistryCheckUpkeepGasUsageWrapperMock(auth *bind.TransactOpts, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryCheckUpkeepGasUsageWrapperMock{KeeperRegistryCheckUpkeepGasUsageWrapperMockCaller: KeeperRegistryCheckUpkeepGasUsageWrapperMockCaller{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperMockTransactor: KeeperRegistryCheckUpkeepGasUsageWrapperMockTransactor{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperMockFilterer: KeeperRegistryCheckUpkeepGasUsageWrapperMockFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryCheckUpkeepGasUsageWrapperMock{address: address, abi: *parsed, KeeperRegistryCheckUpkeepGasUsageWrapperMockCaller: KeeperRegistryCheckUpkeepGasUsageWrapperMockCaller{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperMockTransactor: KeeperRegistryCheckUpkeepGasUsageWrapperMockTransactor{contract: contract}, KeeperRegistryCheckUpkeepGasUsageWrapperMockFilterer: KeeperRegistryCheckUpkeepGasUsageWrapperMockFilterer{contract: contract}}, nil } type KeeperRegistryCheckUpkeepGasUsageWrapperMock struct { diff --git a/core/gethwrappers/generated/keeper_consumer_performance_wrapper/keeper_consumer_performance_wrapper.go b/core/gethwrappers/generated/keeper_consumer_performance_wrapper/keeper_consumer_performance_wrapper.go index 0e1876ce0a2..08178c98824 100644 --- a/core/gethwrappers/generated/keeper_consumer_performance_wrapper/keeper_consumer_performance_wrapper.go +++ b/core/gethwrappers/generated/keeper_consumer_performance_wrapper/keeper_consumer_performance_wrapper.go @@ -52,7 +52,7 @@ func DeployKeeperConsumerPerformance(auth *bind.TransactOpts, backend bind.Contr if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperConsumerPerformance{KeeperConsumerPerformanceCaller: KeeperConsumerPerformanceCaller{contract: contract}, KeeperConsumerPerformanceTransactor: KeeperConsumerPerformanceTransactor{contract: contract}, KeeperConsumerPerformanceFilterer: KeeperConsumerPerformanceFilterer{contract: contract}}, nil + return address, tx, &KeeperConsumerPerformance{address: address, abi: *parsed, KeeperConsumerPerformanceCaller: KeeperConsumerPerformanceCaller{contract: contract}, KeeperConsumerPerformanceTransactor: KeeperConsumerPerformanceTransactor{contract: contract}, KeeperConsumerPerformanceFilterer: KeeperConsumerPerformanceFilterer{contract: contract}}, nil } type KeeperConsumerPerformance struct { diff --git a/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go b/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go index 896927f9e6d..8a4ee2c4de8 100644 --- a/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go +++ b/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go @@ -50,7 +50,7 @@ func DeployKeeperConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperConsumer{KeeperConsumerCaller: KeeperConsumerCaller{contract: contract}, KeeperConsumerTransactor: KeeperConsumerTransactor{contract: contract}, KeeperConsumerFilterer: KeeperConsumerFilterer{contract: contract}}, nil + return address, tx, &KeeperConsumer{address: address, abi: *parsed, KeeperConsumerCaller: KeeperConsumerCaller{contract: contract}, KeeperConsumerTransactor: KeeperConsumerTransactor{contract: contract}, KeeperConsumerFilterer: KeeperConsumerFilterer{contract: contract}}, nil } type KeeperConsumer struct { diff --git a/core/gethwrappers/generated/keeper_registrar_wrapper1_2/keeper_registrar_wrapper1_2.go b/core/gethwrappers/generated/keeper_registrar_wrapper1_2/keeper_registrar_wrapper1_2.go index 3721238f67e..45564b662d0 100644 --- a/core/gethwrappers/generated/keeper_registrar_wrapper1_2/keeper_registrar_wrapper1_2.go +++ b/core/gethwrappers/generated/keeper_registrar_wrapper1_2/keeper_registrar_wrapper1_2.go @@ -52,7 +52,7 @@ func DeployKeeperRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistrar{KeeperRegistrarCaller: KeeperRegistrarCaller{contract: contract}, KeeperRegistrarTransactor: KeeperRegistrarTransactor{contract: contract}, KeeperRegistrarFilterer: KeeperRegistrarFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistrar{address: address, abi: *parsed, KeeperRegistrarCaller: KeeperRegistrarCaller{contract: contract}, KeeperRegistrarTransactor: KeeperRegistrarTransactor{contract: contract}, KeeperRegistrarFilterer: KeeperRegistrarFilterer{contract: contract}}, nil } type KeeperRegistrar struct { diff --git a/core/gethwrappers/generated/keeper_registrar_wrapper1_2_mock/keeper_registrar_wrapper1_2_mock.go b/core/gethwrappers/generated/keeper_registrar_wrapper1_2_mock/keeper_registrar_wrapper1_2_mock.go index ea4c49f7c88..d83fd9a4314 100644 --- a/core/gethwrappers/generated/keeper_registrar_wrapper1_2_mock/keeper_registrar_wrapper1_2_mock.go +++ b/core/gethwrappers/generated/keeper_registrar_wrapper1_2_mock/keeper_registrar_wrapper1_2_mock.go @@ -52,7 +52,7 @@ func DeployKeeperRegistrarMock(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistrarMock{KeeperRegistrarMockCaller: KeeperRegistrarMockCaller{contract: contract}, KeeperRegistrarMockTransactor: KeeperRegistrarMockTransactor{contract: contract}, KeeperRegistrarMockFilterer: KeeperRegistrarMockFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistrarMock{address: address, abi: *parsed, KeeperRegistrarMockCaller: KeeperRegistrarMockCaller{contract: contract}, KeeperRegistrarMockTransactor: KeeperRegistrarMockTransactor{contract: contract}, KeeperRegistrarMockFilterer: KeeperRegistrarMockFilterer{contract: contract}}, nil } type KeeperRegistrarMock struct { diff --git a/core/gethwrappers/generated/keeper_registrar_wrapper2_0/keeper_registrar_wrapper2_0.go b/core/gethwrappers/generated/keeper_registrar_wrapper2_0/keeper_registrar_wrapper2_0.go index c8a7016df4f..56d78f9f0dd 100644 --- a/core/gethwrappers/generated/keeper_registrar_wrapper2_0/keeper_registrar_wrapper2_0.go +++ b/core/gethwrappers/generated/keeper_registrar_wrapper2_0/keeper_registrar_wrapper2_0.go @@ -63,7 +63,7 @@ func DeployKeeperRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistrar{KeeperRegistrarCaller: KeeperRegistrarCaller{contract: contract}, KeeperRegistrarTransactor: KeeperRegistrarTransactor{contract: contract}, KeeperRegistrarFilterer: KeeperRegistrarFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistrar{address: address, abi: *parsed, KeeperRegistrarCaller: KeeperRegistrarCaller{contract: contract}, KeeperRegistrarTransactor: KeeperRegistrarTransactor{contract: contract}, KeeperRegistrarFilterer: KeeperRegistrarFilterer{contract: contract}}, nil } type KeeperRegistrar struct { diff --git a/core/gethwrappers/generated/keeper_registry_logic1_3/keeper_registry_logic1_3.go b/core/gethwrappers/generated/keeper_registry_logic1_3/keeper_registry_logic1_3.go index 6049536df69..11856a0c8ff 100644 --- a/core/gethwrappers/generated/keeper_registry_logic1_3/keeper_registry_logic1_3.go +++ b/core/gethwrappers/generated/keeper_registry_logic1_3/keeper_registry_logic1_3.go @@ -67,7 +67,7 @@ func DeployKeeperRegistryLogic(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryLogic{KeeperRegistryLogicCaller: KeeperRegistryLogicCaller{contract: contract}, KeeperRegistryLogicTransactor: KeeperRegistryLogicTransactor{contract: contract}, KeeperRegistryLogicFilterer: KeeperRegistryLogicFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryLogic{address: address, abi: *parsed, KeeperRegistryLogicCaller: KeeperRegistryLogicCaller{contract: contract}, KeeperRegistryLogicTransactor: KeeperRegistryLogicTransactor{contract: contract}, KeeperRegistryLogicFilterer: KeeperRegistryLogicFilterer{contract: contract}}, nil } type KeeperRegistryLogic struct { diff --git a/core/gethwrappers/generated/keeper_registry_logic2_0/keeper_registry_logic2_0.go b/core/gethwrappers/generated/keeper_registry_logic2_0/keeper_registry_logic2_0.go index e4e24a0463a..819e7e094b2 100644 --- a/core/gethwrappers/generated/keeper_registry_logic2_0/keeper_registry_logic2_0.go +++ b/core/gethwrappers/generated/keeper_registry_logic2_0/keeper_registry_logic2_0.go @@ -52,7 +52,7 @@ func DeployKeeperRegistryLogic(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryLogic{KeeperRegistryLogicCaller: KeeperRegistryLogicCaller{contract: contract}, KeeperRegistryLogicTransactor: KeeperRegistryLogicTransactor{contract: contract}, KeeperRegistryLogicFilterer: KeeperRegistryLogicFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryLogic{address: address, abi: *parsed, KeeperRegistryLogicCaller: KeeperRegistryLogicCaller{contract: contract}, KeeperRegistryLogicTransactor: KeeperRegistryLogicTransactor{contract: contract}, KeeperRegistryLogicFilterer: KeeperRegistryLogicFilterer{contract: contract}}, nil } type KeeperRegistryLogic struct { diff --git a/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_1/keeper_registry_logic_a_wrapper_2_1.go b/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_1/keeper_registry_logic_a_wrapper_2_1.go index 48c4d75e8b3..69d9adbc686 100644 --- a/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_1/keeper_registry_logic_a_wrapper_2_1.go +++ b/core/gethwrappers/generated/keeper_registry_logic_a_wrapper_2_1/keeper_registry_logic_a_wrapper_2_1.go @@ -52,7 +52,7 @@ func DeployKeeperRegistryLogicA(auth *bind.TransactOpts, backend bind.ContractBa if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryLogicA{KeeperRegistryLogicACaller: KeeperRegistryLogicACaller{contract: contract}, KeeperRegistryLogicATransactor: KeeperRegistryLogicATransactor{contract: contract}, KeeperRegistryLogicAFilterer: KeeperRegistryLogicAFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryLogicA{address: address, abi: *parsed, KeeperRegistryLogicACaller: KeeperRegistryLogicACaller{contract: contract}, KeeperRegistryLogicATransactor: KeeperRegistryLogicATransactor{contract: contract}, KeeperRegistryLogicAFilterer: KeeperRegistryLogicAFilterer{contract: contract}}, nil } type KeeperRegistryLogicA struct { diff --git a/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_1/keeper_registry_logic_b_wrapper_2_1.go b/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_1/keeper_registry_logic_b_wrapper_2_1.go index 15330fb8219..984d9221eb1 100644 --- a/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_1/keeper_registry_logic_b_wrapper_2_1.go +++ b/core/gethwrappers/generated/keeper_registry_logic_b_wrapper_2_1/keeper_registry_logic_b_wrapper_2_1.go @@ -96,7 +96,7 @@ func DeployKeeperRegistryLogicB(auth *bind.TransactOpts, backend bind.ContractBa if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryLogicB{KeeperRegistryLogicBCaller: KeeperRegistryLogicBCaller{contract: contract}, KeeperRegistryLogicBTransactor: KeeperRegistryLogicBTransactor{contract: contract}, KeeperRegistryLogicBFilterer: KeeperRegistryLogicBFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryLogicB{address: address, abi: *parsed, KeeperRegistryLogicBCaller: KeeperRegistryLogicBCaller{contract: contract}, KeeperRegistryLogicBTransactor: KeeperRegistryLogicBTransactor{contract: contract}, KeeperRegistryLogicBFilterer: KeeperRegistryLogicBFilterer{contract: contract}}, nil } type KeeperRegistryLogicB struct { diff --git a/core/gethwrappers/generated/keeper_registry_wrapper1_1/keeper_registry_wrapper1_1.go b/core/gethwrappers/generated/keeper_registry_wrapper1_1/keeper_registry_wrapper1_1.go index 896de7f088b..196b2f36f5d 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper1_1/keeper_registry_wrapper1_1.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper1_1/keeper_registry_wrapper1_1.go @@ -52,7 +52,7 @@ func DeployKeeperRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistry{KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistry{address: address, abi: *parsed, KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil } type KeeperRegistry struct { diff --git a/core/gethwrappers/generated/keeper_registry_wrapper1_1_mock/keeper_registry_wrapper1_1_mock.go b/core/gethwrappers/generated/keeper_registry_wrapper1_1_mock/keeper_registry_wrapper1_1_mock.go index d7bfc132eb5..14457f91efa 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper1_1_mock/keeper_registry_wrapper1_1_mock.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper1_1_mock/keeper_registry_wrapper1_1_mock.go @@ -52,7 +52,7 @@ func DeployKeeperRegistryMock(auth *bind.TransactOpts, backend bind.ContractBack if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistryMock{KeeperRegistryMockCaller: KeeperRegistryMockCaller{contract: contract}, KeeperRegistryMockTransactor: KeeperRegistryMockTransactor{contract: contract}, KeeperRegistryMockFilterer: KeeperRegistryMockFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistryMock{address: address, abi: *parsed, KeeperRegistryMockCaller: KeeperRegistryMockCaller{contract: contract}, KeeperRegistryMockTransactor: KeeperRegistryMockTransactor{contract: contract}, KeeperRegistryMockFilterer: KeeperRegistryMockFilterer{contract: contract}}, nil } type KeeperRegistryMock struct { diff --git a/core/gethwrappers/generated/keeper_registry_wrapper1_2/keeper_registry_wrapper1_2.go b/core/gethwrappers/generated/keeper_registry_wrapper1_2/keeper_registry_wrapper1_2.go index cbfd589abf4..6fa0d55b78d 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper1_2/keeper_registry_wrapper1_2.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper1_2/keeper_registry_wrapper1_2.go @@ -74,7 +74,7 @@ func DeployKeeperRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistry{KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistry{address: address, abi: *parsed, KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil } type KeeperRegistry struct { diff --git a/core/gethwrappers/generated/keeper_registry_wrapper1_3/keeper_registry_wrapper1_3.go b/core/gethwrappers/generated/keeper_registry_wrapper1_3/keeper_registry_wrapper1_3.go index 73ff800e12b..bea6e458a00 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper1_3/keeper_registry_wrapper1_3.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper1_3/keeper_registry_wrapper1_3.go @@ -74,7 +74,7 @@ func DeployKeeperRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistry{KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistry{address: address, abi: *parsed, KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil } type KeeperRegistry struct { diff --git a/core/gethwrappers/generated/keeper_registry_wrapper2_0/keeper_registry_wrapper2_0.go b/core/gethwrappers/generated/keeper_registry_wrapper2_0/keeper_registry_wrapper2_0.go index 2aaf8085aa4..bd000995e33 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper2_0/keeper_registry_wrapper2_0.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper2_0/keeper_registry_wrapper2_0.go @@ -94,7 +94,7 @@ func DeployKeeperRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistry{KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistry{address: address, abi: *parsed, KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil } type KeeperRegistry struct { diff --git a/core/gethwrappers/generated/keeper_registry_wrapper_2_1/keeper_registry_wrapper_2_1.go b/core/gethwrappers/generated/keeper_registry_wrapper_2_1/keeper_registry_wrapper_2_1.go index 1db34ca3953..fc9d120c5e3 100644 --- a/core/gethwrappers/generated/keeper_registry_wrapper_2_1/keeper_registry_wrapper_2_1.go +++ b/core/gethwrappers/generated/keeper_registry_wrapper_2_1/keeper_registry_wrapper_2_1.go @@ -70,7 +70,7 @@ func DeployKeeperRegistry(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeeperRegistry{KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil + return address, tx, &KeeperRegistry{address: address, abi: *parsed, KeeperRegistryCaller: KeeperRegistryCaller{contract: contract}, KeeperRegistryTransactor: KeeperRegistryTransactor{contract: contract}, KeeperRegistryFilterer: KeeperRegistryFilterer{contract: contract}}, nil } type KeeperRegistry struct { diff --git a/core/gethwrappers/generated/keepers_vrf_consumer/keepers_vrf_consumer.go b/core/gethwrappers/generated/keepers_vrf_consumer/keepers_vrf_consumer.go index 99e0fece52e..57e0aced8a2 100644 --- a/core/gethwrappers/generated/keepers_vrf_consumer/keepers_vrf_consumer.go +++ b/core/gethwrappers/generated/keepers_vrf_consumer/keepers_vrf_consumer.go @@ -50,7 +50,7 @@ func DeployKeepersVRFConsumer(auth *bind.TransactOpts, backend bind.ContractBack if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &KeepersVRFConsumer{KeepersVRFConsumerCaller: KeepersVRFConsumerCaller{contract: contract}, KeepersVRFConsumerTransactor: KeepersVRFConsumerTransactor{contract: contract}, KeepersVRFConsumerFilterer: KeepersVRFConsumerFilterer{contract: contract}}, nil + return address, tx, &KeepersVRFConsumer{address: address, abi: *parsed, KeepersVRFConsumerCaller: KeepersVRFConsumerCaller{contract: contract}, KeepersVRFConsumerTransactor: KeepersVRFConsumerTransactor{contract: contract}, KeepersVRFConsumerFilterer: KeepersVRFConsumerFilterer{contract: contract}}, nil } type KeepersVRFConsumer struct { diff --git a/core/gethwrappers/generated/link_token_interface/link_token_interface.go b/core/gethwrappers/generated/link_token_interface/link_token_interface.go index d0a6b92c1e4..53fbb56dce7 100644 --- a/core/gethwrappers/generated/link_token_interface/link_token_interface.go +++ b/core/gethwrappers/generated/link_token_interface/link_token_interface.go @@ -52,7 +52,7 @@ func DeployLinkToken(auth *bind.TransactOpts, backend bind.ContractBackend) (com if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &LinkToken{LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil + return address, tx, &LinkToken{address: address, abi: *parsed, LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil } type LinkToken struct { diff --git a/core/gethwrappers/generated/log_emitter/log_emitter.go b/core/gethwrappers/generated/log_emitter/log_emitter.go index 4f0189dfdea..3cb11da5125 100644 --- a/core/gethwrappers/generated/log_emitter/log_emitter.go +++ b/core/gethwrappers/generated/log_emitter/log_emitter.go @@ -52,7 +52,7 @@ func DeployLogEmitter(auth *bind.TransactOpts, backend bind.ContractBackend) (co if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &LogEmitter{LogEmitterCaller: LogEmitterCaller{contract: contract}, LogEmitterTransactor: LogEmitterTransactor{contract: contract}, LogEmitterFilterer: LogEmitterFilterer{contract: contract}}, nil + return address, tx, &LogEmitter{address: address, abi: *parsed, LogEmitterCaller: LogEmitterCaller{contract: contract}, LogEmitterTransactor: LogEmitterTransactor{contract: contract}, LogEmitterFilterer: LogEmitterFilterer{contract: contract}}, nil } type LogEmitter struct { diff --git a/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper/log_triggered_streams_lookup_wrapper.go b/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper/log_triggered_streams_lookup_wrapper.go index 18b61baed7f..ccd5aea2c37 100644 --- a/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper/log_triggered_streams_lookup_wrapper.go +++ b/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper/log_triggered_streams_lookup_wrapper.go @@ -63,7 +63,7 @@ func DeployLogTriggeredStreamsLookup(auth *bind.TransactOpts, backend bind.Contr if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &LogTriggeredStreamsLookup{LogTriggeredStreamsLookupCaller: LogTriggeredStreamsLookupCaller{contract: contract}, LogTriggeredStreamsLookupTransactor: LogTriggeredStreamsLookupTransactor{contract: contract}, LogTriggeredStreamsLookupFilterer: LogTriggeredStreamsLookupFilterer{contract: contract}}, nil + return address, tx, &LogTriggeredStreamsLookup{address: address, abi: *parsed, LogTriggeredStreamsLookupCaller: LogTriggeredStreamsLookupCaller{contract: contract}, LogTriggeredStreamsLookupTransactor: LogTriggeredStreamsLookupTransactor{contract: contract}, LogTriggeredStreamsLookupFilterer: LogTriggeredStreamsLookupFilterer{contract: contract}}, nil } type LogTriggeredStreamsLookup struct { diff --git a/core/gethwrappers/generated/log_upkeep_counter_wrapper/log_upkeep_counter_wrapper.go b/core/gethwrappers/generated/log_upkeep_counter_wrapper/log_upkeep_counter_wrapper.go index fd55349b126..51b7b753cc7 100644 --- a/core/gethwrappers/generated/log_upkeep_counter_wrapper/log_upkeep_counter_wrapper.go +++ b/core/gethwrappers/generated/log_upkeep_counter_wrapper/log_upkeep_counter_wrapper.go @@ -63,7 +63,7 @@ func DeployLogUpkeepCounter(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &LogUpkeepCounter{LogUpkeepCounterCaller: LogUpkeepCounterCaller{contract: contract}, LogUpkeepCounterTransactor: LogUpkeepCounterTransactor{contract: contract}, LogUpkeepCounterFilterer: LogUpkeepCounterFilterer{contract: contract}}, nil + return address, tx, &LogUpkeepCounter{address: address, abi: *parsed, LogUpkeepCounterCaller: LogUpkeepCounterCaller{contract: contract}, LogUpkeepCounterTransactor: LogUpkeepCounterTransactor{contract: contract}, LogUpkeepCounterFilterer: LogUpkeepCounterFilterer{contract: contract}}, nil } type LogUpkeepCounter struct { diff --git a/core/gethwrappers/generated/mock_aggregator_proxy/mock_aggregator_proxy.go b/core/gethwrappers/generated/mock_aggregator_proxy/mock_aggregator_proxy.go index 1694ebd11f1..a9c972e8be9 100644 --- a/core/gethwrappers/generated/mock_aggregator_proxy/mock_aggregator_proxy.go +++ b/core/gethwrappers/generated/mock_aggregator_proxy/mock_aggregator_proxy.go @@ -50,7 +50,7 @@ func DeployMockAggregatorProxy(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &MockAggregatorProxy{MockAggregatorProxyCaller: MockAggregatorProxyCaller{contract: contract}, MockAggregatorProxyTransactor: MockAggregatorProxyTransactor{contract: contract}, MockAggregatorProxyFilterer: MockAggregatorProxyFilterer{contract: contract}}, nil + return address, tx, &MockAggregatorProxy{address: address, abi: *parsed, MockAggregatorProxyCaller: MockAggregatorProxyCaller{contract: contract}, MockAggregatorProxyTransactor: MockAggregatorProxyTransactor{contract: contract}, MockAggregatorProxyFilterer: MockAggregatorProxyFilterer{contract: contract}}, nil } type MockAggregatorProxy struct { diff --git a/core/gethwrappers/generated/mock_ethlink_aggregator_wrapper/mock_ethlink_aggregator_wrapper.go b/core/gethwrappers/generated/mock_ethlink_aggregator_wrapper/mock_ethlink_aggregator_wrapper.go index bcf34a0c477..bc92e3f32b9 100644 --- a/core/gethwrappers/generated/mock_ethlink_aggregator_wrapper/mock_ethlink_aggregator_wrapper.go +++ b/core/gethwrappers/generated/mock_ethlink_aggregator_wrapper/mock_ethlink_aggregator_wrapper.go @@ -50,7 +50,7 @@ func DeployMockETHLINKAggregator(auth *bind.TransactOpts, backend bind.ContractB if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &MockETHLINKAggregator{MockETHLINKAggregatorCaller: MockETHLINKAggregatorCaller{contract: contract}, MockETHLINKAggregatorTransactor: MockETHLINKAggregatorTransactor{contract: contract}, MockETHLINKAggregatorFilterer: MockETHLINKAggregatorFilterer{contract: contract}}, nil + return address, tx, &MockETHLINKAggregator{address: address, abi: *parsed, MockETHLINKAggregatorCaller: MockETHLINKAggregatorCaller{contract: contract}, MockETHLINKAggregatorTransactor: MockETHLINKAggregatorTransactor{contract: contract}, MockETHLINKAggregatorFilterer: MockETHLINKAggregatorFilterer{contract: contract}}, nil } type MockETHLINKAggregator struct { diff --git a/core/gethwrappers/generated/mock_gas_aggregator_wrapper/mock_gas_aggregator_wrapper.go b/core/gethwrappers/generated/mock_gas_aggregator_wrapper/mock_gas_aggregator_wrapper.go index edc0adc5b70..148417f3fc4 100644 --- a/core/gethwrappers/generated/mock_gas_aggregator_wrapper/mock_gas_aggregator_wrapper.go +++ b/core/gethwrappers/generated/mock_gas_aggregator_wrapper/mock_gas_aggregator_wrapper.go @@ -50,7 +50,7 @@ func DeployMockGASAggregator(auth *bind.TransactOpts, backend bind.ContractBacke if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &MockGASAggregator{MockGASAggregatorCaller: MockGASAggregatorCaller{contract: contract}, MockGASAggregatorTransactor: MockGASAggregatorTransactor{contract: contract}, MockGASAggregatorFilterer: MockGASAggregatorFilterer{contract: contract}}, nil + return address, tx, &MockGASAggregator{address: address, abi: *parsed, MockGASAggregatorCaller: MockGASAggregatorCaller{contract: contract}, MockGASAggregatorTransactor: MockGASAggregatorTransactor{contract: contract}, MockGASAggregatorFilterer: MockGASAggregatorFilterer{contract: contract}}, nil } type MockGASAggregator struct { diff --git a/core/gethwrappers/generated/multiwordconsumer_wrapper/multiwordconsumer_wrapper.go b/core/gethwrappers/generated/multiwordconsumer_wrapper/multiwordconsumer_wrapper.go index c939418e575..9a1fc55b6c2 100644 --- a/core/gethwrappers/generated/multiwordconsumer_wrapper/multiwordconsumer_wrapper.go +++ b/core/gethwrappers/generated/multiwordconsumer_wrapper/multiwordconsumer_wrapper.go @@ -52,7 +52,7 @@ func DeployMultiWordConsumer(auth *bind.TransactOpts, backend bind.ContractBacke if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &MultiWordConsumer{MultiWordConsumerCaller: MultiWordConsumerCaller{contract: contract}, MultiWordConsumerTransactor: MultiWordConsumerTransactor{contract: contract}, MultiWordConsumerFilterer: MultiWordConsumerFilterer{contract: contract}}, nil + return address, tx, &MultiWordConsumer{address: address, abi: *parsed, MultiWordConsumerCaller: MultiWordConsumerCaller{contract: contract}, MultiWordConsumerTransactor: MultiWordConsumerTransactor{contract: contract}, MultiWordConsumerFilterer: MultiWordConsumerFilterer{contract: contract}}, nil } type MultiWordConsumer struct { diff --git a/core/gethwrappers/generated/operator_factory/operator_factory.go b/core/gethwrappers/generated/operator_factory/operator_factory.go index b062f6b01a3..c9a6c57ce21 100644 --- a/core/gethwrappers/generated/operator_factory/operator_factory.go +++ b/core/gethwrappers/generated/operator_factory/operator_factory.go @@ -52,7 +52,7 @@ func DeployOperatorFactory(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &OperatorFactory{OperatorFactoryCaller: OperatorFactoryCaller{contract: contract}, OperatorFactoryTransactor: OperatorFactoryTransactor{contract: contract}, OperatorFactoryFilterer: OperatorFactoryFilterer{contract: contract}}, nil + return address, tx, &OperatorFactory{address: address, abi: *parsed, OperatorFactoryCaller: OperatorFactoryCaller{contract: contract}, OperatorFactoryTransactor: OperatorFactoryTransactor{contract: contract}, OperatorFactoryFilterer: OperatorFactoryFilterer{contract: contract}}, nil } type OperatorFactory struct { diff --git a/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go b/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go index 69541858f31..4b7f6347639 100644 --- a/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go +++ b/core/gethwrappers/generated/operator_wrapper/operator_wrapper.go @@ -52,7 +52,7 @@ func DeployOperator(auth *bind.TransactOpts, backend bind.ContractBackend, link if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Operator{OperatorCaller: OperatorCaller{contract: contract}, OperatorTransactor: OperatorTransactor{contract: contract}, OperatorFilterer: OperatorFilterer{contract: contract}}, nil + return address, tx, &Operator{address: address, abi: *parsed, OperatorCaller: OperatorCaller{contract: contract}, OperatorTransactor: OperatorTransactor{contract: contract}, OperatorFilterer: OperatorFilterer{contract: contract}}, nil } type Operator struct { diff --git a/core/gethwrappers/generated/oracle_wrapper/oracle_wrapper.go b/core/gethwrappers/generated/oracle_wrapper/oracle_wrapper.go index 98c7b373382..e22e6cd3034 100644 --- a/core/gethwrappers/generated/oracle_wrapper/oracle_wrapper.go +++ b/core/gethwrappers/generated/oracle_wrapper/oracle_wrapper.go @@ -52,7 +52,7 @@ func DeployOracle(auth *bind.TransactOpts, backend bind.ContractBackend, _link c if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Oracle{OracleCaller: OracleCaller{contract: contract}, OracleTransactor: OracleTransactor{contract: contract}, OracleFilterer: OracleFilterer{contract: contract}}, nil + return address, tx, &Oracle{address: address, abi: *parsed, OracleCaller: OracleCaller{contract: contract}, OracleTransactor: OracleTransactor{contract: contract}, OracleFilterer: OracleFilterer{contract: contract}}, nil } type Oracle struct { diff --git a/core/gethwrappers/generated/perform_data_checker_wrapper/perform_data_checker_wrapper.go b/core/gethwrappers/generated/perform_data_checker_wrapper/perform_data_checker_wrapper.go index aa639deb9c0..a678ad0a8dd 100644 --- a/core/gethwrappers/generated/perform_data_checker_wrapper/perform_data_checker_wrapper.go +++ b/core/gethwrappers/generated/perform_data_checker_wrapper/perform_data_checker_wrapper.go @@ -50,7 +50,7 @@ func DeployPerformDataChecker(auth *bind.TransactOpts, backend bind.ContractBack if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &PerformDataChecker{PerformDataCheckerCaller: PerformDataCheckerCaller{contract: contract}, PerformDataCheckerTransactor: PerformDataCheckerTransactor{contract: contract}, PerformDataCheckerFilterer: PerformDataCheckerFilterer{contract: contract}}, nil + return address, tx, &PerformDataChecker{address: address, abi: *parsed, PerformDataCheckerCaller: PerformDataCheckerCaller{contract: contract}, PerformDataCheckerTransactor: PerformDataCheckerTransactor{contract: contract}, PerformDataCheckerFilterer: PerformDataCheckerFilterer{contract: contract}}, nil } type PerformDataChecker struct { diff --git a/core/gethwrappers/generated/solidity_vrf_consumer_interface/solidity_vrf_consumer_interface.go b/core/gethwrappers/generated/solidity_vrf_consumer_interface/solidity_vrf_consumer_interface.go index de123d5ec7a..8c815be3a52 100644 --- a/core/gethwrappers/generated/solidity_vrf_consumer_interface/solidity_vrf_consumer_interface.go +++ b/core/gethwrappers/generated/solidity_vrf_consumer_interface/solidity_vrf_consumer_interface.go @@ -50,7 +50,7 @@ func DeployVRFConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, _v if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFConsumer{VRFConsumerCaller: VRFConsumerCaller{contract: contract}, VRFConsumerTransactor: VRFConsumerTransactor{contract: contract}, VRFConsumerFilterer: VRFConsumerFilterer{contract: contract}}, nil + return address, tx, &VRFConsumer{address: address, abi: *parsed, VRFConsumerCaller: VRFConsumerCaller{contract: contract}, VRFConsumerTransactor: VRFConsumerTransactor{contract: contract}, VRFConsumerFilterer: VRFConsumerFilterer{contract: contract}}, nil } type VRFConsumer struct { diff --git a/core/gethwrappers/generated/solidity_vrf_consumer_interface_v08/solidity_vrf_consumer_interface_v08.go b/core/gethwrappers/generated/solidity_vrf_consumer_interface_v08/solidity_vrf_consumer_interface_v08.go index e122d3e07fd..b3267a17ce2 100644 --- a/core/gethwrappers/generated/solidity_vrf_consumer_interface_v08/solidity_vrf_consumer_interface_v08.go +++ b/core/gethwrappers/generated/solidity_vrf_consumer_interface_v08/solidity_vrf_consumer_interface_v08.go @@ -50,7 +50,7 @@ func DeployVRFConsumer(auth *bind.TransactOpts, backend bind.ContractBackend, vr if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFConsumer{VRFConsumerCaller: VRFConsumerCaller{contract: contract}, VRFConsumerTransactor: VRFConsumerTransactor{contract: contract}, VRFConsumerFilterer: VRFConsumerFilterer{contract: contract}}, nil + return address, tx, &VRFConsumer{address: address, abi: *parsed, VRFConsumerCaller: VRFConsumerCaller{contract: contract}, VRFConsumerTransactor: VRFConsumerTransactor{contract: contract}, VRFConsumerFilterer: VRFConsumerFilterer{contract: contract}}, nil } type VRFConsumer struct { diff --git a/core/gethwrappers/generated/solidity_vrf_coordinator_interface/solidity_vrf_coordinator_interface.go b/core/gethwrappers/generated/solidity_vrf_coordinator_interface/solidity_vrf_coordinator_interface.go index 36c715a2c08..7ac2210dc7d 100644 --- a/core/gethwrappers/generated/solidity_vrf_coordinator_interface/solidity_vrf_coordinator_interface.go +++ b/core/gethwrappers/generated/solidity_vrf_coordinator_interface/solidity_vrf_coordinator_interface.go @@ -52,7 +52,7 @@ func DeployVRFCoordinator(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFCoordinator{VRFCoordinatorCaller: VRFCoordinatorCaller{contract: contract}, VRFCoordinatorTransactor: VRFCoordinatorTransactor{contract: contract}, VRFCoordinatorFilterer: VRFCoordinatorFilterer{contract: contract}}, nil + return address, tx, &VRFCoordinator{address: address, abi: *parsed, VRFCoordinatorCaller: VRFCoordinatorCaller{contract: contract}, VRFCoordinatorTransactor: VRFCoordinatorTransactor{contract: contract}, VRFCoordinatorFilterer: VRFCoordinatorFilterer{contract: contract}}, nil } type VRFCoordinator struct { diff --git a/core/gethwrappers/generated/solidity_vrf_request_id/solidity_vrf_request_id.go b/core/gethwrappers/generated/solidity_vrf_request_id/solidity_vrf_request_id.go index fd35e245e41..b0d101a96c8 100644 --- a/core/gethwrappers/generated/solidity_vrf_request_id/solidity_vrf_request_id.go +++ b/core/gethwrappers/generated/solidity_vrf_request_id/solidity_vrf_request_id.go @@ -50,7 +50,7 @@ func DeployVRFRequestIDBaseTestHelper(auth *bind.TransactOpts, backend bind.Cont if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFRequestIDBaseTestHelper{VRFRequestIDBaseTestHelperCaller: VRFRequestIDBaseTestHelperCaller{contract: contract}, VRFRequestIDBaseTestHelperTransactor: VRFRequestIDBaseTestHelperTransactor{contract: contract}, VRFRequestIDBaseTestHelperFilterer: VRFRequestIDBaseTestHelperFilterer{contract: contract}}, nil + return address, tx, &VRFRequestIDBaseTestHelper{address: address, abi: *parsed, VRFRequestIDBaseTestHelperCaller: VRFRequestIDBaseTestHelperCaller{contract: contract}, VRFRequestIDBaseTestHelperTransactor: VRFRequestIDBaseTestHelperTransactor{contract: contract}, VRFRequestIDBaseTestHelperFilterer: VRFRequestIDBaseTestHelperFilterer{contract: contract}}, nil } type VRFRequestIDBaseTestHelper struct { diff --git a/core/gethwrappers/generated/solidity_vrf_request_id_v08/solidity_vrf_request_id_v08.go b/core/gethwrappers/generated/solidity_vrf_request_id_v08/solidity_vrf_request_id_v08.go index 181226bfa30..5d931e3d90b 100644 --- a/core/gethwrappers/generated/solidity_vrf_request_id_v08/solidity_vrf_request_id_v08.go +++ b/core/gethwrappers/generated/solidity_vrf_request_id_v08/solidity_vrf_request_id_v08.go @@ -50,7 +50,7 @@ func DeployVRFRequestIDBaseTestHelper(auth *bind.TransactOpts, backend bind.Cont if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFRequestIDBaseTestHelper{VRFRequestIDBaseTestHelperCaller: VRFRequestIDBaseTestHelperCaller{contract: contract}, VRFRequestIDBaseTestHelperTransactor: VRFRequestIDBaseTestHelperTransactor{contract: contract}, VRFRequestIDBaseTestHelperFilterer: VRFRequestIDBaseTestHelperFilterer{contract: contract}}, nil + return address, tx, &VRFRequestIDBaseTestHelper{address: address, abi: *parsed, VRFRequestIDBaseTestHelperCaller: VRFRequestIDBaseTestHelperCaller{contract: contract}, VRFRequestIDBaseTestHelperTransactor: VRFRequestIDBaseTestHelperTransactor{contract: contract}, VRFRequestIDBaseTestHelperFilterer: VRFRequestIDBaseTestHelperFilterer{contract: contract}}, nil } type VRFRequestIDBaseTestHelper struct { diff --git a/core/gethwrappers/generated/solidity_vrf_v08_verifier_wrapper/solidity_vrf_v08_verifier_wrapper.go b/core/gethwrappers/generated/solidity_vrf_v08_verifier_wrapper/solidity_vrf_v08_verifier_wrapper.go index 045c30e9359..6eaf2b996fa 100644 --- a/core/gethwrappers/generated/solidity_vrf_v08_verifier_wrapper/solidity_vrf_v08_verifier_wrapper.go +++ b/core/gethwrappers/generated/solidity_vrf_v08_verifier_wrapper/solidity_vrf_v08_verifier_wrapper.go @@ -62,7 +62,7 @@ func DeployVRFV08TestHelper(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV08TestHelper{VRFV08TestHelperCaller: VRFV08TestHelperCaller{contract: contract}, VRFV08TestHelperTransactor: VRFV08TestHelperTransactor{contract: contract}, VRFV08TestHelperFilterer: VRFV08TestHelperFilterer{contract: contract}}, nil + return address, tx, &VRFV08TestHelper{address: address, abi: *parsed, VRFV08TestHelperCaller: VRFV08TestHelperCaller{contract: contract}, VRFV08TestHelperTransactor: VRFV08TestHelperTransactor{contract: contract}, VRFV08TestHelperFilterer: VRFV08TestHelperFilterer{contract: contract}}, nil } type VRFV08TestHelper struct { diff --git a/core/gethwrappers/generated/solidity_vrf_verifier_wrapper/solidity_vrf_verifier_wrapper.go b/core/gethwrappers/generated/solidity_vrf_verifier_wrapper/solidity_vrf_verifier_wrapper.go index a00768ee1bb..b46cc3de083 100644 --- a/core/gethwrappers/generated/solidity_vrf_verifier_wrapper/solidity_vrf_verifier_wrapper.go +++ b/core/gethwrappers/generated/solidity_vrf_verifier_wrapper/solidity_vrf_verifier_wrapper.go @@ -50,7 +50,7 @@ func DeployVRFTestHelper(auth *bind.TransactOpts, backend bind.ContractBackend) if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFTestHelper{VRFTestHelperCaller: VRFTestHelperCaller{contract: contract}, VRFTestHelperTransactor: VRFTestHelperTransactor{contract: contract}, VRFTestHelperFilterer: VRFTestHelperFilterer{contract: contract}}, nil + return address, tx, &VRFTestHelper{address: address, abi: *parsed, VRFTestHelperCaller: VRFTestHelperCaller{contract: contract}, VRFTestHelperTransactor: VRFTestHelperTransactor{contract: contract}, VRFTestHelperFilterer: VRFTestHelperFilterer{contract: contract}}, nil } type VRFTestHelper struct { diff --git a/core/gethwrappers/generated/solidity_vrf_wrapper/solidity_vrf_wrapper.go b/core/gethwrappers/generated/solidity_vrf_wrapper/solidity_vrf_wrapper.go index fb16ff3a8bc..82199d45389 100644 --- a/core/gethwrappers/generated/solidity_vrf_wrapper/solidity_vrf_wrapper.go +++ b/core/gethwrappers/generated/solidity_vrf_wrapper/solidity_vrf_wrapper.go @@ -50,7 +50,7 @@ func DeployVRF(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Ad if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRF{VRFCaller: VRFCaller{contract: contract}, VRFTransactor: VRFTransactor{contract: contract}, VRFFilterer: VRFFilterer{contract: contract}}, nil + return address, tx, &VRF{address: address, abi: *parsed, VRFCaller: VRFCaller{contract: contract}, VRFTransactor: VRFTransactor{contract: contract}, VRFFilterer: VRFFilterer{contract: contract}}, nil } type VRF struct { diff --git a/core/gethwrappers/generated/streams_lookup_upkeep_wrapper/streams_lookup_upkeep_wrapper.go b/core/gethwrappers/generated/streams_lookup_upkeep_wrapper/streams_lookup_upkeep_wrapper.go index 3332ac8215e..d54ee36f8ff 100644 --- a/core/gethwrappers/generated/streams_lookup_upkeep_wrapper/streams_lookup_upkeep_wrapper.go +++ b/core/gethwrappers/generated/streams_lookup_upkeep_wrapper/streams_lookup_upkeep_wrapper.go @@ -52,7 +52,7 @@ func DeployStreamsLookupUpkeep(auth *bind.TransactOpts, backend bind.ContractBac if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &StreamsLookupUpkeep{StreamsLookupUpkeepCaller: StreamsLookupUpkeepCaller{contract: contract}, StreamsLookupUpkeepTransactor: StreamsLookupUpkeepTransactor{contract: contract}, StreamsLookupUpkeepFilterer: StreamsLookupUpkeepFilterer{contract: contract}}, nil + return address, tx, &StreamsLookupUpkeep{address: address, abi: *parsed, StreamsLookupUpkeepCaller: StreamsLookupUpkeepCaller{contract: contract}, StreamsLookupUpkeepTransactor: StreamsLookupUpkeepTransactor{contract: contract}, StreamsLookupUpkeepFilterer: StreamsLookupUpkeepFilterer{contract: contract}}, nil } type StreamsLookupUpkeep struct { diff --git a/core/gethwrappers/generated/test_api_consumer_wrapper/test_api_consumer_wrapper.go b/core/gethwrappers/generated/test_api_consumer_wrapper/test_api_consumer_wrapper.go index 8503d6f75e9..71b26af95df 100644 --- a/core/gethwrappers/generated/test_api_consumer_wrapper/test_api_consumer_wrapper.go +++ b/core/gethwrappers/generated/test_api_consumer_wrapper/test_api_consumer_wrapper.go @@ -52,7 +52,7 @@ func DeployTestAPIConsumer(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &TestAPIConsumer{TestAPIConsumerCaller: TestAPIConsumerCaller{contract: contract}, TestAPIConsumerTransactor: TestAPIConsumerTransactor{contract: contract}, TestAPIConsumerFilterer: TestAPIConsumerFilterer{contract: contract}}, nil + return address, tx, &TestAPIConsumer{address: address, abi: *parsed, TestAPIConsumerCaller: TestAPIConsumerCaller{contract: contract}, TestAPIConsumerTransactor: TestAPIConsumerTransactor{contract: contract}, TestAPIConsumerFilterer: TestAPIConsumerFilterer{contract: contract}}, nil } type TestAPIConsumer struct { diff --git a/core/gethwrappers/generated/trusted_blockhash_store/trusted_blockhash_store.go b/core/gethwrappers/generated/trusted_blockhash_store/trusted_blockhash_store.go index 43ae1a3f5b4..c571b0a7002 100644 --- a/core/gethwrappers/generated/trusted_blockhash_store/trusted_blockhash_store.go +++ b/core/gethwrappers/generated/trusted_blockhash_store/trusted_blockhash_store.go @@ -52,7 +52,7 @@ func DeployTrustedBlockhashStore(auth *bind.TransactOpts, backend bind.ContractB if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &TrustedBlockhashStore{TrustedBlockhashStoreCaller: TrustedBlockhashStoreCaller{contract: contract}, TrustedBlockhashStoreTransactor: TrustedBlockhashStoreTransactor{contract: contract}, TrustedBlockhashStoreFilterer: TrustedBlockhashStoreFilterer{contract: contract}}, nil + return address, tx, &TrustedBlockhashStore{address: address, abi: *parsed, TrustedBlockhashStoreCaller: TrustedBlockhashStoreCaller{contract: contract}, TrustedBlockhashStoreTransactor: TrustedBlockhashStoreTransactor{contract: contract}, TrustedBlockhashStoreFilterer: TrustedBlockhashStoreFilterer{contract: contract}}, nil } type TrustedBlockhashStore struct { diff --git a/core/gethwrappers/generated/upkeep_counter_wrapper/upkeep_counter_wrapper.go b/core/gethwrappers/generated/upkeep_counter_wrapper/upkeep_counter_wrapper.go index 4f9fe79d77c..13db591730e 100644 --- a/core/gethwrappers/generated/upkeep_counter_wrapper/upkeep_counter_wrapper.go +++ b/core/gethwrappers/generated/upkeep_counter_wrapper/upkeep_counter_wrapper.go @@ -52,7 +52,7 @@ func DeployUpkeepCounter(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &UpkeepCounter{UpkeepCounterCaller: UpkeepCounterCaller{contract: contract}, UpkeepCounterTransactor: UpkeepCounterTransactor{contract: contract}, UpkeepCounterFilterer: UpkeepCounterFilterer{contract: contract}}, nil + return address, tx, &UpkeepCounter{address: address, abi: *parsed, UpkeepCounterCaller: UpkeepCounterCaller{contract: contract}, UpkeepCounterTransactor: UpkeepCounterTransactor{contract: contract}, UpkeepCounterFilterer: UpkeepCounterFilterer{contract: contract}}, nil } type UpkeepCounter struct { diff --git a/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper/upkeep_perform_counter_restrictive_wrapper.go b/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper/upkeep_perform_counter_restrictive_wrapper.go index e6770fd074f..e004ab61434 100644 --- a/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper/upkeep_perform_counter_restrictive_wrapper.go +++ b/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper/upkeep_perform_counter_restrictive_wrapper.go @@ -52,7 +52,7 @@ func DeployUpkeepPerformCounterRestrictive(auth *bind.TransactOpts, backend bind if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &UpkeepPerformCounterRestrictive{UpkeepPerformCounterRestrictiveCaller: UpkeepPerformCounterRestrictiveCaller{contract: contract}, UpkeepPerformCounterRestrictiveTransactor: UpkeepPerformCounterRestrictiveTransactor{contract: contract}, UpkeepPerformCounterRestrictiveFilterer: UpkeepPerformCounterRestrictiveFilterer{contract: contract}}, nil + return address, tx, &UpkeepPerformCounterRestrictive{address: address, abi: *parsed, UpkeepPerformCounterRestrictiveCaller: UpkeepPerformCounterRestrictiveCaller{contract: contract}, UpkeepPerformCounterRestrictiveTransactor: UpkeepPerformCounterRestrictiveTransactor{contract: contract}, UpkeepPerformCounterRestrictiveFilterer: UpkeepPerformCounterRestrictiveFilterer{contract: contract}}, nil } type UpkeepPerformCounterRestrictive struct { diff --git a/core/gethwrappers/generated/upkeep_transcoder/upkeep_transcoder.go b/core/gethwrappers/generated/upkeep_transcoder/upkeep_transcoder.go index a439dddd40f..53d557d79af 100644 --- a/core/gethwrappers/generated/upkeep_transcoder/upkeep_transcoder.go +++ b/core/gethwrappers/generated/upkeep_transcoder/upkeep_transcoder.go @@ -50,7 +50,7 @@ func DeployUpkeepTranscoder(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &UpkeepTranscoder{UpkeepTranscoderCaller: UpkeepTranscoderCaller{contract: contract}, UpkeepTranscoderTransactor: UpkeepTranscoderTransactor{contract: contract}, UpkeepTranscoderFilterer: UpkeepTranscoderFilterer{contract: contract}}, nil + return address, tx, &UpkeepTranscoder{address: address, abi: *parsed, UpkeepTranscoderCaller: UpkeepTranscoderCaller{contract: contract}, UpkeepTranscoderTransactor: UpkeepTranscoderTransactor{contract: contract}, UpkeepTranscoderFilterer: UpkeepTranscoderFilterer{contract: contract}}, nil } type UpkeepTranscoder struct { diff --git a/core/gethwrappers/generated/verifiable_load_log_trigger_upkeep_wrapper/verifiable_load_log_trigger_upkeep_wrapper.go b/core/gethwrappers/generated/verifiable_load_log_trigger_upkeep_wrapper/verifiable_load_log_trigger_upkeep_wrapper.go index 52e241965f8..9648c4bd719 100644 --- a/core/gethwrappers/generated/verifiable_load_log_trigger_upkeep_wrapper/verifiable_load_log_trigger_upkeep_wrapper.go +++ b/core/gethwrappers/generated/verifiable_load_log_trigger_upkeep_wrapper/verifiable_load_log_trigger_upkeep_wrapper.go @@ -76,7 +76,7 @@ func DeployVerifiableLoadLogTriggerUpkeep(auth *bind.TransactOpts, backend bind. if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VerifiableLoadLogTriggerUpkeep{VerifiableLoadLogTriggerUpkeepCaller: VerifiableLoadLogTriggerUpkeepCaller{contract: contract}, VerifiableLoadLogTriggerUpkeepTransactor: VerifiableLoadLogTriggerUpkeepTransactor{contract: contract}, VerifiableLoadLogTriggerUpkeepFilterer: VerifiableLoadLogTriggerUpkeepFilterer{contract: contract}}, nil + return address, tx, &VerifiableLoadLogTriggerUpkeep{address: address, abi: *parsed, VerifiableLoadLogTriggerUpkeepCaller: VerifiableLoadLogTriggerUpkeepCaller{contract: contract}, VerifiableLoadLogTriggerUpkeepTransactor: VerifiableLoadLogTriggerUpkeepTransactor{contract: contract}, VerifiableLoadLogTriggerUpkeepFilterer: VerifiableLoadLogTriggerUpkeepFilterer{contract: contract}}, nil } type VerifiableLoadLogTriggerUpkeep struct { diff --git a/core/gethwrappers/generated/verifiable_load_streams_lookup_upkeep_wrapper/verifiable_load_streams_lookup_upkeep_wrapper.go b/core/gethwrappers/generated/verifiable_load_streams_lookup_upkeep_wrapper/verifiable_load_streams_lookup_upkeep_wrapper.go index e8a10d85dd1..fc39ffb366b 100644 --- a/core/gethwrappers/generated/verifiable_load_streams_lookup_upkeep_wrapper/verifiable_load_streams_lookup_upkeep_wrapper.go +++ b/core/gethwrappers/generated/verifiable_load_streams_lookup_upkeep_wrapper/verifiable_load_streams_lookup_upkeep_wrapper.go @@ -65,7 +65,7 @@ func DeployVerifiableLoadStreamsLookupUpkeep(auth *bind.TransactOpts, backend bi if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VerifiableLoadStreamsLookupUpkeep{VerifiableLoadStreamsLookupUpkeepCaller: VerifiableLoadStreamsLookupUpkeepCaller{contract: contract}, VerifiableLoadStreamsLookupUpkeepTransactor: VerifiableLoadStreamsLookupUpkeepTransactor{contract: contract}, VerifiableLoadStreamsLookupUpkeepFilterer: VerifiableLoadStreamsLookupUpkeepFilterer{contract: contract}}, nil + return address, tx, &VerifiableLoadStreamsLookupUpkeep{address: address, abi: *parsed, VerifiableLoadStreamsLookupUpkeepCaller: VerifiableLoadStreamsLookupUpkeepCaller{contract: contract}, VerifiableLoadStreamsLookupUpkeepTransactor: VerifiableLoadStreamsLookupUpkeepTransactor{contract: contract}, VerifiableLoadStreamsLookupUpkeepFilterer: VerifiableLoadStreamsLookupUpkeepFilterer{contract: contract}}, nil } type VerifiableLoadStreamsLookupUpkeep struct { diff --git a/core/gethwrappers/generated/verifiable_load_upkeep_wrapper/verifiable_load_upkeep_wrapper.go b/core/gethwrappers/generated/verifiable_load_upkeep_wrapper/verifiable_load_upkeep_wrapper.go index 6dc8e73f1fd..b95f311f1cc 100644 --- a/core/gethwrappers/generated/verifiable_load_upkeep_wrapper/verifiable_load_upkeep_wrapper.go +++ b/core/gethwrappers/generated/verifiable_load_upkeep_wrapper/verifiable_load_upkeep_wrapper.go @@ -65,7 +65,7 @@ func DeployVerifiableLoadUpkeep(auth *bind.TransactOpts, backend bind.ContractBa if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VerifiableLoadUpkeep{VerifiableLoadUpkeepCaller: VerifiableLoadUpkeepCaller{contract: contract}, VerifiableLoadUpkeepTransactor: VerifiableLoadUpkeepTransactor{contract: contract}, VerifiableLoadUpkeepFilterer: VerifiableLoadUpkeepFilterer{contract: contract}}, nil + return address, tx, &VerifiableLoadUpkeep{address: address, abi: *parsed, VerifiableLoadUpkeepCaller: VerifiableLoadUpkeepCaller{contract: contract}, VerifiableLoadUpkeepTransactor: VerifiableLoadUpkeepTransactor{contract: contract}, VerifiableLoadUpkeepFilterer: VerifiableLoadUpkeepFilterer{contract: contract}}, nil } type VerifiableLoadUpkeep struct { diff --git a/core/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go b/core/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go index 8317e4c5b3e..16090150fcc 100644 --- a/core/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go +++ b/core/gethwrappers/generated/vrf_consumer_v2/vrf_consumer_v2.go @@ -50,7 +50,7 @@ func DeployVRFConsumerV2(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFConsumerV2{VRFConsumerV2Caller: VRFConsumerV2Caller{contract: contract}, VRFConsumerV2Transactor: VRFConsumerV2Transactor{contract: contract}, VRFConsumerV2Filterer: VRFConsumerV2Filterer{contract: contract}}, nil + return address, tx, &VRFConsumerV2{address: address, abi: *parsed, VRFConsumerV2Caller: VRFConsumerV2Caller{contract: contract}, VRFConsumerV2Transactor: VRFConsumerV2Transactor{contract: contract}, VRFConsumerV2Filterer: VRFConsumerV2Filterer{contract: contract}}, nil } type VRFConsumerV2 struct { diff --git a/core/gethwrappers/generated/vrf_consumer_v2_plus_upgradeable_example/vrf_consumer_v2_plus_upgradeable_example.go b/core/gethwrappers/generated/vrf_consumer_v2_plus_upgradeable_example/vrf_consumer_v2_plus_upgradeable_example.go index fce0876ba3b..deb678c4ebb 100644 --- a/core/gethwrappers/generated/vrf_consumer_v2_plus_upgradeable_example/vrf_consumer_v2_plus_upgradeable_example.go +++ b/core/gethwrappers/generated/vrf_consumer_v2_plus_upgradeable_example/vrf_consumer_v2_plus_upgradeable_example.go @@ -52,7 +52,7 @@ func DeployVRFConsumerV2PlusUpgradeableExample(auth *bind.TransactOpts, backend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFConsumerV2PlusUpgradeableExample{VRFConsumerV2PlusUpgradeableExampleCaller: VRFConsumerV2PlusUpgradeableExampleCaller{contract: contract}, VRFConsumerV2PlusUpgradeableExampleTransactor: VRFConsumerV2PlusUpgradeableExampleTransactor{contract: contract}, VRFConsumerV2PlusUpgradeableExampleFilterer: VRFConsumerV2PlusUpgradeableExampleFilterer{contract: contract}}, nil + return address, tx, &VRFConsumerV2PlusUpgradeableExample{address: address, abi: *parsed, VRFConsumerV2PlusUpgradeableExampleCaller: VRFConsumerV2PlusUpgradeableExampleCaller{contract: contract}, VRFConsumerV2PlusUpgradeableExampleTransactor: VRFConsumerV2PlusUpgradeableExampleTransactor{contract: contract}, VRFConsumerV2PlusUpgradeableExampleFilterer: VRFConsumerV2PlusUpgradeableExampleFilterer{contract: contract}}, nil } type VRFConsumerV2PlusUpgradeableExample struct { diff --git a/core/gethwrappers/generated/vrf_consumer_v2_upgradeable_example/vrf_consumer_v2_upgradeable_example.go b/core/gethwrappers/generated/vrf_consumer_v2_upgradeable_example/vrf_consumer_v2_upgradeable_example.go index 72bdbce58a3..5499868e323 100644 --- a/core/gethwrappers/generated/vrf_consumer_v2_upgradeable_example/vrf_consumer_v2_upgradeable_example.go +++ b/core/gethwrappers/generated/vrf_consumer_v2_upgradeable_example/vrf_consumer_v2_upgradeable_example.go @@ -52,7 +52,7 @@ func DeployVRFConsumerV2UpgradeableExample(auth *bind.TransactOpts, backend bind if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFConsumerV2UpgradeableExample{VRFConsumerV2UpgradeableExampleCaller: VRFConsumerV2UpgradeableExampleCaller{contract: contract}, VRFConsumerV2UpgradeableExampleTransactor: VRFConsumerV2UpgradeableExampleTransactor{contract: contract}, VRFConsumerV2UpgradeableExampleFilterer: VRFConsumerV2UpgradeableExampleFilterer{contract: contract}}, nil + return address, tx, &VRFConsumerV2UpgradeableExample{address: address, abi: *parsed, VRFConsumerV2UpgradeableExampleCaller: VRFConsumerV2UpgradeableExampleCaller{contract: contract}, VRFConsumerV2UpgradeableExampleTransactor: VRFConsumerV2UpgradeableExampleTransactor{contract: contract}, VRFConsumerV2UpgradeableExampleFilterer: VRFConsumerV2UpgradeableExampleFilterer{contract: contract}}, nil } type VRFConsumerV2UpgradeableExample struct { diff --git a/core/gethwrappers/generated/vrf_coordinator_mock/vrf_coordinator_mock.go b/core/gethwrappers/generated/vrf_coordinator_mock/vrf_coordinator_mock.go index 35fbabc8e29..961aa7b30ca 100644 --- a/core/gethwrappers/generated/vrf_coordinator_mock/vrf_coordinator_mock.go +++ b/core/gethwrappers/generated/vrf_coordinator_mock/vrf_coordinator_mock.go @@ -52,7 +52,7 @@ func DeployVRFCoordinatorMock(auth *bind.TransactOpts, backend bind.ContractBack if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFCoordinatorMock{VRFCoordinatorMockCaller: VRFCoordinatorMockCaller{contract: contract}, VRFCoordinatorMockTransactor: VRFCoordinatorMockTransactor{contract: contract}, VRFCoordinatorMockFilterer: VRFCoordinatorMockFilterer{contract: contract}}, nil + return address, tx, &VRFCoordinatorMock{address: address, abi: *parsed, VRFCoordinatorMockCaller: VRFCoordinatorMockCaller{contract: contract}, VRFCoordinatorMockTransactor: VRFCoordinatorMockTransactor{contract: contract}, VRFCoordinatorMockFilterer: VRFCoordinatorMockFilterer{contract: contract}}, nil } type VRFCoordinatorMock struct { diff --git a/core/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go b/core/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go index 1f9ef1eda20..ffe32f78101 100644 --- a/core/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go +++ b/core/gethwrappers/generated/vrf_coordinator_v2/vrf_coordinator_v2.go @@ -84,7 +84,7 @@ func DeployVRFCoordinatorV2(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFCoordinatorV2{VRFCoordinatorV2Caller: VRFCoordinatorV2Caller{contract: contract}, VRFCoordinatorV2Transactor: VRFCoordinatorV2Transactor{contract: contract}, VRFCoordinatorV2Filterer: VRFCoordinatorV2Filterer{contract: contract}}, nil + return address, tx, &VRFCoordinatorV2{address: address, abi: *parsed, VRFCoordinatorV2Caller: VRFCoordinatorV2Caller{contract: contract}, VRFCoordinatorV2Transactor: VRFCoordinatorV2Transactor{contract: contract}, VRFCoordinatorV2Filterer: VRFCoordinatorV2Filterer{contract: contract}}, nil } type VRFCoordinatorV2 struct { diff --git a/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go b/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go index e09c7c46e29..62e8b9f0de3 100644 --- a/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go +++ b/core/gethwrappers/generated/vrf_coordinator_v2_5/vrf_coordinator_v2_5.go @@ -87,7 +87,7 @@ func DeployVRFCoordinatorV25(auth *bind.TransactOpts, backend bind.ContractBacke if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFCoordinatorV25{VRFCoordinatorV25Caller: VRFCoordinatorV25Caller{contract: contract}, VRFCoordinatorV25Transactor: VRFCoordinatorV25Transactor{contract: contract}, VRFCoordinatorV25Filterer: VRFCoordinatorV25Filterer{contract: contract}}, nil + return address, tx, &VRFCoordinatorV25{address: address, abi: *parsed, VRFCoordinatorV25Caller: VRFCoordinatorV25Caller{contract: contract}, VRFCoordinatorV25Transactor: VRFCoordinatorV25Transactor{contract: contract}, VRFCoordinatorV25Filterer: VRFCoordinatorV25Filterer{contract: contract}}, nil } type VRFCoordinatorV25 struct { diff --git a/core/gethwrappers/generated/vrf_coordinator_v2_plus_v2_example/vrf_coordinator_v2_plus_v2_example.go b/core/gethwrappers/generated/vrf_coordinator_v2_plus_v2_example/vrf_coordinator_v2_plus_v2_example.go index dd7865fe8aa..3d9efea9f73 100644 --- a/core/gethwrappers/generated/vrf_coordinator_v2_plus_v2_example/vrf_coordinator_v2_plus_v2_example.go +++ b/core/gethwrappers/generated/vrf_coordinator_v2_plus_v2_example/vrf_coordinator_v2_plus_v2_example.go @@ -59,7 +59,7 @@ func DeployVRFCoordinatorV2PlusV2Example(auth *bind.TransactOpts, backend bind.C if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFCoordinatorV2PlusV2Example{VRFCoordinatorV2PlusV2ExampleCaller: VRFCoordinatorV2PlusV2ExampleCaller{contract: contract}, VRFCoordinatorV2PlusV2ExampleTransactor: VRFCoordinatorV2PlusV2ExampleTransactor{contract: contract}, VRFCoordinatorV2PlusV2ExampleFilterer: VRFCoordinatorV2PlusV2ExampleFilterer{contract: contract}}, nil + return address, tx, &VRFCoordinatorV2PlusV2Example{address: address, abi: *parsed, VRFCoordinatorV2PlusV2ExampleCaller: VRFCoordinatorV2PlusV2ExampleCaller{contract: contract}, VRFCoordinatorV2PlusV2ExampleTransactor: VRFCoordinatorV2PlusV2ExampleTransactor{contract: contract}, VRFCoordinatorV2PlusV2ExampleFilterer: VRFCoordinatorV2PlusV2ExampleFilterer{contract: contract}}, nil } type VRFCoordinatorV2PlusV2Example struct { diff --git a/core/gethwrappers/generated/vrf_external_sub_owner_example/vrf_external_sub_owner_example.go b/core/gethwrappers/generated/vrf_external_sub_owner_example/vrf_external_sub_owner_example.go index 8752e2f39cc..4ab3cdf591d 100644 --- a/core/gethwrappers/generated/vrf_external_sub_owner_example/vrf_external_sub_owner_example.go +++ b/core/gethwrappers/generated/vrf_external_sub_owner_example/vrf_external_sub_owner_example.go @@ -50,7 +50,7 @@ func DeployVRFExternalSubOwnerExample(auth *bind.TransactOpts, backend bind.Cont if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFExternalSubOwnerExample{VRFExternalSubOwnerExampleCaller: VRFExternalSubOwnerExampleCaller{contract: contract}, VRFExternalSubOwnerExampleTransactor: VRFExternalSubOwnerExampleTransactor{contract: contract}, VRFExternalSubOwnerExampleFilterer: VRFExternalSubOwnerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFExternalSubOwnerExample{address: address, abi: *parsed, VRFExternalSubOwnerExampleCaller: VRFExternalSubOwnerExampleCaller{contract: contract}, VRFExternalSubOwnerExampleTransactor: VRFExternalSubOwnerExampleTransactor{contract: contract}, VRFExternalSubOwnerExampleFilterer: VRFExternalSubOwnerExampleFilterer{contract: contract}}, nil } type VRFExternalSubOwnerExample struct { diff --git a/core/gethwrappers/generated/vrf_load_test_external_sub_owner/vrf_load_test_external_sub_owner.go b/core/gethwrappers/generated/vrf_load_test_external_sub_owner/vrf_load_test_external_sub_owner.go index ea3302d2e73..e98239eb4e2 100644 --- a/core/gethwrappers/generated/vrf_load_test_external_sub_owner/vrf_load_test_external_sub_owner.go +++ b/core/gethwrappers/generated/vrf_load_test_external_sub_owner/vrf_load_test_external_sub_owner.go @@ -52,7 +52,7 @@ func DeployVRFLoadTestExternalSubOwner(auth *bind.TransactOpts, backend bind.Con if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFLoadTestExternalSubOwner{VRFLoadTestExternalSubOwnerCaller: VRFLoadTestExternalSubOwnerCaller{contract: contract}, VRFLoadTestExternalSubOwnerTransactor: VRFLoadTestExternalSubOwnerTransactor{contract: contract}, VRFLoadTestExternalSubOwnerFilterer: VRFLoadTestExternalSubOwnerFilterer{contract: contract}}, nil + return address, tx, &VRFLoadTestExternalSubOwner{address: address, abi: *parsed, VRFLoadTestExternalSubOwnerCaller: VRFLoadTestExternalSubOwnerCaller{contract: contract}, VRFLoadTestExternalSubOwnerTransactor: VRFLoadTestExternalSubOwnerTransactor{contract: contract}, VRFLoadTestExternalSubOwnerFilterer: VRFLoadTestExternalSubOwnerFilterer{contract: contract}}, nil } type VRFLoadTestExternalSubOwner struct { diff --git a/core/gethwrappers/generated/vrf_load_test_ownerless_consumer/vrf_load_test_ownerless_consumer.go b/core/gethwrappers/generated/vrf_load_test_ownerless_consumer/vrf_load_test_ownerless_consumer.go index 6beadf5ad52..fea4360e502 100644 --- a/core/gethwrappers/generated/vrf_load_test_ownerless_consumer/vrf_load_test_ownerless_consumer.go +++ b/core/gethwrappers/generated/vrf_load_test_ownerless_consumer/vrf_load_test_ownerless_consumer.go @@ -50,7 +50,7 @@ func DeployVRFLoadTestOwnerlessConsumer(auth *bind.TransactOpts, backend bind.Co if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFLoadTestOwnerlessConsumer{VRFLoadTestOwnerlessConsumerCaller: VRFLoadTestOwnerlessConsumerCaller{contract: contract}, VRFLoadTestOwnerlessConsumerTransactor: VRFLoadTestOwnerlessConsumerTransactor{contract: contract}, VRFLoadTestOwnerlessConsumerFilterer: VRFLoadTestOwnerlessConsumerFilterer{contract: contract}}, nil + return address, tx, &VRFLoadTestOwnerlessConsumer{address: address, abi: *parsed, VRFLoadTestOwnerlessConsumerCaller: VRFLoadTestOwnerlessConsumerCaller{contract: contract}, VRFLoadTestOwnerlessConsumerTransactor: VRFLoadTestOwnerlessConsumerTransactor{contract: contract}, VRFLoadTestOwnerlessConsumerFilterer: VRFLoadTestOwnerlessConsumerFilterer{contract: contract}}, nil } type VRFLoadTestOwnerlessConsumer struct { diff --git a/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go b/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go index 93d50b72dd5..76b5e267d3b 100644 --- a/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go +++ b/core/gethwrappers/generated/vrf_load_test_with_metrics/vrf_load_test_with_metrics.go @@ -52,7 +52,7 @@ func DeployVRFV2LoadTestWithMetrics(auth *bind.TransactOpts, backend bind.Contra if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2LoadTestWithMetrics{VRFV2LoadTestWithMetricsCaller: VRFV2LoadTestWithMetricsCaller{contract: contract}, VRFV2LoadTestWithMetricsTransactor: VRFV2LoadTestWithMetricsTransactor{contract: contract}, VRFV2LoadTestWithMetricsFilterer: VRFV2LoadTestWithMetricsFilterer{contract: contract}}, nil + return address, tx, &VRFV2LoadTestWithMetrics{address: address, abi: *parsed, VRFV2LoadTestWithMetricsCaller: VRFV2LoadTestWithMetricsCaller{contract: contract}, VRFV2LoadTestWithMetricsTransactor: VRFV2LoadTestWithMetricsTransactor{contract: contract}, VRFV2LoadTestWithMetricsFilterer: VRFV2LoadTestWithMetricsFilterer{contract: contract}}, nil } type VRFV2LoadTestWithMetrics struct { diff --git a/core/gethwrappers/generated/vrf_malicious_consumer_v2/vrf_malicious_consumer_v2.go b/core/gethwrappers/generated/vrf_malicious_consumer_v2/vrf_malicious_consumer_v2.go index 5fb16e00e8b..1fe5e61f3ab 100644 --- a/core/gethwrappers/generated/vrf_malicious_consumer_v2/vrf_malicious_consumer_v2.go +++ b/core/gethwrappers/generated/vrf_malicious_consumer_v2/vrf_malicious_consumer_v2.go @@ -50,7 +50,7 @@ func DeployVRFMaliciousConsumerV2(auth *bind.TransactOpts, backend bind.Contract if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFMaliciousConsumerV2{VRFMaliciousConsumerV2Caller: VRFMaliciousConsumerV2Caller{contract: contract}, VRFMaliciousConsumerV2Transactor: VRFMaliciousConsumerV2Transactor{contract: contract}, VRFMaliciousConsumerV2Filterer: VRFMaliciousConsumerV2Filterer{contract: contract}}, nil + return address, tx, &VRFMaliciousConsumerV2{address: address, abi: *parsed, VRFMaliciousConsumerV2Caller: VRFMaliciousConsumerV2Caller{contract: contract}, VRFMaliciousConsumerV2Transactor: VRFMaliciousConsumerV2Transactor{contract: contract}, VRFMaliciousConsumerV2Filterer: VRFMaliciousConsumerV2Filterer{contract: contract}}, nil } type VRFMaliciousConsumerV2 struct { diff --git a/core/gethwrappers/generated/vrf_malicious_consumer_v2_plus/vrf_malicious_consumer_v2_plus.go b/core/gethwrappers/generated/vrf_malicious_consumer_v2_plus/vrf_malicious_consumer_v2_plus.go index 64a5cace7f5..df5a49a8de4 100644 --- a/core/gethwrappers/generated/vrf_malicious_consumer_v2_plus/vrf_malicious_consumer_v2_plus.go +++ b/core/gethwrappers/generated/vrf_malicious_consumer_v2_plus/vrf_malicious_consumer_v2_plus.go @@ -52,7 +52,7 @@ func DeployVRFMaliciousConsumerV2Plus(auth *bind.TransactOpts, backend bind.Cont if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFMaliciousConsumerV2Plus{VRFMaliciousConsumerV2PlusCaller: VRFMaliciousConsumerV2PlusCaller{contract: contract}, VRFMaliciousConsumerV2PlusTransactor: VRFMaliciousConsumerV2PlusTransactor{contract: contract}, VRFMaliciousConsumerV2PlusFilterer: VRFMaliciousConsumerV2PlusFilterer{contract: contract}}, nil + return address, tx, &VRFMaliciousConsumerV2Plus{address: address, abi: *parsed, VRFMaliciousConsumerV2PlusCaller: VRFMaliciousConsumerV2PlusCaller{contract: contract}, VRFMaliciousConsumerV2PlusTransactor: VRFMaliciousConsumerV2PlusTransactor{contract: contract}, VRFMaliciousConsumerV2PlusFilterer: VRFMaliciousConsumerV2PlusFilterer{contract: contract}}, nil } type VRFMaliciousConsumerV2Plus struct { diff --git a/core/gethwrappers/generated/vrf_owner/vrf_owner.go b/core/gethwrappers/generated/vrf_owner/vrf_owner.go index 8a97fcf5aa6..b029bd393f6 100644 --- a/core/gethwrappers/generated/vrf_owner/vrf_owner.go +++ b/core/gethwrappers/generated/vrf_owner/vrf_owner.go @@ -84,7 +84,7 @@ func DeployVRFOwner(auth *bind.TransactOpts, backend bind.ContractBackend, _vrfC if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFOwner{VRFOwnerCaller: VRFOwnerCaller{contract: contract}, VRFOwnerTransactor: VRFOwnerTransactor{contract: contract}, VRFOwnerFilterer: VRFOwnerFilterer{contract: contract}}, nil + return address, tx, &VRFOwner{address: address, abi: *parsed, VRFOwnerCaller: VRFOwnerCaller{contract: contract}, VRFOwnerTransactor: VRFOwnerTransactor{contract: contract}, VRFOwnerFilterer: VRFOwnerFilterer{contract: contract}}, nil } type VRFOwner struct { diff --git a/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go b/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go index ce42ddb7d6c..8a17b64378e 100644 --- a/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go +++ b/core/gethwrappers/generated/vrf_owner_test_consumer/vrf_owner_test_consumer.go @@ -52,7 +52,7 @@ func DeployVRFV2OwnerTestConsumer(auth *bind.TransactOpts, backend bind.Contract if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2OwnerTestConsumer{VRFV2OwnerTestConsumerCaller: VRFV2OwnerTestConsumerCaller{contract: contract}, VRFV2OwnerTestConsumerTransactor: VRFV2OwnerTestConsumerTransactor{contract: contract}, VRFV2OwnerTestConsumerFilterer: VRFV2OwnerTestConsumerFilterer{contract: contract}}, nil + return address, tx, &VRFV2OwnerTestConsumer{address: address, abi: *parsed, VRFV2OwnerTestConsumerCaller: VRFV2OwnerTestConsumerCaller{contract: contract}, VRFV2OwnerTestConsumerTransactor: VRFV2OwnerTestConsumerTransactor{contract: contract}, VRFV2OwnerTestConsumerFilterer: VRFV2OwnerTestConsumerFilterer{contract: contract}}, nil } type VRFV2OwnerTestConsumer struct { diff --git a/core/gethwrappers/generated/vrf_ownerless_consumer_example/vrf_ownerless_consumer_example.go b/core/gethwrappers/generated/vrf_ownerless_consumer_example/vrf_ownerless_consumer_example.go index c71553ac815..697a862533d 100644 --- a/core/gethwrappers/generated/vrf_ownerless_consumer_example/vrf_ownerless_consumer_example.go +++ b/core/gethwrappers/generated/vrf_ownerless_consumer_example/vrf_ownerless_consumer_example.go @@ -50,7 +50,7 @@ func DeployVRFOwnerlessConsumerExample(auth *bind.TransactOpts, backend bind.Con if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFOwnerlessConsumerExample{VRFOwnerlessConsumerExampleCaller: VRFOwnerlessConsumerExampleCaller{contract: contract}, VRFOwnerlessConsumerExampleTransactor: VRFOwnerlessConsumerExampleTransactor{contract: contract}, VRFOwnerlessConsumerExampleFilterer: VRFOwnerlessConsumerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFOwnerlessConsumerExample{address: address, abi: *parsed, VRFOwnerlessConsumerExampleCaller: VRFOwnerlessConsumerExampleCaller{contract: contract}, VRFOwnerlessConsumerExampleTransactor: VRFOwnerlessConsumerExampleTransactor{contract: contract}, VRFOwnerlessConsumerExampleFilterer: VRFOwnerlessConsumerExampleFilterer{contract: contract}}, nil } type VRFOwnerlessConsumerExample struct { diff --git a/core/gethwrappers/generated/vrf_single_consumer_example/vrf_single_consumer_example.go b/core/gethwrappers/generated/vrf_single_consumer_example/vrf_single_consumer_example.go index affc8c53715..ac5c081ab3a 100644 --- a/core/gethwrappers/generated/vrf_single_consumer_example/vrf_single_consumer_example.go +++ b/core/gethwrappers/generated/vrf_single_consumer_example/vrf_single_consumer_example.go @@ -50,7 +50,7 @@ func DeployVRFSingleConsumerExample(auth *bind.TransactOpts, backend bind.Contra if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFSingleConsumerExample{VRFSingleConsumerExampleCaller: VRFSingleConsumerExampleCaller{contract: contract}, VRFSingleConsumerExampleTransactor: VRFSingleConsumerExampleTransactor{contract: contract}, VRFSingleConsumerExampleFilterer: VRFSingleConsumerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFSingleConsumerExample{address: address, abi: *parsed, VRFSingleConsumerExampleCaller: VRFSingleConsumerExampleCaller{contract: contract}, VRFSingleConsumerExampleTransactor: VRFSingleConsumerExampleTransactor{contract: contract}, VRFSingleConsumerExampleFilterer: VRFSingleConsumerExampleFilterer{contract: contract}}, nil } type VRFSingleConsumerExample struct { diff --git a/core/gethwrappers/generated/vrf_v2_consumer_wrapper/vrf_v2_consumer_wrapper.go b/core/gethwrappers/generated/vrf_v2_consumer_wrapper/vrf_v2_consumer_wrapper.go index a5d57945802..e35efc9ec89 100644 --- a/core/gethwrappers/generated/vrf_v2_consumer_wrapper/vrf_v2_consumer_wrapper.go +++ b/core/gethwrappers/generated/vrf_v2_consumer_wrapper/vrf_v2_consumer_wrapper.go @@ -52,7 +52,7 @@ func DeployVRFv2Consumer(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFv2Consumer{VRFv2ConsumerCaller: VRFv2ConsumerCaller{contract: contract}, VRFv2ConsumerTransactor: VRFv2ConsumerTransactor{contract: contract}, VRFv2ConsumerFilterer: VRFv2ConsumerFilterer{contract: contract}}, nil + return address, tx, &VRFv2Consumer{address: address, abi: *parsed, VRFv2ConsumerCaller: VRFv2ConsumerCaller{contract: contract}, VRFv2ConsumerTransactor: VRFv2ConsumerTransactor{contract: contract}, VRFv2ConsumerFilterer: VRFv2ConsumerFilterer{contract: contract}}, nil } type VRFv2Consumer struct { diff --git a/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics/vrf_v2plus_load_test_with_metrics.go b/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics/vrf_v2plus_load_test_with_metrics.go index c8971595c53..017423772f7 100644 --- a/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics/vrf_v2plus_load_test_with_metrics.go +++ b/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics/vrf_v2plus_load_test_with_metrics.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusLoadTestWithMetrics(auth *bind.TransactOpts, backend bind.Co if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusLoadTestWithMetrics{VRFV2PlusLoadTestWithMetricsCaller: VRFV2PlusLoadTestWithMetricsCaller{contract: contract}, VRFV2PlusLoadTestWithMetricsTransactor: VRFV2PlusLoadTestWithMetricsTransactor{contract: contract}, VRFV2PlusLoadTestWithMetricsFilterer: VRFV2PlusLoadTestWithMetricsFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusLoadTestWithMetrics{address: address, abi: *parsed, VRFV2PlusLoadTestWithMetricsCaller: VRFV2PlusLoadTestWithMetricsCaller{contract: contract}, VRFV2PlusLoadTestWithMetricsTransactor: VRFV2PlusLoadTestWithMetricsTransactor{contract: contract}, VRFV2PlusLoadTestWithMetricsFilterer: VRFV2PlusLoadTestWithMetricsFilterer{contract: contract}}, nil } type VRFV2PlusLoadTestWithMetrics struct { diff --git a/core/gethwrappers/generated/vrf_v2plus_single_consumer/vrf_v2plus_single_consumer.go b/core/gethwrappers/generated/vrf_v2plus_single_consumer/vrf_v2plus_single_consumer.go index afa659269db..b9de348b103 100644 --- a/core/gethwrappers/generated/vrf_v2plus_single_consumer/vrf_v2plus_single_consumer.go +++ b/core/gethwrappers/generated/vrf_v2plus_single_consumer/vrf_v2plus_single_consumer.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusSingleConsumerExample(auth *bind.TransactOpts, backend bind. if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusSingleConsumerExample{VRFV2PlusSingleConsumerExampleCaller: VRFV2PlusSingleConsumerExampleCaller{contract: contract}, VRFV2PlusSingleConsumerExampleTransactor: VRFV2PlusSingleConsumerExampleTransactor{contract: contract}, VRFV2PlusSingleConsumerExampleFilterer: VRFV2PlusSingleConsumerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusSingleConsumerExample{address: address, abi: *parsed, VRFV2PlusSingleConsumerExampleCaller: VRFV2PlusSingleConsumerExampleCaller{contract: contract}, VRFV2PlusSingleConsumerExampleTransactor: VRFV2PlusSingleConsumerExampleTransactor{contract: contract}, VRFV2PlusSingleConsumerExampleFilterer: VRFV2PlusSingleConsumerExampleFilterer{contract: contract}}, nil } type VRFV2PlusSingleConsumerExample struct { diff --git a/core/gethwrappers/generated/vrf_v2plus_sub_owner/vrf_v2plus_sub_owner.go b/core/gethwrappers/generated/vrf_v2plus_sub_owner/vrf_v2plus_sub_owner.go index da32a6a202c..8cc57fce6c6 100644 --- a/core/gethwrappers/generated/vrf_v2plus_sub_owner/vrf_v2plus_sub_owner.go +++ b/core/gethwrappers/generated/vrf_v2plus_sub_owner/vrf_v2plus_sub_owner.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusExternalSubOwnerExample(auth *bind.TransactOpts, backend bin if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusExternalSubOwnerExample{VRFV2PlusExternalSubOwnerExampleCaller: VRFV2PlusExternalSubOwnerExampleCaller{contract: contract}, VRFV2PlusExternalSubOwnerExampleTransactor: VRFV2PlusExternalSubOwnerExampleTransactor{contract: contract}, VRFV2PlusExternalSubOwnerExampleFilterer: VRFV2PlusExternalSubOwnerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusExternalSubOwnerExample{address: address, abi: *parsed, VRFV2PlusExternalSubOwnerExampleCaller: VRFV2PlusExternalSubOwnerExampleCaller{contract: contract}, VRFV2PlusExternalSubOwnerExampleTransactor: VRFV2PlusExternalSubOwnerExampleTransactor{contract: contract}, VRFV2PlusExternalSubOwnerExampleFilterer: VRFV2PlusExternalSubOwnerExampleFilterer{contract: contract}}, nil } type VRFV2PlusExternalSubOwnerExample struct { diff --git a/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go b/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go index 852501d09ec..7aae3d37770 100644 --- a/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go +++ b/core/gethwrappers/generated/vrf_v2plus_upgraded_version/vrf_v2plus_upgraded_version.go @@ -87,7 +87,7 @@ func DeployVRFCoordinatorV2PlusUpgradedVersion(auth *bind.TransactOpts, backend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFCoordinatorV2PlusUpgradedVersion{VRFCoordinatorV2PlusUpgradedVersionCaller: VRFCoordinatorV2PlusUpgradedVersionCaller{contract: contract}, VRFCoordinatorV2PlusUpgradedVersionTransactor: VRFCoordinatorV2PlusUpgradedVersionTransactor{contract: contract}, VRFCoordinatorV2PlusUpgradedVersionFilterer: VRFCoordinatorV2PlusUpgradedVersionFilterer{contract: contract}}, nil + return address, tx, &VRFCoordinatorV2PlusUpgradedVersion{address: address, abi: *parsed, VRFCoordinatorV2PlusUpgradedVersionCaller: VRFCoordinatorV2PlusUpgradedVersionCaller{contract: contract}, VRFCoordinatorV2PlusUpgradedVersionTransactor: VRFCoordinatorV2PlusUpgradedVersionTransactor{contract: contract}, VRFCoordinatorV2PlusUpgradedVersionFilterer: VRFCoordinatorV2PlusUpgradedVersionFilterer{contract: contract}}, nil } type VRFCoordinatorV2PlusUpgradedVersion struct { diff --git a/core/gethwrappers/generated/vrfv2_proxy_admin/vrfv2_proxy_admin.go b/core/gethwrappers/generated/vrfv2_proxy_admin/vrfv2_proxy_admin.go index 2cb4edd2082..d92679f4ad7 100644 --- a/core/gethwrappers/generated/vrfv2_proxy_admin/vrfv2_proxy_admin.go +++ b/core/gethwrappers/generated/vrfv2_proxy_admin/vrfv2_proxy_admin.go @@ -52,7 +52,7 @@ func DeployVRFV2ProxyAdmin(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2ProxyAdmin{VRFV2ProxyAdminCaller: VRFV2ProxyAdminCaller{contract: contract}, VRFV2ProxyAdminTransactor: VRFV2ProxyAdminTransactor{contract: contract}, VRFV2ProxyAdminFilterer: VRFV2ProxyAdminFilterer{contract: contract}}, nil + return address, tx, &VRFV2ProxyAdmin{address: address, abi: *parsed, VRFV2ProxyAdminCaller: VRFV2ProxyAdminCaller{contract: contract}, VRFV2ProxyAdminTransactor: VRFV2ProxyAdminTransactor{contract: contract}, VRFV2ProxyAdminFilterer: VRFV2ProxyAdminFilterer{contract: contract}}, nil } type VRFV2ProxyAdmin struct { diff --git a/core/gethwrappers/generated/vrfv2_reverting_example/vrfv2_reverting_example.go b/core/gethwrappers/generated/vrfv2_reverting_example/vrfv2_reverting_example.go index 9f8a4eb0fac..facfc931c91 100644 --- a/core/gethwrappers/generated/vrfv2_reverting_example/vrfv2_reverting_example.go +++ b/core/gethwrappers/generated/vrfv2_reverting_example/vrfv2_reverting_example.go @@ -50,7 +50,7 @@ func DeployVRFV2RevertingExample(auth *bind.TransactOpts, backend bind.ContractB if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2RevertingExample{VRFV2RevertingExampleCaller: VRFV2RevertingExampleCaller{contract: contract}, VRFV2RevertingExampleTransactor: VRFV2RevertingExampleTransactor{contract: contract}, VRFV2RevertingExampleFilterer: VRFV2RevertingExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2RevertingExample{address: address, abi: *parsed, VRFV2RevertingExampleCaller: VRFV2RevertingExampleCaller{contract: contract}, VRFV2RevertingExampleTransactor: VRFV2RevertingExampleTransactor{contract: contract}, VRFV2RevertingExampleFilterer: VRFV2RevertingExampleFilterer{contract: contract}}, nil } type VRFV2RevertingExample struct { diff --git a/core/gethwrappers/generated/vrfv2_transparent_upgradeable_proxy/vrfv2_transparent_upgradeable_proxy.go b/core/gethwrappers/generated/vrfv2_transparent_upgradeable_proxy/vrfv2_transparent_upgradeable_proxy.go index 657cc1894f3..c808650a084 100644 --- a/core/gethwrappers/generated/vrfv2_transparent_upgradeable_proxy/vrfv2_transparent_upgradeable_proxy.go +++ b/core/gethwrappers/generated/vrfv2_transparent_upgradeable_proxy/vrfv2_transparent_upgradeable_proxy.go @@ -52,7 +52,7 @@ func DeployVRFV2TransparentUpgradeableProxy(auth *bind.TransactOpts, backend bin if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2TransparentUpgradeableProxy{VRFV2TransparentUpgradeableProxyCaller: VRFV2TransparentUpgradeableProxyCaller{contract: contract}, VRFV2TransparentUpgradeableProxyTransactor: VRFV2TransparentUpgradeableProxyTransactor{contract: contract}, VRFV2TransparentUpgradeableProxyFilterer: VRFV2TransparentUpgradeableProxyFilterer{contract: contract}}, nil + return address, tx, &VRFV2TransparentUpgradeableProxy{address: address, abi: *parsed, VRFV2TransparentUpgradeableProxyCaller: VRFV2TransparentUpgradeableProxyCaller{contract: contract}, VRFV2TransparentUpgradeableProxyTransactor: VRFV2TransparentUpgradeableProxyTransactor{contract: contract}, VRFV2TransparentUpgradeableProxyFilterer: VRFV2TransparentUpgradeableProxyFilterer{contract: contract}}, nil } type VRFV2TransparentUpgradeableProxy struct { diff --git a/core/gethwrappers/generated/vrfv2_wrapper/vrfv2_wrapper.go b/core/gethwrappers/generated/vrfv2_wrapper/vrfv2_wrapper.go index 61cb52e117d..9e7e25229e5 100644 --- a/core/gethwrappers/generated/vrfv2_wrapper/vrfv2_wrapper.go +++ b/core/gethwrappers/generated/vrfv2_wrapper/vrfv2_wrapper.go @@ -52,7 +52,7 @@ func DeployVRFV2Wrapper(auth *bind.TransactOpts, backend bind.ContractBackend, _ if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2Wrapper{VRFV2WrapperCaller: VRFV2WrapperCaller{contract: contract}, VRFV2WrapperTransactor: VRFV2WrapperTransactor{contract: contract}, VRFV2WrapperFilterer: VRFV2WrapperFilterer{contract: contract}}, nil + return address, tx, &VRFV2Wrapper{address: address, abi: *parsed, VRFV2WrapperCaller: VRFV2WrapperCaller{contract: contract}, VRFV2WrapperTransactor: VRFV2WrapperTransactor{contract: contract}, VRFV2WrapperFilterer: VRFV2WrapperFilterer{contract: contract}}, nil } type VRFV2Wrapper struct { diff --git a/core/gethwrappers/generated/vrfv2_wrapper_consumer_example/vrfv2_wrapper_consumer_example.go b/core/gethwrappers/generated/vrfv2_wrapper_consumer_example/vrfv2_wrapper_consumer_example.go index ba38ddf436f..dbf5d97f9cd 100644 --- a/core/gethwrappers/generated/vrfv2_wrapper_consumer_example/vrfv2_wrapper_consumer_example.go +++ b/core/gethwrappers/generated/vrfv2_wrapper_consumer_example/vrfv2_wrapper_consumer_example.go @@ -52,7 +52,7 @@ func DeployVRFV2WrapperConsumerExample(auth *bind.TransactOpts, backend bind.Con if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2WrapperConsumerExample{VRFV2WrapperConsumerExampleCaller: VRFV2WrapperConsumerExampleCaller{contract: contract}, VRFV2WrapperConsumerExampleTransactor: VRFV2WrapperConsumerExampleTransactor{contract: contract}, VRFV2WrapperConsumerExampleFilterer: VRFV2WrapperConsumerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2WrapperConsumerExample{address: address, abi: *parsed, VRFV2WrapperConsumerExampleCaller: VRFV2WrapperConsumerExampleCaller{contract: contract}, VRFV2WrapperConsumerExampleTransactor: VRFV2WrapperConsumerExampleTransactor{contract: contract}, VRFV2WrapperConsumerExampleFilterer: VRFV2WrapperConsumerExampleFilterer{contract: contract}}, nil } type VRFV2WrapperConsumerExample struct { diff --git a/core/gethwrappers/generated/vrfv2plus_client/vrfv2plus_client.go b/core/gethwrappers/generated/vrfv2plus_client/vrfv2plus_client.go index 91112ff856f..f6a65a63f27 100644 --- a/core/gethwrappers/generated/vrfv2plus_client/vrfv2plus_client.go +++ b/core/gethwrappers/generated/vrfv2plus_client/vrfv2plus_client.go @@ -50,7 +50,7 @@ func DeployVRFV2PlusClient(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusClient{VRFV2PlusClientCaller: VRFV2PlusClientCaller{contract: contract}, VRFV2PlusClientTransactor: VRFV2PlusClientTransactor{contract: contract}, VRFV2PlusClientFilterer: VRFV2PlusClientFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusClient{address: address, abi: *parsed, VRFV2PlusClientCaller: VRFV2PlusClientCaller{contract: contract}, VRFV2PlusClientTransactor: VRFV2PlusClientTransactor{contract: contract}, VRFV2PlusClientFilterer: VRFV2PlusClientFilterer{contract: contract}}, nil } type VRFV2PlusClient struct { diff --git a/core/gethwrappers/generated/vrfv2plus_consumer_example/vrfv2plus_consumer_example.go b/core/gethwrappers/generated/vrfv2plus_consumer_example/vrfv2plus_consumer_example.go index 2ad412a1223..20f3d4422b1 100644 --- a/core/gethwrappers/generated/vrfv2plus_consumer_example/vrfv2plus_consumer_example.go +++ b/core/gethwrappers/generated/vrfv2plus_consumer_example/vrfv2plus_consumer_example.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusConsumerExample(auth *bind.TransactOpts, backend bind.Contra if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusConsumerExample{VRFV2PlusConsumerExampleCaller: VRFV2PlusConsumerExampleCaller{contract: contract}, VRFV2PlusConsumerExampleTransactor: VRFV2PlusConsumerExampleTransactor{contract: contract}, VRFV2PlusConsumerExampleFilterer: VRFV2PlusConsumerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusConsumerExample{address: address, abi: *parsed, VRFV2PlusConsumerExampleCaller: VRFV2PlusConsumerExampleCaller{contract: contract}, VRFV2PlusConsumerExampleTransactor: VRFV2PlusConsumerExampleTransactor{contract: contract}, VRFV2PlusConsumerExampleFilterer: VRFV2PlusConsumerExampleFilterer{contract: contract}}, nil } type VRFV2PlusConsumerExample struct { diff --git a/core/gethwrappers/generated/vrfv2plus_malicious_migrator/vrfv2plus_malicious_migrator.go b/core/gethwrappers/generated/vrfv2plus_malicious_migrator/vrfv2plus_malicious_migrator.go index c0c19a1134c..03c5ffd8ccc 100644 --- a/core/gethwrappers/generated/vrfv2plus_malicious_migrator/vrfv2plus_malicious_migrator.go +++ b/core/gethwrappers/generated/vrfv2plus_malicious_migrator/vrfv2plus_malicious_migrator.go @@ -50,7 +50,7 @@ func DeployVRFV2PlusMaliciousMigrator(auth *bind.TransactOpts, backend bind.Cont if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusMaliciousMigrator{VRFV2PlusMaliciousMigratorCaller: VRFV2PlusMaliciousMigratorCaller{contract: contract}, VRFV2PlusMaliciousMigratorTransactor: VRFV2PlusMaliciousMigratorTransactor{contract: contract}, VRFV2PlusMaliciousMigratorFilterer: VRFV2PlusMaliciousMigratorFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusMaliciousMigrator{address: address, abi: *parsed, VRFV2PlusMaliciousMigratorCaller: VRFV2PlusMaliciousMigratorCaller{contract: contract}, VRFV2PlusMaliciousMigratorTransactor: VRFV2PlusMaliciousMigratorTransactor{contract: contract}, VRFV2PlusMaliciousMigratorFilterer: VRFV2PlusMaliciousMigratorFilterer{contract: contract}}, nil } type VRFV2PlusMaliciousMigrator struct { diff --git a/core/gethwrappers/generated/vrfv2plus_reverting_example/vrfv2plus_reverting_example.go b/core/gethwrappers/generated/vrfv2plus_reverting_example/vrfv2plus_reverting_example.go index 72364701395..5e66eb2474d 100644 --- a/core/gethwrappers/generated/vrfv2plus_reverting_example/vrfv2plus_reverting_example.go +++ b/core/gethwrappers/generated/vrfv2plus_reverting_example/vrfv2plus_reverting_example.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusRevertingExample(auth *bind.TransactOpts, backend bind.Contr if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusRevertingExample{VRFV2PlusRevertingExampleCaller: VRFV2PlusRevertingExampleCaller{contract: contract}, VRFV2PlusRevertingExampleTransactor: VRFV2PlusRevertingExampleTransactor{contract: contract}, VRFV2PlusRevertingExampleFilterer: VRFV2PlusRevertingExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusRevertingExample{address: address, abi: *parsed, VRFV2PlusRevertingExampleCaller: VRFV2PlusRevertingExampleCaller{contract: contract}, VRFV2PlusRevertingExampleTransactor: VRFV2PlusRevertingExampleTransactor{contract: contract}, VRFV2PlusRevertingExampleFilterer: VRFV2PlusRevertingExampleFilterer{contract: contract}}, nil } type VRFV2PlusRevertingExample struct { diff --git a/core/gethwrappers/generated/vrfv2plus_wrapper/vrfv2plus_wrapper.go b/core/gethwrappers/generated/vrfv2plus_wrapper/vrfv2plus_wrapper.go index d7894c576e3..5a5ccb34f1d 100644 --- a/core/gethwrappers/generated/vrfv2plus_wrapper/vrfv2plus_wrapper.go +++ b/core/gethwrappers/generated/vrfv2plus_wrapper/vrfv2plus_wrapper.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusWrapper(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusWrapper{VRFV2PlusWrapperCaller: VRFV2PlusWrapperCaller{contract: contract}, VRFV2PlusWrapperTransactor: VRFV2PlusWrapperTransactor{contract: contract}, VRFV2PlusWrapperFilterer: VRFV2PlusWrapperFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusWrapper{address: address, abi: *parsed, VRFV2PlusWrapperCaller: VRFV2PlusWrapperCaller{contract: contract}, VRFV2PlusWrapperTransactor: VRFV2PlusWrapperTransactor{contract: contract}, VRFV2PlusWrapperFilterer: VRFV2PlusWrapperFilterer{contract: contract}}, nil } type VRFV2PlusWrapper struct { diff --git a/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example/vrfv2plus_wrapper_consumer_example.go b/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example/vrfv2plus_wrapper_consumer_example.go index 9cbd5ef964b..ee2f1e360b0 100644 --- a/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example/vrfv2plus_wrapper_consumer_example.go +++ b/core/gethwrappers/generated/vrfv2plus_wrapper_consumer_example/vrfv2plus_wrapper_consumer_example.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusWrapperConsumerExample(auth *bind.TransactOpts, backend bind if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusWrapperConsumerExample{VRFV2PlusWrapperConsumerExampleCaller: VRFV2PlusWrapperConsumerExampleCaller{contract: contract}, VRFV2PlusWrapperConsumerExampleTransactor: VRFV2PlusWrapperConsumerExampleTransactor{contract: contract}, VRFV2PlusWrapperConsumerExampleFilterer: VRFV2PlusWrapperConsumerExampleFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusWrapperConsumerExample{address: address, abi: *parsed, VRFV2PlusWrapperConsumerExampleCaller: VRFV2PlusWrapperConsumerExampleCaller{contract: contract}, VRFV2PlusWrapperConsumerExampleTransactor: VRFV2PlusWrapperConsumerExampleTransactor{contract: contract}, VRFV2PlusWrapperConsumerExampleFilterer: VRFV2PlusWrapperConsumerExampleFilterer{contract: contract}}, nil } type VRFV2PlusWrapperConsumerExample struct { diff --git a/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer/vrfv2plus_wrapper_load_test_consumer.go b/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer/vrfv2plus_wrapper_load_test_consumer.go index 231945c9b7a..8da1419620b 100644 --- a/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer/vrfv2plus_wrapper_load_test_consumer.go +++ b/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer/vrfv2plus_wrapper_load_test_consumer.go @@ -52,7 +52,7 @@ func DeployVRFV2PlusWrapperLoadTestConsumer(auth *bind.TransactOpts, backend bin if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VRFV2PlusWrapperLoadTestConsumer{VRFV2PlusWrapperLoadTestConsumerCaller: VRFV2PlusWrapperLoadTestConsumerCaller{contract: contract}, VRFV2PlusWrapperLoadTestConsumerTransactor: VRFV2PlusWrapperLoadTestConsumerTransactor{contract: contract}, VRFV2PlusWrapperLoadTestConsumerFilterer: VRFV2PlusWrapperLoadTestConsumerFilterer{contract: contract}}, nil + return address, tx, &VRFV2PlusWrapperLoadTestConsumer{address: address, abi: *parsed, VRFV2PlusWrapperLoadTestConsumerCaller: VRFV2PlusWrapperLoadTestConsumerCaller{contract: contract}, VRFV2PlusWrapperLoadTestConsumerTransactor: VRFV2PlusWrapperLoadTestConsumerTransactor{contract: contract}, VRFV2PlusWrapperLoadTestConsumerFilterer: VRFV2PlusWrapperLoadTestConsumerFilterer{contract: contract}}, nil } type VRFV2PlusWrapperLoadTestConsumer struct { diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index ce553dc8186..67090d16c6d 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -2,7 +2,7 @@ // golang packages, using abigen. package gethwrappers -// Make sure solidity compiler artifacts are up to date. Only output stdout on failure. +// Make sure solidity compiler artifacts are up-to-date. Only output stdout on failure. //go:generate ./generation/compile_contracts.sh //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/FluxAggregator.abi ../../contracts/solc/v0.6/FluxAggregator.bin FluxAggregator flux_aggregator_wrapper diff --git a/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go b/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go index 846ebb197b8..ad0ff294789 100644 --- a/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go +++ b/core/gethwrappers/llo-feeds/generated/errored_verifier/errored_verifier.go @@ -55,7 +55,7 @@ func DeployErroredVerifier(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &ErroredVerifier{ErroredVerifierCaller: ErroredVerifierCaller{contract: contract}, ErroredVerifierTransactor: ErroredVerifierTransactor{contract: contract}, ErroredVerifierFilterer: ErroredVerifierFilterer{contract: contract}}, nil + return address, tx, &ErroredVerifier{address: address, abi: *parsed, ErroredVerifierCaller: ErroredVerifierCaller{contract: contract}, ErroredVerifierTransactor: ErroredVerifierTransactor{contract: contract}, ErroredVerifierFilterer: ErroredVerifierFilterer{contract: contract}}, nil } type ErroredVerifier struct { diff --git a/core/gethwrappers/llo-feeds/generated/exposed_verifier/exposed_verifier.go b/core/gethwrappers/llo-feeds/generated/exposed_verifier/exposed_verifier.go index 2ca74b7cf3d..e27cb58d157 100644 --- a/core/gethwrappers/llo-feeds/generated/exposed_verifier/exposed_verifier.go +++ b/core/gethwrappers/llo-feeds/generated/exposed_verifier/exposed_verifier.go @@ -50,7 +50,7 @@ func DeployExposedVerifier(auth *bind.TransactOpts, backend bind.ContractBackend if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &ExposedVerifier{ExposedVerifierCaller: ExposedVerifierCaller{contract: contract}, ExposedVerifierTransactor: ExposedVerifierTransactor{contract: contract}, ExposedVerifierFilterer: ExposedVerifierFilterer{contract: contract}}, nil + return address, tx, &ExposedVerifier{address: address, abi: *parsed, ExposedVerifierCaller: ExposedVerifierCaller{contract: contract}, ExposedVerifierTransactor: ExposedVerifierTransactor{contract: contract}, ExposedVerifierFilterer: ExposedVerifierFilterer{contract: contract}}, nil } type ExposedVerifier struct { diff --git a/core/gethwrappers/llo-feeds/generated/fee_manager/fee_manager.go b/core/gethwrappers/llo-feeds/generated/fee_manager/fee_manager.go index 666edd3385d..742ec91bf97 100644 --- a/core/gethwrappers/llo-feeds/generated/fee_manager/fee_manager.go +++ b/core/gethwrappers/llo-feeds/generated/fee_manager/fee_manager.go @@ -67,7 +67,7 @@ func DeployFeeManager(auth *bind.TransactOpts, backend bind.ContractBackend, _li if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &FeeManager{FeeManagerCaller: FeeManagerCaller{contract: contract}, FeeManagerTransactor: FeeManagerTransactor{contract: contract}, FeeManagerFilterer: FeeManagerFilterer{contract: contract}}, nil + return address, tx, &FeeManager{address: address, abi: *parsed, FeeManagerCaller: FeeManagerCaller{contract: contract}, FeeManagerTransactor: FeeManagerTransactor{contract: contract}, FeeManagerFilterer: FeeManagerFilterer{contract: contract}}, nil } type FeeManager struct { diff --git a/core/gethwrappers/llo-feeds/generated/reward_manager/reward_manager.go b/core/gethwrappers/llo-feeds/generated/reward_manager/reward_manager.go index 224fb37ef63..c870a403016 100644 --- a/core/gethwrappers/llo-feeds/generated/reward_manager/reward_manager.go +++ b/core/gethwrappers/llo-feeds/generated/reward_manager/reward_manager.go @@ -62,7 +62,7 @@ func DeployRewardManager(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &RewardManager{RewardManagerCaller: RewardManagerCaller{contract: contract}, RewardManagerTransactor: RewardManagerTransactor{contract: contract}, RewardManagerFilterer: RewardManagerFilterer{contract: contract}}, nil + return address, tx, &RewardManager{address: address, abi: *parsed, RewardManagerCaller: RewardManagerCaller{contract: contract}, RewardManagerTransactor: RewardManagerTransactor{contract: contract}, RewardManagerFilterer: RewardManagerFilterer{contract: contract}}, nil } type RewardManager struct { diff --git a/core/gethwrappers/llo-feeds/generated/verifier/verifier.go b/core/gethwrappers/llo-feeds/generated/verifier/verifier.go index 993de18eb57..09bf78b23b5 100644 --- a/core/gethwrappers/llo-feeds/generated/verifier/verifier.go +++ b/core/gethwrappers/llo-feeds/generated/verifier/verifier.go @@ -57,7 +57,7 @@ func DeployVerifier(auth *bind.TransactOpts, backend bind.ContractBackend, verif if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Verifier{VerifierCaller: VerifierCaller{contract: contract}, VerifierTransactor: VerifierTransactor{contract: contract}, VerifierFilterer: VerifierFilterer{contract: contract}}, nil + return address, tx, &Verifier{address: address, abi: *parsed, VerifierCaller: VerifierCaller{contract: contract}, VerifierTransactor: VerifierTransactor{contract: contract}, VerifierFilterer: VerifierFilterer{contract: contract}}, nil } type Verifier struct { diff --git a/core/gethwrappers/llo-feeds/generated/verifier_proxy/verifier_proxy.go b/core/gethwrappers/llo-feeds/generated/verifier_proxy/verifier_proxy.go index 5ed70fef20a..fc7f10b6415 100644 --- a/core/gethwrappers/llo-feeds/generated/verifier_proxy/verifier_proxy.go +++ b/core/gethwrappers/llo-feeds/generated/verifier_proxy/verifier_proxy.go @@ -57,7 +57,7 @@ func DeployVerifierProxy(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &VerifierProxy{VerifierProxyCaller: VerifierProxyCaller{contract: contract}, VerifierProxyTransactor: VerifierProxyTransactor{contract: contract}, VerifierProxyFilterer: VerifierProxyFilterer{contract: contract}}, nil + return address, tx, &VerifierProxy{address: address, abi: *parsed, VerifierProxyCaller: VerifierProxyCaller{contract: contract}, VerifierProxyTransactor: VerifierProxyTransactor{contract: contract}, VerifierProxyFilterer: VerifierProxyFilterer{contract: contract}}, nil } type VerifierProxy struct { diff --git a/core/gethwrappers/shared/generated/burn_mint_erc677/burn_mint_erc677.go b/core/gethwrappers/shared/generated/burn_mint_erc677/burn_mint_erc677.go index f138b3b1f00..1d5b1c4ab17 100644 --- a/core/gethwrappers/shared/generated/burn_mint_erc677/burn_mint_erc677.go +++ b/core/gethwrappers/shared/generated/burn_mint_erc677/burn_mint_erc677.go @@ -52,7 +52,7 @@ func DeployBurnMintERC677(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &BurnMintERC677{BurnMintERC677Caller: BurnMintERC677Caller{contract: contract}, BurnMintERC677Transactor: BurnMintERC677Transactor{contract: contract}, BurnMintERC677Filterer: BurnMintERC677Filterer{contract: contract}}, nil + return address, tx, &BurnMintERC677{address: address, abi: *parsed, BurnMintERC677Caller: BurnMintERC677Caller{contract: contract}, BurnMintERC677Transactor: BurnMintERC677Transactor{contract: contract}, BurnMintERC677Filterer: BurnMintERC677Filterer{contract: contract}}, nil } type BurnMintERC677 struct { diff --git a/core/gethwrappers/shared/generated/erc20/erc20.go b/core/gethwrappers/shared/generated/erc20/erc20.go index f5b1d9b7bf2..9fd43134b6d 100644 --- a/core/gethwrappers/shared/generated/erc20/erc20.go +++ b/core/gethwrappers/shared/generated/erc20/erc20.go @@ -52,7 +52,7 @@ func DeployERC20(auth *bind.TransactOpts, backend bind.ContractBackend, name_ st if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &ERC20{ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil + return address, tx, &ERC20{address: address, abi: *parsed, ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil } type ERC20 struct { diff --git a/core/gethwrappers/shared/generated/link_token/link_token.go b/core/gethwrappers/shared/generated/link_token/link_token.go index 98de7de66a0..14676806266 100644 --- a/core/gethwrappers/shared/generated/link_token/link_token.go +++ b/core/gethwrappers/shared/generated/link_token/link_token.go @@ -52,7 +52,7 @@ func DeployLinkToken(auth *bind.TransactOpts, backend bind.ContractBackend) (com if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &LinkToken{LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil + return address, tx, &LinkToken{address: address, abi: *parsed, LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil } type LinkToken struct { diff --git a/core/gethwrappers/shared/generated/werc20_mock/werc20_mock.go b/core/gethwrappers/shared/generated/werc20_mock/werc20_mock.go index 3d8660c701d..c8ff3722755 100644 --- a/core/gethwrappers/shared/generated/werc20_mock/werc20_mock.go +++ b/core/gethwrappers/shared/generated/werc20_mock/werc20_mock.go @@ -52,7 +52,7 @@ func DeployWERC20Mock(auth *bind.TransactOpts, backend bind.ContractBackend) (co if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &WERC20Mock{WERC20MockCaller: WERC20MockCaller{contract: contract}, WERC20MockTransactor: WERC20MockTransactor{contract: contract}, WERC20MockFilterer: WERC20MockFilterer{contract: contract}}, nil + return address, tx, &WERC20Mock{address: address, abi: *parsed, WERC20MockCaller: WERC20MockCaller{contract: contract}, WERC20MockTransactor: WERC20MockTransactor{contract: contract}, WERC20MockFilterer: WERC20MockFilterer{contract: contract}}, nil } type WERC20Mock struct { diff --git a/core/gethwrappers/transmission/generated/entry_point/entry_point.go b/core/gethwrappers/transmission/generated/entry_point/entry_point.go index 9177186f4d6..09a3a94a7f4 100644 --- a/core/gethwrappers/transmission/generated/entry_point/entry_point.go +++ b/core/gethwrappers/transmission/generated/entry_point/entry_point.go @@ -99,7 +99,7 @@ func DeployEntryPoint(auth *bind.TransactOpts, backend bind.ContractBackend) (co if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &EntryPoint{EntryPointCaller: EntryPointCaller{contract: contract}, EntryPointTransactor: EntryPointTransactor{contract: contract}, EntryPointFilterer: EntryPointFilterer{contract: contract}}, nil + return address, tx, &EntryPoint{address: address, abi: *parsed, EntryPointCaller: EntryPointCaller{contract: contract}, EntryPointTransactor: EntryPointTransactor{contract: contract}, EntryPointFilterer: EntryPointFilterer{contract: contract}}, nil } type EntryPoint struct { diff --git a/core/gethwrappers/transmission/generated/greeter_wrapper/greeter_wrapper.go b/core/gethwrappers/transmission/generated/greeter_wrapper/greeter_wrapper.go index 857472422f7..9814c6a12c0 100644 --- a/core/gethwrappers/transmission/generated/greeter_wrapper/greeter_wrapper.go +++ b/core/gethwrappers/transmission/generated/greeter_wrapper/greeter_wrapper.go @@ -50,7 +50,7 @@ func DeployGreeter(auth *bind.TransactOpts, backend bind.ContractBackend) (commo if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Greeter{GreeterCaller: GreeterCaller{contract: contract}, GreeterTransactor: GreeterTransactor{contract: contract}, GreeterFilterer: GreeterFilterer{contract: contract}}, nil + return address, tx, &Greeter{address: address, abi: *parsed, GreeterCaller: GreeterCaller{contract: contract}, GreeterTransactor: GreeterTransactor{contract: contract}, GreeterFilterer: GreeterFilterer{contract: contract}}, nil } type Greeter struct { diff --git a/core/gethwrappers/transmission/generated/paymaster_wrapper/paymaster_wrapper.go b/core/gethwrappers/transmission/generated/paymaster_wrapper/paymaster_wrapper.go index 84283b28bb8..4910d2b4bb9 100644 --- a/core/gethwrappers/transmission/generated/paymaster_wrapper/paymaster_wrapper.go +++ b/core/gethwrappers/transmission/generated/paymaster_wrapper/paymaster_wrapper.go @@ -66,7 +66,7 @@ func DeployPaymaster(auth *bind.TransactOpts, backend bind.ContractBackend, link if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &Paymaster{PaymasterCaller: PaymasterCaller{contract: contract}, PaymasterTransactor: PaymasterTransactor{contract: contract}, PaymasterFilterer: PaymasterFilterer{contract: contract}}, nil + return address, tx, &Paymaster{address: address, abi: *parsed, PaymasterCaller: PaymasterCaller{contract: contract}, PaymasterTransactor: PaymasterTransactor{contract: contract}, PaymasterFilterer: PaymasterFilterer{contract: contract}}, nil } type Paymaster struct { diff --git a/core/gethwrappers/transmission/generated/sca_wrapper/sca_wrapper.go b/core/gethwrappers/transmission/generated/sca_wrapper/sca_wrapper.go index 36838fc1b05..55a3107710a 100644 --- a/core/gethwrappers/transmission/generated/sca_wrapper/sca_wrapper.go +++ b/core/gethwrappers/transmission/generated/sca_wrapper/sca_wrapper.go @@ -64,7 +64,7 @@ func DeploySCA(auth *bind.TransactOpts, backend bind.ContractBackend, owner comm if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &SCA{SCACaller: SCACaller{contract: contract}, SCATransactor: SCATransactor{contract: contract}, SCAFilterer: SCAFilterer{contract: contract}}, nil + return address, tx, &SCA{address: address, abi: *parsed, SCACaller: SCACaller{contract: contract}, SCATransactor: SCATransactor{contract: contract}, SCAFilterer: SCAFilterer{contract: contract}}, nil } type SCA struct { diff --git a/core/gethwrappers/transmission/generated/smart_contract_account_factory/smart_contract_account_factory.go b/core/gethwrappers/transmission/generated/smart_contract_account_factory/smart_contract_account_factory.go index 1e7761bffe3..0b4daf3fa89 100644 --- a/core/gethwrappers/transmission/generated/smart_contract_account_factory/smart_contract_account_factory.go +++ b/core/gethwrappers/transmission/generated/smart_contract_account_factory/smart_contract_account_factory.go @@ -52,7 +52,7 @@ func DeploySmartContractAccountFactory(auth *bind.TransactOpts, backend bind.Con if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &SmartContractAccountFactory{SmartContractAccountFactoryCaller: SmartContractAccountFactoryCaller{contract: contract}, SmartContractAccountFactoryTransactor: SmartContractAccountFactoryTransactor{contract: contract}, SmartContractAccountFactoryFilterer: SmartContractAccountFactoryFilterer{contract: contract}}, nil + return address, tx, &SmartContractAccountFactory{address: address, abi: *parsed, SmartContractAccountFactoryCaller: SmartContractAccountFactoryCaller{contract: contract}, SmartContractAccountFactoryTransactor: SmartContractAccountFactoryTransactor{contract: contract}, SmartContractAccountFactoryFilterer: SmartContractAccountFactoryFilterer{contract: contract}}, nil } type SmartContractAccountFactory struct { diff --git a/core/gethwrappers/transmission/generated/smart_contract_account_helper/smart_contract_account_helper.go b/core/gethwrappers/transmission/generated/smart_contract_account_helper/smart_contract_account_helper.go index add1d2ce51b..d951227c3a5 100644 --- a/core/gethwrappers/transmission/generated/smart_contract_account_helper/smart_contract_account_helper.go +++ b/core/gethwrappers/transmission/generated/smart_contract_account_helper/smart_contract_account_helper.go @@ -50,7 +50,7 @@ func DeploySmartContractAccountHelper(auth *bind.TransactOpts, backend bind.Cont if err != nil { return common.Address{}, nil, nil, err } - return address, tx, &SmartContractAccountHelper{SmartContractAccountHelperCaller: SmartContractAccountHelperCaller{contract: contract}, SmartContractAccountHelperTransactor: SmartContractAccountHelperTransactor{contract: contract}, SmartContractAccountHelperFilterer: SmartContractAccountHelperFilterer{contract: contract}}, nil + return address, tx, &SmartContractAccountHelper{address: address, abi: *parsed, SmartContractAccountHelperCaller: SmartContractAccountHelperCaller{contract: contract}, SmartContractAccountHelperTransactor: SmartContractAccountHelperTransactor{contract: contract}, SmartContractAccountHelperFilterer: SmartContractAccountHelperFilterer{contract: contract}}, nil } type SmartContractAccountHelper struct { From 78c658e04539db27c0c6322fa1e80744f1f7cfb0 Mon Sep 17 00:00:00 2001 From: Kashif Date: Thu, 26 Oct 2023 23:21:53 +0900 Subject: [PATCH 015/271] [FTM] Enable suggested gas fee on Fantom (#10963) * Enable suggested gas fee on Fantom * update docs * Add contract deployer * Add config change reasoning --------- Co-authored-by: davidcauchi --- .github/workflows/on-demand-ocr-soak-test.yml | 2 ++ core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml | 5 +++-- core/chains/evm/config/toml/defaults/Fantom_Testnet.toml | 3 +-- docs/CONFIG.md | 8 ++++---- integration-tests/contracts/contract_deployer.go | 6 ++++++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 7dc144264da..15f259e7e8d 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -24,6 +24,8 @@ on: - "POLYGON_MAINNET" - "LINEA_GOERLI" - "LINEA_MAINNET" + - "FANTOM_TESTNET" + - "FANTOM_MAINNET" fundingPrivateKey: description: Private funding key (Skip for Simulated) required: false diff --git a/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml b/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml index f7678c37eb0..7046642bb93 100644 --- a/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml @@ -9,8 +9,9 @@ RPCBlockQueryDelay = 2 Enabled = true [GasEstimator] -PriceDefault = '15 gwei' -PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +# Fantom network has been slow to include txs at times when using the BlockHistory estimator, and the recommendation is to use L2Suggested mode. +# There is work under way to improve L2Suggested mode's name so that its use on non-L2 chains will be less confusing in the future. +Mode = 'L2Suggested' [OCR2.Automation] GasLimit = 3800000 \ No newline at end of file diff --git a/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml b/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml index c7a6f72f9aa..0292ed5b743 100644 --- a/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml +++ b/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml @@ -7,8 +7,7 @@ NoNewHeadsThreshold = '0' RPCBlockQueryDelay = 2 [GasEstimator] -PriceDefault = '15 gwei' -PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +Mode = 'L2Suggested' [OCR2.Automation] GasLimit = 3800000 \ No newline at end of file diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 9582a940c32..1fc7d9b632f 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -2724,8 +2724,8 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'BlockHistory' -PriceDefault = '15 gwei' +Mode = 'L2Suggested' +PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' LimitDefault = 500000 @@ -3197,8 +3197,8 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'BlockHistory' -PriceDefault = '15 gwei' +Mode = 'L2Suggested' +PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' LimitDefault = 500000 diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index bdf63d19195..710422891c4 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -169,6 +169,8 @@ func NewContractDeployer(bcClient blockchain.EVMClient, logger zerolog.Logger) ( return &PolygonZkEvmContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil case *blockchain.LineaClient: return &LineaContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil + case *blockchain.FantomClient: + return &FantomContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil } return nil, errors.New("unknown blockchain client implementation for contract deployer, register blockchain client in NewContractDeployer") } @@ -232,6 +234,10 @@ type LineaContractDeployer struct { *EthereumContractDeployer } +type FantomContractDeployer struct { + *EthereumContractDeployer +} + // NewEthereumContractDeployer returns an instantiated instance of the ETH contract deployer func NewEthereumContractDeployer(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractDeployer { return &EthereumContractDeployer{ From 7fef66dc1fb82a865120601f38a1bd8f3c507b01 Mon Sep 17 00:00:00 2001 From: Lei Date: Thu, 26 Oct 2023 13:27:35 -0700 Subject: [PATCH 016/271] tune debugging script (#11070) --- core/scripts/chaincli/handler/debug.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index bdbce799264..7cf801d3326 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -70,7 +70,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { // verify contract is correct typeAndVersion, err := keeperRegistry21.TypeAndVersion(latestCallOpts) if err != nil { - failCheckConfig("failed to get typeAndVersion: are you sure you have the correct contract address?", err) + failCheckConfig("failed to get typeAndVersion: make sure your registry contract address and archive node are valid", err) } if typeAndVersion != expectedTypeAndVersion { failCheckConfig(fmt.Sprintf("invalid registry contract: this command can only debug %s, got: %s", expectedTypeAndVersion, typeAndVersion), nil) @@ -148,10 +148,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failCheckArgs("unable to parse log index", err) } - // find transaciton receipt + // find transaction receipt _, isPending, err := k.client.TransactionByHash(ctx, txHash) if err != nil { - log.Fatal("failed to fetch tx receipt", err) + log.Fatal("failed to get tx by hash", err) } if isPending { resolveIneligible(fmt.Sprintf("tx %s is still pending confirmation", txHash)) @@ -256,6 +256,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { message("using mercury lookup v0.3") } 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 { From e062e38b82aa1b3f150d463b96aedd44bab9f74c Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 26 Oct 2023 15:29:38 -0600 Subject: [PATCH 017/271] [TT-647] Bump Metrics For More Go Package + Test Name Preprocessing (#11095) --- .github/actions/golangci-lint/action.yml | 2 +- .../workflows/automation-benchmark-tests.yml | 2 +- .../workflows/automation-ondemand-tests.yml | 6 ++--- .github/workflows/build-publish-develop.yml | 2 +- .github/workflows/build-publish.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/changelog.yml | 2 +- .github/workflows/ci-core.yml | 6 ++--- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/delete-deployments.yml | 2 +- .github/workflows/dependency-check.yml | 2 +- .../goreleaser-build-publish-develop.yml | 2 +- .github/workflows/integration-chaos-tests.yml | 6 ++--- .../workflows/integration-tests-publish.yml | 2 +- .github/workflows/integration-tests.yml | 26 +++++++++---------- .github/workflows/lint-gh-workflows.yml | 2 +- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- .../on-demand-vrfv2plus-performance-test.yml | 2 +- .github/workflows/operator-ui-cd.yml | 2 +- .github/workflows/performance-tests.yml | 4 +-- .github/workflows/readme.yml | 2 +- .github/workflows/sigscanner.yml | 2 +- .github/workflows/solidity-foundry.yml | 2 +- .github/workflows/solidity-hardhat.yml | 8 +++--- .github/workflows/solidity.yml | 8 +++--- ...evelop-from-smartcontractkit-chainlink.yml | 2 +- 26 files changed, 51 insertions(+), 51 deletions(-) diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index c0aeb529c1e..c9ea735d1fe 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -69,7 +69,7 @@ runs: path: ${{ inputs.go-directory }}/golangci-lint-report.xml - name: Collect Metrics if: always() - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ inputs.gc-basic-auth }} hostname: ${{ inputs.gc-host }} diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 45491af0264..f23102f1ee6 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -128,7 +128,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index 20415b599ed..ac0e34e0834 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -46,7 +46,7 @@ jobs: - name: Collect Metrics if: inputs.chainlinkImage == '' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -91,7 +91,7 @@ jobs: steps: - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -205,7 +205,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/build-publish-develop.yml b/.github/workflows/build-publish-develop.yml index 54ccaad5810..076fdf817df 100644 --- a/.github/workflows/build-publish-develop.yml +++ b/.github/workflows/build-publish-develop.yml @@ -56,7 +56,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 5db70576b37..4d5a42a369f 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -50,7 +50,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7cdf5e46b9e..0f9a8ea8b35 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index a8e6e266a96..c7ca0b880c2 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -31,7 +31,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 4037dc0cd90..40535855906 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -117,7 +117,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -215,7 +215,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -253,7 +253,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 822bf259f92..8bc066f408c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/delete-deployments.yml b/.github/workflows/delete-deployments.yml index 3ec5fb35c97..dc3c17852b1 100644 --- a/.github/workflows/delete-deployments.yml +++ b/.github/workflows/delete-deployments.yml @@ -24,7 +24,7 @@ jobs: - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index 42729a8cf1c..dbf08895757 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -47,7 +47,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/goreleaser-build-publish-develop.yml b/.github/workflows/goreleaser-build-publish-develop.yml index 1edfdedd706..514067fd85d 100644 --- a/.github/workflows/goreleaser-build-publish-develop.yml +++ b/.github/workflows/goreleaser-build-publish-develop.yml @@ -39,7 +39,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index 503e5ec58a4..648d5f9daa3 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -53,7 +53,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -79,7 +79,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -99,7 +99,7 @@ jobs: steps: - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index 06f7bb1b817..60f67f03574 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index edd7755e8b9..98e59ce8d49 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -45,7 +45,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -74,7 +74,7 @@ jobs: - name: Collect Metrics if: needs.changes.outputs.src == 'true' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -122,7 +122,7 @@ jobs: - name: Collect Metrics if: needs.changes.outputs.src == 'true' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -228,7 +228,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -440,7 +440,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -468,7 +468,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -496,7 +496,7 @@ jobs: - name: Collect Metrics if: ${{ github.event_name == 'pull_request' }} id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -591,7 +591,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -697,7 +697,7 @@ jobs: - name: Collect Metrics if: needs.changes.outputs.src == 'true' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -738,7 +738,7 @@ jobs: - name: Collect Metrics if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -796,7 +796,7 @@ jobs: - name: Collect Metrics if: needs.changes.outputs.src == 'true' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -874,7 +874,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -946,7 +946,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/lint-gh-workflows.yml b/.github/workflows/lint-gh-workflows.yml index 8facdc038c1..f1a3cc20803 100644 --- a/.github/workflows/lint-gh-workflows.yml +++ b/.github/workflows/lint-gh-workflows.yml @@ -13,7 +13,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 15f259e7e8d..1fb79d8ccd4 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -89,7 +89,7 @@ jobs: steps: - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 41b6618ba6b..deb977e43fc 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -86,7 +86,7 @@ jobs: steps: - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/operator-ui-cd.yml b/.github/workflows/operator-ui-cd.yml index 36a9d8b715a..54f423e6dc3 100644 --- a/.github/workflows/operator-ui-cd.yml +++ b/.github/workflows/operator-ui-cd.yml @@ -50,7 +50,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 43ed80cb3ff..277940dc2d2 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -42,7 +42,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -79,7 +79,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/readme.yml b/.github/workflows/readme.yml index e847216eb07..585bed41c6a 100644 --- a/.github/workflows/readme.yml +++ b/.github/workflows/readme.yml @@ -31,7 +31,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/sigscanner.yml b/.github/workflows/sigscanner.yml index 285a63cacbe..de34766d2c8 100644 --- a/.github/workflows/sigscanner.yml +++ b/.github/workflows/sigscanner.yml @@ -26,7 +26,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index f6c515b5b86..19c879b09ef 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -86,7 +86,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/solidity-hardhat.yml b/.github/workflows/solidity-hardhat.yml index 6e7de2eba19..129f37c0de6 100644 --- a/.github/workflows/solidity-hardhat.yml +++ b/.github/workflows/solidity-hardhat.yml @@ -48,7 +48,7 @@ jobs: config: ./contracts/ci.json - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -90,7 +90,7 @@ jobs: path: ./contracts/coverage-${{ matrix.split.idx }}.json - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -145,7 +145,7 @@ jobs: run: pnpm test -- $SPLIT - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -170,7 +170,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index f46b13191d2..782dc93a0f5 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -40,7 +40,7 @@ jobs: run: pnpm prepublishOnly - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -84,7 +84,7 @@ jobs: run: gh pr comment -b 'Go solidity wrappers are out-of-date, regenerate them via the `make wrappers-all` command' - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -110,7 +110,7 @@ jobs: run: pnpm solhint - name: Collect Metrics id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} @@ -136,7 +136,7 @@ jobs: - name: Collect Metrics if: needs.changes.outputs.changes == 'true' id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} diff --git a/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml b/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml index 7fe9ffd526b..d27acceca68 100644 --- a/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml +++ b/.github/workflows/sync-develop-from-smartcontractkit-chainlink.yml @@ -30,7 +30,7 @@ jobs: - name: Collect Metrics if: always() id: collect-gha-metrics - uses: smartcontractkit/push-gha-metrics-action@f4d2fcbe12e9e44921e0171d24085ab7d2a30bc9 # v2.0.1 + uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} From eabdba0c76de266554cc33db3cbdb2e123afac4b Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 26 Oct 2023 16:56:02 -0500 Subject: [PATCH 018/271] core/chains/cosmos/types: rm unused (#11099) --- core/chains/cosmos/types/types.go | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 core/chains/cosmos/types/types.go diff --git a/core/chains/cosmos/types/types.go b/core/chains/cosmos/types/types.go deleted file mode 100644 index 69b086a9706..00000000000 --- a/core/chains/cosmos/types/types.go +++ /dev/null @@ -1,8 +0,0 @@ -package types - -// NewNode defines a new node to create. -type NewNode struct { - Name string `json:"name"` - CosmosChainID string `json:"cosmosChainId"` - TendermintURL string `json:"tendermintURL" db:"tendermint_url"` -} From 01200f8ef8d3fab71d130dd8cd80855bf27bc5bb Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Fri, 27 Oct 2023 13:01:05 +0300 Subject: [PATCH 019/271] =?UTF-8?q?VRF-678:create=20sub,=20fund=20it,=20de?= =?UTF-8?q?ploy=20consumer=20and=20add=20to=20sub=20when=20usin=E2=80=A6?= =?UTF-8?q?=20(#11086)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-678:create sub, fund it, deploy consumer and add to sub when using VRFV2 Plus WASP load test with existing env * VRF-678: cleanup * VRF-678: fixing smoke test; adding additional logging --- .../vrfv2plus/vrfv2plus_config/config.go | 1 + .../actions/vrfv2plus/vrfv2plus_steps.go | 96 ++++++++++++------- .../contracts/contract_vrf_models.go | 4 +- .../contracts/ethereum_vrfv2plus_contracts.go | 12 +-- integration-tests/load/vrfv2plus/config.go | 13 ++- integration-tests/load/vrfv2plus/config.toml | 18 ++-- .../load/vrfv2plus/vrfv2plus_test.go | 35 +++++-- integration-tests/smoke/vrfv2plus_test.go | 4 +- 8 files changed, 119 insertions(+), 64 deletions(-) diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go index caee353294f..10d4f19c244 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go @@ -39,6 +39,7 @@ type VRFV2PlusConfig struct { UseExistingEnv bool `envconfig:"USE_EXISTING_ENV" default:"false"` // Whether to use an existing environment or create a new one CoordinatorAddress string `envconfig:"COORDINATOR_ADDRESS" default:""` // Coordinator address ConsumerAddress string `envconfig:"CONSUMER_ADDRESS" default:""` // Consumer address + LinkAddress string `envconfig:"LINK_ADDRESS" default:""` // Link address SubID string `envconfig:"SUB_ID" default:""` // Subscription ID KeyHash string `envconfig:"KEY_HASH" default:""` } diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 3bfa5d4f416..46f0ca58e69 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -241,13 +241,16 @@ func SetupVRFV2_5Environment( mockNativeLINKFeed contracts.MockETHLINKFeed, numberOfConsumers int, numberOfSubToCreate int, + l zerolog.Logger, ) (*VRFV2_5Contracts, []*big.Int, *VRFV2PlusData, error) { - + l.Info().Msg("Starting VRFV2 Plus environment setup") + l.Info().Msg("Deploying VRFV2 Plus contracts") vrfv2_5Contracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient, numberOfConsumers) if err != nil { return nil, nil, nil, errors.Wrap(err, ErrDeployVRFV2_5Contracts) } + l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") err = vrfv2_5Contracts.Coordinator.SetConfig( vrfv2PlusConfig.MinimumConfirmations, vrfv2PlusConfig.MaxGasLimitCoordinatorConfig, @@ -263,6 +266,7 @@ func SetupVRFV2_5Environment( return nil, nil, nil, errors.Wrap(err, ErrSetVRFCoordinatorConfig) } + l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Link and ETH/LINK feed") err = vrfv2_5Contracts.Coordinator.SetLINKAndLINKNativeFeed(linkToken.Address(), mockNativeLINKFeed.Address()) if err != nil { return nil, nil, nil, errors.Wrap(err, ErrSetLinkNativeLinkFeed) @@ -271,32 +275,12 @@ func SetupVRFV2_5Environment( if err != nil { return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) } - - subIDs, err := CreateSubsAndFund(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts, numberOfSubToCreate) - if err != nil { - return nil, nil, nil, err - } - - subToConsumersMap := map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer{} - - //each subscription will have the same consumers - for _, subID := range subIDs { - subToConsumersMap[subID] = vrfv2_5Contracts.LoadTestConsumers - } - - err = AddConsumersToSubs( - subToConsumersMap, - vrfv2_5Contracts.Coordinator, - ) + l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Creating and funding subscriptions, adding consumers") + subIDs, err := CreateFundSubsAndAddConsumers(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, numberOfSubToCreate) if err != nil { return nil, nil, nil, err } - - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) - } - + l.Info().Str("Node URL", env.ClCluster.NodeAPIs()[0].URL()).Msg("Creating VRF Key on the Node") vrfKey, err := env.ClCluster.NodeAPIs()[0].MustCreateVRFKey() if err != nil { return nil, nil, nil, errors.Wrap(err, ErrCreatingVRFv2PlusKey) @@ -307,6 +291,7 @@ func SetupVRFV2_5Environment( if err != nil { return nil, nil, nil, errors.Wrap(err, ErrNodePrimaryKey) } + l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Registering Proving Key") provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, nativeTokenPrimaryKeyAddress, vrfv2_5Contracts.Coordinator) if err != nil { return nil, nil, nil, errors.Wrap(err, ErrRegisteringProvingKey) @@ -318,6 +303,7 @@ func SetupVRFV2_5Environment( chainID := env.EVMClient.GetChainID() + l.Info().Msg("Creating VRFV2 Plus Job") job, err := CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], vrfv2_5Contracts.Coordinator.Address(), @@ -340,6 +326,7 @@ func SetupVRFV2_5Environment( nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, node.WithVRFv2EVMEstimator(addr), ) + l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) if err != nil { return nil, nil, nil, errors.Wrap(err, ErrRestartCLNode) @@ -358,17 +345,52 @@ func SetupVRFV2_5Environment( chainID, } + l.Info().Msg("VRFV2 Plus environment setup is finished") return vrfv2_5Contracts, subIDs, &data, nil } +func CreateFundSubsAndAddConsumers( + env *test_env.CLClusterTestEnv, + vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2_5, + consumers []contracts.VRFv2PlusLoadTestConsumer, + numberOfSubToCreate int, +) ([]*big.Int, error) { + subIDs, err := CreateSubsAndFund(env, vrfv2PlusConfig, linkToken, coordinator, numberOfSubToCreate) + if err != nil { + return nil, err + } + subToConsumersMap := map[*big.Int][]contracts.VRFv2PlusLoadTestConsumer{} + + //each subscription will have the same consumers + for _, subID := range subIDs { + subToConsumersMap[subID] = consumers + } + + err = AddConsumersToSubs( + subToConsumersMap, + coordinator, + ) + if err != nil { + return nil, err + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, errors.Wrap(err, ErrWaitTXsComplete) + } + return subIDs, nil +} + func CreateSubsAndFund( env *test_env.CLClusterTestEnv, vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, linkToken contracts.LinkToken, - vrfv2_5Contracts *VRFV2_5Contracts, + coordinator contracts.VRFCoordinatorV2_5, subAmountToCreate int, ) ([]*big.Int, error) { - subs, err := CreateSubs(env, vrfv2_5Contracts.Coordinator, subAmountToCreate) + subs, err := CreateSubs(env, coordinator, subAmountToCreate) if err != nil { return nil, err } @@ -376,7 +398,7 @@ func CreateSubsAndFund( if err != nil { return nil, errors.Wrap(err, ErrWaitTXsComplete) } - err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts.Coordinator, subs) + err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, subs) if err != nil { return nil, err } @@ -503,21 +525,27 @@ func SetupVRFV2PlusWrapperEnvironment( return wrapperContracts, wrapperSubID, nil } func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { - err := coordinator.CreateSubscription() + tx, err := coordinator.CreateSubscription() if err != nil { return nil, errors.Wrap(err, ErrCreateVRFSubscription) } - - sub, err := coordinator.WaitForSubscriptionCreatedEvent(time.Second * 10) + err = env.EVMClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrFindSubID) + return nil, errors.Wrap(err, ErrWaitTXsComplete) } - err = env.EVMClient.WaitForEvents() + receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + + //SubscriptionsCreated Log should be emitted with the subscription ID + subID := receipt.Logs[0].Topics[1].Big() + + //verify that the subscription was created + _, err = coordinator.FindSubscriptionID(subID) if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, errors.Wrap(err, ErrFindSubID) } - return sub.SubId, nil + + return subID, nil } func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 656cabb92e9..f0f57f58e75 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -73,7 +73,7 @@ type VRFCoordinatorV2_5 interface { publicProvingKey [2]*big.Int, ) error HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) - CreateSubscription() error + CreateSubscription() (*types.Transaction, error) GetActiveSubscriptionIds(ctx context.Context, startIndex *big.Int, maxCount *big.Int) ([]*big.Int, error) Migrate(subId *big.Int, coordinatorAddress string) error RegisterMigratableCoordinator(migratableCoordinatorAddress string) error @@ -83,7 +83,7 @@ type VRFCoordinatorV2_5 interface { GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2_5.GetSubscription, error) GetNativeTokenTotalBalance(ctx context.Context) (*big.Int, error) GetLinkTotalBalance(ctx context.Context) (*big.Int, error) - FindSubscriptionID() (*big.Int, error) + FindSubscriptionID(subID *big.Int) (*big.Int, error) WaitForSubscriptionCreatedEvent(timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCreated, error) WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []*big.Int, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, error) diff --git a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go index df7cca54b2e..1488f97131a 100644 --- a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go @@ -182,16 +182,16 @@ func (v *EthereumVRFCoordinatorV2_5) RegisterProvingKey( return v.client.ProcessTransaction(tx) } -func (v *EthereumVRFCoordinatorV2_5) CreateSubscription() error { +func (v *EthereumVRFCoordinatorV2_5) CreateSubscription() (*types.Transaction, error) { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { - return err + return nil, err } tx, err := v.coordinator.CreateSubscription(opts) if err != nil { - return err + return nil, err } - return v.client.ProcessTransaction(tx) + return tx, v.client.ProcessTransaction(tx) } func (v *EthereumVRFCoordinatorV2_5) Migrate(subId *big.Int, coordinatorAddress string) error { @@ -250,11 +250,11 @@ func (v *EthereumVRFCoordinatorV2_5) FundSubscriptionWithNative(subId *big.Int, return v.client.ProcessTransaction(tx) } -func (v *EthereumVRFCoordinatorV2_5) FindSubscriptionID() (*big.Int, error) { +func (v *EthereumVRFCoordinatorV2_5) FindSubscriptionID(subID *big.Int) (*big.Int, error) { owner := v.client.GetDefaultWallet().Address() subscriptionIterator, err := v.coordinator.FilterSubscriptionCreated( nil, - nil, + []*big.Int{subID}, ) if err != nil { return nil, err diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index 329e4abf135..50003c82865 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -36,8 +36,11 @@ type PerformanceConfig struct { type ExistingEnvConfig struct { CoordinatorAddress string `toml:"coordinator_address"` ConsumerAddress string `toml:"consumer_address"` + LinkAddress string `toml:"link_address"` SubID string `toml:"sub_id"` KeyHash string `toml:"key_hash"` + SubFunding + CreateFundSubsAndAddConsumers bool `toml:"create_fund_subs_and_add_consumers"` } type NewEnvConfig struct { @@ -49,9 +52,13 @@ type Common struct { } type Funding struct { - NodeFunds float64 `toml:"node_funds"` - SubFundsLink int64 `toml:"sub_funds_link"` - SubFundsNative int64 `toml:"sub_funds_native"` + NodeFunds float64 `toml:"node_funds"` + SubFunding +} + +type SubFunding struct { + SubFundsLink int64 `toml:"sub_funds_link"` + SubFundsNative int64 `toml:"sub_funds_native"` } type Soak struct { diff --git a/integration-tests/load/vrfv2plus/config.toml b/integration-tests/load/vrfv2plus/config.toml index 31a0bf56652..05e22bd51e8 100644 --- a/integration-tests/load/vrfv2plus/config.toml +++ b/integration-tests/load/vrfv2plus/config.toml @@ -3,17 +3,19 @@ minimum_confirmations = 3 [NewEnvConfig] -sub_funds_link = 1000 -sub_funds_native = 1000 +sub_funds_link = 1 +sub_funds_native = 1 node_funds = 10 - [ExistingEnvConfig] -coordinator_address = "0x4931Ce2e341398c8eD8A5D0F6ADb920476D6DaBb" +coordinator_address = "0x27b61f155F772b291D1d9B478BeAd37B2Ae447b0" consumer_address = "0x087F232165D9bA1A602f148025e5D0666953F64a" sub_id = "52116875585187328970776211988181422347535732407068188096422095950800466618218" -key_hash = "0x4c422465ed6a06cfc84575a5437fef7b9dc6263133f648afbe6ae7b2c694d3b3" - +key_hash = "0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae" +create_fund_subs_and_add_consumers = true +link_address = "0x779877A7B0D9E8603169DdbD7836e478b4624789" +sub_funds_link = 3 +sub_funds_native = 1 # 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds [Soak] @@ -29,7 +31,7 @@ rate_limit_unit_duration = "3s" rps = 1 randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 10 +number_of_sub_to_create = 5 # approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx [Stress] @@ -37,7 +39,7 @@ rate_limit_unit_duration = "1s" rps = 3 randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 20 +number_of_sub_to_create = 5 # approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds [Spike] diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index a3cb4d4b7f5..e619cf78fd3 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -74,6 +74,9 @@ func TestVRFV2PlusPerformance(t *testing.T) { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented vrfv2PlusConfig.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress vrfv2PlusConfig.ConsumerAddress = cfg.ExistingEnvConfig.ConsumerAddress + vrfv2PlusConfig.LinkAddress = cfg.ExistingEnvConfig.LinkAddress + vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.ExistingEnvConfig.SubFunding.SubFundsLink + vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.ExistingEnvConfig.SubFunding.SubFundsNative vrfv2PlusConfig.SubID = cfg.ExistingEnvConfig.SubID vrfv2PlusConfig.KeyHash = cfg.ExistingEnvConfig.KeyHash @@ -90,17 +93,29 @@ func TestVRFV2PlusPerformance(t *testing.T) { coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2_5(vrfv2PlusConfig.CoordinatorAddress) require.NoError(t, err) - consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(vrfv2PlusConfig.ConsumerAddress) - require.NoError(t, err) + var consumers []contracts.VRFv2PlusLoadTestConsumer + if cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { + linkToken, err := env.ContractLoader.LoadLINKToken(vrfv2PlusConfig.LinkAddress) + require.NoError(t, err) + consumers, err = vrfv2plus.DeployVRFV2PlusConsumers(env.ContractDeployer, coordinator, 1) + require.NoError(t, err) + subIDs, err = vrfv2plus.CreateFundSubsAndAddConsumers(env, &vrfv2PlusConfig, linkToken, coordinator, consumers, vrfv2PlusConfig.NumberOfSubToCreate) + require.NoError(t, err) + } else { + consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(vrfv2PlusConfig.ConsumerAddress) + require.NoError(t, err) + consumers = append(consumers, consumer) + var ok bool + subID, ok := new(big.Int).SetString(vrfv2PlusConfig.SubID, 10) + require.True(t, ok) + subIDs = append(subIDs, subID) + } vrfv2PlusContracts = &vrfv2plus.VRFV2_5Contracts{ Coordinator: coordinator, - LoadTestConsumers: []contracts.VRFv2PlusLoadTestConsumer{consumer}, + LoadTestConsumers: consumers, BHS: nil, } - var ok bool - subID, ok := new(big.Int).SetString(vrfv2PlusConfig.SubID, 10) - require.True(t, ok) vrfv2PlusData = &vrfv2plus.VRFV2PlusData{ VRFV2PlusKeyData: vrfv2plus.VRFV2PlusKeyData{ @@ -112,7 +127,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { PrimaryEthAddress: "", ChainID: nil, } - subIDs = append(subIDs, subID) + } else { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented vrfv2PlusConfig.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeFunds @@ -150,6 +165,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { mockETHLinkFeed, 1, vrfv2PlusConfig.NumberOfSubToCreate, + l, ) require.NoError(t, err, "error setting up VRF v2_5 env") } @@ -198,7 +214,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { var wg sync.WaitGroup wg.Add(1) //todo - timeout should be configurable depending on the perf test type - requestCount, fulfilmentCount, err := vrfv2plus.WaitForRequestCountEqualToFulfilmentCount(consumer, 30*time.Second, &wg) + requestCount, fulfilmentCount, err := vrfv2plus.WaitForRequestCountEqualToFulfilmentCount(consumer, 2*time.Minute, &wg) require.NoError(t, err) wg.Wait() @@ -212,7 +228,8 @@ func TestVRFV2PlusPerformance(t *testing.T) { func teardown( t *testing.T, consumer contracts.VRFv2PlusLoadTestConsumer, - lc *wasp.LokiClient, updatedLabels map[string]string, + lc *wasp.LokiClient, + updatedLabels map[string]string, testReporter *testreporters.VRFV2PlusTestReporter, testType string, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index c2cc0878b66..408e5a95ed3 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -46,7 +46,7 @@ func TestVRFv2Plus(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkToken, mockETHLinkFeed, 1, 1) + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkToken, mockETHLinkFeed, 1, 1, l) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] @@ -271,7 +271,7 @@ func TestVRFv2PlusMigration(t *testing.T) { linkAddress, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, 2, 1) + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, 2, 1, l) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] From d8414631ddf1464eb4b06056cbfa21cbf991bcfa Mon Sep 17 00:00:00 2001 From: "app-token-issuer-infra-releng[bot]" <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:56:54 +0300 Subject: [PATCH 020/271] Update Operator UI from v0.8.0-06f745d to v0.8.0-e10948a (#11094) * Update Operator UI from v0.8.0-06f745d to v0.8.0-e10948a * Sig scanner check --------- Co-authored-by: github-merge-queue[bot] Co-authored-by: george-dorin --- operator_ui/TAG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator_ui/TAG b/operator_ui/TAG index 023fd8732cd..e08ca072670 100644 --- a/operator_ui/TAG +++ b/operator_ui/TAG @@ -1 +1 @@ -v0.8.0-06f745d +v0.8.0-e10948a From e6118da8b89033d857efa1aef6d7f4ba9bdd9d91 Mon Sep 17 00:00:00 2001 From: george-dorin <120329946+george-dorin@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:09:24 +0300 Subject: [PATCH 021/271] Add string equivalent for observation price, bid, ask (#11077) * Add string equivalent for observation price, bid, ask * Update logging --- core/services/ocrcommon/telemetry.go | 64 ++-- core/services/ocrcommon/telemetry_test.go | 57 ++-- .../telem/telem_enhanced_ea_mercury.pb.go | 280 ++++++++++-------- .../telem/telem_enhanced_ea_mercury.proto | 3 + 4 files changed, 227 insertions(+), 177 deletions(-) diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 54a5002093d..5277143c8b3 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" @@ -230,7 +231,7 @@ func (e *EnhancedTelemetryService[T]) collectAndSend(trrs *pipeline.TaskRunResul } eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) if err != nil { - e.lggr.Warnf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()) + e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", err) } value := e.getParsedValue(trrs, trr) @@ -278,39 +279,42 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(obs relaym bridgeRawResponse, ok := trr.Result.Value.(string) if !ok { - e.lggr.Warnf("cannot get bridge response from bridge task, job %d, id %s", e.job.ID, trr.Task.DotID()) + e.lggr.Warnf("cannot get bridge response from bridge task, job %d, id %s, expected string got %T", e.job.ID, trr.Task.DotID(), trr.Result.Value) continue } eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) if err != nil { - e.lggr.Warnf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()) + e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", err) } assetSymbol := e.getAssetSymbolFromRequestData(bridgeTask.RequestData) benchmarkPrice, bidPrice, askPrice := e.getPricesFromResults(trr, &trrs) t := &telem.EnhancedEAMercury{ - DataSource: eaTelem.DataSource, - DpBenchmarkPrice: benchmarkPrice, - DpBid: bidPrice, - DpAsk: askPrice, - CurrentBlockNumber: obsBlockNum, - CurrentBlockHash: common.BytesToHash(obsBlockHash).String(), - CurrentBlockTimestamp: obsBlockTimestamp, - BridgeTaskRunStartedTimestamp: trr.CreatedAt.UnixMilli(), - BridgeTaskRunEndedTimestamp: trr.FinishedAt.Time.UnixMilli(), - ProviderRequestedTimestamp: eaTelem.ProviderRequestedTimestamp, - ProviderReceivedTimestamp: eaTelem.ProviderReceivedTimestamp, - ProviderDataStreamEstablished: eaTelem.ProviderDataStreamEstablished, - ProviderIndicatedTime: eaTelem.ProviderIndicatedTime, - Feed: e.job.OCR2OracleSpec.FeedID.Hex(), - ObservationBenchmarkPrice: obsBenchmarkPrice, - ObservationBid: obsBid, - ObservationAsk: obsAsk, - ConfigDigest: repts.ConfigDigest.Hex(), - Round: int64(repts.Round), - Epoch: int64(repts.Epoch), - AssetSymbol: assetSymbol, + DataSource: eaTelem.DataSource, + DpBenchmarkPrice: benchmarkPrice, + DpBid: bidPrice, + DpAsk: askPrice, + CurrentBlockNumber: obsBlockNum, + CurrentBlockHash: common.BytesToHash(obsBlockHash).String(), + CurrentBlockTimestamp: obsBlockTimestamp, + BridgeTaskRunStartedTimestamp: trr.CreatedAt.UnixMilli(), + BridgeTaskRunEndedTimestamp: trr.FinishedAt.Time.UnixMilli(), + ProviderRequestedTimestamp: eaTelem.ProviderRequestedTimestamp, + ProviderReceivedTimestamp: eaTelem.ProviderReceivedTimestamp, + ProviderDataStreamEstablished: eaTelem.ProviderDataStreamEstablished, + ProviderIndicatedTime: eaTelem.ProviderIndicatedTime, + Feed: e.job.OCR2OracleSpec.FeedID.Hex(), + ObservationBenchmarkPrice: obsBenchmarkPrice.Int64(), //Deprecated: observation value will not fit in int64, we will use the string equivalent field ObservationBenchmarkPriceString + ObservationBid: obsBid.Int64(), //Deprecated: observation value will not fit in int64, we will use the string equivalent field ObservationBidString + ObservationAsk: obsAsk.Int64(), //Deprecated: observation value will not fit in int64, we will use the string equivalent field ObservationAskString + ConfigDigest: repts.ConfigDigest.Hex(), + Round: int64(repts.Round), + Epoch: int64(repts.Epoch), + AssetSymbol: assetSymbol, + ObservationBenchmarkPriceString: obsBenchmarkPrice.String(), + ObservationBidString: obsBid.String(), + ObservationAskString: obsAsk.String(), } bytes, err := proto.Marshal(t) @@ -408,17 +412,19 @@ func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.Ta } // getFinalValues runs a parse on the pipeline.TaskRunResults and returns the values -func (e *EnhancedTelemetryService[T]) getFinalValues(obs relaymercuryv1.Observation) (int64, int64, int64, int64, []byte, uint64) { - var benchmarkPrice, bid, ask int64 +func (e *EnhancedTelemetryService[T]) getFinalValues(obs relaymercuryv1.Observation) (*big.Int, *big.Int, *big.Int, int64, []byte, uint64) { + benchmarkPrice := big.NewInt(0) + bid := big.NewInt(0) + ask := big.NewInt(0) if obs.BenchmarkPrice.Val != nil { - benchmarkPrice = obs.BenchmarkPrice.Val.Int64() + benchmarkPrice = obs.BenchmarkPrice.Val } if obs.Bid.Val != nil { - bid = obs.Bid.Val.Int64() + bid = obs.Bid.Val } if obs.Ask.Val != nil { - ask = obs.Ask.Val.Int64() + ask = obs.Ask.Val } return benchmarkPrice, bid, ask, obs.CurrentBlockNum.Val, obs.CurrentBlockHash.Val, obs.CurrentBlockTimestamp.Val diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index 88fb7de3e39..5d1494a038a 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -433,17 +433,17 @@ func TestGetFinalValues(t *testing.T) { } benchmarkPrice, bid, ask, blockNr, blockHash, blockTimestamp := e.getFinalValues(o) - require.Equal(t, benchmarkPrice, int64(111111)) - require.Equal(t, bid, int64(222222)) - require.Equal(t, ask, int64(333333)) + require.Equal(t, benchmarkPrice, big.NewInt(111111)) + require.Equal(t, bid, big.NewInt(222222)) + require.Equal(t, ask, big.NewInt(333333)) require.Equal(t, blockNr, int64(123456789)) require.Equal(t, blockHash, common.HexToHash("0x123321").Bytes()) require.Equal(t, blockTimestamp, uint64(987654321)) benchmarkPrice, bid, ask, blockNr, blockHash, blockTimestamp = e.getFinalValues(mercuryv1.Observation{}) - require.Equal(t, benchmarkPrice, int64(0)) - require.Equal(t, bid, int64(0)) - require.Equal(t, ask, int64(0)) + require.Equal(t, benchmarkPrice, big.NewInt(0)) + require.Equal(t, bid, big.NewInt(0)) + require.Equal(t, ask, big.NewInt(0)) require.Equal(t, blockNr, int64(0)) require.Nil(t, blockHash) require.Equal(t, blockTimestamp, uint64(0)) @@ -598,27 +598,30 @@ func TestCollectMercuryEnhancedTelemetry(t *testing.T) { } expectedTelemetry := telem.EnhancedEAMercury{ - DataSource: "data-source-name", - DpBenchmarkPrice: 123456.123456, - DpBid: 1234567.1234567, - DpAsk: 321123, - CurrentBlockNumber: 123456789, - CurrentBlockHash: common.HexToHash("0x123321").String(), - CurrentBlockTimestamp: 987654321, - BridgeTaskRunStartedTimestamp: trrsMercury[0].CreatedAt.UnixMilli(), - BridgeTaskRunEndedTimestamp: trrsMercury[0].FinishedAt.Time.UnixMilli(), - ProviderRequestedTimestamp: 92233720368547760, - ProviderReceivedTimestamp: -92233720368547760, - ProviderDataStreamEstablished: 1, - ProviderIndicatedTime: -123456789, - Feed: common.HexToHash("0x111").String(), - ObservationBenchmarkPrice: 111111, - ObservationBid: 222222, - ObservationAsk: 333333, - ConfigDigest: "0200000000000000000000000000000000000000000000000000000000000000", - Round: 22, - Epoch: 11, - AssetSymbol: "USD/LINK", + DataSource: "data-source-name", + DpBenchmarkPrice: 123456.123456, + DpBid: 1234567.1234567, + DpAsk: 321123, + CurrentBlockNumber: 123456789, + CurrentBlockHash: common.HexToHash("0x123321").String(), + CurrentBlockTimestamp: 987654321, + BridgeTaskRunStartedTimestamp: trrsMercury[0].CreatedAt.UnixMilli(), + BridgeTaskRunEndedTimestamp: trrsMercury[0].FinishedAt.Time.UnixMilli(), + ProviderRequestedTimestamp: 92233720368547760, + ProviderReceivedTimestamp: -92233720368547760, + ProviderDataStreamEstablished: 1, + ProviderIndicatedTime: -123456789, + Feed: common.HexToHash("0x111").String(), + ObservationBenchmarkPrice: 111111, + ObservationBid: 222222, + ObservationAsk: 333333, + ConfigDigest: "0200000000000000000000000000000000000000000000000000000000000000", + Round: 22, + Epoch: 11, + AssetSymbol: "USD/LINK", + ObservationBenchmarkPriceString: "111111", + ObservationBidString: "222222", + ObservationAskString: "333333", } expectedMessage, _ := proto.Marshal(&expectedTelemetry) diff --git a/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go b/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go index 571a725b883..32c3061cc44 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go +++ b/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go @@ -1,8 +1,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v3.21.12 -// source: telem_enhanced_ea_mercury.proto +// protoc-gen-go v1.31.0 +// protoc v4.24.3 +// source: core/services/synchronization/telem/telem_enhanced_ea_mercury.proto package telem @@ -25,33 +25,36 @@ type EnhancedEAMercury struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DataSource string `protobuf:"bytes,1,opt,name=data_source,json=dataSource,proto3" json:"data_source,omitempty"` - DpBenchmarkPrice float64 `protobuf:"fixed64,2,opt,name=dp_benchmark_price,json=dpBenchmarkPrice,proto3" json:"dp_benchmark_price,omitempty"` - DpBid float64 `protobuf:"fixed64,3,opt,name=dp_bid,json=dpBid,proto3" json:"dp_bid,omitempty"` - DpAsk float64 `protobuf:"fixed64,4,opt,name=dp_ask,json=dpAsk,proto3" json:"dp_ask,omitempty"` - CurrentBlockNumber int64 `protobuf:"varint,5,opt,name=current_block_number,json=currentBlockNumber,proto3" json:"current_block_number,omitempty"` - CurrentBlockHash string `protobuf:"bytes,6,opt,name=current_block_hash,json=currentBlockHash,proto3" json:"current_block_hash,omitempty"` - CurrentBlockTimestamp uint64 `protobuf:"varint,7,opt,name=current_block_timestamp,json=currentBlockTimestamp,proto3" json:"current_block_timestamp,omitempty"` - BridgeTaskRunStartedTimestamp int64 `protobuf:"varint,8,opt,name=bridge_task_run_started_timestamp,json=bridgeTaskRunStartedTimestamp,proto3" json:"bridge_task_run_started_timestamp,omitempty"` - BridgeTaskRunEndedTimestamp int64 `protobuf:"varint,9,opt,name=bridge_task_run_ended_timestamp,json=bridgeTaskRunEndedTimestamp,proto3" json:"bridge_task_run_ended_timestamp,omitempty"` - ProviderRequestedTimestamp int64 `protobuf:"varint,10,opt,name=provider_requested_timestamp,json=providerRequestedTimestamp,proto3" json:"provider_requested_timestamp,omitempty"` - ProviderReceivedTimestamp int64 `protobuf:"varint,11,opt,name=provider_received_timestamp,json=providerReceivedTimestamp,proto3" json:"provider_received_timestamp,omitempty"` - ProviderDataStreamEstablished int64 `protobuf:"varint,12,opt,name=provider_data_stream_established,json=providerDataStreamEstablished,proto3" json:"provider_data_stream_established,omitempty"` - ProviderIndicatedTime int64 `protobuf:"varint,13,opt,name=provider_indicated_time,json=providerIndicatedTime,proto3" json:"provider_indicated_time,omitempty"` - Feed string `protobuf:"bytes,14,opt,name=feed,proto3" json:"feed,omitempty"` - ObservationBenchmarkPrice int64 `protobuf:"varint,15,opt,name=observation_benchmark_price,json=observationBenchmarkPrice,proto3" json:"observation_benchmark_price,omitempty"` - ObservationBid int64 `protobuf:"varint,16,opt,name=observation_bid,json=observationBid,proto3" json:"observation_bid,omitempty"` - ObservationAsk int64 `protobuf:"varint,17,opt,name=observation_ask,json=observationAsk,proto3" json:"observation_ask,omitempty"` - ConfigDigest string `protobuf:"bytes,18,opt,name=config_digest,json=configDigest,proto3" json:"config_digest,omitempty"` - Round int64 `protobuf:"varint,19,opt,name=round,proto3" json:"round,omitempty"` - Epoch int64 `protobuf:"varint,20,opt,name=epoch,proto3" json:"epoch,omitempty"` - AssetSymbol string `protobuf:"bytes,21,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"` + DataSource string `protobuf:"bytes,1,opt,name=data_source,json=dataSource,proto3" json:"data_source,omitempty"` + DpBenchmarkPrice float64 `protobuf:"fixed64,2,opt,name=dp_benchmark_price,json=dpBenchmarkPrice,proto3" json:"dp_benchmark_price,omitempty"` + DpBid float64 `protobuf:"fixed64,3,opt,name=dp_bid,json=dpBid,proto3" json:"dp_bid,omitempty"` + DpAsk float64 `protobuf:"fixed64,4,opt,name=dp_ask,json=dpAsk,proto3" json:"dp_ask,omitempty"` + CurrentBlockNumber int64 `protobuf:"varint,5,opt,name=current_block_number,json=currentBlockNumber,proto3" json:"current_block_number,omitempty"` + CurrentBlockHash string `protobuf:"bytes,6,opt,name=current_block_hash,json=currentBlockHash,proto3" json:"current_block_hash,omitempty"` + CurrentBlockTimestamp uint64 `protobuf:"varint,7,opt,name=current_block_timestamp,json=currentBlockTimestamp,proto3" json:"current_block_timestamp,omitempty"` + BridgeTaskRunStartedTimestamp int64 `protobuf:"varint,8,opt,name=bridge_task_run_started_timestamp,json=bridgeTaskRunStartedTimestamp,proto3" json:"bridge_task_run_started_timestamp,omitempty"` + BridgeTaskRunEndedTimestamp int64 `protobuf:"varint,9,opt,name=bridge_task_run_ended_timestamp,json=bridgeTaskRunEndedTimestamp,proto3" json:"bridge_task_run_ended_timestamp,omitempty"` + ProviderRequestedTimestamp int64 `protobuf:"varint,10,opt,name=provider_requested_timestamp,json=providerRequestedTimestamp,proto3" json:"provider_requested_timestamp,omitempty"` + ProviderReceivedTimestamp int64 `protobuf:"varint,11,opt,name=provider_received_timestamp,json=providerReceivedTimestamp,proto3" json:"provider_received_timestamp,omitempty"` + ProviderDataStreamEstablished int64 `protobuf:"varint,12,opt,name=provider_data_stream_established,json=providerDataStreamEstablished,proto3" json:"provider_data_stream_established,omitempty"` + ProviderIndicatedTime int64 `protobuf:"varint,13,opt,name=provider_indicated_time,json=providerIndicatedTime,proto3" json:"provider_indicated_time,omitempty"` + Feed string `protobuf:"bytes,14,opt,name=feed,proto3" json:"feed,omitempty"` + ObservationBenchmarkPrice int64 `protobuf:"varint,15,opt,name=observation_benchmark_price,json=observationBenchmarkPrice,proto3" json:"observation_benchmark_price,omitempty"` + ObservationBid int64 `protobuf:"varint,16,opt,name=observation_bid,json=observationBid,proto3" json:"observation_bid,omitempty"` + ObservationAsk int64 `protobuf:"varint,17,opt,name=observation_ask,json=observationAsk,proto3" json:"observation_ask,omitempty"` + ConfigDigest string `protobuf:"bytes,18,opt,name=config_digest,json=configDigest,proto3" json:"config_digest,omitempty"` + Round int64 `protobuf:"varint,19,opt,name=round,proto3" json:"round,omitempty"` + Epoch int64 `protobuf:"varint,20,opt,name=epoch,proto3" json:"epoch,omitempty"` + AssetSymbol string `protobuf:"bytes,21,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"` + ObservationBenchmarkPriceString string `protobuf:"bytes,22,opt,name=observation_benchmark_price_string,json=observationBenchmarkPriceString,proto3" json:"observation_benchmark_price_string,omitempty"` + ObservationBidString string `protobuf:"bytes,23,opt,name=observation_bid_string,json=observationBidString,proto3" json:"observation_bid_string,omitempty"` + ObservationAskString string `protobuf:"bytes,24,opt,name=observation_ask_string,json=observationAskString,proto3" json:"observation_ask_string,omitempty"` } func (x *EnhancedEAMercury) Reset() { *x = EnhancedEAMercury{} if protoimpl.UnsafeEnabled { - mi := &file_telem_enhanced_ea_mercury_proto_msgTypes[0] + mi := &file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -64,7 +67,7 @@ func (x *EnhancedEAMercury) String() string { func (*EnhancedEAMercury) ProtoMessage() {} func (x *EnhancedEAMercury) ProtoReflect() protoreflect.Message { - mi := &file_telem_enhanced_ea_mercury_proto_msgTypes[0] + mi := &file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -77,7 +80,7 @@ func (x *EnhancedEAMercury) ProtoReflect() protoreflect.Message { // Deprecated: Use EnhancedEAMercury.ProtoReflect.Descriptor instead. func (*EnhancedEAMercury) Descriptor() ([]byte, []int) { - return file_telem_enhanced_ea_mercury_proto_rawDescGZIP(), []int{0} + return file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescGZIP(), []int{0} } func (x *EnhancedEAMercury) GetDataSource() string { @@ -227,98 +230,133 @@ func (x *EnhancedEAMercury) GetAssetSymbol() string { return "" } -var File_telem_enhanced_ea_mercury_proto protoreflect.FileDescriptor - -var file_telem_enhanced_ea_mercury_proto_rawDesc = []byte{ - 0x0a, 0x1f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x65, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x64, - 0x5f, 0x65, 0x61, 0x5f, 0x6d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x22, 0xd5, 0x07, 0x0a, 0x11, 0x45, 0x6e, 0x68, - 0x61, 0x6e, 0x63, 0x65, 0x64, 0x45, 0x41, 0x4d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x12, 0x1f, - 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x2c, 0x0a, 0x12, 0x64, 0x70, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x64, 0x70, 0x42, - 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x15, 0x0a, - 0x06, 0x64, 0x70, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, - 0x70, 0x42, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x70, 0x5f, 0x61, 0x73, 0x6b, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, 0x70, 0x41, 0x73, 0x6b, 0x12, 0x30, 0x0a, 0x14, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, - 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, - 0x61, 0x73, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x36, 0x0a, 0x17, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x48, 0x0a, 0x21, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x74, 0x61, - 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1d, - 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x44, 0x0a, - 0x1f, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, - 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x54, 0x61, - 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x40, 0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1a, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3e, 0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x65, 0x73, - 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x1d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x36, - 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x65, 0x65, 0x64, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x65, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1b, 0x6f, 0x62, - 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, - 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x19, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x65, 0x6e, 0x63, - 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x62, - 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x42, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6f, 0x62, - 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, - 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x21, 0x0a, - 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x15, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, - 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, - 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +func (x *EnhancedEAMercury) GetObservationBenchmarkPriceString() string { + if x != nil { + return x.ObservationBenchmarkPriceString + } + return "" +} + +func (x *EnhancedEAMercury) GetObservationBidString() string { + if x != nil { + return x.ObservationBidString + } + return "" +} + +func (x *EnhancedEAMercury) GetObservationAskString() string { + if x != nil { + return x.ObservationAskString + } + return "" +} + +var File_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto protoreflect.FileDescriptor + +var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDesc = []byte{ + 0x0a, 0x43, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, + 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x65, 0x6e, 0x68, 0x61, + 0x6e, 0x63, 0x65, 0x64, 0x5f, 0x65, 0x61, 0x5f, 0x6d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x22, 0x8e, 0x09, 0x0a, + 0x11, 0x45, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x45, 0x41, 0x4d, 0x65, 0x72, 0x63, 0x75, + 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x70, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, + 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x10, 0x64, 0x70, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, + 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x70, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x05, 0x64, 0x70, 0x42, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x70, 0x5f, 0x61, + 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, 0x70, 0x41, 0x73, 0x6b, 0x12, + 0x30, 0x0a, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, + 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x48, 0x0a, 0x21, 0x62, 0x72, 0x69, 0x64, 0x67, + 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x1d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, + 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x44, 0x0a, 0x1f, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, + 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x62, 0x72, 0x69, 0x64, + 0x67, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x40, 0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1a, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3e, 0x0a, 0x1b, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x5f, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x44, 0x61, 0x74, + 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x65, + 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x65, 0x65, 0x64, 0x12, 0x3e, + 0x0a, 0x1b, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x65, + 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x19, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x27, + 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, + 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x70, 0x6f, 0x63, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, + 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, + 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x4b, 0x0a, 0x22, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, + 0x69, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x1f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x65, 0x6e, + 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x62, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x17, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, + 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x62, 0x73, 0x65, 0x72, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x4e, 0x5a, + 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, + 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_telem_enhanced_ea_mercury_proto_rawDescOnce sync.Once - file_telem_enhanced_ea_mercury_proto_rawDescData = file_telem_enhanced_ea_mercury_proto_rawDesc + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescOnce sync.Once + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescData = file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDesc ) -func file_telem_enhanced_ea_mercury_proto_rawDescGZIP() []byte { - file_telem_enhanced_ea_mercury_proto_rawDescOnce.Do(func() { - file_telem_enhanced_ea_mercury_proto_rawDescData = protoimpl.X.CompressGZIP(file_telem_enhanced_ea_mercury_proto_rawDescData) +func file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescGZIP() []byte { + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescOnce.Do(func() { + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescData) }) - return file_telem_enhanced_ea_mercury_proto_rawDescData + return file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescData } -var file_telem_enhanced_ea_mercury_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_telem_enhanced_ea_mercury_proto_goTypes = []interface{}{ +var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_goTypes = []interface{}{ (*EnhancedEAMercury)(nil), // 0: telem.EnhancedEAMercury } -var file_telem_enhanced_ea_mercury_proto_depIdxs = []int32{ +var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name @@ -326,13 +364,13 @@ var file_telem_enhanced_ea_mercury_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for field type_name } -func init() { file_telem_enhanced_ea_mercury_proto_init() } -func file_telem_enhanced_ea_mercury_proto_init() { - if File_telem_enhanced_ea_mercury_proto != nil { +func init() { file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_init() } +func file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_init() { + if File_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_telem_enhanced_ea_mercury_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*EnhancedEAMercury); i { case 0: return &v.state @@ -349,18 +387,18 @@ func file_telem_enhanced_ea_mercury_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_telem_enhanced_ea_mercury_proto_rawDesc, + RawDescriptor: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_telem_enhanced_ea_mercury_proto_goTypes, - DependencyIndexes: file_telem_enhanced_ea_mercury_proto_depIdxs, - MessageInfos: file_telem_enhanced_ea_mercury_proto_msgTypes, + GoTypes: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_goTypes, + DependencyIndexes: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_depIdxs, + MessageInfos: file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_msgTypes, }.Build() - File_telem_enhanced_ea_mercury_proto = out.File - file_telem_enhanced_ea_mercury_proto_rawDesc = nil - file_telem_enhanced_ea_mercury_proto_goTypes = nil - file_telem_enhanced_ea_mercury_proto_depIdxs = nil + File_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto = out.File + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDesc = nil + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_goTypes = nil + file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_depIdxs = nil } diff --git a/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto b/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto index 805d976d2d6..92242fd2a1e 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto +++ b/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto @@ -26,4 +26,7 @@ message EnhancedEAMercury { int64 round=19; int64 epoch=20; string asset_symbol=21; + string observation_benchmark_price_string = 22; + string observation_bid_string = 23; + string observation_ask_string = 24; } From 12ff0e6dabfd52333bf4ebdc3f1725f5c4172ba7 Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:42:05 +0200 Subject: [PATCH 022/271] Update CTF and SELECTED_NETWORKS env in E2E tests (#11103) * Update CTF and SELECTED_NETWORKS env * Update go mod --- integration-tests/benchmark/keeper_test.go | 2 +- integration-tests/chaos/automation_chaos_test.go | 11 ++++++----- integration-tests/chaos/ocr2vrf_chaos_test.go | 4 ++-- integration-tests/chaos/ocr_chaos_test.go | 2 +- integration-tests/docker/test_env/test_env_builder.go | 2 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/load/functions/setup.go | 2 +- integration-tests/performance/cron_test.go | 2 +- integration-tests/performance/directrequest_test.go | 2 +- integration-tests/performance/flux_test.go | 2 +- integration-tests/performance/keeper_test.go | 2 +- integration-tests/performance/ocr_test.go | 2 +- integration-tests/performance/vrf_test.go | 2 +- integration-tests/reorg/automation_reorg_test.go | 4 ++-- integration-tests/smoke/automation_test.go | 8 ++++---- integration-tests/smoke/ocr2_test.go | 2 +- integration-tests/smoke/ocr2vrf_test.go | 2 +- integration-tests/testsetups/ocr.go | 8 ++++---- 19 files changed, 33 insertions(+), 32 deletions(-) diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 7178ab854ea..ed26caf4704 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -299,7 +299,7 @@ func getEnv(key, fallback string) string { func SetupAutomationBenchmarkEnv(t *testing.T) (*environment.Environment, blockchain.EVMNetwork) { l := logging.GetTestLogger(t) - testNetwork := networks.SelectedNetwork // Environment currently being used to run benchmark test on + testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] // Environment currently being used to run benchmark test on blockTime := "1" networkDetailTOML := `MinIncomingConfirmations = 1` diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 244f6c36ea9..62f85d3256d 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -43,7 +43,7 @@ ListenAddresses = ["0.0.0.0:6690"]` defaultAutomationSettings = map[string]interface{}{ "replicas": 6, - "toml": client.AddNetworksConfig(baseTOML, networks.SelectedNetwork), + "toml": client.AddNetworksConfig(baseTOML, networks.MustGetSelectedNetworksFromEnv()[0]), "db": map[string]interface{}{ "stateful": true, "capacity": "1Gi", @@ -61,9 +61,10 @@ ListenAddresses = ["0.0.0.0:6690"]` } defaultEthereumSettings = ðereum.Props{ - NetworkName: networks.SelectedNetwork.Name, - Simulated: networks.SelectedNetwork.Simulated, - WsURLs: networks.SelectedNetwork.URLs, + // utils.MustGetSelectedNetworksFromEnv() + NetworkName: networks.MustGetSelectedNetworksFromEnv()[0].Name, + Simulated: networks.MustGetSelectedNetworksFromEnv()[0].Simulated, + WsURLs: networks.MustGetSelectedNetworksFromEnv()[0].URLs, Values: map[string]interface{}{ "resources": map[string]interface{}{ "requests": map[string]interface{}{ @@ -182,7 +183,7 @@ func TestAutomationChaos(t *testing.T) { testCase := tst t.Run(fmt.Sprintf("Automation_%s", name), func(t *testing.T) { t.Parallel() - network := networks.SelectedNetwork // Need a new copy of the network for each test + network := networks.MustGetSelectedNetworksFromEnv()[0] // Need a new copy of the network for each test testEnvironment := environment. New(&environment.Config{ diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index 1d7f61f783c..cbab1bf9e78 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -31,7 +31,7 @@ import ( func TestOCR2VRFChaos(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - loadedNetwork := networks.SelectedNetwork + loadedNetwork := networks.MustGetSelectedNetworksFromEnv()[0] defaultOCR2VRFSettings := map[string]interface{}{ "replicas": 6, @@ -119,7 +119,7 @@ func TestOCR2VRFChaos(t *testing.T) { testCase := tc t.Run(fmt.Sprintf("OCR2VRF_%s", testCaseName), func(t *testing.T) { t.Parallel() - testNetwork := networks.SelectedNetwork // Need a new copy of the network for each test + testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] // Need a new copy of the network for each test testEnvironment := environment. New(&environment.Config{ NamespacePrefix: fmt.Sprintf( diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index f3ee12046fb..0d72e3932e7 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -53,7 +53,7 @@ var ( ) func TestMain(m *testing.M) { - defaultOCRSettings["toml"] = client.AddNetworksConfig(config.BaseOCRP2PV1Config, networks.SelectedNetwork) + defaultOCRSettings["toml"] = client.AddNetworksConfig(config.BaseOCRP2PV1Config, networks.MustGetSelectedNetworksFromEnv()[0]) os.Exit(m.Run()) } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 072728321bf..d1550240500 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -238,7 +238,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } return b.te, nil } - networkConfig := networks.SelectedNetwork + networkConfig := networks.MustGetSelectedNetworksFromEnv()[0] var internalDockerUrls test_env.InternalDockerUrls if b.hasGeth && networkConfig.Simulated { networkConfig, internalDockerUrls, err = b.te.StartGeth() diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 7dd2d017785..7683a966920 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -21,7 +21,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-env v0.38.3 - github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231018101901-23824db88d36 + github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231027132403-4898f11e80b6 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 3be74077277..d280310b9f2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2370,8 +2370,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231018101901-23824db88d36 h1:ow84QG8vEHMvfjGg0RF8HNYh80WcHci6PIenXyY6K8Y= -github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231018101901-23824db88d36/go.mod h1:RWlmjwnjIGbQAnRfKwe02Ife82nNI3rZmdI0zgkfbyk= +github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231027132403-4898f11e80b6 h1:f1nUQ/1eUTMwNbOZK0P7P6OHvTDGQSn2KE+LtwY0rXA= +github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231027132403-4898f11e80b6/go.mod h1:RWlmjwnjIGbQAnRfKwe02Ife82nNI3rZmdI0zgkfbyk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/load/functions/setup.go b/integration-tests/load/functions/setup.go index 5d44cbc698e..5253c531eee 100644 --- a/integration-tests/load/functions/setup.go +++ b/integration-tests/load/functions/setup.go @@ -50,7 +50,7 @@ type S4SecretsCfg struct { } func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { - bc, err := blockchain.NewEVMClientFromNetwork(networks.SelectedNetwork, log.Logger) + bc, err := blockchain.NewEVMClientFromNetwork(networks.MustGetSelectedNetworksFromEnv()[0], log.Logger) if err != nil { return nil, err } diff --git a/integration-tests/performance/cron_test.go b/integration-tests/performance/cron_test.go index 959cb0fa3e1..84a49646474 100644 --- a/integration-tests/performance/cron_test.go +++ b/integration-tests/performance/cron_test.go @@ -109,7 +109,7 @@ func TestCronPerformance(t *testing.T) { func setupCronTest(t *testing.T) (testEnvironment *environment.Environment) { logging.Init() - network := networks.SelectedNetwork + network := networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := ethereum.New(nil) if !network.Simulated { evmConfig = ethereum.New(ðereum.Props{ diff --git a/integration-tests/performance/directrequest_test.go b/integration-tests/performance/directrequest_test.go index 4ff2b856195..faaac5910d2 100644 --- a/integration-tests/performance/directrequest_test.go +++ b/integration-tests/performance/directrequest_test.go @@ -129,7 +129,7 @@ func TestDirectRequestPerformance(t *testing.T) { } func setupDirectRequestTest(t *testing.T) (testEnvironment *environment.Environment) { - network := networks.SelectedNetwork + network := networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := ethereum.New(nil) if !network.Simulated { evmConfig = ethereum.New(ðereum.Props{ diff --git a/integration-tests/performance/flux_test.go b/integration-tests/performance/flux_test.go index 3f9db27c106..df3022003e1 100644 --- a/integration-tests/performance/flux_test.go +++ b/integration-tests/performance/flux_test.go @@ -173,7 +173,7 @@ func TestFluxPerformance(t *testing.T) { } func setupFluxTest(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.SelectedNetwork + testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] evmConf := ethereum.New(nil) if !testNetwork.Simulated { evmConf = ethereum.New(ðereum.Props{ diff --git a/integration-tests/performance/keeper_test.go b/integration-tests/performance/keeper_test.go index 08ea95b4340..7a2cc933de2 100644 --- a/integration-tests/performance/keeper_test.go +++ b/integration-tests/performance/keeper_test.go @@ -134,7 +134,7 @@ func setupKeeperTest( contracts.ContractDeployer, contracts.LinkToken, ) { - network := networks.SelectedNetwork + network := networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := eth.New(nil) if !network.Simulated { evmConfig = eth.New(ð.Props{ diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go index b18d7f1f791..1db030f1cce 100644 --- a/integration-tests/performance/ocr_test.go +++ b/integration-tests/performance/ocr_test.go @@ -90,7 +90,7 @@ func TestOCRBasic(t *testing.T) { } func setupOCRTest(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.SelectedNetwork + testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := ethereum.New(nil) if !testNetwork.Simulated { evmConfig = ethereum.New(ðereum.Props{ diff --git a/integration-tests/performance/vrf_test.go b/integration-tests/performance/vrf_test.go index 510e378eb82..c715641692d 100644 --- a/integration-tests/performance/vrf_test.go +++ b/integration-tests/performance/vrf_test.go @@ -135,7 +135,7 @@ func TestVRFBasic(t *testing.T) { } func setupVRFTest(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.SelectedNetwork + testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := ethereum.New(nil) if !testNetwork.Simulated { evmConfig = ethereum.New(ðereum.Props{ diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index e94e5c28538..a1260cc37ac 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -48,7 +48,7 @@ HistoryDepth = 400 [EVM.GasEstimator] Mode = 'FixedPrice' LimitDefault = 5_000_000` - activeEVMNetwork = networks.SelectedNetwork + activeEVMNetwork = networks.MustGetSelectedNetworksFromEnv()[0] defaultAutomationSettings = map[string]interface{}{ "toml": client.AddNetworkDetailedConfig(baseTOML, networkTOML, activeEVMNetwork), "db": map[string]interface{}{ @@ -135,7 +135,7 @@ func TestAutomationReorg(t *testing.T) { for name, registryVersion := range registryVersions { t.Run(name, func(t *testing.T) { t.Parallel() - network := networks.SelectedNetwork + network := networks.MustGetSelectedNetworksFromEnv()[0] defaultAutomationSettings["replicas"] = numberOfNodes cd := chainlink.New(0, defaultAutomationSettings) diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 834de5ce481..3addac1b9d1 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -79,9 +79,9 @@ var ( func TestMain(m *testing.M) { logging.Init() - fmt.Printf("Running Smoke Test on %s\n", networks.SelectedNetwork.Name) // Print to get around disabled logging - fmt.Printf("Chainlink Image %s\n", os.Getenv("CHAINLINK_IMAGE")) // Print to get around disabled logging - fmt.Printf("Chainlink Version %s\n", os.Getenv("CHAINLINK_VERSION")) // Print to get around disabled logging + fmt.Printf("Running Smoke Test on %s\n", networks.MustGetSelectedNetworksFromEnv()[0].Name) // Print to get around disabled logging + fmt.Printf("Chainlink Image %s\n", os.Getenv("CHAINLINK_IMAGE")) // Print to get around disabled logging + fmt.Printf("Chainlink Version %s\n", os.Getenv("CHAINLINK_VERSION")) // Print to get around disabled logging os.Exit(m.Run()) } @@ -1028,7 +1028,7 @@ func setupAutomationTestDocker( l := logging.GetTestLogger(t) // Add registry version to config registryConfig.RegistryVersion = registryVersion - network := networks.SelectedNetwork + network := networks.MustGetSelectedNetworksFromEnv()[0] // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index d82d84a2072..582ca17f7b6 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -107,7 +107,7 @@ func setupOCR2Test(t *testing.T, forwardersEnabled bool) ( testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork, ) { - testNetwork = networks.SelectedNetwork + testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := ethereum.New(nil) if !testNetwork.Simulated { evmConfig = ethereum.New(ðereum.Props{ diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 81488639187..8c102f6fd21 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -149,7 +149,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { } func setupOCR2VRFEnvironment(t *testing.T) (testEnvironment *environment.Environment, testNetwork blockchain.EVMNetwork) { - testNetwork = networks.SelectedNetwork + testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] evmConfig := eth.New(nil) if !testNetwork.Simulated { evmConfig = eth.New(ð.Props{ diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index 048f3124ad9..c4b1fc7ab1e 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -126,7 +126,7 @@ func NewOCRSoakTest(t *testing.T, forwarderFlow bool) (*OCRSoakTest, error) { // DeployEnvironment deploys the test environment, starting all Chainlink nodes and other components for the test func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { - network := networks.SelectedNetwork // Environment currently being used to soak test on + network := networks.MustGetSelectedNetworksFromEnv()[0] // Environment currently being used to soak test on nsPre := "soak-ocr-" if o.OperatorForwarderFlow { nsPre = fmt.Sprintf("%sforwarder-", nsPre) @@ -165,7 +165,7 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { // LoadEnvironment loads an existing test environment using the provided URLs func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, chainURL, mockServerURL string) { var ( - network = networks.SelectedNetwork + network = networks.MustGetSelectedNetworksFromEnv()[0] err error ) o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) @@ -185,7 +185,7 @@ func (o *OCRSoakTest) Environment() *environment.Environment { func (o *OCRSoakTest) Setup() { var ( err error - network = networks.SelectedNetwork + network = networks.MustGetSelectedNetworksFromEnv()[0] ) // Environment currently being used to soak test on @@ -387,7 +387,7 @@ func (o *OCRSoakTest) LoadState() error { o.startTime = testState.StartTime o.startingBlockNum = testState.StartingBlockNum - network := networks.SelectedNetwork + network := networks.MustGetSelectedNetworksFromEnv()[0] o.chainClient, err = blockchain.ConnectEVMClient(network, o.log) if err != nil { return err From 5808e734cf024a5e8c5450e939e1f2d5b3cba546 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 27 Oct 2023 08:52:16 -0500 Subject: [PATCH 023/271] core: log a warning when deprecated P2P.V1 config is set in TOML (#11073) * core: log a warning when deprecated P2P.V1 config is set in TOML * change default P2P from V1 to V2 * bump operator ui --- .../evm/config/mocks/chain_scoped_config.go | 6 +- core/cmd/shell_local.go | 5 +- core/config/app_config.go | 2 +- core/config/docs/core.toml | 7 +- core/scripts/chaincli/handler/handler.go | 4 +- .../common/vrf/docker/toml-config/base.toml | 1 - core/scripts/ocr2vrf/util.go | 13 +- core/services/chainlink/config.go | 44 +++ core/services/chainlink/config_general.go | 14 +- core/services/chainlink/config_p2p_test.go | 7 +- core/services/chainlink/config_test.go | 39 ++- .../chainlink/mocks/general_config.go | 6 +- .../testdata/config-empty-effective.toml | 4 +- .../chainlink/testdata/config-full.toml | 4 +- .../config-multi-chain-effective.toml | 4 +- .../ocr2/plugins/mercury/helpers_test.go | 4 - core/utils/config/validate.go | 14 + .../testdata/config-empty-effective.toml | 4 +- core/web/resolver/testdata/config-full.toml | 4 +- .../config-multi-chain-effective.toml | 4 +- docs/CHANGELOG.md | 10 +- docs/CONFIG.md | 12 +- integration-tests/benchmark/keeper_test.go | 1 - .../chaos/automation_chaos_test.go | 1 - integration-tests/config/config.go | 6 +- integration-tests/performance/ocr_test.go | 4 + .../reorg/automation_reorg_test.go | 6 +- integration-tests/smoke/automation_test.go | 1 - integration-tests/smoke/keeper_test.go | 2 +- integration-tests/types/config/node/core.go | 11 +- .../types/config/node/defaults/sample.toml | 1 - testdata/scripts/node/validate/default.txtar | 4 +- .../disk-based-logging-disabled.txtar | 4 +- .../validate/disk-based-logging-no-dir.txtar | 4 +- .../node/validate/disk-based-logging.txtar | 4 +- testdata/scripts/node/validate/invalid.txtar | 4 +- testdata/scripts/node/validate/valid.txtar | 4 +- testdata/scripts/node/validate/warnings.txtar | 279 ++++++++++++++++++ 38 files changed, 463 insertions(+), 85 deletions(-) create mode 100644 testdata/scripts/node/validate/warnings.txtar diff --git a/core/chains/evm/config/mocks/chain_scoped_config.go b/core/chains/evm/config/mocks/chain_scoped_config.go index 0854b82165a..cb18282f495 100644 --- a/core/chains/evm/config/mocks/chain_scoped_config.go +++ b/core/chains/evm/config/mocks/chain_scoped_config.go @@ -252,9 +252,9 @@ func (_m *ChainScopedConfig) Log() coreconfig.Log { return r0 } -// LogConfiguration provides a mock function with given fields: log -func (_m *ChainScopedConfig) LogConfiguration(log coreconfig.LogfFn) { - _m.Called(log) +// LogConfiguration provides a mock function with given fields: log, warn +func (_m *ChainScopedConfig) LogConfiguration(log coreconfig.LogfFn, warn coreconfig.LogfFn) { + _m.Called(log, warn) } // Mercury provides a mock function with given fields: diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index f578604db33..401375238d8 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -290,7 +290,7 @@ func (s *Shell) runNode(c *cli.Context) error { s.Config.SetPasswords(pwd, vrfpwd) - s.Config.LogConfiguration(lggr.Debugf) + s.Config.LogConfiguration(lggr.Debugf, lggr.Warnf) if err := s.Config.Validate(); err != nil { return errors.Wrap(err, "config validation failed") @@ -689,7 +689,8 @@ var errDBURLMissing = errors.New("You must set CL_DATABASE_URL env variable or p // ConfigValidate validate the client configuration and pretty-prints results func (s *Shell) ConfigFileValidate(_ *cli.Context) error { - s.Config.LogConfiguration(func(f string, params ...any) { fmt.Printf(f, params...) }) + fn := func(f string, params ...any) { fmt.Printf(f, params...) } + s.Config.LogConfiguration(fn, fn) if err := s.configExitErr(s.Config.Validate); err != nil { return err } diff --git a/core/config/app_config.go b/core/config/app_config.go index ab8d9559673..648939b871b 100644 --- a/core/config/app_config.go +++ b/core/config/app_config.go @@ -28,7 +28,7 @@ type AppConfig interface { Validate() error ValidateDB() error - LogConfiguration(log LogfFn) + LogConfiguration(log, warn LogfFn) SetLogLevel(lvl zapcore.Level) error SetLogSQL(logSQL bool) SetPasswords(keystore, vrf *string) diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 18f5810adcc..1ca4c656a7f 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -359,6 +359,8 @@ TraceLogging = false # Default # automatically fall back to V1. If V2 starts working again later, it will automatically be preferred again. This is useful # for migrating networks without downtime. Note that the two networking stacks _must not_ be configured to bind to the same IP/port. # +# Note: P2P.V1 is deprecated will be removed in the future. +# # All nodes in the OCR network should share the same networking stack. [P2P] # IncomingMessageBufferSize is the per-remote number of incoming @@ -377,9 +379,10 @@ PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' # Example # TraceLogging enables trace level logging. TraceLogging = false # Default +# P2P.V1 is deprecated and will be removed in a future version. [P2P.V1] # Enabled enables P2P V1. -Enabled = true # Default +Enabled = false # Default # AnnounceIP should be set as the externally reachable IP address of the Chainlink node. AnnounceIP = '1.2.3.4' # Example # AnnouncePort should be set as the externally reachable port of the Chainlink node. @@ -423,7 +426,7 @@ PeerstoreWriteInterval = '5m' # Default [P2P.V2] # Enabled enables P2P V2. # Note: V1.Enabled is true by default, so it must be set false in order to run V2 only. -Enabled = false # Default +Enabled = true # Default # AnnounceAddresses is the addresses the peer will advertise on the network in `host:port` form as accepted by the TCP version of Go’s `net.Dial`. # The addresses should be reachable by other nodes on the network. When attempting to connect to another node, # a node will attempt to dial all of the other node’s AnnounceAddresses in round-robin fashion. diff --git a/core/scripts/chaincli/handler/handler.go b/core/scripts/chaincli/handler/handler.go index c51792f9adc..f72e94605d4 100644 --- a/core/scripts/chaincli/handler/handler.go +++ b/core/scripts/chaincli/handler/handler.go @@ -64,9 +64,7 @@ HTTPSPort = 0 LogPoller = true [OCR2] Enabled = true -[P2P] -[P2P.V2] -Enabled = true + [Keeper] TurnLookBack = 0 [[EVM]] diff --git a/core/scripts/common/vrf/docker/toml-config/base.toml b/core/scripts/common/vrf/docker/toml-config/base.toml index 0bb83beb94a..39aab2e63ab 100644 --- a/core/scripts/common/vrf/docker/toml-config/base.toml +++ b/core/scripts/common/vrf/docker/toml-config/base.toml @@ -26,6 +26,5 @@ HTTPSPort = 0 [P2P] [P2P.V2] -Enabled = true AnnounceAddresses = ['0.0.0.0:6690'] ListenAddresses = ['0.0.0.0:6690'] diff --git a/core/scripts/ocr2vrf/util.go b/core/scripts/ocr2vrf/util.go index f8d104a5f3e..a2ff55524d3 100644 --- a/core/scripts/ocr2vrf/util.go +++ b/core/scripts/ocr2vrf/util.go @@ -14,16 +14,17 @@ import ( "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" + "github.com/urfave/cli" + "go.dedis.ch/kyber/v3" + "go.dedis.ch/kyber/v3/group/edwards25519" + "go.dedis.ch/kyber/v3/pairing" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/ocr2vrf/altbn_128" "github.com/smartcontractkit/ocr2vrf/dkg" "github.com/smartcontractkit/ocr2vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" - "github.com/urfave/cli" - "go.dedis.ch/kyber/v3" - "go.dedis.ch/kyber/v3/group/edwards25519" - "go.dedis.ch/kyber/v3/pairing" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" @@ -43,11 +44,7 @@ var ( g1 = suite.G1() g2 = suite.G2() tomlConfigTemplate = ` - [P2P.V1] - Enabled = false - [P2P.V2] - Enabled = true ListenAddresses = ["127.0.0.1:8000"] [Feature] diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 62be789f859..26e2d539bac 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -52,6 +52,50 @@ func (c *Config) TOMLString() (string, error) { return string(b), nil } +// deprecationWarnings returns an error if the Config contains deprecated fields. +// This is typically used before defaults have been applied, with input from the user. +func (c *Config) deprecationWarnings() (err error) { + if c.P2P.V1 != (toml.P2PV1{}) { + err = multierr.Append(err, config.ErrDeprecated{Name: "P2P.V1"}) + var err2 error + if c.P2P.V1.AnnounceIP != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "AnnounceIP"}) + } + if c.P2P.V1.AnnouncePort != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "AnnouncePort"}) + } + if c.P2P.V1.BootstrapCheckInterval != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "BootstrapCheckInterval"}) + } + if c.P2P.V1.DefaultBootstrapPeers != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "DefaultBootstrapPeers"}) + } + if c.P2P.V1.DHTAnnouncementCounterUserPrefix != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "DHTAnnouncementCounterUserPrefix"}) + } + if c.P2P.V1.DHTLookupInterval != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "DHTLookupInterval"}) + } + if c.P2P.V1.ListenIP != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "ListenIP"}) + } + if c.P2P.V1.ListenPort != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "ListenPort"}) + } + if c.P2P.V1.NewStreamTimeout != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "NewStreamTimeout"}) + } + if c.P2P.V1.PeerstoreWriteInterval != nil { + err2 = multierr.Append(err2, config.ErrDeprecated{Name: "PeerstoreWriteInterval"}) + } + err2 = config.NamedMultiErrorList(err2, "P2P.V1") + err = multierr.Append(err, err2) + } + return +} + +// Validate returns an error if the Config is not valid for use, as-is. +// This is typically used after defaults have been applied. func (c *Config) Validate() error { if err := config.Validate(c); err != nil { return fmt.Errorf("invalid configuration: %w", err) diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 8e3dc100a44..6243146e91e 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -35,11 +35,13 @@ import ( type generalConfig struct { inputTOML string // user input, normalized via de/re-serialization effectiveTOML string // with default values included - secretsTOML string // with env overdies includes, redacted + secretsTOML string // with env overrides includes, redacted c *Config // all fields non-nil (unless the legacy method signature return a pointer) secrets *Secrets + warning error // warnings about inputTOML, e.g. deprecated fields + logLevelDefault zapcore.Level appIDOnce sync.Once @@ -123,7 +125,7 @@ func (o *GeneralConfigOpts) parseSecrets(secrets string) error { return nil } -// New returns a coreconfig.GeneralConfig for the given options. +// New returns a GeneralConfig for the given options. func (o GeneralConfigOpts) New() (GeneralConfig, error) { err := o.parse() if err != nil { @@ -135,6 +137,8 @@ func (o GeneralConfigOpts) New() (GeneralConfig, error) { return nil, err } + _, warning := utils.MultiErrorList(o.Config.deprecationWarnings()) + o.Config.setDefaults() if !o.SkipEnv { err = o.Secrets.setEnv() @@ -163,6 +167,7 @@ func (o GeneralConfigOpts) New() (GeneralConfig, error) { secretsTOML: secrets, c: &o.Config, secrets: &o.Secrets, + warning: warning, } if lvl := o.Config.Log.Level; lvl != nil { cfg.logLevelDefault = zapcore.Level(*lvl) @@ -253,10 +258,13 @@ func validateEnv() (err error) { return } -func (g *generalConfig) LogConfiguration(log coreconfig.LogfFn) { +func (g *generalConfig) LogConfiguration(log, warn coreconfig.LogfFn) { log("# Secrets:\n%s\n", g.secretsTOML) log("# Input Configuration:\n%s\n", g.inputTOML) log("# Effective Configuration, with defaults applied:\n%s\n", g.effectiveTOML) + if g.warning != nil { + warn("# Configuration warning:\n%s\n", g.warning) + } } // ConfigTOML implements chainlink.ConfigV2 diff --git a/core/services/chainlink/config_p2p_test.go b/core/services/chainlink/config_p2p_test.go index d6adfe7051c..21ce8f17e48 100644 --- a/core/services/chainlink/config_p2p_test.go +++ b/core/services/chainlink/config_p2p_test.go @@ -4,9 +4,10 @@ import ( "testing" "time" - "github.com/smartcontractkit/libocr/commontypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/libocr/commontypes" ) func TestP2PConfig(t *testing.T) { @@ -23,7 +24,7 @@ func TestP2PConfig(t *testing.T) { assert.True(t, p2p.TraceLogging()) v1 := p2p.V1() - assert.False(t, v1.Enabled()) + assert.True(t, v1.Enabled()) assert.Equal(t, "1.2.3.4", v1.AnnounceIP().String()) assert.Equal(t, uint16(1234), v1.AnnouncePort()) assert.Equal(t, time.Minute, v1.BootstrapCheckInterval()) @@ -38,7 +39,7 @@ func TestP2PConfig(t *testing.T) { assert.Equal(t, time.Minute, v1.PeerstoreWriteInterval()) v2 := p2p.V2() - assert.True(t, v2.Enabled()) + assert.False(t, v2.Enabled()) assert.Equal(t, []string{"a", "b", "c"}, v2.AnnounceAddresses()) assert.ElementsMatch( t, diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index d811dd8209d..597dab6ba1c 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -389,7 +389,7 @@ func TestConfig_Marshal(t *testing.T) { PeerID: mustPeerID("12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw"), TraceLogging: ptr(true), V1: toml.P2PV1{ - Enabled: ptr(false), + Enabled: ptr(true), AnnounceIP: mustIP("1.2.3.4"), AnnouncePort: ptr[uint16](1234), BootstrapCheckInterval: models.MustNewDuration(time.Minute), @@ -402,7 +402,7 @@ func TestConfig_Marshal(t *testing.T) { PeerstoreWriteInterval: models.MustNewDuration(time.Minute), }, V2: toml.P2PV2{ - Enabled: ptr(true), + Enabled: ptr(false), AnnounceAddresses: &[]string{"a", "b", "c"}, DefaultBootstrappers: &[]ocrcommontypes.BootstrapperLocator{ {PeerID: "12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw", Addrs: []string{"foo:42", "bar:10"}}, @@ -820,7 +820,7 @@ PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' TraceLogging = true [P2P.V1] -Enabled = false +Enabled = true AnnounceIP = '1.2.3.4' AnnouncePort = 1234 BootstrapCheckInterval = '1m0s' @@ -833,7 +833,7 @@ NewStreamTimeout = '1s' PeerstoreWriteInterval = '1m0s' [P2P.V2] -Enabled = true +Enabled = false AnnounceAddresses = ['a', 'b', 'c'] DefaultBootstrappers = ['12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw@foo:42/bar:10', '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw@test:99'] DeltaDial = '1m0s' @@ -1248,6 +1248,21 @@ func Test_generalConfig_LogConfiguration(t *testing.T) { secrets = "# Secrets:\n" input = "# Input Configuration:\n" effective = "# Effective Configuration, with defaults applied:\n" + warning = "# Configuration warning:\n" + + deprecated = `2 errors: + - P2P.V1: is deprecated and will be removed in a future version + - P2P.V1: 10 errors: + - AnnounceIP: is deprecated and will be removed in a future version + - AnnouncePort: is deprecated and will be removed in a future version + - BootstrapCheckInterval: is deprecated and will be removed in a future version + - DefaultBootstrapPeers: is deprecated and will be removed in a future version + - DHTAnnouncementCounterUserPrefix: is deprecated and will be removed in a future version + - DHTLookupInterval: is deprecated and will be removed in a future version + - ListenIP: is deprecated and will be removed in a future version + - ListenPort: is deprecated and will be removed in a future version + - NewStreamTimeout: is deprecated and will be removed in a future version + - PeerstoreWriteInterval: is deprecated and will be removed in a future version` ) tests := []struct { name string @@ -1257,10 +1272,11 @@ func Test_generalConfig_LogConfiguration(t *testing.T) { wantConfig string wantEffective string wantSecrets string + wantWarning string }{ {name: "empty", wantEffective: emptyEffectiveTOML, wantSecrets: emptyEffectiveSecretsTOML}, {name: "full", inputSecrets: secretsFullTOML, inputConfig: fullTOML, - wantConfig: fullTOML, wantEffective: fullTOML, wantSecrets: secretsFullRedactedTOML}, + wantConfig: fullTOML, wantEffective: fullTOML, wantSecrets: secretsFullRedactedTOML, wantWarning: deprecated}, {name: "multi-chain", inputSecrets: secretsMultiTOML, inputConfig: multiChainTOML, wantConfig: multiChainTOML, wantEffective: multiChainEffectiveTOML, wantSecrets: secretsMultiRedactedTOML}, } @@ -1274,10 +1290,11 @@ func Test_generalConfig_LogConfiguration(t *testing.T) { } c, err := opts.New() require.NoError(t, err) - c.LogConfiguration(lggr.Infof) + c.LogConfiguration(lggr.Infof, lggr.Warnf) inputLogs := observed.FilterMessageSnippet(secrets).All() if assert.Len(t, inputLogs, 1) { + assert.Equal(t, zapcore.InfoLevel, inputLogs[0].Level) got := strings.TrimPrefix(inputLogs[0].Message, secrets) got = strings.TrimSuffix(got, "\n") assert.Equal(t, tt.wantSecrets, got) @@ -1285,6 +1302,7 @@ func Test_generalConfig_LogConfiguration(t *testing.T) { inputLogs = observed.FilterMessageSnippet(input).All() if assert.Len(t, inputLogs, 1) { + assert.Equal(t, zapcore.InfoLevel, inputLogs[0].Level) got := strings.TrimPrefix(inputLogs[0].Message, input) got = strings.TrimSuffix(got, "\n") assert.Equal(t, tt.wantConfig, got) @@ -1292,10 +1310,19 @@ func Test_generalConfig_LogConfiguration(t *testing.T) { inputLogs = observed.FilterMessageSnippet(effective).All() if assert.Len(t, inputLogs, 1) { + assert.Equal(t, zapcore.InfoLevel, inputLogs[0].Level) got := strings.TrimPrefix(inputLogs[0].Message, effective) got = strings.TrimSuffix(got, "\n") assert.Equal(t, tt.wantEffective, got) } + + inputLogs = observed.FilterMessageSnippet(warning).All() + if tt.wantWarning != "" && assert.Len(t, inputLogs, 1) { + assert.Equal(t, zapcore.WarnLevel, inputLogs[0].Level) + got := strings.TrimPrefix(inputLogs[0].Message, warning) + got = strings.TrimSuffix(got, "\n") + assert.Equal(t, tt.wantWarning, got) + } }) } } diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index 8098b9634f5..0bc51ea4310 100644 --- a/core/services/chainlink/mocks/general_config.go +++ b/core/services/chainlink/mocks/general_config.go @@ -298,9 +298,9 @@ func (_m *GeneralConfig) Log() config.Log { return r0 } -// LogConfiguration provides a mock function with given fields: log -func (_m *GeneralConfig) LogConfiguration(log config.LogfFn) { - _m.Called(log) +// LogConfiguration provides a mock function with given fields: log, warn +func (_m *GeneralConfig) LogConfiguration(log config.LogfFn, warn config.LogfFn) { + _m.Called(log, warn) } // Mercury provides a mock function with given fields: diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index e746d66777d..48d432138a8 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -142,7 +142,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -155,7 +155,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 3bd422f8923..1534a411dc1 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -148,7 +148,7 @@ PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' TraceLogging = true [P2P.V1] -Enabled = false +Enabled = true AnnounceIP = '1.2.3.4' AnnouncePort = 1234 BootstrapCheckInterval = '1m0s' @@ -161,7 +161,7 @@ NewStreamTimeout = '1s' PeerstoreWriteInterval = '1m0s' [P2P.V2] -Enabled = true +Enabled = false AnnounceAddresses = ['a', 'b', 'c'] DefaultBootstrappers = ['12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw@foo:42/bar:10', '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw@test:99'] DeltaDial = '1m0s' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 89b034169c6..1dcbfe3a830 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -142,7 +142,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -155,7 +155,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index ce4e0895164..60904b58139 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -190,10 +190,6 @@ func setupNode( c.P2P.PeerID = ptr(p2pKey.PeerID()) c.P2P.TraceLogging = ptr(true) - // [P2P.V1] - // Enabled = false - c.P2P.V1.Enabled = ptr(false) - // [P2P.V2] // Enabled = true // AnnounceAddresses = ['$EXT_IP:17775'] diff --git a/core/utils/config/validate.go b/core/utils/config/validate.go index 3ed0ffbabba..32cb94b5205 100644 --- a/core/utils/config/validate.go +++ b/core/utils/config/validate.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" + "github.com/Masterminds/semver/v3" "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-relay/pkg/config" @@ -146,3 +147,16 @@ type ErrOverride struct { func (e ErrOverride) Error() string { return fmt.Sprintf("%s: overrides (duplicate keys or list elements) are not allowed for multiple secrets files", e.Name) } + +type ErrDeprecated struct { + Name string + Version semver.Version +} + +func (e ErrDeprecated) Error() string { + when := "a future version" + if e.Version != (semver.Version{}) { + when = fmt.Sprintf("version %s", e.Version) + } + return fmt.Sprintf("%s: is deprecated and will be removed in %s", e.Name, when) +} diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index e746d66777d..48d432138a8 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -142,7 +142,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -155,7 +155,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 5a815b2e012..4b53396b94c 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -148,7 +148,7 @@ PeerID = '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw' TraceLogging = true [P2P.V1] -Enabled = false +Enabled = true AnnounceIP = '1.2.3.4' AnnouncePort = 1234 BootstrapCheckInterval = '1m0s' @@ -161,7 +161,7 @@ NewStreamTimeout = '1s' PeerstoreWriteInterval = '1m0s' [P2P.V2] -Enabled = true +Enabled = false AnnounceAddresses = ['a', 'b', 'c'] DefaultBootstrappers = ['12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw@foo:42/bar:10', '12D3KooWMoejJznyDuEk5aX6GvbjaG12UzeornPCBNzMRqdwrFJw@test:99'] DeltaDial = '1m0s' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 89b034169c6..1dcbfe3a830 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -142,7 +142,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -155,7 +155,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 44d018769ec..daeddf2ce66 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -29,9 +29,15 @@ These will eventually replace `TelemetryIngress.URL` and `TelemetryIngress.Serve - LogPoller will now use finality tags to dynamically determine finality on evm chains if `UseFinalityTags=true`, rather than the fixed `FinalityDepth` specified in toml config -### Upcoming Required Configuration Change +### Changed + +- `P2P.V1` is now disabled (`Enabled = false`) by default. It must be explicitly enabled with `true` to be used. However, it is deprecated and will be removed in the future. +- `P2P.V2` is now enabled (`Enabled = true`) by default. -- Starting in 2.9.0, chainlink nodes will no longer allow `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey`. Any TOML configuration that sets this fields will prevent the node from booting. These fields will be replaced by `[[TelemetryIngress.Endpoints]]` +### Upcoming Required Configuration Changes +Starting in `v2.9.0`: +- `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` will no longer be allowed. Any TOML configuration that sets this fields will prevent the node from booting. These fields will be replaced by `[[TelemetryIngress.Endpoints]]` +- `P2P.V1` will no longer be supported and must not be set in TOML configuration in order to boot. Use `P2P.V2` instead. If you are using both, `V1` can simply be removed. ### Removed diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 1fc7d9b632f..da986e0500f 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -981,6 +981,8 @@ If both are configured, then for each link with another peer, V2 networking will automatically fall back to V1. If V2 starts working again later, it will automatically be preferred again. This is useful for migrating networks without downtime. Note that the two networking stacks _must not_ be configured to bind to the same IP/port. +Note: P2P.V1 is deprecated will be removed in the future. + All nodes in the OCR network should share the same networking stack. ### IncomingMessageBufferSize @@ -1017,7 +1019,7 @@ TraceLogging enables trace level logging. ## P2P.V1 ```toml [P2P.V1] -Enabled = true # Default +Enabled = false # Default AnnounceIP = '1.2.3.4' # Example AnnouncePort = 1337 # Example BootstrapCheckInterval = '20s' # Default @@ -1029,11 +1031,11 @@ ListenPort = 1337 # Example NewStreamTimeout = '10s' # Default PeerstoreWriteInterval = '5m' # Default ``` - +P2P.V1 is deprecated and will be removed in a future version. ### Enabled ```toml -Enabled = true # Default +Enabled = false # Default ``` Enabled enables P2P V1. @@ -1119,7 +1121,7 @@ PeerstoreWriteInterval controls how often the peerstore for the OCR V1 networkin ## P2P.V2 ```toml [P2P.V2] -Enabled = false # Default +Enabled = true # Default AnnounceAddresses = ['1.2.3.4:9999', '[a52d:0:a88:1274::abcd]:1337'] # Example DefaultBootstrappers = ['12D3KooWMHMRLQkgPbFSYHwD3NBuwtS1AmxhvKVUrcfyaGDASR4U@1.2.3.4:9999', '12D3KooWM55u5Swtpw9r8aFLQHEtw7HR4t44GdNs654ej5gRs2Dh@example.com:1234'] # Example DeltaDial = '15s' # Default @@ -1130,7 +1132,7 @@ ListenAddresses = ['1.2.3.4:9999', '[a52d:0:a88:1274::abcd]:1337'] # Example ### Enabled ```toml -Enabled = false # Default +Enabled = true # Default ``` Enabled enables P2P V2. Note: V1.Enabled is true by default, so it must be set false in order to run V2 only. diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index ed26caf4704..7f484fc69fc 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -37,7 +37,6 @@ Enabled = true [P2P] [P2P.V2] -Enabled = true AnnounceAddresses = ["0.0.0.0:6690"] ListenAddresses = ["0.0.0.0:6690"] [Keeper] diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 62f85d3256d..8697044aa79 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -37,7 +37,6 @@ Enabled = true [P2P] [P2P.V2] -Enabled = true AnnounceAddresses = ["0.0.0.0:6690"] ListenAddresses = ["0.0.0.0:6690"]` diff --git a/integration-tests/config/config.go b/integration-tests/config/config.go index cd3f5983a28..44c108b0d7f 100644 --- a/integration-tests/config/config.go +++ b/integration-tests/config/config.go @@ -4,6 +4,10 @@ var ( BaseOCRP2PV1Config = `[OCR] Enabled = true +[P2P] +[P2P.V2] +Enabled = false + [P2P] [P2P.V1] Enabled = true @@ -18,7 +22,6 @@ Enabled = true [P2P] [P2P.V2] -Enabled = true AnnounceAddresses = ["0.0.0.0:6690"] ListenAddresses = ["0.0.0.0:6690"]` @@ -67,7 +70,6 @@ CaptureEATelemetry = true [P2P] [P2P.V2] -Enabled = true ListenAddresses = ['0.0.0.0:6690']` TelemetryIngressConfig = `[TelemetryIngress] diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go index 1db030f1cce..4d875022ed2 100644 --- a/integration-tests/performance/ocr_test.go +++ b/integration-tests/performance/ocr_test.go @@ -102,6 +102,10 @@ func setupOCRTest(t *testing.T) (testEnvironment *environment.Environment, testN baseTOML := `[OCR] Enabled = true +[P2P] +[P2P.V2] +Enabled = false + [P2P] [P2P.V1] Enabled = true diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index a1260cc37ac..608144eafd0 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -9,6 +9,9 @@ import ( "time" "github.com/onsi/gomega" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-env/environment" "github.com/smartcontractkit/chainlink-env/pkg/cdk8s/blockscout" "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" @@ -17,8 +20,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -35,7 +36,6 @@ Enabled = true [P2P] [P2P.V2] -Enabled = true AnnounceAddresses = ["0.0.0.0:6690"] ListenAddresses = ["0.0.0.0:6690"]` networkTOML = `Enabled = true diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 3addac1b9d1..17373e6a95f 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1038,7 +1038,6 @@ func setupAutomationTestDocker( clNodeConfig.Keeper.TurnLookBack = it_utils.Ptr[int64](int64(0)) clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval clNodeConfig.Keeper.Registry.PerformGasOverhead = it_utils.Ptr[uint32](uint32(150000)) - clNodeConfig.P2P.V2.Enabled = it_utils.Ptr[bool](true) clNodeConfig.P2P.V2.AnnounceAddresses = &[]string{"0.0.0.0:6690"} clNodeConfig.P2P.V2.ListenAddresses = &[]string{"0.0.0.0:6690"} diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 21dbeb8753c..d42944fd558 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1098,7 +1098,7 @@ func setupKeeperTest(t *testing.T) ( contracts.LinkToken, *test_env.CLClusterTestEnv, ) { - clNodeConfig := node.NewConfig(node.NewBaseConfig()) + clNodeConfig := node.NewConfig(node.NewBaseConfig(), node.WithP2Pv1()) turnLookBack := int64(0) syncInterval := models.MustMakeDuration(5 * time.Second) performGasOverhead := uint32(150000) diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 966e270e518..37047cdb667 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -115,13 +115,14 @@ func WithP2Pv1() NodeConfigOpt { ListenIP: utils2.MustIP("0.0.0.0"), ListenPort: utils2.Ptr[uint16](6690), } + // disabled default + c.P2P.V2 = toml.P2PV2{Enabled: utils2.Ptr(false)} } } func WithP2Pv2() NodeConfigOpt { return func(c *chainlink.Config) { c.P2P.V2 = toml.P2PV2{ - Enabled: utils2.Ptr(true), ListenAddresses: &[]string{"0.0.0.0:6690"}, } } @@ -130,14 +131,14 @@ func WithP2Pv2() NodeConfigOpt { func WithTracing() NodeConfigOpt { return func(c *chainlink.Config) { c.Tracing = toml.Tracing{ - Enabled: utils2.Ptr(true), + Enabled: utils2.Ptr(true), CollectorTarget: utils2.Ptr("otel-collector:4317"), // ksortable unique id - NodeID: utils2.Ptr(ksuid.New().String()), - Attributes: map[string]string{ + NodeID: utils2.Ptr(ksuid.New().String()), + Attributes: map[string]string{ "env": "smoke", }, - SamplingRatio: utils2.Ptr(1.0), + SamplingRatio: utils2.Ptr(1.0), } } } diff --git a/integration-tests/types/config/node/defaults/sample.toml b/integration-tests/types/config/node/defaults/sample.toml index 3663998003c..b0e1bc2a07d 100644 --- a/integration-tests/types/config/node/defaults/sample.toml +++ b/integration-tests/types/config/node/defaults/sample.toml @@ -15,7 +15,6 @@ DefaultTransactionQueueDepth = 0 [P2P] [P2P.V2] -Enabled = true ListenAddresses = ['0.0.0.0:6690'] AnnounceAddresses = ['0.0.0.0:6690'] DeltaDial = '500ms' diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 06a623c9ca5..189476bfa84 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -154,7 +154,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -167,7 +167,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 1c0956b10d8..593aa0b21d0 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -198,7 +198,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -211,7 +211,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 19e180e0cff..7b8aa5e3836 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -198,7 +198,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -211,7 +211,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 062a21b1967..ef6548619e1 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -198,7 +198,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -211,7 +211,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 0fa16473812..87b877bc882 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -188,7 +188,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -201,7 +201,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 002c249fb7c..c607da10644 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -195,7 +195,7 @@ PeerID = '' TraceLogging = false [P2P.V1] -Enabled = true +Enabled = false AnnounceIP = '' AnnouncePort = 0 BootstrapCheckInterval = '20s' @@ -208,7 +208,7 @@ NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' [P2P.V2] -Enabled = false +Enabled = true AnnounceAddresses = [] DefaultBootstrappers = [] DeltaDial = '15s' diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar new file mode 100644 index 00000000000..ee7926f8f5f --- /dev/null +++ b/testdata/scripts/node/validate/warnings.txtar @@ -0,0 +1,279 @@ +exec chainlink node -c config.toml -s secrets.toml validate +cmp stdout out.txt + +-- config.toml -- +[P2P.V1] +Enabled = true +AnnounceIP = '' +AnnouncePort = 0 +BootstrapCheckInterval = '20s' +DefaultBootstrapPeers = [] +DHTAnnouncementCounterUserPrefix = 0 +DHTLookupInterval = 10 +ListenIP = '0.0.0.0' +ListenPort = 0 +NewStreamTimeout = '10s' +PeerstoreWriteInterval = '5m0s' + +-- secrets.toml -- +[Database] +URL = 'postgresql://user:pass1234567890abcd@localhost:5432/dbname?sslmode=disable' + +[Password] +Keystore = 'keystore_pass' + +-- out.txt -- +# Secrets: +[Database] +URL = 'xxxxx' +AllowSimplePasswords = false + +[Password] +Keystore = 'xxxxx' + +# Input Configuration: +[P2P] +[P2P.V1] +Enabled = true +AnnounceIP = '' +AnnouncePort = 0 +BootstrapCheckInterval = '20s' +DefaultBootstrapPeers = [] +DHTAnnouncementCounterUserPrefix = 0 +DHTLookupInterval = 10 +ListenIP = '0.0.0.0' +ListenPort = 0 +NewStreamTimeout = '10s' +PeerstoreWriteInterval = '5m0s' + +# Effective Configuration, with defaults applied: +InsecureFastScrypt = false +RootDir = '~/.chainlink' +ShutdownGracePeriod = '5s' + +[Feature] +FeedsManager = true +LogPoller = false +UICSAKeys = false + +[Database] +DefaultIdleInTxSessionTimeout = '1h0m0s' +DefaultLockTimeout = '15s' +DefaultQueryTimeout = '10s' +LogQueries = false +MaxIdleConns = 10 +MaxOpenConns = 20 +MigrateOnStartup = true + +[Database.Backup] +Dir = '' +Frequency = '1h0m0s' +Mode = 'none' +OnVersionUpgrade = true + +[Database.Listener] +MaxReconnectDuration = '10m0s' +MinReconnectInterval = '1m0s' +FallbackPollInterval = '30s' + +[Database.Lock] +Enabled = true +LeaseDuration = '10s' +LeaseRefreshInterval = '1s' + +[TelemetryIngress] +UniConn = true +Logging = false +BufferSize = 100 +MaxBatchSize = 50 +SendInterval = '500ms' +SendTimeout = '10s' +UseBatchSend = true +URL = '' +ServerPubKey = '' + +[AuditLogger] +Enabled = false +ForwardToUrl = '' +JsonWrapperKey = '' +Headers = [] + +[Log] +Level = 'info' +JSONConsole = false +UnixTS = false + +[Log.File] +Dir = '' +MaxSize = '5.12gb' +MaxAgeDays = 0 +MaxBackups = 1 + +[WebServer] +AllowOrigins = 'http://localhost:3000,http://localhost:6688' +BridgeResponseURL = '' +BridgeCacheTTL = '0s' +HTTPWriteTimeout = '10s' +HTTPPort = 6688 +SecureCookies = true +SessionTimeout = '15m0s' +SessionReaperExpiration = '240h0m0s' +HTTPMaxSize = '32.77kb' +StartTimeout = '15s' +ListenIP = '0.0.0.0' + +[WebServer.MFA] +RPID = '' +RPOrigin = '' + +[WebServer.RateLimit] +Authenticated = 1000 +AuthenticatedPeriod = '1m0s' +Unauthenticated = 5 +UnauthenticatedPeriod = '20s' + +[WebServer.TLS] +CertPath = '' +ForceRedirect = false +Host = '' +HTTPSPort = 6689 +KeyPath = '' +ListenIP = '0.0.0.0' + +[JobPipeline] +ExternalInitiatorsEnabled = false +MaxRunDuration = '10m0s' +MaxSuccessfulRuns = 10000 +ReaperInterval = '1h0m0s' +ReaperThreshold = '24h0m0s' +ResultWriteQueueDepth = 100 + +[JobPipeline.HTTPRequest] +DefaultTimeout = '15s' +MaxSize = '32.77kb' + +[FluxMonitor] +DefaultTransactionQueueDepth = 1 +SimulateTransactions = false + +[OCR2] +Enabled = false +ContractConfirmations = 3 +BlockchainTimeout = '20s' +ContractPollInterval = '1m0s' +ContractSubscribeInterval = '2m0s' +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' +CaptureEATelemetry = false +CaptureAutomationCustomTelemetry = false +DefaultTransactionQueueDepth = 1 +SimulateTransactions = false +TraceLogging = false + +[OCR] +Enabled = false +ObservationTimeout = '5s' +BlockchainTimeout = '20s' +ContractPollInterval = '1m0s' +ContractSubscribeInterval = '2m0s' +DefaultTransactionQueueDepth = 1 +KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' +SimulateTransactions = false +TransmitterAddress = '' +CaptureEATelemetry = false +TraceLogging = false + +[P2P] +IncomingMessageBufferSize = 10 +OutgoingMessageBufferSize = 10 +PeerID = '' +TraceLogging = false + +[P2P.V1] +Enabled = true +AnnounceIP = '' +AnnouncePort = 0 +BootstrapCheckInterval = '20s' +DefaultBootstrapPeers = [] +DHTAnnouncementCounterUserPrefix = 0 +DHTLookupInterval = 10 +ListenIP = '0.0.0.0' +ListenPort = 0 +NewStreamTimeout = '10s' +PeerstoreWriteInterval = '5m0s' + +[P2P.V2] +Enabled = true +AnnounceAddresses = [] +DefaultBootstrappers = [] +DeltaDial = '15s' +DeltaReconcile = '1m0s' +ListenAddresses = [] + +[Keeper] +DefaultTransactionQueueDepth = 1 +GasPriceBufferPercent = 20 +GasTipCapBufferPercent = 20 +BaseFeeBufferPercent = 20 +MaxGracePeriod = 100 +TurnLookBack = 1000 + +[Keeper.Registry] +CheckGasOverhead = 200000 +PerformGasOverhead = 300000 +MaxPerformDataSize = 5000 +SyncInterval = '30m0s' +SyncUpkeepQueueSize = 10 + +[AutoPprof] +Enabled = false +ProfileRoot = '' +PollInterval = '10s' +GatherDuration = '10s' +GatherTraceDuration = '5s' +MaxProfileSize = '100.00mb' +CPUProfileRate = 1 +MemProfileRate = 1 +BlockProfileRate = 1 +MutexProfileFraction = 1 +MemThreshold = '4.00gb' +GoroutineThreshold = 5000 + +[Pyroscope] +ServerAddress = '' +Environment = 'mainnet' + +[Sentry] +Debug = false +DSN = '' +Environment = '' +Release = '' + +[Insecure] +DevWebServer = false +OCRDevelopmentMode = false +InfiniteDepthQueries = false +DisableRateLimiting = false + +[Tracing] +Enabled = false +CollectorTarget = '' +NodeID = '' +SamplingRatio = 0.0 + +# Configuration warning: +2 errors: + - P2P.V1: is deprecated and will be removed in a future version + - P2P.V1: 10 errors: + - AnnounceIP: is deprecated and will be removed in a future version + - AnnouncePort: is deprecated and will be removed in a future version + - BootstrapCheckInterval: is deprecated and will be removed in a future version + - DefaultBootstrapPeers: is deprecated and will be removed in a future version + - DHTAnnouncementCounterUserPrefix: is deprecated and will be removed in a future version + - DHTLookupInterval: is deprecated and will be removed in a future version + - ListenIP: is deprecated and will be removed in a future version + - ListenPort: is deprecated and will be removed in a future version + - NewStreamTimeout: is deprecated and will be removed in a future version + - PeerstoreWriteInterval: is deprecated and will be removed in a future version +Valid configuration. From 7206a62a49f098c816172a1d4c1f51af04f24e15 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 27 Oct 2023 09:32:43 -0500 Subject: [PATCH 024/271] tools/docker: use longer password (#11098) --- tools/docker/.env | 2 +- tools/docker/dev-secrets.toml | 4 ---- tools/docker/develop.Dockerfile | 2 +- tools/docker/docker-compose.yaml | 8 ++------ 4 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 tools/docker/dev-secrets.toml diff --git a/tools/docker/.env b/tools/docker/.env index 32de93edb42..05f4314ff22 100644 --- a/tools/docker/.env +++ b/tools/docker/.env @@ -4,7 +4,7 @@ # Chainlink env vars CHAINLINK_DB_NAME=node_dev CL_DEV=true -CHAINLINK_PGPASSWORD=node +CHAINLINK_PGPASSWORD=thispasswordislongenough # Explorer env vars EXPLORER_DB_NAME=explorer_dev diff --git a/tools/docker/dev-secrets.toml b/tools/docker/dev-secrets.toml deleted file mode 100644 index b27b8a8a8e3..00000000000 --- a/tools/docker/dev-secrets.toml +++ /dev/null @@ -1,4 +0,0 @@ -# dev only credentials - -[Database] -AllowSimplePasswords = true diff --git a/tools/docker/develop.Dockerfile b/tools/docker/develop.Dockerfile index 46fad445d66..f663eaf1cf2 100644 --- a/tools/docker/develop.Dockerfile +++ b/tools/docker/develop.Dockerfile @@ -54,7 +54,7 @@ EXPOSE 8546 # Default env setup for testing ENV CHAINLINK_DB_NAME chainlink_test -ENV CHAINLINK_PGPASSWORD=node +ENV CHAINLINK_PGPASSWORD=thispasswordislongenough ENV CL_DATABASE_URL=postgresql://postgres:$CHAINLINK_PGPASSWORD@localhost:5432/$CHAINLINK_DB_NAME?sslmode=disable ENV TYPEORM_USERNAME=postgres ENV TYPEORM_PASSWORD=node diff --git a/tools/docker/docker-compose.yaml b/tools/docker/docker-compose.yaml index 4d3ef7def20..c01d3579356 100644 --- a/tools/docker/docker-compose.yaml +++ b/tools/docker/docker-compose.yaml @@ -10,7 +10,7 @@ services: # Note that the keystore import allows us to submit transactions # immediately because addresses are specified when starting the # parity/geth node to be prefunded with eth. - entrypoint: /bin/sh -c "chainlink -c /run/secrets/config -s /run/secrets/secrets node start -d -p /run/secrets/node_password -a /run/secrets/apicredentials" + entrypoint: /bin/sh -c "chainlink -c /run/secrets/config node start -d -p /run/secrets/node_password -a /run/secrets/apicredentials" restart: always environment: - CL_DATABASE_URL @@ -23,7 +23,6 @@ services: - apicredentials - keystore - config - - secrets node-2: container_name: chainlink-node-2 @@ -31,7 +30,7 @@ services: build: context: ../../ dockerfile: core/chainlink.Dockerfile - entrypoint: /bin/sh -c "chainlink -c /run/secrets/config -s /run/secrets/secrets node start -d -p /run/secrets/node_password -a /run/secrets/apicredentials" + entrypoint: /bin/sh -c "chainlink -c /run/secrets/config node start -d -p /run/secrets/node_password -a /run/secrets/apicredentials" restart: always environment: - CL_DATABASE_URL @@ -44,7 +43,6 @@ services: - apicredentials - keystore - config - - secrets # TODO # - replace clroot with secrets @@ -59,6 +57,4 @@ secrets: file: ../secrets/0xb90c7E3F7815F59EAD74e7543eB6D9E8538455D6.json config: file: config.toml - secrets: - file: dev-secrets.toml From c95d8f80832d7867daf98dd73ae2f4ad670b970c Mon Sep 17 00:00:00 2001 From: Lei Date: Fri, 27 Oct 2023 09:41:34 -0700 Subject: [PATCH 025/271] fix solhint issues under src/v0.8/automation (#11065) --- contracts/package.json | 2 +- contracts/src/v0.8/automation/AutomationBase.sol | 5 +++-- contracts/src/v0.8/automation/AutomationCompatible.sol | 4 ++-- contracts/src/v0.8/automation/Chainable.sol | 10 ++++++---- contracts/src/v0.8/automation/ExecutionPrevention.sol | 5 +++-- contracts/src/v0.8/automation/HeartbeatRequester.sol | 6 ++++-- contracts/src/v0.8/automation/UpkeepTranscoder.sol | 6 ++++-- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index 46b47440a6e..935c7901b6b 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 377 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 350 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", diff --git a/contracts/src/v0.8/automation/AutomationBase.sol b/contracts/src/v0.8/automation/AutomationBase.sol index d91780a79f7..8267fbc6a4e 100644 --- a/contracts/src/v0.8/automation/AutomationBase.sol +++ b/contracts/src/v0.8/automation/AutomationBase.sol @@ -8,7 +8,8 @@ contract AutomationBase { * @notice method that allows it to be simulated via eth_call by checking that * the sender is the zero address. */ - function preventExecution() internal view { + function _preventExecution() internal view { + // solhint-disable-next-line avoid-tx-origin if (tx.origin != address(0)) { revert OnlySimulatedBackend(); } @@ -19,7 +20,7 @@ contract AutomationBase { * that the sender is the zero address. */ modifier cannotExecute() { - preventExecution(); + _preventExecution(); _; } } diff --git a/contracts/src/v0.8/automation/AutomationCompatible.sol b/contracts/src/v0.8/automation/AutomationCompatible.sol index 5634956ea70..65332436842 100644 --- a/contracts/src/v0.8/automation/AutomationCompatible.sol +++ b/contracts/src/v0.8/automation/AutomationCompatible.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "./AutomationBase.sol"; -import "./interfaces/AutomationCompatibleInterface.sol"; +import {AutomationBase} from "./AutomationBase.sol"; +import {AutomationCompatibleInterface} from "./interfaces/AutomationCompatibleInterface.sol"; abstract contract AutomationCompatible is AutomationBase, AutomationCompatibleInterface {} diff --git a/contracts/src/v0.8/automation/Chainable.sol b/contracts/src/v0.8/automation/Chainable.sol index 1b446f013a0..29ac7796c4a 100644 --- a/contracts/src/v0.8/automation/Chainable.sol +++ b/contracts/src/v0.8/automation/Chainable.sol @@ -10,29 +10,31 @@ contract Chainable { /** * @dev addresses of the next contract in the chain **have to be immutable/constant** or the system won't work */ - address private immutable FALLBACK_ADDRESS; + address private immutable i_FALLBACK_ADDRESS; /** * @param fallbackAddress the address of the next contract in the chain */ constructor(address fallbackAddress) { - FALLBACK_ADDRESS = fallbackAddress; + i_FALLBACK_ADDRESS = fallbackAddress; } /** * @notice returns the address of the next contract in the chain */ function fallbackTo() external view returns (address) { - return FALLBACK_ADDRESS; + return i_FALLBACK_ADDRESS; } /** * @notice the fallback function routes the call to the next contract in the chain * @dev most of the implementation is copied directly from OZ's Proxy contract */ + // solhint-disable payable-fallback + // solhint-disable-next-line no-complex-fallback fallback() external { // copy to memory for assembly access - address next = FALLBACK_ADDRESS; + address next = i_FALLBACK_ADDRESS; // copied directly from OZ's Proxy contract assembly { // Copy msg.data. We take full control of memory in this inline assembly diff --git a/contracts/src/v0.8/automation/ExecutionPrevention.sol b/contracts/src/v0.8/automation/ExecutionPrevention.sol index a8baf55fd22..30a823c4b8d 100644 --- a/contracts/src/v0.8/automation/ExecutionPrevention.sol +++ b/contracts/src/v0.8/automation/ExecutionPrevention.sol @@ -8,7 +8,8 @@ abstract contract ExecutionPrevention { * @notice method that allows it to be simulated via eth_call by checking that * the sender is the zero address. */ - function preventExecution() internal view { + function _preventExecution() internal view { + // solhint-disable-next-line avoid-tx-origin if (tx.origin != address(0)) { revert OnlySimulatedBackend(); } @@ -19,7 +20,7 @@ abstract contract ExecutionPrevention { * that the sender is the zero address. */ modifier cannotExecute() { - preventExecution(); + _preventExecution(); _; } } diff --git a/contracts/src/v0.8/automation/HeartbeatRequester.sol b/contracts/src/v0.8/automation/HeartbeatRequester.sol index d5802a79580..aa390738001 100644 --- a/contracts/src/v0.8/automation/HeartbeatRequester.sol +++ b/contracts/src/v0.8/automation/HeartbeatRequester.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MIT +// solhint-disable-next-line one-contract-per-file pragma solidity 0.8.6; -import "./../interfaces/TypeAndVersionInterface.sol"; -import "../shared/access/ConfirmedOwner.sol"; +import {TypeAndVersionInterface} from "./../interfaces/TypeAndVersionInterface.sol"; +import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; // defines some interfaces for type safety and reduces encoding/decoding // does not use the full interfaces intentionally because the requester only uses a fraction of them @@ -32,6 +33,7 @@ contract HeartbeatRequester is TypeAndVersionInterface, ConfirmedOwner { * - HeartbeatRequester 1.0.0: The requester fetches the latest aggregator address from proxy, and request a new round * using the aggregator address. */ + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables string public constant override typeAndVersion = "HeartbeatRequester 1.0.0"; constructor() ConfirmedOwner(msg.sender) {} diff --git a/contracts/src/v0.8/automation/UpkeepTranscoder.sol b/contracts/src/v0.8/automation/UpkeepTranscoder.sol index 450da8c14a1..144a96c7e77 100644 --- a/contracts/src/v0.8/automation/UpkeepTranscoder.sol +++ b/contracts/src/v0.8/automation/UpkeepTranscoder.sol @@ -2,8 +2,9 @@ pragma solidity ^0.8.0; -import "./interfaces/UpkeepTranscoderInterface.sol"; -import "../interfaces/TypeAndVersionInterface.sol"; +import {UpkeepTranscoderInterface} from "./interfaces/UpkeepTranscoderInterface.sol"; +import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; +import {UpkeepFormat} from "./UpkeepFormat.sol"; /** * @notice Transcoder for converting upkeep data from one keeper @@ -16,6 +17,7 @@ contract UpkeepTranscoder is UpkeepTranscoderInterface, TypeAndVersionInterface * @notice versions: * - UpkeepTranscoder 1.0.0: placeholder to allow new formats in the future */ + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables string public constant override typeAndVersion = "UpkeepTranscoder 1.0.0"; /** From 2df49da68def10542e9f885aa8e8435f807ba2b8 Mon Sep 17 00:00:00 2001 From: Erik Burton Date: Sat, 28 Oct 2023 04:01:16 -0700 Subject: [PATCH 026/271] [RE-2009] Bump action references (#11115) * Bump docker/setup-buildx-action from 2.7.0 to 3.0.0 * Bump docker/build-push-action from 3.2.0 to 5.0.0 * Bump aws-actions/configure-aws-credentials from 3.0.2 to 4.0.1 --- .github/actions/build-sign-publish-chainlink/action.yml | 6 +++--- .github/actions/goreleaser-build-sign-publish/action.yml | 2 +- .github/workflows/helm-publish.yml | 2 +- .github/workflows/performance-tests.yml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml index bd633bced74..55c682bc8d9 100644 --- a/.github/actions/build-sign-publish-chainlink/action.yml +++ b/.github/actions/build-sign-publish-chainlink/action.yml @@ -112,7 +112,7 @@ runs: registry: ${{ inputs.ecr-hostname }} - name: Setup Docker Buildx - uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0 + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Generate docker metadata for root image id: meta-root @@ -128,7 +128,7 @@ runs: - name: Build and push root docker image id: buildpush-root - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 # v3.2.0 + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: push: ${{ inputs.publish }} context: . @@ -161,7 +161,7 @@ runs: - name: Build and push non-root docker image id: buildpush-nonroot - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 # v3.2.0 + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: push: ${{ inputs.publish }} context: . diff --git a/.github/actions/goreleaser-build-sign-publish/action.yml b/.github/actions/goreleaser-build-sign-publish/action.yml index 0cc144564c0..845d2443fc1 100644 --- a/.github/actions/goreleaser-build-sign-publish/action.yml +++ b/.github/actions/goreleaser-build-sign-publish/action.yml @@ -65,7 +65,7 @@ runs: using: composite steps: - name: Setup docker buildx - uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0 + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Set up qemu uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 - name: Setup go diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-publish.yml index 48a7060fc77..6ea46e6a52d 100644 --- a/.github/workflows/helm-publish.yml +++ b/.github/workflows/helm-publish.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure aws credentials - uses: aws-actions/configure-aws-credentials@50ac8dd1e1b10d09dac7b8727528b91bed831ac0 # v3.0.2 + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN_GATI }} role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS }} diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 277940dc2d2..87fb75beca8 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -29,9 +29,9 @@ jobs: id: login-ecr uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@ecf95283f03858871ff00b787d79c419715afc34 # v2.7.0 + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Build and Push - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 # v3.2.0 + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: context: . file: core/chainlink.Dockerfile From b6550805131cf86361250eb986e869348b3a490d Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 30 Oct 2023 07:48:36 -0500 Subject: [PATCH 027/271] skipped test cleanup (#11026) --- core/internal/testutils/testutils.go | 4 ++++ .../evm21/logprovider/integration_test.go | 20 +++++++++---------- .../internal/ocr2vrf_integration_test.go | 4 ++-- .../uni_client_integration_test.go | 5 +++-- .../vrf/v2/integration_v2_plus_test.go | 4 +--- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index 938d814b9eb..79c86f0c5f8 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -451,3 +451,7 @@ func MustDecodeBase64(s string) (b []byte) { } return } + +func SkipFlakey(t *testing.T, ticketURL string) { + t.Skip("Flakey", ticketURL) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index 506dcb9ea33..811468746e3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -455,9 +455,7 @@ func TestIntegration_LogEventProvider_RateLimit(t *testing.T) { } func TestIntegration_LogRecoverer_Backfill(t *testing.T) { - t.Skip() // TODO: remove skip after removing constant timeouts - ctx, cancel := context.WithTimeout(testutils.Context(t), time.Second*60) - defer cancel() + ctx := testutils.Context(t) backend, stopMining, accounts := setupBackend(t) defer stopMining() @@ -515,21 +513,21 @@ func TestIntegration_LogRecoverer_Backfill(t *testing.T) { }() defer recoverer.Close() - lctx, lcancel := context.WithTimeout(ctx, time.Second*15) - defer lcancel() var allProposals []ocr2keepers.UpkeepPayload - for lctx.Err() == nil { + for { poll(backend.Commit()) proposals, err := recoverer.GetRecoveryProposals(ctx) require.NoError(t, err) allProposals = append(allProposals, proposals...) - if len(allProposals) < n { - time.Sleep(100 * time.Millisecond) - continue + if len(allProposals) >= n { + break // success + } + select { + case <-ctx.Done(): + t.Fatalf("could not recover logs before timeout: %s", ctx.Err()) + case <-time.After(100 * time.Millisecond): } - break } - require.NoError(t, lctx.Err(), "could not recover logs before timeout") } func collectPayloads(ctx context.Context, t *testing.T, logProvider logprovider.LogEventProvider, n, rounds int) []ocr2keepers.UpkeepPayload { diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index b9a4d500026..cf7a408725d 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -326,12 +326,12 @@ func setupNodeOCR2( } func TestIntegration_OCR2VRF_ForwarderFlow(t *testing.T) { - t.Skip() + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/VRF-688") runOCR2VRFTest(t, true) } func TestIntegration_OCR2VRF(t *testing.T) { - t.Skip() + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/VRF-688") runOCR2VRFTest(t, false) } diff --git a/core/services/synchronization/uni_client_integration_test.go b/core/services/synchronization/uni_client_integration_test.go index 1ad2865669e..fcc0dc23717 100644 --- a/core/services/synchronization/uni_client_integration_test.go +++ b/core/services/synchronization/uni_client_integration_test.go @@ -6,16 +6,17 @@ import ( "testing" "time" - "github.com/smartcontractkit/wsrpc" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/wsrpc" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" ) func TestUniClient(t *testing.T) { - t.Skip() + t.Skip("Incomplete", "https://smartcontract-it.atlassian.net/browse/BCF-2729") privKey, err := hex.DecodeString("TODO") require.NoError(t, err) pubKey, err := hex.DecodeString("TODO") diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 9adf47f256b..f08c10c2004 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -486,9 +486,7 @@ func TestVRFV2PlusIntegration_SingleConsumer_EOA_Request_Batching_Enabled(t *tes } func TestVRFV2PlusIntegration_SingleConsumer_EIP150_HappyPath(t *testing.T) { - // See: https://smartcontract-it.atlassian.net/browse/VRF-589 - // Temporarily skipping to figure out issue with test - t.Skip() + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/VRF-589") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) From db64df96ba3ab987a27d684b8161c35fcaa569b8 Mon Sep 17 00:00:00 2001 From: Cedric Date: Mon, 30 Oct 2023 14:05:31 +0000 Subject: [PATCH 028/271] [BCF-2630] Add pipeline runner wrapper (#11091) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../generic/pipeline_runner_adapter.go | 94 ++++++++++++ .../generic/pipeline_runner_adapter_test.go | 144 ++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 8 files changed, 247 insertions(+), 9 deletions(-) create mode 100644 core/services/ocr2/plugins/generic/pipeline_runner_adapter.go create mode 100644 core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c8b616a4b6e..f097ea89be5 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -302,7 +302,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index c4d0575bb20..0461daa25e9 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1458,8 +1458,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 h1:fFD5SgSJtnXvkGLK3CExNKpUIz4sGrNNkKv3Ljw63Hk= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go new file mode 100644 index 00000000000..5c58522f409 --- /dev/null +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go @@ -0,0 +1,94 @@ +package generic + +import ( + "context" + "time" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/store/models" +) + +var _ types.PipelineRunnerService = (*PipelineRunnerAdapter)(nil) + +type pipelineRunner interface { + ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) +} + +type PipelineRunnerAdapter struct { + runner pipelineRunner + job job.Job + logger logger.Logger +} + +func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) ([]types.TaskResult, error) { + s := pipeline.Spec{ + DotDagSource: spec, + CreatedAt: time.Now(), + MaxTaskDuration: models.Interval(options.MaxTaskDuration), + JobID: p.job.ID, + JobName: p.job.Name.ValueOrZero(), + JobType: string(p.job.Type), + } + + defaultVars := map[string]interface{}{ + "jb": map[string]interface{}{ + "databaseID": p.job.ID, + "externalJobID": p.job.ExternalJobID, + "name": p.job.Name.ValueOrZero(), + }, + } + + err := merge(defaultVars, vars.Vars) + if err != nil { + return nil, err + } + + finalVars := pipeline.NewVarsFrom(defaultVars) + _, trrs, err := p.runner.ExecuteRun(ctx, s, finalVars, p.logger) + if err != nil { + return nil, err + } + + taskResults := make([]types.TaskResult, len(trrs)) + for i, trr := range trrs { + taskResults[i] = types.TaskResult{ + ID: trr.ID.String(), + Type: string(trr.Task.Type()), + Value: trr.Result.Value, + Error: trr.Result.Error, + Index: int(trr.TaskRun.Index), + } + } + return taskResults, nil +} + +func NewPipelineRunnerAdapter(logger logger.Logger, job job.Job, runner pipelineRunner) *PipelineRunnerAdapter { + return &PipelineRunnerAdapter{ + logger: logger, + job: job, + runner: runner, + } +} + +// merge merges mapTwo into mapOne, modifying mapOne in the process. +func merge(mapOne, mapTwo map[string]interface{}) error { + for k, v := range mapTwo { + // if `mapOne` doesn't have `k`, then nothing to do, just assign v to `mapOne`. + if _, ok := mapOne[k]; !ok { + mapOne[k] = v + } else { + vAsMap, vOK := v.(map[string]interface{}) + mapOneVAsMap, moOK := mapOne[k].(map[string]interface{}) + if vOK && moOK { + merge(mapOneVAsMap, vAsMap) + } else { + mapOne[k] = v + } + } + } + + return nil +} diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go new file mode 100644 index 00000000000..d1f06d87662 --- /dev/null +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go @@ -0,0 +1,144 @@ +package generic + +import ( + "context" + "net/http" + "reflect" + "testing" + "time" + + "github.com/google/uuid" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" + _ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +const spec = ` +answer [type=sum values=<[ $(val), 2 ]>] +answer; +` + +func TestAdapter_Integration(t *testing.T) { + logger := logger.TestLogger(t) + cfg := configtest.NewTestGeneralConfig(t) + url := cfg.Database().URL() + db, err := pg.NewConnection(url.String(), cfg.Database().Dialect(), cfg.Database()) + require.NoError(t, err) + + keystore := keystore.NewInMemory(db, utils.FastScryptParams, logger, cfg.Database()) + pipelineORM := pipeline.NewORM(db, logger, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) + bridgesORM := bridges.NewORM(db, logger, cfg.Database()) + pr := pipeline.NewRunner( + pipelineORM, + bridgesORM, + cfg.JobPipeline(), + cfg.WebServer(), + nil, + keystore.Eth(), + keystore.VRF(), + logger, + http.DefaultClient, + http.DefaultClient, + ) + pra := NewPipelineRunnerAdapter(logger, job.Job{}, pr) + results, err := pra.ExecuteRun(context.Background(), spec, types.Vars{Vars: map[string]interface{}{"val": 1}}, types.Options{}) + require.NoError(t, err) + + finalResult := results[0].Value.(decimal.Decimal) + + assert.True(t, decimal.NewFromInt(3).Equal(finalResult)) +} + +func newMockPipelineRunner() *mockPipelineRunner { + return &mockPipelineRunner{} +} + +type mockPipelineRunner struct { + results pipeline.TaskRunResults + err error + run *pipeline.Run + spec pipeline.Spec + vars pipeline.Vars +} + +func (m *mockPipelineRunner) ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (*pipeline.Run, pipeline.TaskRunResults, error) { + m.spec = spec + m.vars = vars + return m.run, m.results, m.err +} + +func TestAdapter_AddsDefaultVars(t *testing.T) { + logger := logger.TestLogger(t) + mpr := newMockPipelineRunner() + jobID, externalJobID, name := int32(100), uuid.New(), null.StringFrom("job-name") + pra := NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name}, mpr) + + _, err := pra.ExecuteRun(context.Background(), spec, types.Vars{}, types.Options{}) + require.NoError(t, err) + + gotName, err := mpr.vars.Get("jb.name") + require.NoError(t, err) + assert.Equal(t, name.String, gotName) + + gotID, err := mpr.vars.Get("jb.databaseID") + require.NoError(t, err) + assert.Equal(t, jobID, gotID) + + gotExternalID, err := mpr.vars.Get("jb.externalJobID") + require.NoError(t, err) + assert.Equal(t, externalJobID, gotExternalID) +} + +func TestPipelineRunnerAdapter_SetsVarsOnSpec(t *testing.T) { + logger := logger.TestLogger(t) + mpr := newMockPipelineRunner() + jobID, externalJobID, name, jobType := int32(100), uuid.New(), null.StringFrom("job-name"), job.Type("generic") + pra := NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name, Type: jobType}, mpr) + + maxDuration := time.Duration(100 * time.Second) + _, err := pra.ExecuteRun(context.Background(), spec, types.Vars{}, types.Options{MaxTaskDuration: maxDuration}) + require.NoError(t, err) + + assert.Equal(t, jobID, mpr.spec.JobID) + assert.Equal(t, name.ValueOrZero(), mpr.spec.JobName) + assert.Equal(t, string(jobType), mpr.spec.JobType) + assert.Equal(t, maxDuration, mpr.spec.MaxTaskDuration.Duration()) + +} + +func TestMerge(t *testing.T) { + vars := map[string]interface{}{ + "jb": map[string]interface{}{ + "databaseID": "some-job-id", + }, + } + addedVars := map[string]interface{}{ + "jb": map[string]interface{}{ + "some-other-var": "foo", + }, + "val": 0, + } + + err := merge(vars, addedVars) + require.NoError(t, err) + + assert.True(t, reflect.DeepEqual(vars, map[string]interface{}{ + "jb": map[string]interface{}{ + "databaseID": "some-job-id", + "some-other-var": "foo", + }, + "val": 0, + }), vars) +} diff --git a/go.mod b/go.mod index df970160acc..3679c79ed03 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index 59286787c2e..e2bc6610488 100644 --- a/go.sum +++ b/go.sum @@ -1459,8 +1459,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 h1:fFD5SgSJtnXvkGLK3CExNKpUIz4sGrNNkKv3Ljw63Hk= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 7683a966920..d3e09d94d7c 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -385,7 +385,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index d280310b9f2..725e05914cf 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2364,8 +2364,8 @@ github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc4 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= github.com/smartcontractkit/chainlink-env v0.38.3 h1:ZtOnwkG622R0VCTxL5V09AnT/QXhlFwkGTjd0Lsfpfg= github.com/smartcontractkit/chainlink-env v0.38.3/go.mod h1:7z4sw/hN8TxioQCLwFqQdhK3vaOV0a22Qe99z4bRUcg= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9 h1:fFD5SgSJtnXvkGLK3CExNKpUIz4sGrNNkKv3Ljw63Hk= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231020230319-2ede955d1dc9/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From e242dfb9ae44a36ef05bb7b09f75fce0f415e308 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Oct 2023 10:17:18 -0400 Subject: [PATCH 029/271] Every instance of mercury transmitter should not load reports for all feeds on startup (#10829) * Every instance of mercury transmitter should not load reports for all feeds on startup * Fix a few persistence manager bugs * Bump Migration version --- core/services/relay/evm/mercury/orm.go | 38 +++++---- core/services/relay/evm/mercury/orm_test.go | 48 +++++++----- .../relay/evm/mercury/persistence_manager.go | 4 +- .../evm/mercury/persistence_manager_test.go | 78 ++++++++++++++----- .../services/relay/evm/mercury/transmitter.go | 2 +- .../relay/evm/mercury/transmitter_test.go | 4 + ...d_feed_id_to_mercury_transmit_requests.sql | 14 ++++ 7 files changed, 130 insertions(+), 58 deletions(-) create mode 100644 core/store/migrate/migrations/0205_add_feed_id_to_mercury_transmit_requests.sql diff --git a/core/services/relay/evm/mercury/orm.go b/core/services/relay/evm/mercury/orm.go index dd7d7b33e74..7273519f6b6 100644 --- a/core/services/relay/evm/mercury/orm.go +++ b/core/services/relay/evm/mercury/orm.go @@ -23,8 +23,8 @@ import ( type ORM interface { InsertTransmitRequest(req *pb.TransmitRequest, jobID int32, reportCtx ocrtypes.ReportContext, qopts ...pg.QOpt) error DeleteTransmitRequests(reqs []*pb.TransmitRequest, qopts ...pg.QOpt) error - GetTransmitRequests(qopts ...pg.QOpt) ([]*Transmission, error) - PruneTransmitRequests(maxSize int, qopts ...pg.QOpt) error + GetTransmitRequests(jobID int32, qopts ...pg.QOpt) ([]*Transmission, error) + PruneTransmitRequests(jobID int32, maxSize int, qopts ...pg.QOpt) error LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg.QOpt) (report []byte, err error) } @@ -49,6 +49,11 @@ func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) ORM { // InsertTransmitRequest inserts one transmit request if the payload does not exist already. func (o *orm) InsertTransmitRequest(req *pb.TransmitRequest, jobID int32, reportCtx ocrtypes.ReportContext, qopts ...pg.QOpt) error { + feedID, err := FeedIDFromReport(req.Payload) + if err != nil { + return err + } + q := o.q.WithOpts(qopts...) var wg sync.WaitGroup wg.Add(2) @@ -57,16 +62,12 @@ func (o *orm) InsertTransmitRequest(req *pb.TransmitRequest, jobID int32, report go func() { defer wg.Done() err1 = q.ExecQ(` - INSERT INTO mercury_transmit_requests (payload, payload_hash, config_digest, epoch, round, extra_hash, job_id) - VALUES ($1, $2, $3, $4, $5, $6, $7) + INSERT INTO mercury_transmit_requests (payload, payload_hash, config_digest, epoch, round, extra_hash, job_id, feed_id) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (payload_hash) DO NOTHING - `, req.Payload, hashPayload(req.Payload), reportCtx.ConfigDigest[:], reportCtx.Epoch, reportCtx.Round, reportCtx.ExtraHash[:], jobID) + `, req.Payload, hashPayload(req.Payload), reportCtx.ConfigDigest[:], reportCtx.Epoch, reportCtx.Round, reportCtx.ExtraHash[:], jobID, feedID[:]) }() - feedID, err := FeedIDFromReport(req.Payload) - if err != nil { - return err - } go func() { defer wg.Done() err2 = q.ExecQ(` @@ -101,15 +102,16 @@ func (o *orm) DeleteTransmitRequests(reqs []*pb.TransmitRequest, qopts ...pg.QOp } // GetTransmitRequests returns all transmit requests in chronologically descending order. -func (o *orm) GetTransmitRequests(qopts ...pg.QOpt) ([]*Transmission, error) { +func (o *orm) GetTransmitRequests(jobID int32, qopts ...pg.QOpt) ([]*Transmission, error) { q := o.q.WithOpts(qopts...) // The priority queue uses epoch and round to sort transmissions so order by // the same fields here for optimal insertion into the pq. rows, err := q.QueryContext(q.ParentCtx, ` SELECT payload, config_digest, epoch, round, extra_hash FROM mercury_transmit_requests + WHERE job_id = $1 ORDER BY epoch DESC, round DESC - `) + `, jobID) if err != nil { return nil, err } @@ -142,20 +144,22 @@ func (o *orm) GetTransmitRequests(qopts ...pg.QOpt) ([]*Transmission, error) { return transmissions, nil } -// PruneTransmitRequests keeps at most maxSize rows in the table, deleting the -// oldest transactions. -func (o *orm) PruneTransmitRequests(maxSize int, qopts ...pg.QOpt) error { +// PruneTransmitRequests keeps at most maxSize rows for the given job ID, +// deleting the oldest transactions. +func (o *orm) PruneTransmitRequests(jobID int32, maxSize int, qopts ...pg.QOpt) error { q := o.q.WithOpts(qopts...) // Prune the oldest requests by epoch and round. return q.ExecQ(` DELETE FROM mercury_transmit_requests - WHERE payload_hash NOT IN ( + WHERE job_id = $1 AND + payload_hash NOT IN ( SELECT payload_hash FROM mercury_transmit_requests + WHERE job_id = $1 ORDER BY epoch DESC, round DESC - LIMIT $1 + LIMIT $2 ) - `, maxSize) + `, jobID, maxSize) } func (o *orm) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg.QOpt) (report []byte, err error) { diff --git a/core/services/relay/evm/mercury/orm_test.go b/core/services/relay/evm/mercury/orm_test.go index a6a72327677..56dea70417b 100644 --- a/core/services/relay/evm/mercury/orm_test.go +++ b/core/services/relay/evm/mercury/orm_test.go @@ -3,6 +3,7 @@ package mercury import ( "testing" + "github.com/cometbft/cometbft/libs/rand" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -16,7 +17,7 @@ import ( func TestORM(t *testing.T) { db := pgtest.NewSqlxDB(t) - var jobID int32 // foreign key constraints disabled so can leave as 0 + jobID := rand.Int32() // foreign key constraints disabled so value doesn't matter pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) lggr := logger.TestLogger(t) @@ -48,7 +49,7 @@ func TestORM(t *testing.T) { err = orm.InsertTransmitRequest(&pb.TransmitRequest{Payload: reports[2]}, jobID, reportContexts[2]) require.NoError(t, err) - transmissions, err := orm.GetTransmitRequests() + transmissions, err := orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[2]}, ReportCtx: reportContexts[2]}, @@ -65,7 +66,7 @@ func TestORM(t *testing.T) { err = orm.DeleteTransmitRequests([]*pb.TransmitRequest{{Payload: reports[1]}}) require.NoError(t, err) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[2]}, ReportCtx: reportContexts[2]}, @@ -80,7 +81,7 @@ func TestORM(t *testing.T) { err = orm.DeleteTransmitRequests([]*pb.TransmitRequest{{Payload: []byte("does-not-exist")}}) require.NoError(t, err) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[2]}, ReportCtx: reportContexts[2]}, @@ -98,7 +99,7 @@ func TestORM(t *testing.T) { require.NoError(t, err) assert.Equal(t, reports[2], l) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Empty(t, transmissions) @@ -106,7 +107,7 @@ func TestORM(t *testing.T) { err = orm.InsertTransmitRequest(&pb.TransmitRequest{Payload: reports[3]}, jobID, reportContexts[3]) require.NoError(t, err) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[3]}, ReportCtx: reportContexts[3]}, @@ -118,7 +119,7 @@ func TestORM(t *testing.T) { err = orm.InsertTransmitRequest(&pb.TransmitRequest{Payload: reports[3]}, jobID, reportContexts[3]) require.NoError(t, err) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[3]}, ReportCtx: reportContexts[3]}, @@ -131,7 +132,7 @@ func TestORM(t *testing.T) { func TestORM_PruneTransmitRequests(t *testing.T) { db := pgtest.NewSqlxDB(t) - var jobID int32 // foreign key constraints disabled so can leave as 0 + jobID := rand.Int32() // foreign key constraints disabled so value doesn't matter pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) @@ -157,10 +158,10 @@ func TestORM_PruneTransmitRequests(t *testing.T) { require.NoError(t, err) // Max size greater than table size, expect no-op - err = orm.PruneTransmitRequests(5) + err = orm.PruneTransmitRequests(jobID, 5) require.NoError(t, err) - transmissions, err := orm.GetTransmitRequests() + transmissions, err := orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[1]}, ReportCtx: makeReportContext(1, 2)}, @@ -168,37 +169,48 @@ func TestORM_PruneTransmitRequests(t *testing.T) { }) // Max size equal to table size, expect no-op - err = orm.PruneTransmitRequests(2) + err = orm.PruneTransmitRequests(jobID, 2) require.NoError(t, err) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) require.Equal(t, transmissions, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[1]}, ReportCtx: makeReportContext(1, 2)}, {Req: &pb.TransmitRequest{Payload: reports[0]}, ReportCtx: makeReportContext(1, 1)}, }) + // Max size is table size + 1, but jobID differs, expect no-op + err = orm.PruneTransmitRequests(-1, 2) + require.NoError(t, err) + + transmissions, err = orm.GetTransmitRequests(jobID) + require.NoError(t, err) + require.Equal(t, []*Transmission{ + {Req: &pb.TransmitRequest{Payload: reports[1]}, ReportCtx: makeReportContext(1, 2)}, + {Req: &pb.TransmitRequest{Payload: reports[0]}, ReportCtx: makeReportContext(1, 1)}, + }, transmissions) + err = orm.InsertTransmitRequest(&pb.TransmitRequest{Payload: reports[2]}, jobID, makeReportContext(2, 1)) require.NoError(t, err) err = orm.InsertTransmitRequest(&pb.TransmitRequest{Payload: reports[3]}, jobID, makeReportContext(2, 2)) require.NoError(t, err) - // Max size is table size + 1, expect the oldest row to be pruned. - err = orm.PruneTransmitRequests(3) + // Max size is table size - 1, expect the oldest row to be pruned. + err = orm.PruneTransmitRequests(jobID, 3) require.NoError(t, err) - transmissions, err = orm.GetTransmitRequests() + transmissions, err = orm.GetTransmitRequests(jobID) require.NoError(t, err) - require.Equal(t, transmissions, []*Transmission{ + require.Equal(t, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[3]}, ReportCtx: makeReportContext(2, 2)}, {Req: &pb.TransmitRequest{Payload: reports[2]}, ReportCtx: makeReportContext(2, 1)}, {Req: &pb.TransmitRequest{Payload: reports[1]}, ReportCtx: makeReportContext(1, 2)}, - }) + }, transmissions) } func TestORM_InsertTransmitRequest_LatestReport(t *testing.T) { db := pgtest.NewSqlxDB(t) - var jobID int32 // foreign key constraints disabled so can leave as 0 + jobID := rand.Int32() // foreign key constraints disabled so value doesn't matter pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) diff --git a/core/services/relay/evm/mercury/persistence_manager.go b/core/services/relay/evm/mercury/persistence_manager.go index 9e8df72a155..1c8dad45301 100644 --- a/core/services/relay/evm/mercury/persistence_manager.go +++ b/core/services/relay/evm/mercury/persistence_manager.go @@ -78,7 +78,7 @@ func (pm *PersistenceManager) AsyncDelete(req *pb.TransmitRequest) { } func (pm *PersistenceManager) Load(ctx context.Context) ([]*Transmission, error) { - return pm.orm.GetTransmitRequests(pg.WithParentCtx(ctx)) + return pm.orm.GetTransmitRequests(pm.jobID, pg.WithParentCtx(ctx)) } func (pm *PersistenceManager) runFlushDeletesLoop() { @@ -118,7 +118,7 @@ func (pm *PersistenceManager) runPruneLoop() { ticker.Stop() return case <-ticker.C: - if err := pm.orm.PruneTransmitRequests(pm.maxTransmitQueueSize, pg.WithParentCtx(ctx), pg.WithLongQueryTimeout()); err != nil { + if err := pm.orm.PruneTransmitRequests(pm.jobID, pm.maxTransmitQueueSize, pg.WithParentCtx(ctx), pg.WithLongQueryTimeout()); err != nil { pm.lggr.Errorw("Failed to prune transmit requests table", "err", err) } else { pm.lggr.Debugw("Pruned transmit requests table") diff --git a/core/services/relay/evm/mercury/persistence_manager_test.go b/core/services/relay/evm/mercury/persistence_manager_test.go index 97628ed9c2b..d185a64a8f1 100644 --- a/core/services/relay/evm/mercury/persistence_manager_test.go +++ b/core/services/relay/evm/mercury/persistence_manager_test.go @@ -5,7 +5,10 @@ import ( "testing" "time" + "github.com/cometbft/cometbft/libs/rand" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/sqlx" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" @@ -16,19 +19,22 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) -func bootstrapPersistenceManager(t *testing.T) (*PersistenceManager, *observer.ObservedLogs) { +func bootstrapPersistenceManager(t *testing.T, jobID int32, db *sqlx.DB) (*PersistenceManager, *observer.ObservedLogs) { t.Helper() - db := pgtest.NewSqlxDB(t) - pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) - pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.DebugLevel) orm := NewORM(db, lggr, pgtest.NewQConfig(true)) - return NewPersistenceManager(lggr, orm, 0, 2, 5*time.Millisecond, 5*time.Millisecond), observedLogs + return NewPersistenceManager(lggr, orm, jobID, 2, 5*time.Millisecond, 5*time.Millisecond), observedLogs } func TestPersistenceManager(t *testing.T) { + jobID1 := rand.Int32() + jobID2 := jobID1 + 1 + ctx := context.Background() - pm, _ := bootstrapPersistenceManager(t) + db := pgtest.NewSqlxDB(t) + pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) + pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) + pm, _ := bootstrapPersistenceManager(t, jobID1, db) reports := sampleReports @@ -52,11 +58,23 @@ func TestPersistenceManager(t *testing.T) { require.Equal(t, []*Transmission{ {Req: &pb.TransmitRequest{Payload: reports[1]}}, }, transmissions) + + t.Run("scopes load to only transmissions with matching job ID", func(t *testing.T) { + pm2, _ := bootstrapPersistenceManager(t, jobID2, db) + transmissions, err = pm2.Load(ctx) + require.NoError(t, err) + + assert.Len(t, transmissions, 0) + }) } func TestPersistenceManagerAsyncDelete(t *testing.T) { ctx := context.Background() - pm, observedLogs := bootstrapPersistenceManager(t) + jobID := rand.Int32() + db := pgtest.NewSqlxDB(t) + pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) + pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) + pm, observedLogs := bootstrapPersistenceManager(t, jobID, db) reports := sampleReports @@ -96,16 +114,32 @@ func TestPersistenceManagerAsyncDelete(t *testing.T) { } func TestPersistenceManagerPrune(t *testing.T) { + jobID1 := rand.Int32() + jobID2 := jobID1 + 1 + db := pgtest.NewSqlxDB(t) + pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) + pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) + ctx := context.Background() - pm, observedLogs := bootstrapPersistenceManager(t) - reports := sampleReports + reports := make([][]byte, 25) + for i := 0; i < 25; i++ { + reports[i] = buildSampleV1Report(int64(i)) + } - err := pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[0]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 1}}) + pm2, _ := bootstrapPersistenceManager(t, jobID2, db) + for i := 0; i < 20; i++ { + err := pm2.Insert(ctx, &pb.TransmitRequest{Payload: reports[i]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: uint32(i)}}) + require.NoError(t, err) + } + + pm, observedLogs := bootstrapPersistenceManager(t, jobID1, db) + + err := pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[21]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 21}}) require.NoError(t, err) - err = pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[1]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 2}}) + err = pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[22]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 22}}) require.NoError(t, err) - err = pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[2]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 3}}) + err = pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[23]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 23}}) require.NoError(t, err) err = pm.Start(ctx) @@ -118,24 +152,28 @@ func TestPersistenceManagerPrune(t *testing.T) { transmissions, err := pm.Load(ctx) require.NoError(t, err) require.Equal(t, []*Transmission{ - {Req: &pb.TransmitRequest{Payload: reports[2]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 3}}}, - {Req: &pb.TransmitRequest{Payload: reports[1]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 2}}}, + {Req: &pb.TransmitRequest{Payload: reports[23]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 23}}}, + {Req: &pb.TransmitRequest{Payload: reports[22]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 22}}}, }, transmissions) // Test pruning stops after Close. err = pm.Close() require.NoError(t, err) - err = pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[3]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 4}}) + err = pm.Insert(ctx, &pb.TransmitRequest{Payload: reports[24]}, ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 24}}) require.NoError(t, err) - time.Sleep(15 * time.Millisecond) - transmissions, err = pm.Load(ctx) require.NoError(t, err) require.Equal(t, []*Transmission{ - {Req: &pb.TransmitRequest{Payload: reports[3]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 4}}}, - {Req: &pb.TransmitRequest{Payload: reports[2]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 3}}}, - {Req: &pb.TransmitRequest{Payload: reports[1]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 2}}}, + {Req: &pb.TransmitRequest{Payload: reports[24]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 24}}}, + {Req: &pb.TransmitRequest{Payload: reports[23]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 23}}}, + {Req: &pb.TransmitRequest{Payload: reports[22]}, ReportCtx: ocrtypes.ReportContext{ReportTimestamp: ocrtypes.ReportTimestamp{Epoch: 22}}}, }, transmissions) + + t.Run("prune was scoped to job ID", func(t *testing.T) { + transmissions, err = pm2.Load(ctx) + require.NoError(t, err) + assert.Len(t, transmissions, 20) + }) } diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index 0c701e3b4b3..0c2721442b4 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -137,7 +137,7 @@ func NewTransmitter(lggr logger.Logger, cfgTracker ConfigTracker, rpcClient wsrp jobID, fmt.Sprintf("%x", fromAccount), make(chan (struct{})), - NewTransmitQueue(lggr, feedIDHex, maxTransmitQueueSize, nil, persistenceManager), + nil, sync.WaitGroup{}, transmitSuccessCount.WithLabelValues(feedIDHex), transmitDuplicateCount.WithLabelValues(feedIDHex), diff --git a/core/services/relay/evm/mercury/transmitter_test.go b/core/services/relay/evm/mercury/transmitter_test.go index 6723ffcbcac..c8a68d41a16 100644 --- a/core/services/relay/evm/mercury/transmitter_test.go +++ b/core/services/relay/evm/mercury/transmitter_test.go @@ -26,6 +26,7 @@ func Test_MercuryTransmitter_Transmit(t *testing.T) { var jobID int32 pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) + q := NewTransmitQueue(lggr, "", 0, nil, nil) t.Run("v1 report transmission successfully enqueued", func(t *testing.T) { report := sampleV1Report @@ -40,6 +41,7 @@ func Test_MercuryTransmitter_Transmit(t *testing.T) { }, } mt := NewTransmitter(lggr, nil, c, sampleClientPubKey, jobID, sampleFeedID, db, pgtest.NewQConfig(true), nil) + mt.queue = q err := mt.Transmit(testutils.Context(t), sampleReportContext, report, sampleSigs) require.NoError(t, err) @@ -57,6 +59,7 @@ func Test_MercuryTransmitter_Transmit(t *testing.T) { }, } mt := NewTransmitter(lggr, nil, c, sampleClientPubKey, jobID, sampleFeedID, db, pgtest.NewQConfig(true), nil) + mt.queue = q err := mt.Transmit(testutils.Context(t), sampleReportContext, report, sampleSigs) require.NoError(t, err) @@ -74,6 +77,7 @@ func Test_MercuryTransmitter_Transmit(t *testing.T) { }, } mt := NewTransmitter(lggr, nil, c, sampleClientPubKey, jobID, sampleFeedID, db, pgtest.NewQConfig(true), nil) + mt.queue = q err := mt.Transmit(testutils.Context(t), sampleReportContext, report, sampleSigs) require.NoError(t, err) diff --git a/core/store/migrate/migrations/0205_add_feed_id_to_mercury_transmit_requests.sql b/core/store/migrate/migrations/0205_add_feed_id_to_mercury_transmit_requests.sql new file mode 100644 index 00000000000..04cf5a2571d --- /dev/null +++ b/core/store/migrate/migrations/0205_add_feed_id_to_mercury_transmit_requests.sql @@ -0,0 +1,14 @@ +-- +goose Up +ALTER TABLE mercury_transmit_requests ADD COLUMN feed_id BYTEA CHECK (feed_id IS NULL OR octet_length(feed_id) = 32); +DROP INDEX idx_mercury_transmission_requests_epoch_round; +CREATE INDEX idx_mercury_transmission_requests_job_id_epoch_round ON mercury_transmit_requests (job_id, epoch DESC, round DESC); +CREATE INDEX idx_mercury_transmit_requests_job_id ON mercury_transmit_requests (job_id); +CREATE INDEX idx_mercury_transmit_requests_feed_id ON mercury_transmit_requests (feed_id); +CREATE INDEX idx_mercury_feed_latest_reports_job_id ON feed_latest_reports (job_id); + +-- +goose Down +ALTER TABLE mercury_transmit_requests DROP COLUMN feed_id; +DROP INDEX idx_mercury_transmit_requests_job_id; +DROP INDEX idx_mercury_feed_latest_reports_job_id; +CREATE INDEX idx_mercury_transmission_requests_epoch_round ON mercury_transmit_requests (epoch DESC, round DESC); +DROP INDEX idx_mercury_transmission_requests_job_id_epoch_round; From 5e1c3a3f2ddf35ac6e86f9640ba33f71173efadc Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:22:07 +0100 Subject: [PATCH 030/271] Use CTF instead of chainlink-env in E2E tests (#11120) --- integration-tests/actions/actions.go | 2 +- integration-tests/benchmark/keeper_test.go | 12 ++++++------ integration-tests/chaos/automation_chaos_test.go | 12 ++++++------ integration-tests/chaos/ocr2vrf_chaos_test.go | 10 +++++----- integration-tests/chaos/ocr_chaos_test.go | 16 ++++++++-------- .../client/chainlink_config_builder.go | 2 +- integration-tests/client/chainlink_k8s.go | 2 +- integration-tests/go.mod | 3 +-- integration-tests/go.sum | 6 ++---- integration-tests/performance/cron_test.go | 12 ++++++------ .../performance/directrequest_test.go | 10 +++++----- integration-tests/performance/flux_test.go | 10 +++++----- integration-tests/performance/keeper_test.go | 10 +++++----- integration-tests/performance/ocr_test.go | 10 +++++----- integration-tests/performance/vrf_test.go | 6 +++--- integration-tests/reorg/automation_reorg_test.go | 8 ++++---- integration-tests/reorg/reorg_confirmer.go | 8 ++++---- integration-tests/reorg/reorg_test.go | 14 +++++++------- integration-tests/smoke/ocr2_test.go | 10 +++++----- integration-tests/smoke/ocr2vrf_test.go | 6 +++--- integration-tests/testsetups/don_evm_chain.go | 10 +++++----- integration-tests/testsetups/keeper_benchmark.go | 2 +- integration-tests/testsetups/ocr.go | 10 +++++----- integration-tests/testsetups/profile.go | 2 +- integration-tests/testsetups/vrfv2.go | 2 +- 25 files changed, 96 insertions(+), 99 deletions(-) diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index dcdca91cc78..010b431b56f 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -16,9 +16,9 @@ import ( "github.com/rs/zerolog/log" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/environment" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/testreporters" "github.com/smartcontractkit/chainlink-testing-framework/utils" diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 7f484fc69fc..6fbf929e47d 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -11,13 +11,13 @@ import ( "github.com/stretchr/testify/require" - env_client "github.com/smartcontractkit/chainlink-env/client" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/cdk8s/blockscout" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + env_client "github.com/smartcontractkit/chainlink-testing-framework/k8s/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 8697044aa79..a3d4e37406d 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -11,13 +11,13 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/chaos" - "github.com/smartcontractkit/chainlink-env/environment" - a "github.com/smartcontractkit/chainlink-env/pkg/alias" - "github.com/smartcontractkit/chainlink-env/pkg/cdk8s/blockscout" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils" diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index cbab1bf9e78..0beccadddda 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -10,12 +10,12 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/chaos" - "github.com/smartcontractkit/chainlink-env/environment" - a "github.com/smartcontractkit/chainlink-env/pkg/alias" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils" diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 0d72e3932e7..b65f8bb74f7 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -11,15 +11,15 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/chaos" - "github.com/smartcontractkit/chainlink-env/environment" - a "github.com/smartcontractkit/chainlink-env/pkg/alias" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils" @@ -75,7 +75,7 @@ func TestOCRChaos(t *testing.T) { // and chaos.NewNetworkPartition method (https://chaos-mesh.org/docs/simulate-network-chaos-on-kubernetes/) // in order to regenerate Go bindings if k8s version will be updated // you can pull new CRD spec from your current cluster and check README here - // https://github.com/smartcontractkit/chainlink-env/blob/master/README.md + // https://github.com/smartcontractkit/chainlink-testing-framework/k8s/blob/master/README.md NetworkChaosFailMajorityNetwork: { ethereum.New(nil), chainlink.New(0, defaultOCRSettings), diff --git a/integration-tests/client/chainlink_config_builder.go b/integration-tests/client/chainlink_config_builder.go index 9c1050300b1..13cc1e7fe93 100644 --- a/integration-tests/client/chainlink_config_builder.go +++ b/integration-tests/client/chainlink_config_builder.go @@ -4,8 +4,8 @@ import ( "fmt" "os" - "github.com/smartcontractkit/chainlink-env/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" ) const ( diff --git a/integration-tests/client/chainlink_k8s.go b/integration-tests/client/chainlink_k8s.go index 4aa7c6d0fec..3fbf9eaf73c 100644 --- a/integration-tests/client/chainlink_k8s.go +++ b/integration-tests/client/chainlink_k8s.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink-env/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" ) type ChainlinkK8sClient struct { diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d3e09d94d7c..c1a8cdb61ce 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -20,8 +20,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-env v0.38.3 - github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231027132403-4898f11e80b6 + github.com/smartcontractkit/chainlink-testing-framework v1.18.0 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 725e05914cf..77778de9d23 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2362,16 +2362,14 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-env v0.38.3 h1:ZtOnwkG622R0VCTxL5V09AnT/QXhlFwkGTjd0Lsfpfg= -github.com/smartcontractkit/chainlink-env v0.38.3/go.mod h1:7z4sw/hN8TxioQCLwFqQdhK3vaOV0a22Qe99z4bRUcg= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231027132403-4898f11e80b6 h1:f1nUQ/1eUTMwNbOZK0P7P6OHvTDGQSn2KE+LtwY0rXA= -github.com/smartcontractkit/chainlink-testing-framework v1.17.12-0.20231027132403-4898f11e80b6/go.mod h1:RWlmjwnjIGbQAnRfKwe02Ife82nNI3rZmdI0zgkfbyk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.0 h1:Ru7odxF0tq0FixJXM58rNZw0PvyQnRroqAInBAM83gs= +github.com/smartcontractkit/chainlink-testing-framework v1.18.0/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/performance/cron_test.go b/integration-tests/performance/cron_test.go index 84a49646474..e700a66e1f8 100644 --- a/integration-tests/performance/cron_test.go +++ b/integration-tests/performance/cron_test.go @@ -12,14 +12,14 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/logging" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/performance/directrequest_test.go b/integration-tests/performance/directrequest_test.go index faaac5910d2..d229f9fb3ee 100644 --- a/integration-tests/performance/directrequest_test.go +++ b/integration-tests/performance/directrequest_test.go @@ -11,13 +11,13 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/performance/flux_test.go b/integration-tests/performance/flux_test.go index df3022003e1..be536450a76 100644 --- a/integration-tests/performance/flux_test.go +++ b/integration-tests/performance/flux_test.go @@ -12,13 +12,13 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/performance/keeper_test.go b/integration-tests/performance/keeper_test.go index 7a2cc933de2..cd9818f99d3 100644 --- a/integration-tests/performance/keeper_test.go +++ b/integration-tests/performance/keeper_test.go @@ -12,12 +12,12 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - eth "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go index 4d875022ed2..e81cc91cf7f 100644 --- a/integration-tests/performance/ocr_test.go +++ b/integration-tests/performance/ocr_test.go @@ -10,13 +10,13 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/performance/vrf_test.go b/integration-tests/performance/vrf_test.go index c715641692d..eeaceffaaf5 100644 --- a/integration-tests/performance/vrf_test.go +++ b/integration-tests/performance/vrf_test.go @@ -12,10 +12,10 @@ import ( "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 608144eafd0..697ae28ce3b 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -12,11 +12,11 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/cdk8s/blockscout" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils" diff --git a/integration-tests/reorg/reorg_confirmer.go b/integration-tests/reorg/reorg_confirmer.go index 6647816c975..be535d2a6da 100644 --- a/integration-tests/reorg/reorg_confirmer.go +++ b/integration-tests/reorg/reorg_confirmer.go @@ -11,11 +11,11 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink-env/chaos" - "github.com/smartcontractkit/chainlink-env/environment" - a "github.com/smartcontractkit/chainlink-env/pkg/alias" - "github.com/smartcontractkit/chainlink-env/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" ) // The steps are: diff --git a/integration-tests/reorg/reorg_test.go b/integration-tests/reorg/reorg_test.go index 74468b92536..f92becfa50a 100644 --- a/integration-tests/reorg/reorg_test.go +++ b/integration-tests/reorg/reorg_test.go @@ -13,15 +13,15 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/logging" - "github.com/smartcontractkit/chainlink-env/pkg/cdk8s/blockscout" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" - "github.com/smartcontractkit/chainlink-env/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/onsi/gomega" diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 582ca17f7b6..1b33cdce769 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 8c102f6fd21..0d6a77a1157 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -9,10 +9,10 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - eth "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils" diff --git a/integration-tests/testsetups/don_evm_chain.go b/integration-tests/testsetups/don_evm_chain.go index 545d9515801..3ade7f0d69d 100644 --- a/integration-tests/testsetups/don_evm_chain.go +++ b/integration-tests/testsetups/don_evm_chain.go @@ -6,13 +6,13 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/require" - e "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + e "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index 466eb97fdd3..f786cca9bb5 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -21,8 +21,8 @@ import ( "github.com/slack-go/slack" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/logging" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index c4b1fc7ab1e..ee8116f3f99 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -26,13 +26,13 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" - "github.com/smartcontractkit/chainlink-env/environment" - "github.com/smartcontractkit/chainlink-env/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-env/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-env/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" + mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" diff --git a/integration-tests/testsetups/profile.go b/integration-tests/testsetups/profile.go index 6f978cdebee..14fe3d29ae6 100644 --- a/integration-tests/testsetups/profile.go +++ b/integration-tests/testsetups/profile.go @@ -7,8 +7,8 @@ import ( . "github.com/onsi/gomega" "golang.org/x/sync/errgroup" - "github.com/smartcontractkit/chainlink-env/environment" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" "github.com/smartcontractkit/chainlink/integration-tests/client" diff --git a/integration-tests/testsetups/vrfv2.go b/integration-tests/testsetups/vrfv2.go index cfa26e8f279..194c7ff4e6c 100644 --- a/integration-tests/testsetups/vrfv2.go +++ b/integration-tests/testsetups/vrfv2.go @@ -14,8 +14,8 @@ import ( "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-env/environment" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/logging" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" From c94240344024998e32969b24783430d8b42ab0c9 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Oct 2023 12:36:30 -0400 Subject: [PATCH 031/271] MERC 1388 update telemetry module to address the multi report structure (#10827) * w * Don't warn uselessly on bid/ask missing * - Update enhancedEAmercury proto file - Fix tests * Genereate * fix foundry deps --------- Co-authored-by: george-dorin Co-authored-by: Rens Rooimans --- core/services/ocr2/delegate.go | 2 +- core/services/ocrcommon/telemetry.go | 189 +++++++--- core/services/ocrcommon/telemetry_test.go | 211 ++++++++--- .../relay/evm/mercury/v1/data_source.go | 37 +- .../relay/evm/mercury/v2/data_source.go | 46 +-- .../relay/evm/mercury/v3/data_source.go | 47 +-- .../telem/telem_enhanced_ea_mercury.pb.go | 327 +++++++++++------- .../telem/telem_enhanced_ea_mercury.proto | 33 +- tools/flakeytests/coverage.txt | 93 +++++ 9 files changed, 698 insertions(+), 287 deletions(-) create mode 100644 tools/flakeytests/coverage.txt diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index ef1ae7c5888..efb6f04fd3d 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -551,7 +551,7 @@ func (d *Delegate) newServicesMercury( mercuryServices, err2 := mercury.NewServices(jb, mercuryProvider, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, d.cfg.JobPipeline(), chEnhancedTelem, chain, d.mercuryORM, (mercuryutils.FeedID)(*spec.FeedID)) - if ocrcommon.ShouldCollectEnhancedTelemetryMercury(&jb) { + if ocrcommon.ShouldCollectEnhancedTelemetryMercury(jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, chEnhancedTelem, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(spec.FeedID.String(), synchronization.EnhancedEAMercury, rid.Network, rid.ChainID), lggr.Named("EnhancedTelemetryMercury")) mercuryServices = append(mercuryServices, enhancedTelemService) } diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 5277143c8b3..29d1ad92e41 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -7,7 +7,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/libocr/commontypes" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "google.golang.org/protobuf/proto" @@ -15,10 +14,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/utils" relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" + relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" + relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" ) type eaTelemetry struct { @@ -36,9 +38,15 @@ type EnhancedTelemetryData struct { } type EnhancedTelemetryMercuryData struct { - TaskRunResults pipeline.TaskRunResults - Observation relaymercuryv1.Observation - RepTimestamp ocrtypes.ReportTimestamp + V1Observation *relaymercuryv1.Observation + V2Observation *relaymercuryv2.Observation + V3Observation *relaymercuryv3.Observation + TaskRunResults pipeline.TaskRunResults + RepTimestamp ocrtypes.ReportTimestamp + FeedVersion mercuryutils.FeedVersion + FetchMaxFinalizedTimestamp bool + IsLinkFeed bool + IsNativeFeed bool } type EnhancedTelemetryService[T EnhancedTelemetryData | EnhancedTelemetryMercuryData] struct { @@ -69,13 +77,13 @@ func (e *EnhancedTelemetryService[T]) Start(context.Context) error { for { select { case t := <-e.chTelem: - switch any(t).(type) { + switch v := any(t).(type) { case EnhancedTelemetryData: - s := any(t).(EnhancedTelemetryData) - e.collectEATelemetry(s.TaskRunResults, s.FinalResults, s.RepTimestamp) + e.collectEATelemetry(v.TaskRunResults, v.FinalResults, v.RepTimestamp) case EnhancedTelemetryMercuryData: - s := any(t).(EnhancedTelemetryMercuryData) - e.collectMercuryEnhancedTelemetry(s.Observation, s.TaskRunResults, s.RepTimestamp) + e.collectMercuryEnhancedTelemetry(v) + default: + e.lggr.Errorf("unrecognised telemetry data type: %T", t) } case <-e.chDone: return @@ -224,14 +232,19 @@ func (e *EnhancedTelemetryService[T]) collectAndSend(trrs *pipeline.TaskRunResul continue } + if trr.Result.Error != nil { + e.lggr.Warnw(fmt.Sprintf("cannot get bridge response from bridge task, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", trr.Result.Error) + continue + } bridgeRawResponse, ok := trr.Result.Value.(string) if !ok { - e.lggr.Warnf("cannot get bridge response from bridge task, job %d, id %s", e.job.ID, trr.Task.DotID()) + e.lggr.Warnf("cannot parse bridge response from bridge task, job %d, id %s: expected string, got: %v (type %T)", e.job.ID, trr.Task.DotID(), trr.Result.Value, trr.Result.Value) continue } eaTelem, err := parseEATelemetry([]byte(bridgeRawResponse)) if err != nil { e.lggr.Warnw(fmt.Sprintf("cannot parse EA telemetry, job %d, id %s", e.job.ID, trr.Task.DotID()), "err", err) + continue } value := e.getParsedValue(trrs, trr) @@ -254,7 +267,7 @@ func (e *EnhancedTelemetryService[T]) collectAndSend(trrs *pipeline.TaskRunResul bytes, err := proto.Marshal(t) if err != nil { - e.lggr.Warnf("protobuf marshal failed %v", err.Error()) + e.lggr.Warnw("protobuf marshal failed", "err", err) continue } @@ -264,14 +277,81 @@ func (e *EnhancedTelemetryService[T]) collectAndSend(trrs *pipeline.TaskRunResul // collectMercuryEnhancedTelemetry checks if enhanced telemetry should be collected, fetches the information needed and // sends the telemetry -func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(obs relaymercuryv1.Observation, trrs pipeline.TaskRunResults, repts ocrtypes.ReportTimestamp) { +func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(d EnhancedTelemetryMercuryData) { if e.monitoringEndpoint == nil { return } - obsBenchmarkPrice, obsBid, obsAsk, obsBlockNum, obsBlockHash, obsBlockTimestamp := e.getFinalValues(obs) + // v1 fields + var bn int64 + var bh string + var bt uint64 + // v1+v2+v3 fields + bp := big.NewInt(0) + //v1+v3 fields + bid := big.NewInt(0) + ask := big.NewInt(0) + // v2+v3 fields + var mfts, lp, np int64 + + switch { + case d.V1Observation != nil: + obs := *d.V1Observation + if obs.CurrentBlockNum.Err == nil { + bn = obs.CurrentBlockNum.Val + } + if obs.CurrentBlockHash.Err == nil { + bh = common.BytesToHash(obs.CurrentBlockHash.Val).Hex() + } + if obs.CurrentBlockTimestamp.Err == nil { + bt = obs.CurrentBlockTimestamp.Val + } + if obs.BenchmarkPrice.Err == nil && obs.BenchmarkPrice.Val != nil { + bp = obs.BenchmarkPrice.Val + } + if obs.Bid.Err == nil && obs.Bid.Val != nil { + bid = obs.Bid.Val + } + if obs.Ask.Err == nil && obs.Ask.Val != nil { + ask = obs.Ask.Val + } + case d.V2Observation != nil: + obs := *d.V2Observation + if obs.MaxFinalizedTimestamp.Err == nil { + mfts = obs.MaxFinalizedTimestamp.Val + } + if obs.LinkPrice.Err == nil && obs.LinkPrice.Val != nil { + lp = obs.LinkPrice.Val.Int64() + } + if obs.NativePrice.Err == nil && obs.NativePrice.Val != nil { + np = obs.NativePrice.Val.Int64() + } + if obs.BenchmarkPrice.Err == nil && obs.BenchmarkPrice.Val != nil { + bp = obs.BenchmarkPrice.Val + } + case d.V3Observation != nil: + obs := *d.V3Observation + if obs.MaxFinalizedTimestamp.Err == nil { + mfts = obs.MaxFinalizedTimestamp.Val + } + if obs.LinkPrice.Err == nil && obs.LinkPrice.Val != nil { + lp = obs.LinkPrice.Val.Int64() + } + if obs.NativePrice.Err == nil && obs.NativePrice.Val != nil { + np = obs.NativePrice.Val.Int64() + } + if obs.BenchmarkPrice.Err == nil && obs.BenchmarkPrice.Val != nil { + bp = obs.BenchmarkPrice.Val + } + if obs.Bid.Err == nil && obs.Bid.Val != nil { + bid = obs.Bid.Val + } + if obs.Ask.Err == nil && obs.Ask.Val != nil { + ask = obs.Ask.Val + } + } - for _, trr := range trrs { + for _, trr := range d.TaskRunResults { if trr.Task.Type() != pipeline.TaskTypeBridge { continue } @@ -288,16 +368,19 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(obs relaym } assetSymbol := e.getAssetSymbolFromRequestData(bridgeTask.RequestData) - benchmarkPrice, bidPrice, askPrice := e.getPricesFromResults(trr, &trrs) + + benchmarkPrice, bidPrice, askPrice := e.getPricesFromResults(trr, d.TaskRunResults, d.FeedVersion) t := &telem.EnhancedEAMercury{ DataSource: eaTelem.DataSource, DpBenchmarkPrice: benchmarkPrice, DpBid: bidPrice, DpAsk: askPrice, - CurrentBlockNumber: obsBlockNum, - CurrentBlockHash: common.BytesToHash(obsBlockHash).String(), - CurrentBlockTimestamp: obsBlockTimestamp, + CurrentBlockNumber: bn, + CurrentBlockHash: bh, + CurrentBlockTimestamp: bt, + FetchMaxFinalizedTimestamp: d.FetchMaxFinalizedTimestamp, + MaxFinalizedTimestamp: mfts, BridgeTaskRunStartedTimestamp: trr.CreatedAt.UnixMilli(), BridgeTaskRunEndedTimestamp: trr.FinishedAt.Time.UnixMilli(), ProviderRequestedTimestamp: eaTelem.ProviderRequestedTimestamp, @@ -305,16 +388,21 @@ func (e *EnhancedTelemetryService[T]) collectMercuryEnhancedTelemetry(obs relaym ProviderDataStreamEstablished: eaTelem.ProviderDataStreamEstablished, ProviderIndicatedTime: eaTelem.ProviderIndicatedTime, Feed: e.job.OCR2OracleSpec.FeedID.Hex(), - ObservationBenchmarkPrice: obsBenchmarkPrice.Int64(), //Deprecated: observation value will not fit in int64, we will use the string equivalent field ObservationBenchmarkPriceString - ObservationBid: obsBid.Int64(), //Deprecated: observation value will not fit in int64, we will use the string equivalent field ObservationBidString - ObservationAsk: obsAsk.Int64(), //Deprecated: observation value will not fit in int64, we will use the string equivalent field ObservationAskString - ConfigDigest: repts.ConfigDigest.Hex(), - Round: int64(repts.Round), - Epoch: int64(repts.Epoch), + ObservationBenchmarkPrice: bp.Int64(), + ObservationBid: bid.Int64(), + ObservationAsk: ask.Int64(), + ObservationBenchmarkPriceString: stringOrEmpty(bp), + ObservationBidString: stringOrEmpty(bid), + ObservationAskString: stringOrEmpty(ask), + IsLinkFeed: d.IsLinkFeed, + LinkPrice: lp, + IsNativeFeed: d.IsNativeFeed, + NativePrice: np, + ConfigDigest: d.RepTimestamp.ConfigDigest.Hex(), + Round: int64(d.RepTimestamp.Round), + Epoch: int64(d.RepTimestamp.Epoch), AssetSymbol: assetSymbol, - ObservationBenchmarkPriceString: obsBenchmarkPrice.String(), - ObservationBidString: obsBid.String(), - ObservationAskString: obsAsk.String(), + Version: uint32(d.FeedVersion), } bytes, err := proto.Marshal(t) @@ -347,19 +435,19 @@ func (e *EnhancedTelemetryService[T]) getAssetSymbolFromRequestData(requestData } // ShouldCollectEnhancedTelemetryMercury checks if enhanced telemetry should be collected and sent -func ShouldCollectEnhancedTelemetryMercury(job *job.Job) bool { - if job.Type.String() == pipeline.OffchainReporting2JobType && job.OCR2OracleSpec != nil { - return job.OCR2OracleSpec.CaptureEATelemetry +func ShouldCollectEnhancedTelemetryMercury(jb job.Job) bool { + if jb.Type.String() == pipeline.OffchainReporting2JobType && jb.OCR2OracleSpec != nil { + return jb.OCR2OracleSpec.CaptureEATelemetry } return false } // getPricesFromResults parses the pipeline.TaskRunResults for pipeline.TaskTypeJSONParse and gets the benchmarkPrice, // bid and ask. This functions expects the pipeline.TaskRunResults to be correctly ordered -func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.TaskRunResult, allTasks *pipeline.TaskRunResults) (float64, float64, float64) { +func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.TaskRunResult, allTasks pipeline.TaskRunResults, mercuryVersion mercuryutils.FeedVersion) (float64, float64, float64) { var benchmarkPrice, askPrice, bidPrice float64 var err error - //We rely on task results to be sorted in the correct order + // We rely on task results to be sorted in the correct order benchmarkPriceTask := allTasks.GetNextTaskOf(startTask) if benchmarkPriceTask == nil { e.lggr.Warnf("cannot parse enhanced EA telemetry benchmark price, task is nil, job %d, id %s", e.job.ID) @@ -376,12 +464,18 @@ func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.Ta } } + // mercury version 2 only supports benchmarkPrice + if mercuryVersion == 2 { + return benchmarkPrice, 0, 0 + } + bidTask := allTasks.GetNextTaskOf(*benchmarkPriceTask) if bidTask == nil { e.lggr.Warnf("cannot parse enhanced EA telemetry bid price, task is nil, job %d, id %s", e.job.ID) return benchmarkPrice, 0, 0 } - if bidTask.Task.Type() == pipeline.TaskTypeJSONParse { + + if bidTask != nil && bidTask.Task.Type() == pipeline.TaskTypeJSONParse { if bidTask.Result.Error != nil { e.lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry bid price, job %d, id %s: %s", e.job.ID, bidTask.Task.DotID(), bidTask.Result.Error), "err", bidTask.Result.Error) } else { @@ -397,7 +491,7 @@ func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.Ta e.lggr.Warnf("cannot parse enhanced EA telemetry ask price, task is nil, job %d, id %s", e.job.ID) return benchmarkPrice, bidPrice, 0 } - if askTask.Task.Type() == pipeline.TaskTypeJSONParse { + if askTask != nil && askTask.Task.Type() == pipeline.TaskTypeJSONParse { if bidTask.Result.Error != nil { e.lggr.Warnw(fmt.Sprintf("got error for enhanced EA telemetry ask price, job %d, id %s: %s", e.job.ID, askTask.Task.DotID(), askTask.Result.Error), "err", askTask.Result.Error) } else { @@ -411,23 +505,11 @@ func (e *EnhancedTelemetryService[T]) getPricesFromResults(startTask pipeline.Ta return benchmarkPrice, bidPrice, askPrice } -// getFinalValues runs a parse on the pipeline.TaskRunResults and returns the values -func (e *EnhancedTelemetryService[T]) getFinalValues(obs relaymercuryv1.Observation) (*big.Int, *big.Int, *big.Int, int64, []byte, uint64) { - benchmarkPrice := big.NewInt(0) - bid := big.NewInt(0) - ask := big.NewInt(0) - - if obs.BenchmarkPrice.Val != nil { - benchmarkPrice = obs.BenchmarkPrice.Val - } - if obs.Bid.Val != nil { - bid = obs.Bid.Val - } - if obs.Ask.Val != nil { - ask = obs.Ask.Val +// MaybeEnqueueEnhancedTelem sends data to the telemetry channel for processing +func MaybeEnqueueEnhancedTelem(jb job.Job, ch chan<- EnhancedTelemetryMercuryData, data EnhancedTelemetryMercuryData) { + if ShouldCollectEnhancedTelemetryMercury(jb) { + EnqueueEnhancedTelem[EnhancedTelemetryMercuryData](ch, data) } - - return benchmarkPrice, bid, ask, obs.CurrentBlockNum.Val, obs.CurrentBlockHash.Val, obs.CurrentBlockTimestamp.Val } // EnqueueEnhancedTelem sends data to the telemetry channel for processing @@ -447,3 +529,10 @@ func getResultFloat64(task *pipeline.TaskRunResult) (float64, error) { resultFloat64, _ := result.Float64() return resultFloat64, nil } + +func stringOrEmpty(n *big.Int) string { + if n.Cmp(big.NewInt(0)) == 0 { + return "" + } + return n.String() +} diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index 5d1494a038a..e6a798780b5 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" mercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" + mercury_v2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -361,7 +362,7 @@ func TestCollectAndSend(t *testing.T) { wg.Wait() assert.Equal(t, logs.Len(), 2) - assert.Contains(t, logs.All()[0].Message, "cannot get bridge response from bridge task") + assert.Contains(t, logs.All()[0].Message, "cannot parse bridge response from bridge task") badTrrs = &pipeline.TaskRunResults{ pipeline.TaskRunResult{ @@ -372,20 +373,19 @@ func TestCollectAndSend(t *testing.T) { Value: "[]", }, }} - wg.Add(1) enhancedTelemChan <- EnhancedTelemetryData{ TaskRunResults: *badTrrs, FinalResults: *finalResult, RepTimestamp: observationTimestamp, } wg.Wait() - assert.Equal(t, logs.Len(), 4) - assert.Contains(t, logs.All()[2].Message, "cannot parse EA telemetry") - assert.Contains(t, logs.All()[3].Message, "cannot get json parse value") + assert.Equal(t, 2, logs.Len()) + assert.Contains(t, logs.All()[0].Message, "cannot parse bridge response from bridge task") + assert.Contains(t, logs.All()[1].Message, "cannot get json parse value") doneCh <- struct{}{} } -var trrsMercury = pipeline.TaskRunResults{ +var trrsMercuryV1 = pipeline.TaskRunResults{ pipeline.TaskRunResult{ Task: &pipeline.BridgeTask{ BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), @@ -421,32 +421,24 @@ var trrsMercury = pipeline.TaskRunResults{ }, } -func TestGetFinalValues(t *testing.T) { - e := EnhancedTelemetryService[EnhancedTelemetryMercuryData]{} - o := mercuryv1.Observation{ - BenchmarkPrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(111111)}, - Bid: mercury.ObsResult[*big.Int]{Val: big.NewInt(222222)}, - Ask: mercury.ObsResult[*big.Int]{Val: big.NewInt(333333)}, - CurrentBlockNum: mercury.ObsResult[int64]{Val: 123456789}, - CurrentBlockHash: mercury.ObsResult[[]byte]{Val: common.HexToHash("0x123321").Bytes()}, - CurrentBlockTimestamp: mercury.ObsResult[uint64]{Val: 987654321}, - } - - benchmarkPrice, bid, ask, blockNr, blockHash, blockTimestamp := e.getFinalValues(o) - require.Equal(t, benchmarkPrice, big.NewInt(111111)) - require.Equal(t, bid, big.NewInt(222222)) - require.Equal(t, ask, big.NewInt(333333)) - require.Equal(t, blockNr, int64(123456789)) - require.Equal(t, blockHash, common.HexToHash("0x123321").Bytes()) - require.Equal(t, blockTimestamp, uint64(987654321)) - - benchmarkPrice, bid, ask, blockNr, blockHash, blockTimestamp = e.getFinalValues(mercuryv1.Observation{}) - require.Equal(t, benchmarkPrice, big.NewInt(0)) - require.Equal(t, bid, big.NewInt(0)) - require.Equal(t, ask, big.NewInt(0)) - require.Equal(t, blockNr, int64(0)) - require.Nil(t, blockHash) - require.Equal(t, blockTimestamp, uint64(0)) +var trrsMercuryV2 = pipeline.TaskRunResults{ + pipeline.TaskRunResult{ + Task: &pipeline.BridgeTask{ + BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), + RequestData: `{"data":{"to":"LINK","from":"USD"}}`, + }, + Result: pipeline.Result{ + Value: bridgeResponse, + }, + }, + pipeline.TaskRunResult{ + Task: &pipeline.JSONParseTask{ + BaseTask: pipeline.NewBaseTask(1, "ds1_benchmark", nil, nil, 1), + }, + Result: pipeline.Result{ + Value: float64(123456.123456), + }, + }, } func TestGetPricesFromResults(t *testing.T) { @@ -458,25 +450,25 @@ func TestGetPricesFromResults(t *testing.T) { }, } - benchmarkPrice, bid, ask := e.getPricesFromResults(trrsMercury[0], &trrsMercury) + benchmarkPrice, bid, ask := e.getPricesFromResults(trrsMercuryV1[0], trrsMercuryV1, 1) require.Equal(t, 123456.123456, benchmarkPrice) require.Equal(t, 1234567.1234567, bid) require.Equal(t, float64(321123), ask) - benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercury[0], &pipeline.TaskRunResults{}) + benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercuryV1[0], pipeline.TaskRunResults{}, 1) require.Equal(t, float64(0), benchmarkPrice) require.Equal(t, float64(0), bid) require.Equal(t, float64(0), ask) require.Equal(t, 1, logs.Len()) require.Contains(t, logs.All()[0].Message, "cannot parse enhanced EA telemetry") - tt := trrsMercury[:2] - e.getPricesFromResults(trrsMercury[0], &tt) + tt := trrsMercuryV1[:2] + e.getPricesFromResults(trrsMercuryV1[0], tt, 1) require.Equal(t, 2, logs.Len()) require.Contains(t, logs.All()[1].Message, "cannot parse enhanced EA telemetry bid price, task is nil") - tt = trrsMercury[:3] - e.getPricesFromResults(trrsMercury[0], &tt) + tt = trrsMercuryV1[:3] + e.getPricesFromResults(trrsMercuryV1[0], tt, 1) require.Equal(t, 3, logs.Len()) require.Contains(t, logs.All()[2].Message, "cannot parse enhanced EA telemetry ask price, task is nil") @@ -513,7 +505,7 @@ func TestGetPricesFromResults(t *testing.T) { Value: nil, }, }} - benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercury[0], &trrs2) + benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercuryV1[0], trrs2, 3) require.Equal(t, benchmarkPrice, float64(0)) require.Equal(t, bid, float64(0)) require.Equal(t, ask, float64(0)) @@ -521,11 +513,16 @@ func TestGetPricesFromResults(t *testing.T) { require.Contains(t, logs.All()[3].Message, "cannot parse enhanced EA telemetry benchmark price") require.Contains(t, logs.All()[4].Message, "cannot parse enhanced EA telemetry bid price") require.Contains(t, logs.All()[5].Message, "cannot parse enhanced EA telemetry ask price") + + benchmarkPrice, bid, ask = e.getPricesFromResults(trrsMercuryV1[0], trrsMercuryV2, 2) + require.Equal(t, 123456.123456, benchmarkPrice) + require.Equal(t, float64(0), bid) + require.Equal(t, float64(0), ask) } func TestShouldCollectEnhancedTelemetryMercury(t *testing.T) { - j := &job.Job{ + j := job.Job{ Type: job.Type(pipeline.OffchainReporting2JobType), OCR2OracleSpec: &job.OCR2OracleSpec{ CaptureEATelemetry: true, @@ -547,7 +544,7 @@ func TestGetAssetSymbolFromRequestData(t *testing.T) { require.Equal(t, e.getAssetSymbolFromRequestData(reqData), "USD/LINK") } -func TestCollectMercuryEnhancedTelemetry(t *testing.T) { +func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { wg := sync.WaitGroup{} ingressClient := mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) @@ -581,8 +578,8 @@ func TestCollectMercuryEnhancedTelemetry(t *testing.T) { wg.Add(1) chTelem <- EnhancedTelemetryMercuryData{ - TaskRunResults: trrsMercury, - Observation: mercuryv1.Observation{ + TaskRunResults: trrsMercuryV1, + V1Observation: &mercuryv1.Observation{ BenchmarkPrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(111111)}, Bid: mercury.ObsResult[*big.Int]{Val: big.NewInt(222222)}, Ask: mercury.ObsResult[*big.Int]{Val: big.NewInt(333333)}, @@ -605,8 +602,8 @@ func TestCollectMercuryEnhancedTelemetry(t *testing.T) { CurrentBlockNumber: 123456789, CurrentBlockHash: common.HexToHash("0x123321").String(), CurrentBlockTimestamp: 987654321, - BridgeTaskRunStartedTimestamp: trrsMercury[0].CreatedAt.UnixMilli(), - BridgeTaskRunEndedTimestamp: trrsMercury[0].FinishedAt.Time.UnixMilli(), + BridgeTaskRunStartedTimestamp: trrsMercuryV1[0].CreatedAt.UnixMilli(), + BridgeTaskRunEndedTimestamp: trrsMercuryV1[0].FinishedAt.Time.UnixMilli(), ProviderRequestedTimestamp: 92233720368547760, ProviderReceivedTimestamp: -92233720368547760, ProviderDataStreamEstablished: 1, @@ -637,7 +634,7 @@ func TestCollectMercuryEnhancedTelemetry(t *testing.T) { Value: nil, }}, }, - Observation: mercuryv1.Observation{}, + V1Observation: &mercuryv1.Observation{}, RepTimestamp: types.ReportTimestamp{ ConfigDigest: types.ConfigDigest{2}, Epoch: 11, @@ -645,10 +642,10 @@ func TestCollectMercuryEnhancedTelemetry(t *testing.T) { }, } wg.Add(1) - trrsMercury[0].Result.Value = "" + trrsMercuryV1[0].Result.Value = "" chTelem <- EnhancedTelemetryMercuryData{ - TaskRunResults: trrsMercury, - Observation: mercuryv1.Observation{}, + TaskRunResults: trrsMercuryV1, + V1Observation: &mercuryv1.Observation{}, RepTimestamp: types.ReportTimestamp{ ConfigDigest: types.ConfigDigest{2}, Epoch: 11, @@ -662,3 +659,119 @@ func TestCollectMercuryEnhancedTelemetry(t *testing.T) { require.Contains(t, logs.All()[1].Message, "cannot parse EA telemetry") chDone <- struct{}{} } + +func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { + wg := sync.WaitGroup{} + ingressClient := mocks.NewTelemetryService(t) + ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) + monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("0xa", synchronization.EnhancedEAMercury, "test-network", "test-chainID") + + var sentMessage []byte + ingressClient.On("Send", mock.Anything, mock.AnythingOfType("[]uint8"), mock.AnythingOfType("string"), mock.AnythingOfType("TelemetryType")).Return().Run(func(args mock.Arguments) { + sentMessage = args[1].([]byte) + wg.Done() + }) + + lggr, logs := logger.TestLoggerObserved(t, zap.WarnLevel) + chTelem := make(chan EnhancedTelemetryMercuryData, 100) + chDone := make(chan struct{}) + feedID := common.HexToHash("0x111") + e := EnhancedTelemetryService[EnhancedTelemetryMercuryData]{ + chDone: chDone, + chTelem: chTelem, + job: &job.Job{ + Type: job.Type(pipeline.OffchainReporting2JobType), + OCR2OracleSpec: &job.OCR2OracleSpec{ + CaptureEATelemetry: true, + FeedID: &feedID, + }, + }, + lggr: lggr, + monitoringEndpoint: monitoringEndpoint, + } + require.NoError(t, e.Start(testutils.Context(t))) + + wg.Add(1) + + chTelem <- EnhancedTelemetryMercuryData{ + TaskRunResults: trrsMercuryV2, + V2Observation: &mercury_v2.Observation{ + BenchmarkPrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(111111)}, + MaxFinalizedTimestamp: mercury.ObsResult[int64]{Val: 321}, + LinkPrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(4321)}, + NativePrice: mercury.ObsResult[*big.Int]{Val: big.NewInt(54321)}, + }, + RepTimestamp: types.ReportTimestamp{ + ConfigDigest: types.ConfigDigest{2}, + Epoch: 11, + Round: 22, + }, + } + + expectedTelemetry := telem.EnhancedEAMercury{ + DataSource: "data-source-name", + DpBenchmarkPrice: 123456.123456, + CurrentBlockNumber: 0, + CurrentBlockHash: "", + CurrentBlockTimestamp: 0, + BridgeTaskRunStartedTimestamp: trrsMercuryV1[0].CreatedAt.UnixMilli(), + BridgeTaskRunEndedTimestamp: trrsMercuryV1[0].FinishedAt.Time.UnixMilli(), + ProviderRequestedTimestamp: 92233720368547760, + ProviderReceivedTimestamp: -92233720368547760, + ProviderDataStreamEstablished: 1, + ProviderIndicatedTime: -123456789, + Feed: common.HexToHash("0x111").String(), + ObservationBenchmarkPrice: 111111, + ObservationBid: 0, + ObservationAsk: 0, + ConfigDigest: "0200000000000000000000000000000000000000000000000000000000000000", + Round: 22, + Epoch: 11, + AssetSymbol: "USD/LINK", + ObservationBenchmarkPriceString: "111111", + MaxFinalizedTimestamp: 321, + LinkPrice: 4321, + NativePrice: 54321, + } + + expectedMessage, _ := proto.Marshal(&expectedTelemetry) + wg.Wait() + + require.Equal(t, expectedMessage, sentMessage) + + chTelem <- EnhancedTelemetryMercuryData{ + TaskRunResults: pipeline.TaskRunResults{ + pipeline.TaskRunResult{Task: &pipeline.BridgeTask{ + BaseTask: pipeline.NewBaseTask(0, "ds1", nil, nil, 0), + }, + Result: pipeline.Result{ + Value: nil, + }}, + }, + V2Observation: &mercury_v2.Observation{}, + RepTimestamp: types.ReportTimestamp{ + ConfigDigest: types.ConfigDigest{2}, + Epoch: 11, + Round: 22, + }, + } + wg.Add(1) + trrsMercuryV2[0].Result.Value = "" + chTelem <- EnhancedTelemetryMercuryData{ + TaskRunResults: trrsMercuryV2, + V2Observation: &mercury_v2.Observation{}, + RepTimestamp: types.ReportTimestamp{ + ConfigDigest: types.ConfigDigest{2}, + Epoch: 11, + Round: 22, + }, + } + + wg.Wait() + require.Equal(t, 4, logs.Len()) + require.Contains(t, logs.All()[0].Message, "cannot parse enhanced EA telemetry bid price") + require.Contains(t, logs.All()[1].Message, "cannot get bridge response from bridge task") + require.Contains(t, logs.All()[2].Message, "cannot parse EA telemetry") + require.Contains(t, logs.All()[3].Message, "cannot parse enhanced EA telemetry bid price") + chDone <- struct{}{} +} diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 5c1f55ddab7..d225dbee68e 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -13,14 +13,14 @@ import ( relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -59,7 +59,7 @@ func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec return &datasource{pr, jb, spec, lggr, rr, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainHeadTracker, fetcher, initialBlockNumber} } -func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedBlockNum bool) (obs relaymercuryv1.Observation, err error) { +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedBlockNum bool) (obs relaymercuryv1.Observation, pipelineExecutionErr error) { // setCurrentBlock must come first, along with observationTimestamp, to // avoid front-running ds.setCurrentBlock(ctx, &obs) @@ -116,9 +116,9 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam go func() { defer wg.Done() var run *pipeline.Run - run, trrs, err = ds.executeRun(ctx) - if err != nil { - err = fmt.Errorf("Observe failed while executing run: %w", err) + run, trrs, pipelineExecutionErr = ds.executeRun(ctx) + if pipelineExecutionErr != nil { + pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) return } select { @@ -137,27 +137,30 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam } var parsed parseOutput - parsed, err = ds.parse(finaltrrs) - if err != nil { - err = fmt.Errorf("Observe failed while parsing run results: %w", err) + parsed, pipelineExecutionErr = ds.parse(finaltrrs) + if pipelineExecutionErr != nil { + pipelineExecutionErr = fmt.Errorf("Observe failed while parsing run results: %w", pipelineExecutionErr) return } obs.BenchmarkPrice = parsed.benchmarkPrice obs.Bid = parsed.bid obs.Ask = parsed.ask }() - wg.Wait() - if ocrcommon.ShouldCollectEnhancedTelemetryMercury(&ds.jb) { - ocrcommon.EnqueueEnhancedTelem(ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ - TaskRunResults: trrs, - Observation: obs, - RepTimestamp: repts, - }) + wg.Wait() + if pipelineExecutionErr != nil { + return } - return obs, err + ocrcommon.MaybeEnqueueEnhancedTelem(ds.jb, ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ + V1Observation: &obs, + TaskRunResults: trrs, + RepTimestamp: repts, + FeedVersion: mercuryutils.REPORT_V1, + }) + + return obs, nil } func toBigInt(val interface{}) (*big.Int, error) { diff --git a/core/services/relay/evm/mercury/v2/data_source.go b/core/services/relay/evm/mercury/v2/data_source.go index 10c8839a3c5..17bc4a6670c 100644 --- a/core/services/relay/evm/mercury/v2/data_source.go +++ b/core/services/relay/evm/mercury/v2/data_source.go @@ -58,7 +58,7 @@ func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec return &datasource{pr, jb, spec, feedID, lggr, rr, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} } -func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv2.Observation, err error) { +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv2.Observation, pipelineExecutionErr error) { var wg sync.WaitGroup ctx, cancel := context.WithCancel(ctx) @@ -80,15 +80,15 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam }() } + var trrs pipeline.TaskRunResults wg.Add(1) go func() { defer wg.Done() - var trrs pipeline.TaskRunResults var run *pipeline.Run - run, trrs, err = ds.executeRun(ctx) - if err != nil { + run, trrs, pipelineExecutionErr = ds.executeRun(ctx) + if pipelineExecutionErr != nil { cancel() - err = fmt.Errorf("Observe failed while executing run: %w", err) + pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) return } select { @@ -98,12 +98,12 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam } var parsed parseOutput - parsed, err = ds.parse(trrs) - if err != nil { + parsed, pipelineExecutionErr = ds.parse(trrs) + if pipelineExecutionErr != nil { cancel() // This is not expected under normal circumstances - ds.lggr.Errorw("Observe failed while parsing run results", "err", err) - err = fmt.Errorf("Observe failed while parsing run results: %w", err) + ds.lggr.Errorw("Observe failed while parsing run results", "err", pipelineExecutionErr) + pipelineExecutionErr = fmt.Errorf("Observe failed while parsing run results: %w", pipelineExecutionErr) return } obs.BenchmarkPrice = parsed.benchmarkPrice @@ -149,11 +149,12 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam wg.Wait() cancel() + if pipelineExecutionErr != nil { + return + } + if isLink || isNative { - // run has now completed so it is safe to use err or benchmark price - if err != nil { - return - } + // run has now completed so it is safe to use benchmark price if isLink { // This IS the LINK feed, use our observed price obs.LinkPrice.Val, obs.LinkPrice.Err = obs.BenchmarkPrice.Val, obs.BenchmarkPrice.Err @@ -164,16 +165,17 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam } } - // todo: implement telemetry - https://smartcontract-it.atlassian.net/browse/MERC-1388 - // if ocrcommon.ShouldCollectEnhancedTelemetryMercury(&ds.jb) { - // ocrcommon.EnqueueEnhancedTelem(ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ - // TaskRunResults: trrs, - // Observation: obs, - // RepTimestamp: repts, - // }) - // } + ocrcommon.MaybeEnqueueEnhancedTelem(ds.jb, ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ + V2Observation: &obs, + TaskRunResults: trrs, + RepTimestamp: repts, + FeedVersion: mercuryutils.REPORT_V2, + FetchMaxFinalizedTimestamp: fetchMaxFinalizedTimestamp, + IsLinkFeed: isLink, + IsNativeFeed: isNative, + }) - return obs, err + return obs, nil } func toBigInt(val interface{}) (*big.Int, error) { diff --git a/core/services/relay/evm/mercury/v3/data_source.go b/core/services/relay/evm/mercury/v3/data_source.go index 4eadfc35c23..6f2b2eb6bde 100644 --- a/core/services/relay/evm/mercury/v3/data_source.go +++ b/core/services/relay/evm/mercury/v3/data_source.go @@ -59,7 +59,7 @@ func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec return &datasource{pr, jb, spec, feedID, lggr, rr, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} } -func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv3.Observation, err error) { +func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv3.Observation, pipelineExecutionErr error) { var wg sync.WaitGroup ctx, cancel := context.WithCancel(ctx) @@ -81,15 +81,15 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam }() } + var trrs pipeline.TaskRunResults wg.Add(1) go func() { defer wg.Done() - var trrs pipeline.TaskRunResults var run *pipeline.Run - run, trrs, err = ds.executeRun(ctx) - if err != nil { + run, trrs, pipelineExecutionErr = ds.executeRun(ctx) + if pipelineExecutionErr != nil { cancel() - err = fmt.Errorf("Observe failed while executing run: %w", err) + pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) return } select { @@ -99,12 +99,12 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam } var parsed parseOutput - parsed, err = ds.parse(trrs) - if err != nil { + parsed, pipelineExecutionErr = ds.parse(trrs) + if pipelineExecutionErr != nil { cancel() // This is not expected under normal circumstances - ds.lggr.Errorw("Observe failed while parsing run results", "err", err) - err = fmt.Errorf("Observe failed while parsing run results: %w", err) + ds.lggr.Errorw("Observe failed while parsing run results", "err", pipelineExecutionErr) + pipelineExecutionErr = fmt.Errorf("Observe failed while parsing run results: %w", pipelineExecutionErr) return } obs.BenchmarkPrice = parsed.benchmarkPrice @@ -152,11 +152,12 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam wg.Wait() cancel() + if pipelineExecutionErr != nil { + return + } + if isLink || isNative { - // run has now completed so it is safe to use err or benchmark price - if err != nil { - return - } + // run has now completed so it is safe to use benchmark price if isLink { // This IS the LINK feed, use our observed price obs.LinkPrice.Val, obs.LinkPrice.Err = obs.BenchmarkPrice.Val, obs.BenchmarkPrice.Err @@ -167,17 +168,17 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam } } - // todo: implement telemetry https://smartcontract-it.atlassian.net/browse/MERC-1388 - // if ocrcommon.ShouldCollectEnhancedTelemetryMercury(&ds.jb) { - // ocrcommon.EnqueueEnhancedTelem(ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ - // TaskRunResults: trrs, - // Observation: obs, - // RepTimestamp: repts, - // }) - // } + ocrcommon.MaybeEnqueueEnhancedTelem(ds.jb, ds.chEnhancedTelem, ocrcommon.EnhancedTelemetryMercuryData{ + V3Observation: &obs, + TaskRunResults: trrs, + RepTimestamp: repts, + FeedVersion: mercuryutils.REPORT_V3, + FetchMaxFinalizedTimestamp: fetchMaxFinalizedTimestamp, + IsLinkFeed: isLink, + IsNativeFeed: isNative, + }) - cancel() - return obs, err + return obs, nil } func toBigInt(val interface{}) (*big.Int, error) { diff --git a/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go b/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go index 32c3061cc44..9cda6ef99a1 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go +++ b/core/services/synchronization/telem/telem_enhanced_ea_mercury.pb.go @@ -25,30 +25,42 @@ type EnhancedEAMercury struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - DataSource string `protobuf:"bytes,1,opt,name=data_source,json=dataSource,proto3" json:"data_source,omitempty"` - DpBenchmarkPrice float64 `protobuf:"fixed64,2,opt,name=dp_benchmark_price,json=dpBenchmarkPrice,proto3" json:"dp_benchmark_price,omitempty"` - DpBid float64 `protobuf:"fixed64,3,opt,name=dp_bid,json=dpBid,proto3" json:"dp_bid,omitempty"` - DpAsk float64 `protobuf:"fixed64,4,opt,name=dp_ask,json=dpAsk,proto3" json:"dp_ask,omitempty"` - CurrentBlockNumber int64 `protobuf:"varint,5,opt,name=current_block_number,json=currentBlockNumber,proto3" json:"current_block_number,omitempty"` - CurrentBlockHash string `protobuf:"bytes,6,opt,name=current_block_hash,json=currentBlockHash,proto3" json:"current_block_hash,omitempty"` - CurrentBlockTimestamp uint64 `protobuf:"varint,7,opt,name=current_block_timestamp,json=currentBlockTimestamp,proto3" json:"current_block_timestamp,omitempty"` - BridgeTaskRunStartedTimestamp int64 `protobuf:"varint,8,opt,name=bridge_task_run_started_timestamp,json=bridgeTaskRunStartedTimestamp,proto3" json:"bridge_task_run_started_timestamp,omitempty"` - BridgeTaskRunEndedTimestamp int64 `protobuf:"varint,9,opt,name=bridge_task_run_ended_timestamp,json=bridgeTaskRunEndedTimestamp,proto3" json:"bridge_task_run_ended_timestamp,omitempty"` - ProviderRequestedTimestamp int64 `protobuf:"varint,10,opt,name=provider_requested_timestamp,json=providerRequestedTimestamp,proto3" json:"provider_requested_timestamp,omitempty"` - ProviderReceivedTimestamp int64 `protobuf:"varint,11,opt,name=provider_received_timestamp,json=providerReceivedTimestamp,proto3" json:"provider_received_timestamp,omitempty"` - ProviderDataStreamEstablished int64 `protobuf:"varint,12,opt,name=provider_data_stream_established,json=providerDataStreamEstablished,proto3" json:"provider_data_stream_established,omitempty"` - ProviderIndicatedTime int64 `protobuf:"varint,13,opt,name=provider_indicated_time,json=providerIndicatedTime,proto3" json:"provider_indicated_time,omitempty"` - Feed string `protobuf:"bytes,14,opt,name=feed,proto3" json:"feed,omitempty"` - ObservationBenchmarkPrice int64 `protobuf:"varint,15,opt,name=observation_benchmark_price,json=observationBenchmarkPrice,proto3" json:"observation_benchmark_price,omitempty"` - ObservationBid int64 `protobuf:"varint,16,opt,name=observation_bid,json=observationBid,proto3" json:"observation_bid,omitempty"` - ObservationAsk int64 `protobuf:"varint,17,opt,name=observation_ask,json=observationAsk,proto3" json:"observation_ask,omitempty"` - ConfigDigest string `protobuf:"bytes,18,opt,name=config_digest,json=configDigest,proto3" json:"config_digest,omitempty"` - Round int64 `protobuf:"varint,19,opt,name=round,proto3" json:"round,omitempty"` - Epoch int64 `protobuf:"varint,20,opt,name=epoch,proto3" json:"epoch,omitempty"` - AssetSymbol string `protobuf:"bytes,21,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"` - ObservationBenchmarkPriceString string `protobuf:"bytes,22,opt,name=observation_benchmark_price_string,json=observationBenchmarkPriceString,proto3" json:"observation_benchmark_price_string,omitempty"` - ObservationBidString string `protobuf:"bytes,23,opt,name=observation_bid_string,json=observationBidString,proto3" json:"observation_bid_string,omitempty"` - ObservationAskString string `protobuf:"bytes,24,opt,name=observation_ask_string,json=observationAskString,proto3" json:"observation_ask_string,omitempty"` + Version uint32 `protobuf:"varint,32,opt,name=version,proto3" json:"version,omitempty"` + DataSource string `protobuf:"bytes,1,opt,name=data_source,json=dataSource,proto3" json:"data_source,omitempty"` + DpBenchmarkPrice float64 `protobuf:"fixed64,2,opt,name=dp_benchmark_price,json=dpBenchmarkPrice,proto3" json:"dp_benchmark_price,omitempty"` + DpBid float64 `protobuf:"fixed64,3,opt,name=dp_bid,json=dpBid,proto3" json:"dp_bid,omitempty"` + DpAsk float64 `protobuf:"fixed64,4,opt,name=dp_ask,json=dpAsk,proto3" json:"dp_ask,omitempty"` + // v1 fields (block range) + CurrentBlockNumber int64 `protobuf:"varint,5,opt,name=current_block_number,json=currentBlockNumber,proto3" json:"current_block_number,omitempty"` + CurrentBlockHash string `protobuf:"bytes,6,opt,name=current_block_hash,json=currentBlockHash,proto3" json:"current_block_hash,omitempty"` + CurrentBlockTimestamp uint64 `protobuf:"varint,7,opt,name=current_block_timestamp,json=currentBlockTimestamp,proto3" json:"current_block_timestamp,omitempty"` + // v2+v3 fields (timestamp range) + FetchMaxFinalizedTimestamp bool `protobuf:"varint,25,opt,name=fetch_max_finalized_timestamp,json=fetchMaxFinalizedTimestamp,proto3" json:"fetch_max_finalized_timestamp,omitempty"` + MaxFinalizedTimestamp int64 `protobuf:"varint,26,opt,name=max_finalized_timestamp,json=maxFinalizedTimestamp,proto3" json:"max_finalized_timestamp,omitempty"` + ObservationTimestamp uint32 `protobuf:"varint,27,opt,name=observation_timestamp,json=observationTimestamp,proto3" json:"observation_timestamp,omitempty"` + IsLinkFeed bool `protobuf:"varint,28,opt,name=is_link_feed,json=isLinkFeed,proto3" json:"is_link_feed,omitempty"` + LinkPrice int64 `protobuf:"varint,29,opt,name=link_price,json=linkPrice,proto3" json:"link_price,omitempty"` + IsNativeFeed bool `protobuf:"varint,30,opt,name=is_native_feed,json=isNativeFeed,proto3" json:"is_native_feed,omitempty"` + NativePrice int64 `protobuf:"varint,31,opt,name=native_price,json=nativePrice,proto3" json:"native_price,omitempty"` + BridgeTaskRunStartedTimestamp int64 `protobuf:"varint,8,opt,name=bridge_task_run_started_timestamp,json=bridgeTaskRunStartedTimestamp,proto3" json:"bridge_task_run_started_timestamp,omitempty"` + BridgeTaskRunEndedTimestamp int64 `protobuf:"varint,9,opt,name=bridge_task_run_ended_timestamp,json=bridgeTaskRunEndedTimestamp,proto3" json:"bridge_task_run_ended_timestamp,omitempty"` + ProviderRequestedTimestamp int64 `protobuf:"varint,10,opt,name=provider_requested_timestamp,json=providerRequestedTimestamp,proto3" json:"provider_requested_timestamp,omitempty"` + ProviderReceivedTimestamp int64 `protobuf:"varint,11,opt,name=provider_received_timestamp,json=providerReceivedTimestamp,proto3" json:"provider_received_timestamp,omitempty"` + ProviderDataStreamEstablished int64 `protobuf:"varint,12,opt,name=provider_data_stream_established,json=providerDataStreamEstablished,proto3" json:"provider_data_stream_established,omitempty"` + ProviderIndicatedTime int64 `protobuf:"varint,13,opt,name=provider_indicated_time,json=providerIndicatedTime,proto3" json:"provider_indicated_time,omitempty"` + Feed string `protobuf:"bytes,14,opt,name=feed,proto3" json:"feed,omitempty"` + // v1+v2+v3 + ObservationBenchmarkPrice int64 `protobuf:"varint,15,opt,name=observation_benchmark_price,json=observationBenchmarkPrice,proto3" json:"observation_benchmark_price,omitempty"` // This value overflows, will be reserved and removed in future versions + ObservationBenchmarkPriceString string `protobuf:"bytes,22,opt,name=observation_benchmark_price_string,json=observationBenchmarkPriceString,proto3" json:"observation_benchmark_price_string,omitempty"` + // v1+v3 + ObservationBid int64 `protobuf:"varint,16,opt,name=observation_bid,json=observationBid,proto3" json:"observation_bid,omitempty"` // This value overflows, will be reserved and removed in future versions + ObservationAsk int64 `protobuf:"varint,17,opt,name=observation_ask,json=observationAsk,proto3" json:"observation_ask,omitempty"` // This value overflows, will be reserved and removed in future versions + ObservationBidString string `protobuf:"bytes,23,opt,name=observation_bid_string,json=observationBidString,proto3" json:"observation_bid_string,omitempty"` + ObservationAskString string `protobuf:"bytes,24,opt,name=observation_ask_string,json=observationAskString,proto3" json:"observation_ask_string,omitempty"` + ConfigDigest string `protobuf:"bytes,18,opt,name=config_digest,json=configDigest,proto3" json:"config_digest,omitempty"` + Round int64 `protobuf:"varint,19,opt,name=round,proto3" json:"round,omitempty"` + Epoch int64 `protobuf:"varint,20,opt,name=epoch,proto3" json:"epoch,omitempty"` + AssetSymbol string `protobuf:"bytes,21,opt,name=asset_symbol,json=assetSymbol,proto3" json:"asset_symbol,omitempty"` } func (x *EnhancedEAMercury) Reset() { @@ -83,6 +95,13 @@ func (*EnhancedEAMercury) Descriptor() ([]byte, []int) { return file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDescGZIP(), []int{0} } +func (x *EnhancedEAMercury) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + func (x *EnhancedEAMercury) GetDataSource() string { if x != nil { return x.DataSource @@ -132,6 +151,55 @@ func (x *EnhancedEAMercury) GetCurrentBlockTimestamp() uint64 { return 0 } +func (x *EnhancedEAMercury) GetFetchMaxFinalizedTimestamp() bool { + if x != nil { + return x.FetchMaxFinalizedTimestamp + } + return false +} + +func (x *EnhancedEAMercury) GetMaxFinalizedTimestamp() int64 { + if x != nil { + return x.MaxFinalizedTimestamp + } + return 0 +} + +func (x *EnhancedEAMercury) GetObservationTimestamp() uint32 { + if x != nil { + return x.ObservationTimestamp + } + return 0 +} + +func (x *EnhancedEAMercury) GetIsLinkFeed() bool { + if x != nil { + return x.IsLinkFeed + } + return false +} + +func (x *EnhancedEAMercury) GetLinkPrice() int64 { + if x != nil { + return x.LinkPrice + } + return 0 +} + +func (x *EnhancedEAMercury) GetIsNativeFeed() bool { + if x != nil { + return x.IsNativeFeed + } + return false +} + +func (x *EnhancedEAMercury) GetNativePrice() int64 { + if x != nil { + return x.NativePrice + } + return 0 +} + func (x *EnhancedEAMercury) GetBridgeTaskRunStartedTimestamp() int64 { if x != nil { return x.BridgeTaskRunStartedTimestamp @@ -188,6 +256,13 @@ func (x *EnhancedEAMercury) GetObservationBenchmarkPrice() int64 { return 0 } +func (x *EnhancedEAMercury) GetObservationBenchmarkPriceString() string { + if x != nil { + return x.ObservationBenchmarkPriceString + } + return "" +} + func (x *EnhancedEAMercury) GetObservationBid() int64 { if x != nil { return x.ObservationBid @@ -202,6 +277,20 @@ func (x *EnhancedEAMercury) GetObservationAsk() int64 { return 0 } +func (x *EnhancedEAMercury) GetObservationBidString() string { + if x != nil { + return x.ObservationBidString + } + return "" +} + +func (x *EnhancedEAMercury) GetObservationAskString() string { + if x != nil { + return x.ObservationAskString + } + return "" +} + func (x *EnhancedEAMercury) GetConfigDigest() string { if x != nil { return x.ConfigDigest @@ -230,27 +319,6 @@ func (x *EnhancedEAMercury) GetAssetSymbol() string { return "" } -func (x *EnhancedEAMercury) GetObservationBenchmarkPriceString() string { - if x != nil { - return x.ObservationBenchmarkPriceString - } - return "" -} - -func (x *EnhancedEAMercury) GetObservationBidString() string { - if x != nil { - return x.ObservationBidString - } - return "" -} - -func (x *EnhancedEAMercury) GetObservationAskString() string { - if x != nil { - return x.ObservationAskString - } - return "" -} - var File_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto protoreflect.FileDescriptor var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_rawDesc = []byte{ @@ -258,86 +326,107 @@ var file_core_services_synchronization_telem_telem_enhanced_ea_mercury_proto_raw 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x65, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x5f, 0x65, 0x61, 0x5f, 0x6d, 0x65, 0x72, 0x63, 0x75, 0x72, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x22, 0x8e, 0x09, 0x0a, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x22, 0xe2, 0x0b, 0x0a, 0x11, 0x45, 0x6e, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x64, 0x45, 0x41, 0x4d, 0x65, 0x72, 0x63, 0x75, - 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x70, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, - 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x10, 0x64, 0x70, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, - 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x70, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x05, 0x64, 0x70, 0x42, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x70, 0x5f, 0x61, - 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, 0x70, 0x41, 0x73, 0x6b, 0x12, - 0x30, 0x0a, 0x14, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, - 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, - 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x48, 0x0a, 0x21, 0x62, 0x72, 0x69, 0x64, 0x67, - 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x1d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, - 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x12, 0x44, 0x0a, 0x1f, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, - 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, 0x62, 0x72, 0x69, 0x64, - 0x67, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x40, 0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1a, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3e, 0x0a, 0x1b, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x47, 0x0a, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x5f, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x44, 0x61, 0x74, - 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, - 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x65, - 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x65, 0x65, 0x64, 0x12, 0x3e, - 0x0a, 0x1b, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x65, - 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x19, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, - 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, 0x72, - 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, - 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, - 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, - 0x70, 0x6f, 0x63, 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, - 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, - 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, - 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x4b, 0x0a, 0x22, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, - 0x69, 0x63, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x1f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x65, 0x6e, - 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x20, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2c, 0x0a, + 0x12, 0x64, 0x70, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, + 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x10, 0x64, 0x70, 0x42, 0x65, 0x6e, + 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x64, + 0x70, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x64, 0x70, 0x42, + 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x64, 0x70, 0x5f, 0x61, 0x73, 0x6b, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x05, 0x64, 0x70, 0x41, 0x73, 0x6b, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x41, 0x0a, 0x1d, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x66, + 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x66, 0x65, 0x74, 0x63, 0x68, 0x4d, + 0x61, 0x78, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x69, 0x6e, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x1a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x46, 0x69, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x33, 0x0a, 0x15, + 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x14, 0x6f, 0x62, 0x73, + 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x66, 0x65, 0x65, + 0x64, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x46, + 0x65, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x69, 0x6e, 0x6b, 0x50, 0x72, 0x69, + 0x63, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x73, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, + 0x66, 0x65, 0x65, 0x64, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x4e, 0x61, + 0x74, 0x69, 0x76, 0x65, 0x46, 0x65, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, + 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x21, 0x62, + 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1d, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x54, 0x61, + 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x44, 0x0a, 0x1f, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x5f, + 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1b, + 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x75, 0x6e, 0x45, 0x6e, 0x64, + 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x40, 0x0a, 0x1c, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x1a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3e, 0x0a, + 0x1b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, + 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x19, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x47, 0x0a, + 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x65, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, + 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x1d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x73, 0x74, 0x61, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x65, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x65, + 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1b, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x19, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, + 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x22, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x62, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x5f, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1f, + 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x65, 0x6e, 0x63, 0x68, + 0x6d, 0x61, 0x72, 0x6b, 0x50, 0x72, 0x69, 0x63, 0x65, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, + 0x27, 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, + 0x69, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x62, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, + 0x6b, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x73, 0x6b, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x4e, 0x5a, - 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, - 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, + 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x67, 0x65, + 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, + 0x68, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x21, + 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x15, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x79, 0x6d, 0x62, 0x6f, + 0x6c, 0x42, 0x4e, 0x5a, 0x4c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x73, 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, + 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, + 0x72, 0x65, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, + 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x74, 0x65, 0x6c, 0x65, + 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto b/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto index 92242fd2a1e..a527552bb6b 100644 --- a/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto +++ b/core/services/synchronization/telem/telem_enhanced_ea_mercury.proto @@ -5,28 +5,49 @@ option go_package = "github.com/smartcontractkit/chainlink/v2/core/services/sync package telem; message EnhancedEAMercury { + uint32 version = 32; + string data_source=1; double dp_benchmark_price=2; double dp_bid=3; double dp_ask=4; + + // v1 fields (block range) int64 current_block_number=5; string current_block_hash=6; uint64 current_block_timestamp=7; + + // v2+v3 fields (timestamp range) + bool fetch_max_finalized_timestamp = 25; + int64 max_finalized_timestamp=26; + uint32 observation_timestamp=27; + bool is_link_feed=28; + int64 link_price=29; + bool is_native_feed=30; + int64 native_price=31; + int64 bridge_task_run_started_timestamp=8; int64 bridge_task_run_ended_timestamp=9; int64 provider_requested_timestamp=10; int64 provider_received_timestamp=11; int64 provider_data_stream_established=12; int64 provider_indicated_time=13; + string feed=14; - int64 observation_benchmark_price=15; - int64 observation_bid=16; - int64 observation_ask=17; + + // v1+v2+v3 + int64 observation_benchmark_price=15; // This value overflows, will be reserved and removed in future versions + string observation_benchmark_price_string = 22; + // v1+v3 + int64 observation_bid=16; // This value overflows, will be reserved and removed in future versions + int64 observation_ask=17; // This value overflows, will be reserved and removed in future versions + string observation_bid_string = 23; + string observation_ask_string = 24; + string config_digest = 18; int64 round=19; int64 epoch=20; string asset_symbol=21; - string observation_benchmark_price_string = 22; - string observation_bid_string = 23; - string observation_ask_string = 24; + + } diff --git a/tools/flakeytests/coverage.txt b/tools/flakeytests/coverage.txt new file mode 100644 index 00000000000..91640016fe2 --- /dev/null +++ b/tools/flakeytests/coverage.txt @@ -0,0 +1,93 @@ +mode: atomic +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:50.103,54.38 4 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:54.38,55.24 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:55.24,62.18 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:62.18,64.5 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:65.4,65.46 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:72.2,73.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:73.16,75.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:77.2,90.16 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:93.63,95.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:95.16,97.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:99.2,101.16 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:101.16,103.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:104.2,110.16 4 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:110.16,112.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:112.8,112.52 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:112.52,114.18 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:114.18,116.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:117.3,117.83 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:119.2,119.12 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:122.81,124.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:124.16,126.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:128.2,128.31 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:131.77,133.2 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:40.79,44.30 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:44.31,44.32 0 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:46.2,52.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:61.75,70.2 8 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:81.45,85.2 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:87.75,89.28 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:89.28,91.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:91.16,93.19 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:93.19,94.13 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:99.4,99.42 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:99.42,100.13 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:103.4,104.18 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:104.18,106.5 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:110.4,110.39 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:110.39,111.13 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:114.4,114.20 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:115.16,116.32 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:116.32,118.6 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:119.5,119.31 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:120.18,121.38 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:121.38,122.33 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:122.33,124.7 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:125.6,125.32 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:130.3,130.33 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:130.33,132.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:134.2,134.19 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:141.106,144.38 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:144.38,146.27 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:146.27,148.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:150.3,151.36 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:151.36,155.18 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:155.18,161.55 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:161.55,162.14 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:164.5,164.32 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:167.4,168.18 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:168.18,170.5 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:172.4,172.25 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:172.25,174.22 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:174.22,175.37 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:175.37,177.7 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:178.6,178.42 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:184.2,184.29 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:187.30,189.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:189.16,191.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:193.2,194.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:194.16,196.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:198.2,198.30 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:198.30,200.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:200.8,202.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:204.2,204.43 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:12.74,15.25 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:15.25,17.10 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:17.10,19.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:21.3,21.10 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:24.2,25.9 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:25.9,27.3 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:29.2,29.16 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:32.88,34.16 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:34.16,36.17 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:36.17,38.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:40.3,41.17 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:41.17,43.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:45.3,46.17 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:46.17,48.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:51.2,52.19 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:53.22,56.17 3 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:56.17,58.4 1 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:60.3,61.19 2 0 +github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:62.10,63.19 1 0 From 67a79f134e516b51ebf597e2eda0ff587c14d37d Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 30 Oct 2023 12:40:19 -0500 Subject: [PATCH 032/271] core/utils: deprecate StartStopOnce in favor of services.StateMachine (#11117) --- common/headtracker/head_broadcaster.go | 26 ++- common/headtracker/head_tracker.go | 4 +- common/txmgr/broadcaster.go | 3 +- common/txmgr/confirmer.go | 3 +- common/txmgr/txmgr.go | 2 +- core/chains/cosmos/chain.go | 5 +- core/chains/cosmos/cosmostxm/txm.go | 23 +-- core/chains/evm/chain.go | 4 +- core/chains/evm/client/node.go | 3 +- core/chains/evm/client/pool.go | 6 +- core/chains/evm/client/send_only_node.go | 3 +- .../evm/forwarders/forwarder_manager.go | 7 +- core/chains/evm/gas/arbitrum_estimator.go | 5 +- .../chains/evm/gas/block_history_estimator.go | 3 +- core/chains/evm/gas/l2_suggested_estimator.go | 3 +- core/chains/evm/gas/models.go | 3 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 3 +- core/chains/evm/log/broadcaster.go | 7 +- core/chains/evm/logpoller/log_poller.go | 7 +- core/chains/evm/monitor/balance.go | 9 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/blockhashstore/delegate.go | 3 +- core/services/blockheaderfeeder/delegate.go | 3 +- core/services/directrequest/delegate.go | 5 +- core/services/feeds/service.go | 3 +- core/services/fluxmonitorv2/flux_monitor.go | 4 +- core/services/functions/connector_handler.go | 9 +- core/services/functions/listener.go | 8 +- core/services/gateway/connectionmanager.go | 2 +- core/services/gateway/connector/connector.go | 2 +- core/services/gateway/gateway.go | 3 +- .../gateway/handlers/functions/allowlist.go | 3 +- .../handlers/functions/handler.functions.go | 3 +- .../handlers/functions/subscriptions.go | 3 +- core/services/gateway/network/httpserver.go | 4 +- core/services/gateway/network/wsconnection.go | 3 +- core/services/gateway/network/wsserver.go | 4 +- core/services/job/spawner.go | 2 +- .../keeper/registry_synchronizer_core.go | 3 +- core/services/keeper/upkeep_executer.go | 3 +- core/services/nurse.go | 3 +- core/services/ocr/config_overrider.go | 6 +- core/services/ocr/contract_tracker.go | 3 +- core/services/ocr2/plugins/median/plugin.go | 3 +- .../plugins/ocr2keeper/evm20/log_provider.go | 5 +- .../ocr2/plugins/ocr2keeper/evm20/registry.go | 7 +- .../ocr2keeper/evm21/block_subscriber.go | 4 +- .../ocr2keeper/evm21/logprovider/provider.go | 4 +- .../ocr2keeper/evm21/logprovider/recoverer.go | 4 +- .../ocr2/plugins/ocr2keeper/evm21/registry.go | 3 +- .../evm21/transmit/event_provider.go | 5 +- .../ocr2keeper/evm21/upkeepstate/store.go | 3 +- core/services/ocrcommon/peer_wrapper.go | 11 +- core/services/ocrcommon/peerstore.go | 6 +- core/services/ocrcommon/run_saver.go | 4 +- core/services/ocrcommon/telemetry.go | 7 +- core/services/periodicbackup/backup.go | 12 +- core/services/pg/event_broadcaster.go | 7 +- core/services/pipeline/orm.go | 14 +- core/services/pipeline/runner.go | 10 +- core/services/promreporter/prom_reporter.go | 6 +- core/services/relay/evm/config_poller.go | 5 +- core/services/relay/evm/evm.go | 7 +- core/services/relay/evm/functions.go | 8 +- .../relay/evm/functions/logpoller_wrapper.go | 3 +- .../relay/evm/mercury/persistence_manager.go | 3 +- core/services/relay/evm/mercury/queue.go | 9 +- .../services/relay/evm/mercury/transmitter.go | 4 +- .../relay/evm/mercury/wsrpc/client.go | 10 +- .../relay/evm/request_round_tracker.go | 5 +- .../telemetry_ingress_batch_client.go | 4 +- .../telemetry_ingress_client.go | 3 +- core/services/telemetry/manager.go | 11 +- core/services/telemetry/manager_test.go | 10 +- core/services/vrf/v1/listener_v1.go | 3 +- core/services/vrf/v2/listener_v2.go | 3 +- core/utils/helpers_test.go | 5 - core/utils/mailbox_prom.go | 4 +- core/utils/sleeper_task.go | 15 +- core/utils/utils.go | 181 +----------------- core/utils/utils_test.go | 105 ---------- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 86 files changed, 254 insertions(+), 488 deletions(-) delete mode 100644 core/utils/helpers_test.go diff --git a/common/headtracker/head_broadcaster.go b/common/headtracker/head_broadcaster.go index 17d50ef5628..e9ae93419bb 100644 --- a/common/headtracker/head_broadcaster.go +++ b/common/headtracker/head_broadcaster.go @@ -7,6 +7,7 @@ import ( "sync" "time" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -25,13 +26,13 @@ func (set callbackSet[H, BLOCK_HASH]) values() []types.HeadTrackable[H, BLOCK_HA } type HeadBroadcaster[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct { - logger logger.Logger - callbacks callbackSet[H, BLOCK_HASH] - mailbox *utils.Mailbox[H] - mutex *sync.Mutex - chClose utils.StopChan - wgDone sync.WaitGroup - utils.StartStopOnce + services.StateMachine + logger logger.Logger + callbacks callbackSet[H, BLOCK_HASH] + mailbox *utils.Mailbox[H] + mutex sync.Mutex + chClose utils.StopChan + wgDone sync.WaitGroup latest H lastCallbackID int } @@ -44,13 +45,10 @@ func NewHeadBroadcaster[ lggr logger.Logger, ) *HeadBroadcaster[H, BLOCK_HASH] { return &HeadBroadcaster[H, BLOCK_HASH]{ - logger: lggr.Named("HeadBroadcaster"), - callbacks: make(callbackSet[H, BLOCK_HASH]), - mailbox: utils.NewSingleMailbox[H](), - mutex: &sync.Mutex{}, - chClose: make(chan struct{}), - wgDone: sync.WaitGroup{}, - StartStopOnce: utils.StartStopOnce{}, + logger: lggr.Named("HeadBroadcaster"), + callbacks: make(callbackSet[H, BLOCK_HASH]), + mailbox: utils.NewSingleMailbox[H](), + chClose: make(chan struct{}), } } diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 1978b281d44..c24dde595cf 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -40,6 +40,7 @@ type HeadTracker[ ID types.ID, BLOCK_HASH types.Hashable, ] struct { + services.StateMachine log logger.Logger headBroadcaster types.HeadBroadcaster[HTH, BLOCK_HASH] headSaver types.HeadSaver[HTH, BLOCK_HASH] @@ -54,8 +55,7 @@ type HeadTracker[ headListener types.HeadListener[HTH, BLOCK_HASH] chStop utils.StopChan wgDone sync.WaitGroup - utils.StartStopOnce - getNilHead func() HTH + getNilHead func() HTH } // NewHeadTracker instantiates a new HeadTracker using HeadSaver to persist new block numbers. diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 6512f67fe0b..011866bf39d 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -14,6 +14,7 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-relay/pkg/services" clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -105,6 +106,7 @@ type Broadcaster[ SEQ types.Sequence, FEE feetypes.Fee, ] struct { + services.StateMachine logger logger.Logger txStore txmgrtypes.TransactionStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, SEQ, FEE] client txmgrtypes.TransactionClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] @@ -140,7 +142,6 @@ type Broadcaster[ initSync sync.Mutex isStarted bool - utils.StartStopOnce parseAddr func(string) (ADDR, error) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 31bba771410..c22a1594570 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -14,6 +14,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/services" clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" @@ -112,7 +113,7 @@ type Confirmer[ SEQ types.Sequence, FEE feetypes.Fee, ] struct { - utils.StartStopOnce + services.StateMachine txStore txmgrtypes.TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] lggr logger.Logger client txmgrtypes.TxmClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 980067b5fbf..0c7117afab0 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -69,7 +69,7 @@ type Txm[ SEQ types.Sequence, FEE feetypes.Fee, ] struct { - utils.StartStopOnce + services.StateMachine logger logger.Logger txStore txmgrtypes.TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] config txmgrtypes.TransactionManagerChainConfig diff --git a/core/chains/cosmos/chain.go b/core/chains/cosmos/chain.go index 43ba5a4e796..bb44c4bee61 100644 --- a/core/chains/cosmos/chain.go +++ b/core/chains/cosmos/chain.go @@ -27,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // DefaultRequestTimeout is the default Cosmos client timeout. @@ -93,7 +92,7 @@ func NewChain(cfg *CosmosConfig, opts ChainOpts) (adapters.Chain, error) { var _ adapters.Chain = (*chain)(nil) type chain struct { - utils.StartStopOnce + services.StateMachine id string cfg *CosmosConfig txm *cosmostxm.Txm @@ -197,7 +196,7 @@ func (c *chain) Close() error { func (c *chain) Ready() error { return multierr.Combine( - c.StartStopOnce.Ready(), + c.StateMachine.Ready(), c.txm.Ready(), ) } diff --git a/core/chains/cosmos/cosmostxm/txm.go b/core/chains/cosmos/cosmostxm/txm.go index e9fb2f6aca3..8806d97a9fb 100644 --- a/core/chains/cosmos/cosmostxm/txm.go +++ b/core/chains/cosmos/cosmostxm/txm.go @@ -28,20 +28,20 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( - _ services.ServiceCtx = (*Txm)(nil) - _ adapters.TxManager = (*Txm)(nil) + _ services.Service = (*Txm)(nil) + _ adapters.TxManager = (*Txm)(nil) ) // Txm manages transactions for the cosmos blockchain. type Txm struct { - starter utils.StartStopOnce + services.StateMachine eb pg.EventBroadcaster sub pg.Subscription orm *ORM @@ -58,7 +58,6 @@ func NewTxm(db *sqlx.DB, tc func() (cosmosclient.ReaderWriter, error), gpe cosmo lggr = logger.Named(lggr, "Txm") keystoreAdapter := NewKeystoreAdapter(ks, cfg.Bech32Prefix()) return &Txm{ - starter: utils.StartStopOnce{}, eb: eb, orm: NewORM(chainID, db, lggr, logCfg), lggr: lggr, @@ -73,7 +72,7 @@ func NewTxm(db *sqlx.DB, tc func() (cosmosclient.ReaderWriter, error), gpe cosmo // Start subscribes to pg notifications about cosmos msg inserts and processes them. func (txm *Txm) Start(context.Context) error { - return txm.starter.StartOnce("cosmostxm", func() error { + return txm.StartOnce("Txm", func() error { sub, err := txm.eb.Subscribe(pg.ChannelInsertOnCosmosMsg, "") if err != nil { return err @@ -520,13 +519,15 @@ func (txm *Txm) GasPrice() (sdk.DecCoin, error) { // Close close service func (txm *Txm) Close() error { - txm.sub.Close() - close(txm.stop) - <-txm.done - return nil + return txm.StopOnce("Txm", func() error { + txm.sub.Close() + close(txm.stop) + <-txm.done + return nil + }) } -func (txm *Txm) Name() string { return "cosmostxm" } +func (txm *Txm) Name() string { return txm.lggr.Name() } // Healthy service is healthy func (txm *Txm) Healthy() error { diff --git a/core/chains/evm/chain.go b/core/chains/evm/chain.go index 2646d25867b..6eed13271e3 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/evm/chain.go @@ -108,7 +108,7 @@ func (c *LegacyChains) Get(id string) (Chain, error) { } type chain struct { - utils.StartStopOnce + services.StateMachine id *big.Int cfg *evmconfig.ChainScoped client evmclient.Client @@ -366,7 +366,7 @@ func (c *chain) Close() error { func (c *chain) Ready() (merr error) { merr = multierr.Combine( - c.StartStopOnce.Ready(), + c.StateMachine.Ready(), c.txm.Ready(), c.headBroadcaster.Ready(), c.headTracker.Ready(), diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index 4f7132a6cc4..80bac25448d 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -19,6 +19,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -132,7 +133,7 @@ type rawclient struct { // Node represents one ethereum node. // It must have a ws url and may have a http url type node struct { - utils.StartStopOnce + services.StateMachine lfcLog logger.Logger rpcLog logger.Logger name string diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 7e4667623de..2d679ab3d73 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -15,11 +15,11 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -57,7 +57,7 @@ type PoolConfig interface { // Pool represents an abstraction over one or more primary nodes // It is responsible for liveness checking and balancing queries across live nodes type Pool struct { - utils.StartStopOnce + services.StateMachine nodes []Node sendonlys []SendOnlyNode chainID *big.Int diff --git a/core/chains/evm/client/send_only_node.go b/core/chains/evm/client/send_only_node.go index a085ec47531..3f2481c1899 100644 --- a/core/chains/evm/client/send_only_node.go +++ b/core/chains/evm/client/send_only_node.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -56,7 +57,7 @@ var _ SendOnlyNode = &sendOnlyNode{} // It only supports sending transactions // It must a http(s) url type sendOnlyNode struct { - utils.StartStopOnce + services.StateMachine stateMu sync.RWMutex // protects state* fields state NodeState diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index 0c470e76d8c..46bca95ba30 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -9,8 +9,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmlogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -30,7 +32,7 @@ type Config interface { } type FwdMgr struct { - utils.StartStopOnce + services.StateMachine ORM ORM evmClient evmclient.Client cfg Config @@ -61,9 +63,6 @@ func NewFwdMgr(db *sqlx.DB, client evmclient.Client, logpoller evmlogpoller.LogP ORM: NewORM(db, lggr, dbConfig), logpoller: logpoller, sendersCache: make(map[common.Address][]common.Address), - cacheMu: sync.RWMutex{}, - wg: sync.WaitGroup{}, - latestBlock: 0, } fwdMgr.ctx, fwdMgr.cancel = context.WithCancel(context.Background()) return &fwdMgr diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index 6c2b5e8b879..17934bfa070 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -33,6 +33,7 @@ type ethClient interface { // arbitrumEstimator is an Estimator which extends l2SuggestedPriceEstimator to use getPricesInArbGas() for gas limit estimation. type arbitrumEstimator struct { + services.StateMachine cfg ArbConfig EvmEstimator // *l2SuggestedPriceEstimator @@ -49,8 +50,6 @@ type arbitrumEstimator struct { chInitialised chan struct{} chStop utils.StopChan chDone chan struct{} - - utils.StartStopOnce } func NewArbitrumEstimator(lggr logger.Logger, cfg ArbConfig, rpcClient rpcClient, ethClient ethClient) EvmEstimator { @@ -91,7 +90,7 @@ func (a *arbitrumEstimator) Close() error { }) } -func (a *arbitrumEstimator) Ready() error { return a.StartStopOnce.Ready() } +func (a *arbitrumEstimator) Ready() error { return a.StateMachine.Ready() } func (a *arbitrumEstimator) HealthReport() map[string]error { hp := map[string]error{a.Name(): a.Healthy()} diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 14b18ad66ba..42c8f051535 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -15,6 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/assets" @@ -96,7 +97,7 @@ type estimatorGasEstimatorConfig interface { //go:generate mockery --quiet --name Config --output ./mocks/ --case=underscore type ( BlockHistoryEstimator struct { - utils.StartStopOnce + services.StateMachine ethClient evmclient.Client chainID big.Int config chainConfig diff --git a/core/chains/evm/gas/l2_suggested_estimator.go b/core/chains/evm/gas/l2_suggested_estimator.go index 1782e349302..8e6c06a128d 100644 --- a/core/chains/evm/gas/l2_suggested_estimator.go +++ b/core/chains/evm/gas/l2_suggested_estimator.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -28,7 +29,7 @@ type rpcClient interface { // l2SuggestedPriceEstimator is an Estimator which uses the L2 suggested gas price from eth_gasPrice. type l2SuggestedPriceEstimator struct { - utils.StartStopOnce + services.StateMachine client rpcClient pollPeriod time.Duration diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index bd3106c2ad4..7bd88d75433 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -22,7 +22,6 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) @@ -150,10 +149,10 @@ func (fee EvmFee) ValidDynamic() bool { // WrappedEvmEstimator provides a struct that wraps the EVM specific dynamic and legacy estimators into one estimator that conforms to the generic FeeEstimator type WrappedEvmEstimator struct { + services.StateMachine EvmEstimator EIP1559Enabled bool l1Oracle rollups.L1Oracle - utils.StartStopOnce } var _ EvmFeeEstimator = (*WrappedEvmEstimator)(nil) diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index 8cf10325d47..c15aa23c792 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/config" @@ -26,6 +27,7 @@ type ethClient interface { // Reads L2-specific precompiles and caches the l1GasPrice set by the L2. type l1GasPriceOracle struct { + services.StateMachine client ethClient pollPeriod time.Duration logger logger.Logger @@ -38,7 +40,6 @@ type l1GasPriceOracle struct { chInitialised chan struct{} chStop utils.StopChan chDone chan struct{} - utils.StartStopOnce } const ( diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index bc9ba1cf6cf..9c4050fd796 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -12,13 +12,14 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -44,7 +45,7 @@ type ( // Of course, these backfilled logs + any new logs will only be sent after the NumConfirmations for given subscriber. Broadcaster interface { utils.DependentAwaiter - services.ServiceCtx + services.Service httypes.HeadTrackable // ReplayFromBlock enqueues a replay from the provided block number. If forceBroadcast is @@ -88,6 +89,7 @@ type ( } broadcaster struct { + services.StateMachine orm ORM config Config connected atomic.Bool @@ -106,7 +108,6 @@ type ( changeSubscriberStatus *utils.Mailbox[changeSubscriberStatus] newHeads *utils.Mailbox[*evmtypes.Head] - utils.StartStopOnce utils.DependentAwaiter chStop utils.StopChan diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 54999cbdfb1..6cda8f5b46c 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -20,10 +20,11 @@ import ( "github.com/pkg/errors" "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" @@ -31,7 +32,7 @@ import ( //go:generate mockery --quiet --name LogPoller --output ./mocks/ --case=underscore --structname LogPoller --filename log_poller.go type LogPoller interface { - services.ServiceCtx + services.Service Replay(ctx context.Context, fromBlock int64) error ReplayAsync(fromBlock int64) RegisterFilter(filter Filter, qopts ...pg.QOpt) error @@ -92,7 +93,7 @@ var ( ) type logPoller struct { - utils.StartStopOnce + services.StateMachine ec Client orm ORM lggr logger.Logger diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index 94434b733e6..5f5b8a243b0 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -13,12 +13,13 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -29,11 +30,11 @@ type ( BalanceMonitor interface { httypes.HeadTrackable GetEthBalance(gethCommon.Address) *assets.Eth - services.ServiceCtx + services.Service } balanceMonitor struct { - utils.StartStopOnce + services.StateMachine logger logger.Logger ethClient evmclient.Client chainID *big.Int @@ -53,7 +54,7 @@ var _ BalanceMonitor = (*balanceMonitor)(nil) func NewBalanceMonitor(ethClient evmclient.Client, ethKeyStore keystore.Eth, logger logger.Logger) *balanceMonitor { chainId := ethClient.ConfiguredChainID() bm := &balanceMonitor{ - utils.StartStopOnce{}, + services.StateMachine{}, logger.Named("BalanceMonitor"), ethClient, chainId, diff --git a/core/scripts/go.mod b/core/scripts/go.mod index f097ea89be5..7c0161a066a 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -302,7 +302,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 0461daa25e9..e0e5e07ecf8 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1458,8 +1458,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/blockhashstore/delegate.go b/core/services/blockhashstore/delegate.go index 90819f27a1d..123052550ba 100644 --- a/core/services/blockhashstore/delegate.go +++ b/core/services/blockhashstore/delegate.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" @@ -197,7 +198,7 @@ func (d *Delegate) OnDeleteJob(spec job.Job, q pg.Queryer) error { return nil } // service is a job.Service that runs the BHS feeder every pollPeriod. type service struct { - utils.StartStopOnce + services.StateMachine feeder *Feeder wg sync.WaitGroup pollPeriod time.Duration diff --git a/core/services/blockheaderfeeder/delegate.go b/core/services/blockheaderfeeder/delegate.go index 02ab34821f6..971a691d773 100644 --- a/core/services/blockheaderfeeder/delegate.go +++ b/core/services/blockheaderfeeder/delegate.go @@ -8,6 +8,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -211,7 +212,7 @@ func (d *Delegate) OnDeleteJob(spec job.Job, q pg.Queryer) error { return nil } // service is a job.Service that runs the BHS feeder every pollPeriod. type service struct { - utils.StartStopOnce + services.StateMachine feeder *BlockHeaderFeeder done chan struct{} pollPeriod time.Duration diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index 75cebb7a74a..174dca062aa 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -9,13 +9,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -119,6 +120,7 @@ var ( ) type listener struct { + services.StateMachine logger logger.Logger config Config logBroadcaster log.Broadcaster @@ -135,7 +137,6 @@ type listener struct { requesters models.AddressCollection minContractPayment *assets.Link chStop chan struct{} - utils.StartStopOnce } func (l *listener) HealthReport() map[string]error { diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index 4c3647fde49..20919606faf 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" @@ -98,7 +99,7 @@ type Service interface { } type service struct { - utils.StartStopOnce + services.StateMachine orm ORM jobORM job.ORM diff --git a/core/services/fluxmonitorv2/flux_monitor.go b/core/services/fluxmonitorv2/flux_monitor.go index fe8a22d4177..99d33c42399 100644 --- a/core/services/fluxmonitorv2/flux_monitor.go +++ b/core/services/fluxmonitorv2/flux_monitor.go @@ -15,6 +15,7 @@ import ( "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -55,6 +56,7 @@ const DefaultHibernationPollPeriod = 24 * time.Hour // FluxMonitor polls external price adapters via HTTP to check for price swings. type FluxMonitor struct { + services.StateMachine contractAddress common.Address oracleAddress common.Address jobSpec job.Job @@ -80,7 +82,6 @@ type FluxMonitor struct { backlog *utils.BoundedPriorityQueue[log.Broadcast] chProcessLogs chan struct{} - utils.StartStopOnce chStop chan struct{} waitOnStop chan struct{} } @@ -134,7 +135,6 @@ func NewFluxMonitor( PriorityAnswerUpdatedLog: 1, PriorityFlagChangedLog: 2, }), - StartStopOnce: utils.StartStopOnce{}, chProcessLogs: make(chan struct{}, 1), chStop: make(chan struct{}), waitOnStop: make(chan struct{}), diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index c32bf56f0cd..a018157a373 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -8,6 +8,10 @@ import ( "go.uber.org/multierr" + ethCommon "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" @@ -16,13 +20,10 @@ import ( hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/chainlink/v2/core/utils" - - ethCommon "github.com/ethereum/go-ethereum/common" ) type functionsConnectorHandler struct { - utils.StartStopOnce + services.StateMachine connector connector.GatewayConnector signerKey *ecdsa.PrivateKey diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index b76eb0a1c05..773ae610408 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -13,6 +13,9 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/libocr/commontypes" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/cbor" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -23,9 +26,6 @@ import ( evmrelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/s4" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" - "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/smartcontractkit/libocr/commontypes" ) var ( @@ -116,7 +116,7 @@ const ( ) type FunctionsListener struct { - utils.StartStopOnce + services.StateMachine client client.Client contractAddressHex string job job.Job diff --git a/core/services/gateway/connectionmanager.go b/core/services/gateway/connectionmanager.go index ae2ade6511e..278c4beaaae 100644 --- a/core/services/gateway/connectionmanager.go +++ b/core/services/gateway/connectionmanager.go @@ -42,7 +42,7 @@ type ConnectionManager interface { } type connectionManager struct { - utils.StartStopOnce + services.StateMachine config *config.ConnectionManagerConfig dons map[string]*donConnectionManager diff --git a/core/services/gateway/connector/connector.go b/core/services/gateway/connector/connector.go index dd8dce473b5..55786819448 100644 --- a/core/services/gateway/connector/connector.go +++ b/core/services/gateway/connector/connector.go @@ -46,7 +46,7 @@ type GatewayConnectorHandler interface { } type gatewayConnector struct { - utils.StartStopOnce + services.StateMachine config *ConnectorConfig codec api.Codec diff --git a/core/services/gateway/gateway.go b/core/services/gateway/gateway.go index fb38ae10de9..42e03107f3e 100644 --- a/core/services/gateway/gateway.go +++ b/core/services/gateway/gateway.go @@ -13,6 +13,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" @@ -42,7 +43,7 @@ type HandlerFactory interface { } type gateway struct { - utils.StartStopOnce + services.StateMachine codec api.Codec httpServer gw_net.HttpServer diff --git a/core/services/gateway/handlers/functions/allowlist.go b/core/services/gateway/handlers/functions/allowlist.go index d3619d9071e..914a933eb15 100644 --- a/core/services/gateway/handlers/functions/allowlist.go +++ b/core/services/gateway/handlers/functions/allowlist.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_allow_list" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" @@ -45,7 +46,7 @@ type OnchainAllowlist interface { } type onchainAllowlist struct { - utils.StartStopOnce + services.StateMachine config OnchainAllowlistConfig allowlist atomic.Pointer[map[common.Address]struct{}] diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 7eb15ef6ffd..01f450a4ea0 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -68,7 +69,7 @@ type FunctionsHandlerConfig struct { } type functionsHandler struct { - utils.StartStopOnce + services.StateMachine handlerConfig FunctionsHandlerConfig donConfig *config.DONConfig diff --git a/core/services/gateway/handlers/functions/subscriptions.go b/core/services/gateway/handlers/functions/subscriptions.go index 181a98009f1..79233b1031a 100644 --- a/core/services/gateway/handlers/functions/subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -38,7 +39,7 @@ type OnchainSubscriptions interface { } type onchainSubscriptions struct { - utils.StartStopOnce + services.StateMachine config OnchainSubscriptionsConfig subscriptions UserSubscriptions diff --git a/core/services/gateway/network/httpserver.go b/core/services/gateway/network/httpserver.go index d9d3e5ee7e4..3cae8dc2763 100644 --- a/core/services/gateway/network/httpserver.go +++ b/core/services/gateway/network/httpserver.go @@ -8,9 +8,9 @@ import ( "net/http" "time" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name HttpServer --output ./mocks/ --case=underscore @@ -44,7 +44,7 @@ type HTTPServerConfig struct { } type httpServer struct { - utils.StartStopOnce + services.StateMachine config *HTTPServerConfig listener net.Listener server *http.Server diff --git a/core/services/gateway/network/wsconnection.go b/core/services/gateway/network/wsconnection.go index 345d951ed4a..9215d183d18 100644 --- a/core/services/gateway/network/wsconnection.go +++ b/core/services/gateway/network/wsconnection.go @@ -11,7 +11,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // WSConnectionWrapper is a websocket connection abstraction that supports re-connects. @@ -42,7 +41,7 @@ type WSConnectionWrapper interface { } type wsConnectionWrapper struct { - utils.StartStopOnce + services.StateMachine lggr logger.Logger conn atomic.Pointer[websocket.Conn] diff --git a/core/services/gateway/network/wsserver.go b/core/services/gateway/network/wsserver.go index 2fed4a9df4f..86812a313eb 100644 --- a/core/services/gateway/network/wsserver.go +++ b/core/services/gateway/network/wsserver.go @@ -10,9 +10,9 @@ import ( "github.com/gorilla/websocket" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name WebSocketServer --output ./mocks/ --case=underscore @@ -29,7 +29,7 @@ type WebSocketServerConfig struct { } type webSocketServer struct { - utils.StartStopOnce + services.StateMachine config *WebSocketServerConfig listener net.Listener server *http.Server diff --git a/core/services/job/spawner.go b/core/services/job/spawner.go index 200c25deb37..b2a8dad68f0 100644 --- a/core/services/job/spawner.go +++ b/core/services/job/spawner.go @@ -43,6 +43,7 @@ type ( } spawner struct { + relayservices.StateMachine orm ORM config Config checker services.Checker @@ -52,7 +53,6 @@ type ( q pg.Q lggr logger.Logger - utils.StartStopOnce chStop utils.StopChan lbDependentAwaiters []utils.DependentAwaiter } diff --git a/core/services/keeper/registry_synchronizer_core.go b/core/services/keeper/registry_synchronizer_core.go index dea24f1a3bb..761958ce194 100644 --- a/core/services/keeper/registry_synchronizer_core.go +++ b/core/services/keeper/registry_synchronizer_core.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -36,7 +37,7 @@ type RegistrySynchronizerOptions struct { } type RegistrySynchronizer struct { - utils.StartStopOnce + services.StateMachine chStop chan struct{} registryWrapper RegistryWrapper interval time.Duration diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 435b245792c..33ad8b7d773 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -53,6 +54,7 @@ type UpkeepExecuterConfig interface { // UpkeepExecuter implements the logic to communicate with KeeperRegistry type UpkeepExecuter struct { + services.StateMachine chStop utils.StopChan ethClient evmclient.Client config UpkeepExecuterConfig @@ -66,7 +68,6 @@ type UpkeepExecuter struct { logger logger.Logger wgDone sync.WaitGroup effectiveKeeperAddress common.Address - utils.StartStopOnce } // NewUpkeepExecuter is the constructor of UpkeepExecuter diff --git a/core/services/nurse.go b/core/services/nurse.go index 33230ddae02..e414ca280e7 100644 --- a/core/services/nurse.go +++ b/core/services/nurse.go @@ -17,13 +17,14 @@ import ( "github.com/google/pprof/profile" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Nurse struct { - utils.StartStopOnce + services.StateMachine cfg Config log logger.Logger diff --git a/core/services/ocr/config_overrider.go b/core/services/ocr/config_overrider.go index 35d8e2a4c89..5b2ac20c00c 100644 --- a/core/services/ocr/config_overrider.go +++ b/core/services/ocr/config_overrider.go @@ -9,15 +9,17 @@ import ( "time" "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ConfigOverriderImpl struct { - utils.StartStopOnce + services.StateMachine logger logger.Logger flags *ContractFlags contractAddress ethkey.EIP55Address @@ -56,7 +58,7 @@ func NewConfigOverriderImpl( ctx, cancel := context.WithCancel(context.Background()) co := ConfigOverriderImpl{ - utils.StartStopOnce{}, + services.StateMachine{}, logger, flags, contractAddress, diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index a7df28e1c76..c5f3e431e45 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -52,7 +53,7 @@ type ( // OCRContractTracker complies with ContractConfigTracker interface and // handles log events related to the contract more generally OCRContractTracker struct { - utils.StartStopOnce + services.StateMachine ethClient evmclient.Client contract *offchain_aggregator_wrapper.OffchainAggregator diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go index b78b75cbfcb..f8517386b36 100644 --- a/core/services/ocr2/plugins/median/plugin.go +++ b/core/services/ocr2/plugins/median/plugin.go @@ -8,6 +8,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -48,7 +49,7 @@ func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProv } type reportingPluginFactoryService struct { - utils.StartStopOnce + services.StateMachine lggr logger.Logger ocrtypes.ReportingPluginFactory } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go index e32c4a0662e..856e508fc57 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go @@ -10,16 +10,17 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" + pluginutils "github.com/smartcontractkit/ocr2keepers/pkg/util" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" "github.com/smartcontractkit/ocr2keepers/pkg/v2/encoding" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type TransmitUnpacker interface { @@ -27,7 +28,7 @@ type TransmitUnpacker interface { } type LogProvider struct { - sync utils.StartStopOnce + sync services.StateMachine mu sync.RWMutex runState int runError error diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go index 7d1de17a5b0..49cab7b5a48 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go @@ -15,9 +15,11 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" "go.uber.org/multierr" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -25,7 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const ( @@ -135,7 +136,7 @@ type activeUpkeep struct { type EvmRegistry struct { HeadProvider - sync utils.StartStopOnce + sync services.StateMachine lggr logger.Logger poller logpoller.LogPoller addr common.Address diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go index 9766d988767..2d524e6f6cd 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go @@ -8,8 +8,10 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -34,7 +36,7 @@ var ( ) type BlockSubscriber struct { - utils.StartStopOnce + services.StateMachine threadCtrl utils.ThreadControl mu sync.RWMutex diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go index b62fb370847..729bf4ade5f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go @@ -15,8 +15,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -81,7 +83,7 @@ var _ LogEventProviderTest = &logEventProvider{} // logEventProvider manages log filters for upkeeps and enables to read the log events. type logEventProvider struct { - utils.StartStopOnce + services.StateMachine threadCtrl utils.ThreadControl lggr logger.Logger diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go index 3994f1d8413..b74160ae91c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go @@ -15,9 +15,11 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/ocr2keepers/pkg/v3/random" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -63,7 +65,7 @@ type visitedRecord struct { } type logRecoverer struct { - utils.StartStopOnce + services.StateMachine threadCtrl utils.ThreadControl lggr logger.Logger diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 1cad587e635..5d180c05b80 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -19,6 +19,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -128,7 +129,7 @@ type MercuryConfig struct { } type EvmRegistry struct { - utils.StartStopOnce + services.StateMachine threadCtrl utils.ThreadControl lggr logger.Logger poller logpoller.LogPoller diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go index b0ae2a7bf63..5fd320df8ed 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go @@ -7,15 +7,16 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" 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/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ ocr2keepers.TransmitEventProvider = &EventProvider{} @@ -23,7 +24,7 @@ var _ ocr2keepers.TransmitEventProvider = &EventProvider{} type logParser func(registry *iregistry21.IKeeperRegistryMaster, log logpoller.Log) (transmitEventLog, error) type EventProvider struct { - sync utils.StartStopOnce + sync services.StateMachine mu sync.RWMutex runState int runError error diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go index 34bd6822d69..6c5f767bd36 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go @@ -10,6 +10,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -59,7 +60,7 @@ type upkeepStateRecord struct { // It stores the state of ineligible upkeeps in a local, in-memory cache. // In addition, performed events are fetched by the scanner on demand. type upkeepStateStore struct { - utils.StartStopOnce + services.StateMachine threadCtrl utils.ThreadControl orm ORM diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index 1d2eca46664..0781303275d 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -9,21 +9,22 @@ import ( p2ppeer "github.com/libp2p/go-libp2p-core/peer" p2ppeerstore "github.com/libp2p/go-libp2p-core/peerstore" "github.com/pkg/errors" + "go.uber.org/multierr" + ocrnetworking "github.com/smartcontractkit/libocr/networking" ocrnetworkingtypes "github.com/smartcontractkit/libocr/networking/types" ocr1types "github.com/smartcontractkit/libocr/offchainreporting/types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/sqlx" - "go.uber.org/multierr" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type PeerWrapperOCRConfig interface { @@ -43,7 +44,7 @@ type ( // SingletonPeerWrapper manages all libocr peers for the application SingletonPeerWrapper struct { - utils.StartStopOnce + services.StateMachine keyStore keystore.Master p2pCfg config.P2P ocrCfg PeerWrapperOCRConfig @@ -97,9 +98,7 @@ func NewSingletonPeerWrapper(keyStore keystore.Master, p2pCfg config.P2P, ocrCfg } } -func (p *SingletonPeerWrapper) IsStarted() bool { - return p.State() == utils.StartStopOnce_Started -} +func (p *SingletonPeerWrapper) IsStarted() bool { return p.Ready() == nil } // Start starts SingletonPeerWrapper. func (p *SingletonPeerWrapper) Start(context.Context) error { diff --git a/core/services/ocrcommon/peerstore.go b/core/services/ocrcommon/peerstore.go index 6bad2db4ee5..f1c318a3bff 100644 --- a/core/services/ocrcommon/peerstore.go +++ b/core/services/ocrcommon/peerstore.go @@ -11,8 +11,10 @@ import ( "github.com/libp2p/go-libp2p-peerstore/pstoremem" ma "github.com/multiformats/go-multiaddr" "github.com/pkg/errors" + "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" @@ -30,7 +32,7 @@ type ( } Pstorewrapper struct { - utils.StartStopOnce + services.StateMachine Peerstore p2ppeerstore.Peerstore peerID string q pg.Q @@ -50,7 +52,7 @@ func NewPeerstoreWrapper(db *sqlx.DB, writeInterval time.Duration, peerID p2pkey q := pg.NewQ(db, namedLogger, cfg) return &Pstorewrapper{ - utils.StartStopOnce{}, + services.StateMachine{}, pstoremem.NewPeerstore(), peerID.Raw(), q, diff --git a/core/services/ocrcommon/run_saver.go b/core/services/ocrcommon/run_saver.go index 3aa3aff876e..d8dcc343588 100644 --- a/core/services/ocrcommon/run_saver.go +++ b/core/services/ocrcommon/run_saver.go @@ -3,13 +3,13 @@ package ocrcommon import ( "context" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type RunResultSaver struct { - utils.StartStopOnce + services.StateMachine maxSuccessfulRuns uint64 runResults <-chan *pipeline.Run diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index 29d1ad92e41..be139723ef2 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -7,10 +7,13 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + + "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/libocr/commontypes" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -50,7 +53,7 @@ type EnhancedTelemetryMercuryData struct { } type EnhancedTelemetryService[T EnhancedTelemetryData | EnhancedTelemetryMercuryData] struct { - utils.StartStopOnce + services.StateMachine chTelem <-chan T chDone chan struct{} diff --git a/core/services/periodicbackup/backup.go b/core/services/periodicbackup/backup.go index 8b9ff89cf44..f43698bbdb0 100644 --- a/core/services/periodicbackup/backup.go +++ b/core/services/periodicbackup/backup.go @@ -11,11 +11,11 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/static" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -37,18 +37,18 @@ type backupResult struct { type ( DatabaseBackup interface { - services.ServiceCtx + services.Service RunBackup(version string) error } databaseBackup struct { + services.StateMachine logger logger.Logger databaseURL url.URL mode config.DatabaseBackupMode frequency time.Duration outputParentDir string done chan bool - utils.StartStopOnce } BackupConfig interface { @@ -77,13 +77,13 @@ func NewDatabaseBackup(dbUrl url.URL, rootDir string, backupConfig BackupConfig, } return &databaseBackup{ + services.StateMachine{}, lggr, dbUrl, backupConfig.Mode(), backupConfig.Frequency(), outputParentDir, make(chan bool), - utils.StartStopOnce{}, }, nil } @@ -129,7 +129,7 @@ func (backup *databaseBackup) Name() string { } func (backup *databaseBackup) HealthReport() map[string]error { - return map[string]error{backup.Name(): backup.StartStopOnce.Healthy()} + return map[string]error{backup.Name(): backup.Healthy()} } func (backup *databaseBackup) frequencyIsTooSmall() bool { diff --git a/core/services/pg/event_broadcaster.go b/core/services/pg/event_broadcaster.go index 3c74df6d745..f18ac3251a8 100644 --- a/core/services/pg/event_broadcaster.go +++ b/core/services/pg/event_broadcaster.go @@ -11,8 +11,9 @@ import ( "github.com/lib/pq" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -23,12 +24,13 @@ import ( // EventBroadcaster opaquely manages a collection of Postgres event listeners // and broadcasts events to subscribers (with an optional payload filter). type EventBroadcaster interface { - services.ServiceCtx + services.Service Subscribe(channel, payloadFilter string) (Subscription, error) Notify(channel string, payload string) error } type eventBroadcaster struct { + services.StateMachine uri string minReconnectInterval time.Duration maxReconnectDuration time.Duration @@ -39,7 +41,6 @@ type eventBroadcaster struct { chStop chan struct{} chDone chan struct{} lggr logger.Logger - utils.StartStopOnce } var _ EventBroadcaster = (*eventBroadcaster)(nil) diff --git a/core/services/pipeline/orm.go b/core/services/pipeline/orm.go index f4e69df1ec8..148901bb36c 100644 --- a/core/services/pipeline/orm.go +++ b/core/services/pipeline/orm.go @@ -11,13 +11,13 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" + "github.com/smartcontractkit/sqlx" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/smartcontractkit/sqlx" ) // KeepersObservationSource is the same for all keeper jobs and it is not persisted in DB @@ -74,7 +74,7 @@ const KeepersObservationSource = ` //go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore type ORM interface { - services.ServiceCtx + services.Service CreateSpec(pipeline Pipeline, maxTaskTimeout models.Interval, qopts ...pg.QOpt) (int32, error) CreateRun(run *Run, qopts ...pg.QOpt) (err error) InsertRun(run *Run, qopts ...pg.QOpt) error @@ -95,7 +95,7 @@ type ORM interface { } type orm struct { - utils.StartStopOnce + services.StateMachine q pg.Q lggr logger.Logger maxSuccessfulRuns uint64 @@ -111,7 +111,7 @@ var _ ORM = (*orm)(nil) func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, jobPipelineMaxSuccessfulRuns uint64) *orm { ctx, cancel := context.WithCancel(context.Background()) return &orm{ - utils.StartStopOnce{}, + services.StateMachine{}, pg.NewQ(db, lggr, cfg), lggr.Named("PipelineORM"), jobPipelineMaxSuccessfulRuns, diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 20319682ef6..3dbe94747e7 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -14,13 +14,13 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -28,7 +28,7 @@ import ( //go:generate mockery --quiet --name Runner --output ./mocks/ --case=underscore type Runner interface { - services.ServiceCtx + services.Service // Run is a blocking call that will execute the run until no further progress can be made. // If `incomplete` is true, the run is only partially complete and is suspended, awaiting to be resumed when more data comes in. @@ -52,6 +52,7 @@ type Runner interface { } type runner struct { + services.StateMachine orm ORM btORM bridges.ORM config Config @@ -67,7 +68,6 @@ type runner struct { // test helper runFinished func(*Run) - utils.StartStopOnce chStop utils.StopChan wgDone sync.WaitGroup } @@ -150,7 +150,7 @@ func (r *runner) Name() string { } func (r *runner) HealthReport() map[string]error { - return map[string]error{r.Name(): r.StartStopOnce.Healthy()} + return map[string]error{r.Name(): r.Healthy()} } func (r *runner) destroy() { diff --git a/core/services/promreporter/prom_reporter.go b/core/services/promreporter/prom_reporter.go index c0b48b46e3a..fd6afeeb8ea 100644 --- a/core/services/promreporter/prom_reporter.go +++ b/core/services/promreporter/prom_reporter.go @@ -13,6 +13,7 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -21,6 +22,7 @@ import ( //go:generate mockery --quiet --name PrometheusBackend --output ../../internal/mocks/ --case=underscore type ( promReporter struct { + services.StateMachine db *sql.DB lggr logger.Logger backend PrometheusBackend @@ -28,8 +30,6 @@ type ( chStop utils.StopChan wgDone sync.WaitGroup reportPeriod time.Duration - - utils.StartStopOnce } PrometheusBackend interface { @@ -130,7 +130,7 @@ func (pr *promReporter) Name() string { } func (pr *promReporter) HealthReport() map[string]error { - return map[string]error{pr.Name(): pr.StartStopOnce.Healthy()} + return map[string]error{pr.Name(): pr.Healthy()} } func (pr *promReporter) OnNewLongestChain(ctx context.Context, head *evmtypes.Head) { diff --git a/core/services/relay/evm/config_poller.go b/core/services/relay/evm/config_poller.go index 504155bf1e8..1cf2318b296 100644 --- a/core/services/relay/evm/config_poller.go +++ b/core/services/relay/evm/config_poller.go @@ -11,16 +11,17 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -89,7 +90,7 @@ func configFromLog(logData []byte) (ocrtypes.ContractConfig, error) { } type configPoller struct { - utils.StartStopOnce + services.StateMachine lggr logger.Logger filterName string diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index e7a2bca3448..3f45b41f46e 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -42,7 +42,6 @@ import ( reportcodecv3 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v3/reportcodec" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var _ relaytypes.Relayer = &Relayer{} //nolint:staticcheck @@ -238,7 +237,7 @@ func FilterNamesFromRelayArgs(args relaytypes.RelayArgs) (filterNames []string, } type configWatcher struct { - utils.StartStopOnce + services.StateMachine lggr logger.Logger contractAddress common.Address contractABI abi.ABI @@ -263,7 +262,6 @@ func newConfigWatcher(lggr logger.Logger, ) *configWatcher { replayCtx, replayCancel := context.WithCancel(context.Background()) return &configWatcher{ - StartStopOnce: utils.StartStopOnce{}, lggr: lggr.Named("ConfigWatcher").Named(contractAddress.String()), contractAddress: contractAddress, contractABI: contractABI, @@ -274,7 +272,6 @@ func newConfigWatcher(lggr logger.Logger, fromBlock: fromBlock, replayCtx: replayCtx, replayCancel: replayCancel, - wg: sync.WaitGroup{}, } } @@ -312,7 +309,7 @@ func (c *configWatcher) Close() error { } func (c *configWatcher) HealthReport() map[string]error { - return map[string]error{c.Name(): c.StartStopOnce.Healthy()} + return map[string]error{c.Name(): c.Healthy()} } func (c *configWatcher) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index 43a3d2ff86f..fdd6201c697 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -9,11 +9,14 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "go.uber.org/multierr" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -24,11 +27,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" functionsRelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions" evmRelayTypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type functionsProvider struct { - utils.StartStopOnce + services.StateMachine configWatcher *configWatcher contractTransmitter ContractTransmitter logPollerWrapper evmRelayTypes.LogPollerWrapper diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index db2c7fd68ce..777717d01c7 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" @@ -21,7 +22,7 @@ import ( ) type logPollerWrapper struct { - utils.StartStopOnce + services.StateMachine routerContract *functions_router.FunctionsRouter pluginConfig config.PluginConfig diff --git a/core/services/relay/evm/mercury/persistence_manager.go b/core/services/relay/evm/mercury/persistence_manager.go index 1c8dad45301..9de1f80c6b5 100644 --- a/core/services/relay/evm/mercury/persistence_manager.go +++ b/core/services/relay/evm/mercury/persistence_manager.go @@ -7,6 +7,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" @@ -22,7 +23,7 @@ type PersistenceManager struct { lggr logger.Logger orm ORM - once utils.StartStopOnce + once services.StateMachine stopCh utils.StopChan wg sync.WaitGroup diff --git a/core/services/relay/evm/mercury/queue.go b/core/services/relay/evm/mercury/queue.go index 44042c57725..3d20b3f2b0c 100644 --- a/core/services/relay/evm/mercury/queue.go +++ b/core/services/relay/evm/mercury/queue.go @@ -13,8 +13,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -24,7 +25,7 @@ type asyncDeleter interface { AsyncDelete(req *pb.TransmitRequest) } -var _ services.ServiceCtx = (*TransmitQueue)(nil) +var _ services.Service = (*TransmitQueue)(nil) var transmitQueueLoad = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "mercury_transmit_queue_load", @@ -40,7 +41,7 @@ const promInterval = 6500 * time.Millisecond // TransmitQueue is the high-level package that everything outside of this file should be using // It stores pending transmissions, yielding the latest (highest priority) first to the caller type TransmitQueue struct { - utils.StartStopOnce + services.StateMachine cond sync.Cond lggr logger.Logger @@ -68,7 +69,7 @@ func NewTransmitQueue(lggr logger.Logger, feedID string, maxlen int, transmissio heap.Init(&pq) // ensure the heap is ordered mu := new(sync.RWMutex) return &TransmitQueue{ - utils.StartStopOnce{}, + services.StateMachine{}, sync.Cond{L: mu}, lggr.Named("TransmitQueue"), asyncDeleter, diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index 0c2721442b4..88c3113abc6 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -84,7 +84,7 @@ type TransmitterReportDecoder interface { var _ Transmitter = (*mercuryTransmitter)(nil) type mercuryTransmitter struct { - utils.StartStopOnce + services.StateMachine lggr logger.Logger rpcClient wsrpc.Client cfgTracker ConfigTracker @@ -127,7 +127,7 @@ func NewTransmitter(lggr logger.Logger, cfgTracker ConfigTracker, rpcClient wsrp feedIDHex := fmt.Sprintf("0x%x", feedID[:]) persistenceManager := NewPersistenceManager(lggr, NewORM(db, lggr, cfg), jobID, maxTransmitQueueSize, flushDeletesFrequency, pruneFrequency) return &mercuryTransmitter{ - utils.StartStopOnce{}, + services.StateMachine{}, lggr.Named("MercuryTransmitter").With("feedID", feedIDHex), rpcClient, cfgTracker, diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index fb9a57d9a2d..f6ed1d0db8e 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -11,11 +11,13 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/connectivity" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -59,7 +61,7 @@ var ( ) type Client interface { - services.ServiceCtx + services.Service pb.MercuryClient } @@ -70,7 +72,7 @@ type Conn interface { } type client struct { - utils.StartStopOnce + services.StateMachine csaKey csakey.KeyV2 serverPubKey []byte @@ -211,7 +213,7 @@ func (w *client) HealthReport() map[string]error { // Healthy if connected func (w *client) Healthy() (err error) { - if err = w.StartStopOnce.Healthy(); err != nil { + if err = w.StateMachine.Healthy(); err != nil { return err } state := w.conn.GetState() diff --git a/core/services/relay/evm/request_round_tracker.go b/core/services/relay/evm/request_round_tracker.go index 3507f1dfa87..4e065f2dfdf 100644 --- a/core/services/relay/evm/request_round_tracker.go +++ b/core/services/relay/evm/request_round_tracker.go @@ -8,22 +8,23 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/sqlx" + "github.com/smartcontractkit/chainlink-relay/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // RequestRoundTracker subscribes to new request round logs. type RequestRoundTracker struct { - utils.StartStopOnce + services.StateMachine ethClient evmclient.Client contract *offchain_aggregator_wrapper.OffchainAggregator diff --git a/core/services/synchronization/telemetry_ingress_batch_client.go b/core/services/synchronization/telemetry_ingress_batch_client.go index 4924bb2cd50..26abda65d33 100644 --- a/core/services/synchronization/telemetry_ingress_batch_client.go +++ b/core/services/synchronization/telemetry_ingress_batch_client.go @@ -12,10 +12,10 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/examples/simple/keys" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // NoopTelemetryIngressBatchClient is a no-op interface for TelemetryIngressBatchClient @@ -37,7 +37,7 @@ func (NoopTelemetryIngressBatchClient) Name() string { return func (NoopTelemetryIngressBatchClient) Ready() error { return nil } type telemetryIngressBatchClient struct { - utils.StartStopOnce + services.StateMachine url *url.URL ks keystore.CSA serverPubKeyHex string diff --git a/core/services/synchronization/telemetry_ingress_client.go b/core/services/synchronization/telemetry_ingress_client.go index 1db4f69afd8..9458b7627c9 100644 --- a/core/services/synchronization/telemetry_ingress_client.go +++ b/core/services/synchronization/telemetry_ingress_client.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/examples/simple/keys" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" @@ -35,7 +36,7 @@ func (NoopTelemetryIngressClient) Name() string { return "Noop func (NoopTelemetryIngressClient) Ready() error { return nil } type telemetryIngressClient struct { - utils.StartStopOnce + services.StateMachine url *url.URL ks keystore.CSA serverPubKeyHex string diff --git a/core/services/telemetry/manager.go b/core/services/telemetry/manager.go index 3818341f5b1..2931ec71a13 100644 --- a/core/services/telemetry/manager.go +++ b/core/services/telemetry/manager.go @@ -8,14 +8,15 @@ import ( "time" "github.com/pkg/errors" - "github.com/smartcontractkit/libocr/commontypes" "go.uber.org/multierr" + "github.com/smartcontractkit/libocr/commontypes" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //// Client encapsulates all the functionality needed to @@ -26,7 +27,7 @@ import ( //} type Manager struct { - utils.StartStopOnce + services.StateMachine bufferSize uint endpoints []*telemetryEndpoint ks keystore.CSA @@ -67,7 +68,7 @@ func (l *legacyEndpointConfig) URL() *url.URL { } type telemetryEndpoint struct { - utils.StartStopOnce + services.StateMachine ChainID string Network string URL *url.URL @@ -143,7 +144,7 @@ func (m *Manager) HealthReport() map[string]error { hr[m.lggr.Name()] = m.Healthy() for _, e := range m.endpoints { name := fmt.Sprintf("%s.%s.%s", m.lggr.Name(), e.Network, e.ChainID) - hr[name] = e.StartStopOnce.Healthy() + hr[name] = e.Healthy() } return hr } diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 4aaf3280155..69746625ddd 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -24,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" mocks2 "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/mocks" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func setupMockConfig(t *testing.T, useBatchSend bool) *mocks.TelemetryIngress { @@ -246,10 +246,10 @@ func TestCorrectEndpointRouting(t *testing.T) { }) tm.endpoints[i] = &telemetryEndpoint{ - StartStopOnce: utils.StartStopOnce{}, - ChainID: e.chainID, - Network: e.network, - client: clientMock, + StateMachine: services.StateMachine{}, + ChainID: e.chainID, + Network: e.network, + client: clientMock, } } diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 03b92bc15cb..613c0d124df 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -13,6 +13,7 @@ import ( heaps "github.com/theodesp/go-heaps" "github.com/theodesp/go-heaps/pairing" + "github.com/smartcontractkit/chainlink-relay/pkg/services" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" @@ -43,7 +44,7 @@ type request struct { } type Listener struct { - utils.StartStopOnce + services.StateMachine Cfg vrfcommon.Config FeeCfg vrfcommon.FeeConfig diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 31e76b48fdb..7560baad3a2 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -23,6 +23,7 @@ import ( "github.com/theodesp/go-heaps/pairing" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/services" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/assets" @@ -191,7 +192,7 @@ type vrfPipelineResult struct { } type listenerV2 struct { - utils.StartStopOnce + services.StateMachine cfg vrfcommon.Config feeCfg vrfcommon.FeeConfig l logger.SugaredLogger diff --git a/core/utils/helpers_test.go b/core/utils/helpers_test.go deleted file mode 100644 index d317994cde1..00000000000 --- a/core/utils/helpers_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package utils - -func (once *StartStopOnce) LoadState() StartStopOnceState { - return StartStopOnceState(once.state.Load()) -} diff --git a/core/utils/mailbox_prom.go b/core/utils/mailbox_prom.go index 0291a51d2c4..dc20db84d9d 100644 --- a/core/utils/mailbox_prom.go +++ b/core/utils/mailbox_prom.go @@ -9,6 +9,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" ) var mailboxLoad = promauto.NewGaugeVec(prometheus.GaugeOpts{ @@ -21,7 +23,7 @@ var mailboxLoad = promauto.NewGaugeVec(prometheus.GaugeOpts{ const mailboxPromInterval = 5 * time.Second type MailboxMonitor struct { - StartStopOnce + services.StateMachine appID string mailboxes sync.Map diff --git a/core/utils/sleeper_task.go b/core/utils/sleeper_task.go index 0b45507a82f..fcec2542493 100644 --- a/core/utils/sleeper_task.go +++ b/core/utils/sleeper_task.go @@ -3,6 +3,8 @@ package utils import ( "fmt" "time" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" ) // SleeperTask represents a task that waits in the background to process some work. @@ -19,12 +21,12 @@ type Worker interface { } type sleeperTask struct { + services.StateMachine worker Worker chQueue chan struct{} chStop chan struct{} chDone chan struct{} chWorkDone chan struct{} - StartStopOnce } // NewSleeperTask takes a worker and returns a SleeperTask. @@ -76,13 +78,14 @@ func (s *sleeperTask) WakeUpIfStarted() { // WakeUp wakes up the sleeper task, asking it to execute its Worker. func (s *sleeperTask) WakeUp() { - if s.StartStopOnce.State() == StartStopOnce_Stopped { + if !s.IfStarted(func() { + select { + case s.chQueue <- struct{}{}: + default: + } + }) { panic("cannot wake up stopped sleeper task") } - select { - case s.chQueue <- struct{}{}: - default: - } } func (s *sleeperTask) workDone() { diff --git a/core/utils/utils.go b/core/utils/utils.go index d96546b3e19..b8515f3362a 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -31,6 +31,8 @@ import ( "github.com/robfig/cron/v3" "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/sha3" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" ) const ( @@ -819,14 +821,6 @@ func EVMBytesToUint64(buf []byte) uint64 { return result } -type errNotStarted struct { - state StartStopOnceState -} - -func (e *errNotStarted) Error() string { - return fmt.Sprintf("service is %q, not started", e.state) -} - var ( ErrAlreadyStopped = errors.New("already stopped") ErrCannotStopUnstarted = errors.New("cannot stop unstarted service") @@ -834,176 +828,7 @@ var ( // StartStopOnce contains a StartStopOnceState integer // Deprecated: use services.StateMachine -type StartStopOnce struct { - state atomic.Int32 - sync.RWMutex // lock is held during startup/shutdown, RLock is held while executing functions dependent on a particular state - - // SvcErrBuffer is an ErrorBuffer that let service owners track critical errors happening in the service. - // - // SvcErrBuffer.SetCap(int) Overrides buffer limit from defaultErrorBufferCap - // SvcErrBuffer.Append(error) Appends an error to the buffer - // SvcErrBuffer.Flush() error returns all tracked errors as a single joined error - SvcErrBuffer ErrorBuffer -} - -// StartStopOnceState holds the state for StartStopOnce -type StartStopOnceState int32 - -// nolint -const ( - StartStopOnce_Unstarted StartStopOnceState = iota - StartStopOnce_Started - StartStopOnce_Starting - StartStopOnce_StartFailed - StartStopOnce_Stopping - StartStopOnce_Stopped - StartStopOnce_StopFailed -) - -func (s StartStopOnceState) String() string { - switch s { - case StartStopOnce_Unstarted: - return "Unstarted" - case StartStopOnce_Started: - return "Started" - case StartStopOnce_Starting: - return "Starting" - case StartStopOnce_StartFailed: - return "StartFailed" - case StartStopOnce_Stopping: - return "Stopping" - case StartStopOnce_Stopped: - return "Stopped" - case StartStopOnce_StopFailed: - return "StopFailed" - default: - return fmt.Sprintf("unrecognized state: %d", s) - } -} - -// StartOnce sets the state to Started -func (once *StartStopOnce) StartOnce(name string, fn func() error) error { - // SAFETY: We do this compare-and-swap outside of the lock so that - // concurrent StartOnce() calls return immediately. - success := once.state.CompareAndSwap(int32(StartStopOnce_Unstarted), int32(StartStopOnce_Starting)) - - if !success { - return pkgerrors.Errorf("%v has already been started once; state=%v", name, StartStopOnceState(once.state.Load())) - } - - once.Lock() - defer once.Unlock() - - // Setting cap before calling startup fn in case of crits in startup - once.SvcErrBuffer.SetCap(defaultErrorBufferCap) - err := fn() - - if err == nil { - success = once.state.CompareAndSwap(int32(StartStopOnce_Starting), int32(StartStopOnce_Started)) - } else { - success = once.state.CompareAndSwap(int32(StartStopOnce_Starting), int32(StartStopOnce_StartFailed)) - } - - if !success { - // SAFETY: If this is reached, something must be very wrong: once.state - // was tampered with outside of the lock. - panic(fmt.Sprintf("%v entered unreachable state, unable to set state to started", name)) - } - - return err -} - -// StopOnce sets the state to Stopped -func (once *StartStopOnce) StopOnce(name string, fn func() error) error { - // SAFETY: We hold the lock here so that Stop blocks until StartOnce - // executes. This ensures that a very fast call to Stop will wait for the - // code to finish starting up before teardown. - once.Lock() - defer once.Unlock() - - success := once.state.CompareAndSwap(int32(StartStopOnce_Started), int32(StartStopOnce_Stopping)) - - if !success { - state := once.state.Load() - switch state { - case int32(StartStopOnce_Stopped): - return pkgerrors.Wrapf(ErrAlreadyStopped, "%s has already been stopped", name) - case int32(StartStopOnce_Unstarted): - return pkgerrors.Wrapf(ErrCannotStopUnstarted, "%s has not been started", name) - default: - return pkgerrors.Errorf("%v cannot be stopped from this state; state=%v", name, StartStopOnceState(state)) - } - } - - err := fn() - - if err == nil { - success = once.state.CompareAndSwap(int32(StartStopOnce_Stopping), int32(StartStopOnce_Stopped)) - } else { - success = once.state.CompareAndSwap(int32(StartStopOnce_Stopping), int32(StartStopOnce_StopFailed)) - } - - if !success { - // SAFETY: If this is reached, something must be very wrong: once.state - // was tampered with outside of the lock. - panic(fmt.Sprintf("%v entered unreachable state, unable to set state to stopped", name)) - } - - return err -} - -// State retrieves the current state -func (once *StartStopOnce) State() StartStopOnceState { - state := once.state.Load() - return StartStopOnceState(state) -} - -// IfStarted runs the func and returns true only if started, otherwise returns false -func (once *StartStopOnce) IfStarted(f func()) (ok bool) { - once.RLock() - defer once.RUnlock() - - state := once.state.Load() - - if StartStopOnceState(state) == StartStopOnce_Started { - f() - return true - } - return false -} - -// IfNotStopped runs the func and returns true if in any state other than Stopped -func (once *StartStopOnce) IfNotStopped(f func()) (ok bool) { - once.RLock() - defer once.RUnlock() - - state := once.state.Load() - - if StartStopOnceState(state) == StartStopOnce_Stopped { - return false - } - f() - return true -} - -// Ready returns ErrNotStarted if the state is not started. -func (once *StartStopOnce) Ready() error { - state := once.State() - if state == StartStopOnce_Started { - return nil - } - return &errNotStarted{state: state} -} - -// Healthy returns ErrNotStarted if the state is not started. -// Override this per-service with more specific implementations. -func (once *StartStopOnce) Healthy() error { - state := once.State() - if state == StartStopOnce_Started { - return once.SvcErrBuffer.Flush() - } - return &errNotStarted{state: state} -} +type StartStopOnce = services.StateMachine // EnsureClosed closes the io.Closer, returning nil if it was already // closed or not started yet diff --git a/core/utils/utils_test.go b/core/utils/utils_test.go index 5d728d14f4a..04802feb3a7 100644 --- a/core/utils/utils_test.go +++ b/core/utils/utils_test.go @@ -724,111 +724,6 @@ func TestContextFromChanWithTimeout(t *testing.T) { }) } -func TestStartStopOnceState_String(t *testing.T) { - t.Parallel() - - assert.Equal(t, "Unstarted", utils.StartStopOnce_Unstarted.String()) - assert.Equal(t, "Started", utils.StartStopOnce_Started.String()) - assert.Equal(t, "Starting", utils.StartStopOnce_Starting.String()) - assert.Equal(t, "Stopping", utils.StartStopOnce_Stopping.String()) - assert.Equal(t, "Stopped", utils.StartStopOnce_Stopped.String()) - assert.Equal(t, "unrecognized state: 123", utils.StartStopOnceState(123).String()) -} - -func TestStartStopOnce(t *testing.T) { - t.Parallel() - - var callsCount atomic.Int32 - incCount := func() { - callsCount.Add(1) - } - - var s utils.StartStopOnce - ok := s.IfStarted(incCount) - assert.False(t, ok) - ok = s.IfNotStopped(incCount) - assert.True(t, ok) - assert.Equal(t, int32(1), callsCount.Load()) - - err := s.StartOnce("foo", func() error { return nil }) - assert.NoError(t, err) - - assert.True(t, s.IfStarted(incCount)) - assert.Equal(t, int32(2), callsCount.Load()) - - err = s.StopOnce("foo", func() error { return nil }) - assert.NoError(t, err) - ok = s.IfNotStopped(incCount) - assert.False(t, ok) - assert.Equal(t, int32(2), callsCount.Load()) -} - -func TestStartStopOnce_StartErrors(t *testing.T) { - var s utils.StartStopOnce - - err := s.StartOnce("foo", func() error { return errors.New("foo") }) - assert.Error(t, err) - - var callsCount atomic.Int32 - incCount := func() { - callsCount.Add(1) - } - - assert.False(t, s.IfStarted(incCount)) - assert.Equal(t, int32(0), callsCount.Load()) - - err = s.StartOnce("foo", func() error { return nil }) - require.Error(t, err) - assert.Contains(t, err.Error(), "foo has already been started once") - err = s.StopOnce("foo", func() error { return nil }) - require.Error(t, err) - assert.Contains(t, err.Error(), "foo cannot be stopped from this state; state=StartFailed") - - assert.Equal(t, utils.StartStopOnce_StartFailed, s.LoadState()) -} - -func TestStartStopOnce_StopErrors(t *testing.T) { - var s utils.StartStopOnce - - err := s.StartOnce("foo", func() error { return nil }) - require.NoError(t, err) - - var callsCount atomic.Int32 - incCount := func() { - callsCount.Add(1) - } - - err = s.StopOnce("foo", func() error { return errors.New("explodey mcsplode") }) - assert.Error(t, err) - - assert.False(t, s.IfStarted(incCount)) - assert.Equal(t, int32(0), callsCount.Load()) - assert.True(t, s.IfNotStopped(incCount)) - assert.Equal(t, int32(1), callsCount.Load()) - - err = s.StartOnce("foo", func() error { return nil }) - require.Error(t, err) - assert.Contains(t, err.Error(), "foo has already been started once") - err = s.StopOnce("foo", func() error { return nil }) - require.Error(t, err) - assert.Contains(t, err.Error(), "foo cannot be stopped from this state; state=StopFailed") - - assert.Equal(t, utils.StartStopOnce_StopFailed, s.LoadState()) -} - -func TestStartStopOnce_Ready_Healthy(t *testing.T) { - t.Parallel() - - var s utils.StartStopOnce - assert.Error(t, s.Ready()) - assert.Error(t, s.Healthy()) - - err := s.StartOnce("foo", func() error { return nil }) - assert.NoError(t, err) - assert.NoError(t, s.Ready()) - assert.NoError(t, s.Healthy()) -} - func TestLeftPadBitString(t *testing.T) { t.Parallel() diff --git a/go.mod b/go.mod index 3679c79ed03..14f195d495f 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index e2bc6610488..c9700330fa9 100644 --- a/go.sum +++ b/go.sum @@ -1459,8 +1459,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c1a8cdb61ce..21ce1921267 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -384,7 +384,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 77778de9d23..64305ddc249 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2362,8 +2362,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04 h1:QFMxPq7AqU4qXeW7UBv0eP/mpLt2pG2QkASUyFjKoIE= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231027131428-7dc07d302a04/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From e3caf761e9f036b351d5f4ab686892c4f7d7da42 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 30 Oct 2023 13:15:35 -0500 Subject: [PATCH 033/271] core/chains/cosmos/cosmostxm: simplifications (#11101) --- core/chains/cosmos/chain.go | 102 +++++-- core/chains/cosmos/config.go | 272 ------------------ core/chains/cosmos/config_test.go | 96 ------- core/chains/cosmos/cosmostxm/helpers_test.go | 32 +-- core/chains/cosmos/cosmostxm/key_wrapper.go | 6 +- .../cosmos/cosmostxm/keystore_adapter.go | 16 +- core/chains/cosmos/cosmostxm/orm_test.go | 5 +- core/chains/cosmos/cosmostxm/txm.go | 4 +- .../cosmos/cosmostxm/txm_internal_test.go | 122 ++++---- core/chains/cosmos/cosmostxm/txm_test.go | 265 ++++++++--------- core/cmd/cosmos_chains_commands_test.go | 4 +- core/cmd/cosmos_node_commands_test.go | 13 +- core/cmd/cosmos_transaction_commands_test.go | 61 +--- core/cmd/shell.go | 2 +- core/config/docs/docs_test.go | 4 +- core/internal/cltest/cltest.go | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/chainlink/config.go | 6 +- core/services/chainlink/config_general.go | 4 +- core/services/chainlink/config_test.go | 7 +- .../chainlink/mocks/general_config.go | 10 +- .../relayer_chain_interoperators_test.go | 15 +- core/services/chainlink/relayer_factory.go | 7 +- core/services/chainlink/types.go | 4 +- core/web/cosmos_chains_controller_test.go | 9 +- core/web/cosmos_transfer_controller.go | 108 +++---- core/web/presenters/cosmos_msg.go | 4 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 32 files changed, 377 insertions(+), 821 deletions(-) delete mode 100644 core/chains/cosmos/config.go delete mode 100644 core/chains/cosmos/config_test.go diff --git a/core/chains/cosmos/chain.go b/core/chains/cosmos/chain.go index bb44c4bee61..e11f95d356e 100644 --- a/core/chains/cosmos/chain.go +++ b/core/chains/cosmos/chain.go @@ -7,35 +7,38 @@ import ( "math/big" "time" + "github.com/pelletier/go-toml/v2" "github.com/pkg/errors" "go.uber.org/multierr" sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/smartcontractkit/sqlx" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" relaychains "github.com/smartcontractkit/chainlink-relay/pkg/chains" "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/services" + + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) -// DefaultRequestTimeout is the default Cosmos client timeout. +// defaultRequestTimeout is the default Cosmos client timeout. // Note that while the cosmos node is processing a heavy block, // requests can be delayed significantly (https://github.com/tendermint/tendermint/issues/6899), // however there's nothing we can do but wait until the block is processed. // So we set a fairly high timeout here. // TODO(BCI-979): Remove this, or make this configurable with the updated client. -const DefaultRequestTimeout = 30 * time.Second +const defaultRequestTimeout = 30 * time.Second var ( // ErrChainIDEmpty is returned when chain is required but was empty. @@ -78,7 +81,7 @@ func (o *ChainOpts) Validate() (err error) { return } -func NewChain(cfg *CosmosConfig, opts ChainOpts) (adapters.Chain, error) { +func NewChain(cfg *coscfg.TOMLConfig, opts ChainOpts) (adapters.Chain, error) { if !cfg.IsEnabled() { return nil, fmt.Errorf("cannot create new chain with ID %s, the chain is disabled", *cfg.ChainID) } @@ -94,23 +97,23 @@ var _ adapters.Chain = (*chain)(nil) type chain struct { services.StateMachine id string - cfg *CosmosConfig + cfg *coscfg.TOMLConfig txm *cosmostxm.Txm lggr logger.Logger } -func newChain(id string, cfg *CosmosConfig, db *sqlx.DB, ks loop.Keystore, logCfg pg.QConfig, eb pg.EventBroadcaster, lggr logger.Logger) (*chain, error) { +func newChain(id string, cfg *coscfg.TOMLConfig, db *sqlx.DB, ks loop.Keystore, logCfg pg.QConfig, eb pg.EventBroadcaster, lggr logger.Logger) (*chain, error) { lggr = logger.With(lggr, "cosmosChainID", id) var ch = chain{ id: id, cfg: cfg, lggr: logger.Named(lggr, "Chain"), } - tc := func() (cosmosclient.ReaderWriter, error) { + tc := func() (client.ReaderWriter, error) { return ch.getClient("") } - gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ - cosmosclient.NewClosureGasPriceEstimator(func() (map[string]sdk.DecCoin, error) { + gpe := client.NewMustGasPriceEstimator([]client.GasPricesEstimator{ + client.NewClosureGasPriceEstimator(func() (map[string]sdk.DecCoin, error) { return map[string]sdk.DecCoin{ cfg.GasToken(): sdk.NewDecCoinFromDec(cfg.GasToken(), cfg.FallbackGasPrice()), }, nil @@ -141,12 +144,12 @@ func (c *chain) TxManager() adapters.TxManager { return c.txm } -func (c *chain) Reader(name string) (cosmosclient.Reader, error) { +func (c *chain) Reader(name string) (client.Reader, error) { return c.getClient(name) } // getClient returns a client, optionally requiring a specific node by name. -func (c *chain) getClient(name string) (cosmosclient.ReaderWriter, error) { +func (c *chain) getClient(name string) (client.ReaderWriter, error) { var node db.Node if name == "" { // Any node nodes, err := c.cfg.ListNodes() @@ -171,7 +174,7 @@ func (c *chain) getClient(name string) (cosmosclient.ReaderWriter, error) { return nil, fmt.Errorf("failed to create client for chain %s with node %s: wrong chain id %s", c.id, name, node.CosmosChainID) } } - client, err := cosmosclient.NewClient(c.id, node.TendermintURL, DefaultRequestTimeout, logger.Named(c.lggr, "Client."+name)) + client, err := client.NewClient(c.id, node.TendermintURL, defaultRequestTimeout, logger.Named(c.lggr, "Client."+name)) if err != nil { return nil, fmt.Errorf("failed to create client: %w", err) } @@ -224,7 +227,41 @@ func (c *chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken } func (c *chain) Transact(ctx context.Context, from, to string, amount *big.Int, balanceCheck bool) error { - return chains.ErrLOOPPUnsupported + fromAcc, err := sdk.AccAddressFromBech32(from) + if err != nil { + return fmt.Errorf("failed to parse from account: %s", fromAcc) + } + toAcc, err := sdk.AccAddressFromBech32(to) + if err != nil { + return fmt.Errorf("failed to parse from account: %s", toAcc) + } + coin := sdk.Coin{Amount: sdk.NewIntFromBigInt(amount), Denom: c.Config().GasToken()} + + txm := c.TxManager() + + if balanceCheck { + var reader client.Reader + reader, err = c.Reader("") + if err != nil { + return fmt.Errorf("chain unreachable: %v", err) + } + gasPrice, err2 := txm.GasPrice() + if err2 != nil { + return fmt.Errorf("gas price unavailable: %v", err2) + } + + err = validateBalance(reader, gasPrice, fromAcc, coin) + if err != nil { + return fmt.Errorf("failed to validate balance: %v", err) + } + } + + sendMsg := bank.NewMsgSend(fromAcc, toAcc, sdk.Coins{coin}) + _, err = txm.Enqueue("", sendMsg) + if err != nil { + return fmt.Errorf("failed to enqueue tx: %w", err) + } + return nil } // TODO BCF-2602 statuses are static for non-evm chain and should be dynamic @@ -247,3 +284,34 @@ func (c *chain) listNodeStatuses(start, end int) ([]relaytypes.NodeStatus, int, } return stats, total, nil } + +func nodeStatus(n *coscfg.Node, id relay.ChainID) (relaytypes.NodeStatus, error) { + var s relaytypes.NodeStatus + s.ChainID = id + s.Name = *n.Name + b, err := toml.Marshal(n) + if err != nil { + return relaytypes.NodeStatus{}, err + } + s.Config = string(b) + return s, nil +} + +// maxGasUsedTransfer is an upper bound on how much gas we expect a MsgSend for a single coin to use. +const maxGasUsedTransfer = 100_000 + +// validateBalance validates that fromAddr's balance can cover coin, including fees at gasPrice. +func validateBalance(reader client.Reader, gasPrice sdk.DecCoin, fromAddr sdk.AccAddress, coin sdk.Coin) error { + balance, err := reader.Balance(fromAddr, coin.GetDenom()) + if err != nil { + return err + } + + fee := gasPrice.Amount.MulInt64(maxGasUsedTransfer).RoundInt() + need := coin.Amount.Add(fee) + + if balance.Amount.LT(need) { + return errors.Errorf("balance %q is too low for this transaction to be executed: need %s total, including %s fee", balance, need, fee) + } + return nil +} diff --git a/core/chains/cosmos/config.go b/core/chains/cosmos/config.go deleted file mode 100644 index 8b4c8c13f32..00000000000 --- a/core/chains/cosmos/config.go +++ /dev/null @@ -1,272 +0,0 @@ -package cosmos - -import ( - "fmt" - "net/url" - "slices" - "time" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/pelletier/go-toml/v2" - "github.com/shopspring/decimal" - "go.uber.org/multierr" - - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" - - "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/core/utils/config" -) - -type CosmosConfigs []*CosmosConfig - -func (cs CosmosConfigs) validateKeys() (err error) { - // Unique chain IDs - chainIDs := config.UniqueStrings{} - for i, c := range cs { - if chainIDs.IsDupe(c.ChainID) { - err = multierr.Append(err, config.NewErrDuplicate(fmt.Sprintf("%d.ChainID", i), *c.ChainID)) - } - } - - // Unique node names - names := config.UniqueStrings{} - for i, c := range cs { - for j, n := range c.Nodes { - if names.IsDupe(n.Name) { - err = multierr.Append(err, config.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.Name", i, j), *n.Name)) - } - } - } - - // Unique TendermintURLs - urls := config.UniqueStrings{} - for i, c := range cs { - for j, n := range c.Nodes { - u := (*url.URL)(n.TendermintURL) - if urls.IsDupeFmt(u) { - err = multierr.Append(err, config.NewErrDuplicate(fmt.Sprintf("%d.Nodes.%d.TendermintURL", i, j), u.String())) - } - } - } - return - -} - -func (cs CosmosConfigs) ValidateConfig() (err error) { - return cs.validateKeys() -} - -func (cs *CosmosConfigs) SetFrom(fs *CosmosConfigs) (err error) { - if err1 := fs.validateKeys(); err1 != nil { - return err1 - } - for _, f := range *fs { - if f.ChainID == nil { - *cs = append(*cs, f) - } else if i := slices.IndexFunc(*cs, func(c *CosmosConfig) bool { - return c.ChainID != nil && *c.ChainID == *f.ChainID - }); i == -1 { - *cs = append(*cs, f) - } else { - (*cs)[i].SetFrom(f) - } - } - return -} - -func nodeStatus(n *coscfg.Node, id relay.ChainID) (relaytypes.NodeStatus, error) { - var s relaytypes.NodeStatus - s.ChainID = id - s.Name = *n.Name - b, err := toml.Marshal(n) - if err != nil { - return relaytypes.NodeStatus{}, err - } - s.Config = string(b) - return s, nil -} - -type CosmosNodes []*coscfg.Node - -func (ns *CosmosNodes) SetFrom(fs *CosmosNodes) { - for _, f := range *fs { - if f.Name == nil { - *ns = append(*ns, f) - } else if i := slices.IndexFunc(*ns, func(n *coscfg.Node) bool { - return n.Name != nil && *n.Name == *f.Name - }); i == -1 { - *ns = append(*ns, f) - } else { - setFromNode((*ns)[i], f) - } - } -} - -func setFromNode(n, f *coscfg.Node) { - if f.Name != nil { - n.Name = f.Name - } - if f.TendermintURL != nil { - n.TendermintURL = f.TendermintURL - } -} - -func legacyNode(n *coscfg.Node, id string) db.Node { - return db.Node{ - Name: *n.Name, - CosmosChainID: id, - TendermintURL: (*url.URL)(n.TendermintURL).String(), - } -} - -type CosmosConfig struct { - ChainID *string - // Do not access directly. Use [IsEnabled] - Enabled *bool - coscfg.Chain - Nodes CosmosNodes -} - -func (c *CosmosConfig) IsEnabled() bool { - return c.Enabled == nil || *c.Enabled -} - -func (c *CosmosConfig) SetFrom(f *CosmosConfig) { - if f.ChainID != nil { - c.ChainID = f.ChainID - } - if f.Enabled != nil { - c.Enabled = f.Enabled - } - setFromChain(&c.Chain, &f.Chain) - c.Nodes.SetFrom(&f.Nodes) -} - -func setFromChain(c, f *coscfg.Chain) { - if f.Bech32Prefix != nil { - c.Bech32Prefix = f.Bech32Prefix - } - if f.BlockRate != nil { - c.BlockRate = f.BlockRate - } - if f.BlocksUntilTxTimeout != nil { - c.BlocksUntilTxTimeout = f.BlocksUntilTxTimeout - } - if f.ConfirmPollPeriod != nil { - c.ConfirmPollPeriod = f.ConfirmPollPeriod - } - if f.FallbackGasPrice != nil { - c.FallbackGasPrice = f.FallbackGasPrice - } - if f.GasToken != nil { - c.GasToken = f.GasToken - } - if f.GasLimitMultiplier != nil { - c.GasLimitMultiplier = f.GasLimitMultiplier - } - if f.MaxMsgsPerBatch != nil { - c.MaxMsgsPerBatch = f.MaxMsgsPerBatch - } - if f.OCR2CachePollPeriod != nil { - c.OCR2CachePollPeriod = f.OCR2CachePollPeriod - } - if f.OCR2CacheTTL != nil { - c.OCR2CacheTTL = f.OCR2CacheTTL - } - if f.TxMsgTimeout != nil { - c.TxMsgTimeout = f.TxMsgTimeout - } -} - -func (c *CosmosConfig) ValidateConfig() (err error) { - if c.ChainID == nil { - err = multierr.Append(err, config.ErrMissing{Name: "ChainID", Msg: "required for all chains"}) - } else if *c.ChainID == "" { - err = multierr.Append(err, config.ErrEmpty{Name: "ChainID", Msg: "required for all chains"}) - } - - if len(c.Nodes) == 0 { - err = multierr.Append(err, config.ErrMissing{Name: "Nodes", Msg: "must have at least one node"}) - } - - return -} - -func (c *CosmosConfig) TOMLString() (string, error) { - b, err := toml.Marshal(c) - if err != nil { - return "", err - } - return string(b), nil -} - -var _ coscfg.Config = &CosmosConfig{} - -func (c *CosmosConfig) Bech32Prefix() string { - return *c.Chain.Bech32Prefix -} - -func (c *CosmosConfig) BlockRate() time.Duration { - return c.Chain.BlockRate.Duration() -} - -func (c *CosmosConfig) BlocksUntilTxTimeout() int64 { - return *c.Chain.BlocksUntilTxTimeout -} - -func (c *CosmosConfig) ConfirmPollPeriod() time.Duration { - return c.Chain.ConfirmPollPeriod.Duration() -} - -func (c *CosmosConfig) FallbackGasPrice() sdk.Dec { - return sdkDecFromDecimal(c.Chain.FallbackGasPrice) -} - -func (c *CosmosConfig) GasToken() string { - return *c.Chain.GasToken -} - -func (c *CosmosConfig) GasLimitMultiplier() float64 { - return c.Chain.GasLimitMultiplier.InexactFloat64() -} - -func (c *CosmosConfig) MaxMsgsPerBatch() int64 { - return *c.Chain.MaxMsgsPerBatch -} - -func (c *CosmosConfig) OCR2CachePollPeriod() time.Duration { - return c.Chain.OCR2CachePollPeriod.Duration() -} - -func (c *CosmosConfig) OCR2CacheTTL() time.Duration { - return c.Chain.OCR2CacheTTL.Duration() -} - -func (c *CosmosConfig) TxMsgTimeout() time.Duration { - return c.Chain.TxMsgTimeout.Duration() -} - -func sdkDecFromDecimal(d *decimal.Decimal) sdk.Dec { - i := d.Shift(sdk.Precision) - return sdk.NewDecFromBigIntWithPrec(i.BigInt(), sdk.Precision) -} - -func (c *CosmosConfig) GetNode(name string) (db.Node, error) { - for _, n := range c.Nodes { - if *n.Name == name { - return legacyNode(n, *c.ChainID), nil - } - } - return db.Node{}, fmt.Errorf("%w: node %q", chains.ErrNotFound, name) -} - -func (c *CosmosConfig) ListNodes() ([]db.Node, error) { - var allNodes []db.Node - for _, n := range c.Nodes { - allNodes = append(allNodes, legacyNode(n, *c.ChainID)) - } - return allNodes, nil -} diff --git a/core/chains/cosmos/config_test.go b/core/chains/cosmos/config_test.go deleted file mode 100644 index 54f91a13620..00000000000 --- a/core/chains/cosmos/config_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package cosmos - -import ( - "reflect" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/shopspring/decimal" - "github.com/stretchr/testify/assert" - - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" -) - -func Test_sdkDecFromDecimal(t *testing.T) { - tests := []string{ - "0.0", - "0.1", - "1.0", - "0.000000000000000001", - } - for _, tt := range tests { - t.Run(tt, func(t *testing.T) { - val := decimal.RequireFromString(tt) - exp := sdk.MustNewDecFromStr(tt) - assert.Equal(t, exp, sdkDecFromDecimal(&val)) - }) - } -} - -func TestCosmosConfig_GetNode(t *testing.T) { - type fields struct { - ChainID *string - Nodes CosmosNodes - } - type args struct { - name string - } - tests := []struct { - name string - fields fields - args args - want db.Node - wantErr bool - }{ - { - name: "not found", - args: args{ - name: "not a node", - }, - fields: fields{Nodes: CosmosNodes{}}, - want: db.Node{}, - wantErr: true, - }, - { - name: "success", - args: args{ - name: "node", - }, - fields: fields{ - ChainID: ptr("chainID"), - Nodes: []*coscfg.Node{ - &coscfg.Node{ - Name: ptr("node"), - TendermintURL: &utils.URL{}, - }, - }}, - want: db.Node{ - CosmosChainID: "chainID", - Name: "node", - TendermintURL: "", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &CosmosConfig{ - Nodes: tt.fields.Nodes, - ChainID: tt.fields.ChainID, - } - got, err := c.GetNode(tt.args.name) - if (err != nil) != tt.wantErr { - t.Errorf("CosmosConfig.GetNode() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("CosmosConfig.GetNode() = %v, want %v", got, tt.want) - } - }) - } -} - -func ptr[T any](t T) *T { - return &t -} diff --git a/core/chains/cosmos/cosmostxm/helpers_test.go b/core/chains/cosmos/cosmostxm/helpers_test.go index ad93189082e..a2dfbbeed84 100644 --- a/core/chains/cosmos/cosmostxm/helpers_test.go +++ b/core/chains/cosmos/cosmostxm/helpers_test.go @@ -1,36 +1,8 @@ package cosmostxm -import ( - "context" - "time" +import "golang.org/x/exp/maps" - sdk "github.com/cosmos/cosmos-sdk/types" - "golang.org/x/exp/maps" - - cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" -) - -func (txm *Txm) ORM() *ORM { - return txm.orm -} - -func (txm *Txm) ConfirmTx(ctx context.Context, tc cosmosclient.Reader, txHash string, broadcasted []int64, maxPolls int, pollPeriod time.Duration) error { - return txm.confirmTx(ctx, tc, txHash, broadcasted, maxPolls, pollPeriod) -} - -func (txm *Txm) ConfirmAnyUnconfirmed(ctx context.Context) { - txm.confirmAnyUnconfirmed(ctx) -} - -func (txm *Txm) MarshalMsg(msg sdk.Msg) (string, []byte, error) { - return txm.marshalMsg(msg) -} - -func (txm *Txm) SendMsgBatch(ctx context.Context) { - txm.sendMsgBatch(ctx) -} - -func (ka *KeystoreAdapter) Accounts(ctx context.Context) ([]string, error) { +func (ka *keystoreAdapter) Accounts() ([]string, error) { ka.mutex.Lock() defer ka.mutex.Unlock() err := ka.updateMappingLocked() diff --git a/core/chains/cosmos/cosmostxm/key_wrapper.go b/core/chains/cosmos/cosmostxm/key_wrapper.go index 1d2d686c8c0..e03dfd89b89 100644 --- a/core/chains/cosmos/cosmostxm/key_wrapper.go +++ b/core/chains/cosmos/cosmostxm/key_wrapper.go @@ -8,15 +8,15 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) -// KeyWrapper uses a KeystoreAdapter to implement the cosmos-sdk PrivKey interface for a specific key. +// KeyWrapper uses a keystoreAdapter to implement the cosmos-sdk PrivKey interface for a specific key. type KeyWrapper struct { - adapter *KeystoreAdapter + adapter *keystoreAdapter account string } var _ cryptotypes.PrivKey = &KeyWrapper{} -func NewKeyWrapper(adapter *KeystoreAdapter, account string) *KeyWrapper { +func NewKeyWrapper(adapter *keystoreAdapter, account string) *KeyWrapper { return &KeyWrapper{ adapter: adapter, account: account, diff --git a/core/chains/cosmos/cosmostxm/keystore_adapter.go b/core/chains/cosmos/cosmostxm/keystore_adapter.go index c8556015c6e..6b360dde98c 100644 --- a/core/chains/cosmos/cosmostxm/keystore_adapter.go +++ b/core/chains/cosmos/cosmostxm/keystore_adapter.go @@ -21,23 +21,23 @@ type accountInfo struct { PubKey *secp256k1.PubKey } -// An adapter for a Cosmos loop.Keystore to translate public keys into bech32-prefixed account addresses. -type KeystoreAdapter struct { +// keystoreAdapter adapts a Cosmos loop.Keystore to translate public keys into bech32-prefixed account addresses. +type keystoreAdapter struct { keystore loop.Keystore accountPrefix string mutex sync.RWMutex addressToPubKey map[string]*accountInfo } -func NewKeystoreAdapter(keystore loop.Keystore, accountPrefix string) *KeystoreAdapter { - return &KeystoreAdapter{ +func newKeystoreAdapter(keystore loop.Keystore, accountPrefix string) *keystoreAdapter { + return &keystoreAdapter{ keystore: keystore, accountPrefix: accountPrefix, addressToPubKey: make(map[string]*accountInfo), } } -func (ka *KeystoreAdapter) updateMappingLocked() error { +func (ka *keystoreAdapter) updateMappingLocked() error { accounts, err := ka.keystore.Accounts(context.Background()) if err != nil { return err @@ -90,7 +90,7 @@ func (ka *KeystoreAdapter) updateMappingLocked() error { return nil } -func (ka *KeystoreAdapter) lookup(id string) (*accountInfo, error) { +func (ka *keystoreAdapter) lookup(id string) (*accountInfo, error) { ka.mutex.RLock() ai, ok := ka.addressToPubKey[id] ka.mutex.RUnlock() @@ -111,7 +111,7 @@ func (ka *KeystoreAdapter) lookup(id string) (*accountInfo, error) { return ai, nil } -func (ka *KeystoreAdapter) Sign(ctx context.Context, id string, hash []byte) ([]byte, error) { +func (ka *keystoreAdapter) Sign(ctx context.Context, id string, hash []byte) ([]byte, error) { accountInfo, err := ka.lookup(id) if err != nil { return nil, err @@ -120,7 +120,7 @@ func (ka *KeystoreAdapter) Sign(ctx context.Context, id string, hash []byte) ([] } // Returns the cosmos PubKey associated with the prefixed address. -func (ka *KeystoreAdapter) PubKey(address string) (cryptotypes.PubKey, error) { +func (ka *keystoreAdapter) PubKey(address string) (cryptotypes.PubKey, error) { accountInfo, err := ka.lookup(address) if err != nil { return nil, err diff --git a/core/chains/cosmos/cosmostxm/orm_test.go b/core/chains/cosmos/cosmostxm/orm_test.go index c7418749360..3cee25bac12 100644 --- a/core/chains/cosmos/cosmostxm/orm_test.go +++ b/core/chains/cosmos/cosmostxm/orm_test.go @@ -1,4 +1,4 @@ -package cosmostxm_test +package cosmostxm import ( "testing" @@ -8,7 +8,6 @@ import ( cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -19,7 +18,7 @@ func TestORM(t *testing.T) { lggr := logger.TestLogger(t) logCfg := pgtest.NewQConfig(true) chainID := cosmostest.RandomChainID() - o := cosmostxm.NewORM(chainID, db, lggr, logCfg) + o := NewORM(chainID, db, lggr, logCfg) // Create mid, err := o.InsertMsg("0x123", "", []byte("hello")) diff --git a/core/chains/cosmos/cosmostxm/txm.go b/core/chains/cosmos/cosmostxm/txm.go index 8806d97a9fb..712e1b8fc73 100644 --- a/core/chains/cosmos/cosmostxm/txm.go +++ b/core/chains/cosmos/cosmostxm/txm.go @@ -47,7 +47,7 @@ type Txm struct { orm *ORM lggr logger.Logger tc func() (cosmosclient.ReaderWriter, error) - keystoreAdapter *KeystoreAdapter + keystoreAdapter *keystoreAdapter stop, done chan struct{} cfg coscfg.Config gpe cosmosclient.ComposedGasPriceEstimator @@ -56,7 +56,7 @@ type Txm struct { // NewTxm creates a txm. Uses simulation so should only be used to send txes to trusted contracts i.e. OCR. func NewTxm(db *sqlx.DB, tc func() (cosmosclient.ReaderWriter, error), gpe cosmosclient.ComposedGasPriceEstimator, chainID string, cfg coscfg.Config, ks loop.Keystore, lggr logger.Logger, logCfg pg.QConfig, eb pg.EventBroadcaster) *Txm { lggr = logger.Named(lggr, "Txm") - keystoreAdapter := NewKeystoreAdapter(ks, cfg.Bech32Prefix()) + keystoreAdapter := newKeystoreAdapter(ks, cfg.Bech32Prefix()) return &Txm{ eb: eb, orm: NewORM(chainID, db, lggr, logCfg), diff --git a/core/chains/cosmos/cosmostxm/txm_internal_test.go b/core/chains/cosmos/cosmostxm/txm_internal_test.go index 66a8c98b637..f29f130cae4 100644 --- a/core/chains/cosmos/cosmostxm/txm_internal_test.go +++ b/core/chains/cosmos/cosmostxm/txm_internal_test.go @@ -1,4 +1,4 @@ -package cosmostxm_test +package cosmostxm import ( "fmt" @@ -21,8 +21,6 @@ import ( cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" relayutils "github.com/smartcontractkit/chainlink-relay/pkg/utils" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -31,7 +29,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -func generateExecuteMsg(t *testing.T, msg []byte, from, to cosmostypes.AccAddress) cosmostypes.Msg { +func generateExecuteMsg(msg []byte, from, to cosmostypes.AccAddress) cosmostypes.Msg { return &wasmtypes.MsgExecuteContract{ Sender: from.String(), Contract: to.String(), @@ -59,8 +57,8 @@ func TestTxm(t *testing.T) { } loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - adapter := cosmostxm.NewKeystoreAdapter(loopKs, "wasm") - accounts, err := adapter.Accounts(testutils.Context(t)) + adapter := newKeystoreAdapter(loopKs, "wasm") + accounts, err := adapter.Accounts() require.NoError(t, err) require.Equal(t, len(accounts), 4) @@ -77,7 +75,7 @@ func TestTxm(t *testing.T) { chainID := cosmostest.RandomChainID() two := int64(2) gasToken := "ucosm" - cfg := &cosmos.CosmosConfig{Chain: coscfg.Chain{ + cfg := &coscfg.TOMLConfig{Chain: coscfg.Chain{ MaxMsgsPerBatch: &two, GasToken: &gasToken, }} @@ -94,10 +92,10 @@ func TestTxm(t *testing.T) { tc := newReaderWriterMock(t) tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, logCfg, nil) + txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, logCfg, nil) // Enqueue a single msg, then send it in a batch - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg(t, []byte(`1`), sender1, contract)) + id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`1`), sender1, contract)) require.NoError(t, err) tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil) tc.On("BatchSimulateUnsigned", mock.Anything, mock.Anything).Return(&cosmosclient.BatchSimResults{ @@ -118,10 +116,10 @@ func TestTxm(t *testing.T) { txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil) tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil) - txm.SendMsgBatch(testutils.Context(t)) + txm.sendMsgBatch(testutils.Context(t)) // Should be in completed state - completed, err := txm.ORM().GetMsgs(id1) + completed, err := txm.orm.GetMsgs(id1) require.NoError(t, err) require.Equal(t, 1, len(completed)) assert.Equal(t, completed[0].State, cosmosdb.Confirmed) @@ -131,11 +129,11 @@ func TestTxm(t *testing.T) { tc := newReaderWriterMock(t) tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) + txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg(t, []byte(`0`), sender1, contract)) + id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`0`), sender1, contract)) require.NoError(t, err) - id2, err := txm.Enqueue(contract.String(), generateExecuteMsg(t, []byte(`1`), sender2, contract)) + id2, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`1`), sender2, contract)) require.NoError(t, err) tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil).Once() @@ -173,10 +171,10 @@ func TestTxm(t *testing.T) { txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil).Once() tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil).Once() - txm.SendMsgBatch(testutils.Context(t)) + txm.sendMsgBatch(testutils.Context(t)) // Should be in completed state - completed, err := txm.ORM().GetMsgs(id1, id2) + completed, err := txm.orm.GetMsgs(id1, id2) require.NoError(t, err) require.Equal(t, 2, len(completed)) assert.Equal(t, cosmosdb.Errored, completed[0].State) // cancelled @@ -187,11 +185,11 @@ func TestTxm(t *testing.T) { tc := newReaderWriterMock(t) tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) + txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg(t, []byte(`0`), sender1, contract)) + id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`0`), sender1, contract)) require.NoError(t, err) - id2, err := txm.Enqueue(contract2.String(), generateExecuteMsg(t, []byte(`1`), sender2, contract2)) + id2, err := txm.Enqueue(contract2.String(), generateExecuteMsg([]byte(`1`), sender2, contract2)) require.NoError(t, err) ids := []int64{id1, id2} senders := []string{sender1.String(), sender2.String()} @@ -233,10 +231,10 @@ func TestTxm(t *testing.T) { txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil).Twice() tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil).Twice() - txm.SendMsgBatch(testutils.Context(t)) + txm.sendMsgBatch(testutils.Context(t)) // Should be in completed state - completed, err := txm.ORM().GetMsgs(id1, id2) + completed, err := txm.orm.GetMsgs(id1, id2) require.NoError(t, err) require.Equal(t, 2, len(completed)) assert.Equal(t, cosmosdb.Confirmed, completed[0].State) @@ -251,15 +249,15 @@ func TestTxm(t *testing.T) { }, errors.New("not found")).Twice() tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - i, err := txm.ORM().InsertMsg("blah", "", []byte{0x01}) + txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) + i, err := txm.orm.InsertMsg("blah", "", []byte{0x01}) require.NoError(t, err) txh := "0x123" - require.NoError(t, txm.ORM().UpdateMsgs([]int64{i}, cosmosdb.Started, &txh)) - require.NoError(t, txm.ORM().UpdateMsgs([]int64{i}, cosmosdb.Broadcasted, &txh)) - err = txm.ConfirmTx(testutils.Context(t), tc, txh, []int64{i}, 2, 1*time.Millisecond) + require.NoError(t, txm.orm.UpdateMsgs([]int64{i}, cosmosdb.Started, &txh)) + require.NoError(t, txm.orm.UpdateMsgs([]int64{i}, cosmosdb.Broadcasted, &txh)) + err = txm.confirmTx(testutils.Context(t), tc, txh, []int64{i}, 2, 1*time.Millisecond) require.NoError(t, err) - m, err := txm.ORM().GetMsgs(i) + m, err := txm.orm.GetMsgs(i) require.NoError(t, err) require.Equal(t, 1, len(m)) assert.Equal(t, cosmosdb.Errored, m[0].State) @@ -282,31 +280,31 @@ func TestTxm(t *testing.T) { }, nil).Once() tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) + txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) // Insert and broadcast 3 msgs with different txhashes. - id1, err := txm.ORM().InsertMsg("blah", "", []byte{0x01}) + id1, err := txm.orm.InsertMsg("blah", "", []byte{0x01}) require.NoError(t, err) - id2, err := txm.ORM().InsertMsg("blah", "", []byte{0x02}) + id2, err := txm.orm.InsertMsg("blah", "", []byte{0x02}) require.NoError(t, err) - id3, err := txm.ORM().InsertMsg("blah", "", []byte{0x03}) + id3, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) require.NoError(t, err) - err = txm.ORM().UpdateMsgs([]int64{id1}, cosmosdb.Started, &txHash1) + err = txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Started, &txHash1) require.NoError(t, err) - err = txm.ORM().UpdateMsgs([]int64{id2}, cosmosdb.Started, &txHash2) + err = txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Started, &txHash2) require.NoError(t, err) - err = txm.ORM().UpdateMsgs([]int64{id3}, cosmosdb.Started, &txHash3) + err = txm.orm.UpdateMsgs([]int64{id3}, cosmosdb.Started, &txHash3) require.NoError(t, err) - err = txm.ORM().UpdateMsgs([]int64{id1}, cosmosdb.Broadcasted, &txHash1) + err = txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Broadcasted, &txHash1) require.NoError(t, err) - err = txm.ORM().UpdateMsgs([]int64{id2}, cosmosdb.Broadcasted, &txHash2) + err = txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Broadcasted, &txHash2) require.NoError(t, err) - err = txm.ORM().UpdateMsgs([]int64{id3}, cosmosdb.Broadcasted, &txHash3) + err = txm.orm.UpdateMsgs([]int64{id3}, cosmosdb.Broadcasted, &txHash3) require.NoError(t, err) // Confirm them as in a restart while confirming scenario - txm.ConfirmAnyUnconfirmed(testutils.Context(t)) - msgs, err := txm.ORM().GetMsgs(id1, id2, id3) + txm.confirmAnyUnconfirmed(testutils.Context(t)) + msgs, err := txm.orm.GetMsgs(id1, id2, id3) require.NoError(t, err) require.Equal(t, 3, len(msgs)) assert.Equal(t, cosmosdb.Confirmed, msgs[0].State) @@ -320,33 +318,33 @@ func TestTxm(t *testing.T) { require.NoError(t, err) tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } two := int64(2) - cfgShortExpiry := &cosmos.CosmosConfig{Chain: coscfg.Chain{ + cfgShortExpiry := &coscfg.TOMLConfig{Chain: coscfg.Chain{ MaxMsgsPerBatch: &two, TxMsgTimeout: &timeout, }} cfgShortExpiry.SetDefaults() loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfgShortExpiry, loopKs, lggr, pgtest.NewQConfig(true), nil) + txm := NewTxm(db, tcFn, *gpe, chainID, cfgShortExpiry, loopKs, lggr, pgtest.NewQConfig(true), nil) // Send a single one expired - id1, err := txm.ORM().InsertMsg("blah", "", []byte{0x03}) + id1, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) require.NoError(t, err) time.Sleep(1 * time.Millisecond) - txm.SendMsgBatch(testutils.Context(t)) + txm.sendMsgBatch(testutils.Context(t)) // Should be marked errored - m, err := txm.ORM().GetMsgs(id1) + m, err := txm.orm.GetMsgs(id1) require.NoError(t, err) assert.Equal(t, cosmosdb.Errored, m[0].State) // Send a batch which is all expired - id2, err := txm.ORM().InsertMsg("blah", "", []byte{0x03}) + id2, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) require.NoError(t, err) - id3, err := txm.ORM().InsertMsg("blah", "", []byte{0x03}) + id3, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) require.NoError(t, err) time.Sleep(1 * time.Millisecond) - txm.SendMsgBatch(testutils.Context(t)) + txm.sendMsgBatch(testutils.Context(t)) require.NoError(t, err) - ms, err := txm.ORM().GetMsgs(id2, id3) + ms, err := txm.orm.GetMsgs(id2, id3) require.NoError(t, err) assert.Equal(t, cosmosdb.Errored, ms[0].State) assert.Equal(t, cosmosdb.Errored, ms[1].State) @@ -367,17 +365,17 @@ func TestTxm(t *testing.T) { tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil) tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } two := int64(2) - cfgMaxMsgs := &cosmos.CosmosConfig{Chain: coscfg.Chain{ + cfgMaxMsgs := &coscfg.TOMLConfig{Chain: coscfg.Chain{ MaxMsgsPerBatch: &two, }} cfgMaxMsgs.SetDefaults() loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, cfgMaxMsgs, loopKs, lggr, pgtest.NewQConfig(true), nil) + txm := NewTxm(db, tcFn, *gpe, chainID, cfgMaxMsgs, loopKs, lggr, pgtest.NewQConfig(true), nil) // Leftover started is processed - msg1 := generateExecuteMsg(t, []byte{0x03}, sender1, contract) + msg1 := generateExecuteMsg([]byte{0x03}, sender1, contract) id1 := mustInsertMsg(t, txm, contract.String(), msg1) - require.NoError(t, txm.ORM().UpdateMsgs([]int64{id1}, cosmosdb.Started, nil)) + require.NoError(t, txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Started, nil)) msgs := cosmosclient.SimMsgs{{ID: id1, Msg: &wasmtypes.MsgExecuteContract{ Sender: sender1.String(), Msg: []byte{0x03}, @@ -386,16 +384,16 @@ func TestTxm(t *testing.T) { tc.On("BatchSimulateUnsigned", msgs, mock.Anything). Return(&cosmosclient.BatchSimResults{Failed: nil, Succeeded: msgs}, nil).Once() time.Sleep(1 * time.Millisecond) - txm.SendMsgBatch(testutils.Context(t)) - m, err := txm.ORM().GetMsgs(id1) + txm.sendMsgBatch(testutils.Context(t)) + m, err := txm.orm.GetMsgs(id1) require.NoError(t, err) assert.Equal(t, cosmosdb.Confirmed, m[0].State) // Leftover started is not cancelled - msg2 := generateExecuteMsg(t, []byte{0x04}, sender1, contract) - msg3 := generateExecuteMsg(t, []byte{0x05}, sender1, contract) + msg2 := generateExecuteMsg([]byte{0x04}, sender1, contract) + msg3 := generateExecuteMsg([]byte{0x05}, sender1, contract) id2 := mustInsertMsg(t, txm, contract.String(), msg2) - require.NoError(t, txm.ORM().UpdateMsgs([]int64{id2}, cosmosdb.Started, nil)) + require.NoError(t, txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Started, nil)) time.Sleep(time.Millisecond) // ensure != CreatedAt id3 := mustInsertMsg(t, txm, contract.String(), msg3) msgs = cosmosclient.SimMsgs{{ID: id2, Msg: &wasmtypes.MsgExecuteContract{ @@ -410,19 +408,19 @@ func TestTxm(t *testing.T) { tc.On("BatchSimulateUnsigned", msgs, mock.Anything). Return(&cosmosclient.BatchSimResults{Failed: nil, Succeeded: msgs}, nil).Once() time.Sleep(1 * time.Millisecond) - txm.SendMsgBatch(testutils.Context(t)) + txm.sendMsgBatch(testutils.Context(t)) require.NoError(t, err) - ms, err := txm.ORM().GetMsgs(id2, id3) + ms, err := txm.orm.GetMsgs(id2, id3) require.NoError(t, err) assert.Equal(t, cosmosdb.Confirmed, ms[0].State) assert.Equal(t, cosmosdb.Confirmed, ms[1].State) }) } -func mustInsertMsg(t *testing.T, txm *cosmostxm.Txm, contractID string, msg cosmostypes.Msg) int64 { - typeURL, raw, err := txm.MarshalMsg(msg) +func mustInsertMsg(t *testing.T, txm *Txm, contractID string, msg cosmostypes.Msg) int64 { + typeURL, raw, err := txm.marshalMsg(msg) require.NoError(t, err) - id, err := txm.ORM().InsertMsg(contractID, typeURL, raw) + id, err := txm.orm.InsertMsg(contractID, typeURL, raw) require.NoError(t, err) return id } diff --git a/core/chains/cosmos/cosmostxm/txm_test.go b/core/chains/cosmos/cosmostxm/txm_test.go index a7a8d0280c6..25ac9e8d9ec 100644 --- a/core/chains/cosmos/cosmostxm/txm_test.go +++ b/core/chains/cosmos/cosmostxm/txm_test.go @@ -2,151 +2,120 @@ package cosmostxm_test -import ( - "fmt" - "testing" - "time" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/cometbft/cometbft/abci/types" - sdk "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/google/uuid" - "github.com/onsi/gomega" - "github.com/stretchr/testify/require" - - cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" - "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/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" - - . "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" -) - -func TestTxm_Integration(t *testing.T) { - chainID := cosmostest.RandomChainID() - cosmosChain := coscfg.Chain{} - cosmosChain.SetDefaults() - fallbackGasPrice := sdk.NewDecCoinFromDec(*cosmosChain.GasToken, sdk.MustNewDecFromStr("0.01")) - chainConfig := cosmos.CosmosConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain} - cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "cosmos_txm", func(c *chainlink.Config, s *chainlink.Secrets) { - c.Cosmos = cosmos.CosmosConfigs{&chainConfig} - }) - lggr := logger.TestLogger(t) - logCfg := pgtest.NewQConfig(true) - gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ - cosmosclient.NewFixedGasPriceEstimator(map[string]sdk.DecCoin{ - *cosmosChain.GasToken: fallbackGasPrice, - }, - lggr.(logger.SugaredLogger), - ), - }, lggr) - orm := cosmostxm.NewORM(chainID, db, lggr, logCfg) - eb := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, lggr, uuid.New()) - require.NoError(t, eb.Start(testutils.Context(t))) - t.Cleanup(func() { require.NoError(t, eb.Close()) }) - ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, pgtest.NewQConfig(true)) - zeConfig := sdk.GetConfig() - fmt.Println(zeConfig) - accounts, testdir, tendermintURL := cosmosclient.SetupLocalCosmosNode(t, chainID, *cosmosChain.GasToken) - tc, err := cosmosclient.NewClient(chainID, tendermintURL, cosmos.DefaultRequestTimeout, lggr) - require.NoError(t, err) - - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - keystoreAdapter := cosmostxm.NewKeystoreAdapter(loopKs, *cosmosChain.Bech32Prefix) - - // First create a transmitter key and fund it with 1k native tokens - require.NoError(t, ks.Unlock("blah")) - err = ks.Cosmos().EnsureKey() - require.NoError(t, err) - ksAccounts, err := keystoreAdapter.Accounts(testutils.Context(t)) - require.NoError(t, err) - transmitterAddress := ksAccounts[0] - transmitterID, err := sdk.AccAddressFromBech32(transmitterAddress) - require.NoError(t, err) - an, sn, err := tc.Account(accounts[0].Address) - require.NoError(t, err) - resp, err := tc.SignAndBroadcast([]sdk.Msg{banktypes.NewMsgSend(accounts[0].Address, transmitterID, sdk.NewCoins(sdk.NewInt64Coin(*cosmosChain.GasToken, 100000)))}, - an, sn, gpe.GasPrices()[*cosmosChain.GasToken], accounts[0].PrivateKey, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) - tx, success := cosmosclient.AwaitTxCommitted(t, tc, resp.TxResponse.TxHash) - require.True(t, success) - require.Equal(t, types.CodeTypeOK, tx.TxResponse.Code) - require.NoError(t, err) - - // TODO: find a way to pull this test artifact from - // the chainlink-cosmos repo instead of copying it to cores testdata - contractID := cosmosclient.DeployTestContract(t, tendermintURL, chainID, *cosmosChain.GasToken, accounts[0], cosmosclient.Account{ - Name: "transmitter", - PrivateKey: cosmostxm.NewKeyWrapper(keystoreAdapter, transmitterAddress), - Address: transmitterID, - }, tc, testdir, "../../../testdata/cosmos/my_first_contract.wasm") - - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - // Start txm - txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, &chainConfig, loopKs, lggr, pgtest.NewQConfig(true), eb) - require.NoError(t, txm.Start(testutils.Context(t))) - - // Change the contract state - setMsg := &wasmtypes.MsgExecuteContract{ - Sender: transmitterID.String(), - Contract: contractID.String(), - Msg: []byte(`{"reset":{"count":5}}`), - Funds: sdk.Coins{}, - } - _, err = txm.Enqueue(contractID.String(), setMsg) - require.NoError(t, err) - - // Observe the counter gets set eventually - gomega.NewWithT(t).Eventually(func() bool { - d, err := tc.ContractState(contractID, []byte(`{"get_count":{}}`)) - require.NoError(t, err) - t.Log("contract value", string(d)) - return string(d) == `{"count":5}` - }, 20*time.Second, time.Second).Should(gomega.BeTrue()) - // Ensure messages are completed - gomega.NewWithT(t).Eventually(func() bool { - msgs, err := orm.GetMsgsState(Confirmed, 5) - require.NoError(t, err) - return 1 == len(msgs) - }, 5*time.Second, time.Second).Should(gomega.BeTrue()) - - // Ensure invalid msgs are marked as errored - invalidMsg := &wasmtypes.MsgExecuteContract{ - Sender: transmitterID.String(), - Contract: contractID.String(), - Msg: []byte(`{"blah":{"blah":5}}`), - Funds: sdk.Coins{}, - } - _, err = txm.Enqueue(contractID.String(), invalidMsg) - require.NoError(t, err) - _, err = txm.Enqueue(contractID.String(), invalidMsg) - require.NoError(t, err) - _, err = txm.Enqueue(contractID.String(), setMsg) - require.NoError(t, err) - - // Ensure messages are completed - gomega.NewWithT(t).Eventually(func() bool { - succeeded, err := orm.GetMsgsState(Confirmed, 5) - require.NoError(t, err) - errored, err := orm.GetMsgsState(Errored, 5) - require.NoError(t, err) - t.Log("errored", len(errored), "succeeded", len(succeeded)) - return 2 == len(succeeded) && 2 == len(errored) - }, 20*time.Second, time.Second).Should(gomega.BeTrue()) - - // Observe the messages have been marked as completed - require.NoError(t, txm.Close()) -} - -func ptr[T any](t T) *T { return &t } +// TestTxm_Integration is disabled in order to be moved to chainlink-cosmos before DB testing is available +//func TestTxm_Integration(t *testing.T) { +// chainID := cosmostest.RandomChainID() +// cosmosChain := coscfg.Chain{} +// cosmosChain.SetDefaults() +// fallbackGasPrice := sdk.NewDecCoinFromDec(*cosmosChain.GasToken, sdk.MustNewDecFromStr("0.01")) +// chainConfig := cosmos.CosmosConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain} +// cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "cosmos_txm", func(c *chainlink.Config, s *chainlink.Secrets) { +// c.Cosmos = cosmos.CosmosConfigs{&chainConfig} +// }) +// lggr := logger.TestLogger(t) +// logCfg := pgtest.NewQConfig(true) +// gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ +// cosmosclient.NewFixedGasPriceEstimator(map[string]sdk.DecCoin{ +// *cosmosChain.GasToken: fallbackGasPrice, +// }, +// lggr.(logger.SugaredLogger), +// ), +// }, lggr) +// orm := cosmostxm.NewORM(chainID, db, lggr, logCfg) +// eb := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, lggr, uuid.New()) +// require.NoError(t, eb.Start(testutils.Context(t))) +// t.Cleanup(func() { require.NoError(t, eb.Close()) }) +// ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, pgtest.NewQConfig(true)) +// zeConfig := sdk.GetConfig() +// fmt.Println(zeConfig) +// accounts, testdir, tendermintURL := cosmosclient.SetupLocalCosmosNode(t, chainID, *cosmosChain.GasToken) +// tc, err := cosmosclient.NewClient(chainID, tendermintURL, 0, lggr) +// require.NoError(t, err) +// +// loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} +// keystoreAdapter := cosmostxm.NewKeystoreAdapter(loopKs, *cosmosChain.Bech32Prefix) +// +// // First create a transmitter key and fund it with 1k native tokens +// require.NoError(t, ks.Unlock("blah")) +// err = ks.Cosmos().EnsureKey() +// require.NoError(t, err) +// ksAccounts, err := keystoreAdapter.Accounts() +// require.NoError(t, err) +// transmitterAddress := ksAccounts[0] +// transmitterID, err := sdk.AccAddressFromBech32(transmitterAddress) +// require.NoError(t, err) +// an, sn, err := tc.Account(accounts[0].Address) +// require.NoError(t, err) +// resp, err := tc.SignAndBroadcast([]sdk.Msg{banktypes.NewMsgSend(accounts[0].Address, transmitterID, sdk.NewCoins(sdk.NewInt64Coin(*cosmosChain.GasToken, 100000)))}, +// an, sn, gpe.GasPrices()[*cosmosChain.GasToken], accounts[0].PrivateKey, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) +// tx, success := cosmosclient.AwaitTxCommitted(t, tc, resp.TxResponse.TxHash) +// require.True(t, success) +// require.Equal(t, types.CodeTypeOK, tx.TxResponse.Code) +// require.NoError(t, err) +// +// // TODO: find a way to pull this test artifact from +// // the chainlink-cosmos repo instead of copying it to cores testdata +// contractID := cosmosclient.DeployTestContract(t, tendermintURL, chainID, *cosmosChain.GasToken, accounts[0], cosmosclient.Account{ +// Name: "transmitter", +// PrivateKey: cosmostxm.NewKeyWrapper(keystoreAdapter, transmitterAddress), +// Address: transmitterID, +// }, tc, testdir, "../../../testdata/cosmos/my_first_contract.wasm") +// +// tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } +// // Start txm +// txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, &chainConfig, loopKs, lggr, pgtest.NewQConfig(true), eb) +// require.NoError(t, txm.Start(testutils.Context(t))) +// +// // Change the contract state +// setMsg := &wasmtypes.MsgExecuteContract{ +// Sender: transmitterID.String(), +// Contract: contractID.String(), +// Msg: []byte(`{"reset":{"count":5}}`), +// Funds: sdk.Coins{}, +// } +// _, err = txm.Enqueue(contractID.String(), setMsg) +// require.NoError(t, err) +// +// // Observe the counter gets set eventually +// gomega.NewWithT(t).Eventually(func() bool { +// d, err := tc.ContractState(contractID, []byte(`{"get_count":{}}`)) +// require.NoError(t, err) +// t.Log("contract value", string(d)) +// return string(d) == `{"count":5}` +// }, 20*time.Second, time.Second).Should(gomega.BeTrue()) +// // Ensure messages are completed +// gomega.NewWithT(t).Eventually(func() bool { +// msgs, err := orm.GetMsgsState(Confirmed, 5) +// require.NoError(t, err) +// return 1 == len(msgs) +// }, 5*time.Second, time.Second).Should(gomega.BeTrue()) +// +// // Ensure invalid msgs are marked as errored +// invalidMsg := &wasmtypes.MsgExecuteContract{ +// Sender: transmitterID.String(), +// Contract: contractID.String(), +// Msg: []byte(`{"blah":{"blah":5}}`), +// Funds: sdk.Coins{}, +// } +// _, err = txm.Enqueue(contractID.String(), invalidMsg) +// require.NoError(t, err) +// _, err = txm.Enqueue(contractID.String(), invalidMsg) +// require.NoError(t, err) +// _, err = txm.Enqueue(contractID.String(), setMsg) +// require.NoError(t, err) +// +// // Ensure messages are completed +// gomega.NewWithT(t).Eventually(func() bool { +// succeeded, err := orm.GetMsgsState(Confirmed, 5) +// require.NoError(t, err) +// errored, err := orm.GetMsgsState(Errored, 5) +// require.NoError(t, err) +// t.Log("errored", len(errored), "succeeded", len(succeeded)) +// return 2 == len(succeeded) && 2 == len(errored) +// }, 20*time.Second, time.Second).Should(gomega.BeTrue()) +// +// // Observe the messages have been marked as completed +// require.NoError(t, txm.Close()) +//} +// +//func ptr[T any](t T) *T { return &t } diff --git a/core/cmd/cosmos_chains_commands_test.go b/core/cmd/cosmos_chains_commands_test.go index 55e6a60d1ce..a0d2052d836 100644 --- a/core/cmd/cosmos_chains_commands_test.go +++ b/core/cmd/cosmos_chains_commands_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -17,7 +17,7 @@ func TestShell_IndexCosmosChains(t *testing.T) { t.Parallel() chainID := cosmostest.RandomChainID() - chain := cosmos.CosmosConfig{ + chain := coscfg.TOMLConfig{ ChainID: ptr(chainID), Enabled: ptr(true), } diff --git a/core/cmd/cosmos_node_commands_test.go b/core/cmd/cosmos_node_commands_test.go index c19749ecd12..9ac7dfb2ba0 100644 --- a/core/cmd/cosmos_node_commands_test.go +++ b/core/cmd/cosmos_node_commands_test.go @@ -9,18 +9,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-relay/pkg/utils" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) -func cosmosStartNewApplication(t *testing.T, cfgs ...*cosmos.CosmosConfig) *cltest.TestApplication { +func cosmosStartNewApplication(t *testing.T, cfgs ...*coscfg.TOMLConfig) *cltest.TestApplication { for i := range cfgs { cfgs[i].SetDefaults() } @@ -34,14 +33,14 @@ func TestShell_IndexCosmosNodes(t *testing.T) { t.Parallel() chainID := cosmostest.RandomChainID() - node := coscfg.Node{ + node := config.Node{ Name: ptr("second"), TendermintURL: utils.MustParseURL("http://tender.mint.test/bombay-12"), } - chain := cosmos.CosmosConfig{ + chain := config.TOMLConfig{ ChainID: ptr(chainID), Enabled: ptr(true), - Nodes: cosmos.CosmosNodes{&node}, + Nodes: config.Nodes{&node}, } app := cosmosStartNewApplication(t, &chain) client, r := app.NewShellAndRenderer() diff --git a/core/cmd/cosmos_transaction_commands_test.go b/core/cmd/cosmos_transaction_commands_test.go index 04858d2956a..67b014af2c1 100644 --- a/core/cmd/cosmos_transaction_commands_test.go +++ b/core/cmd/cosmos_transaction_commands_test.go @@ -5,7 +5,6 @@ package cmd_test import ( "flag" "os" - "strconv" "testing" "time" @@ -20,15 +19,11 @@ import ( "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/denom" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/params" "github.com/smartcontractkit/chainlink-relay/pkg/utils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" ) @@ -52,13 +47,13 @@ func TestShell_SendCosmosCoins(t *testing.T) { cosmosChain.SetDefaults() accounts, _, url := cosmosclient.SetupLocalCosmosNode(t, chainID, *cosmosChain.GasToken) require.Greater(t, len(accounts), 1) - nodes := cosmos.CosmosNodes{ + nodes := coscfg.Nodes{ &coscfg.Node{ Name: ptr("random"), TendermintURL: utils.MustParseURL(url), }, } - chainConfig := cosmos.CosmosConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain, Nodes: nodes} + chainConfig := coscfg.TOMLConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain, Nodes: nodes} app := cosmosStartNewApplication(t, &chainConfig) from := accounts[0] @@ -78,9 +73,6 @@ func TestShell_SendCosmosCoins(t *testing.T) { return coin.IsPositive() }, time.Minute, 5*time.Second) - db := app.GetSqlxDB() - orm := cosmostxm.NewORM(chainID, db, logger.TestLogger(t), pgtest.NewQConfig(true)) - client, r := app.NewShellAndRenderer() cliapp := cli.NewApp() @@ -123,49 +115,18 @@ func TestShell_SendCosmosCoins(t *testing.T) { require.NotEmpty(t, renderedMsg.ID) assert.Equal(t, string(cosmosdb.Unstarted), renderedMsg.State) assert.Nil(t, renderedMsg.TxHash) - id, err := strconv.ParseInt(renderedMsg.ID, 10, 64) - require.NoError(t, err) - msgs, err := orm.GetMsgs(id) - require.NoError(t, err) - require.Equal(t, 1, len(msgs)) - msg := msgs[0] - assert.Equal(t, strconv.FormatInt(msg.ID, 10), renderedMsg.ID) - assert.Equal(t, msg.ChainID, renderedMsg.ChainID) - assert.Equal(t, msg.ContractID, renderedMsg.ContractID) - require.NotEqual(t, cosmosdb.Errored, msg.State) - switch msg.State { - case cosmosdb.Unstarted: - assert.Nil(t, msg.TxHash) - case cosmosdb.Broadcasted, cosmosdb.Confirmed: - assert.NotNil(t, msg.TxHash) - } - - // Maybe wait for confirmation - if msg.State != cosmosdb.Confirmed { - require.Eventually(t, func() bool { - msgs, err := orm.GetMsgs(id) - if assert.NoError(t, err) && assert.NotEmpty(t, msgs) { - if msg = msgs[0]; assert.Equal(t, msg.ID, id) { - t.Log("State:", msg.State) - return msg.State == cosmosdb.Confirmed - } - } - return false - }, testutils.WaitTimeout(t), time.Second) - require.NotNil(t, msg.TxHash) - } // Check balance - endBal, err := reader.Balance(from.Address, *cosmosChain.GasToken) + sent, err := denom.ConvertDecCoinToDenom(sdk.NewDecCoinFromDec(nativeToken, sdk.MustNewDecFromStr(tt.amount)), *cosmosChain.GasToken) require.NoError(t, err) - if assert.NotNil(t, startBal) && assert.NotNil(t, endBal) { - diff := startBal.Sub(*endBal).Amount - sent, err := denom.ConvertDecCoinToDenom(sdk.NewDecCoinFromDec(nativeToken, sdk.MustNewDecFromStr(tt.amount)), *cosmosChain.GasToken) + expBal := startBal.Sub(sent) + + testutils.AssertEventually(t, func() bool { + endBal, err := reader.Balance(from.Address, *cosmosChain.GasToken) require.NoError(t, err) - if assert.True(t, diff.IsInt64()) && assert.True(t, sent.Amount.IsInt64()) { - require.Greater(t, diff.Int64(), sent.Amount.Int64()) - } - } + t.Logf("%s <= %s", endBal, expBal) + return endBal.IsLTE(expBal) + }) }) } } diff --git a/core/cmd/shell.go b/core/cmd/shell.go index fbbce4becbc..1ef99992a66 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -174,7 +174,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ Keystore: keyStore.Cosmos(), - CosmosConfigs: cfg.CosmosConfigs(), + TOMLConfigs: cfg.CosmosConfigs(), EventBroadcaster: eventBroadcaster, } initOps = append(initOps, chainlink.InitCosmos(ctx, relayerFactory, cosmosCfg)) diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 276d0239941..927592e448d 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink/v2/core/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/docs" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -92,7 +92,7 @@ func TestDoc(t *testing.T) { }) t.Run("Cosmos", func(t *testing.T) { - var fallbackDefaults cosmos.CosmosConfig + var fallbackDefaults coscfg.TOMLConfig fallbackDefaults.SetDefaults() assertTOML(t, fallbackDefaults.Chain, defaults.Cosmos[0].Chain) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 3fa00901779..d47e6243b82 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -415,7 +415,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ Keystore: keyStore.Cosmos(), - CosmosConfigs: cfg.CosmosConfigs(), + TOMLConfigs: cfg.CosmosConfigs(), EventBroadcaster: eventBroadcaster, DB: db, QConfig: cfg.Database(), diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7c0161a066a..14e29a6740a 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -301,7 +301,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index e0e5e07ecf8..08356fe0766 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1456,8 +1456,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 26e2d539bac..3f55a2dc00f 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -8,10 +8,10 @@ import ( gotoml "github.com/pelletier/go-toml/v2" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/docs" "github.com/smartcontractkit/chainlink/v2/core/config/env" @@ -36,7 +36,7 @@ type Config struct { EVM evmcfg.EVMConfigs `toml:",omitempty"` - Cosmos cosmos.CosmosConfigs `toml:",omitempty"` + Cosmos coscfg.TOMLConfigs `toml:",omitempty"` Solana solana.TOMLConfigs `toml:",omitempty"` @@ -119,7 +119,7 @@ func (c *Config) setDefaults() { for i := range c.Cosmos { if c.Cosmos[i] == nil { - c.Cosmos[i] = new(cosmos.CosmosConfig) + c.Cosmos[i] = new(coscfg.TOMLConfig) } c.Cosmos[i].Chain.SetDefaults() } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 6243146e91e..81e38833359 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -15,10 +15,10 @@ import ( ocrnetworking "github.com/smartcontractkit/libocr/networking" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" starknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" @@ -199,7 +199,7 @@ func (g *generalConfig) EVMConfigs() evmcfg.EVMConfigs { return g.c.EVM } -func (g *generalConfig) CosmosConfigs() cosmos.CosmosConfigs { +func (g *generalConfig) CosmosConfigs() coscfg.TOMLConfigs { return g.c.Cosmos } diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 597dab6ba1c..48fb8272ace 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -23,7 +23,6 @@ import ( stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink/v2/core/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" legacy "github.com/smartcontractkit/chainlink/v2/core/config" @@ -138,7 +137,7 @@ var ( }, }}, }, - Cosmos: []*cosmos.CosmosConfig{ + Cosmos: []*coscfg.TOMLConfig{ { ChainID: ptr("Ibiza-808"), Chain: coscfg.Chain{ @@ -622,7 +621,7 @@ func TestConfig_Marshal(t *testing.T) { }, }, } - full.Cosmos = []*cosmos.CosmosConfig{ + full.Cosmos = []*coscfg.TOMLConfig{ { ChainID: ptr("Malaga-420"), Enabled: ptr(true), @@ -1435,7 +1434,7 @@ func assertValidationError(t *testing.T, invalid interface{ Validate() error }, func TestConfig_setDefaults(t *testing.T) { var c Config c.EVM = evmcfg.EVMConfigs{{ChainID: utils.NewBigI(99999133712345)}} - c.Cosmos = cosmos.CosmosConfigs{{ChainID: ptr("unknown cosmos chain")}} + c.Cosmos = coscfg.TOMLConfigs{{ChainID: ptr("unknown cosmos chain")}} c.Solana = solana.TOMLConfigs{{ChainID: ptr("unknown solana chain")}} c.Starknet = stkcfg.TOMLConfigs{{ChainID: ptr("unknown starknet chain")}} c.setDefaults() diff --git a/core/services/chainlink/mocks/general_config.go b/core/services/chainlink/mocks/general_config.go index 0bc51ea4310..98796e90053 100644 --- a/core/services/chainlink/mocks/general_config.go +++ b/core/services/chainlink/mocks/general_config.go @@ -6,7 +6,7 @@ import ( chainlinkconfig "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" config "github.com/smartcontractkit/chainlink/v2/core/config" - cosmos "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" + cosmosconfig "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" mock "github.com/stretchr/testify/mock" @@ -99,15 +99,15 @@ func (_m *GeneralConfig) ConfigTOML() (string, string) { } // CosmosConfigs provides a mock function with given fields: -func (_m *GeneralConfig) CosmosConfigs() cosmos.CosmosConfigs { +func (_m *GeneralConfig) CosmosConfigs() cosmosconfig.TOMLConfigs { ret := _m.Called() - var r0 cosmos.CosmosConfigs - if rf, ok := ret.Get(0).(func() cosmos.CosmosConfigs); ok { + var r0 cosmosconfig.TOMLConfigs + if rf, ok := ret.Get(0).(func() cosmosconfig.TOMLConfigs); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(cosmos.CosmosConfigs) + r0 = ret.Get(0).(cosmosconfig.TOMLConfigs) } } diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index b7291e7dc74..cfc7dbadc18 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -16,7 +16,6 @@ import ( stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -132,8 +131,8 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { }, } - c.Cosmos = cosmos.CosmosConfigs{ - &cosmos.CosmosConfig{ + c.Cosmos = coscfg.TOMLConfigs{ + &coscfg.TOMLConfig{ ChainID: &cosmosChainID1, Enabled: ptr(true), Chain: coscfg.Chain{ @@ -141,14 +140,14 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Bech32Prefix: ptr("wasm"), GasToken: ptr("cosm"), }, - Nodes: cosmos.CosmosNodes{ + Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 1 node 1"), TendermintURL: (*relayutils.URL)(models.MustParseURL("http://localhost:9548").URL()), }, }, }, - &cosmos.CosmosConfig{ + &coscfg.TOMLConfig{ ChainID: &cosmosChainID2, Enabled: ptr(true), Chain: coscfg.Chain{ @@ -156,7 +155,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Bech32Prefix: ptr("wasm"), GasToken: ptr("cosm"), }, - Nodes: cosmos.CosmosNodes{ + Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 2 node 1"), TendermintURL: (*relayutils.URL)(models.MustParseURL("http://localhost:9598").URL()), @@ -259,7 +258,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { initFuncs: []chainlink.CoreRelayerChainInitFunc{ chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ Keystore: keyStore.Cosmos(), - CosmosConfigs: cfg.CosmosConfigs(), + TOMLConfigs: cfg.CosmosConfigs(), EventBroadcaster: pg.NewNullEventBroadcaster(), DB: db, QConfig: cfg.Database()}), @@ -292,7 +291,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { TOMLConfigs: cfg.StarknetConfigs()}), chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ Keystore: keyStore.Cosmos(), - CosmosConfigs: cfg.CosmosConfigs(), + TOMLConfigs: cfg.CosmosConfigs(), EventBroadcaster: pg.NewNullEventBroadcaster(), DB: db, QConfig: cfg.Database(), diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 31251069df5..a159ee7cd06 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/sqlx" pkgcosmos "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-solana/pkg/solana" pkgsolana "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -223,7 +224,7 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML type CosmosFactoryConfig struct { Keystore keystore.Cosmos - cosmos.CosmosConfigs + coscfg.TOMLConfigs EventBroadcaster pg.EventBroadcaster *sqlx.DB pg.QConfig @@ -234,7 +235,7 @@ func (c CosmosFactoryConfig) Validate() error { if c.Keystore == nil { err = errors.Join(err, fmt.Errorf("nil Keystore")) } - if len(c.CosmosConfigs) == 0 { + if len(c.TOMLConfigs) == 0 { err = errors.Join(err, fmt.Errorf("no CosmosConfigs provided")) } if c.EventBroadcaster == nil { @@ -266,7 +267,7 @@ func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConf ) // create one relayer per chain id - for _, chainCfg := range config.CosmosConfigs { + for _, chainCfg := range config.TOMLConfigs { relayID := relay.ID{Network: relay.Cosmos, ChainID: *chainCfg.ChainID} lggr := cosmosLggr.Named(relayID.ChainID) diff --git a/core/services/chainlink/types.go b/core/services/chainlink/types.go index 1233a179617..72cad694167 100644 --- a/core/services/chainlink/types.go +++ b/core/services/chainlink/types.go @@ -1,10 +1,10 @@ package chainlink import ( + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" ) @@ -14,7 +14,7 @@ import ( type GeneralConfig interface { config.AppConfig toml.HasEVMConfigs - CosmosConfigs() cosmos.CosmosConfigs + CosmosConfigs() coscfg.TOMLConfigs SolanaConfigs() solana.TOMLConfigs StarknetConfigs() stkcfg.TOMLConfigs // ConfigTOML returns both the user provided and effective configuration as TOML. diff --git a/core/web/cosmos_chains_controller_test.go b/core/web/cosmos_chains_controller_test.go index 475ef413528..f8dbe4614fa 100644 --- a/core/web/cosmos_chains_controller_test.go +++ b/core/web/cosmos_chains_controller_test.go @@ -13,7 +13,6 @@ import ( coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -76,7 +75,7 @@ Nodes = [] t.Run(tc.name, func(t *testing.T) { t.Parallel() - controller := setupCosmosChainsControllerTestV2(t, &cosmos.CosmosConfig{ + controller := setupCosmosChainsControllerTestV2(t, &coscfg.TOMLConfig{ ChainID: ptr(validId), Enabled: ptr(true), Chain: coscfg.Chain{ @@ -106,7 +105,7 @@ Nodes = [] func Test_CosmosChainsController_Index(t *testing.T) { t.Parallel() - chainA := &cosmos.CosmosConfig{ + chainA := &coscfg.TOMLConfig{ ChainID: ptr("a" + cosmostest.RandomChainID()), Enabled: ptr(true), Chain: coscfg.Chain{ @@ -114,7 +113,7 @@ func Test_CosmosChainsController_Index(t *testing.T) { }, } - chainB := &cosmos.CosmosConfig{ + chainB := &coscfg.TOMLConfig{ ChainID: ptr("b" + cosmostest.RandomChainID()), Enabled: ptr(true), Chain: coscfg.Chain{ @@ -174,7 +173,7 @@ type TestCosmosChainsController struct { client cltest.HTTPClientCleaner } -func setupCosmosChainsControllerTestV2(t *testing.T, cfgs ...*cosmos.CosmosConfig) *TestCosmosChainsController { +func setupCosmosChainsControllerTestV2(t *testing.T, cfgs ...*coscfg.TOMLConfig) *TestCosmosChainsController { for i := range cfgs { cfgs[i].SetDefaults() } diff --git a/core/web/cosmos_transfer_controller.go b/core/web/cosmos_transfer_controller.go index afe0fe16d1e..965f694fc1b 100644 --- a/core/web/cosmos_transfer_controller.go +++ b/core/web/cosmos_transfer_controller.go @@ -1,26 +1,26 @@ package web import ( + "fmt" "net/http" + "slices" sdk "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/gin-gonic/gin" + "github.com/google/uuid" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" + coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/denom" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" cosmosmodels "github.com/smartcontractkit/chainlink/v2/core/store/models/cosmos" "github.com/smartcontractkit/chainlink/v2/core/web/presenters" ) -// maxGasUsedTransfer is an upper bound on how much gas we expect a MsgSend for a single coin to use. -const maxGasUsedTransfer = 100_000 - // CosmosTransfersController can send LINK tokens to another address type CosmosTransfersController struct { App chainlink.Application @@ -28,9 +28,9 @@ type CosmosTransfersController struct { // Create sends native coins from the Chainlink's account to a specified address. func (tc *CosmosTransfersController) Create(c *gin.Context) { - cosmosChains := tc.App.GetRelayers().LegacyCosmosChains() - if cosmosChains == nil { - jsonAPIError(c, http.StatusBadRequest, ErrCosmosNotEnabled) + relayers := tc.App.GetRelayers().List(chainlink.FilterRelayersByType(relay.Cosmos)) + if relayers == nil { + jsonAPIError(c, http.StatusBadRequest, ErrSolanaNotEnabled) return } @@ -43,91 +43,51 @@ func (tc *CosmosTransfersController) Create(c *gin.Context) { jsonAPIError(c, http.StatusBadRequest, errors.New("missing cosmosChainID")) return } - // TODO what about ctx in Get? ctx was used here but not in ETH calls. maybe better to make the interface require ctx and - // put in TODOs in ETH... - chain, err := cosmosChains.Get(tr.CosmosChainID) //cosmosChains.Chain(c.Request.Context(), tr.CosmosChainID) - if errors.Is(err, cosmos.ErrChainIDInvalid) || errors.Is(err, cosmos.ErrChainIDEmpty) { - jsonAPIError(c, http.StatusBadRequest, err) + if tr.FromAddress.Empty() { + jsonAPIError(c, http.StatusUnprocessableEntity, errors.Errorf("withdrawal source address is missing: %v", tr.FromAddress)) return - } else if err != nil { + } + + relayerID := relay.ID{Network: relay.Cosmos, ChainID: tr.CosmosChainID} + relayer, err := relayers.Get(relayerID) + if err != nil { + if errors.Is(err, chainlink.ErrNoSuchRelayer) { + jsonAPIError(c, http.StatusBadRequest, err) + return + } jsonAPIError(c, http.StatusInternalServerError, err) return } - - if tr.FromAddress.Empty() { - jsonAPIError(c, http.StatusUnprocessableEntity, errors.Errorf("withdrawal source address is missing: %v", tr.FromAddress)) + var gasToken string + cfgs := tc.App.GetConfig().CosmosConfigs() + if i := slices.IndexFunc(cfgs, func(config *coscfg.TOMLConfig) bool { return *config.ChainID == tr.CosmosChainID }); i != -1 { + gasToken = cfgs[i].GasToken() + } else { + jsonAPIError(c, http.StatusInternalServerError, fmt.Errorf("no config for chain id: %s", tr.CosmosChainID)) return } - coin, err := denom.ConvertDecCoinToDenom(sdk.NewDecCoinFromDec(tr.Token, tr.Amount), chain.Config().GasToken()) + + //TODO move this inside? + coin, err := denom.ConvertDecCoinToDenom(sdk.NewDecCoinFromDec(tr.Token, tr.Amount), gasToken) if err != nil { - jsonAPIError(c, http.StatusBadRequest, errors.Errorf("unable to convert %s to %s: %v", tr.Token, chain.Config().GasToken(), err)) + jsonAPIError(c, http.StatusBadRequest, errors.Errorf("unable to convert %s to %s: %v", tr.Token, gasToken, err)) return } else if !coin.Amount.IsPositive() { jsonAPIError(c, http.StatusBadRequest, errors.Errorf("amount must be greater than zero: %s", coin.Amount)) return } - txm := chain.TxManager() - - if !tr.AllowHigherAmounts { - var reader client.Reader - reader, err = chain.Reader("") - if err != nil { - jsonAPIError(c, http.StatusInternalServerError, errors.Errorf("chain unreachable: %v", err)) - return - } - gasPrice, err2 := txm.GasPrice() - if err2 != nil { - jsonAPIError(c, http.StatusInternalServerError, errors.Errorf("gas price unavailable: %v", err2)) - return - } - - err = cosmosValidateBalance(reader, gasPrice, tr.FromAddress, coin) - if err != nil { - jsonAPIError(c, http.StatusUnprocessableEntity, errors.Errorf("failed to validate balance: %v", err)) - return - } - } - - sendMsg := bank.NewMsgSend(tr.FromAddress, tr.DestinationAddress, sdk.Coins{coin}) - msgID, err := txm.Enqueue("", sendMsg) + err = relayer.Transact(c, tr.FromAddress.String(), tr.DestinationAddress.String(), coin.Amount.BigInt(), !tr.AllowHigherAmounts) if err != nil { - jsonAPIError(c, http.StatusInternalServerError, errors.Errorf("transaction failed: %v", err)) + jsonAPIError(c, http.StatusInternalServerError, errors.Errorf("failed to send transaction: %v", err)) return } - resource := presenters.NewCosmosMsgResource(msgID, tr.CosmosChainID, "") - msgs, err := txm.GetMsgs(msgID) - if err != nil { - jsonAPIError(c, http.StatusInternalServerError, errors.Errorf("failed to get message %d: %v", msgID, err)) - return - } - if len(msgs) != 1 { - jsonAPIError(c, http.StatusInternalServerError, errors.Errorf("failed to get message %d: %v", msgID, err)) - return - } - msg := msgs[0] - resource.TxHash = msg.TxHash - resource.State = string(msg.State) + resource := presenters.NewCosmosMsgResource("cosmos_transfer_"+uuid.New().String(), tr.CosmosChainID, "") + resource.State = string(db.Unstarted) tc.App.GetAuditLogger().Audit(audit.CosmosTransactionCreated, map[string]interface{}{ "cosmosTransactionResource": resource, }) jsonAPIResponse(c, resource, "cosmos_msg") } - -// cosmosValidateBalance validates that fromAddr's balance can cover coin, including fees at gasPrice. -func cosmosValidateBalance(reader client.Reader, gasPrice sdk.DecCoin, fromAddr sdk.AccAddress, coin sdk.Coin) error { - balance, err := reader.Balance(fromAddr, coin.GetDenom()) - if err != nil { - return err - } - - fee := gasPrice.Amount.MulInt64(maxGasUsedTransfer).RoundInt() - need := coin.Amount.Add(fee) - - if balance.Amount.LT(need) { - return errors.Errorf("balance %q is too low for this transaction to be executed: need %s total, including %s fee", balance, need, fee) - } - return nil -} diff --git a/core/web/presenters/cosmos_msg.go b/core/web/presenters/cosmos_msg.go index d4fbc905bd6..5bf0bb9b4f8 100644 --- a/core/web/presenters/cosmos_msg.go +++ b/core/web/presenters/cosmos_msg.go @@ -15,9 +15,9 @@ func (CosmosMsgResource) GetName() string { } // NewCosmosMsgResource returns a new partial CosmosMsgResource. -func NewCosmosMsgResource(id int64, chainID string, contractID string) CosmosMsgResource { +func NewCosmosMsgResource(id string, chainID string, contractID string) CosmosMsgResource { return CosmosMsgResource{ - JAID: NewJAIDInt64(id), + JAID: NewJAID(id), ChainID: chainID, ContractID: contractID, } diff --git a/go.mod b/go.mod index 14f195d495f..9c14fe8c144 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb diff --git a/go.sum b/go.sum index c9700330fa9..372e705c71e 100644 --- a/go.sum +++ b/go.sum @@ -1457,8 +1457,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 21ce1921267..a0be1dbd8c7 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -383,7 +383,7 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 64305ddc249..9cc918229fd 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2360,8 +2360,8 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47 h1:vdieOW3CZGdD2R5zvCSMS+0vksyExPN3/Fa1uVfld/A= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20230913032705-f924d753cc47/go.mod h1:xMwqRdj5vqYhCJXgKVqvyAwdcqM6ZAEhnwEQ4Khsop8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= From ef59cf4a36e75ca747ac2f4f1ac973bb04c9c072 Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:36:25 -0700 Subject: [PATCH 034/271] Avoid risky early abort before attempting to send a tx in tx mgr (#11016) * Warn instead of error if anything goes wrong with tx hash conflict detection Ignore any other errors from the DELETE, since we're only looking for an edge case--usually there will be no txhash conflict, and if there is we'll abort anyway on the INSERT. In some cases aborting before we even try to send the tx could be hurtful (risking tx mgr to get stuck) and in others it won't matter either way, but in no case would it ever be helpful. * Document error handling * Raise Warn level to Error, so a persistent error in DELETE query doesn't go unnoticed --- core/chains/evm/txmgr/evm_tx_store.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 96963e78d75..cc28c71d785 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1486,20 +1486,23 @@ func (o *evmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx, // Note: the record of the original abandoned transaction will remain in evm.txes, only the attempt is replaced. (Any receipt // associated with the abandoned attempt would also be lost, although this shouldn't happen since only unconfirmed transactions // can be abandoned.) - result, err := tx.Exec(`DELETE FROM evm.tx_attempts a USING evm.txes t + res, err := tx.Exec(`DELETE FROM evm.tx_attempts a USING evm.txes t WHERE t.id = a.eth_tx_id AND a.hash = $1 AND t.state = $2 AND t.error = 'abandoned'`, attempt.Hash, txmgr.TxFatalError, ) - if err == nil { - count, err := result.RowsAffected() - if err != nil { - return pkgerrors.Wrap(err, "UpdateTxUnstartedToInProgress failed to get rows affected") - } - if count > 0 { - o.logger.Debugf("Replacing abandoned tx with tx hash %s with tx_id=%d with identical tx hash", attempt.Hash, attempt.TxID) - } - } else { - return pkgerrors.Wrap(err, "UpdateTxUnstartedToInProgress failed to delete abandoned transactions") + + if err != nil { + // If the DELETE fails, we don't want to abort before at least attempting the INSERT. tx hash conflicts with + // abandoned transactions can only happen after a nonce reset. If the node is operating normally but there is + // some unexpected issue with the DELETE query, blocking the txmgr from sending transactions would be risky + // and could potentially get the node stuck. If the INSERT is going to succeed then we definitely want to continue. + // And even if the INSERT fails, an error message showing the txmgr is having trouble inserting tx's in the db may be + // easier to understand quickly if there is a problem with the node. + o.logger.Errorw("Ignoring unexpected db error while checking for txhash conflict", "err", err) + } else if rows, err := res.RowsAffected(); err != nil { + o.logger.Errorw("Ignoring unexpected db error reading rows affected while checking for txhash conflict", "err", err) + } else if rows > 0 { + o.logger.Debugf("Replacing abandoned tx with tx hash %s with tx_id=%d with identical tx hash", attempt.Hash, attempt.TxID) } var dbAttempt DbEthTxAttempt From 612409932c1fd92d13de7322981639cbc1263df9 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Mon, 30 Oct 2023 15:58:03 -0400 Subject: [PATCH 035/271] Updates to use default keys when available (#11128) --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index a0be1dbd8c7..1263c406aaf 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.0 + github.com/smartcontractkit/chainlink-testing-framework v1.18.1 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 9cc918229fd..08c037dd0ce 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2368,8 +2368,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.0 h1:Ru7odxF0tq0FixJXM58rNZw0PvyQnRroqAInBAM83gs= -github.com/smartcontractkit/chainlink-testing-framework v1.18.0/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.1 h1:YznR7isiPYbywuUma5eVSyuZYwbUHIGJ2lpcJazOZgo= +github.com/smartcontractkit/chainlink-testing-framework v1.18.1/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= From 44bf841b393c12d4d62339e21fa3233919beb750 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Mon, 30 Oct 2023 16:03:23 -0400 Subject: [PATCH 036/271] Manually Trigger Integration Tests (#11129) * First attempt * Enable dispatch --- .github/workflows/integration-tests.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 98e59ce8d49..5074fc35b9a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -8,6 +8,13 @@ on: push: tags: - "*" + workflow_dispatch: + inputs: + liveNetwork: + description: "Run Live Testnet Tests" + required: false + type: boolean + # Only run 1 of this workflow at a time per PR concurrency: @@ -886,7 +893,7 @@ jobs: ### Start Live Testnet Section testnet-smoke-tests-matrix: - if: ${{ github.event_name == 'schedule' || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) }} ## Only run live tests on new tags and nightly + if: ${{ github.event_name == 'schedule' || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) || (github.event_name == 'workflow_dispatch' && inputs.liveNetwork) }} ## Only run live tests on new tags, schedule, or on manual request environment: integration permissions: checks: write From 2534e1a6055b9e409b2a9f0710cc860f0518ec24 Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Tue, 31 Oct 2023 10:40:05 +0100 Subject: [PATCH 037/271] CCIP-1230 Exposing entire LogPollerBlock from LatestBlock in LogPoller (#11105) * Exposing entire LogPollerBlock from LatestBlock function in the LogPoller's interface * Exposing entire LogPollerBlock from LatestBlock function in the LogPoller's interface --- core/chains/evm/logpoller/disabled.go | 4 +- core/chains/evm/logpoller/helper_test.go | 2 +- core/chains/evm/logpoller/log_poller.go | 8 ++-- .../evm/logpoller/log_poller_internal_test.go | 2 +- core/chains/evm/logpoller/log_poller_test.go | 39 ++++++++++++------- core/chains/evm/logpoller/mocks/log_poller.go | 10 ++--- core/services/blockhashstore/coordinators.go | 6 +-- core/services/blockhashstore/delegate.go | 2 +- core/services/blockhashstore/delegate_test.go | 3 +- core/services/blockhashstore/feeder_test.go | 6 +-- .../plugins/ocr2keeper/evm20/log_provider.go | 24 ++++++------ .../ocr2/plugins/ocr2keeper/evm20/registry.go | 10 ++--- .../plugins/ocr2keeper/evm20/registry_test.go | 2 +- .../ocr2keeper/evm21/block_subscriber.go | 7 ++-- .../ocr2keeper/evm21/block_subscriber_test.go | 4 +- .../evm21/logprovider/block_time.go | 5 ++- .../evm21/logprovider/block_time_test.go | 2 +- .../evm21/logprovider/integration_test.go | 4 +- .../ocr2keeper/evm21/logprovider/provider.go | 10 ++--- .../evm21/logprovider/provider_life_cycle.go | 2 +- .../logprovider/provider_life_cycle_test.go | 5 ++- .../evm21/logprovider/provider_test.go | 2 +- .../ocr2keeper/evm21/logprovider/recoverer.go | 8 ++-- .../evm21/logprovider/recoverer_test.go | 11 +++--- .../ocr2/plugins/ocr2keeper/evm21/registry.go | 10 ++--- .../plugins/ocr2keeper/evm21/registry_test.go | 2 +- .../evm21/transmit/event_provider.go | 6 +-- .../evm21/transmit/event_provider_test.go | 2 +- .../ocr2vrf/coordinator/coordinator.go | 2 +- .../ocr2vrf/coordinator/coordinator_test.go | 4 +- core/services/relay/evm/config_poller.go | 2 +- .../relay/evm/functions/config_poller.go | 2 +- .../relay/evm/functions/logpoller_wrapper.go | 15 +++---- .../evm/functions/logpoller_wrapper_test.go | 2 +- .../relay/evm/mercury/config_poller.go | 2 +- 35 files changed, 123 insertions(+), 104 deletions(-) diff --git a/core/chains/evm/logpoller/disabled.go b/core/chains/evm/logpoller/disabled.go index 4bcf1c50863..b54d4e6fc84 100644 --- a/core/chains/evm/logpoller/disabled.go +++ b/core/chains/evm/logpoller/disabled.go @@ -39,7 +39,9 @@ func (disabled) UnregisterFilter(name string, qopts ...pg.QOpt) error { return E func (disabled) HasFilter(name string) bool { return false } -func (disabled) LatestBlock(qopts ...pg.QOpt) (int64, error) { return -1, ErrDisabled } +func (disabled) LatestBlock(qopts ...pg.QOpt) (LogPollerBlock, error) { + return LogPollerBlock{}, ErrDisabled +} func (disabled) GetBlocksRange(ctx context.Context, numbers []uint64, qopts ...pg.QOpt) ([]LogPollerBlock, error) { return nil, ErrDisabled diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index 8415641c402..c61d3d5fad6 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -92,7 +92,7 @@ func SetupTH(t testing.TB, useFinalityTag bool, finalityDepth, backfillBatchSize func (th *TestHarness) PollAndSaveLogs(ctx context.Context, currentBlockNumber int64) int64 { th.LogPoller.PollAndSaveLogs(ctx, currentBlockNumber) latest, _ := th.LogPoller.LatestBlock(pg.WithParentCtx(ctx)) - return latest + 1 + return latest.BlockNumber + 1 } func (th *TestHarness) assertDontHave(t *testing.T, start, end int) { diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 6cda8f5b46c..4cd2804d9f3 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -38,7 +38,7 @@ type LogPoller interface { RegisterFilter(filter Filter, qopts ...pg.QOpt) error UnregisterFilter(name string, qopts ...pg.QOpt) error HasFilter(name string) bool - LatestBlock(qopts ...pg.QOpt) (int64, error) + LatestBlock(qopts ...pg.QOpt) (LogPollerBlock, error) GetBlocksRange(ctx context.Context, numbers []uint64, qopts ...pg.QOpt) ([]LogPollerBlock, error) // General querying @@ -1019,13 +1019,13 @@ func (lp *logPoller) IndexedLogsTopicRange(eventSig common.Hash, address common. // LatestBlock returns the latest block the log poller is on. It tracks blocks to be able // to detect reorgs. -func (lp *logPoller) LatestBlock(qopts ...pg.QOpt) (int64, error) { +func (lp *logPoller) LatestBlock(qopts ...pg.QOpt) (LogPollerBlock, error) { b, err := lp.orm.SelectLatestBlock(qopts...) if err != nil { - return 0, err + return LogPollerBlock{}, err } - return b.BlockNumber, nil + return *b, nil } func (lp *logPoller) BlockByNumber(n int64, qopts ...pg.QOpt) (*LogPollerBlock, error) { diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index b9474158a6b..c0d081582f7 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -262,7 +262,7 @@ func TestLogPoller_Replay(t *testing.T) { lp.PollAndSaveLogs(tctx, 4) latest, err := lp.LatestBlock() require.NoError(t, err) - require.Equal(t, int64(4), latest) + require.Equal(t, int64(4), latest.BlockNumber) t.Run("abort before replayStart received", func(t *testing.T) { // Replay() should abort immediately if caller's context is cancelled before request signal is read diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 1ee8f4dcb78..471c728cdd6 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -311,8 +311,8 @@ func Test_BackupLogPoller(t *testing.T) { body.Transactions = types.Transactions{} // number of tx's must match # of logs for GetLogs() to succeed rawdb.WriteBody(th.EthDB, h.Hash(), h.Number.Uint64(), body) - currentBlock := th.PollAndSaveLogs(ctx, 1) - assert.Equal(t, int64(35), currentBlock) + currentBlockNumber := th.PollAndSaveLogs(ctx, 1) + assert.Equal(t, int64(35), currentBlockNumber) // simulate logs becoming available rawdb.WriteReceipts(th.EthDB, h.Hash(), h.Number.Uint64(), receipts) @@ -342,12 +342,12 @@ func Test_BackupLogPoller(t *testing.T) { markBlockAsFinalized(t, th, 34) // Run ordinary poller + backup poller at least once - currentBlock, _ = th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) - th.LogPoller.PollAndSaveLogs(ctx, currentBlock+1) + currentBlock, _ := th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) + th.LogPoller.PollAndSaveLogs(ctx, currentBlock.BlockNumber+1) th.LogPoller.BackupPollAndSaveLogs(ctx, 100) currentBlock, _ = th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) - require.Equal(t, int64(37), currentBlock+1) + require.Equal(t, int64(37), currentBlock.BlockNumber+1) // logs still shouldn't show up, because we don't want to backfill the last finalized log // to help with reorg detection @@ -359,11 +359,11 @@ func Test_BackupLogPoller(t *testing.T) { markBlockAsFinalized(t, th, 35) // Run ordinary poller + backup poller at least once more - th.LogPoller.PollAndSaveLogs(ctx, currentBlock+1) + th.LogPoller.PollAndSaveLogs(ctx, currentBlockNumber+1) th.LogPoller.BackupPollAndSaveLogs(ctx, 100) currentBlock, _ = th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) - require.Equal(t, int64(38), currentBlock+1) + require.Equal(t, int64(38), currentBlock.BlockNumber+1) // all 3 logs in block 34 should show up now, thanks to backup logger logs, err = th.LogPoller.Logs(30, 37, EmitterABI.Events["Log1"].ID, th.EmitterAddress1, @@ -471,6 +471,13 @@ func TestLogPoller_BackupPollAndSaveLogsWithDeepBlockDelay(t *testing.T) { // 1 -> 2 -> ... th.PollAndSaveLogs(ctx, 1) + // Check that latest block has the same properties as the head + latestBlock, err := th.LogPoller.LatestBlock() + require.NoError(t, err) + assert.Equal(t, latestBlock.BlockNumber, header.Number.Int64()) + assert.Equal(t, latestBlock.FinalizedBlockNumber, header.Number.Int64()) + assert.Equal(t, latestBlock.BlockHash, header.Hash()) + // Register filter err = th.LogPoller.RegisterFilter(logpoller.Filter{ Name: "Test Emitter", @@ -619,7 +626,7 @@ func TestLogPoller_BlockTimestamps(t *testing.T) { require.Len(t, gethLogs, 2) lb, _ := th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) - th.PollAndSaveLogs(context.Background(), lb+1) + th.PollAndSaveLogs(context.Background(), lb.BlockNumber+1) lg1, err := th.LogPoller.Logs(0, 20, EmitterABI.Events["Log1"].ID, th.EmitterAddress1, pg.WithParentCtx(testutils.Context(t))) require.NoError(t, err) @@ -667,9 +674,9 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { for i := 0; i < finalityDepth; i++ { // Have enough blocks that we could reorg the full finalityDepth-1. ec.Commit() } - currentBlock := int64(1) - lp.PollAndSaveLogs(testutils.Context(t), currentBlock) - currentBlock, err = lp.LatestBlock(pg.WithParentCtx(testutils.Context(t))) + currentBlockNumber := int64(1) + lp.PollAndSaveLogs(testutils.Context(t), currentBlockNumber) + currentBlock, err := lp.LatestBlock(pg.WithParentCtx(testutils.Context(t))) require.NoError(t, err) matchesGeth := func() bool { // Check every block is identical @@ -719,7 +726,7 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { require.NoError(t, err1) t.Logf("New latest (%v, %x), latest parent %x)\n", latest.NumberU64(), latest.Hash(), latest.ParentHash()) } - lp.PollAndSaveLogs(testutils.Context(t), currentBlock) + lp.PollAndSaveLogs(testutils.Context(t), currentBlock.BlockNumber) currentBlock, err = lp.LatestBlock(pg.WithParentCtx(testutils.Context(t))) require.NoError(t, err) } @@ -1245,7 +1252,7 @@ func TestGetReplayFromBlock(t *testing.T) { require.NoError(t, err) latest, err := th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) require.NoError(t, err) - assert.Equal(t, latest, fromBlock) + assert.Equal(t, latest.BlockNumber, fromBlock) // Should take min(latest, requested) in this case requested. requested = int64(7) @@ -1551,6 +1558,10 @@ func Test_PollAndSavePersistsFinalityInBlocks(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { th := SetupTH(t, tt.useFinalityTag, tt.finalityDepth, 3, 2, 1000) + // Should return error before the first poll and save + _, err := th.LogPoller.LatestBlock() + require.Error(t, err) + // Mark first block as finalized h := th.Client.Blockchain().CurrentHeader() th.Client.Blockchain().SetFinalized(h) @@ -1562,7 +1573,7 @@ func Test_PollAndSavePersistsFinalityInBlocks(t *testing.T) { th.PollAndSaveLogs(ctx, 1) - latestBlock, err := th.ORM.SelectLatestBlock() + latestBlock, err := th.LogPoller.LatestBlock() require.NoError(t, err) require.Equal(t, int64(numberOfBlocks), latestBlock.BlockNumber) require.Equal(t, tt.expectedFinalizedBlock, latestBlock.FinalizedBlockNumber) diff --git a/core/chains/evm/logpoller/mocks/log_poller.go b/core/chains/evm/logpoller/mocks/log_poller.go index f4357341646..01be5f7ba55 100644 --- a/core/chains/evm/logpoller/mocks/log_poller.go +++ b/core/chains/evm/logpoller/mocks/log_poller.go @@ -330,7 +330,7 @@ func (_m *LogPoller) IndexedLogsWithSigsExcluding(address common.Address, eventS } // LatestBlock provides a mock function with given fields: qopts -func (_m *LogPoller) LatestBlock(qopts ...pg.QOpt) (int64, error) { +func (_m *LogPoller) LatestBlock(qopts ...pg.QOpt) (logpoller.LogPollerBlock, error) { _va := make([]interface{}, len(qopts)) for _i := range qopts { _va[_i] = qopts[_i] @@ -339,15 +339,15 @@ func (_m *LogPoller) LatestBlock(qopts ...pg.QOpt) (int64, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) - var r0 int64 + var r0 logpoller.LogPollerBlock var r1 error - if rf, ok := ret.Get(0).(func(...pg.QOpt) (int64, error)); ok { + if rf, ok := ret.Get(0).(func(...pg.QOpt) (logpoller.LogPollerBlock, error)); ok { return rf(qopts...) } - if rf, ok := ret.Get(0).(func(...pg.QOpt) int64); ok { + if rf, ok := ret.Get(0).(func(...pg.QOpt) logpoller.LogPollerBlock); ok { r0 = rf(qopts...) } else { - r0 = ret.Get(0).(int64) + r0 = ret.Get(0).(logpoller.LogPollerBlock) } if rf, ok := ret.Get(1).(func(...pg.QOpt) error); ok { diff --git a/core/services/blockhashstore/coordinators.go b/core/services/blockhashstore/coordinators.go index ff5aff1f5e5..4cb58bab6fd 100644 --- a/core/services/blockhashstore/coordinators.go +++ b/core/services/blockhashstore/coordinators.go @@ -128,7 +128,7 @@ func (v *V1Coordinator) Fulfillments(ctx context.Context, fromBlock uint64) ([]E logs, err := v.lp.LogsWithSigs( int64(fromBlock), - int64(toBlock), + toBlock.BlockNumber, []common.Hash{ v1.VRFCoordinatorRandomnessRequestFulfilled{}.Topic(), }, @@ -219,7 +219,7 @@ func (v *V2Coordinator) Fulfillments(ctx context.Context, fromBlock uint64) ([]E logs, err := v.lp.LogsWithSigs( int64(fromBlock), - int64(toBlock), + toBlock.BlockNumber, []common.Hash{ v2.VRFCoordinatorV2RandomWordsFulfilled{}.Topic(), }, @@ -310,7 +310,7 @@ func (v *V2PlusCoordinator) Fulfillments(ctx context.Context, fromBlock uint64) logs, err := v.lp.LogsWithSigs( int64(fromBlock), - int64(toBlock), + toBlock.BlockNumber, []common.Hash{ v2plus.IVRFCoordinatorV2PlusInternalRandomWordsFulfilled{}.Topic(), }, diff --git a/core/services/blockhashstore/delegate.go b/core/services/blockhashstore/delegate.go index 123052550ba..c8e55e47c3c 100644 --- a/core/services/blockhashstore/delegate.go +++ b/core/services/blockhashstore/delegate.go @@ -173,7 +173,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { if err != nil { return 0, errors.Wrap(err, "getting chain head") } - return uint64(head), nil + return uint64(head.BlockNumber), nil }) return []job.ServiceCtx{&service{ diff --git a/core/services/blockhashstore/delegate_test.go b/core/services/blockhashstore/delegate_test.go index 089e9544af5..011ab87ad6b 100644 --- a/core/services/blockhashstore/delegate_test.go +++ b/core/services/blockhashstore/delegate_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -58,7 +59,7 @@ func createTestDelegate(t *testing.T) (*blockhashstore.Delegate, *testData) { sendingKey, _ := cltest.MustInsertRandomKey(t, kst) lp := &mocklp.LogPoller{} lp.On("RegisterFilter", mock.Anything).Return(nil) - lp.On("LatestBlock", mock.Anything, mock.Anything).Return(int64(0), nil) + lp.On("LatestBlock", mock.Anything, mock.Anything).Return(logpoller.LogPollerBlock{}, nil) relayExtenders := evmtest.NewChainRelayExtenders( t, diff --git a/core/services/blockhashstore/feeder_test.go b/core/services/blockhashstore/feeder_test.go index 3145a9fd76d..8d9ed48c4bf 100644 --- a/core/services/blockhashstore/feeder_test.go +++ b/core/services/blockhashstore/feeder_test.go @@ -445,7 +445,7 @@ func (test testCase) testFeederWithLogPollerVRFv1(t *testing.T) { // Mock log poller. lp.On("LatestBlock", mock.Anything). - Return(latest, nil) + Return(logpoller.LogPollerBlock{BlockNumber: latest}, nil) lp.On( "LogsWithSigs", fromBlock, @@ -543,7 +543,7 @@ func (test testCase) testFeederWithLogPollerVRFv2(t *testing.T) { // Mock log poller. lp.On("LatestBlock", mock.Anything). - Return(latest, nil) + Return(logpoller.LogPollerBlock{BlockNumber: latest}, nil) lp.On( "LogsWithSigs", fromBlock, @@ -641,7 +641,7 @@ func (test testCase) testFeederWithLogPollerVRFv2Plus(t *testing.T) { // Mock log poller. lp.On("LatestBlock", mock.Anything). - Return(latest, nil) + Return(logpoller.LogPollerBlock{BlockNumber: latest}, nil) lp.On( "LogsWithSigs", fromBlock, diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go index 856e508fc57..4044bb5f2a4 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go @@ -151,8 +151,8 @@ func (c *LogProvider) PerformLogs(ctx context.Context) ([]ocr2keepers.PerformLog // always check the last lookback number of blocks and rebroadcast // this allows the plugin to make decisions based on event confirmations logs, err := c.logPoller.LogsWithSigs( - end-c.lookbackBlocks, - end, + end.BlockNumber-c.lookbackBlocks, + end.BlockNumber, []common.Hash{ registry.KeeperRegistryUpkeepPerformed{}.Topic(), }, @@ -175,7 +175,7 @@ func (c *LogProvider) PerformLogs(ctx context.Context) ([]ocr2keepers.PerformLog Key: UpkeepKeyHelper[uint32]{}.MakeUpkeepKey(p.CheckBlockNumber, p.Id), TransmitBlock: BlockKeyHelper[int64]{}.MakeBlockKey(p.BlockNumber), TransactionHash: p.TxHash.Hex(), - Confirmations: end - p.BlockNumber, + Confirmations: end.BlockNumber - p.BlockNumber, } vals = append(vals, l) } @@ -194,8 +194,8 @@ func (c *LogProvider) StaleReportLogs(ctx context.Context) ([]ocr2keepers.StaleR // ReorgedUpkeepReportLogs logs, err := c.logPoller.LogsWithSigs( - end-c.lookbackBlocks, - end, + end.BlockNumber-c.lookbackBlocks, + end.BlockNumber, []common.Hash{ registry.KeeperRegistryReorgedUpkeepReport{}.Topic(), }, @@ -212,8 +212,8 @@ func (c *LogProvider) StaleReportLogs(ctx context.Context) ([]ocr2keepers.StaleR // StaleUpkeepReportLogs logs, err = c.logPoller.LogsWithSigs( - end-c.lookbackBlocks, - end, + end.BlockNumber-c.lookbackBlocks, + end.BlockNumber, []common.Hash{ registry.KeeperRegistryStaleUpkeepReport{}.Topic(), }, @@ -230,8 +230,8 @@ func (c *LogProvider) StaleReportLogs(ctx context.Context) ([]ocr2keepers.StaleR // InsufficientFundsUpkeepReportLogs logs, err = c.logPoller.LogsWithSigs( - end-c.lookbackBlocks, - end, + end.BlockNumber-c.lookbackBlocks, + end.BlockNumber, []common.Hash{ registry.KeeperRegistryInsufficientFundsUpkeepReport{}.Topic(), }, @@ -258,7 +258,7 @@ func (c *LogProvider) StaleReportLogs(ctx context.Context) ([]ocr2keepers.StaleR Key: encoding.BasicEncoder{}.MakeUpkeepKey(checkBlockNumber, upkeepId), TransmitBlock: BlockKeyHelper[int64]{}.MakeBlockKey(r.BlockNumber), TransactionHash: r.TxHash.Hex(), - Confirmations: end - r.BlockNumber, + Confirmations: end.BlockNumber - r.BlockNumber, } vals = append(vals, l) } @@ -273,7 +273,7 @@ func (c *LogProvider) StaleReportLogs(ctx context.Context) ([]ocr2keepers.StaleR Key: encoding.BasicEncoder{}.MakeUpkeepKey(checkBlockNumber, upkeepId), TransmitBlock: BlockKeyHelper[int64]{}.MakeBlockKey(r.BlockNumber), TransactionHash: r.TxHash.Hex(), - Confirmations: end - r.BlockNumber, + Confirmations: end.BlockNumber - r.BlockNumber, } vals = append(vals, l) } @@ -288,7 +288,7 @@ func (c *LogProvider) StaleReportLogs(ctx context.Context) ([]ocr2keepers.StaleR Key: encoding.BasicEncoder{}.MakeUpkeepKey(checkBlockNumber, upkeepId), TransmitBlock: BlockKeyHelper[int64]{}.MakeBlockKey(r.BlockNumber), TransactionHash: r.TxHash.Hex(), - Confirmations: end - r.BlockNumber, + Confirmations: end.BlockNumber - r.BlockNumber, } vals = append(vals, l) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go index 49cab7b5a48..2d49a91e98f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go @@ -347,7 +347,7 @@ func (r *EvmRegistry) initialize() error { func (r *EvmRegistry) pollLogs() error { var latest int64 - var end int64 + var end logpoller.LogPollerBlock var err error if end, err = r.poller.LatestBlock(pg.WithParentCtx(r.ctx)); err != nil { @@ -356,11 +356,11 @@ func (r *EvmRegistry) pollLogs() error { r.mu.Lock() latest = r.lastPollBlock - r.lastPollBlock = end + r.lastPollBlock = end.BlockNumber r.mu.Unlock() // if start and end are the same, no polling needs to be done - if latest == 0 || latest == end { + if latest == 0 || latest == end.BlockNumber { return nil } @@ -368,8 +368,8 @@ func (r *EvmRegistry) pollLogs() error { var logs []logpoller.Log if logs, err = r.poller.LogsWithSigs( - end-logEventLookback, - end, + end.BlockNumber-logEventLookback, + end.BlockNumber, upkeepStateEvents, r.addr, pg.WithParentCtx(r.ctx), diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go index 348b5a47c0f..8662bfd0475 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go @@ -189,7 +189,7 @@ func TestPollLogs(t *testing.T) { if test.LatestBlock != nil { mp.On("LatestBlock", mock.Anything). - Return(test.LatestBlock.OutputBlock, test.LatestBlock.OutputErr) + Return(logpoller.LogPollerBlock{BlockNumber: test.LatestBlock.OutputBlock}, test.LatestBlock.OutputErr) } if test.LogsWithSigs != nil { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go index 2d524e6f6cd..d97156ed180 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go @@ -79,12 +79,13 @@ func (bs *BlockSubscriber) getBlockRange(ctx context.Context) ([]uint64, error) if err != nil { return nil, err } - bs.lggr.Infof("latest block from log poller is %d", h) + latestBlockNumber := h.BlockNumber + bs.lggr.Infof("latest block from log poller is %d", latestBlockNumber) var blocks []uint64 for i := bs.blockSize - 1; i >= 0; i-- { - if h-i > 0 { - blocks = append(blocks, uint64(h-i)) + if latestBlockNumber-i > 0 { + blocks = append(blocks, uint64(latestBlockNumber-i)) } } return blocks, nil diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go index 618ea83d4e9..004b5fac6cc 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go @@ -97,7 +97,7 @@ func TestBlockSubscriber_GetBlockRange(t *testing.T) { for _, tc := range tests { t.Run(tc.Name, func(t *testing.T) { lp := new(mocks.LogPoller) - lp.On("LatestBlock", mock.Anything).Return(tc.LatestBlock, tc.LatestBlockErr) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: tc.LatestBlock}, tc.LatestBlockErr) bs := NewBlockSubscriber(hb, lp, finality, lggr) bs.blockHistorySize = historySize bs.blockSize = blockSize @@ -278,7 +278,7 @@ func TestBlockSubscriber_Start(t *testing.T) { hb := commonmocks.NewHeadBroadcaster[*evmtypes.Head, common.Hash](t) hb.On("Subscribe", mock.Anything).Return(&evmtypes.Head{Number: 42}, func() {}) lp := new(mocks.LogPoller) - lp.On("LatestBlock", mock.Anything).Return(int64(100), nil) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: 100}, nil) blocks := []uint64{97, 98, 99, 100} pollerBlocks := []logpoller.LogPollerBlock{ { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time.go index 9fc35dd84be..814ed29d900 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time.go @@ -34,10 +34,11 @@ func (r *blockTimeResolver) BlockTime(ctx context.Context, blockSampleSize int64 if err != nil { return 0, fmt.Errorf("failed to get latest block from poller: %w", err) } - if latest <= blockSampleSize { + latestBlockNumber := latest.BlockNumber + if latestBlockNumber <= blockSampleSize { return defaultBlockTime, nil } - start, end := latest-blockSampleSize, latest + start, end := latestBlockNumber-blockSampleSize, latestBlockNumber startTime, endTime, err := r.getSampleTimestamps(ctx, uint64(start), uint64(end)) if err != nil { return 0, err diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go index 0ad9990e185..7009cfaa9b2 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go @@ -69,7 +69,7 @@ func TestBlockTimeResolver_BlockTime(t *testing.T) { lp := new(lpmocks.LogPoller) resolver := newBlockTimeResolver(lp) - lp.On("LatestBlock", mock.Anything).Return(tc.latestBlock, tc.latestBlockErr) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: tc.latestBlock}, tc.latestBlockErr) lp.On("GetBlocksRange", mock.Anything, mock.Anything).Return(tc.blocksRange, tc.blocksRangeErr) blockTime, err := resolver.BlockTime(ctx, tc.blockSampleSize) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index 811468746e3..dad35420398 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -317,7 +317,7 @@ func TestIntegration_LogEventProvider_RateLimit(t *testing.T) { var minimumBlockCount int64 = 500 latestBlock, _ := lp.LatestBlock() - assert.GreaterOrEqual(t, latestBlock, minimumBlockCount, "to ensure the integrety of the test, the minimum block count before the test should be %d but got %d", minimumBlockCount, latestBlock) + assert.GreaterOrEqual(t, latestBlock.BlockNumber, minimumBlockCount, "to ensure the integrety of the test, the minimum block count before the test should be %d but got %d", minimumBlockCount, latestBlock) } require.NoError(t, logProvider.ReadLogs(ctx, ids...)) @@ -564,7 +564,7 @@ func waitLogPoller(ctx context.Context, t *testing.T, backend *backends.Simulate for { latestPolled, lberr := lp.LatestBlock(pg.WithParentCtx(ctx)) require.NoError(t, lberr) - if latestPolled >= latestBlock { + if latestPolled.BlockNumber >= latestBlock { break } lp.PollAndSaveLogs(ctx, latestBlock) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go index 729bf4ade5f..349db2902b6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go @@ -161,11 +161,11 @@ func (p *logEventProvider) GetLatestPayloads(ctx context.Context) ([]ocr2keepers if err != nil { return nil, fmt.Errorf("%w: %s", ErrHeadNotAvailable, err) } - start := latest - p.opts.LookbackBlocks + start := latest.BlockNumber - p.opts.LookbackBlocks if start <= 0 { start = 1 } - logs := p.buffer.dequeueRange(start, latest, AllowedLogsPerUpkeep, MaxPayloads) + logs := p.buffer.dequeueRange(start, latest.BlockNumber, AllowedLogsPerUpkeep, MaxPayloads) // p.lggr.Debugw("got latest logs from buffer", "latest", latest, "diff", diff, "logs", len(logs)) @@ -199,12 +199,12 @@ func (p *logEventProvider) ReadLogs(pctx context.Context, ids ...*big.Int) error if err != nil { return fmt.Errorf("%w: %s", ErrHeadNotAvailable, err) } - if latest == 0 { + if latest.BlockNumber == 0 { return fmt.Errorf("%w: %s", ErrHeadNotAvailable, "latest block is 0") } - filters := p.getFilters(latest, ids...) + filters := p.getFilters(latest.BlockNumber, ids...) - err = p.readLogs(ctx, latest, filters) + err = p.readLogs(ctx, latest.BlockNumber, filters) p.updateFiltersLastPoll(filters) // p.lggr.Debugw("read logs for entries", "latestBlock", latest, "entries", len(entries), "err", err) if err != nil { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go index ab816adb1b3..69a4872351d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go @@ -128,7 +128,7 @@ func (p *logEventProvider) register(ctx context.Context, lpFilter logpoller.Filt // already registered in DB before, no need to backfill return nil } - backfillBlock := latest - int64(LogBackfillBuffer) + backfillBlock := latest.BlockNumber - int64(LogBackfillBuffer) if backfillBlock < 1 { // New chain, backfill from start backfillBlock = 1 diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go index 4b1ff06f316..03395cb5b5f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" @@ -109,7 +110,7 @@ func TestLogEventProvider_LifeCycle(t *testing.T) { lp := new(mocks.LogPoller) lp.On("RegisterFilter", mock.Anything).Return(nil) lp.On("UnregisterFilter", mock.Anything).Return(nil) - lp.On("LatestBlock", mock.Anything).Return(int64(0), nil) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{}, nil) hasFitlerTimes := 1 if tc.unregister { hasFitlerTimes = 2 @@ -149,7 +150,7 @@ func TestEventLogProvider_RefreshActiveUpkeeps(t *testing.T) { mp.On("RegisterFilter", mock.Anything).Return(nil) mp.On("UnregisterFilter", mock.Anything).Return(nil) mp.On("HasFilter", mock.Anything).Return(false) - mp.On("LatestBlock", mock.Anything).Return(int64(0), nil) + mp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{}, nil) mp.On("ReplayAsync", mock.Anything).Return(nil) p := NewLogProvider(logger.TestLogger(t), mp, &mockedPacker{}, NewUpkeepFilterStore(), NewOptions(200)) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go index db22886cbb7..a8e33ba23b7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go @@ -248,7 +248,7 @@ func TestLogEventProvider_ReadLogs(t *testing.T) { mp.On("ReplayAsync", mock.Anything).Return() mp.On("HasFilter", mock.Anything).Return(false) mp.On("UnregisterFilter", mock.Anything, mock.Anything).Return(nil) - mp.On("LatestBlock", mock.Anything).Return(int64(1), nil) + mp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: int64(1)}, nil) mp.On("LogsWithSigs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]logpoller.Log{ { BlockNumber: 1, diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go index b74160ae91c..d6e7ad51d13 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go @@ -208,7 +208,7 @@ func (r *logRecoverer) getLogTriggerCheckData(ctx context.Context, proposal ocr2 return nil, err } - start, offsetBlock := r.getRecoveryWindow(latest) + start, offsetBlock := r.getRecoveryWindow(latest.BlockNumber) if proposal.Trigger.LogTriggerExtension == nil { return nil, errors.New("missing log trigger extension") } @@ -297,7 +297,7 @@ func (r *logRecoverer) GetRecoveryProposals(ctx context.Context) ([]ocr2keepers. allLogsCounter := 0 logsCount := map[string]int{} - r.sortPending(uint64(latestBlock)) + r.sortPending(uint64(latestBlock.BlockNumber)) var results, pending []ocr2keepers.UpkeepPayload for _, payload := range r.pending { @@ -330,7 +330,7 @@ func (r *logRecoverer) recover(ctx context.Context) error { return fmt.Errorf("%w: %s", ErrHeadNotAvailable, err) } - start, offsetBlock := r.getRecoveryWindow(latest) + start, offsetBlock := r.getRecoveryWindow(latest.BlockNumber) if offsetBlock < 0 { // too soon to recover, we don't have enough blocks return nil @@ -611,7 +611,7 @@ func (r *logRecoverer) tryExpire(ctx context.Context, ids ...string) error { return fmt.Errorf("failed to get states: %w", err) } lggr := r.lggr.With("where", "clean") - start, _ := r.getRecoveryWindow(latestBlock) + start, _ := r.getRecoveryWindow(latestBlock.BlockNumber) r.lock.Lock() defer r.lock.Unlock() var removed int diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go index 2fdf04f76c7..c882a22bc1a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go @@ -32,7 +32,7 @@ func TestLogRecoverer_GetRecoverables(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() lp := &lpmocks.LogPoller{} - lp.On("LatestBlock", mock.Anything).Return(int64(100), nil) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: 100}, nil) r := NewLogRecoverer(logger.TestLogger(t), lp, nil, nil, nil, nil, NewOptions(200)) tests := []struct { @@ -182,7 +182,7 @@ func TestLogRecoverer_Clean(t *testing.T) { start, _ := r.getRecoveryWindow(0) block24h := int64(math.Abs(float64(start))) - lp.On("LatestBlock", mock.Anything).Return(block24h+oldLogsOffset, nil) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: block24h + oldLogsOffset}, nil) statesReader.On("SelectByWorkIDs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.states, nil) r.lock.Lock() @@ -423,7 +423,7 @@ func TestLogRecoverer_Recover(t *testing.T) { recoverer, filterStore, lp, statesReader := setupTestRecoverer(t, time.Millisecond*50, lookbackBlocks) filterStore.AddActiveUpkeeps(tc.active...) - lp.On("LatestBlock", mock.Anything).Return(tc.latestBlock, tc.latestBlockErr) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: tc.latestBlock}, tc.latestBlockErr) lp.On("LogsWithSigs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.logs, tc.logsErr) statesReader.On("SelectByWorkIDs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.states, tc.statesErr) @@ -1206,8 +1206,9 @@ type mockLogPoller struct { func (p *mockLogPoller) LogsWithSigs(start, end int64, eventSigs []common.Hash, address common.Address, qopts ...pg.QOpt) ([]logpoller.Log, error) { return p.LogsWithSigsFn(start, end, eventSigs, address, qopts...) } -func (p *mockLogPoller) LatestBlock(qopts ...pg.QOpt) (int64, error) { - return p.LatestBlockFn(qopts...) +func (p *mockLogPoller) LatestBlock(qopts ...pg.QOpt) (logpoller.LogPollerBlock, error) { + block, err := p.LatestBlockFn(qopts...) + return logpoller.LogPollerBlock{BlockNumber: block}, err } type mockClient struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 5d180c05b80..0ca20477f20 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -362,7 +362,7 @@ func (r *EvmRegistry) refreshLogTriggerUpkeepsBatch(logTriggerIDs []*big.Int) er func (r *EvmRegistry) pollUpkeepStateLogs() error { var latest int64 - var end int64 + var end logpoller.LogPollerBlock var err error if end, err = r.poller.LatestBlock(pg.WithParentCtx(r.ctx)); err != nil { @@ -371,18 +371,18 @@ func (r *EvmRegistry) pollUpkeepStateLogs() error { r.mu.Lock() latest = r.lastPollBlock - r.lastPollBlock = end + r.lastPollBlock = end.BlockNumber r.mu.Unlock() // if start and end are the same, no polling needs to be done - if latest == 0 || latest == end { + if latest == 0 || latest == end.BlockNumber { return nil } var logs []logpoller.Log if logs, err = r.poller.LogsWithSigs( - end-logEventLookback, - end, + end.BlockNumber-logEventLookback, + end.BlockNumber, upkeepStateEvents, r.addr, pg.WithParentCtx(r.ctx), diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go index 0cd5ecd2592..4be0ccce4e9 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go @@ -145,7 +145,7 @@ func TestPollLogs(t *testing.T) { if test.LatestBlock != nil { mp.On("LatestBlock", mock.Anything). - Return(test.LatestBlock.OutputBlock, test.LatestBlock.OutputErr) + Return(logpoller.LogPollerBlock{BlockNumber: test.LatestBlock.OutputBlock}, test.LatestBlock.OutputErr) } if test.LogsWithSigs != nil { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go index 5fd320df8ed..8f84ca1495c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go @@ -141,8 +141,8 @@ func (c *EventProvider) GetLatestEvents(ctx context.Context) ([]ocr2keepers.Tran // always check the last lookback number of blocks and rebroadcast // this allows the plugin to make decisions based on event confirmations logs, err := c.logPoller.LogsWithSigs( - end-c.lookbackBlocks, - end, + end.BlockNumber-c.lookbackBlocks, + end.BlockNumber, []common.Hash{ iregistry21.IKeeperRegistryMasterUpkeepPerformed{}.Topic(), iregistry21.IKeeperRegistryMasterStaleUpkeepReport{}.Topic(), @@ -156,7 +156,7 @@ func (c *EventProvider) GetLatestEvents(ctx context.Context) ([]ocr2keepers.Tran return nil, fmt.Errorf("%w: failed to collect logs from log poller", err) } - return c.processLogs(end, logs...) + return c.processLogs(end.BlockNumber, logs...) } // processLogs will parse the unseen logs and return the corresponding transmit events. diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go index 72f3b63088d..58e95bc423e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go @@ -89,7 +89,7 @@ func TestTransmitEventProvider_Sanity(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - lp.On("LatestBlock", mock.Anything).Return(tc.latestBlock, nil) + lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: tc.latestBlock}, nil) lp.On("LogsWithSigs", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tc.logs, nil) res, err := provider.GetLatestEvents(ctx) diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go index e51b68f415d..1b58a017322 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go @@ -227,7 +227,7 @@ func (c *coordinator) CurrentChainHeight(ctx context.Context) (uint64, error) { if err != nil { return 0, err } - return uint64(head), nil + return uint64(head.BlockNumber), nil } // ReportIsOnchain returns true iff a report for the given OCR epoch/round is diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go index 26d0f2996a9..dc489b4958a 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go @@ -1032,7 +1032,7 @@ func TestCoordinator_ReportBlocks(t *testing.T) { requestedBlocks := []uint64{195, 196} lp := lp_mocks.NewLogPoller(t) lp.On("LatestBlock", mock.Anything). - Return(int64(latestHeadNumber), nil) + Return(logpoller.LogPollerBlock{BlockNumber: int64(latestHeadNumber)}, nil) lp.On("GetBlocksRange", mock.Anything, append(requestedBlocks, uint64(latestHeadNumber-lookbackBlocks+1), uint64(latestHeadNumber)), mock.Anything). Return(nil, errors.New("GetBlocks error")) @@ -1720,7 +1720,7 @@ func getLogPoller( lp := lp_mocks.NewLogPoller(t) if needsLatestBlock { lp.On("LatestBlock", mock.Anything). - Return(int64(latestHeadNumber), nil) + Return(logpoller.LogPollerBlock{BlockNumber: int64(latestHeadNumber)}, nil) } var logPollerBlocks []logpoller.LogPollerBlock diff --git a/core/services/relay/evm/config_poller.go b/core/services/relay/evm/config_poller.go index 1cf2318b296..daccf400ea7 100644 --- a/core/services/relay/evm/config_poller.go +++ b/core/services/relay/evm/config_poller.go @@ -212,7 +212,7 @@ func (cp *configPoller) LatestBlockHeight(ctx context.Context) (blockHeight uint } return 0, err } - return uint64(latest), nil + return uint64(latest.BlockNumber), nil } func (cp *configPoller) isConfigStoreAvailable() bool { diff --git a/core/services/relay/evm/functions/config_poller.go b/core/services/relay/evm/functions/config_poller.go index f068f13cc77..7a59d499898 100644 --- a/core/services/relay/evm/functions/config_poller.go +++ b/core/services/relay/evm/functions/config_poller.go @@ -181,7 +181,7 @@ func (cp *configPoller) LatestBlockHeight(ctx context.Context) (blockHeight uint } return 0, err } - return uint64(latest), nil + return uint64(latest.BlockNumber), nil } // called from LogPollerWrapper in a separate goroutine diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index 777717d01c7..d355bd6569b 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -77,7 +77,7 @@ func (l *logPollerWrapper) Start(context.Context) error { l.lggr.Errorw("LogPollerWrapper: LatestBlock() failed, starting from 0", "error", err) } else { l.lggr.Debugw("LogPollerWrapper: LatestBlock() got starting block", "block", nextBlock) - l.nextBlock = nextBlock - l.blockOffset + l.nextBlock = nextBlock.BlockNumber - l.blockOffset } l.closeWait.Add(1) go l.checkForRouteUpdates() @@ -123,9 +123,10 @@ func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmR l.mu.Unlock() return nil, nil, err } - latest -= l.blockOffset - if latest >= nextBlock { - l.nextBlock = latest + 1 + latestBlockNumber := latest.BlockNumber + latestBlockNumber -= l.blockOffset + if latestBlockNumber >= nextBlock { + l.nextBlock = latestBlockNumber + 1 } l.mu.Unlock() @@ -136,18 +137,18 @@ func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmR l.lggr.Debug("LatestEvents: no non-zero coordinators to check") return resultsReq, resultsResp, errors.New("no non-zero coordinators to check") } - if latest < nextBlock { + if latestBlockNumber < nextBlock { l.lggr.Debugw("LatestEvents: no new blocks to check", "latest", latest, "nextBlock", nextBlock) return resultsReq, resultsResp, nil } for _, coordinator := range coordinators { - requestLogs, err := l.logPoller.Logs(nextBlock, latest, functions_coordinator.FunctionsCoordinatorOracleRequest{}.Topic(), coordinator) + requestLogs, err := l.logPoller.Logs(nextBlock, latestBlockNumber, functions_coordinator.FunctionsCoordinatorOracleRequest{}.Topic(), coordinator) if err != nil { l.lggr.Errorw("LatestEvents: fetching request logs from LogPoller failed", "latest", latest, "nextBlock", nextBlock) return nil, nil, err } - responseLogs, err := l.logPoller.Logs(nextBlock, latest, functions_coordinator.FunctionsCoordinatorOracleResponse{}.Topic(), coordinator) + responseLogs, err := l.logPoller.Logs(nextBlock, latestBlockNumber, functions_coordinator.FunctionsCoordinatorOracleResponse{}.Topic(), coordinator) if err != nil { l.lggr.Errorw("LatestEvents: fetching response logs from LogPoller failed", "latest", latest, "nextBlock", nextBlock) return nil, nil, err diff --git a/core/services/relay/evm/functions/logpoller_wrapper_test.go b/core/services/relay/evm/functions/logpoller_wrapper_test.go index 224aa51a5da..c91c3c49aad 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper_test.go +++ b/core/services/relay/evm/functions/logpoller_wrapper_test.go @@ -60,7 +60,7 @@ func setUp(t *testing.T, updateFrequencySec uint32) (*lpmocks.LogPoller, types.L lpWrapper, err := functions.NewLogPollerWrapper(gethcommon.Address{}, config, client, lp, lggr) require.NoError(t, err) - lp.On("LatestBlock").Return(int64(100), nil) + lp.On("LatestBlock").Return(logpoller.LogPollerBlock{BlockNumber: int64(100)}, nil) return lp, lpWrapper, client } diff --git a/core/services/relay/evm/mercury/config_poller.go b/core/services/relay/evm/mercury/config_poller.go index 2f16157bfac..8964a283049 100644 --- a/core/services/relay/evm/mercury/config_poller.go +++ b/core/services/relay/evm/mercury/config_poller.go @@ -188,7 +188,7 @@ func (cp *ConfigPoller) LatestBlockHeight(ctx context.Context) (blockHeight uint } return 0, err } - return uint64(latest), nil + return uint64(latest.BlockNumber), nil } func (cp *ConfigPoller) startLogSubscription() { From 943a1ade98bcc6cc134aa332074228257c72b141 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 31 Oct 2023 07:07:10 -0400 Subject: [PATCH 038/271] fix/lint (#11131) --- core/chains/evm/txmgr/evm_tx_store.go | 8 ++++---- core/utils/utils.go | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index cc28c71d785..86a06b60250 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1486,19 +1486,19 @@ func (o *evmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx, // Note: the record of the original abandoned transaction will remain in evm.txes, only the attempt is replaced. (Any receipt // associated with the abandoned attempt would also be lost, although this shouldn't happen since only unconfirmed transactions // can be abandoned.) - res, err := tx.Exec(`DELETE FROM evm.tx_attempts a USING evm.txes t + res, err2 := tx.Exec(`DELETE FROM evm.tx_attempts a USING evm.txes t WHERE t.id = a.eth_tx_id AND a.hash = $1 AND t.state = $2 AND t.error = 'abandoned'`, attempt.Hash, txmgr.TxFatalError, ) - if err != nil { + if err2 != nil { // If the DELETE fails, we don't want to abort before at least attempting the INSERT. tx hash conflicts with // abandoned transactions can only happen after a nonce reset. If the node is operating normally but there is // some unexpected issue with the DELETE query, blocking the txmgr from sending transactions would be risky // and could potentially get the node stuck. If the INSERT is going to succeed then we definitely want to continue. // And even if the INSERT fails, an error message showing the txmgr is having trouble inserting tx's in the db may be // easier to understand quickly if there is a problem with the node. - o.logger.Errorw("Ignoring unexpected db error while checking for txhash conflict", "err", err) + o.logger.Errorw("Ignoring unexpected db error while checking for txhash conflict", "err", err2) } else if rows, err := res.RowsAffected(); err != nil { o.logger.Errorw("Ignoring unexpected db error reading rows affected while checking for txhash conflict", "err", err) } else if rows > 0 { @@ -1511,7 +1511,7 @@ func (o *evmTxStore) UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx, if e != nil { return pkgerrors.Wrap(e, "failed to BindNamed") } - err = tx.Get(&dbAttempt, query, args...) + err := tx.Get(&dbAttempt, query, args...) if err != nil { var pqErr *pgconn.PgError if isPqErr := errors.As(err, &pqErr); isPqErr && diff --git a/core/utils/utils.go b/core/utils/utils.go index b8515f3362a..e5541ecf558 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -40,9 +40,6 @@ const ( DefaultSecretSize = 48 // EVMWordByteLen the length of an EVM Word Byte EVMWordByteLen = 32 - - // defaultErrorBufferCap is the default cap on the errors an error buffer can store at any time - defaultErrorBufferCap = 50 ) // ZeroAddress is an address of all zeroes, otherwise in Ethereum as From 14804a959975cf53aa52ab95f5f0438046a9cc42 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 31 Oct 2023 12:45:34 +0100 Subject: [PATCH 039/271] bump version without changes (#11118) --- contracts/scripts/native_solc_compile_all_shared | 2 +- .../src/v0.8/automation/upkeeps/ERC20BalanceMonitor.sol | 4 ++-- .../automation/upkeeps/LinkAvailableBalanceMonitor.sol | 6 +++--- .../src/v0.8/functions/dev/v1_X/FunctionsBilling.sol | 2 +- contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol | 4 ++-- .../v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol | 4 ++-- .../dev/v1_X/accessControl/TermsOfServiceAllowList.sol | 4 ++-- .../functions/tests/v1_X/FunctionsSubscriptions.t.sol | 2 +- contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol | 2 +- contracts/src/v0.8/functions/v1_0_0/FunctionsRouter.sol | 4 ++-- .../src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol | 4 ++-- .../v1_0_0/accessControl/TermsOfServiceAllowList.sol | 4 ++-- contracts/src/v0.8/llo-feeds/FeeManager.sol | 8 ++++---- contracts/src/v0.8/llo-feeds/RewardManager.sol | 4 ++-- contracts/src/v0.8/llo-feeds/Verifier.sol | 2 +- contracts/src/v0.8/llo-feeds/VerifierProxy.sol | 2 +- contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol | 2 +- .../src/v0.8/llo-feeds/interfaces/IRewardManager.sol | 2 +- contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol | 2 +- .../src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol | 2 +- .../v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol | 2 +- .../llo-feeds/test/reward-manager/BaseRewardManager.t.sol | 2 +- .../test/reward-manager/RewardManager.general.t.sol | 2 +- .../v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol | 4 ++-- .../test/verifier/VerifierProxySetVerifierTest.t.sol | 2 +- contracts/src/v0.8/operatorforwarder/dev/Operator.sol | 2 +- contracts/src/v0.8/shared/mocks/WERC20Mock.sol | 2 +- .../v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol | 4 ++-- .../shared/test/token/ERC677/OpStackBurnMintERC677.t.sol | 2 +- contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol | 2 +- .../v0.8/shared/token/ERC20/IOptimismMintableERC20.sol | 2 +- contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol | 8 ++++---- contracts/src/v0.8/shared/token/ERC677/ERC677.sol | 4 ++-- .../v0.8/shared/token/ERC677/OpStackBurnMintERC677.sol | 2 +- contracts/src/v0.8/vendor/MockOVMCrossDomainMessenger.sol | 2 +- .../{v4.8.0 => v4.8.3}/contracts/interfaces/IERC165.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/interfaces/IERC20.sol | 0 .../contracts/interfaces/draft-IERC20Permit.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/mocks/ERC20Mock.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/security/Pausable.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/token/ERC20/ERC20.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/token/ERC20/IERC20.sol | 0 .../contracts/token/ERC20/extensions/ERC20Burnable.sol | 0 .../contracts/token/ERC20/extensions/IERC20Metadata.sol | 0 .../token/ERC20/extensions/draft-ERC20Permit.sol | 0 .../token/ERC20/extensions/draft-IERC20Permit.sol | 0 .../contracts/token/ERC20/utils/SafeERC20.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/Address.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/Context.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/Counters.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/StorageSlot.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/Strings.sol | 0 .../contracts/utils/cryptography/ECDSA.sol | 0 .../contracts/utils/cryptography/EIP712.sol | 0 .../contracts/utils/introspection/IERC165.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/math/Math.sol | 0 .../{v4.8.0 => v4.8.3}/contracts/utils/math/SafeCast.sol | 0 .../contracts/utils/math/SignedMath.sol | 0 .../contracts/utils/structs/EnumerableMap.sol | 0 .../contracts/utils/structs/EnumerableSet.sol | 0 60 files changed, 54 insertions(+), 54 deletions(-) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/interfaces/IERC165.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/interfaces/IERC20.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/interfaces/draft-IERC20Permit.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/mocks/ERC20Mock.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/security/Pausable.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/ERC20.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/IERC20.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/extensions/ERC20Burnable.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/extensions/IERC20Metadata.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/extensions/draft-ERC20Permit.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/extensions/draft-IERC20Permit.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/token/ERC20/utils/SafeERC20.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/Address.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/Context.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/Counters.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/StorageSlot.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/Strings.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/cryptography/ECDSA.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/cryptography/EIP712.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/introspection/IERC165.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/math/Math.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/math/SafeCast.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/math/SignedMath.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/structs/EnumerableMap.sol (100%) rename contracts/src/v0.8/vendor/openzeppelin-solidity/{v4.8.0 => v4.8.3}/contracts/utils/structs/EnumerableSet.sol (100%) diff --git a/contracts/scripts/native_solc_compile_all_shared b/contracts/scripts/native_solc_compile_all_shared index 705cedce7ab..db421f45e04 100755 --- a/contracts/scripts/native_solc_compile_all_shared +++ b/contracts/scripts/native_solc_compile_all_shared @@ -28,4 +28,4 @@ compileContract () { compileContract shared/token/ERC677/BurnMintERC677.sol compileContract shared/token/ERC677/LinkToken.sol compileContract shared/mocks/WERC20Mock.sol -compileContract vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/ERC20.sol +compileContract vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol diff --git a/contracts/src/v0.8/automation/upkeeps/ERC20BalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/ERC20BalanceMonitor.sol index 61a6e882d81..d2a7adc67af 100644 --- a/contracts/src/v0.8/automation/upkeeps/ERC20BalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/ERC20BalanceMonitor.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.4; import "../../shared/access/ConfirmedOwner.sol"; import "../interfaces/KeeperCompatibleInterface.sol"; -import "../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol"; -import "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol"; +import "../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; +import "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title The ERC20BalanceMonitor contract. diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index 028579397aa..e2d42bc0666 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -4,9 +4,9 @@ pragma solidity 0.8.6; import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; -import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol"; -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; -import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol"; +import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; interface IAggregatorProxy { function aggregator() external view returns (address); diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index e7958a7bcab..ed67d485431 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -8,7 +8,7 @@ import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; import {Routable} from "./Routable.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; -import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol"; +import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; /// @title Functions Billing contract /// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON). diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol index 8daa821bb54..2e12a75679a 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol @@ -10,8 +10,8 @@ import {FunctionsSubscriptions} from "./FunctionsSubscriptions.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; -import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol"; -import {Pausable} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol"; +import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; +import {Pausable} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, ITypeAndVersion, ConfirmedOwner { using FunctionsResponse for FunctionsResponse.RequestMeta; diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol index 86e762e39c8..bcd01c1f0cf 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsSubscriptions.sol @@ -7,8 +7,8 @@ import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title Functions Subscriptions contract /// @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON). diff --git a/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol index fbeef3c298a..b36d063ad7d 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/accessControl/TermsOfServiceAllowList.sol @@ -7,8 +7,8 @@ import {ITypeAndVersion} from "../../../../shared/interfaces/ITypeAndVersion.sol import {ConfirmedOwner} from "../../../../shared/access/ConfirmedOwner.sol"; -import {Address} from "../../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Address.sol"; -import {EnumerableSet} from "../../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; +import {EnumerableSet} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; /// @notice A contract to handle access control of subscription management dependent on signing a Terms of Service contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, ITypeAndVersion, ConfirmedOwner { diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol index df905fb8bee..8f08a6c1e86 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol @@ -6,7 +6,7 @@ import {FunctionsRouter} from "../../dev/v1_X/FunctionsRouter.sol"; import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {FunctionsRouterSetup, FunctionsOwnerAcceptTermsOfServiceSetup, FunctionsClientSetup, FunctionsSubscriptionSetup, FunctionsClientRequestSetup, FunctionsFulfillmentSetup} from "./Setup.t.sol"; diff --git a/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol b/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol index 1f99903c5a2..5168bdc01ed 100644 --- a/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol @@ -8,7 +8,7 @@ import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; import {Routable} from "./Routable.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; -import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol"; +import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; /// @title Functions Billing contract /// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON). diff --git a/contracts/src/v0.8/functions/v1_0_0/FunctionsRouter.sol b/contracts/src/v0.8/functions/v1_0_0/FunctionsRouter.sol index dad50b042b9..9f35c4dfe51 100644 --- a/contracts/src/v0.8/functions/v1_0_0/FunctionsRouter.sol +++ b/contracts/src/v0.8/functions/v1_0_0/FunctionsRouter.sol @@ -10,8 +10,8 @@ import {FunctionsSubscriptions} from "./FunctionsSubscriptions.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; -import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol"; -import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol"; +import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; +import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; contract FunctionsRouter is IFunctionsRouter, FunctionsSubscriptions, Pausable, ITypeAndVersion, ConfirmedOwner { using FunctionsResponse for FunctionsResponse.RequestMeta; diff --git a/contracts/src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol b/contracts/src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol index 7fa2368c46d..b93d2174734 100644 --- a/contracts/src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol +++ b/contracts/src/v0.8/functions/v1_0_0/FunctionsSubscriptions.sol @@ -7,8 +7,8 @@ import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; -import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title Functions Subscriptions contract /// @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON). diff --git a/contracts/src/v0.8/functions/v1_0_0/accessControl/TermsOfServiceAllowList.sol b/contracts/src/v0.8/functions/v1_0_0/accessControl/TermsOfServiceAllowList.sol index a7b13577842..8a42e34cb84 100644 --- a/contracts/src/v0.8/functions/v1_0_0/accessControl/TermsOfServiceAllowList.sol +++ b/contracts/src/v0.8/functions/v1_0_0/accessControl/TermsOfServiceAllowList.sol @@ -7,8 +7,8 @@ import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; -import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Address.sol"; -import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableSet.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; /// @notice A contract to handle access control of subscription management dependent on signing a Terms of Service contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController, ITypeAndVersion, ConfirmedOwner { diff --git a/contracts/src/v0.8/llo-feeds/FeeManager.sol b/contracts/src/v0.8/llo-feeds/FeeManager.sol index 0cc8273442d..397605d9b2e 100644 --- a/contracts/src/v0.8/llo-feeds/FeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/FeeManager.sol @@ -4,13 +4,13 @@ pragma solidity 0.8.16; import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; import {IFeeManager} from "./interfaces/IFeeManager.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../libraries/Common.sol"; import {IRewardManager} from "./interfaces/IRewardManager.sol"; import {IWERC20} from "../shared/interfaces/IWERC20.sol"; -import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol"; -import {Math} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/Math.sol"; -import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; +import {Math} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol"; +import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/RewardManager.sol b/contracts/src/v0.8/llo-feeds/RewardManager.sol index e064bff8b67..3777b432fcc 100644 --- a/contracts/src/v0.8/llo-feeds/RewardManager.sol +++ b/contracts/src/v0.8/llo-feeds/RewardManager.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.16; import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; import {IRewardManager} from "./interfaces/IRewardManager.sol"; -import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol"; +import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {Common} from "../libraries/Common.sol"; -import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol"; +import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /** * @title RewardManager diff --git a/contracts/src/v0.8/llo-feeds/Verifier.sol b/contracts/src/v0.8/llo-feeds/Verifier.sol index cd7e3d2bad6..f7ce156a60b 100644 --- a/contracts/src/v0.8/llo-feeds/Verifier.sol +++ b/contracts/src/v0.8/llo-feeds/Verifier.sol @@ -5,7 +5,7 @@ import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; import {IVerifier} from "./interfaces/IVerifier.sol"; import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../libraries/Common.sol"; // OCR2 standard diff --git a/contracts/src/v0.8/llo-feeds/VerifierProxy.sol b/contracts/src/v0.8/llo-feeds/VerifierProxy.sol index 989c5c6b81a..6abb2b78e98 100644 --- a/contracts/src/v0.8/llo-feeds/VerifierProxy.sol +++ b/contracts/src/v0.8/llo-feeds/VerifierProxy.sol @@ -6,7 +6,7 @@ import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol"; import {IVerifier} from "./interfaces/IVerifier.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {AccessControllerInterface} from "../shared/interfaces/AccessControllerInterface.sol"; -import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol"; import {Common} from "../libraries/Common.sol"; diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol b/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol index ed7213870e2..08373a6a5bc 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../../libraries/Common.sol"; import {IVerifierFeeManager} from "./IVerifierFeeManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol b/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol index 68cecc4f97a..a76366a3eb1 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../../libraries/Common.sol"; interface IRewardManager is IERC165 { diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol b/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol index 7617d9a5c35..9b9ba2e6570 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../../libraries/Common.sol"; interface IVerifier is IERC165 { diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol b/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol index 8e490d39d1e..e5a73e612cb 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../../libraries/Common.sol"; interface IVerifierFeeManager is IERC165 { diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol b/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol index c446150406e..ec2611f9e46 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol @@ -5,7 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {FeeManager} from "../../FeeManager.sol"; import {RewardManager} from "../../RewardManager.sol"; import {Common} from "../../../libraries/Common.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/mocks/ERC20Mock.sol"; +import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {WERC20Mock} from "../../../shared/mocks/WERC20Mock.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; import {FeeManagerProxy} from "../mocks/FeeManagerProxy.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol b/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol index 4a701093168..3e50adef95c 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {Test} from "forge-std/Test.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/mocks/ERC20Mock.sol"; +import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {RewardManager} from "../../RewardManager.sol"; import {Common} from "../../../libraries/Common.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol index 9cee5b05c57..e7bc43dc81d 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.general.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.16; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; import {RewardManager} from "../../RewardManager.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/mocks/ERC20Mock.sol"; +import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol index 3faa65fb52b..91e0f9da906 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.16; import {Test} from "forge-std/Test.sol"; import {VerifierProxy} from "../../VerifierProxy.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {IVerifier} from "../../interfaces/IVerifier.sol"; import {ErroredVerifier} from "../mocks/ErroredVerifier.sol"; import {Verifier} from "../../Verifier.sol"; @@ -11,7 +11,7 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; import {FeeManager} from "../../FeeManager.sol"; import {Common} from "../../../libraries/Common.sol"; -import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/mocks/ERC20Mock.sol"; +import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {WERC20Mock} from "../../../shared/mocks/WERC20Mock.sol"; import {FeeManager} from "../../FeeManager.sol"; import {RewardManager} from "../../RewardManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol index 3699aaf420d..a6b23d7e8b4 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.16; import {BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t.sol"; import {IVerifier} from "../../interfaces/IVerifier.sol"; import {VerifierProxy} from "../../VerifierProxy.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {Common} from "../../../libraries/Common.sol"; contract VerifierProxyInitializeVerifierTest is BaseTestWithConfiguredVerifierAndFeeManager { diff --git a/contracts/src/v0.8/operatorforwarder/dev/Operator.sol b/contracts/src/v0.8/operatorforwarder/dev/Operator.sol index b68e2837304..b83996a9ed9 100644 --- a/contracts/src/v0.8/operatorforwarder/dev/Operator.sol +++ b/contracts/src/v0.8/operatorforwarder/dev/Operator.sol @@ -11,7 +11,7 @@ import {IOwnable} from "../../shared/interfaces/IOwnable.sol"; import {WithdrawalInterface} from "./interfaces/WithdrawalInterface.sol"; import {OracleInterface} from "../../interfaces/OracleInterface.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol"; +import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; // @title The Chainlink Operator contract // @notice Node operators can deploy this contract to fulfill requests sent to them diff --git a/contracts/src/v0.8/shared/mocks/WERC20Mock.sol b/contracts/src/v0.8/shared/mocks/WERC20Mock.sol index 02c13be9937..cee7fa7ff83 100644 --- a/contracts/src/v0.8/shared/mocks/WERC20Mock.sol +++ b/contracts/src/v0.8/shared/mocks/WERC20Mock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {ERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/ERC20.sol"; +import {ERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol"; contract WERC20Mock is ERC20 { constructor() ERC20("WERC20Mock", "WERC") {} diff --git a/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol b/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol index 6b13f390009..de9067a569a 100644 --- a/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol +++ b/contracts/src/v0.8/shared/test/token/ERC677/BurnMintERC677.t.sol @@ -7,8 +7,8 @@ import {IERC677} from "../../../token/ERC677/IERC677.sol"; import {BaseTest} from "../../BaseTest.t.sol"; import {BurnMintERC677} from "../../../token/ERC677/BurnMintERC677.sol"; -import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; -import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol"; +import {IERC20} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; contract BurnMintERC677Setup is BaseTest { event Transfer(address indexed from, address indexed to, uint256 value); diff --git a/contracts/src/v0.8/shared/test/token/ERC677/OpStackBurnMintERC677.t.sol b/contracts/src/v0.8/shared/test/token/ERC677/OpStackBurnMintERC677.t.sol index f22a92a4258..7987fefec48 100644 --- a/contracts/src/v0.8/shared/test/token/ERC677/OpStackBurnMintERC677.t.sol +++ b/contracts/src/v0.8/shared/test/token/ERC677/OpStackBurnMintERC677.t.sol @@ -9,7 +9,7 @@ import {BurnMintERC677} from "../../../token/ERC677/BurnMintERC677.sol"; import {BaseTest} from "../../BaseTest.t.sol"; import {OpStackBurnMintERC677} from "../../../token/ERC677/OpStackBurnMintERC677.sol"; -import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol"; +import {IERC165} from "../../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; contract OpStackBurnMintERC677Setup is BaseTest { address internal s_l1Token = address(897352983527); diff --git a/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol b/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol index 2b2f3fd3787..b9b3b54bf73 100644 --- a/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol +++ b/contracts/src/v0.8/shared/token/ERC20/IBurnMintERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; interface IBurnMintERC20 is IERC20 { /// @notice Mints new tokens for a given address. diff --git a/contracts/src/v0.8/shared/token/ERC20/IOptimismMintableERC20.sol b/contracts/src/v0.8/shared/token/ERC20/IOptimismMintableERC20.sol index 6362d9e78ac..4e9d3a2494c 100644 --- a/contracts/src/v0.8/shared/token/ERC20/IOptimismMintableERC20.sol +++ b/contracts/src/v0.8/shared/token/ERC20/IOptimismMintableERC20.sol @@ -2,7 +2,7 @@ // solhint-disable one-contract-per-file pragma solidity ^0.8.0; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; /// @title IOptimismMintableERC20Minimal /// @dev This interface is a subset of the Optimism ERC20 interface that is defined diff --git a/contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol b/contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol index 775d5fb3d94..556914da127 100644 --- a/contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol +++ b/contracts/src/v0.8/shared/token/ERC677/BurnMintERC677.sol @@ -7,10 +7,10 @@ import {IERC677} from "./IERC677.sol"; import {ERC677} from "./ERC677.sol"; import {OwnerIsCreator} from "../../access/OwnerIsCreator.sol"; -import {ERC20Burnable} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/ERC20Burnable.sol"; -import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableSet.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol"; -import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; +import {ERC20Burnable} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/ERC20Burnable.sol"; +import {EnumerableSet} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; +import {IERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; /// @notice A basic ERC677 compatible token contract with burn and minting roles. /// @dev The total supply can be limited during deployment. diff --git a/contracts/src/v0.8/shared/token/ERC677/ERC677.sol b/contracts/src/v0.8/shared/token/ERC677/ERC677.sol index c9a2996e8e9..9a68bac3a11 100644 --- a/contracts/src/v0.8/shared/token/ERC677/ERC677.sol +++ b/contracts/src/v0.8/shared/token/ERC677/ERC677.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.0; import {IERC677} from "./IERC677.sol"; import {IERC677Receiver} from "./IERC677Receiver.sol"; -import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Address.sol"; -import {ERC20} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/ERC20.sol"; +import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; +import {ERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol"; contract ERC677 is IERC677, ERC20 { using Address for address; diff --git a/contracts/src/v0.8/shared/token/ERC677/OpStackBurnMintERC677.sol b/contracts/src/v0.8/shared/token/ERC677/OpStackBurnMintERC677.sol index 714a4a11baf..95c64c9cd23 100644 --- a/contracts/src/v0.8/shared/token/ERC677/OpStackBurnMintERC677.sol +++ b/contracts/src/v0.8/shared/token/ERC677/OpStackBurnMintERC677.sol @@ -5,7 +5,7 @@ import {IOptimismMintableERC20Minimal, IOptimismMintableERC20} from "../ERC20/IO import {BurnMintERC677} from "./BurnMintERC677.sol"; -import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol"; +import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol"; /// @notice A basic ERC677 compatible token contract with burn and minting roles that supports /// the native L2 bridging requirements of the Optimism Stack. diff --git a/contracts/src/v0.8/vendor/MockOVMCrossDomainMessenger.sol b/contracts/src/v0.8/vendor/MockOVMCrossDomainMessenger.sol index 1947379929f..ae8b6af1cc3 100644 --- a/contracts/src/v0.8/vendor/MockOVMCrossDomainMessenger.sol +++ b/contracts/src/v0.8/vendor/MockOVMCrossDomainMessenger.sol @@ -2,7 +2,7 @@ pragma solidity >=0.7.6 <0.9.0; -import "./openzeppelin-solidity/v4.8.0/contracts/utils/Address.sol"; +import "./openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; /** * @title iOVM_CrossDomainMessenger diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC165.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/IERC20.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/draft-IERC20Permit.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/draft-IERC20Permit.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/interfaces/draft-IERC20Permit.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/draft-IERC20Permit.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/mocks/ERC20Mock.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/mocks/ERC20Mock.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/security/Pausable.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/ERC20.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/ERC20.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/ERC20Burnable.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/ERC20Burnable.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/ERC20Burnable.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/ERC20Burnable.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/IERC20Metadata.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/IERC20Metadata.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/IERC20Metadata.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/draft-ERC20Permit.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/draft-ERC20Permit.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/draft-ERC20Permit.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/draft-ERC20Permit.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/draft-IERC20Permit.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/draft-IERC20Permit.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/extensions/draft-IERC20Permit.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/extensions/draft-IERC20Permit.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Address.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Address.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Context.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Context.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Context.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Context.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Counters.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Counters.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Counters.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Counters.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/StorageSlot.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/StorageSlot.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/StorageSlot.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/StorageSlot.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Strings.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Strings.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/Strings.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Strings.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/cryptography/ECDSA.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/cryptography/ECDSA.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/cryptography/ECDSA.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/cryptography/ECDSA.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/cryptography/EIP712.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/cryptography/EIP712.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/cryptography/EIP712.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/cryptography/EIP712.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/introspection/IERC165.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/introspection/IERC165.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/Math.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/Math.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/Math.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SafeCast.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SignedMath.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SignedMath.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/math/SignedMath.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SignedMath.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableMap.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol diff --git a/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableSet.sol b/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol similarity index 100% rename from contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/contracts/utils/structs/EnumerableSet.sol rename to contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableSet.sol From 7cadec057ebc180b131deeed49bbb4a8b78aadcc Mon Sep 17 00:00:00 2001 From: Andrei Smirnov Date: Tue, 31 Oct 2023 15:00:50 +0300 Subject: [PATCH 040/271] Contracts: running hardhat tests in parallel (#11090) Co-authored-by: Rens Rooimans --- contracts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/package.json b/contracts/package.json index 935c7901b6b..05a5293e48d 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -6,7 +6,7 @@ "license": "MIT", "private": false, "scripts": { - "test": "hardhat test", + "test": "hardhat test --parallel", "lint": "eslint --ext js,ts .", "prettier:check": "prettier '**/*' --check --ignore-unknown", "prettier:write": "prettier '**/*' --write --ignore-unknown", From 8fba9c8a4217356cf51a8ddda1daa62fbdb9e150 Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 31 Oct 2023 12:29:17 +0000 Subject: [PATCH 041/271] [BCF-2727] Add telemetry adapter service (#11125) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../ocr2/plugins/generic/telemetry_adapter.go | 56 +++++++++ .../plugins/generic/telemetry_adapter_test.go | 108 ++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 8 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 core/services/ocr2/plugins/generic/telemetry_adapter.go create mode 100644 core/services/ocr2/plugins/generic/telemetry_adapter_test.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 14e29a6740a..690a8d189cc 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -302,7 +302,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 08356fe0766..5cbdb37427d 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1458,8 +1458,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter.go b/core/services/ocr2/plugins/generic/telemetry_adapter.go new file mode 100644 index 00000000000..a81befa9854 --- /dev/null +++ b/core/services/ocr2/plugins/generic/telemetry_adapter.go @@ -0,0 +1,56 @@ +package generic + +import ( + "context" + "errors" + + "github.com/smartcontractkit/libocr/commontypes" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" +) + +var _ types.TelemetryService = (*TelemetryAdapter)(nil) + +type TelemetryAdapter struct { + endpointGenerator types.MonitoringEndpointGenerator + endpoints map[[4]string]commontypes.MonitoringEndpoint +} + +func NewTelemetryAdapter(endpointGen types.MonitoringEndpointGenerator) *TelemetryAdapter { + return &TelemetryAdapter{ + endpoints: make(map[[4]string]commontypes.MonitoringEndpoint), + endpointGenerator: endpointGen, + } +} + +func (t *TelemetryAdapter) Send(ctx context.Context, network string, chainID string, contractID string, telemetryType string, payload []byte) error { + e, err := t.getOrCreateEndpoint(contractID, telemetryType, network, chainID) + if err != nil { + return err + } + e.SendLog(payload) + return nil +} + +func (t *TelemetryAdapter) getOrCreateEndpoint(contractID string, telemetryType string, network string, chainID string) (commontypes.MonitoringEndpoint, error) { + if contractID == "" { + return nil, errors.New("contractID cannot be empty") + } + if telemetryType == "" { + return nil, errors.New("telemetryType cannot be empty") + } + if network == "" { + return nil, errors.New("network cannot be empty") + } + if chainID == "" { + return nil, errors.New("chainID cannot be empty") + } + + key := [4]string{network, chainID, contractID, telemetryType} + e, ok := t.endpoints[key] + if !ok { + e = t.endpointGenerator.GenMonitoringEndpoint(network, chainID, contractID, telemetryType) + t.endpoints[key] = e + } + return e, nil +} diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go new file mode 100644 index 00000000000..d430b889a4b --- /dev/null +++ b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go @@ -0,0 +1,108 @@ +package generic + +import ( + "context" + "fmt" + "testing" + + "github.com/smartcontractkit/libocr/commontypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockEndpoint struct { + network string + chainID string + contractID string + telemetryType string + payload []byte +} + +func (m *mockEndpoint) SendLog(payload []byte) { m.payload = payload } + +type mockGenerator struct{} + +func (m *mockGenerator) GenMonitoringEndpoint(network, chainID, contractID, telemetryType string) commontypes.MonitoringEndpoint { + return &mockEndpoint{ + network: network, + chainID: chainID, + contractID: contractID, + telemetryType: telemetryType, + } +} + +func TestTelemetryAdapter(t *testing.T) { + ta := NewTelemetryAdapter(&mockGenerator{}) + + tests := []struct { + name string + contractID string + telemetryType string + networkID string + chainID string + payload []byte + errorMsg string + }{ + { + name: "valid request", + contractID: "contract", + telemetryType: "mercury", + networkID: "solana", + chainID: "1337", + payload: []byte("uh oh"), + }, + { + name: "no valid contractID", + telemetryType: "mercury", + networkID: "solana", + chainID: "1337", + payload: []byte("uh oh"), + errorMsg: "contractID cannot be empty", + }, + { + name: "no valid chainID", + contractID: "contract", + telemetryType: "mercury", + networkID: "solana", + payload: []byte("uh oh"), + errorMsg: "chainID cannot be empty", + }, + { + name: "no valid telemetryType", + contractID: "contract", + networkID: "solana", + chainID: "1337", + payload: []byte("uh oh"), + errorMsg: "telemetryType cannot be empty", + }, + { + name: "no valid network", + contractID: "contract", + telemetryType: "mercury", + chainID: "1337", + payload: []byte("uh oh"), + errorMsg: "network cannot be empty", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := ta.Send(context.Background(), test.networkID, test.chainID, test.contractID, test.telemetryType, test.payload) + if test.errorMsg != "" { + assert.ErrorContains(t, err, test.errorMsg) + } else { + require.NoError(t, err) + key := [4]string{test.networkID, test.chainID, test.contractID, test.telemetryType} + fmt.Printf("%+v", ta.endpoints) + endpoint, ok := ta.endpoints[key] + require.True(t, ok) + + me := endpoint.(*mockEndpoint) + assert.Equal(t, test.networkID, me.network) + assert.Equal(t, test.chainID, me.chainID) + assert.Equal(t, test.contractID, me.contractID) + assert.Equal(t, test.telemetryType, me.telemetryType) + assert.Equal(t, test.payload, me.payload) + } + }) + } +} diff --git a/go.mod b/go.mod index 9c14fe8c144..ad3cb5f78ed 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index 372e705c71e..f879f16272b 100644 --- a/go.sum +++ b/go.sum @@ -1459,8 +1459,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 1263c406aaf..3affd799194 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -384,7 +384,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 08c037dd0ce..98685fdf889 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2362,8 +2362,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436 h1:XJuWThPInOZ9Bz0zM8xmACO+Ly/cY9+0JOILkHlN/2o= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231030111206-48c9bf5d5436/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From 9fd1d5d0e28dd631ac879035f56bee7e03e78713 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 31 Oct 2023 09:45:26 -0500 Subject: [PATCH 042/271] golangci-lint fix issue & disable only-new-issues (#11133) * core/services/ocr/plugins/generic: rm unused error return * .github/actions/golangci-lint: disable only-new-issues --- .github/actions/golangci-lint/action.yml | 2 +- .../ocr2/plugins/generic/pipeline_runner_adapter.go | 9 ++------- .../ocr2/plugins/generic/pipeline_runner_adapter_test.go | 3 +-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index c9ea735d1fe..97755fa46ea 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -58,7 +58,7 @@ runs: skip-pkg-cache: true skip-build-cache: true # only-new-issues is only applicable to PRs, otherwise it is always set to false - only-new-issues: true + only-new-issues: false # disabled for PRs due to unreliability args: --out-format colored-line-number,checkstyle:golangci-lint-report.xml working-directory: ${{ inputs.go-directory }} - name: Store lint report artifact diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go index 5c58522f409..6afb35ca758 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go @@ -41,10 +41,7 @@ func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, var }, } - err := merge(defaultVars, vars.Vars) - if err != nil { - return nil, err - } + merge(defaultVars, vars.Vars) finalVars := pipeline.NewVarsFrom(defaultVars) _, trrs, err := p.runner.ExecuteRun(ctx, s, finalVars, p.logger) @@ -74,7 +71,7 @@ func NewPipelineRunnerAdapter(logger logger.Logger, job job.Job, runner pipeline } // merge merges mapTwo into mapOne, modifying mapOne in the process. -func merge(mapOne, mapTwo map[string]interface{}) error { +func merge(mapOne, mapTwo map[string]interface{}) { for k, v := range mapTwo { // if `mapOne` doesn't have `k`, then nothing to do, just assign v to `mapOne`. if _, ok := mapOne[k]; !ok { @@ -89,6 +86,4 @@ func merge(mapOne, mapTwo map[string]interface{}) error { } } } - - return nil } diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go index d1f06d87662..ee0038232dc 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go @@ -131,8 +131,7 @@ func TestMerge(t *testing.T) { "val": 0, } - err := merge(vars, addedVars) - require.NoError(t, err) + merge(vars, addedVars) assert.True(t, reflect.DeepEqual(vars, map[string]interface{}{ "jb": map[string]interface{}{ From 921a89ca6243d274062b2090458e6b40421242e9 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Tue, 31 Oct 2023 20:14:02 +0100 Subject: [PATCH 043/271] BCF-2672 cleanup legacy job orm env var loading (#11119) * Remove unused DRSpecConfig param in LoadEnvConfigVarsVRF * Remove unused DirectRequestSpec MinIncomingConfirmationsEnv field * Remove unnecessary DRSpecConfig wrapper interface from job orm * Remove DirectRequestSpec MinIncomingConfirmations override from job orm MinIncomingConfirmations for DirectRequestSpec already gets overridden in delegate before its only usage * Remove DirectRequestSpec MinIncomingConfirmationsENV from graphql spec * Revert removal of DirectRequestSpec MinIncomingConfirmationsENV * Remove legacy chains from job orm - Remove LoadEnvConfigVarsOCR for ocr spec, because it gets called in delegate anyway - Remove legacy chains as it is no longer used in orm * Remove unused legacyChains from job_orm_test.go * Revert "Remove unused legacyChains from job_orm_test.go" This reverts commit dce7e3f5dc67fb924a204ecb5f1491a970786286. * Revert "Remove legacy chains from job orm" This reverts commit a41a48f74b11235a4e9519e691eab31e6bb6088d. * Rename legacy env var loading funcs in job orm and cleanup unused stuff * Remove unused legacy tramsnimtter address env * minor func rename * Update func signature in calls to LoadDefaultVRFPollPeriod * Remove EncryptedOCRKeyBundleIDEnv and ConfirmationsEnv --- core/services/directrequest/delegate.go | 2 +- core/services/job/models.go | 74 +++++++-------- core/services/job/orm.go | 62 ++++--------- core/services/job/orm_test.go | 32 +++---- core/services/ocr/config.go | 2 +- core/services/ocr/delegate.go | 2 +- core/services/vrf/v1/listener_v1.go | 2 +- core/services/vrf/v2/listener_v2.go | 2 +- core/web/presenters/job.go | 114 ++++++++++-------------- core/web/resolver/spec.go | 58 ------------ core/web/resolver/spec_test.go | 72 +++++---------- core/web/schema/type/spec.graphql | 10 --- 12 files changed, 140 insertions(+), 292 deletions(-) diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index 174dca062aa..920f94b4d60 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -77,7 +77,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { if err != nil { return nil, err } - concreteSpec := job.LoadEnvConfigVarsDR(chain.Config().EVM(), *jb.DirectRequestSpec) + concreteSpec := job.SetDRMinIncomingConfirmations(chain.Config().EVM().MinIncomingConfirmations(), *jb.DirectRequestSpec) oracle, err := operator_wrapper.NewOperator(concreteSpec.ContractAddress.Address(), chain.Client()) if err != nil { diff --git a/core/services/job/models.go b/core/services/job/models.go index a3dfce59996..a474040dd41 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -15,6 +15,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -233,35 +234,25 @@ func (pr *PipelineRun) SetID(value string) error { // OCROracleSpec defines the job spec for OCR jobs. type OCROracleSpec struct { - ID int32 `toml:"-"` - ContractAddress ethkey.EIP55Address `toml:"contractAddress"` - P2PBootstrapPeers pq.StringArray `toml:"p2pBootstrapPeers" db:"p2p_bootstrap_peers"` - P2PV2Bootstrappers pq.StringArray `toml:"p2pv2Bootstrappers" db:"p2pv2_bootstrappers"` - IsBootstrapPeer bool `toml:"isBootstrapPeer"` - EncryptedOCRKeyBundleID *models.Sha256Hash `toml:"keyBundleID"` - EncryptedOCRKeyBundleIDEnv bool - TransmitterAddress *ethkey.EIP55Address `toml:"transmitterAddress"` - TransmitterAddressEnv bool - ObservationTimeout models.Interval `toml:"observationTimeout"` - ObservationTimeoutEnv bool - BlockchainTimeout models.Interval `toml:"blockchainTimeout"` - BlockchainTimeoutEnv bool - ContractConfigTrackerSubscribeInterval models.Interval `toml:"contractConfigTrackerSubscribeInterval"` - ContractConfigTrackerSubscribeIntervalEnv bool - ContractConfigTrackerPollInterval models.Interval `toml:"contractConfigTrackerPollInterval"` - ContractConfigTrackerPollIntervalEnv bool - ContractConfigConfirmations uint16 `toml:"contractConfigConfirmations"` - ContractConfigConfirmationsEnv bool - EVMChainID *utils.Big `toml:"evmChainID" db:"evm_chain_id"` - DatabaseTimeout *models.Interval `toml:"databaseTimeout"` - DatabaseTimeoutEnv bool - ObservationGracePeriod *models.Interval `toml:"observationGracePeriod"` - ObservationGracePeriodEnv bool - ContractTransmitterTransmitTimeout *models.Interval `toml:"contractTransmitterTransmitTimeout"` - ContractTransmitterTransmitTimeoutEnv bool - CaptureEATelemetry bool `toml:"captureEATelemetry"` - CreatedAt time.Time `toml:"-"` - UpdatedAt time.Time `toml:"-"` + ID int32 `toml:"-"` + ContractAddress ethkey.EIP55Address `toml:"contractAddress"` + P2PBootstrapPeers pq.StringArray `toml:"p2pBootstrapPeers" db:"p2p_bootstrap_peers"` + P2PV2Bootstrappers pq.StringArray `toml:"p2pv2Bootstrappers" db:"p2pv2_bootstrappers"` + IsBootstrapPeer bool `toml:"isBootstrapPeer"` + EncryptedOCRKeyBundleID *models.Sha256Hash `toml:"keyBundleID"` + TransmitterAddress *ethkey.EIP55Address `toml:"transmitterAddress"` + ObservationTimeout models.Interval `toml:"observationTimeout"` + BlockchainTimeout models.Interval `toml:"blockchainTimeout"` + ContractConfigTrackerSubscribeInterval models.Interval `toml:"contractConfigTrackerSubscribeInterval"` + ContractConfigTrackerPollInterval models.Interval `toml:"contractConfigTrackerPollInterval"` + ContractConfigConfirmations uint16 `toml:"contractConfigConfirmations"` + EVMChainID *utils.Big `toml:"evmChainID" db:"evm_chain_id"` + DatabaseTimeout *models.Interval `toml:"databaseTimeout"` + ObservationGracePeriod *models.Interval `toml:"observationGracePeriod"` + ContractTransmitterTransmitTimeout *models.Interval `toml:"contractTransmitterTransmitTimeout"` + CaptureEATelemetry bool `toml:"captureEATelemetry"` + CreatedAt time.Time `toml:"-"` + UpdatedAt time.Time `toml:"-"` } // GetID is a getter function that returns the ID of the spec. @@ -438,15 +429,14 @@ func (w *WebhookSpec) SetID(value string) error { } type DirectRequestSpec struct { - ID int32 `toml:"-"` - ContractAddress ethkey.EIP55Address `toml:"contractAddress"` - MinIncomingConfirmations clnull.Uint32 `toml:"minIncomingConfirmations"` - MinIncomingConfirmationsEnv bool `toml:"minIncomingConfirmationsEnv"` - Requesters models.AddressCollection `toml:"requesters"` - MinContractPayment *assets.Link `toml:"minContractPaymentLinkJuels"` - EVMChainID *utils.Big `toml:"evmChainID"` - CreatedAt time.Time `toml:"-"` - UpdatedAt time.Time `toml:"-"` + ID int32 `toml:"-"` + ContractAddress ethkey.EIP55Address `toml:"contractAddress"` + MinIncomingConfirmations clnull.Uint32 `toml:"minIncomingConfirmations"` + Requesters models.AddressCollection `toml:"requesters"` + MinContractPayment *assets.Link `toml:"minContractPaymentLinkJuels"` + EVMChainID *utils.Big `toml:"evmChainID"` + CreatedAt time.Time `toml:"-"` + UpdatedAt time.Time `toml:"-"` } type CronSpec struct { @@ -522,13 +512,11 @@ type VRFSpec struct { CoordinatorAddress ethkey.EIP55Address `toml:"coordinatorAddress"` PublicKey secp256k1.PublicKey `toml:"publicKey"` MinIncomingConfirmations uint32 `toml:"minIncomingConfirmations"` - ConfirmationsEnv bool `toml:"-"` EVMChainID *utils.Big `toml:"evmChainID"` FromAddresses []ethkey.EIP55Address `toml:"fromAddresses"` - PollPeriod time.Duration `toml:"pollPeriod"` // For v2 jobs - PollPeriodEnv bool - RequestedConfsDelay int64 `toml:"requestedConfsDelay"` // For v2 jobs. Optional, defaults to 0 if not provided. - RequestTimeout time.Duration `toml:"requestTimeout"` // Optional, defaults to 24hr if not provided. + PollPeriod time.Duration `toml:"pollPeriod"` // For v2 jobs + RequestedConfsDelay int64 `toml:"requestedConfsDelay"` // For v2 jobs. Optional, defaults to 0 if not provided. + RequestTimeout time.Duration `toml:"requestTimeout"` // Optional, defaults to 24hr if not provided. // GasLanePrice specifies the gas lane price for this VRF job. // If the specified keys in FromAddresses do not have the provided gas price the job diff --git a/core/services/job/orm.go b/core/services/job/orm.go index cbdd7ebfae6..372b2fe74a1 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -705,61 +705,41 @@ func (o *orm) FindJobs(offset, limit int) (jobs []Job, count int, err error) { return err } for i := range jobs { - err = multierr.Combine(err, o.LoadEnvConfigVars(&jobs[i])) + err = multierr.Combine(err, o.LoadConfigVars(&jobs[i])) } return nil }) return jobs, int(count), err } -func (o *orm) LoadEnvConfigVars(jb *Job) error { +func (o *orm) LoadConfigVars(jb *Job) error { if jb.OCROracleSpec != nil { ch, err := o.legacyChains.Get(jb.OCROracleSpec.EVMChainID.String()) if err != nil { return err } - newSpec, err := LoadEnvConfigVarsOCR(ch.Config().EVM().OCR(), ch.Config().OCR(), *jb.OCROracleSpec) + newSpec, err := LoadConfigVarsOCR(ch.Config().EVM().OCR(), ch.Config().OCR(), *jb.OCROracleSpec) if err != nil { return err } jb.OCROracleSpec = newSpec - } else if jb.VRFSpec != nil { - ch, err := o.legacyChains.Get(jb.VRFSpec.EVMChainID.String()) - if err != nil { - return err - } - jb.VRFSpec = LoadEnvConfigVarsVRF(ch.Config().EVM(), *jb.VRFSpec) - } else if jb.DirectRequestSpec != nil { - ch, err := o.legacyChains.Get(jb.DirectRequestSpec.EVMChainID.String()) - if err != nil { - return err - } - jb.DirectRequestSpec = LoadEnvConfigVarsDR(ch.Config().EVM(), *jb.DirectRequestSpec) } return nil } -type DRSpecConfig interface { - MinIncomingConfirmations() uint32 -} - -func LoadEnvConfigVarsVRF(cfg DRSpecConfig, vrfs VRFSpec) *VRFSpec { +func LoadDefaultVRFPollPeriod(vrfs VRFSpec) *VRFSpec { if vrfs.PollPeriod == 0 { - vrfs.PollPeriodEnv = true vrfs.PollPeriod = 5 * time.Second } return &vrfs } -func LoadEnvConfigVarsDR(cfg DRSpecConfig, drs DirectRequestSpec) *DirectRequestSpec { - // Take the largest of the global vs specific. - minIncomingConfirmations := cfg.MinIncomingConfirmations() - if !drs.MinIncomingConfirmations.Valid || drs.MinIncomingConfirmations.Uint32 < minIncomingConfirmations { - drs.MinIncomingConfirmationsEnv = true - drs.MinIncomingConfirmations = null.Uint32From(minIncomingConfirmations) +// SetDRMinIncomingConfirmations takes the largest of the global vs specific. +func SetDRMinIncomingConfirmations(defaultMinIncomingConfirmations uint32, drs DirectRequestSpec) *DirectRequestSpec { + if !drs.MinIncomingConfirmations.Valid || drs.MinIncomingConfirmations.Uint32 < defaultMinIncomingConfirmations { + drs.MinIncomingConfirmations = null.Uint32From(defaultMinIncomingConfirmations) } - return &drs } @@ -773,38 +753,30 @@ type OCRConfig interface { TransmitterAddress() (ethkey.EIP55Address, error) } -// LoadEnvConfigVarsLocalOCR loads local OCR env vars into the OCROracleSpec. -func LoadEnvConfigVarsLocalOCR(evmOcrCfg evmconfig.OCR, os OCROracleSpec, ocrCfg OCRConfig) *OCROracleSpec { +// LoadConfigVarsLocalOCR loads local OCR vars into the OCROracleSpec. +func LoadConfigVarsLocalOCR(evmOcrCfg evmconfig.OCR, os OCROracleSpec, ocrCfg OCRConfig) *OCROracleSpec { if os.ObservationTimeout == 0 { - os.ObservationTimeoutEnv = true os.ObservationTimeout = models.Interval(ocrCfg.ObservationTimeout()) } if os.BlockchainTimeout == 0 { - os.BlockchainTimeoutEnv = true os.BlockchainTimeout = models.Interval(ocrCfg.BlockchainTimeout()) } if os.ContractConfigTrackerSubscribeInterval == 0 { - os.ContractConfigTrackerSubscribeIntervalEnv = true os.ContractConfigTrackerSubscribeInterval = models.Interval(ocrCfg.ContractSubscribeInterval()) } if os.ContractConfigTrackerPollInterval == 0 { - os.ContractConfigTrackerPollIntervalEnv = true os.ContractConfigTrackerPollInterval = models.Interval(ocrCfg.ContractPollInterval()) } if os.ContractConfigConfirmations == 0 { - os.ContractConfigConfirmationsEnv = true os.ContractConfigConfirmations = evmOcrCfg.ContractConfirmations() } if os.DatabaseTimeout == nil { - os.DatabaseTimeoutEnv = true os.DatabaseTimeout = models.NewInterval(evmOcrCfg.DatabaseTimeout()) } if os.ObservationGracePeriod == nil { - os.ObservationGracePeriodEnv = true os.ObservationGracePeriod = models.NewInterval(evmOcrCfg.ObservationGracePeriod()) } if os.ContractTransmitterTransmitTimeout == nil { - os.ContractTransmitterTransmitTimeoutEnv = true os.ContractTransmitterTransmitTimeout = models.NewInterval(evmOcrCfg.ContractTransmitterTransmitTimeout()) } os.CaptureEATelemetry = ocrCfg.CaptureEATelemetry() @@ -812,15 +784,14 @@ func LoadEnvConfigVarsLocalOCR(evmOcrCfg evmconfig.OCR, os OCROracleSpec, ocrCfg return &os } -// LoadEnvConfigVarsOCR loads OCR env vars into the OCROracleSpec. -func LoadEnvConfigVarsOCR(evmOcrCfg evmconfig.OCR, ocrCfg OCRConfig, os OCROracleSpec) (*OCROracleSpec, error) { +// LoadConfigVarsOCR loads OCR config vars into the OCROracleSpec. +func LoadConfigVarsOCR(evmOcrCfg evmconfig.OCR, ocrCfg OCRConfig, os OCROracleSpec) (*OCROracleSpec, error) { if os.TransmitterAddress == nil { ta, err := ocrCfg.TransmitterAddress() if !errors.Is(errors.Cause(err), config.ErrEnvUnset) { if err != nil { return nil, err } - os.TransmitterAddressEnv = true os.TransmitterAddress = &ta } } @@ -834,11 +805,10 @@ func LoadEnvConfigVarsOCR(evmOcrCfg evmconfig.OCR, ocrCfg OCRConfig, os OCROracl if err != nil { return nil, err } - os.EncryptedOCRKeyBundleIDEnv = true os.EncryptedOCRKeyBundleID = &encryptedOCRKeyBundleID } - return LoadEnvConfigVarsLocalOCR(evmOcrCfg, os, ocrCfg), nil + return LoadConfigVarsLocalOCR(evmOcrCfg, os, ocrCfg), nil } func (o *orm) FindJobTx(id int32) (Job, error) { @@ -872,7 +842,7 @@ func (o *orm) FindJobWithoutSpecErrors(id int32) (jb Job, err error) { return jb, errors.Wrap(err, "FindJobWithoutSpecErrors failed") } - return jb, o.LoadEnvConfigVars(&jb) + return jb, o.LoadConfigVars(&jb) } // FindSpecErrorsByJobIDs returns all jobs spec errors by jobs IDs @@ -961,7 +931,7 @@ func (o *orm) findJob(jb *Job, col string, arg interface{}, qopts ...pg.QOpt) er if err != nil { return errors.Wrap(err, "findJob failed") } - return o.LoadEnvConfigVars(jb) + return o.LoadConfigVars(jb) } func (o *orm) FindJobIDsWithBridge(name string) (jids []int32, err error) { @@ -1205,7 +1175,7 @@ func (o *orm) FindJobsByPipelineSpecIDs(ids []int32) ([]Job, error) { return err } for i := range jbs { - err = o.LoadEnvConfigVars(&jbs[i]) + err = o.LoadConfigVars(&jbs[i]) //We must return the jobs even if the chainID is disabled if err != nil && !errors.Is(err, chains.ErrNoSuchChainID) { return err diff --git a/core/services/job/orm_test.go b/core/services/job/orm_test.go index a6986d7fb32..cd437147b4f 100644 --- a/core/services/job/orm_test.go +++ b/core/services/job/orm_test.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) func NewTestORM(t *testing.T, db *sqlx.DB, legacyChains evm.LegacyChainContainer, pipelineORM pipeline.ORM, bridgeORM bridges.ORM, keyStore keystore.Master, cfg pg.QConfig) job.ORM { @@ -27,26 +28,28 @@ func NewTestORM(t *testing.T, db *sqlx.DB, legacyChains evm.LegacyChainContainer return o } -func TestLoadEnvConfigVarsLocalOCR(t *testing.T) { +func TestLoadConfigVarsLocalOCR(t *testing.T) { t.Parallel() config := configtest.NewTestGeneralConfig(t) chainConfig := evmtest.NewChainScopedConfig(t, config) jobSpec := &job.OCROracleSpec{} - jobSpec = job.LoadEnvConfigVarsLocalOCR(chainConfig.EVM().OCR(), *jobSpec, chainConfig.OCR()) + jobSpec = job.LoadConfigVarsLocalOCR(chainConfig.EVM().OCR(), *jobSpec, chainConfig.OCR()) - require.True(t, jobSpec.ObservationTimeoutEnv) - require.True(t, jobSpec.BlockchainTimeoutEnv) - require.True(t, jobSpec.ContractConfigTrackerSubscribeIntervalEnv) - require.True(t, jobSpec.ContractConfigTrackerPollIntervalEnv) - require.True(t, jobSpec.ContractConfigConfirmationsEnv) - require.True(t, jobSpec.DatabaseTimeoutEnv) - require.True(t, jobSpec.ObservationGracePeriodEnv) - require.True(t, jobSpec.ContractTransmitterTransmitTimeoutEnv) + require.Equal(t, models.Interval(chainConfig.OCR().ObservationTimeout()), jobSpec.ObservationTimeout) + require.Equal(t, models.Interval(chainConfig.OCR().BlockchainTimeout()), jobSpec.BlockchainTimeout) + require.Equal(t, models.Interval(chainConfig.OCR().ContractSubscribeInterval()), jobSpec.ContractConfigTrackerSubscribeInterval) + require.Equal(t, models.Interval(chainConfig.OCR().ContractPollInterval()), jobSpec.ContractConfigTrackerPollInterval) + require.Equal(t, chainConfig.OCR().CaptureEATelemetry(), jobSpec.CaptureEATelemetry) + + require.Equal(t, chainConfig.EVM().OCR().ContractConfirmations(), jobSpec.ContractConfigConfirmations) + require.Equal(t, models.Interval(chainConfig.EVM().OCR().DatabaseTimeout()), *jobSpec.DatabaseTimeout) + require.Equal(t, models.Interval(chainConfig.EVM().OCR().ObservationGracePeriod()), *jobSpec.ObservationGracePeriod) + require.Equal(t, models.Interval(chainConfig.EVM().OCR().ContractTransmitterTransmitTimeout()), *jobSpec.ContractTransmitterTransmitTimeout) } -func TestLoadEnvConfigVarsDR(t *testing.T) { +func TestSetDRMinIncomingConfirmations(t *testing.T) { t.Parallel() config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { @@ -59,15 +62,14 @@ func TestLoadEnvConfigVarsDR(t *testing.T) { MinIncomingConfirmations: clnull.Uint32From(10), } - drs10 := job.LoadEnvConfigVarsDR(chainConfig.EVM(), jobSpec10) - assert.True(t, drs10.MinIncomingConfirmationsEnv) + drs10 := job.SetDRMinIncomingConfirmations(chainConfig.EVM().MinIncomingConfirmations(), jobSpec10) + assert.Equal(t, uint32(100), drs10.MinIncomingConfirmations.Uint32) jobSpec200 := job.DirectRequestSpec{ MinIncomingConfirmations: clnull.Uint32From(200), } - drs200 := job.LoadEnvConfigVarsDR(chainConfig.EVM(), jobSpec200) - assert.False(t, drs200.MinIncomingConfirmationsEnv) + drs200 := job.SetDRMinIncomingConfirmations(chainConfig.EVM().MinIncomingConfirmations(), jobSpec200) assert.True(t, drs200.MinIncomingConfirmations.Valid) assert.Equal(t, uint32(200), drs200.MinIncomingConfirmations.Uint32) } diff --git a/core/services/ocr/config.go b/core/services/ocr/config.go index e1bc997f269..53ec9f9cea9 100644 --- a/core/services/ocr/config.go +++ b/core/services/ocr/config.go @@ -14,7 +14,7 @@ type Config interface { } func toLocalConfig(cfg ValidationConfig, evmOcrConfig evmconfig.OCR, insecureCfg insecureConfig, spec job.OCROracleSpec, ocrConfig job.OCRConfig) ocrtypes.LocalConfig { - concreteSpec := job.LoadEnvConfigVarsLocalOCR(evmOcrConfig, spec, ocrConfig) + concreteSpec := job.LoadConfigVarsLocalOCR(evmOcrConfig, spec, ocrConfig) lc := ocrtypes.LocalConfig{ BlockchainTimeout: concreteSpec.BlockchainTimeout.Duration(), ContractConfigConfirmations: concreteSpec.ContractConfigConfirmations, diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index dee349a9a0d..b761690485c 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -95,7 +95,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e if err != nil { return nil, err } - concreteSpec, err := job.LoadEnvConfigVarsOCR(chain.Config().EVM().OCR(), chain.Config().OCR(), *jb.OCROracleSpec) + concreteSpec, err := job.LoadConfigVarsOCR(chain.Config().EVM().OCR(), chain.Config().OCR(), *jb.OCROracleSpec) if err != nil { return nil, err } diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 613c0d124df..92e697f2294 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -112,7 +112,7 @@ func (lsn *Listener) getLatestHead() uint64 { // Start complies with job.Service func (lsn *Listener) Start(context.Context) error { return lsn.StartOnce("VRFListener", func() error { - spec := job.LoadEnvConfigVarsVRF(lsn.Cfg, *lsn.Job.VRFSpec) + spec := job.LoadDefaultVRFPollPeriod(*lsn.Job.VRFSpec) unsubscribeLogs := lsn.LogBroadcaster.Register(lsn, log.ListenerOpts{ Contract: lsn.Coordinator.Address(), diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 7560baad3a2..8bac485d656 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -271,7 +271,7 @@ func (lsn *listenerV2) Start(ctx context.Context) error { "proofVerificationGas", GasProofVerification) } - spec := job.LoadEnvConfigVarsVRF(lsn.cfg, *lsn.job.VRFSpec) + spec := job.LoadDefaultVRFPollPeriod(*lsn.job.VRFSpec) unsubscribeLogs := lsn.logBroadcaster.Register(lsn, log.ListenerOpts{ Contract: lsn.coordinator.Address(), diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index 2aa97730881..06b9950755f 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -41,26 +41,24 @@ const ( // DirectRequestSpec defines the spec details of a DirectRequest Job type DirectRequestSpec struct { - ContractAddress ethkey.EIP55Address `json:"contractAddress"` - MinIncomingConfirmations clnull.Uint32 `json:"minIncomingConfirmations"` - MinIncomingConfirmationsEnv bool `json:"minIncomingConfirmationsEnv,omitempty"` - MinContractPayment *assets.Link `json:"minContractPaymentLinkJuels"` - Requesters models.AddressCollection `json:"requesters"` - Initiator string `json:"initiator"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` + ContractAddress ethkey.EIP55Address `json:"contractAddress"` + MinIncomingConfirmations clnull.Uint32 `json:"minIncomingConfirmations"` + MinContractPayment *assets.Link `json:"minContractPaymentLinkJuels"` + Requesters models.AddressCollection `json:"requesters"` + Initiator string `json:"initiator"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + EVMChainID *utils.Big `json:"evmChainID"` } // NewDirectRequestSpec initializes a new DirectRequestSpec from a // job.DirectRequestSpec func NewDirectRequestSpec(spec *job.DirectRequestSpec) *DirectRequestSpec { return &DirectRequestSpec{ - ContractAddress: spec.ContractAddress, - MinIncomingConfirmations: spec.MinIncomingConfirmations, - MinIncomingConfirmationsEnv: spec.MinIncomingConfirmationsEnv, - MinContractPayment: spec.MinContractPayment, - Requesters: spec.Requesters, + ContractAddress: spec.ContractAddress, + MinIncomingConfirmations: spec.MinIncomingConfirmations, + MinContractPayment: spec.MinContractPayment, + Requesters: spec.Requesters, // This is hardcoded to runlog. When we support other initiators, we need // to change this Initiator: "runlog", @@ -120,64 +118,48 @@ func NewFluxMonitorSpec(spec *job.FluxMonitorSpec) *FluxMonitorSpec { // OffChainReportingSpec defines the spec details of a OffChainReporting Job type OffChainReportingSpec struct { - ContractAddress ethkey.EIP55Address `json:"contractAddress"` - P2PBootstrapPeers pq.StringArray `json:"p2pBootstrapPeers"` - P2PV2Bootstrappers pq.StringArray `json:"p2pv2Bootstrappers"` - IsBootstrapPeer bool `json:"isBootstrapPeer"` - EncryptedOCRKeyBundleID *models.Sha256Hash `json:"keyBundleID"` - TransmitterAddress *ethkey.EIP55Address `json:"transmitterAddress"` - ObservationTimeout models.Interval `json:"observationTimeout"` - ObservationTimeoutEnv bool `json:"observationTimeoutEnv,omitempty"` - BlockchainTimeout models.Interval `json:"blockchainTimeout"` - BlockchainTimeoutEnv bool `json:"blockchainTimeoutEnv,omitempty"` - ContractConfigTrackerSubscribeInterval models.Interval `json:"contractConfigTrackerSubscribeInterval"` - ContractConfigTrackerSubscribeIntervalEnv bool `json:"contractConfigTrackerSubscribeIntervalEnv,omitempty"` - ContractConfigTrackerPollInterval models.Interval `json:"contractConfigTrackerPollInterval"` - ContractConfigTrackerPollIntervalEnv bool `json:"contractConfigTrackerPollIntervalEnv,omitempty"` - ContractConfigConfirmations uint16 `json:"contractConfigConfirmations"` - ContractConfigConfirmationsEnv bool `json:"contractConfigConfirmationsEnv,omitempty"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - EVMChainID *utils.Big `json:"evmChainID"` - DatabaseTimeout *models.Interval `json:"databaseTimeout"` - DatabaseTimeoutEnv bool `json:"databaseTimeoutEnv,omitempty"` - ObservationGracePeriod *models.Interval `json:"observationGracePeriod"` - ObservationGracePeriodEnv bool `json:"observationGracePeriodEnv,omitempty"` - ContractTransmitterTransmitTimeout *models.Interval `json:"contractTransmitterTransmitTimeout"` - ContractTransmitterTransmitTimeoutEnv bool `json:"contractTransmitterTransmitTimeoutEnv,omitempty"` - CollectTelemetry bool `json:"collectTelemetry,omitempty"` + ContractAddress ethkey.EIP55Address `json:"contractAddress"` + P2PBootstrapPeers pq.StringArray `json:"p2pBootstrapPeers"` + P2PV2Bootstrappers pq.StringArray `json:"p2pv2Bootstrappers"` + IsBootstrapPeer bool `json:"isBootstrapPeer"` + EncryptedOCRKeyBundleID *models.Sha256Hash `json:"keyBundleID"` + TransmitterAddress *ethkey.EIP55Address `json:"transmitterAddress"` + ObservationTimeout models.Interval `json:"observationTimeout"` + BlockchainTimeout models.Interval `json:"blockchainTimeout"` + ContractConfigTrackerSubscribeInterval models.Interval `json:"contractConfigTrackerSubscribeInterval"` + ContractConfigTrackerPollInterval models.Interval `json:"contractConfigTrackerPollInterval"` + ContractConfigConfirmations uint16 `json:"contractConfigConfirmations"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + EVMChainID *utils.Big `json:"evmChainID"` + DatabaseTimeout *models.Interval `json:"databaseTimeout"` + ObservationGracePeriod *models.Interval `json:"observationGracePeriod"` + ContractTransmitterTransmitTimeout *models.Interval `json:"contractTransmitterTransmitTimeout"` + CollectTelemetry bool `json:"collectTelemetry,omitempty"` } // NewOffChainReportingSpec initializes a new OffChainReportingSpec from a // job.OCROracleSpec func NewOffChainReportingSpec(spec *job.OCROracleSpec) *OffChainReportingSpec { return &OffChainReportingSpec{ - ContractAddress: spec.ContractAddress, - P2PBootstrapPeers: spec.P2PBootstrapPeers, - P2PV2Bootstrappers: spec.P2PV2Bootstrappers, - IsBootstrapPeer: spec.IsBootstrapPeer, - EncryptedOCRKeyBundleID: spec.EncryptedOCRKeyBundleID, - TransmitterAddress: spec.TransmitterAddress, - ObservationTimeout: spec.ObservationTimeout, - ObservationTimeoutEnv: spec.ObservationTimeoutEnv, - BlockchainTimeout: spec.BlockchainTimeout, - BlockchainTimeoutEnv: spec.BlockchainTimeoutEnv, - ContractConfigTrackerSubscribeInterval: spec.ContractConfigTrackerSubscribeInterval, - ContractConfigTrackerSubscribeIntervalEnv: spec.ContractConfigTrackerSubscribeIntervalEnv, - ContractConfigTrackerPollInterval: spec.ContractConfigTrackerPollInterval, - ContractConfigTrackerPollIntervalEnv: spec.ContractConfigTrackerPollIntervalEnv, - ContractConfigConfirmations: spec.ContractConfigConfirmations, - ContractConfigConfirmationsEnv: spec.ContractConfigConfirmationsEnv, - CreatedAt: spec.CreatedAt, - UpdatedAt: spec.UpdatedAt, - EVMChainID: spec.EVMChainID, - DatabaseTimeout: spec.DatabaseTimeout, - DatabaseTimeoutEnv: spec.DatabaseTimeoutEnv, - ObservationGracePeriod: spec.ObservationGracePeriod, - ObservationGracePeriodEnv: spec.ObservationGracePeriodEnv, - ContractTransmitterTransmitTimeout: spec.ContractTransmitterTransmitTimeout, - ContractTransmitterTransmitTimeoutEnv: spec.ContractTransmitterTransmitTimeoutEnv, - CollectTelemetry: spec.CaptureEATelemetry, + ContractAddress: spec.ContractAddress, + P2PBootstrapPeers: spec.P2PBootstrapPeers, + P2PV2Bootstrappers: spec.P2PV2Bootstrappers, + IsBootstrapPeer: spec.IsBootstrapPeer, + EncryptedOCRKeyBundleID: spec.EncryptedOCRKeyBundleID, + TransmitterAddress: spec.TransmitterAddress, + ObservationTimeout: spec.ObservationTimeout, + BlockchainTimeout: spec.BlockchainTimeout, + ContractConfigTrackerSubscribeInterval: spec.ContractConfigTrackerSubscribeInterval, + ContractConfigTrackerPollInterval: spec.ContractConfigTrackerPollInterval, + ContractConfigConfirmations: spec.ContractConfigConfirmations, + CreatedAt: spec.CreatedAt, + UpdatedAt: spec.UpdatedAt, + EVMChainID: spec.EVMChainID, + DatabaseTimeout: spec.DatabaseTimeout, + ObservationGracePeriod: spec.ObservationGracePeriod, + ContractTransmitterTransmitTimeout: spec.ContractTransmitterTransmitTimeout, + CollectTelemetry: spec.CaptureEATelemetry, } } diff --git a/core/web/resolver/spec.go b/core/web/resolver/spec.go index 48040d118a7..c9ee5199229 100644 --- a/core/web/resolver/spec.go +++ b/core/web/resolver/spec.go @@ -164,11 +164,6 @@ func (r *DirectRequestSpecResolver) MinIncomingConfirmations() int32 { return 0 } -// EVMChainID resolves the spec's evm chain id. -func (r *DirectRequestSpecResolver) MinIncomingConfirmationsEnv() bool { - return r.spec.MinIncomingConfirmationsEnv -} - // MinContractPaymentLinkJuels resolves the spec's min contract payment link. func (r *DirectRequestSpecResolver) MinContractPaymentLinkJuels() string { return r.spec.MinContractPayment.String() @@ -328,12 +323,6 @@ func (r *OCRSpecResolver) BlockchainTimeout() *string { return &timeout } -// BlockchainTimeoutEnv resolves whether the spec's blockchain timeout comes -// from an env var. -func (r *OCRSpecResolver) BlockchainTimeoutEnv() bool { - return r.spec.BlockchainTimeoutEnv -} - // ContractAddress resolves the spec's contract address. func (r *OCRSpecResolver) ContractAddress() string { return r.spec.ContractAddress.String() @@ -350,12 +339,6 @@ func (r *OCRSpecResolver) ContractConfigConfirmations() *int32 { return &confirmations } -// ContractConfigConfirmationsEnv resolves whether spec's confirmations -// config comes from an env var. -func (r *OCRSpecResolver) ContractConfigConfirmationsEnv() bool { - return r.spec.ContractConfigConfirmationsEnv -} - // ContractConfigTrackerPollInterval resolves the spec's contract tracker poll // interval config. func (r *OCRSpecResolver) ContractConfigTrackerPollInterval() *string { @@ -368,12 +351,6 @@ func (r *OCRSpecResolver) ContractConfigTrackerPollInterval() *string { return &interval } -// ContractConfigTrackerPollIntervalEnv resolves the whether spec's tracker poll -// config comes from an env var. -func (r *OCRSpecResolver) ContractConfigTrackerPollIntervalEnv() bool { - return r.spec.ContractConfigTrackerPollIntervalEnv -} - // ContractConfigTrackerSubscribeInterval resolves the spec's tracker subscribe // interval config. func (r *OCRSpecResolver) ContractConfigTrackerSubscribeInterval() *string { @@ -386,12 +363,6 @@ func (r *OCRSpecResolver) ContractConfigTrackerSubscribeInterval() *string { return &interval } -// ContractConfigTrackerSubscribeIntervalEnv resolves whether spec's tracker -// subscribe interval config comes from an env var. -func (r *OCRSpecResolver) ContractConfigTrackerSubscribeIntervalEnv() bool { - return r.spec.ContractConfigTrackerSubscribeIntervalEnv -} - // CreatedAt resolves the spec's created at timestamp. func (r *OCRSpecResolver) CreatedAt() graphql.Time { return graphql.Time{Time: r.spec.CreatedAt} @@ -413,34 +384,16 @@ func (r *OCRSpecResolver) DatabaseTimeout() string { return r.spec.DatabaseTimeout.Duration().String() } -// DatabaseTimeoutEnv resolves the whether spec's database timeout -// config comes from an env var. -func (r *OCRSpecResolver) DatabaseTimeoutEnv() bool { - return r.spec.DatabaseTimeoutEnv -} - // ObservationGracePeriod resolves the spec's observation grace period. func (r *OCRSpecResolver) ObservationGracePeriod() string { return r.spec.ObservationGracePeriod.Duration().String() } -// ObservationGracePeriodEnv resolves the whether spec's observation grace period -// config comes from an env var. -func (r *OCRSpecResolver) ObservationGracePeriodEnv() bool { - return r.spec.ObservationGracePeriodEnv -} - // ContractTransmitterTransmitTimeout resolves the spec's contract transmitter transmit timeout. func (r *OCRSpecResolver) ContractTransmitterTransmitTimeout() string { return r.spec.ContractTransmitterTransmitTimeout.Duration().String() } -// ContractTransmitterTransmitTimeoutEnv resolves the whether spec's -// contract transmitter transmit timeout config comes from an env var. -func (r *OCRSpecResolver) ContractTransmitterTransmitTimeoutEnv() bool { - return r.spec.ContractTransmitterTransmitTimeoutEnv -} - // IsBootstrapPeer resolves whether spec is a bootstrap peer. func (r *OCRSpecResolver) IsBootstrapPeer() bool { return r.spec.IsBootstrapPeer @@ -468,12 +421,6 @@ func (r *OCRSpecResolver) ObservationTimeout() *string { return &timeout } -// ObservationTimeoutEnv resolves whether spec's observation timeout comes -// from an env var. -func (r *OCRSpecResolver) ObservationTimeoutEnv() bool { - return r.spec.ObservationTimeoutEnv -} - // P2PBootstrapPeers resolves the spec's p2p bootstrap peers func (r *OCRSpecResolver) P2PBootstrapPeers() *[]string { if len(r.spec.P2PBootstrapPeers) == 0 { @@ -631,11 +578,6 @@ func (r *VRFSpecResolver) MinIncomingConfirmations() int32 { return int32(r.spec.MinIncomingConfirmations) } -// MinIncomingConfirmations resolves the spec's min incoming confirmations. -func (r *VRFSpecResolver) MinIncomingConfirmationsEnv() bool { - return r.spec.ConfirmationsEnv -} - // CoordinatorAddress resolves the spec's coordinator address. func (r *VRFSpecResolver) CoordinatorAddress() string { return r.spec.CoordinatorAddress.String() diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index 04bfffbe05e..8e4095e171e 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -10,6 +10,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/assets" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -91,13 +92,12 @@ func TestResolver_DirectRequestSpec(t *testing.T) { f.Mocks.jobORM.On("FindJobWithoutSpecErrors", id).Return(job.Job{ Type: job.DirectRequest, DirectRequestSpec: &job.DirectRequestSpec{ - ContractAddress: contractAddress, - CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), - MinIncomingConfirmations: clnull.NewUint32(1, true), - MinIncomingConfirmationsEnv: true, - MinContractPayment: assets.NewLinkFromJuels(1000), - Requesters: models.AddressCollection{requesterAddress}, + ContractAddress: contractAddress, + CreatedAt: f.Timestamp(), + EVMChainID: utils.NewBigI(42), + MinIncomingConfirmations: clnull.NewUint32(1, true), + MinContractPayment: assets.NewLinkFromJuels(1000), + Requesters: models.AddressCollection{requesterAddress}, }, }, nil) }, @@ -112,7 +112,6 @@ func TestResolver_DirectRequestSpec(t *testing.T) { createdAt evmChainID minIncomingConfirmations - minIncomingConfirmationsEnv minContractPaymentLinkJuels requesters } @@ -130,7 +129,6 @@ func TestResolver_DirectRequestSpec(t *testing.T) { "createdAt": "2021-01-01T00:00:00Z", "evmChainID": "42", "minIncomingConfirmations": 1, - "minIncomingConfirmationsEnv": true, "minContractPaymentLinkJuels": "1000", "requesters": ["0x3cCad4715152693fE3BC4460591e3D3Fbd071b42"] } @@ -373,30 +371,22 @@ func TestResolver_OCRSpec(t *testing.T) { f.Mocks.jobORM.On("FindJobWithoutSpecErrors", id).Return(job.Job{ Type: job.OffchainReporting, OCROracleSpec: &job.OCROracleSpec{ - BlockchainTimeout: models.Interval(1 * time.Minute), - BlockchainTimeoutEnv: false, - ContractAddress: contractAddress, - ContractConfigConfirmations: 1, - ContractConfigConfirmationsEnv: true, - ContractConfigTrackerPollInterval: models.Interval(1 * time.Minute), - ContractConfigTrackerPollIntervalEnv: false, - ContractConfigTrackerSubscribeInterval: models.Interval(2 * time.Minute), - ContractConfigTrackerSubscribeIntervalEnv: true, - DatabaseTimeout: models.NewInterval(3 * time.Second), - DatabaseTimeoutEnv: true, - ObservationGracePeriod: models.NewInterval(4 * time.Second), - ObservationGracePeriodEnv: true, - ContractTransmitterTransmitTimeout: models.NewInterval(555 * time.Millisecond), - ContractTransmitterTransmitTimeoutEnv: true, - CreatedAt: f.Timestamp(), - EVMChainID: utils.NewBigI(42), - IsBootstrapPeer: false, - EncryptedOCRKeyBundleID: &keyBundleID, - ObservationTimeout: models.Interval(2 * time.Minute), - ObservationTimeoutEnv: false, - P2PBootstrapPeers: pq.StringArray{"/dns4/test.com/tcp/2001/p2pkey"}, - P2PV2Bootstrappers: pq.StringArray{"12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw@localhost:5001"}, - TransmitterAddress: &transmitterAddress, + BlockchainTimeout: models.Interval(1 * time.Minute), + ContractAddress: contractAddress, + ContractConfigConfirmations: 1, + ContractConfigTrackerPollInterval: models.Interval(1 * time.Minute), + ContractConfigTrackerSubscribeInterval: models.Interval(2 * time.Minute), + DatabaseTimeout: models.NewInterval(3 * time.Second), + ObservationGracePeriod: models.NewInterval(4 * time.Second), + ContractTransmitterTransmitTimeout: models.NewInterval(555 * time.Millisecond), + CreatedAt: f.Timestamp(), + EVMChainID: utils.NewBigI(42), + IsBootstrapPeer: false, + EncryptedOCRKeyBundleID: &keyBundleID, + ObservationTimeout: models.Interval(2 * time.Minute), + P2PBootstrapPeers: pq.StringArray{"/dns4/test.com/tcp/2001/p2pkey"}, + P2PV2Bootstrappers: pq.StringArray{"12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw@localhost:5001"}, + TransmitterAddress: &transmitterAddress, }, }, nil) }, @@ -408,26 +398,18 @@ func TestResolver_OCRSpec(t *testing.T) { __typename ... on OCRSpec { blockchainTimeout - blockchainTimeoutEnv contractAddress contractConfigConfirmations - contractConfigConfirmationsEnv contractConfigTrackerPollInterval - contractConfigTrackerPollIntervalEnv contractConfigTrackerSubscribeInterval - contractConfigTrackerSubscribeIntervalEnv databaseTimeout - databaseTimeoutEnv observationGracePeriod - observationGracePeriodEnv contractTransmitterTransmitTimeout - contractTransmitterTransmitTimeoutEnv createdAt evmChainID isBootstrapPeer keyBundleID observationTimeout - observationTimeoutEnv p2pBootstrapPeers p2pv2Bootstrappers transmitterAddress @@ -443,26 +425,18 @@ func TestResolver_OCRSpec(t *testing.T) { "spec": { "__typename": "OCRSpec", "blockchainTimeout": "1m0s", - "blockchainTimeoutEnv": false, "contractAddress": "0x613a38AC1659769640aaE063C651F48E0250454C", "contractConfigConfirmations": 1, - "contractConfigConfirmationsEnv": true, "contractConfigTrackerPollInterval": "1m0s", - "contractConfigTrackerPollIntervalEnv": false, "contractConfigTrackerSubscribeInterval": "2m0s", - "contractConfigTrackerSubscribeIntervalEnv": true, "databaseTimeout": "3s", - "databaseTimeoutEnv": true, "observationGracePeriod": "4s", - "observationGracePeriodEnv": true, "contractTransmitterTransmitTimeout": "555ms", - "contractTransmitterTransmitTimeoutEnv": true, "createdAt": "2021-01-01T00:00:00Z", "evmChainID": "42", "isBootstrapPeer": false, "keyBundleID": "f5bf259689b26f1374efb3c9a9868796953a0f814bb2d39b968d0e61b58620a5", "observationTimeout": "2m0s", - "observationTimeoutEnv": false, "p2pBootstrapPeers": ["/dns4/test.com/tcp/2001/p2pkey"], "p2pv2Bootstrappers": ["12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw@localhost:5001"], "transmitterAddress": "0x3cCad4715152693fE3BC4460591e3D3Fbd071b42" diff --git a/core/web/schema/type/spec.graphql b/core/web/schema/type/spec.graphql index cdcbabf9ef0..98203a1870e 100644 --- a/core/web/schema/type/spec.graphql +++ b/core/web/schema/type/spec.graphql @@ -22,7 +22,6 @@ type DirectRequestSpec { createdAt: Time! evmChainID: String minIncomingConfirmations: Int! - minIncomingConfirmationsEnv: Boolean! minContractPaymentLinkJuels: String! requesters: [String!] } @@ -52,29 +51,21 @@ type KeeperSpec { type OCRSpec { blockchainTimeout: String - blockchainTimeoutEnv: Boolean! contractAddress: String! contractConfigConfirmations: Int - contractConfigConfirmationsEnv: Boolean! contractConfigTrackerPollInterval: String - contractConfigTrackerPollIntervalEnv: Boolean! contractConfigTrackerSubscribeInterval: String - contractConfigTrackerSubscribeIntervalEnv: Boolean! createdAt: Time! evmChainID: String isBootstrapPeer: Boolean! keyBundleID: String observationTimeout: String - observationTimeoutEnv: Boolean! p2pBootstrapPeers: [String!] p2pv2Bootstrappers: [String!] transmitterAddress: String databaseTimeout: String! - databaseTimeoutEnv: Boolean! observationGracePeriod: String! - observationGracePeriodEnv: Boolean! contractTransmitterTransmitTimeout: String! - contractTransmitterTransmitTimeoutEnv: Boolean! } type OCR2Spec { @@ -100,7 +91,6 @@ type VRFSpec { evmChainID: String fromAddresses: [String!] minIncomingConfirmations: Int! - minIncomingConfirmationsEnv: Boolean! pollPeriod: String! publicKey: String! requestedConfsDelay: Int! From 8c96682617ad90b57b759a95f1555c51b842ee00 Mon Sep 17 00:00:00 2001 From: Jim W Date: Tue, 31 Oct 2023 16:53:02 -0400 Subject: [PATCH 044/271] remove dependency of postgres trigger for broadcaster (#11109) * remove dependency of postgres trigger for broadcaster * remove extra argument in NewBroadcaster call * fixes for extra args * fix some failing tests and remove some error wraps * remove pkgerrors from txm * remove parseAddr which is now dead code * fix error handling * remove trigger from postgres via migration; use error wrapping in txmgr * fix naming of new migration --- common/txmgr/broadcaster.go | 40 ------------- common/txmgr/txmgr.go | 50 ++++++++++------ core/chains/evm/evm_txm.go | 1 - core/chains/evm/txmgr/broadcaster_test.go | 60 ++----------------- core/chains/evm/txmgr/builder.go | 7 +-- core/chains/evm/txmgr/common.go | 7 --- core/chains/evm/txmgr/txmgr_test.go | 28 +++------ core/services/pg/channels.go | 1 - .../0206_remove_tx_insert_trigger.sql | 18 ++++++ 9 files changed, 67 insertions(+), 145 deletions(-) create mode 100644 core/store/migrate/migrations/0206_remove_tx_insert_trigger.sql diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 011866bf39d..4f6ffae2ad8 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -21,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -123,8 +122,6 @@ type Broadcaster[ // when Start is called autoSyncSequence bool - txInsertListener pg.Subscription - eventBroadcaster pg.EventBroadcaster processUnstartedTxsImpl ProcessUnstartedTxs[ADDR] ks txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ] @@ -143,8 +140,6 @@ type Broadcaster[ initSync sync.Mutex isStarted bool - parseAddr func(string) (ADDR, error) - sequenceLock sync.RWMutex nextSequenceMap map[ADDR]SEQ generateNextSequence types.GenerateNextSequenceFunc[SEQ] @@ -166,13 +161,11 @@ func NewBroadcaster[ txConfig txmgrtypes.BroadcasterTransactionsConfig, listenerConfig txmgrtypes.BroadcasterListenerConfig, keystore txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ], - eventBroadcaster pg.EventBroadcaster, txAttemptBuilder txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], sequenceSyncer SequenceSyncer[ADDR, TX_HASH, BLOCK_HASH, SEQ], logger logger.Logger, checkerFactory TransmitCheckerFactory[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], autoSyncSequence bool, - parseAddress func(string) (ADDR, error), generateNextSequence types.GenerateNextSequenceFunc[SEQ], ) *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { logger = logger.Named("Broadcaster") @@ -187,11 +180,9 @@ func NewBroadcaster[ feeConfig: feeConfig, txConfig: txConfig, listenerConfig: listenerConfig, - eventBroadcaster: eventBroadcaster, ks: keystore, checkerFactory: checkerFactory, autoSyncSequence: autoSyncSequence, - parseAddr: parseAddress, } b.processUnstartedTxsImpl = b.processUnstartedTxs @@ -215,10 +206,6 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) star return errors.New("Broadcaster is already started") } var err error - eb.txInsertListener, err = eb.eventBroadcaster.Subscribe(pg.ChannelInsertOnTx, "") - if err != nil { - return errors.Wrap(err, "Broadcaster could not start") - } eb.enabledAddresses, err = eb.ks.EnabledAddressesForChain(eb.chainID) if err != nil { return errors.Wrap(err, "Broadcaster: failed to load EnabledAddressesForChain") @@ -239,9 +226,6 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) star go eb.monitorTxs(addr, triggerCh) } - eb.wg.Add(1) - go eb.txInsertTriggerer() - eb.sequenceLock.Lock() defer eb.sequenceLock.Unlock() eb.nextSequenceMap, err = eb.loadNextSequenceMap(eb.enabledAddresses) @@ -266,9 +250,6 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) clos if !eb.isStarted { return errors.Wrap(utils.ErrAlreadyStopped, "Broadcaster is not started") } - if eb.txInsertListener != nil { - eb.txInsertListener.Close() - } close(eb.chStop) eb.wg.Wait() eb.isStarted = false @@ -305,27 +286,6 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Trig } } -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) txInsertTriggerer() { - defer eb.wg.Done() - for { - select { - case ev, ok := <-eb.txInsertListener.Events(): - if !ok { - eb.logger.Debug("txInsertListener channel closed, exiting trigger loop") - return - } - addr, err := eb.parseAddr(ev.Payload) - if err != nil { - eb.logger.Errorw("failed to parse address in trigger", "err", err) - continue - } - eb.Trigger(addr) - case <-eb.chStop: - return - } - } -} - // Load the next sequence map using the tx table or on-chain (if not found in tx table) func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) loadNextSequenceMap(addresses []ADDR) (map[ADDR]SEQ, error) { ctx, cancel := eb.chStop.NewCtx() diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 0c7117afab0..d80f534ad26 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -10,7 +10,6 @@ import ( "time" "github.com/google/uuid" - pkgerrors "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-relay/pkg/services" @@ -166,14 +165,14 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return b.StartOnce("Txm", func() error { var ms services.MultiStart if err := ms.Start(ctx, b.broadcaster); err != nil { - return pkgerrors.Wrap(err, "Txm: Broadcaster failed to start") + return fmt.Errorf("Txm: Broadcaster failed to start: %w", err) } if err := ms.Start(ctx, b.confirmer); err != nil { - return pkgerrors.Wrap(err, "Txm: Confirmer failed to start") + return fmt.Errorf("Txm: Confirmer failed to start: %w", err) } if err := ms.Start(ctx, b.txAttemptBuilder); err != nil { - return pkgerrors.Wrap(err, "Txm: Estimator failed to start") + return fmt.Errorf("Txm: Estimator failed to start: %w", err) } b.wg.Add(1) @@ -190,7 +189,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx if b.fwdMgr != nil { if err := ms.Start(ctx, b.fwdMgr); err != nil { - return pkgerrors.Wrap(err, "Txm: ForwarderManager failed to start") + return fmt.Errorf("Txm: ForwarderManager failed to start: %w", err) } } @@ -223,8 +222,10 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Reset(addr func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) abandon(addr ADDR) (err error) { ctx, cancel := utils.StopChan(b.chStop).NewCtx() defer cancel() - err = b.txStore.Abandon(ctx, b.chainID, addr) - return pkgerrors.Wrapf(err, "abandon failed to update txes for key %s", addr.String()) + if err = b.txStore.Abandon(ctx, b.chainID, addr); err != nil { + return fmt.Errorf("abandon failed to update txes for key %s: %w", addr.String(), err) + } + return nil } func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() (merr error) { @@ -241,14 +242,14 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() (m } if b.fwdMgr != nil { if err := b.fwdMgr.Close(); err != nil { - merr = errors.Join(merr, pkgerrors.Wrap(err, "Txm: failed to stop ForwarderManager")) + merr = errors.Join(merr, fmt.Errorf("Txm: failed to stop ForwarderManager: %w", err)) } } b.wg.Wait() if err := b.txAttemptBuilder.Close(); err != nil { - merr = errors.Join(merr, pkgerrors.Wrap(err, "Txm: failed to close TxAttemptBuilder")) + merr = errors.Join(merr, fmt.Errorf("Txm: failed to close TxAttemptBuilder: %w", err)) } return nil @@ -444,7 +445,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CreateTran var existingTx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] existingTx, err = b.txStore.FindTxWithIdempotencyKey(ctx, *txRequest.IdempotencyKey, b.chainID) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return tx, pkgerrors.Wrap(err, "Failed to search for transaction with IdempotencyKey") + return tx, fmt.Errorf("Failed to search for transaction with IdempotencyKey: %w", err) } if existingTx != nil { b.logger.Infow("Found a Tx with IdempotencyKey. Returning existing Tx without creating a new one.", "IdempotencyKey", *txRequest.IdempotencyKey) @@ -470,31 +471,40 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CreateTran txRequest.ToAddress = txRequest.ForwarderAddress txRequest.EncodedPayload = fwdPayload } else { - b.logger.Errorf("Failed to use forwarder set upstream: %s", fwdErr.Error()) + b.logger.Errorf("Failed to use forwarder set upstream: %w", fwdErr.Error()) } } err = b.txStore.CheckTxQueueCapacity(ctx, txRequest.FromAddress, b.txConfig.MaxQueued(), b.chainID) if err != nil { - return tx, pkgerrors.Wrap(err, "Txm#CreateTransaction") + return tx, fmt.Errorf("Txm#CreateTransaction: %w", err) } tx, err = b.txStore.CreateTransaction(ctx, txRequest, b.chainID) - return + if err != nil { + return tx, err + } + + // Trigger the Broadcaster to check for new transaction + b.broadcaster.Trigger(txRequest.FromAddress) + + return tx, nil } // Calls forwarderMgr to get a proper forwarder for a given EOA. func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetForwarderForEOA(eoa ADDR) (forwarder ADDR, err error) { if !b.txConfig.ForwardersEnabled() { - return forwarder, pkgerrors.Errorf("Forwarding is not enabled, to enable set Transactions.ForwardersEnabled =true") + return forwarder, fmt.Errorf("forwarding is not enabled, to enable set Transactions.ForwardersEnabled =true") } forwarder, err = b.fwdMgr.ForwarderFor(eoa) return } func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) checkEnabled(addr ADDR) error { - err := b.keyStore.CheckEnabled(addr, b.chainID) - return pkgerrors.Wrapf(err, "cannot send transaction from %s on chain ID %s", addr, b.chainID.String()) + if err := b.keyStore.CheckEnabled(addr, b.chainID); err != nil { + return fmt.Errorf("cannot send transaction from %s on chain ID %s: %w", addr, b.chainID.String(), err) + } + return nil } // SendNativeToken creates a transaction that transfers the given value of native tokens @@ -511,7 +521,13 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SendNative Strategy: NewSendEveryStrategy(), } etx, err = b.txStore.CreateTransaction(ctx, txRequest, chainID) - return etx, pkgerrors.Wrap(err, "SendNativeToken failed to insert tx") + if err != nil { + return etx, fmt.Errorf("SendNativeToken failed to insert tx: %w", err) + } + + // Trigger the Broadcaster to check for new transaction + b.broadcaster.Trigger(from) + return etx, nil } type NullTxManager[ diff --git a/core/chains/evm/evm_txm.go b/core/chains/evm/evm_txm.go index d2f4178c7d9..a8673e954a6 100644 --- a/core/chains/evm/evm_txm.go +++ b/core/chains/evm/evm_txm.go @@ -61,7 +61,6 @@ func newEvmTxm( lggr, logPoller, opts.KeyStore, - opts.EventBroadcaster, estimator) } else { txm = opts.GenTxManager(chainID) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 3901da59eeb..61a230c21b1 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -15,7 +15,6 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/google/uuid" - "github.com/onsi/gomega" "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -44,9 +43,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" - pgmocks "github.com/smartcontractkit/chainlink/v2/core/services/pg/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -61,17 +58,14 @@ func NewTestEthBroadcaster( nonceAutoSync bool, ) *txmgr.Broadcaster { t.Helper() - eb := cltest.NewEventBroadcaster(t, config.Database().URL()) ctx := testutils.Context(t) - require.NoError(t, eb.Start(ctx)) - t.Cleanup(func() { assert.NoError(t, eb.Close()) }) lggr := logger.TestLogger(t) ge := config.EVM().GasEstimator() estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(config.EVM().GasEstimator(), ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, keyStore, estimator) txNonceSyncer := txmgr.NewNonceSyncer(txStore, lggr, ethClient) - ethBroadcaster := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(config.EVM().GasEstimator()), config.EVM().Transactions(), config.Database().Listener(), keyStore, eb, txBuilder, txNonceSyncer, lggr, checkerFactory, nonceAutoSync) + ethBroadcaster := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(config.EVM().GasEstimator()), config.EVM().Transactions(), config.Database().Listener(), keyStore, txBuilder, txNonceSyncer, lggr, checkerFactory, nonceAutoSync) // Mark instance as test ethBroadcaster.XXXTestDisableUnstartedTxAutoProcessing() @@ -82,10 +76,6 @@ func NewTestEthBroadcaster( func TestEthBroadcaster_Lifecycle(t *testing.T) { cfg, db := heavyweight.FullTestDBV2(t, "eth_broadcaster_optimistic_locking", nil) - eventBroadcaster := cltest.NewEventBroadcaster(t, cfg.Database().URL()) - err := eventBroadcaster.Start(testutils.Context(t)) - require.NoError(t, err) - t.Cleanup(func() { assert.NoError(t, eventBroadcaster.Close()) }) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) evmcfg := evmtest.NewChainScopedConfig(t, cfg) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -102,7 +92,6 @@ func TestEthBroadcaster_Lifecycle(t *testing.T) { evmcfg.EVM().Transactions(), evmcfg.Database().Listener(), ethKeyStore, - eventBroadcaster, txBuilder, nil, logger.TestLogger(t), @@ -111,7 +100,7 @@ func TestEthBroadcaster_Lifecycle(t *testing.T) { ) // Can't close an unstarted instance - err = eb.Close() + err := eb.Close() require.Error(t, err) ctx := testutils.Context(t) @@ -577,9 +566,6 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testing.T) { // non-transactional DB needed because we deliberately test for FK violation cfg, db := heavyweight.FullTestDBV2(t, "eth_broadcaster_optimistic_locking", nil) - eventBroadcaster := cltest.NewEventBroadcaster(t, cfg.Database().URL()) - require.NoError(t, eventBroadcaster.Start(testutils.Context(t))) - t.Cleanup(func() { assert.NoError(t, eventBroadcaster.Close()) }) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) ccfg := evmtest.NewChainScopedConfig(t, cfg) evmcfg := txmgr.NewEvmTxmConfig(ccfg.EVM()) @@ -605,7 +591,6 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi ccfg.EVM().Transactions(), cfg.Database().Listener(), ethKeyStore, - eventBroadcaster, txBuilder, nil, logger.TestLogger(t), @@ -1113,17 +1098,12 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // same as the parent test, but callback is set by ctor t.Run("callback set by ctor", func(t *testing.T) { - eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) - err := eventBroadcaster.Start(testutils.Context(t)) - require.NoError(t, err) - t.Cleanup(func() { assert.NoError(t, eventBroadcaster.Close()) }) lggr := logger.TestLogger(t) estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr), evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), evmcfg.EVM().GasEstimator(), ethKeyStore, estimator) localNextNonce = getLocalNextNonce(t, eb, fromAddress) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() - eb2 := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), evmcfg.EVM().Transactions(), evmcfg.Database().Listener(), ethKeyStore, eventBroadcaster, txBuilder, nil, lggr, &testCheckerFactory{}, false) - require.NoError(t, err) + eb2 := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), evmcfg.EVM().Transactions(), evmcfg.Database().Listener(), ethKeyStore, txBuilder, nil, lggr, &testCheckerFactory{}, false) retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1724,29 +1704,6 @@ func TestEthBroadcaster_Trigger(t *testing.T) { eb.Trigger(testutils.NewAddress()) } -func TestEthBroadcaster_EthTxInsertEventCausesTriggerToFire(t *testing.T) { - // NOTE: Testing triggers requires committing transactions and does not work with transactional tests - cfg, db := heavyweight.FullTestDBV2(t, "eth_tx_triggers", nil) - txStore := cltest.NewTestTxStore(t, db, cfg.Database()) - - evmcfg := evmtest.NewChainScopedConfig(t, cfg) - - ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() - _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - eventBroadcaster := cltest.NewEventBroadcaster(t, evmcfg.Database().URL()) - require.NoError(t, eventBroadcaster.Start(testutils.Context(t))) - t.Cleanup(func() { require.NoError(t, eventBroadcaster.Close()) }) - - ethTxInsertListener, err := eventBroadcaster.Subscribe(pg.ChannelInsertOnTx, "") - require.NoError(t, err) - - // Give it some time to start listening - time.Sleep(100 * time.Millisecond) - - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) - gomega.NewWithT(t).Eventually(ethTxInsertListener.Events()).Should(gomega.Receive()) -} - func TestEthBroadcaster_SyncNonce(t *testing.T) { db := pgtest.NewSqlxDB(t) ctx := testutils.Context(t) @@ -1765,11 +1722,6 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { ethNodeNonce := uint64(22) - eventBroadcaster := pgmocks.NewEventBroadcaster(t) - sub := pgmocks.NewSubscription(t) - sub.On("Events").Return(make(<-chan pg.Event)) - sub.On("Close") - eventBroadcaster.On("Subscribe", "evm.insert_on_txes", "").Return(sub, nil) estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr), evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) checkerFactory := &testCheckerFactory{} @@ -1783,7 +1735,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() - eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, eventBroadcaster, txBuilder, nil, lggr, checkerFactory, false) + eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, txBuilder, nil, lggr, checkerFactory, false) err := eb.Start(testutils.Context(t)) assert.NoError(t, err) @@ -1801,7 +1753,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() - eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, eventBroadcaster, txBuilder, txNonceSyncer, lggr, checkerFactory, true) + eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, txBuilder, txNonceSyncer, lggr, checkerFactory, true) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(ethNodeNonce), nil).Once() require.NoError(t, eb.Start(ctx)) @@ -1832,7 +1784,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() - eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, eventBroadcaster, txBuilder, txNonceSyncer, lggr, checkerFactory, true) + eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, txBuilder, txNonceSyncer, lggr, checkerFactory, true) eb.XXXTestDisableUnstartedTxAutoProcessing() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("something exploded")).Once() diff --git a/core/chains/evm/txmgr/builder.go b/core/chains/evm/txmgr/builder.go index 39781e83f4c..9123d1dfc03 100644 --- a/core/chains/evm/txmgr/builder.go +++ b/core/chains/evm/txmgr/builder.go @@ -16,7 +16,6 @@ import ( evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) // NewTxm constructs the necessary dependencies for the EvmTxm (broadcaster, confirmer, etc) and returns a new EvmTxManager @@ -31,7 +30,6 @@ func NewTxm( lggr logger.Logger, logPoller logpoller.LogPoller, keyStore keystore.Eth, - eventBroadcaster pg.EventBroadcaster, estimator gas.EvmFeeEstimator, ) (txm TxManager, err error, @@ -52,7 +50,7 @@ func NewTxm( txmCfg := NewEvmTxmConfig(chainConfig) // wrap Evm specific config feeCfg := NewEvmTxmFeeConfig(fCfg) // wrap Evm specific config txmClient := NewEvmTxmClient(client) // wrap Evm specific client - ethBroadcaster := NewEvmBroadcaster(txStore, txmClient, txmCfg, feeCfg, txConfig, listenerConfig, keyStore, eventBroadcaster, txAttemptBuilder, txNonceSyncer, lggr, checker, chainConfig.NonceAutoSync()) + ethBroadcaster := NewEvmBroadcaster(txStore, txmClient, txmCfg, feeCfg, txConfig, listenerConfig, keyStore, txAttemptBuilder, txNonceSyncer, lggr, checker, chainConfig.NonceAutoSync()) ethConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr) var ethResender *Resender if txConfig.ResendAfterThreshold() > 0 { @@ -123,12 +121,11 @@ func NewEvmBroadcaster( txConfig txmgrtypes.BroadcasterTransactionsConfig, listenerConfig txmgrtypes.BroadcasterListenerConfig, keystore KeyStore, - eventBroadcaster pg.EventBroadcaster, txAttemptBuilder TxAttemptBuilder, nonceSyncer NonceSyncer, logger logger.Logger, checkerFactory TransmitCheckerFactory, autoSyncNonce bool, ) *Broadcaster { - return txmgr.NewBroadcaster(txStore, client, chainConfig, feeConfig, txConfig, listenerConfig, keystore, eventBroadcaster, txAttemptBuilder, nonceSyncer, logger, checkerFactory, autoSyncNonce, stringToGethAddress, evmtypes.GenerateNextNonce) + return txmgr.NewBroadcaster(txStore, client, chainConfig, feeConfig, txConfig, listenerConfig, keystore, txAttemptBuilder, nonceSyncer, logger, checkerFactory, autoSyncNonce, evmtypes.GenerateNextNonce) } diff --git a/core/chains/evm/txmgr/common.go b/core/chains/evm/txmgr/common.go index 5dbb2ef9611..37cc89dd7ac 100644 --- a/core/chains/evm/txmgr/common.go +++ b/core/chains/evm/txmgr/common.go @@ -69,10 +69,3 @@ func batchSendTransactions( } return reqs, now, successfulBroadcast, nil } - -func stringToGethAddress(s string) (common.Address, error) { - if !common.IsHexAddress(s) { - return common.Address{}, fmt.Errorf("invalid hex address: %s", s) - } - return common.HexToAddress(s), nil -} diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 6cb43b27716..e9823ee0214 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -37,12 +37,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - pgmocks "github.com/smartcontractkit/chainlink/v2/core/services/pg/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" ) func makeTestEvmTxm( - t *testing.T, db *sqlx.DB, ethClient evmclient.Client, estimator gas.EvmFeeEstimator, ccfg txmgr.ChainConfig, fcfg txmgr.FeeConfig, txConfig evmconfig.Transactions, dbConfig txmgr.DatabaseConfig, listenerConfig txmgr.ListenerConfig, keyStore keystore.Eth, eventBroadcaster pg.EventBroadcaster) (txmgr.TxManager, error) { + t *testing.T, db *sqlx.DB, ethClient evmclient.Client, estimator gas.EvmFeeEstimator, ccfg txmgr.ChainConfig, fcfg txmgr.FeeConfig, txConfig evmconfig.Transactions, dbConfig txmgr.DatabaseConfig, listenerConfig txmgr.ListenerConfig, keyStore keystore.Eth) (txmgr.TxManager, error) { lggr := logger.TestLogger(t) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) @@ -66,7 +65,6 @@ func makeTestEvmTxm( lggr, lp, keyStore, - eventBroadcaster, estimator) } @@ -83,7 +81,7 @@ func TestTxm_SendNativeToken_DoesNotSendToZero(t *testing.T) { keyStore := cltest.NewKeyStore(t, db, dbConfig).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) - txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), keyStore, nil) + txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), keyStore) require.NoError(t, err) _, err = txm.SendNativeToken(testutils.Context(t), big.NewInt(0), from, to, *value, 21000) @@ -109,7 +107,7 @@ func TestTxm_CreateTransaction(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) - txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst.Eth(), nil) + txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst.Eth()) require.NoError(t, err) t.Run("with queue under capacity inserts eth_tx", func(t *testing.T) { @@ -523,7 +521,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) - txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), etKeyStore, nil) + txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), etKeyStore) require.NoError(t, err) t.Run("if another key has any transactions with insufficient eth errors, transmits as normal", func(t *testing.T) { @@ -567,7 +565,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { Meta: nil, Strategy: strategy, }) - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, payload, etx.EncodedPayload) }) @@ -589,7 +587,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { Meta: nil, Strategy: strategy, }) - assert.NoError(t, err) + require.NoError(t, err) require.Equal(t, payload, etx.EncodedPayload) }) } @@ -599,7 +597,6 @@ func TestTxm_Lifecycle(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) kst := ksmocks.NewEth(t) - eventBroadcaster := pgmocks.NewEventBroadcaster(t) config, dbConfig, evmConfig := makeConfigs(t) config.finalityDepth = uint32(42) @@ -615,16 +612,13 @@ func TestTxm_Lifecycle(t *testing.T) { unsub := cltest.NewAwaiter() kst.On("SubscribeToKeyChanges").Return(keyChangeCh, unsub.ItHappened) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) - txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst, eventBroadcaster) + txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst) require.NoError(t, err) head := cltest.Head(42) // It should not hang or panic txm.OnNewLongestChain(testutils.Context(t), head) - sub := pgmocks.NewSubscription(t) - sub.On("Events").Return(make(<-chan pg.Event)) - eventBroadcaster.On("Subscribe", "evm.insert_on_txes", "").Return(sub, nil) evmConfig.bumpThreshold = uint64(1) require.NoError(t, txm.Start(testutils.Context(t))) @@ -638,7 +632,6 @@ func TestTxm_Lifecycle(t *testing.T) { addr := []gethcommon.Address{keyState.Address.Address()} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addr, nil) - sub.On("Close").Return() ethClient.On("PendingNonceAt", mock.AnythingOfType("*context.cancelCtx"), gethcommon.Address{}).Return(uint64(0), nil).Maybe() keyChangeCh <- struct{}{} @@ -670,14 +663,9 @@ func TestTxm_Reset(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, nil) ethClient.On("BatchCallContextAll", mock.Anything, mock.Anything).Return(nil).Maybe() - eventBroadcaster := pgmocks.NewEventBroadcaster(t) - sub := pgmocks.NewSubscription(t) - sub.On("Events").Return(make(<-chan pg.Event)) - sub.On("Close") - eventBroadcaster.On("Subscribe", "evm.insert_on_txes", "").Return(sub, nil) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) - txm, err := makeTestEvmTxm(t, db, ethClient, estimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), cfg.Database(), cfg.Database().Listener(), kst.Eth(), eventBroadcaster) + txm, err := makeTestEvmTxm(t, db, ethClient, estimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), cfg.Database(), cfg.Database().Listener(), kst.Eth()) require.NoError(t, err) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, addr2) diff --git a/core/services/pg/channels.go b/core/services/pg/channels.go index 736cd407962..1d67dabe523 100644 --- a/core/services/pg/channels.go +++ b/core/services/pg/channels.go @@ -2,7 +2,6 @@ package pg // Postgres channel to listen for new evm.txes const ( - ChannelInsertOnTx = "evm.insert_on_txes" ChannelInsertOnCosmosMsg = "insert_on_cosmos_msg" ChannelInsertOnEVMLogs = "evm.insert_on_logs" ) diff --git a/core/store/migrate/migrations/0206_remove_tx_insert_trigger.sql b/core/store/migrate/migrations/0206_remove_tx_insert_trigger.sql new file mode 100644 index 00000000000..94b2e4aa8a6 --- /dev/null +++ b/core/store/migrate/migrations/0206_remove_tx_insert_trigger.sql @@ -0,0 +1,18 @@ +-- +goose Up +DROP TRIGGER IF EXISTS notify_tx_insertion on evm.txes; +DROP FUNCTION IF EXISTS evm.notifyethtxinsertion(); + + +-- +goose Down +-- +goose StatementBegin +CREATE OR REPLACE FUNCTION evm.notifytxinsertion() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + PERFORM pg_notify('evm.insert_on_txes'::text, encode(NEW.from_address, 'hex')); + RETURN NULL; + END + $$; + +CREATE TRIGGER notify_tx_insertion AFTER INSERT ON evm.txes FOR EACH ROW EXECUTE PROCEDURE evm.notifytxinsertion(); +-- +goose StatementEnd \ No newline at end of file From 24de8afe09954c45c16d2143186e042bcb300788 Mon Sep 17 00:00:00 2001 From: Morgan Kuphal <87319522+KuphJr@users.noreply.github.com> Date: Tue, 31 Oct 2023 18:55:58 -0500 Subject: [PATCH 045/271] Get request logs from past n blocks (#11052) * Get request logs from past n blocks * Separate confirmations setting for reqs & resps * Filter already detected reqs & resps * validate pastBlocksToPoll * Fixed bugs which appeared during tests * Addressed feedback * Added test * Added comment to config * Fixed log & removed const * Used const values for defaults * Address feedback * Fixed types for logPoller * Added tests for FilterPreviouslyDetectedEvents * Added additional test assertions * Fixed lint errors --- core/scripts/functions/templates/oracle.toml | 1 + .../ocr2/plugins/functions/config/config.go | 4 + .../relay/evm/functions/logpoller_wrapper.go | 174 +++++++++++++----- .../evm/functions/logpoller_wrapper_test.go | 127 ++++++++++++- 4 files changed, 250 insertions(+), 56 deletions(-) diff --git a/core/scripts/functions/templates/oracle.toml b/core/scripts/functions/templates/oracle.toml index 4739252d68e..d21fe4a5e87 100644 --- a/core/scripts/functions/templates/oracle.toml +++ b/core/scripts/functions/templates/oracle.toml @@ -36,6 +36,7 @@ requestTimeoutSec = 300 maxRequestSizesList = [30_720, 51_200, 102_400, 204_800, 512_000, 1_048_576, 2_097_152, 3_145_728, 5_242_880, 10_485_760] maxSecretsSizesList = [10_240, 20_480, 51_200, 102_400, 307_200, 512_000, 1_048_576, 2_097_152] minimumSubscriptionBalance = "2 link" +pastBlocksToPoll = 25 [pluginConfig.OnchainAllowlist] diff --git a/core/services/ocr2/plugins/functions/config/config.go b/core/services/ocr2/plugins/functions/config/config.go index 3f35d1dba9b..0978500deb5 100644 --- a/core/services/ocr2/plugins/functions/config/config.go +++ b/core/services/ocr2/plugins/functions/config/config.go @@ -23,7 +23,11 @@ type PluginConfig struct { EnableRequestSignatureCheck bool `json:"enableRequestSignatureCheck"` DONID string `json:"donID"` ContractVersion uint32 `json:"contractVersion"` + MinRequestConfirmations uint32 `json:"minRequestConfirmations"` + MinResponseConfirmations uint32 `json:"minResponseConfirmations"` MinIncomingConfirmations uint32 `json:"minIncomingConfirmations"` + PastBlocksToPoll uint32 `json:"pastBlocksToPoll"` + LogPollerCacheDurationSec uint32 `json:"logPollerCacheDurationSec"` // Duration to cache previously detected request or response logs such that they can be filtered when calling logpoller_wrapper.LatestEvents() RequestTimeoutSec uint32 `json:"requestTimeoutSec"` RequestTimeoutCheckFrequencySec uint32 `json:"requestTimeoutCheckFrequencySec"` RequestTimeoutBatchLookupSize uint32 `json:"requestTimeoutBatchLookupSize"` diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index d355bd6569b..6193f4ba862 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -24,21 +24,39 @@ import ( type logPollerWrapper struct { services.StateMachine - routerContract *functions_router.FunctionsRouter - pluginConfig config.PluginConfig - client client.Client - logPoller logpoller.LogPoller - subscribers map[string]evmRelayTypes.RouteUpdateSubscriber - activeCoordinator common.Address - proposedCoordinator common.Address - blockOffset int64 - nextBlock int64 - mu sync.Mutex - closeWait sync.WaitGroup - stopCh utils.StopChan - lggr logger.Logger + routerContract *functions_router.FunctionsRouter + pluginConfig config.PluginConfig + client client.Client + logPoller logpoller.LogPoller + subscribers map[string]evmRelayTypes.RouteUpdateSubscriber + activeCoordinator common.Address + proposedCoordinator common.Address + requestBlockOffset int64 + responseBlockOffset int64 + pastBlocksToPoll int64 + logPollerCacheDurationSec int64 + detectedRequests detectedEvents + detectedResponses detectedEvents + mu sync.Mutex + closeWait sync.WaitGroup + stopCh utils.StopChan + lggr logger.Logger } +type detectedEvent struct { + requestId [32]byte + timeDetected time.Time +} + +type detectedEvents struct { + isPreviouslyDetected map[[32]byte]struct{} + detectedEventsOrdered []detectedEvent +} + +const logPollerCacheDurationSecDefault = 300 +const pastBlocksToPollDefault = 50 +const maxLogsToProcess = 1000 + var _ evmRelayTypes.LogPollerWrapper = &logPollerWrapper{} func NewLogPollerWrapper(routerContractAddress common.Address, pluginConfig config.PluginConfig, client client.Client, logPoller logpoller.LogPoller, lggr logger.Logger) (evmRelayTypes.LogPollerWrapper, error) { @@ -48,18 +66,48 @@ func NewLogPollerWrapper(routerContractAddress common.Address, pluginConfig conf } blockOffset := int64(pluginConfig.MinIncomingConfirmations) - 1 if blockOffset < 0 { + lggr.Warnw("invalid minIncomingConfirmations, using 1 instead", "minIncomingConfirmations", pluginConfig.MinIncomingConfirmations) blockOffset = 0 } + requestBlockOffset := int64(pluginConfig.MinRequestConfirmations) - 1 + if requestBlockOffset < 0 { + lggr.Warnw("invalid minRequestConfirmations, using minIncomingConfirmations instead", "minRequestConfirmations", pluginConfig.MinRequestConfirmations) + requestBlockOffset = blockOffset + } + responseBlockOffset := int64(pluginConfig.MinResponseConfirmations) - 1 + if responseBlockOffset < 0 { + lggr.Warnw("invalid minResponseConfirmations, using minIncomingConfirmations instead", "minResponseConfirmations", pluginConfig.MinResponseConfirmations) + responseBlockOffset = blockOffset + } + logPollerCacheDurationSec := int64(pluginConfig.LogPollerCacheDurationSec) + if logPollerCacheDurationSec <= 0 { + lggr.Warnw("invalid logPollerCacheDuration, using 300 instead", "logPollerCacheDurationSec", logPollerCacheDurationSec) + logPollerCacheDurationSec = logPollerCacheDurationSecDefault + } + pastBlocksToPoll := int64(pluginConfig.PastBlocksToPoll) + if pastBlocksToPoll <= 0 { + lggr.Warnw("invalid pastBlocksToPoll, using 50 instead", "pastBlocksToPoll", pastBlocksToPoll) + pastBlocksToPoll = pastBlocksToPollDefault + } + if blockOffset >= pastBlocksToPoll || requestBlockOffset >= pastBlocksToPoll || responseBlockOffset >= pastBlocksToPoll { + lggr.Errorw("invalid config: number of required confirmation blocks >= pastBlocksToPoll", "pastBlocksToPoll", pastBlocksToPoll, "minIncomingConfirmations", pluginConfig.MinIncomingConfirmations, "minRequestConfirmations", pluginConfig.MinRequestConfirmations, "minResponseConfirmations", pluginConfig.MinResponseConfirmations) + return nil, errors.Errorf("invalid config: number of required confirmation blocks >= pastBlocksToPoll") + } return &logPollerWrapper{ - routerContract: routerContract, - pluginConfig: pluginConfig, - blockOffset: blockOffset, - logPoller: logPoller, - client: client, - subscribers: make(map[string]evmRelayTypes.RouteUpdateSubscriber), - stopCh: make(utils.StopChan), - lggr: lggr, + routerContract: routerContract, + pluginConfig: pluginConfig, + requestBlockOffset: requestBlockOffset, + responseBlockOffset: responseBlockOffset, + pastBlocksToPoll: pastBlocksToPoll, + logPollerCacheDurationSec: logPollerCacheDurationSec, + detectedRequests: detectedEvents{isPreviouslyDetected: make(map[[32]byte]struct{})}, + detectedResponses: detectedEvents{isPreviouslyDetected: make(map[[32]byte]struct{})}, + logPoller: logPoller, + client: client, + subscribers: make(map[string]evmRelayTypes.RouteUpdateSubscriber), + stopCh: make(utils.StopChan), + lggr: lggr, }, nil } @@ -68,20 +116,11 @@ func (l *logPollerWrapper) Start(context.Context) error { l.lggr.Infow("starting LogPollerWrapper", "routerContract", l.routerContract.Address().Hex(), "contractVersion", l.pluginConfig.ContractVersion) l.mu.Lock() defer l.mu.Unlock() - if l.pluginConfig.ContractVersion == 0 { - l.activeCoordinator = l.routerContract.Address() - l.proposedCoordinator = l.routerContract.Address() - } else if l.pluginConfig.ContractVersion == 1 { - nextBlock, err := l.logPoller.LatestBlock() - if err != nil { - l.lggr.Errorw("LogPollerWrapper: LatestBlock() failed, starting from 0", "error", err) - } else { - l.lggr.Debugw("LogPollerWrapper: LatestBlock() got starting block", "block", nextBlock) - l.nextBlock = nextBlock.BlockNumber - l.blockOffset - } - l.closeWait.Add(1) - go l.checkForRouteUpdates() + if l.pluginConfig.ContractVersion != 1 { + return errors.New("only contract version 1 is supported") } + l.closeWait.Add(1) + go l.checkForRouteUpdates() return nil }) } @@ -117,16 +156,15 @@ func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmR if l.proposedCoordinator != (common.Address{}) && l.activeCoordinator != l.proposedCoordinator { coordinators = append(coordinators, l.proposedCoordinator) } - nextBlock := l.nextBlock latest, err := l.logPoller.LatestBlock() if err != nil { l.mu.Unlock() return nil, nil, err } - latestBlockNumber := latest.BlockNumber - latestBlockNumber -= l.blockOffset - if latestBlockNumber >= nextBlock { - l.nextBlock = latestBlockNumber + 1 + latestBlockNum := latest.BlockNumber + startBlockNum := latestBlockNum - l.pastBlocksToPoll + if startBlockNum < 0 { + startBlockNum = 0 } l.mu.Unlock() @@ -137,22 +175,24 @@ func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmR l.lggr.Debug("LatestEvents: no non-zero coordinators to check") return resultsReq, resultsResp, errors.New("no non-zero coordinators to check") } - if latestBlockNumber < nextBlock { - l.lggr.Debugw("LatestEvents: no new blocks to check", "latest", latest, "nextBlock", nextBlock) - return resultsReq, resultsResp, nil - } for _, coordinator := range coordinators { - requestLogs, err := l.logPoller.Logs(nextBlock, latestBlockNumber, functions_coordinator.FunctionsCoordinatorOracleRequest{}.Topic(), coordinator) + requestEndBlock := latestBlockNum - l.requestBlockOffset + requestLogs, err := l.logPoller.Logs(startBlockNum, requestEndBlock, functions_coordinator.FunctionsCoordinatorOracleRequest{}.Topic(), coordinator) if err != nil { - l.lggr.Errorw("LatestEvents: fetching request logs from LogPoller failed", "latest", latest, "nextBlock", nextBlock) + l.lggr.Errorw("LatestEvents: fetching request logs from LogPoller failed", "startBlock", startBlockNum, "endBlock", requestEndBlock) return nil, nil, err } - responseLogs, err := l.logPoller.Logs(nextBlock, latestBlockNumber, functions_coordinator.FunctionsCoordinatorOracleResponse{}.Topic(), coordinator) + l.lggr.Debugw("LatestEvents: fetched request logs", "nRequestLogs", len(requestLogs), "latestBlock", latest, "startBlock", startBlockNum, "endBlock", requestEndBlock) + requestLogs = l.filterPreviouslyDetectedEvents(requestLogs, &l.detectedRequests, "requests") + responseEndBlock := latestBlockNum - l.responseBlockOffset + responseLogs, err := l.logPoller.Logs(startBlockNum, responseEndBlock, functions_coordinator.FunctionsCoordinatorOracleResponse{}.Topic(), coordinator) if err != nil { - l.lggr.Errorw("LatestEvents: fetching response logs from LogPoller failed", "latest", latest, "nextBlock", nextBlock) + l.lggr.Errorw("LatestEvents: fetching response logs from LogPoller failed", "startBlock", startBlockNum, "endBlock", responseEndBlock) return nil, nil, err } + l.lggr.Debugw("LatestEvents: fetched request logs", "nResponseLogs", len(responseLogs), "latestBlock", latest, "startBlock", startBlockNum, "endBlock", responseEndBlock) + responseLogs = l.filterPreviouslyDetectedEvents(responseLogs, &l.detectedResponses, "responses") parsingContract, err := functions_coordinator.NewFunctionsCoordinator(coordinator, l.client) if err != nil { @@ -165,7 +205,7 @@ func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmR gethLog := log.ToGethLog() oracleRequest, err := parsingContract.ParseOracleRequest(gethLog) if err != nil { - l.lggr.Errorw("LatestEvents: failed to parse a request log, skipping") + l.lggr.Errorw("LatestEvents: failed to parse a request log, skipping", "err", err) continue } @@ -241,10 +281,46 @@ func (l *logPollerWrapper) LatestEvents() ([]evmRelayTypes.OracleRequest, []evmR } } - l.lggr.Debugw("LatestEvents: done", "nRequestLogs", len(resultsReq), "nResponseLogs", len(resultsResp), "nextBlock", nextBlock, "latest", latest) + l.lggr.Debugw("LatestEvents: done", "nRequestLogs", len(resultsReq), "nResponseLogs", len(resultsResp), "startBlock", startBlockNum, "endBlock", latestBlockNum) return resultsReq, resultsResp, nil } +func (l *logPollerWrapper) filterPreviouslyDetectedEvents(logs []logpoller.Log, detectedEvents *detectedEvents, filterType string) []logpoller.Log { + if len(logs) > maxLogsToProcess { + l.lggr.Errorw("filterPreviouslyDetectedEvents: too many logs to process, only processing latest maxLogsToProcess logs", "filterType", filterType, "nLogs", len(logs), "maxLogsToProcess", maxLogsToProcess) + logs = logs[len(logs)-maxLogsToProcess:] + } + l.mu.Lock() + defer l.mu.Unlock() + filteredLogs := []logpoller.Log{} + for _, log := range logs { + var requestId [32]byte + if len(log.Topics) < 2 || len(log.Topics[1]) != 32 { + l.lggr.Errorw("filterPreviouslyDetectedEvents: invalid log, skipping", "filterType", filterType, "log", log) + continue + } + copy(requestId[:], log.Topics[1]) // requestId is the second topic (1st topic is the event signature) + if _, ok := detectedEvents.isPreviouslyDetected[requestId]; !ok { + filteredLogs = append(filteredLogs, log) + detectedEvents.isPreviouslyDetected[requestId] = struct{}{} + detectedEvents.detectedEventsOrdered = append(detectedEvents.detectedEventsOrdered, detectedEvent{requestId: requestId, timeDetected: time.Now()}) + } + } + expiredRequests := 0 + for _, detectedEvent := range detectedEvents.detectedEventsOrdered { + expirationTime := time.Now().Add(-time.Second * time.Duration(l.logPollerCacheDurationSec)) + if detectedEvent.timeDetected.Before(expirationTime) { + delete(detectedEvents.isPreviouslyDetected, detectedEvent.requestId) + expiredRequests++ + } else { + break + } + } + detectedEvents.detectedEventsOrdered = detectedEvents.detectedEventsOrdered[expiredRequests:] + l.lggr.Debugw("filterPreviouslyDetectedEvents: done", "filterType", filterType, "nLogs", len(logs), "nFilteredLogs", len(filteredLogs), "nExpiredRequests", expiredRequests, "previouslyDetectedCacheSize", len(detectedEvents.detectedEventsOrdered)) + return filteredLogs +} + // "internal" method called only by EVM relayer components func (l *logPollerWrapper) SubscribeToUpdates(subscriberName string, subscriber evmRelayTypes.RouteUpdateSubscriber) { if l.pluginConfig.ContractVersion == 0 { diff --git a/core/services/relay/evm/functions/logpoller_wrapper_test.go b/core/services/relay/evm/functions/logpoller_wrapper_test.go index c91c3c49aad..2108e822d5e 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper_test.go +++ b/core/services/relay/evm/functions/logpoller_wrapper_test.go @@ -1,22 +1,24 @@ -package functions_test +package functions import ( + "crypto/rand" "encoding/hex" "sync" "testing" + "time" "github.com/ethereum/go-ethereum/common" - gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) @@ -57,17 +59,34 @@ func setUp(t *testing.T, updateFrequencySec uint32) (*lpmocks.LogPoller, types.L ContractUpdateCheckFrequencySec: updateFrequencySec, ContractVersion: 1, } - lpWrapper, err := functions.NewLogPollerWrapper(gethcommon.Address{}, config, client, lp, lggr) + lpWrapper, err := NewLogPollerWrapper(common.Address{}, config, client, lp, lggr) require.NoError(t, err) - lp.On("LatestBlock").Return(logpoller.LogPollerBlock{BlockNumber: int64(100)}, nil) - return lp, lpWrapper, client } +func getMockedRequestLog(t *testing.T) logpoller.Log { + // NOTE: Change this to be a more readable log generation + data, err := hex.DecodeString("000000000000000000000000c113ba31b0080f940ca5812bbccc1e038ea9efb40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c113ba31b0080f940ca5812bbccc1e038ea9efb4000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001117082cd81744eb9504dc37f53a86db7e3fb24929b8e7507b097d501ab5b315fb20e0000000000000000000000001b4f2b0e6363097f413c249910d5bc632993ed08000000000000000000000000000000000000000000000000015bcf880382c000000000000000000000000000665785a800593e8fa915208c1ce62f6e57fd75ba0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000001117000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f588000000000000000000000000000000000000000000000000000000000000c350000000000000000000000000000000000000000000000000000000000000021c00000000000000000000000000000000000000000000000000000000000008866c636f64654c6f636174696f6ec258200000000000000000000000000000000000000000000000000000000000000000686c616e6775616765c25820000000000000000000000000000000000000000000000000000000000000000066736f757263657907d0633836366665643238326533313137636466303836633934396662613133643834666331376131656335353934656361643034353133646632326137623538356333363763633132326236373138306334383737303435616235383033373463353066313862346564386132346131323437383532363731623030633035663237373163663036363632333663333236393939323139363866323833346438626462616266306661643165313237613837643237363936323831643965656539326134646263316337356137316136656333613135356438633230616661643064623432383362613433353736303734653035633433633561653061656466643332323838346536613231386466323430323630316436356437316131303061633065376563643037663565646364633535643562373932646130626632353665623038363139336463376431333965613764373965653531653831356465333834386565643363366330353837393265366461333434363738626436373239346636643639656564356132663836323835343965616530323235323835346232666361333635646265623032383433386537326234383465383864316136646563373933633739656265353834666465363465663831383363313365386231623735663037636532303963393138633532643637613735343862653236366433663964316439656132613162303166633838376231316162383739663164333861373833303563373031316533643938346130393863663634383931316536653065383038396365306130363230393136663134323935343036336630376239343931326435666331393366303138633764616135363136323562313966376463323036663930353365623234643036323234616164326338623430646162663631656166666635326234653831373239353837333830313561643730663739316663643864333739343035353737393563383937363164636665333639373938373437353439633234643530646464303563623337613465613863353162306530313032363738643433653766306563353039653434633564343764353335626261363831303936383264643864653439326532363633646336653133653532383539663664336565306533633430336236366362653338643236366137356163373639363863613465653331396166363965373431333137393162653630376537353832373430366164653038306335623239653665343262386563386137373761663865383166336234616337626263666531643066616633393338613664353061316561633835643933643234343066313863333037356237306433626134663930323836396439383937663266636562626262366263646439333436633336633663643838626434336265306562333134323562343665613765386338336638386230363933343836383666366134313839623535666132666431396634326264333730313634616339356530303635656461663130373761633131366632393930303833616631333839636661666336613433323439376531363437393762633738616633366335613435366136646661326636626430626639326136613930366130653930313130626266323265613066333163663364353132663466303331653236343330633831663935656431323362323938356266623830623161396432646337306232356264613961386261303839323833666166663634383661316231646235613938353564346237363966623835663531353063393935306462303964373536326537353133633234653531636163366634366634633231636234373561613937363166666466626434656138613531626465613432383037313466363538393630656336643139656539373237626339316635313665346466306665346264613762623035343161393462326334396636323938616132396337656130646662653635346632306437663164323239633066303262356535326137363031376237306439383232643533383166623966613166393361353861376338383632326631326462643363623937323363626132313639633337643538303939336333663666393065323039336331336130363132323334303064393731363031656262313631343332613966666333373033396562663537326364326566666635636562323539346236346462336261616431633734663532653938343938353964383363313238353465376263393764363432363464653931343735386333386438383739343132333937653263643534653431366234373962363331623830626633306266653062366239353564393066356362303435346361373531303963393938366330636536316165356566376534653433353036313432633633646235363862383634353139623463306636366137633161376661336538666431323231376666336665383164663830643138386232646334343833356132663332323733666133353139633531343764643233353763326161346336326461386238353232306535386130333565373662633133316634623734376632663731643263663933376431303832356138316533623963323136663962316134646431663239383463656635656363656265353530363662363061373263363063323864303336653766386635323131343735386638326366323330646636363930636364617267739f64617267316461726732ff6f736563726574734c6f636174696f6ec2582000000000000000000000000000000000000000000000000000000000000000016773656372657473430102030000000000000000000000000000000000000000000000000000") + require.NoError(t, err) + topic0, err := hex.DecodeString("bf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff") + require.NoError(t, err) + // Create a random requestID + topic1 := make([]byte, 32) + _, err = rand.Read(topic1) + require.NoError(t, err) + topic2, err := hex.DecodeString("000000000000000000000000665785a800593e8fa915208c1ce62f6e57fd75ba") + require.NoError(t, err) + return logpoller.Log{ + Topics: [][]byte{topic0, topic1, topic2}, + Data: data, + } +} + func TestLogPollerWrapper_SingleSubscriberEmptyEvents(t *testing.T) { t.Parallel() lp, lpWrapper, client := setUp(t, 100_000) // check only once + lp.On("LatestBlock").Return(logpoller.LogPollerBlock{BlockNumber: int64(100)}, nil) lp.On("Logs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]logpoller.Log{}, nil) client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(addr(t, "01"), nil) @@ -87,7 +106,8 @@ func TestLogPollerWrapper_SingleSubscriberEmptyEvents(t *testing.T) { func TestLogPollerWrapper_ErrorOnZeroAddresses(t *testing.T) { t.Parallel() - _, lpWrapper, client := setUp(t, 100_000) // check only once + lp, lpWrapper, client := setUp(t, 100_000) // check only once + lp.On("LatestBlock").Return(logpoller.LogPollerBlock{BlockNumber: int64(100)}, nil) client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(addr(t, "00"), nil) @@ -96,3 +116,96 @@ func TestLogPollerWrapper_ErrorOnZeroAddresses(t *testing.T) { require.Error(t, err) lpWrapper.Close() } + +func TestLogPollerWrapper_LatestEvents_ReorgHandling(t *testing.T) { + t.Parallel() + lp, lpWrapper, client := setUp(t, 100_000) + lp.On("LatestBlock").Return(logpoller.LogPollerBlock{BlockNumber: int64(100)}, nil) + client.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(addr(t, "01"), nil) + lp.On("RegisterFilter", mock.Anything).Return(nil) + subscriber := newSubscriber(1) + lpWrapper.SubscribeToUpdates("mock_subscriber", subscriber) + mockedLog := getMockedRequestLog(t) + // All logPoller queries for responses return none + lp.On("Logs", mock.Anything, mock.Anything, functions_coordinator.FunctionsCoordinatorOracleResponse{}.Topic(), mock.Anything).Return([]logpoller.Log{}, nil) + // On the first logPoller query for requests, the request log appears + lp.On("Logs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]logpoller.Log{mockedLog}, nil).Once() + // On the 2nd query, the request log disappears + lp.On("Logs", mock.Anything, mock.Anything, functions_coordinator.FunctionsCoordinatorOracleRequest{}.Topic(), mock.Anything).Return([]logpoller.Log{}, nil).Once() + // On the 3rd query, the original request log appears again + lp.On("Logs", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]logpoller.Log{mockedLog}, nil).Once() + + require.NoError(t, lpWrapper.Start(testutils.Context(t))) + subscriber.updates.Wait() + + oracleRequests, _, err := lpWrapper.LatestEvents() + require.NoError(t, err) + assert.Equal(t, 1, len(oracleRequests)) + oracleRequests, _, err = lpWrapper.LatestEvents() + require.NoError(t, err) + assert.Equal(t, 0, len(oracleRequests)) + require.NoError(t, err) + oracleRequests, _, err = lpWrapper.LatestEvents() + require.NoError(t, err) + assert.Equal(t, 0, len(oracleRequests)) +} + +func TestLogPollerWrapper_FilterPreviouslyDetectedEvents_TruncatesLogs(t *testing.T) { + t.Parallel() + _, lpWrapper, _ := setUp(t, 100_000) + + inputLogs := make([]logpoller.Log, maxLogsToProcess+100) + for i := 0; i < 1100; i++ { + inputLogs[i] = getMockedRequestLog(t) + } + + functionsLpWrapper := lpWrapper.(*logPollerWrapper) + mockedDetectedEvents := detectedEvents{isPreviouslyDetected: make(map[[32]byte]struct{})} + outputLogs := functionsLpWrapper.filterPreviouslyDetectedEvents(inputLogs, &mockedDetectedEvents, "request") + + assert.Equal(t, maxLogsToProcess, len(outputLogs)) + assert.Equal(t, 1000, len(mockedDetectedEvents.detectedEventsOrdered)) + assert.Equal(t, 1000, len(mockedDetectedEvents.isPreviouslyDetected)) +} + +func TestLogPollerWrapper_FilterPreviouslyDetectedEvents_SkipsInvalidLog(t *testing.T) { + t.Parallel() + _, lpWrapper, _ := setUp(t, 100_000) + inputLogs := []logpoller.Log{getMockedRequestLog(t)} + inputLogs[0].Topics = [][]byte{[]byte("invalid topic")} + mockedDetectedEvents := detectedEvents{isPreviouslyDetected: make(map[[32]byte]struct{})} + + functionsLpWrapper := lpWrapper.(*logPollerWrapper) + outputLogs := functionsLpWrapper.filterPreviouslyDetectedEvents(inputLogs, &mockedDetectedEvents, "request") + + assert.Equal(t, 0, len(outputLogs)) + assert.Equal(t, 0, len(mockedDetectedEvents.detectedEventsOrdered)) + assert.Equal(t, 0, len(mockedDetectedEvents.isPreviouslyDetected)) +} + +func TestLogPollerWrapper_FilterPreviouslyDetectedEvents_FiltersPreviouslyDetectedEvent(t *testing.T) { + t.Parallel() + _, lpWrapper, _ := setUp(t, 100_000) + mockedRequestLog := getMockedRequestLog(t) + inputLogs := []logpoller.Log{mockedRequestLog} + var mockedRequestId [32]byte + copy(mockedRequestId[:], mockedRequestLog.Topics[1]) + + mockedDetectedEvents := detectedEvents{ + isPreviouslyDetected: make(map[[32]byte]struct{}), + detectedEventsOrdered: make([]detectedEvent, 1), + } + mockedDetectedEvents.isPreviouslyDetected[mockedRequestId] = struct{}{} + mockedDetectedEvents.detectedEventsOrdered[0] = detectedEvent{ + requestId: mockedRequestId, + timeDetected: time.Now().Add(-time.Second * time.Duration(logPollerCacheDurationSecDefault+1)), + } + + functionsLpWrapper := lpWrapper.(*logPollerWrapper) + outputLogs := functionsLpWrapper.filterPreviouslyDetectedEvents(inputLogs, &mockedDetectedEvents, "request") + + assert.Equal(t, 0, len(outputLogs)) + // Ensure that expired events are removed from the cache + assert.Equal(t, 0, len(mockedDetectedEvents.detectedEventsOrdered)) + assert.Equal(t, 0, len(mockedDetectedEvents.isPreviouslyDetected)) +} From 45844de10fcc4a31436cf8f9f84ab555e2a0f044 Mon Sep 17 00:00:00 2001 From: David Cauchi <13139524+davidcauchi@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:06:12 +0100 Subject: [PATCH 046/271] Add base goerli benchmark option (#11089) * Add base goerli benchmark option * Add config * Add to op stack deployer * Add FlatFeeMicroLink --- .github/workflows/automation-benchmark-tests.yml | 1 + integration-tests/benchmark/keeper_test.go | 7 +++++++ integration-tests/contracts/contract_deployer.go | 2 ++ 3 files changed, 10 insertions(+) diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index f23102f1ee6..5c4dced9342 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -24,6 +24,7 @@ on: - OPTIMISM_GOERLI - MUMBAI - SEPOLIA + - BASE_GOERLI TestInputs: description: TestInputs required: false diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 6fbf929e47d..55f769e73b4 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -161,6 +161,7 @@ func TestAutomationBenchmark(t *testing.T) { RegistryVersions: registryVersions, KeeperRegistrySettings: &contracts.KeeperRegistrySettings{ PaymentPremiumPPB: uint32(0), + FlatFeeMicroLINK: uint32(40000), BlockCountPerTurn: big.NewInt(100), CheckGasLimit: uint32(45_000_000), //45M StalenessSeconds: big.NewInt(90_000), @@ -282,6 +283,12 @@ var networkConfig = map[string]NetworkConfig{ deltaStage: time.Duration(0), funding: big.NewFloat(ChainlinkNodeFunding), }, + "BaseGoerli": { + upkeepSLA: int64(60), + blockTime: 2 * time.Second, + deltaStage: 20 * time.Second, + funding: big.NewFloat(ChainlinkNodeFunding), + }, } func getEnv(key, fallback string) string { diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 710422891c4..94f6c733869 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -883,6 +883,8 @@ func (e *EthereumContractDeployer) DeployKeeperRegistry( //Optimism payment model case big.NewInt(420): mode = uint8(2) + case big.NewInt(84531): + mode = uint8(2) default: mode = uint8(0) } From fda43935a2582cbe75d38088ef523bcf6091841f Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 1 Nov 2023 12:40:55 -0700 Subject: [PATCH 047/271] add an empty index.html under core/web/assets to avoid running make commands (#11114) --- .github/workflows/automation-ondemand-tests.yml | 2 +- .github/workflows/integration-chaos-tests.yml | 2 +- .github/workflows/integration-tests.yml | 8 ++++---- GNUmakefile | 4 ---- core/web/assets/index.html | 0 integration-tests/Makefile | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) create mode 100644 core/web/assets/index.html diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index ac0e34e0834..fb8adcfdb6f 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -182,7 +182,7 @@ jobs: UPGRADE_VERSION: ${{ steps.determine-build.outputs.upgrade_version }} UPGRADE_IMAGE: ${{ steps.determine-build.outputs.upgrade_image }} with: - test_command_to_run: make test_need_operator_assets && cd ./integration-tests && go test -timeout 60m -count=1 -json -test.parallel=${{ matrix.tests.nodes }} ${{ matrix.tests.command }} 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 60m -count=1 -json -test.parallel=${{ matrix.tests.nodes }} ${{ matrix.tests.command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ steps.determine-build.outputs.image }} cl_image_tag: ${{ steps.determine-build.outputs.version }} diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index 648d5f9daa3..4ad985e9154 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -111,7 +111,7 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 with: - test_command_to_run: make test_need_operator_assets && cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 11 ./chaos 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 11 ./chaos 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 5074fc35b9a..b66ab58d554 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -218,7 +218,7 @@ jobs: PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: - test_command_to_run: make test_need_operator_assets && cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} @@ -417,7 +417,7 @@ jobs: PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: - test_command_to_run: make test_need_operator_assets && cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=${{ matrix.product.nodes }} ${{ steps.build-go-test-command.outputs.run_command }} 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }} @@ -574,7 +574,7 @@ jobs: - name: Run Migration Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 with: - test_command_to_run: make test_need_operator_assets && cd ./integration-tests && go test -timeout 30m -count=1 -json ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ steps.get_latest_version.outputs.latest_version }} @@ -935,7 +935,7 @@ jobs: PYROSCOPE_ENVIRONMENT: ci-smoke-ocr-evm-${{ matrix.testnet }} # TODO: Only for OCR for now PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: - test_command_to_run: make test_need_operator_assets && cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/ocr_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/ocr_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} diff --git a/GNUmakefile b/GNUmakefile index 957df96ce45..32f74e285e9 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -127,10 +127,6 @@ telemetry-protobuf: $(telemetry-protobuf) ## Generate telemetry protocol buffers --go-wsrpc_opt=paths=source_relative \ ./core/services/synchronization/telem/*.proto -.PHONY: test_need_operator_assets -test_need_operator_assets: ## Add blank file in web assets if operator ui has not been built - [ -f "./core/web/assets/index.html" ] || mkdir ./core/web/assets && touch ./core/web/assets/index.html - .PHONY: config-docs config-docs: ## Generate core node configuration documentation go run ./core/config/docs/cmd/generate -o ./docs/ diff --git a/core/web/assets/index.html b/core/web/assets/index.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/integration-tests/Makefile b/integration-tests/Makefile index f26518c0076..257331afcfd 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -118,7 +118,7 @@ test_chaos_verbose: ## Run all smoke tests with verbose logging # Performance .PHONY: test_perf -test_perf: test_need_operator_assets ## Run core node performance tests. +test_perf: ## Run core node performance tests. TEST_LOG_LEVEL="disabled" \ SELECTED_NETWORKS="SIMULATED,SIMULATED_1,SIMULATED_2" \ go test -timeout 1h -count=1 -json $(args) ./performance 2>&1 | tee /tmp/gotest.log | gotestfmt From 08c9f89bcabbde28d40349e010f629ea840bfd9a Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Wed, 1 Nov 2023 22:41:45 +0200 Subject: [PATCH 048/271] Introduce generalized multi node client (#10907) * Introduce generalized multi node client * Export EVM RPC client * Unexport clientAPI interface * Add BlockDifficulty to mocks * Unexport node state * Rename error parsing * Nit fixes * Rename error classification functions * Update NodeSelection names * Deprecate StartStopOnce --- common/chains/client/models.go | 22 + common/client/multi_node.go | 669 +++++++++++ common/client/node.go | 282 +++++ common/client/node_fsm.go | 266 +++++ common/client/node_lifecycle.go | 431 +++++++ common/client/node_selector_highest_head.go | 41 + common/client/node_selector_priority_level.go | 129 ++ common/client/node_selector_round_robin.go | 50 + .../client/node_selector_total_difficulty.go | 54 + common/client/send_only_node.go | 183 +++ common/client/send_only_node_lifecycle.go | 66 ++ common/client/types.go | 133 +++ common/headtracker/types/mocks/head.go | 17 + common/types/head.go | 6 + common/types/mocks/head.go | 17 + common/types/receipt.go | 14 + core/chains/evm/chain.go | 18 + core/chains/evm/client/chain_client.go | 274 +++++ core/chains/evm/client/client.go | 2 +- core/chains/evm/client/client_test.go | 442 ++++--- core/chains/evm/client/errors.go | 14 +- core/chains/evm/client/helpers_test.go | 65 + core/chains/evm/client/rpc_client.go | 1046 +++++++++++++++++ core/chains/evm/txmgr/client.go | 2 +- core/chains/evm/types/models.go | 4 + 25 files changed, 4110 insertions(+), 137 deletions(-) create mode 100644 common/client/multi_node.go create mode 100644 common/client/node.go create mode 100644 common/client/node_fsm.go create mode 100644 common/client/node_lifecycle.go create mode 100644 common/client/node_selector_highest_head.go create mode 100644 common/client/node_selector_priority_level.go create mode 100644 common/client/node_selector_round_robin.go create mode 100644 common/client/node_selector_total_difficulty.go create mode 100644 common/client/send_only_node.go create mode 100644 common/client/send_only_node_lifecycle.go create mode 100644 common/client/types.go create mode 100644 common/types/receipt.go create mode 100644 core/chains/evm/client/chain_client.go create mode 100644 core/chains/evm/client/rpc_client.go diff --git a/common/chains/client/models.go b/common/chains/client/models.go index ebe7bb7576d..bd974f901fc 100644 --- a/common/chains/client/models.go +++ b/common/chains/client/models.go @@ -1,5 +1,9 @@ package client +import ( + "fmt" +) + type SendTxReturnCode int // SendTxReturnCode is a generalized client error that dictates what should be the next action, depending on the RPC error response. @@ -15,3 +19,21 @@ const ( ExceedsMaxFee // Attempt's fee was higher than the node's limit and got rejected. FeeOutOfValidRange // This error is returned when we use a fee price suggested from an RPC, but the network rejects the attempt due to an invalid range(mostly used by L2 chains). Retry by requesting a new suggested fee price. ) + +type NodeTier int + +const ( + Primary = NodeTier(iota) + Secondary +) + +func (n NodeTier) String() string { + switch n { + case Primary: + return "primary" + case Secondary: + return "secondary" + default: + return fmt.Sprintf("NodeTier(%d)", n) + } +} diff --git a/common/client/multi_node.go b/common/client/multi_node.go new file mode 100644 index 00000000000..0da3b89076b --- /dev/null +++ b/common/client/multi_node.go @@ -0,0 +1,669 @@ +package client + +import ( + "context" + "fmt" + "math/big" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" + + "github.com/smartcontractkit/chainlink/v2/common/chains/client" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +var ( + // PromMultiNodeRPCNodeStates reports current RPC node state + PromMultiNodeRPCNodeStates = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "multi_node_states", + Help: "The number of RPC nodes currently in the given state for the given chain", + }, []string{"network", "chainId", "state"}) + ErroringNodeError = fmt.Errorf("no live nodes available") +) + +const ( + NodeSelectionModeHighestHead = "HighestHead" + NodeSelectionModeRoundRobin = "RoundRobin" + NodeSelectionModeTotalDifficulty = "TotalDifficulty" + NodeSelectionModePriorityLevel = "PriorityLevel" +) + +type NodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] interface { + // Select returns a Node, or nil if none can be selected. + // Implementation must be thread-safe. + Select() Node[CHAIN_ID, HEAD, RPC] + // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin" + Name() string +} + +// MultiNode is a generalized multi node client interface that includes methods to interact with different chains. +// It also handles multiple node RPC connections simultaneously. +type MultiNode[ + CHAIN_ID types.ID, + SEQ types.Sequence, + ADDR types.Hashable, + BLOCK_HASH types.Hashable, + TX any, + TX_HASH types.Hashable, + EVENT any, + EVENT_OPS any, + TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], + FEE feetypes.Fee, + HEAD types.Head[BLOCK_HASH], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], +] interface { + clientAPI[ + CHAIN_ID, + SEQ, + ADDR, + BLOCK_HASH, + TX, + TX_HASH, + EVENT, + EVENT_OPS, + TX_RECEIPT, + FEE, + HEAD, + ] + Close() error + NodeStates() map[string]string + SelectNodeRPC() (RPC_CLIENT, error) + + BatchCallContextAll(ctx context.Context, b []any) error + ConfiguredChainID() CHAIN_ID + IsL2() bool +} + +type multiNode[ + CHAIN_ID types.ID, + SEQ types.Sequence, + ADDR types.Hashable, + BLOCK_HASH types.Hashable, + TX any, + TX_HASH types.Hashable, + EVENT any, + EVENT_OPS any, + TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], + FEE feetypes.Fee, + HEAD types.Head[BLOCK_HASH], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], +] struct { + services.StateMachine + nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT] + sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + chainID CHAIN_ID + chainType config.ChainType + logger logger.Logger + selectionMode string + noNewHeadsThreshold time.Duration + nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] + leaseDuration time.Duration + leaseTicker *time.Ticker + chainFamily string + + activeMu sync.RWMutex + activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] + + chStop utils.StopChan + wg sync.WaitGroup + + sendOnlyErrorParser func(err error) client.SendTxReturnCode +} + +func NewMultiNode[ + CHAIN_ID types.ID, + SEQ types.Sequence, + ADDR types.Hashable, + BLOCK_HASH types.Hashable, + TX any, + TX_HASH types.Hashable, + EVENT any, + EVENT_OPS any, + TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], + FEE feetypes.Fee, + HEAD types.Head[BLOCK_HASH], + RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], +]( + logger logger.Logger, + selectionMode string, + leaseDuration time.Duration, + noNewHeadsThreshold time.Duration, + nodes []Node[CHAIN_ID, HEAD, RPC_CLIENT], + sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT], + chainID CHAIN_ID, + chainType config.ChainType, + chainFamily string, + sendOnlyErrorParser func(err error) client.SendTxReturnCode, +) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { + nodeSelector := func() NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] { + switch selectionMode { + case NodeSelectionModeHighestHead: + return NewHighestHeadNodeSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) + case NodeSelectionModeRoundRobin: + return NewRoundRobinSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) + case NodeSelectionModeTotalDifficulty: + return NewTotalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) + case NodeSelectionModePriorityLevel: + return NewPriorityLevelNodeSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) + default: + panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode)) + } + }() + + lggr := logger.Named("MultiNode").With("chainID", chainID.String()) + + c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]{ + nodes: nodes, + sendonlys: sendonlys, + chainID: chainID, + chainType: chainType, + logger: lggr, + selectionMode: selectionMode, + noNewHeadsThreshold: noNewHeadsThreshold, + nodeSelector: nodeSelector, + chStop: make(chan struct{}), + leaseDuration: leaseDuration, + chainFamily: chainFamily, + sendOnlyErrorParser: sendOnlyErrorParser, + } + + c.logger.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) + + return c +} + +// Dial starts every node in the pool +// +// Nodes handle their own redialing and runloops, so this function does not +// return any error if the nodes aren't available +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Dial(ctx context.Context) error { + return c.StartOnce("MultiNode", func() (merr error) { + if len(c.nodes) == 0 { + return errors.Errorf("no available nodes for chain %s", c.chainID.String()) + } + var ms services.MultiStart + for _, n := range c.nodes { + if n.ConfiguredChainID().String() != c.chainID.String() { + return ms.CloseBecause(errors.Errorf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", n.String(), n.ConfiguredChainID().String(), c.chainID.String())) + } + rawNode, ok := n.(*node[CHAIN_ID, HEAD, RPC_CLIENT]) + if ok { + // This is a bit hacky but it allows the node to be aware of + // pool state and prevent certain state transitions that might + // otherwise leave no nodes available. It is better to have one + // node in a degraded state than no nodes at all. + rawNode.nLiveNodes = c.nLiveNodes + } + // node will handle its own redialing and automatic recovery + if err := ms.Start(ctx, n); err != nil { + return err + } + } + for _, s := range c.sendonlys { + if s.ConfiguredChainID().String() != c.chainID.String() { + return ms.CloseBecause(errors.Errorf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", s.String(), s.ConfiguredChainID().String(), c.chainID.String())) + } + if err := ms.Start(ctx, s); err != nil { + return err + } + } + c.wg.Add(1) + go c.runLoop() + + if c.leaseDuration.Seconds() > 0 && c.selectionMode != NodeSelectionModeRoundRobin { + c.logger.Infof("The MultiNode will switch to best node every %s", c.leaseDuration.String()) + c.wg.Add(1) + go c.checkLeaseLoop() + } else { + c.logger.Info("Best node switching is disabled") + } + + return nil + }) +} + +// Close tears down the MultiNode and closes all nodes +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Close() error { + return c.StopOnce("MultiNode", func() error { + close(c.chStop) + c.wg.Wait() + + return services.CloseAll(services.MultiCloser(c.nodes), services.MultiCloser(c.sendonlys)) + }) +} + +// SelectNodeRPC returns an RPC of an active node. If there are no active nodes it returns an error. +// Call this method from your chain-specific client implementation to access any chain-specific rpc calls. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SelectNodeRPC() (rpc RPC_CLIENT, err error) { + n, err := c.selectNode() + if err != nil { + return rpc, err + } + return n.RPC(), nil + +} + +// selectNode returns the active Node, if it is still nodeStateAlive, otherwise it selects a new one from the NodeSelector. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) selectNode() (node Node[CHAIN_ID, HEAD, RPC_CLIENT], err error) { + c.activeMu.RLock() + node = c.activeNode + c.activeMu.RUnlock() + if node != nil && node.State() == nodeStateAlive { + return // still alive + } + + // select a new one + c.activeMu.Lock() + defer c.activeMu.Unlock() + node = c.activeNode + if node != nil && node.State() == nodeStateAlive { + return // another goroutine beat us here + } + + c.activeNode = c.nodeSelector.Select() + + if c.activeNode == nil { + c.logger.Criticalw("No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name()) + errmsg := fmt.Errorf("no live nodes available for chain %s", c.chainID.String()) + c.SvcErrBuffer.Append(errmsg) + err = ErroringNodeError + } + + return c.activeNode, err +} + +// nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. +// totalDifficulty will be 0 if all nodes return nil. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *utils.Big) { + totalDifficulty = utils.NewBigI(0) + for _, n := range c.nodes { + if s, num, td := n.StateAndLatest(); s == nodeStateAlive { + nLiveNodes++ + if num > blockNumber { + blockNumber = num + } + if td != nil && td.Cmp(totalDifficulty) > 0 { + totalDifficulty = td + } + } + } + return +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) checkLease() { + bestNode := c.nodeSelector.Select() + for _, n := range c.nodes { + // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new + // best node. Only terminate connections with more than 1 subscription to account for the aliveLoop subscription + if n.State() == nodeStateAlive && n != bestNode && n.SubscribersCount() > 1 { + c.logger.Infof("Switching to best node from %q to %q", n.String(), bestNode.String()) + n.UnsubscribeAllExceptAliveLoop() + } + } + + c.activeMu.Lock() + if bestNode != c.activeNode { + c.activeNode = bestNode + } + c.activeMu.Unlock() +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) checkLeaseLoop() { + defer c.wg.Done() + c.leaseTicker = time.NewTicker(c.leaseDuration) + defer c.leaseTicker.Stop() + + for { + select { + case <-c.leaseTicker.C: + c.checkLease() + case <-c.chStop: + return + } + } +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) runLoop() { + defer c.wg.Done() + + c.report() + + // Prometheus' default interval is 15s, set this to under 7.5s to avoid + // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) + reportInterval := 6500 * time.Millisecond + monitor := time.NewTicker(utils.WithJitter(reportInterval)) + defer monitor.Stop() + + for { + select { + case <-monitor.C: + c.report() + case <-c.chStop: + return + } + } +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) report() { + type nodeWithState struct { + Node string + State string + } + + var total, dead int + counts := make(map[nodeState]int) + nodeStates := make([]nodeWithState, len(c.nodes)) + for i, n := range c.nodes { + state := n.State() + nodeStates[i] = nodeWithState{n.String(), state.String()} + total++ + if state != nodeStateAlive { + dead++ + } + counts[state]++ + } + for _, state := range allNodeStates { + count := counts[state] + PromMultiNodeRPCNodeStates.WithLabelValues(c.chainFamily, c.chainID.String(), state.String()).Set(float64(count)) + } + + live := total - dead + c.logger.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + if total == dead { + rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total) + c.logger.Criticalw(rerr.Error(), "nodeStates", nodeStates) + c.SvcErrBuffer.Append(rerr) + } else if dead > 0 { + c.logger.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + } +} + +// ClientAPI methods +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BalanceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (*big.Int, error) { + n, err := c.selectNode() + if err != nil { + return nil, err + } + return n.RPC().BalanceAt(ctx, account, blockNumber) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BatchCallContext(ctx context.Context, b []any) error { + n, err := c.selectNode() + if err != nil { + return err + } + return n.RPC().BatchCallContext(ctx, b) +} + +// BatchCallContextAll calls BatchCallContext for every single node including +// sendonlys. +// CAUTION: This should only be used for mass re-transmitting transactions, it +// might have unexpected effects to use it for anything else. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BatchCallContextAll(ctx context.Context, b []any) error { + var wg sync.WaitGroup + defer wg.Wait() + + main, selectionErr := c.selectNode() + var all []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + for _, n := range c.nodes { + all = append(all, n) + } + all = append(all, c.sendonlys...) + for _, n := range all { + if n == main { + // main node is used at the end for the return value + continue + } + // Parallel call made to all other nodes with ignored return value + wg.Add(1) + go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { + defer wg.Done() + err := n.RPC().BatchCallContext(ctx, b) + if err != nil { + c.logger.Debugw("Secondary node BatchCallContext failed", "err", err) + } else { + c.logger.Trace("Secondary node BatchCallContext success") + } + }(n) + } + + if selectionErr != nil { + return selectionErr + } + return main.RPC().BatchCallContext(ctx, b) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (h HEAD, err error) { + n, err := c.selectNode() + if err != nil { + return h, err + } + return n.RPC().BlockByHash(ctx, hash) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) BlockByNumber(ctx context.Context, number *big.Int) (h HEAD, err error) { + n, err := c.selectNode() + if err != nil { + return h, err + } + return n.RPC().BlockByNumber(ctx, number) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + n, err := c.selectNode() + if err != nil { + return err + } + return n.RPC().CallContext(ctx, result, method, args...) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CallContract( + ctx context.Context, + attempt interface{}, + blockNumber *big.Int, +) (rpcErr []byte, extractErr error) { + n, err := c.selectNode() + if err != nil { + return rpcErr, err + } + return n.RPC().CallContract(ctx, attempt, blockNumber) +} + +// ChainID makes a direct RPC call. In most cases it should be better to use the configured chain id instead by +// calling ConfiguredChainID. +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ChainID(ctx context.Context) (id CHAIN_ID, err error) { + n, err := c.selectNode() + if err != nil { + return id, err + } + return n.RPC().ChainID(ctx) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ChainType() config.ChainType { + return c.chainType +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) (code []byte, err error) { + n, err := c.selectNode() + if err != nil { + return code, err + } + return n.RPC().CodeAt(ctx, account, blockNumber) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) ConfiguredChainID() CHAIN_ID { + return c.chainID +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) EstimateGas(ctx context.Context, call any) (gas uint64, err error) { + n, err := c.selectNode() + if err != nil { + return gas, err + } + return n.RPC().EstimateGas(ctx, call) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) FilterEvents(ctx context.Context, query EVENT_OPS) (e []EVENT, err error) { + n, err := c.selectNode() + if err != nil { + return e, err + } + return n.RPC().FilterEvents(ctx, query) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) IsL2() bool { + return c.ChainType().IsL2() +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) LatestBlockHeight(ctx context.Context) (h *big.Int, err error) { + n, err := c.selectNode() + if err != nil { + return h, err + } + return n.RPC().LatestBlockHeight(ctx) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (b *assets.Link, err error) { + n, err := c.selectNode() + if err != nil { + return b, err + } + return n.RPC().LINKBalance(ctx, accountAddress, linkAddress) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) NodeStates() (states map[string]string) { + states = make(map[string]string) + for _, n := range c.nodes { + states[n.Name()] = n.State().String() + } + for _, s := range c.sendonlys { + states[s.Name()] = s.State().String() + } + return +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) PendingSequenceAt(ctx context.Context, addr ADDR) (s SEQ, err error) { + n, err := c.selectNode() + if err != nil { + return s, err + } + return n.RPC().PendingSequenceAt(ctx, addr) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SendEmptyTransaction( + ctx context.Context, + newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), + seq SEQ, + gasLimit uint32, + fee FEE, + fromAddress ADDR, +) (txhash string, err error) { + n, err := c.selectNode() + if err != nil { + return txhash, err + } + return n.RPC().SendEmptyTransaction(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SendTransaction(ctx context.Context, tx TX) error { + main, nodeError := c.selectNode() + var all []SendOnlyNode[CHAIN_ID, RPC_CLIENT] + for _, n := range c.nodes { + all = append(all, n) + } + all = append(all, c.sendonlys...) + for _, n := range all { + if n == main { + // main node is used at the end for the return value + continue + } + // Parallel send to all other nodes with ignored return value + // Async - we do not want to block the main thread with secondary nodes + // in case they are unreliable/slow. + // It is purely a "best effort" send. + // Resource is not unbounded because the default context has a timeout. + ok := c.IfNotStopped(func() { + // Must wrap inside IfNotStopped to avoid waitgroup racing with Close + c.wg.Add(1) + go func(n SendOnlyNode[CHAIN_ID, RPC_CLIENT]) { + defer c.wg.Done() + + txErr := n.RPC().SendTransaction(ctx, tx) + c.logger.Debugw("Sendonly node sent transaction", "name", n.String(), "tx", tx, "err", txErr) + sendOnlyError := c.sendOnlyErrorParser(txErr) + if sendOnlyError != client.Successful { + c.logger.Warnw("RPC returned error", "name", n.String(), "tx", tx, "err", txErr) + } + }(n) + }) + if !ok { + c.logger.Debug("Cannot send transaction on sendonly node; MultiNode is stopped", "node", n.String()) + } + } + if nodeError != nil { + return nodeError + } + return main.RPC().SendTransaction(ctx, tx) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SequenceAt(ctx context.Context, account ADDR, blockNumber *big.Int) (s SEQ, err error) { + n, err := c.selectNode() + if err != nil { + return s, err + } + return n.RPC().SequenceAt(ctx, account, blockNumber) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) SimulateTransaction(ctx context.Context, tx TX) error { + n, err := c.selectNode() + if err != nil { + return err + } + return n.RPC().SimulateTransaction(ctx, tx) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (s types.Subscription, err error) { + n, err := c.selectNode() + if err != nil { + return s, err + } + return n.RPC().Subscribe(ctx, channel, args...) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TokenBalance(ctx context.Context, account ADDR, tokenAddr ADDR) (b *big.Int, err error) { + n, err := c.selectNode() + if err != nil { + return b, err + } + return n.RPC().TokenBalance(ctx, account, tokenAddr) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TransactionByHash(ctx context.Context, txHash TX_HASH) (tx TX, err error) { + n, err := c.selectNode() + if err != nil { + return tx, err + } + return n.RPC().TransactionByHash(ctx, txHash) +} + +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (txr TX_RECEIPT, err error) { + n, err := c.selectNode() + if err != nil { + return txr, err + } + return n.RPC().TransactionReceipt(ctx, txHash) +} diff --git a/common/client/node.go b/common/client/node.go new file mode 100644 index 00000000000..71b34452f02 --- /dev/null +++ b/common/client/node.go @@ -0,0 +1,282 @@ +package client + +import ( + "context" + "fmt" + "net/url" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" + + "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +const QueryTimeout = 10 * time.Second + +var errInvalidChainID = errors.New("invalid chain id") + +var ( + promPoolRPCNodeVerifies = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_verifies", + Help: "The total number of chain ID verifications for the given RPC node", + }, []string{"network", "chainID", "nodeName"}) + promPoolRPCNodeVerifiesFailed = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_verifies_failed", + Help: "The total number of failed chain ID verifications for the given RPC node", + }, []string{"network", "chainID", "nodeName"}) + promPoolRPCNodeVerifiesSuccess = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_verifies_success", + Help: "The total number of successful chain ID verifications for the given RPC node", + }, []string{"network", "chainID", "nodeName"}) +) + +type NodeConfig interface { + PollFailureThreshold() uint32 + PollInterval() time.Duration + SelectionMode() string + SyncThreshold() uint32 +} + +type Node[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] interface { + // State returns nodeState + State() nodeState + // StateAndLatest returns nodeState with the latest received block number & total difficulty. + StateAndLatest() (nodeState, int64, *utils.Big) + // Name is a unique identifier for this node. + Name() string + String() string + RPC() RPC + SubscribersCount() int32 + UnsubscribeAllExceptAliveLoop() + ConfiguredChainID() CHAIN_ID + Order() int32 + Start(context.Context) error + Close() error +} + +type node[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] struct { + services.StateMachine + lfcLog logger.Logger + name string + id int32 + chainID CHAIN_ID + nodePoolCfg NodeConfig + noNewHeadsThreshold time.Duration + order int32 + chainFamily string + + ws url.URL + http *url.URL + + rpc RPC + + stateMu sync.RWMutex // protects state* fields + state nodeState + // Each node is tracking the last received head number and total difficulty + stateLatestBlockNumber int64 + stateLatestTotalDifficulty *utils.Big + + // nodeCtx is the node lifetime's context + nodeCtx context.Context + // cancelNodeCtx cancels nodeCtx when stopping the node + cancelNodeCtx context.CancelFunc + // wg waits for subsidiary goroutines + wg sync.WaitGroup + + // nLiveNodes is a passed in function that allows this node to: + // 1. see how many live nodes there are in total, so we can prevent the last alive node in a pool from being + // moved to out-of-sync state. It is better to have one out-of-sync node than no nodes at all. + // 2. compare against the highest head (by number or difficulty) to ensure we don't fall behind too far. + nLiveNodes func() (count int, blockNumber int64, totalDifficulty *utils.Big) +} + +func NewNode[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +]( + nodeCfg NodeConfig, + noNewHeadsThreshold time.Duration, + lggr logger.Logger, + wsuri url.URL, + httpuri *url.URL, + name string, + id int32, + chainID CHAIN_ID, + nodeOrder int32, + rpc RPC, + chainFamily string, +) Node[CHAIN_ID, HEAD, RPC] { + n := new(node[CHAIN_ID, HEAD, RPC]) + n.name = name + n.id = id + n.chainID = chainID + n.nodePoolCfg = nodeCfg + n.noNewHeadsThreshold = noNewHeadsThreshold + n.ws = wsuri + n.order = nodeOrder + if httpuri != nil { + n.http = httpuri + } + n.nodeCtx, n.cancelNodeCtx = context.WithCancel(context.Background()) + lggr = lggr.Named("Node").With( + "nodeTier", client.Primary.String(), + "nodeName", name, + "node", n.String(), + "chainID", chainID, + "nodeOrder", n.order, + ) + n.lfcLog = lggr.Named("Lifecycle") + n.stateLatestBlockNumber = -1 + n.rpc = rpc + n.chainFamily = chainFamily + return n +} + +func (n *node[CHAIN_ID, HEAD, RPC]) String() string { + s := fmt.Sprintf("(%s)%s:%s", client.Primary.String(), n.name, n.ws.String()) + if n.http != nil { + s = s + fmt.Sprintf(":%s", n.http.String()) + } + return s +} + +func (n *node[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() (chainID CHAIN_ID) { + return n.chainID +} + +func (n *node[CHAIN_ID, HEAD, RPC]) Name() string { + return n.name +} + +func (n *node[CHAIN_ID, HEAD, RPC]) RPC() RPC { + return n.rpc +} + +func (n *node[CHAIN_ID, HEAD, RPC]) SubscribersCount() int32 { + return n.rpc.SubscribersCount() +} + +func (n *node[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { + n.rpc.UnsubscribeAllExceptAliveLoop() +} + +func (n *node[CHAIN_ID, HEAD, RPC]) Close() error { + return n.StopOnce(n.name, func() error { + defer func() { + n.wg.Wait() + n.rpc.Close() + }() + + n.stateMu.Lock() + defer n.stateMu.Unlock() + + n.cancelNodeCtx() + n.state = nodeStateClosed + return nil + }) +} + +// Start dials and verifies the node +// Should only be called once in a node's lifecycle +// Return value is necessary to conform to interface but this will never +// actually return an error. +func (n *node[CHAIN_ID, HEAD, RPC]) Start(startCtx context.Context) error { + return n.StartOnce(n.name, func() error { + n.start(startCtx) + return nil + }) +} + +// start initially dials the node and verifies chain ID +// This spins off lifecycle goroutines. +// Not thread-safe. +// Node lifecycle is synchronous: only one goroutine should be running at a +// time. +func (n *node[CHAIN_ID, HEAD, RPC]) start(startCtx context.Context) { + if n.state != nodeStateUndialed { + panic(fmt.Sprintf("cannot dial node with state %v", n.state)) + } + + if err := n.rpc.Dial(startCtx); err != nil { + n.lfcLog.Errorw("Dial failed: Node is unreachable", "err", err) + n.declareUnreachable() + return + } + n.setState(nodeStateDialed) + + if err := n.verify(startCtx); errors.Is(err, errInvalidChainID) { + n.lfcLog.Errorw("Verify failed: Node has the wrong chain ID", "err", err) + n.declareInvalidChainID() + return + } else if err != nil { + n.lfcLog.Errorw(fmt.Sprintf("Verify failed: %v", err), "err", err) + n.declareUnreachable() + return + } + + n.declareAlive() +} + +// verify checks that all connections to eth nodes match the given chain ID +// Not thread-safe +// Pure verify: does not mutate node "state" field. +func (n *node[CHAIN_ID, HEAD, RPC]) verify(callerCtx context.Context) (err error) { + promPoolRPCNodeVerifies.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() + promFailed := func() { + promPoolRPCNodeVerifiesFailed.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() + } + + st := n.State() + switch st { + case nodeStateDialed, nodeStateOutOfSync, nodeStateInvalidChainID: + default: + panic(fmt.Sprintf("cannot verify node in state %v", st)) + } + + var chainID CHAIN_ID + if chainID, err = n.rpc.ChainID(callerCtx); err != nil { + promFailed() + return errors.Wrapf(err, "failed to verify chain ID for node %s", n.name) + } else if chainID.String() != n.chainID.String() { + promFailed() + return errors.Wrapf( + errInvalidChainID, + "rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s", + chainID.String(), + n.chainID.String(), + n.name, + ) + } + + promPoolRPCNodeVerifiesSuccess.WithLabelValues(n.chainFamily, n.chainID.String(), n.name).Inc() + + return nil +} + +// disconnectAll disconnects all clients connected to the node +// WARNING: NOT THREAD-SAFE +// This must be called from within the n.stateMu lock +func (n *node[CHAIN_ID, HEAD, RPC]) disconnectAll() { + n.rpc.DisconnectAll() +} + +func (n *node[CHAIN_ID, HEAD, RPC]) Order() int32 { + return n.order +} diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go new file mode 100644 index 00000000000..d4fc19140e9 --- /dev/null +++ b/common/client/node_fsm.go @@ -0,0 +1,266 @@ +package client + +import ( + "fmt" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +var ( + promPoolRPCNodeTransitionsToAlive = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_alive", + Help: transitionString(nodeStateAlive), + }, []string{"chainID", "nodeName"}) + promPoolRPCNodeTransitionsToInSync = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_in_sync", + Help: fmt.Sprintf("%s to %s", transitionString(nodeStateOutOfSync), nodeStateAlive), + }, []string{"chainID", "nodeName"}) + promPoolRPCNodeTransitionsToOutOfSync = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_out_of_sync", + Help: transitionString(nodeStateOutOfSync), + }, []string{"chainID", "nodeName"}) + promPoolRPCNodeTransitionsToUnreachable = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_unreachable", + Help: transitionString(nodeStateUnreachable), + }, []string{"chainID", "nodeName"}) + promPoolRPCNodeTransitionsToInvalidChainID = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_invalid_chain_id", + Help: transitionString(nodeStateInvalidChainID), + }, []string{"chainID", "nodeName"}) + promPoolRPCNodeTransitionsToUnusable = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_transitions_to_unusable", + Help: transitionString(nodeStateUnusable), + }, []string{"chainID", "nodeName"}) +) + +// nodeState represents the current state of the node +// Node is a FSM (finite state machine) +type nodeState int + +func (n nodeState) String() string { + switch n { + case nodeStateUndialed: + return "Undialed" + case nodeStateDialed: + return "Dialed" + case nodeStateInvalidChainID: + return "InvalidChainID" + case nodeStateAlive: + return "Alive" + case nodeStateUnreachable: + return "Unreachable" + case nodeStateUnusable: + return "Unusable" + case nodeStateOutOfSync: + return "OutOfSync" + case nodeStateClosed: + return "Closed" + default: + return fmt.Sprintf("nodeState(%d)", n) + } +} + +// GoString prints a prettier state +func (n nodeState) GoString() string { + return fmt.Sprintf("nodeState%s(%d)", n.String(), n) +} + +const ( + // nodeStateUndialed is the first state of a virgin node + nodeStateUndialed = nodeState(iota) + // nodeStateDialed is after a node has successfully dialed but before it has verified the correct chain ID + nodeStateDialed + // nodeStateInvalidChainID is after chain ID verification failed + nodeStateInvalidChainID + // nodeStateAlive is a healthy node after chain ID verification succeeded + nodeStateAlive + // nodeStateUnreachable is a node that cannot be dialed or has disconnected + nodeStateUnreachable + // nodeStateOutOfSync is a node that is accepting connections but exceeded + // the failure threshold without sending any new heads. It will be + // disconnected, then put into a revive loop and re-awakened after redial + // if a new head arrives + nodeStateOutOfSync + // nodeStateUnusable is a sendonly node that has an invalid URL that can never be reached + nodeStateUnusable + // nodeStateClosed is after the connection has been closed and the node is at the end of its lifecycle + nodeStateClosed + // nodeStateLen tracks the number of states + nodeStateLen +) + +// allNodeStates represents all possible states a node can be in +var allNodeStates []nodeState + +func init() { + for s := nodeState(0); s < nodeStateLen; s++ { + allNodeStates = append(allNodeStates, s) + } +} + +// FSM methods + +// State allows reading the current state of the node. +func (n *node[CHAIN_ID, HEAD, RPC]) State() nodeState { + n.stateMu.RLock() + defer n.stateMu.RUnlock() + return n.state +} + +func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *utils.Big) { + n.stateMu.RLock() + defer n.stateMu.RUnlock() + return n.state, n.stateLatestBlockNumber, n.stateLatestTotalDifficulty +} + +// setState is only used by internal state management methods. +// This is low-level; care should be taken by the caller to ensure the new state is a valid transition. +// State changes should always be synchronous: only one goroutine at a time should change state. +// n.stateMu should not be locked for long periods of time because external clients expect a timely response from n.State() +func (n *node[CHAIN_ID, HEAD, RPC]) setState(s nodeState) { + n.stateMu.Lock() + defer n.stateMu.Unlock() + n.state = s +} + +// declareXXX methods change the state and pass conrol off the new state +// management goroutine + +func (n *node[CHAIN_ID, HEAD, RPC]) declareAlive() { + n.transitionToAlive(func() { + n.lfcLog.Infow("RPC Node is online", "nodeState", n.state) + n.wg.Add(1) + go n.aliveLoop() + }) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) transitionToAlive(fn func()) { + promPoolRPCNodeTransitionsToAlive.WithLabelValues(n.chainID.String(), n.name).Inc() + n.stateMu.Lock() + defer n.stateMu.Unlock() + if n.state == nodeStateClosed { + return + } + switch n.state { + case nodeStateDialed, nodeStateInvalidChainID: + n.state = nodeStateAlive + default: + panic(transitionFail(n.state, nodeStateAlive)) + } + fn() +} + +// declareInSync puts a node back into Alive state, allowing it to be used by +// pool consumers again +func (n *node[CHAIN_ID, HEAD, RPC]) declareInSync() { + n.transitionToInSync(func() { + n.lfcLog.Infow("RPC Node is back in sync", "nodeState", n.state) + n.wg.Add(1) + go n.aliveLoop() + }) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInSync(fn func()) { + promPoolRPCNodeTransitionsToAlive.WithLabelValues(n.chainID.String(), n.name).Inc() + promPoolRPCNodeTransitionsToInSync.WithLabelValues(n.chainID.String(), n.name).Inc() + n.stateMu.Lock() + defer n.stateMu.Unlock() + if n.state == nodeStateClosed { + return + } + switch n.state { + case nodeStateOutOfSync: + n.state = nodeStateAlive + default: + panic(transitionFail(n.state, nodeStateAlive)) + } + fn() +} + +// declareOutOfSync puts a node into OutOfSync state, disconnecting all current +// clients and making it unavailable for use until back in-sync. +func (n *node[CHAIN_ID, HEAD, RPC]) declareOutOfSync(isOutOfSync func(num int64, td *utils.Big) bool) { + n.transitionToOutOfSync(func() { + n.lfcLog.Errorw("RPC Node is out of sync", "nodeState", n.state) + n.wg.Add(1) + go n.outOfSyncLoop(isOutOfSync) + }) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) transitionToOutOfSync(fn func()) { + promPoolRPCNodeTransitionsToOutOfSync.WithLabelValues(n.chainID.String(), n.name).Inc() + n.stateMu.Lock() + defer n.stateMu.Unlock() + if n.state == nodeStateClosed { + return + } + switch n.state { + case nodeStateAlive: + n.disconnectAll() + n.state = nodeStateOutOfSync + default: + panic(transitionFail(n.state, nodeStateOutOfSync)) + } + fn() +} + +func (n *node[CHAIN_ID, HEAD, RPC]) declareUnreachable() { + n.transitionToUnreachable(func() { + n.lfcLog.Errorw("RPC Node is unreachable", "nodeState", n.state) + n.wg.Add(1) + go n.unreachableLoop() + }) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) transitionToUnreachable(fn func()) { + promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(n.chainID.String(), n.name).Inc() + n.stateMu.Lock() + defer n.stateMu.Unlock() + if n.state == nodeStateClosed { + return + } + switch n.state { + case nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID: + n.disconnectAll() + n.state = nodeStateUnreachable + default: + panic(transitionFail(n.state, nodeStateUnreachable)) + } + fn() +} + +func (n *node[CHAIN_ID, HEAD, RPC]) declareInvalidChainID() { + n.transitionToInvalidChainID(func() { + n.lfcLog.Errorw("RPC Node has the wrong chain ID", "nodeState", n.state) + n.wg.Add(1) + go n.invalidChainIDLoop() + }) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInvalidChainID(fn func()) { + promPoolRPCNodeTransitionsToInvalidChainID.WithLabelValues(n.chainID.String(), n.name).Inc() + n.stateMu.Lock() + defer n.stateMu.Unlock() + if n.state == nodeStateClosed { + return + } + switch n.state { + case nodeStateDialed, nodeStateOutOfSync: + n.disconnectAll() + n.state = nodeStateInvalidChainID + default: + panic(transitionFail(n.state, nodeStateInvalidChainID)) + } + fn() +} + +func transitionString(state nodeState) string { + return fmt.Sprintf("Total number of times node has transitioned to %s", state) +} + +func transitionFail(from nodeState, to nodeState) string { + return fmt.Sprintf("cannot transition from %#v to %#v", from, to) +} diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go new file mode 100644 index 00000000000..149c5f01a6d --- /dev/null +++ b/common/client/node_lifecycle.go @@ -0,0 +1,431 @@ +package client + +import ( + "context" + "fmt" + "math" + "time" + + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +var ( + promPoolRPCNodeHighestSeenBlock = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "pool_rpc_node_highest_seen_block", + Help: "The highest seen block for the given RPC node", + }, []string{"chainID", "nodeName"}) + promPoolRPCNodeNumSeenBlocks = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_num_seen_blocks", + Help: "The total number of new blocks seen by the given RPC node", + }, []string{"chainID", "nodeName"}) + promPoolRPCNodePolls = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_polls_total", + Help: "The total number of poll checks for the given RPC node", + }, []string{"chainID", "nodeName"}) + promPoolRPCNodePollsFailed = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_polls_failed", + Help: "The total number of failed poll checks for the given RPC node", + }, []string{"chainID", "nodeName"}) + promPoolRPCNodePollsSuccess = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "pool_rpc_node_polls_success", + Help: "The total number of successful poll checks for the given RPC node", + }, []string{"chainID", "nodeName"}) +) + +// zombieNodeCheckInterval controls how often to re-check to see if we need to +// state change in case we have to force a state transition due to no available +// nodes. +// NOTE: This only applies to out-of-sync nodes if they are the last available node +func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration { + interval := noNewHeadsThreshold + if interval <= 0 || interval > QueryTimeout { + interval = QueryTimeout + } + return utils.WithJitter(interval) +} + +func (n *node[CHAIN_ID, HEAD, RPC]) setLatestReceived(blockNumber int64, totalDifficulty *utils.Big) { + n.stateMu.Lock() + defer n.stateMu.Unlock() + n.stateLatestBlockNumber = blockNumber + n.stateLatestTotalDifficulty = totalDifficulty +} + +const ( + msgCannotDisable = "but cannot disable this connection because there are no other RPC endpoints, or all other RPC endpoints are dead." + msgDegradedState = "Chainlink is now operating in a degraded state and urgent action is required to resolve the issue" +) + +// Node is a FSM +// Each state has a loop that goes with it, which monitors the node and moves it into another state as necessary. +// Only one loop must run at a time. +// Each loop passes control onto the next loop as it exits, except when the node is Closed which terminates the loop permanently. + +// This handles node lifecycle for the ALIVE state +// Should only be run ONCE per node, after a successful Dial +func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { + defer n.wg.Done() + + { + // sanity check + state := n.State() + switch state { + case nodeStateAlive: + case nodeStateClosed: + return + default: + panic(fmt.Sprintf("aliveLoop can only run for node in Alive state, got: %s", state)) + } + } + + noNewHeadsTimeoutThreshold := n.noNewHeadsThreshold + pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold() + pollInterval := n.nodePoolCfg.PollInterval() + + lggr := n.lfcLog.Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) + lggr.Tracew("Alive loop starting", "nodeState", n.State()) + + headsC := make(chan HEAD) + sub, err := n.rpc.Subscribe(n.nodeCtx, headsC, "newHeads") + if err != nil { + lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.State()) + n.declareUnreachable() + return + } + n.rpc.SetAliveLoopSub(sub) + defer sub.Unsubscribe() + + var outOfSyncT *time.Ticker + var outOfSyncTC <-chan time.Time + if noNewHeadsTimeoutThreshold > 0 { + lggr.Debugw("Head liveness checking enabled", "nodeState", n.State()) + outOfSyncT = time.NewTicker(noNewHeadsTimeoutThreshold) + defer outOfSyncT.Stop() + outOfSyncTC = outOfSyncT.C + } else { + lggr.Debug("Head liveness checking disabled") + } + + var pollCh <-chan time.Time + if pollInterval > 0 { + lggr.Debug("Polling enabled") + pollT := time.NewTicker(pollInterval) + defer pollT.Stop() + pollCh = pollT.C + if pollFailureThreshold > 0 { + // polling can be enabled with no threshold to enable polling but + // the node will not be marked offline regardless of the number of + // poll failures + lggr.Debug("Polling liveness checking enabled") + } + } else { + lggr.Debug("Polling disabled") + } + + _, highestReceivedBlockNumber, _ := n.StateAndLatest() + var pollFailures uint32 + + for { + select { + case <-n.nodeCtx.Done(): + return + case <-pollCh: + var version string + promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() + lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + ctx, cancel := context.WithTimeout(n.nodeCtx, pollInterval) + version, err := n.RPC().ClientVersion(ctx) + cancel() + if err != nil { + // prevent overflow + if pollFailures < math.MaxUint32 { + promPoolRPCNodePollsFailed.WithLabelValues(n.chainID.String(), n.name).Inc() + pollFailures++ + } + lggr.Warnw(fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", n.String()), "err", err, "pollFailures", pollFailures, "nodeState", n.State()) + } else { + lggr.Debugw("Version poll successful", "nodeState", n.State(), "clientVersion", version) + promPoolRPCNodePollsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc() + pollFailures = 0 + } + if pollFailureThreshold > 0 && pollFailures >= pollFailureThreshold { + lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.State()) + if n.nLiveNodes != nil { + if l, _, _ := n.nLiveNodes(); l < 2 { + lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) + continue + } + } + n.declareUnreachable() + return + } + _, num, td := n.StateAndLatest() + if outOfSync, liveNodes := n.syncStatus(num, td); outOfSync { + // note: there must be another live node for us to be out of sync + lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) + if liveNodes < 2 { + lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) + continue + } + n.declareOutOfSync(n.isOutOfSync) + return + } + case bh, open := <-headsC: + if !open { + lggr.Errorw("Subscription channel unexpectedly closed", "nodeState", n.State()) + n.declareUnreachable() + return + } + promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() + lggr.Tracew("Got head", "head", bh) + if bh.BlockNumber() > highestReceivedBlockNumber { + promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.BlockNumber())) + lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + highestReceivedBlockNumber = bh.BlockNumber() + } else { + lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + } + if outOfSyncT != nil { + outOfSyncT.Reset(noNewHeadsTimeoutThreshold) + } + n.setLatestReceived(bh.BlockNumber(), bh.BlockDifficulty()) + case err := <-sub.Err(): + lggr.Errorw("Subscription was terminated", "err", err, "nodeState", n.State()) + n.declareUnreachable() + return + case <-outOfSyncTC: + // We haven't received a head on the channel for at least the + // threshold amount of time, mark it broken + lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) + if n.nLiveNodes != nil { + if l, _, _ := n.nLiveNodes(); l < 2 { + lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) + // We don't necessarily want to wait the full timeout to check again, we should + // check regularly and log noisily in this state + outOfSyncT.Reset(zombieNodeCheckInterval(n.noNewHeadsThreshold)) + continue + } + } + n.declareOutOfSync(func(num int64, td *utils.Big) bool { return num < highestReceivedBlockNumber }) + return + } + } +} + +func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSync(num int64, td *utils.Big) (outOfSync bool) { + outOfSync, _ = n.syncStatus(num, td) + return +} + +// syncStatus returns outOfSync true if num or td is more than SyncThresold behind the best node. +// Always returns outOfSync false for SyncThreshold 0. +// liveNodes is only included when outOfSync is true. +func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *utils.Big) (outOfSync bool, liveNodes int) { + if n.nLiveNodes == nil { + return // skip for tests + } + threshold := n.nodePoolCfg.SyncThreshold() + if threshold == 0 { + return // disabled + } + // Check against best node + ln, highest, greatest := n.nLiveNodes() + mode := n.nodePoolCfg.SelectionMode() + switch mode { + case NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel: + return num < highest-int64(threshold), ln + case NodeSelectionModeTotalDifficulty: + bigThreshold := utils.NewBigI(int64(threshold)) + return td.Cmp(greatest.Sub(bigThreshold)) < 0, ln + default: + panic("unrecognized NodeSelectionMode: " + mode) + } +} + +const ( + msgReceivedBlock = "Received block for RPC node, waiting until back in-sync to mark as live again" + msgInSync = "RPC node back in sync" +) + +// outOfSyncLoop takes an OutOfSync node and waits until isOutOfSync returns false to go back to live status +func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td *utils.Big) bool) { + defer n.wg.Done() + + { + // sanity check + state := n.State() + switch state { + case nodeStateOutOfSync: + case nodeStateClosed: + return + default: + panic(fmt.Sprintf("outOfSyncLoop can only run for node in OutOfSync state, got: %s", state)) + } + } + + outOfSyncAt := time.Now() + + lggr := n.lfcLog.Named("OutOfSync") + lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) + + // Need to redial since out-of-sync nodes are automatically disconnected + if err := n.rpc.Dial(n.nodeCtx); err != nil { + lggr.Errorw("Failed to dial out-of-sync RPC node", "nodeState", n.State()) + n.declareUnreachable() + return + } + + // Manually re-verify since out-of-sync nodes are automatically disconnected + if err := n.verify(n.nodeCtx); err != nil { + lggr.Errorw(fmt.Sprintf("Failed to verify out-of-sync RPC node: %v", err), "err", err) + n.declareInvalidChainID() + return + } + + lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + + ch := make(chan HEAD) + sub, err := n.rpc.Subscribe(n.nodeCtx, ch, "newHeads") + if err != nil { + lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.State(), "err", err) + n.declareUnreachable() + return + } + defer sub.Unsubscribe() + + for { + select { + case <-n.nodeCtx.Done(): + return + case head, open := <-ch: + if !open { + lggr.Error("Subscription channel unexpectedly closed", "nodeState", n.State()) + n.declareUnreachable() + return + } + n.setLatestReceived(head.BlockNumber(), head.BlockDifficulty()) + if !isOutOfSync(head.BlockNumber(), head.BlockDifficulty()) { + // back in-sync! flip back into alive loop + lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "totalDifficulty", "nodeState", n.State()) + n.declareInSync() + return + } + lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "totalDifficulty", "nodeState", n.State()) + case <-time.After(zombieNodeCheckInterval(n.noNewHeadsThreshold)): + if n.nLiveNodes != nil { + if l, _, _ := n.nLiveNodes(); l < 1 { + lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") + n.declareInSync() + return + } + } + case err := <-sub.Err(): + lggr.Errorw("Subscription was terminated", "nodeState", n.State(), "err", err) + n.declareUnreachable() + return + } + } +} + +func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { + defer n.wg.Done() + + { + // sanity check + state := n.State() + switch state { + case nodeStateUnreachable: + case nodeStateClosed: + return + default: + panic(fmt.Sprintf("unreachableLoop can only run for node in Unreachable state, got: %s", state)) + } + } + + unreachableAt := time.Now() + + lggr := n.lfcLog.Named("Unreachable") + lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) + + dialRetryBackoff := utils.NewRedialBackoff() + + for { + select { + case <-n.nodeCtx.Done(): + return + case <-time.After(dialRetryBackoff.Duration()): + lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) + + err := n.rpc.Dial(n.nodeCtx) + if err != nil { + lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; still unreachable: %v", err), "err", err, "nodeState", n.State()) + continue + } + + n.setState(nodeStateDialed) + + err = n.verify(n.nodeCtx) + + if errors.Is(err, errInvalidChainID) { + lggr.Errorw("Failed to redial RPC node; remote endpoint returned the wrong chain ID", "err", err) + n.declareInvalidChainID() + return + } else if err != nil { + lggr.Errorw(fmt.Sprintf("Failed to redial RPC node; verify failed: %v", err), "err", err) + n.declareUnreachable() + return + } + + lggr.Infow(fmt.Sprintf("Successfully redialled and verified RPC node %s. Node was offline for %s", n.String(), time.Since(unreachableAt)), "nodeState", n.State()) + n.declareAlive() + return + } + } +} + +func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { + defer n.wg.Done() + + { + // sanity check + state := n.State() + switch state { + case nodeStateInvalidChainID: + case nodeStateClosed: + return + default: + panic(fmt.Sprintf("invalidChainIDLoop can only run for node in InvalidChainID state, got: %s", state)) + } + } + + invalidAt := time.Now() + + lggr := n.lfcLog.Named("InvalidChainID") + lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) + + chainIDRecheckBackoff := utils.NewRedialBackoff() + + for { + select { + case <-n.nodeCtx.Done(): + return + case <-time.After(chainIDRecheckBackoff.Duration()): + err := n.verify(n.nodeCtx) + if errors.Is(err, errInvalidChainID) { + lggr.Errorw("Failed to verify RPC node; remote endpoint returned the wrong chain ID", "err", err) + continue + } else if err != nil { + lggr.Errorw(fmt.Sprintf("Unexpected error while verifying RPC node chain ID; %v", err), "err", err) + n.declareUnreachable() + return + } + lggr.Infow(fmt.Sprintf("Successfully verified RPC node. Node was offline for %s", time.Since(invalidAt)), "nodeState", n.State()) + n.declareAlive() + return + } + } +} diff --git a/common/client/node_selector_highest_head.go b/common/client/node_selector_highest_head.go new file mode 100644 index 00000000000..99a130004a9 --- /dev/null +++ b/common/client/node_selector_highest_head.go @@ -0,0 +1,41 @@ +package client + +import ( + "math" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type highestHeadNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] []Node[CHAIN_ID, HEAD, RPC] + +func NewHighestHeadNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + return highestHeadNodeSelector[CHAIN_ID, HEAD, RPC](nodes) +} + +func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { + var highestHeadNumber int64 = math.MinInt64 + var highestHeadNodes []Node[CHAIN_ID, HEAD, RPC] + for _, n := range s { + state, currentHeadNumber, _ := n.StateAndLatest() + if state == nodeStateAlive && currentHeadNumber >= highestHeadNumber { + if highestHeadNumber < currentHeadNumber { + highestHeadNumber = currentHeadNumber + highestHeadNodes = nil + } + highestHeadNodes = append(highestHeadNodes, n) + } + } + return firstOrHighestPriority(highestHeadNodes) +} + +func (s highestHeadNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { + return NodeSelectionModeHighestHead +} diff --git a/common/client/node_selector_priority_level.go b/common/client/node_selector_priority_level.go new file mode 100644 index 00000000000..45cc62de077 --- /dev/null +++ b/common/client/node_selector_priority_level.go @@ -0,0 +1,129 @@ +package client + +import ( + "math" + "sort" + "sync/atomic" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type priorityLevelNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] struct { + nodes []Node[CHAIN_ID, HEAD, RPC] + roundRobinCount []atomic.Uint32 +} + +type nodeWithPriority[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] struct { + node Node[CHAIN_ID, HEAD, RPC] + priority int32 +} + +func NewPriorityLevelNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + return &priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]{ + nodes: nodes, + roundRobinCount: make([]atomic.Uint32, nrOfPriorityTiers(nodes)), + } +} + +func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { + nodes := s.getHighestPriorityAliveTier() + + if len(nodes) == 0 { + return nil + } + priorityLevel := nodes[len(nodes)-1].priority + + // NOTE: Inc returns the number after addition, so we must -1 to get the "current" counter + count := s.roundRobinCount[priorityLevel].Add(1) - 1 + idx := int(count % uint32(len(nodes))) + + return nodes[idx].node +} + +func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { + return NodeSelectionModePriorityLevel +} + +// getHighestPriorityAliveTier filters nodes that are not in state nodeStateAlive and +// returns only the highest tier of alive nodes +func (s priorityLevelNodeSelector[CHAIN_ID, HEAD, RPC]) getHighestPriorityAliveTier() []nodeWithPriority[CHAIN_ID, HEAD, RPC] { + var nodes []nodeWithPriority[CHAIN_ID, HEAD, RPC] + for _, n := range s.nodes { + if n.State() == nodeStateAlive { + nodes = append(nodes, nodeWithPriority[CHAIN_ID, HEAD, RPC]{n, n.Order()}) + } + } + + if len(nodes) == 0 { + return nil + } + + return removeLowerTiers(nodes) +} + +// removeLowerTiers take a slice of nodeWithPriority[CHAIN_ID, BLOCK_HASH, HEAD, RPC] and keeps only the highest tier +func removeLowerTiers[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []nodeWithPriority[CHAIN_ID, HEAD, RPC]) []nodeWithPriority[CHAIN_ID, HEAD, RPC] { + sort.SliceStable(nodes, func(i, j int) bool { + return nodes[i].priority > nodes[j].priority + }) + + var nodes2 []nodeWithPriority[CHAIN_ID, HEAD, RPC] + currentPriority := nodes[len(nodes)-1].priority + + for _, n := range nodes { + if n.priority == currentPriority { + nodes2 = append(nodes2, n) + } + } + + return nodes2 +} + +// nrOfPriorityTiers calculates the total number of priority tiers +func nrOfPriorityTiers[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []Node[CHAIN_ID, HEAD, RPC]) int32 { + highestPriority := int32(0) + for _, n := range nodes { + priority := n.Order() + if highestPriority < priority { + highestPriority = priority + } + } + return highestPriority + 1 +} + +// firstOrHighestPriority takes a list of nodes and returns the first one with the highest priority +func firstOrHighestPriority[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []Node[CHAIN_ID, HEAD, RPC]) Node[CHAIN_ID, HEAD, RPC] { + hp := int32(math.MaxInt32) + var node Node[CHAIN_ID, HEAD, RPC] + for _, n := range nodes { + if n.Order() < hp { + hp = n.Order() + node = n + } + } + return node +} diff --git a/common/client/node_selector_round_robin.go b/common/client/node_selector_round_robin.go new file mode 100644 index 00000000000..5cdad7f52ee --- /dev/null +++ b/common/client/node_selector_round_robin.go @@ -0,0 +1,50 @@ +package client + +import ( + "sync/atomic" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type roundRobinSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] struct { + nodes []Node[CHAIN_ID, HEAD, RPC] + roundRobinCount atomic.Uint32 +} + +func NewRoundRobinSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + return &roundRobinSelector[CHAIN_ID, HEAD, RPC]{ + nodes: nodes, + } +} + +func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { + var liveNodes []Node[CHAIN_ID, HEAD, RPC] + for _, n := range s.nodes { + if n.State() == nodeStateAlive { + liveNodes = append(liveNodes, n) + } + } + + nNodes := len(liveNodes) + if nNodes == 0 { + return nil + } + + // NOTE: Inc returns the number after addition, so we must -1 to get the "current" counter + count := s.roundRobinCount.Add(1) - 1 + idx := int(count % uint32(nNodes)) + + return liveNodes[idx] +} + +func (s *roundRobinSelector[CHAIN_ID, HEAD, RPC]) Name() string { + return NodeSelectionModeRoundRobin +} diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go new file mode 100644 index 00000000000..9b29642d033 --- /dev/null +++ b/common/client/node_selector_total_difficulty.go @@ -0,0 +1,54 @@ +package client + +import ( + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type totalDifficultyNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] []Node[CHAIN_ID, HEAD, RPC] + +func NewTotalDifficultyNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + return totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC](nodes) +} + +func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { + // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil + var highestTD *utils.Big + var nodes []Node[CHAIN_ID, HEAD, RPC] + var aliveNodes []Node[CHAIN_ID, HEAD, RPC] + + for _, n := range s { + state, _, currentTD := n.StateAndLatest() + if state != nodeStateAlive { + continue + } + + aliveNodes = append(aliveNodes, n) + if currentTD != nil && (highestTD == nil || currentTD.Cmp(highestTD) >= 0) { + if highestTD == nil || currentTD.Cmp(highestTD) > 0 { + highestTD = currentTD + nodes = nil + } + nodes = append(nodes, n) + } + } + + //If all nodes have td == nil pick one from the nodes that are alive + if len(nodes) == 0 { + return firstOrHighestPriority(aliveNodes) + } + return firstOrHighestPriority(nodes) +} + +func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { + return NodeSelectionModeTotalDifficulty +} diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go new file mode 100644 index 00000000000..3b382b2dcb0 --- /dev/null +++ b/common/client/send_only_node.go @@ -0,0 +1,183 @@ +package client + +import ( + "context" + "fmt" + "net/url" + "sync" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" + + "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type sendOnlyClient[ + CHAIN_ID types.ID, +] interface { + Close() + ChainID(context.Context) (CHAIN_ID, error) + DialHTTP() error +} + +// SendOnlyNode represents one node used as a sendonly +type SendOnlyNode[ + CHAIN_ID types.ID, + RPC sendOnlyClient[CHAIN_ID], +] interface { + // Start may attempt to connect to the node, but should only return error for misconfiguration - never for temporary errors. + Start(context.Context) error + Close() error + + ConfiguredChainID() CHAIN_ID + RPC() RPC + + String() string + // State returns nodeState + State() nodeState + // Name is a unique identifier for this node. + Name() string +} + +// It only supports sending transactions +// It must use an http(s) url +type sendOnlyNode[ + CHAIN_ID types.ID, + RPC sendOnlyClient[CHAIN_ID], +] struct { + services.StateMachine + + stateMu sync.RWMutex // protects state* fields + state nodeState + + rpc RPC + uri url.URL + log logger.Logger + name string + chainID CHAIN_ID + chStop utils.StopChan + wg sync.WaitGroup +} + +// NewSendOnlyNode returns a new sendonly node +func NewSendOnlyNode[ + CHAIN_ID types.ID, + RPC sendOnlyClient[CHAIN_ID], +]( + lggr logger.Logger, + httpuri url.URL, + name string, + chainID CHAIN_ID, + rpc RPC, +) SendOnlyNode[CHAIN_ID, RPC] { + s := new(sendOnlyNode[CHAIN_ID, RPC]) + s.name = name + s.log = lggr.Named("SendOnlyNode").Named(name).With( + "nodeTier", "sendonly", + ) + s.rpc = rpc + s.uri = httpuri + s.chainID = chainID + s.chStop = make(chan struct{}) + return s +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) Start(ctx context.Context) error { + return s.StartOnce(s.name, func() error { + s.start(ctx) + return nil + }) +} + +// Start setups up and verifies the sendonly node +// Should only be called once in a node's lifecycle +func (s *sendOnlyNode[CHAIN_ID, RPC]) start(startCtx context.Context) { + if s.State() != nodeStateUndialed { + panic(fmt.Sprintf("cannot dial node with state %v", s.state)) + } + + err := s.rpc.DialHTTP() + if err != nil { + promPoolRPCNodeTransitionsToUnusable.WithLabelValues(s.chainID.String(), s.name).Inc() + s.log.Errorw("Dial failed: SendOnly Node is unusable", "err", err) + s.setState(nodeStateUnusable) + return + } + s.setState(nodeStateDialed) + + if s.chainID.String() == "0" { + // Skip verification if chainID is zero + s.log.Warn("sendonly rpc ChainID verification skipped") + } else { + chainID, err := s.rpc.ChainID(startCtx) + if err != nil || chainID.String() != s.chainID.String() { + promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(s.chainID.String(), s.name).Inc() + if err != nil { + promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(s.chainID.String(), s.name).Inc() + s.log.Errorw(fmt.Sprintf("Verify failed: %v", err), "err", err) + s.setState(nodeStateUnreachable) + } else { + promPoolRPCNodeTransitionsToInvalidChainID.WithLabelValues(s.chainID.String(), s.name).Inc() + s.log.Errorf( + "sendonly rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s", + chainID.String(), + s.chainID.String(), + s.name, + ) + s.setState(nodeStateInvalidChainID) + } + // Since it has failed, spin up the verifyLoop that will keep + // retrying until success + s.wg.Add(1) + go s.verifyLoop() + return + } + } + + promPoolRPCNodeTransitionsToAlive.WithLabelValues(s.chainID.String(), s.name).Inc() + s.setState(nodeStateAlive) + s.log.Infow("Sendonly RPC Node is online", "nodeState", s.state) +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) Close() error { + return s.StopOnce(s.name, func() error { + s.rpc.Close() + s.wg.Wait() + s.setState(nodeStateClosed) + return nil + }) +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { + return s.chainID +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { + return s.rpc +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) String() string { + return fmt.Sprintf("(%s)%s:%s", client.Secondary.String(), s.name, s.uri.Redacted()) +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) setState(state nodeState) (changed bool) { + s.stateMu.Lock() + defer s.stateMu.Unlock() + if s.state == state { + return false + } + s.state = state + return true +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) State() nodeState { + s.stateMu.RLock() + defer s.stateMu.RUnlock() + return s.state +} + +func (s *sendOnlyNode[CHAIN_ID, RPC]) Name() string { + return s.name +} diff --git a/common/client/send_only_node_lifecycle.go b/common/client/send_only_node_lifecycle.go new file mode 100644 index 00000000000..0f663eab30e --- /dev/null +++ b/common/client/send_only_node_lifecycle.go @@ -0,0 +1,66 @@ +package client + +import ( + "context" + "fmt" + "time" + + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +// verifyLoop may only be triggered once, on Start, if initial chain ID check +// fails. +// +// It will continue checking until success and then exit permanently. +func (s *sendOnlyNode[CHAIN_ID, RPC]) verifyLoop() { + defer s.wg.Done() + + backoff := utils.NewRedialBackoff() + for { + select { + case <-s.chStop: + return + case <-time.After(backoff.Duration()): + } + chainID, err := s.rpc.ChainID(context.Background()) + if err != nil { + ok := s.IfStarted(func() { + if changed := s.setState(nodeStateUnreachable); changed { + promPoolRPCNodeTransitionsToUnreachable.WithLabelValues(s.chainID.String(), s.name).Inc() + } + }) + if !ok { + return + } + s.log.Errorw(fmt.Sprintf("Verify failed: %v", err), "err", err) + continue + } else if chainID.String() != s.chainID.String() { + ok := s.IfStarted(func() { + if changed := s.setState(nodeStateInvalidChainID); changed { + promPoolRPCNodeTransitionsToInvalidChainID.WithLabelValues(s.chainID.String(), s.name).Inc() + } + }) + if !ok { + return + } + s.log.Errorf( + "sendonly rpc ChainID doesn't match local chain ID: RPC ID=%s, local ID=%s, node name=%s", + chainID.String(), + s.chainID.String(), + s.name, + ) + + continue + } + ok := s.IfStarted(func() { + if changed := s.setState(nodeStateAlive); changed { + promPoolRPCNodeTransitionsToAlive.WithLabelValues(s.chainID.String(), s.name).Inc() + } + }) + if !ok { + return + } + s.log.Infow("Sendonly RPC Node is online", "nodeState", s.state) + return + } +} diff --git a/common/client/types.go b/common/client/types.go new file mode 100644 index 00000000000..f3a6029a9e8 --- /dev/null +++ b/common/client/types.go @@ -0,0 +1,133 @@ +package client + +import ( + "context" + "math/big" + + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +// RPC includes all the necessary methods for a multi-node client to interact directly with any RPC endpoint. +type RPC[ + CHAIN_ID types.ID, + SEQ types.Sequence, + ADDR types.Hashable, + BLOCK_HASH types.Hashable, + TX any, + TX_HASH types.Hashable, + EVENT any, + EVENT_OPS any, + TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], + FEE feetypes.Fee, + HEAD types.Head[BLOCK_HASH], + +] interface { + NodeClient[ + CHAIN_ID, + HEAD, + ] + clientAPI[ + CHAIN_ID, + SEQ, + ADDR, + BLOCK_HASH, + TX, + TX_HASH, + EVENT, + EVENT_OPS, + TX_RECEIPT, + FEE, + HEAD, + ] +} + +// Head is the interface required by the NodeClient +type Head interface { + BlockNumber() int64 + BlockDifficulty() *utils.Big +} + +// NodeClient includes all the necessary RPC methods required by a node. +type NodeClient[ + CHAIN_ID types.ID, + HEAD Head, +] interface { + connection[CHAIN_ID, HEAD] + + DialHTTP() error + DisconnectAll() + Close() + ClientVersion(context.Context) (string, error) + SubscribersCount() int32 + SetAliveLoopSub(types.Subscription) + UnsubscribeAllExceptAliveLoop() +} + +// clientAPI includes all the direct RPC methods required by the generalized common client to implement its own. +type clientAPI[ + CHAIN_ID types.ID, + SEQ types.Sequence, + ADDR types.Hashable, + BLOCK_HASH types.Hashable, + TX any, + TX_HASH types.Hashable, + EVENT any, + EVENT_OPS any, // event filter query options + TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], + FEE feetypes.Fee, + HEAD types.Head[BLOCK_HASH], +] interface { + connection[CHAIN_ID, HEAD] + + // Account + BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) + TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) + SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) + LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) + PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) + EstimateGas(ctx context.Context, call any) (gas uint64, err error) + + // Transactions + SendTransaction(ctx context.Context, tx TX) error + SimulateTransaction(ctx context.Context, tx TX) error + TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) + TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) + SendEmptyTransaction( + ctx context.Context, + newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt any, err error), + seq SEQ, + gasLimit uint32, + fee FEE, + fromAddress ADDR, + ) (txhash string, err error) + + // Blocks + BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) + BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) + LatestBlockHeight(context.Context) (*big.Int, error) + + // Events + FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) + + // Misc + BatchCallContext(ctx context.Context, b []any) error + CallContract( + ctx context.Context, + msg interface{}, + blockNumber *big.Int, + ) (rpcErr []byte, extractErr error) + CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error + CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) +} + +type connection[ + CHAIN_ID types.ID, + HEAD Head, +] interface { + ChainID(ctx context.Context) (CHAIN_ID, error) + Dial(ctx context.Context) error + Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) +} diff --git a/common/headtracker/types/mocks/head.go b/common/headtracker/types/mocks/head.go index edda18d57e8..a56590b6ef3 100644 --- a/common/headtracker/types/mocks/head.go +++ b/common/headtracker/types/mocks/head.go @@ -4,6 +4,7 @@ package mocks import ( types "github.com/smartcontractkit/chainlink/v2/common/types" + utils "github.com/smartcontractkit/chainlink/v2/core/utils" mock "github.com/stretchr/testify/mock" ) @@ -12,6 +13,22 @@ type Head[BLOCK_HASH types.Hashable, CHAIN_ID types.ID] struct { mock.Mock } +// BlockDifficulty provides a mock function with given fields: +func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockDifficulty() *utils.Big { + ret := _m.Called() + + var r0 *utils.Big + if rf, ok := ret.Get(0).(func() *utils.Big); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*utils.Big) + } + } + + return r0 +} + // BlockHash provides a mock function with given fields: func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockHash() BLOCK_HASH { ret := _m.Called() diff --git a/common/types/head.go b/common/types/head.go index 4d339b1cddb..bef9c30d9e9 100644 --- a/common/types/head.go +++ b/common/types/head.go @@ -1,5 +1,7 @@ package types +import "github.com/smartcontractkit/chainlink/v2/core/utils" + // Head provides access to a chain's head, as needed by the TxManager. // This is a generic interface which ALL chains will implement. // @@ -24,4 +26,8 @@ type Head[BLOCK_HASH Hashable] interface { // HashAtHeight returns the hash of the block at the given height, if it is in the chain. // If not in chain, returns the zero hash HashAtHeight(blockNum int64) BLOCK_HASH + + // Returns the total difficulty of the block. For chains who do not have a concept of block + // difficulty, return 0. + BlockDifficulty() *utils.Big } diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go index 3cb303ef267..816a9234a3c 100644 --- a/common/types/mocks/head.go +++ b/common/types/mocks/head.go @@ -4,6 +4,7 @@ package mocks import ( types "github.com/smartcontractkit/chainlink/v2/common/types" + utils "github.com/smartcontractkit/chainlink/v2/core/utils" mock "github.com/stretchr/testify/mock" ) @@ -12,6 +13,22 @@ type Head[BLOCK_HASH types.Hashable] struct { mock.Mock } +// BlockDifficulty provides a mock function with given fields: +func (_m *Head[BLOCK_HASH]) BlockDifficulty() *utils.Big { + ret := _m.Called() + + var r0 *utils.Big + if rf, ok := ret.Get(0).(func() *utils.Big); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*utils.Big) + } + } + + return r0 +} + // BlockHash provides a mock function with given fields: func (_m *Head[BLOCK_HASH]) BlockHash() BLOCK_HASH { ret := _m.Called() diff --git a/common/types/receipt.go b/common/types/receipt.go new file mode 100644 index 00000000000..01d5a72def5 --- /dev/null +++ b/common/types/receipt.go @@ -0,0 +1,14 @@ +package types + +import "math/big" + +type Receipt[TX_HASH Hashable, BLOCK_HASH Hashable] interface { + GetStatus() uint64 + GetTxHash() TX_HASH + GetBlockNumber() *big.Int + IsZero() bool + IsUnmined() bool + GetFeeUsed() uint64 + GetTransactionIndex() uint + GetBlockHash() BLOCK_HASH +} diff --git a/core/chains/evm/chain.go b/core/chains/evm/chain.go index 6eed13271e3..936abc6216c 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/evm/chain.go @@ -498,3 +498,21 @@ func newPrimary(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr return evmclient.NewNode(cfg, noNewHeadsThreshold, lggr, (url.URL)(*n.WSURL), (*url.URL)(n.HTTPURL), *n.Name, id, chainID, *n.Order), nil } + +// TODO-1663: replace newEthClientFromChain with the function below once client.go is deprecated. +//func newEthClientFromChain(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType config.ChainType, nodes []*toml.Node) evmclient.Client { +// var empty url.URL +// var primaries []commonclient.Node[*big.Int, *evmtypes.Head, evmclient.RPCCLient] +// var sendonlys []commonclient.SendOnlyNode[*big.Int, evmclient.RPCCLient] +// for i, node := range nodes { +// if node.SendOnly != nil && *node.SendOnly { +// rpc := evmclient.NewRPCClient(lggr, empty, (*url.URL)(node.HTTPURL), fmt.Sprintf("eth-sendonly-rpc-%d", i), int32(i), chainID, commontypes.Primary) +// sendonly := commonclient.NewSendOnlyNode[*big.Int, evmclient.RPCCLient](lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) +// sendonlys = append(sendonlys, sendonly) +// } else { +// rpc := evmclient.NewRPCClient(lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), fmt.Sprintf("eth-sendonly-rpc-%d", i), int32(i), chainID, commontypes.Primary) +// primaries = append(primaries, commonclient.NewNode[*big.Int, *evmtypes.Head, evmclient.RPCCLient](cfg, noNewHeadsThreshold, lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, *node.Order, rpc, "EVM")) +// } +// } +// return evmclient.NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), noNewHeadsThreshold, primaries, sendonlys, chainID, chainType) +//} diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go new file mode 100644 index 00000000000..bda028cbf33 --- /dev/null +++ b/core/chains/evm/client/chain_client.go @@ -0,0 +1,274 @@ +package client + +import ( + "context" + "math/big" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + + commontypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/core/assets" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +var _ Client = (*chainClient)(nil) + +// TODO-1663: rename this to client, once the client.go file is deprecated. +type chainClient struct { + multiNode commonclient.MultiNode[ + *big.Int, + evmtypes.Nonce, + common.Address, + common.Hash, + *types.Transaction, + common.Hash, + types.Log, + ethereum.FilterQuery, + *evmtypes.Receipt, + *assets.Wei, + *evmtypes.Head, + RPCCLient, + ] + logger logger.Logger +} + +func NewChainClient( + logger logger.Logger, + selectionMode string, + leaseDuration time.Duration, + noNewHeadsThreshold time.Duration, + nodes []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient], + sendonlys []commonclient.SendOnlyNode[*big.Int, RPCCLient], + chainID *big.Int, + chainType config.ChainType, +) Client { + multiNode := commonclient.NewMultiNode[ + *big.Int, + evmtypes.Nonce, + common.Address, + common.Hash, + *types.Transaction, + common.Hash, + types.Log, + ethereum.FilterQuery, + *evmtypes.Receipt, + *assets.Wei, + *evmtypes.Head, + RPCCLient, + ]( + logger, + selectionMode, + leaseDuration, + noNewHeadsThreshold, + nodes, + sendonlys, + chainID, + chainType, + "EVM", + ClassifySendOnlyError, + ) + return &chainClient{ + multiNode: multiNode, + logger: logger, + } +} + +func (c *chainClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + return c.multiNode.BalanceAt(ctx, account, blockNumber) +} + +func (c *chainClient) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { + batch := make([]any, len(b)) + for i, arg := range b { + batch[i] = any(arg) + } + return c.multiNode.BatchCallContext(ctx, batch) +} + +func (c *chainClient) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error { + batch := make([]any, len(b)) + for i, arg := range b { + batch[i] = any(arg) + } + return c.multiNode.BatchCallContextAll(ctx, batch) +} + +// TODO-1663: return custom Block type instead of geth's once client.go is deprecated. +func (c *chainClient) BlockByHash(ctx context.Context, hash common.Hash) (b *types.Block, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return b, err + } + return rpc.BlockByHashGeth(ctx, hash) +} + +// TODO-1663: return custom Block type instead of geth's once client.go is deprecated. +func (c *chainClient) BlockByNumber(ctx context.Context, number *big.Int) (b *types.Block, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return b, err + } + return rpc.BlockByNumberGeth(ctx, number) +} + +func (c *chainClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + return c.multiNode.CallContext(ctx, result, method) +} + +func (c *chainClient) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return c.multiNode.CallContract(ctx, msg, blockNumber) +} + +// TODO-1663: change this to actual ChainID() call once client.go is deprecated. +func (c *chainClient) ChainID() (*big.Int, error) { + //return c.multiNode.ChainID(ctx), nil + return c.multiNode.ConfiguredChainID(), nil +} + +func (c *chainClient) Close() { + c.multiNode.Close() +} + +func (c *chainClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { + return c.multiNode.CodeAt(ctx, account, blockNumber) +} + +func (c *chainClient) ConfiguredChainID() *big.Int { + return c.multiNode.ConfiguredChainID() +} + +func (c *chainClient) Dial(ctx context.Context) error { + return c.multiNode.Dial(ctx) +} + +func (c *chainClient) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) { + return c.multiNode.EstimateGas(ctx, call) +} +func (c *chainClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return c.multiNode.FilterEvents(ctx, q) +} + +func (c *chainClient) HeaderByHash(ctx context.Context, h common.Hash) (head *types.Header, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return head, err + } + return rpc.HeaderByHash(ctx, h) +} + +func (c *chainClient) HeaderByNumber(ctx context.Context, n *big.Int) (head *types.Header, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return head, err + } + return rpc.HeaderByNumber(ctx, n) +} + +func (c *chainClient) HeadByHash(ctx context.Context, h common.Hash) (*evmtypes.Head, error) { + return c.multiNode.BlockByHash(ctx, h) +} + +func (c *chainClient) HeadByNumber(ctx context.Context, n *big.Int) (*evmtypes.Head, error) { + return c.multiNode.BlockByNumber(ctx, n) +} + +func (c *chainClient) IsL2() bool { + return c.multiNode.IsL2() +} + +func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*assets.Link, error) { + return c.multiNode.LINKBalance(ctx, address, linkAddress) +} + +func (c *chainClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { + return c.multiNode.LatestBlockHeight(ctx) +} + +func (c *chainClient) NodeStates() map[string]string { + return c.multiNode.NodeStates() +} + +func (c *chainClient) PendingCodeAt(ctx context.Context, account common.Address) (b []byte, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return b, err + } + return rpc.PendingCodeAt(ctx, account) +} + +// TODO-1663: change this to evmtypes.Nonce(int64) once client.go is deprecated. +func (c *chainClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + n, err := c.multiNode.PendingSequenceAt(ctx, account) + return uint64(n), err +} + +func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { + return c.multiNode.SendTransaction(ctx, tx) +} + +func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commontypes.SendTxReturnCode, error) { + err := c.SendTransaction(ctx, tx) + return ClassifySendError(err, c.logger, tx, fromAddress, c.IsL2()) +} + +func (c *chainClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (evmtypes.Nonce, error) { + return c.multiNode.SequenceAt(ctx, account, blockNumber) +} + +func (c *chainClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (s ethereum.Subscription, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return s, err + } + return rpc.SubscribeFilterLogs(ctx, q, ch) +} + +func (c *chainClient) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) { + csf := newChainIDSubForwarder(c.ConfiguredChainID(), ch) + err := csf.start(c.multiNode.Subscribe(ctx, csf.srcCh, "newHeads")) + if err != nil { + return nil, err + } + return csf, nil +} + +func (c *chainClient) SuggestGasPrice(ctx context.Context) (p *big.Int, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return p, err + } + return rpc.SuggestGasPrice(ctx) +} + +func (c *chainClient) SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return t, err + } + return rpc.SuggestGasTipCap(ctx) +} + +func (c *chainClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { + return c.multiNode.TokenBalance(ctx, address, contractAddress) +} + +func (c *chainClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) { + return c.multiNode.TransactionByHash(ctx, txHash) +} + +// TODO-1663: return custom Receipt type instead of geth's once client.go is deprecated. +func (c *chainClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) { + rpc, err := c.multiNode.SelectNodeRPC() + if err != nil { + return r, err + } + //return rpc.TransactionReceipt(ctx, txHash) + return rpc.TransactionReceiptGeth(ctx, txHash) +} diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index 3a3b8b23a92..af03720ced9 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -213,7 +213,7 @@ func (client *client) HeaderByHash(ctx context.Context, h common.Hash) (*types.H func (client *client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (clienttypes.SendTxReturnCode, error) { err := client.SendTransaction(ctx, tx) - return NewSendErrorReturnCode(err, client.logger, tx, fromAddress, client.pool.ChainType().IsL2()) + return ClassifySendError(err, client.logger, tx, fromAddress, client.pool.ChainType().IsL2()) } // SendTransaction also uses the sendonly HTTP RPC URLs if set diff --git a/core/chains/evm/client/client_test.go b/core/chains/evm/client/client_test.go index 88bc37411c6..81a82d20fa7 100644 --- a/core/chains/evm/client/client_test.go +++ b/core/chains/evm/client/client_test.go @@ -22,7 +22,9 @@ import ( "github.com/stretchr/testify/require" "github.com/tidwall/gjson" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + + commontypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -43,6 +45,33 @@ func mustNewClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, send return c } +func mustNewChainClient(t *testing.T, wsURL string, sendonlys ...url.URL) evmclient.Client { + return mustNewChainClientWithChainID(t, wsURL, testutils.FixtureChainID, sendonlys...) +} + +func mustNewChainClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) evmclient.Client { + cfg := evmclient.TestNodePoolConfig{ + NodeSelectionMode: evmclient.NodeSelectionMode_RoundRobin, + } + c, err := evmclient.NewChainClientWithTestNode(t, cfg, time.Second*0, cfg.NodeLeaseDuration, wsURL, nil, sendonlys, 42, chainID) + require.NoError(t, err) + return c +} + +func mustNewClients(t *testing.T, wsURL string, sendonlys ...url.URL) []evmclient.Client { + var clients []evmclient.Client + clients = append(clients, mustNewClient(t, wsURL, sendonlys...)) + clients = append(clients, mustNewChainClient(t, wsURL, sendonlys...)) + return clients +} + +func mustNewClientsWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) []evmclient.Client { + var clients []evmclient.Client + clients = append(clients, mustNewClientWithChainID(t, wsURL, chainID, sendonlys...)) + clients = append(clients, mustNewChainClientWithChainID(t, wsURL, chainID, sendonlys...)) + return clients +} + func TestEthClient_TransactionReceipt(t *testing.T) { t.Parallel() @@ -78,15 +107,17 @@ func TestEthClient_TransactionReceipt(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - hash := common.HexToHash(txHash) - receipt, err := ethClient.TransactionReceipt(testutils.Context(t), hash) - require.NoError(t, err) - assert.Equal(t, hash, receipt.TxHash) - assert.Equal(t, big.NewInt(11), receipt.BlockNumber) + hash := common.HexToHash(txHash) + receipt, err := ethClient.TransactionReceipt(testutils.Context(t), hash) + require.NoError(t, err) + assert.Equal(t, hash, receipt.TxHash) + assert.Equal(t, big.NewInt(11), receipt.BlockNumber) + } }) t.Run("no tx hash, returns ethereum.NotFound", func(t *testing.T) { @@ -108,13 +139,15 @@ func TestEthClient_TransactionReceipt(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - hash := common.HexToHash(txHash) - _, err = ethClient.TransactionReceipt(testutils.Context(t), hash) - require.Equal(t, ethereum.NotFound, errors.Cause(err)) + hash := common.HexToHash(txHash) + _, err = ethClient.TransactionReceipt(testutils.Context(t), hash) + require.Equal(t, ethereum.NotFound, errors.Cause(err)) + } }) } @@ -144,15 +177,17 @@ func TestEthClient_PendingNonceAt(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - result, err := ethClient.PendingNonceAt(testutils.Context(t), address) - require.NoError(t, err) + result, err := ethClient.PendingNonceAt(testutils.Context(t), address) + require.NoError(t, err) - var expected uint64 = 256 - require.Equal(t, result, expected) + var expected uint64 = 256 + require.Equal(t, result, expected) + } } func TestEthClient_BalanceAt(t *testing.T) { @@ -189,13 +224,15 @@ func TestEthClient_BalanceAt(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - result, err := ethClient.BalanceAt(testutils.Context(t), address, nil) - require.NoError(t, err) - assert.Equal(t, test.balance, result) + result, err := ethClient.BalanceAt(testutils.Context(t), address, nil) + require.NoError(t, err) + assert.Equal(t, test.balance, result) + } }) } } @@ -220,13 +257,15 @@ func TestEthClient_LatestBlockHeight(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - result, err := ethClient.LatestBlockHeight(testutils.Context(t)) - require.NoError(t, err) - require.Equal(t, big.NewInt(256), result) + result, err := ethClient.LatestBlockHeight(testutils.Context(t)) + require.NoError(t, err) + require.Equal(t, big.NewInt(256), result) + } } func TestEthClient_GetERC20Balance(t *testing.T) { @@ -277,13 +316,15 @@ func TestEthClient_GetERC20Balance(t *testing.T) { }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - result, err := ethClient.TokenBalance(ctx, userAddress, contractAddress) - require.NoError(t, err) - assert.Equal(t, test.balance, result) + result, err := ethClient.TokenBalance(ctx, userAddress, contractAddress) + require.NoError(t, err) + assert.Equal(t, test.balance, result) + } }) } } @@ -354,20 +395,22 @@ func TestEthClient_HeaderByNumber(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) - - ctx, cancel := context.WithTimeout(testutils.Context(t), 5*time.Second) - defer cancel() - result, err := ethClient.HeadByNumber(ctx, expectedBlockNum) - if test.error != nil { - require.Error(t, err, test.error) - } else { + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) require.NoError(t, err) - require.Equal(t, expectedBlockHash, result.Hash.Hex()) - require.Equal(t, test.expectedResponseBlock, result.Number) - require.Zero(t, cltest.FixtureChainID.Cmp(result.EVMChainID.ToInt())) + + ctx, cancel := context.WithTimeout(testutils.Context(t), 5*time.Second) + result, err := ethClient.HeadByNumber(ctx, expectedBlockNum) + if test.error != nil { + require.Error(t, err, test.error) + } else { + require.NoError(t, err) + require.Equal(t, expectedBlockHash, result.Hash.Hex()) + require.Equal(t, test.expectedResponseBlock, result.Number) + require.Zero(t, cltest.FixtureChainID.Cmp(result.EVMChainID.ToInt())) + } + cancel() } }) } @@ -395,12 +438,14 @@ func TestEthClient_SendTransaction_NoSecondaryURL(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - err = ethClient.SendTransaction(testutils.Context(t), tx) - assert.NoError(t, err) + err = ethClient.SendTransaction(testutils.Context(t), tx) + assert.NoError(t, err) + } } func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { @@ -432,16 +477,19 @@ func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { t.Cleanup(ts.Close) sendonlyURL := *cltest.MustParseURL(t, ts.URL) - ethClient := mustNewClient(t, wsURL, sendonlyURL, sendonlyURL) - err = ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) - err = ethClient.SendTransaction(testutils.Context(t), tx) - require.NoError(t, err) + clients := mustNewClients(t, wsURL, sendonlyURL, sendonlyURL) + for _, ethClient := range clients { + err = ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) + + err = ethClient.SendTransaction(testutils.Context(t), tx) + require.NoError(t, err) + } // Unfortunately it's a bit tricky to test this, since there is no // synchronization. We have to rely on timing instead. - require.Eventually(t, func() bool { return service.sentCount.Load() == int32(2) }, testutils.WaitTimeout(t), 500*time.Millisecond) + require.Eventually(t, func() bool { return service.sentCount.Load() == int32(len(clients)*2) }, testutils.WaitTimeout(t), 500*time.Millisecond) } func TestEthClient_SendTransactionReturnCode(t *testing.T) { @@ -467,13 +515,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.Fatal) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.Fatal) + } }) t.Run("returns TransactionAlreadyKnown error type when error message is nonce too low", func(t *testing.T) { @@ -493,13 +543,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.TransactionAlreadyKnown) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.TransactionAlreadyKnown) + } }) t.Run("returns Successful error type when there is no error message", func(t *testing.T) { @@ -518,13 +570,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.NoError(t, err) - assert.Equal(t, errType, clienttypes.Successful) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.NoError(t, err) + assert.Equal(t, errType, commontypes.Successful) + } }) t.Run("returns Underpriced error type when transaction is terminally underpriced", func(t *testing.T) { @@ -544,13 +598,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.Underpriced) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.Underpriced) + } }) t.Run("returns Unsupported error type when error message is queue full", func(t *testing.T) { @@ -570,13 +626,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.Unsupported) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.Unsupported) + } }) t.Run("returns Retryable error type when there is a transaction gap", func(t *testing.T) { @@ -596,13 +654,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.Retryable) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.Retryable) + } }) t.Run("returns InsufficientFunds error type when the sender address doesn't have enough funds", func(t *testing.T) { @@ -622,13 +682,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.InsufficientFunds) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.InsufficientFunds) + } }) t.Run("returns ExceedsFeeCap error type when gas price is too high for the node", func(t *testing.T) { @@ -648,13 +710,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.ExceedsMaxFee) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.ExceedsMaxFee) + } }) t.Run("returns Unknown error type when the error can't be categorized", func(t *testing.T) { @@ -674,13 +738,15 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { return }) - ethClient := mustNewClient(t, wsURL) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClients(t, wsURL) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) - assert.Error(t, err) - assert.Equal(t, errType, clienttypes.Unknown) + errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) + assert.Error(t, err) + assert.Equal(t, errType, commontypes.Unknown) + } }) } @@ -718,24 +784,132 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { return }) - ethClient := mustNewClientWithChainID(t, wsURL, chainId) - err := ethClient.Dial(testutils.Context(t)) - require.NoError(t, err) + clients := mustNewClientsWithChainID(t, wsURL, chainId) + for _, ethClient := range clients { + err := ethClient.Dial(testutils.Context(t)) + require.NoError(t, err) - headCh := make(chan *evmtypes.Head) - sub, err := ethClient.SubscribeNewHead(ctx, headCh) - require.NoError(t, err) - defer sub.Unsubscribe() - - select { - case err := <-sub.Err(): - t.Fatal(err) - case <-ctx.Done(): - t.Fatal(ctx.Err()) - case h := <-headCh: - require.NotNil(t, h.EVMChainID) - require.Zero(t, chainId.Cmp(h.EVMChainID.ToInt())) + headCh := make(chan *evmtypes.Head) + sub, err := ethClient.SubscribeNewHead(ctx, headCh) + require.NoError(t, err) + + select { + case err := <-sub.Err(): + t.Fatal(err) + case <-ctx.Done(): + t.Fatal(ctx.Err()) + case h := <-headCh: + require.NotNil(t, h.EVMChainID) + require.Zero(t, chainId.Cmp(h.EVMChainID.ToInt())) + } + sub.Unsubscribe() } } +func TestEthClient_ErroringClient(t *testing.T) { + t.Parallel() + ctx := testutils.Context(t) + + // Empty node means there are no active nodes to select from, causing client to always return error. + erroringClient := evmclient.NewChainClientWithEmptyNode(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID) + + _, err := erroringClient.BalanceAt(ctx, common.Address{}, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + err = erroringClient.BatchCallContext(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + err = erroringClient.BatchCallContextAll(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.BlockByHash(ctx, common.Hash{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.BlockByNumber(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + err = erroringClient.CallContext(ctx, nil, "") + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.CallContract(ctx, ethereum.CallMsg{}, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + // TODO-1663: test actual ChainID() call once client.go is deprecated. + id, err := erroringClient.ChainID() + require.Equal(t, id, testutils.FixtureChainID) + //require.Equal(t, err, commonclient.ErroringNodeError) + require.Equal(t, err, nil) + + _, err = erroringClient.CodeAt(ctx, common.Address{}, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + id = erroringClient.ConfiguredChainID() + require.Equal(t, id, testutils.FixtureChainID) + + err = erroringClient.Dial(ctx) + require.ErrorContains(t, err, "no available nodes for chain") + + _, err = erroringClient.EstimateGas(ctx, ethereum.CallMsg{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.FilterLogs(ctx, ethereum.FilterQuery{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.HeaderByHash(ctx, common.Hash{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.HeaderByNumber(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.HeadByHash(ctx, common.Hash{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.HeadByNumber(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.LINKBalance(ctx, common.Address{}, common.Address{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.LatestBlockHeight(ctx) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.PendingCodeAt(ctx, common.Address{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.PendingNonceAt(ctx, common.Address{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + err = erroringClient.SendTransaction(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + code, err := erroringClient.SendTransactionReturnCode(ctx, nil, common.Address{}) + require.Equal(t, code, commontypes.Unknown) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.SequenceAt(ctx, common.Address{}, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.SubscribeFilterLogs(ctx, ethereum.FilterQuery{}, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.SubscribeNewHead(ctx, nil) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.SuggestGasPrice(ctx) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.SuggestGasTipCap(ctx) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.TokenBalance(ctx, common.Address{}, common.Address{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.TransactionByHash(ctx, common.Hash{}) + require.Equal(t, err, commonclient.ErroringNodeError) + + _, err = erroringClient.TransactionReceipt(ctx, common.Hash{}) + require.Equal(t, err, commonclient.ErroringNodeError) + +} + const headResult = evmclient.HeadResult diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 7b89e7b92d1..7197d77b3d9 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -397,7 +397,7 @@ func ExtractRPCError(baseErr error) (*JsonError, error) { return &jErr, nil } -func NewSendErrorReturnCode(err error, lggr logger.Logger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (clienttypes.SendTxReturnCode, error) { +func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (clienttypes.SendTxReturnCode, error) { sendError := NewSendError(err) if sendError == nil { return clienttypes.Successful, err @@ -465,3 +465,15 @@ func NewSendErrorReturnCode(err error, lggr logger.Logger, tx *types.Transaction } return clienttypes.Unknown, err } + +// ClassifySendOnlyError handles SendOnly nodes error codes. In that case, we don't assume there is another transaction that will be correctly +// priced. +func ClassifySendOnlyError(err error) clienttypes.SendTxReturnCode { + sendError := NewSendError(err) + if sendError == nil || sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() || sendError.IsTransactionAlreadyInMempool() { + // Nonce too low or transaction known errors are expected since + // the primary SendTransaction may well have succeeded already + return clienttypes.Successful + } + return clienttypes.Fatal +} diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 342a9143432..8552b2c0a06 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -9,7 +9,11 @@ import ( "github.com/pkg/errors" + clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + commonconfig "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -64,6 +68,67 @@ func Wrap(err error, s string) error { return wrap(err, s) } +func NewChainClientWithTestNode( + t *testing.T, + nodeCfg commonclient.NodeConfig, + noNewHeadsThreshold time.Duration, + leaseDuration time.Duration, + rpcUrl string, + rpcHTTPURL *url.URL, + sendonlyRPCURLs []url.URL, + id int32, + chainID *big.Int, +) (Client, error) { + parsed, err := url.ParseRequestURI(rpcUrl) + if err != nil { + return nil, err + } + + if parsed.Scheme != "ws" && parsed.Scheme != "wss" { + return nil, errors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) + } + + lggr := logger.TestLogger(t) + rpc := NewRPCClient(lggr, *parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, clienttypes.Primary) + + n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( + nodeCfg, noNewHeadsThreshold, lggr, *parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") + primaries := []commonclient.Node[*big.Int, *evmtypes.Head, RPCCLient]{n} + + var sendonlys []commonclient.SendOnlyNode[*big.Int, RPCCLient] + for i, u := range sendonlyRPCURLs { + if u.Scheme != "http" && u.Scheme != "https" { + return nil, errors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) + } + var empty url.URL + rpc := NewRPCClient(lggr, empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, clienttypes.Secondary) + s := commonclient.NewSendOnlyNode[*big.Int, RPCCLient]( + lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc) + sendonlys = append(sendonlys, s) + } + + var chainType commonconfig.ChainType + c := NewChainClient(lggr, nodeCfg.SelectionMode(), leaseDuration, noNewHeadsThreshold, primaries, sendonlys, chainID, chainType) + t.Cleanup(c.Close) + return c, nil +} + +func NewChainClientWithEmptyNode( + t *testing.T, + selectionMode string, + leaseDuration time.Duration, + noNewHeadsThreshold time.Duration, + chainID *big.Int, +) Client { + + lggr := logger.TestLogger(t) + + var chainType commonconfig.ChainType + c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType) + t.Cleanup(c.Close) + return c +} + type TestableSendOnlyNode interface { SendOnlyNode SetEthClient(newBatchSender BatchSender, newSender TxSender) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go new file mode 100644 index 00000000000..b6ed84eee4e --- /dev/null +++ b/core/chains/evm/client/rpc_client.go @@ -0,0 +1,1046 @@ +package client + +import ( + "context" + "fmt" + "math/big" + "net/url" + "strconv" + "sync" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/google/uuid" + "github.com/pkg/errors" + + clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + commontypes "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/assets" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +// RPCCLient includes all the necessary generalized RPC methods along with any additional chain-specific methods. +type RPCCLient interface { + commonclient.RPC[ + *big.Int, + evmtypes.Nonce, + common.Address, + common.Hash, + *types.Transaction, + common.Hash, + types.Log, + ethereum.FilterQuery, + *evmtypes.Receipt, + *assets.Wei, + *evmtypes.Head, + ] + BlockByHashGeth(ctx context.Context, hash common.Hash) (b *types.Block, err error) + BlockByNumberGeth(ctx context.Context, number *big.Int) (b *types.Block, err error) + HeaderByHash(ctx context.Context, h common.Hash) (head *types.Header, err error) + HeaderByNumber(ctx context.Context, n *big.Int) (head *types.Header, err error) + PendingCodeAt(ctx context.Context, account common.Address) (b []byte, err error) + SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (s ethereum.Subscription, err error) + SuggestGasPrice(ctx context.Context) (p *big.Int, err error) + SuggestGasTipCap(ctx context.Context) (t *big.Int, err error) + TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (r *types.Receipt, err error) +} + +type rpcClient struct { + rpcLog logger.Logger + name string + id int32 + chainID *big.Int + tier clienttypes.NodeTier + + ws rawclient + http *rawclient + + stateMu sync.RWMutex // protects state* fields + + // Need to track subscriptions because closing the RPC does not (always?) + // close the underlying subscription + subs []ethereum.Subscription + + // Need to track the aliveLoop subscription, so we do not cancel it when checking lease on the MultiNode + aliveLoopSub ethereum.Subscription + + // chStopInFlight can be closed to immediately cancel all in-flight requests on + // this rpcClient. Closing and replacing should be serialized through + // stateMu since it can happen on state transitions as well as rpcClient Close. + chStopInFlight chan struct{} +} + +// NewRPCCLient returns a new *rpcClient as commonclient.RPC +func NewRPCClient( + lggr logger.Logger, + wsuri url.URL, + httpuri *url.URL, + name string, + id int32, + chainID *big.Int, + tier clienttypes.NodeTier, +) RPCCLient { + r := new(rpcClient) + r.name = name + r.id = id + r.chainID = chainID + r.tier = tier + r.ws.uri = wsuri + if httpuri != nil { + r.http = &rawclient{uri: *httpuri} + } + r.chStopInFlight = make(chan struct{}) + lggr = lggr.Named("Client").With( + "clientTier", tier.String(), + "clientName", name, + "client", r.String(), + "evmChainID", chainID, + ) + r.rpcLog = lggr.Named("RPC") + + return r +} + +// Not thread-safe, pure dial. +func (r *rpcClient) Dial(callerCtx context.Context) error { + ctx, cancel := r.makeQueryCtx(callerCtx) + defer cancel() + + promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() + lggr := r.rpcLog.With("wsuri", r.ws.uri.Redacted()) + if r.http != nil { + lggr = lggr.With("httpuri", r.http.uri.Redacted()) + } + lggr.Debugw("RPC dial: evmclient.Client#dial") + + wsrpc, err := rpc.DialWebsocket(ctx, r.ws.uri.String(), "") + if err != nil { + promEVMPoolRPCNodeDialsFailed.WithLabelValues(r.chainID.String(), r.name).Inc() + return errors.Wrapf(err, "error while dialing websocket: %v", r.ws.uri.Redacted()) + } + + r.ws.rpc = wsrpc + r.ws.geth = ethclient.NewClient(wsrpc) + + if r.http != nil { + if err := r.DialHTTP(); err != nil { + return err + } + } + + promEVMPoolRPCNodeDialsSuccess.WithLabelValues(r.chainID.String(), r.name).Inc() + + return nil +} + +// Not thread-safe, pure dial. +// DialHTTP doesn't actually make any external HTTP calls +// It can only return error if the URL is malformed. +func (r *rpcClient) DialHTTP() error { + promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() + lggr := r.rpcLog.With("httpuri", r.ws.uri.Redacted()) + lggr.Debugw("RPC dial: evmclient.Client#dial") + + var httprpc *rpc.Client + httprpc, err := rpc.DialHTTP(r.http.uri.String()) + if err != nil { + promEVMPoolRPCNodeDialsFailed.WithLabelValues(r.chainID.String(), r.name).Inc() + return errors.Wrapf(err, "error while dialing HTTP: %v", r.http.uri.Redacted()) + } + + r.http.rpc = httprpc + r.http.geth = ethclient.NewClient(httprpc) + + promEVMPoolRPCNodeDialsSuccess.WithLabelValues(r.chainID.String(), r.name).Inc() + + return nil +} + +func (r *rpcClient) Close() { + defer func() { + if r.ws.rpc != nil { + r.ws.rpc.Close() + } + }() + + r.stateMu.Lock() + defer r.stateMu.Unlock() + r.cancelInflightRequests() +} + +// cancelInflightRequests closes and replaces the chStopInFlight +// WARNING: NOT THREAD-SAFE +// This must be called from within the r.stateMu lock +func (r *rpcClient) cancelInflightRequests() { + close(r.chStopInFlight) + r.chStopInFlight = make(chan struct{}) +} + +func (r *rpcClient) String() string { + s := fmt.Sprintf("(%s)%s:%s", r.tier.String(), r.name, r.ws.uri.Redacted()) + if r.http != nil { + s = s + fmt.Sprintf(":%s", r.http.uri.Redacted()) + } + return s +} + +func (r *rpcClient) logResult( + lggr logger.Logger, + err error, + callDuration time.Duration, + rpcDomain, + callName string, + results ...interface{}, +) { + lggr = lggr.With("duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) + promEVMPoolRPCNodeCalls.WithLabelValues(r.chainID.String(), r.name).Inc() + if err == nil { + promEVMPoolRPCNodeCallsSuccess.WithLabelValues(r.chainID.String(), r.name).Inc() + lggr.Tracew( + fmt.Sprintf("evmclient.Client#%s RPC call success", callName), + results..., + ) + } else { + promEVMPoolRPCNodeCallsFailed.WithLabelValues(r.chainID.String(), r.name).Inc() + lggr.Debugw( + fmt.Sprintf("evmclient.Client#%s RPC call failure", callName), + append(results, "err", err)..., + ) + } + promEVMPoolRPCCallTiming. + WithLabelValues( + r.chainID.String(), // chain id + r.name, // rpcClient name + rpcDomain, // rpc domain + "false", // is send only + strconv.FormatBool(err == nil), // is successful + callName, // rpc call name + ). + Observe(float64(callDuration)) +} + +func (r *rpcClient) getRPCDomain() string { + if r.http != nil { + return r.http.uri.Host + } + return r.ws.uri.Host +} + +// registerSub adds the sub to the rpcClient list +func (r *rpcClient) registerSub(sub ethereum.Subscription) { + r.stateMu.Lock() + defer r.stateMu.Unlock() + r.subs = append(r.subs, sub) +} + +// disconnectAll disconnects all clients connected to the rpcClient +// WARNING: NOT THREAD-SAFE +// This must be called from within the r.stateMu lock +func (r *rpcClient) DisconnectAll() { + if r.ws.rpc != nil { + r.ws.rpc.Close() + } + r.cancelInflightRequests() + r.unsubscribeAll() +} + +// unsubscribeAll unsubscribes all subscriptions +// WARNING: NOT THREAD-SAFE +// This must be called from within the r.stateMu lock +func (r *rpcClient) unsubscribeAll() { + for _, sub := range r.subs { + sub.Unsubscribe() + } + r.subs = nil +} +func (r *rpcClient) SetAliveLoopSub(sub commontypes.Subscription) { + r.stateMu.Lock() + defer r.stateMu.Unlock() + + r.aliveLoopSub = sub +} + +// SubscribersCount returns the number of client subscribed to the node +func (r *rpcClient) SubscribersCount() int32 { + r.stateMu.RLock() + defer r.stateMu.RUnlock() + return int32(len(r.subs)) +} + +// UnsubscribeAllExceptAliveLoop disconnects all subscriptions to the node except the alive loop subscription +// while holding the n.stateMu lock +func (r *rpcClient) UnsubscribeAllExceptAliveLoop() { + r.stateMu.Lock() + defer r.stateMu.Unlock() + + for _, s := range r.subs { + if s != r.aliveLoopSub { + s.Unsubscribe() + } + } +} + +// RPC wrappers + +// CallContext implementation +func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return err + } + defer cancel() + lggr := r.newRqLggr().With( + "method", method, + "args", args, + ) + + lggr.Debug("RPC call: evmclient.Client#CallContext") + start := time.Now() + if http != nil { + err = r.wrapHTTP(http.rpc.CallContext(ctx, result, method, args...)) + } else { + err = r.wrapWS(ws.rpc.CallContext(ctx, result, method, args...)) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "CallContext") + + return err +} + +func (r *rpcClient) BatchCallContext(ctx context.Context, b []any) error { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return err + } + batch := make([]rpc.BatchElem, len(b)) + for i, arg := range b { + batch[i] = arg.(rpc.BatchElem) + } + defer cancel() + lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) + + lggr.Trace("RPC call: evmclient.Client#BatchCallContext") + start := time.Now() + if http != nil { + err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, batch)) + } else { + err = r.wrapWS(ws.rpc.BatchCallContext(ctx, batch)) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "BatchCallContext") + + return err +} + +func (r *rpcClient) Subscribe(ctx context.Context, channel chan<- *evmtypes.Head, args ...interface{}) (commontypes.Subscription, error) { + ctx, cancel, ws, _, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("args", args) + + lggr.Debug("RPC call: evmclient.Client#EthSubscribe") + start := time.Now() + sub, err := ws.rpc.EthSubscribe(ctx, channel, args...) + if err == nil { + r.registerSub(sub) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "EthSubscribe") + + return sub, err +} + +// GethClient wrappers + +func (r *rpcClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (receipt *evmtypes.Receipt, err error) { + err = r.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash, false) + if err != nil { + return nil, err + } + if receipt == nil { + err = ethereum.NotFound + return + } + return +} + +func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("txHash", txHash) + + lggr.Debug("RPC call: evmclient.Client#TransactionReceipt") + + start := time.Now() + if http != nil { + receipt, err = http.geth.TransactionReceipt(ctx, txHash) + err = r.wrapHTTP(err) + } else { + receipt, err = ws.geth.TransactionReceipt(ctx, txHash) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "TransactionReceipt", + "receipt", receipt, + ) + + return +} +func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("txHash", txHash) + + lggr.Debug("RPC call: evmclient.Client#TransactionByHash") + + start := time.Now() + if http != nil { + tx, _, err = http.geth.TransactionByHash(ctx, txHash) + err = r.wrapHTTP(err) + } else { + tx, _, err = ws.geth.TransactionByHash(ctx, txHash) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "TransactionByHash", + "receipt", tx, + ) + + return +} + +func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header *types.Header, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("number", number) + + lggr.Debug("RPC call: evmclient.Client#HeaderByNumber") + start := time.Now() + if http != nil { + header, err = http.geth.HeaderByNumber(ctx, number) + err = r.wrapHTTP(err) + } else { + header, err = ws.geth.HeaderByNumber(ctx, number) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "HeaderByNumber", "header", header) + + return +} + +func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header *types.Header, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("hash", hash) + + lggr.Debug("RPC call: evmclient.Client#HeaderByHash") + start := time.Now() + if http != nil { + header, err = http.geth.HeaderByHash(ctx, hash) + err = r.wrapHTTP(err) + } else { + header, err = ws.geth.HeaderByHash(ctx, hash) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "HeaderByHash", + "header", header, + ) + + return +} + +func (r *rpcClient) BlockByNumber(ctx context.Context, number *big.Int) (head *evmtypes.Head, err error) { + hex := ToBlockNumArg(number) + err = r.CallContext(ctx, &head, "eth_getBlockByNumber", hex, false) + if err != nil { + return nil, err + } + if head == nil { + err = ethereum.NotFound + return + } + head.EVMChainID = utils.NewBig(r.chainID) + return +} + +func (r *rpcClient) BlockByHash(ctx context.Context, hash common.Hash) (head *evmtypes.Head, err error) { + err = r.CallContext(ctx, &head, "eth_getBlockByHash", hash.Hex(), false) + if err != nil { + return nil, err + } + if head == nil { + err = ethereum.NotFound + return + } + head.EVMChainID = utils.NewBig(r.chainID) + return +} + +func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (block *types.Block, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("hash", hash) + + lggr.Debug("RPC call: evmclient.Client#BlockByHash") + start := time.Now() + if http != nil { + block, err = http.geth.BlockByHash(ctx, hash) + err = r.wrapHTTP(err) + } else { + block, err = ws.geth.BlockByHash(ctx, hash) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "BlockByHash", + "block", block, + ) + + return +} + +func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (block *types.Block, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("number", number) + + lggr.Debug("RPC call: evmclient.Client#BlockByNumber") + start := time.Now() + if http != nil { + block, err = http.geth.BlockByNumber(ctx, number) + err = r.wrapHTTP(err) + } else { + block, err = ws.geth.BlockByNumber(ctx, number) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "BlockByNumber", + "block", block, + ) + + return +} + +func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return err + } + defer cancel() + lggr := r.newRqLggr().With("tx", tx) + + lggr.Debug("RPC call: evmclient.Client#SendTransaction") + start := time.Now() + if http != nil { + err = r.wrapHTTP(http.geth.SendTransaction(ctx, tx)) + } else { + err = r.wrapWS(ws.geth.SendTransaction(ctx, tx)) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "SendTransaction") + + return err +} + +func (r *rpcClient) SimulateTransaction(ctx context.Context, tx *types.Transaction) error { + // Not Implemented + return errors.New("SimulateTransaction not implemented") +} + +func (r *rpcClient) SendEmptyTransaction( + ctx context.Context, + newTxAttempt func(nonce evmtypes.Nonce, feeLimit uint32, fee *assets.Wei, fromAddress common.Address) (attempt any, err error), + nonce evmtypes.Nonce, + gasLimit uint32, + fee *assets.Wei, + fromAddress common.Address, +) (txhash string, err error) { + // Not Implemented + return "", errors.New("SendEmptyTransaction not implemented") +} + +// PendingSequenceAt returns one higher than the highest nonce from both mempool and mined transactions +func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Address) (nonce evmtypes.Nonce, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return 0, err + } + defer cancel() + lggr := r.newRqLggr().With("account", account) + + lggr.Debug("RPC call: evmclient.Client#PendingNonceAt") + start := time.Now() + var n uint64 + if http != nil { + n, err = http.geth.PendingNonceAt(ctx, account) + nonce = evmtypes.Nonce(int64(n)) + err = r.wrapHTTP(err) + } else { + n, err = ws.geth.PendingNonceAt(ctx, account) + nonce = evmtypes.Nonce(int64(n)) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "PendingNonceAt", + "nonce", nonce, + ) + + return +} + +// SequenceAt is a bit of a misnomer. You might expect it to return the highest +// mined nonce at the given block number, but it actually returns the total +// transaction count which is the highest mined nonce + 1 +func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (nonce evmtypes.Nonce, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return 0, err + } + defer cancel() + lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) + + lggr.Debug("RPC call: evmclient.Client#NonceAt") + start := time.Now() + var n uint64 + if http != nil { + n, err = http.geth.NonceAt(ctx, account, blockNumber) + nonce = evmtypes.Nonce(int64(n)) + err = r.wrapHTTP(err) + } else { + n, err = ws.geth.NonceAt(ctx, account, blockNumber) + nonce = evmtypes.Nonce(int64(n)) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "NonceAt", + "nonce", nonce, + ) + + return +} + +func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) (code []byte, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("account", account) + + lggr.Debug("RPC call: evmclient.Client#PendingCodeAt") + start := time.Now() + if http != nil { + code, err = http.geth.PendingCodeAt(ctx, account) + err = r.wrapHTTP(err) + } else { + code, err = ws.geth.PendingCodeAt(ctx, account) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "PendingCodeAt", + "code", code, + ) + + return +} + +func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) (code []byte, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) + + lggr.Debug("RPC call: evmclient.Client#CodeAt") + start := time.Now() + if http != nil { + code, err = http.geth.CodeAt(ctx, account, blockNumber) + err = r.wrapHTTP(err) + } else { + code, err = ws.geth.CodeAt(ctx, account, blockNumber) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "CodeAt", + "code", code, + ) + + return +} + +func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return 0, err + } + defer cancel() + call := c.(ethereum.CallMsg) + lggr := r.newRqLggr().With("call", call) + + lggr.Debug("RPC call: evmclient.Client#EstimateGas") + start := time.Now() + if http != nil { + gas, err = http.geth.EstimateGas(ctx, call) + err = r.wrapHTTP(err) + } else { + gas, err = ws.geth.EstimateGas(ctx, call) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "EstimateGas", + "gas", gas, + ) + + return +} + +func (r *rpcClient) SuggestGasPrice(ctx context.Context) (price *big.Int, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr() + + lggr.Debug("RPC call: evmclient.Client#SuggestGasPrice") + start := time.Now() + if http != nil { + price, err = http.geth.SuggestGasPrice(ctx) + err = r.wrapHTTP(err) + } else { + price, err = ws.geth.SuggestGasPrice(ctx) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "SuggestGasPrice", + "price", price, + ) + + return +} + +func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) (val []byte, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) + message := msg.(ethereum.CallMsg) + + lggr.Debug("RPC call: evmclient.Client#CallContract") + start := time.Now() + if http != nil { + val, err = http.geth.CallContract(ctx, message, blockNumber) + err = r.wrapHTTP(err) + } else { + val, err = ws.geth.CallContract(ctx, message, blockNumber) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "CallContract", + "val", val, + ) + + return + +} + +func (r *rpcClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { + var height big.Int + h, err := r.BlockNumber(ctx) + return height.SetUint64(h), err +} + +func (r *rpcClient) BlockNumber(ctx context.Context) (height uint64, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return 0, err + } + defer cancel() + lggr := r.newRqLggr() + + lggr.Debug("RPC call: evmclient.Client#BlockNumber") + start := time.Now() + if http != nil { + height, err = http.geth.BlockNumber(ctx) + err = r.wrapHTTP(err) + } else { + height, err = ws.geth.BlockNumber(ctx) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "BlockNumber", + "height", height, + ) + + return +} + +func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (balance *big.Int, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) + + lggr.Debug("RPC call: evmclient.Client#BalanceAt") + start := time.Now() + if http != nil { + balance, err = http.geth.BalanceAt(ctx, account, blockNumber) + err = r.wrapHTTP(err) + } else { + balance, err = ws.geth.BalanceAt(ctx, account, blockNumber) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "BalanceAt", + "balance", balance, + ) + + return +} + +// TokenBalance returns the balance of the given address for the token contract address. +func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (*big.Int, error) { + result := "" + numLinkBigInt := new(big.Int) + functionSelector := evmtypes.HexToFunctionSelector(BALANCE_OF_ADDRESS_FUNCTION_SELECTOR) // balanceOf(address) + data := utils.ConcatBytes(functionSelector.Bytes(), common.LeftPadBytes(address.Bytes(), utils.EVMWordByteLen)) + args := CallArgs{ + To: contractAddress, + Data: data, + } + err := r.CallContext(ctx, &result, "eth_call", args, "latest") + if err != nil { + return numLinkBigInt, err + } + numLinkBigInt.SetString(result, 0) + return numLinkBigInt, nil +} + +// LINKBalance returns the balance of LINK at the given address +func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*assets.Link, error) { + balance, err := r.TokenBalance(ctx, address, linkAddress) + if err != nil { + return assets.NewLinkFromJuels(0), err + } + return (*assets.Link)(balance), nil +} + +func (r *rpcClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return r.FilterLogs(ctx, q) +} + +func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []types.Log, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("q", q) + + lggr.Debug("RPC call: evmclient.Client#FilterLogs") + start := time.Now() + if http != nil { + l, err = http.geth.FilterLogs(ctx, q) + err = r.wrapHTTP(err) + } else { + l, err = ws.geth.FilterLogs(ctx, q) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "FilterLogs", + "log", l, + ) + + return +} + +func (r *rpcClient) ClientVersion(ctx context.Context) (version string, err error) { + err = r.CallContext(ctx, &version, "web3_clientVersion") + return +} + +func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (sub ethereum.Subscription, err error) { + ctx, cancel, ws, _, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr().With("q", q) + + lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") + start := time.Now() + sub, err = ws.geth.SubscribeFilterLogs(ctx, q, ch) + if err == nil { + r.registerSub(sub) + } + err = r.wrapWS(err) + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs") + + return +} + +func (r *rpcClient) SuggestGasTipCap(ctx context.Context) (tipCap *big.Int, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + if err != nil { + return nil, err + } + defer cancel() + lggr := r.newRqLggr() + + lggr.Debug("RPC call: evmclient.Client#SuggestGasTipCap") + start := time.Now() + if http != nil { + tipCap, err = http.geth.SuggestGasTipCap(ctx) + err = r.wrapHTTP(err) + } else { + tipCap, err = ws.geth.SuggestGasTipCap(ctx) + err = r.wrapWS(err) + } + duration := time.Since(start) + + r.logResult(lggr, err, duration, r.getRPCDomain(), "SuggestGasTipCap", + "tipCap", tipCap, + ) + + return +} + +// Returns the ChainID according to the geth client. This is useful for functions like verify() +// the common node. +func (r *rpcClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { + ctx, cancel, ws, http, err := r.makeLiveQueryCtxAndSafeGetClients(ctx) + + defer cancel() + + if http != nil { + chainID, err = http.geth.ChainID(ctx) + err = r.wrapHTTP(err) + } else { + chainID, err = ws.geth.ChainID(ctx) + err = r.wrapWS(err) + } + return +} + +// newRqLggr generates a new logger with a unique request ID +func (r *rpcClient) newRqLggr() logger.Logger { + return r.rpcLog.With( + "requestID", uuid.New(), + ) +} + +func wrapCallError(err error, tp string) error { + if err == nil { + return nil + } + if errors.Cause(err).Error() == "context deadline exceeded" { + err = errors.Wrap(err, "remote node timed out") + } + return errors.Wrapf(err, "%s call failed", tp) +} + +func (r *rpcClient) wrapWS(err error) error { + err = wrapCallError(err, fmt.Sprintf("%s websocket (%s)", r.tier.String(), r.ws.uri.Redacted())) + return err +} + +func (r *rpcClient) wrapHTTP(err error) error { + err = wrapCallError(err, fmt.Sprintf("%s http (%s)", r.tier.String(), r.http.uri.Redacted())) + if err != nil { + r.rpcLog.Debugw("Call failed", "err", err) + } else { + r.rpcLog.Trace("Call succeeded") + } + return err +} + +// makeLiveQueryCtxAndSafeGetClients wraps makeQueryCtx +func (r *rpcClient) makeLiveQueryCtxAndSafeGetClients(parentCtx context.Context) (ctx context.Context, cancel context.CancelFunc, ws rawclient, http *rawclient, err error) { + // Need to wrap in mutex because state transition can cancel and replace the + // context + r.stateMu.RLock() + cancelCh := r.chStopInFlight + ws = r.ws + if r.http != nil { + cp := *r.http + http = &cp + } + r.stateMu.RUnlock() + ctx, cancel = makeQueryCtx(parentCtx, cancelCh) + return +} + +func (r *rpcClient) makeQueryCtx(ctx context.Context) (context.Context, context.CancelFunc) { + return makeQueryCtx(ctx, r.getChStopInflight()) +} + +// getChStopInflight provides a convenience helper that mutex wraps a +// read to the chStopInFlight +func (r *rpcClient) getChStopInflight() chan struct{} { + r.stateMu.RLock() + defer r.stateMu.RUnlock() + return r.chStopInFlight +} + +func (r *rpcClient) Name() string { + return r.name +} + +func Name(r *rpcClient) string { + return r.name +} diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index 150ee277577..e1b12577749 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -80,7 +80,7 @@ func (c *evmTxmClient) BatchSendTransactions( processingErr[i] = fmt.Errorf("failed to process tx (index %d): %w", i, signedErr) return } - codes[i], txErrs[i] = evmclient.NewSendErrorReturnCode(reqs[i].Error, lggr, tx, attempts[i].Tx.FromAddress, c.client.IsL2()) + codes[i], txErrs[i] = evmclient.ClassifySendError(reqs[i].Error, lggr, tx, attempts[i].Tx.FromAddress, c.client.IsL2()) }(index) } wg.Wait() diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 6210226120f..a71c9e8716c 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -76,6 +76,10 @@ func (h *Head) GetParent() commontypes.Head[common.Hash] { return h.Parent } +func (h *Head) BlockDifficulty() *utils.Big { + return h.Difficulty +} + // EarliestInChain recurses through parents until it finds the earliest one func (h *Head) EarliestInChain() *Head { for h.Parent != nil { From 1a69590dc766a1e7b405fe03cf2e2c6e1ddee64b Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Wed, 1 Nov 2023 17:12:11 -0400 Subject: [PATCH 049/271] [BCI-2313] Rename L2Suggested to SuggestedPrice (#11093) * Rename L2Suggested to SuggestedPrice * Rename L2Suggested to SuggestedPrice * update test error string * update comment * update docs * generate docs * L2Suggested backwards compatibility * L2Suggested backwards compatibility * Update CHANGELOG * Update comments * Update docs/CHANGELOG.md Co-authored-by: amit-momin <108959691+amit-momin@users.noreply.github.com> * Fix error message * Move import * deprecate L2Suggested * Update CONFIG.md --------- Co-authored-by: amit-momin <108959691+amit-momin@users.noreply.github.com> Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- core/chains/evm/config/config_test.go | 2 +- .../config/toml/defaults/Fantom_Mainnet.toml | 5 +- .../config/toml/defaults/Fantom_Testnet.toml | 2 +- .../config/toml/defaults/Klaytn_Mainnet.toml | 2 +- .../config/toml/defaults/Klaytn_Testnet.toml | 2 +- .../config/toml/defaults/Metis_Mainnet.toml | 4 +- .../config/toml/defaults/Metis_Rinkeby.toml | 2 +- .../config/toml/defaults/Scroll_Mainnet.toml | 4 +- .../config/toml/defaults/Scroll_Sepolia.toml | 4 +- core/chains/evm/gas/arbitrum_estimator.go | 8 +-- .../chains/evm/gas/arbitrum_estimator_test.go | 4 +- core/chains/evm/gas/models.go | 4 +- ...imator.go => suggested_price_estimator.go} | 58 +++++++++---------- ...t.go => suggested_price_estimator_test.go} | 18 +++--- core/config/docs/chains-evm.toml | 3 +- core/services/chainlink/config_test.go | 4 +- .../chainlink/testdata/config-full.toml | 2 +- core/web/resolver/testdata/config-full.toml | 2 +- docs/CHANGELOG.md | 8 +++ docs/CONFIG.md | 19 +++--- 20 files changed, 83 insertions(+), 74 deletions(-) rename core/chains/evm/gas/{l2_suggested_estimator.go => suggested_price_estimator.go} (60%) rename core/chains/evm/gas/{l2_suggested_estimator_test.go => suggested_price_estimator_test.go} (87%) diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index d0f9e846e37..0a3fc5f41e6 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -423,7 +423,7 @@ func Test_chainScopedConfig_Validate(t *testing.T) { t.Run("testnet", func(t *testing.T) { cfg := configWithChains(t, 421611, &toml.Chain{ GasEstimator: toml.GasEstimator{ - Mode: ptr("L2Suggested"), + Mode: ptr("SuggestedPrice"), }, }) assert.NoError(t, cfg.Validate()) diff --git a/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml b/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml index 7046642bb93..c7fb6ba4736 100644 --- a/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Fantom_Mainnet.toml @@ -9,9 +9,8 @@ RPCBlockQueryDelay = 2 Enabled = true [GasEstimator] -# Fantom network has been slow to include txs at times when using the BlockHistory estimator, and the recommendation is to use L2Suggested mode. -# There is work under way to improve L2Suggested mode's name so that its use on non-L2 chains will be less confusing in the future. -Mode = 'L2Suggested' +# Fantom network has been slow to include txs at times when using the BlockHistory estimator, and the recommendation is to use SuggestedPrice mode. +Mode = 'SuggestedPrice' [OCR2.Automation] GasLimit = 3800000 \ No newline at end of file diff --git a/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml b/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml index 0292ed5b743..1e1aab14681 100644 --- a/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml +++ b/core/chains/evm/config/toml/defaults/Fantom_Testnet.toml @@ -7,7 +7,7 @@ NoNewHeadsThreshold = '0' RPCBlockQueryDelay = 2 [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' [OCR2.Automation] GasLimit = 3800000 \ No newline at end of file diff --git a/core/chains/evm/config/toml/defaults/Klaytn_Mainnet.toml b/core/chains/evm/config/toml/defaults/Klaytn_Mainnet.toml index 36dc04ae96b..c68f03b0446 100644 --- a/core/chains/evm/config/toml/defaults/Klaytn_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Klaytn_Mainnet.toml @@ -5,6 +5,6 @@ NoNewHeadsThreshold = '30s' OCR.ContractConfirmations = 1 [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '750 gwei' # gwei = ston BumpThreshold = 0 diff --git a/core/chains/evm/config/toml/defaults/Klaytn_Testnet.toml b/core/chains/evm/config/toml/defaults/Klaytn_Testnet.toml index 34b15ca74b1..864aa0fa72a 100644 --- a/core/chains/evm/config/toml/defaults/Klaytn_Testnet.toml +++ b/core/chains/evm/config/toml/defaults/Klaytn_Testnet.toml @@ -5,6 +5,6 @@ NoNewHeadsThreshold = '30s' OCR.ContractConfirmations = 1 [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '750 gwei' # gwei = ston BumpThreshold = 0 diff --git a/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml b/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml index 855fef55a75..3e8efa531cc 100644 --- a/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Metis_Mainnet.toml @@ -8,8 +8,8 @@ NoNewHeadsThreshold = '0' OCR.ContractConfirmations = 1 [GasEstimator] -Mode = 'L2Suggested' -# Metis uses the L2Suggested estimator; we don't want to place any limits on the minimum gas price +Mode = 'SuggestedPrice' +# Metis uses the SuggestedPrice estimator; we don't want to place any limits on the minimum gas price PriceMin = '0' # Never bump gas on metis BumpThreshold = 0 diff --git a/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml b/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml index 487cc224852..7d9fec9076f 100644 --- a/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml +++ b/core/chains/evm/config/toml/defaults/Metis_Rinkeby.toml @@ -9,7 +9,7 @@ OCR.ContractConfirmations = 1 Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceMin = '0' BumpThreshold = 0 diff --git a/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml b/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml index 63c08559016..56ed84c7f38 100644 --- a/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml +++ b/core/chains/evm/config/toml/defaults/Scroll_Mainnet.toml @@ -7,8 +7,8 @@ NoNewHeadsThreshold = '0' OCR.ContractConfirmations = 1 [GasEstimator] -Mode = 'L2Suggested' -# Scroll uses the L2Suggested estimator; we don't want to place any limits on the minimum gas price +Mode = 'SuggestedPrice' +# Scroll uses the SuggestedPrice estimator; we don't want to place any limits on the minimum gas price PriceMin = '0' # Never bump gas on Scroll BumpThreshold = 0 diff --git a/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml b/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml index 5a1a0f9ba7d..af17c4d485e 100644 --- a/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml +++ b/core/chains/evm/config/toml/defaults/Scroll_Sepolia.toml @@ -7,8 +7,8 @@ NoNewHeadsThreshold = '0' OCR.ContractConfirmations = 1 [GasEstimator] -Mode = 'L2Suggested' -# Scroll uses the L2Suggested estimator; we don't want to place any limits on the minimum gas price +Mode = 'SuggestedPrice' +# Scroll uses the SuggestedPrice estimator; we don't want to place any limits on the minimum gas price PriceMin = '0' # Never bump gas on Scroll BumpThreshold = 0 diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index 17934bfa070..78d93243bbe 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -31,12 +31,12 @@ type ethClient interface { CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) } -// arbitrumEstimator is an Estimator which extends l2SuggestedPriceEstimator to use getPricesInArbGas() for gas limit estimation. +// arbitrumEstimator is an Estimator which extends SuggestedPriceEstimator to use getPricesInArbGas() for gas limit estimation. type arbitrumEstimator struct { services.StateMachine cfg ArbConfig - EvmEstimator // *l2SuggestedPriceEstimator + EvmEstimator // *SuggestedPriceEstimator client ethClient pollPeriod time.Duration @@ -56,7 +56,7 @@ func NewArbitrumEstimator(lggr logger.Logger, cfg ArbConfig, rpcClient rpcClient lggr = lggr.Named("ArbitrumEstimator") return &arbitrumEstimator{ cfg: cfg, - EvmEstimator: NewL2SuggestedPriceEstimator(lggr, rpcClient), + EvmEstimator: NewSuggestedPriceEstimator(lggr, rpcClient), client: ethClient, pollPeriod: 10 * time.Second, logger: lggr, @@ -99,7 +99,7 @@ func (a *arbitrumEstimator) HealthReport() map[string]error { } // GetLegacyGas estimates both the gas price and the gas limit. -// - Price is delegated to the embedded l2SuggestedPriceEstimator. +// - Price is delegated to the embedded SuggestedPriceEstimator. // - Limit is computed from the dynamic values perL2Tx and perL1CalldataUnit, provided by the getPricesInArbGas() method // of the precompilie contract at ArbGasInfoAddress. perL2Tx is a constant amount of gas, and perL1CalldataUnit is // multiplied by the length of the tx calldata. The sum of these two values plus the original l2GasLimit is returned. diff --git a/core/chains/evm/gas/arbitrum_estimator_test.go b/core/chains/evm/gas/arbitrum_estimator_test.go index b6e299190c5..a226368edf2 100644 --- a/core/chains/evm/gas/arbitrum_estimator_test.go +++ b/core/chains/evm/gas/arbitrum_estimator_test.go @@ -131,7 +131,7 @@ func TestArbitrumEstimator(t *testing.T) { ethClient := mocks.NewETHClient(t) o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{}, rpcClient, ethClient) _, _, err := o.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), gasLimit, assets.NewWeiI(10), nil) - assert.EqualError(t, err, "bump gas is not supported for this l2") + assert.EqualError(t, err, "bump gas is not supported for this chain") }) t.Run("calling GetLegacyGas on started estimator if initial call failed returns error", func(t *testing.T) { @@ -152,7 +152,7 @@ func TestArbitrumEstimator(t *testing.T) { t.Cleanup(func() { assert.NoError(t, o.Close()) }) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) - assert.EqualError(t, err, "failed to estimate l2 gas; gas price not set") + assert.EqualError(t, err, "failed to estimate gas; gas price not set") }) t.Run("limit computes", func(t *testing.T) { diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 7bd88d75433..299d7d54734 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -78,8 +78,8 @@ func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config, ge return NewWrappedEvmEstimator(NewBlockHistoryEstimator(lggr, ethClient, cfg, geCfg, bh, *ethClient.ConfiguredChainID()), df, l1Oracle) case "FixedPrice": return NewWrappedEvmEstimator(NewFixedPriceEstimator(geCfg, bh, lggr), df, l1Oracle) - case "Optimism2", "L2Suggested": - return NewWrappedEvmEstimator(NewL2SuggestedPriceEstimator(lggr, ethClient), df, l1Oracle) + case "L2Suggested", "SuggestedPrice": + return NewWrappedEvmEstimator(NewSuggestedPriceEstimator(lggr, ethClient), df, l1Oracle) default: lggr.Warnf("GasEstimator: unrecognised mode '%s', falling back to FixedPriceEstimator", s) return NewWrappedEvmEstimator(NewFixedPriceEstimator(geCfg, bh, lggr), df, l1Oracle) diff --git a/core/chains/evm/gas/l2_suggested_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go similarity index 60% rename from core/chains/evm/gas/l2_suggested_estimator.go rename to core/chains/evm/gas/suggested_price_estimator.go index 8e6c06a128d..a4ffb80997e 100644 --- a/core/chains/evm/gas/l2_suggested_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -19,7 +19,7 @@ import ( ) var ( - _ EvmEstimator = &l2SuggestedPriceEstimator{} + _ EvmEstimator = &SuggestedPriceEstimator{} ) //go:generate mockery --quiet --name rpcClient --output ./mocks/ --case=underscore --structname RPCClient @@ -27,8 +27,8 @@ type rpcClient interface { CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error } -// l2SuggestedPriceEstimator is an Estimator which uses the L2 suggested gas price from eth_gasPrice. -type l2SuggestedPriceEstimator struct { +// SuggestedPriceEstimator is an Estimator which uses the suggested gas price from eth_gasPrice. +type SuggestedPriceEstimator struct { services.StateMachine client rpcClient @@ -36,7 +36,7 @@ type l2SuggestedPriceEstimator struct { logger logger.Logger gasPriceMu sync.RWMutex - l2GasPrice *assets.Wei + GasPrice *assets.Wei chForceRefetch chan (chan struct{}) chInitialised chan struct{} @@ -44,12 +44,12 @@ type l2SuggestedPriceEstimator struct { chDone chan struct{} } -// NewL2SuggestedPriceEstimator returns a new Estimator which uses the L2 suggested gas price. -func NewL2SuggestedPriceEstimator(lggr logger.Logger, client rpcClient) EvmEstimator { - return &l2SuggestedPriceEstimator{ +// NewSuggestedPriceEstimator returns a new Estimator which uses the suggested gas price. +func NewSuggestedPriceEstimator(lggr logger.Logger, client rpcClient) EvmEstimator { + return &SuggestedPriceEstimator{ client: client, pollPeriod: 10 * time.Second, - logger: lggr.Named("L2SuggestedEstimator"), + logger: lggr.Named("SuggestedPriceEstimator"), chForceRefetch: make(chan (chan struct{})), chInitialised: make(chan struct{}), chStop: make(chan struct{}), @@ -57,30 +57,30 @@ func NewL2SuggestedPriceEstimator(lggr logger.Logger, client rpcClient) EvmEstim } } -func (o *l2SuggestedPriceEstimator) Name() string { +func (o *SuggestedPriceEstimator) Name() string { return o.logger.Name() } -func (o *l2SuggestedPriceEstimator) Start(context.Context) error { - return o.StartOnce("L2SuggestedEstimator", func() error { +func (o *SuggestedPriceEstimator) Start(context.Context) error { + return o.StartOnce("SuggestedPriceEstimator", func() error { go o.run() <-o.chInitialised return nil }) } -func (o *l2SuggestedPriceEstimator) Close() error { - return o.StopOnce("L2SuggestedEstimator", func() error { +func (o *SuggestedPriceEstimator) Close() error { + return o.StopOnce("SuggestedPriceEstimator", func() error { close(o.chStop) <-o.chDone return nil }) } -func (o *l2SuggestedPriceEstimator) HealthReport() map[string]error { +func (o *SuggestedPriceEstimator) HealthReport() map[string]error { return map[string]error{o.Name(): o.Healthy()} } -func (o *l2SuggestedPriceEstimator) run() { +func (o *SuggestedPriceEstimator) run() { defer close(o.chDone) t := o.refreshPrice() @@ -100,7 +100,7 @@ func (o *l2SuggestedPriceEstimator) run() { } } -func (o *l2SuggestedPriceEstimator) refreshPrice() (t *time.Timer) { +func (o *SuggestedPriceEstimator) refreshPrice() (t *time.Timer) { t = time.NewTimer(utils.WithJitter(o.pollPeriod)) var res hexutil.Big @@ -113,28 +113,28 @@ func (o *l2SuggestedPriceEstimator) refreshPrice() (t *time.Timer) { } bi := (*assets.Wei)(&res) - o.logger.Debugw("refreshPrice", "l2GasPrice", bi) + o.logger.Debugw("refreshPrice", "GasPrice", bi) o.gasPriceMu.Lock() defer o.gasPriceMu.Unlock() - o.l2GasPrice = bi + o.GasPrice = bi return } -func (o *l2SuggestedPriceEstimator) OnNewLongestChain(context.Context, *evmtypes.Head) {} +func (o *SuggestedPriceEstimator) OnNewLongestChain(context.Context, *evmtypes.Head) {} -func (*l2SuggestedPriceEstimator) GetDynamicFee(_ context.Context, _ uint32, _ *assets.Wei) (fee DynamicFee, chainSpecificGasLimit uint32, err error) { +func (*SuggestedPriceEstimator) GetDynamicFee(_ context.Context, _ uint32, _ *assets.Wei) (fee DynamicFee, chainSpecificGasLimit uint32, err error) { err = errors.New("dynamic fees are not implemented for this layer 2") return } -func (*l2SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { +func (*SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { err = errors.New("dynamic fees are not implemented for this layer 2") return } -func (o *l2SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, l2GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...feetypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { - chainSpecificGasLimit = l2GasLimit +func (o *SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...feetypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { + chainSpecificGasLimit = GasLimit ok := o.IfStarted(func() { if slices.Contains(opts, feetypes.OptForceRefetch) { @@ -159,10 +159,10 @@ func (o *l2SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, } } if gasPrice = o.getGasPrice(); gasPrice == nil { - err = errors.New("failed to estimate l2 gas; gas price not set") + err = errors.New("failed to estimate gas; gas price not set") return } - o.logger.Debugw("GetLegacyGas", "l2GasPrice", gasPrice, "l2GasLimit", l2GasLimit) + o.logger.Debugw("GetLegacyGas", "GasPrice", gasPrice, "GasLimit", GasLimit) }) if !ok { return nil, 0, errors.New("estimator is not started") @@ -176,12 +176,12 @@ func (o *l2SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, return } -func (o *l2SuggestedPriceEstimator) BumpLegacyGas(_ context.Context, _ *assets.Wei, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { - return nil, 0, errors.New("bump gas is not supported for this l2") +func (o *SuggestedPriceEstimator) BumpLegacyGas(_ context.Context, _ *assets.Wei, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { + return nil, 0, errors.New("bump gas is not supported for this chain") } -func (o *l2SuggestedPriceEstimator) getGasPrice() (l2GasPrice *assets.Wei) { +func (o *SuggestedPriceEstimator) getGasPrice() (GasPrice *assets.Wei) { o.gasPriceMu.RLock() defer o.gasPriceMu.RUnlock() - return o.l2GasPrice + return o.GasPrice } diff --git a/core/chains/evm/gas/l2_suggested_estimator_test.go b/core/chains/evm/gas/suggested_price_estimator_test.go similarity index 87% rename from core/chains/evm/gas/l2_suggested_estimator_test.go rename to core/chains/evm/gas/suggested_price_estimator_test.go index 69b36033024..808b28a3a6b 100644 --- a/core/chains/evm/gas/l2_suggested_estimator_test.go +++ b/core/chains/evm/gas/suggested_price_estimator_test.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" ) -func TestL2SuggestedEstimator(t *testing.T) { +func TestSuggestedPriceEstimator(t *testing.T) { t.Parallel() maxGasPrice := assets.NewWeiI(100) @@ -27,7 +27,7 @@ func TestL2SuggestedEstimator(t *testing.T) { t.Run("calling GetLegacyGas on unstarted estimator returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewL2SuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "estimator is not started") }) @@ -39,7 +39,7 @@ func TestL2SuggestedEstimator(t *testing.T) { (*big.Int)(res).SetInt64(42) }) - o := gas.NewL2SuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) require.NoError(t, o.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, o.Close()) }) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) @@ -50,7 +50,7 @@ func TestL2SuggestedEstimator(t *testing.T) { t.Run("gas price is lower than user specified max gas price", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewL2SuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -68,7 +68,7 @@ func TestL2SuggestedEstimator(t *testing.T) { t.Run("gas price is lower than global max gas price", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewL2SuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -85,14 +85,14 @@ func TestL2SuggestedEstimator(t *testing.T) { t.Run("calling BumpLegacyGas always returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewL2SuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) _, _, err := o.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), gasLimit, assets.NewWeiI(10), nil) - assert.EqualError(t, err, "bump gas is not supported for this l2") + assert.EqualError(t, err, "bump gas is not supported for this chain") }) t.Run("calling GetLegacyGas on started estimator if initial call failed returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewL2SuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) @@ -100,6 +100,6 @@ func TestL2SuggestedEstimator(t *testing.T) { t.Cleanup(func() { assert.NoError(t, o.Close()) }) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) - assert.EqualError(t, err, "failed to estimate l2 gas; gas price not set") + assert.EqualError(t, err, "failed to estimate gas; gas price not set") }) } diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index c8b5395d6d7..0e0d0d0bd81 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -112,7 +112,8 @@ Enabled = true # Default # # - `FixedPrice` uses static configured values for gas price (can be set via API call). # - `BlockHistory` dynamically adjusts default gas price based on heuristics from mined blocks. -# - `L2Suggested` is a special mode only for use with L2 blockchains. This mode will use the gas price suggested by the rpc endpoint via `eth_gasPrice`. +# - `L2Suggested` mode is deprecated and replaced with `SuggestedPrice`. +# - `SuggestedPrice` is a mode which uses the gas price suggested by the rpc endpoint via `eth_gasPrice`. # - `Arbitrum` is a special mode only for use with Arbitrum blockchains. It uses the suggested gas price (up to `ETH_MAX_GAS_PRICE_WEI`, with `1000 gwei` default) as well as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` default). # # Chainlink nodes decide what gas price to use using an `Estimator`. It ships with several simple and battle-hardened built-in estimators that should work well for almost all use-cases. Note that estimators will change their behaviour slightly depending on if you are in EIP-1559 mode or not. diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 48fb8272ace..986b98d9367 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -468,7 +468,7 @@ func TestConfig_Marshal(t *testing.T) { FlagsContractAddress: mustAddress("0xae4E781a6218A8031764928E88d457937A954fC3"), GasEstimator: evmcfg.GasEstimator{ - Mode: ptr("L2Suggested"), + Mode: ptr("SuggestedPrice"), EIP1559DynamicFees: ptr(true), BumpPercent: ptr[uint16](10), BumpThreshold: ptr[uint32](6), @@ -912,7 +912,7 @@ ResendAfterThreshold = '1h0m0s' Enabled = true [EVM.GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '9.223372036854775807 ether' PriceMax = '281.474976710655 micro' PriceMin = '13 wei' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 1534a411dc1..7ce0d185b1c 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -257,7 +257,7 @@ ResendAfterThreshold = '1h0m0s' Enabled = true [EVM.GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '9.223372036854775807 ether' PriceMax = '281.474976710655 micro' PriceMin = '13 wei' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 4b53396b94c..f44f119075d 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -257,7 +257,7 @@ ResendAfterThreshold = '1h0m0s' Enabled = true [EVM.GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '9.223372036854775807 ether' PriceMax = '281.474976710655 micro' PriceMin = '13 wei' diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index daeddf2ce66..8f3b16c1327 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] +### Changed + +- `L2Suggested` mode is now called `SuggestedPrice` + +### Removed + +- Removed `Optimism2` as a supported gas estimator mode + ... ## 2.7.0 - UNRELEASED diff --git a/docs/CONFIG.md b/docs/CONFIG.md index da986e0500f..313e7b46aaf 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -2726,7 +2726,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' @@ -2885,7 +2885,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '0' @@ -2963,7 +2963,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '750 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' @@ -3042,7 +3042,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '0' @@ -3199,7 +3199,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' @@ -3277,7 +3277,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '750 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '1 gwei' @@ -4383,7 +4383,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '0' @@ -4461,7 +4461,7 @@ ResendAfterThreshold = '1m0s' Enabled = true [GasEstimator] -Mode = 'L2Suggested' +Mode = 'SuggestedPrice' PriceDefault = '20 gwei' PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' PriceMin = '0' @@ -5004,7 +5004,8 @@ Mode controls what type of gas estimator is used. - `FixedPrice` uses static configured values for gas price (can be set via API call). - `BlockHistory` dynamically adjusts default gas price based on heuristics from mined blocks. -- `L2Suggested` is a special mode only for use with L2 blockchains. This mode will use the gas price suggested by the rpc endpoint via `eth_gasPrice`. +- `L2Suggested` mode is deprecated and replaced with `SuggestedPrice`. +- `SuggestedPrice` is a mode which uses the gas price suggested by the rpc endpoint via `eth_gasPrice`. - `Arbitrum` is a special mode only for use with Arbitrum blockchains. It uses the suggested gas price (up to `ETH_MAX_GAS_PRICE_WEI`, with `1000 gwei` default) as well as an estimated gas limit (up to `ETH_GAS_LIMIT_MAX`, with `1,000,000,000` default). Chainlink nodes decide what gas price to use using an `Estimator`. It ships with several simple and battle-hardened built-in estimators that should work well for almost all use-cases. Note that estimators will change their behaviour slightly depending on if you are in EIP-1559 mode or not. From 836ab9048781ef40b1098e9324dddfcd71f3ee5b Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 1 Nov 2023 17:18:06 -0400 Subject: [PATCH 050/271] Utilizes Lazier Fund Return for Tests (#11144) --- .gitignore | 1 + integration-tests/docker/test_env/cl_node_cluster.go | 3 ++- integration-tests/docker/test_env/test_env.go | 2 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- integration-tests/smoke/ocr_test.go | 2 +- integration-tests/smoke/vrfv2plus_test.go | 5 ++--- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index decea4a68a7..bfd66e2a39a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ tools/clroot/db.sqlite3-wal .idea .vscode/ *.iml +debug.env # codeship *.aes diff --git a/integration-tests/docker/test_env/cl_node_cluster.go b/integration-tests/docker/test_env/cl_node_cluster.go index a717a192649..5ae90bb982b 100644 --- a/integration-tests/docker/test_env/cl_node_cluster.go +++ b/integration-tests/docker/test_env/cl_node_cluster.go @@ -3,8 +3,9 @@ package test_env import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/integration-tests/client" "golang.org/x/sync/errgroup" + + "github.com/smartcontractkit/chainlink/integration-tests/client" ) var ( diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 07b193f102f..40ed0d4d535 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -23,11 +23,11 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logwatch" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/utils" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" ) var ( diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 3affd799194..aa670da1c96 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.30.0 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.1 + github.com/smartcontractkit/chainlink-testing-framework v1.18.2 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 98685fdf889..f7b55b259f2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2368,8 +2368,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.1 h1:YznR7isiPYbywuUma5eVSyuZYwbUHIGJ2lpcJazOZgo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.1/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.2 h1:Ac/wdRDF4L479wpFT3yqn6ujb6kFTn7aq8gj9giyFHM= +github.com/smartcontractkit/chainlink-testing-framework v1.18.2/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 8d71c5d08f8..8952f00d768 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -22,7 +22,7 @@ func TestOCRBasic(t *testing.T) { WithGeth(). WithMockAdapter(). WithCLNodes(6). - WithFunding(big.NewFloat(.1)). + WithFunding(big.NewFloat(.01)). WithStandardCleanup(). Build() require.NoError(t, err) diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 408e5a95ed3..d7381c9cd33 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -6,14 +6,13 @@ import ( "testing" "time" - "github.com/kelseyhightower/envconfig" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" - "github.com/ethereum/go-ethereum/common" + "github.com/kelseyhightower/envconfig" "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" From ac9e6185c1008a8702d525829d8e0ed3047aff86 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 2 Nov 2023 04:35:47 -0500 Subject: [PATCH 051/271] core/chains/cosmos: remove; import from chainlink-comsos (#11136) --- core/chains/cosmos/chain.go | 317 ---------- core/chains/cosmos/cosmostxm/helpers_test.go | 15 - core/chains/cosmos/cosmostxm/key_wrapper.go | 62 -- .../cosmos/cosmostxm/keystore_adapter.go | 129 ----- core/chains/cosmos/cosmostxm/main_test.go | 17 - core/chains/cosmos/cosmostxm/orm.go | 104 ---- core/chains/cosmos/cosmostxm/orm_test.go | 76 --- core/chains/cosmos/cosmostxm/txm.go | 542 ------------------ .../cosmos/cosmostxm/txm_internal_test.go | 426 -------------- core/chains/cosmos/cosmostxm/txm_test.go | 121 ---- core/chains/cosmos/relayer_adapter.go | 50 -- core/cmd/shell.go | 5 +- core/cmd/shell_local_test.go | 9 +- core/internal/cltest/cltest.go | 9 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../mocks/relayer_chain_interoperators.go | 251 ++------ .../chainlink/relayer_chain_interoperators.go | 54 +- .../relayer_chain_interoperators_test.go | 18 +- core/services/chainlink/relayer_factory.go | 22 +- core/services/pg/channels.go | 5 +- .../0207_drop_insert_on_terra_msg.sql | 20 + core/web/loader/loader_test.go | 10 +- core/web/resolver/eth_key_test.go | 10 +- core/web/resolver/node_test.go | 11 +- core/web/resolver/resolver_test.go | 4 +- go.mod | 6 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 30 files changed, 148 insertions(+), 2161 deletions(-) delete mode 100644 core/chains/cosmos/chain.go delete mode 100644 core/chains/cosmos/cosmostxm/helpers_test.go delete mode 100644 core/chains/cosmos/cosmostxm/key_wrapper.go delete mode 100644 core/chains/cosmos/cosmostxm/keystore_adapter.go delete mode 100644 core/chains/cosmos/cosmostxm/main_test.go delete mode 100644 core/chains/cosmos/cosmostxm/orm.go delete mode 100644 core/chains/cosmos/cosmostxm/orm_test.go delete mode 100644 core/chains/cosmos/cosmostxm/txm.go delete mode 100644 core/chains/cosmos/cosmostxm/txm_internal_test.go delete mode 100644 core/chains/cosmos/cosmostxm/txm_test.go delete mode 100644 core/chains/cosmos/relayer_adapter.go create mode 100644 core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql diff --git a/core/chains/cosmos/chain.go b/core/chains/cosmos/chain.go deleted file mode 100644 index e11f95d356e..00000000000 --- a/core/chains/cosmos/chain.go +++ /dev/null @@ -1,317 +0,0 @@ -package cosmos - -import ( - "context" - "crypto/rand" - "fmt" - "math/big" - "time" - - "github.com/pelletier/go-toml/v2" - "github.com/pkg/errors" - "go.uber.org/multierr" - - sdk "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" - - "github.com/smartcontractkit/sqlx" - - relaychains "github.com/smartcontractkit/chainlink-relay/pkg/chains" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" - - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos/cosmostxm" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" -) - -// defaultRequestTimeout is the default Cosmos client timeout. -// Note that while the cosmos node is processing a heavy block, -// requests can be delayed significantly (https://github.com/tendermint/tendermint/issues/6899), -// however there's nothing we can do but wait until the block is processed. -// So we set a fairly high timeout here. -// TODO(BCI-979): Remove this, or make this configurable with the updated client. -const defaultRequestTimeout = 30 * time.Second - -var ( - // ErrChainIDEmpty is returned when chain is required but was empty. - ErrChainIDEmpty = errors.New("chain id empty") - // ErrChainIDInvalid is returned when a chain id does not match any configured chains. - ErrChainIDInvalid = errors.New("chain id does not match any local chains") -) - -// Chain is a wrap for easy use in other places in the core node -type Chain = adapters.Chain - -// ChainOpts holds options for configuring a Chain. -type ChainOpts struct { - QueryConfig pg.QConfig - Logger logger.Logger - DB *sqlx.DB - KeyStore loop.Keystore - EventBroadcaster pg.EventBroadcaster -} - -func (o *ChainOpts) Validate() (err error) { - required := func(s string) error { - return fmt.Errorf("%s is required", s) - } - if o.QueryConfig == nil { - err = multierr.Append(err, required("Config")) - } - if o.Logger == nil { - err = multierr.Append(err, required("Logger'")) - } - if o.DB == nil { - err = multierr.Append(err, required("DB")) - } - if o.KeyStore == nil { - err = multierr.Append(err, required("KeyStore")) - } - if o.EventBroadcaster == nil { - err = multierr.Append(err, required("EventBroadcaster")) - } - return -} - -func NewChain(cfg *coscfg.TOMLConfig, opts ChainOpts) (adapters.Chain, error) { - if !cfg.IsEnabled() { - return nil, fmt.Errorf("cannot create new chain with ID %s, the chain is disabled", *cfg.ChainID) - } - c, err := newChain(*cfg.ChainID, cfg, opts.DB, opts.KeyStore, opts.QueryConfig, opts.EventBroadcaster, opts.Logger) - if err != nil { - return nil, err - } - return c, nil -} - -var _ adapters.Chain = (*chain)(nil) - -type chain struct { - services.StateMachine - id string - cfg *coscfg.TOMLConfig - txm *cosmostxm.Txm - lggr logger.Logger -} - -func newChain(id string, cfg *coscfg.TOMLConfig, db *sqlx.DB, ks loop.Keystore, logCfg pg.QConfig, eb pg.EventBroadcaster, lggr logger.Logger) (*chain, error) { - lggr = logger.With(lggr, "cosmosChainID", id) - var ch = chain{ - id: id, - cfg: cfg, - lggr: logger.Named(lggr, "Chain"), - } - tc := func() (client.ReaderWriter, error) { - return ch.getClient("") - } - gpe := client.NewMustGasPriceEstimator([]client.GasPricesEstimator{ - client.NewClosureGasPriceEstimator(func() (map[string]sdk.DecCoin, error) { - return map[string]sdk.DecCoin{ - cfg.GasToken(): sdk.NewDecCoinFromDec(cfg.GasToken(), cfg.FallbackGasPrice()), - }, nil - }), - }, lggr) - ch.txm = cosmostxm.NewTxm(db, tc, *gpe, ch.id, cfg, ks, lggr, logCfg, eb) - - return &ch, nil -} - -func (c *chain) Name() string { - return c.lggr.Name() -} - -func (c *chain) ID() string { - return c.id -} - -func (c *chain) ChainID() string { - return c.id -} - -func (c *chain) Config() coscfg.Config { - return c.cfg -} - -func (c *chain) TxManager() adapters.TxManager { - return c.txm -} - -func (c *chain) Reader(name string) (client.Reader, error) { - return c.getClient(name) -} - -// getClient returns a client, optionally requiring a specific node by name. -func (c *chain) getClient(name string) (client.ReaderWriter, error) { - var node db.Node - if name == "" { // Any node - nodes, err := c.cfg.ListNodes() - if err != nil { - return nil, fmt.Errorf("failed to list nodes: %w", err) - } - if len(nodes) == 0 { - return nil, errors.New("no nodes available") - } - nodeIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(nodes)))) - if err != nil { - return nil, fmt.Errorf("could not generate a random node index: %w", err) - } - node = nodes[nodeIndex.Int64()] - } else { // Named node - var err error - node, err = c.cfg.GetNode(name) - if err != nil { - return nil, fmt.Errorf("failed to get node named %s: %w", name, err) - } - if node.CosmosChainID != c.id { - return nil, fmt.Errorf("failed to create client for chain %s with node %s: wrong chain id %s", c.id, name, node.CosmosChainID) - } - } - client, err := client.NewClient(c.id, node.TendermintURL, defaultRequestTimeout, logger.Named(c.lggr, "Client."+name)) - if err != nil { - return nil, fmt.Errorf("failed to create client: %w", err) - } - c.lggr.Debugw("Created client", "name", node.Name, "tendermint-url", node.TendermintURL) - return client, nil -} - -// Start starts cosmos chain. -func (c *chain) Start(ctx context.Context) error { - return c.StartOnce("Chain", func() error { - c.lggr.Debug("Starting") - return c.txm.Start(ctx) - }) -} - -func (c *chain) Close() error { - return c.StopOnce("Chain", func() error { - c.lggr.Debug("Stopping") - return c.txm.Close() - }) -} - -func (c *chain) Ready() error { - return multierr.Combine( - c.StateMachine.Ready(), - c.txm.Ready(), - ) -} - -func (c *chain) HealthReport() map[string]error { - m := map[string]error{c.Name(): c.Healthy()} - services.CopyHealth(m, c.txm.HealthReport()) - return m -} - -// ChainService interface -func (c *chain) GetChainStatus(ctx context.Context) (relaytypes.ChainStatus, error) { - toml, err := c.cfg.TOMLString() - if err != nil { - return relaytypes.ChainStatus{}, err - } - return relaytypes.ChainStatus{ - ID: c.id, - Enabled: *c.cfg.Enabled, - Config: toml, - }, nil -} -func (c *chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) (stats []relaytypes.NodeStatus, nextPageToken string, total int, err error) { - return relaychains.ListNodeStatuses(int(pageSize), pageToken, c.listNodeStatuses) -} - -func (c *chain) Transact(ctx context.Context, from, to string, amount *big.Int, balanceCheck bool) error { - fromAcc, err := sdk.AccAddressFromBech32(from) - if err != nil { - return fmt.Errorf("failed to parse from account: %s", fromAcc) - } - toAcc, err := sdk.AccAddressFromBech32(to) - if err != nil { - return fmt.Errorf("failed to parse from account: %s", toAcc) - } - coin := sdk.Coin{Amount: sdk.NewIntFromBigInt(amount), Denom: c.Config().GasToken()} - - txm := c.TxManager() - - if balanceCheck { - var reader client.Reader - reader, err = c.Reader("") - if err != nil { - return fmt.Errorf("chain unreachable: %v", err) - } - gasPrice, err2 := txm.GasPrice() - if err2 != nil { - return fmt.Errorf("gas price unavailable: %v", err2) - } - - err = validateBalance(reader, gasPrice, fromAcc, coin) - if err != nil { - return fmt.Errorf("failed to validate balance: %v", err) - } - } - - sendMsg := bank.NewMsgSend(fromAcc, toAcc, sdk.Coins{coin}) - _, err = txm.Enqueue("", sendMsg) - if err != nil { - return fmt.Errorf("failed to enqueue tx: %w", err) - } - return nil -} - -// TODO BCF-2602 statuses are static for non-evm chain and should be dynamic -func (c *chain) listNodeStatuses(start, end int) ([]relaytypes.NodeStatus, int, error) { - stats := make([]relaytypes.NodeStatus, 0) - total := len(c.cfg.Nodes) - if start >= total { - return stats, total, relaychains.ErrOutOfRange - } - if end > total { - end = total - } - nodes := c.cfg.Nodes[start:end] - for _, node := range nodes { - stat, err := nodeStatus(node, c.ChainID()) - if err != nil { - return stats, total, err - } - stats = append(stats, stat) - } - return stats, total, nil -} - -func nodeStatus(n *coscfg.Node, id relay.ChainID) (relaytypes.NodeStatus, error) { - var s relaytypes.NodeStatus - s.ChainID = id - s.Name = *n.Name - b, err := toml.Marshal(n) - if err != nil { - return relaytypes.NodeStatus{}, err - } - s.Config = string(b) - return s, nil -} - -// maxGasUsedTransfer is an upper bound on how much gas we expect a MsgSend for a single coin to use. -const maxGasUsedTransfer = 100_000 - -// validateBalance validates that fromAddr's balance can cover coin, including fees at gasPrice. -func validateBalance(reader client.Reader, gasPrice sdk.DecCoin, fromAddr sdk.AccAddress, coin sdk.Coin) error { - balance, err := reader.Balance(fromAddr, coin.GetDenom()) - if err != nil { - return err - } - - fee := gasPrice.Amount.MulInt64(maxGasUsedTransfer).RoundInt() - need := coin.Amount.Add(fee) - - if balance.Amount.LT(need) { - return errors.Errorf("balance %q is too low for this transaction to be executed: need %s total, including %s fee", balance, need, fee) - } - return nil -} diff --git a/core/chains/cosmos/cosmostxm/helpers_test.go b/core/chains/cosmos/cosmostxm/helpers_test.go deleted file mode 100644 index a2dfbbeed84..00000000000 --- a/core/chains/cosmos/cosmostxm/helpers_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package cosmostxm - -import "golang.org/x/exp/maps" - -func (ka *keystoreAdapter) Accounts() ([]string, error) { - ka.mutex.Lock() - defer ka.mutex.Unlock() - err := ka.updateMappingLocked() - if err != nil { - return nil, err - } - addresses := maps.Keys(ka.addressToPubKey) - - return addresses, nil -} diff --git a/core/chains/cosmos/cosmostxm/key_wrapper.go b/core/chains/cosmos/cosmostxm/key_wrapper.go deleted file mode 100644 index e03dfd89b89..00000000000 --- a/core/chains/cosmos/cosmostxm/key_wrapper.go +++ /dev/null @@ -1,62 +0,0 @@ -package cosmostxm - -import ( - "bytes" - "context" - - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" -) - -// KeyWrapper uses a keystoreAdapter to implement the cosmos-sdk PrivKey interface for a specific key. -type KeyWrapper struct { - adapter *keystoreAdapter - account string -} - -var _ cryptotypes.PrivKey = &KeyWrapper{} - -func NewKeyWrapper(adapter *keystoreAdapter, account string) *KeyWrapper { - return &KeyWrapper{ - adapter: adapter, - account: account, - } -} - -func (a *KeyWrapper) Bytes() []byte { - // don't expose the private key. - return nil -} - -func (a *KeyWrapper) Sign(msg []byte) ([]byte, error) { - return a.adapter.Sign(context.Background(), a.account, msg) -} - -func (a *KeyWrapper) PubKey() cryptotypes.PubKey { - pubKey, err := a.adapter.PubKey(a.account) - if err != nil { - // return an empty pubkey if it's not found. - return &secp256k1.PubKey{Key: []byte{}} - } - return pubKey -} - -func (a *KeyWrapper) Equals(other cryptotypes.LedgerPrivKey) bool { - return bytes.Equal(a.PubKey().Bytes(), other.PubKey().Bytes()) -} - -func (a *KeyWrapper) Type() string { - return "secp256k1" -} - -func (a *KeyWrapper) Reset() { - // no-op -} - -func (a *KeyWrapper) String() string { - return "" -} - -func (a *KeyWrapper) ProtoMessage() { - // no-op -} diff --git a/core/chains/cosmos/cosmostxm/keystore_adapter.go b/core/chains/cosmos/cosmostxm/keystore_adapter.go deleted file mode 100644 index 6b360dde98c..00000000000 --- a/core/chains/cosmos/cosmostxm/keystore_adapter.go +++ /dev/null @@ -1,129 +0,0 @@ -package cosmostxm - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "sync" - - "github.com/cometbft/cometbft/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types/bech32" - "github.com/pkg/errors" - "golang.org/x/crypto/ripemd160" //nolint: staticcheck - - "github.com/smartcontractkit/chainlink-relay/pkg/loop" -) - -type accountInfo struct { - Account string - PubKey *secp256k1.PubKey -} - -// keystoreAdapter adapts a Cosmos loop.Keystore to translate public keys into bech32-prefixed account addresses. -type keystoreAdapter struct { - keystore loop.Keystore - accountPrefix string - mutex sync.RWMutex - addressToPubKey map[string]*accountInfo -} - -func newKeystoreAdapter(keystore loop.Keystore, accountPrefix string) *keystoreAdapter { - return &keystoreAdapter{ - keystore: keystore, - accountPrefix: accountPrefix, - addressToPubKey: make(map[string]*accountInfo), - } -} - -func (ka *keystoreAdapter) updateMappingLocked() error { - accounts, err := ka.keystore.Accounts(context.Background()) - if err != nil { - return err - } - - // similar to cosmos-sdk, cache and re-use calculated bech32 addresses to prevent duplicated work. - // ref: https://github.com/cosmos/cosmos-sdk/blob/3b509c187e1643757f5ef8a0b5ae3decca0c7719/types/address.go#L705 - - type cacheEntry struct { - bech32Addr string - accountInfo *accountInfo - } - accountCache := make(map[string]cacheEntry, len(ka.addressToPubKey)) - for bech32Addr, accountInfo := range ka.addressToPubKey { - accountCache[accountInfo.Account] = cacheEntry{bech32Addr: bech32Addr, accountInfo: accountInfo} - } - - addressToPubKey := make(map[string]*accountInfo, len(accounts)) - for _, account := range accounts { - if prevEntry, ok := accountCache[account]; ok { - addressToPubKey[prevEntry.bech32Addr] = prevEntry.accountInfo - continue - } - pubKeyBytes, err := hex.DecodeString(account) - if err != nil { - return err - } - - if len(pubKeyBytes) != secp256k1.PubKeySize { - return errors.New("length of pubkey is incorrect") - } - - sha := sha256.Sum256(pubKeyBytes) - hasherRIPEMD160 := ripemd160.New() - _, _ = hasherRIPEMD160.Write(sha[:]) - address := crypto.Address(hasherRIPEMD160.Sum(nil)) - - bech32Addr, err := bech32.ConvertAndEncode(ka.accountPrefix, address) - if err != nil { - return err - } - - addressToPubKey[bech32Addr] = &accountInfo{ - Account: account, - PubKey: &secp256k1.PubKey{Key: pubKeyBytes}, - } - } - - ka.addressToPubKey = addressToPubKey - return nil -} - -func (ka *keystoreAdapter) lookup(id string) (*accountInfo, error) { - ka.mutex.RLock() - ai, ok := ka.addressToPubKey[id] - ka.mutex.RUnlock() - if !ok { - // try updating the mapping once, incase there was an update on the keystore. - ka.mutex.Lock() - err := ka.updateMappingLocked() - if err != nil { - ka.mutex.Unlock() - return nil, err - } - ai, ok = ka.addressToPubKey[id] - ka.mutex.Unlock() - if !ok { - return nil, errors.New("No such id") - } - } - return ai, nil -} - -func (ka *keystoreAdapter) Sign(ctx context.Context, id string, hash []byte) ([]byte, error) { - accountInfo, err := ka.lookup(id) - if err != nil { - return nil, err - } - return ka.keystore.Sign(ctx, accountInfo.Account, hash) -} - -// Returns the cosmos PubKey associated with the prefixed address. -func (ka *keystoreAdapter) PubKey(address string) (cryptotypes.PubKey, error) { - accountInfo, err := ka.lookup(address) - if err != nil { - return nil, err - } - return accountInfo.PubKey, nil -} diff --git a/core/chains/cosmos/cosmostxm/main_test.go b/core/chains/cosmos/cosmostxm/main_test.go deleted file mode 100644 index bc340afa430..00000000000 --- a/core/chains/cosmos/cosmostxm/main_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package cosmostxm - -import ( - "os" - "testing" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/params" -) - -func TestMain(m *testing.M) { - params.InitCosmosSdk( - /* bech32Prefix= */ "wasm", - /* token= */ "cosm", - ) - code := m.Run() - os.Exit(code) -} diff --git a/core/chains/cosmos/cosmostxm/orm.go b/core/chains/cosmos/cosmostxm/orm.go deleted file mode 100644 index cc9b179cce5..00000000000 --- a/core/chains/cosmos/cosmostxm/orm.go +++ /dev/null @@ -1,104 +0,0 @@ -package cosmostxm - -import ( - "database/sql" - - "github.com/pkg/errors" - - "github.com/smartcontractkit/sqlx" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - - "github.com/smartcontractkit/chainlink/v2/core/services/pg" -) - -// ORM manages the data model for cosmos tx management. -type ORM struct { - chainID string - q pg.Q -} - -// NewORM creates an ORM scoped to chainID. -func NewORM(chainID string, db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) *ORM { - namedLogger := logger.Named(lggr, "Configs") - q := pg.NewQ(db, namedLogger, cfg) - return &ORM{ - chainID: chainID, - q: q, - } -} - -// InsertMsg inserts a cosmos msg, assumed to be a serialized cosmos ExecuteContractMsg. -func (o *ORM) InsertMsg(contractID, typeURL string, msg []byte, qopts ...pg.QOpt) (int64, error) { - var tm adapters.Msg - q := o.q.WithOpts(qopts...) - err := q.Get(&tm, `INSERT INTO cosmos_msgs (contract_id, type, raw, state, cosmos_chain_id, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) RETURNING *`, contractID, typeURL, msg, db.Unstarted, o.chainID) - if err != nil { - return 0, err - } - return tm.ID, nil -} - -// UpdateMsgsContract updates messages for the given contract. -func (o *ORM) UpdateMsgsContract(contractID string, from, to db.State, qopts ...pg.QOpt) error { - q := o.q.WithOpts(qopts...) - _, err := q.Exec(`UPDATE cosmos_msgs SET state = $1, updated_at = NOW() - WHERE cosmos_chain_id = $2 AND contract_id = $3 AND state = $4`, to, o.chainID, contractID, from) - if err != nil { - return err - } - return nil -} - -// GetMsgsState returns the oldest messages with a given state up to limit. -func (o *ORM) GetMsgsState(state db.State, limit int64, qopts ...pg.QOpt) (adapters.Msgs, error) { - if limit < 1 { - return adapters.Msgs{}, errors.New("limit must be greater than 0") - } - q := o.q.WithOpts(qopts...) - var msgs adapters.Msgs - if err := q.Select(&msgs, `SELECT * FROM cosmos_msgs WHERE state = $1 AND cosmos_chain_id = $2 ORDER BY id ASC LIMIT $3`, state, o.chainID, limit); err != nil { - return nil, err - } - return msgs, nil -} - -// GetMsgs returns any messages matching ids. -func (o *ORM) GetMsgs(ids ...int64) (adapters.Msgs, error) { - var msgs adapters.Msgs - if err := o.q.Select(&msgs, `SELECT * FROM cosmos_msgs WHERE id = ANY($1)`, ids); err != nil { - return nil, err - } - return msgs, nil -} - -// UpdateMsgs updates msgs with the given ids. -// Note state transitions are validated at the db level. -func (o *ORM) UpdateMsgs(ids []int64, state db.State, txHash *string, qopts ...pg.QOpt) error { - if state == db.Broadcasted && txHash == nil { - return errors.New("txHash is required when updating to broadcasted") - } - q := o.q.WithOpts(qopts...) - var res sql.Result - var err error - if state == db.Broadcasted { - res, err = q.Exec(`UPDATE cosmos_msgs SET state = $1, updated_at = NOW(), tx_hash = $2 WHERE id = ANY($3)`, state, *txHash, ids) - } else { - res, err = q.Exec(`UPDATE cosmos_msgs SET state = $1, updated_at = NOW() WHERE id = ANY($2)`, state, ids) - } - if err != nil { - return err - } - count, err := res.RowsAffected() - if err != nil { - return err - } - if int(count) != len(ids) { - return errors.Errorf("expected %d records updated, got %d", len(ids), count) - } - return nil -} diff --git a/core/chains/cosmos/cosmostxm/orm_test.go b/core/chains/cosmos/cosmostxm/orm_test.go deleted file mode 100644 index 3cee25bac12..00000000000 --- a/core/chains/cosmos/cosmostxm/orm_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package cosmostxm - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" -) - -func TestORM(t *testing.T) { - db := pgtest.NewSqlxDB(t) - lggr := logger.TestLogger(t) - logCfg := pgtest.NewQConfig(true) - chainID := cosmostest.RandomChainID() - o := NewORM(chainID, db, lggr, logCfg) - - // Create - mid, err := o.InsertMsg("0x123", "", []byte("hello")) - require.NoError(t, err) - assert.NotEqual(t, 0, int(mid)) - - // Read - unstarted, err := o.GetMsgsState(cosmosdb.Unstarted, 5) - require.NoError(t, err) - require.Equal(t, 1, len(unstarted)) - assert.Equal(t, "hello", string(unstarted[0].Raw)) - assert.Equal(t, chainID, unstarted[0].ChainID) - t.Log(unstarted[0].UpdatedAt, unstarted[0].CreatedAt) - - // Limit - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, 0) - assert.Error(t, err) - assert.Empty(t, unstarted) - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, -1) - assert.Error(t, err) - assert.Empty(t, unstarted) - mid2, err := o.InsertMsg("0xabc", "", []byte("test")) - require.NoError(t, err) - assert.NotEqual(t, 0, int(mid2)) - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, 1) - require.NoError(t, err) - require.Equal(t, 1, len(unstarted)) - assert.Equal(t, "hello", string(unstarted[0].Raw)) - assert.Equal(t, chainID, unstarted[0].ChainID) - unstarted, err = o.GetMsgsState(cosmosdb.Unstarted, 2) - require.NoError(t, err) - require.Equal(t, 2, len(unstarted)) - assert.Equal(t, "test", string(unstarted[1].Raw)) - assert.Equal(t, chainID, unstarted[1].ChainID) - - // Update - txHash := "123" - err = o.UpdateMsgs([]int64{mid}, cosmosdb.Started, &txHash) - require.NoError(t, err) - err = o.UpdateMsgs([]int64{mid}, cosmosdb.Broadcasted, &txHash) - require.NoError(t, err) - broadcasted, err := o.GetMsgsState(cosmosdb.Broadcasted, 5) - require.NoError(t, err) - require.Equal(t, 1, len(broadcasted)) - assert.Equal(t, broadcasted[0].Raw, unstarted[0].Raw) - require.NotNil(t, broadcasted[0].TxHash) - assert.Equal(t, *broadcasted[0].TxHash, txHash) - assert.Equal(t, chainID, broadcasted[0].ChainID) - - err = o.UpdateMsgs([]int64{mid}, cosmosdb.Confirmed, nil) - require.NoError(t, err) - confirmed, err := o.GetMsgsState(cosmosdb.Confirmed, 5) - require.NoError(t, err) - require.Equal(t, 1, len(confirmed)) -} diff --git a/core/chains/cosmos/cosmostxm/txm.go b/core/chains/cosmos/cosmostxm/txm.go deleted file mode 100644 index 712e1b8fc73..00000000000 --- a/core/chains/cosmos/cosmostxm/txm.go +++ /dev/null @@ -1,542 +0,0 @@ -package cosmostxm - -import ( - "cmp" - "context" - "encoding/hex" - "fmt" - "slices" - "strings" - "time" - - "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" - - "github.com/smartcontractkit/sqlx" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - "github.com/cometbft/cometbft/crypto/tmhash" - sdk "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/x/bank/types" - - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - - "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -var ( - _ services.Service = (*Txm)(nil) - _ adapters.TxManager = (*Txm)(nil) -) - -// Txm manages transactions for the cosmos blockchain. -type Txm struct { - services.StateMachine - eb pg.EventBroadcaster - sub pg.Subscription - orm *ORM - lggr logger.Logger - tc func() (cosmosclient.ReaderWriter, error) - keystoreAdapter *keystoreAdapter - stop, done chan struct{} - cfg coscfg.Config - gpe cosmosclient.ComposedGasPriceEstimator -} - -// NewTxm creates a txm. Uses simulation so should only be used to send txes to trusted contracts i.e. OCR. -func NewTxm(db *sqlx.DB, tc func() (cosmosclient.ReaderWriter, error), gpe cosmosclient.ComposedGasPriceEstimator, chainID string, cfg coscfg.Config, ks loop.Keystore, lggr logger.Logger, logCfg pg.QConfig, eb pg.EventBroadcaster) *Txm { - lggr = logger.Named(lggr, "Txm") - keystoreAdapter := newKeystoreAdapter(ks, cfg.Bech32Prefix()) - return &Txm{ - eb: eb, - orm: NewORM(chainID, db, lggr, logCfg), - lggr: lggr, - tc: tc, - keystoreAdapter: keystoreAdapter, - stop: make(chan struct{}), - done: make(chan struct{}), - cfg: cfg, - gpe: gpe, - } -} - -// Start subscribes to pg notifications about cosmos msg inserts and processes them. -func (txm *Txm) Start(context.Context) error { - return txm.StartOnce("Txm", func() error { - sub, err := txm.eb.Subscribe(pg.ChannelInsertOnCosmosMsg, "") - if err != nil { - return err - } - txm.sub = sub - go txm.run() - return nil - }) -} - -func (txm *Txm) confirmAnyUnconfirmed(ctx context.Context) { - // Confirm any broadcasted but not confirmed txes. - // This is an edge case if we crash after having broadcasted but before we confirm. - for { - broadcasted, err := txm.orm.GetMsgsState(db.Broadcasted, txm.cfg.MaxMsgsPerBatch()) - if err != nil { - // Should never happen but if so, theoretically can retry with a reboot - logger.Criticalw(txm.lggr, "unable to look for broadcasted but unconfirmed txes", "err", err) - return - } - if len(broadcasted) == 0 { - return - } - tc, err := txm.tc() - if err != nil { - logger.Criticalw(txm.lggr, "unable to get client for handling broadcasted but unconfirmed txes", "count", len(broadcasted), "err", err) - return - } - msgsByTxHash := make(map[string]adapters.Msgs) - for _, msg := range broadcasted { - msgsByTxHash[*msg.TxHash] = append(msgsByTxHash[*msg.TxHash], msg) - } - for txHash, msgs := range msgsByTxHash { - maxPolls, pollPeriod := txm.confirmPollConfig() - err := txm.confirmTx(ctx, tc, txHash, msgs.GetIDs(), maxPolls, pollPeriod) - if err != nil { - txm.lggr.Errorw("unable to confirm broadcasted but unconfirmed txes", "err", err, "txhash", txHash) - if ctx.Err() != nil { - return - } - } - } - } -} - -func (txm *Txm) run() { - defer close(txm.done) - ctx, cancel := utils.StopChan(txm.stop).NewCtx() - defer cancel() - txm.confirmAnyUnconfirmed(ctx) - // Jitter in case we have multiple cosmos chains each with their own client. - tick := time.After(utils.WithJitter(txm.cfg.BlockRate())) - for { - select { - case <-txm.sub.Events(): - txm.sendMsgBatch(ctx) - case <-tick: - txm.sendMsgBatch(ctx) - tick = time.After(utils.WithJitter(txm.cfg.BlockRate())) - case <-txm.stop: - return - } - } -} - -var ( - typeMsgSend = sdk.MsgTypeURL(&types.MsgSend{}) - typeMsgExecuteContract = sdk.MsgTypeURL(&wasmtypes.MsgExecuteContract{}) -) - -func unmarshalMsg(msgType string, raw []byte) (sdk.Msg, string, error) { - switch msgType { - case typeMsgSend: - var ms types.MsgSend - err := ms.Unmarshal(raw) - if err != nil { - return nil, "", err - } - return &ms, ms.FromAddress, nil - case typeMsgExecuteContract: - var ms wasmtypes.MsgExecuteContract - err := ms.Unmarshal(raw) - if err != nil { - return nil, "", err - } - return &ms, ms.Sender, nil - } - return nil, "", errors.Errorf("unrecognized message type: %s", msgType) -} - -type msgValidator struct { - cutoff time.Time - expired, valid adapters.Msgs -} - -func (e *msgValidator) add(msg adapters.Msg) { - if msg.CreatedAt.Before(e.cutoff) { - e.expired = append(e.expired, msg) - } else { - e.valid = append(e.valid, msg) - } -} - -func (e *msgValidator) sortValid() { - slices.SortFunc(e.valid, func(a, b adapters.Msg) int { - ac, bc := a.CreatedAt, b.CreatedAt - if ac.Equal(bc) { - return cmp.Compare(a.ID, b.ID) - } - if ac.After(bc) { - return 1 - } - return -1 // ac.Before(bc) - }) -} - -func (txm *Txm) sendMsgBatch(ctx context.Context) { - msgs := msgValidator{cutoff: time.Now().Add(-txm.cfg.TxMsgTimeout())} - err := txm.orm.q.Transaction(func(tx pg.Queryer) error { - // There may be leftover Started messages after a crash or failed send attempt. - started, err := txm.orm.GetMsgsState(db.Started, txm.cfg.MaxMsgsPerBatch(), pg.WithQueryer(tx)) - if err != nil { - txm.lggr.Errorw("unable to read unstarted msgs", "err", err) - return err - } - if limit := txm.cfg.MaxMsgsPerBatch() - int64(len(started)); limit > 0 { - // Use the remaining batch budget for Unstarted - unstarted, err := txm.orm.GetMsgsState(db.Unstarted, limit, pg.WithQueryer(tx)) //nolint - if err != nil { - txm.lggr.Errorw("unable to read unstarted msgs", "err", err) - return err - } - for _, msg := range unstarted { - msgs.add(msg) - } - // Update valid, Unstarted messages to Started - err = txm.orm.UpdateMsgs(msgs.valid.GetIDs(), db.Started, nil, pg.WithQueryer(tx)) - if err != nil { - // Assume transient db error retry - txm.lggr.Errorw("unable to mark unstarted txes as started", "err", err) - return err - } - } - for _, msg := range started { - msgs.add(msg) - } - // Update expired messages (Unstarted or Started) to Errored - err = txm.orm.UpdateMsgs(msgs.expired.GetIDs(), db.Errored, nil, pg.WithQueryer(tx)) - if err != nil { - // Assume transient db error retry - txm.lggr.Errorw("unable to mark expired txes as errored", "err", err) - return err - } - return nil - }) - if err != nil { - return - } - if len(msgs.valid) == 0 { - return - } - msgs.sortValid() - txm.lggr.Debugw("building a batch", "not expired", msgs.valid, "marked expired", msgs.expired) - var msgsByFrom = make(map[string]adapters.Msgs) - for _, m := range msgs.valid { - msg, sender, err2 := unmarshalMsg(m.Type, m.Raw) - if err2 != nil { - // Should be impossible given the check in Enqueue - logger.Criticalw(txm.lggr, "Failed to unmarshal msg, skipping", "err", err2, "msg", m) - continue - } - m.DecodedMsg = msg - _, err2 = sdk.AccAddressFromBech32(sender) - if err2 != nil { - // Should never happen, we parse sender on Enqueue - logger.Criticalw(txm.lggr, "Unable to parse sender", "err", err2, "sender", sender) - continue - } - msgsByFrom[sender] = append(msgsByFrom[sender], m) - } - - txm.lggr.Debugw("msgsByFrom", "msgsByFrom", msgsByFrom) - gasPrice, err := txm.GasPrice() - if err != nil { - // Should be impossible - logger.Criticalw(txm.lggr, "Failed to get gas price", "err", err) - return - } - for s, msgs := range msgsByFrom { - sender, _ := sdk.AccAddressFromBech32(s) // Already checked validity above - err := txm.sendMsgBatchFromAddress(ctx, gasPrice, sender, msgs) - if err != nil { - txm.lggr.Errorw("Could not send message batch", "err", err, "from", sender.String()) - continue - } - if ctx.Err() != nil { - return - } - } - -} - -func (txm *Txm) sendMsgBatchFromAddress(ctx context.Context, gasPrice sdk.DecCoin, sender sdk.AccAddress, msgs adapters.Msgs) error { - tc, err := txm.tc() - if err != nil { - logger.Criticalw(txm.lggr, "unable to get client", "err", err) - return err - } - an, sn, err := tc.Account(sender) - if err != nil { - txm.lggr.Warnw("unable to read account", "err", err, "from", sender.String()) - // If we can't read the account, assume transient api issues and leave msgs unstarted - // to retry on next poll. - return err - } - - txm.lggr.Debugw("simulating batch", "from", sender, "msgs", msgs, "seqnum", sn) - simResults, err := tc.BatchSimulateUnsigned(msgs.GetSimMsgs(), sn) - if err != nil { - txm.lggr.Warnw("unable to simulate", "err", err, "from", sender.String()) - // If we can't simulate assume transient api issue and retry on next poll. - // Note one rare scenario in which this can happen: the cosmos node misbehaves - // in that it confirms a txhash is present but still gives an old seq num. - // This is benign as the next retry will succeeds. - return err - } - txm.lggr.Debugw("simulation results", "from", sender, "succeeded", simResults.Succeeded, "failed", simResults.Failed) - err = txm.orm.UpdateMsgs(simResults.Failed.GetSimMsgsIDs(), db.Errored, nil) - if err != nil { - txm.lggr.Errorw("unable to mark failed sim txes as errored", "err", err, "from", sender.String()) - // If we can't mark them as failed retry on next poll. Presumably same ones will fail. - return err - } - - // Continue if there are no successful txes - if len(simResults.Succeeded) == 0 { - txm.lggr.Warnw("all sim msgs errored, not sending tx", "from", sender.String()) - return errors.New("all sim msgs errored") - } - // Get the gas limit for the successful batch - s, err := tc.SimulateUnsigned(simResults.Succeeded.GetMsgs(), sn) - if err != nil { - // In the OCR context this should only happen upon stale report - txm.lggr.Warnw("unexpected failure after successful simulation", "err", err) - return err - } - gasLimit := s.GasInfo.GasUsed - - lb, err := tc.LatestBlock() - if err != nil { - txm.lggr.Warnw("unable to get latest block", "err", err, "from", sender.String()) - // Assume transient api issue and retry. - return err - } - header, timeout := lb.SdkBlock.Header.Height, txm.cfg.BlocksUntilTxTimeout() - if header < 0 { - return fmt.Errorf("invalid negative header height: %d", header) - } else if timeout < 0 { - return fmt.Errorf("invalid negative blocks until tx timeout: %d", timeout) - } - timeoutHeight := uint64(header) + uint64(timeout) - signedTx, err := tc.CreateAndSign(simResults.Succeeded.GetMsgs(), an, sn, gasLimit, txm.cfg.GasLimitMultiplier(), - gasPrice, NewKeyWrapper(txm.keystoreAdapter, sender.String()), timeoutHeight) - if err != nil { - txm.lggr.Errorw("unable to sign tx", "err", err, "from", sender.String()) - return err - } - - // We need to ensure that we either broadcast successfully and mark the tx as - // broadcasted OR we do not broadcast successfully and we do not mark it as broadcasted. - // We do this by first marking it broadcasted then rolling back if the broadcast api call fails. - // There is still a small chance of network failure or node/db crash after broadcasting but before committing the tx, - // in which case the msgs would be picked up again and re-broadcast, ensuring at-least once delivery. - var resp *txtypes.BroadcastTxResponse - err = txm.orm.q.Transaction(func(tx pg.Queryer) error { - txHash := strings.ToUpper(hex.EncodeToString(tmhash.Sum(signedTx))) - err = txm.orm.UpdateMsgs(simResults.Succeeded.GetSimMsgsIDs(), db.Broadcasted, &txHash, pg.WithQueryer(tx)) - if err != nil { - return err - } - - txm.lggr.Infow("broadcasting tx", "from", sender, "msgs", simResults.Succeeded, "gasLimit", gasLimit, "gasPrice", gasPrice.String(), "timeoutHeight", timeoutHeight, "hash", txHash) - resp, err = tc.Broadcast(signedTx, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) - if err != nil { - // Rollback marking as broadcasted - // Note can happen if the node's mempool is full, where we expect errCode 20. - return err - } - if resp.TxResponse == nil { - // Rollback marking as broadcasted - return errors.New("unexpected nil tx response") - } - if resp.TxResponse.TxHash != txHash { - // Should never happen - logger.Criticalw(txm.lggr, "txhash mismatch", "got", resp.TxResponse.TxHash, "want", txHash) - } - return nil - }) - if err != nil { - txm.lggr.Errorw("error broadcasting tx", "err", err, "from", sender.String()) - // Was unable to broadcast, retry on next poll - return err - } - - maxPolls, pollPeriod := txm.confirmPollConfig() - if err := txm.confirmTx(ctx, tc, resp.TxResponse.TxHash, simResults.Succeeded.GetSimMsgsIDs(), maxPolls, pollPeriod); err != nil { - txm.lggr.Errorw("error confirming tx", "err", err, "hash", resp.TxResponse.TxHash) - return err - } - - return nil -} - -func (txm *Txm) confirmPollConfig() (maxPolls int, pollPeriod time.Duration) { - blocks := txm.cfg.BlocksUntilTxTimeout() - blockPeriod := txm.cfg.BlockRate() - pollPeriod = txm.cfg.ConfirmPollPeriod() - if pollPeriod == 0 { - // don't divide by zero - maxPolls = 1 - } else { - maxPolls = int((time.Duration(blocks) * blockPeriod) / pollPeriod) - } - return -} - -func (txm *Txm) confirmTx(ctx context.Context, tc cosmosclient.Reader, txHash string, broadcasted []int64, maxPolls int, pollPeriod time.Duration) error { - // We either mark these broadcasted txes as confirmed or errored. - // Confirmed: we see the txhash onchain. There are no reorgs in cosmos chains. - // Errored: we do not see the txhash onchain after waiting for N blocks worth - // of time (plus a small buffer to account for block time variance) where N - // is TimeoutHeight - HeightAtBroadcast. In other words, if we wait for that long - // and the tx is not confirmed, we know it has timed out. - for tries := 0; tries < maxPolls; tries++ { - // Jitter in-case we're confirming multiple txes in parallel for different keys - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(utils.WithJitter(pollPeriod)): - } - // Confirm that this tx is onchain, ensuring the sequence number has incremented - // so we can build a new batch - tx, err := tc.Tx(txHash) - if err != nil { - if strings.Contains(err.Error(), "not found") { - txm.lggr.Infow("txhash not found yet, still confirming", "hash", txHash) - } else { - txm.lggr.Errorw("error looking for hash of tx", "err", err, "hash", txHash) - } - continue - } - // Sanity check - if tx.TxResponse == nil || tx.TxResponse.TxHash != txHash { - txm.lggr.Errorw("error looking for hash of tx, unexpected response", "tx", tx, "hash", txHash) - continue - } - - txm.lggr.Infow("successfully sent batch", "hash", txHash, "msgs", broadcasted) - // If confirmed mark these as completed. - err = txm.orm.UpdateMsgs(broadcasted, db.Confirmed, nil) - if err != nil { - return err - } - return nil - } - txm.lggr.Errorw("unable to confirm tx after timeout period, marking errored", "hash", txHash) - // If we are unable to confirm the tx after the timeout period - // mark these msgs as errored - err := txm.orm.UpdateMsgs(broadcasted, db.Errored, nil) - if err != nil { - txm.lggr.Errorw("unable to mark timed out txes as errored", "err", err, "txes", broadcasted, "num", len(broadcasted)) - return err - } - return nil -} - -// Enqueue enqueue a msg destined for the cosmos chain. -func (txm *Txm) Enqueue(contractID string, msg sdk.Msg) (int64, error) { - typeURL, raw, err := txm.marshalMsg(msg) - if err != nil { - return 0, err - } - - // We could consider simulating here too, but that would - // introduce another network call and essentially double - // the enqueue time. Enqueue is used in the context of OCRs Transmit - // and must be fast, so we do the minimum. - - var id int64 - err = txm.orm.q.Transaction(func(tx pg.Queryer) (err error) { - // cancel any unstarted msgs (normally just one) - err = txm.orm.UpdateMsgsContract(contractID, db.Unstarted, db.Errored, pg.WithQueryer(tx)) - if err != nil { - return err - } - id, err = txm.orm.InsertMsg(contractID, typeURL, raw, pg.WithQueryer(tx)) - return err - }) - return id, err -} - -func (txm *Txm) marshalMsg(msg sdk.Msg) (string, []byte, error) { - switch ms := msg.(type) { - case *wasmtypes.MsgExecuteContract: - _, err := sdk.AccAddressFromBech32(ms.Sender) - if err != nil { - txm.lggr.Errorw("failed to parse sender, skipping", "err", err, "sender", ms.Sender) - return "", nil, err - } - - case *types.MsgSend: - _, err := sdk.AccAddressFromBech32(ms.FromAddress) - if err != nil { - txm.lggr.Errorw("failed to parse sender, skipping", "err", err, "sender", ms.FromAddress) - return "", nil, err - } - - default: - return "", nil, &cosmos.ErrMsgUnsupported{Msg: msg} - } - typeURL := sdk.MsgTypeURL(msg) - raw, err := proto.Marshal(msg) - if err != nil { - txm.lggr.Errorw("failed to marshal msg, skipping", "err", err, "msg", msg) - return "", nil, err - } - return typeURL, raw, nil -} - -// GetMsgs returns any messages matching ids. -func (txm *Txm) GetMsgs(ids ...int64) (adapters.Msgs, error) { - return txm.orm.GetMsgs(ids...) -} - -// GasPrice returns the gas price from the estimator in the configured fee token. -func (txm *Txm) GasPrice() (sdk.DecCoin, error) { - prices := txm.gpe.GasPrices() - gasPrice, ok := prices[txm.cfg.GasToken()] - if !ok { - return sdk.DecCoin{}, errors.New("unexpected empty gas price") - } - return gasPrice, nil -} - -// Close close service -func (txm *Txm) Close() error { - return txm.StopOnce("Txm", func() error { - txm.sub.Close() - close(txm.stop) - <-txm.done - return nil - }) -} - -func (txm *Txm) Name() string { return txm.lggr.Name() } - -// Healthy service is healthy -func (txm *Txm) Healthy() error { - return nil -} - -// Ready service is ready -func (txm *Txm) Ready() error { - return nil -} - -func (txm *Txm) HealthReport() map[string]error { return map[string]error{txm.Name(): txm.Healthy()} } diff --git a/core/chains/cosmos/cosmostxm/txm_internal_test.go b/core/chains/cosmos/cosmostxm/txm_internal_test.go deleted file mode 100644 index f29f130cae4..00000000000 --- a/core/chains/cosmos/cosmostxm/txm_internal_test.go +++ /dev/null @@ -1,426 +0,0 @@ -package cosmostxm - -import ( - "fmt" - "testing" - "time" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - tmservicetypes "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" - cosmostypes "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - - cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" - tcmocks "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client/mocks" - coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" - relayutils "github.com/smartcontractkit/chainlink-relay/pkg/utils" - - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -func generateExecuteMsg(msg []byte, from, to cosmostypes.AccAddress) cosmostypes.Msg { - return &wasmtypes.MsgExecuteContract{ - Sender: from.String(), - Contract: to.String(), - Msg: msg, - Funds: cosmostypes.Coins{}, - } -} - -func newReaderWriterMock(t *testing.T) *tcmocks.ReaderWriter { - tc := new(tcmocks.ReaderWriter) - tc.Test(t) - t.Cleanup(func() { tc.AssertExpectations(t) }) - return tc -} - -func TestTxm(t *testing.T) { - db := pgtest.NewSqlxDB(t) - lggr := testutils.LoggerAssertMaxLevel(t, zapcore.ErrorLevel) - ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, pgtest.NewQConfig(true)) - require.NoError(t, ks.Unlock("blah")) - - for i := 0; i < 4; i++ { - _, err := ks.Cosmos().Create() - require.NoError(t, err) - } - - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - adapter := newKeystoreAdapter(loopKs, "wasm") - accounts, err := adapter.Accounts() - require.NoError(t, err) - require.Equal(t, len(accounts), 4) - - sender1, err := cosmostypes.AccAddressFromBech32(accounts[0]) - require.NoError(t, err) - sender2, err := cosmostypes.AccAddressFromBech32(accounts[1]) - require.NoError(t, err) - contract, err := cosmostypes.AccAddressFromBech32(accounts[2]) - require.NoError(t, err) - contract2, err := cosmostypes.AccAddressFromBech32(accounts[3]) - require.NoError(t, err) - - logCfg := pgtest.NewQConfig(true) - chainID := cosmostest.RandomChainID() - two := int64(2) - gasToken := "ucosm" - cfg := &coscfg.TOMLConfig{Chain: coscfg.Chain{ - MaxMsgsPerBatch: &two, - GasToken: &gasToken, - }} - cfg.SetDefaults() - gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ - cosmosclient.NewFixedGasPriceEstimator(map[string]cosmostypes.DecCoin{ - cfg.GasToken(): cosmostypes.NewDecCoinFromDec(cfg.GasToken(), cosmostypes.MustNewDecFromStr("0.01")), - }, - lggr.(logger.SugaredLogger), - ), - }, lggr) - - t.Run("single msg", func(t *testing.T) { - tc := newReaderWriterMock(t) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, logCfg, nil) - - // Enqueue a single msg, then send it in a batch - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`1`), sender1, contract)) - require.NoError(t, err) - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil) - tc.On("BatchSimulateUnsigned", mock.Anything, mock.Anything).Return(&cosmosclient.BatchSimResults{ - Failed: nil, - Succeeded: cosmosclient.SimMsgs{{ID: id1, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte(`1`), - }}}, - }, nil) - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil) - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil) - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil) - - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil) - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil) - txm.sendMsgBatch(testutils.Context(t)) - - // Should be in completed state - completed, err := txm.orm.GetMsgs(id1) - require.NoError(t, err) - require.Equal(t, 1, len(completed)) - assert.Equal(t, completed[0].State, cosmosdb.Confirmed) - }) - - t.Run("two msgs different accounts", func(t *testing.T) { - tc := newReaderWriterMock(t) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`0`), sender1, contract)) - require.NoError(t, err) - id2, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`1`), sender2, contract)) - require.NoError(t, err) - - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil).Once() - // Note this must be arg dependent, we don't know which order - // the procesing will happen in (map iteration by from address). - tc.On("BatchSimulateUnsigned", cosmosclient.SimMsgs{ - { - ID: id2, - Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender2.String(), - Msg: []byte(`1`), - Contract: contract.String(), - }, - }, - }, mock.Anything).Return(&cosmosclient.BatchSimResults{ - Failed: nil, - Succeeded: cosmosclient.SimMsgs{ - { - ID: id2, - Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender2.String(), - Msg: []byte(`1`), - Contract: contract.String(), - }, - }, - }, - }, nil).Once() - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil).Once() - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil).Once() - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil).Once() - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil).Once() - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil).Once() - txm.sendMsgBatch(testutils.Context(t)) - - // Should be in completed state - completed, err := txm.orm.GetMsgs(id1, id2) - require.NoError(t, err) - require.Equal(t, 2, len(completed)) - assert.Equal(t, cosmosdb.Errored, completed[0].State) // cancelled - assert.Equal(t, cosmosdb.Confirmed, completed[1].State) - }) - - t.Run("two msgs different contracts", func(t *testing.T) { - tc := newReaderWriterMock(t) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - - id1, err := txm.Enqueue(contract.String(), generateExecuteMsg([]byte(`0`), sender1, contract)) - require.NoError(t, err) - id2, err := txm.Enqueue(contract2.String(), generateExecuteMsg([]byte(`1`), sender2, contract2)) - require.NoError(t, err) - ids := []int64{id1, id2} - senders := []string{sender1.String(), sender2.String()} - contracts := []string{contract.String(), contract2.String()} - for i := 0; i < 2; i++ { - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil).Once() - // Note this must be arg dependent, we don't know which order - // the procesing will happen in (map iteration by from address). - tc.On("BatchSimulateUnsigned", cosmosclient.SimMsgs{ - { - ID: ids[i], - Msg: &wasmtypes.MsgExecuteContract{ - Sender: senders[i], - Msg: []byte(fmt.Sprintf(`%d`, i)), - Contract: contracts[i], - }, - }, - }, mock.Anything).Return(&cosmosclient.BatchSimResults{ - Failed: nil, - Succeeded: cosmosclient.SimMsgs{ - { - ID: ids[i], - Msg: &wasmtypes.MsgExecuteContract{ - Sender: senders[i], - Msg: []byte(fmt.Sprintf(`%d`, i)), - Contract: contracts[i], - }, - }, - }, - }, nil).Once() - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil).Once() - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil).Once() - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil).Once() - } - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil).Twice() - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil).Twice() - txm.sendMsgBatch(testutils.Context(t)) - - // Should be in completed state - completed, err := txm.orm.GetMsgs(id1, id2) - require.NoError(t, err) - require.Equal(t, 2, len(completed)) - assert.Equal(t, cosmosdb.Confirmed, completed[0].State) - assert.Equal(t, cosmosdb.Confirmed, completed[1].State) - }) - - t.Run("failed to confirm", func(t *testing.T) { - tc := newReaderWriterMock(t) - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{ - Tx: &txtypes.Tx{}, - TxResponse: &cosmostypes.TxResponse{TxHash: "0x123"}, - }, errors.New("not found")).Twice() - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - i, err := txm.orm.InsertMsg("blah", "", []byte{0x01}) - require.NoError(t, err) - txh := "0x123" - require.NoError(t, txm.orm.UpdateMsgs([]int64{i}, cosmosdb.Started, &txh)) - require.NoError(t, txm.orm.UpdateMsgs([]int64{i}, cosmosdb.Broadcasted, &txh)) - err = txm.confirmTx(testutils.Context(t), tc, txh, []int64{i}, 2, 1*time.Millisecond) - require.NoError(t, err) - m, err := txm.orm.GetMsgs(i) - require.NoError(t, err) - require.Equal(t, 1, len(m)) - assert.Equal(t, cosmosdb.Errored, m[0].State) - }) - - t.Run("confirm any unconfirmed", func(t *testing.T) { - require.Equal(t, int64(2), cfg.MaxMsgsPerBatch()) - txHash1 := "0x1234" - txHash2 := "0x1235" - txHash3 := "0xabcd" - tc := newReaderWriterMock(t) - tc.On("Tx", txHash1).Return(&txtypes.GetTxResponse{ - TxResponse: &cosmostypes.TxResponse{TxHash: txHash1}, - }, nil).Once() - tc.On("Tx", txHash2).Return(&txtypes.GetTxResponse{ - TxResponse: &cosmostypes.TxResponse{TxHash: txHash2}, - }, nil).Once() - tc.On("Tx", txHash3).Return(&txtypes.GetTxResponse{ - TxResponse: &cosmostypes.TxResponse{TxHash: txHash3}, - }, nil).Once() - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfg, loopKs, lggr, pgtest.NewQConfig(true), nil) - - // Insert and broadcast 3 msgs with different txhashes. - id1, err := txm.orm.InsertMsg("blah", "", []byte{0x01}) - require.NoError(t, err) - id2, err := txm.orm.InsertMsg("blah", "", []byte{0x02}) - require.NoError(t, err) - id3, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Started, &txHash1) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Started, &txHash2) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id3}, cosmosdb.Started, &txHash3) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Broadcasted, &txHash1) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Broadcasted, &txHash2) - require.NoError(t, err) - err = txm.orm.UpdateMsgs([]int64{id3}, cosmosdb.Broadcasted, &txHash3) - require.NoError(t, err) - - // Confirm them as in a restart while confirming scenario - txm.confirmAnyUnconfirmed(testutils.Context(t)) - msgs, err := txm.orm.GetMsgs(id1, id2, id3) - require.NoError(t, err) - require.Equal(t, 3, len(msgs)) - assert.Equal(t, cosmosdb.Confirmed, msgs[0].State) - assert.Equal(t, cosmosdb.Confirmed, msgs[1].State) - assert.Equal(t, cosmosdb.Confirmed, msgs[2].State) - }) - - t.Run("expired msgs", func(t *testing.T) { - tc := new(tcmocks.ReaderWriter) - timeout, err := relayutils.NewDuration(1 * time.Millisecond) - require.NoError(t, err) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - two := int64(2) - cfgShortExpiry := &coscfg.TOMLConfig{Chain: coscfg.Chain{ - MaxMsgsPerBatch: &two, - TxMsgTimeout: &timeout, - }} - cfgShortExpiry.SetDefaults() - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfgShortExpiry, loopKs, lggr, pgtest.NewQConfig(true), nil) - - // Send a single one expired - id1, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - // Should be marked errored - m, err := txm.orm.GetMsgs(id1) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Errored, m[0].State) - - // Send a batch which is all expired - id2, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - id3, err := txm.orm.InsertMsg("blah", "", []byte{0x03}) - require.NoError(t, err) - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - require.NoError(t, err) - ms, err := txm.orm.GetMsgs(id2, id3) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Errored, ms[0].State) - assert.Equal(t, cosmosdb.Errored, ms[1].State) - }) - - t.Run("started msgs", func(t *testing.T) { - tc := new(tcmocks.ReaderWriter) - tc.On("Account", mock.Anything).Return(uint64(0), uint64(0), nil) - tc.On("SimulateUnsigned", mock.Anything, mock.Anything).Return(&txtypes.SimulateResponse{GasInfo: &cosmostypes.GasInfo{ - GasUsed: 1_000_000, - }}, nil) - tc.On("LatestBlock").Return(&tmservicetypes.GetLatestBlockResponse{SdkBlock: &tmservicetypes.Block{ - Header: tmservicetypes.Header{Height: 1}, - }}, nil) - tc.On("CreateAndSign", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{0x01}, nil) - txResp := &cosmostypes.TxResponse{TxHash: "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A"} - tc.On("Broadcast", mock.Anything, mock.Anything).Return(&txtypes.BroadcastTxResponse{TxResponse: txResp}, nil) - tc.On("Tx", mock.Anything).Return(&txtypes.GetTxResponse{Tx: &txtypes.Tx{}, TxResponse: txResp}, nil) - tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } - two := int64(2) - cfgMaxMsgs := &coscfg.TOMLConfig{Chain: coscfg.Chain{ - MaxMsgsPerBatch: &two, - }} - cfgMaxMsgs.SetDefaults() - loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} - txm := NewTxm(db, tcFn, *gpe, chainID, cfgMaxMsgs, loopKs, lggr, pgtest.NewQConfig(true), nil) - - // Leftover started is processed - msg1 := generateExecuteMsg([]byte{0x03}, sender1, contract) - id1 := mustInsertMsg(t, txm, contract.String(), msg1) - require.NoError(t, txm.orm.UpdateMsgs([]int64{id1}, cosmosdb.Started, nil)) - msgs := cosmosclient.SimMsgs{{ID: id1, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte{0x03}, - Contract: contract.String(), - }}} - tc.On("BatchSimulateUnsigned", msgs, mock.Anything). - Return(&cosmosclient.BatchSimResults{Failed: nil, Succeeded: msgs}, nil).Once() - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - m, err := txm.orm.GetMsgs(id1) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Confirmed, m[0].State) - - // Leftover started is not cancelled - msg2 := generateExecuteMsg([]byte{0x04}, sender1, contract) - msg3 := generateExecuteMsg([]byte{0x05}, sender1, contract) - id2 := mustInsertMsg(t, txm, contract.String(), msg2) - require.NoError(t, txm.orm.UpdateMsgs([]int64{id2}, cosmosdb.Started, nil)) - time.Sleep(time.Millisecond) // ensure != CreatedAt - id3 := mustInsertMsg(t, txm, contract.String(), msg3) - msgs = cosmosclient.SimMsgs{{ID: id2, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte{0x04}, - Contract: contract.String(), - }}, {ID: id3, Msg: &wasmtypes.MsgExecuteContract{ - Sender: sender1.String(), - Msg: []byte{0x05}, - Contract: contract.String(), - }}} - tc.On("BatchSimulateUnsigned", msgs, mock.Anything). - Return(&cosmosclient.BatchSimResults{Failed: nil, Succeeded: msgs}, nil).Once() - time.Sleep(1 * time.Millisecond) - txm.sendMsgBatch(testutils.Context(t)) - require.NoError(t, err) - ms, err := txm.orm.GetMsgs(id2, id3) - require.NoError(t, err) - assert.Equal(t, cosmosdb.Confirmed, ms[0].State) - assert.Equal(t, cosmosdb.Confirmed, ms[1].State) - }) -} - -func mustInsertMsg(t *testing.T, txm *Txm, contractID string, msg cosmostypes.Msg) int64 { - typeURL, raw, err := txm.marshalMsg(msg) - require.NoError(t, err) - id, err := txm.orm.InsertMsg(contractID, typeURL, raw) - require.NoError(t, err) - return id -} diff --git a/core/chains/cosmos/cosmostxm/txm_test.go b/core/chains/cosmos/cosmostxm/txm_test.go deleted file mode 100644 index 25ac9e8d9ec..00000000000 --- a/core/chains/cosmos/cosmostxm/txm_test.go +++ /dev/null @@ -1,121 +0,0 @@ -//go:build integration - -package cosmostxm_test - -// TestTxm_Integration is disabled in order to be moved to chainlink-cosmos before DB testing is available -//func TestTxm_Integration(t *testing.T) { -// chainID := cosmostest.RandomChainID() -// cosmosChain := coscfg.Chain{} -// cosmosChain.SetDefaults() -// fallbackGasPrice := sdk.NewDecCoinFromDec(*cosmosChain.GasToken, sdk.MustNewDecFromStr("0.01")) -// chainConfig := cosmos.CosmosConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain} -// cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "cosmos_txm", func(c *chainlink.Config, s *chainlink.Secrets) { -// c.Cosmos = cosmos.CosmosConfigs{&chainConfig} -// }) -// lggr := logger.TestLogger(t) -// logCfg := pgtest.NewQConfig(true) -// gpe := cosmosclient.NewMustGasPriceEstimator([]cosmosclient.GasPricesEstimator{ -// cosmosclient.NewFixedGasPriceEstimator(map[string]sdk.DecCoin{ -// *cosmosChain.GasToken: fallbackGasPrice, -// }, -// lggr.(logger.SugaredLogger), -// ), -// }, lggr) -// orm := cosmostxm.NewORM(chainID, db, lggr, logCfg) -// eb := pg.NewEventBroadcaster(cfg.Database().URL(), 0, 0, lggr, uuid.New()) -// require.NoError(t, eb.Start(testutils.Context(t))) -// t.Cleanup(func() { require.NoError(t, eb.Close()) }) -// ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, pgtest.NewQConfig(true)) -// zeConfig := sdk.GetConfig() -// fmt.Println(zeConfig) -// accounts, testdir, tendermintURL := cosmosclient.SetupLocalCosmosNode(t, chainID, *cosmosChain.GasToken) -// tc, err := cosmosclient.NewClient(chainID, tendermintURL, 0, lggr) -// require.NoError(t, err) -// -// loopKs := &keystore.CosmosLoopKeystore{Cosmos: ks.Cosmos()} -// keystoreAdapter := cosmostxm.NewKeystoreAdapter(loopKs, *cosmosChain.Bech32Prefix) -// -// // First create a transmitter key and fund it with 1k native tokens -// require.NoError(t, ks.Unlock("blah")) -// err = ks.Cosmos().EnsureKey() -// require.NoError(t, err) -// ksAccounts, err := keystoreAdapter.Accounts() -// require.NoError(t, err) -// transmitterAddress := ksAccounts[0] -// transmitterID, err := sdk.AccAddressFromBech32(transmitterAddress) -// require.NoError(t, err) -// an, sn, err := tc.Account(accounts[0].Address) -// require.NoError(t, err) -// resp, err := tc.SignAndBroadcast([]sdk.Msg{banktypes.NewMsgSend(accounts[0].Address, transmitterID, sdk.NewCoins(sdk.NewInt64Coin(*cosmosChain.GasToken, 100000)))}, -// an, sn, gpe.GasPrices()[*cosmosChain.GasToken], accounts[0].PrivateKey, txtypes.BroadcastMode_BROADCAST_MODE_SYNC) -// tx, success := cosmosclient.AwaitTxCommitted(t, tc, resp.TxResponse.TxHash) -// require.True(t, success) -// require.Equal(t, types.CodeTypeOK, tx.TxResponse.Code) -// require.NoError(t, err) -// -// // TODO: find a way to pull this test artifact from -// // the chainlink-cosmos repo instead of copying it to cores testdata -// contractID := cosmosclient.DeployTestContract(t, tendermintURL, chainID, *cosmosChain.GasToken, accounts[0], cosmosclient.Account{ -// Name: "transmitter", -// PrivateKey: cosmostxm.NewKeyWrapper(keystoreAdapter, transmitterAddress), -// Address: transmitterID, -// }, tc, testdir, "../../../testdata/cosmos/my_first_contract.wasm") -// -// tcFn := func() (cosmosclient.ReaderWriter, error) { return tc, nil } -// // Start txm -// txm := cosmostxm.NewTxm(db, tcFn, *gpe, chainID, &chainConfig, loopKs, lggr, pgtest.NewQConfig(true), eb) -// require.NoError(t, txm.Start(testutils.Context(t))) -// -// // Change the contract state -// setMsg := &wasmtypes.MsgExecuteContract{ -// Sender: transmitterID.String(), -// Contract: contractID.String(), -// Msg: []byte(`{"reset":{"count":5}}`), -// Funds: sdk.Coins{}, -// } -// _, err = txm.Enqueue(contractID.String(), setMsg) -// require.NoError(t, err) -// -// // Observe the counter gets set eventually -// gomega.NewWithT(t).Eventually(func() bool { -// d, err := tc.ContractState(contractID, []byte(`{"get_count":{}}`)) -// require.NoError(t, err) -// t.Log("contract value", string(d)) -// return string(d) == `{"count":5}` -// }, 20*time.Second, time.Second).Should(gomega.BeTrue()) -// // Ensure messages are completed -// gomega.NewWithT(t).Eventually(func() bool { -// msgs, err := orm.GetMsgsState(Confirmed, 5) -// require.NoError(t, err) -// return 1 == len(msgs) -// }, 5*time.Second, time.Second).Should(gomega.BeTrue()) -// -// // Ensure invalid msgs are marked as errored -// invalidMsg := &wasmtypes.MsgExecuteContract{ -// Sender: transmitterID.String(), -// Contract: contractID.String(), -// Msg: []byte(`{"blah":{"blah":5}}`), -// Funds: sdk.Coins{}, -// } -// _, err = txm.Enqueue(contractID.String(), invalidMsg) -// require.NoError(t, err) -// _, err = txm.Enqueue(contractID.String(), invalidMsg) -// require.NoError(t, err) -// _, err = txm.Enqueue(contractID.String(), setMsg) -// require.NoError(t, err) -// -// // Ensure messages are completed -// gomega.NewWithT(t).Eventually(func() bool { -// succeeded, err := orm.GetMsgsState(Confirmed, 5) -// require.NoError(t, err) -// errored, err := orm.GetMsgsState(Errored, 5) -// require.NoError(t, err) -// t.Log("errored", len(errored), "succeeded", len(succeeded)) -// return 2 == len(succeeded) && 2 == len(errored) -// }, 20*time.Second, time.Second).Should(gomega.BeTrue()) -// -// // Observe the messages have been marked as completed -// require.NoError(t, txm.Close()) -//} -// -//func ptr[T any](t T) *T { return &t } diff --git a/core/chains/cosmos/relayer_adapter.go b/core/chains/cosmos/relayer_adapter.go deleted file mode 100644 index ace441c2bb5..00000000000 --- a/core/chains/cosmos/relayer_adapter.go +++ /dev/null @@ -1,50 +0,0 @@ -package cosmos - -import ( - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - - pkgcosmos "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" - "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" -) - -// LegacyChainContainer is container interface for Cosmos chains -type LegacyChainContainer interface { - Get(id string) (adapters.Chain, error) - Len() int - List(ids ...string) ([]adapters.Chain, error) - Slice() []adapters.Chain -} - -type LegacyChains = chains.ChainsKV[adapters.Chain] - -var _ LegacyChainContainer = &LegacyChains{} - -func NewLegacyChains(m map[string]adapters.Chain) *LegacyChains { - return chains.NewChainsKV[adapters.Chain](m) -} - -type LoopRelayerChainer interface { - loop.Relayer - Chain() adapters.Chain -} - -type LoopRelayerChain struct { - loop.Relayer - chain adapters.Chain -} - -func NewLoopRelayerChain(r *pkgcosmos.Relayer, s adapters.Chain) *LoopRelayerChain { - ra := relay.NewServerAdapter(r, s) - return &LoopRelayerChain{ - Relayer: ra, - chain: s, - } -} -func (r *LoopRelayerChain) Chain() adapters.Chain { - return r.chain -} - -var _ LoopRelayerChainer = &LoopRelayerChain{} diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 1ef99992a66..308ebf8da8c 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -173,9 +173,8 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: eventBroadcaster, + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), } initOps = append(initOps, chainlink.InitCosmos(ctx, relayerFactory, cosmosCfg)) } diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index d70b06f5a98..89b8704f87b 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -302,8 +302,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) legacy := cltest.NewLegacyChainsWithMockChain(t, ethClient, config) - mockRelayerChainInteroperators := chainlinkmocks.NewRelayerChainInteroperators(t) - mockRelayerChainInteroperators.On("LegacyEVMChains").Return(legacy, nil) + mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() ethClient.On("Dial", mock.Anything).Return(nil) @@ -385,8 +384,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { ethClient.On("Dial", mock.Anything).Return(nil) legacy := cltest.NewLegacyChainsWithMockChain(t, ethClient, config) - mockRelayerChainInteroperators := chainlinkmocks.NewRelayerChainInteroperators(t) - mockRelayerChainInteroperators.On("LegacyEVMChains").Return(legacy, nil) + mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() client := cmd.Shell{ @@ -465,8 +463,7 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) { ethClient.On("Dial", mock.Anything).Return(nil) legacy := cltest.NewLegacyChainsWithMockChain(t, ethClient, config) - mockRelayerChainInteroperators := chainlinkmocks.NewRelayerChainInteroperators(t) - mockRelayerChainInteroperators.On("LegacyEVMChains").Return(legacy, nil) + mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(clienttypes.Successful, nil) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index d47e6243b82..4cb9808fe24 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -414,11 +414,10 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn if cfg.CosmosEnabled() { cosmosCfg := chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: eventBroadcaster, - DB: db, - QConfig: cfg.Database(), + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database(), } initOps = append(initOps, chainlink.InitCosmos(testCtx, relayerFactory, cosmosCfg)) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 690a8d189cc..f2b1f9a4c94 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -301,7 +301,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5cbdb37427d..683cc1ea06c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1456,8 +1456,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= diff --git a/core/services/chainlink/mocks/relayer_chain_interoperators.go b/core/services/chainlink/mocks/relayer_chain_interoperators.go index 0a8758f6d4b..81f112f7663 100644 --- a/core/services/chainlink/mocks/relayer_chain_interoperators.go +++ b/core/services/chainlink/mocks/relayer_chain_interoperators.go @@ -1,248 +1,61 @@ -// Code generated by mockery v2.28.1. DO NOT EDIT. - package mocks import ( - context "context" - - chainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - - cosmos "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" + "context" + "slices" - evm "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + services2 "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - // Manually edited. mockery generates the wrong dependency. edited to use `loop` rather than `loop/internal` - // seems to caused by incorrect alias resolution of the relayer dep - internal "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" - mock "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" - relay "github.com/smartcontractkit/chainlink/v2/core/services/relay" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" - services "github.com/smartcontractkit/chainlink/v2/core/services" - - types "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-relay/pkg/types" ) -// RelayerChainInteroperators is an autogenerated mock type for the RelayerChainInteroperators type -type RelayerChainInteroperators struct { - mock.Mock -} - -// ChainStatus provides a mock function with given fields: ctx, id -func (_m *RelayerChainInteroperators) ChainStatus(ctx context.Context, id relay.ID) (types.ChainStatus, error) { - ret := _m.Called(ctx, id) - - var r0 types.ChainStatus - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, relay.ID) (types.ChainStatus, error)); ok { - return rf(ctx, id) - } - if rf, ok := ret.Get(0).(func(context.Context, relay.ID) types.ChainStatus); ok { - r0 = rf(ctx, id) - } else { - r0 = ret.Get(0).(types.ChainStatus) - } - - if rf, ok := ret.Get(1).(func(context.Context, relay.ID) error); ok { - r1 = rf(ctx, id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ChainStatuses provides a mock function with given fields: ctx, offset, limit -func (_m *RelayerChainInteroperators) ChainStatuses(ctx context.Context, offset int, limit int) ([]types.ChainStatus, int, error) { - ret := _m.Called(ctx, offset, limit) - - var r0 []types.ChainStatus - var r1 int - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, int, int) ([]types.ChainStatus, int, error)); ok { - return rf(ctx, offset, limit) - } - if rf, ok := ret.Get(0).(func(context.Context, int, int) []types.ChainStatus); ok { - r0 = rf(ctx, offset, limit) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.ChainStatus) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int, int) int); ok { - r1 = rf(ctx, offset, limit) - } else { - r1 = ret.Get(1).(int) - } - - if rf, ok := ret.Get(2).(func(context.Context, int, int) error); ok { - r2 = rf(ctx, offset, limit) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 +// FakeRelayerChainInteroperators is a fake chainlink.RelayerChainInteroperators. +// This exists because mockery generation doesn't understand how to produce an alias instead of the underlying type (which is not exported in this case). +type FakeRelayerChainInteroperators struct { + EVMChains evm.LegacyChainContainer + Nodes []types.NodeStatus + NodesErr error } -// Get provides a mock function with given fields: id -func (_m *RelayerChainInteroperators) Get(id relay.ID) (internal.Relayer, error) { - ret := _m.Called(id) - - var r0 internal.Relayer - var r1 error - if rf, ok := ret.Get(0).(func(relay.ID) (internal.Relayer, error)); ok { - return rf(id) - } - if rf, ok := ret.Get(0).(func(relay.ID) internal.Relayer); ok { - r0 = rf(id) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(internal.Relayer) - } - } - - if rf, ok := ret.Get(1).(func(relay.ID) error); ok { - r1 = rf(id) - } else { - r1 = ret.Error(1) - } - - return r0, r1 +func (f *FakeRelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainContainer { + return f.EVMChains } -// LegacyCosmosChains provides a mock function with given fields: -func (_m *RelayerChainInteroperators) LegacyCosmosChains() cosmos.LegacyChainContainer { - ret := _m.Called() - - var r0 cosmos.LegacyChainContainer - if rf, ok := ret.Get(0).(func() cosmos.LegacyChainContainer); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(cosmos.LegacyChainContainer) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) NodeStatuses(ctx context.Context, offset, limit int, relayIDs ...relay.ID) (nodes []types.NodeStatus, count int, err error) { + return slices.Clone(f.Nodes), len(f.Nodes), f.NodesErr } -// LegacyEVMChains provides a mock function with given fields: -func (_m *RelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainContainer { - ret := _m.Called() - - var r0 evm.LegacyChainContainer - if rf, ok := ret.Get(0).(func() evm.LegacyChainContainer); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(evm.LegacyChainContainer) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) Services() []services2.ServiceCtx { + panic("unimplemented") } -// List provides a mock function with given fields: filter -func (_m *RelayerChainInteroperators) List(filter chainlink.FilterFn) chainlink.RelayerChainInteroperators { - ret := _m.Called(filter) - - var r0 chainlink.RelayerChainInteroperators - if rf, ok := ret.Get(0).(func(chainlink.FilterFn) chainlink.RelayerChainInteroperators); ok { - r0 = rf(filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(chainlink.RelayerChainInteroperators) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) List(filter chainlink.FilterFn) chainlink.RelayerChainInteroperators { + panic("unimplemented") } -// NodeStatuses provides a mock function with given fields: ctx, offset, limit, relayIDs -func (_m *RelayerChainInteroperators) NodeStatuses(ctx context.Context, offset int, limit int, relayIDs ...relay.ID) ([]types.NodeStatus, int, error) { - _va := make([]interface{}, len(relayIDs)) - for _i := range relayIDs { - _va[_i] = relayIDs[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, offset, limit) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 []types.NodeStatus - var r1 int - var r2 error - if rf, ok := ret.Get(0).(func(context.Context, int, int, ...relay.ID) ([]types.NodeStatus, int, error)); ok { - return rf(ctx, offset, limit, relayIDs...) - } - if rf, ok := ret.Get(0).(func(context.Context, int, int, ...relay.ID) []types.NodeStatus); ok { - r0 = rf(ctx, offset, limit, relayIDs...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.NodeStatus) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int, int, ...relay.ID) int); ok { - r1 = rf(ctx, offset, limit, relayIDs...) - } else { - r1 = ret.Get(1).(int) - } - - if rf, ok := ret.Get(2).(func(context.Context, int, int, ...relay.ID) error); ok { - r2 = rf(ctx, offset, limit, relayIDs...) - } else { - r2 = ret.Error(2) - } - - return r0, r1, r2 +func (f *FakeRelayerChainInteroperators) Get(id relay.ID) (loop.Relayer, error) { + panic("unimplemented") } -// Services provides a mock function with given fields: -func (_m *RelayerChainInteroperators) Services() []services.ServiceCtx { - ret := _m.Called() - - var r0 []services.ServiceCtx - if rf, ok := ret.Get(0).(func() []services.ServiceCtx); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]services.ServiceCtx) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) Slice() []loop.Relayer { + panic("unimplemented") } -// Slice provides a mock function with given fields: -func (_m *RelayerChainInteroperators) Slice() []internal.Relayer { - ret := _m.Called() - - var r0 []internal.Relayer - if rf, ok := ret.Get(0).(func() []internal.Relayer); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]internal.Relayer) - } - } - - return r0 +func (f *FakeRelayerChainInteroperators) LegacyCosmosChains() chainlink.LegacyCosmosContainer { + panic("unimplemented") } -type mockConstructorTestingTNewRelayerChainInteroperators interface { - mock.TestingT - Cleanup(func()) +func (f *FakeRelayerChainInteroperators) ChainStatus(ctx context.Context, id relay.ID) (types.ChainStatus, error) { + panic("unimplemented") } -// NewRelayerChainInteroperators creates a new instance of RelayerChainInteroperators. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewRelayerChainInteroperators(t mockConstructorTestingTNewRelayerChainInteroperators) *RelayerChainInteroperators { - mock := &RelayerChainInteroperators{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock +func (f *FakeRelayerChainInteroperators) ChainStatuses(ctx context.Context, offset, limit int) ([]types.ChainStatus, int, error) { + panic("unimplemented") } diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index e039afbfc91..b2ec0822d44 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -7,11 +7,12 @@ import ( "sort" "sync" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" @@ -24,10 +25,6 @@ var ErrNoSuchRelayer = errors.New("relayer does not exist") // encapsulates relayers and chains and is the primary entry point for // the node to access relayers, get legacy chains associated to a relayer // and get status about the chains and nodes -// -// note the generated mockery code incorrectly resolves dependencies and needs to be manually edited -// therefore this interface is not auto-generated. for reference use and edit the result: -// `go:generate mockery --quiet --name RelayerChainInteroperators --output ./mocks/ --case=underscore“` type RelayerChainInteroperators interface { Services() []services.ServiceCtx @@ -50,7 +47,7 @@ type LoopRelayerStorer interface { // on the relayer interface. type LegacyChainer interface { LegacyEVMChains() evm.LegacyChainContainer - LegacyCosmosChains() cosmos.LegacyChainContainer + LegacyCosmosChains() LegacyCosmosContainer } type ChainStatuser interface { @@ -135,7 +132,7 @@ func InitCosmos(ctx context.Context, factory RelayerFactory, config CosmosFactor op.loopRelayers[id] = a legacyMap[id.ChainID] = a.Chain() } - op.legacyChains.CosmosChains = cosmos.NewLegacyChains(legacyMap) + op.legacyChains.CosmosChains = NewLegacyCosmos(legacyMap) return nil } @@ -196,7 +193,7 @@ func (rs *CoreRelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainConta // LegacyCosmosChains returns a container with all the cosmos chains // TODO BCF-2511 -func (rs *CoreRelayerChainInteroperators) LegacyCosmosChains() cosmos.LegacyChainContainer { +func (rs *CoreRelayerChainInteroperators) LegacyCosmosChains() LegacyCosmosContainer { rs.mu.Lock() defer rs.mu.Unlock() return rs.legacyChains.CosmosChains @@ -355,5 +352,44 @@ func (rs *CoreRelayerChainInteroperators) Services() (s []services.ServiceCtx) { // deprecated when chain-specific logic is removed from products. type legacyChains struct { EVMChains evm.LegacyChainContainer - CosmosChains cosmos.LegacyChainContainer + CosmosChains LegacyCosmosContainer } + +// LegacyCosmosContainer is container interface for Cosmos chains +type LegacyCosmosContainer interface { + Get(id string) (adapters.Chain, error) + Len() int + List(ids ...string) ([]adapters.Chain, error) + Slice() []adapters.Chain +} + +type LegacyCosmos = chains.ChainsKV[adapters.Chain] + +var _ LegacyCosmosContainer = &LegacyCosmos{} + +func NewLegacyCosmos(m map[string]adapters.Chain) *LegacyCosmos { + return chains.NewChainsKV[adapters.Chain](m) +} + +type CosmosLoopRelayerChainer interface { + loop.Relayer + Chain() adapters.Chain +} + +type CosmosLoopRelayerChain struct { + loop.Relayer + chain adapters.Chain +} + +func NewCosmosLoopRelayerChain(r *cosmos.Relayer, s adapters.Chain) *CosmosLoopRelayerChain { + ra := relay.NewServerAdapter(r, s) + return &CosmosLoopRelayerChain{ + Relayer: ra, + chain: s, + } +} +func (r *CosmosLoopRelayerChain) Chain() adapters.Chain { + return r.chain +} + +var _ CosmosLoopRelayerChainer = &CosmosLoopRelayerChain{} diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index cfc7dbadc18..87293069646 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -257,11 +257,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { name: "2 cosmos chains with 2 nodes", initFuncs: []chainlink.CoreRelayerChainInitFunc{ chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: pg.NewNullEventBroadcaster(), - DB: db, - QConfig: cfg.Database()}), + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database()}), }, expectedCosmosChainCnt: 2, expectedCosmosNodeCnt: 2, @@ -290,11 +289,10 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Keystore: keyStore.StarkNet(), TOMLConfigs: cfg.StarknetConfigs()}), chainlink.InitCosmos(testctx, factory, chainlink.CosmosFactoryConfig{ - Keystore: keyStore.Cosmos(), - TOMLConfigs: cfg.CosmosConfigs(), - EventBroadcaster: pg.NewNullEventBroadcaster(), - DB: db, - QConfig: cfg.Database(), + Keystore: keyStore.Cosmos(), + TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database(), }), }, expectedEVMChainCnt: 2, diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index a159ee7cd06..76bfcd16412 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/sqlx" - pkgcosmos "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -17,7 +17,7 @@ import ( pkgstarknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink" starkchain "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/chain" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/cosmos" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -225,7 +225,6 @@ func (r *RelayerFactory) NewStarkNet(ks keystore.StarkNet, chainCfgs config.TOML type CosmosFactoryConfig struct { Keystore keystore.Cosmos coscfg.TOMLConfigs - EventBroadcaster pg.EventBroadcaster *sqlx.DB pg.QConfig } @@ -238,9 +237,6 @@ func (c CosmosFactoryConfig) Validate() error { if len(c.TOMLConfigs) == 0 { err = errors.Join(err, fmt.Errorf("no CosmosConfigs provided")) } - if c.EventBroadcaster == nil { - err = errors.Join(err, fmt.Errorf("nil EventBroadcaster")) - } if c.DB == nil { err = errors.Join(err, fmt.Errorf("nil DB")) } @@ -254,12 +250,12 @@ func (c CosmosFactoryConfig) Validate() error { return err } -func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConfig) (map[relay.ID]cosmos.LoopRelayerChainer, error) { +func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConfig) (map[relay.ID]CosmosLoopRelayerChainer, error) { err := config.Validate() if err != nil { return nil, fmt.Errorf("cannot create Cosmos relayer: %w", err) } - relayers := make(map[relay.ID]cosmos.LoopRelayerChainer) + relayers := make(map[relay.ID]CosmosLoopRelayerChainer) var ( cosmosLggr = r.Logger.Named("Cosmos") @@ -273,11 +269,9 @@ func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConf lggr := cosmosLggr.Named(relayID.ChainID) opts := cosmos.ChainOpts{ - QueryConfig: config.QConfig, - Logger: lggr, - DB: config.DB, - KeyStore: loopKs, - EventBroadcaster: config.EventBroadcaster, + Logger: lggr, + DB: config.DB, + KeyStore: loopKs, } chain, err := cosmos.NewChain(chainCfg, opts) @@ -285,7 +279,7 @@ func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConf return nil, fmt.Errorf("failed to load Cosmos chain %q: %w", relayID, err) } - relayers[relayID] = cosmos.NewLoopRelayerChain(pkgcosmos.NewRelayer(lggr, chain), chain) + relayers[relayID] = NewCosmosLoopRelayerChain(cosmos.NewRelayer(lggr, chain), chain) } return relayers, nil diff --git a/core/services/pg/channels.go b/core/services/pg/channels.go index 1d67dabe523..aed132a7f2c 100644 --- a/core/services/pg/channels.go +++ b/core/services/pg/channels.go @@ -1,7 +1,4 @@ package pg // Postgres channel to listen for new evm.txes -const ( - ChannelInsertOnCosmosMsg = "insert_on_cosmos_msg" - ChannelInsertOnEVMLogs = "evm.insert_on_logs" -) +const ChannelInsertOnEVMLogs = "evm.insert_on_logs" diff --git a/core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql b/core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql new file mode 100644 index 00000000000..f4ae4b98e2c --- /dev/null +++ b/core/store/migrate/migrations/0207_drop_insert_on_terra_msg.sql @@ -0,0 +1,20 @@ +-- +goose Up + +-- +goose StatementBegin +DROP TRIGGER IF EXISTS insert_on_terra_msg ON PUBLIC.cosmos_msgs; +DROP FUNCTION IF EXISTS PUBLIC.notify_terra_msg_insert; +-- +goose StatementEnd + +-- +goose Down + +-- +goose StatementBegin +CREATE FUNCTION notify_terra_msg_insert() RETURNS trigger + LANGUAGE plpgsql +AS $$ +BEGIN + PERFORM pg_notify('insert_on_terra_msg'::text, NOW()::text); + RETURN NULL; +END +$$; +CREATE TRIGGER notify_terra_msg_insertion AFTER INSERT ON cosmos_msgs FOR EACH STATEMENT EXECUTE PROCEDURE notify_terra_msg_insert(); +-- +goose StatementEnd diff --git a/core/web/loader/loader_test.go b/core/web/loader/loader_test.go index 0dd45a1735d..984aa9f6189 100644 --- a/core/web/loader/loader_test.go +++ b/core/web/loader/loader_test.go @@ -26,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/job" jobORMMocks "github.com/smartcontractkit/chainlink/v2/core/services/job/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -69,9 +68,6 @@ func TestLoader_Nodes(t *testing.T) { ctx := InjectDataloader(testutils.Context(t), app) chainID1, chainID2, notAnID := big.NewInt(1), big.NewInt(2), big.NewInt(3) - relayID1 := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(chainID1.String())} - relayID2 := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(chainID2.String())} - notARelayID := relay.ID{Network: relay.EVM, ChainID: relay.ChainID(notAnID.String())} genNodeStat := func(id string) relaytypes.NodeStatus { return relaytypes.NodeStatus{ @@ -79,11 +75,9 @@ func TestLoader_Nodes(t *testing.T) { ChainID: id, } } - rcInterops := chainlinkmocks.NewRelayerChainInteroperators(t) - rcInterops.On("NodeStatuses", mock.Anything, 0, -1, - relayID2, relayID1, notARelayID).Return([]relaytypes.NodeStatus{ + rcInterops := &chainlinkmocks.FakeRelayerChainInteroperators{Nodes: []relaytypes.NodeStatus{ genNodeStat(chainID2.String()), genNodeStat(chainID1.String()), - }, 2, nil) + }} app.On("GetRelayers").Return(rcInterops) batcher := nodeBatcher{app} diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index 6cac2f4ac4f..a7f8ce56d9f 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -100,7 +100,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.balM.On("GetEthBalance", address).Return(assets.NewEth(1)) f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.App.On("GetKeyStore").Return(f.Mocks.keystore) @@ -149,7 +149,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.App.On("GetKeyStore").Return(f.Mocks.keystore) @@ -268,7 +268,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetAll").Return(keys, nil) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, gError) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.App.On("GetRelayers").Return(f.Mocks.relayerChainInterops) f.App.On("GetKeyStore").Return(f.Mocks.keystore) }, @@ -302,7 +302,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(assets.NewLinkFromJuels(12), gError) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) f.Mocks.balM.On("GetEthBalance", address).Return(assets.NewEth(1)) f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) @@ -358,7 +358,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.chain.On("BalanceMonitor").Return(nil) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) - f.Mocks.relayerChainInterops.On("LegacyEVMChains").Return(f.Mocks.legacyEVMChains) + f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) f.App.On("GetKeyStore").Return(f.Mocks.keystore) diff --git a/core/web/resolver/node_test.go b/core/web/resolver/node_test.go index e949a67a85b..9f34b274201 100644 --- a/core/web/resolver/node_test.go +++ b/core/web/resolver/node_test.go @@ -5,10 +5,10 @@ import ( gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/pkg/errors" - "github.com/stretchr/testify/mock" "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -43,17 +43,16 @@ func TestResolver_Nodes(t *testing.T) { name: "success", authenticated: true, before: func(f *gqlTestFramework) { - f.App.On("GetRelayers").Return(f.Mocks.relayerChainInterops) - f.Mocks.relayerChainInterops.On("NodeStatuses", mock.Anything, PageDefaultOffset, PageDefaultLimit).Return([]types.NodeStatus{ + f.App.On("GetRelayers").Return(chainlink.RelayerChainInteroperators(f.Mocks.relayerChainInterops)) + f.Mocks.relayerChainInterops.Nodes = []types.NodeStatus{ { Name: "node-name", ChainID: chainID.String(), Config: `Name = 'node-name'`, }, - }, 1, nil) + } f.App.On("EVMORM").Return(f.Mocks.evmORM) f.Mocks.evmORM.PutChains(toml.EVMConfig{ChainID: &chainID}) - }, query: query, result: ` @@ -76,7 +75,7 @@ func TestResolver_Nodes(t *testing.T) { name: "generic error", authenticated: true, before: func(f *gqlTestFramework) { - f.Mocks.relayerChainInterops.On("NodeStatuses", mock.Anything, PageDefaultOffset, PageDefaultLimit).Return([]types.NodeStatus{}, 0, gError) + f.Mocks.relayerChainInterops.NodesErr = gError f.App.On("GetRelayers").Return(f.Mocks.relayerChainInterops) }, query: query, diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index d0523d6b968..fa8471c5e2b 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -52,7 +52,7 @@ type mocks struct { solana *keystoreMocks.Solana chain *evmORMMocks.Chain legacyEVMChains *evmORMMocks.LegacyChainContainer - relayerChainInterops *chainlinkMocks.RelayerChainInteroperators + relayerChainInterops *chainlinkMocks.FakeRelayerChainInteroperators ethClient *evmClientMocks.Client eIMgr *webhookmocks.ExternalInitiatorManager balM *evmORMMocks.BalanceMonitor @@ -111,7 +111,7 @@ func setupFramework(t *testing.T) *gqlTestFramework { solana: keystoreMocks.NewSolana(t), chain: evmORMMocks.NewChain(t), legacyEVMChains: evmORMMocks.NewLegacyChainContainer(t), - relayerChainInterops: chainlinkMocks.NewRelayerChainInteroperators(t), + relayerChainInterops: &chainlinkMocks.FakeRelayerChainInteroperators{}, ethClient: evmClientMocks.NewClient(t), eIMgr: webhookmocks.NewExternalInitiatorManager(t), balM: evmORMMocks.NewBalanceMonitor(t), diff --git a/go.mod b/go.mod index ad3cb5f78ed..2df4b05c749 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/smartcontractkit/chainlink/v2 go 1.21 require ( - github.com/CosmWasm/wasmd v0.40.1 github.com/Depado/ginprom v1.7.11 github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/sprig/v3 v3.2.3 @@ -24,7 +23,6 @@ require ( github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 github.com/go-webauthn/webauthn v0.8.2 - github.com/gogo/protobuf v1.3.3 github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 github.com/google/uuid v1.3.1 github.com/gorilla/securecookie v1.1.1 @@ -67,7 +65,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb @@ -116,6 +114,7 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect + github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -184,6 +183,7 @@ require ( github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/gogo/protobuf v1.3.3 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect diff --git a/go.sum b/go.sum index f879f16272b..f13b00d6db7 100644 --- a/go.sum +++ b/go.sum @@ -1457,8 +1457,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index aa670da1c96..be9285c6d48 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -383,7 +383,7 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f7b55b259f2..7c5542bfa68 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2360,8 +2360,8 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0 h1:YrJ3moRDu2kgdv4o3Hym/FWVF4MS5cIZ7o7wk+43pvk= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231030134738-81a5a89699a0/go.mod h1:fxtwgVZzTgoU1CpdSxNvFXecIY2r8DhH2JCzPO4e9G0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= From 463f938e1fb9f4d3782c6f147331d22bc0dc59c4 Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 2 Nov 2023 11:30:54 +0000 Subject: [PATCH 052/271] [chore] Skip Mercury/VRF flakey tests (#11141) --- core/services/relay/evm/mercury/config_poller_test.go | 1 + core/services/vrf/v2/integration_v2_plus_test.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/core/services/relay/evm/mercury/config_poller_test.go b/core/services/relay/evm/mercury/config_poller_test.go index 6a692b0eacd..1b3ba72128d 100644 --- a/core/services/relay/evm/mercury/config_poller_test.go +++ b/core/services/relay/evm/mercury/config_poller_test.go @@ -115,6 +115,7 @@ func TestMercuryConfigPoller(t *testing.T) { } func TestNotify(t *testing.T) { + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-2746") feedIDStr := "8257737fdf4f79639585fd0ed01bea93c248a9ad940e98dd27f41c9b6230fed1" feedIDBytes, err := hexutil.Decode("0x" + feedIDStr) require.NoError(t, err) diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index f08c10c2004..6d2b77acb7d 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -304,6 +304,7 @@ func newVRFCoordinatorV2PlusUniverse(t *testing.T, key ethkey.KeyV2, numConsumer } func TestVRFV2PlusIntegration_SingleConsumer_HappyPath_BatchFulfillment(t *testing.T) { + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-2745") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) @@ -456,6 +457,7 @@ func TestVRFV2PlusIntegration_SingleConsumer_HappyPath(t *testing.T) { } func TestVRFV2PlusIntegration_SingleConsumer_EOA_Request(t *testing.T) { + testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-2744") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) From 627d698401b4b19321b7db603d4dbba4d1c8ca4e Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 2 Nov 2023 09:52:49 -0400 Subject: [PATCH 053/271] Bump Chainlink-relay to c686b4d48672d0eb818beafcff90ad4e33ea5db3 (#11145) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index f2b1f9a4c94..65dcec563e5 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -302,7 +302,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 683cc1ea06c..781eed46ceb 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1458,8 +1458,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 h1:59vz5H52EpwWE/64ZQpNCs7Gtnyi7/ytjyoGjlbKhBA= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/go.mod b/go.mod index 2df4b05c749..569912ed1bf 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index f13b00d6db7..155e54646d7 100644 --- a/go.sum +++ b/go.sum @@ -1459,8 +1459,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 h1:59vz5H52EpwWE/64ZQpNCs7Gtnyi7/ytjyoGjlbKhBA= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index be9285c6d48..33beae119ae 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -384,7 +384,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 7c5542bfa68..7969e82144c 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2362,8 +2362,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111 h1:CElKhWq0WIa9Rmg5Ssajs5Hp3m3u/nYIQdXtpj2gbcc= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231031114820-e9826d481111/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 h1:59vz5H52EpwWE/64ZQpNCs7Gtnyi7/ytjyoGjlbKhBA= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From f7e868e171c0fc2ee656cd575c0912ead5b3fe2d Mon Sep 17 00:00:00 2001 From: Makram Date: Thu, 2 Nov 2023 16:16:04 +0100 Subject: [PATCH 054/271] fix: v2 superscript (#11146) * dereference only after parsing args * fix sending key parsing * fix public key parsing --- .../vrfv2/testnet/v2scripts/super_scripts.go | 101 +++++++++++------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go index f5e37005690..23ad8e1374b 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go +++ b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go @@ -5,10 +5,6 @@ import ( "encoding/hex" "flag" "fmt" - "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/constants" - "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/jobs" - "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/model" - "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/util" "math/big" "os" "strings" @@ -18,6 +14,11 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/shopspring/decimal" + "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/constants" + "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/jobs" + "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/model" + "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/util" + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" @@ -37,12 +38,12 @@ func DeployUniverseViaCLI(e helpers.Environment) { deployCmd := flag.NewFlagSet("deploy-universe", flag.ExitOnError) // required flags - linkAddress := *deployCmd.String("link-address", "", "address of link token") - linkEthAddress := *deployCmd.String("link-eth-feed", "", "address of link eth feed") - bhsContractAddressString := *deployCmd.String("bhs-address", "", "address of BHS contract") - batchBHSAddressString := *deployCmd.String("batch-bhs-address", "", "address of Batch BHS contract") - coordinatorAddressString := *deployCmd.String("coordinator-address", "", "address of VRF Coordinator contract") - batchCoordinatorAddressString := *deployCmd.String("batch-coordinator-address", "", "address Batch VRF Coordinator contract") + linkAddress := deployCmd.String("link-address", "", "address of link token") + linkEthAddress := deployCmd.String("link-eth-feed", "", "address of link eth feed") + bhsContractAddressString := deployCmd.String("bhs-address", "", "address of BHS contract") + batchBHSAddressString := deployCmd.String("batch-bhs-address", "", "address of Batch BHS contract") + coordinatorAddressString := deployCmd.String("coordinator-address", "", "address of VRF Coordinator contract") + batchCoordinatorAddressString := deployCmd.String("batch-coordinator-address", "", "address Batch VRF Coordinator contract") subscriptionBalanceJuelsString := deployCmd.String("subscription-balance", constants.SubscriptionBalanceJuels, "amount to fund subscription") nodeSendingKeyFundingAmount := deployCmd.String("sending-key-funding-amount", constants.NodeSendingKeyFundingAmount, "CL node sending key funding amount") @@ -87,7 +88,10 @@ func DeployUniverseViaCLI(e helpers.Environment) { ReqsForTier5: big.NewInt(*reqsForTier5), } - vrfPrimaryNodeSendingKeys := strings.Split(*vrfPrimaryNodeSendingKeysString, ",") + var vrfPrimaryNodeSendingKeys []string + if len(*vrfPrimaryNodeSendingKeysString) > 0 { + vrfPrimaryNodeSendingKeys = strings.Split(*vrfPrimaryNodeSendingKeysString, ",") + } nodesMap := make(map[string]model.Node) @@ -100,14 +104,14 @@ func DeployUniverseViaCLI(e helpers.Environment) { SendingKeyFundingAmount: fundingAmount, } - bhsContractAddress := common.HexToAddress(bhsContractAddressString) - batchBHSAddress := common.HexToAddress(batchBHSAddressString) - coordinatorAddress := common.HexToAddress(coordinatorAddressString) - batchCoordinatorAddress := common.HexToAddress(batchCoordinatorAddressString) + bhsContractAddress := common.HexToAddress(*bhsContractAddressString) + batchBHSAddress := common.HexToAddress(*batchBHSAddressString) + coordinatorAddress := common.HexToAddress(*coordinatorAddressString) + batchCoordinatorAddress := common.HexToAddress(*batchCoordinatorAddressString) contractAddresses := model.ContractAddresses{ - LinkAddress: linkAddress, - LinkEthAddress: linkEthAddress, + LinkAddress: *linkAddress, + LinkEthAddress: *linkEthAddress, BhsContractAddress: bhsContractAddress, BatchBHSAddress: batchBHSAddress, CoordinatorAddress: coordinatorAddress, @@ -149,29 +153,32 @@ func VRFV2DeployUniverse( batchFulfillmentEnabled bool, nodesMap map[string]model.Node, ) model.JobSpecs { - - // Put key in ECDSA format - if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { - *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) - } - - // Generate compressed public key and key hash - pubBytes, err := hex.DecodeString(*registerKeyUncompressedPubKey) - helpers.PanicErr(err) - pk, err := crypto.UnmarshalPubkey(pubBytes) - helpers.PanicErr(err) - var pkBytes []byte - if big.NewInt(0).Mod(pk.Y, big.NewInt(2)).Uint64() != 0 { - pkBytes = append(pk.X.Bytes(), 1) - } else { - pkBytes = append(pk.X.Bytes(), 0) + var compressedPkHex string + var keyHash common.Hash + if len(*registerKeyUncompressedPubKey) > 0 { + // Put key in ECDSA format + if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { + *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) + } + + // Generate compressed public key and key hash + pubBytes, err := hex.DecodeString(*registerKeyUncompressedPubKey) + helpers.PanicErr(err) + pk, err := crypto.UnmarshalPubkey(pubBytes) + helpers.PanicErr(err) + var pkBytes []byte + if big.NewInt(0).Mod(pk.Y, big.NewInt(2)).Uint64() != 0 { + pkBytes = append(pk.X.Bytes(), 1) + } else { + pkBytes = append(pk.X.Bytes(), 0) + } + var newPK secp256k1.PublicKey + copy(newPK[:], pkBytes) + + compressedPkHex = hexutil.Encode(pkBytes) + keyHash, err = newPK.Hash() + helpers.PanicErr(err) } - var newPK secp256k1.PublicKey - copy(newPK[:], pkBytes) - - compressedPkHex := hexutil.Encode(pkBytes) - keyHash, err := newPK.Hash() - helpers.PanicErr(err) if len(contractAddresses.LinkAddress) == 0 { fmt.Println("\nDeploying LINK Token...") @@ -268,7 +275,13 @@ func VRFV2DeployUniverse( e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses contractAddresses.CoordinatorAddress, - nodesMap[model.VRFPrimaryNodeName].SendingKeys[0].Address, + func() string { + if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { + return keys[0].Address + } else { + return common.HexToAddress("0x0").String() + } + }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, ) @@ -283,7 +296,13 @@ func VRFV2DeployUniverse( e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFBackupNodeName].SendingKeys), "\",\""), //fromAddresses contractAddresses.CoordinatorAddress, - nodesMap[model.VRFPrimaryNodeName].SendingKeys[0], + func() string { + if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { + return keys[0].Address + } else { + return common.HexToAddress("0x0").String() + } + }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, ) From a08b7054e9384c597112496dbe23683983c84a00 Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 2 Nov 2023 15:57:41 +0000 Subject: [PATCH 055/271] [BCF-2463] Add generic job type for lightweight OCR plugins (#10665) * [BCF-2463] Add generic job type for lightweight OCR plugins * [chore] Refactor ErrJobSpecNoRelayer; add ErrRelayerNotEnabled * [feedback] Move validation of PluginConfig to ocr2/validate/validate.go * Add tests for pluginConfig * Sensible defaults for command --- core/services/ocr2/delegate.go | 169 ++++++++++++++++-- .../ocr2/plugins/generic/helpers_test.go | 7 + .../ocr2/plugins/generic/merge_test.go | 32 ++++ .../generic/pipeline_runner_adapter_test.go | 35 +--- .../ocr2/plugins/generic/telemetry_adapter.go | 13 +- .../plugins/generic/telemetry_adapter_test.go | 15 +- core/services/ocr2/validate/validate.go | 33 ++++ core/services/ocr2/validate/validate_test.go | 83 +++++++++ go.mod | 2 +- 9 files changed, 334 insertions(+), 55 deletions(-) create mode 100644 core/services/ocr2/plugins/generic/helpers_test.go create mode 100644 core/services/ocr2/plugins/generic/merge_test.go diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index efb6f04fd3d..e822fd5d8f2 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -8,6 +8,7 @@ import ( "log" "time" + "google.golang.org/grpc" "gopkg.in/guregu/null.v4" "github.com/ethereum/go-ethereum/common" @@ -30,6 +31,7 @@ import ( relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins" "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -43,6 +45,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/persistence" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper" @@ -70,7 +73,28 @@ import ( "github.com/smartcontractkit/chainlink/v2/plugins" ) -var ErrJobSpecNoRelayer = errors.New("OCR2 job spec could not get relayer id") +type ErrJobSpecNoRelayer struct { + PluginName string + Err error +} + +func (e ErrJobSpecNoRelayer) Unwrap() error { return e.Err } + +func (e ErrJobSpecNoRelayer) Error() string { + return fmt.Sprintf("%s services: OCR2 job spec could not get relayer ID: %s", e.PluginName, e.Err) +} + +type ErrRelayNotEnabled struct { + PluginName string + Relay string + Err error +} + +func (e ErrRelayNotEnabled) Unwrap() error { return e.Err } + +func (e ErrRelayNotEnabled) Error() string { + return fmt.Sprintf("%s services: failed to get relay %s, is it enabled? %s", e.PluginName, e.Relay, e.Err) +} type RelayGetter interface { Get(id relay.ID) (loop.Relayer, error) @@ -245,7 +269,7 @@ func (d *Delegate) OnDeleteJob(jb job.Job, q pg.Queryer) error { rid, err := spec.RelayID() if err != nil { - d.lggr.Errorw("DeleteJob: "+ErrJobSpecNoRelayer.Error(), "err", err) + d.lggr.Errorw("DeleteJob", "err", ErrJobSpecNoRelayer{Err: err, PluginName: string(spec.PluginType)}) return nil } // we only have clean to do for the EVM @@ -337,7 +361,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("ServicesForSpec: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: string(spec.PluginType)} } if rid.Network == relay.EVM { @@ -428,6 +452,9 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { s4PluginDB := NewDB(d.db, spec.ID, s4PluginId, lggr, d.cfg.Database()) return d.newServicesOCR2Functions(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, thresholdPluginDB, s4PluginDB, lc, ocrLogger) + case types.GenericPlugin: + return d.newServicesGenericPlugin(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + default: return nil, errors.Errorf("plugin type %s not supported", spec.PluginType) } @@ -473,6 +500,124 @@ func GetEVMEffectiveTransmitterID(jb *job.Job, chain evm.Chain, lggr logger.Suga return spec.TransmitterID.String, nil } +type connProvider interface { + ClientConn() grpc.ClientConnInterface +} + +func defaultPathFromPluginName(pluginName string) string { + // By default we install the command on the system path, in the + // form: `chainlink-` + return fmt.Sprintf("chainlink-%s", pluginName) +} + +func (d *Delegate) newServicesGenericPlugin( + ctx context.Context, + lggr logger.SugaredLogger, + jb job.Job, + bootstrapPeers []commontypes.BootstrapperLocator, + kb ocr2key.KeyBundle, + ocrDB *db, + lc ocrtypes.LocalConfig, + ocrLogger commontypes.Logger, +) (srvs []job.ServiceCtx, err error) { + spec := jb.OCR2OracleSpec + + p := validate.OCR2GenericPluginConfig{} + err = json.Unmarshal(spec.PluginConfig.Bytes(), &p) + if err != nil { + return nil, err + } + cconf := p.CoreConfig + + command := cconf.Command + if command == "" { + command = defaultPathFromPluginName(cconf.PluginName) + } + + // NOTE: we don't need to validate this config, since that happens as part of creating the job. + // See: validate/validate.go's `validateSpec`. + + rid, err := spec.RelayID() + if err != nil { + return nil, ErrJobSpecNoRelayer{PluginName: cconf.PluginName, Err: err} + } + + relayer, err := d.RelayGetter.Get(rid) + if err != nil { + return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: p.CoreConfig.PluginName} + } + + provider, err := relayer.NewPluginProvider(ctx, types.RelayArgs{ + ExternalJobID: jb.ExternalJobID, + JobID: spec.ID, + ContractID: spec.ContractID, + New: d.isNewlyCreatedJob, + RelayConfig: spec.RelayConfig.Bytes(), + ProviderType: cconf.ProviderType, + }, types.PluginArgs{ + TransmitterID: spec.TransmitterID.String, + PluginConfig: spec.PluginConfig.Bytes(), + }) + if err != nil { + return nil, err + } + srvs = append(srvs, provider) + + oracleEndpoint := d.monitoringEndpointGen.GenMonitoringEndpoint( + spec.ContractID, + synchronization.TelemetryType(cconf.TelemetryType), + rid.Network, + rid.ChainID, + ) + oracleArgs := libocr2.OCR2OracleArgs{ + BinaryNetworkEndpointFactory: d.peerWrapper.Peer2, + V2Bootstrappers: bootstrapPeers, + Database: ocrDB, + LocalConfig: lc, + Logger: ocrLogger, + MonitoringEndpoint: oracleEndpoint, + OffchainKeyring: kb, + OnchainKeyring: kb, + ContractTransmitter: provider.ContractTransmitter(), + ContractConfigTracker: provider.ContractConfigTracker(), + OffchainConfigDigester: provider.OffchainConfigDigester(), + } + + pluginLggr := lggr.Named(cconf.PluginName).Named(spec.ContractID).Named(spec.GetID()) + cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(fmt.Sprintf("%s-%s-%s", cconf.PluginName, spec.ContractID, spec.GetID()), command) + if err != nil { + return nil, fmt.Errorf("failed to register loop: %w", err) + } + + errorLog := &errorLog{jobID: jb.ID, recordError: d.jobORM.RecordError} + providerConn, ok := provider.(connProvider) + if !ok { + return nil, errors.New("provider not supported: the provider is not a LOOPP provider") + } + + pluginConfig := types.ReportingPluginServiceConfig{ + PluginName: cconf.PluginName, + Command: command, + ProviderType: cconf.ProviderType, + PluginConfig: string(p.PluginConfig), + } + + pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner) + ta := generic.NewTelemetryAdapter(d.monitoringEndpointGen) + + plugin := reportingplugins.NewLOOPPService(pluginLggr, grpcOpts, cmdFn, pluginConfig, providerConn.ClientConn(), pr, ta, errorLog) + oracleArgs.ReportingPluginFactory = plugin + srvs = append(srvs, plugin) + + oracle, err := libocr2.NewOracle(oracleArgs) + if err != nil { + return nil, err + } + + srvs = append(srvs, job.NewServiceAdapter(oracle)) + return srvs, nil +} + func (d *Delegate) newServicesMercury( ctx context.Context, lggr logger.SugaredLogger, @@ -498,14 +643,14 @@ func (d *Delegate) newServicesMercury( rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("mercury services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "mercury"} } if rid.Network != relay.EVM { return nil, fmt.Errorf("mercury services: expected EVM relayer got %s", rid.Network) } relayer, err := d.RelayGetter.Get(rid) if err != nil { - return nil, fmt.Errorf("failed to get relay %s is it enabled?: %w", spec.Relay, err) + return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: "mercury"} } chain, err := d.legacyChains.Get(rid.ChainID) if err != nil { @@ -574,7 +719,7 @@ func (d *Delegate) newServicesMedian( rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("median services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "median"} } oracleArgsNoPlugin := libocr2.OCR2OracleArgs{ @@ -593,7 +738,7 @@ func (d *Delegate) newServicesMedian( relayer, err := d.RelayGetter.Get(rid) if err != nil { - return nil, fmt.Errorf("median services; failed to get relay %s is it enabled?: %w", spec.Relay, err) + return nil, ErrRelayNotEnabled{Err: err, PluginName: "median", Relay: spec.Relay} } medianServices, err2 := median.NewMedianServices(ctx, jb, d.isNewlyCreatedJob, relayer, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, mConfig, enhancedTelemChan, errorLog) @@ -618,7 +763,7 @@ func (d *Delegate) newServicesDKG( spec := jb.OCR2OracleSpec rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("DKG services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "DKG"} } if rid.Network != relay.EVM { return nil, fmt.Errorf("DKG services: expected EVM relayer got %s", rid.Network) @@ -687,7 +832,7 @@ func (d *Delegate) newServicesOCR2VRF( rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("VRF services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "VRF"} } if rid.Network != relay.EVM { return nil, fmt.Errorf("VRF services: expected EVM relayer got %s", rid.Network) @@ -912,7 +1057,7 @@ func (d *Delegate) newServicesOCR2Keepers21( mc := d.cfg.Mercury().Credentials(credName) rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("keeper2 services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "keeper2"} } if rid.Network != relay.EVM { return nil, fmt.Errorf("keeper2 services: expected EVM relayer got %s", rid.Network) @@ -1026,7 +1171,7 @@ func (d *Delegate) newServicesOCR2Keepers20( rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("keepers2.0 services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "keepers2.0"} } if rid.Network != relay.EVM { return nil, fmt.Errorf("keepers2.0 services: expected EVM relayer got %s", rid.Network) @@ -1161,7 +1306,7 @@ func (d *Delegate) newServicesOCR2Functions( rid, err := spec.RelayID() if err != nil { - return nil, fmt.Errorf("functions services: %w: %w", ErrJobSpecNoRelayer, err) + return nil, ErrJobSpecNoRelayer{Err: err, PluginName: "functions"} } if rid.Network != relay.EVM { return nil, fmt.Errorf("functions services: expected EVM relayer got %s", rid.Network) diff --git a/core/services/ocr2/plugins/generic/helpers_test.go b/core/services/ocr2/plugins/generic/helpers_test.go new file mode 100644 index 00000000000..e23e8e46429 --- /dev/null +++ b/core/services/ocr2/plugins/generic/helpers_test.go @@ -0,0 +1,7 @@ +package generic + +import "github.com/smartcontractkit/libocr/commontypes" + +func (t *TelemetryAdapter) Endpoints() map[[4]string]commontypes.MonitoringEndpoint { + return t.endpoints +} diff --git a/core/services/ocr2/plugins/generic/merge_test.go b/core/services/ocr2/plugins/generic/merge_test.go new file mode 100644 index 00000000000..9618c62357d --- /dev/null +++ b/core/services/ocr2/plugins/generic/merge_test.go @@ -0,0 +1,32 @@ +package generic + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMerge(t *testing.T) { + vars := map[string]interface{}{ + "jb": map[string]interface{}{ + "databaseID": "some-job-id", + }, + } + addedVars := map[string]interface{}{ + "jb": map[string]interface{}{ + "some-other-var": "foo", + }, + "val": 0, + } + + merge(vars, addedVars) + + assert.True(t, reflect.DeepEqual(vars, map[string]interface{}{ + "jb": map[string]interface{}{ + "databaseID": "some-job-id", + "some-other-var": "foo", + }, + "val": 0, + }), vars) +} diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go index ee0038232dc..ef0e7421b50 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go @@ -1,9 +1,8 @@ -package generic +package generic_test import ( "context" "net/http" - "reflect" "testing" "time" @@ -20,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -52,7 +52,7 @@ func TestAdapter_Integration(t *testing.T) { http.DefaultClient, http.DefaultClient, ) - pra := NewPipelineRunnerAdapter(logger, job.Job{}, pr) + pra := generic.NewPipelineRunnerAdapter(logger, job.Job{}, pr) results, err := pra.ExecuteRun(context.Background(), spec, types.Vars{Vars: map[string]interface{}{"val": 1}}, types.Options{}) require.NoError(t, err) @@ -83,7 +83,7 @@ func TestAdapter_AddsDefaultVars(t *testing.T) { logger := logger.TestLogger(t) mpr := newMockPipelineRunner() jobID, externalJobID, name := int32(100), uuid.New(), null.StringFrom("job-name") - pra := NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name}, mpr) + pra := generic.NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name}, mpr) _, err := pra.ExecuteRun(context.Background(), spec, types.Vars{}, types.Options{}) require.NoError(t, err) @@ -105,7 +105,7 @@ func TestPipelineRunnerAdapter_SetsVarsOnSpec(t *testing.T) { logger := logger.TestLogger(t) mpr := newMockPipelineRunner() jobID, externalJobID, name, jobType := int32(100), uuid.New(), null.StringFrom("job-name"), job.Type("generic") - pra := NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name, Type: jobType}, mpr) + pra := generic.NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name, Type: jobType}, mpr) maxDuration := time.Duration(100 * time.Second) _, err := pra.ExecuteRun(context.Background(), spec, types.Vars{}, types.Options{MaxTaskDuration: maxDuration}) @@ -115,29 +115,4 @@ func TestPipelineRunnerAdapter_SetsVarsOnSpec(t *testing.T) { assert.Equal(t, name.ValueOrZero(), mpr.spec.JobName) assert.Equal(t, string(jobType), mpr.spec.JobType) assert.Equal(t, maxDuration, mpr.spec.MaxTaskDuration.Duration()) - -} - -func TestMerge(t *testing.T) { - vars := map[string]interface{}{ - "jb": map[string]interface{}{ - "databaseID": "some-job-id", - }, - } - addedVars := map[string]interface{}{ - "jb": map[string]interface{}{ - "some-other-var": "foo", - }, - "val": 0, - } - - merge(vars, addedVars) - - assert.True(t, reflect.DeepEqual(vars, map[string]interface{}{ - "jb": map[string]interface{}{ - "databaseID": "some-job-id", - "some-other-var": "foo", - }, - "val": 0, - }), vars) } diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter.go b/core/services/ocr2/plugins/generic/telemetry_adapter.go index a81befa9854..e7f87dcd46c 100644 --- a/core/services/ocr2/plugins/generic/telemetry_adapter.go +++ b/core/services/ocr2/plugins/generic/telemetry_adapter.go @@ -6,17 +6,20 @@ import ( "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" + "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" + "github.com/smartcontractkit/chainlink-relay/pkg/types" ) var _ types.TelemetryService = (*TelemetryAdapter)(nil) type TelemetryAdapter struct { - endpointGenerator types.MonitoringEndpointGenerator + endpointGenerator telemetry.MonitoringEndpointGenerator endpoints map[[4]string]commontypes.MonitoringEndpoint } -func NewTelemetryAdapter(endpointGen types.MonitoringEndpointGenerator) *TelemetryAdapter { +func NewTelemetryAdapter(endpointGen telemetry.MonitoringEndpointGenerator) *TelemetryAdapter { return &TelemetryAdapter{ endpoints: make(map[[4]string]commontypes.MonitoringEndpoint), endpointGenerator: endpointGen, @@ -24,7 +27,7 @@ func NewTelemetryAdapter(endpointGen types.MonitoringEndpointGenerator) *Telemet } func (t *TelemetryAdapter) Send(ctx context.Context, network string, chainID string, contractID string, telemetryType string, payload []byte) error { - e, err := t.getOrCreateEndpoint(contractID, telemetryType, network, chainID) + e, err := t.getOrCreateEndpoint(network, chainID, contractID, telemetryType) if err != nil { return err } @@ -32,7 +35,7 @@ func (t *TelemetryAdapter) Send(ctx context.Context, network string, chainID str return nil } -func (t *TelemetryAdapter) getOrCreateEndpoint(contractID string, telemetryType string, network string, chainID string) (commontypes.MonitoringEndpoint, error) { +func (t *TelemetryAdapter) getOrCreateEndpoint(network string, chainID string, contractID string, telemetryType string) (commontypes.MonitoringEndpoint, error) { if contractID == "" { return nil, errors.New("contractID cannot be empty") } @@ -49,7 +52,7 @@ func (t *TelemetryAdapter) getOrCreateEndpoint(contractID string, telemetryType key := [4]string{network, chainID, contractID, telemetryType} e, ok := t.endpoints[key] if !ok { - e = t.endpointGenerator.GenMonitoringEndpoint(network, chainID, contractID, telemetryType) + e = t.endpointGenerator.GenMonitoringEndpoint(contractID, synchronization.TelemetryType(telemetryType), network, chainID) t.endpoints[key] = e } return e, nil diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go index d430b889a4b..9c42b0f85d5 100644 --- a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go +++ b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go @@ -1,13 +1,15 @@ -package generic +package generic_test import ( "context" - "fmt" "testing" "github.com/smartcontractkit/libocr/commontypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" ) type mockEndpoint struct { @@ -22,17 +24,17 @@ func (m *mockEndpoint) SendLog(payload []byte) { m.payload = payload } type mockGenerator struct{} -func (m *mockGenerator) GenMonitoringEndpoint(network, chainID, contractID, telemetryType string) commontypes.MonitoringEndpoint { +func (m *mockGenerator) GenMonitoringEndpoint(contractID string, telemetryType synchronization.TelemetryType, network string, chainID string) commontypes.MonitoringEndpoint { return &mockEndpoint{ network: network, chainID: chainID, contractID: contractID, - telemetryType: telemetryType, + telemetryType: string(telemetryType), } } func TestTelemetryAdapter(t *testing.T) { - ta := NewTelemetryAdapter(&mockGenerator{}) + ta := generic.NewTelemetryAdapter(&mockGenerator{}) tests := []struct { name string @@ -92,8 +94,7 @@ func TestTelemetryAdapter(t *testing.T) { } else { require.NoError(t, err) key := [4]string{test.networkID, test.chainID, test.contractID, test.telemetryType} - fmt.Printf("%+v", ta.endpoints) - endpoint, ok := ta.endpoints[key] + endpoint, ok := ta.Endpoints()[key] require.True(t, ok) me := endpoint.(*mockEndpoint) diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index cde1a1f9276..78802f6559c 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -114,6 +114,8 @@ func validateSpec(tree *toml.Tree, spec job.Job) error { return nil case types.Mercury: return validateOCR2MercurySpec(spec.OCR2OracleSpec.PluginConfig, *spec.OCR2OracleSpec.FeedID) + case types.GenericPlugin: + return validateOCR2GenericPluginSpec(spec.OCR2OracleSpec.PluginConfig) case "": return errors.New("no plugin specified") default: @@ -123,6 +125,37 @@ func validateSpec(tree *toml.Tree, spec job.Job) error { return nil } +type coreConfig struct { + Command string `json:"command"` + ProviderType string `json:"providerType"` + PluginName string `json:"pluginName"` + TelemetryType string `json:"telemetryType"` +} + +type OCR2GenericPluginConfig struct { + CoreConfig coreConfig `json:"coreConfig"` + PluginConfig json.RawMessage +} + +func validateOCR2GenericPluginSpec(jsonConfig job.JSONConfig) error { + p := OCR2GenericPluginConfig{} + err := json.Unmarshal(jsonConfig.Bytes(), &p) + if err != nil { + return err + } + + cc := p.CoreConfig + if cc.PluginName == "" { + return errors.New("generic config invalid: must provide plugin name") + } + + if cc.TelemetryType == "" { + return errors.New("generic config invalid: must provide telemetry type") + } + + return nil +} + func validateDKGSpec(jsonConfig job.JSONConfig) error { if jsonConfig == nil { return errors.New("pluginConfig is empty") diff --git a/core/services/ocr2/validate/validate_test.go b/core/services/ocr2/validate/validate_test.go index 4685ed745dd..5b40224a4bf 100644 --- a/core/services/ocr2/validate/validate_test.go +++ b/core/services/ocr2/validate/validate_test.go @@ -580,6 +580,89 @@ KeyID = "6f3b82406688b8ddb944c6f2e6d808f014c8fa8d568d639c25019568c require.Contains(t, err.Error(), "validation error for keyID") }, }, + { + name: "Generic plugin config validation - nothing provided", + toml: ` +type = "offchainreporting2" +schemaVersion = 1 +name = "dkg" +externalJobID = "6d46d85f-d38c-4f4a-9f00-ac29a25b6330" +maxTaskDuration = "1s" +contractID = "0x3e54dCc49F16411A3aaa4cDbC41A25bCa9763Cee" +ocrKeyBundleID = "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17" +p2pv2Bootstrappers = [ + "12D3KooWSbPRwXY4gxFRJT7LWCnjgGbR4S839nfCRCDgQUiNenxa@127.0.0.1:8000" +] +relay = "evm" +pluginType = "plugin" +transmitterID = "0x74103Cf8b436465870b26aa9Fa2F62AD62b22E35" + +[relayConfig] +chainID = 4 + +[pluginConfig.coreConfig] +`, + assertion: func(t *testing.T, os job.Job, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "must provide plugin name") + }, + }, + { + name: "Generic plugin config validation - plugin name provided", + toml: ` +type = "offchainreporting2" +schemaVersion = 1 +name = "dkg" +externalJobID = "6d46d85f-d38c-4f4a-9f00-ac29a25b6330" +maxTaskDuration = "1s" +contractID = "0x3e54dCc49F16411A3aaa4cDbC41A25bCa9763Cee" +ocrKeyBundleID = "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17" +p2pv2Bootstrappers = [ + "12D3KooWSbPRwXY4gxFRJT7LWCnjgGbR4S839nfCRCDgQUiNenxa@127.0.0.1:8000" +] +relay = "evm" +pluginType = "plugin" +transmitterID = "0x74103Cf8b436465870b26aa9Fa2F62AD62b22E35" + +[relayConfig] +chainID = 4 + +[pluginConfig.coreConfig] +pluginName = "median" +`, + assertion: func(t *testing.T, os job.Job, err error) { + require.Error(t, err) + require.ErrorContains(t, err, "must provide telemetry type") + }, + }, + { + name: "Generic plugin config validation - all provided", + toml: ` +type = "offchainreporting2" +schemaVersion = 1 +name = "dkg" +externalJobID = "6d46d85f-d38c-4f4a-9f00-ac29a25b6330" +maxTaskDuration = "1s" +contractID = "0x3e54dCc49F16411A3aaa4cDbC41A25bCa9763Cee" +ocrKeyBundleID = "08d14c6eed757414d72055d28de6caf06535806c6a14e450f3a2f1c854420e17" +p2pv2Bootstrappers = [ + "12D3KooWSbPRwXY4gxFRJT7LWCnjgGbR4S839nfCRCDgQUiNenxa@127.0.0.1:8000" +] +relay = "evm" +pluginType = "plugin" +transmitterID = "0x74103Cf8b436465870b26aa9Fa2F62AD62b22E35" + +[relayConfig] +chainID = 4 + +[pluginConfig.coreConfig] +pluginName = "median" +telemetryType = "median" +`, + assertion: func(t *testing.T, os job.Job, err error) { + require.NoError(t, err) + }, + }, } for _, tc := range tt { diff --git a/go.mod b/go.mod index 569912ed1bf..820e42c3308 100644 --- a/go.mod +++ b/go.mod @@ -97,6 +97,7 @@ require ( golang.org/x/time v0.3.0 golang.org/x/tools v0.14.0 gonum.org/v1/gonum v0.13.0 + google.golang.org/grpc v1.58.3 google.golang.org/protobuf v1.31.0 gopkg.in/guregu/null.v2 v2.1.2 gopkg.in/guregu/null.v4 v4.0.0 @@ -364,7 +365,6 @@ require ( google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/grpc v1.58.3 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect From 2c295a7756fa41b83039a721598514bdaa522297 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Thu, 2 Nov 2023 17:16:20 +0100 Subject: [PATCH 056/271] BCF-2749 remove legacy chains job orm (#11140) * Remove legacy chains from job orm - This removes evm cfg loading into jobs str8 from db * Remove legacy chains from tests that used it for job orm * Remove legacy env var test from TestRunner integration tests * minor fix for test runner * minor err handling change in vrf delegate_test.go --- core/internal/cltest/cltest.go | 2 +- core/internal/cltest/factories.go | 4 +- core/internal/cltest/job_factories.go | 6 +- core/internal/features/features_test.go | 3 +- core/services/chainlink/application.go | 2 +- core/services/cron/cron_test.go | 6 +- core/services/directrequest/delegate_test.go | 2 +- core/services/feeds/orm_test.go | 4 +- core/services/fluxmonitorv2/orm_test.go | 6 +- core/services/job/job_orm_test.go | 96 ++++++----------- .../job/job_pipeline_orm_integration_test.go | 2 +- core/services/job/orm.go | 68 +++--------- core/services/job/orm_test.go | 5 +- core/services/job/runner_integration_test.go | 100 +----------------- core/services/job/spawner_test.go | 11 +- core/services/pipeline/orm_test.go | 10 +- core/services/vrf/delegate_test.go | 4 +- 17 files changed, 75 insertions(+), 256 deletions(-) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 4cb9808fe24..fb4a69cf30c 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -191,7 +191,7 @@ func NewJobPipelineV2(t testing.TB, cfg pipeline.BridgeConfig, jpcfg JobPipeline lggr := logger.TestLogger(t) prm := pipeline.NewORM(db, lggr, dbCfg, jpcfg.MaxSuccessfulRuns()) btORM := bridges.NewORM(db, lggr, dbCfg) - jrm := job.NewORM(db, legacyChains, prm, btORM, keyStore, lggr, dbCfg) + jrm := job.NewORM(db, prm, btORM, keyStore, lggr, dbCfg) pr := pipeline.NewRunner(prm, btORM, jpcfg, cfg, legacyChains, keyStore.Eth(), keyStore.VRF(), lggr, restrictedHTTPClient, unrestrictedHTTPClient) return JobPipelineV2TestHelper{ prm, diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index 85ffc6b02bd..82235baf6b1 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -563,7 +563,7 @@ func MustInsertV2JobSpec(t *testing.T, db *sqlx.DB, transmitterAddress common.Ad PipelineSpecID: pipelineSpec.ID, } - jorm := job.NewORM(db, nil, nil, nil, nil, logger.TestLogger(t), configtest.NewTestGeneralConfig(t).Database()) + jorm := job.NewORM(db, nil, nil, nil, logger.TestLogger(t), configtest.NewTestGeneralConfig(t).Database()) err = jorm.InsertJob(&jb) require.NoError(t, err) return jb @@ -619,7 +619,7 @@ func MustInsertKeeperJob(t *testing.T, db *sqlx.DB, korm keeper.ORM, from ethkey tlg := logger.TestLogger(t) prm := pipeline.NewORM(db, tlg, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) btORM := bridges.NewORM(db, tlg, cfg.Database()) - jrm := job.NewORM(db, nil, prm, btORM, nil, tlg, cfg.Database()) + jrm := job.NewORM(db, prm, btORM, nil, tlg, cfg.Database()) err = jrm.InsertJob(&jb) require.NoError(t, err) return jb diff --git a/core/internal/cltest/job_factories.go b/core/internal/cltest/job_factories.go index 77fee125e21..b76d6c7ec2e 100644 --- a/core/internal/cltest/job_factories.go +++ b/core/internal/cltest/job_factories.go @@ -11,13 +11,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/bridges" "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/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) const ( @@ -66,9 +64,7 @@ func getORMs(t *testing.T, db *sqlx.DB) (jobORM job.ORM, pipelineORM pipeline.OR lggr := logger.TestLogger(t) pipelineORM = pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgeORM := bridges.NewORM(db, lggr, config.Database()) - cc := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(cc) - jobORM = job.NewORM(db, legacyChains, pipelineORM, bridgeORM, keyStore, lggr, config.Database()) + jobORM = job.NewORM(db, pipelineORM, bridgeORM, keyStore, lggr, config.Database()) t.Cleanup(func() { jobORM.Close() }) return } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 058c8325b9a..3293066191f 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -237,8 +237,7 @@ observationSource = """ pipelineORM := pipeline.NewORM(app.GetSqlxDB(), logger.TestLogger(t), cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) bridgeORM := bridges.NewORM(app.GetSqlxDB(), logger.TestLogger(t), cfg.Database()) - legacyChains := app.GetRelayers().LegacyEVMChains() - jobORM := job.NewORM(app.GetSqlxDB(), legacyChains, pipelineORM, bridgeORM, app.KeyStore, logger.TestLogger(t), cfg.Database()) + jobORM := job.NewORM(app.GetSqlxDB(), pipelineORM, bridgeORM, app.KeyStore, logger.TestLogger(t), cfg.Database()) runs := cltest.WaitForPipelineComplete(t, 0, jobID, 1, 2, jobORM, 5*time.Second, 300*time.Millisecond) require.Len(t, runs, 1) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 354f0479042..63a9b2696cf 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -251,7 +251,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { sessionORM = sessions.NewORM(db, cfg.WebServer().SessionTimeout().Duration(), globalLogger, cfg.Database(), auditLogger) mercuryORM = mercury.NewORM(db, globalLogger, cfg.Database()) pipelineRunner = pipeline.NewRunner(pipelineORM, bridgeORM, cfg.JobPipeline(), cfg.WebServer(), legacyEVMChains, keyStore.Eth(), keyStore.VRF(), globalLogger, restrictedHTTPClient, unrestrictedHTTPClient) - jobORM = job.NewORM(db, legacyEVMChains, pipelineORM, bridgeORM, keyStore, globalLogger, cfg.Database()) + jobORM = job.NewORM(db, pipelineORM, bridgeORM, keyStore, globalLogger, cfg.Database()) txmORM = txmgr.NewTxStore(db, globalLogger, cfg.Database()) ) diff --git a/core/services/cron/cron_test.go b/core/services/cron/cron_test.go index 19a51a30650..b561248eddb 100644 --- a/core/services/cron/cron_test.go +++ b/core/services/cron/cron_test.go @@ -12,14 +12,12 @@ 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/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/cron" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" pipelinemocks "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) func TestCronV2Pipeline(t *testing.T) { @@ -28,12 +26,10 @@ func TestCronV2Pipeline(t *testing.T) { db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) - relayerExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: cfg, Client: evmtest.NewEthClientMockWithDefaultChain(t), KeyStore: keyStore.Eth()}) lggr := logger.TestLogger(t) orm := pipeline.NewORM(db, lggr, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) btORM := bridges.NewORM(db, lggr, cfg.Database()) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayerExtenders) - jobORM := job.NewORM(db, legacyChains, orm, btORM, keyStore, lggr, cfg.Database()) + jobORM := job.NewORM(db, orm, btORM, keyStore, lggr, cfg.Database()) jb := &job.Job{ Type: job.Cron, diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index e58dbaeb50c..34c79a0afbb 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -88,8 +88,8 @@ func NewDirectRequestUniverseWithConfig(t *testing.T, cfg chainlink.GeneralConfi lggr := logger.TestLogger(t) orm := pipeline.NewORM(db, lggr, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) btORM := bridges.NewORM(db, lggr, cfg.Database()) + jobORM := job.NewORM(db, orm, btORM, keyStore, lggr, cfg.Database()) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := job.NewORM(db, legacyChains, orm, btORM, keyStore, lggr, cfg.Database()) delegate := directrequest.NewDelegate(lggr, runner, orm, legacyChains, mailMon) jb := cltest.MakeDirectRequestJobSpec(t) diff --git a/core/services/feeds/orm_test.go b/core/services/feeds/orm_test.go index 746956bbfcd..3d51ad45fff 100644 --- a/core/services/feeds/orm_test.go +++ b/core/services/feeds/orm_test.go @@ -1656,8 +1656,7 @@ func createJob(t *testing.T, db *sqlx.DB, externalJobID uuid.UUID) *job.Job { bridgeORM = bridges.NewORM(db, lggr, config.Database()) relayExtenders = evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) ) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := job.NewORM(db, legacyChains, pipelineORM, bridgeORM, keyStore, lggr, config.Database()) + orm := job.NewORM(db, pipelineORM, bridgeORM, keyStore, lggr, config.Database()) require.NoError(t, keyStore.OCR().Add(cltest.DefaultOCRKey)) require.NoError(t, keyStore.P2P().Add(cltest.DefaultP2PKey)) @@ -1667,6 +1666,7 @@ func createJob(t *testing.T, db *sqlx.DB, externalJobID uuid.UUID) *job.Job { _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) jb, err := ocr.ValidatedOracleSpecToml(legacyChains, testspecs.GenerateOCRSpec(testspecs.OCRSpecParams{ JobID: externalJobID.String(), diff --git a/core/services/fluxmonitorv2/orm_test.go b/core/services/fluxmonitorv2/orm_test.go index 0bb08032617..6e06a1e65b8 100644 --- a/core/services/fluxmonitorv2/orm_test.go +++ b/core/services/fluxmonitorv2/orm_test.go @@ -17,13 +17,11 @@ 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/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/fluxmonitorv2" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -96,11 +94,9 @@ func TestORM_UpdateFluxMonitorRoundStats(t *testing.T) { pipelineORM := pipeline.NewORM(db, lggr, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) bridgeORM := bridges.NewORM(db, lggr, cfg.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{GeneralConfig: cfg, DB: db, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) // Instantiate a real job ORM because we need to create a job to satisfy // a check in pipeline.CreateRun - jobORM := job.NewORM(db, legacyChains, pipelineORM, bridgeORM, keyStore, lggr, cfg.Database()) + jobORM := job.NewORM(db, pipelineORM, bridgeORM, keyStore, lggr, cfg.Database()) orm := newORM(t, db, cfg.Database(), nil) address := testutils.NewAddress() diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 74416e68dce..6306dedcefa 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -83,9 +83,7 @@ func TestORM(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: ethKeyStore}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) borm := bridges.NewORM(db, logger.TestLogger(t), config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) @@ -331,9 +329,7 @@ func TestORM_DeleteJob_DeletesAssociatedRecords(t *testing.T) { lggr := logger.TestLogger(t) pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) scopedConfig := evmtest.NewChainScopedConfig(t, config) korm := keeper.NewORM(db, logger.TestLogger(t), scopedConfig.Database()) @@ -342,6 +338,8 @@ func TestORM_DeleteJob_DeletesAssociatedRecords(t *testing.T) { _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) jb, err := ocr.ValidatedOracleSpecToml(legacyChains, testspecs.GenerateOCRSpec(testspecs.OCRSpecParams{ TransmitterAddress: address.Hex(), DS1BridgeName: bridge.Name.String(), @@ -431,10 +429,8 @@ func TestORM_CreateJob_VRFV2(t *testing.T) { lggr := logger.TestLogger(t) pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) fromAddresses := []string{cltest.NewEIP55Address().String(), cltest.NewEIP55Address().String()} jb, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec( @@ -514,9 +510,7 @@ func TestORM_CreateJob_VRFV2Plus(t *testing.T) { lggr := logger.TestLogger(t) pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - cc := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(cc) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) fromAddresses := []string{cltest.NewEIP55Address().String(), cltest.NewEIP55Address().String()} jb, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec( @@ -599,9 +593,7 @@ func TestORM_CreateJob_OCRBootstrap(t *testing.T) { lggr := logger.TestLogger(t) pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jb, err := ocrbootstrap.ValidatedBootstrapSpecToml(testspecs.GetOCRBootstrapSpec()) require.NoError(t, err) @@ -628,9 +620,7 @@ func TestORM_CreateJob_EVMChainID_Validation(t *testing.T) { pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) t.Run("evm chain id validation for ocr works", func(t *testing.T) { jb := job.Job{ @@ -725,9 +715,7 @@ func TestORM_CreateJob_OCR_DuplicatedContractAddress(t *testing.T) { pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) // defaultChainID is deprecated defaultChainID := customChainID @@ -745,6 +733,8 @@ func TestORM_CreateJob_OCR_DuplicatedContractAddress(t *testing.T) { TransmitterAddress: address.Hex(), JobID: externalJobID.UUID.String(), }) + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) jb, err := ocr.ValidatedOracleSpecToml(legacyChains, spec.Toml()) require.NoError(t, err) @@ -794,9 +784,7 @@ func TestORM_CreateJob_OCR2_DuplicatedContractAddress(t *testing.T) { pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) @@ -857,10 +845,7 @@ func TestORM_CreateJob_OCR2_Sending_Keys_Transmitter_Keys_Validations(t *testing pipelineORM := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - require.True(t, relayExtenders.Len() > 0) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jb, err := ocr2validate.ValidatedOracleSpecToml(config.OCR2(), config.Insecure(), testspecs.GetOCR2EVMSpecMinimal()) require.NoError(t, err) @@ -974,14 +959,15 @@ func Test_FindJobs(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) jb1, err := ocr.ValidatedOracleSpecToml(legacyChains, testspecs.GenerateOCRSpec(testspecs.OCRSpecParams{ JobID: uuid.New().String(), @@ -1054,9 +1040,8 @@ func Test_FindJob(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) @@ -1065,6 +1050,8 @@ func Test_FindJob(t *testing.T) { // Must uniquely name the OCR Specs to properly insert a new job in the job table. externalJobID := uuid.New() _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) job, err := ocr.ValidatedOracleSpecToml(legacyChains, testspecs.GenerateOCRSpec(testspecs.OCRSpecParams{ JobID: externalJobID.String(), @@ -1232,9 +1219,7 @@ func Test_FindJobsByPipelineSpecIDs(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jb, err := directrequest.ValidatedDirectRequestSpec(testspecs.GetDirectRequestSpec()) require.NoError(t, err) @@ -1263,20 +1248,7 @@ func Test_FindJobsByPipelineSpecIDs(t *testing.T) { }) t.Run("with chainID disabled", func(t *testing.T) { - newCfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.EVM[0] = &evmcfg.EVMConfig{ - ChainID: utils.NewBigI(0), - Enabled: ptr(false), - } - c.EVM = append(c.EVM, &evmcfg.EVMConfig{ - ChainID: utils.NewBigI(123123123), - Enabled: ptr(true), - }) - }) - relayExtenders2 := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: newCfg, KeyStore: keyStore.Eth()}) - legacyChains2 := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders2) - - orm2 := NewTestORM(t, db, legacyChains2, pipelineORM, bridgesORM, keyStore, config.Database()) + orm2 := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jbs, err2 := orm2.FindJobsByPipelineSpecIDs([]int32{jb.PipelineSpecID}) require.NoError(t, err2) @@ -1297,7 +1269,7 @@ func Test_FindPipelineRuns(t *testing.T) { bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) @@ -1358,7 +1330,7 @@ func Test_PipelineRunsByJobID(t *testing.T) { bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) @@ -1419,7 +1391,7 @@ func Test_FindPipelineRunIDsByJobID(t *testing.T) { bridgesORM := bridges.NewORM(db, lggr, config.Database()) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, address := cltest.MustInsertRandomKey(t, keyStore.Eth()) @@ -1527,7 +1499,7 @@ func Test_FindPipelineRunsByIDs(t *testing.T) { bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) @@ -1583,9 +1555,7 @@ func Test_FindPipelineRunByID(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jb, err := directrequest.ValidatedDirectRequestSpec(testspecs.GetDirectRequestSpec()) require.NoError(t, err) @@ -1628,9 +1598,7 @@ func Test_FindJobWithoutSpecErrors(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jb, err := directrequest.ValidatedDirectRequestSpec(testspecs.GetDirectRequestSpec()) require.NoError(t, err) @@ -1667,9 +1635,7 @@ func Test_FindSpecErrorsByJobIDs(t *testing.T) { pipelineORM := pipeline.NewORM(db, logger.TestLogger(t), config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) jb, err := directrequest.ValidatedDirectRequestSpec(testspecs.GetDirectRequestSpec()) require.NoError(t, err) @@ -1705,7 +1671,7 @@ func Test_CountPipelineRunsByJobID(t *testing.T) { bridgesORM := bridges.NewORM(db, logger.TestLogger(t), config.Database()) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - orm := NewTestORM(t, db, legacyChains, pipelineORM, bridgesORM, keyStore, config.Database()) + orm := NewTestORM(t, db, pipelineORM, bridgesORM, keyStore, config.Database()) _, bridge := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) _, bridge2 := cltest.MustCreateBridge(t, db, cltest.BridgeOpts{}, config.Database()) diff --git a/core/services/job/job_pipeline_orm_integration_test.go b/core/services/job/job_pipeline_orm_integration_test.go index 1158fc46260..a2b6cc4618c 100644 --- a/core/services/job/job_pipeline_orm_integration_test.go +++ b/core/services/job/job_pipeline_orm_integration_test.go @@ -156,7 +156,7 @@ func TestPipelineORM_Integration(t *testing.T) { legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) runner := pipeline.NewRunner(orm, btORM, config.JobPipeline(), cfg.WebServer(), legacyChains, nil, nil, lggr, nil, nil) - jobORM := NewTestORM(t, db, legacyChains, orm, btORM, keyStore, cfg.Database()) + jobORM := NewTestORM(t, db, orm, btORM, keyStore, cfg.Database()) dbSpec := makeVoterTurnoutOCRJobSpec(t, transmitterAddress, bridge.Name.String(), bridge2.Name.String()) diff --git a/core/services/job/orm.go b/core/services/job/orm.go index 372b2fe74a1..c6ec4ed5c8e 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -21,8 +21,6 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -85,35 +83,25 @@ type ORMConfig interface { } type orm struct { - q pg.Q - legacyChains evm.LegacyChainContainer - keyStore keystore.Master - pipelineORM pipeline.ORM - lggr logger.SugaredLogger - cfg pg.QConfig - bridgeORM bridges.ORM + q pg.Q + keyStore keystore.Master + pipelineORM pipeline.ORM + lggr logger.SugaredLogger + cfg pg.QConfig + bridgeORM bridges.ORM } var _ ORM = (*orm)(nil) -func NewORM( - db *sqlx.DB, - legacyChains evm.LegacyChainContainer, - pipelineORM pipeline.ORM, - bridgeORM bridges.ORM, - keyStore keystore.Master, // needed to validation key properties on new job creation - lggr logger.Logger, - cfg pg.QConfig, -) *orm { +func NewORM(db *sqlx.DB, pipelineORM pipeline.ORM, bridgeORM bridges.ORM, keyStore keystore.Master, lggr logger.Logger, cfg pg.QConfig) *orm { namedLogger := logger.Sugared(lggr.Named("JobORM")) return &orm{ - q: pg.NewQ(db, namedLogger, cfg), - legacyChains: legacyChains, - keyStore: keyStore, - pipelineORM: pipelineORM, - bridgeORM: bridgeORM, - lggr: namedLogger, - cfg: cfg, + q: pg.NewQ(db, namedLogger, cfg), + keyStore: keyStore, + pipelineORM: pipelineORM, + bridgeORM: bridgeORM, + lggr: namedLogger, + cfg: cfg, } } func (o *orm) Close() error { @@ -704,29 +692,12 @@ func (o *orm) FindJobs(offset, limit int) (jobs []Job, count int, err error) { if err != nil { return err } - for i := range jobs { - err = multierr.Combine(err, o.LoadConfigVars(&jobs[i])) - } + return nil }) return jobs, int(count), err } -func (o *orm) LoadConfigVars(jb *Job) error { - if jb.OCROracleSpec != nil { - ch, err := o.legacyChains.Get(jb.OCROracleSpec.EVMChainID.String()) - if err != nil { - return err - } - newSpec, err := LoadConfigVarsOCR(ch.Config().EVM().OCR(), ch.Config().OCR(), *jb.OCROracleSpec) - if err != nil { - return err - } - jb.OCROracleSpec = newSpec - } - return nil -} - func LoadDefaultVRFPollPeriod(vrfs VRFSpec) *VRFSpec { if vrfs.PollPeriod == 0 { vrfs.PollPeriod = 5 * time.Second @@ -842,7 +813,7 @@ func (o *orm) FindJobWithoutSpecErrors(id int32) (jb Job, err error) { return jb, errors.Wrap(err, "FindJobWithoutSpecErrors failed") } - return jb, o.LoadConfigVars(&jb) + return jb, nil } // FindSpecErrorsByJobIDs returns all jobs spec errors by jobs IDs @@ -931,7 +902,7 @@ func (o *orm) findJob(jb *Job, col string, arg interface{}, qopts ...pg.QOpt) er if err != nil { return errors.Wrap(err, "findJob failed") } - return o.LoadConfigVars(jb) + return nil } func (o *orm) FindJobIDsWithBridge(name string) (jids []int32, err error) { @@ -1174,13 +1145,6 @@ func (o *orm) FindJobsByPipelineSpecIDs(ids []int32) ([]Job, error) { if err != nil { return err } - for i := range jbs { - err = o.LoadConfigVars(&jbs[i]) - //We must return the jobs even if the chainID is disabled - if err != nil && !errors.Is(err, chains.ErrNoSuchChainID) { - return err - } - } return nil }) diff --git a/core/services/job/orm_test.go b/core/services/job/orm_test.go index cd437147b4f..48805388a36 100644 --- a/core/services/job/orm_test.go +++ b/core/services/job/orm_test.go @@ -9,7 +9,6 @@ import ( "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "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" @@ -22,8 +21,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/models" ) -func NewTestORM(t *testing.T, db *sqlx.DB, legacyChains evm.LegacyChainContainer, pipelineORM pipeline.ORM, bridgeORM bridges.ORM, keyStore keystore.Master, cfg pg.QConfig) job.ORM { - o := job.NewORM(db, legacyChains, pipelineORM, bridgeORM, keyStore, logger.TestLogger(t), cfg) +func NewTestORM(t *testing.T, db *sqlx.DB, pipelineORM pipeline.ORM, bridgeORM bridges.ORM, keyStore keystore.Master, cfg pg.QConfig) job.ORM { + o := job.NewORM(db, pipelineORM, bridgeORM, keyStore, logger.TestLogger(t), cfg) t.Cleanup(func() { o.Close() }) return o } diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index c0fff1e560a..7e8ed5e87f2 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -85,7 +85,7 @@ func TestRunner(t *testing.T) { c := clhttptest.NewTestLocalOnlyHTTPClient() runner := pipeline.NewRunner(pipelineORM, btORM, config.JobPipeline(), config.WebServer(), legacyChains, nil, nil, logger.TestLogger(t), c, c) - jobORM := NewTestORM(t, db, legacyChains, pipelineORM, btORM, keyStore, config.Database()) + jobORM := NewTestORM(t, db, pipelineORM, btORM, keyStore, config.Database()) _, placeHolderAddress := cltest.MustInsertRandomKey(t, keyStore.Eth()) @@ -428,45 +428,7 @@ answer1 [type=median index=0]; } }) - t.Run("missing required env vars", func(t *testing.T) { - s := ` - type = "offchainreporting" - schemaVersion = 1 - contractAddress = "%s" - isBootstrapPeer = false - evmChainID = "0" - observationSource = """ -ds1 [type=http method=GET url="%s" allowunrestrictednetworkaccess="true" %s]; -ds1_parse [type=jsonparse path="USD" lax=true]; -ds1 -> ds1_parse; -""" -` - s = fmt.Sprintf(s, cltest.NewEIP55Address(), "http://blah.com", "") - jb, err := ocr.ValidatedOracleSpecToml(legacyChains, s) - require.NoError(t, err) - err = toml.Unmarshal([]byte(s), &jb) - require.NoError(t, err) - jb.MaxTaskDuration = models.Interval(cltest.MustParseDuration(t, "1s")) - err = jobORM.CreateJob(&jb) - require.NoError(t, err) - sd := ocr.NewDelegate( - db, - jobORM, - keyStore, - nil, - nil, - nil, - legacyChains, - logger.TestLogger(t), - config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), - ) - _, err = sd.ServicesForSpec(jb) - // We expect this to fail as neither the required vars are not set either via the env nor the job itself. - require.Error(t, err) - }) - - t.Run("use env for minimal bootstrap", func(t *testing.T) { + t.Run("minimal bootstrap", func(t *testing.T) { s := ` type = "offchainreporting" schemaVersion = 1 @@ -504,53 +466,6 @@ ds1 -> ds1_parse; require.NoError(t, err) }) - t.Run("use env for minimal non-bootstrap", func(t *testing.T) { - s := ` - type = "offchainreporting" - schemaVersion = 1 - contractAddress = "%s" - isBootstrapPeer = false - observationTimeout = "15s" - evmChainID = "0" - observationSource = """ -ds1 [type=http method=GET url="%s" allowunrestrictednetworkaccess="true" %s]; -ds1_parse [type=jsonparse path="USD" lax=true]; -ds1 -> ds1_parse; -""" -` - s = fmt.Sprintf(s, cltest.NewEIP55Address(), "http://blah.com", "") - jb, err := ocr.ValidatedOracleSpecToml(legacyChains, s) - require.NoError(t, err) - err = toml.Unmarshal([]byte(s), &jb) - require.NoError(t, err) - jb.MaxTaskDuration = models.Interval(cltest.MustParseDuration(t, "1s")) - err = jobORM.CreateJob(&jb) - require.NoError(t, err) - // Assert the override - assert.Equal(t, jb.OCROracleSpec.ObservationTimeout, models.Interval(cltest.MustParseDuration(t, "15s"))) - // Assert that this is default - assert.Equal(t, models.Interval(20000000000), jb.OCROracleSpec.BlockchainTimeout) - assert.Equal(t, models.Interval(cltest.MustParseDuration(t, "1s")), jb.MaxTaskDuration) - - lggr := logger.TestLogger(t) - pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) - require.NoError(t, pw.Start(testutils.Context(t))) - sd := ocr.NewDelegate( - db, - jobORM, - keyStore, - nil, - pw, - monitoringEndpoint, - legacyChains, - lggr, - config.Database(), - srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), - ) - _, err = sd.ServicesForSpec(jb) - require.NoError(t, err) - }) - t.Run("test min non-bootstrap", func(t *testing.T) { kb, err := keyStore.OCR().Create() require.NoError(t, err) @@ -765,9 +680,6 @@ func TestRunner_Success_Callback_AsyncJob(t *testing.T) { }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient, cltest.UseRealExternalInitiatorManager) - keyStore := cltest.NewKeyStore(t, app.GetSqlxDB(), pgtest.NewQConfig(true)) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: app.GetSqlxDB(), Client: ethClient, GeneralConfig: cfg, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) require.NoError(t, app.Start(testutils.Context(t))) var ( @@ -898,7 +810,7 @@ func TestRunner_Success_Callback_AsyncJob(t *testing.T) { pipelineORM := pipeline.NewORM(app.GetSqlxDB(), logger.TestLogger(t), cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(app.GetSqlxDB(), logger.TestLogger(t), cfg.Database()) - jobORM := NewTestORM(t, app.GetSqlxDB(), legacyChains, pipelineORM, bridgesORM, app.KeyStore, cfg.Database()) + jobORM := NewTestORM(t, app.GetSqlxDB(), pipelineORM, bridgesORM, app.KeyStore, cfg.Database()) // Trigger v2/resume select { @@ -947,10 +859,6 @@ func TestRunner_Error_Callback_AsyncJob(t *testing.T) { }) app := cltest.NewApplicationWithConfig(t, cfg, ethClient, cltest.UseRealExternalInitiatorManager) - keyStore := cltest.NewKeyStore(t, app.GetSqlxDB(), pgtest.NewQConfig(true)) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: app.GetSqlxDB(), Client: ethClient, GeneralConfig: cfg, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - require.NoError(t, app.Start(testutils.Context(t))) var ( @@ -1079,7 +987,7 @@ func TestRunner_Error_Callback_AsyncJob(t *testing.T) { pipelineORM := pipeline.NewORM(app.GetSqlxDB(), logger.TestLogger(t), cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) bridgesORM := bridges.NewORM(app.GetSqlxDB(), logger.TestLogger(t), cfg.Database()) - jobORM := NewTestORM(t, app.GetSqlxDB(), legacyChains, pipelineORM, bridgesORM, app.KeyStore, cfg.Database()) + jobORM := NewTestORM(t, app.GetSqlxDB(), pipelineORM, bridgesORM, app.KeyStore, cfg.Database()) // Trigger v2/resume select { diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index be4a480a6c9..1f10a86e9ce 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/bridges" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -97,7 +98,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) t.Run("should respect its dependents", func(t *testing.T) { lggr := logger.TestLogger(t) - orm := NewTestORM(t, db, legacyChains, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) + orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) a := utils.NewDependentAwaiter() a.AddDependents(1) spawner := job.NewSpawner(orm, config.Database(), noopChecker{}, map[job.Type]job.Delegate{}, db, lggr, []utils.DependentAwaiter{a}) @@ -120,7 +121,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { jobB := makeOCRJobSpec(t, address, bridge.Name.String(), bridge2.Name.String()) lggr := logger.TestLogger(t) - orm := NewTestORM(t, db, legacyChains, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) + orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) eventuallyA := cltest.NewAwaiter() serviceA1 := mocks.NewServiceCtx(t) @@ -185,7 +186,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { serviceA2.On("Start", mock.Anything).Return(nil).Once().Run(func(mock.Arguments) { eventually.ItHappened() }) lggr := logger.TestLogger(t) - orm := NewTestORM(t, db, legacyChains, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) + orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} @@ -219,7 +220,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { serviceA2.On("Start", mock.Anything).Return(nil).Once().Run(func(mock.Arguments) { eventuallyStart.ItHappened() }) lggr := logger.TestLogger(t) - orm := NewTestORM(t, db, legacyChains, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) + orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) d := ocr.NewDelegate(nil, orm, nil, nil, nil, monitoringEndpoint, legacyChains, logger.TestLogger(t), config.Database(), mailMon) delegateA := &delegate{jobA.Type, []job.ServiceCtx{serviceA1, serviceA2}, 0, nil, d} @@ -297,7 +298,7 @@ func TestSpawner_CreateJobDeleteJob(t *testing.T) { jobOCR2VRF := makeOCR2VRFJobSpec(t, keyStore, config, address, chain.ID(), 2) - orm := NewTestORM(t, db, legacyChains, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) + orm := NewTestORM(t, db, pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()), bridges.NewORM(db, lggr, config.Database()), keyStore, config.Database()) mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) processConfig := plugins.NewRegistrarConfig(loop.GRPCOpts{}, func(name string) (*plugins.RegisteredLoop, error) { return nil, nil }) diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index a487c231fb8..f916c24f0a6 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -17,14 +17,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "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/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" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -522,9 +520,7 @@ func Test_GetUnfinishedRuns_Keepers(t *testing.T) { porm := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgeORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jorm := job.NewORM(db, legacyChains, porm, bridgeORM, keyStore, lggr, config.Database()) + jorm := job.NewORM(db, porm, bridgeORM, keyStore, lggr, config.Database()) defer func() { assert.NoError(t, jorm.Close()) }() timestamp := time.Now() @@ -624,9 +620,7 @@ func Test_GetUnfinishedRuns_DirectRequest(t *testing.T) { porm := pipeline.NewORM(db, lggr, config.Database(), config.JobPipeline().MaxSuccessfulRuns()) bridgeORM := bridges.NewORM(db, lggr, config.Database()) - relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: config, KeyStore: keyStore.Eth()}) - legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jorm := job.NewORM(db, legacyChains, porm, bridgeORM, keyStore, lggr, config.Database()) + jorm := job.NewORM(db, porm, bridgeORM, keyStore, lggr, config.Database()) defer func() { assert.NoError(t, jorm.Close()) }() timestamp := time.Now() diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 38b361716b6..91ae4400e39 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -78,10 +78,10 @@ func buildVrfUni(t *testing.T, db *sqlx.DB, cfg chainlink.GeneralConfig) vrfUniv btORM := bridges.NewORM(db, lggr, cfg.Database()) txm := txmmocks.NewMockEvmTxManager(t) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg.Database()) + jrm := job.NewORM(db, prm, btORM, ks, lggr, cfg.Database()) + t.Cleanup(func() { assert.NoError(t, jrm.Close()) }) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{LogBroadcaster: lb, KeyStore: ks.Eth(), Client: ec, DB: db, GeneralConfig: cfg, TxManager: txm}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) - jrm := job.NewORM(db, legacyChains, prm, btORM, ks, lggr, cfg.Database()) - t.Cleanup(func() { jrm.Close() }) pr := pipeline.NewRunner(prm, btORM, cfg.JobPipeline(), cfg.WebServer(), legacyChains, ks.Eth(), ks.VRF(), lggr, nil, nil) require.NoError(t, ks.Unlock(testutils.Password)) k, err := ks.Eth().Create(testutils.FixtureChainID) From 96567e127d72f3f58c7a3e75dfdd1a69a6c316d4 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Thu, 2 Nov 2023 20:35:04 +0400 Subject: [PATCH 057/271] add sepolia-arbitrum to automation benchmark test (#11149) * add sepolia-arbitrum to automation benchmark test * add back P2P.V2 Enabled * fix switch case in DeployKeeperRegistry --- .github/workflows/automation-benchmark-tests.yml | 1 + integration-tests/benchmark/keeper_test.go | 11 +++++++++-- integration-tests/contracts/contract_deployer.go | 14 ++++++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 5c4dced9342..d78e5e76ac6 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -25,6 +25,7 @@ on: - MUMBAI - SEPOLIA - BASE_GOERLI + - ARBITRUM_SEPOLIA TestInputs: description: TestInputs required: false diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 55f769e73b4..25a147fbf0b 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -37,6 +37,7 @@ Enabled = true [P2P] [P2P.V2] +Enabled = true AnnounceAddresses = ["0.0.0.0:6690"] ListenAddresses = ["0.0.0.0:6690"] [Keeper] @@ -242,13 +243,13 @@ func repeatRegistries(registryVersion eth_contracts.KeeperRegistryVersion, numbe var networkConfig = map[string]NetworkConfig{ "SimulatedGeth": { - upkeepSLA: int64(20), + upkeepSLA: int64(120), //2 minutes blockTime: time.Second, deltaStage: 30 * time.Second, funding: big.NewFloat(100_000), }, "geth": { - upkeepSLA: int64(20), + upkeepSLA: int64(120), //2 minutes blockTime: time.Second, deltaStage: 30 * time.Second, funding: big.NewFloat(100_000), @@ -289,6 +290,12 @@ var networkConfig = map[string]NetworkConfig{ deltaStage: 20 * time.Second, funding: big.NewFloat(ChainlinkNodeFunding), }, + "ArbitrumSepolia": { + upkeepSLA: int64(120), + blockTime: time.Second, + deltaStage: 20 * time.Second, + funding: big.NewFloat(ChainlinkNodeFunding), + }, } func getEnv(key, fallback string) string { diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 94f6c733869..e203d8318f2 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -876,14 +876,20 @@ func (e *EthereumContractDeployer) DeployKeeperRegistry( opts *KeeperRegistryOpts, ) (KeeperRegistry, error) { var mode uint8 - switch e.client.GetChainID() { + switch e.client.GetChainID().Int64() { //Arbitrum payment model - case big.NewInt(421613): + //Goerli Arbitrum + case 421613: + mode = uint8(1) + //Sepolia Arbitrum + case 421614: mode = uint8(1) //Optimism payment model - case big.NewInt(420): + //Goerli Optimism + case 420: mode = uint8(2) - case big.NewInt(84531): + //Goerli Base + case 84531: mode = uint8(2) default: mode = uint8(0) From 388b267e1776262008a3488b656620f1f776742c Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Thu, 2 Nov 2023 13:20:09 -0400 Subject: [PATCH 058/271] debug script improvements (#11152) --- core/scripts/chaincli/handler/debug.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 7cf801d3326..daf012ee16e 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -275,13 +275,21 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to execute mercury callback ", err) } + if callbackResult.UpkeepFailureReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) + } upkeepNeeded, performData = callbackResult.UpkeepNeeded, callbackResult.PerformData - // do tenderly simulation + // do tenderly simulations rawCall, err := core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.extraData) if err != nil { - failUnknown("failed to pack raw checkUpkeep call", err) + 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) + if err != nil { + failUnknown("failed to pack raw checkCallback (direct) call", err) + } + addLink("checkCallback (direct) simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, upkeepInfo.Target)) } else { message("did not revert with StreamsLookup error") } From 206fb8b3ab4dc96bb55788bc111abd54939ea040 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:36:28 -0400 Subject: [PATCH 059/271] Avoid rate limiting when pulling public docker hub images (#11153) --- .../build-sign-publish-chainlink/action.yml | 22 +++++++++++++++++++ .github/workflows/build-publish-develop.yml | 2 ++ .github/workflows/build-publish.yml | 12 +++++----- .github/workflows/build.yml | 5 +++-- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml index 55c682bc8d9..fe4ef858f58 100644 --- a/.github/actions/build-sign-publish-chainlink/action.yml +++ b/.github/actions/build-sign-publish-chainlink/action.yml @@ -13,6 +13,12 @@ inputs: description: Path to the Dockerfile (relative to the repo root) default: core/chainlink.Dockerfile required: false + dockerhub_username: + description: Username for Docker Hub to avoid rate limits when pulling public images + required: false + dockerhub_password: + description: Password for Docker Hub to avoid rate limits when pulling public images + required: false ecr-hostname: description: The ECR registry scope default: public.ecr.aws @@ -126,6 +132,14 @@ runs: type=semver,pattern={{version}},suffix=${{ inputs.ecr-tag-suffix }}-root type=sha,format=short,suffix=${{ inputs.ecr-tag-suffix }}-root + # To avoid rate limiting from Docker Hub, we login with a paid user account. + - name: Login to Docker Hub + if: inputs.dockerhub_username && inputs.dockerhub_password + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + username: ${{ inputs.dockerhub_username }} + password: ${{ inputs.dockerhub_password }} + - name: Build and push root docker image id: buildpush-root uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 @@ -159,6 +173,14 @@ runs: images: ${{ env.shared-images }} tags: ${{ env.shared-tag-list }} + # To avoid rate limiting from Docker Hub, we login with a paid user account. + - name: Login to Docker Hub + if: inputs.dockerhub_username && inputs.dockerhub_password + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + username: ${{ inputs.dockerhub_username }} + password: ${{ inputs.dockerhub_password }} + - name: Build and push non-root docker image id: buildpush-nonroot uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 diff --git a/.github/workflows/build-publish-develop.yml b/.github/workflows/build-publish-develop.yml index 076fdf817df..b8859722378 100644 --- a/.github/workflows/build-publish-develop.yml +++ b/.github/workflows/build-publish-develop.yml @@ -52,6 +52,8 @@ jobs: ecr-image-name: chainlink ecr-tag-suffix: ${{ matrix.image.tag-suffix }} dockerfile: ${{ matrix.image.dockerfile }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} git-commit-sha: ${{ steps.git-ref.outputs.checked-out || github.sha }} - name: Collect Metrics if: always() diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 4d5a42a369f..1bda6957a2a 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -1,17 +1,17 @@ -name: 'Build Chainlink and Publish' +name: "Build Chainlink and Publish" on: # Mimics old circleci behaviour push: tags: - - 'v*' + - "v*" branches: - master - - 'release/**' + - "release/**" jobs: checks: - name: 'Checks' + name: "Checks" runs-on: ubuntu-20.04 steps: - name: Checkout repository @@ -42,10 +42,12 @@ jobs: aws-role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS }} aws-region: ${{ secrets.AWS_REGION }} sign-images: true - sign-method: 'keypair' + sign-method: "keypair" cosign-private-key: ${{ secrets.COSIGN_PRIVATE_KEY }} cosign-public-key: ${{ secrets.COSIGN_PUBLIC_KEY }} cosign-password: ${{ secrets.COSIGN_PASSWORD }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} verify-signature: true - name: Collect Metrics if: always() diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f9a8ea8b35..6282e2168d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: 'Build Chainlink' +name: "Build Chainlink" on: pull_request: @@ -7,7 +7,6 @@ on: - master jobs: - build-chainlink: runs-on: ubuntu-20.04 steps: @@ -17,6 +16,8 @@ jobs: - name: Build chainlink image uses: ./.github/actions/build-sign-publish-chainlink with: + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} publish: false sign-images: false - name: Collect Metrics From a9d27aa2ccdebcb8b79af9ddf4d42d7f1fab6d9e Mon Sep 17 00:00:00 2001 From: David Cauchi <13139524+davidcauchi@users.noreply.github.com> Date: Thu, 2 Nov 2023 19:40:47 +0100 Subject: [PATCH 060/271] Remove duplicate table (#11151) --- integration-tests/config/config.go | 1 - 1 file changed, 1 deletion(-) diff --git a/integration-tests/config/config.go b/integration-tests/config/config.go index 44c108b0d7f..1da8254e0ed 100644 --- a/integration-tests/config/config.go +++ b/integration-tests/config/config.go @@ -8,7 +8,6 @@ Enabled = true [P2P.V2] Enabled = false -[P2P] [P2P.V1] Enabled = true ListenIP = '0.0.0.0' From 618d06b3adf412ad5e3eea2fab39177ed8505c15 Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 2 Nov 2023 13:42:06 -0600 Subject: [PATCH 061/271] [TT-668] E2E Dockerhub Login Rate Limit Issue Fix (#11155) --- .../workflows/automation-benchmark-tests.yml | 2 +- .../workflows/automation-ondemand-tests.yml | 2 +- .github/workflows/integration-chaos-tests.yml | 2 +- .github/workflows/integration-tests.yml | 28 ++++++++++++------- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- .../on-demand-vrfv2plus-performance-test.yml | 2 +- .github/workflows/performance-tests.yml | 2 +- 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index d78e5e76ac6..7bdb66c919e 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -109,7 +109,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: DETACH_RUNNER: true TEST_SUITE: benchmark diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index fb8adcfdb6f..88c2c126dc6 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -172,7 +172,7 @@ jobs: echo "version=${{ inputs.chainlinkVersionUpdate }}" >>$GITHUB_OUTPUT fi - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: PYROSCOPE_SERVER: ${{ matrix.tests.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.tests.pyroscope_env }} diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index 4ad985e9154..892a43e76f0 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -109,7 +109,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 11 ./chaos 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b66ab58d554..445a0277315 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -212,7 +212,7 @@ jobs: ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} @@ -223,6 +223,8 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} artifacts_location: ./integration-tests/smoke/logs/ publish_check_name: ${{ matrix.product.name }} token: ${{ secrets.GITHUB_TOKEN }} @@ -231,7 +233,7 @@ jobs: cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + QA_KUBECONFIG: "" - name: Collect Metrics if: always() id: collect-gha-metrics @@ -411,7 +413,7 @@ jobs: ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} @@ -422,6 +424,8 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} artifacts_name: ${{ matrix.product.name }}-test-logs artifacts_location: ./integration-tests/smoke/logs/ publish_check_name: ${{ matrix.product.name }} @@ -431,11 +435,11 @@ jobs: cache_restore_only: "true" QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + QA_KUBECONFIG: "" ## Run this step when changes that do not need the test to run are made - name: Run Setup if: needs.changes.outputs.src == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download go_mod_path: ./integration-tests/go.mod @@ -572,7 +576,7 @@ jobs: run: | echo "Running migration tests from version '${{ steps.get_latest_version.outputs.latest_version }}' to: '${{ github.sha }}'" - name: Run Migration Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -817,12 +821,14 @@ jobs: ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Run Setup if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: go_mod_path: ./integration-tests/go.mod cache_restore_only: true cache_key_id: core-solana-e2e-${{ env.MOD_CACHE_VERSION }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} @@ -844,7 +850,7 @@ jobs: docker rm "$CONTAINER_ID" - name: Run Tests if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: export ENV_JOB_IMAGE=${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-solana-tests:${{ needs.get_solana_sha.outputs.sha }} && make test_smoke cl_repo: ${{ env.CHAINLINK_IMAGE }} @@ -857,7 +863,7 @@ jobs: aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + QA_KUBECONFIG: "" run_setup: false - name: Upload test log uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 @@ -929,7 +935,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} ## Only run OCR smoke test for now - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-ocr-evm-${{ matrix.testnet }} # TODO: Only for OCR for now @@ -940,6 +946,8 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} artifacts_location: ./integration-tests/smoke/logs publish_check_name: ${{ matrix.testnet }} OCR Smoke Test Results token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 1fb79d8ccd4..4a18aabf226 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -129,7 +129,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: DETACH_RUNNER: true TEST_SUITE: soak diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index deb977e43fc..c51f7f5a2fb 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -118,7 +118,7 @@ jobs: with: fetch-depth: 0 - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 6h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 87fb75beca8..57907fe6c2d 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -57,7 +57,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 with: test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 10 ./performance 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: make gomod From 1208fb3d3dec36876dfd74a9a05db437840ce9fc Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:05:10 -0500 Subject: [PATCH 062/271] Remove direct TXM DB reads from VRF (#10987) * Removed direct TXM DB reads from VRF * Addressed PR feedback * Added tx deduping to vrf respCount method * Fixed linting * Fixed failing test --------- Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- common/txmgr/mocks/tx_manager.go | 104 ++++++ common/txmgr/txmgr.go | 40 +++ common/txmgr/types/mocks/tx_store.go | 105 ++++++ common/txmgr/types/tx_store.go | 8 + .../evm/client/simulated_backend_client.go | 3 +- core/chains/evm/txmgr/evm_tx_store.go | 79 ++++- core/chains/evm/txmgr/mocks/evm_tx_store.go | 104 ++++++ core/chains/evm/txmgr/test_helpers.go | 151 ++++++++ core/chains/evm/txmgr/txmgr_test.go | 177 ++-------- core/services/vrf/delegate.go | 137 +------- core/services/vrf/delegate_test.go | 36 +- core/services/vrf/v1/listener_v1.go | 103 ++++-- .../vrf/v2/integration_helpers_test.go | 28 +- .../vrf/v2/integration_v2_plus_test.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 169 ++++----- core/services/vrf/v2/listener_v2.go | 222 +++++++----- .../vrf/v2/listener_v2_helpers_test.go | 22 ++ core/services/vrf/v2/listener_v2_test.go | 329 +++++++++++------- core/services/vrf/v2/listener_v2_types.go | 6 +- core/services/vrf/vrfcommon/utils.go | 78 +++++ core/web/jobs_controller_test.go | 14 +- 21 files changed, 1265 insertions(+), 652 deletions(-) create mode 100644 core/chains/evm/txmgr/test_helpers.go create mode 100644 core/services/vrf/vrfcommon/utils.go diff --git a/common/txmgr/mocks/tx_manager.go b/common/txmgr/mocks/tx_manager.go index c01f182c9bd..89abf1dea51 100644 --- a/common/txmgr/mocks/tx_manager.go +++ b/common/txmgr/mocks/tx_manager.go @@ -59,6 +59,110 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Create return r0, r1 } +// FindTxesByMetaFieldAndStates provides a mock function with given fields: ctx, metaField, metaValue, states, chainID +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, metaField, metaValue, states, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, metaField, metaValue, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, metaField, metaValue, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) error); ok { + r1 = rf(ctx, metaField, metaValue, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithAttemptsAndReceiptsByIdsAndState provides a mock function with given fields: ctx, ids, states, chainID +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, ids, states, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, ids, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, ids, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) error); ok { + r1 = rf(ctx, ids, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithMetaFieldByReceiptBlockNum provides a mock function with given fields: ctx, metaField, blockNum, chainID +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, metaField, blockNum, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, metaField, blockNum, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, metaField, blockNum, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, int64, *big.Int) error); ok { + r1 = rf(ctx, metaField, blockNum, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithMetaFieldByStates provides a mock function with given fields: ctx, metaField, states, chainID +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, metaField, states, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, metaField, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, metaField, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) error); ok { + r1 = rf(ctx, metaField, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetForwarderForEOA provides a mock function with given fields: eoa func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetForwarderForEOA(eoa ADDR) (ADDR, error) { ret := _m.Called(eoa) diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index d80f534ad26..5b7afd32242 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -47,6 +47,14 @@ type TxManager[ RegisterResumeCallback(fn ResumeCallback) SendNativeToken(ctx context.Context, chainID CHAIN_ID, from, to ADDR, value big.Int, gasLimit uint32) (etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) Reset(addr ADDR, abandon bool) error + // Find transactions by a field in the TxMeta blob and transaction states + FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + // Find transactions with a non-null TxMeta field that was provided by transaction states + FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + // Find transactions with a non-null TxMeta field that was provided and a receipt block number greater than or equal to the one provided + FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + // Find transactions loaded with transaction attempts and receipts by transaction IDs and states + FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) } type reset struct { @@ -530,6 +538,26 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) SendNative return etx, nil } +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + txes, err = b.txStore.FindTxesByMetaFieldAndStates(ctx, metaField, metaValue, states, chainID) + return +} + +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + txes, err = b.txStore.FindTxesWithMetaFieldByStates(ctx, metaField, states, chainID) + return +} + +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + txes, err = b.txStore.FindTxesWithMetaFieldByReceiptBlockNum(ctx, metaField, blockNum, chainID) + return +} + +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + txes, err = b.txStore.FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx, ids, states, chainID) + return +} + type NullTxManager[ CHAIN_ID types.ID, HEAD types.Head[BLOCK_HASH], @@ -584,3 +612,15 @@ func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Hea } func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) RegisterResumeCallback(fn ResumeCallback) { } +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + return txes, errors.New(n.ErrMsg) +} +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + return txes, errors.New(n.ErrMsg) +} +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + return txes, errors.New(n.ErrMsg) +} +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { + return txes, errors.New(n.ErrMsg) +} diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index 02388e40f40..7da51de606b 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -4,6 +4,7 @@ package mocks import ( context "context" + big "math/big" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" mock "github.com/stretchr/testify/mock" @@ -361,6 +362,110 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxWithS return r0, r1 } +// FindTxesByMetaFieldAndStates provides a mock function with given fields: ctx, metaField, metaValue, states, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, metaField, metaValue, states, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, metaField, metaValue, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, metaField, metaValue, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, []txmgrtypes.TxState, *big.Int) error); ok { + r1 = rf(ctx, metaField, metaValue, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithAttemptsAndReceiptsByIdsAndState provides a mock function with given fields: ctx, ids, states, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, ids, states, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, ids, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, ids, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []big.Int, []txmgrtypes.TxState, *big.Int) error); ok { + r1 = rf(ctx, ids, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithMetaFieldByReceiptBlockNum provides a mock function with given fields: ctx, metaField, blockNum, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, metaField, blockNum, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, metaField, blockNum, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, metaField, blockNum, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, int64, *big.Int) error); ok { + r1 = rf(ctx, metaField, blockNum, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithMetaFieldByStates provides a mock function with given fields: ctx, metaField, states, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, metaField, states, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, metaField, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, metaField, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, []txmgrtypes.TxState, *big.Int) error); ok { + r1 = rf(ctx, metaField, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindTxsRequiringGasBump provides a mock function with given fields: ctx, address, blockNum, gasBumpThreshold, depth, chainID func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxsRequiringGasBump(ctx context.Context, address ADDR, blockNum int64, gasBumpThreshold int64, depth int64, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, address, blockNum, gasBumpThreshold, depth, chainID) diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index 059a87d7ab2..83cb4b85ee6 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -43,6 +43,14 @@ type TxStore[ CheckTxQueueCapacity(ctx context.Context, fromAddress ADDR, maxQueuedTransactions uint64, chainID CHAIN_ID) (err error) Close() Abandon(ctx context.Context, id CHAIN_ID, addr ADDR) error + // Find transactions by a field in the TxMeta blob and transaction states + FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []TxState, chainID *big.Int) (tx []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + // Find transactions with a non-null TxMeta field that was provided by transaction states + FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []TxState, chainID *big.Int) (tx []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + // Find transactions with a non-null TxMeta field that was provided and a receipt block number greater than or equal to the one provided + FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) (tx []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + // Find transactions loaded with transaction attempts and receipts by transaction IDs and states + FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []TxState, chainID *big.Int) (tx []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) } // TransactionStore contains the persistence layer methods needed to manage Txs and TxAttempts diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index abab2046620..cde536bc7ba 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -318,7 +318,8 @@ func (c *SimulatedBackendClient) BlockByHash(ctx context.Context, hash common.Ha } func (c *SimulatedBackendClient) LatestBlockHeight(ctx context.Context) (*big.Int, error) { - panic("not implemented") + header, err := c.b.HeaderByNumber(ctx, nil) + return header.Number, err } // ChainID returns the ethereum ChainID. diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 86a06b60250..971103bdfd5 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -184,6 +184,7 @@ type DbEthTx struct { func (db *DbEthTx) FromTx(tx *Tx) { db.ID = tx.ID + db.IdempotencyKey = tx.IdempotencyKey db.FromAddress = tx.FromAddress db.ToAddress = tx.ToAddress db.EncodedPayload = tx.EncodedPayload @@ -511,8 +512,8 @@ func (o *evmTxStore) InsertTx(etx *Tx) error { if etx.CreatedAt == (time.Time{}) { etx.CreatedAt = time.Now() } - const insertEthTxSQL = `INSERT INTO evm.txes (nonce, from_address, to_address, encoded_payload, value, gas_limit, error, broadcast_at, initial_broadcast_at, created_at, state, meta, subject, pipeline_task_run_id, min_confirmations, evm_chain_id, transmit_checker) VALUES ( -:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit, :error, :broadcast_at, :initial_broadcast_at, :created_at, :state, :meta, :subject, :pipeline_task_run_id, :min_confirmations, :evm_chain_id, :transmit_checker + const insertEthTxSQL = `INSERT INTO evm.txes (nonce, from_address, to_address, encoded_payload, value, gas_limit, error, broadcast_at, initial_broadcast_at, created_at, state, meta, subject, pipeline_task_run_id, min_confirmations, evm_chain_id, transmit_checker, idempotency_key) VALUES ( +:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit, :error, :broadcast_at, :initial_broadcast_at, :created_at, :state, :meta, :subject, :pipeline_task_run_id, :min_confirmations, :evm_chain_id, :transmit_checker, :idempotency_key ) RETURNING *` var dbTx DbEthTx dbTx.FromTx(etx) @@ -548,14 +549,14 @@ func (o *evmTxStore) FindTxWithAttempts(etxID int64) (etx Tx, err error) { err = o.q.Transaction(func(tx pg.Queryer) error { var dbEtx DbEthTx if err = tx.Get(&dbEtx, `SELECT * FROM evm.txes WHERE id = $1 ORDER BY created_at ASC, id ASC`, etxID); err != nil { - return pkgerrors.Wrapf(err, "failed to find eth_tx with id %d", etxID) + return pkgerrors.Wrapf(err, "failed to find evm.tx with id %d", etxID) } dbEtx.ToTx(&etx) if err = o.loadTxAttemptsAtomic(&etx, pg.WithQueryer(tx)); err != nil { - return pkgerrors.Wrapf(err, "failed to load evm.tx_attempts for eth_tx with id %d", etxID) + return pkgerrors.Wrapf(err, "failed to load evm.tx_attempts for evm.tx with id %d", etxID) } if err = loadEthTxAttemptsReceipts(tx, &etx); err != nil { - return pkgerrors.Wrapf(err, "failed to load evm.receipts for eth_tx with id %d", etxID) + return pkgerrors.Wrapf(err, "failed to load evm.receipts for evm.tx with id %d", etxID) } return nil }, pg.OptReadOnlyTx()) @@ -637,6 +638,8 @@ func loadEthTxesAttemptsReceipts(q pg.Queryer, etxs []*Tx) (err error) { for _, receipt := range receipts { attempt := attemptHashM[receipt.TxHash] + // Although the attempts struct supports multiple receipts, the expectation for EVM is that there is only one receipt + // per tx and therefore attempt too. attempt.Receipts = append(attempt.Receipts, receipt) } return nil @@ -1776,6 +1779,72 @@ func (o *evmTxStore) Abandon(ctx context.Context, chainID *big.Int, addr common. return err } +// Find transactions by a field in the TxMeta blob and transaction states +func (o *evmTxStore) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) ([]*Tx, error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + var dbEtxs []DbEthTx + sql := fmt.Sprintf("SELECT * FROM evm.txes WHERE evm_chain_id = $1 AND meta->>'%s' = $2 AND state = ANY($3)", metaField) + err := qq.Select(&dbEtxs, sql, chainID.String(), metaValue, pq.Array(states)) + txes := make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, pkgerrors.Wrap(err, "failed to FindTxesByMetaFieldAndStates") +} + +// Find transactions with a non-null TxMeta field that was provided by transaction states +func (o *evmTxStore) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []txmgrtypes.TxState, chainID *big.Int) (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 := fmt.Sprintf("SELECT * FROM evm.txes WHERE meta->'%s' IS NOT NULL AND state = ANY($1) AND evm_chain_id = $2", metaField) + err = qq.Select(&dbEtxs, sql, pq.Array(states), chainID.String()) + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, pkgerrors.Wrap(err, "failed to FindTxesWithMetaFieldByStates") +} + +// Find transactions with a non-null TxMeta field that was provided and a receipt block number greater than or equal to the one provided +func (o *evmTxStore) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) (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 := fmt.Sprintf("SELECT et.* FROM evm.txes et JOIN evm.tx_attempts eta on et.id = eta.eth_tx_id JOIN evm.receipts er on eta.hash = er.tx_hash WHERE et.meta->'%s' IS NOT NULL AND er.block_number >= $1 AND et.evm_chain_id = $2", metaField) + err = qq.Select(&dbEtxs, sql, blockNum, chainID.String()) + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + return txes, pkgerrors.Wrap(err, "failed to FindTxesWithMetaFieldByReceiptBlockNum") +} + +// Find transactions loaded with transaction attempts and receipts by transaction IDs and states +func (o *evmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) (txes []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + err = qq.Transaction(func(tx pg.Queryer) error { + var dbEtxs []DbEthTx + if err = tx.Select(&dbEtxs, `SELECT * FROM evm.txes WHERE id = ANY($1) AND state = ANY($2) AND evm_chain_id = $3`, pq.Array(ids), pq.Array(states), chainID.String()); err != nil { + return pkgerrors.Wrapf(err, "failed to find evm.txes") + } + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + if err = o.LoadTxesAttempts(txes, pg.WithQueryer(tx)); err != nil { + return pkgerrors.Wrapf(err, "failed to load evm.tx_attempts for evm.tx") + } + if err = loadEthTxesAttemptsReceipts(tx, txes); err != nil { + return pkgerrors.Wrapf(err, "failed to load evm.receipts for evm.tx") + } + return nil + }) + return txes, pkgerrors.Wrap(err, "FindTxesWithAttemptsAndReceiptsByIdsAndState failed") +} + // 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/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index 69a0d257f7a..4632a8ae342 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -467,6 +467,110 @@ func (_m *EvmTxStore) FindTxWithSequence(ctx context.Context, fromAddress common return r0, r1 } +// FindTxesByMetaFieldAndStates provides a mock function with given fields: ctx, metaField, metaValue, states, chainID +func (_m *EvmTxStore) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, metaField, metaValue, states, chainID) + + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, []types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, metaField, metaValue, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, []types.TxState, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, metaField, metaValue, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, []types.TxState, *big.Int) error); ok { + r1 = rf(ctx, metaField, metaValue, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithAttemptsAndReceiptsByIdsAndState provides a mock function with given fields: ctx, ids, states, chainID +func (_m *EvmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, ids, states, chainID) + + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, ids, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, []big.Int, []types.TxState, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, ids, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, []big.Int, []types.TxState, *big.Int) error); ok { + r1 = rf(ctx, ids, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithMetaFieldByReceiptBlockNum provides a mock function with given fields: ctx, metaField, blockNum, chainID +func (_m *EvmTxStore) FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, metaField, blockNum, chainID) + + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, metaField, blockNum, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, int64, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, metaField, blockNum, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, int64, *big.Int) error); ok { + r1 = rf(ctx, metaField, blockNum, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindTxesWithMetaFieldByStates provides a mock function with given fields: ctx, metaField, states, chainID +func (_m *EvmTxStore) FindTxesWithMetaFieldByStates(ctx context.Context, metaField string, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, metaField, states, chainID) + + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, []types.TxState, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, metaField, states, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, string, []types.TxState, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, metaField, states, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, []types.TxState, *big.Int) error); ok { + r1 = rf(ctx, metaField, states, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindTxsRequiringGasBump provides a mock function with given fields: ctx, address, blockNum, gasBumpThreshold, depth, chainID func (_m *EvmTxStore) FindTxsRequiringGasBump(ctx context.Context, address common.Address, blockNum int64, gasBumpThreshold int64, depth int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, address, blockNum, gasBumpThreshold, depth, chainID) diff --git a/core/chains/evm/txmgr/test_helpers.go b/core/chains/evm/txmgr/test_helpers.go new file mode 100644 index 00000000000..f9c0423a620 --- /dev/null +++ b/core/chains/evm/txmgr/test_helpers.go @@ -0,0 +1,151 @@ +package txmgr + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + + evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" +) + +func ptr[T any](t T) *T { return &t } + +type TestDatabaseConfig struct { + config.Database + defaultQueryTimeout time.Duration +} + +func (d *TestDatabaseConfig) DefaultQueryTimeout() time.Duration { + return d.defaultQueryTimeout +} + +func (d *TestDatabaseConfig) LogSQL() bool { + return false +} + +type TestListenerConfig struct { + config.Listener +} + +func (l *TestListenerConfig) FallbackPollInterval() time.Duration { + return 1 * time.Minute +} + +func (d *TestDatabaseConfig) Listener() config.Listener { + return &TestListenerConfig{} +} + +type TestEvmConfig struct { + evmconfig.EVM + MaxInFlight uint32 + ReaperInterval time.Duration + ReaperThreshold time.Duration + ResendAfterThreshold time.Duration + BumpThreshold uint64 + MaxQueued uint64 +} + +func (e *TestEvmConfig) Transactions() evmconfig.Transactions { + return &transactionsConfig{e: e} +} + +func (e *TestEvmConfig) NonceAutoSync() bool { return true } + +func (e *TestEvmConfig) FinalityDepth() uint32 { return 42 } + +type TestGasEstimatorConfig struct { + bumpThreshold uint64 +} + +func (g *TestGasEstimatorConfig) BlockHistory() evmconfig.BlockHistory { + return &TestBlockHistoryConfig{} +} + +func (g *TestGasEstimatorConfig) EIP1559DynamicFees() bool { return false } +func (g *TestGasEstimatorConfig) LimitDefault() uint32 { return 42 } +func (g *TestGasEstimatorConfig) BumpPercent() uint16 { return 42 } +func (g *TestGasEstimatorConfig) BumpThreshold() uint64 { return g.bumpThreshold } +func (g *TestGasEstimatorConfig) BumpMin() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) FeeCapDefault() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) PriceDefault() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) TipCapDefault() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) TipCapMin() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) LimitMax() uint32 { return 0 } +func (g *TestGasEstimatorConfig) LimitMultiplier() float32 { return 0 } +func (g *TestGasEstimatorConfig) BumpTxDepth() uint32 { return 42 } +func (g *TestGasEstimatorConfig) LimitTransfer() uint32 { return 42 } +func (g *TestGasEstimatorConfig) PriceMax() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) PriceMin() *assets.Wei { return assets.NewWeiI(42) } +func (g *TestGasEstimatorConfig) Mode() string { return "FixedPrice" } +func (g *TestGasEstimatorConfig) LimitJobType() evmconfig.LimitJobType { + return &TestLimitJobTypeConfig{} +} +func (g *TestGasEstimatorConfig) PriceMaxKey(addr common.Address) *assets.Wei { + return assets.NewWeiI(42) +} + +func (e *TestEvmConfig) GasEstimator() evmconfig.GasEstimator { + return &TestGasEstimatorConfig{bumpThreshold: e.BumpThreshold} +} + +type TestLimitJobTypeConfig struct { +} + +func (l *TestLimitJobTypeConfig) OCR() *uint32 { return ptr(uint32(0)) } +func (l *TestLimitJobTypeConfig) OCR2() *uint32 { return ptr(uint32(0)) } +func (l *TestLimitJobTypeConfig) DR() *uint32 { return ptr(uint32(0)) } +func (l *TestLimitJobTypeConfig) FM() *uint32 { return ptr(uint32(0)) } +func (l *TestLimitJobTypeConfig) Keeper() *uint32 { return ptr(uint32(0)) } +func (l *TestLimitJobTypeConfig) VRF() *uint32 { return ptr(uint32(0)) } + +type TestBlockHistoryConfig struct { + evmconfig.BlockHistory +} + +func (b *TestBlockHistoryConfig) BatchSize() uint32 { return 42 } +func (b *TestBlockHistoryConfig) BlockDelay() uint16 { return 42 } +func (b *TestBlockHistoryConfig) BlockHistorySize() uint16 { return 42 } +func (b *TestBlockHistoryConfig) EIP1559FeeCapBufferBlocks() uint16 { return 42 } +func (b *TestBlockHistoryConfig) TransactionPercentile() uint16 { return 42 } + +type transactionsConfig struct { + evmconfig.Transactions + e *TestEvmConfig +} + +func (*transactionsConfig) ForwardersEnabled() bool { return true } +func (t *transactionsConfig) MaxInFlight() uint32 { return t.e.MaxInFlight } +func (t *transactionsConfig) MaxQueued() uint64 { return t.e.MaxQueued } +func (t *transactionsConfig) ReaperInterval() time.Duration { return t.e.ReaperInterval } +func (t *transactionsConfig) ReaperThreshold() time.Duration { return t.e.ReaperThreshold } +func (t *transactionsConfig) ResendAfterThreshold() time.Duration { return t.e.ResendAfterThreshold } + +type MockConfig struct { + EvmConfig *TestEvmConfig + RpcDefaultBatchSize uint32 + finalityDepth uint32 + finalityTagEnabled bool +} + +func (c *MockConfig) EVM() evmconfig.EVM { + return c.EvmConfig +} + +func (c *MockConfig) NonceAutoSync() bool { return true } +func (c *MockConfig) ChainType() config.ChainType { return "" } +func (c *MockConfig) FinalityDepth() uint32 { return c.finalityDepth } +func (c *MockConfig) SetFinalityDepth(fd uint32) { c.finalityDepth = fd } +func (c *MockConfig) FinalityTagEnabled() bool { return c.finalityTagEnabled } +func (c *MockConfig) RPCDefaultBatchSize() uint32 { return c.RpcDefaultBatchSize } + +func MakeTestConfigs(t *testing.T) (*MockConfig, *TestDatabaseConfig, *TestEvmConfig) { + db := &TestDatabaseConfig{defaultQueryTimeout: pg.DefaultQueryTimeout} + ec := &TestEvmConfig{BumpThreshold: 42, MaxInFlight: uint32(42), MaxQueued: uint64(0), ReaperInterval: time.Duration(0), ReaperThreshold: time.Duration(0)} + config := &MockConfig{EvmConfig: ec} + return config, db, ec +} diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index e9823ee0214..4aa54bc52a1 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/common" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/google/uuid" @@ -27,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -36,7 +34,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -76,7 +73,7 @@ func TestTxm_SendNativeToken_DoesNotSendToZero(t *testing.T) { to := utils.ZeroAddress value := assets.NewEth(1).ToInt() - config, dbConfig, evmConfig := makeConfigs(t) + config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) keyStore := cltest.NewKeyStore(t, db, dbConfig).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -102,7 +99,7 @@ func TestTxm_CreateTransaction(t *testing.T) { gasLimit := uint32(1000) payload := []byte{1, 2, 3} - config, dbConfig, evmConfig := makeConfigs(t) + config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -115,7 +112,7 @@ func TestTxm_CreateTransaction(t *testing.T) { strategy := newMockTxStrategy(t) strategy.On("Subject").Return(uuid.NullUUID{UUID: subject, Valid: true}) strategy.On("PruneQueue", mock.Anything, mock.Anything).Return(int64(0), nil) - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) etx, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ FromAddress: fromAddress, ToAddress: toAddress, @@ -151,7 +148,7 @@ func TestTxm_CreateTransaction(t *testing.T) { cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, fromAddress) t.Run("with queue at capacity does not insert eth_tx", func(t *testing.T) { - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) _, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ FromAddress: fromAddress, ToAddress: testutils.NewAddress(), @@ -165,7 +162,7 @@ func TestTxm_CreateTransaction(t *testing.T) { }) t.Run("doesn't insert eth_tx if a matching tx already exists for that pipeline_task_run_id", func(t *testing.T) { - evmConfig.maxQueued = uint64(3) + evmConfig.MaxQueued = uint64(3) id := uuid.New() tx1, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ FromAddress: fromAddress, @@ -221,7 +218,7 @@ func TestTxm_CreateTransaction(t *testing.T) { checker := txmgr.TransmitCheckerSpec{ CheckerType: txmgr.TransmitCheckerTypeSimulate, } - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) etx, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ FromAddress: fromAddress, ToAddress: toAddress, @@ -260,7 +257,7 @@ func TestTxm_CreateTransaction(t *testing.T) { SubID: &testDefaultSubID, GlobalSubID: &testDefaultGlobalSubID, } - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) checker := txmgr.TransmitCheckerSpec{ CheckerType: txmgr.TransmitCheckerTypeVRFV2, VRFCoordinatorAddress: testutils.NewAddressPtr(), @@ -292,7 +289,7 @@ func TestTxm_CreateTransaction(t *testing.T) { t.Run("forwards tx when a proper forwarder is set up", func(t *testing.T) { pgtest.MustExec(t, db, `DELETE FROM evm.txes`) pgtest.MustExec(t, db, `DELETE FROM evm.forwarders`) - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) // Create mock forwarder, mock authorizedsenders call. form := forwarders.NewORM(db, logger.TestLogger(t), cfg.Database()) @@ -322,7 +319,7 @@ func TestTxm_CreateTransaction(t *testing.T) { }) t.Run("insert Tx successfully with a IdempotencyKey", func(t *testing.T) { - evmConfig.maxQueued = uint64(3) + evmConfig.MaxQueued = uint64(3) id := uuid.New() idempotencyKey := "1" _, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ @@ -338,7 +335,7 @@ func TestTxm_CreateTransaction(t *testing.T) { }) t.Run("doesn't insert eth_tx if a matching tx already exists for that IdempotencyKey", func(t *testing.T) { - evmConfig.maxQueued = uint64(3) + evmConfig.MaxQueued = uint64(3) id := uuid.New() idempotencyKey := "2" tx1, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ @@ -371,138 +368,6 @@ func newMockTxStrategy(t *testing.T) *commontxmmocks.TxStrategy { return commontxmmocks.NewTxStrategy(t) } -type databaseConfig struct { - config.Database - defaultQueryTimeout time.Duration -} - -func (d *databaseConfig) DefaultQueryTimeout() time.Duration { - return d.defaultQueryTimeout -} - -func (d *databaseConfig) LogSQL() bool { - return false -} - -type listenerConfig struct { - config.Listener -} - -func (l *listenerConfig) FallbackPollInterval() time.Duration { - return 1 * time.Minute -} - -func (d *databaseConfig) Listener() config.Listener { - return &listenerConfig{} -} - -type evmConfig struct { - evmconfig.EVM - maxInFlight uint32 - reaperInterval time.Duration - reaperThreshold time.Duration - resendAfterThreshold time.Duration - bumpThreshold uint64 - maxQueued uint64 -} - -func (e *evmConfig) Transactions() evmconfig.Transactions { - return &transactionsConfig{e: e} -} - -func (e *evmConfig) GasEstimator() evmconfig.GasEstimator { - return &gasEstimatorConfig{bumpThreshold: e.bumpThreshold} -} - -func (e *evmConfig) NonceAutoSync() bool { return true } - -func (e *evmConfig) FinalityDepth() uint32 { return 42 } - -type gasEstimatorConfig struct { - bumpThreshold uint64 -} - -func (g *gasEstimatorConfig) BlockHistory() evmconfig.BlockHistory { - return &blockHistoryConfig{} -} - -func (g *gasEstimatorConfig) EIP1559DynamicFees() bool { return false } -func (g *gasEstimatorConfig) LimitDefault() uint32 { return 42 } -func (g *gasEstimatorConfig) BumpPercent() uint16 { return 42 } -func (g *gasEstimatorConfig) BumpThreshold() uint64 { return g.bumpThreshold } -func (g *gasEstimatorConfig) BumpMin() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) FeeCapDefault() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) PriceDefault() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) TipCapDefault() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) TipCapMin() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) LimitMax() uint32 { return 0 } -func (g *gasEstimatorConfig) LimitMultiplier() float32 { return 0 } -func (g *gasEstimatorConfig) BumpTxDepth() uint32 { return 42 } -func (g *gasEstimatorConfig) LimitTransfer() uint32 { return 42 } -func (g *gasEstimatorConfig) PriceMax() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) PriceMin() *assets.Wei { return assets.NewWeiI(42) } -func (g *gasEstimatorConfig) Mode() string { return "FixedPrice" } -func (g *gasEstimatorConfig) LimitJobType() evmconfig.LimitJobType { return &limitJobTypeConfig{} } -func (g *gasEstimatorConfig) PriceMaxKey(addr common.Address) *assets.Wei { - return assets.NewWeiI(42) -} - -type limitJobTypeConfig struct { -} - -func (l *limitJobTypeConfig) OCR() *uint32 { return ptr(uint32(0)) } -func (l *limitJobTypeConfig) OCR2() *uint32 { return ptr(uint32(0)) } -func (l *limitJobTypeConfig) DR() *uint32 { return ptr(uint32(0)) } -func (l *limitJobTypeConfig) FM() *uint32 { return ptr(uint32(0)) } -func (l *limitJobTypeConfig) Keeper() *uint32 { return ptr(uint32(0)) } -func (l *limitJobTypeConfig) VRF() *uint32 { return ptr(uint32(0)) } - -type blockHistoryConfig struct { - evmconfig.BlockHistory -} - -func (b *blockHistoryConfig) BatchSize() uint32 { return 42 } -func (b *blockHistoryConfig) BlockDelay() uint16 { return 42 } -func (b *blockHistoryConfig) BlockHistorySize() uint16 { return 42 } -func (b *blockHistoryConfig) EIP1559FeeCapBufferBlocks() uint16 { return 42 } -func (b *blockHistoryConfig) TransactionPercentile() uint16 { return 42 } - -type transactionsConfig struct { - evmconfig.Transactions - e *evmConfig -} - -func (*transactionsConfig) ForwardersEnabled() bool { return true } -func (t *transactionsConfig) MaxInFlight() uint32 { return t.e.maxInFlight } -func (t *transactionsConfig) MaxQueued() uint64 { return t.e.maxQueued } -func (t *transactionsConfig) ReaperInterval() time.Duration { return t.e.reaperInterval } -func (t *transactionsConfig) ReaperThreshold() time.Duration { return t.e.reaperThreshold } -func (t *transactionsConfig) ResendAfterThreshold() time.Duration { return t.e.resendAfterThreshold } - -type mockConfig struct { - evmConfig *evmConfig - rpcDefaultBatchSize uint32 - finalityDepth uint32 - finalityTagEnabled bool -} - -func (c *mockConfig) EVM() evmconfig.EVM { - return c.evmConfig -} - -func (c *mockConfig) NonceAutoSync() bool { return true } -func (c *mockConfig) ChainType() config.ChainType { return "" } -func (c *mockConfig) FinalityDepth() uint32 { return c.finalityDepth } -func (c *mockConfig) FinalityTagEnabled() bool { return c.finalityTagEnabled } -func (c *mockConfig) RPCDefaultBatchSize() uint32 { return c.rpcDefaultBatchSize } - -func makeConfigs(t *testing.T) (*mockConfig, *databaseConfig, *evmConfig) { - db := &databaseConfig{defaultQueryTimeout: pg.DefaultQueryTimeout} - ec := &evmConfig{bumpThreshold: 42, maxInFlight: uint32(42), maxQueued: uint64(0), reaperInterval: time.Duration(0), reaperThreshold: time.Duration(0)} - config := &mockConfig{evmConfig: ec} - return config, db, ec -} - func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) @@ -517,7 +382,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { gasLimit := uint32(1000) toAddress := testutils.NewAddress() - config, dbConfig, evmConfig := makeConfigs(t) + config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) @@ -527,7 +392,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { t.Run("if another key has any transactions with insufficient eth errors, transmits as normal", func(t *testing.T) { payload := cltest.MustRandomBytes(t, 100) - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, otherKey.Address) strategy := newMockTxStrategy(t) strategy.On("Subject").Return(uuid.NullUUID{}) @@ -550,7 +415,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { t.Run("if this key has any transactions with insufficient eth errors, inserts it anyway", func(t *testing.T) { payload := cltest.MustRandomBytes(t, 100) - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, thisKey.Address) strategy := newMockTxStrategy(t) @@ -578,7 +443,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { strategy.On("Subject").Return(uuid.NullUUID{}) strategy.On("PruneQueue", mock.Anything, mock.Anything).Return(int64(0), nil) - evmConfig.maxQueued = uint64(1) + evmConfig.MaxQueued = uint64(1) etx, err := txm.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ FromAddress: evmFromAddress, ToAddress: toAddress, @@ -598,13 +463,13 @@ func TestTxm_Lifecycle(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) kst := ksmocks.NewEth(t) - config, dbConfig, evmConfig := makeConfigs(t) - config.finalityDepth = uint32(42) - config.rpcDefaultBatchSize = uint32(4) + config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) + config.SetFinalityDepth(uint32(42)) + config.RpcDefaultBatchSize = uint32(4) - evmConfig.resendAfterThreshold = 1 * time.Hour - evmConfig.reaperThreshold = 1 * time.Hour - evmConfig.reaperInterval = 1 * time.Hour + evmConfig.ResendAfterThreshold = 1 * time.Hour + evmConfig.ReaperThreshold = 1 * time.Hour + evmConfig.ReaperInterval = 1 * time.Hour kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return([]gethcommon.Address{}, nil) @@ -619,7 +484,7 @@ func TestTxm_Lifecycle(t *testing.T) { // It should not hang or panic txm.OnNewLongestChain(testutils.Context(t), head) - evmConfig.bumpThreshold = uint64(1) + evmConfig.BumpThreshold = uint64(1) require.NoError(t, txm.Start(testutils.Context(t))) diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index f6b6a460b89..558d48752d2 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -1,10 +1,7 @@ package vrf import ( - "encoding/hex" "fmt" - "math/big" - "strings" "time" "github.com/avast/retry-go/v4" @@ -87,7 +84,6 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { if err != nil { return nil, err } - chainId := chain.Client().ConfiguredChainID() coordinator, err := solidity_vrf_coordinator_interface.NewVRFCoordinator(jb.VRFSpec.CoordinatorAddress.Address(), chain.Client()) if err != nil { return nil, err @@ -168,23 +164,19 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { chain.Config().EVM(), chain.Config().EVM().GasEstimator(), lV2Plus, - chain.Client(), + chain, chain.ID(), - chain.LogBroadcaster(), d.q, v2.NewCoordinatorV2_5(coordinatorV2Plus), batchCoordinatorV2, vrfOwner, aggregator, - chain.TxManager(), d.pr, d.ks.Eth(), jb, d.mailMon, utils.NewHighCapacityMailbox[log.Broadcast](), func() {}, - GetStartingResponseCountsV2(d.q, lV2Plus, chainId.Uint64(), chain.Config().EVM().FinalityDepth()), - chain.HeadBroadcaster(), vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())))}, nil } if _, ok := task.(*pipeline.VRFTaskV2); ok { @@ -223,49 +215,42 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { chain.Config().EVM(), chain.Config().EVM().GasEstimator(), lV2, - chain.Client(), + chain, chain.ID(), - chain.LogBroadcaster(), d.q, v2.NewCoordinatorV2(coordinatorV2), batchCoordinatorV2, vrfOwner, aggregator, - chain.TxManager(), d.pr, d.ks.Eth(), jb, d.mailMon, utils.NewHighCapacityMailbox[log.Broadcast](), func() {}, - GetStartingResponseCountsV2(d.q, lV2, chainId.Uint64(), chain.Config().EVM().FinalityDepth()), - chain.HeadBroadcaster(), vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())))}, nil } if _, ok := task.(*pipeline.VRFTask); ok { return []job.ServiceCtx{&v1.Listener{ - Cfg: chain.Config().EVM(), - FeeCfg: chain.Config().EVM().GasEstimator(), - L: logger.Sugared(lV1), - HeadBroadcaster: chain.HeadBroadcaster(), - LogBroadcaster: chain.LogBroadcaster(), - Q: d.q, - Txm: chain.TxManager(), - Coordinator: coordinator, - PipelineRunner: d.pr, - GethKs: d.ks.Eth(), - Job: jb, - MailMon: d.mailMon, + Cfg: chain.Config().EVM(), + FeeCfg: chain.Config().EVM().GasEstimator(), + L: logger.Sugared(lV1), + Q: d.q, + Coordinator: coordinator, + PipelineRunner: d.pr, + GethKs: d.ks.Eth(), + Job: jb, + MailMon: d.mailMon, // Note the mailbox size effectively sets a limit on how many logs we can replay // in the event of a VRF outage. ReqLogs: utils.NewHighCapacityMailbox[log.Broadcast](), ChStop: make(chan struct{}), WaitOnStop: make(chan struct{}), NewHead: make(chan struct{}, 1), - ResponseCount: GetStartingResponseCountsV1(d.q, lV1, chainId.Uint64(), chain.Config().EVM().FinalityDepth()), BlockNumberToReqID: pairing.New(), ReqAdded: func() {}, Deduper: vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())), + Chain: chain, }}, nil } } @@ -314,101 +299,3 @@ func FromAddressMaxGasPricesAllEqual(jb job.Job, keySpecificMaxGasPriceWei keySp } return } - -func GetStartingResponseCountsV1(q pg.Q, l logger.Logger, chainID uint64, evmFinalityDepth uint32) map[[32]byte]uint64 { - respCounts := map[[32]byte]uint64{} - - // Only check as far back as the evm finality depth for completed transactions. - counts, err := getRespCounts(q, chainID, evmFinalityDepth) - if err != nil { - // Continue with an empty map, do not block job on this. - l.Errorw("Unable to read previous confirmed fulfillments", "err", err) - return respCounts - } - - for _, c := range counts { - // Remove the quotes from the json - req := strings.Replace(c.RequestID, `"`, ``, 2) - // Remove the 0x prefix - b, err := hex.DecodeString(req[2:]) - if err != nil { - l.Errorw("Unable to read fulfillment", "err", err, "reqID", c.RequestID) - continue - } - var reqID [32]byte - copy(reqID[:], b) - respCounts[reqID] = uint64(c.Count) - } - - return respCounts -} - -func GetStartingResponseCountsV2( - q pg.Q, - l logger.Logger, - chainID uint64, - evmFinalityDepth uint32, -) map[string]uint64 { - respCounts := map[string]uint64{} - - // Only check as far back as the evm finality depth for completed transactions. - counts, err := getRespCounts(q, chainID, evmFinalityDepth) - if err != nil { - // Continue with an empty map, do not block job on this. - l.Errorw("Unable to read previous confirmed fulfillments", "err", err) - return respCounts - } - - for _, c := range counts { - // Remove the quotes from the json - req := strings.Replace(c.RequestID, `"`, ``, 2) - // Remove the 0x prefix - b, err := hex.DecodeString(req[2:]) - if err != nil { - l.Errorw("Unable to read fulfillment", "err", err, "reqID", c.RequestID) - continue - } - bi := new(big.Int).SetBytes(b) - respCounts[bi.String()] = uint64(c.Count) - } - return respCounts -} - -func getRespCounts(q pg.Q, chainID uint64, evmFinalityDepth uint32) ( - []struct { - RequestID string - Count int - }, - error, -) { - counts := []struct { - RequestID string - Count int - }{} - // This query should use the idx_evm.txes_state_from_address_evm_chain_id - // index, since the quantity of unconfirmed/unstarted/in_progress transactions _should_ be small - // relative to the rest of the data. - unconfirmedQuery := ` -SELECT meta->'RequestID' AS request_id, count(meta->'RequestID') AS count -FROM evm.txes et -WHERE et.meta->'RequestID' IS NOT NULL -AND et.state IN ('unconfirmed', 'unstarted', 'in_progress') -GROUP BY meta->'RequestID' - ` - // Fetch completed transactions only as far back as the given cutoffBlockNumber. This avoids - // a table scan of the evm.txes table, which could be large if it is unpruned. - confirmedQuery := ` -SELECT meta->'RequestID' AS request_id, count(meta->'RequestID') AS count -FROM evm.txes et JOIN evm.tx_attempts eta on et.id = eta.eth_tx_id - join evm.receipts er on eta.hash = er.tx_hash -WHERE et.meta->'RequestID' is not null -AND er.block_number >= (SELECT number FROM evm.heads WHERE evm_chain_id = $1 ORDER BY number DESC LIMIT 1) - $2 -GROUP BY meta->'RequestID' - ` - query := unconfirmedQuery + "\nUNION ALL\n" + confirmedQuery - err := q.Select(&counts, query, chainID, evmFinalityDepth) - if err != nil { - return nil, err - } - return counts, nil -} diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 91ae4400e39..927e2ae6829 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -17,9 +17,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "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" @@ -58,7 +58,7 @@ type vrfUniverse struct { ks keystore.Master vrfkey vrfkey.KeyV2 submitter common.Address - txm *txmmocks.MockEvmTxManager + txm *txmgr.TxManager hb httypes.HeadBroadcaster legacyChains evm.LegacyChainContainer cid big.Int @@ -68,28 +68,33 @@ func buildVrfUni(t *testing.T, db *sqlx.DB, cfg chainlink.GeneralConfig) vrfUniv // Mock all chain interactions lb := log_mocks.NewBroadcaster(t) lb.On("AddDependents", 1).Maybe() + lb.On("Register", mock.Anything, mock.Anything).Return(func() {}).Maybe() ec := evmclimocks.NewClient(t) ec.On("ConfiguredChainID").Return(testutils.FixtureChainID) + ec.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(51), nil).Maybe() lggr := logger.TestLogger(t) hb := headtracker.NewHeadBroadcaster(lggr) // Don't mock db interactions prm := pipeline.NewORM(db, lggr, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) btORM := bridges.NewORM(db, lggr, cfg.Database()) - txm := txmmocks.NewMockEvmTxManager(t) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg.Database()) + _, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) + txm, err := txmgr.NewTxm(db, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), ec, logger.TestLogger(t), nil, ks.Eth(), nil) + orm := headtracker.NewORM(db, lggr, cfg.Database(), *testutils.FixtureChainID) + require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), cltest.Head(51))) jrm := job.NewORM(db, prm, btORM, ks, lggr, cfg.Database()) t.Cleanup(func() { assert.NoError(t, jrm.Close()) }) relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{LogBroadcaster: lb, KeyStore: ks.Eth(), Client: ec, DB: db, GeneralConfig: cfg, TxManager: txm}) legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) pr := pipeline.NewRunner(prm, btORM, cfg.JobPipeline(), cfg.WebServer(), legacyChains, ks.Eth(), ks.VRF(), lggr, nil, nil) require.NoError(t, ks.Unlock(testutils.Password)) - k, err := ks.Eth().Create(testutils.FixtureChainID) - require.NoError(t, err) + k, err2 := ks.Eth().Create(testutils.FixtureChainID) + require.NoError(t, err2) submitter := k.Address require.NoError(t, err) - vrfkey, err := ks.VRF().Create() - require.NoError(t, err) + vrfkey, err3 := ks.VRF().Create() + require.NoError(t, err3) return vrfUniverse{ jrm: jrm, @@ -100,7 +105,7 @@ func buildVrfUni(t *testing.T, db *sqlx.DB, cfg chainlink.GeneralConfig) vrfUniv ks: ks, vrfkey: vrfkey, submitter: submitter, - txm: txm, + txm: &txm, hb: hb, legacyChains: legacyChains, cid: *ec.ConfiguredChainID(), @@ -172,6 +177,7 @@ func setup(t *testing.T) (vrfUniverse, *v1.Listener, job.Job) { listener.RunHeadListener(func() {}) }() t.Cleanup(func() { listener.Stop(t) }) + require.NoError(t, listener.Start(testutils.Context(t))) return vuni, listener, jb } @@ -302,20 +308,6 @@ func TestDelegate_ValidLog(t *testing.T) { // Expect a call to check if the req is already fulfilled. vuni.ec.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(generateCallbackReturnValues(t, false), nil) - // Ensure we queue up a valid eth transaction - // Linked to requestID - vuni.txm.On("CreateTransaction", - mock.Anything, - mock.MatchedBy(func(txRequest txmgr.TxRequest) bool { - meta := txRequest.Meta - return txRequest.FromAddress == vuni.submitter && - txRequest.ToAddress == common.HexToAddress(jb.VRFSpec.CoordinatorAddress.String()) && - txRequest.FeeLimit == uint32(500000) && - meta.JobID != nil && meta.RequestID != nil && meta.RequestTxHash != nil && - (*meta.JobID > 0 && *meta.RequestID == tc.reqID && *meta.RequestTxHash == txHash) - }), - ).Once().Return(txmgr.Tx{}, nil) - listener.HandleLog(log.NewLogBroadcast(tc.log, vuni.cid, nil)) // Wait until the log is present waitForChannel(t, added, time.Second, "request not added to the queue") diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 92e697f2294..b1f9bbb5034 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -3,20 +3,22 @@ package v1 import ( "context" "encoding/hex" + "errors" "fmt" "math/big" + "strings" "sync" "time" + "github.com/avast/retry-go/v4" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" heaps "github.com/theodesp/go-heaps" "github.com/theodesp/go-heaps/pairing" "github.com/smartcontractkit/chainlink-relay/pkg/services" - httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -46,24 +48,22 @@ type request struct { type Listener struct { services.StateMachine - Cfg vrfcommon.Config - FeeCfg vrfcommon.FeeConfig - L logger.SugaredLogger - LogBroadcaster log.Broadcaster - Coordinator *solidity_vrf_coordinator_interface.VRFCoordinator - PipelineRunner pipeline.Runner - Job job.Job - Q pg.Q - HeadBroadcaster httypes.HeadBroadcasterRegistry - Txm txmgr.TxManager - GethKs vrfcommon.GethKeyStore - MailMon *utils.MailboxMonitor - ReqLogs *utils.Mailbox[log.Broadcast] - ChStop utils.StopChan - WaitOnStop chan struct{} - NewHead chan struct{} - LatestHead uint64 - LatestHeadMu sync.RWMutex + Cfg vrfcommon.Config + FeeCfg vrfcommon.FeeConfig + L logger.SugaredLogger + Coordinator *solidity_vrf_coordinator_interface.VRFCoordinator + PipelineRunner pipeline.Runner + Job job.Job + Q pg.Q + GethKs vrfcommon.GethKeyStore + MailMon *utils.MailboxMonitor + ReqLogs *utils.Mailbox[log.Broadcast] + ChStop utils.StopChan + WaitOnStop chan struct{} + NewHead chan struct{} + LatestHead uint64 + LatestHeadMu sync.RWMutex + Chain evm.Chain // We can keep these pending logs in memory because we // only mark them confirmed once we send a corresponding fulfillment transaction. @@ -110,11 +110,11 @@ func (lsn *Listener) getLatestHead() uint64 { } // Start complies with job.Service -func (lsn *Listener) Start(context.Context) error { +func (lsn *Listener) Start(ctx context.Context) error { return lsn.StartOnce("VRFListener", func() error { spec := job.LoadDefaultVRFPollPeriod(*lsn.Job.VRFSpec) - unsubscribeLogs := lsn.LogBroadcaster.Register(lsn, log.ListenerOpts{ + unsubscribeLogs := lsn.Chain.LogBroadcaster().Register(lsn, log.ListenerOpts{ Contract: lsn.Coordinator.Address(), ParseLog: lsn.Coordinator.ParseLog, LogsWithTopics: map[common.Hash][][]log.Topic{ @@ -136,10 +136,19 @@ func (lsn *Listener) Start(context.Context) error { }) // Subscribe to the head broadcaster for handling // per request conf requirements. - latestHead, unsubscribeHeadBroadcaster := lsn.HeadBroadcaster.Subscribe(lsn) + latestHead, unsubscribeHeadBroadcaster := lsn.Chain.HeadBroadcaster().Subscribe(lsn) if latestHead != nil { lsn.setLatestHead(latestHead) } + + // Populate the response count map + lsn.RespCountMu.Lock() + defer lsn.RespCountMu.Unlock() + respCount, err := lsn.GetStartingResponseCountsV1(ctx) + if err != nil { + return err + } + lsn.ResponseCount = respCount go lsn.RunLogListener([]func(){unsubscribeLogs}, spec.MinIncomingConfirmations) go lsn.RunHeadListener(unsubscribeHeadBroadcaster) @@ -148,6 +157,48 @@ func (lsn *Listener) Start(context.Context) error { }) } +func (lsn *Listener) GetStartingResponseCountsV1(ctx context.Context) (respCount map[[32]byte]uint64, err error) { + respCounts := make(map[[32]byte]uint64) + var latestBlockNum *big.Int + // Retry client call for LatestBlockHeight if fails + // Want to avoid failing startup due to potential faulty RPC call + err = retry.Do(func() error { + latestBlockNum, err = lsn.Chain.Client().LatestBlockHeight(ctx) + return err + }, retry.Attempts(10), retry.Delay(500*time.Millisecond)) + if err != nil { + return nil, err + } + if latestBlockNum == nil { + return nil, errors.New("LatestBlockHeight return nil block num") + } + confirmedBlockNum := latestBlockNum.Int64() - int64(lsn.Chain.Config().EVM().FinalityDepth()) + // Only check as far back as the evm finality depth for completed transactions. + var counts []vrfcommon.RespCountEntry + counts, err = vrfcommon.GetRespCounts(ctx, lsn.Chain.TxManager(), lsn.Chain.Client().ConfiguredChainID(), confirmedBlockNum) + if err != nil { + // Continue with an empty map, do not block job on this. + lsn.L.Errorw("Unable to read previous confirmed fulfillments", "err", err) + return respCounts, nil + } + + for _, c := range counts { + // Remove the quotes from the json + req := strings.Replace(c.RequestID, `"`, ``, 2) + // Remove the 0x prefix + b, err := hex.DecodeString(req[2:]) + if err != nil { + lsn.L.Errorw("Unable to read fulfillment", "err", err, "reqID", c.RequestID) + continue + } + var reqID [32]byte + copy(reqID[:], b) + respCounts[reqID] = uint64(c.Count) + } + + return respCounts, nil +} + // Removes and returns all the confirmed logs from // the pending queue. func (lsn *Listener) extractConfirmedLogs() []request { @@ -314,7 +365,7 @@ func (lsn *Listener) handleLog(lb log.Broadcast, minConfs uint32) { } func (lsn *Listener) shouldProcessLog(lb log.Broadcast) bool { - consumed, err := lsn.LogBroadcaster.WasAlreadyConsumed(lb) + consumed, err := lsn.Chain.LogBroadcaster().WasAlreadyConsumed(lb) if err != nil { lsn.L.Errorw("Could not determine if log was already consumed", "error", err, "txHash", lb.RawLog().TxHash) // Do not process, let lb resend it as a retry mechanism. @@ -324,7 +375,7 @@ func (lsn *Listener) shouldProcessLog(lb log.Broadcast) bool { } func (lsn *Listener) markLogAsConsumed(lb log.Broadcast) { - err := lsn.LogBroadcaster.MarkConsumed(lb) + err := lsn.Chain.LogBroadcaster().MarkConsumed(lb) lsn.L.ErrorIf(err, fmt.Sprintf("Unable to mark log %v as consumed", lb.String())) } @@ -432,7 +483,7 @@ func (lsn *Listener) ProcessRequest(ctx context.Context, req request) bool { // The VRF pipeline has no async tasks, so we don't need to check for `incomplete` if _, err = lsn.PipelineRunner.Run(ctx, run, lggr, true, func(tx pg.Queryer) error { // Always mark consumed regardless of whether the proof failed or not. - if err = lsn.LogBroadcaster.MarkConsumed(req.lb, pg.WithQueryer(tx)); err != nil { + if err = lsn.Chain.LogBroadcaster().MarkConsumed(req.lb, pg.WithQueryer(tx)); err != nil { lggr.Errorw("Failed mark consumed", "err", err) } return nil diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index 09c9a0ed437..60a7cd18d20 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -113,7 +113,7 @@ func testSingleConsumerHappyPath( }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, requestID1, subID, uni.backend, db, vrfVersion) + mine(t, requestID1, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. // In particular: @@ -133,7 +133,7 @@ func testSingleConsumerHappyPath( t.Log("runs", len(runs)) return len(runs) == 2 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mine(t, requestID2, subID, uni.backend, db, vrfVersion) + mine(t, requestID2, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. // In particular: @@ -285,7 +285,7 @@ func testMultipleConsumersNeedBHS( return len(runs) == 1 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mine(t, requestID, subID, uni.backend, db, vrfVersion) + mine(t, requestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) rwfe := assertRandomWordsFulfilled(t, requestID, true, coordinator, nativePayment) if len(assertions) > 0 { @@ -446,7 +446,7 @@ func testMultipleConsumersNeedTrustedBHS( return len(runs) == 1 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mine(t, requestID, subID, uni.backend, db, vrfVersion) + mine(t, requestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) rwfe := assertRandomWordsFulfilled(t, requestID, true, coordinator, nativePayment) if len(assertions) > 0 { @@ -592,7 +592,7 @@ func testSingleConsumerHappyPathBatchFulfillment( return len(runs) == numRequests }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mineBatch(t, reqIDs, subID, uni.backend, db, vrfVersion) + mineBatch(t, reqIDs, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) for i, requestID := range reqIDs { // Assert correct state of RandomWordsFulfilled event. @@ -694,7 +694,7 @@ func testSingleConsumerNeedsTopUp( // Mine the fulfillment. Need to wait for Txm to mark the tx as confirmed // so that we can actually see the event on the simulated chain. - mine(t, requestID, subID, uni.backend, db, vrfVersion) + mine(t, requestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert the state of the RandomWordsFulfilled event. rwfe := assertRandomWordsFulfilled(t, requestID, true, coordinator, nativePayment) @@ -818,7 +818,7 @@ func testBlockHeaderFeeder( return len(runs) == 1 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mine(t, requestID, subID, uni.backend, db, vrfVersion) + mine(t, requestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) rwfe := assertRandomWordsFulfilled(t, requestID, true, coordinator, nativePayment) if len(assertions) > 0 { @@ -1021,7 +1021,7 @@ func testSingleConsumerForcedFulfillment( }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, requestID, subID, uni.backend, db, vrfVersion) + mine(t, requestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. // In this particular case: @@ -1264,7 +1264,7 @@ func testSingleConsumerBigGasCallbackSandwich( }, 3*time.Second, 1*time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, reqIDs[1], subID, uni.backend, db, vrfVersion) + mine(t, reqIDs[1], subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert the random word was fulfilled assertRandomWordsFulfilled(t, reqIDs[1], false, uni.rootContract, nativePayment) @@ -1365,7 +1365,7 @@ func testSingleConsumerMultipleGasLanes( }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, cheapRequestID, subID, uni.backend, db, vrfVersion) + mine(t, cheapRequestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. assertRandomWordsFulfilled(t, cheapRequestID, true, uni.rootContract, nativePayment) @@ -1397,7 +1397,7 @@ func testSingleConsumerMultipleGasLanes( }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, expensiveRequestID, subID, uni.backend, db, vrfVersion) + mine(t, expensiveRequestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. assertRandomWordsFulfilled(t, expensiveRequestID, true, uni.rootContract, nativePayment) @@ -1477,7 +1477,7 @@ func testSingleConsumerAlwaysRevertingCallbackStillFulfilled( }, testutils.WaitTimeout(t), 1*time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, requestID, subID, uni.backend, db, vrfVersion) + mine(t, requestID, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. assertRandomWordsFulfilled(t, requestID, false, uni.rootContract, nativePayment) @@ -1552,7 +1552,7 @@ func testConsumerProxyHappyPath( }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, requestID1, subID, uni.backend, db, vrfVersion) + mine(t, requestID1, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. assertRandomWordsFulfilled(t, requestID1, true, uni.rootContract, nativePayment) @@ -1576,7 +1576,7 @@ func testConsumerProxyHappyPath( t.Log("runs", len(runs)) return len(runs) == 2 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mine(t, requestID2, subID, uni.backend, db, vrfVersion) + mine(t, requestID2, subID, uni.backend, db, vrfVersion, testutils.SimulatedChainID) assertRandomWordsFulfilled(t, requestID2, true, uni.rootContract, nativePayment) // Assert correct number of random words sent by coordinator. diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 6d2b77acb7d..094d7d060e4 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -1200,7 +1200,7 @@ func TestVRFV2PlusIntegration_Migration(t *testing.T) { return len(runs) == 1 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) - mine(t, requestID, subID, uni.backend, db, vrfcommon.V2Plus) + mine(t, requestID, subID, uni.backend, db, vrfcommon.V2Plus, testutils.SimulatedChainID) assertRandomWordsFulfilled(t, requestID, true, uni.rootContract, false) // Assert correct number of random words sent by coordinator. diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 093adc8eaaf..3a691ec2e2a 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -24,6 +24,7 @@ import ( "github.com/onsi/gomega" "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" @@ -32,6 +33,8 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmlogger "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" @@ -58,6 +61,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "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/services/job" @@ -67,9 +72,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" - "github.com/smartcontractkit/chainlink/v2/core/services/vrf" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/proof" + v1 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v1" v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" @@ -127,24 +133,14 @@ type coordinatorV2Universe struct { batchCoordinatorContractAddress common.Address } -const ( - ConfirmedEthTxesV2Query = `SELECT * FROM evm.txes - WHERE evm.txes.state = 'confirmed' - AND evm.txes.meta->>'RequestID' = $1 - AND CAST(evm.txes.meta->>'SubId' AS NUMERIC) = $2 LIMIT 1` - ConfirmedEthTxesV2PlusQuery = `SELECT * FROM evm.txes - WHERE evm.txes.state = 'confirmed' - AND evm.txes.meta->>'RequestID' = $1 - AND CAST(evm.txes.meta->>'GlobalSubId' AS NUMERIC) = $2 LIMIT 1` - ConfirmedEthTxesV2BatchQuery = ` - SELECT * FROM evm.txes - WHERE evm.txes.state = 'confirmed' - AND CAST(evm.txes.meta->>'SubId' AS NUMERIC) = $1` - ConfirmedEthTxesV2PlusBatchQuery = ` - SELECT * FROM evm.txes - WHERE evm.txes.state = 'confirmed' - AND CAST(evm.txes.meta->>'GlobalSubId' AS NUMERIC) = $1` -) +func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.Master, ec *evmclimocks.Client) txmgrcommon.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] { + _, _, evmConfig := txmgr.MakeTestConfigs(t) + txmConfig := txmgr.NewEvmTxmConfig(evmConfig) + txm := txmgr.NewEvmTxm(ec.ConfiguredChainID(), txmConfig, evmConfig.Transactions(), keyStore.Eth(), logger.TestLogger(t), nil, nil, + nil, txStore, nil, nil, nil, nil) + + return txm +} func newVRFCoordinatorV2Universe(t *testing.T, key ethkey.KeyV2, numConsumers int) coordinatorV2Universe { testutils.SkipShort(t, "VRFCoordinatorV2Universe") @@ -454,7 +450,7 @@ func sendEth(t *testing.T, key ethkey.KeyV2, ec *backends.SimulatedBackend, to c nonce, err := ec.PendingNonceAt(testutils.Context(t), key.Address) require.NoError(t, err) tx := gethtypes.NewTx(&gethtypes.DynamicFeeTx{ - ChainID: big.NewInt(1337), + ChainID: testutils.SimulatedChainID, Nonce: nonce, GasTipCap: big.NewInt(1), GasFeeCap: assets.GWei(10).ToInt(), // block base fee in sim @@ -463,7 +459,7 @@ func sendEth(t *testing.T, key ethkey.KeyV2, ec *backends.SimulatedBackend, to c Value: big.NewInt(0).Mul(big.NewInt(int64(eth)), big.NewInt(1e18)), Data: nil, }) - signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(big.NewInt(1337)), key.ToEcdsaPrivKey()) + signedTx, err := gethtypes.SignTx(tx, gethtypes.NewLondonSigner(testutils.SimulatedChainID), key.ToEcdsaPrivKey()) require.NoError(t, err) err = ec.SendTransaction(testutils.Context(t), signedTx) require.NoError(t, err) @@ -762,32 +758,42 @@ func assertNumRandomWords( } } -func mine(t *testing.T, requestID, subID *big.Int, backend *backends.SimulatedBackend, db *sqlx.DB, vrfVersion vrfcommon.Version) bool { - var query string +func mine(t *testing.T, requestID, subID *big.Int, backend *backends.SimulatedBackend, db *sqlx.DB, vrfVersion vrfcommon.Version, chainId *big.Int) bool { + cfg := pgtest.NewQConfig(false) + txstore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + var metaField string if vrfVersion == vrfcommon.V2Plus { - query = ConfirmedEthTxesV2PlusQuery + metaField = "GlobalSubId" } else if vrfVersion == vrfcommon.V2 { - query = ConfirmedEthTxesV2Query + metaField = "SubId" } else { t.Errorf("unsupported vrf version %s", vrfVersion) } + return gomega.NewWithT(t).Eventually(func() bool { backend.Commit() - var txs []txmgr.DbEthTx - err := db.Select(&txs, query, common.BytesToHash(requestID.Bytes()).String(), subID.String()) + txes, err := txstore.FindTxesByMetaFieldAndStates(testutils.Context(t), metaField, subID.String(), []txmgrtypes.TxState{txmgrcommon.TxConfirmed}, chainId) require.NoError(t, err) - t.Log("num txs", len(txs)) - return len(txs) == 1 + for _, tx := range txes { + meta, err := tx.GetMeta() + require.NoError(t, err) + if meta.RequestID.String() == common.BytesToHash(requestID.Bytes()).String() { + return true + } + } + return false }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) } -func mineBatch(t *testing.T, requestIDs []*big.Int, subID *big.Int, backend *backends.SimulatedBackend, db *sqlx.DB, vrfVersion vrfcommon.Version) bool { +func mineBatch(t *testing.T, requestIDs []*big.Int, subID *big.Int, backend *backends.SimulatedBackend, db *sqlx.DB, vrfVersion vrfcommon.Version, chainId *big.Int) bool { requestIDMap := map[string]bool{} - var query string + cfg := pgtest.NewQConfig(false) + txstore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + var metaField string if vrfVersion == vrfcommon.V2Plus { - query = ConfirmedEthTxesV2PlusBatchQuery + metaField = "GlobalSubId" } else if vrfVersion == vrfcommon.V2 { - query = ConfirmedEthTxesV2BatchQuery + metaField = "SubId" } else { t.Errorf("unsupported vrf version %s", vrfVersion) } @@ -796,12 +802,10 @@ func mineBatch(t *testing.T, requestIDs []*big.Int, subID *big.Int, backend *bac } return gomega.NewWithT(t).Eventually(func() bool { backend.Commit() - var txs []txmgr.DbEthTx - require.NoError(t, db.Select(&txs, query, subID.String())) - for _, tx := range txs { - var evmTx txmgr.Tx - tx.ToTx(&evmTx) - meta, err := evmTx.GetMeta() + txes, err := txstore.FindTxesByMetaFieldAndStates(testutils.Context(t), metaField, subID.String(), []txmgrtypes.TxState{txmgrcommon.TxConfirmed}, chainId) + require.NoError(t, err) + for _, tx := range txes { + meta, err := tx.GetMeta() require.NoError(t, err) for _, requestID := range meta.RequestIDs { if _, ok := requestIDMap[requestID.String()]; ok { @@ -1188,7 +1192,7 @@ func TestVRFV2Integration_SingleConsumer_Wrapper(t *testing.T) { }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, requestID, new(big.Int).SetUint64(wrapperSubID), uni.backend, db, vrfcommon.V2) + mine(t, requestID, new(big.Int).SetUint64(wrapperSubID), uni.backend, db, vrfcommon.V2, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. assertRandomWordsFulfilled(t, requestID, true, uni.rootContract, false) @@ -1268,7 +1272,7 @@ func TestVRFV2Integration_Wrapper_High_Gas(t *testing.T) { }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) // Mine the fulfillment that was queued. - mine(t, requestID, new(big.Int).SetUint64(wrapperSubID), uni.backend, db, vrfcommon.V2) + mine(t, requestID, new(big.Int).SetUint64(wrapperSubID), uni.backend, db, vrfcommon.V2, testutils.SimulatedChainID) // Assert correct state of RandomWordsFulfilled event. assertRandomWordsFulfilled(t, requestID, true, uni.rootContract, false) @@ -1599,6 +1603,10 @@ func TestIntegrationVRFV2(t *testing.T) { require.Zero(t, key.Cmp(keys[0])) require.NoError(t, app.Start(testutils.Context(t))) + var chain evm.Chain + chain, err = app.GetRelayers().LegacyEVMChains().Get(testutils.SimulatedChainID.String()) + require.NoError(t, err) + listenerV2 := v22.MakeTestListenerV2(chain) jbs := createVRFJobs( t, @@ -1751,11 +1759,10 @@ func TestIntegrationVRFV2(t *testing.T) { }) // We should see the response count present - chain, err := app.GetRelayers().LegacyEVMChains().Get(big.NewInt(1337).String()) require.NoError(t, err) - - q := pg.NewQ(app.GetSqlxDB(), app.Logger, app.Config.Database()) - counts := vrf.GetStartingResponseCountsV2(q, app.Logger, chain.Client().ConfiguredChainID().Uint64(), chain.Config().EVM().FinalityDepth()) + var counts map[string]uint64 + counts, err = listenerV2.GetStartingResponseCountsV2(testutils.Context(t)) + require.NoError(t, err) t.Log(counts, rf[0].RequestID().String()) assert.Equal(t, uint64(1), counts[rf[0].RequestID().String()]) } @@ -1997,19 +2004,30 @@ func TestFulfillmentCost(t *testing.T) { func TestStartingCountsV1(t *testing.T) { cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "vrf_test_starting_counts", nil) - _, err := db.Exec(`INSERT INTO evm.heads (hash, number, parent_hash, created_at, timestamp, evm_chain_id) - VALUES ($1, 4, $2, NOW(), NOW(), 1337)`, utils.NewHash(), utils.NewHash()) - require.NoError(t, err) lggr := logger.TestLogger(t) - q := pg.NewQ(db, lggr, cfg.Database()) - finalityDepth := 3 - counts := vrf.GetStartingResponseCountsV1(q, lggr, 1337, uint32(finalityDepth)) - assert.Equal(t, 0, len(counts)) + qCfg := pgtest.NewQConfig(false) + txStore := txmgr.NewTxStore(db, logger.TestLogger(t), qCfg) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg.Database()) + ec := evmclimocks.NewClient(t) + ec.On("ConfiguredChainID").Return(testutils.SimulatedChainID) + ec.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(2), nil).Maybe() + txm := makeTestTxm(t, txStore, ks, ec) + relayExtenders := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{KeyStore: ks.Eth(), Client: ec, DB: db, GeneralConfig: cfg, TxManager: txm}) + legacyChains := evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) + chain, err := legacyChains.Get(testutils.SimulatedChainID.String()) + require.NoError(t, err) + listenerV1 := &v1.Listener{ + Chain: chain, + } + listenerV2 := v22.MakeTestListenerV2(chain) + var counts map[[32]byte]uint64 + counts, err = listenerV1.GetStartingResponseCountsV1(testutils.Context(t)) + require.NoError(t, err) + assert.Equal(t, 0, len(counts)) err = ks.Unlock(testutils.Password) require.NoError(t, err) - k, err := ks.Eth().Create(big.NewInt(1337)) + k, err := ks.Eth().Create(testutils.SimulatedChainID) require.NoError(t, err) b := time.Now() n1, n2, n3, n4 := evmtypes.Nonce(0), evmtypes.Nonce(1), evmtypes.Nonce(2), evmtypes.Nonce(3) @@ -2027,7 +2045,7 @@ func TestStartingCountsV1(t *testing.T) { md2, err := json.Marshal(&m2) md2_ := datatypes.JSON(md2) require.NoError(t, err) - chainID := utils.NewBig(big.NewInt(1337)) + chainID := utils.NewBig(testutils.SimulatedChainID) confirmedTxes := []txmgr.Tx{ { Sequence: &n1, @@ -2100,16 +2118,13 @@ func TestStartingCountsV1(t *testing.T) { ChainID: chainID.ToInt(), }) } - sql := `INSERT INTO evm.txes (nonce, from_address, to_address, encoded_payload, value, gas_limit, state, created_at, broadcast_at, initial_broadcast_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id) -VALUES (:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit, :state, :created_at, :broadcast_at, :initial_broadcast_at, :meta, :subject, :evm_chain_id, :min_confirmations, :pipeline_task_run_id);` - for _, tx := range append(confirmedTxes, unconfirmedTxes...) { - var dbEtx txmgr.DbEthTx - dbEtx.FromTx(&tx) //nolint:gosec // just copying fields - _, err = db.NamedExec(sql, &dbEtx) + txList := append(confirmedTxes, unconfirmedTxes...) + for i := range txList { + err = txStore.InsertTx(&txList[i]) require.NoError(t, err) } - // add evm.tx_attempts for confirmed + // add tx attempt for confirmed broadcastBlock := int64(1) var txAttempts []txmgr.TxAttempt for i := range confirmedTxes { @@ -2124,7 +2139,7 @@ VALUES (:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit ChainSpecificFeeLimit: uint32(100), }) } - // add evm.tx_attempts for unconfirmed + // add tx attempt for unconfirmed for i := range unconfirmedTxes { txAttempts = append(txAttempts, txmgr.TxAttempt{ TxID: int64(i + 1 + len(confirmedTxes)), @@ -2139,41 +2154,35 @@ VALUES (:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit for _, txAttempt := range txAttempts { t.Log("tx attempt eth tx id: ", txAttempt.TxID) } - sql = `INSERT INTO evm.tx_attempts (eth_tx_id, gas_price, signed_raw_tx, hash, state, created_at, chain_specific_gas_limit) - VALUES (:eth_tx_id, :gas_price, :signed_raw_tx, :hash, :state, :created_at, :chain_specific_gas_limit)` - for _, attempt := range txAttempts { - var dbAttempt txmgr.DbEthTxAttempt - dbAttempt.FromTxAttempt(&attempt) //nolint:gosec // just copying fields - _, err = db.NamedExec(sql, &dbAttempt) + for i := range txAttempts { + err = txStore.InsertTxAttempt(&txAttempts[i]) require.NoError(t, err) } // add evm.receipts - receipts := []txmgr.Receipt{} + receipts := []evmtypes.Receipt{} for i := 0; i < 4; i++ { - receipts = append(receipts, txmgr.Receipt{ + receipts = append(receipts, evmtypes.Receipt{ BlockHash: utils.NewHash(), TxHash: txAttempts[i].Hash, - BlockNumber: broadcastBlock, + BlockNumber: big.NewInt(broadcastBlock), TransactionIndex: 1, - Receipt: evmtypes.Receipt{}, - CreatedAt: time.Now(), }) } - sql = `INSERT INTO evm.receipts (block_hash, tx_hash, block_number, transaction_index, receipt, created_at) - VALUES (:block_hash, :tx_hash, :block_number, :transaction_index, :receipt, :created_at)` - for _, r := range receipts { - _, err2 := db.NamedExec(sql, r) - require.NoError(t, err2) + for i := range receipts { + _, err = txStore.InsertReceipt(&receipts[i]) + require.NoError(t, err) } - counts = vrf.GetStartingResponseCountsV1(q, lggr, 1337, uint32(finalityDepth)) + counts, err = listenerV1.GetStartingResponseCountsV1(testutils.Context(t)) + require.NoError(t, err) assert.Equal(t, 3, len(counts)) assert.Equal(t, uint64(1), counts[utils.PadByteToHash(0x10)]) assert.Equal(t, uint64(2), counts[utils.PadByteToHash(0x11)]) assert.Equal(t, uint64(2), counts[utils.PadByteToHash(0x12)]) - countsV2 := vrf.GetStartingResponseCountsV2(q, lggr, 1337, uint32(finalityDepth)) + countsV2, err := listenerV2.GetStartingResponseCountsV2(testutils.Context(t)) + require.NoError(t, err) t.Log(countsV2) assert.Equal(t, 3, len(countsV2)) assert.Equal(t, uint64(1), countsV2[big.NewInt(0x10).String()]) diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 8bac485d656..17cb9ec96e4 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -4,6 +4,7 @@ import ( "cmp" "context" "database/sql" + "encoding/hex" "fmt" "math" "math/big" @@ -12,6 +13,7 @@ import ( "sync" "time" + "github.com/avast/retry-go/v4" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -27,8 +29,7 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/assets" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -56,6 +57,8 @@ var ( batchCoordinatorV2ABI = evmtypes.MustGetABI(batch_vrf_coordinator_v2.BatchVRFCoordinatorV2ABI) batchCoordinatorV2PlusABI = evmtypes.MustGetABI(batch_vrf_coordinator_v2plus.BatchVRFCoordinatorV2PlusABI) vrfOwnerABI = evmtypes.MustGetABI(vrf_owner.VRFOwnerMetaData.ABI) + // These are the transaction states used when summing up already reserved subscription funds that are about to be used in in-flight transactions + reserveEthLinkQueryStates = []txmgrtypes.TxState{txmgrcommon.TxUnconfirmed, txmgrcommon.TxUnstarted, txmgrcommon.TxInProgress} ) const ( @@ -73,29 +76,8 @@ const ( // backoffFactor is the factor by which to increase the delay each time a request fails. backoffFactor = 1.3 - V2ReservedLinkQuery = `SELECT SUM(CAST(meta->>'MaxLink' AS NUMERIC(78, 0))) - FROM evm.txes - WHERE meta->>'MaxLink' IS NOT NULL - AND evm_chain_id = $1 - AND CAST(meta->>'SubId' AS NUMERIC) = $2 - AND state IN ('unconfirmed', 'unstarted', 'in_progress') - GROUP BY meta->>'SubId'` - - V2PlusReservedLinkQuery = `SELECT SUM(CAST(meta->>'MaxLink' AS NUMERIC(78, 0))) - FROM evm.txes - WHERE meta->>'MaxLink' IS NOT NULL - AND evm_chain_id = $1 - AND CAST(meta->>'GlobalSubId' AS NUMERIC) = $2 - AND state IN ('unconfirmed', 'unstarted', 'in_progress') - GROUP BY meta->>'GlobalSubId'` - - V2PlusReservedEthQuery = `SELECT SUM(CAST(meta->>'MaxEth' AS NUMERIC(78, 0))) - FROM evm.txes - WHERE meta->>'MaxEth' IS NOT NULL - AND evm_chain_id = $1 - AND CAST(meta->>'GlobalSubId' AS NUMERIC) = $2 - AND state IN ('unconfirmed', 'unstarted', 'in_progress') - GROUP BY meta->>'GlobalSubId'` + txMetaFieldSubId = "SubId" + txMetaGlobalSubId = "GlobalSubId" CouldNotDetermineIfLogConsumedMsg = "Could not determine if log was already consumed" ) @@ -116,33 +98,27 @@ func New( cfg vrfcommon.Config, feeCfg vrfcommon.FeeConfig, l logger.Logger, - ethClient evmclient.Client, + chain evm.Chain, chainID *big.Int, - logBroadcaster log.Broadcaster, q pg.Q, coordinator CoordinatorV2_X, batchCoordinator batch_vrf_coordinator_v2.BatchVRFCoordinatorV2Interface, vrfOwner vrf_owner.VRFOwnerInterface, aggregator *aggregator_v3_interface.AggregatorV3Interface, - txm txmgr.TxManager, pipelineRunner pipeline.Runner, gethks keystore.Eth, job job.Job, mailMon *utils.MailboxMonitor, reqLogs *utils.Mailbox[log.Broadcast], reqAdded func(), - respCount map[string]uint64, - headBroadcaster httypes.HeadBroadcasterRegistry, deduper *vrfcommon.LogDeduper, ) job.ServiceCtx { return &listenerV2{ cfg: cfg, feeCfg: feeCfg, l: logger.Sugared(l), - ethClient: ethClient, + chain: chain, chainID: chainID, - logBroadcaster: logBroadcaster, - txm: txm, mailMon: mailMon, coordinator: coordinator, batchCoordinator: batchCoordinator, @@ -154,9 +130,7 @@ func New( reqLogs: reqLogs, chStop: make(chan struct{}), reqAdded: reqAdded, - respCount: respCount, blockNumberToReqID: pairing.New(), - headBroadcaster: headBroadcaster, latestHeadMu: sync.RWMutex{}, wg: &sync.WaitGroup{}, aggregator: aggregator, @@ -193,14 +167,12 @@ type vrfPipelineResult struct { type listenerV2 struct { services.StateMachine - cfg vrfcommon.Config - feeCfg vrfcommon.FeeConfig - l logger.SugaredLogger - ethClient evmclient.Client - chainID *big.Int - logBroadcaster log.Broadcaster - txm txmgr.TxManager - mailMon *utils.MailboxMonitor + cfg vrfcommon.Config + feeCfg vrfcommon.FeeConfig + l logger.SugaredLogger + chain evm.Chain + chainID *big.Int + mailMon *utils.MailboxMonitor coordinator CoordinatorV2_X batchCoordinator batch_vrf_coordinator_v2.BatchVRFCoordinatorV2Interface @@ -229,7 +201,6 @@ type listenerV2 struct { blockNumberToReqID *pairing.PairHeap // head tracking data structures - headBroadcaster httypes.HeadBroadcasterRegistry latestHeadMu sync.RWMutex latestHeadNumber uint64 @@ -273,7 +244,7 @@ func (lsn *listenerV2) Start(ctx context.Context) error { spec := job.LoadDefaultVRFPollPeriod(*lsn.job.VRFSpec) - unsubscribeLogs := lsn.logBroadcaster.Register(lsn, log.ListenerOpts{ + unsubscribeLogs := lsn.chain.LogBroadcaster().Register(lsn, log.ListenerOpts{ Contract: lsn.coordinator.Address(), ParseLog: lsn.coordinator.ParseLog, LogsWithTopics: lsn.coordinator.LogsWithTopics(spec.PublicKey.MustHash()), @@ -284,11 +255,20 @@ func (lsn *listenerV2) Start(ctx context.Context) error { ReplayStartedCallback: lsn.ReplayStartedCallback, }) - latestHead, unsubscribeHeadBroadcaster := lsn.headBroadcaster.Subscribe(lsn) + latestHead, unsubscribeHeadBroadcaster := lsn.chain.HeadBroadcaster().Subscribe(lsn) if latestHead != nil { lsn.setLatestHead(latestHead) } + lsn.respCountMu.Lock() + defer lsn.respCountMu.Unlock() + var respCount map[string]uint64 + respCount, err = lsn.GetStartingResponseCountsV2(ctx) + if err != nil { + return err + } + lsn.respCount = respCount + // Log listener gathers request logs lsn.wg.Add(1) go func() { @@ -306,6 +286,46 @@ func (lsn *listenerV2) Start(ctx context.Context) error { }) } +func (lsn *listenerV2) GetStartingResponseCountsV2(ctx context.Context) (respCount map[string]uint64, err error) { + respCounts := map[string]uint64{} + var latestBlockNum *big.Int + // Retry client call for LatestBlockHeight if fails + // Want to avoid failing startup due to potential faulty RPC call + err = retry.Do(func() error { + latestBlockNum, err = lsn.chain.Client().LatestBlockHeight(ctx) + return err + }, retry.Attempts(10), retry.Delay(500*time.Millisecond)) + if err != nil { + return nil, err + } + if latestBlockNum == nil { + return nil, errors.New("LatestBlockHeight return nil block num") + } + confirmedBlockNum := latestBlockNum.Int64() - int64(lsn.chain.Config().EVM().FinalityDepth()) + // Only check as far back as the evm finality depth for completed transactions. + var counts []vrfcommon.RespCountEntry + counts, err = vrfcommon.GetRespCounts(ctx, lsn.chain.TxManager(), lsn.chainID, confirmedBlockNum) + if err != nil { + // Continue with an empty map, do not block job on this. + lsn.l.Errorw("Unable to read previous confirmed fulfillments", "err", err) + return respCounts, nil + } + + for _, c := range counts { + // Remove the quotes from the json + req := strings.Replace(c.RequestID, `"`, ``, 2) + // Remove the 0x prefix + b, err := hex.DecodeString(req[2:]) + if err != nil { + lsn.l.Errorw("Unable to read fulfillment", "err", err, "reqID", c.RequestID) + continue + } + bi := new(big.Int).SetBytes(b) + respCounts[bi.String()] = uint64(c.Count) + } + return respCounts, nil +} + func (lsn *listenerV2) setLatestHead(head *evmtypes.Head) { lsn.latestHeadMu.Lock() defer lsn.latestHeadMu.Unlock() @@ -520,68 +540,80 @@ func (lsn *listenerV2) processPendingVRFRequests(ctx context.Context) { // MaybeSubtractReservedLink figures out how much LINK is reserved for other VRF requests that // have not been fully confirmed yet on-chain, and subtracts that from the given startBalance, // and returns that value if there are no errors. -func MaybeSubtractReservedLink(q pg.Q, startBalance *big.Int, chainID uint64, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { - var ( - reservedLink string - query string - ) +func (lsn *listenerV2) MaybeSubtractReservedLink(ctx context.Context, startBalance *big.Int, chainID *big.Int, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { + var metaField string if vrfVersion == vrfcommon.V2Plus { - query = V2PlusReservedLinkQuery + metaField = txMetaGlobalSubId } else if vrfVersion == vrfcommon.V2 { - query = V2ReservedLinkQuery + metaField = txMetaFieldSubId } else { return nil, errors.Errorf("unsupported vrf version %s", vrfVersion) } - err := q.Get(&reservedLink, query, chainID, subID.String()) + txes, err := lsn.chain.TxManager().FindTxesByMetaFieldAndStates(ctx, metaField, subID.String(), reserveEthLinkQueryStates, chainID) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, errors.Wrap(err, "getting reserved LINK") + return nil, errors.Wrap(err, "TXM FindTxesByMetaFieldAndStates failed") } - if reservedLink != "" { - reservedLinkInt, success := big.NewInt(0).SetString(reservedLink, 10) - if !success { - return nil, fmt.Errorf("converting reserved LINK %s", reservedLink) + reservedLinkSum := big.NewInt(0) + // Aggregate non-null MaxLink from all txes returned + for _, tx := range txes { + var meta *txmgrtypes.TxMeta[common.Address, common.Hash] + meta, err = tx.GetMeta() + if err != nil { + return nil, errors.Wrap(err, "GetMeta for Tx failed") } + if meta != nil && meta.MaxLink != nil { + txMaxLink, success := new(big.Int).SetString(*meta.MaxLink, 10) + if !success { + return nil, fmt.Errorf("converting reserved LINK %s", *meta.MaxLink) + } - return new(big.Int).Sub(startBalance, reservedLinkInt), nil + reservedLinkSum.Add(reservedLinkSum, txMaxLink) + } } - return new(big.Int).Set(startBalance), nil + return new(big.Int).Sub(startBalance, reservedLinkSum), nil } // MaybeSubtractReservedEth figures out how much ether is reserved for other VRF requests that // have not been fully confirmed yet on-chain, and subtracts that from the given startBalance, // and returns that value if there are no errors. -func MaybeSubtractReservedEth(q pg.Q, startBalance *big.Int, chainID uint64, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { - var ( - reservedEther string - query string - ) +func (lsn *listenerV2) MaybeSubtractReservedEth(ctx context.Context, startBalance *big.Int, chainID *big.Int, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { + var metaField string if vrfVersion == vrfcommon.V2Plus { - query = V2PlusReservedEthQuery + metaField = txMetaGlobalSubId } else if vrfVersion == vrfcommon.V2 { // native payment is not supported for v2, so returning 0 reserved ETH return big.NewInt(0), nil } else { return nil, errors.Errorf("unsupported vrf version %s", vrfVersion) } - err := q.Get(&reservedEther, query, chainID, subID.String()) + txes, err := lsn.chain.TxManager().FindTxesByMetaFieldAndStates(ctx, metaField, subID.String(), reserveEthLinkQueryStates, chainID) if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, errors.Wrap(err, "getting reserved ether") + return nil, errors.Wrap(err, "TXM FindTxesByMetaFieldAndStates failed") } - if reservedEther != "" { - reservedEtherInt, success := big.NewInt(0).SetString(reservedEther, 10) - if !success { - return nil, fmt.Errorf("converting reserved ether %s", reservedEther) + reservedEthSum := big.NewInt(0) + // Aggregate non-null MaxEth from all txes returned + for _, tx := range txes { + var meta *txmgrtypes.TxMeta[common.Address, common.Hash] + meta, err = tx.GetMeta() + if err != nil { + return nil, errors.Wrap(err, "GetMeta for Tx failed") } + if meta != nil && meta.MaxEth != nil { + txMaxEth, success := new(big.Int).SetString(*meta.MaxEth, 10) + if !success { + return nil, fmt.Errorf("converting reserved ETH %s", *meta.MaxEth) + } - return new(big.Int).Sub(startBalance, reservedEtherInt), nil + reservedEthSum.Add(reservedEthSum, txMaxEth) + } } if startBalance != nil { - return new(big.Int).Set(startBalance), nil + return new(big.Int).Sub(startBalance, reservedEthSum), nil } return big.NewInt(0), nil } @@ -812,14 +844,14 @@ func (lsn *listenerV2) processRequestsPerSubBatch( subIsActive bool, ) map[string]struct{} { var processed = make(map[string]struct{}) - startBalanceNoReserveLink, err := MaybeSubtractReservedLink( - lsn.q, startLinkBalance, lsn.chainID.Uint64(), subID, lsn.coordinator.Version()) + startBalanceNoReserveLink, err := lsn.MaybeSubtractReservedLink( + ctx, startLinkBalance, lsn.chainID, subID, lsn.coordinator.Version()) if err != nil { lsn.l.Errorw("Couldn't get reserved LINK for subscription", "sub", reqs[0].req.SubID(), "err", err) return processed } - startBalanceNoReserveEth, err := MaybeSubtractReservedEth( - lsn.q, startEthBalance, lsn.chainID.Uint64(), subID, lsn.coordinator.Version()) + startBalanceNoReserveEth, err := lsn.MaybeSubtractReservedEth( + ctx, startEthBalance, lsn.chainID, subID, lsn.coordinator.Version()) if err != nil { lsn.l.Errorw("Couldn't get reserved ether for subscription", "sub", reqs[0].req.SubID(), "err", err) return processed @@ -883,7 +915,7 @@ func (lsn *listenerV2) enqueueForceFulfillment( // fulfill the request through the VRF owner err = lsn.q.Transaction(func(tx pg.Queryer) error { - if err = lsn.logBroadcaster.MarkConsumed(p.req.lb, pg.WithQueryer(tx)); err != nil { + if err = lsn.chain.LogBroadcaster().MarkConsumed(p.req.lb, pg.WithQueryer(tx)); err != nil { return err } @@ -901,7 +933,7 @@ func (lsn *listenerV2) enqueueForceFulfillment( if err != nil { return errors.Wrap(err, "abi pack VRFOwner.fulfillRandomWords") } - estimateGasLimit, err := lsn.ethClient.EstimateGas(ctx, ethereum.CallMsg{ + estimateGasLimit, err := lsn.chain.Client().EstimateGas(ctx, ethereum.CallMsg{ From: fromAddress, To: &vrfOwnerAddressSpec, Data: txData, @@ -919,7 +951,7 @@ func (lsn *listenerV2) enqueueForceFulfillment( requestID := common.BytesToHash(p.req.req.RequestID().Bytes()) subID := p.req.req.SubID() requestTxHash := p.req.req.Raw().TxHash - etx, err = lsn.txm.CreateTransaction(ctx, txmgr.TxRequest{ + etx, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ FromAddress: fromAddress, ToAddress: lsn.vrfOwner.Address(), EncodedPayload: txData, @@ -943,7 +975,7 @@ func (lsn *listenerV2) enqueueForceFulfillment( func (lsn *listenerV2) isConsumerValidAfterFinalityDepthElapsed(ctx context.Context, req pendingRequest) bool { latestHead := lsn.getLatestHead() if latestHead-req.req.Raw().BlockNumber > uint64(lsn.cfg.FinalityDepth()) { - code, err := lsn.ethClient.CodeAt(ctx, req.req.Sender(), big.NewInt(int64(latestHead))) + code, err := lsn.chain.Client().CodeAt(ctx, req.req.Sender(), big.NewInt(int64(latestHead))) if err != nil { lsn.l.Warnw("Failed to fetch contract code", "err", err) return true // error fetching code, give the benefit of doubt to the consumer @@ -1103,7 +1135,7 @@ func (lsn *listenerV2) processRequestsPerSubHelper( if err = lsn.pipelineRunner.InsertFinishedRun(p.run, true, pg.WithQueryer(tx)); err != nil { return err } - if err = lsn.logBroadcaster.MarkConsumed(p.req.lb, pg.WithQueryer(tx)); err != nil { + if err = lsn.chain.LogBroadcaster().MarkConsumed(p.req.lb, pg.WithQueryer(tx)); err != nil { return err } @@ -1126,7 +1158,7 @@ func (lsn *listenerV2) processRequestsPerSubHelper( requestID := common.BytesToHash(p.req.req.RequestID().Bytes()) coordinatorAddress := lsn.coordinator.Address() requestTxHash := p.req.req.Raw().TxHash - transaction, err = lsn.txm.CreateTransaction(ctx, txmgr.TxRequest{ + transaction, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ FromAddress: fromAddress, ToAddress: lsn.coordinator.Address(), EncodedPayload: hexutil.MustDecode(p.payload), @@ -1185,15 +1217,15 @@ func (lsn *listenerV2) processRequestsPerSub( } var processed = make(map[string]struct{}) - chainId := lsn.ethClient.ConfiguredChainID() - startBalanceNoReserveLink, err := MaybeSubtractReservedLink( - lsn.q, startLinkBalance, chainId.Uint64(), subID, lsn.coordinator.Version()) + chainId := lsn.chain.Client().ConfiguredChainID() + startBalanceNoReserveLink, err := lsn.MaybeSubtractReservedLink( + ctx, startLinkBalance, chainId, subID, lsn.coordinator.Version()) if err != nil { lsn.l.Errorw("Couldn't get reserved LINK for subscription", "sub", reqs[0].req.SubID(), "err", err) return processed } - startBalanceNoReserveEth, err := MaybeSubtractReservedEth( - lsn.q, startEthBalance, lsn.chainID.Uint64(), subID, lsn.coordinator.Version()) + startBalanceNoReserveEth, err := lsn.MaybeSubtractReservedEth( + ctx, startEthBalance, lsn.chainID, subID, lsn.coordinator.Version()) if err != nil { lsn.l.Errorw("Couldn't get reserved ETH for subscription", "sub", reqs[0].req.SubID(), "err", err) return processed @@ -1299,7 +1331,7 @@ func (lsn *listenerV2) checkReqsFulfilled(ctx context.Context, l logger.Logger, } } - err := lsn.ethClient.BatchCallContext(ctx, calls) + err := lsn.chain.Client().BatchCallContext(ctx, calls) if err != nil { return fulfilled, errors.Wrap(err, "making batch call") } @@ -1582,7 +1614,7 @@ func (lsn *listenerV2) getConfirmedAt(req RandomWordsRequested, nodeMinConfs uin func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { if v, ok := lb.DecodedLog().(*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled); ok { lsn.l.Debugw("Received fulfilled log", "reqID", v.RequestId, "success", v.Success) - consumed, err := lsn.logBroadcaster.WasAlreadyConsumed(lb) + consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(lb) if err != nil { lsn.l.Errorw(CouldNotDetermineIfLogConsumedMsg, "err", err, "txHash", lb.RawLog().TxHash) return @@ -1602,7 +1634,7 @@ func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { if v, ok := lb.DecodedLog().(*vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalRandomWordsFulfilled); ok { lsn.l.Debugw("Received fulfilled log", "reqID", v.RequestId, "success", v.Success) - consumed, err := lsn.logBroadcaster.WasAlreadyConsumed(lb) + consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(lb) if err != nil { lsn.l.Errorw(CouldNotDetermineIfLogConsumedMsg, "err", err, "txHash", lb.RawLog().TxHash) return @@ -1623,7 +1655,7 @@ func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { req, err := lsn.coordinator.ParseRandomWordsRequested(lb.RawLog()) if err != nil { lsn.l.Errorw("Failed to parse log", "err", err, "txHash", lb.RawLog().TxHash) - consumed, err := lsn.logBroadcaster.WasAlreadyConsumed(lb) + consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(lb) if err != nil { lsn.l.Errorw(CouldNotDetermineIfLogConsumedMsg, "err", err, "txHash", lb.RawLog().TxHash) return @@ -1648,7 +1680,7 @@ func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { } func (lsn *listenerV2) markLogAsConsumed(lb log.Broadcast) { - err := lsn.logBroadcaster.MarkConsumed(lb) + err := lsn.chain.LogBroadcaster().MarkConsumed(lb) lsn.l.ErrorIf(err, fmt.Sprintf("Unable to mark log %v as consumed", lb.String())) } diff --git a/core/services/vrf/v2/listener_v2_helpers_test.go b/core/services/vrf/v2/listener_v2_helpers_test.go index 8ba900bdc3a..fc34a115b1c 100644 --- a/core/services/vrf/v2/listener_v2_helpers_test.go +++ b/core/services/vrf/v2/listener_v2_helpers_test.go @@ -7,7 +7,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" v2 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" + "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" ) func TestListener_EstimateFeeJuels(t *testing.T) { @@ -29,3 +31,23 @@ func TestListener_EstimateFeeJuels(t *testing.T) { require.Nil(t, actual) require.Error(t, err) } + +func Test_TxListDeduper(t *testing.T) { + tx1 := &txmgr.Tx{ + ID: 1, + Value: *big.NewInt(0), + ChainID: big.NewInt(0), + } + tx2 := &txmgr.Tx{ + ID: 1, + Value: *big.NewInt(1), + ChainID: big.NewInt(0), + } + tx3 := &txmgr.Tx{ + ID: 2, + Value: *big.NewInt(1), + ChainID: big.NewInt(0), + } + txList := vrfcommon.DedupeTxList([]*txmgr.Tx{tx1, tx2, tx3}) + require.Equal(t, len(txList), 2) +} diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index 70d5b8154e0..17615feb63a 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -1,6 +1,7 @@ package v2 import ( + "encoding/json" "math/big" "sync" "testing" @@ -13,39 +14,44 @@ import ( "github.com/stretchr/testify/require" "github.com/theodesp/go-heaps/pairing" - "github.com/smartcontractkit/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" + evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "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" + clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/testdata/testspecs" "github.com/smartcontractkit/chainlink/v2/core/utils" ) -const ( - addEthTxQuery = `INSERT INTO evm.txes (from_address, to_address, encoded_payload, value, gas_limit, state, created_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id) - VALUES ( - $1, $2, $3, $4, $5, $6, NOW(), $7, $8, $9, $10, $11 - ) - RETURNING "txes".*` - - addConfirmedEthTxQuery = `INSERT INTO evm.txes (nonce, broadcast_at, initial_broadcast_at, error, from_address, to_address, encoded_payload, value, gas_limit, state, created_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id) - VALUES ( - $1, NOW(), NOW(), NULL, $2, $3, $4, $5, $6, 'confirmed', NOW(), $7, $8, $9, $10, $11 - ) - RETURNING "txes".*` -) +func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.Master) txmgrcommon.TxManager[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] { + _, _, evmConfig := txmgr.MakeTestConfigs(t) + ec := evmtest.NewEthClientMockWithDefaultChain(t) + txmConfig := txmgr.NewEvmTxmConfig(evmConfig) + txm := txmgr.NewEvmTxm(ec.ConfiguredChainID(), txmConfig, evmConfig.Transactions(), keyStore.Eth(), logger.TestLogger(t), nil, nil, + nil, txStore, nil, nil, nil, nil) + + return txm +} + +func MakeTestListenerV2(chain evm.Chain) *listenerV2 { + return &listenerV2{chainID: chain.Client().ConfiguredChainID(), chain: chain} +} func txMetaSubIDs(t *testing.T, vrfVersion vrfcommon.Version, subID *big.Int) (*uint64, *string) { var ( @@ -62,89 +68,118 @@ func txMetaSubIDs(t *testing.T, vrfVersion vrfcommon.Version, subID *big.Int) (* return txMetaSubID, txMetaGlobalSubID } -func addEthTx(t *testing.T, db *sqlx.DB, from common.Address, state txmgrtypes.TxState, maxLink string, subID *big.Int, reqTxHash common.Hash, vrfVersion vrfcommon.Version) { +func addEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, from common.Address, state txmgrtypes.TxState, maxLink string, subID *big.Int, reqTxHash common.Hash, vrfVersion vrfcommon.Version) { txMetaSubID, txMetaGlobalSubID := txMetaSubIDs(t, vrfVersion, subID) - _, err := db.Exec(addEthTxQuery, - from, // from - from, // to - []byte(`blah`), // payload - 0, // value - 0, // limit - state, - txmgr.TxMeta{ - MaxLink: &maxLink, - SubID: txMetaSubID, - GlobalSubID: txMetaGlobalSubID, - RequestTxHash: &reqTxHash, - }, - uuid.NullUUID{}, - 1337, - 0, // confs - nil) + b, err := json.Marshal(txmgr.TxMeta{ + MaxLink: &maxLink, + SubID: txMetaSubID, + GlobalSubID: txMetaGlobalSubID, + RequestTxHash: &reqTxHash, + }) + require.NoError(t, err) + meta := datatypes.JSON(b) + tx := &txmgr.Tx{ + FromAddress: from, + ToAddress: from, + EncodedPayload: []byte(`blah`), + Value: *big.NewInt(0), + FeeLimit: 0, + State: state, + Meta: &meta, + Subject: uuid.NullUUID{}, + ChainID: testutils.SimulatedChainID, + MinConfirmations: clnull.Uint32{Uint32: 0}, + PipelineTaskRunID: uuid.NullUUID{}, + } + err = txStore.InsertTx(tx) require.NoError(t, err) } -func addConfirmedEthTx(t *testing.T, db *sqlx.DB, from common.Address, maxLink string, subID *big.Int, nonce uint64, vrfVersion vrfcommon.Version) { +func addConfirmedEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, from common.Address, maxLink string, subID *big.Int, nonce evmtypes.Nonce, vrfVersion vrfcommon.Version) { txMetaSubID, txMetaGlobalSubID := txMetaSubIDs(t, vrfVersion, subID) - _, err := db.Exec(addConfirmedEthTxQuery, - nonce, // nonce - from, // from - from, // to - []byte(`blah`), // payload - 0, // value - 0, // limit - txmgr.TxMeta{ - MaxLink: &maxLink, - SubID: txMetaSubID, - GlobalSubID: txMetaGlobalSubID, - }, - uuid.NullUUID{}, - 1337, - 0, // confs - nil) + b, err := json.Marshal(txmgr.TxMeta{ + MaxLink: &maxLink, + SubID: txMetaSubID, + GlobalSubID: txMetaGlobalSubID, + }) + require.NoError(t, err) + meta := datatypes.JSON(b) + now := time.Now() + + tx := &txmgr.Tx{ + Sequence: &nonce, + FromAddress: from, + ToAddress: from, + EncodedPayload: []byte(`blah`), + Value: *big.NewInt(0), + FeeLimit: 0, + State: txmgrcommon.TxConfirmed, + Meta: &meta, + Subject: uuid.NullUUID{}, + ChainID: testutils.SimulatedChainID, + MinConfirmations: clnull.Uint32{Uint32: 0}, + PipelineTaskRunID: uuid.NullUUID{}, + BroadcastAt: &now, + InitialBroadcastAt: &now, + } + err = txStore.InsertTx(tx) require.NoError(t, err) } -func addEthTxNativePayment(t *testing.T, db *sqlx.DB, from common.Address, state txmgrtypes.TxState, maxNative string, subID *big.Int, reqTxHash common.Hash, vrfVersion vrfcommon.Version) { +func addEthTxNativePayment(t *testing.T, txStore txmgr.TestEvmTxStore, from common.Address, state txmgrtypes.TxState, maxNative string, subID *big.Int, reqTxHash common.Hash, vrfVersion vrfcommon.Version) { txMetaSubID, txMetaGlobalSubID := txMetaSubIDs(t, vrfVersion, subID) - _, err := db.Exec(addEthTxQuery, - from, // from - from, // to - []byte(`blah`), // payload - 0, // value - 0, // limit - state, - txmgr.TxMeta{ - MaxEth: &maxNative, - SubID: txMetaSubID, - GlobalSubID: txMetaGlobalSubID, - RequestTxHash: &reqTxHash, - }, - uuid.NullUUID{}, - 1337, - 0, // confs - nil) + b, err := json.Marshal(txmgr.TxMeta{ + MaxEth: &maxNative, + SubID: txMetaSubID, + GlobalSubID: txMetaGlobalSubID, + RequestTxHash: &reqTxHash, + }) + require.NoError(t, err) + meta := datatypes.JSON(b) + tx := &txmgr.Tx{ + FromAddress: from, + ToAddress: from, + EncodedPayload: []byte(`blah`), + Value: *big.NewInt(0), + FeeLimit: 0, + State: state, + Meta: &meta, + Subject: uuid.NullUUID{}, + ChainID: testutils.SimulatedChainID, + MinConfirmations: clnull.Uint32{Uint32: 0}, + PipelineTaskRunID: uuid.NullUUID{}, + } + err = txStore.InsertTx(tx) require.NoError(t, err) } -func addConfirmedEthTxNativePayment(t *testing.T, db *sqlx.DB, from common.Address, maxNative string, subID *big.Int, nonce uint64, vrfVersion vrfcommon.Version) { +func addConfirmedEthTxNativePayment(t *testing.T, txStore txmgr.TestEvmTxStore, from common.Address, maxNative string, subID *big.Int, nonce evmtypes.Nonce, vrfVersion vrfcommon.Version) { txMetaSubID, txMetaGlobalSubID := txMetaSubIDs(t, vrfVersion, subID) - _, err := db.Exec(addConfirmedEthTxQuery, - nonce, // nonce - from, // from - from, // to - []byte(`blah`), // payload - 0, // value - 0, // limit - txmgr.TxMeta{ - MaxEth: &maxNative, - SubID: txMetaSubID, - GlobalSubID: txMetaGlobalSubID, - }, - uuid.NullUUID{}, - 1337, - 0, // confs - nil) + b, err := json.Marshal(txmgr.TxMeta{ + MaxEth: &maxNative, + SubID: txMetaSubID, + GlobalSubID: txMetaGlobalSubID, + }) + require.NoError(t, err) + meta := datatypes.JSON(b) + now := time.Now() + tx := &txmgr.Tx{ + Sequence: &nonce, + FromAddress: from, + ToAddress: from, + EncodedPayload: []byte(`blah`), + Value: *big.NewInt(0), + FeeLimit: 0, + State: txmgrcommon.TxConfirmed, + Meta: &meta, + Subject: uuid.NullUUID{}, + ChainID: testutils.SimulatedChainID, + MinConfirmations: clnull.Uint32{Uint32: 0}, + PipelineTaskRunID: uuid.NullUUID{}, + BroadcastAt: &now, + InitialBroadcastAt: &now, + } + err = txStore.InsertTx(tx) require.NoError(t, err) } @@ -152,57 +187,72 @@ func testMaybeSubtractReservedLink(t *testing.T, vrfVersion vrfcommon.Version) { db := pgtest.NewSqlxDB(t) lggr := logger.TestLogger(t) cfg := pgtest.NewQConfig(false) - q := pg.NewQ(db, lggr, cfg) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg) require.NoError(t, ks.Unlock("blah")) - chainID := uint64(1337) - k, err := ks.Eth().Create(big.NewInt(int64(chainID))) + chainID := testutils.SimulatedChainID + k, err := ks.Eth().Create(chainID) require.NoError(t, err) subID := new(big.Int).SetUint64(1) reqTxHash := common.HexToHash("0xc524fafafcaec40652b1f84fca09c231185437d008d195fccf2f51e64b7062f8") + j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ + RequestedConfsDelay: 10, + }).Toml()) + require.NoError(t, err) + txstore := txmgr.NewTxStore(db, lggr, cfg) + txm := makeTestTxm(t, txstore, ks) + chain := evmmocks.NewChain(t) + chain.On("TxManager").Return(txm) + listener := &listenerV2{ + respCount: map[string]uint64{}, + job: j, + chain: chain, + } + + ctx := testutils.Context(t) + // Insert an unstarted eth tx with link metadata - addEthTx(t, db, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) - start, err := MaybeSubtractReservedLink(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTx(t, txstore, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) + start, err := listener.MaybeSubtractReservedLink(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) assert.Equal(t, "90000", start.String()) // A confirmed tx should not affect the starting balance - addConfirmedEthTx(t, db, k.Address, "10000", subID, 1, vrfVersion) - start, err = MaybeSubtractReservedLink(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addConfirmedEthTx(t, txstore, k.Address, "10000", subID, 1, vrfVersion) + start, err = listener.MaybeSubtractReservedLink(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) assert.Equal(t, "90000", start.String()) // An unconfirmed tx _should_ affect the starting balance. - addEthTx(t, db, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedLink(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTx(t, txstore, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedLink(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) assert.Equal(t, "80000", start.String()) // One subscriber's reserved link should not affect other subscribers prospective balance. otherSubID := new(big.Int).SetUint64(2) require.NoError(t, err) - addEthTx(t, db, k.Address, txmgrcommon.TxUnstarted, "10000", otherSubID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedLink(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTx(t, txstore, k.Address, txmgrcommon.TxUnstarted, "10000", otherSubID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedLink(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) require.Equal(t, "80000", start.String()) // One key's data should not affect other keys' data in the case of different subscribers. - k2, err := ks.Eth().Create(big.NewInt(1337)) + k2, err := ks.Eth().Create(testutils.SimulatedChainID) require.NoError(t, err) anotherSubID := new(big.Int).SetUint64(3) - addEthTx(t, db, k2.Address, txmgrcommon.TxUnstarted, "10000", anotherSubID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedLink(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTx(t, txstore, k2.Address, txmgrcommon.TxUnstarted, "10000", anotherSubID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedLink(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) require.Equal(t, "80000", start.String()) // A subscriber's balance is deducted with the link reserved across multiple keys, // i.e, gas lanes. - addEthTx(t, db, k2.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedLink(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTx(t, txstore, k2.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedLink(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) require.Equal(t, "70000", start.String()) } @@ -219,57 +269,73 @@ func testMaybeSubtractReservedNative(t *testing.T, vrfVersion vrfcommon.Version) db := pgtest.NewSqlxDB(t) lggr := logger.TestLogger(t) cfg := pgtest.NewQConfig(false) - q := pg.NewQ(db, lggr, cfg) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg) require.NoError(t, ks.Unlock("blah")) - chainID := uint64(1337) - k, err := ks.Eth().Create(big.NewInt(int64(chainID))) + chainID := testutils.SimulatedChainID + k, err := ks.Eth().Create(chainID) require.NoError(t, err) subID := new(big.Int).SetUint64(1) reqTxHash := common.HexToHash("0xc524fafafcaec40652b1f84fca09c231185437d008d195fccf2f51e64b7062f8") + j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ + RequestedConfsDelay: 10, + }).Toml()) + require.NoError(t, err) + txstore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + txm := makeTestTxm(t, txstore, ks) + require.NoError(t, err) + chain := evmmocks.NewChain(t) + chain.On("TxManager").Return(txm) + listener := &listenerV2{ + respCount: map[string]uint64{}, + job: j, + chain: chain, + } + + ctx := testutils.Context(t) + // Insert an unstarted eth tx with native metadata - addEthTxNativePayment(t, db, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) - start, err := MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTxNativePayment(t, txstore, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) + start, err := listener.MaybeSubtractReservedEth(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) assert.Equal(t, "90000", start.String()) // A confirmed tx should not affect the starting balance - addConfirmedEthTxNativePayment(t, db, k.Address, "10000", subID, 1, vrfVersion) - start, err = MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addConfirmedEthTxNativePayment(t, txstore, k.Address, "10000", subID, 1, vrfVersion) + start, err = listener.MaybeSubtractReservedEth(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) assert.Equal(t, "90000", start.String()) // An unconfirmed tx _should_ affect the starting balance. - addEthTxNativePayment(t, db, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTxNativePayment(t, txstore, k.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedEth(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) assert.Equal(t, "80000", start.String()) // One subscriber's reserved native should not affect other subscribers prospective balance. otherSubID := new(big.Int).SetUint64(2) require.NoError(t, err) - addEthTxNativePayment(t, db, k.Address, txmgrcommon.TxUnstarted, "10000", otherSubID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTxNativePayment(t, txstore, k.Address, txmgrcommon.TxUnstarted, "10000", otherSubID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedEth(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) require.Equal(t, "80000", start.String()) // One key's data should not affect other keys' data in the case of different subscribers. - k2, err := ks.Eth().Create(big.NewInt(1337)) + k2, err := ks.Eth().Create(testutils.SimulatedChainID) require.NoError(t, err) anotherSubID := new(big.Int).SetUint64(3) - addEthTxNativePayment(t, db, k2.Address, txmgrcommon.TxUnstarted, "10000", anotherSubID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTxNativePayment(t, txstore, k2.Address, txmgrcommon.TxUnstarted, "10000", anotherSubID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedEth(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) require.Equal(t, "80000", start.String()) // A subscriber's balance is deducted with the native reserved across multiple keys, // i.e, gas lanes. - addEthTxNativePayment(t, db, k2.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) - start, err = MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfVersion) + addEthTxNativePayment(t, txstore, k2.Address, txmgrcommon.TxUnstarted, "10000", subID, reqTxHash, vrfVersion) + start, err = listener.MaybeSubtractReservedEth(ctx, big.NewInt(100_000), chainID, subID, vrfVersion) require.NoError(t, err) require.Equal(t, "70000", start.String()) } @@ -282,13 +348,26 @@ func TestMaybeSubtractReservedNativeV2(t *testing.T) { db := pgtest.NewSqlxDB(t) lggr := logger.TestLogger(t) cfg := pgtest.NewQConfig(false) - q := pg.NewQ(db, lggr, cfg) ks := keystore.NewInMemory(db, utils.FastScryptParams, lggr, cfg) require.NoError(t, ks.Unlock("blah")) - chainID := uint64(1337) + chainID := testutils.SimulatedChainID subID := new(big.Int).SetUint64(1) + + j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ + RequestedConfsDelay: 10, + }).Toml()) + require.NoError(t, err) + txstore := txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + txm := makeTestTxm(t, txstore, ks) + chain := evmmocks.NewChain(t) + chain.On("TxManager").Return(txm).Maybe() + listener := &listenerV2{ + respCount: map[string]uint64{}, + job: j, + chain: chain, + } // returns error because native payment is not supported for V2 - start, err := MaybeSubtractReservedEth(q, big.NewInt(100_000), chainID, subID, vrfcommon.V2) + start, err := listener.MaybeSubtractReservedEth(testutils.Context(t), big.NewInt(100_000), chainID, subID, vrfcommon.V2) require.NoError(t, err) assert.Equal(t, big.NewInt(0), start) } @@ -445,12 +524,14 @@ func TestListener_handleLog(tt *testing.T) { lb.On("WasAlreadyConsumed", log).Return(false, nil).Once() lb.On("MarkConsumed", log).Return(nil).Once() defer lb.AssertExpectations(t) + chain := evmmocks.NewChain(t) + chain.On("LogBroadcaster").Return(lb) listener := &listenerV2{ respCount: map[string]uint64{}, job: j, blockNumberToReqID: pairing.New(), latestHeadMu: sync.RWMutex{}, - logBroadcaster: lb, + chain: chain, l: logger.TestLogger(t), } listener.handleLog(log, minConfs) @@ -476,12 +557,14 @@ func TestListener_handleLog(tt *testing.T) { lb.On("WasAlreadyConsumed", log).Return(false, nil).Once() lb.On("MarkConsumed", log).Return(nil).Once() defer lb.AssertExpectations(t) + chain := evmmocks.NewChain(t) + chain.On("LogBroadcaster").Return(lb) listener := &listenerV2{ respCount: map[string]uint64{}, job: j, blockNumberToReqID: pairing.New(), latestHeadMu: sync.RWMutex{}, - logBroadcaster: lb, + chain: chain, l: logger.TestLogger(t), } listener.handleLog(log, minConfs) diff --git a/core/services/vrf/v2/listener_v2_types.go b/core/services/vrf/v2/listener_v2_types.go index e0596abcd1a..5ad44c31a8b 100644 --- a/core/services/vrf/v2/listener_v2_types.go +++ b/core/services/vrf/v2/listener_v2_types.go @@ -170,7 +170,7 @@ func (lsn *listenerV2) processBatch( return errors.Wrap(err, "inserting finished pipeline runs") } - if err = lsn.logBroadcaster.MarkManyConsumed(batch.lbs, pg.WithQueryer(tx)); err != nil { + if err = lsn.chain.LogBroadcaster().MarkManyConsumed(batch.lbs, pg.WithQueryer(tx)); err != nil { return errors.Wrap(err, "mark logs consumed") } @@ -181,7 +181,7 @@ func (lsn *listenerV2) processBatch( for _, reqID := range batch.reqIDs { reqIDHashes = append(reqIDHashes, common.BytesToHash(reqID.Bytes())) } - ethTX, err = lsn.txm.CreateTransaction(ctx, txmgr.TxRequest{ + ethTX, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ FromAddress: fromAddress, ToAddress: lsn.batchCoordinator.Address(), EncodedPayload: payload, @@ -234,7 +234,7 @@ func (lsn *listenerV2) getUnconsumed(l logger.Logger, reqs []pendingRequest) (un // This check to see if the log was consumed needs to be in the same // goroutine as the mark consumed to avoid processing duplicates. - consumed, err := lsn.logBroadcaster.WasAlreadyConsumed(req.lb) + consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(req.lb) if err != nil { // Do not process for now, retry on next iteration. l.Errorw("Could not determine if log was already consumed", diff --git a/core/services/vrf/vrfcommon/utils.go b/core/services/vrf/vrfcommon/utils.go new file mode 100644 index 00000000000..f9cc012d8fb --- /dev/null +++ b/core/services/vrf/vrfcommon/utils.go @@ -0,0 +1,78 @@ +package vrfcommon + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" +) + +type RespCountEntry struct { + RequestID string + Count int +} + +func GetRespCounts(ctx context.Context, txm txmgr.TxManager, chainID *big.Int, confirmedBlockNum int64) ( + []RespCountEntry, + error, +) { + counts := []RespCountEntry{} + metaField := "RequestID" + states := []txmgrtypes.TxState{txmgrcommon.TxUnconfirmed, txmgrcommon.TxUnstarted, txmgrcommon.TxInProgress} + // Search for txes with a non-null meta field in the provided states + unconfirmedTxes, err := txm.FindTxesWithMetaFieldByStates(ctx, metaField, states, chainID) + if err != nil { + return nil, errors.Wrap(err, "getRespCounts failed due to error in FindTxesWithMetaFieldByStates") + } + // Fetch completed transactions only as far back as the given confirmedBlockNum. This avoids + // a table scan of the whole table, which could be large if it is unpruned. + var confirmedTxes []*txmgr.Tx + confirmedTxes, err = txm.FindTxesWithMetaFieldByReceiptBlockNum(ctx, metaField, confirmedBlockNum, chainID) + if err != nil { + return nil, errors.Wrap(err, "getRespCounts failed due to error in FindTxesWithMetaFieldByReceiptBlockNum") + } + txes := DedupeTxList(append(unconfirmedTxes, confirmedTxes...)) + respCountMap := make(map[string]int) + // Consolidate the number of txes for each meta RequestID + for _, tx := range txes { + var meta *txmgrtypes.TxMeta[common.Address, common.Hash] + meta, err = tx.GetMeta() + if err != nil { + return nil, errors.Wrap(err, "getRespCounts failed parsing tx meta field") + } + if meta != nil && meta.RequestID != nil { + requestId := meta.RequestID.String() + if _, exists := respCountMap[requestId]; !exists { + respCountMap[requestId] = 0 + } + respCountMap[requestId]++ + } + } + + // Parse response count map into output + for key, value := range respCountMap { + respCountEntry := RespCountEntry{ + RequestID: key, + Count: value, + } + counts = append(counts, respCountEntry) + } + return counts, nil +} + +func DedupeTxList(txes []*txmgr.Tx) []*txmgr.Tx { + txIdMap := make(map[string]bool) + dedupedTxes := []*txmgr.Tx{} + for _, tx := range txes { + if _, found := txIdMap[tx.GetID()]; !found { + txIdMap[tx.GetID()] = true + dedupedTxes = append(dedupedTxes, tx) + } + } + return dedupedTxes +} diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index fc2e8d7a30e..0bd947bbce3 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "math/big" "net/http" "net/url" "strconv" @@ -19,10 +20,12 @@ import ( p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/pelletier/go-toml" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/smartcontractkit/sqlx" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -658,7 +661,8 @@ func setupJobsControllerTests(t *testing.T) (ta *cltest.TestApplication, cc clte c.P2P.V1.Enabled = ptr(true) c.P2P.PeerID = &cltest.DefaultP2PPeerID }) - app := cltest.NewApplicationWithConfigAndKey(t, cfg, cltest.DefaultP2PKey) + ec := setupEthClientForControllerTests(t) + app := cltest.NewApplicationWithConfigAndKey(t, cfg, cltest.DefaultP2PKey, ec) require.NoError(t, app.Start(testutils.Context(t))) client := app.NewHTTPClient(nil) @@ -668,6 +672,14 @@ func setupJobsControllerTests(t *testing.T) (ta *cltest.TestApplication, cc clte return app, client } +func setupEthClientForControllerTests(t *testing.T) *evmclimocks.Client { + ec := cltest.NewEthMocksWithStartupAssertions(t) + ec.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil).Maybe() + ec.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(100), nil).Maybe() + ec.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Once().Return(big.NewInt(0), nil).Maybe() + return ec +} + func setupJobSpecsControllerTestsWithJobs(t *testing.T) (*cltest.TestApplication, cltest.HTTPClientCleaner, job.Job, int32, job.Job, int32) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.OCR.Enabled = ptr(true) From 8b6f4899ef0a981b869d7deeed45e40de3193aa2 Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 2 Nov 2023 15:40:18 -0600 Subject: [PATCH 063/271] E2E Metrics Missing Tag Suffix (#11163) --- .github/workflows/integration-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 445a0277315..785c48da40a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -455,7 +455,7 @@ jobs: with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: ETH Smoke Tests ${{ matrix.product.name }} + this-job-name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true - name: Keep action running to view traces From 5cc88a82ec77b1d4f3f37b0255af778a056e7e5c Mon Sep 17 00:00:00 2001 From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Date: Thu, 2 Nov 2023 19:43:54 -0400 Subject: [PATCH 064/271] Handle PR bodies correctly (#11165) --- .github/workflows/operator-ui-cd.yml | 2 +- operator_ui/check.sh | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/operator-ui-cd.yml b/.github/workflows/operator-ui-cd.yml index 54f423e6dc3..bd589da728f 100644 --- a/.github/workflows/operator-ui-cd.yml +++ b/.github/workflows/operator-ui-cd.yml @@ -39,7 +39,7 @@ jobs: url: ${{ secrets.AWS_INFRA_RELENG_TOKEN_ISSUER_LAMBDA_URL }} - name: Open PR - uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 # v4.2.4 + uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2 with: title: Update Operator UI from ${{ steps.update.outputs.current_tag }} to ${{ steps.update.outputs.latest_tag }} token: ${{ steps.get-gh-token.outputs.access-token }} diff --git a/operator_ui/check.sh b/operator_ui/check.sh index 614afd4b07f..9e738218088 100755 --- a/operator_ui/check.sh +++ b/operator_ui/check.sh @@ -26,12 +26,13 @@ else echo "$latest_tag" >"$tag_file" echo "Tag updated $current_tag -> $latest_tag" if [ "$CI" ]; then - echo "current_tag=$current_tag" >> $GITHUB_OUTPUT - echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT - # See https://github.com/peter-evans/create-pull-request/blob/main/docs/examples.md#setting-the-pull-request-body-from-a-file - body="${body//'%'/'%25'}" - body="${body//$'\n'/'%0A'}" - body="${body//$'\r'/'%0D'}" - echo "body=$body" >> $GITHUB_OUTPUT + echo "current_tag=$current_tag" >>$GITHUB_OUTPUT + echo "latest_tag=$latest_tag" >>$GITHUB_OUTPUT + + # See https://github.com/orgs/community/discussions/26288#discussioncomment-3876281 + delimiter="$(openssl rand -hex 8)" + echo "body<<${delimiter}" >>"${GITHUB_OUTPUT}" + echo "$body" >>"${GITHUB_OUTPUT}" + echo "${delimiter}" >>"${GITHUB_OUTPUT}" fi fi From ed9dc15b98c2f0cbf105d0f1b5fd40cf7571998c Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Fri, 3 Nov 2023 10:39:34 +0000 Subject: [PATCH 065/271] fix: v2plus superscript (#11156) --- .../testnet/v2plusscripts/super_scripts.go | 91 +++++++++++-------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go index f805e7b74f0..752e06bbb25 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go @@ -6,15 +6,16 @@ import ( "encoding/hex" "flag" "fmt" + "math/big" + "os" + "strings" + "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/constants" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/jobs" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/model" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/util" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" - "math/big" - "os" - "strings" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -467,12 +468,12 @@ func DeployUniverseViaCLI(e helpers.Environment) { deployCmd := flag.NewFlagSet("deploy-universe", flag.ExitOnError) // required flags - linkAddress := *deployCmd.String("link-address", "", "address of link token") - linkEthAddress := *deployCmd.String("link-eth-feed", "", "address of link eth feed") - bhsContractAddressString := *deployCmd.String("bhs-address", "", "address of BHS contract") - batchBHSAddressString := *deployCmd.String("batch-bhs-address", "", "address of Batch BHS contract") - coordinatorAddressString := *deployCmd.String("coordinator-address", "", "address of VRF Coordinator contract") - batchCoordinatorAddressString := *deployCmd.String("batch-coordinator-address", "", "address Batch VRF Coordinator contract") + linkAddress := deployCmd.String("link-address", "", "address of link token") + linkEthAddress := deployCmd.String("link-eth-feed", "", "address of link eth feed") + bhsContractAddressString := deployCmd.String("bhs-address", "", "address of BHS contract") + batchBHSAddressString := deployCmd.String("batch-bhs-address", "", "address of Batch BHS contract") + coordinatorAddressString := deployCmd.String("coordinator-address", "", "address of VRF Coordinator contract") + batchCoordinatorAddressString := deployCmd.String("batch-coordinator-address", "", "address Batch VRF Coordinator contract") subscriptionBalanceJuelsString := deployCmd.String("subscription-balance", "1e19", "amount to fund subscription with Link token (Juels)") subscriptionBalanceNativeWeiString := deployCmd.String("subscription-balance-native", "1e18", "amount to fund subscription with native token (Wei)") @@ -513,14 +514,14 @@ func DeployUniverseViaCLI(e helpers.Environment) { SendingKeyFundingAmount: fundingAmount, } - bhsContractAddress := common.HexToAddress(bhsContractAddressString) - batchBHSAddress := common.HexToAddress(batchBHSAddressString) - coordinatorAddress := common.HexToAddress(coordinatorAddressString) - batchCoordinatorAddress := common.HexToAddress(batchCoordinatorAddressString) + bhsContractAddress := common.HexToAddress(*bhsContractAddressString) + batchBHSAddress := common.HexToAddress(*batchBHSAddressString) + coordinatorAddress := common.HexToAddress(*coordinatorAddressString) + batchCoordinatorAddress := common.HexToAddress(*batchCoordinatorAddressString) contractAddresses := model.ContractAddresses{ - LinkAddress: linkAddress, - LinkEthAddress: linkEthAddress, + LinkAddress: *linkAddress, + LinkEthAddress: *linkEthAddress, BhsContractAddress: bhsContractAddress, BatchBHSAddress: batchBHSAddress, CoordinatorAddress: coordinatorAddress, @@ -563,28 +564,32 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, batchFulfillmentEnabled bool, nodesMap map[string]model.Node, ) model.JobSpecs { - // Put key in ECDSA format - if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { - *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) - } + var compressedPkHex string + var keyHash common.Hash + if len(*registerKeyUncompressedPubKey) > 0 { + // Put key in ECDSA format + if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { + *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) + } - // Generate compressed public key and key hash - pubBytes, err := hex.DecodeString(*registerKeyUncompressedPubKey) - helpers.PanicErr(err) - pk, err := crypto.UnmarshalPubkey(pubBytes) - helpers.PanicErr(err) - var pkBytes []byte - if big.NewInt(0).Mod(pk.Y, big.NewInt(2)).Uint64() != 0 { - pkBytes = append(pk.X.Bytes(), 1) - } else { - pkBytes = append(pk.X.Bytes(), 0) - } - var newPK secp256k1.PublicKey - copy(newPK[:], pkBytes) + // Generate compressed public key and key hash + pubBytes, err := hex.DecodeString(*registerKeyUncompressedPubKey) + helpers.PanicErr(err) + pk, err := crypto.UnmarshalPubkey(pubBytes) + helpers.PanicErr(err) + var pkBytes []byte + if big.NewInt(0).Mod(pk.Y, big.NewInt(2)).Uint64() != 0 { + pkBytes = append(pk.X.Bytes(), 1) + } else { + pkBytes = append(pk.X.Bytes(), 0) + } + var newPK secp256k1.PublicKey + copy(newPK[:], pkBytes) - compressedPkHex := hexutil.Encode(pkBytes) - keyHash, err := newPK.Hash() - helpers.PanicErr(err) + compressedPkHex = hexutil.Encode(pkBytes) + keyHash, err = newPK.Hash() + helpers.PanicErr(err) + } if len(contractAddresses.LinkAddress) == 0 { fmt.Println("\nDeploying LINK Token...") @@ -689,7 +694,13 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses contractAddresses.CoordinatorAddress, - nodesMap[model.VRFPrimaryNodeName].SendingKeys[0].Address, + func() string { + if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { + return keys[0].Address + } else { + return common.HexToAddress("0x0").String() + } + }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, ) @@ -704,7 +715,13 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFBackupNodeName].SendingKeys), "\",\""), //fromAddresses contractAddresses.CoordinatorAddress, - nodesMap[model.VRFPrimaryNodeName].SendingKeys[0], + func() string { + if keys := nodesMap[model.VRFPrimaryNodeName].SendingKeys; len(keys) > 0 { + return keys[0].Address + } else { + return common.HexToAddress("0x0").String() + } + }(), contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, ) From 4ea52f902f2ff1a0bef53e90c19ee46a3e01cf6c Mon Sep 17 00:00:00 2001 From: "app-token-issuer-infra-releng[bot]" <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Date: Fri, 3 Nov 2023 11:17:17 +0000 Subject: [PATCH 066/271] Update Operator UI from v0.8.0-e10948a to v0.8.0-2f868c3 (#11135) Co-authored-by: github-merge-queue[bot] --- operator_ui/TAG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator_ui/TAG b/operator_ui/TAG index e08ca072670..3b63cc3addb 100644 --- a/operator_ui/TAG +++ b/operator_ui/TAG @@ -1 +1 @@ -v0.8.0-e10948a +v0.8.0-2f868c3 From 4174f36b2727fbba5da81af591c24a16d396c802 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Fri, 3 Nov 2023 12:23:13 +0100 Subject: [PATCH 067/271] added smoke test & load test for log poller (#11110) * added smoke test & load test for log poller * read CL nodes logs in parallel and compare them in parallel with EVM node logs (has a big impact on execution, when we emit 100k+ logs) * add simple config validation * add smoke tests for backup process and replay * run replay test for 15m instead of 5m (for debuggin) * do not use hardcoded postgres values * added support for chaos experiments (pausing containers) + a smoke test that uses them * streamline log poller tests * remove backup poller test -- way to test it reliably in e2e tests * don't skip replay test * add go.work* to .gitignore * add tests that can easier run in CI + some changes after testing with live testnets * wait for LP to finalise endblock + on demand workflow in GH * rename on demand workflow * fix typo in workflow name --- .github/log_poller_on_demand.yml | 66 + .gitignore | 3 +- core/chains/evm/logpoller/log_poller.go | 4 +- integration-tests/client/chainlink.go | 20 + integration-tests/client/chainlink_models.go | 14 + .../contracts/contract_deployer.go | 20 + .../contracts/contract_models.go | 10 + integration-tests/contracts/test_contracts.go | 79 ++ integration-tests/docker/cmd/test_env.go | 1 + integration-tests/docker/test_env/test_env.go | 1 + .../docker/test_env/test_env_builder.go | 67 +- integration-tests/go.mod | 8 +- integration-tests/go.sum | 4 +- integration-tests/load/log_poller/config.toml | 22 + .../load/log_poller/log_poller_test.go | 24 + .../reorg/log_poller_maybe_reorg_test.go | 42 + integration-tests/smoke/automation_test.go | 3 +- integration-tests/smoke/log_poller_test.go | 140 ++ .../universal/log_poller/config.go | 247 ++++ integration-tests/universal/log_poller/gun.go | 78 ++ .../universal/log_poller/helpers.go | 1136 +++++++++++++++++ .../universal/log_poller/scenarios.go | 498 ++++++++ 22 files changed, 2460 insertions(+), 27 deletions(-) create mode 100644 .github/log_poller_on_demand.yml create mode 100644 integration-tests/contracts/test_contracts.go create mode 100644 integration-tests/load/log_poller/config.toml create mode 100644 integration-tests/load/log_poller/log_poller_test.go create mode 100644 integration-tests/reorg/log_poller_maybe_reorg_test.go create mode 100644 integration-tests/smoke/log_poller_test.go create mode 100644 integration-tests/universal/log_poller/config.go create mode 100644 integration-tests/universal/log_poller/gun.go create mode 100644 integration-tests/universal/log_poller/helpers.go create mode 100644 integration-tests/universal/log_poller/scenarios.go diff --git a/.github/log_poller_on_demand.yml b/.github/log_poller_on_demand.yml new file mode 100644 index 00000000000..856d1e02349 --- /dev/null +++ b/.github/log_poller_on_demand.yml @@ -0,0 +1,66 @@ +name: On Demand Log Poller Consistency Test +on: + workflow_dispatch: + inputs: + contracts: + description: Number of test contracts + default: "2" + required: true + eventsPerTx: + description: Number of events to emit per transaction + default: "10" + required: true + useFinalityTag: + description: Use finality tag + default: "false" + required: true + loadDuration: + description: Load duration (e.g. 10s, 10m, 1h) + default: "10m" + required: true + chainlinkImage: + description: Chainlink image to use + default: "public.ecr.aws/chainlink/chainlink" + required: true + chainlinkVersion: + description: Chainlink version to use + default: "v2.7.0-beta0" + required: true + selectedNetworks: + description: Network to use (only Sepolia or Mumbai) + default: "Sepolia" + required: true + fundingKey: + description: Private key used to fund the contracts + required: true + rpcURL: + description: RPC URL to use + required: true + wsURL: + description: WS URL to use + required: true + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version-file: "integration-tests/go.mod" + cache: true + - name: Show overrides + env: + CONTRACTS: ${{ inputs.contracts }} + EVENTS_PER_TX: ${{ inputs.eventsPerTx }} + LOAD_DURATION: ${{ inputs.loadDuration }} + USE_FINALITY_TAG: ${{ inputs.useFinalityTag }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} + EVM_KEYS: ${{ inputs.fundingKey }} + EVM_HTTP_URLS: ${{ inputs.rpcURL }} + EVM_URLS: ${{ inputs.wsURL }} + run: | + go test -v -timeout 5h -run=TestLogPollerFromEnv integration-tests/reorg/log_poller_maybe_reorg_test.go \ No newline at end of file diff --git a/.gitignore b/.gitignore index bfd66e2a39a..61ebfab0e9b 100644 --- a/.gitignore +++ b/.gitignore @@ -65,7 +65,7 @@ tests-*.xml tmp-manifest-*.yaml ztarrepo.tar.gz **/test-ledger/* -__debug_bin +__debug_bin* # goreleaser builds cosign.* @@ -82,3 +82,4 @@ contracts/yarn.lock # Ignore DevSpace cache and log folder .devspace/ +go.work* \ No newline at end of file diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 4cd2804d9f3..01d6a2aad47 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -466,6 +466,7 @@ func (lp *logPoller) run() { // Serially process replay requests. lp.lggr.Infow("Executing replay", "fromBlock", fromBlock, "requested", fromBlockReq) lp.PollAndSaveLogs(lp.ctx, fromBlock) + lp.lggr.Infow("Executing replay finished", "fromBlock", fromBlock, "requested", fromBlockReq) } } else { lp.lggr.Errorw("Error executing replay, could not get fromBlock", "err", err) @@ -574,13 +575,14 @@ func (lp *logPoller) BackupPollAndSaveLogs(ctx context.Context, backupPollerBloc lastSafeBackfillBlock := latestFinalizedBlockNumber - 1 if lastSafeBackfillBlock >= lp.backupPollerNextBlock { - lp.lggr.Infow("Backup poller backfilling logs", "start", lp.backupPollerNextBlock, "end", lastSafeBackfillBlock) + lp.lggr.Infow("Backup poller started backfilling logs", "start", lp.backupPollerNextBlock, "end", lastSafeBackfillBlock) if err = lp.backfill(ctx, lp.backupPollerNextBlock, lastSafeBackfillBlock); err != nil { // If there's an error backfilling, we can just return and retry from the last block saved // since we don't save any blocks on backfilling. We may re-insert the same logs but thats ok. lp.lggr.Warnw("Backup poller failed", "err", err) return } + lp.lggr.Infow("Backup poller finished backfilling", "start", lp.backupPollerNextBlock, "end", lastSafeBackfillBlock) lp.backupPollerNextBlock = lastSafeBackfillBlock + 1 } } diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 8a79cb3ec95..3638fa11c7f 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -1213,3 +1213,23 @@ func (c *ChainlinkClient) GetForwarders() (*Forwarders, *http.Response, error) { } return response, resp.RawResponse, err } + +// Replays log poller from block number +func (c *ChainlinkClient) ReplayLogPollerFromBlock(fromBlock, evmChainID int64) (*ReplayResponse, *http.Response, error) { + specObj := &ReplayResponse{} + c.l.Info().Str(NodeURL, c.Config.URL).Int64("From block", fromBlock).Int64("EVM chain ID", evmChainID).Msg("Replaying Log Poller from block") + resp, err := c.APIClient.R(). + SetResult(&specObj). + SetQueryParams(map[string]string{ + "evmChainID": fmt.Sprint(evmChainID), + }). + SetPathParams(map[string]string{ + "fromBlock": fmt.Sprint(fromBlock), + }). + Post("/v2/replay_from_block/{fromBlock}") + if err != nil { + return nil, nil, err + } + + return specObj, resp.RawResponse, err +} diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index 6013e13e0fa..c6d1209d2ea 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -9,6 +9,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) // EIServiceConfig represents External Initiator service config @@ -1407,3 +1408,16 @@ type ForwarderAttributes struct { CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } + +type ReplayResponse struct { + Data ReplayResponseData `json:"data"` +} + +type ReplayResponseData struct { + Attributes ReplayResponseAttributes `json:"attributes"` +} + +type ReplayResponseAttributes struct { + Message string `json:"message"` + EVMChainID *utils.Big `json:"evmChainID"` +} diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index e203d8318f2..5a3fad256e4 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -45,6 +45,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" registry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" + le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/mock_aggregator_proxy" @@ -138,6 +139,7 @@ type ContractDeployer interface { DeployMercuryVerifierProxyContract(accessControllerAddr common.Address) (MercuryVerifierProxy, error) DeployMercuryFeeManager(linkAddress common.Address, nativeAddress common.Address, proxyAddress common.Address, rewardManagerAddress common.Address) (MercuryFeeManager, error) DeployMercuryRewardManager(linkAddress common.Address) (MercuryRewardManager, error) + DeployLogEmitterContract() (LogEmitter, error) } // NewContractDeployer returns an instance of a contract deployer based on the client type @@ -1613,3 +1615,21 @@ func (e *EthereumContractDeployer) DeployWERC20Mock() (WERC20Mock, error) { l: e.l, }, err } + +func (e *EthereumContractDeployer) DeployLogEmitterContract() (LogEmitter, error) { + address, _, instance, err := e.client.DeployContract("Log Emitter", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return le.DeployLogEmitter(auth, backend) + }) + if err != nil { + return nil, err + } + return &LogEmitterContract{ + client: e.client, + instance: instance.(*le.LogEmitter), + address: *address, + l: e.l, + }, err +} diff --git a/integration-tests/contracts/contract_models.go b/integration-tests/contracts/contract_models.go index 51fce7cb120..4c8d610fa1b 100644 --- a/integration-tests/contracts/contract_models.go +++ b/integration-tests/contracts/contract_models.go @@ -400,3 +400,13 @@ type WERC20Mock interface { Transfer(to string, amount *big.Int) error Mint(account common.Address, amount *big.Int) (*types.Transaction, error) } + +type LogEmitter interface { + Address() common.Address + EmitLogInts(ints []int) (*types.Transaction, error) + EmitLogIntsIndexed(ints []int) (*types.Transaction, error) + EmitLogStrings(strings []string) (*types.Transaction, error) + EmitLogInt(payload int) (*types.Transaction, error) + EmitLogIntIndexed(payload int) (*types.Transaction, error) + EmitLogString(strings string) (*types.Transaction, error) +} diff --git a/integration-tests/contracts/test_contracts.go b/integration-tests/contracts/test_contracts.go new file mode 100644 index 00000000000..ccdd2989e49 --- /dev/null +++ b/integration-tests/contracts/test_contracts.go @@ -0,0 +1,79 @@ +package contracts + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/rs/zerolog" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + + le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" +) + +type LogEmitterContract struct { + address common.Address + client blockchain.EVMClient + instance *le.LogEmitter + l zerolog.Logger +} + +func (e *LogEmitterContract) Address() common.Address { + return e.address +} + +func (e *LogEmitterContract) EmitLogInts(ints []int) (*types.Transaction, error) { + opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + bigInts := make([]*big.Int, len(ints)) + for i, v := range ints { + bigInts[i] = big.NewInt(int64(v)) + } + tx, err := e.instance.EmitLog1(opts, bigInts) + if err != nil { + return nil, err + } + return tx, e.client.ProcessTransaction(tx) +} + +func (e *LogEmitterContract) EmitLogIntsIndexed(ints []int) (*types.Transaction, error) { + opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + bigInts := make([]*big.Int, len(ints)) + for i, v := range ints { + bigInts[i] = big.NewInt(int64(v)) + } + tx, err := e.instance.EmitLog2(opts, bigInts) + if err != nil { + return nil, err + } + return tx, e.client.ProcessTransaction(tx) +} + +func (e *LogEmitterContract) EmitLogStrings(strings []string) (*types.Transaction, error) { + opts, err := e.client.TransactionOpts(e.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := e.instance.EmitLog3(opts, strings) + if err != nil { + return nil, err + } + return tx, e.client.ProcessTransaction(tx) +} + +func (e *LogEmitterContract) EmitLogInt(payload int) (*types.Transaction, error) { + return e.EmitLogInts([]int{payload}) +} + +func (e *LogEmitterContract) EmitLogIntIndexed(payload int) (*types.Transaction, error) { + return e.EmitLogIntsIndexed([]int{payload}) +} + +func (e *LogEmitterContract) EmitLogString(strings string) (*types.Transaction, error) { + return e.EmitLogStrings([]string{strings}) +} diff --git a/integration-tests/docker/cmd/test_env.go b/integration-tests/docker/cmd/test_env.go index 31b7de5dcdd..f760f45f8d0 100644 --- a/integration-tests/docker/cmd/test_env.go +++ b/integration-tests/docker/cmd/test_env.go @@ -50,6 +50,7 @@ func main() { return nil }, } + startEnvCmd.AddCommand(startFullEnvCmd) // Set default log level for non-testcontainer code diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 40ed0d4d535..e067e46090d 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -166,6 +166,7 @@ func (te *CLClusterTestEnv) FundChainlinkNodes(amount *big.Float) error { if err := cl.Fund(te.EVMClient, amount); err != nil { return errors.Wrap(err, ErrFundCLNode) } + time.Sleep(5 * time.Second) } return te.EVMClient.WaitForEvents() } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index d1550240500..c07ea762623 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" ) type CleanUpType string @@ -30,22 +31,24 @@ const ( ) type CLTestEnvBuilder struct { - hasLogWatch bool - hasGeth bool - hasKillgrave bool - hasForwarders bool - clNodeConfig *chainlink.Config - secretsConfig string - nonDevGethNetworks []blockchain.EVMNetwork - clNodesCount int - customNodeCsaKeys []string - defaultNodeCsaKeys []string - l zerolog.Logger - t *testing.T - te *CLClusterTestEnv - isNonEVM bool - cleanUpType CleanUpType - cleanUpCustomFn func() + hasLogWatch bool + hasGeth bool + hasKillgrave bool + hasForwarders bool + clNodeConfig *chainlink.Config + secretsConfig string + nonDevGethNetworks []blockchain.EVMNetwork + clNodesCount int + customNodeCsaKeys []string + defaultNodeCsaKeys []string + l zerolog.Logger + t *testing.T + te *CLClusterTestEnv + isNonEVM bool + cleanUpType CleanUpType + cleanUpCustomFn func() + chainOptionsFn []ChainOption + evmClientNetworkOption []EVMClientNetworkOption /* funding */ ETHFunds *big.Float @@ -162,6 +165,24 @@ func (b *CLTestEnvBuilder) WithCustomCleanup(customFn func()) *CLTestEnvBuilder return b } +type ChainOption = func(*evmcfg.Chain) *evmcfg.Chain + +func (b *CLTestEnvBuilder) WithChainOptions(opts ...ChainOption) *CLTestEnvBuilder { + b.chainOptionsFn = make([]ChainOption, 0, 0) + b.chainOptionsFn = append(b.chainOptionsFn, opts...) + + return b +} + +type EVMClientNetworkOption = func(*blockchain.EVMNetwork) *blockchain.EVMNetwork + +func (b *CLTestEnvBuilder) EVMClientNetworkOptions(opts ...EVMClientNetworkOption) *CLTestEnvBuilder { + b.evmClientNetworkOption = make([]EVMClientNetworkOption, 0, 0) + b.evmClientNetworkOption = append(b.evmClientNetworkOption, opts...) + + return b +} + func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { if b.te == nil { var err error @@ -245,10 +266,14 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { if err != nil { return nil, err } - } if !b.isNonEVM { + if b.evmClientNetworkOption != nil && len(b.evmClientNetworkOption) > 0 { + for _, fn := range b.evmClientNetworkOption { + fn(&networkConfig) + } + } bc, err := blockchain.NewEVMClientFromNetwork(networkConfig, b.l) if err != nil { return nil, err @@ -294,6 +319,14 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } node.SetChainConfig(cfg, wsUrls, httpUrls, networkConfig, b.hasForwarders) + + if b.chainOptionsFn != nil && len(b.chainOptionsFn) > 0 { + for _, fn := range b.chainOptionsFn { + for _, evmCfg := range cfg.EVM { + fn(&evmCfg.Chain) + } + } + } } err := b.te.StartClCluster(cfg, b.clNodesCount, b.secretsConfig) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 33beae119ae..127980a2cb9 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -6,6 +6,7 @@ go 1.21 replace github.com/smartcontractkit/chainlink/v2 => ../ require ( + cosmossdk.io/errors v1.0.0 github.com/K-Phoen/grabana v0.21.17 github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.12.0 @@ -18,13 +19,15 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 + github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.2 + github.com/smartcontractkit/chainlink-testing-framework v1.18.2-0.20231030212542-5fb562e774a5 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 + github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.3.0 github.com/spf13/cobra v1.6.1 @@ -49,7 +52,6 @@ require ( cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect cosmossdk.io/depinject v1.0.0-alpha.3 // indirect - cosmossdk.io/errors v1.0.0 // indirect cosmossdk.io/math v1.0.1 // indirect dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.0.0 // indirect @@ -375,7 +377,6 @@ require ( github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/scylladb/go-reflectx v1.0.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sercand/kuberesolver v2.4.0+incompatible // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect @@ -387,7 +388,6 @@ require ( github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect - github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 7969e82144c..24da9467176 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2368,8 +2368,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.2 h1:Ac/wdRDF4L479wpFT3yqn6ujb6kFTn7aq8gj9giyFHM= -github.com/smartcontractkit/chainlink-testing-framework v1.18.2/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.2-0.20231030212542-5fb562e774a5 h1:4hTf8pvtdtwoaeKFSEYjBZPvDbZ05WgiHsb0TPL6HqQ= +github.com/smartcontractkit/chainlink-testing-framework v1.18.2-0.20231030212542-5fb562e774a5/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/load/log_poller/config.toml b/integration-tests/load/log_poller/config.toml new file mode 100644 index 00000000000..2e328001943 --- /dev/null +++ b/integration-tests/load/log_poller/config.toml @@ -0,0 +1,22 @@ +[general] +generator = "looped" +contracts = 10 +events_per_tx = 10 + +[chaos] +experiment_count = 10 + +[looped] +[looped.contract] +execution_count = 300 + +[looped.fuzz] +min_emit_wait_time_ms = 100 +max_emit_wait_time_ms = 500 + +[wasp] +[wasp.load] +call_timeout = "3m" +rate_limit_unit_duration = "2s" +LPS = 30 +duration = "1m" \ No newline at end of file diff --git a/integration-tests/load/log_poller/log_poller_test.go b/integration-tests/load/log_poller/log_poller_test.go new file mode 100644 index 00000000000..ec67815832c --- /dev/null +++ b/integration-tests/load/log_poller/log_poller_test.go @@ -0,0 +1,24 @@ +package logpoller + +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + + lp_helpers "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" + "github.com/stretchr/testify/require" +) + +func TestLoadTestLogPoller(t *testing.T) { + cfg, err := lp_helpers.ReadConfig(lp_helpers.DefaultConfigFilename) + require.NoError(t, err) + + eventsToEmit := []abi.Event{} + for _, event := range lp_helpers.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + lp_helpers.ExecuteBasicLogPollerTest(t, cfg) +} diff --git a/integration-tests/reorg/log_poller_maybe_reorg_test.go b/integration-tests/reorg/log_poller_maybe_reorg_test.go new file mode 100644 index 00000000000..4e802bdb09c --- /dev/null +++ b/integration-tests/reorg/log_poller_maybe_reorg_test.go @@ -0,0 +1,42 @@ +package reorg + +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" +) + +func TestLogPollerFromEnv(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 100, + UseFinalityTag: true, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 800, + MaxEmitWaitTimeMs: 1200, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + err := cfg.OverrideFromEnv() + if err != nil { + t.Errorf("failed to override config from env: %v", err) + t.FailNow() + } + + logpoller.ExecuteCILogPollerTest(t, &cfg) +} diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 17373e6a95f..9e35b24df1e 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -11,9 +11,8 @@ import ( "testing" "time" - "github.com/kelseyhightower/envconfig" - "github.com/ethereum/go-ethereum/common" + "github.com/kelseyhightower/envconfig" "github.com/onsi/gomega" "github.com/stretchr/testify/require" diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go new file mode 100644 index 00000000000..0df7817f1e3 --- /dev/null +++ b/integration-tests/smoke/log_poller_test.go @@ -0,0 +1,140 @@ +package smoke + +import ( + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" +) + +// consistency test with no network disruptions with approximate emission of 1500-1600 logs per second for ~110-120 seconds +// 6 filters are registered +func TestLogPollerFewFilters(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 4, + UseFinalityTag: false, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + logpoller.ExecuteBasicLogPollerTest(t, &cfg) +} + +// consistency test with no network disruptions with approximate emission of 1000-1100 logs per second for ~110-120 seconds +// 900 filters are registered +func TestLogManyFiltersPoller(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 300, + EventsPerTx: 3, + UseFinalityTag: false, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 30, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + logpoller.ExecuteBasicLogPollerTest(t, &cfg) +} + +// consistency test that introduces random distruptions by pausing either Chainlink or Postgres containers for random interval of 5-20 seconds +// with approximate emission of 520-550 logs per second for ~110 seconds +// 6 filters are registered +func TestLogPollerWithChaos(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 100, + UseFinalityTag: false, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + ChaosConfig: &logpoller.ChaosConfig{ + ExperimentCount: 10, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + logpoller.ExecuteBasicLogPollerTest(t, &cfg) +} + +// consistency test that registers filters after events were emitted and then triggers replay via API +// unfortunately there is no way to make sure that logs that are indexed are only picked up by replay +// and not by backup poller +// with approximate emission of 24 logs per second for ~110 seconds +// 6 filters are registered +func TestLogPollerReplay(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 4, + UseFinalityTag: false, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + consistencyTimeout := "5m" + + logpoller.ExecuteLogPollerReplay(t, &cfg, consistencyTimeout) +} diff --git a/integration-tests/universal/log_poller/config.go b/integration-tests/universal/log_poller/config.go new file mode 100644 index 00000000000..623fa6606ed --- /dev/null +++ b/integration-tests/universal/log_poller/config.go @@ -0,0 +1,247 @@ +package logpoller + +import ( + "fmt" + "os" + "strconv" + + "cosmossdk.io/errors" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/pelletier/go-toml/v2" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink/v2/core/store/models" +) + +const ( + DefaultConfigFilename = "config.toml" + + ErrReadPerfConfig = "failed to read TOML config for performance tests" + ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" +) + +type GeneratorType = string + +const ( + GeneratorType_WASP = "wasp" + GeneratorType_Looped = "looped" +) + +type Config struct { + General *General `toml:"general"` + ChaosConfig *ChaosConfig `toml:"chaos"` + Wasp *WaspConfig `toml:"wasp"` + LoopedConfig *LoopedConfig `toml:"looped"` +} + +type LoopedConfig struct { + ContractConfig `toml:"contract"` + FuzzConfig `toml:"fuzz"` +} + +type ContractConfig struct { + ExecutionCount int `toml:"execution_count"` +} + +type FuzzConfig struct { + MinEmitWaitTimeMs int `toml:"min_emit_wait_time_ms"` + MaxEmitWaitTimeMs int `toml:"max_emit_wait_time_ms"` +} + +type General struct { + Generator string `toml:"generator"` + EventsToEmit []abi.Event `toml:"-"` + Contracts int `toml:"contracts"` + EventsPerTx int `toml:"events_per_tx"` + UseFinalityTag bool `toml:"use_finality_tag"` +} + +type ChaosConfig struct { + ExperimentCount int `toml:"experiment_count"` +} + +type WaspConfig struct { + Load *Load `toml:"load"` +} + +type Load struct { + RPS int64 `toml:"rps"` + LPS int64 `toml:"lps"` + RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` + Duration *models.Duration `toml:"duration"` + CallTimeout *models.Duration `toml:"call_timeout"` +} + +func ReadConfig(configName string) (*Config, error) { + var cfg *Config + d, err := os.ReadFile(configName) + if err != nil { + return nil, errors.Wrap(err, ErrReadPerfConfig) + } + err = toml.Unmarshal(d, &cfg) + if err != nil { + return nil, errors.Wrap(err, ErrUnmarshalPerfConfig) + } + + if err := cfg.validate(); err != nil { + return nil, err + } + + log.Debug().Interface("Config", cfg).Msg("Parsed config") + return cfg, nil +} + +func (c *Config) OverrideFromEnv() error { + if contr := os.Getenv("CONTRACTS"); contr != "" { + c.General.Contracts = mustParseInt(contr) + } + + if eventsPerTx := os.Getenv("EVENTS_PER_TX"); eventsPerTx != "" { + c.General.EventsPerTx = mustParseInt(eventsPerTx) + } + + if useFinalityTag := os.Getenv("USE_FINALITY_TAG"); useFinalityTag != "" { + c.General.UseFinalityTag = mustParseBool(useFinalityTag) + } + + if duration := os.Getenv("LOAD_DURATION"); duration != "" { + d, err := models.ParseDuration(duration) + if err != nil { + return err + } + + if c.General.Generator == GeneratorType_WASP { + c.Wasp.Load.Duration = &d + } else { + // make the looped generator approximately run for desired duration + // on average we will emit 1 event per second + c.LoopedConfig.FuzzConfig.MinEmitWaitTimeMs = 900 + c.LoopedConfig.FuzzConfig.MaxEmitWaitTimeMs = 1100 + c.LoopedConfig.ContractConfig.ExecutionCount = int(d.Duration().Seconds()) + } + } + + return nil +} + +func (c *Config) validate() error { + if c.General == nil { + return fmt.Errorf("General config is nil") + } + + err := c.General.validate() + if err != nil { + return fmt.Errorf("General config validation failed: %v", err) + } + + switch c.General.Generator { + case GeneratorType_WASP: + if c.Wasp == nil { + return fmt.Errorf("Wasp config is nil") + } + if c.Wasp.Load == nil { + return fmt.Errorf("Wasp load config is nil") + } + + err = c.Wasp.validate() + if err != nil { + return fmt.Errorf("Wasp config validation failed: %v", err) + } + case GeneratorType_Looped: + if c.LoopedConfig == nil { + return fmt.Errorf("Looped config is nil") + } + + err = c.LoopedConfig.validate() + if err != nil { + return fmt.Errorf("Looped config validation failed: %v", err) + } + default: + return fmt.Errorf("Unknown generator type: %s", c.General.Generator) + } + + return nil +} + +func (g *General) validate() error { + if g.Generator == "" { + return fmt.Errorf("Generator is empty") + } + + if g.Contracts == 0 { + return fmt.Errorf("Contracts is 0, but must be > 0") + } + + if g.EventsPerTx == 0 { + return fmt.Errorf("Events_per_tx is 0, but must be > 0") + } + + return nil +} + +func (w *WaspConfig) validate() error { + if w.Load == nil { + return fmt.Errorf("Load config is nil") + } + + err := w.Load.validate() + if err != nil { + return fmt.Errorf("Load config validation failed: %v", err) + } + + return nil +} + +func (l *Load) validate() error { + if l.RPS == 0 && l.LPS == 0 { + return fmt.Errorf("Either RPS or LPS needs to be set") + } + + if l.RPS != 0 && l.LPS != 0 { + return fmt.Errorf("Only one of RPS or LPS can be set") + } + + if l.Duration == nil { + return fmt.Errorf("duration is nil") + } + + if l.CallTimeout == nil { + return fmt.Errorf("call_timeout is nil") + } + if l.RateLimitUnitDuration == nil { + return fmt.Errorf("rate_limit_unit_duration is nil") + } + + return nil +} + +func (l *LoopedConfig) validate() error { + if l.ExecutionCount == 0 { + return fmt.Errorf("execution_count is 0, but must be > 0") + } + + if l.MinEmitWaitTimeMs == 0 { + return fmt.Errorf("min_emit_wait_time_ms is 0, but must be > 0") + } + + if l.MaxEmitWaitTimeMs == 0 { + return fmt.Errorf("max_emit_wait_time_ms is 0, but must be > 0") + } + + return nil +} + +func mustParseInt(s string) int { + i, err := strconv.Atoi(s) + if err != nil { + panic(err) + } + return i +} + +func mustParseBool(s string) bool { + b, err := strconv.ParseBool(s) + if err != nil { + panic(err) + } + return b +} diff --git a/integration-tests/universal/log_poller/gun.go b/integration-tests/universal/log_poller/gun.go new file mode 100644 index 00000000000..11932330a3b --- /dev/null +++ b/integration-tests/universal/log_poller/gun.go @@ -0,0 +1,78 @@ +package logpoller + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/wasp" +) + +/* LogEmitterGun is a gun that constantly emits logs from a contract */ +type LogEmitterGun struct { + contract *contracts.LogEmitter + eventsToEmit []abi.Event + logger zerolog.Logger + eventsPerTx int +} + +type Counter struct { + mu *sync.Mutex + value int +} + +func NewLogEmitterGun( + contract *contracts.LogEmitter, + eventsToEmit []abi.Event, + eventsPerTx int, + logger zerolog.Logger, +) *LogEmitterGun { + return &LogEmitterGun{ + contract: contract, + eventsToEmit: eventsToEmit, + eventsPerTx: eventsPerTx, + logger: logger, + } +} + +func (m *LogEmitterGun) Call(l *wasp.Generator) *wasp.CallResult { + localCounter := 0 + logEmitter := (*m.contract) + address := logEmitter.Address() + for _, event := range m.eventsToEmit { + m.logger.Debug().Str("Emitter address", address.String()).Str("Event type", event.Name).Msg("Emitting log from emitter") + var err error + switch event.Name { + case "Log1": + _, err = logEmitter.EmitLogInts(getIntSlice(m.eventsPerTx)) + case "Log2": + _, err = logEmitter.EmitLogIntsIndexed(getIntSlice(m.eventsPerTx)) + case "Log3": + _, err = logEmitter.EmitLogStrings(getStringSlice(m.eventsPerTx)) + default: + err = fmt.Errorf("Unknown event name: %s", event.Name) + } + + if err != nil { + return &wasp.CallResult{Error: err.Error(), Failed: true} + } + localCounter += 1 + } + + // I don't think that will work as expected, I should atomically read the value and save it, so maybe just a mutex? + if counter, ok := l.InputSharedData().(*Counter); ok { + counter.mu.Lock() + defer counter.mu.Unlock() + counter.value += localCounter + } else { + return &wasp.CallResult{ + Error: "SharedData did not contain a Counter", + Failed: true, + } + } + + return &wasp.CallResult{} +} diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go new file mode 100644 index 00000000000..aa488eb1be5 --- /dev/null +++ b/integration-tests/universal/log_poller/helpers.go @@ -0,0 +1,1136 @@ +package logpoller + +import ( + "bytes" + "context" + "errors" + "fmt" + "math/big" + "math/rand" + "sort" + "strings" + "sync" + "testing" + "time" + + geth "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + geth_types "github.com/ethereum/go-ethereum/core/types" + "github.com/rs/zerolog" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_blockchain "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/store/models" + + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/stretchr/testify/require" + + "github.com/scylladb/go-reflectx" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" + utils2 "github.com/smartcontractkit/chainlink/integration-tests/utils" + evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + lpEvm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/sqlx" +) + +var ( + EmitterABI, _ = abi.JSON(strings.NewReader(le.LogEmitterABI)) + automationUtilsABI = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) + bytes0 = [32]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, + } // bytes representation of 0x0000000000000000000000000000000000000000000000000000000000000000 + +) + +var registerSingleTopicFilter = func(registry contracts.KeeperRegistry, upkeepID *big.Int, emitterAddress common.Address, topic common.Hash) error { + logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ + ContractAddress: emitterAddress, + FilterSelector: 0, + Topic0: topic, + Topic1: bytes0, + Topic2: bytes0, + Topic3: bytes0, + } + encodedLogTriggerConfig, err := automationUtilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + if err != nil { + return err + } + + err = registry.SetUpkeepTriggerConfig(upkeepID, encodedLogTriggerConfig) + if err != nil { + return err + } + + return nil +} + +// this is not really possible, log trigger doesn't support multiple topics, even if log poller does +var registerMultipleTopicsFilter = func(registry contracts.KeeperRegistry, upkeepID *big.Int, emitterAddress common.Address, topics []abi.Event) error { + if len(topics) > 4 { + return errors.New("Cannot register more than 4 topics") + } + + var getTopic = func(topics []abi.Event, i int) common.Hash { + if i > len(topics)-1 { + return bytes0 + } + + return topics[i].ID + } + + var getFilterSelector = func(topics []abi.Event) (uint8, error) { + switch len(topics) { + case 0: + return 0, errors.New("Cannot register filter with 0 topics") + case 1: + return 0, nil + case 2: + return 1, nil + case 3: + return 3, nil + case 4: + return 7, nil + default: + return 0, errors.New("Cannot register filter with more than 4 topics") + } + } + + filterSelector, err := getFilterSelector(topics) + if err != nil { + return err + } + + logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ + ContractAddress: emitterAddress, + FilterSelector: filterSelector, + Topic0: getTopic(topics, 0), + Topic1: getTopic(topics, 1), + Topic2: getTopic(topics, 2), + Topic3: getTopic(topics, 3), + } + encodedLogTriggerConfig, err := automationUtilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + if err != nil { + return err + } + + err = registry.SetUpkeepTriggerConfig(upkeepID, encodedLogTriggerConfig) + if err != nil { + return err + } + + return nil +} + +func NewOrm(logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (*lpEvm.DbORM, *sqlx.DB, error) { + dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", "127.0.0.1", postgresDb.ExternalPort, postgresDb.User, postgresDb.Password, postgresDb.DbName) + db, err := sqlx.Open("postgres", dsn) + if err != nil { + return nil, db, err + } + + db.MapperFunc(reflectx.CamelToSnakeASCII) + return lpEvm.NewORM(chainID, db, logger, pg.NewQConfig(false)), db, nil +} + +type ExpectedFilter struct { + emitterAddress common.Address + topic common.Hash +} + +func getExpectedFilters(logEmitters []*contracts.LogEmitter, cfg *Config) []ExpectedFilter { + expectedFilters := make([]ExpectedFilter, 0) + for _, emitter := range logEmitters { + for _, event := range cfg.General.EventsToEmit { + expectedFilters = append(expectedFilters, ExpectedFilter{ + emitterAddress: (*emitter).Address(), + topic: event.ID, + }) + } + } + + return expectedFilters +} + +var nodeHasExpectedFilters = func(expectedFilters []ExpectedFilter, logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (bool, error) { + orm, db, err := NewOrm(logger, chainID, postgresDb) + if err != nil { + return false, err + } + + defer db.Close() + knownFilters, err := orm.LoadFilters() + if err != nil { + return false, err + } + + for _, expectedFilter := range expectedFilters { + filterFound := false + for _, knownFilter := range knownFilters { + if bytes.Equal(expectedFilter.emitterAddress.Bytes(), knownFilter.Addresses[0].Bytes()) && bytes.Equal(expectedFilter.topic.Bytes(), knownFilter.EventSigs[0].Bytes()) { + filterFound = true + break + } + } + + if !filterFound { + return false, fmt.Errorf("No filter found for emitter %s and topic %s", expectedFilter.emitterAddress.String(), expectedFilter.topic.Hex()) + } + } + + return true, nil +} + +var randomWait = func(minMilliseconds, maxMilliseconds int) { + rand.New(rand.NewSource(time.Now().UnixNano())) + randomMilliseconds := rand.Intn(maxMilliseconds-minMilliseconds+1) + minMilliseconds + time.Sleep(time.Duration(randomMilliseconds) * time.Millisecond) +} + +type LogEmitterChannel struct { + logsEmitted int + err error + currentIndex int +} + +func getIntSlice(length int) []int { + result := make([]int, length) + for i := 0; i < length; i++ { + result[i] = i + } + + return result +} + +func getStringSlice(length int) []string { + result := make([]string, length) + for i := 0; i < length; i++ { + result[i] = "amazing event" + } + + return result +} + +var emitEvents = func(ctx context.Context, l zerolog.Logger, logEmitter *contracts.LogEmitter, cfg *Config, wg *sync.WaitGroup, results chan LogEmitterChannel) { + address := (*logEmitter).Address().String() + localCounter := 0 + select { + case <-ctx.Done(): + l.Warn().Str("Emitter address", address).Msg("Context cancelled, not emitting events") + return + default: + defer wg.Done() + for i := 0; i < cfg.LoopedConfig.ExecutionCount; i++ { + for _, event := range cfg.General.EventsToEmit { + l.Debug().Str("Emitter address", address).Str("Event type", event.Name).Str("index", fmt.Sprintf("%d/%d", (i+1), cfg.LoopedConfig.ExecutionCount)).Msg("Emitting log from emitter") + var err error + switch event.Name { + case "Log1": + _, err = (*logEmitter).EmitLogInts(getIntSlice(cfg.General.EventsPerTx)) + case "Log2": + _, err = (*logEmitter).EmitLogIntsIndexed(getIntSlice(cfg.General.EventsPerTx)) + case "Log3": + _, err = (*logEmitter).EmitLogStrings(getStringSlice(cfg.General.EventsPerTx)) + default: + err = fmt.Errorf("Unknown event name: %s", event.Name) + } + + if err != nil { + results <- LogEmitterChannel{ + logsEmitted: 0, + err: err, + } + return + } + localCounter += cfg.General.EventsPerTx + + randomWait(cfg.LoopedConfig.FuzzConfig.MinEmitWaitTimeMs, cfg.LoopedConfig.FuzzConfig.MaxEmitWaitTimeMs) + } + + if (i+1)%10 == 0 { + l.Info().Str("Emitter address", address).Str("Index", fmt.Sprintf("%d/%d", i+1, cfg.LoopedConfig.ExecutionCount)).Msg("Emitted all three events") + } + } + + l.Info().Str("Emitter address", address).Int("Total logs emitted", localCounter).Msg("Finished emitting events") + + results <- LogEmitterChannel{ + logsEmitted: localCounter, + err: nil, + } + } +} + +var waitForEndBlockInLogPoller = func(endBlock int64, chainID *big.Int, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { + for i := 1; i < len(nodes.Nodes); i++ { + clNode := nodes.Nodes[i] + orm, db, err := NewOrm(coreLogger, chainID, clNode.PostgresDb) + if err != nil { + return false, err + } + + defer db.Close() + block, err := orm.SelectBlockByNumber(endBlock) + if err != nil { + return false, err + } + + if block == nil { + return false, nil + } + } + + return true, nil +} + +var chainHasFinalisedEndBlock = func(l zerolog.Logger, evmClient ctf_blockchain.EVMClient, endBlock int64) (bool, error) { + effectiveEndBlock := endBlock + 1 + lastFinalisedBlockHeader, err := evmClient.GetLatestFinalizedBlockHeader(context.Background()) + if err != nil { + return false, err + } + + l.Info().Int64("Last finalised block header", lastFinalisedBlockHeader.Number.Int64()).Int64("End block", effectiveEndBlock).Int64("Blocks left till end block", effectiveEndBlock-lastFinalisedBlockHeader.Number.Int64()).Msg("Waiting for the finalized block to move beyond end block") + + return lastFinalisedBlockHeader.Number.Int64() > effectiveEndBlock, nil +} + +var logPollerHasFinalisedEndBlock = func(endBlock int64, chainID *big.Int, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { + wg := &sync.WaitGroup{} + + type boolQueryResult struct { + nodeName string + hasFinalised bool + err error + } + + endBlockCh := make(chan boolQueryResult, len(nodes.Nodes)-1) + ctx, cancelFn := context.WithCancel(context.Background()) + + for i := 1; i < len(nodes.Nodes); i++ { + wg.Add(1) + + go func(clNode *test_env.ClNode, r chan boolQueryResult) { + defer wg.Done() + select { + case <-ctx.Done(): + return + default: + orm, db, err := NewOrm(coreLogger, chainID, clNode.PostgresDb) + if err != nil { + r <- boolQueryResult{ + nodeName: clNode.ContainerName, + hasFinalised: false, + err: err, + } + } + + defer db.Close() + + latestBlock, err := orm.SelectLatestBlock() + if err != nil { + r <- boolQueryResult{ + nodeName: clNode.ContainerName, + hasFinalised: false, + err: err, + } + } + + r <- boolQueryResult{ + nodeName: clNode.ContainerName, + hasFinalised: latestBlock.FinalizedBlockNumber > endBlock, + err: nil, + } + + } + }(nodes.Nodes[i], endBlockCh) + } + + var err error + allFinalisedCh := make(chan bool, 1) + + go func() { + foundMap := make(map[string]bool, 0) + for r := range endBlockCh { + if r.err != nil { + err = r.err + cancelFn() + return + } + + foundMap[r.nodeName] = r.hasFinalised + if r.hasFinalised { + l.Info().Str("Node name", r.nodeName).Msg("CL node has finalised end block") + } else { + l.Warn().Str("Node name", r.nodeName).Msg("CL node has not finalised end block yet") + } + + if len(foundMap) == len(nodes.Nodes)-1 { + allFinalised := true + for _, v := range foundMap { + if !v { + allFinalised = false + break + } + } + + allFinalisedCh <- allFinalised + return + } + } + }() + + wg.Wait() + close(endBlockCh) + + return <-allFinalisedCh, err +} + +var clNodesHaveExpectedLogCount = func(startBlock, endBlock int64, chainID *big.Int, expectedLogCount int, expectedFilters []ExpectedFilter, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { + wg := &sync.WaitGroup{} + + type logQueryResult struct { + nodeName string + logCount int + hasExpectedCount bool + err error + } + + queryCh := make(chan logQueryResult, len(nodes.Nodes)-1) + ctx, cancelFn := context.WithCancel(context.Background()) + + for i := 1; i < len(nodes.Nodes); i++ { + wg.Add(1) + + go func(clNode *test_env.ClNode, r chan logQueryResult) { + defer wg.Done() + select { + case <-ctx.Done(): + return + default: + orm, db, err := NewOrm(coreLogger, chainID, clNode.PostgresDb) + if err != nil { + r <- logQueryResult{ + nodeName: clNode.ContainerName, + logCount: 0, + hasExpectedCount: false, + err: err, + } + } + + defer db.Close() + foundLogsCount := 0 + + for _, filter := range expectedFilters { + logs, err := orm.SelectLogs(startBlock, endBlock, filter.emitterAddress, filter.topic) + if err != nil { + r <- logQueryResult{ + nodeName: clNode.ContainerName, + logCount: 0, + hasExpectedCount: false, + err: err, + } + } + + foundLogsCount += len(logs) + } + + r <- logQueryResult{ + nodeName: clNode.ContainerName, + logCount: foundLogsCount, + hasExpectedCount: foundLogsCount >= expectedLogCount, + err: err, + } + } + }(nodes.Nodes[i], queryCh) + } + + var err error + allFoundCh := make(chan bool, 1) + + go func() { + foundMap := make(map[string]bool, 0) + for r := range queryCh { + if r.err != nil { + err = r.err + cancelFn() + return + } + + foundMap[r.nodeName] = r.hasExpectedCount + if r.hasExpectedCount { + l.Info().Str("Node name", r.nodeName).Int("Logs count", r.logCount).Msg("Expected log count found in CL node") + } else { + l.Warn().Str("Node name", r.nodeName).Str("Found/Expected logs", fmt.Sprintf("%d/%d", r.logCount, expectedLogCount)).Int("Missing logs", expectedLogCount-r.logCount).Msg("Too low log count found in CL node") + } + + if len(foundMap) == len(nodes.Nodes)-1 { + allFound := true + for _, v := range foundMap { + if !v { + allFound = false + break + } + } + + allFoundCh <- allFound + return + } + } + }() + + wg.Wait() + close(queryCh) + + return <-allFoundCh, err +} + +type MissingLogs map[string][]geth_types.Log + +func (m *MissingLogs) IsEmpty() bool { + for _, v := range *m { + if len(v) > 0 { + return false + } + } + + return true +} + +var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient ctf_blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *Config) (MissingLogs, error) { + wg := &sync.WaitGroup{} + + type dbQueryResult struct { + err error + nodeName string + logs []logpoller.Log + } + + ctx, cancelFn := context.WithCancel(context.Background()) + resultCh := make(chan dbQueryResult, len(clnodeCluster.Nodes)-1) + + for i := 1; i < len(clnodeCluster.Nodes); i++ { + wg.Add(1) + + go func(ctx context.Context, i int, r chan dbQueryResult) { + defer wg.Done() + select { + case <-ctx.Done(): + l.Warn().Msg("Context cancelled. Terminating fetching logs from log poller's DB") + return + default: + nodeName := clnodeCluster.Nodes[i].ContainerName + + l.Info().Str("Node name", nodeName).Msg("Fetching log poller logs") + orm, db, err := NewOrm(coreLogger, evmClient.GetChainID(), clnodeCluster.Nodes[i].PostgresDb) + if err != nil { + r <- dbQueryResult{ + err: err, + nodeName: nodeName, + logs: []logpoller.Log{}, + } + } + + defer db.Close() + logs := make([]logpoller.Log, 0) + + for j := 0; j < len(logEmitters); j++ { + address := (*logEmitters[j]).Address() + + for _, event := range cfg.General.EventsToEmit { + l.Debug().Str("Event name", event.Name).Str("Emitter address", address.String()).Msg("Fetching single emitter's logs") + result, err := orm.SelectLogs(startBlock, endBlock, address, event.ID) + if err != nil { + r <- dbQueryResult{ + err: err, + nodeName: nodeName, + logs: []logpoller.Log{}, + } + } + + sort.Slice(result, func(i, j int) bool { + return result[i].BlockNumber < result[j].BlockNumber + }) + + logs = append(logs, result...) + + l.Debug().Str("Event name", event.Name).Str("Emitter address", address.String()).Int("Log count", len(result)).Msg("Logs found per node") + } + } + + l.Warn().Int("Count", len(logs)).Str("Node name", nodeName).Msg("Fetched log poller logs") + + r <- dbQueryResult{ + err: nil, + nodeName: nodeName, + logs: logs, + } + } + }(ctx, i, resultCh) + } + + allLogPollerLogs := make(map[string][]logpoller.Log, 0) + missingLogs := map[string][]geth_types.Log{} + var dbError error + + go func() { + for r := range resultCh { + if r.err != nil { + l.Err(r.err).Str("Node name", r.nodeName).Msg("Error fetching logs from log poller's DB") + dbError = r.err + cancelFn() + return + } + // use channel for aggregation and then for := range over it after closing resultCh? + allLogPollerLogs[r.nodeName] = r.logs + } + }() + + wg.Wait() + close(resultCh) + + if dbError != nil { + return nil, dbError + } + + allLogsInEVMNode, err := getEVMLogs(startBlock, endBlock, logEmitters, evmClient, l, cfg) + if err != nil { + return nil, err + } + + wg = &sync.WaitGroup{} + + type missingLogResult struct { + nodeName string + logs []geth_types.Log + } + + l.Info().Msg("Started comparison of logs from EVM node and CL nodes. This may take a while if there's a lot of logs") + missingCh := make(chan missingLogResult, len(clnodeCluster.Nodes)-1) + evmLogCount := len(allLogsInEVMNode) + for i := 1; i < len(clnodeCluster.Nodes); i++ { + wg.Add(1) + + go func(i int, result chan missingLogResult) { + defer wg.Done() + nodeName := clnodeCluster.Nodes[i].ContainerName + l.Info().Str("Node name", nodeName).Str("Progress", fmt.Sprintf("0/%d", evmLogCount)).Msg("Comparing single CL node's logs with EVM logs") + + missingLogs := make([]geth_types.Log, 0) + for i, evmLog := range allLogsInEVMNode { + logFound := false + for _, logPollerLog := range allLogPollerLogs[nodeName] { + if logPollerLog.BlockNumber == int64(evmLog.BlockNumber) && logPollerLog.TxHash == evmLog.TxHash && bytes.Equal(logPollerLog.Data, evmLog.Data) && logPollerLog.LogIndex == int64(evmLog.Index) && + logPollerLog.Address == evmLog.Address && logPollerLog.BlockHash == evmLog.BlockHash && bytes.Equal(logPollerLog.Topics[0][:], evmLog.Topics[0].Bytes()) { + logFound = true + continue + } + } + + if i%10000 == 0 && i != 0 { + l.Info().Str("Node name", nodeName).Str("Progress", fmt.Sprintf("%d/%d", i, evmLogCount)).Msg("Comparing single CL node's logs with EVM logs") + } + + if !logFound { + missingLogs = append(missingLogs, evmLog) + } + } + + if len(missingLogs) > 0 { + l.Warn().Int("Count", len(missingLogs)).Str("Node name", nodeName).Msg("Some EMV logs were missing from CL node") + } else { + l.Info().Str("Node name", nodeName).Msg("All EVM logs were found in CL node") + } + + result <- missingLogResult{ + nodeName: nodeName, + logs: missingLogs, + } + }(i, missingCh) + } + + wg.Wait() + close(missingCh) + + for v := range missingCh { + if len(v.logs) > 0 { + missingLogs[v.nodeName] = v.logs + } + } + + expectedTotalLogsEmitted := getExpectedLogCount(cfg) + if int64(len(allLogsInEVMNode)) != expectedTotalLogsEmitted { + l.Warn().Str("Actual/Expected", fmt.Sprintf("%d/%d", expectedTotalLogsEmitted, len(allLogsInEVMNode))).Msg("Some of the test logs were not found in EVM node. This is a bug in the test") + } + + return missingLogs, nil +} + +var printMissingLogsByType = func(missingLogs map[string][]geth_types.Log, l zerolog.Logger, cfg *Config) { + var findHumanName = func(topic common.Hash) string { + for _, event := range cfg.General.EventsToEmit { + if event.ID == topic { + return event.Name + } + } + + return "Unknown event" + } + + missingByType := make(map[string]int) + for _, logs := range missingLogs { + for _, v := range logs { + humanName := findHumanName(v.Topics[0]) + if _, ok := missingByType[humanName]; ok { + missingByType[humanName] += 1 + } else { + missingByType[humanName] = 1 + } + } + } + + for k, v := range missingByType { + l.Warn().Str("Event name", k).Int("Missing count", v).Msg("Missing logs by type") + } +} + +var getEVMLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient ctf_blockchain.EVMClient, l zerolog.Logger, cfg *Config) ([]geth_types.Log, error) { + allLogsInEVMNode := make([]geth_types.Log, 0) + for j := 0; j < len(logEmitters); j++ { + address := (*logEmitters[j]).Address() + for _, event := range cfg.General.EventsToEmit { + l.Debug().Str("Event name", event.Name).Str("Emitter address", address.String()).Msg("Fetching logs from EVM node") + logsInEVMNode, err := evmClient.FilterLogs(context.Background(), geth.FilterQuery{ + Addresses: []common.Address{(address)}, + Topics: [][]common.Hash{{event.ID}}, + FromBlock: big.NewInt(startBlock), + ToBlock: big.NewInt(endBlock), + }) + if err != nil { + return nil, err + } + + sort.Slice(logsInEVMNode, func(i, j int) bool { + return logsInEVMNode[i].BlockNumber < logsInEVMNode[j].BlockNumber + }) + + allLogsInEVMNode = append(allLogsInEVMNode, logsInEVMNode...) + l.Debug().Str("Event name", event.Name).Str("Emitter address", address.String()).Int("Log count", len(logsInEVMNode)).Msg("Logs found in EVM node") + } + } + + l.Warn().Int("Count", len(allLogsInEVMNode)).Msg("Logs in EVM node") + + return allLogsInEVMNode, nil +} + +func executeGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { + if cfg.General.Generator == GeneratorType_WASP { + return runWaspGenerator(t, cfg, logEmitters) + } + + return runLoopedGenerator(t, cfg, logEmitters) +} + +func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { + l := logging.GetTestLogger(t) + + var RPSprime int64 + + // if LPS is set, we need to calculate based on countract count and events per transaction + if cfg.Wasp.Load.LPS > 0 { + RPSprime = cfg.Wasp.Load.LPS / int64(cfg.General.Contracts) / int64(cfg.General.EventsPerTx) / int64(len(cfg.General.EventsToEmit)) + + if RPSprime < 1 { + return 0, fmt.Errorf("Invalid load configuration, effective RPS would have been zero. Adjust LPS, contracts count, events per tx or events to emit") + } + } + + // if RPS is set simply split it between contracts + if cfg.Wasp.Load.RPS > 0 { + RPSprime = cfg.Wasp.Load.RPS / int64(cfg.General.Contracts) + } + + counter := &Counter{ + mu: &sync.Mutex{}, + value: 0, + } + + p := wasp.NewProfile() + + for _, logEmitter := range logEmitters { + g, err := wasp.NewGenerator(&wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: fmt.Sprintf("log_poller_gen_%s", (*logEmitter).Address().String()), + RateLimitUnitDuration: cfg.Wasp.Load.RateLimitUnitDuration.Duration(), + CallTimeout: cfg.Wasp.Load.CallTimeout.Duration(), + Schedule: wasp.Plain( + RPSprime, + cfg.Wasp.Load.Duration.Duration(), + ), + Gun: NewLogEmitterGun( + logEmitter, + cfg.General.EventsToEmit, + cfg.General.EventsPerTx, + l, + ), + SharedData: counter, + }) + p.Add(g, err) + } + + _, err := p.Run(true) + + if err != nil { + return 0, err + } + + return counter.value, nil +} + +func runLoopedGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmitter) (int, error) { + l := logging.GetTestLogger(t) + + // Start emitting events in parallel, each contract is emitting events in a separate goroutine + // We will stop as soon as we encounter an error + wg := &sync.WaitGroup{} + emitterCh := make(chan LogEmitterChannel, len(logEmitters)) + + ctx, cancelFn := context.WithCancel(context.Background()) + defer cancelFn() + + for i := 0; i < len(logEmitters); i++ { + wg.Add(1) + go emitEvents(ctx, l, logEmitters[i], cfg, wg, emitterCh) + } + + var emitErr error + total := 0 + + aggrChan := make(chan int, len(logEmitters)) + + go func() { + for emitter := range emitterCh { + if emitter.err != nil { + emitErr = emitter.err + cancelFn() + return + } + aggrChan <- emitter.logsEmitted + } + }() + + wg.Wait() + close(emitterCh) + + for i := 0; i < len(logEmitters); i++ { + total += <-aggrChan + } + + if emitErr != nil { + return 0, emitErr + } + + return int(total), nil +} + +func getExpectedLogCount(cfg *Config) int64 { + if cfg.General.Generator == GeneratorType_WASP { + if cfg.Wasp.Load.RPS != 0 { + return cfg.Wasp.Load.RPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) * int64(cfg.General.EventsPerTx) + } else { + return cfg.Wasp.Load.LPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) + } + } + + return int64(len(cfg.General.EventsToEmit) * cfg.LoopedConfig.ExecutionCount * cfg.General.Contracts * cfg.General.EventsPerTx) +} + +var chaosPauseSyncFn = func(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv) error { + rand.New(rand.NewSource(time.Now().UnixNano())) + randomBool := rand.Intn(2) == 0 + + randomNode := testEnv.ClCluster.Nodes[rand.Intn(len(testEnv.ClCluster.Nodes)-1)+1] + var component ctf_test_env.EnvComponent + + if randomBool { + component = randomNode.EnvComponent + } else { + component = randomNode.PostgresDb.EnvComponent + } + + pauseTimeSec := rand.Intn(20-5) + 5 + l.Info().Str("Container", component.ContainerName).Int("Pause time", pauseTimeSec).Msg("Pausing component") + pauseTimeDur := time.Duration(pauseTimeSec) * time.Second + err := component.ChaosPause(l, pauseTimeDur) + l.Info().Str("Container", component.ContainerName).Msg("Component unpaused") + + if err != nil { + return err + } + + return nil +} + +var executeChaosExperiment = func(l zerolog.Logger, testEnv *test_env.CLClusterTestEnv, cfg *Config, errorCh chan error) { + if cfg.ChaosConfig == nil || cfg.ChaosConfig.ExperimentCount == 0 { + errorCh <- nil + return + } + + chaosChan := make(chan error, cfg.ChaosConfig.ExperimentCount) + + wg := &sync.WaitGroup{} + + go func() { + // if we wanted to have more than 1 container paused, we'd need to make sure we aren't trying to pause an already paused one + guardChan := make(chan struct{}, 1) + + for i := 0; i < cfg.ChaosConfig.ExperimentCount; i++ { + wg.Add(1) + guardChan <- struct{}{} + go func() { + defer func() { + <-guardChan + wg.Done() + l.Info().Str("Current/Total", fmt.Sprintf("%d/%d", i, cfg.ChaosConfig.ExperimentCount)).Msg("Done with experiment") + }() + chaosChan <- chaosPauseSyncFn(l, testEnv) + }() + } + + wg.Wait() + + close(chaosChan) + }() + + go func() { + for { + select { + case err, ok := <-chaosChan: + if !ok { + l.Info().Msg("All chaos experiments finished") + errorCh <- nil + return + } else { + if err != nil { + l.Err(err).Msg("Error encountered during chaos experiment") + errorCh <- err + return + } + } + } + } + }() +} + +var GetFinalityDepth = func(chainId int64) (int64, error) { + var finalityDepth int64 + switch chainId { + // Ethereum Sepolia + case 11155111: + finalityDepth = 50 + // Polygon Mumbai + case 80001: + finalityDepth = 500 + // Simulated network + case 1337: + finalityDepth = 10 + default: + return 0, fmt.Errorf("No known finality depth for chain %d", chainId) + } + + return finalityDepth, nil +} + +var GetEndBlockToWaitFor = func(endBlock, chainId int64, cfg *Config) (int64, error) { + if cfg.General.UseFinalityTag { + return endBlock + 1, nil + } + + finalityDepth, err := GetFinalityDepth(chainId) + if err != nil { + return 0, err + } + + return endBlock + finalityDepth, nil +} + +const ( + automationDefaultUpkeepGasLimit = uint32(2500000) + automationDefaultLinkFunds = int64(9e18) + automationDefaultUpkeepsToDeploy = 10 + automationExpectedData = "abcdef" + defaultAmountOfUpkeeps = 2 +) + +var ( + defaultOCRRegistryConfig = contracts.KeeperRegistrySettings{ + PaymentPremiumPPB: uint32(200000000), + FlatFeeMicroLINK: uint32(0), + BlockCountPerTurn: big.NewInt(10), + CheckGasLimit: uint32(2500000), + StalenessSeconds: big.NewInt(90000), + GasCeilingMultiplier: uint16(1), + MinUpkeepSpend: big.NewInt(0), + MaxPerformGas: uint32(5000000), + FallbackGasPrice: big.NewInt(2e11), + FallbackLinkPrice: big.NewInt(2e18), + MaxCheckDataSize: uint32(5000), + MaxPerformDataSize: uint32(5000), + } + + automationDefaultRegistryConfig = contracts.KeeperRegistrySettings{ + PaymentPremiumPPB: uint32(200000000), + FlatFeeMicroLINK: uint32(0), + BlockCountPerTurn: big.NewInt(10), + CheckGasLimit: uint32(2500000), + StalenessSeconds: big.NewInt(90000), + GasCeilingMultiplier: uint16(1), + MinUpkeepSpend: big.NewInt(0), + MaxPerformGas: uint32(5000000), + FallbackGasPrice: big.NewInt(2e11), + FallbackLinkPrice: big.NewInt(2e18), + MaxCheckDataSize: uint32(5000), + MaxPerformDataSize: uint32(5000), + } +) + +func setupLogPollerTestDocker( + t *testing.T, + testName string, + registryVersion ethereum.KeeperRegistryVersion, + registryConfig contracts.KeeperRegistrySettings, + upkeepsNeeded int, + lpPollingInterval time.Duration, + finalityTagEnabled bool, +) ( + blockchain.EVMClient, + []*client.ChainlinkClient, + contracts.ContractDeployer, + contracts.LinkToken, + contracts.KeeperRegistry, + contracts.KeeperRegistrar, + *test_env.CLClusterTestEnv, +) { + l := logging.GetTestLogger(t) + // Add registry version to config + registryConfig.RegistryVersion = registryVersion + network := networks.MustGetSelectedNetworksFromEnv()[0] + + finalityDepth, err := GetFinalityDepth(network.ChainID) + require.NoError(t, err, "Error getting finality depth") + + // build the node config + clNodeConfig := node.NewConfig(node.NewBaseConfig()) + syncInterval := models.MustMakeDuration(5 * time.Minute) + clNodeConfig.Feature.LogPoller = it_utils.Ptr[bool](true) + clNodeConfig.OCR2.Enabled = it_utils.Ptr[bool](true) + clNodeConfig.Keeper.TurnLookBack = it_utils.Ptr[int64](int64(0)) + clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval + clNodeConfig.Keeper.Registry.PerformGasOverhead = it_utils.Ptr[uint32](uint32(150000)) + clNodeConfig.P2P.V2.Enabled = it_utils.Ptr[bool](true) + clNodeConfig.P2P.V2.AnnounceAddresses = &[]string{"0.0.0.0:6690"} + clNodeConfig.P2P.V2.ListenAddresses = &[]string{"0.0.0.0:6690"} + + //launch the environment + var env *test_env.CLClusterTestEnv + chainlinkNodeFunding := 0.5 + l.Debug().Msgf("Funding amount: %f", chainlinkNodeFunding) + clNodesCount := 5 + + var logPolllerSettingsFn = func(chain *evmcfg.Chain) *evmcfg.Chain { + chain.LogPollInterval = models.MustNewDuration(lpPollingInterval) + chain.FinalityDepth = utils2.Ptr[uint32](uint32(finalityDepth)) + chain.FinalityTagEnabled = utils2.Ptr[bool](finalityTagEnabled) + return chain + } + + var evmClientSettingsFn = func(network *blockchain.EVMNetwork) *blockchain.EVMNetwork { + network.FinalityDepth = uint64(finalityDepth) + network.FinalityTag = finalityTagEnabled + return network + } + + env, err = test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(clNodesCount). + WithCLNodeConfig(clNodeConfig). + WithFunding(big.NewFloat(chainlinkNodeFunding)). + WithChainOptions(logPolllerSettingsFn). + EVMClientNetworkOptions(evmClientSettingsFn). + WithStandardCleanup(). + Build() + require.NoError(t, err, "Error deploying test environment") + + env.ParallelTransactions(true) + nodeClients := env.ClCluster.NodeAPIs() + workerNodes := nodeClients[1:] + + var linkToken contracts.LinkToken + + switch network.ChainID { + // Simulated + case 1337: + linkToken, err = env.ContractDeployer.DeployLinkTokenContract() + // Ethereum Sepolia + case 11155111: + linkToken, err = env.ContractLoader.LoadLINKToken("0x779877A7B0D9E8603169DdbD7836e478b4624789") + // Polygon Mumbai + case 80001: + linkToken, err = env.ContractLoader.LoadLINKToken("0x326C977E6efc84E512bB9C30f76E30c160eD06FB") + default: + panic("Not implemented") + } + require.NoError(t, err, "Error loading/deploying LINK token") + + linkBalance, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(linkToken.Address())) + require.NoError(t, err, "Error getting LINK balance") + + l.Info().Str("Balance", big.NewInt(0).Div(linkBalance, big.NewInt(1e18)).String()).Msg("LINK balance") + minLinkBalanceSingleNode := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(9)) + minLinkBalance := big.NewInt(0).Mul(minLinkBalanceSingleNode, big.NewInt(int64(upkeepsNeeded))) + if minLinkBalance.Cmp(linkBalance) < 0 { + require.FailNowf(t, "Not enough LINK", "Not enough LINK to run the test. Need at least %s", big.NewInt(0).Div(minLinkBalance, big.NewInt(1e18)).String()) + } + + registry, registrar := actions.DeployAutoOCRRegistryAndRegistrar( + t, + registryVersion, + registryConfig, + linkToken, + env.ContractDeployer, + env.EVMClient, + ) + + // Fund the registry with LINK + err = linkToken.Transfer(registry.Address(), big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(defaultAmountOfUpkeeps)))) + require.NoError(t, err, "Funding keeper registry contract shouldn't fail") + + err = actions.CreateOCRKeeperJobsLocal(l, nodeClients, registry.Address(), network.ChainID, 0, registryVersion) + require.NoError(t, err, "Error creating OCR Keeper Jobs") + ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, workerNodes, registryConfig, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress()) + require.NoError(t, err, "Error building OCR config vars") + err = registry.SetConfig(automationDefaultRegistryConfig, ocrConfig) + require.NoError(t, err, "Registry config should be set successfully") + require.NoError(t, env.EVMClient.WaitForEvents(), "Waiting for config to be set") + + return env.EVMClient, nodeClients, env.ContractDeployer, linkToken, registry, registrar, env +} diff --git a/integration-tests/universal/log_poller/scenarios.go b/integration-tests/universal/log_poller/scenarios.go new file mode 100644 index 00000000000..d14a3bcb2a7 --- /dev/null +++ b/integration-tests/universal/log_poller/scenarios.go @@ -0,0 +1,498 @@ +package logpoller + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + "github.com/onsi/gomega" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/stretchr/testify/require" +) + +func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { + l := logging.GetTestLogger(t) + coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ + + if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { + l.Warn().Msg("No events to emit specified, using all events from log emitter contract") + for _, event := range EmitterABI.Events { + cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) + } + } + + l.Info().Msg("Starting basic log poller test") + + var ( + err error + testName = "basic-log-poller" + upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) + ) + + chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( + t, testName, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(500*time.Millisecond), cfg.General.UseFinalityTag, + ) + + _, upkeepIDs := actions.DeployConsumers( + t, + registry, + registrar, + linkToken, + contractDeployer, + chainClient, + upKeepsNeeded, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + true, + false, + ) + + // Deploy Log Emitter contracts + logEmitters := make([]*contracts.LogEmitter, 0) + for i := 0; i < cfg.General.Contracts; i++ { + logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() + logEmitters = append(logEmitters, &logEmitter) + require.NoError(t, err, "Error deploying log emitter contract") + l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") + time.Sleep(200 * time.Millisecond) + } + + // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) + // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) + for i := 0; i < len(upkeepIDs); i++ { + emitterAddress := (*logEmitters[i%cfg.General.Contracts]).Address() + upkeepID := upkeepIDs[i] + topicId := cfg.General.EventsToEmit[i%len(cfg.General.EventsToEmit)].ID + + l.Info().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") + err = registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) + randomWait(50, 200) + require.NoError(t, err, "Error registering log trigger for log emitter") + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + + // Make sure that all nodes have expected filters registered before starting to emit events + expectedFilters := getExpectedFilters(logEmitters, cfg) + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { + nodeName := testEnv.ClCluster.Nodes[i].ContainerName + l.Info().Str("Node name", nodeName).Msg("Fetching filters from log poller's DB") + + hasFilters, err := nodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) + if err != nil { + l.Warn().Err(err).Msg("Error checking if node has expected filters. Retrying...") + return + } + + g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") + } + }, "30s", "1s").Should(gomega.Succeed()) + l.Info().Msg("All nodes have expected filters registered") + l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") + + // Save block number before starting to emit events, so that we can later use it when querying logs + sb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + startBlock := int64(sb) + + l.Info().Msg("STARTING EVENT EMISSION") + startTime := time.Now() + + // Start chaos experimnents by randomly pausing random containers (Chainlink nodes or their DBs) + chaosDoneCh := make(chan error, 1) + go func() { + executeChaosExperiment(l, testEnv, cfg, chaosDoneCh) + }() + + totalLogsEmitted, err := executeGenerator(t, cfg, logEmitters) + endTime := time.Now() + require.NoError(t, err, "Error executing event generator") + + expectedLogsEmitted := getExpectedLogCount(cfg) + duration := int(endTime.Sub(startTime).Seconds()) + l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") + + // Save block number after finishing to emit events, so that we can later use it when querying logs + eb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + + endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) + require.NoError(t, err, "Error getting end block to wait for") + + l.Info().Msg("Waiting before proceeding with test until all chaos experiments finish") + chaosError := <-chaosDoneCh + require.NoError(t, chaosError, "Error encountered during chaos experiment") + + // Wait until last block in which events were emitted has been finalised + // how long should we wait here until all logs are processed? wait for block X to be processed by all nodes? + waitDuration := "15m" + l.Warn().Str("Duration", waitDuration).Msg("Waiting for logs to be processed by all nodes and for chain to advance beyond finality") + + gom.Eventually(func(g gomega.Gomega) { + hasAdvanced, err := chainHasFinalisedEndBlock(l, testEnv.EVMClient, endBlock) + if err != nil { + l.Warn().Err(err).Msg("Error checking if chain has advanced beyond finality. Retrying...") + } + g.Expect(hasAdvanced).To(gomega.BeTrue(), "Chain has not advanced beyond finality") + }, waitDuration, "30s").Should(gomega.Succeed()) + + l.Warn().Str("Duration", "1m").Msg("Waiting for all CL nodes to have end block finalised") + gom.Eventually(func(g gomega.Gomega) { + hasFinalised, err := logPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") + } + g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") + }, "1m", "30s").Should(gomega.Succeed()) + + gom.Eventually(func(g gomega.Gomega) { + logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), totalLogsEmitted, expectedFilters, l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if CL nodes have expected log count. Retrying...") + } + g.Expect(logCountMatches).To(gomega.BeTrue(), "Not all CL nodes have expected log count") + }, waitDuration, "5s").Should(gomega.Succeed()) + + // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has + logConsistencyWaitDuration := "1m" + l.Warn().Str("Duration", logConsistencyWaitDuration).Msg("Waiting for CL nodes to have all the logs that EVM node has") + + gom.Eventually(func(g gomega.Gomega) { + missingLogs, err := getMissingLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, testEnv.ClCluster, l, coreLogger, cfg) + if err != nil { + l.Warn().Err(err).Msg("Error getting missing logs. Retrying...") + } + + if !missingLogs.IsEmpty() { + printMissingLogsByType(missingLogs, l, cfg) + } + g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") + }, logConsistencyWaitDuration, "5s").Should(gomega.Succeed()) +} + +func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string) { + l := logging.GetTestLogger(t) + coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ + + if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { + l.Warn().Msg("No events to emit specified, using all events from log emitter contract") + for _, event := range EmitterABI.Events { + cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) + } + } + + l.Info().Msg("Starting replay log poller test") + + var ( + err error + testName = "replay-log-poller" + upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) + ) + + // we set blockBackfillDepth to 0, to make sure nothing will be backfilled and won't interfere with our test + chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( + t, testName, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag) + + _, upkeepIDs := actions.DeployConsumers( + t, + registry, + registrar, + linkToken, + contractDeployer, + chainClient, + upKeepsNeeded, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + true, + false, + ) + + // Deploy Log Emitter contracts + logEmitters := make([]*contracts.LogEmitter, 0) + for i := 0; i < cfg.General.Contracts; i++ { + logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() + logEmitters = append(logEmitters, &logEmitter) + require.NoError(t, err, "Error deploying log emitter contract") + l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") + time.Sleep(200 * time.Millisecond) + } + + //wait for contracts to be uploaded to chain, TODO: could make this wait fluent + time.Sleep(5 * time.Second) + + // Save block number before starting to emit events, so that we can later use it when querying logs + sb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + startBlock := int64(sb) + + l.Info().Msg("STARTING EVENT EMISSION") + startTime := time.Now() + totalLogsEmitted, err := executeGenerator(t, cfg, logEmitters) + endTime := time.Now() + require.NoError(t, err, "Error executing event generator") + expectedLogsEmitted := getExpectedLogCount(cfg) + duration := int(endTime.Sub(startTime).Seconds()) + l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") + + // Save block number after finishing to emit events, so that we can later use it when querying logs + eb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + + endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) + require.NoError(t, err, "Error getting end block to wait for") + + // Lets make sure no logs are in DB yet + expectedFilters := getExpectedFilters(logEmitters, cfg) + logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), 0, expectedFilters, l, coreLogger, testEnv.ClCluster) + require.NoError(t, err, "Error checking if CL nodes have expected log count") + require.True(t, logCountMatches, "Some CL nodes already had logs in DB") + l.Info().Msg("No logs were saved by CL nodes yet, as expected. Proceeding.") + + // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) + // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) + for i := 0; i < len(upkeepIDs); i++ { + emitterAddress := (*logEmitters[i%cfg.General.Contracts]).Address() + upkeepID := upkeepIDs[i] + topicId := cfg.General.EventsToEmit[i%len(cfg.General.EventsToEmit)].ID + + l.Info().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") + err = registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) + require.NoError(t, err, "Error registering log trigger for log emitter") + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + + // Make sure that all nodes have expected filters registered before starting to emit events + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { + nodeName := testEnv.ClCluster.Nodes[i].ContainerName + l.Info().Str("Node name", nodeName).Msg("Fetching filters from log poller's DB") + + hasFilters, err := nodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) + if err != nil { + l.Warn().Err(err).Msg("Error checking if node has expected filters. Retrying...") + return + } + + g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") + } + }, "30s", "1s").Should(gomega.Succeed()) + l.Info().Msg("All nodes have expected filters registered") + l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") + + l.Warn().Str("Duration", "1m").Msg("Waiting for all CL nodes to have end block finalised") + gom.Eventually(func(g gomega.Gomega) { + hasFinalised, err := logPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") + } + g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") + }, "1m", "30s").Should(gomega.Succeed()) + + // Trigger replay + l.Info().Msg("Triggering log poller's replay") + for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { + nodeName := testEnv.ClCluster.Nodes[i].ContainerName + response, _, err := testEnv.ClCluster.Nodes[i].API.ReplayLogPollerFromBlock(startBlock, testEnv.EVMClient.GetChainID().Int64()) + require.NoError(t, err, "Error triggering log poller's replay on node %s", nodeName) + require.Equal(t, "Replay started", response.Data.Attributes.Message, "Unexpected response message from log poller's replay") + } + + l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for logs to be processed by all nodes and for chain to advance beyond finality") + + gom.Eventually(func(g gomega.Gomega) { + logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), totalLogsEmitted, expectedFilters, l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if CL nodes have expected log count. Retrying...") + } + g.Expect(logCountMatches).To(gomega.BeTrue(), "Not all CL nodes have expected log count") + }, consistencyTimeout, "30s").Should(gomega.Succeed()) + + // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has + l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for CL nodes to have all the logs that EVM node has") + + gom.Eventually(func(g gomega.Gomega) { + missingLogs, err := getMissingLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, testEnv.ClCluster, l, coreLogger, cfg) + if err != nil { + l.Warn().Err(err).Msg("Error getting missing logs. Retrying...") + } + + if !missingLogs.IsEmpty() { + printMissingLogsByType(missingLogs, l, cfg) + } + g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") + }, consistencyTimeout, "10s").Should(gomega.Succeed()) +} + +type FinalityBlockFn = func(chainId int64, endBlock int64) (int64, error) + +func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { + l := logging.GetTestLogger(t) + coreLogger := core_logger.TestLogger(t) //needed by ORM ¯\_(ツ)_/¯ + + if cfg.General.EventsToEmit == nil || len(cfg.General.EventsToEmit) == 0 { + l.Warn().Msg("No events to emit specified, using all events from log emitter contract") + for _, event := range EmitterABI.Events { + cfg.General.EventsToEmit = append(cfg.General.EventsToEmit, event) + } + } + + l.Info().Msg("Starting CI log poller test") + + var ( + err error + testName = "ci-log-poller" + upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) + ) + + chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( + t, testName, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag, + ) + + _, upkeepIDs := actions.DeployConsumers( + t, + registry, + registrar, + linkToken, + contractDeployer, + chainClient, + upKeepsNeeded, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + true, + false, + ) + + // Deploy Log Emitter contracts + logEmitters := make([]*contracts.LogEmitter, 0) + for i := 0; i < cfg.General.Contracts; i++ { + logEmitter, err := testEnv.ContractDeployer.DeployLogEmitterContract() + logEmitters = append(logEmitters, &logEmitter) + require.NoError(t, err, "Error deploying log emitter contract") + l.Info().Str("Contract address", logEmitter.Address().Hex()).Msg("Log emitter contract deployed") + time.Sleep(200 * time.Millisecond) + } + + // Register log triggered upkeep for each combination of log emitter contract and event signature (topic) + // We need to register a separate upkeep for each event signature, because log trigger doesn't support multiple topics (even if log poller does) + for i := 0; i < len(upkeepIDs); i++ { + emitterAddress := (*logEmitters[i%cfg.General.Contracts]).Address() + upkeepID := upkeepIDs[i] + topicId := cfg.General.EventsToEmit[i%len(cfg.General.EventsToEmit)].ID + + l.Info().Int("Upkeep id", int(upkeepID.Int64())).Str("Emitter address", emitterAddress.String()).Str("Topic", topicId.Hex()).Msg("Registering log trigger for log emitter") + err = registerSingleTopicFilter(registry, upkeepID, emitterAddress, topicId) + randomWait(50, 200) + require.NoError(t, err, "Error registering log trigger for log emitter") + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") + + // Make sure that all nodes have expected filters registered before starting to emit events + expectedFilters := getExpectedFilters(logEmitters, cfg) + gom := gomega.NewGomegaWithT(t) + gom.Eventually(func(g gomega.Gomega) { + for i := 1; i < len(testEnv.ClCluster.Nodes); i++ { + nodeName := testEnv.ClCluster.Nodes[i].ContainerName + l.Info().Str("Node name", nodeName).Msg("Fetching filters from log poller's DB") + + hasFilters, err := nodeHasExpectedFilters(expectedFilters, coreLogger, testEnv.EVMClient.GetChainID(), testEnv.ClCluster.Nodes[i].PostgresDb) + if err != nil { + l.Warn().Err(err).Msg("Error checking if node has expected filters. Retrying...") + return + } + + g.Expect(hasFilters).To(gomega.BeTrue(), "Not all expected filters were found in the DB") + } + }, "1m", "1s").Should(gomega.Succeed()) + l.Info().Msg("All nodes have expected filters registered") + l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") + + // Save block number before starting to emit events, so that we can later use it when querying logs + sb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + startBlock := int64(sb) + + l.Info().Msg("STARTING EVENT EMISSION") + startTime := time.Now() + + // Start chaos experimnents by randomly pausing random containers (Chainlink nodes or their DBs) + chaosDoneCh := make(chan error, 1) + go func() { + executeChaosExperiment(l, testEnv, cfg, chaosDoneCh) + }() + + totalLogsEmitted, err := executeGenerator(t, cfg, logEmitters) + endTime := time.Now() + require.NoError(t, err, "Error executing event generator") + + expectedLogsEmitted := getExpectedLogCount(cfg) + duration := int(endTime.Sub(startTime).Seconds()) + l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") + + // Save block number after finishing to emit events, so that we can later use it when querying logs + eb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + + endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) + require.NoError(t, err, "Error getting end block to wait for") + + l.Info().Msg("Waiting before proceeding with test until all chaos experiments finish") + chaosError := <-chaosDoneCh + require.NoError(t, chaosError, "Error encountered during chaos experiment") + + // Wait until last block in which events were emitted has been finalised (with buffer) + waitDuration := "45m" + l.Warn().Str("Duration", waitDuration).Msg("Waiting for chain to advance beyond finality") + + gom.Eventually(func(g gomega.Gomega) { + hasAdvanced, err := chainHasFinalisedEndBlock(l, testEnv.EVMClient, endBlock) + if err != nil { + l.Warn().Err(err).Msg("Error checking if chain has advanced beyond finality. Retrying...") + } + g.Expect(hasAdvanced).To(gomega.BeTrue(), "Chain has not advanced beyond finality") + }, waitDuration, "30s").Should(gomega.Succeed()) + + l.Warn().Str("Duration", waitDuration).Msg("Waiting for all CL nodes to have end block finalised") + gom.Eventually(func(g gomega.Gomega) { + hasFinalised, err := logPollerHasFinalisedEndBlock(endBlock, testEnv.EVMClient.GetChainID(), l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if nodes have finalised end block. Retrying...") + } + g.Expect(hasFinalised).To(gomega.BeTrue(), "Some nodes have not finalised end block") + }, waitDuration, "30s").Should(gomega.Succeed()) + + // Wait until all CL nodes have exactly the same logs emitted by test contracts as the EVM node has + logConsistencyWaitDuration := "10m" + l.Warn().Str("Duration", logConsistencyWaitDuration).Msg("Waiting for CL nodes to have all the logs that EVM node has") + + gom.Eventually(func(g gomega.Gomega) { + missingLogs, err := getMissingLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, testEnv.ClCluster, l, coreLogger, cfg) + if err != nil { + l.Warn().Err(err).Msg("Error getting missing logs. Retrying...") + } + + if !missingLogs.IsEmpty() { + printMissingLogsByType(missingLogs, l, cfg) + } + g.Expect(missingLogs.IsEmpty()).To(gomega.BeTrue(), "Some CL nodes were missing logs") + }, logConsistencyWaitDuration, "20s").Should(gomega.Succeed()) + + evmLogs, _ := getEVMLogs(startBlock, endBlock, logEmitters, testEnv.EVMClient, l, cfg) + + if totalLogsEmitted != len(evmLogs) { + l.Warn().Int("Total logs emitted", totalLogsEmitted).Int("Total logs in EVM", len(evmLogs)).Msg("Test passed, but total logs emitted does not match total logs in EVM") + } +} From 305356206be809671fd497345d3851028ab60d9f Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 3 Nov 2023 12:27:54 +0000 Subject: [PATCH 068/271] [BCF-2750] Reorder telemetry params (#11147) --- core/services/functions/listener_test.go | 2 +- core/services/ocr/delegate.go | 4 ++-- core/services/ocr2/delegate.go | 22 +++++++++---------- .../ocr2/plugins/generic/telemetry_adapter.go | 2 +- .../plugins/generic/telemetry_adapter_test.go | 2 +- core/services/ocrcommon/telemetry_test.go | 8 +++---- core/services/telemetry/common.go | 2 +- core/services/telemetry/ingress.go | 14 ++++++------ core/services/telemetry/ingress_batch.go | 14 ++++++------ core/services/telemetry/ingress_batch_test.go | 2 +- core/services/telemetry/ingress_test.go | 2 +- core/services/telemetry/manager.go | 6 ++--- core/services/telemetry/manager_test.go | 18 +++++++-------- core/services/telemetry/noop.go | 2 +- 14 files changed, 50 insertions(+), 50 deletions(-) diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 007a2a91688..3b7ed46988d 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -125,7 +125,7 @@ func NewFunctionsListenerUniverse(t *testing.T, timeoutSec int, pruneFrequencySe ingressClient := sync_mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) - monEndpoint := ingressAgent.GenMonitoringEndpoint(contractAddress, synchronization.FunctionsRequests, "test-network", "test-chainID") + monEndpoint := ingressAgent.GenMonitoringEndpoint("test-network", "test-chainID", contractAddress, synchronization.FunctionsRequests) s4Storage := s4_mocks.NewStorage(t) client := chain.Client() diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index b761690485c..bbed43c151b 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -297,7 +297,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e enhancedTelemChan := make(chan ocrcommon.EnhancedTelemetryData, 100) if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) { - enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(concreteSpec.ContractAddress.String(), synchronization.EnhancedEA, "EVM", chain.ID().String()), lggr.Named("EnhancedTelemetry")) + enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint("EVM", chain.ID().String(), concreteSpec.ContractAddress.String(), synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry")) services = append(services, enhancedTelemService) } @@ -319,7 +319,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e Logger: ocrLogger, V1Bootstrappers: v1BootstrapPeers, V2Bootstrappers: v2Bootstrappers, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(concreteSpec.ContractAddress.String(), synchronization.OCR, "EVM", chain.ID().String()), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint("EVM", chain.ID().String(), concreteSpec.ContractAddress.String(), synchronization.OCR), ConfigOverrider: configOverrider, }) if err != nil { diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index e822fd5d8f2..39a8c84d6b9 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -564,10 +564,10 @@ func (d *Delegate) newServicesGenericPlugin( srvs = append(srvs, provider) oracleEndpoint := d.monitoringEndpointGen.GenMonitoringEndpoint( - spec.ContractID, - synchronization.TelemetryType(cconf.TelemetryType), rid.Network, rid.ChainID, + spec.ContractID, + synchronization.TelemetryType(cconf.TelemetryType), ) oracleArgs := libocr2.OCR2OracleArgs{ BinaryNetworkEndpointFactory: d.peerWrapper.Peer2, @@ -686,7 +686,7 @@ func (d *Delegate) newServicesMercury( Database: ocrDB, LocalConfig: lc, Logger: ocrLogger, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.FeedID.String(), synchronization.OCR3Mercury, rid.Network, rid.ChainID), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.FeedID.String(), synchronization.OCR3Mercury), OffchainConfigDigester: mercuryProvider.OffchainConfigDigester(), OffchainKeyring: kb, OnchainKeyring: kb, @@ -697,7 +697,7 @@ func (d *Delegate) newServicesMercury( mercuryServices, err2 := mercury.NewServices(jb, mercuryProvider, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, d.cfg.JobPipeline(), chEnhancedTelem, chain, d.mercuryORM, (mercuryutils.FeedID)(*spec.FeedID)) if ocrcommon.ShouldCollectEnhancedTelemetryMercury(jb) { - enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, chEnhancedTelem, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(spec.FeedID.String(), synchronization.EnhancedEAMercury, rid.Network, rid.ChainID), lggr.Named("EnhancedTelemetryMercury")) + enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, chEnhancedTelem, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.FeedID.String(), synchronization.EnhancedEAMercury), lggr.Named("EnhancedTelemetryMercury")) mercuryServices = append(mercuryServices, enhancedTelemService) } @@ -728,7 +728,7 @@ func (d *Delegate) newServicesMedian( Database: ocrDB, LocalConfig: lc, Logger: ocrLogger, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.OCR2Median, rid.Network, rid.ChainID), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR2Median), OffchainKeyring: kb, OnchainKeyring: kb, } @@ -744,7 +744,7 @@ func (d *Delegate) newServicesMedian( medianServices, err2 := median.NewMedianServices(ctx, jb, d.isNewlyCreatedJob, relayer, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, mConfig, enhancedTelemChan, errorLog) if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) { - enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.EnhancedEA, rid.Network, rid.ChainID), lggr.Named("EnhancedTelemetry")) + enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry")) medianServices = append(medianServices, enhancedTelemService) } @@ -965,7 +965,7 @@ func (d *Delegate) newServicesOCR2VRF( VRFContractTransmitter: vrfProvider.ContractTransmitter(), VRFDatabase: ocrDB, VRFLocalConfig: lc, - VRFMonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.OCR2VRF, rid.Network, rid.ChainID), + VRFMonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR2VRF), DKGContractConfigTracker: dkgProvider.ContractConfigTracker(), DKGOffchainConfigDigester: dkgProvider.OffchainConfigDigester(), DKGContract: dkgpkg.NewOnchainContract(dkgContract, &altbn_128.G2{}), @@ -1104,7 +1104,7 @@ func (d *Delegate) newServicesOCR2Keepers21( ContractConfigTracker: keeperProvider.ContractConfigTracker(), KeepersDatabase: ocrDB, Logger: ocrLogger, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.OCR2Automation, rid.Network, rid.ChainID), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR2Automation), OffchainConfigDigester: keeperProvider.OffchainConfigDigester(), OffchainKeyring: kb, OnchainKeyring: services.Keyring(), @@ -1249,7 +1249,7 @@ func (d *Delegate) newServicesOCR2Keepers20( KeepersDatabase: ocrDB, LocalConfig: lc, Logger: ocrLogger, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.OCR2Automation, rid.Network, rid.ChainID), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR2Automation), OffchainConfigDigester: keeperProvider.OffchainConfigDigester(), OffchainKeyring: kb, OnchainKeyring: kb, @@ -1358,7 +1358,7 @@ func (d *Delegate) newServicesOCR2Functions( Database: functionsOcrDB, LocalConfig: lc, Logger: ocrLogger, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.OCR2Functions, rid.Network, rid.ChainID), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR2Functions), OffchainConfigDigester: functionsProvider.OffchainConfigDigester(), OffchainKeyring: kb, OnchainKeyring: kb, @@ -1422,7 +1422,7 @@ func (d *Delegate) newServicesOCR2Functions( ContractID: spec.ContractID, Logger: lggr, MailMon: d.mailMon, - URLsMonEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(spec.ContractID, synchronization.FunctionsRequests, rid.Network, rid.ChainID), + URLsMonEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.FunctionsRequests), EthKeystore: d.ethKs, ThresholdKeyShare: thresholdKeyShare, LogPollerWrapper: functionsProvider.LogPollerWrapper(), diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter.go b/core/services/ocr2/plugins/generic/telemetry_adapter.go index e7f87dcd46c..51d94f5cfe7 100644 --- a/core/services/ocr2/plugins/generic/telemetry_adapter.go +++ b/core/services/ocr2/plugins/generic/telemetry_adapter.go @@ -52,7 +52,7 @@ func (t *TelemetryAdapter) getOrCreateEndpoint(network string, chainID string, c key := [4]string{network, chainID, contractID, telemetryType} e, ok := t.endpoints[key] if !ok { - e = t.endpointGenerator.GenMonitoringEndpoint(contractID, synchronization.TelemetryType(telemetryType), network, chainID) + e = t.endpointGenerator.GenMonitoringEndpoint(network, chainID, contractID, synchronization.TelemetryType(telemetryType)) t.endpoints[key] = e } return e, nil diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go index 9c42b0f85d5..e137343f2b4 100644 --- a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go +++ b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go @@ -24,7 +24,7 @@ func (m *mockEndpoint) SendLog(payload []byte) { m.payload = payload } type mockGenerator struct{} -func (m *mockGenerator) GenMonitoringEndpoint(contractID string, telemetryType synchronization.TelemetryType, network string, chainID string) commontypes.MonitoringEndpoint { +func (m *mockGenerator) GenMonitoringEndpoint(network string, chainID string, contractID string, telemetryType synchronization.TelemetryType) commontypes.MonitoringEndpoint { return &mockEndpoint{ network: network, chainID: chainID, diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index e6a798780b5..9e3dedce8a8 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -189,7 +189,7 @@ func TestSendEATelemetry(t *testing.T) { wg := sync.WaitGroup{} ingressClient := mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) - monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("0xa", synchronization.EnhancedEA, "test-network", "test-chainID") + monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("test-network", "test-chainID", "0xa", synchronization.EnhancedEA) var sentMessage []byte ingressClient.On("Send", mock.Anything, mock.AnythingOfType("[]uint8"), mock.AnythingOfType("string"), mock.AnythingOfType("TelemetryType")).Return().Run(func(args mock.Arguments) { @@ -305,7 +305,7 @@ func TestCollectAndSend(t *testing.T) { wg := sync.WaitGroup{} ingressClient := mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) - monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("0xa", synchronization.EnhancedEA, "test-network", "test-chainID") + monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("test-network", "test-chainID", "0xa", synchronization.EnhancedEA) ingressClient.On("Send", mock.Anything, mock.AnythingOfType("[]uint8"), mock.AnythingOfType("string"), mock.AnythingOfType("TelemetryType")).Return().Run(func(args mock.Arguments) { wg.Done() }) @@ -548,7 +548,7 @@ func TestCollectMercuryEnhancedTelemetryV1(t *testing.T) { wg := sync.WaitGroup{} ingressClient := mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) - monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("0xa", synchronization.EnhancedEAMercury, "test-network", "test-chainID") + monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("test-network", "test-chainID", "0xa", synchronization.EnhancedEAMercury) var sentMessage []byte ingressClient.On("Send", mock.Anything, mock.AnythingOfType("[]uint8"), mock.AnythingOfType("string"), mock.AnythingOfType("TelemetryType")).Return().Run(func(args mock.Arguments) { @@ -664,7 +664,7 @@ func TestCollectMercuryEnhancedTelemetryV2(t *testing.T) { wg := sync.WaitGroup{} ingressClient := mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(ingressClient) - monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("0xa", synchronization.EnhancedEAMercury, "test-network", "test-chainID") + monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("test-network", "test-chainID", "0xa", synchronization.EnhancedEAMercury) var sentMessage []byte ingressClient.On("Send", mock.Anything, mock.AnythingOfType("[]uint8"), mock.AnythingOfType("string"), mock.AnythingOfType("TelemetryType")).Return().Run(func(args mock.Arguments) { diff --git a/core/services/telemetry/common.go b/core/services/telemetry/common.go index 5a3f6706f7d..37a92f16c6d 100644 --- a/core/services/telemetry/common.go +++ b/core/services/telemetry/common.go @@ -7,5 +7,5 @@ import ( ) type MonitoringEndpointGenerator interface { - GenMonitoringEndpoint(contractID string, telemType synchronization.TelemetryType, network string, chainID string) ocrtypes.MonitoringEndpoint + GenMonitoringEndpoint(network string, chainID string, contractID string, telemType synchronization.TelemetryType) ocrtypes.MonitoringEndpoint } diff --git a/core/services/telemetry/ingress.go b/core/services/telemetry/ingress.go index 637fa0dd3ba..266155095bf 100644 --- a/core/services/telemetry/ingress.go +++ b/core/services/telemetry/ingress.go @@ -18,25 +18,25 @@ func NewIngressAgentWrapper(telemetryIngressClient synchronization.TelemetryServ return &IngressAgentWrapper{telemetryIngressClient} } -func (t *IngressAgentWrapper) GenMonitoringEndpoint(contractID string, telemType synchronization.TelemetryType, network string, chainID string) ocrtypes.MonitoringEndpoint { - return NewIngressAgent(t.telemetryIngressClient, contractID, telemType, network, chainID) +func (t *IngressAgentWrapper) GenMonitoringEndpoint(network, chainID string, contractID string, telemType synchronization.TelemetryType) ocrtypes.MonitoringEndpoint { + return NewIngressAgent(t.telemetryIngressClient, network, chainID, contractID, telemType) } type IngressAgent struct { telemetryIngressClient synchronization.TelemetryService - contractID string - telemType synchronization.TelemetryType network string chainID string + contractID string + telemType synchronization.TelemetryType } -func NewIngressAgent(telemetryIngressClient synchronization.TelemetryService, contractID string, telemType synchronization.TelemetryType, network string, chainID string) *IngressAgent { +func NewIngressAgent(telemetryIngressClient synchronization.TelemetryService, network string, chainID string, contractID string, telemType synchronization.TelemetryType) *IngressAgent { return &IngressAgent{ telemetryIngressClient, - contractID, - telemType, network, chainID, + contractID, + telemType, } } diff --git a/core/services/telemetry/ingress_batch.go b/core/services/telemetry/ingress_batch.go index df860853592..bb08c76d7e2 100644 --- a/core/services/telemetry/ingress_batch.go +++ b/core/services/telemetry/ingress_batch.go @@ -21,27 +21,27 @@ func NewIngressAgentBatchWrapper(telemetryIngressBatchClient synchronization.Tel } // GenMonitoringEndpoint returns a new ingress batch agent instantiated with the batch client and a contractID -func (t *IngressAgentBatchWrapper) GenMonitoringEndpoint(contractID string, telemType synchronization.TelemetryType, network string, chainID string) ocrtypes.MonitoringEndpoint { - return NewIngressAgentBatch(t.telemetryIngressBatchClient, contractID, telemType, network, chainID) +func (t *IngressAgentBatchWrapper) GenMonitoringEndpoint(network string, chainID string, contractID string, telemType synchronization.TelemetryType) ocrtypes.MonitoringEndpoint { + return NewIngressAgentBatch(t.telemetryIngressBatchClient, network, chainID, contractID, telemType) } // IngressAgentBatch allows for sending batch telemetry for a given contractID type IngressAgentBatch struct { telemetryIngressBatchClient synchronization.TelemetryService - contractID string - telemType synchronization.TelemetryType network string chainID string + contractID string + telemType synchronization.TelemetryType } // NewIngressAgentBatch creates a new IngressAgentBatch with the given batch client and contractID -func NewIngressAgentBatch(telemetryIngressBatchClient synchronization.TelemetryService, contractID string, telemType synchronization.TelemetryType, network string, chainID string) *IngressAgentBatch { +func NewIngressAgentBatch(telemetryIngressBatchClient synchronization.TelemetryService, network string, chainID string, contractID string, telemType synchronization.TelemetryType) *IngressAgentBatch { return &IngressAgentBatch{ telemetryIngressBatchClient, - contractID, - telemType, network, chainID, + contractID, + telemType, } } diff --git a/core/services/telemetry/ingress_batch_test.go b/core/services/telemetry/ingress_batch_test.go index 3923b569fed..91e6a07ad7f 100644 --- a/core/services/telemetry/ingress_batch_test.go +++ b/core/services/telemetry/ingress_batch_test.go @@ -14,7 +14,7 @@ import ( func TestIngressAgentBatch(t *testing.T) { telemetryBatchClient := mocks.NewTelemetryService(t) ingressAgentBatch := telemetry.NewIngressAgentWrapper(telemetryBatchClient) - monitoringEndpoint := ingressAgentBatch.GenMonitoringEndpoint("0xa", synchronization.OCR, "test-network", "test-chainID") + monitoringEndpoint := ingressAgentBatch.GenMonitoringEndpoint("test-network", "test-chainID", "0xa", synchronization.OCR) // Handle the Send call and store the telem var telemPayload synchronization.TelemPayload diff --git a/core/services/telemetry/ingress_test.go b/core/services/telemetry/ingress_test.go index 31028f2f605..7e83384dc6c 100644 --- a/core/services/telemetry/ingress_test.go +++ b/core/services/telemetry/ingress_test.go @@ -14,7 +14,7 @@ import ( func TestIngressAgent(t *testing.T) { telemetryClient := mocks.NewTelemetryService(t) ingressAgent := telemetry.NewIngressAgentWrapper(telemetryClient) - monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("0xa", synchronization.OCR, "test-network", "test-chainID") + monitoringEndpoint := ingressAgent.GenMonitoringEndpoint("test-network", "test-chainID", "0xa", synchronization.OCR) // Handle the Send call and store the telem var telemPayload synchronization.TelemPayload diff --git a/core/services/telemetry/manager.go b/core/services/telemetry/manager.go index 2931ec71a13..cc14a956c12 100644 --- a/core/services/telemetry/manager.go +++ b/core/services/telemetry/manager.go @@ -150,7 +150,7 @@ func (m *Manager) HealthReport() map[string]error { } // GenMonitoringEndpoint creates a new monitoring endpoints based on the existing available endpoints defined in the core config TOML, if no endpoint for the network and chainID exists, a NOOP agent will be used and the telemetry will not be sent -func (m *Manager) GenMonitoringEndpoint(contractID string, telemType synchronization.TelemetryType, network string, chainID string) commontypes.MonitoringEndpoint { +func (m *Manager) GenMonitoringEndpoint(network string, chainID string, contractID string, telemType synchronization.TelemetryType) commontypes.MonitoringEndpoint { e, found := m.getEndpoint(network, chainID) @@ -160,10 +160,10 @@ func (m *Manager) GenMonitoringEndpoint(contractID string, telemType synchroniza } if m.useBatchSend { - return NewIngressAgentBatch(e.client, contractID, telemType, network, chainID) + return NewIngressAgentBatch(e.client, network, chainID, contractID, telemType) } - return NewIngressAgent(e.client, contractID, telemType, network, chainID) + return NewIngressAgent(e.client, network, chainID, contractID, telemType) } diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 69746625ddd..2d51d9f4491 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -56,14 +56,14 @@ func TestManagerAgents(t *testing.T) { tm := NewManager(tic, ks, lggr) require.Equal(t, "*synchronization.telemetryIngressBatchClient", reflect.TypeOf(tm.endpoints[0].client).String()) - me := tm.GenMonitoringEndpoint("", "", "network-1", "network-1-chainID-1") + me := tm.GenMonitoringEndpoint("network-1", "network-1-chainID-1", "", "") require.Equal(t, "*telemetry.IngressAgentBatch", reflect.TypeOf(me).String()) tic = setupMockConfig(t, false) tic.On("Endpoints").Return([]config.TelemetryIngressEndpoint{te}) tm = NewManager(tic, ks, lggr) require.Equal(t, "*synchronization.telemetryIngressClient", reflect.TypeOf(tm.endpoints[0].client).String()) - me = tm.GenMonitoringEndpoint("", "", "network-1", "network-1-chainID-1") + me = tm.GenMonitoringEndpoint("network-1", "network-1-chainID-1", "", "") require.Equal(t, "*telemetry.IngressAgent", reflect.TypeOf(me).String()) } @@ -254,17 +254,17 @@ func TestCorrectEndpointRouting(t *testing.T) { } //Unknown networks or chainID - noopEndpoint := tm.GenMonitoringEndpoint("some-contractID", "some-type", "unknown-network", "unknown-chainID") + noopEndpoint := tm.GenMonitoringEndpoint("unknown-network", "unknown-chainID", "some-contractID", "some-type") require.Equal(t, "*telemetry.NoopAgent", reflect.TypeOf(noopEndpoint).String()) require.Equal(t, 1, obsLogs.Len()) require.Contains(t, obsLogs.TakeAll()[0].Message, "no telemetry endpoint found") - noopEndpoint = tm.GenMonitoringEndpoint("some-contractID", "some-type", "network-1", "unknown-chainID") + noopEndpoint = tm.GenMonitoringEndpoint("network-1", "unknown-chainID", "some-contractID", "some-type") require.Equal(t, "*telemetry.NoopAgent", reflect.TypeOf(noopEndpoint).String()) require.Equal(t, 1, obsLogs.Len()) require.Contains(t, obsLogs.TakeAll()[0].Message, "no telemetry endpoint found") - noopEndpoint = tm.GenMonitoringEndpoint("some-contractID", "some-type", "network-2", "network-1-chainID-1") + noopEndpoint = tm.GenMonitoringEndpoint("network-2", "network-1-chainID-1", "some-contractID", "some-type") require.Equal(t, "*telemetry.NoopAgent", reflect.TypeOf(noopEndpoint).String()) require.Equal(t, 1, obsLogs.Len()) require.Contains(t, obsLogs.TakeAll()[0].Message, "no telemetry endpoint found") @@ -274,10 +274,10 @@ func TestCorrectEndpointRouting(t *testing.T) { telemType := fmt.Sprintf("TelemType_%s", e.chainID) contractID := fmt.Sprintf("contractID_%s", e.chainID) me := tm.GenMonitoringEndpoint( - contractID, - synchronization.TelemetryType(telemType), e.network, e.chainID, + contractID, + synchronization.TelemetryType(telemType), ) me.SendLog([]byte(e.chainID)) require.Equal(t, 0, obsLogs.Len()) @@ -316,7 +316,7 @@ func TestLegacyMode(t *testing.T) { }) tm.endpoints[0].client = clientMock - e := tm.GenMonitoringEndpoint("some-contractID", "some-type", "unknown-network", "unknown-chainID") + e := tm.GenMonitoringEndpoint("unknown-network", "unknown-chainID", "some-contractID", "some-type") require.Equal(t, "*telemetry.IngressAgentBatch", reflect.TypeOf(e).String()) e.SendLog([]byte("endpoint-1-message-1")) @@ -324,7 +324,7 @@ func TestLegacyMode(t *testing.T) { e.SendLog([]byte("endpoint-1-message-3")) require.Len(t, clientSent, 3) - e2 := tm.GenMonitoringEndpoint("another-contractID", "another-type", "another-unknown-network", "another-unknown-chainID") + e2 := tm.GenMonitoringEndpoint("another-unknown-network", "another-unknown-chainID", "another-contractID", "another-type") require.Equal(t, "*telemetry.IngressAgentBatch", reflect.TypeOf(e).String()) e2.SendLog([]byte("endpoint-2-message-1")) diff --git a/core/services/telemetry/noop.go b/core/services/telemetry/noop.go index cbeb0387089..4da8868c8f0 100644 --- a/core/services/telemetry/noop.go +++ b/core/services/telemetry/noop.go @@ -16,6 +16,6 @@ func (t *NoopAgent) SendLog(log []byte) { } // GenMonitoringEndpoint creates a monitoring endpoint for telemetry -func (t *NoopAgent) GenMonitoringEndpoint(contractID string, telemType synchronization.TelemetryType, network string, chainID string) ocrtypes.MonitoringEndpoint { +func (t *NoopAgent) GenMonitoringEndpoint(network string, chainID string, contractID string, telemType synchronization.TelemetryType) ocrtypes.MonitoringEndpoint { return t } From 04ba364831eee6541c976cf8ea60c8eac09a0e5a Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 3 Nov 2023 09:07:11 -0400 Subject: [PATCH 069/271] Toggle Simulated Test Runs (#11157) * Toggle Simulated Test Runs * Debug inputs * Figured it out * Skip notify --- .github/workflows/integration-tests.yml | 15 ++++++++++----- integration-tests/soak/ocr_test.go | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 785c48da40a..0456c5f9d42 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -10,12 +10,16 @@ on: - "*" workflow_dispatch: inputs: + simulatedNetwork: + description: "Run Simulated Network Tests" + required: false + type: boolean + default: true liveNetwork: - description: "Run Live Testnet Tests" + description: "Run Live Network Tests" required: false type: boolean - # Only run 1 of this workflow at a time per PR concurrency: group: integration-tests-chainlink-${{ github.ref }} @@ -58,6 +62,7 @@ jobs: hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} this-job-name: Check Paths That Require Tests To Run continue-on-error: true + outputs: src: ${{ steps.changes.outputs.src }} build-chainlink: @@ -176,7 +181,7 @@ jobs: echo "MATRIX_JSON=${COMBINED_ARRAY}" >> $GITHUB_ENV eth-smoke-tests-matrix-automation: - if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} + if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || (github.event_name == 'workflow_dispatch' && !inputs.simulatedNetwork)) }} environment: integration permissions: checks: write @@ -246,7 +251,7 @@ jobs: continue-on-error: true eth-smoke-tests-matrix: - if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} + if: ${{ !(contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') || (github.event_name == 'workflow_dispatch' && !inputs.simulatedNetwork)) }} environment: integration permissions: checks: write @@ -971,7 +976,7 @@ jobs: testnet-smoke-tests-notify: name: Live Testnet Start Slack Thread - if: ${{ always() && needs.testnet-smoke-tests-matrix.result != 'skipped' && needs.testnet-smoke-tests-matrix.result != 'cancelled' }} + if: ${{ always() && needs.testnet-smoke-tests-matrix.result != 'skipped' && needs.testnet-smoke-tests-matrix.result != 'cancelled' && github.event_name != 'workflow_dispatch' }} environment: integration outputs: thread_ts: ${{ steps.slack.outputs.thread_ts }} diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index b2375f13ac2..9973c23808e 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -16,6 +16,7 @@ func TestOCRSoak(t *testing.T) { // Use this variable to pass in any custom EVM specific TOML values to your Chainlink nodes customNetworkTOML := `` // Uncomment below for debugging TOML issues on the node + // network := networks.MustGetSelectedNetworksFromEnv()[0] // fmt.Println("Using Chainlink TOML\n---------------------") // fmt.Println(client.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) // fmt.Println("---------------------") From 2b8ba05471a4c0f8fa1d30b7f47579f86a95efcf Mon Sep 17 00:00:00 2001 From: Chunkai Yang Date: Fri, 3 Nov 2023 09:35:15 -0400 Subject: [PATCH 070/271] Make solc output folder per contract (#11126) * update generation output path to be folder per contract * lint bash script * mass update go generate wrapper path * regen wrappers and db * fix not-updated wrappers * fix more wrappers --- contracts/scripts/native_solc_compile_all_6 | 11 +- contracts/scripts/native_solc_compile_all_7 | 11 +- .../native_solc_compile_all_automation | 13 +- .../native_solc_compile_all_events_mock | 11 +- .../scripts/native_solc_compile_all_feeds | 11 +- .../scripts/native_solc_compile_all_llo-feeds | 11 +- .../scripts/native_solc_compile_all_logpoller | 11 +- .../scripts/native_solc_compile_all_ocr2vrf | 13 +- .../native_solc_compile_all_operatorforwarder | 13 +- .../scripts/native_solc_compile_all_shared | 11 +- .../native_solc_compile_all_transmission | 11 +- contracts/scripts/native_solc_compile_all_vrf | 25 +- ...rapper-dependency-versions-do-not-edit.txt | 226 +++++++++--------- core/gethwrappers/go_generate.go | 218 ++++++++--------- ...rapper-dependency-versions-do-not-edit.txt | 12 +- core/gethwrappers/llo-feeds/go_generate.go | 12 +- ...rapper-dependency-versions-do-not-edit.txt | 8 +- core/gethwrappers/shared/go_generate.go | 8 +- ...rapper-dependency-versions-do-not-edit.txt | 12 +- core/gethwrappers/transmission/go_generate.go | 12 +- 20 files changed, 350 insertions(+), 310 deletions(-) diff --git a/contracts/scripts/native_solc_compile_all_6 b/contracts/scripts/native_solc_compile_all_6 index 7f8f4fa6957..f7bd60d6781 100755 --- a/contracts/scripts/native_solc_compile_all_6 +++ b/contracts/scripts/native_solc_compile_all_6 @@ -12,7 +12,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION @@ -20,10 +20,13 @@ export SOLC_VERSION=$SOLC_VERSION compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v0.6 \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.6 \ - $ROOT/contracts/src/v0.6/$1 + -o "$ROOT"/contracts/solc/v0.6/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.6 \ + "$ROOT"/contracts/src/v0.6/"$1" } compileContract Flags.sol diff --git a/contracts/scripts/native_solc_compile_all_7 b/contracts/scripts/native_solc_compile_all_7 index b2d76b3cb5f..fd64d9ffce7 100755 --- a/contracts/scripts/native_solc_compile_all_7 +++ b/contracts/scripts/native_solc_compile_all_7 @@ -12,7 +12,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION @@ -20,10 +20,13 @@ export SOLC_VERSION=$SOLC_VERSION compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v0.7 \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.7 \ - $ROOT/contracts/src/v0.7/$1 + -o "$ROOT"/contracts/solc/v0.7/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.7 \ + "$ROOT"/contracts/src/v0.7/"$1" } compileContract tests/MultiWordConsumer.sol diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index 414453c8482..beb557de126 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -12,7 +12,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION @@ -20,10 +20,13 @@ export SOLC_VERSION=$SOLC_VERSION compileContract () { - solc @openzeppelin/=$ROOT/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8,$ROOT/contracts/node_modules\ - $ROOT/contracts/src/v0.8/$1 + local contract + contract=$(basename "$1" ".sol") + + solc @openzeppelin/="$ROOT"/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8,"$ROOT"/contracts/node_modules\ + "$ROOT"/contracts/src/v0.8/"$1" } compileContract automation/upkeeps/CronUpkeepFactory.sol diff --git a/contracts/scripts/native_solc_compile_all_events_mock b/contracts/scripts/native_solc_compile_all_events_mock index 993530e2fa1..68e8bdfa6a9 100755 --- a/contracts/scripts/native_solc_compile_all_events_mock +++ b/contracts/scripts/native_solc_compile_all_events_mock @@ -12,17 +12,20 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8\ - $ROOT/contracts/src/v0.8/$1 + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\ + "$ROOT"/contracts/src/v0.8/"$1" } # This script is used to compile the contracts for the Events Mocks used in the tests. diff --git a/contracts/scripts/native_solc_compile_all_feeds b/contracts/scripts/native_solc_compile_all_feeds index 2bbd9fe869c..eac5a6b70cf 100755 --- a/contracts/scripts/native_solc_compile_all_feeds +++ b/contracts/scripts/native_solc_compile_all_feeds @@ -12,7 +12,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION @@ -20,10 +20,13 @@ export SOLC_VERSION=$SOLC_VERSION compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8\ - $ROOT/contracts/src/v0.8/$1 + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\ + "$ROOT"/contracts/src/v0.8/"$1" } # Aggregators diff --git a/contracts/scripts/native_solc_compile_all_llo-feeds b/contracts/scripts/native_solc_compile_all_llo-feeds index 27ef714ec1c..2caa6fb98de 100755 --- a/contracts/scripts/native_solc_compile_all_llo-feeds +++ b/contracts/scripts/native_solc_compile_all_llo-feeds @@ -11,7 +11,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION @@ -19,10 +19,13 @@ export SOLC_VERSION=$SOLC_VERSION ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8\ - $ROOT/contracts/src/v0.8/$1 + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\ + "$ROOT"/contracts/src/v0.8/"$1" } compileContract llo-feeds/Verifier.sol diff --git a/contracts/scripts/native_solc_compile_all_logpoller b/contracts/scripts/native_solc_compile_all_logpoller index 91a0606dba8..b6ac51ecedb 100755 --- a/contracts/scripts/native_solc_compile_all_logpoller +++ b/contracts/scripts/native_solc_compile_all_logpoller @@ -11,7 +11,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION @@ -19,10 +19,13 @@ export SOLC_VERSION=$SOLC_VERSION ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8\ - $ROOT/contracts/src/v0.8/$1 + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\ + "$ROOT"/contracts/src/v0.8/"$1" } diff --git a/contracts/scripts/native_solc_compile_all_ocr2vrf b/contracts/scripts/native_solc_compile_all_ocr2vrf index 42478d7ebc2..755edd34f56 100755 --- a/contracts/scripts/native_solc_compile_all_ocr2vrf +++ b/contracts/scripts/native_solc_compile_all_ocr2vrf @@ -16,7 +16,7 @@ echo "Compiling OCR2VRF contracts..." SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION @@ -24,11 +24,14 @@ export SOLC_VERSION=$SOLC_VERSION compileContract () { - solc --overwrite --optimize --optimize-runs $2 --metadata-hash none \ - -o $ROOT/contracts/solc/v0.8.19 \ + local contract + contract=$(basename "$1" ".sol") + + solc --overwrite --optimize --optimize-runs "$2" --metadata-hash none \ + -o "$ROOT"/contracts/solc/v0.8.19/"$contract" \ --abi --bin \ - --allow-paths $ROOT/../$FOLDER/contracts \ - $ROOT/$1 + --allow-paths "$ROOT"/../$FOLDER/contracts \ + "$ROOT"/"$1" } # OCR2VRF diff --git a/contracts/scripts/native_solc_compile_all_operatorforwarder b/contracts/scripts/native_solc_compile_all_operatorforwarder index 3bc5cb9249f..2d455994813 100755 --- a/contracts/scripts/native_solc_compile_all_operatorforwarder +++ b/contracts/scripts/native_solc_compile_all_operatorforwarder @@ -11,17 +11,20 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION compileContract () { - solc @openzeppelin/=$ROOT/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8,$ROOT/contracts/node_modules\ - $ROOT/contracts/src/v0.8/$1 + local contract + contract=$(basename "$1" ".sol") + + solc @openzeppelin/="$ROOT"/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8,"$ROOT"/contracts/node_modules\ + "$ROOT"/contracts/src/v0.8/"$1" } # Contracts diff --git a/contracts/scripts/native_solc_compile_all_shared b/contracts/scripts/native_solc_compile_all_shared index db421f45e04..9178237b8a5 100755 --- a/contracts/scripts/native_solc_compile_all_shared +++ b/contracts/scripts/native_solc_compile_all_shared @@ -11,7 +11,7 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION @@ -19,10 +19,13 @@ export SOLC_VERSION=$SOLC_VERSION ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8\ - $ROOT/contracts/src/v0.8/$1 + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\ + "$ROOT"/contracts/src/v0.8/"$1" } compileContract shared/token/ERC677/BurnMintERC677.sol diff --git a/contracts/scripts/native_solc_compile_all_transmission b/contracts/scripts/native_solc_compile_all_transmission index e08f38e2bac..281fa7aea73 100755 --- a/contracts/scripts/native_solc_compile_all_transmission +++ b/contracts/scripts/native_solc_compile_all_transmission @@ -11,17 +11,20 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION compileContract () { + local contract + contract=$(basename "$1" ".sol") + solc --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8\ - $ROOT/contracts/src/v0.8/$1 + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8\ + "$ROOT"/contracts/src/v0.8/"$1" } # Contracts diff --git a/contracts/scripts/native_solc_compile_all_vrf b/contracts/scripts/native_solc_compile_all_vrf index 80adba8e6f7..4eed35cf5bc 100755 --- a/contracts/scripts/native_solc_compile_all_vrf +++ b/contracts/scripts/native_solc_compile_all_vrf @@ -11,24 +11,30 @@ OPTIMIZE_RUNS=1000000 SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; cd ../../ && pwd -P )" -python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt +python3 -m pip install --require-hashes -r "$SCRIPTPATH"/requirements.txt solc-select install $SOLC_VERSION solc-select use $SOLC_VERSION export SOLC_VERSION=$SOLC_VERSION compileContract () { - solc @openzeppelin/=$ROOT/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8,$ROOT/contracts/node_modules\ - $ROOT/contracts/src/v0.8/$1 + local contract + contract=$(basename "$1" ".sol") + + solc @openzeppelin/="$ROOT"/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $OPTIMIZE_RUNS --metadata-hash none \ + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8,"$ROOT"/contracts/node_modules\ + "$ROOT"/contracts/src/v0.8/"$1" } compileContractAltOpts () { - solc @openzeppelin/=$ROOT/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs $2 --metadata-hash none \ - -o $ROOT/contracts/solc/v$SOLC_VERSION \ - --abi --bin --allow-paths $ROOT/contracts/src/v0.8,$ROOT/contracts/node_modules\ - $ROOT/contracts/src/v0.8/$1 + local contract + contract=$(basename "$1" ".sol") + + solc @openzeppelin/="$ROOT"/contracts/node_modules/@openzeppelin/ --overwrite --optimize --optimize-runs "$2" --metadata-hash none \ + -o "$ROOT"/contracts/solc/v$SOLC_VERSION/"$contract" \ + --abi --bin --allow-paths "$ROOT"/contracts/src/v0.8,"$ROOT"/contracts/node_modules\ + "$ROOT"/contracts/src/v0.8/"$1" } # VRF @@ -73,6 +79,7 @@ compileContract vrf/dev/testhelpers/VRFConsumerV2PlusUpgradeableExample.sol compileContract vrf/dev/testhelpers/VRFV2PlusMaliciousMigrator.sol compileContract vrf/dev/libraries/VRFV2PlusClient.sol compileContract vrf/dev/testhelpers/VRFCoordinatorV2Plus_V2Example.sol +compileContract vrf/dev/BlockhashStore.sol compileContract vrf/dev/TrustedBlockhashStore.sol compileContract vrf/dev/testhelpers/VRFV2PlusLoadTestWithMetrics.sol compileContractAltOpts vrf/dev/testhelpers/VRFCoordinatorV2PlusUpgradedVersion.sol 5 diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 0d0bb388f2f..a14b461fa7a 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,116 +1,116 @@ GETH_VERSION: 1.12.0 -KeeperConsumer: ../../contracts/solc/v0.8.16/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer.bin 53d7902867ce421641ffa9de63204b89ab9dc157b93f0beb9ac08c6450365a70 -KeeperConsumerPerformance: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 -PerformDataChecker: ../../contracts/solc/v0.8.16/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 -UpkeepCounter: ../../contracts/solc/v0.8.16/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter.bin 77f000229a501f638dd2dc439859257f632894c728b31e68aea4f6d6c52f1b71 -UpkeepPerformCounterRestrictive: ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive.bin 20955b21acceb58355fa287b29194a73edf5937067ba7140667301017cb2b24c -VRFv2Consumer: ../../contracts/solc/v0.8.6/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer.bin 12368b3b5e06392440143a13b94c0ea2f79c4c897becc3b060982559e10ace40 -aggregator_v2v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface.bin 95e8814b408bb05bf21742ef580d98698b7db6a9bac6a35c3de12b23aec4ee28 -aggregator_v3_interface: ../../contracts/solc/v0.8.6/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV3Interface.bin 351b55d3b0f04af67db6dfb5c92f1c64479400ca1fec77afc20bc0ce65cb49ab -authorized_forwarder: ../../contracts/solc/v0.8.19/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder.bin 8ea76c883d460f8353a45a493f2aebeb5a2d9a7b4619d1bc4fff5fb590bb3e10 -authorized_receiver: ../../contracts/solc/v0.8.19/AuthorizedReceiver.abi ../../contracts/solc/v0.8.19/AuthorizedReceiver.bin 18e8969ba3234b027e1b16c11a783aca58d0ea5c2361010ec597f134b7bf1c4f -automation_consumer_benchmark: ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark.abi ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark.bin f52c76f1aaed4be541d82d97189d70f5aa027fc9838037dd7a7d21910c8c488e -automation_forwarder_logic: ../../contracts/solc/v0.8.16/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic.bin 15ae0c367297955fdab4b552dbb10e1f2be80a8fde0efec4a4d398693e9d72b5 -automation_registrar_wrapper2_1: ../../contracts/solc/v0.8.16/AutomationRegistrar2_1.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_1.bin eb06d853aab39d3196c593b03e555851cbe8386e0fe54a74c2479f62d14b3c42 -automation_utils_2_1: ../../contracts/solc/v0.8.16/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1.bin 331bfa79685aee6ddf63b64c0747abee556c454cae3fb8175edff425b615d8aa -batch_blockhash_store: ../../contracts/solc/v0.8.6/BatchBlockhashStore.abi ../../contracts/solc/v0.8.6/BatchBlockhashStore.bin 14356c48ef70f66ef74f22f644450dbf3b2a147c1b68deaa7e7d1eb8ffab15db -batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2.bin d0a54963260d8c1f1bbd984b758285e6027cfb5a7e42701bcb562ab123219332 -batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus.bin 7bb76ae241cf1b37b41920830b836cb99f1ad33efd7435ca2398ff6cd2fe5d48 -blockhash_store: ../../contracts/solc/v0.8.6/BlockhashStore.abi ../../contracts/solc/v0.8.6/BlockhashStore.bin 12b0662f1636a341c8863bdec7a20f2ddd97c3a4fd1a7ae353fe316609face4e -chain_specific_util_helper: ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper.bin 5f10664e31abc768f4a37901cae7a3bef90146180f97303e5a1bde5a08d84595 -consumer_wrapper: ../../contracts/solc/v0.7/Consumer.abi ../../contracts/solc/v0.7/Consumer.bin 894d1cbd920dccbd36d92918c1037c6ded34f66f417ccb18ec3f33c64ef83ec5 -cron_upkeep_factory_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory.abi - dacb0f8cdf54ae9d2781c5e720fc314b32ed5e58eddccff512c75d6067292cd7 -cron_upkeep_wrapper: ../../contracts/solc/v0.8.6/CronUpkeep.abi - 362fcfcf30a6ab3acff83095ea4b2b9056dd5e9dcb94bc5411aae58995d22709 -dummy_protocol_wrapper: ../../contracts/solc/v0.8.16/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol.bin 583a448170b13abf7ed64e406e8177d78c9e55ab44efd141eee60de23a71ee3b -flags_wrapper: ../../contracts/solc/v0.6/Flags.abi ../../contracts/solc/v0.6/Flags.bin 2034d1b562ca37a63068851915e3703980276e8d5f7db6db8a3351a49d69fc4a -flux_aggregator_wrapper: ../../contracts/solc/v0.6/FluxAggregator.abi ../../contracts/solc/v0.6/FluxAggregator.bin a3b0a6396c4aa3b5ee39b3c4bd45efc89789d4859379a8a92caca3a0496c5794 -functions_billing_registry_events_mock: ../../contracts/solc/v0.8.6/FunctionsBillingRegistryEventsMock.abi ../../contracts/solc/v0.8.6/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 -functions_oracle_events_mock: ../../contracts/solc/v0.8.6/FunctionsOracleEventsMock.abi ../../contracts/solc/v0.8.6/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c -gas_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin 4a5dcdac486d18fcd58e3488c15c1710ae76b977556a3f3191bd269a4bc75723 -gas_wrapper_mock: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin a9b08f18da59125c6fc305855710241f3d35161b8b9f3e3f635a7b1d5c6da9c8 -i_keeper_registry_master_wrapper_2_1: ../../contracts/solc/v0.8.16/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster.bin 6501bb9bcf5048bab2737b00685c6984a24867e234ddf5b60a65904eee9a4ebc -i_log_automation: ../../contracts/solc/v0.8.16/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation.bin 296beccb6af655d6fc3a6e676b244831cce2da6688d3afc4f21f8738ae59e03e -keeper_consumer_performance_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 -keeper_consumer_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer.bin 53d7902867ce421641ffa9de63204b89ab9dc157b93f0beb9ac08c6450365a70 -keeper_registrar_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistrar.abi ../../contracts/solc/v0.8.6/KeeperRegistrar.bin e49b2f8b23da17af1ed2209b8ae0968cc04350554d636711e6c24a3ad3118692 -keeper_registrar_wrapper1_2_mock: ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock.bin 5b155a7cb3def309fd7525de1d7cd364ebf8491bdc3060eac08ea0ff55ab29bc -keeper_registrar_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistrar2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistrar2_0.bin 647f125c2f0dafabcdc545cb77b15dc2ec3ea9429357806813179b1fd555c2d2 -keeper_registry_logic1_3: ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3.bin 903f8b9c8e25425ca6d0b81b89e339d695a83630bfbfa24a6f3b38869676bc5a -keeper_registry_logic2_0: ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0.bin d69d2bc8e4844293dbc2d45abcddc50b84c88554ecccfa4fa77c0ca45ec80871 -keeper_registry_logic_a_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1.bin 77481ab75c9aa86a62a7b2a708599b5ea1a6346ed1c0def6d4826e7ae523f1ee -keeper_registry_logic_b_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1.bin 467d10741a04601b136553a2b1c6ab37f2a65d809366faf03180a22ff26be215 -keeper_registry_wrapper1_1: ../../contracts/solc/v0.7/KeeperRegistry1_1.abi ../../contracts/solc/v0.7/KeeperRegistry1_1.bin 6ce079f2738f015f7374673a2816e8e9787143d00b780ea7652c8aa9ad9e1e20 -keeper_registry_wrapper1_1_mock: ../../contracts/solc/v0.7/KeeperRegistry1_1Mock.abi ../../contracts/solc/v0.7/KeeperRegistry1_1Mock.bin 98ddb3680e86359de3b5d17e648253ba29a84703f087a1b52237824003a8c6df -keeper_registry_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2.bin a40ff877dd7c280f984cbbb2b428e160662b0c295e881d5f778f941c0088ca22 -keeper_registry_wrapper1_3: ../../contracts/solc/v0.8.6/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3.bin d4dc760b767ae274ee25c4a604ea371e1fa603a7b6421b69efb2088ad9e8abb3 -keeper_registry_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0.bin c32dea7d5ef66b7c58ddc84ddf69aa44df1b3ae8601fbc271c95be4ff5853056 -keeper_registry_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1.bin 604e4a0cd980c713929b523b999462a3aa0ed06f96ff563a4c8566cf59c8445b -keepers_vrf_consumer: ../../contracts/solc/v0.8.6/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer.bin fa75572e689c9e84705c63e8dbe1b7b8aa1a8fe82d66356c4873d024bb9166e8 -log_emitter: ../../contracts/solc/v0.8.19/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter.bin 244ba13730c036de0b02beef4e3d9c9a96946ce353c27f366baecc7f5be5a6fd -log_triggered_streams_lookup_wrapper: ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup.bin f8da43a927c1a66238a9f4fd5d5dd7e280e361daa0444da1f7f79498ace901e1 -log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter.bin 42426bbb83f96dfbe55fc576d6c65020eaeed690e2289cf99b0c4aa810a5f4ec -mock_aggregator_proxy: ../../contracts/solc/v0.8.6/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy.bin b16c108f3dd384c342ddff5e94da7c0a8d39d1be5e3d8f2cf61ecc7f0e50ff42 -mock_ethlink_aggregator_wrapper: ../../contracts/solc/v0.6/MockETHLINKAggregator.abi ../../contracts/solc/v0.6/MockETHLINKAggregator.bin 1c52c24f797b8482aa12b8251dcea1c072827bd5b3426b822621261944b99ca0 -mock_gas_aggregator_wrapper: ../../contracts/solc/v0.6/MockGASAggregator.abi ../../contracts/solc/v0.6/MockGASAggregator.bin bacbb1ea4dc6beac0db8a13ca5c75e2fd61b903d70feea9b3b1c8b10fe8df4f3 -multiwordconsumer_wrapper: ../../contracts/solc/v0.7/MultiWordConsumer.abi ../../contracts/solc/v0.7/MultiWordConsumer.bin 6e68abdf614e3ed0f5066c1b5f9d7c1199f1e7c5c5251fe8a471344a59afc6ba +KeeperConsumer: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 53d7902867ce421641ffa9de63204b89ab9dc157b93f0beb9ac08c6450365a70 +KeeperConsumerPerformance: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 +PerformDataChecker: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 +UpkeepCounter: ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.bin 77f000229a501f638dd2dc439859257f632894c728b31e68aea4f6d6c52f1b71 +UpkeepPerformCounterRestrictive: ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.bin 20955b21acceb58355fa287b29194a73edf5937067ba7140667301017cb2b24c +VRFv2Consumer: ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.bin 12368b3b5e06392440143a13b94c0ea2f79c4c897becc3b060982559e10ace40 +aggregator_v2v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.bin 95e8814b408bb05bf21742ef580d98698b7db6a9bac6a35c3de12b23aec4ee28 +aggregator_v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.bin 351b55d3b0f04af67db6dfb5c92f1c64479400ca1fec77afc20bc0ce65cb49ab +authorized_forwarder: ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.bin 8ea76c883d460f8353a45a493f2aebeb5a2d9a7b4619d1bc4fff5fb590bb3e10 +authorized_receiver: ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedReceiver.abi ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedReceiver.bin 18e8969ba3234b027e1b16c11a783aca58d0ea5c2361010ec597f134b7bf1c4f +automation_consumer_benchmark: ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.abi ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.bin f52c76f1aaed4be541d82d97189d70f5aa027fc9838037dd7a7d21910c8c488e +automation_forwarder_logic: ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.bin 15ae0c367297955fdab4b552dbb10e1f2be80a8fde0efec4a4d398693e9d72b5 +automation_registrar_wrapper2_1: ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.bin eb06d853aab39d3196c593b03e555851cbe8386e0fe54a74c2479f62d14b3c42 +automation_utils_2_1: ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin 331bfa79685aee6ddf63b64c0747abee556c454cae3fb8175edff425b615d8aa +batch_blockhash_store: ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.abi ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.bin 14356c48ef70f66ef74f22f644450dbf3b2a147c1b68deaa7e7d1eb8ffab15db +batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.bin d0a54963260d8c1f1bbd984b758285e6027cfb5a7e42701bcb562ab123219332 +batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin 7bb76ae241cf1b37b41920830b836cb99f1ad33efd7435ca2398ff6cd2fe5d48 +blockhash_store: ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.abi ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.bin 12b0662f1636a341c8863bdec7a20f2ddd97c3a4fd1a7ae353fe316609face4e +chain_specific_util_helper: ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.bin 5f10664e31abc768f4a37901cae7a3bef90146180f97303e5a1bde5a08d84595 +consumer_wrapper: ../../contracts/solc/v0.7/Consumer/Consumer.abi ../../contracts/solc/v0.7/Consumer/Consumer.bin 894d1cbd920dccbd36d92918c1037c6ded34f66f417ccb18ec3f33c64ef83ec5 +cron_upkeep_factory_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeepFactory.abi - dacb0f8cdf54ae9d2781c5e720fc314b32ed5e58eddccff512c75d6067292cd7 +cron_upkeep_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeep.abi - 362fcfcf30a6ab3acff83095ea4b2b9056dd5e9dcb94bc5411aae58995d22709 +dummy_protocol_wrapper: ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.bin 583a448170b13abf7ed64e406e8177d78c9e55ab44efd141eee60de23a71ee3b +flags_wrapper: ../../contracts/solc/v0.6/Flags/Flags.abi ../../contracts/solc/v0.6/Flags/Flags.bin 2034d1b562ca37a63068851915e3703980276e8d5f7db6db8a3351a49d69fc4a +flux_aggregator_wrapper: ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator.abi ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator.bin a3b0a6396c4aa3b5ee39b3c4bd45efc89789d4859379a8a92caca3a0496c5794 +functions_billing_registry_events_mock: ../../contracts/solc/v0.8.6/FunctionsBillingRegistryEventsMock/FunctionsBillingRegistryEventsMock.abi ../../contracts/solc/v0.8.6/FunctionsBillingRegistryEventsMock/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 +functions_oracle_events_mock: ../../contracts/solc/v0.8.6/FunctionsOracleEventsMock/FunctionsOracleEventsMock.abi ../../contracts/solc/v0.8.6/FunctionsOracleEventsMock/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c +gas_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin 4a5dcdac486d18fcd58e3488c15c1710ae76b977556a3f3191bd269a4bc75723 +gas_wrapper_mock: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin a9b08f18da59125c6fc305855710241f3d35161b8b9f3e3f635a7b1d5c6da9c8 +i_keeper_registry_master_wrapper_2_1: ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin 6501bb9bcf5048bab2737b00685c6984a24867e234ddf5b60a65904eee9a4ebc +i_log_automation: ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin 296beccb6af655d6fc3a6e676b244831cce2da6688d3afc4f21f8738ae59e03e +keeper_consumer_performance_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 +keeper_consumer_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 53d7902867ce421641ffa9de63204b89ab9dc157b93f0beb9ac08c6450365a70 +keeper_registrar_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.bin e49b2f8b23da17af1ed2209b8ae0968cc04350554d636711e6c24a3ad3118692 +keeper_registrar_wrapper1_2_mock: ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.bin 5b155a7cb3def309fd7525de1d7cd364ebf8491bdc3060eac08ea0ff55ab29bc +keeper_registrar_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/KeeperRegistrar2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/KeeperRegistrar2_0.bin 647f125c2f0dafabcdc545cb77b15dc2ec3ea9429357806813179b1fd555c2d2 +keeper_registry_logic1_3: ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.bin 903f8b9c8e25425ca6d0b81b89e339d695a83630bfbfa24a6f3b38869676bc5a +keeper_registry_logic2_0: ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.bin d69d2bc8e4844293dbc2d45abcddc50b84c88554ecccfa4fa77c0ca45ec80871 +keeper_registry_logic_a_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.bin 77481ab75c9aa86a62a7b2a708599b5ea1a6346ed1c0def6d4826e7ae523f1ee +keeper_registry_logic_b_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.bin 467d10741a04601b136553a2b1c6ab37f2a65d809366faf03180a22ff26be215 +keeper_registry_wrapper1_1: ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.abi ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.bin 6ce079f2738f015f7374673a2816e8e9787143d00b780ea7652c8aa9ad9e1e20 +keeper_registry_wrapper1_1_mock: ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.abi ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.bin 98ddb3680e86359de3b5d17e648253ba29a84703f087a1b52237824003a8c6df +keeper_registry_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.bin a40ff877dd7c280f984cbbb2b428e160662b0c295e881d5f778f941c0088ca22 +keeper_registry_wrapper1_3: ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.bin d4dc760b767ae274ee25c4a604ea371e1fa603a7b6421b69efb2088ad9e8abb3 +keeper_registry_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.bin c32dea7d5ef66b7c58ddc84ddf69aa44df1b3ae8601fbc271c95be4ff5853056 +keeper_registry_wrapper_2_1: ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.bin 604e4a0cd980c713929b523b999462a3aa0ed06f96ff563a4c8566cf59c8445b +keepers_vrf_consumer: ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin fa75572e689c9e84705c63e8dbe1b7b8aa1a8fe82d66356c4873d024bb9166e8 +log_emitter: ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin 244ba13730c036de0b02beef4e3d9c9a96946ce353c27f366baecc7f5be5a6fd +log_triggered_streams_lookup_wrapper: ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin f8da43a927c1a66238a9f4fd5d5dd7e280e361daa0444da1f7f79498ace901e1 +log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin 42426bbb83f96dfbe55fc576d6c65020eaeed690e2289cf99b0c4aa810a5f4ec +mock_aggregator_proxy: ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.bin b16c108f3dd384c342ddff5e94da7c0a8d39d1be5e3d8f2cf61ecc7f0e50ff42 +mock_ethlink_aggregator_wrapper: ../../contracts/solc/v0.6/MockETHLINKAggregator/MockETHLINKAggregator.abi ../../contracts/solc/v0.6/MockETHLINKAggregator/MockETHLINKAggregator.bin 1c52c24f797b8482aa12b8251dcea1c072827bd5b3426b822621261944b99ca0 +mock_gas_aggregator_wrapper: ../../contracts/solc/v0.6/MockGASAggregator/MockGASAggregator.abi ../../contracts/solc/v0.6/MockGASAggregator/MockGASAggregator.bin bacbb1ea4dc6beac0db8a13ca5c75e2fd61b903d70feea9b3b1c8b10fe8df4f3 +multiwordconsumer_wrapper: ../../contracts/solc/v0.7/MultiWordConsumer/MultiWordConsumer.abi ../../contracts/solc/v0.7/MultiWordConsumer/MultiWordConsumer.bin 6e68abdf614e3ed0f5066c1b5f9d7c1199f1e7c5c5251fe8a471344a59afc6ba offchain_aggregator_wrapper: OffchainAggregator/OffchainAggregator.abi - 5c8d6562e94166d4790f1ee6e4321d359d9f7262e6c5452a712b1f1c896f45cf -operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory.abi ../../contracts/solc/v0.8.19/OperatorFactory.bin 0fdfacf8879537b854875608dfca41c6221c342174417112acaa67dfcadafddc -operator_wrapper: ../../contracts/solc/v0.8.19/Operator.abi ../../contracts/solc/v0.8.19/Operator.bin d7abd0e67f30a3a4c9c04c896124391306fa364fcf579fa6df04dbf912b48568 -oracle_wrapper: ../../contracts/solc/v0.6/Oracle.abi ../../contracts/solc/v0.6/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a -perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 -solidity_vrf_consumer_interface: ../../contracts/solc/v0.6/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer.bin ecc99378aa798014de9db42b2eb81320778b0663dbe208008dad75ccdc1d4366 -solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f -solidity_vrf_coordinator_interface: ../../contracts/solc/v0.6/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator.bin a23d3c395156804788c7f6fbda2994e8f7184304c0f0c9f2c4ddeaf073d346d2 -solidity_vrf_request_id: ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper.bin 383b59e861732c1911ddb7b002c6158608496ce889979296527215fd0366b318 -solidity_vrf_request_id_v08: ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper.bin f2559015d6f3e5d285c57b011be9b2300632e93dd6c4524e58202d6200f09edc -solidity_vrf_v08_verifier_wrapper: ../../contracts/solc/v0.8.6/VRFTestHelper.abi ../../contracts/solc/v0.8.6/VRFTestHelper.bin f37f8b21a81c113085c6137835a2246db6ebda07da455c4f2b5c7ec60c725c3b -solidity_vrf_verifier_wrapper: ../../contracts/solc/v0.6/VRFTestHelper.abi ../../contracts/solc/v0.6/VRFTestHelper.bin 44c2b67d8d2990ab580453deb29d63508c6147a3dc49908a1db563bef06e6474 -solidity_vrf_wrapper: ../../contracts/solc/v0.6/VRF.abi ../../contracts/solc/v0.6/VRF.bin 04ede5b83c06ba5b76ef99c081c72928007d8a7aaefcf21449a46a07cbd4bfc2 -streams_lookup_compatible_interface: ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface.abi ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface.bin feb92cc666df21ea04ab9d7a588a513847b01b2f66fc167d06ab28ef2b17e015 -streams_lookup_upkeep_wrapper: ../../contracts/solc/v0.8.16/StreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/StreamsLookupUpkeep.bin b1a598963cacac51ed4706538d0f142bdc0d94b9a4b13e2d402131cdf05c9bcf -test_api_consumer_wrapper: ../../contracts/solc/v0.6/TestAPIConsumer.abi ../../contracts/solc/v0.6/TestAPIConsumer.bin ed10893cb18894c18e275302329c955f14ea2de37ee044f84aa1e067ac5ea71e -trusted_blockhash_store: ../../contracts/solc/v0.8.6/TrustedBlockhashStore.abi ../../contracts/solc/v0.8.6/TrustedBlockhashStore.bin 98cb0dc06c15af5dcd3b53bdfc98e7ed2489edc96a42203294ac2fc0efdda02b -type_and_version_interface_wrapper: ../../contracts/solc/v0.8.6/TypeAndVersionInterface.abi ../../contracts/solc/v0.8.6/TypeAndVersionInterface.bin bc9c3a6e73e3ebd5b58754df0deeb3b33f4bb404d5709bb904aed51d32f4b45e -upkeep_counter_wrapper: ../../contracts/solc/v0.8.16/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter.bin 77f000229a501f638dd2dc439859257f632894c728b31e68aea4f6d6c52f1b71 -upkeep_perform_counter_restrictive_wrapper: ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive.bin 20955b21acceb58355fa287b29194a73edf5937067ba7140667301017cb2b24c -upkeep_transcoder: ../../contracts/solc/v0.8.6/UpkeepTranscoder.abi ../../contracts/solc/v0.8.6/UpkeepTranscoder.bin 336c92a981597be26508455f81a908a0784a817b129a59686c5b2c4afcba730a -verifiable_load_log_trigger_upkeep_wrapper: ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep.bin fb674ba44c0e8f3b385cd10b2f7dea5cd07b5f38df08066747e8b1542e152557 -verifiable_load_streams_lookup_upkeep_wrapper: ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep.bin 785f68c44bfff070505eaa65e38a1af94046e5f9afc1189bcf2c8cfcd1102d66 -verifiable_load_upkeep_wrapper: ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep.bin a3e02c43756ea91e7ce4b81e48c11648f1d12f6663c236780147e41dfa36ebee -vrf_consumer_v2: ../../contracts/solc/v0.8.6/VRFConsumerV2.abi ../../contracts/solc/v0.8.6/VRFConsumerV2.bin 9ef258bf8e9f8d880fd229ceb145593d91e24fc89366baa0bf19169c5787d15f -vrf_consumer_v2_plus_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample.bin 3155c611e4d6882e9324b6e975033b31356776ea8b031ca63d63da37589d583b -vrf_consumer_v2_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample.bin f1790a9a2f2a04c730593e483459709cb89e897f8a19d7a3ac0cfe6a97265e6e -vrf_coordinator_mock: ../../contracts/solc/v0.8.6/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock.bin 5c495cf8df1f46d8736b9150cdf174cce358cb8352f60f0d5bb9581e23920501 -vrf_coordinator_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2.bin 295f35ce282060317dfd01f45959f5a2b05ba26913e422fbd4fb6bf90b107006 -vrf_coordinator_v2_5: ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5.bin b0e7c42a30b36d9d31fa9a3f26bad7937152e3dddee5bd8dd3d121390c879ab6 -vrf_coordinator_v2_plus_v2_example: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example.bin 4a5b86701983b1b65f0a8dfa116b3f6d75f8f706fa274004b57bdf5992e4cec3 +operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.abi ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.bin 0fdfacf8879537b854875608dfca41c6221c342174417112acaa67dfcadafddc +operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin d7abd0e67f30a3a4c9c04c896124391306fa364fcf579fa6df04dbf912b48568 +oracle_wrapper: ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a +perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 +solidity_vrf_consumer_interface: ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.bin ecc99378aa798014de9db42b2eb81320778b0663dbe208008dad75ccdc1d4366 +solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f +solidity_vrf_coordinator_interface: ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.bin a23d3c395156804788c7f6fbda2994e8f7184304c0f0c9f2c4ddeaf073d346d2 +solidity_vrf_request_id: ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.bin 383b59e861732c1911ddb7b002c6158608496ce889979296527215fd0366b318 +solidity_vrf_request_id_v08: ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.bin f2559015d6f3e5d285c57b011be9b2300632e93dd6c4524e58202d6200f09edc +solidity_vrf_v08_verifier_wrapper: ../../contracts/solc/v0.8.6/VRFTestHelper/VRFTestHelper.abi ../../contracts/solc/v0.8.6/VRFTestHelper/VRFTestHelper.bin f37f8b21a81c113085c6137835a2246db6ebda07da455c4f2b5c7ec60c725c3b +solidity_vrf_verifier_wrapper: ../../contracts/solc/v0.6/VRFTestHelper/VRFTestHelper.abi ../../contracts/solc/v0.6/VRFTestHelper/VRFTestHelper.bin 44c2b67d8d2990ab580453deb29d63508c6147a3dc49908a1db563bef06e6474 +solidity_vrf_wrapper: ../../contracts/solc/v0.6/VRF/VRF.abi ../../contracts/solc/v0.6/VRF/VRF.bin 04ede5b83c06ba5b76ef99c081c72928007d8a7aaefcf21449a46a07cbd4bfc2 +streams_lookup_compatible_interface: ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface/StreamsLookupCompatibleInterface.abi ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface/StreamsLookupCompatibleInterface.bin feb92cc666df21ea04ab9d7a588a513847b01b2f66fc167d06ab28ef2b17e015 +streams_lookup_upkeep_wrapper: ../../contracts/solc/v0.8.16/StreamsLookupUpkeep/StreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/StreamsLookupUpkeep/StreamsLookupUpkeep.bin b1a598963cacac51ed4706538d0f142bdc0d94b9a4b13e2d402131cdf05c9bcf +test_api_consumer_wrapper: ../../contracts/solc/v0.6/TestAPIConsumer/TestAPIConsumer.abi ../../contracts/solc/v0.6/TestAPIConsumer/TestAPIConsumer.bin ed10893cb18894c18e275302329c955f14ea2de37ee044f84aa1e067ac5ea71e +trusted_blockhash_store: ../../contracts/solc/v0.8.6/TrustedBlockhashStore/TrustedBlockhashStore.abi ../../contracts/solc/v0.8.6/TrustedBlockhashStore/TrustedBlockhashStore.bin 98cb0dc06c15af5dcd3b53bdfc98e7ed2489edc96a42203294ac2fc0efdda02b +type_and_version_interface_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.bin bc9c3a6e73e3ebd5b58754df0deeb3b33f4bb404d5709bb904aed51d32f4b45e +upkeep_counter_wrapper: ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.bin 77f000229a501f638dd2dc439859257f632894c728b31e68aea4f6d6c52f1b71 +upkeep_perform_counter_restrictive_wrapper: ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.bin 20955b21acceb58355fa287b29194a73edf5937067ba7140667301017cb2b24c +upkeep_transcoder: ../../contracts/solc/v0.8.6/UpkeepTranscoder/UpkeepTranscoder.abi ../../contracts/solc/v0.8.6/UpkeepTranscoder/UpkeepTranscoder.bin 336c92a981597be26508455f81a908a0784a817b129a59686c5b2c4afcba730a +verifiable_load_log_trigger_upkeep_wrapper: ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep/VerifiableLoadLogTriggerUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep/VerifiableLoadLogTriggerUpkeep.bin fb674ba44c0e8f3b385cd10b2f7dea5cd07b5f38df08066747e8b1542e152557 +verifiable_load_streams_lookup_upkeep_wrapper: ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep/VerifiableLoadStreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep/VerifiableLoadStreamsLookupUpkeep.bin 785f68c44bfff070505eaa65e38a1af94046e5f9afc1189bcf2c8cfcd1102d66 +verifiable_load_upkeep_wrapper: ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep/VerifiableLoadUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep/VerifiableLoadUpkeep.bin a3e02c43756ea91e7ce4b81e48c11648f1d12f6663c236780147e41dfa36ebee +vrf_consumer_v2: ../../contracts/solc/v0.8.6/VRFConsumerV2/VRFConsumerV2.abi ../../contracts/solc/v0.8.6/VRFConsumerV2/VRFConsumerV2.bin 9ef258bf8e9f8d880fd229ceb145593d91e24fc89366baa0bf19169c5787d15f +vrf_consumer_v2_plus_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample/VRFConsumerV2PlusUpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample/VRFConsumerV2PlusUpgradeableExample.bin 3155c611e4d6882e9324b6e975033b31356776ea8b031ca63d63da37589d583b +vrf_consumer_v2_upgradeable_example: ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.bin f1790a9a2f2a04c730593e483459709cb89e897f8a19d7a3ac0cfe6a97265e6e +vrf_coordinator_mock: ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.bin 5c495cf8df1f46d8736b9150cdf174cce358cb8352f60f0d5bb9581e23920501 +vrf_coordinator_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.bin 295f35ce282060317dfd01f45959f5a2b05ba26913e422fbd4fb6bf90b107006 +vrf_coordinator_v2_5: ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin b0e7c42a30b36d9d31fa9a3f26bad7937152e3dddee5bd8dd3d121390c879ab6 +vrf_coordinator_v2_plus_v2_example: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.bin 4a5b86701983b1b65f0a8dfa116b3f6d75f8f706fa274004b57bdf5992e4cec3 vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus.bin e4409bbe361258273458a5c99408b3d7f0cc57a2560dee91c0596cc6d6f738be -vrf_coordinator_v2plus_interface: ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal.abi ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal.bin 834a2ce0e83276372a0e1446593fd89798f4cf6dc95d4be0113e99fadf61558b -vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample.bin 14f888eb313930b50233a6f01ea31eba0206b7f41a41f6311670da8bb8a26963 -vrf_load_test_external_sub_owner: ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner.bin 2097faa70265e420036cc8a3efb1f1e0836ad2d7323b295b9a26a125dbbe6c7d -vrf_load_test_ownerless_consumer: ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer.bin 74f914843cbc70b9c3079c3e1c709382ce415225e8bb40113e7ac018bfcb0f5c -vrf_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics.bin 8ab9de5816fbdf93a2865e2711b85a39a6fc9c413a4b336578c485be1158d430 -vrf_malicious_consumer_v2: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2.bin 9755fa8ffc7f5f0b337d5d413d77b0c9f6cd6f68c31727d49acdf9d4a51bc522 -vrf_malicious_consumer_v2_plus: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus.bin e2a72638e11da807b6533d037e7e5aaeed695efd5035777b8e20d2f8973a574c -vrf_owner: ../../contracts/solc/v0.8.6/VRFOwner.abi ../../contracts/solc/v0.8.6/VRFOwner.bin eccfae5ee295b5850e22f61240c469f79752b8d9a3bac5d64aec7ac8def2f6cb -vrf_owner_test_consumer: ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer.bin 0537bbe96c5a8bbd44d0a65fbb7e51f6a9f9e75f4673225845ac1ba33f4e7974 -vrf_ownerless_consumer_example: ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample.abi ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample.bin 9893b3805863273917fb282eed32274e32aa3d5c2a67a911510133e1218132be -vrf_single_consumer_example: ../../contracts/solc/v0.8.6/VRFSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFSingleConsumerExample.bin 892a5ed35da2e933f7fd7835cd6f7f70ef3aa63a9c03a22c5b1fd026711b0ece -vrf_v2_consumer_wrapper: ../../contracts/solc/v0.8.6/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer.bin 12368b3b5e06392440143a13b94c0ea2f79c4c897becc3b060982559e10ace40 -vrf_v2plus_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics.bin 0a89cb7ed9dfb42f91e559b03dc351ccdbe14d281a7ab71c63bd3f47eeed7711 -vrf_v2plus_single_consumer: ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample.bin 6226d05afa1664033b182bfbdde11d5dfb1d4c8e3eb0bd0448c8bfb76f5b96e4 -vrf_v2plus_sub_owner: ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample.bin 7541f986571b8a5671a256edc27ae9b8df9bcdff45ac3b96e5609bbfcc320e4e -vrf_v2plus_upgraded_version: ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion.bin c0793d86fb6e45342c4424184fe241c16da960c0b4de76816364b933344d0756 -vrfv2_proxy_admin: ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin.abi ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin.bin 402b1103087ffe1aa598854a8f8b38f8cd3de2e3aaa86369e28017a9157f4980 -vrfv2_reverting_example: ../../contracts/solc/v0.8.6/VRFV2RevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2RevertingExample.bin 1ae46f80351d428bd85ba58b9041b2a608a1845300d79a8fed83edf96606de87 -vrfv2_transparent_upgradeable_proxy: ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy.abi ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy.bin fe1a8e6852fbd06d91f64315c5cede86d340891f5b5cc981fb5b86563f7eac3f -vrfv2_wrapper: ../../contracts/solc/v0.8.6/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper.bin d5e9a982325d2d4f517c4f2bc818795f61555408ef4b38fb59b923d144970e38 -vrfv2_wrapper_consumer_example: ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample.bin 3c5c9f1c501e697a7e77e959b48767e2a0bb1372393fd7686f7aaef3eb794231 -vrfv2_wrapper_interface: ../../contracts/solc/v0.8.6/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface.bin ff8560169de171a68b360b7438d13863682d07040d984fd0fb096b2379421003 -vrfv2plus_client: ../../contracts/solc/v0.8.6/VRFV2PlusClient.abi ../../contracts/solc/v0.8.6/VRFV2PlusClient.bin 3ffbfa4971a7e5f46051a26b1722613f265d89ea1867547ecec58500953a9501 -vrfv2plus_consumer_example: ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample.bin 2c480a6d7955d33a00690fdd943486d95802e48a03f3cc243df314448e4ddb2c -vrfv2plus_malicious_migrator: ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator.abi ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator.bin 80dbc98be5e42246960c889d29488f978d3db0127e95e9b295352c481d8c9b07 -vrfv2plus_reverting_example: ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample.bin 6c9053a94f90b8151964d3311310478b57744fbbd153e8ee742ed570e1e49798 -vrfv2plus_wrapper: ../../contracts/solc/v0.8.6/VRFV2PlusWrapper.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapper.bin 934bafba386b934f491827e535306726069f4cafef9125079ea88abf0d808877 -vrfv2plus_wrapper_consumer_example: ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample.bin a14c4c6e2299cd963a8f0ed069e61dd135af5aad4c13a94f6ea7e086eced7191 -vrfv2plus_wrapper_load_test_consumer: ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer.bin 55e3bd534045125fb6579a201ab766185e9b0fac5737b4f37897bb69c9f599fa +vrf_coordinator_v2plus_interface: ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.abi ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.bin 834a2ce0e83276372a0e1446593fd89798f4cf6dc95d4be0113e99fadf61558b +vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.bin 14f888eb313930b50233a6f01ea31eba0206b7f41a41f6311670da8bb8a26963 +vrf_load_test_external_sub_owner: ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.bin 2097faa70265e420036cc8a3efb1f1e0836ad2d7323b295b9a26a125dbbe6c7d +vrf_load_test_ownerless_consumer: ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.bin 74f914843cbc70b9c3079c3e1c709382ce415225e8bb40113e7ac018bfcb0f5c +vrf_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.bin 8ab9de5816fbdf93a2865e2711b85a39a6fc9c413a4b336578c485be1158d430 +vrf_malicious_consumer_v2: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.bin 9755fa8ffc7f5f0b337d5d413d77b0c9f6cd6f68c31727d49acdf9d4a51bc522 +vrf_malicious_consumer_v2_plus: ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.bin e2a72638e11da807b6533d037e7e5aaeed695efd5035777b8e20d2f8973a574c +vrf_owner: ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.abi ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.bin eccfae5ee295b5850e22f61240c469f79752b8d9a3bac5d64aec7ac8def2f6cb +vrf_owner_test_consumer: ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.bin 0537bbe96c5a8bbd44d0a65fbb7e51f6a9f9e75f4673225845ac1ba33f4e7974 +vrf_ownerless_consumer_example: ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample/VRFOwnerlessConsumerExample.abi ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample/VRFOwnerlessConsumerExample.bin 9893b3805863273917fb282eed32274e32aa3d5c2a67a911510133e1218132be +vrf_single_consumer_example: ../../contracts/solc/v0.8.6/VRFSingleConsumerExample/VRFSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFSingleConsumerExample/VRFSingleConsumerExample.bin 892a5ed35da2e933f7fd7835cd6f7f70ef3aa63a9c03a22c5b1fd026711b0ece +vrf_v2_consumer_wrapper: ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.bin 12368b3b5e06392440143a13b94c0ea2f79c4c897becc3b060982559e10ace40 +vrf_v2plus_load_test_with_metrics: ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics/VRFV2PlusLoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics/VRFV2PlusLoadTestWithMetrics.bin 0a89cb7ed9dfb42f91e559b03dc351ccdbe14d281a7ab71c63bd3f47eeed7711 +vrf_v2plus_single_consumer: ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample/VRFV2PlusSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample/VRFV2PlusSingleConsumerExample.bin 6226d05afa1664033b182bfbdde11d5dfb1d4c8e3eb0bd0448c8bfb76f5b96e4 +vrf_v2plus_sub_owner: ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample/VRFV2PlusExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample/VRFV2PlusExternalSubOwnerExample.bin 7541f986571b8a5671a256edc27ae9b8df9bcdff45ac3b96e5609bbfcc320e4e +vrf_v2plus_upgraded_version: ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.bin c0793d86fb6e45342c4424184fe241c16da960c0b4de76816364b933344d0756 +vrfv2_proxy_admin: ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.abi ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.bin 402b1103087ffe1aa598854a8f8b38f8cd3de2e3aaa86369e28017a9157f4980 +vrfv2_reverting_example: ../../contracts/solc/v0.8.6/VRFV2RevertingExample/VRFV2RevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2RevertingExample/VRFV2RevertingExample.bin 1ae46f80351d428bd85ba58b9041b2a608a1845300d79a8fed83edf96606de87 +vrfv2_transparent_upgradeable_proxy: ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.abi ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.bin fe1a8e6852fbd06d91f64315c5cede86d340891f5b5cc981fb5b86563f7eac3f +vrfv2_wrapper: ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin d5e9a982325d2d4f517c4f2bc818795f61555408ef4b38fb59b923d144970e38 +vrfv2_wrapper_consumer_example: ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.bin 3c5c9f1c501e697a7e77e959b48767e2a0bb1372393fd7686f7aaef3eb794231 +vrfv2_wrapper_interface: ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.bin ff8560169de171a68b360b7438d13863682d07040d984fd0fb096b2379421003 +vrfv2plus_client: ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.abi ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.bin 3ffbfa4971a7e5f46051a26b1722613f265d89ea1867547ecec58500953a9501 +vrfv2plus_consumer_example: ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.bin 2c480a6d7955d33a00690fdd943486d95802e48a03f3cc243df314448e4ddb2c +vrfv2plus_malicious_migrator: ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.abi ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.bin 80dbc98be5e42246960c889d29488f978d3db0127e95e9b295352c481d8c9b07 +vrfv2plus_reverting_example: ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample/VRFV2PlusRevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample/VRFV2PlusRevertingExample.bin 6c9053a94f90b8151964d3311310478b57744fbbd153e8ee742ed570e1e49798 +vrfv2plus_wrapper: ../../contracts/solc/v0.8.6/VRFV2PlusWrapper/VRFV2PlusWrapper.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapper/VRFV2PlusWrapper.bin 934bafba386b934f491827e535306726069f4cafef9125079ea88abf0d808877 +vrfv2plus_wrapper_consumer_example: ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample/VRFV2PlusWrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample/VRFV2PlusWrapperConsumerExample.bin a14c4c6e2299cd963a8f0ed069e61dd135af5aad4c13a94f6ea7e086eced7191 +vrfv2plus_wrapper_load_test_consumer: ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer/VRFV2PlusWrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer/VRFV2PlusWrapperLoadTestConsumer.bin 55e3bd534045125fb6579a201ab766185e9b0fac5737b4f37897bb69c9f599fa diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 67090d16c6d..3965c159080 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -5,139 +5,139 @@ package gethwrappers // Make sure solidity compiler artifacts are up-to-date. Only output stdout on failure. //go:generate ./generation/compile_contracts.sh -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/FluxAggregator.abi ../../contracts/solc/v0.6/FluxAggregator.bin FluxAggregator flux_aggregator_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRF.abi ../../contracts/solc/v0.6/VRF.bin VRF solidity_vrf_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFTestHelper.abi ../../contracts/solc/v0.6/VRFTestHelper.bin VRFTestHelper solidity_vrf_verifier_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator.bin VRFCoordinator solidity_vrf_coordinator_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer.bin VRFConsumer solidity_vrf_consumer_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper.bin VRFRequestIDBaseTestHelper solidity_vrf_request_id -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/Flags.abi ../../contracts/solc/v0.6/Flags.bin Flags flags_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/Oracle.abi ../../contracts/solc/v0.6/Oracle.bin Oracle oracle_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/TestAPIConsumer.abi ../../contracts/solc/v0.6/TestAPIConsumer.bin TestAPIConsumer test_api_consumer_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/MockETHLINKAggregator.abi ../../contracts/solc/v0.6/MockETHLINKAggregator.bin MockETHLINKAggregator mock_ethlink_aggregator_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/MockGASAggregator.abi ../../contracts/solc/v0.6/MockGASAggregator.bin MockGASAggregator mock_gas_aggregator_wrapper - -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/Consumer.abi ../../contracts/solc/v0.7/Consumer.bin Consumer consumer_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/MultiWordConsumer.abi ../../contracts/solc/v0.7/MultiWordConsumer.bin MultiWordConsumer multiwordconsumer_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/Operator.abi ../../contracts/solc/v0.8.19/Operator.bin Operator operator_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/OperatorFactory.abi ../../contracts/solc/v0.8.19/OperatorFactory.bin OperatorFactory operator_factory -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder.bin AuthorizedForwarder authorized_forwarder -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AuthorizedReceiver.abi ../../contracts/solc/v0.8.19/AuthorizedReceiver.bin AuthorizedReceiver authorized_receiver +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator.abi ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator.bin FluxAggregator flux_aggregator_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRF/VRF.abi ../../contracts/solc/v0.6/VRF/VRF.bin VRF solidity_vrf_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFTestHelper/VRFTestHelper.abi ../../contracts/solc/v0.6/VRFTestHelper/VRFTestHelper.bin VRFTestHelper solidity_vrf_verifier_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.bin VRFCoordinator solidity_vrf_coordinator_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.bin VRFConsumer solidity_vrf_consumer_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.bin VRFRequestIDBaseTestHelper solidity_vrf_request_id +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/Flags/Flags.abi ../../contracts/solc/v0.6/Flags/Flags.bin Flags flags_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin Oracle oracle_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/TestAPIConsumer/TestAPIConsumer.abi ../../contracts/solc/v0.6/TestAPIConsumer/TestAPIConsumer.bin TestAPIConsumer test_api_consumer_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/MockETHLINKAggregator/MockETHLINKAggregator.abi ../../contracts/solc/v0.6/MockETHLINKAggregator/MockETHLINKAggregator.bin MockETHLINKAggregator mock_ethlink_aggregator_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.6/MockGASAggregator/MockGASAggregator.abi ../../contracts/solc/v0.6/MockGASAggregator/MockGASAggregator.bin MockGASAggregator mock_gas_aggregator_wrapper + +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/Consumer/Consumer.abi ../../contracts/solc/v0.7/Consumer/Consumer.bin Consumer consumer_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/MultiWordConsumer/MultiWordConsumer.abi ../../contracts/solc/v0.7/MultiWordConsumer/MultiWordConsumer.bin MultiWordConsumer multiwordconsumer_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin Operator operator_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.abi ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.bin OperatorFactory operator_factory +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.bin AuthorizedForwarder authorized_forwarder +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedReceiver.abi ../../contracts/solc/v0.8.19/AuthorizedReceiver/AuthorizedReceiver.bin AuthorizedReceiver authorized_receiver //go:generate go run ./generation/generate/wrap.go OffchainAggregator/OffchainAggregator.abi - OffchainAggregator offchain_aggregator_wrapper // Automation -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/KeeperRegistry1_1.abi ../../contracts/solc/v0.7/KeeperRegistry1_1.bin KeeperRegistry keeper_registry_wrapper1_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/KeeperRegistry1_1Mock.abi ../../contracts/solc/v0.7/KeeperRegistry1_1Mock.bin KeeperRegistryMock keeper_registry_wrapper1_1_mock -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.7/UpkeepPerformCounterRestrictive.bin UpkeepPerformCounterRestrictive upkeep_perform_counter_restrictive_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/UpkeepCounter.abi ../../contracts/solc/v0.7/UpkeepCounter.bin UpkeepCounter upkeep_counter_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/CronUpkeepFactory.abi - CronUpkeepFactory cron_upkeep_factory_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/CronUpkeep.abi - CronUpkeep cron_upkeep_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar.abi ../../contracts/solc/v0.8.6/KeeperRegistrar.bin KeeperRegistrar keeper_registrar_wrapper1_2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock.bin KeeperRegistrarMock keeper_registrar_wrapper1_2_mock -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2.bin KeeperRegistry keeper_registry_wrapper1_2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/TypeAndVersionInterface.abi ../../contracts/solc/v0.8.6/TypeAndVersionInterface.bin TypeAndVersionInterface type_and_version_interface_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin KeeperRegistryCheckUpkeepGasUsageWrapper gas_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin KeeperRegistryCheckUpkeepGasUsageWrapperMock gas_wrapper_mock -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3.bin KeeperRegistry keeper_registry_wrapper1_3 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3.bin KeeperRegistryLogic keeper_registry_logic1_3 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistrar2_0.bin KeeperRegistrar keeper_registrar_wrapper2_0 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0.bin KeeperRegistry keeper_registry_wrapper2_0 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0.bin KeeperRegistryLogic keeper_registry_logic2_0 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/UpkeepTranscoder.abi ../../contracts/solc/v0.8.6/UpkeepTranscoder.bin UpkeepTranscoder upkeep_transcoder -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep.bin VerifiableLoadUpkeep verifiable_load_upkeep_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep.bin VerifiableLoadStreamsLookupUpkeep verifiable_load_streams_lookup_upkeep_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep.bin VerifiableLoadLogTriggerUpkeep verifiable_load_log_trigger_upkeep_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/StreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/StreamsLookupUpkeep.bin StreamsLookupUpkeep streams_lookup_upkeep_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface.abi ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface.bin StreamsLookupCompatibleInterface streams_lookup_compatible_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark.abi ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark.bin AutomationConsumerBenchmark automation_consumer_benchmark -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationRegistrar2_1.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_1.bin AutomationRegistrar automation_registrar_wrapper2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1.bin KeeperRegistry keeper_registry_wrapper_2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1.bin KeeperRegistryLogicA keeper_registry_logic_a_wrapper_2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1.bin KeeperRegistryLogicB keeper_registry_logic_b_wrapper_2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster.bin IKeeperRegistryMaster i_keeper_registry_master_wrapper_2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation.bin ILogAutomation i_log_automation -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1.bin AutomationUtils automation_utils_2_1 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic.bin AutomationForwarderLogic automation_forwarder_logic -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter.bin LogUpkeepCounter log_upkeep_counter_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup.bin LogTriggeredStreamsLookup log_triggered_streams_lookup_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol.bin DummyProtocol dummy_protocol_wrapper - -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer.bin KeeperConsumer keeper_consumer_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance.bin KeeperConsumerPerformance keeper_consumer_performance_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker.bin PerformDataChecker perform_data_checker_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter.bin UpkeepCounter upkeep_counter_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive.bin UpkeepPerformCounterRestrictive upkeep_perform_counter_restrictive_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.abi ../../contracts/solc/v0.7/KeeperRegistry1_1/KeeperRegistry1_1.bin KeeperRegistry keeper_registry_wrapper1_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.abi ../../contracts/solc/v0.7/KeeperRegistry1_1Mock/KeeperRegistry1_1Mock.bin KeeperRegistryMock keeper_registry_wrapper1_1_mock +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.7/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.bin UpkeepPerformCounterRestrictive upkeep_perform_counter_restrictive_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.7/UpkeepCounter/UpkeepCounter.abi ../../contracts/solc/v0.7/UpkeepCounter/UpkeepCounter.bin UpkeepCounter upkeep_counter_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeepFactory.abi - CronUpkeepFactory cron_upkeep_factory_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeep.abi - CronUpkeep cron_upkeep_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.bin KeeperRegistrar keeper_registrar_wrapper1_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.bin KeeperRegistrarMock keeper_registrar_wrapper1_2_mock +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/KeeperRegistry1_2.bin KeeperRegistry keeper_registry_wrapper1_2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_2/TypeAndVersionInterface.bin TypeAndVersionInterface type_and_version_interface_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin KeeperRegistryCheckUpkeepGasUsageWrapper gas_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin KeeperRegistryCheckUpkeepGasUsageWrapperMock gas_wrapper_mock +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistry1_3/KeeperRegistry1_3.bin KeeperRegistry keeper_registry_wrapper1_3 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic1_3/KeeperRegistryLogic1_3.bin KeeperRegistryLogic keeper_registry_logic1_3 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/KeeperRegistrar2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/KeeperRegistrar2_0.bin KeeperRegistrar keeper_registrar_wrapper2_0 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistry2_0/KeeperRegistry2_0.bin KeeperRegistry keeper_registry_wrapper2_0 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistryLogic2_0/KeeperRegistryLogic2_0.bin KeeperRegistryLogic keeper_registry_logic2_0 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/UpkeepTranscoder/UpkeepTranscoder.abi ../../contracts/solc/v0.8.6/UpkeepTranscoder/UpkeepTranscoder.bin UpkeepTranscoder upkeep_transcoder +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep/VerifiableLoadUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadUpkeep/VerifiableLoadUpkeep.bin VerifiableLoadUpkeep verifiable_load_upkeep_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep/VerifiableLoadStreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadStreamsLookupUpkeep/VerifiableLoadStreamsLookupUpkeep.bin VerifiableLoadStreamsLookupUpkeep verifiable_load_streams_lookup_upkeep_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep/VerifiableLoadLogTriggerUpkeep.abi ../../contracts/solc/v0.8.16/VerifiableLoadLogTriggerUpkeep/VerifiableLoadLogTriggerUpkeep.bin VerifiableLoadLogTriggerUpkeep verifiable_load_log_trigger_upkeep_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/StreamsLookupUpkeep/StreamsLookupUpkeep.abi ../../contracts/solc/v0.8.16/StreamsLookupUpkeep/StreamsLookupUpkeep.bin StreamsLookupUpkeep streams_lookup_upkeep_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface/StreamsLookupCompatibleInterface.abi ../../contracts/solc/v0.8.16/StreamsLookupCompatibleInterface/StreamsLookupCompatibleInterface.bin StreamsLookupCompatibleInterface streams_lookup_compatible_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.abi ../../contracts/solc/v0.8.16/AutomationConsumerBenchmark/AutomationConsumerBenchmark.bin AutomationConsumerBenchmark automation_consumer_benchmark +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.abi ../../contracts/solc/v0.8.16/AutomationRegistrar2_1/AutomationRegistrar2_1.bin AutomationRegistrar automation_registrar_wrapper2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistry2_1/KeeperRegistry2_1.bin KeeperRegistry keeper_registry_wrapper_2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicA2_1/KeeperRegistryLogicA2_1.bin KeeperRegistryLogicA keeper_registry_logic_a_wrapper_2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.abi ../../contracts/solc/v0.8.16/KeeperRegistryLogicB2_1/KeeperRegistryLogicB2_1.bin KeeperRegistryLogicB keeper_registry_logic_b_wrapper_2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin IKeeperRegistryMaster i_keeper_registry_master_wrapper_2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin ILogAutomation i_log_automation +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin AutomationUtils automation_utils_2_1 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.bin AutomationForwarderLogic automation_forwarder_logic +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin LogUpkeepCounter log_upkeep_counter_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin LogTriggeredStreamsLookup log_triggered_streams_lookup_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.bin DummyProtocol dummy_protocol_wrapper + +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin KeeperConsumer keeper_consumer_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin KeeperConsumerPerformance keeper_consumer_performance_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin PerformDataChecker perform_data_checker_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.bin UpkeepCounter upkeep_counter_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.bin UpkeepPerformCounterRestrictive upkeep_perform_counter_restrictive_wrapper // v0.8.6 VRFConsumer -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock.bin VRFCoordinatorMock vrf_coordinator_mock -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer.bin VRFConsumer solidity_vrf_consumer_interface_v08 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper.bin VRFRequestIDBaseTestHelper solidity_vrf_request_id_v08 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample.abi ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample.bin VRFOwnerlessConsumerExample vrf_ownerless_consumer_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer.bin VRFLoadTestOwnerlessConsumer vrf_load_test_ownerless_consumer -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner.bin VRFLoadTestExternalSubOwner vrf_load_test_external_sub_owner -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics.bin VRFV2LoadTestWithMetrics vrf_load_test_with_metrics -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer.bin VRFV2OwnerTestConsumer vrf_owner_test_consumer -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer.bin VRFv2Consumer vrf_v2_consumer_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.abi ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordinatorMock.bin VRFCoordinatorMock vrf_coordinator_mock +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin VRFConsumer solidity_vrf_consumer_interface_v08 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.abi ../../contracts/solc/v0.8.6/VRFRequestIDBaseTestHelper/VRFRequestIDBaseTestHelper.bin VRFRequestIDBaseTestHelper solidity_vrf_request_id_v08 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample/VRFOwnerlessConsumerExample.abi ../../contracts/solc/v0.8.6/VRFOwnerlessConsumerExample/VRFOwnerlessConsumerExample.bin VRFOwnerlessConsumerExample vrf_ownerless_consumer_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.abi ../../contracts/solc/v0.8.6/VRFLoadTestOwnerlessConsumer/VRFLoadTestOwnerlessConsumer.bin VRFLoadTestOwnerlessConsumer vrf_load_test_ownerless_consumer +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.bin VRFLoadTestExternalSubOwner vrf_load_test_external_sub_owner +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2LoadTestWithMetrics/VRFV2LoadTestWithMetrics.bin VRFV2LoadTestWithMetrics vrf_load_test_with_metrics +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2OwnerTestConsumer/VRFV2OwnerTestConsumer.bin VRFV2OwnerTestConsumer vrf_owner_test_consumer +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.bin VRFv2Consumer vrf_v2_consumer_wrapper //go:generate go run ./generation/generate_link/wrap_link.go // VRF V2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BlockhashStore.abi ../../contracts/solc/v0.8.6/BlockhashStore.bin BlockhashStore blockhash_store -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BatchBlockhashStore.abi ../../contracts/solc/v0.8.6/BatchBlockhashStore.bin BatchBlockhashStore batch_blockhash_store -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2.bin BatchVRFCoordinatorV2 batch_vrf_coordinator_v2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFOwner.abi ../../contracts/solc/v0.8.6/VRFOwner.bin VRFOwner vrf_owner -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2.bin VRFCoordinatorV2 vrf_coordinator_v2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumerV2.abi ../../contracts/solc/v0.8.6/VRFConsumerV2.bin VRFConsumerV2 vrf_consumer_v2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2.bin VRFMaliciousConsumerV2 vrf_malicious_consumer_v2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.abi ../../contracts/solc/v0.8.6/BlockhashStore/BlockhashStore.bin BlockhashStore blockhash_store +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.abi ../../contracts/solc/v0.8.6/BatchBlockhashStore/BatchBlockhashStore.bin BatchBlockhashStore batch_blockhash_store +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/BatchVRFCoordinatorV2.bin BatchVRFCoordinatorV2 batch_vrf_coordinator_v2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.abi ../../contracts/solc/v0.8.6/VRFOwner/VRFOwner.bin VRFOwner vrf_owner +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.bin VRFCoordinatorV2 vrf_coordinator_v2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumerV2/VRFConsumerV2.abi ../../contracts/solc/v0.8.6/VRFConsumerV2/VRFConsumerV2.bin VRFConsumerV2 vrf_consumer_v2 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2/VRFMaliciousConsumerV2.bin VRFMaliciousConsumerV2 vrf_malicious_consumer_v2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFTestHelper.abi ../../contracts/solc/v0.8.6/VRFTestHelper.bin VRFV08TestHelper solidity_vrf_v08_verifier_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFSingleConsumerExample.bin VRFSingleConsumerExample vrf_single_consumer_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFTestHelper/VRFTestHelper.abi ../../contracts/solc/v0.8.6/VRFTestHelper/VRFTestHelper.bin VRFV08TestHelper solidity_vrf_v08_verifier_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFSingleConsumerExample/VRFSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFSingleConsumerExample/VRFSingleConsumerExample.bin VRFSingleConsumerExample vrf_single_consumer_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample.bin VRFExternalSubOwnerExample vrf_external_sub_owner_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.bin VRFExternalSubOwnerExample vrf_external_sub_owner_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2RevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2RevertingExample.bin VRFV2RevertingExample vrfv2_reverting_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2RevertingExample/VRFV2RevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2RevertingExample/VRFV2RevertingExample.bin VRFV2RevertingExample vrfv2_reverting_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample.bin VRFConsumerV2UpgradeableExample vrf_consumer_v2_upgradeable_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2UpgradeableExample/VRFConsumerV2UpgradeableExample.bin VRFConsumerV2UpgradeableExample vrf_consumer_v2_upgradeable_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy.abi ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy.bin VRFV2TransparentUpgradeableProxy vrfv2_transparent_upgradeable_proxy -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin.abi ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin.bin VRFV2ProxyAdmin vrfv2_proxy_admin -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper.bin ChainSpecificUtilHelper chain_specific_util_helper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.abi ../../contracts/solc/v0.8.6/VRFV2TransparentUpgradeableProxy/VRFV2TransparentUpgradeableProxy.bin VRFV2TransparentUpgradeableProxy vrfv2_transparent_upgradeable_proxy +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.abi ../../contracts/solc/v0.8.6/VRFV2ProxyAdmin/VRFV2ProxyAdmin.bin VRFV2ProxyAdmin vrfv2_proxy_admin +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.bin ChainSpecificUtilHelper chain_specific_util_helper // VRF V2 Wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper.bin VRFV2Wrapper vrfv2_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface.bin VRFV2WrapperInterface vrfv2_wrapper_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample.bin VRFV2WrapperConsumerExample vrfv2_wrapper_consumer_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.abi ../../contracts/solc/v0.8.6/VRFV2Wrapper/VRFV2Wrapper.bin VRFV2Wrapper vrfv2_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.abi ../../contracts/solc/v0.8.6/VRFV2WrapperInterface/VRFV2WrapperInterface.bin VRFV2WrapperInterface vrfv2_wrapper_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2WrapperConsumerExample/VRFV2WrapperConsumerExample.bin VRFV2WrapperConsumerExample vrfv2_wrapper_consumer_example // Keepers X VRF v2 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer.bin KeepersVRFConsumer keepers_vrf_consumer +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.abi ../../contracts/solc/v0.8.6/KeepersVRFConsumer/KeepersVRFConsumer.bin KeepersVRFConsumer keepers_vrf_consumer // VRF V2Plus -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal.abi ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal.bin IVRFCoordinatorV2PlusInternal vrf_coordinator_v2plus_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus.bin BatchVRFCoordinatorV2Plus batch_vrf_coordinator_v2plus -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/TrustedBlockhashStore.abi ../../contracts/solc/v0.8.6/TrustedBlockhashStore.bin TrustedBlockhashStore trusted_blockhash_store -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample.bin VRFV2PlusConsumerExample vrfv2plus_consumer_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5.bin VRFCoordinatorV2_5 vrf_coordinator_v2_5 -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusWrapper.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapper.bin VRFV2PlusWrapper vrfv2plus_wrapper -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample.bin VRFV2PlusWrapperConsumerExample vrfv2plus_wrapper_consumer_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus.bin VRFMaliciousConsumerV2Plus vrf_malicious_consumer_v2_plus -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample.bin VRFV2PlusSingleConsumerExample vrf_v2plus_single_consumer -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample.bin VRFV2PlusExternalSubOwnerExample vrf_v2plus_sub_owner -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample.bin VRFV2PlusRevertingExample vrfv2plus_reverting_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample.bin VRFConsumerV2PlusUpgradeableExample vrf_consumer_v2_plus_upgradeable_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusClient.abi ../../contracts/solc/v0.8.6/VRFV2PlusClient.bin VRFV2PlusClient vrfv2plus_client -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example.bin VRFCoordinatorV2Plus_V2Example vrf_coordinator_v2_plus_v2_example -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator.abi ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator.bin VRFV2PlusMaliciousMigrator vrfv2plus_malicious_migrator -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics.bin VRFV2PlusLoadTestWithMetrics vrf_v2plus_load_test_with_metrics -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion.bin VRFCoordinatorV2PlusUpgradedVersion vrf_v2plus_upgraded_version -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer.bin VRFV2PlusWrapperLoadTestConsumer vrfv2plus_wrapper_load_test_consumer +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.abi ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.bin IVRFCoordinatorV2PlusInternal vrf_coordinator_v2plus_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin BatchVRFCoordinatorV2Plus batch_vrf_coordinator_v2plus +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/TrustedBlockhashStore/TrustedBlockhashStore.abi ../../contracts/solc/v0.8.6/TrustedBlockhashStore/TrustedBlockhashStore.bin TrustedBlockhashStore trusted_blockhash_store +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusConsumerExample/VRFV2PlusConsumerExample.bin VRFV2PlusConsumerExample vrfv2plus_consumer_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin VRFCoordinatorV2_5 vrf_coordinator_v2_5 +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusWrapper/VRFV2PlusWrapper.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapper/VRFV2PlusWrapper.bin VRFV2PlusWrapper vrfv2plus_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample/VRFV2PlusWrapperConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperConsumerExample/VRFV2PlusWrapperConsumerExample.bin VRFV2PlusWrapperConsumerExample vrfv2plus_wrapper_consumer_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.abi ../../contracts/solc/v0.8.6/VRFMaliciousConsumerV2Plus/VRFMaliciousConsumerV2Plus.bin VRFMaliciousConsumerV2Plus vrf_malicious_consumer_v2_plus +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample/VRFV2PlusSingleConsumerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusSingleConsumerExample/VRFV2PlusSingleConsumerExample.bin VRFV2PlusSingleConsumerExample vrf_v2plus_single_consumer +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample/VRFV2PlusExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusExternalSubOwnerExample/VRFV2PlusExternalSubOwnerExample.bin VRFV2PlusExternalSubOwnerExample vrf_v2plus_sub_owner +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample/VRFV2PlusRevertingExample.abi ../../contracts/solc/v0.8.6/VRFV2PlusRevertingExample/VRFV2PlusRevertingExample.bin VRFV2PlusRevertingExample vrfv2plus_reverting_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample/VRFConsumerV2PlusUpgradeableExample.abi ../../contracts/solc/v0.8.6/VRFConsumerV2PlusUpgradeableExample/VRFConsumerV2PlusUpgradeableExample.bin VRFConsumerV2PlusUpgradeableExample vrf_consumer_v2_plus_upgradeable_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.abi ../../contracts/solc/v0.8.6/VRFV2PlusClient/VRFV2PlusClient.bin VRFV2PlusClient vrfv2plus_client +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.bin VRFCoordinatorV2Plus_V2Example vrf_coordinator_v2_plus_v2_example +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.abi ../../contracts/solc/v0.8.6/VRFV2PlusMaliciousMigrator/VRFV2PlusMaliciousMigrator.bin VRFV2PlusMaliciousMigrator vrfv2plus_malicious_migrator +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics/VRFV2PlusLoadTestWithMetrics.abi ../../contracts/solc/v0.8.6/VRFV2PlusLoadTestWithMetrics/VRFV2PlusLoadTestWithMetrics.bin VRFV2PlusLoadTestWithMetrics vrf_v2plus_load_test_with_metrics +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2PlusUpgradedVersion/VRFCoordinatorV2PlusUpgradedVersion.bin VRFCoordinatorV2PlusUpgradedVersion vrf_v2plus_upgraded_version +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer/VRFV2PlusWrapperLoadTestConsumer.abi ../../contracts/solc/v0.8.6/VRFV2PlusWrapperLoadTestConsumer/VRFV2PlusWrapperLoadTestConsumer.bin VRFV2PlusWrapperLoadTestConsumer vrfv2plus_wrapper_load_test_consumer // Aggregators -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface.bin AggregatorV2V3Interface aggregator_v2v3_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV3Interface.bin AggregatorV3Interface aggregator_v3_interface -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy.bin MockAggregatorProxy mock_aggregator_proxy +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.bin AggregatorV2V3Interface aggregator_v2v3_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.bin AggregatorV3Interface aggregator_v3_interface +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.abi ../../contracts/solc/v0.8.6/MockAggregatorProxy/MockAggregatorProxy.bin MockAggregatorProxy mock_aggregator_proxy // Log tester -//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter.bin LogEmitter log_emitter +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.abi ../../contracts/solc/v0.8.19/LogEmitter/LogEmitter.bin LogEmitter log_emitter // Chainlink Functions //go:generate go generate ./functions diff --git a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt index abc3b47db2c..293defcfbe0 100644 --- a/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/llo-feeds/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,10 +1,10 @@ GETH_VERSION: 1.12.0 -errored_verifier: ../../../contracts/solc/v0.8.16/ErroredVerifier.abi ../../../contracts/solc/v0.8.16/ErroredVerifier.bin 510d18a58bfda646be35e46491baf73041eb333a349615465b20e2b5b41c5f73 -exposed_verifier: ../../../contracts/solc/v0.8.16/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier.bin 6932cea8f2738e874d3ec9e1a4231d2421704030c071d9e15dd2f7f08482c246 -fee_manager: ../../../contracts/solc/v0.8.16/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager.bin 1b852df75bfabcc2b57539e84309cd57f9e693a2bb6b25a50e4a6101ccf32c49 +errored_verifier: ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.abi ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.bin 510d18a58bfda646be35e46491baf73041eb333a349615465b20e2b5b41c5f73 +exposed_verifier: ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.bin 6932cea8f2738e874d3ec9e1a4231d2421704030c071d9e15dd2f7f08482c246 +fee_manager: ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.bin 1b852df75bfabcc2b57539e84309cd57f9e693a2bb6b25a50e4a6101ccf32c49 llo_feeds: ../../../contracts/solc/v0.8.16/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager.bin cb71e018f67e49d7bc0e194c822204dfd59f79ff42e4fc8fd8ab63f3acd71361 llo_feeds_test: ../../../contracts/solc/v0.8.16/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier.bin 6932cea8f2738e874d3ec9e1a4231d2421704030c071d9e15dd2f7f08482c246 -reward_manager: ../../../contracts/solc/v0.8.16/RewardManager.abi ../../../contracts/solc/v0.8.16/RewardManager.bin db73e9062b17a1d5aa14c06881fe2be49bd95b00b7f1a8943910c5e4ded5b221 -verifier: ../../../contracts/solc/v0.8.16/Verifier.abi ../../../contracts/solc/v0.8.16/Verifier.bin df12786bbeccf3a8f3389479cf93c055b4efd5904b9f99a4835f81af43fe62bf -verifier_proxy: ../../../contracts/solc/v0.8.16/VerifierProxy.abi ../../../contracts/solc/v0.8.16/VerifierProxy.bin 6393443d0a323f2dbe9687dc30fd77f8dfa918944b61c651759746ff2d76e4e5 +reward_manager: ../../../contracts/solc/v0.8.16/RewardManager/RewardManager.abi ../../../contracts/solc/v0.8.16/RewardManager/RewardManager.bin db73e9062b17a1d5aa14c06881fe2be49bd95b00b7f1a8943910c5e4ded5b221 +verifier: ../../../contracts/solc/v0.8.16/Verifier/Verifier.abi ../../../contracts/solc/v0.8.16/Verifier/Verifier.bin df12786bbeccf3a8f3389479cf93c055b4efd5904b9f99a4835f81af43fe62bf +verifier_proxy: ../../../contracts/solc/v0.8.16/VerifierProxy/VerifierProxy.abi ../../../contracts/solc/v0.8.16/VerifierProxy/VerifierProxy.bin 6393443d0a323f2dbe9687dc30fd77f8dfa918944b61c651759746ff2d76e4e5 werc20_mock: ../../../contracts/solc/v0.8.19/WERC20Mock.abi ../../../contracts/solc/v0.8.19/WERC20Mock.bin ff2ca3928b2aa9c412c892cb8226c4d754c73eeb291bb7481c32c48791b2aa94 diff --git a/core/gethwrappers/llo-feeds/go_generate.go b/core/gethwrappers/llo-feeds/go_generate.go index 8d9e3be0493..5b2088f43a0 100644 --- a/core/gethwrappers/llo-feeds/go_generate.go +++ b/core/gethwrappers/llo-feeds/go_generate.go @@ -3,9 +3,9 @@ package gethwrappers // Chainlink LLO -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/Verifier.abi ../../../contracts/solc/v0.8.16/Verifier.bin Verifier verifier -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/VerifierProxy.abi ../../../contracts/solc/v0.8.16/VerifierProxy.bin VerifierProxy verifier_proxy -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/ErroredVerifier.abi ../../../contracts/solc/v0.8.16/ErroredVerifier.bin ErroredVerifier errored_verifier -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier.bin ExposedVerifier exposed_verifier -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/RewardManager.abi ../../../contracts/solc/v0.8.16/RewardManager.bin RewardManager reward_manager -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager.bin FeeManager fee_manager +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/Verifier/Verifier.abi ../../../contracts/solc/v0.8.16/Verifier/Verifier.bin Verifier verifier +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/VerifierProxy/VerifierProxy.abi ../../../contracts/solc/v0.8.16/VerifierProxy/VerifierProxy.bin VerifierProxy verifier_proxy +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.abi ../../../contracts/solc/v0.8.16/ErroredVerifier/ErroredVerifier.bin ErroredVerifier errored_verifier +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.abi ../../../contracts/solc/v0.8.16/ExposedVerifier/ExposedVerifier.bin ExposedVerifier exposed_verifier +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/RewardManager/RewardManager.abi ../../../contracts/solc/v0.8.16/RewardManager/RewardManager.bin RewardManager reward_manager +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.abi ../../../contracts/solc/v0.8.16/FeeManager/FeeManager.bin FeeManager fee_manager diff --git a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 7ac7e77a4b8..af907ce85eb 100644 --- a/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/shared/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,5 +1,5 @@ GETH_VERSION: 1.12.0 -burn_mint_erc677: ../../../contracts/solc/v0.8.19/BurnMintERC677.abi ../../../contracts/solc/v0.8.19/BurnMintERC677.bin 405c9016171e614b17e10588653ef8d33dcea21dd569c3fddc596a46fcff68a3 -erc20: ../../../contracts/solc/v0.8.19/ERC20.abi ../../../contracts/solc/v0.8.19/ERC20.bin 5b1a93d9b24f250e49a730c96335a8113c3f7010365cba578f313b483001d4fc -link_token: ../../../contracts/solc/v0.8.19/LinkToken.abi ../../../contracts/solc/v0.8.19/LinkToken.bin c0ef9b507103aae541ebc31d87d051c2764ba9d843076b30ec505d37cdfffaba -werc20_mock: ../../../contracts/solc/v0.8.19/WERC20Mock.abi ../../../contracts/solc/v0.8.19/WERC20Mock.bin ff2ca3928b2aa9c412c892cb8226c4d754c73eeb291bb7481c32c48791b2aa94 +burn_mint_erc677: ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.abi ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.bin 405c9016171e614b17e10588653ef8d33dcea21dd569c3fddc596a46fcff68a3 +erc20: ../../../contracts/solc/v0.8.19/ERC20/ERC20.abi ../../../contracts/solc/v0.8.19/ERC20/ERC20.bin 5b1a93d9b24f250e49a730c96335a8113c3f7010365cba578f313b483001d4fc +link_token: ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.abi ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.bin c0ef9b507103aae541ebc31d87d051c2764ba9d843076b30ec505d37cdfffaba +werc20_mock: ../../../contracts/solc/v0.8.19/WERC20Mock/WERC20Mock.abi ../../../contracts/solc/v0.8.19/WERC20Mock/WERC20Mock.bin ff2ca3928b2aa9c412c892cb8226c4d754c73eeb291bb7481c32c48791b2aa94 diff --git a/core/gethwrappers/shared/go_generate.go b/core/gethwrappers/shared/go_generate.go index 85a01670c9a..6f3bead7d6b 100644 --- a/core/gethwrappers/shared/go_generate.go +++ b/core/gethwrappers/shared/go_generate.go @@ -2,7 +2,7 @@ // golang packages, using abigen. package gethwrappers -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/BurnMintERC677.abi ../../../contracts/solc/v0.8.19/BurnMintERC677.bin BurnMintERC677 burn_mint_erc677 -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/LinkToken.abi ../../../contracts/solc/v0.8.19/LinkToken.bin LinkToken link_token -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/ERC20.abi ../../../contracts/solc/v0.8.19/ERC20.bin ERC20 erc20 -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/WERC20Mock.abi ../../../contracts/solc/v0.8.19/WERC20Mock.bin WERC20Mock werc20_mock +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.abi ../../../contracts/solc/v0.8.19/BurnMintERC677/BurnMintERC677.bin BurnMintERC677 burn_mint_erc677 +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.abi ../../../contracts/solc/v0.8.19/LinkToken/LinkToken.bin LinkToken link_token +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/ERC20/ERC20.abi ../../../contracts/solc/v0.8.19/ERC20/ERC20.bin ERC20 erc20 +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.19/WERC20Mock/WERC20Mock.abi ../../../contracts/solc/v0.8.19/WERC20Mock/WERC20Mock.bin WERC20Mock werc20_mock diff --git a/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 8ea0492fa40..a6d32bf0a85 100644 --- a/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/transmission/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,9 +1,9 @@ GETH_VERSION: 1.12.0 -entry_point: ../../../contracts/solc/v0.8.15/EntryPoint.abi ../../../contracts/solc/v0.8.15/EntryPoint.bin 2cb4bb2ba3efa8df3dfb0a57eb3727d17b68fe202682024fa7cfb4faf026833e +entry_point: ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.abi ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.bin 2cb4bb2ba3efa8df3dfb0a57eb3727d17b68fe202682024fa7cfb4faf026833e greeter: ../../../contracts/solc/v0.8.15/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c -greeter_wrapper: ../../../contracts/solc/v0.8.15/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c -paymaster_wrapper: ../../../contracts/solc/v0.8.15/Paymaster.abi ../../../contracts/solc/v0.8.15/Paymaster.bin 189ef817a5b7a6ff53ddf35b1988465b8aec479c47b77236fe20bf7e67d48100 +greeter_wrapper: ../../../contracts/solc/v0.8.15/Greeter/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter/Greeter.bin 653dcba5c33a46292073939ce1e639372cf521c0ec2814d4c9f20c72f796f18c +paymaster_wrapper: ../../../contracts/solc/v0.8.15/Paymaster/Paymaster.abi ../../../contracts/solc/v0.8.15/Paymaster/Paymaster.bin 189ef817a5b7a6ff53ddf35b1988465b8aec479c47b77236fe20bf7e67d48100 sca: ../../../contracts/solc/v0.8.15/SCA.abi ../../../contracts/solc/v0.8.15/SCA.bin ae0f860cdac87d4ac505edbd228bd3ea1108550453aba67aebcb61f09cf70d0b -sca_wrapper: ../../../contracts/solc/v0.8.15/SCA.abi ../../../contracts/solc/v0.8.15/SCA.bin 2a8100fbdb41e6ce917ed333a624eaa4a8984b07e2d8d8ca6bba9bc9f74b05d7 -smart_contract_account_factory: ../../../contracts/solc/v0.8.15/SmartContractAccountFactory.abi ../../../contracts/solc/v0.8.15/SmartContractAccountFactory.bin a44d6fa2dbf9cb3441d6d637d89e1cd656f28b6bf4146f58d508067474bf845b -smart_contract_account_helper: ../../../contracts/solc/v0.8.15/SmartContractAccountHelper.abi ../../../contracts/solc/v0.8.15/SmartContractAccountHelper.bin 22f960a74bd1581a12aa4f8f438a3f265f32f43682f5c1897ca50707b9982d56 +sca_wrapper: ../../../contracts/solc/v0.8.15/SCA/SCA.abi ../../../contracts/solc/v0.8.15/SCA/SCA.bin 2a8100fbdb41e6ce917ed333a624eaa4a8984b07e2d8d8ca6bba9bc9f74b05d7 +smart_contract_account_factory: ../../../contracts/solc/v0.8.15/SmartContractAccountFactory/SmartContractAccountFactory.abi ../../../contracts/solc/v0.8.15/SmartContractAccountFactory/SmartContractAccountFactory.bin a44d6fa2dbf9cb3441d6d637d89e1cd656f28b6bf4146f58d508067474bf845b +smart_contract_account_helper: ../../../contracts/solc/v0.8.15/SmartContractAccountHelper/SmartContractAccountHelper.abi ../../../contracts/solc/v0.8.15/SmartContractAccountHelper/SmartContractAccountHelper.bin 22f960a74bd1581a12aa4f8f438a3f265f32f43682f5c1897ca50707b9982d56 diff --git a/core/gethwrappers/transmission/go_generate.go b/core/gethwrappers/transmission/go_generate.go index 52182a11504..54c6ecf94ed 100644 --- a/core/gethwrappers/transmission/go_generate.go +++ b/core/gethwrappers/transmission/go_generate.go @@ -3,9 +3,9 @@ package gethwrappers // Transmission -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter.bin Greeter greeter_wrapper -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/SmartContractAccountFactory.abi ../../../contracts/solc/v0.8.15/SmartContractAccountFactory.bin SmartContractAccountFactory smart_contract_account_factory -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/EntryPoint.abi ../../../contracts/solc/v0.8.15/EntryPoint.bin EntryPoint entry_point -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/SmartContractAccountHelper.abi ../../../contracts/solc/v0.8.15/SmartContractAccountHelper.bin SmartContractAccountHelper smart_contract_account_helper -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/SCA.abi ../../../contracts/solc/v0.8.15/SCA.bin SCA sca_wrapper -//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/Paymaster.abi ../../../contracts/solc/v0.8.15/Paymaster.bin Paymaster paymaster_wrapper +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/Greeter/Greeter.abi ../../../contracts/solc/v0.8.15/Greeter/Greeter.bin Greeter greeter_wrapper +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/SmartContractAccountFactory/SmartContractAccountFactory.abi ../../../contracts/solc/v0.8.15/SmartContractAccountFactory/SmartContractAccountFactory.bin SmartContractAccountFactory smart_contract_account_factory +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.abi ../../../contracts/solc/v0.8.15/EntryPoint/EntryPoint.bin EntryPoint entry_point +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/SmartContractAccountHelper/SmartContractAccountHelper.abi ../../../contracts/solc/v0.8.15/SmartContractAccountHelper/SmartContractAccountHelper.bin SmartContractAccountHelper smart_contract_account_helper +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/SCA/SCA.abi ../../../contracts/solc/v0.8.15/SCA/SCA.bin SCA sca_wrapper +//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.15/Paymaster/Paymaster.abi ../../../contracts/solc/v0.8.15/Paymaster/Paymaster.bin Paymaster paymaster_wrapper From 41eac0a47137b65a70caa824cdb7f2c3b7c749c6 Mon Sep 17 00:00:00 2001 From: Francisco de Borja Aranda Castillejo Date: Fri, 3 Nov 2023 16:31:51 +0100 Subject: [PATCH 071/271] DEVEX-1046: add functionalities to LinkMon (#11097) * DEVEX-1046: add functionalities to LinkMon * add timestamp checks * adding suggestions and remove duplicated checks * add changes and tests * polishing last details * add custom errors * rebase develop * rebase develop --- .../upkeeps/LinkAvailableBalanceMonitor.sol | 311 ++++++++-------- .../LinkAvailableBalanceMonitor.test.ts | 352 +++++++++--------- 2 files changed, 339 insertions(+), 324 deletions(-) diff --git a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol index e2d42bc0666..9b9dc2d6b7d 100644 --- a/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol +++ b/contracts/src/v0.8/automation/upkeeps/LinkAvailableBalanceMonitor.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.6; +pragma solidity 0.8.19; import {AutomationCompatibleInterface} from "../interfaces/AutomationCompatibleInterface.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; -import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol"; import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; import {Pausable} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/security/Pausable.sol"; @@ -35,94 +34,87 @@ interface ILinkAvailable { /// we could save a fair amount of gas and re-write this upkeep for use with Automation v2.0+, /// which has significantly different trust assumptions contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationCompatibleInterface { - using EnumerableMap for EnumerableMap.AddressToUintMap; - + event BalanceUpdated(address indexed addr, uint256 oldBalance, uint256 newBalance); event FundsWithdrawn(uint256 amountWithdrawn, address payee); - event TopUpSucceeded(address indexed topUpAddress); + event UpkeepIntervalSet(uint256 oldUpkeepInterval, uint256 newUpkeepInterval); + event MaxCheckSet(uint256 oldMaxCheck, uint256 newMaxCheck); + event MaxPerformSet(uint256 oldMaxPerform, uint256 newMaxPerform); + event MinWaitPeriodSet(uint256 s_minWaitPeriodSeconds, uint256 minWaitPeriodSeconds); event TopUpBlocked(address indexed topUpAddress); + event TopUpFailed(address indexed recipient); + event TopUpSucceeded(address indexed topUpAddress); + event TopUpUpdated(address indexed addr, uint256 oldTopUpAmount, uint256 newTopUpAmount); event WatchlistUpdated(); - event MaxPerformUpdated(uint256 oldMaxPerform, uint256 newMaxPerform); - event MaxCheckUpdated(uint256 oldMaxCheck, uint256 newMaxCheck); + error InvalidAddress(address target); + error InvalidMaxCheck(uint16 maxCheck); + error InvalixMaxPerform(uint16 maxPerform); + error InvalidMinBalance(uint96 minBalance); + error InvalidTopUpAmount(uint96 topUpAmount); + error InvalidUpkeepInterval(uint8 upkeepInterval); + error InvalidLinkTokenAddress(address lt); error InvalidWatchList(); error DuplicateAddress(address duplicate); + struct MonitoredAddress { + uint96 minBalance; + uint96 topUpAmount; + uint56 lastTopUpTimestamp; + bool isActive; + } + IERC20 private immutable LINK_TOKEN; - EnumerableMap.AddressToUintMap private s_watchList; - uint256 private s_topUpAmount; - uint32 private s_minWaitPeriodSeconds; + uint256 private s_minWaitPeriodSeconds; uint16 private s_maxPerform; uint16 private s_maxCheck; + uint8 private s_upkeepInterval; + address[] private s_watchList; + mapping(address targetAddress => MonitoredAddress targetProperties) internal s_targets; /// @param linkTokenAddress the LINK token address - /// @param topUpAmount the amount of LINK to top up an aggregator with at once constructor( address linkTokenAddress, - uint256 topUpAmount, + uint256 minWaitPeriodSeconds, uint16 maxPerform, - uint16 maxCheck + uint16 maxCheck, + uint8 upkeepInterval ) ConfirmedOwner(msg.sender) { - require(linkTokenAddress != address(0), "LinkAvailableBalanceMonitor: invalid linkTokenAddress"); - require(topUpAmount > 0, "LinkAvailableBalanceMonitor: invalid topUpAmount"); + if (linkTokenAddress == address(0)) revert InvalidLinkTokenAddress(linkTokenAddress); LINK_TOKEN = IERC20(linkTokenAddress); - s_topUpAmount = topUpAmount; - s_maxPerform = maxPerform; - s_maxCheck = maxCheck; + setMinWaitPeriodSeconds(minWaitPeriodSeconds); + setMaxPerform(maxPerform); + setMaxCheck(maxCheck); + setUpkeepInterval(upkeepInterval); } /// @notice Sets the list of subscriptions to watch and their funding parameters /// @param addresses the list of target addresses to watch (could be direct target or IAggregatorProxy) /// @param minBalances the list of corresponding minBalance for the target address - function setWatchList(address[] calldata addresses, uint256[] calldata minBalances) external onlyOwner { - if (addresses.length != minBalances.length) { - revert InvalidWatchList(); - } - // first, remove all existing addresses from list - for (uint256 idx = s_watchList.length(); idx > 0; idx--) { - (address target, ) = s_watchList.at(idx - 1); - require(s_watchList.remove(target), "LinkAvailableBalanceMonitor: unable to setWatchlist"); - } - // then set new addresses - for (uint256 idx = 0; idx < addresses.length; idx++) { - if (s_watchList.contains(addresses[idx])) { - revert DuplicateAddress(addresses[idx]); - } - if (addresses[idx] == address(0)) { - revert InvalidWatchList(); - } - s_watchList.set(addresses[idx], minBalances[idx]); - } - emit WatchlistUpdated(); - } - - /// @notice Adds addresses to the watchlist without overwriting existing members - /// @param addresses the list of target addresses to watch (could be direct target or IAggregatorProxy) - /// @param minBalances the list of corresponding minBalance for the target address - function addToWatchList(address[] calldata addresses, uint256[] calldata minBalances) external onlyOwner { - if (addresses.length != minBalances.length) { + /// @param topUpAmounts the list of corresponding minTopUp for the target address + function setWatchList( + address[] calldata addresses, + uint96[] calldata minBalances, + uint96[] calldata topUpAmounts + ) external onlyOwner { + if (addresses.length != minBalances.length || addresses.length != topUpAmounts.length) { revert InvalidWatchList(); } - for (uint256 idx = 0; idx < addresses.length; idx++) { - if (s_watchList.contains(addresses[idx])) { - revert DuplicateAddress(addresses[idx]); - } - if (addresses[idx] == address(0)) { - revert InvalidWatchList(); - } - s_watchList.set(addresses[idx], minBalances[idx]); + for (uint256 idx = 0; idx < s_watchList.length; idx++) { + delete s_targets[s_watchList[idx]]; } - emit WatchlistUpdated(); - } - - /// @notice Removes addresses from the watchlist - /// @param addresses the list of target addresses to remove from the watchlist - function removeFromWatchlist(address[] calldata addresses) external onlyOwner { for (uint256 idx = 0; idx < addresses.length; idx++) { - if (!s_watchList.contains(addresses[idx])) { - revert InvalidWatchList(); - } - s_watchList.remove(addresses[idx]); + address targetAddress = addresses[idx]; + if (s_targets[targetAddress].isActive) revert DuplicateAddress(addresses[idx]); + if (addresses[idx] == address(0)) revert InvalidWatchList(); + if (topUpAmounts[idx] == 0) revert InvalidWatchList(); + s_targets[targetAddress] = MonitoredAddress({ + isActive: true, + minBalance: minBalances[idx], + topUpAmount: topUpAmounts[idx], + lastTopUpTimestamp: 0 + }); } + s_watchList = addresses; emit WatchlistUpdated(); } @@ -135,20 +127,21 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp function sampleUnderfundedAddresses() public view returns (address[] memory) { uint16 maxPerform = s_maxPerform; uint16 maxCheck = s_maxCheck; - uint256 numTargets = s_watchList.length(); - uint256 idx = uint256(blockhash(block.number - 1)) % numTargets; // start at random index, to distribute load + uint256 numTargets = s_watchList.length; + uint256 idx = uint256(blockhash(block.number - (block.number % s_upkeepInterval) - 1)) % numTargets; uint256 numToCheck = numTargets < maxCheck ? numTargets : maxCheck; uint256 numFound = 0; address[] memory targetsToFund = new address[](maxPerform); + MonitoredAddress memory target; for ( uint256 numChecked = 0; numChecked < numToCheck; (idx, numChecked) = ((idx + 1) % numTargets, numChecked + 1) ) { - (address target, uint256 minBalance) = s_watchList.at(idx); - (bool needsFunding, ) = _needsFunding(target, minBalance); - if (needsFunding) { - targetsToFund[numFound] = target; + address targetAddress = s_watchList[idx]; + target = s_targets[targetAddress]; + if (_needsFunding(targetAddress, target.minBalance)) { + targetsToFund[numFound] = targetAddress; numFound++; if (numFound == maxPerform) { break; // max number of addresses in batch reached @@ -163,29 +156,59 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp return targetsToFund; } - /// @notice Send funds to the targets provided. - /// @param targetAddresses the list of targets to fund function topUp(address[] memory targetAddresses) public whenNotPaused { - uint256 topUpAmount = s_topUpAmount; - uint256 stopIdx = targetAddresses.length; - uint256 numCanFund = LINK_TOKEN.balanceOf(address(this)) / topUpAmount; - stopIdx = numCanFund < stopIdx ? numCanFund : stopIdx; - for (uint256 idx = 0; idx < stopIdx; idx++) { - (bool exists, uint256 minBalance) = s_watchList.tryGet(targetAddresses[idx]); - if (!exists) { - emit TopUpBlocked(targetAddresses[idx]); - continue; - } - (bool needsFunding, address target) = _needsFunding(targetAddresses[idx], minBalance); - if (!needsFunding) { - emit TopUpBlocked(targetAddresses[idx]); - continue; + MonitoredAddress memory target; + uint256 localBalance = LINK_TOKEN.balanceOf(address(this)); + for (uint256 idx = 0; idx < targetAddresses.length; idx++) { + address targetAddress = targetAddresses[idx]; + target = s_targets[targetAddress]; + if (localBalance >= target.topUpAmount && _needsFunding(targetAddress, target.minBalance)) { + bool success = LINK_TOKEN.transfer(targetAddress, target.topUpAmount); + if (success) { + localBalance -= target.topUpAmount; + target.lastTopUpTimestamp = uint56(block.timestamp); + emit TopUpSucceeded(targetAddress); + } else { + emit TopUpFailed(targetAddress); + } + } else { + emit TopUpBlocked(targetAddress); } - LINK_TOKEN.transfer(target, topUpAmount); - emit TopUpSucceeded(targetAddresses[idx]); } } + /// @notice checks the target (could be direct target or IAggregatorProxy), and determines + /// if it is elligible for funding + /// @param targetAddress the target to check + /// @param minBalance minimum balance required for the target + /// @return bool whether the target needs funding or not + function _needsFunding(address targetAddress, uint256 minBalance) private view returns (bool) { + // Explicitly check if the targetAddress is the zero address + // or if it's not a contract. In both cases return with false, + // to prevent target.linkAvailableForPayment from running, + // which would revert the operation. + if (targetAddress == address(0) || targetAddress.code.length == 0) { + return false; + } + MonitoredAddress memory addressToCheck = s_targets[targetAddress]; + ILinkAvailable target; + IAggregatorProxy proxy = IAggregatorProxy(targetAddress); + try proxy.aggregator() returns (address aggregatorAddress) { + if (aggregatorAddress == address(0)) return false; + target = ILinkAvailable(aggregatorAddress); + } catch { + target = ILinkAvailable(targetAddress); + } + try target.linkAvailableForPayment() returns (int256 balance) { + if ( + balance < int256(minBalance) && addressToCheck.lastTopUpTimestamp + s_minWaitPeriodSeconds <= block.timestamp + ) { + return true; + } + } catch {} + return false; + } + /// @notice Gets list of subscription ids that are underfunded and returns a keeper-compatible payload. /// @return upkeepNeeded signals if upkeep is needed /// @return performData is an abi encoded list of subscription ids that need funds @@ -193,12 +216,6 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp bytes calldata ) external view override whenNotPaused returns (bool upkeepNeeded, bytes memory performData) { address[] memory needsFunding = sampleUnderfundedAddresses(); - uint256 numCanFund = LINK_TOKEN.balanceOf(address(this)) / s_topUpAmount; - if (numCanFund < needsFunding.length) { - assembly { - mstore(needsFunding, numCanFund) // resize - } - } upkeepNeeded = needsFunding.length > 0; performData = abi.encode(needsFunding); return (upkeepNeeded, performData); @@ -215,38 +232,54 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp /// @param amount the amount of the LINK to withdraw /// @param payee the address to pay function withdraw(uint256 amount, address payable payee) external onlyOwner { - require(payee != address(0), "LinkAvailableBalanceMonitor: invalid payee address"); + if (payee == address(0)) revert InvalidAddress(payee); LINK_TOKEN.transfer(payee, amount); emit FundsWithdrawn(amount, payee); } - /// @notice Sets the top up amount - function setTopUpAmount(uint256 topUpAmount) external onlyOwner returns (uint256) { - require(topUpAmount > 0, "LinkAvailableBalanceMonitor: invalid linkTokenAddress"); - return s_topUpAmount = topUpAmount; + /// @notice Sets the minimum balance for the given target address + function setMinBalance(address target, uint96 minBalance) external onlyOwner { + if (target == address(0)) revert InvalidAddress(target); + if (minBalance == 0) revert InvalidMinBalance(minBalance); + if (!s_targets[target].isActive) revert InvalidWatchList(); + uint256 oldBalance = s_targets[target].minBalance; + s_targets[target].minBalance = minBalance; + emit BalanceUpdated(target, oldBalance, minBalance); } /// @notice Sets the minimum balance for the given target address - function setMinBalance(address target, uint256 minBalance) external onlyOwner returns (uint256) { - require(minBalance > 0, "LinkAvailableBalanceMonitor: invalid minBalance"); - (bool exists, uint256 prevMinBalance) = s_watchList.tryGet(target); - if (!exists) { - revert InvalidWatchList(); - } - s_watchList.set(target, minBalance); - return prevMinBalance; + function setTopUpAmount(address target, uint96 topUpAmount) external onlyOwner { + if (target == address(0)) revert InvalidAddress(target); + if (topUpAmount == 0) revert InvalidTopUpAmount(topUpAmount); + if (!s_targets[target].isActive) revert InvalidWatchList(); + uint256 oldTopUpAmount = s_targets[target].topUpAmount; + s_targets[target].topUpAmount = topUpAmount; + emit BalanceUpdated(target, oldTopUpAmount, topUpAmount); } /// @notice Update s_maxPerform - function setMaxPerform(uint16 maxPerform) external onlyOwner { - emit MaxPerformUpdated(s_maxPerform, maxPerform); + function setMaxPerform(uint16 maxPerform) public onlyOwner { s_maxPerform = maxPerform; + emit MaxPerformSet(s_maxPerform, maxPerform); } /// @notice Update s_maxCheck - function setMaxCheck(uint16 maxCheck) external onlyOwner { - emit MaxCheckUpdated(s_maxCheck, maxCheck); + function setMaxCheck(uint16 maxCheck) public onlyOwner { s_maxCheck = maxCheck; + emit MaxCheckSet(s_maxCheck, maxCheck); + } + + /// @notice Sets the minimum wait period (in seconds) for addresses between funding + function setMinWaitPeriodSeconds(uint256 minWaitPeriodSeconds) public onlyOwner { + s_minWaitPeriodSeconds = minWaitPeriodSeconds; + emit MinWaitPeriodSet(s_minWaitPeriodSeconds, minWaitPeriodSeconds); + } + + /// @notice Update s_upkeepInterval + function setUpkeepInterval(uint8 upkeepInterval) public onlyOwner { + if (upkeepInterval > 255) revert InvalidUpkeepInterval(upkeepInterval); + s_upkeepInterval = upkeepInterval; + emit UpkeepIntervalSet(s_upkeepInterval, upkeepInterval); } /// @notice Gets maxPerform @@ -259,31 +292,27 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp return s_maxCheck; } - /// @notice Gets the list of subscription ids being watched - function getWatchList() external view returns (address[] memory, uint256[] memory) { - uint256 len = s_watchList.length(); - address[] memory targets = new address[](len); - uint256[] memory minBalances = new uint256[](len); - - for (uint256 idx = 0; idx < len; idx++) { - (targets[idx], minBalances[idx]) = s_watchList.at(idx); - } + /// @notice Gets the minimum wait period + function getMinWaitPeriodSeconds() external view returns (uint256) { + return s_minWaitPeriodSeconds; + } - return (targets, minBalances); + /// @notice Gets upkeepInterval + function getUpkeepInterval() external view returns (uint8) { + return s_upkeepInterval; } - /// @notice Gets the configured top up amount - function getTopUpAmount() external view returns (uint256) { - return s_topUpAmount; + /// @notice Gets the list of subscription ids being watched + function getWatchList() external view returns (address[] memory) { + return s_watchList; } - /// @notice Gets the configured minimum balance for the given target - function getMinBalance(address target) external view returns (uint256) { - (bool exists, uint256 minBalance) = s_watchList.tryGet(target); - if (!exists) { - revert InvalidWatchList(); - } - return minBalance; + /// @notice Gets configuration information for an address on the watchlist + function getAccountInfo( + address targetAddress + ) external view returns (bool isActive, uint256 minBalance, uint256 topUpAmount) { + MonitoredAddress memory target = s_targets[targetAddress]; + return (target.isActive, target.minBalance, target.topUpAmount); } /// @notice Pause the contract, which prevents executing performUpkeep @@ -295,26 +324,4 @@ contract LinkAvailableBalanceMonitor is ConfirmedOwner, Pausable, AutomationComp function unpause() external onlyOwner { _unpause(); } - - /// @notice checks the target (could be direct target or IAggregatorProxy), and determines - /// if it is elligible for funding - /// @param targetAddress the target to check - /// @param minBalance minimum balance required for the target - /// @return bool whether the target needs funding or not - /// @return address the address of the contract needing funding - function _needsFunding(address targetAddress, uint256 minBalance) private view returns (bool, address) { - ILinkAvailable target; - IAggregatorProxy proxy = IAggregatorProxy(targetAddress); - try proxy.aggregator() returns (address aggregatorAddress) { - target = ILinkAvailable(aggregatorAddress); - } catch { - target = ILinkAvailable(targetAddress); - } - try target.linkAvailableForPayment() returns (int256 balance) { - if (balance < 0 || uint256(balance) < minBalance) { - return (true, address(target)); - } - } catch {} - return (false, address(0)); - } } diff --git a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts index af0063fb503..76a3dcfff1b 100644 --- a/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts +++ b/contracts/test/v0.8/automation/LinkAvailableBalanceMonitor.test.ts @@ -34,6 +34,7 @@ const PAUSED_ERR = 'Pausable: paused' const zeroLINK = ethers.utils.parseEther('0') const oneLINK = ethers.utils.parseEther('1') const twoLINK = ethers.utils.parseEther('2') +const fourLINK = ethers.utils.parseEther('4') const fiveLINK = ethers.utils.parseEther('5') const tenLINK = ethers.utils.parseEther('10') const oneHundredLINK = ethers.utils.parseEther('100') @@ -59,6 +60,7 @@ let directTarget2: MockContract let watchListAddresses: string[] let watchListMinBalances: BigNumber[] +let watchListTopUpAmounts: BigNumber[] async function assertContractLinkBalances( balance1: BigNumber, @@ -120,6 +122,7 @@ const setup = async () => { directTarget2.address, ] watchListMinBalances = [oneLINK, oneLINK, oneLINK, twoLINK, twoLINK] + watchListTopUpAmounts = [twoLINK, twoLINK, twoLINK, twoLINK, twoLINK] await proxy1.mock.aggregator.returns(aggregator1.address) await proxy2.mock.aggregator.returns(aggregator2.address) @@ -144,9 +147,17 @@ const setup = async () => { // New parameters needed by the constructor const maxPerform = 5 const maxCheck = 20 + const minWaitPeriodSeconds = 0 + const upkeepInterval = 10 lt = (await ltFactory.deploy()) as LinkToken - labm = await labmFactory.deploy(lt.address, twoLINK, maxPerform, maxCheck) + labm = await labmFactory.deploy( + lt.address, + minWaitPeriodSeconds, + maxPerform, + maxCheck, + upkeepInterval, + ) await labm.deployed() for (let i = 1; i <= 4; i++) { @@ -156,7 +167,11 @@ const setup = async () => { const setTx = await labm .connect(owner) - .setWatchList(watchListAddresses, watchListMinBalances) + .setWatchList( + watchListAddresses, + watchListMinBalances, + watchListTopUpAmounts, + ) await setTx.wait() } @@ -174,19 +189,27 @@ describe('LinkAvailableBalanceMonitor', () => { describe('setTopUpAmount()', () => { it('configures the top-up amount', async () => { - await labm.connect(owner).setTopUpAmount(100) - assert.equal((await labm.getTopUpAmount()).toNumber(), 100) + await labm + .connect(owner) + .setTopUpAmount(directTarget1.address, BigNumber.from(100)) + const report = await labm.getAccountInfo(directTarget1.address) + assert.equal(report.topUpAmount.toString(), '100') }) it('configuresis only callable by the owner', async () => { - await expect(labm.connect(stranger).setTopUpAmount(100)).to.be.reverted + await expect( + labm.connect(stranger).setTopUpAmount(directTarget1.address, 100), + ).to.be.reverted }) }) describe('setMinBalance()', () => { it('configures the min balance', async () => { - await labm.connect(owner).setMinBalance(proxy1.address, 100) - assert.equal((await labm.getMinBalance(proxy1.address)).toNumber(), 100) + await labm + .connect(owner) + .setMinBalance(proxy1.address, BigNumber.from(100)) + const report = await labm.getAccountInfo(proxy1.address) + assert.equal(report.minBalance.toString(), '100') }) it('reverts if address is not in the watchlist', async () => { @@ -266,66 +289,29 @@ describe('LinkAvailableBalanceMonitor', () => { beforeEach(async () => { // reset watchlist to empty before running these tests - await labm.connect(owner).setWatchList([], []) - let watchList = await labm.getWatchList() - assert.deepEqual(watchList, [[], []]) + await labm.connect(owner).setWatchList([], [], []) + const watchList = await labm.getWatchList() + assert.deepEqual(watchList, []) }) it('Should allow owner to adjust the watchlist', async () => { // add first watchlist let tx = await labm .connect(owner) - .setWatchList([watchAddress1], [oneLINK]) + .setWatchList([watchAddress1], [oneLINK], [oneLINK]) let watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], [watchAddress1]) - assert.deepEqual( - watchList[1].map((x) => x.toString()), - [oneLINK].map((x) => x.toString()), - ) + assert.deepEqual(watchList[0], watchAddress1) // add more to watchlist tx = await labm .connect(owner) .setWatchList( [watchAddress1, watchAddress2, watchAddress3], [oneLINK, oneLINK, oneLINK], + [oneLINK, oneLINK, oneLINK], ) await tx.wait() watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], [ - watchAddress1, - watchAddress2, - watchAddress3, - ]) - assert.deepEqual( - watchList[1].map((x) => x.toString()), - [oneLINK, oneLINK, oneLINK].map((x) => x.toString()), - ) - // remove some from watchlist - tx = await labm - .connect(owner) - .removeFromWatchlist([watchAddress3, watchAddress1]) - await tx.wait() - watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], [watchAddress2]) - assert.deepEqual( - watchList[1].map((x) => x.toString()), - [oneLINK].map((x) => x.toString()), - ) - // add some to watchlist - tx = await labm - .connect(owner) - .addToWatchList([watchAddress1, watchAddress3], [twoLINK, twoLINK]) - await tx.wait() - watchList = await labm.getWatchList() - assert.deepEqual(watchList[0], [ - watchAddress2, - watchAddress1, - watchAddress3, - ]) - assert.deepEqual( - watchList[1].map((x) => x.toString()), - [oneLINK, twoLINK, twoLINK].map((x) => x.toString()), - ) + assert.deepEqual(watchList, [watchAddress1, watchAddress2, watchAddress3]) }) it('Should not allow different length arrays in the watchlist', async () => { @@ -335,6 +321,7 @@ describe('LinkAvailableBalanceMonitor', () => { .setWatchList( [watchAddress1, watchAddress2, watchAddress1], [oneLINK, oneLINK], + [oneLINK, oneLINK], ) await expect(tx).to.be.revertedWith(errMsg) }) @@ -346,12 +333,6 @@ describe('LinkAvailableBalanceMonitor', () => { .setWatchList( [watchAddress1, watchAddress2, watchAddress1], [oneLINK, oneLINK, oneLINK], - ) - await expect(tx).to.be.revertedWith(errMsg) - tx = labm - .connect(owner) - .addToWatchList( - [watchAddress1, watchAddress2, watchAddress1], [oneLINK, oneLINK, oneLINK], ) await expect(tx).to.be.revertedWith(errMsg) @@ -360,14 +341,8 @@ describe('LinkAvailableBalanceMonitor', () => { it('Should not allow strangers to set the watchlist', async () => { const setTxStranger = labm .connect(stranger) - .setWatchList([watchAddress1], [oneLINK]) + .setWatchList([watchAddress1], [oneLINK], [oneLINK]) await expect(setTxStranger).to.be.revertedWith(OWNABLE_ERR) - const addTxStranger = labm - .connect(stranger) - .addToWatchList([watchAddress1], [oneLINK]) - await expect(addTxStranger).to.be.revertedWith(OWNABLE_ERR) - const removeTxStranger = labm.connect(stranger).removeFromWatchlist([]) - await expect(removeTxStranger).to.be.revertedWith(OWNABLE_ERR) }) it('Should revert if any of the addresses are empty', async () => { @@ -376,12 +351,6 @@ describe('LinkAvailableBalanceMonitor', () => { .setWatchList( [watchAddress1, ethers.constants.AddressZero], [oneLINK, oneLINK], - ) - await expect(tx).to.be.revertedWith(INVALID_WATCHLIST_ERR) - tx = labm - .connect(owner) - .addToWatchList( - [watchAddress1, ethers.constants.AddressZero], [oneLINK, oneLINK], ) await expect(tx).to.be.revertedWith(INVALID_WATCHLIST_ERR) @@ -390,69 +359,68 @@ describe('LinkAvailableBalanceMonitor', () => { describe('checkUpkeep() / sampleUnderfundedAddresses() [ @skip-coverage ]', () => { it('Should return list of address that are underfunded', async () => { - const fundTx = await lt.connect(owner).transfer( - labm.address, - tenLINK, // needs 10 total - ) + const fundTx = await lt + .connect(owner) + .transfer(labm.address, oneHundredLINK) await fundTx.wait() + + await labm.setWatchList( + watchListAddresses, + watchListMinBalances, + watchListTopUpAmounts, + ) + const [should, payload] = await labm.checkUpkeep('0x') assert.isTrue(should) let [addresses] = ethers.utils.defaultAbiCoder.decode( ['address[]'], payload, ) + expect(addresses).to.deep.equalInAnyOrder(watchListAddresses) - // checkUpkeep payload should match sampleUnderfundedAddresses() addresses = await labm.sampleUnderfundedAddresses() expect(addresses).to.deep.equalInAnyOrder(watchListAddresses) }) - it('Should return some results even if contract cannot fund all eligible targets', async () => { + it('Should omit aggregators that have sufficient funding', async () => { const fundTx = await lt.connect(owner).transfer( labm.address, - fiveLINK, // needs 2Link per contract, so can fund 2 max + oneHundredLINK, // enough for anything that needs funding ) await fundTx.wait() - const [should, payload] = await labm.checkUpkeep('0x') - assert.isTrue(should) - let [addresses] = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - payload, + + await labm.setWatchList( + [aggregator2.address, directTarget1.address, directTarget2.address], + [oneLINK, twoLINK, twoLINK], + [oneLINK, oneLINK, oneLINK], ) - assert.equal(addresses.length, 2) - assert.notEqual(addresses[0], addresses[1]) - assert(watchListAddresses.includes(addresses[0])) - assert(watchListAddresses.includes(addresses[1])) - // underfunded sample should still match list - addresses = await labm.sampleUnderfundedAddresses() - expect(addresses).to.deep.equalInAnyOrder(watchListAddresses) - }) - it('Should omit aggregators that have sufficient funding', async () => { + // all of them are underfunded, return 3 + await aggregator2.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget1.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK) + let addresses = await labm.sampleUnderfundedAddresses() - expect(addresses).to.deep.equalInAnyOrder(watchListAddresses) - await aggregator2.mock.linkAvailableForPayment.returns(oneLINK) // aggregator2 is enough funded - await directTarget1.mock.linkAvailableForPayment.returns(oneLINK) // directTarget1 is NOT enough funded - await directTarget2.mock.linkAvailableForPayment.returns(twoLINK) // directTarget2 is enough funded - addresses = await labm.sampleUnderfundedAddresses() expect(addresses).to.deep.equalInAnyOrder([ - proxy1.address, - proxy3.address, + aggregator2.address, directTarget1.address, + directTarget2.address, ]) - await aggregator1.mock.linkAvailableForPayment.returns(tenLINK) + await aggregator2.mock.linkAvailableForPayment.returns(oneLINK) // aggregator2 is enough funded + await directTarget1.mock.linkAvailableForPayment.returns(oneLINK) // directTarget1 is NOT enough funded + await directTarget2.mock.linkAvailableForPayment.returns(oneLINK) // directTarget2 is NOT funded addresses = await labm.sampleUnderfundedAddresses() expect(addresses).to.deep.equalInAnyOrder([ - proxy3.address, directTarget1.address, + directTarget2.address, ]) - await aggregator3.mock.linkAvailableForPayment.returns(tenLINK) + await directTarget1.mock.linkAvailableForPayment.returns(tenLINK) addresses = await labm.sampleUnderfundedAddresses() - expect(addresses).to.deep.equalInAnyOrder([directTarget1.address]) + expect(addresses).to.deep.equalInAnyOrder([directTarget2.address]) - await directTarget1.mock.linkAvailableForPayment.returns(tenLINK) + await directTarget2.mock.linkAvailableForPayment.returns(tenLINK) addresses = await labm.sampleUnderfundedAddresses() expect(addresses).to.deep.equalInAnyOrder([]) }) @@ -471,6 +439,7 @@ describe('LinkAvailableBalanceMonitor', () => { let MAX_CHECK: number let proxyAddresses: string[] let minBalances: BigNumber[] + let topUpAmount: BigNumber[] let aggregators: MockContract[] beforeEach(async () => { @@ -478,6 +447,7 @@ describe('LinkAvailableBalanceMonitor', () => { MAX_CHECK = await labm.getMaxCheck() proxyAddresses = [] minBalances = [] + topUpAmount = [] aggregators = [] const numAggregators = MAX_CHECK + 50 for (let idx = 0; idx < numAggregators; idx++) { @@ -493,18 +463,18 @@ describe('LinkAvailableBalanceMonitor', () => { await aggregator.mock.linkAvailableForPayment.returns(0) proxyAddresses.push(proxy.address) minBalances.push(oneLINK) + topUpAmount.push(oneLINK) aggregators.push(aggregator) } - await labm.setWatchList(proxyAddresses, minBalances) - expect(await labm.getWatchList()).to.deep.equalInAnyOrder([ - proxyAddresses, - minBalances, - ]) + await labm.setWatchList(proxyAddresses, minBalances, topUpAmount) + let watchlist = await labm.getWatchList() + expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses) + assert.equal(watchlist.length, minBalances.length) }) it('Should not include more than MAX_PERFORM addresses', async () => { const addresses = await labm.sampleUnderfundedAddresses() - assert.equal(addresses.length, MAX_PERFORM) + expect(addresses.length).to.be.lessThanOrEqual(MAX_PERFORM) }) it('Should sample from the list of addresses pseudorandomly', async () => { @@ -547,7 +517,11 @@ describe('LinkAvailableBalanceMonitor', () => { ) await labm .connect(owner) - .setWatchList(watchListAddresses, watchListMinBalances) + .setWatchList( + watchListAddresses, + watchListMinBalances, + watchListTopUpAmounts, + ) }) it('Should revert when paused', async () => { @@ -557,32 +531,38 @@ describe('LinkAvailableBalanceMonitor', () => { }) it('Should fund the appropriate addresses', async () => { - await lt.connect(owner).transfer(labm.address, tenLINK) - await assertContractLinkBalances( - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - zeroLINK, - ) + await aggregator1.mock.linkAvailableForPayment.returns(zeroLINK) + await aggregator2.mock.linkAvailableForPayment.returns(zeroLINK) + await aggregator3.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget1.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK) + + const fundTx = await lt.connect(owner).transfer(labm.address, tenLINK) + await fundTx.wait() + + h.assertLinkTokenBalance(lt, aggregator1.address, zeroLINK) + h.assertLinkTokenBalance(lt, aggregator2.address, zeroLINK) + h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK) + h.assertLinkTokenBalance(lt, directTarget1.address, zeroLINK) + h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK) + const performTx = await labm .connect(keeperRegistry) - .performUpkeep(validPayload, { gasLimit: 2_500_000 }) + .performUpkeep(validPayload, { gasLimit: 1_500_000 }) await performTx.wait() - await assertContractLinkBalances( - twoLINK, - twoLINK, - twoLINK, - twoLINK, - twoLINK, - ) + + h.assertLinkTokenBalance(lt, aggregator1.address, twoLINK) + h.assertLinkTokenBalance(lt, aggregator2.address, twoLINK) + h.assertLinkTokenBalance(lt, aggregator3.address, twoLINK) + h.assertLinkTokenBalance(lt, directTarget1.address, twoLINK) + h.assertLinkTokenBalance(lt, directTarget2.address, twoLINK) }) it('Can handle MAX_PERFORM proxies within gas limit', async () => { - // add MAX_PERFORM number of proxies const MAX_PERFORM = await labm.getMaxPerform() const proxyAddresses = [] const minBalances = [] + const topUpAmount = [] for (let idx = 0; idx < MAX_PERFORM; idx++) { const proxy = await deployMockContract( owner, @@ -596,20 +576,29 @@ describe('LinkAvailableBalanceMonitor', () => { await aggregator.mock.linkAvailableForPayment.returns(0) proxyAddresses.push(proxy.address) minBalances.push(oneLINK) + topUpAmount.push(oneLINK) } - await labm.setWatchList(proxyAddresses, minBalances) - expect(await labm.getWatchList()).to.deep.equalInAnyOrder([ - proxyAddresses, - minBalances, - ]) + await labm.setWatchList(proxyAddresses, minBalances, topUpAmount) + let watchlist = await labm.getWatchList() + expect(watchlist).to.deep.equalInAnyOrder(proxyAddresses) + assert.equal(watchlist.length, minBalances.length) + // add funds - const fundsNeeded = (await labm.getTopUpAmount()).mul(MAX_PERFORM) + const wl = await labm.getWatchList() + let fundsNeeded = BigNumber.from(0) + for (let idx = 0; idx < wl.length; idx++) { + const targetInfo = await labm.getAccountInfo(wl[idx]) + const targetTopUpAmount = targetInfo.topUpAmount + fundsNeeded.add(targetTopUpAmount) + } await lt.connect(owner).transfer(labm.address, fundsNeeded) + // encode payload const payload = ethers.utils.defaultAbiCoder.encode( ['address[]'], [proxyAddresses], ) + // do the thing await labm .connect(keeperRegistry) @@ -618,6 +607,11 @@ describe('LinkAvailableBalanceMonitor', () => { }) describe('topUp()', () => { + it('Should revert topUp address(0)', async () => { + const tx = await labm.connect(owner).topUp([ethers.constants.AddressZero]) + await expect(tx).to.emit(labm, 'TopUpBlocked') + }) + context('when not paused', () => { it('Should be callable by anyone', async () => { const users = [owner, keeperRegistry, stranger] @@ -654,13 +648,13 @@ describe('LinkAvailableBalanceMonitor', () => { it('Should fund the appropriate addresses', async () => { const tx = await labm.connect(keeperRegistry).topUp(watchListAddresses) - await assertContractLinkBalances( - twoLINK, - twoLINK, - twoLINK, - twoLINK, - twoLINK, - ) + + await aggregator1.mock.linkAvailableForPayment.returns(twoLINK) + await aggregator2.mock.linkAvailableForPayment.returns(twoLINK) + await aggregator3.mock.linkAvailableForPayment.returns(twoLINK) + await directTarget1.mock.linkAvailableForPayment.returns(twoLINK) + await directTarget2.mock.linkAvailableForPayment.returns(twoLINK) + await expect(tx) .to.emit(labm, 'TopUpSucceeded') .withArgs(proxy1.address) @@ -682,13 +676,12 @@ describe('LinkAvailableBalanceMonitor', () => { await labm .connect(keeperRegistry) .topUp([proxy1.address, directTarget1.address]) - await assertContractLinkBalances( - twoLINK, - zeroLINK, - zeroLINK, - twoLINK, - zeroLINK, - ) + + await aggregator1.mock.linkAvailableForPayment.returns(twoLINK) + await aggregator2.mock.linkAvailableForPayment.returns(zeroLINK) + await aggregator3.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget1.mock.linkAvailableForPayment.returns(twoLINK) + await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK) }) it('Should skip un-approved addresses', async () => { @@ -697,6 +690,7 @@ describe('LinkAvailableBalanceMonitor', () => { .setWatchList( [proxy1.address, directTarget1.address], [oneLINK, oneLINK], + [oneLINK, oneLINK], ) const tx = await labm .connect(keeperRegistry) @@ -707,13 +701,13 @@ describe('LinkAvailableBalanceMonitor', () => { directTarget1.address, directTarget2.address, ]) - await assertContractLinkBalances( - twoLINK, - zeroLINK, - zeroLINK, - twoLINK, - zeroLINK, - ) + + h.assertLinkTokenBalance(lt, aggregator1.address, twoLINK) + h.assertLinkTokenBalance(lt, aggregator2.address, zeroLINK) + h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK) + h.assertLinkTokenBalance(lt, directTarget1.address, twoLINK) + h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK) + await expect(tx) .to.emit(labm, 'TopUpSucceeded') .withArgs(proxy1.address) @@ -730,7 +724,11 @@ describe('LinkAvailableBalanceMonitor', () => { it('Should skip an address if the proxy is invalid and it is not a direct target', async () => { await labm .connect(owner) - .setWatchList([proxy1.address, proxy4.address], [oneLINK, oneLINK]) + .setWatchList( + [proxy1.address, proxy4.address], + [oneLINK, oneLINK], + [oneLINK, oneLINK], + ) const tx = await labm .connect(keeperRegistry) .topUp([proxy1.address, proxy4.address]) @@ -744,7 +742,11 @@ describe('LinkAvailableBalanceMonitor', () => { await proxy4.mock.aggregator.returns(aggregator4.address) await labm .connect(owner) - .setWatchList([proxy1.address, proxy4.address], [oneLINK, oneLINK]) + .setWatchList( + [proxy1.address, proxy4.address], + [oneLINK, oneLINK], + [oneLINK, oneLINK], + ) const tx = await labm .connect(keeperRegistry) .topUp([proxy1.address, proxy4.address]) @@ -759,7 +761,11 @@ describe('LinkAvailableBalanceMonitor', () => { await aggregator4.mock.linkAvailableForPayment.returns(tenLINK) await labm .connect(owner) - .setWatchList([proxy1.address, proxy4.address], [oneLINK, oneLINK]) + .setWatchList( + [proxy1.address, proxy4.address], + [oneLINK, oneLINK], + [oneLINK, oneLINK], + ) const tx = await labm .connect(keeperRegistry) .topUp([proxy1.address, proxy4.address]) @@ -776,6 +782,7 @@ describe('LinkAvailableBalanceMonitor', () => { .setWatchList( [proxy1.address, directTarget1.address], [oneLINK, oneLINK], + [oneLINK, oneLINK], ) const tx = await labm .connect(keeperRegistry) @@ -790,25 +797,26 @@ describe('LinkAvailableBalanceMonitor', () => { }) context('when partially funded', () => { - it('Should fund as many addresses as possible', async () => { + it('Should fund as many addresses as possible T', async () => { await lt.connect(owner).transfer( labm.address, - fiveLINK, // only enough LINK to fund 2 addresses + fourLINK, // only enough LINK to fund 2 addresses ) + + await aggregator1.mock.linkAvailableForPayment.returns(twoLINK) + await aggregator2.mock.linkAvailableForPayment.returns(twoLINK) + await aggregator3.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget1.mock.linkAvailableForPayment.returns(zeroLINK) + await directTarget2.mock.linkAvailableForPayment.returns(zeroLINK) + + h.assertLinkTokenBalance(lt, aggregator1.address, twoLINK) + h.assertLinkTokenBalance(lt, aggregator2.address, twoLINK) + h.assertLinkTokenBalance(lt, aggregator3.address, zeroLINK) + h.assertLinkTokenBalance(lt, directTarget1.address, zeroLINK) + h.assertLinkTokenBalance(lt, directTarget2.address, zeroLINK) + const tx = await labm.connect(keeperRegistry).topUp(watchListAddresses) - await assertContractLinkBalances( - twoLINK, - twoLINK, - zeroLINK, - zeroLINK, - zeroLINK, - ) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(proxy1.address) - await expect(tx) - .to.emit(labm, 'TopUpSucceeded') - .withArgs(proxy2.address) + await expect(tx).to.emit(labm, 'TopUpSucceeded') }) }) }) From cea3e6e037d77291ba392a69ba0dc7c6c9cc67c2 Mon Sep 17 00:00:00 2001 From: CL-Andrew <96407253+CL-Andrew@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:15:10 -0700 Subject: [PATCH 072/271] Optional and Configurable LDAP User/Session Management Support and Reworked Pluggable Auth Driver Interface (#9750) * Initial commit of LDAP Auth driver support with toml config docs and parser driver, pluggable auth interface defined and localauth (default) moved to scoped module * 'orm sessions.UserManager' to 'um sessions.UserManager' * Add missing checks for the UserApiTokenEnabled config field for token related calls, rename ServerTls to ServerTLS * Update docs toml LDAP section to clarify how the fields are used and specify LDAP terminology * Clarify LDAP 'cn' in toml docs for LDAP * Fix WebServer TOML and config definitions, split types for WebServerLDAPSecrets to following config and secret toml convention, improved error handling on startup for missing WebServer and LDAP fields * Error application startup if authentication method is not one of the valid options, instead of defaulting to local * Don't export unneeded ldapGroupMembersListToUser in ldap module * Bugfixes for LDAP find user when no results of passed email, address the two ways local CLI can attempt auth using local client for LDAP implementation of createSession, moved ErrNotSupported up to authentication types level so router can expose to API response when type message * Rework LDAP function to check if list of provided query emails possess the 'active' attribute/group in a single query. Now returns list of bools one to one for the passed in emails array and correctly handles the case for querying more than one email at a time, changing function return signature from just error * Update LDAP field naming for Cn, Dn to Go convention CN, DN in toml and types, tidy god mod, fix path imports for test files, fix toml comments * Post merge toml module rename LDAP model fixes Populate test config and test secrets toml file with new LDAP config fields, use secrets parse type for LDAPSecrets interface * Update top level application struct to accomodate new sibling AuthProvider field to preserve always available local admin auth This commit splits the newly added UserManager interface (now renamed) into two interfaces where the existing local user ORM auth provider covers the implementation for the required always available Admin commands. These are used when configuring the node initially (creating the admin user) or assuming the admin role from the command line, which should work locally as well regardless of the configured Authentication Provider. Renamed new UserManager interface to AuthenticationProvider, which no longer has the boilerplate Admin prefix functions. * Update all err comparison checks for ErrNotSupported to errors.Is * go generate mocks * Tidy unecessary string cast, bump ldap library to latest and use v3, test forward compatibility, mod tidy * Clean up auth provider config switch statement * Update checked in test txtar output * Update gql test and mocks with new authprovider mock, updated in mock struct. Add missing TestPassword call * Update remaining test config toml files with new WebServer LDAP fields, populated where test case makes sense, add toml config validation checks on parse for LDAP fields non empty, update LDAP Server field to Models Secret URL, add parsing test for secrets * Update LDAP module with missing API token implementation - creation, use, and deletion Bugfixes and logic improvements for LDAP session reaper/upstream sync. Reaper now correctly syncs roles and users from upstream via sleeper task tied to LDAP auth actions. User sessions and API tokens are correctly removed when the expire TTL is met, the local LDAP sessions and API tokens role is updated and synced with the state of the upstream LDAP server as part of the logic of this sleeper task, and if a user is no longer present in any of the defined groups they are automatically removed from the LDAP sessions and API tokens tables (checked on cadence of sleeper task Work call) Update LDAP webconfigToken duration to match wrapped models.Duration type Add const for LDAPUniqueMemberAttribute/uniqueMember in ldapauth module Add info logger connection attempt message in case of hang on node startup Fix expired LDAP api tokens purge issue Nicer error for session missing / expired on attempt of sesion token use (remove error within stdout) * Add missing support for local CLI user and auth when using LDAP Authentication module Fixed edge cases for FindUser, check local users table as well, local user API Token creation and support Add localauth_user flag for ldap specific tables to support node usage by the initial required local admin user, add logic in CreateSession * Update config UpstreamSyncInterval and UpstreamSyncRateLimit functionality for LDAP Sync daemon Implement .Work call on timer for LDAP sync in the background, independent of Auth related calls. The implementation of SleeperTask calls Work when hooked into Auth events, being called on login or logout. Now if UpstreamSyncInterval is defined as non 0, a background timer call will call the sync function, respecting the new UpstreamSyncRateLimit field * LDAP Fix for checks of optional isactive property on group query, find user functionality, and admin functions Bug fix for ldap driver not supporting local admin users case of change password and list users, FindUser functionality can now return matches of local admin users List Users now includes local users and works as expected for upstream LDAP users who have any of the defined groups required for node access. Bugfix for group search query in both the sync and ldap providers modules. Factored out group query functionality for both call sites Set Password support for only local admin users as functionality is still supported and required when using LDAP auth, upstream user modification remains unsupported Bugfix for shell local initialization not using local admin auth ORM, causing issue with initial assume user step in ListUsers * bump migration file index * remove incorrect rebased merge resolution for Explorer removal * Change default config definitions for LDAP 'Is Active' attribute checks to empty, as not all LDAP providers will use 'ActiveAttribute', or retain group member access when inactive. Fix error handling in find users for case when NoRows, dont log error automatically with Transaction middleware * Simplify sessions purge sql exec using pq.Array instead of manually generating placeholders, and Regenerate mocks * Merge go mod require groups, gotidy * Rename changed authentication provider session ORM in test files, fix config test reordering, add missing mock, update const err strings * Add mock value for one test case of config ldap is active attribute, revert purge sessions api token test file, migrate to new errors module and update how errors are wrapped, lowercase all error messages * Factor out unsupported action error message in user controller with new errUnsupportedForAuth type * Rebase, update migration index * Update config_test full case, error case for missing fields * Fix tests with missing Mocks for cmd shell, config resolver, and sessions localauth Missing mocks for LocalAdminUsersORM Revert change unrelated to LDAP feature in AuthorizedUserWithSession (refactored) Fix leaked internal error over HTTP response + test case for delete user Fix mocks and missing TestPassword call cases for graphql mutation tests, update incorrect password test case Add expected LDAP config fields for config resolver tests * Bump migration file index for ldap tables * Linter fixes - application.go localAdminUsersORM in initialized one line Fix config sesion timeout interface naming (r -> l) Typos fix in ldap.go docstring Rework logic in checkErr for FindUser logic of testing admin table query before failing (rework to avoid error shadowing) Invert logic for err != nil in case for local admin user found Fix missing errors.Is comparison for sessions.ErrUserSessionExpired in ldap module Run docs generate Fix typo in txtar test LDAP config * Add missing ldap fields to warnings.txtar, fix err shadowing linter errors in ldap.go and sync.go Run go mod tidy * Fix linter import order and groupings * More import ordering lint * Rebase, bump sql migraiton index * use correct guregu/null.v4 version * Implement test cases for ldap module, create LDAP client and LDAPConn wrapper interfaces and mocks New LDAPClient and LDAPConn interfaces allow test mocks to handle Bind and Search functionality. the ldap implementation has been updated to store the ldapClient (still ephemeral single use, like a factory) in the struct such that the test harness can swap the implementation with the mocks. Create helpers_test.go following codebase convention to allow a Setter method to be defined for the ldapClient field, but separated from the production build. This allows the ldap struct and field properties to remain unexported. Test helper contains test mock configand helper constructor function New ldap_test.go module with cases for ldap query functionality and local admin support assertions ldap.go module improvements, return struct in constructor instead of interface type for authentication provider, define user facing error consts (test assertion), store ldapClient in struct, nicer error handling for user not found in FindUser, fix err shadowing reuse error in token expired case, fix typos in ListUsers LDAP Sync rework for new ldapclient field, use new interface to support mocking Remove dangling commited localauth orm.mock, which was being imported by a missed test. Test now imports the correct mock (new authentication provider mocks) * Updated CHANGELOG.md * Update go.mod * Linter fixes UserNoLDAPGroups -> ErrUserNoLDAPGroups, shadowing * module -> package, format package docstring properly for ldapauth to support godoc render Remove redundant LDAP prefix for UniqueMemberAttribute * Remove rebased gomod line * define const NodeAdmins* for mocked tests in helpers_test, reference [WebServer].AuthenticationMethod in changelog * Add missing returns in sync Work call when failed to establish LDAP connection as it is required for the sync functionality, flip return flow in TestPassword for admin fallback * Updating naming and address nits LocalAdminUsersORM -> BasicAdminUsersORM, regenerate mocks Save indent in WebServer ValidateConfig when ldapauth Update comments and rename CreateEphemeralClient -> CreateEphemeralConnection * Add missed go generate Application change for BasicAdminUsersORM rename --- core/cmd/admin_commands_test.go | 6 +- core/cmd/app_test.go | 1 + core/cmd/shell.go | 12 +- core/cmd/shell_local.go | 7 +- core/cmd/shell_local_test.go | 11 +- core/cmd/shell_remote_test.go | 16 +- core/cmd/shell_test.go | 13 +- core/config/docs/core.toml | 40 + core/config/docs/secrets.toml | 9 + core/config/toml/types.go | 146 +++ core/config/web_config.go | 25 + core/internal/cltest/cltest.go | 2 +- core/internal/cltest/mocks.go | 4 +- core/internal/features/features_test.go | 2 +- core/internal/mocks/application.go | 48 +- core/scripts/go.mod | 3 + core/scripts/go.sum | 15 + core/services/chainlink/application.go | 49 +- core/services/chainlink/config.go | 24 +- core/services/chainlink/config_general.go | 2 +- .../services/chainlink/config_general_test.go | 10 + core/services/chainlink/config_test.go | 51 +- core/services/chainlink/config_web_server.go | 145 +++ .../testdata/config-empty-effective.toml | 20 + .../chainlink/testdata/config-full.toml | 20 + .../chainlink/testdata/config-invalid.toml | 22 + .../config-multi-chain-effective.toml | 20 + .../secrets-webserver-ldap.toml | 4 + .../testdata/secrets-full-redacted.toml | 6 + .../chainlink/testdata/secrets-full.toml | 6 + core/sessions/authentication.go | 66 ++ core/sessions/ldapauth/client.go | 47 + core/sessions/ldapauth/helpers_test.go | 131 +++ core/sessions/ldapauth/ldap.go | 858 ++++++++++++++++++ core/sessions/ldapauth/ldap_test.go | 639 +++++++++++++ core/sessions/ldapauth/mocks/ldap_client.go | 53 ++ core/sessions/ldapauth/mocks/ldap_conn.go | 82 ++ core/sessions/ldapauth/sync.go | 343 +++++++ core/sessions/{ => localauth}/orm.go | 111 +-- core/sessions/{ => localauth}/orm_test.go | 10 +- core/sessions/{ => localauth}/reaper.go | 2 +- core/sessions/{ => localauth}/reaper_test.go | 40 +- .../{orm.go => authentication_provider.go} | 62 +- core/sessions/mocks/basic_admin_users_orm.go | 91 ++ core/sessions/session.go | 74 ++ core/sessions/user.go | 64 -- core/sessions/webauthn.go | 4 +- .../0208_create_ldap_sessions_table.sql | 22 + core/web/auth/auth.go | 5 +- core/web/auth/auth_test.go | 4 +- core/web/auth/gql_test.go | 4 +- core/web/resolver/api_token_test.go | 64 +- core/web/resolver/mutation.go | 20 +- core/web/resolver/resolver_test.go | 6 +- .../testdata/config-empty-effective.toml | 20 + core/web/resolver/testdata/config-full.toml | 20 + .../config-multi-chain-effective.toml | 20 + core/web/resolver/user_test.go | 26 +- core/web/router.go | 12 +- core/web/sessions_controller.go | 6 +- core/web/sessions_controller_test.go | 18 +- core/web/user_controller.go | 76 +- core/web/user_controller_test.go | 4 +- core/web/webauthn_controller.go | 6 +- docs/CHANGELOG.md | 5 + docs/CONFIG.md | 133 +++ docs/SECRETS.md | 27 + go.mod | 3 + go.sum | 15 + integration-tests/go.mod | 3 + integration-tests/go.sum | 9 + testdata/scripts/node/validate/default.txtar | 20 + .../disk-based-logging-disabled.txtar | 20 + .../validate/disk-based-logging-no-dir.txtar | 20 + .../node/validate/disk-based-logging.txtar | 20 + testdata/scripts/node/validate/invalid.txtar | 20 + testdata/scripts/node/validate/valid.txtar | 20 + testdata/scripts/node/validate/warnings.txtar | 20 + 78 files changed, 3743 insertions(+), 341 deletions(-) create mode 100644 core/services/chainlink/testdata/mergingsecretsdata/secrets-webserver-ldap.toml create mode 100644 core/sessions/authentication.go create mode 100644 core/sessions/ldapauth/client.go create mode 100644 core/sessions/ldapauth/helpers_test.go create mode 100644 core/sessions/ldapauth/ldap.go create mode 100644 core/sessions/ldapauth/ldap_test.go create mode 100644 core/sessions/ldapauth/mocks/ldap_client.go create mode 100644 core/sessions/ldapauth/mocks/ldap_conn.go create mode 100644 core/sessions/ldapauth/sync.go rename core/sessions/{ => localauth}/orm.go (80%) rename core/sessions/{ => localauth}/orm_test.go (95%) rename core/sessions/{ => localauth}/reaper.go (98%) rename core/sessions/{ => localauth}/reaper_test.go (69%) rename core/sessions/mocks/{orm.go => authentication_provider.go} (75%) create mode 100644 core/sessions/mocks/basic_admin_users_orm.go create mode 100644 core/sessions/session.go create mode 100644 core/store/migrate/migrations/0208_create_ldap_sessions_table.sql diff --git a/core/cmd/admin_commands_test.go b/core/cmd/admin_commands_test.go index a5512fdddaa..954e3577d3d 100644 --- a/core/cmd/admin_commands_test.go +++ b/core/cmd/admin_commands_test.go @@ -62,7 +62,7 @@ func TestShell_ChangeRole(t *testing.T) { app := startNewApplicationV2(t, nil) client, _ := app.NewShellAndRenderer() user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) tests := []struct { name string @@ -101,7 +101,7 @@ func TestShell_DeleteUser(t *testing.T) { app := startNewApplicationV2(t, nil) client, _ := app.NewShellAndRenderer() user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.BasicAdminUsersORM().CreateUser(&user)) tests := []struct { name string @@ -135,7 +135,7 @@ func TestShell_ListUsers(t *testing.T) { app := startNewApplicationV2(t, nil) client, _ := app.NewShellAndRenderer() user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) set := flag.NewFlagSet("test", 0) cltest.FlagSetApplyFromAction(client.ListUsers, set, "") diff --git a/core/cmd/app_test.go b/core/cmd/app_test.go index bbb00bff3ec..e5e29406426 100644 --- a/core/cmd/app_test.go +++ b/core/cmd/app_test.go @@ -151,6 +151,7 @@ func Test_initServerConfig(t *testing.T) { "../services/chainlink/testdata/mergingsecretsdata/secrets-mercury-split-one.toml", "../services/chainlink/testdata/mergingsecretsdata/secrets-mercury-split-two.toml", "../services/chainlink/testdata/mergingsecretsdata/secrets-threshold.toml", + "../services/chainlink/testdata/mergingsecretsdata/secrets-webserver-ldap.toml", }, }, wantErr: false, diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 308ebf8da8c..80ecd2590b0 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -776,8 +776,8 @@ func (f *fileSessionRequestBuilder) Build(file string) (sessions.SessionRequest, // APIInitializer is the interface used to create the API User credentials // needed to access the API. Does nothing if API user already exists. type APIInitializer interface { - // Initialize creates a new user for API access, or does nothing if one exists. - Initialize(orm sessions.ORM, lggr logger.Logger) (sessions.User, error) + // Initialize creates a new local Admin user for API access, or does nothing if one exists. + Initialize(orm sessions.BasicAdminUsersORM, lggr logger.Logger) (sessions.User, error) } type promptingAPIInitializer struct { @@ -791,11 +791,11 @@ func NewPromptingAPIInitializer(prompter Prompter) APIInitializer { } // Initialize uses the terminal to get credentials that it then saves in the store. -func (t *promptingAPIInitializer) Initialize(orm sessions.ORM, lggr logger.Logger) (sessions.User, error) { +func (t *promptingAPIInitializer) Initialize(orm sessions.BasicAdminUsersORM, lggr logger.Logger) (sessions.User, error) { // Load list of users to determine which to assume, or if a user needs to be created dbUsers, err := orm.ListUsers() if err != nil { - return sessions.User{}, err + return sessions.User{}, errors.Wrap(err, "Unable to List users for initialization") } // If there are no users in the database, prompt for initial admin user creation @@ -845,7 +845,7 @@ func NewFileAPIInitializer(file string) APIInitializer { return fileAPIInitializer{file: file} } -func (f fileAPIInitializer) Initialize(orm sessions.ORM, lggr logger.Logger) (sessions.User, error) { +func (f fileAPIInitializer) Initialize(orm sessions.BasicAdminUsersORM, lggr logger.Logger) (sessions.User, error) { request, err := credentialsFromFile(f.file, lggr) if err != nil { return sessions.User{}, err @@ -854,7 +854,7 @@ func (f fileAPIInitializer) Initialize(orm sessions.ORM, lggr logger.Logger) (se // Load list of users to determine which to assume, or if a user needs to be created dbUsers, err := orm.ListUsers() if err != nil { - return sessions.User{}, err + return sessions.User{}, errors.Wrap(err, "Unable to List users for initialization") } // If there are no users in the database, create initial admin user from session request from file creds diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 401375238d8..dea9a29359e 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -362,7 +362,8 @@ func (s *Shell) runNode(c *cli.Context) error { return s.errorOut(errors.Wrap(err, "fatal error instantiating application")) } - sessionORM := app.SessionORM() + // Local shell initialization always uses local auth users table for admin auth + authProviderORM := app.BasicAdminUsersORM() keyStore := app.GetKeyStore() err = s.KeyStoreAuthenticator.authenticate(keyStore, s.Config.Password()) if err != nil { @@ -449,11 +450,11 @@ func (s *Shell) runNode(c *cli.Context) error { } var user sessions.User - if user, err = NewFileAPIInitializer(c.String("api")).Initialize(sessionORM, lggr); err != nil { + if user, err = NewFileAPIInitializer(c.String("api")).Initialize(authProviderORM, lggr); err != nil { if !errors.Is(err, ErrNoCredentialFile) { return errors.Wrap(err, "error creating api initializer") } - if user, err = s.FallbackAPIInitializer.Initialize(sessionORM, lggr); err != nil { + if user, err = s.FallbackAPIInitializer.Initialize(authProviderORM, lggr); err != nil { if errors.Is(err, ErrorNoAPICredentialsAvailable) { return errors.WithStack(err) } diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 89b8704f87b..df60e16423e 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -25,7 +25,7 @@ import ( chainlinkmocks "github.com/smartcontractkit/chainlink/v2/core/services/chainlink/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" evmrelayer "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" - "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -79,7 +79,7 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { }) db := pgtest.NewSqlxDB(t) keyStore := cltest.NewKeyStore(t, db, cfg.Database()) - sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg.Database(), audit.NoopLogger) + authProviderORM := localauth.NewORM(db, time.Minute, logger.TestLogger(t), cfg.Database(), audit.NoopLogger) lggr := logger.TestLogger(t) @@ -100,7 +100,8 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { pgtest.MustExec(t, db, "DELETE FROM users;") app := mocks.NewApplication(t) - app.On("SessionORM").Return(sessionORM).Maybe() + app.On("AuthenticationProvider").Return(authProviderORM).Maybe() + app.On("BasicAdminUsersORM").Return(authProviderORM).Maybe() app.On("GetKeyStore").Return(keyStore).Maybe() app.On("GetRelayers").Return(testRelayers).Maybe() app.On("Start", mock.Anything).Maybe().Return(nil) @@ -171,7 +172,7 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { c.Insecure.OCRDevelopmentMode = nil }) db := pgtest.NewSqlxDB(t) - sessionORM := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg.Database(), audit.NoopLogger) + authProviderORM := localauth.NewORM(db, time.Minute, logger.TestLogger(t), cfg.Database(), audit.NoopLogger) // Clear out fixture users/users created from the other test cases // This asserts that on initial run with an empty users table that the credentials file will instantiate and @@ -199,7 +200,7 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { } testRelayers := genTestEVMRelayers(t, opts, keyStore) app := mocks.NewApplication(t) - app.On("SessionORM").Return(sessionORM) + app.On("BasicAdminUsersORM").Return(authProviderORM) app.On("GetKeyStore").Return(keyStore) app.On("GetRelayers").Return(testRelayers).Maybe() app.On("Start", mock.Anything).Maybe().Return(nil) diff --git a/core/cmd/shell_remote_test.go b/core/cmd/shell_remote_test.go index 7f998225f63..91b56ee53a4 100644 --- a/core/cmd/shell_remote_test.go +++ b/core/cmd/shell_remote_test.go @@ -258,7 +258,7 @@ func TestShell_DestroyExternalInitiator_NotFound(t *testing.T) { func TestShell_RemoteLogin(t *testing.T) { app := startNewApplicationV2(t, nil) - orm := app.SessionORM() + orm := app.AuthenticationProvider() u := cltest.NewUserWithSession(t, orm) @@ -301,7 +301,7 @@ func TestShell_RemoteBuildCompatibility(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, nil) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) enteredStrings := []string{u.Email, cltest.Password} prompter := &cltest.MockCountingPrompter{T: t, EnteredStrings: append(enteredStrings, enteredStrings...)} client := app.NewAuthenticatingShell(prompter) @@ -340,7 +340,7 @@ func TestShell_CheckRemoteBuildCompatibility(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, nil) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) tests := []struct { name string remoteVersion, remoteSha string @@ -416,7 +416,7 @@ func TestShell_ChangePassword(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, nil) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) enteredStrings := []string{u.Email, cltest.Password} prompter := &cltest.MockCountingPrompter{T: t, EnteredStrings: enteredStrings} @@ -466,7 +466,7 @@ func TestShell_Profile_InvalidSecondsParam(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, nil) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) enteredStrings := []string{u.Email, cltest.Password} prompter := &cltest.MockCountingPrompter{T: t, EnteredStrings: enteredStrings} @@ -497,7 +497,7 @@ func TestShell_Profile(t *testing.T) { t.Parallel() app := startNewApplicationV2(t, nil) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) enteredStrings := []string{u.Email, cltest.Password} prompter := &cltest.MockCountingPrompter{T: t, EnteredStrings: enteredStrings} @@ -648,7 +648,7 @@ func TestShell_AutoLogin(t *testing.T) { app := startNewApplicationV2(t, nil) user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.BasicAdminUsersORM().CreateUser(&user)) sr := sessions.SessionRequest{ Email: user.Email, @@ -676,7 +676,7 @@ func TestShell_AutoLogin_AuthFails(t *testing.T) { app := startNewApplicationV2(t, nil) user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.BasicAdminUsersORM().CreateUser(&user)) sr := sessions.SessionRequest{ Email: user.Email, diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index 9b87e8fb1da..2a8c2c55861 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -26,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -33,7 +34,7 @@ func TestTerminalCookieAuthenticator_AuthenticateWithoutSession(t *testing.T) { t.Parallel() app := cltest.NewApplicationEVMDisabled(t) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) tests := []struct { name, email, pwd string @@ -65,7 +66,7 @@ func TestTerminalCookieAuthenticator_AuthenticateWithSession(t *testing.T) { app := cltest.NewApplicationEVMDisabled(t) require.NoError(t, app.Start(testutils.Context(t))) - u := cltest.NewUserWithSession(t, app.SessionORM()) + u := cltest.NewUserWithSession(t, app.AuthenticationProvider()) tests := []struct { name, email, pwd string @@ -155,7 +156,7 @@ func TestTerminalAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) lggr := logger.TestLogger(t) - orm := sessions.NewORM(db, time.Minute, lggr, pgtest.NewQConfig(true), audit.NoopLogger) + orm := localauth.NewORM(db, time.Minute, lggr, pgtest.NewQConfig(true), audit.NoopLogger) mock := &cltest.MockCountingPrompter{T: t, EnteredStrings: test.enteredStrings, NotTerminal: !test.isTerminal} tai := cmd.NewPromptingAPIInitializer(mock) @@ -186,7 +187,7 @@ func TestTerminalAPIInitializer_InitializeWithExistingAPIUser(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) lggr := logger.TestLogger(t) - orm := sessions.NewORM(db, time.Minute, lggr, cfg.Database(), audit.NoopLogger) + orm := localauth.NewORM(db, time.Minute, lggr, cfg.Database(), audit.NoopLogger) // Clear out fixture users/users created from the other test cases // This asserts that on initial run with an empty users table that the credentials file will instantiate and @@ -223,7 +224,7 @@ func TestFileAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) lggr := logger.TestLogger(t) - orm := sessions.NewORM(db, time.Minute, lggr, pgtest.NewQConfig(true), audit.NoopLogger) + orm := localauth.NewORM(db, time.Minute, lggr, pgtest.NewQConfig(true), audit.NoopLogger) // Clear out fixture users/users created from the other test cases // This asserts that on initial run with an empty users table that the credentials file will instantiate and @@ -248,7 +249,7 @@ func TestFileAPIInitializer_InitializeWithoutAPIUser(t *testing.T) { func TestFileAPIInitializer_InitializeWithExistingAPIUser(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), cfg.Database(), audit.NoopLogger) + orm := localauth.NewORM(db, time.Minute, logger.TestLogger(t), cfg.Database(), audit.NoopLogger) tests := []struct { name string diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 1ca4c656a7f..0a8e6aba3be 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -161,6 +161,8 @@ MaxAgeDays = 0 # Default MaxBackups = 1 # Default [WebServer] +# AuthenticationMethod defines which pluggable auth interface to use for user login and role assumption. Options include 'local' and 'ldap'. See docs for more details +AuthenticationMethod = 'local' # Default # AllowOrigins controls the URLs Chainlink nodes emit in the `Allow-Origins` header of its API responses. The setting can be a comma-separated list with no spaces. You might experience CORS issues if this is not set correctly. # # You should set this to the external URL that you use to access the Chainlink UI. @@ -191,6 +193,44 @@ StartTimeout = '15s' # Default # ListenIP specifies the IP to bind the HTTP server to ListenIP = '0.0.0.0' # Default +# Optional LDAP config if WebServer.AuthenticationMethod is set to 'ldap' +# LDAP queries are all parameterized to support custom LDAP 'dn', 'cn', and attributes +[WebServer.LDAP] +# ServerTLS defines the option to require the secure ldaps +ServerTLS = true # Default +# SessionTimeout determines the amount of idle time to elapse before session cookies expire. This signs out GUI users from their sessions. +SessionTimeout = '15m0s' # Default +# QueryTimeout defines how long queries should wait before timing out, defined in seconds +QueryTimeout = '2m0s' # Default +# BaseUserAttr defines the base attribute used to populate LDAP queries such as "uid=$", default is example +BaseUserAttr = 'uid' # Default +# BaseDN defines the base LDAP 'dn' search filter to apply to every LDAP query, replace example,com with the appropriate LDAP server's structure +BaseDN = 'dc=custom,dc=example,dc=com' # Example +# UsersDN defines the 'dn' query to use when querying for the 'users' 'ou' group +UsersDN = 'ou=users' # Default +# GroupsDN defines the 'dn' query to use when querying for the 'groups' 'ou' group +GroupsDN = 'ou=groups' # Default +# ActiveAttribute is an optional user field to check truthiness for if a user is valid/active. This is only required if the LDAP provider lists inactive users as members of groups +ActiveAttribute = '' # Default +# ActiveAttributeAllowedValue is the value to check against for the above optional user attribute +ActiveAttributeAllowedValue = '' # Default +# AdminUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Admin' role +AdminUserGroupCN = 'NodeAdmins' # Default +# EditUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Edit' role +EditUserGroupCN = 'NodeEditors' # Default +# RunUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Run' role +RunUserGroupCN = 'NodeRunners' # Default +# ReadUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Read' role +ReadUserGroupCN = 'NodeReadOnly' # Default +# UserApiTokenEnabled enables the users to issue API tokens with the same access of their role +UserApiTokenEnabled = false # Default +# UserAPITokenDuration is the duration of time an API token is active for before expiring +UserAPITokenDuration = '240h0m0s' # Default +# UpstreamSyncInterval is the interval at which the background LDAP sync task will be called. A '0s' value disables the background sync being run on an interval. This check is already performed during login/logout actions, all sessions and API tokens stored in the local ldap tables are updated to match the remote server +UpstreamSyncInterval = '0s' # Default +# UpstreamSyncRateLimit defines a duration to limit the number of query/API calls to the upstream LDAP provider. It prevents the sync functionality from being called multiple times within the defined duration +UpstreamSyncRateLimit = '2m0s' # Default + [WebServer.RateLimit] # Authenticated defines the threshold to which authenticated requests get limited. More than this many authenticated requests per `AuthenticatedRateLimitPeriod` will be rejected. Authenticated = 1000 # Default diff --git a/core/config/docs/secrets.toml b/core/config/docs/secrets.toml index 2b491a77497..4ed2325dfb2 100644 --- a/core/config/docs/secrets.toml +++ b/core/config/docs/secrets.toml @@ -14,6 +14,15 @@ BackupURL = "postgresql://user:pass@read-replica.example.com:5432/dbname?sslmode # Environment variable: `CL_DATABASE_ALLOW_SIMPLE_PASSWORDS` AllowSimplePasswords = false # Default +# Optional LDAP config +[WebServer.LDAP] +# ServerAddress is the full ldaps:// address of the ldap server to authenticate with and query +ServerAddress = 'ldaps://127.0.0.1' # Example +# ReadOnlyUserLogin is the username of the read only root user used to authenticate the requested LDAP queries +ReadOnlyUserLogin = 'viewer@example.com' # Example +# ReadOnlyUserPass is the password for the above account +ReadOnlyUserPass = 'password' # Example + [Password] # Keystore is the password for the node's account. # diff --git a/core/config/toml/types.go b/core/config/toml/types.go index b7c8cfbc473..61962d43e5f 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/config/parse" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" + "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -101,6 +102,7 @@ func (c *Core) ValidateConfig() (err error) { type Secrets struct { Database DatabaseSecrets `toml:",omitempty"` Password Passwords `toml:",omitempty"` + WebServer WebServerSecrets `toml:",omitempty"` Pyroscope PyroscopeSecrets `toml:",omitempty"` Prometheus PrometheusSecrets `toml:",omitempty"` Mercury MercurySecrets `toml:",omitempty"` @@ -592,6 +594,7 @@ func (l *LogFile) setFrom(f *LogFile) { } type WebServer struct { + AuthenticationMethod *string AllowOrigins *string BridgeResponseURL *models.URL BridgeCacheTTL *models.Duration @@ -604,12 +607,16 @@ type WebServer struct { StartTimeout *models.Duration ListenIP *net.IP + LDAP WebServerLDAP `toml:",omitempty"` MFA WebServerMFA `toml:",omitempty"` RateLimit WebServerRateLimit `toml:",omitempty"` TLS WebServerTLS `toml:",omitempty"` } func (w *WebServer) setFrom(f *WebServer) { + if v := f.AuthenticationMethod; v != nil { + w.AuthenticationMethod = v + } if v := f.AllowOrigins; v != nil { w.AllowOrigins = v } @@ -644,11 +651,46 @@ func (w *WebServer) setFrom(f *WebServer) { w.HTTPMaxSize = v } + w.LDAP.setFrom(&f.LDAP) w.MFA.setFrom(&f.MFA) w.RateLimit.setFrom(&f.RateLimit) w.TLS.setFrom(&f.TLS) } +func (w *WebServer) ValidateConfig() (err error) { + // Validate LDAP fields when authentication method is LDAPAuth + if *w.AuthenticationMethod != string(sessions.LDAPAuth) { + return + } + + // Assert LDAP fields when AuthMethod set to LDAP + if *w.LDAP.BaseDN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.BaseDN", Msg: "LDAP BaseDN can not be empty"}) + } + if *w.LDAP.BaseUserAttr == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.BaseUserAttr", Msg: "LDAP BaseUserAttr can not be empty"}) + } + if *w.LDAP.UsersDN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.UsersDN", Msg: "LDAP UsersDN can not be empty"}) + } + if *w.LDAP.GroupsDN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.GroupsDN", Msg: "LDAP GroupsDN can not be empty"}) + } + if *w.LDAP.AdminUserGroupCN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.AdminUserGroupCN", Msg: "LDAP AdminUserGroupCN can not be empty"}) + } + if *w.LDAP.EditUserGroupCN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.RunUserGroupCN", Msg: "LDAP ReadUserGroupCN can not be empty"}) + } + if *w.LDAP.RunUserGroupCN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.RunUserGroupCN", Msg: "LDAP RunUserGroupCN can not be empty"}) + } + if *w.LDAP.ReadUserGroupCN == "" { + err = multierr.Append(err, configutils.ErrInvalid{Name: "LDAP.ReadUserGroupCN", Msg: "LDAP ReadUserGroupCN can not be empty"}) + } + return err +} + type WebServerMFA struct { RPID *string RPOrigin *string @@ -715,6 +757,110 @@ func (w *WebServerTLS) setFrom(f *WebServerTLS) { } } +type WebServerLDAP struct { + ServerTLS *bool + SessionTimeout *models.Duration + QueryTimeout *models.Duration + BaseUserAttr *string + BaseDN *string + UsersDN *string + GroupsDN *string + ActiveAttribute *string + ActiveAttributeAllowedValue *string + AdminUserGroupCN *string + EditUserGroupCN *string + RunUserGroupCN *string + ReadUserGroupCN *string + UserApiTokenEnabled *bool + UserAPITokenDuration *models.Duration + UpstreamSyncInterval *models.Duration + UpstreamSyncRateLimit *models.Duration +} + +func (w *WebServerLDAP) setFrom(f *WebServerLDAP) { + if v := f.ServerTLS; v != nil { + w.ServerTLS = v + } + if v := f.SessionTimeout; v != nil { + w.SessionTimeout = v + } + if v := f.SessionTimeout; v != nil { + w.SessionTimeout = v + } + if v := f.QueryTimeout; v != nil { + w.QueryTimeout = v + } + if v := f.BaseUserAttr; v != nil { + w.BaseUserAttr = v + } + if v := f.BaseDN; v != nil { + w.BaseDN = v + } + if v := f.UsersDN; v != nil { + w.UsersDN = v + } + if v := f.GroupsDN; v != nil { + w.GroupsDN = v + } + if v := f.ActiveAttribute; v != nil { + w.ActiveAttribute = v + } + if v := f.ActiveAttributeAllowedValue; v != nil { + w.ActiveAttributeAllowedValue = v + } + if v := f.AdminUserGroupCN; v != nil { + w.AdminUserGroupCN = v + } + if v := f.EditUserGroupCN; v != nil { + w.EditUserGroupCN = v + } + if v := f.RunUserGroupCN; v != nil { + w.RunUserGroupCN = v + } + if v := f.ReadUserGroupCN; v != nil { + w.ReadUserGroupCN = v + } + if v := f.UserApiTokenEnabled; v != nil { + w.UserApiTokenEnabled = v + } + if v := f.UserAPITokenDuration; v != nil { + w.UserAPITokenDuration = v + } + if v := f.UpstreamSyncInterval; v != nil { + w.UpstreamSyncInterval = v + } + if v := f.UpstreamSyncRateLimit; v != nil { + w.UpstreamSyncRateLimit = v + } +} + +type WebServerLDAPSecrets struct { + ServerAddress *models.SecretURL + ReadOnlyUserLogin *models.Secret + ReadOnlyUserPass *models.Secret +} + +func (w *WebServerLDAPSecrets) setFrom(f *WebServerLDAPSecrets) { + if v := f.ServerAddress; v != nil { + w.ServerAddress = v + } + if v := f.ReadOnlyUserLogin; v != nil { + w.ReadOnlyUserLogin = v + } + if v := f.ReadOnlyUserPass; v != nil { + w.ReadOnlyUserPass = v + } +} + +type WebServerSecrets struct { + LDAP WebServerLDAPSecrets `toml:",omitempty"` +} + +func (w *WebServerSecrets) SetFrom(f *WebServerSecrets) error { + w.LDAP.setFrom(&f.LDAP) + return nil +} + type JobPipeline struct { ExternalInitiatorsEnabled *bool MaxRunDuration *models.Duration diff --git a/core/config/web_config.go b/core/config/web_config.go index 12209a02670..429a31e7e82 100644 --- a/core/config/web_config.go +++ b/core/config/web_config.go @@ -32,7 +32,31 @@ type MFA interface { RPOrigin() string } +type LDAP interface { + ServerAddress() string + ReadOnlyUserLogin() string + ReadOnlyUserPass() string + ServerTLS() bool + SessionTimeout() models.Duration + QueryTimeout() time.Duration + BaseUserAttr() string + BaseDN() string + UsersDN() string + GroupsDN() string + ActiveAttribute() string + ActiveAttributeAllowedValue() string + AdminUserGroupCN() string + EditUserGroupCN() string + RunUserGroupCN() string + ReadUserGroupCN() string + UserApiTokenEnabled() bool + UserAPITokenDuration() models.Duration + UpstreamSyncInterval() models.Duration + UpstreamSyncRateLimit() models.Duration +} + type WebServer interface { + AuthenticationMethod() string AllowOrigins() string BridgeCacheTTL() time.Duration BridgeResponseURL() *url.URL @@ -49,4 +73,5 @@ type WebServer interface { TLS() TLS RateLimit() RateLimit MFA() MFA + LDAP() LDAP } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index fb4a69cf30c..66162aef102 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -628,7 +628,7 @@ func (ta *TestApplication) NewHTTPClient(user *User) HTTPClientCleaner { u, err := clsessions.NewUser(user.Email, Password, user.Role) require.NoError(ta.t, err) - err = ta.SessionORM().CreateUser(&u) + err = ta.BasicAdminUsersORM().CreateUser(&u) require.NoError(ta.t, err) sessionID := ta.MustSeedNewSession(user.Email) diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index 9fdbcbb373d..00f72199dd9 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -309,7 +309,7 @@ func MustRandomUser(t testing.TB) sessions.User { return r } -func NewUserWithSession(t testing.TB, orm sessions.ORM) sessions.User { +func NewUserWithSession(t testing.TB, orm sessions.AuthenticationProvider) sessions.User { u := MustRandomUser(t) require.NoError(t, orm.CreateUser(&u)) @@ -330,7 +330,7 @@ func NewMockAPIInitializer(t testing.TB) *MockAPIInitializer { return &MockAPIInitializer{t: t} } -func (m *MockAPIInitializer) Initialize(orm sessions.ORM, lggr logger.Logger) (sessions.User, error) { +func (m *MockAPIInitializer) Initialize(orm sessions.BasicAdminUsersORM, lggr logger.Logger) (sessions.User, error) { if user, err := orm.FindUser(APIEmailAdmin); err == nil { return user, err } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 3293066191f..23451bf29fe 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -266,7 +266,7 @@ func TestIntegration_AuthToken(t *testing.T) { mockUser := cltest.MustRandomUser(t) key, secret := uuid.New().String(), uuid.New().String() apiToken := auth.Token{AccessKey: key, Secret: secret} - orm := app.SessionORM() + orm := app.AuthenticationProvider() require.NoError(t, orm.CreateUser(&mockUser)) require.NoError(t, orm.SetAuthToken(&mockUser, &apiToken)) diff --git a/core/internal/mocks/application.go b/core/internal/mocks/application.go index ec656509afd..7853361db93 100644 --- a/core/internal/mocks/application.go +++ b/core/internal/mocks/application.go @@ -63,6 +63,38 @@ func (_m *Application) AddJobV2(ctx context.Context, _a1 *job.Job) error { return r0 } +// AuthenticationProvider provides a mock function with given fields: +func (_m *Application) AuthenticationProvider() sessions.AuthenticationProvider { + ret := _m.Called() + + var r0 sessions.AuthenticationProvider + if rf, ok := ret.Get(0).(func() sessions.AuthenticationProvider); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(sessions.AuthenticationProvider) + } + } + + return r0 +} + +// BasicAdminUsersORM provides a mock function with given fields: +func (_m *Application) BasicAdminUsersORM() sessions.BasicAdminUsersORM { + ret := _m.Called() + + var r0 sessions.BasicAdminUsersORM + if rf, ok := ret.Get(0).(func() sessions.BasicAdminUsersORM); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(sessions.BasicAdminUsersORM) + } + } + + return r0 +} + // BridgeORM provides a mock function with given fields: func (_m *Application) BridgeORM() bridges.ORM { ret := _m.Called() @@ -439,22 +471,6 @@ func (_m *Application) SecretGenerator() chainlink.SecretGenerator { return r0 } -// SessionORM provides a mock function with given fields: -func (_m *Application) SessionORM() sessions.ORM { - ret := _m.Called() - - var r0 sessions.ORM - if rf, ok := ret.Get(0).(func() sessions.ORM); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(sessions.ORM) - } - } - - return r0 -} - // SetLogLevel provides a mock function with given fields: lvl func (_m *Application) SetLogLevel(lvl zapcore.Level) error { ret := _m.Called(lvl) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 65dcec563e5..17c2cff1039 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -44,6 +44,7 @@ require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect @@ -119,8 +120,10 @@ require ( github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect + github.com/go-ldap/ldap/v3 v3.4.5 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 781eed46ceb..ae1f924c0f7 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -79,6 +79,8 @@ github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOv github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -124,6 +126,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -424,6 +428,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -438,6 +444,8 @@ github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEai github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= +github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -1745,6 +1753,7 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1785,6 +1794,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1846,6 +1856,7 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1872,6 +1883,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1975,6 +1987,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1987,6 +2000,7 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2063,6 +2077,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 63a9b2696cf..3285acdc07a 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -52,6 +52,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/sessions/ldapauth" + "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -82,7 +84,8 @@ type Application interface { EVMORM() evmtypes.Configs PipelineORM() pipeline.ORM BridgeORM() bridges.ORM - SessionORM() sessions.ORM + BasicAdminUsersORM() sessions.BasicAdminUsersORM + AuthenticationProvider() sessions.AuthenticationProvider TxmStorageService() txmgr.EvmTxStore AddJobV2(ctx context.Context, job *job.Job) error DeleteJob(ctx context.Context, jobID int32) error @@ -115,7 +118,8 @@ type ChainlinkApplication struct { pipelineORM pipeline.ORM pipelineRunner pipeline.Runner bridgeORM bridges.ORM - sessionORM sessions.ORM + localAdminUsersORM sessions.BasicAdminUsersORM + authenticationProvider sessions.AuthenticationProvider txmStorageService txmgr.EvmTxStore FeedsService feeds.Service webhookJobRunner webhook.JobRunner @@ -245,10 +249,36 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, fmt.Errorf("no evm chains found") } + // Initialize Local Users ORM and Authentication Provider specified in config + // BasicAdminUsersORM is initialized and required regardless of separate Authentication Provider + localAdminUsersORM := localauth.NewORM(db, cfg.WebServer().SessionTimeout().Duration(), globalLogger, cfg.Database(), auditLogger) + + // Initialize Sessions ORM based on environment configured authenticator + // localDB auth or remote LDAP auth + authMethod := cfg.WebServer().AuthenticationMethod() + var authenticationProvider sessions.AuthenticationProvider + var sessionReaper utils.SleeperTask + + switch sessions.AuthenticationProviderName(authMethod) { + case sessions.LDAPAuth: + var err error + authenticationProvider, err = ldapauth.NewLDAPAuthenticator( + db, cfg.Database(), cfg.WebServer().LDAP(), cfg.Insecure().DevWebServer(), globalLogger, auditLogger, + ) + if err != nil { + return nil, errors.Wrap(err, "NewApplication: failed to initialize LDAP Authentication module") + } + sessionReaper = ldapauth.NewLDAPServerStateSync(db, cfg.Database(), cfg.WebServer().LDAP(), globalLogger) + case sessions.LocalAuth: + authenticationProvider = localauth.NewORM(db, cfg.WebServer().SessionTimeout().Duration(), globalLogger, cfg.Database(), auditLogger) + sessionReaper = localauth.NewSessionReaper(db.DB, cfg.WebServer(), globalLogger) + default: + return nil, errors.Errorf("NewApplication: Unexpected 'AuthenticationMethod': %s supported values: %s, %s", authMethod, sessions.LocalAuth, sessions.LDAPAuth) + } + var ( pipelineORM = pipeline.NewORM(db, globalLogger, cfg.Database(), cfg.JobPipeline().MaxSuccessfulRuns()) bridgeORM = bridges.NewORM(db, globalLogger, cfg.Database()) - sessionORM = sessions.NewORM(db, cfg.WebServer().SessionTimeout().Duration(), globalLogger, cfg.Database(), auditLogger) mercuryORM = mercury.NewORM(db, globalLogger, cfg.Database()) pipelineRunner = pipeline.NewRunner(pipelineORM, bridgeORM, cfg.JobPipeline(), cfg.WebServer(), legacyEVMChains, keyStore.Eth(), keyStore.VRF(), globalLogger, restrictedHTTPClient, unrestrictedHTTPClient) jobORM = job.NewORM(db, pipelineORM, bridgeORM, keyStore, globalLogger, cfg.Database()) @@ -440,13 +470,14 @@ func NewApplication(opts ApplicationOpts) (Application, error) { pipelineRunner: pipelineRunner, pipelineORM: pipelineORM, bridgeORM: bridgeORM, - sessionORM: sessionORM, + localAdminUsersORM: localAdminUsersORM, + authenticationProvider: authenticationProvider, txmStorageService: txmORM, FeedsService: feedsService, Config: cfg, webhookJobRunner: webhookJobRunner, KeyStore: keyStore, - SessionReaper: sessions.NewSessionReaper(db.DB, cfg.WebServer(), globalLogger), + SessionReaper: sessionReaper, ExternalInitiatorManager: externalInitiatorManager, HealthChecker: healthChecker, Nurse: nurse, @@ -612,8 +643,12 @@ func (app *ChainlinkApplication) BridgeORM() bridges.ORM { return app.bridgeORM } -func (app *ChainlinkApplication) SessionORM() sessions.ORM { - return app.sessionORM +func (app *ChainlinkApplication) BasicAdminUsersORM() sessions.BasicAdminUsersORM { + return app.localAdminUsersORM +} + +func (app *ChainlinkApplication) AuthenticationProvider() sessions.AuthenticationProvider { + return app.authenticationProvider } // TODO BCF-2516 remove this all together remove EVM specifics diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 3f55a2dc00f..10598718f97 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -168,28 +168,32 @@ type Secrets struct { } func (s *Secrets) SetFrom(f *Secrets) (err error) { - if err1 := s.Database.SetFrom(&f.Database); err1 != nil { - err = multierr.Append(err, config.NamedMultiErrorList(err1, "Database")) + if err2 := s.Database.SetFrom(&f.Database); err2 != nil { + err = multierr.Append(err, config.NamedMultiErrorList(err2, "Database")) } if err2 := s.Password.SetFrom(&f.Password); err2 != nil { err = multierr.Append(err, config.NamedMultiErrorList(err2, "Password")) } - if err3 := s.Pyroscope.SetFrom(&f.Pyroscope); err3 != nil { - err = multierr.Append(err, config.NamedMultiErrorList(err3, "Pyroscope")) + if err2 := s.WebServer.SetFrom(&f.WebServer); err2 != nil { + err = multierr.Append(err, config.NamedMultiErrorList(err2, "WebServer")) } - if err4 := s.Prometheus.SetFrom(&f.Prometheus); err4 != nil { - err = multierr.Append(err, config.NamedMultiErrorList(err4, "Prometheus")) + if err2 := s.Pyroscope.SetFrom(&f.Pyroscope); err2 != nil { + err = multierr.Append(err, config.NamedMultiErrorList(err2, "Pyroscope")) } - if err5 := s.Mercury.SetFrom(&f.Mercury); err5 != nil { - err = multierr.Append(err, config.NamedMultiErrorList(err5, "Mercury")) + if err2 := s.Prometheus.SetFrom(&f.Prometheus); err2 != nil { + err = multierr.Append(err, config.NamedMultiErrorList(err2, "Prometheus")) } - if err6 := s.Threshold.SetFrom(&f.Threshold); err6 != nil { - err = multierr.Append(err, config.NamedMultiErrorList(err6, "Threshold")) + if err2 := s.Mercury.SetFrom(&f.Mercury); err2 != nil { + err = multierr.Append(err, config.NamedMultiErrorList(err2, "Mercury")) + } + + if err2 := s.Threshold.SetFrom(&f.Threshold); err2 != nil { + err = multierr.Append(err, config.NamedMultiErrorList(err2, "Threshold")) } _, err = utils.MultiErrorList(err) diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 81e38833359..6a835e09c89 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -348,7 +348,7 @@ func (g *generalConfig) StarkNetEnabled() bool { } func (g *generalConfig) WebServer() config.WebServer { - return &webServerConfig{c: g.c.WebServer, rootDir: g.RootDir} + return &webServerConfig{c: g.c.WebServer, s: g.secrets.WebServer, rootDir: g.RootDir} } func (g *generalConfig) AutoPprofBlockProfileRate() int { diff --git a/core/services/chainlink/config_general_test.go b/core/services/chainlink/config_general_test.go index 46931e53e2b..c122f8f968c 100644 --- a/core/services/chainlink/config_general_test.go +++ b/core/services/chainlink/config_general_test.go @@ -149,6 +149,9 @@ var mercurySecretsTOMLSplitTwo string //go:embed testdata/mergingsecretsdata/secrets-threshold.toml var thresholdSecretsTOML string +//go:embed testdata/mergingsecretsdata/secrets-webserver-ldap.toml +var WebServerLDAPSecretsTOML string + func TestConfig_SecretsMerging(t *testing.T) { t.Run("verify secrets merging in GeneralConfigOpts.New()", func(t *testing.T) { databaseSecrets, err := parseSecrets(databaseSecretsTOML) @@ -165,6 +168,8 @@ func TestConfig_SecretsMerging(t *testing.T) { require.NoErrorf(t, err6, "error: %s", err6) thresholdSecrets, err7 := parseSecrets(thresholdSecretsTOML) require.NoErrorf(t, err7, "error: %s", err7) + webserverLDAPSecrets, err8 := parseSecrets(WebServerLDAPSecretsTOML) + require.NoErrorf(t, err8, "error: %s", err8) opts := new(GeneralConfigOpts) configFiles := []string{ @@ -178,6 +183,7 @@ func TestConfig_SecretsMerging(t *testing.T) { "testdata/mergingsecretsdata/secrets-mercury-split-one.toml", "testdata/mergingsecretsdata/secrets-mercury-split-two.toml", "testdata/mergingsecretsdata/secrets-threshold.toml", + "testdata/mergingsecretsdata/secrets-webserver-ldap.toml", } err = opts.Setup(configFiles, secretsFiles) require.NoErrorf(t, err, "error: %s", err) @@ -194,6 +200,10 @@ func TestConfig_SecretsMerging(t *testing.T) { assert.Equal(t, (string)(*prometheusSecrets.Prometheus.AuthToken), (string)(*opts.Secrets.Prometheus.AuthToken)) assert.Equal(t, (string)(*thresholdSecrets.Threshold.ThresholdKeyShare), (string)(*opts.Secrets.Threshold.ThresholdKeyShare)) + assert.Equal(t, webserverLDAPSecrets.WebServer.LDAP.ServerAddress.URL().String(), opts.Secrets.WebServer.LDAP.ServerAddress.URL().String()) + assert.Equal(t, webserverLDAPSecrets.WebServer.LDAP.ReadOnlyUserLogin, opts.Secrets.WebServer.LDAP.ReadOnlyUserLogin) + assert.Equal(t, webserverLDAPSecrets.WebServer.LDAP.ReadOnlyUserPass, opts.Secrets.WebServer.LDAP.ReadOnlyUserPass) + err = assertDeepEqualityMercurySecrets(*merge(mercurySecrets_a.Mercury, mercurySecrets_b.Mercury), opts.Secrets.Mercury) require.NoErrorf(t, err, "merged mercury secrets unequal") }) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 986b98d9367..96e6db42c80 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -308,6 +308,7 @@ func TestConfig_Marshal(t *testing.T) { }, } full.WebServer = toml.WebServer{ + AuthenticationMethod: ptr("local"), AllowOrigins: ptr("*"), BridgeResponseURL: mustURL("https://bridge.response"), BridgeCacheTTL: models.MustNewDuration(10 * time.Second), @@ -323,6 +324,25 @@ func TestConfig_Marshal(t *testing.T) { RPID: ptr("test-rpid"), RPOrigin: ptr("test-rp-origin"), }, + LDAP: toml.WebServerLDAP{ + ServerTLS: ptr(true), + SessionTimeout: models.MustNewDuration(15 * time.Minute), + QueryTimeout: models.MustNewDuration(2 * time.Minute), + BaseUserAttr: ptr("uid"), + BaseDN: ptr("dc=custom,dc=example,dc=com"), + UsersDN: ptr("ou=users"), + GroupsDN: ptr("ou=groups"), + ActiveAttribute: ptr("organizationalStatus"), + ActiveAttributeAllowedValue: ptr("ACTIVE"), + AdminUserGroupCN: ptr("NodeAdmins"), + EditUserGroupCN: ptr("NodeEditors"), + RunUserGroupCN: ptr("NodeRunners"), + ReadUserGroupCN: ptr("NodeReadOnly"), + UserApiTokenEnabled: ptr(false), + UserAPITokenDuration: models.MustNewDuration(240 * time.Hour), + UpstreamSyncInterval: models.MustNewDuration(0 * time.Second), + UpstreamSyncRateLimit: models.MustNewDuration(2 * time.Minute), + }, RateLimit: toml.WebServerRateLimit{ Authenticated: ptr[int64](42), AuthenticatedPeriod: models.MustNewDuration(time.Second), @@ -738,6 +758,7 @@ MaxAgeDays = 17 MaxBackups = 9 `}, {"WebServer", Config{Core: toml.Core{WebServer: full.WebServer}}, `[WebServer] +AuthenticationMethod = 'local' AllowOrigins = '*' BridgeResponseURL = 'https://bridge.response' BridgeCacheTTL = '10s' @@ -750,6 +771,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '192.158.1.37' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = 'dc=custom,dc=example,dc=com' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = 'organizationalStatus' +ActiveAttributeAllowedValue = 'ACTIVE' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = 'test-rpid' RPOrigin = 'test-rp-origin' @@ -1118,8 +1158,17 @@ func TestConfig_Validate(t *testing.T) { toml string exp string }{ - {name: "invalid", toml: invalidTOML, exp: `invalid configuration: 5 errors: + {name: "invalid", toml: invalidTOML, exp: `invalid configuration: 6 errors: - Database.Lock.LeaseRefreshInterval: invalid value (6s): must be less than or equal to half of LeaseDuration (10s) + - WebServer: 8 errors: + - LDAP.BaseDN: invalid value (): LDAP BaseDN can not be empty + - LDAP.BaseUserAttr: invalid value (): LDAP BaseUserAttr can not be empty + - LDAP.UsersDN: invalid value (): LDAP UsersDN can not be empty + - LDAP.GroupsDN: invalid value (): LDAP GroupsDN can not be empty + - LDAP.AdminUserGroupCN: invalid value (): LDAP AdminUserGroupCN can not be empty + - LDAP.RunUserGroupCN: invalid value (): LDAP ReadUserGroupCN can not be empty + - LDAP.RunUserGroupCN: invalid value (): LDAP RunUserGroupCN can not be empty + - LDAP.ReadUserGroupCN: invalid value (): LDAP ReadUserGroupCN can not be empty - EVM: 8 errors: - 1.ChainID: invalid value (1): duplicate - must be unique - 0.Nodes.1.Name: invalid value (foo): duplicate - must be unique diff --git a/core/services/chainlink/config_web_server.go b/core/services/chainlink/config_web_server.go index a931d67f386..06db398e2ea 100644 --- a/core/services/chainlink/config_web_server.go +++ b/core/services/chainlink/config_web_server.go @@ -98,6 +98,7 @@ func (m *mfaConfig) RPOrigin() string { type webServerConfig struct { c toml.WebServer + s toml.WebServerSecrets rootDir func() string } @@ -113,6 +114,14 @@ func (w *webServerConfig) MFA() config.MFA { return &mfaConfig{c: w.c.MFA} } +func (w *webServerConfig) LDAP() config.LDAP { + return &ldapConfig{c: w.c.LDAP, s: w.s.LDAP} +} + +func (w *webServerConfig) AuthenticationMethod() string { + return *w.c.AuthenticationMethod +} + func (w *webServerConfig) AllowOrigins() string { return *w.c.AllowOrigins } @@ -168,3 +177,139 @@ func (w *webServerConfig) SessionTimeout() models.Duration { func (w *webServerConfig) ListenIP() net.IP { return *w.c.ListenIP } + +type ldapConfig struct { + c toml.WebServerLDAP + s toml.WebServerLDAPSecrets +} + +func (l *ldapConfig) ServerAddress() string { + if l.s.ServerAddress == nil { + return "" + } + return l.s.ServerAddress.URL().String() +} + +func (l *ldapConfig) ReadOnlyUserLogin() string { + if l.s.ReadOnlyUserLogin == nil { + return "" + } + return string(*l.s.ReadOnlyUserLogin) +} + +func (l *ldapConfig) ReadOnlyUserPass() string { + if l.s.ReadOnlyUserPass == nil { + return "" + } + return string(*l.s.ReadOnlyUserPass) +} + +func (l *ldapConfig) ServerTLS() bool { + if l.c.ServerTLS == nil { + return false + } + return *l.c.ServerTLS +} + +func (l *ldapConfig) SessionTimeout() models.Duration { + return *l.c.SessionTimeout +} + +func (l *ldapConfig) QueryTimeout() time.Duration { + return l.c.QueryTimeout.Duration() +} + +func (l *ldapConfig) UserAPITokenDuration() models.Duration { + return *l.c.UserAPITokenDuration +} + +func (l *ldapConfig) BaseUserAttr() string { + if l.c.BaseUserAttr == nil { + return "" + } + return *l.c.BaseUserAttr +} + +func (l *ldapConfig) BaseDN() string { + if l.c.BaseDN == nil { + return "" + } + return *l.c.BaseDN +} + +func (l *ldapConfig) UsersDN() string { + if l.c.UsersDN == nil { + return "" + } + return *l.c.UsersDN +} + +func (l *ldapConfig) GroupsDN() string { + if l.c.GroupsDN == nil { + return "" + } + return *l.c.GroupsDN +} + +func (l *ldapConfig) ActiveAttribute() string { + if l.c.ActiveAttribute == nil { + return "" + } + return *l.c.ActiveAttribute +} + +func (l *ldapConfig) ActiveAttributeAllowedValue() string { + if l.c.ActiveAttributeAllowedValue == nil { + return "" + } + return *l.c.ActiveAttributeAllowedValue +} + +func (l *ldapConfig) AdminUserGroupCN() string { + if l.c.AdminUserGroupCN == nil { + return "" + } + return *l.c.AdminUserGroupCN +} + +func (l *ldapConfig) EditUserGroupCN() string { + if l.c.EditUserGroupCN == nil { + return "" + } + return *l.c.EditUserGroupCN +} + +func (l *ldapConfig) RunUserGroupCN() string { + if l.c.RunUserGroupCN == nil { + return "" + } + return *l.c.RunUserGroupCN +} + +func (l *ldapConfig) ReadUserGroupCN() string { + if l.c.ReadUserGroupCN == nil { + return "" + } + return *l.c.ReadUserGroupCN +} + +func (l *ldapConfig) UserApiTokenEnabled() bool { + if l.c.UserApiTokenEnabled == nil { + return false + } + return *l.c.UserApiTokenEnabled +} + +func (l *ldapConfig) UpstreamSyncInterval() models.Duration { + if l.c.UpstreamSyncInterval == nil { + return models.Duration{} + } + return *l.c.UpstreamSyncInterval +} + +func (l *ldapConfig) UpstreamSyncRateLimit() models.Duration { + if l.c.UpstreamSyncRateLimit == nil { + return models.Duration{} + } + return *l.c.UpstreamSyncRateLimit +} diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index 48d432138a8..f5d775fe744 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -61,6 +61,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -73,6 +74,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 7ce0d185b1c..5ede10ef695 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -67,6 +67,7 @@ MaxAgeDays = 17 MaxBackups = 9 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = '*' BridgeResponseURL = 'https://bridge.response' BridgeCacheTTL = '10s' @@ -79,6 +80,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '192.158.1.37' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = 'dc=custom,dc=example,dc=com' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = 'organizationalStatus' +ActiveAttributeAllowedValue = 'ACTIVE' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = 'test-rpid' RPOrigin = 'test-rp-origin' diff --git a/core/services/chainlink/testdata/config-invalid.toml b/core/services/chainlink/testdata/config-invalid.toml index 3b7e89299f6..4d8c9bc29a9 100644 --- a/core/services/chainlink/testdata/config-invalid.toml +++ b/core/services/chainlink/testdata/config-invalid.toml @@ -2,6 +2,28 @@ LeaseRefreshInterval='6s' LeaseDuration='10s' +[WebServer] +AuthenticationMethod = 'ldap' + +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = '' +BaseDN = '' +UsersDN = '' +GroupsDN = '' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = '' +EditUserGroupCN = '' +RunUserGroupCN = '' +ReadUserGroupCN = '' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [[EVM]] ChainID = '1' Transactions.MaxInFlight= 10 diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 1dcbfe3a830..9dd0be8f5d2 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -61,6 +61,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -73,6 +74,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/core/services/chainlink/testdata/mergingsecretsdata/secrets-webserver-ldap.toml b/core/services/chainlink/testdata/mergingsecretsdata/secrets-webserver-ldap.toml new file mode 100644 index 00000000000..f73efcff0cc --- /dev/null +++ b/core/services/chainlink/testdata/mergingsecretsdata/secrets-webserver-ldap.toml @@ -0,0 +1,4 @@ +[WebServer.LDAP] +ServerAddress = 'ldaps://127.0.0.1' +ReadOnlyUserLogin = 'viewer@example.com' +ReadOnlyUserPass = 'password' \ No newline at end of file diff --git a/core/services/chainlink/testdata/secrets-full-redacted.toml b/core/services/chainlink/testdata/secrets-full-redacted.toml index 740c3250edb..9d91d79cb51 100644 --- a/core/services/chainlink/testdata/secrets-full-redacted.toml +++ b/core/services/chainlink/testdata/secrets-full-redacted.toml @@ -7,6 +7,12 @@ AllowSimplePasswords = false Keystore = 'xxxxx' VRF = 'xxxxx' +[WebServer] +[WebServer.LDAP] +ServerAddress = 'xxxxx' +ReadOnlyUserLogin = 'xxxxx' +ReadOnlyUserPass = 'xxxxx' + [Pyroscope] AuthToken = 'xxxxx' diff --git a/core/services/chainlink/testdata/secrets-full.toml b/core/services/chainlink/testdata/secrets-full.toml index 37e5dafc7d7..37a3e2e7dc2 100644 --- a/core/services/chainlink/testdata/secrets-full.toml +++ b/core/services/chainlink/testdata/secrets-full.toml @@ -6,6 +6,12 @@ BackupURL = "postgresql://user:pass@localhost:5432/backupdbname?sslmode=disable" Keystore = "keystore_pass" VRF = "VRF_pass" +[WebServer] +[WebServer.LDAP] +ServerAddress = 'ldaps://127.0.0.1' +ReadOnlyUserLogin = 'viewer@example.com' +ReadOnlyUserPass = 'password' + [Pyroscope] AuthToken = "pyroscope-token" diff --git a/core/sessions/authentication.go b/core/sessions/authentication.go new file mode 100644 index 00000000000..0f0dda3bf33 --- /dev/null +++ b/core/sessions/authentication.go @@ -0,0 +1,66 @@ +package sessions + +import ( + "errors" + "fmt" + + "github.com/smartcontractkit/chainlink/v2/core/auth" + "github.com/smartcontractkit/chainlink/v2/core/bridges" +) + +// Application config constant options +type AuthenticationProviderName string + +const ( + LocalAuth AuthenticationProviderName = "local" + LDAPAuth AuthenticationProviderName = "ldap" +) + +// ErrUserSessionExpired defines the error triggered when the user session has expired +var ErrUserSessionExpired = errors.New("session missing or expired, please login again") + +// ErrNotSupported defines the error where interface functionality doesn't align with the underlying Auth Provider +var ErrNotSupported = fmt.Errorf("functionality not supported with current authentication provider: %w", errors.ErrUnsupported) + +// ErrEmptySessionID captures the empty case error message +var ErrEmptySessionID = errors.New("session ID cannot be empty") + +//go:generate mockery --quiet --name BasicAdminUsersORM --output ./mocks/ --case=underscore + +// BasicAdminUsersORM is the interface that defines the functionality required for supporting basic admin functionality +// adjacent to the identity provider authentication provider implementation. It is currently implemented by the local +// users/sessions ORM containing local admin CLI actions. This is separate from the AuthenticationProvider, +// as local admin management (ie initial core node setup, initial admin user creation), is always +// required no matter what the pluggable AuthenticationProvider implementation is. +type BasicAdminUsersORM interface { + ListUsers() ([]User, error) + CreateUser(user *User) error + FindUser(email string) (User, error) +} + +//go:generate mockery --quiet --name AuthenticationProvider --output ./mocks/ --case=underscore + +// AuthenticationProvider is an interface that abstracts the required application calls to a user management backend +// Currently localauth (users table DB) or LDAP server (readonly) +type AuthenticationProvider interface { + FindUser(email string) (User, error) + FindUserByAPIToken(apiToken string) (User, error) + ListUsers() ([]User, error) + AuthorizedUserWithSession(sessionID string) (User, error) + DeleteUser(email string) error + DeleteUserSession(sessionID string) error + CreateSession(sr SessionRequest) (string, error) + ClearNonCurrentSessions(sessionID string) error + CreateUser(user *User) error + UpdateRole(email, newRole string) (User, error) + SetAuthToken(user *User, token *auth.Token) error + CreateAndSetAuthToken(user *User) (*auth.Token, error) + DeleteAuthToken(user *User) error + SetPassword(user *User, newPassword string) error + TestPassword(email, password string) error + Sessions(offset, limit int) ([]Session, error) + GetUserWebAuthn(email string) ([]WebAuthn, error) + SaveWebAuthn(token *WebAuthn) error + + FindExternalInitiator(eia *auth.Token) (initiator *bridges.ExternalInitiator, err error) +} diff --git a/core/sessions/ldapauth/client.go b/core/sessions/ldapauth/client.go new file mode 100644 index 00000000000..bb259f8c9a2 --- /dev/null +++ b/core/sessions/ldapauth/client.go @@ -0,0 +1,47 @@ +package ldapauth + +import ( + "fmt" + + "github.com/go-ldap/ldap/v3" + + "github.com/smartcontractkit/chainlink/v2/core/config" +) + +type ldapClient struct { + config config.LDAP +} + +//go:generate mockery --quiet --name LDAPClient --output ./mocks/ --case=underscore + +// Wrapper for creating a handle to a *ldap.Conn/LDAPConn interface +type LDAPClient interface { + CreateEphemeralConnection() (LDAPConn, error) +} + +//go:generate mockery --quiet --name LDAPConn --output ./mocks/ --case=underscore + +// Wrapper for ldap connection and mock testing, implemented by *ldap.Conn +type LDAPConn interface { + Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) + Bind(username string, password string) error + Close() (err error) +} + +func newLDAPClient(config config.LDAP) LDAPClient { + return &ldapClient{config} +} + +// CreateEphemeralConnection returns a valid, active LDAP connection for upstream Search and Bind queries +func (l *ldapClient) CreateEphemeralConnection() (LDAPConn, error) { + conn, err := ldap.DialURL(l.config.ServerAddress()) + if err != nil { + return nil, fmt.Errorf("failed to Dial LDAP Server: %w", err) + } + // Root level root user auth with credentials provided from config + bindStr := l.config.BaseUserAttr() + "=" + l.config.ReadOnlyUserLogin() + "," + l.config.BaseDN() + if err := conn.Bind(bindStr, l.config.ReadOnlyUserPass()); err != nil { + return nil, fmt.Errorf("unable to login as initial root LDAP user: %w", err) + } + return conn, nil +} diff --git a/core/sessions/ldapauth/helpers_test.go b/core/sessions/ldapauth/helpers_test.go new file mode 100644 index 00000000000..c554d5436ed --- /dev/null +++ b/core/sessions/ldapauth/helpers_test.go @@ -0,0 +1,131 @@ +package ldapauth + +import ( + "time" + + "github.com/smartcontractkit/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/logger/audit" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/store/models" +) + +// Returns an instantiated ldapAuthenticator struct without validation for testing +func NewTestLDAPAuthenticator( + db *sqlx.DB, + pgCfg pg.QConfig, + ldapCfg config.LDAP, + dev bool, + lggr logger.Logger, + auditLogger audit.AuditLogger, +) (*ldapAuthenticator, error) { + namedLogger := lggr.Named("LDAPAuthenticationProvider") + ldapAuth := ldapAuthenticator{ + q: pg.NewQ(db, namedLogger, pgCfg), + ldapClient: newLDAPClient(ldapCfg), + config: ldapCfg, + lggr: lggr.Named("LDAPAuthenticationProvider"), + auditLogger: auditLogger, + } + + return &ldapAuth, nil +} + +// Default server group name mappings for test config and mocked ldap search results +const ( + NodeAdminsGroupCN = "NodeAdmins" + NodeEditorsGroupCN = "NodeEditors" + NodeRunnersGroupCN = "NodeRunners" + NodeReadOnlyGroupCN = "NodeReadOnly" +) + +// Implement a setter function within the _test file so that the ldapauth_test module can set the unexported field with a mock +func (l *ldapAuthenticator) SetLDAPClient(newClient LDAPClient) { + l.ldapClient = newClient +} + +// Implements config.LDAP +type TestConfig struct { +} + +func (t *TestConfig) ServerAddress() string { + return "ldaps://MOCK" +} + +func (t *TestConfig) ReadOnlyUserLogin() string { + return "mock-readonly" +} + +func (t *TestConfig) ReadOnlyUserPass() string { + return "mock-password" +} + +func (t *TestConfig) ServerTLS() bool { + return false +} + +func (t *TestConfig) SessionTimeout() models.Duration { + return models.MustMakeDuration(time.Duration(0)) +} + +func (t *TestConfig) QueryTimeout() time.Duration { + return time.Duration(0) +} + +func (t *TestConfig) UserAPITokenDuration() models.Duration { + return models.MustMakeDuration(time.Duration(0)) +} + +func (t *TestConfig) BaseUserAttr() string { + return "uid" +} + +func (t *TestConfig) BaseDN() string { + return "dc=custom,dc=example,dc=com" +} + +func (t *TestConfig) UsersDN() string { + return "ou=users" +} + +func (t *TestConfig) GroupsDN() string { + return "ou=groups" +} + +func (t *TestConfig) ActiveAttribute() string { + return "organizationalStatus" +} + +func (t *TestConfig) ActiveAttributeAllowedValue() string { + return "ACTIVE" +} + +func (t *TestConfig) AdminUserGroupCN() string { + return NodeAdminsGroupCN +} + +func (t *TestConfig) EditUserGroupCN() string { + return NodeEditorsGroupCN +} + +func (t *TestConfig) RunUserGroupCN() string { + return NodeRunnersGroupCN +} + +func (t *TestConfig) ReadUserGroupCN() string { + return NodeReadOnlyGroupCN +} + +func (t *TestConfig) UserApiTokenEnabled() bool { + return true +} + +func (t *TestConfig) UpstreamSyncInterval() models.Duration { + return models.MustMakeDuration(time.Duration(0)) +} + +func (t *TestConfig) UpstreamSyncRateLimit() models.Duration { + return models.MustMakeDuration(time.Duration(0)) +} diff --git a/core/sessions/ldapauth/ldap.go b/core/sessions/ldapauth/ldap.go new file mode 100644 index 00000000000..188f2684e7e --- /dev/null +++ b/core/sessions/ldapauth/ldap.go @@ -0,0 +1,858 @@ +/* +The LDAP authentication package forwards the credentials in the user session request +for authentication with a configured upstream LDAP server + +This package relies on the two following local database tables: + + ldap_sessions: Upon successful LDAP response, creates a keyed local copy of the user email + ldap_user_api_tokens: User created API tokens, tied to the node, storing user email. + +Note: user can have only one API token at a time, and token expiration is enforced + +User session and roles are cached and revalidated with the upstream service at the interval defined in +the local LDAP config through the Application.sessionReaper implementation in reaper.go. + +Changes to the upstream identity server will propagate through and update local tables (web sessions, API tokens) +by either removing the entries or updating the roles. This sync happens for every auth endpoint hit, and +via the defined sync interval. One goroutine is created to coordinate the sync timing in the New function + +This implementation is read only; user mutation actions such as Delete are not supported. + +MFA is supported via the remote LDAP server implementation. Sufficient request time out should accommodate +for a blocking auth call while the user responds to a potential push notification callback. +*/ +package ldapauth + +import ( + "crypto/subtle" + "database/sql" + "errors" + "fmt" + "strings" + "time" + + "github.com/go-ldap/ldap/v3" + "github.com/smartcontractkit/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/auth" + "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/logger/audit" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" +) + +const ( + UniqueMemberAttribute = "uniqueMember" +) + +var ErrUserNotInUpstream = errors.New("LDAP query returned no matching users") +var ErrUserNoLDAPGroups = errors.New("user present in directory, but matching no role groups assigned") + +type ldapAuthenticator struct { + q pg.Q + ldapClient LDAPClient + config config.LDAP + lggr logger.Logger + auditLogger audit.AuditLogger +} + +// ldapAuthenticator implements sessions.AuthenticationProvider interface +var _ sessions.AuthenticationProvider = (*ldapAuthenticator)(nil) + +func NewLDAPAuthenticator( + db *sqlx.DB, + pgCfg pg.QConfig, + ldapCfg config.LDAP, + dev bool, + lggr logger.Logger, + auditLogger audit.AuditLogger, +) (*ldapAuthenticator, error) { + namedLogger := lggr.Named("LDAPAuthenticationProvider") + + // If not chainlink dev and not tls, error + if !dev && !ldapCfg.ServerTLS() { + return nil, errors.New("LDAP Authentication driver requires TLS when running in Production mode") + } + + // Ensure all RBAC role mappings to LDAP Groups are defined, and required fields populated, or error on startup + if ldapCfg.AdminUserGroupCN() == "" || ldapCfg.EditUserGroupCN() == "" || + ldapCfg.RunUserGroupCN() == "" || ldapCfg.ReadUserGroupCN() == "" { + return nil, errors.New("LDAP Group mapping from server group name for all local RBAC role required. Set group names for `_UserGroupCN` fields") + } + if ldapCfg.ServerAddress() == "" { + return nil, errors.New("LDAP ServerAddress config required") + } + if ldapCfg.ReadOnlyUserLogin() == "" { + return nil, errors.New("LDAP ReadOnlyUserLogin config required") + } + + ldapAuth := ldapAuthenticator{ + q: pg.NewQ(db, namedLogger, pgCfg), + ldapClient: newLDAPClient(ldapCfg), + config: ldapCfg, + lggr: lggr.Named("LDAPAuthenticationProvider"), + auditLogger: auditLogger, + } + + // Single override of library defined global + ldap.DefaultTimeout = ldapCfg.QueryTimeout() + + // Test initial connection and credentials + lggr.Infof("Attempting initial connection to configured LDAP server with bind as API user") + conn, err := ldapAuth.ldapClient.CreateEphemeralConnection() + if err != nil { + return nil, fmt.Errorf("unable to establish connection to LDAP server with provided URL and credentials: %w", err) + } + conn.Close() + + // Store LDAP connection config for auth/new connection per request instead of persisted connection with reconnect + return &ldapAuth, nil +} + +// FindUser will attempt to return an LDAP user with mapped role by email. +func (l *ldapAuthenticator) FindUser(email string) (sessions.User, error) { + email = strings.ToLower(email) + foundUser := sessions.User{} + + // First check for the supported local admin users table + var foundLocalAdminUser sessions.User + checkErr := l.q.Transaction(func(tx pg.Queryer) error { + sql := "SELECT * FROM users WHERE lower(email) = lower($1)" + return tx.Get(&foundLocalAdminUser, sql, email) + }) + if checkErr != nil { + // If error is not nil, there was either an issue or no local users found + if !errors.Is(checkErr, sql.ErrNoRows) { + // If the error is not that no local user was found, log and exit + l.lggr.Errorf("error searching users table: %v", checkErr) + return sessions.User{}, errors.New("error Finding user") + } + } else { + // Error was nil, local user found. Return + return foundLocalAdminUser, nil + } + + // First query for user "is active" property if defined + usersActive, err := l.validateUsersActive([]string{email}) + if err != nil { + if errors.Is(err, ErrUserNotInUpstream) { + return foundUser, ErrUserNotInUpstream + } + l.lggr.Errorf("error in validateUsers call: %v", err) + return foundUser, errors.New("error running query to validate user active") + } + if !usersActive[0] { + return foundUser, errors.New("user not active") + } + + conn, err := l.ldapClient.CreateEphemeralConnection() + if err != nil { + l.lggr.Errorf("error in LDAP dial: ", err) + return foundUser, errors.New("unable to establish connection to LDAP server with provided URL and credentials") + } + defer conn.Close() + + // User email and role are the only upstream data that needs queried for. + // List query user groups using the provided email, on success is a list of group the uniquemember belongs to + // data is readily available + escapedEmail := ldap.EscapeFilter(email) + searchBaseDN := fmt.Sprintf("%s, %s", l.config.GroupsDN(), l.config.BaseDN()) + filterQuery := fmt.Sprintf("(&(uniquemember=%s=%s,%s,%s))", l.config.BaseUserAttr(), escapedEmail, l.config.UsersDN(), l.config.BaseDN()) + searchRequest := ldap.NewSearchRequest( + searchBaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, + 0, int(l.config.QueryTimeout().Seconds()), false, + filterQuery, + []string{"cn"}, + nil, + ) + + // Query the server + result, err := conn.Search(searchRequest) + if err != nil { + l.lggr.Errorf("error searching users in LDAP query: %v", err) + return foundUser, errors.New("error searching users in LDAP directory") + } + + if len(result.Entries) == 0 { + // Provided email is not present in upstream LDAP server, local admin CLI auth is supported + // So query and check the users table as well before failing + if err = l.q.Transaction(func(tx pg.Queryer) error { + var localUserRole sessions.UserRole + if err = tx.Get(&localUserRole, "SELECT role FROM users WHERE email = $1", email); err != nil { + return err + } + foundUser = sessions.User{ + Email: email, + Role: localUserRole, + } + return nil + }); err != nil { + // Above query for local user unsuccessful, return error + l.lggr.Warnf("No local users table user found with email %s", email) + return foundUser, errors.New("no users found with provided email") + } + + // If the above query to the local users table was successful, return that local user's role + return foundUser, nil + } + + // Populate found user by email and role based on matched group names + userRole, err := l.groupSearchResultsToUserRole(result.Entries) + if err != nil { + l.lggr.Warnf("User '%s' found but no matching assigned groups in LDAP to assume role", email) + return sessions.User{}, err + } + + // Convert search result to sessions.User type with required fields + foundUser = sessions.User{ + Email: email, + Role: userRole, + } + + return foundUser, nil +} + +// FindUserByAPIToken retrieves a possible stored user and role from the ldap_user_api_tokens table store +func (l *ldapAuthenticator) FindUserByAPIToken(apiToken string) (sessions.User, error) { + if !l.config.UserApiTokenEnabled() { + return sessions.User{}, errors.New("API token is not enabled ") + } + + var foundUser sessions.User + err := l.q.Transaction(func(tx pg.Queryer) error { + // Query the ldap user API token table for given token, user role and email are cached so + // no further upstream LDAP query is performed, sessions and tokens are synced against the upstream server + // via the UpstreamSyncInterval config and reaper.go sync implementation + var foundUserToken struct { + UserEmail string + UserRole sessions.UserRole + Valid bool + } + if err := tx.Get(&foundUserToken, + "SELECT user_email, user_role, created_at + $2 >= now() as valid FROM ldap_user_api_tokens WHERE token_key = $1", + apiToken, l.config.UserAPITokenDuration().Duration(), + ); err != nil { + return err + } + if !foundUserToken.Valid { + return sessions.ErrUserSessionExpired + } + foundUser = sessions.User{ + Email: foundUserToken.UserEmail, + Role: foundUserToken.UserRole, + } + return nil + }) + if err != nil { + if errors.Is(err, sessions.ErrUserSessionExpired) { + // API Token expired, purge + if _, execErr := l.q.Exec("DELETE FROM ldap_user_api_tokens WHERE token_key = $1", apiToken); err != nil { + l.lggr.Errorf("error purging stale ldap API token session: %v", execErr) + } + } + return sessions.User{}, err + } + return foundUser, nil +} + +// ListUsers will load and return all active users in applicable LDAP groups, extended with local admin users as well +func (l *ldapAuthenticator) ListUsers() ([]sessions.User, error) { + // For each defined role/group, query for the list of group members to gather the full list of possible users + users := []sessions.User{} + var err error + + conn, err := l.ldapClient.CreateEphemeralConnection() + if err != nil { + l.lggr.Errorf("error in LDAP dial: ", err) + return users, errors.New("unable to establish connection to LDAP server with provided URL and credentials") + } + defer conn.Close() + + // Query for list of uniqueMember IDs present in Admin group + adminUsers, err := l.ldapGroupMembersListToUser(conn, l.config.AdminUserGroupCN(), sessions.UserRoleAdmin) + if err != nil { + l.lggr.Errorf("error in ldapGroupMembersListToUser: ", err) + return users, errors.New("unable to list group users") + } + // Query for list of uniqueMember IDs present in Edit group + editUsers, err := l.ldapGroupMembersListToUser(conn, l.config.EditUserGroupCN(), sessions.UserRoleEdit) + if err != nil { + l.lggr.Errorf("error in ldapGroupMembersListToUser: ", err) + return users, errors.New("unable to list group users") + } + // Query for list of uniqueMember IDs present in Run group + runUsers, err := l.ldapGroupMembersListToUser(conn, l.config.RunUserGroupCN(), sessions.UserRoleRun) + if err != nil { + l.lggr.Errorf("error in ldapGroupMembersListToUser: ", err) + return users, errors.New("unable to list group users") + } + // Query for list of uniqueMember IDs present in Read group + readUsers, err := l.ldapGroupMembersListToUser(conn, l.config.ReadUserGroupCN(), sessions.UserRoleView) + if err != nil { + l.lggr.Errorf("error in ldapGroupMembersListToUser: ", err) + return users, errors.New("unable to list group users") + } + + // Aggregate full list + users = append(users, adminUsers...) + users = append(users, editUsers...) + users = append(users, runUsers...) + users = append(users, readUsers...) + + // Dedupe preserving order of highest role + uniqueRef := make(map[string]struct{}) + dedupedUsers := []sessions.User{} + for _, user := range users { + if _, ok := uniqueRef[user.Email]; !ok { + uniqueRef[user.Email] = struct{}{} + dedupedUsers = append(dedupedUsers, user) + } + } + + // If no active attribute to check is defined, user simple being assigned the group is enough, return full list + if l.config.ActiveAttribute() == "" { + return dedupedUsers, nil + } + + // Now optionally validate that all uniqueMembers are active in the org/LDAP server + emails := []string{} + for _, user := range dedupedUsers { + emails = append(emails, user.Email) + } + activeUsers, err := l.validateUsersActive(emails) + if err != nil { + l.lggr.Errorf("error validating supplied user list: ", err) + return users, errors.New("error validating supplied user list") + } + + // Filter non active users + returnUsers := []sessions.User{} + for i, active := range activeUsers { + if active { + returnUsers = append(returnUsers, dedupedUsers[i]) + } + } + + // Extend with local admin users + var localAdminUsers []sessions.User + if err := l.q.Transaction(func(tx pg.Queryer) error { + sql := "SELECT * FROM users ORDER BY email ASC;" + return tx.Select(&localAdminUsers, sql) + }); err != nil { + l.lggr.Errorf("error extending upstream LDAP users with local admin users in users table: ", err) + } else { + returnUsers = append(returnUsers, localAdminUsers...) + } + + return returnUsers, nil +} + +// ldapGroupMembersListToUser queries the LDAP server given a conn for a list of uniqueMember who are part of the parameterized group +func (l *ldapAuthenticator) ldapGroupMembersListToUser(conn LDAPConn, groupNameCN string, roleToAssign sessions.UserRole) ([]sessions.User, error) { + users, err := ldapGroupMembersListToUser( + conn, groupNameCN, roleToAssign, l.config.GroupsDN(), + l.config.BaseDN(), l.config.QueryTimeout(), + l.lggr, + ) + if err != nil { + l.lggr.Errorf("error listing members of group (%s): %v", groupNameCN, err) + return users, errors.New("error searching group members in LDAP directory") + } + return users, nil +} + +// AuthorizedUserWithSession will return the API user associated with the Session ID if it +// exists and hasn't expired, and update session's LastUsed field. The state of the upstream LDAP server +// is polled and synced at the defined interval via a SleeperTask +func (l *ldapAuthenticator) AuthorizedUserWithSession(sessionID string) (sessions.User, error) { + if len(sessionID) == 0 { + return sessions.User{}, errors.New("session ID cannot be empty") + } + var foundUser sessions.User + err := l.q.Transaction(func(tx pg.Queryer) error { + // Query the ldap_sessions table for given session ID, user role and email are cached so + // no further upstream LDAP query is performed + var foundSession struct { + UserEmail string + UserRole sessions.UserRole + Valid bool + } + if err := tx.Get(&foundSession, + "SELECT user_email, user_role, created_at + $2 >= now() as valid FROM ldap_sessions WHERE id = $1", + sessionID, l.config.SessionTimeout().Duration(), + ); err != nil { + return sessions.ErrUserSessionExpired + } + if !foundSession.Valid { + // Sessions expired, purge + return sessions.ErrUserSessionExpired + } + foundUser = sessions.User{ + Email: foundSession.UserEmail, + Role: foundSession.UserRole, + } + return nil + }) + if err != nil { + if errors.Is(err, sessions.ErrUserSessionExpired) { + if _, execErr := l.q.Exec("DELETE FROM ldap_sessions WHERE id = $1", sessionID); err != nil { + l.lggr.Errorf("error purging stale ldap session: %v", execErr) + } + } + return sessions.User{}, err + } + return foundUser, nil +} + +// DeleteUser is not supported for read only LDAP +func (l *ldapAuthenticator) DeleteUser(email string) error { + return sessions.ErrNotSupported +} + +// DeleteUserSession removes an ldapSession table entry by ID +func (l *ldapAuthenticator) DeleteUserSession(sessionID string) error { + _, err := l.q.Exec("DELETE FROM ldap_sessions WHERE id = $1", sessionID) + return err +} + +// GetUserWebAuthn returns an empty stub, MFA token prompt is handled either by the upstream +// server blocking callback, or an error code to pass a OTP +func (l *ldapAuthenticator) GetUserWebAuthn(email string) ([]sessions.WebAuthn, error) { + return []sessions.WebAuthn{}, nil +} + +// CreateSession will forward the session request credentials to the +// LDAP server, querying for a user + role response if username and +// password match. The API call is blocking with timeout, so a sufficient timeout +// should allow the user to respond to potential MFA push notifications +func (l *ldapAuthenticator) CreateSession(sr sessions.SessionRequest) (string, error) { + conn, err := l.ldapClient.CreateEphemeralConnection() + if err != nil { + return "", errors.New("unable to establish connection to LDAP server with provided URL and credentials") + } + defer conn.Close() + + var returnErr error + + // Attempt to LDAP Bind with user provided credentials + escapedEmail := ldap.EscapeFilter(strings.ToLower(sr.Email)) + searchBaseDN := fmt.Sprintf("%s=%s,%s,%s", l.config.BaseUserAttr(), escapedEmail, l.config.UsersDN(), l.config.BaseDN()) + if err = conn.Bind(searchBaseDN, sr.Password); err != nil { + l.lggr.Infof("Error binding user authentication request in LDAP Bind: %v", err) + returnErr = errors.New("unable to log in with LDAP server. Check credentials") + } + + // Bind was successful meaning user and credentials are present in LDAP directory + // Reuse FindUser functionality to fetch user roles used to create ldap_session entry + // with cached user email and role + foundUser, err := l.FindUser(escapedEmail) + if err != nil { + l.lggr.Infof("Successful user login, but error querying for user groups: user: %s, error %v", escapedEmail, err) + returnErr = errors.New("log in successful, but no assigned groups to assume role") + } + + isLocalUser := false + if returnErr != nil { + // Unable to log in against LDAP server, attempt fallback local auth with credentials, case of local CLI Admin account + // Successful local user sessions can not be managed by the upstream server and have expiration handled by the reaper sync module + foundUser, returnErr = l.localLoginFallback(sr) + isLocalUser = true + } + + // If err is still populated, return + if returnErr != nil { + return "", returnErr + } + + l.lggr.Infof("Successful LDAP login request for user %s - %s", sr.Email, foundUser.Role) + + // Save session, user, and role to database. Given a session ID for future queries, the LDAP server will not be queried + // Sessions are set to expire after the duration + creation date elapsed, and are synced on an interval against the upstream + // LDAP server + session := sessions.NewSession() + _, err = l.q.Exec( + "INSERT INTO ldap_sessions (id, user_email, user_role, localauth_user, created_at) VALUES ($1, $2, $3, $4, now())", + session.ID, + strings.ToLower(sr.Email), + foundUser.Role, + isLocalUser, + ) + if err != nil { + l.lggr.Errorf("unable to create new session in ldap_sessions table %v", err) + return "", fmt.Errorf("error creating local LDAP session: %w", err) + } + + l.auditLogger.Audit(audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) + + return session.ID, nil +} + +// ClearNonCurrentSessions removes all ldap_sessions but the id passed in. +func (l *ldapAuthenticator) ClearNonCurrentSessions(sessionID string) error { + _, err := l.q.Exec("DELETE FROM ldap_sessions where id != $1", sessionID) + return err +} + +// CreateUser is not supported for read only LDAP +func (l *ldapAuthenticator) CreateUser(user *sessions.User) error { + return sessions.ErrNotSupported +} + +// UpdateRole is not supported for read only LDAP +func (l *ldapAuthenticator) UpdateRole(email, newRole string) (sessions.User, error) { + return sessions.User{}, sessions.ErrNotSupported +} + +// SetPassword for remote users is not supported via the read only LDAP implementation, however change password +// in the context of updating a local admin user's password is required +func (l *ldapAuthenticator) SetPassword(user *sessions.User, newPassword string) error { + // Ensure specified user is part of the local admins user table + var localAdminUser sessions.User + if err := l.q.Transaction(func(tx pg.Queryer) error { + sql := "SELECT * FROM users WHERE lower(email) = lower($1)" + return tx.Get(&localAdminUser, sql, user.Email) + }); err != nil { + l.lggr.Infof("Can not change password, local user with email not found in users table: %s, err: %v", user.Email, err) + return sessions.ErrNotSupported + } + + // User is local admin, save new password + hashedPassword, err := utils.HashPassword(newPassword) + if err != nil { + return err + } + if err := l.q.Transaction(func(tx pg.Queryer) error { + sql := "UPDATE users SET hashed_password = $1, updated_at = now() WHERE email = $2 RETURNING *" + return tx.Get(user, sql, hashedPassword, user.Email) + }); err != nil { + l.lggr.Errorf("unable to set password for user: %s, err: %v", user.Email, err) + return errors.New("unable to save password") + } + return nil +} + +// TestPassword tests if an LDAP login bind can be performed with provided credentials, returns nil if success +func (l *ldapAuthenticator) TestPassword(email string, password string) error { + conn, err := l.ldapClient.CreateEphemeralConnection() + if err != nil { + return errors.New("unable to establish connection to LDAP server with provided URL and credentials") + } + defer conn.Close() + + // Attempt to LDAP Bind with user provided credentials + escapedEmail := ldap.EscapeFilter(strings.ToLower(email)) + searchBaseDN := fmt.Sprintf("%s=%s,%s,%s", l.config.BaseUserAttr(), escapedEmail, l.config.UsersDN(), l.config.BaseDN()) + err = conn.Bind(searchBaseDN, password) + if err == nil { + return nil + } + l.lggr.Infof("Error binding user authentication request in TestPassword call LDAP Bind: %v", err) + + // Fall back to test local users table in case of supported local CLI users as well + var hashedPassword string + if err := l.q.Get(&hashedPassword, "SELECT hashed_password FROM users WHERE lower(email) = lower($1)", email); err != nil { + return errors.New("invalid credentials") + } + if !utils.CheckPasswordHash(password, hashedPassword) { + return errors.New("invalid credentials") + } + + return nil +} + +// CreateAndSetAuthToken generates a new credential token with the user role +func (l *ldapAuthenticator) CreateAndSetAuthToken(user *sessions.User) (*auth.Token, error) { + newToken := auth.NewToken() + + err := l.SetAuthToken(user, newToken) + if err != nil { + return nil, err + } + + return newToken, nil +} + +// SetAuthToken updates the user to use the given Authentication Token. +func (l *ldapAuthenticator) SetAuthToken(user *sessions.User, token *auth.Token) error { + if !l.config.UserApiTokenEnabled() { + return errors.New("API token is not enabled ") + } + + salt := utils.NewSecret(utils.DefaultSecretSize) + hashedSecret, err := auth.HashedSecret(token, salt) + if err != nil { + return fmt.Errorf("LDAPAuth SetAuthToken hashed secret error: %w", err) + } + + err = l.q.Transaction(func(tx pg.Queryer) error { + // Is this user a local CLI Admin or upstream LDAP user? + // Check presence in local users table. Set localauth_user column true if present. + // This flag omits the session/token from being purged by the sync daemon/reaper.go + isLocalCLIAdmin := false + err = l.q.QueryRow("SELECT EXISTS (SELECT 1 FROM users WHERE email = $1)", user.Email).Scan(&isLocalCLIAdmin) + if err != nil { + return fmt.Errorf("error checking user presence in users table: %w", err) + } + + // Remove any existing API tokens + if _, err = l.q.Exec("DELETE FROM ldap_user_api_tokens WHERE user_email = $1", user.Email); err != nil { + return fmt.Errorf("error executing DELETE FROM ldap_user_api_tokens: %w", err) + } + // Create new API token for user + _, err = l.q.Exec( + "INSERT INTO ldap_user_api_tokens (user_email, user_role, localauth_user, token_key, token_salt, token_hashed_secret, created_at) VALUES ($1, $2, $3, $4, $5, $6, now())", + user.Email, + user.Role, + isLocalCLIAdmin, + token.AccessKey, + salt, + hashedSecret, + ) + if err != nil { + return fmt.Errorf("failed insert into ldap_user_api_tokens: %w", err) + } + return nil + }) + if err != nil { + return errors.New("error creating API token") + } + + l.auditLogger.Audit(audit.APITokenCreated, map[string]interface{}{"user": user.Email}) + return nil +} + +// DeleteAuthToken clears and disables the users Authentication Token. +func (l *ldapAuthenticator) DeleteAuthToken(user *sessions.User) error { + _, err := l.q.Exec("DELETE FROM ldap_user_api_tokens WHERE email = $1") + return err +} + +// SaveWebAuthn is not supported for read only LDAP +func (l *ldapAuthenticator) SaveWebAuthn(token *sessions.WebAuthn) error { + return sessions.ErrNotSupported +} + +// Sessions returns all sessions limited by the parameters. +func (l *ldapAuthenticator) Sessions(offset, limit int) ([]sessions.Session, error) { + var sessions []sessions.Session + sql := `SELECT * FROM ldap_sessions ORDER BY created_at, id LIMIT $1 OFFSET $2;` + if err := l.q.Select(&sessions, sql, limit, offset); err != nil { + return sessions, nil + } + return sessions, nil +} + +// FindExternalInitiator supports the 'Run' role external intiator header auth functionality +func (l *ldapAuthenticator) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiator, error) { + exi := &bridges.ExternalInitiator{} + err := l.q.Get(exi, `SELECT * FROM external_initiators WHERE access_key = $1`, eia.AccessKey) + return exi, err +} + +// localLoginFallback tests the credentials provided against the 'local' authentication method +// This covers the case of local CLI API calls requiring local login separate from the LDAP server +func (l *ldapAuthenticator) localLoginFallback(sr sessions.SessionRequest) (sessions.User, error) { + var user sessions.User + sql := "SELECT * FROM users WHERE lower(email) = lower($1)" + err := l.q.Get(&user, sql, sr.Email) + if err != nil { + return user, err + } + if !constantTimeEmailCompare(strings.ToLower(sr.Email), strings.ToLower(user.Email)) { + l.auditLogger.Audit(audit.AuthLoginFailedEmail, map[string]interface{}{"email": sr.Email}) + return user, errors.New("invalid email") + } + + if !utils.CheckPasswordHash(sr.Password, user.HashedPassword) { + l.auditLogger.Audit(audit.AuthLoginFailedPassword, map[string]interface{}{"email": sr.Email}) + return user, errors.New("invalid password") + } + + return user, nil +} + +// validateUsersActive performs an additional LDAP server query for the supplied emails, checking the +// returned user data for an 'active' property defined optionally in the config. +// Returns same length bool 'valid' array, indexed by sorted email +func (l *ldapAuthenticator) validateUsersActive(emails []string) ([]bool, error) { + validUsers := make([]bool, len(emails)) + // If active attribute to check is not defined in config, skip + if l.config.ActiveAttribute() == "" { + // fill with valids + for i := range emails { + validUsers[i] = true + } + return validUsers, nil + } + + conn, err := l.ldapClient.CreateEphemeralConnection() + if err != nil { + l.lggr.Errorf("error in LDAP dial: ", err) + return validUsers, errors.New("unable to establish connection to LDAP server with provided URL and credentials") + } + defer conn.Close() + + // Build the full email list query to pull all 'isActive' information for each user specified in one query + filterQuery := "(|" + for _, email := range emails { + escapedEmail := ldap.EscapeFilter(email) + filterQuery = fmt.Sprintf("%s(%s=%s)", filterQuery, l.config.BaseUserAttr(), escapedEmail) + } + filterQuery = fmt.Sprintf("(&%s))", filterQuery) + searchBaseDN := fmt.Sprintf("%s,%s", l.config.UsersDN(), l.config.BaseDN()) + searchRequest := ldap.NewSearchRequest( + searchBaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, + 0, int(l.config.QueryTimeout().Seconds()), false, + filterQuery, + []string{l.config.BaseUserAttr(), l.config.ActiveAttribute()}, + nil, + ) + // Query LDAP server for the ActiveAttribute property of each specified user + results, err := conn.Search(searchRequest) + if err != nil { + l.lggr.Errorf("error searching user in LDAP query: %v", err) + return validUsers, errors.New("error searching users in LDAP directory") + } + + // Ensure user response entries + if len(results.Entries) == 0 { + return validUsers, ErrUserNotInUpstream + } + + // Pull expected ActiveAttribute value from list of string possible values + // keyed on email for final step to return flag bool list where order is preserved + emailToActiveMap := make(map[string]bool) + for _, result := range results.Entries { + isActiveAttribute := result.GetAttributeValue(l.config.ActiveAttribute()) + uidAttribute := result.GetAttributeValue(l.config.BaseUserAttr()) + emailToActiveMap[uidAttribute] = isActiveAttribute == l.config.ActiveAttributeAllowedValue() + } + for i, email := range emails { + active, ok := emailToActiveMap[email] + if ok && active { + validUsers[i] = true + } + } + + return validUsers, nil +} + +// ldapGroupMembersListToUser queries the LDAP server given a conn for a list of uniqueMember who are part of the parameterized group. Reused by sync.go +func ldapGroupMembersListToUser( + conn LDAPConn, + groupNameCN string, + roleToAssign sessions.UserRole, + groupsDN string, + baseDN string, + queryTimeout time.Duration, + lggr logger.Logger, +) ([]sessions.User, error) { + users := []sessions.User{} + // Prepare and query the GroupsDN for the specified group name + searchBaseDN := fmt.Sprintf("%s, %s", groupsDN, baseDN) + filterQuery := fmt.Sprintf("(&(cn=%s))", groupNameCN) + searchRequest := ldap.NewSearchRequest( + searchBaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, + 0, int(queryTimeout.Seconds()), false, + filterQuery, + []string{UniqueMemberAttribute}, + nil, + ) + result, err := conn.Search(searchRequest) + if err != nil { + lggr.Errorf("error searching group members in LDAP query: %v", err) + return users, errors.New("error searching group members in LDAP directory") + } + + // The result.Entry query response here is for the 'group' type of LDAP resource. The result should be a single entry, containing + // a single Attribute named 'uniqueMember' containing a list of string Values. These Values are strings that should be returned in + // the format "uid=test.user@example.com,ou=users,dc=example,dc=com". The 'uid' is then manually parsed here as the library does + // not expose the functionality + if len(result.Entries) != 1 { + lggr.Errorf("unexpected length of query results for group user members, expected one got %d", len(result.Entries)) + return users, errors.New("error searching group members in LDAP directory") + } + + // Get string list of members from 'uniqueMember' attribute + uniqueMemberValues := result.Entries[0].GetAttributeValues(UniqueMemberAttribute) + for _, uniqueMemberEntry := range uniqueMemberValues { + parts := strings.Split(uniqueMemberEntry, ",") // Split attribute value on comma (uid, ou, dc parts) + uidComponent := "" + for _, part := range parts { // Iterate parts for "uid=" + if strings.HasPrefix(part, "uid=") { + uidComponent = part + break + } + } + if uidComponent == "" { + lggr.Errorf("unexpected LDAP group query response for unique members - expected list of LDAP Values for uniqueMember containing LDAP strings in format uid=test.user@example.com,ou=users,dc=example,dc=com. Got %s", uniqueMemberEntry) + continue + } + // Map each user email to the sessions.User struct + userEmail := strings.TrimPrefix(uidComponent, "uid=") + users = append(users, sessions.User{ + Email: userEmail, + Role: roleToAssign, + }) + } + return users, nil +} + +// groupSearchResultsToUserRole takes a list of LDAP group search result entries and returns the associated +// internal user role based on the group name mappings defined in the configuration +func (l *ldapAuthenticator) groupSearchResultsToUserRole(ldapGroups []*ldap.Entry) (sessions.UserRole, error) { + return GroupSearchResultsToUserRole( + ldapGroups, + l.config.AdminUserGroupCN(), + l.config.EditUserGroupCN(), + l.config.RunUserGroupCN(), + l.config.ReadUserGroupCN(), + ) +} + +func GroupSearchResultsToUserRole(ldapGroups []*ldap.Entry, adminCN string, editCN string, runCN string, readCN string) (sessions.UserRole, error) { + // If defined Admin group name is present in groups search result, return UserRoleAdmin + for _, group := range ldapGroups { + if group.GetAttributeValue("cn") == adminCN { + return sessions.UserRoleAdmin, nil + } + } + // Check edit role + for _, group := range ldapGroups { + if group.GetAttributeValue("cn") == editCN { + return sessions.UserRoleEdit, nil + } + } + // Check run role + for _, group := range ldapGroups { + if group.GetAttributeValue("cn") == runCN { + return sessions.UserRoleRun, nil + } + } + // Check view role + for _, group := range ldapGroups { + if group.GetAttributeValue("cn") == readCN { + return sessions.UserRoleView, nil + } + } + // No role group found, error + return sessions.UserRoleView, ErrUserNoLDAPGroups +} + +const constantTimeEmailLength = 256 + +func constantTimeEmailCompare(left, right string) bool { + length := mathutil.Max(constantTimeEmailLength, len(left), len(right)) + leftBytes := make([]byte, length) + rightBytes := make([]byte, length) + copy(leftBytes, left) + copy(rightBytes, right) + return subtle.ConstantTimeCompare(leftBytes, rightBytes) == 1 +} diff --git a/core/sessions/ldapauth/ldap_test.go b/core/sessions/ldapauth/ldap_test.go new file mode 100644 index 00000000000..261141d66e9 --- /dev/null +++ b/core/sessions/ldapauth/ldap_test.go @@ -0,0 +1,639 @@ +package ldapauth_test + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/go-ldap/ldap/v3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/logger/audit" + "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/sessions/ldapauth" + "github.com/smartcontractkit/chainlink/v2/core/sessions/ldapauth/mocks" +) + +// Setup LDAP Auth authenticator +func setupAuthenticationProvider(t *testing.T, ldapClient ldapauth.LDAPClient) (*sqlx.DB, sessions.AuthenticationProvider) { + t.Helper() + + cfg := ldapauth.TestConfig{} + db := pgtest.NewSqlxDB(t) + ldapAuthProvider, err := ldapauth.NewTestLDAPAuthenticator(db, pgtest.NewQConfig(true), &cfg, true, logger.TestLogger(t), &audit.AuditLoggerService{}) + if err != nil { + t.Fatalf("Error constructing NewTestLDAPAuthenticator: %v\n", err) + } + + // Override the LDAPClient responsible for returning the *ldap.Conn struct with Mock + ldapAuthProvider.SetLDAPClient(ldapClient) + return db, ldapAuthProvider +} + +func TestORM_FindUser_Empty(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // User not in upstream, return no entry + expectedResults := ldap.SearchResult{} + + // On search performed for validateUsersActive + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedResults, nil) + + // Not in upstream, no local admin users, expect error + _, err := ldapAuthProvider.FindUser("unknown-user") + require.ErrorContains(t, err, "LDAP query returned no matching users") +} + +func TestORM_FindUser_NoGroups(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // User present in Upstream but no groups assigned + user1 := cltest.MustRandomUser(t) + expectedResults := ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "cn=User One,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "organizationalStatus", + Values: []string{"ACTIVE"}, + }, + { + Name: "uid", + Values: []string{user1.Email}, + }, + }, + }, + }, + } + + // On search performed for validateUsersActive + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedResults, nil) + + // No Groups, expect error + _, err := ldapAuthProvider.FindUser(user1.Email) + require.ErrorContains(t, err, "user present in directory, but matching no role groups assigned") +} + +func TestORM_FindUser_NotActive(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // User present in Upstream but not active + user1 := cltest.MustRandomUser(t) + expectedResults := ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "cn=User One,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "organizationalStatus", + Values: []string{"INACTIVE"}, + }, + { + Name: "uid", + Values: []string{user1.Email}, + }, + }, + }, + }, + } + + // On search performed for validateUsersActive + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedResults, nil) + + // User not active, expect error + _, err := ldapAuthProvider.FindUser(user1.Email) + require.ErrorContains(t, err, "user not active") +} + +func TestORM_FindUser_Single(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // User present and valid + user1 := cltest.MustRandomUser(t) + expectedResults := ldap.SearchResult{ // Users query + Entries: []*ldap.Entry{ + { + DN: "cn=User One,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "organizationalStatus", + Values: []string{"ACTIVE"}, + }, + { + Name: "uid", + Values: []string{user1.Email}, + }, + }, + }, + }, + } + expectedGroupResults := ldap.SearchResult{ // Groups query + Entries: []*ldap.Entry{ + { + DN: "cn=NodeEditors,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{"NodeEditors"}, + }, + }, + }, + }, + } + + // On search performed for validateUsersActive + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedResults, nil).Once() + + // Second call on user groups search + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedGroupResults, nil).Once() + + // User active, and has editor group. Expect success + user, err := ldapAuthProvider.FindUser(user1.Email) + require.NoError(t, err) + require.Equal(t, user1.Email, user.Email) + require.Equal(t, sessions.UserRoleEdit, user.Role) +} + +func TestORM_FindUser_FallbackMatchLocalAdmin(t *testing.T) { + t.Parallel() + + // Initilaize LDAP Authentication Provider with mock client + mockLdapClient := mocks.NewLDAPClient(t) + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // Not in upstream, but utilize text fixture admin user presence in test DB. Succeed + user, err := ldapAuthProvider.FindUser(cltest.APIEmailAdmin) + require.NoError(t, err) + require.Equal(t, cltest.APIEmailAdmin, user.Email) + require.Equal(t, sessions.UserRoleAdmin, user.Role) +} + +func TestORM_FindUserByAPIToken_Success(t *testing.T) { + // Initilaize LDAP Authentication Provider with mock client + mockLdapClient := mocks.NewLDAPClient(t) + db, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // Ensure valid tokens return a user with role + testEmail := "test@test.com" + apiToken := "example" + _, err := db.Exec("INSERT INTO ldap_user_api_tokens values ($1, 'edit', false, $2, '', '', now())", testEmail, apiToken) + require.NoError(t, err) + + // Found user by API token in specific ldap_user_api_tokens table + user, err := ldapAuthProvider.FindUserByAPIToken(apiToken) + require.NoError(t, err) + require.Equal(t, testEmail, user.Email) + require.Equal(t, sessions.UserRoleEdit, user.Role) +} + +func TestORM_FindUserByAPIToken_Expired(t *testing.T) { + cfg := ldapauth.TestConfig{} + + // Initilaize LDAP Authentication Provider with mock client + mockLdapClient := mocks.NewLDAPClient(t) + db, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // Ensure valid tokens return a user with role + testEmail := "test@test.com" + apiToken := "example" + expiredTime := time.Now().Add(-cfg.UserAPITokenDuration().Duration()) + _, err := db.Exec("INSERT INTO ldap_user_api_tokens values ($1, 'edit', false, $2, '', '', $3)", testEmail, apiToken, expiredTime) + require.NoError(t, err) + + // Token found, but expired. Expect error + _, err = ldapAuthProvider.FindUserByAPIToken(apiToken) + require.Equal(t, sessions.ErrUserSessionExpired, err) +} + +func TestORM_ListUsers_Full(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + user1 := cltest.MustRandomUser(t) + user2 := cltest.MustRandomUser(t) + user3 := cltest.MustRandomUser(t) + user4 := cltest.MustRandomUser(t) + user5 := cltest.MustRandomUser(t) + user6 := cltest.MustRandomUser(t) + + // LDAP Group queries per role - admin + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeAdminsGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapauth.UniqueMemberAttribute, + Values: []string{ + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user1.Email), + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user2.Email), + }, + }, + }, + }, + }, + }, nil).Once() + // LDAP Group queries per role - edit + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeEditorsGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapauth.UniqueMemberAttribute, + Values: []string{ + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user3.Email), + }, + }, + }, + }, + }, + }, nil).Once() + // LDAP Group queries per role - run + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "cn=NodeRunners,ou=Groups,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapauth.UniqueMemberAttribute, + Values: []string{ + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user4.Email), + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user4.Email), // Test deduped + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user5.Email), + }, + }, + }, + }, + }, + }, nil).Once() + // LDAP Group queries per role - view + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&ldap.SearchResult{ + Entries: []*ldap.Entry{ + { + DN: "cn=NodeReadOnly,ou=Groups,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: ldapauth.UniqueMemberAttribute, + Values: []string{ + fmt.Sprintf("uid=%s,ou=users,dc=example,dc=com", user6.Email), + }, + }, + }, + }, + }, + }, nil).Once() + // Lastly followed by IsActive lookup + type userActivePair struct { + email string + active string + } + emailsActive := []userActivePair{ + {user1.Email, "ACTIVE"}, + {user2.Email, "INACTIVE"}, + {user3.Email, "ACTIVE"}, + {user4.Email, "ACTIVE"}, + {user5.Email, "INACTIVE"}, + {user6.Email, "ACTIVE"}, + } + listUpstreamUsersQuery := ldap.SearchResult{} + for _, upstreamUser := range emailsActive { + listUpstreamUsersQuery.Entries = append(listUpstreamUsersQuery.Entries, &ldap.Entry{ + DN: "cn=User,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "organizationalStatus", + Values: []string{upstreamUser.active}, + }, + { + Name: "uid", + Values: []string{upstreamUser.email}, + }, + }, + }, + ) + } + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&listUpstreamUsersQuery, nil).Once() + + // Asserts 'uid=' parsing log in ldapGroupMembersListToUser + // Expected full list of users above, including local admin user, excluding 'inactive' and duplicate users + users, err := ldapAuthProvider.ListUsers() + require.NoError(t, err) + require.Equal(t, users[0].Email, user1.Email) + require.Equal(t, users[0].Role, sessions.UserRoleAdmin) + require.Equal(t, users[1].Email, user3.Email) // User 2 inactive + require.Equal(t, users[1].Role, sessions.UserRoleEdit) + require.Equal(t, users[2].Email, user4.Email) + require.Equal(t, users[2].Role, sessions.UserRoleRun) + require.Equal(t, users[3].Email, user6.Email) // User 5 inactive + require.Equal(t, users[3].Role, sessions.UserRoleView) + require.Equal(t, users[4].Email, cltest.APIEmailAdmin) // Text fixture user is local admin included as well + require.Equal(t, users[4].Role, sessions.UserRoleAdmin) +} + +func TestORM_CreateSession_UpstreamBind(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // Upsream user present + user1 := cltest.MustRandomUser(t) + expectedResults := ldap.SearchResult{ // Users query + Entries: []*ldap.Entry{ + { + DN: "cn=User One,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "organizationalStatus", + Values: []string{"ACTIVE"}, + }, + { + Name: "uid", + Values: []string{user1.Email}, + }, + }, + }, + }, + } + expectedGroupResults := ldap.SearchResult{ // Groups query + Entries: []*ldap.Entry{ + { + DN: "cn=NodeEditors,ou=Users,dc=example,dc=com", + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{"NodeEditors"}, + }, + }, + }, + }, + } + + // On search performed for validateUsersActive + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedResults, nil).Once() + + // Second call on user groups search + mockLdapConnProvider.On("Search", mock.AnythingOfType("*ldap.SearchRequest")).Return(&expectedGroupResults, nil).Once() + + // User active, and has editor group. Expect success + mockLdapConnProvider.On("Bind", mock.Anything, cltest.Password).Return(nil) + sessionRequest := sessions.SessionRequest{ + Email: user1.Email, + Password: cltest.Password, + } + + _, err := ldapAuthProvider.CreateSession(sessionRequest) + require.NoError(t, err) +} + +func TestORM_CreateSession_LocalAdminFallbackLogin(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // Fail the bind to trigger 'localLoginFallback' - local admin users should still be able to login + // regardless of whether the authentication provider is remote or not + mockLdapConnProvider.On("Bind", mock.Anything, cltest.Password).Return(errors.New("unable to login via LDAP server")).Once() + + // User active, and has editor group. Expect success + sessionRequest := sessions.SessionRequest{ + Email: cltest.APIEmailAdmin, + Password: cltest.Password, + } + + _, err := ldapAuthProvider.CreateSession(sessionRequest) + require.NoError(t, err) + + // Finally, assert login failing altogether + // User active, and has editor group. Expect success + mockLdapConnProvider.On("Bind", mock.Anything, "incorrect-password").Return(errors.New("unable to login via LDAP server")).Once() + sessionRequest = sessions.SessionRequest{ + Email: cltest.APIEmailAdmin, + Password: "incorrect-password", + } + + _, err = ldapAuthProvider.CreateSession(sessionRequest) + require.ErrorContains(t, err, "invalid password") +} + +func TestORM_SetPassword_LocalAdminFallbackLogin(t *testing.T) { + t.Parallel() + + mockLdapClient := mocks.NewLDAPClient(t) + mockLdapConnProvider := mocks.NewLDAPConn(t) + mockLdapClient.On("CreateEphemeralConnection").Return(mockLdapConnProvider, nil) + mockLdapConnProvider.On("Close").Return(nil) + + // Initilaize LDAP Authentication Provider with mock client + _, ldapAuthProvider := setupAuthenticationProvider(t, mockLdapClient) + + // Fail the bind to trigger 'localLoginFallback' - local admin users should still be able to login + // regardless of whether the authentication provider is remote or not + mockLdapConnProvider.On("Bind", mock.Anything, cltest.Password).Return(errors.New("unable to login via LDAP server")).Once() + + // User active, and has editor group. Expect success + sessionRequest := sessions.SessionRequest{ + Email: cltest.APIEmailAdmin, + Password: cltest.Password, + } + + _, err := ldapAuthProvider.CreateSession(sessionRequest) + require.NoError(t, err) + + // Finally, assert login failing altogether + // User active, and has editor group. Expect success + mockLdapConnProvider.On("Bind", mock.Anything, "incorrect-password").Return(errors.New("unable to login via LDAP server")).Once() + sessionRequest = sessions.SessionRequest{ + Email: cltest.APIEmailAdmin, + Password: "incorrect-password", + } + + _, err = ldapAuthProvider.CreateSession(sessionRequest) + require.ErrorContains(t, err, "invalid password") +} + +func TestORM_MapSearchGroups(t *testing.T) { + t.Parallel() + + cfg := ldapauth.TestConfig{} + + tests := []struct { + name string + groupsQuerySearchResult []*ldap.Entry + wantMappedRole sessions.UserRole + wantErr error + }{ + { + "user in admin group only", + []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeAdminsGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{ldapauth.NodeAdminsGroupCN}, + }, + }, + }, + }, + sessions.UserRoleAdmin, + nil, + }, + { + "user in edit group", + []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeEditorsGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{ldapauth.NodeEditorsGroupCN}, + }, + }, + }, + }, + sessions.UserRoleEdit, + nil, + }, + { + "user in run group", + []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeRunnersGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{ldapauth.NodeRunnersGroupCN}, + }, + }, + }, + }, + sessions.UserRoleRun, + nil, + }, + { + "user in view role", + []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeReadOnlyGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{ldapauth.NodeReadOnlyGroupCN}, + }, + }, + }, + }, + sessions.UserRoleView, + nil, + }, + { + "user in none", + []*ldap.Entry{}, + sessions.UserRole(""), // ignored, error case + ldapauth.ErrUserNoLDAPGroups, + }, + { + "user in run and view", + []*ldap.Entry{ + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeRunnersGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{ldapauth.NodeRunnersGroupCN}, + }, + }, + }, + { + DN: fmt.Sprintf("cn=%s,ou=Groups,dc=example,dc=com", ldapauth.NodeReadOnlyGroupCN), + Attributes: []*ldap.EntryAttribute{ + { + Name: "cn", + Values: []string{ldapauth.NodeReadOnlyGroupCN}, + }, + }, + }, + }, + sessions.UserRoleRun, // Take highest role + nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + role, err := ldapauth.GroupSearchResultsToUserRole( + test.groupsQuerySearchResult, + cfg.AdminUserGroupCN(), + cfg.EditUserGroupCN(), + cfg.RunUserGroupCN(), + cfg.ReadUserGroupCN(), + ) + if test.wantErr != nil { + assert.Equal(t, test.wantErr, err) + } else { + assert.Equal(t, test.wantMappedRole, role) + } + }) + } +} diff --git a/core/sessions/ldapauth/mocks/ldap_client.go b/core/sessions/ldapauth/mocks/ldap_client.go new file mode 100644 index 00000000000..7a44778dcaa --- /dev/null +++ b/core/sessions/ldapauth/mocks/ldap_client.go @@ -0,0 +1,53 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package mocks + +import ( + ldapauth "github.com/smartcontractkit/chainlink/v2/core/sessions/ldapauth" + mock "github.com/stretchr/testify/mock" +) + +// LDAPClient is an autogenerated mock type for the LDAPClient type +type LDAPClient struct { + mock.Mock +} + +// CreateEphemeralConnection provides a mock function with given fields: +func (_m *LDAPClient) CreateEphemeralConnection() (ldapauth.LDAPConn, error) { + ret := _m.Called() + + var r0 ldapauth.LDAPConn + var r1 error + if rf, ok := ret.Get(0).(func() (ldapauth.LDAPConn, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() ldapauth.LDAPConn); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(ldapauth.LDAPConn) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewLDAPClient creates a new instance of LDAPClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLDAPClient(t interface { + mock.TestingT + Cleanup(func()) +}) *LDAPClient { + mock := &LDAPClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/sessions/ldapauth/mocks/ldap_conn.go b/core/sessions/ldapauth/mocks/ldap_conn.go new file mode 100644 index 00000000000..c05fb6c4fa6 --- /dev/null +++ b/core/sessions/ldapauth/mocks/ldap_conn.go @@ -0,0 +1,82 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package mocks + +import ( + ldap "github.com/go-ldap/ldap/v3" + + mock "github.com/stretchr/testify/mock" +) + +// LDAPConn is an autogenerated mock type for the LDAPConn type +type LDAPConn struct { + mock.Mock +} + +// Bind provides a mock function with given fields: username, password +func (_m *LDAPConn) Bind(username string, password string) error { + ret := _m.Called(username, password) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(username, password) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Close provides a mock function with given fields: +func (_m *LDAPConn) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Search provides a mock function with given fields: searchRequest +func (_m *LDAPConn) Search(searchRequest *ldap.SearchRequest) (*ldap.SearchResult, error) { + ret := _m.Called(searchRequest) + + var r0 *ldap.SearchResult + var r1 error + if rf, ok := ret.Get(0).(func(*ldap.SearchRequest) (*ldap.SearchResult, error)); ok { + return rf(searchRequest) + } + if rf, ok := ret.Get(0).(func(*ldap.SearchRequest) *ldap.SearchResult); ok { + r0 = rf(searchRequest) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ldap.SearchResult) + } + } + + if rf, ok := ret.Get(1).(func(*ldap.SearchRequest) error); ok { + r1 = rf(searchRequest) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewLDAPConn creates a new instance of LDAPConn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLDAPConn(t interface { + mock.TestingT + Cleanup(func()) +}) *LDAPConn { + mock := &LDAPConn{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/sessions/ldapauth/sync.go b/core/sessions/ldapauth/sync.go new file mode 100644 index 00000000000..ce7a338f40e --- /dev/null +++ b/core/sessions/ldapauth/sync.go @@ -0,0 +1,343 @@ +package ldapauth + +import ( + "errors" + "fmt" + "time" + + "github.com/go-ldap/ldap/v3" + "github.com/lib/pq" + "github.com/smartcontractkit/sqlx" + + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type LDAPServerStateSyncer struct { + q pg.Q + ldapClient LDAPClient + config config.LDAP + lggr logger.Logger + nextSyncTime time.Time +} + +// NewLDAPServerStateSync creates a reaper that cleans stale sessions from the store. +func NewLDAPServerStateSync( + db *sqlx.DB, + pgCfg pg.QConfig, + config config.LDAP, + lggr logger.Logger, +) utils.SleeperTask { + namedLogger := lggr.Named("LDAPServerStateSync") + serverSync := LDAPServerStateSyncer{ + q: pg.NewQ(db, namedLogger, pgCfg), + ldapClient: newLDAPClient(config), + config: config, + lggr: namedLogger, + nextSyncTime: time.Time{}, + } + // If enabled, start a background task that calls the Sync/Work function on an + // interval without needing an auth event to trigger it + // Use IsInstant to check 0 value to omit functionality. + if !config.UpstreamSyncInterval().IsInstant() { + lggr.Info("LDAP Config UpstreamSyncInterval is non-zero, sync functionality will be called on a timer, respecting the UpstreamSyncRateLimit value") + serverSync.StartWorkOnTimer() + } else { + // Ensure upstream server state is synced on startup manually if interval check not set + serverSync.Work() + } + + // Start background Sync call task reactive to auth related events + serverSyncSleeperTask := utils.NewSleeperTask(&serverSync) + return serverSyncSleeperTask +} + +func (ldSync *LDAPServerStateSyncer) Name() string { + return "LDAPServerStateSync" +} + +func (ldSync *LDAPServerStateSyncer) StartWorkOnTimer() { + time.AfterFunc(ldSync.config.UpstreamSyncInterval().Duration(), ldSync.StartWorkOnTimer) + ldSync.Work() +} + +func (ldSync *LDAPServerStateSyncer) Work() { + // Purge expired ldap_sessions and ldap_user_api_tokens + recordCreationStaleThreshold := ldSync.config.SessionTimeout().Before(time.Now()) + err := ldSync.deleteStaleSessions(recordCreationStaleThreshold) + if err != nil { + ldSync.lggr.Error("unable to expire local LDAP sessions: ", err) + } + recordCreationStaleThreshold = ldSync.config.UserAPITokenDuration().Before(time.Now()) + err = ldSync.deleteStaleAPITokens(recordCreationStaleThreshold) + if err != nil { + ldSync.lggr.Error("unable to expire user API tokens: ", err) + } + + // Optional rate limiting check to limit the amount of upstream LDAP server queries performed + if !ldSync.config.UpstreamSyncRateLimit().IsInstant() { + if !time.Now().After(ldSync.nextSyncTime) { + return + } + + // Enough time has elapsed to sync again, store the time for when next sync is allowed and begin sync + ldSync.nextSyncTime = time.Now().Add(ldSync.config.UpstreamSyncRateLimit().Duration()) + } + + ldSync.lggr.Info("Begin Upstream LDAP provider state sync after checking time against config UpstreamSyncInterval and UpstreamSyncRateLimit") + + // For each defined role/group, query for the list of group members to gather the full list of possible users + users := []sessions.User{} + + conn, err := ldSync.ldapClient.CreateEphemeralConnection() + if err != nil { + ldSync.lggr.Errorf("Failed to Dial LDAP Server", err) + return + } + // Root level root user auth with credentials provided from config + bindStr := ldSync.config.BaseUserAttr() + "=" + ldSync.config.ReadOnlyUserLogin() + "," + ldSync.config.BaseDN() + if err = conn.Bind(bindStr, ldSync.config.ReadOnlyUserPass()); err != nil { + ldSync.lggr.Errorf("Unable to login as initial root LDAP user", err) + } + defer conn.Close() + + // Query for list of uniqueMember IDs present in Admin group + adminUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.AdminUserGroupCN(), sessions.UserRoleAdmin) + if err != nil { + ldSync.lggr.Errorf("Error in ldapGroupMembersListToUser: ", err) + return + } + // Query for list of uniqueMember IDs present in Edit group + editUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.EditUserGroupCN(), sessions.UserRoleEdit) + if err != nil { + ldSync.lggr.Errorf("Error in ldapGroupMembersListToUser: ", err) + return + } + // Query for list of uniqueMember IDs present in Edit group + runUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.RunUserGroupCN(), sessions.UserRoleRun) + if err != nil { + ldSync.lggr.Errorf("Error in ldapGroupMembersListToUser: ", err) + return + } + // Query for list of uniqueMember IDs present in Edit group + readUsers, err := ldSync.ldapGroupMembersListToUser(conn, ldSync.config.ReadUserGroupCN(), sessions.UserRoleView) + if err != nil { + ldSync.lggr.Errorf("Error in ldapGroupMembersListToUser: ", err) + return + } + + users = append(users, adminUsers...) + users = append(users, editUsers...) + users = append(users, runUsers...) + users = append(users, readUsers...) + + // Dedupe preserving order of highest role (sorted) + // Preserve members as a map for future lookup + upstreamUserStateMap := make(map[string]sessions.User) + dedupedEmails := []string{} + for _, user := range users { + if _, ok := upstreamUserStateMap[user.Email]; !ok { + upstreamUserStateMap[user.Email] = user + dedupedEmails = append(dedupedEmails, user.Email) + } + } + + // For each unique user in list of active sessions, check for 'Is Active' propery if defined in the config. Some LDAP providers + // list group members that are no longer marked as active + usersActiveFlags, err := ldSync.validateUsersActive(dedupedEmails, conn) + if err != nil { + ldSync.lggr.Errorf("Error validating supplied user list: ", err) + } + // Remove users in the upstreamUserStateMap source of truth who are part of groups but marked as deactivated/no-active + for i, active := range usersActiveFlags { + if !active { + delete(upstreamUserStateMap, dedupedEmails[i]) + } + } + + // upstreamUserStateMap is now the most up to date source of truth + // Now sync database sessions and roles with new data + err = ldSync.q.Transaction(func(tx pg.Queryer) error { + // First, purge users present in the local ldap_sessions table but not in the upstream server + type LDAPSession struct { + UserEmail string + UserRole sessions.UserRole + } + var existingSessions []LDAPSession + if err = tx.Select(&existingSessions, "SELECT user_email, user_role FROM ldap_sessions WHERE localauth_user = false"); err != nil { + return fmt.Errorf("unable to query ldap_sessions table: %w", err) + } + var existingAPITokens []LDAPSession + if err = tx.Select(&existingAPITokens, "SELECT user_email, user_role FROM ldap_user_api_tokens WHERE localauth_user = false"); err != nil { + return fmt.Errorf("unable to query ldap_user_api_tokens table: %w", err) + } + + // Create existing sessions and API tokens lookup map for later + existingSessionsMap := make(map[string]LDAPSession) + for _, sess := range existingSessions { + existingSessionsMap[sess.UserEmail] = sess + } + existingAPITokensMap := make(map[string]LDAPSession) + for _, sess := range existingAPITokens { + existingAPITokensMap[sess.UserEmail] = sess + } + + // Populate list of session emails present in the local session table but not in the upstream state + emailsToPurge := []interface{}{} + for _, ldapSession := range existingSessions { + if _, ok := upstreamUserStateMap[ldapSession.UserEmail]; !ok { + emailsToPurge = append(emailsToPurge, ldapSession.UserEmail) + } + } + // Likewise for API Tokens table + apiTokenEmailsToPurge := []interface{}{} + for _, ldapSession := range existingAPITokens { + if _, ok := upstreamUserStateMap[ldapSession.UserEmail]; !ok { + apiTokenEmailsToPurge = append(apiTokenEmailsToPurge, ldapSession.UserEmail) + } + } + + // Remove any active sessions this user may have + if len(emailsToPurge) > 0 { + _, err = ldSync.q.Exec("DELETE FROM ldap_sessions WHERE user_email = ANY($1)", pq.Array(emailsToPurge)) + if err != nil { + return err + } + } + + // Remove any active API tokens this user may have + if len(apiTokenEmailsToPurge) > 0 { + _, err = ldSync.q.Exec("DELETE FROM ldap_user_api_tokens WHERE user_email = ANY($1)", pq.Array(apiTokenEmailsToPurge)) + if err != nil { + return err + } + } + + // For each user session row, update role to match state of user map from upstream source + queryWhenClause := "" + emailValues := []interface{}{} + // Prepare CASE WHEN query statement with parameterized argument $n placeholders and matching role based on index + for email, user := range upstreamUserStateMap { + // Only build on SET CASE statement per local session and API token role, not for each upstream user value + _, sessionOk := existingSessionsMap[email] + _, tokenOk := existingAPITokensMap[email] + if !sessionOk && !tokenOk { + continue + } + emailValues = append(emailValues, email) + queryWhenClause += fmt.Sprintf("WHEN user_email = $%d THEN '%s' ", len(emailValues), user.Role) + } + + // If there are remaining user entries to update + if len(emailValues) != 0 { + // Set new role state for all rows in single Exec + query := fmt.Sprintf("UPDATE ldap_sessions SET user_role = CASE %s ELSE user_role END", queryWhenClause) + _, err = ldSync.q.Exec(query, emailValues...) + if err != nil { + return err + } + + // Update role of API tokens as well + query = fmt.Sprintf("UPDATE ldap_user_api_tokens SET user_role = CASE %s ELSE user_role END", queryWhenClause) + _, err = ldSync.q.Exec(query, emailValues...) + if err != nil { + return err + } + } + + ldSync.lggr.Info("local ldap_sessions and ldap_user_api_tokens table successfully synced with upstream LDAP state") + return nil + }) + if err != nil { + ldSync.lggr.Errorf("Error syncing local database state: ", err) + } + ldSync.lggr.Info("Upstream LDAP sync complete") +} + +// deleteStaleSessions deletes all ldap_sessions before the passed time. +func (ldSync *LDAPServerStateSyncer) deleteStaleSessions(before time.Time) error { + _, err := ldSync.q.Exec("DELETE FROM ldap_sessions WHERE created_at < $1", before) + return err +} + +// deleteStaleAPITokens deletes all ldap_user_api_tokens before the passed time. +func (ldSync *LDAPServerStateSyncer) deleteStaleAPITokens(before time.Time) error { + _, err := ldSync.q.Exec("DELETE FROM ldap_user_api_tokens WHERE created_at < $1", before) + return err +} + +// ldapGroupMembersListToUser queries the LDAP server given a conn for a list of uniqueMember who are part of the parameterized group +func (ldSync *LDAPServerStateSyncer) ldapGroupMembersListToUser(conn LDAPConn, groupNameCN string, roleToAssign sessions.UserRole) ([]sessions.User, error) { + users, err := ldapGroupMembersListToUser( + conn, groupNameCN, roleToAssign, ldSync.config.GroupsDN(), + ldSync.config.BaseDN(), ldSync.config.QueryTimeout(), + ldSync.lggr, + ) + if err != nil { + ldSync.lggr.Errorf("Error listing members of group (%s): %v", groupNameCN, err) + return users, errors.New("error searching group members in LDAP directory") + } + return users, nil +} + +// validateUsersActive performs an additional LDAP server query for the supplied emails, checking the +// returned user data for an 'active' property defined optionally in the config. +// Returns same length bool 'valid' array, order preserved +func (ldSync *LDAPServerStateSyncer) validateUsersActive(emails []string, conn LDAPConn) ([]bool, error) { + validUsers := make([]bool, len(emails)) + // If active attribute to check is not defined in config, skip + if ldSync.config.ActiveAttribute() == "" { + // pre fill with valids + for i := range emails { + validUsers[i] = true + } + return validUsers, nil + } + + // Build the full email list query to pull all 'isActive' information for each user specified in one query + filterQuery := "(|" + for _, email := range emails { + escapedEmail := ldap.EscapeFilter(email) + filterQuery = fmt.Sprintf("%s(%s=%s)", filterQuery, ldSync.config.BaseUserAttr(), escapedEmail) + } + filterQuery = fmt.Sprintf("(&%s))", filterQuery) + searchBaseDN := fmt.Sprintf("%s,%s", ldSync.config.UsersDN(), ldSync.config.BaseDN()) + searchRequest := ldap.NewSearchRequest( + searchBaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, + 0, int(ldSync.config.QueryTimeout().Seconds()), false, + filterQuery, + []string{ldSync.config.BaseUserAttr(), ldSync.config.ActiveAttribute()}, + nil, + ) + // Query LDAP server for the ActiveAttribute property of each specified user + results, err := conn.Search(searchRequest) + if err != nil { + ldSync.lggr.Errorf("Error searching user in LDAP query: %v", err) + return validUsers, errors.New("error searching users in LDAP directory") + } + // Ensure user response entries + if len(results.Entries) == 0 { + return validUsers, errors.New("no users matching email query") + } + + // Pull expected ActiveAttribute value from list of string possible values + // keyed on email for final step to return flag bool list where order is preserved + emailToActiveMap := make(map[string]bool) + for _, result := range results.Entries { + isActiveAttribute := result.GetAttributeValue(ldSync.config.ActiveAttribute()) + uidAttribute := result.GetAttributeValue(ldSync.config.BaseUserAttr()) + emailToActiveMap[uidAttribute] = isActiveAttribute == ldSync.config.ActiveAttributeAllowedValue() + } + for i, email := range emails { + active, ok := emailToActiveMap[email] + if ok && active { + validUsers[i] = true + } + } + + return validUsers, nil +} diff --git a/core/sessions/orm.go b/core/sessions/localauth/orm.go similarity index 80% rename from core/sessions/orm.go rename to core/sessions/localauth/orm.go index eaac211f242..d6fb8cd5788 100644 --- a/core/sessions/orm.go +++ b/core/sessions/localauth/orm.go @@ -1,4 +1,4 @@ -package sessions +package localauth import ( "crypto/subtle" @@ -14,34 +14,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) -//go:generate mockery --quiet --name ORM --output ./mocks/ --case=underscore - -type ORM interface { - FindUser(email string) (User, error) - FindUserByAPIToken(apiToken string) (User, error) - ListUsers() ([]User, error) - AuthorizedUserWithSession(sessionID string) (User, error) - DeleteUser(email string) error - DeleteUserSession(sessionID string) error - CreateSession(sr SessionRequest) (string, error) - ClearNonCurrentSessions(sessionID string) error - CreateUser(user *User) error - UpdateRole(email, newRole string) (User, error) - SetAuthToken(user *User, token *auth.Token) error - CreateAndSetAuthToken(user *User) (*auth.Token, error) - DeleteAuthToken(user *User) error - SetPassword(user *User, newPassword string) error - Sessions(offset, limit int) ([]Session, error) - GetUserWebAuthn(email string) ([]WebAuthn, error) - SaveWebAuthn(token *WebAuthn) error - - FindExternalInitiator(eia *auth.Token) (initiator *bridges.ExternalInitiator, err error) -} - type orm struct { q pg.Q sessionDuration time.Duration @@ -49,38 +26,40 @@ type orm struct { auditLogger audit.AuditLogger } -var _ ORM = (*orm)(nil) +// orm implements sessions.AuthenticationProvider and sessions.BasicAdminUsersORM interfaces +var _ sessions.AuthenticationProvider = (*orm)(nil) +var _ sessions.BasicAdminUsersORM = (*orm)(nil) -func NewORM(db *sqlx.DB, sd time.Duration, lggr logger.Logger, cfg pg.QConfig, auditLogger audit.AuditLogger) ORM { - lggr = lggr.Named("SessionsORM") +func NewORM(db *sqlx.DB, sd time.Duration, lggr logger.Logger, cfg pg.QConfig, auditLogger audit.AuditLogger) sessions.AuthenticationProvider { + namedLogger := lggr.Named("LocalAuthAuthenticationProviderORM") return &orm{ - q: pg.NewQ(db, lggr, cfg), + q: pg.NewQ(db, namedLogger, cfg), sessionDuration: sd, - lggr: lggr, + lggr: lggr.Named("LocalAuthAuthenticationProviderORM"), auditLogger: auditLogger, } } // FindUser will attempt to return an API user by email. -func (o *orm) FindUser(email string) (User, error) { +func (o *orm) FindUser(email string) (sessions.User, error) { return o.findUser(email) } // FindUserByAPIToken will attempt to return an API user via the user's table token_key column. -func (o *orm) FindUserByAPIToken(apiToken string) (user User, err error) { +func (o *orm) FindUserByAPIToken(apiToken string) (user sessions.User, err error) { sql := "SELECT * FROM users WHERE token_key = $1" err = o.q.Get(&user, sql, apiToken) return } -func (o *orm) findUser(email string) (user User, err error) { +func (o *orm) findUser(email string) (user sessions.User, err error) { sql := "SELECT * FROM users WHERE lower(email) = lower($1)" err = o.q.Get(&user, sql, email) return } // ListUsers will load and return all user rows from the db. -func (o *orm) ListUsers() (users []User, err error) { +func (o *orm) ListUsers() (users []sessions.User, err error) { sql := "SELECT * FROM users ORDER BY email ASC;" err = o.q.Select(&users, sql) return @@ -100,31 +79,27 @@ func (o *orm) updateSessionLastUsed(sessionID string) error { return o.q.ExecQ("UPDATE sessions SET last_used = now() WHERE id = $1", sessionID) } -// ErrUserSessionExpired defines the error triggered when the user session has expired -var ( - ErrUserSessionExpired = errors.New("user session missing or expired, please login again") - ErrEmptySessionID = errors.New("session ID cannot be empty") -) - // AuthorizedUserWithSession will return the API user associated with the Session ID if it // exists and hasn't expired, and update session's LastUsed field. -func (o *orm) AuthorizedUserWithSession(sessionID string) (user User, err error) { +// AuthorizedUserWithSession will return the API user associated with the Session ID if it +// exists and hasn't expired, and update session's LastUsed field. +func (o *orm) AuthorizedUserWithSession(sessionID string) (user sessions.User, err error) { if len(sessionID) == 0 { - return User{}, ErrEmptySessionID + return sessions.User{}, sessions.ErrEmptySessionID } email, err := o.findValidSession(sessionID) if err != nil { - return User{}, ErrUserSessionExpired + return sessions.User{}, sessions.ErrUserSessionExpired } user, err = o.findUser(email) if err != nil { - return User{}, ErrUserSessionExpired + return sessions.User{}, sessions.ErrUserSessionExpired } if err := o.updateSessionLastUsed(sessionID); err != nil { - return User{}, err + return sessions.User{}, err } return user, nil @@ -151,8 +126,8 @@ func (o *orm) DeleteUserSession(sessionID string) error { // tokens for the user. This list must be used when logging in (for obvious reasons) but // must also be used for registration to prevent the user from enrolling the same hardware // token multiple times. -func (o *orm) GetUserWebAuthn(email string) ([]WebAuthn, error) { - var uwas []WebAuthn +func (o *orm) GetUserWebAuthn(email string) ([]sessions.WebAuthn, error) { + var uwas []sessions.WebAuthn err := o.q.Select(&uwas, "SELECT email, public_key_data FROM web_authns WHERE LOWER(email) = $1", strings.ToLower(email)) if err != nil { return uwas, err @@ -165,7 +140,7 @@ func (o *orm) GetUserWebAuthn(email string) ([]WebAuthn, error) { // CreateSession will check the password in the SessionRequest against // the hashed API User password in the db. Also will check WebAuthn if it's // enabled for that user. -func (o *orm) CreateSession(sr SessionRequest) (string, error) { +func (o *orm) CreateSession(sr sessions.SessionRequest) (string, error) { user, err := o.FindUser(sr.Email) if err != nil { return "", err @@ -196,7 +171,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // No webauthn tokens registered for the current user, so normal authentication is now complete if len(uwas) == 0 { lggr.Infof("No MFA for user. Creating Session") - session := NewSession() + session := sessions.NewSession() _, err = o.q.Exec("INSERT INTO sessions (id, email, last_used, created_at) VALUES ($1, $2, now(), now())", session.ID, user.Email) o.auditLogger.Audit(audit.AuthLoginSuccessNo2FA, map[string]interface{}{"email": sr.Email}) return session.ID, err @@ -207,7 +182,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // data in the next round trip request (tap key to include webauthn data on the login page) if sr.WebAuthnData == "" { lggr.Warnf("Attempted login to MFA user. Generating challenge for user.") - options, webauthnError := BeginWebAuthnLogin(user, uwas, sr) + options, webauthnError := sessions.BeginWebAuthnLogin(user, uwas, sr) if webauthnError != nil { lggr.Errorf("Could not begin WebAuthn verification: %v", webauthnError) return "", errors.New("MFA Error") @@ -225,7 +200,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { // The user is at the final stage of logging in with MFA. We have an // attestation back from the user, we now need to verify that it is // correct. - err = FinishWebAuthnLogin(user, uwas, sr) + err = sessions.FinishWebAuthnLogin(user, uwas, sr) if err != nil { // The user does have WebAuthn enabled but failed the check @@ -236,7 +211,7 @@ func (o *orm) CreateSession(sr SessionRequest) (string, error) { lggr.Infof("User passed MFA authentication and login will proceed") // This is a success so we can create the sessions - session := NewSession() + session := sessions.NewSession() _, err = o.q.Exec("INSERT INTO sessions (id, email, last_used, created_at) VALUES ($1, $2, now(), now())", session.ID, user.Email) if err != nil { return "", err @@ -271,14 +246,14 @@ func (o *orm) ClearNonCurrentSessions(sessionID string) error { } // CreateUser creates a new API user -func (o *orm) CreateUser(user *User) error { +func (o *orm) CreateUser(user *sessions.User) error { sql := "INSERT INTO users (email, hashed_password, role, created_at, updated_at) VALUES ($1, $2, $3, now(), now()) RETURNING *" return o.q.Get(user, sql, strings.ToLower(user.Email), user.HashedPassword, user.Role) } // UpdateRole overwrites role field of the user specified by email. -func (o *orm) UpdateRole(email, newRole string) (User, error) { - var userToEdit User +func (o *orm) UpdateRole(email, newRole string) (sessions.User, error) { + var userToEdit sessions.User if newRole == "" { return userToEdit, errors.New("user role must be specified") @@ -291,7 +266,7 @@ func (o *orm) UpdateRole(email, newRole string) (User, error) { } // Patch validated role - userRole, err := GetUserRole(newRole) + userRole, err := sessions.GetUserRole(newRole) if err != nil { return err } @@ -316,7 +291,7 @@ func (o *orm) UpdateRole(email, newRole string) (User, error) { } // SetAuthToken updates the user to use the given Authentication Token. -func (o *orm) SetPassword(user *User, newPassword string) error { +func (o *orm) SetPassword(user *sessions.User, newPassword string) error { hashedPassword, err := utils.HashPassword(newPassword) if err != nil { return err @@ -325,7 +300,19 @@ func (o *orm) SetPassword(user *User, newPassword string) error { return o.q.Get(user, sql, hashedPassword, user.Email) } -func (o *orm) CreateAndSetAuthToken(user *User) (*auth.Token, error) { +// TestPassword checks plaintext user provided password with hashed database password, returns nil if matched +func (o *orm) TestPassword(email string, password string) error { + var hashedPassword string + if err := o.q.Get(&hashedPassword, "SELECT hashed_password FROM users WHERE lower(email) = lower($1)", email); err != nil { + return errors.New("no matching user for provided email") + } + if !utils.CheckPasswordHash(password, hashedPassword) { + return errors.New("passwords don't match") + } + return nil +} + +func (o *orm) CreateAndSetAuthToken(user *sessions.User) (*auth.Token, error) { newToken := auth.NewToken() err := o.SetAuthToken(user, newToken) @@ -337,7 +324,7 @@ func (o *orm) CreateAndSetAuthToken(user *User) (*auth.Token, error) { } // SetAuthToken updates the user to use the given Authentication Token. -func (o *orm) SetAuthToken(user *User, token *auth.Token) error { +func (o *orm) SetAuthToken(user *sessions.User, token *auth.Token) error { salt := utils.NewSecret(utils.DefaultSecretSize) hashedSecret, err := auth.HashedSecret(token, salt) if err != nil { @@ -348,20 +335,20 @@ func (o *orm) SetAuthToken(user *User, token *auth.Token) error { } // DeleteAuthToken clears and disables the users Authentication Token. -func (o *orm) DeleteAuthToken(user *User) error { +func (o *orm) DeleteAuthToken(user *sessions.User) error { sql := "UPDATE users SET token_salt = '', token_key = '', token_hashed_secret = '', updated_at = now() WHERE email = $1 RETURNING *" return o.q.Get(user, sql, user.Email) } // SaveWebAuthn saves new WebAuthn token information. -func (o *orm) SaveWebAuthn(token *WebAuthn) error { +func (o *orm) SaveWebAuthn(token *sessions.WebAuthn) error { sql := "INSERT INTO web_authns (email, public_key_data) VALUES ($1, $2)" _, err := o.q.Exec(sql, token.Email, token.PublicKeyData) return err } // Sessions returns all sessions limited by the parameters. -func (o *orm) Sessions(offset, limit int) (sessions []Session, err error) { +func (o *orm) Sessions(offset, limit int) (sessions []sessions.Session, err error) { sql := `SELECT * FROM sessions ORDER BY created_at, id LIMIT $1 OFFSET $2;` if err = o.q.Select(&sessions, sql, limit, offset); err != nil { return diff --git a/core/sessions/orm_test.go b/core/sessions/localauth/orm_test.go similarity index 95% rename from core/sessions/orm_test.go rename to core/sessions/localauth/orm_test.go index 5decb823086..7868937ad08 100644 --- a/core/sessions/orm_test.go +++ b/core/sessions/localauth/orm_test.go @@ -1,4 +1,4 @@ -package sessions_test +package localauth_test import ( "encoding/json" @@ -7,6 +7,7 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/webauthn" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,14 +19,15 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" "github.com/smartcontractkit/chainlink/v2/core/utils" ) -func setupORM(t *testing.T) (*sqlx.DB, sessions.ORM) { +func setupORM(t *testing.T) (*sqlx.DB, sessions.AuthenticationProvider) { t.Helper() db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, time.Minute, logger.TestLogger(t), pgtest.NewQConfig(true), &audit.AuditLoggerService{}) + orm := localauth.NewORM(db, time.Minute, logger.TestLogger(t), pgtest.NewQConfig(true), &audit.AuditLoggerService{}) return db, orm } @@ -66,7 +68,7 @@ func TestORM_AuthorizedUserWithSession(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - orm := sessions.NewORM(db, test.sessionDuration, logger.TestLogger(t), pgtest.NewQConfig(true), &audit.AuditLoggerService{}) + orm := localauth.NewORM(db, test.sessionDuration, logger.TestLogger(t), pgtest.NewQConfig(true), &audit.AuditLoggerService{}) user := cltest.MustRandomUser(t) require.NoError(t, orm.CreateUser(&user)) diff --git a/core/sessions/reaper.go b/core/sessions/localauth/reaper.go similarity index 98% rename from core/sessions/reaper.go rename to core/sessions/localauth/reaper.go index c4f0ed6796c..77d1b1abef2 100644 --- a/core/sessions/reaper.go +++ b/core/sessions/localauth/reaper.go @@ -1,4 +1,4 @@ -package sessions +package localauth import ( "database/sql" diff --git a/core/sessions/reaper_test.go b/core/sessions/localauth/reaper_test.go similarity index 69% rename from core/sessions/reaper_test.go rename to core/sessions/localauth/reaper_test.go index a96c3822ef5..43a263d0321 100644 --- a/core/sessions/reaper_test.go +++ b/core/sessions/localauth/reaper_test.go @@ -1,4 +1,4 @@ -package sessions_test +package localauth_test import ( "testing" @@ -9,8 +9,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/sessions" + "github.com/smartcontractkit/chainlink/v2/core/sessions/localauth" "github.com/smartcontractkit/chainlink/v2/core/store/models" + "github.com/onsi/gomega" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,10 +33,9 @@ func TestSessionReaper_ReapSessions(t *testing.T) { db := pgtest.NewSqlxDB(t) config := sessionReaperConfig{} lggr := logger.TestLogger(t) - orm := sessions.NewORM(db, config.SessionTimeout().Duration(), lggr, pgtest.NewQConfig(true), audit.NoopLogger) - - r := sessions.NewSessionReaper(db.DB, config, lggr) + orm := localauth.NewORM(db, config.SessionTimeout().Duration(), lggr, pgtest.NewQConfig(true), audit.NoopLogger) + r := localauth.NewSessionReaper(db.DB, config, lggr) t.Cleanup(func() { assert.NoError(t, r.Stop()) }) @@ -53,31 +54,28 @@ func TestSessionReaper_ReapSessions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - user := cltest.MustRandomUser(t) - require.NoError(t, orm.CreateUser(&user)) - - session := sessions.NewSession() - session.Email = user.Email - - _, err := db.Exec("INSERT INTO sessions (last_used, email, id, created_at) VALUES ($1, $2, $3, now())", test.lastUsed, user.Email, test.name) - require.NoError(t, err) - t.Cleanup(func() { - _, err2 := db.Exec("DELETE FROM sessions where email = $1", user.Email) + _, err2 := db.Exec("DELETE FROM sessions where email = $1", cltest.APIEmailAdmin) require.NoError(t, err2) }) + _, err := db.Exec("INSERT INTO sessions (last_used, email, id, created_at) VALUES ($1, $2, $3, now())", test.lastUsed, cltest.APIEmailAdmin, test.name) + require.NoError(t, err) + r.WakeUp() - <-r.(interface { - WorkDone() <-chan struct{} - }).WorkDone() - sessions, err := orm.Sessions(0, 10) - assert.NoError(t, err) if test.wantReap { - assert.Len(t, sessions, 0) + gomega.NewWithT(t).Eventually(func() []sessions.Session { + sessions, err := orm.Sessions(0, 10) + assert.NoError(t, err) + return sessions + }).Should(gomega.HaveLen(0)) } else { - assert.Len(t, sessions, 1) + gomega.NewWithT(t).Consistently(func() []sessions.Session { + sessions, err := orm.Sessions(0, 10) + assert.NoError(t, err) + return sessions + }).Should(gomega.HaveLen(1)) } }) } diff --git a/core/sessions/mocks/orm.go b/core/sessions/mocks/authentication_provider.go similarity index 75% rename from core/sessions/mocks/orm.go rename to core/sessions/mocks/authentication_provider.go index 5699b9f8892..d6e33d11e45 100644 --- a/core/sessions/mocks/orm.go +++ b/core/sessions/mocks/authentication_provider.go @@ -11,13 +11,13 @@ import ( sessions "github.com/smartcontractkit/chainlink/v2/core/sessions" ) -// ORM is an autogenerated mock type for the ORM type -type ORM struct { +// AuthenticationProvider is an autogenerated mock type for the AuthenticationProvider type +type AuthenticationProvider struct { mock.Mock } // AuthorizedUserWithSession provides a mock function with given fields: sessionID -func (_m *ORM) AuthorizedUserWithSession(sessionID string) (sessions.User, error) { +func (_m *AuthenticationProvider) AuthorizedUserWithSession(sessionID string) (sessions.User, error) { ret := _m.Called(sessionID) var r0 sessions.User @@ -41,7 +41,7 @@ func (_m *ORM) AuthorizedUserWithSession(sessionID string) (sessions.User, error } // ClearNonCurrentSessions provides a mock function with given fields: sessionID -func (_m *ORM) ClearNonCurrentSessions(sessionID string) error { +func (_m *AuthenticationProvider) ClearNonCurrentSessions(sessionID string) error { ret := _m.Called(sessionID) var r0 error @@ -55,7 +55,7 @@ func (_m *ORM) ClearNonCurrentSessions(sessionID string) error { } // CreateAndSetAuthToken provides a mock function with given fields: user -func (_m *ORM) CreateAndSetAuthToken(user *sessions.User) (*auth.Token, error) { +func (_m *AuthenticationProvider) CreateAndSetAuthToken(user *sessions.User) (*auth.Token, error) { ret := _m.Called(user) var r0 *auth.Token @@ -81,7 +81,7 @@ func (_m *ORM) CreateAndSetAuthToken(user *sessions.User) (*auth.Token, error) { } // CreateSession provides a mock function with given fields: sr -func (_m *ORM) CreateSession(sr sessions.SessionRequest) (string, error) { +func (_m *AuthenticationProvider) CreateSession(sr sessions.SessionRequest) (string, error) { ret := _m.Called(sr) var r0 string @@ -105,7 +105,7 @@ func (_m *ORM) CreateSession(sr sessions.SessionRequest) (string, error) { } // CreateUser provides a mock function with given fields: user -func (_m *ORM) CreateUser(user *sessions.User) error { +func (_m *AuthenticationProvider) CreateUser(user *sessions.User) error { ret := _m.Called(user) var r0 error @@ -119,7 +119,7 @@ func (_m *ORM) CreateUser(user *sessions.User) error { } // DeleteAuthToken provides a mock function with given fields: user -func (_m *ORM) DeleteAuthToken(user *sessions.User) error { +func (_m *AuthenticationProvider) DeleteAuthToken(user *sessions.User) error { ret := _m.Called(user) var r0 error @@ -133,7 +133,7 @@ func (_m *ORM) DeleteAuthToken(user *sessions.User) error { } // DeleteUser provides a mock function with given fields: email -func (_m *ORM) DeleteUser(email string) error { +func (_m *AuthenticationProvider) DeleteUser(email string) error { ret := _m.Called(email) var r0 error @@ -147,7 +147,7 @@ func (_m *ORM) DeleteUser(email string) error { } // DeleteUserSession provides a mock function with given fields: sessionID -func (_m *ORM) DeleteUserSession(sessionID string) error { +func (_m *AuthenticationProvider) DeleteUserSession(sessionID string) error { ret := _m.Called(sessionID) var r0 error @@ -161,7 +161,7 @@ func (_m *ORM) DeleteUserSession(sessionID string) error { } // FindExternalInitiator provides a mock function with given fields: eia -func (_m *ORM) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiator, error) { +func (_m *AuthenticationProvider) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiator, error) { ret := _m.Called(eia) var r0 *bridges.ExternalInitiator @@ -187,7 +187,7 @@ func (_m *ORM) FindExternalInitiator(eia *auth.Token) (*bridges.ExternalInitiato } // FindUser provides a mock function with given fields: email -func (_m *ORM) FindUser(email string) (sessions.User, error) { +func (_m *AuthenticationProvider) FindUser(email string) (sessions.User, error) { ret := _m.Called(email) var r0 sessions.User @@ -211,7 +211,7 @@ func (_m *ORM) FindUser(email string) (sessions.User, error) { } // FindUserByAPIToken provides a mock function with given fields: apiToken -func (_m *ORM) FindUserByAPIToken(apiToken string) (sessions.User, error) { +func (_m *AuthenticationProvider) FindUserByAPIToken(apiToken string) (sessions.User, error) { ret := _m.Called(apiToken) var r0 sessions.User @@ -235,7 +235,7 @@ func (_m *ORM) FindUserByAPIToken(apiToken string) (sessions.User, error) { } // GetUserWebAuthn provides a mock function with given fields: email -func (_m *ORM) GetUserWebAuthn(email string) ([]sessions.WebAuthn, error) { +func (_m *AuthenticationProvider) GetUserWebAuthn(email string) ([]sessions.WebAuthn, error) { ret := _m.Called(email) var r0 []sessions.WebAuthn @@ -261,7 +261,7 @@ func (_m *ORM) GetUserWebAuthn(email string) ([]sessions.WebAuthn, error) { } // ListUsers provides a mock function with given fields: -func (_m *ORM) ListUsers() ([]sessions.User, error) { +func (_m *AuthenticationProvider) ListUsers() ([]sessions.User, error) { ret := _m.Called() var r0 []sessions.User @@ -287,7 +287,7 @@ func (_m *ORM) ListUsers() ([]sessions.User, error) { } // SaveWebAuthn provides a mock function with given fields: token -func (_m *ORM) SaveWebAuthn(token *sessions.WebAuthn) error { +func (_m *AuthenticationProvider) SaveWebAuthn(token *sessions.WebAuthn) error { ret := _m.Called(token) var r0 error @@ -301,7 +301,7 @@ func (_m *ORM) SaveWebAuthn(token *sessions.WebAuthn) error { } // Sessions provides a mock function with given fields: offset, limit -func (_m *ORM) Sessions(offset int, limit int) ([]sessions.Session, error) { +func (_m *AuthenticationProvider) Sessions(offset int, limit int) ([]sessions.Session, error) { ret := _m.Called(offset, limit) var r0 []sessions.Session @@ -327,7 +327,7 @@ func (_m *ORM) Sessions(offset int, limit int) ([]sessions.Session, error) { } // SetAuthToken provides a mock function with given fields: user, token -func (_m *ORM) SetAuthToken(user *sessions.User, token *auth.Token) error { +func (_m *AuthenticationProvider) SetAuthToken(user *sessions.User, token *auth.Token) error { ret := _m.Called(user, token) var r0 error @@ -341,7 +341,7 @@ func (_m *ORM) SetAuthToken(user *sessions.User, token *auth.Token) error { } // SetPassword provides a mock function with given fields: user, newPassword -func (_m *ORM) SetPassword(user *sessions.User, newPassword string) error { +func (_m *AuthenticationProvider) SetPassword(user *sessions.User, newPassword string) error { ret := _m.Called(user, newPassword) var r0 error @@ -354,8 +354,22 @@ func (_m *ORM) SetPassword(user *sessions.User, newPassword string) error { return r0 } +// TestPassword provides a mock function with given fields: email, password +func (_m *AuthenticationProvider) TestPassword(email string, password string) error { + ret := _m.Called(email, password) + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(email, password) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateRole provides a mock function with given fields: email, newRole -func (_m *ORM) UpdateRole(email string, newRole string) (sessions.User, error) { +func (_m *AuthenticationProvider) UpdateRole(email string, newRole string) (sessions.User, error) { ret := _m.Called(email, newRole) var r0 sessions.User @@ -378,13 +392,13 @@ func (_m *ORM) UpdateRole(email string, newRole string) (sessions.User, error) { return r0, r1 } -// NewORM creates a new instance of ORM. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// NewAuthenticationProvider creates a new instance of AuthenticationProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. -func NewORM(t interface { +func NewAuthenticationProvider(t interface { mock.TestingT Cleanup(func()) -}) *ORM { - mock := &ORM{} +}) *AuthenticationProvider { + mock := &AuthenticationProvider{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/sessions/mocks/basic_admin_users_orm.go b/core/sessions/mocks/basic_admin_users_orm.go new file mode 100644 index 00000000000..845e2d8880e --- /dev/null +++ b/core/sessions/mocks/basic_admin_users_orm.go @@ -0,0 +1,91 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package mocks + +import ( + sessions "github.com/smartcontractkit/chainlink/v2/core/sessions" + mock "github.com/stretchr/testify/mock" +) + +// BasicAdminUsersORM is an autogenerated mock type for the BasicAdminUsersORM type +type BasicAdminUsersORM struct { + mock.Mock +} + +// CreateUser provides a mock function with given fields: user +func (_m *BasicAdminUsersORM) CreateUser(user *sessions.User) error { + ret := _m.Called(user) + + var r0 error + if rf, ok := ret.Get(0).(func(*sessions.User) error); ok { + r0 = rf(user) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// FindUser provides a mock function with given fields: email +func (_m *BasicAdminUsersORM) FindUser(email string) (sessions.User, error) { + ret := _m.Called(email) + + var r0 sessions.User + var r1 error + if rf, ok := ret.Get(0).(func(string) (sessions.User, error)); ok { + return rf(email) + } + if rf, ok := ret.Get(0).(func(string) sessions.User); ok { + r0 = rf(email) + } else { + r0 = ret.Get(0).(sessions.User) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(email) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListUsers provides a mock function with given fields: +func (_m *BasicAdminUsersORM) ListUsers() ([]sessions.User, error) { + ret := _m.Called() + + var r0 []sessions.User + var r1 error + if rf, ok := ret.Get(0).(func() ([]sessions.User, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []sessions.User); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]sessions.User) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewBasicAdminUsersORM creates a new instance of BasicAdminUsersORM. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBasicAdminUsersORM(t interface { + mock.TestingT + Cleanup(func()) +}) *BasicAdminUsersORM { + mock := &BasicAdminUsersORM{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/sessions/session.go b/core/sessions/session.go new file mode 100644 index 00000000000..90964596e9a --- /dev/null +++ b/core/sessions/session.go @@ -0,0 +1,74 @@ +package sessions + +import ( + "crypto/subtle" + "time" + + "github.com/pkg/errors" + "gopkg.in/guregu/null.v4" + + "github.com/smartcontractkit/chainlink/v2/core/auth" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +// SessionRequest encapsulates the fields needed to generate a new SessionID, +// including the hashed password. +type SessionRequest struct { + Email string `json:"email"` + Password string `json:"password"` + WebAuthnData string `json:"webauthndata"` + WebAuthnConfig WebAuthnConfiguration + SessionStore *WebAuthnSessionStore +} + +// Session holds the unique id for the authenticated session. +type Session struct { + ID string `json:"id"` + Email string `json:"email"` + LastUsed time.Time `json:"lastUsed"` + CreatedAt time.Time `json:"createdAt"` +} + +// NewSession returns a session instance with ID set to a random ID and +// LastUsed to now. +func NewSession() Session { + return Session{ + ID: utils.NewBytes32ID(), + LastUsed: time.Now(), + } +} + +// Changeauth.TokenRequest is sent when updating a User's authentication token. +type ChangeAuthTokenRequest struct { + Password string `json:"password"` +} + +// GenerateAuthToken randomly generates and sets the users Authentication +// Token. +func (u *User) GenerateAuthToken() (*auth.Token, error) { + token := auth.NewToken() + return token, u.SetAuthToken(token) +} + +// SetAuthToken updates the user to use the given Authentication Token. +func (u *User) SetAuthToken(token *auth.Token) error { + salt := utils.NewSecret(utils.DefaultSecretSize) + hashedSecret, err := auth.HashedSecret(token, salt) + if err != nil { + return errors.Wrap(err, "user") + } + u.TokenSalt = null.StringFrom(salt) + u.TokenKey = null.StringFrom(token.AccessKey) + u.TokenHashedSecret = null.StringFrom(hashedSecret) + return nil +} + +// AuthenticateUserByToken returns true on successful authentication of the +// user against the given Authentication Token. +func AuthenticateUserByToken(token *auth.Token, user *User) (bool, error) { + hashedSecret, err := auth.HashedSecret(token, user.TokenSalt.ValueOrZero()) + if err != nil { + return false, err + } + return subtle.ConstantTimeCompare([]byte(hashedSecret), []byte(user.TokenHashedSecret.ValueOrZero())) == 1, nil +} diff --git a/core/sessions/user.go b/core/sessions/user.go index a1208744323..f2e4827b922 100644 --- a/core/sessions/user.go +++ b/core/sessions/user.go @@ -1,7 +1,6 @@ package sessions import ( - "crypto/subtle" "fmt" "net/mail" "time" @@ -9,7 +8,6 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -108,65 +106,3 @@ func GetUserRole(role string) (UserRole, error) { ) return UserRole(""), errors.New(errStr) } - -// SessionRequest encapsulates the fields needed to generate a new SessionID, -// including the hashed password. -type SessionRequest struct { - Email string `json:"email"` - Password string `json:"password"` - WebAuthnData string `json:"webauthndata"` - WebAuthnConfig WebAuthnConfiguration - SessionStore *WebAuthnSessionStore -} - -// Session holds the unique id for the authenticated session. -type Session struct { - ID string `json:"id"` - Email string `json:"email"` - LastUsed time.Time `json:"lastUsed"` - CreatedAt time.Time `json:"createdAt"` -} - -// NewSession returns a session instance with ID set to a random ID and -// LastUsed to now. -func NewSession() Session { - return Session{ - ID: utils.NewBytes32ID(), - LastUsed: time.Now(), - } -} - -// Changeauth.TokenRequest is sent when updating a User's authentication token. -type ChangeAuthTokenRequest struct { - Password string `json:"password"` -} - -// GenerateAuthToken randomly generates and sets the users Authentication -// Token. -func (u *User) GenerateAuthToken() (*auth.Token, error) { - token := auth.NewToken() - return token, u.SetAuthToken(token) -} - -// SetAuthToken updates the user to use the given Authentication Token. -func (u *User) SetAuthToken(token *auth.Token) error { - salt := utils.NewSecret(utils.DefaultSecretSize) - hashedSecret, err := auth.HashedSecret(token, salt) - if err != nil { - return errors.Wrap(err, "user") - } - u.TokenSalt = null.StringFrom(salt) - u.TokenKey = null.StringFrom(token.AccessKey) - u.TokenHashedSecret = null.StringFrom(hashedSecret) - return nil -} - -// AuthenticateUserByToken returns true on successful authentication of the -// user against the given Authentication Token. -func AuthenticateUserByToken(token *auth.Token, user *User) (bool, error) { - hashedSecret, err := auth.HashedSecret(token, user.TokenSalt.ValueOrZero()) - if err != nil { - return false, err - } - return subtle.ConstantTimeCompare([]byte(hashedSecret), []byte(user.TokenHashedSecret.ValueOrZero())) == 1, nil -} diff --git a/core/sessions/webauthn.go b/core/sessions/webauthn.go index 0dd8242dc8a..41e31d7aaa8 100644 --- a/core/sessions/webauthn.go +++ b/core/sessions/webauthn.go @@ -279,7 +279,7 @@ func (store *WebAuthnSessionStore) GetWebauthnSession(key string) (data webauthn return } -func AddCredentialToUser(o ORM, email string, credential *webauthn.Credential) error { +func AddCredentialToUser(ap AuthenticationProvider, email string, credential *webauthn.Credential) error { credj, err := json.Marshal(credential) if err != nil { return err @@ -289,5 +289,5 @@ func AddCredentialToUser(o ORM, email string, credential *webauthn.Credential) e Email: email, PublicKeyData: sqlxTypes.JSONText(credj), } - return o.SaveWebAuthn(&token) + return ap.SaveWebAuthn(&token) } diff --git a/core/store/migrate/migrations/0208_create_ldap_sessions_table.sql b/core/store/migrate/migrations/0208_create_ldap_sessions_table.sql new file mode 100644 index 00000000000..f788cdab076 --- /dev/null +++ b/core/store/migrate/migrations/0208_create_ldap_sessions_table.sql @@ -0,0 +1,22 @@ +-- +goose Up +CREATE TABLE IF NOT EXISTS ldap_sessions ( + id text PRIMARY KEY, + user_email text NOT NULL, + user_role user_roles, + localauth_user BOOLEAN, + created_at timestamp with time zone NOT NULL +); + +CREATE TABLE IF NOT EXISTS ldap_user_api_tokens ( + user_email text PRIMARY KEY, + user_role user_roles, + localauth_user BOOLEAN, + token_key text UNIQUE NOT NULL, + token_salt text NOT NULL, + token_hashed_secret text NOT NULL, + created_at timestamp with time zone NOT NULL +); + +-- +goose Down +DROP TABLE ldap_sessions; +DROP TABLE ldap_user_api_tokens; diff --git a/core/web/auth/auth.go b/core/web/auth/auth.go index a0a9df58c79..c2458f52627 100644 --- a/core/web/auth/auth.go +++ b/core/web/auth/auth.go @@ -78,6 +78,9 @@ func AuthenticateByToken(c *gin.Context, authr Authenticator) error { AccessKey: c.GetHeader(APIKey), Secret: c.GetHeader(APISecret), } + if token.AccessKey == "" { + return auth.ErrorAuthFailed + } if token.AccessKey == "" { return auth.ErrorAuthFailed @@ -86,7 +89,7 @@ func AuthenticateByToken(c *gin.Context, authr Authenticator) error { // We need to first load the user row so we can compare tokens using the stored salt user, err := authr.FindUserByAPIToken(token.AccessKey) if err != nil { - if errors.Is(err, sql.ErrNoRows) { + if errors.Is(err, sql.ErrNoRows) || errors.Is(err, clsessions.ErrUserSessionExpired) { return auth.ErrorAuthFailed } return err diff --git a/core/web/auth/auth_test.go b/core/web/auth/auth_test.go index 896542915ae..f0b4e5068fb 100644 --- a/core/web/auth/auth_test.go +++ b/core/web/auth/auth_test.go @@ -33,7 +33,7 @@ func authSuccess(*gin.Context, webauth.Authenticator) error { } type userFindFailer struct { - sessions.ORM + sessions.AuthenticationProvider err error } @@ -46,7 +46,7 @@ func (u userFindFailer) FindUserByAPIToken(token string) (sessions.User, error) } type userFindSuccesser struct { - sessions.ORM + sessions.AuthenticationProvider user sessions.User } diff --git a/core/web/auth/gql_test.go b/core/web/auth/gql_test.go index 4688f62a336..4f3f8e27baf 100644 --- a/core/web/auth/gql_test.go +++ b/core/web/auth/gql_test.go @@ -21,7 +21,7 @@ import ( func Test_AuthenticateGQL_Unauthenticated(t *testing.T) { t.Parallel() - sessionORM := mocks.NewORM(t) + sessionORM := mocks.NewAuthenticationProvider(t) sessionStore := cookie.NewStore([]byte("secret")) r := gin.Default() @@ -44,7 +44,7 @@ func Test_AuthenticateGQL_Unauthenticated(t *testing.T) { func Test_AuthenticateGQL_Authenticated(t *testing.T) { t.Parallel() - sessionORM := mocks.NewORM(t) + sessionORM := mocks.NewAuthenticationProvider(t) sessionStore := cookie.NewStore([]byte(cltest.SessionSecret)) sessionID := "sessionID" diff --git a/core/web/resolver/api_token_test.go b/core/web/resolver/api_token_test.go index b5ed52be3c5..fae0204caf5 100644 --- a/core/web/resolver/api_token_test.go +++ b/core/web/resolver/api_token_test.go @@ -39,6 +39,11 @@ func TestResolver_CreateAPIToken(t *testing.T) { "password": defaultPassword, }, } + variablesIncorrect := map[string]interface{}{ + "input": map[string]interface{}{ + "password": "wrong-password", + }, + } gError := errors.New("error") testCases := []GQLTestCase{ @@ -56,12 +61,13 @@ func TestResolver_CreateAPIToken(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("CreateAndSetAuthToken", session.User).Return(&auth.Token{ + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("TestPassword", session.User.Email, defaultPassword).Return(nil) + f.Mocks.authProvider.On("CreateAndSetAuthToken", session.User).Return(&auth.Token{ Secret: "new-secret", AccessKey: "new-access-key", }, nil) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -83,13 +89,12 @@ func TestResolver_CreateAPIToken(t *testing.T) { require.True(t, ok) require.NotNil(t, session) - session.User.HashedPassword = "wrong-password" - - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("TestPassword", session.User.Email, "wrong-password").Return(gError) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, - variables: variables, + variables: variablesIncorrect, result: ` { "createAPIToken": { @@ -114,8 +119,8 @@ func TestResolver_CreateAPIToken(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, gError) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, gError) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -142,9 +147,10 @@ func TestResolver_CreateAPIToken(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("CreateAndSetAuthToken", session.User).Return(nil, gError) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("TestPassword", session.User.Email, defaultPassword).Return(nil) + f.Mocks.authProvider.On("CreateAndSetAuthToken", session.User).Return(nil, gError) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -189,6 +195,11 @@ func TestResolver_DeleteAPIToken(t *testing.T) { "password": defaultPassword, }, } + variablesIncorrect := map[string]interface{}{ + "input": map[string]interface{}{ + "password": "wrong-password", + }, + } gError := errors.New("error") testCases := []GQLTestCase{ @@ -208,9 +219,10 @@ func TestResolver_DeleteAPIToken(t *testing.T) { err = session.User.TokenKey.UnmarshalText([]byte("new-access-key")) require.NoError(t, err) - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("DeleteAuthToken", session.User).Return(nil) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("TestPassword", session.User.Email, defaultPassword).Return(nil) + f.Mocks.authProvider.On("DeleteAuthToken", session.User).Return(nil) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -231,13 +243,12 @@ func TestResolver_DeleteAPIToken(t *testing.T) { require.True(t, ok) require.NotNil(t, session) - session.User.HashedPassword = "wrong-password" - - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("TestPassword", session.User.Email, "wrong-password").Return(gError) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, - variables: variables, + variables: variablesIncorrect, result: ` { "deleteAPIToken": { @@ -262,8 +273,8 @@ func TestResolver_DeleteAPIToken(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, gError) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, gError) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -290,9 +301,10 @@ func TestResolver_DeleteAPIToken(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("DeleteAuthToken", session.User).Return(gError) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("TestPassword", session.User.Email, defaultPassword).Return(nil) + f.Mocks.authProvider.On("DeleteAuthToken", session.User).Return(gError) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 68cbb0b7896..f9eee0734a3 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -882,7 +882,7 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { return nil, errors.New("couldn't retrieve user session") } - dbUser, err := r.App.SessionORM().FindUser(session.User.Email) + dbUser, err := r.App.AuthenticationProvider().FindUser(session.User.Email) if err != nil { return nil, err } @@ -895,11 +895,11 @@ func (r *Resolver) UpdateUserPassword(ctx context.Context, args struct { }), nil } - if err = r.App.SessionORM().ClearNonCurrentSessions(session.SessionID); err != nil { + if err = r.App.AuthenticationProvider().ClearNonCurrentSessions(session.SessionID); err != nil { return nil, clearSessionsError{} } - err = r.App.SessionORM().SetPassword(&dbUser, args.Input.NewPassword) + err = r.App.AuthenticationProvider().SetPassword(&dbUser, args.Input.NewPassword) if err != nil { return nil, failedPasswordUpdateError{} } @@ -937,12 +937,13 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { if !ok { return nil, errors.New("Failed to obtain current user from context") } - dbUser, err := r.App.SessionORM().FindUser(session.User.Email) + dbUser, err := r.App.AuthenticationProvider().FindUser(session.User.Email) if err != nil { return nil, err } - if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { + err = r.App.AuthenticationProvider().TestPassword(dbUser.Email, args.Input.Password) + if err != nil { r.App.GetAuditLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewCreateAPITokenPayload(nil, map[string]string{ @@ -950,7 +951,7 @@ func (r *Resolver) CreateAPIToken(ctx context.Context, args struct { }), nil } - newToken, err := r.App.SessionORM().CreateAndSetAuthToken(&dbUser) + newToken, err := r.App.AuthenticationProvider().CreateAndSetAuthToken(&dbUser) if err != nil { return nil, err } @@ -970,12 +971,13 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { if !ok { return nil, errors.New("Failed to obtain current user from context") } - dbUser, err := r.App.SessionORM().FindUser(session.User.Email) + dbUser, err := r.App.AuthenticationProvider().FindUser(session.User.Email) if err != nil { return nil, err } - if !utils.CheckPasswordHash(args.Input.Password, dbUser.HashedPassword) { + err = r.App.AuthenticationProvider().TestPassword(dbUser.Email, args.Input.Password) + if err != nil { r.App.GetAuditLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": dbUser.Email}) return NewDeleteAPITokenPayload(nil, map[string]string{ @@ -983,7 +985,7 @@ func (r *Resolver) DeleteAPIToken(ctx context.Context, args struct { }), nil } - err = r.App.SessionORM().DeleteAuthToken(&dbUser) + err = r.App.AuthenticationProvider().DeleteAuthToken(&dbUser) if err != nil { return nil, err } diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index fa8471c5e2b..85c495faaae 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -27,7 +27,7 @@ import ( pipelineMocks "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" webhookmocks "github.com/smartcontractkit/chainlink/v2/core/services/webhook/mocks" clsessions "github.com/smartcontractkit/chainlink/v2/core/sessions" - sessionsMocks "github.com/smartcontractkit/chainlink/v2/core/sessions/mocks" + authProviderMocks "github.com/smartcontractkit/chainlink/v2/core/sessions/mocks" "github.com/smartcontractkit/chainlink/v2/core/web/auth" "github.com/smartcontractkit/chainlink/v2/core/web/loader" "github.com/smartcontractkit/chainlink/v2/core/web/schema" @@ -37,7 +37,7 @@ type mocks struct { bridgeORM *bridgeORMMocks.ORM evmORM *evmtest.TestConfigs jobORM *jobORMMocks.ORM - sessionsORM *sessionsMocks.ORM + authProvider *authProviderMocks.AuthenticationProvider pipelineORM *pipelineMocks.ORM feedsSvc *feedsMocks.Service cfg *chainlinkMocks.GeneralConfig @@ -97,7 +97,7 @@ func setupFramework(t *testing.T) *gqlTestFramework { evmORM: evmtest.NewTestConfigs(), jobORM: jobORMMocks.NewORM(t), feedsSvc: feedsMocks.NewService(t), - sessionsORM: sessionsMocks.NewORM(t), + authProvider: authProviderMocks.NewAuthenticationProvider(t), pipelineORM: pipelineMocks.NewORM(t), cfg: chainlinkMocks.NewGeneralConfig(t), scfg: evmConfigMocks.NewChainScopedConfig(t), diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index 48d432138a8..f5d775fe744 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -61,6 +61,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -73,6 +74,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index f44f119075d..95d898c353b 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -67,6 +67,7 @@ MaxAgeDays = 17 MaxBackups = 9 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = '*' BridgeResponseURL = 'https://bridge.response' BridgeCacheTTL = '10s' @@ -79,6 +80,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '192.158.1.37' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = 'test-rpid' RPOrigin = 'test-rp-origin' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 1dcbfe3a830..9dd0be8f5d2 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -61,6 +61,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -73,6 +74,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/core/web/resolver/user_test.go b/core/web/resolver/user_test.go index e3808eebcbb..bc64beeb459 100644 --- a/core/web/resolver/user_test.go +++ b/core/web/resolver/user_test.go @@ -53,10 +53,10 @@ func TestResolver_UpdateUserPassword(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("SetPassword", session.User, "new").Return(nil) - f.Mocks.sessionsORM.On("ClearNonCurrentSessions", session.SessionID).Return(nil) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("SetPassword", session.User, "new").Return(nil) + f.Mocks.authProvider.On("ClearNonCurrentSessions", session.SessionID).Return(nil) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -79,8 +79,8 @@ func TestResolver_UpdateUserPassword(t *testing.T) { session.User.HashedPassword = "random-string" - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -108,11 +108,11 @@ func TestResolver_UpdateUserPassword(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("ClearNonCurrentSessions", session.SessionID).Return( + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("ClearNonCurrentSessions", session.SessionID).Return( clearSessionsError{}, ) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, @@ -139,10 +139,10 @@ func TestResolver_UpdateUserPassword(t *testing.T) { session.User.HashedPassword = pwd - f.Mocks.sessionsORM.On("FindUser", session.User.Email).Return(*session.User, nil) - f.Mocks.sessionsORM.On("ClearNonCurrentSessions", session.SessionID).Return(nil) - f.Mocks.sessionsORM.On("SetPassword", session.User, "new").Return(failedPasswordUpdateError{}) - f.App.On("SessionORM").Return(f.Mocks.sessionsORM) + f.Mocks.authProvider.On("FindUser", session.User.Email).Return(*session.User, nil) + f.Mocks.authProvider.On("ClearNonCurrentSessions", session.SessionID).Return(nil) + f.Mocks.authProvider.On("SetPassword", session.User, "new").Return(failedPasswordUpdateError{}) + f.App.On("AuthenticationProvider").Return(f.Mocks.authProvider) }, query: mutation, variables: variables, diff --git a/core/web/router.go b/core/web/router.go index a873f14b708..28bd4f2170c 100644 --- a/core/web/router.go +++ b/core/web/router.go @@ -90,7 +90,7 @@ func NewRouter(app chainlink.Application, prometheus *ginprom.Prometheus) (*gin. guiAssetRoutes(engine, config.Insecure().DisableRateLimiting(), app.GetLogger()) api.POST("/query", - auth.AuthenticateGQL(app.SessionORM(), app.GetLogger().Named("GQLHandler")), + auth.AuthenticateGQL(app.AuthenticationProvider(), app.GetLogger().Named("GQLHandler")), loader.Middleware(app), graphqlHandler(app), ) @@ -170,7 +170,7 @@ func secureMiddleware(tlsRedirect bool, tlsHost string, devWebServer bool) gin.H } func debugRoutes(app chainlink.Application, r *gin.RouterGroup) { - group := r.Group("/debug", auth.Authenticate(app.SessionORM(), auth.AuthenticateBySession)) + group := r.Group("/debug", auth.Authenticate(app.AuthenticationProvider(), auth.AuthenticateBySession)) group.GET("/vars", expvar.Handler()) } @@ -207,7 +207,7 @@ func sessionRoutes(app chainlink.Application, r *gin.RouterGroup) { )) sc := NewSessionsController(app) unauth.POST("/sessions", sc.Create) - auth := r.Group("/", auth.Authenticate(app.SessionORM(), auth.AuthenticateBySession)) + auth := r.Group("/", auth.Authenticate(app.AuthenticationProvider(), auth.AuthenticateBySession)) auth.DELETE("/sessions", sc.Destroy) } @@ -231,7 +231,7 @@ func v2Routes(app chainlink.Application, r *gin.RouterGroup) { psec := PipelineJobSpecErrorsController{app} unauthedv2.PATCH("/resume/:runID", prc.Resume) - authv2 := r.Group("/v2", auth.Authenticate(app.SessionORM(), + authv2 := r.Group("/v2", auth.Authenticate(app.AuthenticationProvider(), auth.AuthenticateByToken, auth.AuthenticateBySession, )) @@ -301,7 +301,7 @@ func v2Routes(app chainlink.Application, r *gin.RouterGroup) { // duplicated from above, with `evm` instead of `eth` // legacy ones remain for backwards compatibility - ethKeysGroup := authv2.Group("", auth.Authenticate(app.SessionORM(), + ethKeysGroup := authv2.Group("", auth.Authenticate(app.AuthenticationProvider(), auth.AuthenticateByToken, auth.AuthenticateBySession, )) @@ -427,7 +427,7 @@ func v2Routes(app chainlink.Application, r *gin.RouterGroup) { } ping := PingController{app} - userOrEI := r.Group("/v2", auth.Authenticate(app.SessionORM(), + userOrEI := r.Group("/v2", auth.Authenticate(app.AuthenticationProvider(), auth.AuthenticateExternalInitiator, auth.AuthenticateByToken, auth.AuthenticateBySession, diff --git a/core/web/sessions_controller.go b/core/web/sessions_controller.go index 6f029456bd1..23ecfd3b798 100644 --- a/core/web/sessions_controller.go +++ b/core/web/sessions_controller.go @@ -39,7 +39,7 @@ func (sc *SessionsController) Create(c *gin.Context) { } // Does this user have 2FA enabled? - userWebAuthnTokens, err := sc.App.SessionORM().GetUserWebAuthn(sr.Email) + userWebAuthnTokens, err := sc.App.AuthenticationProvider().GetUserWebAuthn(sr.Email) if err != nil { sc.App.GetLogger().Errorf("Error loading user WebAuthn data: %s", err) jsonAPIError(c, http.StatusInternalServerError, errors.New("internal Server Error")) @@ -53,7 +53,7 @@ func (sc *SessionsController) Create(c *gin.Context) { sr.WebAuthnConfig = sc.App.GetWebAuthnConfiguration() } - sid, err := sc.App.SessionORM().CreateSession(sr) + sid, err := sc.App.AuthenticationProvider().CreateSession(sr) if err != nil { jsonAPIError(c, http.StatusUnauthorized, err) return @@ -78,7 +78,7 @@ func (sc *SessionsController) Destroy(c *gin.Context) { jsonAPIResponse(c, Session{Authenticated: false}, "session") return } - if err := sc.App.SessionORM().DeleteUserSession(sessionID); err != nil { + if err := sc.App.AuthenticationProvider().DeleteUserSession(sessionID); err != nil { jsonAPIError(c, http.StatusInternalServerError, err) return } diff --git a/core/web/sessions_controller_test.go b/core/web/sessions_controller_test.go index 7184e3f95b4..c2950caf3d1 100644 --- a/core/web/sessions_controller_test.go +++ b/core/web/sessions_controller_test.go @@ -27,7 +27,7 @@ func TestSessionsController_Create(t *testing.T) { require.NoError(t, app.Start(testutils.Context(t))) user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) client := clhttptest.NewTestLocalOnlyHTTPClient() tests := []struct { @@ -59,7 +59,7 @@ func TestSessionsController_Create(t *testing.T) { decrypted, err := cltest.DecodeSessionCookie(sessionCookie.Value) require.NoError(t, err) - user, err := app.SessionORM().AuthorizedUserWithSession(decrypted) + user, err := app.AuthenticationProvider().AuthorizedUserWithSession(decrypted) assert.NoError(t, err) assert.Equal(t, test.email, user.Email) @@ -69,7 +69,7 @@ func TestSessionsController_Create(t *testing.T) { } else { require.True(t, resp.StatusCode >= 400, "Should not be able to create session") // Ignore fixture session - sessions, err := app.SessionORM().Sessions(1, 2) + sessions, err := app.AuthenticationProvider().Sessions(1, 2) assert.NoError(t, err) assert.Empty(t, sessions) } @@ -90,7 +90,7 @@ func TestSessionsController_Create_ReapSessions(t *testing.T) { require.NoError(t, app.Start(testutils.Context(t))) user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) staleSession := cltest.NewSession() staleSession.LastUsed = time.Now().Add(-cltest.MustParseDuration(t, "241h")) @@ -107,7 +107,7 @@ func TestSessionsController_Create_ReapSessions(t *testing.T) { var s []sessions.Session gomega.NewWithT(t).Eventually(func() []sessions.Session { - s, err = app.SessionORM().Sessions(0, 10) + s, err = app.AuthenticationProvider().Sessions(0, 10) assert.NoError(t, err) return s }).Should(gomega.HaveLen(1)) @@ -124,7 +124,7 @@ func TestSessionsController_Destroy(t *testing.T) { require.NoError(t, app.Start(testutils.Context(t))) user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) correctSession := sessions.NewSession() correctSession.Email = user.Email @@ -150,7 +150,7 @@ func TestSessionsController_Destroy(t *testing.T) { resp, err := client.Do(request) assert.NoError(t, err) - _, err = app.SessionORM().AuthorizedUserWithSession(test.sessionID) + _, err = app.AuthenticationProvider().AuthorizedUserWithSession(test.sessionID) assert.Error(t, err) if test.success { assert.Equal(t, http.StatusOK, resp.StatusCode) @@ -170,7 +170,7 @@ func TestSessionsController_Destroy_ReapSessions(t *testing.T) { require.NoError(t, app.Start(testutils.Context(t))) user := cltest.MustRandomUser(t) - require.NoError(t, app.SessionORM().CreateUser(&user)) + require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) correctSession := sessions.NewSession() correctSession.Email = user.Email @@ -192,7 +192,7 @@ func TestSessionsController_Destroy_ReapSessions(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) gomega.NewWithT(t).Eventually(func() []sessions.Session { - sessions, err := app.SessionORM().Sessions(0, 10) + sessions, err := app.AuthenticationProvider().Sessions(0, 10) assert.NoError(t, err) return sessions }).Should(gomega.HaveLen(0)) diff --git a/core/web/user_controller.go b/core/web/user_controller.go index 115971eafc7..857fff7b37f 100644 --- a/core/web/user_controller.go +++ b/core/web/user_controller.go @@ -30,10 +30,16 @@ type UpdatePasswordRequest struct { NewPassword string `json:"newPassword"` } +var errUnsupportedForAuth = errors.New("action is unsupported with configured authentication provider") + // Index lists all API users func (c *UserController) Index(ctx *gin.Context) { - users, err := c.App.SessionORM().ListUsers() + users, err := c.App.AuthenticationProvider().ListUsers() if err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } c.App.GetLogger().Errorf("Unable to list users", "err", err) jsonAPIError(ctx, http.StatusInternalServerError, err) return @@ -76,7 +82,7 @@ func (c *UserController) Create(ctx *gin.Context) { jsonAPIError(ctx, http.StatusBadRequest, errors.Errorf("error creating API user: %s", err)) return } - if err = c.App.SessionORM().CreateUser(&user); err != nil { + if err = c.App.AuthenticationProvider().CreateUser(&user); err != nil { // If this is a duplicate key error (code 23505), return a nicer error message var pgErr *pgconn.PgError if ok := errors.As(err, &pgErr); ok { @@ -85,6 +91,10 @@ func (c *UserController) Create(ctx *gin.Context) { return } } + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } c.App.GetLogger().Errorf("Error creating new API user", "err", err) jsonAPIError(ctx, http.StatusInternalServerError, errors.New("error creating API user")) return @@ -132,8 +142,12 @@ func (c *UserController) UpdateRole(ctx *gin.Context) { return } - user, err := c.App.SessionORM().UpdateRole(request.Email, request.NewRole) + user, err := c.App.AuthenticationProvider().UpdateRole(request.Email, request.NewRole) if err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } jsonAPIError(ctx, http.StatusInternalServerError, errors.Wrap(err, "error updating API user")) return } @@ -146,8 +160,12 @@ func (c *UserController) Delete(ctx *gin.Context) { email := ctx.Param("email") // Attempt find user by email - user, err := c.App.SessionORM().FindUser(email) + user, err := c.App.AuthenticationProvider().FindUser(email) if err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } jsonAPIError(ctx, http.StatusBadRequest, errors.Errorf("specified user not found: %s", email)) return } @@ -163,7 +181,11 @@ func (c *UserController) Delete(ctx *gin.Context) { return } - if err = c.App.SessionORM().DeleteUser(email); err != nil { + if err = c.App.AuthenticationProvider().DeleteUser(email); err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } c.App.GetLogger().Errorf("Error deleting API user", "err", err) jsonAPIError(ctx, http.StatusInternalServerError, errors.New("error deleting API user")) return @@ -185,8 +207,12 @@ func (c *UserController) UpdatePassword(ctx *gin.Context) { jsonAPIError(ctx, http.StatusInternalServerError, errors.New("failed to obtain current user from context")) return } - user, err := c.App.SessionORM().FindUser(sessionUser.Email) + user, err := c.App.AuthenticationProvider().FindUser(sessionUser.Email) if err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } c.App.GetLogger().Errorf("failed to obtain current user record: %s", err) jsonAPIError(ctx, http.StatusInternalServerError, errors.New("unable to update password")) return @@ -222,19 +248,29 @@ func (c *UserController) NewAPIToken(ctx *gin.Context) { jsonAPIError(ctx, http.StatusInternalServerError, errors.New("failed to obtain current user from context")) return } - user, err := c.App.SessionORM().FindUser(sessionUser.Email) + user, err := c.App.AuthenticationProvider().FindUser(sessionUser.Email) if err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } c.App.GetLogger().Errorf("failed to obtain current user record: %s", err) - jsonAPIError(ctx, http.StatusInternalServerError, errors.New("unable to creatae API token")) + jsonAPIError(ctx, http.StatusInternalServerError, errors.New("unable to create API token")) return } - if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { + // In order to create an API token, login validation with provided password must succeed + err = c.App.AuthenticationProvider().TestPassword(sessionUser.Email, request.Password) + if err != nil { c.App.GetAuditLogger().Audit(audit.APITokenCreateAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } newToken := auth.NewToken() - if err := c.App.SessionORM().SetAuthToken(&user, newToken); err != nil { + if err := c.App.AuthenticationProvider().SetAuthToken(&user, newToken); err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } jsonAPIError(ctx, http.StatusInternalServerError, err) return } @@ -256,18 +292,27 @@ func (c *UserController) DeleteAPIToken(ctx *gin.Context) { jsonAPIError(ctx, http.StatusInternalServerError, errors.New("failed to obtain current user from context")) return } - user, err := c.App.SessionORM().FindUser(sessionUser.Email) + user, err := c.App.AuthenticationProvider().FindUser(sessionUser.Email) if err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } c.App.GetLogger().Errorf("failed to obtain current user record: %s", err) jsonAPIError(ctx, http.StatusInternalServerError, errors.New("unable to delete API token")) return } - if !utils.CheckPasswordHash(request.Password, user.HashedPassword) { + err = c.App.AuthenticationProvider().TestPassword(sessionUser.Email, request.Password) + if err != nil { c.App.GetAuditLogger().Audit(audit.APITokenDeleteAttemptPasswordMismatch, map[string]interface{}{"user": user.Email}) jsonAPIError(ctx, http.StatusUnauthorized, errors.New("incorrect password")) return } - if err := c.App.SessionORM().DeleteAuthToken(&user); err != nil { + if err := c.App.AuthenticationProvider().DeleteAuthToken(&user); err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + jsonAPIError(ctx, http.StatusBadRequest, errUnsupportedForAuth) + return + } jsonAPIError(ctx, http.StatusInternalServerError, err) return } @@ -291,12 +336,15 @@ func (c *UserController) updateUserPassword(ctx *gin.Context, user *clsession.Us if err != nil { return err } - orm := c.App.SessionORM() + orm := c.App.AuthenticationProvider() if err := orm.ClearNonCurrentSessions(sessionID); err != nil { c.App.GetLogger().Errorf("failed to clear non current user sessions: %s", err) return errors.New("unable to update password") } if err := orm.SetPassword(user, newPassword); err != nil { + if errors.Is(err, clsession.ErrNotSupported) { + return errUnsupportedForAuth + } c.App.GetLogger().Errorf("failed to update current user password: %s", err) return errors.New("unable to update password") } diff --git a/core/web/user_controller_test.go b/core/web/user_controller_test.go index a11082ff6a4..6baab1c396a 100644 --- a/core/web/user_controller_test.go +++ b/core/web/user_controller_test.go @@ -188,7 +188,7 @@ func TestUserController_UpdateRole(t *testing.T) { client := app.NewHTTPClient(nil) user := cltest.MustRandomUser(t) - err := app.SessionORM().CreateUser(&user) + err := app.AuthenticationProvider().CreateUser(&user) require.NoError(t, err) testCases := []struct { @@ -235,7 +235,7 @@ func TestUserController_DeleteUser(t *testing.T) { client := app.NewHTTPClient(nil) user := cltest.MustRandomUser(t) - err := app.SessionORM().CreateUser(&user) + err := app.AuthenticationProvider().CreateUser(&user) require.NoError(t, err) resp, cleanup := client.Delete(fmt.Sprintf("/v2/users/%s", url.QueryEscape(user.Email))) diff --git a/core/web/webauthn_controller.go b/core/web/webauthn_controller.go index 05090013237..41c8f268ad4 100644 --- a/core/web/webauthn_controller.go +++ b/core/web/webauthn_controller.go @@ -36,7 +36,7 @@ func (c *WebAuthnController) BeginRegistration(ctx *gin.Context) { return } - orm := c.App.SessionORM() + orm := c.App.AuthenticationProvider() uwas, err := orm.GetUserWebAuthn(user.Email) if err != nil { c.App.GetLogger().Errorf("failed to obtain current user MFA tokens: error in GetUserWebAuthn: %+v", err) @@ -66,7 +66,7 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { return } - orm := c.App.SessionORM() + orm := c.App.AuthenticationProvider() uwas, err := orm.GetUserWebAuthn(user.Email) if err != nil { c.App.GetLogger().Errorf("failed to obtain current user MFA tokens: error in GetUserWebAuthn: %s", err) @@ -83,7 +83,7 @@ func (c *WebAuthnController) FinishRegistration(ctx *gin.Context) { return } - if sessions.AddCredentialToUser(c.App.SessionORM(), user.Email, credential) != nil { + if sessions.AddCredentialToUser(c.App.AuthenticationProvider(), user.Email, credential) != nil { c.App.GetLogger().Errorf("Could not save WebAuthn credential to DB for user: %s", user.Email) jsonAPIError(ctx, http.StatusInternalServerError, errors.New("internal Server Error")) return diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8f3b16c1327..b5b393542be 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] +### Added + +- Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. + + ### Changed - `L2Suggested` mode is now called `SuggestedPrice` diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 313e7b46aaf..23508df172a 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -459,6 +459,7 @@ MaxBackups determines the maximum number of old log files to retain. Keeping thi ## WebServer ```toml [WebServer] +AuthenticationMethod = 'local' # Default AllowOrigins = 'http://localhost:3000,http://localhost:6688' # Default BridgeCacheTTL = '0s' # Default BridgeResponseURL = 'https://my-chainlink-node.example.com:6688' # Example @@ -473,6 +474,12 @@ ListenIP = '0.0.0.0' # Default ``` +### AuthenticationMethod +```toml +AuthenticationMethod = 'local' # Default +``` +AuthenticationMethod defines which pluggable auth interface to use for user login and role assumption. Options include 'local' and 'ldap'. See docs for more details + ### AllowOrigins ```toml AllowOrigins = 'http://localhost:3000,http://localhost:6688' # Default @@ -546,6 +553,132 @@ ListenIP = '0.0.0.0' # Default ``` ListenIP specifies the IP to bind the HTTP server to +## WebServer.LDAP +```toml +[WebServer.LDAP] +ServerTLS = true # Default +SessionTimeout = '15m0s' # Default +QueryTimeout = '2m0s' # Default +BaseUserAttr = 'uid' # Default +BaseDN = 'dc=custom,dc=example,dc=com' # Example +UsersDN = 'ou=users' # Default +GroupsDN = 'ou=groups' # Default +ActiveAttribute = '' # Default +ActiveAttributeAllowedValue = '' # Default +AdminUserGroupCN = 'NodeAdmins' # Default +EditUserGroupCN = 'NodeEditors' # Default +RunUserGroupCN = 'NodeRunners' # Default +ReadUserGroupCN = 'NodeReadOnly' # Default +UserApiTokenEnabled = false # Default +UserAPITokenDuration = '240h0m0s' # Default +UpstreamSyncInterval = '0s' # Default +UpstreamSyncRateLimit = '2m0s' # Default +``` +Optional LDAP config if WebServer.AuthenticationMethod is set to 'ldap' +LDAP queries are all parameterized to support custom LDAP 'dn', 'cn', and attributes + +### ServerTLS +```toml +ServerTLS = true # Default +``` +ServerTLS defines the option to require the secure ldaps + +### SessionTimeout +```toml +SessionTimeout = '15m0s' # Default +``` +SessionTimeout determines the amount of idle time to elapse before session cookies expire. This signs out GUI users from their sessions. + +### QueryTimeout +```toml +QueryTimeout = '2m0s' # Default +``` +QueryTimeout defines how long queries should wait before timing out, defined in seconds + +### BaseUserAttr +```toml +BaseUserAttr = 'uid' # Default +``` +BaseUserAttr defines the base attribute used to populate LDAP queries such as "uid=$", default is example + +### BaseDN +```toml +BaseDN = 'dc=custom,dc=example,dc=com' # Example +``` +BaseDN defines the base LDAP 'dn' search filter to apply to every LDAP query, replace example,com with the appropriate LDAP server's structure + +### UsersDN +```toml +UsersDN = 'ou=users' # Default +``` +UsersDN defines the 'dn' query to use when querying for the 'users' 'ou' group + +### GroupsDN +```toml +GroupsDN = 'ou=groups' # Default +``` +GroupsDN defines the 'dn' query to use when querying for the 'groups' 'ou' group + +### ActiveAttribute +```toml +ActiveAttribute = '' # Default +``` +ActiveAttribute is an optional user field to check truthiness for if a user is valid/active. This is only required if the LDAP provider lists inactive users as members of groups + +### ActiveAttributeAllowedValue +```toml +ActiveAttributeAllowedValue = '' # Default +``` +ActiveAttributeAllowedValue is the value to check against for the above optional user attribute + +### AdminUserGroupCN +```toml +AdminUserGroupCN = 'NodeAdmins' # Default +``` +AdminUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Admin' role + +### EditUserGroupCN +```toml +EditUserGroupCN = 'NodeEditors' # Default +``` +EditUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Edit' role + +### RunUserGroupCN +```toml +RunUserGroupCN = 'NodeRunners' # Default +``` +RunUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Run' role + +### ReadUserGroupCN +```toml +ReadUserGroupCN = 'NodeReadOnly' # Default +``` +ReadUserGroupCN is the LDAP 'cn' of the LDAP group that maps the core node's 'Read' role + +### UserApiTokenEnabled +```toml +UserApiTokenEnabled = false # Default +``` +UserApiTokenEnabled enables the users to issue API tokens with the same access of their role + +### UserAPITokenDuration +```toml +UserAPITokenDuration = '240h0m0s' # Default +``` +UserAPITokenDuration is the duration of time an API token is active for before expiring + +### UpstreamSyncInterval +```toml +UpstreamSyncInterval = '0s' # Default +``` +UpstreamSyncInterval is the interval at which the background LDAP sync task will be called. A '0s' value disables the background sync being run on an interval. This check is already performed during login/logout actions, all sessions and API tokens stored in the local ldap tables are updated to match the remote server + +### UpstreamSyncRateLimit +```toml +UpstreamSyncRateLimit = '2m0s' # Default +``` +UpstreamSyncRateLimit defines a duration to limit the number of query/API calls to the upstream LDAP provider. It prevents the sync functionality from being called multiple times within the defined duration + ## WebServer.RateLimit ```toml [WebServer.RateLimit] diff --git a/docs/SECRETS.md b/docs/SECRETS.md index af316cab14b..fa7ba76df42 100644 --- a/docs/SECRETS.md +++ b/docs/SECRETS.md @@ -51,6 +51,33 @@ AllowSimplePasswords skips the password complexity check normally enforced on UR Environment variable: `CL_DATABASE_ALLOW_SIMPLE_PASSWORDS` +## WebServer.LDAP +```toml +[WebServer.LDAP] +ServerAddress = 'ldaps://127.0.0.1' # Example +ReadOnlyUserLogin = 'viewer@example.com' # Example +ReadOnlyUserPass = 'password' # Example +``` +Optional LDAP config + +### ServerAddress +```toml +ServerAddress = 'ldaps://127.0.0.1' # Example +``` +ServerAddress is the full ldaps:// address of the ldap server to authenticate with and query + +### ReadOnlyUserLogin +```toml +ReadOnlyUserLogin = 'viewer@example.com' # Example +``` +ReadOnlyUserLogin is the username of the read only root user used to authenticate the requested LDAP queries + +### ReadOnlyUserPass +```toml +ReadOnlyUserPass = 'password' # Example +``` +ReadOnlyUserPass is the password for the above account + ## Password ```toml [Password] diff --git a/go.mod b/go.mod index 820e42c3308..999c1b0402f 100644 --- a/go.mod +++ b/go.mod @@ -114,6 +114,7 @@ require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect @@ -169,8 +170,10 @@ require ( github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect + github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/go.sum b/go.sum index 155e54646d7..ee06cc9b751 100644 --- a/go.sum +++ b/go.sum @@ -79,6 +79,8 @@ github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOv github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -129,6 +131,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -419,6 +423,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -433,6 +439,8 @@ github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEai github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= +github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -1748,6 +1756,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1788,6 +1797,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1850,6 +1860,7 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1876,6 +1887,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1980,6 +1992,7 @@ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1993,6 +2006,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2068,6 +2082,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 127980a2cb9..93820c6ebfe 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -58,6 +58,7 @@ require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect @@ -152,9 +153,11 @@ require ( github.com/gin-contrib/sessions v0.0.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/log v0.2.1 // indirect + github.com/go-ldap/ldap/v3 v3.4.5 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 24da9467176..60805eae825 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -575,6 +575,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -641,6 +643,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= @@ -1027,6 +1031,8 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -1047,6 +1053,8 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= +github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= @@ -2713,6 +2721,7 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 189476bfa84..8a3b1af96fa 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -73,6 +73,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -85,6 +86,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 593aa0b21d0..31fded1b423 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -117,6 +117,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -129,6 +130,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 7b8aa5e3836..78fc976912c 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -117,6 +117,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -129,6 +130,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index ef6548619e1..226a7bbb3b4 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -117,6 +117,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -129,6 +130,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 87b877bc882..5cd3d567467 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -107,6 +107,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -119,6 +120,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index c607da10644..fd24150b587 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -114,6 +114,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -126,6 +127,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index ee7926f8f5f..828d953da9a 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -110,6 +110,7 @@ MaxAgeDays = 0 MaxBackups = 1 [WebServer] +AuthenticationMethod = 'local' AllowOrigins = 'http://localhost:3000,http://localhost:6688' BridgeResponseURL = '' BridgeCacheTTL = '0s' @@ -122,6 +123,25 @@ HTTPMaxSize = '32.77kb' StartTimeout = '15s' ListenIP = '0.0.0.0' +[WebServer.LDAP] +ServerTLS = true +SessionTimeout = '15m0s' +QueryTimeout = '2m0s' +BaseUserAttr = 'uid' +BaseDN = '' +UsersDN = 'ou=users' +GroupsDN = 'ou=groups' +ActiveAttribute = '' +ActiveAttributeAllowedValue = '' +AdminUserGroupCN = 'NodeAdmins' +EditUserGroupCN = 'NodeEditors' +RunUserGroupCN = 'NodeRunners' +ReadUserGroupCN = 'NodeReadOnly' +UserApiTokenEnabled = false +UserAPITokenDuration = '240h0m0s' +UpstreamSyncInterval = '0s' +UpstreamSyncRateLimit = '2m0s' + [WebServer.MFA] RPID = '' RPOrigin = '' From 59bf37c5f8ebfb368e93ef7b6f2ccad4b50b246d Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Mon, 6 Nov 2023 11:03:45 +0200 Subject: [PATCH 073/271] =?UTF-8?q?VRF-669:=20adding=20CTF=20tests=20for?= =?UTF-8?q?=20VRF=20v2.5=20cancel=20subscription,=20oracle=20wi=E2=80=A6?= =?UTF-8?q?=20(#11159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-669: adding CTF tests for VRF v2.5 cancel subscription, oracle withdraw; added fund return as a teardown for the WASP load test * VRF-669: updating tests to have better balance calculation * VRF-669: small update * VRF-669: enabling tests; making use of big.Int Cmp() method * VRF-669: adding sub cancellation test for the coordinator owner; code refactoring; fixing load test --- .github/workflows/integration-tests.yml | 2 +- integration-tests/actions/actions.go | 16 + .../vrfv2plus/vrfv2plus_config/config.go | 6 +- .../actions/vrfv2plus/vrfv2plus_steps.go | 118 +++- .../contracts/contract_vrf_models.go | 6 + .../contracts/ethereum_vrfv2plus_contracts.go | 101 ++++ integration-tests/load/vrfv2plus/config.go | 4 +- integration-tests/load/vrfv2plus/config.toml | 8 +- integration-tests/load/vrfv2plus/gun.go | 5 +- .../load/vrfv2plus/vrfv2plus_test.go | 62 +- integration-tests/smoke/vrfv2plus_test.go | 530 ++++++++++++++---- integration-tests/utils/common.go | 10 + 12 files changed, 737 insertions(+), 131 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 0456c5f9d42..928c716dd11 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -303,7 +303,7 @@ jobs: pyroscope_env: ci-smoke-vrf2-evm-simulated - name: vrfv2plus nodes: 1 - os: ubuntu-latest + os: ubuntu20.04-8cores-32GB pyroscope_env: ci-smoke-vrf2plus-evm-simulated - name: forwarder_ocr nodes: 1 diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 010b431b56f..b45b8e83b95 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -2,8 +2,10 @@ package actions import ( + "crypto/ecdsa" "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/crypto" "math/big" "strings" "testing" @@ -443,3 +445,17 @@ func DeployMockETHLinkFeed(cd contracts.ContractDeployer, answer *big.Int) (cont } return mockETHLINKFeed, err } + +// todo - move to CTF +func GenerateWallet() (common.Address, error) { + privateKey, err := crypto.GenerateKey() + if err != nil { + return common.Address{}, err + } + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return common.Address{}, errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + } + return crypto.PubkeyToAddress(*publicKeyECDSA), nil +} diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go index 10d4f19c244..a47103a8a18 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go @@ -7,8 +7,8 @@ type VRFV2PlusConfig struct { IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator - SubscriptionFundingAmountLink int64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"10"` // Amount of LINK to fund the subscription with - SubscriptionFundingAmountNative int64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_NATIVE" default:"1"` // Amount of native currency to fund the subscription with + SubscriptionFundingAmountLink float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"5"` // Amount of LINK to fund the subscription with + SubscriptionFundingAmountNative float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_NATIVE" default:"1"` // Amount of native currency to fund the subscription with NumberOfWords uint32 `envconfig:"NUMBER_OF_WORDS" default:"3"` // Number of words to request CallbackGasLimit uint32 `envconfig:"CALLBACK_GAS_LIMIT" default:"1000000"` // Gas limit for the callback MaxGasLimitCoordinatorConfig uint32 `envconfig:"MAX_GAS_LIMIT_COORDINATOR_CONFIG" default:"2500000"` // Max gas limit for the VRF Coordinator config @@ -23,6 +23,8 @@ type VRFV2PlusConfig struct { RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request RandomnessRequestCountPerRequestDeviation uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST_DEVIATION" default:"0"` // How many randomness requests to send per request + RandomWordsFulfilledEventTimeout time.Duration `envconfig:"RANDOM_WORDS_FULFILLED_EVENT_TIMEOUT" default:"2m"` // How long to wait for the RandomWordsFulfilled event to be emitted + //Wrapper Config WrapperGasOverhead uint32 `envconfig:"WRAPPER_GAS_OVERHEAD" default:"50000"` CoordinatorGasOverhead uint32 `envconfig:"COORDINATOR_GAS_OVERHEAD" default:"52000"` diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 46f0ca58e69..e720116c210 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -3,6 +3,7 @@ package vrfv2plus import ( "context" "fmt" + "github.com/smartcontractkit/chainlink-testing-framework/utils" "math/big" "sync" "time" @@ -221,12 +222,18 @@ func VRFV2PlusUpgradedVersionRegisterProvingKey( return provingKey, nil } -func FundVRFCoordinatorV2_5Subscription(linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, chainClient blockchain.EVMClient, subscriptionID *big.Int, linkFundingAmount *big.Int) error { +func FundVRFCoordinatorV2_5Subscription( + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2_5, + chainClient blockchain.EVMClient, + subscriptionID *big.Int, + linkFundingAmountJuels *big.Int, +) error { encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint256"}]`, subscriptionID) if err != nil { return errors.Wrap(err, ErrABIEncodingFunding) } - _, err = linkToken.TransferAndCall(coordinator.Address(), big.NewInt(0).Mul(linkFundingAmount, big.NewInt(1e18)), encodedSubId) + _, err = linkToken.TransferAndCall(coordinator.Address(), linkFundingAmountJuels, encodedSubId) if err != nil { return errors.Wrap(err, ErrSendingLinkToken) } @@ -236,9 +243,10 @@ func FundVRFCoordinatorV2_5Subscription(linkToken contracts.LinkToken, coordinat // SetupVRFV2_5Environment will create specified number of subscriptions and add the same conumer/s to each of them func SetupVRFV2_5Environment( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, + registerProvingKeyAgainstAddress string, numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, @@ -275,8 +283,12 @@ func SetupVRFV2_5Environment( if err != nil { return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) } - l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Creating and funding subscriptions, adding consumers") - subIDs, err := CreateFundSubsAndAddConsumers(env, vrfv2PlusConfig, linkToken, vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, numberOfSubToCreate) + l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Int("Number of Subs to create", numberOfSubToCreate).Msg("Creating and funding subscriptions, adding consumers") + subIDs, err := CreateFundSubsAndAddConsumers( + env, + vrfv2PlusConfig, + linkToken, + vrfv2_5Contracts.Coordinator, vrfv2_5Contracts.LoadTestConsumers, numberOfSubToCreate) if err != nil { return nil, nil, nil, err } @@ -287,12 +299,8 @@ func SetupVRFV2_5Environment( } pubKeyCompressed := vrfKey.Data.ID - nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() - if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrNodePrimaryKey) - } l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Registering Proving Key") - provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, nativeTokenPrimaryKeyAddress, vrfv2_5Contracts.Coordinator) + provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfv2_5Contracts.Coordinator) if err != nil { return nil, nil, nil, errors.Wrap(err, ErrRegisteringProvingKey) } @@ -303,6 +311,11 @@ func SetupVRFV2_5Environment( chainID := env.EVMClient.GetChainID() + nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() + if err != nil { + return nil, nil, nil, errors.Wrap(err, ErrNodePrimaryKey) + } + l.Info().Msg("Creating VRFV2 Plus Job") job, err := CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], @@ -351,7 +364,7 @@ func SetupVRFV2_5Environment( func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, consumers []contracts.VRFv2PlusLoadTestConsumer, @@ -385,7 +398,7 @@ func CreateFundSubsAndAddConsumers( func CreateSubsAndFund( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, subAmountToCreate int, @@ -439,7 +452,7 @@ func AddConsumersToSubs( func SetupVRFV2PlusWrapperEnvironment( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, coordinator contracts.VRFCoordinatorV2_5, @@ -574,19 +587,24 @@ func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkT func FundSubscriptions( env *test_env.CLClusterTestEnv, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, linkAddress contracts.LinkToken, coordinator contracts.VRFCoordinatorV2_5, subIDs []*big.Int, ) error { for _, subID := range subIDs { //Native Billing - err := coordinator.FundSubscriptionWithNative(subID, big.NewInt(0).Mul(big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountNative), big.NewInt(1e18))) + amountWei := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) + err := coordinator.FundSubscriptionWithNative( + subID, + amountWei, + ) if err != nil { return errors.Wrap(err, ErrFundSubWithNativeToken) } //Link Billing - err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, big.NewInt(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + amountJuels := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { return errors.Wrap(err, ErrFundSubWithLinkToken) } @@ -605,7 +623,8 @@ func RequestRandomnessAndWaitForFulfillment( subID *big.Int, isNativeBilling bool, randomnessRequestCountPerRequest uint16, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l) @@ -622,7 +641,15 @@ func RequestRandomnessAndWaitForFulfillment( return nil, errors.Wrap(err, ErrRequestRandomness) } - return WaitForRequestAndFulfillmentEvents(consumer.Address(), coordinator, vrfv2PlusData, subID, isNativeBilling, l) + return WaitForRequestAndFulfillmentEvents( + consumer.Address(), + coordinator, + vrfv2PlusData, + subID, + isNativeBilling, + randomWordsFulfilledEventTimeout, + l, + ) } func RequestRandomnessAndWaitForFulfillmentUpgraded( @@ -631,7 +658,7 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( vrfv2PlusData *VRFV2PlusData, subID *big.Int, isNativeBilling bool, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, l zerolog.Logger, ) (*vrf_v2plus_upgraded_version.VRFCoordinatorV2PlusUpgradedVersionRandomWordsFulfilled, error) { logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l) @@ -679,7 +706,8 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( vrfv2PlusData *VRFV2PlusData, subID *big.Int, isNativeBilling bool, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { logRandRequest(consumer.Address(), coordinator.Address(), subID, isNativeBilling, vrfv2PlusConfig, l) @@ -708,7 +736,15 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( if err != nil { return nil, errors.Wrap(err, "error getting wrapper address") } - return WaitForRequestAndFulfillmentEvents(wrapperAddress.String(), coordinator, vrfv2PlusData, subID, isNativeBilling, l) + return WaitForRequestAndFulfillmentEvents( + wrapperAddress.String(), + coordinator, + vrfv2PlusData, + subID, + isNativeBilling, + randomWordsFulfilledEventTimeout, + l, + ) } func WaitForRequestAndFulfillmentEvents( @@ -717,6 +753,7 @@ func WaitForRequestAndFulfillmentEvents( vrfv2PlusData *VRFV2PlusData, subID *big.Int, isNativeBilling bool, + randomWordsFulfilledEventTimeout time.Duration, l zerolog.Logger, ) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( @@ -734,7 +771,7 @@ func WaitForRequestAndFulfillmentEvents( randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( []*big.Int{subID}, []*big.Int{randomWordsRequestedEvent.RequestId}, - time.Minute*2, + randomWordsFulfilledEventTimeout, ) if err != nil { return nil, errors.Wrap(err, ErrWaitRandomWordsFulfilledEvent) @@ -777,6 +814,41 @@ func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadT } } +func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator contracts.VRFCoordinatorV2_5, l zerolog.Logger) error { + linkTotalBalance, err := coordinator.GetLinkTotalBalance(context.Background()) + if err != nil { + return errors.Wrap(err, "Error getting LINK total balance") + } + defaultWallet := client.GetDefaultWallet().Address() + l.Info(). + Str("LINK amount", linkTotalBalance.String()). + Str("Returning to", defaultWallet). + Msg("Returning LINK for fulfilled requests") + err = coordinator.OracleWithdraw( + common.HexToAddress(defaultWallet), + linkTotalBalance, + ) + if err != nil { + return errors.Wrap(err, "Error withdrawing LINK from coordinator to default wallet") + } + nativeTotalBalance, err := coordinator.GetNativeTokenTotalBalance(context.Background()) + if err != nil { + return errors.Wrap(err, "Error getting NATIVE total balance") + } + l.Info(). + Str("Native Token amount", linkTotalBalance.String()). + Str("Returning to", defaultWallet). + Msg("Returning Native Token for fulfilled requests") + err = coordinator.OracleWithdrawNative( + common.HexToAddress(defaultWallet), + nativeTotalBalance, + ) + if err != nil { + return errors.Wrap(err, "Error withdrawing NATIVE from coordinator to default wallet") + } + return nil +} + func getLoadTestMetrics( consumer contracts.VRFv2PlusLoadTestConsumer, metricsChannel chan *contracts.VRFLoadTestMetrics, @@ -934,7 +1006,7 @@ func logRandRequest( coordinator string, subID *big.Int, isNativeBilling bool, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, l zerolog.Logger) { l.Debug(). Str("Consumer", consumer). diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index f0f57f58e75..c82924143b0 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -80,11 +80,17 @@ type VRFCoordinatorV2_5 interface { AddConsumer(subId *big.Int, consumerAddress string) error FundSubscriptionWithNative(subId *big.Int, nativeTokenAmount *big.Int) error Address() string + PendingRequestsExist(ctx context.Context, subID *big.Int) (bool, error) GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2_5.GetSubscription, error) + OwnerCancelSubscription(subID *big.Int) (*types.Transaction, error) + CancelSubscription(subID *big.Int, to common.Address) (*types.Transaction, error) + OracleWithdraw(recipient common.Address, amount *big.Int) error + OracleWithdrawNative(recipient common.Address, amount *big.Int) error GetNativeTokenTotalBalance(ctx context.Context) (*big.Int, error) GetLinkTotalBalance(ctx context.Context) (*big.Int, error) FindSubscriptionID(subID *big.Int) (*big.Int, error) WaitForSubscriptionCreatedEvent(timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCreated, error) + WaitForSubscriptionCanceledEvent(subID *big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled, error) WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []*big.Int, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsRequested, error) WaitForMigrationCompletedEvent(timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25MigrationCompleted, error) diff --git a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go index 1488f97131a..330166dc79d 100644 --- a/integration-tests/contracts/ethereum_vrfv2plus_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2plus_contracts.go @@ -96,6 +96,18 @@ func (v *EthereumVRFCoordinatorV2_5) GetActiveSubscriptionIds(ctx context.Contex return activeSubscriptionIds, nil } +func (v *EthereumVRFCoordinatorV2_5) PendingRequestsExist(ctx context.Context, subID *big.Int) (bool, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + pendingRequestExists, err := v.coordinator.PendingRequestExists(opts, subID) + if err != nil { + return false, err + } + return pendingRequestExists, nil +} + func (v *EthereumVRFCoordinatorV2_5) GetSubscription(ctx context.Context, subID *big.Int) (vrf_coordinator_v2_5.GetSubscription, error) { opts := &bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), @@ -131,6 +143,75 @@ func (v *EthereumVRFCoordinatorV2_5) GetNativeTokenTotalBalance(ctx context.Cont return totalBalance, nil } +// OwnerCancelSubscription cancels subscription by Coordinator owner +// return funds to sub owner, +// does not check if pending requests for a sub exist +func (v *EthereumVRFCoordinatorV2_5) OwnerCancelSubscription(subID *big.Int) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.coordinator.OwnerCancelSubscription( + opts, + subID, + ) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + +// CancelSubscription cancels subscription by Sub owner, +// return funds to specified address, +// checks if pending requests for a sub exist +func (v *EthereumVRFCoordinatorV2_5) CancelSubscription(subID *big.Int, to common.Address) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.coordinator.CancelSubscription( + opts, + subID, + to, + ) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFCoordinatorV2_5) OracleWithdraw(recipient common.Address, amount *big.Int) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.coordinator.OracleWithdraw( + opts, + recipient, + amount, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFCoordinatorV2_5) OracleWithdrawNative(recipient common.Address, amount *big.Int) error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.coordinator.OracleWithdrawNative( + opts, + recipient, + amount, + ) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + func (v *EthereumVRFCoordinatorV2_5) SetConfig(minimumRequestConfirmations uint16, maxGasLimit uint32, stalenessSeconds uint32, gasAfterPaymentCalculation uint32, fallbackWeiPerUnitLink *big.Int, feeConfig vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { @@ -287,6 +368,26 @@ func (v *EthereumVRFCoordinatorV2_5) WaitForSubscriptionCreatedEvent(timeout tim } } +func (v *EthereumVRFCoordinatorV2_5) WaitForSubscriptionCanceledEvent(subID *big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled, error) { + eventsChannel := make(chan *vrf_coordinator_v2_5.VRFCoordinatorV25SubscriptionCanceled) + subscription, err := v.coordinator.WatchSubscriptionCanceled(nil, eventsChannel, []*big.Int{subID}) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for SubscriptionCanceled event") + case sub := <-eventsChannel: + return sub, nil + } + } +} + func (v *EthereumVRFCoordinatorV2_5) WaitForRandomWordsFulfilledEvent(subID []*big.Int, requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, error) { randomWordsFulfilledEventsChannel := make(chan *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled) subscription, err := v.coordinator.WatchRandomWordsFulfilled(nil, randomWordsFulfilledEventsChannel, requestID, subID) diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index 50003c82865..a5439210c2d 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -57,8 +57,8 @@ type Funding struct { } type SubFunding struct { - SubFundsLink int64 `toml:"sub_funds_link"` - SubFundsNative int64 `toml:"sub_funds_native"` + SubFundsLink float64 `toml:"sub_funds_link"` + SubFundsNative float64 `toml:"sub_funds_native"` } type Soak struct { diff --git a/integration-tests/load/vrfv2plus/config.toml b/integration-tests/load/vrfv2plus/config.toml index 05e22bd51e8..e3200fafe22 100644 --- a/integration-tests/load/vrfv2plus/config.toml +++ b/integration-tests/load/vrfv2plus/config.toml @@ -9,8 +9,8 @@ node_funds = 10 [ExistingEnvConfig] coordinator_address = "0x27b61f155F772b291D1d9B478BeAd37B2Ae447b0" -consumer_address = "0x087F232165D9bA1A602f148025e5D0666953F64a" -sub_id = "52116875585187328970776211988181422347535732407068188096422095950800466618218" +#consumer_address = "0x087F232165D9bA1A602f148025e5D0666953F64a" +#sub_id = "52116875585187328970776211988181422347535732407068188096422095950800466618218" key_hash = "0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae" create_fund_subs_and_add_consumers = true link_address = "0x779877A7B0D9E8603169DdbD7836e478b4624789" @@ -31,7 +31,7 @@ rate_limit_unit_duration = "3s" rps = 1 randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 5 +number_of_sub_to_create = 1 # approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx [Stress] @@ -39,7 +39,7 @@ rate_limit_unit_duration = "1s" rps = 3 randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 5 +number_of_sub_to_create = 1 # approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds [Spike] diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index c9947fa32f5..21be1c74ca7 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -15,7 +15,7 @@ type SingleHashGun struct { contracts *vrfv2plus.VRFV2_5Contracts keyHash [32]byte subIDs []*big.Int - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig logger zerolog.Logger } @@ -23,7 +23,7 @@ func NewSingleHashGun( contracts *vrfv2plus.VRFV2_5Contracts, keyHash [32]byte, subIDs []*big.Int, - vrfv2PlusConfig *vrfv2plus_config.VRFV2PlusConfig, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, logger zerolog.Logger, ) *SingleHashGun { return &SingleHashGun{ @@ -52,6 +52,7 @@ func (m *SingleHashGun) Call(l *wasp.Generator) *wasp.CallResult { randBool(), randomnessRequestCountPerRequest, m.vrfv2PlusConfig, + m.vrfv2PlusConfig.RandomWordsFulfilledEventTimeout, m.logger, ) if err != nil { diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index e619cf78fd3..4d3de014bcd 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -27,6 +27,7 @@ var ( vrfv2PlusContracts *vrfv2plus.VRFV2_5Contracts vrfv2PlusData *vrfv2plus.VRFV2PlusData subIDs []*big.Int + eoaWalletAddress string labels = map[string]string{ "branch": "vrfv2Plus_healthcheck", @@ -85,6 +86,32 @@ func TestVRFV2PlusPerformance(t *testing.T) { WithCustomCleanup( func() { teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + //cancel subs and return funds to sub owner + for _, subID := range subIDs { + l.Info(). + Str("Returning funds from SubID", subID.String()). + Str("Returning funds to", eoaWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subID) + if err != nil { + l.Error().Err(err).Msg("Error checking if pending requests exist") + } + if !pendingRequestsExist { + _, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } else { + l.Error().Str("Sub ID", subID.String()).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") + } + + } + } }). Build() @@ -99,7 +126,14 @@ func TestVRFV2PlusPerformance(t *testing.T) { require.NoError(t, err) consumers, err = vrfv2plus.DeployVRFV2PlusConsumers(env.ContractDeployer, coordinator, 1) require.NoError(t, err) - subIDs, err = vrfv2plus.CreateFundSubsAndAddConsumers(env, &vrfv2PlusConfig, linkToken, coordinator, consumers, vrfv2PlusConfig.NumberOfSubToCreate) + subIDs, err = vrfv2plus.CreateFundSubsAndAddConsumers( + env, + vrfv2PlusConfig, + linkToken, + coordinator, + consumers, + vrfv2PlusConfig.NumberOfSubToCreate, + ) require.NoError(t, err) } else { consumer, err := env.ContractLoader.LoadVRFv2PlusLoadTestConsumer(vrfv2PlusConfig.ConsumerAddress) @@ -141,6 +175,25 @@ func TestVRFV2PlusPerformance(t *testing.T) { WithCustomCleanup( func() { teardown(t, vrfv2PlusContracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2PlusConfig) + + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + for _, subID := range subIDs { + l.Info(). + Str("Returning funds from SubID", subID.String()). + Str("Returning funds to", eoaWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + _, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } + //err = vrfv2plus.ReturnFundsForFulfilledRequests(env.EVMClient, vrfv2PlusContracts.Coordinator, l) + //l.Error().Err(err).Msg("Error returning funds for fulfilled requests") + } if err := env.Cleanup(); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") } @@ -160,15 +213,18 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusContracts, subIDs, vrfv2PlusData, err = vrfv2plus.SetupVRFV2_5Environment( env, - &vrfv2PlusConfig, + vrfv2PlusConfig, linkToken, mockETHLinkFeed, + //register proving key against EOA address in order to return funds to this address + env.EVMClient.GetDefaultWallet().Address(), 1, vrfv2PlusConfig.NumberOfSubToCreate, l, ) require.NoError(t, err, "error setting up VRF v2_5 env") } + eoaWalletAddress = env.EVMClient.GetDefaultWallet().Address() l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { @@ -186,7 +242,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { vrfv2PlusContracts, vrfv2PlusData.KeyHash, subIDs, - &vrfv2PlusConfig, + vrfv2PlusConfig, l, ), Labels: labels, diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index d7381c9cd33..acd548c88e2 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -2,6 +2,8 @@ package smoke import ( "context" + "fmt" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "math/big" "testing" "time" @@ -45,7 +47,10 @@ func TestVRFv2Plus(t *testing.T) { linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkToken, mockETHLinkFeed, 1, 1, l) + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, vrfv2PlusConfig, linkToken, mockETHLinkFeed, defaultWalletAddress, 1, 1, l) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] @@ -55,7 +60,8 @@ func TestVRFv2Plus(t *testing.T) { vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) - t.Run("VRFV2 Plus With Link Billing", func(t *testing.T) { + t.Run("Link Billing", func(t *testing.T) { + testConfig := vrfv2PlusConfig var isNativeBilling = false subBalanceBeforeRequest := subscription.Balance @@ -69,8 +75,9 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subID, isNativeBilling, - vrfv2PlusConfig.RandomnessRequestCountPerRequest, - &vrfv2PlusConfig, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -90,14 +97,14 @@ func TestVRFv2Plus(t *testing.T) { require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - require.Equal(t, vrfv2PlusConfig.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) - - t.Run("VRFV2 Plus With Native Billing", func(t *testing.T) { + t.Run("Native Billing", func(t *testing.T) { + testConfig := vrfv2PlusConfig var isNativeBilling = true subNativeTokenBalanceBeforeRequest := subscription.NativeBalance @@ -111,8 +118,9 @@ func TestVRFv2Plus(t *testing.T) { vrfv2PlusData, subID, isNativeBilling, - vrfv2PlusConfig.RandomnessRequestCountPerRequest, - &vrfv2PlusConfig, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -131,120 +139,451 @@ func TestVRFv2Plus(t *testing.T) { require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - require.Equal(t, vrfv2PlusConfig.NumberOfWords, uint32(len(status.RandomWords))) + require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } }) + t.Run("Direct Funding (VRFV2PlusWrapper)", func(t *testing.T) { + testConfig := vrfv2PlusConfig + wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( + env, + testConfig, + linkToken, + mockETHLinkFeed, + vrfv2PlusContracts.Coordinator, + vrfv2PlusData.KeyHash, + 1, + ) + require.NoError(t, err) - wrapperContracts, wrapperSubID, err := vrfv2plus.SetupVRFV2PlusWrapperEnvironment( - env, - &vrfv2PlusConfig, - linkToken, - mockETHLinkFeed, - vrfv2PlusContracts.Coordinator, - vrfv2PlusData.KeyHash, - 1, - ) - require.NoError(t, err) + t.Run("Link Billing", func(t *testing.T) { + testConfig := vrfv2PlusConfig + var isNativeBilling = false + + wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(context.Background(), wrapperContracts.LoadTestConsumers[0].Address()) + require.NoError(t, err, "error getting wrapper consumer balance") + + wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + require.NoError(t, err, "error getting subscription information") + subBalanceBeforeRequest := wrapperSubscription.Balance + + randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( + wrapperContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + wrapperSubID, + isNativeBilling, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) + wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + require.NoError(t, err, "error getting subscription information") + subBalanceAfterRequest := wrapperSubscription.Balance + require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) + + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, consumerStatus.Fulfilled) + + expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) + + wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(context.Background(), wrapperContracts.LoadTestConsumers[0].Address()) + require.NoError(t, err, "error getting wrapper consumer balance") + require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) + + //todo: uncomment when VRF-651 will be fixed + //require.Equal(t, 1, consumerStatus.Paid.Cmp(randomWordsFulfilledEvent.Payment), "Expected Consumer contract pay more than the Coordinator Sub") + vrfv2plus.LogFulfillmentDetailsLinkBilling(l, wrapperConsumerJuelsBalanceBeforeRequest, wrapperConsumerJuelsBalanceAfterRequest, consumerStatus, randomWordsFulfilledEvent) + + require.Equal(t, testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) + for _, w := range consumerStatus.RandomWords { + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") + } + }) + t.Run("Native Billing", func(t *testing.T) { + testConfig := vrfv2PlusConfig + var isNativeBilling = true + + wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + require.NoError(t, err, "error getting wrapper consumer balance") + + wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + require.NoError(t, err, "error getting subscription information") + subBalanceBeforeRequest := wrapperSubscription.NativeBalance + + randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( + wrapperContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + wrapperSubID, + isNativeBilling, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + expectedSubBalanceWei := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) + wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + require.NoError(t, err, "error getting subscription information") + subBalanceAfterRequest := wrapperSubscription.NativeBalance + require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) + + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, consumerStatus.Fulfilled) + + expectedWrapperConsumerWeiBalance := new(big.Int).Sub(wrapperConsumerBalanceBeforeRequestWei, consumerStatus.Paid) + + wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + require.NoError(t, err, "error getting wrapper consumer balance") + require.Equal(t, expectedWrapperConsumerWeiBalance, wrapperConsumerBalanceAfterRequestWei) + + //todo: uncomment when VRF-651 will be fixed + //require.Equal(t, 1, consumerStatus.Paid.Cmp(randomWordsFulfilledEvent.Payment), "Expected Consumer contract pay more than the Coordinator Sub") + vrfv2plus.LogFulfillmentDetailsNativeBilling(l, wrapperConsumerBalanceBeforeRequestWei, wrapperConsumerBalanceAfterRequestWei, consumerStatus, randomWordsFulfilledEvent) + + require.Equal(t, testConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) + for _, w := range consumerStatus.RandomWords { + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") + } + }) + }) + t.Run("Canceling Sub And Returning Funds", func(t *testing.T) { + testConfig := vrfv2PlusConfig + subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( + env, + testConfig, + linkToken, + vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.LoadTestConsumers, + 1, + ) + require.NoError(t, err) + subIDForCancelling := subIDsForCancelling[0] - t.Run("VRFV2 Plus With Direct Funding (VRFV2PlusWrapper) - Link Billing", func(t *testing.T) { - var isNativeBilling = false + testWalletAddress, err := actions.GenerateWallet() - wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(context.Background(), wrapperContracts.LoadTestConsumers[0].Address()) - require.NoError(t, err, "error getting wrapper consumer balance") + testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), testWalletAddress) + require.NoError(t, err) + + testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(context.Background(), testWalletAddress.String()) + require.NoError(t, err) - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) require.NoError(t, err, "error getting subscription information") - subBalanceBeforeRequest := wrapperSubscription.Balance - randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( - wrapperContracts.LoadTestConsumers[0], + subBalanceLink := subscriptionForCancelling.Balance + subBalanceNative := subscriptionForCancelling.NativeBalance + l.Info(). + Str("Subscription Amount Native", subBalanceNative.String()). + Str("Subscription Amount Link", subBalanceLink.String()). + Str("Returning funds from SubID", subIDForCancelling.String()). + Str("Returning funds to", testWalletAddress.String()). + Msg("Canceling subscription and returning funds to subscription owner") + tx, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subIDForCancelling, testWalletAddress) + require.NoError(t, err, "Error canceling subscription") + + subscriptionCanceledEvent, err := vrfv2PlusContracts.Coordinator.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) + require.NoError(t, err, "error waiting for subscription canceled event") + + cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + require.NoError(t, err, "error getting tx cancellation Tx Receipt") + + txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) + cancellationTxFeeWei := new(big.Int).Mul(txGasUsed, cancellationTxReceipt.EffectiveGasPrice) + + l.Info(). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Effective Gas Price", cancellationTxReceipt.EffectiveGasPrice.String()). + Uint64("Gas Used", cancellationTxReceipt.GasUsed). + Msg("Cancellation TX Receipt") + + l.Info(). + Str("Returned Subscription Amount Native", subscriptionCanceledEvent.AmountNative.String()). + Str("Returned Subscription Amount Link", subscriptionCanceledEvent.AmountLink.String()). + Str("SubID", subscriptionCanceledEvent.SubId.String()). + Str("Returned to", subscriptionCanceledEvent.To.String()). + Msg("Subscription Canceled Event") + + require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") + require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") + + testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), testWalletAddress) + require.NoError(t, err) + + testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(context.Background(), testWalletAddress.String()) + require.NoError(t, err) + + //Verify that sub was deleted from Coordinator + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") + + subFundsReturnedNativeActual := new(big.Int).Sub(testWalletBalanceNativeAfterSubCancelling, testWalletBalanceNativeBeforeSubCancelling) + subFundsReturnedLinkActual := new(big.Int).Sub(testWalletBalanceLinkAfterSubCancelling, testWalletBalanceLinkBeforeSubCancelling) + + subFundsReturnedNativeExpected := new(big.Int).Sub(subBalanceNative, cancellationTxFeeWei) + deltaSpentOnCancellationTxFee := new(big.Int).Sub(subBalanceNative, subFundsReturnedNativeActual) + l.Info(). + Str("Sub Balance - Native", subBalanceNative.String()). + Str("Delta Spent On Cancellation Tx Fee - `NativeBalance - subFundsReturnedNativeActual`", deltaSpentOnCancellationTxFee.String()). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Sub Funds Returned Actual - Native", subFundsReturnedNativeActual.String()). + Str("Sub Funds Returned Expected - `NativeBalance - cancellationTxFeeWei`", subFundsReturnedNativeExpected.String()). + Str("Sub Funds Returned Actual - Link", subFundsReturnedLinkActual.String()). + Str("Sub Balance - Link", subBalanceLink.String()). + Msg("Sub funds returned") + + //todo - this fails on SIMULATED env as tx cost is calculated different as for testnets and it's not receipt.EffectiveGasPrice*receipt.GasUsed + //require.Equal(t, subFundsReturnedNativeExpected, subFundsReturnedNativeActual, "Returned funds are not equal to sub balance that was cancelled") + require.Equal(t, 1, testWalletBalanceNativeAfterSubCancelling.Cmp(testWalletBalanceNativeBeforeSubCancelling), "Native funds were not returned after sub cancellation") + require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") + + }) + t.Run("Owner Canceling Sub And Returning Funds While Having Pending Requests", func(t *testing.T) { + testConfig := vrfv2PlusConfig + //underfund subs in order rand fulfillments to fail + testConfig.SubscriptionFundingAmountNative = float64(0.000000000000000001) //1 Wei + testConfig.SubscriptionFundingAmountLink = float64(0.000000000000000001) //1 Juels + + subIDsForCancelling, err := vrfv2plus.CreateFundSubsAndAddConsumers( + env, + testConfig, + linkToken, vrfv2PlusContracts.Coordinator, - vrfv2PlusData, - wrapperSubID, - isNativeBilling, - &vrfv2PlusConfig, - l, + vrfv2PlusContracts.LoadTestConsumers, + 1, ) - require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + require.NoError(t, err) - expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + subIDForCancelling := subIDsForCancelling[0] + + subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) require.NoError(t, err, "error getting subscription information") - subBalanceAfterRequest := wrapperSubscription.Balance - require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) - require.NoError(t, err, "error getting rand request status") - require.True(t, consumerStatus.Fulfilled) + vrfv2plus.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2PlusContracts.Coordinator) - expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) + activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + require.NoError(t, err) - wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(context.Background(), wrapperContracts.LoadTestConsumers[0].Address()) - require.NoError(t, err, "error getting wrapper consumer balance") - require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) + require.True(t, utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subIDForCancelling)) - //todo: uncomment when VRF-651 will be fixed - //require.Equal(t, 1, consumerStatus.Paid.Cmp(randomWordsFulfilledEvent.Payment), "Expected Consumer contract pay more than the Coordinator Sub") - vrfv2plus.LogFulfillmentDetailsLinkBilling(l, wrapperConsumerJuelsBalanceBeforeRequest, wrapperConsumerJuelsBalanceAfterRequest, consumerStatus, randomWordsFulfilledEvent) + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subIDForCancelling) + require.NoError(t, err) + require.False(t, pendingRequestsExist, "Pending requests should not exist") - require.Equal(t, vrfv2PlusConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) - for _, w := range consumerStatus.RandomWords { - l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") - require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") - } - }) + _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + subIDForCancelling, + false, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + 5*time.Second, + l, + ) - t.Run("VRFV2 Plus With Direct Funding (VRFV2PlusWrapper) - Native Billing", func(t *testing.T) { - var isNativeBilling = true + require.Error(t, err, "error should occur for waiting for fulfilment due to low sub balance") - wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) - require.NoError(t, err, "error getting wrapper consumer balance") + _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + subIDForCancelling, + true, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + require.Error(t, err, "error should occur for waiting for fulfilment due to low sub balance") + + pendingRequestsExist, err = vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subIDForCancelling) + require.NoError(t, err) + require.True(t, pendingRequestsExist, "Pending requests should exist after unfulfilled rand requests due to low sub balance") + + walletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + require.NoError(t, err) + + walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + require.NoError(t, err) + + subscriptionForCancelling, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) require.NoError(t, err, "error getting subscription information") - subBalanceBeforeRequest := wrapperSubscription.NativeBalance - randomWordsFulfilledEvent, err := vrfv2plus.DirectFundingRequestRandomnessAndWaitForFulfillment( - wrapperContracts.LoadTestConsumers[0], + subBalanceLink := subscriptionForCancelling.Balance + subBalanceNative := subscriptionForCancelling.NativeBalance + l.Info(). + Str("Subscription Amount Native", subBalanceNative.String()). + Str("Subscription Amount Link", subBalanceLink.String()). + Str("Returning funds from SubID", subIDForCancelling.String()). + Str("Returning funds to", defaultWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + tx, err := vrfv2PlusContracts.Coordinator.OwnerCancelSubscription(subIDForCancelling) + require.NoError(t, err, "Error canceling subscription") + + subscriptionCanceledEvent, err := vrfv2PlusContracts.Coordinator.WaitForSubscriptionCanceledEvent(subIDForCancelling, time.Second*30) + require.NoError(t, err, "error waiting for subscription canceled event") + + cancellationTxReceipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + require.NoError(t, err, "error getting tx cancellation Tx Receipt") + + txGasUsed := new(big.Int).SetUint64(cancellationTxReceipt.GasUsed) + cancellationTxFeeWei := new(big.Int).Mul(txGasUsed, cancellationTxReceipt.EffectiveGasPrice) + + l.Info(). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Effective Gas Price", cancellationTxReceipt.EffectiveGasPrice.String()). + Uint64("Gas Used", cancellationTxReceipt.GasUsed). + Msg("Cancellation TX Receipt") + + l.Info(). + Str("Returned Subscription Amount Native", subscriptionCanceledEvent.AmountNative.String()). + Str("Returned Subscription Amount Link", subscriptionCanceledEvent.AmountLink.String()). + Str("SubID", subscriptionCanceledEvent.SubId.String()). + Str("Returned to", subscriptionCanceledEvent.To.String()). + Msg("Subscription Canceled Event") + + require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") + require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") + + walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + require.NoError(t, err) + + walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + require.NoError(t, err) + + //Verify that sub was deleted from Coordinator + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + fmt.Println("err", err) + require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") + + subFundsReturnedNativeActual := new(big.Int).Sub(walletBalanceNativeAfterSubCancelling, walletBalanceNativeBeforeSubCancelling) + subFundsReturnedLinkActual := new(big.Int).Sub(walletBalanceLinkAfterSubCancelling, walletBalanceLinkBeforeSubCancelling) + + subFundsReturnedNativeExpected := new(big.Int).Sub(subBalanceNative, cancellationTxFeeWei) + deltaSpentOnCancellationTxFee := new(big.Int).Sub(subBalanceNative, subFundsReturnedNativeActual) + l.Info(). + Str("Sub Balance - Native", subBalanceNative.String()). + Str("Delta Spent On Cancellation Tx Fee - `NativeBalance - subFundsReturnedNativeActual`", deltaSpentOnCancellationTxFee.String()). + Str("Cancellation Tx Fee Wei", cancellationTxFeeWei.String()). + Str("Sub Funds Returned Actual - Native", subFundsReturnedNativeActual.String()). + Str("Sub Funds Returned Expected - `NativeBalance - cancellationTxFeeWei`", subFundsReturnedNativeExpected.String()). + Str("Sub Funds Returned Actual - Link", subFundsReturnedLinkActual.String()). + Str("Sub Balance - Link", subBalanceLink.String()). + Str("walletBalanceNativeBeforeSubCancelling", walletBalanceNativeBeforeSubCancelling.String()). + Str("walletBalanceNativeAfterSubCancelling", walletBalanceNativeAfterSubCancelling.String()). + Msg("Sub funds returned") + + //todo - need to use different wallet for each test to verify exact amount of Native/LINK returned + //todo - as defaultWallet is used in other tests in parallel which might affect the balance + //require.Equal(t, 1, walletBalanceNativeAfterSubCancelling.Cmp(walletBalanceNativeBeforeSubCancelling), "Native funds were not returned after sub cancellation") + + //todo - this fails on SIMULATED env as tx cost is calculated different as for testnets and it's not receipt.EffectiveGasPrice*receipt.GasUsed + //require.Equal(t, subFundsReturnedNativeExpected, subFundsReturnedNativeActual, "Returned funds are not equal to sub balance that was cancelled") + require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") + + activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + require.NoError(t, err, "error getting active subscription ids") + + require.False( + t, + utils.BigIntSliceContains(activeSubscriptionIdsAfterSubCancellation, subIDForCancelling), + "Active subscription ids should not contain sub id after sub cancellation", + ) + }) + t.Run("Oracle Withdraw", func(t *testing.T) { + testConfig := vrfv2PlusConfig + subIDsForOracleWithDraw, err := vrfv2plus.CreateFundSubsAndAddConsumers( + env, + testConfig, + linkToken, + vrfv2PlusContracts.Coordinator, + vrfv2PlusContracts.LoadTestConsumers, + 1, + ) + require.NoError(t, err) + subIDForOracleWithdraw := subIDsForOracleWithDraw[0] + + fulfilledEventLink, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.LoadTestConsumers[0], vrfv2PlusContracts.Coordinator, vrfv2PlusData, - wrapperSubID, - isNativeBilling, - &vrfv2PlusConfig, + subIDForOracleWithdraw, + false, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, l, ) - require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + require.NoError(t, err) - expectedSubBalanceWei := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) - require.NoError(t, err, "error getting subscription information") - subBalanceAfterRequest := wrapperSubscription.NativeBalance - require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) + fulfilledEventNative, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + subIDForOracleWithdraw, + true, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err) + amountToWithdrawLink := fulfilledEventLink.Payment - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) - require.NoError(t, err, "error getting rand request status") - require.True(t, consumerStatus.Fulfilled) + defaultWalletBalanceNativeBeforeOracleWithdraw, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + require.NoError(t, err) - expectedWrapperConsumerWeiBalance := new(big.Int).Sub(wrapperConsumerBalanceBeforeRequestWei, consumerStatus.Paid) + defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + require.NoError(t, err) - wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) - require.NoError(t, err, "error getting wrapper consumer balance") - require.Equal(t, expectedWrapperConsumerWeiBalance, wrapperConsumerBalanceAfterRequestWei) + l.Info(). + Str("Returning to", defaultWalletAddress). + Str("Amount", amountToWithdrawLink.String()). + Msg("Invoking Oracle Withdraw for LINK") - //todo: uncomment when VRF-651 will be fixed - //require.Equal(t, 1, consumerStatus.Paid.Cmp(randomWordsFulfilledEvent.Payment), "Expected Consumer contract pay more than the Coordinator Sub") - vrfv2plus.LogFulfillmentDetailsNativeBilling(l, wrapperConsumerBalanceBeforeRequestWei, wrapperConsumerBalanceAfterRequestWei, consumerStatus, randomWordsFulfilledEvent) + err = vrfv2PlusContracts.Coordinator.OracleWithdraw( + common.HexToAddress(defaultWalletAddress), + amountToWithdrawLink, + ) + require.NoError(t, err, "error withdrawing LINK from coordinator to default wallet") + amountToWithdrawNative := fulfilledEventNative.Payment - require.Equal(t, vrfv2PlusConfig.NumberOfWords, uint32(len(consumerStatus.RandomWords))) - for _, w := range consumerStatus.RandomWords { - l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") - require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") - } - }) + l.Info(). + Str("Returning to", defaultWalletAddress). + Str("Amount", amountToWithdrawNative.String()). + Msg("Invoking Oracle Withdraw for Native") + + err = vrfv2PlusContracts.Coordinator.OracleWithdrawNative( + common.HexToAddress(defaultWalletAddress), + amountToWithdrawNative, + ) + require.NoError(t, err, "error withdrawing Native tokens from coordinator to default wallet") + + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) + defaultWalletBalanceNativeAfterOracleWithdraw, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + require.NoError(t, err) + + defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + require.NoError(t, err) + + //not possible to verify exact amount of Native/LINK returned as defaultWallet is used in other tests in parallel which might affect the balance + require.Equal(t, 1, defaultWalletBalanceNativeAfterOracleWithdraw.Cmp(defaultWalletBalanceNativeBeforeOracleWithdraw), "Native funds were not returned after oracle withdraw native") + require.Equal(t, 1, defaultWalletBalanceLinkAfterOracleWithdraw.Cmp(defaultWalletBalanceLinkBeforeOracleWithdraw), "LINK funds were not returned after oracle withdraw") + }) } func TestVRFv2PlusMigration(t *testing.T) { @@ -270,7 +609,10 @@ func TestVRFv2PlusMigration(t *testing.T) { linkAddress, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err, "error deploying LINK contract") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, &vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, 2, 1, l) + nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() + require.NoError(t, err, "error getting primary eth address") + + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, nativeTokenPrimaryKeyAddress, 2, 1, l) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] @@ -395,10 +737,10 @@ func TestVRFv2PlusMigration(t *testing.T) { expectedLinkTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorLinkTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.Balance) expectedEthTotalBalanceForOldCoordinator := new(big.Int).Sub(oldCoordinatorEthTotalBalanceBeforeMigration, oldSubscriptionBeforeMigration.NativeBalance) - require.Equal(t, expectedLinkTotalBalanceForMigratedCoordinator, migratedCoordinatorLinkTotalBalanceAfterMigration) - require.Equal(t, expectedEthTotalBalanceForMigratedCoordinator, migratedCoordinatorEthTotalBalanceAfterMigration) - require.Equal(t, expectedLinkTotalBalanceForOldCoordinator, oldCoordinatorLinkTotalBalanceAfterMigration) - require.Equal(t, expectedEthTotalBalanceForOldCoordinator, oldCoordinatorEthTotalBalanceAfterMigration) + require.Equal(t, 0, expectedLinkTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorLinkTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedEthTotalBalanceForMigratedCoordinator.Cmp(migratedCoordinatorEthTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedLinkTotalBalanceForOldCoordinator.Cmp(oldCoordinatorLinkTotalBalanceAfterMigration)) + require.Equal(t, 0, expectedEthTotalBalanceForOldCoordinator.Cmp(oldCoordinatorEthTotalBalanceAfterMigration)) //Verify rand requests fulfills with Link Token billing _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillmentUpgraded( @@ -407,7 +749,7 @@ func TestVRFv2PlusMigration(t *testing.T) { vrfv2PlusData, subID, false, - &vrfv2PlusConfig, + vrfv2PlusConfig, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") @@ -419,7 +761,7 @@ func TestVRFv2PlusMigration(t *testing.T) { vrfv2PlusData, subID, true, - &vrfv2PlusConfig, + vrfv2PlusConfig, l, ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") diff --git a/integration-tests/utils/common.go b/integration-tests/utils/common.go index c8243097a7d..9aacaeed416 100644 --- a/integration-tests/utils/common.go +++ b/integration-tests/utils/common.go @@ -1,6 +1,7 @@ package utils import ( + "math/big" "net" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -23,3 +24,12 @@ func MustIP(s string) *net.IP { } return &ip } + +func BigIntSliceContains(slice []*big.Int, b *big.Int) bool { + for _, a := range slice { + if b.Cmp(a) == 0 { + return true + } + } + return false +} From e623afd8079d0875301df33acf74f75e989abcde Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Mon, 6 Nov 2023 11:22:13 +0100 Subject: [PATCH 074/271] move workflow to correct directory (#11168) --- .../on-demand-log-poller.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/{log_poller_on_demand.yml => workflows/on-demand-log-poller.yml} (95%) diff --git a/.github/log_poller_on_demand.yml b/.github/workflows/on-demand-log-poller.yml similarity index 95% rename from .github/log_poller_on_demand.yml rename to .github/workflows/on-demand-log-poller.yml index 856d1e02349..c5470a5a3dc 100644 --- a/.github/log_poller_on_demand.yml +++ b/.github/workflows/on-demand-log-poller.yml @@ -31,7 +31,7 @@ on: default: "Sepolia" required: true fundingKey: - description: Private key used to fund the contracts + description: Private key used to fund the contracts (must have sufficient ETH and LINK!) required: true rpcURL: description: RPC URL to use From 28e959668484eeaac3e48ccb44a85b6e35ef21f8 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 6 Nov 2023 10:03:10 -0500 Subject: [PATCH 075/271] Include multiple blocks in observation (#11142) * Include multiple blocks in observation * Bump chainlink-relay * Take the trash out * Add prom metrics * Bump chainlink-relay => c28841d7cd41b14fa1207586a905c44f735e9517 * Address PR comments * Update core/services/relay/evm/mercury/v1/data_source.go Co-authored-by: martin-cll <121895364+martin-cll@users.noreply.github.com> * Address PR feedback * Fix test --------- Co-authored-by: martin-cll <121895364+martin-cll@users.noreply.github.com> --- core/chains/evm/types/models.go | 15 ++ core/chains/evm/types/models_test.go | 23 +++ core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../evm/mercury/mocks/chain_head_tracker.go | 30 +-- .../services/relay/evm/mercury/types/types.go | 2 - .../relay/evm/mercury/v1/data_source.go | 90 ++++++--- .../relay/evm/mercury/v1/data_source_test.go | 183 ++++++++++-------- docs/CHANGELOG.md | 7 + go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 13 files changed, 221 insertions(+), 147 deletions(-) diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index a71c9e8716c..6db5d49575c 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -227,6 +227,21 @@ func (h *Head) NextInt() *big.Int { return new(big.Int).Add(h.ToInt(), big.NewInt(1)) } +// AsSlice returns a slice of heads up to length k +// len(heads) may be less than k if the available chain is not long enough +func (h *Head) AsSlice(k int) (heads []*Head) { + if k < 1 || h == nil { + return + } + heads = make([]*Head, 1) + heads[0] = h + for len(heads) < k && h.Parent != nil { + h = h.Parent + heads = append(heads, h) + } + return +} + func (h *Head) UnmarshalJSON(bs []byte) error { type head struct { Hash common.Hash `json:"hash"` diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index 2f9dc7dd7c3..2911e426e86 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -129,6 +129,29 @@ func TestHead_ChainLength(t *testing.T) { assert.Equal(t, uint32(0), head2.ChainLength()) } +func TestHead_AsSlice(t *testing.T) { + h1 := &evmtypes.Head{ + Number: 1, + } + h2 := &evmtypes.Head{ + Number: 2, + Parent: h1, + } + h3 := &evmtypes.Head{ + Number: 3, + Parent: h2, + } + + assert.Len(t, (*evmtypes.Head)(nil).AsSlice(0), 0) + assert.Len(t, (*evmtypes.Head)(nil).AsSlice(1), 0) + + assert.Len(t, h3.AsSlice(0), 0) + assert.Equal(t, []*evmtypes.Head{h3}, h3.AsSlice(1)) + assert.Equal(t, []*evmtypes.Head{h3, h2}, h3.AsSlice(2)) + assert.Equal(t, []*evmtypes.Head{h3, h2, h1}, h3.AsSlice(3)) + assert.Equal(t, []*evmtypes.Head{h3, h2, h1}, h3.AsSlice(4)) +} + func TestModels_HexToFunctionSelector(t *testing.T) { t.Parallel() fid := evmtypes.HexToFunctionSelector("0xb3f98adc") diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 17c2cff1039..6b0a33451e2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -305,7 +305,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index ae1f924c0f7..2cb0eb25826 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1466,8 +1466,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 h1:59vz5H52EpwWE/64ZQpNCs7Gtnyi7/ytjyoGjlbKhBA= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/relay/evm/mercury/mocks/chain_head_tracker.go b/core/services/relay/evm/mercury/mocks/chain_head_tracker.go index 1a5a7e47c5b..b6f2981cf07 100644 --- a/core/services/relay/evm/mercury/mocks/chain_head_tracker.go +++ b/core/services/relay/evm/mercury/mocks/chain_head_tracker.go @@ -4,13 +4,11 @@ package mocks import ( common "github.com/ethereum/go-ethereum/common" - client "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - - commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink/v2/common/types" ) // ChainHeadTracker is an autogenerated mock type for the ChainHeadTracker type @@ -18,32 +16,16 @@ type ChainHeadTracker struct { mock.Mock } -// Client provides a mock function with given fields: -func (_m *ChainHeadTracker) Client() client.Client { - ret := _m.Called() - - var r0 client.Client - if rf, ok := ret.Get(0).(func() client.Client); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(client.Client) - } - } - - return r0 -} - // HeadTracker provides a mock function with given fields: -func (_m *ChainHeadTracker) HeadTracker() commontypes.HeadTracker[*evmtypes.Head, common.Hash] { +func (_m *ChainHeadTracker) HeadTracker() types.HeadTracker[*evmtypes.Head, common.Hash] { ret := _m.Called() - var r0 commontypes.HeadTracker[*evmtypes.Head, common.Hash] - if rf, ok := ret.Get(0).(func() commontypes.HeadTracker[*evmtypes.Head, common.Hash]); ok { + var r0 types.HeadTracker[*evmtypes.Head, common.Hash] + if rf, ok := ret.Get(0).(func() types.HeadTracker[*evmtypes.Head, common.Hash]); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(commontypes.HeadTracker[*evmtypes.Head, common.Hash]) + r0 = ret.Get(0).(types.HeadTracker[*evmtypes.Head, common.Hash]) } } diff --git a/core/services/relay/evm/mercury/types/types.go b/core/services/relay/evm/mercury/types/types.go index ca266ca8ccd..7059689939a 100644 --- a/core/services/relay/evm/mercury/types/types.go +++ b/core/services/relay/evm/mercury/types/types.go @@ -8,14 +8,12 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) //go:generate mockery --quiet --name ChainHeadTracker --output ../mocks/ --case=underscore type ChainHeadTracker interface { - Client() evmclient.Client HeadTracker() httypes.HeadTracker } diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index d225dbee68e..1b16dc76f98 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -8,6 +8,8 @@ import ( "sync" pkgerrors "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -25,6 +27,24 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +var ( + insufficientBlocksCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_insufficient_blocks_count", + Help: fmt.Sprintf("Count of times that there were not enough blocks in the chain during observation (need: %d)", nBlocksObservation), + }, + []string{"feedID"}, + ) + zeroBlocksCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_zero_blocks_count", + Help: "Count of times that there were zero blocks in the chain during observation", + }, + []string{"feedID"}, + ) +) + +// nBlocksObservation controls how many blocks are included in the LatestBlocks observation +const nBlocksObservation int = 5 + type Runner interface { ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) } @@ -51,18 +71,31 @@ type datasource struct { chainHeadTracker types.ChainHeadTracker fetcher Fetcher initialBlockNumber *int64 + + insufficientBlocksCounter prometheus.Counter + zeroBlocksCounter prometheus.Counter } var _ relaymercuryv1.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainHeadTracker types.ChainHeadTracker, fetcher Fetcher, initialBlockNumber *int64, feedID [32]byte) *datasource { - return &datasource{pr, jb, spec, lggr, rr, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainHeadTracker, fetcher, initialBlockNumber} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainHeadTracker types.ChainHeadTracker, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, lggr, rr, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainHeadTracker, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} +} + +type ErrEmptyLatestReport struct { + Err error +} + +func (e ErrEmptyLatestReport) Unwrap() error { return e.Err } + +func (e ErrEmptyLatestReport) Error() string { + return fmt.Sprintf("FetchInitialMaxFinalizedBlockNumber returned empty LatestReport; this is a new feed. No initialBlockNumber was set, tried to use current block number to determine maxFinalizedBlockNumber but got error: %v", e.Err) } func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedBlockNum bool) (obs relaymercuryv1.Observation, pipelineExecutionErr error) { - // setCurrentBlock must come first, along with observationTimestamp, to - // avoid front-running - ds.setCurrentBlock(ctx, &obs) + // setLatestBlocks must come chronologically before observations, along + // with observationTimestamp, to avoid front-running + ds.setLatestBlocks(ctx, &obs) var wg sync.WaitGroup if fetchMaxFinalizedBlockNum { @@ -89,7 +122,7 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam } if ds.initialBlockNumber == nil { if obs.CurrentBlockNum.Err != nil { - obs.MaxFinalizedBlockNumber.Err = fmt.Errorf("FetchInitialMaxFinalizedBlockNumber returned empty LatestReport; this is a new feed. No initialBlockNumber was set, tried to use current block number to determine maxFinalizedBlockNumber but got error: %w", obs.CurrentBlockNum.Err) + obs.MaxFinalizedBlockNumber.Err = ErrEmptyLatestReport{Err: obs.CurrentBlockNum.Err} } else { // Subract 1 here because we will later add 1 to the // maxFinalizedBlockNumber to get the first validFromBlockNum, which @@ -258,37 +291,40 @@ func (ds *datasource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.T return run, trrs, err } -func (ds *datasource) setCurrentBlock(ctx context.Context, obs *relaymercuryv1.Observation) { - latestHead, err := ds.getCurrentBlock(ctx) - if err != nil { +func (ds *datasource) setLatestBlocks(ctx context.Context, obs *relaymercuryv1.Observation) { + latestBlocks := ds.getLatestBlocks(ctx, nBlocksObservation) + if len(latestBlocks) < nBlocksObservation { + ds.insufficientBlocksCounter.Inc() + ds.lggr.Warnw("Insufficient blocks", "latestBlocks", latestBlocks, "lenLatestBlocks", len(latestBlocks), "nBlocksObservation", nBlocksObservation) + } + + // TODO: remove with https://smartcontract-it.atlassian.net/browse/BCF-2209 + if len(latestBlocks) == 0 { + ds.zeroBlocksCounter.Inc() + err := errors.New("no blocks available") obs.CurrentBlockNum.Err = err obs.CurrentBlockHash.Err = err obs.CurrentBlockTimestamp.Err = err - return + } else { + obs.CurrentBlockNum.Val = latestBlocks[0].Number + obs.CurrentBlockHash.Val = latestBlocks[0].Hash.Bytes() + if latestBlocks[0].Timestamp.IsZero() { + obs.CurrentBlockTimestamp.Val = 0 + } else { + obs.CurrentBlockTimestamp.Val = uint64(latestBlocks[0].Timestamp.Unix()) + } } - obs.CurrentBlockNum.Val = latestHead.Number - obs.CurrentBlockHash.Val = latestHead.Hash.Bytes() - if latestHead.Timestamp.IsZero() { - obs.CurrentBlockTimestamp.Val = 0 - } else { - obs.CurrentBlockTimestamp.Val = uint64(latestHead.Timestamp.Unix()) + for _, block := range latestBlocks { + obs.LatestBlocks = append(obs.LatestBlocks, relaymercuryv1.NewBlock(block.Number, block.Hash.Bytes(), uint64(block.Timestamp.Unix()))) } } -func (ds *datasource) getCurrentBlock(ctx context.Context) (*evmtypes.Head, error) { - // Use the headtracker's view of the latest block, this is very fast since +func (ds *datasource) getLatestBlocks(ctx context.Context, k int) (blocks []*evmtypes.Head) { + // Use the headtracker's view of the chain, this is very fast since // it doesn't make any external network requests, and it is the // headtracker's job to ensure it has an up-to-date view of the chain based // on responses from all available RPC nodes latestHead := ds.chainHeadTracker.HeadTracker().LatestChain() - if latestHead == nil { - logger.Sugared(ds.lggr).AssumptionViolation("HeadTracker unexpectedly returned nil head, falling back to RPC call") - var err error - latestHead, err = ds.chainHeadTracker.Client().HeadByNumber(ctx, nil) - if err != nil { - return nil, err - } - } - return latestHead, nil + return latestHead.AsSlice(k) } diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 6e460951301..42983fa0022 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -19,17 +18,17 @@ import ( relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/assets" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -56,11 +55,9 @@ func (m *mockFetcher) LatestTimestamp(context.Context) (int64, error) { var _ types.ChainHeadTracker = &mockHeadTracker{} type mockHeadTracker struct { - c evmclient.Client h httypes.HeadTracker } -func (m *mockHeadTracker) Client() evmclient.Client { return m.c } func (m *mockHeadTracker) HeadTracker() httypes.HeadTracker { return m.h } type mockORM struct { @@ -74,7 +71,8 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg func TestMercury_Observe(t *testing.T) { orm := &mockORM{} - ds := &datasource{lggr: logger.TestLogger(t), orm: orm, codec: (reportcodecv1.ReportCodec{})} + lggr := logger.TestLogger(t) + ds := NewDataSource(orm, nil, job.Job{}, pipeline.Spec{}, lggr, nil, nil, nil, nil, nil, mercuryutils.FeedID{}) ctx := testutils.Context(t) repts := ocrtypes.ReportTimestamp{} @@ -108,9 +106,7 @@ func TestMercury_Observe(t *testing.T) { ds.spec = spec h := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - c := evmclimocks.NewClient(t) ht := &mockHeadTracker{ - c: c, h: h, } ds.chainHeadTracker = ht @@ -202,25 +198,21 @@ func TestMercury_Observe(t *testing.T) { assert.NoError(t, obs.MaxFinalizedBlockNumber.Err) assert.Equal(t, head.Number-1, obs.MaxFinalizedBlockNumber.Val) }) - t.Run("if current block num errored", func(t *testing.T) { + t.Run("if no current block available", func(t *testing.T) { h2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) h2.On("LatestChain").Return((*evmtypes.Head)(nil)) ht.h = h2 - c2 := evmclimocks.NewClient(t) - c2.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, errors.New("head retrieval failed")) - ht.c = c2 obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) - assert.EqualError(t, obs.MaxFinalizedBlockNumber.Err, "FetchInitialMaxFinalizedBlockNumber returned empty LatestReport; this is a new feed. No initialBlockNumber was set, tried to use current block number to determine maxFinalizedBlockNumber but got error: head retrieval failed") + assert.EqualError(t, obs.MaxFinalizedBlockNumber.Err, "FetchInitialMaxFinalizedBlockNumber returned empty LatestReport; this is a new feed. No initialBlockNumber was set, tried to use current block number to determine maxFinalizedBlockNumber but got error: no blocks available") }) }) }) }) ht.h = h - ht.c = c t.Run("when fetchMaxFinalizedBlockNum=false", func(t *testing.T) { t.Run("when run execution fails, returns error", func(t *testing.T) { @@ -322,52 +314,96 @@ func TestMercury_Observe(t *testing.T) { t.Fatal("expected run on channel") } }) - t.Run("if head tracker returns nil, falls back to RPC method", func(t *testing.T) { - t.Run("if call succeeds", func(t *testing.T) { - h = commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - h.On("LatestChain").Return((*evmtypes.Head)(nil)) - ht.h = h - c.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(head, nil).Once() - - obs, err := ds.Observe(ctx, repts, false) - assert.NoError(t, err) + }) - assert.Equal(t, head.Number, obs.CurrentBlockNum.Val) - assert.NoError(t, obs.CurrentBlockNum.Err) - assert.Equal(t, fmt.Sprintf("%x", head.Hash), fmt.Sprintf("%x", obs.CurrentBlockHash.Val)) - assert.NoError(t, obs.CurrentBlockHash.Err) - assert.Equal(t, uint64(head.Timestamp.Unix()), obs.CurrentBlockTimestamp.Val) - assert.NoError(t, obs.CurrentBlockTimestamp.Err) + t.Run("LatestBlocks is populated correctly", func(t *testing.T) { + t.Run("when chain length is zero", func(t *testing.T) { + ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + ht2.On("LatestChain").Return((*evmtypes.Head)(nil)) + ht.h = ht2 - h.AssertExpectations(t) - c.AssertExpectations(t) - }) - t.Run("if call fails, returns error for that observation", func(t *testing.T) { - c = evmclimocks.NewClient(t) - ht.c = c - c.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, errors.New("client call failed")).Once() + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) - obs, err := ds.Observe(ctx, repts, false) - assert.NoError(t, err) + assert.Len(t, obs.LatestBlocks, 0) - assert.Zero(t, obs.CurrentBlockNum.Val) - assert.EqualError(t, obs.CurrentBlockNum.Err, "client call failed") - assert.Zero(t, obs.CurrentBlockHash.Val) - assert.EqualError(t, obs.CurrentBlockHash.Err, "client call failed") - assert.Zero(t, obs.CurrentBlockTimestamp.Val) - assert.EqualError(t, obs.CurrentBlockTimestamp.Err, "client call failed") + ht2.AssertExpectations(t) + }) + t.Run("when chain is too short", func(t *testing.T) { + h4 := &evmtypes.Head{ + Number: 4, + Parent: nil, + } + h5 := &evmtypes.Head{ + Number: 5, + Parent: h4, + } + h6 := &evmtypes.Head{ + Number: 6, + Parent: h5, + } - c.AssertExpectations(t) - }) + ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + ht2.On("LatestChain").Return(h6) + ht.h = ht2 + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.Len(t, obs.LatestBlocks, 3) + assert.Equal(t, 6, int(obs.LatestBlocks[0].Num)) + assert.Equal(t, 5, int(obs.LatestBlocks[1].Num)) + assert.Equal(t, 4, int(obs.LatestBlocks[2].Num)) + + ht2.AssertExpectations(t) + }) + t.Run("when chain is long enough", func(t *testing.T) { + h1 := &evmtypes.Head{ + Number: 1, + } + h2 := &evmtypes.Head{ + Number: 2, + Parent: h1, + } + h3 := &evmtypes.Head{ + Number: 3, + Parent: h2, + } + h4 := &evmtypes.Head{ + Number: 4, + Parent: h3, + } + h5 := &evmtypes.Head{ + Number: 5, + Parent: h4, + } + h6 := &evmtypes.Head{ + Number: 6, + Parent: h5, + } + + ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) + ht2.On("LatestChain").Return(h6) + ht.h = ht2 + + obs, err := ds.Observe(ctx, repts, true) + assert.NoError(t, err) + + assert.Len(t, obs.LatestBlocks, 5) + assert.Equal(t, 6, int(obs.LatestBlocks[0].Num)) + assert.Equal(t, 5, int(obs.LatestBlocks[1].Num)) + assert.Equal(t, 4, int(obs.LatestBlocks[2].Num)) + assert.Equal(t, 3, int(obs.LatestBlocks[3].Num)) + assert.Equal(t, 2, int(obs.LatestBlocks[4].Num)) + + ht2.AssertExpectations(t) }) }) } -func TestMercury_SetCurrentBlock(t *testing.T) { +func TestMercury_SetLatestBlocks(t *testing.T) { lggr := logger.TestLogger(t) - ds := datasource{ - lggr: lggr, - } + ds := NewDataSource(nil, nil, job.Job{}, pipeline.Spec{}, lggr, nil, nil, nil, nil, nil, mercuryutils.FeedID{}) h := evmtypes.Head{ Number: testutils.NewRandomPositiveInt64(), @@ -390,64 +426,41 @@ func TestMercury_SetCurrentBlock(t *testing.T) { ds.chainHeadTracker = chainHeadTracker obs := relaymercuryv1.Observation{} - ds.setCurrentBlock(context.Background(), &obs) + ds.setLatestBlocks(context.Background(), &obs) assert.Equal(t, h.Number, obs.CurrentBlockNum.Val) assert.Equal(t, h.Hash.Bytes(), obs.CurrentBlockHash.Val) assert.Equal(t, uint64(h.Timestamp.Unix()), obs.CurrentBlockTimestamp.Val) - chainHeadTracker.AssertExpectations(t) - headTracker.AssertExpectations(t) - }) - - t.Run("if headtracker returns nil head and eth call succeeds", func(t *testing.T) { - ethClient := evmclimocks.NewClient(t) - headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - chainHeadTracker := mercurymocks.NewChainHeadTracker(t) - - chainHeadTracker.On("Client").Return(ethClient) - chainHeadTracker.On("HeadTracker").Return(headTracker) - // This can happen in some cases e.g. RPC node is offline - headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(&h, nil) - - ds.chainHeadTracker = chainHeadTracker - - obs := relaymercuryv1.Observation{} - ds.setCurrentBlock(context.Background(), &obs) - - assert.Equal(t, h.Number, obs.CurrentBlockNum.Val) - assert.Equal(t, h.Hash.Bytes(), obs.CurrentBlockHash.Val) - assert.Equal(t, uint64(h.Timestamp.Unix()), obs.CurrentBlockTimestamp.Val) + assert.Len(t, obs.LatestBlocks, 1) chainHeadTracker.AssertExpectations(t) - ethClient.AssertExpectations(t) headTracker.AssertExpectations(t) }) - t.Run("if headtracker returns nil head and eth call fails", func(t *testing.T) { - ethClient := evmclimocks.NewClient(t) + t.Run("if headtracker returns nil head", func(t *testing.T) { headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) chainHeadTracker := mercurymocks.NewChainHeadTracker(t) - chainHeadTracker.On("Client").Return(ethClient) chainHeadTracker.On("HeadTracker").Return(headTracker) // This can happen in some cases e.g. RPC node is offline headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) - err := errors.New("foo") - ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, err) ds.chainHeadTracker = chainHeadTracker obs := relaymercuryv1.Observation{} - ds.setCurrentBlock(context.Background(), &obs) + ds.setLatestBlocks(context.Background(), &obs) + + assert.Zero(t, obs.CurrentBlockNum.Val) + assert.Zero(t, obs.CurrentBlockHash.Val) + assert.Zero(t, obs.CurrentBlockTimestamp.Val) + assert.EqualError(t, obs.CurrentBlockNum.Err, "no blocks available") + assert.EqualError(t, obs.CurrentBlockHash.Err, "no blocks available") + assert.EqualError(t, obs.CurrentBlockTimestamp.Err, "no blocks available") - assert.Equal(t, err, obs.CurrentBlockNum.Err) - assert.Equal(t, err, obs.CurrentBlockHash.Err) - assert.Equal(t, err, obs.CurrentBlockTimestamp.Err) + assert.Len(t, obs.LatestBlocks, 0) chainHeadTracker.AssertExpectations(t) - ethClient.AssertExpectations(t) headTracker.AssertExpectations(t) }) } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b5b393542be..a9f9d080f42 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -22,6 +22,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `Optimism2` as a supported gas estimator mode +### Added + +- Mercury v0.2 has improved consensus around current block that uses the most recent 5 blocks instead of only the latest one +- Two new prom metrics for mercury, nops should consider adding alerting on these: + - `mercury_insufficient_blocks_count` + - `mercury_zero_blocks_count` + ... ## 2.7.0 - UNRELEASED diff --git a/go.mod b/go.mod index 999c1b0402f..f271bf421f6 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index ee06cc9b751..8426876e239 100644 --- a/go.sum +++ b/go.sum @@ -1467,8 +1467,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 h1:59vz5H52EpwWE/64ZQpNCs7Gtnyi7/ytjyoGjlbKhBA= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 93820c6ebfe..c4e6fd38482 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -388,7 +388,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 60805eae825..4744fc086a3 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2370,8 +2370,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672 h1:59vz5H52EpwWE/64ZQpNCs7Gtnyi7/ytjyoGjlbKhBA= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231101203911-c686b4d48672/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From b24af1367e5af984db6ebf8523e9949bad736516 Mon Sep 17 00:00:00 2001 From: Andrei Smirnov Date: Mon, 6 Nov 2023 18:13:43 +0300 Subject: [PATCH 076/271] Functions: fixed subscriptions tracking logic (#11167) * Functions: fixed subscriptions tracking logic * Addressed PR feedback * Addressed PR feedback * Addressed PR feedback --- .../handlers/functions/handler.functions.go | 12 ++++- .../handlers/functions/subscriptions.go | 22 ++++---- .../handlers/functions/subscriptions_test.go | 52 +++++++++++++++++-- 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 01f450a4ea0..d0011145d40 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "time" "github.com/ethereum/go-ethereum/common" @@ -178,8 +179,15 @@ func (h *functionsHandler) HandleUserMessage(ctx context.Context, msg *api.Messa return ErrRateLimited } if h.subscriptions != nil && h.minimumBalance != nil { - if balance, err := h.subscriptions.GetMaxUserBalance(sender); err != nil || balance.Cmp(h.minimumBalance.ToInt()) < 0 { - h.lggr.Debug("received a message from a user having insufficient balance", "sender", msg.Body.Sender, "balance", balance.String()) + balance, err := h.subscriptions.GetMaxUserBalance(sender) + if err != nil { + h.lggr.Debugw("error getting max user balance", "sender", msg.Body.Sender, "err", err) + } + if balance == nil { + balance = big.NewInt(0) + } + if err != nil || balance.Cmp(h.minimumBalance.ToInt()) < 0 { + h.lggr.Debugw("received a message from a user having insufficient balance", "sender", msg.Body.Sender, "balance", balance.String()) return fmt.Errorf("sender has insufficient balance: %v juels", balance.String()) } } diff --git a/core/services/gateway/handlers/functions/subscriptions.go b/core/services/gateway/handlers/functions/subscriptions.go index 79233b1031a..c7a6519e693 100644 --- a/core/services/gateway/handlers/functions/subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions.go @@ -130,19 +130,16 @@ func (s *onchainSubscriptions) queryLoop() { blockNumber := big.NewInt(0).Sub(latestBlockHeight, s.blockConfirmations) - updateLastKnownCount := func() { + if lastKnownCount == 0 || start > lastKnownCount { count, err := s.getSubscriptionsCount(ctx, blockNumber) if err != nil { - s.lggr.Errorw("Error getting subscriptions count", "err", err) - return + s.lggr.Errorw("Error getting new subscriptions count", "err", err) + } else { + s.lggr.Infow("Updated subscriptions count", "count", count, "blockNumber", blockNumber.Int64()) + lastKnownCount = count } - s.lggr.Infow("Updated subscriptions count", "err", err, "count", count, "blockNumber", blockNumber.Int64()) - lastKnownCount = count } - if lastKnownCount == 0 { - updateLastKnownCount() - } if lastKnownCount == 0 { s.lggr.Info("Router has no subscriptions yet") return @@ -152,12 +149,9 @@ func (s *onchainSubscriptions) queryLoop() { start = 1 } - end := start + uint64(s.config.UpdateRangeSize) + end := start + uint64(s.config.UpdateRangeSize) - 1 if end > lastKnownCount { - updateLastKnownCount() - if end > lastKnownCount { - end = lastKnownCount - } + end = lastKnownCount } if err := s.querySubscriptionsRange(ctx, blockNumber, start, end); err != nil { s.lggr.Errorw("Error querying subscriptions", "err", err, "start", start, "end", end) @@ -180,6 +174,8 @@ func (s *onchainSubscriptions) queryLoop() { } func (s *onchainSubscriptions) querySubscriptionsRange(ctx context.Context, blockNumber *big.Int, start, end uint64) error { + s.lggr.Debugw("Querying subscriptions", "blockNumber", blockNumber, "start", start, "end", end) + subscriptions, err := s.router.GetSubscriptionsInRange(&bind.CallOpts{ Pending: false, BlockNumber: blockNumber, diff --git a/core/services/gateway/handlers/functions/subscriptions_test.go b/core/services/gateway/handlers/functions/subscriptions_test.go index 1e46bff5c0f..adbf637ad73 100644 --- a/core/services/gateway/handlers/functions/subscriptions_test.go +++ b/core/services/gateway/handlers/functions/subscriptions_test.go @@ -2,6 +2,7 @@ package functions_test import ( "math/big" + "sync/atomic" "testing" "time" @@ -24,9 +25,7 @@ const ( invalidUser = "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C" ) -func TestSubscriptions(t *testing.T) { - t.Parallel() - +func TestSubscriptions_OnePass(t *testing.T) { getSubscriptionCount := hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000003") getSubscriptionsInRange := hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000109e6e1b12098cc8f3a1e9719a817ec53ab9b35c000000000000000000000000000000000000000000000000000034e23f515cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f5340f0968ee8b7dfd97e3327a6139273cc2c4fa000000000000000000000000000000000000000000000001158e460913d000000000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc14b92364c75e20000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005439e5881a529f3ccbffc0e82d49f9db3950aefe") @@ -46,7 +45,7 @@ func TestSubscriptions(t *testing.T) { BlockConfirmations: 1, UpdateFrequencySec: 1, UpdateTimeoutSec: 1, - UpdateRangeSize: 10, + UpdateRangeSize: 3, } subscriptions, err := functions.NewOnchainSubscriptions(client, config, logger.TestLogger(t)) require.NoError(t, err) @@ -57,6 +56,7 @@ func TestSubscriptions(t *testing.T) { assert.NoError(t, subscriptions.Close()) }) + // initially we have 3 subs and range is 3, which needs one pass gomega.NewGomegaWithT(t).Eventually(func() bool { expectedBalance := big.NewInt(0).SetBytes(hexutil.MustDecode("0x01158e460913d00000")) balance, err1 := subscriptions.GetMaxUserBalance(common.HexToAddress(validUser)) @@ -64,3 +64,47 @@ func TestSubscriptions(t *testing.T) { return err1 == nil && err2 != nil && balance.Cmp(expectedBalance) == 0 }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) } + +func TestSubscriptions_MultiPass(t *testing.T) { + const ncycles int32 = 5 + var currentCycle atomic.Int32 + getSubscriptionCount := hexutil.MustDecode("0x0000000000000000000000000000000000000000000000000000000000000006") + getSubscriptionsInRange := hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000109e6e1b12098cc8f3a1e9719a817ec53ab9b35c000000000000000000000000000000000000000000000000000034e23f515cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f5340f0968ee8b7dfd97e3327a6139273cc2c4fa000000000000000000000000000000000000000000000001158e460913d000000000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001bc14b92364c75e20000000000000000000000009ed925d8206a4f88a2f643b28b3035b315753cd60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000005439e5881a529f3ccbffc0e82d49f9db3950aefe") + + ctx := testutils.Context(t) + client := mocks.NewClient(t) + client.On("LatestBlockHeight", mock.Anything).Return(big.NewInt(42), nil) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // getSubscriptionCount + To: &common.Address{}, + Data: hexutil.MustDecode("0x66419970"), + }, mock.Anything).Run(func(args mock.Arguments) { + currentCycle.Add(1) + }).Return(getSubscriptionCount, nil) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // GetSubscriptionsInRange(1,3) + To: &common.Address{}, + Data: hexutil.MustDecode("0xec2454e500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003"), + }, mock.Anything).Return(getSubscriptionsInRange, nil) + client.On("CallContract", mock.Anything, ethereum.CallMsg{ // GetSubscriptionsInRange(4,6) + To: &common.Address{}, + Data: hexutil.MustDecode("0xec2454e500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006"), + }, mock.Anything).Return(getSubscriptionsInRange, nil) + config := functions.OnchainSubscriptionsConfig{ + ContractAddress: common.Address{}, + BlockConfirmations: 1, + UpdateFrequencySec: 1, + UpdateTimeoutSec: 1, + UpdateRangeSize: 3, + } + subscriptions, err := functions.NewOnchainSubscriptions(client, config, logger.TestLogger(t)) + require.NoError(t, err) + + err = subscriptions.Start(ctx) + require.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, subscriptions.Close()) + }) + + gomega.NewGomegaWithT(t).Eventually(func() bool { + return currentCycle.Load() == ncycles + }, testutils.WaitTimeout(t), time.Second).Should(gomega.BeTrue()) +} From a7572683ba10864d98fd1d7cffd27889908d8e22 Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:56:59 +0000 Subject: [PATCH 077/271] Update hardhat config for ArbSepolia and V2_5 (#11177) * Update hardhat config for ArbSepolia and V2_5 * Prettier fix * Minor fix --- contracts/hardhat.config.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contracts/hardhat.config.ts b/contracts/hardhat.config.ts index 521345ffc9e..5306827b8e3 100644 --- a/contracts/hardhat.config.ts +++ b/contracts/hardhat.config.ts @@ -105,6 +105,18 @@ let config = { }, }, }, + 'src/v0.8/vrf/dev/VRFCoordinatorV2_5.sol': { + version: '0.8.6', + settings: { + optimizer: { + enabled: true, + runs: 50, // see native_solc_compile_all_vrf + }, + metadata: { + bytecodeHash: 'none', + }, + }, + }, }, }, contractSizer: { From bda4d5aba0cada85b4dceca88415718d3afd70a8 Mon Sep 17 00:00:00 2001 From: Kashif Date: Tue, 7 Nov 2023 03:13:11 +0900 Subject: [PATCH 078/271] Add kroma support (#11179) * Add kroma support * Bump ctf for kroma client support * add kroma l1 gas test --- .github/workflows/on-demand-ocr-soak-test.yml | 2 + .../config/toml/defaults/Kroma_Mainnet.toml | 26 +++ .../config/toml/defaults/Kroma_Sepolia.toml | 26 +++ core/chains/evm/gas/chain_specific.go | 2 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 12 +- .../gas/rollups/l1_gas_price_oracle_test.go | 22 +++ core/config/chaintype.go | 4 +- core/config/docs/chains-evm.toml | 2 +- core/services/chainlink/config_test.go | 4 +- core/services/ocr/contract_tracker.go | 2 +- docs/CONFIG.md | 160 +++++++++++++++++- .../contracts/contract_deployer.go | 6 + integration-tests/go.mod | 4 +- integration-tests/go.sum | 6 +- 14 files changed, 263 insertions(+), 15 deletions(-) create mode 100644 core/chains/evm/config/toml/defaults/Kroma_Mainnet.toml create mode 100644 core/chains/evm/config/toml/defaults/Kroma_Sepolia.toml diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 4a18aabf226..1e510c23be3 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -26,6 +26,8 @@ on: - "LINEA_MAINNET" - "FANTOM_TESTNET" - "FANTOM_MAINNET" + - "KROMA_MAINNET" + - "KROMA_SEPOLIA" fundingPrivateKey: description: Private funding key (Skip for Simulated) required: false diff --git a/core/chains/evm/config/toml/defaults/Kroma_Mainnet.toml b/core/chains/evm/config/toml/defaults/Kroma_Mainnet.toml new file mode 100644 index 00000000000..55154bf766c --- /dev/null +++ b/core/chains/evm/config/toml/defaults/Kroma_Mainnet.toml @@ -0,0 +1,26 @@ +ChainID = '255' +ChainType = 'kroma' # Kroma is based on the Optimism Bedrock architechture +FinalityDepth = 400 +LogPollInterval = '2s' +NoNewHeadsThreshold = '40s' +MinIncomingConfirmations = 1 + +[GasEstimator] +EIP1559DynamicFees = true +PriceMin = '1 wei' +BumpMin = '100 wei' + +[GasEstimator.BlockHistory] +BlockHistorySize = 24 + +[Transactions] +ResendAfterThreshold = '30s' + +[HeadTracker] +HistoryDepth = 400 + +[NodePool] +SyncThreshold = 10 + +[OCR] +ContractConfirmations = 1 diff --git a/core/chains/evm/config/toml/defaults/Kroma_Sepolia.toml b/core/chains/evm/config/toml/defaults/Kroma_Sepolia.toml new file mode 100644 index 00000000000..643b0556b32 --- /dev/null +++ b/core/chains/evm/config/toml/defaults/Kroma_Sepolia.toml @@ -0,0 +1,26 @@ +ChainID = '2358' +ChainType = 'kroma' # Kroma is based on the Optimism Bedrock architechture +FinalityDepth = 400 +LogPollInterval = '2s' +NoNewHeadsThreshold = '40s' +MinIncomingConfirmations = 1 + +[GasEstimator] +EIP1559DynamicFees = true +PriceMin = '1 wei' +BumpMin = '100 wei' + +[GasEstimator.BlockHistory] +BlockHistorySize = 24 + +[Transactions] +ResendAfterThreshold = '30s' + +[HeadTracker] +HistoryDepth = 400 + +[NodePool] +SyncThreshold = 10 + +[OCR] +ContractConfirmations = 1 diff --git a/core/chains/evm/gas/chain_specific.go b/core/chains/evm/gas/chain_specific.go index cd38f49ee0b..4d87b8b454e 100644 --- a/core/chains/evm/gas/chain_specific.go +++ b/core/chains/evm/gas/chain_specific.go @@ -19,7 +19,7 @@ func chainSpecificIsUsable(tx evmtypes.Transaction, baseFee *assets.Wei, chainTy return false } } - if chainType == config.ChainOptimismBedrock { + if chainType == config.ChainOptimismBedrock || chainType == config.ChainKroma { // This is a special deposit transaction type introduced in Bedrock upgrade. // This is a system transaction that it will occur at least one time per block. // We should discard this type before even processing it to avoid flooding the diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index c15aa23c792..d990017bd0f 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -57,11 +57,18 @@ const ( // `function l1BaseFee() external view returns (uint256);` OPGasOracle_l1BaseFee = "519b4bd3" + // GasOracleAddress is the address of the precompiled contract that exists on Kroma chain. + // This is the case for Kroma. + KromaGasOracleAddress = "0x4200000000000000000000000000000000000005" + // GasOracle_l1BaseFee is the a hex encoded call to: + // `function l1BaseFee() external view returns (uint256);` + KromaGasOracle_l1BaseFee = "519b4bd3" + // Interval at which to poll for L1BaseFee. A good starting point is the L1 block time. PollPeriod = 12 * time.Second ) -var supportedChainTypes = []config.ChainType{config.ChainArbitrum, config.ChainOptimismBedrock} +var supportedChainTypes = []config.ChainType{config.ChainArbitrum, config.ChainOptimismBedrock, config.ChainKroma} func IsRollupWithL1Support(chainType config.ChainType) bool { return slices.Contains(supportedChainTypes, chainType) @@ -76,6 +83,9 @@ func NewL1GasPriceOracle(lggr logger.Logger, ethClient ethClient, chainType conf case config.ChainOptimismBedrock: address = OPGasOracleAddress callArgs = OPGasOracle_l1BaseFee + case config.ChainKroma: + address = KromaGasOracleAddress + callArgs = KromaGasOracle_l1BaseFee default: panic(fmt.Sprintf("Received unspported chaintype %s", chainType)) } diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go index 9fd2a66201c..320c9cb71da 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go @@ -59,6 +59,28 @@ func TestL1GasPriceOracle(t *testing.T) { assert.Equal(t, assets.NewWei(l1BaseFee), gasPrice) }) + t.Run("Calling GasPrice on started Kroma L1Oracle returns Kroma l1GasPrice", func(t *testing.T) { + l1BaseFee := big.NewInt(200) + + ethClient := mocks.NewETHClient(t) + ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { + callMsg := args.Get(1).(ethereum.CallMsg) + blockNumber := args.Get(2).(*big.Int) + assert.Equal(t, KromaGasOracleAddress, callMsg.To.String()) + assert.Equal(t, KromaGasOracle_l1BaseFee, fmt.Sprintf("%x", callMsg.Data)) + assert.Nil(t, blockNumber) + }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) + + oracle := NewL1GasPriceOracle(logger.TestLogger(t), ethClient, config.ChainKroma) + require.NoError(t, oracle.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) + + gasPrice, err := oracle.GasPrice(testutils.Context(t)) + require.NoError(t, err) + + assert.Equal(t, assets.NewWei(l1BaseFee), gasPrice) + }) + t.Run("Calling GasPrice on started OPStack L1Oracle returns OPStack l1GasPrice", func(t *testing.T) { l1BaseFee := big.NewInt(200) diff --git a/core/config/chaintype.go b/core/config/chaintype.go index fe67b0925aa..c99099ee616 100644 --- a/core/config/chaintype.go +++ b/core/config/chaintype.go @@ -15,16 +15,18 @@ const ( ChainOptimismBedrock ChainType = "optimismBedrock" ChainXDai ChainType = "xdai" ChainCelo ChainType = "celo" + ChainKroma ChainType = "kroma" ) var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Join([]string{ string(ChainArbitrum), string(ChainMetis), string(ChainXDai), string(ChainOptimismBedrock), string(ChainCelo), + string(ChainKroma), }, ", ")) // IsValid returns true if the ChainType value is known or empty. func (c ChainType) IsValid() bool { switch c { - case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo: + case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma: return true } return false diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 0e0d0d0bd81..082bbd6cd19 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -14,7 +14,7 @@ BlockBackfillDepth = 10 # Default # BlockBackfillSkip enables skipping of very long backfills. BlockBackfillSkip = false # Default # ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -# Available types: arbitrum, metis, optimismBedrock, xdai +# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma ChainType = 'arbitrum' # Example # FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID so it should not be necessary to change this under normal operation. # BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 96e6db42c80..59a02f1dcf9 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -1190,7 +1190,7 @@ func TestConfig_Validate(t *testing.T) { - 1: 6 errors: - ChainType: invalid value (Foo): must not be set with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo or omitted + - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma or omitted - HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth - GasEstimator: 2 errors: - FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault @@ -1199,7 +1199,7 @@ func TestConfig_Validate(t *testing.T) { - 2: 5 errors: - ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo or omitted + - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma or omitted - FinalityDepth: invalid value (0): must be greater than or equal to 1 - MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1 - 3.Nodes: 5 errors: diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index c5f3e431e45..3a216e025f0 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -401,7 +401,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight // care about the block height; we have no way of getting the L1 block // height anyway return 0, nil - case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai: + case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma: // continue } latestBlockHeight := t.getLatestBlockHeight() diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 23508df172a..4b55c804a3d 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -2907,6 +2907,85 @@ GasLimit = 3800000

+
Kroma Mainnet (255)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'kroma' +FinalityDepth = 400 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '2s' +LogKeepBlocksDepth = 100000 +MinIncomingConfirmations = 1 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '40s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '30s' + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '1 wei' +LimitDefault = 500000 +LimitMax = 500000 +LimitMultiplier = '1' +LimitTransfer = 21000 +BumpMin = '100 wei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = true +FeeCapDefault = '100 gwei' +TipCapDefault = '1 wei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 24 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 400 +MaxBufferSize = 3 +SamplingInterval = '1s' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 10 +LeaseDuration = '0s' + +[OCR] +ContractConfirmations = 1 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5300000 +``` + +

+
Optimism Goerli (420)

```toml @@ -3301,6 +3380,85 @@ GasLimit = 5300000

+
Kroma Sepolia (2358)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'kroma' +FinalityDepth = 400 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '2s' +LogKeepBlocksDepth = 100000 +MinIncomingConfirmations = 1 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '40s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '30s' + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '1 wei' +LimitDefault = 500000 +LimitMax = 500000 +LimitMultiplier = '1' +LimitTransfer = 21000 +BumpMin = '100 wei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = true +FeeCapDefault = '100 gwei' +TipCapDefault = '1 wei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 24 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 400 +MaxBufferSize = 3 +SamplingInterval = '1s' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 10 +LeaseDuration = '0s' + +[OCR] +ContractConfirmations = 1 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5300000 +``` + +

+
Fantom Testnet (4002)

```toml @@ -4916,7 +5074,7 @@ BlockBackfillSkip enables skipping of very long backfills. ChainType = 'arbitrum' # Example ``` ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -Available types: arbitrum, metis, optimismBedrock, xdai +Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma ### FinalityDepth ```toml diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 5a3fad256e4..0c36a260815 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -173,6 +173,8 @@ func NewContractDeployer(bcClient blockchain.EVMClient, logger zerolog.Logger) ( return &LineaContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil case *blockchain.FantomClient: return &FantomContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil + case *blockchain.KromaClient: + return &KromaContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil } return nil, errors.New("unknown blockchain client implementation for contract deployer, register blockchain client in NewContractDeployer") } @@ -240,6 +242,10 @@ type FantomContractDeployer struct { *EthereumContractDeployer } +type KromaContractDeployer struct { + *EthereumContractDeployer +} + // NewEthereumContractDeployer returns an instantiated instance of the ETH contract deployer func NewEthereumContractDeployer(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractDeployer { return &EthereumContractDeployer{ diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c4e6fd38482..f73838f1ab0 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,7 +22,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.2-0.20231030212542-5fb562e774a5 + github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.27 @@ -427,7 +427,6 @@ require ( github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xlab/treeprint v1.1.0 // indirect - github.com/yuin/goldmark v1.4.13 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.1 // indirect @@ -455,7 +454,6 @@ require ( golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 4744fc086a3..843cef26901 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.2-0.20231030212542-5fb562e774a5 h1:4hTf8pvtdtwoaeKFSEYjBZPvDbZ05WgiHsb0TPL6HqQ= -github.com/smartcontractkit/chainlink-testing-framework v1.18.2-0.20231030212542-5fb562e774a5/go.mod h1:lMdEUTdSmzldCwqf+todFEyebE9Vlb23+5rvIHJBPOk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66 h1:AOqcHiAppMoIvM2WSJNIZzJDnOQNXyElbLFK3ZqoJeM= +github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= @@ -2578,7 +2578,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -2765,7 +2764,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= From 0f66f7fd324fb0b8ecef07989eee7dd55bdcdf32 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Mon, 6 Nov 2023 20:28:52 +0200 Subject: [PATCH 079/271] Move common client models to correct path (#11169) * Move common client models to correct path * Drop unnecessary aliases --- common/{chains => }/client/models.go | 0 common/client/multi_node.go | 7 +- common/client/node.go | 5 +- common/client/send_only_node.go | 3 +- common/txmgr/broadcaster.go | 24 +++--- common/txmgr/confirmer.go | 20 ++--- common/txmgr/resender.go | 8 +- common/txmgr/types/client.go | 6 +- core/chains/evm/client/chain_client.go | 3 +- core/chains/evm/client/client.go | 6 +- core/chains/evm/client/client_test.go | 57 +++++++------ core/chains/evm/client/errors.go | 38 ++++----- core/chains/evm/client/helpers_test.go | 5 +- core/chains/evm/client/mocks/client.go | 14 ++-- core/chains/evm/client/null_client.go | 6 +- core/chains/evm/client/rpc_client.go | 5 +- .../evm/client/simulated_backend_client.go | 10 +-- core/chains/evm/txmgr/broadcaster_test.go | 82 +++++++++--------- core/chains/evm/txmgr/client.go | 20 ++--- core/chains/evm/txmgr/confirmer_test.go | 84 +++++++++---------- core/cmd/shell_local_test.go | 24 +++--- core/internal/cltest/cltest.go | 4 +- 22 files changed, 212 insertions(+), 219 deletions(-) rename common/{chains => }/client/models.go (100%) diff --git a/common/chains/client/models.go b/common/client/models.go similarity index 100% rename from common/chains/client/models.go rename to common/client/models.go diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 0da3b89076b..f54e3115d95 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/common/chains/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/assets" @@ -121,7 +120,7 @@ type multiNode[ chStop utils.StopChan wg sync.WaitGroup - sendOnlyErrorParser func(err error) client.SendTxReturnCode + sendOnlyErrorParser func(err error) SendTxReturnCode } func NewMultiNode[ @@ -147,7 +146,7 @@ func NewMultiNode[ chainID CHAIN_ID, chainType config.ChainType, chainFamily string, - sendOnlyErrorParser func(err error) client.SendTxReturnCode, + sendOnlyErrorParser func(err error) SendTxReturnCode, ) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { nodeSelector := func() NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] { switch selectionMode { @@ -605,7 +604,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP txErr := n.RPC().SendTransaction(ctx, tx) c.logger.Debugw("Sendonly node sent transaction", "name", n.String(), "tx", tx, "err", txErr) sendOnlyError := c.sendOnlyErrorParser(txErr) - if sendOnlyError != client.Successful { + if sendOnlyError != Successful { c.logger.Warnw("RPC returned error", "name", n.String(), "tx", tx, "err", txErr) } }(n) diff --git a/common/client/node.go b/common/client/node.go index 71b34452f02..20d098e03f7 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/common/chains/client" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -136,7 +135,7 @@ func NewNode[ } n.nodeCtx, n.cancelNodeCtx = context.WithCancel(context.Background()) lggr = lggr.Named("Node").With( - "nodeTier", client.Primary.String(), + "nodeTier", Primary.String(), "nodeName", name, "node", n.String(), "chainID", chainID, @@ -150,7 +149,7 @@ func NewNode[ } func (n *node[CHAIN_ID, HEAD, RPC]) String() string { - s := fmt.Sprintf("(%s)%s:%s", client.Primary.String(), n.name, n.ws.String()) + s := fmt.Sprintf("(%s)%s:%s", Primary.String(), n.name, n.ws.String()) if n.http != nil { s = s + fmt.Sprintf(":%s", n.http.String()) } diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index 3b382b2dcb0..0051fb014d9 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -8,7 +8,6 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/common/chains/client" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -159,7 +158,7 @@ func (s *sendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { } func (s *sendOnlyNode[CHAIN_ID, RPC]) String() string { - return fmt.Sprintf("(%s)%s:%s", client.Secondary.String(), s.name, s.uri.Redacted()) + return fmt.Sprintf("(%s)%s:%s", Secondary.String(), s.name, s.uri.Redacted()) } func (s *sendOnlyNode[CHAIN_ID, RPC]) setState(state nodeState) (changed bool) { diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 4f6ffae2ad8..80c7adbfc16 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -15,7 +15,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-relay/pkg/services" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" @@ -553,19 +553,19 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand lgr.Infow("Sending transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash, "err", err, "meta", etx.Meta, "feeLimit", etx.FeeLimit, "attempt", attempt, "etx", etx) errType, err := eb.client.SendTransactionReturnCode(ctx, etx, attempt, lgr) - if errType != clienttypes.Fatal { + if errType != client.Fatal { etx.InitialBroadcastAt = &initialBroadcastAt etx.BroadcastAt = &initialBroadcastAt } switch errType { - case clienttypes.Fatal: + case client.Fatal: eb.SvcErrBuffer.Append(err) etx.Error = null.StringFrom(err.Error()) return eb.saveFatallyErroredTransaction(lgr, &etx), true - case clienttypes.TransactionAlreadyKnown: + case client.TransactionAlreadyKnown: fallthrough - case clienttypes.Successful: + case client.Successful: // Either the transaction was successful or one of the following four scenarios happened: // // SCENARIO 1 @@ -618,9 +618,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // Increment sequence if successfully broadcasted eb.IncrementNextSequence(etx.FromAddress, sequence) return err, true - case clienttypes.Underpriced: + case client.Underpriced: return eb.tryAgainBumpingGas(ctx, lgr, err, etx, attempt, initialBroadcastAt) - case clienttypes.InsufficientFunds: + case client.InsufficientFunds: // NOTE: This bails out of the entire cycle and essentially "blocks" on // any transaction that gets insufficient_funds. This is OK if a // transaction with a large VALUE blocks because this always comes last @@ -630,13 +630,13 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // theoretically be sent, but will instead be blocked. eb.SvcErrBuffer.Append(err) fallthrough - case clienttypes.Retryable: + case client.Retryable: return err, true - case clienttypes.FeeOutOfValidRange: + case client.FeeOutOfValidRange: return eb.tryAgainWithNewEstimation(ctx, lgr, err, etx, attempt, initialBroadcastAt) - case clienttypes.Unsupported: + case client.Unsupported: return err, false - case clienttypes.ExceedsMaxFee: + case client.ExceedsMaxFee: // Broadcaster: Note that we may have broadcast to multiple nodes and had it // accepted by one of them! It is not guaranteed that all nodes share // the same tx fee cap. That is why we must treat this as an unknown @@ -649,7 +649,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand default: // Every error that doesn't fall under one of the above categories will be treated as Unknown. fallthrough - case clienttypes.Unknown: + case client.Unknown: eb.SvcErrBuffer.Append(err) lgr.Criticalw(`Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+ `Urgent resolution required, Chainlink is currently operating in a degraded state and may miss transactions`, "err", err, "etx", etx, "attempt", attempt) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index c22a1594570..1d921490945 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -15,7 +15,7 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-relay/pkg/services" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -362,7 +362,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Che for idx, txErr := range txErrs { // Add to Unconfirm array, all tx where error wasn't TransactionAlreadyKnown. if txErr != nil { - if txCodes[idx] == clienttypes.TransactionAlreadyKnown { + if txCodes[idx] == client.TransactionAlreadyKnown { continue } } @@ -819,7 +819,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han errType, sendError := ec.client.SendTransactionReturnCode(ctx, etx, attempt, lggr) switch errType { - case clienttypes.Underpriced: + case client.Underpriced: // This should really not ever happen in normal operation since we // already bumped above the required minimum in broadcaster. ec.lggr.Warnw("Got terminally underpriced error for gas bump, this should never happen unless the remote RPC node changed its configuration on the fly, or you are using multiple RPC nodes with different minimum gas price requirements. This is not recommended", "err", sendError, "attempt", attempt) @@ -854,12 +854,12 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han return errors.Wrap(err, "saveReplacementInProgressAttempt failed") } return ec.handleInProgressAttempt(ctx, lggr, etx, replacementAttempt, blockHeight) - case clienttypes.ExceedsMaxFee: + case client.ExceedsMaxFee: // Confirmer: The gas price was bumped too high. This transaction attempt cannot be accepted. // Best thing we can do is to re-send the previous attempt at the old // price and discard this bumped version. fallthrough - case clienttypes.Fatal: + case client.Fatal: // WARNING: This should never happen! // Should NEVER be fatal this is an invariant violation. The // Broadcaster can never create a TxAttempt that will @@ -874,20 +874,20 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han ec.SvcErrBuffer.Append(sendError) // This will loop continuously on every new head so it must be handled manually by the node operator! return ec.txStore.DeleteInProgressAttempt(ctx, attempt) - case clienttypes.TransactionAlreadyKnown: + case client.TransactionAlreadyKnown: // Sequence too low indicated that a transaction at this sequence was confirmed already. // Mark confirmed_missing_receipt and wait for the next cycle to try to get a receipt lggr.Debugw("Sequence already used", "txAttemptID", attempt.ID, "txHash", attempt.Hash.String(), "err", sendError) timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveConfirmedMissingReceiptAttempt(ctx, timeout, &attempt, now) - case clienttypes.InsufficientFunds: + case client.InsufficientFunds: timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveInsufficientFundsAttempt(ctx, timeout, &attempt, now) - case clienttypes.Successful: + case client.Successful: lggr.Debugw("Successfully broadcast transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash.String()) timeout := ec.dbConfig.DefaultQueryTimeout() return ec.txStore.SaveSentAttempt(ctx, timeout, &attempt, now) - case clienttypes.Unknown: + case client.Unknown: // Every error that doesn't fall under one of the above categories will be treated as Unknown. fallthrough default: @@ -1058,7 +1058,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) For } attempt.Tx = *etx // for logging ec.lggr.Debugw("Sending transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash, "err", err, "meta", etx.Meta, "feeLimit", etx.FeeLimit, "attempt", attempt) - if errCode, err := ec.client.SendTransactionReturnCode(context.TODO(), *etx, attempt, ec.lggr); errCode != clienttypes.Successful && err != nil { + if errCode, err := ec.client.SendTransactionReturnCode(context.TODO(), *etx, attempt, ec.lggr); errCode != client.Successful && err != nil { ec.lggr.Errorw(fmt.Sprintf("ForceRebroadcast: failed to rebroadcast tx %v with sequence %v and gas limit %v: %s", etx.ID, *etx.Sequence, etx.FeeLimit, err.Error()), "err", err, "fee", attempt.TxFee) continue } diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index 655de0f1135..d788b82773f 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" @@ -175,13 +175,13 @@ func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) resendUnconfi return nil } -func logResendResult(lggr logger.Logger, codes []clienttypes.SendTxReturnCode) { +func logResendResult(lggr logger.Logger, codes []client.SendTxReturnCode) { var nNew int var nFatal int for _, c := range codes { - if c == clienttypes.Successful { + if c == client.Successful { nNew++ - } else if c == clienttypes.Fatal { + } else if c == client.Fatal { nFatal++ } } diff --git a/common/txmgr/types/client.go b/common/txmgr/types/client.go index 6d7f1c55558..58c1b6f6ad2 100644 --- a/common/txmgr/types/client.go +++ b/common/txmgr/types/client.go @@ -6,7 +6,7 @@ import ( "math/big" "time" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -49,7 +49,7 @@ type TransactionClient[ bathSize int, lggr logger.Logger, ) ( - txCodes []clienttypes.SendTxReturnCode, + txCodes []client.SendTxReturnCode, txErrs []error, broadcastTime time.Time, successfulTxIDs []int64, @@ -59,7 +59,7 @@ type TransactionClient[ tx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], lggr logger.Logger, - ) (clienttypes.SendTxReturnCode, error) + ) (client.SendTxReturnCode, error) SendEmptyTransaction( ctx context.Context, newTxAttempt func(seq SEQ, feeLimit uint32, fee FEE, fromAddress ADDR) (attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error), diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index bda028cbf33..4c5108745c5 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - commontypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -213,7 +212,7 @@ func (c *chainClient) SendTransaction(ctx context.Context, tx *types.Transaction return c.multiNode.SendTransaction(ctx, tx) } -func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commontypes.SendTxReturnCode, error) { +func (c *chainClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { err := c.SendTransaction(ctx, tx) return ClassifySendError(err, c.logger, tx, fromAddress, c.IsL2()) } diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index af03720ced9..fb8a39f3798 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -6,7 +6,7 @@ import ( "strings" "time" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -61,7 +61,7 @@ type Client interface { HeadByHash(ctx context.Context, n common.Hash) (*evmtypes.Head, error) SubscribeNewHead(ctx context.Context, ch chan<- *evmtypes.Head) (ethereum.Subscription, error) - SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (clienttypes.SendTxReturnCode, error) + SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) // Wrapped Geth client methods // blockNumber can be specified as `nil` to imply latest block @@ -211,7 +211,7 @@ func (client *client) HeaderByHash(ctx context.Context, h common.Hash) (*types.H return client.pool.HeaderByHash(ctx, h) } -func (client *client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (clienttypes.SendTxReturnCode, error) { +func (client *client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { err := client.SendTransaction(ctx, tx) return ClassifySendError(err, client.logger, tx, fromAddress, client.pool.ChainType().IsL2()) } diff --git a/core/chains/evm/client/client_test.go b/core/chains/evm/client/client_test.go index 81a82d20fa7..673fe044afe 100644 --- a/core/chains/evm/client/client_test.go +++ b/core/chains/evm/client/client_test.go @@ -24,49 +24,48 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - commontypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/utils" ) -func mustNewClient(t *testing.T, wsURL string, sendonlys ...url.URL) evmclient.Client { +func mustNewClient(t *testing.T, wsURL string, sendonlys ...url.URL) client.Client { return mustNewClientWithChainID(t, wsURL, testutils.FixtureChainID, sendonlys...) } -func mustNewClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) evmclient.Client { - cfg := evmclient.TestNodePoolConfig{ - NodeSelectionMode: evmclient.NodeSelectionMode_RoundRobin, +func mustNewClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) client.Client { + cfg := client.TestNodePoolConfig{ + NodeSelectionMode: client.NodeSelectionMode_RoundRobin, } - c, err := evmclient.NewClientWithTestNode(t, cfg, time.Second*0, wsURL, nil, sendonlys, 42, chainID) + c, err := client.NewClientWithTestNode(t, cfg, time.Second*0, wsURL, nil, sendonlys, 42, chainID) require.NoError(t, err) return c } -func mustNewChainClient(t *testing.T, wsURL string, sendonlys ...url.URL) evmclient.Client { +func mustNewChainClient(t *testing.T, wsURL string, sendonlys ...url.URL) client.Client { return mustNewChainClientWithChainID(t, wsURL, testutils.FixtureChainID, sendonlys...) } -func mustNewChainClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) evmclient.Client { - cfg := evmclient.TestNodePoolConfig{ - NodeSelectionMode: evmclient.NodeSelectionMode_RoundRobin, +func mustNewChainClientWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) client.Client { + cfg := client.TestNodePoolConfig{ + NodeSelectionMode: client.NodeSelectionMode_RoundRobin, } - c, err := evmclient.NewChainClientWithTestNode(t, cfg, time.Second*0, cfg.NodeLeaseDuration, wsURL, nil, sendonlys, 42, chainID) + c, err := client.NewChainClientWithTestNode(t, cfg, time.Second*0, cfg.NodeLeaseDuration, wsURL, nil, sendonlys, 42, chainID) require.NoError(t, err) return c } -func mustNewClients(t *testing.T, wsURL string, sendonlys ...url.URL) []evmclient.Client { - var clients []evmclient.Client +func mustNewClients(t *testing.T, wsURL string, sendonlys ...url.URL) []client.Client { + var clients []client.Client clients = append(clients, mustNewClient(t, wsURL, sendonlys...)) clients = append(clients, mustNewChainClient(t, wsURL, sendonlys...)) return clients } -func mustNewClientsWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) []evmclient.Client { - var clients []evmclient.Client +func mustNewClientsWithChainID(t *testing.T, wsURL string, chainID *big.Int, sendonlys ...url.URL) []client.Client { + var clients []client.Client clients = append(clients, mustNewClientWithChainID(t, wsURL, chainID, sendonlys...)) clients = append(clients, mustNewChainClientWithChainID(t, wsURL, chainID, sendonlys...)) return clients @@ -287,7 +286,7 @@ func TestEthClient_GetERC20Balance(t *testing.T) { t.Run(test.name, func(t *testing.T) { contractAddress := testutils.NewAddress() userAddress := testutils.NewAddress() - functionSelector := evmtypes.HexToFunctionSelector(evmclient.BALANCE_OF_ADDRESS_FUNCTION_SELECTOR) // balanceOf(address) + functionSelector := evmtypes.HexToFunctionSelector(client.BALANCE_OF_ADDRESS_FUNCTION_SELECTOR) // balanceOf(address) txData := utils.ConcatBytes(functionSelector.Bytes(), common.LeftPadBytes(userAddress.Bytes(), utils.EVMWordByteLen)) wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { @@ -522,7 +521,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.Fatal) + assert.Equal(t, errType, commonclient.Fatal) } }) @@ -550,7 +549,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.TransactionAlreadyKnown) + assert.Equal(t, errType, commonclient.TransactionAlreadyKnown) } }) @@ -577,7 +576,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.NoError(t, err) - assert.Equal(t, errType, commontypes.Successful) + assert.Equal(t, errType, commonclient.Successful) } }) @@ -605,7 +604,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.Underpriced) + assert.Equal(t, errType, commonclient.Underpriced) } }) @@ -633,7 +632,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.Unsupported) + assert.Equal(t, errType, commonclient.Unsupported) } }) @@ -661,7 +660,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.Retryable) + assert.Equal(t, errType, commonclient.Retryable) } }) @@ -689,7 +688,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.InsufficientFunds) + assert.Equal(t, errType, commonclient.InsufficientFunds) } }) @@ -717,7 +716,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.ExceedsMaxFee) + assert.Equal(t, errType, commonclient.ExceedsMaxFee) } }) @@ -745,7 +744,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { errType, err := ethClient.SendTransactionReturnCode(testutils.Context(t), tx, fromAddress) assert.Error(t, err) - assert.Equal(t, errType, commontypes.Unknown) + assert.Equal(t, errType, commonclient.Unknown) } }) } @@ -811,7 +810,7 @@ func TestEthClient_ErroringClient(t *testing.T) { ctx := testutils.Context(t) // Empty node means there are no active nodes to select from, causing client to always return error. - erroringClient := evmclient.NewChainClientWithEmptyNode(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID) + erroringClient := client.NewChainClientWithEmptyNode(t, commonclient.NodeSelectionModeRoundRobin, time.Second*0, time.Second*0, testutils.FixtureChainID) _, err := erroringClient.BalanceAt(ctx, common.Address{}, nil) require.Equal(t, err, commonclient.ErroringNodeError) @@ -883,7 +882,7 @@ func TestEthClient_ErroringClient(t *testing.T) { require.Equal(t, err, commonclient.ErroringNodeError) code, err := erroringClient.SendTransactionReturnCode(ctx, nil, common.Address{}) - require.Equal(t, code, commontypes.Unknown) + require.Equal(t, code, commonclient.Unknown) require.Equal(t, err, commonclient.ErroringNodeError) _, err = erroringClient.SequenceAt(ctx, common.Address{}, nil) @@ -912,4 +911,4 @@ func TestEthClient_ErroringClient(t *testing.T) { } -const headResult = evmclient.HeadResult +const headResult = client.HeadResult diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 7197d77b3d9..0d177455e33 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -397,20 +397,20 @@ func ExtractRPCError(baseErr error) (*JsonError, error) { return &jErr, nil } -func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (clienttypes.SendTxReturnCode, error) { +func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fromAddress common.Address, isL2 bool) (commonclient.SendTxReturnCode, error) { sendError := NewSendError(err) if sendError == nil { - return clienttypes.Successful, err + return commonclient.Successful, err } if sendError.Fatal() { lggr.Criticalw("Fatal error sending transaction", "err", sendError, "etx", tx) // Attempt is thrown away in this case; we don't need it since it never got accepted by a node - return clienttypes.Fatal, err + return commonclient.Fatal, err } if sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() { // Nonce too low indicated that a transaction at this nonce was confirmed already. // Mark it as TransactionAlreadyKnown. - return clienttypes.TransactionAlreadyKnown, err + return commonclient.TransactionAlreadyKnown, err } if sendError.IsReplacementUnderpriced() { lggr.Errorw(fmt.Sprintf("Replacement transaction underpriced for eth_tx %x. "+ @@ -419,41 +419,41 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro tx.Hash(), err), "gasPrice", tx.GasPrice, "gasTipCap", tx.GasTipCap, "gasFeeCap", tx.GasFeeCap) // Assume success and hand off to the next cycle. - return clienttypes.Successful, err + return commonclient.Successful, err } if sendError.IsTransactionAlreadyInMempool() { lggr.Debugw("Transaction already in mempool", "txHash", tx.Hash, "nodeErr", sendError.Error()) - return clienttypes.Successful, err + return commonclient.Successful, err } if sendError.IsTemporarilyUnderpriced() { lggr.Infow("Transaction temporarily underpriced", "err", sendError.Error()) - return clienttypes.Successful, err + return commonclient.Successful, err } if sendError.IsTerminallyUnderpriced() { - return clienttypes.Underpriced, err + return commonclient.Underpriced, err } if sendError.L2FeeTooLow() || sendError.IsL2FeeTooHigh() || sendError.IsL2Full() { if isL2 { - return clienttypes.FeeOutOfValidRange, err + return commonclient.FeeOutOfValidRange, err } - return clienttypes.Unsupported, errors.Wrap(sendError, "this error type only handled for L2s") + return commonclient.Unsupported, errors.Wrap(sendError, "this error type only handled for L2s") } if sendError.IsNonceTooHighError() { // This error occurs when the tx nonce is greater than current_nonce + tx_count_in_mempool, // instead of keeping the tx in mempool. This can happen if previous transactions haven't // reached the client yet. The correct thing to do is to mark it as retryable. lggr.Warnw("Transaction has a nonce gap.", "err", err) - return clienttypes.Retryable, err + return commonclient.Retryable, err } if sendError.IsInsufficientEth() { lggr.Criticalw(fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+ "ACTION REQUIRED: Chainlink wallet with address 0x%x is OUT OF FUNDS", tx.Hash(), tx.Type(), sendError.Error(), fromAddress, ), "err", sendError) - return clienttypes.InsufficientFunds, err + return commonclient.InsufficientFunds, err } if sendError.IsTimeout() { - return clienttypes.Retryable, errors.Wrapf(sendError, "timeout while sending transaction %s", tx.Hash().Hex()) + return commonclient.Retryable, errors.Wrapf(sendError, "timeout while sending transaction %s", tx.Hash().Hex()) } if sendError.IsTxFeeExceedsCap() { lggr.Criticalw(fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), @@ -461,19 +461,19 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro "err", sendError, "id", "RPCTxFeeCapExceeded", ) - return clienttypes.ExceedsMaxFee, err + return commonclient.ExceedsMaxFee, err } - return clienttypes.Unknown, err + return commonclient.Unknown, err } // ClassifySendOnlyError handles SendOnly nodes error codes. In that case, we don't assume there is another transaction that will be correctly // priced. -func ClassifySendOnlyError(err error) clienttypes.SendTxReturnCode { +func ClassifySendOnlyError(err error) commonclient.SendTxReturnCode { sendError := NewSendError(err) if sendError == nil || sendError.IsNonceTooLowError() || sendError.IsTransactionAlreadyMined() || sendError.IsTransactionAlreadyInMempool() { // Nonce too low or transaction known errors are expected since // the primary SendTransaction may well have succeeded already - return clienttypes.Successful + return commonclient.Successful } - return clienttypes.Fatal + return commonclient.Fatal } diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 8552b2c0a06..2820ba992c3 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -9,7 +9,6 @@ import ( "github.com/pkg/errors" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -89,7 +88,7 @@ func NewChainClientWithTestNode( } lggr := logger.TestLogger(t) - rpc := NewRPCClient(lggr, *parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, clienttypes.Primary) + rpc := NewRPCClient(lggr, *parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary) n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( nodeCfg, noNewHeadsThreshold, lggr, *parsed, rpcHTTPURL, "eth-primary-node-0", id, chainID, 1, rpc, "EVM") @@ -101,7 +100,7 @@ func NewChainClientWithTestNode( return nil, errors.Errorf("sendonly ethereum rpc url scheme must be http(s): %s", u.String()) } var empty url.URL - rpc := NewRPCClient(lggr, empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, clienttypes.Secondary) + rpc := NewRPCClient(lggr, empty, &sendonlyRPCURLs[i], fmt.Sprintf("eth-sendonly-rpc-%d", i), id, chainID, commonclient.Secondary) s := commonclient.NewSendOnlyNode[*big.Int, RPCCLient]( lggr, u, fmt.Sprintf("eth-sendonly-%d", i), chainID, rpc) sendonlys = append(sendonlys, s) diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index fdcb15d6a63..7617a7c05f9 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -7,10 +7,10 @@ import ( assets "github.com/smartcontractkit/chainlink/v2/core/assets" - chainsclient "github.com/smartcontractkit/chainlink/v2/common/chains/client" - common "github.com/ethereum/go-ethereum/common" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + context "context" ethereum "github.com/ethereum/go-ethereum" @@ -566,18 +566,18 @@ func (_m *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er } // SendTransactionReturnCode provides a mock function with given fields: ctx, tx, fromAddress -func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (chainsclient.SendTxReturnCode, error) { +func (_m *Client) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { ret := _m.Called(ctx, tx, fromAddress) - var r0 chainsclient.SendTxReturnCode + var r0 commonclient.SendTxReturnCode var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (chainsclient.SendTxReturnCode, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) (commonclient.SendTxReturnCode, error)); ok { return rf(ctx, tx, fromAddress) } - if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) chainsclient.SendTxReturnCode); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.Transaction, common.Address) commonclient.SendTxReturnCode); ok { r0 = rf(ctx, tx, fromAddress) } else { - r0 = ret.Get(0).(chainsclient.SendTxReturnCode) + r0 = ret.Get(0).(commonclient.SendTxReturnCode) } if rf, ok := ret.Get(1).(func(context.Context, *types.Transaction, common.Address) error); ok { diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index 8e271aea1e7..286f62b3b8b 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -121,9 +121,9 @@ func (nc *NullClient) HeaderByHash(ctx context.Context, h common.Hash) (*types.H return nil, nil } -func (nc *NullClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, sender common.Address) (clienttypes.SendTxReturnCode, error) { +func (nc *NullClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, sender common.Address) (commonclient.SendTxReturnCode, error) { nc.lggr.Debug("SendTransactionReturnCode") - return clienttypes.Successful, nil + return commonclient.Successful, nil } func (nc *NullClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index b6ed84eee4e..04b9fad1fcd 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -17,7 +17,6 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/assets" @@ -57,7 +56,7 @@ type rpcClient struct { name string id int32 chainID *big.Int - tier clienttypes.NodeTier + tier commonclient.NodeTier ws rawclient http *rawclient @@ -85,7 +84,7 @@ func NewRPCClient( name string, id int32, chainID *big.Int, - tier clienttypes.NodeTier, + tier commonclient.NodeTier, ) RPCCLient { r := new(rpcClient) r.name = name diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index cde536bc7ba..78239089676 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -18,7 +18,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -417,16 +417,16 @@ func (c *SimulatedBackendClient) HeaderByHash(ctx context.Context, h common.Hash return c.b.HeaderByHash(ctx, h) } -func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (clienttypes.SendTxReturnCode, error) { +func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, tx *types.Transaction, fromAddress common.Address) (commonclient.SendTxReturnCode, error) { err := c.SendTransaction(ctx, tx) if err == nil { - return clienttypes.Successful, nil + return commonclient.Successful, nil } if strings.Contains(err.Error(), "could not fetch parent") || strings.Contains(err.Error(), "invalid transaction") { - return clienttypes.Fatal, err + return commonclient.Fatal, err } // All remaining error messages returned from SendTransaction are considered Unknown. - return clienttypes.Unknown, err + return commonclient.Unknown, err } // SendTransaction sends a transaction. diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 61a230c21b1..ca2697ca99e 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -22,11 +22,11 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/assets" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" @@ -51,7 +51,7 @@ import ( func NewTestEthBroadcaster( t testing.TB, txStore txmgr.TestEvmTxStore, - ethClient evmclient.Client, + ethClient client.Client, keyStore keystore.Eth, config evmconfig.ChainScopedConfig, checkerFactory txmgr.TransmitCheckerFactory, @@ -217,7 +217,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { } ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(2) && tx.Value().Cmp(big.NewInt(242)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Earlier tr := int32(99) @@ -245,7 +245,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { require.Equal(t, value.String(), tx.Value().String()) require.Equal(t, earlierEthTx.EncodedPayload, tx.Data()) return true - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Later laterEthTx := txmgr.Tx{ @@ -268,7 +268,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { require.Equal(t, value.String(), tx.Value().String()) require.Equal(t, laterEthTx.EncodedPayload, tx.Data()) return true - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Insertion order deliberately reversed to test ordering require.NoError(t, txStore.InsertTx(&expensiveEthTx)) @@ -349,7 +349,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { t.Run("sends transactions with type 0x2 in EIP-1559 mode", func(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(343) && tx.Value().Cmp(big.NewInt(242)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(242)), &cltest.FixtureChainID) // Do the thing @@ -400,7 +400,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { } ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(344) && tx.Value().Cmp(big.NewInt(442)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() ethClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", mock.MatchedBy(func(callarg map[string]interface{}) bool { if fmt.Sprintf("%s", callarg["value"]) == "0x1ba" { // 442 assert.Equal(t, txRequest.FromAddress, callarg["from"]) @@ -433,7 +433,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { t.Run("with unknown error, sends tx as normal", func(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(345) && tx.Value().Cmp(big.NewInt(542)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() ethClient.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", mock.MatchedBy(func(callarg map[string]interface{}) bool { return fmt.Sprintf("%s", callarg["value"]) == "0x21e" // 542 }), "latest").Return(errors.New("this is not a revert, something unexpected went wrong")).Once() @@ -454,7 +454,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { }) t.Run("on revert, marks tx as fatally errored and does not send", func(t *testing.T) { - jerr := evmclient.JsonError{ + jerr := client.JsonError{ Code: 42, Message: "oh no, it reverted", Data: []byte{42, 166, 34}, @@ -503,7 +503,7 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == 0 && tx.Value().Cmp(big.NewInt(442)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, cltest.EvmTxRequestWithValue(big.Int(assets.NewEthValue(442))), @@ -526,7 +526,7 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == 1 && tx.Value().Cmp(big.NewInt(442)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, cltest.EvmTxRequestWithValue(big.Int(assets.NewEthValue(442))), @@ -646,7 +646,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success_WithMultiplier(t *testing ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { assert.Equal(t, int(1600), int(tx.Gas())) return true - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() txRequest := txmgr.TxRequest{ FromAddress: fromAddress, @@ -730,7 +730,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing { @@ -766,7 +766,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) - }), fromAddress).Return(clienttypes.Fatal, errors.New("exceeds block gas limit")).Once() + }), fromAddress).Return(commonclient.Fatal, errors.New("exceeds block gas limit")).Once() // Do the thing { @@ -802,7 +802,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) - }), fromAddress).Return(clienttypes.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() + }), fromAddress).Return(commonclient.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() // Do the thing { @@ -837,7 +837,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) - }), fromAddress).Return(clienttypes.TransactionAlreadyKnown, errors.New("nonce too low")).Once() + }), fromAddress).Return(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once() // Do the thing { @@ -874,7 +874,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) - }), fromAddress).Return(clienttypes.Retryable, failedToReachNodeError).Once() + }), fromAddress).Return(commonclient.Retryable, failedToReachNodeError).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -920,7 +920,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { s, e := txmgr.GetGethSignedTx(attempt.SignedRawTx) require.NoError(t, e) return tx.Nonce() == uint64(firstNonce) && tx.GasPrice().Int64() == s.GasPrice().Int64() - }), fromAddress).Return(clienttypes.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() + }), fromAddress).Return(commonclient.Successful, errors.New("known transaction: a1313bd99a81fb4d8ad1d2e90b67c6b3fa77545c990d6251444b83b70b6f8980")).Once() // Do the thing { @@ -980,7 +980,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // First send, replacement underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(0) - }), fromAddress).Return(clienttypes.Successful, errors.New("replacement transaction underpriced")).Once() + }), fromAddress).Return(commonclient.Successful, errors.New("replacement transaction underpriced")).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1017,7 +1017,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Fatal, errors.New(fatalErrorExample)).Once() + }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -1067,7 +1067,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Fatal, errors.New(fatalErrorExample)).Once() + }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) require.Error(t, err) @@ -1088,7 +1088,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Fatal, errors.New(fatalErrorExample)).Once() + }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1120,7 +1120,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.ExceedsMaxFee, errors.New(TxFeeExceedsCapError)).Twice() + }), fromAddress).Return(commonclient.ExceedsMaxFee, errors.New(TxFeeExceedsCapError)).Twice() // In the first case, the tx was NOT accepted into the mempool. In the case // of multiple RPC nodes, it is possible that it can be accepted by // another node even if the primary one returns "exceeds the configured @@ -1178,7 +1178,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(localNextNonce) - }), fromAddress).Return(clienttypes.Unknown, errors.New(retryableErrorExample)).Once() + }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() // Nonce is the same as localNextNonce, implying that this sent transaction has not been accepted ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() @@ -1204,7 +1204,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // Now on the second run, it is successful ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() retryable, err = eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -1230,7 +1230,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(localNextNonce) - }), fromAddress).Return(clienttypes.Unknown, errors.New(retryableErrorExample)).Once() + }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("pending nonce fetch failed")).Once() // Do the thing @@ -1256,7 +1256,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // Now on the second run, it is successful ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() retryable, err = eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -1282,7 +1282,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Unknown, errors.New(retryableErrorExample)).Once() + }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() // Nonce is one higher than localNextNonce, implying that despite the error, this sent transaction has been accepted into the mempool ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce+1), nil).Once() @@ -1316,17 +1316,17 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // First was underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(evmcfg.EVM().GasEstimator().PriceDefault().ToInt()) == 0 - }), fromAddress).Return(clienttypes.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Second with gas bump was still underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(big.NewInt(25000000000)) == 0 - }), fromAddress).Return(clienttypes.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Third succeeded ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(big.NewInt(30000000000)) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1362,7 +1362,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Retryable, failedToReachNodeError).Once() + }), fromAddress).Return(commonclient.Retryable, failedToReachNodeError).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1393,7 +1393,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Successful, errors.New(temporarilyUnderpricedError)).Once() + }), fromAddress).Return(commonclient.Successful, errors.New(temporarilyUnderpricedError)).Once() // Do the thing retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1433,7 +1433,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // First was underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasPrice().Cmp(evmcfg2.EVM().GasEstimator().PriceDefault().ToInt()) == 0 - }), fromAddress).Return(clienttypes.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Do the thing retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1451,7 +1451,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.InsufficientFunds, errors.New(insufficientEthError)).Once() + }), fromAddress).Return(commonclient.InsufficientFunds, errors.New(insufficientEthError)).Once() retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) require.Error(t, err) @@ -1481,7 +1481,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce - }), fromAddress).Return(clienttypes.Retryable, errors.New(nonceGapError)).Once() + }), fromAddress).Return(commonclient.Retryable, errors.New(nonceGapError)).Once() retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) require.Error(t, err) @@ -1525,7 +1525,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce = getLocalNextNonce(t, eb, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(1)) == 0 - }), fromAddress).Return(clienttypes.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Check gas tip cap verification retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -1569,15 +1569,15 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // Second was underpriced but above minimum ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(gasTipCapDefault.ToInt()) == 0 - }), fromAddress).Return(clienttypes.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Resend at the bumped price ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(0).Add(gasTipCapDefault.ToInt(), evmcfg2.EVM().GasEstimator().BumpMin().ToInt())) == 0 - }), fromAddress).Return(clienttypes.Underpriced, errors.New(underpricedError)).Once() + }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Final bump succeeds ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(0).Add(gasTipCapDefault.ToInt(), big.NewInt(0).Mul(evmcfg2.EVM().GasEstimator().BumpMin().ToInt(), big.NewInt(2)))) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() retryable, err = eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) require.NoError(t, err) diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index e1b12577749..8789f5f173e 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -13,8 +13,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -24,10 +24,10 @@ import ( var _ TxmClient = (*evmTxmClient)(nil) type evmTxmClient struct { - client evmclient.Client + client client.Client } -func NewEvmTxmClient(c evmclient.Client) *evmTxmClient { +func NewEvmTxmClient(c client.Client) *evmTxmClient { return &evmTxmClient{client: c} } @@ -45,14 +45,14 @@ func (c *evmTxmClient) BatchSendTransactions( batchSize int, lggr logger.Logger, ) ( - codes []clienttypes.SendTxReturnCode, + codes []commonclient.SendTxReturnCode, txErrs []error, broadcastTime time.Time, successfulTxIDs []int64, err error, ) { // preallocate - codes = make([]clienttypes.SendTxReturnCode, len(attempts)) + codes = make([]commonclient.SendTxReturnCode, len(attempts)) txErrs = make([]error, len(attempts)) reqs, broadcastTime, successfulTxIDs, batchErr := batchSendTransactions(ctx, attempts, batchSize, lggr, c.client) @@ -80,7 +80,7 @@ func (c *evmTxmClient) BatchSendTransactions( processingErr[i] = fmt.Errorf("failed to process tx (index %d): %w", i, signedErr) return } - codes[i], txErrs[i] = evmclient.ClassifySendError(reqs[i].Error, lggr, tx, attempts[i].Tx.FromAddress, c.client.IsL2()) + codes[i], txErrs[i] = client.ClassifySendError(reqs[i].Error, lggr, tx, attempts[i].Tx.FromAddress, c.client.IsL2()) }(index) } wg.Wait() @@ -88,11 +88,11 @@ func (c *evmTxmClient) BatchSendTransactions( return } -func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.Logger) (clienttypes.SendTxReturnCode, error) { +func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.Logger) (commonclient.SendTxReturnCode, error) { signedTx, err := GetGethSignedTx(attempt.SignedRawTx) if err != nil { lggr.Criticalw("Fatal error signing transaction", "err", err, "etx", etx) - return clienttypes.Fatal, err + return commonclient.Fatal, err } return c.client.SendTransactionReturnCode(ctx, signedTx, etx.FromAddress) } @@ -174,5 +174,5 @@ func (c *evmTxmClient) CallContract(ctx context.Context, a TxAttempt, blockNumbe Data: a.Tx.EncodedPayload, AccessList: nil, }, blockNumber) - return evmclient.ExtractRPCError(errCall) + return client.ExtractRPCError(errCall) } diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 8fbdb7696d9..1385250a206 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -20,12 +20,12 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/assets" - evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" @@ -564,7 +564,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { data, err := utils.ABIEncode(`[{"type":"uint256"}]`, big.NewInt(10)) require.NoError(t, err) sig := utils.Keccak256Fixed([]byte(`MyError(uint256)`)) - ethClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(nil, &evmclient.JsonError{ + ethClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(nil, &client.JsonError{ Code: 1, Message: "reverted", Data: utils.ConcatBytes(sig[:4], data), @@ -1658,7 +1658,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing require.NoError(t, db.Get(&dbAttempt, `UPDATE evm.tx_attempts SET broadcast_before_block_num=$1 WHERE id=$2 RETURNING *`, oldEnough, attempt1.ID)) // Send transaction and assume success. - ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(clienttypes.Successful, nil).Once() + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once() err := ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead) require.NoError(t, err) @@ -1703,7 +1703,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing require.NoError(t, db.Get(&dbAttempt, `UPDATE evm.tx_attempts SET broadcast_before_block_num=$1 WHERE id=$2 RETURNING *`, oldEnough, attempt1.ID)) // Send transaction and assume success. - ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(clienttypes.Successful, nil).Once() + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return(commonclient.Successful, nil).Once() err := ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead) require.NoError(t, err) @@ -1787,7 +1787,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { })).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) - }), fromAddress).Return(clienttypes.Fatal, errors.New("exceeds block gas limit")).Once() + }), fromAddress).Return(commonclient.Fatal, errors.New("exceeds block gas limit")).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -1819,7 +1819,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { // Once for the bumped attempt which exceeds limit ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx.Sequence) && tx.GasPrice().Int64() == int64(20000000000) - }), fromAddress).Return(clienttypes.ExceedsMaxFee, errors.New("tx fee (1.10 ether) exceeds the configured cap (1.00 ether)")).Once() + }), fromAddress).Return(commonclient.ExceedsMaxFee, errors.New("tx fee (1.10 ether) exceeds the configured cap (1.00 ether)")).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -1858,7 +1858,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { })).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -1904,7 +1904,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, fmt.Errorf("known transaction: %s", ethTx.Hash().Hex())).Once() + }), fromAddress).Return(commonclient.Successful, fmt.Errorf("known transaction: %s", ethTx.Hash().Hex())).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -1944,7 +1944,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.TransactionAlreadyKnown, errors.New("nonce too low")).Once() + }), fromAddress).Return(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -1996,7 +1996,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == n && expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Unknown, errors.New("some network error")).Once() + }), fromAddress).Return(commonclient.Unknown, errors.New("some network error")).Once() // Do the thing err := ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead) @@ -2024,7 +2024,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { n = *etx2.Sequence ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == n && expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2063,7 +2063,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == n && expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.TransactionAlreadyKnown, errors.New("nonce too low")).Once() + }), fromAddress).Return(commonclient.TransactionAlreadyKnown, errors.New("nonce too low")).Once() // Creates new attempt as normal if currentHead is not high enough require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2104,7 +2104,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, errors.New("replacement transaction underpriced")).Once() + }), fromAddress).Return(commonclient.Successful, errors.New("replacement transaction underpriced")).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2141,7 +2141,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, fmt.Errorf("known transaction: %s", ethTx.Hash().Hex())).Once() + }), fromAddress).Return(commonclient.Successful, fmt.Errorf("known transaction: %s", ethTx.Hash().Hex())).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2180,7 +2180,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, errors.New(temporarilyUnderpricedError)).Once() + }), fromAddress).Return(commonclient.Successful, errors.New(temporarilyUnderpricedError)).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2209,7 +2209,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && gasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, errors.New("already known")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx + }), fromAddress).Return(commonclient.Successful, errors.New("already known")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx // Do the thing require.NoError(t, ec2.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2239,7 +2239,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && gasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, errors.New("already known")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx + }), fromAddress).Return(commonclient.Successful, errors.New("already known")).Once() // we already submitted at this price, now it's time to bump and submit again but since we simply resubmitted rather than increasing gas price, geth already knows about this tx // Do the thing require.NoError(t, ec2.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2278,7 +2278,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { gasTipCap := assets.GWei(42) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx4.Sequence && gasTipCap.ToInt().Cmp(tx.GasTipCap()) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) var err error etx4, err = txStore.FindTxWithAttempts(etx4.ID) @@ -2308,7 +2308,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { // Third attempt failed to bump, resubmits old one instead ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx4.Sequence && attempt4_2.Hash.String() == tx.Hash().String() - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() require.NoError(t, ec2.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) var err error @@ -2344,7 +2344,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { mock.Anything).Return(ðTx, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx4.Sequence && expectedBumpedTipCap.ToInt().Cmp(tx.GasTipCap()) == 0 - }), fromAddress).Return(clienttypes.Successful, errors.New("replacement transaction underpriced")).Once() + }), fromAddress).Return(commonclient.Successful, errors.New("replacement transaction underpriced")).Once() // Do it require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2401,10 +2401,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh // Fail the first time with terminally underpriced. ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Once() + commonclient.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Once() // Succeed the second time after bumping gas. ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Successful, nil).Once() + commonclient.Successful, nil).Once() kst.On("SignTx", mock.Anything, mock.Anything, mock.Anything).Return( signedTx, nil, ).Once() @@ -2424,10 +2424,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh // Fail a few times with terminally underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Times(3) + commonclient.Underpriced, errors.New("Transaction gas price is too low. It does not satisfy your node's minimal gas price")).Times(3) // Succeed the second time after bumping gas. ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Successful, nil).Once() + commonclient.Successful, nil).Once() signedLegacyTx := new(types.Transaction) kst.On("SignTx", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Type() == 0x0 && tx.Nonce() == uint64(*etx.Sequence) @@ -2456,10 +2456,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh // Fail a few times with terminally underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Underpriced, errors.New("transaction underpriced")).Times(3) + commonclient.Underpriced, errors.New("transaction underpriced")).Times(3) // Succeed the second time after bumping gas. ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Successful, nil).Once() + commonclient.Successful, nil).Once() signedDxFeeTx := new(types.Transaction) kst.On("SignTx", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Type() == 0x2 && tx.Nonce() == uint64(*etx.Sequence) @@ -2517,7 +2517,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.InsufficientFunds, insufficientEthError).Once() + }), fromAddress).Return(commonclient.InsufficientFunds, insufficientEthError).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2543,7 +2543,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.InsufficientFunds, insufficientEthError).Once() + }), fromAddress).Return(commonclient.InsufficientFunds, insufficientEthError).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2568,7 +2568,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return expectedBumpedGasPrice.Cmp(tx.GasPrice()) == 0 - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing require.NoError(t, ec.RebroadcastWhereNecessary(testutils.Context(t), currentHead)) @@ -2600,7 +2600,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(n) - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() nonce++ } @@ -2695,7 +2695,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { require.NoError(t, err) // Keeps gas price and nonce the same return atx.GasPrice().Cmp(tx.GasPrice()) == 0 && atx.Nonce() == tx.Nonce() - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(testutils.Context(t), &head)) @@ -2718,7 +2718,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { cltest.MustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attemptHash) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( - clienttypes.Successful, nil).Once() + commonclient.Successful, nil).Once() // Do the thing require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(testutils.Context(t), &head)) @@ -2753,7 +2753,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { s, err := txmgr.GetGethSignedTx(attempt3.SignedRawTx) require.NoError(t, err) return tx.Hash() == s.Hash() - }), fromAddress).Return(clienttypes.Successful, nil).Once() + }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(testutils.Context(t), &head)) @@ -2817,7 +2817,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { tx.Gas() == uint64(overrideGasLimit) && reflect.DeepEqual(tx.Data(), etx1.EncodedPayload) && tx.To().String() == etx1.ToAddress.String() - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{1}, gasPriceWei, fromAddress, overrideGasLimit)) }) @@ -2832,7 +2832,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { tx.Gas() == uint64(etx1.FeeLimit) && reflect.DeepEqual(tx.Data(), etx1.EncodedPayload) && tx.To().String() == etx1.ToAddress.String() - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{(1)}, gasPriceWei, fromAddress, 0)) }) @@ -2843,10 +2843,10 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx1.Sequence) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && tx.Gas() == uint64(overrideGasLimit) - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx2.Sequence) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && tx.Gas() == uint64(overrideGasLimit) - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{(1), (2)}, gasPriceWei, fromAddress, overrideGasLimit)) }) @@ -2857,10 +2857,10 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(1) - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(2) - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() for i := 3; i <= 5; i++ { nonce := i ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { @@ -2870,7 +2870,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { *tx.To() == fromAddress && tx.Value().Cmp(big.NewInt(0)) == 0 && len(tx.Data()) == 0 - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() } nonces := []evmtypes.Nonce{(1), (2), (3), (4), (5)} @@ -2883,7 +2883,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(0) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && uint32(tx.Gas()) == config.EVM().GasEstimator().LimitDefault() - }), mock.Anything).Return(clienttypes.Successful, nil).Once() + }), mock.Anything).Return(commonclient.Successful, nil).Once() require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{(0)}, gasPriceWei, fromAddress, 0)) }) diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index df60e16423e..a73e98a935b 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/cmd" cmdMocks "github.com/smartcontractkit/chainlink/v2/core/cmd/mocks" @@ -307,7 +307,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() ethClient.On("Dial", mock.Anything).Return(nil) - client := cmd.Shell{ + c := cmd.Shell{ Config: config, AppFactory: cltest.InstanceAppFactory{App: app}, FallbackAPIInitializer: cltest.NewMockAPIInitializer(t), @@ -318,7 +318,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { beginningNonce := uint64(7) endingNonce := uint64(10) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RebroadcastTransactions, set, "") + cltest.FlagSetApplyFromAction(c.RebroadcastTransactions, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("beginningNonce", strconv.FormatUint(beginningNonce, 10))) @@ -328,16 +328,16 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { require.NoError(t, set.Set("address", fromAddress.Hex())) require.NoError(t, set.Set("password", "../internal/fixtures/correct_password.txt")) - c := cli.NewContext(nil, set, nil) + ctx := cli.NewContext(nil, set, nil) for i := beginningNonce; i <= endingNonce; i++ { n := i ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == n - }), mock.Anything).Once().Return(clienttypes.Successful, nil) + }), mock.Anything).Once().Return(client.Successful, nil) } - assert.NoError(t, client.RebroadcastTransactions(c)) + assert.NoError(t, c.RebroadcastTransactions(ctx)) } func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { @@ -388,7 +388,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() - client := cmd.Shell{ + c := cmd.Shell{ Config: config, AppFactory: cltest.InstanceAppFactory{App: app}, FallbackAPIInitializer: cltest.NewMockAPIInitializer(t), @@ -397,7 +397,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { } set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RebroadcastTransactions, set, "") + cltest.FlagSetApplyFromAction(c.RebroadcastTransactions, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("beginningNonce", strconv.FormatUint(uint64(beginningNonce), 10))) @@ -407,16 +407,16 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { require.NoError(t, set.Set("address", fromAddress.Hex())) require.NoError(t, set.Set("password", "../internal/fixtures/correct_password.txt")) - c := cli.NewContext(nil, set, nil) + ctx := cli.NewContext(nil, set, nil) for i := beginningNonce; i <= endingNonce; i++ { n := i ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return uint(tx.Nonce()) == n - }), mock.Anything).Once().Return(clienttypes.Successful, nil) + }), mock.Anything).Once().Return(client.Successful, nil) } - assert.NoError(t, client.RebroadcastTransactions(c)) + assert.NoError(t, c.RebroadcastTransactions(ctx)) cltest.AssertEthTxAttemptCountStays(t, app.GetSqlxDB(), 1) }) @@ -466,7 +466,7 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) { mockRelayerChainInteroperators := &chainlinkmocks.FakeRelayerChainInteroperators{EVMChains: legacy} app.On("GetRelayers").Return(mockRelayerChainInteroperators).Maybe() - ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(clienttypes.Successful, nil) + ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil) client := cmd.Shell{ Config: config, diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 66162aef102..e5c2ca031a7 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -44,7 +44,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/loop" - clienttypes "github.com/smartcontractkit/chainlink/v2/common/chains/client" + "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/assets" @@ -518,7 +518,7 @@ func NewEthMocksWithTransactionsOnBlocksAssertions(t testing.TB) *evmclimocks.Cl c.On("Dial", mock.Anything).Maybe().Return(nil) c.On("SubscribeNewHead", mock.Anything, mock.Anything).Maybe().Return(EmptyMockSubscription(t), nil) c.On("SendTransaction", mock.Anything, mock.Anything).Maybe().Return(nil) - c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(clienttypes.Successful, nil) + c.On("SendTransactionReturnCode", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(client.Successful, nil) // Construct chain h2 := Head(2) h1 := HeadWithHash(1, h2.ParentHash) From 84bbe2af6ac8889e40f0210414375c956df65275 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Mon, 6 Nov 2023 14:42:47 -0500 Subject: [PATCH 080/271] populate retry interval for mercury requests [DO NOT MERGE] (#11150) * populate retry interval for mercury requests * fix go mod * populate retry interval based on the work ID counter * add tests * use const value * include check block number in plugin retry key * address comments * refactor * test for plugin retry counter * Bump ocr2keepers to 0.7.28 (#11181) * handles 206 response code * reduce mercury permission cache period * address comments * remove special logging for 206 --------- Co-authored-by: Akshay Aggarwal --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../ocr2/plugins/ocr2keeper/evm21/registry.go | 19 +- .../ocr2keeper/evm21/streams_lookup.go | 88 ++++++-- .../ocr2keeper/evm21/streams_lookup_test.go | 205 ++++++++++++++---- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 9 files changed, 248 insertions(+), 82 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 6b0a33451e2..8d731f3f41d 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -22,7 +22,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 - github.com/smartcontractkit/ocr2keepers v0.7.27 + github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/spf13/cobra v1.6.1 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 2cb0eb25826..ebab8c990d3 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1478,8 +1478,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 h1:qOsw2ETQD/Sb/W2xuYn2KPWjvvsWA0C+l19rWFq8iNg= github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= -github.com/smartcontractkit/ocr2keepers v0.7.27 h1:kwqMrzmEdq6gH4yqNuLQCbdlED0KaIjwZzu3FF+Gves= -github.com/smartcontractkit/ocr2keepers v0.7.27/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= +github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= +github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb h1:OMaBUb4X9IFPLbGbCHsMU+kw/BPCrewaVwWGIBc0I4A= diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 0ca20477f20..73e2bc0a9c0 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -20,6 +20,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -35,11 +36,14 @@ import ( ) const ( + defaultPluginRetryExpiration = 30 * time.Minute // defaultAllowListExpiration decides how long an upkeep's allow list info will be valid for. - defaultAllowListExpiration = 20 * time.Minute - // allowListCleanupInterval decides when the expired items in allowList cache will be deleted. - allowListCleanupInterval = 5 * time.Minute + defaultAllowListExpiration = 10 * time.Minute + // cleanupInterval decides when the expired items in cache will be deleted. + cleanupInterval = 5 * time.Minute logTriggerRefreshBatchSize = 32 + totalFastPluginRetries = 5 + totalMediumPluginRetries = 10 ) var ( @@ -100,9 +104,10 @@ func NewEvmRegistry( headFunc: func(ocr2keepers.BlockKey) {}, chLog: make(chan logpoller.Log, 1000), mercury: &MercuryConfig{ - cred: mc, - abi: core.StreamsCompatibleABI, - allowListCache: cache.New(defaultAllowListExpiration, allowListCleanupInterval), + cred: mc, + abi: core.StreamsCompatibleABI, + allowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), + pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), }, hc: http.DefaultClient, logEventProvider: logEventProvider, @@ -126,6 +131,8 @@ type MercuryConfig struct { abi abi.ABI // allowListCache stores the upkeeps privileges. In 2.1, this only includes a JSON bytes for allowed to use mercury allowListCache *cache.Cache + + pluginRetryCache *cache.Cache } type EvmRegistry struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go index 6f2594b6c38..f183e1f6bbe 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go @@ -162,10 +162,11 @@ func (r *EvmRegistry) streamsLookup(ctx context.Context, checkResults []ocr2keep func (r *EvmRegistry) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *StreamsLookup, i int, checkResults []ocr2keepers.CheckResult, lggr logger.Logger) { defer wg.Done() - state, reason, values, retryable, err := r.doMercuryRequest(ctx, lookup, lggr) + state, reason, values, retryable, ri, err := r.doMercuryRequest(ctx, lookup, generatePluginRetryKey(checkResults[i].WorkID, lookup.block), lggr) if err != nil { - lggr.Errorf("upkeep %s retryable %v doMercuryRequest: %s", lookup.upkeepId, retryable, err.Error()) + lggr.Errorf("upkeep %s retryable %v retryInterval %s doMercuryRequest: %s", lookup.upkeepId, retryable, ri, err.Error()) checkResults[i].Retryable = retryable + checkResults[i].RetryInterval = ri checkResults[i].PipelineExecutionState = uint8(state) checkResults[i].IneligibilityReason = uint8(reason) return @@ -278,12 +279,12 @@ func (r *EvmRegistry) checkCallback(ctx context.Context, values [][]byte, lookup } // doMercuryRequest sends requests to Mercury API to retrieve mercury data. -func (r *EvmRegistry) doMercuryRequest(ctx context.Context, sl *StreamsLookup, lggr logger.Logger) (encoding.PipelineExecutionState, encoding.UpkeepFailureReason, [][]byte, bool, error) { +func (r *EvmRegistry) doMercuryRequest(ctx context.Context, sl *StreamsLookup, prk string, lggr logger.Logger) (encoding.PipelineExecutionState, encoding.UpkeepFailureReason, [][]byte, bool, time.Duration, error) { var isMercuryV03 bool resultLen := len(sl.Feeds) ch := make(chan MercuryData, resultLen) if len(sl.Feeds) == 0 { - return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) + return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) } if sl.FeedParamKey == feedIdHex && sl.TimeParamKey == blockNumber { // only mercury v0.2 @@ -297,10 +298,11 @@ func (r *EvmRegistry) doMercuryRequest(ctx context.Context, sl *StreamsLookup, l ch = make(chan MercuryData, resultLen) go r.multiFeedsRequest(ctx, ch, sl, lggr) } else { - return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) + return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) } var reqErr error + var ri time.Duration results := make([][]byte, len(sl.Feeds)) retryable := true allSuccess := true @@ -323,8 +325,11 @@ func (r *EvmRegistry) doMercuryRequest(ctx context.Context, sl *StreamsLookup, l results[m.Index] = m.Bytes[0] } } + if retryable && !allSuccess { + ri = r.calculateRetryConfig(prk) + } // only retry when not all successful AND none are not retryable - return state, encoding.UpkeepFailureReasonNone, results, retryable && !allSuccess, reqErr + return state, encoding.UpkeepFailureReasonNone, results, retryable && !allSuccess, ri, reqErr } // singleFeedRequest sends a v0.2 Mercury request for a single feed report. @@ -378,7 +383,7 @@ func (r *EvmRegistry) singleFeedRequest(ctx context.Context, ch chan<- MercuryDa return err1 } - if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError { + if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusBadGateway || resp.StatusCode == http.StatusServiceUnavailable || resp.StatusCode == http.StatusGatewayTimeout { lggr.Warnf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, sl.Feeds[index]) retryable = true state = encoding.MercuryFlakyFailure @@ -415,9 +420,9 @@ func (r *EvmRegistry) singleFeedRequest(ctx context.Context, ch chan<- MercuryDa sent = true return nil }, - // only retry when the error is 404 Not Found or 500 Internal Server Error + // only retry when the error is 404 Not Found, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) + return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) || err.Error() == fmt.Sprintf("%d", http.StatusBadGateway) || err.Error() == fmt.Sprintf("%d", http.StatusServiceUnavailable) || err.Error() == fmt.Sprintf("%d", http.StatusGatewayTimeout) }), retry.Context(ctx), retry.Delay(retryDelay), @@ -504,15 +509,29 @@ func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryDa retryable = false state = encoding.InvalidMercuryRequest return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3 with message: %s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, string(body)) - } else if resp.StatusCode == http.StatusInternalServerError { + } else if resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusBadGateway || resp.StatusCode == http.StatusServiceUnavailable || resp.StatusCode == http.StatusGatewayTimeout { retryable = true state = encoding.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 = encoding.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", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode) + return fmt.Errorf("%d", resp.StatusCode) + } else if resp.StatusCode == http.StatusPartialContent { + //var response MercuryV03Response + //err1 = json.Unmarshal(body, &response) + //if err1 != nil { + // lggr.Warnf("at timestamp %s upkeep %s failed to unmarshal body to MercuryV03Response from mercury v0.3: %v", sl.Time.String(), sl.upkeepId.String(), err1) + // retryable = false + // state = encoding.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 + //var receivedFeeds []string + //for _, f := range response.Reports { + // receivedFeeds = append(receivedFeeds, f.FeedID) + //} + lggr.Warnf("at timestamp %s upkeep %s requested [%s] feeds but mercury v0.3 server returned 206 status, treating it as 404 and retrying", sl.Time.String(), sl.upkeepId.String(), sl.Feeds) + retryable = true + state = encoding.MercuryFlakyFailure + return fmt.Errorf("%d", http.StatusPartialContent) } else if resp.StatusCode != http.StatusOK { retryable = false state = encoding.InvalidMercuryRequest @@ -532,8 +551,11 @@ func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryDa // 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(sl.Feeds) { - // TODO: AUTO-5044: calculate what reports are missing and log a warning - lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server retruned 200 status with %d reports while we requested %d feeds, treating as 404 (not found) and retrying", sl.Time.String(), sl.upkeepId.String(), len(response.Reports), len(sl.Feeds)) + var receivedFeeds []string + for _, f := range response.Reports { + receivedFeeds = append(receivedFeeds, f.FeedID) + } + lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server returned 206 status with [%s] reports while we requested [%s] feeds, retrying", sl.Time.String(), sl.upkeepId.String(), receivedFeeds, sl.Feeds) retryable = true state = encoding.MercuryFlakyFailure return fmt.Errorf("%d", http.StatusNotFound) @@ -558,9 +580,9 @@ func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryDa sent = true return nil }, - // only retry when the error is 404 Not Found or 500 Internal Server Error + // only retry when the error is 206 Partial Content, 404 Not Found, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) + return err.Error() == fmt.Sprintf("%d", http.StatusPartialContent) || err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) || err.Error() == fmt.Sprintf("%d", http.StatusBadGateway) || err.Error() == fmt.Sprintf("%d", http.StatusServiceUnavailable) || err.Error() == fmt.Sprintf("%d", http.StatusGatewayTimeout) }), retry.Context(ctx), retry.Delay(retryDelay), @@ -593,3 +615,29 @@ func (r *EvmRegistry) generateHMAC(method string, path string, body []byte, clie userHmac := hex.EncodeToString(signedMessage.Sum(nil)) return userHmac } + +// calculateRetryConfig returns plugin retry interval based on how many times plugin has retried this work +func (r *EvmRegistry) calculateRetryConfig(prk string) time.Duration { + var ri time.Duration + var retries int + totalAttempts, ok := r.mercury.pluginRetryCache.Get(prk) + if ok { + retries = totalAttempts.(int) + if retries < totalFastPluginRetries { + ri = 1 * time.Second + } else if retries < totalMediumPluginRetries { + ri = 5 * time.Second + } + // if the core node has retried totalMediumPluginRetries times, do not set retry interval and plugin will use + // the default interval + } else { + ri = 1 * time.Second + } + r.mercury.pluginRetryCache.Set(prk, retries+1, cache.DefaultExpiration) + return ri +} + +// generatePluginRetryKey returns a plugin retry cache key +func generatePluginRetryKey(workID string, block uint64) string { + return workID + "|" + fmt.Sprintf("%d", block) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go index 6f7065ef875..8d7c67d80ce 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go @@ -10,6 +10,7 @@ import ( "net/http" "strings" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -65,8 +66,9 @@ func setupEVMRegistry(t *testing.T) *EvmRegistry { Username: "FakeClientID", Password: "FakeClientKey", }, - abi: streamsLookupCompatibleABI, - allowListCache: cache.New(defaultAllowListExpiration, allowListCleanupInterval), + abi: streamsLookupCompatibleABI, + allowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), + pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), }, hc: mockHttpClient, } @@ -427,15 +429,18 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { upkeepId, _ := new(big.Int).SetString("88786950015966611018675766524283132478093844178961698330929478019253453382042", 10) tests := []struct { - name string - lookup *StreamsLookup - mockHttpStatusCode int - mockChainlinkBlobs []string - expectedValues [][]byte - expectedRetryable bool - expectedError error - state encoding.PipelineExecutionState - reason encoding.UpkeepFailureReason + name string + lookup *StreamsLookup + mockHttpStatusCode int + mockChainlinkBlobs []string + pluginRetries int + pluginRetryKey string + expectedValues [][]byte + expectedRetryable bool + expectedRetryInterval time.Duration + expectedError error + state encoding.PipelineExecutionState + reason encoding.UpkeepFailureReason }{ { name: "success", @@ -456,7 +461,7 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { expectedError: nil, }, { - name: "failure - retryable", + name: "failure - retryable and interval is 1s", lookup: &StreamsLookup{ StreamsLookupError: &encoding.StreamsLookupError{ FeedParamKey: feedIdHex, @@ -467,6 +472,49 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { }, upkeepId: upkeepId, }, + mockHttpStatusCode: http.StatusInternalServerError, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{nil}, + expectedRetryable: true, + pluginRetries: 0, + expectedRetryInterval: 1 * time.Second, + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), + state: encoding.MercuryFlakyFailure, + }, + { + name: "failure - retryable and interval is 5s", + lookup: &StreamsLookup{ + StreamsLookupError: &encoding.StreamsLookupError{ + FeedParamKey: feedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: blockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + upkeepId: upkeepId, + }, + pluginRetries: 5, + mockHttpStatusCode: http.StatusInternalServerError, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{nil}, + expectedRetryable: true, + expectedRetryInterval: 5 * time.Second, + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), + state: encoding.MercuryFlakyFailure, + }, + { + name: "failure - not retryable because there are many plugin retries already", + lookup: &StreamsLookup{ + StreamsLookupError: &encoding.StreamsLookupError{ + FeedParamKey: feedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: blockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + upkeepId: upkeepId, + }, + pluginRetries: 10, mockHttpStatusCode: http.StatusInternalServerError, mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, expectedValues: [][]byte{nil}, @@ -486,11 +534,11 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { }, upkeepId: upkeepId, }, - mockHttpStatusCode: http.StatusBadGateway, + mockHttpStatusCode: http.StatusTooManyRequests, mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, expectedValues: [][]byte{nil}, expectedRetryable: false, - expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 25880526 upkeep 88786950015966611018675766524283132478093844178961698330929478019253453382042 received status code 502 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"), + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 25880526 upkeep 88786950015966611018675766524283132478093844178961698330929478019253453382042 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"), state: encoding.InvalidMercuryRequest, }, { @@ -528,6 +576,9 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + if tt.pluginRetries != 0 { + r.mercury.pluginRetryCache.Set(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) + } hc := mocks.NewHttpClient(t) for _, blob := range tt.mockChainlinkBlobs { @@ -539,7 +590,7 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { StatusCode: tt.mockHttpStatusCode, Body: io.NopCloser(bytes.NewReader(b)), } - if tt.expectedError != nil && tt.expectedRetryable { + if tt.expectedError != nil && tt.expectedRetryable || tt.pluginRetries > 0 { hc.On("Do", mock.Anything).Return(resp, nil).Times(totalAttempt) } else { hc.On("Do", mock.Anything).Return(resp, nil).Once() @@ -547,13 +598,18 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { } r.hc = hc - state, reason, values, retryable, reqErr := r.doMercuryRequest(context.Background(), tt.lookup, r.lggr) + state, reason, values, retryable, ri, reqErr := r.doMercuryRequest(context.Background(), tt.lookup, tt.pluginRetryKey, r.lggr) assert.Equal(t, tt.expectedValues, values) assert.Equal(t, tt.expectedRetryable, retryable) + if retryable { + newRetries, _ := r.mercury.pluginRetryCache.Get(tt.pluginRetryKey) + assert.Equal(t, tt.pluginRetries+1, newRetries.(int)) + } + assert.Equal(t, tt.expectedRetryInterval, ri) assert.Equal(t, tt.state, state) assert.Equal(t, tt.reason, reason) if tt.expectedError != nil { - assert.Equal(t, tt.expectedError.Error(), reqErr.Error()) + assert.True(t, strings.HasPrefix(reqErr.Error(), "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000")) } }) } @@ -563,15 +619,17 @@ func TestEvmRegistry_DoMercuryRequestV03(t *testing.T) { upkeepId, _ := new(big.Int).SetString("88786950015966611018675766524283132478093844178961698330929478019253453382042", 10) tests := []struct { - name string - lookup *StreamsLookup - mockHttpStatusCode int - mockChainlinkBlobs []string - expectedValues [][]byte - expectedRetryable bool - expectedError error - state encoding.PipelineExecutionState - reason encoding.UpkeepFailureReason + name string + lookup *StreamsLookup + mockHttpStatusCode int + mockChainlinkBlobs []string + pluginRetryKey string + expectedValues [][]byte + expectedRetryable bool + expectedRetryInterval time.Duration + expectedError error + state encoding.PipelineExecutionState + reason encoding.UpkeepFailureReason }{ { name: "success v0.3", @@ -622,9 +680,10 @@ func TestEvmRegistry_DoMercuryRequestV03(t *testing.T) { } r.hc = hc - state, reason, values, retryable, reqErr := r.doMercuryRequest(context.Background(), tt.lookup, r.lggr) + state, reason, values, retryable, ri, reqErr := r.doMercuryRequest(context.Background(), tt.lookup, tt.pluginRetryKey, r.lggr) assert.Equal(t, tt.expectedValues, values) assert.Equal(t, tt.expectedRetryable, retryable) + assert.Equal(t, tt.expectedRetryInterval, ri) assert.Equal(t, tt.state, state) assert.Equal(t, tt.reason, reason) if tt.expectedError != nil { @@ -640,6 +699,7 @@ func TestEvmRegistry_SingleFeedRequest(t *testing.T) { name string index int lookup *StreamsLookup + pluginRetryKey string blob string statusCode int lastStatusCode int @@ -728,8 +788,8 @@ func TestEvmRegistry_SingleFeedRequest(t *testing.T) { blob: "0xab2123dc", retryNumber: 1, statusCode: http.StatusNotFound, - lastStatusCode: http.StatusBadGateway, - errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 404\n#2: at block 123456 upkeep 123456789 received status code 502 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + lastStatusCode: http.StatusTooManyRequests, + errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 404\n#2: at block 123456 upkeep 123456789 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", }, { name: "failure - returns not retryable", @@ -744,8 +804,8 @@ func TestEvmRegistry_SingleFeedRequest(t *testing.T) { upkeepId: upkeepId, }, blob: "0xab2123dc", - statusCode: http.StatusBadGateway, - errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 123456 upkeep 123456789 received status code 502 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + statusCode: http.StatusConflict, + errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 123456 upkeep 123456789 received status code 409 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", }, } @@ -819,6 +879,8 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { lookup *StreamsLookup statusCode int lastStatusCode int + pluginRetries int + pluginRetryKey string retryNumber int retryable bool errorMessage string @@ -883,6 +945,47 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { }, statusCode: http.StatusOK, }, + { + name: "success - retry 206", + lookup: &StreamsLookup{ + StreamsLookupError: &encoding.StreamsLookupError{ + FeedParamKey: feedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: timestamp, + Time: big.NewInt(123456), + }, + upkeepId: upkeepId, + }, + firstResponse: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + }, + }, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000019", + }, + }, + }, + retryNumber: 1, + statusCode: http.StatusPartialContent, + lastStatusCode: http.StatusOK, + }, { name: "success - retry for 500", lookup: &StreamsLookup{ @@ -946,7 +1049,7 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { errorMessage: "All attempts fail:\n#1: hex string without 0x prefix", }, { - name: "failure - returns retryable", + name: "failure - returns retryable with 1s plugin retry interval", lookup: &StreamsLookup{ StreamsLookupError: &encoding.StreamsLookupError{ FeedParamKey: feedIDs, @@ -962,7 +1065,7 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { errorMessage: "All attempts fail:\n#1: 500\n#2: 500\n#3: 500", }, { - name: "failure - returns retryable and then non-retryable", + name: "failure - returns retryable with 5s plugin retry interval", lookup: &StreamsLookup{ StreamsLookupError: &encoding.StreamsLookupError{ FeedParamKey: feedIDs, @@ -972,27 +1075,30 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { }, upkeepId: upkeepId, }, - retryNumber: 1, - statusCode: http.StatusInternalServerError, - lastStatusCode: http.StatusUnauthorized, - errorMessage: "All attempts fail:\n#1: 500\n#2: at timestamp 123456 upkeep 123456789 received status code 401 from mercury v0.3, most likely this is caused by unauthorized upkeep", + pluginRetries: 6, + retryNumber: totalAttempt, + statusCode: http.StatusInternalServerError, + retryable: true, + errorMessage: "All attempts fail:\n#1: 500\n#2: 500\n#3: 500", }, { - name: "failure - returns status code 420 not retryable", + name: "failure - returns retryable and then non-retryable", lookup: &StreamsLookup{ StreamsLookupError: &encoding.StreamsLookupError{ FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, TimeParamKey: timestamp, Time: big.NewInt(123456), }, upkeepId: upkeepId, }, - statusCode: 420, - errorMessage: "All attempts fail:\n#1: at timestamp 123456 upkeep 123456789 received status code 420 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", + retryNumber: 1, + statusCode: http.StatusInternalServerError, + lastStatusCode: http.StatusUnauthorized, + errorMessage: "All attempts fail:\n#1: 500\n#2: at timestamp 123456 upkeep 123456789 received status code 401 from mercury v0.3, most likely this is caused by unauthorized upkeep", }, { - name: "failure - returns status code 502 not retryable", + name: "failure - returns status code 422 not retryable", lookup: &StreamsLookup{ StreamsLookupError: &encoding.StreamsLookupError{ FeedParamKey: feedIDs, @@ -1002,8 +1108,8 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { }, upkeepId: upkeepId, }, - statusCode: http.StatusBadGateway, - errorMessage: "All attempts fail:\n#1: at timestamp 123456 upkeep 123456789 received status code 502 from mercury v0.3", + statusCode: http.StatusUnprocessableEntity, + errorMessage: "All attempts fail:\n#1: at timestamp 123456 upkeep 123456789 received status code 422 from mercury v0.3", }, { name: "success - retry when reports length does not match feeds length", @@ -1042,14 +1148,19 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { }, }, }, - retryNumber: 1, - statusCode: http.StatusOK, + retryNumber: 1, + statusCode: http.StatusOK, + lastStatusCode: http.StatusOK, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + if tt.pluginRetries != 0 { + r.mercury.pluginRetryCache.Set(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) + } + hc := mocks.NewHttpClient(t) b, err := json.Marshal(tt.response) assert.Nil(t, err) @@ -1071,7 +1182,7 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { b1, err := json.Marshal(tt.response) assert.Nil(t, err) resp1 := &http.Response{ - StatusCode: tt.statusCode, + StatusCode: tt.lastStatusCode, Body: io.NopCloser(bytes.NewReader(b1)), } hc.On("Do", mock.Anything).Return(resp0, nil).Once().On("Do", mock.Anything).Return(resp1, nil).Once() diff --git a/go.mod b/go.mod index f271bf421f6..4d8a9294020 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 - github.com/smartcontractkit/ocr2keepers v0.7.27 + github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 diff --git a/go.sum b/go.sum index 8426876e239..acd966e8aa3 100644 --- a/go.sum +++ b/go.sum @@ -1479,8 +1479,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 h1:qOsw2ETQD/Sb/W2xuYn2KPWjvvsWA0C+l19rWFq8iNg= github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= -github.com/smartcontractkit/ocr2keepers v0.7.27 h1:kwqMrzmEdq6gH4yqNuLQCbdlED0KaIjwZzu3FF+Gves= -github.com/smartcontractkit/ocr2keepers v0.7.27/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= +github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= +github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb h1:OMaBUb4X9IFPLbGbCHsMU+kw/BPCrewaVwWGIBc0I4A= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index f73838f1ab0..936729944f8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 - github.com/smartcontractkit/ocr2keepers v0.7.27 + github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 843cef26901..7bd733023c5 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2384,8 +2384,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 h1:qOsw2ETQD/Sb/W2xuYn2KPWjvvsWA0C+l19rWFq8iNg= github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= -github.com/smartcontractkit/ocr2keepers v0.7.27 h1:kwqMrzmEdq6gH4yqNuLQCbdlED0KaIjwZzu3FF+Gves= -github.com/smartcontractkit/ocr2keepers v0.7.27/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= +github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= +github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb h1:OMaBUb4X9IFPLbGbCHsMU+kw/BPCrewaVwWGIBc0I4A= From 7b5e6a5ea6c4a450af73acd67a322553d033a16a Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Mon, 6 Nov 2023 14:56:22 -0500 Subject: [PATCH 081/271] Fixes old pointer aliases (#11187) --- .../chaos/automation_chaos_test.go | 17 ++++++++--------- integration-tests/chaos/ocr2vrf_chaos_test.go | 17 ++++++++--------- integration-tests/chaos/ocr_chaos_test.go | 17 ++++++++--------- integration-tests/reorg/reorg_confirmer.go | 7 ++++--- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index a3d4e37406d..22c9e742f38 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -14,7 +14,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/cdk8s/blockscout" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" @@ -132,7 +131,7 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMinority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -141,7 +140,7 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -150,9 +149,9 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, DurationStr: "1m", - ContainerNames: &[]*string{a.Str("chainlink-db")}, + ContainerNames: &[]*string{utils.Ptr("chainlink-db")}, }, }, NetworkChaosFailMajorityNetwork: { @@ -160,8 +159,8 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{ChaosGroupMajority: a.Str("1")}, - ToLabels: &map[string]*string{ChaosGroupMinority: a.Str("1")}, + FromLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + ToLabels: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -170,8 +169,8 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{"app": a.Str("geth")}, - ToLabels: &map[string]*string{ChaosGroupMajorityPlus: a.Str("1")}, + FromLabels: &map[string]*string{"app": utils.Ptr("geth")}, + ToLabels: &map[string]*string{ChaosGroupMajorityPlus: utils.Ptr("1")}, DurationStr: "1m", }, }, diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index 0beccadddda..ba75974f01a 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -13,7 +13,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" @@ -68,7 +67,7 @@ func TestOCR2VRFChaos(t *testing.T) { chainlink.New(0, defaultOCR2VRFSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMinority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -78,7 +77,7 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewFailPods, // &chaos.Props{ - // LabelsSelector: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + // LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, // DurationStr: "1m", // }, //}, @@ -88,9 +87,9 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewFailPods, // &chaos.Props{ - // LabelsSelector: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + // LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, // DurationStr: "1m", - // ContainerNames: &[]*string{a.Str("chainlink-db")}, + // ContainerNames: &[]*string{utils.Ptr("chainlink-db")}, // }, //}, //NetworkChaosFailMajorityNetwork: { @@ -98,8 +97,8 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewNetworkPartition, // &chaos.Props{ - // FromLabels: &map[string]*string{ChaosGroupMajority: a.Str("1")}, - // ToLabels: &map[string]*string{ChaosGroupMinority: a.Str("1")}, + // FromLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + // ToLabels: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, // DurationStr: "1m", // }, //}, @@ -108,8 +107,8 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewNetworkPartition, // &chaos.Props{ - // FromLabels: &map[string]*string{"app": a.Str("geth")}, - // ToLabels: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + // FromLabels: &map[string]*string{"app": utils.Ptr("geth")}, + // ToLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, // DurationStr: "1m", // }, //}, diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index b65f8bb74f7..599fad8ddc5 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -15,7 +15,6 @@ import ( ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" @@ -81,8 +80,8 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{ChaosGroupMajority: a.Str("1")}, - ToLabels: &map[string]*string{ChaosGroupMinority: a.Str("1")}, + FromLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + ToLabels: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -91,8 +90,8 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{"app": a.Str("geth")}, - ToLabels: &map[string]*string{ChaosGroupMajorityPlus: a.Str("1")}, + FromLabels: &map[string]*string{"app": utils.Ptr("geth")}, + ToLabels: &map[string]*string{ChaosGroupMajorityPlus: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -101,7 +100,7 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMinority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -110,7 +109,7 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, DurationStr: "1m", }, }, @@ -119,9 +118,9 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: a.Str("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, DurationStr: "1m", - ContainerNames: &[]*string{a.Str("chainlink-db")}, + ContainerNames: &[]*string{utils.Ptr("chainlink-db")}, }, }, } diff --git a/integration-tests/reorg/reorg_confirmer.go b/integration-tests/reorg/reorg_confirmer.go index be535d2a6da..885bed2ad45 100644 --- a/integration-tests/reorg/reorg_confirmer.go +++ b/integration-tests/reorg/reorg_confirmer.go @@ -14,8 +14,9 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - a "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" + + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // The steps are: @@ -231,8 +232,8 @@ func (rc *ReorgController) forkNetwork(header blockchain.NodeHeader) error { rc.cfg.Env.Cfg.Namespace, &chaos.Props{ DurationStr: "999h", - FromLabels: &map[string]*string{"app": a.Str(reorg.TXNodesAppLabel)}, - ToLabels: &map[string]*string{"app": a.Str(reorg.MinerNodesAppLabel)}, + FromLabels: &map[string]*string{"app": utils.Ptr(reorg.TXNodesAppLabel)}, + ToLabels: &map[string]*string{"app": utils.Ptr(reorg.MinerNodesAppLabel)}, }, )) rc.chaosExperimentName = expName From 2b3806209238ebe3b2d8199639bf5c864fe4ba3c Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:01:26 -0800 Subject: [PATCH 082/271] [Functions] Respond with an error on insufficient balance (#11183) Co-authored-by: Morgan Kuphal <87319522+KuphJr@users.noreply.github.com> --- core/services/functions/connector_handler.go | 7 ++++++ .../functions/connector_handler_test.go | 24 ++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index a018157a373..8a8710e6ea6 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -78,6 +78,13 @@ func (h *functionsConnectorHandler) HandleGatewayMessage(ctx context.Context, ga } if balance, err := h.subscriptions.GetMaxUserBalance(fromAddr); err != nil || balance.Cmp(h.minimumBalance.ToInt()) < 0 { h.lggr.Errorw("user subscription has insufficient balance", "id", gatewayId, "address", fromAddr, "balance", balance, "minBalance", h.minimumBalance) + response := functions.SecretsResponseBase{ + Success: false, + ErrorMessage: "user subscription has insufficient balance", + } + if err := h.sendResponse(ctx, gatewayId, body, response); err != nil { + h.lggr.Errorw("failed to send response to gateway", "id", gatewayId, "error", err) + } return } diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index bb3e2acbabd..7bf98d7501d 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -39,7 +39,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { allowlist.On("Close", mock.Anything).Return(nil) subscriptions.On("Start", mock.Anything).Return(nil) subscriptions.On("Close", mock.Anything).Return(nil) - handler, err := functions.NewFunctionsConnectorHandler(addr.Hex(), privateKey, storage, allowlist, rateLimiter, subscriptions, *assets.NewLinkFromJuels(0), logger) + handler, err := functions.NewFunctionsConnectorHandler(addr.Hex(), privateKey, storage, allowlist, rateLimiter, subscriptions, *assets.NewLinkFromJuels(100), logger) require.NoError(t, err) handler.SetConnector(connector) @@ -78,7 +78,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { } storage.On("List", ctx, addr).Return(snapshot, nil).Once() allowlist.On("Allow", addr).Return(true).Once() - subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil) + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -91,6 +91,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { t.Run("orm error", func(t *testing.T) { storage.On("List", ctx, addr).Return(nil, errors.New("boom")).Once() allowlist.On("Allow", addr).Return(true).Once() + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -135,7 +136,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { storage.On("Put", ctx, &key, &record, signature).Return(nil).Once() allowlist.On("Allow", addr).Return(true).Once() - subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil) + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -148,6 +149,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { t.Run("orm error", func(t *testing.T) { storage.On("Put", ctx, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("boom")).Once() allowlist.On("Allow", addr).Return(true).Once() + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -163,6 +165,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { require.NoError(t, msg.Sign(privateKey)) storage.On("Put", ctx, mock.Anything, mock.Anything, mock.Anything).Return(s4.ErrWrongSignature).Once() allowlist.On("Allow", addr).Return(true).Once() + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -177,6 +180,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { msg.Body.Payload = json.RawMessage(`{sdfgdfgoscsicosd:sdf:::sdf ::; xx}`) require.NoError(t, msg.Sign(privateKey)) allowlist.On("Allow", addr).Return(true).Once() + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -186,6 +190,19 @@ func TestFunctionsConnectorHandler(t *testing.T) { handler.HandleGatewayMessage(ctx, "gw1", &msg) }) + + t.Run("insufficient balance", func(t *testing.T) { + allowlist.On("Allow", addr).Return(true).Once() + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(0), nil).Once() + connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { + msg, ok := args[2].(*api.Message) + require.True(t, ok) + require.Equal(t, `{"success":false,"error_message":"user subscription has insufficient balance"}`, string(msg.Body.Payload)) + + }).Return(nil).Once() + + handler.HandleGatewayMessage(ctx, "gw1", &msg) + }) }) t.Run("unsupported method", func(t *testing.T) { @@ -201,6 +218,7 @@ func TestFunctionsConnectorHandler(t *testing.T) { require.NoError(t, msg.Sign(privateKey)) allowlist.On("Allow", addr).Return(true).Once() + subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() handler.HandleGatewayMessage(testutils.Context(t), "gw1", &msg) }) }) From 0ae0b691466687ac7be2b49d9d4a1bd3136d6424 Mon Sep 17 00:00:00 2001 From: Erik Burton Date: Mon, 6 Nov 2023 13:24:15 -0800 Subject: [PATCH 083/271] chore: bump sigstore/cosign-installer from 2.1.0 to 3.1.2 (#11192) --- .github/actions/build-sign-publish-chainlink/action.yml | 2 +- .github/actions/goreleaser-build-sign-publish/action.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml index fe4ef858f58..62add53092a 100644 --- a/.github/actions/build-sign-publish-chainlink/action.yml +++ b/.github/actions/build-sign-publish-chainlink/action.yml @@ -223,7 +223,7 @@ runs: - if: inputs.sign-images == 'true' name: Install cosign - uses: sigstore/cosign-installer@581838fbedd492d2350a9ecd427a95d6de1e5d01 # v2.1.0 + uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2 with: cosign-release: "v1.6.0" diff --git a/.github/actions/goreleaser-build-sign-publish/action.yml b/.github/actions/goreleaser-build-sign-publish/action.yml index 845d2443fc1..b2d42c1234e 100644 --- a/.github/actions/goreleaser-build-sign-publish/action.yml +++ b/.github/actions/goreleaser-build-sign-publish/action.yml @@ -84,7 +84,7 @@ runs: version: ${{ inputs.zig-version }} - name: Setup cosign if: inputs.enable-cosign == 'true' - uses: sigstore/cosign-installer@581838fbedd492d2350a9ecd427a95d6de1e5d01 # v2.1.0 + uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2 with: cosign-release: ${{ inputs.cosign-version }} - name: Login to docker registry From c71ead762d71e8952698b1be303c158cbd6f74e0 Mon Sep 17 00:00:00 2001 From: Tate Date: Mon, 6 Nov 2023 17:00:15 -0700 Subject: [PATCH 084/271] [TT-668] Use Internal Mirror for docker images used in e2e tests (#11189) Part of the effor to remove the e2e flakes caused by dockerhub rate limit issues --- .github/workflows/integration-tests.yml | 4 ---- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 928c716dd11..125ddb3f4f3 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -228,8 +228,6 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} artifacts_location: ./integration-tests/smoke/logs/ publish_check_name: ${{ matrix.product.name }} token: ${{ secrets.GITHUB_TOKEN }} @@ -429,8 +427,6 @@ jobs: cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} artifacts_name: ${{ matrix.product.name }}-test-logs artifacts_location: ./integration-tests/smoke/logs/ publish_check_name: ${{ matrix.product.name }} diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 936729944f8..8daa5a29e5b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,7 +22,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66 + github.com/smartcontractkit/chainlink-testing-framework v1.18.4 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.28 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 7bd733023c5..696e06b6c91 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66 h1:AOqcHiAppMoIvM2WSJNIZzJDnOQNXyElbLFK3ZqoJeM= -github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.4 h1:IAalKSqRDSGj10zE/JvFrngKGp7mEIVTPh5jTnsaCec= +github.com/smartcontractkit/chainlink-testing-framework v1.18.4/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= From 0e5b5232e378263964f202ff4bb1712c4ad77813 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Tue, 7 Nov 2023 11:00:37 +0400 Subject: [PATCH 085/271] Fix Automation benchmark test (#11191) * fix benchmark test * bump runner --- .github/workflows/automation-benchmark-tests.yml | 2 +- integration-tests/benchmark/keeper_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 7bdb66c919e..a4338d642bc 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -57,7 +57,7 @@ jobs: id-token: write contents: read name: ${{ inputs.network }} Automation Benchmark Test - runs-on: ubuntu-latest + runs-on: ubuntu20.04-16cores-64GB env: SELECTED_NETWORKS: ${{ inputs.network }} SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 25a147fbf0b..9342f3629b9 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -227,7 +227,7 @@ func addRegistry(registryToTest string) []eth_contracts.KeeperRegistryVersion { case "2_0-Multiple": return repeatRegistries(eth_contracts.RegistryVersion_2_0, NumberOfRegistries) case "2_1-Multiple": - return repeatRegistries(eth_contracts.RegistryVersion_1_0, NumberOfRegistries) + return repeatRegistries(eth_contracts.RegistryVersion_2_1, NumberOfRegistries) default: return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0} } From fcaf226947ceece466df04e0524428f132ecc267 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Tue, 7 Nov 2023 10:12:36 +0100 Subject: [PATCH 086/271] Add to delegate job spec CaptureEATelemetry override from evm ocr cfg (#11197) * Add to delegate job spec CaptureEATelemetry override from evm ocr cfg * Add logs for when Ea telemetry is disabled * Update core/services/ocr/delegate.go Co-authored-by: Jordan Krage * Update core/services/ocr2/delegate.go * Update core/services/ocrcommon/data_source.go --------- Co-authored-by: Patrick Co-authored-by: Jordan Krage Co-authored-by: Gheorghe Strimtu --- core/services/ocr/delegate.go | 3 +++ core/services/ocr2/delegate.go | 2 ++ core/services/ocrcommon/data_source.go | 2 ++ 3 files changed, 7 insertions(+) diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index bbed43c151b..6eb6714a474 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -295,10 +295,13 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e configOverrider = configOverriderService } + jb.OCROracleSpec.CaptureEATelemetry = chain.Config().OCR().CaptureEATelemetry() enhancedTelemChan := make(chan ocrcommon.EnhancedTelemetryData, 100) if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint("EVM", chain.ID().String(), concreteSpec.ContractAddress.String(), synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry")) services = append(services, enhancedTelemService) + } else { + lggr.Infow("Enhanced telemetry is disabled for job", "job", jb.Name) } oracle, err := ocr.NewOracle(ocr.OracleArgs{ diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 39a8c84d6b9..99aa492bc7b 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -746,6 +746,8 @@ func (d *Delegate) newServicesMedian( if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry")) medianServices = append(medianServices, enhancedTelemService) + } else { + lggr.Infow("Enhanced telemetry is disabled for job", "job", jb.Name) } return medianServices, err2 diff --git a/core/services/ocrcommon/data_source.go b/core/services/ocrcommon/data_source.go index ed832e45fcf..0363a7124b6 100644 --- a/core/services/ocrcommon/data_source.go +++ b/core/services/ocrcommon/data_source.go @@ -144,6 +144,8 @@ func (ds *inMemoryDataSource) executeRun(ctx context.Context, timestamp Observat FinalResults: finalResult, RepTimestamp: timestamp, }) + } else { + ds.lggr.Infow("Enhanced telemetry is disabled for job", "job", ds.jb.Name) } return run, finalResult, err From 48b9902f5dc931c6a2d06699ea26f660b65630f1 Mon Sep 17 00:00:00 2001 From: Mohamed Mehany <7327188+mohamed-mehany@users.noreply.github.com> Date: Tue, 7 Nov 2023 12:11:36 +0100 Subject: [PATCH 087/271] Adds WeMix chain config (#10793) * Adds WeMix testnet config * Extending NoNewHeads threshold to 3s * Adds WeMix contract loader * Downgrading to go 1.20.5 bullseye * Revert "Downgrading to go 1.20.5 bullseye" This reverts commit 7eab839819456513dcf7f83ca8f08c97bdeb3835. * Update testing branch * Excludes fee delegation transactions for Wemix * Update testing branch * Linting * Fix typo * Update CTF --------- Co-authored-by: davidcauchi --- .github/workflows/on-demand-ocr-soak-test.yml | 2 + .../config/toml/defaults/WeMix_Mainnet.toml | 14 ++ .../config/toml/defaults/WeMix_Testnet.toml | 14 ++ .../evm/gas/block_history_estimator_test.go | 6 + core/chains/evm/gas/chain_specific.go | 7 + core/config/chaintype.go | 6 +- core/config/docs/chains-evm.toml | 2 +- core/services/chainlink/config_test.go | 4 +- core/services/ocr/contract_tracker.go | 2 +- docs/CONFIG.md | 160 +++++++++++++++++- .../contracts/contract_deployer.go | 6 + .../contracts/contract_loader.go | 7 + integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 14 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml create mode 100644 core/chains/evm/config/toml/defaults/WeMix_Testnet.toml diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 1e510c23be3..b6ccad22467 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -28,6 +28,8 @@ on: - "FANTOM_MAINNET" - "KROMA_MAINNET" - "KROMA_SEPOLIA" + - "WEMIX_TESTNET" + - "WEMIX_MAINNET" fundingPrivateKey: description: Private funding key (Skip for Simulated) required: false diff --git a/core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml b/core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml new file mode 100644 index 00000000000..ee50a9844a4 --- /dev/null +++ b/core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml @@ -0,0 +1,14 @@ +ChainID = '1111' +ChainType = 'wemix' +FinalityDepth = 1 +MinIncomingConfirmations = 1 +# WeMix emits a block every 1 second, regardless of transactions +LogPollInterval = '3s' +NoNewHeadsThreshold = '30s' + +[OCR] +ContractConfirmations = 1 + +[GasEstimator] +EIP1559DynamicFees = true +TipCapDefault = '100 gwei' diff --git a/core/chains/evm/config/toml/defaults/WeMix_Testnet.toml b/core/chains/evm/config/toml/defaults/WeMix_Testnet.toml new file mode 100644 index 00000000000..6cdb451eb1d --- /dev/null +++ b/core/chains/evm/config/toml/defaults/WeMix_Testnet.toml @@ -0,0 +1,14 @@ +ChainID = '1112' +ChainType = 'wemix' +FinalityDepth = 1 +MinIncomingConfirmations = 1 +# WeMix emits a block every 1 second, regardless of transactions +LogPollInterval = '3s' +NoNewHeadsThreshold = '30s' + +[OCR] +ContractConfirmations = 1 + +[GasEstimator] +EIP1559DynamicFees = true +TipCapDefault = '100 gwei' diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index 7f4d157e37a..decb68dbe99 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -1329,6 +1329,12 @@ func TestBlockHistoryEstimator_IsUsable(t *testing.T) { assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) }) + t.Run("returns false if transaction is of type 0x16 only on WeMix", func(t *testing.T) { + cfg.ChainTypeF = "wemix" + tx := evmtypes.Transaction{Type: 0x16, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + }) + t.Run("returns false if transaction has base fee higher than the gas price only on Celo", func(t *testing.T) { cfg.ChainTypeF = "celo" tx := evmtypes.Transaction{Type: 0x0, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} diff --git a/core/chains/evm/gas/chain_specific.go b/core/chains/evm/gas/chain_specific.go index 4d87b8b454e..5c0b256bd3f 100644 --- a/core/chains/evm/gas/chain_specific.go +++ b/core/chains/evm/gas/chain_specific.go @@ -42,5 +42,12 @@ func chainSpecificIsUsable(tx evmtypes.Transaction, baseFee *assets.Wei, chainTy return false } } + if chainType == config.ChainWeMix { + // WeMix specific transaction types that enables fee delegation. + // https://docs.wemix.com/v/en/design/fee-delegation + if tx.Type == 0x16 { + return false + } + } return true } diff --git a/core/config/chaintype.go b/core/config/chaintype.go index c99099ee616..0110f247b73 100644 --- a/core/config/chaintype.go +++ b/core/config/chaintype.go @@ -15,18 +15,18 @@ const ( ChainOptimismBedrock ChainType = "optimismBedrock" ChainXDai ChainType = "xdai" ChainCelo ChainType = "celo" + ChainWeMix ChainType = "wemix" ChainKroma ChainType = "kroma" ) var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Join([]string{ string(ChainArbitrum), string(ChainMetis), string(ChainXDai), string(ChainOptimismBedrock), string(ChainCelo), - string(ChainKroma), -}, ", ")) + string(ChainKroma), string(ChainWeMix)}, ", ")) // IsValid returns true if the ChainType value is known or empty. func (c ChainType) IsValid() bool { switch c { - case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma: + case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix: return true } return false diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 082bbd6cd19..c0cdfc7a310 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -14,7 +14,7 @@ BlockBackfillDepth = 10 # Default # BlockBackfillSkip enables skipping of very long backfills. BlockBackfillSkip = false # Default # ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma +# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix ChainType = 'arbitrum' # Example # FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID so it should not be necessary to change this under normal operation. # BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 59a02f1dcf9..cc3fda167de 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -1190,7 +1190,7 @@ func TestConfig_Validate(t *testing.T) { - 1: 6 errors: - ChainType: invalid value (Foo): must not be set with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma or omitted + - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted - HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth - GasEstimator: 2 errors: - FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault @@ -1199,7 +1199,7 @@ func TestConfig_Validate(t *testing.T) { - 2: 5 errors: - ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma or omitted + - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted - FinalityDepth: invalid value (0): must be greater than or equal to 1 - MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1 - 3.Nodes: 5 errors: diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 3a216e025f0..2308fa3035d 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -401,7 +401,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight // care about the block height; we have no way of getting the L1 block // height anyway return 0, nil - case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma: + case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix: // continue } latestBlockHeight := t.getLatestBlockHeight() diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 4b55c804a3d..fd8822c1626 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -3302,6 +3302,164 @@ GasLimit = 5300000

+
WeMix Mainnet (1111)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'wemix' +FinalityDepth = 1 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '3s' +LogKeepBlocksDepth = 100000 +MinIncomingConfirmations = 1 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '30s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '1m0s' + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '1 gwei' +LimitDefault = 500000 +LimitMax = 500000 +LimitMultiplier = '1' +LimitTransfer = 21000 +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = true +FeeCapDefault = '100 gwei' +TipCapDefault = '100 gwei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 100 +MaxBufferSize = 3 +SamplingInterval = '1s' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' + +[OCR] +ContractConfirmations = 1 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5300000 +``` + +

+ +
WeMix Testnet (1112)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'wemix' +FinalityDepth = 1 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '3s' +LogKeepBlocksDepth = 100000 +MinIncomingConfirmations = 1 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '30s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '1m0s' + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '115792089237316195423570985008687907853269984665.640564039457584007913129639935 tether' +PriceMin = '1 gwei' +LimitDefault = 500000 +LimitMax = 500000 +LimitMultiplier = '1' +LimitTransfer = 21000 +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = true +FeeCapDefault = '100 gwei' +TipCapDefault = '100 gwei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 100 +MaxBufferSize = 3 +SamplingInterval = '1s' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' + +[OCR] +ContractConfirmations = 1 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5300000 +``` + +

+
Simulated (1337)

```toml @@ -5074,7 +5232,7 @@ BlockBackfillSkip enables skipping of very long backfills. ChainType = 'arbitrum' # Example ``` ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma +Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix ### FinalityDepth ```toml diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 0c36a260815..916971f82d3 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -175,6 +175,8 @@ func NewContractDeployer(bcClient blockchain.EVMClient, logger zerolog.Logger) ( return &FantomContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil case *blockchain.KromaClient: return &KromaContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil + case *blockchain.WeMixClient: + return &WeMixContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil } return nil, errors.New("unknown blockchain client implementation for contract deployer, register blockchain client in NewContractDeployer") } @@ -246,6 +248,10 @@ type KromaContractDeployer struct { *EthereumContractDeployer } +type WeMixContractDeployer struct { + *EthereumContractDeployer +} + // NewEthereumContractDeployer returns an instantiated instance of the ETH contract deployer func NewEthereumContractDeployer(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractDeployer { return &EthereumContractDeployer{ diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index 4dda2d3f0c4..cfe7a35467e 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -64,6 +64,8 @@ func NewContractLoader(bcClient blockchain.EVMClient, logger zerolog.Logger) (Co return &OptimismContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil case *blockchain.PolygonZkEvmClient: return &PolygonZkEvmContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil + case *blockchain.WeMixClient: + return &WeMixContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil } return nil, errors.New("unknown blockchain client implementation for contract Loader, register blockchain client in NewContractLoader") } @@ -107,6 +109,11 @@ type PolygonZKEVMContractLoader struct { *EthereumContractLoader } +// WeMixContractLoader wraps for WeMix +type WeMixContractLoader struct { + *EthereumContractLoader +} + // NewEthereumContractLoader returns an instantiated instance of the ETH contract Loader func NewEthereumContractLoader(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractLoader { return &EthereumContractLoader{ diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 8daa5a29e5b..dab3cfa64b5 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,7 +22,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.4 + github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 github.com/smartcontractkit/ocr2keepers v0.7.28 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 696e06b6c91..e8ee06ff49e 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.4 h1:IAalKSqRDSGj10zE/JvFrngKGp7mEIVTPh5jTnsaCec= -github.com/smartcontractkit/chainlink-testing-framework v1.18.4/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65 h1:/iRhwYy5KFsaS9Zo1T64QxAd11HGZB5p/LHI5oVc4BU= +github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= From cc308a14d879478933de95c3a153e3cb7cf1a3b2 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 7 Nov 2023 13:22:56 +0100 Subject: [PATCH 088/271] fix mockery version (#11199) --- contracts/GNUmakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile index b477164a496..e41d6422c2f 100644 --- a/contracts/GNUmakefile +++ b/contracts/GNUmakefile @@ -34,7 +34,7 @@ abigen: ## Build & install abigen. .PHONY: mockery mockery: $(mockery) ## Install mockery. - go install github.com/vektra/mockery/v2@v2.28.1 + go install github.com/vektra/mockery/v2@v2.35.4 .PHONY: foundry foundry: ## Install foundry. From 302eb05d592132309b264e316f443f1ceb81b6c3 Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 7 Nov 2023 13:57:26 +0000 Subject: [PATCH 089/271] [BCF-2632] Adapt median to POC (#11178) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/ocr2/delegate.go | 9 +- .../generic/pipeline_runner_adapter.go | 12 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- plugins/cmd/chainlink-medianpoc/main.go | 42 ++++++ plugins/medianpoc/data_source.go | 79 +++++++++++ plugins/medianpoc/data_source_test.go | 115 ++++++++++++++++ plugins/medianpoc/plugin.go | 126 ++++++++++++++++++ plugins/medianpoc/plugin_test.go | 105 +++++++++++++++ 13 files changed, 489 insertions(+), 17 deletions(-) create mode 100644 plugins/cmd/chainlink-medianpoc/main.go create mode 100644 plugins/medianpoc/data_source.go create mode 100644 plugins/medianpoc/data_source_test.go create mode 100644 plugins/medianpoc/plugin.go create mode 100644 plugins/medianpoc/plugin_test.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 8d731f3f41d..7766697c608 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -305,7 +305,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index ebab8c990d3..5149148d030 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1466,8 +1466,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 99aa492bc7b..75147ca2333 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -596,10 +596,11 @@ func (d *Delegate) newServicesGenericPlugin( } pluginConfig := types.ReportingPluginServiceConfig{ - PluginName: cconf.PluginName, - Command: command, - ProviderType: cconf.ProviderType, - PluginConfig: string(p.PluginConfig), + PluginName: cconf.PluginName, + Command: command, + ProviderType: cconf.ProviderType, + TelemetryType: cconf.TelemetryType, + PluginConfig: string(p.PluginConfig), } pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner) diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go index 6afb35ca758..def33114e8c 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go @@ -23,7 +23,7 @@ type PipelineRunnerAdapter struct { logger logger.Logger } -func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) ([]types.TaskResult, error) { +func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) (types.TaskResults, error) { s := pipeline.Spec{ DotDagSource: spec, CreatedAt: time.Now(), @@ -54,9 +54,13 @@ func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, var taskResults[i] = types.TaskResult{ ID: trr.ID.String(), Type: string(trr.Task.Type()), - Value: trr.Result.Value, - Error: trr.Result.Error, - Index: int(trr.TaskRun.Index), + Index: int(trr.Task.OutputIndex()), + + TaskValue: types.TaskValue{ + Value: trr.Result.Value, + Error: trr.Result.Error, + IsTerminal: len(trr.Task.Outputs()) == 0, + }, } } return taskResults, nil diff --git a/go.mod b/go.mod index 4d8a9294020..a5628eb4b80 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index acd966e8aa3..a35e0f32bd9 100644 --- a/go.sum +++ b/go.sum @@ -1467,8 +1467,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index dab3cfa64b5..3c5a5e1f2e7 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -388,7 +388,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index e8ee06ff49e..cd5cb84a686 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2370,8 +2370,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/plugins/cmd/chainlink-medianpoc/main.go b/plugins/cmd/chainlink-medianpoc/main.go new file mode 100644 index 00000000000..325de6538fa --- /dev/null +++ b/plugins/cmd/chainlink-medianpoc/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/hashicorp/go-plugin" + + "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins" + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/plugins/medianpoc" +) + +const ( + loggerName = "PluginMedianPoc" +) + +func main() { + s := loop.MustNewStartedServer(loggerName) + defer s.Stop() + + p := medianpoc.NewPlugin(s.Logger) + defer s.Logger.ErrorIfFn(p.Close, "Failed to close") + + s.MustRegister(p) + + stop := make(chan struct{}) + defer close(stop) + + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: reportingplugins.ReportingPluginHandshakeConfig(), + Plugins: map[string]plugin.Plugin{ + reportingplugins.PluginServiceName: &reportingplugins.GRPCService[types.MedianProvider]{ + PluginServer: p, + BrokerConfig: loop.BrokerConfig{ + Logger: s.Logger, + StopCh: stop, + GRPCOpts: s.GRPCOpts, + }, + }, + }, + GRPCServer: s.GRPCOpts.NewServer, + }) +} diff --git a/plugins/medianpoc/data_source.go b/plugins/medianpoc/data_source.go new file mode 100644 index 00000000000..7b20f1e5eb3 --- /dev/null +++ b/plugins/medianpoc/data_source.go @@ -0,0 +1,79 @@ +package medianpoc + +import ( + "context" + "errors" + "fmt" + "math/big" + "sync" + "time" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type DataSource struct { + pipelineRunner types.PipelineRunnerService + spec string + lggr logger.Logger + + current bridges.BridgeMetaData + mu sync.RWMutex +} + +func (d *DataSource) Observe(ctx context.Context, reportTimestamp ocrtypes.ReportTimestamp) (*big.Int, error) { + md, err := bridges.MarshalBridgeMetaData(d.currentAnswer()) + if err != nil { + d.lggr.Warnw("unable to attach metadata for run", "err", err) + } + + // NOTE: job metadata is automatically attached by the pipeline runner service + vars := types.Vars{ + Vars: map[string]interface{}{ + "jobRun": md, + }, + } + + results, err := d.pipelineRunner.ExecuteRun(ctx, d.spec, vars, types.Options{}) + if err != nil { + return nil, err + } + + finalResults := results.FinalResults() + if len(finalResults) == 0 { + return nil, errors.New("pipeline execution failed: not enough results") + } + + finalResult := finalResults[0] + if finalResult.Error != nil { + return nil, fmt.Errorf("pipeline execution failed: %w", finalResult.Error) + } + + asDecimal, err := utils.ToDecimal(finalResult.Value) + if err != nil { + return nil, errors.New("cannot convert observation to decimal") + } + + resultAsBigInt := asDecimal.BigInt() + d.updateAnswer(resultAsBigInt) + return resultAsBigInt, nil +} + +func (d *DataSource) currentAnswer() (*big.Int, *big.Int) { + d.mu.RLock() + defer d.mu.RUnlock() + return d.current.LatestAnswer, d.current.UpdatedAt +} + +func (d *DataSource) updateAnswer(latestAnswer *big.Int) { + d.mu.Lock() + defer d.mu.Unlock() + d.current = bridges.BridgeMetaData{ + LatestAnswer: latestAnswer, + UpdatedAt: big.NewInt(time.Now().Unix()), + } +} diff --git a/plugins/medianpoc/data_source_test.go b/plugins/medianpoc/data_source_test.go new file mode 100644 index 00000000000..e9a7945cee4 --- /dev/null +++ b/plugins/medianpoc/data_source_test.go @@ -0,0 +1,115 @@ +package medianpoc + +import ( + "context" + "errors" + "math/big" + "testing" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" +) + +type mockPipelineRunner struct { + results types.TaskResults + err error + spec string + vars types.Vars + options types.Options +} + +func (m *mockPipelineRunner) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) (types.TaskResults, error) { + m.spec = spec + m.vars = vars + m.options = options + return m.results, m.err +} + +func TestDataSource(t *testing.T) { + lggr := logger.TestLogger(t) + expect := int64(3) + pr := &mockPipelineRunner{ + results: types.TaskResults{ + { + TaskValue: types.TaskValue{ + Value: expect, + Error: nil, + IsTerminal: true, + }, + Index: 2, + }, + { + TaskValue: types.TaskValue{ + Value: int(4), + Error: nil, + IsTerminal: false, + }, + Index: 1, + }, + }, + } + spec := "SPEC" + ds := &DataSource{ + pipelineRunner: pr, + spec: spec, + lggr: lggr, + } + res, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{}) + require.NoError(t, err) + assert.Equal(t, big.NewInt(expect), res) + assert.Equal(t, spec, pr.spec) + assert.Equal(t, big.NewInt(expect), ds.current.LatestAnswer) +} + +func TestDataSource_ResultErrors(t *testing.T) { + lggr := logger.TestLogger(t) + pr := &mockPipelineRunner{ + results: types.TaskResults{ + { + TaskValue: types.TaskValue{ + Error: errors.New("something went wrong"), + IsTerminal: true, + }, + Index: 0, + }, + }, + } + spec := "SPEC" + ds := &DataSource{ + pipelineRunner: pr, + spec: spec, + lggr: lggr, + } + _, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{}) + assert.ErrorContains(t, err, "something went wrong") +} + +func TestDataSource_ResultNotAnInt(t *testing.T) { + lggr := logger.TestLogger(t) + + expect := "string-result" + pr := &mockPipelineRunner{ + results: types.TaskResults{ + { + TaskValue: types.TaskValue{ + Value: expect, + IsTerminal: true, + }, + Index: 0, + }, + }, + } + spec := "SPEC" + ds := &DataSource{ + pipelineRunner: pr, + spec: spec, + lggr: lggr, + } + _, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{}) + assert.ErrorContains(t, err, "cannot convert observation to decimal") +} diff --git a/plugins/medianpoc/plugin.go b/plugins/medianpoc/plugin.go new file mode 100644 index 00000000000..ceea1eb84f5 --- /dev/null +++ b/plugins/medianpoc/plugin.go @@ -0,0 +1,126 @@ +package medianpoc + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins" + "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +func NewPlugin(lggr logger.Logger) *Plugin { + return &Plugin{ + Plugin: loop.Plugin{Logger: lggr}, + MedianProviderServer: reportingplugins.MedianProviderServer{}, + stop: make(utils.StopChan), + } +} + +type Plugin struct { + loop.Plugin + stop utils.StopChan + reportingplugins.MedianProviderServer +} + +type jsonConfig struct { + Pipelines map[string]string `json:"pipelines"` +} + +func (j jsonConfig) defaultPipeline() (string, error) { + return j.getPipeline("__DEFAULT_PIPELINE__") +} + +func (j jsonConfig) getPipeline(key string) (string, error) { + v, ok := j.Pipelines[key] + if ok { + return v, nil + } + return "", fmt.Errorf("no pipeline found for %s", key) +} + +func (p *Plugin) NewReportingPluginFactory( + ctx context.Context, + config types.ReportingPluginServiceConfig, + provider types.MedianProvider, + pipelineRunner types.PipelineRunnerService, + telemetry types.TelemetryClient, + errorLog types.ErrorLog, +) (types.ReportingPluginFactory, error) { + f, err := p.newFactory(ctx, config, provider, pipelineRunner, telemetry, errorLog) + if err != nil { + return nil, err + } + s := &reportingPluginFactoryService{lggr: p.Logger, ReportingPluginFactory: f} + p.SubService(s) + return s, nil +} + +func (p *Plugin) newFactory(ctx context.Context, config types.ReportingPluginServiceConfig, provider types.MedianProvider, pipelineRunner types.PipelineRunnerService, telemetry types.TelemetryClient, errorLog types.ErrorLog) (*median.NumericalMedianFactory, error) { + jc := &jsonConfig{} + err := json.Unmarshal([]byte(config.PluginConfig), jc) + if err != nil { + return nil, err + } + + dp, err := jc.defaultPipeline() + if err != nil { + return nil, err + } + ds := &DataSource{ + pipelineRunner: pipelineRunner, + spec: dp, + lggr: p.Logger, + } + + jfp, err := jc.getPipeline("juelsPerFeeCoinPipeline") + if err != nil { + return nil, err + } + jds := &DataSource{ + pipelineRunner: pipelineRunner, + spec: jfp, + lggr: p.Logger, + } + factory := &median.NumericalMedianFactory{ + ContractTransmitter: provider.MedianContract(), + DataSource: ds, + JuelsPerFeeCoinDataSource: jds, + Logger: logger.NewOCRWrapper( + p.Logger, + true, + func(msg string) {}, + ), + OnchainConfigCodec: provider.OnchainConfigCodec(), + ReportCodec: provider.ReportCodec(), + } + return factory, nil +} + +type reportingPluginFactoryService struct { + services.StateMachine + lggr logger.Logger + ocrtypes.ReportingPluginFactory +} + +func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() } + +func (r *reportingPluginFactoryService) Start(ctx context.Context) error { + return r.StartOnce("ReportingPluginFactory", func() error { return nil }) +} + +func (r *reportingPluginFactoryService) Close() error { + return r.StopOnce("ReportingPluginFactory", func() error { return nil }) +} + +func (r *reportingPluginFactoryService) HealthReport() map[string]error { + return map[string]error{r.Name(): r.Healthy()} +} diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go new file mode 100644 index 00000000000..74a0695c6c9 --- /dev/null +++ b/plugins/medianpoc/plugin_test.go @@ -0,0 +1,105 @@ +package medianpoc + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type mockErrorLog struct { + types.ErrorLog +} + +type mockOffchainConfigDigester struct { + ocrtypes.OffchainConfigDigester +} + +type mockContractTransmitter struct { + ocrtypes.ContractTransmitter +} + +type mockContractConfigTracker struct { + ocrtypes.ContractConfigTracker +} + +type mockReportCodec struct { + median.ReportCodec +} + +type mockMedianContract struct { + median.MedianContract +} + +type mockOnchainConfigCodec struct { + median.OnchainConfigCodec +} + +type provider struct { + types.Service +} + +func (p provider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { + return mockOffchainConfigDigester{} +} + +func (p provider) ContractTransmitter() ocrtypes.ContractTransmitter { + return mockContractTransmitter{} +} + +func (p provider) ContractConfigTracker() ocrtypes.ContractConfigTracker { + return mockContractConfigTracker{} +} + +func (p provider) ReportCodec() median.ReportCodec { + return mockReportCodec{} +} + +func (p provider) MedianContract() median.MedianContract { + return mockMedianContract{} +} + +func (p provider) OnchainConfigCodec() median.OnchainConfigCodec { + return mockOnchainConfigCodec{} +} + +func TestNewPlugin(t *testing.T) { + lggr := logger.TestLogger(t) + p := NewPlugin(lggr) + + defaultSpec := "default-spec" + juelsPerFeeCoinSpec := "jpfc-spec" + config := types.ReportingPluginServiceConfig{ + PluginConfig: fmt.Sprintf( + `{"pipelines": {"__DEFAULT_PIPELINE__": "%s", "juelsPerFeeCoinPipeline": "%s"}}`, + defaultSpec, + juelsPerFeeCoinSpec, + ), + } + pr := &mockPipelineRunner{} + prov := provider{} + + f, err := p.newFactory( + context.Background(), + config, + prov, + pr, + nil, + mockErrorLog{}, + ) + require.NoError(t, err) + + ds := f.DataSource.(*DataSource) + assert.Equal(t, defaultSpec, ds.spec) + jpfcDs := f.JuelsPerFeeCoinDataSource.(*DataSource) + assert.Equal(t, juelsPerFeeCoinSpec, jpfcDs.spec) +} From b1c5a856d71f560883d2cb61d633c47660d41365 Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 7 Nov 2023 14:59:44 +0000 Subject: [PATCH 090/271] [fix] Pull in correct commit sha for pull request events (#11202) --- tools/flakeytests/utils.go | 44 +++++++++++++++++++++------------ tools/flakeytests/utils_test.go | 29 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/tools/flakeytests/utils.go b/tools/flakeytests/utils.go index 18ab43980b3..7ead45c8587 100644 --- a/tools/flakeytests/utils.go +++ b/tools/flakeytests/utils.go @@ -29,23 +29,16 @@ func DigString(mp map[string]interface{}, path []string) (string, error) { return vs, nil } -func GetGithubMetadata(repo string, eventName string, sha string, path string) Context { - event := map[string]interface{}{} - if path != "" { - r, err := os.Open(path) - if err != nil { - log.Fatalf("Error reading gh event at path: %s", path) - } - - d, err := io.ReadAll(r) - if err != nil { - log.Fatal("Error reading gh event into string") - } +func getGithubMetadata(repo string, eventName string, sha string, e io.Reader) Context { + d, err := io.ReadAll(e) + if err != nil { + log.Fatal("Error reading gh event into string") + } - err = json.Unmarshal(d, &event) - if err != nil { - log.Fatalf("Error unmarshaling gh event at path: %s", path) - } + event := map[string]interface{}{} + err = json.Unmarshal(d, &event) + if err != nil { + log.Fatalf("Error unmarshaling gh event at path") } basicCtx := &Context{Repository: repo, CommitSHA: sha, Type: eventName} @@ -58,8 +51,27 @@ func GetGithubMetadata(repo string, eventName string, sha string, path string) C } basicCtx.PullRequestURL = prURL + + // For pull request events, the $GITHUB_SHA variable doesn't actually + // contain the sha for the latest commit, as documented here: + // https://stackoverflow.com/a/68068674 + var newSha string + s, err := DigString(event, []string{"pull_request", "head", "sha"}) + if err == nil { + newSha = s + } + + basicCtx.CommitSHA = newSha return *basicCtx default: return *basicCtx } } + +func GetGithubMetadata(repo string, eventName string, sha string, path string) Context { + event, err := os.Open(path) + if err != nil { + log.Fatalf("Error reading gh event at path: %s", path) + } + return getGithubMetadata(repo, eventName, sha, event) +} diff --git a/tools/flakeytests/utils_test.go b/tools/flakeytests/utils_test.go index d3ef8eb602d..17d597c3c02 100644 --- a/tools/flakeytests/utils_test.go +++ b/tools/flakeytests/utils_test.go @@ -1,6 +1,8 @@ package flakeytests import ( + "fmt" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -17,3 +19,30 @@ func TestDigString(t *testing.T) { require.NoError(t, err) assert.Equal(t, "some-url", out) } + +var prEventTemplate = ` +{ + "pull_request": { + "head": { + "sha": "%s" + }, + "_links": { + "html": { + "href": "%s" + } + } + } +} +` + +func TestGetGithubMetadata(t *testing.T) { + repo, eventName, sha, event := "chainlink", "merge_group", "a-sha", `{}` + ctx := getGithubMetadata(repo, eventName, sha, strings.NewReader(event)) + assert.Equal(t, Context{Repository: repo, CommitSHA: sha, Type: eventName}, ctx) + + anotherSha, eventName, url := "another-sha", "pull_request", "a-url" + event = fmt.Sprintf(prEventTemplate, anotherSha, url) + sha = "302eb05d592132309b264e316f443f1ceb81b6c3" + ctx = getGithubMetadata(repo, eventName, sha, strings.NewReader(event)) + assert.Equal(t, Context{Repository: repo, CommitSHA: anotherSha, Type: eventName, PullRequestURL: url}, ctx) +} From 031d9d26c32171152dd29bcad10ccc2e93478a6d Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 7 Nov 2023 15:23:43 +0000 Subject: [PATCH 091/271] [chore] Pin to chainlink-relay@main (#11203) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7766697c608..9dbe132346c 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -305,7 +305,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 5149148d030..7025d38c20d 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1466,8 +1466,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/go.mod b/go.mod index a5628eb4b80..d61b7b6f61b 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 diff --git a/go.sum b/go.sum index a35e0f32bd9..f2737ab3aea 100644 --- a/go.sum +++ b/go.sum @@ -1467,8 +1467,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 3c5a5e1f2e7..6f2809df8c1 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -388,7 +388,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index cd5cb84a686..9e1be8b8144 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2370,8 +2370,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From f6f3cfefec2d2a44e23a000c1a6dbd64f40edd2f Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 7 Nov 2023 10:25:51 -0500 Subject: [PATCH 092/271] Simple delete queue for mercury transmitter (#11182) --- .../services/relay/evm/mercury/transmitter.go | 85 +++++++++++++++++-- docs/CHANGELOG.md | 5 ++ 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index 88c3113abc6..557210e58a5 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -33,6 +33,7 @@ import ( var ( maxTransmitQueueSize = 10_000 + maxDeleteQueueSize = 10_000 transmitTimeout = 5 * time.Second ) @@ -60,6 +61,24 @@ var ( }, []string{"feedID"}, ) + transmitQueueDeleteErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_transmit_queue_delete_error_count", + Help: "Running count of DB errors when trying to delete an item from the queue DB", + }, + []string{"feedID"}, + ) + transmitQueueInsertErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_transmit_queue_insert_error_count", + Help: "Running count of DB errors when trying to insert an item into the queue DB", + }, + []string{"feedID"}, + ) + transmitQueuePushErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_transmit_queue_push_error_count", + Help: "Running count of DB errors when trying to push an item onto the queue", + }, + []string{"feedID"}, + ) transmitServerErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "mercury_transmit_server_error_count", Help: "Number of errored transmissions that failed due to an error returned by the mercury server", @@ -99,9 +118,14 @@ type mercuryTransmitter struct { queue *TransmitQueue wg sync.WaitGroup - transmitSuccessCount prometheus.Counter - transmitDuplicateCount prometheus.Counter - transmitConnectionErrorCount prometheus.Counter + deleteQueue chan *pb.TransmitRequest + + transmitSuccessCount prometheus.Counter + transmitDuplicateCount prometheus.Counter + transmitConnectionErrorCount prometheus.Counter + transmitQueueDeleteErrorCount prometheus.Counter + transmitQueueInsertErrorCount prometheus.Counter + transmitQueuePushErrorCount prometheus.Counter } var PayloadTypes = getPayloadTypes() @@ -139,9 +163,13 @@ func NewTransmitter(lggr logger.Logger, cfgTracker ConfigTracker, rpcClient wsrp make(chan (struct{})), nil, sync.WaitGroup{}, + make(chan *pb.TransmitRequest, maxDeleteQueueSize), transmitSuccessCount.WithLabelValues(feedIDHex), transmitDuplicateCount.WithLabelValues(feedIDHex), transmitConnectionErrorCount.WithLabelValues(feedIDHex), + transmitQueueDeleteErrorCount.WithLabelValues(feedIDHex), + transmitQueueInsertErrorCount.WithLabelValues(feedIDHex), + transmitQueuePushErrorCount.WithLabelValues(feedIDHex), } } @@ -164,6 +192,8 @@ func (mt *mercuryTransmitter) Start(ctx context.Context) (err error) { return err } mt.wg.Add(1) + go mt.runDeleteQueueLoop() + mt.wg.Add(1) go mt.runQueueLoop() return nil }) @@ -192,6 +222,46 @@ func (mt *mercuryTransmitter) HealthReport() map[string]error { return report } +func (mt *mercuryTransmitter) runDeleteQueueLoop() { + defer mt.wg.Done() + runloopCtx, cancel := mt.stopCh.Ctx(context.Background()) + defer cancel() + + // Exponential backoff for very rarely occurring errors (DB disconnect etc) + b := backoff.Backoff{ + Min: 1 * time.Second, + Max: 120 * time.Second, + Factor: 2, + Jitter: true, + } + + for { + select { + case req := <-mt.deleteQueue: + for { + if err := mt.persistenceManager.Delete(runloopCtx, req); err != nil { + mt.lggr.Errorw("Failed to delete transmit request record", "error", err, "req", req) + mt.transmitQueueDeleteErrorCount.Inc() + select { + case <-time.After(b.Duration()): + // Wait a backoff duration before trying to delete again + continue + case <-mt.stopCh: + // abort and return immediately on stop even if items remain in queue + return + } + } + break + } + // success + b.Reset() + case <-mt.stopCh: + // abort and return immediately on stop even if items remain in queue + return + } + } +} + func (mt *mercuryTransmitter) runQueueLoop() { defer mt.wg.Done() // Exponential backoff with very short retry interval (since latency is a priority) @@ -253,9 +323,10 @@ func (mt *mercuryTransmitter) runQueueLoop() { } } - if err := mt.persistenceManager.Delete(runloopCtx, t.Req); err != nil { - mt.lggr.Errorw("Failed to delete transmit request record", "error", err, "reportCtx", t.ReportCtx) - return + select { + case mt.deleteQueue <- t.Req: + default: + mt.lggr.Criticalw("Delete queue is full", "reportCtx", t.ReportCtx) } } } @@ -288,9 +359,11 @@ func (mt *mercuryTransmitter) Transmit(ctx context.Context, reportCtx ocrtypes.R mt.lggr.Tracew("Transmit enqueue", "req", req, "report", report, "reportCtx", reportCtx, "signatures", signatures) if err := mt.persistenceManager.Insert(ctx, req, reportCtx); err != nil { + mt.transmitQueueInsertErrorCount.Inc() return err } if ok := mt.queue.Push(req, reportCtx); !ok { + mt.transmitQueuePushErrorCount.Inc() return errors.New("transmit queue is closed") } return nil diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a9f9d080f42..a10f9dd1c6d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. +- New prom metrics for mercury: + `mercury_transmit_queue_delete_error_count` + `mercury_transmit_queue_insert_error_count` + `mercury_transmit_queue_push_error_count` + Nops should consider alerting on these. ### Changed From 8b4e0f8db32502f928860b928e6102a96db78b07 Mon Sep 17 00:00:00 2001 From: Awbrey Hughlett Date: Tue, 7 Nov 2023 12:00:25 -0500 Subject: [PATCH 093/271] Update SimulatedBackendClient CallContext (#11164) * Update SimulatedBackendClient CallContext The function `CallContext` has different supported contract function calls than `BatchCallContext` even though the latter is simply a batch version of the former. This commit makes the two functions match both in the supported calls, but also in the validation and execution of those calls. * Update core/chains/evm/client/simulated_backend_client.go Use suggestion on default address returned on error. Co-authored-by: Jordan Krage * replace errors with panics * align parameters to documentation, real-world RPCs, and the simulated backend client * allow different receipt types * comment on strong typing on result * remove logic for handling incomplete transaction receipt handling on simulated backend client * reduce panics in favor of errors * remove commented code --------- Co-authored-by: Jordan Krage --- .../evm/client/simulated_backend_client.go | 503 ++++++++---------- core/chains/evm/txmgr/transmitchecker.go | 2 +- .../plugins/ocr2keeper/evm21/core/utils.go | 17 +- 3 files changed, 235 insertions(+), 287 deletions(-) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 78239089676..d542e98e6eb 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -25,6 +25,41 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +func init() { + var err error + + balanceOfABI, err = abi.JSON(strings.NewReader(balanceOfABIString)) + if err != nil { + panic(fmt.Errorf("%w: while parsing erc20ABI", err)) + } +} + +var ( + balanceOfABIString = `[ + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +]` + + balanceOfABI abi.ABI +) + // SimulatedBackendClient is an Client implementation using a simulated // blockchain backend. Note that not all RPC methods are implemented here. type SimulatedBackendClient struct { @@ -51,69 +86,6 @@ func (c *SimulatedBackendClient) Dial(context.Context) error { // other simulated clients might still be using it func (c *SimulatedBackendClient) Close() {} -// checkEthCallArgs extracts and verifies the arguments for an eth_call RPC -func (c *SimulatedBackendClient) checkEthCallArgs( - args []interface{}) (*CallArgs, *big.Int, error) { - if len(args) != 2 { - return nil, nil, fmt.Errorf( - "should have two arguments after \"eth_call\", got %d", len(args)) - } - callArgs, ok := args[0].(map[string]interface{}) - if !ok { - return nil, nil, fmt.Errorf("third arg to SimulatedBackendClient.Call "+ - "must be an eth.CallArgs, got %+#v", args[0]) - } - blockNumber, err := c.blockNumber(args[1]) - if err != nil { - return nil, nil, fmt.Errorf("fourth arg to SimulatedBackendClient.Call "+ - "must be the string \"latest\", or a *big.Int, got %#+v", args[1]) - } - - // to and from need to map to a common.Address but could come in as a string - var ( - toAddr common.Address - frmAddr common.Address - ) - - toAddr, err = interfaceToAddress(callArgs["to"]) - if err != nil { - return nil, nil, err - } - - // from is optional in the standard client; default to 0x when missing - if value, ok := callArgs["from"]; ok { - addr, err := interfaceToAddress(value) - if err != nil { - return nil, nil, err - } - - frmAddr = addr - } else { - frmAddr = common.HexToAddress("0x") - } - - ca := CallArgs{ - To: toAddr, - From: frmAddr, - Data: callArgs["data"].(hexutil.Bytes), - } - - return &ca, blockNumber, nil -} - -func interfaceToAddress(value interface{}) (common.Address, error) { - switch v := value.(type) { - case common.Address: - return v, nil - case string: - return common.HexToAddress(v), nil - case *big.Int: - return common.BigToAddress(v), nil - default: - return common.HexToAddress("0x"), fmt.Errorf("unrecognized value type for converting value to common.Address; try string, *big.Int, or common.Address") - } -} - // CallContext mocks the ethereum client RPC calls used by chainlink, copying the // return value into result. // The simulated client avoids the old block error from the simulated backend by @@ -121,41 +93,16 @@ func interfaceToAddress(value interface{}) (common.Address, error) { // and will not return an error when an old block is used. func (c *SimulatedBackendClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { switch method { + case "eth_getTransactionReceipt": + return c.ethGetTransactionReceipt(ctx, result, args...) + case "eth_getBlockByNumber": + return c.ethGetBlockByNumber(ctx, result, args...) case "eth_call": - var ( - callArgs *CallArgs - b []byte - err error - ) - - if callArgs, _, err = c.checkEthCallArgs(args); err != nil { - return err - } - - callMsg := ethereum.CallMsg{From: callArgs.From, To: &callArgs.To, Data: callArgs.Data} - - if b, err = c.b.CallContract(ctx, callMsg, nil /* always latest block */); err != nil { - return fmt.Errorf("%w: while calling contract at address %x with "+ - "data %x", err, callArgs.To, callArgs.Data) - } - - switch r := result.(type) { - case *hexutil.Bytes: - *r = append(*r, b...) - - if !bytes.Equal(*r, b) { - return fmt.Errorf("was passed a non-empty array, or failed to copy "+ - "answer. Expected %x = %x", *r, b) - } - return nil - default: - return fmt.Errorf("first arg to SimulatedBackendClient.Call is an "+ - "unrecognized type: %T; add processing logic for it here", result) - } + return c.ethCall(ctx, result, args...) + case "eth_getHeaderByNumber": + return c.ethGetHeaderByNumber(ctx, result, args...) default: - return fmt.Errorf("second arg to SimulatedBackendClient.Call is an RPC "+ - "API method which has not yet been implemented: %s. Add processing for "+ - "it here", method) + return fmt.Errorf("second arg to SimulatedBackendClient.Call is an RPC API method which has not yet been implemented: %s. Add processing for it here", method) } } @@ -175,38 +122,6 @@ func (c *SimulatedBackendClient) currentBlockNumber() *big.Int { return c.b.Blockchain().CurrentBlock().Number } -var balanceOfABIString = `[ - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "balance", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - } -]` - -var balanceOfABI abi.ABI - -func init() { - var err error - balanceOfABI, err = abi.JSON(strings.NewReader(balanceOfABIString)) - if err != nil { - panic(fmt.Errorf("%w: while parsing erc20ABI", err)) - } -} - func (c *SimulatedBackendClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (balance *big.Int, err error) { callData, err := balanceOfABI.Pack("balanceOf", address) if err != nil { @@ -251,13 +166,12 @@ func (c *SimulatedBackendClient) blockNumber(number interface{}) (blockNumber *b case "earliest": return big.NewInt(0), nil case "pending": - panic("not implemented") // I don't understand the semantics of this. + panic("pending block not supported by simulated backend client") // I don't understand the semantics of this. // return big.NewInt(0).Add(c.currentBlockNumber(), big.NewInt(1)), nil default: - blockNumber, err = utils.HexToUint256(n) + blockNumber, err := hexutil.DecodeBig(n) if err != nil { - return nil, fmt.Errorf("%w: while parsing '%s' as hex-encoded"+ - "block number", err, n) + return nil, fmt.Errorf("%w: while parsing '%s' as hex-encoded block number", err, n) } return blockNumber, nil } @@ -521,114 +435,18 @@ func (c *SimulatedBackendClient) BatchCallContext(ctx context.Context, b []rpc.B for i, elem := range b { switch elem.Method { case "eth_getTransactionReceipt": - if _, ok := elem.Result.(*evmtypes.Receipt); !ok { - return fmt.Errorf("SimulatedBackendClient expected return type of *evmtypes.Receipt for eth_getTransactionReceipt, got type %T", elem.Result) - } - if len(elem.Args) != 1 { - return fmt.Errorf("SimulatedBackendClient expected 1 arg, got %d for eth_getTransactionReceipt", len(elem.Args)) - } - hash, is := elem.Args[0].(common.Hash) - if !is { - return fmt.Errorf("SimulatedBackendClient expected arg to be a hash, got: %T", elem.Args[0]) - } - receipt, err := c.b.TransactionReceipt(ctx, hash) - if receipt != nil { - *(b[i].Result.(*evmtypes.Receipt)) = *evmtypes.FromGethReceipt(receipt) - } - b[i].Error = err + b[i].Error = c.ethGetTransactionReceipt(ctx, b[i].Result, b[i].Args...) case "eth_getBlockByNumber": - switch v := elem.Result.(type) { - case *evmtypes.Head: - case *evmtypes.Block: - default: - return fmt.Errorf("SimulatedBackendClient expected return type of [*evmtypes.Head] or [*evmtypes.Block] for eth_getBlockByNumber, got type %T", v) - } - if len(elem.Args) != 2 { - return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_getBlockByNumber", len(elem.Args)) - } - blockNumOrTag, is := elem.Args[0].(string) - if !is { - return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getBlockByNumber, got: %T", elem.Args[0]) - } - _, is = elem.Args[1].(bool) - if !is { - return fmt.Errorf("SimulatedBackendClient expected second arg to be a boolean for eth_getBlockByNumber, got: %T", elem.Args[1]) - } - header, err := c.fetchHeader(ctx, blockNumOrTag) - if err != nil { - return err - } - switch res := elem.Result.(type) { - case *evmtypes.Head: - res.Number = header.Number.Int64() - res.Hash = header.Hash() - res.ParentHash = header.ParentHash - res.Timestamp = time.Unix(int64(header.Time), 0).UTC() - case *evmtypes.Block: - res.Number = header.Number.Int64() - res.Hash = header.Hash() - res.ParentHash = header.ParentHash - res.Timestamp = time.Unix(int64(header.Time), 0).UTC() - default: - return fmt.Errorf("SimulatedBackendClient Unexpected Type %T", elem.Result) - } - b[i].Error = err + b[i].Error = c.ethGetBlockByNumber(ctx, b[i].Result, b[i].Args...) case "eth_call": - if len(elem.Args) != 2 { - return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_call", len(elem.Args)) - } - - _, ok := elem.Result.(*string) - if !ok { - return fmt.Errorf("SimulatedBackendClient expected result to be *string for eth_call, got: %T", elem.Result) - } - - params, ok := elem.Args[0].(map[string]interface{}) - if !ok { - return fmt.Errorf("SimulatedBackendClient expected first arg to be map[string]interface{} for eth_call, got: %T", elem.Args[0]) - } - - blockNum, ok := elem.Args[1].(string) - if !ok { - return fmt.Errorf("SimulatedBackendClient expected second arg to be a string for eth_call, got: %T", elem.Args[1]) - } - - if blockNum != "" { - if _, ok = new(big.Int).SetString(blockNum, 0); !ok { - return fmt.Errorf("error while converting block number string: %s to big.Int ", blockNum) - } - } - - callMsg := toCallMsg(params) - resp, err := c.b.CallContract(ctx, callMsg, nil) - *(b[i].Result.(*string)) = hexutil.Encode(resp) - b[i].Error = err + b[i].Error = c.ethCall(ctx, b[i].Result, b[i].Args...) case "eth_getHeaderByNumber": - if len(elem.Args) != 1 { - return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_getHeaderByNumber", len(elem.Args)) - } - blockNum, is := elem.Args[0].(string) - if !is { - return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getHeaderByNumber, got: %T", elem.Args[0]) - } - n, err := hexutil.DecodeBig(blockNum) - if err != nil { - return fmt.Errorf("error while converting hex block number %s to big.Int ", blockNum) - } - header, err := c.b.HeaderByNumber(ctx, n) - if err != nil { - return err - } - switch v := elem.Result.(type) { - case *types.Header: - b[i].Result = header - default: - return fmt.Errorf("SimulatedBackendClient Unexpected Type %T", v) - } + b[i].Error = c.ethGetHeaderByNumber(ctx, b[i].Result, b[i].Args...) default: return fmt.Errorf("SimulatedBackendClient got unsupported method %s", elem.Method) } } + return nil } @@ -655,32 +473,175 @@ func (c *SimulatedBackendClient) Commit() common.Hash { return c.b.Commit() } -func toCallMsg(params map[string]interface{}) ethereum.CallMsg { - var callMsg ethereum.CallMsg +func (c *SimulatedBackendClient) IsL2() bool { + return false +} - switch to := params["to"].(type) { - case string: - toAddr := common.HexToAddress(to) - callMsg.To = &toAddr - case common.Address: - callMsg.To = &to - case *common.Address: - callMsg.To = to +func (c *SimulatedBackendClient) fetchHeader(ctx context.Context, blockNumOrTag string) (*types.Header, error) { + switch blockNumOrTag { + case rpc.SafeBlockNumber.String(): + return c.b.Blockchain().CurrentSafeBlock(), nil + case rpc.LatestBlockNumber.String(): + return c.b.Blockchain().CurrentHeader(), nil + case rpc.FinalizedBlockNumber.String(): + return c.b.Blockchain().CurrentFinalBlock(), nil default: - panic("unexpected type of 'to' parameter") + blockNum, ok := new(big.Int).SetString(blockNumOrTag, 0) + if !ok { + return nil, fmt.Errorf("error while converting block number string: %s to big.Int ", blockNumOrTag) + } + return c.b.HeaderByNumber(ctx, blockNum) } +} - switch from := params["from"].(type) { - case nil: - // This parameter is not required so nil is acceptable - case string: - callMsg.From = common.HexToAddress(from) - case common.Address: - callMsg.From = from - case *common.Address: - callMsg.From = *from +func (c *SimulatedBackendClient) ethGetTransactionReceipt(ctx context.Context, result interface{}, args ...interface{}) error { + if len(args) != 1 { + return fmt.Errorf("SimulatedBackendClient expected 1 arg, got %d for eth_getTransactionReceipt", len(args)) + } + + hash, is := args[0].(common.Hash) + if !is { + return fmt.Errorf("SimulatedBackendClient expected arg to be a hash, got: %T", args[0]) + } + + receipt, err := c.b.TransactionReceipt(ctx, hash) + if err != nil { + return err + } + + // strongly typing the result here has the consequence of not being flexible in + // custom types where a real-world RPC client would allow for custom types with + // custom marshalling. + switch typed := result.(type) { + case *types.Receipt: + *typed = *receipt + case *evmtypes.Receipt: + *typed = *evmtypes.FromGethReceipt(receipt) + default: + return fmt.Errorf("SimulatedBackendClient expected return type of *evmtypes.Receipt for eth_getTransactionReceipt, got type %T", result) + } + + return nil +} + +func (c *SimulatedBackendClient) ethGetBlockByNumber(ctx context.Context, result interface{}, args ...interface{}) error { + if len(args) != 2 { + return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_getBlockByNumber", len(args)) + } + + blockNumOrTag, is := args[0].(string) + if !is { + return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getBlockByNumber, got: %T", args[0]) + } + + _, is = args[1].(bool) + if !is { + return fmt.Errorf("SimulatedBackendClient expected second arg to be a boolean for eth_getBlockByNumber, got: %T", args[1]) + } + + header, err := c.fetchHeader(ctx, blockNumOrTag) + if err != nil { + return err + } + + switch res := result.(type) { + case *evmtypes.Head: + res.Number = header.Number.Int64() + res.Hash = header.Hash() + res.ParentHash = header.ParentHash + res.Timestamp = time.Unix(int64(header.Time), 0).UTC() + case *evmtypes.Block: + res.Number = header.Number.Int64() + res.Hash = header.Hash() + res.ParentHash = header.ParentHash + res.Timestamp = time.Unix(int64(header.Time), 0).UTC() + default: + return fmt.Errorf("SimulatedBackendClient Unexpected Type %T", res) + } + + return nil +} + +func (c *SimulatedBackendClient) ethCall(ctx context.Context, result interface{}, args ...interface{}) error { + if len(args) != 2 { + return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_call", len(args)) + } + + params, ok := args[0].(map[string]interface{}) + if !ok { + return fmt.Errorf("SimulatedBackendClient expected first arg to be map[string]interface{} for eth_call, got: %T", args[0]) + } + + if _, err := c.blockNumber(args[1]); err != nil { + return fmt.Errorf("SimulatedBackendClient expected second arg to be the string 'latest' or a *big.Int for eth_call, got: %T", args[1]) + } + + resp, err := c.b.CallContract(ctx, toCallMsg(params), nil /* always latest block on simulated backend */) + if err != nil { + return err + } + + switch typedResult := result.(type) { + case *hexutil.Bytes: + *typedResult = append(*typedResult, resp...) + + if !bytes.Equal(*typedResult, resp) { + return fmt.Errorf("SimulatedBackendClient was passed a non-empty array, or failed to copy answer. Expected %x = %x", *typedResult, resp) + } + case *string: + *typedResult = hexutil.Encode(resp) default: - panic("unexpected type of 'from' parameter") + return fmt.Errorf("SimulatedBackendClient unexpected type %T", result) + } + + return nil +} + +func (c *SimulatedBackendClient) ethGetHeaderByNumber(ctx context.Context, result interface{}, args ...interface{}) error { + if len(args) != 1 { + return fmt.Errorf("SimulatedBackendClient expected 1 arg, got %d for eth_getHeaderByNumber", len(args)) + } + + blockNumber, err := c.blockNumber(args[0]) + if err != nil { + return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getHeaderByNumber: %w", err) + } + + header, err := c.b.HeaderByNumber(ctx, blockNumber) + if err != nil { + return err + } + + switch typedResult := result.(type) { + case *types.Header: + *typedResult = *header + default: + return fmt.Errorf("SimulatedBackendClient unexpected Type %T", typedResult) + } + + return nil +} + +func toCallMsg(params map[string]interface{}) ethereum.CallMsg { + var callMsg ethereum.CallMsg + + toAddr, err := interfaceToAddress(params["to"]) + if err != nil { + panic(fmt.Errorf("unexpected 'to' parameter: %s", err)) + } + + callMsg.To = &toAddr + + // from is optional in the standard client; default to 0x when missing + if value, ok := params["from"]; ok { + addr, err := interfaceToAddress(value) + if err != nil { + panic(fmt.Errorf("unexpected 'from' parameter: %s", err)) + } + + callMsg.From = addr + } else { + callMsg.From = common.HexToAddress("0x") } switch data := params["data"].(type) { @@ -691,7 +652,7 @@ func toCallMsg(params map[string]interface{}) ethereum.CallMsg { case []byte: callMsg.Data = data default: - panic("unexpected type of 'data' parameter") + panic("unexpected type of 'data' parameter; try hexutil.Bytes, []byte, or nil") } if value, ok := params["value"].(*big.Int); ok { @@ -709,23 +670,23 @@ func toCallMsg(params map[string]interface{}) ethereum.CallMsg { return callMsg } -func (c *SimulatedBackendClient) IsL2() bool { - return false -} +func interfaceToAddress(value interface{}) (common.Address, error) { + switch v := value.(type) { + case common.Address: + return v, nil + case string: + if ok := common.IsHexAddress(v); !ok { + return common.Address{}, fmt.Errorf("string not formatted as a hex encoded evm address") + } -func (c *SimulatedBackendClient) fetchHeader(ctx context.Context, blockNumOrTag string) (*types.Header, error) { - switch blockNumOrTag { - case rpc.SafeBlockNumber.String(): - return c.b.Blockchain().CurrentSafeBlock(), nil - case rpc.LatestBlockNumber.String(): - return c.b.Blockchain().CurrentHeader(), nil - case rpc.FinalizedBlockNumber.String(): - return c.b.Blockchain().CurrentFinalBlock(), nil - default: - blockNum, ok := new(big.Int).SetString(blockNumOrTag, 0) - if !ok { - return nil, fmt.Errorf("error while converting block number string: %s to big.Int ", blockNumOrTag) + return common.HexToAddress(v), nil + case *big.Int: + if v.Uint64() > 0 || len(v.Bytes()) > 20 { + return common.Address{}, fmt.Errorf("invalid *big.Int; value must be larger than 0 with a byte length <= 20") } - return c.b.HeaderByNumber(ctx, blockNum) + + return common.BigToAddress(v), nil + default: + return common.Address{}, fmt.Errorf("unrecognized value type for converting value to common.Address; use hex encoded string, *big.Int, or common.Address") } } diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go index 4636b708489..eb6edd3f587 100644 --- a/core/chains/evm/txmgr/transmitchecker.go +++ b/core/chains/evm/txmgr/transmitchecker.go @@ -217,7 +217,7 @@ func (v *VRFV1Checker) Check( requestTransactionReceipt := &gethtypes.Receipt{} batch := []rpc.BatchElem{{ Method: "eth_getBlockByNumber", - Args: []interface{}{nil}, + Args: []interface{}{"latest", false}, Result: mostRecentHead, }, { Method: "eth_getTransactionReceipt", diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go index 6a31b938fc6..1da28c1ad09 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go @@ -3,7 +3,6 @@ package core import ( "context" "math/big" - "strings" "github.com/ethereum/go-ethereum/common" @@ -14,20 +13,8 @@ import ( // GetTxBlock calls eth_getTransactionReceipt on the eth client to obtain a tx receipt func GetTxBlock(ctx context.Context, client client.Client, txHash common.Hash) (*big.Int, common.Hash, error) { receipt := types.Receipt{} - err := client.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash) - if err != nil { - if strings.Contains(err.Error(), "not yet been implemented") { - // workaround for simulated chains - // Exploratory: fix this properly (e.g. in the simulated backend) - r, err1 := client.TransactionReceipt(ctx, txHash) - if err1 != nil { - return nil, common.Hash{}, err1 - } - if r.Status != 1 { - return nil, common.Hash{}, nil - } - return r.BlockNumber, r.BlockHash, nil - } + + if err := client.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash); err != nil { return nil, common.Hash{}, err } From 38de9a61c0233b412e9cf8a32011909022da6320 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Tue, 7 Nov 2023 12:08:01 -0500 Subject: [PATCH 094/271] add a TODO for 206 response parsing (#11188) --- .../plugins/ocr2keeper/evm21/streams_lookup.go | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go index f183e1f6bbe..660550afe97 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go @@ -514,20 +514,7 @@ func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryDa state = encoding.MercuryFlakyFailure return fmt.Errorf("%d", resp.StatusCode) } else if resp.StatusCode == http.StatusPartialContent { - //var response MercuryV03Response - //err1 = json.Unmarshal(body, &response) - //if err1 != nil { - // lggr.Warnf("at timestamp %s upkeep %s failed to unmarshal body to MercuryV03Response from mercury v0.3: %v", sl.Time.String(), sl.upkeepId.String(), err1) - // retryable = false - // state = encoding.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 - //var receivedFeeds []string - //for _, f := range response.Reports { - // receivedFeeds = append(receivedFeeds, f.FeedID) - //} + // TODO (AUTO-5044): handle response code 206 entirely with errors field parsing lggr.Warnf("at timestamp %s upkeep %s requested [%s] feeds but mercury v0.3 server returned 206 status, treating it as 404 and retrying", sl.Time.String(), sl.upkeepId.String(), sl.Feeds) retryable = true state = encoding.MercuryFlakyFailure From 4e45a2ac05849703f34a4f5ce4ae539e9cfdcbcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedemann=20F=C3=BCrst?= <59653747+friedemannf@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:43:02 +0100 Subject: [PATCH 095/271] Add zkSync support (#11162) * feat(zksync): add custom error messages * feat(zksync): add custom chain type to ignore 0x71 transaction type and commit default node config * fix: add exhaustive case stmt for zkSync ChainType * docs: regenerate CONFIG.md * test: add zksync ChainType to expected output * test: add test for custom zkSync tx type * fix: reduce HistoryDepth setting to 5 * ci: trigger pipelines * fix(zksync): also ignore tx type 0xff * docs(zksync): regenerate CONFIG.md --- core/chains/evm/client/errors.go | 18 +- core/chains/evm/client/errors_test.go | 16 ++ .../config/toml/defaults/zkSync_Goerli.toml | 14 ++ .../config/toml/defaults/zkSync_Mainnet.toml | 14 ++ .../evm/gas/block_history_estimator_test.go | 16 ++ core/chains/evm/gas/chain_specific.go | 7 + core/config/chaintype.go | 5 +- core/config/docs/chains-evm.toml | 2 +- core/scripts/common/helpers.go | 5 + core/services/chainlink/config_test.go | 4 +- core/services/ocr/contract_tracker.go | 2 +- docs/CONFIG.md | 160 +++++++++++++++++- 12 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 core/chains/evm/config/toml/defaults/zkSync_Goerli.toml create mode 100644 core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 0d177455e33..4cb505dc9eb 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -207,7 +207,23 @@ var harmony = ClientErrors{ Fatal: harmonyFatal, } -var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo} +var zkSync = ClientErrors{ + NonceTooLow: regexp.MustCompile(`(?:: |^)nonce too low\..+actual: \d*$`), + NonceTooHigh: regexp.MustCompile(`(?:: |^)nonce too high\..+actual: \d*$`), + TerminallyUnderpriced: regexp.MustCompile(`(?:: |^)max fee per gas less than block base fee$`), + InsufficientEth: regexp.MustCompile(`(?:: |^)(?:insufficient balance for transfer$|insufficient funds for gas + value)`), + TxFeeExceedsCap: regexp.MustCompile(`(?:: |^)max priority fee per gas higher than max fee per gas$`), + // intrinsic gas too low - gas limit less than 14700 + // Not enough gas for transaction validation - gas limit less than L2 fee + // Failed to pay the fee to the operator - gas limit less than L2+L1 fee + // Error function_selector = 0x, data = 0x - contract call with gas limit of 0 + // can't start a transaction from a non-account - trying to send from an invalid address, e.g. estimating a contract -> contract tx + // max fee per gas higher than 2^64-1 - uint64 overflow + // oversized data - data too large + Fatal: regexp.MustCompile(`(?:: |^)(?:exceeds block gas limit|intrinsic gas too low|Not enough gas for transaction validation|Failed to pay the fee to the operator|Error function_selector = 0x, data = 0x|invalid sender. can't start a transaction from a non-account|max(?: priority)? fee per (?:gas|pubdata byte) higher than 2\^64-1|oversized data. max: \d+; actual: \d+)$`), +} + +var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo, zkSync} func (s *SendError) is(errorType int) bool { if s == nil || s.err == nil { diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go index a5a3cc15eb6..ad8079824ab 100644 --- a/core/chains/evm/client/errors_test.go +++ b/core/chains/evm/client/errors_test.go @@ -40,6 +40,7 @@ func Test_Eth_Errors(t *testing.T) { {"call failed: nonce too low: address 0x0499BEA33347cb62D79A9C0b1EDA01d8d329894c current nonce (5833) > tx nonce (5511)", true, "Avalanche"}, {"call failed: OldNonce", true, "Nethermind"}, {"call failed: OldNonce, Current nonce: 22, nonce of rejected tx: 17", true, "Nethermind"}, + {"nonce too low. allowed nonce range: 427 - 447, actual: 426", true, "zkSync"}, } for _, test := range tests { @@ -60,6 +61,7 @@ func Test_Eth_Errors(t *testing.T) { {"nonce too high: address 0x336394A3219e71D9d9bd18201d34E95C1Bb7122C, tx: 8089 state: 8090", true, "Arbitrum"}, {"nonce too high", true, "Geth"}, {"nonce too high", true, "Erigon"}, + {"nonce too high. allowed nonce range: 427 - 477, actual: 527", true, "zkSync"}, } for _, test := range tests { @@ -152,6 +154,7 @@ func Test_Eth_Errors(t *testing.T) { {"FeeTooLowToCompete", true, "Nethermind"}, {"transaction underpriced", true, "Klaytn"}, {"intrinsic gas too low", true, "Klaytn"}, + {"max fee per gas less than block base fee", true, "zkSync"}, } for _, test := range tests { @@ -194,6 +197,8 @@ func Test_Eth_Errors(t *testing.T) { {"call failed: InsufficientFunds, Account balance: 4740799397601480913, cumulative cost: 22019342038993800000", true, "Nethermind"}, {"insufficient funds", true, "Klaytn"}, {"insufficient funds for gas * price + value + gatewayFee", true, "celo"}, + {"insufficient balance for transfer", true, "zkSync"}, + {"insufficient funds for gas + value. balance: 42719769622667482000, fee: 48098250000000, value: 42719769622667482000", true, "celo"}, } for _, test := range tests { err = evmclient.NewSendErrorS(test.message) @@ -213,6 +218,7 @@ func Test_Eth_Errors(t *testing.T) { {"invalid gas fee cap", true, "Klaytn"}, {"max fee per gas higher than max priority fee per gas", true, "Klaytn"}, {"tx fee (1.10 of currency celo) exceeds the configured cap (1.00 celo)", true, "celo"}, + {"max priority fee per gas higher than max fee per gas", true, "zkSync"}, } for _, test := range tests { err = evmclient.NewSendErrorS(test.message) @@ -329,6 +335,16 @@ func Test_Eth_Errors_Fatal(t *testing.T) { {"`to` address of transaction in blacklist", true, "Harmony"}, {"`from` address of transaction in blacklist", true, "Harmony"}, {"staking message does not match directive message", true, "Harmony"}, + + {"intrinsic gas too low", true, "zkSync"}, + {"failed to validate the transaction. reason: Validation revert: Account validation error: Not enough gas for transaction validation", true, "zkSync"}, + {"failed to validate the transaction. reason: Validation revert: Failed to pay for the transaction: Failed to pay the fee to the operator", true, "zkSync"}, + {"failed to validate the transaction. reason: Validation revert: Account validation error: Error function_selector = 0x, data = 0x", true, "zkSync"}, + {"invalid sender. can't start a transaction from a non-account", true, "zkSync"}, + {"Failed to serialize transaction: max fee per gas higher than 2^64-1", true, "zkSync"}, + {"Failed to serialize transaction: max fee per pubdata byte higher than 2^64-1", true, "zkSync"}, + {"Failed to serialize transaction: max priority fee per gas higher than 2^64-1", true, "zkSync"}, + {"Failed to serialize transaction: oversized data. max: 1000000; actual: 1000000", true, "zkSync"}, } for _, test := range tests { diff --git a/core/chains/evm/config/toml/defaults/zkSync_Goerli.toml b/core/chains/evm/config/toml/defaults/zkSync_Goerli.toml new file mode 100644 index 00000000000..04529a41b81 --- /dev/null +++ b/core/chains/evm/config/toml/defaults/zkSync_Goerli.toml @@ -0,0 +1,14 @@ +ChainID = '280' +ChainType = 'zksync' +FinalityDepth = 1 +LogPollInterval = '5s' +MinIncomingConfirmations = 1 +NoNewHeadsThreshold = '1m' + +[GasEstimator] +LimitDefault = 3_500_000 +PriceMax = 18446744073709551615 +PriceMin = 0 + +[HeadTracker] +HistoryDepth = 5 diff --git a/core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml b/core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml new file mode 100644 index 00000000000..d7808edd15f --- /dev/null +++ b/core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml @@ -0,0 +1,14 @@ +ChainID = '324' +ChainType = 'zksync' +FinalityDepth = 1 +LogPollInterval = '5s' +MinIncomingConfirmations = 1 +NoNewHeadsThreshold = '1m' + +[GasEstimator] +LimitDefault = 3_500_000 +PriceMax = 18446744073709551615 +PriceMin = 0 + +[HeadTracker] +HistoryDepth = 5 diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index decb68dbe99..c8b193c4435 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -23,6 +23,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/config" "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" @@ -1348,6 +1349,21 @@ func TestBlockHistoryEstimator_IsUsable(t *testing.T) { assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) }) + + t.Run("returns false if transaction is of type 0x71 or 0xff only on zkSync", func(t *testing.T) { + cfg.ChainTypeF = string(config.ChainZkSync) + tx := evmtypes.Transaction{Type: 0x71, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + + tx.Type = 0x02 + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + + tx.Type = 0xff + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + + cfg.ChainTypeF = "" + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + }) } func TestBlockHistoryEstimator_EffectiveTipCap(t *testing.T) { diff --git a/core/chains/evm/gas/chain_specific.go b/core/chains/evm/gas/chain_specific.go index 5c0b256bd3f..4f0d2e6b2f8 100644 --- a/core/chains/evm/gas/chain_specific.go +++ b/core/chains/evm/gas/chain_specific.go @@ -49,5 +49,12 @@ func chainSpecificIsUsable(tx evmtypes.Transaction, baseFee *assets.Wei, chainTy return false } } + if chainType == config.ChainZkSync { + // zKSync specific type for contract deployment & priority transactions + // https://era.zksync.io/docs/reference/concepts/transactions.html#eip-712-0x71 + if tx.Type == 0x71 || tx.Type == 0xff { + return false + } + } return true } diff --git a/core/config/chaintype.go b/core/config/chaintype.go index 0110f247b73..21fb8cd297d 100644 --- a/core/config/chaintype.go +++ b/core/config/chaintype.go @@ -17,16 +17,17 @@ const ( ChainCelo ChainType = "celo" ChainWeMix ChainType = "wemix" ChainKroma ChainType = "kroma" + ChainZkSync ChainType = "zksync" ) var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Join([]string{ string(ChainArbitrum), string(ChainMetis), string(ChainXDai), string(ChainOptimismBedrock), string(ChainCelo), - string(ChainKroma), string(ChainWeMix)}, ", ")) + string(ChainKroma), string(ChainWeMix), string(ChainZkSync)}, ", ")) // IsValid returns true if the ChainType value is known or empty. func (c ChainType) IsValid() bool { switch c { - case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix: + case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix, ChainZkSync: return true } return false diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index c0cdfc7a310..381ab794d60 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -14,7 +14,7 @@ BlockBackfillDepth = 10 # Default # BlockBackfillSkip enables skipping of very long backfills. BlockBackfillSkip = false # Default # ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix +# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync ChainType = 'arbitrum' # Example # FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID so it should not be necessary to change this under normal operation. # BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go index d03dcec097f..c141e8a29c4 100644 --- a/core/scripts/common/helpers.go +++ b/core/scripts/common/helpers.go @@ -219,6 +219,11 @@ func explorerLinkPrefix(chainID int64) (prefix string) { case 8453: prefix = "https://basescan.org" + case 280: // zkSync Goerli testnet + prefix = "https://goerli.explorer.zksync.io" + case 324: // zkSync mainnet + prefix = "https://explorer.zksync.io" + default: // Unknown chain, return prefix as-is prefix = "" } diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index cc3fda167de..34fcc4bbe91 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -1190,7 +1190,7 @@ func TestConfig_Validate(t *testing.T) { - 1: 6 errors: - ChainType: invalid value (Foo): must not be set with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted + - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync or omitted - HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth - GasEstimator: 2 errors: - FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault @@ -1199,7 +1199,7 @@ func TestConfig_Validate(t *testing.T) { - 2: 5 errors: - ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id - Nodes: missing: must have at least one node - - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted + - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync or omitted - FinalityDepth: invalid value (0): must be greater than or equal to 1 - MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1 - 3.Nodes: 5 errors: diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 2308fa3035d..f49e556d4e8 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -401,7 +401,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight // care about the block height; we have no way of getting the L1 block // height anyway return 0, nil - case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix: + case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix, config.ChainZkSync: // continue } latestBlockHeight := t.getLatestBlockHeight() diff --git a/docs/CONFIG.md b/docs/CONFIG.md index fd8822c1626..1eb9cd5023d 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -2986,6 +2986,164 @@ GasLimit = 5300000

+
zkSync Goerli (280)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'zksync' +FinalityDepth = 1 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '5s' +LogKeepBlocksDepth = 100000 +MinIncomingConfirmations = 1 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '1m0s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '1m0s' + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '18.446744073709551615 ether' +PriceMin = '0' +LimitDefault = 3500000 +LimitMax = 500000 +LimitMultiplier = '1' +LimitTransfer = 21000 +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = false +FeeCapDefault = '100 gwei' +TipCapDefault = '1 wei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 5 +MaxBufferSize = 3 +SamplingInterval = '1s' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' + +[OCR] +ContractConfirmations = 4 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5300000 +``` + +

+ +
zkSync Mainnet (324)

+ +```toml +AutoCreateKey = true +BlockBackfillDepth = 10 +BlockBackfillSkip = false +ChainType = 'zksync' +FinalityDepth = 1 +FinalityTagEnabled = false +LogBackfillBatchSize = 1000 +LogPollInterval = '5s' +LogKeepBlocksDepth = 100000 +MinIncomingConfirmations = 1 +MinContractPayment = '0.00001 link' +NonceAutoSync = true +NoNewHeadsThreshold = '1m0s' +RPCDefaultBatchSize = 250 +RPCBlockQueryDelay = 1 + +[Transactions] +ForwardersEnabled = false +MaxInFlight = 16 +MaxQueued = 250 +ReaperInterval = '1h0m0s' +ReaperThreshold = '168h0m0s' +ResendAfterThreshold = '1m0s' + +[BalanceMonitor] +Enabled = true + +[GasEstimator] +Mode = 'BlockHistory' +PriceDefault = '20 gwei' +PriceMax = '18.446744073709551615 ether' +PriceMin = '0' +LimitDefault = 3500000 +LimitMax = 500000 +LimitMultiplier = '1' +LimitTransfer = 21000 +BumpMin = '5 gwei' +BumpPercent = 20 +BumpThreshold = 3 +EIP1559DynamicFees = false +FeeCapDefault = '100 gwei' +TipCapDefault = '1 wei' +TipCapMin = '1 wei' + +[GasEstimator.BlockHistory] +BatchSize = 25 +BlockHistorySize = 8 +CheckInclusionBlocks = 12 +CheckInclusionPercentile = 90 +TransactionPercentile = 60 + +[HeadTracker] +HistoryDepth = 5 +MaxBufferSize = 3 +SamplingInterval = '1s' + +[NodePool] +PollFailureThreshold = 5 +PollInterval = '10s' +SelectionMode = 'HighestHead' +SyncThreshold = 5 +LeaseDuration = '0s' + +[OCR] +ContractConfirmations = 4 +ContractTransmitterTransmitTimeout = '10s' +DatabaseTimeout = '10s' +ObservationGracePeriod = '1s' + +[OCR2] +[OCR2.Automation] +GasLimit = 5300000 +``` + +

+
Optimism Goerli (420)

```toml @@ -5232,7 +5390,7 @@ BlockBackfillSkip enables skipping of very long backfills. ChainType = 'arbitrum' # Example ``` ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID. -Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix +Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync ### FinalityDepth ```toml From e4b50ff0516e172782d96ea96fcefd876742c5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedemann=20F=C3=BCrst?= <59653747+friedemannf@users.noreply.github.com> Date: Tue, 7 Nov 2023 19:45:37 +0100 Subject: [PATCH 096/271] Bump libocr => 13e0202ae8d7e38245422aa93af82010390f9e9b (#11212) * Bump libocr to 13e0202ae8d7e38245422aa93af82010390f9e9b * go mod tidy --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 9dbe132346c..b72d07978fd 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,7 +21,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 + github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7025d38c20d..b1e62010ab4 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1476,8 +1476,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 h1:qOsw2ETQD/Sb/W2xuYn2KPWjvvsWA0C+l19rWFq8iNg= -github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= +github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= +github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= diff --git a/go.mod b/go.mod index d61b7b6f61b..cd0fb0fab4e 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb - github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 + github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb diff --git a/go.sum b/go.sum index f2737ab3aea..fb97398f842 100644 --- a/go.sum +++ b/go.sum @@ -1477,8 +1477,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 h1:qOsw2ETQD/Sb/W2xuYn2KPWjvvsWA0C+l19rWFq8iNg= -github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= +github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= +github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 6f2809df8c1..eb542651b56 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 - github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 + github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 9e1be8b8144..ecafa2706e2 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2382,8 +2382,8 @@ github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88 github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= -github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545 h1:qOsw2ETQD/Sb/W2xuYn2KPWjvvsWA0C+l19rWFq8iNg= -github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= +github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= +github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= From f5d6797e4f0af7a7d8e634b31daaecc098548c8c Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 7 Nov 2023 12:21:32 -0700 Subject: [PATCH 097/271] [TT-681] Remove usages of github.com/pkg/errors (#11213) --- integration-tests/actions/actions.go | 18 +-- integration-tests/actions/actions_local.go | 5 +- .../actions/automation_ocr_helpers_local.go | 3 +- .../actions/ocr_helpers_local.go | 9 +- integration-tests/actions/vrfv1/actions.go | 9 +- .../actions/vrfv2_actions/vrfv2_steps.go | 27 ++-- .../actions/vrfv2plus/vrfv2plus_steps.go | 127 +++++++++--------- .../contracts/ethereum_contracts.go | 5 +- .../contracts/ethereum_ocr2vrf_contracts.go | 23 ++-- integration-tests/docker/test_env/cl_node.go | 5 +- .../docker/test_env/cl_node_cluster.go | 5 +- integration-tests/docker/test_env/test_env.go | 12 +- .../docker/test_env/test_env_builder.go | 8 +- integration-tests/go.mod | 4 +- integration-tests/go.sum | 4 +- integration-tests/load/functions/config.go | 13 +- integration-tests/load/functions/gateway.go | 20 +-- integration-tests/load/functions/setup.go | 24 ++-- integration-tests/load/vrfv2/config.go | 11 +- integration-tests/load/vrfv2/vu.go | 7 +- integration-tests/load/vrfv2plus/config.go | 11 +- integration-tests/reorg/reorg_confirmer.go | 8 +- integration-tests/smoke/ocr2_test.go | 49 ------- integration-tests/smoke/ocr2vrf_test.go | 5 +- integration-tests/smoke/vrfv2plus_test.go | 8 +- 25 files changed, 189 insertions(+), 231 deletions(-) diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index b45b8e83b95..bacf5a9dbfa 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -5,16 +5,16 @@ import ( "crypto/ecdsa" "encoding/json" "fmt" - "github.com/ethereum/go-ethereum/crypto" "math/big" "strings" "testing" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "go.uber.org/zap/zapcore" @@ -262,7 +262,7 @@ func TeardownSuite( ) error { l := logging.GetTestLogger(t) if err := testreporters.WriteTeardownLogs(t, env, optionalTestReporter, failingLogLevel); err != nil { - return errors.Wrap(err, "Error dumping environment logs, leaving environment running for manual retrieval") + return fmt.Errorf("Error dumping environment logs, leaving environment running for manual retrieval, err: %w", err) } // Delete all jobs to stop depleting the funds err := DeleteAllJobs(chainlinkNodes) @@ -330,16 +330,16 @@ func DeleteAllJobs(chainlinkNodes []*client.ChainlinkK8sClient) error { } jobs, _, err := node.ReadJobs() if err != nil { - return errors.Wrap(err, "error reading jobs from chainlink node") + return fmt.Errorf("error reading jobs from chainlink node, err: %w", err) } for _, maps := range jobs.Data { if _, ok := maps["id"]; !ok { - return errors.Errorf("error reading job id from chainlink node's jobs %+v", jobs.Data) + return fmt.Errorf("error reading job id from chainlink node's jobs %+v", jobs.Data) } id := maps["id"].(string) _, err := node.DeleteJob(id) if err != nil { - return errors.Wrap(err, "error deleting job from chainlink node") + return fmt.Errorf("error deleting job from chainlink node, err: %w", err) } } } @@ -350,7 +350,7 @@ func DeleteAllJobs(chainlinkNodes []*client.ChainlinkK8sClient) error { // all from a remote, k8s style environment func ReturnFunds(chainlinkNodes []*client.ChainlinkK8sClient, blockchainClient blockchain.EVMClient) error { if blockchainClient == nil { - return errors.New("blockchain client is nil, unable to return funds from chainlink nodes") + return fmt.Errorf("blockchain client is nil, unable to return funds from chainlink nodes") } log.Info().Msg("Attempting to return Chainlink node funds to default network wallets") if blockchainClient.NetworkSimulated() { @@ -416,7 +416,7 @@ func UpgradeChainlinkNodeVersions( nodes ...*client.ChainlinkK8sClient, ) error { if newImage == "" && newVersion == "" { - return errors.New("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") + return fmt.Errorf("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") } for _, node := range nodes { if err := node.UpgradeVersion(testEnvironment, newImage, newVersion); err != nil { @@ -455,7 +455,7 @@ func GenerateWallet() (common.Address, error) { publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { - return common.Address{}, errors.New("cannot assert type: publicKey is not of type *ecdsa.PublicKey") + return common.Address{}, fmt.Errorf("cannot assert type: publicKey is not of type *ecdsa.PublicKey") } return crypto.PubkeyToAddress(*publicKeyECDSA), nil } diff --git a/integration-tests/actions/actions_local.go b/integration-tests/actions/actions_local.go index b65bac43bb1..f5d2a9035f5 100644 --- a/integration-tests/actions/actions_local.go +++ b/integration-tests/actions/actions_local.go @@ -2,7 +2,8 @@ package actions import ( - "github.com/pkg/errors" + "fmt" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -13,7 +14,7 @@ func UpgradeChainlinkNodeVersionsLocal( nodes ...*test_env.ClNode, ) error { if newImage == "" && newVersion == "" { - return errors.New("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") + return fmt.Errorf("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") } for _, node := range nodes { if err := node.UpgradeVersion(node.NodeConfig, newImage, newVersion); err != nil { diff --git a/integration-tests/actions/automation_ocr_helpers_local.go b/integration-tests/actions/automation_ocr_helpers_local.go index ccc2eea99d8..f541594c4d2 100644 --- a/integration-tests/actions/automation_ocr_helpers_local.go +++ b/integration-tests/actions/automation_ocr_helpers_local.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" - "github.com/pkg/errors" "github.com/rs/zerolog" ocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -187,7 +186,7 @@ func CreateOCRKeeperJobsLocal( } else if registryVersion == ethereum.RegistryVersion_2_0 { contractVersion = "v2.0" } else { - return errors.New("v2.0 and v2.1 are the only supported versions") + return fmt.Errorf("v2.0 and v2.1 are the only supported versions") } bootstrapSpec := &client.OCR2TaskJobSpec{ diff --git a/integration-tests/actions/ocr_helpers_local.go b/integration-tests/actions/ocr_helpers_local.go index 8bb4e834794..5836ee7945c 100644 --- a/integration-tests/actions/ocr_helpers_local.go +++ b/integration-tests/actions/ocr_helpers_local.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" - "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" @@ -280,7 +279,7 @@ func TrackForwarderLocal( chainID := chainClient.GetChainID() _, _, err := node.TrackForwarder(chainID, authorizedForwarder) if err != nil { - return errors.Wrap(err, "failed to track forwarder") + return fmt.Errorf("failed to track forwarder, err: %w", err) } logger.Info().Str("NodeURL", node.Config.URL). Str("ForwarderAddress", authorizedForwarder.Hex()). @@ -305,7 +304,7 @@ func DeployOCRContractsForwarderFlowLocal( contracts.DefaultOffChainAggregatorOptions(), ) if err != nil { - return nil, errors.Wrap(err, "failed to deploy offchain aggregator") + return nil, fmt.Errorf("failed to deploy offchain aggregator, err: %w", err) } ocrInstances = append(ocrInstances, ocrInstance) err = client.WaitForEvents() @@ -329,7 +328,7 @@ func DeployOCRContractsForwarderFlowLocal( for _, ocrInstance := range ocrInstances { err := ocrInstance.SetPayees(transmitters, payees) if err != nil { - return nil, errors.Wrap(err, "failed to set OCR payees") + return nil, fmt.Errorf("failed to set OCR payees, err: %w", err) } if err := client.WaitForEvents(); err != nil { return nil, err @@ -348,7 +347,7 @@ func DeployOCRContractsForwarderFlowLocal( forwarderAddresses, ) if err != nil { - return nil, errors.Wrap(err, "failed to set on-chain config") + return nil, fmt.Errorf("failed to set on-chain config, err: %w", err) } if err = client.WaitForEvents(); err != nil { return nil, err diff --git a/integration-tests/actions/vrfv1/actions.go b/integration-tests/actions/vrfv1/actions.go index 68d3e584cee..f8d7190709f 100644 --- a/integration-tests/actions/vrfv1/actions.go +++ b/integration-tests/actions/vrfv1/actions.go @@ -1,7 +1,8 @@ package vrfv1 import ( - "github.com/pkg/errors" + "fmt" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) @@ -21,15 +22,15 @@ type Contracts struct { func DeployVRFContracts(cd contracts.ContractDeployer, bc blockchain.EVMClient, lt contracts.LinkToken) (*Contracts, error) { bhs, err := cd.DeployBlockhashStore() if err != nil { - return nil, errors.Wrap(err, ErrDeployBHSV1) + return nil, fmt.Errorf("%s, err %w", ErrDeployBHSV1, err) } coordinator, err := cd.DeployVRFCoordinator(lt.Address(), bhs.Address()) if err != nil { - return nil, errors.Wrap(err, ErrDeployVRFCootrinatorV1) + return nil, fmt.Errorf("%s, err %w", ErrDeployVRFCootrinatorV1, err) } consumer, err := cd.DeployVRFConsumer(lt.Address(), coordinator.Address()) if err != nil { - return nil, errors.Wrap(err, ErrDeployVRFConsumerV1) + return nil, fmt.Errorf("%s, err %w", ErrDeployVRFConsumerV1, err) } if err := bc.WaitForEvents(); err != nil { return nil, err diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index 24ac217a334..a832d020b0f 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -6,7 +6,6 @@ import ( "math/big" "github.com/google/uuid" - "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -43,15 +42,15 @@ func DeployVRFV2Contracts( ) (*VRFV2Contracts, error) { bhs, err := contractDeployer.DeployBlockhashStore() if err != nil { - return nil, errors.Wrap(err, ErrDeployBlockHashStore) + return nil, fmt.Errorf("%s, err %w", ErrDeployBlockHashStore, err) } coordinator, err := contractDeployer.DeployVRFCoordinatorV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) if err != nil { - return nil, errors.Wrap(err, ErrDeployCoordinator) + return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) } loadTestConsumer, err := contractDeployer.DeployVRFv2LoadTestConsumer(coordinator.Address()) if err != nil { - return nil, errors.Wrap(err, ErrAdvancedConsumer) + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) } err = chainClient.WaitForEvents() if err != nil { @@ -70,7 +69,7 @@ func CreateVRFV2Jobs( for _, chainlinkNode := range chainlinkNodes { vrfKey, err := chainlinkNode.MustCreateVRFKey() if err != nil { - return nil, errors.Wrap(err, ErrCreatingVRFv2Key) + return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Key, err) } pubKeyCompressed := vrfKey.Data.ID jobUUID := uuid.New() @@ -79,11 +78,11 @@ func CreateVRFV2Jobs( } ost, err := os.String() if err != nil { - return nil, errors.Wrap(err, ErrParseJob) + return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) } nativeTokenPrimaryKeyAddress, err := chainlinkNode.PrimaryEthAddress() if err != nil { - return nil, errors.Wrap(err, ErrNodePrimaryKey) + return nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } job, err := chainlinkNode.MustCreateJob(&client.VRFV2JobSpec{ Name: fmt.Sprintf("vrf-%s", jobUUID), @@ -97,15 +96,15 @@ func CreateVRFV2Jobs( BatchFulfillmentEnabled: false, }) if err != nil { - return nil, errors.Wrap(err, ErrCreatingVRFv2Job) + return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) } provingKey, err := VRFV2RegisterProvingKey(vrfKey, nativeTokenPrimaryKeyAddress, coordinator) if err != nil { - return nil, errors.Wrap(err, ErrCreatingProvingKey) + return nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKey, err) } keyHash, err := coordinator.HashOfKey(context.Background(), provingKey) if err != nil { - return nil, errors.Wrap(err, ErrCreatingProvingKeyHash) + return nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKeyHash, err) } ji := VRFV2JobInfo{ Job: job, @@ -125,14 +124,14 @@ func VRFV2RegisterProvingKey( ) (VRFV2EncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) if err != nil { - return VRFV2EncodedProvingKey{}, errors.Wrap(err, ErrEncodingProvingKey) + return VRFV2EncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( oracleAddress, provingKey, ) if err != nil { - return VRFV2EncodedProvingKey{}, errors.Wrap(err, ErrRegisterProvingKey) + return VRFV2EncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrRegisterProvingKey, err) } return provingKey, nil } @@ -140,11 +139,11 @@ func VRFV2RegisterProvingKey( func FundVRFCoordinatorV2Subscription(linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, chainClient blockchain.EVMClient, subscriptionID uint64, linkFundingAmount *big.Int) error { encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint64"}]`, subscriptionID) if err != nil { - return errors.Wrap(err, ErrABIEncodingFunding) + return fmt.Errorf("%s, err %w", ErrABIEncodingFunding, err) } _, err = linkToken.TransferAndCall(coordinator.Address(), big.NewInt(0).Mul(linkFundingAmount, big.NewInt(1e18)), encodedSubId) if err != nil { - return errors.Wrap(err, ErrSendingLinkToken) + return fmt.Errorf("%s, err %w", ErrSendingLinkToken, err) } return chainClient.WaitForEvents() } diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index e720116c210..e964623fb2e 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -3,17 +3,17 @@ package vrfv2plus import ( "context" "fmt" - "github.com/smartcontractkit/chainlink-testing-framework/utils" "math/big" "sync" "time" + "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" - "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions" @@ -71,19 +71,19 @@ func DeployVRFV2_5Contracts( ) (*VRFV2_5Contracts, error) { bhs, err := contractDeployer.DeployBlockhashStore() if err != nil { - return nil, errors.Wrap(err, ErrDeployBlockHashStore) + return nil, fmt.Errorf("%s, err %w", ErrDeployBlockHashStore, err) } err = chainClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } coordinator, err := contractDeployer.DeployVRFCoordinatorV2_5(bhs.Address()) if err != nil { - return nil, errors.Wrap(err, ErrDeployCoordinator) + return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) } err = chainClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } consumers, err := DeployVRFV2PlusConsumers(contractDeployer, coordinator, consumerContractsAmount) if err != nil { @@ -91,7 +91,7 @@ func DeployVRFV2_5Contracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } return &VRFV2_5Contracts{coordinator, bhs, consumers}, nil } @@ -107,11 +107,11 @@ func DeployVRFV2PlusDirectFundingContracts( vrfv2PlusWrapper, err := contractDeployer.DeployVRFV2PlusWrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) if err != nil { - return nil, errors.Wrap(err, ErrDeployWrapper) + return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) } err = chainClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } consumers, err := DeployVRFV2PlusWrapperConsumers(contractDeployer, linkTokenAddress, vrfv2PlusWrapper, consumerContractsAmount) @@ -120,7 +120,7 @@ func DeployVRFV2PlusDirectFundingContracts( } err = chainClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } return &VRFV2PlusWrapperContracts{vrfv2PlusWrapper, consumers}, nil } @@ -130,7 +130,7 @@ func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coord for i := 1; i <= consumerContractsAmount; i++ { loadTestConsumer, err := contractDeployer.DeployVRFv2PlusLoadTestConsumer(coordinator.Address()) if err != nil { - return nil, errors.Wrap(err, ErrAdvancedConsumer) + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) } consumers = append(consumers, loadTestConsumer) } @@ -142,7 +142,7 @@ func DeployVRFV2PlusWrapperConsumers(contractDeployer contracts.ContractDeployer for i := 1; i <= consumerContractsAmount; i++ { loadTestConsumer, err := contractDeployer.DeployVRFV2PlusWrapperLoadTestConsumer(linkTokenAddress, vrfV2PlusWrapper.Address()) if err != nil { - return nil, errors.Wrap(err, ErrAdvancedConsumer) + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) } consumers = append(consumers, loadTestConsumer) } @@ -163,7 +163,7 @@ func CreateVRFV2PlusJob( } ost, err := os.String() if err != nil { - return nil, errors.Wrap(err, ErrParseJob) + return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) } job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ @@ -178,7 +178,7 @@ func CreateVRFV2PlusJob( BatchFulfillmentEnabled: false, }) if err != nil { - return nil, errors.Wrap(err, ErrCreatingVRFv2PlusJob) + return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusJob, err) } return job, nil @@ -191,14 +191,14 @@ func VRFV2_5RegisterProvingKey( ) (VRFV2PlusEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) if err != nil { - return VRFV2PlusEncodedProvingKey{}, errors.Wrap(err, ErrEncodingProvingKey) + return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( oracleAddress, provingKey, ) if err != nil { - return VRFV2PlusEncodedProvingKey{}, errors.Wrap(err, ErrRegisterProvingKey) + return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrRegisterProvingKey, err) } return provingKey, nil } @@ -210,14 +210,14 @@ func VRFV2PlusUpgradedVersionRegisterProvingKey( ) (VRFV2PlusEncodedProvingKey, error) { provingKey, err := actions.EncodeOnChainVRFProvingKey(*vrfKey) if err != nil { - return VRFV2PlusEncodedProvingKey{}, errors.Wrap(err, ErrEncodingProvingKey) + return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrEncodingProvingKey, err) } err = coordinator.RegisterProvingKey( oracleAddress, provingKey, ) if err != nil { - return VRFV2PlusEncodedProvingKey{}, errors.Wrap(err, ErrRegisterProvingKey) + return VRFV2PlusEncodedProvingKey{}, fmt.Errorf("%s, err %w", ErrRegisterProvingKey, err) } return provingKey, nil } @@ -231,11 +231,11 @@ func FundVRFCoordinatorV2_5Subscription( ) error { encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint256"}]`, subscriptionID) if err != nil { - return errors.Wrap(err, ErrABIEncodingFunding) + return fmt.Errorf("%s, err %w", ErrABIEncodingFunding, err) } _, err = linkToken.TransferAndCall(coordinator.Address(), linkFundingAmountJuels, encodedSubId) if err != nil { - return errors.Wrap(err, ErrSendingLinkToken) + return fmt.Errorf("%s, err %w", ErrSendingLinkToken, err) } return chainClient.WaitForEvents() } @@ -255,7 +255,7 @@ func SetupVRFV2_5Environment( l.Info().Msg("Deploying VRFV2 Plus contracts") vrfv2_5Contracts, err := DeployVRFV2_5Contracts(env.ContractDeployer, env.EVMClient, numberOfConsumers) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrDeployVRFV2_5Contracts) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2_5Contracts, err) } l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") @@ -271,17 +271,17 @@ func SetupVRFV2_5Environment( }, ) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrSetVRFCoordinatorConfig) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrSetVRFCoordinatorConfig, err) } l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Setting Link and ETH/LINK feed") err = vrfv2_5Contracts.Coordinator.SetLINKAndLINKNativeFeed(linkToken.Address(), mockNativeLINKFeed.Address()) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrSetLinkNativeLinkFeed) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrSetLinkNativeLinkFeed, err) } err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Int("Number of Subs to create", numberOfSubToCreate).Msg("Creating and funding subscriptions, adding consumers") subIDs, err := CreateFundSubsAndAddConsumers( @@ -295,25 +295,25 @@ func SetupVRFV2_5Environment( l.Info().Str("Node URL", env.ClCluster.NodeAPIs()[0].URL()).Msg("Creating VRF Key on the Node") vrfKey, err := env.ClCluster.NodeAPIs()[0].MustCreateVRFKey() if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrCreatingVRFv2PlusKey) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusKey, err) } pubKeyCompressed := vrfKey.Data.ID l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Msg("Registering Proving Key") provingKey, err := VRFV2_5RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfv2_5Contracts.Coordinator) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrRegisteringProvingKey) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRegisteringProvingKey, err) } keyHash, err := vrfv2_5Contracts.Coordinator.HashOfKey(context.Background(), provingKey) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrCreatingProvingKeyHash) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKeyHash, err) } chainID := env.EVMClient.GetChainID() nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrNodePrimaryKey) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } l.Info().Msg("Creating VRFV2 Plus Job") @@ -326,7 +326,7 @@ func SetupVRFV2_5Environment( vrfv2PlusConfig.MinimumConfirmations, ) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrCreateVRFV2PlusJobs) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreateVRFV2PlusJobs, err) } // this part is here because VRFv2 can work with only a specific key @@ -334,7 +334,7 @@ func SetupVRFV2_5Environment( // Key = '...' addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrGetPrimaryKey) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) } nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, node.WithVRFv2EVMEstimator(addr), @@ -342,7 +342,7 @@ func SetupVRFV2_5Environment( l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) if err != nil { - return nil, nil, nil, errors.Wrap(err, ErrRestartCLNode) + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRestartCLNode, err) } vrfv2PlusKeyData := VRFV2PlusKeyData{ @@ -391,7 +391,7 @@ func CreateFundSubsAndAddConsumers( err = env.EVMClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } return subIDs, nil } @@ -409,7 +409,7 @@ func CreateSubsAndFund( } err = env.EVMClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, subs) if err != nil { @@ -443,7 +443,7 @@ func AddConsumersToSubs( for _, consumer := range consumers { err := coordinator.AddConsumer(subID, consumer.Address()) if err != nil { - return errors.Wrap(err, ErrAddConsumerToSub) + return fmt.Errorf("%s, err %w", ErrAddConsumerToSub, err) } } } @@ -475,7 +475,7 @@ func SetupVRFV2PlusWrapperEnvironment( err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } err = wrapperContracts.VRFV2PlusWrapper.SetConfig( vrfv2PlusConfig.WrapperGasOverhead, @@ -494,7 +494,7 @@ func SetupVRFV2PlusWrapperEnvironment( err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } //fund sub @@ -505,7 +505,7 @@ func SetupVRFV2PlusWrapperEnvironment( err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, []*big.Int{wrapperSubID}) @@ -523,7 +523,7 @@ func SetupVRFV2PlusWrapperEnvironment( } err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } //fund consumer with Eth @@ -533,21 +533,24 @@ func SetupVRFV2PlusWrapperEnvironment( } err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } return wrapperContracts, wrapperSubID, nil } func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { tx, err := coordinator.CreateSubscription() if err != nil { - return nil, errors.Wrap(err, ErrCreateVRFSubscription) + return nil, fmt.Errorf("%s, err %w", ErrCreateVRFSubscription, err) } err = env.EVMClient.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, ErrWaitTXsComplete) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } //SubscriptionsCreated Log should be emitted with the subscription ID subID := receipt.Logs[0].Topics[1].Big() @@ -555,7 +558,7 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts //verify that the subscription was created _, err = coordinator.FindSubscriptionID(subID) if err != nil { - return nil, errors.Wrap(err, ErrFindSubID) + return nil, fmt.Errorf("%s, err %w", ErrFindSubID, err) } return subID, nil @@ -564,11 +567,11 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) if err != nil { - return nil, nil, errors.Wrap(err, ErrLinkTotalBalance) + return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) } nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) if err != nil { - return nil, nil, errors.Wrap(err, ErrNativeTokenBalance) + return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) } return } @@ -576,11 +579,11 @@ func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2Pl func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) if err != nil { - return nil, nil, errors.Wrap(err, ErrLinkTotalBalance) + return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) } nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) if err != nil { - return nil, nil, errors.Wrap(err, ErrNativeTokenBalance) + return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) } return } @@ -600,18 +603,18 @@ func FundSubscriptions( amountWei, ) if err != nil { - return errors.Wrap(err, ErrFundSubWithNativeToken) + return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) } //Link Billing amountJuels := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { - return errors.Wrap(err, ErrFundSubWithLinkToken) + return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) } } err := env.EVMClient.WaitForEvents() if err != nil { - return errors.Wrap(err, ErrWaitTXsComplete) + return fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } return nil } @@ -638,7 +641,7 @@ func RequestRandomnessAndWaitForFulfillment( randomnessRequestCountPerRequest, ) if err != nil { - return nil, errors.Wrap(err, ErrRequestRandomness) + return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) } return WaitForRequestAndFulfillmentEvents( @@ -672,7 +675,7 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( vrfv2PlusConfig.RandomnessRequestCountPerRequest, ) if err != nil { - return nil, errors.Wrap(err, ErrRequestRandomness) + return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) } randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( @@ -682,7 +685,7 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( time.Minute*1, ) if err != nil { - return nil, errors.Wrap(err, ErrWaitRandomWordsRequestedEvent) + return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) } LogRandomnessRequestedEventUpgraded(l, coordinator, randomWordsRequestedEvent) @@ -693,7 +696,7 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( time.Minute*2, ) if err != nil { - return nil, errors.Wrap(err, ErrWaitRandomWordsFulfilledEvent) + return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsFulfilledEvent, err) } LogRandomWordsFulfilledEventUpgraded(l, coordinator, randomWordsFulfilledEvent) @@ -719,7 +722,7 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( vrfv2PlusConfig.RandomnessRequestCountPerRequest, ) if err != nil { - return nil, errors.Wrap(err, ErrRequestRandomnessDirectFundingNativePayment) + return nil, fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingNativePayment, err) } } else { _, err := consumer.RequestRandomness( @@ -729,12 +732,12 @@ func DirectFundingRequestRandomnessAndWaitForFulfillment( vrfv2PlusConfig.RandomnessRequestCountPerRequest, ) if err != nil { - return nil, errors.Wrap(err, ErrRequestRandomnessDirectFundingLinkPayment) + return nil, fmt.Errorf("%s, err %w", ErrRequestRandomnessDirectFundingLinkPayment, err) } } wrapperAddress, err := consumer.GetWrapper(context.Background()) if err != nil { - return nil, errors.Wrap(err, "error getting wrapper address") + return nil, fmt.Errorf("error getting wrapper address, err: %w", err) } return WaitForRequestAndFulfillmentEvents( wrapperAddress.String(), @@ -763,7 +766,7 @@ func WaitForRequestAndFulfillmentEvents( time.Minute*1, ) if err != nil { - return nil, errors.Wrap(err, ErrWaitRandomWordsRequestedEvent) + return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) } LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent, isNativeBilling) @@ -774,7 +777,7 @@ func WaitForRequestAndFulfillmentEvents( randomWordsFulfilledEventTimeout, ) if err != nil { - return nil, errors.Wrap(err, ErrWaitRandomWordsFulfilledEvent) + return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsFulfilledEvent, err) } LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent, isNativeBilling) @@ -817,7 +820,7 @@ func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadT func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator contracts.VRFCoordinatorV2_5, l zerolog.Logger) error { linkTotalBalance, err := coordinator.GetLinkTotalBalance(context.Background()) if err != nil { - return errors.Wrap(err, "Error getting LINK total balance") + return fmt.Errorf("Error getting LINK total balance, err: %w", err) } defaultWallet := client.GetDefaultWallet().Address() l.Info(). @@ -829,11 +832,11 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co linkTotalBalance, ) if err != nil { - return errors.Wrap(err, "Error withdrawing LINK from coordinator to default wallet") + return fmt.Errorf("Error withdrawing LINK from coordinator to default wallet, err: %w", err) } nativeTotalBalance, err := coordinator.GetNativeTokenTotalBalance(context.Background()) if err != nil { - return errors.Wrap(err, "Error getting NATIVE total balance") + return fmt.Errorf("Error getting NATIVE total balance, err: %w", err) } l.Info(). Str("Native Token amount", linkTotalBalance.String()). @@ -844,7 +847,7 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co nativeTotalBalance, ) if err != nil { - return errors.Wrap(err, "Error withdrawing NATIVE from coordinator to default wallet") + return fmt.Errorf("Error withdrawing NATIVE from coordinator to default wallet, err: %w", err) } return nil } diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index 5b3a93fe0c2..cde6e325f2a 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -13,7 +13,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -2162,11 +2161,11 @@ func (e *EthereumFunctionsRouter) CreateSubscriptionWithConsumer(consumer string topicOneInputs := abi.Arguments{fabi.Events["SubscriptionCreated"].Inputs[0]} topicOneHash := []common.Hash{r.Logs[0].Topics[1:][0]} if err := abi.ParseTopicsIntoMap(topicsMap, topicOneInputs, topicOneHash); err != nil { - return 0, errors.Wrap(err, "failed to decode topic value") + return 0, fmt.Errorf("failed to decode topic value, err: %w", err) } e.l.Info().Interface("NewTopicsDecoded", topicsMap).Send() if topicsMap["subscriptionId"] == 0 { - return 0, errors.New("failed to decode subscription ID after creation") + return 0, fmt.Errorf("failed to decode subscription ID after creation") } return topicsMap["subscriptionId"].(uint64), nil } diff --git a/integration-tests/contracts/ethereum_ocr2vrf_contracts.go b/integration-tests/contracts/ethereum_ocr2vrf_contracts.go index e8149b21251..cb52d1941a8 100644 --- a/integration-tests/contracts/ethereum_ocr2vrf_contracts.go +++ b/integration-tests/contracts/ethereum_ocr2vrf_contracts.go @@ -10,7 +10,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" @@ -230,7 +229,7 @@ func (dkgContract *EthereumDKG) WaitForTransmittedEvent(timeout time.Duration) ( case err = <-subscription.Err(): return nil, err case <-time.After(timeout): - return nil, errors.New("timeout waiting for DKGTransmitted event") + return nil, fmt.Errorf("timeout waiting for DKGTransmitted event") case transmittedEvent := <-transmittedEventsChannel: return transmittedEvent, nil } @@ -250,7 +249,7 @@ func (dkgContract *EthereumDKG) WaitForConfigSetEvent(timeout time.Duration) (*d case err = <-subscription.Err(): return nil, err case <-time.After(timeout): - return nil, errors.New("timeout waiting for DKGConfigSet event") + return nil, fmt.Errorf("timeout waiting for DKGConfigSet event") case configSetEvent := <-configSetEventsChannel: return configSetEvent, nil } @@ -451,7 +450,7 @@ func (consumer *EthereumVRFBeaconConsumer) RequestRandomness( ) (*types.Receipt, error) { opts, err := consumer.client.TransactionOpts(consumer.client.GetDefaultWallet()) if err != nil { - return nil, errors.Wrap(err, "TransactionOpts failed") + return nil, fmt.Errorf("TransactionOpts failed, err: %w", err) } tx, err := consumer.vrfBeaconConsumer.TestRequestRandomness( opts, @@ -460,20 +459,20 @@ func (consumer *EthereumVRFBeaconConsumer) RequestRandomness( confirmationDelayArg, ) if err != nil { - return nil, errors.Wrap(err, "TestRequestRandomness failed") + return nil, fmt.Errorf("TestRequestRandomness failed, err: %w", err) } err = consumer.client.ProcessTransaction(tx) if err != nil { - return nil, errors.Wrap(err, "ProcessTransaction failed") + return nil, fmt.Errorf("ProcessTransaction failed, err: %w", err) } err = consumer.client.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, "WaitForEvents failed") + return nil, fmt.Errorf("WaitForEvents failed, err: %w", err) } receipt, err := consumer.client.GetTxReceipt(tx.Hash()) if err != nil { - return nil, errors.Wrap(err, "GetTxReceipt failed") + return nil, fmt.Errorf("GetTxReceipt failed, err: %w", err) } log.Info().Interface("Sub ID", subID). Interface("Number of Words", numWords). @@ -526,20 +525,20 @@ func (consumer *EthereumVRFBeaconConsumer) RequestRandomnessFulfillment( arguments, ) if err != nil { - return nil, errors.Wrap(err, "TestRequestRandomnessFulfillment failed") + return nil, fmt.Errorf("TestRequestRandomnessFulfillment failed, err: %w", err) } err = consumer.client.ProcessTransaction(tx) if err != nil { - return nil, errors.Wrap(err, "ProcessTransaction failed") + return nil, fmt.Errorf("ProcessTransaction failed, err: %w", err) } err = consumer.client.WaitForEvents() if err != nil { - return nil, errors.Wrap(err, "WaitForEvents failed") + return nil, fmt.Errorf("WaitForEvents failed, err: %w", err) } receipt, err := consumer.client.GetTxReceipt(tx.Hash()) if err != nil { - return nil, errors.Wrap(err, "GetTxReceipt failed") + return nil, fmt.Errorf("GetTxReceipt failed, err: %w", err) } log.Info().Interface("Sub ID", subID). Interface("Number of Words", numWords). diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 4c40e641210..4de3d27d754 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -17,7 +17,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/pelletier/go-toml/v2" - "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" tc "github.com/testcontainers/testcontainers-go" @@ -282,7 +281,7 @@ func (n *ClNode) StartContainer() error { Logger: l, }) if err != nil { - return errors.Wrap(err, ErrStartCLNodeContainer) + return fmt.Errorf("%s err: %w", ErrStartCLNodeContainer, err) } if n.lw != nil { if err := n.lw.ConnectContainer(context.Background(), container, "cl-node", true); err != nil { @@ -314,7 +313,7 @@ func (n *ClNode) StartContainer() error { }, n.l) if err != nil { - return errors.Wrap(err, ErrConnectNodeClient) + return fmt.Errorf("%s err: %w", ErrConnectNodeClient, err) } clClient.Config.InternalIP = n.ContainerName n.Container = container diff --git a/integration-tests/docker/test_env/cl_node_cluster.go b/integration-tests/docker/test_env/cl_node_cluster.go index 5ae90bb982b..08122b5744d 100644 --- a/integration-tests/docker/test_env/cl_node_cluster.go +++ b/integration-tests/docker/test_env/cl_node_cluster.go @@ -1,8 +1,9 @@ package test_env import ( + "fmt" + "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink/integration-tests/client" @@ -61,7 +62,7 @@ func (c *ClCluster) NodeCSAKeys() ([]string, error) { for _, n := range c.Nodes { csaKeys, err := n.GetNodeCSAKeys() if err != nil { - return nil, errors.Wrap(err, ErrGetNodeCSAKeys) + return nil, fmt.Errorf("%s, err: %w", ErrGetNodeCSAKeys, err) } keys = append(keys, csaKeys.Data[0].ID) } diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index e067e46090d..a6495bed540 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -8,11 +8,11 @@ import ( "math/big" "os" "path/filepath" + "runtime/debug" "testing" "time" "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" tc "github.com/testcontainers/testcontainers-go" @@ -114,7 +114,7 @@ func (te *CLClusterTestEnv) StartPrivateChain() error { for _, chain := range te.PrivateChain { primaryNode := chain.GetPrimaryNode() if primaryNode == nil { - return errors.WithStack(fmt.Errorf("primary node is nil in PrivateChain interface")) + return fmt.Errorf("primary node is nil in PrivateChain interface, stack: %s", string(debug.Stack())) } err := primaryNode.Start() if err != nil { @@ -164,7 +164,7 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i func (te *CLClusterTestEnv) FundChainlinkNodes(amount *big.Float) error { for _, cl := range te.ClCluster.Nodes { if err := cl.Fund(te.EVMClient, amount); err != nil { - return errors.Wrap(err, ErrFundCLNode) + return fmt.Errorf("%s, err: %w", ErrFundCLNode, err) } time.Sleep(5 * time.Second) } @@ -181,10 +181,10 @@ func (te *CLClusterTestEnv) Terminate() error { func (te *CLClusterTestEnv) Cleanup() error { te.l.Info().Msg("Cleaning up test environment") if te.t == nil { - return errors.New("cannot cleanup test environment without a testing.T") + return fmt.Errorf("cannot cleanup test environment without a testing.T") } if te.ClCluster == nil || len(te.ClCluster.Nodes) == 0 { - return errors.New("chainlink nodes are nil, unable cleanup chainlink nodes") + return fmt.Errorf("chainlink nodes are nil, unable cleanup chainlink nodes") } // TODO: This is an imperfect and temporary solution, see TT-590 for a more sustainable solution @@ -196,7 +196,7 @@ func (te *CLClusterTestEnv) Cleanup() error { } if te.EVMClient == nil { - return errors.New("evm client is nil, unable to return funds from chainlink nodes during cleanup") + return fmt.Errorf("evm client is nil, unable to return funds from chainlink nodes during cleanup") } else if te.EVMClient.NetworkSimulated() { te.l.Info(). Str("Network Name", te.EVMClient.GetNetworkName()). diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index c07ea762623..9f64ab64c98 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -4,9 +4,9 @@ import ( "fmt" "math/big" "os" + "runtime/debug" "testing" - "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -230,7 +230,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { case CleanUpTypeNone: b.l.Warn().Msg("test environment won't be cleaned up") case "": - return b.te, errors.WithMessage(errors.New("explicit cleanup type must be set when building test environment"), "test environment builder failed") + return b.te, fmt.Errorf("test environment builder failed: %w", fmt.Errorf("explicit cleanup type must be set when building test environment")) } if b.nonDevGethNetworks != nil { @@ -243,14 +243,14 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { for i, n := range b.te.PrivateChain { primaryNode := n.GetPrimaryNode() if primaryNode == nil { - return b.te, errors.WithStack(fmt.Errorf("primary node is nil in PrivateChain interface")) + return b.te, fmt.Errorf("primary node is nil in PrivateChain interface, stack: %s", string(debug.Stack())) } nonDevNetworks = append(nonDevNetworks, *n.GetNetworkConfig()) nonDevNetworks[i].URLs = []string{primaryNode.GetInternalWsUrl()} nonDevNetworks[i].HTTPURLs = []string{primaryNode.GetInternalHttpUrl()} } if nonDevNetworks == nil { - return nil, errors.New("cannot create nodes with custom config without nonDevNetworks") + return nil, fmt.Errorf("cannot create nodes with custom config without nonDevNetworks") } err = b.te.StartClCluster(b.clNodeConfig, b.clNodesCount, b.secretsConfig) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index eb542651b56..2ac3c38a719 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -17,12 +17,11 @@ require ( github.com/manifoldco/promptui v0.9.0 github.com/onsi/gomega v1.27.8 github.com/pelletier/go-toml/v2 v2.1.0 - github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65 + github.com/smartcontractkit/chainlink-testing-framework v1.18.5 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 @@ -364,6 +363,7 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // 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/prometheus/alertmanager v0.25.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ecafa2706e2..ce5e51a5681 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65 h1:/iRhwYy5KFsaS9Zo1T64QxAd11HGZB5p/LHI5oVc4BU= -github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.5 h1:R0f13AUbon1ltHE/vudkyUnLRGaoeocIDVv+FsHZjno= +github.com/smartcontractkit/chainlink-testing-framework v1.18.5/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/load/functions/config.go b/integration-tests/load/functions/config.go index 5c622401aba..451d01a6c89 100644 --- a/integration-tests/load/functions/config.go +++ b/integration-tests/load/functions/config.go @@ -1,12 +1,13 @@ package loadfunctions import ( + "fmt" + "math/big" + "os" + "github.com/pelletier/go-toml/v2" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "math/big" - "os" ) const ( @@ -103,18 +104,18 @@ func ReadConfig() (*PerformanceConfig, error) { var cfg *PerformanceConfig d, err := os.ReadFile(DefaultConfigFilename) if err != nil { - return nil, errors.Wrap(err, ErrReadPerfConfig) + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) } err = toml.Unmarshal(d, &cfg) if err != nil { - return nil, errors.Wrap(err, ErrUnmarshalPerfConfig) + return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) } log.Debug().Interface("PerformanceConfig", cfg).Msg("Parsed performance config") mpk := os.Getenv("MUMBAI_KEYS") murls := os.Getenv("MUMBAI_URLS") snet := os.Getenv("SELECTED_NETWORKS") if mpk == "" || murls == "" || snet == "" { - return nil, errors.New( + return nil, fmt.Errorf( "ensure variables are set:\nMUMBAI_KEYS variable, private keys, comma separated\nSELECTED_NETWORKS=MUMBAI\nMUMBAI_URLS variable, websocket urls, comma separated", ) } else { diff --git a/integration-tests/load/functions/gateway.go b/integration-tests/load/functions/gateway.go index aefe4fbedc2..78b0f14cf18 100644 --- a/integration-tests/load/functions/gateway.go +++ b/integration-tests/load/functions/gateway.go @@ -8,16 +8,16 @@ import ( "encoding/hex" "encoding/json" "fmt" + "time" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/go-resty/resty/v2" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" "github.com/smartcontractkit/chainlink/v2/core/services/s4" "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" - "time" ) type RPCResponse struct { @@ -182,12 +182,12 @@ func EncryptS4Secrets(deployerPk *ecdsa.PrivateKey, tdh2Pk *tdh2easy.PublicKey, donKey = bytes.Join([][]byte{b, donKey}, nil) donPubKey, err := crypto.UnmarshalPubkey(donKey) if err != nil { - return "", errors.Wrap(err, "failed to unmarshal DON key") + return "", fmt.Errorf("failed to unmarshal DON key: %w", err) } eciesDONPubKey := ecies.ImportECDSAPublic(donPubKey) signature, err := deployerPk.Sign(rand.Reader, []byte(msgJSON), nil) if err != nil { - return "", errors.Wrap(err, "failed to sign the msg with Ethereum key") + return "", fmt.Errorf("failed to sign the msg with Ethereum key: %w", err) } signedSecrets, err := json.Marshal(struct { Signature []byte `json:"signature"` @@ -197,29 +197,29 @@ func EncryptS4Secrets(deployerPk *ecdsa.PrivateKey, tdh2Pk *tdh2easy.PublicKey, Message: msgJSON, }) if err != nil { - return "", errors.Wrap(err, "failed to marshal signed secrets") + return "", fmt.Errorf("failed to marshal signed secrets: %w", err) } ct, err := ecies.Encrypt(rand.Reader, eciesDONPubKey, signedSecrets, nil, nil) if err != nil { - return "", errors.Wrap(err, "failed to encrypt with DON key") + return "", fmt.Errorf("failed to encrypt with DON key: %w", err) } ct0xFormat, err := json.Marshal(map[string]interface{}{"0x0": base64.StdEncoding.EncodeToString(ct)}) if err != nil { - return "", errors.Wrap(err, "failed to marshal DON key encrypted format") + return "", fmt.Errorf("failed to marshal DON key encrypted format: %w", err) } ctTDH2Format, err := tdh2easy.Encrypt(tdh2Pk, ct0xFormat) if err != nil { - return "", errors.Wrap(err, "failed to encrypt with TDH2 public key") + return "", fmt.Errorf("failed to encrypt with TDH2 public key: %w", err) } tdh2Message, err := ctTDH2Format.Marshal() if err != nil { - return "", errors.Wrap(err, "failed to marshal TDH2 encrypted msg") + return "", fmt.Errorf("failed to marshal TDH2 encrypted msg: %w", err) } finalMsg, err := json.Marshal(map[string]interface{}{ "encryptedSecrets": "0x" + hex.EncodeToString(tdh2Message), }) if err != nil { - return "", errors.Wrap(err, "failed to marshal secrets msg") + return "", fmt.Errorf("failed to marshal secrets msg: %w", err) } return string(finalMsg), nil } diff --git a/integration-tests/load/functions/setup.go b/integration-tests/load/functions/setup.go index 5253c531eee..81bc660b35e 100644 --- a/integration-tests/load/functions/setup.go +++ b/integration-tests/load/functions/setup.go @@ -2,6 +2,7 @@ package loadfunctions import ( "crypto/ecdsa" + "fmt" "math/big" mrand "math/rand" "os" @@ -10,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/go-resty/resty/v2" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/networks" @@ -91,41 +91,41 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { log.Info().Msg("Creating new subscription") subID, err := router.CreateSubscriptionWithConsumer(loadTestClient.Address()) if err != nil { - return nil, errors.Wrap(err, "failed to create a new subscription") + return nil, fmt.Errorf("failed to create a new subscription: %w", err) } encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint64"}]`, subID) if err != nil { - return nil, errors.Wrap(err, "failed to encode subscription ID for funding") + return nil, fmt.Errorf("failed to encode subscription ID for funding: %w", err) } _, err = lt.TransferAndCall(router.Address(), big.NewInt(0).Mul(cfg.Common.Funding.SubFunds, big.NewInt(1e18)), encodedSubId) if err != nil { - return nil, errors.Wrap(err, "failed to transferAndCall router, LINK funding") + return nil, fmt.Errorf("failed to transferAndCall router, LINK funding: %w", err) } cfg.Common.SubscriptionID = subID } pKey, pubKey, err := parseEthereumPrivateKey(os.Getenv("MUMBAI_KEYS")) if err != nil { - return nil, errors.Wrap(err, "failed to load Ethereum private key") + return nil, fmt.Errorf("failed to load Ethereum private key: %w", err) } tpk, err := coord.GetThresholdPublicKey() if err != nil { - return nil, errors.Wrap(err, "failed to get Threshold public key") + return nil, fmt.Errorf("failed to get Threshold public key: %w", err) } log.Info().Hex("ThresholdPublicKeyBytesHex", tpk).Msg("Loaded coordinator keys") donPubKey, err := coord.GetDONPublicKey() if err != nil { - return nil, errors.Wrap(err, "failed to get DON public key") + return nil, fmt.Errorf("failed to get DON public key: %w", err) } log.Info().Hex("DONPublicKeyHex", donPubKey).Msg("Loaded DON key") tdh2pk, err := ParseTDH2Key(tpk) if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal tdh2 public key") + return nil, fmt.Errorf("failed to unmarshal tdh2 public key: %w", err) } var encryptedSecrets string if cfg.Common.Secrets != "" { encryptedSecrets, err = EncryptS4Secrets(pKey, tdh2pk, donPubKey, cfg.Common.Secrets) if err != nil { - return nil, errors.Wrap(err, "failed to generate tdh2 secrets") + return nil, fmt.Errorf("failed to generate tdh2 secrets: %w", err) } slotID, slotVersion, err := UploadS4Secrets(resty.New(), &S4SecretsCfg{ GatewayURL: cfg.Common.GatewayURL, @@ -139,7 +139,7 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { S4SetPayload: encryptedSecrets, }) if err != nil { - return nil, errors.Wrap(err, "failed to upload secrets to S4") + return nil, fmt.Errorf("failed to upload secrets to S4: %w", err) } cfg.Common.SecretsSlotID = slotID cfg.Common.SecretsVersionID = slotVersion @@ -168,13 +168,13 @@ func SetupLocalLoadTestEnv(cfg *PerformanceConfig) (*FunctionsTest, error) { func parseEthereumPrivateKey(pk string) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { pKey, err := crypto.HexToECDSA(pk) if err != nil { - return nil, nil, errors.Wrap(err, "failed to convert Ethereum key from hex") + return nil, nil, fmt.Errorf("failed to convert Ethereum key from hex: %w", err) } publicKey := pKey.Public() pubKey, ok := publicKey.(*ecdsa.PublicKey) if !ok { - return nil, nil, errors.Wrap(err, "failed to get public key from Ethereum private key") + return nil, nil, fmt.Errorf("failed to get public key from Ethereum private key: %w", err) } log.Info().Str("Address", crypto.PubkeyToAddress(*pubKey).Hex()).Msg("Parsed private key for address") return pKey, pubKey, nil diff --git a/integration-tests/load/vrfv2/config.go b/integration-tests/load/vrfv2/config.go index ee5f3ff80dd..0c62cc351b4 100644 --- a/integration-tests/load/vrfv2/config.go +++ b/integration-tests/load/vrfv2/config.go @@ -1,12 +1,13 @@ package loadvrfv2 import ( + "fmt" + "math/big" + "os" + "github.com/pelletier/go-toml/v2" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "math/big" - "os" ) const ( @@ -63,11 +64,11 @@ func ReadConfig() (*PerformanceConfig, error) { var cfg *PerformanceConfig d, err := os.ReadFile(DefaultConfigFilename) if err != nil { - return nil, errors.Wrap(err, ErrReadPerfConfig) + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) } err = toml.Unmarshal(d, &cfg) if err != nil { - return nil, errors.Wrap(err, ErrUnmarshalPerfConfig) + return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) } log.Debug().Interface("PerformanceConfig", cfg).Msg("Parsed performance config") return cfg, nil diff --git a/integration-tests/load/vrfv2/vu.go b/integration-tests/load/vrfv2/vu.go index df05a9168e2..4658388d400 100644 --- a/integration-tests/load/vrfv2/vu.go +++ b/integration-tests/load/vrfv2/vu.go @@ -1,13 +1,14 @@ package loadvrfv2 import ( - "github.com/pkg/errors" + "fmt" + "time" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/wasp" - "time" ) /* JobVolumeVU is a "virtual user" that creates a VRFv2 job and constantly requesting new randomness only for this job instance */ @@ -54,7 +55,7 @@ func (m *JobVolumeVU) Clone(_ *wasp.Generator) wasp.VirtualUser { func (m *JobVolumeVU) Setup(_ *wasp.Generator) error { jobs, err := vrfv2_actions.CreateVRFV2Jobs(m.nodes, m.contracts.Coordinator, m.bc, m.minIncomingConfirmations) if err != nil { - return errors.Wrap(err, "failed to create VRFv2 jobs in setup") + return fmt.Errorf("failed to create VRFv2 jobs in setup: %w", err) } m.jobs = jobs m.keyHash = jobs[0].KeyHash diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index a5439210c2d..cba3fdcde59 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -2,12 +2,13 @@ package loadvrfv2plus import ( "encoding/base64" + "fmt" + "os" + "github.com/pelletier/go-toml/v2" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "os" ) const ( @@ -95,18 +96,18 @@ func ReadConfig() (*PerformanceConfig, error) { if rawConfig == "" { d, err = os.ReadFile(DefaultConfigFilename) if err != nil { - return nil, errors.Wrap(err, ErrReadPerfConfig) + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) } } else { d, err = base64.StdEncoding.DecodeString(rawConfig) } err = toml.Unmarshal(d, &cfg) if err != nil { - return nil, errors.Wrap(err, ErrUnmarshalPerfConfig) + return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) } if cfg.Soak.RandomnessRequestCountPerRequest <= cfg.Soak.RandomnessRequestCountPerRequestDeviation { - return nil, errors.Wrap(err, ErrDeviationShouldBeLessThanOriginal) + return nil, fmt.Errorf("%s, err: %w", ErrDeviationShouldBeLessThanOriginal, err) } log.Debug().Interface("Config", cfg).Msg("Parsed config") diff --git a/integration-tests/reorg/reorg_confirmer.go b/integration-tests/reorg/reorg_confirmer.go index 885bed2ad45..2193131680a 100644 --- a/integration-tests/reorg/reorg_confirmer.go +++ b/integration-tests/reorg/reorg_confirmer.go @@ -2,13 +2,13 @@ package reorg import ( "context" + "fmt" "math/big" "sync" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" @@ -70,7 +70,7 @@ type ReorgController struct { // NewReorgController creates a type that can create reorg chaos and confirm reorg has happened func NewReorgController(cfg *ReorgConfig) (*ReorgController, error) { if len(cfg.Network.GetClients()) == 1 { - return nil, errors.New("need at least 3 nodes to re-org") + return nil, fmt.Errorf("need at least 3 nodes to re-org") } ctx, ctxCancel := context.WithTimeout(context.Background(), cfg.Timeout) rc := &ReorgController{ @@ -165,7 +165,7 @@ func (rc *ReorgController) VerifyReorgComplete() error { } } if rc.currentVerifiedBlocks+1 < rc.ReorgDepth { - return errors.New("Reorg depth has not met") + return fmt.Errorf("Reorg depth has not met") } return nil } @@ -217,7 +217,7 @@ func (rc *ReorgController) Wait() error { if rc.complete { return nil } - return errors.New("timeout waiting for reorg to complete") + return fmt.Errorf("timeout waiting for reorg to complete") } // forkNetwork stomp the network between target reorged node and the rest diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 1b33cdce769..a6dcdcd139d 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -5,24 +5,14 @@ import ( "fmt" "math/big" "net/http" - "strings" "testing" "time" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" - mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" @@ -102,42 +92,3 @@ func TestOCRv2Basic(t *testing.T) { roundData.Answer.Int64(), ) } - -func setupOCR2Test(t *testing.T, forwardersEnabled bool) ( - testEnvironment *environment.Environment, - testNetwork blockchain.EVMNetwork, -) { - testNetwork = networks.MustGetSelectedNetworksFromEnv()[0] - evmConfig := ethereum.New(nil) - if !testNetwork.Simulated { - evmConfig = ethereum.New(ðereum.Props{ - NetworkName: testNetwork.Name, - Simulated: testNetwork.Simulated, - WsURLs: testNetwork.URLs, - }) - } - - var toml string - if forwardersEnabled { - toml = client.AddNetworkDetailedConfig(config.BaseOCR2Config, config.ForwarderNetworkDetailConfig, testNetwork) - } else { - toml = client.AddNetworksConfig(config.BaseOCR2Config, testNetwork) - } - - chainlinkChart := chainlink.New(0, map[string]interface{}{ - "replicas": 6, - "toml": toml, - }) - - testEnvironment = environment.New(&environment.Config{ - NamespacePrefix: fmt.Sprintf("smoke-ocr2-%s", strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-")), - Test: t, - }). - AddHelm(mockservercfg.New(nil)). - AddHelm(mockserver.New(nil)). - AddHelm(evmConfig). - AddHelm(chainlinkChart) - err := testEnvironment.Run() - require.NoError(t, err, "Error running test environment") - return testEnvironment, testNetwork -} diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 0d6a77a1157..912c121d075 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -1,6 +1,7 @@ package smoke import ( + "context" "fmt" "math/big" "strings" @@ -80,7 +81,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(nil, requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(context.Background(), requestID, big.NewInt(int64(i))) require.NoError(t, err) l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") @@ -141,7 +142,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(nil, requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(context.Background(), requestID, big.NewInt(int64(i))) require.NoError(t, err, "Error getting Randomness result from Consumer Contract") l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness Fulfillment retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness Fulfillment retrieved from Consumer contract give an answer other than 0") diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index acd548c88e2..3510a1505a7 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -3,14 +3,14 @@ package smoke import ( "context" "fmt" - "github.com/smartcontractkit/chainlink/integration-tests/utils" "math/big" "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" - "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" @@ -271,6 +271,7 @@ func TestVRFv2Plus(t *testing.T) { subIDForCancelling := subIDsForCancelling[0] testWalletAddress, err := actions.GenerateWallet() + require.NoError(t, err) testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), testWalletAddress) require.NoError(t, err) @@ -638,7 +639,7 @@ func TestVRFv2PlusMigration(t *testing.T) { require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) _, err = vrfv2plus.VRFV2PlusUpgradedVersionRegisterProvingKey(vrfv2PlusData.VRFKey, vrfv2PlusData.PrimaryEthAddress, newCoordinator) - require.NoError(t, err, errors.Wrap(err, vrfv2plus.ErrRegisteringProvingKey)) + require.NoError(t, err, fmt.Errorf("%s, err: %w", vrfv2plus.ErrRegisteringProvingKey, err)) err = newCoordinator.SetConfig( vrfv2PlusConfig.MinimumConfirmations, @@ -651,6 +652,7 @@ func TestVRFv2PlusMigration(t *testing.T) { FulfillmentFlatFeeNativePPM: vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, }, ) + require.NoError(t, err) err = newCoordinator.SetLINKAndLINKNativeFeed(linkAddress.Address(), mockETHLinkFeedAddress.Address()) require.NoError(t, err, vrfv2plus.ErrSetLinkNativeLinkFeed) From 8c81f61f36fa9aeddde43b1c139a20c6915a3121 Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 7 Nov 2023 12:23:45 -0700 Subject: [PATCH 098/271] [TT-685] Enforce CTF version to be tag in the form v1.2.3 (#11209) --- .github/actions/build-test-image/action.yml | 9 ++++++++- .github/workflows/integration-tests.yml | 14 +++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index c4b39b4d7af..683ce912ec5 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -30,6 +30,13 @@ inputs: runs: using: composite steps: + - name: Get CTF Version + id: version + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + with: + go-project-path: ./integration-tests + module-name: github.com/smartcontractkit/chainlink-testing-framework + enforce-semantic-tag: "true" # it has to be in the form of v1.2.3 or the image won't exist - name: Check if image exists id: check-image uses: smartcontractkit/chainlink-github-actions/docker/image-exists@00c6214deb10a3f374c6d3430c32c5202015d463 # v2.2.12 @@ -48,7 +55,7 @@ runs: file: ./integration-tests/test.Dockerfile build-args: | BASE_IMAGE=${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image - IMAGE_VERSION=v0.38.2 + IMAGE_VERSION=${{ steps.version.output.version }} SUITES="${{ inputs.suites }}" AWS_REGION: ${{ inputs.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 125ddb3f4f3..382e9cbbcf8 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -35,6 +35,18 @@ env: MOD_CACHE_VERSION: 2 jobs: + enforce-ctf-version: + name: Enforce CTF Version + runs-on: ubuntu-latest + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Enforce CTF Version + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + with: + go-project-path: ./integration-tests + module-name: github.com/smartcontractkit/chainlink-testing-framework + enforce-semantic-tag: "true" changes: environment: integration name: Check Paths That Require Tests To Run @@ -81,7 +93,7 @@ jobs: tag-suffix: -plugins name: Build Chainlink Image ${{ matrix.image.name }} runs-on: ubuntu20.04-16cores-64GB - needs: [changes] + needs: [changes, enforce-ctf-version] steps: - name: Collect Metrics if: needs.changes.outputs.src == 'true' From 5af9bca45861bc61a1375cf0770570341c9a7a88 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 7 Nov 2023 14:38:42 -0500 Subject: [PATCH 099/271] [TT-524] Separate Live Testnet Tests (#11196) * Separate Live Testnet Tests * \n * Remove typo * Extrapolates building Chainlink image --- .../actions/build-chainlink-image/action.yml | 48 +++ .github/workflows/integration-tests.yml | 243 +------------ .github/workflows/live-testnet-tests.yml | 342 ++++++++++++++++++ 3 files changed, 399 insertions(+), 234 deletions(-) create mode 100644 .github/actions/build-chainlink-image/action.yml create mode 100644 .github/workflows/live-testnet-tests.yml diff --git a/.github/actions/build-chainlink-image/action.yml b/.github/actions/build-chainlink-image/action.yml new file mode 100644 index 00000000000..5041d9d1db1 --- /dev/null +++ b/.github/actions/build-chainlink-image/action.yml @@ -0,0 +1,48 @@ +name: Build Chainlink Image +description: A composite action that allows building and publishing the Chainlink image for integration testing + +inputs: + tag_suffix: + description: The suffix to append to the image tag (usually blank or "-plugins") + default: "" + dockerfile: + description: The path to the Dockerfile to use (usually core/chainlink.Dockerfile or plugins/chainlink.Dockerfile) + default: core/chainlink.Dockerfile + git_commit_sha: + description: The git commit sha to use for the image tag + default: ${{ github.sha }} + GRAFANA_CLOUD_BASIC_AUTH: + description: "grafana cloud basic auth" + GRAFANA_CLOUD_HOST: + description: "grafana cloud hostname" + AWS_REGION: + description: "AWS region to use for ECR" + AWS_ROLE_TO_ASSUME: + description: "AWS role to assume for ECR" + +runs: + using: composite + steps: + - name: Check if image exists + id: check-image + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + with: + repository: chainlink + tag: ${{ inputs.git_commit_sha }}${{ inputs.tag_suffix }} + AWS_REGION: ${{ inputs.AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ inputs.AWS_ROLE_TO_ASSUME }} + - name: Build Image + if: steps.check-image.outputs.exists == 'false' && needs.changes.outputs.src == 'true' + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + with: + cl_repo: smartcontractkit/chainlink + cl_ref: ${{ inputs.git_commit_sha }} + cl_dockerfile: ${{ inputs.dockerfile }} + push_tag: ${{ env.CHAINLINK_IMAGE }}:${{ inputs.git_commit_sha }}${{ inputs.tag_suffix }} + QA_AWS_REGION: ${{ inputs.AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ inputs.AWS_ROLE_TO_ASSUME }} + - name: Print Chainlink Image Built + shell: sh + run: | + echo "### Chainlink node image tag used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 382e9cbbcf8..4c617a00c43 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -2,23 +2,10 @@ name: Integration Tests on: merge_group: pull_request: - schedule: - - cron: "0 0 * * *" - # - cron: "0 * * * *" # DEBUG: Run every hour to nail down flakes push: tags: - "*" workflow_dispatch: - inputs: - simulatedNetwork: - description: "Run Simulated Network Tests" - required: false - type: boolean - default: true - liveNetwork: - description: "Run Live Network Tests" - required: false - type: boolean # Only run 1 of this workflow at a time per PR concurrency: @@ -74,9 +61,9 @@ jobs: hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} this-job-name: Check Paths That Require Tests To Run continue-on-error: true - outputs: src: ${{ steps.changes.outputs.src }} + build-chainlink: environment: integration permissions: @@ -108,30 +95,16 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - - name: Check if image exists - if: needs.changes.outputs.src == 'true' - id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 - with: - repository: chainlink - tag: ${{ github.sha }}${{ matrix.image.tag-suffix }} + - name: Build Chainlink Image + uses: ./.github/actions/build-chainlink-image + with: + tag_suffix: ${{ matrix.image.tag-suffix }} + dockerfile: ${{ matrix.image.dockerfile }} + git_commit_sha: ${{ github.sha }} + GRAFANA_CLOUD_BASIC_AUTH: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - - name: Build Image - if: steps.check-image.outputs.exists == 'false' && needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 - with: - cl_repo: smartcontractkit/chainlink - cl_ref: ${{ github.sha }} - cl_dockerfile: ${{ matrix.image.dockerfile }} - push_tag: ${{ env.CHAINLINK_IMAGE }}:${{ github.sha }}${{ matrix.image.tag-suffix }} - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - - name: Print Chainlink Image Built - if: needs.changes.outputs.src == 'true' - run: | - echo "### Chainlink node image tag used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY build-test-image: if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'schedule' || contains(join(github.event.pull_request.labels.*.name, ' '), 'build-test-image') @@ -908,201 +881,3 @@ jobs: matrix-aggregator-status: ${{ needs.solana-smoke-tests-matrix.result }} continue-on-error: true ### End Solana Section - - ### Start Live Testnet Section - - testnet-smoke-tests-matrix: - if: ${{ github.event_name == 'schedule' || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) || (github.event_name == 'workflow_dispatch' && inputs.liveNetwork) }} ## Only run live tests on new tags, schedule, or on manual request - environment: integration - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - needs: [build-chainlink] - env: - SELECTED_NETWORKS: ${{ matrix.testnet }} - CHAINLINK_COMMIT_SHA: ${{ github.sha }} - CHAINLINK_ENV_USER: ${{ github.actor }} - TEST_LOG_LEVEL: debug - EVM_KEYS: ${{ secrets.QA_EVM_KEYS }} - - OPTIMISM_GOERLI_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_URLS }} - OPTIMISM_GOERLI_HTTP_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_HTTP_URLS }} - - ARBITRUM_GOERLI_URLS: ${{ secrets.QA_ARBITRUM_GOERLI_URLS }} - ARBITRUM_GOERLI_HTTP_URLS: ${{ secrets.QA_ARBITRUM_GOERLI_HTTP_URLS }} - strategy: - fail-fast: false - matrix: - # NOTE: If changing this matrix, make sure to update the matrix in the testnet-smoke-tests-notify job to be the same - # otherwise reporting will be broken. Getting a single matrix for multiple jobs is a pain - # https://github.com/orgs/community/discussions/26284#discussioncomment-3251198 - testnet: [OPTIMISM_GOERLI, ARBITRUM_GOERLI] - name: Live Testnet Smoke Tests ${{ matrix.testnet }} - runs-on: ubuntu-latest - steps: - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - ## Only run OCR smoke test for now - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-ocr-evm-${{ matrix.testnet }} # TODO: Only for OCR for now - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/ocr_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt - test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./integration-tests/smoke/logs - publish_check_name: ${{ matrix.testnet }} OCR Smoke Test Results - token: ${{ secrets.GITHUB_TOKEN }} - go_mod_path: ./integration-tests/go.mod - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: "true" - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - - - 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: Live Testnet Smoke Tests ${{ matrix.testnet }} - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - continue-on-error: true - - testnet-smoke-tests-notify: - name: Live Testnet Start Slack Thread - if: ${{ always() && needs.testnet-smoke-tests-matrix.result != 'skipped' && needs.testnet-smoke-tests-matrix.result != 'cancelled' && github.event_name != 'workflow_dispatch' }} - environment: integration - outputs: - thread_ts: ${{ steps.slack.outputs.thread_ts }} - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - runs-on: ubuntu-latest - needs: testnet-smoke-tests-matrix - steps: - - name: Debug Result - run: echo ${{needs.testnet-smoke-tests-matrix.result}} - - name: Main Slack Notification - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 - id: slack - with: - channel-id: ${{ secrets.QA_SLACK_CHANNEL }} - payload: | - { - "attachments": [ - { - "color": "${{ needs.testnet-smoke-tests-matrix.result == 'success' && '#2E7D32' || '#C62828' }}", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "Live Smoke Test Results ${{ needs.testnet-smoke-tests-matrix.result == 'success' && ':white_check_mark:' || ':x:'}}", - "emoji": true - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "<${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}|${{ github.ref_name }}> | <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}> | <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Run>" - } - } - ] - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} - - testnet-smoke-tests-results: - name: Post Live Testnet Smoke Test Results - if: ${{ always() && needs.testnet-smoke-tests-matrix.result != 'skipped' && needs.testnet-smoke-tests-matrix.result != 'cancelled' }} - environment: integration - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - runs-on: ubuntu-latest - needs: testnet-smoke-tests-notify - strategy: - fail-fast: false - matrix: - # NOTE: If changing this matrix, make sure to update the matrix in the testnet-smoke-tests-matrix job to be the same - # otherwise reporting will be broken. Getting a single matrix for multiple jobs is a pain - # https://github.com/orgs/community/discussions/26284#discussioncomment-3251198 - testnet: [OPTIMISM_GOERLI, ARBITRUM_GOERLI] - steps: - - name: Get Results - id: test-results - run: | - echo "Querying test results" - - echo "status=$(curl \ - -H "Authorization: Bearer ${{ github.token }}" \ - 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ - | jq -r '.jobs[] | select(.name == "Live Testnet Smoke Tests ${{ matrix.testnet}}").steps[] | select(.name == "Run Tests").conclusion')" >> $GITHUB_OUTPUT - - echo "status=$(curl \ - -H "Authorization: Bearer ${{ github.token }}" \ - 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ - | jq -r '.jobs[] | select(.name == "Live Testnet Smoke Tests ${{ matrix.testnet}}").steps[] | select(.name == "Run Tests").conclusion')" - echo "thread_ts=${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}" - - - name: Test Details - uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 - with: - channel-id: ${{ secrets.QA_SLACK_CHANNEL }} - payload: | - { - "thread_ts": "${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}", - "attachments": [ - { - "color": "${{ steps.test-results.outputs.status == 'success' && '#2E7D32' || '#C62828' }}", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "${{ matrix.testnet }} Smoke Test Results ${{ steps.test-results.outputs.status == 'success' && ':white_check_mark:' || ':x:'}}", - "emoji": true - } - }, - { - "type": "divider" - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "OCR ${{ steps.test-results.outputs.status == 'success' && ':white_check_mark:' || ':x:'}}" - } - } - ] - } - ] - } - env: - SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} - - ### End Live Testnet Section diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml new file mode 100644 index 00000000000..23e9b3c04cf --- /dev/null +++ b/.github/workflows/live-testnet-tests.yml @@ -0,0 +1,342 @@ +name: Live Testnet Tests +on: + schedule: + - cron: "0 0 * * *" # Run nightly + push: + tags: + - "*" + workflow_dispatch: + +env: + CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink + INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com + MOD_CACHE_VERSION: 2 + + CHAINLINK_COMMIT_SHA: ${{ github.sha }} + CHAINLINK_ENV_USER: ${{ github.actor }} + TEST_LOG_LEVEL: debug + EVM_KEYS: ${{ secrets.QA_EVM_KEYS }} + + OPTIMISM_GOERLI_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_URLS }} + OPTIMISM_GOERLI_HTTP_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_HTTP_URLS }} + + ARBITRUM_GOERLI_URLS: ${{ secrets.QA_ARBITRUM_GOERLI_URLS }} + ARBITRUM_GOERLI_HTTP_URLS: ${{ secrets.QA_ARBITRUM_GOERLI_HTTP_URLS }} + +jobs: + build-chainlink: + environment: integration + permissions: + id-token: write + contents: read + name: Build Chainlink Image + runs-on: ubuntu20.04-16cores-64GB + steps: + - name: Collect Metrics + 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: Build Chainlink Image + continue-on-error: true + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Build Chainlink Image + uses: ./.github/actions/build-chainlink-image + with: + tag_suffix: "" + dockerfile: core/chainlink.Dockerfile + git_commit_sha: ${{ github.sha }} + GRAFANA_CLOUD_BASIC_AUTH: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} + AWS_REGION: ${{ secrets.QA_AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + + + sepolia-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink] + env: + SELECTED_NETWORKS: SEPOLIA + strategy: + fail-fast: false + matrix: + product: [ocr, automation] + name: Sepolia ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/${{ matrix.product }}_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./integration-tests/smoke/logs + publish_check_name: Seplia ${{ matrix.product }} Smoke Test Results + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - 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: Sepolia ${{ matrix.product }} Tests + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true + + optimism-goerli-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink] + env: + SELECTED_NETWORKS: OPTIMISM_GOERLI + strategy: + fail-fast: false + matrix: + product: [ocr, automation] + name: Optimism Goerli ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-goerli + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/${{ matrix.product }}_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./integration-tests/smoke/logs + publish_check_name: Seplia ${{ matrix.product }} Smoke Test Results + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - 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: Optimism Goerli ${{ matrix.product }} Tests + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true + + arbitrum-goerli-smoke-tests: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + needs: [build-chainlink] + env: + SELECTED_NETWORKS: ARBITRUM_GOERLI + strategy: + fail-fast: false + matrix: + product: [ocr, automation] + name: Arbitrum Goerli ${{ matrix.product }} Tests + runs-on: ubuntu-latest + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + env: + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-goerli + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/${{ matrix.product }}_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ env.CHAINLINK_IMAGE }} + cl_image_tag: ${{ github.sha }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + artifacts_location: ./integration-tests/smoke/logs + publish_check_name: Arbitrum Goerli ${{ matrix.product }} Smoke Test Results + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - 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: Arbitrum Goerli ${{ matrix.product }} Tests + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true + + testnet-smoke-tests-notify: + name: Start Slack Thread + if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} + environment: integration + outputs: + thread_ts: ${{ steps.slack.outputs.thread_ts }} + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + runs-on: ubuntu-latest + needs: [sepolia-smoke-tests, optimism-goerli-smoke-tests, arbitrum-goerli-smoke-tests] + steps: + - name: Debug Result + run: echo ${{needs.*.result}} + - name: Main Slack Notification + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + id: slack + with: + channel-id: ${{ secrets.QA_SLACK_CHANNEL }} + payload: | + { + "attachments": [ + { + "color": "${{ needs.*.result == 'success' && '#2E7D32' || '#C62828' }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "Live Smoke Test Results ${{ needs.*.result == 'success' && ':white_check_mark:' || ':x:'}}", + "emoji": true + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "<${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}|${{ github.ref_name }}> | <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}> | <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Run>" + } + } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + + testnet-smoke-tests-results: + name: Post Test Results + if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + runs-on: ubuntu-latest + needs: testnet-smoke-tests-notify + strategy: + fail-fast: false + matrix: + testnet: [sepolia, optimism-goerli, arbitrum-goerli] + steps: + - name: Get Results + id: test-results + run: | + echo "Querying test results" + + echo "status=$(curl \ + -H "Authorization: Bearer ${{ github.token }}" \ + 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ + | jq -r '.jobs[] | select(.name == "Live Testnet Smoke Tests ${{ matrix.testnet }}-smoke-tests").steps[] | select(.name == "Run Tests").conclusion')" >> $GITHUB_OUTPUT + + echo "status=$(curl \ + -H "Authorization: Bearer ${{ github.token }}" \ + 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ + | jq -r '.jobs[] | select(.name == "Live Testnet Smoke Tests ${{ matrix.testnet }}-smoke-tests"").steps[] | select(.name == "Run Tests").conclusion')" + echo "thread_ts=${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}" + + - name: Test Details + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + with: + channel-id: ${{ secrets.QA_SLACK_CHANNEL }} + payload: | + { + "thread_ts": "${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}", + "attachments": [ + { + "color": "${{ steps.test-results.outputs.status == 'success' && '#2E7D32' || '#C62828' }}", + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "${{ matrix.testnet }} Smoke Test Results ${{ steps.test-results.outputs.status == 'success' && ':white_check_mark:' || ':x:'}}", + "emoji": true + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "OCR ${{ steps.test-results.outputs.status == 'success' && ':white_check_mark:' || ':x:'}}" + } + } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} From 647ce13cfac9c3bdd7a8f86636dde775f321a22b Mon Sep 17 00:00:00 2001 From: frank zhu Date: Tue, 7 Nov 2023 13:40:22 -0600 Subject: [PATCH 100/271] add lint and update tests for solidity foundry gha (#11184) * add lint and update tests for solidity foundry gha * fix typo * fix typo * refactor conditional into individual steps * remove if statement for checkout repo and add comment --- .github/workflows/solidity-foundry.yml | 13 +++++++++---- .github/workflows/solidity.yml | 8 +++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index 19c879b09ef..7c9df796171 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -34,11 +34,12 @@ jobs: matrix: product: [vrf, automation, llo-feeds, functions, shared] needs: [changes] - if: needs.changes.outputs.changes == 'true' - name: Tests + name: Foundry Tests ${{ matrix.product }} ${{ fromJSON('["(skipped)", ""]')[needs.changes.outputs.changes == 'true'] }} # See https://github.com/foundry-rs/foundry/issues/3827 runs-on: ubuntu-22.04 + # The if statements for steps after checkout repo is workaround for + # passing required check for PRs that don't have filtered changes. steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 @@ -49,15 +50,18 @@ jobs: # and not native Foundry. This is to make sure the dependencies # stay in sync. - name: Setup NodeJS + if: needs.changes.outputs.changes == 'true' uses: ./.github/actions/setup-nodejs - name: Install Foundry + if: needs.changes.outputs.changes == 'true' uses: foundry-rs/foundry-toolchain@v1 with: # Has to match the `make foundry` version. version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b - name: Run Forge build + if: needs.changes.outputs.changes == 'true' run: | forge --version forge build @@ -67,6 +71,7 @@ jobs: FOUNDRY_PROFILE: ${{ matrix.product }} - name: Run Forge tests + if: needs.changes.outputs.changes == 'true' run: | forge test -vvv id: test @@ -75,7 +80,7 @@ jobs: FOUNDRY_PROFILE: ${{ matrix.product }} - name: Run Forge snapshot - if: ${{ !contains(fromJson('["vrf"]'), matrix.product) && !contains(fromJson('["automation"]'), matrix.product) }} + if: ${{ !contains(fromJson('["vrf"]'), matrix.product) && !contains(fromJson('["automation"]'), matrix.product) && needs.changes.outputs.changes == 'true' }} run: | forge snapshot --nmt "testFuzz_\w{1,}?" --check gas-snapshots/${{ matrix.product }}.gas-snapshot id: snapshot @@ -84,7 +89,7 @@ jobs: FOUNDRY_PROFILE: ${{ matrix.product }} - name: Collect Metrics - if: always() + if: needs.changes.outputs.changes == 'true' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 782dc93a0f5..069d9de45ab 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -24,6 +24,7 @@ jobs: src: - 'contracts/**/*' - '.github/workflows/solidity.yml' + - '.github/workflows/solidity-foundry.yml' prepublish-test: needs: [changes] @@ -91,24 +92,29 @@ jobs: this-job-name: Native Compilation continue-on-error: true + # The if statements for steps after checkout repo is a workaround for + # passing required check for PRs that don't have filtered changes. lint: defaults: run: working-directory: contracts needs: [changes] - if: needs.changes.outputs.changes == 'true' name: Lint ${{ fromJSON('["(skipped)", ""]')[needs.changes.outputs.changes == 'true'] }} runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup NodeJS + if: needs.changes.outputs.changes == 'true' uses: ./.github/actions/setup-nodejs - name: Run pnpm lint + if: needs.changes.outputs.changes == 'true' run: pnpm lint - name: Run solhint + if: needs.changes.outputs.changes == 'true' run: pnpm solhint - name: Collect Metrics + if: needs.changes.outputs.changes == 'true' id: collect-gha-metrics uses: smartcontractkit/push-gha-metrics-action@d1618b772a97fd87e6505de97b872ee0b1f1729a # v2.0.2 with: From 44f6d388d2c85cbb199f9550c7814528a6fa412b Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 7 Nov 2023 16:37:50 -0500 Subject: [PATCH 101/271] Removes Old Needs Check (#11220) * Removes old Needs Check * Properly gate action --- .github/actions/build-chainlink-image/action.yml | 2 +- .github/workflows/integration-tests.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/build-chainlink-image/action.yml b/.github/actions/build-chainlink-image/action.yml index 5041d9d1db1..ac29a3d7b8d 100644 --- a/.github/actions/build-chainlink-image/action.yml +++ b/.github/actions/build-chainlink-image/action.yml @@ -32,7 +32,7 @@ runs: AWS_REGION: ${{ inputs.AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ inputs.AWS_ROLE_TO_ASSUME }} - name: Build Image - if: steps.check-image.outputs.exists == 'false' && needs.changes.outputs.src == 'true' + if: steps.check-image.outputs.exists == 'false' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 with: cl_repo: smartcontractkit/chainlink diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 4c617a00c43..ba66a53ab62 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -96,6 +96,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Build Chainlink Image + if: needs.changes.outputs.src == 'true' uses: ./.github/actions/build-chainlink-image with: tag_suffix: ${{ matrix.image.tag-suffix }} From 33b9d2a2e30087dea3fdb05d46c45be817ac6679 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Wed, 8 Nov 2023 13:12:38 +0400 Subject: [PATCH 102/271] fix build-test-image action (#11225) --- .github/actions/build-test-image/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index 683ce912ec5..a241f51d920 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -55,7 +55,7 @@ runs: file: ./integration-tests/test.Dockerfile build-args: | BASE_IMAGE=${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image - IMAGE_VERSION=${{ steps.version.output.version }} + IMAGE_VERSION=${{ steps.version.outputs.version }} SUITES="${{ inputs.suites }}" AWS_REGION: ${{ inputs.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} From ea27753bc31b7454915337984773d52528c6bd55 Mon Sep 17 00:00:00 2001 From: David Cauchi <13139524+davidcauchi@users.noreply.github.com> Date: Wed, 8 Nov 2023 10:55:38 +0100 Subject: [PATCH 103/271] Add baseFeeWei to logs (#11204) --- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- core/chains/evm/gas/block_history_estimator.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index b6ccad22467..4ea10cd4823 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -20,7 +20,7 @@ on: - "BSC_TESTNET" - "SCROLL_SEPOLIA" - "SCROLL_MAINNET" - - "MUMBAI" + - "POLYGON_MUMBAI" - "POLYGON_MAINNET" - "LINEA_GOERLI" - "LINEA_MAINNET" diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 42c8f051535..80ae19f109f 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -163,7 +163,7 @@ func (b *BlockHistoryEstimator) setLatest(head *evmtypes.Head) { if baseFee := head.BaseFeePerGas; baseFee != nil { promBlockHistoryEstimatorCurrentBaseFee.WithLabelValues(b.chainID.String()).Set(float64(baseFee.Int64())) } - b.logger.Debugw("Set latest block", "blockNum", head.Number, "blockHash", head.Hash, "baseFee", head.BaseFeePerGas) + b.logger.Debugw("Set latest block", "blockNum", head.Number, "blockHash", head.Hash, "baseFee", head.BaseFeePerGas, "baseFeeWei", head.BaseFeePerGas.ToInt()) b.latestMu.Lock() defer b.latestMu.Unlock() b.latest = head From b5191f1a8cf391e80a2960b65170177e439beed9 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Wed, 8 Nov 2023 14:23:41 +0100 Subject: [PATCH 104/271] BCF-2749 add tests for ocr enhancedTelemetry service creation (#11206) * Add Enhanced telemetry is disabled log to newServicesMercury delegate * Add test to check if eaTelemetry service was created in ocr servicseForSpec based on ocr spec flag --- core/services/job/runner_integration_test.go | 68 ++++++++++++++++++++ core/services/ocr2/delegate.go | 2 + 2 files changed, 70 insertions(+) diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index 7e8ed5e87f2..deb4bff6b08 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -528,6 +528,74 @@ answer1 [type=median index=0]; require.NoError(t, err) }) + t.Run("test enhanced telemetry service creation", func(t *testing.T) { + testCases := []struct { + jbCaptureEATelemetry bool + specCaptureEATelemetry bool + expected bool + }{{false, false, false}, + {true, false, false}, + {false, true, true}, + {true, true, true}, + } + + for _, tc := range testCases { + + config = configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { + c.P2P.V1.Enabled = ptr(true) + c.OCR.CaptureEATelemetry = ptr(tc.specCaptureEATelemetry) + }) + + relayExtenders = evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, Client: ethClient, GeneralConfig: config, KeyStore: ethKeyStore}) + legacyChains = evmrelay.NewLegacyChainsFromRelayerExtenders(relayExtenders) + + kb, err := keyStore.OCR().Create() + require.NoError(t, err) + + s := fmt.Sprintf(minimalNonBootstrapTemplate, cltest.NewEIP55Address(), transmitterAddress.Hex(), kb.ID(), "http://blah.com", "") + jb, err := ocr.ValidatedOracleSpecToml(legacyChains, s) + require.NoError(t, err) + err = toml.Unmarshal([]byte(s), &jb) + require.NoError(t, err) + + jb.MaxTaskDuration = models.Interval(cltest.MustParseDuration(t, "1s")) + err = jobORM.CreateJob(&jb) + require.NoError(t, err) + assert.Equal(t, jb.MaxTaskDuration, models.Interval(cltest.MustParseDuration(t, "1s"))) + + lggr := logger.TestLogger(t) + pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) + require.NoError(t, pw.Start(testutils.Context(t))) + sd := ocr.NewDelegate( + db, + jobORM, + keyStore, + nil, + pw, + monitoringEndpoint, + legacyChains, + lggr, + config.Database(), + srvctest.Start(t, utils.NewMailboxMonitor(t.Name())), + ) + + jb.OCROracleSpec.CaptureEATelemetry = tc.jbCaptureEATelemetry + services, err := sd.ServicesForSpec(jb) + require.NoError(t, err) + + enhancedTelemetryServiceCreated := false + for _, service := range services { + _, ok := service.(*ocrcommon.EnhancedTelemetryService[ocrcommon.EnhancedTelemetryData]) + enhancedTelemetryServiceCreated = ok + if enhancedTelemetryServiceCreated { + break + } + } + + require.Equal(t, tc.expected, enhancedTelemetryServiceCreated) + } + }) + t.Run("test job spec error is created", func(t *testing.T) { // Create a keystore with an ocr key bundle and p2p key. kb, err := keyStore.OCR().Create() diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 75147ca2333..1e87915bd15 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -700,6 +700,8 @@ func (d *Delegate) newServicesMercury( if ocrcommon.ShouldCollectEnhancedTelemetryMercury(jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, chEnhancedTelem, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.FeedID.String(), synchronization.EnhancedEAMercury), lggr.Named("EnhancedTelemetryMercury")) mercuryServices = append(mercuryServices, enhancedTelemService) + } else { + lggr.Infow("Enhanced telemetry is disabled for mercury job", "job", jb.Name) } return mercuryServices, err2 From c3e889f1456d052ec0e96af383669bb7685d3a05 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 8 Nov 2023 17:00:30 +0100 Subject: [PATCH 105/271] Log poller on demand streamlining (#11231) * move workflow to correct directory * streamline on-demand values a bit * get RPC urls and private keys from secrets * download and run from inside the test folder * checkout repo before running tests * get inputs and mask them * fix step ordering in workflow * fix default image tag * use latest pumba@CTF * run one test * fix directory name * run on powerful runner * add guide explaining how to use log poller tests * update LP readme --- .github/workflows/on-demand-log-poller.yml | 71 +++++--- integration-tests/LOG_POLLER.md | 163 ++++++++++++++++++ integration-tests/docker/test_env/test_env.go | 17 ++ .../reorg/log_poller_maybe_reorg_test.go | 4 +- .../universal/log_poller/config.go | 11 +- .../universal/log_poller/scenarios.go | 2 +- 6 files changed, 235 insertions(+), 33 deletions(-) create mode 100644 integration-tests/LOG_POLLER.md diff --git a/.github/workflows/on-demand-log-poller.yml b/.github/workflows/on-demand-log-poller.yml index c5470a5a3dc..42f901ec304 100644 --- a/.github/workflows/on-demand-log-poller.yml +++ b/.github/workflows/on-demand-log-poller.yml @@ -24,43 +24,64 @@ on: required: true chainlinkVersion: description: Chainlink version to use - default: "v2.7.0-beta0" + default: "2.7.0-beta1" required: true selectedNetworks: - description: Network to use (only Sepolia or Mumbai) - default: "Sepolia" - required: true - fundingKey: - description: Private key used to fund the contracts (must have sufficient ETH and LINK!) - required: true - rpcURL: - description: RPC URL to use + type: choice + options: + - "SIMULATED" + - "SEPOLIA" + - "MUMBAI" + fundingPrivateKey: + description: Private funding key (Skip for Simulated) required: true + type: string wsURL: - description: WS URL to use + description: WS URL for the network (Skip for Simulated) required: true + type: string + httpURL: + description: HTTP URL for the network (Skip for Simulated) + required: true + type: string jobs: test: - runs-on: ubuntu-latest + env: + CONTRACTS: ${{ inputs.contracts }} + EVENTS_PER_TX: ${{ inputs.eventsPerTx }} + LOAD_DURATION: ${{ inputs.loadDuration }} + USE_FINALITY_TAG: ${{ inputs.useFinalityTag }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} + REF_NAME: ${{ github.head_ref || github.ref_name }} + runs-on: ubuntu20.04-8cores-32GB steps: - - uses: actions/checkout@v3 + - name: Get Inputs + run: | + EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) + EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) + EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) + + echo ::add-mask::$EVM_URLS + echo ::add-mask::$EVM_HTTP_URLS + echo ::add-mask::$EVM_KEYS + + echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV + echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV + echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.REF_NAME }} - name: Setup Go uses: actions/setup-go@v3 with: go-version-file: "integration-tests/go.mod" cache: true - - name: Show overrides - env: - CONTRACTS: ${{ inputs.contracts }} - EVENTS_PER_TX: ${{ inputs.eventsPerTx }} - LOAD_DURATION: ${{ inputs.loadDuration }} - USE_FINALITY_TAG: ${{ inputs.useFinalityTag }} - CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} - CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} - SELECTED_NETWORKS: ${{ inputs.selectedNetworks }} - EVM_KEYS: ${{ inputs.fundingKey }} - EVM_HTTP_URLS: ${{ inputs.rpcURL }} - EVM_URLS: ${{ inputs.wsURL }} + - name: Run tests run: | - go test -v -timeout 5h -run=TestLogPollerFromEnv integration-tests/reorg/log_poller_maybe_reorg_test.go \ No newline at end of file + cd integration-tests + go mod download + go test -v -timeout 5h -v -count=1 -run ^TestLogPollerFromEnv$ ./reorg/log_poller_maybe_reorg_test.go diff --git a/integration-tests/LOG_POLLER.md b/integration-tests/LOG_POLLER.md new file mode 100644 index 00000000000..6e98fba5525 --- /dev/null +++ b/integration-tests/LOG_POLLER.md @@ -0,0 +1,163 @@ +# How to run Log Poller's tests + +## Limitations +* currently they can only be run in Docker, not in Kubernetes +* when using `looped` runner it's not possible to directly control execution time +* WASP's `gun` implementation is imperfect in terms of generated load + +## Configuration +Due to unfinished migration to TOML config tests use a mixed configuration approach: +* network, RPC endpoints, funding keys, etc need to be provided by env vars +* test-specific configuration can be provided by TOML file or via a `Config` struct (to which TOML is parsed anyway) additionally some of it can be overridden by env vars (for ease of use in CI) +** smoke tests use the programmatical approach +** load test uses the TOML approach + +## Approximated test scenario +Different tests might have slightly modified scenarios, but generally they follow this pattern: +* start CL nodes +* setup OCR +* upload Automation Registry 2.1 +* deploy UpKeep Consumers +* deploy test contracts +* register filters for test contracts +* make sure all CL nodes have filters registered +* emit test logs +* wait for log poller to finalise last block in which logs were emitted +** block number is determined either by finality tag or fixed finality depth depending on network configuration +* wait for all CL nodes to have expected log count +* compare logs that present in the EVM node with logs in CL nodes + +All of the checks use fluent waits. + +### Required env vars +* `CHAINLINK_IMAGE` +* `CHAINLINK_VERSION` +* `SELECTED_NETWORKS` + +### Env vars required for live testnet tests +* `EVM_WS_URL` -- RPC websocket +* `EVM_HTTP_URL` -- RPC HTTP +* `EVM_KEYS` -- private keys used for funding + +Since on live testnets we are using existing and canonical LINK contracts funding keys need to contain enough LINK to pay for the test. There's an automated check that fails during setup if there's not enough LINK. Approximately `9 LINK` is required for each UpKeep contract test uses to register a `LogTrigger`. Test contract emits 3 types of events and unless configured otherwise (programmatically!) all of them will be used, which means that due to Automation's limitation we need to register a separate `LogTrigger` for each event type for each contract. So if you want to test with 100 contracts, then you'd need to register 300 UpKeep contracts and thus your funding address needs to have at least 2700 LINK. + +### Programmatical config +There are two load generators available: +* `looped` -- it's a simple generator that just loops over all contracts and emits events at random intervals +* `wasp` -- based on WASP load testing tool, it's more sophisticated and allows to control execution time + +#### Looped config +``` + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, # number of test contracts to deploy + EventsPerTx: 4, # number of events to emit in a single transaction + UseFinalityTag: false, # if set to true then Log Poller will use finality tag returned by chain, when determining last finalised block (won't work on a simulated network, it requires eth2) + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, # number of times each contract will be called + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, # minimum number of milliseconds to wait before emitting events + MaxEmitWaitTimeMs: 500, # maximum number of milliseconds to wait before emitting events + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { # modify that function to emit only logs you want + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit +``` + +Remember that final number of events emitted will be `Contracts * EventsPerTx * ExecutionCount * len(eventToEmit)`. And that that last number by default is equal to `3` (that's because we want to emit different event types, not just one). You can change that by overriding `EventsToEmit` field. + +#### WASP config +``` + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 4, + UseFinalityTag: false, + }, + Wasp: &logpoller.WaspConfig{ + Load: &logpoller.Load{ + RPS: 10, # requests per second + LPS: 0, # logs per second + RateLimitUnitDuration: models.MustNewDuration(5 * time.Minutes), # for how long the load should be limited (ramp-up period) + Duration: models.MustNewDuration(5 * time.Minutes), # how long to generate the load for + CallTimeout: models.MustNewDuration(5 * time.Minutes), # how long to wait for a single call to finish + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit +``` + +Remember that you cannot specify both `RPS` and `LPS`. If you want to use `LPS` then omit `RPS` field. Also remember that depending on the events you decide to emit RPS might mean 1 request or might mean 3 requests (if you go with the default `EventsToEmit`). + +For other nuances do check [gun.go][integration-tests/universal/log_poller/gun.go]. + +### TOML config +That config follows the same structure as programmatical config shown above. + +Sample config: [config.toml](integration-tests/load/log_poller/config.toml) + +Use this snippet instead of creating the `Config` struct programmatically: +``` + cfg, err := lp_helpers.ReadConfig(lp_helpers.DefaultConfigFilename) + require.NoError(t, err) +``` + +And remember to add events you want emit: +``` + eventsToEmit := []abi.Event{} + for _, event := range lp_helpers.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit +``` + +### Timeouts +Various checks inside the tests have hardcoded timeouts, which might not be suitable for your execution parameters, for example if you decided to emit 1M logs, then waiting for all of them to be indexed for `1m` might not be enough. Remember to adjust them accordingly. + +Sample snippet: +``` + gom.Eventually(func(g gomega.Gomega) { + logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), totalLogsEmitted, expectedFilters, l, coreLogger, testEnv.ClCluster) + if err != nil { + l.Warn().Err(err).Msg("Error checking if CL nodes have expected log count. Retrying...") + } + g.Expect(logCountMatches).To(gomega.BeTrue(), "Not all CL nodes have expected log count") + }, "1m", "30s").Should(gomega.Succeed()) # 1m is the timeout for all nodes to have expected log count +``` + +## Tests +* [Load](integration-tests/load/log_poller/log_poller_test.go) +* [Smoke](integration-tests/smoke/log_poller/log_poller_test.go) + +## Running tests +After setting all the environment variables you can run the test with: +``` +# run in the root folder of chainlink repo +go test -v -test.timeout=2700s -run TestLogPollerReplay integration-tests/smoke/log_poller_test.go +``` + +Remember to adjust test timeout accordingly to match expected duration. + + +## Github Actions +If all of that seems too complicated use this [on-demand workflow](https://github.com/smartcontractkit/chainlink/actions/workflows/on-demand-log-poller.yml). + +Execution time here is an approximation, so depending on network conditions it might be slightly longer or shorter. \ No newline at end of file diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index a6495bed540..b3fc1c0cf6c 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -187,6 +187,8 @@ func (te *CLClusterTestEnv) Cleanup() error { return fmt.Errorf("chainlink nodes are nil, unable cleanup chainlink nodes") } + te.logWhetherAllContainersAreRunning() + // TODO: This is an imperfect and temporary solution, see TT-590 for a more sustainable solution // Collect logs if the test fails, or if we just want them if te.t.Failed() || os.Getenv("TEST_LOG_COLLECT") == "true" { @@ -216,6 +218,21 @@ func (te *CLClusterTestEnv) Cleanup() error { return nil } +func (te *CLClusterTestEnv) logWhetherAllContainersAreRunning() { + for _, node := range te.ClCluster.Nodes { + isCLRunning := node.Container.IsRunning() + isDBRunning := node.PostgresDb.Container.IsRunning() + + if !isCLRunning { + te.l.Warn().Str("Node", node.ContainerName).Msg("Chainlink node was not running, when test ended") + } + + if !isDBRunning { + te.l.Warn().Str("Node", node.ContainerName).Msg("Postgres DB is not running, when test ended") + } + } +} + // collectTestLogs collects the logs from all the Chainlink nodes in the test environment and writes them to local files func (te *CLClusterTestEnv) collectTestLogs() error { te.l.Info().Msg("Collecting test logs") diff --git a/integration-tests/reorg/log_poller_maybe_reorg_test.go b/integration-tests/reorg/log_poller_maybe_reorg_test.go index 4e802bdb09c..0176fdbbdd6 100644 --- a/integration-tests/reorg/log_poller_maybe_reorg_test.go +++ b/integration-tests/reorg/log_poller_maybe_reorg_test.go @@ -20,8 +20,8 @@ func TestLogPollerFromEnv(t *testing.T) { ExecutionCount: 100, }, FuzzConfig: logpoller.FuzzConfig{ - MinEmitWaitTimeMs: 800, - MaxEmitWaitTimeMs: 1200, + MinEmitWaitTimeMs: 400, + MaxEmitWaitTimeMs: 600, }, }, } diff --git a/integration-tests/universal/log_poller/config.go b/integration-tests/universal/log_poller/config.go index 623fa6606ed..7297e811241 100644 --- a/integration-tests/universal/log_poller/config.go +++ b/integration-tests/universal/log_poller/config.go @@ -112,11 +112,12 @@ func (c *Config) OverrideFromEnv() error { if c.General.Generator == GeneratorType_WASP { c.Wasp.Load.Duration = &d } else { - // make the looped generator approximately run for desired duration - // on average we will emit 1 event per second - c.LoopedConfig.FuzzConfig.MinEmitWaitTimeMs = 900 - c.LoopedConfig.FuzzConfig.MaxEmitWaitTimeMs = 1100 - c.LoopedConfig.ContractConfig.ExecutionCount = int(d.Duration().Seconds()) + // this is completely arbitrary and practice shows that even with this values + // test executes much longer than specified, probably due to network latency + c.LoopedConfig.FuzzConfig.MinEmitWaitTimeMs = 400 + c.LoopedConfig.FuzzConfig.MaxEmitWaitTimeMs = 600 + // divide by 4 based on past runs, but we should do it in a better way + c.LoopedConfig.ContractConfig.ExecutionCount = int(d.Duration().Seconds() / 4) } } diff --git a/integration-tests/universal/log_poller/scenarios.go b/integration-tests/universal/log_poller/scenarios.go index d14a3bcb2a7..1110a2f8caf 100644 --- a/integration-tests/universal/log_poller/scenarios.go +++ b/integration-tests/universal/log_poller/scenarios.go @@ -309,7 +309,7 @@ func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string require.Equal(t, "Replay started", response.Data.Attributes.Message, "Unexpected response message from log poller's replay") } - l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for logs to be processed by all nodes and for chain to advance beyond finality") + l.Warn().Str("Duration", consistencyTimeout).Msg("Waiting for replay logs to be processed by all nodes") gom.Eventually(func(g gomega.Gomega) { logCountMatches, err := clNodesHaveExpectedLogCount(startBlock, endBlock, testEnv.EVMClient.GetChainID(), totalLogsEmitted, expectedFilters, l, coreLogger, testEnv.ClCluster) From 7be2d6516addfaaab435d34dfac0039cfa63479b Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Wed, 8 Nov 2023 18:40:17 +0200 Subject: [PATCH 106/271] chore/VRF-722-externalise-oracleWithdraw-address (#11216) * VRF-722: made eoa address configurable which is used for VRF Key registration in VRF super scripts * VRF-722: PR comments --- core/scripts/common/vrf/model/model.go | 5 ++ core/scripts/common/vrf/setup-envs/README.md | 4 +- core/scripts/common/vrf/setup-envs/main.go | 27 +++++--- core/scripts/vrfv2/testnet/README.md | 16 +++-- .../vrfv2/testnet/v2scripts/super_scripts.go | 54 +++++++++------- core/scripts/vrfv2plus/testnet/README.md | 11 +++- .../testnet/v2plusscripts/super_scripts.go | 63 ++++++++++++------- 7 files changed, 117 insertions(+), 63 deletions(-) diff --git a/core/scripts/common/vrf/model/model.go b/core/scripts/common/vrf/model/model.go index bd0e3bbe364..42deb424536 100644 --- a/core/scripts/common/vrf/model/model.go +++ b/core/scripts/common/vrf/model/model.go @@ -44,3 +44,8 @@ type ContractAddresses struct { CoordinatorAddress common.Address BatchCoordinatorAddress common.Address } + +type VRFKeyRegistrationConfig struct { + VRFKeyUncompressedPubKey string + RegisterAgainstAddress string +} diff --git a/core/scripts/common/vrf/setup-envs/README.md b/core/scripts/common/vrf/setup-envs/README.md index 33515338a24..f3b391f0eed 100644 --- a/core/scripts/common/vrf/setup-envs/README.md +++ b/core/scripts/common/vrf/setup-envs/README.md @@ -35,7 +35,9 @@ go run . \ --min-confs=3 \ --num-eth-keys=1 \ --num-vrf-keys=1 \ ---sending-key-funding-amount="1e17" +--sending-key-funding-amount="1e17" \ +--register-vrf-key-against-address= ``` Optional parameters - will not be deployed if specified (NOT WORKING YET) diff --git a/core/scripts/common/vrf/setup-envs/main.go b/core/scripts/common/vrf/setup-envs/main.go index 6748408f476..7c2530ffd47 100644 --- a/core/scripts/common/vrf/setup-envs/main.go +++ b/core/scripts/common/vrf/setup-envs/main.go @@ -85,6 +85,8 @@ func main() { batchBHSAddressString := flag.String("batch-bhs-address", "", "address of Batch BHS contract") coordinatorAddressString := flag.String("coordinator-address", "", "address of VRF Coordinator contract") batchCoordinatorAddressString := flag.String("batch-coordinator-address", "", "address Batch VRF Coordinator contract") + registerVRFKeyAgainstAddress := flag.String("register-vrf-key-against-address", "", "VRF Key registration against address - "+ + "from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments") e := helpers.SetupEnv(false) flag.Parse() @@ -171,6 +173,11 @@ func main() { BatchCoordinatorAddress: common.HexToAddress(*batchCoordinatorAddressString), } + vrfKeyRegistrationConfig := model.VRFKeyRegistrationConfig{ + VRFKeyUncompressedPubKey: nodesMap[model.VRFPrimaryNodeName].VrfKeys[0], + RegisterAgainstAddress: *registerVRFKeyAgainstAddress, + } + var jobSpecs model.JobSpecs switch *vrfVersion { @@ -188,10 +195,10 @@ func main() { } coordinatorConfigV2 := v2scripts.CoordinatorConfigV2{ - MinConfs: minConfs, - MaxGasLimit: &constants.MaxGasLimit, - StalenessSeconds: &constants.StalenessSeconds, - GasAfterPayment: &constants.GasAfterPayment, + MinConfs: *minConfs, + MaxGasLimit: constants.MaxGasLimit, + StalenessSeconds: constants.StalenessSeconds, + GasAfterPayment: constants.GasAfterPayment, FallbackWeiPerUnitLink: constants.FallbackWeiPerUnitLink, FeeConfig: feeConfigV2, } @@ -199,7 +206,7 @@ func main() { jobSpecs = v2scripts.VRFV2DeployUniverse( e, subscriptionBalanceJuels, - &nodesMap[model.VRFPrimaryNodeName].VrfKeys[0], + vrfKeyRegistrationConfig, contractAddresses, coordinatorConfigV2, *batchFulfillmentEnabled, @@ -211,10 +218,10 @@ func main() { FulfillmentFlatFeeNativePPM: uint32(constants.FlatFeeNativePPM), } coordinatorConfigV2Plus := v2plusscripts.CoordinatorConfigV2Plus{ - MinConfs: minConfs, - MaxGasLimit: &constants.MaxGasLimit, - StalenessSeconds: &constants.StalenessSeconds, - GasAfterPayment: &constants.GasAfterPayment, + MinConfs: *minConfs, + MaxGasLimit: constants.MaxGasLimit, + StalenessSeconds: constants.StalenessSeconds, + GasAfterPayment: constants.GasAfterPayment, FallbackWeiPerUnitLink: constants.FallbackWeiPerUnitLink, FeeConfig: feeConfigV2Plus, } @@ -223,7 +230,7 @@ func main() { e, subscriptionBalanceJuels, subscriptionBalanceNativeWei, - &nodesMap[model.VRFPrimaryNodeName].VrfKeys[0], + vrfKeyRegistrationConfig, contractAddresses, coordinatorConfigV2Plus, *batchFulfillmentEnabled, diff --git a/core/scripts/vrfv2/testnet/README.md b/core/scripts/vrfv2/testnet/README.md index 1b2d986f554..b527c576fd0 100644 --- a/core/scripts/vrfv2/testnet/README.md +++ b/core/scripts/vrfv2/testnet/README.md @@ -57,11 +57,19 @@ To deploy a full VRF environment on-chain, run: ```shell go run . deploy-universe \ ---sending-key-funding-amount 100000000000000000 \ ---subscription-balance=10000000000000000000 \ +--subscription-balance=5000000000000000000 \ #5 LINK --uncompressed-pub-key= \ ---vrf-primary-node-sending-keys="" \ ---batch-fulfillment-enabled false +--vrf-primary-node-sending-keys="" \ #used to fund the keys and for sample VRF Job Spec generation +--sending-key-funding-amount 100000000000000000 \ #0.1 ETH, fund addresses specified in vrf-primary-node-sending-keys +--batch-fulfillment-enabled false \ #only used for sample VRF Job Spec generation +--register-vrf-key-against-address=<"from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments> +``` +```shell +go run . deploy-universe \ +--subscription-balance=5000000000000000000 \ +--uncompressed-pub-key="0xf3706e247a7b205c8a8bd25a6e8c4650474da496151371085d45beeead27e568c1a5e8330c7fa718f8a31226efbff6632ed6f8ed470b637aa9be2b948e9dcef6" \ +--batch-fulfillment-enabled false \ +--register-vrf-key-against-address="0x23b5613fc04949F4A53d1cc8d6BCCD21ffc38C11" ``` ## Deploying the Consumer Contract diff --git a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go index 23ad8e1374b..b623ae63084 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go +++ b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go @@ -26,10 +26,10 @@ import ( ) type CoordinatorConfigV2 struct { - MinConfs *int - MaxGasLimit *int64 - StalenessSeconds *int64 - GasAfterPayment *int64 + MinConfs int + MaxGasLimit int64 + StalenessSeconds int64 + GasAfterPayment int64 FallbackWeiPerUnitLink *big.Int FeeConfig vrf_coordinator_v2.VRFCoordinatorV2FeeConfig } @@ -52,7 +52,10 @@ func DeployUniverseViaCLI(e helpers.Environment) { // optional flags fallbackWeiPerUnitLinkString := deployCmd.String("fallback-wei-per-unit-link", constants.FallbackWeiPerUnitLink.String(), "fallback wei/link ratio") - registerKeyUncompressedPubKey := deployCmd.String("uncompressed-pub-key", "", "uncompressed public key") + registerVRFKeyUncompressedPubKey := deployCmd.String("uncompressed-pub-key", "", "uncompressed public key") + registerVRFKeyAgainstAddress := deployCmd.String("register-vrf-key-against-address", "", "VRF Key registration against address - "+ + "from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments") + vrfPrimaryNodeSendingKeysString := deployCmd.String("vrf-primary-node-sending-keys", "", "VRF Primary Node sending keys") minConfs := deployCmd.Int("min-confs", constants.MinConfs, "min confs") @@ -119,18 +122,23 @@ func DeployUniverseViaCLI(e helpers.Environment) { } coordinatorConfig := CoordinatorConfigV2{ - MinConfs: minConfs, - MaxGasLimit: maxGasLimit, - StalenessSeconds: stalenessSeconds, - GasAfterPayment: gasAfterPayment, + MinConfs: *minConfs, + MaxGasLimit: *maxGasLimit, + StalenessSeconds: *stalenessSeconds, + GasAfterPayment: *gasAfterPayment, FallbackWeiPerUnitLink: fallbackWeiPerUnitLink, FeeConfig: feeConfig, } + vrfKeyRegistrationConfig := model.VRFKeyRegistrationConfig{ + VRFKeyUncompressedPubKey: *registerVRFKeyUncompressedPubKey, + RegisterAgainstAddress: *registerVRFKeyAgainstAddress, + } + VRFV2DeployUniverse( e, subscriptionBalanceJuels, - registerKeyUncompressedPubKey, + vrfKeyRegistrationConfig, contractAddresses, coordinatorConfig, *batchFulfillmentEnabled, @@ -147,7 +155,7 @@ func DeployUniverseViaCLI(e helpers.Environment) { func VRFV2DeployUniverse( e helpers.Environment, subscriptionBalanceJuels *big.Int, - registerKeyUncompressedPubKey *string, + vrfKeyRegistrationConfig model.VRFKeyRegistrationConfig, contractAddresses model.ContractAddresses, coordinatorConfig CoordinatorConfigV2, batchFulfillmentEnabled bool, @@ -155,14 +163,14 @@ func VRFV2DeployUniverse( ) model.JobSpecs { var compressedPkHex string var keyHash common.Hash - if len(*registerKeyUncompressedPubKey) > 0 { + if len(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) > 0 { // Put key in ECDSA format - if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { - *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) + if strings.HasPrefix(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, "0x") { + vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey = strings.Replace(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, "0x", "04", 1) } // Generate compressed public key and key hash - pubBytes, err := hex.DecodeString(*registerKeyUncompressedPubKey) + pubBytes, err := hex.DecodeString(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) helpers.PanicErr(err) pk, err := crypto.UnmarshalPubkey(pubBytes) helpers.PanicErr(err) @@ -217,10 +225,10 @@ func VRFV2DeployUniverse( SetCoordinatorConfig( e, *coordinator, - uint16(*coordinatorConfig.MinConfs), - uint32(*coordinatorConfig.MaxGasLimit), - uint32(*coordinatorConfig.StalenessSeconds), - uint32(*coordinatorConfig.GasAfterPayment), + uint16(coordinatorConfig.MinConfs), + uint32(coordinatorConfig.MaxGasLimit), + uint32(coordinatorConfig.StalenessSeconds), + uint32(coordinatorConfig.GasAfterPayment), coordinatorConfig.FallbackWeiPerUnitLink, coordinatorConfig.FeeConfig, ) @@ -228,12 +236,12 @@ func VRFV2DeployUniverse( fmt.Println("\nConfig set, getting current config from deployed contract...") PrintCoordinatorConfig(coordinator) - if len(*registerKeyUncompressedPubKey) > 0 { + if len(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) > 0 { fmt.Println("\nRegistering proving key...") //NOTE - register proving key against EOA account, and not against Oracle's sending address in other to be able // easily withdraw funds from Coordinator contract back to EOA account - RegisterCoordinatorProvingKey(e, *coordinator, *registerKeyUncompressedPubKey, e.Owner.From.String()) + RegisterCoordinatorProvingKey(e, *coordinator, vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, vrfKeyRegistrationConfig.RegisterAgainstAddress) fmt.Println("\nProving key registered, getting proving key hashes from deployed contract...") _, _, provingKeyHashes, configErr := coordinator.GetRequestConfig(nil) @@ -271,7 +279,7 @@ func VRFV2DeployUniverse( contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress batchFulfillmentEnabled, //batchFulfillmentEnabled compressedPkHex, //publicKey - *coordinatorConfig.MinConfs, //minIncomingConfirmations + coordinatorConfig.MinConfs, //minIncomingConfirmations e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses contractAddresses.CoordinatorAddress, @@ -348,7 +356,7 @@ func VRFV2DeployUniverse( "\nVRF Subscription Id:", subID, "\nVRF Subscription Balance:", *subscriptionBalanceJuels, "\nPossible VRF Request command: ", - fmt.Sprintf("go run . eoa-load-test-request-with-metrics --consumer-address=%s --sub-id=%d --key-hash=%s --request-confirmations %d --requests 1 --runs 1 --cb-gas-limit 1_000_000", consumerAddress, subID, keyHash, *coordinatorConfig.MinConfs), + fmt.Sprintf("go run . eoa-load-test-request-with-metrics --consumer-address=%s --sub-id=%d --key-hash=%s --request-confirmations %d --requests 1 --runs 1 --cb-gas-limit 1_000_000", consumerAddress, subID, keyHash, coordinatorConfig.MinConfs), "\nRetrieve Request Status: ", fmt.Sprintf("go run . eoa-load-test-read-metrics --consumer-address=%s", consumerAddress), "\nA node can now be configured to run a VRF job with the below job spec :\n", diff --git a/core/scripts/vrfv2plus/testnet/README.md b/core/scripts/vrfv2plus/testnet/README.md index b95ec99d5fb..6402569c560 100644 --- a/core/scripts/vrfv2plus/testnet/README.md +++ b/core/scripts/vrfv2plus/testnet/README.md @@ -58,7 +58,16 @@ cd /core/scripts/vrfv2/testnet - Not specifying `--link-eth-feed` would make the super script deploy a new LINK-ETH feed contract and use it for funding VRF V2+ subscription ```shell -go run . deploy-universe --link-address=$LINK --link-eth-feed=$LINK_ETH_FEED --subscription-balance= --uncompressed-pub-key=$PUB_KEY --oracle-address=$ORACLE_ADDRESS +go run . deploy-universe \ +--link-address=$LINK \ +--link-eth-feed=$LINK_ETH_FEED \ +--subscription-balance=5000000000000000000 \ #5 LINK +--subscription-balance-native=1000000000000000000 \ #1 ETH +--uncompressed-pub-key= \ +--vrf-primary-node-sending-keys="" \ #used to fund the keys and for sample VRF Job Spec generation +--sending-key-funding-amount 100000000000000000 \ #0.1 ETH, fund addresses specified in vrf-primary-node-sending-keys +--batch-fulfillment-enabled false \ #only used for sample VRF Job Spec generation +--register-vrf-key-against-address="" # from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments ``` ## Deploying the Consumer Contract diff --git a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go index 752e06bbb25..50584d885a2 100644 --- a/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go +++ b/core/scripts/vrfv2plus/testnet/v2plusscripts/super_scripts.go @@ -39,10 +39,10 @@ import ( var coordinatorV2PlusABI = evmtypes.MustGetABI(vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalABI) type CoordinatorConfigV2Plus struct { - MinConfs *int - MaxGasLimit *int64 - StalenessSeconds *int64 - GasAfterPayment *int64 + MinConfs int + MaxGasLimit int64 + StalenessSeconds int64 + GasAfterPayment int64 FallbackWeiPerUnitLink *big.Int FeeConfig vrf_coordinator_v2_5.VRFCoordinatorV25FeeConfig } @@ -481,7 +481,10 @@ func DeployUniverseViaCLI(e helpers.Environment) { // optional flags fallbackWeiPerUnitLinkString := deployCmd.String("fallback-wei-per-unit-link", "6e16", "fallback wei/link ratio") - registerKeyUncompressedPubKey := deployCmd.String("uncompressed-pub-key", "", "uncompressed public key") + registerVRFKeyUncompressedPubKey := deployCmd.String("uncompressed-pub-key", "", "uncompressed public key") + registerVRFKeyAgainstAddress := deployCmd.String("register-vrf-key-against-address", "", "VRF Key registration against address - "+ + "from this address you can perform `coordinator.oracleWithdraw` to withdraw earned funds from rand request fulfilments") + vrfPrimaryNodeSendingKeysString := deployCmd.String("vrf-primary-node-sending-keys", "", "VRF Primary Node sending keys") minConfs := deployCmd.Int("min-confs", constants.MinConfs, "min confs") nodeSendingKeyFundingAmount := deployCmd.String("sending-key-funding-amount", constants.NodeSendingKeyFundingAmount, "CL node sending key funding amount") @@ -505,10 +508,17 @@ func DeployUniverseViaCLI(e helpers.Environment) { FulfillmentFlatFeeNativePPM: uint32(*flatFeeEthPPM), } - vrfPrimaryNodeSendingKeys := strings.Split(*vrfPrimaryNodeSendingKeysString, ",") + var vrfPrimaryNodeSendingKeys []string + if len(*vrfPrimaryNodeSendingKeysString) > 0 { + vrfPrimaryNodeSendingKeys = strings.Split(*vrfPrimaryNodeSendingKeysString, ",") + } nodesMap := make(map[string]model.Node) + fundingAmount, ok := new(big.Int).SetString(*nodeSendingKeyFundingAmount, 10) + if !ok { + panic(fmt.Sprintf("failed to parse node sending key funding amount '%s'", *nodeSendingKeyFundingAmount)) + } nodesMap[model.VRFPrimaryNodeName] = model.Node{ SendingKeys: util.MapToSendingKeyArr(vrfPrimaryNodeSendingKeys), SendingKeyFundingAmount: fundingAmount, @@ -529,19 +539,24 @@ func DeployUniverseViaCLI(e helpers.Environment) { } coordinatorConfig := CoordinatorConfigV2Plus{ - MinConfs: minConfs, - MaxGasLimit: maxGasLimit, - StalenessSeconds: stalenessSeconds, - GasAfterPayment: gasAfterPayment, + MinConfs: *minConfs, + MaxGasLimit: *maxGasLimit, + StalenessSeconds: *stalenessSeconds, + GasAfterPayment: *gasAfterPayment, FallbackWeiPerUnitLink: fallbackWeiPerUnitLink, FeeConfig: feeConfig, } + vrfKeyRegistrationConfig := model.VRFKeyRegistrationConfig{ + VRFKeyUncompressedPubKey: *registerVRFKeyUncompressedPubKey, + RegisterAgainstAddress: *registerVRFKeyAgainstAddress, + } + VRFV2PlusDeployUniverse( e, subscriptionBalanceJuels, subscriptionBalanceNativeWei, - registerKeyUncompressedPubKey, + vrfKeyRegistrationConfig, contractAddresses, coordinatorConfig, *batchFulfillmentEnabled, @@ -558,7 +573,7 @@ func DeployUniverseViaCLI(e helpers.Environment) { func VRFV2PlusDeployUniverse(e helpers.Environment, subscriptionBalanceJuels *big.Int, subscriptionBalanceNativeWei *big.Int, - registerKeyUncompressedPubKey *string, + vrfKeyRegistrationConfig model.VRFKeyRegistrationConfig, contractAddresses model.ContractAddresses, coordinatorConfig CoordinatorConfigV2Plus, batchFulfillmentEnabled bool, @@ -566,14 +581,14 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, ) model.JobSpecs { var compressedPkHex string var keyHash common.Hash - if len(*registerKeyUncompressedPubKey) > 0 { + if len(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) > 0 { // Put key in ECDSA format - if strings.HasPrefix(*registerKeyUncompressedPubKey, "0x") { - *registerKeyUncompressedPubKey = strings.Replace(*registerKeyUncompressedPubKey, "0x", "04", 1) + if strings.HasPrefix(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, "0x") { + vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey = strings.Replace(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, "0x", "04", 1) } // Generate compressed public key and key hash - pubBytes, err := hex.DecodeString(*registerKeyUncompressedPubKey) + pubBytes, err := hex.DecodeString(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) helpers.PanicErr(err) pk, err := crypto.UnmarshalPubkey(pubBytes) helpers.PanicErr(err) @@ -628,10 +643,10 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, SetCoordinatorConfig( e, *coordinator, - uint16(*coordinatorConfig.MinConfs), - uint32(*coordinatorConfig.MaxGasLimit), - uint32(*coordinatorConfig.StalenessSeconds), - uint32(*coordinatorConfig.GasAfterPayment), + uint16(coordinatorConfig.MinConfs), + uint32(coordinatorConfig.MaxGasLimit), + uint32(coordinatorConfig.StalenessSeconds), + uint32(coordinatorConfig.GasAfterPayment), coordinatorConfig.FallbackWeiPerUnitLink, coordinatorConfig.FeeConfig, ) @@ -639,12 +654,12 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, fmt.Println("\nConfig set, getting current config from deployed contract...") PrintCoordinatorConfig(coordinator) - if len(*registerKeyUncompressedPubKey) > 0 { + if len(vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey) > 0 { fmt.Println("\nRegistering proving key...") //NOTE - register proving key against EOA account, and not against Oracle's sending address in other to be able // easily withdraw funds from Coordinator contract back to EOA account - RegisterCoordinatorProvingKey(e, *coordinator, *registerKeyUncompressedPubKey, e.Owner.From.String()) + RegisterCoordinatorProvingKey(e, *coordinator, vrfKeyRegistrationConfig.VRFKeyUncompressedPubKey, vrfKeyRegistrationConfig.RegisterAgainstAddress) fmt.Println("\nProving key registered, getting proving key hashes from deployed contract...") _, _, provingKeyHashes, configErr := coordinator.GetRequestConfig(nil) @@ -690,7 +705,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, contractAddresses.BatchCoordinatorAddress, //batchCoordinatorAddress batchFulfillmentEnabled, //batchFulfillmentEnabled compressedPkHex, //publicKey - *coordinatorConfig.MinConfs, //minIncomingConfirmations + coordinatorConfig.MinConfs, //minIncomingConfirmations e.ChainID, //evmChainID strings.Join(util.MapToAddressArr(nodesMap[model.VRFPrimaryNodeName].SendingKeys), "\",\""), //fromAddresses contractAddresses.CoordinatorAddress, @@ -768,7 +783,7 @@ func VRFV2PlusDeployUniverse(e helpers.Environment, "\nVRF Subscription LINK Balance:", *subscriptionBalanceJuels, "\nVRF Subscription Native Balance:", *subscriptionBalanceNativeWei, "\nPossible VRF Request command: ", - fmt.Sprintf("go run . eoa-load-test-request-with-metrics --consumer-address=%s --sub-id=%d --key-hash=%s --request-confirmations %d --requests 1 --runs 1 --cb-gas-limit 1_000_000", consumerAddress, subID, keyHash, *coordinatorConfig.MinConfs), + fmt.Sprintf("go run . eoa-load-test-request-with-metrics --consumer-address=%s --sub-id=%d --key-hash=%s --request-confirmations %d --requests 1 --runs 1 --cb-gas-limit 1_000_000", consumerAddress, subID, keyHash, coordinatorConfig.MinConfs), "\nRetrieve Request Status: ", fmt.Sprintf("go run . eoa-load-test-read-metrics --consumer-address=%s", consumerAddress), "\nA node can now be configured to run a VRF job with the below job spec :\n", From c80fb93bc127b0021d4d2f98ff64001154661c54 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 8 Nov 2023 08:52:15 -0800 Subject: [PATCH 107/271] remove duplicate contracts (#11127) --- .../native_solc_compile_all_automation | 2 - .../automation/testhelpers/KeeperBase.sol | 21 ---------- .../testhelpers/KeeperCompatibleInterface.sol | 42 ------------------- .../automation/testhelpers/KeeperConsumer.sol | 4 +- .../testhelpers/PerformDataChecker.sol | 2 +- .../keeper_consumer_wrapper.go | 4 +- ...rapper-dependency-versions-do-not-edit.txt | 11 +---- 7 files changed, 6 insertions(+), 80 deletions(-) delete mode 100644 contracts/src/v0.8/automation/testhelpers/KeeperBase.sol delete mode 100644 contracts/src/v0.8/automation/testhelpers/KeeperCompatibleInterface.sol diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index beb557de126..1c54d677135 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -63,8 +63,6 @@ compileContract automation/v2_1/AutomationForwarderLogic.sol compileContract automation/testhelpers/LogTriggeredStreamsLookup.sol compileContract automation/testhelpers/DummyProtocol.sol -compileContract automation/testhelpers/KeeperBase.sol -compileContract automation/testhelpers/KeeperCompatibleInterface.sol compileContract automation/testhelpers/KeeperConsumer.sol compileContract automation/testhelpers/KeeperConsumerPerformance.sol compileContract automation/testhelpers/PerformDataChecker.sol diff --git a/contracts/src/v0.8/automation/testhelpers/KeeperBase.sol b/contracts/src/v0.8/automation/testhelpers/KeeperBase.sol deleted file mode 100644 index 6fe41607f75..00000000000 --- a/contracts/src/v0.8/automation/testhelpers/KeeperBase.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.16; - -contract KeeperBase { - /** - * @notice method that allows it to be simulated via eth_call by checking that - * the sender is the zero address. - */ - function preventExecution() internal view { - require(tx.origin == address(0), "only for simulated backend"); - } - - /** - * @notice modifier that allows it to be simulated via eth_call by checking - * that the sender is the zero address. - */ - modifier cannotExecute() { - preventExecution(); - _; - } -} diff --git a/contracts/src/v0.8/automation/testhelpers/KeeperCompatibleInterface.sol b/contracts/src/v0.8/automation/testhelpers/KeeperCompatibleInterface.sol deleted file mode 100644 index 113f5ef6a55..00000000000 --- a/contracts/src/v0.8/automation/testhelpers/KeeperCompatibleInterface.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.16; - -interface KeeperCompatibleInterface { - /** - * @notice method that is simulated by the keepers to see if any work actually - * needs to be performed. This method does does not actually need to be - * executable, and since it is only ever simulated it can consume lots of gas. - * @dev To ensure that it is never called, you may want to add the - * cannotExecute modifier from KeeperBase to your implementation of this - * method. - * @param checkData specified in the upkeep registration so it is always the - * same for a registered upkeep. This can easily be broken down into specific - * arguments using `abi.decode`, so multiple upkeeps can be registered on the - * same contract and easily differentiated by the contract. - * @return upkeepNeeded boolean to indicate whether the keeper should call - * performUpkeep or not. - * @return performData bytes that the keeper should call performUpkeep with, if - * upkeep is needed. If you would like to encode data to decode later, try - * `abi.encode`. - */ - function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData); - - /** - * @notice method that is actually executed by the keepers, via the registry. - * The data returned by the checkUpkeep simulation will be passed into - * this method to actually be executed. - * @dev The input to this method should not be trusted, and the caller of the - * method should not even be restricted to any single registry. Anyone should - * be able call it, and the input should be validated, there is no guarantee - * that the data passed in is the performData returned from checkUpkeep. This - * could happen due to malicious keepers, racing keepers, or simply a state - * change while the performUpkeep transaction is waiting for confirmation. - * Always validate the data passed in. - * @param performData is the data which was passed back from the checkData - * simulation. If it is encoded, it can easily be decoded into other types by - * calling `abi.decode`. This data should not be trusted, and should be - * validated against the contract's current state. - */ - function performUpkeep(bytes calldata performData) external; -} diff --git a/contracts/src/v0.8/automation/testhelpers/KeeperConsumer.sol b/contracts/src/v0.8/automation/testhelpers/KeeperConsumer.sol index ba4694234a9..fb492f376c2 100644 --- a/contracts/src/v0.8/automation/testhelpers/KeeperConsumer.sol +++ b/contracts/src/v0.8/automation/testhelpers/KeeperConsumer.sol @@ -1,7 +1,7 @@ pragma solidity 0.8.16; -import "./KeeperCompatibleInterface.sol"; -import "./KeeperBase.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; +import "../KeeperBase.sol"; contract KeeperConsumer is KeeperCompatibleInterface, KeeperBase { uint public counter; diff --git a/contracts/src/v0.8/automation/testhelpers/PerformDataChecker.sol b/contracts/src/v0.8/automation/testhelpers/PerformDataChecker.sol index 03c57ea8e41..268942f931d 100644 --- a/contracts/src/v0.8/automation/testhelpers/PerformDataChecker.sol +++ b/contracts/src/v0.8/automation/testhelpers/PerformDataChecker.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import "./KeeperCompatibleInterface.sol"; +import "../interfaces/KeeperCompatibleInterface.sol"; contract PerformDataChecker is KeeperCompatibleInterface { uint256 public counter; diff --git a/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go b/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go index 8a4ee2c4de8..feb614aa83b 100644 --- a/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go +++ b/core/gethwrappers/generated/keeper_consumer_wrapper/keeper_consumer_wrapper.go @@ -29,8 +29,8 @@ var ( ) var KeeperConsumerMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"updateInterval\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"interval\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastTimeStamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a060405234801561001057600080fd5b5060405161036c38038061036c83398101604081905261002f9161003f565b6080524260015560008055610058565b60006020828403121561005157600080fd5b5051919050565b6080516102fa610072600039600060cc01526102fa6000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806361bc221a1161005057806361bc221a1461009d5780636e04ff0d146100a6578063947a36fb146100c757600080fd5b80633f3b3b271461006c5780634585e33b14610088575b600080fd5b61007560015481565b6040519081526020015b60405180910390f35b61009b6100963660046101c5565b6100ee565b005b61007560005481565b6100b96100b43660046101c5565b610103565b60405161007f929190610237565b6100757f000000000000000000000000000000000000000000000000000000000000000081565b6000546100fc9060016102ad565b6000555050565b6000606061010f610157565b6001848481818080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250959a92995091975050505050505050565b32156101c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f6f6e6c7920666f722073696d756c61746564206261636b656e64000000000000604482015260640160405180910390fd5b565b600080602083850312156101d857600080fd5b823567ffffffffffffffff808211156101f057600080fd5b818501915085601f83011261020457600080fd5b81358181111561021357600080fd5b86602082850101111561022557600080fd5b60209290920196919550909350505050565b821515815260006020604081840152835180604085015260005b8181101561026d57858101830151858201606001528201610251565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b808201808211156102e7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000810000a", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"updateInterval\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"OnlySimulatedBackend\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"checkData\",\"type\":\"bytes\"}],\"name\":\"checkUpkeep\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"upkeepNeeded\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"interval\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastTimeStamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a060405234801561001057600080fd5b5060405161033838038061033883398101604081905261002f9161003f565b6080524260015560008055610058565b60006020828403121561005157600080fd5b5051919050565b6080516102c6610072600039600060cc01526102c66000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806361bc221a1161005057806361bc221a1461009d5780636e04ff0d146100a6578063947a36fb146100c757600080fd5b80633f3b3b271461006c5780634585e33b14610088575b600080fd5b61007560015481565b6040519081526020015b60405180910390f35b61009b610096366004610191565b6100ee565b005b61007560005481565b6100b96100b4366004610191565b610103565b60405161007f929190610203565b6100757f000000000000000000000000000000000000000000000000000000000000000081565b6000546100fc906001610279565b6000555050565b6000606061010f610157565b6001848481818080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250959a92995091975050505050505050565b321561018f576040517fb60ac5db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b600080602083850312156101a457600080fd5b823567ffffffffffffffff808211156101bc57600080fd5b818501915085601f8301126101d057600080fd5b8135818111156101df57600080fd5b8660208285010111156101f157600080fd5b60209290920196919550909350505050565b821515815260006020604081840152835180604085015260005b818110156102395785810183015185820160600152820161021d565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b808201808211156102b3577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9291505056fea164736f6c6343000810000a", } var KeeperConsumerABI = KeeperConsumerMetaData.ABI diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index a14b461fa7a..6482c01cf88 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -1,10 +1,4 @@ GETH_VERSION: 1.12.0 -KeeperConsumer: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 53d7902867ce421641ffa9de63204b89ab9dc157b93f0beb9ac08c6450365a70 -KeeperConsumerPerformance: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 -PerformDataChecker: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 -UpkeepCounter: ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.abi ../../contracts/solc/v0.8.16/UpkeepCounter/UpkeepCounter.bin 77f000229a501f638dd2dc439859257f632894c728b31e68aea4f6d6c52f1b71 -UpkeepPerformCounterRestrictive: ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.abi ../../contracts/solc/v0.8.16/UpkeepPerformCounterRestrictive/UpkeepPerformCounterRestrictive.bin 20955b21acceb58355fa287b29194a73edf5937067ba7140667301017cb2b24c -VRFv2Consumer: ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.abi ../../contracts/solc/v0.8.6/VRFv2Consumer/VRFv2Consumer.bin 12368b3b5e06392440143a13b94c0ea2f79c4c897becc3b060982559e10ace40 aggregator_v2v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV2V3Interface.bin 95e8814b408bb05bf21742ef580d98698b7db6a9bac6a35c3de12b23aec4ee28 aggregator_v3_interface: ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.abi ../../contracts/solc/v0.8.6/AggregatorV2V3Interface/AggregatorV3Interface.bin 351b55d3b0f04af67db6dfb5c92f1c64479400ca1fec77afc20bc0ce65cb49ab authorized_forwarder: ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.abi ../../contracts/solc/v0.8.19/AuthorizedForwarder/AuthorizedForwarder.bin 8ea76c883d460f8353a45a493f2aebeb5a2d9a7b4619d1bc4fff5fb590bb3e10 @@ -24,14 +18,12 @@ cron_upkeep_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeep.ab dummy_protocol_wrapper: ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.bin 583a448170b13abf7ed64e406e8177d78c9e55ab44efd141eee60de23a71ee3b flags_wrapper: ../../contracts/solc/v0.6/Flags/Flags.abi ../../contracts/solc/v0.6/Flags/Flags.bin 2034d1b562ca37a63068851915e3703980276e8d5f7db6db8a3351a49d69fc4a flux_aggregator_wrapper: ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator.abi ../../contracts/solc/v0.6/FluxAggregator/FluxAggregator.bin a3b0a6396c4aa3b5ee39b3c4bd45efc89789d4859379a8a92caca3a0496c5794 -functions_billing_registry_events_mock: ../../contracts/solc/v0.8.6/FunctionsBillingRegistryEventsMock/FunctionsBillingRegistryEventsMock.abi ../../contracts/solc/v0.8.6/FunctionsBillingRegistryEventsMock/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 -functions_oracle_events_mock: ../../contracts/solc/v0.8.6/FunctionsOracleEventsMock/FunctionsOracleEventsMock.abi ../../contracts/solc/v0.8.6/FunctionsOracleEventsMock/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c gas_wrapper: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2/KeeperRegistryCheckUpkeepGasUsageWrapper1_2.bin 4a5dcdac486d18fcd58e3488c15c1710ae76b977556a3f3191bd269a4bc75723 gas_wrapper_mock: ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.bin a9b08f18da59125c6fc305855710241f3d35161b8b9f3e3f635a7b1d5c6da9c8 i_keeper_registry_master_wrapper_2_1: ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.abi ../../contracts/solc/v0.8.16/IKeeperRegistryMaster/IKeeperRegistryMaster.bin 6501bb9bcf5048bab2737b00685c6984a24867e234ddf5b60a65904eee9a4ebc i_log_automation: ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.abi ../../contracts/solc/v0.8.16/ILogAutomation/ILogAutomation.bin 296beccb6af655d6fc3a6e676b244831cce2da6688d3afc4f21f8738ae59e03e keeper_consumer_performance_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.abi ../../contracts/solc/v0.8.16/KeeperConsumerPerformance/KeeperConsumerPerformance.bin eeda39f5d3e1c8ffa0fb6cd1803731b98a4bc262d41833458e3fe8b40933ae90 -keeper_consumer_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 53d7902867ce421641ffa9de63204b89ab9dc157b93f0beb9ac08c6450365a70 +keeper_consumer_wrapper: ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.abi ../../contracts/solc/v0.8.16/KeeperConsumer/KeeperConsumer.bin 2c6163b145082fbab74b7343577a9cec8fda8b0da9daccf2a82581b1f5a84b83 keeper_registrar_wrapper1_2: ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2/KeeperRegistrar.bin e49b2f8b23da17af1ed2209b8ae0968cc04350554d636711e6c24a3ad3118692 keeper_registrar_wrapper1_2_mock: ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.abi ../../contracts/solc/v0.8.6/KeeperRegistrar1_2Mock/KeeperRegistrar1_2Mock.bin 5b155a7cb3def309fd7525de1d7cd364ebf8491bdc3060eac08ea0ff55ab29bc keeper_registrar_wrapper2_0: ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/KeeperRegistrar2_0.abi ../../contracts/solc/v0.8.6/KeeperRegistrar2_0/KeeperRegistrar2_0.bin 647f125c2f0dafabcdc545cb77b15dc2ec3ea9429357806813179b1fd555c2d2 @@ -84,7 +76,6 @@ vrf_coordinator_mock: ../../contracts/solc/v0.8.6/VRFCoordinatorMock/VRFCoordina vrf_coordinator_v2: ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2/VRFCoordinatorV2.bin 295f35ce282060317dfd01f45959f5a2b05ba26913e422fbd4fb6bf90b107006 vrf_coordinator_v2_5: ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2_5/VRFCoordinatorV2_5.bin b0e7c42a30b36d9d31fa9a3f26bad7937152e3dddee5bd8dd3d121390c879ab6 vrf_coordinator_v2_plus_v2_example: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus_V2Example/VRFCoordinatorV2Plus_V2Example.bin 4a5b86701983b1b65f0a8dfa116b3f6d75f8f706fa274004b57bdf5992e4cec3 -vrf_coordinator_v2plus: ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.6/VRFCoordinatorV2Plus.bin e4409bbe361258273458a5c99408b3d7f0cc57a2560dee91c0596cc6d6f738be vrf_coordinator_v2plus_interface: ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.abi ../../contracts/solc/v0.8.6/IVRFCoordinatorV2PlusInternal/IVRFCoordinatorV2PlusInternal.bin 834a2ce0e83276372a0e1446593fd89798f4cf6dc95d4be0113e99fadf61558b vrf_external_sub_owner_example: ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.abi ../../contracts/solc/v0.8.6/VRFExternalSubOwnerExample/VRFExternalSubOwnerExample.bin 14f888eb313930b50233a6f01ea31eba0206b7f41a41f6311670da8bb8a26963 vrf_load_test_external_sub_owner: ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.abi ../../contracts/solc/v0.8.6/VRFLoadTestExternalSubOwner/VRFLoadTestExternalSubOwner.bin 2097faa70265e420036cc8a3efb1f1e0836ad2d7323b295b9a26a125dbbe6c7d From 027068e06365716c615dcbbd7bdcf462e9a228c1 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 8 Nov 2023 11:33:29 -0600 Subject: [PATCH 108/271] bump deps (#11148) * bump deps - github.com/go-webauthn/webauthn v0.8.6 - github.com/prometheus/common v0.45.0 - github.com/prometheus/prometheus v0.47.2 - github.com/tidwall/gjson v1.17.0 - gonum.org/v1/gonum v0.14.0 * Use UnmarshalFirst to maintain compatibility --------- Co-authored-by: Ryan Tinianov --- core/cbor/cbor.go | 7 +++--- core/scripts/go.mod | 20 +++++++-------- core/scripts/go.sum | 53 +++++++++++++++++----------------------- go.mod | 20 +++++++-------- go.sum | 53 +++++++++++++++++----------------------- integration-tests/go.mod | 22 ++++++++--------- integration-tests/go.sum | 48 ++++++++++++++++-------------------- 7 files changed, 102 insertions(+), 121 deletions(-) diff --git a/core/cbor/cbor.go b/core/cbor/cbor.go index 754e5729345..cc3f74e423e 100644 --- a/core/cbor/cbor.go +++ b/core/cbor/cbor.go @@ -17,7 +17,7 @@ func ParseDietCBOR(b []byte) (map[string]interface{}, error) { b = autoAddMapDelimiters(b) var m map[interface{}]interface{} - if err := cbor.Unmarshal(b, &m); err != nil { + if _, err := cbor.UnmarshalFirst(b, &m); err != nil { return nil, err } @@ -38,7 +38,8 @@ func ParseDietCBOR(b []byte) (map[string]interface{}, error) { // "top-level map" requirement of "diet" CBOR. func ParseDietCBORToStruct(b []byte, v interface{}) error { b = autoAddMapDelimiters(b) - return cbor.Unmarshal(b, v) + _, err := cbor.UnmarshalFirst(b, v) + return err } // ParseStandardCBOR parses CBOR in "standards compliant" mode. @@ -49,7 +50,7 @@ func ParseStandardCBOR(b []byte) (a interface{}, err error) { if len(b) == 0 { return nil, nil } - if err = cbor.Unmarshal(b, &a); err != nil { + if _, err = cbor.UnmarshalFirst(b, &a); err != nil { return nil, err } return diff --git a/core/scripts/go.mod b/core/scripts/go.mod index b72d07978fd..aada3850db2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -106,7 +106,7 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gagliardetto/binary v0.7.1 // indirect github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 // indirect @@ -132,13 +132,13 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/revoke v0.1.9 // indirect - github.com/go-webauthn/webauthn v0.8.2 // indirect + github.com/go-webauthn/webauthn v0.8.6 // indirect + github.com/go-webauthn/x v0.1.4 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect @@ -146,7 +146,7 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/go-tpm v0.3.3 // indirect + github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect @@ -254,7 +254,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect github.com/minio/sha256-simd v0.1.1 // indirect @@ -289,9 +289,9 @@ require ( github.com/pressly/goose/v3 v3.15.1 // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.46.0 // indirect + github.com/prometheus/prometheus v0.47.2 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rjeczalik/notify v0.9.3 // indirect @@ -325,7 +325,7 @@ require ( github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/btree v1.6.0 // indirect - github.com/tidwall/gjson v1.16.0 // indirect + github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -367,7 +367,7 @@ require ( golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.14.0 // indirect - gonum.org/v1/gonum v0.13.0 // indirect + gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index b1e62010ab4..4f42c84d4ef 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -391,8 +391,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= @@ -478,10 +478,10 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-webauthn/revoke v0.1.9 h1:gSJ1ckA9VaKA2GN4Ukp+kiGTk1/EXtaDb1YE8RknbS0= -github.com/go-webauthn/revoke v0.1.9/go.mod h1:j6WKPnv0HovtEs++paan9g3ar46gm1NarktkXBaPR+w= -github.com/go-webauthn/webauthn v0.8.2 h1:8KLIbpldjz9KVGHfqEgJNbkhd7bbRXhNw4QWFJE15oA= -github.com/go-webauthn/webauthn v0.8.2/go.mod h1:d+ezx/jMCNDiqSMzOchuynKb9CVU1NM9BumOnokfcVQ= +github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= +github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= +github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -499,9 +499,12 @@ github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q8 github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= @@ -567,12 +570,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= -github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= -github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= -github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= -github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= -github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -626,7 +625,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -1180,8 +1178,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= @@ -1388,16 +1386,16 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.46.0 h1:9JSdXnsuT6YsbODEhSQMwxNkGwPExfmzqG73vCMk/Kw= -github.com/prometheus/prometheus v0.46.0/go.mod h1:10L5IJE5CEsjee1FnOcVswYXlPIscDWWt3IJ2UDYrz4= +github.com/prometheus/prometheus v0.47.2 h1:jWcnuQHz1o1Wu3MZ6nMJDuTI0kU5yJp9pkxh8XEkNvI= +github.com/prometheus/prometheus v0.47.2/go.mod h1:J/bmOSjgH7lFxz2gZhrWEZs2i64vMS+HIuZfmYNhJ/M= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= @@ -1507,7 +1505,6 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -1518,7 +1515,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= @@ -1565,8 +1561,8 @@ github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47 github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= -github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= @@ -1814,7 +1810,6 @@ golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1869,8 +1864,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1957,7 +1952,6 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2089,8 +2083,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -2176,7 +2170,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go. google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= diff --git a/go.mod b/go.mod index cd0fb0fab4e..e6af9533dc7 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/esote/minmaxheap v1.0.0 github.com/ethereum/go-ethereum v1.12.0 github.com/fatih/color v1.15.0 - github.com/fxamacker/cbor/v2 v2.4.0 + github.com/fxamacker/cbor/v2 v2.5.0 github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 github.com/getsentry/sentry-go v0.19.0 github.com/gin-contrib/cors v1.4.0 @@ -22,7 +22,7 @@ require ( github.com/gin-contrib/sessions v0.0.5 github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 - github.com/go-webauthn/webauthn v0.8.2 + github.com/go-webauthn/webauthn v0.8.6 github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 github.com/google/uuid v1.3.1 github.com/gorilla/securecookie v1.1.1 @@ -57,8 +57,8 @@ require ( github.com/pressly/goose/v3 v3.15.1 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.5.0 - github.com/prometheus/common v0.44.0 - github.com/prometheus/prometheus v0.46.0 + github.com/prometheus/common v0.45.0 + github.com/prometheus/prometheus v0.47.2 github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/go-internal v1.11.0 github.com/scylladb/go-reflectx v1.0.1 @@ -79,7 +79,7 @@ require ( github.com/spf13/cast v1.5.1 github.com/stretchr/testify v1.8.4 github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a - github.com/tidwall/gjson v1.16.0 + github.com/tidwall/gjson v1.17.0 github.com/ugorji/go/codec v1.2.11 github.com/ulule/limiter/v3 v3.11.2 github.com/umbracle/ethgo v0.1.3 @@ -96,7 +96,7 @@ require ( golang.org/x/text v0.13.0 golang.org/x/time v0.3.0 golang.org/x/tools v0.14.0 - gonum.org/v1/gonum v0.13.0 + gonum.org/v1/gonum v0.14.0 google.golang.org/grpc v1.58.3 google.golang.org/protobuf v1.31.0 gopkg.in/guregu/null.v2 v2.1.2 @@ -182,13 +182,13 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/revoke v0.1.9 // indirect + github.com/go-webauthn/x v0.1.4 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -196,7 +196,7 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/go-tpm v0.3.3 // indirect + github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/gorilla/context v1.1.1 // indirect @@ -287,7 +287,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect github.com/minio/sha256-simd v0.1.1 // indirect diff --git a/go.sum b/go.sum index fb97398f842..5c70c22a75e 100644 --- a/go.sum +++ b/go.sum @@ -386,8 +386,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= @@ -475,10 +475,10 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-webauthn/revoke v0.1.9 h1:gSJ1ckA9VaKA2GN4Ukp+kiGTk1/EXtaDb1YE8RknbS0= -github.com/go-webauthn/revoke v0.1.9/go.mod h1:j6WKPnv0HovtEs++paan9g3ar46gm1NarktkXBaPR+w= -github.com/go-webauthn/webauthn v0.8.2 h1:8KLIbpldjz9KVGHfqEgJNbkhd7bbRXhNw4QWFJE15oA= -github.com/go-webauthn/webauthn v0.8.2/go.mod h1:d+ezx/jMCNDiqSMzOchuynKb9CVU1NM9BumOnokfcVQ= +github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= +github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= +github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= @@ -496,9 +496,12 @@ github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q8 github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= @@ -564,12 +567,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= -github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= -github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= -github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= -github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= -github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -623,7 +622,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -1181,8 +1179,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= @@ -1391,16 +1389,16 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.46.0 h1:9JSdXnsuT6YsbODEhSQMwxNkGwPExfmzqG73vCMk/Kw= -github.com/prometheus/prometheus v0.46.0/go.mod h1:10L5IJE5CEsjee1FnOcVswYXlPIscDWWt3IJ2UDYrz4= +github.com/prometheus/prometheus v0.47.2 h1:jWcnuQHz1o1Wu3MZ6nMJDuTI0kU5yJp9pkxh8XEkNvI= +github.com/prometheus/prometheus v0.47.2/go.mod h1:J/bmOSjgH7lFxz2gZhrWEZs2i64vMS+HIuZfmYNhJ/M= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= @@ -1509,7 +1507,6 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -1520,7 +1517,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= @@ -1567,8 +1563,8 @@ github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a/go.mod h1:/sfW47 github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= -github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= @@ -1817,7 +1813,6 @@ golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1873,8 +1868,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1960,7 +1955,6 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2094,8 +2088,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -2181,7 +2175,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go. google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 2ac3c38a719..8fe63a46d26 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -142,7 +142,7 @@ require ( github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fvbommel/sortorder v1.0.2 // indirect - github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/fxamacker/cbor/v2 v2.5.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gagliardetto/binary v0.7.1 // indirect github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 // indirect @@ -174,15 +174,15 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/revoke v0.1.9 // indirect - github.com/go-webauthn/webauthn v0.8.2 // indirect + github.com/go-webauthn/webauthn v0.8.6 // indirect + github.com/go-webauthn/x v0.1.4 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect github.com/gogo/status v1.1.1 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -191,7 +191,7 @@ require ( github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/go-tpm v0.3.3 // indirect + github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect @@ -316,7 +316,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.55 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect @@ -369,11 +369,11 @@ require ( github.com/prometheus/alertmanager v0.25.1 // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/exporter-toolkit v0.10.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.46.0 // indirect + github.com/prometheus/prometheus v0.47.2 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect @@ -409,7 +409,7 @@ require ( github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/btree v1.6.0 // indirect - github.com/tidwall/gjson v1.16.0 // indirect + github.com/tidwall/gjson v1.17.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -456,14 +456,14 @@ require ( golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect golang.org/x/mod v0.13.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.14.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - gonum.org/v1/gonum v0.13.0 // indirect + gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ce5e51a5681..b8e8b205c87 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -994,8 +994,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo= github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= -github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= -github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gagliardetto/binary v0.6.1/go.mod h1:aOfYkc20U0deHaHn/LVZXiqlkDbFAX0FpTlDhsXa0S0= @@ -1134,10 +1134,10 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-webauthn/revoke v0.1.9 h1:gSJ1ckA9VaKA2GN4Ukp+kiGTk1/EXtaDb1YE8RknbS0= -github.com/go-webauthn/revoke v0.1.9/go.mod h1:j6WKPnv0HovtEs++paan9g3ar46gm1NarktkXBaPR+w= -github.com/go-webauthn/webauthn v0.8.2 h1:8KLIbpldjz9KVGHfqEgJNbkhd7bbRXhNw4QWFJE15oA= -github.com/go-webauthn/webauthn v0.8.2/go.mod h1:d+ezx/jMCNDiqSMzOchuynKb9CVU1NM9BumOnokfcVQ= +github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= +github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= +github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -1189,9 +1189,12 @@ github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRs github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= @@ -1266,12 +1269,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI= -github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw= -github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo= -github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4= -github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0= -github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= @@ -1351,7 +1350,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= @@ -1987,8 +1985,9 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= @@ -2262,8 +2261,8 @@ github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+ github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/exporter-toolkit v0.8.2/go.mod h1:00shzmJL7KxcsabLWcONwpyNEuWhREOnFqZW7vadFS0= @@ -2419,7 +2418,6 @@ github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -2430,7 +2428,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= @@ -2482,8 +2479,8 @@ github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1: github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= -github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -2801,7 +2798,6 @@ golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -2900,8 +2896,8 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= -golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -3007,7 +3003,6 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -3192,8 +3187,8 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= @@ -3407,7 +3402,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go. google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= From 4ccd1c5e63a398ae17b0ede50e3785630277a20d Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 8 Nov 2023 17:35:58 +0000 Subject: [PATCH 109/271] Randomize database names when using heavyweight ORM (#11233) --- core/chains/evm/logpoller/log_poller_test.go | 6 ++-- core/chains/evm/txmgr/broadcaster_test.go | 4 +-- core/cmd/shell_local_test.go | 6 ++-- core/internal/cltest/heavyweight/orm.go | 24 ++++++++------- core/internal/features/features_test.go | 15 +++++----- .../features/ocr2/features_ocr2_test.go | 11 ++++--- .../fluxmonitorv2/flux_monitor_test.go | 18 ++---------- .../fluxmonitorv2/integrations_test.go | 2 +- core/services/keeper/integration_test.go | 6 ++-- .../v1/internal/testutils.go | 7 ++--- .../ocr2/plugins/mercury/helpers_test.go | 2 +- .../evm21/logprovider/integration_test.go | 3 +- .../plugins/ocr2keeper/integration_21_test.go | 4 +-- .../plugins/ocr2keeper/integration_test.go | 11 ++++--- .../internal/ocr2vrf_integration_test.go | 2 +- core/services/pg/event_broadcaster_test.go | 2 +- core/services/pg/lease_lock_test.go | 4 +-- core/services/pipeline/orm_test.go | 14 ++++----- core/services/vrf/v1/integration_test.go | 5 ++-- core/services/vrf/v2/bhs_feeder_test.go | 2 +- .../vrf/v2/integration_helpers_test.go | 29 +++++++++---------- .../vrf/v2/integration_v2_plus_test.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 10 +++---- core/store/migrate/migrate_test.go | 10 +++---- 24 files changed, 92 insertions(+), 107 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 471c728cdd6..5f013ca9140 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -85,7 +85,7 @@ func populateDatabase(t testing.TB, o *logpoller.DbORM, chainID *big.Int) (commo func BenchmarkSelectLogsCreatedAfter(b *testing.B) { chainId := big.NewInt(137) - _, db := heavyweight.FullTestDBV2(b, "logs_scale", nil) + _, db := heavyweight.FullTestDBV2(b, nil) o := logpoller.NewORM(chainId, db, logger.TestLogger(b), pgtest.NewQConfig(false)) event, address, _ := populateDatabase(b, o, chainId) @@ -103,7 +103,7 @@ func BenchmarkSelectLogsCreatedAfter(b *testing.B) { func TestPopulateLoadedDB(t *testing.T) { t.Skip("Only for local load testing and query analysis") - _, db := heavyweight.FullTestDBV2(t, "logs_scale", nil) + _, db := heavyweight.FullTestDBV2(t, nil) chainID := big.NewInt(137) o := logpoller.NewORM(big.NewInt(137), db, logger.TestLogger(t), pgtest.NewQConfig(true)) @@ -1328,7 +1328,7 @@ func TestNotifyAfterInsert(t *testing.T) { // Use a non-transactional db for this test because notify events // are not delivered until the transaction is committed. var dbURL string - _, sqlxDB := heavyweight.FullTestDBV2(t, "notify_after_insert_log", func(c *chainlink.Config, s *chainlink.Secrets) { + _, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { dbURL = s.Database.URL.URL().String() }) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index ca2697ca99e..fcbc7a1f4c2 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -75,7 +75,7 @@ func NewTestEthBroadcaster( } func TestEthBroadcaster_Lifecycle(t *testing.T) { - cfg, db := heavyweight.FullTestDBV2(t, "eth_broadcaster_optimistic_locking", nil) + cfg, db := heavyweight.FullTestDBV2(t, nil) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) evmcfg := evmtest.NewChainScopedConfig(t, cfg) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -565,7 +565,7 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testing.T) { // non-transactional DB needed because we deliberately test for FK violation - cfg, db := heavyweight.FullTestDBV2(t, "eth_broadcaster_optimistic_locking", nil) + cfg, db := heavyweight.FullTestDBV2(t, nil) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) ccfg := evmtest.NewChainScopedConfig(t, cfg) evmcfg := txmgr.NewEvmTxmConfig(ccfg.EVM()) diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index a73e98a935b..4d906214ef4 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -280,7 +280,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { // Use a non-transactional db for this test because we need to // test multiple connections to the database, and changes made within // the transaction cannot be seen from another connection. - config, sqlxDB := heavyweight.FullTestDBV2(t, "rebroadcasttransactions", func(c *chainlink.Config, s *chainlink.Secrets) { + config, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres // evm config is used in this test. but if set, it must be pass config validation. // simplest to make it nil @@ -359,7 +359,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { // Use the non-transactional db for this test because we need to // test multiple connections to the database, and changes made within // the transaction cannot be seen from another connection. - config, sqlxDB := heavyweight.FullTestDBV2(t, "rebroadcasttransactions_outsiderange", func(c *chainlink.Config, s *chainlink.Secrets) { + config, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres // evm config is used in this test. but if set, it must be pass config validation. // simplest to make it nil @@ -437,7 +437,7 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - config, sqlxDB := heavyweight.FullTestDBV2(t, "rebroadcasttransactions_outsiderange", func(c *chainlink.Config, s *chainlink.Secrets) { + config, sqlxDB := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres c.EVM = nil diff --git a/core/internal/cltest/heavyweight/orm.go b/core/internal/cltest/heavyweight/orm.go index 2f9370f35a6..b46e7114cf3 100644 --- a/core/internal/cltest/heavyweight/orm.go +++ b/core/internal/cltest/heavyweight/orm.go @@ -7,13 +7,14 @@ import ( "database/sql" "errors" "fmt" - "math/rand" "net/url" "os" "path" "runtime" + "strings" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,21 +31,25 @@ import ( // FullTestDBV2 creates a pristine DB which runs in a separate database than the normal // unit tests, so you can do things like use other Postgres connection types with it. -func FullTestDBV2(t testing.TB, name string, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - return prepareFullTestDBV2(t, name, false, true, overrideFn) +func FullTestDBV2(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { + return prepareFullTestDBV2(t, false, true, overrideFn) } // FullTestDBNoFixturesV2 is the same as FullTestDB, but it does not load fixtures. -func FullTestDBNoFixturesV2(t testing.TB, name string, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - return prepareFullTestDBV2(t, name, false, false, overrideFn) +func FullTestDBNoFixturesV2(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { + return prepareFullTestDBV2(t, false, false, overrideFn) } // FullTestDBEmptyV2 creates an empty DB (without migrations). -func FullTestDBEmptyV2(t testing.TB, name string, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { - return prepareFullTestDBV2(t, name, true, false, overrideFn) +func FullTestDBEmptyV2(t testing.TB, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { + return prepareFullTestDBV2(t, true, false, overrideFn) } -func prepareFullTestDBV2(t testing.TB, name string, empty bool, loadFixtures bool, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { +func generateName() string { + return strings.ReplaceAll(uuid.New().String(), "-", "") +} + +func prepareFullTestDBV2(t testing.TB, empty bool, loadFixtures bool, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) (chainlink.GeneralConfig, *sqlx.DB) { testutils.SkipShort(t, "FullTestDB") if empty && loadFixtures { @@ -59,8 +64,7 @@ func prepareFullTestDBV2(t testing.TB, name string, empty bool, loadFixtures boo }) require.NoError(t, os.MkdirAll(gcfg.RootDir(), 0700)) - name = fmt.Sprintf("%s_%x", name, rand.Intn(0xFFF)) // to avoid name collisions - migrationTestDBURL, err := dropAndCreateThrowawayTestDB(gcfg.Database().URL(), name, empty) + migrationTestDBURL, err := dropAndCreateThrowawayTestDB(gcfg.Database().URL(), generateName(), empty) require.NoError(t, err) db, err := pg.NewConnection(migrationTestDBURL, dialects.Postgres, gcfg.Database()) require.NoError(t, err) diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 23451bf29fe..b5f42d8bf3e 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -675,11 +675,11 @@ func setupOCRContracts(t *testing.T) (*bind.TransactOpts, *backends.SimulatedBac return owner, b, ocrContractAddress, ocrContract, flagsContract, flagsContractAddress } -func setupNode(t *testing.T, owner *bind.TransactOpts, portV1, portV2 int, dbName string, +func setupNode(t *testing.T, owner *bind.TransactOpts, portV1, portV2 int, b *backends.SimulatedBackend, ns ocrnetworking.NetworkingStack, overrides func(c *chainlink.Config, s *chainlink.Secrets), ) (*cltest.TestApplication, string, common.Address, ocrkey.KeyV2) { p2pKey := keystest.NewP2PKeyV2(t) - config, _ := heavyweight.FullTestDBV2(t, dbName, func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) // Disables ocr spec validation so we can have fast polling for the test. c.OCR.Enabled = ptr(true) @@ -748,7 +748,6 @@ func setupForwarderEnabledNode( owner *bind.TransactOpts, portV1, portV2 int, - dbName string, b *backends.SimulatedBackend, ns ocrnetworking.NetworkingStack, overrides func(c *chainlink.Config, s *chainlink.Secrets), @@ -760,7 +759,7 @@ func setupForwarderEnabledNode( ocrkey.KeyV2, ) { p2pKey := keystest.NewP2PKeyV2(t) - config, _ := heavyweight.FullTestDBV2(t, dbName, func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) // Disables ocr spec validation so we can have fast polling for the test. c.OCR.Enabled = ptr(true) @@ -867,7 +866,7 @@ func TestIntegration_OCR(t *testing.T) { // Note it's plausible these ports could be occupied on a CI machine. // May need a port randomize + retry approach if we observe collisions. - appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV1, bootstrapNodePortV2, fmt.Sprintf("b_%d", test.id), b, test.ns, nil) + appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV1, bootstrapNodePortV2, b, test.ns, nil) var ( oracles []confighelper.OracleIdentityExtra transmitters []common.Address @@ -878,7 +877,7 @@ func TestIntegration_OCR(t *testing.T) { for i := 0; i < numOracles; i++ { portV1 := ports[2*i] portV2 := ports[2*i+1] - app, peerID, transmitter, key := setupNode(t, owner, portV1, portV2, fmt.Sprintf("o%d_%d", i, test.id), b, test.ns, func(c *chainlink.Config, s *chainlink.Secrets) { + app, peerID, transmitter, key := setupNode(t, owner, portV1, portV2, b, test.ns, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FlagsContractAddress = ptr(ethkey.EIP55AddressFromAddress(flagsContractAddress)) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(test.eip1559) if test.ns != ocrnetworking.NetworkingStackV1 { @@ -1092,7 +1091,7 @@ func TestIntegration_OCR_ForwarderFlow(t *testing.T) { // Note it's plausible these ports could be occupied on a CI machine. // May need a port randomize + retry approach if we observe collisions. - appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV1, bootstrapNodePortV2, fmt.Sprintf("b_%d", 1), b, ocrnetworking.NetworkingStackV2, nil) + appBootstrap, bootstrapPeerID, _, _ := setupNode(t, owner, bootstrapNodePortV1, bootstrapNodePortV2, b, ocrnetworking.NetworkingStackV2, nil) var ( oracles []confighelper.OracleIdentityExtra @@ -1105,7 +1104,7 @@ func TestIntegration_OCR_ForwarderFlow(t *testing.T) { for i := 0; i < numOracles; i++ { portV1 := ports[2*i] portV2 := ports[2*i+1] - app, peerID, transmitter, forwarder, key := setupForwarderEnabledNode(t, owner, portV1, portV2, fmt.Sprintf("o%d_%d", i, 1), b, ocrnetworking.NetworkingStackV2, func(c *chainlink.Config, s *chainlink.Secrets) { + app, peerID, transmitter, forwarder, key := setupForwarderEnabledNode(t, owner, portV1, portV2, b, ocrnetworking.NetworkingStackV2, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = ptr(true) c.EVM[0].FlagsContractAddress = ptr(ethkey.EIP55AddressFromAddress(flagsContractAddress)) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 25b6781c4e9..3e220935685 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -105,13 +105,12 @@ func setupNodeOCR2( t *testing.T, owner *bind.TransactOpts, port int, - dbName string, useForwarder bool, b *backends.SimulatedBackend, p2pV2Bootstrappers []commontypes.BootstrapperLocator, ) *ocr2Node { p2pKey := keystest.NewP2PKeyV2(t) - config, _ := heavyweight.FullTestDBV2(t, fmt.Sprintf("%s%d", dbName, port), func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) // Disables ocr spec validation so we can have fast polling for the test. c.Feature.LogPoller = ptr(true) @@ -193,7 +192,7 @@ func TestIntegration_OCR2(t *testing.T) { lggr := logger.TestLogger(t) bootstrapNodePort := freeport.GetOne(t) - bootstrapNode := setupNodeOCR2(t, owner, bootstrapNodePort, "bootstrap", false /* useForwarders */, b, nil) + bootstrapNode := setupNodeOCR2(t, owner, bootstrapNodePort, false /* useForwarders */, b, nil) var ( oracles []confighelper2.OracleIdentityExtra @@ -203,7 +202,7 @@ func TestIntegration_OCR2(t *testing.T) { ) ports := freeport.GetN(t, 4) for i := 0; i < 4; i++ { - node := setupNodeOCR2(t, owner, ports[i], fmt.Sprintf("oracle%d", i), false /* useForwarders */, b, []commontypes.BootstrapperLocator{ + node := setupNodeOCR2(t, owner, ports[i], false /* useForwarders */, b, []commontypes.BootstrapperLocator{ // Supply the bootstrap IP and port as a V2 peer address {PeerID: bootstrapNode.peerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, }) @@ -477,7 +476,7 @@ func TestIntegration_OCR2_ForwarderFlow(t *testing.T) { lggr := logger.TestLogger(t) bootstrapNodePort := freeport.GetOne(t) - bootstrapNode := setupNodeOCR2(t, owner, bootstrapNodePort, "bootstrap", true /* useForwarders */, b, nil) + bootstrapNode := setupNodeOCR2(t, owner, bootstrapNodePort, true /* useForwarders */, b, nil) var ( oracles []confighelper2.OracleIdentityExtra @@ -488,7 +487,7 @@ func TestIntegration_OCR2_ForwarderFlow(t *testing.T) { ) ports := freeport.GetN(t, 4) for i := uint16(0); i < 4; i++ { - node := setupNodeOCR2(t, owner, ports[i], fmt.Sprintf("oracle%d", i), true /* useForwarders */, b, []commontypes.BootstrapperLocator{ + node := setupNodeOCR2(t, owner, ports[i], true /* useForwarders */, b, []commontypes.BootstrapperLocator{ // Supply the bootstrap IP and port as a V2 peer address {PeerID: bootstrapNode.peerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, }) diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index 0d1eb085a84..26ec520e7cd 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -3,7 +3,6 @@ package fluxmonitorv2_test import ( "fmt" "math/big" - "strings" "testing" "time" @@ -26,7 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -284,8 +282,8 @@ func setupStoreWithKey(t *testing.T) (*sqlx.DB, common.Address) { } // setupStoreWithKey setups a new store and adds a key to the keystore -func setupFullDBWithKey(t *testing.T, name string) (*sqlx.DB, common.Address) { - cfg, db := heavyweight.FullTestDBV2(t, name, nil) +func setupFullDBWithKey(t *testing.T) (*sqlx.DB, common.Address) { + cfg, db := heavyweight.FullTestDBV2(t, nil) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() _, nodeAddr := cltest.MustInsertRandomKey(t, ethKeyStore) @@ -906,18 +904,8 @@ func TestFluxMonitor_HibernationTickerFiresMultipleTimes(t *testing.T) { g.Eventually(func() int { return len(pollOccured) }, testutils.WaitTimeout(t)).Should(gomega.Equal(3)) } -// chainlink_test_TestFluxMonitor_HibernationIsEnteredAndRetryTickerStopped -// 63 bytes is max and chainlink_test_ takes up 15, plus 4 for a random hex suffix. -func dbName(s string) string { - diff := len(cmd.TestDBNamePrefix) + len("_FFF") - if len(s) <= diff { - return strings.ReplaceAll(strings.ToLower(s), "/", "") - } - return strings.ReplaceAll(strings.ToLower(s[len(s)-diff:]), "/", "") -} - func TestFluxMonitor_HibernationIsEnteredAndRetryTickerStopped(t *testing.T) { - db, nodeAddr := setupFullDBWithKey(t, "hibernation") + db, nodeAddr := setupFullDBWithKey(t) oracles := []common.Address{nodeAddr, testutils.NewAddress()} const ( diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 2c45ed5ad89..b2f24e08d54 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -206,7 +206,7 @@ func startApplication( fa fluxAggregatorUniverse, overrides func(c *chainlink.Config, s *chainlink.Secrets), ) *cltest.TestApplication { - config, _ := heavyweight.FullTestDBV2(t, dbName(t.Name()), overrides) + config, _ := heavyweight.FullTestDBV2(t, overrides) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, fa.backend, fa.key) require.NoError(t, app.Start(testutils.Context(t))) return app diff --git a/core/services/keeper/integration_test.go b/core/services/keeper/integration_test.go index 39431063bcd..f76ef935741 100644 --- a/core/services/keeper/integration_test.go +++ b/core/services/keeper/integration_test.go @@ -236,7 +236,7 @@ func TestKeeperEthIntegration(t *testing.T) { backend.Commit() // setup app - config, db := heavyweight.FullTestDBV2(t, fmt.Sprintf("keeper_eth_integration_%s", test.name), func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = &test.eip1559 c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps c.Keeper.Registry.SyncInterval = models.MustNewDuration(24 * time.Hour) // disable full sync ticker for test @@ -393,7 +393,7 @@ func TestKeeperForwarderEthIntegration(t *testing.T) { backend.Commit() // setup app - config, db := heavyweight.FullTestDBV2(t, "keeper_forwarder_flow", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = ptr(true) c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps @@ -540,7 +540,7 @@ func TestMaxPerformDataSize(t *testing.T) { backend.Commit() // setup app - config, db := heavyweight.FullTestDBV2(t, "keeper_max_perform_data_test", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Keeper.MaxGracePeriod = ptr[int64](0) // avoid waiting to re-submit for upkeeps c.Keeper.Registry.SyncInterval = models.MustNewDuration(24 * time.Hour) // disable full sync ticker for test c.Keeper.Registry.MaxPerformDataSize = ptr(uint32(maxPerformDataSize)) // set the max perform data size diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index 5c824323eb6..9f63d60eef6 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -302,7 +302,6 @@ func StartNewNode( t *testing.T, owner *bind.TransactOpts, port int, - dbName string, b *backends.SimulatedBackend, maxGas uint32, p2pV2Bootstrappers []commontypes.BootstrapperLocator, @@ -310,7 +309,7 @@ func StartNewNode( thresholdKeyShare string, ) *Node { p2pKey := keystest.NewP2PKeyV2(t) - config, _ := heavyweight.FullTestDBV2(t, fmt.Sprintf("%s%d", dbName, port), func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) c.Feature.LogPoller = ptr(true) @@ -550,7 +549,7 @@ func CreateFunctionsNodes( } bootstrapPort := freeport.GetOne(t) - bootstrapNode = StartNewNode(t, owner, bootstrapPort, "bootstrap", b, uint32(maxGas), nil, nil, "") + bootstrapNode = StartNewNode(t, owner, bootstrapPort, b, uint32(maxGas), nil, nil, "") AddBootstrapJob(t, bootstrapNode.App, routerAddress) // oracle nodes with jobs, bridges and mock EAs @@ -568,7 +567,7 @@ func CreateFunctionsNodes( } else { ocr2Keystore = ocr2Keystores[i] } - oracleNode := StartNewNode(t, owner, ports[i], fmt.Sprintf("oracle%d", i), b, uint32(maxGas), []commontypes.BootstrapperLocator{ + oracleNode := StartNewNode(t, owner, ports[i], b, uint32(maxGas), []commontypes.BootstrapperLocator{ {PeerID: bootstrapNode.PeerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapPort)}}, }, ocr2Keystore, thresholdKeyShare) oracleNodes = append(oracleNodes, oracleNode.App) diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index 60904b58139..588f772120e 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -163,7 +163,7 @@ func setupNode( p2paddresses := []string{fmt.Sprintf("127.0.0.1:%d", port)} - config, _ := heavyweight.FullTestDBV2(t, fmt.Sprintf("%s%d", dbName, port), func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { // [JobPipeline] // MaxSuccessfulRuns = 0 c.JobPipeline.MaxSuccessfulRuns = ptr(uint64(0)) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index dad35420398..63ed4114b8e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -3,7 +3,6 @@ package logprovider_test import ( "context" "errors" - "fmt" "math/big" "testing" "time" @@ -693,7 +692,7 @@ func setupBackend(t *testing.T) (*backends.SimulatedBackend, func(), []*bind.Tra func ptr[T any](v T) *T { return &v } func setupDB(t *testing.T) *sqlx.DB { - _, db := heavyweight.FullTestDBV2(t, fmt.Sprintf("%s%d", "chainlink_test", 5432), func(c *chainlink.Config, s *chainlink.Secrets) { + _, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = ptr(true) c.OCR.Enabled = ptr(false) diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index 15280de73cf..562f972bc42 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -479,7 +479,7 @@ func setupNodes(t *testing.T, nodeKeys [5]ethkey.KeyV2, registry *iregistry21.IK // Setup bootstrap + oracle nodes bootstrapNodePort := freeport.GetOne(t) - appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNode(t, bootstrapNodePort, "bootstrap_keeper_ocr", nodeKeys[0], backend, nil, mServer) + appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNode(t, bootstrapNodePort, nodeKeys[0], backend, nil, mServer) bootstrapNode := Node{ appBootstrap, bootstrapTransmitter, bootstrapKb, } @@ -490,7 +490,7 @@ func setupNodes(t *testing.T, nodeKeys [5]ethkey.KeyV2, registry *iregistry21.IK // Set up the minimum 4 oracles all funded ports := freeport.GetN(t, 4) for i := 0; i < 4; i++ { - app, peerID, transmitter, kb := setupNode(t, ports[i], fmt.Sprintf("oracle_keeper%d", i), nodeKeys[i+1], backend, []commontypes.BootstrapperLocator{ + app, peerID, transmitter, kb := setupNode(t, ports[i], nodeKeys[i+1], backend, []commontypes.BootstrapperLocator{ // Supply the bootstrap IP and port as a V2 peer address {PeerID: bootstrapPeerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, }, mServer) diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index 7c881a18eb9..f50321631ce 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -111,7 +111,6 @@ func deployKeeper20Registry( func setupNode( t *testing.T, port int, - dbName string, nodeKey ethkey.KeyV2, backend *backends.SimulatedBackend, p2pV2Bootstrappers []commontypes.BootstrapperLocator, @@ -119,7 +118,7 @@ func setupNode( ) (chainlink.Application, string, common.Address, ocr2key.KeyBundle) { p2pKey := keystest.NewP2PKeyV2(t) p2paddresses := []string{fmt.Sprintf("127.0.0.1:%d", port)} - cfg, _ := heavyweight.FullTestDBV2(t, fmt.Sprintf("%s%d", dbName, port), func(c *chainlink.Config, s *chainlink.Secrets) { + cfg, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Feature.LogPoller = ptr(true) c.OCR.Enabled = ptr(false) @@ -240,7 +239,7 @@ func TestIntegration_KeeperPluginBasic(t *testing.T) { // Setup bootstrap + oracle nodes bootstrapNodePort := freeport.GetOne(t) - appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNode(t, bootstrapNodePort, "bootstrap_keeper_ocr", nodeKeys[0], backend, nil, NewSimulatedMercuryServer()) + appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNode(t, bootstrapNodePort, nodeKeys[0], backend, nil, NewSimulatedMercuryServer()) bootstrapNode := Node{ appBootstrap, bootstrapTransmitter, bootstrapKb, } @@ -251,7 +250,7 @@ func TestIntegration_KeeperPluginBasic(t *testing.T) { // Set up the minimum 4 oracles all funded ports := freeport.GetN(t, 4) for i := 0; i < 4; i++ { - app, peerID, transmitter, kb := setupNode(t, ports[i], fmt.Sprintf("oracle_keeper%d", i), nodeKeys[i+1], backend, []commontypes.BootstrapperLocator{ + app, peerID, transmitter, kb := setupNode(t, ports[i], nodeKeys[i+1], backend, []commontypes.BootstrapperLocator{ // Supply the bootstrap IP and port as a V2 peer address {PeerID: bootstrapPeerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, }, NewSimulatedMercuryServer()) @@ -501,7 +500,7 @@ func TestIntegration_KeeperPluginForwarderEnabled(t *testing.T) { effectiveTransmitters := make([]common.Address, 0) // Setup bootstrap + oracle nodes bootstrapNodePort := freeport.GetOne(t) - appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNode(t, bootstrapNodePort, "bootstrap_keeper_ocr", nodeKeys[0], backend, nil, NewSimulatedMercuryServer()) + appBootstrap, bootstrapPeerID, bootstrapTransmitter, bootstrapKb := setupNode(t, bootstrapNodePort, nodeKeys[0], backend, nil, NewSimulatedMercuryServer()) bootstrapNode := Node{ appBootstrap, bootstrapTransmitter, bootstrapKb, @@ -513,7 +512,7 @@ func TestIntegration_KeeperPluginForwarderEnabled(t *testing.T) { // Set up the minimum 4 oracles all funded ports := freeport.GetN(t, 4) for i := 0; i < 4; i++ { - app, peerID, transmitter, kb := setupNode(t, ports[i], fmt.Sprintf("oracle_keeper%d", i), nodeKeys[i+1], backend, []commontypes.BootstrapperLocator{ + app, peerID, transmitter, kb := setupNode(t, ports[i], nodeKeys[i+1], backend, []commontypes.BootstrapperLocator{ // Supply the bootstrap IP and port as a V2 peer address {PeerID: bootstrapPeerID, Addrs: []string{fmt.Sprintf("127.0.0.1:%d", bootstrapNodePort)}}, }, NewSimulatedMercuryServer()) diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index cf7a408725d..0dbb6a5915e 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -226,7 +226,7 @@ func setupNodeOCR2( p2pV2Bootstrappers []commontypes.BootstrapperLocator, ) *ocr2Node { p2pKey := keystest.NewP2PKeyV2(t) - config, _ := heavyweight.FullTestDBV2(t, fmt.Sprintf("%s%d", dbName, port), func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Insecure.OCRDevelopmentMode = ptr(true) // Disables ocr spec validation so we can have fast polling for the test. c.Feature.LogPoller = ptr(true) diff --git a/core/services/pg/event_broadcaster_test.go b/core/services/pg/event_broadcaster_test.go index bea7dfb5a85..a82e26e0589 100644 --- a/core/services/pg/event_broadcaster_test.go +++ b/core/services/pg/event_broadcaster_test.go @@ -16,7 +16,7 @@ import ( ) func TestEventBroadcaster(t *testing.T) { - config, _ := heavyweight.FullTestDBNoFixturesV2(t, "event_broadcaster", nil) + config, _ := heavyweight.FullTestDBNoFixturesV2(t, nil) eventBroadcaster := cltest.NewEventBroadcaster(t, config.Database().URL()) require.NoError(t, eventBroadcaster.Start(testutils.Context(t))) diff --git a/core/services/pg/lease_lock_test.go b/core/services/pg/lease_lock_test.go index 9f857ffa20b..483e03e0039 100644 --- a/core/services/pg/lease_lock_test.go +++ b/core/services/pg/lease_lock_test.go @@ -24,7 +24,7 @@ func newLeaseLock(t *testing.T, db *sqlx.DB, cfg pg.LeaseLockConfig) pg.LeaseLoc } func Test_LeaseLock(t *testing.T) { - cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "leaselock", func(c *chainlink.Config, s *chainlink.Secrets) { + cfg, db := heavyweight.FullTestDBNoFixturesV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { t := true c.Database.Lock.Enabled = &t }) @@ -207,7 +207,7 @@ func Test_LeaseLock(t *testing.T) { require.NoError(t, db.Close()) t.Run("on virgin database", func(t *testing.T) { - _, db := heavyweight.FullTestDBEmptyV2(t, "leaselock", nil) + _, db := heavyweight.FullTestDBEmptyV2(t, nil) cfg := pg.LeaseLockConfig{ DefaultQueryTimeout: cfg.Database().DefaultQueryTimeout(), LeaseDuration: 15 * time.Second, diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index f916c24f0a6..295ad20a007 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -33,11 +33,11 @@ type ormconfig struct { func (ormconfig) JobPipelineMaxSuccessfulRuns() uint64 { return 123456 } -func setupORM(t *testing.T, name string) (db *sqlx.DB, orm pipeline.ORM) { +func setupORM(t *testing.T, heavy bool) (db *sqlx.DB, orm pipeline.ORM) { t.Helper() - if name != "" { - _, db = heavyweight.FullTestDBV2(t, name, nil) + if heavy { + _, db = heavyweight.FullTestDBV2(t, nil) } else { db = pgtest.NewSqlxDB(t) } @@ -47,12 +47,12 @@ func setupORM(t *testing.T, name string) (db *sqlx.DB, orm pipeline.ORM) { return } -func setupHeavyORM(t *testing.T, name string) (db *sqlx.DB, orm pipeline.ORM) { - return setupORM(t, name) +func setupHeavyORM(t *testing.T) (db *sqlx.DB, orm pipeline.ORM) { + return setupORM(t, true) } func setupLiteORM(t *testing.T) (db *sqlx.DB, orm pipeline.ORM) { - return setupORM(t, "") + return setupORM(t, false) } func Test_PipelineORM_CreateSpec(t *testing.T) { @@ -464,7 +464,7 @@ func Test_PipelineORM_DeleteRun(t *testing.T) { } func Test_PipelineORM_DeleteRunsOlderThan(t *testing.T) { - _, orm := setupHeavyORM(t, "pipeline_runs_reaper") + _, orm := setupHeavyORM(t) var runsIds []int64 diff --git a/core/services/vrf/v1/integration_test.go b/core/services/vrf/v1/integration_test.go index b7e6be43183..a7dca56776f 100644 --- a/core/services/vrf/v1/integration_test.go +++ b/core/services/vrf/v1/integration_test.go @@ -2,7 +2,6 @@ package v1_test import ( "encoding/hex" - "fmt" "math/big" "strings" "testing" @@ -46,7 +45,7 @@ func TestIntegration_VRF_JPV2(t *testing.T) { for _, tt := range tests { test := tt t.Run(test.name, func(t *testing.T) { - config, _ := heavyweight.FullTestDBV2(t, fmt.Sprintf("vrf_jpv2_%v", test.eip1559), func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = &test.eip1559 c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) }) @@ -129,7 +128,7 @@ func TestIntegration_VRF_JPV2(t *testing.T) { func TestIntegration_VRF_WithBHS(t *testing.T) { t.Parallel() - config, _ := heavyweight.FullTestDBV2(t, "vrf_with_bhs", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.EIP1559DynamicFees = ptr(true) c.EVM[0].BlockBackfillDepth = ptr[uint32](500) c.Feature.LogPoller = ptr(true) diff --git a/core/services/vrf/v2/bhs_feeder_test.go b/core/services/vrf/v2/bhs_feeder_test.go index 0da28378d01..219fe1c8fd2 100644 --- a/core/services/vrf/v2/bhs_feeder_test.go +++ b/core/services/vrf/v2/bhs_feeder_test.go @@ -51,7 +51,7 @@ func TestStartHeartbeats(t *testing.T) { keys = append(keys, ownerKey, vrfKey) - config, _ := heavyweight.FullTestDBV2(t, "vrfv2_needs_blockhash_store", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, gasLanePriceWei, keySpecificOverrides...)(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index 60a7cd18d20..a086cbbb09f 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -1,7 +1,6 @@ package v2_test import ( - "fmt" "math/big" "strings" "testing" @@ -62,7 +61,7 @@ func testSingleConsumerHappyPath( key1 := cltest.MustGenerateRandomKey(t) key2 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_happypath", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -202,7 +201,7 @@ func testMultipleConsumersNeedBHS( GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, }) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_needs_blockhash_store", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), keySpecificOverrides...)(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) @@ -349,7 +348,7 @@ func testMultipleConsumersNeedTrustedBHS( uni.backend.Commit() } - config, db := heavyweight.FullTestDBV2(t, "vrfv2_needs_trusted_blockhash_store", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), keySpecificOverrides...)(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(5_000_000)) @@ -531,7 +530,7 @@ func testSingleConsumerHappyPathBatchFulfillment( ) { key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_batch_happypath", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -635,7 +634,7 @@ func testSingleConsumerNeedsTopUp( ) { key := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(1000) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_needstopup", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(1000), toml.KeySpecific{ // Gas lane. Key: ptr(key.EIP55Address), @@ -739,7 +738,7 @@ func testBlockHeaderFeeder( gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_test_block_header_feeder", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, gasLanePriceWei, toml.KeySpecific{ // Gas lane. Key: ptr(vrfKey.EIP55Address), @@ -894,7 +893,7 @@ func testSingleConsumerForcedFulfillment( key1 := cltest.MustGenerateRandomKey(t) key2 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, fmt.Sprintf("vrfv2_singleconsumer_forcefulfill_%v", batchEnabled), func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1061,7 +1060,7 @@ func testSingleConsumerEIP150( key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, _ := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_eip150_happypath", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), v2.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1129,7 +1128,7 @@ func testSingleConsumerEIP150Revert( key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, _ := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_eip150_revert", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), v2.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1192,7 +1191,7 @@ func testSingleConsumerBigGasCallbackSandwich( ) { key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(100) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_bigcallback_sandwich", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(100), v2.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1308,7 +1307,7 @@ func testSingleConsumerMultipleGasLanes( expensiveKey := cltest.MustGenerateRandomKey(t) cheapGasLane := assets.GWei(10) expensiveGasLane := assets.GWei(1000) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_multiplegaslanes", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), v2.KeySpecific{ // Cheap gas lane. Key: ptr(cheapKey.EIP55Address), @@ -1428,7 +1427,7 @@ func testSingleConsumerAlwaysRevertingCallbackStillFulfilled( ) { key := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_alwaysrevertingcallback", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), v2.KeySpecific{ // Gas lane. Key: ptr(key.EIP55Address), @@ -1496,7 +1495,7 @@ func testConsumerProxyHappyPath( key1 := cltest.MustGenerateRandomKey(t) key2 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_consumerproxy_happypath", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), v2.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1624,7 +1623,7 @@ func testMaliciousConsumer( batchEnabled bool, vrfVersion vrfcommon.Version, ) { - config, _ := heavyweight.FullTestDBV2(t, "vrf_v2plus_integration_malicious", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](2_000_000) c.EVM[0].GasEstimator.PriceMax = assets.GWei(1) c.EVM[0].GasEstimator.PriceDefault = assets.GWei(1) diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 094d7d060e4..75026423f4b 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -1141,7 +1141,7 @@ func TestVRFV2PlusIntegration_Migration(t *testing.T) { uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2plus_migration", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 3a691ec2e2a..e50ed91491b 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -967,7 +967,7 @@ func testEoa( key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, _ := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_eoa_request", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1128,7 +1128,7 @@ func TestVRFV2Integration_SingleConsumer_Wrapper(t *testing.T) { callBackGasLimit := int64(100_000) // base callback gas. key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_singleconsumer_wrapper", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1208,7 +1208,7 @@ func TestVRFV2Integration_Wrapper_High_Gas(t *testing.T) { key1 := cltest.MustGenerateRandomKey(t) callBackGasLimit := int64(2_000_000) // base callback gas. gasLanePriceWei := assets.GWei(10) - config, db := heavyweight.FullTestDBV2(t, "vrfv2_wrapper_high_gas_revert", func(c *chainlink.Config, s *chainlink.Secrets) { + config, db := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, assets.GWei(10), toml.KeySpecific{ // Gas lane. Key: ptr(key1.EIP55Address), @@ -1585,7 +1585,7 @@ func TestIntegrationVRFV2(t *testing.T) { gasPrice := assets.GWei(1) key := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) - config, _ := heavyweight.FullTestDBV2(t, "vrf_v2_integration", func(c *chainlink.Config, s *chainlink.Secrets) { + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { simulatedOverrides(t, gasPrice, toml.KeySpecific{ Key: &key.EIP55Address, GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, @@ -2003,7 +2003,7 @@ func TestFulfillmentCost(t *testing.T) { } func TestStartingCountsV1(t *testing.T) { - cfg, db := heavyweight.FullTestDBNoFixturesV2(t, "vrf_test_starting_counts", nil) + cfg, db := heavyweight.FullTestDBNoFixturesV2(t, nil) lggr := logger.TestLogger(t) qCfg := pgtest.NewQConfig(false) diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index fe218589d2d..ef105c75ff6 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -73,7 +73,7 @@ func getOCR2Spec100() OffchainReporting2OracleSpec100 { } func TestMigrate_0100_BootstrapConfigs(t *testing.T) { - cfg, db := heavyweight.FullTestDBEmptyV2(t, migrationDir, nil) + cfg, db := heavyweight.FullTestDBEmptyV2(t, nil) lggr := logger.TestLogger(t) err := goose.UpTo(db.DB, migrationDir, 99) require.NoError(t, err) @@ -342,7 +342,7 @@ ON jobs.offchainreporting2_oracle_spec_id = ocr2.id` } func TestMigrate_101_GenericOCR2(t *testing.T) { - _, db := heavyweight.FullTestDBEmptyV2(t, migrationDir, nil) + _, db := heavyweight.FullTestDBEmptyV2(t, nil) err := goose.UpTo(db.DB, migrationDir, 100) require.NoError(t, err) @@ -392,7 +392,7 @@ func TestMigrate_101_GenericOCR2(t *testing.T) { func TestMigrate(t *testing.T) { lggr := logger.TestLogger(t) - _, db := heavyweight.FullTestDBEmptyV2(t, migrationDir, nil) + _, db := heavyweight.FullTestDBEmptyV2(t, nil) err := goose.UpTo(db.DB, migrationDir, 100) require.NoError(t, err) @@ -443,7 +443,7 @@ func TestSetMigrationENVVars(t *testing.T) { } func TestDatabaseBackFillWithMigration202(t *testing.T) { - _, db := heavyweight.FullTestDBEmptyV2(t, migrationDir, nil) + _, db := heavyweight.FullTestDBEmptyV2(t, nil) err := goose.UpTo(db.DB, migrationDir, 201) require.NoError(t, err) @@ -523,7 +523,7 @@ func BenchmarkBackfillingRecordsWithMigration202(b *testing.B) { maxLogsSize := 100_000 // Disable Goose logging for benchmarking goose.SetLogger(goose.NopLogger()) - _, db := heavyweight.FullTestDBEmptyV2(b, migrationDir, nil) + _, db := heavyweight.FullTestDBEmptyV2(b, nil) err := goose.UpTo(db.DB, migrationDir, previousMigration) require.NoError(b, err) From 5d4961b70ec804589ea66bb33e087f1da41a9e33 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 8 Nov 2023 12:10:56 -0600 Subject: [PATCH 110/271] core/store/migration/migrations: rm last sqlx.NewTx use (#11230) --- core/store/migrate/migrations/0036_external_job_id.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/store/migrate/migrations/0036_external_job_id.go b/core/store/migrate/migrations/0036_external_job_id.go index 82f26206d88..8637bd38f56 100644 --- a/core/store/migrate/migrations/0036_external_job_id.go +++ b/core/store/migrate/migrations/0036_external_job_id.go @@ -45,7 +45,7 @@ func Up36(ctx context.Context, tx *sql.Tx) error { // Update all jobs to have an external_job_id. // We do this to avoid using the uuid postgres extension. var jobIDs []int32 - txx := sqlx.NewTx(tx, "postgres") + txx := sqlx.Tx{Tx: tx} if err := txx.SelectContext(ctx, &jobIDs, "SELECT id FROM jobs"); err != nil { return err } From b8caeda1aee8b7127bb2d0a208630ce42beee46d Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Wed, 8 Nov 2023 16:13:59 -0500 Subject: [PATCH 111/271] (test): Amend Functions onTokenTransferTest to be able to fuzz with full LINK supply (#11234) --- .../gas-snapshots/functions.gas-snapshot | 10 ++--- .../tests/v1_X/FunctionsSubscriptions.t.sol | 42 ++++++++++++------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index d575c8ca196..e742be27549 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -130,11 +130,11 @@ FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Reve FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13459) FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59592) FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15010) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 28446, ~: 28446) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 30958, ~: 30958) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14293, ~: 14293) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 35938, ~: 35938) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 59686, ~: 59686) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43685, ~: 45548) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46197, ~: 48060) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51089, ~: 53040) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol index 8f08a6c1e86..5a54bcc84ca 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsSubscriptions.t.sol @@ -309,11 +309,22 @@ contract FunctionsSubscriptions_OwnerWithdraw is FunctionsFulfillmentSetup { } /// @notice #onTokenTransfer -contract FunctionsSubscriptions_OnTokenTransfer is FunctionsSubscriptionSetup { +contract FunctionsSubscriptions_OnTokenTransfer is FunctionsClientSetup { + uint64 s_subscriptionId; + + function setUp() public virtual override { + FunctionsClientSetup.setUp(); + + // Create subscription, but do not fund it + s_subscriptionId = s_functionsRouter.createSubscription(); + s_functionsRouter.addConsumer(s_subscriptionId, address(s_functionsClient)); + } + function test_OnTokenTransfer_RevertIfPaused(uint96 fundingAmount) public { // Funding amount must be less than LINK total supply - vm.assume(fundingAmount < 1_000_000_000 * 1e18); - vm.assume(fundingAmount > 0); + uint256 totalSupplyJuels = 1_000_000_000 * 1e18; + vm.assume(fundingAmount <= totalSupplyJuels); + vm.assume(fundingAmount >= 0); s_functionsRouter.pause(); vm.expectRevert("Pausable: paused"); @@ -322,8 +333,9 @@ contract FunctionsSubscriptions_OnTokenTransfer is FunctionsSubscriptionSetup { function test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96 fundingAmount) public { // Funding amount must be less than LINK total supply - vm.assume(fundingAmount < 1_000_000_000 * 1e18); - vm.assume(fundingAmount > 0); + uint256 totalSupplyJuels = 1_000_000_000 * 1e18; + vm.assume(fundingAmount <= totalSupplyJuels); + vm.assume(fundingAmount >= 0); vm.expectRevert(FunctionsSubscriptions.OnlyCallableFromLink.selector); s_functionsRouter.onTokenTransfer(address(s_functionsRouter), fundingAmount, abi.encode(s_subscriptionId)); @@ -331,8 +343,9 @@ contract FunctionsSubscriptions_OnTokenTransfer is FunctionsSubscriptionSetup { function test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96 fundingAmount) public { // Funding amount must be less than LINK total supply - vm.assume(fundingAmount < 1_000_000_000 * 1e18); - vm.assume(fundingAmount > 0); + uint256 totalSupplyJuels = 1_000_000_000 * 1e18; + vm.assume(fundingAmount <= totalSupplyJuels); + vm.assume(fundingAmount >= 0); vm.expectRevert(FunctionsSubscriptions.InvalidCalldata.selector); s_linkToken.transferAndCall(address(s_functionsRouter), fundingAmount, new bytes(0)); @@ -340,8 +353,9 @@ contract FunctionsSubscriptions_OnTokenTransfer is FunctionsSubscriptionSetup { function test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96 fundingAmount) public { // Funding amount must be less than LINK total supply - vm.assume(fundingAmount < 1_000_000_000 * 1e18); - vm.assume(fundingAmount > 0); + uint256 totalSupplyJuels = 1_000_000_000 * 1e18; + vm.assume(fundingAmount <= totalSupplyJuels); + vm.assume(fundingAmount >= 0); vm.expectRevert(FunctionsSubscriptions.InvalidSubscription.selector); uint64 invalidSubscriptionId = 123456789; @@ -349,17 +363,15 @@ contract FunctionsSubscriptions_OnTokenTransfer is FunctionsSubscriptionSetup { } function test_OnTokenTransfer_Success(uint96 fundingAmount) public { - uint96 subscriptionBalanceBefore = s_functionsRouter.getSubscription(s_subscriptionId).balance; - // Funding amount must be less than LINK total supply - uint96 TOTAL_LINK = 1_000_000_000 * 1e18; + uint256 totalSupplyJuels = 1_000_000_000 * 1e18; // Some of the total supply is already in the subscription account - vm.assume(fundingAmount < TOTAL_LINK - subscriptionBalanceBefore); - vm.assume(fundingAmount > 0); + vm.assume(fundingAmount <= totalSupplyJuels); + vm.assume(fundingAmount >= 0); s_linkToken.transferAndCall(address(s_functionsRouter), fundingAmount, abi.encode(s_subscriptionId)); uint96 subscriptionBalanceAfter = s_functionsRouter.getSubscription(s_subscriptionId).balance; - assertEq(subscriptionBalanceBefore + fundingAmount, subscriptionBalanceAfter); + assertEq(fundingAmount, subscriptionBalanceAfter); } } From 6481bed281200e17ca09508b839dc90223fe3ffb Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Thu, 9 Nov 2023 11:42:50 +0100 Subject: [PATCH 112/271] Add GetAPIClient to ClNode E2E docker wrapper (#11240) --- integration-tests/docker/test_env/cl_node.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 4de3d27d754..6e74e54a4f9 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -203,6 +203,10 @@ func (n *ClNode) GetContainerName() string { return strings.Replace(name, "/", "", -1) } +func (n *ClNode) GetAPIClient() *client.ChainlinkClient { + return n.API +} + func (n *ClNode) GetPeerUrl() (string, error) { p2pKeys, err := n.API.MustReadP2PKeys() if err != nil { From dd2c5ef1a71d821d97f199573b04df71dcab6172 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 9 Nov 2023 12:58:22 +0100 Subject: [PATCH 113/271] Move Sol files from top level to project dirs (#11227) * mv agg interfaces * rm unused ownable interface * mv AuthorizedReceiverInterface * mv libs to llo-feeds * fix interfaces/AggregatorV2V3Interface ref * rm dup interface --- .../gas-snapshots/llo-feeds.gas-snapshot | 20 +++++++++++++++++++ .../scripts/native_solc_compile_all_feeds | 2 +- contracts/src/v0.8/ValidatorProxy.sol | 2 +- .../automation/v1_2/KeeperRegistry1_2.sol | 2 +- .../automation/v1_3/KeeperRegistryBase1_3.sol | 2 +- .../automation/v2_0/KeeperRegistryBase2_0.sol | 2 +- .../automation/v2_1/KeeperRegistryBase2_1.sol | 2 +- .../shared/interfaces/OwnableInterface.sol | 10 ---------- .../functions/dev/v1_X/FunctionsBilling.sol | 2 +- .../functions/v1_0_0/FunctionsBilling.sol | 2 +- .../v0.8/interfaces/FeedRegistryInterface.sol | 2 +- .../arbitrum/ArbitrumSequencerUptimeFeed.sol | 6 +++--- .../l2ep/dev/arbitrum/ArbitrumValidator.sol | 2 +- .../optimism/OptimismSequencerUptimeFeed.sol | 6 +++--- .../l2ep/dev/optimism/OptimismValidator.sol | 2 +- contracts/src/v0.8/llo-feeds/FeeManager.sol | 2 +- .../src/v0.8/llo-feeds/RewardManager.sol | 2 +- contracts/src/v0.8/llo-feeds/Verifier.sol | 2 +- .../src/v0.8/llo-feeds/VerifierProxy.sol | 2 +- .../v0.8/llo-feeds/interfaces/IFeeManager.sol | 2 +- .../llo-feeds/interfaces/IRewardManager.sol | 2 +- .../v0.8/llo-feeds/interfaces/IVerifier.sol | 2 +- .../interfaces/IVerifierFeeManager.sol | 2 +- .../llo-feeds/interfaces/IVerifierProxy.sol | 2 +- .../{ => llo-feeds}/libraries/ByteUtil.sol | 0 .../v0.8/{ => llo-feeds}/libraries/Common.sol | 0 .../test/ByteUtilTest.t.sol | 2 +- .../test/fee-manager/BaseFeeManager.t.sol | 2 +- .../FeeManager.getFeeAndReward.t.sol | 2 +- .../fee-manager/FeeManager.processFee.t.sol | 2 +- .../llo-feeds/test/gas/Gas_VerifierTest.t.sol | 2 +- .../llo-feeds/test/mocks/ErroredVerifier.sol | 2 +- .../reward-manager/BaseRewardManager.t.sol | 2 +- .../reward-manager/RewardManager.claim.t.sol | 2 +- .../RewardManager.setRecipients.t.sol | 2 +- ...RewardManager.updateRewardRecipients.t.sol | 2 +- .../test/verifier/BaseVerifierTest.t.sol | 2 +- .../VerifierProxySetVerifierTest.t.sol | 2 +- .../VerifierSetConfigFromSourceTest.t.sol | 2 +- .../test/verifier/VerifierSetConfigTest.t.sol | 2 +- .../test/verifier/VerifierVerifyTest.t.sol | 2 +- .../v0.8/mocks/MockAggregatorValidator.sol | 2 +- .../dev/AuthorizedReceiver.sol | 2 +- .../v0.8/operatorforwarder/dev/Operator.sol | 2 +- .../AuthorizedReceiverInterface.sol | 0 .../interfaces/AggregatorInterface.sol | 0 .../interfaces/AggregatorV2V3Interface.sol | 0 .../interfaces/AggregatorV3Interface.sol | 0 .../AggregatorValidatorInterface.sol | 0 .../src/v0.8/shared/token/ERC677/ERC677.sol | 2 +- .../shared/token/ERC677/IERC677Receiver.sol | 6 ------ contracts/src/v0.8/tests/FeedConsumer.sol | 2 +- .../src/v0.8/tests/MockETHLINKAggregator.sol | 2 +- contracts/src/v0.8/tests/MockV3Aggregator.sol | 2 +- .../transmission/dev/ERC-4337/Paymaster.sol | 2 +- contracts/src/v0.8/vrf/VRFCoordinatorV2.sol | 2 +- contracts/src/v0.8/vrf/VRFV2Wrapper.sol | 2 +- .../src/v0.8/vrf/dev/SubscriptionAPI.sol | 2 +- .../src/v0.8/vrf/dev/VRFV2PlusWrapper.sol | 2 +- .../VRFCoordinatorV2TestHelper.sol | 2 +- .../test/v0.8/dev/ArbitrumValidator.test.ts | 2 +- .../test/v0.8/dev/OptimismValidator.test.ts | 2 +- 62 files changed, 76 insertions(+), 72 deletions(-) delete mode 100644 contracts/src/v0.8/dev/shared/interfaces/OwnableInterface.sol rename contracts/src/v0.8/{ => llo-feeds}/libraries/ByteUtil.sol (100%) rename contracts/src/v0.8/{ => llo-feeds}/libraries/Common.sol (100%) rename contracts/src/v0.8/{libraries => llo-feeds}/test/ByteUtilTest.t.sol (99%) rename contracts/src/v0.8/{ => operatorforwarder/dev}/interfaces/AuthorizedReceiverInterface.sol (100%) rename contracts/src/v0.8/{ => shared}/interfaces/AggregatorInterface.sol (100%) rename contracts/src/v0.8/{ => shared}/interfaces/AggregatorV2V3Interface.sol (100%) rename contracts/src/v0.8/{ => shared}/interfaces/AggregatorV3Interface.sol (100%) rename contracts/src/v0.8/{ => shared}/interfaces/AggregatorValidatorInterface.sol (100%) delete mode 100644 contracts/src/v0.8/shared/token/ERC677/IERC677Receiver.sol diff --git a/contracts/gas-snapshots/llo-feeds.gas-snapshot b/contracts/gas-snapshots/llo-feeds.gas-snapshot index a9877fbe33c..ad9339a3410 100644 --- a/contracts/gas-snapshots/llo-feeds.gas-snapshot +++ b/contracts/gas-snapshots/llo-feeds.gas-snapshot @@ -1,3 +1,23 @@ +ByteUtilTest:test_readAddress() (gas: 542) +ByteUtilTest:test_readAddressMultiWord() (gas: 540) +ByteUtilTest:test_readAddressWithEmptyArray() (gas: 3274) +ByteUtilTest:test_readAddressWithNotEnoughBytes() (gas: 3314) +ByteUtilTest:test_readUint192Max() (gas: 485) +ByteUtilTest:test_readUint192Min() (gas: 508) +ByteUtilTest:test_readUint192MultiWord() (gas: 486) +ByteUtilTest:test_readUint192WithEmptyArray() (gas: 3274) +ByteUtilTest:test_readUint192WithNotEnoughBytes() (gas: 3314) +ByteUtilTest:test_readUint256Max() (gas: 502) +ByteUtilTest:test_readUint256Min() (gas: 546) +ByteUtilTest:test_readUint256MultiWord() (gas: 500) +ByteUtilTest:test_readUint256WithEmptyArray() (gas: 3296) +ByteUtilTest:test_readUint256WithNotEnoughBytes() (gas: 3293) +ByteUtilTest:test_readUint32Max() (gas: 507) +ByteUtilTest:test_readUint32Min() (gas: 487) +ByteUtilTest:test_readUint32MultiWord() (gas: 552) +ByteUtilTest:test_readUint32WithEmptyArray() (gas: 3253) +ByteUtilTest:test_readUint32WithNotEnoughBytes() (gas: 3272) +ByteUtilTest:test_readZeroAddress() (gas: 519) FeeManagerProcessFeeTest:test_DiscountIsAppliedForNative() (gas: 52282) FeeManagerProcessFeeTest:test_DiscountIsReturnedForNative() (gas: 52235) FeeManagerProcessFeeTest:test_DiscountIsReturnedForNativeWithSurcharge() (gas: 78440) diff --git a/contracts/scripts/native_solc_compile_all_feeds b/contracts/scripts/native_solc_compile_all_feeds index eac5a6b70cf..2c5808d4663 100755 --- a/contracts/scripts/native_solc_compile_all_feeds +++ b/contracts/scripts/native_solc_compile_all_feeds @@ -30,6 +30,6 @@ compileContract () { } # Aggregators -compileContract interfaces/AggregatorV2V3Interface.sol +compileContract shared/interfaces/AggregatorV2V3Interface.sol compileContract Chainlink.sol compileContract ChainlinkClient.sol diff --git a/contracts/src/v0.8/ValidatorProxy.sol b/contracts/src/v0.8/ValidatorProxy.sol index 35909ad87de..627af73b395 100644 --- a/contracts/src/v0.8/ValidatorProxy.sol +++ b/contracts/src/v0.8/ValidatorProxy.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import {ConfirmedOwner} from "./shared/access/ConfirmedOwner.sol"; -import {AggregatorValidatorInterface} from "./interfaces/AggregatorValidatorInterface.sol"; +import {AggregatorValidatorInterface} from "./shared/interfaces/AggregatorValidatorInterface.sol"; import {TypeAndVersionInterface} from "./interfaces/TypeAndVersionInterface.sol"; // solhint-disable custom-errors diff --git a/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol b/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol index 262b8357f7a..2fa1ee6188b 100644 --- a/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol +++ b/contracts/src/v0.8/automation/v1_2/KeeperRegistry1_2.sol @@ -7,7 +7,7 @@ import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "../KeeperBase.sol"; import "../../interfaces/TypeAndVersionInterface.sol"; -import "../../interfaces/AggregatorV3Interface.sol"; +import "../../shared/interfaces/AggregatorV3Interface.sol"; import "../interfaces/KeeperCompatibleInterface.sol"; import "../interfaces/v1_2/KeeperRegistryInterface1_2.sol"; import "../interfaces/MigratableKeeperRegistryInterface.sol"; diff --git a/contracts/src/v0.8/automation/v1_3/KeeperRegistryBase1_3.sol b/contracts/src/v0.8/automation/v1_3/KeeperRegistryBase1_3.sol index 6328b651671..c21f3a73912 100644 --- a/contracts/src/v0.8/automation/v1_3/KeeperRegistryBase1_3.sol +++ b/contracts/src/v0.8/automation/v1_3/KeeperRegistryBase1_3.sol @@ -8,7 +8,7 @@ import "../../vendor/@eth-optimism/contracts/v0.8.6/contracts/L2/predeploys/OVM_ import "../ExecutionPrevention.sol"; import {Config, Upkeep} from "../interfaces/v1_3/AutomationRegistryInterface1_3.sol"; import "../../shared/access/ConfirmedOwner.sol"; -import "../../interfaces/AggregatorV3Interface.sol"; +import "../../shared/interfaces/AggregatorV3Interface.sol"; import "../../shared/interfaces/LinkTokenInterface.sol"; import "../interfaces/KeeperCompatibleInterface.sol"; import "../interfaces/UpkeepTranscoderInterface.sol"; diff --git a/contracts/src/v0.8/automation/v2_0/KeeperRegistryBase2_0.sol b/contracts/src/v0.8/automation/v2_0/KeeperRegistryBase2_0.sol index 14e9b204475..9b78e5806ff 100644 --- a/contracts/src/v0.8/automation/v2_0/KeeperRegistryBase2_0.sol +++ b/contracts/src/v0.8/automation/v2_0/KeeperRegistryBase2_0.sol @@ -7,7 +7,7 @@ import "../../vendor/@eth-optimism/contracts/v0.8.6/contracts/L2/predeploys/OVM_ import {ArbSys} from "../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; import "../ExecutionPrevention.sol"; import "../../shared/access/ConfirmedOwner.sol"; -import "../../interfaces/AggregatorV3Interface.sol"; +import "../../shared/interfaces/AggregatorV3Interface.sol"; import "../../shared/interfaces/LinkTokenInterface.sol"; import "../interfaces/KeeperCompatibleInterface.sol"; import "../interfaces/UpkeepTranscoderInterface.sol"; diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol index f5d89de5467..f81fcef636e 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistryBase2_1.sol @@ -11,7 +11,7 @@ import {StreamsLookupCompatibleInterface} from "../interfaces/StreamsLookupCompa import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; import {IAutomationForwarder} from "../interfaces/IAutomationForwarder.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; -import {AggregatorV3Interface} from "../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; import {KeeperCompatibleInterface} from "../interfaces/KeeperCompatibleInterface.sol"; import {UpkeepFormat} from "../interfaces/UpkeepTranscoderInterface.sol"; diff --git a/contracts/src/v0.8/dev/shared/interfaces/OwnableInterface.sol b/contracts/src/v0.8/dev/shared/interfaces/OwnableInterface.sol deleted file mode 100644 index a24cbee504c..00000000000 --- a/contracts/src/v0.8/dev/shared/interfaces/OwnableInterface.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface OwnableInterface { - function owner() external returns (address); - - function transferOwnership(address recipient) external; - - function acceptOwnership() external; -} diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index ed67d485431..adc6218733f 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol"; -import {AggregatorV3Interface} from "../../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; import {Routable} from "./Routable.sol"; diff --git a/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol b/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol index 5168bdc01ed..8de53dd9c04 100644 --- a/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/v1_0_0/FunctionsBilling.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import {IFunctionsSubscriptions} from "./interfaces/IFunctionsSubscriptions.sol"; -import {AggregatorV3Interface} from "../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; import {IFunctionsBilling} from "./interfaces/IFunctionsBilling.sol"; import {Routable} from "./Routable.sol"; diff --git a/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol b/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol index 1d2367d82a8..f3272174ae2 100644 --- a/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol +++ b/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; pragma abicoder v2; -import {AggregatorV2V3Interface} from "./AggregatorV2V3Interface.sol"; +import {AggregatorV2V3Interface} from "../shared/interfaces/AggregatorV2V3Interface.sol"; interface FeedRegistryInterface { struct Phase { diff --git a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol index 6d8d31a8085..5250fbda278 100644 --- a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol +++ b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumSequencerUptimeFeed.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.4; import {AddressAliasHelper} from "../../../vendor/arb-bridge-eth/v0.8.0-custom/contracts/libraries/AddressAliasHelper.sol"; -import {AggregatorInterface} from "../../../interfaces/AggregatorInterface.sol"; -import {AggregatorV3Interface} from "../../../interfaces/AggregatorV3Interface.sol"; -import {AggregatorV2V3Interface} from "../../../interfaces/AggregatorV2V3Interface.sol"; +import {AggregatorInterface} from "../../../shared/interfaces/AggregatorInterface.sol"; +import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; +import {AggregatorV2V3Interface} from "../../../shared/interfaces/AggregatorV2V3Interface.sol"; import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; import {FlagsInterface} from "../interfaces/FlagsInterface.sol"; import {ArbitrumSequencerUptimeFeedInterface} from "../interfaces/ArbitrumSequencerUptimeFeedInterface.sol"; diff --git a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol index 3b5fd277e56..2043ffa6287 100644 --- a/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol +++ b/contracts/src/v0.8/l2ep/dev/arbitrum/ArbitrumValidator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {AggregatorValidatorInterface} from "../../../interfaces/AggregatorValidatorInterface.sol"; +import {AggregatorValidatorInterface} from "../../../shared/interfaces/AggregatorValidatorInterface.sol"; import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; import {SimpleWriteAccessController} from "../../../shared/access/SimpleWriteAccessController.sol"; diff --git a/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol b/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol index b522a600bab..fcf6093e3cd 100644 --- a/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol +++ b/contracts/src/v0.8/l2ep/dev/optimism/OptimismSequencerUptimeFeed.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; -import {AggregatorInterface} from "../../../interfaces/AggregatorInterface.sol"; -import {AggregatorV3Interface} from "../../../interfaces/AggregatorV3Interface.sol"; -import {AggregatorV2V3Interface} from "../../../interfaces/AggregatorV2V3Interface.sol"; +import {AggregatorInterface} from "../../../shared/interfaces/AggregatorInterface.sol"; +import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; +import {AggregatorV2V3Interface} from "../../../shared/interfaces/AggregatorV2V3Interface.sol"; import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; import {OptimismSequencerUptimeFeedInterface} from "./../interfaces/OptimismSequencerUptimeFeedInterface.sol"; import {SimpleReadAccessController} from "../../../shared/access/SimpleReadAccessController.sol"; diff --git a/contracts/src/v0.8/l2ep/dev/optimism/OptimismValidator.sol b/contracts/src/v0.8/l2ep/dev/optimism/OptimismValidator.sol index a955b7e92ca..e41c61a4536 100644 --- a/contracts/src/v0.8/l2ep/dev/optimism/OptimismValidator.sol +++ b/contracts/src/v0.8/l2ep/dev/optimism/OptimismValidator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {AggregatorValidatorInterface} from "../../../interfaces/AggregatorValidatorInterface.sol"; +import {AggregatorValidatorInterface} from "../../../shared/interfaces/AggregatorValidatorInterface.sol"; import {TypeAndVersionInterface} from "../../../interfaces/TypeAndVersionInterface.sol"; import {OptimismSequencerUptimeFeedInterface} from "./../interfaces/OptimismSequencerUptimeFeedInterface.sol"; diff --git a/contracts/src/v0.8/llo-feeds/FeeManager.sol b/contracts/src/v0.8/llo-feeds/FeeManager.sol index 397605d9b2e..c9981045a4a 100644 --- a/contracts/src/v0.8/llo-feeds/FeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/FeeManager.sol @@ -5,7 +5,7 @@ import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; import {IFeeManager} from "./interfaces/IFeeManager.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../libraries/Common.sol"; +import {Common} from "./libraries/Common.sol"; import {IRewardManager} from "./interfaces/IRewardManager.sol"; import {IWERC20} from "../shared/interfaces/IWERC20.sol"; import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; diff --git a/contracts/src/v0.8/llo-feeds/RewardManager.sol b/contracts/src/v0.8/llo-feeds/RewardManager.sol index 3777b432fcc..596755142e8 100644 --- a/contracts/src/v0.8/llo-feeds/RewardManager.sol +++ b/contracts/src/v0.8/llo-feeds/RewardManager.sol @@ -5,7 +5,7 @@ import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; import {IRewardManager} from "./interfaces/IRewardManager.sol"; import {IERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC20.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; -import {Common} from "../libraries/Common.sol"; +import {Common} from "./libraries/Common.sol"; import {SafeERC20} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/Verifier.sol b/contracts/src/v0.8/llo-feeds/Verifier.sol index f7ce156a60b..3e668c09ff0 100644 --- a/contracts/src/v0.8/llo-feeds/Verifier.sol +++ b/contracts/src/v0.8/llo-feeds/Verifier.sol @@ -6,7 +6,7 @@ import {IVerifier} from "./interfaces/IVerifier.sol"; import {IVerifierProxy} from "./interfaces/IVerifierProxy.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../libraries/Common.sol"; +import {Common} from "./libraries/Common.sol"; // OCR2 standard uint256 constant MAX_NUM_ORACLES = 31; diff --git a/contracts/src/v0.8/llo-feeds/VerifierProxy.sol b/contracts/src/v0.8/llo-feeds/VerifierProxy.sol index 6abb2b78e98..a35c54573c1 100644 --- a/contracts/src/v0.8/llo-feeds/VerifierProxy.sol +++ b/contracts/src/v0.8/llo-feeds/VerifierProxy.sol @@ -8,7 +8,7 @@ import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol import {AccessControllerInterface} from "../shared/interfaces/AccessControllerInterface.sol"; import {IERC165} from "../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; import {IVerifierFeeManager} from "./interfaces/IVerifierFeeManager.sol"; -import {Common} from "../libraries/Common.sol"; +import {Common} from "./libraries/Common.sol"; /** * The verifier proxy contract is the gateway for all report verification requests diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol b/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol index 08373a6a5bc..e006f0254eb 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IFeeManager.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../libraries/Common.sol"; import {IVerifierFeeManager} from "./IVerifierFeeManager.sol"; interface IFeeManager is IERC165, IVerifierFeeManager { diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol b/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol index a76366a3eb1..7a4d4216715 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IRewardManager.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../libraries/Common.sol"; interface IRewardManager is IERC165 { /** diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol b/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol index 9b9ba2e6570..9e1e6d314cd 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IVerifier.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../libraries/Common.sol"; interface IVerifier is IERC165 { /** diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol b/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol index e5a73e612cb..323b8a2cf00 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IVerifierFeeManager.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {IERC165} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../libraries/Common.sol"; interface IVerifierFeeManager is IERC165 { /** diff --git a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol b/contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol index c2665261e9a..d86bb46dd9c 100644 --- a/contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol +++ b/contracts/src/v0.8/llo-feeds/interfaces/IVerifierProxy.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.16; -import {Common} from "../../libraries/Common.sol"; +import {Common} from "../libraries/Common.sol"; import {AccessControllerInterface} from "../../shared/interfaces/AccessControllerInterface.sol"; import {IVerifierFeeManager} from "./IVerifierFeeManager.sol"; diff --git a/contracts/src/v0.8/libraries/ByteUtil.sol b/contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol similarity index 100% rename from contracts/src/v0.8/libraries/ByteUtil.sol rename to contracts/src/v0.8/llo-feeds/libraries/ByteUtil.sol diff --git a/contracts/src/v0.8/libraries/Common.sol b/contracts/src/v0.8/llo-feeds/libraries/Common.sol similarity index 100% rename from contracts/src/v0.8/libraries/Common.sol rename to contracts/src/v0.8/llo-feeds/libraries/Common.sol diff --git a/contracts/src/v0.8/libraries/test/ByteUtilTest.t.sol b/contracts/src/v0.8/llo-feeds/test/ByteUtilTest.t.sol similarity index 99% rename from contracts/src/v0.8/libraries/test/ByteUtilTest.t.sol rename to contracts/src/v0.8/llo-feeds/test/ByteUtilTest.t.sol index 0629d0235ee..b4e87364ac9 100644 --- a/contracts/src/v0.8/libraries/test/ByteUtilTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/ByteUtilTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {Test} from "forge-std/Test.sol"; -import {ByteUtil} from "../ByteUtil.sol"; +import {ByteUtil} from "../libraries/ByteUtil.sol"; contract ByteUtilTest is Test { using ByteUtil for bytes; diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol b/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol index ec2611f9e46..db0b3d8b3d9 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/fee-manager/BaseFeeManager.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.16; import {Test} from "forge-std/Test.sol"; import {FeeManager} from "../../FeeManager.sol"; import {RewardManager} from "../../RewardManager.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {WERC20Mock} from "../../../shared/mocks/WERC20Mock.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol b/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol index 801a1e39925..6a24806353d 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.getFeeAndReward.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; import "./BaseFeeManager.t.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol b/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol index 9c2bd711ed5..e0093b88a4f 100644 --- a/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/fee-manager/FeeManager.processFee.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.16; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; import "./BaseFeeManager.t.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol index 3198576e8aa..6938437b013 100644 --- a/contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/gas/Gas_VerifierTest.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.16; import {BaseTest, BaseTestWithConfiguredVerifierAndFeeManager} from "../verifier/BaseVerifierTest.t.sol"; import {SimpleWriteAccessController} from "../../../shared/access/SimpleWriteAccessController.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; contract Verifier_setConfig is BaseTest { diff --git a/contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol b/contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol index a0a404d88d7..770b7b809d0 100644 --- a/contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol +++ b/contracts/src/v0.8/llo-feeds/test/mocks/ErroredVerifier.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {IVerifier} from "../../interfaces/IVerifier.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; contract ErroredVerifier is IVerifier { function supportsInterface(bytes4 interfaceId) public pure override returns (bool) { diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol b/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol index 3e50adef95c..a9953d73c74 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/reward-manager/BaseRewardManager.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.16; import {Test} from "forge-std/Test.sol"; import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {RewardManager} from "../../RewardManager.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; import {IRewardManager} from "../../interfaces/IRewardManager.sol"; /** diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol index 9a3749d1dde..a6c98c03031 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.claim.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; /** * @title BaseRewardManagerTest diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol index 0e45ba00da4..a8cf6260f5c 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.setRecipients.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; /** * @title BaseRewardManagerTest diff --git a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol index 4b3063ac016..b1836e0fb93 100644 --- a/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/reward-manager/RewardManager.updateRewardRecipients.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {BaseRewardManagerTest} from "./BaseRewardManager.t.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; /** * @title BaseRewardManagerTest diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol index 91e0f9da906..34e2090115d 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/BaseVerifierTest.t.sol @@ -10,7 +10,7 @@ import {Verifier} from "../../Verifier.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; import {FeeManager} from "../../FeeManager.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol"; import {WERC20Mock} from "../../../shared/mocks/WERC20Mock.sol"; import {FeeManager} from "../../FeeManager.sol"; diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol index a6b23d7e8b4..17fc49979e7 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierProxySetVerifierTest.t.sol @@ -5,7 +5,7 @@ import {BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t. import {IVerifier} from "../../interfaces/IVerifier.sol"; import {VerifierProxy} from "../../VerifierProxy.sol"; import {IERC165} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/interfaces/IERC165.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; contract VerifierProxyInitializeVerifierTest is BaseTestWithConfiguredVerifierAndFeeManager { function test_revertsIfNotCorrectVerifier() public { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol index 6c5eac9b6dd..ba3acce0eec 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigFromSourceTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.16; import {BaseTest, BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; contract VerifierSetConfigFromSourceTest is BaseTest { function setUp() public virtual override { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol index f0b045e7f30..374a976786b 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierSetConfigTest.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.16; import {BaseTest, BaseTestWithMultipleConfiguredDigests} from "./BaseVerifierTest.t.sol"; import {Verifier} from "../../Verifier.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; contract VerifierSetConfigTest is BaseTest { function setUp() public virtual override { diff --git a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol index b4fcac75d3a..34e02bcfb95 100644 --- a/contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol +++ b/contracts/src/v0.8/llo-feeds/test/verifier/VerifierVerifyTest.t.sol @@ -5,7 +5,7 @@ import {BaseTestWithConfiguredVerifierAndFeeManager} from "./BaseVerifierTest.t. import {Verifier} from "../../Verifier.sol"; import {VerifierProxy} from "../../VerifierProxy.sol"; import {AccessControllerInterface} from "../../../shared/interfaces/AccessControllerInterface.sol"; -import {Common} from "../../../libraries/Common.sol"; +import {Common} from "../../libraries/Common.sol"; contract VerifierVerifyTest is BaseTestWithConfiguredVerifierAndFeeManager { bytes32[3] internal s_reportContext; diff --git a/contracts/src/v0.8/mocks/MockAggregatorValidator.sol b/contracts/src/v0.8/mocks/MockAggregatorValidator.sol index a43236de9ff..bdc935cd231 100644 --- a/contracts/src/v0.8/mocks/MockAggregatorValidator.sol +++ b/contracts/src/v0.8/mocks/MockAggregatorValidator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../interfaces/AggregatorValidatorInterface.sol"; +import "../shared/interfaces/AggregatorValidatorInterface.sol"; contract MockAggregatorValidator is AggregatorValidatorInterface { uint8 immutable id; diff --git a/contracts/src/v0.8/operatorforwarder/dev/AuthorizedReceiver.sol b/contracts/src/v0.8/operatorforwarder/dev/AuthorizedReceiver.sol index 04d2635d583..bc5f1c0e7e4 100644 --- a/contracts/src/v0.8/operatorforwarder/dev/AuthorizedReceiver.sol +++ b/contracts/src/v0.8/operatorforwarder/dev/AuthorizedReceiver.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import {AuthorizedReceiverInterface} from "../../interfaces/AuthorizedReceiverInterface.sol"; +import {AuthorizedReceiverInterface} from "./interfaces/AuthorizedReceiverInterface.sol"; // solhint-disable custom-errors abstract contract AuthorizedReceiver is AuthorizedReceiverInterface { diff --git a/contracts/src/v0.8/operatorforwarder/dev/Operator.sol b/contracts/src/v0.8/operatorforwarder/dev/Operator.sol index b83996a9ed9..c8451bf03ce 100644 --- a/contracts/src/v0.8/operatorforwarder/dev/Operator.sol +++ b/contracts/src/v0.8/operatorforwarder/dev/Operator.sol @@ -5,7 +5,7 @@ import {AuthorizedReceiver} from "./AuthorizedReceiver.sol"; import {LinkTokenReceiver} from "./LinkTokenReceiver.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; -import {AuthorizedReceiverInterface} from "../../interfaces/AuthorizedReceiverInterface.sol"; +import {AuthorizedReceiverInterface} from "./interfaces/AuthorizedReceiverInterface.sol"; import {OperatorInterface} from "../../interfaces/OperatorInterface.sol"; import {IOwnable} from "../../shared/interfaces/IOwnable.sol"; import {WithdrawalInterface} from "./interfaces/WithdrawalInterface.sol"; diff --git a/contracts/src/v0.8/interfaces/AuthorizedReceiverInterface.sol b/contracts/src/v0.8/operatorforwarder/dev/interfaces/AuthorizedReceiverInterface.sol similarity index 100% rename from contracts/src/v0.8/interfaces/AuthorizedReceiverInterface.sol rename to contracts/src/v0.8/operatorforwarder/dev/interfaces/AuthorizedReceiverInterface.sol diff --git a/contracts/src/v0.8/interfaces/AggregatorInterface.sol b/contracts/src/v0.8/shared/interfaces/AggregatorInterface.sol similarity index 100% rename from contracts/src/v0.8/interfaces/AggregatorInterface.sol rename to contracts/src/v0.8/shared/interfaces/AggregatorInterface.sol diff --git a/contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol b/contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol similarity index 100% rename from contracts/src/v0.8/interfaces/AggregatorV2V3Interface.sol rename to contracts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol diff --git a/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol b/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol similarity index 100% rename from contracts/src/v0.8/interfaces/AggregatorV3Interface.sol rename to contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol diff --git a/contracts/src/v0.8/interfaces/AggregatorValidatorInterface.sol b/contracts/src/v0.8/shared/interfaces/AggregatorValidatorInterface.sol similarity index 100% rename from contracts/src/v0.8/interfaces/AggregatorValidatorInterface.sol rename to contracts/src/v0.8/shared/interfaces/AggregatorValidatorInterface.sol diff --git a/contracts/src/v0.8/shared/token/ERC677/ERC677.sol b/contracts/src/v0.8/shared/token/ERC677/ERC677.sol index 9a68bac3a11..aa75a1170c7 100644 --- a/contracts/src/v0.8/shared/token/ERC677/ERC677.sol +++ b/contracts/src/v0.8/shared/token/ERC677/ERC677.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import {IERC677} from "./IERC677.sol"; -import {IERC677Receiver} from "./IERC677Receiver.sol"; +import {IERC677Receiver} from "../../interfaces/IERC677Receiver.sol"; import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol"; import {ERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol"; diff --git a/contracts/src/v0.8/shared/token/ERC677/IERC677Receiver.sol b/contracts/src/v0.8/shared/token/ERC677/IERC677Receiver.sol deleted file mode 100644 index 2e44d860a82..00000000000 --- a/contracts/src/v0.8/shared/token/ERC677/IERC677Receiver.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface IERC677Receiver { - function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external; -} diff --git a/contracts/src/v0.8/tests/FeedConsumer.sol b/contracts/src/v0.8/tests/FeedConsumer.sol index 3c9462b0ac5..c9fc62357a6 100644 --- a/contracts/src/v0.8/tests/FeedConsumer.sol +++ b/contracts/src/v0.8/tests/FeedConsumer.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {AggregatorV2V3Interface} from "../interfaces/AggregatorV2V3Interface.sol"; +import {AggregatorV2V3Interface} from "../shared/interfaces/AggregatorV2V3Interface.sol"; contract FeedConsumer { AggregatorV2V3Interface public immutable AGGREGATOR; diff --git a/contracts/src/v0.8/tests/MockETHLINKAggregator.sol b/contracts/src/v0.8/tests/MockETHLINKAggregator.sol index 98dd775a117..d685aac7314 100644 --- a/contracts/src/v0.8/tests/MockETHLINKAggregator.sol +++ b/contracts/src/v0.8/tests/MockETHLINKAggregator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../interfaces/AggregatorV3Interface.sol"; +import "../shared/interfaces/AggregatorV3Interface.sol"; contract MockETHLINKAggregator is AggregatorV3Interface { int256 public answer; diff --git a/contracts/src/v0.8/tests/MockV3Aggregator.sol b/contracts/src/v0.8/tests/MockV3Aggregator.sol index d261b2c4b14..9822d23e853 100644 --- a/contracts/src/v0.8/tests/MockV3Aggregator.sol +++ b/contracts/src/v0.8/tests/MockV3Aggregator.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../interfaces/AggregatorV2V3Interface.sol"; +import "../shared/interfaces/AggregatorV2V3Interface.sol"; /** * @title MockV3Aggregator diff --git a/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol b/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol index cd84bb6a0e0..970ddf6b7e6 100644 --- a/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol +++ b/contracts/src/v0.8/transmission/dev/ERC-4337/Paymaster.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.15; import {IPaymaster} from "../../../vendor/entrypoint/interfaces/IPaymaster.sol"; import {SCALibrary} from "./SCALibrary.sol"; import {LinkTokenInterface} from "../../../shared/interfaces/LinkTokenInterface.sol"; -import {AggregatorV3Interface} from "../../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../../shared/interfaces/AggregatorV3Interface.sol"; import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; import {UserOperation} from "../../../vendor/entrypoint/interfaces/UserOperation.sol"; import {_packValidationData} from "../../../vendor/entrypoint/core/Helpers.sol"; diff --git a/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol b/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol index 5150d263a8b..5acd3e74358 100644 --- a/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol +++ b/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.4; import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol"; import {BlockhashStoreInterface} from "./interfaces/BlockhashStoreInterface.sol"; -import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../shared/interfaces/AggregatorV3Interface.sol"; import {VRFCoordinatorV2Interface} from "./interfaces/VRFCoordinatorV2Interface.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {IERC677Receiver} from "../shared/interfaces/IERC677Receiver.sol"; diff --git a/contracts/src/v0.8/vrf/VRFV2Wrapper.sol b/contracts/src/v0.8/vrf/VRFV2Wrapper.sol index 805c8d76cb6..abe479cb20a 100644 --- a/contracts/src/v0.8/vrf/VRFV2Wrapper.sol +++ b/contracts/src/v0.8/vrf/VRFV2Wrapper.sol @@ -6,7 +6,7 @@ import {ConfirmedOwner} from "../shared/access/ConfirmedOwner.sol"; import {TypeAndVersionInterface} from "../interfaces/TypeAndVersionInterface.sol"; import {VRFConsumerBaseV2} from "./VRFConsumerBaseV2.sol"; import {LinkTokenInterface} from "../shared/interfaces/LinkTokenInterface.sol"; -import {AggregatorV3Interface} from "../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../shared/interfaces/AggregatorV3Interface.sol"; import {VRFCoordinatorV2Interface} from "./interfaces/VRFCoordinatorV2Interface.sol"; import {VRFV2WrapperInterface} from "./interfaces/VRFV2WrapperInterface.sol"; import {VRFV2WrapperConsumerBase} from "./VRFV2WrapperConsumerBase.sol"; diff --git a/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol b/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol index e4708bb1fcf..d7cc5b86c5a 100644 --- a/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol +++ b/contracts/src/v0.8/vrf/dev/SubscriptionAPI.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v4.7.3/contracts/utils/structs/EnumerableSet.sol"; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; -import {AggregatorV3Interface} from "../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; import {IERC677Receiver} from "../../shared/interfaces/IERC677Receiver.sol"; import {IVRFSubscriptionV2Plus} from "./interfaces/IVRFSubscriptionV2Plus.sol"; diff --git a/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol b/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol index 9c3b983e300..a724b70a3d8 100644 --- a/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol +++ b/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol @@ -6,7 +6,7 @@ import {TypeAndVersionInterface} from "../../interfaces/TypeAndVersionInterface. import {IVRFV2PlusMigrate} from "./interfaces/IVRFV2PlusMigrate.sol"; import {VRFConsumerBaseV2Plus} from "./VRFConsumerBaseV2Plus.sol"; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; -import {AggregatorV3Interface} from "../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; import {VRFV2PlusClient} from "./libraries/VRFV2PlusClient.sol"; import {IVRFV2PlusWrapper} from "./interfaces/IVRFV2PlusWrapper.sol"; import {VRFV2PlusWrapperConsumerBase} from "./VRFV2PlusWrapperConsumerBase.sol"; diff --git a/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorV2TestHelper.sol b/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorV2TestHelper.sol index f9385329686..c5d1d90c126 100644 --- a/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorV2TestHelper.sol +++ b/contracts/src/v0.8/vrf/testhelpers/VRFCoordinatorV2TestHelper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {AggregatorV3Interface} from "../../interfaces/AggregatorV3Interface.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; // Ideally this contract should inherit from VRFCoordinatorV2 and delegate calls to VRFCoordinatorV2 // However, due to exceeding contract size limit, the logic from VRFCoordinatorV2 is ported over to this contract diff --git a/contracts/test/v0.8/dev/ArbitrumValidator.test.ts b/contracts/test/v0.8/dev/ArbitrumValidator.test.ts index 2f95a6f6fb0..232eea95839 100644 --- a/contracts/test/v0.8/dev/ArbitrumValidator.test.ts +++ b/contracts/test/v0.8/dev/ArbitrumValidator.test.ts @@ -12,7 +12,7 @@ import { abi as arbitrumSequencerStatusRecorderAbi } from '../../../artifacts/sr // @ts-ignore import { abi as arbitrumInboxAbi } from '../../../artifacts/src/v0.8/vendor/arb-bridge-eth/v0.8.0-custom/contracts/bridge/interfaces/IInbox.sol/IInbox.json' // @ts-ignore -import { abi as aggregatorAbi } from '../../../artifacts/src/v0.8/interfaces/AggregatorV2V3Interface.sol/AggregatorV2V3Interface.json' +import { abi as aggregatorAbi } from '../../../artifacts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol/AggregatorV2V3Interface.json' const truncateBigNumToAddress = (num: BigNumberish) => { // Pad, then slice off '0x' prefix diff --git a/contracts/test/v0.8/dev/OptimismValidator.test.ts b/contracts/test/v0.8/dev/OptimismValidator.test.ts index 120b1057d14..ee69211f56d 100644 --- a/contracts/test/v0.8/dev/OptimismValidator.test.ts +++ b/contracts/test/v0.8/dev/OptimismValidator.test.ts @@ -8,7 +8,7 @@ import { abi as optimismSequencerStatusRecorderAbi } from '../../../artifacts/sr // @ts-ignore import { abi as optimismL1CrossDomainMessengerAbi } from '@eth-optimism/contracts/artifacts/contracts/L1/messaging/L1CrossDomainMessenger.sol' // @ts-ignore -import { abi as aggregatorAbi } from '../../../artifacts/src/v0.8/interfaces/AggregatorV2V3Interface.sol/AggregatorV2V3Interface.json' +import { abi as aggregatorAbi } from '../../../artifacts/src/v0.8/shared/interfaces/AggregatorV2V3Interface.sol/AggregatorV2V3Interface.json' describe('OptimismValidator', () => { const GAS_LIMIT = BigNumber.from(1_900_000) From cc05bfd1cf693f57dd5d7075fbc571523212b596 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Thu, 9 Nov 2023 14:49:26 +0200 Subject: [PATCH 114/271] Remove core label dependency from common (#11241) --- common/txmgr/broadcaster.go | 2 +- common/txmgr/confirmer.go | 2 +- common/txmgr/resender.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 80c7adbfc16..d68b5091011 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -14,12 +14,12 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-relay/pkg/chains/label" "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 1d921490945..1d7446d9d2d 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -14,13 +14,13 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/chains/label" "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index d788b82773f..75781c08407 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -6,11 +6,11 @@ import ( "fmt" "time" + "github.com/smartcontractkit/chainlink-relay/pkg/chains/label" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) From b9bd82ade13007862b3820cdbc8f4a82ab8c1f1c Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 9 Nov 2023 13:05:31 +0000 Subject: [PATCH 115/271] [chore] Add run id to flakey test logs (#11243) --- .github/workflows/ci-core.yml | 2 ++ tools/flakeytests/cmd/runner/main.go | 3 ++- tools/flakeytests/reporter.go | 1 + tools/flakeytests/utils.go | 10 ++++++---- tools/flakeytests/utils_test.go | 10 +++++----- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 40535855906..d0bae664801 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -166,6 +166,7 @@ jobs: GITHUB_EVENT_PATH: ${{ github.event_path }} GITHUB_EVENT_NAME: ${{ github.event_name }} GITHUB_REPO: ${{ github.repository }} + GITHUB_RUN_ID: ${{ github.run_id }} run: | ./runner \ -grafana_auth=$GRAFANA_CLOUD_BASIC_AUTH \ @@ -173,6 +174,7 @@ jobs: -gh_sha=$GITHUB_SHA \ -gh_event_path=$GITHUB_EVENT_PATH \ -gh_event_name=$GITHUB_EVENT_NAME \ + -gh_run_id=$GITHUB_RUN_ID \ -gh_repo=$GITHUB_REPO \ -command=./tools/bin/go_core_tests \ `ls -R ./artifacts/go_core_tests*/output.txt` diff --git a/tools/flakeytests/cmd/runner/main.go b/tools/flakeytests/cmd/runner/main.go index 601832a8375..f38179f502b 100644 --- a/tools/flakeytests/cmd/runner/main.go +++ b/tools/flakeytests/cmd/runner/main.go @@ -20,6 +20,7 @@ func main() { ghEventPath := flag.String("gh_event_path", "", "path to associated gh event") ghEventName := flag.String("gh_event_name", "", "type of associated gh event") ghRepo := flag.String("gh_repo", "", "name of gh repository") + ghRunID := flag.String("gh_run_id", "", "run id of the gh workflow") flag.Parse() if *grafanaHost == "" { @@ -47,7 +48,7 @@ func main() { readers = append(readers, r) } - ctx := flakeytests.GetGithubMetadata(*ghRepo, *ghEventName, *ghSHA, *ghEventPath) + ctx := flakeytests.GetGithubMetadata(*ghRepo, *ghEventName, *ghSHA, *ghEventPath, *ghRunID) rep := flakeytests.NewLokiReporter(*grafanaHost, *grafanaAuth, *command, ctx) r := flakeytests.NewRunner(readers, rep, numReruns) err := r.Run() diff --git a/tools/flakeytests/reporter.go b/tools/flakeytests/reporter.go index db3890b5c7b..6696ec29a40 100644 --- a/tools/flakeytests/reporter.go +++ b/tools/flakeytests/reporter.go @@ -37,6 +37,7 @@ type Context struct { PullRequestURL string `json:"pull_request_url,omitempty"` Repository string `json:"repository"` Type string `json:"event_type"` + RunURL string `json:"run_url,omitempty"` } type LokiReporter struct { diff --git a/tools/flakeytests/utils.go b/tools/flakeytests/utils.go index 7ead45c8587..698a9b49c95 100644 --- a/tools/flakeytests/utils.go +++ b/tools/flakeytests/utils.go @@ -2,6 +2,7 @@ package flakeytests import ( "encoding/json" + "fmt" "io" "log" "os" @@ -29,7 +30,7 @@ func DigString(mp map[string]interface{}, path []string) (string, error) { return vs, nil } -func getGithubMetadata(repo string, eventName string, sha string, e io.Reader) Context { +func getGithubMetadata(repo string, eventName string, sha string, e io.Reader, runID string) Context { d, err := io.ReadAll(e) if err != nil { log.Fatal("Error reading gh event into string") @@ -41,7 +42,8 @@ func getGithubMetadata(repo string, eventName string, sha string, e io.Reader) C log.Fatalf("Error unmarshaling gh event at path") } - basicCtx := &Context{Repository: repo, CommitSHA: sha, Type: eventName} + runURL := fmt.Sprintf("%s/actions/%s", repo, runID) + basicCtx := &Context{Repository: repo, CommitSHA: sha, Type: eventName, RunURL: runURL} switch eventName { case "pull_request": prURL := "" @@ -68,10 +70,10 @@ func getGithubMetadata(repo string, eventName string, sha string, e io.Reader) C } } -func GetGithubMetadata(repo string, eventName string, sha string, path string) Context { +func GetGithubMetadata(repo string, eventName string, sha string, path string, runID string) Context { event, err := os.Open(path) if err != nil { log.Fatalf("Error reading gh event at path: %s", path) } - return getGithubMetadata(repo, eventName, sha, event) + return getGithubMetadata(repo, eventName, sha, event, runID) } diff --git a/tools/flakeytests/utils_test.go b/tools/flakeytests/utils_test.go index 17d597c3c02..761d9cd255c 100644 --- a/tools/flakeytests/utils_test.go +++ b/tools/flakeytests/utils_test.go @@ -36,13 +36,13 @@ var prEventTemplate = ` ` func TestGetGithubMetadata(t *testing.T) { - repo, eventName, sha, event := "chainlink", "merge_group", "a-sha", `{}` - ctx := getGithubMetadata(repo, eventName, sha, strings.NewReader(event)) - assert.Equal(t, Context{Repository: repo, CommitSHA: sha, Type: eventName}, ctx) + repo, eventName, sha, event, runID := "chainlink", "merge_group", "a-sha", `{}`, "1234" + ctx := getGithubMetadata(repo, eventName, sha, strings.NewReader(event), runID) + assert.Equal(t, Context{Repository: repo, CommitSHA: sha, Type: eventName, RunURL: fmt.Sprintf("%s/actions/%s", repo, runID)}, ctx) anotherSha, eventName, url := "another-sha", "pull_request", "a-url" event = fmt.Sprintf(prEventTemplate, anotherSha, url) sha = "302eb05d592132309b264e316f443f1ceb81b6c3" - ctx = getGithubMetadata(repo, eventName, sha, strings.NewReader(event)) - assert.Equal(t, Context{Repository: repo, CommitSHA: anotherSha, Type: eventName, PullRequestURL: url}, ctx) + ctx = getGithubMetadata(repo, eventName, sha, strings.NewReader(event), runID) + assert.Equal(t, Context{Repository: repo, CommitSHA: anotherSha, Type: eventName, PullRequestURL: url, RunURL: fmt.Sprintf("%s/actions/%s", repo, runID)}, ctx) } From bc4ff537590279a124b96ef28002acfc5579179f Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Thu, 9 Nov 2023 15:31:27 +0100 Subject: [PATCH 116/271] bump solhint & prettier deps (#11228) * bump solhint deps * bump cl solhint chainlink-solidity/explicit-returns * bump solhint * bump prettier-plugin-solidity --- contracts/.solhint.json | 3 +- contracts/.solhintignore | 2 +- contracts/package.json | 10 +- contracts/pnpm-lock.yaml | 145 +++++++++++++++--- .../interfaces/IOwnableFunctionsRouter.sol | 4 +- .../tests/v1_X/FunctionsRequest.t.sol | 24 +-- .../src/v0.8/functions/tests/v1_X/OCR2.t.sol | 36 ++--- .../interfaces/IOwnableFunctionsRouter.sol | 4 +- .../l2ep/dev/CrossDomainDelegateForwarder.sol | 4 +- .../v0.8/l2ep/dev/CrossDomainForwarder.sol | 4 +- 10 files changed, 148 insertions(+), 88 deletions(-) diff --git a/contracts/.solhint.json b/contracts/.solhint.json index 3b69ca6a7f2..e66b915d679 100644 --- a/contracts/.solhint.json +++ b/contracts/.solhint.json @@ -37,6 +37,7 @@ "chainlink-solidity/prefix-immutable-variables-with-i": "warn", "chainlink-solidity/all-caps-constant-storage-variables": "warn", "chainlink-solidity/no-hardhat-imports": "warn", - "chainlink-solidity/inherited-constructor-args-not-in-contract-definition": "warn" + "chainlink-solidity/inherited-constructor-args-not-in-contract-definition": "warn", + "chainlink-solidity/explicit-returns": "warn" } } diff --git a/contracts/.solhintignore b/contracts/.solhintignore index bc7be4fbfee..ba2aac1fb3a 100644 --- a/contracts/.solhintignore +++ b/contracts/.solhintignore @@ -1,4 +1,4 @@ -# 377 warnings +# 344 warnings #./src/v0.8/automation # Ignore Functions v1.0.0 code that was frozen after audit diff --git a/contracts/package.json b/contracts/package.json index 05a5293e48d..6d0b7af6ccd 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 350 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 371 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", @@ -72,11 +72,11 @@ "istanbul": "^0.4.5", "moment": "^2.29.4", "prettier": "^3.0.3", - "prettier-plugin-solidity": "1.1.3", + "prettier-plugin-solidity": "1.1.4-dev", "rlp": "^2.2.7", - "solhint": "^3.6.2", - "solhint-plugin-chainlink-solidity": "git+https://github.com/smartcontractkit/chainlink-solhint-rules.git", - "solhint-plugin-prettier": "^0.0.5", + "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", "tslib": "^2.6.2", diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index 2b6082d656a..ac4efd5f59f 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -140,20 +140,20 @@ devDependencies: specifier: ^3.0.3 version: 3.0.3 prettier-plugin-solidity: - specifier: 1.1.3 - version: 1.1.3(prettier@3.0.3) + specifier: 1.1.4-dev + version: 1.1.4-dev(prettier@3.0.3) rlp: specifier: ^2.2.7 version: 2.2.7 solhint: - specifier: ^3.6.2 - version: 3.6.2 + specifier: ^4.0.0 + version: 4.0.0 solhint-plugin-chainlink-solidity: - specifier: git+https://github.com/smartcontractkit/chainlink-solhint-rules.git - version: github.com/smartcontractkit/chainlink-solhint-rules/6229ce5d3cc3e4a2454411bebc887c5ca240dcf2 + specifier: git+https://github.com/smartcontractkit/chainlink-solhint-rules.git#v1.2.0 + version: github.com/smartcontractkit/chainlink-solhint-rules/cfc50b32f95b730304a50deb2e27e88d87115874 solhint-plugin-prettier: - specifier: ^0.0.5 - version: 0.0.5(prettier-plugin-solidity@1.1.3)(prettier@3.0.3) + specifier: ^0.1.0 + version: 0.1.0(prettier-plugin-solidity@1.1.4-dev)(prettier@3.0.3) solidity-coverage: specifier: ^0.8.5 version: 0.8.5(hardhat@2.18.1) @@ -1317,6 +1317,35 @@ packages: tslib: 2.6.2 dev: true + /@pnpm/config.env-replace@1.1.0: + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + dev: true + + /@pnpm/network.ca-file@1.0.2: + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + dependencies: + graceful-fs: 4.2.10 + dev: true + + /@pnpm/npm-conf@2.2.2: + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + dev: true + + /@prettier/sync@0.3.0(prettier@3.0.3): + resolution: {integrity: sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==} + peerDependencies: + prettier: ^3.0.0 + dependencies: + prettier: 3.0.3 + dev: true + /@resolver-engine/core@0.3.3: resolution: {integrity: sha512-eB8nEbKDJJBi5p5SrvrvILn4a0h42bKtbCTri3ZxCGt6UvoQyp7HnGOfki944bUjBSHKK3RvgfViHn+kqdXtnQ==} dependencies: @@ -1477,6 +1506,12 @@ packages: antlr4ts: 0.5.0-alpha.4 dev: true + /@solidity-parser/parser@0.16.1: + resolution: {integrity: sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==} + dependencies: + antlr4ts: 0.5.0-alpha.4 + dev: true + /@szmarczak/http-timer@1.1.2: resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} engines: {node: '>=6'} @@ -3833,6 +3868,13 @@ packages: typedarray: 0.0.6 dev: true + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /constant-case@2.0.0: resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} dependencies: @@ -7326,6 +7368,13 @@ packages: graceful-fs: 4.2.10 dev: true + /latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + dependencies: + package-json: 8.1.1 + dev: true + /lcid@1.0.0: resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==} engines: {node: '>=0.10.0'} @@ -8523,6 +8572,16 @@ packages: engines: {node: '>=6'} dev: true + /package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + dependencies: + got: 12.1.0 + registry-auth-token: 5.0.2 + registry-url: 6.0.1 + semver: 7.5.4 + dev: true + /param-case@2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} dependencies: @@ -8822,6 +8881,7 @@ packages: /prettier-plugin-solidity@1.1.3(prettier@2.8.8): resolution: {integrity: sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==} engines: {node: '>=12'} + requiresBuild: true peerDependencies: prettier: '>=2.3.0 || >=3.0.0-alpha.0' dependencies: @@ -8832,15 +8892,15 @@ packages: dev: true optional: true - /prettier-plugin-solidity@1.1.3(prettier@3.0.3): - resolution: {integrity: sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==} - engines: {node: '>=12'} + /prettier-plugin-solidity@1.1.4-dev(prettier@3.0.3): + resolution: {integrity: sha512-SIDnHIPLN/Pod/dZoyJL07ViEcDxrXoT47ROQshpA/WFgyq/rRzLIc3oWkKfWiicHOD493Y/L1n9ds1GbwPoKQ==} + engines: {node: '>=16'} peerDependencies: - prettier: '>=2.3.0 || >=3.0.0-alpha.0' + prettier: '>=2.3.0' dependencies: - '@solidity-parser/parser': 0.16.0 + '@solidity-parser/parser': 0.16.1 prettier: 3.0.3 - semver: 7.5.0 + semver: 7.5.4 solidity-comments-extractor: 0.0.7 dev: true @@ -8892,6 +8952,10 @@ packages: signal-exit: 3.0.7 dev: true + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + /proxy-addr@2.0.5: resolution: {integrity: sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==} engines: {node: '>= 0.10'} @@ -9080,6 +9144,16 @@ packages: unpipe: 1.0.0 dev: true + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + dev: true + /read-pkg-up@1.0.1: resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==} engines: {node: '>=0.10.0'} @@ -9216,6 +9290,20 @@ packages: regjsparser: 0.1.5 dev: true + /registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + dependencies: + '@pnpm/npm-conf': 2.2.2 + dev: true + + /registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + dependencies: + rc: 1.2.8 + dev: true + /regjsgen@0.2.0: resolution: {integrity: sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==} dev: true @@ -9553,9 +9641,11 @@ packages: resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} engines: {node: '>=10'} hasBin: true + requiresBuild: true dependencies: lru-cache: 6.0.0 dev: true + optional: true /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} @@ -9863,19 +9953,20 @@ packages: - debug dev: true - /solhint-plugin-prettier@0.0.5(prettier-plugin-solidity@1.1.3)(prettier@3.0.3): - resolution: {integrity: sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==} + /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.1.4-dev)(prettier@3.0.3): + resolution: {integrity: sha512-SDOTSM6tZxZ6hamrzl3GUgzF77FM6jZplgL2plFBclj/OjKP8Z3eIPojKU73gRr0MvOS8ACZILn8a5g0VTz/Gw==} peerDependencies: - prettier: ^1.15.0 || ^2.0.0 - prettier-plugin-solidity: ^1.0.0-alpha.14 + prettier: ^3.0.0 + prettier-plugin-solidity: ^1.0.0 dependencies: + '@prettier/sync': 0.3.0(prettier@3.0.3) prettier: 3.0.3 prettier-linter-helpers: 1.0.0 - prettier-plugin-solidity: 1.1.3(prettier@3.0.3) + prettier-plugin-solidity: 1.1.4-dev(prettier@3.0.3) dev: true - /solhint@3.6.2: - resolution: {integrity: sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==} + /solhint@4.0.0: + resolution: {integrity: sha512-bFViMcFvhqVd/HK3Roo7xZXX5nbujS7Bxeg5vnZc9QvH0yCWCrQ38Yrn1pbAY9tlKROc6wFr+rK1mxYgYrjZgA==} hasBin: true dependencies: '@solidity-parser/parser': 0.16.0 @@ -9889,6 +9980,7 @@ packages: glob: 8.1.0 ignore: 5.2.4 js-yaml: 4.1.0 + latest-version: 7.0.0 lodash: 4.17.21 pluralize: 8.0.0 semver: 7.5.4 @@ -10321,6 +10413,11 @@ packages: engines: {node: '>=4'} dev: true + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -12349,8 +12446,8 @@ packages: ethereumjs-util: 6.2.1 dev: true - github.com/smartcontractkit/chainlink-solhint-rules/6229ce5d3cc3e4a2454411bebc887c5ca240dcf2: - resolution: {tarball: https://codeload.github.com/smartcontractkit/chainlink-solhint-rules/tar.gz/6229ce5d3cc3e4a2454411bebc887c5ca240dcf2} + github.com/smartcontractkit/chainlink-solhint-rules/cfc50b32f95b730304a50deb2e27e88d87115874: + resolution: {tarball: https://codeload.github.com/smartcontractkit/chainlink-solhint-rules/tar.gz/cfc50b32f95b730304a50deb2e27e88d87115874} name: '@chainlink/solhint-plugin-chainlink-solidity' - version: 1.0.1 + version: 1.2.0 dev: true diff --git a/contracts/src/v0.8/functions/dev/v1_X/interfaces/IOwnableFunctionsRouter.sol b/contracts/src/v0.8/functions/dev/v1_X/interfaces/IOwnableFunctionsRouter.sol index 39b84a930aa..f6d7880da3e 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/interfaces/IOwnableFunctionsRouter.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/interfaces/IOwnableFunctionsRouter.sol @@ -5,6 +5,4 @@ import {IFunctionsRouter} from "./IFunctionsRouter.sol"; import {IOwnable} from "../../../../shared/interfaces/IOwnable.sol"; /// @title Chainlink Functions Router interface with Ownability. -interface IOwnableFunctionsRouter is IOwnable, IFunctionsRouter { - -} +interface IOwnableFunctionsRouter is IOwnable, IFunctionsRouter {} diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsRequest.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsRequest.t.sol index 5457a221b61..e9684d9f5b3 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsRequest.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsRequest.t.sol @@ -30,31 +30,19 @@ contract FunctionsRequest_EncodeCBOR is Test { } /// @notice #initializeRequest -contract FunctionsRequest_InitializeRequest is Test { - -} +contract FunctionsRequest_InitializeRequest is Test {} /// @notice #initializeRequestForInlineJavaScript -contract FunctionsRequest_InitializeRequestForInlineJavaScript is Test { - -} +contract FunctionsRequest_InitializeRequestForInlineJavaScript is Test {} /// @notice #addSecretsReference -contract FunctionsRequest_AddSecretsReference is Test { - -} +contract FunctionsRequest_AddSecretsReference is Test {} /// @notice #addDONHostedSecrets -contract FunctionsRequest_AddDONHostedSecrets is Test { - -} +contract FunctionsRequest_AddDONHostedSecrets is Test {} /// @notice #setArgs -contract FunctionsRequest_SetArgs is Test { - -} +contract FunctionsRequest_SetArgs is Test {} /// @notice #setBytesArgs -contract FunctionsRequest_SetBytesArgs is Test { - -} +contract FunctionsRequest_SetBytesArgs is Test {} diff --git a/contracts/src/v0.8/functions/tests/v1_X/OCR2.t.sol b/contracts/src/v0.8/functions/tests/v1_X/OCR2.t.sol index 745ad4f0ae9..3dc0db85a47 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/OCR2.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/OCR2.t.sol @@ -6,39 +6,25 @@ pragma solidity ^0.8.19; // ================================================================ /// @notice #constructor -contract OCR2Base_Constructor { - -} +contract OCR2Base_Constructor {} /// @notice #checkConfigValid -contract OCR2Base_CheckConfigValid { - -} +contract OCR2Base_CheckConfigValid {} /// @notice #latestConfigDigestAndEpoch -contract OCR2Base_LatestConfigDigestAndEpoch { - -} +contract OCR2Base_LatestConfigDigestAndEpoch {} /// @notice #setConfig -contract OCR2Base_SetConfig { - -} +contract OCR2Base_SetConfig {} /// @notice #configDigestFromConfigData -contract OCR2Base_ConfigDigestFromConfigData { - -} +contract OCR2Base_ConfigDigestFromConfigData {} /// @notice #latestConfigDetails -contract OCR2Base_LatestConfigDetails { - -} +contract OCR2Base_LatestConfigDetails {} /// @notice #transmitters -contract OCR2Base_Transmitters { - -} +contract OCR2Base_Transmitters {} /// @notice #_report contract OCR2Base__Report { @@ -46,11 +32,7 @@ contract OCR2Base__Report { } /// @notice #requireExpectedMsgDataLength -contract OCR2Base_RequireExpectedMsgDataLength { - -} +contract OCR2Base_RequireExpectedMsgDataLength {} /// @notice #transmit -contract OCR2Base_Transmit { - -} +contract OCR2Base_Transmit {} diff --git a/contracts/src/v0.8/functions/v1_0_0/interfaces/IOwnableFunctionsRouter.sol b/contracts/src/v0.8/functions/v1_0_0/interfaces/IOwnableFunctionsRouter.sol index 89eb48022be..c5f3d82677e 100644 --- a/contracts/src/v0.8/functions/v1_0_0/interfaces/IOwnableFunctionsRouter.sol +++ b/contracts/src/v0.8/functions/v1_0_0/interfaces/IOwnableFunctionsRouter.sol @@ -5,6 +5,4 @@ import {IFunctionsRouter} from "./IFunctionsRouter.sol"; import {IOwnable} from "../../../shared/interfaces/IOwnable.sol"; /// @title Chainlink Functions Router interface with Ownability. -interface IOwnableFunctionsRouter is IOwnable, IFunctionsRouter { - -} +interface IOwnableFunctionsRouter is IOwnable, IFunctionsRouter {} diff --git a/contracts/src/v0.8/l2ep/dev/CrossDomainDelegateForwarder.sol b/contracts/src/v0.8/l2ep/dev/CrossDomainDelegateForwarder.sol index 1eb6cba932d..5dc73619afc 100644 --- a/contracts/src/v0.8/l2ep/dev/CrossDomainDelegateForwarder.sol +++ b/contracts/src/v0.8/l2ep/dev/CrossDomainDelegateForwarder.sol @@ -10,6 +10,4 @@ import {DelegateForwarderInterface} from "./interfaces/DelegateForwarderInterfac * @dev Any other L2 contract which uses this contract's address as a privileged position, * can consider that position to be held by the `l1Owner` */ -abstract contract CrossDomainDelegateForwarder is DelegateForwarderInterface, CrossDomainOwnable { - -} +abstract contract CrossDomainDelegateForwarder is DelegateForwarderInterface, CrossDomainOwnable {} diff --git a/contracts/src/v0.8/l2ep/dev/CrossDomainForwarder.sol b/contracts/src/v0.8/l2ep/dev/CrossDomainForwarder.sol index 1f9066c5054..8f218f66b80 100644 --- a/contracts/src/v0.8/l2ep/dev/CrossDomainForwarder.sol +++ b/contracts/src/v0.8/l2ep/dev/CrossDomainForwarder.sol @@ -10,6 +10,4 @@ import {ForwarderInterface} from "./interfaces/ForwarderInterface.sol"; * @dev Any other L2 contract which uses this contract's address as a privileged position, * can consider that position to be held by the `l1Owner` */ -abstract contract CrossDomainForwarder is ForwarderInterface, CrossDomainOwnable { - -} +abstract contract CrossDomainForwarder is ForwarderInterface, CrossDomainOwnable {} From 606e5f2b113bca494199a8385819132d6625403d Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 9 Nov 2023 09:24:14 -0600 Subject: [PATCH 117/271] switch sqlx from smartcontractkit to jmoiron (#11238) --- core/bridges/orm.go | 2 +- core/bridges/orm_test.go | 2 +- core/chains/evm/chain.go | 2 +- core/chains/evm/chain_test.go | 2 +- core/chains/evm/evm_txm.go | 2 +- .../evm/forwarders/forwarder_manager.go | 3 +- core/chains/evm/forwarders/orm.go | 2 +- core/chains/evm/forwarders/orm_test.go | 2 +- .../evm/headtracker/head_tracker_test.go | 2 +- core/chains/evm/headtracker/orm.go | 2 +- core/chains/evm/log/helpers_test.go | 2 +- core/chains/evm/log/orm.go | 2 +- core/chains/evm/logpoller/observability.go | 2 +- core/chains/evm/logpoller/orm.go | 2 +- core/chains/evm/txmgr/builder.go | 2 +- core/chains/evm/txmgr/evm_tx_store.go | 2 +- core/chains/evm/txmgr/txmgr_test.go | 2 +- core/cmd/ocr2vrf_configure_commands.go | 2 +- core/cmd/shell.go | 2 +- core/cmd/shell_local.go | 2 +- core/internal/cltest/cltest.go | 2 +- core/internal/cltest/factories.go | 2 +- core/internal/cltest/heavyweight/orm.go | 2 +- core/internal/cltest/job_factories.go | 2 +- core/internal/cltest/mocks.go | 2 +- core/internal/mocks/application.go | 2 +- core/internal/testutils/evmtest/evmtest.go | 2 +- core/internal/testutils/pgtest/pgtest.go | 2 +- core/internal/testutils/pgtest/txdb.go | 2 +- core/internal/testutils/pgtest/txdb_test.go | 2 +- core/internal/testutils/testutils.go | 2 +- core/scripts/go.mod | 5 ++-- core/scripts/go.sum | 6 ++-- core/scripts/vrfv2/testnet/main.go | 2 +- core/scripts/vrfv2plus/testnet/main.go | 5 ++-- core/services/chainlink/application.go | 2 +- core/services/chainlink/relayer_factory.go | 2 +- core/services/feeds/orm.go | 2 +- core/services/feeds/orm_test.go | 2 +- core/services/feeds/service.go | 3 +- core/services/fluxmonitorv2/delegate.go | 2 +- core/services/fluxmonitorv2/flux_monitor.go | 3 +- .../fluxmonitorv2/flux_monitor_test.go | 2 +- .../fluxmonitorv2/integrations_test.go | 2 +- core/services/fluxmonitorv2/orm.go | 2 +- core/services/functions/orm.go | 2 +- core/services/job/helpers_test.go | 2 +- core/services/job/job_orm_test.go | 3 +- .../job/job_pipeline_orm_integration_test.go | 2 +- core/services/job/orm.go | 6 ++-- core/services/job/orm_test.go | 2 +- core/services/job/spawner.go | 2 +- core/services/job/spawner_test.go | 2 +- core/services/keeper/delegate.go | 2 +- core/services/keeper/orm.go | 2 +- core/services/keeper/orm_test.go | 2 +- .../registry_synchronizer_helper_test.go | 2 +- core/services/keeper/upkeep_executer_test.go | 2 +- core/services/keystore/helpers_test.go | 2 +- core/services/keystore/keystoretest.go | 2 +- core/services/keystore/master.go | 2 +- core/services/keystore/orm.go | 2 +- core/services/ocr/contract_tracker.go | 3 +- core/services/ocr/database.go | 2 +- core/services/ocr/delegate.go | 2 +- core/services/ocr/helpers_internal_test.go | 2 +- core/services/ocr2/database.go | 2 +- core/services/ocr2/database_test.go | 2 +- core/services/ocr2/delegate.go | 2 +- .../ocr2/plugins/dkg/persistence/db.go | 2 +- .../ocr2/plugins/dkg/persistence/db_test.go | 2 +- core/services/ocr2/plugins/dkg/plugin.go | 2 +- .../services/ocr2/plugins/functions/plugin.go | 2 +- .../evm21/logprovider/integration_test.go | 2 +- .../ocr2/plugins/ocr2keeper/evm21/services.go | 2 +- .../ocr2keeper/evm21/upkeepstate/orm.go | 2 +- core/services/ocr2/plugins/ocr2keeper/util.go | 2 +- core/services/ocrbootstrap/database_test.go | 2 +- core/services/ocrbootstrap/delegate.go | 2 +- core/services/ocrcommon/peer_wrapper.go | 2 +- core/services/ocrcommon/peerstore.go | 3 +- core/services/pg/connection.go | 2 +- core/services/pg/connection_test.go | 2 +- core/services/pg/helpers_test.go | 2 +- core/services/pg/lease_lock.go | 2 +- core/services/pg/lease_lock_test.go | 2 +- core/services/pg/locked_db.go | 2 +- core/services/pg/q.go | 2 +- core/services/pg/sqlx.go | 2 +- core/services/pg/transaction.go | 2 +- core/services/pipeline/orm.go | 2 +- core/services/pipeline/orm_test.go | 2 +- core/services/pipeline/runner_test.go | 2 +- core/services/pipeline/test_helpers_test.go | 2 +- core/services/relay/evm/evm.go | 2 +- core/services/relay/evm/evm_test.go | 2 +- core/services/relay/evm/median.go | 2 +- core/services/relay/evm/mercury/orm.go | 2 +- .../evm/mercury/persistence_manager_test.go | 2 +- .../services/relay/evm/mercury/transmitter.go | 2 +- core/services/relay/evm/ocr2keeper.go | 2 +- core/services/relay/evm/ocr2vrf.go | 2 +- .../relay/evm/request_round_tracker.go | 3 +- core/services/s4/postgres_orm.go | 2 +- core/services/versioning/orm.go | 2 +- core/services/vrf/delegate.go | 2 +- core/services/vrf/delegate_test.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 2 +- core/services/webhook/authorizer_test.go | 2 +- .../webhook/external_initiator_manager.go | 2 +- core/sessions/ldapauth/helpers_test.go | 2 +- core/sessions/ldapauth/ldap.go | 2 +- core/sessions/ldapauth/ldap_test.go | 2 +- core/sessions/ldapauth/sync.go | 2 +- core/sessions/localauth/orm.go | 2 +- core/sessions/localauth/orm_test.go | 2 +- core/sessions/webauthn.go | 2 +- core/sessions/webauthn_test.go | 2 +- core/store/migrate/migrate.go | 2 +- .../migrations/0036_external_job_id.go | 2 +- core/web/jobs_controller_test.go | 2 +- go.mod | 5 ++-- go.sum | 6 ++-- integration-tests/go.mod | 5 ++-- integration-tests/go.sum | 6 ++-- .../universal/log_poller/helpers.go | 28 +++++++++---------- 126 files changed, 156 insertions(+), 157 deletions(-) diff --git a/core/bridges/orm.go b/core/bridges/orm.go index 96801f4484c..cfad1da836e 100644 --- a/core/bridges/orm.go +++ b/core/bridges/orm.go @@ -6,8 +6,8 @@ import ( "sync" "time" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/bridges/orm_test.go b/core/bridges/orm_test.go index b110b4f519d..0b485764c8b 100644 --- a/core/bridges/orm_test.go +++ b/core/bridges/orm_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" diff --git a/core/chains/evm/chain.go b/core/chains/evm/chain.go index 936abc6216c..b5896393d3c 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/evm/chain.go @@ -11,7 +11,7 @@ import ( gotoml "github.com/pelletier/go-toml/v2" "go.uber.org/multierr" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" relaychains "github.com/smartcontractkit/chainlink-relay/pkg/chains" "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/chains/evm/chain_test.go b/core/chains/evm/chain_test.go index ba24598ef73..f25af87a35b 100644 --- a/core/chains/evm/chain_test.go +++ b/core/chains/evm/chain_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" diff --git a/core/chains/evm/evm_txm.go b/core/chains/evm/evm_txm.go index a8673e954a6..bfc0f6378bf 100644 --- a/core/chains/evm/evm_txm.go +++ b/core/chains/evm/evm_txm.go @@ -3,7 +3,7 @@ package evm import ( "fmt" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index 46bca95ba30..934da487fd7 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -10,9 +10,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmlogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/forwarders/orm.go b/core/chains/evm/forwarders/orm.go index 287698d22f6..df89dbe29e9 100644 --- a/core/chains/evm/forwarders/orm.go +++ b/core/chains/evm/forwarders/orm.go @@ -4,8 +4,8 @@ import ( "database/sql" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/chains/evm/forwarders/orm_test.go b/core/chains/evm/forwarders/orm_test.go index a3d5c2831fe..f6d63dc574f 100644 --- a/core/chains/evm/forwarders/orm_test.go +++ b/core/chains/evm/forwarders/orm_test.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" ) type TestORM struct { diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 502aa4ae6db..8af344098f8 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -18,7 +18,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/exp/maps" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/chains/evm/headtracker/orm.go b/core/chains/evm/headtracker/orm.go index 426df68b301..34f46ce44de 100644 --- a/core/chains/evm/headtracker/orm.go +++ b/core/chains/evm/headtracker/orm.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index 688757a3e96..f787002578e 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -18,7 +18,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" diff --git a/core/chains/evm/log/orm.go b/core/chains/evm/log/orm.go index 4e51940f344..d383419d728 100644 --- a/core/chains/evm/log/orm.go +++ b/core/chains/evm/log/orm.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/chains/evm/logpoller/observability.go b/core/chains/evm/logpoller/observability.go index 7f54fa9f09a..c4b58b42a2e 100644 --- a/core/chains/evm/logpoller/observability.go +++ b/core/chains/evm/logpoller/observability.go @@ -5,9 +5,9 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index 06f4acbb4f1..e044be2e0c9 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -8,8 +8,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/chains/evm/txmgr/builder.go b/core/chains/evm/txmgr/builder.go index 9123d1dfc03..5e3d61301ca 100644 --- a/core/chains/evm/txmgr/builder.go +++ b/core/chains/evm/txmgr/builder.go @@ -4,7 +4,7 @@ import ( "math/big" "time" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 971103bdfd5..4db7989b466 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -13,9 +13,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/jackc/pgconn" + "github.com/jmoiron/sqlx" "github.com/lib/pq" pkgerrors "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" nullv4 "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/common/txmgr" diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 4aa54bc52a1..4e201b9c6fe 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" diff --git a/core/cmd/ocr2vrf_configure_commands.go b/core/cmd/ocr2vrf_configure_commands.go index a3feddd611d..bb4cef4708b 100644 --- a/core/cmd/ocr2vrf_configure_commands.go +++ b/core/cmd/ocr2vrf_configure_commands.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" "github.com/urfave/cli" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 80ecd2590b0..595c42b9fe1 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -29,7 +29,7 @@ import ( "go.uber.org/zap/zapcore" "golang.org/x/sync/errgroup" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/loop" diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index dea9a29359e..954cead5c37 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -29,7 +29,7 @@ import ( "golang.org/x/sync/errgroup" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/build" diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index e5c2ca031a7..778ccbdb154 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -39,8 +39,8 @@ import ( "github.com/tidwall/gjson" "github.com/urfave/cli" + "github.com/jmoiron/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/loop" diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index 82235baf6b1..a52b9a5d06b 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -21,7 +21,7 @@ import ( "github.com/urfave/cli" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/core/internal/cltest/heavyweight/orm.go b/core/internal/cltest/heavyweight/orm.go index b46e7114cf3..5df28a49778 100644 --- a/core/internal/cltest/heavyweight/orm.go +++ b/core/internal/cltest/heavyweight/orm.go @@ -18,7 +18,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/internal/cltest/job_factories.go b/core/internal/cltest/job_factories.go index b76d6c7ec2e..a9e403fb608 100644 --- a/core/internal/cltest/job_factories.go +++ b/core/internal/cltest/job_factories.go @@ -7,7 +7,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index 00f72199dd9..540924d7f02 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/internal/mocks/application.go b/core/internal/mocks/application.go index 7853361db93..48f8e12dac3 100644 --- a/core/internal/mocks/application.go +++ b/core/internal/mocks/application.go @@ -31,7 +31,7 @@ import ( sessions "github.com/smartcontractkit/chainlink/v2/core/sessions" - sqlx "github.com/smartcontractkit/sqlx" + sqlx "github.com/jmoiron/sqlx" txmgr "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 3a08c815166..80237d218d7 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/ethereum/go-ethereum" + "github.com/jmoiron/sqlx" "github.com/pelletier/go-toml/v2" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" diff --git a/core/internal/testutils/pgtest/pgtest.go b/core/internal/testutils/pgtest/pgtest.go index 283326de85f..1900fcc62b3 100644 --- a/core/internal/testutils/pgtest/pgtest.go +++ b/core/internal/testutils/pgtest/pgtest.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/google/uuid" + "github.com/jmoiron/sqlx" "github.com/scylladb/go-reflectx" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/core/internal/testutils/pgtest/txdb.go b/core/internal/testutils/pgtest/txdb.go index 598a5dddc55..da9fd6cb2d0 100644 --- a/core/internal/testutils/pgtest/txdb.go +++ b/core/internal/testutils/pgtest/txdb.go @@ -12,7 +12,7 @@ import ( "sync" "testing" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/v2/core/config/env" diff --git a/core/internal/testutils/pgtest/txdb_test.go b/core/internal/testutils/pgtest/txdb_test.go index 71960c6150a..c1aeef4b8c2 100644 --- a/core/internal/testutils/pgtest/txdb_test.go +++ b/core/internal/testutils/pgtest/txdb_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/google/uuid" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/stretchr/testify/assert" ) diff --git a/core/internal/testutils/testutils.go b/core/internal/testutils/testutils.go index 79c86f0c5f8..6ffd873d092 100644 --- a/core/internal/testutils/testutils.go +++ b/core/internal/testutils/testutils.go @@ -27,7 +27,7 @@ import ( "github.com/tidwall/gjson" "go.uber.org/zap/zaptest/observer" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/core/scripts/go.mod b/core/scripts/go.mod index aada3850db2..3adcc130bc0 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -13,6 +13,7 @@ require ( github.com/ethereum/go-ethereum v1.12.0 github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.1 + github.com/jmoiron/sqlx v1.3.5 github.com/joho/godotenv v1.4.0 github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f github.com/montanaflynn/stats v0.7.1 @@ -24,7 +25,6 @@ require ( github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 - github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 @@ -199,7 +199,6 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.0 // indirect @@ -304,7 +303,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 4f42c84d4ef..7f5d0e227d6 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1462,8 +1462,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= @@ -1480,8 +1480,6 @@ github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGuj github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= -github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb h1:OMaBUb4X9IFPLbGbCHsMU+kw/BPCrewaVwWGIBc0I4A= -github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb/go.mod h1:HNUu4cJekUdsJbwRBCiOybtkPJEfGRELQPe2tkoDEyk= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/core/scripts/vrfv2/testnet/main.go b/core/scripts/vrfv2/testnet/main.go index 5b216776bd9..677c0b105ea 100644 --- a/core/scripts/vrfv2/testnet/main.go +++ b/core/scripts/vrfv2/testnet/main.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/shopspring/decimal" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/assets" diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index 0d1bf9a9481..b7940d6fda0 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -6,12 +6,13 @@ import ( "encoding/hex" "flag" "fmt" - "github.com/smartcontractkit/chainlink/core/scripts/vrfv2plus/testnet/v2plusscripts" "log" "math/big" "os" "strings" + "github.com/smartcontractkit/chainlink/core/scripts/vrfv2plus/testnet/v2plusscripts" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/chain_specific_util_helper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" @@ -25,7 +26,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/shopspring/decimal" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/assets" diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 3285acdc07a..0d479b1f1ab 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -16,7 +16,7 @@ import ( "go.uber.org/multierr" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/loop" relayservices "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 76bfcd16412..d452decda1e 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -7,7 +7,7 @@ import ( "github.com/pelletier/go-toml/v2" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" diff --git a/core/services/feeds/orm.go b/core/services/feeds/orm.go index 30b6ad632a6..24ed7b8b369 100644 --- a/core/services/feeds/orm.go +++ b/core/services/feeds/orm.go @@ -9,7 +9,7 @@ import ( "github.com/lib/pq" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/feeds/orm_test.go b/core/services/feeds/orm_test.go index 3d51ad45fff..02b9e24739c 100644 --- a/core/services/feeds/orm_test.go +++ b/core/services/feeds/orm_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index 20919606faf..f6e8952d6b1 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -15,9 +15,10 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" diff --git a/core/services/fluxmonitorv2/delegate.go b/core/services/fluxmonitorv2/delegate.go index d380122f715..e63f3556726 100644 --- a/core/services/fluxmonitorv2/delegate.go +++ b/core/services/fluxmonitorv2/delegate.go @@ -3,7 +3,7 @@ package fluxmonitorv2 import ( "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/services/fluxmonitorv2/flux_monitor.go b/core/services/fluxmonitorv2/flux_monitor.go index 99d33c42399..5dbeaeafc31 100644 --- a/core/services/fluxmonitorv2/flux_monitor.go +++ b/core/services/fluxmonitorv2/flux_monitor.go @@ -13,9 +13,10 @@ import ( "github.com/pkg/errors" "github.com/shopspring/decimal" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/bridges" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index 26ec520e7cd..e81e1ba9e63 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -18,7 +18,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/assets" diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index b2f24e08d54..38c73d3ad74 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -24,7 +24,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" diff --git a/core/services/fluxmonitorv2/orm.go b/core/services/fluxmonitorv2/orm.go index 61395e8708a..f85ab146c7e 100644 --- a/core/services/fluxmonitorv2/orm.go +++ b/core/services/fluxmonitorv2/orm.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/services/functions/orm.go b/core/services/functions/orm.go index b6f692019a1..7838c700858 100644 --- a/core/services/functions/orm.go +++ b/core/services/functions/orm.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/job/helpers_test.go b/core/services/job/helpers_test.go index 167ed5297cc..4151ed401c8 100644 --- a/core/services/job/helpers_test.go +++ b/core/services/job/helpers_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 6306dedcefa..f4471e75c68 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -16,8 +16,6 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -38,6 +36,7 @@ import ( ocr2validate "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/validate" "github.com/smartcontractkit/chainlink/v2/core/services/ocrbootstrap" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" diff --git a/core/services/job/job_pipeline_orm_integration_test.go b/core/services/job/job_pipeline_orm_integration_test.go index a2b6cc4618c..f1307753d29 100644 --- a/core/services/job/job_pipeline_orm_integration_test.go +++ b/core/services/job/job_pipeline_orm_integration_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/services/job/orm.go b/core/services/job/orm.go index c6ec4ed5c8e..fb897bc9281 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -16,7 +16,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/types" @@ -992,11 +992,13 @@ func (o *orm) loadPipelineRunIDs(jobID *int32, offset, limit int, tx pg.Queryer) // range minID <-> maxID. for n := int64(1000); maxID > 0 && len(ids) < limit; n *= 2 { + var batch []int64 minID := maxID - n - if err = tx.Select(&ids, stmt, offset, limit-len(ids), minID, maxID); err != nil { + if err = tx.Select(&batch, stmt, offset, limit-len(ids), minID, maxID); err != nil { err = errors.Wrap(err, "error loading runs") return } + ids = append(ids, batch...) if offset > 0 { if len(ids) > 0 { // If we're already receiving rows back, then we no longer need an offset diff --git a/core/services/job/orm_test.go b/core/services/job/orm_test.go index 48805388a36..41d02dba060 100644 --- a/core/services/job/orm_test.go +++ b/core/services/job/orm_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" diff --git a/core/services/job/spawner.go b/core/services/job/spawner.go index b2a8dad68f0..03ee8cee13a 100644 --- a/core/services/job/spawner.go +++ b/core/services/job/spawner.go @@ -9,7 +9,7 @@ import ( pkgerrors "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" relayservices "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index 1f10a86e9ce..cfe646d8660 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/services/keeper/delegate.go b/core/services/keeper/delegate.go index 6c68203f843..6d413624969 100644 --- a/core/services/keeper/delegate.go +++ b/core/services/keeper/delegate.go @@ -3,7 +3,7 @@ package keeper import ( "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/keeper/orm.go b/core/services/keeper/orm.go index e281d610644..91883f8056c 100644 --- a/core/services/keeper/orm.go +++ b/core/services/keeper/orm.go @@ -3,9 +3,9 @@ package keeper import ( "math/rand" + "github.com/jmoiron/sqlx" "github.com/lib/pq" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" diff --git a/core/services/keeper/orm_test.go b/core/services/keeper/orm_test.go index d990effa103..d67baa09a06 100644 --- a/core/services/keeper/orm_test.go +++ b/core/services/keeper/orm_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/services/keeper/registry_synchronizer_helper_test.go b/core/services/keeper/registry_synchronizer_helper_test.go index 63dc6343535..966366b1069 100644 --- a/core/services/keeper/registry_synchronizer_helper_test.go +++ b/core/services/keeper/registry_synchronizer_helper_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 7f9698435f8..32b1d2c191d 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/services/keystore/helpers_test.go b/core/services/keystore/helpers_test.go index 13627dd0231..d0b2a21ab38 100644 --- a/core/services/keystore/helpers_test.go +++ b/core/services/keystore/helpers_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" diff --git a/core/services/keystore/keystoretest.go b/core/services/keystore/keystoretest.go index 0b5ce4e0057..6efc8e76bcf 100644 --- a/core/services/keystore/keystoretest.go +++ b/core/services/keystore/keystoretest.go @@ -4,7 +4,7 @@ import ( "errors" "sync" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/keystore/master.go b/core/services/keystore/master.go index fb28202b527..05f19495f9d 100644 --- a/core/services/keystore/master.go +++ b/core/services/keystore/master.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" diff --git a/core/services/keystore/orm.go b/core/services/keystore/orm.go index 6f612105ea9..3d75d6f2369 100644 --- a/core/services/keystore/orm.go +++ b/core/services/keystore/orm.go @@ -7,8 +7,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" ) func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) ksORM { diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index f49e556d4e8..db19bdd4f0a 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -14,13 +14,14 @@ import ( gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-relay/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" diff --git a/core/services/ocr/database.go b/core/services/ocr/database.go index cd8e584e39a..524dfa0e7bb 100644 --- a/core/services/ocr/database.go +++ b/core/services/ocr/database.go @@ -11,9 +11,9 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index 6eb6714a474..0559469abb4 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" diff --git a/core/services/ocr/helpers_internal_test.go b/core/services/ocr/helpers_internal_test.go index 9a1f887986e..57b669ef401 100644 --- a/core/services/ocr/helpers_internal_test.go +++ b/core/services/ocr/helpers_internal_test.go @@ -3,7 +3,7 @@ package ocr import ( "testing" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/database.go b/core/services/ocr2/database.go index 7061ad0452f..5591f33fd40 100644 --- a/core/services/ocr2/database.go +++ b/core/services/ocr2/database.go @@ -6,11 +6,11 @@ import ( "encoding/binary" "time" + "github.com/jmoiron/sqlx" "github.com/lib/pq" "github.com/pkg/errors" ocrcommon "github.com/smartcontractkit/libocr/commontypes" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/ocr2/database_test.go b/core/services/ocr2/database_test.go index aabb2b33a79..b70ac629da1 100644 --- a/core/services/ocr2/database_test.go +++ b/core/services/ocr2/database_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/jmoiron/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 1e87915bd15..95ec1469156 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -27,7 +28,6 @@ import ( "github.com/smartcontractkit/ocr2vrf/altbn_128" dkgpkg "github.com/smartcontractkit/ocr2vrf/dkg" "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - "github.com/smartcontractkit/sqlx" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/loop" diff --git a/core/services/ocr2/plugins/dkg/persistence/db.go b/core/services/ocr2/plugins/dkg/persistence/db.go index 75fb3b391fa..c020a68cbe7 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db.go +++ b/core/services/ocr2/plugins/dkg/persistence/db.go @@ -9,13 +9,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" "github.com/smartcontractkit/ocr2vrf/types/hash" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/ocr2/plugins/dkg/persistence/db_test.go b/core/services/ocr2/plugins/dkg/persistence/db_test.go index b830a8db3bc..4e029c1cb2a 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db_test.go +++ b/core/services/ocr2/plugins/dkg/persistence/db_test.go @@ -7,9 +7,9 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" + "github.com/jmoiron/sqlx" ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" "github.com/smartcontractkit/ocr2vrf/types/hash" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/core/services/ocr2/plugins/dkg/plugin.go b/core/services/ocr2/plugins/dkg/plugin.go index 540518b553c..92910ff7bbe 100644 --- a/core/services/ocr2/plugins/dkg/plugin.go +++ b/core/services/ocr2/plugins/dkg/plugin.go @@ -6,12 +6,12 @@ import ( "fmt" "math/big" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" "github.com/smartcontractkit/ocr2vrf/altbn_128" "github.com/smartcontractkit/ocr2vrf/dkg" - "github.com/smartcontractkit/sqlx" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 10f780371b2..26cffac5abf 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -7,8 +7,8 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index 63ed4114b8e..9a1e99610c1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -17,7 +17,7 @@ import ( "go.uber.org/zap/zapcore" "golang.org/x/time/rate" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go b/core/services/ocr2/plugins/ocr2keeper/evm21/services.go index f9d3dd92591..d178a9af574 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/services.go @@ -4,11 +4,11 @@ import ( "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm.go index 5db2f8bd0f3..c918ad595fa 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm.go @@ -4,8 +4,8 @@ import ( "math/big" "time" + "github.com/jmoiron/sqlx" "github.com/lib/pq" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 132afd0d29d..fca98d87005 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -3,12 +3,12 @@ package ocr2keeper import ( "fmt" + "github.com/jmoiron/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocr2keepers20 "github.com/smartcontractkit/ocr2keepers/pkg/v2" ocr2keepers20coordinator "github.com/smartcontractkit/ocr2keepers/pkg/v2/coordinator" ocr2keepers20polling "github.com/smartcontractkit/ocr2keepers/pkg/v2/observer/polling" ocr2keepers20runner "github.com/smartcontractkit/ocr2keepers/pkg/v2/runner" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/types" diff --git a/core/services/ocrbootstrap/database_test.go b/core/services/ocrbootstrap/database_test.go index 2f160eff582..e00e318c69c 100644 --- a/core/services/ocrbootstrap/database_test.go +++ b/core/services/ocrbootstrap/database_test.go @@ -3,7 +3,7 @@ package ocrbootstrap_test import ( "testing" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/stretchr/testify/require" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" diff --git a/core/services/ocrbootstrap/delegate.go b/core/services/ocrbootstrap/delegate.go index 3910ca05d38..34e3ee0a710 100644 --- a/core/services/ocrbootstrap/delegate.go +++ b/core/services/ocrbootstrap/delegate.go @@ -7,8 +7,8 @@ import ( "github.com/pkg/errors" + "github.com/jmoiron/sqlx" ocr "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/sqlx" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/loop" diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index 0781303275d..1daa84b7212 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -11,11 +11,11 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/jmoiron/sqlx" ocrnetworking "github.com/smartcontractkit/libocr/networking" ocrnetworkingtypes "github.com/smartcontractkit/libocr/networking/types" ocr1types "github.com/smartcontractkit/libocr/offchainreporting/types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/services/ocrcommon/peerstore.go b/core/services/ocrcommon/peerstore.go index f1c318a3bff..02a4d90f578 100644 --- a/core/services/ocrcommon/peerstore.go +++ b/core/services/ocrcommon/peerstore.go @@ -12,9 +12,10 @@ import ( ma "github.com/multiformats/go-multiaddr" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey" diff --git a/core/services/pg/connection.go b/core/services/pg/connection.go index 19c48a118b9..0bafd5dcd0f 100644 --- a/core/services/pg/connection.go +++ b/core/services/pg/connection.go @@ -6,8 +6,8 @@ import ( "github.com/google/uuid" _ "github.com/jackc/pgx/v4/stdlib" // need to make sure pgx driver is registered before opening connection + "github.com/jmoiron/sqlx" "github.com/scylladb/go-reflectx" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/store/dialects" ) diff --git a/core/services/pg/connection_test.go b/core/services/pg/connection_test.go index 92781343c61..651bf9d2d9b 100644 --- a/core/services/pg/connection_test.go +++ b/core/services/pg/connection_test.go @@ -5,7 +5,7 @@ import ( "github.com/google/uuid" _ "github.com/jackc/pgx/v4/stdlib" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/pg/helpers_test.go b/core/services/pg/helpers_test.go index c5ccda6bd9a..52158535a2e 100644 --- a/core/services/pg/helpers_test.go +++ b/core/services/pg/helpers_test.go @@ -1,6 +1,6 @@ package pg -import "github.com/smartcontractkit/sqlx" +import "github.com/jmoiron/sqlx" func SetConn(lock interface{}, conn *sqlx.Conn) { switch v := lock.(type) { diff --git a/core/services/pg/lease_lock.go b/core/services/pg/lease_lock.go index 656005016ef..e21cec44bda 100644 --- a/core/services/pg/lease_lock.go +++ b/core/services/pg/lease_lock.go @@ -8,8 +8,8 @@ import ( "time" "github.com/google/uuid" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "go.uber.org/multierr" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/pg/lease_lock_test.go b/core/services/pg/lease_lock_test.go index 483e03e0039..1b4116b5bf9 100644 --- a/core/services/pg/lease_lock_test.go +++ b/core/services/pg/lease_lock_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" diff --git a/core/services/pg/locked_db.go b/core/services/pg/locked_db.go index af4481285ce..a9157fe1ae1 100644 --- a/core/services/pg/locked_db.go +++ b/core/services/pg/locked_db.go @@ -8,7 +8,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/pg/q.go b/core/services/pg/q.go index 9c70d813ab6..470d39c825c 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/logger" ) diff --git a/core/services/pg/sqlx.go b/core/services/pg/sqlx.go index cd5427463dd..820cd51712e 100644 --- a/core/services/pg/sqlx.go +++ b/core/services/pg/sqlx.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" mapper "github.com/scylladb/go-reflectx" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/logger" ) diff --git a/core/services/pg/transaction.go b/core/services/pg/transaction.go index 932b1120859..d237c20d4c6 100644 --- a/core/services/pg/transaction.go +++ b/core/services/pg/transaction.go @@ -10,7 +10,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/logger" diff --git a/core/services/pipeline/orm.go b/core/services/pipeline/orm.go index 148901bb36c..d60050700f7 100644 --- a/core/services/pipeline/orm.go +++ b/core/services/pipeline/orm.go @@ -11,7 +11,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/services/pipeline/orm_test.go b/core/services/pipeline/orm_test.go index 295ad20a007..dcbbfd9c97e 100644 --- a/core/services/pipeline/orm_test.go +++ b/core/services/pipeline/orm_test.go @@ -10,7 +10,7 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/services/pipeline/runner_test.go b/core/services/pipeline/runner_test.go index 3abcdbe0abe..695590e7bd0 100644 --- a/core/services/pipeline/runner_test.go +++ b/core/services/pipeline/runner_test.go @@ -37,7 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" ) func newRunner(t testing.TB, db *sqlx.DB, bridgeORM bridges.ORM, cfg chainlink.GeneralConfig) (pipeline.Runner, *mocks.ORM) { diff --git a/core/services/pipeline/test_helpers_test.go b/core/services/pipeline/test_helpers_test.go index 8353f5fb5b4..3b72a1625be 100644 --- a/core/services/pipeline/test_helpers_test.go +++ b/core/services/pipeline/test_helpers_test.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" ) func fakeExternalAdapter(t *testing.T, expectedRequest, response interface{}) http.Handler { diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 3f45b41f46e..111e3622b19 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -13,12 +13,12 @@ import ( pkgerrors "github.com/pkg/errors" "go.uber.org/multierr" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median/evmreportcodec" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" diff --git a/core/services/relay/evm/evm_test.go b/core/services/relay/evm/evm_test.go index 4e9c44a7b93..8f49128ff2d 100644 --- a/core/services/relay/evm/evm_test.go +++ b/core/services/relay/evm/evm_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/relay/evm/median.go b/core/services/relay/evm/median.go index b7d751e01e1..5184326cf25 100644 --- a/core/services/relay/evm/median.go +++ b/core/services/relay/evm/median.go @@ -7,12 +7,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" diff --git a/core/services/relay/evm/mercury/orm.go b/core/services/relay/evm/mercury/orm.go index 7273519f6b6..f8d4c8cb1ee 100644 --- a/core/services/relay/evm/mercury/orm.go +++ b/core/services/relay/evm/mercury/orm.go @@ -8,9 +8,9 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/lib/pq" pkgerrors "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" diff --git a/core/services/relay/evm/mercury/persistence_manager_test.go b/core/services/relay/evm/mercury/persistence_manager_test.go index d185a64a8f1..dbdb9777252 100644 --- a/core/services/relay/evm/mercury/persistence_manager_test.go +++ b/core/services/relay/evm/mercury/persistence_manager_test.go @@ -6,8 +6,8 @@ import ( "time" "github.com/cometbft/cometbft/libs/rand" + "github.com/jmoiron/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index 557210e58a5..269f28b122d 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -16,9 +16,9 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" "github.com/smartcontractkit/chainlink-relay/pkg/services" diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index baf98b9b006..a284d677ebf 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -8,12 +8,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" - "github.com/smartcontractkit/sqlx" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index 14004d0b1aa..0c9414068e3 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -7,11 +7,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" diff --git a/core/services/relay/evm/request_round_tracker.go b/core/services/relay/evm/request_round_tracker.go index 4e065f2dfdf..c1c3a49e0e4 100644 --- a/core/services/relay/evm/request_round_tracker.go +++ b/core/services/relay/evm/request_round_tracker.go @@ -9,11 +9,12 @@ import ( gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink-relay/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" diff --git a/core/services/s4/postgres_orm.go b/core/services/s4/postgres_orm.go index d0a79dba959..1f91270fd08 100644 --- a/core/services/s4/postgres_orm.go +++ b/core/services/s4/postgres_orm.go @@ -9,8 +9,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" ) const ( diff --git a/core/services/versioning/orm.go b/core/services/versioning/orm.go index 03bd64fdd2b..8ed745955dc 100644 --- a/core/services/versioning/orm.go +++ b/core/services/versioning/orm.go @@ -7,8 +7,8 @@ import ( "github.com/Masterminds/semver/v3" "github.com/jackc/pgconn" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index 558d48752d2..e976d01b995 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -10,7 +10,7 @@ import ( "github.com/theodesp/go-heaps/pairing" "go.uber.org/multierr" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 927e2ae6829..389e1159be1 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index e50ed91491b..1f607da2f26 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -28,7 +28,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/core/services/webhook/authorizer_test.go b/core/services/webhook/authorizer_test.go index 19dbf381408..b6eb2feaccb 100644 --- a/core/services/webhook/authorizer_test.go +++ b/core/services/webhook/authorizer_test.go @@ -3,7 +3,7 @@ package webhook_test import ( "testing" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/webhook/external_initiator_manager.go b/core/services/webhook/external_initiator_manager.go index 2e881ec42d6..01edf82b114 100644 --- a/core/services/webhook/external_initiator_manager.go +++ b/core/services/webhook/external_initiator_manager.go @@ -10,7 +10,7 @@ import ( "github.com/lib/pq" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/sessions/ldapauth/helpers_test.go b/core/sessions/ldapauth/helpers_test.go index c554d5436ed..3566ea84380 100644 --- a/core/sessions/ldapauth/helpers_test.go +++ b/core/sessions/ldapauth/helpers_test.go @@ -3,7 +3,7 @@ package ldapauth import ( "time" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/sessions/ldapauth/ldap.go b/core/sessions/ldapauth/ldap.go index 188f2684e7e..04f6fbfbbb6 100644 --- a/core/sessions/ldapauth/ldap.go +++ b/core/sessions/ldapauth/ldap.go @@ -32,7 +32,7 @@ import ( "time" "github.com/go-ldap/ldap/v3" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" diff --git a/core/sessions/ldapauth/ldap_test.go b/core/sessions/ldapauth/ldap_test.go index 261141d66e9..c85e0db831e 100644 --- a/core/sessions/ldapauth/ldap_test.go +++ b/core/sessions/ldapauth/ldap_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" diff --git a/core/sessions/ldapauth/sync.go b/core/sessions/ldapauth/sync.go index ce7a338f40e..67f101b62a4 100644 --- a/core/sessions/ldapauth/sync.go +++ b/core/sessions/ldapauth/sync.go @@ -6,8 +6,8 @@ import ( "time" "github.com/go-ldap/ldap/v3" + "github.com/jmoiron/sqlx" "github.com/lib/pq" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/sessions/localauth/orm.go b/core/sessions/localauth/orm.go index d6fb8cd5788..090dc468a62 100644 --- a/core/sessions/localauth/orm.go +++ b/core/sessions/localauth/orm.go @@ -6,8 +6,8 @@ import ( "strings" "time" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/sqlx" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" diff --git a/core/sessions/localauth/orm_test.go b/core/sessions/localauth/orm_test.go index 7868937ad08..c2e155de282 100644 --- a/core/sessions/localauth/orm_test.go +++ b/core/sessions/localauth/orm_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/sessions/webauthn.go b/core/sessions/webauthn.go index 41e31d7aaa8..89e7758bc5b 100644 --- a/core/sessions/webauthn.go +++ b/core/sessions/webauthn.go @@ -9,8 +9,8 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/webauthn" + sqlxTypes "github.com/jmoiron/sqlx/types" "github.com/pkg/errors" - sqlxTypes "github.com/smartcontractkit/sqlx/types" ) // WebAuthn holds the credentials for API user. diff --git a/core/sessions/webauthn_test.go b/core/sessions/webauthn_test.go index b3c1ecf7897..9c055d9c794 100644 --- a/core/sessions/webauthn_test.go +++ b/core/sessions/webauthn_test.go @@ -7,7 +7,7 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/webauthn" - sqlxTypes "github.com/smartcontractkit/sqlx/types" + sqlxTypes "github.com/jmoiron/sqlx/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/core/store/migrate/migrate.go b/core/store/migrate/migrate.go index 69cf9a78247..1e58d7a0b05 100644 --- a/core/store/migrate/migrate.go +++ b/core/store/migrate/migrate.go @@ -9,9 +9,9 @@ import ( "strconv" "strings" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "github.com/pressly/goose/v3" - "github.com/smartcontractkit/sqlx" "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink/v2/core/config/env" diff --git a/core/store/migrate/migrations/0036_external_job_id.go b/core/store/migrate/migrations/0036_external_job_id.go index 8637bd38f56..fc9ec08ec60 100644 --- a/core/store/migrate/migrations/0036_external_job_id.go +++ b/core/store/migrate/migrations/0036_external_job_id.go @@ -6,8 +6,8 @@ import ( "fmt" "github.com/google/uuid" + "github.com/jmoiron/sqlx" "github.com/pressly/goose/v3" - "github.com/smartcontractkit/sqlx" ) func init() { diff --git a/core/web/jobs_controller_test.go b/core/web/jobs_controller_test.go index 0bd947bbce3..2e3f3a83693 100644 --- a/core/web/jobs_controller_test.go +++ b/core/web/jobs_controller_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/sqlx" + "github.com/jmoiron/sqlx" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/go.mod b/go.mod index e6af9533dc7..67896860e09 100644 --- a/go.mod +++ b/go.mod @@ -65,14 +65,13 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 - github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wsrpc v0.7.2 @@ -238,7 +237,7 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect + github.com/jmoiron/sqlx v1.3.5 github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect diff --git a/go.sum b/go.sum index 5c70c22a75e..89e9aee85fa 100644 --- a/go.sum +++ b/go.sum @@ -1463,8 +1463,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= @@ -1481,8 +1481,6 @@ github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGuj github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= -github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb h1:OMaBUb4X9IFPLbGbCHsMU+kw/BPCrewaVwWGIBc0I4A= -github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb/go.mod h1:HNUu4cJekUdsJbwRBCiOybtkPJEfGRELQPe2tkoDEyk= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 8fe63a46d26..c51bd3c4f6e 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -12,6 +12,7 @@ require ( github.com/ethereum/go-ethereum v1.12.0 github.com/go-resty/resty/v2 v2.7.0 github.com/google/uuid v1.3.1 + github.com/jmoiron/sqlx v1.3.5 github.com/kelseyhightower/envconfig v1.4.0 github.com/lib/pq v1.10.9 github.com/manifoldco/promptui v0.9.0 @@ -26,7 +27,6 @@ require ( github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 - github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.3.0 github.com/spf13/cobra v1.6.1 @@ -259,7 +259,6 @@ require ( github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -387,7 +386,7 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index b8e8b205c87..4afa0de1d38 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2367,8 +2367,8 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= @@ -2387,8 +2387,6 @@ github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGuj github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= -github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb h1:OMaBUb4X9IFPLbGbCHsMU+kw/BPCrewaVwWGIBc0I4A= -github.com/smartcontractkit/sqlx v1.3.5-0.20210805004948-4be295aacbeb/go.mod h1:HNUu4cJekUdsJbwRBCiOybtkPJEfGRELQPe2tkoDEyk= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index aa488eb1be5..ab7a221955b 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -17,36 +17,36 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" geth_types "github.com/ethereum/go-ethereum/core/types" + "github.com/jmoiron/sqlx" "github.com/rs/zerolog" + "github.com/scylladb/go-reflectx" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctf_blockchain "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" "github.com/smartcontractkit/wasp" + evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + lpEvm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/store/models" - ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/stretchr/testify/require" - - "github.com/scylladb/go-reflectx" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" utils2 "github.com/smartcontractkit/chainlink/integration-tests/utils" - evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - lpEvm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" - core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/sqlx" ) var ( From c2a1b26ed24f50a30cac95bd25a8f517f3367c8c Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 9 Nov 2023 11:42:44 -0500 Subject: [PATCH 118/271] Actually serialize the LatestBlocks part of the observation (#11237) * Actually serialize the LatestBlocks part of the observation * Fix tests * Regenerate mocks --- .gitignore | 6 +- common/headtracker/head_tracker.go | 3 + common/headtracker/types/mocks/head.go | 20 +++- common/types/head.go | 9 +- common/types/mocks/head.go | 20 +++- .../evm/client/simulated_backend_client.go | 2 +- core/chains/evm/types/models.go | 4 + core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../relay/evm/mercury/v1/data_source.go | 3 +- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- tools/flakeytests/coverage.txt | 93 ------------------- 15 files changed, 68 insertions(+), 110 deletions(-) delete mode 100644 tools/flakeytests/coverage.txt diff --git a/.gitignore b/.gitignore index 61ebfab0e9b..48e228eb836 100644 --- a/.gitignore +++ b/.gitignore @@ -79,7 +79,9 @@ MacOSX* contracts/yarn.lock - # Ignore DevSpace cache and log folder .devspace/ -go.work* \ No newline at end of file +go.work* + +# This sometimes shows up for some reason +tools/flakeytests/coverage.txt diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index c24dde595cf..54262dd93f7 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -197,6 +197,9 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context "blockHeight", head.BlockNumber(), "blockHash", head.BlockHash(), "parentHeadHash", head.GetParentHash(), + "blockTs", head.GetTimestamp(), + "blockTsUnix", head.GetTimestamp().Unix(), + "blockDifficulty", head.BlockDifficulty(), ) err := ht.headSaver.Save(ctx, head) diff --git a/common/headtracker/types/mocks/head.go b/common/headtracker/types/mocks/head.go index a56590b6ef3..1de1f78de8c 100644 --- a/common/headtracker/types/mocks/head.go +++ b/common/headtracker/types/mocks/head.go @@ -3,9 +3,13 @@ package mocks import ( + time "time" + + mock "github.com/stretchr/testify/mock" + types "github.com/smartcontractkit/chainlink/v2/common/types" + utils "github.com/smartcontractkit/chainlink/v2/core/utils" - mock "github.com/stretchr/testify/mock" ) // Head is an autogenerated mock type for the Head type @@ -131,6 +135,20 @@ func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetParentHash() BLOCK_HASH { return r0 } +// GetTimestamp provides a mock function with given fields: +func (_m *Head[BLOCK_HASH, CHAIN_ID]) GetTimestamp() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + // HasChainID provides a mock function with given fields: func (_m *Head[BLOCK_HASH, CHAIN_ID]) HasChainID() bool { ret := _m.Called() diff --git a/common/types/head.go b/common/types/head.go index bef9c30d9e9..000bad2390e 100644 --- a/common/types/head.go +++ b/common/types/head.go @@ -1,6 +1,10 @@ package types -import "github.com/smartcontractkit/chainlink/v2/core/utils" +import ( + "time" + + "github.com/smartcontractkit/chainlink/v2/core/utils" +) // Head provides access to a chain's head, as needed by the TxManager. // This is a generic interface which ALL chains will implement. @@ -10,6 +14,9 @@ type Head[BLOCK_HASH Hashable] interface { // BlockNumber is the head's block number BlockNumber() int64 + // Timestamp the time of mining of the block + GetTimestamp() time.Time + // ChainLength returns the length of the chain followed by recursively looking up parents ChainLength() uint32 diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go index 816a9234a3c..82fd910a08b 100644 --- a/common/types/mocks/head.go +++ b/common/types/mocks/head.go @@ -3,9 +3,13 @@ package mocks import ( + time "time" + + mock "github.com/stretchr/testify/mock" + types "github.com/smartcontractkit/chainlink/v2/common/types" + utils "github.com/smartcontractkit/chainlink/v2/core/utils" - mock "github.com/stretchr/testify/mock" ) // Head is an autogenerated mock type for the Head type @@ -117,6 +121,20 @@ func (_m *Head[BLOCK_HASH]) GetParentHash() BLOCK_HASH { return r0 } +// GetTimestamp provides a mock function with given fields: +func (_m *Head[BLOCK_HASH]) GetTimestamp() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + // HashAtHeight provides a mock function with given fields: blockNum func (_m *Head[BLOCK_HASH]) HashAtHeight(blockNum int64) BLOCK_HASH { ret := _m.Called(blockNum) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index d542e98e6eb..f4ad6a65a1a 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -301,7 +301,7 @@ func (c *SimulatedBackendClient) SubscribeNewHead( case h := <-ch: var head *evmtypes.Head if h != nil { - head = &evmtypes.Head{Number: h.Number.Int64(), Hash: h.Hash(), ParentHash: h.ParentHash, Parent: lastHead, EVMChainID: utils.NewBig(c.chainId)} + head = &evmtypes.Head{Difficulty: (*utils.Big)(h.Difficulty), Timestamp: time.Unix(int64(h.Time), 0), Number: h.Number.Int64(), Hash: h.Hash(), ParentHash: h.ParentHash, Parent: lastHead, EVMChainID: utils.NewBig(c.chainId)} lastHead = head } select { diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 6db5d49575c..c2d61e00703 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -76,6 +76,10 @@ func (h *Head) GetParent() commontypes.Head[common.Hash] { return h.Parent } +func (h *Head) GetTimestamp() time.Time { + return h.Timestamp +} + func (h *Head) BlockDifficulty() *utils.Big { return h.Difficulty } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 3adcc130bc0..5f881f354e6 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7f5d0e227d6..1d455305a94 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1464,8 +1464,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 h1:ZBsxdB/5iIpl/tWhXe/RHrOwBG7pbKOMeppy5Zt2BVc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 1b16dc76f98..0f8f56f46e4 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -42,8 +42,7 @@ var ( ) ) -// nBlocksObservation controls how many blocks are included in the LatestBlocks observation -const nBlocksObservation int = 5 +const nBlocksObservation int = relaymercuryv1.MaxAllowedBlocks type Runner interface { ExecuteRun(ctx context.Context, spec pipeline.Spec, vars pipeline.Vars, l logger.Logger) (run *pipeline.Run, trrs pipeline.TaskRunResults, err error) diff --git a/go.mod b/go.mod index 67896860e09..f35077e9234 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/go.sum b/go.sum index 89e9aee85fa..2c9d9e53715 100644 --- a/go.sum +++ b/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 h1:ZBsxdB/5iIpl/tWhXe/RHrOwBG7pbKOMeppy5Zt2BVc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index c51bd3c4f6e..83657baa011 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -387,7 +387,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 4afa0de1d38..a873f9b7c16 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2369,8 +2369,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 h1:ZBsxdB/5iIpl/tWhXe/RHrOwBG7pbKOMeppy5Zt2BVc= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/tools/flakeytests/coverage.txt b/tools/flakeytests/coverage.txt deleted file mode 100644 index 91640016fe2..00000000000 --- a/tools/flakeytests/coverage.txt +++ /dev/null @@ -1,93 +0,0 @@ -mode: atomic -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:50.103,54.38 4 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:54.38,55.24 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:55.24,62.18 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:62.18,64.5 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:65.4,65.46 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:72.2,73.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:73.16,75.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:77.2,90.16 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:93.63,95.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:95.16,97.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:99.2,101.16 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:101.16,103.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:104.2,110.16 4 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:110.16,112.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:112.8,112.52 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:112.52,114.18 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:114.18,116.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:117.3,117.83 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:119.2,119.12 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:122.81,124.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:124.16,126.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:128.2,128.31 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/reporter.go:131.77,133.2 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:40.79,44.30 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:44.31,44.32 0 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:46.2,52.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:61.75,70.2 8 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:81.45,85.2 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:87.75,89.28 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:89.28,91.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:91.16,93.19 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:93.19,94.13 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:99.4,99.42 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:99.42,100.13 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:103.4,104.18 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:104.18,106.5 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:110.4,110.39 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:110.39,111.13 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:114.4,114.20 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:115.16,116.32 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:116.32,118.6 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:119.5,119.31 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:120.18,121.38 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:121.38,122.33 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:122.33,124.7 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:125.6,125.32 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:130.3,130.33 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:130.33,132.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:134.2,134.19 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:141.106,144.38 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:144.38,146.27 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:146.27,148.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:150.3,151.36 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:151.36,155.18 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:155.18,161.55 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:161.55,162.14 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:164.5,164.32 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:167.4,168.18 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:168.18,170.5 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:172.4,172.25 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:172.25,174.22 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:174.22,175.37 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:175.37,177.7 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:178.6,178.42 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:184.2,184.29 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:187.30,189.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:189.16,191.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:193.2,194.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:194.16,196.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:198.2,198.30 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:198.30,200.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:200.8,202.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/runner.go:204.2,204.43 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:12.74,15.25 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:15.25,17.10 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:17.10,19.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:21.3,21.10 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:24.2,25.9 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:25.9,27.3 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:29.2,29.16 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:32.88,34.16 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:34.16,36.17 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:36.17,38.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:40.3,41.17 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:41.17,43.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:45.3,46.17 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:46.17,48.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:51.2,52.19 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:53.22,56.17 3 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:56.17,58.4 1 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:60.3,61.19 2 0 -github.com/smartcontractkit/chainlink/v2/tools/flakeytests/utils.go:62.10,63.19 1 0 From 012865049d9a88057e25f2b928315ed12897a8b2 Mon Sep 17 00:00:00 2001 From: Andrei Smirnov Date: Thu, 9 Nov 2023 20:23:30 +0300 Subject: [PATCH 119/271] Functions: ARB+OP cost estimation tweaks (#11102) * Functions: ARB+OP gas tweaks * make wrappers-all * Updated gas snapshot * Single line comments * (refactor): rework ChainSpecificUtil usage to be an additional flat fee, rather than gas price * (test): Add ChainSpecificUtil foundry tests * Regenerate geth wrappers * Regenerate gas snapshot * Amend L1Fee as gas units, not in wei * Prettier * Revert "Amend L1Fee as gas units, not in wei" This reverts commit 75e47cadbe4a7a58fc731bc57b9dfaa7eb8a7898. * Denote that _getCurrentTxL1GasFees's return is in Wei * (refactor) rework FunctionsBilling unit conversion helper to be juels from wei * Changes from review * Regenerate gas snapshot --------- Co-authored-by: Justin Kaseman --- .../gas-snapshots/functions.gas-snapshot | 73 +++--- .../functions/dev/v1_X/ChainSpecificUtil.sol | 78 +++++++ .../functions/dev/v1_X/FunctionsBilling.sol | 34 +-- .../dev/v1_X/FunctionsCoordinator.sol | 9 +- .../tests/v1_X/ChainSpecificUtil.t.sol | 217 ++++++++++++++++++ .../tests/v1_X/FunctionsCoordinator.t.sol | 1 - .../src/v0.8/functions/tests/v1_X/Gas.t.sol | 8 - .../src/v0.8/functions/tests/v1_X/Setup.t.sol | 87 +++++-- .../FunctionsCoordinatorHarness.sol | 5 +- .../functions_coordinator.go | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- 11 files changed, 434 insertions(+), 82 deletions(-) create mode 100644 contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol create mode 100644 contracts/src/v0.8/functions/tests/v1_X/ChainSpecificUtil.t.sol diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index e742be27549..4e891535b77 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,9 +1,18 @@ +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14497117) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14497095) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14497111) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14508531) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14508508) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14508480) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14508431) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14508420) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14508464) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) FunctionsBilling_EstimateCost:test_EstimateCost_RevertsIfGasPriceAboveCeiling() (gas: 32458) -FunctionsBilling_EstimateCost:test_EstimateCost_Success() (gas: 53227) -FunctionsBilling_EstimateCost:test_EstimateCost_SuccessLowGasPrice() (gas: 53330) +FunctionsBilling_EstimateCost:test_EstimateCost_Success() (gas: 53807) +FunctionsBilling_EstimateCost:test_EstimateCost_SuccessLowGasPrice() (gas: 53910) FunctionsBilling_GetAdminFee:test_GetAdminFee_Success() (gas: 18226) FunctionsBilling_GetConfig:test_GetConfig_Success() (gas: 23671) FunctionsBilling_GetDONFee:test_GetDONFee_Success() (gas: 15792) @@ -18,39 +27,39 @@ FunctionsBilling_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 18974) FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 38251) FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8801) FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) -FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 498114) -FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 199285) +FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 501740) +FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 202944) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_RevertIfNotRouter() (gas: 14623) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_Success() (gas: 22923) FunctionsClient__SendRequest:test__SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 55059) -FunctionsCoordinator_Constructor:test_Constructor_Success() (gas: 11984) +FunctionsCoordinator_Constructor:test_Constructor_Success() (gas: 12006) FunctionsCoordinator_GetDONPublicKey:test_GetDONPublicKey_RevertIfEmpty() (gas: 15334) -FunctionsCoordinator_GetDONPublicKey:test_GetDONPublicKey_Success() (gas: 106496) +FunctionsCoordinator_GetDONPublicKey:test_GetDONPublicKey_Success() (gas: 106506) FunctionsCoordinator_GetThresholdPublicKey:test_GetThresholdPublicKey_RevertIfEmpty() (gas: 15313) -FunctionsCoordinator_GetThresholdPublicKey:test_GetThresholdPublicKey_Success() (gas: 656556) +FunctionsCoordinator_GetThresholdPublicKey:test_GetThresholdPublicKey_Success() (gas: 656362) FunctionsCoordinator_SetDONPublicKey:test_SetDONPublicKey_RevertNotOwner() (gas: 20364) -FunctionsCoordinator_SetDONPublicKey:test_SetDONPublicKey_Success() (gas: 101275) +FunctionsCoordinator_SetDONPublicKey:test_SetDONPublicKey_Success() (gas: 101285) FunctionsCoordinator_SetThresholdPublicKey:test_SetThresholdPublicKey_RevertNotOwner() (gas: 13892) -FunctionsCoordinator_SetThresholdPublicKey:test_SetThresholdPublicKey_Success() (gas: 651248) +FunctionsCoordinator_SetThresholdPublicKey:test_SetThresholdPublicKey_Success() (gas: 651054) FunctionsCoordinator_StartRequest:test_StartRequest_RevertIfNotRouter() (gas: 22703) -FunctionsCoordinator_StartRequest:test_StartRequest_Success() (gas: 107681) +FunctionsCoordinator_StartRequest:test_StartRequest_Success() (gas: 108848) FunctionsCoordinator__IsTransmitter:test__IsTransmitter_SuccessFound() (gas: 18957) FunctionsCoordinator__IsTransmitter:test__IsTransmitter_SuccessNotFound() (gas: 19690) FunctionsRequest_DEFAULT_BUFFER_SIZE:test_DEFAULT_BUFFER_SIZE() (gas: 246) FunctionsRequest_EncodeCBOR:test_EncodeCBOR_Success() (gas: 223) FunctionsRequest_REQUEST_DATA_VERSION:test_REQUEST_DATA_VERSION() (gas: 225) FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12007) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 169900) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 160227) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 174021) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 164352) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38115) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35238) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 178373) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 182497) FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28086) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 153924) -FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 296712) -FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 310327) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2484946) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 515433) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 158041) +FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 323262) +FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 336879) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2512144) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 542628) FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37159) @@ -71,15 +80,15 @@ FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotNe FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_RevertIfNotOwner() (gas: 23392) FunctionsRouter_ProposeContractsUpdate:test_ProposeContractsUpdate_Success() (gas: 118479) FunctionsRouter_SendRequest:test_SendRequest_RevertIfConsumerNotAllowed() (gas: 59347) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfDuplicateRequestId() (gas: 192799) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfDuplicateRequestId() (gas: 193436) FunctionsRouter_SendRequest:test_SendRequest_RevertIfEmptyData() (gas: 29426) FunctionsRouter_SendRequest:test_SendRequest_RevertIfIncorrectDonId() (gas: 57925) -FunctionsRouter_SendRequest:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 186299) +FunctionsRouter_SendRequest:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 186932) FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 50947) FunctionsRouter_SendRequest:test_SendRequest_RevertIfInvalidDonId() (gas: 25082) FunctionsRouter_SendRequest:test_SendRequest_RevertIfNoSubscription() (gas: 29132) FunctionsRouter_SendRequest:test_SendRequest_RevertIfPaused() (gas: 34291) -FunctionsRouter_SendRequest:test_SendRequest_Success() (gas: 285026) +FunctionsRouter_SendRequest:test_SendRequest_Success() (gas: 286243) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfConsumerNotAllowed() (gas: 65843) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfEmptyData() (gas: 36012) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfIncorrectDonId() (gas: 29896) @@ -87,8 +96,8 @@ FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalid FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfInvalidDonId() (gas: 27503) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfNoSubscription() (gas: 35717) FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_RevertIfPaused() (gas: 40810) -FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_Success() (gas: 291595) -FunctionsRouter_SendRequestToProposed:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 192791) +FunctionsRouter_SendRequestToProposed:test_SendRequestToProposed_Success() (gas: 292812) +FunctionsRouter_SendRequestToProposed:test_SendRequest_RevertIfInsufficientSubscriptionBalance() (gas: 193424) FunctionsRouter_SetAllowListId:test_SetAllowListId_Success() (gas: 30688) FunctionsRouter_SetAllowListId:test_UpdateConfig_RevertIfNotOwner() (gas: 13403) FunctionsRouter_Unpause:test_Unpause_RevertIfNotOwner() (gas: 13293) @@ -101,7 +110,7 @@ FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOw FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfPaused() (gas: 60987) FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderBecomesBlocked() (gas: 94677) FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_RevertIfSenderIsNotNewOwner() (gas: 62693) -FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_Success() (gas: 214560) +FunctionsSubscriptions_AcceptSubscriptionOwnerTransfer:test_AcceptSubscriptionOwnerTransfer_Success() (gas: 215197) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumers() (gas: 137893) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfMaximumConsumersAfterConfigUpdate() (gas: 164837) FunctionsSubscriptions_AddConsumer:test_AddConsumer_RevertIfNoSubscription() (gas: 12946) @@ -113,7 +122,7 @@ FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNoSubs FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotAllowedSender() (gas: 57885) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfNotSubscriptionOwner() (gas: 89272) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPaused() (gas: 20148) -FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 193688) +FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_RevertIfPendingRequests() (gas: 194325) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitAllBalanceAsDeposit() (gas: 114506) FunctionsSubscriptions_CancelSubscription:test_CancelSubscription_SuccessForfeitSomeBalanceAsDeposit() (gas: 125832) FunctionsSubscriptions_CancelSubscription_ReceiveDeposit:test_CancelSubscription_SuccessRecieveDeposit() (gas: 74973) @@ -133,8 +142,8 @@ FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 1501 FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43685, ~: 45548) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46197, ~: 48060) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51089, ~: 53040) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51000, ~: 53040) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 85879, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) @@ -146,7 +155,7 @@ FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_Reve FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_Success() (gas: 54867) FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessDeletesSubscription() (gas: 49607) FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessSubOwnerRefunded() (gas: 50896) -FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessWhenRequestInFlight() (gas: 164303) +FunctionsSubscriptions_OwnerCancelSubscription:test_OwnerCancelSubscription_SuccessWhenRequestInFlight() (gas: 164812) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfAmountMoreThanBalance() (gas: 17924) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfBalanceInvariant() (gas: 210) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_RevertIfNotOwner() (gas: 15555) @@ -155,7 +164,7 @@ FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessIfRecipientAddres FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessPaysRecipient() (gas: 54413) FunctionsSubscriptions_OwnerWithdraw:test_OwnerWithdraw_SuccessSetsBalanceToZero() (gas: 37790) FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessFalseIfNoPendingRequests() (gas: 14981) -FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 175857) +FunctionsSubscriptions_PendingRequestExists:test_PendingRequestExists_SuccessTrueIfPendingRequests() (gas: 176494) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfEmptyNewOwner() (gas: 27611) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfInvalidNewOwner() (gas: 57709) FunctionsSubscriptions_ProposeSubscriptionOwnerTransfer:test_ProposeSubscriptionOwnerTransfer_RevertIfNoSubscription() (gas: 15001) @@ -171,7 +180,7 @@ FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNoSubscription FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotAllowedSender() (gas: 57800) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfNotSubscriptionOwner() (gas: 87208) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPaused() (gas: 18049) -FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191221) +FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_RevertIfPendingRequests() (gas: 191858) FunctionsSubscriptions_RemoveConsumer:test_RemoveConsumer_Success() (gas: 41979) FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNoSubscription() (gas: 12891) FunctionsSubscriptions_SetFlags:test_SetFlags_RevertIfNotOwner() (gas: 15684) @@ -209,5 +218,5 @@ Gas_AcceptTermsOfService:test_AcceptTermsOfService_Gas() (gas: 84675) Gas_AddConsumer:test_AddConsumer_Gas() (gas: 79087) Gas_CreateSubscription:test_CreateSubscription_Gas() (gas: 73375) Gas_FundSubscription:test_FundSubscription_Gas() (gas: 38546) -Gas_SendRequest:test_SendRequest_MaximumGas() (gas: 964214) -Gas_SendRequest:test_SendRequest_MinimumGas() (gas: 156934) \ No newline at end of file +Gas_SendRequest:test_SendRequest_MaximumGas() (gas: 979631) +Gas_SendRequest:test_SendRequest_MinimumGas() (gas: 157578) \ No newline at end of file diff --git a/contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol b/contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol new file mode 100644 index 00000000000..f0eec19db24 --- /dev/null +++ b/contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import {OVM_GasPriceOracle} from "../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; + +/// @dev A library that abstracts out opcodes that behave differently across chains. +/// @dev The methods below return values that are pertinent to the given chain. +library ChainSpecificUtil { + // ------------ Start Arbitrum Constants ------------ + + /// @dev ARBGAS_ADDR is the address of the ArbGasInfo precompile on Arbitrum. + /// @dev reference: https://github.com/OffchainLabs/nitro/blob/v2.0.14/contracts/src/precompiles/ArbGasInfo.sol#L10 + address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C); + ArbGasInfo private constant ARBGAS = ArbGasInfo(ARBGAS_ADDR); + + uint256 private constant ARB_MAINNET_CHAIN_ID = 42161; + uint256 private constant ARB_GOERLI_TESTNET_CHAIN_ID = 421613; + uint256 private constant ARB_SEPOLIA_TESTNET_CHAIN_ID = 421614; + + // ------------ End Arbitrum Constants ------------ + + // ------------ Start Optimism Constants ------------ + /// @dev L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism + bytes internal constant L1_FEE_DATA_PADDING = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + /// @dev OVM_GASPRICEORACLE_ADDR is the address of the OVM_GasPriceOracle precompile on Optimism. + /// @dev reference: https://community.optimism.io/docs/developers/build/transaction-fees/#estimating-the-l1-data-fee + address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); + OVM_GasPriceOracle private constant OVM_GASPRICEORACLE = OVM_GasPriceOracle(OVM_GASPRICEORACLE_ADDR); + + uint256 private constant OP_MAINNET_CHAIN_ID = 10; + uint256 private constant OP_GOERLI_CHAIN_ID = 420; + uint256 private constant OP_SEPOLIA_CHAIN_ID = 11155420; + + /// @dev Base is a OP stack based rollup and follows the same L1 pricing logic as Optimism. + uint256 private constant BASE_MAINNET_CHAIN_ID = 8453; + uint256 private constant BASE_GOERLI_CHAIN_ID = 84531; + uint256 private constant BASE_SEPOLIA_CHAIN_ID = 84532; + + // ------------ End Optimism Constants ------------ + + /// @notice Returns the L1 fees in wei that will be paid for the current transaction, given any calldata + /// @notice for the current transaction. + /// @notice When on a known Arbitrum chain, it uses ArbGas.getCurrentTxL1GasFees to get the fees. + /// @notice On Arbitrum, the provided calldata is not used to calculate the fees. + /// @notice On Optimism, the provided calldata is passed to the OVM_GasPriceOracle predeploy + /// @notice and getL1Fee is called to get the fees. + function _getCurrentTxL1GasFees(bytes memory txCallData) internal view returns (uint256 l1FeeWei) { + uint256 chainid = block.chainid; + if (_isArbitrumChainId(chainid)) { + return ARBGAS.getCurrentTxL1GasFees(); + } else if (_isOptimismChainId(chainid)) { + return OVM_GASPRICEORACLE.getL1Fee(bytes.concat(txCallData, L1_FEE_DATA_PADDING)); + } + return 0; + } + + /// @notice Return true if and only if the provided chain ID is an Arbitrum chain ID. + function _isArbitrumChainId(uint256 chainId) internal pure returns (bool) { + return + chainId == ARB_MAINNET_CHAIN_ID || + chainId == ARB_GOERLI_TESTNET_CHAIN_ID || + chainId == ARB_SEPOLIA_TESTNET_CHAIN_ID; + } + + /// @notice Return true if and only if the provided chain ID is an Optimism (or Base) chain ID. + /// @notice Note that optimism chain id's are also OP stack chain id's. + function _isOptimismChainId(uint256 chainId) internal pure returns (bool) { + return + chainId == OP_MAINNET_CHAIN_ID || + chainId == OP_GOERLI_CHAIN_ID || + chainId == OP_SEPOLIA_CHAIN_ID || + chainId == BASE_MAINNET_CHAIN_ID || + chainId == BASE_GOERLI_CHAIN_ID || + chainId == BASE_SEPOLIA_CHAIN_ID; + } +} diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index adc6218733f..bf43ead8d72 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -10,6 +10,8 @@ import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; +import {ChainSpecificUtil} from "./ChainSpecificUtil.sol"; + /// @title Functions Billing contract /// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON). abstract contract FunctionsBilling is Routable, IFunctionsBilling { @@ -123,10 +125,10 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { return uint256(weiPerUnitLink); } - function _getJuelsPerGas(uint256 gasPriceWei) private view returns (uint96) { - // (1e18 juels/link) * (wei/gas) / (wei/link) = juels per gas + function _getJuelsFromWei(uint256 amountWei) private view returns (uint96) { + // (1e18 juels/link) * wei / (wei/link) = juels // There are only 1e9*1e18 = 1e27 juels in existence, should not exceed uint96 (2^96 ~ 7e28) - return SafeCast.toUint96((1e18 * gasPriceWei) / getWeiPerUnitLink()); + return SafeCast.toUint96((1e18 * amountWei) / getWeiPerUnitLink()); } // ================================================================ @@ -159,8 +161,6 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { uint72 donFee, uint72 adminFee ) internal view returns (uint96) { - uint256 executionGas = s_config.gasOverheadBeforeCallback + s_config.gasOverheadAfterCallback + callbackGasLimit; - // If gas price is less than the minimum fulfillment gas price, override to using the minimum if (gasPriceWei < s_config.minimumEstimateGasPriceWei) { gasPriceWei = s_config.minimumEstimateGasPriceWei; @@ -170,11 +170,13 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { ((gasPriceWei * s_config.fulfillmentGasPriceOverEstimationBP) / 10_000); /// @NOTE: Basis Points are 1/100th of 1%, divide by 10_000 to bring back to original units - uint96 juelsPerGas = _getJuelsPerGas(gasPriceWithOverestimation); - uint256 estimatedGasReimbursement = juelsPerGas * executionGas; - uint96 fees = uint96(donFee) + uint96(adminFee); + uint256 executionGas = s_config.gasOverheadBeforeCallback + s_config.gasOverheadAfterCallback + callbackGasLimit; + uint256 l1FeeWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data); + uint96 estimatedGasReimbursementJuels = _getJuelsFromWei((gasPriceWithOverestimation * executionGas) + l1FeeWei); + + uint96 feesJuels = uint96(donFee) + uint96(adminFee); - return SafeCast.toUint96(estimatedGasReimbursement + fees); + return estimatedGasReimbursementJuels + feesJuels; } // ================================================================ @@ -248,6 +250,7 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { /// @param requestId identifier for the request that was generated by the Registry in the beginBilling commitment /// @param response response data from DON consensus /// @param err error from DON consensus + /// @param reportBatchSize the number of fulfillments in the transmitter's report /// @return result fulfillment result /// @dev Only callable by a node that has been approved on the Coordinator /// @dev simulated offchain to determine if sufficient balance is present to fulfill the request @@ -256,21 +259,22 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { bytes memory response, bytes memory err, bytes memory onchainMetadata, - bytes memory /* offchainMetadata TODO: use in getDonFee() for dynamic billing */ + bytes memory /* offchainMetadata TODO: use in getDonFee() for dynamic billing */, + uint8 reportBatchSize ) internal returns (FunctionsResponse.FulfillResult) { FunctionsResponse.Commitment memory commitment = abi.decode(onchainMetadata, (FunctionsResponse.Commitment)); - uint96 juelsPerGas = _getJuelsPerGas(tx.gasprice); + uint256 gasOverheadWei = (commitment.gasOverheadBeforeCallback + commitment.gasOverheadAfterCallback) * tx.gasprice; + uint256 l1FeeShareWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data) / reportBatchSize; // Gas overhead without callback - uint96 gasOverheadJuels = juelsPerGas * - (commitment.gasOverheadBeforeCallback + commitment.gasOverheadAfterCallback); + uint96 gasOverheadJuels = _getJuelsFromWei(gasOverheadWei + l1FeeShareWei); // The Functions Router will perform the callback to the client contract (FunctionsResponse.FulfillResult resultCode, uint96 callbackCostJuels) = _getRouter().fulfill( response, err, - juelsPerGas, - gasOverheadJuels + commitment.donFee, // costWithoutFulfillment + _getJuelsFromWei(tx.gasprice), // Juels Per Gas conversion rate + gasOverheadJuels + commitment.donFee, // cost without callback or admin fee, those will be added by the Router msg.sender, commitment ); diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol index eb0d954ae02..2caab41c746 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol @@ -156,7 +156,14 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli // Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig for (uint256 i = 0; i < requestIds.length; ++i) { FunctionsResponse.FulfillResult result = FunctionsResponse.FulfillResult( - _fulfillAndBill(requestIds[i], results[i], errors[i], onchainMetadata[i], offchainMetadata[i]) + _fulfillAndBill( + requestIds[i], + results[i], + errors[i], + onchainMetadata[i], + offchainMetadata[i], + uint8(requestIds.length) // will not exceed "MaxRequestBatchSize" on the Job's ReportingPluginConfig + ) ); // Emit on successfully processing the fulfillment diff --git a/contracts/src/v0.8/functions/tests/v1_X/ChainSpecificUtil.t.sol b/contracts/src/v0.8/functions/tests/v1_X/ChainSpecificUtil.t.sol new file mode 100644 index 00000000000..5384a66d912 --- /dev/null +++ b/contracts/src/v0.8/functions/tests/v1_X/ChainSpecificUtil.t.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {BaseTest} from "./BaseTest.t.sol"; +import {FunctionsClient} from "../../dev/v1_X/FunctionsClient.sol"; +import {FunctionsRouter} from "../../dev/v1_X/FunctionsRouter.sol"; +import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; +import {FunctionsRequest} from "../../dev/v1_X/libraries/FunctionsRequest.sol"; +import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; + +import {FunctionsFulfillmentSetup} from "./Setup.t.sol"; + +import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import {OVM_GasPriceOracle} from "../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; + +/// @notice #_getCurrentTxL1GasFees Arbitrum +/// @dev Arbitrum gas formula = L2 Gas Price * (Gas used on L2 + Extra Buffer for L1 cost) +/// @dev where Extra Buffer for L1 cost = (L1 Estimated Cost / L2 Gas Price) +contract ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum is FunctionsFulfillmentSetup { + address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C); + uint256 private constant L1_FEE_WEI = 15_818_209_764_247; + + uint96 l1FeeJuels = uint96((1e18 * L1_FEE_WEI) / uint256(LINK_ETH_RATE)); + + function setUp() public virtual override { + vm.mockCall(ARBGAS_ADDR, abi.encodeWithSelector(ArbGasInfo.getCurrentTxL1GasFees.selector), abi.encode(L1_FEE_WEI)); + } + + function test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() public { + // Set the chainID + vm.chainId(42161); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } + + function test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() public { + // Set the chainID + vm.chainId(421613); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } + + function test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() public { + // Set the chainID + vm.chainId(421614); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } +} + +/// @notice #_getCurrentTxL1GasFees Optimism +/// @dev Optimism gas formula = ((l2_base_fee + l2_priority_fee) * l2_gas_used) + L1 data fee +/// @dev where L1 data fee = l1_gas_price * ((count_zero_bytes(tx_data) * 4 + count_non_zero_bytes(tx_data) * 16) + fixed_overhead + noncalldata_gas) * dynamic_overhead +contract ChainSpecificUtil__getCurrentTxL1GasFees_Optimism is FunctionsFulfillmentSetup { + address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); + uint256 private constant L1_FEE_WEI = 15_818_209_764_247; + + uint96 l1FeeJuels = uint96((1e18 * L1_FEE_WEI) / uint256(LINK_ETH_RATE)); + + function setUp() public virtual override { + vm.mockCall( + OVM_GASPRICEORACLE_ADDR, + abi.encodeWithSelector(OVM_GasPriceOracle.getL1Fee.selector), + abi.encode(L1_FEE_WEI) + ); + } + + function test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() public { + // Set the chainID + vm.chainId(10); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } + + function test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() public { + // Set the chainID + vm.chainId(420); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } + + function test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() public { + // Set the chainID + vm.chainId(11155420); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } +} + +/// @notice #_getCurrentTxL1GasFees Base +/// @dev Base gas formula uses Optimism formula = ((l2_base_fee + l2_priority_fee) * l2_gas_used) + L1 data fee +/// @dev where L1 data fee = l1_gas_price * ((count_zero_bytes(tx_data) * 4 + count_non_zero_bytes(tx_data) * 16) + fixed_overhead + noncalldata_gas) * dynamic_overhead +contract ChainSpecificUtil__getCurrentTxL1GasFees_Base is FunctionsFulfillmentSetup { + address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); + uint256 private constant L1_FEE_WEI = 15_818_209_764_247; + + uint96 l1FeeJuels = uint96((1e18 * L1_FEE_WEI) / uint256(LINK_ETH_RATE)); + + function setUp() public virtual override { + vm.mockCall( + OVM_GASPRICEORACLE_ADDR, + abi.encodeWithSelector(OVM_GasPriceOracle.getL1Fee.selector), + abi.encode(L1_FEE_WEI) + ); + } + + function test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() public { + // Set the chainID + vm.chainId(8453); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } + + function test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() public { + // Set the chainID + vm.chainId(84531); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } + + function test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() public { + // Set the chainID + vm.chainId(84532); + + // Setup sends and fulfills request #1 + FunctionsFulfillmentSetup.setUp(); + + // Check request cost estimate + uint96 expectedEstimatedTotalCostJuels = _getExpectedCostEstimate(s_requests[1].requestData.callbackGasLimit) + + l1FeeJuels; + assertEq(s_requests[1].commitment.estimatedTotalCostJuels, expectedEstimatedTotalCostJuels); + + // Check response actual cost + uint96 expectedTotalCostJuels = _getExpectedCost(5416) + l1FeeJuels; + assertEq(s_responses[1].totalCostJuels, expectedTotalCostJuels); + } +} diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol index 7166add19fe..f6d3d41e632 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsCoordinator.t.sol @@ -10,7 +10,6 @@ import {Routable} from "../../dev/v1_X/Routable.sol"; import {BaseTest} from "./BaseTest.t.sol"; import {FunctionsRouterSetup, FunctionsDONSetup, FunctionsSubscriptionSetup} from "./Setup.t.sol"; -import "forge-std/console.sol"; /// @notice #constructor contract FunctionsCoordinator_Constructor is FunctionsRouterSetup { diff --git a/contracts/src/v0.8/functions/tests/v1_X/Gas.t.sol b/contracts/src/v0.8/functions/tests/v1_X/Gas.t.sol index 55ab3810b41..f2d7af54e4f 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/Gas.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/Gas.t.sol @@ -154,14 +154,6 @@ contract Gas_SendRequest is FunctionsSubscriptionSetup { /// @notice #fulfillRequest contract FunctionsClient_FulfillRequest is FunctionsClientRequestSetup { - struct Report { - bytes32[] rs; - bytes32[] ss; - bytes32 vs; - bytes report; - bytes32[3] reportContext; - } - mapping(uint256 reportNumber => Report) s_reports; FunctionsClientTestHelper s_functionsClientWithMaximumReturnData; diff --git a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol index 0c08fd20cd3..97418958bc2 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol @@ -225,6 +225,14 @@ contract FunctionsSubscriptionSetup is FunctionsClientSetup { /// @notice Set up to initate a minimal request and store it in s_requests[1] contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { + struct Report { + bytes32[] rs; + bytes32[] ss; + bytes32 vs; + bytes report; + bytes32[3] reportContext; + } + struct RequestData { string sourceCode; bytes secrets; @@ -240,6 +248,12 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { mapping(uint256 requestNumber => Request) s_requests; + struct Response { + uint96 totalCostJuels; + } + + mapping(uint256 requestNumber => Response) s_responses; + uint96 s_fulfillmentRouterOwnerBalance = 0; uint96 s_fulfillmentCoordinatorBalance = 0; @@ -255,7 +269,24 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { _sendAndStoreRequest(1, sourceCode, secrets, args, bytesArgs, callbackGasLimit); } - function _getExpectedCost(uint256 gasUsed) internal view returns (uint96 totalCostJuels) { + /// @notice Predicts the estimated cost (maximum cost) of a request + /// @dev Meant only for Ethereum, does not add L2 chains' L1 fee + function _getExpectedCostEstimate(uint256 callbackGas) internal view returns (uint96) { + uint256 gasPrice = TX_GASPRICE_START < getCoordinatorConfig().minimumEstimateGasPriceWei + ? getCoordinatorConfig().minimumEstimateGasPriceWei + : TX_GASPRICE_START; + uint256 gasPriceWithOverestimation = gasPrice + + ((gasPrice * getCoordinatorConfig().fulfillmentGasPriceOverEstimationBP) / 10_000); + uint96 juelsPerGas = uint96((1e18 * gasPriceWithOverestimation) / uint256(LINK_ETH_RATE)); + uint96 gasOverheadJuels = juelsPerGas * + ((getCoordinatorConfig().gasOverheadBeforeCallback + getCoordinatorConfig().gasOverheadAfterCallback)); + uint96 callbackGasCostJuels = uint96(juelsPerGas * callbackGas); + return gasOverheadJuels + s_donFee + s_adminFee + callbackGasCostJuels; + } + + /// @notice Predicts the actual cost of a request + /// @dev Meant only for Ethereum, does not add L2 chains' L1 fee + function _getExpectedCost(uint256 gasUsed) internal view returns (uint96) { uint96 juelsPerGas = uint96((1e18 * TX_GASPRICE_START) / uint256(LINK_ETH_RATE)); uint96 gasOverheadJuels = juelsPerGas * (getCoordinatorConfig().gasOverheadBeforeCallback + getCoordinatorConfig().gasOverheadAfterCallback); @@ -400,7 +431,7 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { bytes memory report, bytes32[3] memory reportContext, uint256[] memory signerPrivateKeys - ) internal pure returns (bytes32[] memory rawRs, bytes32[] memory rawSs, bytes32 rawVs) { + ) internal pure returns (bytes32[] memory, bytes32[] memory, bytes32) { bytes32[] memory rs = new bytes32[](signerPrivateKeys.length); bytes32[] memory ss = new bytes32[](signerPrivateKeys.length); bytes memory vs = new bytes(signerPrivateKeys.length); @@ -417,13 +448,35 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { return (rs, ss, bytes32(vs)); } + function _buildAndSignReport( + uint256[] memory requestNumberKeys, + string[] memory results, + bytes[] memory errors + ) internal view returns (Report memory) { + (bytes memory report, bytes32[3] memory reportContext) = _buildReport(requestNumberKeys, results, errors); + + // Sign the report + // Need at least 3 signers to fulfill minimum number of: (configInfo.n + configInfo.f) / 2 + 1 + uint256[] memory signerPrivateKeys = new uint256[](3); + signerPrivateKeys[0] = NOP_SIGNER_PRIVATE_KEY_1; + signerPrivateKeys[1] = NOP_SIGNER_PRIVATE_KEY_2; + signerPrivateKeys[2] = NOP_SIGNER_PRIVATE_KEY_3; + (bytes32[] memory rawRs, bytes32[] memory rawSs, bytes32 rawVs) = _signReport( + report, + reportContext, + signerPrivateKeys + ); + + return Report({report: report, reportContext: reportContext, rs: rawRs, ss: rawSs, vs: rawVs}); + } + /// @notice Provide a response from the DON to fulfill one or more requests and store the updated balances of the DON & Admin /// @param requestNumberKeys - One or more requestNumberKeys that were used to store the request in `s_requests` of the requests, that will be added to the report /// @param results - The result that will be sent to the consumer contract's callback. For each index, e.g. result[index] or errors[index], only one of should be filled. /// @param errors - The error that will be sent to the consumer contract's callback. For each index, e.g. result[index] or errors[index], only one of should be filled. /// @param transmitter - The address that will send the `.report` transaction /// @param expectedToSucceed - Boolean representing if the report transmission is expected to produce a RequestProcessed event for every fulfillment. If not, we ignore retrieving the event log. - /// @param requestProcessedIndex - On a successful fulfillment the Router will emit a RequestProcessed event. To grab that event we must know the order at which this event was thrown in the report transmission lifecycle. This can change depending on the test setup (e.g. the Client contract gives an extra event during its callback) + /// @param requestProcessedStartIndex - On a successful fulfillment the Router will emit a RequestProcessed event. To grab that event we must know the order at which this event was thrown in the report transmission lifecycle. This can change depending on the test setup (e.g. the Client contract gives an extra event during its callback) /// @param transmitterGasToUse - Override the default amount of gas that the transmitter sends the `.report` transaction with function _reportAndStore( uint256[] memory requestNumberKeys, @@ -431,7 +484,7 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { bytes[] memory errors, address transmitter, bool expectedToSucceed, - uint8 requestProcessedIndex, + uint8 requestProcessedStartIndex, uint256 transmitterGasToUse ) internal { { @@ -440,19 +493,7 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { } } - (bytes memory report, bytes32[3] memory reportContext) = _buildReport(requestNumberKeys, results, errors); - - // Sign the report - // Need at least 3 signers to fulfill minimum number of: (configInfo.n + configInfo.f) / 2 + 1 - uint256[] memory signerPrivateKeys = new uint256[](3); - signerPrivateKeys[0] = NOP_SIGNER_PRIVATE_KEY_1; - signerPrivateKeys[1] = NOP_SIGNER_PRIVATE_KEY_2; - signerPrivateKeys[2] = NOP_SIGNER_PRIVATE_KEY_3; - (bytes32[] memory rawRs, bytes32[] memory rawSs, bytes32 rawVs) = _signReport( - report, - reportContext, - signerPrivateKeys - ); + Report memory r = _buildAndSignReport(requestNumberKeys, results, errors); // Send as transmitter vm.stopPrank(); @@ -461,20 +502,24 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { // Send report vm.recordLogs(); if (transmitterGasToUse > 0) { - s_functionsCoordinator.transmit{gas: transmitterGasToUse}(reportContext, report, rawRs, rawSs, rawVs); + s_functionsCoordinator.transmit{gas: transmitterGasToUse}(r.reportContext, r.report, r.rs, r.ss, r.vs); } else { - s_functionsCoordinator.transmit(reportContext, report, rawRs, rawSs, rawVs); + s_functionsCoordinator.transmit(r.reportContext, r.report, r.rs, r.ss, r.vs); } if (expectedToSucceed) { // Get actual cost from RequestProcessed event log (uint96 totalCostJuels, , , , , ) = abi.decode( - vm.getRecordedLogs()[requestProcessedIndex].data, + vm.getRecordedLogs()[requestProcessedStartIndex].data, (uint96, address, FunctionsResponse.FulfillResult, bytes, bytes, bytes) ); + // Store response of first request + // TODO: handle multiple requests + s_responses[requestNumberKeys[0]] = Response({totalCostJuels: totalCostJuels}); // Store profit amounts - s_fulfillmentRouterOwnerBalance += s_adminFee; + s_fulfillmentRouterOwnerBalance += s_adminFee * uint96(requestNumberKeys.length); // totalCostJuels = costWithoutCallbackJuels + adminFee + callbackGasCostJuels + // TODO: handle multiple requests s_fulfillmentCoordinatorBalance += totalCostJuels - s_adminFee; } diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol index bc103fc3561..c1b6d5d0b14 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsCoordinatorHarness.sol @@ -79,9 +79,10 @@ contract FunctionsCoordinatorHarness is FunctionsCoordinator { bytes memory response, bytes memory err, bytes memory onchainMetadata, - bytes memory offchainMetadata + bytes memory offchainMetadata, + uint8 reportBatchSize ) external returns (FunctionsResponse.FulfillResult) { - return super._fulfillAndBill(requestId, response, err, onchainMetadata, offchainMetadata); + return super._fulfillAndBill(requestId, response, err, onchainMetadata, offchainMetadata, reportBatchSize); } function disperseFeePool_HARNESS() external { diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index ffe072fc657..b2e5ec4b09a 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -72,7 +72,7 @@ type FunctionsResponseRequestMeta struct { var FunctionsCoordinatorMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60c06040523480156200001157600080fd5b506040516200529938038062005299833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614c166200068360003960008181610845015281816109d301528181610ca601528181610f3a0152818161104501528181611830015261332c0152600061126e0152614c166000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a036600461361f565b61059c565b005b6101a56101b53660046137c8565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b60405161020391906138e2565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c36600461398a565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613a19565b6108d7565b6101a5610a90565b6101a5610b92565b6101a561029336600461361f565b610d92565b6102a0610de2565b6040516102039190613aa3565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613ab6565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613acf565b610fd4565b6040516102039190613c24565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613c78565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b6040516102039190613d2f565b61053b610536366004613e1f565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004613f38565b6119e3565b61057b61240f565b604051908152602001610203565b6101a5610597366004614005565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec8284836140bb565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f518486290610836908390613d2f565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d291906141e1565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff1661422d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd2614252565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c31614252565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf5614252565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614281565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec8284836140bb565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e6090614022565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea890614022565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed490614022565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a8836142b9565b6128b2565b90506110bf6060830160408401614005565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a088016143a6565b61111f61016088016101408901614005565b61112988806143c3565b61113b6101208b016101008c01614428565b60208b01356111516101008d0160e08e01614443565b8b60405161116799989796959493929190614460565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d50565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a89190614508565b6112b29190614550565b6112bd906001614508565b60ff1690506112dd565b60208201516112d7906001614508565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f2614572565b600281111561140357611403614572565b905250905060028160200151600281111561142057611420614572565b14801561146757506006816000015160ff168154811061144257611442614252565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da6135b7565b6000808a8a6040516114ed9291906145a1565b604051908190038120611504918e906020016145b1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d614252565b61157a91901a601b614508565b8e8e8681811061158c5761158c614252565b905060200201358d8d878181106115a5576115a5614252565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff8082168552929650929450840191610100900416600281111561168457611684614572565b600281111561169557611695614572565b90525092506001836020015160028111156116b2576116b2614572565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061173357611733614252565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf614252565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa600186614508565b9450508061180790614281565b905061154e565b50505061181f833383858e8e612e07565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd5565b98975050505050505050565b6060600c805461199b90614022565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea890614022565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b598160036145c5565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c10908861311d565b60055415611dc557600554600090611c2a906001906145dc565b9050600060058281548110611c4157611c41614252565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b614252565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb6145ef565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d646145ef565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee614252565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e38614572565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed0614252565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f71614572565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b614252565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe5614572565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f614252565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561212057612120614572565b02179055505082518051600592508390811061213e5761213e614252565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba614252565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790558061222481614281565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e49184917401000000000000000000000000000000000000000090041661461e565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a00151613136565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff1696909591949193919261463b565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c191906146eb565b5093505092505080426125d491906145dc565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b612679816131e1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b90508051600003612768576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600b54600091612787916bffffffffffffffffffffffff1661473b565b905060005b82518110156128535781600a60008584815181106127ac576127ac614252565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128149190614766565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284c90614281565b905061278c565b508151612860908261478b565b600b80546000906128809084906bffffffffffffffffffffffff1661422d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6d576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aaa8560e001513a848860800151612fd5565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b06576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b1f91906147b3565b905060003087604001518860a001518960c001516001612b3f91906147c6565b8a5180516020918201206101008d015160e08e0151604051612bf398979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d029190613c24565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5d8260206145c5565b612d688560206145c5565b612d74886101446147b3565b612d7e91906147b3565b612d8891906147b3565b612d939060006147b3565b9050368114612dfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e19868801886148c2565b8451949950929750909550935091501580612e3657508351855114155b80612e4357508251855114155b80612e5057508151855114155b80612e5d57508051855114155b15612e94576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8551811015612fc7576000612f2c878381518110612eb757612eb7614252565b6020026020010151878481518110612ed157612ed1614252565b6020026020010151878581518110612eeb57612eeb614252565b6020026020010151878681518110612f0557612f05614252565b6020026020010151878781518110612f1f57612f1f614252565b60200260200101516132d6565b90506000816006811115612f4257612f42614572565b1480612f5f57506001816006811115612f5d57612f5d614572565b145b15612fb657868281518110612f7657612f76614252565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc081614281565b9050612e97565b505050505050505050505050565b6008546000908190869061300d9063ffffffff6c0100000000000000000000000082048116916801000000000000000090041661461e565b613017919061461e565b60085463ffffffff919091169150790100000000000000000000000000000000000000000000000000900464ffffffffff1685101561307a57600854790100000000000000000000000000000000000000000000000000900464ffffffffff1694505b600854600090612710906130949063ffffffff16886145c5565b61309e9190614994565b6130a890876147b3565b905060006130b5826134e6565b905060006130d1846bffffffffffffffffffffffff84166145c5565b905060006130ed68ffffffffffffffffff808916908a16614766565b905061310f61310a6bffffffffffffffffffffffff8316846147b3565b613515565b9a9950505050505050505050565b6000613127610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161315a999897969594939291906149a8565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613260576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080838060200190518101906132ed9190614a74565b905060006132fa3a6134e6565b905060008261012001518361010001516133149190614b3c565b6133259064ffffffffff168361478b565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298b8b878960e0015168ffffffffffffffffff16886133849190614766565b338b6040518763ffffffff1660e01b81526004016133a796959493929190614b5a565b60408051808303816000875af11580156133c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133e99190614bd6565b9092509050600082600681111561340257613402614572565b148061341f5750600182600681111561341d5761341d614572565b145b156134d85760008b81526007602052604081205561343d8184614766565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0870151600b805468ffffffffffffffffff909216939092916134a991859116614766565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505b509998505050505050505050565b600061350f6134f361240f565b61350584670de0b6b3a76400006145c5565b61310a9190614994565b92915050565b60006bffffffffffffffffffffffff8211156135b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f8401126135e857600080fd5b50813567ffffffffffffffff81111561360057600080fd5b60208301915083602082850101111561361857600080fd5b9250929050565b6000806020838503121561363257600080fd5b823567ffffffffffffffff81111561364957600080fd5b613655858286016135d6565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156136b4576136b4613661565b60405290565b604051610160810167ffffffffffffffff811182821017156136b4576136b4613661565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561372557613725613661565b604052919050565b63ffffffff8116811461267957600080fd5b80356111708161372d565b68ffffffffffffffffff8116811461267957600080fd5b80356111708161374a565b64ffffffffff8116811461267957600080fd5b80356111708161376c565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b600061012082840312156137db57600080fd5b6137e3613690565b6137ec8361373f565b81526137fa6020840161373f565b602082015261380b6040840161373f565b604082015261381c6060840161373f565b606082015261382d60808401613761565b608082015261383e60a0840161377f565b60a082015261384f60c0840161378a565b60c082015261386060e0840161379c565b60e082015261010061387381850161373f565b908201529392505050565b6000815180845260005b818110156138a457602081850181015186830182015201613888565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b6020815260006138f5602083018461387e565b9392505050565b600082601f83011261390d57600080fd5b813567ffffffffffffffff81111561392757613927613661565b61395860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016136de565b81815284602083860101111561396d57600080fd5b816020850160208301376000918101602001919091529392505050565b60006020828403121561399c57600080fd5b813567ffffffffffffffff8111156139b357600080fd5b6139bf848285016138fc565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b8035611170816139c7565b6bffffffffffffffffffffffff8116811461267957600080fd5b8035611170816139f4565b60008060408385031215613a2c57600080fd5b8235613a37816139c7565b91506020830135613a47816139f4565b809150509250929050565b600081518084526020808501945080840160005b83811015613a9857815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613a66565b509495945050505050565b6020815260006138f56020830184613a52565b600060208284031215613ac857600080fd5b5035919050565b600060208284031215613ae157600080fd5b813567ffffffffffffffff811115613af857600080fd5b820161016081850312156138f557600080fd5b805182526020810151613b36602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613b5660408401826bffffffffffffffffffffffff169052565b506060810151613b7e606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613b9a608084018267ffffffffffffffff169052565b5060a0810151613bb260a084018263ffffffff169052565b5060c0810151613bcf60c084018268ffffffffffffffffff169052565b5060e0810151613bec60e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b610160810161350f8284613b0b565b60008083601f840112613c4557600080fd5b50813567ffffffffffffffff811115613c5d57600080fd5b6020830191508360208260051b850101111561361857600080fd5b60008060008060008060008060e0898b031215613c9457600080fd5b606089018a811115613ca557600080fd5b8998503567ffffffffffffffff80821115613cbf57600080fd5b613ccb8c838d016135d6565b909950975060808b0135915080821115613ce457600080fd5b613cf08c838d01613c33565b909750955060a08b0135915080821115613d0957600080fd5b50613d168b828c01613c33565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151613d83608084018268ffffffffffffffffff169052565b5060a0830151613d9c60a084018264ffffffffff169052565b5060c0830151613db260c084018261ffff169052565b5060e0830151613de260e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b803561117081613dfe565b600080600080600060808688031215613e3757600080fd5b8535613e4281613dfe565b9450602086013567ffffffffffffffff811115613e5e57600080fd5b613e6a888289016135d6565b9095509350506040860135613e7e8161372d565b949793965091946060013592915050565b600067ffffffffffffffff821115613ea957613ea9613661565b5060051b60200190565b600082601f830112613ec457600080fd5b81356020613ed9613ed483613e8f565b6136de565b82815260059290921b84018101918181019086841115613ef857600080fd5b8286015b84811015613f1c578035613f0f816139c7565b8352918301918301613efc565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c08789031215613f5157600080fd5b863567ffffffffffffffff80821115613f6957600080fd5b613f758a838b01613eb3565b97506020890135915080821115613f8b57600080fd5b613f978a838b01613eb3565b9650613fa560408a01613f27565b95506060890135915080821115613fbb57600080fd5b613fc78a838b016138fc565b9450613fd560808a01613e14565b935060a0890135915080821115613feb57600080fd5b50613ff889828a016138fc565b9150509295509295509295565b60006020828403121561401757600080fd5b81356138f5816139c7565b600181811c9082168061403657607f821691505b60208210810361406f577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c8101602086101561409c5750805b601f850160051c820191505b81811015610a88578281556001016140a8565b67ffffffffffffffff8311156140d3576140d3613661565b6140e7836140e18354614022565b83614075565b6000601f84116001811461413957600085156141035750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556141cf565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156141885786850135825560209485019460019092019101614168565b50868210156141c3577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b80516111708161374a565b6000602082840312156141f357600080fd5b81516138f58161374a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115612661576126616141fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036142b2576142b26141fe565b5060010190565b600061016082360312156142cc57600080fd5b6142d46136ba565b823567ffffffffffffffff8111156142eb57600080fd5b6142f7368286016138fc565b82525060208301356020820152614310604084016139e9565b604082015261432160608401613a0e565b606082015261433260808401613761565b608082015261434360a08401613e14565b60a082015261435460c08401613e14565b60c082015261436560e0840161373f565b60e082015261010061437881850161378a565b9082015261012061438a848201613e14565b9082015261014061439c8482016139e9565b9082015292915050565b6000602082840312156143b857600080fd5b81356138f581613dfe565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126143f857600080fd5b83018035915067ffffffffffffffff82111561441357600080fd5b60200191503681900382131561361857600080fd5b60006020828403121561443a57600080fd5b6138f58261378a565b60006020828403121561445557600080fd5b81356138f58161372d565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061310f60e0830184613b0b565b60ff818116838216019081111561350f5761350f6141fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061456357614563614521565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b808202811582820484141761350f5761350f6141fe565b8181038181111561350f5761350f6141fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff818116838216019080821115612661576126616141fe565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261466b8184018a613a52565b9050828103608084015261467f8189613a52565b905060ff871660a084015282810360c084015261469c818761387e565b905067ffffffffffffffff851660e08401528281036101008401526146c1818561387e565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a0868803121561470357600080fd5b61470c866146d1565b945060208601519350604086015192506060860151915061472f608087016146d1565b90509295509295909350565b60006bffffffffffffffffffffffff8084168061475a5761475a614521565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115612661576126616141fe565b6bffffffffffffffffffffffff818116838216028082169190828114613df657613df66141fe565b8082018082111561350f5761350f6141fe565b67ffffffffffffffff818116838216019080821115612661576126616141fe565b600082601f8301126147f857600080fd5b81356020614808613ed483613e8f565b82815260059290921b8401810191818101908684111561482757600080fd5b8286015b84811015613f1c578035835291830191830161482b565b600082601f83011261485357600080fd5b81356020614863613ed483613e8f565b82815260059290921b8401810191818101908684111561488257600080fd5b8286015b84811015613f1c57803567ffffffffffffffff8111156148a65760008081fd5b6148b48986838b01016138fc565b845250918301918301614886565b600080600080600060a086880312156148da57600080fd5b853567ffffffffffffffff808211156148f257600080fd5b6148fe89838a016147e7565b9650602088013591508082111561491457600080fd5b61492089838a01614842565b9550604088013591508082111561493657600080fd5b61494289838a01614842565b9450606088013591508082111561495857600080fd5b61496489838a01614842565b9350608088013591508082111561497a57600080fd5b5061498788828901614842565b9150509295509295909350565b6000826149a3576149a3614521565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b1660408501528160608501526149ef8285018b613a52565b91508382036080850152614a03828a613a52565b915060ff881660a085015283820360c0850152614a20828861387e565b90861660e085015283810361010085015290506146c1818561387e565b8051611170816139c7565b8051611170816139f4565b805161117081613dfe565b80516111708161372d565b80516111708161376c565b60006101608284031215614a8757600080fd5b614a8f6136ba565b82518152614a9f60208401614a3d565b6020820152614ab060408401614a48565b6040820152614ac160608401614a3d565b6060820152614ad260808401614a53565b6080820152614ae360a08401614a5e565b60a0820152614af460c084016141d6565b60c0820152614b0560e084016141d6565b60e0820152610100614b18818501614a69565b90820152610120614b2a848201614a69565b90820152610140613873848201614a5e565b64ffffffffff818116838216019080821115612661576126616141fe565b6000610200808352614b6e8184018a61387e565b90508281036020840152614b82818961387e565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614bcb905060a0830184613b0b565b979650505050505050565b60008060408385031215614be957600080fd5b825160078110614bf857600080fd5b6020840151909250613a47816139f456fea164736f6c6343000813000a", + Bin: "0x60c06040523480156200001157600080fd5b506040516200556438038062005564833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614ee16200068360003960008181610845015281816109d301528181610ca601528181610f3a015281816110450152818161183001526133aa0152600061126e0152614ee16000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a0366004613857565b61059c565b005b6101a56101b5366004613a00565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613b24565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613bc5565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613c54565b6108d7565b6101a5610a90565b6101a5610b92565b6101a5610293366004613857565b610d92565b6102a0610de2565b6040516102039190613cde565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613cf1565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613d0a565b610fd4565b6040516102039190613e5f565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613eb3565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b6040516102039190613f6a565b61053b61053636600461405a565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004614173565b6119e3565b61057b61240f565b604051908152602001610203565b6101a5610597366004614240565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec8284836142f6565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f518486290610836908390613f6a565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d2919061441c565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff16614468565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161448d565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561448d565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d87816144bc565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec8284836142f6565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e609061425d565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea89061425d565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed49061425d565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a8836144f4565b6128b2565b90506110bf6060830160408401614240565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a088016145e1565b61111f61016088016101408901614240565b61112988806145fe565b61113b6101208b016101008c01614663565b60208b01356111516101008d0160e08e0161467e565b8b6040516111679998979695949392919061469b565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d50565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a89190614743565b6112b2919061478b565b6112bd906001614743565b60ff1690506112dd565b60208201516112d7906001614743565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f26147ad565b6002811115611403576114036147ad565b9052509050600281602001516002811115611420576114206147ad565b14801561146757506006816000015160ff16815481106114425761144261448d565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da6137ef565b6000808a8a6040516114ed9291906147dc565b604051908190038120611504918e906020016147ec565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d61448d565b61157a91901a601b614743565b8e8e8681811061158c5761158c61448d565b905060200201358d8d878181106115a5576115a561448d565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff80821685529296509294508401916101009004166002811115611684576116846147ad565b6002811115611695576116956147ad565b90525092506001836020015160028111156116b2576116b26147ad565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f81106117335761173361448d565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf61448d565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa600186614743565b94505080611807906144bc565b905061154e565b50505061181f833383858e8e612e07565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd7565b98975050505050505050565b6060600c805461199b9061425d565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea89061425d565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b59816003614800565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c109088613144565b60055415611dc557600554600090611c2a90600190614817565b9050600060058281548110611c4157611c4161448d565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b61448d565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb61482a565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d6461482a565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee61448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e386147ad565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed061448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f716147ad565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b61448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe56147ad565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f61448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115612120576121206147ad565b02179055505082518051600592508390811061213e5761213e61448d565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba61448d565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905580612224816144bc565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e491849174010000000000000000000000000000000000000000900416614859565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a0015161315d565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff16969095919491939192614876565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c19190614926565b5093505092505080426125d49190614817565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b61267981613208565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b90508051600003612768576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600b54600091612787916bffffffffffffffffffffffff16614976565b905060005b82518110156128535781600a60008584815181106127ac576127ac61448d565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff1661281491906149a1565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284c906144bc565b905061278c565b50815161286090826149c6565b600b80546000906128809084906bffffffffffffffffffffffff16614468565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6d576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aaa8560e001513a848860800151612fd7565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b06576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b1f91906149ee565b905060003087604001518860a001518960c001516001612b3f9190614a01565b8a5180516020918201206101008d015160e08e0151604051612bf398979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d029190613e5f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5d826020614800565b612d68856020614800565b612d74886101446149ee565b612d7e91906149ee565b612d8891906149ee565b612d939060006149ee565b9050368114612dfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e1986880188614afd565b8451949950929750909550935091501580612e3657508351855114155b80612e4357508251855114155b80612e5057508151855114155b80612e5d57508051855114155b15612e94576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8551811015612fc9576000612f2e878381518110612eb757612eb761448d565b6020026020010151878481518110612ed157612ed161448d565b6020026020010151878581518110612eeb57612eeb61448d565b6020026020010151878681518110612f0557612f0561448d565b6020026020010151878781518110612f1f57612f1f61448d565b60200260200101518c516132fd565b90506000816006811115612f4457612f446147ad565b1480612f6157506001816006811115612f5f57612f5f6147ad565b145b15612fb857868281518110612f7857612f7861448d565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc2816144bc565b9050612e97565b505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561303257600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b6008546000906127109061304c9063ffffffff1687614800565b6130569190614bcf565b61306090866149ee565b60085490915060009087906130999063ffffffff6c01000000000000000000000000820481169168010000000000000000900416614859565b6130a39190614859565b63ffffffff16905060006130ed6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061356d92505050565b9050600061310e826130ff8587614800565b61310991906149ee565b6136af565b9050600061312a68ffffffffffffffffff808916908a166149a1565b905061313681836149a1565b9a9950505050505050505050565b600061314e610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161318199989796959493929190614be3565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133149190614caf565b905060003a82610120015183610100015161332f9190614d77565b64ffffffffff166133409190614800565b905060008460ff166133886000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061356d92505050565b6133929190614bcf565b905060006133a361310983856149ee565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298d8d6133ef3a6136af565b60e08b01516134099068ffffffffffffffffff16896149a1565b338c6040518763ffffffff1660e01b815260040161342c96959493929190614d95565b60408051808303816000875af115801561344a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061346e9190614e11565b90925090506000826006811115613487576134876147ad565b14806134a4575060018260068111156134a2576134a26147ad565b145b1561355d5760008d8152600760205260408120556134c281846149a1565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0880151600b805468ffffffffffffffffff9092169390929161352e918591166149a1565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505b509b9a5050505050505050505050565b600046613579816136e3565b156135f557606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ee9190614e44565b9392505050565b6135fe81613706565b156136a65773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e84604051806080016040528060488152602001614e8d6048913960405160200161365e929190614e5d565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016136899190613b24565b602060405180830381865afa1580156135ca573d6000803e3d6000fd5b50600092915050565b60006136dd6136bc61240f565b6136ce84670de0b6b3a7640000614800565b6136d89190614bcf565b61374d565b92915050565b600061a4b18214806136f7575062066eed82145b806136dd57505062066eee1490565b6000600a82148061371857506101a482145b80613725575062aa37dc82145b80613731575061210582145b8061373e575062014a3382145b806136dd57505062014a341490565b60006bffffffffffffffffffffffff8211156137eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f84011261382057600080fd5b50813567ffffffffffffffff81111561383857600080fd5b60208301915083602082850101111561385057600080fd5b9250929050565b6000806020838503121561386a57600080fd5b823567ffffffffffffffff81111561388157600080fd5b61388d8582860161380e565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156138ec576138ec613899565b60405290565b604051610160810167ffffffffffffffff811182821017156138ec576138ec613899565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561395d5761395d613899565b604052919050565b63ffffffff8116811461267957600080fd5b803561117081613965565b68ffffffffffffffffff8116811461267957600080fd5b803561117081613982565b64ffffffffff8116811461267957600080fd5b8035611170816139a4565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613a1357600080fd5b613a1b6138c8565b613a2483613977565b8152613a3260208401613977565b6020820152613a4360408401613977565b6040820152613a5460608401613977565b6060820152613a6560808401613999565b6080820152613a7660a084016139b7565b60a0820152613a8760c084016139c2565b60c0820152613a9860e084016139d4565b60e0820152610100613aab818501613977565b908201529392505050565b60005b83811015613ad1578181015183820152602001613ab9565b50506000910152565b60008151808452613af2816020860160208601613ab6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006135ee6020830184613ada565b600082601f830112613b4857600080fd5b813567ffffffffffffffff811115613b6257613b62613899565b613b9360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613916565b818152846020838601011115613ba857600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613bd757600080fd5b813567ffffffffffffffff811115613bee57600080fd5b613bfa84828501613b37565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b803561117081613c02565b6bffffffffffffffffffffffff8116811461267957600080fd5b803561117081613c2f565b60008060408385031215613c6757600080fd5b8235613c7281613c02565b91506020830135613c8281613c2f565b809150509250929050565b600081518084526020808501945080840160005b83811015613cd357815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613ca1565b509495945050505050565b6020815260006135ee6020830184613c8d565b600060208284031215613d0357600080fd5b5035919050565b600060208284031215613d1c57600080fd5b813567ffffffffffffffff811115613d3357600080fd5b820161016081850312156135ee57600080fd5b805182526020810151613d71602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613d9160408401826bffffffffffffffffffffffff169052565b506060810151613db9606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613dd5608084018267ffffffffffffffff169052565b5060a0810151613ded60a084018263ffffffff169052565b5060c0810151613e0a60c084018268ffffffffffffffffff169052565b5060e0810151613e2760e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016136dd8284613d46565b60008083601f840112613e8057600080fd5b50813567ffffffffffffffff811115613e9857600080fd5b6020830191508360208260051b850101111561385057600080fd5b60008060008060008060008060e0898b031215613ecf57600080fd5b606089018a811115613ee057600080fd5b8998503567ffffffffffffffff80821115613efa57600080fd5b613f068c838d0161380e565b909950975060808b0135915080821115613f1f57600080fd5b613f2b8c838d01613e6e565b909750955060a08b0135915080821115613f4457600080fd5b50613f518b828c01613e6e565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151613fbe608084018268ffffffffffffffffff169052565b5060a0830151613fd760a084018264ffffffffff169052565b5060c0830151613fed60c084018261ffff169052565b5060e083015161401d60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b803561117081614039565b60008060008060006080868803121561407257600080fd5b853561407d81614039565b9450602086013567ffffffffffffffff81111561409957600080fd5b6140a58882890161380e565b90955093505060408601356140b981613965565b949793965091946060013592915050565b600067ffffffffffffffff8211156140e4576140e4613899565b5060051b60200190565b600082601f8301126140ff57600080fd5b8135602061411461410f836140ca565b613916565b82815260059290921b8401810191818101908684111561413357600080fd5b8286015b8481101561415757803561414a81613c02565b8352918301918301614137565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561418c57600080fd5b863567ffffffffffffffff808211156141a457600080fd5b6141b08a838b016140ee565b975060208901359150808211156141c657600080fd5b6141d28a838b016140ee565b96506141e060408a01614162565b955060608901359150808211156141f657600080fd5b6142028a838b01613b37565b945061421060808a0161404f565b935060a089013591508082111561422657600080fd5b5061423389828a01613b37565b9150509295509295509295565b60006020828403121561425257600080fd5b81356135ee81613c02565b600181811c9082168061427157607f821691505b6020821081036142aa577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156142d75750805b601f850160051c820191505b81811015610a88578281556001016142e3565b67ffffffffffffffff83111561430e5761430e613899565b6143228361431c835461425d565b836142b0565b6000601f841160018114614374576000851561433e5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561440a565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156143c357868501358255602094850194600190920191016143a3565b50868210156143fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613982565b60006020828403121561442e57600080fd5b81516135ee81613982565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561266157612661614439565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036144ed576144ed614439565b5060010190565b6000610160823603121561450757600080fd5b61450f6138f2565b823567ffffffffffffffff81111561452657600080fd5b61453236828601613b37565b8252506020830135602082015261454b60408401613c24565b604082015261455c60608401613c49565b606082015261456d60808401613999565b608082015261457e60a0840161404f565b60a082015261458f60c0840161404f565b60c08201526145a060e08401613977565b60e08201526101006145b38185016139c2565b908201526101206145c584820161404f565b908201526101406145d7848201613c24565b9082015292915050565b6000602082840312156145f357600080fd5b81356135ee81614039565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261463357600080fd5b83018035915067ffffffffffffffff82111561464e57600080fd5b60200191503681900382131561385057600080fd5b60006020828403121561467557600080fd5b6135ee826139c2565b60006020828403121561469057600080fd5b81356135ee81613965565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061313660e0830184613d46565b60ff81811683821601908111156136dd576136dd614439565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061479e5761479e61475c565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b80820281158282048414176136dd576136dd614439565b818103818111156136dd576136dd614439565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561266157612661614439565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526148a68184018a613c8d565b905082810360808401526148ba8189613c8d565b905060ff871660a084015282810360c08401526148d78187613ada565b905067ffffffffffffffff851660e08401528281036101008401526148fc8185613ada565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a0868803121561493e57600080fd5b6149478661490c565b945060208601519350604086015192506060860151915061496a6080870161490c565b90509295509295909350565b60006bffffffffffffffffffffffff808416806149955761499561475c565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561266157612661614439565b6bffffffffffffffffffffffff81811683821602808216919082811461403157614031614439565b808201808211156136dd576136dd614439565b67ffffffffffffffff81811683821601908082111561266157612661614439565b600082601f830112614a3357600080fd5b81356020614a4361410f836140ca565b82815260059290921b84018101918181019086841115614a6257600080fd5b8286015b848110156141575780358352918301918301614a66565b600082601f830112614a8e57600080fd5b81356020614a9e61410f836140ca565b82815260059290921b84018101918181019086841115614abd57600080fd5b8286015b8481101561415757803567ffffffffffffffff811115614ae15760008081fd5b614aef8986838b0101613b37565b845250918301918301614ac1565b600080600080600060a08688031215614b1557600080fd5b853567ffffffffffffffff80821115614b2d57600080fd5b614b3989838a01614a22565b96506020880135915080821115614b4f57600080fd5b614b5b89838a01614a7d565b95506040880135915080821115614b7157600080fd5b614b7d89838a01614a7d565b94506060880135915080821115614b9357600080fd5b614b9f89838a01614a7d565b93506080880135915080821115614bb557600080fd5b50614bc288828901614a7d565b9150509295509295909350565b600082614bde57614bde61475c565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614c2a8285018b613c8d565b91508382036080850152614c3e828a613c8d565b915060ff881660a085015283820360c0850152614c5b8288613ada565b90861660e085015283810361010085015290506148fc8185613ada565b805161117081613c02565b805161117081613c2f565b805161117081614039565b805161117081613965565b8051611170816139a4565b60006101608284031215614cc257600080fd5b614cca6138f2565b82518152614cda60208401614c78565b6020820152614ceb60408401614c83565b6040820152614cfc60608401614c78565b6060820152614d0d60808401614c8e565b6080820152614d1e60a08401614c99565b60a0820152614d2f60c08401614411565b60c0820152614d4060e08401614411565b60e0820152610100614d53818501614ca4565b90820152610120614d65848201614ca4565b90820152610140613aab848201614c99565b64ffffffffff81811683821601908082111561266157612661614439565b6000610200808352614da98184018a613ada565b90508281036020840152614dbd8189613ada565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614e06905060a0830184613d46565b979650505050505050565b60008060408385031215614e2457600080fd5b825160078110614e3357600080fd5b6020840151909250613c8281613c2f565b600060208284031215614e5657600080fd5b5051919050565b60008351614e6f818460208801613ab6565b835190830190614e83818360208801613ab6565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", } var FunctionsCoordinatorABI = FunctionsCoordinatorMetaData.ABI diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index cff49cd07c2..3543116d21d 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -4,7 +4,7 @@ functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServ functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b -functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 9e11effc1922d258d3fc38564b87f4466c56162f33d553ec6d66edcfa55923af +functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 948c04942910f308942fdde460317f9ec038b6b702b018471ce6157a14a09072 functions_load_test_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.bin c8dbbd5ebb34435800d6674700068837c3a252db60046a14b0e61e829db517de functions_oracle_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 9dedd3a36043605fd9bedf821e7ec5b4281a5c7ae2e4a1955f37aff8ba13519f From b811f0093a787754ad836049e58cc8cefec67d91 Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 9 Nov 2023 17:43:09 +0000 Subject: [PATCH 120/271] [fix] Use correct format for GH run URL (#11246) --- tools/flakeytests/utils.go | 2 +- tools/flakeytests/utils_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/flakeytests/utils.go b/tools/flakeytests/utils.go index 698a9b49c95..d2326c47262 100644 --- a/tools/flakeytests/utils.go +++ b/tools/flakeytests/utils.go @@ -42,7 +42,7 @@ func getGithubMetadata(repo string, eventName string, sha string, e io.Reader, r log.Fatalf("Error unmarshaling gh event at path") } - runURL := fmt.Sprintf("%s/actions/%s", repo, runID) + runURL := fmt.Sprintf("github.com/%s/actions/runs/%s", repo, runID) basicCtx := &Context{Repository: repo, CommitSHA: sha, Type: eventName, RunURL: runURL} switch eventName { case "pull_request": diff --git a/tools/flakeytests/utils_test.go b/tools/flakeytests/utils_test.go index 761d9cd255c..6ea912d11d4 100644 --- a/tools/flakeytests/utils_test.go +++ b/tools/flakeytests/utils_test.go @@ -37,12 +37,13 @@ var prEventTemplate = ` func TestGetGithubMetadata(t *testing.T) { repo, eventName, sha, event, runID := "chainlink", "merge_group", "a-sha", `{}`, "1234" + expectedRunURL := fmt.Sprintf("github.com/%s/actions/runs/%s", repo, runID) ctx := getGithubMetadata(repo, eventName, sha, strings.NewReader(event), runID) - assert.Equal(t, Context{Repository: repo, CommitSHA: sha, Type: eventName, RunURL: fmt.Sprintf("%s/actions/%s", repo, runID)}, ctx) + assert.Equal(t, Context{Repository: repo, CommitSHA: sha, Type: eventName, RunURL: expectedRunURL}, ctx) anotherSha, eventName, url := "another-sha", "pull_request", "a-url" event = fmt.Sprintf(prEventTemplate, anotherSha, url) sha = "302eb05d592132309b264e316f443f1ceb81b6c3" ctx = getGithubMetadata(repo, eventName, sha, strings.NewReader(event), runID) - assert.Equal(t, Context{Repository: repo, CommitSHA: anotherSha, Type: eventName, PullRequestURL: url, RunURL: fmt.Sprintf("%s/actions/%s", repo, runID)}, ctx) + assert.Equal(t, Context{Repository: repo, CommitSHA: anotherSha, Type: eventName, PullRequestURL: url, RunURL: expectedRunURL}, ctx) } From 5dea552b46533c687b804a40fbed433868732f0b Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Thu, 9 Nov 2023 21:04:30 +0100 Subject: [PATCH 121/271] Generalized Multinode unit tests BCI-2283 (#11066) * POC for generalized multinode tests * sendOnly node tests * multi node tests * node fsm test & node aliveLoop * node lifecycle tests * increase test coverage * fixed flakey tests * fixed rebase artifacts * nit fixes * regen mocks * review fixes * replace core/testutils with `chainlink-relay/pkg/utils/tests` * Apply suggestions from code review Co-authored-by: Jordan Krage Co-authored-by: Dimitris Grigoriou * update chainlink-relay * make gomodtidy * Update common/client/send_only_node_test.go Co-authored-by: Dimitris Grigoriou * simplify node state check * sendOnly & node tests fixes * make gomodtidy * fix deps * update relay * fix silly cleanup issue * Apply suggestions from code review Co-authored-by: Dimitris Grigoriou * review fixes --------- Co-authored-by: Jordan Krage Co-authored-by: Dimitris Grigoriou --- common/client/mock_hashable_test.go | 18 + common/client/mock_head_test.go | 57 + common/client/mock_node_client_test.go | 168 +++ common/client/mock_node_selector_test.go | 57 + common/client/mock_node_test.go | 195 +++ common/client/mock_rpc_test.go | 608 ++++++++++ common/client/mock_send_only_client_test.go | 72 ++ common/client/mock_send_only_node_test.go | 127 ++ common/client/multi_node.go | 44 +- common/client/multi_node_test.go | 635 ++++++++++ common/client/node.go | 25 +- common/client/node_fsm_test.go | 108 ++ common/client/node_lifecycle.go | 12 +- common/client/node_lifecycle_test.go | 1070 +++++++++++++++++ common/client/node_selector.go | 46 + .../client/node_selector_highest_head_test.go | 176 +++ .../node_selector_priority_level_test.go | 88 ++ .../client/node_selector_round_robin_test.go | 61 + common/client/node_selector_test.go | 18 + .../node_selector_total_difficulty_test.go | 178 +++ common/client/node_test.go | 80 ++ common/client/send_only_node.go | 4 + common/client/send_only_node_test.go | 139 +++ common/client/types.go | 6 + common/types/test_utils.go | 16 + 25 files changed, 3956 insertions(+), 52 deletions(-) create mode 100644 common/client/mock_hashable_test.go create mode 100644 common/client/mock_head_test.go create mode 100644 common/client/mock_node_client_test.go create mode 100644 common/client/mock_node_selector_test.go create mode 100644 common/client/mock_node_test.go create mode 100644 common/client/mock_rpc_test.go create mode 100644 common/client/mock_send_only_client_test.go create mode 100644 common/client/mock_send_only_node_test.go create mode 100644 common/client/multi_node_test.go create mode 100644 common/client/node_fsm_test.go create mode 100644 common/client/node_lifecycle_test.go create mode 100644 common/client/node_selector.go create mode 100644 common/client/node_selector_highest_head_test.go create mode 100644 common/client/node_selector_priority_level_test.go create mode 100644 common/client/node_selector_round_robin_test.go create mode 100644 common/client/node_selector_test.go create mode 100644 common/client/node_selector_total_difficulty_test.go create mode 100644 common/client/node_test.go create mode 100644 common/client/send_only_node_test.go create mode 100644 common/types/test_utils.go diff --git a/common/client/mock_hashable_test.go b/common/client/mock_hashable_test.go new file mode 100644 index 00000000000..d9f1670c073 --- /dev/null +++ b/common/client/mock_hashable_test.go @@ -0,0 +1,18 @@ +package client + +import "cmp" + +// Hashable - simple implementation of types.Hashable interface to be used as concrete type in tests +type Hashable string + +func (h Hashable) Cmp(c Hashable) int { + return cmp.Compare(h, c) +} + +func (h Hashable) String() string { + return string(h) +} + +func (h Hashable) Bytes() []byte { + return []byte(h) +} diff --git a/common/client/mock_head_test.go b/common/client/mock_head_test.go new file mode 100644 index 00000000000..b9cf0a5866f --- /dev/null +++ b/common/client/mock_head_test.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + utils "github.com/smartcontractkit/chainlink/v2/core/utils" + mock "github.com/stretchr/testify/mock" +) + +// mockHead is an autogenerated mock type for the Head type +type mockHead struct { + mock.Mock +} + +// BlockDifficulty provides a mock function with given fields: +func (_m *mockHead) BlockDifficulty() *utils.Big { + ret := _m.Called() + + var r0 *utils.Big + if rf, ok := ret.Get(0).(func() *utils.Big); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*utils.Big) + } + } + + return r0 +} + +// BlockNumber provides a mock function with given fields: +func (_m *mockHead) BlockNumber() int64 { + ret := _m.Called() + + var r0 int64 + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + return r0 +} + +// newMockHead creates a new instance of mockHead. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockHead(t interface { + mock.TestingT + Cleanup(func()) +}) *mockHead { + mock := &mockHead{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_node_client_test.go b/common/client/mock_node_client_test.go new file mode 100644 index 00000000000..7c8eb69171f --- /dev/null +++ b/common/client/mock_node_client_test.go @@ -0,0 +1,168 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" +) + +// mockNodeClient is an autogenerated mock type for the NodeClient type +type mockNodeClient[CHAIN_ID types.ID, HEAD Head] struct { + mock.Mock +} + +// ChainID provides a mock function with given fields: ctx +func (_m *mockNodeClient[CHAIN_ID, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { + ret := _m.Called(ctx) + + var r0 CHAIN_ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ClientVersion provides a mock function with given fields: _a0 +func (_m *mockNodeClient[CHAIN_ID, HEAD]) ClientVersion(_a0 context.Context) (string, error) { + ret := _m.Called(_a0) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) Close() { + _m.Called() +} + +// Dial provides a mock function with given fields: ctx +func (_m *mockNodeClient[CHAIN_ID, HEAD]) Dial(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DialHTTP provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) DialHTTP() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DisconnectAll provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) DisconnectAll() { + _m.Called() +} + +// SetAliveLoopSub provides a mock function with given fields: _a0 +func (_m *mockNodeClient[CHAIN_ID, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { + _m.Called(_a0) +} + +// Subscribe provides a mock function with given fields: ctx, channel, args +func (_m *mockNodeClient[CHAIN_ID, HEAD]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { + var _ca []interface{} + _ca = append(_ca, ctx, channel) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 types.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) (types.Subscription, error)); ok { + return rf(ctx, channel, args...) + } + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) types.Subscription); ok { + r0 = rf(ctx, channel, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD, ...interface{}) error); ok { + r1 = rf(ctx, channel, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribersCount provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) SubscribersCount() int32 { + ret := _m.Called() + + var r0 int32 + if rf, ok := ret.Get(0).(func() int32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int32) + } + + return r0 +} + +// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: +func (_m *mockNodeClient[CHAIN_ID, HEAD]) UnsubscribeAllExceptAliveLoop() { + _m.Called() +} + +// newMockNodeClient creates a new instance of mockNodeClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockNodeClient[CHAIN_ID types.ID, HEAD Head](t interface { + mock.TestingT + Cleanup(func()) +}) *mockNodeClient[CHAIN_ID, HEAD] { + mock := &mockNodeClient[CHAIN_ID, HEAD]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_node_selector_test.go b/common/client/mock_node_selector_test.go new file mode 100644 index 00000000000..e7b8d9ecb8d --- /dev/null +++ b/common/client/mock_node_selector_test.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" +) + +// mockNodeSelector is an autogenerated mock type for the NodeSelector type +type mockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { + mock.Mock +} + +// Name provides a mock function with given fields: +func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Name() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Select provides a mock function with given fields: +func (_m *mockNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { + ret := _m.Called() + + var r0 Node[CHAIN_ID, HEAD, RPC] + if rf, ok := ret.Get(0).(func() Node[CHAIN_ID, HEAD, RPC]); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(Node[CHAIN_ID, HEAD, RPC]) + } + } + + return r0 +} + +// newMockNodeSelector creates a new instance of mockNodeSelector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockNodeSelector[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]](t interface { + mock.TestingT + Cleanup(func()) +}) *mockNodeSelector[CHAIN_ID, HEAD, RPC] { + mock := &mockNodeSelector[CHAIN_ID, HEAD, RPC]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go new file mode 100644 index 00000000000..bd704cd2c6f --- /dev/null +++ b/common/client/mock_node_test.go @@ -0,0 +1,195 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" + + utils "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +// mockNode is an autogenerated mock type for the Node type +type mockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]] struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConfiguredChainID provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) ConfiguredChainID() CHAIN_ID { + ret := _m.Called() + + var r0 CHAIN_ID + if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + return r0 +} + +// Name provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Name() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Order provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Order() int32 { + ret := _m.Called() + + var r0 int32 + if rf, ok := ret.Get(0).(func() int32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int32) + } + + return r0 +} + +// RPC provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) RPC() RPC { + ret := _m.Called() + + var r0 RPC + if rf, ok := ret.Get(0).(func() RPC); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(RPC) + } + + return r0 +} + +// Start provides a mock function with given fields: _a0 +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) Start(_a0 context.Context) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// State provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { + ret := _m.Called() + + var r0 nodeState + if rf, ok := ret.Get(0).(func() nodeState); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(nodeState) + } + + return r0 +} + +// StateAndLatest provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *utils.Big) { + ret := _m.Called() + + var r0 nodeState + var r1 int64 + var r2 *utils.Big + if rf, ok := ret.Get(0).(func() (nodeState, int64, *utils.Big)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() nodeState); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(nodeState) + } + + if rf, ok := ret.Get(1).(func() int64); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(int64) + } + + if rf, ok := ret.Get(2).(func() *utils.Big); ok { + r2 = rf() + } else { + if ret.Get(2) != nil { + r2 = ret.Get(2).(*utils.Big) + } + } + + return r0, r1, r2 +} + +// String provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) String() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// SubscribersCount provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) SubscribersCount() int32 { + ret := _m.Called() + + var r0 int32 + if rf, ok := ret.Get(0).(func() int32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int32) + } + + return r0 +} + +// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { + _m.Called() +} + +// newMockNode creates a new instance of mockNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockNode[CHAIN_ID types.ID, HEAD Head, RPC NodeClient[CHAIN_ID, HEAD]](t interface { + mock.TestingT + Cleanup(func()) +}) *mockNode[CHAIN_ID, HEAD, RPC] { + mock := &mockNode[CHAIN_ID, HEAD, RPC]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go new file mode 100644 index 00000000000..c378b9384e4 --- /dev/null +++ b/common/client/mock_rpc_test.go @@ -0,0 +1,608 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + big "math/big" + + assets "github.com/smartcontractkit/chainlink/v2/core/assets" + + context "context" + + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + + mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink/v2/common/types" +) + +// mockRPC is an autogenerated mock type for the RPC type +type mockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH]] struct { + mock.Mock +} + +// BalanceAt provides a mock function with given fields: ctx, accountAddress, blockNumber +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BalanceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (*big.Int, error) { + ret := _m.Called(ctx, accountAddress, blockNumber) + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) (*big.Int, error)); ok { + return rf(ctx, accountAddress, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) *big.Int); ok { + r0 = rf(ctx, accountAddress, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { + r1 = rf(ctx, accountAddress, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BatchCallContext provides a mock function with given fields: ctx, b +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BatchCallContext(ctx context.Context, b []interface{}) error { + ret := _m.Called(ctx, b) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []interface{}) error); ok { + r0 = rf(ctx, b) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// BlockByHash provides a mock function with given fields: ctx, hash +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BlockByHash(ctx context.Context, hash BLOCK_HASH) (HEAD, error) { + ret := _m.Called(ctx, hash) + + var r0 HEAD + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, BLOCK_HASH) (HEAD, error)); ok { + return rf(ctx, hash) + } + if rf, ok := ret.Get(0).(func(context.Context, BLOCK_HASH) HEAD); ok { + r0 = rf(ctx, hash) + } else { + r0 = ret.Get(0).(HEAD) + } + + if rf, ok := ret.Get(1).(func(context.Context, BLOCK_HASH) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BlockByNumber provides a mock function with given fields: ctx, number +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) BlockByNumber(ctx context.Context, number *big.Int) (HEAD, error) { + ret := _m.Called(ctx, number) + + var r0 HEAD + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (HEAD, error)); ok { + return rf(ctx, number) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) HEAD); ok { + r0 = rf(ctx, number) + } else { + r0 = ret.Get(0).(HEAD) + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, number) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CallContext provides a mock function with given fields: ctx, result, method, args +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, ctx, result, method) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, string, ...interface{}) error); ok { + r0 = rf(ctx, result, method, args...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CallContract provides a mock function with given fields: ctx, msg, blockNumber +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CallContract(ctx context.Context, msg interface{}, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, msg, blockNumber) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) ([]byte, error)); ok { + return rf(ctx, msg, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}, *big.Int) []byte); ok { + r0 = rf(ctx, msg, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}, *big.Int) error); ok { + r1 = rf(ctx, msg, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ChainID provides a mock function with given fields: ctx +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) ChainID(ctx context.Context) (CHAIN_ID, error) { + ret := _m.Called(ctx) + + var r0 CHAIN_ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ClientVersion provides a mock function with given fields: _a0 +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) ClientVersion(_a0 context.Context) (string, error) { + ret := _m.Called(_a0) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Close() { + _m.Called() +} + +// CodeAt provides a mock function with given fields: ctx, account, blockNumber +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) CodeAt(ctx context.Context, account ADDR, blockNumber *big.Int) ([]byte, error) { + ret := _m.Called(ctx, account, blockNumber) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) ([]byte, error)); ok { + return rf(ctx, account, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) []byte); ok { + r0 = rf(ctx, account, blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { + r1 = rf(ctx, account, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Dial provides a mock function with given fields: ctx +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Dial(ctx context.Context) error { + ret := _m.Called(ctx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DialHTTP provides a mock function with given fields: +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) DialHTTP() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DisconnectAll provides a mock function with given fields: +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) DisconnectAll() { + _m.Called() +} + +// EstimateGas provides a mock function with given fields: ctx, call +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) EstimateGas(ctx context.Context, call interface{}) (uint64, error) { + ret := _m.Called(ctx, call) + + var r0 uint64 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, interface{}) (uint64, error)); ok { + return rf(ctx, call) + } + if rf, ok := ret.Get(0).(func(context.Context, interface{}) uint64); ok { + r0 = rf(ctx, call) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(context.Context, interface{}) error); ok { + r1 = rf(ctx, call) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FilterEvents provides a mock function with given fields: ctx, query +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) FilterEvents(ctx context.Context, query EVENT_OPS) ([]EVENT, error) { + ret := _m.Called(ctx, query) + + var r0 []EVENT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, EVENT_OPS) ([]EVENT, error)); ok { + return rf(ctx, query) + } + if rf, ok := ret.Get(0).(func(context.Context, EVENT_OPS) []EVENT); ok { + r0 = rf(ctx, query) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]EVENT) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, EVENT_OPS) error); ok { + r1 = rf(ctx, query) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LINKBalance provides a mock function with given fields: ctx, accountAddress, linkAddress +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) LINKBalance(ctx context.Context, accountAddress ADDR, linkAddress ADDR) (*assets.Link, error) { + ret := _m.Called(ctx, accountAddress, linkAddress) + + var r0 *assets.Link + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) (*assets.Link, error)); ok { + return rf(ctx, accountAddress, linkAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) *assets.Link); ok { + r0 = rf(ctx, accountAddress, linkAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*assets.Link) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok { + r1 = rf(ctx, accountAddress, linkAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// LatestBlockHeight provides a mock function with given fields: _a0 +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) LatestBlockHeight(_a0 context.Context) (*big.Int, error) { + ret := _m.Called(_a0) + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PendingSequenceAt provides a mock function with given fields: ctx, addr +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) PendingSequenceAt(ctx context.Context, addr ADDR) (SEQ, error) { + ret := _m.Called(ctx, addr) + + var r0 SEQ + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ADDR) (SEQ, error)); ok { + return rf(ctx, addr) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR) SEQ); ok { + r0 = rf(ctx, addr) + } else { + r0 = ret.Get(0).(SEQ) + } + + if rf, ok := ret.Get(1).(func(context.Context, ADDR) error); ok { + r1 = rf(ctx, addr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendEmptyTransaction provides a mock function with given fields: ctx, newTxAttempt, seq, gasLimit, fee, fromAddress +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SendEmptyTransaction(ctx context.Context, newTxAttempt func(SEQ, uint32, FEE, ADDR) (interface{}, error), seq SEQ, gasLimit uint32, fee FEE, fromAddress ADDR) (string, error) { + ret := _m.Called(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) (string, error)); ok { + return rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) string); ok { + r0 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, func(SEQ, uint32, FEE, ADDR) (interface{}, error), SEQ, uint32, FEE, ADDR) error); ok { + r1 = rf(ctx, newTxAttempt, seq, gasLimit, fee, fromAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SendTransaction provides a mock function with given fields: ctx, tx +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SendTransaction(ctx context.Context, tx TX) error { + ret := _m.Called(ctx, tx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, TX) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SequenceAt provides a mock function with given fields: ctx, accountAddress, blockNumber +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SequenceAt(ctx context.Context, accountAddress ADDR, blockNumber *big.Int) (SEQ, error) { + ret := _m.Called(ctx, accountAddress, blockNumber) + + var r0 SEQ + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) (SEQ, error)); ok { + return rf(ctx, accountAddress, blockNumber) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR, *big.Int) SEQ); ok { + r0 = rf(ctx, accountAddress, blockNumber) + } else { + r0 = ret.Get(0).(SEQ) + } + + if rf, ok := ret.Get(1).(func(context.Context, ADDR, *big.Int) error); ok { + r1 = rf(ctx, accountAddress, blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SetAliveLoopSub provides a mock function with given fields: _a0 +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SetAliveLoopSub(_a0 types.Subscription) { + _m.Called(_a0) +} + +// SimulateTransaction provides a mock function with given fields: ctx, tx +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SimulateTransaction(ctx context.Context, tx TX) error { + ret := _m.Called(ctx, tx) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, TX) error); ok { + r0 = rf(ctx, tx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Subscribe provides a mock function with given fields: ctx, channel, args +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) Subscribe(ctx context.Context, channel chan<- HEAD, args ...interface{}) (types.Subscription, error) { + var _ca []interface{} + _ca = append(_ca, ctx, channel) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 types.Subscription + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) (types.Subscription, error)); ok { + return rf(ctx, channel, args...) + } + if rf, ok := ret.Get(0).(func(context.Context, chan<- HEAD, ...interface{}) types.Subscription); ok { + r0 = rf(ctx, channel, args...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Subscription) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, chan<- HEAD, ...interface{}) error); ok { + r1 = rf(ctx, channel, args...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SubscribersCount provides a mock function with given fields: +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) SubscribersCount() int32 { + ret := _m.Called() + + var r0 int32 + if rf, ok := ret.Get(0).(func() int32); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int32) + } + + return r0 +} + +// TokenBalance provides a mock function with given fields: ctx, accountAddress, tokenAddress +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TokenBalance(ctx context.Context, accountAddress ADDR, tokenAddress ADDR) (*big.Int, error) { + ret := _m.Called(ctx, accountAddress, tokenAddress) + + var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) (*big.Int, error)); ok { + return rf(ctx, accountAddress, tokenAddress) + } + if rf, ok := ret.Get(0).(func(context.Context, ADDR, ADDR) *big.Int); ok { + r0 = rf(ctx, accountAddress, tokenAddress) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, ADDR, ADDR) error); ok { + r1 = rf(ctx, accountAddress, tokenAddress) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionByHash provides a mock function with given fields: ctx, txHash +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TransactionByHash(ctx context.Context, txHash TX_HASH) (TX, error) { + ret := _m.Called(ctx, txHash) + + var r0 TX + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) (TX, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) TX); ok { + r0 = rf(ctx, txHash) + } else { + r0 = ret.Get(0).(TX) + } + + if rf, ok := ret.Get(1).(func(context.Context, TX_HASH) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionReceipt provides a mock function with given fields: ctx, txHash +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) TransactionReceipt(ctx context.Context, txHash TX_HASH) (TX_RECEIPT, error) { + ret := _m.Called(ctx, txHash) + + var r0 TX_RECEIPT + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) (TX_RECEIPT, error)); ok { + return rf(ctx, txHash) + } + if rf, ok := ret.Get(0).(func(context.Context, TX_HASH) TX_RECEIPT); ok { + r0 = rf(ctx, txHash) + } else { + r0 = ret.Get(0).(TX_RECEIPT) + } + + if rf, ok := ret.Get(1).(func(context.Context, TX_HASH) error); ok { + r1 = rf(ctx, txHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UnsubscribeAllExceptAliveLoop provides a mock function with given fields: +func (_m *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]) UnsubscribeAllExceptAliveLoop() { + _m.Called() +} + +// newMockRPC creates a new instance of mockRPC. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockRPC[CHAIN_ID types.ID, SEQ types.Sequence, ADDR types.Hashable, BLOCK_HASH types.Hashable, TX interface{}, TX_HASH types.Hashable, EVENT interface{}, EVENT_OPS interface{}, TX_RECEIPT types.Receipt[TX_HASH, BLOCK_HASH], FEE feetypes.Fee, HEAD types.Head[BLOCK_HASH]](t interface { + mock.TestingT + Cleanup(func()) +}) *mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD] { + mock := &mockRPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_send_only_client_test.go b/common/client/mock_send_only_client_test.go new file mode 100644 index 00000000000..481b2602ea3 --- /dev/null +++ b/common/client/mock_send_only_client_test.go @@ -0,0 +1,72 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" +) + +// mockSendOnlyClient is an autogenerated mock type for the sendOnlyClient type +type mockSendOnlyClient[CHAIN_ID types.ID] struct { + mock.Mock +} + +// ChainID provides a mock function with given fields: _a0 +func (_m *mockSendOnlyClient[CHAIN_ID]) ChainID(_a0 context.Context) (CHAIN_ID, error) { + ret := _m.Called(_a0) + + var r0 CHAIN_ID + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (CHAIN_ID, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) CHAIN_ID); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *mockSendOnlyClient[CHAIN_ID]) Close() { + _m.Called() +} + +// DialHTTP provides a mock function with given fields: +func (_m *mockSendOnlyClient[CHAIN_ID]) DialHTTP() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// newMockSendOnlyClient creates a new instance of mockSendOnlyClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockSendOnlyClient[CHAIN_ID types.ID](t interface { + mock.TestingT + Cleanup(func()) +}) *mockSendOnlyClient[CHAIN_ID] { + mock := &mockSendOnlyClient[CHAIN_ID]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/mock_send_only_node_test.go b/common/client/mock_send_only_node_test.go new file mode 100644 index 00000000000..524d7d8a6c5 --- /dev/null +++ b/common/client/mock_send_only_node_test.go @@ -0,0 +1,127 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package client + +import ( + context "context" + + types "github.com/smartcontractkit/chainlink/v2/common/types" + mock "github.com/stretchr/testify/mock" +) + +// mockSendOnlyNode is an autogenerated mock type for the SendOnlyNode type +type mockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]] struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConfiguredChainID provides a mock function with given fields: +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) ConfiguredChainID() CHAIN_ID { + ret := _m.Called() + + var r0 CHAIN_ID + if rf, ok := ret.Get(0).(func() CHAIN_ID); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(CHAIN_ID) + } + + return r0 +} + +// Name provides a mock function with given fields: +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Name() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// RPC provides a mock function with given fields: +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) RPC() RPC { + ret := _m.Called() + + var r0 RPC + if rf, ok := ret.Get(0).(func() RPC); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(RPC) + } + + return r0 +} + +// Start provides a mock function with given fields: _a0 +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) Start(_a0 context.Context) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// State provides a mock function with given fields: +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) State() nodeState { + ret := _m.Called() + + var r0 nodeState + if rf, ok := ret.Get(0).(func() nodeState); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(nodeState) + } + + return r0 +} + +// String provides a mock function with given fields: +func (_m *mockSendOnlyNode[CHAIN_ID, RPC]) String() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// newMockSendOnlyNode creates a new instance of mockSendOnlyNode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockSendOnlyNode[CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID]](t interface { + mock.TestingT + Cleanup(func()) +}) *mockSendOnlyNode[CHAIN_ID, RPC] { + mock := &mockSendOnlyNode[CHAIN_ID, RPC]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/common/client/multi_node.go b/common/client/multi_node.go index f54e3115d95..c268cfb23cd 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -30,25 +30,6 @@ var ( ErroringNodeError = fmt.Errorf("no live nodes available") ) -const ( - NodeSelectionModeHighestHead = "HighestHead" - NodeSelectionModeRoundRobin = "RoundRobin" - NodeSelectionModeTotalDifficulty = "TotalDifficulty" - NodeSelectionModePriorityLevel = "PriorityLevel" -) - -type NodeSelector[ - CHAIN_ID types.ID, - HEAD Head, - RPC NodeClient[CHAIN_ID, HEAD], -] interface { - // Select returns a Node, or nil if none can be selected. - // Implementation must be thread-safe. - Select() Node[CHAIN_ID, HEAD, RPC] - // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin" - Name() string -} - // MultiNode is a generalized multi node client interface that includes methods to interact with different chains. // It also handles multiple node RPC connections simultaneously. type MultiNode[ @@ -113,6 +94,7 @@ type multiNode[ leaseDuration time.Duration leaseTicker *time.Ticker chainFamily string + reportInterval time.Duration activeMu sync.RWMutex activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] @@ -148,23 +130,13 @@ func NewMultiNode[ chainFamily string, sendOnlyErrorParser func(err error) SendTxReturnCode, ) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { - nodeSelector := func() NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] { - switch selectionMode { - case NodeSelectionModeHighestHead: - return NewHighestHeadNodeSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) - case NodeSelectionModeRoundRobin: - return NewRoundRobinSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) - case NodeSelectionModeTotalDifficulty: - return NewTotalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) - case NodeSelectionModePriorityLevel: - return NewPriorityLevelNodeSelector[CHAIN_ID, HEAD, RPC_CLIENT](nodes) - default: - panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode)) - } - }() + nodeSelector := newNodeSelector(selectionMode, nodes) lggr := logger.Named("MultiNode").With("chainID", chainID.String()) + // Prometheus' default interval is 15s, set this to under 7.5s to avoid + // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) + const reportInterval = 6500 * time.Millisecond c := &multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]{ nodes: nodes, sendonlys: sendonlys, @@ -178,6 +150,7 @@ func NewMultiNode[ leaseDuration: leaseDuration, chainFamily: chainFamily, sendOnlyErrorParser: sendOnlyErrorParser, + reportInterval: reportInterval, } c.logger.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) @@ -341,10 +314,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.report() - // Prometheus' default interval is 15s, set this to under 7.5s to avoid - // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) - reportInterval := 6500 * time.Millisecond - monitor := time.NewTicker(utils.WithJitter(reportInterval)) + monitor := time.NewTicker(utils.WithJitter(c.reportInterval)) defer monitor.Stop() for { diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go new file mode 100644 index 00000000000..1fddbc3be3c --- /dev/null +++ b/common/client/multi_node_test.go @@ -0,0 +1,635 @@ +package client + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type multiNodeRPCClient RPC[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] + +type testMultiNode struct { + *multiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient] +} + +type multiNodeOpts struct { + logger logger.Logger + selectionMode string + leaseDuration time.Duration + noNewHeadsThreshold time.Duration + nodes []Node[types.ID, types.Head[Hashable], multiNodeRPCClient] + sendonlys []SendOnlyNode[types.ID, multiNodeRPCClient] + chainID types.ID + chainType config.ChainType + chainFamily string + sendOnlyErrorParser func(err error) SendTxReturnCode +} + +func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { + if opts.logger == nil { + opts.logger = logger.TestLogger(t) + } + + result := NewMultiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient](opts.logger, + opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, + opts.chainID, opts.chainType, opts.chainFamily, opts.sendOnlyErrorParser) + return testMultiNode{ + result.(*multiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient]), + } +} + +func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] { + return newMockRPC[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]](t) +} + +func newHealthyNode(t *testing.T, chainID types.ID) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { + return newNodeWithState(t, chainID, nodeStateAlive) +} + +func newNodeWithState(t *testing.T, chainID types.ID, state nodeState) *mockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] { + node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node.On("ConfiguredChainID").Return(chainID).Once() + node.On("Start", mock.Anything).Return(nil).Once() + node.On("Close").Return(nil).Once() + node.On("State").Return(state).Maybe() + node.On("String").Return(fmt.Sprintf("healthy_node_%d", rand.Int())).Maybe() + return node +} +func TestMultiNode_Dial(t *testing.T) { + t.Parallel() + + newMockNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient] + newMockSendOnlyNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient] + + t.Run("Fails without nodes", func(t *testing.T) { + t.Parallel() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: types.RandomID(), + }) + err := mn.Dial(tests.Context(t)) + assert.EqualError(t, err, fmt.Sprintf("no available nodes for chain %s", mn.chainID.String())) + }) + t.Run("Fails with wrong node's chainID", func(t *testing.T) { + t.Parallel() + node := newMockNode(t) + multiNodeChainID := types.NewIDFromInt(10) + nodeChainID := types.NewIDFromInt(11) + node.On("ConfiguredChainID").Return(nodeChainID).Twice() + const nodeName = "nodeName" + node.On("String").Return(nodeName).Once() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: multiNodeChainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + }) + err := mn.Dial(tests.Context(t)) + assert.EqualError(t, err, fmt.Sprintf("node %s has configured chain ID %s which does not match multinode configured chain ID of %s", nodeName, nodeChainID, mn.chainID)) + }) + t.Run("Fails if node fails", func(t *testing.T) { + t.Parallel() + node := newMockNode(t) + chainID := types.RandomID() + node.On("ConfiguredChainID").Return(chainID).Once() + expectedError := errors.New("failed to start node") + node.On("Start", mock.Anything).Return(expectedError).Once() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + }) + err := mn.Dial(tests.Context(t)) + assert.EqualError(t, err, expectedError.Error()) + }) + + t.Run("Closes started nodes on failure", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node1 := newHealthyNode(t, chainID) + node2 := newMockNode(t) + node2.On("ConfiguredChainID").Return(chainID).Once() + expectedError := errors.New("failed to start node") + node2.On("Start", mock.Anything).Return(expectedError).Once() + + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + }) + err := mn.Dial(tests.Context(t)) + assert.EqualError(t, err, expectedError.Error()) + }) + t.Run("Fails with wrong send only node's chainID", func(t *testing.T) { + t.Parallel() + multiNodeChainID := types.NewIDFromInt(10) + node := newHealthyNode(t, multiNodeChainID) + sendOnly := newMockSendOnlyNode(t) + sendOnlyChainID := types.NewIDFromInt(11) + sendOnly.On("ConfiguredChainID").Return(sendOnlyChainID).Twice() + const sendOnlyName = "sendOnlyNodeName" + sendOnly.On("String").Return(sendOnlyName).Once() + + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: multiNodeChainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly}, + }) + err := mn.Dial(tests.Context(t)) + assert.EqualError(t, err, fmt.Sprintf("sendonly node %s has configured chain ID %s which does not match multinode configured chain ID of %s", sendOnlyName, sendOnlyChainID, mn.chainID)) + }) + + newHealthySendOnly := func(t *testing.T, chainID types.ID) *mockSendOnlyNode[types.ID, multiNodeRPCClient] { + node := newMockSendOnlyNode(t) + node.On("ConfiguredChainID").Return(chainID).Once() + node.On("Start", mock.Anything).Return(nil).Once() + node.On("Close").Return(nil).Once() + return node + } + t.Run("Fails on send only node failure", func(t *testing.T) { + t.Parallel() + chainID := types.NewIDFromInt(10) + node := newHealthyNode(t, chainID) + sendOnly1 := newHealthySendOnly(t, chainID) + sendOnly2 := newMockSendOnlyNode(t) + sendOnly2.On("ConfiguredChainID").Return(chainID).Once() + expectedError := errors.New("failed to start send only node") + sendOnly2.On("Start", mock.Anything).Return(expectedError).Once() + + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{sendOnly1, sendOnly2}, + }) + err := mn.Dial(tests.Context(t)) + assert.EqualError(t, err, expectedError.Error()) + }) + t.Run("Starts successfully with healthy nodes", func(t *testing.T) { + t.Parallel() + chainID := types.NewIDFromInt(10) + node := newHealthyNode(t, chainID) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{newHealthySendOnly(t, chainID)}, + }) + defer func() { assert.NoError(t, mn.Close()) }() + err := mn.Dial(tests.Context(t)) + require.NoError(t, err) + selectedNode, err := mn.selectNode() + require.NoError(t, err) + assert.Equal(t, node, selectedNode) + }) +} + +func TestMultiNode_Report(t *testing.T) { + t.Parallel() + t.Run("Dial starts periodical reporting", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node1 := newHealthyNode(t, chainID) + node2 := newNodeWithState(t, chainID, nodeStateOutOfSync) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + logger: lggr, + }) + mn.reportInterval = tests.TestInterval + defer func() { assert.NoError(t, mn.Close()) }() + err := mn.Dial(tests.Context(t)) + require.NoError(t, err) + tests.AssertLogCountEventually(t, observedLogs, "At least one primary node is dead: 1/2 nodes are alive", 2) + }) + t.Run("Report critical error on all node failure", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node := newNodeWithState(t, chainID, nodeStateOutOfSync) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + logger: lggr, + }) + mn.reportInterval = tests.TestInterval + defer func() { assert.NoError(t, mn.Close()) }() + err := mn.Dial(tests.Context(t)) + require.NoError(t, err) + tests.AssertLogCountEventually(t, observedLogs, "no primary nodes available: 0/1 nodes are alive", 2) + err = mn.Healthy() + require.Error(t, err) + assert.Contains(t, err.Error(), "no primary nodes available: 0/1 nodes are alive") + }) +} + +func TestMultiNode_CheckLease(t *testing.T) { + t.Parallel() + t.Run("Round robin disables lease check", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node := newHealthyNode(t, chainID) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + logger: lggr, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + }) + defer func() { assert.NoError(t, mn.Close()) }() + err := mn.Dial(tests.Context(t)) + require.NoError(t, err) + tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled") + }) + t.Run("Misconfigured lease check period won't start", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node := newHealthyNode(t, chainID) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeHighestHead, + chainID: chainID, + logger: lggr, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node}, + leaseDuration: 0, + }) + defer func() { assert.NoError(t, mn.Close()) }() + err := mn.Dial(tests.Context(t)) + require.NoError(t, err) + tests.RequireLogMessage(t, observedLogs, "Best node switching is disabled") + }) + t.Run("Lease check updates active node", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node := newHealthyNode(t, chainID) + node.On("SubscribersCount").Return(int32(2)) + node.On("UnsubscribeAllExceptAliveLoop") + bestNode := newHealthyNode(t, chainID) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(bestNode) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeHighestHead, + chainID: chainID, + logger: lggr, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node, bestNode}, + leaseDuration: tests.TestInterval, + }) + defer func() { assert.NoError(t, mn.Close()) }() + mn.nodeSelector = nodeSelector + err := mn.Dial(tests.Context(t)) + require.NoError(t, err) + tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("Switching to best node from %q to %q", node.String(), bestNode.String())) + tests.AssertEventually(t, func() bool { + mn.activeMu.RLock() + active := mn.activeNode + mn.activeMu.RUnlock() + return bestNode == active + }) + }) + t.Run("NodeStates returns proper states", func(t *testing.T) { + t.Parallel() + chainID := types.NewIDFromInt(10) + nodes := map[string]nodeState{ + "node_1": nodeStateAlive, + "node_2": nodeStateUnreachable, + "node_3": nodeStateDialed, + } + + opts := multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + } + + expectedResult := map[string]string{} + for name, state := range nodes { + node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node.On("Name").Return(name).Once() + node.On("State").Return(state).Once() + opts.nodes = append(opts.nodes, node) + + sendOnly := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) + sendOnlyName := "send_only_" + name + sendOnly.On("Name").Return(sendOnlyName).Once() + sendOnly.On("State").Return(state).Once() + opts.sendonlys = append(opts.sendonlys, sendOnly) + + expectedResult[name] = state.String() + expectedResult[sendOnlyName] = state.String() + } + + mn := newTestMultiNode(t, opts) + states := mn.NodeStates() + assert.Equal(t, expectedResult, states) + }) +} + +func TestMultiNode_selectNode(t *testing.T) { + t.Parallel() + t.Run("Returns same node, if it's still healthy", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + node1 := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node1.On("State").Return(nodeStateAlive).Once() + node1.On("String").Return("node1").Maybe() + node2 := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node2.On("String").Return("node2").Maybe() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{node1, node2}, + }) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(node1).Once() + mn.nodeSelector = nodeSelector + prevActiveNode, err := mn.selectNode() + require.NoError(t, err) + require.Equal(t, node1.String(), prevActiveNode.String()) + newActiveNode, err := mn.selectNode() + require.NoError(t, err) + require.Equal(t, prevActiveNode.String(), newActiveNode.String()) + + }) + t.Run("Updates node if active is not healthy", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + oldBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + oldBest.On("String").Return("oldBest").Maybe() + newBest := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + newBest.On("String").Return("newBest").Maybe() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{oldBest, newBest}, + }) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(oldBest).Once() + mn.nodeSelector = nodeSelector + activeNode, err := mn.selectNode() + require.NoError(t, err) + require.Equal(t, oldBest.String(), activeNode.String()) + // old best died, so we should replace it + oldBest.On("State").Return(nodeStateOutOfSync).Twice() + nodeSelector.On("Select").Return(newBest).Once() + newActiveNode, err := mn.selectNode() + require.NoError(t, err) + require.Equal(t, newBest.String(), newActiveNode.String()) + + }) + t.Run("No active nodes - reports critical error", func(t *testing.T) { + t.Parallel() + chainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + logger: lggr, + }) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(nil).Once() + nodeSelector.On("Name").Return("MockedNodeSelector").Once() + mn.nodeSelector = nodeSelector + node, err := mn.selectNode() + require.EqualError(t, err, ErroringNodeError.Error()) + require.Nil(t, node) + tests.RequireLogMessage(t, observedLogs, "No live RPC nodes available") + + }) +} + +func TestMultiNode_nLiveNodes(t *testing.T) { + t.Parallel() + type nodeParams struct { + BlockNumber int64 + TotalDifficulty *utils.Big + State nodeState + } + testCases := []struct { + Name string + ExpectedNLiveNodes int + ExpectedBlockNumber int64 + ExpectedTotalDifficulty *utils.Big + NodeParams []nodeParams + }{ + { + Name: "no nodes", + ExpectedTotalDifficulty: utils.NewBigI(0), + }, + { + Name: "Best node is not healthy", + ExpectedTotalDifficulty: utils.NewBigI(10), + ExpectedBlockNumber: 20, + ExpectedNLiveNodes: 3, + NodeParams: []nodeParams{ + { + State: nodeStateOutOfSync, + BlockNumber: 1000, + TotalDifficulty: utils.NewBigI(2000), + }, + { + State: nodeStateAlive, + BlockNumber: 20, + TotalDifficulty: utils.NewBigI(9), + }, + { + State: nodeStateAlive, + BlockNumber: 19, + TotalDifficulty: utils.NewBigI(10), + }, + { + State: nodeStateAlive, + BlockNumber: 11, + TotalDifficulty: nil, + }, + }, + }, + } + + chainID := types.RandomID() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) + for i := range testCases { + tc := testCases[i] + t.Run(tc.Name, func(t *testing.T) { + for _, params := range tc.NodeParams { + node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node.On("StateAndLatest").Return(params.State, params.BlockNumber, params.TotalDifficulty) + mn.nodes = append(mn.nodes, node) + } + + nNodes, blockNum, td := mn.nLiveNodes() + assert.Equal(t, tc.ExpectedNLiveNodes, nNodes) + assert.Equal(t, tc.ExpectedTotalDifficulty, td) + assert.Equal(t, tc.ExpectedBlockNumber, blockNum) + }) + } +} + +func TestMultiNode_BatchCallContextAll(t *testing.T) { + t.Parallel() + t.Run("Fails if failed to select active node", func(t *testing.T) { + chainID := types.RandomID() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(nil).Once() + nodeSelector.On("Name").Return("MockedNodeSelector").Once() + mn.nodeSelector = nodeSelector + err := mn.BatchCallContextAll(tests.Context(t), nil) + require.EqualError(t, err, ErroringNodeError.Error()) + }) + t.Run("Returns error if RPC call fails for active node", func(t *testing.T) { + chainID := types.RandomID() + rpc := newMultiNodeRPCClient(t) + expectedError := errors.New("rpc failed to do the batch call") + rpc.On("BatchCallContext", mock.Anything, mock.Anything).Return(expectedError).Once() + node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node.On("RPC").Return(rpc) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(node).Once() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) + mn.nodeSelector = nodeSelector + err := mn.BatchCallContextAll(tests.Context(t), nil) + require.EqualError(t, err, expectedError.Error()) + }) + t.Run("Waits for all nodes to complete the call and logs results", func(t *testing.T) { + // setup RPCs + failedRPC := newMultiNodeRPCClient(t) + failedRPC.On("BatchCallContext", mock.Anything, mock.Anything). + Return(errors.New("rpc failed to do the batch call")).Once() + okRPC := newMultiNodeRPCClient(t) + okRPC.On("BatchCallContext", mock.Anything, mock.Anything).Return(nil).Twice() + + // setup ok and failed auxiliary nodes + okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) + okNode.On("RPC").Return(okRPC).Once() + failedNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + failedNode.On("RPC").Return(failedRPC).Once() + + // setup main node + mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + mainNode.On("RPC").Return(okRPC) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(mainNode).Once() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: types.RandomID(), + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{failedNode, mainNode}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{okNode}, + logger: lggr, + }) + mn.nodeSelector = nodeSelector + + err := mn.BatchCallContextAll(tests.Context(t), nil) + require.NoError(t, err) + tests.RequireLogMessage(t, observedLogs, "Secondary node BatchCallContext failed") + }) +} + +func TestMultiNode_SendTransaction(t *testing.T) { + t.Parallel() + t.Run("Fails if failed to select active node", func(t *testing.T) { + chainID := types.RandomID() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(nil).Once() + nodeSelector.On("Name").Return("MockedNodeSelector").Once() + mn.nodeSelector = nodeSelector + err := mn.SendTransaction(tests.Context(t), nil) + require.EqualError(t, err, ErroringNodeError.Error()) + }) + t.Run("Returns error if RPC call fails for active node", func(t *testing.T) { + chainID := types.RandomID() + rpc := newMultiNodeRPCClient(t) + expectedError := errors.New("rpc failed to do the batch call") + rpc.On("SendTransaction", mock.Anything, mock.Anything).Return(expectedError).Once() + node := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + node.On("RPC").Return(rpc) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(node).Once() + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: chainID, + }) + mn.nodeSelector = nodeSelector + err := mn.SendTransaction(tests.Context(t), nil) + require.EqualError(t, err, expectedError.Error()) + }) + t.Run("Returns result of main node and logs secondary nodes results", func(t *testing.T) { + // setup RPCs + failedRPC := newMultiNodeRPCClient(t) + failedRPC.On("SendTransaction", mock.Anything, mock.Anything). + Return(errors.New("rpc failed to do the batch call")).Once() + okRPC := newMultiNodeRPCClient(t) + okRPC.On("SendTransaction", mock.Anything, mock.Anything).Return(nil).Twice() + + // setup ok and failed auxiliary nodes + okNode := newMockSendOnlyNode[types.ID, multiNodeRPCClient](t) + okNode.On("RPC").Return(okRPC).Once() + okNode.On("String").Return("okNode") + failedNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + failedNode.On("RPC").Return(failedRPC).Once() + failedNode.On("String").Return("failedNode") + + // setup main node + mainNode := newMockNode[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + mainNode.On("RPC").Return(okRPC) + nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) + nodeSelector.On("Select").Return(mainNode).Once() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + mn := newTestMultiNode(t, multiNodeOpts{ + selectionMode: NodeSelectionModeRoundRobin, + chainID: types.RandomID(), + nodes: []Node[types.ID, types.Head[Hashable], multiNodeRPCClient]{failedNode, mainNode}, + sendonlys: []SendOnlyNode[types.ID, multiNodeRPCClient]{okNode}, + logger: lggr, + sendOnlyErrorParser: func(err error) SendTxReturnCode { + if err != nil { + return Fatal + } + + return Successful + }, + }) + mn.nodeSelector = nodeSelector + + err := mn.SendTransaction(tests.Context(t), nil) + require.NoError(t, err) + tests.AssertLogEventually(t, observedLogs, "Sendonly node sent transaction") + tests.AssertLogEventually(t, observedLogs, "RPC returned error") + }) +} diff --git a/common/client/node.go b/common/client/node.go index 20d098e03f7..f28a171a558 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -44,6 +44,7 @@ type NodeConfig interface { SyncThreshold() uint32 } +//go:generate mockery --quiet --name Node --structname mockNode --filename "mock_node_test.go" --inpackage --case=underscore type Node[ CHAIN_ID types.ID, HEAD Head, @@ -177,19 +178,21 @@ func (n *node[CHAIN_ID, HEAD, RPC]) UnsubscribeAllExceptAliveLoop() { } func (n *node[CHAIN_ID, HEAD, RPC]) Close() error { - return n.StopOnce(n.name, func() error { - defer func() { - n.wg.Wait() - n.rpc.Close() - }() + return n.StopOnce(n.name, n.close) +} - n.stateMu.Lock() - defer n.stateMu.Unlock() +func (n *node[CHAIN_ID, HEAD, RPC]) close() error { + defer func() { + n.wg.Wait() + n.rpc.Close() + }() - n.cancelNodeCtx() - n.state = nodeStateClosed - return nil - }) + n.stateMu.Lock() + defer n.stateMu.Unlock() + + n.cancelNodeCtx() + n.state = nodeStateClosed + return nil } // Start dials and verifies the node diff --git a/common/client/node_fsm_test.go b/common/client/node_fsm_test.go new file mode 100644 index 00000000000..87e90846699 --- /dev/null +++ b/common/client/node_fsm_test.go @@ -0,0 +1,108 @@ +package client + +import ( + "slices" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +type fnMock struct{ calls int } + +func (fm *fnMock) Fn() { + fm.calls++ +} + +func (fm *fnMock) AssertNotCalled(t *testing.T) { + assert.Equal(t, 0, fm.calls) +} + +func (fm *fnMock) AssertCalled(t *testing.T) { + assert.Greater(t, fm.calls, 0) +} + +func newTestTransitionNode(t *testing.T, rpc *mockNodeClient[types.ID, Head]) testNode { + return newTestNode(t, testNodeOpts{rpc: rpc}) +} + +func TestUnit_Node_StateTransitions(t *testing.T) { + t.Parallel() + + t.Run("setState", func(t *testing.T) { + n := newTestTransitionNode(t, nil) + assert.Equal(t, nodeStateUndialed, n.State()) + n.setState(nodeStateAlive) + assert.Equal(t, nodeStateAlive, n.State()) + n.setState(nodeStateUndialed) + assert.Equal(t, nodeStateUndialed, n.State()) + }) + + t.Run("transitionToAlive", func(t *testing.T) { + const destinationState = nodeStateAlive + allowedStates := []nodeState{nodeStateDialed, nodeStateInvalidChainID} + rpc := newMockNodeClient[types.ID, Head](t) + testTransition(t, rpc, testNode.transitionToAlive, destinationState, allowedStates...) + }) + + t.Run("transitionToInSync", func(t *testing.T) { + const destinationState = nodeStateAlive + allowedStates := []nodeState{nodeStateOutOfSync} + rpc := newMockNodeClient[types.ID, Head](t) + testTransition(t, rpc, testNode.transitionToInSync, destinationState, allowedStates...) + }) + t.Run("transitionToOutOfSync", func(t *testing.T) { + const destinationState = nodeStateOutOfSync + allowedStates := []nodeState{nodeStateAlive} + rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("DisconnectAll").Once() + testTransition(t, rpc, testNode.transitionToOutOfSync, destinationState, allowedStates...) + }) + t.Run("transitionToUnreachable", func(t *testing.T) { + const destinationState = nodeStateUnreachable + allowedStates := []nodeState{nodeStateUndialed, nodeStateDialed, nodeStateAlive, nodeStateOutOfSync, nodeStateInvalidChainID} + rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("DisconnectAll").Times(len(allowedStates)) + testTransition(t, rpc, testNode.transitionToUnreachable, destinationState, allowedStates...) + }) + t.Run("transitionToInvalidChain", func(t *testing.T) { + const destinationState = nodeStateInvalidChainID + allowedStates := []nodeState{nodeStateDialed, nodeStateOutOfSync} + rpc := newMockNodeClient[types.ID, Head](t) + rpc.On("DisconnectAll").Times(len(allowedStates)) + testTransition(t, rpc, testNode.transitionToInvalidChainID, destinationState, allowedStates...) + }) +} + +func testTransition(t *testing.T, rpc *mockNodeClient[types.ID, Head], transition func(node testNode, fn func()), destinationState nodeState, allowedStates ...nodeState) { + node := newTestTransitionNode(t, rpc) + for _, allowedState := range allowedStates { + m := new(fnMock) + node.setState(allowedState) + transition(node, m.Fn) + assert.Equal(t, destinationState, node.State(), "Expected node to successfully transition from %s to %s state", allowedState, destinationState) + m.AssertCalled(t) + } + // noop on attempt to transition from Closed state + m := new(fnMock) + node.setState(nodeStateClosed) + transition(node, m.Fn) + m.AssertNotCalled(t) + assert.Equal(t, nodeStateClosed, node.State(), "Expected node to remain in closed state on transition attempt") + + for _, nodeState := range allNodeStates { + if slices.Contains(allowedStates, nodeState) || nodeState == nodeStateClosed { + continue + } + + m := new(fnMock) + node.setState(nodeState) + assert.Panics(t, func() { + transition(node, m.Fn) + }, "Expected transition from `%s` to `%s` to panic", nodeState, destinationState) + m.AssertNotCalled(t) + assert.Equal(t, nodeState, node.State(), "Expected node to remain in initial state on invalid transition") + + } +} diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 149c5f01a6d..4193560e296 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -60,6 +60,8 @@ const ( msgDegradedState = "Chainlink is now operating in a degraded state and urgent action is required to resolve the issue" ) +const rpcSubscriptionMethodNewHeads = "newHeads" + // Node is a FSM // Each state has a loop that goes with it, which monitors the node and moves it into another state as necessary. // Only one loop must run at a time. @@ -90,12 +92,14 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Tracew("Alive loop starting", "nodeState", n.State()) headsC := make(chan HEAD) - sub, err := n.rpc.Subscribe(n.nodeCtx, headsC, "newHeads") + sub, err := n.rpc.Subscribe(n.nodeCtx, headsC, rpcSubscriptionMethodNewHeads) if err != nil { lggr.Errorw("Initial subscribe for heads failed", "nodeState", n.State()) n.declareUnreachable() return } + // TODO: nit fix. If multinode switches primary node before we set sub as AliveSub, sub will be closed and we'll + // falsely transition this node to unreachable state n.rpc.SetAliveLoopSub(sub) defer sub.Unsubscribe() @@ -289,7 +293,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) ch := make(chan HEAD) - sub, err := n.rpc.Subscribe(n.nodeCtx, ch, "newHeads") + sub, err := n.rpc.Subscribe(n.nodeCtx, ch, rpcSubscriptionMethodNewHeads) if err != nil { lggr.Errorw("Failed to subscribe heads on out-of-sync RPC node", "nodeState", n.State(), "err", err) n.declareUnreachable() @@ -310,11 +314,11 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td n.setLatestReceived(head.BlockNumber(), head.BlockDifficulty()) if !isOutOfSync(head.BlockNumber(), head.BlockDifficulty()) { // back in-sync! flip back into alive loop - lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "totalDifficulty", "nodeState", n.State()) + lggr.Infow(fmt.Sprintf("%s: %s. Node was out-of-sync for %s", msgInSync, n.String(), time.Since(outOfSyncAt)), "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.State()) n.declareInSync() return } - lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "totalDifficulty", "nodeState", n.State()) + lggr.Debugw(msgReceivedBlock, "blockNumber", head.BlockNumber(), "blockDifficulty", head.BlockDifficulty(), "nodeState", n.State()) case <-time.After(zombieNodeCheckInterval(n.noNewHeadsThreshold)): if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 1 { diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go new file mode 100644 index 00000000000..564c08bbdcc --- /dev/null +++ b/common/client/node_lifecycle_test.go @@ -0,0 +1,1070 @@ +package client + +import ( + "fmt" + "sync/atomic" + "testing" + + "github.com/cometbft/cometbft/libs/rand" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "go.uber.org/zap" + + "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/common/types/mocks" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { + t.Parallel() + + newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { + node := newTestNode(t, opts) + opts.rpc.On("Close").Return(nil).Once() + + node.setState(nodeStateDialed) + return node + } + + t.Run("returns on closed", func(t *testing.T) { + node := newTestNode(t, testNodeOpts{}) + node.setState(nodeStateClosed) + node.wg.Add(1) + node.aliveLoop() + + }) + t.Run("if initial subscribe fails, transitions to unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + + expectedError := errors.New("failed to subscribe to rpc") + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError).Once() + rpc.On("DisconnectAll").Once() + // might be called in unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareAlive() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + + }) + t.Run("if remote RPC connection is closed transitions to unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + sub := mocks.NewSubscription(t) + errChan := make(chan error) + close(errChan) + sub.On("Err").Return((<-chan error)(errChan)).Once() + sub.On("Unsubscribe").Once() + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("SetAliveLoopSub", sub).Once() + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll").Once() + // might be called in unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, "Subscription was terminated") + assert.Equal(t, nodeStateUnreachable, node.State()) + }) + + newSubscribedNode := func(t *testing.T, opts testNodeOpts) testNode { + sub := mocks.NewSubscription(t) + sub.On("Err").Return((<-chan error)(nil)) + sub.On("Unsubscribe").Once() + opts.rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + opts.rpc.On("SetAliveLoopSub", sub).Once() + return newDialedNode(t, opts) + } + t.Run("Stays alive and waits for signal", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{}, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, "Head liveness checking disabled") + tests.AssertLogEventually(t, observedLogs, "Polling disabled") + assert.Equal(t, nodeStateAlive, node.State()) + }) + t.Run("stays alive while below pollFailureThreshold and resets counter on success", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + const pollFailureThreshold = 3 + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{ + pollFailureThreshold: pollFailureThreshold, + pollInterval: tests.TestInterval, + }, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + pollError := errors.New("failed to get ClientVersion") + // 1. Return error several times, but below threshold + rpc.On("ClientVersion", mock.Anything).Return("", pollError).Run(func(_ mock.Arguments) { + // stays healthy while below threshold + assert.Equal(t, nodeStateAlive, node.State()) + }).Times(pollFailureThreshold - 1) + // 2. Successful call that is expected to reset counter + rpc.On("ClientVersion", mock.Anything).Return("client_version", nil).Once() + // 3. Return error. If we have not reset the timer, we'll transition to nonAliveState + rpc.On("ClientVersion", mock.Anything).Return("", pollError).Once() + // 4. Once during the call, check if node is alive + var ensuredAlive atomic.Bool + rpc.On("ClientVersion", mock.Anything).Return("client_version", nil).Run(func(_ mock.Arguments) { + if ensuredAlive.Load() { + return + } + ensuredAlive.Store(true) + assert.Equal(t, nodeStateAlive, node.State()) + }).Once() + // redundant call to stay in alive state + rpc.On("ClientVersion", mock.Anything).Return("client_version", nil) + node.declareAlive() + tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold) + tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) + assert.True(t, ensuredAlive.Load(), "expected to ensure that node was alive") + + }) + t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + const pollFailureThreshold = 3 + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{ + pollFailureThreshold: pollFailureThreshold, + pollInterval: tests.TestInterval, + }, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + pollError := errors.New("failed to get ClientVersion") + rpc.On("ClientVersion", mock.Anything).Return("", pollError) + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll").Once() + // might be called in unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareAlive() + tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Poll failure, RPC endpoint %s failed to respond properly", node.String()), pollFailureThreshold) + tests.AssertEventually(t, func() bool { + return nodeStateUnreachable == node.State() + }) + }) + t.Run("with threshold poll failures, but we are the last node alive, forcibly keeps it alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + const pollFailureThreshold = 3 + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{ + pollFailureThreshold: pollFailureThreshold, + pollInterval: tests.TestInterval, + }, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return 1, 20, utils.NewBigI(10) + } + pollError := errors.New("failed to get ClientVersion") + rpc.On("ClientVersion", mock.Anything).Return("", pollError) + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailureThreshold)) + assert.Equal(t, nodeStateAlive, node.State()) + }) + t.Run("when behind more than SyncThreshold, transitions to out of sync", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + const syncThreshold = 10 + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{ + pollInterval: tests.TestInterval, + syncThreshold: syncThreshold, + selectionMode: NodeSelectionModeRoundRobin, + }, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + node.stateLatestBlockNumber = 20 + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return 10, syncThreshold + node.stateLatestBlockNumber + 1, utils.NewBigI(10) + } + rpc.On("ClientVersion", mock.Anything).Return("", nil) + // tries to redial in outOfSync + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { + assert.Equal(t, nodeStateOutOfSync, node.State()) + }).Once() + // disconnects all on transfer to unreachable or outOfSync + rpc.On("DisconnectAll").Maybe() + // might be called in unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, "Failed to dial out-of-sync RPC node") + }) + t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + const syncThreshold = 10 + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{ + pollInterval: tests.TestInterval, + syncThreshold: syncThreshold, + selectionMode: NodeSelectionModeRoundRobin, + }, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + node.stateLatestBlockNumber = 20 + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return 1, syncThreshold + node.stateLatestBlockNumber + 1, utils.NewBigI(10) + } + rpc.On("ClientVersion", mock.Anything).Return("", nil) + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState)) + }) + t.Run("when behind but SyncThreshold=0, stay alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{ + pollInterval: tests.TestInterval, + syncThreshold: 0, + selectionMode: NodeSelectionModeRoundRobin, + }, + rpc: rpc, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + node.stateLatestBlockNumber = 20 + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return 1, node.stateLatestBlockNumber + 100, utils.NewBigI(10) + } + rpc.On("ClientVersion", mock.Anything).Return("", nil) + node.declareAlive() + tests.AssertLogCountEventually(t, observedLogs, "Version poll successful", 2) + assert.Equal(t, nodeStateAlive, node.State()) + }) + + t.Run("when no new heads received for threshold, transitions to out of sync", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{}, + noNewHeadsThreshold: tests.TestInterval, + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + // tries to redial in outOfSync + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Run(func(_ mock.Arguments) { + assert.Equal(t, nodeStateOutOfSync, node.State()) + }).Once() + // disconnects all on transfer to unreachable or outOfSync + rpc.On("DisconnectAll").Maybe() + // might be called in unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareAlive() + tests.AssertEventually(t, func() bool { + // right after outOfSync we'll transfer to unreachable due to returned error on Dial + // we check that we were in out of sync state on first Dial call + return node.State() == nodeStateUnreachable + }) + }) + t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newSubscribedNode(t, testNodeOpts{ + config: testNodeConfig{}, + lggr: lggr, + noNewHeadsThreshold: tests.TestInterval, + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return 1, 20, utils.NewBigI(10) + } + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState)) + assert.Equal(t, nodeStateAlive, node.State()) + }) + + t.Run("rpc closed head channel", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + sub := mocks.NewSubscription(t) + sub.On("Err").Return((<-chan error)(nil)) + sub.On("Unsubscribe").Once() + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + ch := args.Get(1).(chan<- Head) + close(ch) + }).Return(sub, nil).Once() + rpc.On("SetAliveLoopSub", sub).Once() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + node := newDialedNode(t, testNodeOpts{ + lggr: lggr, + config: testNodeConfig{}, + noNewHeadsThreshold: tests.TestInterval, + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + // disconnects all on transfer to unreachable or outOfSync + rpc.On("DisconnectAll").Once() + // might be called in unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareAlive() + tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed") + assert.Equal(t, nodeStateUnreachable, node.State()) + + }) + t.Run("updates block number and difficulty on new head", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + sub := mocks.NewSubscription(t) + sub.On("Err").Return((<-chan error)(nil)) + sub.On("Unsubscribe").Once() + expectedBlockNumber := rand.Int64() + expectedDiff := utils.NewBigI(rand.Int64()) + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + ch := args.Get(1).(chan<- Head) + go writeHeads(t, ch, head{BlockNumber: expectedBlockNumber, BlockDifficulty: expectedDiff}) + }).Return(sub, nil).Once() + rpc.On("SetAliveLoopSub", sub).Once() + node := newDialedNode(t, testNodeOpts{ + config: testNodeConfig{}, + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + node.declareAlive() + tests.AssertEventually(t, func() bool { + state, block, diff := node.StateAndLatest() + return state == nodeStateAlive && block == expectedBlockNumber == diff.Equal(expectedDiff) + }) + }) +} + +type head struct { + BlockNumber int64 + BlockDifficulty *utils.Big +} + +func writeHeads(t *testing.T, ch chan<- Head, heads ...head) { + for _, head := range heads { + h := newMockHead(t) + h.On("BlockNumber").Return(head.BlockNumber) + h.On("BlockDifficulty").Return(head.BlockDifficulty) + select { + case ch <- h: + case <-tests.Context(t).Done(): + return + } + } +} + +func setupRPCForAliveLoop(t *testing.T, rpc *mockNodeClient[types.ID, Head]) { + rpc.On("Dial", mock.Anything).Return(nil).Maybe() + aliveSubscription := mocks.NewSubscription(t) + aliveSubscription.On("Err").Return((<-chan error)(nil)).Maybe() + aliveSubscription.On("Unsubscribe").Maybe() + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(aliveSubscription, nil).Maybe() + rpc.On("SetAliveLoopSub", mock.Anything).Maybe() +} + +func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { + t.Parallel() + + newAliveNode := func(t *testing.T, opts testNodeOpts) testNode { + node := newTestNode(t, opts) + opts.rpc.On("Close").Return(nil).Once() + // disconnects all on transfer to unreachable or outOfSync + opts.rpc.On("DisconnectAll") + node.setState(nodeStateAlive) + return node + } + + stubIsOutOfSync := func(num int64, td *utils.Big) bool { + return false + } + + t.Run("returns on closed", func(t *testing.T) { + t.Parallel() + node := newTestNode(t, testNodeOpts{}) + node.setState(nodeStateClosed) + node.wg.Add(1) + node.outOfSyncLoop(stubIsOutOfSync) + }) + t.Run("on old blocks stays outOfSync and returns on close", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + + outOfSyncSubscription := mocks.NewSubscription(t) + outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) + outOfSyncSubscription.On("Unsubscribe").Once() + heads := []head{{BlockNumber: 7}, {BlockNumber: 11}, {BlockNumber: 13}} + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + ch := args.Get(1).(chan<- Head) + go writeHeads(t, ch, heads...) + }).Return(outOfSyncSubscription, nil).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() + + node.declareOutOfSync(func(num int64, td *utils.Big) bool { + return true + }) + tests.AssertLogCountEventually(t, observedLogs, msgReceivedBlock, len(heads)) + assert.Equal(t, nodeStateOutOfSync, node.State()) + }) + t.Run("if initial dial fails, transitions to unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + + expectedError := errors.New("failed to dial rpc") + // might be called again in unreachable loop, so no need to set once + rpc.On("Dial", mock.Anything).Return(expectedError) + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("if fail to get chainID, transitions to invalidChainID", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + expectedError := errors.New("failed to get chain ID") + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(types.NewIDFromInt(0), expectedError) + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateInvalidChainID + }) + }) + t.Run("if chainID does not match, transitions to invalidChainID", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateInvalidChainID + }) + }) + t.Run("if fails to subscribe, becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + expectedError := errors.New("failed to subscribe") + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(nil, expectedError) + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on subscription termination becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + + sub := mocks.NewSubscription(t) + errChan := make(chan error, 1) + errChan <- errors.New("subscription was terminate") + sub.On("Err").Return((<-chan error)(errChan)) + sub.On("Unsubscribe").Once() + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(sub, nil).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertLogEventually(t, observedLogs, "Subscription was terminated") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("becomes unreachable if head channel is closed", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + + sub := mocks.NewSubscription(t) + sub.On("Err").Return((<-chan error)(nil)) + sub.On("Unsubscribe").Once() + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + ch := args.Get(1).(chan<- Head) + close(ch) + }).Return(sub, nil).Once() + rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertLogEventually(t, observedLogs, "Subscription channel unexpectedly closed") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + + t.Run("becomes alive if it receives a newer head", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + + outOfSyncSubscription := mocks.NewSubscription(t) + outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) + outOfSyncSubscription.On("Unsubscribe").Once() + const highestBlock = 1000 + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { + ch := args.Get(1).(chan<- Head) + go writeHeads(t, ch, head{BlockNumber: highestBlock - 1}, head{BlockNumber: highestBlock}) + }).Return(outOfSyncSubscription, nil).Once() + + setupRPCForAliveLoop(t, rpc) + + node.declareOutOfSync(func(num int64, td *utils.Big) bool { + return num < highestBlock + }) + tests.AssertLogEventually(t, observedLogs, msgReceivedBlock) + tests.AssertLogEventually(t, observedLogs, msgInSync) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) + t.Run("becomes alive if there is no other nodes", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newAliveNode(t, testNodeOpts{ + noNewHeadsThreshold: tests.TestInterval, + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return 0, 100, utils.NewBigI(200) + } + + rpc.On("Dial", mock.Anything).Return(nil).Once() + // might be called multiple times + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil).Once() + + outOfSyncSubscription := mocks.NewSubscription(t) + outOfSyncSubscription.On("Err").Return((<-chan error)(nil)) + outOfSyncSubscription.On("Unsubscribe").Once() + rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Return(outOfSyncSubscription, nil).Once() + + setupRPCForAliveLoop(t, rpc) + + node.declareOutOfSync(stubIsOutOfSync) + tests.AssertLogEventually(t, observedLogs, "RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) +} + +func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { + t.Parallel() + + newAliveNode := func(t *testing.T, opts testNodeOpts) testNode { + node := newTestNode(t, opts) + opts.rpc.On("Close").Return(nil).Once() + // disconnects all on transfer to unreachable + opts.rpc.On("DisconnectAll") + + node.setState(nodeStateAlive) + return node + } + t.Run("returns on closed", func(t *testing.T) { + t.Parallel() + node := newTestNode(t, testNodeOpts{}) + node.setState(nodeStateClosed) + node.wg.Add(1) + node.unreachableLoop() + + }) + t.Run("on failed redial, keeps trying", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + node.declareUnreachable() + tests.AssertLogCountEventually(t, observedLogs, "Failed to redial RPC node; still unreachable", 2) + }) + t.Run("on failed chainID verification, keep trying", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { + assert.Equal(t, nodeStateDialed, node.State()) + }).Return(nodeChainID, errors.New("failed to get chain id")) + node.declareUnreachable() + tests.AssertLogCountEventually(t, observedLogs, "Failed to redial RPC node; verify failed", 2) + }) + t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareUnreachable() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateInvalidChainID + }) + }) + t.Run("on valid chain ID becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newAliveNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + + setupRPCForAliveLoop(t, rpc) + + node.declareUnreachable() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) +} + +func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { + t.Parallel() + newDialedNode := func(t *testing.T, opts testNodeOpts) testNode { + node := newTestNode(t, opts) + opts.rpc.On("Close").Return(nil).Once() + opts.rpc.On("DisconnectAll") + + node.setState(nodeStateDialed) + return node + } + t.Run("returns on closed", func(t *testing.T) { + t.Parallel() + node := newTestNode(t, testNodeOpts{}) + node.setState(nodeStateClosed) + node.wg.Add(1) + node.invalidChainIDLoop() + + }) + t.Run("on failed chainID call becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("ChainID", mock.Anything).Return(nodeChainID, errors.New("failed to get chain id")) + // for unreachable loop + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")).Maybe() + node.declareInvalidChainID() + tests.AssertLogEventually(t, observedLogs, "Unexpected error while verifying RPC node chain ID") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on chainID mismatch keeps trying", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + node.declareInvalidChainID() + tests.AssertLogCountEventually(t, observedLogs, "Failed to verify RPC node; remote endpoint returned the wrong chain ID", 2) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateInvalidChainID + }) + }) + t.Run("on valid chainID becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newDialedNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + + setupRPCForAliveLoop(t, rpc) + + node.declareInvalidChainID() + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) +} + +func TestUnit_NodeLifecycle_start(t *testing.T) { + t.Parallel() + + newNode := func(t *testing.T, opts testNodeOpts) testNode { + node := newTestNode(t, opts) + opts.rpc.On("Close").Return(nil).Once() + + return node + } + t.Run("if fails on initial dial, becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(errors.New("failed to dial")) + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll") + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertLogEventually(t, observedLogs, "Dial failed: Node is unreachable") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("if chainID verification fails, becomes unreachable", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + lggr: lggr, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Run(func(_ mock.Arguments) { + assert.Equal(t, nodeStateDialed, node.State()) + }).Return(nodeChainID, errors.New("failed to get chain id")) + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll") + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertLogEventually(t, observedLogs, "Verify failed") + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateUnreachable + }) + }) + t.Run("on chain ID mismatch transitions to invalidChainID", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.NewIDFromInt(10) + rpcChainID := types.NewIDFromInt(11) + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(rpcChainID, nil) + // disconnects all on transfer to unreachable + rpc.On("DisconnectAll") + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateInvalidChainID + }) + }) + t.Run("on valid chain ID becomes alive", func(t *testing.T) { + t.Parallel() + rpc := newMockNodeClient[types.ID, Head](t) + nodeChainID := types.RandomID() + node := newNode(t, testNodeOpts{ + rpc: rpc, + chainID: nodeChainID, + }) + defer func() { assert.NoError(t, node.close()) }() + + rpc.On("Dial", mock.Anything).Return(nil) + rpc.On("ChainID", mock.Anything).Return(nodeChainID, nil) + + setupRPCForAliveLoop(t, rpc) + + err := node.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertEventually(t, func() bool { + return node.State() == nodeStateAlive + }) + }) +} + +func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { + t.Parallel() + t.Run("skip if nLiveNodes is not configured", func(t *testing.T) { + node := newTestNode(t, testNodeOpts{}) + outOfSync, liveNodes := node.syncStatus(0, nil) + assert.Equal(t, false, outOfSync) + assert.Equal(t, 0, liveNodes) + }) + t.Run("skip if syncThreshold is not configured", func(t *testing.T) { + node := newTestNode(t, testNodeOpts{}) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return + } + outOfSync, liveNodes := node.syncStatus(0, nil) + assert.Equal(t, false, outOfSync) + assert.Equal(t, 0, liveNodes) + }) + t.Run("panics on invalid selection mode", func(t *testing.T) { + node := newTestNode(t, testNodeOpts{ + config: testNodeConfig{syncThreshold: 1}, + }) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + return + } + assert.Panics(t, func() { + _, _ = node.syncStatus(0, nil) + }) + }) + t.Run("block height selection mode", func(t *testing.T) { + const syncThreshold = 10 + const highestBlock = 1000 + const nodesNum = 20 + const totalDifficulty = 3000 + testCases := []struct { + name string + blockNumber int64 + outOfSync bool + }{ + { + name: "below threshold", + blockNumber: highestBlock - syncThreshold - 1, + outOfSync: true, + }, + { + name: "equal to threshold", + blockNumber: highestBlock - syncThreshold, + outOfSync: false, + }, + { + name: "equal to highest block", + blockNumber: highestBlock, + outOfSync: false, + }, + { + name: "higher than highest block", + blockNumber: highestBlock, + outOfSync: false, + }, + } + + for _, selectionMode := range []string{NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel} { + node := newTestNode(t, testNodeOpts{ + config: testNodeConfig{ + syncThreshold: syncThreshold, + selectionMode: selectionMode, + }, + }) + node.nLiveNodes = func() (int, int64, *utils.Big) { + return nodesNum, highestBlock, utils.NewBigI(totalDifficulty) + } + for _, td := range []int64{totalDifficulty - syncThreshold - 1, totalDifficulty - syncThreshold, totalDifficulty, totalDifficulty + 1} { + for _, testCase := range testCases { + t.Run(fmt.Sprintf("%s: selectionMode: %s: total difficulty: %d", testCase.name, selectionMode, td), func(t *testing.T) { + outOfSync, liveNodes := node.syncStatus(testCase.blockNumber, utils.NewBigI(td)) + assert.Equal(t, nodesNum, liveNodes) + assert.Equal(t, testCase.outOfSync, outOfSync) + }) + } + } + } + + }) + t.Run("total difficulty selection mode", func(t *testing.T) { + const syncThreshold = 10 + const highestBlock = 1000 + const nodesNum = 20 + const totalDifficulty = 3000 + testCases := []struct { + name string + totalDifficulty int64 + outOfSync bool + }{ + { + name: "below threshold", + totalDifficulty: totalDifficulty - syncThreshold - 1, + outOfSync: true, + }, + { + name: "equal to threshold", + totalDifficulty: totalDifficulty - syncThreshold, + outOfSync: false, + }, + { + name: "equal to highest block", + totalDifficulty: totalDifficulty, + outOfSync: false, + }, + { + name: "higher than highest block", + totalDifficulty: totalDifficulty, + outOfSync: false, + }, + } + + node := newTestNode(t, testNodeOpts{ + config: testNodeConfig{ + syncThreshold: syncThreshold, + selectionMode: NodeSelectionModeTotalDifficulty, + }, + }) + node.nLiveNodes = func() (int, int64, *utils.Big) { + return nodesNum, highestBlock, utils.NewBigI(totalDifficulty) + } + for _, hb := range []int64{highestBlock - syncThreshold - 1, highestBlock - syncThreshold, highestBlock, highestBlock + 1} { + for _, testCase := range testCases { + t.Run(fmt.Sprintf("%s: selectionMode: %s: highest block: %d", testCase.name, NodeSelectionModeTotalDifficulty, hb), func(t *testing.T) { + outOfSync, liveNodes := node.syncStatus(hb, utils.NewBigI(testCase.totalDifficulty)) + assert.Equal(t, nodesNum, liveNodes) + assert.Equal(t, testCase.outOfSync, outOfSync) + }) + } + } + + }) +} diff --git a/common/client/node_selector.go b/common/client/node_selector.go new file mode 100644 index 00000000000..45604ebe8d9 --- /dev/null +++ b/common/client/node_selector.go @@ -0,0 +1,46 @@ +package client + +import ( + "fmt" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +const ( + NodeSelectionModeHighestHead = "HighestHead" + NodeSelectionModeRoundRobin = "RoundRobin" + NodeSelectionModeTotalDifficulty = "TotalDifficulty" + NodeSelectionModePriorityLevel = "PriorityLevel" +) + +//go:generate mockery --quiet --name NodeSelector --structname mockNodeSelector --filename "mock_node_selector_test.go" --inpackage --case=underscore +type NodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +] interface { + // Select returns a Node, or nil if none can be selected. + // Implementation must be thread-safe. + Select() Node[CHAIN_ID, HEAD, RPC] + // Name returns the strategy name, e.g. "HighestHead" or "RoundRobin" + Name() string +} + +func newNodeSelector[ + CHAIN_ID types.ID, + HEAD Head, + RPC NodeClient[CHAIN_ID, HEAD], +](selectionMode string, nodes []Node[CHAIN_ID, HEAD, RPC]) NodeSelector[CHAIN_ID, HEAD, RPC] { + switch selectionMode { + case NodeSelectionModeHighestHead: + return NewHighestHeadNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + case NodeSelectionModeRoundRobin: + return NewRoundRobinSelector[CHAIN_ID, HEAD, RPC](nodes) + case NodeSelectionModeTotalDifficulty: + return NewTotalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + case NodeSelectionModePriorityLevel: + return NewPriorityLevelNodeSelector[CHAIN_ID, HEAD, RPC](nodes) + default: + panic(fmt.Sprintf("unsupported NodeSelectionMode: %s", selectionMode)) + } +} diff --git a/common/client/node_selector_highest_head_test.go b/common/client/node_selector_highest_head_test.go new file mode 100644 index 00000000000..6e47bbedcae --- /dev/null +++ b/common/client/node_selector_highest_head_test.go @@ -0,0 +1,176 @@ +package client + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +func TestHighestHeadNodeSelectorName(t *testing.T) { + selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeHighestHead, nil) + assert.Equal(t, selector.Name(), NodeSelectionModeHighestHead) +} + +func TestHighestHeadNodeSelector(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + } else if i == 1 { + // second node is alive, LatestReceivedBlockNumber = 1 + node.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + } else { + // third node is alive, LatestReceivedBlockNumber = 2 (best node) + node.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + } + node.On("Order").Maybe().Return(int32(1)) + nodes = append(nodes, node) + } + + selector := newNodeSelector[types.ID, Head, nodeClient](NodeSelectionModeHighestHead, nodes) + assert.Same(t, nodes[2], selector.Select()) + + t.Run("stick to the same node", func(t *testing.T) { + node := newMockNode[types.ID, Head, nodeClient](t) + // fourth node is alive, LatestReceivedBlockNumber = 2 (same as 3rd) + node.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node.On("Order").Return(int32(1)) + nodes = append(nodes, node) + + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + assert.Same(t, nodes[2], selector.Select()) + }) + + t.Run("another best node", func(t *testing.T) { + node := newMockNode[types.ID, Head, nodeClient](t) + // fifth node is alive, LatestReceivedBlockNumber = 3 (better than 3rd and 4th) + node.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node.On("Order").Return(int32(1)) + nodes = append(nodes, node) + + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + assert.Same(t, nodes[4], selector.Select()) + }) + + t.Run("nodes never update latest block number", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node1.On("Order").Return(int32(1)) + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node2.On("Order").Return(int32(1)) + selector := newNodeSelector(NodeSelectionModeHighestHead, []Node[types.ID, Head, nodeClient]{node1, node2}) + assert.Same(t, node1, selector.Select()) + }) +} + +func TestHighestHeadNodeSelector_None(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + } else { + // others are unreachable + node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), nil) + } + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + assert.Nil(t, selector.Select()) +} + +func TestHighestHeadNodeSelectorWithOrder(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + t.Run("same head and order", func(t *testing.T) { + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + node.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node.On("Order").Return(int32(2)) + nodes = append(nodes, node) + } + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + //Should select the first node because all things are equal + assert.Same(t, nodes[0], selector.Select()) + }) + + t.Run("same head but different order", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node1.On("Order").Return(int32(3)) + + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node2.On("Order").Return(int32(1)) + + node3 := newMockNode[types.ID, Head, nodeClient](t) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node3.On("Order").Return(int32(2)) + + nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + //Should select the second node as it has the highest priority + assert.Same(t, nodes[1], selector.Select()) + }) + + t.Run("different head but same order", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), nil) + node1.On("Order").Maybe().Return(int32(3)) + + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(2), nil) + node2.On("Order").Maybe().Return(int32(3)) + + node3 := newMockNode[types.ID, Head, nodeClient](t) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), nil) + node3.On("Order").Return(int32(3)) + + nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + //Should select the third node as it has the highest head + assert.Same(t, nodes[2], selector.Select()) + }) + + t.Run("different head and different order", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(10), nil) + node1.On("Order").Maybe().Return(int32(3)) + + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(11), nil) + node2.On("Order").Maybe().Return(int32(4)) + + node3 := newMockNode[types.ID, Head, nodeClient](t) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(11), nil) + node3.On("Order").Maybe().Return(int32(3)) + + node4 := newMockNode[types.ID, Head, nodeClient](t) + node4.On("StateAndLatest").Return(nodeStateAlive, int64(10), nil) + node4.On("Order").Maybe().Return(int32(1)) + + nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} + selector := newNodeSelector(NodeSelectionModeHighestHead, nodes) + //Should select the third node as it has the highest head and will win the priority tie-breaker + assert.Same(t, nodes[2], selector.Select()) + }) +} diff --git a/common/client/node_selector_priority_level_test.go b/common/client/node_selector_priority_level_test.go new file mode 100644 index 00000000000..ac84645e91c --- /dev/null +++ b/common/client/node_selector_priority_level_test.go @@ -0,0 +1,88 @@ +package client + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/v2/common/types" + + "github.com/stretchr/testify/assert" +) + +func TestPriorityLevelNodeSelectorName(t *testing.T) { + selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModePriorityLevel, nil) + assert.Equal(t, selector.Name(), NodeSelectionModePriorityLevel) +} + +func TestPriorityLevelNodeSelector(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + n1 := newMockNode[types.ID, Head, nodeClient](t) + n1.On("State").Return(nodeStateAlive) + n1.On("Order").Return(int32(1)) + + n2 := newMockNode[types.ID, Head, nodeClient](t) + n2.On("State").Return(nodeStateAlive) + n2.On("Order").Return(int32(1)) + + n3 := newMockNode[types.ID, Head, nodeClient](t) + n3.On("State").Return(nodeStateAlive) + n3.On("Order").Return(int32(1)) + + nodes = append(nodes, n1, n2, n3) + selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) + assert.Same(t, nodes[0], selector.Select()) + assert.Same(t, nodes[1], selector.Select()) + assert.Same(t, nodes[2], selector.Select()) + assert.Same(t, nodes[0], selector.Select()) + assert.Same(t, nodes[1], selector.Select()) + assert.Same(t, nodes[2], selector.Select()) +} + +func TestPriorityLevelNodeSelector_None(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("State").Return(nodeStateOutOfSync) + node.On("Order").Return(int32(1)) + } else { + // others are unreachable + node.On("State").Return(nodeStateUnreachable) + node.On("Order").Return(int32(1)) + } + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) + assert.Nil(t, selector.Select()) +} + +func TestPriorityLevelNodeSelector_DifferentOrder(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + n1 := newMockNode[types.ID, Head, nodeClient](t) + n1.On("State").Return(nodeStateAlive) + n1.On("Order").Return(int32(1)) + + n2 := newMockNode[types.ID, Head, nodeClient](t) + n2.On("State").Return(nodeStateAlive) + n2.On("Order").Return(int32(2)) + + n3 := newMockNode[types.ID, Head, nodeClient](t) + n3.On("State").Return(nodeStateAlive) + n3.On("Order").Return(int32(3)) + + nodes = append(nodes, n1, n2, n3) + selector := newNodeSelector(NodeSelectionModePriorityLevel, nodes) + assert.Same(t, nodes[0], selector.Select()) + assert.Same(t, nodes[0], selector.Select()) +} diff --git a/common/client/node_selector_round_robin_test.go b/common/client/node_selector_round_robin_test.go new file mode 100644 index 00000000000..e5078d858f1 --- /dev/null +++ b/common/client/node_selector_round_robin_test.go @@ -0,0 +1,61 @@ +package client + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +func TestRoundRobinNodeSelectorName(t *testing.T) { + selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeRoundRobin, nil) + assert.Equal(t, selector.Name(), NodeSelectionModeRoundRobin) +} + +func TestRoundRobinNodeSelector(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("State").Return(nodeStateOutOfSync) + } else { + // second & third nodes are alive + node.On("State").Return(nodeStateAlive) + } + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModeRoundRobin, nodes) + assert.Same(t, nodes[1], selector.Select()) + assert.Same(t, nodes[2], selector.Select()) + assert.Same(t, nodes[1], selector.Select()) + assert.Same(t, nodes[2], selector.Select()) +} + +func TestRoundRobinNodeSelector_None(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("State").Return(nodeStateOutOfSync) + } else { + // others are unreachable + node.On("State").Return(nodeStateUnreachable) + } + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModeRoundRobin, nodes) + assert.Nil(t, selector.Select()) +} diff --git a/common/client/node_selector_test.go b/common/client/node_selector_test.go new file mode 100644 index 00000000000..226cb67168d --- /dev/null +++ b/common/client/node_selector_test.go @@ -0,0 +1,18 @@ +package client + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/common/types" +) + +func TestNodeSelector(t *testing.T) { + // rest of the tests are located in specific node selectors tests + t.Run("panics on unknown type", func(t *testing.T) { + assert.Panics(t, func() { + _ = newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]]("unknown", nil) + }) + }) +} diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go new file mode 100644 index 00000000000..4eecb859db9 --- /dev/null +++ b/common/client/node_selector_total_difficulty_test.go @@ -0,0 +1,178 @@ +package client + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/stretchr/testify/assert" +) + +func TestTotalDifficultyNodeSelectorName(t *testing.T) { + selector := newNodeSelector[types.ID, Head, NodeClient[types.ID, Head]](NodeSelectionModeTotalDifficulty, nil) + assert.Equal(t, selector.Name(), NodeSelectionModeTotalDifficulty) +} + +func TestTotalDifficultyNodeSelector(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + } else if i == 1 { + // second node is alive + node.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(7)) + } else { + // third node is alive and best + node.On("StateAndLatest").Return(nodeStateAlive, int64(2), utils.NewBigI(8)) + } + node.On("Order").Maybe().Return(int32(1)) + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + assert.Same(t, nodes[2], selector.Select()) + + t.Run("stick to the same node", func(t *testing.T) { + node := newMockNode[types.ID, Head, nodeClient](t) + // fourth node is alive (same as 3rd) + node.On("StateAndLatest").Return(nodeStateAlive, int64(2), utils.NewBigI(8)) + node.On("Order").Maybe().Return(int32(1)) + nodes = append(nodes, node) + + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + assert.Same(t, nodes[2], selector.Select()) + }) + + t.Run("another best node", func(t *testing.T) { + node := newMockNode[types.ID, Head, nodeClient](t) + // fifth node is alive (better than 3rd and 4th) + node.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(11)) + node.On("Order").Maybe().Return(int32(1)) + nodes = append(nodes, node) + + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + assert.Same(t, nodes[4], selector.Select()) + }) + + t.Run("nodes never update latest block number", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node1.On("Order").Maybe().Return(int32(1)) + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(-1), nil) + node2.On("Order").Maybe().Return(int32(1)) + nodes := []Node[types.ID, Head, nodeClient]{node1, node2} + + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + assert.Same(t, node1, selector.Select()) + }) +} + +func TestTotalDifficultyNodeSelector_None(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + if i == 0 { + // first node is out of sync + node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) + } else { + // others are unreachable + node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), utils.NewBigI(7)) + } + nodes = append(nodes, node) + } + + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + assert.Nil(t, selector.Select()) +} + +func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { + t.Parallel() + + type nodeClient NodeClient[types.ID, Head] + var nodes []Node[types.ID, Head, nodeClient] + + t.Run("same td and order", func(t *testing.T) { + for i := 0; i < 3; i++ { + node := newMockNode[types.ID, Head, nodeClient](t) + node.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(10)) + node.On("Order").Return(int32(2)) + nodes = append(nodes, node) + } + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + //Should select the first node because all things are equal + assert.Same(t, nodes[0], selector.Select()) + }) + + t.Run("same td but different order", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(10)) + node1.On("Order").Return(int32(3)) + + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(10)) + node2.On("Order").Return(int32(1)) + + node3 := newMockNode[types.ID, Head, nodeClient](t) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(10)) + node3.On("Order").Return(int32(2)) + + nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + //Should select the second node as it has the highest priority + assert.Same(t, nodes[1], selector.Select()) + }) + + t.Run("different td but same order", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(10)) + node1.On("Order").Maybe().Return(int32(3)) + + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(11)) + node2.On("Order").Maybe().Return(int32(3)) + + node3 := newMockNode[types.ID, Head, nodeClient](t) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(12)) + node3.On("Order").Return(int32(3)) + + nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + //Should select the third node as it has the highest td + assert.Same(t, nodes[2], selector.Select()) + }) + + t.Run("different head and different order", func(t *testing.T) { + node1 := newMockNode[types.ID, Head, nodeClient](t) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(100)) + node1.On("Order").Maybe().Return(int32(4)) + + node2 := newMockNode[types.ID, Head, nodeClient](t) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(110)) + node2.On("Order").Maybe().Return(int32(5)) + + node3 := newMockNode[types.ID, Head, nodeClient](t) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(110)) + node3.On("Order").Maybe().Return(int32(1)) + + node4 := newMockNode[types.ID, Head, nodeClient](t) + node4.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(105)) + node4.On("Order").Maybe().Return(int32(2)) + + nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} + selector := newNodeSelector(NodeSelectionModeTotalDifficulty, nodes) + //Should select the third node as it has the highest td and will win the priority tie-breaker + assert.Same(t, nodes[2], selector.Select()) + }) +} diff --git a/common/client/node_test.go b/common/client/node_test.go new file mode 100644 index 00000000000..0438e11e612 --- /dev/null +++ b/common/client/node_test.go @@ -0,0 +1,80 @@ +package client + +import ( + "net/url" + "testing" + "time" + + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type testNodeConfig struct { + pollFailureThreshold uint32 + pollInterval time.Duration + selectionMode string + syncThreshold uint32 +} + +func (n testNodeConfig) PollFailureThreshold() uint32 { + return n.pollFailureThreshold +} + +func (n testNodeConfig) PollInterval() time.Duration { + return n.pollInterval +} + +func (n testNodeConfig) SelectionMode() string { + return n.selectionMode +} + +func (n testNodeConfig) SyncThreshold() uint32 { + return n.syncThreshold +} + +type testNode struct { + *node[types.ID, Head, NodeClient[types.ID, Head]] +} + +type testNodeOpts struct { + config testNodeConfig + noNewHeadsThreshold time.Duration + lggr logger.Logger + wsuri url.URL + httpuri *url.URL + name string + id int32 + chainID types.ID + nodeOrder int32 + rpc *mockNodeClient[types.ID, Head] + chainFamily string +} + +func newTestNode(t *testing.T, opts testNodeOpts) testNode { + if opts.lggr == nil { + opts.lggr = logger.TestLogger(t) + } + + if opts.name == "" { + opts.name = "tes node" + } + + if opts.chainFamily == "" { + opts.chainFamily = "test node chain family" + } + + if opts.chainID == nil { + opts.chainID = types.RandomID() + } + + if opts.id == 0 { + opts.id = 42 + } + + nodeI := NewNode[types.ID, Head, NodeClient[types.ID, Head]](opts.config, opts.noNewHeadsThreshold, opts.lggr, + opts.wsuri, opts.httpuri, opts.name, opts.id, opts.chainID, opts.nodeOrder, opts.rpc, opts.chainFamily) + + return testNode{ + nodeI.(*node[types.ID, Head, NodeClient[types.ID, Head]]), + } +} diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index 0051fb014d9..fa793a826a6 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +//go:generate mockery --quiet --name sendOnlyClient --structname mockSendOnlyClient --filename "mock_send_only_client_test.go" --inpackage --case=underscore type sendOnlyClient[ CHAIN_ID types.ID, ] interface { @@ -22,6 +23,8 @@ type sendOnlyClient[ } // SendOnlyNode represents one node used as a sendonly +// +//go:generate mockery --quiet --name SendOnlyNode --structname mockSendOnlyNode --filename "mock_send_only_node_test.go" --inpackage --case=underscore type SendOnlyNode[ CHAIN_ID types.ID, RPC sendOnlyClient[CHAIN_ID], @@ -143,6 +146,7 @@ func (s *sendOnlyNode[CHAIN_ID, RPC]) start(startCtx context.Context) { func (s *sendOnlyNode[CHAIN_ID, RPC]) Close() error { return s.StopOnce(s.name, func() error { s.rpc.Close() + close(s.chStop) s.wg.Wait() s.setState(nodeStateClosed) return nil diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go new file mode 100644 index 00000000000..bfe55153656 --- /dev/null +++ b/common/client/send_only_node_test.go @@ -0,0 +1,139 @@ +package client + +import ( + "fmt" + "net/url" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + + "github.com/smartcontractkit/chainlink/v2/common/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestNewSendOnlyNode(t *testing.T) { + t.Parallel() + + urlFormat := "http://user:%s@testurl.com" + password := "pass" + u, err := url.Parse(fmt.Sprintf(urlFormat, password)) + require.NoError(t, err) + redacted := fmt.Sprintf(urlFormat, "xxxxx") + lggr := logger.TestLogger(t) + name := "TestNewSendOnlyNode" + chainID := types.RandomID() + client := newMockSendOnlyClient[types.ID](t) + + node := NewSendOnlyNode(lggr, *u, name, chainID, client) + assert.NotNil(t, node) + + // Must contain name & url with redacted password + assert.Contains(t, node.String(), fmt.Sprintf("%s:%s", name, redacted)) + assert.Equal(t, node.ConfiguredChainID(), chainID) +} + +func TestStartSendOnlyNode(t *testing.T) { + t.Parallel() + t.Run("becomes unusable if initial dial fails", func(t *testing.T) { + t.Parallel() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + client := newMockSendOnlyClient[types.ID](t) + client.On("Close").Once() + expectedError := errors.New("some http error") + client.On("DialHTTP").Return(expectedError).Once() + s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.RandomID(), client) + + defer func() { assert.NoError(t, s.Close()) }() + err := s.Start(tests.Context(t)) + require.NoError(t, err) + + assert.Equal(t, nodeStateUnusable, s.State()) + tests.RequireLogMessage(t, observedLogs, "Dial failed: SendOnly Node is unusable") + }) + t.Run("Default ChainID(0) produces warn and skips checks", func(t *testing.T) { + t.Parallel() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + client := newMockSendOnlyClient[types.ID](t) + client.On("Close").Once() + client.On("DialHTTP").Return(nil).Once() + s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), types.NewIDFromInt(0), client) + + defer func() { assert.NoError(t, s.Close()) }() + err := s.Start(tests.Context(t)) + require.NoError(t, err) + + assert.Equal(t, nodeStateAlive, s.State()) + tests.RequireLogMessage(t, observedLogs, "sendonly rpc ChainID verification skipped") + }) + t.Run("Can recover from chainID verification failure", func(t *testing.T) { + t.Parallel() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + client := newMockSendOnlyClient[types.ID](t) + client.On("Close").Once() + client.On("DialHTTP").Return(nil) + expectedError := errors.New("failed to get chain ID") + chainID := types.RandomID() + const failuresCount = 2 + client.On("ChainID", mock.Anything).Return(types.RandomID(), expectedError).Times(failuresCount) + client.On("ChainID", mock.Anything).Return(chainID, nil) + + s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), chainID, client) + + defer func() { assert.NoError(t, s.Close()) }() + err := s.Start(tests.Context(t)) + require.NoError(t, err) + + assert.Equal(t, nodeStateUnreachable, s.State()) + tests.AssertLogCountEventually(t, observedLogs, fmt.Sprintf("Verify failed: %v", expectedError), failuresCount) + tests.AssertEventually(t, func() bool { + return s.State() == nodeStateAlive + }) + }) + t.Run("Can recover from chainID mismatch", func(t *testing.T) { + t.Parallel() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + client := newMockSendOnlyClient[types.ID](t) + client.On("Close").Once() + client.On("DialHTTP").Return(nil).Once() + configuredChainID := types.NewIDFromInt(11) + rpcChainID := types.NewIDFromInt(20) + const failuresCount = 2 + client.On("ChainID", mock.Anything).Return(rpcChainID, nil).Times(failuresCount) + client.On("ChainID", mock.Anything).Return(configuredChainID, nil) + s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), configuredChainID, client) + + defer func() { assert.NoError(t, s.Close()) }() + err := s.Start(tests.Context(t)) + require.NoError(t, err) + + assert.Equal(t, nodeStateInvalidChainID, s.State()) + tests.AssertLogCountEventually(t, observedLogs, "sendonly rpc ChainID doesn't match local chain ID", failuresCount) + tests.AssertEventually(t, func() bool { + return s.State() == nodeStateAlive + }) + }) + t.Run("Start with Random ChainID", func(t *testing.T) { + t.Parallel() + lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + client := newMockSendOnlyClient[types.ID](t) + client.On("Close").Once() + client.On("DialHTTP").Return(nil).Once() + configuredChainID := types.RandomID() + client.On("ChainID", mock.Anything).Return(configuredChainID, nil) + s := NewSendOnlyNode(lggr, url.URL{}, t.Name(), configuredChainID, client) + + defer func() { assert.NoError(t, s.Close()) }() + err := s.Start(tests.Context(t)) + assert.NoError(t, err) + tests.AssertEventually(t, func() bool { + return s.State() == nodeStateAlive + }) + assert.Equal(t, 0, observedLogs.Len()) // No warnings expected + }) +} diff --git a/common/client/types.go b/common/client/types.go index f3a6029a9e8..0e52f1db72c 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -11,6 +11,8 @@ import ( ) // RPC includes all the necessary methods for a multi-node client to interact directly with any RPC endpoint. +// +//go:generate mockery --quiet --name RPC --structname mockRPC --inpackage --filename "mock_rpc_test.go" --case=underscore type RPC[ CHAIN_ID types.ID, SEQ types.Sequence, @@ -45,12 +47,16 @@ type RPC[ } // Head is the interface required by the NodeClient +// +//go:generate mockery --quiet --name Head --structname mockHead --filename "mock_head_test.go" --inpackage --case=underscore type Head interface { BlockNumber() int64 BlockDifficulty() *utils.Big } // NodeClient includes all the necessary RPC methods required by a node. +// +//go:generate mockery --quiet --name NodeClient --structname mockNodeClient --filename "mock_node_client_test.go" --inpackage --case=underscore type NodeClient[ CHAIN_ID types.ID, HEAD Head, diff --git a/common/types/test_utils.go b/common/types/test_utils.go new file mode 100644 index 00000000000..40560f7866c --- /dev/null +++ b/common/types/test_utils.go @@ -0,0 +1,16 @@ +package types + +import ( + "math" + "math/big" + "math/rand" +) + +func RandomID() ID { + id := rand.Int63n(math.MaxInt32) + 10000 + return big.NewInt(id) +} + +func NewIDFromInt(id int64) ID { + return big.NewInt(id) +} From d8dc7abe3c9bd9bedd036cf69c742e883832fc9e Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Thu, 9 Nov 2023 16:09:14 -0500 Subject: [PATCH 122/271] [Functions] Add Billing event (#11185) * FUN-974 - Add Billing event * Add L1 fee share to billing event * Update gas snapshot --- .../gas-snapshots/functions.gas-snapshot | 44 +++--- .../functions/dev/v1_X/FunctionsBilling.sol | 19 ++- .../tests/v1_X/FunctionsBilling.t.sol | 52 ++++++- .../functions_coordinator.go | 147 +++++++++++++++++- ...rapper-dependency-versions-do-not-edit.txt | 2 +- 5 files changed, 237 insertions(+), 27 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index 4e891535b77..af95b75df10 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14497117) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14497095) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14497111) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14508531) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14508508) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14508480) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14508431) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14508420) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14508464) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14534216) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14534194) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14534210) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14545630) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14545607) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14545579) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14545530) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14545519) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14545563) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -26,9 +26,11 @@ FunctionsBilling_OracleWithdrawAll:test_OracleWithdrawAll_SuccessPaysTransmitter FunctionsBilling_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 18974) FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 38251) FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8801) +FunctionsBilling__FulfillAndBill:test__FulfillAndBill_RevertIfInvalidCommitment() (gas: 13302) +FunctionsBilling__FulfillAndBill:test__FulfillAndBill_Success() (gas: 180763) FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) -FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 501740) -FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 202944) +FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 504364) +FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 205568) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_RevertIfNotRouter() (gas: 14623) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_Success() (gas: 22923) FunctionsClient__SendRequest:test__SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 55059) @@ -49,17 +51,17 @@ FunctionsRequest_DEFAULT_BUFFER_SIZE:test_DEFAULT_BUFFER_SIZE() (gas: 246) FunctionsRequest_EncodeCBOR:test_EncodeCBOR_Success() (gas: 223) FunctionsRequest_REQUEST_DATA_VERSION:test_REQUEST_DATA_VERSION() (gas: 225) FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12007) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 174021) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 164352) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 174037) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 164368) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38115) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35238) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 182497) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 182513) FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28086) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 158041) -FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 323262) -FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 336879) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2512144) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 542628) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 158055) +FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 327615) +FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 341236) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2516517) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 546996) FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37159) @@ -142,8 +144,8 @@ FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 1501 FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43685, ~: 45548) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46197, ~: 48060) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51000, ~: 53040) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 85879, ~: 89604) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51089, ~: 53040) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index bf43ead8d72..bd13a3a5a1a 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -20,6 +20,15 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { using FunctionsResponse for FunctionsResponse.FulfillResult; uint256 private constant REASONABLE_GAS_PRICE_CEILING = 1_000_000_000_000_000; // 1 million gwei + + event RequestBilled( + bytes32 indexed requestId, + uint96 juelsPerGas, + uint256 l1FeeShareWei, + uint96 callbackCostJuels, + uint96 totalCostJuels + ); + // ================================================================ // | Request Commitment state | // ================================================================ @@ -268,12 +277,13 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { uint256 l1FeeShareWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data) / reportBatchSize; // Gas overhead without callback uint96 gasOverheadJuels = _getJuelsFromWei(gasOverheadWei + l1FeeShareWei); + uint96 juelsPerGas = _getJuelsFromWei(tx.gasprice); // The Functions Router will perform the callback to the client contract (FunctionsResponse.FulfillResult resultCode, uint96 callbackCostJuels) = _getRouter().fulfill( response, err, - _getJuelsFromWei(tx.gasprice), // Juels Per Gas conversion rate + juelsPerGas, gasOverheadJuels + commitment.donFee, // cost without callback or admin fee, those will be added by the Router msg.sender, commitment @@ -292,6 +302,13 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { // Put donFee into the pool of fees, to be split later // Saves on storage writes that would otherwise be charged to the user s_feePool += commitment.donFee; + emit RequestBilled({ + requestId: requestId, + juelsPerGas: juelsPerGas, + l1FeeShareWei: l1FeeShareWei, + callbackCostJuels: callbackCostJuels, + totalCostJuels: gasOverheadJuels + callbackCostJuels + commitment.donFee + commitment.adminFee + }); } return resultCode; diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol index 82dea8672c8..6e94e4fc5f7 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.19; import {FunctionsCoordinator} from "../../dev/v1_X/FunctionsCoordinator.sol"; import {FunctionsBilling} from "../../dev/v1_X/FunctionsBilling.sol"; import {FunctionsRequest} from "../../dev/v1_X/libraries/FunctionsRequest.sol"; +import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol"; import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; import {Routable} from "../../dev/v1_X/Routable.sol"; @@ -221,8 +222,55 @@ contract FunctionsBilling__StartBilling { } /// @notice #_fulfillAndBill -contract FunctionsBilling__FulfillAndBill { - // TODO: make contract internal function helper +contract FunctionsBilling__FulfillAndBill is FunctionsClientRequestSetup { + function test__FulfillAndBill_RevertIfInvalidCommitment() public { + vm.expectRevert(); + s_functionsCoordinator.fulfillAndBill_HARNESS( + s_requests[1].requestId, + new bytes(0), + new bytes(0), + new bytes(0), // malformed commitment data + new bytes(0), + 1 + ); + } + + event RequestBilled( + bytes32 indexed requestId, + uint96 juelsPerGas, + uint256 l1FeeShareWei, + uint96 callbackCostJuels, + uint96 totalCostJuels + ); + + function test__FulfillAndBill_Success() public { + uint96 juelsPerGas = uint96((1e18 * TX_GASPRICE_START) / uint256(LINK_ETH_RATE)); + uint96 callbackCostGas = 5072; // Taken manually + uint96 callbackCostJuels = juelsPerGas * callbackCostGas; + uint96 gasOverheadJuels = juelsPerGas * + (getCoordinatorConfig().gasOverheadBeforeCallback + getCoordinatorConfig().gasOverheadAfterCallback); + + uint96 totalCostJuels = gasOverheadJuels + callbackCostJuels + s_donFee + s_adminFee; + + // topic0 (function signature, always checked), check topic1 (true), NOT topic2 (false), NOT topic3 (false), and data (true). + bool checkTopic1 = true; + bool checkTopic2 = false; + bool checkTopic3 = false; + bool checkData = true; + vm.expectEmit(checkTopic1, checkTopic2, checkTopic3, checkData); + emit RequestBilled(s_requests[1].requestId, juelsPerGas, 0, callbackCostJuels, totalCostJuels); + + FunctionsResponse.FulfillResult resultCode = s_functionsCoordinator.fulfillAndBill_HARNESS( + s_requests[1].requestId, + new bytes(0), + new bytes(0), + abi.encode(s_requests[1].commitment), + new bytes(0), + 1 + ); + + assertEq(uint256(resultCode), uint256(FunctionsResponse.FulfillResult.FULFILLED)); + } } /// @notice #deleteCommitment diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index b2e5ec4b09a..397ea3d18bb 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -71,8 +71,8 @@ type FunctionsResponseRequestMeta struct { } var FunctionsCoordinatorMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60c06040523480156200001157600080fd5b506040516200556438038062005564833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614ee16200068360003960008181610845015281816109d301528181610ca601528181610f3a015281816110450152818161183001526133aa0152600061126e0152614ee16000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a0366004613857565b61059c565b005b6101a56101b5366004613a00565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613b24565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613bc5565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613c54565b6108d7565b6101a5610a90565b6101a5610b92565b6101a5610293366004613857565b610d92565b6102a0610de2565b6040516102039190613cde565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613cf1565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613d0a565b610fd4565b6040516102039190613e5f565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613eb3565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b6040516102039190613f6a565b61053b61053636600461405a565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004614173565b6119e3565b61057b61240f565b604051908152602001610203565b6101a5610597366004614240565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec8284836142f6565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f518486290610836908390613f6a565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d2919061441c565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff16614468565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161448d565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561448d565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d87816144bc565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec8284836142f6565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e609061425d565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea89061425d565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed49061425d565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a8836144f4565b6128b2565b90506110bf6060830160408401614240565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a088016145e1565b61111f61016088016101408901614240565b61112988806145fe565b61113b6101208b016101008c01614663565b60208b01356111516101008d0160e08e0161467e565b8b6040516111679998979695949392919061469b565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d50565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a89190614743565b6112b2919061478b565b6112bd906001614743565b60ff1690506112dd565b60208201516112d7906001614743565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f26147ad565b6002811115611403576114036147ad565b9052509050600281602001516002811115611420576114206147ad565b14801561146757506006816000015160ff16815481106114425761144261448d565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da6137ef565b6000808a8a6040516114ed9291906147dc565b604051908190038120611504918e906020016147ec565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d61448d565b61157a91901a601b614743565b8e8e8681811061158c5761158c61448d565b905060200201358d8d878181106115a5576115a561448d565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff80821685529296509294508401916101009004166002811115611684576116846147ad565b6002811115611695576116956147ad565b90525092506001836020015160028111156116b2576116b26147ad565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f81106117335761173361448d565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf61448d565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa600186614743565b94505080611807906144bc565b905061154e565b50505061181f833383858e8e612e07565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd7565b98975050505050505050565b6060600c805461199b9061425d565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea89061425d565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b59816003614800565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c109088613144565b60055415611dc557600554600090611c2a90600190614817565b9050600060058281548110611c4157611c4161448d565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b61448d565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb61482a565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d6461482a565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee61448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e386147ad565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed061448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f716147ad565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b61448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe56147ad565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f61448d565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115612120576121206147ad565b02179055505082518051600592508390811061213e5761213e61448d565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba61448d565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905580612224816144bc565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e491849174010000000000000000000000000000000000000000900416614859565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a0015161315d565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff16969095919491939192614876565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c19190614926565b5093505092505080426125d49190614817565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b61267981613208565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b90508051600003612768576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600b54600091612787916bffffffffffffffffffffffff16614976565b905060005b82518110156128535781600a60008584815181106127ac576127ac61448d565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff1661281491906149a1565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284c906144bc565b905061278c565b50815161286090826149c6565b600b80546000906128809084906bffffffffffffffffffffffff16614468565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6d576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aaa8560e001513a848860800151612fd7565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b06576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b1f91906149ee565b905060003087604001518860a001518960c001516001612b3f9190614a01565b8a5180516020918201206101008d015160e08e0151604051612bf398979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d029190613e5f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5d826020614800565b612d68856020614800565b612d74886101446149ee565b612d7e91906149ee565b612d8891906149ee565b612d939060006149ee565b9050368114612dfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e1986880188614afd565b8451949950929750909550935091501580612e3657508351855114155b80612e4357508251855114155b80612e5057508151855114155b80612e5d57508051855114155b15612e94576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8551811015612fc9576000612f2e878381518110612eb757612eb761448d565b6020026020010151878481518110612ed157612ed161448d565b6020026020010151878581518110612eeb57612eeb61448d565b6020026020010151878681518110612f0557612f0561448d565b6020026020010151878781518110612f1f57612f1f61448d565b60200260200101518c516132fd565b90506000816006811115612f4457612f446147ad565b1480612f6157506001816006811115612f5f57612f5f6147ad565b145b15612fb857868281518110612f7857612f7861448d565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc2816144bc565b9050612e97565b505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561303257600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b6008546000906127109061304c9063ffffffff1687614800565b6130569190614bcf565b61306090866149ee565b60085490915060009087906130999063ffffffff6c01000000000000000000000000820481169168010000000000000000900416614859565b6130a39190614859565b63ffffffff16905060006130ed6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061356d92505050565b9050600061310e826130ff8587614800565b61310991906149ee565b6136af565b9050600061312a68ffffffffffffffffff808916908a166149a1565b905061313681836149a1565b9a9950505050505050505050565b600061314e610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161318199989796959493929190614be3565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133149190614caf565b905060003a82610120015183610100015161332f9190614d77565b64ffffffffff166133409190614800565b905060008460ff166133886000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061356d92505050565b6133929190614bcf565b905060006133a361310983856149ee565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298d8d6133ef3a6136af565b60e08b01516134099068ffffffffffffffffff16896149a1565b338c6040518763ffffffff1660e01b815260040161342c96959493929190614d95565b60408051808303816000875af115801561344a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061346e9190614e11565b90925090506000826006811115613487576134876147ad565b14806134a4575060018260068111156134a2576134a26147ad565b145b1561355d5760008d8152600760205260408120556134c281846149a1565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0880151600b805468ffffffffffffffffff9092169390929161352e918591166149a1565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505b509b9a5050505050505050505050565b600046613579816136e3565b156135f557606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ee9190614e44565b9392505050565b6135fe81613706565b156136a65773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e84604051806080016040528060488152602001614e8d6048913960405160200161365e929190614e5d565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016136899190613b24565b602060405180830381865afa1580156135ca573d6000803e3d6000fd5b50600092915050565b60006136dd6136bc61240f565b6136ce84670de0b6b3a7640000614800565b6136d89190614bcf565b61374d565b92915050565b600061a4b18214806136f7575062066eed82145b806136dd57505062066eee1490565b6000600a82148061371857506101a482145b80613725575062aa37dc82145b80613731575061210582145b8061373e575062014a3382145b806136dd57505062014a341490565b60006bffffffffffffffffffffffff8211156137eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f84011261382057600080fd5b50813567ffffffffffffffff81111561383857600080fd5b60208301915083602082850101111561385057600080fd5b9250929050565b6000806020838503121561386a57600080fd5b823567ffffffffffffffff81111561388157600080fd5b61388d8582860161380e565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156138ec576138ec613899565b60405290565b604051610160810167ffffffffffffffff811182821017156138ec576138ec613899565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561395d5761395d613899565b604052919050565b63ffffffff8116811461267957600080fd5b803561117081613965565b68ffffffffffffffffff8116811461267957600080fd5b803561117081613982565b64ffffffffff8116811461267957600080fd5b8035611170816139a4565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613a1357600080fd5b613a1b6138c8565b613a2483613977565b8152613a3260208401613977565b6020820152613a4360408401613977565b6040820152613a5460608401613977565b6060820152613a6560808401613999565b6080820152613a7660a084016139b7565b60a0820152613a8760c084016139c2565b60c0820152613a9860e084016139d4565b60e0820152610100613aab818501613977565b908201529392505050565b60005b83811015613ad1578181015183820152602001613ab9565b50506000910152565b60008151808452613af2816020860160208601613ab6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006135ee6020830184613ada565b600082601f830112613b4857600080fd5b813567ffffffffffffffff811115613b6257613b62613899565b613b9360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613916565b818152846020838601011115613ba857600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613bd757600080fd5b813567ffffffffffffffff811115613bee57600080fd5b613bfa84828501613b37565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b803561117081613c02565b6bffffffffffffffffffffffff8116811461267957600080fd5b803561117081613c2f565b60008060408385031215613c6757600080fd5b8235613c7281613c02565b91506020830135613c8281613c2f565b809150509250929050565b600081518084526020808501945080840160005b83811015613cd357815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613ca1565b509495945050505050565b6020815260006135ee6020830184613c8d565b600060208284031215613d0357600080fd5b5035919050565b600060208284031215613d1c57600080fd5b813567ffffffffffffffff811115613d3357600080fd5b820161016081850312156135ee57600080fd5b805182526020810151613d71602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613d9160408401826bffffffffffffffffffffffff169052565b506060810151613db9606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613dd5608084018267ffffffffffffffff169052565b5060a0810151613ded60a084018263ffffffff169052565b5060c0810151613e0a60c084018268ffffffffffffffffff169052565b5060e0810151613e2760e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016136dd8284613d46565b60008083601f840112613e8057600080fd5b50813567ffffffffffffffff811115613e9857600080fd5b6020830191508360208260051b850101111561385057600080fd5b60008060008060008060008060e0898b031215613ecf57600080fd5b606089018a811115613ee057600080fd5b8998503567ffffffffffffffff80821115613efa57600080fd5b613f068c838d0161380e565b909950975060808b0135915080821115613f1f57600080fd5b613f2b8c838d01613e6e565b909750955060a08b0135915080821115613f4457600080fd5b50613f518b828c01613e6e565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151613fbe608084018268ffffffffffffffffff169052565b5060a0830151613fd760a084018264ffffffffff169052565b5060c0830151613fed60c084018261ffff169052565b5060e083015161401d60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b803561117081614039565b60008060008060006080868803121561407257600080fd5b853561407d81614039565b9450602086013567ffffffffffffffff81111561409957600080fd5b6140a58882890161380e565b90955093505060408601356140b981613965565b949793965091946060013592915050565b600067ffffffffffffffff8211156140e4576140e4613899565b5060051b60200190565b600082601f8301126140ff57600080fd5b8135602061411461410f836140ca565b613916565b82815260059290921b8401810191818101908684111561413357600080fd5b8286015b8481101561415757803561414a81613c02565b8352918301918301614137565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561418c57600080fd5b863567ffffffffffffffff808211156141a457600080fd5b6141b08a838b016140ee565b975060208901359150808211156141c657600080fd5b6141d28a838b016140ee565b96506141e060408a01614162565b955060608901359150808211156141f657600080fd5b6142028a838b01613b37565b945061421060808a0161404f565b935060a089013591508082111561422657600080fd5b5061423389828a01613b37565b9150509295509295509295565b60006020828403121561425257600080fd5b81356135ee81613c02565b600181811c9082168061427157607f821691505b6020821081036142aa577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156142d75750805b601f850160051c820191505b81811015610a88578281556001016142e3565b67ffffffffffffffff83111561430e5761430e613899565b6143228361431c835461425d565b836142b0565b6000601f841160018114614374576000851561433e5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b17835561440a565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156143c357868501358255602094850194600190920191016143a3565b50868210156143fe577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613982565b60006020828403121561442e57600080fd5b81516135ee81613982565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561266157612661614439565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036144ed576144ed614439565b5060010190565b6000610160823603121561450757600080fd5b61450f6138f2565b823567ffffffffffffffff81111561452657600080fd5b61453236828601613b37565b8252506020830135602082015261454b60408401613c24565b604082015261455c60608401613c49565b606082015261456d60808401613999565b608082015261457e60a0840161404f565b60a082015261458f60c0840161404f565b60c08201526145a060e08401613977565b60e08201526101006145b38185016139c2565b908201526101206145c584820161404f565b908201526101406145d7848201613c24565b9082015292915050565b6000602082840312156145f357600080fd5b81356135ee81614039565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261463357600080fd5b83018035915067ffffffffffffffff82111561464e57600080fd5b60200191503681900382131561385057600080fd5b60006020828403121561467557600080fd5b6135ee826139c2565b60006020828403121561469057600080fd5b81356135ee81613965565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061313660e0830184613d46565b60ff81811683821601908111156136dd576136dd614439565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061479e5761479e61475c565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b80820281158282048414176136dd576136dd614439565b818103818111156136dd576136dd614439565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561266157612661614439565b600061012063ffffffff808d1684528b6020850152808b166040850152508060608401526148a68184018a613c8d565b905082810360808401526148ba8189613c8d565b905060ff871660a084015282810360c08401526148d78187613ada565b905067ffffffffffffffff851660e08401528281036101008401526148fc8185613ada565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a0868803121561493e57600080fd5b6149478661490c565b945060208601519350604086015192506060860151915061496a6080870161490c565b90509295509295909350565b60006bffffffffffffffffffffffff808416806149955761499561475c565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561266157612661614439565b6bffffffffffffffffffffffff81811683821602808216919082811461403157614031614439565b808201808211156136dd576136dd614439565b67ffffffffffffffff81811683821601908082111561266157612661614439565b600082601f830112614a3357600080fd5b81356020614a4361410f836140ca565b82815260059290921b84018101918181019086841115614a6257600080fd5b8286015b848110156141575780358352918301918301614a66565b600082601f830112614a8e57600080fd5b81356020614a9e61410f836140ca565b82815260059290921b84018101918181019086841115614abd57600080fd5b8286015b8481101561415757803567ffffffffffffffff811115614ae15760008081fd5b614aef8986838b0101613b37565b845250918301918301614ac1565b600080600080600060a08688031215614b1557600080fd5b853567ffffffffffffffff80821115614b2d57600080fd5b614b3989838a01614a22565b96506020880135915080821115614b4f57600080fd5b614b5b89838a01614a7d565b95506040880135915080821115614b7157600080fd5b614b7d89838a01614a7d565b94506060880135915080821115614b9357600080fd5b614b9f89838a01614a7d565b93506080880135915080821115614bb557600080fd5b50614bc288828901614a7d565b9150509295509295909350565b600082614bde57614bde61475c565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614c2a8285018b613c8d565b91508382036080850152614c3e828a613c8d565b915060ff881660a085015283820360c0850152614c5b8288613ada565b90861660e085015283810361010085015290506148fc8185613ada565b805161117081613c02565b805161117081613c2f565b805161117081614039565b805161117081613965565b8051611170816139a4565b60006101608284031215614cc257600080fd5b614cca6138f2565b82518152614cda60208401614c78565b6020820152614ceb60408401614c83565b6040820152614cfc60608401614c78565b6060820152614d0d60808401614c8e565b6080820152614d1e60a08401614c99565b60a0820152614d2f60c08401614411565b60c0820152614d4060e08401614411565b60e0820152610100614d53818501614ca4565b90820152610120614d65848201614ca4565b90820152610140613aab848201614c99565b64ffffffffff81811683821601908082111561266157612661614439565b6000610200808352614da98184018a613ada565b90508281036020840152614dbd8189613ada565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614e06905060a0830184613d46565b979650505050505050565b60008060408385031215614e2457600080fd5b825160078110614e3357600080fd5b6020840151909250613c8281613c2f565b600060208284031215614e5657600080fd5b5051919050565b60008351614e6f818460208801613ab6565b835190830190614e83818360208801613ab6565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60c06040523480156200001157600080fd5b506040516200560838038062005608833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614f856200068360003960008181610845015281816109d301528181610ca601528181610f3a015281816110450152818161183001526133b70152600061126e0152614f856000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046138fb565b61059c565b005b6101a56101b5366004613aa4565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613bc8565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613c69565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613cf8565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046138fb565b610d92565b6102a0610de2565b6040516102039190613d82565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613d95565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613dae565b610fd4565b6040516102039190613f03565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613f57565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b604051610203919061400e565b61053b6105363660046140fe565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004614217565b6119e3565b61057b61240f565b604051908152602001610203565b6101a56105973660046142e4565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec82848361439a565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f51848629061083690839061400e565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d291906144c0565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd2614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c31614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf5614531565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614560565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec82848361439a565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e6090614301565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea890614301565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed490614301565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614598565b6128b2565b90506110bf60608301604084016142e4565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a08801614685565b61111f610160880161014089016142e4565b61112988806146a2565b61113b6101208b016101008c01614707565b60208b01356111516101008d0160e08e01614722565b8b6040516111679998979695949392919061473f565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d50565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a891906147e7565b6112b2919061482f565b6112bd9060016147e7565b60ff1690506112dd565b60208201516112d79060016147e7565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f2614851565b600281111561140357611403614851565b905250905060028160200151600281111561142057611420614851565b14801561146757506006816000015160ff168154811061144257611442614531565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da613893565b6000808a8a6040516114ed929190614880565b604051908190038120611504918e90602001614890565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d614531565b61157a91901a601b6147e7565b8e8e8681811061158c5761158c614531565b905060200201358d8d878181106115a5576115a5614531565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff8082168552929650929450840191610100900416600281111561168457611684614851565b600281111561169557611695614851565b90525092506001836020015160028111156116b2576116b2614851565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061173357611733614531565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf614531565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa6001866147e7565b9450508061180790614560565b905061154e565b50505061181f833383858e8e612e07565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd7565b98975050505050505050565b6060600c805461199b90614301565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea890614301565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b598160036148a4565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c109088613144565b60055415611dc557600554600090611c2a906001906148bb565b9050600060058281548110611c4157611c41614531565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b614531565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb6148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d646148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e38614851565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed0614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f71614851565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe5614851565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561212057612120614851565b02179055505082518051600592508390811061213e5761213e614531565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba614531565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790558061222481614560565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e4918491740100000000000000000000000000000000000000009004166148fd565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a0015161315d565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff1696909591949193919261491a565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c191906149ca565b5093505092505080426125d491906148bb565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b61267981613208565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b90508051600003612768576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600b54600091612787916bffffffffffffffffffffffff16614a1a565b905060005b82518110156128535781600a60008584815181106127ac576127ac614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128149190614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284c90614560565b905061278c565b5081516128609082614a6a565b600b80546000906128809084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6d576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aaa8560e001513a848860800151612fd7565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b06576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b1f9190614a92565b905060003087604001518860a001518960c001516001612b3f9190614aa5565b8a5180516020918201206101008d015160e08e0151604051612bf398979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d029190613f03565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5d8260206148a4565b612d688560206148a4565b612d7488610144614a92565b612d7e9190614a92565b612d889190614a92565b612d93906000614a92565b9050368114612dfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e1986880188614ba1565b8451949950929750909550935091501580612e3657508351855114155b80612e4357508251855114155b80612e5057508151855114155b80612e5d57508051855114155b15612e94576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8551811015612fc9576000612f2e878381518110612eb757612eb7614531565b6020026020010151878481518110612ed157612ed1614531565b6020026020010151878581518110612eeb57612eeb614531565b6020026020010151878681518110612f0557612f05614531565b6020026020010151878781518110612f1f57612f1f614531565b60200260200101518c516132fd565b90506000816006811115612f4457612f44614851565b1480612f6157506001816006811115612f5f57612f5f614851565b145b15612fb857868281518110612f7857612f78614531565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc281614560565b9050612e97565b505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561303257600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b6008546000906127109061304c9063ffffffff16876148a4565b6130569190614c73565b6130609086614a92565b60085490915060009087906130999063ffffffff6c010000000000000000000000008204811691680100000000000000009004166148fd565b6130a391906148fd565b63ffffffff16905060006130ed6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b9050600061310e826130ff85876148a4565b6131099190614a92565b613753565b9050600061312a68ffffffffffffffffff808916908a16614a45565b90506131368183614a45565b9a9950505050505050505050565b600061314e610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161318199989796959493929190614c87565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133149190614d53565b905060003a82610120015183610100015161332f9190614e1b565b64ffffffffff1661334091906148a4565b905060008460ff166133886000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b6133929190614c73565b905060006133a36131098385614a92565b905060006133b03a613753565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff168961340f9190614a45565b338d6040518763ffffffff1660e01b815260040161343296959493929190614e39565b60408051808303816000875af1158015613450573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134749190614eb5565b9092509050600082600681111561348d5761348d614851565b14806134aa575060018260068111156134a8576134a8614851565b145b156136005760008e8152600760205260408120556134c88185614a45565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161353491859116614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b6135b39190614a45565b6135bd9190614a45565b6135c79190614a45565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b60004661361d81613787565b1561369957606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561366e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136929190614ee8565b9392505050565b6136a2816137aa565b1561374a5773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e84604051806080016040528060488152602001614f3160489139604051602001613702929190614f01565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161372d9190613bc8565b602060405180830381865afa15801561366e573d6000803e3d6000fd5b50600092915050565b600061378161376061240f565b61377284670de0b6b3a76400006148a4565b61377c9190614c73565b6137f1565b92915050565b600061a4b182148061379b575062066eed82145b8061378157505062066eee1490565b6000600a8214806137bc57506101a482145b806137c9575062aa37dc82145b806137d5575061210582145b806137e2575062014a3382145b8061378157505062014a341490565b60006bffffffffffffffffffffffff82111561388f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f8401126138c457600080fd5b50813567ffffffffffffffff8111156138dc57600080fd5b6020830191508360208285010111156138f457600080fd5b9250929050565b6000806020838503121561390e57600080fd5b823567ffffffffffffffff81111561392557600080fd5b613931858286016138b2565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156139905761399061393d565b60405290565b604051610160810167ffffffffffffffff811182821017156139905761399061393d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a0157613a0161393d565b604052919050565b63ffffffff8116811461267957600080fd5b803561117081613a09565b68ffffffffffffffffff8116811461267957600080fd5b803561117081613a26565b64ffffffffff8116811461267957600080fd5b803561117081613a48565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613ab757600080fd5b613abf61396c565b613ac883613a1b565b8152613ad660208401613a1b565b6020820152613ae760408401613a1b565b6040820152613af860608401613a1b565b6060820152613b0960808401613a3d565b6080820152613b1a60a08401613a5b565b60a0820152613b2b60c08401613a66565b60c0820152613b3c60e08401613a78565b60e0820152610100613b4f818501613a1b565b908201529392505050565b60005b83811015613b75578181015183820152602001613b5d565b50506000910152565b60008151808452613b96816020860160208601613b5a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006136926020830184613b7e565b600082601f830112613bec57600080fd5b813567ffffffffffffffff811115613c0657613c0661393d565b613c3760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016139ba565b818152846020838601011115613c4c57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613c7b57600080fd5b813567ffffffffffffffff811115613c9257600080fd5b613c9e84828501613bdb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b803561117081613ca6565b6bffffffffffffffffffffffff8116811461267957600080fd5b803561117081613cd3565b60008060408385031215613d0b57600080fd5b8235613d1681613ca6565b91506020830135613d2681613cd3565b809150509250929050565b600081518084526020808501945080840160005b83811015613d7757815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613d45565b509495945050505050565b6020815260006136926020830184613d31565b600060208284031215613da757600080fd5b5035919050565b600060208284031215613dc057600080fd5b813567ffffffffffffffff811115613dd757600080fd5b8201610160818503121561369257600080fd5b805182526020810151613e15602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613e3560408401826bffffffffffffffffffffffff169052565b506060810151613e5d606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613e79608084018267ffffffffffffffff169052565b5060a0810151613e9160a084018263ffffffff169052565b5060c0810151613eae60c084018268ffffffffffffffffff169052565b5060e0810151613ecb60e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016137818284613dea565b60008083601f840112613f2457600080fd5b50813567ffffffffffffffff811115613f3c57600080fd5b6020830191508360208260051b85010111156138f457600080fd5b60008060008060008060008060e0898b031215613f7357600080fd5b606089018a811115613f8457600080fd5b8998503567ffffffffffffffff80821115613f9e57600080fd5b613faa8c838d016138b2565b909950975060808b0135915080821115613fc357600080fd5b613fcf8c838d01613f12565b909750955060a08b0135915080821115613fe857600080fd5b50613ff58b828c01613f12565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151614062608084018268ffffffffffffffffff169052565b5060a083015161407b60a084018264ffffffffff169052565b5060c083015161409160c084018261ffff169052565b5060e08301516140c160e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b8035611170816140dd565b60008060008060006080868803121561411657600080fd5b8535614121816140dd565b9450602086013567ffffffffffffffff81111561413d57600080fd5b614149888289016138b2565b909550935050604086013561415d81613a09565b949793965091946060013592915050565b600067ffffffffffffffff8211156141885761418861393d565b5060051b60200190565b600082601f8301126141a357600080fd5b813560206141b86141b38361416e565b6139ba565b82815260059290921b840181019181810190868411156141d757600080fd5b8286015b848110156141fb5780356141ee81613ca6565b83529183019183016141db565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561423057600080fd5b863567ffffffffffffffff8082111561424857600080fd5b6142548a838b01614192565b9750602089013591508082111561426a57600080fd5b6142768a838b01614192565b965061428460408a01614206565b9550606089013591508082111561429a57600080fd5b6142a68a838b01613bdb565b94506142b460808a016140f3565b935060a08901359150808211156142ca57600080fd5b506142d789828a01613bdb565b9150509295509295509295565b6000602082840312156142f657600080fd5b813561369281613ca6565b600181811c9082168061431557607f821691505b60208210810361434e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c8101602086101561437b5750805b601f850160051c820191505b81811015610a8857828155600101614387565b67ffffffffffffffff8311156143b2576143b261393d565b6143c6836143c08354614301565b83614354565b6000601f84116001811461441857600085156143e25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556144ae565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156144675786850135825560209485019460019092019101614447565b50868210156144a2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613a26565b6000602082840312156144d257600080fd5b815161369281613a26565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115612661576126616144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614591576145916144dd565b5060010190565b600061016082360312156145ab57600080fd5b6145b3613996565b823567ffffffffffffffff8111156145ca57600080fd5b6145d636828601613bdb565b825250602083013560208201526145ef60408401613cc8565b604082015261460060608401613ced565b606082015261461160808401613a3d565b608082015261462260a084016140f3565b60a082015261463360c084016140f3565b60c082015261464460e08401613a1b565b60e0820152610100614657818501613a66565b908201526101206146698482016140f3565b9082015261014061467b848201613cc8565b9082015292915050565b60006020828403121561469757600080fd5b8135613692816140dd565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126146d757600080fd5b83018035915067ffffffffffffffff8211156146f257600080fd5b6020019150368190038213156138f457600080fd5b60006020828403121561471957600080fd5b61369282613a66565b60006020828403121561473457600080fd5b813561369281613a09565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061313660e0830184613dea565b60ff8181168382160190811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061484257614842614800565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b8082028115828204841417613781576137816144dd565b81810381811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff818116838216019080821115612661576126616144dd565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261494a8184018a613d31565b9050828103608084015261495e8189613d31565b905060ff871660a084015282810360c084015261497b8187613b7e565b905067ffffffffffffffff851660e08401528281036101008401526149a08185613b7e565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a086880312156149e257600080fd5b6149eb866149b0565b9450602086015193506040860151925060608601519150614a0e608087016149b0565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614a3957614a39614800565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115612661576126616144dd565b6bffffffffffffffffffffffff8181168382160280821691908281146140d5576140d56144dd565b80820180821115613781576137816144dd565b67ffffffffffffffff818116838216019080821115612661576126616144dd565b600082601f830112614ad757600080fd5b81356020614ae76141b38361416e565b82815260059290921b84018101918181019086841115614b0657600080fd5b8286015b848110156141fb5780358352918301918301614b0a565b600082601f830112614b3257600080fd5b81356020614b426141b38361416e565b82815260059290921b84018101918181019086841115614b6157600080fd5b8286015b848110156141fb57803567ffffffffffffffff811115614b855760008081fd5b614b938986838b0101613bdb565b845250918301918301614b65565b600080600080600060a08688031215614bb957600080fd5b853567ffffffffffffffff80821115614bd157600080fd5b614bdd89838a01614ac6565b96506020880135915080821115614bf357600080fd5b614bff89838a01614b21565b95506040880135915080821115614c1557600080fd5b614c2189838a01614b21565b94506060880135915080821115614c3757600080fd5b614c4389838a01614b21565b93506080880135915080821115614c5957600080fd5b50614c6688828901614b21565b9150509295509295909350565b600082614c8257614c82614800565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614cce8285018b613d31565b91508382036080850152614ce2828a613d31565b915060ff881660a085015283820360c0850152614cff8288613b7e565b90861660e085015283810361010085015290506149a08185613b7e565b805161117081613ca6565b805161117081613cd3565b8051611170816140dd565b805161117081613a09565b805161117081613a48565b60006101608284031215614d6657600080fd5b614d6e613996565b82518152614d7e60208401614d1c565b6020820152614d8f60408401614d27565b6040820152614da060608401614d1c565b6060820152614db160808401614d32565b6080820152614dc260a08401614d3d565b60a0820152614dd360c084016144b5565b60c0820152614de460e084016144b5565b60e0820152610100614df7818501614d48565b90820152610120614e09848201614d48565b90820152610140613b4f848201614d3d565b64ffffffffff818116838216019080821115612661576126616144dd565b6000610200808352614e4d8184018a613b7e565b90508281036020840152614e618189613b7e565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614eaa905060a0830184613dea565b979650505050505050565b60008060408385031215614ec857600080fd5b825160078110614ed757600080fd5b6020840151909250613d2681613cd3565b600060208284031215614efa57600080fd5b5051919050565b60008351614f13818460208801613b5a565b835190830190614f27818360208801613b5a565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", } var FunctionsCoordinatorABI = FunctionsCoordinatorMetaData.ABI @@ -1528,6 +1528,137 @@ func (_FunctionsCoordinator *FunctionsCoordinatorFilterer) ParseOwnershipTransfe return event, nil } +type FunctionsCoordinatorRequestBilledIterator struct { + Event *FunctionsCoordinatorRequestBilled + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *FunctionsCoordinatorRequestBilledIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(FunctionsCoordinatorRequestBilled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(FunctionsCoordinatorRequestBilled) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *FunctionsCoordinatorRequestBilledIterator) Error() error { + return it.fail +} + +func (it *FunctionsCoordinatorRequestBilledIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type FunctionsCoordinatorRequestBilled struct { + RequestId [32]byte + JuelsPerGas *big.Int + L1FeeShareWei *big.Int + CallbackCostJuels *big.Int + TotalCostJuels *big.Int + Raw types.Log +} + +func (_FunctionsCoordinator *FunctionsCoordinatorFilterer) FilterRequestBilled(opts *bind.FilterOpts, requestId [][32]byte) (*FunctionsCoordinatorRequestBilledIterator, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _FunctionsCoordinator.contract.FilterLogs(opts, "RequestBilled", requestIdRule) + if err != nil { + return nil, err + } + return &FunctionsCoordinatorRequestBilledIterator{contract: _FunctionsCoordinator.contract, event: "RequestBilled", logs: logs, sub: sub}, nil +} + +func (_FunctionsCoordinator *FunctionsCoordinatorFilterer) WatchRequestBilled(opts *bind.WatchOpts, sink chan<- *FunctionsCoordinatorRequestBilled, requestId [][32]byte) (event.Subscription, error) { + + var requestIdRule []interface{} + for _, requestIdItem := range requestId { + requestIdRule = append(requestIdRule, requestIdItem) + } + + logs, sub, err := _FunctionsCoordinator.contract.WatchLogs(opts, "RequestBilled", requestIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(FunctionsCoordinatorRequestBilled) + if err := _FunctionsCoordinator.contract.UnpackLog(event, "RequestBilled", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_FunctionsCoordinator *FunctionsCoordinatorFilterer) ParseRequestBilled(log types.Log) (*FunctionsCoordinatorRequestBilled, error) { + event := new(FunctionsCoordinatorRequestBilled) + if err := _FunctionsCoordinator.contract.UnpackLog(event, "RequestBilled", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + type FunctionsCoordinatorTransmittedIterator struct { Event *FunctionsCoordinatorTransmitted @@ -1673,6 +1804,8 @@ func (_FunctionsCoordinator *FunctionsCoordinator) ParseLog(log types.Log) (gene return _FunctionsCoordinator.ParseOwnershipTransferRequested(log) case _FunctionsCoordinator.abi.Events["OwnershipTransferred"].ID: return _FunctionsCoordinator.ParseOwnershipTransferred(log) + case _FunctionsCoordinator.abi.Events["RequestBilled"].ID: + return _FunctionsCoordinator.ParseRequestBilled(log) case _FunctionsCoordinator.abi.Events["Transmitted"].ID: return _FunctionsCoordinator.ParseTransmitted(log) @@ -1709,6 +1842,10 @@ func (FunctionsCoordinatorOwnershipTransferred) Topic() common.Hash { return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") } +func (FunctionsCoordinatorRequestBilled) Topic() common.Hash { + return common.HexToHash("0x90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e046") +} + func (FunctionsCoordinatorTransmitted) Topic() common.Hash { return common.HexToHash("0xb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62") } @@ -1810,6 +1947,12 @@ type FunctionsCoordinatorInterface interface { ParseOwnershipTransferred(log types.Log) (*FunctionsCoordinatorOwnershipTransferred, error) + FilterRequestBilled(opts *bind.FilterOpts, requestId [][32]byte) (*FunctionsCoordinatorRequestBilledIterator, error) + + WatchRequestBilled(opts *bind.WatchOpts, sink chan<- *FunctionsCoordinatorRequestBilled, requestId [][32]byte) (event.Subscription, error) + + ParseRequestBilled(log types.Log) (*FunctionsCoordinatorRequestBilled, error) + FilterTransmitted(opts *bind.FilterOpts) (*FunctionsCoordinatorTransmittedIterator, error) WatchTransmitted(opts *bind.WatchOpts, sink chan<- *FunctionsCoordinatorTransmitted) (event.Subscription, error) diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 3543116d21d..41524c3a829 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -4,7 +4,7 @@ functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServ functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b -functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 948c04942910f308942fdde460317f9ec038b6b702b018471ce6157a14a09072 +functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 96416d5be2ae4625395567397da88f71b215005cf8ad71a1cdaa56e6b5e16908 functions_load_test_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.bin c8dbbd5ebb34435800d6674700068837c3a252db60046a14b0e61e829db517de functions_oracle_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 9dedd3a36043605fd9bedf821e7ec5b4281a5c7ae2e4a1955f37aff8ba13519f From 22d77f95e1355986120580576632e9880ce36ffe Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Thu, 9 Nov 2023 19:53:33 -0500 Subject: [PATCH 123/271] FUN-958 - Golf golf changes, store repeated .length as a variable (#11251) --- .../gas-snapshots/functions.gas-snapshot | 52 +++++++++---------- .../functions/dev/v1_X/FunctionsBilling.sol | 9 ++-- .../dev/v1_X/FunctionsCoordinator.sol | 15 +++--- .../functions_coordinator.go | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- 5 files changed, 41 insertions(+), 39 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index af95b75df10..d15666f8857 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14534216) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14534194) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14534210) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14545630) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14545607) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14545579) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14545530) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14545519) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14545563) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14534206) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14534184) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14534200) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14545620) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14545597) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14545569) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14545520) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14545509) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14545553) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -17,20 +17,20 @@ FunctionsBilling_GetAdminFee:test_GetAdminFee_Success() (gas: 18226) FunctionsBilling_GetConfig:test_GetConfig_Success() (gas: 23671) FunctionsBilling_GetDONFee:test_GetDONFee_Success() (gas: 15792) FunctionsBilling_GetWeiPerUnitLink:test_GetWeiPerUnitLink_Success() (gas: 31773) -FunctionsBilling_OracleWithdraw:test_OracleWithdraw_RevertIfInsufficientBalance() (gas: 70138) -FunctionsBilling_OracleWithdraw:test_OracleWithdraw_RevertWithNoBalance() (gas: 106295) -FunctionsBilling_OracleWithdraw:test_OracleWithdraw_SuccessTransmitterWithBalanceNoAmountGiven() (gas: 140174) -FunctionsBilling_OracleWithdraw:test_OracleWithdraw_SuccessTransmitterWithBalanceValidAmountGiven() (gas: 142502) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_RevertIfInsufficientBalance() (gas: 70128) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_RevertWithNoBalance() (gas: 106285) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_SuccessTransmitterWithBalanceNoAmountGiven() (gas: 140164) +FunctionsBilling_OracleWithdraw:test_OracleWithdraw_SuccessTransmitterWithBalanceValidAmountGiven() (gas: 142492) FunctionsBilling_OracleWithdrawAll:test_OracleWithdrawAll_RevertIfNotOwner() (gas: 13296) FunctionsBilling_OracleWithdrawAll:test_OracleWithdrawAll_SuccessPaysTransmittersWithBalance() (gas: 147278) FunctionsBilling_UpdateConfig:test_UpdateConfig_RevertIfNotOwner() (gas: 18974) FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 38251) -FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8801) +FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8810) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_RevertIfInvalidCommitment() (gas: 13302) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_Success() (gas: 180763) FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) -FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 504364) -FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 205568) +FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 504354) +FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 205558) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_RevertIfNotRouter() (gas: 14623) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_Success() (gas: 22923) FunctionsClient__SendRequest:test__SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 55059) @@ -51,17 +51,17 @@ FunctionsRequest_DEFAULT_BUFFER_SIZE:test_DEFAULT_BUFFER_SIZE() (gas: 246) FunctionsRequest_EncodeCBOR:test_EncodeCBOR_Success() (gas: 223) FunctionsRequest_REQUEST_DATA_VERSION:test_REQUEST_DATA_VERSION() (gas: 225) FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12007) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 174037) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 164368) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 174027) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 164358) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38115) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35238) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 182513) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 182503) FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28086) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 158055) -FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 327615) -FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 341236) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2516517) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 546996) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 158046) +FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 327605) +FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 341226) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2516507) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 546986) FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37159) @@ -144,8 +144,8 @@ FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 1501 FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43685, ~: 45548) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46197, ~: 48060) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51089, ~: 53040) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51177, ~: 53040) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 85879, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index bd13a3a5a1a..e4b6fe7d90c 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -374,15 +374,16 @@ abstract contract FunctionsBilling is Routable, IFunctionsBilling { // All transmitters are assumed to also be observers // Pay out the DON fee to all transmitters address[] memory transmitters = _getTransmitters(); - if (transmitters.length == 0) { + uint256 numberOfTransmitters = transmitters.length; + if (numberOfTransmitters == 0) { revert NoTransmittersSet(); } - uint96 feePoolShare = s_feePool / uint96(transmitters.length); + uint96 feePoolShare = s_feePool / uint96(numberOfTransmitters); // Bounded by "maxNumOracles" on OCR2Abstract.sol - for (uint256 i = 0; i < transmitters.length; ++i) { + for (uint256 i = 0; i < numberOfTransmitters; ++i) { s_withdrawableTokens[transmitters[i]] += feePoolShare; } - s_feePool -= feePoolShare * uint96(transmitters.length); + s_feePool -= feePoolShare * uint96(numberOfTransmitters); } // Overriden in FunctionsCoordinator.sol diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol index 2caab41c746..3ee4931e97c 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol @@ -142,19 +142,20 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli report, (bytes32[], bytes[], bytes[], bytes[], bytes[]) ); + uint256 numberOfFulfillments = uint8(requestIds.length); if ( - requestIds.length == 0 || - requestIds.length != results.length || - requestIds.length != errors.length || - requestIds.length != onchainMetadata.length || - requestIds.length != offchainMetadata.length + numberOfFulfillments == 0 || + numberOfFulfillments != results.length || + numberOfFulfillments != errors.length || + numberOfFulfillments != onchainMetadata.length || + numberOfFulfillments != offchainMetadata.length ) { revert ReportInvalid(); } // Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig - for (uint256 i = 0; i < requestIds.length; ++i) { + for (uint256 i = 0; i < numberOfFulfillments; ++i) { FunctionsResponse.FulfillResult result = FunctionsResponse.FulfillResult( _fulfillAndBill( requestIds[i], @@ -162,7 +163,7 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli errors[i], onchainMetadata[i], offchainMetadata[i], - uint8(requestIds.length) // will not exceed "MaxRequestBatchSize" on the Job's ReportingPluginConfig + uint8(numberOfFulfillments) // will not exceed "MaxRequestBatchSize" on the Job's ReportingPluginConfig ) ); diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index 397ea3d18bb..917107524bf 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -72,7 +72,7 @@ type FunctionsResponseRequestMeta struct { var FunctionsCoordinatorMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60c06040523480156200001157600080fd5b506040516200560838038062005608833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614f856200068360003960008181610845015281816109d301528181610ca601528181610f3a015281816110450152818161183001526133b70152600061126e0152614f856000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046138fb565b61059c565b005b6101a56101b5366004613aa4565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613bc8565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613c69565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613cf8565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046138fb565b610d92565b6102a0610de2565b6040516102039190613d82565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613d95565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613dae565b610fd4565b6040516102039190613f03565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613f57565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b604051610203919061400e565b61053b6105363660046140fe565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004614217565b6119e3565b61057b61240f565b604051908152602001610203565b6101a56105973660046142e4565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec82848361439a565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f51848629061083690839061400e565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d291906144c0565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd2614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c31614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf5614531565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614560565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec82848361439a565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e6090614301565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea890614301565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed490614301565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614598565b6128b2565b90506110bf60608301604084016142e4565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a08801614685565b61111f610160880161014089016142e4565b61112988806146a2565b61113b6101208b016101008c01614707565b60208b01356111516101008d0160e08e01614722565b8b6040516111679998979695949392919061473f565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d50565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a891906147e7565b6112b2919061482f565b6112bd9060016147e7565b60ff1690506112dd565b60208201516112d79060016147e7565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f2614851565b600281111561140357611403614851565b905250905060028160200151600281111561142057611420614851565b14801561146757506006816000015160ff168154811061144257611442614531565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da613893565b6000808a8a6040516114ed929190614880565b604051908190038120611504918e90602001614890565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d614531565b61157a91901a601b6147e7565b8e8e8681811061158c5761158c614531565b905060200201358d8d878181106115a5576115a5614531565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff8082168552929650929450840191610100900416600281111561168457611684614851565b600281111561169557611695614851565b90525092506001836020015160028111156116b2576116b2614851565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061173357611733614531565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf614531565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa6001866147e7565b9450508061180790614560565b905061154e565b50505061181f833383858e8e612e07565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd7565b98975050505050505050565b6060600c805461199b90614301565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea890614301565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b598160036148a4565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c109088613144565b60055415611dc557600554600090611c2a906001906148bb565b9050600060058281548110611c4157611c41614531565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b614531565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb6148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d646148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e38614851565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed0614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f71614851565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe5614851565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561212057612120614851565b02179055505082518051600592508390811061213e5761213e614531565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba614531565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790558061222481614560565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e4918491740100000000000000000000000000000000000000009004166148fd565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a0015161315d565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff1696909591949193919261491a565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c191906149ca565b5093505092505080426125d491906148bb565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b61267981613208565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b90508051600003612768576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8051600b54600091612787916bffffffffffffffffffffffff16614a1a565b905060005b82518110156128535781600a60008584815181106127ac576127ac614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128149190614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284c90614560565b905061278c565b5081516128609082614a6a565b600b80546000906128809084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6d576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aaa8560e001513a848860800151612fd7565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b06576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b1f9190614a92565b905060003087604001518860a001518960c001516001612b3f9190614aa5565b8a5180516020918201206101008d015160e08e0151604051612bf398979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d029190613f03565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5d8260206148a4565b612d688560206148a4565b612d7488610144614a92565b612d7e9190614a92565b612d889190614a92565b612d93906000614a92565b9050368114612dfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e1986880188614ba1565b8451949950929750909550935091501580612e3657508351855114155b80612e4357508251855114155b80612e5057508151855114155b80612e5d57508051855114155b15612e94576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8551811015612fc9576000612f2e878381518110612eb757612eb7614531565b6020026020010151878481518110612ed157612ed1614531565b6020026020010151878581518110612eeb57612eeb614531565b6020026020010151878681518110612f0557612f05614531565b6020026020010151878781518110612f1f57612f1f614531565b60200260200101518c516132fd565b90506000816006811115612f4457612f44614851565b1480612f6157506001816006811115612f5f57612f5f614851565b145b15612fb857868281518110612f7857612f78614531565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc281614560565b9050612e97565b505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561303257600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b6008546000906127109061304c9063ffffffff16876148a4565b6130569190614c73565b6130609086614a92565b60085490915060009087906130999063ffffffff6c010000000000000000000000008204811691680100000000000000009004166148fd565b6130a391906148fd565b63ffffffff16905060006130ed6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b9050600061310e826130ff85876148a4565b6131099190614a92565b613753565b9050600061312a68ffffffffffffffffff808916908a16614a45565b90506131368183614a45565b9a9950505050505050505050565b600061314e610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161318199989796959493929190614c87565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133149190614d53565b905060003a82610120015183610100015161332f9190614e1b565b64ffffffffff1661334091906148a4565b905060008460ff166133886000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b6133929190614c73565b905060006133a36131098385614a92565b905060006133b03a613753565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff168961340f9190614a45565b338d6040518763ffffffff1660e01b815260040161343296959493929190614e39565b60408051808303816000875af1158015613450573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134749190614eb5565b9092509050600082600681111561348d5761348d614851565b14806134aa575060018260068111156134a8576134a8614851565b145b156136005760008e8152600760205260408120556134c88185614a45565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161353491859116614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b6135b39190614a45565b6135bd9190614a45565b6135c79190614a45565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b60004661361d81613787565b1561369957606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561366e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136929190614ee8565b9392505050565b6136a2816137aa565b1561374a5773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e84604051806080016040528060488152602001614f3160489139604051602001613702929190614f01565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161372d9190613bc8565b602060405180830381865afa15801561366e573d6000803e3d6000fd5b50600092915050565b600061378161376061240f565b61377284670de0b6b3a76400006148a4565b61377c9190614c73565b6137f1565b92915050565b600061a4b182148061379b575062066eed82145b8061378157505062066eee1490565b6000600a8214806137bc57506101a482145b806137c9575062aa37dc82145b806137d5575061210582145b806137e2575062014a3382145b8061378157505062014a341490565b60006bffffffffffffffffffffffff82111561388f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f8401126138c457600080fd5b50813567ffffffffffffffff8111156138dc57600080fd5b6020830191508360208285010111156138f457600080fd5b9250929050565b6000806020838503121561390e57600080fd5b823567ffffffffffffffff81111561392557600080fd5b613931858286016138b2565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156139905761399061393d565b60405290565b604051610160810167ffffffffffffffff811182821017156139905761399061393d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a0157613a0161393d565b604052919050565b63ffffffff8116811461267957600080fd5b803561117081613a09565b68ffffffffffffffffff8116811461267957600080fd5b803561117081613a26565b64ffffffffff8116811461267957600080fd5b803561117081613a48565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613ab757600080fd5b613abf61396c565b613ac883613a1b565b8152613ad660208401613a1b565b6020820152613ae760408401613a1b565b6040820152613af860608401613a1b565b6060820152613b0960808401613a3d565b6080820152613b1a60a08401613a5b565b60a0820152613b2b60c08401613a66565b60c0820152613b3c60e08401613a78565b60e0820152610100613b4f818501613a1b565b908201529392505050565b60005b83811015613b75578181015183820152602001613b5d565b50506000910152565b60008151808452613b96816020860160208601613b5a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006136926020830184613b7e565b600082601f830112613bec57600080fd5b813567ffffffffffffffff811115613c0657613c0661393d565b613c3760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016139ba565b818152846020838601011115613c4c57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613c7b57600080fd5b813567ffffffffffffffff811115613c9257600080fd5b613c9e84828501613bdb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b803561117081613ca6565b6bffffffffffffffffffffffff8116811461267957600080fd5b803561117081613cd3565b60008060408385031215613d0b57600080fd5b8235613d1681613ca6565b91506020830135613d2681613cd3565b809150509250929050565b600081518084526020808501945080840160005b83811015613d7757815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613d45565b509495945050505050565b6020815260006136926020830184613d31565b600060208284031215613da757600080fd5b5035919050565b600060208284031215613dc057600080fd5b813567ffffffffffffffff811115613dd757600080fd5b8201610160818503121561369257600080fd5b805182526020810151613e15602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613e3560408401826bffffffffffffffffffffffff169052565b506060810151613e5d606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613e79608084018267ffffffffffffffff169052565b5060a0810151613e9160a084018263ffffffff169052565b5060c0810151613eae60c084018268ffffffffffffffffff169052565b5060e0810151613ecb60e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016137818284613dea565b60008083601f840112613f2457600080fd5b50813567ffffffffffffffff811115613f3c57600080fd5b6020830191508360208260051b85010111156138f457600080fd5b60008060008060008060008060e0898b031215613f7357600080fd5b606089018a811115613f8457600080fd5b8998503567ffffffffffffffff80821115613f9e57600080fd5b613faa8c838d016138b2565b909950975060808b0135915080821115613fc357600080fd5b613fcf8c838d01613f12565b909750955060a08b0135915080821115613fe857600080fd5b50613ff58b828c01613f12565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151614062608084018268ffffffffffffffffff169052565b5060a083015161407b60a084018264ffffffffff169052565b5060c083015161409160c084018261ffff169052565b5060e08301516140c160e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b8035611170816140dd565b60008060008060006080868803121561411657600080fd5b8535614121816140dd565b9450602086013567ffffffffffffffff81111561413d57600080fd5b614149888289016138b2565b909550935050604086013561415d81613a09565b949793965091946060013592915050565b600067ffffffffffffffff8211156141885761418861393d565b5060051b60200190565b600082601f8301126141a357600080fd5b813560206141b86141b38361416e565b6139ba565b82815260059290921b840181019181810190868411156141d757600080fd5b8286015b848110156141fb5780356141ee81613ca6565b83529183019183016141db565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561423057600080fd5b863567ffffffffffffffff8082111561424857600080fd5b6142548a838b01614192565b9750602089013591508082111561426a57600080fd5b6142768a838b01614192565b965061428460408a01614206565b9550606089013591508082111561429a57600080fd5b6142a68a838b01613bdb565b94506142b460808a016140f3565b935060a08901359150808211156142ca57600080fd5b506142d789828a01613bdb565b9150509295509295509295565b6000602082840312156142f657600080fd5b813561369281613ca6565b600181811c9082168061431557607f821691505b60208210810361434e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c8101602086101561437b5750805b601f850160051c820191505b81811015610a8857828155600101614387565b67ffffffffffffffff8311156143b2576143b261393d565b6143c6836143c08354614301565b83614354565b6000601f84116001811461441857600085156143e25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556144ae565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156144675786850135825560209485019460019092019101614447565b50868210156144a2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613a26565b6000602082840312156144d257600080fd5b815161369281613a26565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115612661576126616144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614591576145916144dd565b5060010190565b600061016082360312156145ab57600080fd5b6145b3613996565b823567ffffffffffffffff8111156145ca57600080fd5b6145d636828601613bdb565b825250602083013560208201526145ef60408401613cc8565b604082015261460060608401613ced565b606082015261461160808401613a3d565b608082015261462260a084016140f3565b60a082015261463360c084016140f3565b60c082015261464460e08401613a1b565b60e0820152610100614657818501613a66565b908201526101206146698482016140f3565b9082015261014061467b848201613cc8565b9082015292915050565b60006020828403121561469757600080fd5b8135613692816140dd565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126146d757600080fd5b83018035915067ffffffffffffffff8211156146f257600080fd5b6020019150368190038213156138f457600080fd5b60006020828403121561471957600080fd5b61369282613a66565b60006020828403121561473457600080fd5b813561369281613a09565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061313660e0830184613dea565b60ff8181168382160190811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061484257614842614800565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b8082028115828204841417613781576137816144dd565b81810381811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff818116838216019080821115612661576126616144dd565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261494a8184018a613d31565b9050828103608084015261495e8189613d31565b905060ff871660a084015282810360c084015261497b8187613b7e565b905067ffffffffffffffff851660e08401528281036101008401526149a08185613b7e565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a086880312156149e257600080fd5b6149eb866149b0565b9450602086015193506040860151925060608601519150614a0e608087016149b0565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614a3957614a39614800565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115612661576126616144dd565b6bffffffffffffffffffffffff8181168382160280821691908281146140d5576140d56144dd565b80820180821115613781576137816144dd565b67ffffffffffffffff818116838216019080821115612661576126616144dd565b600082601f830112614ad757600080fd5b81356020614ae76141b38361416e565b82815260059290921b84018101918181019086841115614b0657600080fd5b8286015b848110156141fb5780358352918301918301614b0a565b600082601f830112614b3257600080fd5b81356020614b426141b38361416e565b82815260059290921b84018101918181019086841115614b6157600080fd5b8286015b848110156141fb57803567ffffffffffffffff811115614b855760008081fd5b614b938986838b0101613bdb565b845250918301918301614b65565b600080600080600060a08688031215614bb957600080fd5b853567ffffffffffffffff80821115614bd157600080fd5b614bdd89838a01614ac6565b96506020880135915080821115614bf357600080fd5b614bff89838a01614b21565b95506040880135915080821115614c1557600080fd5b614c2189838a01614b21565b94506060880135915080821115614c3757600080fd5b614c4389838a01614b21565b93506080880135915080821115614c5957600080fd5b50614c6688828901614b21565b9150509295509295909350565b600082614c8257614c82614800565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614cce8285018b613d31565b91508382036080850152614ce2828a613d31565b915060ff881660a085015283820360c0850152614cff8288613b7e565b90861660e085015283810361010085015290506149a08185613b7e565b805161117081613ca6565b805161117081613cd3565b8051611170816140dd565b805161117081613a09565b805161117081613a48565b60006101608284031215614d6657600080fd5b614d6e613996565b82518152614d7e60208401614d1c565b6020820152614d8f60408401614d27565b6040820152614da060608401614d1c565b6060820152614db160808401614d32565b6080820152614dc260a08401614d3d565b60a0820152614dd360c084016144b5565b60c0820152614de460e084016144b5565b60e0820152610100614df7818501614d48565b90820152610120614e09848201614d48565b90820152610140613b4f848201614d3d565b64ffffffffff818116838216019080821115612661576126616144dd565b6000610200808352614e4d8184018a613b7e565b90508281036020840152614e618189613b7e565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614eaa905060a0830184613dea565b979650505050505050565b60008060408385031215614ec857600080fd5b825160078110614ed757600080fd5b6020840151909250613d2681613cd3565b600060208284031215614efa57600080fd5b5051919050565b60008351614f13818460208801613b5a565b835190830190614f27818360208801613b5a565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", + Bin: "0x60c06040523480156200001157600080fd5b506040516200560838038062005608833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614f856200068360003960008181610845015281816109d301528181610ca601528181610f3a015281816110450152818161183001526133b70152600061126e0152614f856000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046138fb565b61059c565b005b6101a56101b5366004613aa4565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613bc8565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613c69565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613cf8565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046138fb565b610d92565b6102a0610de2565b6040516102039190613d82565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613d95565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613dae565b610fd4565b6040516102039190613f03565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613f57565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b604051610203919061400e565b61053b6105363660046140fe565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004614217565b6119e3565b61057b61240f565b604051908152602001610203565b6101a56105973660046142e4565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec82848361439a565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f51848629061083690839061400e565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d291906144c0565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd2614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c31614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf5614531565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614560565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec82848361439a565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e6090614301565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea890614301565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed490614301565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614598565b6128b3565b90506110bf60608301604084016142e4565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a08801614685565b61111f610160880161014089016142e4565b61112988806146a2565b61113b6101208b016101008c01614707565b60208b01356111516101008d0160e08e01614722565b8b6040516111679998979695949392919061473f565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d51565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a891906147e7565b6112b2919061482f565b6112bd9060016147e7565b60ff1690506112dd565b60208201516112d79060016147e7565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f2614851565b600281111561140357611403614851565b905250905060028160200151600281111561142057611420614851565b14801561146757506006816000015160ff168154811061144257611442614531565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da613893565b6000808a8a6040516114ed929190614880565b604051908190038120611504918e90602001614890565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d614531565b61157a91901a601b6147e7565b8e8e8681811061158c5761158c614531565b905060200201358d8d878181106115a5576115a5614531565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff8082168552929650929450840191610100900416600281111561168457611684614851565b600281111561169557611695614851565b90525092506001836020015160028111156116b2576116b2614851565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061173357611733614531565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf614531565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa6001866147e7565b9450508061180790614560565b905061154e565b50505061181f833383858e8e612e08565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd7565b98975050505050505050565b6060600c805461199b90614301565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea890614301565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b598160036148a4565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c109088613144565b60055415611dc557600554600090611c2a906001906148bb565b9050600060058281548110611c4157611c41614531565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b614531565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb6148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d646148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e38614851565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed0614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f71614851565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe5614851565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561212057612120614851565b02179055505082518051600592508390811061213e5761213e614531565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba614531565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790558061222481614560565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e4918491740100000000000000000000000000000000000000009004166148fd565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a0015161315d565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff1696909591949193919261491a565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c191906149ca565b5093505092505080426125d491906148bb565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b61267981613208565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b8051909150600081900361276b576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b5460009061278a9083906bffffffffffffffffffffffff16614a1a565b905060005b828110156128555781600a60008684815181106127ae576127ae614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128169190614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284e90614560565b905061278f565b506128608282614a6a565b600b80546000906128809084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6e576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aab8560e001513a848860800151612fd7565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b07576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b209190614a92565b905060003087604001518860a001518960c001516001612b409190614aa5565b8a5180516020918201206101008d015160e08e0151604051612bf498979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d039190613f03565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5e8260206148a4565b612d698560206148a4565b612d7588610144614a92565b612d7f9190614a92565b612d899190614a92565b612d94906000614a92565b9050368114612dff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e1a86880188614ba1565b84519499509297509095509350915060ff16801580612e3a575084518114155b80612e46575083518114155b80612e52575082518114155b80612e5e575081518114155b15612e95576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015612fc8576000612f2d888381518110612eb757612eb7614531565b6020026020010151888481518110612ed157612ed1614531565b6020026020010151888581518110612eeb57612eeb614531565b6020026020010151888681518110612f0557612f05614531565b6020026020010151888781518110612f1f57612f1f614531565b6020026020010151886132fd565b90506000816006811115612f4357612f43614851565b1480612f6057506001816006811115612f5e57612f5e614851565b145b15612fb757878281518110612f7757612f77614531565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc181614560565b9050612e98565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561303257600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b6008546000906127109061304c9063ffffffff16876148a4565b6130569190614c73565b6130609086614a92565b60085490915060009087906130999063ffffffff6c010000000000000000000000008204811691680100000000000000009004166148fd565b6130a391906148fd565b63ffffffff16905060006130ed6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b9050600061310e826130ff85876148a4565b6131099190614a92565b613753565b9050600061312a68ffffffffffffffffff808916908a16614a45565b90506131368183614a45565b9a9950505050505050505050565b600061314e610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161318199989796959493929190614c87565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133149190614d53565b905060003a82610120015183610100015161332f9190614e1b565b64ffffffffff1661334091906148a4565b905060008460ff166133886000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b6133929190614c73565b905060006133a36131098385614a92565b905060006133b03a613753565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff168961340f9190614a45565b338d6040518763ffffffff1660e01b815260040161343296959493929190614e39565b60408051808303816000875af1158015613450573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134749190614eb5565b9092509050600082600681111561348d5761348d614851565b14806134aa575060018260068111156134a8576134a8614851565b145b156136005760008e8152600760205260408120556134c88185614a45565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161353491859116614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b6135b39190614a45565b6135bd9190614a45565b6135c79190614a45565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b60004661361d81613787565b1561369957606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561366e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136929190614ee8565b9392505050565b6136a2816137aa565b1561374a5773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e84604051806080016040528060488152602001614f3160489139604051602001613702929190614f01565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161372d9190613bc8565b602060405180830381865afa15801561366e573d6000803e3d6000fd5b50600092915050565b600061378161376061240f565b61377284670de0b6b3a76400006148a4565b61377c9190614c73565b6137f1565b92915050565b600061a4b182148061379b575062066eed82145b8061378157505062066eee1490565b6000600a8214806137bc57506101a482145b806137c9575062aa37dc82145b806137d5575061210582145b806137e2575062014a3382145b8061378157505062014a341490565b60006bffffffffffffffffffffffff82111561388f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f8401126138c457600080fd5b50813567ffffffffffffffff8111156138dc57600080fd5b6020830191508360208285010111156138f457600080fd5b9250929050565b6000806020838503121561390e57600080fd5b823567ffffffffffffffff81111561392557600080fd5b613931858286016138b2565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156139905761399061393d565b60405290565b604051610160810167ffffffffffffffff811182821017156139905761399061393d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a0157613a0161393d565b604052919050565b63ffffffff8116811461267957600080fd5b803561117081613a09565b68ffffffffffffffffff8116811461267957600080fd5b803561117081613a26565b64ffffffffff8116811461267957600080fd5b803561117081613a48565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613ab757600080fd5b613abf61396c565b613ac883613a1b565b8152613ad660208401613a1b565b6020820152613ae760408401613a1b565b6040820152613af860608401613a1b565b6060820152613b0960808401613a3d565b6080820152613b1a60a08401613a5b565b60a0820152613b2b60c08401613a66565b60c0820152613b3c60e08401613a78565b60e0820152610100613b4f818501613a1b565b908201529392505050565b60005b83811015613b75578181015183820152602001613b5d565b50506000910152565b60008151808452613b96816020860160208601613b5a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006136926020830184613b7e565b600082601f830112613bec57600080fd5b813567ffffffffffffffff811115613c0657613c0661393d565b613c3760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016139ba565b818152846020838601011115613c4c57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613c7b57600080fd5b813567ffffffffffffffff811115613c9257600080fd5b613c9e84828501613bdb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b803561117081613ca6565b6bffffffffffffffffffffffff8116811461267957600080fd5b803561117081613cd3565b60008060408385031215613d0b57600080fd5b8235613d1681613ca6565b91506020830135613d2681613cd3565b809150509250929050565b600081518084526020808501945080840160005b83811015613d7757815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613d45565b509495945050505050565b6020815260006136926020830184613d31565b600060208284031215613da757600080fd5b5035919050565b600060208284031215613dc057600080fd5b813567ffffffffffffffff811115613dd757600080fd5b8201610160818503121561369257600080fd5b805182526020810151613e15602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613e3560408401826bffffffffffffffffffffffff169052565b506060810151613e5d606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613e79608084018267ffffffffffffffff169052565b5060a0810151613e9160a084018263ffffffff169052565b5060c0810151613eae60c084018268ffffffffffffffffff169052565b5060e0810151613ecb60e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016137818284613dea565b60008083601f840112613f2457600080fd5b50813567ffffffffffffffff811115613f3c57600080fd5b6020830191508360208260051b85010111156138f457600080fd5b60008060008060008060008060e0898b031215613f7357600080fd5b606089018a811115613f8457600080fd5b8998503567ffffffffffffffff80821115613f9e57600080fd5b613faa8c838d016138b2565b909950975060808b0135915080821115613fc357600080fd5b613fcf8c838d01613f12565b909750955060a08b0135915080821115613fe857600080fd5b50613ff58b828c01613f12565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151614062608084018268ffffffffffffffffff169052565b5060a083015161407b60a084018264ffffffffff169052565b5060c083015161409160c084018261ffff169052565b5060e08301516140c160e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b8035611170816140dd565b60008060008060006080868803121561411657600080fd5b8535614121816140dd565b9450602086013567ffffffffffffffff81111561413d57600080fd5b614149888289016138b2565b909550935050604086013561415d81613a09565b949793965091946060013592915050565b600067ffffffffffffffff8211156141885761418861393d565b5060051b60200190565b600082601f8301126141a357600080fd5b813560206141b86141b38361416e565b6139ba565b82815260059290921b840181019181810190868411156141d757600080fd5b8286015b848110156141fb5780356141ee81613ca6565b83529183019183016141db565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561423057600080fd5b863567ffffffffffffffff8082111561424857600080fd5b6142548a838b01614192565b9750602089013591508082111561426a57600080fd5b6142768a838b01614192565b965061428460408a01614206565b9550606089013591508082111561429a57600080fd5b6142a68a838b01613bdb565b94506142b460808a016140f3565b935060a08901359150808211156142ca57600080fd5b506142d789828a01613bdb565b9150509295509295509295565b6000602082840312156142f657600080fd5b813561369281613ca6565b600181811c9082168061431557607f821691505b60208210810361434e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c8101602086101561437b5750805b601f850160051c820191505b81811015610a8857828155600101614387565b67ffffffffffffffff8311156143b2576143b261393d565b6143c6836143c08354614301565b83614354565b6000601f84116001811461441857600085156143e25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556144ae565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156144675786850135825560209485019460019092019101614447565b50868210156144a2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613a26565b6000602082840312156144d257600080fd5b815161369281613a26565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115612661576126616144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614591576145916144dd565b5060010190565b600061016082360312156145ab57600080fd5b6145b3613996565b823567ffffffffffffffff8111156145ca57600080fd5b6145d636828601613bdb565b825250602083013560208201526145ef60408401613cc8565b604082015261460060608401613ced565b606082015261461160808401613a3d565b608082015261462260a084016140f3565b60a082015261463360c084016140f3565b60c082015261464460e08401613a1b565b60e0820152610100614657818501613a66565b908201526101206146698482016140f3565b9082015261014061467b848201613cc8565b9082015292915050565b60006020828403121561469757600080fd5b8135613692816140dd565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126146d757600080fd5b83018035915067ffffffffffffffff8211156146f257600080fd5b6020019150368190038213156138f457600080fd5b60006020828403121561471957600080fd5b61369282613a66565b60006020828403121561473457600080fd5b813561369281613a09565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061313660e0830184613dea565b60ff8181168382160190811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061484257614842614800565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b8082028115828204841417613781576137816144dd565b81810381811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff818116838216019080821115612661576126616144dd565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261494a8184018a613d31565b9050828103608084015261495e8189613d31565b905060ff871660a084015282810360c084015261497b8187613b7e565b905067ffffffffffffffff851660e08401528281036101008401526149a08185613b7e565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a086880312156149e257600080fd5b6149eb866149b0565b9450602086015193506040860151925060608601519150614a0e608087016149b0565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614a3957614a39614800565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115612661576126616144dd565b6bffffffffffffffffffffffff8181168382160280821691908281146140d5576140d56144dd565b80820180821115613781576137816144dd565b67ffffffffffffffff818116838216019080821115612661576126616144dd565b600082601f830112614ad757600080fd5b81356020614ae76141b38361416e565b82815260059290921b84018101918181019086841115614b0657600080fd5b8286015b848110156141fb5780358352918301918301614b0a565b600082601f830112614b3257600080fd5b81356020614b426141b38361416e565b82815260059290921b84018101918181019086841115614b6157600080fd5b8286015b848110156141fb57803567ffffffffffffffff811115614b855760008081fd5b614b938986838b0101613bdb565b845250918301918301614b65565b600080600080600060a08688031215614bb957600080fd5b853567ffffffffffffffff80821115614bd157600080fd5b614bdd89838a01614ac6565b96506020880135915080821115614bf357600080fd5b614bff89838a01614b21565b95506040880135915080821115614c1557600080fd5b614c2189838a01614b21565b94506060880135915080821115614c3757600080fd5b614c4389838a01614b21565b93506080880135915080821115614c5957600080fd5b50614c6688828901614b21565b9150509295509295909350565b600082614c8257614c82614800565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614cce8285018b613d31565b91508382036080850152614ce2828a613d31565b915060ff881660a085015283820360c0850152614cff8288613b7e565b90861660e085015283810361010085015290506149a08185613b7e565b805161117081613ca6565b805161117081613cd3565b8051611170816140dd565b805161117081613a09565b805161117081613a48565b60006101608284031215614d6657600080fd5b614d6e613996565b82518152614d7e60208401614d1c565b6020820152614d8f60408401614d27565b6040820152614da060608401614d1c565b6060820152614db160808401614d32565b6080820152614dc260a08401614d3d565b60a0820152614dd360c084016144b5565b60c0820152614de460e084016144b5565b60e0820152610100614df7818501614d48565b90820152610120614e09848201614d48565b90820152610140613b4f848201614d3d565b64ffffffffff818116838216019080821115612661576126616144dd565b6000610200808352614e4d8184018a613b7e565b90508281036020840152614e618189613b7e565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614eaa905060a0830184613dea565b979650505050505050565b60008060408385031215614ec857600080fd5b825160078110614ed757600080fd5b6020840151909250613d2681613cd3565b600060208284031215614efa57600080fd5b5051919050565b60008351614f13818460208801613b5a565b835190830190614f27818360208801613b5a565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", } var FunctionsCoordinatorABI = FunctionsCoordinatorMetaData.ABI diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 41524c3a829..534e5543e86 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -4,7 +4,7 @@ functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServ functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b -functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 96416d5be2ae4625395567397da88f71b215005cf8ad71a1cdaa56e6b5e16908 +functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 38e168fa57c9626140e1e4d05f4124b4b69bd775e6e0f4481e017ad86c4d95a0 functions_load_test_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.bin c8dbbd5ebb34435800d6674700068837c3a252db60046a14b0e61e829db517de functions_oracle_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 9dedd3a36043605fd9bedf821e7ec5b4281a5c7ae2e4a1955f37aff8ba13519f From 96ae30c1eecd5251f1830ba6c40b83863fe0fa65 Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Thu, 9 Nov 2023 21:24:25 -0500 Subject: [PATCH 124/271] Remove Functions OCR2Base config digest check & use custom errors (#11249) --- .../gas-snapshots/functions.gas-snapshot | 44 +++++++-------- .../dev/v1_X/FunctionsCoordinator.sol | 6 ++- .../v0.8/functions/dev/v1_X/ocr/OCR2Base.sol | 54 ++++++------------- .../functions_coordinator.go | 4 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- 5 files changed, 46 insertions(+), 64 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index d15666f8857..521b3bffac1 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14534206) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14534184) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14534200) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14545620) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14545597) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14545569) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14545520) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14545509) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14545553) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14600662) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14600640) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14600656) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14612076) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14612053) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14612025) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14611976) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14611965) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14612009) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -29,8 +29,8 @@ FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8 FunctionsBilling__FulfillAndBill:test__FulfillAndBill_RevertIfInvalidCommitment() (gas: 13302) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_Success() (gas: 180763) FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) -FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 504354) -FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 205558) +FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 497786) +FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 198990) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_RevertIfNotRouter() (gas: 14623) FunctionsClient_HandleOracleFulfillment:test_HandleOracleFulfillment_Success() (gas: 22923) FunctionsClient__SendRequest:test__SendRequest_RevertIfInvalidCallbackGasLimit() (gas: 55059) @@ -51,17 +51,17 @@ FunctionsRequest_DEFAULT_BUFFER_SIZE:test_DEFAULT_BUFFER_SIZE() (gas: 246) FunctionsRequest_EncodeCBOR:test_EncodeCBOR_Success() (gas: 223) FunctionsRequest_REQUEST_DATA_VERSION:test_REQUEST_DATA_VERSION() (gas: 225) FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12007) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 174027) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 164358) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 167459) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 157790) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38115) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35238) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 182503) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 175935) FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28086) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 158046) -FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 327605) -FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 341226) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2516507) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 546986) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 151478) +FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 321037) +FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 334658) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2509939) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 540418) FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37159) @@ -141,11 +141,11 @@ FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Reve FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13459) FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59592) FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15010) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43685, ~: 45548) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46197, ~: 48060) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43863, ~: 45548) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46375, ~: 48060) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51177, ~: 53040) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 85879, ~: 89604) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfNoAmount() (gas: 15638) diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol index 3ee4931e97c..15949a497e7 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol @@ -44,7 +44,7 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli address router, Config memory config, address linkToNativeFeed - ) OCR2Base(true) FunctionsBilling(router, config, linkToNativeFeed) {} + ) OCR2Base() FunctionsBilling(router, config, linkToNativeFeed) {} /// @inheritdoc IFunctionsCoordinator function getThresholdPublicKey() external view override returns (bytes memory) { @@ -151,7 +151,9 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli numberOfFulfillments != onchainMetadata.length || numberOfFulfillments != offchainMetadata.length ) { - revert ReportInvalid(); + revert ReportInvalid( + "All fields on the report must be of equal length: requestIds, results, errors, onchainMetadata, offchainMetadata" + ); } // Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig diff --git a/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol b/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol index dd9ea84a519..375159bf4c9 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/ocr/OCR2Base.sol @@ -10,17 +10,10 @@ import {OCR2Abstract} from "./OCR2Abstract.sol"; * doc, which refers to this contract as simply the "contract". */ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { - error ReportInvalid(); + error ReportInvalid(string message); error InvalidConfig(string message); - bool internal immutable i_uniqueReports; - - constructor(bool uniqueReports) ConfirmedOwner(msg.sender) { - i_uniqueReports = uniqueReports; - } - - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - uint256 private constant maxUint32 = (1 << 32) - 1; + constructor() ConfirmedOwner(msg.sender) {} // incremented each time a new config is posted. This count is incorporated // into the config digest, to prevent replay attacks. @@ -144,12 +137,12 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol for (uint256 i = 0; i < args.signers.length; i++) { + if (args.signers[i] == address(0)) revert InvalidConfig("signer must not be empty"); + if (args.transmitters[i] == address(0)) revert InvalidConfig("transmitter must not be empty"); // add new signer/transmitter addresses - // solhint-disable-next-line custom-errors - require(s_oracles[args.signers[i]].role == Role.Unset, "repeated signer address"); + if (s_oracles[args.signers[i]].role != Role.Unset) revert InvalidConfig("repeated signer address"); s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); - // solhint-disable-next-line custom-errors - require(s_oracles[args.transmitters[i]].role == Role.Unset, "repeated transmitter address"); + if (s_oracles[args.transmitters[i]].role != Role.Unset) revert InvalidConfig("repeated transmitter address"); s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); s_signers.push(args.signers[i]); s_transmitters.push(args.transmitters[i]); @@ -287,8 +280,7 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { ss.length * 32 + // 32 bytes per entry in _ss 0; // placeholder - // solhint-disable-next-line custom-errors - require(msg.data.length == expected, "calldata length mismatch"); + if (msg.data.length != expected) revert ReportInvalid("calldata length mismatch"); } /** @@ -319,30 +311,20 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { emit Transmitted(configDigest, uint32(epochAndRound >> 8)); - ConfigInfo memory configInfo = s_configInfo; - // solhint-disable-next-line custom-errors - require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); + // The following check is disabled to allow both current and proposed routes to submit reports using the same OCR config digest + // Chainlink Functions uses globally unique request IDs. Metadata about the request is stored and checked in the Coordinator and Router + // require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); _requireExpectedMsgDataLength(report, rs, ss); - uint256 expectedNumSignatures; - if (i_uniqueReports) { - expectedNumSignatures = (configInfo.n + configInfo.f) / 2 + 1; - } else { - expectedNumSignatures = configInfo.f + 1; - } + uint256 expectedNumSignatures = (s_configInfo.n + s_configInfo.f) / 2 + 1; - // solhint-disable-next-line custom-errors - require(rs.length == expectedNumSignatures, "wrong number of signatures"); - // solhint-disable-next-line custom-errors - require(rs.length == ss.length, "signatures out of registration"); + if (rs.length != expectedNumSignatures) revert ReportInvalid("wrong number of signatures"); + if (rs.length != ss.length) revert ReportInvalid("report rs and ss must be of equal length"); Oracle memory transmitter = s_oracles[msg.sender]; - // solhint-disable-next-line custom-errors - require( // Check that sender is authorized to report - transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index], - "unauthorized transmitter" - ); + if (transmitter.role != Role.Transmitter && msg.sender != s_transmitters[transmitter.index]) + revert ReportInvalid("unauthorized transmitter"); } address[MAX_NUM_ORACLES] memory signed; @@ -357,10 +339,8 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { for (uint256 i = 0; i < rs.length; ++i) { address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); o = s_oracles[signer]; - // solhint-disable-next-line custom-errors - require(o.role == Role.Signer, "address not authorized to sign"); - // solhint-disable-next-line custom-errors - require(signed[o.index] == address(0), "non-unique signature"); + if (o.role != Role.Signer) revert ReportInvalid("address not authorized to sign"); + if (signed[o.index] != address(0)) revert ReportInvalid("non-unique signature"); signed[o.index] = signer; signerCount += 1; } diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index 917107524bf..3e3fac16d13 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -71,8 +71,8 @@ type FunctionsResponseRequestMeta struct { } var FunctionsCoordinatorMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60c06040523480156200001157600080fd5b506040516200560838038062005608833981016040819052620000349162000474565b8282828260013380600081620000915760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c457620000c48162000140565b50505015156080526001600160a01b038116620000f457604051632530e88560e11b815260040160405180910390fd5b6001600160a01b0390811660a052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200013482620001eb565b50505050505062000633565b336001600160a01b038216036200019a5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000088565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001f562000349565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033e9083906200057d565b60405180910390a150565b6200035362000355565b565b6000546001600160a01b03163314620003535760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000088565b80516001600160a01b0381168114620003c957600080fd5b919050565b60405161012081016001600160401b03811182821017156200040057634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c957600080fd5b80516001600160481b0381168114620003c957600080fd5b805164ffffffffff81168114620003c957600080fd5b805161ffff81168114620003c957600080fd5b80516001600160e01b0381168114620003c957600080fd5b60008060008385036101608112156200048c57600080fd5b6200049785620003b1565b935061012080601f1983011215620004ae57600080fd5b620004b8620003ce565b9150620004c86020870162000406565b8252620004d86040870162000406565b6020830152620004eb6060870162000406565b6040830152620004fe6080870162000406565b60608301526200051160a087016200041b565b60808301526200052460c0870162000433565b60a08301526200053760e0870162000449565b60c08301526101006200054c8188016200045c565b60e08401526200055e82880162000406565b90830152509150620005746101408501620003b1565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005d060808401826001600160481b03169052565b5060a0830151620005ea60a084018264ffffffffff169052565b5060c08301516200060160c084018261ffff169052565b5060e08301516200061d60e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805160a051614f856200068360003960008181610845015281816109d301528181610ca601528181610f3a015281816110450152818161183001526133b70152600061126e0152614f856000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046138fb565b61059c565b005b6101a56101b5366004613aa4565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613bc8565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613c69565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613cf8565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046138fb565b610d92565b6102a0610de2565b6040516102039190613d82565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613d95565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613dae565b610fd4565b6040516102039190613f03565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004613f57565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b604051610203919061400e565b61053b6105363660046140fe565b61182c565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f661198c565b6101a561056e366004614217565b6119e3565b61057b61240f565b604051908152602001610203565b6101a56105973660046142e4565b612668565b6105a461267c565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec82848361439a565b505050565b6105f96126ff565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f51848629061083690839061400e565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d291906144c0565b905090565b6108df612707565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6126ff565b610ba2612707565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd2614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c31614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf5614531565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614560565b9050610bb1565b5050565b610d9a61267c565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec82848361439a565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e6090614301565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea890614301565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed490614301565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614598565b6128b3565b90506110bf60608301604084016142e4565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a08801614685565b61111f610160880161014089016142e4565b61112988806146a2565b61113b6101208b016101008c01614707565b60208b01356111516101008d0160e08e01614722565b8b6040516111679998979695949392919061473f565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16040805160608101825260025480825260035460ff8082166020850152610100909104169282019290925290831461125c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f636f6e666967446967657374206d69736d6174636800000000000000000000006044820152606401610b0d565b61126a8b8b8b8b8b8b612d51565b60007f0000000000000000000000000000000000000000000000000000000000000000156112c7576002826020015183604001516112a891906147e7565b6112b2919061482f565b6112bd9060016147e7565b60ff1690506112dd565b60208201516112d79060016147e7565b60ff1690505b888114611346576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b8887146113af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e00006044820152606401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113f2576113f2614851565b600281111561140357611403614851565b905250905060028160200151600281111561142057611420614851565b14801561146757506006816000015160ff168154811061144257611442614531565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1633145b6114cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b50505050506114da613893565b6000808a8a6040516114ed929190614880565b604051908190038120611504918e90602001614890565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b8981101561180e57600060018489846020811061156d5761156d614531565b61157a91901a601b6147e7565b8e8e8681811061158c5761158c614531565b905060200201358d8d878181106115a5576115a5614531565b90506020020135604051600081526020016040526040516115e2949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa158015611604573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff8082168552929650929450840191610100900416600281111561168457611684614851565b600281111561169557611695614851565b90525092506001836020015160028111156116b2576116b2614851565b14611719576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061173357611733614531565b602002015173ffffffffffffffffffffffffffffffffffffffff16146117b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117cf576117cf614531565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117fa6001866147e7565b9450508061180790614560565b905061154e565b50505061181f833383858e8e612e08565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b5050505066038d7ea4c68000821115611925576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061192f610841565b9050600061197287878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b905061198085858385612fd7565b98975050505050505050565b6060600c805461199b90614301565b90506000036119d6576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea890614301565b855185518560ff16601f831115611a56576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611ac0576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611b4e576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611b598160036148a4565b8311611bc1576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611bc961267c565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611c109088613144565b60055415611dc557600554600090611c2a906001906148bb565b9050600060058281548110611c4157611c41614531565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611c7b57611c7b614531565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611cfb57611cfb6148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611d6457611d646148ce565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611c10915050565b60005b81515181101561222c5760006004600084600001518481518110611dee57611dee614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611e3857611e38614851565b14611e9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611ed057611ed0614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00001617610100836002811115611f7157611f71614851565b021790555060009150611f819050565b6004600084602001518481518110611f9b57611f9b614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611fe557611fe5614851565b1461204c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff82168152602081016002815250600460008460200151848151811061207f5761207f614531565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561212057612120614851565b02179055505082518051600592508390811061213e5761213e614531565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106121ba576121ba614531565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790558061222481614560565b915050611dc8565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff43811682029290921780855592048116929182916014916122e4918491740100000000000000000000000000000000000000009004166148fd565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123434630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a0015161315d565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986123fa988b9891977401000000000000000000000000000000000000000090920463ffffffff1696909591949193919261491a565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa15801561259d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c191906149ca565b5093505092505080426125d491906148bb565b836020015163ffffffff161080156125f657506000836020015163ffffffff16115b1561262457505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b60008213612661576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b61267061267c565b61267981613208565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146126fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6126fd61267c565b600b546bffffffffffffffffffffffff1660000361272157565b600061272b610de2565b8051909150600081900361276b576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b5460009061278a9083906bffffffffffffffffffffffff16614a1a565b905060005b828110156128555781600a60008684815181106127ae576127ae614531565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128169190614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508061284e90614560565b905061278f565b506128608282614a6a565b600b80546000906128809084906bffffffffffffffffffffffff1661450c565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612a6e576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612aab8560e001513a848860800151612fd7565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612b07576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612b209190614a92565b905060003087604001518860a001518960c001516001612b409190614aa5565b8a5180516020918201206101008d015160e08e0151604051612bf498979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612d039190613f03565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612d5e8260206148a4565b612d698560206148a4565b612d7588610144614a92565b612d7f9190614a92565b612d899190614a92565b612d94906000614a92565b9050368114612dff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612e1a86880188614ba1565b84519499509297509095509350915060ff16801580612e3a575084518114155b80612e46575083518114155b80612e52575082518114155b80612e5e575081518114155b15612e95576040517f0be3632800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b81811015612fc8576000612f2d888381518110612eb757612eb7614531565b6020026020010151888481518110612ed157612ed1614531565b6020026020010151888581518110612eeb57612eeb614531565b6020026020010151888681518110612f0557612f05614531565b6020026020010151888781518110612f1f57612f1f614531565b6020026020010151886132fd565b90506000816006811115612f4357612f43614851565b1480612f6057506001816006811115612f5e57612f5e614851565b145b15612fb757878281518110612f7757612f77614531565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b50612fc181614560565b9050612e98565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561303257600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b6008546000906127109061304c9063ffffffff16876148a4565b6130569190614c73565b6130609086614a92565b60085490915060009087906130999063ffffffff6c010000000000000000000000008204811691680100000000000000009004166148fd565b6130a391906148fd565b63ffffffff16905060006130ed6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b9050600061310e826130ff85876148a4565b6131099190614a92565b613753565b9050600061312a68ffffffffffffffffff808916908a16614a45565b90506131368183614a45565b9a9950505050505050505050565b600061314e610de2565b511115610d8e57610d8e612707565b6000808a8a8a8a8a8a8a8a8a60405160200161318199989796959493929190614c87565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613287576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133149190614d53565b905060003a82610120015183610100015161332f9190614e1b565b64ffffffffff1661334091906148a4565b905060008460ff166133886000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061361192505050565b6133929190614c73565b905060006133a36131098385614a92565b905060006133b03a613753565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff168961340f9190614a45565b338d6040518763ffffffff1660e01b815260040161343296959493929190614e39565b60408051808303816000875af1158015613450573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134749190614eb5565b9092509050600082600681111561348d5761348d614851565b14806134aa575060018260068111156134a8576134a8614851565b145b156136005760008e8152600760205260408120556134c88185614a45565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161353491859116614a45565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b6135b39190614a45565b6135bd9190614a45565b6135c79190614a45565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b60004661361d81613787565b1561369957606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561366e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136929190614ee8565b9392505050565b6136a2816137aa565b1561374a5773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e84604051806080016040528060488152602001614f3160489139604051602001613702929190614f01565b6040516020818303038152906040526040518263ffffffff1660e01b815260040161372d9190613bc8565b602060405180830381865afa15801561366e573d6000803e3d6000fd5b50600092915050565b600061378161376061240f565b61377284670de0b6b3a76400006148a4565b61377c9190614c73565b6137f1565b92915050565b600061a4b182148061379b575062066eed82145b8061378157505062066eee1490565b6000600a8214806137bc57506101a482145b806137c9575062aa37dc82145b806137d5575061210582145b806137e2575062014a3382145b8061378157505062014a341490565b60006bffffffffffffffffffffffff82111561388f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f8401126138c457600080fd5b50813567ffffffffffffffff8111156138dc57600080fd5b6020830191508360208285010111156138f457600080fd5b9250929050565b6000806020838503121561390e57600080fd5b823567ffffffffffffffff81111561392557600080fd5b613931858286016138b2565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff811182821017156139905761399061393d565b60405290565b604051610160810167ffffffffffffffff811182821017156139905761399061393d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613a0157613a0161393d565b604052919050565b63ffffffff8116811461267957600080fd5b803561117081613a09565b68ffffffffffffffffff8116811461267957600080fd5b803561117081613a26565b64ffffffffff8116811461267957600080fd5b803561117081613a48565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613ab757600080fd5b613abf61396c565b613ac883613a1b565b8152613ad660208401613a1b565b6020820152613ae760408401613a1b565b6040820152613af860608401613a1b565b6060820152613b0960808401613a3d565b6080820152613b1a60a08401613a5b565b60a0820152613b2b60c08401613a66565b60c0820152613b3c60e08401613a78565b60e0820152610100613b4f818501613a1b565b908201529392505050565b60005b83811015613b75578181015183820152602001613b5d565b50506000910152565b60008151808452613b96816020860160208601613b5a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006136926020830184613b7e565b600082601f830112613bec57600080fd5b813567ffffffffffffffff811115613c0657613c0661393d565b613c3760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016139ba565b818152846020838601011115613c4c57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613c7b57600080fd5b813567ffffffffffffffff811115613c9257600080fd5b613c9e84828501613bdb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461267957600080fd5b803561117081613ca6565b6bffffffffffffffffffffffff8116811461267957600080fd5b803561117081613cd3565b60008060408385031215613d0b57600080fd5b8235613d1681613ca6565b91506020830135613d2681613cd3565b809150509250929050565b600081518084526020808501945080840160005b83811015613d7757815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613d45565b509495945050505050565b6020815260006136926020830184613d31565b600060208284031215613da757600080fd5b5035919050565b600060208284031215613dc057600080fd5b813567ffffffffffffffff811115613dd757600080fd5b8201610160818503121561369257600080fd5b805182526020810151613e15602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613e3560408401826bffffffffffffffffffffffff169052565b506060810151613e5d606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613e79608084018267ffffffffffffffff169052565b5060a0810151613e9160a084018263ffffffff169052565b5060c0810151613eae60c084018268ffffffffffffffffff169052565b5060e0810151613ecb60e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016137818284613dea565b60008083601f840112613f2457600080fd5b50813567ffffffffffffffff811115613f3c57600080fd5b6020830191508360208260051b85010111156138f457600080fd5b60008060008060008060008060e0898b031215613f7357600080fd5b606089018a811115613f8457600080fd5b8998503567ffffffffffffffff80821115613f9e57600080fd5b613faa8c838d016138b2565b909950975060808b0135915080821115613fc357600080fd5b613fcf8c838d01613f12565b909750955060a08b0135915080821115613fe857600080fd5b50613ff58b828c01613f12565b999c989b50969995989497949560c00135949350505050565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151614062608084018268ffffffffffffffffff169052565b5060a083015161407b60a084018264ffffffffff169052565b5060c083015161409160c084018261ffff169052565b5060e08301516140c160e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461267957600080fd5b8035611170816140dd565b60008060008060006080868803121561411657600080fd5b8535614121816140dd565b9450602086013567ffffffffffffffff81111561413d57600080fd5b614149888289016138b2565b909550935050604086013561415d81613a09565b949793965091946060013592915050565b600067ffffffffffffffff8211156141885761418861393d565b5060051b60200190565b600082601f8301126141a357600080fd5b813560206141b86141b38361416e565b6139ba565b82815260059290921b840181019181810190868411156141d757600080fd5b8286015b848110156141fb5780356141ee81613ca6565b83529183019183016141db565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561423057600080fd5b863567ffffffffffffffff8082111561424857600080fd5b6142548a838b01614192565b9750602089013591508082111561426a57600080fd5b6142768a838b01614192565b965061428460408a01614206565b9550606089013591508082111561429a57600080fd5b6142a68a838b01613bdb565b94506142b460808a016140f3565b935060a08901359150808211156142ca57600080fd5b506142d789828a01613bdb565b9150509295509295509295565b6000602082840312156142f657600080fd5b813561369281613ca6565b600181811c9082168061431557607f821691505b60208210810361434e577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c8101602086101561437b5750805b601f850160051c820191505b81811015610a8857828155600101614387565b67ffffffffffffffff8311156143b2576143b261393d565b6143c6836143c08354614301565b83614354565b6000601f84116001811461441857600085156143e25750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556144ae565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156144675786850135825560209485019460019092019101614447565b50868210156144a2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613a26565b6000602082840312156144d257600080fd5b815161369281613a26565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115612661576126616144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614591576145916144dd565b5060010190565b600061016082360312156145ab57600080fd5b6145b3613996565b823567ffffffffffffffff8111156145ca57600080fd5b6145d636828601613bdb565b825250602083013560208201526145ef60408401613cc8565b604082015261460060608401613ced565b606082015261461160808401613a3d565b608082015261462260a084016140f3565b60a082015261463360c084016140f3565b60c082015261464460e08401613a1b565b60e0820152610100614657818501613a66565b908201526101206146698482016140f3565b9082015261014061467b848201613cc8565b9082015292915050565b60006020828403121561469757600080fd5b8135613692816140dd565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126146d757600080fd5b83018035915067ffffffffffffffff8211156146f257600080fd5b6020019150368190038213156138f457600080fd5b60006020828403121561471957600080fd5b61369282613a66565b60006020828403121561473457600080fd5b813561369281613a09565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061313660e0830184613dea565b60ff8181168382160190811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061484257614842614800565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b8082028115828204841417613781576137816144dd565b81810381811115613781576137816144dd565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff818116838216019080821115612661576126616144dd565b600061012063ffffffff808d1684528b6020850152808b1660408501525080606084015261494a8184018a613d31565b9050828103608084015261495e8189613d31565b905060ff871660a084015282810360c084015261497b8187613b7e565b905067ffffffffffffffff851660e08401528281036101008401526149a08185613b7e565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a086880312156149e257600080fd5b6149eb866149b0565b9450602086015193506040860151925060608601519150614a0e608087016149b0565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614a3957614a39614800565b92169190910492915050565b6bffffffffffffffffffffffff818116838216019080821115612661576126616144dd565b6bffffffffffffffffffffffff8181168382160280821691908281146140d5576140d56144dd565b80820180821115613781576137816144dd565b67ffffffffffffffff818116838216019080821115612661576126616144dd565b600082601f830112614ad757600080fd5b81356020614ae76141b38361416e565b82815260059290921b84018101918181019086841115614b0657600080fd5b8286015b848110156141fb5780358352918301918301614b0a565b600082601f830112614b3257600080fd5b81356020614b426141b38361416e565b82815260059290921b84018101918181019086841115614b6157600080fd5b8286015b848110156141fb57803567ffffffffffffffff811115614b855760008081fd5b614b938986838b0101613bdb565b845250918301918301614b65565b600080600080600060a08688031215614bb957600080fd5b853567ffffffffffffffff80821115614bd157600080fd5b614bdd89838a01614ac6565b96506020880135915080821115614bf357600080fd5b614bff89838a01614b21565b95506040880135915080821115614c1557600080fd5b614c2189838a01614b21565b94506060880135915080821115614c3757600080fd5b614c4389838a01614b21565b93506080880135915080821115614c5957600080fd5b50614c6688828901614b21565b9150509295509295909350565b600082614c8257614c82614800565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614cce8285018b613d31565b91508382036080850152614ce2828a613d31565b915060ff881660a085015283820360c0850152614cff8288613b7e565b90861660e085015283810361010085015290506149a08185613b7e565b805161117081613ca6565b805161117081613cd3565b8051611170816140dd565b805161117081613a09565b805161117081613a48565b60006101608284031215614d6657600080fd5b614d6e613996565b82518152614d7e60208401614d1c565b6020820152614d8f60408401614d27565b6040820152614da060608401614d1c565b6060820152614db160808401614d32565b6080820152614dc260a08401614d3d565b60a0820152614dd360c084016144b5565b60c0820152614de460e084016144b5565b60e0820152610100614df7818501614d48565b90820152610120614e09848201614d48565b90820152610140613b4f848201614d3d565b64ffffffffff818116838216019080821115612661576126616144dd565b6000610200808352614e4d8184018a613b7e565b90508281036020840152614e618189613b7e565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614eaa905060a0830184613dea565b979650505050505050565b60008060408385031215614ec857600080fd5b825160078110614ed757600080fd5b6020840151909250613d2681613cd3565b600060208284031215614efa57600080fd5b5051919050565b60008351614f13818460208801613b5a565b835190830190614f27818360208801613b5a565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60a06040523480156200001157600080fd5b50604051620057423803806200574283398101604081905262000034916200046d565b8282828233806000816200008f5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c257620000c28162000139565b5050506001600160a01b038116620000ed57604051632530e88560e11b815260040160405180910390fd5b6001600160a01b03908116608052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200012d82620001e4565b5050505050506200062c565b336001600160a01b03821603620001935760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000086565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee62000342565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033790839062000576565b60405180910390a150565b6200034c6200034e565b565b6000546001600160a01b031633146200034c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000086565b80516001600160a01b0381168114620003c257600080fd5b919050565b60405161012081016001600160401b0381118282101715620003f957634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c257600080fd5b80516001600160481b0381168114620003c257600080fd5b805164ffffffffff81168114620003c257600080fd5b805161ffff81168114620003c257600080fd5b80516001600160e01b0381168114620003c257600080fd5b60008060008385036101608112156200048557600080fd5b6200049085620003aa565b935061012080601f1983011215620004a757600080fd5b620004b1620003c7565b9150620004c160208701620003ff565b8252620004d160408701620003ff565b6020830152620004e460608701620003ff565b6040830152620004f760808701620003ff565b60608301526200050a60a0870162000414565b60808301526200051d60c087016200042c565b60a08301526200053060e0870162000442565b60c08301526101006200054581880162000455565b60e084015262000557828801620003ff565b908301525091506200056d6101408501620003aa565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005c960808401826001600160481b03169052565b5060a0830151620005e360a084018264ffffffffff169052565b5060c0830151620005fa60c084018261ffff169052565b5060e08301516200061660e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b6080516150d06200067260003960008181610845015281816109d301528181610ca601528181610f3a0152818161104501528181611789015261350201526150d06000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a0366004613a46565b61059c565b005b6101a56101b5366004613bef565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613d13565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613db4565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613e43565b6108d7565b6101a5610a90565b6101a5610b92565b6101a5610293366004613a46565b610d92565b6102a0610de2565b6040516102039190613ecd565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613ee0565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613ef9565b610fd4565b604051610203919061404e565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab3660046140a2565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b6040516102039190614159565b61053b610536366004614249565b611785565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f66118e5565b6101a561056e366004614362565b61193c565b61057b6124b8565b604051908152602001610203565b6101a561059736600461442f565b612711565b6105a4612725565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec8284836144e5565b505050565b6105f96127a8565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f518486290610836908390614159565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d2919061460b565b905090565b6108df6127b0565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff16614657565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6127a8565b610ba26127b0565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561467c565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d87816146ab565b9050610bb1565b5050565b610d9a612725565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec8284836144e5565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e609061444c565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea89061444c565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed49061444c565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a8836146e3565b61295c565b90506110bf606083016040840161442f565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a088016147d0565b61111f6101608801610140890161442f565b61112988806147ed565b61113b6101208b016101008c01614852565b60208b01356111516101008d0160e08e0161486d565b8b6040516111679998979695949392919061488a565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16111d68a8a8a8a8a8a612dfa565b6003546000906002906111f49060ff80821691610100900416614932565b6111fe919061497a565b611209906001614932565b60ff169050878114611277576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b878614611306576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f7265706f727420727320616e64207373206d757374206265206f66206571756160448201527f6c206c656e6774680000000000000000000000000000000000000000000000006064820152608401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113495761134961499c565b600281111561135a5761135a61499c565b90525090506002816020015160028111156113775761137761499c565b141580156113c057506006816000015160ff168154811061139a5761139a61467c565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff163314155b15611427576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b505050506114336139de565b6000808a8a6040516114469291906149cb565b60405190819003812061145d918e906020016149db565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b898110156117675760006001848984602081106114c6576114c661467c565b6114d391901a601b614932565b8e8e868181106114e5576114e561467c565b905060200201358d8d878181106114fe576114fe61467c565b905060200201356040516000815260200160405260405161153b949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561155d573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff808216855292965092945084019161010090041660028111156115dd576115dd61499c565b60028111156115ee576115ee61499c565b905250925060018360200151600281111561160b5761160b61499c565b14611672576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061168c5761168c61467c565b602002015173ffffffffffffffffffffffffffffffffffffffff161461170e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117285761172861467c565b73ffffffffffffffffffffffffffffffffffffffff9092166020929092020152611753600186614932565b94505080611760906146ab565b90506114a7565b505050611778833383858e8e612eb1565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b15801561182557600080fd5b505afa158015611839573d6000803e3d6000fd5b5050505066038d7ea4c6800082111561187e576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611888610841565b905060006118cb87878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b90506118d985858385613122565b98975050505050505050565b6060600c80546118f49061444c565b905060000361192f576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea89061444c565b855185518560ff16601f8311156119af576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611a19576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611aa7576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611ab28160036149ef565b8311611b1a576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611b22612725565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611b69908861328f565b60055415611d1e57600554600090611b8390600190614a06565b9050600060058281548110611b9a57611b9a61467c565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611bd457611bd461467c565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611c5457611c54614a19565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611cbd57611cbd614a19565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611b69915050565b60005b8151518110156122d557815180516000919083908110611d4357611d4361467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611dc8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f7369676e6572206d757374206e6f7420626520656d70747900000000000000006044820152606401610b0d565b600073ffffffffffffffffffffffffffffffffffffffff1682602001518281518110611df657611df661467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611e7b576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d7074790000006044820152606401610b0d565b60006004600084600001518481518110611e9757611e9761467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611ee157611ee161499c565b14611f48576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611f7957611f7961467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561201a5761201a61499c565b02179055506000915061202a9050565b60046000846020015184815181106120445761204461467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff16600281111561208e5761208e61499c565b146120f5576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff8216815260208101600281525060046000846020015184815181106121285761212861467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016176101008360028111156121c9576121c961499c565b0217905550508251805160059250839081106121e7576121e761467c565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106122635761226361467c565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055806122cd816146ab565b915050611d21565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161238d91849174010000000000000000000000000000000000000000900416614a48565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123ec4630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a001516132a8565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986124a3988b9891977401000000000000000000000000000000000000000090920463ffffffff16969095919491939192614a65565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a9190614b15565b50935050925050804261267d9190614a06565b836020015163ffffffff1610801561269f57506000836020015163ffffffff16115b156126cd57505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b6000821361270a576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b612719612725565b61272281613353565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146127a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6127a6612725565b600b546bffffffffffffffffffffffff166000036127ca57565b60006127d4610de2565b80519091506000819003612814576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b546000906128339083906bffffffffffffffffffffffff16614b65565b905060005b828110156128fe5781600a60008684815181106128575761285761467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128bf9190614b90565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806128f7906146ab565b9050612838565b506129098282614bb5565b600b80546000906129299084906bffffffffffffffffffffffff16614657565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612b17576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612b548560e001513a848860800151613122565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612bb0576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612bc99190614bdd565b905060003087604001518860a001518960c001516001612be99190614bf0565b8a5180516020918201206101008d015160e08e0151604051612c9d98979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612dac919061404e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612e078260206149ef565b612e128560206149ef565b612e1e88610144614bdd565b612e289190614bdd565b612e329190614bdd565b612e3d906000614bdd565b9050368114612ea8576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612ec386880188614cec565b84519499509297509095509350915060ff16801580612ee3575084518114155b80612eef575083518114155b80612efb575082518114155b80612f07575081518114155b15612fe0576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152607060248201527f416c6c206669656c6473206f6e20746865207265706f7274206d75737420626560448201527f206f6620657175616c206c656e6774683a20726571756573744964732c20726560648201527f73756c74732c206572726f72732c206f6e636861696e4d657461646174612c2060848201527f6f6666636861696e4d657461646174610000000000000000000000000000000060a482015260c401610b0d565b60005b818110156131135760006130788883815181106130025761300261467c565b602002602001015188848151811061301c5761301c61467c565b60200260200101518885815181106130365761303661467c565b60200260200101518886815181106130505761305061467c565b602002602001015188878151811061306a5761306a61467c565b602002602001015188613448565b9050600081600681111561308e5761308e61499c565b14806130ab575060018160068111156130a9576130a961499c565b145b15613102578782815181106130c2576130c261467c565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b5061310c816146ab565b9050612fe3565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561317d57600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b600854600090612710906131979063ffffffff16876149ef565b6131a19190614dbe565b6131ab9086614bdd565b60085490915060009087906131e49063ffffffff6c01000000000000000000000000820481169168010000000000000000900416614a48565b6131ee9190614a48565b63ffffffff16905060006132386000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061375c92505050565b905060006132598261324a85876149ef565b6132549190614bdd565b61389e565b9050600061327568ffffffffffffffffff808916908a16614b90565b90506132818183614b90565b9a9950505050505050505050565b6000613299610de2565b511115610d8e57610d8e6127b0565b6000808a8a8a8a8a8a8a8a8a6040516020016132cc99989796959493929190614dd2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036133d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000808480602001905181019061345f9190614e9e565b905060003a82610120015183610100015161347a9190614f66565b64ffffffffff1661348b91906149ef565b905060008460ff166134d36000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061375c92505050565b6134dd9190614dbe565b905060006134ee6132548385614bdd565b905060006134fb3a61389e565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff168961355a9190614b90565b338d6040518763ffffffff1660e01b815260040161357d96959493929190614f84565b60408051808303816000875af115801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135bf9190615000565b909250905060008260068111156135d8576135d861499c565b14806135f5575060018260068111156135f3576135f361499c565b145b1561374b5760008e8152600760205260408120556136138185614b90565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161367f91859116614b90565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b6136fe9190614b90565b6137089190614b90565b6137129190614b90565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b600046613768816138d2565b156137e457606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137dd9190615033565b9392505050565b6137ed816138f5565b156138955773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e8460405180608001604052806048815260200161507c6048913960405160200161384d92919061504c565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016138789190613d13565b602060405180830381865afa1580156137b9573d6000803e3d6000fd5b50600092915050565b60006138cc6138ab6124b8565b6138bd84670de0b6b3a76400006149ef565b6138c79190614dbe565b61393c565b92915050565b600061a4b18214806138e6575062066eed82145b806138cc57505062066eee1490565b6000600a82148061390757506101a482145b80613914575062aa37dc82145b80613920575061210582145b8061392d575062014a3382145b806138cc57505062014a341490565b60006bffffffffffffffffffffffff8211156139da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f840112613a0f57600080fd5b50813567ffffffffffffffff811115613a2757600080fd5b602083019150836020828501011115613a3f57600080fd5b9250929050565b60008060208385031215613a5957600080fd5b823567ffffffffffffffff811115613a7057600080fd5b613a7c858286016139fd565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613adb57613adb613a88565b60405290565b604051610160810167ffffffffffffffff81118282101715613adb57613adb613a88565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b4c57613b4c613a88565b604052919050565b63ffffffff8116811461272257600080fd5b803561117081613b54565b68ffffffffffffffffff8116811461272257600080fd5b803561117081613b71565b64ffffffffff8116811461272257600080fd5b803561117081613b93565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613c0257600080fd5b613c0a613ab7565b613c1383613b66565b8152613c2160208401613b66565b6020820152613c3260408401613b66565b6040820152613c4360608401613b66565b6060820152613c5460808401613b88565b6080820152613c6560a08401613ba6565b60a0820152613c7660c08401613bb1565b60c0820152613c8760e08401613bc3565b60e0820152610100613c9a818501613b66565b908201529392505050565b60005b83811015613cc0578181015183820152602001613ca8565b50506000910152565b60008151808452613ce1816020860160208601613ca5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006137dd6020830184613cc9565b600082601f830112613d3757600080fd5b813567ffffffffffffffff811115613d5157613d51613a88565b613d8260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b05565b818152846020838601011115613d9757600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613dc657600080fd5b813567ffffffffffffffff811115613ddd57600080fd5b613de984828501613d26565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461272257600080fd5b803561117081613df1565b6bffffffffffffffffffffffff8116811461272257600080fd5b803561117081613e1e565b60008060408385031215613e5657600080fd5b8235613e6181613df1565b91506020830135613e7181613e1e565b809150509250929050565b600081518084526020808501945080840160005b83811015613ec257815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e90565b509495945050505050565b6020815260006137dd6020830184613e7c565b600060208284031215613ef257600080fd5b5035919050565b600060208284031215613f0b57600080fd5b813567ffffffffffffffff811115613f2257600080fd5b820161016081850312156137dd57600080fd5b805182526020810151613f60602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613f8060408401826bffffffffffffffffffffffff169052565b506060810151613fa8606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613fc4608084018267ffffffffffffffff169052565b5060a0810151613fdc60a084018263ffffffff169052565b5060c0810151613ff960c084018268ffffffffffffffffff169052565b5060e081015161401660e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016138cc8284613f35565b60008083601f84011261406f57600080fd5b50813567ffffffffffffffff81111561408757600080fd5b6020830191508360208260051b8501011115613a3f57600080fd5b60008060008060008060008060e0898b0312156140be57600080fd5b606089018a8111156140cf57600080fd5b8998503567ffffffffffffffff808211156140e957600080fd5b6140f58c838d016139fd565b909950975060808b013591508082111561410e57600080fd5b61411a8c838d0161405d565b909750955060a08b013591508082111561413357600080fd5b506141408b828c0161405d565b999c989b50969995989497949560c00135949350505050565b815163ffffffff9081168252602080840151821690830152604080840151821690830152606080840151918216908301526101208201905060808301516141ad608084018268ffffffffffffffffff169052565b5060a08301516141c660a084018264ffffffffff169052565b5060c08301516141dc60c084018261ffff169052565b5060e083015161420c60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461272257600080fd5b803561117081614228565b60008060008060006080868803121561426157600080fd5b853561426c81614228565b9450602086013567ffffffffffffffff81111561428857600080fd5b614294888289016139fd565b90955093505060408601356142a881613b54565b949793965091946060013592915050565b600067ffffffffffffffff8211156142d3576142d3613a88565b5060051b60200190565b600082601f8301126142ee57600080fd5b813560206143036142fe836142b9565b613b05565b82815260059290921b8401810191818101908684111561432257600080fd5b8286015b8481101561434657803561433981613df1565b8352918301918301614326565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561437b57600080fd5b863567ffffffffffffffff8082111561439357600080fd5b61439f8a838b016142dd565b975060208901359150808211156143b557600080fd5b6143c18a838b016142dd565b96506143cf60408a01614351565b955060608901359150808211156143e557600080fd5b6143f18a838b01613d26565b94506143ff60808a0161423e565b935060a089013591508082111561441557600080fd5b5061442289828a01613d26565b9150509295509295509295565b60006020828403121561444157600080fd5b81356137dd81613df1565b600181811c9082168061446057607f821691505b602082108103614499577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156144c65750805b601f850160051c820191505b81811015610a88578281556001016144d2565b67ffffffffffffffff8311156144fd576144fd613a88565b6145118361450b835461444c565b8361449f565b6000601f841160018114614563576000851561452d5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556145f9565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156145b25786850135825560209485019460019092019101614592565b50868210156145ed577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613b71565b60006020828403121561461d57600080fd5b81516137dd81613b71565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561270a5761270a614628565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036146dc576146dc614628565b5060010190565b600061016082360312156146f657600080fd5b6146fe613ae1565b823567ffffffffffffffff81111561471557600080fd5b61472136828601613d26565b8252506020830135602082015261473a60408401613e13565b604082015261474b60608401613e38565b606082015261475c60808401613b88565b608082015261476d60a0840161423e565b60a082015261477e60c0840161423e565b60c082015261478f60e08401613b66565b60e08201526101006147a2818501613bb1565b908201526101206147b484820161423e565b908201526101406147c6848201613e13565b9082015292915050565b6000602082840312156147e257600080fd5b81356137dd81614228565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261482257600080fd5b83018035915067ffffffffffffffff82111561483d57600080fd5b602001915036819003821315613a3f57600080fd5b60006020828403121561486457600080fd5b6137dd82613bb1565b60006020828403121561487f57600080fd5b81356137dd81613b54565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061328160e0830184613f35565b60ff81811683821601908111156138cc576138cc614628565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061498d5761498d61494b565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b80820281158282048414176138cc576138cc614628565b818103818111156138cc576138cc614628565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561270a5761270a614628565b600061012063ffffffff808d1684528b6020850152808b16604085015250806060840152614a958184018a613e7c565b90508281036080840152614aa98189613e7c565b905060ff871660a084015282810360c0840152614ac68187613cc9565b905067ffffffffffffffff851660e0840152828103610100840152614aeb8185613cc9565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a08688031215614b2d57600080fd5b614b3686614afb565b9450602086015193506040860151925060608601519150614b5960808701614afb565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614b8457614b8461494b565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561270a5761270a614628565b6bffffffffffffffffffffffff81811683821602808216919082811461422057614220614628565b808201808211156138cc576138cc614628565b67ffffffffffffffff81811683821601908082111561270a5761270a614628565b600082601f830112614c2257600080fd5b81356020614c326142fe836142b9565b82815260059290921b84018101918181019086841115614c5157600080fd5b8286015b848110156143465780358352918301918301614c55565b600082601f830112614c7d57600080fd5b81356020614c8d6142fe836142b9565b82815260059290921b84018101918181019086841115614cac57600080fd5b8286015b8481101561434657803567ffffffffffffffff811115614cd05760008081fd5b614cde8986838b0101613d26565b845250918301918301614cb0565b600080600080600060a08688031215614d0457600080fd5b853567ffffffffffffffff80821115614d1c57600080fd5b614d2889838a01614c11565b96506020880135915080821115614d3e57600080fd5b614d4a89838a01614c6c565b95506040880135915080821115614d6057600080fd5b614d6c89838a01614c6c565b94506060880135915080821115614d8257600080fd5b614d8e89838a01614c6c565b93506080880135915080821115614da457600080fd5b50614db188828901614c6c565b9150509295509295909350565b600082614dcd57614dcd61494b565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614e198285018b613e7c565b91508382036080850152614e2d828a613e7c565b915060ff881660a085015283820360c0850152614e4a8288613cc9565b90861660e08501528381036101008501529050614aeb8185613cc9565b805161117081613df1565b805161117081613e1e565b805161117081614228565b805161117081613b54565b805161117081613b93565b60006101608284031215614eb157600080fd5b614eb9613ae1565b82518152614ec960208401614e67565b6020820152614eda60408401614e72565b6040820152614eeb60608401614e67565b6060820152614efc60808401614e7d565b6080820152614f0d60a08401614e88565b60a0820152614f1e60c08401614600565b60c0820152614f2f60e08401614600565b60e0820152610100614f42818501614e93565b90820152610120614f54848201614e93565b90820152610140613c9a848201614e88565b64ffffffffff81811683821601908082111561270a5761270a614628565b6000610200808352614f988184018a613cc9565b90508281036020840152614fac8189613cc9565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614ff5905060a0830184613f35565b979650505050505050565b6000806040838503121561501357600080fd5b82516007811061502257600080fd5b6020840151909250613e7181613e1e565b60006020828403121561504557600080fd5b5051919050565b6000835161505e818460208801613ca5565b835190830190615072818360208801613ca5565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", } var FunctionsCoordinatorABI = FunctionsCoordinatorMetaData.ABI diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 534e5543e86..93c4e64a3af 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -4,7 +4,7 @@ functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServ functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b -functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 38e168fa57c9626140e1e4d05f4124b4b69bd775e6e0f4481e017ad86c4d95a0 +functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 4e05ca5e624b7a1e604b81b84bc088818b376d533f556ba1c2ee586b7eb38b68 functions_load_test_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.bin c8dbbd5ebb34435800d6674700068837c3a252db60046a14b0e61e829db517de functions_oracle_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 9dedd3a36043605fd9bedf821e7ec5b4281a5c7ae2e4a1955f37aff8ba13519f From 75d0743eae6f419756f5de12ddd922a711809c35 Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 10 Nov 2023 11:07:07 +0000 Subject: [PATCH 125/271] Add medianpoc to plugins dockerfile (#11247) --- GNUmakefile | 4 ++++ plugins/chainlink.Dockerfile | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/GNUmakefile b/GNUmakefile index 32f74e285e9..69d82da6c84 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -61,6 +61,10 @@ chainlink-local-start: install-median: ## Build & install the chainlink-median binary. go install $(GOFLAGS) ./plugins/cmd/chainlink-median +.PHONY: install-medianpoc +install-medianpoc: ## Build & install the chainlink-medianpoc binary. + go install $(GOFLAGS) ./plugins/cmd/chainlink-medianpoc + .PHONY: docker ## Build the chainlink docker image docker: docker buildx build \ diff --git a/plugins/chainlink.Dockerfile b/plugins/chainlink.Dockerfile index 001ee30bf74..f07fab48122 100644 --- a/plugins/chainlink.Dockerfile +++ b/plugins/chainlink.Dockerfile @@ -19,6 +19,9 @@ RUN make install-chainlink # Build LOOP Plugins RUN make install-median +# Install medianpoc binary +RUN make install-medianpoc + RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-solana | xargs -I % ln -s % /chainlink-solana RUN mkdir /chainlink-starknet RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-starknet/relayer | xargs -I % ln -s % /chainlink-starknet/relayer @@ -49,6 +52,7 @@ RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && apt-get clean all COPY --from=buildgo /go/bin/chainlink /usr/local/bin/ +COPY --from=buildgo /go/bin/chainlink-medianpoc /usr/local/bin/ COPY --from=buildgo /go/bin/chainlink-median /usr/local/bin/ ENV CL_MEDIAN_CMD chainlink-median From 0918b37d4e27c465a2e360167ad6e1ca594fe6e5 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Fri, 10 Nov 2023 15:52:08 +0100 Subject: [PATCH 126/271] solidity style guide 2.0 (#10995) * solidity style guide 2.0 * split into rules and guidelines * improve layout --- contracts/STYLE.md | 234 ----------------------- contracts/STYLE_GUIDE.md | 391 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+), 234 deletions(-) delete mode 100644 contracts/STYLE.md create mode 100644 contracts/STYLE_GUIDE.md diff --git a/contracts/STYLE.md b/contracts/STYLE.md deleted file mode 100644 index d9692a65210..00000000000 --- a/contracts/STYLE.md +++ /dev/null @@ -1,234 +0,0 @@ -# Solidity Style Guide - -## Background - -Our starting point is the [official Solidity Style Guide](https://solidity.readthedocs.io/en/v0.8.0/style-guide.html) and [ConsenSys's Secure Development practices](https://consensys.github.io/smart-contract-best-practices/), but we deviate in some ways. We lean heavily on [Prettier](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/.prettierrc) for formatting, and if you have to set up a new Solidity project we recommend starting with [our prettier config](https://github.com/smartcontractkit/chainlink/blob/develop/.prettierrc.js). We are trying to automate as much of this styleguide with Solhint as possible. - -### Code Organization - -- Group functionality together. E.g. Declare structs, events, and helper functions near the functions that use them. This is helpful when reading code because the related pieces are localized. It is also consistent with inheritance and libraries, which are separate pieces of code designed for a specific goal. - - Why not follow the Solidity recommendation of grouping by visibility? Visibility is clearly defined next to the method signature, making it trivial to check. However, searching can be deceiving because of inherited methods. Given this inconsistency in grouping, we find it easier to read and more consistent to organize code around functionality. Additionally, we recommend testing the public interface for any Solidity contract to ensure it only exposes expected methods. - -### Delineate Unaudited Code - -- In a large repo it is worthwhile to keep code that has not yet been audited separate from the code that has been audited. This allows you to easily keep track of which files need to be reviewed. - - E.g. we keep unaudited code in a directory named `dev`. Only once it has been audited we move the audited files out of `dev` and only then is it considered safe to deploy. - -## Variables - -### Visibility - -- All contract variables should be private. Getters should be explicitly written and documented when you want to expose a variable publicly. Whether a getter function reads from storage, a constant, or calculates a value from somewhere else, that’s all implementation details that should not be exposed to the consumer by casing or other conventions. - -Examples: - -Good: - -```javascript -uint256 private s_myVar; - -function getMyVar() external view returns(uint256){ - return s_myVar; -} -``` - -Bad: - -```javascript -uint256 public s_myVar; -``` - -### Naming and Casing - -- Function arguments are named like this: `argumentName`. No leading or trailing underscores necessary. -- Storage variables prefixed with an `s_` to make it clear that they live in storage and are expensive to read and write: `s_variableName`. They should always be private, and you should write explicit getters if you want to expose a storage variable. -- Immutable variables should be prefixed with an `i_` to make it clear that they are immutable. E.g. `i_decimalPlaces`. They should always be private, and you should write explicit getters if you want to expose an immutable variable. -- Internal/private constants should be all caps with underscores: `FOO_BAR`. Like other contract variables, constants should not be public. Create getter methods if you want to publicly expose constants. -- Explicitly declare variable size: `uint256` not just `uint`. In addition to being explicit, it matches the naming used to calculate function selectors. - -Examples: - -Good: - -```javascript -uint256 private s_myVar; -uint256 private immutable i_myImmutVar; -uint256 private constant MY_CONST_VAR; - -function multiplyMyVar(uint256 multiplier) external view returns(uint256){ - return multiplier * s_myVar; -} -``` - -Bad: - -```javascript -uint private s_myVar; -uint256 private immutable myImmutVar; -uint256 private constant s_myConstVar; - -function multiplyMyVar_(uint _multiplier) external view returns(uint256){ - return _mutliplier * s_myVar; -} -``` - -### Types - -- If you are storing an address and know/expect it to be of a type(or interface), make the variable that type. This more clearly documents the behavior of this variable than the `address` type and often leads to less casting code whenever the address is used. - -Examples: - -Good: - -```javascript -import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -// . -// . -// . -AggregatorV3Interface private s_priceFeed; - -constructor(address priceFeed) { - s_priceFeed = AggregatorV3Interface(priceFeed); -} -``` - -Bad: - -```javascript -import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -// . -// . -// . -address private s_priceFeed; - -constructor(address priceFeed) { - s_priceFeed = priceFeed; -} -``` - -## Functions - -### Visibility - -- Method visibility should always be explicitly declared. Contract’s [public interfaces should be tested](https://github.com/smartcontractkit/chainlink/blob/master/contracts/test/test-helpers/helpers.ts#L221) to enforce this and make sure that internal logic is not accidentally exposed. - -### Naming - -- Function names should start with imperative verbs, not nouns or other tenses. - - `requestData` not `dataRequest` - - `approve` not `approved` -- Prefix private and internal methods with an underscore. There should never be a publicly callable method starting with an underscore. - - E.g. `_setOwner(address)` -- Prefix your public getters with `get` and your public setters with `set`. - - `getConfig` and `setConfig`. - -## Modifiers - -- Only extract a modifier once a check is duplicated in multiple places. Modifiers arguably hurt readability, so we have found that they are not worth extracting until there is duplication. -- Modifiers should be treated as if they are view functions. They should not change state, only read it. While it is possible to change state in a modifier, it is unconventional and surprising. - -### Naming - -There are two common classes of modifiers, and their name should be prefixed accordingly to quickly represent their behavior: - -- Control flow modifiers: Prefix the modifier name with `if` in the case that a modifier only enables or disables the subsequent code in the modified method, but does not revert. -- Reverting modifiers: Prefix the modifier name with `validate` in the case that a modifier reverts if a condition is not met. - -### Return Values - -- If an address is cast as a contract type, return the type, do not cast back to the address type. This prevents the consumer of the method signature from having to cast again, but presents an equivalent API for off-chain APIs. Additionally it is a more declarative API, providing more context if we return a type. - -## Events - -- Events should only be triggered on state changes. If the value is set but not changed, we prefer avoiding a log emission indicating a change. (e.g. Either do not emit a log, or name the event `ConfigSet` instead of `ConfigUpdated`.) - -### Naming - -- When possible event names should correspond to the method they are in or the action that is being taken. Events preferably follow the format , where the action performed is the past tense of the imperative verb in the method name. e.g. calling `setConfig` should emit an event called `ConfigSet`, not `ConfigUpdated` in a method named `setConfig`. - -## Errors - -### Use Custom Errors - -Whenever possible (Solidity v0.8+) use [custom errors](https://blog.soliditylang.org/2021/04/21/custom-errors/) instead of emitting strings. This saves contract code size and simultaneously provides more informative error messages. - -### Expose Errors - -It is common to call a contract and then check the call succeeded: - -```javascript -(bool success, ) = to.call(data); -require(success, "Contract call failed"); -``` - -While this may look descriptive it swallows the error. Instead bubble up the error: - -```javascript -error YourError(bytes response); - -(bool success, bytes memory response) = to.call(data); -if (!success) { revert YourError(response); } -``` - -This will cost slightly more gas to copy the response into memory, but will ultimately make contract usage more understandable and easier to debug. Whether it is worth the extra gas is a judgement call you’ll have to make based on your needs. - -The original error will not be human readable in an off-chain explorer because it is RLP hex encoded but is easily decoded with standard Solidity ABI decoding tools, or a hex to UTF-8 converter and some basic ABI knowledge. - -## Control Flow - -### `if` Statements - -Always wrap the result statement of your `if` conditions in a closure, even if it is only one line. - -Bad: - -```javascript - if (condition) statement; -``` - -Good: - -```javascript - if (condition) { statement; } -``` - -## Interfaces - -### Scope - -- Interfaces should be as concise as reasonably possible. Break it up into smaller composable interfaces when that is sensible. - -### Naming - -- Up through Solidity version 0.8: Interfaces should be named `FooInterface`, this follows our historical naming pattern. -- Starting in Solidity v0.9: Interfaces should be named `IFoo` instead of `FooInterface`. This follows the patterns of popular [libraries like OpenZeppelin’s](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L9). - -## Vendor Dependencies - -- That’s it, vendor your Solidity dependencies. Supply chain attacks are all the rage these days. There is not yet a silver bullet for best way to vendor, it depends on the size of your project and your needs. You should be as explicit as possible about where the code comes from and make sure that this is enforced in some way; e.g. reference a hash. Some options: - - NPM packages work for repos already in the JavaScript ecosystem. If you go this route you should lock to a hash of the repo or use a proxy registry like GitHub Packages. - - Git submodules are great if you don’t mind git submodules. - - Copy and paste the code into a `vendor` directory. Record attribution of the code and license in the repo along with the commit or version that you pulled the code from. - -## Common Behaviors - -### Transferring Ownership - -- When transferring control, whether it is of a token or a role in a contract, prefer "safe ownership" transfer patterns where the recipient must accept ownership. This avoids accidentally burning the control. This is also inline with the secure pattern of [prefer pull over push](https://consensys.github.io/smart-contract-best-practices/recommendations/#favor-pull-over-push-for-external-calls). - -### Use Factories - -- If you expect multiple instances of a contract to be deployed, it is probably best to [build a factory](https://www.quicknode.com/guides/solidity/how-to-create-a-smart-contract-factory-in-solidity-using-hardhat) as this allows for simpler deployments later. Additionally it reduces the burden of verifying the correctness of the contract deployment. If many people have to deploy an instance of a contract then doing so with a contract makes it much easier for verification because instead of checking the code hash and/or the compiler and maybe the source code, you only have to check that the contract was deployed through a factory. -- Factories can add some pain when deploying with immutable variables. In general it is difficult to parse those out immutable variables from internal transactions. There is nothing inherently wrong with contracts deployed in this manner but at the time of writing they may not easily verify on Etherscan. - -### Call with Exact Gas - -- `call` accepts a gas parameter, but that parameter is a ceiling on gas usage. If a transaction does not have enough gas, `call` will simply provide as much gas as it safely can. This is unintuitive and can lead to transactions failing for unexpected reasons. We have [an implementation of `callWithExactGas`](https://github.com/smartcontractkit/chainlink/blob/075f3e2caf61b8685d2dc78714f1ee39764fda17/contracts/src/v0.8/KeeperRegistry.sol#L792) to ensure the precise gas amount requested is provided. - -## Picking a Pragma - -- If a contract or library is expected to be imported by outside parties then the pragma should be kept as loose as possible without sacrificing safety. We publish versions for every minor semver version of Solidity, and maintain a corresponding set of tests for each published version. - - Examples: libraries, interfaces, abstract contracts, and contracts expected to be inherited from -- Otherwise, Solidity contracts should have a pragma which is locked to a specific version. - - Example: Most concrete contracts. -- Avoid changing pragmas after audit. Unless there is a bug that has affects your contract, then you should try to stick to a known good pragma. In practice this means we typically only support one (occasionally two) pragma for any “major”(minor by semver naming) Solidity version. diff --git a/contracts/STYLE_GUIDE.md b/contracts/STYLE_GUIDE.md new file mode 100644 index 00000000000..3868117d4b9 --- /dev/null +++ b/contracts/STYLE_GUIDE.md @@ -0,0 +1,391 @@ +# Structure + +This guide is split into two sections: [Guidelines](#guidelines) and [Rules](#rules). +Guidelines are recommendations that should be followed but are hard to enforce in an automated way. +Rules are all enforced through CI, this can be through Solhint rules or other tools. + +## Background + +Our starting point is the [official Solidity Style Guide](https://docs.soliditylang.org/en/v0.8.21/style-guide.html) and [ConsenSys's Secure Development practices](https://consensys.github.io/smart-contract-best-practices/), but we deviate in some ways. We lean heavily on [Prettier](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/.prettierrc) for formatting, and if you have to set up a new Solidity project we recommend starting with [our prettier config](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/.prettierrc). We are trying to automate as much of this styleguide with Solhint as possible. + +This guide is not meant to be applied retroactively. There is no need to rewrite existing code to adhere to this guide, and when making (small) changes in existing files, it is not required to do so in accordance to this guide if it would conflict with other practices in that existing file. Consistency is preferred. + +We will be looking into `forge fmt`, but for now we still use `prettier`. + + +# Guidelines + +## Code Organization +- Group functionality together. E.g. Declare structs, events, and helper functions near the functions that use them. This is helpful when reading code because the related pieces are localized. It is also consistent with inheritance and libraries, which are separate pieces of code designed for a specific goal. +- 🤔Why not follow the Solidity recommendation of grouping by visibility? Visibility is clearly defined next to the method signature, making it trivial to check. However, searching can be deceiving because of inherited methods. Given this inconsistency in grouping, we find it easier to read and more consistent to organize code around functionality. Additionally, we recommend testing the public interface for any Solidity contract to ensure it only exposes expected methods. +- Follow the [Solidity folder structure CLIP](https://github.com/smartcontractkit/CLIPs/tree/main/clips/2023-04-13-solidity-folder-structure) + +### Delineate Unaudited Code + +- In a large repo it is worthwhile to keep code that has not yet been audited separate from the code that has been audited. This allows you to easily keep track of which files need to be reviewed. + - E.g. we keep unaudited code in a directory named `dev` that exists within each projects folder. Only once it has been audited we move the audited files out of `dev` and only then is it considered safe to deploy. + - This `dev` folder also has implications for when code is valid for bug bounties, so be extra careful to move functionality out of a `dev` folder. + + +## comments +- Besides comment above functions/structs, comments should live everywhere a reader might be confused. + Don’t overestimate the reader of your contract, expect confusion in many places and document accordingly. + This will help massively during audits and onboarding new team members. +- Headers should be used to group functionality, the following header style and length is recommended. + - Don’t use headers for a single function, or to say “getters”. Group by functionality e.g. the `Tokens and pools` , or `fees` logic within the CCIP OnRamp. + +```solidity + // ================================================================ + // │ Tokens and pools │ + // ================================================================ + +.... + + // ================================================================ + // │ Fees │ + // ================================================================ +``` + +## Variables + +- Function arguments are named like this: `argumentName`. No leading or trailing underscores necessary. +- Names should be explicit on the unit it contains, e.g. a network fee that is charged in USD cents + +```solidity +uint256 fee; // bad +uint256 networkFee; // bad +uint256 networkFeeUSD; // bad +uint256 networkFeeUSDCents; // good +``` + +### Types + +- If you are storing an address and know/expect it to be of a type(or interface), make the variable that type. This more clearly documents the behavior of this variable than the `address` type and often leads to less casting code whenever the address is used. + +### Structs + +- All structs should be packed to have the lowest memory footprint to reduce gas usage. Even structs that will never be written to storage should be packed. + - A contract can be considered a struct; it should also be packed to reduce gas cost. +- Structs should contain struct packing comments to clearly indicate the storage slot layout + - Using the exact characters from the example below will ensure visually appealing struct packing comments. + - Notice there is no line on the unpacked last `fee` item. +- Struct should contain comments, clearly indicating the denomination of values e.g. 0.01 USD if the variable name doesn’t already do that (which it should). + - Simple tool that could help packing structs and adding comments: https://github.com/RensR/Spack + +```solidity +/// @dev Struct to hold the fee configuration for a fee token, same as the FeeTokenConfig but with +/// token included so that an array of these can be passed in to setFeeTokenConfig to set the mapping +struct FeeTokenConfigArgs { + address token; // ────────────╮ Token address + uint32 networkFeeUSD; // │ Flat network fee to charge for messages, multiples of 0.01 USD + // │ multiline comments should work like this. More fee info + uint64 gasMultiplier; // ─────╯ Price multiplier for gas costs, 1e18 based so 11e17 = 10% extra cost + uint64 premiumMultiplier; // ─╮ Multiplier for fee-token-specific premiums + bool enabled; // ─────────────╯ Whether this fee token is enabled + uint256 fee; // The flat fee the user pays in juels +} +``` +## Functions + +### Naming + +- Function names should start with imperative verbs, not nouns or other tenses. + - `requestData` not `dataRequest` + - `approve` not `approved` + - `getFeeParameters` not `feeParameters` + +- Prefix your public getters with `get` and your public setters with `set`. + - `getConfig` and `setConfig`. + +### Return Values + +- If an address is cast as a contract type, return the type, do not cast back to the address type. + This prevents the consumer of the method signature from having to cast again, but presents an equivalent API for off-chain APIs. + Additionally, it is a more declarative API, providing more context if we return a type. + +## Modifiers + +- Only extract a modifier once a check is duplicated in multiple places. Modifiers arguably hurt readability, so we have found that they are not worth extracting until there is duplication. +- Modifiers should be treated as if they are view functions. They should not change state, only read it. While it is possible to change state in a modifier, it is unconventional and surprising. +- Modifiers tend to bloat contract size because the code is duplicated wherever the modifier is used. + +## Events + +- Events should only be triggered on state changes. If the value is set but not changed, we prefer avoiding a log emission indicating a change. (e.g. Either do not emit a log, or name the event `ConfigSet` instead of `ConfigUpdated`.) +- Events should be emitted for all state changes, not emitting should be an exception +- When possible event names should correspond to the method they are in or the action that is being taken. Events preferably follow the format , where the action performed is the past tense of the imperative verb in the method name. e.g. calling `setConfig` should emit an event called `ConfigSet`, not `ConfigUpdated` in a method named `setConfig`. + + +### Expose Errors + +It is common to call a contract and then check the call succeeded: + +```solidity +(bool success, ) = to.call(data); +require(success, "Contract call failed"); +``` + +While this may look descriptive it swallows the error. Instead, bubble up the error: + +```solidity +bool success; +retData = new bytes(maxReturnBytes); +assembly { + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0, 0) + + // limit our copy to maxReturnBytes bytes + let toCopy := returndatasize() + if gt(toCopy, maxReturnBytes) { + toCopy := maxReturnBytes + } + // Store the length of the copied bytes + mstore(retData, toCopy) + // copy the bytes from retData[0:_toCopy] + returndatacopy(add(retData, 0x20), 0, toCopy) +} +return (success, retData); +``` + +This will cost slightly more gas to copy the response into memory, but will ultimately make contract usage more understandable and easier to debug. Whether it is worth the extra gas is a judgement call you’ll have to make based on your needs. + +The original error will not be human-readable in an off-chain explorer because it is RLP hex encoded but is easily decoded with standard Solidity ABI decoding tools, or a hex to UTF-8 converter and some basic ABI knowledge. + + +## Interfaces + +- Interfaces should be as concise as reasonably possible. Break it up into smaller composable interfaces when that is sensible. + +## Dependencies + +- Prefer not reinventing the wheel, especially if there is an Openzeppelin wheel. +- The `shared` folder can be treated as a first party dependency and it is recommend to check if some functionality might already be in there before either writing it yourself or adding a third party dependency. +- When we have reinvented the wheel already (like with ownership), it is OK to keep using these contracts. If there are clear benefits of using another standard like OZ, we can deprecate the custom implementation and start using the new standard in all new projects. Migration will not be required unless there are serious issues with the old implementation. +- When the decision is made to use a new standard, it is no longer allowed to use the old standard for new projects. + +### Vendor dependencies + +- That’s it, vendor your Solidity dependencies. Supply chain attacks are all the rage these days. There is not yet a silver bullet for best way to vendor, it depends on the size of your project and your needs. You should be as explicit as possible about where the code comes from and make sure that this is enforced in some way; e.g. reference a hash. Some options: + - NPM packages work for repos already in the JavaScript ecosystem. If you go this route you should lock to a hash of the repo or use a proxy registry like GitHub Packages. + - Copy and paste the code into a `vendor` directory. Record attribution of the code and license in the repo along with the commit or version that you pulled the code from. + - Foundry uses git submodules for its dependencies. We only use the `forge-std` lib through submodules, we don’t import any non-Foundry-testing code through this method. + + +## Common Behaviors + +### Transferring Ownership + +- When transferring control, whether it is of a token or a role in a contract, prefer "safe ownership" transfer patterns where the recipient must accept ownership. This avoids accidentally burning the control. This is also inline with the secure pattern of [prefer pull over push](https://consensys.github.io/smart-contract-best-practices/recommendations/#favor-pull-over-push-for-external-calls). + +### Call with Exact Gas + +- `call` accepts a gas parameter, but that parameter is a ceiling on gas usage. If a transaction does not have enough gas, `call` will simply provide as much gas as it safely can. This is unintuitive and can lead to transactions failing for unexpected reasons. We have [an implementation of `callWithExactGas`](https://github.com/smartcontractkit/chainlink/blob/075f3e2caf61b8685d2dc78714f1ee39764fda17/contracts/src/v0.8/KeeperRegistry.sol#L792) to ensure the precise gas amount requested is provided. + +### Sending tokens + +- Prefer [ERC20.safeTransfer](https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#SafeERC20) over ERC20.transfer + +### Gas golfing + +- Golf your code. Make it cheap, within reason. + - Focus on the hot path +- Most of the cost of executing Solidity is related to reading/writing storage +- Calling other contracts will also be costly +- Common types to safely use are + - uint40 for timestamps (or uint32 if you really need the space) + - uint96 for link, as there are only 1b link tokens +- prefer `++i` over `i++` +- If you’re unsure about golfing, ask in the #tech-solidity channel + +## Testing + +- Test using Foundry. +- Aim for at least 90% *useful* coverage as a baseline, but (near) 100% is achievable in Solidity. Always 100% test the critical path. + - Make sure to test for each event emitted + - Test each reverting path +- Consider fuzzing, start with stateless (very easy in Foundry) and if that works, try stateful fuzzing. +- Consider fork testing if applicable + +### Foundry + +- Create a Foundry profile for each project folder in `foundry.toml` +- Foundry tests live in the project folder in `src`, not in the `contracts/test/` folder +- Set the block number and timestamp. It is preferred to set these values to some reasonable value close to reality. +- There should be no code between `vm.expectEmit`/`vm.expectRevert` and the function call + +## Picking a Pragma + +- If a contract or library is expected to be imported by outside parties then the pragma should be kept as loose as possible without sacrificing safety. We publish versions for every minor semver version of Solidity, and maintain a corresponding set of tests for each published version. + - Examples: libraries, interfaces, abstract contracts, and contracts expected to be inherited from +- Otherwise, Solidity contracts should have a pragma which is locked to a specific version. + - Example: Most concrete contracts. +- Avoid changing pragmas after audit. Unless there is a bug that has affects your contract, then you should try to stick to a known good pragma. In practice this means we typically only support one (occasionally two) pragma for any “major”(minor by semver naming) Solidity version. +- The current advised pragma is `0.8.19` or higher, lower versions should be avoided when starting a new project. Newer versions can be considered. +- All contracts should have a SPDX license identifier. If unsure about which one to pick, please consult with legal. Most older contracts have been MIT, but some of the newer products have been using BUSL-1.1 + + +## Versioning + +Contracts should implement the following interface + +```solidity +interface ITypeAndVersion { + function typeAndVersion() external pure returns (string memory); +} +``` + +Here are some examples of what this should look like: + +```solidity +contract AccessControlledFoo is Foo { + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "AccessControlledFoo 1.0.0"; +} + +contract OffchainAggregator is ITypeAndVersion { + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "OffchainAggregator 1.0.0"; + + function getData() public returns(uint256) { + return 4; + } +} + +// Next version of Aggregator contract +contract SuperDuperAggregator is ITypeAndVersion { + /// This is a new contract that has not been released yet, so we + /// add a `-dev` suffix to the typeAndVersion. + + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "SuperDuperAggregator 1.1.0-dev"; + + function getData() public returns(uint256) { + return 5; + } +} +``` + +All contracts will expose a `typeAndVersion` constant. +The string has the following format: `-` with the `-dev` part only being applicable to contracts that have not been fully released. +Try to fit it into 32 bytes to keep impact on contract sizes minimal. +Solhint will complain about a public constant variable that isn’t FULL_CAPS without the solhint-disable comment. + + + + + + + + + + +# Rules + +All rules have a `rule` tag which indicated how the rule is enforced. + + +## Comments + +- Comments should be in the `//` (default) or `///` (natspec) format, not the `/* */` format. + - rule: `tbd` +- Comments should follow [NatSpec](https://docs.soliditylang.org/en/latest/natspec-format.html) + - rule: `tbd` + +## Imports + +- Imports should always be explicit + - rule: `no-global-import` +- Imports have follow the following format: + - rule: `tbd` + +```solidity +import {IInterface} from "../interfaces/IInterface.sol"; + +import {AnythingElse} from "../code/AnythingElse.sol"; + +import {ThirdPartyCode} from "../../vendor/ThirdPartyCode.sol"; +``` + +- An example would be + +```solidity +import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; +import {IPool} from "../interfaces/pools/IPool.sol"; + +import {AggregateRateLimiter} from "../AggregateRateLimiter.sol"; +import {Client} from "../libraries/Client.sol"; + +import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.0/contracts/token/ERC20/IERC20.sol"; +``` + +## Variables + +### Visibility + +All contract variables should be private by default. Getters should be explicitly written and documented when you want to expose a variable publicly. +Whether a getter function reads from storage, a constant, or calculates a value from somewhere else, that’s all implementation details that should not be exposed to the consumer by casing or other conventions. + +rule: tbd + +### Naming and Casing + +- Storage variables prefixed with an `s_` to make it clear that they live in storage and are expensive to read and write: `s_variableName`. They should always be private, and you should write explicit getters if you want to expose a storage variable. + - rule: `chainlink-solidity/prefix-storage-variables-with-s-underscore` +- Immutable variables should be prefixed with an `i_` to make it clear that they are immutable. E.g. `i_decimalPlaces`. They should always be private, and you should write explicit getters if you want to expose an immutable variable. + - rule: `chainlink-solidity/prefix-immutable-variables-with-i` +- Internal/private constants should be all caps with underscores: `FOO_BAR`. Like other contract variables, constants should not be public. Create getter methods if you want to publicly expose constants. + - rule: `chainlink-solidity/all-caps-constant-storage-variables` +- Explicitly declare variable size: `uint256` not just `uint`. In addition to being explicit, it matches the naming used to calculate function selectors. + - rule: `explicit-types` +- Mapping should always be named if Solidity allows it (≥0.8.18) + - rule: `tbd` + + +## Functions + +### Visibility + +- Method visibility should always be explicitly declared. + - rule: `state-visibility` + +- Prefix private and internal methods with an underscore. There should never be a publicly callable method starting with an underscore. + - E.g. `_setOwner(address)` + - rule: `chainlink-solidity/prefix-internal-functions-with-underscore` + +### Return values + +- Returned values should always be explicit. Using named return values and then returning with an empty return should be avoided + - rule: `chainlink-solidity/explicit-returns` + +```solidity +// Bad +function getNum() external view returns (uint64 num) { + num = 4; + return; +} + +// Good +function getNum() external view returns (uint64 num) { + num = 4; + return num; +} + +// Good +function getNum() external view returns (uint64 num) { + return 4; +} +``` + +## Errors + +Use [custom errors](https://blog.soliditylang.org/2021/04/21/custom-errors/) instead of emitting strings. This saves contract code size and simultaneously provides more informative error messages. + +rule: `custom-errors` + +## Interfaces + +Interfaces should be named `IFoo` instead of `FooInterface`. This follows the patterns of popular [libraries like OpenZeppelin’s](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol#L9). + +rule: `tbd` \ No newline at end of file From 41025f45fcf3c5e7135d03f0e77dfb66ee770163 Mon Sep 17 00:00:00 2001 From: Tate Date: Fri, 10 Nov 2023 08:37:12 -0700 Subject: [PATCH 127/271] [TT-689] Send slack notification on test base image build/publish failure (#11248) --- .github/workflows/integration-tests-publish.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index 60f67f03574..a66ea612281 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -37,3 +37,11 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + - name: Notify Slack + if: failure() + uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 + env: + SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} + with: + channel-id: "#team-test-tooling-internal" + slack-message: ":x: :mild-panic-intensifies: Publish Integration Test Image failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" From 4a6f2fe749e19d92be013b9b8f59cefe829b07eb Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Fri, 10 Nov 2023 19:52:39 +0100 Subject: [PATCH 128/271] turn off 0.6 & 0.7 tests (#11132) * turn off 0.6 tests * reduce splits and mark other tests as sow * add modification protection * rm 0.7 tests --- .github/workflows/solidity.yml | 18 + contracts/ci.json | 22 +- contracts/test/v0.6/AggregatorFacade.test.ts | 167 - contracts/test/v0.6/BasicConsumer.test.ts | 243 -- contracts/test/v0.6/BlockhashStore.test.ts | 285 -- contracts/test/v0.6/Chainlink.test.ts | 186 - contracts/test/v0.6/ChainlinkClient.test.ts | 376 -- contracts/test/v0.6/CheckedMath.test.ts | 183 - .../v0.6/DeviationFlaggingValidator.test.ts | 296 -- contracts/test/v0.6/Flags.test.ts | 405 -- contracts/test/v0.6/FluxAggregator.test.ts | 3252 -------------- contracts/test/v0.6/Median.test.ts | 237 - contracts/test/v0.6/Owned.test.ts | 84 - contracts/test/v0.6/SignedSafeMath.test.ts | 187 - .../v0.6/SimpleReadAccessController.test.ts | 250 -- .../v0.6/SimpleWriteAccessController.test.ts | 214 - contracts/test/v0.6/VRFD20.test.ts | 303 -- contracts/test/v0.7/AggregatorProxy.test.ts | 743 ---- .../test/v0.7/AuthorizedForwarder.test.ts | 444 -- contracts/test/v0.7/Chainlink.test.ts | 186 - contracts/test/v0.7/ChainlinkClient.test.ts | 454 -- .../CompoundPriceFlaggingValidator.test.ts | 471 -- contracts/test/v0.7/ConfirmedOwner.test.ts | 136 - contracts/test/v0.7/KeeperRegistry1_1.test.ts | 1725 -------- contracts/test/v0.7/Operator.test.ts | 3819 ----------------- contracts/test/v0.7/OperatorFactory.test.ts | 293 -- .../v0.7/StalenessFlaggingValidator.test.ts | 632 --- .../v0.7/UpkeepRegistrationRequests.test.ts | 603 --- contracts/test/v0.7/VRFD20.test.ts | 303 -- contracts/test/v0.7/gasUsage.test.ts | 178 - 30 files changed, 27 insertions(+), 16668 deletions(-) delete mode 100644 contracts/test/v0.6/AggregatorFacade.test.ts delete mode 100644 contracts/test/v0.6/BasicConsumer.test.ts delete mode 100644 contracts/test/v0.6/BlockhashStore.test.ts delete mode 100644 contracts/test/v0.6/Chainlink.test.ts delete mode 100644 contracts/test/v0.6/ChainlinkClient.test.ts delete mode 100644 contracts/test/v0.6/CheckedMath.test.ts delete mode 100644 contracts/test/v0.6/DeviationFlaggingValidator.test.ts delete mode 100644 contracts/test/v0.6/Flags.test.ts delete mode 100644 contracts/test/v0.6/FluxAggregator.test.ts delete mode 100644 contracts/test/v0.6/Median.test.ts delete mode 100644 contracts/test/v0.6/Owned.test.ts delete mode 100644 contracts/test/v0.6/SignedSafeMath.test.ts delete mode 100644 contracts/test/v0.6/SimpleReadAccessController.test.ts delete mode 100644 contracts/test/v0.6/SimpleWriteAccessController.test.ts delete mode 100644 contracts/test/v0.6/VRFD20.test.ts delete mode 100644 contracts/test/v0.7/AggregatorProxy.test.ts delete mode 100644 contracts/test/v0.7/AuthorizedForwarder.test.ts delete mode 100644 contracts/test/v0.7/Chainlink.test.ts delete mode 100644 contracts/test/v0.7/ChainlinkClient.test.ts delete mode 100644 contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts delete mode 100644 contracts/test/v0.7/ConfirmedOwner.test.ts delete mode 100644 contracts/test/v0.7/KeeperRegistry1_1.test.ts delete mode 100644 contracts/test/v0.7/Operator.test.ts delete mode 100644 contracts/test/v0.7/OperatorFactory.test.ts delete mode 100644 contracts/test/v0.7/StalenessFlaggingValidator.test.ts delete mode 100644 contracts/test/v0.7/UpkeepRegistrationRequests.test.ts delete mode 100644 contracts/test/v0.7/VRFD20.test.ts delete mode 100644 contracts/test/v0.7/gasUsage.test.ts diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 069d9de45ab..5699657fa5d 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -20,11 +20,29 @@ jobs: - uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50 # v2.11.1 id: changes with: + list-files: "csv" filters: | src: - 'contracts/**/*' - '.github/workflows/solidity.yml' - '.github/workflows/solidity-foundry.yml' + old_sol: + - 'contracts/src/v0.4/**/*' + - 'contracts/src/v0.5/**/*' + - 'contracts/src/v0.6/**/*' + - 'contracts/src/v0.7/**/*' + + + - name: Fail if read-only files have changed + if: ${{ steps.changes.outputs.old_sol == 'true' }} + run: | + echo "One or more read-only Solidity file(s) has changed." + for file in ${{ steps.changes.outputs.old_sol_files }}; do + echo "$file was changed" + done + exit 1 + + prepublish-test: needs: [changes] diff --git a/contracts/ci.json b/contracts/ci.json index dd1fbb0a88f..f1eff76513c 100644 --- a/contracts/ci.json +++ b/contracts/ci.json @@ -6,23 +6,19 @@ "dir": "cross-version", "numOfSplits": 1 }, - { - "dir": "v0.6", - "numOfSplits": 1 - }, - { - "dir": "v0.7", - "numOfSplits": 1 - }, { "dir": "v0.8", - "numOfSplits": 8, + "numOfSplits": 6, "slowTests": [ - "Keeper", - "Cron.test", - "CronUpkeep.test", + "Cron", + "CronUpkeep", + "VRFSubscriptionBalanceMonitor", "EthBalanceMonitor", - "CanaryUpkeep" + "KeeperRegistrar", + "KeeperRegistry1_2", + "KeeperRegistry1_3", + "KeeperRegistry2_0", + "KeeperRegistry2_1" ] } ] diff --git a/contracts/test/v0.6/AggregatorFacade.test.ts b/contracts/test/v0.6/AggregatorFacade.test.ts deleted file mode 100644 index f85c24ae6cd..00000000000 --- a/contracts/test/v0.6/AggregatorFacade.test.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { ethers } from 'hardhat' -import { numToBytes32, publicAbi } from '../test-helpers/helpers' -import { assert } from 'chai' -import { Contract, ContractFactory, Signer } from 'ethers' -import { getUsers } from '../test-helpers/setup' -import { convertFufillParams, decodeRunRequest } from '../test-helpers/oracle' -import { bigNumEquals, evmRevert } from '../test-helpers/matchers' - -let defaultAccount: Signer - -let linkTokenFactory: ContractFactory -let aggregatorFactory: ContractFactory -let oracleFactory: ContractFactory -let aggregatorFacadeFactory: ContractFactory - -before(async () => { - const users = await getUsers() - - defaultAccount = users.roles.defaultAccount - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - defaultAccount, - ) - aggregatorFactory = await ethers.getContractFactory( - 'src/v0.4/Aggregator.sol:Aggregator', - defaultAccount, - ) - oracleFactory = await ethers.getContractFactory( - 'src/v0.6/Oracle.sol:Oracle', - defaultAccount, - ) - aggregatorFacadeFactory = await ethers.getContractFactory( - 'src/v0.6/AggregatorFacade.sol:AggregatorFacade', - defaultAccount, - ) -}) - -describe('AggregatorFacade', () => { - const jobId1 = - '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000001' - const previousResponse = numToBytes32(54321) - const response = numToBytes32(67890) - const decimals = 18 - const description = 'LINK / USD: Historic Aggregator Facade' - - let link: Contract - let aggregator: Contract - let oc1: Contract - let facade: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(defaultAccount).deploy() - oc1 = await oracleFactory.connect(defaultAccount).deploy(link.address) - aggregator = await aggregatorFactory - .connect(defaultAccount) - .deploy(link.address, 0, 1, [oc1.address], [jobId1]) - facade = await aggregatorFacadeFactory - .connect(defaultAccount) - .deploy(aggregator.address, decimals, description) - - let requestTx = await aggregator.requestRateUpdate() - let receipt = await requestTx.wait() - let request = decodeRunRequest(receipt.logs?.[3]) - await oc1.fulfillOracleRequest( - ...convertFufillParams(request, previousResponse), - ) - requestTx = await aggregator.requestRateUpdate() - receipt = await requestTx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - await oc1.fulfillOracleRequest(...convertFufillParams(request, response)) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(facade, [ - 'aggregator', - 'decimals', - 'description', - 'getAnswer', - 'getRoundData', - 'getTimestamp', - 'latestAnswer', - 'latestRound', - 'latestRoundData', - 'latestTimestamp', - 'version', - ]) - }) - - describe('#constructor', () => { - it('uses the decimals set in the constructor', async () => { - bigNumEquals(decimals, await facade.decimals()) - }) - - it('uses the description set in the constructor', async () => { - assert.equal(description, await facade.description()) - }) - - it('sets the version to 2', async () => { - bigNumEquals(2, await facade.version()) - }) - }) - - describe('#getAnswer/latestAnswer', () => { - it('pulls the rate from the aggregator', async () => { - bigNumEquals(response, await facade.latestAnswer()) - const latestRound = await facade.latestRound() - bigNumEquals(response, await facade.getAnswer(latestRound)) - }) - }) - - describe('#getTimestamp/latestTimestamp', () => { - it('pulls the timestamp from the aggregator', async () => { - const height = await aggregator.latestTimestamp() - assert.notEqual('0', height.toString()) - bigNumEquals(height, await facade.latestTimestamp()) - const latestRound = await facade.latestRound() - bigNumEquals( - await aggregator.latestTimestamp(), - await facade.getTimestamp(latestRound), - ) - }) - }) - - describe('#getRoundData', () => { - it('assembles the requested round data', async () => { - const previousId = (await facade.latestRound()).sub(1) - const round = await facade.getRoundData(previousId) - bigNumEquals(previousId, round.roundId) - bigNumEquals(previousResponse, round.answer) - bigNumEquals(await facade.getTimestamp(previousId), round.startedAt) - bigNumEquals(await facade.getTimestamp(previousId), round.updatedAt) - bigNumEquals(previousId, round.answeredInRound) - }) - - it('returns zero data for non-existing rounds', async () => { - const roundId = 13371337 - await evmRevert(facade.getRoundData(roundId), 'No data present') - }) - }) - - describe('#latestRoundData', () => { - it('assembles the requested round data', async () => { - const latestId = await facade.latestRound() - const round = await facade.latestRoundData() - bigNumEquals(latestId, round.roundId) - bigNumEquals(response, round.answer) - bigNumEquals(await facade.getTimestamp(latestId), round.startedAt) - bigNumEquals(await facade.getTimestamp(latestId), round.updatedAt) - bigNumEquals(latestId, round.answeredInRound) - }) - - describe('when there is no latest round', () => { - beforeEach(async () => { - aggregator = await aggregatorFactory - .connect(defaultAccount) - .deploy(link.address, 0, 1, [oc1.address], [jobId1]) - facade = await aggregatorFacadeFactory - .connect(defaultAccount) - .deploy(aggregator.address, decimals, description) - }) - - it('assembles the requested round data', async () => { - await evmRevert(facade.latestRoundData(), 'No data present') - }) - }) - }) -}) diff --git a/contracts/test/v0.6/BasicConsumer.test.ts b/contracts/test/v0.6/BasicConsumer.test.ts deleted file mode 100644 index ce0b7c643e2..00000000000 --- a/contracts/test/v0.6/BasicConsumer.test.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { ethers } from 'hardhat' -import { toWei, increaseTime5Minutes, toHex } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { BigNumber, constants, Contract, ContractFactory } from 'ethers' -import { Roles, getUsers } from '../test-helpers/setup' -import { bigNumEquals, evmRevert } from '../test-helpers/matchers' -import { - convertFufillParams, - decodeRunRequest, - encodeOracleRequest, - RunRequest, -} from '../test-helpers/oracle' -import cbor from 'cbor' -import { makeDebug } from '../test-helpers/debug' - -const d = makeDebug('BasicConsumer') -let basicConsumerFactory: ContractFactory -let oracleFactory: ContractFactory -let linkTokenFactory: ContractFactory - -let roles: Roles - -before(async () => { - roles = (await getUsers()).roles - basicConsumerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/BasicConsumer.sol:BasicConsumer', - roles.defaultAccount, - ) - oracleFactory = await ethers.getContractFactory( - 'src/v0.6/Oracle.sol:Oracle', - roles.oracleNode, - ) - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) -}) - -describe('BasicConsumer', () => { - const specId = '0x4c7b7ffb66b344fbaa64995af81e355a'.padEnd(66, '0') - const currency = 'USD' - const payment = toWei('1') - let link: Contract - let oc: Contract - let cc: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - oc = await oracleFactory.connect(roles.oracleNode).deploy(link.address) - cc = await basicConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, oc.address, specId) - }) - - it('has a predictable gas price [ @skip-coverage ]', async () => { - const rec = await ethers.provider.getTransactionReceipt( - cc.deployTransaction.hash ?? '', - ) - assert.isBelow(rec.gasUsed?.toNumber() ?? -1, 1750000) - }) - - describe('#requestEthereumPrice', () => { - describe('without LINK', () => { - it('reverts', async () => - await expect(cc.requestEthereumPrice(currency, payment)).to.be.reverted) - }) - - describe('with LINK', () => { - beforeEach(async () => { - await link.transfer(cc.address, toWei('1')) - }) - - it('triggers a log event in the Oracle contract', async () => { - const tx = await cc.requestEthereumPrice(currency, payment) - const receipt = await tx.wait() - - const log = receipt?.logs?.[3] - assert.equal(log?.address.toLowerCase(), oc.address.toLowerCase()) - - const request = decodeRunRequest(log) - const expected = { - path: ['USD'], - get: 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY', - } - - assert.equal(toHex(specId), request.specId) - bigNumEquals(toWei('1'), request.payment) - assert.equal(cc.address.toLowerCase(), request.requester.toLowerCase()) - assert.equal(1, request.dataVersion) - assert.deepEqual(expected, cbor.decodeFirstSync(request.data)) - }) - - it('has a reasonable gas cost [ @skip-coverage ]', async () => { - const tx = await cc.requestEthereumPrice(currency, payment) - const receipt = await tx.wait() - - assert.isBelow(receipt?.gasUsed?.toNumber() ?? -1, 140000) - }) - }) - }) - - describe('#fulfillOracleRequest', () => { - const response = ethers.utils.formatBytes32String('1,000,000.00') - let request: RunRequest - - beforeEach(async () => { - await link.transfer(cc.address, toWei('1')) - const tx = await cc.requestEthereumPrice(currency, payment) - const receipt = await tx.wait() - - request = decodeRunRequest(receipt?.logs?.[3]) - }) - - it('records the data given to it by the oracle', async () => { - await oc - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - const currentPrice = await cc.currentPrice() - assert.equal(currentPrice, response) - }) - - it('logs the data given to it by the oracle', async () => { - const tx = await oc - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - const receipt = await tx.wait() - - assert.equal(2, receipt?.logs?.length) - const log = receipt?.logs?.[1] - - assert.equal(log?.topics[2], response) - }) - - describe('when the consumer does not recognize the request ID', () => { - let otherRequest: RunRequest - - beforeEach(async () => { - // Create a request directly via the oracle, rather than through the - // chainlink client (consumer). The client should not respond to - // fulfillment of this request, even though the oracle will faithfully - // forward the fulfillment to it. - const args = encodeOracleRequest( - toHex(specId), - cc.address, - basicConsumerFactory.interface.getSighash('fulfill'), - 43, - constants.HashZero, - ) - const tx = await link.transferAndCall(oc.address, 0, args) - const receipt = await tx.wait() - - otherRequest = decodeRunRequest(receipt?.logs?.[2]) - }) - - it('does not accept the data provided', async () => { - d('otherRequest %s', otherRequest) - await oc - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(otherRequest, response)) - - const received = await cc.currentPrice() - - assert.equal(ethers.utils.parseBytes32String(received), '') - }) - }) - - describe('when called by anyone other than the oracle contract', () => { - it('does not accept the data provided', async () => { - await evmRevert( - cc.connect(roles.oracleNode).fulfill(request.requestId, response), - ) - - const received = await cc.currentPrice() - assert.equal(ethers.utils.parseBytes32String(received), '') - }) - }) - }) - - describe('#cancelRequest', () => { - const depositAmount = toWei('1') - let request: RunRequest - - beforeEach(async () => { - await link.transfer(cc.address, depositAmount) - const tx = await cc.requestEthereumPrice(currency, payment) - const receipt = await tx.wait() - - request = decodeRunRequest(receipt.logs?.[3]) - }) - - describe('before 5 minutes', () => { - it('cant cancel the request', () => - evmRevert( - cc - .connect(roles.consumer) - .cancelRequest( - oc.address, - request.requestId, - request.payment, - request.callbackFunc, - request.expiration, - ), - )) - }) - - describe('after 5 minutes', () => { - it('can cancel the request', async () => { - await increaseTime5Minutes(ethers.provider) - - await cc - .connect(roles.consumer) - .cancelRequest( - oc.address, - request.requestId, - request.payment, - request.callbackFunc, - request.expiration, - ) - }) - }) - }) - - describe('#withdrawLink', () => { - const depositAmount = toWei('1') - - beforeEach(async () => { - await link.transfer(cc.address, depositAmount) - const balance = await link.balanceOf(cc.address) - bigNumEquals(balance, depositAmount) - }) - - it('transfers LINK out of the contract', async () => { - await cc.connect(roles.consumer).withdrawLink() - const ccBalance = await link.balanceOf(cc.address) - const consumerBalance = BigNumber.from( - await link.balanceOf(await roles.consumer.getAddress()), - ) - bigNumEquals(ccBalance, 0) - bigNumEquals(consumerBalance, depositAmount) - }) - }) -}) diff --git a/contracts/test/v0.6/BlockhashStore.test.ts b/contracts/test/v0.6/BlockhashStore.test.ts deleted file mode 100644 index 453b2eca3b1..00000000000 --- a/contracts/test/v0.6/BlockhashStore.test.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { Contract, ContractFactory } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' - -let personas: Personas -let blockhashStoreTestHelperFactory: ContractFactory - -type TestBlocks = { - num: number - rlpHeader: Uint8Array - hash: string -} - -const mainnetBlocks: TestBlocks[] = [ - { - num: 10000467, - rlpHeader: ethers.utils.arrayify( - '0xf90215a058ee3c05e880cb25a3db92b9f1479c5453690ca97f9bcbb18d21965d3213578ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea674fdde714fd979de3edf0f56aa9716b898ec8a0a448355652812a7d518b5c979a15bba02cfe4576d8eb61e8b5731ecc37f2bec6a0049f25ed97f9ed9a9c8521ab39cd2c48438d1d18c84dcab5bf494c19595bd462a0b1169f28bdbe5dd61ebc20b7a459be9d7fa898f5a3ba5fed6d502d94b9a8101bb901001000008180000210000080010001080310e004800c3040000060484000010804088050044302a500240041040010012120840002400005092000808000640012081000880010008040200208000004050800400002244044006041040040010890040504020040008004222502000800220000021800006400802036500000000400014640d00020002110000001440000001509543802080004210004100de04744a2810000000032250080810000502210c04289480800000423080800004000a020220030203000020001000000042c00420090000008003308459020e010a01000200190900040e81000040040000020000a8044001000202010000600c087086c49cadb1b57839898538398909483984b9e845eb02fbf94505059452d65746865726d696e652d6575312d34a06d0287c21536fac432714bd3f3712ff1a7e409faf1b10edac9b9547da1d4f7b188930531280477460c', - ), - hash: '0x4a65bcdf3466a16740b74849cc10fc57d4acb24cce148665482812699a400464', - }, - { - num: 10000468, - rlpHeader: ethers.utils.arrayify( - '0xf9020da04a65bcdf3466a16740b74849cc10fc57d4acb24cce148665482812699a400464a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479404668ec2f57cc15c381b461b9fedab5d451c8f7fa0bcd4ddbb7125a5c06df96d862921dc0bba8664b3f759a233fe565a615c0ab3eaa0087ab379852c83e4b660de1668fc93333201ad0d233167ea6cef8bacaf5cba2aa0d81855037b2a6b56eba0c2ed129fb4102fb831b0baa187a0f6e0c155400f7855b9010080040040200000000010102081000000500040010408040800010110000000008000005808020000902021818000210000000000081100401000400014400001041008000020448800180128800008000200000420e01200000000000000011000001000020000208000b42200a0008000510200080200008c002018108010014030200000080000000002000010008000011008004003081000400080100803040080040300000002044080480000000000008080101000000050000000000840000002200040000a0080000442008006005502800000040008000890201002022402208002900020900000000080000100100201080000000003400000004887086d57541477ba839898548398968083989147845eb02fc28c73706964657230380b03ac53a076c676a0ab090b373b6242851a4beab7b8cdc9d3ebe211747a255b78c0278c42880ea13d40042dd1e6', - ), - hash: '0x00fd2589a272b85ffaf63223641571bf95891c936b7514ee4e87a593e52de7c9', - }, - { - num: 10000469, - rlpHeader: ethers.utils.arrayify( - '0xf90211a000fd2589a272b85ffaf63223641571bf95891c936b7514ee4e87a593e52de7c9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347945a0b54d5dc17e0aadc383d2db43b0a0d3e029c4ca01b28d3b4e4d3442a9e6caed9f80b6a639bce6f3a283d4e004e6bb44e483ceeeba067c00d9067bc023b8fab8e3afd1bc0f2470f08003bdf9f167fbfeede2422ac4ea09d8b344d9ab1b7288f8c5e644c53b1a5288da6d6ee0e388ec76f291def48da15b90100c462095870a26a0804132e208110329710d459054558159c103208d44820002016108136199200061063699d8400254a22828c11b5512e3303c98ec7747cc02d00161880c2f2c580e806bccc04805190265f096601342058020a8324c277735d8202220412f03303201252a3000038883a4bb0010e6b004408306232150a84d110100d0c4b9d228022812602c05c801d20500d4ed10010ce2400428a96950a98050c00e603292a806c4983b25814880000440a23821191121996410c5110c949616c2066a4a0488087d4c226c14208042c00d609b5cc44051400219d93626818728612a9b18690e03c902014a900e0018828011494b80d4708799b0d8a83cace87086e64fefefb48839898558398968083986664845eb02fc7906574682d70726f2d687a662d74303032a09f1918a362b55ebd072cc9548fb74f89301d41c2a1feb13c08a1c2c3cb0606d88810dfa530069367fb', - ), - hash: '0x325fde74e261fc483a16506bbc711b645b043ad24c7db0136845a1be262cf0c9', - }, - { - num: 10000470, - rlpHeader: ethers.utils.arrayify( - '0xf90215a0325fde74e261fc483a16506bbc711b645b043ad24c7db0136845a1be262cf0c9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea674fdde714fd979de3edf0f56aa9716b898ec8a020647cfa35563093442a12d80bf2bacb83da1de8340366677f3822591a334ccea066ad7285f6c5b6407f62c6b65a83aeaaa71ad9a97c2bb15139140f2dbb60f7e0a0c0e633851d0b5ce661ecc054517425e82425fcc6170db9693e5b5a6dd5ef6d6bb90100c0c000c1520708182080c8e461891c2402800a80d44a00034259414012012a5006a1416331181504902044960808f1129018800311621e920886804693749b10542400142e984580ccba634881c4156962200ecfb004000005468db44842781c59923110262660802315006106388b028412c42c000820c508e66b7851fa68002008144cd7860cd884280802915163399c168d5a11b0649486084110149469a1e61c31134204b903206566885180bc0426c0c6c0a4d408e182242f08180d204c624a040248425041ac028010d088820402ba4bd38c2d1215829300543465603822110500811290490148049300040e000c280086a09e8100089818ce480a887e87086c4965bf3c8a839898568398705c839847d2845eb02fe994505059452d65746865726d696e652d6575312d35a09d8ae288d0eede524f3ef5e6cfcc5ba07f380bc695bb71578a7b91cfa517071b8859d0976006378e52', - ), - hash: '0x5cf096dfd1fc2d2947a96fdec5377ab7beaa0eb00c80728a3b96f0864cec506a', - }, -] - -const maticBlocks: TestBlocks[] = [ - { - num: 10000467, - rlpHeader: ethers.utils.arrayify( - '0xf9025da0212093b89337e6741aca0c6c1cbfc64b56155bdcc3623fa9bcbfa0498fa135aba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0ac0ec242516093308f7a2cc6965f38835eb3db69cba93401daef672666a3aefea0d06985e9ae671d22fb4b3d73ef4e074a448f66b4769ad8d59c3b8aef1ede15e2a00076d4897a88e08c25ca12c558f622d03d532d8b674e8c6b9847000b98dbe480b90100040000000200000000000000000000000000000000000000100000000000400000000000000000000000002800000000100080000000000000000001000000400000000000000000000000080002008000000000000000000021000000000000000000000200000000001000000008000000000008000001800800100000000000010000000010100000800000000000001000000200100000000000000000002000000004000000000000000080010000000000000000200000000000000040000000420000000000010000000000000004040004000000001000001000200100100080000000000400000000100000100000000000000000000000021000000e839898538401312d008302e54b84600df884b861d78301091883626f7288676f312e31352e35856c696e7578000000000000000003eb49c29f5facd767206f64b8a5c9b325bced5c9156f489c6281c68eddc9e5f2ef1177c02a99d8ab6216dcf2879eefddfc27c75ffa9ef6a2185ce9983d1434901a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0x6c3b869ca26fece236545f7914d8249651d729852dc1445f53a94d5a59cdc9da', - }, - { - num: 10000468, - rlpHeader: ethers.utils.arrayify( - '0xf9025da06c3b869ca26fece236545f7914d8249651d729852dc1445f53a94d5a59cdc9daa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fa236c78bbe5939cc62985e32582c2158468a5b2b4dd02d514edb0bea95f0fd3a0e05ccfb09764e5cd6811ef2c2616d4a57f187be84235e2569c9b8d70489f1a44a0aea27aed2ad1d553e30501e6fe47fee0842c3b7ce5867e579b29975f02ec4282b90100008000100000000000000400080800000009000000010020000000000800000000000000080000000000000000000000000080000080000820400000000000000000200000000000000000080000008000200000200000000003009000020000000200000010000000001000000000000000000000000000800040100000000000000000000010000000100100000000000000000102004000000040000000002000000008000000000000000000000000000000200000000000000000000041000000020000080001010000000000000008000000110000001001800020000000100000000001400000040000000000000010010000000001000000001000000e839898548401312d00830494ed84600df886b861d78301091883626f7288676f312e31352e35856c696e75780000000000000000aa8ed86143b48b6aa7170d2083c3a7be31cbdfdc40f39badb8747f4c2198279a71c0d3eb5d25f3b7da5a48b887f61e22fe0baa692aa03807ad12f6fe25af087e00a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0x258aa48bde013579fbfef2e222bcc222b1f57bf898a71c623f9024229c9f6111', - }, - { - num: 10000469, - rlpHeader: ethers.utils.arrayify( - '0xf9025aa0258aa48bde013579fbfef2e222bcc222b1f57bf898a71c623f9024229c9f6111a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fa236c78bbe5939cc62985e32582c2158468a5b2b4dd02d514edb0bea95f0fd3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e839898558401312d008084600df888b861d78301091883626f7288676f312e31352e35856c696e75780000000000000000bd8668cc5d89583a7cc26fb96650e61f045ffe5248ae80c667ba7648df41e3d552060998ac151f2d15bd1b98f0a2a50c4281729a4c0aae4758a3bad280207c2901a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0x611779767f1deb5a17723ec71d1b397b18a0fc9a40d282810a33bd6a0a5f46f9', - }, - { - num: 10000470, - rlpHeader: ethers.utils.arrayify( - '0xf9025aa0611779767f1deb5a17723ec71d1b397b18a0fc9a40d282810a33bd6a0a5f46f9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fa236c78bbe5939cc62985e32582c2158468a5b2b4dd02d514edb0bea95f0fd3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e839898568401312d008084600df88ab861d78301091883626f7288676f312e31352e35856c696e75780000000000000000b617675c3b01e98319508130e1a583d57ce6b3a8a97fa2fbdaa33673cc6c609d6f7c361c833838f54b724d3a83cdd73e2398bb147970cd0b057865386cb08e1300a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0x2edf2f5c5faa5046b2304f76c92096a25e7c4343a7b75c36b29e8e9755d93397', - }, -] - -// The following headers from Binance Smart Chain were retrieved using `go run -// binance.go`, where binance.go contains -// -// package main -// -// import ( -// "context" -// "fmt" -// "log" -// "math/big" -// "math/rand" -// "strings" -// -// "github.com/ethereum/go-ethereum/ethclient" -// "github.com/ethereum/go-ethereum/rlp" -// ) -// -// var tsBlockTemplate = ` -// { -// num: %d, -// rlpHeader: ethers.utils.arrayify( -// '0x%x', -// ), -// hash: '0x%x', -// }, -// ` -// -// func main() { -// client, err := ethclient.Dial("https://bsc-dataseed.binance.org/") -// if err != nil { -// log.Fatal(err) -// } -// -// header, err := client.HeaderByNumber(context.Background(), nil) -// if err != nil { -// log.Fatal(err) -// } -// topBlockNum := header.Number.Int64() -// numBlocks := int64(4) -// if topBlockNum < numBlocks { -// log.Fatalf("need at least %d consecutive blocks", numBlocks) -// } -// targetBlock := int64(rand.Intn(int(topBlockNum - numBlocks))) -// simulatedHeadBlock := targetBlock + numBlocks - 1 -// for blockNum := targetBlock; blockNum <= simulatedHeadBlock; blockNum++ { -// header, err := client.HeaderByNumber(context.Background(), big.NewInt(blockNum)) -// if err != nil { -// log.Fatal(err) -// } -// s, err := rlp.EncodeToBytes(header) -// if err != nil { -// log.Fatalf("could not encode header: got error %s from %v", err, header) -// } -// // fmt.Printf("header for block number %d: 0x%x\n", blockNum, s) -// fmt.Printf(strings.TrimLeft(tsBlockTemplate, "\n"), blockNum, s, header.Hash()) -// } -// } -const binanceBlocks: TestBlocks[] = [ - { - num: 1875651, - rlpHeader: ethers.utils.arrayify( - '0xf9025da029c26248bebbe0d0acb209d13ac9337c4b5c313696c031dd63b3cd16cbdc0c21a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794b8f7166496996a7da21cf1f1b04d9b3e26a3d077a03f962867b5e86191c3280bd52c4249587e08ddfa9851cea981fb7a5721c9157aa05924ae05d17347687ba81d093aee159ccc65cefc8314b0515ef921e553df05a2a089af99a7afa586e7d67062d051df4255304bb730f6d62fdd3bdb207f1513b23bb901000100000000000000000800000000000000000000000200000000000000800000000000000200100000000000000800000000000000000000000000000000000000000000000000800000140800000008201000001000000202000000001200000000002002020000000000000000080000000000000002000000001000000000000002000000008010000000000000000002040080008400280000c00000081000400000004000000010000000020000000000000000000000000000000000000001000210200000000000000000000800000000000000000000000000002010000004000000000001000000000000000000000800020000000000000000000002831c9ec38401c9c380830789c2845f9faab1b861d883010002846765746888676f312e31332e34856c696e7578000000000000003311ee6830f31dc9116d8a59178b539d91eb6811c1d533c4a59bf77262689c552218bb1eae9cb9d6bf6e1066bea78052c8767313ace71c919d02e70760bd255401a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0xe0a935b1e37420ac1d855215bdad4730a5ffe315eda287c6c18aa86c426ede74', - }, - { - num: 1875652, - rlpHeader: ethers.utils.arrayify( - '0xf9025da0e0a935b1e37420ac1d855215bdad4730a5ffe315eda287c6c18aa86c426ede74a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794c2be4ec20253b8642161bc3f444f53679c1f3d47a0dbf2d40cf5533b65ac9b5be35cac75f7b106244ba14ee476a832c28d46a53452a04f83b8a51d3e17b6a02a0caa79acc7597996c5b8c68dba12c33095ae086089eea02fa2642645b2de17227a6c18c3fa491f54b3bdfe8ac8e04924a33a005a0e9e61b901000100000100000000000008000000000000000000040000000000000000800000000000000000000000000000000800000800000000000400000000000020000040100080000000000000000800000000209000001000000200000000801000400800002002030000000000000100080000002000000002004000011000000002000100040000000000100000000000000000040100009000300000000000000002004000004000000000000000020000002000000010000000200000800000000001000280000000000000008000000000000000800000000000020000002000041000000000000001200020001000080000002a40020040000000000000000002831c9ec48401c9c38083044b40845f9faab4b861d883010002846765746888676f312e31332e34856c696e757800000000000000cfc02687b2394922055792a8e67dad566f6690de06b229d752433b2067207b5f43b9f3c63f91cea5a79bbfc51d9132b933a706ab504038a92f37d57af2bb6c2e01a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0x629e5abcae42940e00d7b38aa7b2ecccfbab582cb7a0b2c3658c2dad8e66549d', - }, - { - num: 1875653, - rlpHeader: ethers.utils.arrayify( - '0xf9025da0629e5abcae42940e00d7b38aa7b2ecccfbab582cb7a0b2c3658c2dad8e66549da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ce2fd7544e0b2cc94692d4a704debef7bcb61328a0718e7db53041585a814d658c32c88fd550c2c6d200826020008925e0a0f7967fa000fbf842e492a47cc9786885783259a08aed71055e78216006408932515fd960a0c7ffeb2189b8fcde43733cf1958cdb1c38c44052cfbb41125382240c232a98f8b901000000000000000000000000000000000000000002000000000004000000000000000000010000000000000000000000000000000000000200000000004020200000010000000800000000208800000000201000000000000000080000000000000000002002220000000000000000080000000000000000000000001000000000100000000000080010000000000000000000040000000000000000000000000002000000000008000000004000000000000000000000200000000000000000000000000202000000000000000000000000000000000008000000000000002080001000000000000001000000000000000000080100000000000000000000000002831c9ec58401c9c38083025019845f9faab7b861d883010002846765746888676f312e31332e34856c696e7578000000000000008c3c7a5c83e930fbd9d14f83c9b3931f032f0f678919c35b8b32ca6dae9948950bfa326fae134fa234fa7b84c06bdc3f7c6d6414c2a266df1339e563be8bd9cc00a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0xae8574651adabfd0ca55e2cee0e2e639ced73ec1cc0a35debeeceee6943442a9', - }, - { - num: 1875654, - rlpHeader: ethers.utils.arrayify( - '0xf9025da0ae8574651adabfd0ca55e2cee0e2e639ced73ec1cc0a35debeeceee6943442a9a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d6caa02bbebaebb5d7e581e4b66559e635f805ffa02df6a1173c63ec0a8acc46c818670f030aece1154b9f3bbc70f46a8427dd8dd6a0fa8835c499682d8c90759ff9ea1c291048755b967e48880a0fc21d19ec034a59a0b4e22607cb105c04156044b3f98c2cecae1553b45aa9b6044c37573791a27576b901000200000008000000000001000000000000000000000020000000000000020000000000000000000000000000000000000000000000000040000000000220000000000000000400000000001802000000201000000000000000000000000000000000002002020000000000000000080000000000000000000000001000000000000000000000100000000000000000000000040000000000000000010200200002000400000000400000000200000000000000080000000000000000000008000000000200000000000000000000000000000000000000000000000000002000001000000000000001000000000000000000000000000000000008080000000002831c9ec68401c9c3808301e575845f9faabab861d883010002846765746888676f312e31332e34856c696e757800000000000000399e73b0e963ec029e815623a414aa852508a28dd9799a1bf4e2380c8db687a46cc5b6cc20352ae21e35cfd28124a32fcd49ac8fac5b03901b3e03963e4fff5801a00000000000000000000000000000000000000000000000000000000000000000880000000000000000', - ), - hash: '0x189990455c59a5dea78071df9a2008ede292ff0a062fc5c4c6ca35fbe476f834', - }, -] - -before(async () => { - personas = (await getUsers()).personas - blockhashStoreTestHelperFactory = await ethers.getContractFactory( - 'src/v0.6/tests/BlockhashStoreTestHelper.sol:BlockhashStoreTestHelper', - personas.Default, - ) -}) - -runBlockhashStoreTests(mainnetBlocks, 'Ethereum') -runBlockhashStoreTests(maticBlocks, 'Matic') -runBlockhashStoreTests(binanceBlocks, 'Binance Smart Chain') - -async function runBlockhashStoreTests( - blocks: TestBlocks[], - description: string, -) { - describe(`BlockhashStore (${description})`, () => { - let blockhashStoreTestHelper: Contract - - beforeEach(async () => { - blockhashStoreTestHelper = await blockhashStoreTestHelperFactory - .connect(personas.Default) - .deploy() - - const [lastBlock] = blocks.slice(-1) - await blockhashStoreTestHelper - .connect(personas.Default) - .godmodeSetHash(lastBlock.num, lastBlock.hash) - assert.strictEqual( - await blockhashStoreTestHelper.getBlockhash(lastBlock.num), - lastBlock.hash, - ) - }) - - it('getBlockhash reverts for unknown blockhashes', async () => { - await expect( - blockhashStoreTestHelper.getBlockhash(99999999), - ).to.be.revertedWith('blockhash not found in store') - }) - - it('storeVerifyHeader records valid blockhashes', async () => { - for (let i = blocks.length - 2; i >= 0; i--) { - assert.strictEqual( - ethers.utils.keccak256(blocks[i + 1].rlpHeader), - await blockhashStoreTestHelper.getBlockhash(blocks[i + 1].num), - ) - await blockhashStoreTestHelper - .connect(personas.Default) - .storeVerifyHeader(blocks[i].num, blocks[i + 1].rlpHeader) - assert.strictEqual( - await blockhashStoreTestHelper.getBlockhash(blocks[i].num), - blocks[i].hash, - ) - } - }) - - it('storeVerifyHeader rejects unknown headers', async () => { - const unknownBlock = blocks[0] - await expect( - blockhashStoreTestHelper - .connect(personas.Default) - .storeVerifyHeader(unknownBlock.num - 1, unknownBlock.rlpHeader), - ).to.be.revertedWith('header has unknown blockhash') - }) - - it('storeVerifyHeader rejects corrupted headers', async () => { - const [lastBlock] = blocks.slice(-1) - const modifiedHeader = new Uint8Array(lastBlock.rlpHeader) - modifiedHeader[137] += 1 - await expect( - blockhashStoreTestHelper - .connect(personas.Default) - .storeVerifyHeader(lastBlock.num - 1, modifiedHeader), - ).to.be.revertedWith('header has unknown blockhash') - }) - - it('store accepts recent block numbers', async () => { - await ethers.provider.send('evm_mine', []) - - const n = (await ethers.provider.getBlockNumber()) - 1 - await blockhashStoreTestHelper.connect(personas.Default).store(n) - - assert.equal( - await blockhashStoreTestHelper.getBlockhash(n), - (await ethers.provider.getBlock(n)).hash, - ) - }) - - it('store rejects future block numbers', async () => { - await expect( - blockhashStoreTestHelper.connect(personas.Default).store(99999999999), - ).to.be.revertedWith('blockhash(n) failed') - }) - - it('store rejects old block numbers', async () => { - for (let i = 0; i < 300; i++) { - await ethers.provider.send('evm_mine', []) - } - - await expect( - blockhashStoreTestHelper - .connect(personas.Default) - .store((await ethers.provider.getBlockNumber()) - 256), - ).to.be.revertedWith('blockhash(n) failed') - }) - - it('storeEarliest works', async () => { - for (let i = 0; i < 300; i++) { - await ethers.provider.send('evm_mine', []) - } - - await blockhashStoreTestHelper.connect(personas.Default).storeEarliest() - - const n = (await ethers.provider.getBlockNumber()) - 256 - assert.equal( - await blockhashStoreTestHelper.getBlockhash(n), - (await ethers.provider.getBlock(n)).hash, - ) - }) - }) -} diff --git a/contracts/test/v0.6/Chainlink.test.ts b/contracts/test/v0.6/Chainlink.test.ts deleted file mode 100644 index f3587dc30a7..00000000000 --- a/contracts/test/v0.6/Chainlink.test.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi, decodeDietCBOR, hexToBuf } from '../test-helpers/helpers' -import { assert } from 'chai' -import { Contract, ContractFactory, providers, Signer } from 'ethers' -import { Roles, getUsers } from '../test-helpers/setup' -import { makeDebug } from '../test-helpers/debug' - -const debug = makeDebug('ChainlinkTestHelper') -let concreteChainlinkFactory: ContractFactory - -let roles: Roles - -before(async () => { - roles = (await getUsers()).roles - concreteChainlinkFactory = await ethers.getContractFactory( - 'src/v0.6/tests/ChainlinkTestHelper.sol:ChainlinkTestHelper', - roles.defaultAccount, - ) -}) - -describe('ChainlinkTestHelper', () => { - let ccl: Contract - let defaultAccount: Signer - - beforeEach(async () => { - defaultAccount = roles.defaultAccount - ccl = await concreteChainlinkFactory.connect(defaultAccount).deploy() - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(ccl, [ - 'add', - 'addBytes', - 'addInt', - 'addStringArray', - 'addUint', - 'closeEvent', - 'setBuffer', - ]) - }) - - async function parseCCLEvent(tx: providers.TransactionResponse) { - const receipt = await tx.wait() - const data = receipt.logs?.[0].data - const d = debug.extend('parseCCLEvent') - d('data %s', data) - return ethers.utils.defaultAbiCoder.decode(['bytes'], data ?? '') - } - - describe('#close', () => { - it('handles empty payloads', async () => { - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, {}) - }) - }) - - describe('#setBuffer', () => { - it('emits the buffer', async () => { - await ccl.setBuffer('0xA161616162') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { a: 'b' }) - }) - }) - - describe('#add', () => { - it('stores and logs keys and values', async () => { - await ccl.add('first', 'word!!') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { first: 'word!!' }) - }) - - it('handles two entries', async () => { - await ccl.add('first', 'uno') - await ccl.add('second', 'dos') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - assert.deepEqual(decoded, { - first: 'uno', - second: 'dos', - }) - }) - }) - - describe('#addBytes', () => { - it('stores and logs keys and values', async () => { - await ccl.addBytes('first', '0xaabbccddeeff') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - const expected = hexToBuf('0xaabbccddeeff') - assert.deepEqual(decoded, { first: expected }) - }) - - it('handles two entries', async () => { - await ccl.addBytes('first', '0x756E6F') - await ccl.addBytes('second', '0x646F73') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - const expectedFirst = hexToBuf('0x756E6F') - const expectedSecond = hexToBuf('0x646F73') - assert.deepEqual(decoded, { - first: expectedFirst, - second: expectedSecond, - }) - }) - - it('handles strings', async () => { - await ccl.addBytes('first', ethers.utils.toUtf8Bytes('apple')) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - const expected = ethers.utils.toUtf8Bytes('apple') - assert.deepEqual(decoded, { first: expected }) - }) - }) - - describe('#addInt', () => { - it('stores and logs keys and values', async () => { - await ccl.addInt('first', 1) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { first: 1 }) - }) - - it('handles two entries', async () => { - await ccl.addInt('first', 1) - await ccl.addInt('second', 2) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - assert.deepEqual(decoded, { - first: 1, - second: 2, - }) - }) - }) - - describe('#addUint', () => { - it('stores and logs keys and values', async () => { - await ccl.addUint('first', 1) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { first: 1 }) - }) - - it('handles two entries', async () => { - await ccl.addUint('first', 1) - await ccl.addUint('second', 2) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - assert.deepEqual(decoded, { - first: 1, - second: 2, - }) - }) - }) - - describe('#addStringArray', () => { - it('stores and logs keys and values', async () => { - await ccl.addStringArray('word', [ - ethers.utils.formatBytes32String('seinfeld'), - ethers.utils.formatBytes32String('"4"'), - ethers.utils.formatBytes32String('LIFE'), - ]) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { word: ['seinfeld', '"4"', 'LIFE'] }) - }) - }) -}) diff --git a/contracts/test/v0.6/ChainlinkClient.test.ts b/contracts/test/v0.6/ChainlinkClient.test.ts deleted file mode 100644 index bfd43d7f3f9..00000000000 --- a/contracts/test/v0.6/ChainlinkClient.test.ts +++ /dev/null @@ -1,376 +0,0 @@ -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { Contract, ContractFactory } from 'ethers' -import { Roles, getUsers } from '../test-helpers/setup' -import { - convertFufillParams, - decodeCCRequest, - decodeRunRequest, - RunRequest, -} from '../test-helpers/oracle' -import { decodeDietCBOR } from '../test-helpers/helpers' -import { evmRevert } from '../test-helpers/matchers' - -let concreteChainlinkClientFactory: ContractFactory -let emptyOracleFactory: ContractFactory -let getterSetterFactory: ContractFactory -let oracleFactory: ContractFactory -let linkTokenFactory: ContractFactory - -let roles: Roles - -before(async () => { - roles = (await getUsers()).roles - - concreteChainlinkClientFactory = await ethers.getContractFactory( - 'src/v0.6/tests/ChainlinkClientTestHelper.sol:ChainlinkClientTestHelper', - roles.defaultAccount, - ) - emptyOracleFactory = await ethers.getContractFactory( - 'src/v0.6/tests/EmptyOracle.sol:EmptyOracle', - roles.defaultAccount, - ) - getterSetterFactory = await ethers.getContractFactory( - 'src/v0.5/tests/GetterSetter.sol:GetterSetter', - roles.defaultAccount, - ) - oracleFactory = await ethers.getContractFactory( - 'src/v0.4/Oracle.sol:Oracle', - roles.defaultAccount, - ) - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) -}) - -describe('ChainlinkClientTestHelper', () => { - const specId = - '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' - let cc: Contract - let gs: Contract - let oc: Contract - let newoc: Contract - let link: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - oc = await oracleFactory.connect(roles.defaultAccount).deploy(link.address) - newoc = await oracleFactory - .connect(roles.defaultAccount) - .deploy(link.address) - gs = await getterSetterFactory.connect(roles.defaultAccount).deploy() - cc = await concreteChainlinkClientFactory - .connect(roles.defaultAccount) - .deploy(link.address, oc.address) - }) - - describe('#newRequest', () => { - it('forwards the information to the oracle contract through the link token', async () => { - const tx = await cc.publicNewRequest( - specId, - gs.address, - ethers.utils.toUtf8Bytes('requestedBytes32(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - - assert.equal(1, receipt.logs?.length) - const [jId, cbAddr, cbFId, cborData] = receipt.logs - ? decodeCCRequest(receipt.logs[0]) - : [] - const params = decodeDietCBOR(cborData ?? '') - - assert.equal(specId, jId) - assert.equal(gs.address, cbAddr) - assert.equal('0xed53e511', cbFId) - assert.deepEqual({}, params) - }) - }) - - describe('#chainlinkRequest(Request)', () => { - it('emits an event from the contract showing the run ID', async () => { - const tx = await cc.publicRequest( - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - - const { events, logs } = await tx.wait() - - assert.equal(4, events?.length) - - assert.equal(logs?.[0].address, cc.address) - assert.equal(events?.[0].event, 'ChainlinkRequested') - }) - }) - - describe('#chainlinkRequestTo(Request)', () => { - it('emits an event from the contract showing the run ID', async () => { - const tx = await cc.publicRequestRunTo( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { events } = await tx.wait() - - assert.equal(4, events?.length) - assert.equal(events?.[0].event, 'ChainlinkRequested') - }) - - it('emits an event on the target oracle contract', async () => { - const tx = await cc.publicRequestRunTo( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { logs } = await tx.wait() - const event = logs && newoc.interface.parseLog(logs[3]) - - assert.equal(4, logs?.length) - assert.equal(event?.name, 'OracleRequest') - }) - - it('does not modify the stored oracle address', async () => { - await cc.publicRequestRunTo( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - - const actualOracleAddress = await cc.publicOracleAddress() - assert.equal(oc.address, actualOracleAddress) - }) - }) - - describe('#cancelChainlinkRequest', () => { - let requestId: string - // a concrete chainlink attached to an empty oracle - let ecc: Contract - - beforeEach(async () => { - const emptyOracle = await emptyOracleFactory - .connect(roles.defaultAccount) - .deploy() - ecc = await concreteChainlinkClientFactory - .connect(roles.defaultAccount) - .deploy(link.address, emptyOracle.address) - - const tx = await ecc.publicRequest( - specId, - ecc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { events } = await tx.wait() - requestId = (events?.[0]?.args as any).id - }) - - it('emits an event from the contract showing the run was cancelled', async () => { - const tx = await ecc.publicCancelRequest( - requestId, - 0, - ethers.utils.hexZeroPad('0x', 4), - 0, - ) - const { events } = await tx.wait() - - assert.equal(1, events?.length) - assert.equal(events?.[0].event, 'ChainlinkCancelled') - assert.equal(requestId, (events?.[0].args as any).id) - }) - - it('throws if given a bogus event ID', async () => { - await evmRevert( - ecc.publicCancelRequest( - ethers.utils.formatBytes32String('bogusId'), - 0, - ethers.utils.hexZeroPad('0x', 4), - 0, - ), - ) - }) - }) - - describe('#recordChainlinkFulfillment(modifier)', () => { - let request: RunRequest - - beforeEach(async () => { - const tx = await cc.publicRequest( - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { logs } = await tx.wait() - - request = decodeRunRequest(logs?.[3]) - }) - - it('emits an event marking the request fulfilled', async () => { - const tx = await oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - const { logs } = await tx.wait() - - const event = logs && cc.interface.parseLog(logs[0]) - - assert.equal(1, logs?.length) - assert.equal(event?.name, 'ChainlinkFulfilled') - assert.equal(request.requestId, event?.args.id) - }) - - it('should only allow one fulfillment per id', async () => { - await oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - - await evmRevert( - oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Must have a valid requestId', - ) - }) - - it('should only allow the oracle to fulfill the request', async () => { - await evmRevert( - oc - .connect(roles.stranger) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Not an authorized node to fulfill requests', - ) - }) - }) - - describe('#fulfillChainlinkRequest(function)', () => { - let request: RunRequest - - beforeEach(async () => { - const tx = await cc.publicRequest( - specId, - cc.address, - ethers.utils.toUtf8Bytes( - 'publicFulfillChainlinkRequest(bytes32,bytes32)', - ), - 0, - ) - const { logs } = await tx.wait() - - request = decodeRunRequest(logs?.[3]) - }) - - it('emits an event marking the request fulfilled', async () => { - const tx = await oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - - const { logs } = await tx.wait() - const event = logs && cc.interface.parseLog(logs[0]) - - assert.equal(1, logs?.length) - assert.equal(event?.name, 'ChainlinkFulfilled') - assert.equal(request.requestId, event?.args?.id) - }) - - it('should only allow one fulfillment per id', async () => { - await oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - - await evmRevert( - oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Must have a valid requestId', - ) - }) - - it('should only allow the oracle to fulfill the request', async () => { - await evmRevert( - oc - .connect(roles.stranger) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Not an authorized node to fulfill requests', - ) - }) - }) - - describe('#chainlinkToken', () => { - it('returns the Link Token address', async () => { - const addr = await cc.publicChainlinkToken() - assert.equal(addr, link.address) - }) - }) - - describe('#addExternalRequest', () => { - let mock: Contract - let request: RunRequest - - beforeEach(async () => { - mock = await concreteChainlinkClientFactory - .connect(roles.defaultAccount) - .deploy(link.address, oc.address) - - const tx = await cc.publicRequest( - specId, - mock.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const receipt = await tx.wait() - - request = decodeRunRequest(receipt.logs?.[3]) - await mock.publicAddExternalRequest(oc.address, request.requestId) - }) - - it('allows the external request to be fulfilled', async () => { - await oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - }) - - it('does not allow the same requestId to be used', async () => { - await evmRevert( - cc.publicAddExternalRequest(newoc.address, request.requestId), - ) - }) - }) -}) diff --git a/contracts/test/v0.6/CheckedMath.test.ts b/contracts/test/v0.6/CheckedMath.test.ts deleted file mode 100644 index 14520d9d9b9..00000000000 --- a/contracts/test/v0.6/CheckedMath.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: MIT -// Adapted from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/c9630526e24ba53d9647787588a19ffaa3dd65e1/test/math/SignedSafeMath.test.js - -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { BigNumber, constants, Contract, ContractFactory } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { bigNumEquals } from '../test-helpers/matchers' - -let mathFactory: ContractFactory -let personas: Personas - -before(async () => { - personas = (await getUsers()).personas - mathFactory = await ethers.getContractFactory( - 'src/v0.6/tests/CheckedMathTestHelper.sol:CheckedMathTestHelper', - personas.Default, - ) -}) - -const int256Max = constants.MaxInt256 -const int256Min = constants.MinInt256 - -describe('CheckedMath', () => { - let math: Contract - - beforeEach(async () => { - math = await mathFactory.connect(personas.Default).deploy() - }) - - describe('#add', () => { - const a = BigNumber.from('1234') - const b = BigNumber.from('5678') - - it('is commutative', async () => { - const c1 = await math.add(a, b) - const c2 = await math.add(b, a) - - bigNumEquals(c1.result, c2.result) - assert.isTrue(c1.ok) - assert.isTrue(c2.ok) - }) - - it('is commutative with big numbers', async () => { - const c1 = await math.add(int256Max, int256Min) - const c2 = await math.add(int256Min, int256Max) - - bigNumEquals(c1.result, c2.result) - assert.isTrue(c1.ok) - assert.isTrue(c2.ok) - }) - - it('returns false when overflowing', async () => { - const c1 = await math.add(int256Max, 1) - const c2 = await math.add(1, int256Max) - - bigNumEquals(0, c1.result) - bigNumEquals(0, c2.result) - assert.isFalse(c1.ok) - assert.isFalse(c2.ok) - }) - - it('returns false when underflowing', async () => { - const c1 = await math.add(int256Min, -1) - const c2 = await math.add(-1, int256Min) - - bigNumEquals(0, c1.result) - bigNumEquals(0, c2.result) - assert.isFalse(c1.ok) - assert.isFalse(c2.ok) - }) - }) - - describe('#sub', () => { - const a = BigNumber.from('1234') - const b = BigNumber.from('5678') - - it('subtracts correctly if it does not overflow and the result is negative', async () => { - const c = await math.sub(a, b) - const expected = a.sub(b) - - bigNumEquals(expected, c.result) - assert.isTrue(c.ok) - }) - - it('subtracts correctly if it does not overflow and the result is positive', async () => { - const c = await math.sub(b, a) - const expected = b.sub(a) - - bigNumEquals(expected, c.result) - assert.isTrue(c.ok) - }) - - it('returns false on overflow', async () => { - const c = await math.sub(int256Max, -1) - - bigNumEquals(0, c.result) - assert.isFalse(c.ok) - }) - - it('returns false on underflow', async () => { - const c = await math.sub(int256Min, 1) - - bigNumEquals(0, c.result) - assert.isFalse(c.ok) - }) - }) - - describe('#mul', () => { - const a = BigNumber.from('5678') - const b = BigNumber.from('-1234') - - it('is commutative', async () => { - const c1 = await math.mul(a, b) - const c2 = await math.mul(b, a) - - bigNumEquals(c1.result, c2.result) - assert.isTrue(c1.ok) - assert.isTrue(c2.ok) - }) - - it('multiplies by 0 correctly', async () => { - const c = await math.mul(a, 0) - - bigNumEquals(0, c.result) - assert.isTrue(c.ok) - }) - - it('returns false on multiplication overflow', async () => { - const c = await math.mul(int256Max, 2) - - bigNumEquals(0, c.result) - assert.isFalse(c.ok) - }) - - it('returns false when the integer minimum is negated', async () => { - const c = await math.mul(int256Min, -1) - - bigNumEquals(0, c.result) - assert.isFalse(c.ok) - }) - }) - - describe('#div', () => { - const a = BigNumber.from('5678') - const b = BigNumber.from('-5678') - - it('divides correctly', async () => { - const c = await math.div(a, b) - - bigNumEquals(a.div(b), c.result) - assert.isTrue(c.ok) - }) - - it('divides a 0 numerator correctly', async () => { - const c = await math.div(0, a) - - bigNumEquals(0, c.result) - assert.isTrue(c.ok) - }) - - it('returns complete number result on non-even division', async () => { - const c = await math.div(7000, 5678) - - bigNumEquals(1, c.result) - assert.isTrue(c.ok) - }) - - it('reverts when 0 is the denominator', async () => { - const c = await math.div(a, 0) - - bigNumEquals(0, c.result) - assert.isFalse(c.ok) - }) - - it('reverts on underflow with a negative denominator', async () => { - const c = await math.div(int256Min, -1) - - bigNumEquals(0, c.result) - assert.isFalse(c.ok) - }) - }) -}) diff --git a/contracts/test/v0.6/DeviationFlaggingValidator.test.ts b/contracts/test/v0.6/DeviationFlaggingValidator.test.ts deleted file mode 100644 index f79a8c7aa47..00000000000 --- a/contracts/test/v0.6/DeviationFlaggingValidator.test.ts +++ /dev/null @@ -1,296 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { BigNumber, Contract, ContractFactory } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { bigNumEquals } from '../test-helpers/matchers' - -let personas: Personas -let validatorFactory: ContractFactory -let flagsFactory: ContractFactory -let acFactory: ContractFactory - -before(async () => { - personas = (await getUsers()).personas - validatorFactory = await ethers.getContractFactory( - 'src/v0.6/DeviationFlaggingValidator.sol:DeviationFlaggingValidator', - personas.Carol, - ) - flagsFactory = await ethers.getContractFactory( - 'src/v0.6/Flags.sol:Flags', - personas.Carol, - ) - acFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleWriteAccessController.sol:SimpleWriteAccessController', - personas.Carol, - ) -}) - -describe('DeviationFlaggingValidator', () => { - let validator: Contract - let flags: Contract - let ac: Contract - const flaggingThreshold = 10000 // 10% - const previousRoundId = 2 - const previousValue = 1000000 - const currentRoundId = 3 - const currentValue = 1000000 - - beforeEach(async () => { - ac = await acFactory.connect(personas.Carol).deploy() - flags = await flagsFactory.connect(personas.Carol).deploy(ac.address) - validator = await validatorFactory - .connect(personas.Carol) - .deploy(flags.address, flaggingThreshold) - await ac.connect(personas.Carol).addAccess(validator.address) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(validator, [ - 'THRESHOLD_MULTIPLIER', - 'flaggingThreshold', - 'flags', - 'isValid', - 'setFlagsAddress', - 'setFlaggingThreshold', - 'validate', - // Owned methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#constructor', () => { - it('sets the arguments passed in', async () => { - assert.equal(flags.address, await validator.flags()) - bigNumEquals(flaggingThreshold, await validator.flaggingThreshold()) - }) - }) - - describe('#validate', () => { - describe('when the deviation is greater than the threshold', () => { - const currentValue = 1100010 - - it('does raises a flag for the calling address', async () => { - await expect( - validator - .connect(personas.Nelly) - .validate( - previousRoundId, - previousValue, - currentRoundId, - currentValue, - ), - ) - .to.emit(flags, 'FlagRaised') - .withArgs(await personas.Nelly.getAddress()) - }) - - it('uses less than the gas allotted by the aggregator', async () => { - const tx = await validator - .connect(personas.Nelly) - .validate( - previousRoundId, - previousValue, - currentRoundId, - currentValue, - ) - const receipt = await tx.wait() - assert(receipt) - if (receipt && receipt.gasUsed) { - assert.isAbove(receipt.gasUsed.toNumber(), 60000) - } - }) - }) - - describe('when the deviation is less than or equal to the threshold', () => { - const currentValue = 1100009 - - it('does raises a flag for the calling address', async () => { - await expect( - validator - .connect(personas.Nelly) - .validate( - previousRoundId, - previousValue, - currentRoundId, - currentValue, - ), - ).to.not.emit(flags, 'FlagRaised') - }) - - it('uses less than the gas allotted by the aggregator', async () => { - const tx = await validator - .connect(personas.Nelly) - .validate( - previousRoundId, - previousValue, - currentRoundId, - currentValue, - ) - const receipt = await tx.wait() - assert(receipt) - if (receipt && receipt.gasUsed) { - assert.isAbove(receipt.gasUsed.toNumber(), 24000) - } - }) - }) - - describe('when called with a previous value of zero', () => { - const previousValue = 0 - - it('does not raise any flags', async () => { - const tx = await validator - .connect(personas.Nelly) - .validate( - previousRoundId, - previousValue, - currentRoundId, - currentValue, - ) - const receipt = await tx.wait() - assert.equal(0, receipt.events?.length) - }) - }) - }) - - describe('#isValid', () => { - const previousValue = 1000000 - - describe('with a validation larger than the deviation', () => { - const currentValue = 1100010 - it('is not valid', async () => { - assert.isFalse( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - - describe('with a validation smaller than the deviation', () => { - const currentValue = 1100009 - it('is valid', async () => { - assert.isTrue( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - - describe('with positive previous and negative current', () => { - const previousValue = 1000000 - const currentValue = -900000 - it('correctly detects the difference', async () => { - assert.isFalse( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - - describe('with negative previous and positive current', () => { - const previousValue = -900000 - const currentValue = 1000000 - it('correctly detects the difference', async () => { - assert.isFalse( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - - describe('when the difference overflows', () => { - const previousValue = BigNumber.from(2).pow(255).sub(1) - const currentValue = BigNumber.from(-1) - - it('does not revert and returns false', async () => { - assert.isFalse( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - - describe('when the rounding overflows', () => { - const previousValue = BigNumber.from(2).pow(255).div(10000) - const currentValue = BigNumber.from(1) - - it('does not revert and returns false', async () => { - assert.isFalse( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - - describe('when the division overflows', () => { - const previousValue = BigNumber.from(2).pow(255).sub(1) - const currentValue = BigNumber.from(-1) - - it('does not revert and returns false', async () => { - assert.isFalse( - await validator.isValid(0, previousValue, 1, currentValue), - ) - }) - }) - }) - - describe('#setFlaggingThreshold', () => { - const newThreshold = 777 - - it('changes the flagging thresold', async () => { - assert.equal(flaggingThreshold, await validator.flaggingThreshold()) - - await validator.connect(personas.Carol).setFlaggingThreshold(newThreshold) - - assert.equal(newThreshold, await validator.flaggingThreshold()) - }) - - it('emits a log event only when actually changed', async () => { - await expect( - validator.connect(personas.Carol).setFlaggingThreshold(newThreshold), - ) - .to.emit(validator, 'FlaggingThresholdUpdated') - .withArgs(flaggingThreshold, newThreshold) - - await expect( - validator.connect(personas.Carol).setFlaggingThreshold(newThreshold), - ).to.not.emit(validator, 'FlaggingThresholdUpdated') - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - validator.connect(personas.Neil).setFlaggingThreshold(newThreshold), - ).to.be.revertedWith('Only callable by owner') - }) - }) - }) - - describe('#setFlagsAddress', () => { - const newFlagsAddress = '0x0123456789012345678901234567890123456789' - - it('changes the flags address', async () => { - assert.equal(flags.address, await validator.flags()) - - await validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress) - - assert.equal(newFlagsAddress, await validator.flags()) - }) - - it('emits a log event only when actually changed', async () => { - await expect( - validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress), - ) - .to.emit(validator, 'FlagsAddressUpdated') - .withArgs(flags.address, newFlagsAddress) - - await expect( - validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress), - ).to.not.emit(validator, 'FlagsAddressUpdated') - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - validator.connect(personas.Neil).setFlagsAddress(newFlagsAddress), - ).to.be.revertedWith('Only callable by owner') - }) - }) - }) -}) diff --git a/contracts/test/v0.6/Flags.test.ts b/contracts/test/v0.6/Flags.test.ts deleted file mode 100644 index 8f589184299..00000000000 --- a/contracts/test/v0.6/Flags.test.ts +++ /dev/null @@ -1,405 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { Contract, ContractFactory } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' - -let personas: Personas - -let controllerFactory: ContractFactory -let flagsFactory: ContractFactory -let consumerFactory: ContractFactory - -let controller: Contract -let flags: Contract -let consumer: Contract - -before(async () => { - personas = (await getUsers()).personas - controllerFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleWriteAccessController.sol:SimpleWriteAccessController', - personas.Nelly, - ) - consumerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/FlagsTestHelper.sol:FlagsTestHelper', - personas.Nelly, - ) - flagsFactory = await ethers.getContractFactory( - 'src/v0.6/Flags.sol:Flags', - personas.Nelly, - ) -}) - -describe('Flags', () => { - beforeEach(async () => { - controller = await controllerFactory.deploy() - flags = await flagsFactory.deploy(controller.address) - await flags.disableAccessCheck() - consumer = await consumerFactory.deploy(flags.address) - }) - - it('has a limited public interface [ @skip-coverage ]', async () => { - publicAbi(flags, [ - 'getFlag', - 'getFlags', - 'lowerFlags', - 'raiseFlag', - 'raiseFlags', - 'raisingAccessController', - 'setRaisingAccessController', - // Ownable methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - // AccessControl methods: - 'addAccess', - 'disableAccessCheck', - 'enableAccessCheck', - 'removeAccess', - 'checkEnabled', - 'hasAccess', - ]) - }) - - describe('#raiseFlag', () => { - describe('when called by the owner', () => { - it('updates the warning flag', async () => { - assert.equal(false, await flags.getFlag(consumer.address)) - - await flags.connect(personas.Nelly).raiseFlag(consumer.address) - - assert.equal(true, await flags.getFlag(consumer.address)) - }) - - it('emits an event log', async () => { - await expect(flags.connect(personas.Nelly).raiseFlag(consumer.address)) - .to.emit(flags, 'FlagRaised') - .withArgs(consumer.address) - }) - - describe('if a flag has already been raised', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).raiseFlag(consumer.address) - }) - - it('emits an event log', async () => { - const tx = await flags - .connect(personas.Nelly) - .raiseFlag(consumer.address) - const receipt = await tx.wait() - assert.equal(0, receipt.events?.length) - }) - }) - }) - - describe('when called by an enabled setter', () => { - beforeEach(async () => { - await controller - .connect(personas.Nelly) - .addAccess(await personas.Neil.getAddress()) - }) - - it('sets the flags', async () => { - await flags.connect(personas.Neil).raiseFlag(consumer.address), - assert.equal(true, await flags.getFlag(consumer.address)) - }) - }) - - describe('when called by a non-enabled setter', () => { - it('reverts', async () => { - await expect( - flags.connect(personas.Neil).raiseFlag(consumer.address), - ).to.be.revertedWith('Not allowed to raise flags') - }) - }) - - describe('when called when there is no raisingAccessController', () => { - beforeEach(async () => { - await expect( - flags - .connect(personas.Nelly) - .setRaisingAccessController( - '0x0000000000000000000000000000000000000000', - ), - ).to.emit(flags, 'RaisingAccessControllerUpdated') - assert.equal( - '0x0000000000000000000000000000000000000000', - await flags.raisingAccessController(), - ) - }) - - it('succeeds for the owner', async () => { - await flags.connect(personas.Nelly).raiseFlag(consumer.address) - assert.equal(true, await flags.getFlag(consumer.address)) - }) - - it('reverts for non-owner', async () => { - await expect(flags.connect(personas.Neil).raiseFlag(consumer.address)) - .to.be.reverted - }) - }) - }) - - describe('#raiseFlags', () => { - describe('when called by the owner', () => { - it('updates the warning flag', async () => { - assert.equal(false, await flags.getFlag(consumer.address)) - - await flags.connect(personas.Nelly).raiseFlags([consumer.address]) - - assert.equal(true, await flags.getFlag(consumer.address)) - }) - - it('emits an event log', async () => { - await expect( - flags.connect(personas.Nelly).raiseFlags([consumer.address]), - ) - .to.emit(flags, 'FlagRaised') - .withArgs(consumer.address) - }) - - describe('if a flag has already been raised', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).raiseFlags([consumer.address]) - }) - - it('emits an event log', async () => { - const tx = await flags - .connect(personas.Nelly) - .raiseFlags([consumer.address]) - const receipt = await tx.wait() - assert.equal(0, receipt.events?.length) - }) - }) - }) - - describe('when called by an enabled setter', () => { - beforeEach(async () => { - await controller - .connect(personas.Nelly) - .addAccess(await personas.Neil.getAddress()) - }) - - it('sets the flags', async () => { - await flags.connect(personas.Neil).raiseFlags([consumer.address]), - assert.equal(true, await flags.getFlag(consumer.address)) - }) - }) - - describe('when called by a non-enabled setter', () => { - it('reverts', async () => { - await expect( - flags.connect(personas.Neil).raiseFlags([consumer.address]), - ).to.be.revertedWith('Not allowed to raise flags') - }) - }) - - describe('when called when there is no raisingAccessController', () => { - beforeEach(async () => { - await expect( - flags - .connect(personas.Nelly) - .setRaisingAccessController( - '0x0000000000000000000000000000000000000000', - ), - ).to.emit(flags, 'RaisingAccessControllerUpdated') - - assert.equal( - '0x0000000000000000000000000000000000000000', - await flags.raisingAccessController(), - ) - }) - - it('succeeds for the owner', async () => { - await flags.connect(personas.Nelly).raiseFlags([consumer.address]) - assert.equal(true, await flags.getFlag(consumer.address)) - }) - - it('reverts for non-owners', async () => { - await expect( - flags.connect(personas.Neil).raiseFlags([consumer.address]), - ).to.be.reverted - }) - }) - }) - - describe('#lowerFlags', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).raiseFlags([consumer.address]) - }) - - describe('when called by the owner', () => { - it('updates the warning flag', async () => { - assert.equal(true, await flags.getFlag(consumer.address)) - - await flags.connect(personas.Nelly).lowerFlags([consumer.address]) - - assert.equal(false, await flags.getFlag(consumer.address)) - }) - - it('emits an event log', async () => { - await expect( - flags.connect(personas.Nelly).lowerFlags([consumer.address]), - ) - .to.emit(flags, 'FlagLowered') - .withArgs(consumer.address) - }) - - describe('if a flag has already been raised', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).lowerFlags([consumer.address]) - }) - - it('emits an event log', async () => { - const tx = await flags - .connect(personas.Nelly) - .lowerFlags([consumer.address]) - const receipt = await tx.wait() - assert.equal(0, receipt.events?.length) - }) - }) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - flags.connect(personas.Neil).lowerFlags([consumer.address]), - ).to.be.revertedWith('Only callable by owner') - }) - }) - }) - - describe('#getFlag', () => { - describe('if the access control is turned on', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).enableAccessCheck() - }) - - it('reverts', async () => { - await expect(consumer.getFlag(consumer.address)).to.be.revertedWith( - 'No access', - ) - }) - - describe('if access is granted to the address', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).addAccess(consumer.address) - }) - - it('does not revert', async () => { - await consumer.getFlag(consumer.address) - }) - }) - }) - - describe('if the access control is turned off', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).disableAccessCheck() - }) - - it('does not revert', async () => { - await consumer.getFlag(consumer.address) - }) - - describe('if access is granted to the address', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).addAccess(consumer.address) - }) - - it('does not revert', async () => { - await consumer.getFlag(consumer.address) - }) - }) - }) - }) - - describe('#getFlags', () => { - beforeEach(async () => { - await flags.connect(personas.Nelly).disableAccessCheck() - await flags - .connect(personas.Nelly) - .raiseFlags([ - await personas.Neil.getAddress(), - await personas.Norbert.getAddress(), - ]) - }) - - it('respects the access controls of #getFlag', async () => { - await flags.connect(personas.Nelly).enableAccessCheck() - - await expect(consumer.getFlag(consumer.address)).to.be.revertedWith( - 'No access', - ) - - await flags.connect(personas.Nelly).addAccess(consumer.address) - - await consumer.getFlag(consumer.address) - }) - - it('returns the flags in the order they are requested', async () => { - const response = await consumer.getFlags([ - await personas.Nelly.getAddress(), - await personas.Neil.getAddress(), - await personas.Ned.getAddress(), - await personas.Norbert.getAddress(), - ]) - - assert.deepEqual([false, true, false, true], response) - }) - }) - - describe('#setRaisingAccessController', () => { - let controller2: Contract - - beforeEach(async () => { - controller2 = await controllerFactory.connect(personas.Nelly).deploy() - await controller2.connect(personas.Nelly).enableAccessCheck() - }) - - it('updates access control rules', async () => { - const neilAddress = await personas.Neil.getAddress() - await controller.connect(personas.Nelly).addAccess(neilAddress) - await flags.connect(personas.Neil).raiseFlags([consumer.address]) // doesn't raise - - await flags - .connect(personas.Nelly) - .setRaisingAccessController(controller2.address) - - await expect( - flags.connect(personas.Neil).raiseFlags([consumer.address]), - ).to.be.revertedWith('Not allowed to raise flags') - }) - - it('emits a log announcing the change', async () => { - await expect( - flags - .connect(personas.Nelly) - .setRaisingAccessController(controller2.address), - ) - .to.emit(flags, 'RaisingAccessControllerUpdated') - .withArgs(controller.address, controller2.address) - }) - - it('does not emit a log when there is no change', async () => { - await flags - .connect(personas.Nelly) - .setRaisingAccessController(controller2.address) - - await expect( - flags - .connect(personas.Nelly) - .setRaisingAccessController(controller2.address), - ).to.not.emit(flags, 'RaisingAccessControllerUpdated') - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - flags - .connect(personas.Neil) - .setRaisingAccessController(controller2.address), - ).to.be.revertedWith('Only callable by owner') - }) - }) - }) -}) diff --git a/contracts/test/v0.6/FluxAggregator.test.ts b/contracts/test/v0.6/FluxAggregator.test.ts deleted file mode 100644 index 5a268ceebe9..00000000000 --- a/contracts/test/v0.6/FluxAggregator.test.ts +++ /dev/null @@ -1,3252 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { - Signer, - Contract, - ContractFactory, - BigNumber, - BigNumberish, - ContractTransaction, - constants, -} from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { bigNumEquals, evmRevert } from '../test-helpers/matchers' -import { - publicAbi, - toWei, - increaseTimeBy, - mineBlock, - evmWordToAddress, -} from '../test-helpers/helpers' -import { randomBytes } from '@ethersproject/random' -import { fail } from 'assert' - -let personas: Personas -let linkTokenFactory: ContractFactory -let fluxAggregatorFactory: ContractFactory -let validatorMockFactory: ContractFactory -let testHelperFactory: ContractFactory -let validatorFactory: ContractFactory -let flagsFactory: ContractFactory -let acFactory: ContractFactory -let gasGuzzlerFactory: ContractFactory -let emptyAddress: string - -before(async () => { - personas = (await getUsers()).personas - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - ) - fluxAggregatorFactory = await ethers.getContractFactory( - 'src/v0.6/FluxAggregator.sol:FluxAggregator', - ) - validatorMockFactory = await ethers.getContractFactory( - 'src/v0.6/tests/AggregatorValidatorMock.sol:AggregatorValidatorMock', - ) - testHelperFactory = await ethers.getContractFactory( - 'src/v0.6/tests/FluxAggregatorTestHelper.sol:FluxAggregatorTestHelper', - ) - validatorFactory = await ethers.getContractFactory( - 'src/v0.6/DeviationFlaggingValidator.sol:DeviationFlaggingValidator', - ) - flagsFactory = await ethers.getContractFactory('src/v0.6/Flags.sol:Flags') - acFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleWriteAccessController.sol:SimpleWriteAccessController', - ) - gasGuzzlerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/GasGuzzler.sol:GasGuzzler', - ) - emptyAddress = constants.AddressZero -}) - -describe('FluxAggregator', () => { - const paymentAmount = toWei('3') - const deposit = toWei('100') - const answer = 100 - const minAns = 1 - const maxAns = 1 - const rrDelay = 0 - const timeout = 1800 - const decimals = 18 - const description = 'LINK/USD' - const reserveRounds = 2 - const minSubmissionValue = BigNumber.from('1') - const maxSubmissionValue = BigNumber.from('100000000000000000000') - - let aggregator: Contract - let link: Contract - let testHelper: Contract - let validator: Contract - let gasGuzzler: Contract - let nextRound: number - let oracles: Signer[] - - async function updateFutureRounds( - aggregator: Contract, - overrides: { - minAnswers?: BigNumberish - maxAnswers?: BigNumberish - payment?: BigNumberish - restartDelay?: BigNumberish - timeout?: BigNumberish - } = {}, - ) { - overrides = overrides || {} - const round = { - payment: overrides.payment || paymentAmount, - minAnswers: overrides.minAnswers || minAns, - maxAnswers: overrides.maxAnswers || maxAns, - restartDelay: overrides.restartDelay || rrDelay, - timeout: overrides.timeout || timeout, - } - - return aggregator.updateFutureRounds( - round.payment, - round.minAnswers, - round.maxAnswers, - round.restartDelay, - round.timeout, - ) - } - - async function addOracles( - aggregator: Contract, - oraclesAndAdmin: Signer[], - minAnswers: number, - maxAnswers: number, - restartDelay: number, - ): Promise { - return aggregator.connect(personas.Carol).changeOracles( - [], - oraclesAndAdmin.map(async (oracle) => await oracle.getAddress()), - oraclesAndAdmin.map(async (admin) => await admin.getAddress()), - minAnswers, - maxAnswers, - restartDelay, - ) - } - - async function advanceRound( - aggregator: Contract, - submitters: Signer[], - currentSubmission: number = answer, - ): Promise { - for (const submitter of submitters) { - await aggregator.connect(submitter).submit(nextRound, currentSubmission) - } - nextRound++ - return nextRound - } - - const ShouldBeSet = 'expects it to be different' - const ShouldNotBeSet = 'expects it to equal' - let startingState: any - - async function checkOracleRoundState( - state: any, - want: { - eligibleToSubmit: boolean - roundId: BigNumberish - latestSubmission: BigNumberish - startedAt: string - timeout: BigNumberish - availableFunds: BigNumberish - oracleCount: BigNumberish - paymentAmount: BigNumberish - }, - ) { - assert.equal( - want.eligibleToSubmit, - state._eligibleToSubmit, - 'round state: unexecpted eligibility', - ) - bigNumEquals( - want.roundId, - state._roundId, - 'round state: unexpected Round ID', - ) - bigNumEquals( - want.latestSubmission, - state._latestSubmission, - 'round state: unexpected latest submission', - ) - if (want.startedAt === ShouldBeSet) { - assert.isAbove( - state._startedAt.toNumber(), - startingState._startedAt.toNumber(), - 'round state: expected the started at to be the same as previous', - ) - } else { - bigNumEquals( - 0, - state._startedAt, - 'round state: expected the started at not to be updated', - ) - } - bigNumEquals( - want.timeout, - state._timeout.toNumber(), - 'round state: unexepcted timeout', - ) - bigNumEquals( - want.availableFunds, - state._availableFunds, - 'round state: unexepected funds', - ) - bigNumEquals( - want.oracleCount, - state._oracleCount, - 'round state: unexpected oracle count', - ) - bigNumEquals( - want.paymentAmount, - state._paymentAmount, - 'round state: unexpected paymentamount', - ) - } - - beforeEach(async () => { - link = await linkTokenFactory.connect(personas.Default).deploy() - aggregator = await fluxAggregatorFactory - .connect(personas.Carol) - .deploy( - link.address, - paymentAmount, - timeout, - emptyAddress, - minSubmissionValue, - maxSubmissionValue, - decimals, - ethers.utils.formatBytes32String(description), - ) - await link.transfer(aggregator.address, deposit) - await aggregator.updateAvailableFunds() - bigNumEquals(deposit, await link.balanceOf(aggregator.address)) - nextRound = 1 - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(aggregator, [ - 'acceptAdmin', - 'allocatedFunds', - 'availableFunds', - 'changeOracles', - 'decimals', - 'description', - 'getAdmin', - 'getAnswer', - 'getOracles', - 'getRoundData', - 'getTimestamp', - 'latestAnswer', - 'latestRound', - 'latestRoundData', - 'latestTimestamp', - 'linkToken', - 'maxSubmissionCount', - 'maxSubmissionValue', - 'minSubmissionCount', - 'minSubmissionValue', - 'onTokenTransfer', - 'oracleCount', - 'oracleRoundState', - 'paymentAmount', - 'requestNewRound', - 'restartDelay', - 'setRequesterPermissions', - 'setValidator', - 'submit', - 'timeout', - 'transferAdmin', - 'updateAvailableFunds', - 'updateFutureRounds', - 'withdrawFunds', - 'withdrawPayment', - 'withdrawablePayment', - 'validator', - 'version', - // Owned methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#constructor', () => { - it('sets the paymentAmount', async () => { - bigNumEquals( - BigNumber.from(paymentAmount), - await aggregator.paymentAmount(), - ) - }) - - it('sets the timeout', async () => { - bigNumEquals(BigNumber.from(timeout), await aggregator.timeout()) - }) - - it('sets the decimals', async () => { - bigNumEquals(BigNumber.from(decimals), await aggregator.decimals()) - }) - - it('sets the description', async () => { - assert.equal( - ethers.utils.formatBytes32String(description), - await aggregator.description(), - ) - }) - - it('sets the version to 3', async () => { - bigNumEquals(3, await aggregator.version()) - }) - - it('sets the validator', async () => { - assert.equal(emptyAddress, await aggregator.validator()) - }) - }) - - describe('#submit', () => { - let minMax - - beforeEach(async () => { - oracles = [personas.Neil, personas.Ned, personas.Nelly] - minMax = oracles.length - await addOracles(aggregator, oracles, minMax, minMax, rrDelay) - }) - - it('updates the allocated and available funds counters', async () => { - bigNumEquals(0, await aggregator.allocatedFunds()) - - const tx = await aggregator - .connect(personas.Neil) - .submit(nextRound, answer) - const receipt = await tx.wait() - - bigNumEquals(paymentAmount, await aggregator.allocatedFunds()) - const expectedAvailable = deposit.sub(paymentAmount) - bigNumEquals(expectedAvailable, await aggregator.availableFunds()) - const logged = BigNumber.from( - receipt.logs?.[2].topics[1] ?? BigNumber.from(-1), - ) - bigNumEquals(expectedAvailable, logged) - }) - - it('emits a log event announcing submission details', async () => { - await expect(aggregator.connect(personas.Nelly).submit(nextRound, answer)) - .to.emit(aggregator, 'SubmissionReceived') - .withArgs(answer, nextRound, await personas.Nelly.getAddress()) - }) - - describe('when the minimum oracles have not reported', () => { - it('pays the oracles that have reported', async () => { - bigNumEquals( - 0, - await aggregator - .connect(personas.Neil) - .withdrawablePayment(await personas.Neil.getAddress()), - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - bigNumEquals( - paymentAmount, - await aggregator - .connect(personas.Neil) - .withdrawablePayment(await personas.Neil.getAddress()), - ) - bigNumEquals( - 0, - await aggregator - .connect(personas.Ned) - .withdrawablePayment(await personas.Ned.getAddress()), - ) - bigNumEquals( - 0, - await aggregator - .connect(personas.Nelly) - .withdrawablePayment(await personas.Nelly.getAddress()), - ) - }) - - it('does not update the answer', async () => { - bigNumEquals(ethers.constants.Zero, await aggregator.latestAnswer()) - - // Not updated because of changes by the owner setting minSubmissionCount to 3 - await aggregator.connect(personas.Ned).submit(nextRound, answer) - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - bigNumEquals(ethers.constants.Zero, await aggregator.latestAnswer()) - }) - }) - - describe('when an oracle prematurely bumps the round', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { minAnswers: 2, maxAnswers: 3 }) - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Neil).submit(nextRound + 1, answer), - 'previous round not supersedable', - ) - }) - }) - - describe('when the minimum number of oracles have reported', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { minAnswers: 2, maxAnswers: 3 }) - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - - it('updates the answer with the median', async () => { - bigNumEquals(0, await aggregator.latestAnswer()) - - await aggregator.connect(personas.Ned).submit(nextRound, 99) - bigNumEquals(99, await aggregator.latestAnswer()) // ((100+99) / 2).to_i - - await aggregator.connect(personas.Nelly).submit(nextRound, 101) - - bigNumEquals(100, await aggregator.latestAnswer()) - }) - - it('updates the updated timestamp', async () => { - const originalTimestamp = await aggregator.latestTimestamp() - assert.isAbove(originalTimestamp.toNumber(), 0) - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - const currentTimestamp = await aggregator.latestTimestamp() - assert.isAbove( - currentTimestamp.toNumber(), - originalTimestamp.toNumber(), - ) - }) - - it('announces the new answer with a log event', async () => { - const tx = await aggregator - .connect(personas.Nelly) - .submit(nextRound, answer) - const receipt = await tx.wait() - - const newAnswer = BigNumber.from( - receipt.logs?.[0].topics[1] ?? ethers.constants.Zero, - ) - - assert.equal(answer, newAnswer.toNumber()) - }) - - it('does not set the timedout flag', async () => { - evmRevert(aggregator.getRoundData(nextRound), 'No data present') - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - const round = await aggregator.getRoundData(nextRound) - assert.equal(nextRound, round.answeredInRound.toNumber()) - }) - - it('updates the round details', async () => { - evmRevert(aggregator.latestRoundData(), 'No data present') - - increaseTimeBy(15, ethers.provider) - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - const roundAfter = await aggregator.getRoundData(nextRound) - bigNumEquals(nextRound, roundAfter.roundId) - bigNumEquals(answer, roundAfter.answer) - assert.isFalse(roundAfter.startedAt.isZero()) - bigNumEquals( - await aggregator.getTimestamp(nextRound), - roundAfter.updatedAt, - ) - bigNumEquals(nextRound, roundAfter.answeredInRound) - - assert.isBelow( - roundAfter.startedAt.toNumber(), - roundAfter.updatedAt.toNumber(), - ) - - const roundAfterLatest = await aggregator.latestRoundData() - bigNumEquals(roundAfter.roundId, roundAfterLatest.roundId) - bigNumEquals(roundAfter.answer, roundAfterLatest.answer) - bigNumEquals(roundAfter.startedAt, roundAfterLatest.startedAt) - bigNumEquals(roundAfter.updatedAt, roundAfterLatest.updatedAt) - bigNumEquals( - roundAfter.answeredInRound, - roundAfterLatest.answeredInRound, - ) - }) - }) - - describe('when an oracle submits for a round twice', () => { - it('reverts', async () => { - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - await evmRevert( - aggregator.connect(personas.Neil).submit(nextRound, answer), - 'cannot report on previous rounds', - ) - }) - }) - - describe('when updated after the max answers submitted', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator) - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Ned).submit(nextRound, answer), - 'round not accepting submissions', - ) - }) - }) - - describe('when a new highest round number is passed in', () => { - it('increments the answer round', async () => { - const startingState = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - bigNumEquals(1, startingState._roundId) - - await advanceRound(aggregator, oracles) - - const updatedState = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - bigNumEquals(2, updatedState._roundId) - }) - - it('sets the startedAt time for the reporting round', async () => { - evmRevert(aggregator.getRoundData(nextRound), 'No data present') - - const tx = await aggregator - .connect(oracles[0]) - .submit(nextRound, answer) - await aggregator.connect(oracles[1]).submit(nextRound, answer) - await aggregator.connect(oracles[2]).submit(nextRound, answer) - const receipt = await tx.wait() - const block = await ethers.provider.getBlock(receipt.blockHash ?? '') - - const round = await aggregator.getRoundData(nextRound) - bigNumEquals(BigNumber.from(block.timestamp), round.startedAt) - }) - - it('announces a new round by emitting a log', async () => { - const tx = await aggregator - .connect(personas.Neil) - .submit(nextRound, answer) - const receipt = await tx.wait() - - const topics = receipt.logs?.[0].topics ?? [] - const roundNumber = BigNumber.from(topics[1]) - const startedBy = evmWordToAddress(topics[2]) - - bigNumEquals(nextRound, roundNumber.toNumber()) - bigNumEquals(startedBy, await personas.Neil.getAddress()) - }) - }) - - describe('when a round is passed in higher than expected', () => { - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Neil).submit(nextRound + 1, answer), - 'invalid round to report', - ) - }) - }) - - describe('when called by a non-oracle', () => { - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Carol).submit(nextRound, answer), - 'not enabled oracle', - ) - }) - }) - - describe('when there are not sufficient available funds', () => { - beforeEach(async () => { - await aggregator - .connect(personas.Carol) - .withdrawFunds( - await personas.Carol.getAddress(), - deposit.sub(paymentAmount.mul(oracles.length).mul(reserveRounds)), - ) - - // drain remaining funds - await advanceRound(aggregator, oracles) - await advanceRound(aggregator, oracles) - }) - - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Neil).submit(nextRound, answer), - 'SafeMath: subtraction overflow', - ) - }) - }) - - describe('when a new round opens before the previous rounds closes', () => { - beforeEach(async () => { - oracles = [personas.Nancy, personas.Norbert] - await addOracles(aggregator, oracles, 3, 4, rrDelay) - await advanceRound(aggregator, [ - personas.Nelly, - personas.Neil, - personas.Nancy, - ]) - - // start the next round - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - }) - - it('still allows the previous round to be answered', async () => { - await aggregator.connect(personas.Ned).submit(nextRound - 1, answer) - }) - - describe('once the current round is answered', () => { - beforeEach(async () => { - oracles = [personas.Neil, personas.Nancy] - for (let i = 0; i < oracles.length; i++) { - await aggregator.connect(oracles[i]).submit(nextRound, answer) - } - }) - - it('does not allow reports for the previous round', async () => { - await evmRevert( - aggregator.connect(personas.Ned).submit(nextRound - 1, answer), - 'invalid round to report', - ) - }) - }) - - describe('when the previous round has finished', () => { - beforeEach(async () => { - await aggregator - .connect(personas.Norbert) - .submit(nextRound - 1, answer) - }) - - it('does not allow reports for the previous round', async () => { - await evmRevert( - aggregator.connect(personas.Ned).submit(nextRound - 1, answer), - 'round not accepting submissions', - ) - }) - }) - }) - - describe('when price is updated mid-round', () => { - const newAmount = toWei('50') - - it('pays the same amount to all oracles per round', async () => { - await link.transfer( - aggregator.address, - newAmount.mul(oracles.length).mul(reserveRounds), - ) - await aggregator.updateAvailableFunds() - - bigNumEquals( - 0, - await aggregator - .connect(personas.Neil) - .withdrawablePayment(await personas.Neil.getAddress()), - ) - bigNumEquals( - 0, - await aggregator - .connect(personas.Nelly) - .withdrawablePayment(await personas.Nelly.getAddress()), - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - await updateFutureRounds(aggregator, { payment: newAmount }) - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - bigNumEquals( - paymentAmount, - await aggregator - .connect(personas.Neil) - .withdrawablePayment(await personas.Neil.getAddress()), - ) - bigNumEquals( - paymentAmount, - await aggregator - .connect(personas.Nelly) - .withdrawablePayment(await personas.Nelly.getAddress()), - ) - }) - }) - - describe('when delay is on', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { - minAnswers: oracles.length, - maxAnswers: oracles.length, - restartDelay: 1, - }) - }) - - it("does not revert on the oracle's first round", async () => { - // Since lastUpdatedRound defaults to zero and that's the only - // indication that an oracle hasn't responded, this test guards against - // the situation where we don't check that and no one can start a round. - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - - it('does revert before the delay', async () => { - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - nextRound++ - - await evmRevert( - aggregator.connect(personas.Neil).submit(nextRound, answer), - 'previous round not supersedable', - ) - }) - }) - - describe('when an oracle starts a round before the restart delay is over', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator.connect(personas.Carol), { - minAnswers: 1, - maxAnswers: 1, - }) - - oracles = [personas.Neil, personas.Ned, personas.Nelly] - for (let i = 0; i < oracles.length; i++) { - await aggregator.connect(oracles[i]).submit(nextRound, answer) - nextRound++ - } - - const newDelay = 2 - // Since Ned and Nelly have answered recently, and we set the delay - // to 2, only Nelly can answer as she is the only oracle that hasn't - // started the last two rounds. - await updateFutureRounds(aggregator, { - maxAnswers: oracles.length, - restartDelay: newDelay, - }) - }) - - describe('when called by an oracle who has not answered recently', () => { - it('does not revert', async () => { - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - }) - - describe('when called by an oracle who answered recently', () => { - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Ned).submit(nextRound, answer), - 'round not accepting submissions', - ) - - await evmRevert( - aggregator.connect(personas.Nelly).submit(nextRound, answer), - 'round not accepting submissions', - ) - }) - }) - }) - - describe('when the price is not updated for a round', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { - minAnswers: oracles.length, - maxAnswers: oracles.length, - restartDelay: 1, - }) - - for (const oracle of oracles) { - await aggregator.connect(oracle).submit(nextRound, answer) - } - nextRound++ - - await aggregator.connect(personas.Ned).submit(nextRound, answer) - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - await increaseTimeBy(timeout + 1, ethers.provider) - nextRound++ - }) - - it('allows a new round to be started', async () => { - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - }) - - it('sets the info for the previous round', async () => { - const previousRound = nextRound - 1 - let updated = await aggregator.getTimestamp(previousRound) - let ans = await aggregator.getAnswer(previousRound) - assert.equal(0, updated.toNumber()) - assert.equal(0, ans.toNumber()) - - const tx = await aggregator - .connect(personas.Nelly) - .submit(nextRound, answer) - const receipt = await tx.wait() - - const block = await ethers.provider.getBlock(receipt.blockHash ?? '') - - updated = await aggregator.getTimestamp(previousRound) - ans = await aggregator.getAnswer(previousRound) - bigNumEquals(BigNumber.from(block.timestamp), updated) - assert.equal(answer, ans.toNumber()) - - const round = await aggregator.getRoundData(previousRound) - bigNumEquals(previousRound, round.roundId) - bigNumEquals(ans, round.answer) - bigNumEquals(updated, round.updatedAt) - bigNumEquals(previousRound - 1, round.answeredInRound) - }) - - it('sets the previous round as timed out', async () => { - const previousRound = nextRound - 1 - evmRevert(aggregator.getRoundData(previousRound), 'No data present') - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - - const round = await aggregator.getRoundData(previousRound) - assert.notEqual(round.roundId, round.answeredInRound) - bigNumEquals(previousRound - 1, round.answeredInRound) - }) - - it('still respects the delay restriction', async () => { - // expected to revert because the sender started the last round - await evmRevert( - aggregator.connect(personas.Ned).submit(nextRound, answer), - ) - }) - - it('uses the timeout set at the beginning of the round', async () => { - await updateFutureRounds(aggregator, { - timeout: timeout + 100000, - }) - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - }) - }) - - describe('submitting values near the edges of allowed values', () => { - it('rejects values below the submission value range', async () => { - await evmRevert( - aggregator - .connect(personas.Neil) - .submit(nextRound, minSubmissionValue.sub(1)), - 'value below minSubmissionValue', - ) - }) - - it('accepts submissions equal to the min submission value', async () => { - await aggregator - .connect(personas.Neil) - .submit(nextRound, minSubmissionValue) - }) - - it('accepts submissions equal to the max submission value', async () => { - await aggregator - .connect(personas.Neil) - .submit(nextRound, maxSubmissionValue) - }) - - it('rejects submissions equal to the max submission value', async () => { - await evmRevert( - aggregator - .connect(personas.Neil) - .submit(nextRound, maxSubmissionValue.add(1)), - 'value above maxSubmissionValue', - ) - }) - }) - - describe('when a validator is set', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { minAnswers: 1, maxAnswers: 1 }) - oracles = [personas.Nelly] - - validator = await validatorMockFactory.connect(personas.Carol).deploy() - await aggregator.connect(personas.Carol).setValidator(validator.address) - }) - - it('calls out to the validator', async () => { - await expect( - aggregator.connect(personas.Nelly).submit(nextRound, answer), - ) - .to.emit(validator, 'Validated') - .withArgs(0, 0, nextRound, answer) - }) - }) - - describe('when the answer validator eats all gas', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { minAnswers: 1, maxAnswers: 1 }) - oracles = [personas.Nelly] - - gasGuzzler = await gasGuzzlerFactory.connect(personas.Carol).deploy() - await aggregator - .connect(personas.Carol) - .setValidator(gasGuzzler.address) - assert.equal(gasGuzzler.address, await aggregator.validator()) - }) - - it('still updates', async () => { - bigNumEquals(0, await aggregator.latestAnswer()) - - await aggregator - .connect(personas.Nelly) - .submit(nextRound, answer, { gasLimit: 500000 }) - - bigNumEquals(answer, await aggregator.latestAnswer()) - }) - }) - }) - - describe('#getAnswer', () => { - const answers = [1, 10, 101, 1010, 10101, 101010, 1010101] - - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - - for (const answer of answers) { - await aggregator.connect(personas.Neil).submit(nextRound++, answer) - } - }) - - it('retrieves the answer recorded for past rounds', async () => { - for (let i = nextRound; i < nextRound; i++) { - const answer = await aggregator.getAnswer(i) - bigNumEquals(BigNumber.from(answers[i - 1]), answer) - } - }) - - it("returns 0 for answers greater than uint32's max", async () => { - const overflowedId = BigNumber.from(2).pow(32).add(1) - const answer = await aggregator.getAnswer(overflowedId) - bigNumEquals(0, answer) - }) - }) - - describe('#getTimestamp', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - - for (let i = 0; i < 10; i++) { - await aggregator.connect(personas.Neil).submit(nextRound++, i + 1) - } - }) - - it('retrieves the answer recorded for past rounds', async () => { - let lastTimestamp = ethers.constants.Zero - - for (let i = 1; i < nextRound; i++) { - const currentTimestamp = await aggregator.getTimestamp(i) - assert.isAtLeast(currentTimestamp.toNumber(), lastTimestamp.toNumber()) - lastTimestamp = currentTimestamp - } - }) - - it("returns 0 for answers greater than uint32's max", async () => { - const overflowedId = BigNumber.from(2).pow(32).add(1) - const answer = await aggregator.getTimestamp(overflowedId) - bigNumEquals(0, answer) - }) - }) - - describe('#changeOracles', () => { - describe('adding oracles', () => { - it('increases the oracle count', async () => { - const pastCount = await aggregator.oracleCount() - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - const currentCount = await aggregator.oracleCount() - - bigNumEquals(currentCount, pastCount + 1) - }) - - it('adds the address in getOracles', async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - assert.deepEqual( - [await personas.Neil.getAddress()], - await aggregator.getOracles(), - ) - }) - - it('updates the round details', async () => { - await addOracles( - aggregator, - [personas.Neil, personas.Ned, personas.Nelly], - 1, - 3, - 2, - ) - bigNumEquals(1, await aggregator.minSubmissionCount()) - bigNumEquals(3, await aggregator.maxSubmissionCount()) - bigNumEquals(2, await aggregator.restartDelay()) - }) - - it('emits a log', async () => { - const tx = await aggregator - .connect(personas.Carol) - .changeOracles( - [], - [await personas.Ned.getAddress()], - [await personas.Neil.getAddress()], - 1, - 1, - 0, - ) - expect(tx) - .to.emit(aggregator, 'OraclePermissionsUpdated') - .withArgs(await personas.Ned.getAddress(), true) - - expect(tx) - .to.emit(aggregator, 'OracleAdminUpdated') - .withArgs( - await personas.Ned.getAddress(), - await personas.Neil.getAddress(), - ) - }) - - describe('when the oracle has already been added', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - }) - - it('reverts', async () => { - await evmRevert( - addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay), - 'oracle already enabled', - ) - }) - }) - - describe('when called by anyone but the owner', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Neil) - .changeOracles( - [], - [await personas.Neil.getAddress()], - [await personas.Neil.getAddress()], - minAns, - maxAns, - rrDelay, - ), - 'Only callable by owner', - ) - }) - }) - - describe('when an oracle gets added mid-round', () => { - beforeEach(async () => { - oracles = [personas.Neil, personas.Ned] - await addOracles( - aggregator, - oracles, - oracles.length, - oracles.length, - rrDelay, - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - await addOracles( - aggregator, - [personas.Nelly], - oracles.length + 1, - oracles.length + 1, - rrDelay, - ) - }) - - it('does not allow the oracle to update the round', async () => { - await evmRevert( - aggregator.connect(personas.Nelly).submit(nextRound, answer), - 'not yet enabled oracle', - ) - }) - - it('does allow the oracle to update future rounds', async () => { - // complete round - await aggregator.connect(personas.Ned).submit(nextRound, answer) - - // now can participate in new rounds - await aggregator.connect(personas.Nelly).submit(nextRound + 1, answer) - }) - }) - - describe('when an oracle is added after removed for a round', () => { - it('allows the oracle to update', async () => { - oracles = [personas.Neil, personas.Nelly] - await addOracles( - aggregator, - oracles, - oracles.length, - oracles.length, - rrDelay, - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - nextRound++ - - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - 1, - 1, - rrDelay, - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - nextRound++ - - await addOracles(aggregator, [personas.Nelly], 1, 1, rrDelay) - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - }) - }) - - describe('when an oracle is added and immediately removed mid-round', () => { - it('allows the oracle to update', async () => { - await addOracles( - aggregator, - oracles, - oracles.length, - oracles.length, - rrDelay, - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - nextRound++ - - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - 1, - 1, - rrDelay, - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - nextRound++ - - await addOracles(aggregator, [personas.Nelly], 1, 1, rrDelay) - - await aggregator.connect(personas.Nelly).submit(nextRound, answer) - }) - }) - - describe('when an oracle is re-added with a different admin address', () => { - it('reverts', async () => { - await addOracles( - aggregator, - oracles, - oracles.length, - oracles.length, - rrDelay, - ) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - 1, - 1, - rrDelay, - ) - - await evmRevert( - aggregator - .connect(personas.Carol) - .changeOracles( - [], - [await personas.Nelly.getAddress()], - [await personas.Carol.getAddress()], - 1, - 1, - rrDelay, - ), - 'owner cannot overwrite admin', - ) - }) - }) - - const limit = 77 - describe(`when adding more than ${limit} oracles`, () => { - let oracles: Signer[] - - beforeEach(async () => { - oracles = [] - for (let i = 0; i < limit; i++) { - const account = await new ethers.Wallet( - randomBytes(32), - ethers.provider, - ) - await personas.Default.sendTransaction({ - to: account.address, - value: toWei('0.1'), - }) - - oracles.push(account) - } - - await link.transfer( - aggregator.address, - paymentAmount.mul(limit).mul(reserveRounds), - ) - await aggregator.updateAvailableFunds() - - let addresses = oracles.slice(0, 50).map(async (o) => o.getAddress()) - await aggregator - .connect(personas.Carol) - .changeOracles([], addresses, addresses, 1, 50, rrDelay) - // add in two transactions to avoid gas limit issues - addresses = oracles.slice(50, 100).map(async (o) => o.getAddress()) - await aggregator - .connect(personas.Carol) - .changeOracles([], addresses, addresses, 1, oracles.length, rrDelay) - }) - - it('not use too much gas [ @skip-coverage ]', async () => { - let tx: any - assert.deepEqual( - // test adveserial quickselect algo - [2, 4, 6, 8, 10, 12, 14, 16, 1, 9, 5, 11, 3, 13, 7, 15], - adverserialQuickselectList(16), - ) - const inputs = adverserialQuickselectList(limit) - for (let i = 0; i < limit; i++) { - tx = await aggregator - .connect(oracles[i]) - .submit(nextRound, inputs[i]) - } - assert.isTrue(!!tx) - if (tx) { - const receipt = await tx.wait() - assert.isBelow(receipt.gasUsed.toNumber(), 600_000) - } - }) - - function adverserialQuickselectList(len: number): number[] { - const xs: number[] = [] - const pi: number[] = [] - for (let i = 0; i < len; i++) { - pi[i] = i - xs[i] = 0 - } - - for (let l = len; l > 0; l--) { - const pivot = Math.floor((l - 1) / 2) - xs[pi[pivot]] = l - const temp = pi[l - 1] - pi[l - 1] = pi[pivot] - pi[pivot] = temp - } - return xs - } - - it('reverts when another oracle is added', async () => { - await evmRevert( - aggregator - .connect(personas.Carol) - .changeOracles( - [], - [await personas.Neil.getAddress()], - [await personas.Neil.getAddress()], - limit + 1, - limit + 1, - rrDelay, - ), - 'max oracles allowed', - ) - }) - }) - - it('reverts when minSubmissions is set to 0', async () => { - await evmRevert( - addOracles(aggregator, [personas.Neil], 0, 0, 0), - 'min must be greater than 0', - ) - }) - }) - - describe('removing oracles', () => { - beforeEach(async () => { - oracles = [personas.Neil, personas.Nelly] - await addOracles( - aggregator, - oracles, - oracles.length, - oracles.length, - rrDelay, - ) - }) - - it('decreases the oracle count', async () => { - const pastCount = await aggregator.oracleCount() - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - const currentCount = await aggregator.oracleCount() - - expect(currentCount).to.equal(pastCount - 1) - }) - - it('updates the round details', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles([await personas.Neil.getAddress()], [], [], 1, 1, 0) - - bigNumEquals(1, await aggregator.minSubmissionCount()) - bigNumEquals(1, await aggregator.maxSubmissionCount()) - bigNumEquals(ethers.constants.Zero, await aggregator.restartDelay()) - }) - - it('emits a log', async () => { - await expect( - aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ), - ) - .to.emit(aggregator, 'OraclePermissionsUpdated') - .withArgs(await personas.Neil.getAddress(), false) - }) - - it('removes the address in getOracles', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - assert.deepEqual( - [await personas.Nelly.getAddress()], - await aggregator.getOracles(), - ) - }) - - describe('when the oracle is not currently added', () => { - beforeEach(async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - }) - - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ), - 'oracle not enabled', - ) - }) - }) - - describe('when removing the last oracle', () => { - it('does not revert', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - - await aggregator - .connect(personas.Carol) - .changeOracles([await personas.Nelly.getAddress()], [], [], 0, 0, 0) - }) - }) - - describe('when called by anyone but the owner', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Ned) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - 0, - 0, - rrDelay, - ), - 'Only callable by owner', - ) - }) - }) - - describe('when an oracle gets removed', () => { - beforeEach(async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - 1, - 1, - rrDelay, - ) - }) - - it('is allowed to report on one more round', async () => { - // next round - await advanceRound(aggregator, [personas.Nelly]) - // finish round - await advanceRound(aggregator, [personas.Neil]) - - // cannot participate in future rounds - await evmRevert( - aggregator.connect(personas.Nelly).submit(nextRound, answer), - 'no longer allowed oracle', - ) - }) - }) - - describe('when an oracle gets removed mid-round', () => { - beforeEach(async () => { - await aggregator.connect(personas.Neil).submit(nextRound, answer) - - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - 1, - 1, - rrDelay, - ) - }) - - it('is allowed to finish that round and one more round', async () => { - await advanceRound(aggregator, [personas.Nelly]) // finish round - - await advanceRound(aggregator, [personas.Nelly]) // next round - - // cannot participate in future rounds - await evmRevert( - aggregator.connect(personas.Nelly).submit(nextRound, answer), - 'no longer allowed oracle', - ) - }) - }) - - it('reverts when minSubmissions is set to 0', async () => { - await evmRevert( - aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - 0, - 0, - 0, - ), - 'min must be greater than 0', - ) - }) - }) - - describe('adding and removing oracles at once', () => { - beforeEach(async () => { - oracles = [personas.Neil, personas.Ned] - await addOracles(aggregator, oracles, 1, 1, rrDelay) - }) - - it('can swap out oracles', async () => { - assert.include( - await aggregator.getOracles(), - await personas.Ned.getAddress(), - ) - assert.notInclude( - await aggregator.getOracles(), - await personas.Nelly.getAddress(), - ) - - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Ned.getAddress()], - [await personas.Nelly.getAddress()], - [await personas.Nelly.getAddress()], - 1, - 1, - rrDelay, - ) - - assert.notInclude( - await aggregator.getOracles(), - await personas.Ned.getAddress(), - ) - assert.include( - await aggregator.getOracles(), - await personas.Nelly.getAddress(), - ) - }) - - it('is possible to remove and add the same address', async () => { - assert.include( - await aggregator.getOracles(), - await personas.Ned.getAddress(), - ) - - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Ned.getAddress()], - [await personas.Ned.getAddress()], - [await personas.Ned.getAddress()], - 1, - 1, - rrDelay, - ) - - assert.include( - await aggregator.getOracles(), - await personas.Ned.getAddress(), - ) - }) - }) - }) - - describe('#getOracles', () => { - describe('after adding oracles', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - - assert.deepEqual( - [await personas.Neil.getAddress()], - await aggregator.getOracles(), - ) - }) - - it('returns the addresses of added oracles', async () => { - await addOracles(aggregator, [personas.Ned], minAns, maxAns, rrDelay) - - assert.deepEqual( - [await personas.Neil.getAddress(), await personas.Ned.getAddress()], - await aggregator.getOracles(), - ) - - await addOracles(aggregator, [personas.Nelly], minAns, maxAns, rrDelay) - assert.deepEqual( - [ - await personas.Neil.getAddress(), - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ], - await aggregator.getOracles(), - ) - }) - }) - - describe('after removing oracles', () => { - beforeEach(async () => { - await addOracles( - aggregator, - [personas.Neil, personas.Ned, personas.Nelly], - minAns, - maxAns, - rrDelay, - ) - - assert.deepEqual( - [ - await personas.Neil.getAddress(), - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ], - await aggregator.getOracles(), - ) - }) - - it('reorders when removing from the beginning', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Neil.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - assert.deepEqual( - [await personas.Nelly.getAddress(), await personas.Ned.getAddress()], - await aggregator.getOracles(), - ) - }) - - it('reorders when removing from the middle', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Ned.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - assert.deepEqual( - [await personas.Neil.getAddress(), await personas.Nelly.getAddress()], - await aggregator.getOracles(), - ) - }) - - it('pops the last node off at the end', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [await personas.Nelly.getAddress()], - [], - [], - minAns, - maxAns, - rrDelay, - ) - assert.deepEqual( - [await personas.Neil.getAddress(), await personas.Ned.getAddress()], - await aggregator.getOracles(), - ) - }) - }) - }) - - describe('#withdrawFunds', () => { - it('succeeds', async () => { - await aggregator - .connect(personas.Carol) - .withdrawFunds(await personas.Carol.getAddress(), deposit) - - bigNumEquals(0, await aggregator.availableFunds()) - bigNumEquals( - deposit, - await link.balanceOf(await personas.Carol.getAddress()), - ) - }) - - it('does not let withdrawals happen multiple times', async () => { - await aggregator - .connect(personas.Carol) - .withdrawFunds(await personas.Carol.getAddress(), deposit) - - await evmRevert( - aggregator - .connect(personas.Carol) - .withdrawFunds(await personas.Carol.getAddress(), deposit), - 'insufficient reserve funds', - ) - }) - - describe('with a number higher than the available LINK balance', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - - it('fails', async () => { - await evmRevert( - aggregator - .connect(personas.Carol) - .withdrawFunds(await personas.Carol.getAddress(), deposit), - 'insufficient reserve funds', - ) - - bigNumEquals( - deposit.sub(paymentAmount), - await aggregator.availableFunds(), - ) - }) - }) - - describe('with oracles still present', () => { - beforeEach(async () => { - oracles = [personas.Neil, personas.Ned, personas.Nelly] - await addOracles(aggregator, oracles, 1, 1, rrDelay) - - bigNumEquals(deposit, await aggregator.availableFunds()) - }) - - it('does not allow withdrawal with less than 2x rounds of payments', async () => { - const oracleReserve = paymentAmount - .mul(oracles.length) - .mul(reserveRounds) - const allowed = deposit.sub(oracleReserve) - - //one more than the allowed amount cannot be withdrawn - await evmRevert( - aggregator - .connect(personas.Carol) - .withdrawFunds(await personas.Carol.getAddress(), allowed.add(1)), - 'insufficient reserve funds', - ) - - // the allowed amount can be withdrawn - await aggregator - .connect(personas.Carol) - .withdrawFunds(await personas.Carol.getAddress(), allowed) - }) - }) - - describe('when called by a non-owner', () => { - it('fails', async () => { - await evmRevert( - aggregator - .connect(personas.Eddy) - .withdrawFunds(await personas.Carol.getAddress(), deposit), - 'Only callable by owner', - ) - - bigNumEquals(deposit, await aggregator.availableFunds()) - }) - }) - }) - - describe('#updateFutureRounds', () => { - let minSubmissionCount, maxSubmissionCount - const newPaymentAmount = toWei('2') - const newMin = 1 - const newMax = 3 - const newDelay = 2 - - beforeEach(async () => { - oracles = [personas.Neil, personas.Ned, personas.Nelly] - minSubmissionCount = oracles.length - maxSubmissionCount = oracles.length - await addOracles( - aggregator, - oracles, - minSubmissionCount, - maxSubmissionCount, - rrDelay, - ) - - bigNumEquals(paymentAmount, await aggregator.paymentAmount()) - assert.equal(minSubmissionCount, await aggregator.minSubmissionCount()) - assert.equal(maxSubmissionCount, await aggregator.maxSubmissionCount()) - }) - - it('updates the min and max answer counts', async () => { - await updateFutureRounds(aggregator, { - payment: newPaymentAmount, - minAnswers: newMin, - maxAnswers: newMax, - restartDelay: newDelay, - }) - - bigNumEquals(newPaymentAmount, await aggregator.paymentAmount()) - bigNumEquals( - BigNumber.from(newMin), - await aggregator.minSubmissionCount(), - ) - bigNumEquals( - BigNumber.from(newMax), - await aggregator.maxSubmissionCount(), - ) - bigNumEquals(BigNumber.from(newDelay), await aggregator.restartDelay()) - }) - - it('emits a log announcing the new round details', async () => { - await expect( - updateFutureRounds(aggregator, { - payment: newPaymentAmount, - minAnswers: newMin, - maxAnswers: newMax, - restartDelay: newDelay, - timeout: timeout + 1, - }), - ) - .to.emit(aggregator, 'RoundDetailsUpdated') - .withArgs(newPaymentAmount, newMin, newMax, newDelay, timeout + 1) - }) - - describe('when it is set to higher than the number or oracles', () => { - it('reverts', async () => { - await evmRevert( - updateFutureRounds(aggregator, { - maxAnswers: 4, - }), - 'max cannot exceed total', - ) - }) - }) - - describe('when it sets the min higher than the max', () => { - it('reverts', async () => { - await evmRevert( - updateFutureRounds(aggregator, { - minAnswers: 3, - maxAnswers: 2, - }), - 'max must equal/exceed min', - ) - }) - }) - - describe('when delay equal or greater the oracle count', () => { - it('reverts', async () => { - await evmRevert( - updateFutureRounds(aggregator, { - restartDelay: 3, - }), - 'delay cannot exceed total', - ) - }) - }) - - describe('when the payment amount does not cover reserve rounds', () => { - beforeEach(async () => {}) - - it('reverts', async () => { - const most = deposit.div(oracles.length * reserveRounds) - - // Relaxed check for the revert message due to a bug in ethers where any error message - // that starts with insufficient funds will be incorrectly returned as 'insufficient funds for intrinsic transaction cost' - await updateFutureRounds(aggregator, { - payment: most.add(1), - }).then( - () => { - // onFulfillment callback - fail('expected to revert but did not') - }, - (error: any) => { - // onRejected callback - const message = - error instanceof Object && 'message' in error - ? error.message - : JSON.stringify(error) - assert.isTrue(message.includes('insufficient funds')) - }, - ) - - await updateFutureRounds(aggregator, { - payment: most, - }) - }) - }) - - describe('min oracles is set to 0', () => { - it('reverts', async () => { - await evmRevert( - aggregator.updateFutureRounds(paymentAmount, 0, 0, rrDelay, timeout), - 'min must be greater than 0', - ) - }) - }) - - describe('when called by anyone but the owner', () => { - it('reverts', async () => { - await evmRevert( - updateFutureRounds(aggregator.connect(personas.Ned)), - 'Only callable by owner', - ) - }) - }) - }) - - describe('#updateAvailableFunds', () => { - it('checks the LINK token to see if any additional funds are available', async () => { - const originalBalance = await aggregator.availableFunds() - - await aggregator.updateAvailableFunds() - - bigNumEquals(originalBalance, await aggregator.availableFunds()) - - await link.transfer(aggregator.address, deposit) - await aggregator.updateAvailableFunds() - - const newBalance = await aggregator.availableFunds() - bigNumEquals(originalBalance.add(deposit), newBalance) - }) - - it('removes allocated funds from the available balance', async () => { - const originalBalance = await aggregator.availableFunds() - - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - await aggregator.connect(personas.Neil).submit(nextRound, answer) - await link.transfer(aggregator.address, deposit) - await aggregator.updateAvailableFunds() - - const expected = originalBalance.add(deposit).sub(paymentAmount) - const newBalance = await aggregator.availableFunds() - bigNumEquals(expected, newBalance) - }) - - it('emits a log', async () => { - await link.transfer(aggregator.address, deposit) - - const tx = await aggregator.updateAvailableFunds() - const receipt = await tx.wait() - - const reportedBalance = BigNumber.from(receipt.logs?.[0].topics[1] ?? -1) - bigNumEquals(await aggregator.availableFunds(), reportedBalance) - }) - - describe('when the available funds have not changed', () => { - it('does not emit a log', async () => { - const tx = await aggregator.updateAvailableFunds() - const receipt = await tx.wait() - - assert.equal(0, receipt.logs?.length) - }) - }) - }) - - describe('#withdrawPayment', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], minAns, maxAns, rrDelay) - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - - it('transfers LINK to the recipient', async () => { - const originalBalance = await link.balanceOf(aggregator.address) - bigNumEquals(0, await link.balanceOf(await personas.Neil.getAddress())) - - await aggregator - .connect(personas.Neil) - .withdrawPayment( - await personas.Neil.getAddress(), - await personas.Neil.getAddress(), - paymentAmount, - ) - - bigNumEquals( - originalBalance.sub(paymentAmount), - await link.balanceOf(aggregator.address), - ) - bigNumEquals( - paymentAmount, - await link.balanceOf(await personas.Neil.getAddress()), - ) - }) - - it('decrements the allocated funds counter', async () => { - const originalAllocation = await aggregator.allocatedFunds() - - await aggregator - .connect(personas.Neil) - .withdrawPayment( - await personas.Neil.getAddress(), - await personas.Neil.getAddress(), - paymentAmount, - ) - - bigNumEquals( - originalAllocation.sub(paymentAmount), - await aggregator.allocatedFunds(), - ) - }) - - describe('when the caller withdraws more than they have', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Neil) - .withdrawPayment( - await personas.Neil.getAddress(), - await personas.Neil.getAddress(), - paymentAmount.add(BigNumber.from(1)), - ), - 'insufficient withdrawable funds', - ) - }) - }) - - describe('when the caller is not the admin', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Nelly) - .withdrawPayment( - await personas.Neil.getAddress(), - await personas.Nelly.getAddress(), - BigNumber.from(1), - ), - 'only callable by admin', - ) - }) - }) - }) - - describe('#transferAdmin', () => { - beforeEach(async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [], - [await personas.Ned.getAddress()], - [await personas.Neil.getAddress()], - minAns, - maxAns, - rrDelay, - ) - }) - - describe('when the admin tries to transfer the admin', () => { - it('works', async () => { - await expect( - aggregator - .connect(personas.Neil) - .transferAdmin( - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ), - ) - .to.emit(aggregator, 'OracleAdminUpdateRequested') - .withArgs( - await personas.Ned.getAddress(), - await personas.Neil.getAddress(), - await personas.Nelly.getAddress(), - ) - assert.equal( - await personas.Neil.getAddress(), - await aggregator.getAdmin(await personas.Ned.getAddress()), - ) - }) - }) - - describe('when the non-admin owner tries to update the admin', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Carol) - .transferAdmin( - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ), - 'only callable by admin', - ) - }) - }) - - describe('when the non-admin oracle tries to update the admin', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Ned) - .transferAdmin( - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ), - 'only callable by admin', - ) - }) - }) - }) - - describe('#acceptAdmin', () => { - beforeEach(async () => { - await aggregator - .connect(personas.Carol) - .changeOracles( - [], - [await personas.Ned.getAddress()], - [await personas.Neil.getAddress()], - minAns, - maxAns, - rrDelay, - ) - const tx = await aggregator - .connect(personas.Neil) - .transferAdmin( - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ) - await tx.wait() - }) - - describe('when the new admin tries to accept', () => { - it('works', async () => { - await expect( - aggregator - .connect(personas.Nelly) - .acceptAdmin(await personas.Ned.getAddress()), - ) - .to.emit(aggregator, 'OracleAdminUpdated') - .withArgs( - await personas.Ned.getAddress(), - await personas.Nelly.getAddress(), - ) - assert.equal( - await personas.Nelly.getAddress(), - await aggregator.getAdmin(await personas.Ned.getAddress()), - ) - }) - }) - - describe('when someone other than the new admin tries to accept', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Ned) - .acceptAdmin(await personas.Ned.getAddress()), - 'only callable by pending admin', - ) - await evmRevert( - aggregator - .connect(personas.Neil) - .acceptAdmin(await personas.Ned.getAddress()), - 'only callable by pending admin', - ) - }) - }) - }) - - describe('#onTokenTransfer', () => { - it('updates the available balance', async () => { - const originalBalance = await aggregator.availableFunds() - - await aggregator.updateAvailableFunds() - - bigNumEquals(originalBalance, await aggregator.availableFunds()) - - await link.transferAndCall(aggregator.address, deposit, '0x') - - const newBalance = await aggregator.availableFunds() - bigNumEquals(originalBalance.add(deposit), newBalance) - }) - - it('reverts given calldata', async () => { - await evmRevert( - // error message is not bubbled up by link token - link.transferAndCall(aggregator.address, deposit, '0x12345678'), - ) - }) - }) - - describe('#requestNewRound', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], 1, 1, 0) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - nextRound = nextRound + 1 - - await aggregator.setRequesterPermissions( - await personas.Carol.getAddress(), - true, - 0, - ) - }) - - it('announces a new round via log event', async () => { - await expect(aggregator.requestNewRound()).to.emit(aggregator, 'NewRound') - }) - - it('returns the new round ID', async () => { - testHelper = await testHelperFactory.connect(personas.Carol).deploy() - await aggregator.setRequesterPermissions(testHelper.address, true, 0) - let roundId = await testHelper.requestedRoundId() - assert.equal(roundId.toNumber(), 0) - - await testHelper.requestNewRound(aggregator.address) - - // return value captured by test helper - roundId = await testHelper.requestedRoundId() - assert.isAbove(roundId.toNumber(), 0) - }) - - describe('when there is a round in progress', () => { - beforeEach(async () => { - await aggregator.requestNewRound() - }) - - it('reverts', async () => { - await evmRevert( - aggregator.requestNewRound(), - 'prev round must be supersedable', - ) - }) - - describe('when that round has timed out', () => { - beforeEach(async () => { - await increaseTimeBy(timeout + 1, ethers.provider) - await mineBlock(ethers.provider) - }) - - it('starts a new round', async () => { - await expect(aggregator.requestNewRound()).to.emit( - aggregator, - 'NewRound', - ) - }) - }) - }) - - describe('when there is a restart delay set', () => { - beforeEach(async () => { - await aggregator.setRequesterPermissions( - await personas.Eddy.getAddress(), - true, - 1, - ) - }) - - it('reverts if a round is started before the delay', async () => { - await aggregator.connect(personas.Eddy).requestNewRound() - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - nextRound = nextRound + 1 - - // Eddy can't start because of the delay - await evmRevert( - aggregator.connect(personas.Eddy).requestNewRound(), - 'must delay requests', - ) - // Carol starts a new round instead - await aggregator.connect(personas.Carol).requestNewRound() - - // round completes - await aggregator.connect(personas.Neil).submit(nextRound, answer) - nextRound = nextRound + 1 - - // now Eddy can start again - await aggregator.connect(personas.Eddy).requestNewRound() - }) - }) - - describe('when all oracles have been removed and then re-added', () => { - it('does not get stuck', async () => { - await aggregator - .connect(personas.Carol) - .changeOracles([await personas.Neil.getAddress()], [], [], 0, 0, 0) - - // advance a few rounds - for (let i = 0; i < 7; i++) { - await aggregator.requestNewRound() - nextRound = nextRound + 1 - await increaseTimeBy(timeout + 1, ethers.provider) - await mineBlock(ethers.provider) - } - - await addOracles(aggregator, [personas.Neil], 1, 1, 0) - await aggregator.connect(personas.Neil).submit(nextRound, answer) - }) - }) - }) - - describe('#setRequesterPermissions', () => { - beforeEach(async () => { - await addOracles(aggregator, [personas.Neil], 1, 1, 0) - - await aggregator.connect(personas.Neil).submit(nextRound, answer) - nextRound = nextRound + 1 - }) - - describe('when called by the owner', () => { - it('allows the specified address to start new rounds', async () => { - await aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - true, - 0, - ) - - await aggregator.connect(personas.Neil).requestNewRound() - }) - - it('emits a log announcing the update', async () => { - await expect( - aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - true, - 0, - ), - ) - .to.emit(aggregator, 'RequesterPermissionsSet') - .withArgs(await personas.Neil.getAddress(), true, 0) - }) - - describe('when the address is already authorized', () => { - beforeEach(async () => { - await aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - true, - 0, - ) - }) - - it('does not emit a log for already authorized accounts', async () => { - const tx = await aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - true, - 0, - ) - const receipt = await tx.wait() - assert.equal(0, receipt?.logs?.length) - }) - }) - - describe('when permission is removed by the owner', () => { - beforeEach(async () => { - await aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - true, - 0, - ) - }) - - it('does not allow the specified address to start new rounds', async () => { - await aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - false, - 0, - ) - - await evmRevert( - aggregator.connect(personas.Neil).requestNewRound(), - 'not authorized requester', - ) - }) - - it('emits a log announcing the update', async () => { - await expect( - aggregator.setRequesterPermissions( - await personas.Neil.getAddress(), - false, - 0, - ), - ) - .to.emit(aggregator, 'RequesterPermissionsSet') - .withArgs(await personas.Neil.getAddress(), false, 0) - }) - - it('does not emit a log for accounts without authorization', async () => { - const tx = await aggregator.setRequesterPermissions( - await personas.Ned.getAddress(), - false, - 0, - ) - const receipt = await tx.wait() - assert.equal(0, receipt?.logs?.length) - }) - }) - }) - - describe('when called by a stranger', () => { - it('reverts', async () => { - await evmRevert( - aggregator - .connect(personas.Neil) - .setRequesterPermissions(await personas.Neil.getAddress(), true, 0), - 'Only callable by owner', - ) - - await evmRevert( - aggregator.connect(personas.Neil).requestNewRound(), - 'not authorized requester', - ) - }) - }) - }) - - describe('#oracleRoundState', () => { - describe('when round ID 0 is passed in', () => { - const previousSubmission = 42 - let baseFunds: any - let minAnswers: number - let maxAnswers: number - let submitters: Signer[] - - beforeEach(async () => { - oracles = [ - personas.Neil, - personas.Ned, - personas.Nelly, - personas.Nancy, - personas.Norbert, - ] - minAnswers = 3 - maxAnswers = 4 - - await addOracles(aggregator, oracles, minAnswers, maxAnswers, rrDelay) - submitters = [ - personas.Nelly, - personas.Ned, - personas.Neil, - personas.Nancy, - ] - await advanceRound(aggregator, submitters, previousSubmission) - baseFunds = BigNumber.from(deposit).sub( - paymentAmount.mul(submitters.length), - ) - startingState = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - }) - - it('returns all of the important round information', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 2, - latestSubmission: previousSubmission, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds, - oracleCount: oracles.length, - paymentAmount, - }) - }) - - it('reverts if called by a contract', async () => { - testHelper = await testHelperFactory.connect(personas.Carol).deploy() - await evmRevert( - testHelper.readOracleRoundState( - aggregator.address, - await personas.Neil.getAddress(), - ), - 'off-chain reading only', - ) - }) - - describe('when the restart delay is not enforced', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { - minAnswers, - maxAnswers, - restartDelay: 0, - }) - }) - - describe('< min submissions and oracle not included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [personas.Neil]) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 2, - latestSubmission: previousSubmission, - startedAt: ShouldBeSet, - timeout, - availableFunds: baseFunds.sub(paymentAmount), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('< min submissions and oracle included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [personas.Nelly]) - }) - - it('is not eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 2, - latestSubmission: answer, - startedAt: ShouldBeSet, - timeout, - availableFunds: baseFunds.sub(paymentAmount), - oracleCount: oracles.length, - paymentAmount, - }) - }) - - describe('and timed out', () => { - beforeEach(async () => { - await increaseTimeBy(timeout + 1, ethers.provider) - await mineBlock(ethers.provider) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - describe('>= min submissions and oracle not included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [ - personas.Neil, - personas.Nancy, - personas.Ned, - ]) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 2, - latestSubmission: previousSubmission, - startedAt: ShouldBeSet, - timeout, - availableFunds: baseFunds.sub(paymentAmount.mul(3)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('>= min submissions and oracle included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [ - personas.Neil, - personas.Nelly, - personas.Ned, - ]) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(3)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - - describe('and timed out', () => { - beforeEach(async () => { - await increaseTimeBy(timeout + 1, ethers.provider) - await mineBlock(ethers.provider) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(3)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - describe('max submissions and oracle not included', () => { - beforeEach(async () => { - submitters = [ - personas.Neil, - personas.Ned, - personas.Nancy, - personas.Norbert, - ] - assert.equal( - submitters.length, - maxAnswers, - 'precondition, please update submitters if maxAnswers changes', - ) - await advanceRound(aggregator, submitters) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 3, - latestSubmission: previousSubmission, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(4)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('max submissions and oracle included', () => { - beforeEach(async () => { - submitters = [ - personas.Neil, - personas.Ned, - personas.Nelly, - personas.Nancy, - ] - assert.equal( - submitters.length, - maxAnswers, - 'precondition, please update submitters if maxAnswers changes', - ) - await advanceRound(aggregator, submitters) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(4)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - describe('when the restart delay is enforced', () => { - beforeEach(async () => { - await updateFutureRounds(aggregator, { - minAnswers, - maxAnswers, - restartDelay: maxAnswers - 1, - }) - }) - - describe('< min submissions and oracle not included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [personas.Neil, personas.Ned]) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 2, - latestSubmission: previousSubmission, - startedAt: ShouldBeSet, - timeout, - availableFunds: baseFunds.sub(paymentAmount.mul(2)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('< min submissions and oracle included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [personas.Neil, personas.Nelly]) - }) - - it('is not eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 2, - latestSubmission: answer, - startedAt: ShouldBeSet, - timeout, - availableFunds: baseFunds.sub(paymentAmount.mul(2)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - - describe('and timed out', () => { - beforeEach(async () => { - await increaseTimeBy(timeout + 1, ethers.provider) - await mineBlock(ethers.provider) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(2)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - describe('>= min submissions and oracle not included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [ - personas.Neil, - personas.Ned, - personas.Nancy, - ]) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 2, - latestSubmission: previousSubmission, - startedAt: ShouldBeSet, - timeout, - availableFunds: baseFunds.sub(paymentAmount.mul(3)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('>= min submissions and oracle included', () => { - beforeEach(async () => { - await advanceRound(aggregator, [ - personas.Neil, - personas.Ned, - personas.Nelly, - ]) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(3)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - - describe('and timed out', () => { - beforeEach(async () => { - await increaseTimeBy(timeout + 1, ethers.provider) - await mineBlock(ethers.provider) - }) - - it('is eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, // restart delay enforced - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(3)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - describe('max submissions and oracle not included', () => { - beforeEach(async () => { - submitters = [ - personas.Neil, - personas.Ned, - personas.Nancy, - personas.Norbert, - ] - assert.equal( - submitters.length, - maxAnswers, - 'precondition, please update submitters if maxAnswers changes', - ) - await advanceRound(aggregator, submitters, answer) - }) - - it('is not eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 3, - latestSubmission: previousSubmission, - startedAt: ShouldNotBeSet, - timeout: 0, // details have been deleted - availableFunds: baseFunds.sub(paymentAmount.mul(4)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('max submissions and oracle included', () => { - beforeEach(async () => { - submitters = [ - personas.Neil, - personas.Ned, - personas.Nelly, - personas.Nancy, - ] - assert.equal( - submitters.length, - maxAnswers, - 'precondition, please update submitters if maxAnswers changes', - ) - await advanceRound(aggregator, submitters, answer) - }) - - it('is not eligible to submit', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 3, - latestSubmission: answer, - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: baseFunds.sub(paymentAmount.mul(4)), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - }) - - describe('when non-zero round ID 0 is passed in', () => { - const answers = [0, 42, 47, 52, 57] - let currentFunds: any - - beforeEach(async () => { - oracles = [personas.Neil, personas.Ned, personas.Nelly] - - await addOracles(aggregator, oracles, 2, 3, rrDelay) - startingState = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 0, - ) - await advanceRound(aggregator, oracles, answers[1]) - await advanceRound( - aggregator, - [personas.Neil, personas.Ned], - answers[2], - ) - await advanceRound(aggregator, oracles, answers[3]) - await advanceRound(aggregator, [personas.Neil], answers[4]) - const submissionsSoFar = 9 - currentFunds = BigNumber.from(deposit).sub( - paymentAmount.mul(submissionsSoFar), - ) - }) - - it('returns info about previous rounds', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 1, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 1, - latestSubmission: answers[3], - startedAt: ShouldBeSet, - timeout: 0, - availableFunds: currentFunds, - oracleCount: oracles.length, - paymentAmount: 0, - }) - }) - - it('returns info about previous rounds that were not submitted to', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 2, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 2, - latestSubmission: answers[3], - startedAt: ShouldBeSet, - timeout, - availableFunds: currentFunds, - oracleCount: oracles.length, - paymentAmount, - }) - }) - - describe('for the current round', () => { - describe('which has not been submitted to', () => { - it("returns info about the current round that hasn't been submitted to", async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 4, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 4, - latestSubmission: answers[3], - startedAt: ShouldBeSet, - timeout, - availableFunds: currentFunds, - oracleCount: oracles.length, - paymentAmount, - }) - }) - - it('returns info about the subsequent round', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 5, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 5, - latestSubmission: answers[3], - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: currentFunds, - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - - describe('which has been submitted to', () => { - beforeEach(async () => { - await aggregator.connect(personas.Nelly).submit(4, answers[4]) - }) - - it("returns info about the current round that hasn't been submitted to", async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 4, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 4, - latestSubmission: answers[4], - startedAt: ShouldBeSet, - timeout, - availableFunds: currentFunds.sub(paymentAmount), - oracleCount: oracles.length, - paymentAmount, - }) - }) - - it('returns info about the subsequent round', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 5, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: true, - roundId: 5, - latestSubmission: answers[4], - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: currentFunds.sub(paymentAmount), - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - it('returns speculative info about future rounds', async () => { - const state = await aggregator.oracleRoundState( - await personas.Nelly.getAddress(), - 6, - ) - - await checkOracleRoundState(state, { - eligibleToSubmit: false, - roundId: 6, - latestSubmission: answers[3], - startedAt: ShouldNotBeSet, - timeout: 0, - availableFunds: currentFunds, - oracleCount: oracles.length, - paymentAmount, - }) - }) - }) - }) - - describe('#getRoundData', () => { - let latestRoundId: any - beforeEach(async () => { - oracles = [personas.Nelly] - const minMax = oracles.length - await addOracles(aggregator, oracles, minMax, minMax, rrDelay) - await advanceRound(aggregator, oracles, answer) - latestRoundId = await aggregator.latestRound() - }) - - it('returns the relevant round information', async () => { - const round = await aggregator.getRoundData(latestRoundId) - bigNumEquals(latestRoundId, round.roundId) - bigNumEquals(answer, round.answer) - const nowSeconds = new Date().valueOf() / 1000 - assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120) - bigNumEquals(round.updatedAt, round.startedAt) - bigNumEquals(latestRoundId, round.answeredInRound) - }) - - it('reverts if a round is not present', async () => { - await evmRevert( - aggregator.getRoundData(latestRoundId.add(1)), - 'No data present', - ) - }) - - it('reverts if a round ID is too big', async () => { - const overflowedId = BigNumber.from(2).pow(32).add(1) - - await evmRevert(aggregator.getRoundData(overflowedId), 'No data present') - }) - }) - - describe('#latestRoundData', () => { - beforeEach(async () => { - oracles = [personas.Nelly] - const minMax = oracles.length - await addOracles(aggregator, oracles, minMax, minMax, rrDelay) - }) - - describe('when an answer has already been received', () => { - beforeEach(async () => { - await advanceRound(aggregator, oracles, answer) - }) - - it('returns the relevant round info without reverting', async () => { - const round = await aggregator.latestRoundData() - const latestRoundId = await aggregator.latestRound() - - bigNumEquals(latestRoundId, round.roundId) - bigNumEquals(answer, round.answer) - const nowSeconds = new Date().valueOf() / 1000 - assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120) - bigNumEquals(round.updatedAt, round.startedAt) - bigNumEquals(latestRoundId, round.answeredInRound) - }) - }) - - it('reverts if a round is not present', async () => { - await evmRevert(aggregator.latestRoundData(), 'No data present') - }) - }) - - describe('#latestAnswer', () => { - beforeEach(async () => { - oracles = [personas.Nelly] - const minMax = oracles.length - await addOracles(aggregator, oracles, minMax, minMax, rrDelay) - }) - - describe('when an answer has already been received', () => { - beforeEach(async () => { - await advanceRound(aggregator, oracles, answer) - }) - - it('returns the latest answer without reverting', async () => { - bigNumEquals(answer, await aggregator.latestAnswer()) - }) - }) - - it('returns zero', async () => { - bigNumEquals(0, await aggregator.latestAnswer()) - }) - }) - - describe('#setValidator', () => { - beforeEach(async () => { - validator = await validatorMockFactory.connect(personas.Carol).deploy() - assert.equal(emptyAddress, await aggregator.validator()) - }) - - it('emits a log event showing the validator was changed', async () => { - await expect( - aggregator.connect(personas.Carol).setValidator(validator.address), - ) - .to.emit(aggregator, 'ValidatorUpdated') - .withArgs(emptyAddress, validator.address) - assert.equal(validator.address, await aggregator.validator()) - - await expect( - aggregator.connect(personas.Carol).setValidator(validator.address), - ).to.not.emit(aggregator, 'ValidatorUpdated') - assert.equal(validator.address, await aggregator.validator()) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - aggregator.connect(personas.Neil).setValidator(validator.address), - 'Only callable by owner', - ) - }) - }) - }) - - describe('integrating with historic deviation checker', () => { - let validator: Contract - let flags: Contract - let ac: Contract - const flaggingThreshold = 1000 // 1% - - beforeEach(async () => { - ac = await acFactory.connect(personas.Carol).deploy() - flags = await flagsFactory.connect(personas.Carol).deploy(ac.address) - validator = await validatorFactory - .connect(personas.Carol) - .deploy(flags.address, flaggingThreshold) - await ac.connect(personas.Carol).addAccess(validator.address) - - await aggregator.connect(personas.Carol).setValidator(validator.address) - - oracles = [personas.Nelly] - const minMax = oracles.length - await addOracles(aggregator, oracles, minMax, minMax, rrDelay) - }) - - it('raises a flag on with high enough deviation', async () => { - await aggregator.connect(personas.Nelly).submit(nextRound, 100) - nextRound++ - - await expect(aggregator.connect(personas.Nelly).submit(nextRound, 102)) - .to.emit(flags, 'FlagRaised') - .withArgs(aggregator.address) - }) - - it('does not raise a flag with low enough deviation', async () => { - await aggregator.connect(personas.Nelly).submit(nextRound, 100) - nextRound++ - - await expect( - aggregator.connect(personas.Nelly).submit(nextRound, 101), - ).to.not.emit(flags, 'FlagRaised') - }) - }) -}) diff --git a/contracts/test/v0.6/Median.test.ts b/contracts/test/v0.6/Median.test.ts deleted file mode 100644 index 8aea4722b6d..00000000000 --- a/contracts/test/v0.6/Median.test.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { Signer, Contract, ContractFactory, BigNumber } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { bigNumEquals } from '../test-helpers/matchers' - -let defaultAccount: Signer -let medianTestHelperFactory: ContractFactory -before(async () => { - const personas: Personas = (await getUsers()).personas - defaultAccount = personas.Default - medianTestHelperFactory = await ethers.getContractFactory( - 'src/v0.6/tests/MedianTestHelper.sol:MedianTestHelper', - defaultAccount, - ) -}) - -describe('Median', () => { - let median: Contract - - beforeEach(async () => { - median = await medianTestHelperFactory.connect(defaultAccount).deploy() - }) - - describe('testing various lists', () => { - const tests = [ - { - name: 'ordered ascending', - responses: [0, 1, 2, 3, 4, 5, 6, 7], - want: 3, - }, - { - name: 'ordered descending', - responses: [7, 6, 5, 4, 3, 2, 1, 0], - want: 3, - }, - { - name: 'unordered length 1', - responses: [20], - want: 20, - }, - { - name: 'unordered length 2', - responses: [20, 0], - want: 10, - }, - { - name: 'unordered length 3', - responses: [20, 0, 16], - want: 16, - }, - { - name: 'unordered length 4', - responses: [20, 0, 15, 16], - want: 15, - }, - { - name: 'unordered length 7', - responses: [1001, 1, 101, 10, 11, 0, 111], - want: 11, - }, - { - name: 'unordered length 9', - responses: [8, 8, 4, 5, 5, 7, 9, 5, 9], - want: 7, - }, - { - name: 'unordered long', - responses: [33, 44, 89, 101, 67, 7, 23, 55, 88, 324, 0, 88], - want: 61, // 67 + 55 / 2 - }, - { - name: 'unordered longer', - responses: [ - 333121, 323453, 337654, 345363, 345363, 333456, 335477, 333323, - 332352, 354648, 983260, 333856, 335468, 376987, 333253, 388867, - 337879, 333324, 338678, - ], - want: 335477, - }, - { - name: 'overflowing numbers', - responses: [ - BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819967', - ), - BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819967', - ), - ], - want: BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819967', - ), - }, - { - name: 'overflowing numbers', - responses: [ - BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819967', - ), - BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819966', - ), - ], - want: BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819966', - ), - }, - { - name: 'really long', - responses: [ - 56, 2, 31, 33, 55, 38, 35, 12, 41, 47, 21, 22, 40, 39, 10, 32, 49, 3, - 54, 45, 53, 14, 20, 59, 1, 30, 24, 6, 5, 37, 58, 51, 46, 17, 29, 7, - 27, 9, 43, 8, 34, 42, 28, 23, 57, 0, 11, 48, 52, 50, 15, 16, 26, 25, - 4, 36, 19, 44, 18, 13, - ], - want: 29, - }, - ] - - for (const test of tests) { - it(test.name, async () => { - bigNumEquals(test.want, await median.publicGet(test.responses)) - }) - } - }) - - // long running (minutes) exhaustive test. - // skipped because very slow, but useful for thorough validation - xit('permutations', async () => { - const permutations = (list: number[]) => { - const result: number[][] = [] - const used: number[] = [] - - const permute = (unused: number[]) => { - if (unused.length == 0) { - result.push([...used]) - return - } - - for (let i = 0; i < unused.length; i++) { - const elem = unused.splice(i, 1)[0] - used.push(elem) - permute(unused) - unused.splice(i, 0, elem) - used.pop() - } - } - - permute(list) - return result - } - - { - const list = [0, 2, 5, 7, 8, 10] - for (const permuted of permutations(list)) { - for (let i = 0; i < list.length; i++) { - for (let j = 0; j < list.length; j++) { - if (i < j) { - const foo = await median.publicQuickselectTwo(permuted, i, j) - bigNumEquals(list[i], foo[0]) - bigNumEquals(list[j], foo[1]) - } - } - } - } - } - - { - const list = [0, 1, 1, 1, 2] - for (const permuted of permutations(list)) { - for (let i = 0; i < list.length; i++) { - for (let j = 0; j < list.length; j++) { - if (i < j) { - const foo = await median.publicQuickselectTwo(permuted, i, j) - bigNumEquals(list[i], foo[0]) - bigNumEquals(list[j], foo[1]) - } - } - } - } - } - }) - - // Checks the validity of the sorting network in `shortList` - describe('validate sorting network', () => { - const net = [ - [0, 1], - [2, 3], - [4, 5], - [0, 2], - [1, 3], - [4, 6], - [1, 2], - [5, 6], - [0, 4], - [1, 5], - [2, 6], - [1, 4], - [3, 6], - [2, 4], - [3, 5], - [3, 4], - ] - - // See: https://en.wikipedia.org/wiki/Sorting_network#Zero-one_principle - xit('zero-one principle', async () => { - const sortWithNet = (list: number[]) => { - for (const [i, j] of net) { - if (list[i] > list[j]) { - ;[list[i], list[j]] = [list[j], list[i]] - } - } - } - - for (let n = 0; n < (1 << 7) - 1; n++) { - const list = [ - (n >> 6) & 1, - (n >> 5) & 1, - (n >> 4) & 1, - (n >> 3) & 1, - (n >> 2) & 1, - (n >> 1) & 1, - (n >> 0) & 1, - ] - const sum = list.reduce((a, b) => a + b, 0) - sortWithNet(list) - const sortedSum = list.reduce((a, b) => a + b, 0) - assert.equal(sortedSum, sum, 'Number of zeros and ones changed') - list.reduce((switched, i) => { - assert.isTrue(!switched || i != 0, 'error at n=' + n.toString()) - return i != 0 - }, false) - } - }) - }) -}) diff --git a/contracts/test/v0.6/Owned.test.ts b/contracts/test/v0.6/Owned.test.ts deleted file mode 100644 index f522b9c44c9..00000000000 --- a/contracts/test/v0.6/Owned.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { Signer, Contract, ContractFactory } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' - -let personas: Personas - -let owner: Signer -let nonOwner: Signer -let newOwner: Signer - -let ownedFactory: ContractFactory -let owned: Contract - -before(async () => { - personas = (await getUsers()).personas - owner = personas.Carol - nonOwner = personas.Neil - newOwner = personas.Ned - ownedFactory = await ethers.getContractFactory( - 'src/v0.6/Owned.sol:Owned', - owner, - ) -}) - -describe('Owned', () => { - beforeEach(async () => { - owned = await ownedFactory.connect(owner).deploy() - }) - - it('has a limited public interface [ @skip-coverage ]', async () => { - publicAbi(owned, ['acceptOwnership', 'owner', 'transferOwnership']) - }) - - describe('#constructor', () => { - it('assigns ownership to the deployer', async () => { - const [actual, expected] = await Promise.all([ - owner.getAddress(), - owned.owner(), - ]) - - assert.equal(actual, expected) - }) - }) - - describe('#transferOwnership', () => { - describe('when called by an owner', () => { - it('emits a log', async () => { - await expect( - owned.connect(owner).transferOwnership(await newOwner.getAddress()), - ) - .to.emit(owned, 'OwnershipTransferRequested') - .withArgs(await owner.getAddress(), await newOwner.getAddress()) - }) - }) - }) - - describe('when called by anyone but the owner', () => { - it('reverts', async () => - await expect( - owned.connect(nonOwner).transferOwnership(await newOwner.getAddress()), - ).to.be.reverted) - }) - - describe('#acceptOwnership', () => { - describe('after #transferOwnership has been called', () => { - beforeEach(async () => { - await owned - .connect(owner) - .transferOwnership(await newOwner.getAddress()) - }) - - it('allows the recipient to call it', async () => { - await expect(owned.connect(newOwner).acceptOwnership()) - .to.emit(owned, 'OwnershipTransferred') - .withArgs(await owner.getAddress(), await newOwner.getAddress()) - }) - - it('does not allow a non-recipient to call it', async () => - await expect(owned.connect(nonOwner).acceptOwnership()).to.be.reverted) - }) - }) -}) diff --git a/contracts/test/v0.6/SignedSafeMath.test.ts b/contracts/test/v0.6/SignedSafeMath.test.ts deleted file mode 100644 index e942f64d6b5..00000000000 --- a/contracts/test/v0.6/SignedSafeMath.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { ethers } from 'hardhat' -import { expect } from 'chai' -import { Signer, Contract, ContractFactory, BigNumber } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { bigNumEquals } from '../test-helpers/matchers' - -let defaultAccount: Signer -let concreteSignedSafeMathFactory: ContractFactory - -before(async () => { - const personas: Personas = (await getUsers()).personas - defaultAccount = personas.Default - concreteSignedSafeMathFactory = await ethers.getContractFactory( - 'src/v0.6/tests/ConcreteSignedSafeMath.sol:ConcreteSignedSafeMath', - defaultAccount, - ) -}) - -describe('SignedSafeMath', () => { - // a version of the adder contract where we make all ABI exposed functions constant - // TODO: submit upstream PR to support constant contract type generation - let adder: Contract - let response: BigNumber - - const INT256_MAX = BigNumber.from( - '57896044618658097711785492504343953926634992332820282019728792003956564819967', - ) - const INT256_MIN = BigNumber.from( - '-57896044618658097711785492504343953926634992332820282019728792003956564819968', - ) - - beforeEach(async () => { - adder = await concreteSignedSafeMathFactory.connect(defaultAccount).deploy() - }) - - describe('#add', () => { - describe('given a positive and a positive', () => { - it('works', async () => { - response = await adder.testAdd(1, 2) - bigNumEquals(3, response) - }) - - it('works with zero', async () => { - response = await adder.testAdd(INT256_MAX, 0) - bigNumEquals(INT256_MAX, response) - }) - - describe('when both are large enough to overflow', () => { - it('throws', async () => { - await expect(adder.testAdd(INT256_MAX, 1)).to.be.revertedWith( - 'SignedSafeMath: addition overflow', - ) - }) - }) - }) - - describe('given a negative and a negative', () => { - it('works', async () => { - response = await adder.testAdd(-1, -2) - bigNumEquals(-3, response) - }) - - it('works with zero', async () => { - response = await adder.testAdd(INT256_MIN, 0) - bigNumEquals(INT256_MIN, response) - }) - - describe('when both are large enough to overflow', () => { - it('throws', async () => { - await expect(adder.testAdd(INT256_MIN, -1)).to.be.revertedWith( - 'SignedSafeMath: addition overflow', - ) - }) - }) - }) - - describe('given a positive and a negative', () => { - it('works', async () => { - response = await adder.testAdd(1, -2) - bigNumEquals(-1, response) - }) - }) - - describe('given a negative and a positive', () => { - it('works', async () => { - response = await adder.testAdd(-1, 2) - bigNumEquals(1, response) - }) - }) - }) - - describe('#avg', () => { - describe('given a positive and a positive', () => { - it('works', async () => { - response = await adder.testAvg(2, 4) - bigNumEquals(3, response) - }) - - it('works with zero', async () => { - response = await adder.testAvg(0, 4) - bigNumEquals(2, response) - response = await adder.testAvg(4, 0) - bigNumEquals(2, response) - }) - - it('works with large numbers', async () => { - response = await adder.testAvg(INT256_MAX, INT256_MAX) - bigNumEquals(INT256_MAX, response) - }) - - it('rounds towards zero', async () => { - response = await adder.testAvg(1, 2) - bigNumEquals(1, response) - }) - }) - - describe('given a negative and a negative', () => { - it('works', async () => { - response = await adder.testAvg(-2, -4) - bigNumEquals(-3, response) - }) - - it('works with zero', async () => { - response = await adder.testAvg(0, -4) - bigNumEquals(-2, response) - response = await adder.testAvg(-4, 0) - bigNumEquals(-2, response) - }) - - it('works with large numbers', async () => { - response = await adder.testAvg(INT256_MIN, INT256_MIN) - bigNumEquals(INT256_MIN, response) - }) - - it('rounds towards zero', async () => { - response = await adder.testAvg(-1, -2) - bigNumEquals(-1, response) - }) - }) - - describe('given a positive and a negative', () => { - it('works', async () => { - response = await adder.testAvg(2, -4) - bigNumEquals(-1, response) - response = await adder.testAvg(4, -2) - bigNumEquals(1, response) - }) - - it('works with large numbers', async () => { - response = await adder.testAvg(INT256_MAX, -2) - bigNumEquals(INT256_MAX.sub(2).div(2), response) - response = await adder.testAvg(INT256_MAX, INT256_MIN) - bigNumEquals(0, response) - }) - - it('rounds towards zero', async () => { - response = await adder.testAvg(1, -4) - bigNumEquals(-1, response) - response = await adder.testAvg(4, -1) - bigNumEquals(1, response) - }) - }) - - describe('given a negative and a positive', () => { - it('works', async () => { - response = await adder.testAvg(-2, 4) - bigNumEquals(1, response) - response = await adder.testAvg(-4, 2) - bigNumEquals(-1, response) - }) - - it('works with large numbers', async () => { - response = await adder.testAvg(INT256_MIN, 2) - bigNumEquals(INT256_MIN.add(2).div(2), response) - response = await adder.testAvg(INT256_MIN, INT256_MAX) - bigNumEquals(0, response) - }) - - it('rounds towards zero', async () => { - response = await adder.testAvg(-1, 4) - bigNumEquals(1, response) - response = await adder.testAvg(-4, 1) - bigNumEquals(-1, response) - }) - }) - }) -}) diff --git a/contracts/test/v0.6/SimpleReadAccessController.test.ts b/contracts/test/v0.6/SimpleReadAccessController.test.ts deleted file mode 100644 index 7b76bc38cad..00000000000 --- a/contracts/test/v0.6/SimpleReadAccessController.test.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { Contract, ContractFactory, Transaction } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' - -let personas: Personas - -let controllerFactory: ContractFactory -let controller: Contract - -before(async () => { - personas = (await getUsers()).personas - controllerFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleReadAccessController.sol:SimpleReadAccessController', - personas.Carol, - ) -}) - -describe('SimpleReadAccessController', () => { - beforeEach(async () => { - controller = await controllerFactory.connect(personas.Carol).deploy() - }) - - it('has a limited public interface [ @skip-coverage ]', async () => { - publicAbi(controller, [ - 'hasAccess', - 'addAccess', - 'disableAccessCheck', - 'enableAccessCheck', - 'removeAccess', - 'checkEnabled', - // Owned - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#constructor', () => { - it('defaults checkEnabled to true', async () => { - assert(await controller.checkEnabled()) - }) - }) - - describe('#hasAccess', () => { - it('allows unauthorized calls originating from the same account', async () => { - assert.isTrue( - await controller - .connect(personas.Eddy) - .hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('blocks unauthorized calls originating from different accounts', async () => { - assert.isFalse( - await controller - .connect(personas.Carol) - .hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - assert.isFalse( - await controller - .connect(personas.Eddy) - .hasAccess(await personas.Carol.getAddress(), '0x00'), - ) - }) - }) - - describe('#addAccess', () => { - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller - .connect(personas.Eddy) - .addAccess(await personas.Eddy.getAddress()), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - assert.isFalse( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - tx = await controller.addAccess(await personas.Eddy.getAddress()) - }) - - it('adds the address to the controller', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - await expect(tx) - .to.emit(controller, 'AddedAccess') - .withArgs(await personas.Eddy.getAddress()) - }) - - describe('when called twice', () => { - it('does not emit a log', async () => { - const tx2 = await controller.addAccess( - await personas.Eddy.getAddress(), - ) - const receipt = await tx2.wait() - assert.equal(receipt.events?.length, 0) - }) - }) - }) - }) - - describe('#removeAccess', () => { - beforeEach(async () => { - await controller.addAccess(await personas.Eddy.getAddress()) - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller - .connect(personas.Eddy) - .removeAccess(await personas.Eddy.getAddress()), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - tx = await controller.removeAccess(await personas.Eddy.getAddress()) - }) - - it('removes the address from the controller', async () => { - assert.isFalse( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - await expect(tx) - .to.emit(controller, 'RemovedAccess') - .withArgs(await personas.Eddy.getAddress()) - }) - - describe('when called twice', () => { - it('does not emit a log', async () => { - const tx2 = await controller.removeAccess( - await personas.Eddy.getAddress(), - ) - const receipt = await tx2.wait() - assert.equal(receipt.events?.length, 0) - }) - }) - }) - }) - - describe('#disableAccessCheck', () => { - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller.connect(personas.Eddy).disableAccessCheck(), - ).to.be.revertedWith('Only callable by owner') - assert.isTrue(await controller.checkEnabled()) - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - await controller.addAccess(await personas.Eddy.getAddress()) - tx = await controller.disableAccessCheck() - }) - - it('sets checkEnabled to false', async () => { - assert.isFalse(await controller.checkEnabled()) - }) - - it('allows users with access', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('allows users without access', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Ned.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - await expect(tx).to.emit(controller, 'CheckAccessDisabled') - }) - - describe('when called twice', () => { - it('does not emit a log', async () => { - const tx2 = await controller.disableAccessCheck() - const receipt = await tx2.wait() - assert.equal(receipt.events?.length, 0) - }) - }) - }) - }) - - describe('#enableAccessCheck', () => { - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller.connect(personas.Eddy).enableAccessCheck(), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - await controller.disableAccessCheck() - await controller.addAccess(await personas.Eddy.getAddress()) - tx = await controller.enableAccessCheck() - }) - - it('allows users with access', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('does not allow users without access', async () => { - assert.isFalse( - await controller.hasAccess(await personas.Ned.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - expect(tx).to.emit(controller, 'CheckAccessEnabled') - }) - - describe('when called twice', () => { - it('does not emit a log', async () => { - const tx2 = await controller.enableAccessCheck() - const receipt = await tx2.wait() - assert.equal(receipt.events?.length, 0) - }) - }) - }) - }) -}) diff --git a/contracts/test/v0.6/SimpleWriteAccessController.test.ts b/contracts/test/v0.6/SimpleWriteAccessController.test.ts deleted file mode 100644 index ae6c1691f91..00000000000 --- a/contracts/test/v0.6/SimpleWriteAccessController.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { Contract, ContractFactory, Transaction } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' - -let personas: Personas - -let controllerFactory: ContractFactory -let controller: Contract - -before(async () => { - personas = (await getUsers()).personas - controllerFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleWriteAccessController.sol:SimpleWriteAccessController', - personas.Carol, - ) -}) - -describe('SimpleWriteAccessController', () => { - beforeEach(async () => { - controller = await controllerFactory.connect(personas.Carol).deploy() - }) - - it('has a limited public interface [ @skip-coverage ]', async () => { - publicAbi(controller, [ - 'hasAccess', - 'addAccess', - 'disableAccessCheck', - 'enableAccessCheck', - 'removeAccess', - 'checkEnabled', - // Owned - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#constructor', () => { - it('defaults checkEnabled to true', async () => { - assert(await controller.checkEnabled()) - }) - }) - - describe('#hasAccess', () => { - it('allows unauthorized calls originating from the same account', async () => { - assert.isFalse( - await controller - .connect(personas.Eddy) - .hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('blocks unauthorized calls originating from different accounts', async () => { - assert.isFalse( - await controller - .connect(personas.Carol) - .hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - assert.isFalse( - await controller - .connect(personas.Eddy) - .hasAccess(await personas.Carol.getAddress(), '0x00'), - ) - }) - }) - - describe('#addAccess', () => { - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller - .connect(personas.Eddy) - .addAccess(await personas.Eddy.getAddress()), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - assert.isFalse( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - tx = await controller.addAccess(await personas.Eddy.getAddress()) - }) - - it('adds the address to the controller', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - expect(tx) - .to.emit(controller, 'AddedAccess') - .withArgs(await personas.Eddy.getAddress()) - }) - }) - }) - - describe('#removeAccess', () => { - beforeEach(async () => { - await controller.addAccess(await personas.Eddy.getAddress()) - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller - .connect(personas.Eddy) - .removeAccess(await personas.Eddy.getAddress()), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - tx = await controller.removeAccess(await personas.Eddy.getAddress()) - }) - - it('removes the address from the controller', async () => { - assert.isFalse( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - expect(tx) - .to.emit(controller, 'RemovedAccess') - .withArgs(await personas.Eddy.getAddress()) - }) - }) - }) - - describe('#disableAccessCheck', () => { - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller.connect(personas.Eddy).disableAccessCheck(), - ).to.be.revertedWith('Only callable by owner') - assert.isTrue(await controller.checkEnabled()) - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - await controller.addAccess(await personas.Eddy.getAddress()) - tx = await controller.disableAccessCheck() - }) - - it('sets checkEnabled to false', async () => { - assert.isFalse(await controller.checkEnabled()) - }) - - it('allows users with access', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('allows users without access', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Ned.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - await expect(tx).to.emit(controller, 'CheckAccessDisabled') - }) - }) - }) - - describe('#enableAccessCheck', () => { - describe('when called by a non-owner', () => { - it('reverts', async () => { - await expect( - controller.connect(personas.Eddy).enableAccessCheck(), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('when called by the owner', () => { - let tx: Transaction - beforeEach(async () => { - await controller.disableAccessCheck() - await controller.addAccess(await personas.Eddy.getAddress()) - tx = await controller.enableAccessCheck() - }) - - it('allows users with access', async () => { - assert.isTrue( - await controller.hasAccess(await personas.Eddy.getAddress(), '0x00'), - ) - }) - - it('does not allow users without access', async () => { - assert.isFalse( - await controller.hasAccess(await personas.Ned.getAddress(), '0x00'), - ) - }) - - it('announces the change via a log', async () => { - await expect(tx).to.emit(controller, 'CheckAccessEnabled') - }) - }) - }) -}) diff --git a/contracts/test/v0.6/VRFD20.test.ts b/contracts/test/v0.6/VRFD20.test.ts deleted file mode 100644 index 77141be6230..00000000000 --- a/contracts/test/v0.6/VRFD20.test.ts +++ /dev/null @@ -1,303 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { - BigNumber, - constants, - Contract, - ContractFactory, - ContractTransaction, -} from 'ethers' -import { getUsers, Personas, Roles } from '../test-helpers/setup' -import { - evmWordToAddress, - getLog, - publicAbi, - toBytes32String, - toWei, - numToBytes32, - getLogs, -} from '../test-helpers/helpers' - -let roles: Roles -let personas: Personas -let linkTokenFactory: ContractFactory -let vrfCoordinatorMockFactory: ContractFactory -let vrfD20Factory: ContractFactory - -before(async () => { - const users = await getUsers() - - roles = users.roles - personas = users.personas - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) - vrfCoordinatorMockFactory = await ethers.getContractFactory( - 'src/v0.6/tests/VRFCoordinatorMock.sol:VRFCoordinatorMock', - roles.defaultAccount, - ) - vrfD20Factory = await ethers.getContractFactory( - 'src/v0.6/examples/VRFD20.sol:VRFD20', - roles.defaultAccount, - ) -}) - -describe('VRFD20', () => { - const deposit = toWei('1') - const fee = toWei('0.1') - const keyHash = toBytes32String('keyHash') - - let link: Contract - let vrfCoordinator: Contract - let vrfD20: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - vrfCoordinator = await vrfCoordinatorMockFactory - .connect(roles.defaultAccount) - .deploy(link.address) - vrfD20 = await vrfD20Factory - .connect(roles.defaultAccount) - .deploy(vrfCoordinator.address, link.address, keyHash, fee) - await link.transfer(vrfD20.address, deposit) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(vrfD20, [ - // Owned - 'acceptOwnership', - 'owner', - 'transferOwnership', - //VRFConsumerBase - 'rawFulfillRandomness', - // VRFD20 - 'rollDice', - 'house', - 'withdrawLINK', - 'keyHash', - 'fee', - 'setKeyHash', - 'setFee', - ]) - }) - - describe('#withdrawLINK', () => { - describe('failure', () => { - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20 - .connect(roles.stranger) - .withdrawLINK(await roles.stranger.getAddress(), deposit), - ).to.be.revertedWith('Only callable by owner') - }) - - it('reverts when not enough LINK in the contract', async () => { - const withdrawAmount = deposit.mul(2) - await expect( - vrfD20 - .connect(roles.defaultAccount) - .withdrawLINK( - await roles.defaultAccount.getAddress(), - withdrawAmount, - ), - ).to.be.reverted - }) - }) - - describe('success', () => { - it('withdraws LINK', async () => { - const startingAmount = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - const expectedAmount = BigNumber.from(startingAmount).add(deposit) - await vrfD20 - .connect(roles.defaultAccount) - .withdrawLINK(await roles.defaultAccount.getAddress(), deposit) - const actualAmount = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - assert.equal(actualAmount.toString(), expectedAmount.toString()) - }) - }) - }) - - describe('#setKeyHash', () => { - const newHash = toBytes32String('newhash') - - describe('failure', () => { - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20.connect(roles.stranger).setKeyHash(newHash), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('success', () => { - it('sets the key hash', async () => { - await vrfD20.setKeyHash(newHash) - const actualHash = await vrfD20.keyHash() - assert.equal(actualHash, newHash) - }) - }) - }) - - describe('#setFee', () => { - const newFee = 1234 - - describe('failure', () => { - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20.connect(roles.stranger).setFee(newFee), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('success', () => { - it('sets the fee', async () => { - await vrfD20.setFee(newFee) - const actualFee = await vrfD20.fee() - assert.equal(actualFee.toString(), newFee.toString()) - }) - }) - }) - - describe('#house', () => { - describe('failure', () => { - it('reverts when dice not rolled', async () => { - await expect( - vrfD20.house(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Dice not rolled') - }) - - it('reverts when dice roll is in progress', async () => { - await vrfD20.rollDice(await personas.Nancy.getAddress()) - await expect( - vrfD20.house(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Roll in progress') - }) - }) - - describe('success', () => { - it('returns the correct house', async () => { - const randomness = 98765 - const expectedHouse = 'Martell' - const tx = await vrfD20.rollDice(await personas.Nancy.getAddress()) - const log = await getLog(tx, 3) - const eventRequestId = log?.topics?.[1] - await vrfCoordinator.callBackWithRandomness( - eventRequestId, - randomness, - vrfD20.address, - ) - const response = await vrfD20.house(await personas.Nancy.getAddress()) - assert.equal(response.toString(), expectedHouse) - }) - }) - }) - - describe('#rollDice', () => { - describe('success', () => { - let tx: ContractTransaction - beforeEach(async () => { - tx = await vrfD20.rollDice(await personas.Nancy.getAddress()) - }) - - it('emits a RandomnessRequest event from the VRFCoordinator', async () => { - const log = await getLog(tx, 2) - const topics = log?.topics - assert.equal(evmWordToAddress(topics?.[1]), vrfD20.address) - assert.equal(topics?.[2], keyHash) - assert.equal(topics?.[3], constants.HashZero) - }) - }) - - describe('failure', () => { - it('reverts when LINK balance is zero', async () => { - const vrfD202 = await vrfD20Factory - .connect(roles.defaultAccount) - .deploy(vrfCoordinator.address, link.address, keyHash, fee) - await expect( - vrfD202.rollDice(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Not enough LINK to pay fee') - }) - - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20 - .connect(roles.stranger) - .rollDice(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Only callable by owner') - }) - - it('reverts when the roller rolls more than once', async () => { - await vrfD20.rollDice(await personas.Nancy.getAddress()) - await expect( - vrfD20.rollDice(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Already rolled') - }) - }) - }) - - describe('#fulfillRandomness', () => { - const randomness = 98765 - const expectedModResult = (randomness % 20) + 1 - const expectedHouse = 'Martell' - let eventRequestId: string - beforeEach(async () => { - const tx = await vrfD20.rollDice(await personas.Nancy.getAddress()) - const log = await getLog(tx, 3) - eventRequestId = log?.topics?.[1] - }) - - describe('success', () => { - let tx: ContractTransaction - beforeEach(async () => { - tx = await vrfCoordinator.callBackWithRandomness( - eventRequestId, - randomness, - vrfD20.address, - ) - }) - - it('emits a DiceLanded event', async () => { - const log = await getLog(tx, 0) - assert.equal(log?.topics[1], eventRequestId) - assert.equal(log?.topics[2], numToBytes32(expectedModResult)) - }) - - it('sets the correct dice roll result', async () => { - const response = await vrfD20.house(await personas.Nancy.getAddress()) - assert.equal(response.toString(), expectedHouse) - }) - - it('allows someone else to roll', async () => { - const secondRandomness = 55555 - tx = await vrfD20.rollDice(await personas.Ned.getAddress()) - const log = await getLog(tx, 3) - eventRequestId = log?.topics?.[1] - tx = await vrfCoordinator.callBackWithRandomness( - eventRequestId, - secondRandomness, - vrfD20.address, - ) - }) - }) - - describe('failure', () => { - it('does not fulfill when fulfilled by the wrong VRFcoordinator', async () => { - const vrfCoordinator2 = await vrfCoordinatorMockFactory - .connect(roles.defaultAccount) - .deploy(link.address) - - const tx = await vrfCoordinator2.callBackWithRandomness( - eventRequestId, - randomness, - vrfD20.address, - ) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/AggregatorProxy.test.ts b/contracts/test/v0.7/AggregatorProxy.test.ts deleted file mode 100644 index 6e8ee41983d..00000000000 --- a/contracts/test/v0.7/AggregatorProxy.test.ts +++ /dev/null @@ -1,743 +0,0 @@ -import { ethers } from 'hardhat' -import { - increaseTimeBy, - numToBytes32, - publicAbi, - toWei, -} from '../test-helpers/helpers' -import { assert } from 'chai' -import { BigNumber, constants, Contract, ContractFactory, Signer } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { bigNumEquals, evmRevert } from '../test-helpers/matchers' - -let personas: Personas -let defaultAccount: Signer - -let linkTokenFactory: ContractFactory -let aggregatorFactory: ContractFactory -let historicAggregatorFactory: ContractFactory -let aggregatorFacadeFactory: ContractFactory -let aggregatorProxyFactory: ContractFactory -let fluxAggregatorFactory: ContractFactory -let reverterFactory: ContractFactory - -before(async () => { - const users = await getUsers() - - personas = users.personas - defaultAccount = users.roles.defaultAccount - - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - defaultAccount, - ) - aggregatorFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MockV3Aggregator.sol:MockV3Aggregator', - defaultAccount, - ) - historicAggregatorFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MockV2Aggregator.sol:MockV2Aggregator', - defaultAccount, - ) - aggregatorFacadeFactory = await ethers.getContractFactory( - 'src/v0.6/AggregatorFacade.sol:AggregatorFacade', - defaultAccount, - ) - historicAggregatorFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MockV2Aggregator.sol:MockV2Aggregator', - defaultAccount, - ) - aggregatorFacadeFactory = await ethers.getContractFactory( - 'src/v0.6/AggregatorFacade.sol:AggregatorFacade', - defaultAccount, - ) - aggregatorProxyFactory = await ethers.getContractFactory( - 'src/v0.7/dev/AggregatorProxy.sol:AggregatorProxy', - defaultAccount, - ) - fluxAggregatorFactory = await ethers.getContractFactory( - 'src/v0.6/FluxAggregator.sol:FluxAggregator', - defaultAccount, - ) - reverterFactory = await ethers.getContractFactory( - 'src/v0.6/tests/Reverter.sol:Reverter', - defaultAccount, - ) -}) - -describe('AggregatorProxy', () => { - const deposit = toWei('100') - const response = numToBytes32(54321) - const response2 = numToBytes32(67890) - const decimals = 18 - const phaseBase = BigNumber.from(2).pow(64) - - let link: Contract - let aggregator: Contract - let aggregator2: Contract - let historicAggregator: Contract - let proxy: Contract - let flux: Contract - let reverter: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(defaultAccount).deploy() - aggregator = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response) - await link.transfer(aggregator.address, deposit) - proxy = await aggregatorProxyFactory - .connect(defaultAccount) - .deploy(aggregator.address) - const emptyAddress = constants.AddressZero - flux = await fluxAggregatorFactory - .connect(personas.Carol) - .deploy(link.address, 0, 0, emptyAddress, 0, 0, 18, 'TEST / LINK') - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(proxy, [ - 'aggregator', - 'confirmAggregator', - 'decimals', - 'description', - 'getAnswer', - 'getRoundData', - 'getTimestamp', - 'latestAnswer', - 'latestRound', - 'latestRoundData', - 'latestTimestamp', - 'phaseAggregators', - 'phaseId', - 'proposeAggregator', - 'proposedAggregator', - 'proposedGetRoundData', - 'proposedLatestRoundData', - 'version', - // Ownable methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('constructor', () => { - it('sets the proxy phase and aggregator', async () => { - bigNumEquals(1, await proxy.phaseId()) - assert.equal(aggregator.address, await proxy.phaseAggregators(1)) - }) - }) - - describe('#latestRound', () => { - it('pulls the rate from the aggregator', async () => { - bigNumEquals(phaseBase.add(1), await proxy.latestRound()) - }) - }) - - describe('#latestAnswer', () => { - it('pulls the rate from the aggregator', async () => { - bigNumEquals(response, await proxy.latestAnswer()) - const latestRound = await proxy.latestRound() - bigNumEquals(response, await proxy.getAnswer(latestRound)) - }) - - describe('after being updated to another contract', () => { - beforeEach(async () => { - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - await link.transfer(aggregator2.address, deposit) - bigNumEquals(response2, await aggregator2.latestAnswer()) - - await proxy.proposeAggregator(aggregator2.address) - await proxy.confirmAggregator(aggregator2.address) - }) - - it('pulls the rate from the new aggregator', async () => { - bigNumEquals(response2, await proxy.latestAnswer()) - const latestRound = await proxy.latestRound() - bigNumEquals(response2, await proxy.getAnswer(latestRound)) - }) - }) - - describe('when the relevant info is not available', () => { - beforeEach(async () => { - await proxy.proposeAggregator(flux.address) - await proxy.confirmAggregator(flux.address) - }) - - it('does not revert when called with a non existent ID', async () => { - const actual = await proxy.latestAnswer() - bigNumEquals(0, actual) - }) - }) - }) - - describe('#getAnswer', () => { - describe('when the relevant round is not available', () => { - beforeEach(async () => { - await proxy.proposeAggregator(flux.address) - await proxy.confirmAggregator(flux.address) - }) - - it('does not revert when called with a non existent ID', async () => { - const proxyId = phaseBase.mul(await proxy.phaseId()).add(1) - const actual = await proxy.getAnswer(proxyId) - bigNumEquals(0, actual) - }) - }) - - describe('when the answer reverts in a non-predicted way', () => { - it('reverts', async () => { - reverter = await reverterFactory.connect(defaultAccount).deploy() - await proxy.proposeAggregator(reverter.address) - await proxy.confirmAggregator(reverter.address) - assert.equal(reverter.address, await proxy.aggregator()) - - const proxyId = phaseBase.mul(await proxy.phaseId()) - - await evmRevert(proxy.getAnswer(proxyId), 'Raised by Reverter.sol') - }) - }) - - describe('after being updated to another contract', () => { - let preUpdateRoundId: BigNumber - let preUpdateAnswer: BigNumber - - beforeEach(async () => { - preUpdateRoundId = await proxy.latestRound() - preUpdateAnswer = await proxy.latestAnswer() - - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - await link.transfer(aggregator2.address, deposit) - bigNumEquals(response2, await aggregator2.latestAnswer()) - - await proxy.proposeAggregator(aggregator2.address) - await proxy.confirmAggregator(aggregator2.address) - }) - - it('reports answers for previous phases', async () => { - const actualAnswer = await proxy.getAnswer(preUpdateRoundId) - bigNumEquals(preUpdateAnswer, actualAnswer) - }) - }) - - describe('when the relevant info is not available', () => { - it('returns 0', async () => { - const actual = await proxy.getAnswer(phaseBase.mul(777)) - bigNumEquals(0, actual) - }) - }) - - describe('when the round ID is too large', () => { - const overflowRoundId = BigNumber.from(2) - .pow(255) - .add(phaseBase) // get the original phase - .add(1) // get the original round - it('returns 0', async () => { - const actual = await proxy.getTimestamp(overflowRoundId) - bigNumEquals(0, actual) - }) - }) - }) - - describe('#getTimestamp', () => { - describe('when the relevant round is not available', () => { - beforeEach(async () => { - await proxy.proposeAggregator(flux.address) - await proxy.confirmAggregator(flux.address) - }) - - it('does not revert when called with a non existent ID', async () => { - const proxyId = phaseBase.mul(await proxy.phaseId()).add(1) - const actual = await proxy.getTimestamp(proxyId) - bigNumEquals(0, actual) - }) - }) - - describe('when the relevant info is not available', () => { - it('returns 0', async () => { - const actual = await proxy.getTimestamp(phaseBase.mul(777)) - bigNumEquals(0, actual) - }) - }) - - describe('when the round ID is too large', () => { - const overflowRoundId = BigNumber.from(2) - .pow(255) - .add(phaseBase) // get the original phase - .add(1) // get the original round - - it('returns 0', async () => { - const actual = await proxy.getTimestamp(overflowRoundId) - bigNumEquals(0, actual) - }) - }) - }) - - describe('#latestTimestamp', () => { - beforeEach(async () => { - const height = await aggregator.latestTimestamp() - assert.notEqual('0', height.toString()) - }) - - it('pulls the timestamp from the aggregator', async () => { - bigNumEquals( - await aggregator.latestTimestamp(), - await proxy.latestTimestamp(), - ) - const latestRound = await proxy.latestRound() - bigNumEquals( - await aggregator.latestTimestamp(), - await proxy.getTimestamp(latestRound), - ) - }) - - describe('after being updated to another contract', () => { - beforeEach(async () => { - await increaseTimeBy(30, ethers.provider) - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - - const height2 = await aggregator2.latestTimestamp() - assert.notEqual('0', height2.toString()) - - const height1 = await aggregator.latestTimestamp() - assert.notEqual( - height1.toString(), - height2.toString(), - 'Height1 and Height2 should not be equal', - ) - - await proxy.proposeAggregator(aggregator2.address) - await proxy.confirmAggregator(aggregator2.address) - }) - - it('pulls the timestamp from the new aggregator', async () => { - bigNumEquals( - await aggregator2.latestTimestamp(), - await proxy.latestTimestamp(), - ) - const latestRound = await proxy.latestRound() - bigNumEquals( - await aggregator2.latestTimestamp(), - await proxy.getTimestamp(latestRound), - ) - }) - }) - }) - - describe('#getRoundData', () => { - describe('when pointed at a Historic Aggregator', () => { - beforeEach(async () => { - historicAggregator = await historicAggregatorFactory - .connect(defaultAccount) - .deploy(response2) - await proxy.proposeAggregator(historicAggregator.address) - await proxy.confirmAggregator(historicAggregator.address) - }) - - it('reverts', async () => { - const latestRoundId = await historicAggregator.latestRound() - await evmRevert(proxy.getRoundData(latestRoundId)) - }) - - describe('when pointed at an Aggregator Facade', () => { - beforeEach(async () => { - const facade = await aggregatorFacadeFactory - .connect(defaultAccount) - .deploy(aggregator.address, 18, 'LINK/USD: Aggregator Facade') - await proxy.proposeAggregator(facade.address) - await proxy.confirmAggregator(facade.address) - }) - - it('works for a valid roundId', async () => { - const aggId = await aggregator.latestRound() - const phaseId = phaseBase.mul(await proxy.phaseId()) - const proxyId = phaseId.add(aggId) - - const round = await proxy.getRoundData(proxyId) - bigNumEquals(proxyId, round.id) - bigNumEquals(response, round.answer) - const nowSeconds = new Date().valueOf() / 1000 - assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120) - bigNumEquals(round.updatedAt, round.startedAt) - bigNumEquals(proxyId, round.answeredInRound) - }) - }) - }) - - describe('when pointed at a FluxAggregator', () => { - beforeEach(async () => { - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - - await proxy.proposeAggregator(aggregator2.address) - await proxy.confirmAggregator(aggregator2.address) - }) - - it('works for a valid round ID', async () => { - const aggId = phaseBase.sub(2) - await aggregator2 - .connect(personas.Carol) - .updateRoundData(aggId, response2, 77, 42) - - const phaseId = phaseBase.mul(await proxy.phaseId()) - const proxyId = phaseId.add(aggId) - - const round = await proxy.getRoundData(proxyId) - bigNumEquals(proxyId, round.id) - bigNumEquals(response2, round.answer) - bigNumEquals(42, round.startedAt) - bigNumEquals(77, round.updatedAt) - bigNumEquals(proxyId, round.answeredInRound) - }) - }) - - it('reads round ID of a previous phase', async () => { - const oldphaseId = phaseBase.mul(await proxy.phaseId()) - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - - await proxy.proposeAggregator(aggregator2.address) - await proxy.confirmAggregator(aggregator2.address) - - const aggId = await aggregator.latestRound() - const proxyId = oldphaseId.add(aggId) - - const round = await proxy.getRoundData(proxyId) - bigNumEquals(proxyId, round.id) - bigNumEquals(response, round.answer) - - const nowSeconds = new Date().valueOf() / 1000 - assert.isAbove(round.startedAt.toNumber(), nowSeconds - 120) - bigNumEquals(round.startedAt, round.updatedAt) - bigNumEquals(proxyId, round.answeredInRound) - }) - }) - - describe('#latestRoundData', () => { - describe('when pointed at a Historic Aggregator', () => { - beforeEach(async () => { - historicAggregator = await historicAggregatorFactory - .connect(defaultAccount) - .deploy(response2) - await proxy.proposeAggregator(historicAggregator.address) - await proxy.confirmAggregator(historicAggregator.address) - }) - - it('reverts', async () => { - await evmRevert(proxy.latestRoundData()) - }) - - describe('when pointed at an Aggregator Facade', () => { - beforeEach(async () => { - const facade = await aggregatorFacadeFactory - .connect(defaultAccount) - .deploy( - historicAggregator.address, - 17, - 'DOGE/ZWL: Aggregator Facade', - ) - await proxy.proposeAggregator(facade.address) - await proxy.confirmAggregator(facade.address) - }) - - it('does not revert', async () => { - const aggId = await historicAggregator.latestRound() - const phaseId = phaseBase.mul(await proxy.phaseId()) - const proxyId = phaseId.add(aggId) - - const round = await proxy.latestRoundData() - bigNumEquals(proxyId, round.id) - bigNumEquals(response2, round.answer) - const nowSeconds = new Date().valueOf() / 1000 - assert.isAbove(round.updatedAt.toNumber(), nowSeconds - 120) - bigNumEquals(round.updatedAt, round.startedAt) - bigNumEquals(proxyId, round.answeredInRound) - }) - - it('uses the decimals set in the constructor', async () => { - bigNumEquals(17, await proxy.decimals()) - }) - - it('uses the description set in the constructor', async () => { - assert.equal('DOGE/ZWL: Aggregator Facade', await proxy.description()) - }) - - it('sets the version to 2', async () => { - bigNumEquals(2, await proxy.version()) - }) - }) - }) - - describe('when pointed at a FluxAggregator', () => { - beforeEach(async () => { - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - - await proxy.proposeAggregator(aggregator2.address) - await proxy.confirmAggregator(aggregator2.address) - }) - - it('does not revert', async () => { - const aggId = phaseBase.sub(2) - await aggregator2 - .connect(personas.Carol) - .updateRoundData(aggId, response2, 77, 42) - - const phaseId = phaseBase.mul(await proxy.phaseId()) - const proxyId = phaseId.add(aggId) - - const round = await proxy.latestRoundData() - bigNumEquals(proxyId, round.id) - bigNumEquals(response2, round.answer) - bigNumEquals(42, round.startedAt) - bigNumEquals(77, round.updatedAt) - bigNumEquals(proxyId, round.answeredInRound) - }) - - it('uses the decimals of the aggregator', async () => { - bigNumEquals(18, await proxy.decimals()) - }) - - it('uses the description of the aggregator', async () => { - assert.equal( - 'v0.6/tests/MockV3Aggregator.sol', - await proxy.description(), - ) - }) - - it('uses the version of the aggregator', async () => { - bigNumEquals(0, await proxy.version()) - }) - }) - }) - - describe('#proposeAggregator', () => { - beforeEach(async () => { - await proxy.transferOwnership(await personas.Carol.getAddress()) - await proxy.connect(personas.Carol).acceptOwnership() - - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, 1) - - assert.equal(aggregator.address, await proxy.aggregator()) - }) - - describe('when called by the owner', () => { - it('sets the address of the proposed aggregator', async () => { - await proxy - .connect(personas.Carol) - .proposeAggregator(aggregator2.address) - - assert.equal(aggregator2.address, await proxy.proposedAggregator()) - }) - - it('emits an AggregatorProposed event', async () => { - const tx = await proxy - .connect(personas.Carol) - .proposeAggregator(aggregator2.address) - const receipt = await tx.wait() - const eventLog = receipt?.events - - assert.equal(eventLog?.length, 1) - assert.equal(eventLog?.[0].event, 'AggregatorProposed') - assert.equal(eventLog?.[0].args?.[0], aggregator.address) - assert.equal(eventLog?.[0].args?.[1], aggregator2.address) - }) - }) - - describe('when called by a non-owner', () => { - it('does not update', async () => { - await evmRevert( - proxy.connect(personas.Neil).proposeAggregator(aggregator2.address), - 'Only callable by owner', - ) - - assert.equal(aggregator.address, await proxy.aggregator()) - }) - }) - }) - - describe('#confirmAggregator', () => { - beforeEach(async () => { - await proxy.transferOwnership(await personas.Carol.getAddress()) - await proxy.connect(personas.Carol).acceptOwnership() - - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, 1) - - assert.equal(aggregator.address, await proxy.aggregator()) - }) - - describe('when called by the owner', () => { - beforeEach(async () => { - await proxy - .connect(personas.Carol) - .proposeAggregator(aggregator2.address) - }) - - it('sets the address of the new aggregator', async () => { - await proxy - .connect(personas.Carol) - .confirmAggregator(aggregator2.address) - - assert.equal(aggregator2.address, await proxy.aggregator()) - }) - - it('increases the phase', async () => { - bigNumEquals(1, await proxy.phaseId()) - - await proxy - .connect(personas.Carol) - .confirmAggregator(aggregator2.address) - - bigNumEquals(2, await proxy.phaseId()) - }) - - it('increases the round ID', async () => { - bigNumEquals(phaseBase.add(1), await proxy.latestRound()) - - await proxy - .connect(personas.Carol) - .confirmAggregator(aggregator2.address) - - bigNumEquals(phaseBase.mul(2).add(1), await proxy.latestRound()) - }) - - it('sets the proxy phase and aggregator', async () => { - assert.equal( - '0x0000000000000000000000000000000000000000', - await proxy.phaseAggregators(2), - ) - - await proxy - .connect(personas.Carol) - .confirmAggregator(aggregator2.address) - - assert.equal(aggregator2.address, await proxy.phaseAggregators(2)) - }) - - it('emits an AggregatorConfirmed event', async () => { - const tx = await proxy - .connect(personas.Carol) - .confirmAggregator(aggregator2.address) - const receipt = await tx.wait() - const eventLog = receipt?.events - - assert.equal(eventLog?.length, 1) - assert.equal(eventLog?.[0].event, 'AggregatorConfirmed') - assert.equal(eventLog?.[0].args?.[0], aggregator.address) - assert.equal(eventLog?.[0].args?.[1], aggregator2.address) - }) - }) - - describe('when called by a non-owner', () => { - beforeEach(async () => { - await proxy - .connect(personas.Carol) - .proposeAggregator(aggregator2.address) - }) - - it('does not update', async () => { - await evmRevert( - proxy.connect(personas.Neil).confirmAggregator(aggregator2.address), - 'Only callable by owner', - ) - - assert.equal(aggregator.address, await proxy.aggregator()) - }) - }) - }) - - describe('#proposedGetRoundData', () => { - beforeEach(async () => { - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - }) - - describe('when an aggregator has been proposed', () => { - beforeEach(async () => { - await proxy - .connect(defaultAccount) - .proposeAggregator(aggregator2.address) - assert.equal(await proxy.proposedAggregator(), aggregator2.address) - }) - - it('returns the data for the proposed aggregator', async () => { - const roundId = await aggregator2.latestRound() - const round = await proxy.proposedGetRoundData(roundId) - bigNumEquals(roundId, round.id) - bigNumEquals(response2, round.answer) - }) - - describe('after the aggregator has been confirmed', () => { - beforeEach(async () => { - await proxy - .connect(defaultAccount) - .confirmAggregator(aggregator2.address) - assert.equal(await proxy.aggregator(), aggregator2.address) - }) - - it('reverts', async () => { - const roundId = await aggregator2.latestRound() - await evmRevert( - proxy.proposedGetRoundData(roundId), - 'No proposed aggregator present', - ) - }) - }) - }) - }) - - describe('#proposedLatestRoundData', () => { - beforeEach(async () => { - aggregator2 = await aggregatorFactory - .connect(defaultAccount) - .deploy(decimals, response2) - }) - - describe('when an aggregator has been proposed', () => { - beforeEach(async () => { - await proxy - .connect(defaultAccount) - .proposeAggregator(aggregator2.address) - assert.equal(await proxy.proposedAggregator(), aggregator2.address) - }) - - it('returns the data for the proposed aggregator', async () => { - const roundId = await aggregator2.latestRound() - const round = await proxy.proposedLatestRoundData() - bigNumEquals(roundId, round.id) - bigNumEquals(response2, round.answer) - }) - - describe('after the aggregator has been confirmed', () => { - beforeEach(async () => { - await proxy - .connect(defaultAccount) - .confirmAggregator(aggregator2.address) - assert.equal(await proxy.aggregator(), aggregator2.address) - }) - - it('reverts', async () => { - await evmRevert( - proxy.proposedLatestRoundData(), - 'No proposed aggregator present', - ) - }) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/AuthorizedForwarder.test.ts b/contracts/test/v0.7/AuthorizedForwarder.test.ts deleted file mode 100644 index e1fa2f1f708..00000000000 --- a/contracts/test/v0.7/AuthorizedForwarder.test.ts +++ /dev/null @@ -1,444 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { Contract, ContractFactory, ContractReceipt } from 'ethers' -import { getUsers, Roles } from '../test-helpers/setup' -import { evmRevert } from '../test-helpers/matchers' - -let getterSetterFactory: ContractFactory -let forwarderFactory: ContractFactory -let brokenFactory: ContractFactory -let linkTokenFactory: ContractFactory - -let roles: Roles -const zeroAddress = ethers.constants.AddressZero - -before(async () => { - const users = await getUsers() - - roles = users.roles - getterSetterFactory = await ethers.getContractFactory( - 'src/v0.4/tests/GetterSetter.sol:GetterSetter', - roles.defaultAccount, - ) - brokenFactory = await ethers.getContractFactory( - 'src/v0.8/tests/Broken.sol:Broken', - roles.defaultAccount, - ) - forwarderFactory = await ethers.getContractFactory( - 'src/v0.7/AuthorizedForwarder.sol:AuthorizedForwarder', - roles.defaultAccount, - ) - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) -}) - -describe('AuthorizedForwarder', () => { - let link: Contract - let forwarder: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - forwarder = await forwarderFactory - .connect(roles.defaultAccount) - .deploy( - link.address, - await roles.defaultAccount.getAddress(), - zeroAddress, - '0x', - ) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(forwarder, [ - 'forward', - 'getAuthorizedSenders', - 'getChainlinkToken', - 'isAuthorizedSender', - 'ownerForward', - 'setAuthorizedSenders', - 'transferOwnershipWithMessage', - 'typeAndVersion', - // ConfirmedOwner - 'transferOwnership', - 'acceptOwnership', - 'owner', - ]) - }) - - describe('#typeAndVersion', () => { - it('describes the authorized forwarder', async () => { - assert.equal( - await forwarder.typeAndVersion(), - 'AuthorizedForwarder 1.0.0', - ) - }) - }) - - describe('deployment', () => { - it('sets the correct link token', async () => { - assert.equal(await forwarder.getChainlinkToken(), link.address) - }) - - it('reverts on zeroAddress value for link token', async () => { - await evmRevert( - forwarderFactory.connect(roles.defaultAccount).deploy( - zeroAddress, // Link Address - await roles.defaultAccount.getAddress(), - zeroAddress, - '0x', - ), - ) - }) - - it('sets no authorized senders', async () => { - const senders = await forwarder.getAuthorizedSenders() - assert.equal(senders.length, 0) - }) - }) - - describe('#setAuthorizedSenders', () => { - let newSenders: string[] - let receipt: ContractReceipt - describe('when called by the owner', () => { - describe('set authorized senders containing duplicate/s', () => { - beforeEach(async () => { - newSenders = [ - await roles.oracleNode1.getAddress(), - await roles.oracleNode1.getAddress(), - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - }) - it('reverts with a must not have duplicate senders message', async () => { - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .setAuthorizedSenders(newSenders), - 'Must not have duplicate senders', - ) - }) - }) - - describe('setting 3 authorized senders', () => { - beforeEach(async () => { - newSenders = [ - await roles.oracleNode1.getAddress(), - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - const tx = await forwarder - .connect(roles.defaultAccount) - .setAuthorizedSenders(newSenders) - receipt = await tx.wait() - }) - - it('adds the authorized nodes', async () => { - const authorizedSenders = await forwarder.getAuthorizedSenders() - assert.equal(newSenders.length, authorizedSenders.length) - for (let i = 0; i < authorizedSenders.length; i++) { - assert.equal(authorizedSenders[i], newSenders[i]) - } - }) - - it('emits an event', async () => { - assert.equal(receipt.events?.length, 1) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'AuthorizedSendersChanged') - const encodedSenders = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'address'], - [newSenders, await roles.defaultAccount.getAddress()], - ) - assert.equal(responseEvent?.data, encodedSenders) - }) - - it('replaces the authorized nodes', async () => { - const newSenders = await forwarder - .connect(roles.defaultAccount) - .getAuthorizedSenders() - assert.notIncludeOrderedMembers(newSenders, [ - await roles.oracleNode.getAddress(), - ]) - }) - - after(async () => { - await forwarder - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.oracleNode.getAddress()]) - }) - }) - - describe('setting 0 authorized senders', () => { - beforeEach(async () => { - newSenders = [] - }) - - it('reverts with a minimum senders message', async () => { - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .setAuthorizedSenders(newSenders), - 'Must have at least 1 sender', - ) - }) - }) - }) - - describe('when called by a non-owner', () => { - it('cannot add an authorized node', async () => { - await evmRevert( - forwarder - .connect(roles.stranger) - .setAuthorizedSenders([await roles.stranger.getAddress()]), - 'Cannot set authorized senders', - ) - }) - }) - }) - - describe('#forward', () => { - let bytes: string - let payload: string - let mock: Contract - - beforeEach(async () => { - mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() - bytes = ethers.utils.hexlify(ethers.utils.randomBytes(100)) - payload = getterSetterFactory.interface.encodeFunctionData( - getterSetterFactory.interface.getFunction('setBytes'), - [bytes], - ) - }) - - describe('when called by an unauthorized node', () => { - it('reverts', async () => { - await evmRevert( - forwarder.connect(roles.stranger).forward(mock.address, payload), - ) - }) - }) - - describe('when called by an authorized node', () => { - beforeEach(async () => { - await forwarder - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.defaultAccount.getAddress()]) - }) - - describe('when destination call reverts', () => { - let brokenMock: Contract - let brokenPayload: string - let brokenMsgPayload: string - - beforeEach(async () => { - brokenMock = await brokenFactory - .connect(roles.defaultAccount) - .deploy() - brokenMsgPayload = brokenFactory.interface.encodeFunctionData( - brokenFactory.interface.getFunction('revertWithMessage'), - ['Failure message'], - ) - - brokenPayload = brokenFactory.interface.encodeFunctionData( - brokenFactory.interface.getFunction('revertSilently'), - [], - ) - }) - - describe('when reverts with message', () => { - it('return revert message', async () => { - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .forward(brokenMock.address, brokenMsgPayload), - "reverted with reason string 'Failure message'", - ) - }) - }) - - describe('when reverts without message', () => { - it('return silent failure message', async () => { - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .forward(brokenMock.address, brokenPayload), - 'Forwarded call reverted without reason', - ) - }) - }) - }) - - describe('when sending to a non-contract address', () => { - it('reverts', async () => { - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .forward(zeroAddress, payload), - 'Must forward to a contract', - ) - }) - }) - - describe('when attempting to forward to the link token', () => { - it('reverts', async () => { - const sighash = linkTokenFactory.interface.getSighash('name') // any Link Token function - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .forward(link.address, sighash), - ) - }) - }) - - describe('when forwarding to any other address', () => { - it('forwards the data', async () => { - const tx = await forwarder - .connect(roles.defaultAccount) - .forward(mock.address, payload) - await tx.wait() - assert.equal(await mock.getBytes(), bytes) - }) - - it('perceives the message is sent by the AuthorizedForwarder', async () => { - const tx = await forwarder - .connect(roles.defaultAccount) - .forward(mock.address, payload) - await expect(tx) - .to.emit(mock, 'SetBytes') - .withArgs(forwarder.address, bytes) - }) - }) - }) - }) - - describe('#transferOwnershipWithMessage', () => { - const message = '0x42' - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - forwarder - .connect(roles.stranger) - .transferOwnershipWithMessage( - await roles.stranger.getAddress(), - message, - ), - 'Only callable by owner', - ) - }) - }) - - describe('when called by the owner', () => { - it('calls the normal ownership transfer proposal', async () => { - const tx = await forwarder - .connect(roles.defaultAccount) - .transferOwnershipWithMessage( - await roles.stranger.getAddress(), - message, - ) - const receipt = await tx.wait() - - assert.equal(receipt?.events?.[0]?.event, 'OwnershipTransferRequested') - assert.equal(receipt?.events?.[0]?.address, forwarder.address) - assert.equal( - receipt?.events?.[0]?.args?.[0], - await roles.defaultAccount.getAddress(), - ) - assert.equal( - receipt?.events?.[0]?.args?.[1], - await roles.stranger.getAddress(), - ) - }) - - it('calls the normal ownership transfer proposal', async () => { - const tx = await forwarder - .connect(roles.defaultAccount) - .transferOwnershipWithMessage( - await roles.stranger.getAddress(), - message, - ) - const receipt = await tx.wait() - - assert.equal( - receipt?.events?.[1]?.event, - 'OwnershipTransferRequestedWithMessage', - ) - assert.equal(receipt?.events?.[1]?.address, forwarder.address) - assert.equal( - receipt?.events?.[1]?.args?.[0], - await roles.defaultAccount.getAddress(), - ) - assert.equal( - receipt?.events?.[1]?.args?.[1], - await roles.stranger.getAddress(), - ) - assert.equal(receipt?.events?.[1]?.args?.[2], message) - }) - }) - }) - - describe('#ownerForward', () => { - let bytes: string - let payload: string - let mock: Contract - - beforeEach(async () => { - mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() - bytes = ethers.utils.hexlify(ethers.utils.randomBytes(100)) - payload = getterSetterFactory.interface.encodeFunctionData( - getterSetterFactory.interface.getFunction('setBytes'), - [bytes], - ) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - forwarder.connect(roles.stranger).ownerForward(mock.address, payload), - ) - }) - }) - - describe('when called by owner', () => { - describe('when attempting to forward to the link token', () => { - it('does not revert', async () => { - const sighash = linkTokenFactory.interface.getSighash('name') // any Link Token function - - await forwarder - .connect(roles.defaultAccount) - .ownerForward(link.address, sighash) - }) - }) - - describe('when forwarding to any other address', () => { - it('forwards the data', async () => { - const tx = await forwarder - .connect(roles.defaultAccount) - .ownerForward(mock.address, payload) - await tx.wait() - assert.equal(await mock.getBytes(), bytes) - }) - - it('reverts when sending to a non-contract address', async () => { - await evmRevert( - forwarder - .connect(roles.defaultAccount) - .ownerForward(zeroAddress, payload), - 'Must forward to a contract', - ) - }) - - it('perceives the message is sent by the Operator', async () => { - const tx = await forwarder - .connect(roles.defaultAccount) - .ownerForward(mock.address, payload) - await expect(tx) - .to.emit(mock, 'SetBytes') - .withArgs(forwarder.address, bytes) - }) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/Chainlink.test.ts b/contracts/test/v0.7/Chainlink.test.ts deleted file mode 100644 index 7792895934c..00000000000 --- a/contracts/test/v0.7/Chainlink.test.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi, decodeDietCBOR, hexToBuf } from '../test-helpers/helpers' -import { assert } from 'chai' -import { Contract, ContractFactory, providers, Signer } from 'ethers' -import { Roles, getUsers } from '../test-helpers/setup' -import { makeDebug } from '../test-helpers/debug' - -const debug = makeDebug('ChainlinkTestHelper') -let concreteChainlinkFactory: ContractFactory - -let roles: Roles - -before(async () => { - roles = (await getUsers()).roles - concreteChainlinkFactory = await ethers.getContractFactory( - 'src/v0.7/tests/ChainlinkTestHelper.sol:ChainlinkTestHelper', - roles.defaultAccount, - ) -}) - -describe('ChainlinkTestHelper', () => { - let ccl: Contract - let defaultAccount: Signer - - beforeEach(async () => { - defaultAccount = roles.defaultAccount - ccl = await concreteChainlinkFactory.connect(defaultAccount).deploy() - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(ccl, [ - 'add', - 'addBytes', - 'addInt', - 'addStringArray', - 'addUint', - 'closeEvent', - 'setBuffer', - ]) - }) - - async function parseCCLEvent(tx: providers.TransactionResponse) { - const receipt = await tx.wait() - const data = receipt.logs?.[0].data - const d = debug.extend('parseCCLEvent') - d('data %s', data) - return ethers.utils.defaultAbiCoder.decode(['bytes'], data ?? '') - } - - describe('#close', () => { - it('handles empty payloads', async () => { - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, {}) - }) - }) - - describe('#setBuffer', () => { - it('emits the buffer', async () => { - await ccl.setBuffer('0xA161616162') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { a: 'b' }) - }) - }) - - describe('#add', () => { - it('stores and logs keys and values', async () => { - await ccl.add('first', 'word!!') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { first: 'word!!' }) - }) - - it('handles two entries', async () => { - await ccl.add('first', 'uno') - await ccl.add('second', 'dos') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - assert.deepEqual(decoded, { - first: 'uno', - second: 'dos', - }) - }) - }) - - describe('#addBytes', () => { - it('stores and logs keys and values', async () => { - await ccl.addBytes('first', '0xaabbccddeeff') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - const expected = hexToBuf('0xaabbccddeeff') - assert.deepEqual(decoded, { first: expected }) - }) - - it('handles two entries', async () => { - await ccl.addBytes('first', '0x756E6F') - await ccl.addBytes('second', '0x646F73') - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - const expectedFirst = hexToBuf('0x756E6F') - const expectedSecond = hexToBuf('0x646F73') - assert.deepEqual(decoded, { - first: expectedFirst, - second: expectedSecond, - }) - }) - - it('handles strings', async () => { - await ccl.addBytes('first', ethers.utils.toUtf8Bytes('apple')) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - const expected = ethers.utils.toUtf8Bytes('apple') - assert.deepEqual(decoded, { first: expected }) - }) - }) - - describe('#addInt', () => { - it('stores and logs keys and values', async () => { - await ccl.addInt('first', 1) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { first: 1 }) - }) - - it('handles two entries', async () => { - await ccl.addInt('first', 1) - await ccl.addInt('second', 2) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - assert.deepEqual(decoded, { - first: 1, - second: 2, - }) - }) - }) - - describe('#addUint', () => { - it('stores and logs keys and values', async () => { - await ccl.addUint('first', 1) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { first: 1 }) - }) - - it('handles two entries', async () => { - await ccl.addUint('first', 1) - await ccl.addUint('second', 2) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - - assert.deepEqual(decoded, { - first: 1, - second: 2, - }) - }) - }) - - describe('#addStringArray', () => { - it('stores and logs keys and values', async () => { - await ccl.addStringArray('word', [ - ethers.utils.formatBytes32String('seinfeld'), - ethers.utils.formatBytes32String('"4"'), - ethers.utils.formatBytes32String('LIFE'), - ]) - const tx = await ccl.closeEvent() - const [payload] = await parseCCLEvent(tx) - const decoded = await decodeDietCBOR(payload) - assert.deepEqual(decoded, { word: ['seinfeld', '"4"', 'LIFE'] }) - }) - }) -}) diff --git a/contracts/test/v0.7/ChainlinkClient.test.ts b/contracts/test/v0.7/ChainlinkClient.test.ts deleted file mode 100644 index 198d382af79..00000000000 --- a/contracts/test/v0.7/ChainlinkClient.test.ts +++ /dev/null @@ -1,454 +0,0 @@ -import { ethers } from 'hardhat' -import { assert } from 'chai' -import { Contract, ContractFactory } from 'ethers' -import { Roles, getUsers } from '../test-helpers/setup' -import { - convertFufillParams, - decodeCCRequest, - decodeRunRequest, - RunRequest, -} from '../test-helpers/oracle' -import { decodeDietCBOR } from '../test-helpers/helpers' -import { evmRevert } from '../test-helpers/matchers' - -let concreteChainlinkClientFactory: ContractFactory -let emptyOracleFactory: ContractFactory -let getterSetterFactory: ContractFactory -let operatorFactory: ContractFactory -let linkTokenFactory: ContractFactory - -let roles: Roles - -before(async () => { - roles = (await getUsers()).roles - - concreteChainlinkClientFactory = await ethers.getContractFactory( - 'src/v0.7/tests/ChainlinkClientTestHelper.sol:ChainlinkClientTestHelper', - roles.defaultAccount, - ) - emptyOracleFactory = await ethers.getContractFactory( - 'src/v0.6/tests/EmptyOracle.sol:EmptyOracle', - roles.defaultAccount, - ) - getterSetterFactory = await ethers.getContractFactory( - 'src/v0.5/tests/GetterSetter.sol:GetterSetter', - roles.defaultAccount, - ) - operatorFactory = await ethers.getContractFactory( - 'src/v0.7/Operator.sol:Operator', - roles.defaultAccount, - ) - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) -}) - -describe('ChainlinkClientTestHelper', () => { - const specId = - '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' - let cc: Contract - let gs: Contract - let oc: Contract - let newoc: Contract - let link: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - oc = await operatorFactory - .connect(roles.defaultAccount) - .deploy(link.address, await roles.defaultAccount.getAddress()) - newoc = await operatorFactory - .connect(roles.defaultAccount) - .deploy(link.address, await roles.defaultAccount.getAddress()) - gs = await getterSetterFactory.connect(roles.defaultAccount).deploy() - cc = await concreteChainlinkClientFactory - .connect(roles.defaultAccount) - .deploy(link.address, oc.address) - }) - - describe('#newRequest', () => { - it('forwards the information to the oracle contract through the link token', async () => { - const tx = await cc.publicNewRequest( - specId, - gs.address, - ethers.utils.toUtf8Bytes('requestedBytes32(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - - assert.equal(1, receipt.logs?.length) - const [jId, cbAddr, cbFId, cborData] = receipt.logs - ? decodeCCRequest(receipt.logs[0]) - : [] - const params = decodeDietCBOR(cborData ?? '') - - assert.equal(specId, jId) - assert.equal(gs.address, cbAddr) - assert.equal('0xed53e511', cbFId) - assert.deepEqual({}, params) - }) - }) - - describe('#chainlinkRequest(Request)', () => { - it('emits an event from the contract showing the run ID', async () => { - const tx = await cc.publicRequest( - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - - const { events, logs } = await tx.wait() - - assert.equal(4, events?.length) - - assert.equal(logs?.[0].address, cc.address) - assert.equal(events?.[0].event, 'ChainlinkRequested') - }) - }) - - describe('#chainlinkRequestTo(Request)', () => { - it('emits an event from the contract showing the run ID', async () => { - const tx = await cc.publicRequestRunTo( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { events } = await tx.wait() - - assert.equal(4, events?.length) - assert.equal(events?.[0].event, 'ChainlinkRequested') - }) - - it('emits an event on the target oracle contract', async () => { - const tx = await cc.publicRequestRunTo( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { logs } = await tx.wait() - const event = logs && newoc.interface.parseLog(logs[3]) - - assert.equal(4, logs?.length) - assert.equal(event?.name, 'OracleRequest') - }) - - it('does not modify the stored oracle address', async () => { - await cc.publicRequestRunTo( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - - const actualOracleAddress = await cc.publicOracleAddress() - assert.equal(oc.address, actualOracleAddress) - }) - }) - - describe('#requestOracleData', () => { - it('emits an event from the contract showing the run ID', async () => { - const tx = await cc.publicRequestOracleData( - specId, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - - const { events, logs } = await tx.wait() - - assert.equal(4, events?.length) - - assert.equal(logs?.[0].address, cc.address) - assert.equal(events?.[0].event, 'ChainlinkRequested') - }) - }) - - describe('#requestOracleDataFrom', () => { - it('emits an event from the contract showing the run ID', async () => { - const tx = await cc.publicRequestOracleDataFrom( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { events } = await tx.wait() - - assert.equal(4, events?.length) - assert.equal(events?.[0].event, 'ChainlinkRequested') - }) - - it('emits an event on the target oracle contract', async () => { - const tx = await cc.publicRequestOracleDataFrom( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { logs } = await tx.wait() - const event = logs && newoc.interface.parseLog(logs[3]) - - assert.equal(4, logs?.length) - assert.equal(event?.name, 'OracleRequest') - }) - - it('does not modify the stored oracle address', async () => { - await cc.publicRequestOracleDataFrom( - newoc.address, - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - - const actualOracleAddress = await cc.publicOracleAddress() - assert.equal(oc.address, actualOracleAddress) - }) - }) - - describe('#cancelChainlinkRequest', () => { - let requestId: string - // a concrete chainlink attached to an empty oracle - let ecc: Contract - - beforeEach(async () => { - const emptyOracle = await emptyOracleFactory - .connect(roles.defaultAccount) - .deploy() - ecc = await concreteChainlinkClientFactory - .connect(roles.defaultAccount) - .deploy(link.address, emptyOracle.address) - - const tx = await ecc.publicRequest( - specId, - ecc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { events } = await tx.wait() - requestId = (events?.[0]?.args as any).id - }) - - it('emits an event from the contract showing the run was cancelled', async () => { - const tx = await ecc.publicCancelRequest( - requestId, - 0, - ethers.utils.hexZeroPad('0x', 4), - 0, - ) - const { events } = await tx.wait() - - assert.equal(1, events?.length) - assert.equal(events?.[0].event, 'ChainlinkCancelled') - assert.equal(requestId, (events?.[0].args as any).id) - }) - - it('throws if given a bogus event ID', async () => { - await evmRevert( - ecc.publicCancelRequest( - ethers.utils.formatBytes32String('bogusId'), - 0, - ethers.utils.hexZeroPad('0x', 4), - 0, - ), - ) - }) - }) - - describe('#recordChainlinkFulfillment(modifier)', () => { - let request: RunRequest - - beforeEach(async () => { - await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()]) - const tx = await cc.publicRequest( - specId, - cc.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const { logs } = await tx.wait() - - request = decodeRunRequest(logs?.[3]) - }) - - it('emits an event marking the request fulfilled', async () => { - const tx = await oc - .connect(roles.defaultAccount) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - const { logs } = await tx.wait() - - const event = logs && cc.interface.parseLog(logs[1]) - - assert.equal(2, logs?.length) - assert.equal(event?.name, 'ChainlinkFulfilled') - assert.equal(request.requestId, event?.args.id) - }) - - it('should only allow one fulfillment per id', async () => { - await oc - .connect(roles.defaultAccount) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - - await evmRevert( - oc - .connect(roles.defaultAccount) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Must have a valid requestId', - ) - }) - - it('should only allow the oracle to fulfill the request', async () => { - await evmRevert( - oc - .connect(roles.stranger) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Not authorized sender', - ) - }) - }) - - describe('#fulfillChainlinkRequest(function)', () => { - let request: RunRequest - - beforeEach(async () => { - await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()]) - const tx = await cc.publicRequest( - specId, - cc.address, - ethers.utils.toUtf8Bytes( - 'publicFulfillChainlinkRequest(bytes32,bytes32)', - ), - 0, - ) - const { logs } = await tx.wait() - - request = decodeRunRequest(logs?.[3]) - }) - - it('emits an event marking the request fulfilled', async () => { - const tx = await oc - .connect(roles.defaultAccount) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - - const { logs } = await tx.wait() - const event = logs && cc.interface.parseLog(logs[1]) - - assert.equal(2, logs?.length) - assert.equal(event?.name, 'ChainlinkFulfilled') - assert.equal(request.requestId, event?.args?.id) - }) - - it('should only allow one fulfillment per id', async () => { - await oc - .connect(roles.defaultAccount) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - - await evmRevert( - oc - .connect(roles.defaultAccount) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Must have a valid requestId', - ) - }) - - it('should only allow the oracle to fulfill the request', async () => { - await evmRevert( - oc - .connect(roles.stranger) - .fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ), - 'Not authorized sender', - ) - }) - }) - - describe('#chainlinkToken', () => { - it('returns the Link Token address', async () => { - const addr = await cc.publicChainlinkToken() - assert.equal(addr, link.address) - }) - }) - - describe('#addExternalRequest', () => { - let mock: Contract - let request: RunRequest - - beforeEach(async () => { - mock = await concreteChainlinkClientFactory - .connect(roles.defaultAccount) - .deploy(link.address, oc.address) - - const tx = await cc.publicRequest( - specId, - mock.address, - ethers.utils.toUtf8Bytes('fulfillRequest(bytes32,bytes32)'), - 0, - ) - const receipt = await tx.wait() - - request = decodeRunRequest(receipt.logs?.[3]) - await mock.publicAddExternalRequest(oc.address, request.requestId) - }) - - it('allows the external request to be fulfilled', async () => { - await oc.setAuthorizedSenders([await roles.defaultAccount.getAddress()]) - await oc.fulfillOracleRequest( - ...convertFufillParams( - request, - ethers.utils.formatBytes32String('hi mom!'), - ), - ) - }) - - it('does not allow the same requestId to be used', async () => { - await evmRevert( - cc.publicAddExternalRequest(newoc.address, request.requestId), - ) - }) - }) -}) diff --git a/contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts b/contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts deleted file mode 100644 index 315f7bd9e6b..00000000000 --- a/contracts/test/v0.7/CompoundPriceFlaggingValidator.test.ts +++ /dev/null @@ -1,471 +0,0 @@ -import { ethers } from 'hardhat' -import { evmWordToAddress, getLogs, publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { - BigNumber, - Contract, - ContractFactory, - ContractTransaction, -} from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { evmRevert } from '../test-helpers/matchers' - -let personas: Personas -let validatorFactory: ContractFactory -let acFactory: ContractFactory -let flagsFactory: ContractFactory -let aggregatorFactory: ContractFactory -let compoundOracleFactory: ContractFactory - -before(async () => { - personas = (await getUsers()).personas - - validatorFactory = await ethers.getContractFactory( - 'src/v0.7/dev/CompoundPriceFlaggingValidator.sol:CompoundPriceFlaggingValidator', - personas.Carol, - ) - acFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleWriteAccessController.sol:SimpleWriteAccessController', - personas.Carol, - ) - flagsFactory = await ethers.getContractFactory( - 'src/v0.6/Flags.sol:Flags', - personas.Carol, - ) - aggregatorFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MockV3Aggregator.sol:MockV3Aggregator', - personas.Carol, - ) - compoundOracleFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MockCompoundOracle.sol:MockCompoundOracle', - personas.Carol, - ) -}) - -describe('CompoundPriceFlaggingVlidator', () => { - let validator: Contract - let aggregator: Contract - let compoundOracle: Contract - let flags: Contract - let ac: Contract - - const aggregatorDecimals = 18 - // 1000 - const initialAggregatorPrice = BigNumber.from('1000000000000000000000') - - const compoundSymbol = 'ETH' - const compoundDecimals = 6 - // 1100 (10% deviation from aggregator price) - const initialCompoundPrice = BigNumber.from('1100000000') - - // (50,000,000 / 1,000,000,000) = 0.05 = 5% deviation threshold - const initialDeviationNumerator = 50_000_000 - - beforeEach(async () => { - ac = await acFactory.connect(personas.Carol).deploy() - flags = await flagsFactory.connect(personas.Carol).deploy(ac.address) - aggregator = await aggregatorFactory - .connect(personas.Carol) - .deploy(aggregatorDecimals, initialAggregatorPrice) - compoundOracle = await compoundOracleFactory - .connect(personas.Carol) - .deploy() - await compoundOracle.setPrice( - compoundSymbol, - initialCompoundPrice, - compoundDecimals, - ) - validator = await validatorFactory - .connect(personas.Carol) - .deploy(flags.address, compoundOracle.address) - await validator - .connect(personas.Carol) - .setFeedDetails( - aggregator.address, - compoundSymbol, - compoundDecimals, - initialDeviationNumerator, - ) - await ac.connect(personas.Carol).addAccess(validator.address) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(validator, [ - 'update', - 'check', - 'setFeedDetails', - 'setFlagsAddress', - 'setCompoundOpenOracleAddress', - 'getFeedDetails', - 'flags', - 'compoundOpenOracle', - // Upkeep methods: - 'checkUpkeep', - 'performUpkeep', - // Owned methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#constructor', () => { - it('sets the owner', async () => { - assert.equal(await validator.owner(), await personas.Carol.getAddress()) - }) - - it('sets the arguments passed in', async () => { - assert.equal(await validator.flags(), flags.address) - assert.equal(await validator.compoundOpenOracle(), compoundOracle.address) - }) - }) - - describe('#setOpenOracleAddress', () => { - let newCompoundOracle: Contract - let tx: ContractTransaction - - beforeEach(async () => { - newCompoundOracle = await compoundOracleFactory - .connect(personas.Carol) - .deploy() - tx = await validator - .connect(personas.Carol) - .setCompoundOpenOracleAddress(newCompoundOracle.address) - }) - - it('changes the compound oracke address', async () => { - assert.equal( - await validator.compoundOpenOracle(), - newCompoundOracle.address, - ) - }) - - it('emits a log event', async () => { - await expect(tx) - .to.emit(validator, 'CompoundOpenOracleAddressUpdated') - .withArgs(compoundOracle.address, newCompoundOracle.address) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - validator - .connect(personas.Neil) - .setCompoundOpenOracleAddress(newCompoundOracle.address), - 'Only callable by owner', - ) - }) - }) - }) - - describe('#setFlagsAddress', () => { - let newFlagsContract: Contract - let tx: ContractTransaction - - beforeEach(async () => { - newFlagsContract = await flagsFactory - .connect(personas.Carol) - .deploy(ac.address) - tx = await validator - .connect(personas.Carol) - .setFlagsAddress(newFlagsContract.address) - }) - - it('changes the flags address', async () => { - assert.equal(await validator.flags(), newFlagsContract.address) - }) - - it('emits a log event', async () => { - await expect(tx) - .to.emit(validator, 'FlagsAddressUpdated') - .withArgs(flags.address, newFlagsContract.address) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - validator - .connect(personas.Neil) - .setFlagsAddress(newFlagsContract.address), - 'Only callable by owner', - ) - }) - }) - }) - - describe('#setFeedDetails', () => { - let mockAggregator: Contract - let tx: ContractTransaction - const symbol = 'BTC' - const decimals = 8 - const deviationNumerator = 50_000_000 // 5% - - beforeEach(async () => { - await compoundOracle.connect(personas.Carol).setPrice('BTC', 1500000, 2) - mockAggregator = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, 4000000000000) - tx = await validator - .connect(personas.Carol) - .setFeedDetails( - mockAggregator.address, - symbol, - decimals, - deviationNumerator, - ) - }) - - it('sets the correct state', async () => { - const response = await validator - .connect(personas.Carol) - .getFeedDetails(mockAggregator.address) - - assert.equal(response[0], symbol) - assert.equal(response[1], decimals) - assert.equal(response[2].toString(), deviationNumerator.toString()) - }) - - it('uses the existing symbol if one already exists', async () => { - const newSymbol = 'LINK' - - await compoundOracle - .connect(personas.Carol) - .setPrice(newSymbol, 1500000, 2) - - tx = await validator - .connect(personas.Carol) - .setFeedDetails( - mockAggregator.address, - newSymbol, - decimals, - deviationNumerator, - ) - - // Check the event - await expect(tx) - .to.emit(validator, 'FeedDetailsSet') - .withArgs(mockAggregator.address, symbol, decimals, deviationNumerator) - - // Check the state - const response = await validator - .connect(personas.Carol) - .getFeedDetails(mockAggregator.address) - assert.equal(response[0], symbol) - }) - - it('emits an event', async () => { - await expect(tx) - .to.emit(validator, 'FeedDetailsSet') - .withArgs(mockAggregator.address, symbol, decimals, deviationNumerator) - }) - - it('fails when given a 0 numerator', async () => { - await evmRevert( - validator - .connect(personas.Carol) - .setFeedDetails(mockAggregator.address, symbol, decimals, 0), - 'Invalid threshold numerator', - ) - }) - - it('fails when given a numerator above 1 billion', async () => { - await evmRevert( - validator - .connect(personas.Carol) - .setFeedDetails( - mockAggregator.address, - symbol, - decimals, - 1_200_000_000, - ), - 'Invalid threshold numerator', - ) - }) - - it('fails when the compound price is invalid', async () => { - await evmRevert( - validator - .connect(personas.Carol) - .setFeedDetails( - mockAggregator.address, - 'TEST', - decimals, - deviationNumerator, - ), - 'Invalid Compound price', - ) - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - validator - .connect(personas.Neil) - .setFeedDetails( - mockAggregator.address, - symbol, - decimals, - deviationNumerator, - ), - 'Only callable by owner', - ) - }) - }) - }) - - describe('#check', () => { - describe('with a single aggregator', () => { - describe('with a deviated price exceding threshold', () => { - it('returns the deviated aggregator', async () => { - const aggregators = [aggregator.address] - const response = await validator.check(aggregators) - assert.equal(response.length, 1) - assert.equal(response[0], aggregator.address) - }) - }) - - describe('with a price within the threshold', () => { - const newCompoundPrice = BigNumber.from('1000000000') - beforeEach(async () => { - await compoundOracle.setPrice( - 'ETH', - newCompoundPrice, - compoundDecimals, - ) - }) - - it('returns an empty array', async () => { - const aggregators = [aggregator.address] - const response = await validator.check(aggregators) - assert.equal(response.length, 0) - }) - }) - }) - }) - - describe('#update', () => { - describe('with a single aggregator', () => { - describe('with a deviated price exceding threshold', () => { - it('raises a flag on the flags contract', async () => { - const aggregators = [aggregator.address] - const tx = await validator.connect(personas.Carol).update(aggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 1) - assert.equal(evmWordToAddress(logs[0].topics[1]), aggregator.address) - }) - }) - - describe('with a price within the threshold', () => { - const newCompoundPrice = BigNumber.from('1000000000') - beforeEach(async () => { - await compoundOracle.setPrice( - 'ETH', - newCompoundPrice, - compoundDecimals, - ) - }) - - it('does nothing', async () => { - const aggregators = [aggregator.address] - const tx = await validator.connect(personas.Carol).update(aggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - }) - }) - - describe('#checkUpkeep', () => { - describe('with a single aggregator', () => { - describe('with a deviated price exceding threshold', () => { - it('returns the deviated aggregator', async () => { - const aggregators = [aggregator.address] - const encodedAggregators = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const response = await validator - .connect(personas.Carol) - .checkUpkeep(encodedAggregators) - - const decodedResponse = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - response?.[1], - ) - assert.equal(decodedResponse?.[0]?.[0], aggregators[0]) - }) - }) - - describe('with a price within the threshold', () => { - const newCompoundPrice = BigNumber.from('1000000000') - beforeEach(async () => { - await compoundOracle.setPrice( - 'ETH', - newCompoundPrice, - compoundDecimals, - ) - }) - - it('returns an empty array', async () => { - const aggregators = [aggregator.address] - const encodedAggregators = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const response = await validator - .connect(personas.Carol) - .checkUpkeep(encodedAggregators) - const decodedResponse = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - response?.[1], - ) - assert.equal(decodedResponse?.[0]?.length, 0) - }) - }) - }) - }) - - describe('#performUpkeep', () => { - describe('with a single aggregator', () => { - describe('with a deviated price exceding threshold', () => { - it('raises a flag on the flags contract', async () => { - const aggregators = [aggregator.address] - const encodedAggregators = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const tx = await validator - .connect(personas.Carol) - .performUpkeep(encodedAggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 1) - assert.equal(evmWordToAddress(logs[0].topics[1]), aggregator.address) - }) - }) - - describe('with a price within the threshold', () => { - const newCompoundPrice = BigNumber.from('1000000000') - beforeEach(async () => { - await compoundOracle.setPrice( - 'ETH', - newCompoundPrice, - compoundDecimals, - ) - }) - - it('does nothing', async () => { - const aggregators = [aggregator.address] - const encodedAggregators = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const tx = await validator - .connect(personas.Carol) - .performUpkeep(encodedAggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/ConfirmedOwner.test.ts b/contracts/test/v0.7/ConfirmedOwner.test.ts deleted file mode 100644 index 3502cd15bc2..00000000000 --- a/contracts/test/v0.7/ConfirmedOwner.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { ethers } from 'hardhat' -import { publicAbi } from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { Contract, ContractFactory, Signer } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { evmRevert } from '../test-helpers/matchers' - -let confirmedOwnerTestHelperFactory: ContractFactory -let confirmedOwnerFactory: ContractFactory - -let personas: Personas -let owner: Signer -let nonOwner: Signer -let newOwner: Signer - -before(async () => { - const users = await getUsers() - personas = users.personas - owner = personas.Carol - nonOwner = personas.Neil - newOwner = personas.Ned - - confirmedOwnerTestHelperFactory = await ethers.getContractFactory( - 'src/v0.7/tests/ConfirmedOwnerTestHelper.sol:ConfirmedOwnerTestHelper', - owner, - ) - confirmedOwnerFactory = await ethers.getContractFactory( - 'src/v0.7/ConfirmedOwner.sol:ConfirmedOwner', - owner, - ) -}) - -describe('ConfirmedOwner', () => { - let confirmedOwner: Contract - - beforeEach(async () => { - confirmedOwner = await confirmedOwnerTestHelperFactory - .connect(owner) - .deploy() - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(confirmedOwner, [ - 'acceptOwnership', - 'owner', - 'transferOwnership', - // test helper public methods - 'modifierOnlyOwner', - ]) - }) - - describe('#constructor', () => { - it('assigns ownership to the deployer', async () => { - const [actual, expected] = await Promise.all([ - owner.getAddress(), - confirmedOwner.owner(), - ]) - - assert.equal(actual, expected) - }) - - it('reverts if assigned to the zero address', async () => { - await evmRevert( - confirmedOwnerFactory - .connect(owner) - .deploy(ethers.constants.AddressZero), - 'Cannot set owner to zero', - ) - }) - }) - - describe('#onlyOwner modifier', () => { - describe('when called by an owner', () => { - it('successfully calls the method', async () => { - const tx = await confirmedOwner.connect(owner).modifierOnlyOwner() - await expect(tx).to.emit(confirmedOwner, 'Here') - }) - }) - - describe('when called by anyone but the owner', () => { - it('reverts', async () => - await evmRevert(confirmedOwner.connect(nonOwner).modifierOnlyOwner())) - }) - }) - - describe('#transferOwnership', () => { - describe('when called by an owner', () => { - it('emits a log', async () => { - const tx = await confirmedOwner - .connect(owner) - .transferOwnership(await newOwner.getAddress()) - await expect(tx) - .to.emit(confirmedOwner, 'OwnershipTransferRequested') - .withArgs(await owner.getAddress(), await newOwner.getAddress()) - }) - - it('does not allow ownership transfer to self', async () => { - await evmRevert( - confirmedOwner - .connect(owner) - .transferOwnership(await owner.getAddress()), - 'Cannot transfer to self', - ) - }) - }) - }) - - describe('when called by anyone but the owner', () => { - it('reverts', async () => - await evmRevert( - confirmedOwner - .connect(nonOwner) - .transferOwnership(await newOwner.getAddress()), - )) - }) - - describe('#acceptOwnership', () => { - describe('after #transferOwnership has been called', () => { - beforeEach(async () => { - await confirmedOwner - .connect(owner) - .transferOwnership(await newOwner.getAddress()) - }) - - it('allows the recipient to call it', async () => { - const tx = await confirmedOwner.connect(newOwner).acceptOwnership() - await expect(tx) - .to.emit(confirmedOwner, 'OwnershipTransferred') - .withArgs(await owner.getAddress(), await newOwner.getAddress()) - }) - - it('does not allow a non-recipient to call it', async () => - await evmRevert(confirmedOwner.connect(nonOwner).acceptOwnership())) - }) - }) -}) diff --git a/contracts/test/v0.7/KeeperRegistry1_1.test.ts b/contracts/test/v0.7/KeeperRegistry1_1.test.ts deleted file mode 100644 index 4e3a8c91b35..00000000000 --- a/contracts/test/v0.7/KeeperRegistry1_1.test.ts +++ /dev/null @@ -1,1725 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { evmRevert } from '../test-helpers/matchers' -import { getUsers, Personas } from '../test-helpers/setup' -import { BigNumber, BigNumberish, Signer } from 'ethers' -import { LinkToken__factory as LinkTokenFactory } from '../../typechain/factories/LinkToken__factory' -import { KeeperRegistry1_1__factory as KeeperRegistryFactory } from '../../typechain/factories/KeeperRegistry1_1__factory' -import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../typechain/factories/MockV3Aggregator__factory' -import { UpkeepMock__factory as UpkeepMockFactory } from '../../typechain/factories/UpkeepMock__factory' -import { UpkeepReverter__factory as UpkeepReverterFactory } from '../../typechain/factories/UpkeepReverter__factory' -import { KeeperRegistry1_1 as KeeperRegistry } from '../../typechain/KeeperRegistry1_1' -import { MockV3Aggregator } from '../../typechain/MockV3Aggregator' -import { LinkToken } from '../../typechain/LinkToken' -import { UpkeepMock } from '../../typechain/UpkeepMock' -import { toWei } from '../test-helpers/helpers' - -async function getUpkeepID(tx: any) { - const receipt = await tx.wait() - return receipt.events[0].args.id -} - -// ----------------------------------------------------------------------------------------------- -// DEV: these *should* match the perform/check gas overhead values in the contract and on the node -const PERFORM_GAS_OVERHEAD = BigNumber.from(90000) -const CHECK_GAS_OVERHEAD = BigNumber.from(170000) -// ----------------------------------------------------------------------------------------------- - -// Smart contract factories -let linkTokenFactory: LinkTokenFactory -let mockV3AggregatorFactory: MockV3AggregatorFactory -let keeperRegistryFactory: KeeperRegistryFactory -let upkeepMockFactory: UpkeepMockFactory -let upkeepReverterFactory: UpkeepReverterFactory - -let personas: Personas - -before(async () => { - personas = (await getUsers()).personas - - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - ) - // need full path because there are two contracts with name MockV3Aggregator - mockV3AggregatorFactory = (await ethers.getContractFactory( - 'src/v0.7/tests/MockV3Aggregator.sol:MockV3Aggregator', - )) as unknown as MockV3AggregatorFactory - // @ts-ignore bug in autogen file - keeperRegistryFactory = await ethers.getContractFactory('KeeperRegistry1_1') - upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') - upkeepReverterFactory = await ethers.getContractFactory('UpkeepReverter') -}) - -describe('KeeperRegistry1_1', () => { - const linkEth = BigNumber.from(300000000) - const gasWei = BigNumber.from(100) - const linkDivisibility = BigNumber.from('1000000000000000000') - const executeGas = BigNumber.from('100000') - const paymentPremiumBase = BigNumber.from('1000000000') - const paymentPremiumPPB = BigNumber.from('250000000') - const flatFeeMicroLink = BigNumber.from(0) - const blockCountPerTurn = BigNumber.from(3) - const emptyBytes = '0x00' - const zeroAddress = ethers.constants.AddressZero - const extraGas = BigNumber.from('250000') - const registryGasOverhead = BigNumber.from('80000') - const stalenessSeconds = BigNumber.from(43820) - const gasCeilingMultiplier = BigNumber.from(1) - const maxCheckGas = BigNumber.from(20000000) - const fallbackGasPrice = BigNumber.from(200) - const fallbackLinkPrice = BigNumber.from(200000000) - - let owner: Signer - let keeper1: Signer - let keeper2: Signer - let keeper3: Signer - let nonkeeper: Signer - let admin: Signer - let payee1: Signer - let payee2: Signer - let payee3: Signer - - let linkToken: LinkToken - let linkEthFeed: MockV3Aggregator - let gasPriceFeed: MockV3Aggregator - let registry: KeeperRegistry - let mock: UpkeepMock - - let id: BigNumber - let keepers: string[] - let payees: string[] - - beforeEach(async () => { - owner = personas.Default - keeper1 = personas.Carol - keeper2 = personas.Eddy - keeper3 = personas.Nancy - nonkeeper = personas.Ned - admin = personas.Neil - payee1 = personas.Nelly - payee2 = personas.Norbert - payee3 = personas.Nick - - keepers = [ - await keeper1.getAddress(), - await keeper2.getAddress(), - await keeper3.getAddress(), - ] - payees = [ - await payee1.getAddress(), - await payee2.getAddress(), - await payee3.getAddress(), - ] - - linkToken = await linkTokenFactory.connect(owner).deploy() - gasPriceFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(0, gasWei) - linkEthFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(9, linkEth) - registry = await keeperRegistryFactory - .connect(owner) - .deploy( - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - gasCeilingMultiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - - mock = await upkeepMockFactory.deploy() - await linkToken - .connect(owner) - .transfer(await keeper1.getAddress(), toWei('1000')) - await linkToken - .connect(owner) - .transfer(await keeper2.getAddress(), toWei('1000')) - await linkToken - .connect(owner) - .transfer(await keeper3.getAddress(), toWei('1000')) - - await registry.connect(owner).setKeepers(keepers, payees) - const tx = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - id = await getUpkeepID(tx) - }) - - const linkForGas = ( - upkeepGasSpent: BigNumberish, - premiumPPB?: BigNumberish, - flatFee?: BigNumberish, - ) => { - premiumPPB = premiumPPB === undefined ? paymentPremiumPPB : premiumPPB - flatFee = flatFee === undefined ? flatFeeMicroLink : flatFee - const gasSpent = registryGasOverhead.add(BigNumber.from(upkeepGasSpent)) - const base = gasWei.mul(gasSpent).mul(linkDivisibility).div(linkEth) - const premium = base.mul(premiumPPB).div(paymentPremiumBase) - const flatFeeJules = BigNumber.from(flatFee).mul('1000000000000') - return base.add(premium).add(flatFeeJules) - } - - describe('#setKeepers', () => { - const IGNORE_ADDRESS = '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF' - it('reverts when not called by the owner', async () => { - await evmRevert( - registry.connect(keeper1).setKeepers([], []), - 'Only callable by owner', - ) - }) - - it('reverts when adding the same keeper twice', async () => { - await evmRevert( - registry - .connect(owner) - .setKeepers( - [await keeper1.getAddress(), await keeper1.getAddress()], - [await payee1.getAddress(), await payee1.getAddress()], - ), - 'cannot add keeper twice', - ) - }) - - it('reverts with different numbers of keepers/payees', async () => { - await evmRevert( - registry - .connect(owner) - .setKeepers( - [await keeper1.getAddress(), await keeper2.getAddress()], - [await payee1.getAddress()], - ), - 'address lists not the same length', - ) - await evmRevert( - registry - .connect(owner) - .setKeepers( - [await keeper1.getAddress()], - [await payee1.getAddress(), await payee2.getAddress()], - ), - 'address lists not the same length', - ) - }) - - it('reverts if the payee is the zero address', async () => { - await evmRevert( - registry - .connect(owner) - .setKeepers( - [await keeper1.getAddress(), await keeper2.getAddress()], - [ - await payee1.getAddress(), - '0x0000000000000000000000000000000000000000', - ], - ), - 'cannot set payee to the zero address', - ) - }) - - it('emits events for every keeper added and removed', async () => { - const oldKeepers = [ - await keeper1.getAddress(), - await keeper2.getAddress(), - ] - const oldPayees = [await payee1.getAddress(), await payee2.getAddress()] - await registry.connect(owner).setKeepers(oldKeepers, oldPayees) - assert.deepEqual(oldKeepers, await registry.getKeeperList()) - - // remove keepers - const newKeepers = [ - await keeper2.getAddress(), - await keeper3.getAddress(), - ] - const newPayees = [await payee2.getAddress(), await payee3.getAddress()] - const tx = await registry.connect(owner).setKeepers(newKeepers, newPayees) - assert.deepEqual(newKeepers, await registry.getKeeperList()) - - await expect(tx) - .to.emit(registry, 'KeepersUpdated') - .withArgs(newKeepers, newPayees) - }) - - it('updates the keeper to inactive when removed', async () => { - await registry.connect(owner).setKeepers(keepers, payees) - await registry - .connect(owner) - .setKeepers( - [await keeper1.getAddress(), await keeper3.getAddress()], - [await payee1.getAddress(), await payee3.getAddress()], - ) - const added = await registry.getKeeperInfo(await keeper1.getAddress()) - assert.isTrue(added.active) - const removed = await registry.getKeeperInfo(await keeper2.getAddress()) - assert.isFalse(removed.active) - }) - - it('does not change the payee if IGNORE_ADDRESS is used as payee', async () => { - const oldKeepers = [ - await keeper1.getAddress(), - await keeper2.getAddress(), - ] - const oldPayees = [await payee1.getAddress(), await payee2.getAddress()] - await registry.connect(owner).setKeepers(oldKeepers, oldPayees) - assert.deepEqual(oldKeepers, await registry.getKeeperList()) - - const newKeepers = [ - await keeper2.getAddress(), - await keeper3.getAddress(), - ] - const newPayees = [IGNORE_ADDRESS, await payee3.getAddress()] - const tx = await registry.connect(owner).setKeepers(newKeepers, newPayees) - assert.deepEqual(newKeepers, await registry.getKeeperList()) - - const ignored = await registry.getKeeperInfo(await keeper2.getAddress()) - assert.equal(await payee2.getAddress(), ignored.payee) - assert.equal(true, ignored.active) - - await expect(tx) - .to.emit(registry, 'KeepersUpdated') - .withArgs(newKeepers, newPayees) - }) - - it('reverts if the owner changes the payee', async () => { - await registry.connect(owner).setKeepers(keepers, payees) - await evmRevert( - registry - .connect(owner) - .setKeepers(keepers, [ - await payee1.getAddress(), - await payee2.getAddress(), - await owner.getAddress(), - ]), - 'cannot change payee', - ) - }) - }) - - describe('#registerUpkeep', () => { - it('reverts if the target is not a contract', async () => { - await evmRevert( - registry - .connect(owner) - .registerUpkeep( - zeroAddress, - executeGas, - await admin.getAddress(), - emptyBytes, - ), - 'target is not a contract', - ) - }) - - it('reverts if called by a non-owner', async () => { - await evmRevert( - registry - .connect(keeper1) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ), - 'Only callable by owner or registrar', - ) - }) - - it('reverts if execute gas is too low', async () => { - await evmRevert( - registry - .connect(owner) - .registerUpkeep( - mock.address, - 2299, - await admin.getAddress(), - emptyBytes, - ), - 'min gas is 2300', - ) - }) - - it('reverts if execute gas is too high', async () => { - await evmRevert( - registry - .connect(owner) - .registerUpkeep( - mock.address, - 5000001, - await admin.getAddress(), - emptyBytes, - ), - 'max gas is 5000000', - ) - }) - - it('creates a record of the registration', async () => { - const tx = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - id = await getUpkeepID(tx) - await expect(tx) - .to.emit(registry, 'UpkeepRegistered') - .withArgs(id, executeGas, await admin.getAddress()) - const registration = await registry.getUpkeep(id) - assert.equal(mock.address, registration.target) - assert.equal(0, registration.balance.toNumber()) - assert.equal(emptyBytes, registration.checkData) - assert(registration.maxValidBlocknumber.eq('0xffffffffffffffff')) - }) - }) - - describe('#addFunds', () => { - const amount = toWei('1') - - beforeEach(async () => { - await linkToken.connect(keeper1).approve(registry.address, toWei('100')) - }) - - it('reverts if the registration does not exist', async () => { - await evmRevert( - registry.connect(keeper1).addFunds(id.add(1), amount), - 'upkeep must be active', - ) - }) - - it('adds to the balance of the registration', async () => { - await registry.connect(keeper1).addFunds(id, amount) - const registration = await registry.getUpkeep(id) - assert.isTrue(amount.eq(registration.balance)) - }) - - it('emits a log', async () => { - const tx = await registry.connect(keeper1).addFunds(id, amount) - await expect(tx) - .to.emit(registry, 'FundsAdded') - .withArgs(id, await keeper1.getAddress(), amount) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(id) - await evmRevert( - registry.connect(keeper1).addFunds(id, amount), - 'upkeep must be active', - ) - }) - }) - - describe('#checkUpkeep', () => { - it('reverts if the upkeep is not funded', async () => { - await mock.setCanPerform(true) - await mock.setCanCheck(true) - await evmRevert( - registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()), - 'insufficient funds', - ) - }) - - context('when the registration is funded', () => { - beforeEach(async () => { - await linkToken.connect(keeper1).approve(registry.address, toWei('100')) - await registry.connect(keeper1).addFunds(id, toWei('100')) - }) - - it('reverts if executed', async () => { - await mock.setCanPerform(true) - await mock.setCanCheck(true) - await evmRevert( - registry.checkUpkeep(id, await keeper1.getAddress()), - 'only for simulated backend', - ) - }) - - it('reverts if the specified keeper is not valid', async () => { - await mock.setCanPerform(true) - await mock.setCanCheck(true) - await evmRevert( - registry.checkUpkeep(id, await owner.getAddress()), - 'only for simulated backend', - ) - }) - - context('and upkeep is not needed', () => { - beforeEach(async () => { - await mock.setCanCheck(false) - }) - - it('reverts', async () => { - await evmRevert( - registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()), - 'upkeep not needed', - ) - }) - }) - - context('and the upkeep check fails', () => { - beforeEach(async () => { - const reverter = await upkeepReverterFactory.deploy() - const tx = await registry - .connect(owner) - .registerUpkeep( - reverter.address, - 2500000, - await admin.getAddress(), - emptyBytes, - ) - id = await getUpkeepID(tx) - await linkToken - .connect(keeper1) - .approve(registry.address, toWei('100')) - await registry.connect(keeper1).addFunds(id, toWei('100')) - }) - - it('reverts', async () => { - await evmRevert( - registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()), - 'call to check target failed', - ) - }) - }) - - context('and upkeep check simulations succeeds', () => { - beforeEach(async () => { - await mock.setCanCheck(true) - await mock.setCanPerform(true) - }) - - context('and the registry is paused', () => { - beforeEach(async () => { - await registry.connect(owner).pause() - }) - - it('reverts', async () => { - await evmRevert( - registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()), - 'Pausable: paused', - ) - - await registry.connect(owner).unpause() - - await registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()) - }) - }) - - it('returns true with pricing info if the target can execute', async () => { - const newGasMultiplier = BigNumber.from(10) - await registry - .connect(owner) - .setConfig( - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - newGasMultiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - const response = await registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()) - assert.isTrue(response.gasLimit.eq(executeGas)) - assert.isTrue(response.linkEth.eq(linkEth)) - assert.isTrue( - response.adjustedGasWei.eq(gasWei.mul(newGasMultiplier)), - ) - assert.isTrue( - response.maxLinkPayment.eq( - linkForGas(executeGas.toNumber()).mul(newGasMultiplier), - ), - ) - }) - - it('has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]', async () => { - await mock.setCheckGasToBurn(maxCheckGas) - await mock.setPerformGasToBurn(executeGas) - const gas = maxCheckGas - .add(executeGas) - .add(PERFORM_GAS_OVERHEAD) - .add(CHECK_GAS_OVERHEAD) - await registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress(), { - gasLimit: gas, - }) - }) - }) - }) - }) - - describe('#performUpkeep', () => { - let _lastKeeper = keeper1 - async function getPerformPaymentAmount() { - _lastKeeper = _lastKeeper === keeper1 ? keeper2 : keeper1 - const before = ( - await registry.getKeeperInfo(await _lastKeeper.getAddress()) - ).balance - await registry.connect(_lastKeeper).performUpkeep(id, '0x') - const after = ( - await registry.getKeeperInfo(await _lastKeeper.getAddress()) - ).balance - const difference = after.sub(before) - return difference - } - - it('reverts if the registration is not funded', async () => { - await evmRevert( - registry.connect(keeper2).performUpkeep(id, '0x'), - 'insufficient funds', - ) - }) - - context('when the registration is funded', () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(id, toWei('100')) - }) - - it('does not revert if the target cannot execute', async () => { - const mockResponse = await mock - .connect(zeroAddress) - .callStatic.checkUpkeep('0x') - assert.isFalse(mockResponse.callable) - - await registry.connect(keeper3).performUpkeep(id, '0x') - }) - - it('returns false if the target cannot execute', async () => { - const mockResponse = await mock - .connect(zeroAddress) - .callStatic.checkUpkeep('0x') - assert.isFalse(mockResponse.callable) - - assert.isFalse( - await registry.connect(keeper1).callStatic.performUpkeep(id, '0x'), - ) - }) - - it('returns true if called', async () => { - await mock.setCanPerform(true) - - const response = await registry - .connect(keeper1) - .callStatic.performUpkeep(id, '0x') - assert.isTrue(response) - }) - - it('reverts if not enough gas supplied', async () => { - await mock.setCanPerform(true) - - await evmRevert( - registry - .connect(keeper1) - .performUpkeep(id, '0x', { gasLimit: BigNumber.from('120000') }), - ) - }) - - it('executes the data passed to the registry', async () => { - await mock.setCanPerform(true) - - const performData = '0xc0ffeec0ffee' - const tx = await registry - .connect(keeper1) - .performUpkeep(id, performData, { gasLimit: extraGas }) - const receipt = await tx.wait() - const eventLog = receipt?.events - - assert.equal(eventLog?.length, 2) - assert.equal(eventLog?.[1].event, 'UpkeepPerformed') - assert.equal(eventLog?.[1].args?.[0].toNumber(), id.toNumber()) - assert.equal(eventLog?.[1].args?.[1], true) - assert.equal(eventLog?.[1].args?.[2], await keeper1.getAddress()) - assert.isNotEmpty(eventLog?.[1].args?.[3]) - assert.equal(eventLog?.[1].args?.[4], performData) - }) - - it('updates payment balances', async () => { - const keeperBefore = await registry.getKeeperInfo( - await keeper1.getAddress(), - ) - const registrationBefore = await registry.getUpkeep(id) - const keeperLinkBefore = await linkToken.balanceOf( - await keeper1.getAddress(), - ) - const registryLinkBefore = await linkToken.balanceOf(registry.address) - - // Do the thing - await registry.connect(keeper1).performUpkeep(id, '0x') - - const keeperAfter = await registry.getKeeperInfo( - await keeper1.getAddress(), - ) - const registrationAfter = await registry.getUpkeep(id) - const keeperLinkAfter = await linkToken.balanceOf( - await keeper1.getAddress(), - ) - const registryLinkAfter = await linkToken.balanceOf(registry.address) - - assert.isTrue(keeperAfter.balance.gt(keeperBefore.balance)) - assert.isTrue(registrationBefore.balance.gt(registrationAfter.balance)) - assert.isTrue(keeperLinkAfter.eq(keeperLinkBefore)) - assert.isTrue(registryLinkBefore.eq(registryLinkAfter)) - }) - - it('only pays for gas used [ @skip-coverage ]', async () => { - const before = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - const tx = await registry.connect(keeper1).performUpkeep(id, '0x') - const receipt = await tx.wait() - const after = (await registry.getKeeperInfo(await keeper1.getAddress())) - .balance - - const max = linkForGas(executeGas.toNumber()) - const totalTx = linkForGas(receipt.gasUsed.toNumber()) - const difference = after.sub(before) - assert.isTrue(max.gt(totalTx)) - assert.isTrue(totalTx.gt(difference)) - assert.isTrue(linkForGas(5700).lt(difference)) // exact number is flaky - assert.isTrue(linkForGas(6000).gt(difference)) // instead test a range - }) - - it('only pays at a rate up to the gas ceiling [ @skip-coverage ]', async () => { - const multiplier = BigNumber.from(10) - const gasPrice = BigNumber.from('1000000000') // 10M x the gas feed's rate - await registry - .connect(owner) - .setConfig( - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - multiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - - const before = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - const tx = await registry - .connect(keeper1) - .performUpkeep(id, '0x', { gasPrice }) - const receipt = await tx.wait() - const after = (await registry.getKeeperInfo(await keeper1.getAddress())) - .balance - - const max = linkForGas(executeGas).mul(multiplier) - const totalTx = linkForGas(receipt.gasUsed).mul(multiplier) - const difference = after.sub(before) - assert.isTrue(max.gt(totalTx)) - assert.isTrue(totalTx.gt(difference)) - assert.isTrue(linkForGas(5700).mul(multiplier).lt(difference)) - assert.isTrue(linkForGas(6000).mul(multiplier).gt(difference)) - }) - - it('only pays as much as the node spent [ @skip-coverage ]', async () => { - const multiplier = BigNumber.from(10) - const gasPrice = BigNumber.from(200) // 2X the gas feed's rate - const effectiveMultiplier = BigNumber.from(2) - await registry - .connect(owner) - .setConfig( - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - multiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - - const before = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - const tx = await registry - .connect(keeper1) - .performUpkeep(id, '0x', { gasPrice }) - const receipt = await tx.wait() - const after = (await registry.getKeeperInfo(await keeper1.getAddress())) - .balance - - const max = linkForGas(executeGas.toNumber()).mul(effectiveMultiplier) - const totalTx = linkForGas(receipt.gasUsed).mul(effectiveMultiplier) - const difference = after.sub(before) - assert.isTrue(max.gt(totalTx)) - assert.isTrue(totalTx.gt(difference)) - assert.isTrue(linkForGas(5700).mul(effectiveMultiplier).lt(difference)) - assert.isTrue(linkForGas(6000).mul(effectiveMultiplier).gt(difference)) - }) - - it('pays the caller even if the target function fails', async () => { - const tx = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - const id = await getUpkeepID(tx) - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(id, toWei('100')) - const keeperBalanceBefore = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - - // Do the thing - await registry.connect(keeper1).performUpkeep(id, '0x') - - const keeperBalanceAfter = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - assert.isTrue(keeperBalanceAfter.gt(keeperBalanceBefore)) - }) - - it('reverts if called by a non-keeper', async () => { - await evmRevert( - registry.connect(nonkeeper).performUpkeep(id, '0x'), - 'only active keepers', - ) - }) - - it('reverts if the upkeep has been canceled', async () => { - await mock.setCanPerform(true) - - await registry.connect(owner).cancelUpkeep(id) - - await evmRevert( - registry.connect(keeper1).performUpkeep(id, '0x'), - 'invalid upkeep id', - ) - }) - - it('uses the fallback gas price if the feed price is stale [ @skip-coverage ]', async () => { - const normalAmount = await getPerformPaymentAmount() - const roundId = 99 - const answer = 100 - const updatedAt = 946684800 // New Years 2000 🥳 - const startedAt = 946684799 - await gasPriceFeed - .connect(owner) - .updateRoundData(roundId, answer, updatedAt, startedAt) - const amountWithStaleFeed = await getPerformPaymentAmount() - assert.isTrue(normalAmount.lt(amountWithStaleFeed)) - }) - - it('uses the fallback gas price if the feed price is non-sensical [ @skip-coverage ]', async () => { - const normalAmount = await getPerformPaymentAmount() - const roundId = 99 - const updatedAt = Math.floor(Date.now() / 1000) - const startedAt = 946684799 - await gasPriceFeed - .connect(owner) - .updateRoundData(roundId, -100, updatedAt, startedAt) - const amountWithNegativeFeed = await getPerformPaymentAmount() - await gasPriceFeed - .connect(owner) - .updateRoundData(roundId, 0, updatedAt, startedAt) - const amountWithZeroFeed = await getPerformPaymentAmount() - assert.isTrue(normalAmount.lt(amountWithNegativeFeed)) - assert.isTrue(normalAmount.lt(amountWithZeroFeed)) - }) - - it('uses the fallback if the link price feed is stale', async () => { - const normalAmount = await getPerformPaymentAmount() - const roundId = 99 - const answer = 100 - const updatedAt = 946684800 // New Years 2000 🥳 - const startedAt = 946684799 - await linkEthFeed - .connect(owner) - .updateRoundData(roundId, answer, updatedAt, startedAt) - const amountWithStaleFeed = await getPerformPaymentAmount() - assert.isTrue(normalAmount.lt(amountWithStaleFeed)) - }) - - it('uses the fallback link price if the feed price is non-sensical', async () => { - const normalAmount = await getPerformPaymentAmount() - const roundId = 99 - const updatedAt = Math.floor(Date.now() / 1000) - const startedAt = 946684799 - await linkEthFeed - .connect(owner) - .updateRoundData(roundId, -100, updatedAt, startedAt) - const amountWithNegativeFeed = await getPerformPaymentAmount() - await linkEthFeed - .connect(owner) - .updateRoundData(roundId, 0, updatedAt, startedAt) - const amountWithZeroFeed = await getPerformPaymentAmount() - assert.isTrue(normalAmount.lt(amountWithNegativeFeed)) - assert.isTrue(normalAmount.lt(amountWithZeroFeed)) - }) - - it('reverts if the same caller calls twice in a row', async () => { - await registry.connect(keeper1).performUpkeep(id, '0x') - await evmRevert( - registry.connect(keeper1).performUpkeep(id, '0x'), - 'keepers must take turns', - ) - await registry.connect(keeper2).performUpkeep(id, '0x') - await evmRevert( - registry.connect(keeper2).performUpkeep(id, '0x'), - 'keepers must take turns', - ) - await registry.connect(keeper1).performUpkeep(id, '0x') - }) - - it('has a large enough gas overhead to cover upkeeps that use all their gas [ @skip-coverage ]', async () => { - await mock.setPerformGasToBurn(executeGas) - await mock.setCanPerform(true) - const gas = executeGas.add(PERFORM_GAS_OVERHEAD) - const performData = '0xc0ffeec0ffee' - const tx = await registry - .connect(keeper1) - .performUpkeep(id, performData, { gasLimit: gas }) - const receipt = await tx.wait() - const eventLog = receipt?.events - - assert.equal(eventLog?.length, 2) - assert.equal(eventLog?.[1].event, 'UpkeepPerformed') - assert.equal(eventLog?.[1].args?.[0].toNumber(), id.toNumber()) - assert.equal(eventLog?.[1].args?.[1], true) - assert.equal(eventLog?.[1].args?.[2], await keeper1.getAddress()) - assert.isNotEmpty(eventLog?.[1].args?.[3]) - assert.equal(eventLog?.[1].args?.[4], performData) - }) - }) - }) - - describe('#withdrawFunds', () => { - beforeEach(async () => { - await linkToken.connect(keeper1).approve(registry.address, toWei('100')) - await registry.connect(keeper1).addFunds(id, toWei('1')) - }) - - it('reverts if called by anyone but the admin', async () => { - await evmRevert( - registry - .connect(owner) - .withdrawFunds(id.add(1).toNumber(), await payee1.getAddress()), - 'only callable by admin', - ) - }) - - it('reverts if called on an uncanceled upkeep', async () => { - await evmRevert( - registry.connect(admin).withdrawFunds(id, await payee1.getAddress()), - 'upkeep must be canceled', - ) - }) - - it('reverts if called with the 0 address', async () => { - await evmRevert( - registry.connect(admin).withdrawFunds(id, zeroAddress), - 'cannot send to zero address', - ) - }) - - describe('after the registration is cancelled', () => { - beforeEach(async () => { - await registry.connect(owner).cancelUpkeep(id) - }) - - it('moves the funds out and updates the balance', async () => { - const payee1Before = await linkToken.balanceOf( - await payee1.getAddress(), - ) - const registryBefore = await linkToken.balanceOf(registry.address) - - let registration = await registry.getUpkeep(id) - assert.isTrue(toWei('1').eq(registration.balance)) - - await registry - .connect(admin) - .withdrawFunds(id, await payee1.getAddress()) - - const payee1After = await linkToken.balanceOf(await payee1.getAddress()) - const registryAfter = await linkToken.balanceOf(registry.address) - - assert.isTrue(payee1Before.add(toWei('1')).eq(payee1After)) - assert.isTrue(registryBefore.sub(toWei('1')).eq(registryAfter)) - - registration = await registry.getUpkeep(id) - assert.equal(0, registration.balance.toNumber()) - }) - }) - }) - - describe('#cancelUpkeep', () => { - it('reverts if the ID is not valid', async () => { - await evmRevert( - registry.connect(owner).cancelUpkeep(id.add(1).toNumber()), - 'too late to cancel upkeep', - ) - }) - - it('reverts if called by a non-owner/non-admin', async () => { - await evmRevert( - registry.connect(keeper1).cancelUpkeep(id), - 'only owner or admin', - ) - }) - - describe('when called by the owner', async () => { - it('sets the registration to invalid immediately', async () => { - const tx = await registry.connect(owner).cancelUpkeep(id) - const receipt = await tx.wait() - const registration = await registry.getUpkeep(id) - assert.equal( - registration.maxValidBlocknumber.toNumber(), - receipt.blockNumber, - ) - }) - - it('emits an event', async () => { - const tx = await registry.connect(owner).cancelUpkeep(id) - const receipt = await tx.wait() - await expect(tx) - .to.emit(registry, 'UpkeepCanceled') - .withArgs(id, BigNumber.from(receipt.blockNumber)) - }) - - it('updates the canceled registrations list', async () => { - let canceled = await registry.callStatic.getCanceledUpkeepList() - assert.deepEqual([], canceled) - - await registry.connect(owner).cancelUpkeep(id) - - canceled = await registry.callStatic.getCanceledUpkeepList() - assert.deepEqual([id], canceled) - }) - - it('immediately prevents upkeep', async () => { - await registry.connect(owner).cancelUpkeep(id) - - await evmRevert( - registry.connect(keeper2).performUpkeep(id, '0x'), - 'invalid upkeep id', - ) - }) - - it('does not revert if reverts if called multiple times', async () => { - await registry.connect(owner).cancelUpkeep(id) - await evmRevert( - registry.connect(owner).cancelUpkeep(id), - 'too late to cancel upkeep', - ) - }) - - describe('when called by the owner when the admin has just canceled', () => { - let oldExpiration: BigNumber - - beforeEach(async () => { - await registry.connect(admin).cancelUpkeep(id) - const registration = await registry.getUpkeep(id) - oldExpiration = registration.maxValidBlocknumber - }) - - it('allows the owner to cancel it more quickly', async () => { - await registry.connect(owner).cancelUpkeep(id) - - const registration = await registry.getUpkeep(id) - const newExpiration = registration.maxValidBlocknumber - assert.isTrue(newExpiration.lt(oldExpiration)) - }) - }) - }) - - describe('when called by the admin', async () => { - const delay = 50 - - it('sets the registration to invalid in 50 blocks', async () => { - const tx = await registry.connect(admin).cancelUpkeep(id) - const receipt = await tx.wait() - const registration = await registry.getUpkeep(id) - assert.equal( - registration.maxValidBlocknumber.toNumber(), - receipt.blockNumber + 50, - ) - }) - - it('emits an event', async () => { - const tx = await registry.connect(admin).cancelUpkeep(id) - const receipt = await tx.wait() - await expect(tx) - .to.emit(registry, 'UpkeepCanceled') - .withArgs(id, BigNumber.from(receipt.blockNumber + delay)) - }) - - it('updates the canceled registrations list', async () => { - let canceled = await registry.callStatic.getCanceledUpkeepList() - assert.deepEqual([], canceled) - - await registry.connect(admin).cancelUpkeep(id) - - canceled = await registry.callStatic.getCanceledUpkeepList() - assert.deepEqual([id], canceled) - }) - - it('immediately prevents upkeep', async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(id, toWei('100')) - await registry.connect(admin).cancelUpkeep(id) - await registry.connect(keeper2).performUpkeep(id, '0x') // still works - - for (let i = 0; i < delay; i++) { - await ethers.provider.send('evm_mine', []) - } - - await evmRevert( - registry.connect(keeper2).performUpkeep(id, '0x'), - 'invalid upkeep id', - ) - }) - - it('reverts if called again by the admin', async () => { - await registry.connect(admin).cancelUpkeep(id) - - await evmRevert( - registry.connect(admin).cancelUpkeep(id), - 'too late to cancel upkeep', - ) - }) - - it('does not revert or double add the cancellation record if called by the owner immediately after', async () => { - await registry.connect(admin).cancelUpkeep(id) - - await registry.connect(owner).cancelUpkeep(id) - - const canceled = await registry.callStatic.getCanceledUpkeepList() - assert.deepEqual([id], canceled) - }) - - it('reverts if called by the owner after the timeout', async () => { - await registry.connect(admin).cancelUpkeep(id) - - for (let i = 0; i < delay; i++) { - await ethers.provider.send('evm_mine', []) - } - - await evmRevert( - registry.connect(owner).cancelUpkeep(id), - 'too late to cancel upkeep', - ) - }) - }) - }) - - describe('#withdrawPayment', () => { - beforeEach(async () => { - await linkToken.connect(owner).approve(registry.address, toWei('100')) - await registry.connect(owner).addFunds(id, toWei('100')) - await registry.connect(keeper1).performUpkeep(id, '0x') - }) - - it('reverts if called by anyone but the payee', async () => { - await evmRevert( - registry - .connect(payee2) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ), - 'only callable by payee', - ) - }) - - it('reverts if called with the 0 address', async () => { - await evmRevert( - registry - .connect(payee2) - .withdrawPayment(await keeper1.getAddress(), zeroAddress), - 'cannot send to zero address', - ) - }) - - it('updates the balances', async () => { - const to = await nonkeeper.getAddress() - const keeperBefore = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - const registrationBefore = (await registry.getUpkeep(id)).balance - const toLinkBefore = await linkToken.balanceOf(to) - const registryLinkBefore = await linkToken.balanceOf(registry.address) - - //// Do the thing - await registry - .connect(payee1) - .withdrawPayment(await keeper1.getAddress(), to) - - const keeperAfter = ( - await registry.getKeeperInfo(await keeper1.getAddress()) - ).balance - const registrationAfter = (await registry.getUpkeep(id)).balance - const toLinkAfter = await linkToken.balanceOf(to) - const registryLinkAfter = await linkToken.balanceOf(registry.address) - - assert.isTrue(keeperAfter.eq(BigNumber.from(0))) - assert.isTrue(registrationBefore.eq(registrationAfter)) - assert.isTrue(toLinkBefore.add(keeperBefore).eq(toLinkAfter)) - assert.isTrue(registryLinkBefore.sub(keeperBefore).eq(registryLinkAfter)) - }) - - it('emits a log announcing the withdrawal', async () => { - const balance = (await registry.getKeeperInfo(await keeper1.getAddress())) - .balance - const tx = await registry - .connect(payee1) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ) - await expect(tx) - .to.emit(registry, 'PaymentWithdrawn') - .withArgs( - await keeper1.getAddress(), - balance, - await nonkeeper.getAddress(), - await payee1.getAddress(), - ) - }) - }) - - describe('#transferPayeeship', () => { - it('reverts when called by anyone but the current payee', async () => { - await evmRevert( - registry - .connect(payee2) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ), - 'only callable by payee', - ) - }) - - it('reverts when transferring to self', async () => { - await evmRevert( - registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee1.getAddress(), - ), - 'cannot transfer to self', - ) - }) - - it('does not change the payee', async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - - const info = await registry.getKeeperInfo(await keeper1.getAddress()) - assert.equal(await payee1.getAddress(), info.payee) - }) - - it('emits an event announcing the new payee', async () => { - const tx = await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - await expect(tx) - .to.emit(registry, 'PayeeshipTransferRequested') - .withArgs( - await keeper1.getAddress(), - await payee1.getAddress(), - await payee2.getAddress(), - ) - }) - - it('does not emit an event when called with the same proposal', async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - - const tx = await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - const receipt = await tx.wait() - assert.equal(0, receipt.logs.length) - }) - }) - - describe('#acceptPayeeship', () => { - beforeEach(async () => { - await registry - .connect(payee1) - .transferPayeeship( - await keeper1.getAddress(), - await payee2.getAddress(), - ) - }) - - it('reverts when called by anyone but the proposed payee', async () => { - await evmRevert( - registry.connect(payee1).acceptPayeeship(await keeper1.getAddress()), - 'only callable by proposed payee', - ) - }) - - it('emits an event announcing the new payee', async () => { - const tx = await registry - .connect(payee2) - .acceptPayeeship(await keeper1.getAddress()) - await expect(tx) - .to.emit(registry, 'PayeeshipTransferred') - .withArgs( - await keeper1.getAddress(), - await payee1.getAddress(), - await payee2.getAddress(), - ) - }) - - it('does change the payee', async () => { - await registry.connect(payee2).acceptPayeeship(await keeper1.getAddress()) - - const info = await registry.getKeeperInfo(await keeper1.getAddress()) - assert.equal(await payee2.getAddress(), info.payee) - }) - }) - - describe('#setConfig', () => { - const payment = BigNumber.from(1) - const flatFee = BigNumber.from(2) - const checks = BigNumber.from(3) - const staleness = BigNumber.from(4) - const ceiling = BigNumber.from(5) - const maxGas = BigNumber.from(6) - const fbGasEth = BigNumber.from(7) - const fbLinkEth = BigNumber.from(8) - - it('reverts when called by anyone but the proposed owner', async () => { - await evmRevert( - registry - .connect(payee1) - .setConfig( - payment, - flatFee, - checks, - maxGas, - staleness, - gasCeilingMultiplier, - fbGasEth, - fbLinkEth, - ), - 'Only callable by owner', - ) - }) - - it('updates the config', async () => { - const old = await registry.getConfig() - const oldFlatFee = await registry.getFlatFee() - assert.isTrue(paymentPremiumPPB.eq(old.paymentPremiumPPB)) - assert.isTrue(flatFeeMicroLink.eq(oldFlatFee)) - assert.isTrue(blockCountPerTurn.eq(old.blockCountPerTurn)) - assert.isTrue(stalenessSeconds.eq(old.stalenessSeconds)) - assert.isTrue(gasCeilingMultiplier.eq(old.gasCeilingMultiplier)) - - await registry - .connect(owner) - .setConfig( - payment, - flatFee, - checks, - maxGas, - staleness, - ceiling, - fbGasEth, - fbLinkEth, - ) - - const updated = await registry.getConfig() - const newFlatFee = await registry.getFlatFee() - assert.equal(updated.paymentPremiumPPB, payment.toNumber()) - assert.equal(newFlatFee, flatFee.toNumber()) - assert.equal(updated.blockCountPerTurn, checks.toNumber()) - assert.equal(updated.stalenessSeconds, staleness.toNumber()) - assert.equal(updated.gasCeilingMultiplier, ceiling.toNumber()) - assert.equal(updated.checkGasLimit, maxGas.toNumber()) - assert.equal(updated.fallbackGasPrice.toNumber(), fbGasEth.toNumber()) - assert.equal(updated.fallbackLinkPrice.toNumber(), fbLinkEth.toNumber()) - }) - - it('emits an event', async () => { - const tx = await registry - .connect(owner) - .setConfig( - payment, - flatFee, - checks, - maxGas, - staleness, - ceiling, - fbGasEth, - fbLinkEth, - ) - await expect(tx) - .to.emit(registry, 'ConfigSet') - .withArgs( - payment, - checks, - maxGas, - staleness, - ceiling, - fbGasEth, - fbLinkEth, - ) - }) - }) - - describe('#onTokenTransfer', () => { - const amount = toWei('1') - - it('reverts if not called by the LINK token', async () => { - const data = ethers.utils.defaultAbiCoder.encode( - ['uint256'], - [id.toNumber().toString()], - ) - - await evmRevert( - registry - .connect(keeper1) - .onTokenTransfer(await keeper1.getAddress(), amount, data), - 'only callable through LINK', - ) - }) - - it('reverts if not called with more or less than 32 bytes', async () => { - const longData = ethers.utils.defaultAbiCoder.encode( - ['uint256', 'uint256'], - ['33', '34'], - ) - const shortData = '0x12345678' - - await evmRevert( - linkToken - .connect(owner) - .transferAndCall(registry.address, amount, longData), - ) - await evmRevert( - linkToken - .connect(owner) - .transferAndCall(registry.address, amount, shortData), - ) - }) - - it('reverts if the upkeep is canceled', async () => { - await registry.connect(admin).cancelUpkeep(id) - await evmRevert( - registry.connect(keeper1).addFunds(id, amount), - 'upkeep must be active', - ) - }) - - it('updates the funds of the job id passed', async () => { - const data = ethers.utils.defaultAbiCoder.encode( - ['uint256'], - [id.toNumber().toString()], - ) - - const before = (await registry.getUpkeep(id)).balance - await linkToken - .connect(owner) - .transferAndCall(registry.address, amount, data) - const after = (await registry.getUpkeep(id)).balance - - assert.isTrue(before.add(amount).eq(after)) - }) - }) - - describe('#recoverFunds', () => { - const sent = toWei('7') - - beforeEach(async () => { - await linkToken.connect(keeper1).approve(registry.address, toWei('100')) - - // add funds to upkeep 1 and perform and withdraw some payment - const tx = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - const id1 = await getUpkeepID(tx) - await registry.connect(keeper1).addFunds(id1, toWei('5')) - await registry.connect(keeper1).performUpkeep(id1, '0x') - await registry.connect(keeper2).performUpkeep(id1, '0x') - await registry.connect(keeper3).performUpkeep(id1, '0x') - await registry - .connect(payee1) - .withdrawPayment( - await keeper1.getAddress(), - await nonkeeper.getAddress(), - ) - - // transfer funds directly to the registry - await linkToken.connect(keeper1).transfer(registry.address, sent) - - // add funds to upkeep 2 and perform and withdraw some payment - const tx2 = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - const id2 = await getUpkeepID(tx2) - await registry.connect(keeper1).addFunds(id2, toWei('5')) - await registry.connect(keeper1).performUpkeep(id2, '0x') - await registry.connect(keeper2).performUpkeep(id2, '0x') - await registry.connect(keeper3).performUpkeep(id2, '0x') - await registry - .connect(payee2) - .withdrawPayment( - await keeper2.getAddress(), - await nonkeeper.getAddress(), - ) - - // transfer funds using onTokenTransfer - const data = ethers.utils.defaultAbiCoder.encode( - ['uint256'], - [id2.toNumber().toString()], - ) - await linkToken - .connect(owner) - .transferAndCall(registry.address, toWei('1'), data) - - // remove a keeper - await registry - .connect(owner) - .setKeepers( - [await keeper1.getAddress(), await keeper2.getAddress()], - [await payee1.getAddress(), await payee2.getAddress()], - ) - - // withdraw some funds - await registry.connect(owner).cancelUpkeep(id1) - await registry.connect(admin).withdrawFunds(id1, await admin.getAddress()) - }) - - it('reverts if not called by owner', async () => { - await evmRevert( - registry.connect(keeper1).recoverFunds(), - 'Only callable by owner', - ) - }) - - it('allows any funds that have been accidentally transfered to be moved', async () => { - const balanceBefore = await linkToken.balanceOf(registry.address) - - await linkToken.balanceOf(registry.address) - - await registry.connect(owner).recoverFunds() - const balanceAfter = await linkToken.balanceOf(registry.address) - assert.isTrue(balanceBefore.eq(balanceAfter.add(sent))) - }) - }) - - describe('#pause', () => { - it('reverts if called by a non-owner', async () => { - await evmRevert( - registry.connect(keeper1).pause(), - 'Only callable by owner', - ) - }) - - it('marks the contract as paused', async () => { - assert.isFalse(await registry.paused()) - - await registry.connect(owner).pause() - - assert.isTrue(await registry.paused()) - }) - }) - - describe('#unpause', () => { - beforeEach(async () => { - await registry.connect(owner).pause() - }) - - it('reverts if called by a non-owner', async () => { - await evmRevert( - registry.connect(keeper1).unpause(), - 'Only callable by owner', - ) - }) - - it('marks the contract as not paused', async () => { - assert.isTrue(await registry.paused()) - - await registry.connect(owner).unpause() - - assert.isFalse(await registry.paused()) - }) - }) - - describe('#getMaxPaymentForGas', () => { - const gasAmounts = [100000, 10000000] - const premiums = [0, 250000000] - const flatFees = [0, 1000000] - it('calculates the max fee approptiately', async () => { - for (let idx = 0; idx < gasAmounts.length; idx++) { - const gas = gasAmounts[idx] - for (let jdx = 0; jdx < premiums.length; jdx++) { - const premium = premiums[jdx] - for (let kdx = 0; kdx < flatFees.length; kdx++) { - const flatFee = flatFees[kdx] - await registry - .connect(owner) - .setConfig( - premium, - flatFee, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - gasCeilingMultiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - const price = await registry.getMaxPaymentForGas(gas) - expect(price).to.equal(linkForGas(gas, premium, flatFee)) - } - } - } - }) - }) - - describe('#checkUpkeep / #performUpkeep', () => { - const performData = '0xc0ffeec0ffee' - const multiplier = BigNumber.from(10) - const flatFee = BigNumber.from('100000') //0.1 LINK - const callGasPrice = 1 - - it('uses the same minimum balance calculation [ @skip-coverage ]', async () => { - await registry - .connect(owner) - .setConfig( - paymentPremiumPPB, - flatFee, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - multiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - await linkToken.connect(owner).approve(registry.address, toWei('100')) - - const tx1 = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - const upkeepID1 = await getUpkeepID(tx1) - const tx2 = await registry - .connect(owner) - .registerUpkeep( - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - ) - const upkeepID2 = await getUpkeepID(tx2) - await mock.setCanCheck(true) - await mock.setCanPerform(true) - // upkeep 1 is underfunded, 2 is funded - const minBalance1 = (await registry.getMaxPaymentForGas(executeGas)).sub( - 1, - ) - const minBalance2 = await registry.getMaxPaymentForGas(executeGas) - await registry.connect(owner).addFunds(upkeepID1, minBalance1) - await registry.connect(owner).addFunds(upkeepID2, minBalance2) - // upkeep 1 check should revert, 2 should succeed - await evmRevert( - registry - .connect(zeroAddress) - .callStatic.checkUpkeep(upkeepID1, await keeper1.getAddress(), { - gasPrice: callGasPrice, - }), - ) - await registry - .connect(zeroAddress) - .callStatic.checkUpkeep(upkeepID2, await keeper1.getAddress(), { - gasPrice: callGasPrice, - }) - // upkeep 1 perform should revert, 2 should succeed - await evmRevert( - registry - .connect(keeper1) - .performUpkeep(upkeepID1, performData, { gasLimit: extraGas }), - 'insufficient funds', - ) - await registry - .connect(keeper1) - .performUpkeep(upkeepID2, performData, { gasLimit: extraGas }) - }) - }) - - describe('#getMinBalanceForUpkeep / #checkUpkeep', () => { - it('calculates the minimum balance appropriately', async () => { - const oneWei = BigNumber.from('1') - await linkToken.connect(keeper1).approve(registry.address, toWei('100')) - await mock.setCanCheck(true) - await mock.setCanPerform(true) - const minBalance = await registry.getMinBalanceForUpkeep(id) - const tooLow = minBalance.sub(oneWei) - await registry.connect(keeper1).addFunds(id, tooLow) - await evmRevert( - registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()), - 'insufficient funds', - ) - await registry.connect(keeper1).addFunds(id, oneWei) - await registry - .connect(zeroAddress) - .callStatic.checkUpkeep(id, await keeper1.getAddress()) - }) - }) -}) diff --git a/contracts/test/v0.7/Operator.test.ts b/contracts/test/v0.7/Operator.test.ts deleted file mode 100644 index 4af846576b3..00000000000 --- a/contracts/test/v0.7/Operator.test.ts +++ /dev/null @@ -1,3819 +0,0 @@ -import { ethers } from 'hardhat' -import { - publicAbi, - toBytes32String, - toWei, - stringToBytes, - increaseTime5Minutes, - getLog, -} from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { - BigNumber, - constants, - Contract, - ContractFactory, - ContractReceipt, - ContractTransaction, - Signer, -} from 'ethers' -import { getUsers, Roles } from '../test-helpers/setup' -import { bigNumEquals, evmRevert } from '../test-helpers/matchers' -import type { providers } from 'ethers' -import { - convertCancelParams, - convertCancelByRequesterParams, - convertFufillParams, - convertFulfill2Params, - decodeRunRequest, - encodeOracleRequest, - encodeRequestOracleData, - RunRequest, -} from '../test-helpers/oracle' - -let v7ConsumerFactory: ContractFactory -let basicConsumerFactory: ContractFactory -let multiWordConsumerFactory: ContractFactory -let gasGuzzlingConsumerFactory: ContractFactory -let getterSetterFactory: ContractFactory -let maliciousRequesterFactory: ContractFactory -let maliciousConsumerFactory: ContractFactory -let maliciousMultiWordConsumerFactory: ContractFactory -let operatorFactory: ContractFactory -let forwarderFactory: ContractFactory -let linkTokenFactory: ContractFactory -const zeroAddress = ethers.constants.AddressZero - -let roles: Roles - -before(async () => { - const users = await getUsers() - - roles = users.roles - v7ConsumerFactory = await ethers.getContractFactory( - 'src/v0.7/tests/Consumer.sol:Consumer', - ) - basicConsumerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/BasicConsumer.sol:BasicConsumer', - ) - multiWordConsumerFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MultiWordConsumer.sol:MultiWordConsumer', - ) - gasGuzzlingConsumerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/GasGuzzlingConsumer.sol:GasGuzzlingConsumer', - ) - getterSetterFactory = await ethers.getContractFactory( - 'src/v0.4/tests/GetterSetter.sol:GetterSetter', - ) - maliciousRequesterFactory = await ethers.getContractFactory( - 'src/v0.4/tests/MaliciousRequester.sol:MaliciousRequester', - ) - maliciousConsumerFactory = await ethers.getContractFactory( - 'src/v0.4/tests/MaliciousConsumer.sol:MaliciousConsumer', - ) - maliciousMultiWordConsumerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/MaliciousMultiWordConsumer.sol:MaliciousMultiWordConsumer', - ) - operatorFactory = await ethers.getContractFactory( - 'src/v0.7/Operator.sol:Operator', - ) - forwarderFactory = await ethers.getContractFactory( - 'src/v0.7/AuthorizedForwarder.sol:AuthorizedForwarder', - ) - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - ) -}) - -describe('Operator', () => { - let fHash: string - let specId: string - let to: string - let link: Contract - let operator: Contract - let forwarder1: Contract - let forwarder2: Contract - let owner: Signer - - beforeEach(async () => { - fHash = getterSetterFactory.interface.getSighash('requestedBytes32') - specId = - '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' - to = '0x80e29acb842498fe6591f020bd82766dce619d43' - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - owner = roles.defaultAccount - operator = await operatorFactory - .connect(owner) - .deploy(link.address, await owner.getAddress()) - await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.oracleNode.getAddress()]) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(operator, [ - 'acceptAuthorizedReceivers', - 'acceptOwnableContracts', - 'cancelOracleRequest', - 'cancelOracleRequestByRequester', - 'distributeFunds', - 'fulfillOracleRequest', - 'fulfillOracleRequest2', - 'getAuthorizedSenders', - 'getChainlinkToken', - 'getExpiryTime', - 'isAuthorizedSender', - 'onTokenTransfer', - 'operatorRequest', - 'oracleRequest', - 'ownerForward', - 'ownerTransferAndCall', - 'setAuthorizedSenders', - 'setAuthorizedSendersOn', - 'transferOwnableContracts', - 'typeAndVersion', - 'withdraw', - 'withdrawable', - // Ownable methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#typeAndVersion', () => { - it('describes the operator', async () => { - assert.equal(await operator.typeAndVersion(), 'Operator 1.0.0') - }) - }) - - describe('#transferOwnableContracts', () => { - beforeEach(async () => { - forwarder1 = await forwarderFactory - .connect(owner) - .deploy(link.address, operator.address, zeroAddress, '0x') - forwarder2 = await forwarderFactory - .connect(owner) - .deploy(link.address, operator.address, zeroAddress, '0x') - }) - - describe('being called by the owner', () => { - it('cannot transfer to self', async () => { - await evmRevert( - operator - .connect(owner) - .transferOwnableContracts([forwarder1.address], operator.address), - 'Cannot transfer to self', - ) - }) - - it('emits an ownership transfer request event', async () => { - const tx = await operator - .connect(owner) - .transferOwnableContracts( - [forwarder1.address, forwarder2.address], - await roles.oracleNode1.getAddress(), - ) - const receipt = await tx.wait() - assert.equal(receipt?.events?.length, 2) - const log1 = receipt?.events?.[0] - assert.equal(log1?.event, 'OwnershipTransferRequested') - assert.equal(log1?.address, forwarder1.address) - assert.equal(log1?.args?.[0], operator.address) - assert.equal(log1?.args?.[1], await roles.oracleNode1.getAddress()) - const log2 = receipt?.events?.[1] - assert.equal(log2?.event, 'OwnershipTransferRequested') - assert.equal(log2?.address, forwarder2.address) - assert.equal(log2?.args?.[0], operator.address) - assert.equal(log2?.args?.[1], await roles.oracleNode1.getAddress()) - }) - }) - - describe('being called by a non-owner', () => { - it('reverts with message', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .transferOwnableContracts( - [forwarder1.address], - await roles.oracleNode2.getAddress(), - ), - 'Only callable by owner', - ) - }) - }) - }) - - describe('#acceptOwnableContracts', () => { - describe('being called by the owner', () => { - let operator2: Contract - let receipt: ContractReceipt - - beforeEach(async () => { - operator2 = await operatorFactory - .connect(roles.defaultAccount) - .deploy(link.address, await roles.defaultAccount.getAddress()) - forwarder1 = await forwarderFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, zeroAddress, '0x') - forwarder2 = await forwarderFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, zeroAddress, '0x') - await operator - .connect(roles.defaultAccount) - .transferOwnableContracts( - [forwarder1.address, forwarder2.address], - operator2.address, - ) - const tx = await operator2 - .connect(roles.defaultAccount) - .acceptOwnableContracts([forwarder1.address, forwarder2.address]) - receipt = await tx.wait() - }) - - it('sets the new owner on the forwarder', async () => { - assert.equal(await forwarder1.owner(), operator2.address) - }) - - it('emits ownership transferred events', async () => { - assert.equal(receipt?.events?.[0]?.event, 'OwnableContractAccepted') - assert.equal(receipt?.events?.[0]?.args?.[0], forwarder1.address) - - assert.equal(receipt?.events?.[1]?.event, 'OwnershipTransferred') - assert.equal(receipt?.events?.[1]?.address, forwarder1.address) - assert.equal(receipt?.events?.[1]?.args?.[0], operator.address) - assert.equal(receipt?.events?.[1]?.args?.[1], operator2.address) - - assert.equal(receipt?.events?.[2]?.event, 'OwnableContractAccepted') - assert.equal(receipt?.events?.[2]?.args?.[0], forwarder2.address) - - assert.equal(receipt?.events?.[3]?.event, 'OwnershipTransferred') - assert.equal(receipt?.events?.[3]?.address, forwarder2.address) - assert.equal(receipt?.events?.[3]?.args?.[0], operator.address) - assert.equal(receipt?.events?.[3]?.args?.[1], operator2.address) - }) - }) - - describe('being called by a non-owner authorized sender', () => { - it('does not revert', async () => { - await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.oracleNode1.getAddress()]) - - await operator.connect(roles.oracleNode1).acceptOwnableContracts([]) - }) - }) - - describe('being called by a non owner', () => { - it('reverts with message', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .acceptOwnableContracts([await roles.oracleNode2.getAddress()]), - 'Cannot set authorized senders', - ) - }) - }) - }) - - describe('#distributeFunds', () => { - describe('when called with empty arrays', () => { - it('reverts with invalid array message', async () => { - await evmRevert( - operator.connect(roles.defaultAccount).distributeFunds([], []), - 'Invalid array length(s)', - ) - }) - }) - - describe('when called with unequal array lengths', () => { - it('reverts with invalid array message', async () => { - const receivers = [ - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - const amounts = [1, 2, 3] - await evmRevert( - operator - .connect(roles.defaultAccount) - .distributeFunds(receivers, amounts), - 'Invalid array length(s)', - ) - }) - }) - - describe('when called with not enough ETH', () => { - it('reverts with subtraction overflow message', async () => { - const amountToSend = toWei('2') - const ethSent = toWei('1') - await evmRevert( - operator - .connect(roles.defaultAccount) - .distributeFunds( - [await roles.oracleNode2.getAddress()], - [amountToSend], - { - value: ethSent, - }, - ), - 'SafeMath: subtraction overflow', - ) - }) - }) - - describe('when called with too much ETH', () => { - it('reverts with too much ETH message', async () => { - const amountToSend = toWei('2') - const ethSent = toWei('3') - await evmRevert( - operator - .connect(roles.defaultAccount) - .distributeFunds( - [await roles.oracleNode2.getAddress()], - [amountToSend], - { - value: ethSent, - }, - ), - 'Too much ETH sent', - ) - }) - }) - - describe('when called with correct values', () => { - it('updates the balances', async () => { - const node2BalanceBefore = await roles.oracleNode2.getBalance() - const node3BalanceBefore = await roles.oracleNode3.getBalance() - const receivers = [ - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - const sendNode2 = toWei('2') - const sendNode3 = toWei('3') - const totalAmount = toWei('5') - const amounts = [sendNode2, sendNode3] - - await operator - .connect(roles.defaultAccount) - .distributeFunds(receivers, amounts, { value: totalAmount }) - - const node2BalanceAfter = await roles.oracleNode2.getBalance() - const node3BalanceAfter = await roles.oracleNode3.getBalance() - - assert.equal( - node2BalanceAfter.sub(node2BalanceBefore).toString(), - sendNode2.toString(), - ) - - assert.equal( - node3BalanceAfter.sub(node3BalanceBefore).toString(), - sendNode3.toString(), - ) - }) - }) - }) - - describe('#setAuthorizedSenders', () => { - let newSenders: string[] - let receipt: ContractReceipt - describe('when called by the owner', () => { - describe('setting 3 authorized senders', () => { - beforeEach(async () => { - newSenders = [ - await roles.oracleNode1.getAddress(), - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - const tx = await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders(newSenders) - receipt = await tx.wait() - }) - - it('adds the authorized nodes', async () => { - const authorizedSenders = await operator.getAuthorizedSenders() - assert.equal(newSenders.length, authorizedSenders.length) - for (let i = 0; i < authorizedSenders.length; i++) { - assert.equal(authorizedSenders[i], newSenders[i]) - } - }) - - it('emits an event on the Operator', async () => { - assert.equal(receipt.events?.length, 1) - - const encodedSenders1 = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'address'], - [newSenders, await roles.defaultAccount.getAddress()], - ) - - const responseEvent1 = receipt.events?.[0] - assert.equal(responseEvent1?.event, 'AuthorizedSendersChanged') - assert.equal(responseEvent1?.data, encodedSenders1) - }) - - it('replaces the authorized nodes', async () => { - const originalAuthorization = await operator - .connect(roles.defaultAccount) - .isAuthorizedSender(await roles.oracleNode.getAddress()) - assert.isFalse(originalAuthorization) - }) - - after(async () => { - await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.oracleNode.getAddress()]) - }) - }) - - describe('setting 0 authorized senders', () => { - beforeEach(async () => { - newSenders = [] - }) - - it('reverts with a minimum senders message', async () => { - await evmRevert( - operator - .connect(roles.defaultAccount) - .setAuthorizedSenders(newSenders), - 'Must have at least 1 sender', - ) - }) - }) - }) - - describe('when called by an authorized sender', () => { - beforeEach(async () => { - newSenders = [await roles.oracleNode1.getAddress()] - await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders(newSenders) - }) - - it('succeeds', async () => { - await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.stranger.getAddress()]) - }) - }) - - describe('when called by a non-owner', () => { - it('cannot add an authorized node', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .setAuthorizedSenders([await roles.stranger.getAddress()]), - 'Cannot set authorized senders', - ) - }) - }) - }) - - describe('#setAuthorizedSendersOn', () => { - let newSenders: string[] - - beforeEach(async () => { - await operator - .connect(roles.defaultAccount) - .setAuthorizedSenders([await roles.oracleNode1.getAddress()]) - newSenders = [ - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - - forwarder1 = await forwarderFactory - .connect(owner) - .deploy(link.address, operator.address, zeroAddress, '0x') - forwarder2 = await forwarderFactory - .connect(owner) - .deploy(link.address, operator.address, zeroAddress, '0x') - }) - - describe('when called by a non-authorized sender', () => { - it('reverts', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .setAuthorizedSendersOn(newSenders, [forwarder1.address]), - 'Cannot set authorized senders', - ) - }) - }) - - describe('when called by an owner', () => { - it('does not revert', async () => { - await operator - .connect(roles.defaultAccount) - .setAuthorizedSendersOn( - [forwarder1.address, forwarder2.address], - newSenders, - ) - }) - }) - - describe('when called by an authorized sender', () => { - it('does not revert', async () => { - await operator - .connect(roles.oracleNode1) - .setAuthorizedSendersOn( - [forwarder1.address, forwarder2.address], - newSenders, - ) - }) - - it('does revert with 0 senders', async () => { - await operator - .connect(roles.oracleNode1) - .setAuthorizedSendersOn( - [forwarder1.address, forwarder2.address], - newSenders, - ) - }) - - it('emits a log announcing the change and who made it', async () => { - const targets = [forwarder1.address, forwarder2.address] - const tx = await operator - .connect(roles.oracleNode1) - .setAuthorizedSendersOn(targets, newSenders) - - const receipt = await tx.wait() - const encodedArgs = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'address[]', 'address'], - [targets, newSenders, await roles.oracleNode1.getAddress()], - ) - - const event1 = receipt.events?.[0] - assert.equal(event1?.event, 'TargetsUpdatedAuthorizedSenders') - assert.equal(event1?.address, operator.address) - assert.equal(event1?.data, encodedArgs) - }) - - it('updates the sender list on each of the targets', async () => { - const tx = await operator - .connect(roles.oracleNode1) - .setAuthorizedSendersOn( - [forwarder1.address, forwarder2.address], - newSenders, - ) - - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 3, receipt.toString()) - const encodedSenders = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'address'], - [newSenders, operator.address], - ) - - const event1 = receipt.events?.[1] - assert.equal(event1?.event, 'AuthorizedSendersChanged') - assert.equal(event1?.address, forwarder1.address) - assert.equal(event1?.data, encodedSenders) - - const event2 = receipt.events?.[2] - assert.equal(event2?.event, 'AuthorizedSendersChanged') - assert.equal(event2?.address, forwarder2.address) - assert.equal(event2?.data, encodedSenders) - }) - }) - }) - - describe('#acceptAuthorizedReceivers', () => { - let newSenders: string[] - - describe('being called by the owner', () => { - let operator2: Contract - let receipt: ContractReceipt - - beforeEach(async () => { - operator2 = await operatorFactory - .connect(roles.defaultAccount) - .deploy(link.address, await roles.defaultAccount.getAddress()) - forwarder1 = await forwarderFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, zeroAddress, '0x') - forwarder2 = await forwarderFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, zeroAddress, '0x') - await operator - .connect(roles.defaultAccount) - .transferOwnableContracts( - [forwarder1.address, forwarder2.address], - operator2.address, - ) - newSenders = [ - await roles.oracleNode2.getAddress(), - await roles.oracleNode3.getAddress(), - ] - - const tx = await operator2 - .connect(roles.defaultAccount) - .acceptAuthorizedReceivers( - [forwarder1.address, forwarder2.address], - newSenders, - ) - receipt = await tx.wait() - }) - - it('sets the new owner on the forwarder', async () => { - assert.equal(await forwarder1.owner(), operator2.address) - }) - - it('emits ownership transferred events', async () => { - assert.equal(receipt?.events?.[0]?.event, 'OwnableContractAccepted') - assert.equal(receipt?.events?.[0]?.args?.[0], forwarder1.address) - - assert.equal(receipt?.events?.[1]?.event, 'OwnershipTransferred') - assert.equal(receipt?.events?.[1]?.address, forwarder1.address) - assert.equal(receipt?.events?.[1]?.args?.[0], operator.address) - assert.equal(receipt?.events?.[1]?.args?.[1], operator2.address) - - assert.equal(receipt?.events?.[2]?.event, 'OwnableContractAccepted') - assert.equal(receipt?.events?.[2]?.args?.[0], forwarder2.address) - - assert.equal(receipt?.events?.[3]?.event, 'OwnershipTransferred') - assert.equal(receipt?.events?.[3]?.address, forwarder2.address) - assert.equal(receipt?.events?.[3]?.args?.[0], operator.address) - assert.equal(receipt?.events?.[3]?.args?.[1], operator2.address) - - assert.equal( - receipt?.events?.[4]?.event, - 'TargetsUpdatedAuthorizedSenders', - ) - - const encodedSenders = ethers.utils.defaultAbiCoder.encode( - ['address[]', 'address'], - [newSenders, operator2.address], - ) - assert.equal(receipt?.events?.[5]?.event, 'AuthorizedSendersChanged') - assert.equal(receipt?.events?.[5]?.address, forwarder1.address) - assert.equal(receipt?.events?.[5]?.data, encodedSenders) - - assert.equal(receipt?.events?.[6]?.event, 'AuthorizedSendersChanged') - assert.equal(receipt?.events?.[6]?.address, forwarder2.address) - assert.equal(receipt?.events?.[6]?.data, encodedSenders) - }) - }) - - describe('being called by a non owner', () => { - it('reverts with message', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .acceptAuthorizedReceivers( - [forwarder1.address, forwarder2.address], - newSenders, - ), - 'Cannot set authorized senders', - ) - }) - }) - }) - - describe('#onTokenTransfer', () => { - describe('when called from any address but the LINK token', () => { - it('triggers the intended method', async () => { - const callData = encodeOracleRequest( - specId, - to, - fHash, - 0, - constants.HashZero, - ) - - await evmRevert( - operator.onTokenTransfer( - await roles.defaultAccount.getAddress(), - 0, - callData, - ), - ) - }) - }) - - describe('when called from the LINK token', () => { - it('triggers the intended method', async () => { - const callData = encodeOracleRequest( - specId, - to, - fHash, - 0, - constants.HashZero, - ) - - const tx = await link.transferAndCall(operator.address, 0, callData, { - value: 0, - }) - const receipt = await tx.wait() - - assert.equal(3, receipt.logs?.length) - }) - - describe('with no data', () => { - it('reverts', async () => { - await evmRevert( - link.transferAndCall(operator.address, 0, '0x', { - value: 0, - }), - ) - }) - }) - }) - - describe('malicious requester', () => { - let mock: Contract - let requester: Contract - const paymentAmount = toWei('1') - - beforeEach(async () => { - mock = await maliciousRequesterFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(mock.address, paymentAmount) - }) - - it('cannot withdraw from oracle', async () => { - const operatorOriginalBalance = await link.balanceOf(operator.address) - const mockOriginalBalance = await link.balanceOf(mock.address) - - await evmRevert(mock.maliciousWithdraw()) - - const operatorNewBalance = await link.balanceOf(operator.address) - const mockNewBalance = await link.balanceOf(mock.address) - - bigNumEquals(operatorOriginalBalance, operatorNewBalance) - bigNumEquals(mockNewBalance, mockOriginalBalance) - }) - - describe('if the requester tries to create a requestId for another contract', () => { - it('the requesters ID will not match with the oracle contract', async () => { - const tx = await mock.maliciousTargetConsumer(to) - const receipt = await tx.wait() - - const mockRequestId = receipt.logs?.[0].data - const requestId = (receipt.events?.[0].args as any).requestId - assert.notEqual(mockRequestId, requestId) - }) - - it('the target requester can still create valid requests', async () => { - requester = await basicConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - await link.transfer(requester.address, paymentAmount) - await mock.maliciousTargetConsumer(requester.address) - await requester.requestEthereumPrice('USD', paymentAmount) - }) - }) - }) - - it('does not allow recursive calls of onTokenTransfer', async () => { - const requestPayload = encodeOracleRequest( - specId, - to, - fHash, - 0, - constants.HashZero, - ) - - const ottSelector = - operatorFactory.interface.getSighash('onTokenTransfer') - const header = - '000000000000000000000000c5fdf4076b8f3a5357c5e395ab970b5b54098fef' + // to - '0000000000000000000000000000000000000000000000000000000000000539' + // amount - '0000000000000000000000000000000000000000000000000000000000000060' + // offset - '0000000000000000000000000000000000000000000000000000000000000136' // length - - const maliciousPayload = ottSelector + header + requestPayload.slice(2) - - await evmRevert( - link.transferAndCall(operator.address, 0, maliciousPayload, { - value: 0, - }), - ) - }) - }) - - describe('#oracleRequest', () => { - describe('when called through the LINK token', () => { - const paid = 100 - let log: providers.Log | undefined - let receipt: providers.TransactionReceipt - - beforeEach(async () => { - const args = encodeOracleRequest( - specId, - to, - fHash, - 1, - constants.HashZero, - ) - const tx = await link.transferAndCall(operator.address, paid, args) - receipt = await tx.wait() - assert.equal(3, receipt?.logs?.length) - - log = receipt.logs && receipt.logs[2] - }) - - it('logs an event', async () => { - assert.equal(operator.address, log?.address) - - assert.equal(log?.topics?.[1], specId) - - const req = decodeRunRequest(receipt?.logs?.[2]) - assert.equal(await roles.defaultAccount.getAddress(), req.requester) - bigNumEquals(paid, req.payment) - }) - - it('uses the expected event signature', async () => { - // If updating this test, be sure to update models.RunLogTopic. - const eventSignature = - '0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65' - assert.equal(eventSignature, log?.topics?.[0]) - }) - - it('does not allow the same requestId to be used twice', async () => { - const args2 = encodeOracleRequest( - specId, - to, - fHash, - 1, - constants.HashZero, - ) - await evmRevert(link.transferAndCall(operator.address, paid, args2)) - }) - - describe('when called with a payload less than 2 EVM words + function selector', () => { - it('throws an error', async () => { - const funcSelector = - operatorFactory.interface.getSighash('oracleRequest') - const maliciousData = - funcSelector + - '0000000000000000000000000000000000000000000000000000000000000000000' - await evmRevert( - link.transferAndCall(operator.address, paid, maliciousData), - ) - }) - }) - - describe('when called with a payload between 3 and 9 EVM words', () => { - it('throws an error', async () => { - const funcSelector = - operatorFactory.interface.getSighash('oracleRequest') - const maliciousData = - funcSelector + - '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' - await evmRevert( - link.transferAndCall(operator.address, paid, maliciousData), - ) - }) - }) - }) - - describe('when dataVersion is higher than 255', () => { - it('throws an error', async () => { - const paid = 100 - const args = encodeOracleRequest( - specId, - to, - fHash, - 1, - constants.HashZero, - 256, - ) - await evmRevert(link.transferAndCall(operator.address, paid, args)) - }) - }) - - describe('when not called through the LINK token', () => { - it('reverts', async () => { - await evmRevert( - operator - .connect(roles.oracleNode) - .oracleRequest( - '0x0000000000000000000000000000000000000000', - 0, - specId, - to, - fHash, - 1, - 1, - '0x', - ), - ) - }) - }) - }) - - describe('#operatorRequest', () => { - describe('when called through the LINK token', () => { - const paid = 100 - let log: providers.Log | undefined - let receipt: providers.TransactionReceipt - - beforeEach(async () => { - const args = encodeRequestOracleData( - specId, - fHash, - 1, - constants.HashZero, - ) - const tx = await link.transferAndCall(operator.address, paid, args) - receipt = await tx.wait() - assert.equal(3, receipt?.logs?.length) - - log = receipt.logs && receipt.logs[2] - }) - - it('logs an event', async () => { - assert.equal(operator.address, log?.address) - - assert.equal(log?.topics?.[1], specId) - - const req = decodeRunRequest(receipt?.logs?.[2]) - assert.equal(await roles.defaultAccount.getAddress(), req.requester) - bigNumEquals(paid, req.payment) - }) - - it('uses the expected event signature', async () => { - // If updating this test, be sure to update models.RunLogTopic. - const eventSignature = - '0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65' - assert.equal(eventSignature, log?.topics?.[0]) - }) - - it('does not allow the same requestId to be used twice', async () => { - const args2 = encodeRequestOracleData( - specId, - fHash, - 1, - constants.HashZero, - ) - await evmRevert(link.transferAndCall(operator.address, paid, args2)) - }) - - describe('when called with a payload less than 2 EVM words + function selector', () => { - it('throws an error', async () => { - const funcSelector = - operatorFactory.interface.getSighash('oracleRequest') - const maliciousData = - funcSelector + - '0000000000000000000000000000000000000000000000000000000000000000000' - await evmRevert( - link.transferAndCall(operator.address, paid, maliciousData), - ) - }) - }) - - describe('when called with a payload between 3 and 9 EVM words', () => { - it('throws an error', async () => { - const funcSelector = - operatorFactory.interface.getSighash('oracleRequest') - const maliciousData = - funcSelector + - '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' - await evmRevert( - link.transferAndCall(operator.address, paid, maliciousData), - ) - }) - }) - }) - - describe('when dataVersion is higher than 255', () => { - it('throws an error', async () => { - const paid = 100 - const args = encodeRequestOracleData( - specId, - fHash, - 1, - constants.HashZero, - 256, - ) - await evmRevert(link.transferAndCall(operator.address, paid, args)) - }) - }) - - describe('when not called through the LINK token', () => { - it('reverts', async () => { - await evmRevert( - operator - .connect(roles.oracleNode) - .oracleRequest( - '0x0000000000000000000000000000000000000000', - 0, - specId, - to, - fHash, - 1, - 1, - '0x', - ), - ) - }) - }) - }) - - describe('#fulfillOracleRequest', () => { - const response = 'Hi Mom!' - let maliciousRequester: Contract - let basicConsumer: Contract - let maliciousConsumer: Contract - let gasGuzzlingConsumer: Contract - let request: ReturnType - - describe('gas guzzling consumer [ @skip-coverage ]', () => { - beforeEach(async () => { - gasGuzzlingConsumer = await gasGuzzlingConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(gasGuzzlingConsumer.address, paymentAmount) - const tx = - await gasGuzzlingConsumer.gassyRequestEthereumPrice(paymentAmount) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('emits an OracleResponse event', async () => { - const fulfillParams = convertFufillParams(request, response) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 1) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - }) - - describe('cooperative consumer', () => { - beforeEach(async () => { - basicConsumer = await basicConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(basicConsumer.address, paymentAmount) - const currency = 'USD' - const tx = await basicConsumer.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - describe('when called by an unauthorized node', () => { - beforeEach(async () => { - assert.equal( - false, - await operator.isAuthorizedSender( - await roles.stranger.getAddress(), - ), - ) - }) - - it('raises an error', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .fulfillOracleRequest(...convertFufillParams(request, response)), - ) - }) - }) - - describe('when fulfilled with the wrong function', () => { - let v7Consumer - beforeEach(async () => { - v7Consumer = await v7ConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(v7Consumer.address, paymentAmount) - const currency = 'USD' - const tx = await v7Consumer.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('raises an error', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .fulfillOracleRequest(...convertFufillParams(request, response)), - ) - }) - }) - - describe('when called by an authorized node', () => { - it('raises an error if the request ID does not exist', async () => { - request.requestId = ethers.utils.formatBytes32String('DOESNOTEXIST') - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)), - ) - }) - - it('sets the value on the requested contract', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - const currentValue = await basicConsumer.currentPrice() - assert.equal(response, ethers.utils.parseBytes32String(currentValue)) - }) - - it('emits an OracleResponse event', async () => { - const fulfillParams = convertFufillParams(request, response) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 3) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - - it('does not allow a request to be fulfilled twice', async () => { - const response2 = response + ' && Hello World!!' - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response2)), - ) - - const currentValue = await basicConsumer.currentPrice() - assert.equal(response, ethers.utils.parseBytes32String(currentValue)) - }) - }) - - describe('when the oracle does not provide enough gas', () => { - // if updating this defaultGasLimit, be sure it matches with the - // defaultGasLimit specified in store/tx_manager.go - const defaultGasLimit = 500000 - - beforeEach(async () => { - bigNumEquals(0, await operator.withdrawable()) - }) - - it('does not allow the oracle to withdraw the payment', async () => { - await evmRevert( - operator.connect(roles.oracleNode).fulfillOracleRequest( - ...convertFufillParams(request, response, { - gasLimit: 70000, - }), - ), - ) - - bigNumEquals(0, await operator.withdrawable()) - }) - - it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { - await operator.connect(roles.oracleNode).fulfillOracleRequest( - ...convertFufillParams(request, response, { - gasLimit: defaultGasLimit, - }), - ) - - bigNumEquals(request.payment, await operator.withdrawable()) - }) - }) - }) - - describe('with a malicious requester', () => { - beforeEach(async () => { - const paymentAmount = toWei('1') - maliciousRequester = await maliciousRequesterFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousRequester.address, paymentAmount) - }) - - it('cannot cancel before the expiration', async () => { - await evmRevert( - maliciousRequester.maliciousRequestCancel( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ), - ) - }) - - it('cannot call functions on the LINK token through callbacks', async () => { - await evmRevert( - maliciousRequester.request( - specId, - link.address, - ethers.utils.toUtf8Bytes('transfer(address,uint256)'), - ), - ) - }) - - describe('requester lies about amount of LINK sent', () => { - it('the oracle uses the amount of LINK actually paid', async () => { - const tx = await maliciousRequester.maliciousPrice(specId) - const receipt = await tx.wait() - const req = decodeRunRequest(receipt.logs?.[3]) - - assert(toWei('1').eq(req.payment)) - }) - }) - }) - - describe('with a malicious consumer', () => { - const paymentAmount = toWei('1') - - beforeEach(async () => { - maliciousConsumer = await maliciousConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousConsumer.address, paymentAmount) - }) - - describe('fails during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('assertFail(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response2 = 'hack the planet 102' - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response2)), - ) - }) - }) - - describe('calls selfdestruct', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - await maliciousConsumer.remove() - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - }) - - describe('request is canceled during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('cancelRequestOnFulfill(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - const mockBalance = await link.balanceOf(maliciousConsumer.address) - bigNumEquals(mockBalance, 0) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response2 = 'hack the planet 102' - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response2)), - ) - }) - }) - - describe('tries to steal funds from node', () => { - it('is not successful with call', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthCall(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with send', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthSend(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with transfer', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthTransfer(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, response)) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - }) - - describe('when calling an owned contract', () => { - beforeEach(async () => { - forwarder1 = await forwarderFactory - .connect(roles.defaultAccount) - .deploy(link.address, link.address, operator.address, '0x') - }) - - it('does not allow the contract to callback to owned contracts', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('whatever(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - let request = decodeRunRequest(receipt.logs?.[3]) - let responseParams = convertFufillParams(request, response) - // set the params to be the owned address - responseParams[2] = forwarder1.address - - //accept ownership - await operator - .connect(roles.defaultAccount) - .acceptOwnableContracts([forwarder1.address]) - - // do the thing - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...responseParams), - 'Cannot call owned contract', - ) - - await operator - .connect(roles.defaultAccount) - .transferOwnableContracts([forwarder1.address], link.address) - //reverts for a different reason after transferring ownership - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...responseParams), - 'Params do not match request ID', - ) - }) - }) - }) - }) - - describe('#fulfillOracleRequest2', () => { - describe('single word fulfils', () => { - const response = 'Hi mom!' - const responseTypes = ['bytes32'] - const responseValues = [toBytes32String(response)] - let maliciousRequester: Contract - let basicConsumer: Contract - let maliciousConsumer: Contract - let gasGuzzlingConsumer: Contract - let request: ReturnType - let request2: ReturnType - - describe('gas guzzling consumer [ @skip-coverage ]', () => { - beforeEach(async () => { - gasGuzzlingConsumer = await gasGuzzlingConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(gasGuzzlingConsumer.address, paymentAmount) - const tx = - await gasGuzzlingConsumer.gassyRequestEthereumPrice(paymentAmount) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('emits an OracleResponse2 event', async () => { - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 1) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - }) - - describe('cooperative consumer', () => { - beforeEach(async () => { - basicConsumer = await basicConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(basicConsumer.address, paymentAmount) - const currency = 'USD' - const tx = await basicConsumer.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - describe('when called by an unauthorized node', () => { - beforeEach(async () => { - assert.equal( - false, - await operator.isAuthorizedSender( - await roles.stranger.getAddress(), - ), - ) - }) - - it('raises an error', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ), - ) - }) - }) - - describe('when called by an authorized node', () => { - it('raises an error if the request ID does not exist', async () => { - request.requestId = ethers.utils.formatBytes32String('DOESNOTEXIST') - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ), - ) - }) - - it('sets the value on the requested contract', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const currentValue = await basicConsumer.currentPrice() - assert.equal( - response, - ethers.utils.parseBytes32String(currentValue), - ) - }) - - it('emits an OracleResponse2 event', async () => { - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 3) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - - it('does not allow a request to be fulfilled twice', async () => { - const response2 = response + ' && Hello World!!' - const response2Values = [toBytes32String(response2)] - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - response2Values, - ), - ), - ) - - const currentValue = await basicConsumer.currentPrice() - assert.equal( - response, - ethers.utils.parseBytes32String(currentValue), - ) - }) - }) - - describe('when the oracle does not provide enough gas', () => { - // if updating this defaultGasLimit, be sure it matches with the - // defaultGasLimit specified in store/tx_manager.go - const defaultGasLimit = 500000 - - beforeEach(async () => { - bigNumEquals(0, await operator.withdrawable()) - }) - - it('does not allow the oracle to withdraw the payment', async () => { - await evmRevert( - operator.connect(roles.oracleNode).fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - { - gasLimit: 70000, - }, - ), - ), - ) - - bigNumEquals(0, await operator.withdrawable()) - }) - - it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { - await operator.connect(roles.oracleNode).fulfillOracleRequest2( - ...convertFulfill2Params(request, responseTypes, responseValues, { - gasLimit: defaultGasLimit, - }), - ) - - bigNumEquals(request.payment, await operator.withdrawable()) - }) - }) - }) - - describe('with a malicious oracle', () => { - beforeEach(async () => { - // Setup Request 1 - basicConsumer = await basicConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(basicConsumer.address, paymentAmount) - const currency = 'USD' - const tx = await basicConsumer.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - // Setup Request 2 - await link.transfer(basicConsumer.address, paymentAmount) - const tx2 = await basicConsumer.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt2 = await tx2.wait() - request2 = decodeRunRequest(receipt2.logs?.[3]) - }) - - it('cannot spoof requestId in response data by moving calldata offset', async () => { - // Malicious Oracle Fulfill 2 - const functionSelector = '0x6ae0bc76' // fulfillOracleRequest2 - const dataOffset = - '0000000000000000000000000000000000000000000000000000000000000100' // Moved to 0x0124 - const fillerBytes = - '0000000000000000000000000000000000000000000000000000000000000000' - const expectedCalldataStart = request.requestId.slice(2) // 0xe4, this is checked against requestId in validateMultiWordResponseId - const dataSize = - '0000000000000000000000000000000000000000000000000000000000000040' // Two 32 byte blocks - const maliciousCalldataId = request2.requestId.slice(2) // 0x0124, set to a different requestId - const calldataData = - '1122334455667788991122334455667788991122334455667788991122334455' // some garbage value as response value - - const data = - functionSelector + - /** Input Params - slice off 0x prefix and pad with 0's */ - request.requestId.slice(2) + - request.payment.slice(2).padStart(64, '0') + - request.callbackAddr.slice(2).padStart(64, '0') + - request.callbackFunc.slice(2).padEnd(64, '0') + - request.expiration.slice(2).padStart(64, '0') + - // calldata "data" - dataOffset + - fillerBytes + - expectedCalldataStart + - dataSize + - maliciousCalldataId + - calldataData - - await evmRevert( - operator.connect(roles.oracleNode).signer.sendTransaction({ - to: operator.address, - data, - }), - ) - }) - }) - - describe('with a malicious requester', () => { - beforeEach(async () => { - const paymentAmount = toWei('1') - maliciousRequester = await maliciousRequesterFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousRequester.address, paymentAmount) - }) - - it('cannot cancel before the expiration', async () => { - await evmRevert( - maliciousRequester.maliciousRequestCancel( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ), - ) - }) - - it('cannot call functions on the LINK token through callbacks', async () => { - await evmRevert( - maliciousRequester.request( - specId, - link.address, - ethers.utils.toUtf8Bytes('transfer(address,uint256)'), - ), - ) - }) - - describe('requester lies about amount of LINK sent', () => { - it('the oracle uses the amount of LINK actually paid', async () => { - const tx = await maliciousRequester.maliciousPrice(specId) - const receipt = await tx.wait() - const req = decodeRunRequest(receipt.logs?.[3]) - - assert(toWei('1').eq(req.payment)) - }) - }) - }) - - describe('with a malicious consumer', () => { - const paymentAmount = toWei('1') - - beforeEach(async () => { - maliciousConsumer = await maliciousConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousConsumer.address, paymentAmount) - }) - - describe('fails during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('assertFail(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response2 = 'hack the planet 102' - const response2Values = [toBytes32String(response2)] - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - response2Values, - ), - ), - ) - }) - }) - - describe('calls selfdestruct', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - await maliciousConsumer.remove() - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - }) - - describe('request is canceled during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes( - 'cancelRequestOnFulfill(bytes32,bytes32)', - ), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const mockBalance = await link.balanceOf(maliciousConsumer.address) - bigNumEquals(mockBalance, 0) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response2 = 'hack the planet 102' - const response2Values = [toBytes32String(response2)] - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - response2Values, - ), - ), - ) - }) - }) - - describe('tries to steal funds from node', () => { - it('is not successful with call', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthCall(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with send', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthSend(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with transfer', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthTransfer(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - }) - - describe('when calling an owned contract', () => { - beforeEach(async () => { - forwarder1 = await forwarderFactory - .connect(roles.defaultAccount) - .deploy(link.address, link.address, operator.address, '0x') - }) - - it('does not allow the contract to callback to owned contracts', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('whatever(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - let request = decodeRunRequest(receipt.logs?.[3]) - let responseParams = convertFufillParams(request, response) - // set the params to be the owned address - responseParams[2] = forwarder1.address - - //accept ownership - await operator - .connect(roles.defaultAccount) - .acceptOwnableContracts([forwarder1.address]) - - // do the thing - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...responseParams), - 'Cannot call owned contract', - ) - - await operator - .connect(roles.defaultAccount) - .transferOwnableContracts([forwarder1.address], link.address) - //reverts for a different reason after transferring ownership - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...responseParams), - 'Params do not match request ID', - ) - }) - }) - }) - }) - - describe('multi word fulfils', () => { - describe('one bytes parameter', () => { - const response = - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.\ - Fusce euismod malesuada ligula, eget semper metus ultrices sit amet.' - const responseTypes = ['bytes'] - const responseValues = [stringToBytes(response)] - let maliciousRequester: Contract - let multiConsumer: Contract - let maliciousConsumer: Contract - let gasGuzzlingConsumer: Contract - let request: ReturnType - - describe('gas guzzling consumer [ @skip-coverage ]', () => { - beforeEach(async () => { - gasGuzzlingConsumer = await gasGuzzlingConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(gasGuzzlingConsumer.address, paymentAmount) - const tx = - await gasGuzzlingConsumer.gassyMultiWordRequest(paymentAmount) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('emits an OracleResponse2 event', async () => { - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 1) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - }) - - describe('cooperative consumer', () => { - beforeEach(async () => { - multiConsumer = await multiWordConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(multiConsumer.address, paymentAmount) - const currency = 'USD' - const tx = await multiConsumer.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it("matches the consumer's request ID", async () => { - const nonce = await multiConsumer.publicGetNextRequestCount() - const tx = await multiConsumer.requestEthereumPrice('USD', 0) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - const packed = ethers.utils.solidityPack( - ['address', 'uint256'], - [multiConsumer.address, nonce], - ) - const expected = ethers.utils.keccak256(packed) - assert.equal(expected, request.requestId) - }) - - describe('when called by an unauthorized node', () => { - beforeEach(async () => { - assert.equal( - false, - await operator.isAuthorizedSender( - await roles.stranger.getAddress(), - ), - ) - }) - - it('raises an error', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ), - ) - }) - }) - - describe('when called by an authorized node', () => { - it('raises an error if the request ID does not exist', async () => { - request.requestId = - ethers.utils.formatBytes32String('DOESNOTEXIST') - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ), - ) - }) - - it('sets the value on the requested contract', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const currentValue = await multiConsumer.currentPrice() - assert.equal(response, ethers.utils.toUtf8String(currentValue)) - }) - - it('emits an OracleResponse2 event', async () => { - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 3) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - - it('does not allow a request to be fulfilled twice', async () => { - const response2 = response + ' && Hello World!!' - const response2Values = [stringToBytes(response2)] - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - response2Values, - ), - ), - ) - - const currentValue = await multiConsumer.currentPrice() - assert.equal(response, ethers.utils.toUtf8String(currentValue)) - }) - }) - - describe('when the oracle does not provide enough gas', () => { - // if updating this defaultGasLimit, be sure it matches with the - // defaultGasLimit specified in store/tx_manager.go - const defaultGasLimit = 500000 - - beforeEach(async () => { - bigNumEquals(0, await operator.withdrawable()) - }) - - it('does not allow the oracle to withdraw the payment', async () => { - await evmRevert( - operator.connect(roles.oracleNode).fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - { - gasLimit: 70000, - }, - ), - ), - ) - - bigNumEquals(0, await operator.withdrawable()) - }) - - it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { - await operator.connect(roles.oracleNode).fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - { - gasLimit: defaultGasLimit, - }, - ), - ) - - bigNumEquals(request.payment, await operator.withdrawable()) - }) - }) - }) - - describe('with a malicious requester', () => { - beforeEach(async () => { - const paymentAmount = toWei('1') - maliciousRequester = await maliciousRequesterFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousRequester.address, paymentAmount) - }) - - it('cannot cancel before the expiration', async () => { - await evmRevert( - maliciousRequester.maliciousRequestCancel( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ), - ) - }) - - it('cannot call functions on the LINK token through callbacks', async () => { - await evmRevert( - maliciousRequester.request( - specId, - link.address, - ethers.utils.toUtf8Bytes('transfer(address,uint256)'), - ), - ) - }) - - describe('requester lies about amount of LINK sent', () => { - it('the oracle uses the amount of LINK actually paid', async () => { - const tx = await maliciousRequester.maliciousPrice(specId) - const receipt = await tx.wait() - const req = decodeRunRequest(receipt.logs?.[3]) - - assert(toWei('1').eq(req.payment)) - }) - }) - }) - - describe('with a malicious consumer', () => { - const paymentAmount = toWei('1') - - beforeEach(async () => { - maliciousConsumer = await maliciousMultiWordConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousConsumer.address, paymentAmount) - }) - - describe('fails during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('assertFail(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response2 = 'hack the planet 102' - const response2Values = [stringToBytes(response2)] - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - response2Values, - ), - ), - ) - }) - }) - - describe('calls selfdestruct', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - await maliciousConsumer.remove() - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - }) - - describe('request is canceled during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes( - 'cancelRequestOnFulfill(bytes32,bytes32)', - ), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const mockBalance = await link.balanceOf( - maliciousConsumer.address, - ) - bigNumEquals(mockBalance, 0) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response2 = 'hack the planet 102' - const response2Values = [stringToBytes(response2)] - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - response2Values, - ), - ), - ) - }) - }) - - describe('tries to steal funds from node', () => { - it('is not successful with call', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthCall(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with send', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthSend(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with transfer', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthTransfer(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - }) - }) - }) - - describe('multiple bytes32 parameters', () => { - const response1 = '100' - const response2 = '7777777' - const response3 = 'forty two' - const responseTypes = ['bytes32', 'bytes32', 'bytes32'] - const responseValues = [ - toBytes32String(response1), - toBytes32String(response2), - toBytes32String(response3), - ] - let maliciousRequester: Contract - let multiConsumer: Contract - let maliciousConsumer: Contract - let gasGuzzlingConsumer: Contract - let request: ReturnType - - describe('gas guzzling consumer [ @skip-coverage ]', () => { - beforeEach(async () => { - gasGuzzlingConsumer = await gasGuzzlingConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(gasGuzzlingConsumer.address, paymentAmount) - const tx = - await gasGuzzlingConsumer.gassyMultiWordRequest(paymentAmount) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('emits an OracleResponse2 event', async () => { - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 1) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - }) - - describe('cooperative consumer', () => { - beforeEach(async () => { - multiConsumer = await multiWordConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(multiConsumer.address, paymentAmount) - const currency = 'USD' - const tx = await multiConsumer.requestMultipleParameters( - currency, - paymentAmount, - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - describe('when called by an unauthorized node', () => { - beforeEach(async () => { - assert.equal( - false, - await operator.isAuthorizedSender( - await roles.stranger.getAddress(), - ), - ) - }) - - it('raises an error', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ), - ) - }) - }) - - describe('when called by an authorized node', () => { - it('raises an error if the request ID does not exist', async () => { - request.requestId = - ethers.utils.formatBytes32String('DOESNOTEXIST') - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ), - ) - }) - - it('sets the value on the requested contract', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const firstValue = await multiConsumer.usd() - const secondValue = await multiConsumer.eur() - const thirdValue = await multiConsumer.jpy() - assert.equal( - response1, - ethers.utils.parseBytes32String(firstValue), - ) - assert.equal( - response2, - ethers.utils.parseBytes32String(secondValue), - ) - assert.equal( - response3, - ethers.utils.parseBytes32String(thirdValue), - ) - }) - - it('emits an OracleResponse2 event', async () => { - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - const tx = await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams) - const receipt = await tx.wait() - assert.equal(receipt.events?.length, 3) - const responseEvent = receipt.events?.[0] - assert.equal(responseEvent?.event, 'OracleResponse') - assert.equal(responseEvent?.args?.[0], request.requestId) - }) - - it('does not allow a request to be fulfilled twice', async () => { - const response4 = response3 + ' && Hello World!!' - const repeatedResponseValues = [ - toBytes32String(response1), - toBytes32String(response2), - toBytes32String(response4), - ] - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - repeatedResponseValues, - ), - ), - ) - - const firstValue = await multiConsumer.usd() - const secondValue = await multiConsumer.eur() - const thirdValue = await multiConsumer.jpy() - assert.equal( - response1, - ethers.utils.parseBytes32String(firstValue), - ) - assert.equal( - response2, - ethers.utils.parseBytes32String(secondValue), - ) - assert.equal( - response3, - ethers.utils.parseBytes32String(thirdValue), - ) - }) - }) - - describe('when the oracle does not provide enough gas', () => { - // if updating this defaultGasLimit, be sure it matches with the - // defaultGasLimit specified in store/tx_manager.go - const defaultGasLimit = 500000 - - beforeEach(async () => { - bigNumEquals(0, await operator.withdrawable()) - }) - - it('does not allow the oracle to withdraw the payment', async () => { - await evmRevert( - operator.connect(roles.oracleNode).fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - { - gasLimit: 70000, - }, - ), - ), - ) - - bigNumEquals(0, await operator.withdrawable()) - }) - - it(`${defaultGasLimit} is enough to pass the gas requirement`, async () => { - await operator.connect(roles.oracleNode).fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - { - gasLimit: defaultGasLimit, - }, - ), - ) - - bigNumEquals(request.payment, await operator.withdrawable()) - }) - }) - }) - - describe('with a malicious requester', () => { - beforeEach(async () => { - const paymentAmount = toWei('1') - maliciousRequester = await maliciousRequesterFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousRequester.address, paymentAmount) - }) - - it('cannot cancel before the expiration', async () => { - await evmRevert( - maliciousRequester.maliciousRequestCancel( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ), - ) - }) - - it('cannot call functions on the LINK token through callbacks', async () => { - await evmRevert( - maliciousRequester.request( - specId, - link.address, - ethers.utils.toUtf8Bytes('transfer(address,uint256)'), - ), - ) - }) - - describe('requester lies about amount of LINK sent', () => { - it('the oracle uses the amount of LINK actually paid', async () => { - const tx = await maliciousRequester.maliciousPrice(specId) - const receipt = await tx.wait() - const req = decodeRunRequest(receipt.logs?.[3]) - - assert(toWei('1').eq(req.payment)) - }) - }) - }) - - describe('with a malicious consumer', () => { - const paymentAmount = toWei('1') - - beforeEach(async () => { - maliciousConsumer = await maliciousMultiWordConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address) - await link.transfer(maliciousConsumer.address, paymentAmount) - }) - - describe('fails during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('assertFail(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response4 = 'hack the planet 102' - const repeatedResponseValues = [ - toBytes32String(response1), - toBytes32String(response2), - toBytes32String(response4), - ] - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - repeatedResponseValues, - ), - ), - ) - }) - }) - - describe('calls selfdestruct', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('doesNothing(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - await maliciousConsumer.remove() - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - }) - - describe('request is canceled during fulfillment', () => { - beforeEach(async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes( - 'cancelRequestOnFulfill(bytes32,bytes32)', - ), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - bigNumEquals(0, await link.balanceOf(maliciousConsumer.address)) - }) - - it('allows the oracle node to receive their payment', async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - const mockBalance = await link.balanceOf( - maliciousConsumer.address, - ) - bigNumEquals(mockBalance, 0) - - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(balance, 0) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), paymentAmount) - const newBalance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - bigNumEquals(paymentAmount, newBalance) - }) - - it("can't fulfill the data again", async () => { - const response4 = 'hack the planet 102' - const repeatedResponseValues = [ - toBytes32String(response1), - toBytes32String(response2), - toBytes32String(response4), - ] - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - repeatedResponseValues, - ), - ), - ) - }) - }) - - describe('tries to steal funds from node', () => { - it('is not successful with call', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthCall(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with send', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthSend(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - - it('is not successful with transfer', async () => { - const tx = await maliciousConsumer.requestData( - specId, - ethers.utils.toUtf8Bytes('stealEthTransfer(bytes32,bytes32)'), - ) - const receipt = await tx.wait() - request = decodeRunRequest(receipt.logs?.[3]) - - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params( - request, - responseTypes, - responseValues, - ), - ) - bigNumEquals( - 0, - await ethers.provider.getBalance(maliciousConsumer.address), - ) - }) - }) - }) - }) - }) - - describe('when the response data is too short', () => { - const response = 'Hi mom!' - const responseTypes = ['bytes32'] - const responseValues = [toBytes32String(response)] - - it('reverts', async () => { - let basicConsumer = await basicConsumerFactory - .connect(roles.defaultAccount) - .deploy(link.address, operator.address, specId) - const paymentAmount = toWei('1') - await link.transfer(basicConsumer.address, paymentAmount) - const tx = await basicConsumer.requestEthereumPrice( - 'USD', - paymentAmount, - ) - const receipt = await tx.wait() - let request = decodeRunRequest(receipt.logs?.[3]) - - const fulfillParams = convertFulfill2Params( - request, - responseTypes, - responseValues, - ) - fulfillParams[5] = '0x' // overwrite the data to be of lenght 0 - await evmRevert( - operator - .connect(roles.oracleNode) - .fulfillOracleRequest2(...fulfillParams), - 'Response must be > 32 bytes', - ) - }) - }) - }) - - describe('#withdraw', () => { - describe('without reserving funds via oracleRequest', () => { - it('does nothing', async () => { - let balance = await link.balanceOf(await roles.oracleNode.getAddress()) - assert.equal(0, balance.toNumber()) - await evmRevert( - operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), toWei('1')), - ) - balance = await link.balanceOf(await roles.oracleNode.getAddress()) - assert.equal(0, balance.toNumber()) - }) - - describe('recovering funds that were mistakenly sent', () => { - const paid = 1 - beforeEach(async () => { - await link.transfer(operator.address, paid) - }) - - it('withdraws funds', async () => { - const operatorBalanceBefore = await link.balanceOf(operator.address) - const accountBalanceBefore = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.defaultAccount.getAddress(), paid) - - const operatorBalanceAfter = await link.balanceOf(operator.address) - const accountBalanceAfter = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - - const accountDifference = - accountBalanceAfter.sub(accountBalanceBefore) - const operatorDifference = - operatorBalanceBefore.sub(operatorBalanceAfter) - - bigNumEquals(operatorDifference, paid) - bigNumEquals(accountDifference, paid) - }) - }) - }) - - describe('reserving funds via oracleRequest', () => { - const payment = 15 - let request: ReturnType - - beforeEach(async () => { - const requester = await roles.defaultAccount.getAddress() - const args = encodeOracleRequest( - specId, - requester, - fHash, - 0, - constants.HashZero, - ) - const tx = await link.transferAndCall(operator.address, payment, args) - const receipt = await tx.wait() - assert.equal(3, receipt.logs?.length) - request = decodeRunRequest(receipt.logs?.[2]) - }) - - describe('but not freeing funds w fulfillOracleRequest', () => { - it('does not transfer funds', async () => { - await evmRevert( - operator - .connect(roles.defaultAccount) - .withdraw(await roles.oracleNode.getAddress(), payment), - ) - const balance = await link.balanceOf( - await roles.oracleNode.getAddress(), - ) - assert.equal(0, balance.toNumber()) - }) - - describe('recovering funds that were mistakenly sent', () => { - const paid = 1 - beforeEach(async () => { - await link.transfer(operator.address, paid) - }) - - it('withdraws funds', async () => { - const operatorBalanceBefore = await link.balanceOf(operator.address) - const accountBalanceBefore = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.defaultAccount.getAddress(), paid) - - const operatorBalanceAfter = await link.balanceOf(operator.address) - const accountBalanceAfter = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - - const accountDifference = - accountBalanceAfter.sub(accountBalanceBefore) - const operatorDifference = - operatorBalanceBefore.sub(operatorBalanceAfter) - - bigNumEquals(operatorDifference, paid) - bigNumEquals(accountDifference, paid) - }) - }) - }) - - describe('and freeing funds', () => { - beforeEach(async () => { - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest( - ...convertFufillParams(request, 'Hello World!'), - ) - }) - - it('does not allow input greater than the balance', async () => { - const originalOracleBalance = await link.balanceOf(operator.address) - const originalStrangerBalance = await link.balanceOf( - await roles.stranger.getAddress(), - ) - const withdrawalAmount = payment + 1 - - assert.isAbove(withdrawalAmount, originalOracleBalance.toNumber()) - await evmRevert( - operator - .connect(roles.defaultAccount) - .withdraw(await roles.stranger.getAddress(), withdrawalAmount), - ) - - const newOracleBalance = await link.balanceOf(operator.address) - const newStrangerBalance = await link.balanceOf( - await roles.stranger.getAddress(), - ) - - assert.equal( - originalOracleBalance.toNumber(), - newOracleBalance.toNumber(), - ) - assert.equal( - originalStrangerBalance.toNumber(), - newStrangerBalance.toNumber(), - ) - }) - - it('allows transfer of partial balance by owner to specified address', async () => { - const partialAmount = 6 - const difference = payment - partialAmount - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.stranger.getAddress(), partialAmount) - const strangerBalance = await link.balanceOf( - await roles.stranger.getAddress(), - ) - const oracleBalance = await link.balanceOf(operator.address) - assert.equal(partialAmount, strangerBalance.toNumber()) - assert.equal(difference, oracleBalance.toNumber()) - }) - - it('allows transfer of entire balance by owner to specified address', async () => { - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.stranger.getAddress(), payment) - const balance = await link.balanceOf( - await roles.stranger.getAddress(), - ) - assert.equal(payment, balance.toNumber()) - }) - - it('does not allow a transfer of funds by non-owner', async () => { - await evmRevert( - operator - .connect(roles.stranger) - .withdraw(await roles.stranger.getAddress(), payment), - ) - const balance = await link.balanceOf( - await roles.stranger.getAddress(), - ) - assert.isTrue(ethers.constants.Zero.eq(balance)) - }) - - describe('recovering funds that were mistakenly sent', () => { - const paid = 1 - beforeEach(async () => { - await link.transfer(operator.address, paid) - }) - - it('withdraws funds', async () => { - const operatorBalanceBefore = await link.balanceOf(operator.address) - const accountBalanceBefore = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - - await operator - .connect(roles.defaultAccount) - .withdraw(await roles.defaultAccount.getAddress(), paid) - - const operatorBalanceAfter = await link.balanceOf(operator.address) - const accountBalanceAfter = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - - const accountDifference = - accountBalanceAfter.sub(accountBalanceBefore) - const operatorDifference = - operatorBalanceBefore.sub(operatorBalanceAfter) - - bigNumEquals(operatorDifference, paid) - bigNumEquals(accountDifference, paid) - }) - }) - }) - }) - }) - - describe('#withdrawable', () => { - let request: ReturnType - const amount = toWei('1') - - beforeEach(async () => { - const requester = await roles.defaultAccount.getAddress() - const args = encodeOracleRequest( - specId, - requester, - fHash, - 0, - constants.HashZero, - ) - const tx = await link.transferAndCall(operator.address, amount, args) - const receipt = await tx.wait() - assert.equal(3, receipt.logs?.length) - request = decodeRunRequest(receipt.logs?.[2]) - await operator - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request, 'Hello World!')) - }) - - it('returns the correct value', async () => { - const withdrawAmount = await operator.withdrawable() - bigNumEquals(withdrawAmount, request.payment) - }) - - describe('funds that were mistakenly sent', () => { - const paid = 1 - beforeEach(async () => { - await link.transfer(operator.address, paid) - }) - - it('returns the correct value', async () => { - const withdrawAmount = await operator.withdrawable() - - const expectedAmount = amount.add(paid) - bigNumEquals(withdrawAmount, expectedAmount) - }) - }) - }) - - describe('#ownerTransferAndCall', () => { - let operator2: Contract - let args: string - let to: string - const startingBalance = 1000 - const payment = 20 - - beforeEach(async () => { - operator2 = await operatorFactory - .connect(roles.oracleNode2) - .deploy(link.address, await roles.oracleNode2.getAddress()) - to = operator2.address - args = encodeOracleRequest( - specId, - operator.address, - operatorFactory.interface.getSighash('fulfillOracleRequest'), - 1, - constants.HashZero, - ) - }) - - describe('when called by a non-owner', () => { - it('reverts with owner error message', async () => { - await link.transfer(operator.address, startingBalance) - await evmRevert( - operator - .connect(roles.stranger) - .ownerTransferAndCall(to, payment, args), - 'Only callable by owner', - ) - }) - }) - - describe('when called by the owner', () => { - beforeEach(async () => { - await link.transfer(operator.address, startingBalance) - }) - - describe('without sufficient funds in contract', () => { - it('reverts with funds message', async () => { - const tooMuch = startingBalance * 2 - await evmRevert( - operator - .connect(roles.defaultAccount) - .ownerTransferAndCall(to, tooMuch, args), - 'Amount requested is greater than withdrawable balance', - ) - }) - }) - - describe('with sufficient funds', () => { - let tx: ContractTransaction - let receipt: ContractReceipt - let requesterBalanceBefore: BigNumber - let requesterBalanceAfter: BigNumber - let receiverBalanceBefore: BigNumber - let receiverBalanceAfter: BigNumber - - before(async () => { - requesterBalanceBefore = await link.balanceOf(operator.address) - receiverBalanceBefore = await link.balanceOf(operator2.address) - tx = await operator - .connect(roles.defaultAccount) - .ownerTransferAndCall(to, payment, args) - receipt = await tx.wait() - requesterBalanceAfter = await link.balanceOf(operator.address) - receiverBalanceAfter = await link.balanceOf(operator2.address) - }) - - it('emits an event', async () => { - assert.equal(3, receipt.logs?.length) - const transferLog = await getLog(tx, 1) - const parsedLog = link.interface.parseLog({ - data: transferLog.data, - topics: transferLog.topics, - }) - await expect(parsedLog.name).to.equal('Transfer') - }) - - it('transfers the tokens', async () => { - bigNumEquals( - requesterBalanceBefore.sub(requesterBalanceAfter), - payment, - ) - bigNumEquals(receiverBalanceAfter.sub(receiverBalanceBefore), payment) - }) - }) - }) - }) - - describe('#cancelOracleRequestByRequester', () => { - const nonce = 17 - - describe('with no pending requests', () => { - it('fails', async () => { - const fakeRequest: RunRequest = { - requestId: ethers.utils.formatBytes32String('1337'), - payment: '0', - callbackFunc: - getterSetterFactory.interface.getSighash('requestedBytes32'), - expiration: '999999999999', - - callbackAddr: '', - data: Buffer.from(''), - dataVersion: 0, - specId: '', - requester: '', - topic: '', - } - await increaseTime5Minutes(ethers.provider) - - await evmRevert( - operator - .connect(roles.stranger) - .cancelOracleRequestByRequester( - ...convertCancelByRequesterParams(fakeRequest, nonce), - ), - ) - }) - }) - - describe('with a pending request', () => { - const startingBalance = 100 - let request: ReturnType - let receipt: providers.TransactionReceipt - - beforeEach(async () => { - const requestAmount = 20 - - await link.transfer(await roles.consumer.getAddress(), startingBalance) - - const args = encodeOracleRequest( - specId, - await roles.consumer.getAddress(), - fHash, - nonce, - constants.HashZero, - ) - const tx = await link - .connect(roles.consumer) - .transferAndCall(operator.address, requestAmount, args) - receipt = await tx.wait() - - assert.equal(3, receipt.logs?.length) - request = decodeRunRequest(receipt.logs?.[2]) - - // pre conditions - const oracleBalance = await link.balanceOf(operator.address) - bigNumEquals(request.payment, oracleBalance) - - const consumerAmount = await link.balanceOf( - await roles.consumer.getAddress(), - ) - assert.equal( - startingBalance - Number(request.payment), - consumerAmount.toNumber(), - ) - }) - - describe('from a stranger', () => { - it('fails', async () => { - await evmRevert( - operator - .connect(roles.consumer) - .cancelOracleRequestByRequester( - ...convertCancelByRequesterParams(request, nonce), - ), - ) - }) - }) - - describe('from the requester', () => { - it('refunds the correct amount', async () => { - await increaseTime5Minutes(ethers.provider) - await operator - .connect(roles.consumer) - .cancelOracleRequestByRequester( - ...convertCancelByRequesterParams(request, nonce), - ) - const balance = await link.balanceOf( - await roles.consumer.getAddress(), - ) - - assert.equal(startingBalance, balance.toNumber()) // 100 - }) - - it('triggers a cancellation event', async () => { - await increaseTime5Minutes(ethers.provider) - const tx = await operator - .connect(roles.consumer) - .cancelOracleRequestByRequester( - ...convertCancelByRequesterParams(request, nonce), - ) - const receipt = await tx.wait() - - assert.equal(receipt.logs?.length, 2) - assert.equal(request.requestId, receipt.logs?.[0].topics[1]) - }) - - it('fails when called twice', async () => { - await increaseTime5Minutes(ethers.provider) - await operator - .connect(roles.consumer) - .cancelOracleRequestByRequester( - ...convertCancelByRequesterParams(request, nonce), - ) - - await evmRevert( - operator - .connect(roles.consumer) - .cancelOracleRequestByRequester(...convertCancelParams(request)), - ) - }) - }) - }) - }) - - describe('#cancelOracleRequest', () => { - describe('with no pending requests', () => { - it('fails', async () => { - const fakeRequest: RunRequest = { - requestId: ethers.utils.formatBytes32String('1337'), - payment: '0', - callbackFunc: - getterSetterFactory.interface.getSighash('requestedBytes32'), - expiration: '999999999999', - - callbackAddr: '', - data: Buffer.from(''), - dataVersion: 0, - specId: '', - requester: '', - topic: '', - } - await increaseTime5Minutes(ethers.provider) - - await evmRevert( - operator - .connect(roles.stranger) - .cancelOracleRequest(...convertCancelParams(fakeRequest)), - ) - }) - }) - - describe('with a pending request', () => { - const startingBalance = 100 - let request: ReturnType - let receipt: providers.TransactionReceipt - - beforeEach(async () => { - const requestAmount = 20 - - await link.transfer(await roles.consumer.getAddress(), startingBalance) - - const args = encodeOracleRequest( - specId, - await roles.consumer.getAddress(), - fHash, - 1, - constants.HashZero, - ) - const tx = await link - .connect(roles.consumer) - .transferAndCall(operator.address, requestAmount, args) - receipt = await tx.wait() - - assert.equal(3, receipt.logs?.length) - request = decodeRunRequest(receipt.logs?.[2]) - }) - - it('has correct initial balances', async () => { - const oracleBalance = await link.balanceOf(operator.address) - bigNumEquals(request.payment, oracleBalance) - - const consumerAmount = await link.balanceOf( - await roles.consumer.getAddress(), - ) - assert.equal( - startingBalance - Number(request.payment), - consumerAmount.toNumber(), - ) - }) - - describe('from a stranger', () => { - it('fails', async () => { - await evmRevert( - operator - .connect(roles.consumer) - .cancelOracleRequest(...convertCancelParams(request)), - ) - }) - }) - - describe('from the requester', () => { - it('refunds the correct amount', async () => { - await increaseTime5Minutes(ethers.provider) - await operator - .connect(roles.consumer) - .cancelOracleRequest(...convertCancelParams(request)) - const balance = await link.balanceOf( - await roles.consumer.getAddress(), - ) - - assert.equal(startingBalance, balance.toNumber()) // 100 - }) - - it('triggers a cancellation event', async () => { - await increaseTime5Minutes(ethers.provider) - const tx = await operator - .connect(roles.consumer) - .cancelOracleRequest(...convertCancelParams(request)) - const receipt = await tx.wait() - - assert.equal(receipt.logs?.length, 2) - assert.equal(request.requestId, receipt.logs?.[0].topics[1]) - }) - - it('fails when called twice', async () => { - await increaseTime5Minutes(ethers.provider) - await operator - .connect(roles.consumer) - .cancelOracleRequest(...convertCancelParams(request)) - - await evmRevert( - operator - .connect(roles.consumer) - .cancelOracleRequest(...convertCancelParams(request)), - ) - }) - }) - }) - }) - - describe('#ownerForward', () => { - let bytes: string - let payload: string - let mock: Contract - - beforeEach(async () => { - bytes = ethers.utils.hexlify(ethers.utils.randomBytes(100)) - payload = getterSetterFactory.interface.encodeFunctionData( - getterSetterFactory.interface.getFunction('setBytes'), - [bytes], - ) - mock = await getterSetterFactory.connect(roles.defaultAccount).deploy() - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - operator.connect(roles.stranger).ownerForward(mock.address, payload), - ) - }) - }) - - describe('when called by owner', () => { - describe('when attempting to forward to the link token', () => { - it('reverts', async () => { - const sighash = linkTokenFactory.interface.getSighash('name') - await evmRevert( - operator - .connect(roles.defaultAccount) - .ownerForward(link.address, sighash), - 'Cannot call to LINK', - ) - }) - }) - - describe('when forwarding to any other address', () => { - it('forwards the data', async () => { - const tx = await operator - .connect(roles.defaultAccount) - .ownerForward(mock.address, payload) - await tx.wait() - assert.equal(await mock.getBytes(), bytes) - }) - - it('reverts when sending to a non-contract address', async () => { - await evmRevert( - operator - .connect(roles.defaultAccount) - .ownerForward(zeroAddress, payload), - 'Must forward to a contract', - ) - }) - - it('perceives the message is sent by the Operator', async () => { - const tx = await operator - .connect(roles.defaultAccount) - .ownerForward(mock.address, payload) - const receipt = await tx.wait() - const log: any = receipt.logs?.[0] - const logData = mock.interface.decodeEventLog( - mock.interface.getEvent('SetBytes'), - log.data, - log.topics, - ) - assert.equal(ethers.utils.getAddress(logData.from), operator.address) - }) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/OperatorFactory.test.ts b/contracts/test/v0.7/OperatorFactory.test.ts deleted file mode 100644 index d2a24600e23..00000000000 --- a/contracts/test/v0.7/OperatorFactory.test.ts +++ /dev/null @@ -1,293 +0,0 @@ -import { ethers } from 'hardhat' -import { evmWordToAddress, publicAbi } from '../test-helpers/helpers' -import { assert } from 'chai' -import { Contract, ContractFactory, ContractReceipt } from 'ethers' -import { getUsers, Roles } from '../test-helpers/setup' - -let linkTokenFactory: ContractFactory -let operatorGeneratorFactory: ContractFactory -let operatorFactory: ContractFactory -let forwarderFactory: ContractFactory - -let roles: Roles - -before(async () => { - const users = await getUsers() - - roles = users.roles - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) - operatorGeneratorFactory = await ethers.getContractFactory( - 'src/v0.7/OperatorFactory.sol:OperatorFactory', - roles.defaultAccount, - ) - operatorFactory = await ethers.getContractFactory( - 'src/v0.7/Operator.sol:Operator', - roles.defaultAccount, - ) - forwarderFactory = await ethers.getContractFactory( - 'src/v0.7/AuthorizedForwarder.sol:AuthorizedForwarder', - roles.defaultAccount, - ) -}) - -describe('OperatorFactory', () => { - let link: Contract - let operatorGenerator: Contract - let operator: Contract - let forwarder: Contract - let receipt: ContractReceipt - let emittedOperator: string - let emittedForwarder: string - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - operatorGenerator = await operatorGeneratorFactory - .connect(roles.defaultAccount) - .deploy(link.address) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(operatorGenerator, [ - 'created', - 'deployNewOperator', - 'deployNewOperatorAndForwarder', - 'deployNewForwarder', - 'deployNewForwarderAndTransferOwnership', - 'getChainlinkToken', - 'typeAndVersion', - ]) - }) - - describe('#typeAndVersion', () => { - it('describes the authorized forwarder', async () => { - assert.equal( - await operatorGenerator.typeAndVersion(), - 'OperatorFactory 1.0.0', - ) - }) - }) - - describe('#deployNewOperator', () => { - beforeEach(async () => { - const tx = await operatorGenerator - .connect(roles.oracleNode) - .deployNewOperator() - - receipt = await tx.wait() - emittedOperator = evmWordToAddress(receipt.logs?.[0].topics?.[1]) - }) - - it('emits an event', async () => { - assert.equal(receipt?.events?.[0]?.event, 'OperatorCreated') - assert.equal(emittedOperator, receipt.events?.[0].args?.[0]) - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[0].args?.[1], - ) - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[0].args?.[2], - ) - }) - - it('sets the correct owner', async () => { - operator = await operatorFactory - .connect(roles.defaultAccount) - .attach(emittedOperator) - const ownerString = await operator.owner() - assert.equal(ownerString, await roles.oracleNode.getAddress()) - }) - - it('records that it deployed that address', async () => { - assert.isTrue(await operatorGenerator.created(emittedOperator)) - }) - }) - - describe('#deployNewOperatorAndForwarder', () => { - beforeEach(async () => { - const tx = await operatorGenerator - .connect(roles.oracleNode) - .deployNewOperatorAndForwarder() - - receipt = await tx.wait() - emittedOperator = evmWordToAddress(receipt.logs?.[0].topics?.[1]) - emittedForwarder = evmWordToAddress(receipt.logs?.[3].topics?.[1]) - }) - - it('emits an event recording that the operator was deployed', async () => { - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[0].args?.[1], - ) - assert.equal(receipt?.events?.[0]?.event, 'OperatorCreated') - assert.equal(receipt?.events?.[0]?.args?.[0], emittedOperator) - assert.equal( - receipt?.events?.[0]?.args?.[1], - await roles.oracleNode.getAddress(), - ) - assert.equal( - receipt?.events?.[0]?.args?.[2], - await roles.oracleNode.getAddress(), - ) - }) - - it('proposes the transfer of the forwarder to the operator', async () => { - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[0].args?.[1], - ) - assert.equal( - receipt?.events?.[1]?.topics?.[0], - '0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278', //OwnershipTransferRequested(address,address) - ) - assert.equal( - evmWordToAddress(receipt?.events?.[1]?.topics?.[1]), - operatorGenerator.address, - ) - assert.equal( - evmWordToAddress(receipt?.events?.[1]?.topics?.[2]), - emittedOperator, - ) - - assert.equal( - receipt?.events?.[2]?.topics?.[0], - '0x4e1e878dc28d5f040db5969163ff1acd75c44c3f655da2dde9c70bbd8e56dc7e', //OwnershipTransferRequestedWithMessage(address,address,bytes) - ) - assert.equal( - evmWordToAddress(receipt?.events?.[2]?.topics?.[1]), - operatorGenerator.address, - ) - assert.equal( - evmWordToAddress(receipt?.events?.[2]?.topics?.[2]), - emittedOperator, - ) - }) - - it('emits an event recording that the forwarder was deployed', async () => { - assert.equal(receipt?.events?.[3]?.event, 'AuthorizedForwarderCreated') - assert.equal(receipt?.events?.[3]?.args?.[0], emittedForwarder) - assert.equal(receipt?.events?.[3]?.args?.[1], operatorGenerator.address) - assert.equal( - receipt?.events?.[3]?.args?.[2], - await roles.oracleNode.getAddress(), - ) - }) - - it('sets the correct owner on the operator', async () => { - operator = await operatorFactory - .connect(roles.defaultAccount) - .attach(receipt?.events?.[0]?.args?.[0]) - assert.equal(await roles.oracleNode.getAddress(), await operator.owner()) - }) - - it('sets the operator as the owner of the forwarder', async () => { - forwarder = await forwarderFactory - .connect(roles.defaultAccount) - .attach(emittedForwarder) - assert.equal(operatorGenerator.address, await forwarder.owner()) - }) - - it('records that it deployed that address', async () => { - assert.isTrue(await operatorGenerator.created(emittedOperator)) - assert.isTrue(await operatorGenerator.created(emittedForwarder)) - }) - }) - - describe('#deployNewForwarder', () => { - beforeEach(async () => { - const tx = await operatorGenerator - .connect(roles.oracleNode) - .deployNewForwarder() - - receipt = await tx.wait() - emittedForwarder = receipt.events?.[0].args?.[0] - }) - - it('emits an event', async () => { - assert.equal(receipt?.events?.[0]?.event, 'AuthorizedForwarderCreated') - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[0].args?.[1], - ) // owner - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[0].args?.[2], - ) // sender - }) - - it('sets the caller as the owner', async () => { - forwarder = await forwarderFactory - .connect(roles.defaultAccount) - .attach(emittedForwarder) - const ownerString = await forwarder.owner() - assert.equal(ownerString, await roles.oracleNode.getAddress()) - }) - - it('records that it deployed that address', async () => { - assert.isTrue(await operatorGenerator.created(emittedForwarder)) - }) - }) - - describe('#deployNewForwarderAndTransferOwnership', () => { - const message = '0x42' - - beforeEach(async () => { - const tx = await operatorGenerator - .connect(roles.oracleNode) - .deployNewForwarderAndTransferOwnership( - await roles.stranger.getAddress(), - message, - ) - receipt = await tx.wait() - - emittedForwarder = evmWordToAddress(receipt.logs?.[2].topics?.[1]) - }) - - it('emits an event', async () => { - assert.equal(receipt?.events?.[2]?.event, 'AuthorizedForwarderCreated') - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[2].args?.[1], - ) // owner - assert.equal( - await roles.oracleNode.getAddress(), - receipt.events?.[2].args?.[2], - ) // sender - }) - - it('sets the caller as the owner', async () => { - forwarder = await forwarderFactory - .connect(roles.defaultAccount) - .attach(emittedForwarder) - const ownerString = await forwarder.owner() - assert.equal(ownerString, await roles.oracleNode.getAddress()) - }) - - it('proposes a transfer to the recipient', async () => { - const emittedOwner = evmWordToAddress(receipt.logs?.[0].topics?.[1]) - assert.equal(emittedOwner, await roles.oracleNode.getAddress()) - const emittedRecipient = evmWordToAddress(receipt.logs?.[0].topics?.[2]) - assert.equal(emittedRecipient, await roles.stranger.getAddress()) - }) - - it('proposes a transfer to the recipient with the specified message', async () => { - const emittedOwner = evmWordToAddress(receipt.logs?.[1].topics?.[1]) - assert.equal(emittedOwner, await roles.oracleNode.getAddress()) - const emittedRecipient = evmWordToAddress(receipt.logs?.[1].topics?.[2]) - assert.equal(emittedRecipient, await roles.stranger.getAddress()) - - const encodedMessage = ethers.utils.defaultAbiCoder.encode( - ['bytes'], - [message], - ) - assert.equal(receipt?.logs?.[1]?.data, encodedMessage) - }) - - it('records that it deployed that address', async () => { - assert.isTrue(await operatorGenerator.created(emittedForwarder)) - }) - }) -}) diff --git a/contracts/test/v0.7/StalenessFlaggingValidator.test.ts b/contracts/test/v0.7/StalenessFlaggingValidator.test.ts deleted file mode 100644 index 8a5c4b67632..00000000000 --- a/contracts/test/v0.7/StalenessFlaggingValidator.test.ts +++ /dev/null @@ -1,632 +0,0 @@ -import { ethers } from 'hardhat' -import { - evmWordToAddress, - getLog, - getLogs, - numToBytes32, - publicAbi, -} from '../test-helpers/helpers' -import { assert, expect } from 'chai' -import { BigNumber, Contract, ContractFactory } from 'ethers' -import { Personas, getUsers } from '../test-helpers/setup' -import { evmRevert } from '../test-helpers/matchers' - -let personas: Personas -let validatorFactory: ContractFactory -let flagsFactory: ContractFactory -let acFactory: ContractFactory -let aggregatorFactory: ContractFactory - -before(async () => { - personas = (await getUsers()).personas - - validatorFactory = await ethers.getContractFactory( - 'src/v0.7/dev/StalenessFlaggingValidator.sol:StalenessFlaggingValidator', - personas.Carol, - ) - flagsFactory = await ethers.getContractFactory( - 'src/v0.6/Flags.sol:Flags', - personas.Carol, - ) - acFactory = await ethers.getContractFactory( - 'src/v0.6/SimpleWriteAccessController.sol:SimpleWriteAccessController', - personas.Carol, - ) - aggregatorFactory = await ethers.getContractFactory( - 'src/v0.7/tests/MockV3Aggregator.sol:MockV3Aggregator', - personas.Carol, - ) -}) - -describe('StalenessFlaggingValidator', () => { - let validator: Contract - let flags: Contract - let ac: Contract - - const flaggingThreshold1 = 10000 - const flaggingThreshold2 = 20000 - - beforeEach(async () => { - ac = await acFactory.connect(personas.Carol).deploy() - flags = await flagsFactory.connect(personas.Carol).deploy(ac.address) - validator = await validatorFactory - .connect(personas.Carol) - .deploy(flags.address) - - await ac.connect(personas.Carol).addAccess(validator.address) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(validator, [ - 'update', - 'check', - 'setThresholds', - 'setFlagsAddress', - 'threshold', - 'flags', - // Upkeep methods: - 'checkUpkeep', - 'performUpkeep', - // Owned methods: - 'acceptOwnership', - 'owner', - 'transferOwnership', - ]) - }) - - describe('#constructor', () => { - it('sets the arguments passed in', async () => { - assert.equal(await validator.flags(), flags.address) - }) - - it('sets the owner', async () => { - assert.equal(await validator.owner(), await personas.Carol.getAddress()) - }) - }) - - describe('#setFlagsAddress', () => { - const newFlagsAddress = '0x0123456789012345678901234567890123456789' - - it('changes the flags address', async () => { - assert.equal(flags.address, await validator.flags()) - - await validator.connect(personas.Carol).setFlagsAddress(newFlagsAddress) - - assert.equal(newFlagsAddress, await validator.flags()) - }) - - it('emits a log event only when actually changed', async () => { - const tx = await validator - .connect(personas.Carol) - .setFlagsAddress(newFlagsAddress) - await expect(tx) - .to.emit(validator, 'FlagsAddressUpdated') - .withArgs(flags.address, newFlagsAddress) - - const sameChangeTx = await validator - .connect(personas.Carol) - .setFlagsAddress(newFlagsAddress) - - await expect(sameChangeTx).to.not.emit(validator, 'FlagsAddressUpdated') - }) - - describe('when called by a non-owner', () => { - it('reverts', async () => { - await evmRevert( - validator.connect(personas.Neil).setFlagsAddress(newFlagsAddress), - 'Only callable by owner', - ) - }) - }) - }) - - describe('#setThresholds', () => { - let agg1: Contract - let agg2: Contract - let aggregators: Array - let thresholds: Array - - beforeEach(async () => { - const decimals = 8 - const initialAnswer = 10000000000 - agg1 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - agg2 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - }) - - describe('failure', () => { - beforeEach(() => { - aggregators = [agg1.address, agg2.address] - thresholds = [flaggingThreshold1] - }) - - it('reverts when called by a non-owner', async () => { - await evmRevert( - validator - .connect(personas.Neil) - .setThresholds(aggregators, thresholds), - 'Only callable by owner', - ) - }) - - it('reverts when passed uneven arrays', async () => { - await evmRevert( - validator - .connect(personas.Carol) - .setThresholds(aggregators, thresholds), - 'Different sized arrays', - ) - }) - }) - - describe('success', () => { - let tx: any - - beforeEach(() => { - aggregators = [agg1.address, agg2.address] - thresholds = [flaggingThreshold1, flaggingThreshold2] - }) - - describe('when called with 2 new thresholds', () => { - beforeEach(async () => { - tx = await validator - .connect(personas.Carol) - .setThresholds(aggregators, thresholds) - }) - - it('sets the thresholds', async () => { - const first = await validator.threshold(agg1.address) - const second = await validator.threshold(agg2.address) - assert.equal(first.toString(), flaggingThreshold1.toString()) - assert.equal(second.toString(), flaggingThreshold2.toString()) - }) - - it('emits events', async () => { - const firstEvent = await getLog(tx, 0) - assert.equal(evmWordToAddress(firstEvent.topics[1]), agg1.address) - assert.equal(firstEvent.topics[3], numToBytes32(flaggingThreshold1)) - const secondEvent = await getLog(tx, 1) - assert.equal(evmWordToAddress(secondEvent.topics[1]), agg2.address) - assert.equal(secondEvent.topics[3], numToBytes32(flaggingThreshold2)) - }) - }) - - describe('when called with 2, but 1 has not changed', () => { - it('emits only 1 event', async () => { - tx = await validator - .connect(personas.Carol) - .setThresholds(aggregators, thresholds) - - const newThreshold = flaggingThreshold2 + 1 - tx = await validator - .connect(personas.Carol) - .setThresholds(aggregators, [flaggingThreshold1, newThreshold]) - const logs = await getLogs(tx) - assert.equal(logs.length, 1) - const log = logs[0] - assert.equal(evmWordToAddress(log.topics[1]), agg2.address) - assert.equal(log.topics[2], numToBytes32(flaggingThreshold2)) - assert.equal(log.topics[3], numToBytes32(newThreshold)) - }) - }) - }) - }) - - describe('#check', () => { - let agg1: Contract - let agg2: Contract - let aggregators: Array - let thresholds: Array - const decimals = 8 - const initialAnswer = 10000000000 - beforeEach(async () => { - agg1 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - agg2 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - aggregators = [agg1.address, agg2.address] - thresholds = [flaggingThreshold1, flaggingThreshold2] - await validator.setThresholds(aggregators, thresholds) - }) - - describe('when neither are stale', () => { - it('returns an empty array', async () => { - const response = await validator.check(aggregators) - assert.equal(response.length, 0) - }) - }) - - describe('when threshold is not set in the validator', () => { - it('returns an empty array', async () => { - const agg3 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - const response = await validator.check([agg3.address]) - assert.equal(response.length, 0) - }) - }) - - describe('when one of the aggregators is stale', () => { - it('returns an array with one stale aggregator', async () => { - const currentTimestamp = await agg1.latestTimestamp() - const staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - const response = await validator.check(aggregators) - - assert.equal(response.length, 1) - assert.equal(response[0], agg1.address) - }) - }) - - describe('When both aggregators are stale', () => { - it('returns an array with both aggregators', async () => { - let currentTimestamp = await agg1.latestTimestamp() - let staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - currentTimestamp = await agg2.latestTimestamp() - staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold2 + 1), - ) - await agg2.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const response = await validator.check(aggregators) - - assert.equal(response.length, 2) - assert.equal(response[0], agg1.address) - assert.equal(response[1], agg2.address) - }) - }) - }) - - describe('#update', () => { - let agg1: Contract - let agg2: Contract - let aggregators: Array - let thresholds: Array - const decimals = 8 - const initialAnswer = 10000000000 - beforeEach(async () => { - agg1 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - agg2 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - aggregators = [agg1.address, agg2.address] - thresholds = [flaggingThreshold1, flaggingThreshold2] - await validator.setThresholds(aggregators, thresholds) - }) - - describe('when neither are stale', () => { - it('does not raise a flag', async () => { - const tx = await validator.update(aggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - - describe('when threshold is not set in the validator', () => { - it('does not raise a flag', async () => { - const agg3 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - const tx = await validator.update([agg3.address]) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - - describe('when one is stale', () => { - it('raises a flag for that aggregator', async () => { - const currentTimestamp = await agg1.latestTimestamp() - const staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const tx = await validator.update(aggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 1) - assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address) - }) - }) - - describe('when both are stale', () => { - it('raises 2 flags, one for each aggregator', async () => { - let currentTimestamp = await agg1.latestTimestamp() - let staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - currentTimestamp = await agg2.latestTimestamp() - staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold2 + 1), - ) - await agg2.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const tx = await validator.update(aggregators) - const logs = await getLogs(tx) - assert.equal(logs.length, 2) - assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address) - assert.equal(evmWordToAddress(logs[1].topics[1]), agg2.address) - }) - }) - }) - - describe('#checkUpkeep', () => { - let agg1: Contract - let agg2: Contract - let aggregators: Array - let thresholds: Array - const decimals = 8 - const initialAnswer = 10000000000 - beforeEach(async () => { - agg1 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - agg2 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - aggregators = [agg1.address, agg2.address] - thresholds = [flaggingThreshold1, flaggingThreshold2] - await validator.setThresholds(aggregators, thresholds) - }) - - describe('when neither are stale', () => { - it('returns false and an empty array', async () => { - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const response = await validator.checkUpkeep(bytesData) - - assert.equal(response[0], false) - const decodedResponse = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - response?.[1], - ) - assert.equal(decodedResponse[0].length, 0) - }) - }) - - describe('when threshold is not set in the validator', () => { - it('returns flase and an empty array', async () => { - const agg3 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [[agg3.address]], - ) - const response = await validator.checkUpkeep(bytesData) - - assert.equal(response[0], false) - const decodedResponse = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - response?.[1], - ) - assert.equal(decodedResponse[0].length, 0) - }) - }) - - describe('when one of the aggregators is stale', () => { - it('returns true with an array with one stale aggregator', async () => { - const currentTimestamp = await agg1.latestTimestamp() - const staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const response = await validator.checkUpkeep(bytesData) - - assert.equal(response[0], true) - const decodedResponse = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - response?.[1], - ) - const decodedArray = decodedResponse[0] - assert.equal(decodedArray.length, 1) - assert.equal(decodedArray[0], agg1.address) - }) - }) - - describe('When both aggregators are stale', () => { - it('returns true with an array with both aggregators', async () => { - let currentTimestamp = await agg1.latestTimestamp() - let staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - currentTimestamp = await agg2.latestTimestamp() - staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold2 + 1), - ) - await agg2.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const response = await validator.checkUpkeep(bytesData) - - assert.equal(response[0], true) - const decodedResponse = ethers.utils.defaultAbiCoder.decode( - ['address[]'], - response?.[1], - ) - const decodedArray = decodedResponse[0] - assert.equal(decodedArray.length, 2) - assert.equal(decodedArray[0], agg1.address) - assert.equal(decodedArray[1], agg2.address) - }) - }) - }) - - describe('#performUpkeep', () => { - let agg1: Contract - let agg2: Contract - let aggregators: Array - let thresholds: Array - const decimals = 8 - const initialAnswer = 10000000000 - beforeEach(async () => { - agg1 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - agg2 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - aggregators = [agg1.address, agg2.address] - thresholds = [flaggingThreshold1, flaggingThreshold2] - await validator.setThresholds(aggregators, thresholds) - }) - - describe('when neither are stale', () => { - it('does not raise a flag', async () => { - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const tx = await validator.performUpkeep(bytesData) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - - describe('when threshold is not set in the validator', () => { - it('does not raise a flag', async () => { - const agg3 = await aggregatorFactory - .connect(personas.Carol) - .deploy(decimals, initialAnswer) - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [[agg3.address]], - ) - const tx = await validator.performUpkeep(bytesData) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - - describe('when one is stale', () => { - it('raises a flag for that aggregator', async () => { - const currentTimestamp = await agg1.latestTimestamp() - const staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const tx = await validator.performUpkeep(bytesData) - const logs = await getLogs(tx) - assert.equal(logs.length, 1) - assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address) - }) - }) - - describe('when both are stale', () => { - it('raises 2 flags, one for each aggregator', async () => { - let currentTimestamp = await agg1.latestTimestamp() - let staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold1 + 1), - ) - await agg1.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - currentTimestamp = await agg2.latestTimestamp() - staleTimestamp = currentTimestamp.sub( - BigNumber.from(flaggingThreshold2 + 1), - ) - await agg2.updateRoundData( - 99, - initialAnswer, - staleTimestamp, - staleTimestamp, - ) - - const bytesData = ethers.utils.defaultAbiCoder.encode( - ['address[]'], - [aggregators], - ) - const tx = await validator.performUpkeep(bytesData) - const logs = await getLogs(tx) - assert.equal(logs.length, 2) - assert.equal(evmWordToAddress(logs[0].topics[1]), agg1.address) - assert.equal(evmWordToAddress(logs[1].topics[1]), agg2.address) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/UpkeepRegistrationRequests.test.ts b/contracts/test/v0.7/UpkeepRegistrationRequests.test.ts deleted file mode 100644 index 5ec9306c668..00000000000 --- a/contracts/test/v0.7/UpkeepRegistrationRequests.test.ts +++ /dev/null @@ -1,603 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { evmRevert } from '../test-helpers/matchers' -import { getUsers, Personas } from '../test-helpers/setup' -import { BigNumber, Signer } from 'ethers' -import { LinkToken__factory as LinkTokenFactory } from '../../typechain/factories/LinkToken__factory' -import { KeeperRegistry1_1__factory as KeeperRegistryFactory } from '../../typechain/factories/KeeperRegistry1_1__factory' -import { MockV3Aggregator__factory as MockV3AggregatorFactory } from '../../typechain/factories/MockV3Aggregator__factory' -import { UpkeepRegistrationRequests__factory as UpkeepRegistrationRequestsFactory } from '../../typechain/factories/UpkeepRegistrationRequests__factory' -import { UpkeepMock__factory as UpkeepMockFactory } from '../../typechain/factories/UpkeepMock__factory' -import { KeeperRegistry1_1 as KeeperRegistry } from '../../typechain/KeeperRegistry1_1' -import { UpkeepRegistrationRequests } from '../../typechain/UpkeepRegistrationRequests' -import { MockV3Aggregator } from '../../typechain/MockV3Aggregator' -import { LinkToken } from '../../typechain/LinkToken' -import { UpkeepMock } from '../../typechain/UpkeepMock' - -let linkTokenFactory: LinkTokenFactory -let mockV3AggregatorFactory: MockV3AggregatorFactory -let keeperRegistryFactory: KeeperRegistryFactory -let upkeepRegistrationRequestsFactory: UpkeepRegistrationRequestsFactory -let upkeepMockFactory: UpkeepMockFactory - -let personas: Personas - -before(async () => { - personas = (await getUsers()).personas - - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - ) - mockV3AggregatorFactory = (await ethers.getContractFactory( - 'src/v0.7/tests/MockV3Aggregator.sol:MockV3Aggregator', - )) as unknown as MockV3AggregatorFactory - // @ts-ignore bug in autogen file - keeperRegistryFactory = await ethers.getContractFactory('KeeperRegistry1_1') - upkeepRegistrationRequestsFactory = await ethers.getContractFactory( - 'UpkeepRegistrationRequests', - ) - upkeepMockFactory = await ethers.getContractFactory('UpkeepMock') -}) - -const errorMsgs = { - onlyOwner: 'revert Only callable by owner', - onlyAdmin: 'only admin / owner can cancel', - hashPayload: 'hash and payload do not match', - requestNotFound: 'request not found', -} - -describe('UpkeepRegistrationRequests', () => { - const upkeepName = 'SampleUpkeep' - - const linkEth = BigNumber.from(300000000) - const gasWei = BigNumber.from(100) - const executeGas = BigNumber.from(100000) - const source = BigNumber.from(100) - const paymentPremiumPPB = BigNumber.from(250000000) - const flatFeeMicroLink = BigNumber.from(0) - - const window_big = BigNumber.from(1000) - const window_small = BigNumber.from(2) - const threshold_big = BigNumber.from(1000) - const threshold_small = BigNumber.from(5) - - const blockCountPerTurn = BigNumber.from(3) - const emptyBytes = '0x00' - const stalenessSeconds = BigNumber.from(43820) - const gasCeilingMultiplier = BigNumber.from(1) - const maxCheckGas = BigNumber.from(20000000) - const fallbackGasPrice = BigNumber.from(200) - const fallbackLinkPrice = BigNumber.from(200000000) - const minLINKJuels = BigNumber.from('1000000000000000000') - const amount = BigNumber.from('5000000000000000000') - const amount1 = BigNumber.from('6000000000000000000') - - let owner: Signer - let admin: Signer - let someAddress: Signer - let registrarOwner: Signer - let stranger: Signer - - let linkToken: LinkToken - let linkEthFeed: MockV3Aggregator - let gasPriceFeed: MockV3Aggregator - let registry: KeeperRegistry - let mock: UpkeepMock - let registrar: UpkeepRegistrationRequests - - beforeEach(async () => { - owner = personas.Default - admin = personas.Neil - someAddress = personas.Ned - registrarOwner = personas.Nelly - stranger = personas.Nancy - - linkToken = await linkTokenFactory.connect(owner).deploy() - gasPriceFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(0, gasWei) - linkEthFeed = await mockV3AggregatorFactory - .connect(owner) - .deploy(9, linkEth) - registry = await keeperRegistryFactory - .connect(owner) - .deploy( - linkToken.address, - linkEthFeed.address, - gasPriceFeed.address, - paymentPremiumPPB, - flatFeeMicroLink, - blockCountPerTurn, - maxCheckGas, - stalenessSeconds, - gasCeilingMultiplier, - fallbackGasPrice, - fallbackLinkPrice, - ) - - mock = await upkeepMockFactory.deploy() - - registrar = await upkeepRegistrationRequestsFactory - .connect(registrarOwner) - .deploy(linkToken.address, minLINKJuels) - - await registry.setRegistrar(registrar.address) - }) - - describe('#typeAndVersion', () => { - it('uses the correct type and version', async () => { - const typeAndVersion = await registrar.typeAndVersion() - assert.equal(typeAndVersion, 'UpkeepRegistrationRequests 1.0.0') - }) - }) - - describe('#register', () => { - it('reverts if not called by the LINK token', async () => { - await evmRevert( - registrar - .connect(someAddress) - .register( - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - ), - 'Must use LINK token', - ) - }) - - it('reverts if the amount passed in data mismatches actual amount sent', async () => { - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - true, - window_small, - threshold_big, - registry.address, - minLINKJuels, - ) - - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount1, - source, - ], - ) - - await evmRevert( - linkToken.transferAndCall(registrar.address, amount, abiEncodedBytes), - 'Amount mismatch', - ) - }) - - it('reverts if the admin address is 0x0000...', async () => { - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - '0x0000000000000000000000000000000000000000', - emptyBytes, - amount, - source, - ], - ) - - await evmRevert( - linkToken.transferAndCall(registrar.address, amount, abiEncodedBytes), - 'Unable to create request', - ) - }) - - it('Auto Approve ON - registers an upkeep on KeeperRegistry instantly and emits both RegistrationRequested and RegistrationApproved events', async () => { - //get current upkeep count - const upkeepCount = await registry.getUpkeepCount() - - //set auto approve ON with high threshold limits - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - true, - window_small, - threshold_big, - registry.address, - minLINKJuels, - ) - - //register with auto approve ON - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - ], - ) - const tx = await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - - //confirm if a new upkeep has been registered and the details are the same as the one just registered - const newupkeep = await registry.getUpkeep(upkeepCount) - assert.equal(newupkeep.target, mock.address) - assert.equal(newupkeep.admin, await admin.getAddress()) - assert.equal(newupkeep.checkData, emptyBytes) - assert.equal(newupkeep.balance.toString(), amount.toString()) - assert.equal(newupkeep.executeGas, executeGas.toNumber()) - - await expect(tx).to.emit(registrar, 'RegistrationRequested') - await expect(tx).to.emit(registrar, 'RegistrationApproved') - }) - - it('Auto Approve OFF - does not registers an upkeep on KeeperRegistry, emits only RegistrationRequested event', async () => { - //get upkeep count before attempting registration - const beforeCount = await registry.getUpkeepCount() - - //set auto approve OFF, threshold limits dont matter in this case - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - false, - window_small, - threshold_big, - registry.address, - minLINKJuels, - ) - - //register with auto approve OFF - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - ], - ) - const tx = await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - const receipt = await tx.wait() - - //get upkeep count after attempting registration - const afterCount = await registry.getUpkeepCount() - //confirm that a new upkeep has NOT been registered and upkeep count is still the same - assert.deepEqual(beforeCount, afterCount) - - //confirm that only RegistrationRequested event is emitted and RegistrationApproved event is not - await expect(tx).to.emit(registrar, 'RegistrationRequested') - await expect(tx).not.to.emit(registrar, 'RegistrationApproved') - - const hash = receipt.logs[2].topics[1] - const pendingRequest = await registrar.getPendingRequest(hash) - assert.equal(await admin.getAddress(), pendingRequest[0]) - assert.ok(amount.eq(pendingRequest[1])) - }) - - it('Auto Approve ON - Throttle max approvals - does not registers an upkeep on KeeperRegistry beyond the throttle limit, emits only RegistrationRequested event after throttle starts', async () => { - //get upkeep count before attempting registration - const beforeCount = await registry.getUpkeepCount() - - //set auto approve on, with low threshold limits - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - true, - window_big, - threshold_small, - registry.address, - minLINKJuels, - ) - - let abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - ]) - - //register within threshold, new upkeep should be registered - await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - const intermediateCount = await registry.getUpkeepCount() - //make sure 1 upkeep was registered - assert.equal(beforeCount.toNumber() + 1, intermediateCount.toNumber()) - - //try registering more than threshold(say 2x), new upkeeps should not be registered after the threshold amount is reached - for (let step = 0; step < threshold_small.toNumber() * 2; step++) { - abiEncodedBytes = registrar.interface.encodeFunctionData('register', [ - upkeepName, - emptyBytes, - mock.address, - executeGas.toNumber() + step, // make unique hash - await admin.getAddress(), - emptyBytes, - amount, - source, - ]) - - await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - } - const afterCount = await registry.getUpkeepCount() - //count of newly registered upkeeps should be equal to the threshold set for auto approval - const newRegistrationsCount = - afterCount.toNumber() - beforeCount.toNumber() - assert( - newRegistrationsCount == threshold_small.toNumber(), - 'Registrations beyond threshold', - ) - }) - }) - - describe('#approve', () => { - let hash: string - - beforeEach(async () => { - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - false, - window_small, - threshold_big, - registry.address, - minLINKJuels, - ) - - //register with auto approve OFF - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - ], - ) - - const tx = await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - const receipt = await tx.wait() - hash = receipt.logs[2].topics[1] - }) - - it('reverts if not called by the owner', async () => { - const tx = registrar - .connect(stranger) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevert(tx, 'Only callable by owner') - }) - - it('reverts if the hash does not exist', async () => { - const tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44', - ) - await evmRevert(tx, errorMsgs.requestNotFound) - }) - - it('reverts if any member of the payload changes', async () => { - let tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - ethers.Wallet.createRandom().address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevert(tx, errorMsgs.hashPayload) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - 10000, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevert(tx, errorMsgs.hashPayload) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - ethers.Wallet.createRandom().address, - emptyBytes, - hash, - ) - await evmRevert(tx, errorMsgs.hashPayload) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - '0x1234', - hash, - ) - await evmRevert(tx, errorMsgs.hashPayload) - }) - - it('approves an existing registration request', async () => { - const tx = await registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await expect(tx).to.emit(registrar, 'RegistrationApproved') - }) - - it('deletes the request afterwards / reverts if the request DNE', async () => { - await registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - const tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevert(tx, errorMsgs.requestNotFound) - }) - }) - - describe('#cancel', () => { - let hash: string - - beforeEach(async () => { - await registrar - .connect(registrarOwner) - .setRegistrationConfig( - false, - window_small, - threshold_big, - registry.address, - minLINKJuels, - ) - - //register with auto approve OFF - const abiEncodedBytes = registrar.interface.encodeFunctionData( - 'register', - [ - upkeepName, - emptyBytes, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - amount, - source, - ], - ) - const tx = await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - const receipt = await tx.wait() - hash = receipt.logs[2].topics[1] - // submit duplicate request (increase balance) - await linkToken.transferAndCall( - registrar.address, - amount, - abiEncodedBytes, - ) - }) - - it('reverts if not called by the admin / owner', async () => { - const tx = registrar.connect(stranger).cancel(hash) - await evmRevert(tx, errorMsgs.onlyAdmin) - }) - - it('reverts if the hash does not exist', async () => { - const tx = registrar - .connect(registrarOwner) - .cancel( - '0x000000000000000000000000322813fd9a801c5507c9de605d63cea4f2ce6c44', - ) - await evmRevert(tx, 'request not found') - }) - - it('refunds the total request balance to the admin address', async () => { - const before = await linkToken.balanceOf(await admin.getAddress()) - const tx = await registrar.connect(admin).cancel(hash) - const after = await linkToken.balanceOf(await admin.getAddress()) - assert.isTrue(after.sub(before).eq(amount.mul(BigNumber.from(2)))) - await expect(tx).to.emit(registrar, 'RegistrationRejected') - }) - - it('deletes the request hash', async () => { - await registrar.connect(registrarOwner).cancel(hash) - let tx = registrar.connect(registrarOwner).cancel(hash) - await evmRevert(tx, errorMsgs.requestNotFound) - tx = registrar - .connect(registrarOwner) - .approve( - upkeepName, - mock.address, - executeGas, - await admin.getAddress(), - emptyBytes, - hash, - ) - await evmRevert(tx, errorMsgs.requestNotFound) - }) - }) -}) diff --git a/contracts/test/v0.7/VRFD20.test.ts b/contracts/test/v0.7/VRFD20.test.ts deleted file mode 100644 index f1e0e9ab0a8..00000000000 --- a/contracts/test/v0.7/VRFD20.test.ts +++ /dev/null @@ -1,303 +0,0 @@ -import { ethers } from 'hardhat' -import { assert, expect } from 'chai' -import { - BigNumber, - constants, - Contract, - ContractFactory, - ContractTransaction, -} from 'ethers' -import { getUsers, Personas, Roles } from '../test-helpers/setup' -import { - evmWordToAddress, - getLog, - publicAbi, - toBytes32String, - toWei, - numToBytes32, - getLogs, -} from '../test-helpers/helpers' - -let roles: Roles -let personas: Personas -let linkTokenFactory: ContractFactory -let vrfCoordinatorMockFactory: ContractFactory -let vrfD20Factory: ContractFactory - -before(async () => { - const users = await getUsers() - - roles = users.roles - personas = users.personas - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) - vrfCoordinatorMockFactory = await ethers.getContractFactory( - 'src/v0.7/tests/VRFCoordinatorMock.sol:VRFCoordinatorMock', - roles.defaultAccount, - ) - vrfD20Factory = await ethers.getContractFactory( - 'src/v0.6/examples/VRFD20.sol:VRFD20', - roles.defaultAccount, - ) -}) - -describe('VRFD20', () => { - const deposit = toWei('1') - const fee = toWei('0.1') - const keyHash = toBytes32String('keyHash') - - let link: Contract - let vrfCoordinator: Contract - let vrfD20: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - vrfCoordinator = await vrfCoordinatorMockFactory - .connect(roles.defaultAccount) - .deploy(link.address) - vrfD20 = await vrfD20Factory - .connect(roles.defaultAccount) - .deploy(vrfCoordinator.address, link.address, keyHash, fee) - await link.transfer(vrfD20.address, deposit) - }) - - it('has a limited public interface [ @skip-coverage ]', () => { - publicAbi(vrfD20, [ - // Owned - 'acceptOwnership', - 'owner', - 'transferOwnership', - //VRFConsumerBase - 'rawFulfillRandomness', - // VRFD20 - 'rollDice', - 'house', - 'withdrawLINK', - 'keyHash', - 'fee', - 'setKeyHash', - 'setFee', - ]) - }) - - describe('#withdrawLINK', () => { - describe('failure', () => { - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20 - .connect(roles.stranger) - .withdrawLINK(await roles.stranger.getAddress(), deposit), - ).to.be.revertedWith('Only callable by owner') - }) - - it('reverts when not enough LINK in the contract', async () => { - const withdrawAmount = deposit.mul(2) - await expect( - vrfD20 - .connect(roles.defaultAccount) - .withdrawLINK( - await roles.defaultAccount.getAddress(), - withdrawAmount, - ), - ).to.be.reverted - }) - }) - - describe('success', () => { - it('withdraws LINK', async () => { - const startingAmount = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - const expectedAmount = BigNumber.from(startingAmount).add(deposit) - await vrfD20 - .connect(roles.defaultAccount) - .withdrawLINK(await roles.defaultAccount.getAddress(), deposit) - const actualAmount = await link.balanceOf( - await roles.defaultAccount.getAddress(), - ) - assert.equal(actualAmount.toString(), expectedAmount.toString()) - }) - }) - }) - - describe('#setKeyHash', () => { - const newHash = toBytes32String('newhash') - - describe('failure', () => { - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20.connect(roles.stranger).setKeyHash(newHash), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('success', () => { - it('sets the key hash', async () => { - await vrfD20.setKeyHash(newHash) - const actualHash = await vrfD20.keyHash() - assert.equal(actualHash, newHash) - }) - }) - }) - - describe('#setFee', () => { - const newFee = 1234 - - describe('failure', () => { - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20.connect(roles.stranger).setFee(newFee), - ).to.be.revertedWith('Only callable by owner') - }) - }) - - describe('success', () => { - it('sets the fee', async () => { - await vrfD20.setFee(newFee) - const actualFee = await vrfD20.fee() - assert.equal(actualFee.toString(), newFee.toString()) - }) - }) - }) - - describe('#house', () => { - describe('failure', () => { - it('reverts when dice not rolled', async () => { - await expect( - vrfD20.house(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Dice not rolled') - }) - - it('reverts when dice roll is in progress', async () => { - await vrfD20.rollDice(await personas.Nancy.getAddress()) - await expect( - vrfD20.house(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Roll in progress') - }) - }) - - describe('success', () => { - it('returns the correct house', async () => { - const randomness = 98765 - const expectedHouse = 'Martell' - const tx = await vrfD20.rollDice(await personas.Nancy.getAddress()) - const log = await getLog(tx, 3) - const eventRequestId = log?.topics?.[1] - await vrfCoordinator.callBackWithRandomness( - eventRequestId, - randomness, - vrfD20.address, - ) - const response = await vrfD20.house(await personas.Nancy.getAddress()) - assert.equal(response.toString(), expectedHouse) - }) - }) - }) - - describe('#rollDice', () => { - describe('success', () => { - let tx: ContractTransaction - beforeEach(async () => { - tx = await vrfD20.rollDice(await personas.Nancy.getAddress()) - }) - - it('emits a RandomnessRequest event from the VRFCoordinator', async () => { - const log = await getLog(tx, 2) - const topics = log?.topics - assert.equal(evmWordToAddress(topics?.[1]), vrfD20.address) - assert.equal(topics?.[2], keyHash) - assert.equal(topics?.[3], constants.HashZero) - }) - }) - - describe('failure', () => { - it('reverts when LINK balance is zero', async () => { - const vrfD202 = await vrfD20Factory - .connect(roles.defaultAccount) - .deploy(vrfCoordinator.address, link.address, keyHash, fee) - await expect( - vrfD202.rollDice(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Not enough LINK to pay fee') - }) - - it('reverts when called by a non-owner', async () => { - await expect( - vrfD20 - .connect(roles.stranger) - .rollDice(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Only callable by owner') - }) - - it('reverts when the roller rolls more than once', async () => { - await vrfD20.rollDice(await personas.Nancy.getAddress()) - await expect( - vrfD20.rollDice(await personas.Nancy.getAddress()), - ).to.be.revertedWith('Already rolled') - }) - }) - }) - - describe('#fulfillRandomness', () => { - const randomness = 98765 - const expectedModResult = (randomness % 20) + 1 - const expectedHouse = 'Martell' - let eventRequestId: string - beforeEach(async () => { - const tx = await vrfD20.rollDice(await personas.Nancy.getAddress()) - const log = await getLog(tx, 3) - eventRequestId = log?.topics?.[1] - }) - - describe('success', () => { - let tx: ContractTransaction - beforeEach(async () => { - tx = await vrfCoordinator.callBackWithRandomness( - eventRequestId, - randomness, - vrfD20.address, - ) - }) - - it('emits a DiceLanded event', async () => { - const log = await getLog(tx, 0) - assert.equal(log?.topics[1], eventRequestId) - assert.equal(log?.topics[2], numToBytes32(expectedModResult)) - }) - - it('sets the correct dice roll result', async () => { - const response = await vrfD20.house(await personas.Nancy.getAddress()) - assert.equal(response.toString(), expectedHouse) - }) - - it('allows someone else to roll', async () => { - const secondRandomness = 55555 - tx = await vrfD20.rollDice(await personas.Ned.getAddress()) - const log = await getLog(tx, 3) - eventRequestId = log?.topics?.[1] - tx = await vrfCoordinator.callBackWithRandomness( - eventRequestId, - secondRandomness, - vrfD20.address, - ) - }) - }) - - describe('failure', () => { - it('does not fulfill when fulfilled by the wrong VRFcoordinator', async () => { - const vrfCoordinator2 = await vrfCoordinatorMockFactory - .connect(roles.defaultAccount) - .deploy(link.address) - - const tx = await vrfCoordinator2.callBackWithRandomness( - eventRequestId, - randomness, - vrfD20.address, - ) - const logs = await getLogs(tx) - assert.equal(logs.length, 0) - }) - }) - }) -}) diff --git a/contracts/test/v0.7/gasUsage.test.ts b/contracts/test/v0.7/gasUsage.test.ts deleted file mode 100644 index 97146622d06..00000000000 --- a/contracts/test/v0.7/gasUsage.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { ethers } from 'hardhat' -import { toBytes32String, toWei } from '../test-helpers/helpers' -import { Contract, ContractFactory } from 'ethers' -import { getUsers, Roles } from '../test-helpers/setup' -import { - convertFufillParams, - convertFulfill2Params, - decodeRunRequest, -} from '../test-helpers/oracle' -import { gasDiffLessThan } from '../test-helpers/matchers' - -let operatorFactory: ContractFactory -let oracleFactory: ContractFactory -let basicConsumerFactory: ContractFactory -let linkTokenFactory: ContractFactory - -let roles: Roles - -before(async () => { - const users = await getUsers() - - roles = users.roles - operatorFactory = await ethers.getContractFactory( - 'src/v0.7/Operator.sol:Operator', - roles.defaultAccount, - ) - oracleFactory = await ethers.getContractFactory( - 'src/v0.6/Oracle.sol:Oracle', - roles.defaultAccount, - ) - basicConsumerFactory = await ethers.getContractFactory( - 'src/v0.6/tests/BasicConsumer.sol:BasicConsumer', - roles.defaultAccount, - ) - linkTokenFactory = await ethers.getContractFactory( - 'src/v0.4/LinkToken.sol:LinkToken', - roles.defaultAccount, - ) -}) - -describe('Operator Gas Tests [ @skip-coverage ]', () => { - const specId = - '0x4c7b7ffb66b344fbaa64995af81e355a00000000000000000000000000000000' - let link: Contract - let oracle1: Contract - let operator1: Contract - let operator2: Contract - - beforeEach(async () => { - link = await linkTokenFactory.connect(roles.defaultAccount).deploy() - - operator1 = await operatorFactory - .connect(roles.defaultAccount) - .deploy(link.address, await roles.defaultAccount.getAddress()) - await operator1.setAuthorizedSenders([await roles.oracleNode.getAddress()]) - - operator2 = await operatorFactory - .connect(roles.defaultAccount) - .deploy(link.address, await roles.defaultAccount.getAddress()) - await operator2.setAuthorizedSenders([await roles.oracleNode.getAddress()]) - - oracle1 = await oracleFactory - .connect(roles.defaultAccount) - .deploy(link.address) - await oracle1.setFulfillmentPermission( - await roles.oracleNode.getAddress(), - true, - ) - }) - - // Test Oracle.fulfillOracleRequest vs Operator.fulfillOracleRequest - describe('v0.6/Oracle vs v0.7/Operator #fulfillOracleRequest', () => { - const response = 'Hi Mom!' - let basicConsumer1: Contract - let basicConsumer2: Contract - - let request1: ReturnType - let request2: ReturnType - - beforeEach(async () => { - basicConsumer1 = await basicConsumerFactory - .connect(roles.consumer) - .deploy(link.address, oracle1.address, specId) - basicConsumer2 = await basicConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator1.address, specId) - - const paymentAmount = toWei('1') - const currency = 'USD' - - await link.transfer(basicConsumer1.address, paymentAmount) - const tx1 = await basicConsumer1.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt1 = await tx1.wait() - request1 = decodeRunRequest(receipt1.logs?.[3]) - - await link.transfer(basicConsumer2.address, paymentAmount) - const tx2 = await basicConsumer2.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt2 = await tx2.wait() - request2 = decodeRunRequest(receipt2.logs?.[3]) - }) - - it('uses acceptable gas', async () => { - const tx1 = await oracle1 - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request1, response)) - const tx2 = await operator1 - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request2, response)) - const receipt1 = await tx1.wait() - const receipt2 = await tx2.wait() - // 38014 vs 40260 - gasDiffLessThan(3900, receipt1, receipt2) - }) - }) - - // Test Operator1.fulfillOracleRequest vs Operator2.fulfillOracleRequest2 - // with single word response - describe('Operator #fulfillOracleRequest vs #fulfillOracleRequest2', () => { - const response = 'Hi Mom!' - let basicConsumer1: Contract - let basicConsumer2: Contract - - let request1: ReturnType - let request2: ReturnType - - beforeEach(async () => { - basicConsumer1 = await basicConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator1.address, specId) - basicConsumer2 = await basicConsumerFactory - .connect(roles.consumer) - .deploy(link.address, operator2.address, specId) - - const paymentAmount = toWei('1') - const currency = 'USD' - - await link.transfer(basicConsumer1.address, paymentAmount) - const tx1 = await basicConsumer1.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt1 = await tx1.wait() - request1 = decodeRunRequest(receipt1.logs?.[3]) - - await link.transfer(basicConsumer2.address, paymentAmount) - const tx2 = await basicConsumer2.requestEthereumPrice( - currency, - paymentAmount, - ) - const receipt2 = await tx2.wait() - request2 = decodeRunRequest(receipt2.logs?.[3]) - }) - - it('uses acceptable gas', async () => { - const tx1 = await operator1 - .connect(roles.oracleNode) - .fulfillOracleRequest(...convertFufillParams(request1, response)) - - const responseTypes = ['bytes32'] - const responseValues = [toBytes32String(response)] - const tx2 = await operator2 - .connect(roles.oracleNode) - .fulfillOracleRequest2( - ...convertFulfill2Params(request2, responseTypes, responseValues), - ) - - const receipt1 = await tx1.wait() - const receipt2 = await tx2.wait() - gasDiffLessThan(1240, receipt1, receipt2) - }) - }) -}) From b058357980ef75a06602fd26f19db4b0a1a286ce Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 13 Nov 2023 10:14:24 +0100 Subject: [PATCH 129/271] bump foundry (#11245) --- .github/workflows/solidity-foundry.yml | 2 +- contracts/GNUmakefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index 7c9df796171..90d18ecac2e 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -58,7 +58,7 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 with: # Has to match the `make foundry` version. - version: nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b + version: nightly-09fe3e041369a816365a020f715ad6f94dbce9f2 - name: Run Forge build if: needs.changes.outputs.changes == 'true' diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile index e41d6422c2f..e8808138673 100644 --- a/contracts/GNUmakefile +++ b/contracts/GNUmakefile @@ -38,7 +38,7 @@ mockery: $(mockery) ## Install mockery. .PHONY: foundry foundry: ## Install foundry. - foundryup --version nightly-5be158ba6dc7c798a6f032026fe60fc01686b33b + foundryup --version nightly-09fe3e041369a816365a020f715ad6f94dbce9f2 .PHONY: foundry-refresh foundry-refresh: foundry From 35e146fcb61e9036b747bd32ee76cf0d039bd344 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Mon, 13 Nov 2023 14:54:48 +0100 Subject: [PATCH 130/271] Eth2 showcase with log poller (#11214) * move workflow to correct directory * streamline on-demand values a bit * get RPC urls and private keys from secrets * download and run from inside the test folder * checkout repo before running tests * get inputs and mask them * fix step ordering in workflow * fix default image tag * use latest pumba@CTF * run one test * fix directory name * run on powerful runner * show usage of ethereum env builder with eth2 * update usage of eth2 * adjust to latest --- integration-tests/docker/test_env/test_env.go | 46 +++++-- .../docker/test_env/test_env_builder.go | 68 +++++++--- .../docker/test_env/test_env_config.go | 10 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/smoke/automation_test.go | 4 +- integration-tests/smoke/log_poller_test.go | 128 +++++++++++++++++- .../universal/log_poller/helpers.go | 13 +- 8 files changed, 230 insertions(+), 45 deletions(-) diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index b3fc1c0cf6c..4a304872116 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -40,15 +40,16 @@ type CLClusterTestEnv struct { LogWatch *logwatch.LogWatch /* components */ - ClCluster *ClCluster - Geth *test_env.Geth // for tests using --dev networks - PrivateChain []test_env.PrivateChain // for tests using non-dev networks - MockAdapter *test_env.Killgrave - EVMClient blockchain.EVMClient - ContractDeployer contracts.ContractDeployer - ContractLoader contracts.ContractLoader - l zerolog.Logger - t *testing.T + ClCluster *ClCluster + PrivateChain []test_env.PrivateChain // for tests using non-dev networks -- unify it with new approach + MockAdapter *test_env.Killgrave + EVMClient blockchain.EVMClient + ContractDeployer contracts.ContractDeployer + ContractLoader contracts.ContractLoader + RpcProvider test_env.RpcProvider + PrivateEthereumConfig *test_env.EthereumNetwork // new approach to private chains, supporting eth1 and eth2 + l zerolog.Logger + t *testing.T } func NewTestEnv() (*CLClusterTestEnv, error) { @@ -59,7 +60,6 @@ func NewTestEnv() (*CLClusterTestEnv, error) { } n := []string{network.Name} return &CLClusterTestEnv{ - Geth: test_env.NewGeth(n), MockAdapter: test_env.NewKillgrave(n, ""), Network: network, l: log.Logger, @@ -67,11 +67,10 @@ func NewTestEnv() (*CLClusterTestEnv, error) { } // WithTestEnvConfig sets the test environment cfg. -// Sets up the Geth and MockAdapter containers with the provided cfg. +// Sets up private ethereum chain and MockAdapter containers with the provided cfg. func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTestEnv { te.Cfg = cfg n := []string{te.Network.Name} - te.Geth = test_env.NewGeth(n, test_env.WithContainerName(te.Cfg.Geth.ContainerName)) te.MockAdapter = test_env.NewKillgrave(n, te.Cfg.MockAdapter.ImpostersPath, test_env.WithContainerName(te.Cfg.MockAdapter.ContainerName)) return te } @@ -79,7 +78,6 @@ func (te *CLClusterTestEnv) WithTestEnvConfig(cfg *TestEnvConfig) *CLClusterTest func (te *CLClusterTestEnv) WithTestLogger(t *testing.T) *CLClusterTestEnv { te.t = t te.l = logging.GetTestLogger(t) - te.Geth.WithTestLogger(t) te.MockAdapter.WithTestLogger(t) return te } @@ -128,8 +126,26 @@ func (te *CLClusterTestEnv) StartPrivateChain() error { return nil } -func (te *CLClusterTestEnv) StartGeth() (blockchain.EVMNetwork, test_env.InternalDockerUrls, error) { - return te.Geth.StartContainer() +func (te *CLClusterTestEnv) StartEthereumNetwork(cfg *test_env.EthereumNetwork) (blockchain.EVMNetwork, test_env.RpcProvider, error) { + // if environment is being restored from a previous state, use the existing config + // this might fail terribly if temporary folders with chain data on the host machine were removed + if te.Cfg != nil && te.Cfg.EthereumNetwork != nil { + builder := test_env.NewEthereumNetworkBuilder() + c, err := builder.WithExistingConfig(*te.Cfg.EthereumNetwork). + WithTest(te.t). + Build() + if err != nil { + return blockchain.EVMNetwork{}, test_env.RpcProvider{}, err + } + cfg = &c + } + n, rpc, err := cfg.Start() + + if err != nil { + return blockchain.EVMNetwork{}, test_env.RpcProvider{}, err + } + + return n, rpc, nil } func (te *CLClusterTestEnv) StartMockAdapter() error { diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 9f64ab64c98..e97f869d64e 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -31,8 +31,8 @@ const ( ) type CLTestEnvBuilder struct { - hasLogWatch bool - hasGeth bool + hasLogWatch bool + // hasGeth bool hasKillgrave bool hasForwarders bool clNodeConfig *chainlink.Config @@ -49,6 +49,7 @@ type CLTestEnvBuilder struct { cleanUpCustomFn func() chainOptionsFn []ChainOption evmClientNetworkOption []EVMClientNetworkOption + ethereumNetwork *test_env.EthereumNetwork /* funding */ ETHFunds *big.Float @@ -118,8 +119,27 @@ func (b *CLTestEnvBuilder) WithFunding(eth *big.Float) *CLTestEnvBuilder { return b } +// deprecated +// left only for backward compatibility func (b *CLTestEnvBuilder) WithGeth() *CLTestEnvBuilder { - b.hasGeth = true + ethBuilder := test_env.NewEthereumNetworkBuilder() + cfg, err := ethBuilder. + WithConsensusType(test_env.ConsensusType_PoW). + WithExecutionLayer(test_env.ExecutionLayer_Geth). + WithTest(b.t). + Build() + + if err != nil { + panic(err) + } + + b.ethereumNetwork = &cfg + + return b +} + +func (b *CLTestEnvBuilder) WithPrivateEthereumNetwork(en test_env.EthereumNetwork) *CLTestEnvBuilder { + b.ethereumNetwork = &en return b } @@ -191,13 +211,6 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { return nil, err } } - b.l.Info(). - Bool("hasGeth", b.hasGeth). - Bool("hasKillgrave", b.hasKillgrave). - Int("clNodesCount", b.clNodesCount). - Strs("customNodeCsaKeys", b.customNodeCsaKeys). - Strs("defaultNodeCsaKeys", b.defaultNodeCsaKeys). - Msg("Building CL cluster test environment..") var err error if b.t != nil { @@ -259,13 +272,21 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } return b.te, nil } + networkConfig := networks.MustGetSelectedNetworksFromEnv()[0] - var internalDockerUrls test_env.InternalDockerUrls - if b.hasGeth && networkConfig.Simulated { - networkConfig, internalDockerUrls, err = b.te.StartGeth() + var rpcProvider test_env.RpcProvider + if b.ethereumNetwork != nil && networkConfig.Simulated { + // TODO here we should save the ethereum network config to te.Cfg, but it doesn't exist at this point + // in general it seems we have no methods for saving config to file and we only load it from file + // but I don't know how that config file is to be created or whether anyone ever done that + var enCfg test_env.EthereumNetwork + b.ethereumNetwork.DockerNetworkNames = []string{b.te.Network.Name} + networkConfig, rpcProvider, err = b.te.StartEthereumNetwork(b.ethereumNetwork) if err != nil { return nil, err } + b.te.RpcProvider = rpcProvider + b.te.PrivateEthereumConfig = &enCfg } if !b.isNonEVM { @@ -311,8 +332,8 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { var httpUrls []string var wsUrls []string if networkConfig.Simulated { - httpUrls = []string{internalDockerUrls.HttpUrl} - wsUrls = []string{internalDockerUrls.WsUrl} + httpUrls = rpcProvider.PrivateHttpUrls() + wsUrls = rpcProvider.PrivateWsUrsl() } else { httpUrls = networkConfig.HTTPURLs wsUrls = networkConfig.URLs @@ -341,7 +362,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { b.defaultNodeCsaKeys = nodeCsaKeys } - if b.hasGeth && b.clNodesCount > 0 && b.ETHFunds != nil { + if b.ethereumNetwork != nil && b.clNodesCount > 0 && b.ETHFunds != nil { b.te.ParallelTransactions(true) defer b.te.ParallelTransactions(false) if err := b.te.FundChainlinkNodes(b.ETHFunds); err != nil { @@ -349,5 +370,20 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } } + var enDesc string + if b.te.PrivateEthereumConfig != nil { + enDesc = b.te.PrivateEthereumConfig.Describe() + } else { + enDesc = "none" + } + + b.l.Info(). + Str("privateEthereumNetwork", enDesc). + Bool("hasKillgrave", b.hasKillgrave). + Int("clNodesCount", b.clNodesCount). + Strs("customNodeCsaKeys", b.customNodeCsaKeys). + Strs("defaultNodeCsaKeys", b.defaultNodeCsaKeys). + Msg("Building CL cluster test environment..") + return b.te, nil } diff --git a/integration-tests/docker/test_env/test_env_config.go b/integration-tests/docker/test_env/test_env_config.go index 1a0c8d5c86a..0902deb0c2d 100644 --- a/integration-tests/docker/test_env/test_env_config.go +++ b/integration-tests/docker/test_env/test_env_config.go @@ -3,14 +3,16 @@ package test_env import ( "encoding/json" + cte "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" env "github.com/smartcontractkit/chainlink/integration-tests/types/envcommon" ) type TestEnvConfig struct { - Networks []string `json:"networks"` - Geth GethConfig `json:"geth"` - MockAdapter MockAdapterConfig `json:"mock_adapter"` - ClCluster *ClCluster `json:"clCluster"` + Networks []string `json:"networks"` + Geth GethConfig `json:"geth"` + MockAdapter MockAdapterConfig `json:"mock_adapter"` + ClCluster *ClCluster `json:"clCluster"` + EthereumNetwork *cte.EthereumNetwork `json:"private_ethereum_config"` } type MockAdapterConfig struct { diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 83657baa011..6a7e7195ff5 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,7 +22,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework v1.18.5 + github.com/smartcontractkit/chainlink-testing-framework v1.18.6 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index a873f9b7c16..780e81c7ef1 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2375,8 +2375,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.5 h1:R0f13AUbon1ltHE/vudkyUnLRGaoeocIDVv+FsHZjno= -github.com/smartcontractkit/chainlink-testing-framework v1.18.5/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.18.6 h1:UL3DxsPflSRALP62rsg5v3NdOsa8RHGhHMUImoWDD6k= +github.com/smartcontractkit/chainlink-testing-framework v1.18.6/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 9e35b24df1e..4f969c5d68d 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1070,8 +1070,8 @@ func setupAutomationTestDocker( var httpUrls []string var wsUrls []string if network.Simulated { - httpUrls = []string{env.Geth.InternalHttpUrl} - wsUrls = []string{env.Geth.InternalWsUrl} + httpUrls = []string{env.RpcProvider.PrivateHttpUrls()[0]} + wsUrls = []string{env.RpcProvider.PrivateWsUrsl()[0]} } else { httpUrls = network.HTTPURLs wsUrls = network.URLs diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 0df7817f1e3..36ee2164c45 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -9,7 +9,7 @@ import ( // consistency test with no network disruptions with approximate emission of 1500-1600 logs per second for ~110-120 seconds // 6 filters are registered -func TestLogPollerFewFilters(t *testing.T) { +func TestLogPollerFewFiltersFixedDepth(t *testing.T) { cfg := logpoller.Config{ General: &logpoller.General{ Generator: logpoller.GeneratorType_Looped, @@ -38,9 +38,38 @@ func TestLogPollerFewFilters(t *testing.T) { logpoller.ExecuteBasicLogPollerTest(t, &cfg) } +func TestLogPollerFewFiltersFinalityTag(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 4, + UseFinalityTag: true, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + logpoller.ExecuteBasicLogPollerTest(t, &cfg) +} + // consistency test with no network disruptions with approximate emission of 1000-1100 logs per second for ~110-120 seconds // 900 filters are registered -func TestLogManyFiltersPoller(t *testing.T) { +func TestLogManyFiltersPollerFixedDepth(t *testing.T) { cfg := logpoller.Config{ General: &logpoller.General{ Generator: logpoller.GeneratorType_Looped, @@ -69,10 +98,39 @@ func TestLogManyFiltersPoller(t *testing.T) { logpoller.ExecuteBasicLogPollerTest(t, &cfg) } +func TestLogManyFiltersPollerFinalityTag(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 300, + EventsPerTx: 3, + UseFinalityTag: true, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 30, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + logpoller.ExecuteBasicLogPollerTest(t, &cfg) +} + // consistency test that introduces random distruptions by pausing either Chainlink or Postgres containers for random interval of 5-20 seconds // with approximate emission of 520-550 logs per second for ~110 seconds // 6 filters are registered -func TestLogPollerWithChaos(t *testing.T) { +func TestLogPollerWithChaosFixedDepth(t *testing.T) { cfg := logpoller.Config{ General: &logpoller.General{ Generator: logpoller.GeneratorType_Looped, @@ -104,12 +162,74 @@ func TestLogPollerWithChaos(t *testing.T) { logpoller.ExecuteBasicLogPollerTest(t, &cfg) } +func TestLogPollerWithChaosFinalityTag(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 100, + UseFinalityTag: true, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + ChaosConfig: &logpoller.ChaosConfig{ + ExperimentCount: 10, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + + logpoller.ExecuteBasicLogPollerTest(t, &cfg) +} + // consistency test that registers filters after events were emitted and then triggers replay via API // unfortunately there is no way to make sure that logs that are indexed are only picked up by replay // and not by backup poller // with approximate emission of 24 logs per second for ~110 seconds // 6 filters are registered -func TestLogPollerReplay(t *testing.T) { +func TestLogPollerReplayFixedDepth(t *testing.T) { + cfg := logpoller.Config{ + General: &logpoller.General{ + Generator: logpoller.GeneratorType_Looped, + Contracts: 2, + EventsPerTx: 4, + UseFinalityTag: false, + }, + LoopedConfig: &logpoller.LoopedConfig{ + ContractConfig: logpoller.ContractConfig{ + ExecutionCount: 100, + }, + FuzzConfig: logpoller.FuzzConfig{ + MinEmitWaitTimeMs: 200, + MaxEmitWaitTimeMs: 500, + }, + }, + } + + eventsToEmit := []abi.Event{} + for _, event := range logpoller.EmitterABI.Events { + eventsToEmit = append(eventsToEmit, event) + } + + cfg.General.EventsToEmit = eventsToEmit + consistencyTimeout := "5m" + + logpoller.ExecuteLogPollerReplay(t, &cfg, consistencyTimeout) +} + +func TestLogPollerReplayFinalityTag(t *testing.T) { cfg := logpoller.Config{ General: &logpoller.General{ Generator: logpoller.GeneratorType_Looped, diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index ab7a221955b..9f88827bb48 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -1068,9 +1068,20 @@ func setupLogPollerTestDocker( return network } + ethBuilder := ctf_test_env.NewEthereumNetworkBuilder() + cfg, err := ethBuilder. + WithConsensusType(ctf_test_env.ConsensusType_PoS). + WithConsensusLayer(ctf_test_env.ConsensusLayer_Prysm). + WithExecutionLayer(ctf_test_env.ExecutionLayer_Geth). + WithBeaconChainConfig(ctf_test_env.BeaconChainConfig{ + SecondsPerSlot: 8, + SlotsPerEpoch: 2, + }). + Build() + env, err = test_env.NewCLTestEnvBuilder(). WithTestLogger(t). - WithGeth(). + WithPrivateEthereumNetwork(cfg). WithCLNodes(clNodesCount). WithCLNodeConfig(clNodeConfig). WithFunding(big.NewFloat(chainlinkNodeFunding)). From de5027383ba3120554e8dc88b5b178973585159b Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:04:10 +0000 Subject: [PATCH 131/271] Always wait for doCheck to complete before returning (#10878) * Always wait for doCheck to complete before returning Use threadctrl to avail of context cancellation but still rely on a wait group to blocl until the lookups finish Update tests * Revert async streamsLookup to fix test * Rearrange thread control as a test * Copy index * WIP --- .../ocr2keeper/evm21/registry_check_pipeline.go | 5 ++++- .../plugins/ocr2keeper/evm21/streams_lookup.go | 16 +++++++++++++--- .../ocr2keeper/evm21/streams_lookup_test.go | 15 ++++++++++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go index d3530994702..c9752ea14db 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go @@ -41,7 +41,10 @@ func (r *EvmRegistry) CheckUpkeeps(ctx context.Context, keys ...ocr2keepers.Upke } chResult := make(chan checkResult, 1) - go r.doCheck(ctx, keys, chResult) + + r.threadCtrl.Go(func(ctx context.Context) { + r.doCheck(ctx, keys, chResult) + }) select { case rs := <-chResult: diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go index 660550afe97..fb2821a74b7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go @@ -149,10 +149,15 @@ func (r *EvmRegistry) streamsLookup(ctx context.Context, checkResults []ocr2keep } var wg sync.WaitGroup + for i, lookup := range lookups { + i := i wg.Add(1) - go r.doLookup(ctx, &wg, lookup, i, checkResults, lggr) + r.threadCtrl.Go(func(ctx context.Context) { + r.doLookup(ctx, &wg, lookup, i, checkResults, lggr) + }) } + wg.Wait() // don't surface error to plugin bc StreamsLookup process should be self-contained. @@ -289,14 +294,19 @@ func (r *EvmRegistry) doMercuryRequest(ctx context.Context, sl *StreamsLookup, p if sl.FeedParamKey == feedIdHex && sl.TimeParamKey == blockNumber { // only mercury v0.2 for i := range sl.Feeds { - go r.singleFeedRequest(ctx, ch, i, sl, lggr) + i := i + r.threadCtrl.Go(func(ctx context.Context) { + r.singleFeedRequest(ctx, ch, i, sl, lggr) + }) } } else if sl.FeedParamKey == feedIDs { // only mercury v0.3 resultLen = 1 isMercuryV03 = true ch = make(chan MercuryData, resultLen) - go r.multiFeedsRequest(ctx, ch, sl, lggr) + r.threadCtrl.Go(func(ctx context.Context) { + r.multiFeedsRequest(ctx, ch, sl, lggr) + }) } else { return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go index 8d7c67d80ce..145d701454d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" + "github.com/smartcontractkit/chainlink/v2/core/utils" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -70,7 +71,8 @@ func setupEVMRegistry(t *testing.T) *EvmRegistry { allowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), }, - hc: mockHttpClient, + hc: mockHttpClient, + threadCtrl: utils.NewThreadControl(), } return r } @@ -220,6 +222,7 @@ func TestEvmRegistry_StreamsLookup(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + defer r.Close() client := new(evmClientMocks.Client) r.client = client @@ -362,6 +365,7 @@ func TestEvmRegistry_AllowedToUseMercury(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + defer r.Close() client := new(evmClientMocks.Client) r.client = client @@ -576,9 +580,12 @@ func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + defer r.Close() + if tt.pluginRetries != 0 { r.mercury.pluginRetryCache.Set(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) } + hc := mocks.NewHttpClient(t) for _, blob := range tt.mockChainlinkBlobs { @@ -812,6 +819,8 @@ func TestEvmRegistry_SingleFeedRequest(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + defer r.Close() + hc := mocks.NewHttpClient(t) mr := MercuryV02Response{ChainlinkBlob: tt.blob} @@ -1157,6 +1166,8 @@ func TestEvmRegistry_MultiFeedRequest(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := setupEVMRegistry(t) + defer r.Close() + if tt.pluginRetries != 0 { r.mercury.pluginRetryCache.Set(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) } @@ -1319,6 +1330,8 @@ func TestEvmRegistry_CheckCallback(t *testing.T) { t.Run(tt.name, func(t *testing.T) { client := new(evmClientMocks.Client) r := setupEVMRegistry(t) + defer r.Close() + payload, err := r.abi.Pack("checkCallback", tt.lookup.upkeepId, values, tt.lookup.ExtraData) require.Nil(t, err) args := map[string]interface{}{ From 12062831ae01b21c6ea6bc052107ecbe254da6c4 Mon Sep 17 00:00:00 2001 From: Bruno Moura Date: Mon, 13 Nov 2023 15:04:39 +0000 Subject: [PATCH 132/271] Remove Mercury plugin dependency on raw evm chain. (#11201) * mercury: remove dead code * mercury: Add ChainReader * mercury: services init * fix sqlx import * mercury: error handling * mod tidy * mercury: log error from reader * mercury: ensure a failed observation when reading from chain return an error * mercury: add test for setLatestBlocks error * make a happy linter * Update core/services/relay/evm/mercury_provider.go Co-authored-by: Sam --------- Co-authored-by: Sam --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/ocr2/delegate.go | 6 +- core/services/ocr2/plugins/mercury/plugin.go | 3 +- core/services/relay/evm/evm.go | 5 +- .../evm/mercury/mocks/chain_head_tracker.go | 47 ------------- .../services/relay/evm/mercury/types/types.go | 6 -- .../relay/evm/mercury/v1/data_source.go | 53 +++++++------- .../relay/evm/mercury/v1/data_source_test.go | 70 +++++++++---------- core/services/relay/evm/mercury_provider.go | 38 ++++++++++ go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 14 files changed, 113 insertions(+), 133 deletions(-) delete mode 100644 core/services/relay/evm/mercury/mocks/chain_head_tracker.go diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 5f881f354e6..bb68175ddfc 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 1d455305a94..bd3b75d37aa 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1464,8 +1464,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 h1:ZBsxdB/5iIpl/tWhXe/RHrOwBG7pbKOMeppy5Zt2BVc= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 h1:28XkPE6YfJ4uabTX9/7sueRV6IKtY4hcm1nIt1e6b20= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 95ec1469156..19296c72f00 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -653,10 +653,6 @@ func (d *Delegate) newServicesMercury( if err != nil { return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: "mercury"} } - chain, err := d.legacyChains.Get(rid.ChainID) - if err != nil { - return nil, fmt.Errorf("mercury services: failed to get chain %s: %w", rid.ChainID, err) - } provider, err2 := relayer.NewPluginProvider(ctx, types.RelayArgs{ @@ -695,7 +691,7 @@ func (d *Delegate) newServicesMercury( chEnhancedTelem := make(chan ocrcommon.EnhancedTelemetryMercuryData, 100) - mercuryServices, err2 := mercury.NewServices(jb, mercuryProvider, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, d.cfg.JobPipeline(), chEnhancedTelem, chain, d.mercuryORM, (mercuryutils.FeedID)(*spec.FeedID)) + mercuryServices, err2 := mercury.NewServices(jb, mercuryProvider, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, d.cfg.JobPipeline(), chEnhancedTelem, d.mercuryORM, (mercuryutils.FeedID)(*spec.FeedID)) if ocrcommon.ShouldCollectEnhancedTelemetryMercury(jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, chEnhancedTelem, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.FeedID.String(), synchronization.EnhancedEAMercury), lggr.Named("EnhancedTelemetryMercury")) diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index 69a3b53c284..ddef1374a4c 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -37,7 +37,6 @@ func NewServices( argsNoPlugin libocr2.MercuryOracleArgs, cfg Config, chEnhancedTelem chan ocrcommon.EnhancedTelemetryMercuryData, - chainHeadTracker types.ChainHeadTracker, orm types.DataSourceORM, feedID utils.FeedID, ) ([]job.ServiceCtx, error) { @@ -66,7 +65,7 @@ func NewServices( lggr, runResults, chEnhancedTelem, - chainHeadTracker, + ocr2Provider.ChainReader(), ocr2Provider.MercuryServerFetcher(), pluginConfig.InitialBlockNumber.Ptr(), feedID, diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 111e3622b19..aa1d1d774bd 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -10,10 +10,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/jmoiron/sqlx" pkgerrors "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/jmoiron/sqlx" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median/evmreportcodec" @@ -189,7 +189,8 @@ func (r *Relayer) NewMercuryProvider(rargs relaytypes.RelayArgs, pargs relaytype } transmitter := mercury.NewTransmitter(lggr, cw.ContractConfigTracker(), client, privKey.PublicKey, rargs.JobID, *relayConfig.FeedID, r.db, r.pgCfg, transmitterCodec) - return NewMercuryProvider(cw, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil + chainReader := NewChainReader(r.chain.HeadTracker()) + return NewMercuryProvider(cw, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, chainReader, lggr), nil } func (r *Relayer) NewFunctionsProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (relaytypes.FunctionsProvider, error) { diff --git a/core/services/relay/evm/mercury/mocks/chain_head_tracker.go b/core/services/relay/evm/mercury/mocks/chain_head_tracker.go deleted file mode 100644 index b6f2981cf07..00000000000 --- a/core/services/relay/evm/mercury/mocks/chain_head_tracker.go +++ /dev/null @@ -1,47 +0,0 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. - -package mocks - -import ( - common "github.com/ethereum/go-ethereum/common" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - - mock "github.com/stretchr/testify/mock" - - types "github.com/smartcontractkit/chainlink/v2/common/types" -) - -// ChainHeadTracker is an autogenerated mock type for the ChainHeadTracker type -type ChainHeadTracker struct { - mock.Mock -} - -// HeadTracker provides a mock function with given fields: -func (_m *ChainHeadTracker) HeadTracker() types.HeadTracker[*evmtypes.Head, common.Hash] { - ret := _m.Called() - - var r0 types.HeadTracker[*evmtypes.Head, common.Hash] - if rf, ok := ret.Get(0).(func() types.HeadTracker[*evmtypes.Head, common.Hash]); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(types.HeadTracker[*evmtypes.Head, common.Hash]) - } - } - - return r0 -} - -// NewChainHeadTracker creates a new instance of ChainHeadTracker. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewChainHeadTracker(t interface { - mock.TestingT - Cleanup(func()) -}) *ChainHeadTracker { - mock := &ChainHeadTracker{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/services/relay/evm/mercury/types/types.go b/core/services/relay/evm/mercury/types/types.go index 7059689939a..49bffb6c290 100644 --- a/core/services/relay/evm/mercury/types/types.go +++ b/core/services/relay/evm/mercury/types/types.go @@ -8,15 +8,9 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) -//go:generate mockery --quiet --name ChainHeadTracker --output ../mocks/ --case=underscore -type ChainHeadTracker interface { - HeadTracker() httypes.HeadTracker -} - type DataSourceORM interface { LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg.QOpt) (report []byte, err error) } diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 0f8f56f46e4..0bdfb67de78 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -16,7 +16,6 @@ import ( relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" @@ -67,7 +66,7 @@ type datasource struct { mu sync.RWMutex chEnhancedTelem chan<- ocrcommon.EnhancedTelemetryMercuryData - chainHeadTracker types.ChainHeadTracker + chainReader relaymercury.ChainReader fetcher Fetcher initialBlockNumber *int64 @@ -77,8 +76,8 @@ type datasource struct { var _ relaymercuryv1.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainHeadTracker types.ChainHeadTracker, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { - return &datasource{pr, jb, spec, lggr, rr, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainHeadTracker, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader relaymercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, lggr, rr, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} } type ErrEmptyLatestReport struct { @@ -94,7 +93,11 @@ func (e ErrEmptyLatestReport) Error() string { func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedBlockNum bool) (obs relaymercuryv1.Observation, pipelineExecutionErr error) { // setLatestBlocks must come chronologically before observations, along // with observationTimestamp, to avoid front-running - ds.setLatestBlocks(ctx, &obs) + + // Errors are not expected when reading from the underlying ChainReader + if err := ds.setLatestBlocks(ctx, &obs); err != nil { + return obs, err + } var wg sync.WaitGroup if fetchMaxFinalizedBlockNum { @@ -290,8 +293,13 @@ func (ds *datasource) executeRun(ctx context.Context) (*pipeline.Run, pipeline.T return run, trrs, err } -func (ds *datasource) setLatestBlocks(ctx context.Context, obs *relaymercuryv1.Observation) { - latestBlocks := ds.getLatestBlocks(ctx, nBlocksObservation) +func (ds *datasource) setLatestBlocks(ctx context.Context, obs *relaymercuryv1.Observation) error { + latestBlocks, err := ds.chainReader.LatestHeads(ctx, nBlocksObservation) + if err != nil { + ds.lggr.Errorw("failed to read latest blocks", "error", err) + return err + } + if len(latestBlocks) < nBlocksObservation { ds.insufficientBlocksCounter.Inc() ds.lggr.Warnw("Insufficient blocks", "latestBlocks", latestBlocks, "lenLatestBlocks", len(latestBlocks), "nBlocksObservation", nBlocksObservation) @@ -299,31 +307,22 @@ func (ds *datasource) setLatestBlocks(ctx context.Context, obs *relaymercuryv1.O // TODO: remove with https://smartcontract-it.atlassian.net/browse/BCF-2209 if len(latestBlocks) == 0 { + obsErr := fmt.Errorf("no blocks available") ds.zeroBlocksCounter.Inc() - err := errors.New("no blocks available") - obs.CurrentBlockNum.Err = err - obs.CurrentBlockHash.Err = err - obs.CurrentBlockTimestamp.Err = err + obs.CurrentBlockNum.Err = obsErr + obs.CurrentBlockHash.Err = obsErr + obs.CurrentBlockTimestamp.Err = obsErr } else { - obs.CurrentBlockNum.Val = latestBlocks[0].Number - obs.CurrentBlockHash.Val = latestBlocks[0].Hash.Bytes() - if latestBlocks[0].Timestamp.IsZero() { - obs.CurrentBlockTimestamp.Val = 0 - } else { - obs.CurrentBlockTimestamp.Val = uint64(latestBlocks[0].Timestamp.Unix()) - } + obs.CurrentBlockNum.Val = int64(latestBlocks[0].Number) + obs.CurrentBlockHash.Val = latestBlocks[0].Hash + obs.CurrentBlockTimestamp.Val = latestBlocks[0].Timestamp } for _, block := range latestBlocks { - obs.LatestBlocks = append(obs.LatestBlocks, relaymercuryv1.NewBlock(block.Number, block.Hash.Bytes(), uint64(block.Timestamp.Unix()))) + obs.LatestBlocks = append( + obs.LatestBlocks, + relaymercuryv1.NewBlock(int64(block.Number), block.Hash, block.Timestamp)) } -} -func (ds *datasource) getLatestBlocks(ctx context.Context, k int) (blocks []*evmtypes.Head) { - // Use the headtracker's view of the chain, this is very fast since - // it doesn't make any external network requests, and it is the - // headtracker's job to ensure it has an up-to-date view of the chain based - // on responses from all available RPC nodes - latestHead := ds.chainHeadTracker.HeadTracker().LatestChain() - return latestHead.AsSlice(k) + return nil } diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 42983fa0022..40542c2631a 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -3,6 +3,7 @@ package mercury_v1 import ( "context" "fmt" + "io" "math/big" "math/rand" "testing" @@ -18,7 +19,6 @@ import ( relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/assets" - httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -26,8 +26,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" mercurymocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/types" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" reportcodecv1 "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/v1/reportcodec" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -52,14 +52,6 @@ func (m *mockFetcher) LatestTimestamp(context.Context) (int64, error) { return 0, nil } -var _ types.ChainHeadTracker = &mockHeadTracker{} - -type mockHeadTracker struct { - h httypes.HeadTracker -} - -func (m *mockHeadTracker) HeadTracker() httypes.HeadTracker { return m.h } - type mockORM struct { report []byte err error @@ -69,6 +61,15 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg return m.report, m.err } +type mockChainReader struct { + err error + obs []relaymercury.Head +} + +func (m *mockChainReader) LatestHeads(context.Context, int) ([]relaymercury.Head, error) { + return m.obs, m.err +} + func TestMercury_Observe(t *testing.T) { orm := &mockORM{} lggr := logger.TestLogger(t) @@ -106,10 +107,7 @@ func TestMercury_Observe(t *testing.T) { ds.spec = spec h := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - ht := &mockHeadTracker{ - h: h, - } - ds.chainHeadTracker = ht + ds.chainReader = evm.NewChainReader(h) head := &evmtypes.Head{ Number: int64(rand.Int31()), @@ -201,7 +199,7 @@ func TestMercury_Observe(t *testing.T) { t.Run("if no current block available", func(t *testing.T) { h2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) h2.On("LatestChain").Return((*evmtypes.Head)(nil)) - ht.h = h2 + ds.chainReader = evm.NewChainReader(h2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -212,7 +210,7 @@ func TestMercury_Observe(t *testing.T) { }) }) - ht.h = h + ds.chainReader = evm.NewChainReader(h) t.Run("when fetchMaxFinalizedBlockNum=false", func(t *testing.T) { t.Run("when run execution fails, returns error", func(t *testing.T) { @@ -320,7 +318,7 @@ func TestMercury_Observe(t *testing.T) { t.Run("when chain length is zero", func(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return((*evmtypes.Head)(nil)) - ht.h = ht2 + ds.chainReader = evm.NewChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -345,7 +343,7 @@ func TestMercury_Observe(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return(h6) - ht.h = ht2 + ds.chainReader = evm.NewChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -384,7 +382,7 @@ func TestMercury_Observe(t *testing.T) { ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) ht2.On("LatestChain").Return(h6) - ht.h = ht2 + ds.chainReader = evm.NewChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) @@ -398,6 +396,18 @@ func TestMercury_Observe(t *testing.T) { ht2.AssertExpectations(t) }) + + t.Run("when chain reader returns an error", func(t *testing.T) { + + ds.chainReader = &mockChainReader{ + err: io.EOF, + obs: nil, + } + + obs, err := ds.Observe(ctx, repts, true) + assert.Error(t, err) + assert.Equal(t, obs, relaymercuryv1.Observation{}) + }) }) } @@ -418,39 +428,31 @@ func TestMercury_SetLatestBlocks(t *testing.T) { t.Run("returns head from headtracker if present", func(t *testing.T) { headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - chainHeadTracker := mercurymocks.NewChainHeadTracker(t) - - chainHeadTracker.On("HeadTracker").Return(headTracker) headTracker.On("LatestChain").Return(&h, nil) - - ds.chainHeadTracker = chainHeadTracker + ds.chainReader = evm.NewChainReader(headTracker) obs := relaymercuryv1.Observation{} - ds.setLatestBlocks(context.Background(), &obs) + err := ds.setLatestBlocks(context.Background(), &obs) + assert.NoError(t, err) assert.Equal(t, h.Number, obs.CurrentBlockNum.Val) assert.Equal(t, h.Hash.Bytes(), obs.CurrentBlockHash.Val) assert.Equal(t, uint64(h.Timestamp.Unix()), obs.CurrentBlockTimestamp.Val) assert.Len(t, obs.LatestBlocks, 1) - - chainHeadTracker.AssertExpectations(t) headTracker.AssertExpectations(t) }) t.Run("if headtracker returns nil head", func(t *testing.T) { headTracker := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - chainHeadTracker := mercurymocks.NewChainHeadTracker(t) - - chainHeadTracker.On("HeadTracker").Return(headTracker) // This can happen in some cases e.g. RPC node is offline headTracker.On("LatestChain").Return((*evmtypes.Head)(nil)) - ds.chainHeadTracker = chainHeadTracker - + ds.chainReader = evm.NewChainReader(headTracker) obs := relaymercuryv1.Observation{} - ds.setLatestBlocks(context.Background(), &obs) + err := ds.setLatestBlocks(context.Background(), &obs) + assert.NoError(t, err) assert.Zero(t, obs.CurrentBlockNum.Val) assert.Zero(t, obs.CurrentBlockHash.Val) assert.Zero(t, obs.CurrentBlockTimestamp.Val) @@ -459,8 +461,6 @@ func TestMercury_SetLatestBlocks(t *testing.T) { assert.EqualError(t, obs.CurrentBlockTimestamp.Err, "no blocks available") assert.Len(t, obs.LatestBlocks, 0) - - chainHeadTracker.AssertExpectations(t) headTracker.AssertExpectations(t) }) } diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index 914401c0897..bba5e699bc6 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -12,6 +12,7 @@ import ( relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" "github.com/smartcontractkit/chainlink-relay/pkg/services" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" @@ -25,6 +26,7 @@ type mercuryProvider struct { reportCodecV1 relaymercuryv1.ReportCodec reportCodecV2 relaymercuryv2.ReportCodec reportCodecV3 relaymercuryv3.ReportCodec + chainReader relaymercury.ChainReader logger logger.Logger ms services.MultiStart @@ -36,6 +38,7 @@ func NewMercuryProvider( reportCodecV1 relaymercuryv1.ReportCodec, reportCodecV2 relaymercuryv2.ReportCodec, reportCodecV3 relaymercuryv3.ReportCodec, + chainReader relaymercury.ChainReader, lggr logger.Logger, ) *mercuryProvider { return &mercuryProvider{ @@ -44,6 +47,7 @@ func NewMercuryProvider( reportCodecV1, reportCodecV2, reportCodecV3, + chainReader, lggr, services.MultiStart{}, } @@ -103,3 +107,37 @@ func (p *mercuryProvider) ContractTransmitter() ocrtypes.ContractTransmitter { func (p *mercuryProvider) MercuryServerFetcher() relaymercury.MercuryServerFetcher { return p.transmitter } + +func (p *mercuryProvider) ChainReader() relaymercury.ChainReader { + return p.chainReader +} + +var _ relaymercury.ChainReader = (*chainReader)(nil) + +type chainReader struct { + tracker httypes.HeadTracker +} + +func NewChainReader(h httypes.HeadTracker) relaymercury.ChainReader { + return &chainReader{ + tracker: h, + } +} + +func (r *chainReader) LatestHeads(ctx context.Context, k int) ([]relaymercury.Head, error) { + evmBlocks := r.tracker.LatestChain().AsSlice(k) + if len(evmBlocks) == 0 { + return nil, nil + } + + blocks := make([]relaymercury.Head, len(evmBlocks)) + for x := 0; x < len(evmBlocks); x++ { + blocks[x] = relaymercury.Head{ + Number: uint64(evmBlocks[x].BlockNumber()), + Hash: evmBlocks[x].Hash.Bytes(), + Timestamp: uint64(evmBlocks[x].Timestamp.Unix()), + } + } + + return blocks, nil +} diff --git a/go.mod b/go.mod index f35077e9234..8a4d58469c7 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/go.sum b/go.sum index 2c9d9e53715..1b96c936c5e 100644 --- a/go.sum +++ b/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 h1:ZBsxdB/5iIpl/tWhXe/RHrOwBG7pbKOMeppy5Zt2BVc= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 h1:28XkPE6YfJ4uabTX9/7sueRV6IKtY4hcm1nIt1e6b20= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 6a7e7195ff5..b5455838b58 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -387,7 +387,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 780e81c7ef1..fc486e54511 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2369,8 +2369,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78 h1:ZBsxdB/5iIpl/tWhXe/RHrOwBG7pbKOMeppy5Zt2BVc= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108205920-694ce17a4a78/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 h1:28XkPE6YfJ4uabTX9/7sueRV6IKtY4hcm1nIt1e6b20= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From 94625ef958b02c3eb759dd10d897b1d9bfc2fe5c Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:25:50 +0000 Subject: [PATCH 133/271] Add a csv flag to the verifiable load subcommand (#10833) --- .../command/keeper/verifiable_load.go | 12 +++++++- .../handler/keeper_verifiable_load.go | 30 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/core/scripts/chaincli/command/keeper/verifiable_load.go b/core/scripts/chaincli/command/keeper/verifiable_load.go index 7d77f0d3a37..33acf9bf3b2 100644 --- a/core/scripts/chaincli/command/keeper/verifiable_load.go +++ b/core/scripts/chaincli/command/keeper/verifiable_load.go @@ -1,6 +1,8 @@ package keeper import ( + "log" + "github.com/spf13/cobra" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" @@ -15,6 +17,14 @@ var verifiableLoad = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { cfg := config.New() hdlr := handler.NewKeeper(cfg) - hdlr.GetVerifiableLoadStats(cmd.Context()) + csv, err := cmd.Flags().GetBool("csv") + if err != nil { + log.Fatal("failed to get verify flag: ", err) + } + hdlr.GetVerifiableLoadStats(cmd.Context(), csv) }, } + +func init() { + verifiableLoad.Flags().BoolP("csv", "c", false, "Specify if stats should be output as CSV") +} diff --git a/core/scripts/chaincli/handler/keeper_verifiable_load.go b/core/scripts/chaincli/handler/keeper_verifiable_load.go index 429a7620079..b71a9af3387 100644 --- a/core/scripts/chaincli/handler/keeper_verifiable_load.go +++ b/core/scripts/chaincli/handler/keeper_verifiable_load.go @@ -2,6 +2,7 @@ package handler import ( "context" + "fmt" "log" "math/big" "sort" @@ -57,7 +58,7 @@ type upkeepStats struct { SortedAllDelays []float64 } -func (k *Keeper) GetVerifiableLoadStats(ctx context.Context) { +func (k *Keeper) GetVerifiableLoadStats(ctx context.Context, csv bool) { var v verifiableLoad var err error addr := common.HexToAddress(k.cfg.VerifiableLoadContractAddress) @@ -84,6 +85,10 @@ func (k *Keeper) GetVerifiableLoadStats(ctx context.Context) { log.Fatalf("failed to get active upkeep IDs from %s: %v", k.cfg.VerifiableLoadContractAddress, err) } + if csv { + fmt.Println("upkeep ID,total performs,p50,p90,p95,p99,max delay,total delay blocks,average perform delay") + } + us := &upkeepStats{BlockNumber: blockNum} resultsChan := make(chan *upkeepInfo, maxUpkeepNum) @@ -94,7 +99,7 @@ func (k *Keeper) GetVerifiableLoadStats(ctx context.Context) { // create a number of workers to process the upkeep ids in batch for i := 0; i < workerNum; i++ { wg.Add(1) - go k.getUpkeepInfo(idChan, resultsChan, v, opts, &wg) + go k.getUpkeepInfo(idChan, resultsChan, v, opts, &wg, csv) } for _, id := range upkeepIds { @@ -120,12 +125,16 @@ func (k *Keeper) GetVerifiableLoadStats(ctx context.Context) { p90, _ := stats.Percentile(us.SortedAllDelays, 90) p95, _ := stats.Percentile(us.SortedAllDelays, 95) p99, _ := stats.Percentile(us.SortedAllDelays, 99) - maxDelay := us.SortedAllDelays[len(us.SortedAllDelays)-1] + + maxDelay := float64(0) + if len(us.SortedAllDelays) > 0 { + maxDelay = us.SortedAllDelays[len(us.SortedAllDelays)-1] + } log.Printf("For total %d upkeeps: total performs: %d, p50: %f, p90: %f, p95: %f, p99: %f, max delay: %f, total delay blocks: %f, average perform delay: %f\n", len(upkeepIds), us.TotalPerforms, p50, p90, p95, p99, maxDelay, us.TotalDelayBlock, us.TotalDelayBlock/float64(us.TotalPerforms)) log.Printf("All STATS ABOVE ARE CALCULATED AT BLOCK %d", blockNum) } -func (k *Keeper) getUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInfo, v verifiableLoad, opts *bind.CallOpts, wg *sync.WaitGroup) { +func (k *Keeper) getUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInfo, v verifiableLoad, opts *bind.CallOpts, wg *sync.WaitGroup, csv bool) { defer wg.Done() for id := range idChan { @@ -171,9 +180,18 @@ func (k *Keeper) getUpkeepInfo(idChan chan *big.Int, resultsChan chan *upkeepInf p90, _ := stats.Percentile(info.SortedAllDelays, 90) p95, _ := stats.Percentile(info.SortedAllDelays, 95) p99, _ := stats.Percentile(info.SortedAllDelays, 99) - maxDelay := info.SortedAllDelays[len(info.SortedAllDelays)-1] - log.Printf("upkeep ID %s has %d performs in total. p50: %f, p90: %f, p95: %f, p99: %f, max delay: %f, total delay blocks: %d, average perform delay: %f\n", id, info.TotalPerforms, p50, p90, p95, p99, maxDelay, uint64(info.TotalDelayBlock), info.TotalDelayBlock/float64(info.TotalPerforms)) + maxDelay := float64(0) + + if len(info.SortedAllDelays) > 0 { + maxDelay = info.SortedAllDelays[len(info.SortedAllDelays)-1] + } + + if csv { + fmt.Printf("%s,%d,%f,%f,%f,%f,%f,%d,%f\n", id, info.TotalPerforms, p50, p90, p95, p99, maxDelay, uint64(info.TotalDelayBlock), info.TotalDelayBlock/float64(info.TotalPerforms)) + } else { + log.Printf("upkeep ID %s has %d performs in total. p50: %f, p90: %f, p95: %f, p99: %f, max delay: %f, total delay blocks: %d, average perform delay: %f\n", id, info.TotalPerforms, p50, p90, p95, p99, maxDelay, uint64(info.TotalDelayBlock), info.TotalDelayBlock/float64(info.TotalPerforms)) + } resultsChan <- info } } From 0ba7e9a7cd402ed558835a39d0ced4e7c2b39732 Mon Sep 17 00:00:00 2001 From: george-dorin <120329946+george-dorin@users.noreply.github.com> Date: Mon, 13 Nov 2023 17:37:45 +0200 Subject: [PATCH 134/271] EVM GRPC provider (#11208) * Initial draft * Add median provider checks * Move provider-server to relay * Bump chainlink-relay version * Add tests * Add comment explaining why the code lives inside the newServicesGenericPlugin method * Fix formatting * Fix lint --- core/services/ocr2/delegate.go | 23 ++++++- core/services/relay/grpc_provider_server.go | 68 +++++++++++++++++++ .../relay/grpc_provider_server_test.go | 27 ++++++++ core/services/relay/relay_test.go | 26 +++++++ 4 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 core/services/relay/grpc_provider_server.go create mode 100644 core/services/relay/grpc_provider_server_test.go diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 19296c72f00..9905ed6ae6c 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -590,9 +590,26 @@ func (d *Delegate) newServicesGenericPlugin( } errorLog := &errorLog{jobID: jb.ID, recordError: d.jobORM.RecordError} + var providerClientConn grpc.ClientConnInterface providerConn, ok := provider.(connProvider) - if !ok { - return nil, errors.New("provider not supported: the provider is not a LOOPP provider") + if ok { + providerClientConn = providerConn.ClientConn() + } else { + //We chose to deal with the difference between a LOOP provider and an embedded provider here rather than + //in NewServerAdapter because this has a smaller blast radius, as the scope of this workaround is to + //enable the medianpoc for EVM and not touch the other providers. + //TODO: remove this workaround when the EVM relayer is running inside of an LOOPP + d.lggr.Info("provider is not a LOOPP provider, switching to provider server") + + ps, err2 := relay.NewProviderServer(provider, types.OCR2PluginType(cconf.ProviderType), d.lggr) + if err2 != nil { + return nil, fmt.Errorf("cannot start EVM provider server: %s", err) + } + providerClientConn, err2 = ps.GetConn() + if err2 != nil { + return nil, fmt.Errorf("cannot connect to EVM provider server: %s", err) + } + srvs = append(srvs, ps) } pluginConfig := types.ReportingPluginServiceConfig{ @@ -606,7 +623,7 @@ func (d *Delegate) newServicesGenericPlugin( pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner) ta := generic.NewTelemetryAdapter(d.monitoringEndpointGen) - plugin := reportingplugins.NewLOOPPService(pluginLggr, grpcOpts, cmdFn, pluginConfig, providerConn.ClientConn(), pr, ta, errorLog) + plugin := reportingplugins.NewLOOPPService(pluginLggr, grpcOpts, cmdFn, pluginConfig, providerClientConn, pr, ta, errorLog) oracleArgs.ReportingPluginFactory = plugin srvs = append(srvs, plugin) diff --git a/core/services/relay/grpc_provider_server.go b/core/services/relay/grpc_provider_server.go new file mode 100644 index 00000000000..943af0e6362 --- /dev/null +++ b/core/services/relay/grpc_provider_server.go @@ -0,0 +1,68 @@ +package relay + +import ( + "context" + "net" + + "go.uber.org/multierr" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +type ProviderServer struct { + s *grpc.Server + lis net.Listener + lggr logger.Logger + conns []*grpc.ClientConn +} + +func (p *ProviderServer) Start(ctx context.Context) error { + p.serve() + return nil +} + +func (p *ProviderServer) Close() error { + var err error + for _, c := range p.conns { + err = multierr.Combine(err, c.Close()) + } + p.s.Stop() + return err +} + +func (p *ProviderServer) GetConn() (*grpc.ClientConn, error) { + cc, err := grpc.Dial(p.lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) + p.conns = append(p.conns, cc) + return cc, err +} + +// NewProviderServer creates a GRPC server that will wrap a provider, this is a workaround to test the Node API PoC until the EVM relayer is loopifyed +func NewProviderServer(p types.PluginProvider, pType types.OCR2PluginType, lggr logger.Logger) (*ProviderServer, error) { + lis, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + ps := ProviderServer{ + s: grpc.NewServer(), + lis: lis, + lggr: lggr.Named("EVM.ProviderServer"), + } + err = loop.RegisterStandAloneProvider(ps.s, p, pType) + if err != nil { + return nil, err + } + + return &ps, nil +} + +func (p *ProviderServer) serve() { + go func() { + if err := p.s.Serve(p.lis); err != nil { + p.lggr.Errorf("Failed to serve EVM provider server: %v", err) + } + }() +} diff --git a/core/services/relay/grpc_provider_server_test.go b/core/services/relay/grpc_provider_server_test.go new file mode 100644 index 00000000000..e7ee8d7f150 --- /dev/null +++ b/core/services/relay/grpc_provider_server_test.go @@ -0,0 +1,27 @@ +package relay + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +func TestProviderServer(t *testing.T) { + r := &mockRelayer{} + sa := NewServerAdapter(r, mockRelayerExt{}) + mp, _ := sa.NewPluginProvider(context.Background(), types.RelayArgs{ProviderType: string(types.Median)}, types.PluginArgs{}) + + lggr := logger.TestLogger(t) + _, err := NewProviderServer(mp, "unsupported-type", lggr) + require.Error(t, err) + + ps, err := NewProviderServer(staticMedianProvider{}, types.Median, lggr) + require.NoError(t, err) + + _, err = ps.GetConn() + require.NoError(t, err) +} diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index d3a94773498..5bcd14c64a0 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/smartcontractkit/chainlink-relay/pkg/loop" @@ -61,6 +63,30 @@ type staticMedianProvider struct { types.MedianProvider } +func (s staticMedianProvider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester { + return nil +} + +func (s staticMedianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { + return nil +} + +func (s staticMedianProvider) ContractTransmitter() ocrtypes.ContractTransmitter { + return nil +} + +func (s staticMedianProvider) ReportCodec() median.ReportCodec { + return nil +} + +func (s staticMedianProvider) MedianContract() median.MedianContract { + return nil +} + +func (s staticMedianProvider) OnchainConfigCodec() median.OnchainConfigCodec { + return nil +} + type staticFunctionsProvider struct { types.FunctionsProvider } From b66b7233ed5b9f432e621b62c213daf45f365574 Mon Sep 17 00:00:00 2001 From: Tate Date: Mon, 13 Nov 2023 09:06:03 -0700 Subject: [PATCH 135/271] [TT-500][TT-682]Add build and lint to integration-tests (#11221) * Add build and lint to integration-tests * Fix the build to actually build all the test files * Add needs for the new job so it will affect required steps * Review comments * fix build and lint errors * bump lint and go versions * lint weirdness in migration test * Wire the test context in the VRFV2SoakTest Run function to a parent context tied to the test. * Merge conflict fix and improved .golangci lint file with fixes for those * One more .golangci cleanup * Fix newly added lint errors * add build to integration-tests makefile --- .github/actions/golangci-lint/action.yml | 2 +- .github/workflows/integration-tests.yml | 72 ++++-- .../on-demand-vrfv2plus-performance-test.yml | 2 +- .tool-versions | 4 +- GNUmakefile | 2 +- integration-tests/.golangci.yml | 78 +++++++ integration-tests/.tool-versions | 3 +- integration-tests/Makefile | 6 + integration-tests/actions/actions.go | 1 - integration-tests/actions/actions_local.go | 2 +- .../actions/automation_ocr_helpers.go | 5 +- integration-tests/actions/ocr2_helpers.go | 7 +- .../actions/ocr2_helpers_local.go | 11 +- .../ocr2vrf_actions/ocr2vrf_config_helpers.go | 7 +- .../actions/ocr2vrf_actions/ocr2vrf_steps.go | 8 +- integration-tests/actions/ocr_helpers.go | 1 - .../actions/ocr_helpers_local.go | 3 +- .../actions/operator_forwarder_helpers.go | 18 +- .../actions/vrfv2plus/vrfv2plus_steps.go | 5 +- .../chaos/automation_chaos_test.go | 15 +- integration-tests/chaos/ocr2vrf_chaos_test.go | 7 +- integration-tests/chaos/ocr_chaos_test.go | 8 +- integration-tests/client/chainlink_k8s.go | 2 +- .../contracts/contract_deployer.go | 32 +-- .../contracts/contract_loader.go | 1 + .../contracts/contract_vrf_models.go | 13 +- .../contracts/ethereum_contracts.go | 15 +- .../contracts/ethereum_keeper_contracts.go | 95 ++++---- integration-tests/contracts/test_contracts.go | 1 + integration-tests/docker/cmd/test_env.go | 7 +- integration-tests/docker/test_env/cl_node.go | 102 +-------- integration-tests/docker/test_env/test_env.go | 5 +- .../docker/test_env/test_env_builder.go | 4 +- integration-tests/load/functions/config.go | 4 +- .../load/functions/functions_test.go | 5 +- integration-tests/load/functions/gateway.go | 5 +- .../load/functions/gateway_gun.go | 9 +- .../load/functions/onchain_monitoring.go | 5 +- .../load/functions/request_gun.go | 19 +- integration-tests/load/functions/setup.go | 3 +- .../load/log_poller/log_poller_test.go | 3 +- integration-tests/load/vrfv2/cmd/dashboard.go | 3 +- integration-tests/load/vrfv2/config.go | 1 + integration-tests/load/vrfv2/gun.go | 5 +- .../load/vrfv2/onchain_monitoring.go | 12 +- integration-tests/load/vrfv2/vrfv2_test.go | 3 +- integration-tests/load/vrfv2/vu.go | 3 +- .../load/vrfv2plus/cmd/dashboard.go | 3 +- integration-tests/load/vrfv2plus/config.go | 4 + integration-tests/load/vrfv2plus/gun.go | 10 +- .../load/vrfv2plus/onchain_monitoring.go | 8 +- .../load/vrfv2plus/vrfv2plus_test.go | 22 +- .../migration/upgrade_version_test.go | 5 +- integration-tests/performance/cron_test.go | 3 +- .../performance/directrequest_test.go | 4 +- integration-tests/performance/flux_test.go | 10 +- integration-tests/performance/keeper_test.go | 10 +- integration-tests/performance/ocr_test.go | 8 +- integration-tests/performance/vrf_test.go | 6 +- .../reorg/automation_reorg_test.go | 16 +- .../reorg/log_poller_maybe_reorg_test.go | 1 + integration-tests/reorg/reorg_test.go | 8 +- integration-tests/runner_helpers.go | 4 +- integration-tests/smoke/automation_test.go | 120 +++++----- integration-tests/smoke/flux_test.go | 10 +- integration-tests/smoke/forwarder_ocr_test.go | 6 +- .../smoke/forwarders_ocr2_test.go | 6 +- integration-tests/smoke/keeper_test.go | 92 ++++---- integration-tests/smoke/log_poller_test.go | 1 + integration-tests/smoke/ocr2_test.go | 6 +- integration-tests/smoke/ocr2vrf_test.go | 11 +- integration-tests/smoke/ocr_test.go | 6 +- integration-tests/smoke/runlog_test.go | 4 +- integration-tests/smoke/vrf_test.go | 6 +- integration-tests/smoke/vrfv2_test.go | 6 +- integration-tests/smoke/vrfv2plus_test.go | 89 ++++---- .../testreporters/keeper_benchmark.go | 4 +- integration-tests/testreporters/ocr.go | 4 +- integration-tests/testreporters/profile.go | 2 +- integration-tests/testreporters/vrfv2plus.go | 5 +- .../testsetups/keeper_benchmark.go | 10 +- integration-tests/testsetups/ocr.go | 17 +- integration-tests/testsetups/vrfv2.go | 6 +- integration-tests/types/envcommon/common.go | 4 +- .../universal/log_poller/config.go | 27 +-- integration-tests/universal/log_poller/gun.go | 7 +- .../universal/log_poller/helpers.go | 212 ++++++++---------- .../universal/log_poller/scenarios.go | 26 +-- integration-tests/utils/cl_node_jobs.go | 5 +- integration-tests/utils/common.go | 19 ++ integration-tests/utils/log.go | 19 -- integration-tests/utils/templates/secrets.go | 1 + 92 files changed, 759 insertions(+), 718 deletions(-) create mode 100644 integration-tests/.golangci.yml delete mode 100644 integration-tests/utils/log.go diff --git a/.github/actions/golangci-lint/action.yml b/.github/actions/golangci-lint/action.yml index 97755fa46ea..055960ff282 100644 --- a/.github/actions/golangci-lint/action.yml +++ b/.github/actions/golangci-lint/action.yml @@ -53,7 +53,7 @@ runs: - name: golangci-lint uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 with: - version: v1.55.0 + version: v1.55.2 # We already cache these directories in setup-go skip-pkg-cache: true skip-build-cache: true diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index ba66a53ab62..9294dceae6d 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -64,6 +64,36 @@ jobs: outputs: src: ${{ steps.changes.outputs.src }} + build-lint-integration-tests: + name: Build and Lint integration-tests + runs-on: ubuntu20.04-16cores-64GB + steps: + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Setup Go + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + with: + test_download_vendor_packages_command: cd ./integration-tests && go mod download + go_mod_path: ./integration-tests/go.mod + cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + cache_restore_only: "true" + - name: Build Go + run: | + cd ./integration-tests + go build ./... + SELECTED_NETWORKS=SIMULATED go test -run=^# ./... + - name: Lint Go + uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 + with: + version: v1.55.2 + # We already cache these directories in setup-go + skip-pkg-cache: true + skip-build-cache: true + # only-new-issues is only applicable to PRs, otherwise it is always set to false + only-new-issues: false # disabled for PRs due to unreliability + args: --out-format colored-line-number,checkstyle:golangci-lint-report.xml + working-directory: ./integration-tests + build-chainlink: environment: integration permissions: @@ -174,7 +204,7 @@ jobs: pull-requests: write id-token: write contents: read - needs: [build-chainlink, changes, compare-tests] + needs: [build-chainlink, changes, compare-tests, build-lint-integration-tests] env: SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 CHAINLINK_COMMIT_SHA: ${{ github.sha }} @@ -242,7 +272,7 @@ jobs: pull-requests: write id-token: write contents: read - needs: [build-chainlink, changes] + needs: [build-chainlink, changes, build-lint-integration-tests] env: SELECTED_NETWORKS: SIMULATED,SIMULATED_1,SIMULATED_2 CHAINLINK_COMMIT_SHA: ${{ github.sha }} @@ -356,16 +386,16 @@ jobs: run: | PORT_BASE=3001 MAX_PORT=8000 - + # Use PR number as offset. Given GitHub PRs are incremental, this guarantees uniqueness for at least 5000 PRs. OFFSET=$GITHUB_PR_NUMBER echo "PR Number: $OFFSET" - + # Ensure that we don't exceed the max port if (( OFFSET > (MAX_PORT - PORT_BASE) )); then OFFSET=$((OFFSET % (MAX_PORT - PORT_BASE))) fi - + # Map the offset to the port range REMOTE_PORT=$((PORT_BASE + OFFSET)) echo "REMOTE_PORT=$REMOTE_PORT" >> $GITHUB_OUTPUT @@ -376,25 +406,25 @@ jobs: TRACING_SSH_SERVER: ${{ secrets.TRACING_SSH_SERVER }} REMOTE_PORT: ${{ steps.generate-port.outputs.REMOTE_PORT }} run: | - eval $(ssh-agent) - echo "test" - echo "$TRACING_SSH_KEY" | wc -c - echo "$TRACING_SSH_KEY" | tr -d '\r' | wc -c - echo "$TRACING_SSH_KEY" | tr -d '\r' | base64 --decode | ssh-add - - # f: background process - # N: do not execute a remote command - # R: remote port forwarding - ssh -o StrictHostKeyChecking=no -f -N -R $REMOTE_PORT:127.0.0.1:3000 user-gha@$TRACING_SSH_SERVER - echo "To view Grafana locally:" - echo "ssh -N -L 8000:localhost:$REMOTE_PORT user-gha@$TRACING_SSH_SERVER" - echo "Then visit http://localhost:8000 in a browser." - echo "If you are unable to connect, check with the security team that you have access to the tracing server." + eval $(ssh-agent) + echo "test" + echo "$TRACING_SSH_KEY" | wc -c + echo "$TRACING_SSH_KEY" | tr -d '\r' | wc -c + echo "$TRACING_SSH_KEY" | tr -d '\r' | base64 --decode | ssh-add - + # f: background process + # N: do not execute a remote command + # R: remote port forwarding + ssh -o StrictHostKeyChecking=no -f -N -R $REMOTE_PORT:127.0.0.1:3000 user-gha@$TRACING_SSH_SERVER + echo "To view Grafana locally:" + echo "ssh -N -L 8000:localhost:$REMOTE_PORT user-gha@$TRACING_SSH_SERVER" + echo "Then visit http://localhost:8000 in a browser." + echo "If you are unable to connect, check with the security team that you have access to the tracing server." - name: Show Grafana Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | - docker logs grafana - docker logs tempo - docker logs otel-collector + docker logs grafana + docker logs tempo + docker logs otel-collector - name: Set sleep time to use in future steps if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index c51f7f5a2fb..b4f9f46de02 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -54,7 +54,7 @@ on: useExistingEnv: description: Set `true` to use existing environment or `false` to deploy CL node and all contracts required: false - default: false + default: "false" configBase64: description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) required: false diff --git a/.tool-versions b/.tool-versions index 87910cf6d6f..c60396ccb86 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,7 +1,7 @@ -golang 1.21.1 +golang 1.21.4 mockery 2.35.4 nodejs 16.16.0 postgres 13.3 helm 3.10.3 zig 0.10.1 -golangci-lint 1.55.0 +golangci-lint 1.55.2 diff --git a/GNUmakefile b/GNUmakefile index 69d82da6c84..2801f949682 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -138,7 +138,7 @@ config-docs: ## Generate core node configuration documentation .PHONY: golangci-lint golangci-lint: ## Run golangci-lint for all issues. [ -d "./golangci-lint" ] || mkdir ./golangci-lint && \ - docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.55.0 golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 > ./golangci-lint/$(shell date +%Y-%m-%d_%H:%M:%S).txt + docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v1.55.2 golangci-lint run --max-issues-per-linter 0 --max-same-issues 0 > ./golangci-lint/$(shell date +%Y-%m-%d_%H:%M:%S).txt GORELEASER_CONFIG ?= .goreleaser.yaml diff --git a/integration-tests/.golangci.yml b/integration-tests/.golangci.yml new file mode 100644 index 00000000000..d22b26b8260 --- /dev/null +++ b/integration-tests/.golangci.yml @@ -0,0 +1,78 @@ +run: + timeout: 15m +linters: + enable: + - exhaustive + - exportloopref + - revive + - goimports + - gosec + - misspell + - rowserrcheck + - errorlint +linters-settings: + exhaustive: + default-signifies-exhaustive: true + goimports: + local-prefixes: github.com/smartcontractkit/chainlink + golint: + min-confidence: 0.999 + gosec: + excludes: + - G101 + govet: + # report about shadowed variables + check-shadowing: true + revive: + confidence: 0.8 + rules: + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + - name: if-return + - name: increment-decrement + # - name: var-naming // doesn't work with some generated names + - name: var-declaration + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unused-parameter + - name: unreachable-code + - name: redefines-builtin-id + - name: waitgroup-by-value + - name: unconditional-recursion + - name: struct-tag + - name: string-format + - name: string-of-int + - name: range-val-address + - name: range-val-in-closure + - name: modifies-value-receiver + - name: modifies-parameter + - name: identical-branches + - name: get-return + # - name: flag-parameter // probably one we should work on doing better at in the future + # - name: early-return // probably one we should work on doing better at in the future + - name: defer + - name: constant-logical-expr + - name: confusing-naming + - name: confusing-results + - name: bool-literal-in-expr + - name: atomic +issues: + exclude-rules: + - text: "^G404: Use of weak random number generator" + linters: + - gosec + - linters: + - govet + text: "declaration of \"err\" shadows" diff --git a/integration-tests/.tool-versions b/integration-tests/.tool-versions index 68b6d994197..47b73e9de11 100644 --- a/integration-tests/.tool-versions +++ b/integration-tests/.tool-versions @@ -1,4 +1,5 @@ -golang 1.21.1 +golang 1.21.4 k3d 5.4.6 kubectl 1.25.5 nodejs 18.13.0 +golangci-lint 1.55.2 diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 257331afcfd..fb4bfa74f3e 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -56,6 +56,12 @@ install_gotestfmt: go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest set -euo pipefail +lint: + golangci-lint --color=always run ./... --fix -v + +build: + @go build ./... && SELECTED_NETWORKS=SIMULATED go test -run=^# ./... + # Builds the test image # tag: the tag for the test image being built, example: tag=tate # base_tag: the tag for the base-test-image to use, example: base_tag=latest diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index bacf5a9dbfa..02a25234774 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -254,7 +254,6 @@ func GetMockserverInitializerDataForOTPE( func TeardownSuite( t *testing.T, env *environment.Environment, - logsFolderPath string, chainlinkNodes []*client.ChainlinkK8sClient, optionalTestReporter testreporters.TestReporter, // Optionally pass in a test reporter to log further metrics failingLogLevel zapcore.Level, // Examines logs after the test, and fails the test if any Chainlink logs are found at or above provided level diff --git a/integration-tests/actions/actions_local.go b/integration-tests/actions/actions_local.go index f5d2a9035f5..d4913cabd8a 100644 --- a/integration-tests/actions/actions_local.go +++ b/integration-tests/actions/actions_local.go @@ -17,7 +17,7 @@ func UpgradeChainlinkNodeVersionsLocal( return fmt.Errorf("unable to upgrade node version, found empty image and version, must provide either a new image or a new version") } for _, node := range nodes { - if err := node.UpgradeVersion(node.NodeConfig, newImage, newVersion); err != nil { + if err := node.UpgradeVersion(newImage, newVersion); err != nil { return err } } diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index 998b1ee89cf..e1635902db5 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -14,14 +14,15 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/logging" ocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocr2keepers20config "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/store/models" diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index aead74f2bdd..02ce73e813e 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -15,14 +15,15 @@ import ( "golang.org/x/sync/errgroup" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" - "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" diff --git a/integration-tests/actions/ocr2_helpers_local.go b/integration-tests/actions/ocr2_helpers_local.go index b3fe6eb041f..65e0a466bee 100644 --- a/integration-tests/actions/ocr2_helpers_local.go +++ b/integration-tests/actions/ocr2_helpers_local.go @@ -12,6 +12,12 @@ import ( "github.com/google/uuid" "github.com/lib/pq" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "golang.org/x/sync/errgroup" + "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -19,11 +25,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/testhelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" - "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "golang.org/x/sync/errgroup" - "gopkg.in/guregu/null.v4" ) func CreateOCRv2JobsLocal( diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go index ce693964323..e424aaa11b3 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go @@ -16,9 +16,6 @@ import ( "go.dedis.ch/kyber/v3/group/edwards25519" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/ocr2vrf/altbn_128" @@ -26,6 +23,10 @@ import ( "github.com/smartcontractkit/ocr2vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go index c123aaff6a2..72d668076e9 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func SetAndWaitForVRFBeaconProcessToFinish(t *testing.T, ocr2VRFPluginConfig *OCR2VRFPluginConfig, vrfBeacon contracts.VRFBeacon) { @@ -172,7 +173,7 @@ func FundVRFCoordinatorV3Subscription(t *testing.T, linkToken contracts.LinkToke require.NoError(t, err, "Error waiting for TXs to complete") } -func DeployOCR2VRFContracts(t *testing.T, contractDeployer contracts.ContractDeployer, chainClient blockchain.EVMClient, linkToken contracts.LinkToken, mockETHLinkFeed contracts.MockETHLINKFeed, beaconPeriodBlocksCount *big.Int, keyID string) (contracts.DKG, contracts.VRFCoordinatorV3, contracts.VRFBeacon, contracts.VRFBeaconConsumer) { +func DeployOCR2VRFContracts(t *testing.T, contractDeployer contracts.ContractDeployer, chainClient blockchain.EVMClient, linkToken contracts.LinkToken, beaconPeriodBlocksCount *big.Int, keyID string) (contracts.DKG, contracts.VRFCoordinatorV3, contracts.VRFBeacon, contracts.VRFBeaconConsumer) { dkg, err := contractDeployer.DeployDKG() require.NoError(t, err, "Error deploying DKG Contract") @@ -272,14 +273,14 @@ func RequestRandomnessFulfillmentAndWaitForFulfilment( } func getRequestId(t *testing.T, consumer contracts.VRFBeaconConsumer, receipt *types.Receipt, confirmationDelay *big.Int) *big.Int { - periodBlocks, err := consumer.IBeaconPeriodBlocks(nil) + periodBlocks, err := consumer.IBeaconPeriodBlocks(utils.TestContext(t)) require.NoError(t, err, "Error getting Beacon Period block count") blockNumber := receipt.BlockNumber periodOffset := new(big.Int).Mod(blockNumber, periodBlocks) nextBeaconOutputHeight := new(big.Int).Sub(new(big.Int).Add(blockNumber, periodBlocks), periodOffset) - requestID, err := consumer.GetRequestIdsBy(nil, nextBeaconOutputHeight, confirmationDelay) + requestID, err := consumer.GetRequestIdsBy(utils.TestContext(t), nextBeaconOutputHeight, confirmationDelay) require.NoError(t, err, "Error getting requestID from consumer contract") return requestID @@ -305,7 +306,6 @@ func SetupOCR2VRFUniverse( contractDeployer, chainClient, linkToken, - mockETHLinkFeed, ocr2vrf_constants.BeaconPeriodBlocksCount, ocr2vrf_constants.KeyID, ) diff --git a/integration-tests/actions/ocr_helpers.go b/integration-tests/actions/ocr_helpers.go index cfc8cfe589b..4f713dcdd6d 100644 --- a/integration-tests/actions/ocr_helpers.go +++ b/integration-tests/actions/ocr_helpers.go @@ -27,7 +27,6 @@ func DeployOCRContracts( numberOfContracts int, linkTokenContract contracts.LinkToken, contractDeployer contracts.ContractDeployer, - bootstrapNode *client.ChainlinkK8sClient, workerNodes []*client.ChainlinkK8sClient, client blockchain.EVMClient, ) ([]contracts.OffchainAggregator, error) { diff --git a/integration-tests/actions/ocr_helpers_local.go b/integration-tests/actions/ocr_helpers_local.go index 5836ee7945c..e6dd5ae77f6 100644 --- a/integration-tests/actions/ocr_helpers_local.go +++ b/integration-tests/actions/ocr_helpers_local.go @@ -10,9 +10,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/rs/zerolog" + "golang.org/x/sync/errgroup" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" - "golang.org/x/sync/errgroup" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" diff --git a/integration-tests/actions/operator_forwarder_helpers.go b/integration-tests/actions/operator_forwarder_helpers.go index 37b50c4fa9a..a1d7135416c 100644 --- a/integration-tests/actions/operator_forwarder_helpers.go +++ b/integration-tests/actions/operator_forwarder_helpers.go @@ -1,7 +1,6 @@ package actions import ( - "context" "math/big" "testing" @@ -17,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func DeployForwarderContracts( @@ -67,7 +67,7 @@ func AcceptAuthorizedReceiversOperator( err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for events in nodes shouldn't fail") - senders, err := forwarderInstance.GetAuthorizedSenders(context.Background()) + senders, err := forwarderInstance.GetAuthorizedSenders(utils.TestContext(t)) require.NoError(t, err, "Getting authorized senders shouldn't fail") var nodesAddrs []string for _, o := range nodeAddresses { @@ -75,20 +75,18 @@ func AcceptAuthorizedReceiversOperator( } require.Equal(t, nodesAddrs, senders, "Senders addresses should match node addresses") - owner, err := forwarderInstance.Owner(context.Background()) + owner, err := forwarderInstance.Owner(utils.TestContext(t)) require.NoError(t, err, "Getting authorized forwarder owner shouldn't fail") require.Equal(t, operator.Hex(), owner, "Forwarder owner should match operator") } func ProcessNewEvent( t *testing.T, - eventSub geth.Subscription, operatorCreated chan *operator_factory.OperatorFactoryOperatorCreated, authorizedForwarderCreated chan *operator_factory.OperatorFactoryAuthorizedForwarderCreated, event *types.Log, eventDetails *abi.Event, operatorFactoryInstance contracts.OperatorFactory, - contractABI *abi.ABI, chainClient blockchain.EVMClient, ) { l := logging.GetTestLogger(t) @@ -141,7 +139,7 @@ func SubscribeOperatorFactoryEvents( l := logging.GetTestLogger(t) contractABI, err := operator_factory.OperatorFactoryMetaData.GetAbi() require.NoError(t, err, "Getting contract abi for OperatorFactory shouldn't fail") - latestBlockNum, err := chainClient.LatestBlockNumber(context.Background()) + latestBlockNum, err := chainClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Subscribing to contract event log for OperatorFactory instance shouldn't fail") query := geth.FilterQuery{ FromBlock: big.NewInt(0).SetUint64(latestBlockNum), @@ -149,7 +147,7 @@ func SubscribeOperatorFactoryEvents( } eventLogs := make(chan types.Log) - sub, err := chainClient.SubscribeFilterLogs(context.Background(), query, eventLogs) + sub, err := chainClient.SubscribeFilterLogs(utils.TestContext(t), query, eventLogs) require.NoError(t, err, "Subscribing to contract event log for OperatorFactory instance shouldn't fail") go func() { defer sub.Unsubscribe() @@ -160,14 +158,14 @@ func SubscribeOperatorFactoryEvents( l.Error().Err(err).Msg("Error while watching for new contract events. Retrying Subscription") sub.Unsubscribe() - sub, err = chainClient.SubscribeFilterLogs(context.Background(), query, eventLogs) + sub, err = chainClient.SubscribeFilterLogs(utils.TestContext(t), query, eventLogs) require.NoError(t, err, "Subscribing to contract event log for OperatorFactory instance shouldn't fail") case vLog := <-eventLogs: eventDetails, err := contractABI.EventByID(vLog.Topics[0]) require.NoError(t, err, "Getting event details for OperatorFactory instance shouldn't fail") go ProcessNewEvent( - t, sub, operatorCreated, authorizedForwarderCreated, &vLog, - eventDetails, operatorFactoryInstance, contractABI, chainClient, + t, operatorCreated, authorizedForwarderCreated, &vLog, + eventDetails, operatorFactoryInstance, chainClient, ) if eventDetails.Name == "AuthorizedForwarderCreated" || eventDetails.Name == "OperatorCreated" { remainingExpectedEvents-- diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index e964623fb2e..28fb2635ff3 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/rs/zerolog" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" @@ -802,7 +803,7 @@ func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadT fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) case <-ticker.C: - go getLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) + go retreiveLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) case metrics = <-metricsChannel: if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { ticker.Stop() @@ -852,7 +853,7 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co return nil } -func getLoadTestMetrics( +func retreiveLoadTestMetrics( consumer contracts.VRFv2PlusLoadTestConsumer, metricsChannel chan *contracts.VRFLoadTestMetrics, metricsErrorChannel chan error, diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 22c9e742f38..6ebf14d806e 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -1,7 +1,6 @@ package chaos import ( - "context" "fmt" "math/big" "testing" @@ -25,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -116,6 +116,7 @@ func TestAutomationChaos(t *testing.T) { } for name, registryVersion := range registryVersions { + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() @@ -176,9 +177,9 @@ func TestAutomationChaos(t *testing.T) { }, } - for n, tst := range testCases { - name := n - testCase := tst + for name, testCase := range testCases { + name := name + testCase := testCase t.Run(fmt.Sprintf("Automation_%s", name), func(t *testing.T) { t.Parallel() network := networks.MustGetSelectedNetworksFromEnv()[0] // Need a new copy of the network for each test @@ -223,7 +224,7 @@ func TestAutomationChaos(t *testing.T) { if chainClient != nil { chainClient.GasStats().PrintStats() } - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -268,7 +269,7 @@ func TestAutomationChaos(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(it_utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") @@ -283,7 +284,7 @@ func TestAutomationChaos(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(it_utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 10 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index ba75974f01a..8739a5960af 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCR2VRFChaos(t *testing.T) { @@ -149,7 +150,7 @@ func TestOCR2VRFChaos(t *testing.T) { require.NoError(t, err, "Retrieving on-chain wallet addresses for chainlink nodes shouldn't fail") t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -185,7 +186,7 @@ func TestOCR2VRFChaos(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(nil, requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) require.NoError(t, err) l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") @@ -212,7 +213,7 @@ func TestOCR2VRFChaos(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(nil, requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) require.NoError(t, err, "Error getting Randomness result from Consumer Contract") l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 599fad8ddc5..76e25d92000 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -1,7 +1,6 @@ package chaos import ( - "context" "fmt" "math/big" "os" @@ -27,6 +26,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -164,7 +164,7 @@ func TestOCRChaos(t *testing.T) { if chainClient != nil { chainClient.GasStats().PrintStats() } - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -180,7 +180,7 @@ func TestOCRChaos(t *testing.T) { err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(10)) require.NoError(t, err) - ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, bootstrapNode, workerNodes, chainClient) + ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, workerNodes, chainClient) require.NoError(t, err) err = chainClient.WaitForEvents() require.NoError(t, err) @@ -195,7 +195,7 @@ func TestOCRChaos(t *testing.T) { err := ocr.RequestNewRound() require.NoError(t, err, "Error requesting new round") } - round, err := ocrInstances[0].GetLatestRound(context.Background()) + round, err := ocrInstances[0].GetLatestRound(it_utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred()) l.Info().Int64("RoundID", round.RoundId.Int64()).Msg("Latest OCR Round") if round.RoundId.Int64() == chaosStartRound && !chaosApplied { diff --git a/integration-tests/client/chainlink_k8s.go b/integration-tests/client/chainlink_k8s.go index 3fbf9eaf73c..27fd956103e 100644 --- a/integration-tests/client/chainlink_k8s.go +++ b/integration-tests/client/chainlink_k8s.go @@ -63,7 +63,7 @@ func (c *ChainlinkK8sClient) UpgradeVersion(testEnvironment *environment.Environ }, }, } - testEnvironment, err := testEnvironment.UpdateHelm(c.ChartName, upgradeVals) + _, err := testEnvironment.UpdateHelm(c.ChartName, upgradeVals) return err } diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 916971f82d3..45195d327ee 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -12,11 +12,12 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_load_test_client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_v1_events_mock" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_consumer_benchmark" @@ -868,22 +869,21 @@ func (e *EthereumContractDeployer) LoadKeeperRegistrar(address common.Address, r client: e.client, registrar20: instance.(*keeper_registrar_wrapper2_0.KeeperRegistrar), }, err - } else { - instance, err := e.client.LoadContract("AutomationRegistrar", address, func( - address common.Address, - backend bind.ContractBackend, - ) (interface{}, error) { - return registrar21.NewAutomationRegistrar(address, backend) - }) - if err != nil { - return nil, err - } - return &EthereumKeeperRegistrar{ - address: &address, - client: e.client, - registrar21: instance.(*registrar21.AutomationRegistrar), - }, err } + instance, err := e.client.LoadContract("AutomationRegistrar", address, func( + address common.Address, + backend bind.ContractBackend, + ) (interface{}, error) { + return registrar21.NewAutomationRegistrar(address, backend) + }) + if err != nil { + return nil, err + } + return &EthereumKeeperRegistrar{ + address: &address, + client: e.client, + registrar21: instance.(*registrar21.AutomationRegistrar), + }, err } func (e *EthereumContractDeployer) DeployKeeperRegistry( diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index cfe7a35467e..9a2f20226d3 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -2,6 +2,7 @@ package contracts import ( "errors" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_load_test_with_metrics" diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index c82924143b0..baee2ccd929 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -262,12 +262,13 @@ type RequestStatus struct { } type LoadTestRequestStatus struct { - Fulfilled bool - RandomWords []*big.Int - requestTimestamp *big.Int - fulfilmentTimestamp *big.Int - requestBlockNumber *big.Int - fulfilmentBlockNumber *big.Int + Fulfilled bool + RandomWords []*big.Int + // Currently Unused November 8, 2023, Mignt be used in near future, will remove if not. + // requestTimestamp *big.Int + // fulfilmentTimestamp *big.Int + // requestBlockNumber *big.Int + // fulfilmentBlockNumber *big.Int } type VRFLoadTestMetrics struct { diff --git a/integration-tests/contracts/ethereum_contracts.go b/integration-tests/contracts/ethereum_contracts.go index cde6e325f2a..9cb858fe007 100644 --- a/integration-tests/contracts/ethereum_contracts.go +++ b/integration-tests/contracts/ethereum_contracts.go @@ -16,6 +16,11 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" + ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" + ocrTypes "github.com/smartcontractkit/libocr/offchainreporting/types" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_load_test_client" @@ -43,10 +48,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/werc20_mock" - "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" - "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" - ocrConfigHelper "github.com/smartcontractkit/libocr/offchainreporting/confighelper" - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink/integration-tests/client" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" @@ -939,7 +940,7 @@ func (f *EthereumFluxAggregator) PaymentAmount(ctx context.Context) (*big.Int, e return payment, nil } -func (f *EthereumFluxAggregator) RequestNewRound(ctx context.Context) error { +func (f *EthereumFluxAggregator) RequestNewRound(_ context.Context) error { opts, err := f.client.TransactionOpts(f.client.GetDefaultWallet()) if err != nil { return err @@ -978,7 +979,7 @@ func (f *EthereumFluxAggregator) WatchSubmissionReceived(ctx context.Context, ev } } -func (f *EthereumFluxAggregator) SetRequesterPermissions(ctx context.Context, addr common.Address, authorized bool, roundsDelay uint32) error { +func (f *EthereumFluxAggregator) SetRequesterPermissions(_ context.Context, addr common.Address, authorized bool, roundsDelay uint32) error { opts, err := f.client.TransactionOpts(f.client.GetDefaultWallet()) if err != nil { return err @@ -1019,7 +1020,7 @@ func (f *EthereumFluxAggregator) LatestRoundID(ctx context.Context) (*big.Int, e } func (f *EthereumFluxAggregator) WithdrawPayment( - ctx context.Context, + _ context.Context, from common.Address, to common.Address, amount *big.Int) error { diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 135b016ee55..2c0250e7454 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -250,25 +250,25 @@ func (rcs *KeeperRegistrySettings) EncodeOnChainConfig(registrar string, registr encodedOnchainConfig, err := utilsABI.Methods["_onChainConfig"].Inputs.Pack(&onchainConfigStruct) return encodedOnchainConfig, err - } else { - configType := goabi.MustNewType("tuple(uint32 paymentPremiumPPB,uint32 flatFeeMicroLink,uint32 checkGasLimit,uint24 stalenessSeconds,uint16 gasCeilingMultiplier,uint96 minUpkeepSpend,uint32 maxPerformGas,uint32 maxCheckDataSize,uint32 maxPerformDataSize,uint256 fallbackGasPrice,uint256 fallbackLinkPrice,address transcoder,address registrar)") - onchainConfig, err := goabi.Encode(map[string]interface{}{ - "paymentPremiumPPB": rcs.PaymentPremiumPPB, - "flatFeeMicroLink": rcs.FlatFeeMicroLINK, - "checkGasLimit": rcs.CheckGasLimit, - "stalenessSeconds": rcs.StalenessSeconds, - "gasCeilingMultiplier": rcs.GasCeilingMultiplier, - "minUpkeepSpend": rcs.MinUpkeepSpend, - "maxPerformGas": rcs.MaxPerformGas, - "maxCheckDataSize": rcs.MaxCheckDataSize, - "maxPerformDataSize": rcs.MaxPerformDataSize, - "fallbackGasPrice": rcs.FallbackGasPrice, - "fallbackLinkPrice": rcs.FallbackLinkPrice, - "transcoder": common.Address{}, - "registrar": registrar, - }, configType) - return onchainConfig, err } + configType := goabi.MustNewType("tuple(uint32 paymentPremiumPPB,uint32 flatFeeMicroLink,uint32 checkGasLimit,uint24 stalenessSeconds,uint16 gasCeilingMultiplier,uint96 minUpkeepSpend,uint32 maxPerformGas,uint32 maxCheckDataSize,uint32 maxPerformDataSize,uint256 fallbackGasPrice,uint256 fallbackLinkPrice,address transcoder,address registrar)") + onchainConfig, err := goabi.Encode(map[string]interface{}{ + "paymentPremiumPPB": rcs.PaymentPremiumPPB, + "flatFeeMicroLink": rcs.FlatFeeMicroLINK, + "checkGasLimit": rcs.CheckGasLimit, + "stalenessSeconds": rcs.StalenessSeconds, + "gasCeilingMultiplier": rcs.GasCeilingMultiplier, + "minUpkeepSpend": rcs.MinUpkeepSpend, + "maxPerformGas": rcs.MaxPerformGas, + "maxCheckDataSize": rcs.MaxCheckDataSize, + "maxPerformDataSize": rcs.MaxPerformDataSize, + "fallbackGasPrice": rcs.FallbackGasPrice, + "fallbackLinkPrice": rcs.FallbackLinkPrice, + "transcoder": common.Address{}, + "registrar": registrar, + }, configType) + return onchainConfig, err + } func (v *EthereumKeeperRegistry) RegistryOwnerAddress() common.Address { @@ -276,6 +276,7 @@ func (v *EthereumKeeperRegistry) RegistryOwnerAddress() common.Address { Pending: false, } + //nolint: exhaustive switch v.version { case ethereum.RegistryVersion_2_1: ownerAddress, _ := v.registry2_1.Owner(callOpts) @@ -283,6 +284,8 @@ func (v *EthereumKeeperRegistry) RegistryOwnerAddress() common.Address { case ethereum.RegistryVersion_2_0: ownerAddress, _ := v.registry2_0.Owner(callOpts) return ownerAddress + case ethereum.RegistryVersion_1_0, ethereum.RegistryVersion_1_1, ethereum.RegistryVersion_1_2, ethereum.RegistryVersion_1_3: + return common.HexToAddress(v.client.GetDefaultWallet().Address()) } return common.HexToAddress(v.client.GetDefaultWallet().Address()) @@ -664,7 +667,7 @@ func (v *EthereumKeeperRegistry) GetKeeperInfo(ctx context.Context, keeperAddr s info, err = v.registry1_2.GetKeeperInfo(opts, common.HexToAddress(keeperAddr)) case ethereum.RegistryVersion_1_3: info, err = v.registry1_3.GetKeeperInfo(opts, common.HexToAddress(keeperAddr)) - case ethereum.RegistryVersion_2_0: + case ethereum.RegistryVersion_2_0, ethereum.RegistryVersion_2_1: // this is not used anywhere return nil, fmt.Errorf("not supported") } @@ -710,6 +713,8 @@ func (v *EthereumKeeperRegistry) SetKeepers(keepers []string, payees []string, o ocrConfig.OffchainConfigVersion, ocrConfig.OffchainConfig, ) + case ethereum.RegistryVersion_2_1: + return fmt.Errorf("not supported") } if err != nil { @@ -760,6 +765,8 @@ func (v *EthereumKeeperRegistry) RegisterUpkeep(target string, gasLimit uint32, checkData, nil, //offchain config ) + case ethereum.RegistryVersion_2_1: + return fmt.Errorf("not supported") } if err != nil { @@ -877,6 +884,8 @@ func (v *EthereumKeeperRegistry) GetKeeperList(ctx context.Context) ([]string, e return []string{}, err } list = state.Transmitters + case ethereum.RegistryVersion_2_1: + return nil, fmt.Errorf("not supported") } if err != nil { @@ -1112,6 +1121,7 @@ func (v *EthereumKeeperRegistry) ParseUpkeepPerformedLog(log *types.Log) (*Upkee // ParseStaleUpkeepReportLog Parses Stale upkeep report log func (v *EthereumKeeperRegistry) ParseStaleUpkeepReportLog(log *types.Log) (*StaleUpkeepReportLog, error) { + //nolint:exhaustive switch v.version { case ethereum.RegistryVersion_2_0: parsedLog, err := v.registry2_0.ParseStaleUpkeepReport(*log) @@ -1129,7 +1139,6 @@ func (v *EthereumKeeperRegistry) ParseStaleUpkeepReportLog(log *types.Log) (*Sta return &StaleUpkeepReportLog{ Id: parsedLog.Id, }, nil - } return nil, fmt.Errorf("keeper registry version %d is not supported", v.version) } @@ -1850,7 +1859,7 @@ func (v *EthereumKeeperConsumerPerformance) GetUpkeepCount(ctx context.Context) return eligible, err } -func (v *EthereumKeeperConsumerPerformance) SetCheckGasToBurn(ctx context.Context, gas *big.Int) error { +func (v *EthereumKeeperConsumerPerformance) SetCheckGasToBurn(_ context.Context, gas *big.Int) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err @@ -1862,7 +1871,7 @@ func (v *EthereumKeeperConsumerPerformance) SetCheckGasToBurn(ctx context.Contex return v.client.ProcessTransaction(tx) } -func (v *EthereumKeeperConsumerPerformance) SetPerformGasToBurn(ctx context.Context, gas *big.Int) error { +func (v *EthereumKeeperConsumerPerformance) SetPerformGasToBurn(_ context.Context, gas *big.Int) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err @@ -1897,7 +1906,7 @@ func (v *EthereumKeeperPerformDataCheckerConsumer) Counter(ctx context.Context) return cnt, nil } -func (v *EthereumKeeperPerformDataCheckerConsumer) SetExpectedData(ctx context.Context, expectedData []byte) error { +func (v *EthereumKeeperPerformDataCheckerConsumer) SetExpectedData(_ context.Context, expectedData []byte) error { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { return err @@ -2041,31 +2050,23 @@ func (v *EthereumKeeperRegistrar) EncodeRegisterRequest(name string, email []byt common.HexToAddress(senderAddr), ) - if err != nil { - return nil, err - } - return req, nil - } else { - req, err := registrarABI.Pack( - "register", - name, - email, - common.HexToAddress(upkeepAddr), - gasLimit, - common.HexToAddress(adminAddr), - uint8(0), // trigger type - checkData, - []byte{}, // triggerConfig - []byte{}, // offchainConfig - amount, - common.HexToAddress(senderAddr), - ) - - if err != nil { - return nil, err - } - return req, nil + return req, err } + req, err := registrarABI.Pack( + "register", + name, + email, + common.HexToAddress(upkeepAddr), + gasLimit, + common.HexToAddress(adminAddr), + uint8(0), // trigger type + checkData, + []byte{}, // triggerConfig + []byte{}, // offchainConfig + amount, + common.HexToAddress(senderAddr), + ) + return req, err } registryABI, err := abi.JSON(strings.NewReader(keeper_registrar_wrapper1_2.KeeperRegistrarMetaData.ABI)) if err != nil { diff --git a/integration-tests/contracts/test_contracts.go b/integration-tests/contracts/test_contracts.go index ccdd2989e49..3080668da69 100644 --- a/integration-tests/contracts/test_contracts.go +++ b/integration-tests/contracts/test_contracts.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/rs/zerolog" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" diff --git a/integration-tests/docker/cmd/test_env.go b/integration-tests/docker/cmd/test_env.go index f760f45f8d0..5fe2001350e 100644 --- a/integration-tests/docker/cmd/test_env.go +++ b/integration-tests/docker/cmd/test_env.go @@ -9,10 +9,11 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/spf13/cobra" "github.com/testcontainers/testcontainers-go" + + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) func main() { @@ -31,7 +32,7 @@ func main() { Use: "cl-cluster", Short: "Basic CL cluster", RunE: func(cmd *cobra.Command, args []string) error { - utils.SetupCoreDockerEnvLogger() + log.Logger = logging.GetLogger(nil, "CORE_DOCKER_ENV_LOG_LEVEL") log.Info().Msg("Starting CL cluster test environment..") _, err := test_env.NewCLTestEnvBuilder(). diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 6e74e54a4f9..3c0a6d3af76 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -1,15 +1,11 @@ package test_env import ( - "context" - "crypto/ed25519" - "encoding/hex" "fmt" "math/big" "net/url" "os" "strings" - "sync" "testing" "time" @@ -29,8 +25,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logwatch" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2/types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/utils" @@ -118,7 +112,7 @@ func (n *ClNode) SetTestLogger(t *testing.T) { // Restart restarts only CL node, DB container is reused func (n *ClNode) Restart(cfg *chainlink.Config) error { - if err := n.Container.Terminate(context.Background()); err != nil { + if err := n.Container.Terminate(utils.TestContext(n.t)); err != nil { return err } n.NodeConfig = cfg @@ -126,7 +120,7 @@ func (n *ClNode) Restart(cfg *chainlink.Config) error { } // UpgradeVersion restarts the cl node with new image and version -func (n *ClNode) UpgradeVersion(cfg *chainlink.Config, newImage, newVersion string) error { +func (n *ClNode) UpgradeVersion(newImage, newVersion string) error { if newVersion == "" { return fmt.Errorf("new version is empty") } @@ -142,9 +136,9 @@ func (n *ClNode) PrimaryETHAddress() (string, error) { return n.API.PrimaryEthAddress() } -func (n *ClNode) AddBootstrapJob(verifierAddr common.Address, fromBlock uint64, chainId int64, +func (n *ClNode) AddBootstrapJob(verifierAddr common.Address, chainId int64, feedId [32]byte) (*client.Job, error) { - spec := utils.BuildBootstrapSpec(verifierAddr, chainId, fromBlock, feedId) + spec := utils.BuildBootstrapSpec(verifierAddr, chainId, feedId) return n.API.MustCreateJob(spec) } @@ -196,7 +190,7 @@ func (n *ClNode) AddMercuryOCRJob(verifierAddr common.Address, fromBlock uint64, } func (n *ClNode) GetContainerName() string { - name, err := n.Container.Name(context.Background()) + name, err := n.Container.Name(utils.TestContext(n.t)) if err != nil { return "" } @@ -288,15 +282,15 @@ func (n *ClNode) StartContainer() error { return fmt.Errorf("%s err: %w", ErrStartCLNodeContainer, err) } if n.lw != nil { - if err := n.lw.ConnectContainer(context.Background(), container, "cl-node", true); err != nil { + if err := n.lw.ConnectContainer(utils.TestContext(n.t), container, "cl-node", true); err != nil { return err } } - clEndpoint, err := test_env.GetEndpoint(context.Background(), container, "http") + clEndpoint, err := test_env.GetEndpoint(utils.TestContext(n.t), container, "http") if err != nil { return err } - ip, err := container.ContainerIP(context.Background()) + ip, err := container.ContainerIP(utils.TestContext(n.t)) if err != nil { return err } @@ -414,83 +408,3 @@ func (n *ClNode) getContainerRequest(secrets string) ( }, }, nil } - -func GetOracleIdentities(chainlinkNodes []*ClNode) ([]int, []confighelper.OracleIdentityExtra) { - S := make([]int, len(chainlinkNodes)) - oracleIdentities := make([]confighelper.OracleIdentityExtra, len(chainlinkNodes)) - sharedSecretEncryptionPublicKeys := make([]ocrtypes.ConfigEncryptionPublicKey, len(chainlinkNodes)) - var wg sync.WaitGroup - for i, cl := range chainlinkNodes { - wg.Add(1) - go func(i int, cl *ClNode) error { - defer wg.Done() - - ocr2Keys, err := cl.API.MustReadOCR2Keys() - if err != nil { - return err - } - var ocr2Config client.OCR2KeyAttributes - for _, key := range ocr2Keys.Data { - if key.Attributes.ChainType == string(chaintype.EVM) { - ocr2Config = key.Attributes - break - } - } - - keys, err := cl.API.MustReadP2PKeys() - if err != nil { - return err - } - p2pKeyID := keys.Data[0].Attributes.PeerID - - offchainPkBytes, err := hex.DecodeString(strings.TrimPrefix(ocr2Config.OffChainPublicKey, "ocr2off_evm_")) - if err != nil { - return err - } - - offchainPkBytesFixed := [ed25519.PublicKeySize]byte{} - copy(offchainPkBytesFixed[:], offchainPkBytes) - if err != nil { - return err - } - - configPkBytes, err := hex.DecodeString(strings.TrimPrefix(ocr2Config.ConfigPublicKey, "ocr2cfg_evm_")) - if err != nil { - return err - } - - configPkBytesFixed := [ed25519.PublicKeySize]byte{} - copy(configPkBytesFixed[:], configPkBytes) - if err != nil { - return err - } - - onchainPkBytes, err := hex.DecodeString(strings.TrimPrefix(ocr2Config.OnChainPublicKey, "ocr2on_evm_")) - if err != nil { - return err - } - - csaKeys, _, err := cl.API.ReadCSAKeys() - if err != nil { - return err - } - - sharedSecretEncryptionPublicKeys[i] = configPkBytesFixed - oracleIdentities[i] = confighelper.OracleIdentityExtra{ - OracleIdentity: confighelper.OracleIdentity{ - OnchainPublicKey: onchainPkBytes, - OffchainPublicKey: offchainPkBytesFixed, - PeerID: p2pKeyID, - TransmitAccount: ocrtypes.Account(csaKeys.Data[0].ID), - }, - ConfigEncryptionPublicKey: configPkBytesFixed, - } - S[i] = 1 - - return nil - }(i, cl) - } - wg.Wait() - - return S, oracleIdentities -} diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 4a304872116..9987bab2fe0 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -1,7 +1,6 @@ package test_env import ( - "context" "encoding/json" "fmt" "io" @@ -53,7 +52,7 @@ type CLClusterTestEnv struct { } func NewTestEnv() (*CLClusterTestEnv, error) { - utils.SetupCoreDockerEnvLogger() + log.Logger = logging.GetLogger(nil, "CORE_DOCKER_ENV_LOG_LEVEL") network, err := docker.CreateNetwork(log.Logger) if err != nil { return nil, err @@ -267,7 +266,7 @@ func (te *CLClusterTestEnv) collectTestLogs() error { return err } defer logFile.Close() - logReader, err := node.Container.Logs(context.Background()) + logReader, err := node.Container.Logs(utils.TestContext(te.t)) if err != nil { return err } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index e97f869d64e..77c56690155 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -188,7 +188,7 @@ func (b *CLTestEnvBuilder) WithCustomCleanup(customFn func()) *CLTestEnvBuilder type ChainOption = func(*evmcfg.Chain) *evmcfg.Chain func (b *CLTestEnvBuilder) WithChainOptions(opts ...ChainOption) *CLTestEnvBuilder { - b.chainOptionsFn = make([]ChainOption, 0, 0) + b.chainOptionsFn = make([]ChainOption, 0) b.chainOptionsFn = append(b.chainOptionsFn, opts...) return b @@ -197,7 +197,7 @@ func (b *CLTestEnvBuilder) WithChainOptions(opts ...ChainOption) *CLTestEnvBuild type EVMClientNetworkOption = func(*blockchain.EVMNetwork) *blockchain.EVMNetwork func (b *CLTestEnvBuilder) EVMClientNetworkOptions(opts ...EVMClientNetworkOption) *CLTestEnvBuilder { - b.evmClientNetworkOption = make([]EVMClientNetworkOption, 0, 0) + b.evmClientNetworkOption = make([]EVMClientNetworkOption, 0) b.evmClientNetworkOption = append(b.evmClientNetworkOption, opts...) return b diff --git a/integration-tests/load/functions/config.go b/integration-tests/load/functions/config.go index 451d01a6c89..ad7e7446afb 100644 --- a/integration-tests/load/functions/config.go +++ b/integration-tests/load/functions/config.go @@ -7,6 +7,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -118,8 +119,7 @@ func ReadConfig() (*PerformanceConfig, error) { return nil, fmt.Errorf( "ensure variables are set:\nMUMBAI_KEYS variable, private keys, comma separated\nSELECTED_NETWORKS=MUMBAI\nMUMBAI_URLS variable, websocket urls, comma separated", ) - } else { - cfg.MumbaiPrivateKey = mpk } + cfg.MumbaiPrivateKey = mpk return cfg, nil } diff --git a/integration-tests/load/functions/functions_test.go b/integration-tests/load/functions/functions_test.go index 7822035208e..dc52846d3c9 100644 --- a/integration-tests/load/functions/functions_test.go +++ b/integration-tests/load/functions/functions_test.go @@ -1,10 +1,11 @@ package loadfunctions import ( - "github.com/smartcontractkit/wasp" - "github.com/stretchr/testify/require" "testing" "time" + + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" ) func TestFunctionsLoad(t *testing.T) { diff --git a/integration-tests/load/functions/gateway.go b/integration-tests/load/functions/gateway.go index 78b0f14cf18..ac5f895ac18 100644 --- a/integration-tests/load/functions/gateway.go +++ b/integration-tests/load/functions/gateway.go @@ -14,10 +14,11 @@ import ( "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/go-resty/resty/v2" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" + "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" "github.com/smartcontractkit/chainlink/v2/core/services/s4" - "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" ) type RPCResponse struct { @@ -115,7 +116,7 @@ func UploadS4Secrets(rc *resty.Client, s4Cfg *S4SecretsCfg) (uint8, uint64, erro log.Debug().Interface("Result", result).Msg("S4 secrets_set response result") for _, nodeResponse := range result.Result.Body.Payload.NodeResponses { if !nodeResponse.Body.Payload.Success { - return 0, 0, fmt.Errorf("node response was not succesful") + return 0, 0, fmt.Errorf("node response was not successful") } } return uint8(envelope.SlotID), envelope.Version, nil diff --git a/integration-tests/load/functions/gateway_gun.go b/integration-tests/load/functions/gateway_gun.go index fd13922d0a7..3dafb458a50 100644 --- a/integration-tests/load/functions/gateway_gun.go +++ b/integration-tests/load/functions/gateway_gun.go @@ -3,14 +3,15 @@ package loadfunctions import ( "crypto/ecdsa" "fmt" - "github.com/go-resty/resty/v2" - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" - "github.com/smartcontractkit/wasp" "math/rand" "os" "strconv" "time" + + "github.com/go-resty/resty/v2" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" + "github.com/smartcontractkit/wasp" ) /* SingleFunctionCallGun is a gun that constantly requests randomness for one feed */ diff --git a/integration-tests/load/functions/onchain_monitoring.go b/integration-tests/load/functions/onchain_monitoring.go index 0a8b4cef46a..c4b4bdb78c0 100644 --- a/integration-tests/load/functions/onchain_monitoring.go +++ b/integration-tests/load/functions/onchain_monitoring.go @@ -1,10 +1,11 @@ package loadfunctions import ( - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/wasp" "testing" "time" + + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/wasp" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ diff --git a/integration-tests/load/functions/request_gun.go b/integration-tests/load/functions/request_gun.go index d9987eaa756..bd4cf5f35aa 100644 --- a/integration-tests/load/functions/request_gun.go +++ b/integration-tests/load/functions/request_gun.go @@ -13,16 +13,15 @@ const ( ) type SingleFunctionCallGun struct { - ft *FunctionsTest - mode TestMode - times uint32 - source string - slotID uint8 - slotVersion uint64 - encryptedSecrets []byte - args []string - subscriptionId uint64 - jobId [32]byte + ft *FunctionsTest + mode TestMode + times uint32 + source string + slotID uint8 + slotVersion uint64 + args []string + subscriptionId uint64 + jobId [32]byte } func NewSingleFunctionCallGun( diff --git a/integration-tests/load/functions/setup.go b/integration-tests/load/functions/setup.go index 81bc660b35e..c0be47ca836 100644 --- a/integration-tests/load/functions/setup.go +++ b/integration-tests/load/functions/setup.go @@ -12,9 +12,10 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/go-resty/resty/v2" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" "github.com/smartcontractkit/chainlink/integration-tests/contracts" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/integration-tests/load/log_poller/log_poller_test.go b/integration-tests/load/log_poller/log_poller_test.go index ec67815832c..04366848f0e 100644 --- a/integration-tests/load/log_poller/log_poller_test.go +++ b/integration-tests/load/log_poller/log_poller_test.go @@ -5,8 +5,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" - lp_helpers "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" "github.com/stretchr/testify/require" + + lp_helpers "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" ) func TestLoadTestLogPoller(t *testing.T) { diff --git a/integration-tests/load/vrfv2/cmd/dashboard.go b/integration-tests/load/vrfv2/cmd/dashboard.go index 3035da0422f..0fb7be2b78b 100644 --- a/integration-tests/load/vrfv2/cmd/dashboard.go +++ b/integration-tests/load/vrfv2/cmd/dashboard.go @@ -1,6 +1,8 @@ package main import ( + "os" + "github.com/K-Phoen/grabana/dashboard" "github.com/K-Phoen/grabana/logs" "github.com/K-Phoen/grabana/row" @@ -8,7 +10,6 @@ import ( "github.com/K-Phoen/grabana/timeseries" "github.com/K-Phoen/grabana/timeseries/axis" "github.com/smartcontractkit/wasp" - "os" ) func main() { diff --git a/integration-tests/load/vrfv2/config.go b/integration-tests/load/vrfv2/config.go index 0c62cc351b4..0a595f753c2 100644 --- a/integration-tests/load/vrfv2/config.go +++ b/integration-tests/load/vrfv2/config.go @@ -7,6 +7,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index d6a8977738b..8100baaa7f7 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -1,9 +1,10 @@ package loadvrfv2 import ( + "github.com/smartcontractkit/wasp" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" - "github.com/smartcontractkit/wasp" ) /* SingleHashGun is a gun that constantly requests randomness for one feed */ @@ -21,7 +22,7 @@ func SingleFeedGun(contracts *vrfv2_actions.VRFV2Contracts, keyHash [32]byte) *S } // Call implements example gun call, assertions on response bodies should be done here -func (m *SingleHashGun) Call(l *wasp.Generator) *wasp.CallResult { +func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { err := m.contracts.LoadTestConsumer.RequestRandomness( m.keyHash, vrfConst.SubID, diff --git a/integration-tests/load/vrfv2/onchain_monitoring.go b/integration-tests/load/vrfv2/onchain_monitoring.go index b4503d27fad..879c7089e16 100644 --- a/integration-tests/load/vrfv2/onchain_monitoring.go +++ b/integration-tests/load/vrfv2/onchain_monitoring.go @@ -1,12 +1,14 @@ package loadvrfv2 import ( - "context" - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - "github.com/smartcontractkit/wasp" "testing" "time" + + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ @@ -34,7 +36,7 @@ func MonitorLoadStats(t *testing.T, vrfv2Contracts *vrfv2_actions.VRFV2Contracts } for { time.Sleep(1 * time.Second) - metrics, err := vrfv2Contracts.LoadTestConsumer.GetLoadTestMetrics(context.Background()) + metrics, err := vrfv2Contracts.LoadTestConsumer.GetLoadTestMetrics(utils.TestContext(t)) if err != nil { log.Error().Err(err).Msg(ErrMetrics) } diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index a9fb80a72ad..44325965bd7 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -3,9 +3,10 @@ package loadvrfv2 import ( "testing" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" ) func TestVRFV2Load(t *testing.T) { diff --git a/integration-tests/load/vrfv2/vu.go b/integration-tests/load/vrfv2/vu.go index 4658388d400..7eb02ae330f 100644 --- a/integration-tests/load/vrfv2/vu.go +++ b/integration-tests/load/vrfv2/vu.go @@ -4,11 +4,12 @@ import ( "fmt" "time" + "github.com/smartcontractkit/wasp" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/wasp" ) /* JobVolumeVU is a "virtual user" that creates a VRFv2 job and constantly requesting new randomness only for this job instance */ diff --git a/integration-tests/load/vrfv2plus/cmd/dashboard.go b/integration-tests/load/vrfv2plus/cmd/dashboard.go index 9a0ba682a18..049ee9ff2e9 100644 --- a/integration-tests/load/vrfv2plus/cmd/dashboard.go +++ b/integration-tests/load/vrfv2plus/cmd/dashboard.go @@ -1,6 +1,8 @@ package main import ( + "os" + "github.com/K-Phoen/grabana/dashboard" "github.com/K-Phoen/grabana/logs" "github.com/K-Phoen/grabana/row" @@ -8,7 +10,6 @@ import ( "github.com/K-Phoen/grabana/timeseries" "github.com/K-Phoen/grabana/timeseries/axis" "github.com/smartcontractkit/wasp" - "os" ) func main() { diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index cba3fdcde59..96dbf99c6b2 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -7,6 +7,7 @@ import ( "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -100,6 +101,9 @@ func ReadConfig() (*PerformanceConfig, error) { } } else { d, err = base64.StdEncoding.DecodeString(rawConfig) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) + } } err = toml.Unmarshal(d, &cfg) if err != nil { diff --git a/integration-tests/load/vrfv2plus/gun.go b/integration-tests/load/vrfv2plus/gun.go index 21be1c74ca7..8ab278b73e9 100644 --- a/integration-tests/load/vrfv2plus/gun.go +++ b/integration-tests/load/vrfv2plus/gun.go @@ -1,12 +1,14 @@ package loadvrfv2plus import ( + "math/big" + "math/rand" + "github.com/rs/zerolog" + "github.com/smartcontractkit/wasp" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" - "github.com/smartcontractkit/wasp" - "math/big" - "math/rand" ) /* SingleHashGun is a gun that constantly requests randomness for one feed */ @@ -36,7 +38,7 @@ func NewSingleHashGun( } // Call implements example gun call, assertions on response bodies should be done here -func (m *SingleHashGun) Call(l *wasp.Generator) *wasp.CallResult { +func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { //todo - should work with multiple consumers and consumers having different keyhashes and wallets //randomly increase/decrease randomness request count per TX diff --git a/integration-tests/load/vrfv2plus/onchain_monitoring.go b/integration-tests/load/vrfv2plus/onchain_monitoring.go index c56d835234e..c911546af0c 100644 --- a/integration-tests/load/vrfv2plus/onchain_monitoring.go +++ b/integration-tests/load/vrfv2plus/onchain_monitoring.go @@ -2,11 +2,13 @@ package loadvrfv2plus import ( "context" - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/wasp" "testing" "time" + + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 4d3de014bcd..e7734fee0d5 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -1,20 +1,22 @@ package loadvrfv2plus import ( - "context" - "github.com/ethereum/go-ethereum/common" - "github.com/kelseyhightower/envconfig" - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/wasp" - "github.com/stretchr/testify/require" "math/big" "os" "sync" "testing" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/kelseyhightower/envconfig" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" @@ -97,7 +99,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { Str("Returning funds from SubID", subID.String()). Str("Returning funds to", eoaWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subID) + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(utils.TestContext(t), subID) if err != nil { l.Error().Err(err).Msg("Error checking if pending requests exist") } @@ -228,7 +230,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err, "error getting subscription information for subscription %s", subID.String()) vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) } diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index bf97f43d058..c851f36ec62 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -1,13 +1,12 @@ package migration import ( + "os" "testing" - "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/stretchr/testify/require" - "os" - + "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) diff --git a/integration-tests/performance/cron_test.go b/integration-tests/performance/cron_test.go index e700a66e1f8..7e90d29221d 100644 --- a/integration-tests/performance/cron_test.go +++ b/integration-tests/performance/cron_test.go @@ -20,7 +20,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/smartcontractkit/chainlink-testing-framework/networks" @@ -44,7 +43,7 @@ func CleanupPerformanceTest( if chainClient != nil { chainClient.GasStats().PrintStats() } - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, &testReporter, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, &testReporter, zapcore.PanicLevel, chainClient) require.NoError(t, err, "Error tearing down environment") } diff --git a/integration-tests/performance/directrequest_test.go b/integration-tests/performance/directrequest_test.go index d229f9fb3ee..1a3f1d2a010 100644 --- a/integration-tests/performance/directrequest_test.go +++ b/integration-tests/performance/directrequest_test.go @@ -1,7 +1,6 @@ package performance import ( - "context" "fmt" "math/big" "strings" @@ -25,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" + "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/google/uuid" ) @@ -108,7 +108,7 @@ func TestDirectRequestPerformance(t *testing.T) { gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(context.Background()) + d, err := consumer.Data(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") l.Debug().Int64("Data", d.Int64()).Msg("Found on chain") diff --git a/integration-tests/performance/flux_test.go b/integration-tests/performance/flux_test.go index be536450a76..18b13ab9076 100644 --- a/integration-tests/performance/flux_test.go +++ b/integration-tests/performance/flux_test.go @@ -1,7 +1,6 @@ package performance import ( - "context" "fmt" "math/big" "strings" @@ -26,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestFluxPerformance(t *testing.T) { @@ -83,7 +83,7 @@ func TestFluxPerformance(t *testing.T) { require.NoError(t, err, "Setting oracle options in the Flux Aggregator contract shouldn't fail") err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - oracles, err := fluxInstance.GetOracles(context.Background()) + oracles, err := fluxInstance.GetOracles(utils.TestContext(t)) require.NoError(t, err, "Getting oracle details from the Flux aggregator contract shouldn't fail") l.Info().Str("Oracles", strings.Join(oracles, ",")).Msg("Oracles set") @@ -120,7 +120,7 @@ func TestFluxPerformance(t *testing.T) { chainClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err := fluxInstance.GetContractData(context.Background()) + data, err := fluxInstance.GetContractData(utils.TestContext(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") l.Info().Interface("Data", data).Msg("Round data") require.Equal(t, int64(1e5), data.LatestRoundData.Answer.Int64(), @@ -140,7 +140,7 @@ func TestFluxPerformance(t *testing.T) { require.NoError(t, err, "Setting value path in mock server shouldn't fail") err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err = fluxInstance.GetContractData(context.Background()) + data, err = fluxInstance.GetContractData(utils.TestContext(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") require.Equal(t, int64(1e10), data.LatestRoundData.Answer.Int64(), "Expected latest round answer to be %d, but found %d", int64(1e10), data.LatestRoundData.Answer.Int64()) @@ -153,7 +153,7 @@ func TestFluxPerformance(t *testing.T) { l.Info().Interface("data", data).Msg("Round data") for _, oracleAddr := range nodeAddresses { - payment, _ := fluxInstance.WithdrawablePayment(context.Background(), oracleAddr) + payment, _ := fluxInstance.WithdrawablePayment(utils.TestContext(t), oracleAddr) require.Equal(t, int64(2), payment.Int64(), "Expected flux aggregator contract's withdrawable payment to be %d, but found %d", int64(2), payment.Int64()) } diff --git a/integration-tests/performance/keeper_test.go b/integration-tests/performance/keeper_test.go index cd9818f99d3..8e273a96f69 100644 --- a/integration-tests/performance/keeper_test.go +++ b/integration-tests/performance/keeper_test.go @@ -2,7 +2,6 @@ package performance //revive:disable:dot-imports import ( - "context" "fmt" "math/big" "strings" @@ -26,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var keeperDefaultRegistryConfig = contracts.KeeperRegistrySettings{ @@ -74,7 +74,7 @@ func TestKeeperPerformance(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) @@ -84,7 +84,7 @@ func TestKeeperPerformance(t *testing.T) { // Cancel all the registered upkeeps via the registry for i := 0; i < len(upkeepIDs); i++ { - err := registry.CancelUpkeep(upkeepIDs[i]) + err = registry.CancelUpkeep(upkeepIDs[i]) require.NoError(t, err, "Could not cancel upkeep at index %d", i) } @@ -95,7 +95,7 @@ func TestKeeperPerformance(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(context.Background()) + countersAfterCancellation[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Index", i).Int64("Upkeeps Performed", countersAfterCancellation[i].Int64()).Msg("Cancelled Upkeep") } @@ -103,7 +103,7 @@ func TestKeeperPerformance(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant because the upkeep was cancelled, so it shouldn't increase anymore - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterCancellation[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go index e81cc91cf7f..47879cebb81 100644 --- a/integration-tests/performance/ocr_test.go +++ b/integration-tests/performance/ocr_test.go @@ -1,7 +1,6 @@ package performance import ( - "context" "fmt" "math/big" "strings" @@ -25,6 +24,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCRBasic(t *testing.T) { @@ -53,7 +53,7 @@ func TestOCRBasic(t *testing.T) { err = actions.FundChainlinkNodes(chainlinkNodes, chainClient, big.NewFloat(.05)) require.NoError(t, err, "Error funding Chainlink nodes") - ocrInstances, err := actions.DeployOCRContracts(1, linkTokenContract, contractDeployer, bootstrapNode, workerNodes, chainClient) + ocrInstances, err := actions.DeployOCRContracts(1, linkTokenContract, contractDeployer, workerNodes, chainClient) require.NoError(t, err) err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") @@ -64,7 +64,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(1, ocrInstances, chainClient, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -73,7 +73,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(2, ocrInstances, chainClient, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } diff --git a/integration-tests/performance/vrf_test.go b/integration-tests/performance/vrf_test.go index eeaceffaaf5..7a38a454955 100644 --- a/integration-tests/performance/vrf_test.go +++ b/integration-tests/performance/vrf_test.go @@ -1,7 +1,6 @@ package performance import ( - "context" "fmt" "math/big" "strings" @@ -23,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFBasic(t *testing.T) { @@ -97,7 +97,7 @@ func TestVRFBasic(t *testing.T) { encodedProvingKeys := make([][2]*big.Int, 0) encodedProvingKeys = append(encodedProvingKeys, provingKey) - requestHash, err := coordinator.HashOfKey(context.Background(), encodedProvingKeys[0]) + requestHash, err := coordinator.HashOfKey(utils.TestContext(t), encodedProvingKeys[0]) require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") err = consumer.RequestRandomness(requestHash, big.NewInt(1)) require.NoError(t, err, "Requesting randomness shouldn't fail") @@ -108,7 +108,7 @@ func TestVRFBasic(t *testing.T) { jobRuns, err := chainlinkNodes[0].MustReadRunsByJob(job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - out, err := consumer.RandomnessOutput(context.Background()) + out, err := consumer.RandomnessOutput(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") // Checks that the job has actually run g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 697ae28ce3b..58cd147201e 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -2,7 +2,6 @@ package reorg //revive:disable:dot-imports import ( - "context" "fmt" "math/big" "testing" @@ -19,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -133,6 +132,8 @@ func TestAutomationReorg(t *testing.T) { } for name, registryVersion := range registryVersions { + name := name + registryVersion := registryVersion t.Run(name, func(t *testing.T) { t.Parallel() network := networks.MustGetSelectedNetworksFromEnv()[0] @@ -167,7 +168,7 @@ func TestAutomationReorg(t *testing.T) { // Register cleanup for any test t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -209,7 +210,7 @@ func TestAutomationReorg(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(it_utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") @@ -240,7 +241,7 @@ func TestAutomationReorg(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they reach 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(it_utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 10 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") @@ -250,13 +251,14 @@ func TestAutomationReorg(t *testing.T) { }, "5m", "1s").Should(gomega.Succeed()) l.Info().Msg("Upkeep performed during unstable chain, waiting for reorg to finish") - rc.WaitDepthReached() + err = rc.WaitDepthReached() + require.NoError(t, err) l.Info().Msg("Reorg finished, chain should be stable now. Expecting upkeeps to keep getting performed") gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they reach 20 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(it_utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 20 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") diff --git a/integration-tests/reorg/log_poller_maybe_reorg_test.go b/integration-tests/reorg/log_poller_maybe_reorg_test.go index 0176fdbbdd6..d319e39aa20 100644 --- a/integration-tests/reorg/log_poller_maybe_reorg_test.go +++ b/integration-tests/reorg/log_poller_maybe_reorg_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/accounts/abi" + logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" ) diff --git a/integration-tests/reorg/reorg_test.go b/integration-tests/reorg/reorg_test.go index f92becfa50a..d5fefdbc562 100644 --- a/integration-tests/reorg/reorg_test.go +++ b/integration-tests/reorg/reorg_test.go @@ -1,7 +1,6 @@ package reorg import ( - "context" "fmt" "math/big" "os" @@ -22,15 +21,16 @@ import ( mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/onsi/gomega" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -85,7 +85,7 @@ func CleanupReorgTest( if chainClient != nil { chainClient.GasStats().PrintStats() } - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.PanicLevel, chainClient) require.NoError(t, err, "Error tearing down environment") } @@ -221,7 +221,7 @@ func TestDirectRequestReorg(t *testing.T) { gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(context.Background()) + d, err := consumer.Data(it_utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") log.Debug().Int64("Data", d.Int64()).Msg("Found on chain") diff --git a/integration-tests/runner_helpers.go b/integration-tests/runner_helpers.go index 43268a703ac..def2ebdc1d4 100644 --- a/integration-tests/runner_helpers.go +++ b/integration-tests/runner_helpers.go @@ -122,7 +122,7 @@ func collectBranchesAndTags(results chan []string, errChan chan error) { go func() { stdOut, stdErr, err := gh.Exec("api", fmt.Sprintf("repos/%s/branches", chainlinkRepo), "-q", ".[][\"name\"]", "--paginate") if err != nil { - errChan <- fmt.Errorf("%v: %s", err, stdErr.String()) + errChan <- fmt.Errorf("%w: %s", err, stdErr.String()) } branches := strings.Split(stdOut.String(), "\n") cleanBranches := []string{} @@ -139,7 +139,7 @@ func collectBranchesAndTags(results chan []string, errChan chan error) { go func() { stdOut, stdErr, err := gh.Exec("api", fmt.Sprintf("repos/%s/tags", chainlinkRepo), "-q", ".[][\"name\"]", "--paginate") if err != nil { - errChan <- fmt.Errorf("%v: %s", err, stdErr.String()) + errChan <- fmt.Errorf("%w: %s", err, stdErr.String()) } tags := strings.Split(stdOut.String(), "\n") cleanTags := []string{} diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 4f969c5d68d..1a093a88159 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "encoding/json" "fmt" "math/big" @@ -32,7 +31,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var utilsABI = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) @@ -110,7 +109,6 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { upgradeImage string upgradeVersion string err error - testName = "basic-upkeep" ) if nodeUpgrade { upgradeImage = os.Getenv("UPGRADE_IMAGE") @@ -118,7 +116,6 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { if len(upgradeImage) == 0 || len(upgradeVersion) == 0 { t.Fatal("UPGRADE_IMAGE and UPGRADE_VERSION must be set to upgrade nodes") } - testName = "node-upgrade" } // Use the name to determine if this is a log trigger or mercury @@ -128,7 +125,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { isMercury := isMercuryV02 || isMercuryV03 chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupAutomationTestDocker( - t, testName, registryVersion, defaultOCRRegistryConfig, nodeUpgrade, isMercuryV02, isMercuryV03, + t, registryVersion, defaultOCRRegistryConfig, isMercuryV02, isMercuryV03, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -172,7 +169,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") @@ -187,13 +184,14 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { expect := 5 // Upgrade the nodes one at a time and check that the upkeeps are still being performed for i := 0; i < 5; i++ { - actions.UpgradeChainlinkNodeVersionsLocal(upgradeImage, upgradeVersion, testEnv.ClCluster.Nodes[i]) + err = actions.UpgradeChainlinkNodeVersionsLocal(upgradeImage, upgradeVersion, testEnv.ClCluster.Nodes[i]) + require.NoError(t, err, "Error when upgrading node %d", i) time.Sleep(time.Second * 10) expect = expect + 5 gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are increasing by 5 in each step within 5 minutes for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">=", int64(expect)), @@ -216,7 +214,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(context.Background()) + countersAfterCancellation[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeep Count", countersAfterCancellation[i].Int64()).Int("Upkeep Index", i).Msg("Cancelled upkeep") } @@ -225,7 +223,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant (At most increase by 1 to account for stale performs) because the upkeep was cancelled - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterCancellation[i].Int64()+1), "Expected consumer counter to remain less than or equal to %d, but got %d", @@ -241,7 +239,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { l := logging.GetTestLogger(t) chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "set-trigger-config", ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, false, false, false, + t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers( @@ -271,7 +269,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") @@ -328,7 +326,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { time.Sleep(10 * time.Second) for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterSetNoMatch[i], err = consumers[i].Counter(context.Background()) + countersAfterSetNoMatch[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeep Count", countersAfterSetNoMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") } @@ -338,7 +336,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant (At most increase by 2 to account for stale performs) because the upkeep trigger config is not met bufferCount := int64(2) - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterSetNoMatch[i].Int64()+bufferCount), "Expected consumer counter to remain less than or equal to %d, but got %d", @@ -374,7 +372,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterSetMatch[i], err = consumers[i].Counter(context.Background()) + countersAfterSetMatch[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeep Count", countersAfterSetMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") } @@ -393,7 +391,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := int64(5) l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") @@ -416,7 +414,7 @@ func TestAutomationAddFunds(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "add-funds", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(1), automationDefaultUpkeepGasLimit, false, false) @@ -424,7 +422,7 @@ func TestAutomationAddFunds(t *testing.T) { gom := gomega.NewGomegaWithT(t) // Since the upkeep is currently underfunded, check that it doesn't get executed gom.Consistently(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(context.Background()) + counter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), "Expected consumer counter to remain zero, but got %d", counter.Int64()) @@ -444,7 +442,7 @@ func TestAutomationAddFunds(t *testing.T) { // Now the new upkeep should be performing because we added enough funds gom.Eventually(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(context.Background()) + counter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -467,7 +465,7 @@ func TestAutomationPauseUnPause(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "pause-unpause", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) @@ -476,7 +474,7 @@ func TestAutomationPauseUnPause(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)), "Expected consumer counter to be greater than 5, but got %d", counter.Int64()) @@ -496,7 +494,7 @@ func TestAutomationPauseUnPause(t *testing.T) { var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterPause[i], err = consumers[i].Counter(context.Background()) + countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Upkeep Index", i).Int64("Upkeeps Performed", countersAfterPause[i].Int64()).Msg("Paused Upkeep") } @@ -505,7 +503,7 @@ func TestAutomationPauseUnPause(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // In most cases counters should remain constant, but there might be a straggling perform tx which // gets committed later and increases counter by 1 - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterPause[i].Int64()+1), "Expected consumer counter not have increased more than %d, but got %d", @@ -525,7 +523,7 @@ func TestAutomationPauseUnPause(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 + numbers of performing before pause for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", countersAfterPause[i].Int64()+1), "Expected consumer counter to be greater than %d, but got %d", countersAfterPause[i].Int64()+1, counter.Int64()) @@ -550,7 +548,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "register-upkeep", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) @@ -561,7 +559,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { // store the value of their initial counters in order to compare later on that the value increased. gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -581,7 +579,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { // Test that the newly registered upkeep is also performing. gom.Eventually(func(g gomega.Gomega) { - counter, err := newUpkeep.Counter(context.Background()) + counter, err := newUpkeep.Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling newly deployed upkeep's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -590,7 +588,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(context.Background()) + currentCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info(). @@ -621,7 +619,7 @@ func TestAutomationPauseRegistry(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "pause-registry", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) @@ -630,7 +628,7 @@ func TestAutomationPauseRegistry(t *testing.T) { // Observe that the upkeeps which are initially registered are performing gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d") @@ -646,7 +644,7 @@ func TestAutomationPauseRegistry(t *testing.T) { // Store how many times each upkeep performed once the registry was successfully paused var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterPause[i], err = consumers[i].Counter(context.Background()) + countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) } @@ -654,7 +652,7 @@ func TestAutomationPauseRegistry(t *testing.T) { // because they are no longer getting serviced gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterPause[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -679,7 +677,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) chainClient, chainlinkNodes, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "keeper-nodes-down", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) @@ -691,7 +689,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // Watch upkeeps being performed and store their counters in order to compare them later in the test gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -710,7 +708,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // Assert that upkeeps are still performed and their counters have increased gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(context.Background()) + currentCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", initialCounters[i].Int64()), "Expected counter to have increased from initial value of %s, but got %s", @@ -731,7 +729,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // See how many times each upkeep was executed var countersAfterNoMoreNodes = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterNoMoreNodes[i], err = consumers[i].Counter(context.Background()) + countersAfterNoMoreNodes[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Upkeep Index", i).Int64("Performed", countersAfterNoMoreNodes[i].Int64()).Msg("Upkeeps Performed") } @@ -740,7 +738,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // all the nodes were taken down gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterNoMoreNodes[i].Int64()+1), "Expected consumer counter to not have increased more than %d, but got %d", @@ -764,7 +762,7 @@ func TestAutomationPerformSimulation(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "perform-simulation", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumersPerformance, _ := actions.DeployPerformanceConsumers( @@ -789,7 +787,7 @@ func TestAutomationPerformSimulation(t *testing.T) { // Initially performGas is set high, so performUpkeep reverts and no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { // Consumer count should remain at 0 - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.Equal(int64(0)), "Expected consumer counter to remain constant at %d, but got %d", 0, cnt.Int64(), @@ -797,14 +795,14 @@ func TestAutomationPerformSimulation(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Set performGas on consumer to be low, so that performUpkeep starts becoming successful - err := consumerPerformance.SetPerformGasToBurn(context.Background(), big.NewInt(100000)) + err := consumerPerformance.SetPerformGasToBurn(utils.TestContext(t), big.NewInt(100000)) require.NoError(t, err, "Perform gas should be set successfully on consumer") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for set perform gas tx") // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -828,7 +826,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) chainClient, chainlinkNodes, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "gas-limit", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) consumersPerformance, upkeepIDs := actions.DeployPerformanceConsumers( @@ -854,7 +852,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Initially performGas is set higher than defaultUpkeepGasLimit, so no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.Equal(int64(0)), @@ -870,7 +868,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -878,19 +876,19 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m to perform once, 1m buffer // Now increase the checkGasBurn on consumer, upkeep should stop performing - err = consumerPerformance.SetCheckGasToBurn(context.Background(), big.NewInt(3000000)) + err = consumerPerformance.SetCheckGasToBurn(utils.TestContext(t), big.NewInt(3000000)) require.NoError(t, err, "Check gas burn should be set successfully on consumer") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetCheckGasToBurn tx") // Get existing performed count - existingCnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + existingCnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) require.NoError(t, err, "Calling consumer's counter shouldn't fail") l.Info().Int64("Upkeep counter", existingCnt.Int64()).Msg("Upkeep counter when check gas increased") // In most cases count should remain constant, but it might increase by upto 1 due to pending perform gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.BeNumerically("<=", existingCnt.Int64()+1), @@ -898,7 +896,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { ) }, "1m", "1s").Should(gomega.Succeed()) - existingCnt, err = consumerPerformance.GetUpkeepCount(context.Background()) + existingCnt, err = consumerPerformance.GetUpkeepCount(utils.TestContext(t)) require.NoError(t, err, "Calling consumer's counter shouldn't fail") existingCntInt := existingCnt.Int64() l.Info().Int64("Upkeep counter", existingCntInt).Msg("Upkeep counter when consistently block finished") @@ -918,7 +916,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Upkeep should start performing again, and it should get regularly performed gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", existingCntInt), "Expected consumer counter to be greater than %d, but got %d", existingCntInt, cnt.Int64(), @@ -942,7 +940,7 @@ func TestUpdateCheckData(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, "update-check-data", registryVersion, defaultOCRRegistryConfig, false, false, false, + t, registryVersion, defaultOCRRegistryConfig, false, false, ) performDataChecker, upkeepIDs := actions.DeployPerformDataCheckerConsumers( @@ -962,7 +960,7 @@ func TestUpdateCheckData(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { // expect the counter to remain 0 because perform data does not match for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(context.Background()) + counter, err := performDataChecker[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker"+ " for upkeep at index "+strconv.Itoa(i)) g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), @@ -981,7 +979,7 @@ func TestUpdateCheckData(t *testing.T) { // retrieve new check data for all upkeeps for i := 0; i < len(upkeepIDs); i++ { - upkeep, err := registry.GetUpkeepInfo(context.Background(), upkeepIDs[i]) + upkeep, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepIDs[i]) require.NoError(t, err, "Failed to get upkeep info at index %d", i) require.Equal(t, []byte(automationExpectedData), upkeep.CheckData, "Upkeep data not as expected") } @@ -989,7 +987,7 @@ func TestUpdateCheckData(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(context.Background()) + counter, err := performDataChecker[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker counter"+ " for upkeep at index "+strconv.Itoa(i)) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -1007,10 +1005,8 @@ type TestConfig struct { func setupAutomationTestDocker( t *testing.T, - testName string, registryVersion ethereum.KeeperRegistryVersion, registryConfig contracts.KeeperRegistrySettings, - statefulDb bool, isMercuryV02 bool, isMercuryV03 bool, ) ( @@ -1032,11 +1028,11 @@ func setupAutomationTestDocker( // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) syncInterval := models.MustMakeDuration(5 * time.Minute) - clNodeConfig.Feature.LogPoller = it_utils.Ptr[bool](true) - clNodeConfig.OCR2.Enabled = it_utils.Ptr[bool](true) - clNodeConfig.Keeper.TurnLookBack = it_utils.Ptr[int64](int64(0)) + clNodeConfig.Feature.LogPoller = utils.Ptr[bool](true) + clNodeConfig.OCR2.Enabled = utils.Ptr[bool](true) + clNodeConfig.Keeper.TurnLookBack = utils.Ptr[int64](int64(0)) clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval - clNodeConfig.Keeper.Registry.PerformGasOverhead = it_utils.Ptr[uint32](uint32(150000)) + clNodeConfig.Keeper.Registry.PerformGasOverhead = utils.Ptr[uint32](uint32(150000)) clNodeConfig.P2P.V2.AnnounceAddresses = &[]string{"0.0.0.0:6690"} clNodeConfig.P2P.V2.ListenAddresses = &[]string{"0.0.0.0:6690"} @@ -1086,11 +1082,13 @@ func setupAutomationTestDocker( if isMercuryV02 { output := `{"chainlinkBlob":"0x0001c38d71fed6c320b90e84b6f559459814d068e2a1700adc931ca9717d4fe70000000000000000000000000000000000000000000000000000000001a80b52b4bf1233f9cb71144a253a1791b202113c4ab4a92fa1b176d684b4959666ff8200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001004254432d5553442d415242495452554d2d544553544e4554000000000000000000000000000000000000000000000000000000000000000000000000645570be000000000000000000000000000000000000000000000000000002af2b818dc5000000000000000000000000000000000000000000000000000002af2426faf3000000000000000000000000000000000000000000000000000002af32dc209700000000000000000000000000000000000000000000000000000000012130f8df0a9745bb6ad5e2df605e158ba8ad8a33ef8a0acf9851f0f01668a3a3f2b68600000000000000000000000000000000000000000000000000000000012130f60000000000000000000000000000000000000000000000000000000000000002c4a7958dce105089cf5edb68dad7dcfe8618d7784eb397f97d5a5fade78c11a58275aebda478968e545f7e3657aba9dcbe8d44605e4c6fde3e24edd5e22c94270000000000000000000000000000000000000000000000000000000000000002459c12d33986018a8959566d145225f0c4a4e61a9a3f50361ccff397899314f0018162cf10cd89897635a0bb62a822355bd199d09f4abe76e4d05261bb44733d"}` - env.MockAdapter.SetStringValuePath("/client", []string{http.MethodGet, http.MethodPost}, map[string]string{"Content-Type": "application/json"}, output) + err = env.MockAdapter.SetStringValuePath("/client", []string{http.MethodGet, http.MethodPost}, map[string]string{"Content-Type": "application/json"}, output) + require.NoError(t, err) } if isMercuryV03 { output := `{"reports":[{"feedID":"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000","validFromTimestamp":0,"observationsTimestamp":0,"fullReport":"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}]}` - env.MockAdapter.SetStringValuePath("/api/v1/reports/bulk", []string{http.MethodGet, http.MethodPost}, map[string]string{"Content-Type": "application/json"}, output) + err = env.MockAdapter.SetStringValuePath("/api/v1/reports/bulk", []string{http.MethodGet, http.MethodPost}, map[string]string{"Content-Type": "application/json"}, output) + require.NoError(t, err) } } else { env, err = test_env.NewCLTestEnvBuilder(). diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 8c2b3638bff..2997ff1c74a 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "net/http" @@ -19,6 +18,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestFluxBasic(t *testing.T) { @@ -74,7 +74,7 @@ func TestFluxBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - oracles, err := fluxInstance.GetOracles(context.Background()) + oracles, err := fluxInstance.GetOracles(utils.TestContext(t)) require.NoError(t, err, "Getting oracle details from the Flux aggregator contract shouldn't fail") l.Info().Str("Oracles", strings.Join(oracles, ",")).Msg("Oracles set") @@ -108,7 +108,7 @@ func TestFluxBasic(t *testing.T) { env.EVMClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err := fluxInstance.GetContractData(context.Background()) + data, err := fluxInstance.GetContractData(utils.TestContext(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") require.Equal(t, int64(1e5), data.LatestRoundData.Answer.Int64(), "Expected latest round answer to be %d, but found %d", int64(1e5), data.LatestRoundData.Answer.Int64()) @@ -127,7 +127,7 @@ func TestFluxBasic(t *testing.T) { require.NoError(t, err, "Setting value path in mock server shouldn't fail") err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err = fluxInstance.GetContractData(context.Background()) + data, err = fluxInstance.GetContractData(utils.TestContext(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") require.Equal(t, int64(1e10), data.LatestRoundData.Answer.Int64(), "Expected latest round answer to be %d, but found %d", int64(1e10), data.LatestRoundData.Answer.Int64()) @@ -140,7 +140,7 @@ func TestFluxBasic(t *testing.T) { l.Info().Interface("data", data).Msg("Round data") for _, oracleAddr := range nodeAddresses { - payment, _ := fluxInstance.WithdrawablePayment(context.Background(), oracleAddr) + payment, _ := fluxInstance.WithdrawablePayment(utils.TestContext(t), oracleAddr) require.Equal(t, int64(2), payment.Int64(), "Expected flux aggregator contract's withdrawable payment to be %d, but found %d", int64(2), payment.Int64()) } diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 727b83a601a..7203e031780 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "math/big" "testing" @@ -12,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestForwarderOCRBasic(t *testing.T) { @@ -72,7 +72,7 @@ func TestForwarderOCRBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - answer, err := ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -83,7 +83,7 @@ func TestForwarderOCRBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - answer, err = ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index baa5a781f6b..be87eb56292 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "net/http" @@ -17,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestForwarderOCR2Basic(t *testing.T) { @@ -92,7 +92,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.StartNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Getting latest answer from OCRv2 contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCRw contract to be 5 but got %d", answer.Int64()) @@ -103,7 +103,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.StartNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Error getting latest OCRv2 answer") require.Equal(t, int64(ocrRoundVal), answer.Int64(), fmt.Sprintf("Expected latest answer from OCRv2 contract to be %d but got %d", ocrRoundVal, answer.Int64())) } diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index d42944fd558..b28ab1ff101 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "strconv" @@ -23,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -109,7 +109,7 @@ func TestKeeperBasicSmoke(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) @@ -131,7 +131,7 @@ func TestKeeperBasicSmoke(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(context.Background()) + countersAfterCancellation[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Index", i).Int64("Upkeeps Performed", countersAfterCancellation[i].Int64()).Msg("Cancelled Upkeep") } @@ -139,7 +139,7 @@ func TestKeeperBasicSmoke(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant because the upkeep was cancelled, so it shouldn't increase anymore - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterCancellation[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -187,11 +187,11 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { // Wait for upkeep to be performed twice by different keepers (buddies) gom.Eventually(func(g gomega.Gomega) error { - counter, err := consumers[0].Counter(context.Background()) + counter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Number of upkeeps performed") - upkeepInfo, err := registry.GetUpkeepInfo(context.Background(), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -205,7 +205,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { }, "1m", "1s").Should(gomega.Succeed()) gom.Eventually(func(g gomega.Gomega) error { - upkeepInfo, err := registry.GetUpkeepInfo(context.Background(), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -219,7 +219,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { // Expect no new keepers to perform for a while gom.Consistently(func(g gomega.Gomega) { - upkeepInfo, err := registry.GetUpkeepInfo(context.Background(), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -235,11 +235,11 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { // Expect a new keeper to perform gom.Eventually(func(g gomega.Gomega) error { - counter, err := consumers[0].Counter(context.Background()) + counter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Num upkeeps performed") - upkeepInfo, err := registry.GetUpkeepInfo(context.Background(), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -296,7 +296,7 @@ func TestKeeperSimulation(t *testing.T) { // Initially performGas is set high, so performUpkeep reverts and no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { // Consumer count should remain at 0 - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.Equal(int64(0)), @@ -304,20 +304,20 @@ func TestKeeperSimulation(t *testing.T) { ) // Not even reverted upkeeps should be performed. Last keeper for the upkeep should be 0 address - upkeepInfo, err := registry.GetUpkeepInfo(context.Background(), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") g.Expect(upkeepInfo.LastKeeper).Should(gomega.Equal(actions.ZeroAddress.String()), "Last keeper should be zero address") }, "1m", "1s").Should(gomega.Succeed()) // Set performGas on consumer to be low, so that performUpkeep starts becoming successful - err = consumerPerformance.SetPerformGasToBurn(context.Background(), big.NewInt(100000)) + err = consumerPerformance.SetPerformGasToBurn(utils.TestContext(t), big.NewInt(100000)) require.NoError(t, err, "Error setting PerformGasToBurn") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting to set PerformGasToBurn") // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) error { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -368,7 +368,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // Initially performGas is set higher than defaultUpkeepGasLimit, so no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.Equal(int64(0)), @@ -384,7 +384,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) error { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -393,13 +393,13 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { }, "1m", "1s").Should(gomega.Succeed()) // Now increase the checkGasBurn on consumer, upkeep should stop performing - err = consumerPerformance.SetCheckGasToBurn(context.Background(), big.NewInt(3000000)) + err = consumerPerformance.SetCheckGasToBurn(utils.TestContext(t), big.NewInt(3000000)) require.NoError(t, err, "Error setting CheckGasToBurn") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetCheckGasToBurn tx") // Get existing performed count - existingCnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + existingCnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) require.NoError(t, err, "Error calling consumer's counter") l.Info().Int64("Upkeep counter", existingCnt.Int64()).Msg("Check Gas Increased") @@ -407,7 +407,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // gets committed later. Since every keeper node cannot have more than 1 straggling tx, it // is sufficient to check that the upkeep count does not increase by more than 6. gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.BeNumerically("<=", existingCnt.Int64()+numUpkeepsAllowedForStragglingTxs), @@ -415,7 +415,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { ) }, "3m", "1s").Should(gomega.Succeed()) - existingCnt, err = consumerPerformance.GetUpkeepCount(context.Background()) + existingCnt, err = consumerPerformance.GetUpkeepCount(utils.TestContext(t)) require.NoError(t, err, "Error calling consumer's counter") existingCntInt := existingCnt.Int64() l.Info().Int64("Upkeep counter", existingCntInt).Msg("Upkeep counter when consistently block finished") @@ -430,7 +430,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // Upkeep should start performing again, and it should get regularly performed gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(context.Background()) + cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", existingCntInt), "Expected consumer counter to be greater than %d, but got %d", existingCntInt, cnt.Int64(), @@ -478,7 +478,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { // store the value of their initial counters in order to compare later on that the value increased. gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter"+ " for upkeep at index "+strconv.Itoa(i)) @@ -500,7 +500,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { // Test that the newly registered upkeep is also performing. gom.Eventually(func(g gomega.Gomega) error { - counter, err := newUpkeep.Counter(context.Background()) + counter, err := newUpkeep.Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling newly deployed upkeep's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -510,7 +510,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(context.Background()) + currentCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info(). @@ -563,7 +563,7 @@ func TestKeeperAddFunds(t *testing.T) { // Since the upkeep is currently underfunded, check that it doesn't get executed gom.Consistently(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(context.Background()) + counter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), "Expected consumer counter to remain zero, but got %d", counter.Int64()) @@ -583,7 +583,7 @@ func TestKeeperAddFunds(t *testing.T) { // Now the new upkeep should be performing because we added enough funds gom.Eventually(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(context.Background()) + counter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -628,7 +628,7 @@ func TestKeeperRemove(t *testing.T) { // Make sure the upkeeps are running before we remove a keeper gom.Eventually(func(g gomega.Gomega) error { for upkeepID := 0; upkeepID < len(upkeepIDs); upkeepID++ { - counter, err := consumers[upkeepID].Counter(context.Background()) + counter, err := consumers[upkeepID].Counter(utils.TestContext(t)) initialCounters[upkeepID] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter"+ " for upkeep with ID "+strconv.Itoa(upkeepID)) @@ -637,7 +637,7 @@ func TestKeeperRemove(t *testing.T) { return nil }, "1m", "1s").Should(gomega.Succeed()) - keepers, err := registry.GetKeeperList(context.Background()) + keepers, err := registry.GetKeeperList(utils.TestContext(t)) require.NoError(t, err, "Error getting list of Keepers") // Remove the first keeper from the list @@ -660,7 +660,7 @@ func TestKeeperRemove(t *testing.T) { // The upkeeps should still perform and their counters should have increased compared to the first check gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Cmp(initialCounters[i]) == 1, "Expected consumer counter to be greater "+ "than initial counter which was %s, but got %s", initialCounters[i], counter) @@ -705,7 +705,7 @@ func TestKeeperPauseRegistry(t *testing.T) { // Observe that the upkeeps which are initially registered are performing gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d") @@ -722,7 +722,7 @@ func TestKeeperPauseRegistry(t *testing.T) { // Store how many times each upkeep performed once the registry was successfully paused var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterPause[i], err = consumers[i].Counter(context.Background()) + countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Error retrieving consumer at index %d", i) } @@ -730,7 +730,7 @@ func TestKeeperPauseRegistry(t *testing.T) { // because they are no longer getting serviced gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Error retrieving consumer contract at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterPause[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -791,7 +791,7 @@ func TestKeeperMigrateRegistry(t *testing.T) { // Check that the first upkeep from the first registry is performing (before being migrated) gom.Eventually(func(g gomega.Gomega) error { - counterBeforeMigration, err := consumers[0].Counter(context.Background()) + counterBeforeMigration, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counterBeforeMigration.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %s", counterBeforeMigration) @@ -810,12 +810,12 @@ func TestKeeperMigrateRegistry(t *testing.T) { err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting to pause first registry") - counterAfterMigration, err := consumers[0].Counter(context.Background()) + counterAfterMigration, err := consumers[0].Counter(utils.TestContext(t)) require.NoError(t, err, "Error calling consumer's counter") // Check that once we migrated the upkeep, the counter has increased gom.Eventually(func(g gomega.Gomega) error { - currentCounter, err := consumers[0].Counter(context.Background()) + currentCounter, err := consumers[0].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", counterAfterMigration.Int64()), "Expected counter to have increased, but stayed constant at %s", counterAfterMigration) @@ -860,7 +860,7 @@ func TestKeeperNodeDown(t *testing.T) { // Watch upkeeps being performed and store their counters in order to compare them later in the test gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -882,7 +882,7 @@ func TestKeeperNodeDown(t *testing.T) { // Assert that upkeeps are still performed and their counters have increased gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(context.Background()) + currentCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", initialCounters[i].Int64()), "Expected counter to have increased from initial value of %s, but got %s", @@ -908,7 +908,7 @@ func TestKeeperNodeDown(t *testing.T) { // See how many times each upkeep was executed var countersAfterNoMoreNodes = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterNoMoreNodes[i], err = consumers[i].Counter(context.Background()) + countersAfterNoMoreNodes[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Error retrieving consumer counter %d", i) l.Info(). Int("Index", i). @@ -921,7 +921,7 @@ func TestKeeperNodeDown(t *testing.T) { // so a +6 on the upper limit side should be sufficient. gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterNoMoreNodes[i].Int64()+numUpkeepsAllowedForStragglingTxs, @@ -964,7 +964,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)), "Expected consumer counter to be greater than 5, but got %d", counter.Int64()) @@ -985,7 +985,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterPause[i], err = consumers[i].Counter(context.Background()) + countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Error retrieving upkeep count at index %d", i) l.Info(). Int("Index", i). @@ -998,7 +998,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { // In most cases counters should remain constant, but there might be a straggling perform tx which // gets committed later. Since every keeper node cannot have more than 1 straggling tx, it // is sufficient to check that the upkeep count does not increase by more than 6. - latestCounter, err := consumers[i].Counter(context.Background()) + latestCounter, err := consumers[i].Counter(utils.TestContext(t)) require.NoError(t, err, "Error retrieving counter at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterPause[i].Int64()+numUpkeepsAllowedForStragglingTxs), "Expected consumer counter not have increased more than %d, but got %d", @@ -1018,7 +1018,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 + numbers of performing before pause for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(context.Background()) + counter, err := consumers[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter"+ " for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)+countersAfterPause[i].Int64()), @@ -1055,7 +1055,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { // expect the counter to remain 0 because perform data does not match for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(context.Background()) + counter, err := performDataChecker[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), "Expected perform data checker counter to be 0, but got %d", counter.Int64()) @@ -1073,7 +1073,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { // retrieve new check data for all upkeeps for i := 0; i < len(upkeepIDs); i++ { - upkeep, err := registry.GetUpkeepInfo(context.Background(), upkeepIDs[i]) + upkeep, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepIDs[i]) require.NoError(t, err, "Error getting upkeep info from index %d", i) require.Equal(t, []byte(keeperExpectedData), upkeep.CheckData, "Check data not as expected") } @@ -1081,7 +1081,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(context.Background()) + counter, err := performDataChecker[i].Counter(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)), "Expected perform data checker counter to be greater than 5, but got %d", counter.Int64()) diff --git a/integration-tests/smoke/log_poller_test.go b/integration-tests/smoke/log_poller_test.go index 36ee2164c45..03a287ee6b7 100644 --- a/integration-tests/smoke/log_poller_test.go +++ b/integration-tests/smoke/log_poller_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/accounts/abi" + logpoller "github.com/smartcontractkit/chainlink/integration-tests/universal/log_poller" ) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index a6dcdcd139d..5950e9febb6 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "net/http" @@ -16,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // Tests a basic OCRv2 median feed @@ -73,7 +73,7 @@ func TestOCRv2Basic(t *testing.T) { err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(context.Background(), big.NewInt(1)) + roundData, err := aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", @@ -85,7 +85,7 @@ func TestOCRv2Basic(t *testing.T) { err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) - roundData, err = aggregatorContracts[0].GetRound(context.Background(), big.NewInt(2)) + roundData, err = aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(2)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 912c121d075..57bd5412b14 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "strings" @@ -16,7 +15,6 @@ import ( eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/ocr2vrf_actions" @@ -24,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCR2VRFRedeemModel(t *testing.T) { @@ -45,7 +44,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { require.NoError(t, err, "Retreiving on-chain wallet addresses for chainlink nodes shouldn't fail") t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.ErrorLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.ErrorLevel, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -81,7 +80,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(context.Background(), requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) require.NoError(t, err) l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") @@ -107,7 +106,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { require.NoError(t, err, "Retreiving on-chain wallet addresses for chainlink nodes shouldn't fail") t.Cleanup(func() { - err := actions.TeardownSuite(t, testEnvironment, utils.ProjectRoot, chainlinkNodes, nil, zapcore.ErrorLevel, chainClient) + err := actions.TeardownSuite(t, testEnvironment, chainlinkNodes, nil, zapcore.ErrorLevel, chainClient) require.NoError(t, err, "Error tearing down environment") }) @@ -142,7 +141,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(context.Background(), requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) require.NoError(t, err, "Error getting Randomness result from Consumer Contract") l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness Fulfillment retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness Fulfillment retrieved from Consumer contract give an answer other than 0") diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 8952f00d768..45205565e21 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "math/big" "testing" @@ -11,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCRBasic(t *testing.T) { @@ -46,7 +46,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -55,7 +55,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(context.Background()) + answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index f29cb4bc893..20389da378f 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "net/http" @@ -16,6 +15,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestRunLogBasic(t *testing.T) { @@ -87,7 +87,7 @@ func TestRunLogBasic(t *testing.T) { gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(context.Background()) + d, err := consumer.Data(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") l.Debug().Int64("Data", d.Int64()).Msg("Found on chain") diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 444d1ce20ee..61d2c5cdd70 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "testing" @@ -17,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv1" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFBasic(t *testing.T) { @@ -81,7 +81,7 @@ func TestVRFBasic(t *testing.T) { encodedProvingKeys := make([][2]*big.Int, 0) encodedProvingKeys = append(encodedProvingKeys, provingKey) - requestHash, err := contracts.Coordinator.HashOfKey(context.Background(), encodedProvingKeys[0]) + requestHash, err := contracts.Coordinator.HashOfKey(utils.TestContext(t), encodedProvingKeys[0]) require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") err = contracts.Consumer.RequestRandomness(requestHash, big.NewInt(1)) require.NoError(t, err, "Requesting randomness shouldn't fail") @@ -92,7 +92,7 @@ func TestVRFBasic(t *testing.T) { jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - out, err := contracts.Consumer.RandomnessOutput(context.Background()) + out, err := contracts.Consumer.RandomnessOutput(utils.TestContext(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") // Checks that the job has actually run g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index c960bb6c691..714ed752a36 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "math/big" "testing" "time" @@ -16,6 +15,7 @@ import ( vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFv2Basic(t *testing.T) { @@ -97,11 +97,11 @@ func TestVRFv2Basic(t *testing.T) { jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfV2jobs[0].Job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically("==", 1)) - lastRequestID, err = vrfv2Contracts.LoadTestConsumer.GetLastRequestId(context.Background()) + lastRequestID, err = vrfv2Contracts.LoadTestConsumer.GetLastRequestId(utils.TestContext(t)) l.Debug().Interface("Last Request ID", lastRequestID).Msg("Last Request ID Received") g.Expect(err).ShouldNot(gomega.HaveOccurred()) - status, err := vrfv2Contracts.LoadTestConsumer.GetRequestStatus(context.Background(), lastRequestID) + status, err := vrfv2Contracts.LoadTestConsumer.GetRequestStatus(utils.TestContext(t), lastRequestID) g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(status.Fulfilled).Should(gomega.BeTrue()) l.Debug().Interface("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 3510a1505a7..cfeca0a66a3 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -1,7 +1,6 @@ package smoke import ( - "context" "fmt" "math/big" "testing" @@ -55,7 +54,7 @@ func TestVRFv2Plus(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) @@ -83,7 +82,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := subscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) @@ -92,7 +91,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -125,7 +124,7 @@ func TestVRFv2Plus(t *testing.T) { ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceWei := new(big.Int).Sub(subNativeTokenBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err) subBalanceAfterRequest := subscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) @@ -134,7 +133,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -162,10 +161,10 @@ func TestVRFv2Plus(t *testing.T) { testConfig := vrfv2PlusConfig var isNativeBilling = false - wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(context.Background(), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(utils.TestContext(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.Balance @@ -182,18 +181,18 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) - wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(context.Background(), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(utils.TestContext(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) @@ -211,10 +210,10 @@ func TestVRFv2Plus(t *testing.T) { testConfig := vrfv2PlusConfig var isNativeBilling = true - wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.NativeBalance @@ -231,18 +230,18 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceWei := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), wrapperSubID) + wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) expectedWrapperConsumerWeiBalance := new(big.Int).Sub(wrapperConsumerBalanceBeforeRequestWei, consumerStatus.Paid) - wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerWeiBalance, wrapperConsumerBalanceAfterRequestWei) @@ -273,13 +272,13 @@ func TestVRFv2Plus(t *testing.T) { testWalletAddress, err := actions.GenerateWallet() require.NoError(t, err) - testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), testWalletAddress) + testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), testWalletAddress) require.NoError(t, err) - testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(context.Background(), testWalletAddress.String()) + testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), testWalletAddress.String()) require.NoError(t, err) - subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -318,14 +317,14 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), testWalletAddress) + testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), testWalletAddress) require.NoError(t, err) - testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(context.Background(), testWalletAddress.String()) + testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), testWalletAddress.String()) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") subFundsReturnedNativeActual := new(big.Int).Sub(testWalletBalanceNativeAfterSubCancelling, testWalletBalanceNativeBeforeSubCancelling) @@ -367,17 +366,17 @@ func TestVRFv2Plus(t *testing.T) { subIDForCancelling := subIDsForCancelling[0] - subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2PlusContracts.Coordinator) - activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err) require.True(t, utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subIDForCancelling)) - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subIDForCancelling) + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(utils.TestContext(t), subIDForCancelling) require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") @@ -409,17 +408,17 @@ func TestVRFv2Plus(t *testing.T) { require.Error(t, err, "error should occur for waiting for fulfilment due to low sub balance") - pendingRequestsExist, err = vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subIDForCancelling) + pendingRequestsExist, err = vrfv2PlusContracts.Coordinator.PendingRequestsExist(utils.TestContext(t), subIDForCancelling) require.NoError(t, err) require.True(t, pendingRequestsExist, "Pending requests should exist after unfulfilled rand requests due to low sub balance") - walletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + walletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) require.NoError(t, err) - subscriptionForCancelling, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + subscriptionForCancelling, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -458,14 +457,14 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subIDForCancelling) + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) fmt.Println("err", err) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") @@ -494,7 +493,7 @@ func TestVRFv2Plus(t *testing.T) { //require.Equal(t, subFundsReturnedNativeExpected, subFundsReturnedNativeActual, "Returned funds are not equal to sub balance that was cancelled") require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") - activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error getting active subscription ids") require.False( @@ -543,10 +542,10 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) amountToWithdrawLink := fulfilledEventLink.Payment - defaultWalletBalanceNativeBeforeOracleWithdraw, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeBeforeOracleWithdraw, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) require.NoError(t, err) l.Info(). @@ -575,10 +574,10 @@ func TestVRFv2Plus(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) - defaultWalletBalanceNativeAfterOracleWithdraw, err := env.EVMClient.BalanceAt(context.Background(), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeAfterOracleWithdraw, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(context.Background(), defaultWalletAddress) + defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) require.NoError(t, err) //not possible to verify exact amount of Native/LINK returned as defaultWallet is used in other tests in parallel which might affect the balance @@ -618,17 +617,17 @@ func TestVRFv2PlusMigration(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) - activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error occurred getting active sub ids") require.Len(t, activeSubIdsOldCoordinatorBeforeMigration, 1, "Active Sub Ids length is not equal to 1") require.Equal(t, subID, activeSubIdsOldCoordinatorBeforeMigration[0]) - oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err, "error getting subscription information") //Migration Process @@ -699,14 +698,14 @@ func TestVRFv2PlusMigration(t *testing.T) { migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) require.NoError(t, err) - migratedSubscription, err := newCoordinator.GetSubscription(context.Background(), subID) + migratedSubscription, err := newCoordinator.GetSubscription(utils.TestContext(t), subID) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetailsAfterMigration(l, newCoordinator, subID, migratedSubscription) //Verify that Coordinators were updated in Consumers for _, consumer := range vrfv2PlusContracts.LoadTestConsumers { - coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(context.Background()) + coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(utils.TestContext(t)) require.NoError(t, err, "error getting Coordinator from Consumer contract") require.Equal(t, newCoordinator.Address(), coordinatorAddressInConsumerAfterMigration.String()) l.Debug(). @@ -722,13 +721,13 @@ func TestVRFv2PlusMigration(t *testing.T) { require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) //Verify that old sub was deleted from old Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(context.Background(), subID) + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") - _, err = vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + _, err = vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) require.Error(t, err, "error not occurred getting active sub ids. Should occur since it should revert when sub id array is empty") - activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(context.Background(), big.NewInt(0), big.NewInt(0)) + activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error occurred getting active sub ids") require.Len(t, activeSubIdsMigratedCoordinator, 1, "Active Sub Ids length is not equal to 1 for Migrated Coordinator after migration") require.Equal(t, subID, activeSubIdsMigratedCoordinator[0]) diff --git a/integration-tests/testreporters/keeper_benchmark.go b/integration-tests/testreporters/keeper_benchmark.go index c800eb37be2..e9f2eaad7c5 100644 --- a/integration-tests/testreporters/keeper_benchmark.go +++ b/integration-tests/testreporters/keeper_benchmark.go @@ -183,7 +183,7 @@ func (k *KeeperBenchmarkTestReporter) WriteReport(folderLocation string) error { } for contractIndex, report := range k.Reports { - avg, median, ninetyPct, ninetyNinePct, max := intListStats(report.AllCheckDelays) + avg, median, ninetyPct, ninetyNinePct, max = intListStats(report.AllCheckDelays) err = keeperReportWriter.Write([]string{ fmt.Sprint(contractIndex), report.RegistryAddress, @@ -305,6 +305,8 @@ func (k *KeeperBenchmarkTestReporter) SendSlackNotification(t *testing.T, slackC } // intListStats helper calculates some statistics on an int list: avg, median, 90pct, 99pct, max +// +//nolint:revive func intListStats(in []int64) (float64, int64, int64, int64, int64) { length := len(in) if length == 0 { diff --git a/integration-tests/testreporters/ocr.go b/integration-tests/testreporters/ocr.go index a04718ea228..abbb261fa74 100644 --- a/integration-tests/testreporters/ocr.go +++ b/integration-tests/testreporters/ocr.go @@ -67,9 +67,7 @@ func (e *OCRRoundState) Time() time.Time { // CSV returns a CSV representation of the test state and all events func (e *OCRRoundState) CSV() [][]string { rows := [][]string{{e.StartTime.Format("2006-01-02 15:04:05.00 MST"), fmt.Sprintf("Expecting new Answer: %d", e.Answer)}} - for _, anomaly := range e.anomalies { - rows = append(rows, anomaly) - } + rows = append(rows, e.anomalies...) return rows } diff --git a/integration-tests/testreporters/profile.go b/integration-tests/testreporters/profile.go index 9ac7713e94d..ab9dec138e4 100644 --- a/integration-tests/testreporters/profile.go +++ b/integration-tests/testreporters/profile.go @@ -54,7 +54,7 @@ func (c *ChainlinkProfileTestReporter) WriteReport(folderLocation string) error } // SendNotification hasn't been implemented for this test -func (c *ChainlinkProfileTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { +func (c *ChainlinkProfileTestReporter) SendSlackNotification(_ *testing.T, _ *slack.Client) error { log.Warn().Msg("No Slack notification integration for Chainlink profile tests") return nil } diff --git a/integration-tests/testreporters/vrfv2plus.go b/integration-tests/testreporters/vrfv2plus.go index 83d4678dfdd..38220ca8821 100644 --- a/integration-tests/testreporters/vrfv2plus.go +++ b/integration-tests/testreporters/vrfv2plus.go @@ -2,12 +2,13 @@ package testreporters import ( "fmt" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "math/big" "os" "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" + "github.com/slack-go/slack" "github.com/smartcontractkit/chainlink-testing-framework/testreporters" @@ -53,7 +54,7 @@ func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient headerText = fmt.Sprintf(":x: VRF %s Test FAILED :x:", o.TestType) } - messageBlocks := testreporters.SlackNotifyBlocks(headerText, fmt.Sprintf("%s", os.Getenv("SELECTED_NETWORKS")), []string{ + messageBlocks := testreporters.SlackNotifyBlocks(headerText, os.Getenv("SELECTED_NETWORKS"), []string{ fmt.Sprintf( "Summary\n"+ "Perf Test Type: %s\n"+ diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index f786cca9bb5..bb6c582c137 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -37,6 +37,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // KeeperBenchmarkTest builds a test to check that chainlink nodes are able to upkeep a specified amount of Upkeep @@ -229,7 +230,7 @@ func (k *KeeperBenchmarkTest) Run() { "NumberOfRegistries": len(k.keeperRegistries), } inputs := k.Inputs - startingBlock, err := k.chainClient.LatestBlockNumber(context.Background()) + startingBlock, err := k.chainClient.LatestBlockNumber(utils.TestContext(k.t)) require.NoError(k.t, err, "Error getting latest block number") k.startingBlock = big.NewInt(0).SetUint64(startingBlock) startTime := time.Now() @@ -305,7 +306,7 @@ func (k *KeeperBenchmarkTest) Run() { err = fmt.Errorf("initial error") // to ensure our for loop runs at least once ) for err != nil { // This RPC call can possibly time out or otherwise die. Failure is not an option, keep retrying to get our stats. - ctx, cancel := context.WithTimeout(context.Background(), timeout) + ctx, cancel := context.WithTimeout(utils.TestContext(k.t), timeout) logs, err = k.chainClient.FilterLogs(ctx, filterQuery) cancel() if err != nil { @@ -407,12 +408,13 @@ func (k *KeeperBenchmarkTest) observeUpkeepEvents() { FromBlock: k.startingBlock, } - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(utils.TestContext(k.t), 5*time.Second) sub, err := k.chainClient.SubscribeFilterLogs(ctx, filterQuery, eventLogs) cancel() require.NoError(k.t, err, "Subscribing to upkeep performed events log shouldn't fail") interruption := make(chan os.Signal, 1) + //nolint:staticcheck //ignore SA1016 we need to send the os.Kill signal signal.Notify(interruption, os.Kill, os.Interrupt, syscall.SIGTERM) go func() { @@ -429,7 +431,7 @@ func (k *KeeperBenchmarkTest) observeUpkeepEvents() { Str("Backoff", backoff.String()). Msg("Error while subscribing to Keeper Event Logs. Resubscribing...") - ctx, cancel := context.WithTimeout(context.Background(), backoff) + ctx, cancel := context.WithTimeout(utils.TestContext(k.t), backoff) sub, err = k.chainClient.SubscribeFilterLogs(ctx, filterQuery, eventLogs) cancel() if err != nil { diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index ee8116f3f99..3fb9dd9844a 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -42,6 +42,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -163,7 +164,7 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { } // LoadEnvironment loads an existing test environment using the provided URLs -func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, chainURL, mockServerURL string) { +func (o *OCRSoakTest) LoadEnvironment(chainlinkURLs []string, mockServerURL string) { var ( network = networks.MustGetSelectedNetworksFromEnv()[0] err error @@ -241,7 +242,6 @@ func (o *OCRSoakTest) Setup() { o.Inputs.NumberOfContracts, linkTokenContract, contractDeployer, - o.bootstrapNode, o.workerNodes, o.chainClient, ) @@ -258,7 +258,7 @@ func (o *OCRSoakTest) Setup() { // Run starts the OCR soak test func (o *OCRSoakTest) Run() { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + ctx, cancel := context.WithTimeout(utils.TestContext(o.t), time.Second*5) latestBlockNum, err := o.chainClient.LatestBlockNumber(ctx) cancel() require.NoError(o.t, err, "Error getting current block number") @@ -343,7 +343,7 @@ func (o *OCRSoakTest) SaveState() error { if err != nil { return err } - // #nosec G306 - let everyone read + //nolint:gosec // G306 - let everyone read if err = os.WriteFile(saveFileLocation, data, 0644); err != nil { return err } @@ -468,6 +468,7 @@ func (o *OCRSoakTest) Interrupted() bool { func (o *OCRSoakTest) testLoop(testDuration time.Duration, newValue int) { endTest := time.After(testDuration) interruption := make(chan os.Signal, 1) + //nolint:staticcheck //ignore SA1016 we need to send the os.Kill signal signal.Notify(interruption, os.Kill, os.Interrupt, syscall.SIGTERM) lastValue := 0 newRoundTrigger := time.NewTimer(0) // Want to trigger a new round ASAP @@ -558,7 +559,7 @@ func (o *OCRSoakTest) setFilterQuery() { // WARNING: Should only be used for observation and logging. This is not a reliable way to collect events. func (o *OCRSoakTest) observeOCREvents() error { eventLogs := make(chan types.Log) - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(utils.TestContext(o.t), 5*time.Second) eventSub, err := o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { @@ -592,7 +593,7 @@ func (o *OCRSoakTest) observeOCREvents() error { Str("Backoff", backoff.String()). Interface("Query", o.filterQuery). Msg("Error while subscribed to OCR Logs. Resubscribing") - ctx, cancel = context.WithTimeout(context.Background(), backoff) + ctx, cancel = context.WithTimeout(utils.TestContext(o.t), backoff) eventSub, err = o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { @@ -645,12 +646,12 @@ func (o *OCRSoakTest) collectEvents() error { timeout := time.Second * 15 o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") - ctx, cancel := context.WithTimeout(context.Background(), timeout) + ctx, cancel := context.WithTimeout(utils.TestContext(o.t), timeout) contractEvents, err := o.chainClient.FilterLogs(ctx, o.filterQuery) cancel() for err != nil { o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") - ctx, cancel := context.WithTimeout(context.Background(), timeout) + ctx, cancel := context.WithTimeout(utils.TestContext(o.t), timeout) contractEvents, err = o.chainClient.FilterLogs(ctx, o.filterQuery) cancel() if err != nil { diff --git a/integration-tests/testsetups/vrfv2.go b/integration-tests/testsetups/vrfv2.go index 194c7ff4e6c..8c5fde72168 100644 --- a/integration-tests/testsetups/vrfv2.go +++ b/integration-tests/testsetups/vrfv2.go @@ -22,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // VRFV2SoakTest defines a typical VRFV2 soak test @@ -87,7 +88,8 @@ func (v *VRFV2SoakTest) Run(t *testing.T) { Msg("Starting VRFV2 Soak Test") // set the requests to only run for a certain amount of time - testContext, testCancel := context.WithTimeout(context.Background(), v.Inputs.TestDuration) + ctx := utils.TestContext(t) + testContext, testCancel := context.WithTimeout(ctx, v.Inputs.TestDuration) defer testCancel() v.NumberOfRandRequests = 0 @@ -126,7 +128,7 @@ func (v *VRFV2SoakTest) Run(t *testing.T) { //todo - need to find better way for this time.Sleep(1 * time.Minute) - loadTestMetrics, err := v.Inputs.ConsumerContract.GetLoadTestMetrics(nil) + loadTestMetrics, err := v.Inputs.ConsumerContract.GetLoadTestMetrics(ctx) if err != nil { l.Error().Err(err).Msg("Error Occurred when getting Load Test Metrics from Consumer contract") } diff --git a/integration-tests/types/envcommon/common.go b/integration-tests/types/envcommon/common.go index 607c481f33f..bdabcaf96b0 100644 --- a/integration-tests/types/envcommon/common.go +++ b/integration-tests/types/envcommon/common.go @@ -2,7 +2,7 @@ package envcommon import ( "encoding/json" - "io/ioutil" + "io" "os" ) @@ -12,7 +12,7 @@ func ParseJSONFile(path string, v any) error { return err } defer jsonFile.Close() - b, _ := ioutil.ReadAll(jsonFile) + b, _ := io.ReadAll(jsonFile) err = json.Unmarshal(b, v) if err != nil { return err diff --git a/integration-tests/universal/log_poller/config.go b/integration-tests/universal/log_poller/config.go index 7297e811241..78a0da46bc6 100644 --- a/integration-tests/universal/log_poller/config.go +++ b/integration-tests/universal/log_poller/config.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -131,33 +132,33 @@ func (c *Config) validate() error { err := c.General.validate() if err != nil { - return fmt.Errorf("General config validation failed: %v", err) + return fmt.Errorf("General config validation failed: %w", err) } switch c.General.Generator { case GeneratorType_WASP: if c.Wasp == nil { - return fmt.Errorf("Wasp config is nil") + return fmt.Errorf("wasp config is nil") } if c.Wasp.Load == nil { - return fmt.Errorf("Wasp load config is nil") + return fmt.Errorf("wasp load config is nil") } err = c.Wasp.validate() if err != nil { - return fmt.Errorf("Wasp config validation failed: %v", err) + return fmt.Errorf("wasp config validation failed: %w", err) } case GeneratorType_Looped: if c.LoopedConfig == nil { - return fmt.Errorf("Looped config is nil") + return fmt.Errorf("looped config is nil") } err = c.LoopedConfig.validate() if err != nil { - return fmt.Errorf("Looped config validation failed: %v", err) + return fmt.Errorf("looped config validation failed: %w", err) } default: - return fmt.Errorf("Unknown generator type: %s", c.General.Generator) + return fmt.Errorf("unknown generator type: %s", c.General.Generator) } return nil @@ -165,15 +166,15 @@ func (c *Config) validate() error { func (g *General) validate() error { if g.Generator == "" { - return fmt.Errorf("Generator is empty") + return fmt.Errorf("generator is empty") } if g.Contracts == 0 { - return fmt.Errorf("Contracts is 0, but must be > 0") + return fmt.Errorf("contracts is 0, but must be > 0") } if g.EventsPerTx == 0 { - return fmt.Errorf("Events_per_tx is 0, but must be > 0") + return fmt.Errorf("events_per_tx is 0, but must be > 0") } return nil @@ -186,7 +187,7 @@ func (w *WaspConfig) validate() error { err := w.Load.validate() if err != nil { - return fmt.Errorf("Load config validation failed: %v", err) + return fmt.Errorf("Load config validation failed: %w", err) } return nil @@ -194,11 +195,11 @@ func (w *WaspConfig) validate() error { func (l *Load) validate() error { if l.RPS == 0 && l.LPS == 0 { - return fmt.Errorf("Either RPS or LPS needs to be set") + return fmt.Errorf("either RPS or LPS needs to be set") } if l.RPS != 0 && l.LPS != 0 { - return fmt.Errorf("Only one of RPS or LPS can be set") + return fmt.Errorf("only one of RPS or LPS can be set") } if l.Duration == nil { diff --git a/integration-tests/universal/log_poller/gun.go b/integration-tests/universal/log_poller/gun.go index 11932330a3b..39286f1b53e 100644 --- a/integration-tests/universal/log_poller/gun.go +++ b/integration-tests/universal/log_poller/gun.go @@ -7,8 +7,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/rs/zerolog" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) /* LogEmitterGun is a gun that constantly emits logs from a contract */ @@ -53,13 +54,13 @@ func (m *LogEmitterGun) Call(l *wasp.Generator) *wasp.CallResult { case "Log3": _, err = logEmitter.EmitLogStrings(getStringSlice(m.eventsPerTx)) default: - err = fmt.Errorf("Unknown event name: %s", event.Name) + err = fmt.Errorf("unknown event name: %s", event.Name) } if err != nil { return &wasp.CallResult{Error: err.Error(), Failed: true} } - localCounter += 1 + localCounter++ } // I don't think that will work as expected, I should atomically read the value and save it, so maybe just a mutex? diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 9f88827bb48..08ceb4a7be4 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -3,7 +3,6 @@ package logpoller import ( "bytes" "context" - "errors" "fmt" "math/big" "math/rand" @@ -22,16 +21,15 @@ import ( "github.com/scylladb/go-reflectx" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/wasp" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - ctf_blockchain "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/wasp" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - lpEvm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" le "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" @@ -41,12 +39,13 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" - utils2 "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -80,64 +79,65 @@ var registerSingleTopicFilter = func(registry contracts.KeeperRegistry, upkeepID return nil } +// Currently Unused November 8, 2023, Might be useful in the near future so keeping it here for now // this is not really possible, log trigger doesn't support multiple topics, even if log poller does -var registerMultipleTopicsFilter = func(registry contracts.KeeperRegistry, upkeepID *big.Int, emitterAddress common.Address, topics []abi.Event) error { - if len(topics) > 4 { - return errors.New("Cannot register more than 4 topics") - } - - var getTopic = func(topics []abi.Event, i int) common.Hash { - if i > len(topics)-1 { - return bytes0 - } - - return topics[i].ID - } - - var getFilterSelector = func(topics []abi.Event) (uint8, error) { - switch len(topics) { - case 0: - return 0, errors.New("Cannot register filter with 0 topics") - case 1: - return 0, nil - case 2: - return 1, nil - case 3: - return 3, nil - case 4: - return 7, nil - default: - return 0, errors.New("Cannot register filter with more than 4 topics") - } - } - - filterSelector, err := getFilterSelector(topics) - if err != nil { - return err - } - - logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ - ContractAddress: emitterAddress, - FilterSelector: filterSelector, - Topic0: getTopic(topics, 0), - Topic1: getTopic(topics, 1), - Topic2: getTopic(topics, 2), - Topic3: getTopic(topics, 3), - } - encodedLogTriggerConfig, err := automationUtilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) - if err != nil { - return err - } - - err = registry.SetUpkeepTriggerConfig(upkeepID, encodedLogTriggerConfig) - if err != nil { - return err - } - - return nil -} - -func NewOrm(logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (*lpEvm.DbORM, *sqlx.DB, error) { +// var registerMultipleTopicsFilter = func(registry contracts.KeeperRegistry, upkeepID *big.Int, emitterAddress common.Address, topics []abi.Event) error { +// if len(topics) > 4 { +// return errors.New("Cannot register more than 4 topics") +// } + +// var getTopic = func(topics []abi.Event, i int) common.Hash { +// if i > len(topics)-1 { +// return bytes0 +// } + +// return topics[i].ID +// } + +// var getFilterSelector = func(topics []abi.Event) (uint8, error) { +// switch len(topics) { +// case 0: +// return 0, errors.New("Cannot register filter with 0 topics") +// case 1: +// return 0, nil +// case 2: +// return 1, nil +// case 3: +// return 3, nil +// case 4: +// return 7, nil +// default: +// return 0, errors.New("Cannot register filter with more than 4 topics") +// } +// } + +// filterSelector, err := getFilterSelector(topics) +// if err != nil { +// return err +// } + +// logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ +// ContractAddress: emitterAddress, +// FilterSelector: filterSelector, +// Topic0: getTopic(topics, 0), +// Topic1: getTopic(topics, 1), +// Topic2: getTopic(topics, 2), +// Topic3: getTopic(topics, 3), +// } +// encodedLogTriggerConfig, err := automationUtilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) +// if err != nil { +// return err +// } + +// err = registry.SetUpkeepTriggerConfig(upkeepID, encodedLogTriggerConfig) +// if err != nil { +// return err +// } + +// return nil +// } + +func NewOrm(logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_test_env.PostgresDb) (*logpoller.DbORM, *sqlx.DB, error) { dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", "127.0.0.1", postgresDb.ExternalPort, postgresDb.User, postgresDb.Password, postgresDb.DbName) db, err := sqlx.Open("postgres", dsn) if err != nil { @@ -145,7 +145,7 @@ func NewOrm(logger core_logger.SugaredLogger, chainID *big.Int, postgresDb *ctf_ } db.MapperFunc(reflectx.CamelToSnakeASCII) - return lpEvm.NewORM(chainID, db, logger, pg.NewQConfig(false)), db, nil + return logpoller.NewORM(chainID, db, logger, pg.NewQConfig(false)), db, nil } type ExpectedFilter struct { @@ -189,7 +189,7 @@ var nodeHasExpectedFilters = func(expectedFilters []ExpectedFilter, logger core_ } if !filterFound { - return false, fmt.Errorf("No filter found for emitter %s and topic %s", expectedFilter.emitterAddress.String(), expectedFilter.topic.Hex()) + return false, fmt.Errorf("no filter found for emitter %s and topic %s", expectedFilter.emitterAddress.String(), expectedFilter.topic.Hex()) } } @@ -203,9 +203,10 @@ var randomWait = func(minMilliseconds, maxMilliseconds int) { } type LogEmitterChannel struct { - logsEmitted int - err error - currentIndex int + logsEmitted int + err error + // unused + // currentIndex int } func getIntSlice(length int) []int { @@ -247,7 +248,7 @@ var emitEvents = func(ctx context.Context, l zerolog.Logger, logEmitter *contrac case "Log3": _, err = (*logEmitter).EmitLogStrings(getStringSlice(cfg.General.EventsPerTx)) default: - err = fmt.Errorf("Unknown event name: %s", event.Name) + err = fmt.Errorf("unknown event name: %s", event.Name) } if err != nil { @@ -276,29 +277,7 @@ var emitEvents = func(ctx context.Context, l zerolog.Logger, logEmitter *contrac } } -var waitForEndBlockInLogPoller = func(endBlock int64, chainID *big.Int, l zerolog.Logger, coreLogger core_logger.SugaredLogger, nodes *test_env.ClCluster) (bool, error) { - for i := 1; i < len(nodes.Nodes); i++ { - clNode := nodes.Nodes[i] - orm, db, err := NewOrm(coreLogger, chainID, clNode.PostgresDb) - if err != nil { - return false, err - } - - defer db.Close() - block, err := orm.SelectBlockByNumber(endBlock) - if err != nil { - return false, err - } - - if block == nil { - return false, nil - } - } - - return true, nil -} - -var chainHasFinalisedEndBlock = func(l zerolog.Logger, evmClient ctf_blockchain.EVMClient, endBlock int64) (bool, error) { +var chainHasFinalisedEndBlock = func(l zerolog.Logger, evmClient blockchain.EVMClient, endBlock int64) (bool, error) { effectiveEndBlock := endBlock + 1 lastFinalisedBlockHeader, err := evmClient.GetLatestFinalizedBlockHeader(context.Background()) if err != nil { @@ -512,7 +491,7 @@ func (m *MissingLogs) IsEmpty() bool { return true } -var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient ctf_blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *Config) (MissingLogs, error) { +var getMissingLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, clnodeCluster *test_env.ClCluster, l zerolog.Logger, coreLogger core_logger.SugaredLogger, cfg *Config) (MissingLogs, error) { wg := &sync.WaitGroup{} type dbQueryResult struct { @@ -696,11 +675,7 @@ var printMissingLogsByType = func(missingLogs map[string][]geth_types.Log, l zer for _, logs := range missingLogs { for _, v := range logs { humanName := findHumanName(v.Topics[0]) - if _, ok := missingByType[humanName]; ok { - missingByType[humanName] += 1 - } else { - missingByType[humanName] = 1 - } + missingByType[humanName]++ } } @@ -709,7 +684,7 @@ var printMissingLogsByType = func(missingLogs map[string][]geth_types.Log, l zer } } -var getEVMLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient ctf_blockchain.EVMClient, l zerolog.Logger, cfg *Config) ([]geth_types.Log, error) { +var getEVMLogs = func(startBlock, endBlock int64, logEmitters []*contracts.LogEmitter, evmClient blockchain.EVMClient, l zerolog.Logger, cfg *Config) ([]geth_types.Log, error) { allLogsInEVMNode := make([]geth_types.Log, 0) for j := 0; j < len(logEmitters); j++ { address := (*logEmitters[j]).Address() @@ -757,7 +732,7 @@ func runWaspGenerator(t *testing.T, cfg *Config, logEmitters []*contracts.LogEmi RPSprime = cfg.Wasp.Load.LPS / int64(cfg.General.Contracts) / int64(cfg.General.EventsPerTx) / int64(len(cfg.General.EventsToEmit)) if RPSprime < 1 { - return 0, fmt.Errorf("Invalid load configuration, effective RPS would have been zero. Adjust LPS, contracts count, events per tx or events to emit") + return 0, fmt.Errorf("invalid load configuration, effective RPS would have been zero. Adjust LPS, contracts count, events per tx or events to emit") } } @@ -854,9 +829,8 @@ func getExpectedLogCount(cfg *Config) int64 { if cfg.General.Generator == GeneratorType_WASP { if cfg.Wasp.Load.RPS != 0 { return cfg.Wasp.Load.RPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) * int64(cfg.General.EventsPerTx) - } else { - return cfg.Wasp.Load.LPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) } + return cfg.Wasp.Load.LPS * int64(cfg.Wasp.Load.Duration.Duration().Seconds()) } return int64(len(cfg.General.EventsToEmit) * cfg.LoopedConfig.ExecutionCount * cfg.General.Contracts * cfg.General.EventsPerTx) @@ -903,6 +877,7 @@ var executeChaosExperiment = func(l zerolog.Logger, testEnv *test_env.CLClusterT guardChan := make(chan struct{}, 1) for i := 0; i < cfg.ChaosConfig.ExperimentCount; i++ { + i := i wg.Add(1) guardChan <- struct{}{} go func() { @@ -921,22 +896,21 @@ var executeChaosExperiment = func(l zerolog.Logger, testEnv *test_env.CLClusterT }() go func() { - for { - select { - case err, ok := <-chaosChan: - if !ok { - l.Info().Msg("All chaos experiments finished") - errorCh <- nil - return - } else { - if err != nil { - l.Err(err).Msg("Error encountered during chaos experiment") - errorCh <- err - return - } - } + for err := range chaosChan { + // This will receive errors until chaosChan is closed + if err != nil { + // If an error is encountered, log it, send it to the error channel, and return from the function + l.Err(err).Msg("Error encountered during chaos experiment") + errorCh <- err + return // Return on actual error } + // No need for an else block here, because if err is nil (which happens when the channel is closed), + // the loop will exit and the following log and nil send will execute. } + + // After the loop exits, which it will do when chaosChan is closed, log that all experiments are finished. + l.Info().Msg("All chaos experiments finished") + errorCh <- nil // Only send nil once, after all errors have been handled and the channel is closed }() } @@ -953,7 +927,7 @@ var GetFinalityDepth = func(chainId int64) (int64, error) { case 1337: finalityDepth = 10 default: - return 0, fmt.Errorf("No known finality depth for chain %d", chainId) + return 0, fmt.Errorf("no known finality depth for chain %d", chainId) } return finalityDepth, nil @@ -1014,7 +988,6 @@ var ( func setupLogPollerTestDocker( t *testing.T, - testName string, registryVersion ethereum.KeeperRegistryVersion, registryConfig contracts.KeeperRegistrySettings, upkeepsNeeded int, @@ -1057,8 +1030,8 @@ func setupLogPollerTestDocker( var logPolllerSettingsFn = func(chain *evmcfg.Chain) *evmcfg.Chain { chain.LogPollInterval = models.MustNewDuration(lpPollingInterval) - chain.FinalityDepth = utils2.Ptr[uint32](uint32(finalityDepth)) - chain.FinalityTagEnabled = utils2.Ptr[bool](finalityTagEnabled) + chain.FinalityDepth = it_utils.Ptr[uint32](uint32(finalityDepth)) + chain.FinalityTagEnabled = it_utils.Ptr[bool](finalityTagEnabled) return chain } @@ -1078,6 +1051,7 @@ func setupLogPollerTestDocker( SlotsPerEpoch: 2, }). Build() + require.NoError(t, err, "Error building ethereum network config") env, err = test_env.NewCLTestEnvBuilder(). WithTestLogger(t). diff --git a/integration-tests/universal/log_poller/scenarios.go b/integration-tests/universal/log_poller/scenarios.go index 1110a2f8caf..886547d46e1 100644 --- a/integration-tests/universal/log_poller/scenarios.go +++ b/integration-tests/universal/log_poller/scenarios.go @@ -1,19 +1,20 @@ package logpoller import ( - "context" "fmt" "math/big" "testing" "time" "github.com/onsi/gomega" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/utils" core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/stretchr/testify/require" ) func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { @@ -31,12 +32,11 @@ func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { var ( err error - testName = "basic-log-poller" upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) ) chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( - t, testName, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(500*time.Millisecond), cfg.General.UseFinalityTag, + t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(500*time.Millisecond), cfg.General.UseFinalityTag, ) _, upkeepIDs := actions.DeployConsumers( @@ -100,7 +100,7 @@ func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + sb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -122,7 +122,7 @@ func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + eb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Error getting latest block number") endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) @@ -194,13 +194,12 @@ func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string var ( err error - testName = "replay-log-poller" upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) ) // we set blockBackfillDepth to 0, to make sure nothing will be backfilled and won't interfere with our test chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( - t, testName, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag) + t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag) _, upkeepIDs := actions.DeployConsumers( t, @@ -230,7 +229,7 @@ func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string time.Sleep(5 * time.Second) // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + sb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -244,7 +243,7 @@ func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + eb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Error getting latest block number") endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) @@ -352,12 +351,11 @@ func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { var ( err error - testName = "ci-log-poller" upKeepsNeeded = cfg.General.Contracts * len(cfg.General.EventsToEmit) ) chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupLogPollerTestDocker( - t, testName, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag, + t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, upKeepsNeeded, time.Duration(1000*time.Millisecond), cfg.General.UseFinalityTag, ) _, upkeepIDs := actions.DeployConsumers( @@ -421,7 +419,7 @@ func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + sb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -443,7 +441,7 @@ func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(context.Background()) + eb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) require.NoError(t, err, "Error getting latest block number") endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) diff --git a/integration-tests/utils/cl_node_jobs.go b/integration-tests/utils/cl_node_jobs.go index 16b0c167cfe..65dc6e4e392 100644 --- a/integration-tests/utils/cl_node_jobs.go +++ b/integration-tests/utils/cl_node_jobs.go @@ -10,13 +10,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/google/uuid" "github.com/lib/pq" + "gopkg.in/guregu/null.v4" + coreClient "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "gopkg.in/guregu/null.v4" ) -func BuildBootstrapSpec(verifierAddr common.Address, chainID int64, fromBlock uint64, feedId [32]byte) *coreClient.OCR2TaskJobSpec { +func BuildBootstrapSpec(verifierAddr common.Address, chainID int64, feedId [32]byte) *coreClient.OCR2TaskJobSpec { hash := common.BytesToHash(feedId[:]) return &coreClient.OCR2TaskJobSpec{ Name: fmt.Sprintf("bootstrap-%s", uuid.NewString()), diff --git a/integration-tests/utils/common.go b/integration-tests/utils/common.go index 9aacaeed416..5ef3209c920 100644 --- a/integration-tests/utils/common.go +++ b/integration-tests/utils/common.go @@ -1,8 +1,10 @@ package utils import ( + "context" "math/big" "net" + "testing" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) @@ -33,3 +35,20 @@ func BigIntSliceContains(slice []*big.Int, b *big.Int) bool { } return false } + +// TestContext returns a context with the test's deadline, if available. +func TestContext(tb testing.TB) context.Context { + ctx := context.Background() + var cancel func() + switch t := tb.(type) { + case *testing.T: + if d, ok := t.Deadline(); ok { + ctx, cancel = context.WithDeadline(ctx, d) + } + } + if cancel == nil { + ctx, cancel = context.WithCancel(ctx) + } + tb.Cleanup(cancel) + return ctx +} diff --git a/integration-tests/utils/log.go b/integration-tests/utils/log.go deleted file mode 100644 index 499be8002d4..00000000000 --- a/integration-tests/utils/log.go +++ /dev/null @@ -1,19 +0,0 @@ -package utils - -import ( - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "os" -) - -func SetupCoreDockerEnvLogger() { - lvlStr := os.Getenv("CORE_DOCKER_ENV_LOG_LEVEL") - if lvlStr == "" { - lvlStr = "info" - } - lvl, err := zerolog.ParseLevel(lvlStr) - if err != nil { - panic(err) - } - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Level(lvl) -} diff --git a/integration-tests/utils/templates/secrets.go b/integration-tests/utils/templates/secrets.go index f81287e871f..45edf0d0127 100644 --- a/integration-tests/utils/templates/secrets.go +++ b/integration-tests/utils/templates/secrets.go @@ -2,6 +2,7 @@ package templates import ( "github.com/google/uuid" + "github.com/smartcontractkit/chainlink-testing-framework/utils/templates" ) From f7981f5c10403eca31a11d55b356e1191725f5c3 Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Mon, 13 Nov 2023 17:43:00 +0100 Subject: [PATCH 136/271] CCIP-1277 LogPoller - Fixing leaky abstraction by removing Q() from the ORM interface (#11200) * Fixing leaky abstraction by removing Q() from the ORM interface. Moving all the TX internals into the ORM implementation * Adding test * Post review fix * Post rebase fixes --- core/chains/evm/logpoller/log_poller.go | 39 +---- core/chains/evm/logpoller/models.go | 9 ++ core/chains/evm/logpoller/observability.go | 22 +-- .../evm/logpoller/observability_test.go | 2 +- core/chains/evm/logpoller/orm.go | 99 ++++++++---- core/chains/evm/logpoller/orm_test.go | 141 +++++++++++++++++- 6 files changed, 232 insertions(+), 80 deletions(-) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 01d6a2aad47..b86ede5dbcb 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -679,9 +679,7 @@ func (lp *logPoller) backfill(ctx context.Context, start, end int64) error { } lp.lggr.Debugw("Backfill found logs", "from", from, "to", to, "logs", len(gethLogs), "blocks", blocks) - err = lp.orm.Q().WithOpts(pg.WithParentCtx(ctx)).Transaction(func(tx pg.Queryer) error { - return lp.orm.InsertLogs(convertLogs(gethLogs, blocks, lp.lggr, lp.ec.ConfiguredChainID()), pg.WithQueryer(tx)) - }) + err = lp.orm.InsertLogs(convertLogs(gethLogs, blocks, lp.lggr, lp.ec.ConfiguredChainID()), pg.WithParentCtx(ctx)) if err != nil { lp.lggr.Warnw("Unable to insert logs, retrying", "err", err, "from", from, "to", to) return err @@ -750,21 +748,7 @@ func (lp *logPoller) getCurrentBlockMaybeHandleReorg(ctx context.Context, curren // the canonical set per read. Typically, if an application took action on a log // it would be saved elsewhere e.g. evm.txes, so it seems better to just support the fast reads. // Its also nicely analogous to reading from the chain itself. - err2 = lp.orm.Q().WithOpts(pg.WithParentCtx(ctx)).Transaction(func(tx pg.Queryer) error { - // These deletes are bounded by reorg depth, so they are - // fast and should not slow down the log readers. - err3 := lp.orm.DeleteBlocksAfter(blockAfterLCA.Number, pg.WithQueryer(tx)) - if err3 != nil { - lp.lggr.Warnw("Unable to clear reorged blocks, retrying", "err", err3) - return err3 - } - err3 = lp.orm.DeleteLogsAfter(blockAfterLCA.Number, pg.WithQueryer(tx)) - if err3 != nil { - lp.lggr.Warnw("Unable to clear reorged logs, retrying", "err", err3) - return err3 - } - return nil - }) + err2 = lp.orm.DeleteLogsAndBlocksAfter(blockAfterLCA.Number, pg.WithParentCtx(ctx)) if err2 != nil { // If we error on db commit, we can't know if the tx went through or not. // We return an error here which will cause us to restart polling from lastBlockSaved + 1 @@ -849,20 +833,11 @@ func (lp *logPoller) PollAndSaveLogs(ctx context.Context, currentBlockNumber int return } lp.lggr.Debugw("Unfinalized log query", "logs", len(logs), "currentBlockNumber", currentBlockNumber, "blockHash", currentBlock.Hash, "timestamp", currentBlock.Timestamp.Unix()) - err = lp.orm.Q().WithOpts(pg.WithParentCtx(ctx)).Transaction(func(tx pg.Queryer) error { - if err2 := lp.orm.InsertBlock(h, currentBlockNumber, currentBlock.Timestamp, latestFinalizedBlockNumber, pg.WithQueryer(tx)); err2 != nil { - return err2 - } - if len(logs) == 0 { - return nil - } - return lp.orm.InsertLogs(convertLogs(logs, - []LogPollerBlock{{BlockNumber: currentBlockNumber, - BlockTimestamp: currentBlock.Timestamp}}, - lp.lggr, - lp.ec.ConfiguredChainID(), - ), pg.WithQueryer(tx)) - }) + block := NewLogPollerBlock(h, currentBlockNumber, currentBlock.Timestamp, latestFinalizedBlockNumber) + err = lp.orm.InsertLogsWithBlock( + convertLogs(logs, []LogPollerBlock{block}, lp.lggr, lp.ec.ConfiguredChainID()), + block, + ) if err != nil { lp.lggr.Warnw("Unable to save logs resuming from last saved block + 1", "err", err, "block", currentBlockNumber) return diff --git a/core/chains/evm/logpoller/models.go b/core/chains/evm/logpoller/models.go index 9c55786777c..87ddd079a5b 100644 --- a/core/chains/evm/logpoller/models.go +++ b/core/chains/evm/logpoller/models.go @@ -56,3 +56,12 @@ func (l *Log) ToGethLog() types.Log { Index: uint(l.LogIndex), } } + +func NewLogPollerBlock(blockHash common.Hash, blockNumber int64, timestamp time.Time, finalizedBlockNumber int64) LogPollerBlock { + return LogPollerBlock{ + BlockHash: blockHash, + BlockNumber: blockNumber, + BlockTimestamp: timestamp, + FinalizedBlockNumber: finalizedBlockNumber, + } +} diff --git a/core/chains/evm/logpoller/observability.go b/core/chains/evm/logpoller/observability.go index c4b58b42a2e..03f4b77be25 100644 --- a/core/chains/evm/logpoller/observability.go +++ b/core/chains/evm/logpoller/observability.go @@ -68,19 +68,15 @@ func NewObservedORM(chainID *big.Int, db *sqlx.DB, lggr logger.Logger, cfg pg.QC } } -func (o *ObservedORM) Q() pg.Q { - return o.ORM.Q() -} - func (o *ObservedORM) InsertLogs(logs []Log, qopts ...pg.QOpt) error { return withObservedExec(o, "InsertLogs", func() error { return o.ORM.InsertLogs(logs, qopts...) }) } -func (o *ObservedORM) InsertBlock(hash common.Hash, blockNumber int64, blockTimestamp time.Time, lastFinalizedBlock int64, qopts ...pg.QOpt) error { - return withObservedExec(o, "InsertBlock", func() error { - return o.ORM.InsertBlock(hash, blockNumber, blockTimestamp, lastFinalizedBlock, qopts...) +func (o *ObservedORM) InsertLogsWithBlock(logs []Log, block LogPollerBlock, qopts ...pg.QOpt) error { + return withObservedExec(o, "InsertLogsWithBlock", func() error { + return o.ORM.InsertLogsWithBlock(logs, block, qopts...) }) } @@ -102,21 +98,15 @@ func (o *ObservedORM) DeleteFilter(name string, qopts ...pg.QOpt) error { }) } -func (o *ObservedORM) DeleteBlocksAfter(start int64, qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteBlocksAfter", func() error { - return o.ORM.DeleteBlocksAfter(start, qopts...) - }) -} - func (o *ObservedORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { return withObservedExec(o, "DeleteBlocksBefore", func() error { return o.ORM.DeleteBlocksBefore(end, qopts...) }) } -func (o *ObservedORM) DeleteLogsAfter(start int64, qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteLogsAfter", func() error { - return o.ORM.DeleteLogsAfter(start, qopts...) +func (o *ObservedORM) DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) error { + return withObservedExec(o, "DeleteLogsAndBlocksAfter", func() error { + return o.ORM.DeleteLogsAndBlocksAfter(start, qopts...) }) } diff --git a/core/chains/evm/logpoller/observability_test.go b/core/chains/evm/logpoller/observability_test.go index 0d3eadf47d7..ded3d7854dd 100644 --- a/core/chains/evm/logpoller/observability_test.go +++ b/core/chains/evm/logpoller/observability_test.go @@ -38,7 +38,7 @@ func TestMultipleMetricsArePublished(t *testing.T) { _, _ = orm.SelectLatestLogEventSigsAddrsWithConfs(0, []common.Address{{}}, []common.Hash{{}}, 1, pg.WithParentCtx(ctx)) _, _ = orm.SelectIndexedLogsCreatedAfter(common.Address{}, common.Hash{}, 1, []common.Hash{}, time.Now(), 0, pg.WithParentCtx(ctx)) _ = orm.InsertLogs([]Log{}, pg.WithParentCtx(ctx)) - _ = orm.InsertBlock(common.Hash{}, 1, time.Now(), 0, pg.WithParentCtx(ctx)) + _ = orm.InsertLogsWithBlock([]Log{}, NewLogPollerBlock(common.Hash{}, 1, time.Now(), 0), pg.WithParentCtx(ctx)) require.Equal(t, 13, testutil.CollectAndCount(orm.queryDuration)) require.Equal(t, 10, testutil.CollectAndCount(orm.datasetSize)) diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index e044be2e0c9..a1b86d2cb2c 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -20,17 +20,15 @@ import ( // it exposes some of the database implementation details (e.g. pg.Q). Ideally it should be agnostic and could be applied to any persistence layer. // What is more, LogPoller should not be aware of the underlying database implementation and delegate all the queries to the ORM. type ORM interface { - Q() pg.Q InsertLogs(logs []Log, qopts ...pg.QOpt) error - InsertBlock(blockHash common.Hash, blockNumber int64, blockTimestamp time.Time, lastFinalizedBlockNumber int64, qopts ...pg.QOpt) error + InsertLogsWithBlock(logs []Log, block LogPollerBlock, qopts ...pg.QOpt) error InsertFilter(filter Filter, qopts ...pg.QOpt) error LoadFilters(qopts ...pg.QOpt) (map[string]Filter, error) DeleteFilter(name string, qopts ...pg.QOpt) error - DeleteBlocksAfter(start int64, qopts ...pg.QOpt) error DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error - DeleteLogsAfter(start int64, qopts ...pg.QOpt) error + DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) error DeleteExpiredLogs(qopts ...pg.QOpt) error GetBlocksRange(start int64, end int64, qopts ...pg.QOpt) ([]LogPollerBlock, error) @@ -58,6 +56,7 @@ type ORM interface { type DbORM struct { chainID *big.Int q pg.Q + lggr logger.Logger } // NewORM creates a DbORM scoped to chainID. @@ -67,13 +66,10 @@ func NewORM(chainID *big.Int, db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) * return &DbORM{ chainID: chainID, q: q, + lggr: lggr, } } -func (o *DbORM) Q() pg.Q { - return o.q -} - // InsertBlock is idempotent to support replays. func (o *DbORM) InsertBlock(blockHash common.Hash, blockNumber int64, blockTimestamp time.Time, finalizedBlock int64, qopts ...pg.QOpt) error { args, err := newQueryArgs(o.chainID). @@ -191,12 +187,6 @@ func (o *DbORM) SelectLatestLogByEventSigWithConfs(eventSig common.Hash, address return &l, nil } -// DeleteBlocksAfter delete all blocks after and including start. -func (o *DbORM) DeleteBlocksAfter(start int64, qopts ...pg.QOpt) error { - q := o.q.WithOpts(qopts...) - return q.ExecQ(`DELETE FROM evm.log_poller_blocks WHERE block_number >= $1 AND evm_chain_id = $2`, start, utils.NewBig(o.chainID)) -} - // DeleteBlocksBefore delete all blocks before and including end. func (o *DbORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { q := o.q.WithOpts(qopts...) @@ -204,9 +194,31 @@ func (o *DbORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { return err } -func (o *DbORM) DeleteLogsAfter(start int64, qopts ...pg.QOpt) error { - q := o.q.WithOpts(qopts...) - return q.ExecQ(`DELETE FROM evm.logs WHERE block_number >= $1 AND evm_chain_id = $2`, start, utils.NewBig(o.chainID)) +func (o *DbORM) DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) error { + // These deletes are bounded by reorg depth, so they are + // fast and should not slow down the log readers. + return o.q.WithOpts(qopts...).Transaction(func(tx pg.Queryer) error { + args, err := newQueryArgs(o.chainID). + withStartBlock(start). + toArgs() + if err != nil { + o.lggr.Error("Cant build args for DeleteLogsAndBlocksAfter queries", "err", err) + return err + } + + _, err = tx.NamedExec(`DELETE FROM evm.log_poller_blocks WHERE block_number >= :start_block AND evm_chain_id = :evm_chain_id`, args) + if err != nil { + o.lggr.Warnw("Unable to clear reorged blocks, retrying", "err", err) + return err + } + + _, err = tx.NamedExec(`DELETE FROM evm.logs WHERE block_number >= :start_block AND evm_chain_id = :evm_chain_id`, args) + if err != nil { + o.lggr.Warnw("Unable to clear reorged logs, retrying", "err", err) + return err + } + return nil + }) } type Exp struct { @@ -233,13 +245,35 @@ func (o *DbORM) DeleteExpiredLogs(qopts ...pg.QOpt) error { // InsertLogs is idempotent to support replays. func (o *DbORM) InsertLogs(logs []Log, qopts ...pg.QOpt) error { - for _, log := range logs { - if o.chainID.Cmp(log.EvmChainId.ToInt()) != 0 { - return errors.Errorf("invalid chainID in log got %v want %v", log.EvmChainId.ToInt(), o.chainID) - } + if err := o.validateLogs(logs); err != nil { + return err } - q := o.q.WithOpts(qopts...) + return o.q.WithOpts(qopts...).Transaction(func(tx pg.Queryer) error { + return o.insertLogsWithinTx(logs, tx) + }) +} + +func (o *DbORM) InsertLogsWithBlock(logs []Log, block LogPollerBlock, qopts ...pg.QOpt) error { + // Optimization, don't open TX when there is only a block to be persisted + if len(logs) == 0 { + return o.InsertBlock(block.BlockHash, block.BlockNumber, block.BlockTimestamp, block.FinalizedBlockNumber, qopts...) + } + + if err := o.validateLogs(logs); err != nil { + return err + } + + // Block and logs goes with the same TX to ensure atomicity + return o.q.WithOpts(qopts...).Transaction(func(tx pg.Queryer) error { + if err := o.InsertBlock(block.BlockHash, block.BlockNumber, block.BlockTimestamp, block.FinalizedBlockNumber, pg.WithQueryer(tx)); err != nil { + return err + } + return o.insertLogsWithinTx(logs, tx) + }) +} + +func (o *DbORM) insertLogsWithinTx(logs []Log, tx pg.Queryer) error { batchInsertSize := 4000 for i := 0; i < len(logs); i += batchInsertSize { start, end := i, i+batchInsertSize @@ -247,12 +281,14 @@ func (o *DbORM) InsertLogs(logs []Log, qopts ...pg.QOpt) error { end = len(logs) } - err := q.ExecQNamed(` - INSERT INTO evm.logs - (evm_chain_id, log_index, block_hash, block_number, block_timestamp, address, event_sig, topics, tx_hash, data, created_at) + _, err := tx.NamedExec(` + INSERT INTO evm.logs + (evm_chain_id, log_index, block_hash, block_number, block_timestamp, address, event_sig, topics, tx_hash, data, created_at) VALUES - (:evm_chain_id, :log_index, :block_hash, :block_number, :block_timestamp, :address, :event_sig, :topics, :tx_hash, :data, NOW()) - ON CONFLICT DO NOTHING`, logs[start:end]) + (:evm_chain_id, :log_index, :block_hash, :block_number, :block_timestamp, :address, :event_sig, :topics, :tx_hash, :data, NOW()) + ON CONFLICT DO NOTHING`, + logs[start:end], + ) if err != nil { if errors.Is(err, context.DeadlineExceeded) && batchInsertSize > 500 { @@ -267,6 +303,15 @@ func (o *DbORM) InsertLogs(logs []Log, qopts ...pg.QOpt) error { return nil } +func (o *DbORM) validateLogs(logs []Log) error { + for _, log := range logs { + if o.chainID.Cmp(log.EvmChainId.ToInt()) != 0 { + return errors.Errorf("invalid chainID in log got %v want %v", log.EvmChainId.ToInt(), o.chainID) + } + } + return nil +} + func (o *DbORM) SelectLogsByBlockRange(start, end int64) ([]Log, error) { args, err := newQueryArgs(o.chainID). withStartBlock(start). diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 66e1afdc939..887984055ef 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -4,6 +4,7 @@ import ( "bytes" "database/sql" "fmt" + "math" "math/big" "testing" "time" @@ -15,7 +16,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -179,13 +183,13 @@ func TestORM(t *testing.T) { assert.Equal(t, int64(12), latest.BlockNumber) // Delete a block (only 10 on chain). - require.NoError(t, o1.DeleteBlocksAfter(10)) + require.NoError(t, o1.DeleteLogsAndBlocksAfter(10)) _, err = o1.SelectBlockByHash(common.HexToHash("0x1234")) require.Error(t, err) assert.True(t, errors.Is(err, sql.ErrNoRows)) // Delete blocks from another chain. - require.NoError(t, o2.DeleteBlocksAfter(11)) + require.NoError(t, o2.DeleteLogsAndBlocksAfter(11)) _, err = o2.SelectBlockByHash(common.HexToHash("0x1234")) require.Error(t, err) assert.True(t, errors.Is(err, sql.ErrNoRows)) @@ -318,7 +322,6 @@ func TestORM(t *testing.T) { require.Error(t, err) assert.True(t, errors.Is(err, sql.ErrNoRows)) // With block 12, anything <=2 should work - require.NoError(t, o1.DeleteBlocksAfter(10)) require.NoError(t, o1.InsertBlock(common.HexToHash("0x1234"), 11, time.Now(), 0)) require.NoError(t, o1.InsertBlock(common.HexToHash("0x1235"), 12, time.Now(), 0)) _, err = o1.SelectLatestLogByEventSigWithConfs(topic, common.HexToAddress("0x1234"), 0) @@ -421,7 +424,7 @@ func TestORM(t *testing.T) { assert.Len(t, logs, 7) // Delete logs after should delete all logs. - err = o1.DeleteLogsAfter(1) + err = o1.DeleteLogsAndBlocksAfter(1) require.NoError(t, err) logs, err = o1.SelectLogsByBlockRange(1, latest.BlockNumber) require.NoError(t, err) @@ -1301,3 +1304,133 @@ func TestNestedLogPollerBlocksQuery(t *testing.T) { require.NoError(t, err) require.Len(t, logs, 0) } + +func TestInsertLogsWithBlock(t *testing.T) { + chainID := testutils.NewRandomEVMChainID() + event := utils.RandomBytes32() + address := utils.RandomAddress() + + // We need full db here, because we want to test transaction rollbacks. + // Using pgtest.NewSqlxDB(t) will run all tests in TXs which is not desired for this type of test + // (inner tx rollback will rollback outer tx, blocking rest of execution) + _, db := heavyweight.FullTestDBV2(t, nil) + o := logpoller.NewORM(chainID, db, logger.TestLogger(t), pgtest.NewQConfig(true)) + + correctLog := GenLog(chainID, 1, 1, utils.RandomAddress().String(), event[:], address) + invalidLog := GenLog(chainID, -10, -10, utils.RandomAddress().String(), event[:], address) + correctBlock := logpoller.NewLogPollerBlock(utils.RandomBytes32(), 20, time.Now(), 10) + invalidBlock := logpoller.NewLogPollerBlock(utils.RandomBytes32(), -10, time.Now(), -10) + + tests := []struct { + name string + logs []logpoller.Log + block logpoller.LogPollerBlock + shouldRollback bool + }{ + { + name: "properly persist all data", + logs: []logpoller.Log{correctLog}, + block: correctBlock, + shouldRollback: false, + }, + { + name: "rollbacks transaction when block is invalid", + logs: []logpoller.Log{correctLog}, + block: invalidBlock, + shouldRollback: true, + }, + { + name: "rollbacks transaction when log is invalid", + logs: []logpoller.Log{invalidLog}, + block: correctBlock, + shouldRollback: true, + }, + { + name: "rollback when only some logs are invalid", + logs: []logpoller.Log{correctLog, invalidLog}, + block: correctBlock, + shouldRollback: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // clean all logs and blocks between test cases + defer func() { _ = o.DeleteLogsAndBlocksAfter(0) }() + insertError := o.InsertLogsWithBlock(tt.logs, tt.block) + + logs, logsErr := o.SelectLogs(0, math.MaxInt, address, event) + block, blockErr := o.SelectLatestBlock() + + if tt.shouldRollback { + assert.Error(t, insertError) + + assert.NoError(t, logsErr) + assert.Len(t, logs, 0) + + assert.Error(t, blockErr) + } else { + assert.NoError(t, insertError) + + assert.NoError(t, logsErr) + assert.Len(t, logs, len(tt.logs)) + + assert.NoError(t, blockErr) + assert.Equal(t, block.BlockNumber, tt.block.BlockNumber) + } + }) + } +} + +func TestInsertLogsInTx(t *testing.T) { + chainID := testutils.NewRandomEVMChainID() + event := utils.RandomBytes32() + address := utils.RandomAddress() + maxLogsSize := 9000 + + // We need full db here, because we want to test transaction rollbacks. + _, db := heavyweight.FullTestDBV2(t, nil) + o := logpoller.NewORM(chainID, db, logger.TestLogger(t), pgtest.NewQConfig(true)) + + logs := make([]logpoller.Log, maxLogsSize, maxLogsSize+1) + for i := 0; i < maxLogsSize; i++ { + logs[i] = GenLog(chainID, int64(i+1), int64(i+1), utils.RandomAddress().String(), event[:], address) + } + invalidLog := GenLog(chainID, -10, -10, utils.RandomAddress().String(), event[:], address) + + tests := []struct { + name string + logs []logpoller.Log + shouldRollback bool + }{ + { + name: "all logs persisted", + logs: logs, + shouldRollback: false, + }, + { + name: "rollback when invalid log is passed", + logs: append(logs, invalidLog), + shouldRollback: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // clean all logs and blocks between test cases + defer func() { _ = o.DeleteLogsAndBlocksAfter(0) }() + + insertErr := o.InsertLogs(tt.logs) + logsFromDb, err := o.SelectLogs(0, math.MaxInt, address, event) + assert.NoError(t, err) + + if tt.shouldRollback { + assert.Error(t, insertErr) + assert.Len(t, logsFromDb, 0) + } else { + assert.NoError(t, insertErr) + assert.Len(t, logsFromDb, len(tt.logs)) + } + }) + } +} From c8902245b2847fd3c201ebc10a992181f84a3d02 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 13 Nov 2023 13:03:15 -0500 Subject: [PATCH 137/271] bumping relay and wiring logger through (#11172) --- core/cmd/shell.go | 5 +++-- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 595c42b9fe1..e1ac0b99caa 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -60,7 +60,7 @@ var ( grpcOpts loop.GRPCOpts ) -func initGlobals(cfgProm config.Prometheus, cfgTracing config.Tracing) error { +func initGlobals(cfgProm config.Prometheus, cfgTracing config.Tracing, logger logger.Logger) error { // Avoid double initializations, but does not prevent relay methods from being called multiple times. var err error initGlobalsOnce.Do(func() { @@ -71,6 +71,7 @@ func initGlobals(cfgProm config.Prometheus, cfgTracing config.Tracing) error { CollectorTarget: cfgTracing.CollectorTarget(), NodeAttributes: cfgTracing.Attributes(), SamplingRatio: cfgTracing.SamplingRatio(), + OnDialError: func(error) { logger.Errorw("Failed to dial", "err", err) }, }) }) return err @@ -134,7 +135,7 @@ type ChainlinkAppFactory struct{} // NewApplication returns a new instance of the node with the given config. func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.GeneralConfig, appLggr logger.Logger, db *sqlx.DB) (app chainlink.Application, err error) { - err = initGlobals(cfg.Prometheus(), cfg.Tracing()) + err = initGlobals(cfg.Prometheus(), cfg.Tracing(), appLggr) if err != nil { appLggr.Errorf("Failed to initialize globals: %v", err) } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index bb68175ddfc..eb41312a6ab 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index bd3b75d37aa..35e85fe2c97 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1464,8 +1464,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 h1:28XkPE6YfJ4uabTX9/7sueRV6IKtY4hcm1nIt1e6b20= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a h1:G/pD8uI1PULRJU8Y3eLLzjqQBp9ruG9hj+wWxtyrgTo= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/go.mod b/go.mod index 8a4d58469c7..0a85fe7f488 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/go.sum b/go.sum index 1b96c936c5e..7fe91a6b123 100644 --- a/go.sum +++ b/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 h1:28XkPE6YfJ4uabTX9/7sueRV6IKtY4hcm1nIt1e6b20= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a h1:G/pD8uI1PULRJU8Y3eLLzjqQBp9ruG9hj+wWxtyrgTo= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index b5455838b58..a943e1c41a9 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -387,7 +387,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index fc486e54511..5719c36b5a8 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2369,8 +2369,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742 h1:28XkPE6YfJ4uabTX9/7sueRV6IKtY4hcm1nIt1e6b20= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231108215906-8bbaf383b742/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a h1:G/pD8uI1PULRJU8Y3eLLzjqQBp9ruG9hj+wWxtyrgTo= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= From 58b0e2536cad8f183865cc2004cd6969492c0816 Mon Sep 17 00:00:00 2001 From: FelixFan1992 Date: Mon, 13 Nov 2023 13:38:17 -0500 Subject: [PATCH 138/271] add ocr3-automation telemetry type (#11087) * add ocr3-automation telemetry type * pass ocr3 automation telem type to 2.1 --- core/services/ocr2/delegate.go | 2 +- core/services/synchronization/common.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 9905ed6ae6c..bbb3b5cf7ae 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -1122,7 +1122,7 @@ func (d *Delegate) newServicesOCR2Keepers21( ContractConfigTracker: keeperProvider.ContractConfigTracker(), KeepersDatabase: ocrDB, Logger: ocrLogger, - MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR2Automation), + MonitoringEndpoint: d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.OCR3Automation), OffchainConfigDigester: keeperProvider.OffchainConfigDigester(), OffchainKeyring: kb, OnchainKeyring: services.Keyring(), diff --git a/core/services/synchronization/common.go b/core/services/synchronization/common.go index 32f3a86c6f9..584f5b24380 100644 --- a/core/services/synchronization/common.go +++ b/core/services/synchronization/common.go @@ -22,6 +22,7 @@ const ( OCR3Mercury TelemetryType = "ocr3-mercury" OCR2VRF TelemetryType = "ocr2-vrf" AutomationCustom TelemetryType = "automation-custom" + OCR3Automation TelemetryType = "ocr3-automation" ) type TelemPayload struct { From 023985b15d3a58db8425bc30aed49cca454679f4 Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Mon, 13 Nov 2023 14:53:46 -0500 Subject: [PATCH 139/271] [Functions] Coordinator v1.1 changes (#11269) * inline abi decode and reduce string length * mv ChainSpecificUtil to lib * Regenerate geth wrappers & gas snapshot --------- Co-authored-by: Rens Rooimans --- .../gas-snapshots/functions.gas-snapshot | 24 +++++++++---------- .../functions/dev/v1_X/FunctionsBilling.sol | 2 +- .../dev/v1_X/FunctionsCoordinator.sol | 20 +++++++--------- .../{ => libraries}/ChainSpecificUtil.sol | 4 ++-- .../functions_coordinator.go | 2 +- ...rapper-dependency-versions-do-not-edit.txt | 2 +- 6 files changed, 25 insertions(+), 29 deletions(-) rename contracts/src/v0.8/functions/dev/v1_X/{ => libraries}/ChainSpecificUtil.sol (94%) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index 521b3bffac1..82b5b494a74 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14600662) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14600640) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14600656) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14612076) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14612053) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14612025) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14611976) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14611965) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14612009) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14577815) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14577793) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14577809) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14589229) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14589206) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14589178) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14589129) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14589118) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14589162) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -141,10 +141,10 @@ FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Reve FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_RevertIfStartIsAfterEnd() (gas: 13459) FunctionsSubscriptions_GetSubscriptionsInRange:test_GetSubscriptionsInRange_Success() (gas: 59592) FunctionsSubscriptions_GetTotalBalance:test_GetTotalBalance_Success() (gas: 15010) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43863, ~: 45548) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46375, ~: 48060) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoCalldata(uint96) (runs: 256, μ: 43508, ~: 45548) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNoSubscription(uint96) (runs: 256, μ: 46020, ~: 48060) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfCallerIsNotLink(uint96) (runs: 256, μ: 14295, ~: 14295) -FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51177, ~: 53040) +FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_RevertIfPaused(uint96) (runs: 256, μ: 51089, ~: 53040) FunctionsSubscriptions_OnTokenTransfer:test_OnTokenTransfer_Success(uint96) (runs: 256, μ: 86057, ~: 89604) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfAmountMoreThanBalance() (gas: 20745) FunctionsSubscriptions_OracleWithdraw:test_OracleWithdraw_RevertIfBalanceInvariant() (gas: 189) diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol index e4b6fe7d90c..cb4f2f45677 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsBilling.sol @@ -10,7 +10,7 @@ import {FunctionsResponse} from "./libraries/FunctionsResponse.sol"; import {SafeCast} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; -import {ChainSpecificUtil} from "./ChainSpecificUtil.sol"; +import {ChainSpecificUtil} from "./libraries/ChainSpecificUtil.sol"; /// @title Functions Billing contract /// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON). diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol index 15949a497e7..16e9029ce3f 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol @@ -133,15 +133,13 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli address[MAX_NUM_ORACLES] memory /*signers*/, bytes calldata report ) internal override { - bytes32[] memory requestIds; - bytes[] memory results; - bytes[] memory errors; - bytes[] memory onchainMetadata; - bytes[] memory offchainMetadata; - (requestIds, results, errors, onchainMetadata, offchainMetadata) = abi.decode( - report, - (bytes32[], bytes[], bytes[], bytes[], bytes[]) - ); + ( + bytes32[] memory requestIds, + bytes[] memory results, + bytes[] memory errors, + bytes[] memory onchainMetadata, + bytes[] memory offchainMetadata + ) = abi.decode(report, (bytes32[], bytes[], bytes[], bytes[], bytes[])); uint256 numberOfFulfillments = uint8(requestIds.length); if ( @@ -151,9 +149,7 @@ contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilli numberOfFulfillments != onchainMetadata.length || numberOfFulfillments != offchainMetadata.length ) { - revert ReportInvalid( - "All fields on the report must be of equal length: requestIds, results, errors, onchainMetadata, offchainMetadata" - ); + revert ReportInvalid("Fields must be equal length"); } // Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig diff --git a/contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol b/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol similarity index 94% rename from contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol rename to contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol index f0eec19db24..d6569a256bf 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/ChainSpecificUtil.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; -import {OVM_GasPriceOracle} from "../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; +import {ArbGasInfo} from "../../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import {OVM_GasPriceOracle} from "../../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; /// @dev A library that abstracts out opcodes that behave differently across chains. /// @dev The methods below return values that are pertinent to the given chain. diff --git a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go index 3e3fac16d13..5f0d2d45f2d 100644 --- a/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go +++ b/core/gethwrappers/functions/generated/functions_coordinator/functions_coordinator.go @@ -72,7 +72,7 @@ type FunctionsResponseRequestMeta struct { var FunctionsCoordinatorMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"router\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"linkToNativeFeed\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"EmptyPublicKey\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InconsistentReportData\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCalldata\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"InvalidConfig\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"linkWei\",\"type\":\"int256\"}],\"name\":\"InvalidLinkWeiPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSubscription\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"MustBeSubOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoTransmittersSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouter\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"OnlyCallableByRouterOwner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PaymentTooLarge\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"ReportInvalid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RouterMustBeSet\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedPublicKeyChange\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnauthorizedSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"UnsupportedRequestDataVersion\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"CommitmentDeleted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"previousConfigBlockNumber\",\"type\":\"uint32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"configCount\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"signers\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"transmitters\",\"type\":\"address[]\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"f\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"onchainConfig\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"offchainConfigVersion\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"offchainConfig\",\"type\":\"bytes\"}],\"name\":\"ConfigSet\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"ConfigUpdated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"requestInitiator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"callbackGasLimit\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"indexed\":false,\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"name\":\"OracleRequest\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"transmitter\",\"type\":\"address\"}],\"name\":\"OracleResponse\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"juelsPerGas\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"l1FeeShareWei\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"callbackCostJuels\",\"type\":\"uint96\"},{\"indexed\":false,\"internalType\":\"uint96\",\"name\":\"totalCostJuels\",\"type\":\"uint96\"}],\"name\":\"RequestBilled\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"name\":\"Transmitted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"}],\"name\":\"deleteCommitment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint256\",\"name\":\"gasPriceWei\",\"type\":\"uint256\"}],\"name\":\"estimateCost\",\"outputs\":[{\"internalType\":\"uint96\",\"name\":\"\",\"type\":\"uint96\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAdminFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"getDONFee\",\"outputs\":[{\"internalType\":\"uint72\",\"name\":\"\",\"type\":\"uint72\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getDONPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getThresholdPublicKey\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getWeiPerUnitLink\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDetails\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"configCount\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockNumber\",\"type\":\"uint32\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestConfigDigestAndEpoch\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"scanLogs\",\"type\":\"bool\"},{\"internalType\":\"bytes32\",\"name\":\"configDigest\",\"type\":\"bytes32\"},{\"internalType\":\"uint32\",\"name\":\"epoch\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"}],\"name\":\"oracleWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"oracleWithdrawAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_signers\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"_transmitters\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"_f\",\"type\":\"uint8\"},{\"internalType\":\"bytes\",\"name\":\"_onchainConfig\",\"type\":\"bytes\"},{\"internalType\":\"uint64\",\"name\":\"_offchainConfigVersion\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"_offchainConfig\",\"type\":\"bytes\"}],\"name\":\"setConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"donPublicKey\",\"type\":\"bytes\"}],\"name\":\"setDONPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"thresholdPublicKey\",\"type\":\"bytes\"}],\"name\":\"setThresholdPublicKey\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"flags\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"requestingContract\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"availableBalance\",\"type\":\"uint96\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initiatedRequests\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"dataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint64\",\"name\":\"completedRequests\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"subscriptionOwner\",\"type\":\"address\"}],\"internalType\":\"structFunctionsResponse.RequestMeta\",\"name\":\"request\",\"type\":\"tuple\"}],\"name\":\"startRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestId\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"coordinator\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"estimatedTotalCostJuels\",\"type\":\"uint96\"},{\"internalType\":\"address\",\"name\":\"client\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"subscriptionId\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"callbackGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"adminFee\",\"type\":\"uint72\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint40\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint40\"},{\"internalType\":\"uint32\",\"name\":\"timeoutTimestamp\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsResponse.Commitment\",\"name\":\"commitment\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32[3]\",\"name\":\"reportContext\",\"type\":\"bytes32[3]\"},{\"internalType\":\"bytes\",\"name\":\"report\",\"type\":\"bytes\"},{\"internalType\":\"bytes32[]\",\"name\":\"rs\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"ss\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32\",\"name\":\"rawVs\",\"type\":\"bytes32\"}],\"name\":\"transmit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"transmitters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"typeAndVersion\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fulfillmentGasPriceOverEstimationBP\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"feedStalenessSeconds\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadBeforeCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"gasOverheadAfterCallback\",\"type\":\"uint32\"},{\"internalType\":\"uint72\",\"name\":\"donFee\",\"type\":\"uint72\"},{\"internalType\":\"uint40\",\"name\":\"minimumEstimateGasPriceWei\",\"type\":\"uint40\"},{\"internalType\":\"uint16\",\"name\":\"maxSupportedRequestDataVersion\",\"type\":\"uint16\"},{\"internalType\":\"uint224\",\"name\":\"fallbackNativePerUnitLink\",\"type\":\"uint224\"},{\"internalType\":\"uint32\",\"name\":\"requestTimeoutSeconds\",\"type\":\"uint32\"}],\"internalType\":\"structFunctionsBilling.Config\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"updateConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60a06040523480156200001157600080fd5b50604051620057423803806200574283398101604081905262000034916200046d565b8282828233806000816200008f5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c257620000c28162000139565b5050506001600160a01b038116620000ed57604051632530e88560e11b815260040160405180910390fd5b6001600160a01b03908116608052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200012d82620001e4565b5050505050506200062c565b336001600160a01b03821603620001935760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000086565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee62000342565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033790839062000576565b60405180910390a150565b6200034c6200034e565b565b6000546001600160a01b031633146200034c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000086565b80516001600160a01b0381168114620003c257600080fd5b919050565b60405161012081016001600160401b0381118282101715620003f957634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c257600080fd5b80516001600160481b0381168114620003c257600080fd5b805164ffffffffff81168114620003c257600080fd5b805161ffff81168114620003c257600080fd5b80516001600160e01b0381168114620003c257600080fd5b60008060008385036101608112156200048557600080fd5b6200049085620003aa565b935061012080601f1983011215620004a757600080fd5b620004b1620003c7565b9150620004c160208701620003ff565b8252620004d160408701620003ff565b6020830152620004e460608701620003ff565b6040830152620004f760808701620003ff565b60608301526200050a60a0870162000414565b60808301526200051d60c087016200042c565b60a08301526200053060e0870162000442565b60c08301526101006200054581880162000455565b60e084015262000557828801620003ff565b908301525091506200056d6101408501620003aa565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005c960808401826001600160481b03169052565b5060a0830151620005e360a084018264ffffffffff169052565b5060c0830151620005fa60c084018261ffff169052565b5060e08301516200061660e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b6080516150d06200067260003960008181610845015281816109d301528181610ca601528181610f3a0152818161104501528181611789015261350201526150d06000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a0366004613a46565b61059c565b005b6101a56101b5366004613bef565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613d13565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613db4565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613e43565b6108d7565b6101a5610a90565b6101a5610b92565b6101a5610293366004613a46565b610d92565b6102a0610de2565b6040516102039190613ecd565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613ee0565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613ef9565b610fd4565b604051610203919061404e565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab3660046140a2565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b6040516102039190614159565b61053b610536366004614249565b611785565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f66118e5565b6101a561056e366004614362565b61193c565b61057b6124b8565b604051908152602001610203565b6101a561059736600461442f565b612711565b6105a4612725565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec8284836144e5565b505050565b6105f96127a8565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f518486290610836908390614159565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d2919061460b565b905090565b6108df6127b0565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff16614657565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6127a8565b610ba26127b0565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561467c565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d87816146ab565b9050610bb1565b5050565b610d9a612725565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec8284836144e5565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e609061444c565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea89061444c565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed49061444c565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a8836146e3565b61295c565b90506110bf606083016040840161442f565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a088016147d0565b61111f6101608801610140890161442f565b61112988806147ed565b61113b6101208b016101008c01614852565b60208b01356111516101008d0160e08e0161486d565b8b6040516111679998979695949392919061488a565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16111d68a8a8a8a8a8a612dfa565b6003546000906002906111f49060ff80821691610100900416614932565b6111fe919061497a565b611209906001614932565b60ff169050878114611277576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b878614611306576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f7265706f727420727320616e64207373206d757374206265206f66206571756160448201527f6c206c656e6774680000000000000000000000000000000000000000000000006064820152608401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113495761134961499c565b600281111561135a5761135a61499c565b90525090506002816020015160028111156113775761137761499c565b141580156113c057506006816000015160ff168154811061139a5761139a61467c565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff163314155b15611427576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b505050506114336139de565b6000808a8a6040516114469291906149cb565b60405190819003812061145d918e906020016149db565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b898110156117675760006001848984602081106114c6576114c661467c565b6114d391901a601b614932565b8e8e868181106114e5576114e561467c565b905060200201358d8d878181106114fe576114fe61467c565b905060200201356040516000815260200160405260405161153b949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561155d573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff808216855292965092945084019161010090041660028111156115dd576115dd61499c565b60028111156115ee576115ee61499c565b905250925060018360200151600281111561160b5761160b61499c565b14611672576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061168c5761168c61467c565b602002015173ffffffffffffffffffffffffffffffffffffffff161461170e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117285761172861467c565b73ffffffffffffffffffffffffffffffffffffffff9092166020929092020152611753600186614932565b94505080611760906146ab565b90506114a7565b505050611778833383858e8e612eb1565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b15801561182557600080fd5b505afa158015611839573d6000803e3d6000fd5b5050505066038d7ea4c6800082111561187e576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611888610841565b905060006118cb87878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b90506118d985858385613122565b98975050505050505050565b6060600c80546118f49061444c565b905060000361192f576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea89061444c565b855185518560ff16601f8311156119af576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611a19576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611aa7576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611ab28160036149ef565b8311611b1a576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611b22612725565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611b69908861328f565b60055415611d1e57600554600090611b8390600190614a06565b9050600060058281548110611b9a57611b9a61467c565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611bd457611bd461467c565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611c5457611c54614a19565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611cbd57611cbd614a19565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611b69915050565b60005b8151518110156122d557815180516000919083908110611d4357611d4361467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611dc8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f7369676e6572206d757374206e6f7420626520656d70747900000000000000006044820152606401610b0d565b600073ffffffffffffffffffffffffffffffffffffffff1682602001518281518110611df657611df661467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611e7b576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d7074790000006044820152606401610b0d565b60006004600084600001518481518110611e9757611e9761467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611ee157611ee161499c565b14611f48576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611f7957611f7961467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561201a5761201a61499c565b02179055506000915061202a9050565b60046000846020015184815181106120445761204461467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff16600281111561208e5761208e61499c565b146120f5576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff8216815260208101600281525060046000846020015184815181106121285761212861467c565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016176101008360028111156121c9576121c961499c565b0217905550508251805160059250839081106121e7576121e761467c565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106122635761226361467c565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055806122cd816146ab565b915050611d21565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161238d91849174010000000000000000000000000000000000000000900416614a48565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123ec4630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a001516132a8565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986124a3988b9891977401000000000000000000000000000000000000000090920463ffffffff16969095919491939192614a65565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a9190614b15565b50935050925050804261267d9190614a06565b836020015163ffffffff1610801561269f57506000836020015163ffffffff16115b156126cd57505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b6000821361270a576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b612719612725565b61272281613353565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146127a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6127a6612725565b600b546bffffffffffffffffffffffff166000036127ca57565b60006127d4610de2565b80519091506000819003612814576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b546000906128339083906bffffffffffffffffffffffff16614b65565b905060005b828110156128fe5781600a60008684815181106128575761285761467c565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128bf9190614b90565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806128f7906146ab565b9050612838565b506129098282614bb5565b600b80546000906129299084906bffffffffffffffffffffffff16614657565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612b17576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612b548560e001513a848860800151613122565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612bb0576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612bc99190614bdd565b905060003087604001518860a001518960c001516001612be99190614bf0565b8a5180516020918201206101008d015160e08e0151604051612c9d98979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612dac919061404e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612e078260206149ef565b612e128560206149ef565b612e1e88610144614bdd565b612e289190614bdd565b612e329190614bdd565b612e3d906000614bdd565b9050368114612ea8576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b606080808080612ec386880188614cec565b84519499509297509095509350915060ff16801580612ee3575084518114155b80612eef575083518114155b80612efb575082518114155b80612f07575081518114155b15612fe0576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152607060248201527f416c6c206669656c6473206f6e20746865207265706f7274206d75737420626560448201527f206f6620657175616c206c656e6774683a20726571756573744964732c20726560648201527f73756c74732c206572726f72732c206f6e636861696e4d657461646174612c2060848201527f6f6666636861696e4d657461646174610000000000000000000000000000000060a482015260c401610b0d565b60005b818110156131135760006130788883815181106130025761300261467c565b602002602001015188848151811061301c5761301c61467c565b60200260200101518885815181106130365761303661467c565b60200260200101518886815181106130505761305061467c565b602002602001015188878151811061306a5761306a61467c565b602002602001015188613448565b9050600081600681111561308e5761308e61499c565b14806130ab575060018160068111156130a9576130a961499c565b145b15613102578782815181106130c2576130c261467c565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b5061310c816146ab565b9050612fe3565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561317d57600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b600854600090612710906131979063ffffffff16876149ef565b6131a19190614dbe565b6131ab9086614bdd565b60085490915060009087906131e49063ffffffff6c01000000000000000000000000820481169168010000000000000000900416614a48565b6131ee9190614a48565b63ffffffff16905060006132386000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061375c92505050565b905060006132598261324a85876149ef565b6132549190614bdd565b61389e565b9050600061327568ffffffffffffffffff808916908a16614b90565b90506132818183614b90565b9a9950505050505050505050565b6000613299610de2565b511115610d8e57610d8e6127b0565b6000808a8a8a8a8a8a8a8a8a6040516020016132cc99989796959493929190614dd2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036133d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000808480602001905181019061345f9190614e9e565b905060003a82610120015183610100015161347a9190614f66565b64ffffffffff1661348b91906149ef565b905060008460ff166134d36000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061375c92505050565b6134dd9190614dbe565b905060006134ee6132548385614bdd565b905060006134fb3a61389e565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff168961355a9190614b90565b338d6040518763ffffffff1660e01b815260040161357d96959493929190614f84565b60408051808303816000875af115801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135bf9190615000565b909250905060008260068111156135d8576135d861499c565b14806135f5575060018260068111156135f3576135f361499c565b145b1561374b5760008e8152600760205260408120556136138185614b90565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161367f91859116614b90565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b6136fe9190614b90565b6137089190614b90565b6137129190614b90565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b600046613768816138d2565b156137e457606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137dd9190615033565b9392505050565b6137ed816138f5565b156138955773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e8460405180608001604052806048815260200161507c6048913960405160200161384d92919061504c565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016138789190613d13565b602060405180830381865afa1580156137b9573d6000803e3d6000fd5b50600092915050565b60006138cc6138ab6124b8565b6138bd84670de0b6b3a76400006149ef565b6138c79190614dbe565b61393c565b92915050565b600061a4b18214806138e6575062066eed82145b806138cc57505062066eee1490565b6000600a82148061390757506101a482145b80613914575062aa37dc82145b80613920575061210582145b8061392d575062014a3382145b806138cc57505062014a341490565b60006bffffffffffffffffffffffff8211156139da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f840112613a0f57600080fd5b50813567ffffffffffffffff811115613a2757600080fd5b602083019150836020828501011115613a3f57600080fd5b9250929050565b60008060208385031215613a5957600080fd5b823567ffffffffffffffff811115613a7057600080fd5b613a7c858286016139fd565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613adb57613adb613a88565b60405290565b604051610160810167ffffffffffffffff81118282101715613adb57613adb613a88565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613b4c57613b4c613a88565b604052919050565b63ffffffff8116811461272257600080fd5b803561117081613b54565b68ffffffffffffffffff8116811461272257600080fd5b803561117081613b71565b64ffffffffff8116811461272257600080fd5b803561117081613b93565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613c0257600080fd5b613c0a613ab7565b613c1383613b66565b8152613c2160208401613b66565b6020820152613c3260408401613b66565b6040820152613c4360608401613b66565b6060820152613c5460808401613b88565b6080820152613c6560a08401613ba6565b60a0820152613c7660c08401613bb1565b60c0820152613c8760e08401613bc3565b60e0820152610100613c9a818501613b66565b908201529392505050565b60005b83811015613cc0578181015183820152602001613ca8565b50506000910152565b60008151808452613ce1816020860160208601613ca5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006137dd6020830184613cc9565b600082601f830112613d3757600080fd5b813567ffffffffffffffff811115613d5157613d51613a88565b613d8260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b05565b818152846020838601011115613d9757600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613dc657600080fd5b813567ffffffffffffffff811115613ddd57600080fd5b613de984828501613d26565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461272257600080fd5b803561117081613df1565b6bffffffffffffffffffffffff8116811461272257600080fd5b803561117081613e1e565b60008060408385031215613e5657600080fd5b8235613e6181613df1565b91506020830135613e7181613e1e565b809150509250929050565b600081518084526020808501945080840160005b83811015613ec257815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e90565b509495945050505050565b6020815260006137dd6020830184613e7c565b600060208284031215613ef257600080fd5b5035919050565b600060208284031215613f0b57600080fd5b813567ffffffffffffffff811115613f2257600080fd5b820161016081850312156137dd57600080fd5b805182526020810151613f60602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613f8060408401826bffffffffffffffffffffffff169052565b506060810151613fa8606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613fc4608084018267ffffffffffffffff169052565b5060a0810151613fdc60a084018263ffffffff169052565b5060c0810151613ff960c084018268ffffffffffffffffff169052565b5060e081015161401660e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b61016081016138cc8284613f35565b60008083601f84011261406f57600080fd5b50813567ffffffffffffffff81111561408757600080fd5b6020830191508360208260051b8501011115613a3f57600080fd5b60008060008060008060008060e0898b0312156140be57600080fd5b606089018a8111156140cf57600080fd5b8998503567ffffffffffffffff808211156140e957600080fd5b6140f58c838d016139fd565b909950975060808b013591508082111561410e57600080fd5b61411a8c838d0161405d565b909750955060a08b013591508082111561413357600080fd5b506141408b828c0161405d565b999c989b50969995989497949560c00135949350505050565b815163ffffffff9081168252602080840151821690830152604080840151821690830152606080840151918216908301526101208201905060808301516141ad608084018268ffffffffffffffffff169052565b5060a08301516141c660a084018264ffffffffff169052565b5060c08301516141dc60c084018261ffff169052565b5060e083015161420c60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461272257600080fd5b803561117081614228565b60008060008060006080868803121561426157600080fd5b853561426c81614228565b9450602086013567ffffffffffffffff81111561428857600080fd5b614294888289016139fd565b90955093505060408601356142a881613b54565b949793965091946060013592915050565b600067ffffffffffffffff8211156142d3576142d3613a88565b5060051b60200190565b600082601f8301126142ee57600080fd5b813560206143036142fe836142b9565b613b05565b82815260059290921b8401810191818101908684111561432257600080fd5b8286015b8481101561434657803561433981613df1565b8352918301918301614326565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561437b57600080fd5b863567ffffffffffffffff8082111561439357600080fd5b61439f8a838b016142dd565b975060208901359150808211156143b557600080fd5b6143c18a838b016142dd565b96506143cf60408a01614351565b955060608901359150808211156143e557600080fd5b6143f18a838b01613d26565b94506143ff60808a0161423e565b935060a089013591508082111561441557600080fd5b5061442289828a01613d26565b9150509295509295509295565b60006020828403121561444157600080fd5b81356137dd81613df1565b600181811c9082168061446057607f821691505b602082108103614499577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156144c65750805b601f850160051c820191505b81811015610a88578281556001016144d2565b67ffffffffffffffff8311156144fd576144fd613a88565b6145118361450b835461444c565b8361449f565b6000601f841160018114614563576000851561452d5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b1783556145f9565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156145b25786850135825560209485019460019092019101614592565b50868210156145ed577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613b71565b60006020828403121561461d57600080fd5b81516137dd81613b71565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561270a5761270a614628565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036146dc576146dc614628565b5060010190565b600061016082360312156146f657600080fd5b6146fe613ae1565b823567ffffffffffffffff81111561471557600080fd5b61472136828601613d26565b8252506020830135602082015261473a60408401613e13565b604082015261474b60608401613e38565b606082015261475c60808401613b88565b608082015261476d60a0840161423e565b60a082015261477e60c0840161423e565b60c082015261478f60e08401613b66565b60e08201526101006147a2818501613bb1565b908201526101206147b484820161423e565b908201526101406147c6848201613e13565b9082015292915050565b6000602082840312156147e257600080fd5b81356137dd81614228565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261482257600080fd5b83018035915067ffffffffffffffff82111561483d57600080fd5b602001915036819003821315613a3f57600080fd5b60006020828403121561486457600080fd5b6137dd82613bb1565b60006020828403121561487f57600080fd5b81356137dd81613b54565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061328160e0830184613f35565b60ff81811683821601908111156138cc576138cc614628565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061498d5761498d61494b565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b80820281158282048414176138cc576138cc614628565b818103818111156138cc576138cc614628565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561270a5761270a614628565b600061012063ffffffff808d1684528b6020850152808b16604085015250806060840152614a958184018a613e7c565b90508281036080840152614aa98189613e7c565b905060ff871660a084015282810360c0840152614ac68187613cc9565b905067ffffffffffffffff851660e0840152828103610100840152614aeb8185613cc9565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a08688031215614b2d57600080fd5b614b3686614afb565b9450602086015193506040860151925060608601519150614b5960808701614afb565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614b8457614b8461494b565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561270a5761270a614628565b6bffffffffffffffffffffffff81811683821602808216919082811461422057614220614628565b808201808211156138cc576138cc614628565b67ffffffffffffffff81811683821601908082111561270a5761270a614628565b600082601f830112614c2257600080fd5b81356020614c326142fe836142b9565b82815260059290921b84018101918181019086841115614c5157600080fd5b8286015b848110156143465780358352918301918301614c55565b600082601f830112614c7d57600080fd5b81356020614c8d6142fe836142b9565b82815260059290921b84018101918181019086841115614cac57600080fd5b8286015b8481101561434657803567ffffffffffffffff811115614cd05760008081fd5b614cde8986838b0101613d26565b845250918301918301614cb0565b600080600080600060a08688031215614d0457600080fd5b853567ffffffffffffffff80821115614d1c57600080fd5b614d2889838a01614c11565b96506020880135915080821115614d3e57600080fd5b614d4a89838a01614c6c565b95506040880135915080821115614d6057600080fd5b614d6c89838a01614c6c565b94506060880135915080821115614d8257600080fd5b614d8e89838a01614c6c565b93506080880135915080821115614da457600080fd5b50614db188828901614c6c565b9150509295509295909350565b600082614dcd57614dcd61494b565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614e198285018b613e7c565b91508382036080850152614e2d828a613e7c565b915060ff881660a085015283820360c0850152614e4a8288613cc9565b90861660e08501528381036101008501529050614aeb8185613cc9565b805161117081613df1565b805161117081613e1e565b805161117081614228565b805161117081613b54565b805161117081613b93565b60006101608284031215614eb157600080fd5b614eb9613ae1565b82518152614ec960208401614e67565b6020820152614eda60408401614e72565b6040820152614eeb60608401614e67565b6060820152614efc60808401614e7d565b6080820152614f0d60a08401614e88565b60a0820152614f1e60c08401614600565b60c0820152614f2f60e08401614600565b60e0820152610100614f42818501614e93565b90820152610120614f54848201614e93565b90820152610140613c9a848201614e88565b64ffffffffff81811683821601908082111561270a5761270a614628565b6000610200808352614f988184018a613cc9565b90508281036020840152614fac8189613cc9565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614ff5905060a0830184613f35565b979650505050505050565b6000806040838503121561501357600080fd5b82516007811061502257600080fd5b6020840151909250613e7181613e1e565b60006020828403121561504557600080fd5b5051919050565b6000835161505e818460208801613ca5565b835190830190615072818360208801613ca5565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", + Bin: "0x60a06040523480156200001157600080fd5b50604051620056d0380380620056d083398101604081905262000034916200046d565b8282828233806000816200008f5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c257620000c28162000139565b5050506001600160a01b038116620000ed57604051632530e88560e11b815260040160405180910390fd5b6001600160a01b03908116608052600b80549183166c01000000000000000000000000026001600160601b039092169190911790556200012d82620001e4565b5050505050506200062c565b336001600160a01b03821603620001935760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000086565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b620001ee62000342565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff16600160f01b026001600160f01b0364ffffffffff909216600160c81b0264ffffffffff60c81b196001600160481b03909416600160801b0293909316600160801b600160f01b031963ffffffff9586166c010000000000000000000000000263ffffffff60601b19978716680100000000000000000297909716600160401b600160801b0319998716640100000000026001600160401b0319909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e0830151610100840151909216600160e01b026001600160e01b0390921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906200033790839062000576565b60405180910390a150565b6200034c6200034e565b565b6000546001600160a01b031633146200034c5760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000086565b80516001600160a01b0381168114620003c257600080fd5b919050565b60405161012081016001600160401b0381118282101715620003f957634e487b7160e01b600052604160045260246000fd5b60405290565b805163ffffffff81168114620003c257600080fd5b80516001600160481b0381168114620003c257600080fd5b805164ffffffffff81168114620003c257600080fd5b805161ffff81168114620003c257600080fd5b80516001600160e01b0381168114620003c257600080fd5b60008060008385036101608112156200048557600080fd5b6200049085620003aa565b935061012080601f1983011215620004a757600080fd5b620004b1620003c7565b9150620004c160208701620003ff565b8252620004d160408701620003ff565b6020830152620004e460608701620003ff565b6040830152620004f760808701620003ff565b60608301526200050a60a0870162000414565b60808301526200051d60c087016200042c565b60a08301526200053060e0870162000442565b60c08301526101006200054581880162000455565b60e084015262000557828801620003ff565b908301525091506200056d6101408501620003aa565b90509250925092565b815163ffffffff908116825260208084015182169083015260408084015182169083015260608084015191821690830152610120820190506080830151620005c960808401826001600160481b03169052565b5060a0830151620005e360a084018264ffffffffff169052565b5060c0830151620005fa60c084018261ffff169052565b5060e08301516200061660e08401826001600160e01b03169052565b506101009283015163ffffffff16919092015290565b60805161505e6200067260003960008181610845015281816109d301528181610ca601528181610f3a01528181611045015281816117890152613490015261505e6000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806381ff7048116100e3578063c3f909d41161008c578063e3d0e71211610066578063e3d0e71214610560578063e4ddcea614610573578063f2fde38b1461058957600080fd5b8063c3f909d4146103b0578063d227d24514610528578063d328a91e1461055857600080fd5b8063a631571e116100bd578063a631571e1461035d578063afcb95d71461037d578063b1dc65a41461039d57600080fd5b806381ff7048146102b557806385b214cf146103225780638da5cb5b1461033557600080fd5b806366316d8d116101455780637f15e1661161011f5780637f15e16614610285578063814118341461029857806381f1b938146102ad57600080fd5b806366316d8d1461026257806379ba5097146102755780637d4807871461027d57600080fd5b8063181f5a7711610176578063181f5a77146101ba5780632a905ccc1461020c57806359b5b7ac1461022e57600080fd5b8063083a5466146101925780631112dadc146101a7575b600080fd5b6101a56101a03660046139d4565b61059c565b005b6101a56101b5366004613b7d565b6105f1565b6101f66040518060400160405280601c81526020017f46756e6374696f6e7320436f6f7264696e61746f722076312e312e300000000081525081565b6040516102039190613ca1565b60405180910390f35b610214610841565b60405168ffffffffffffffffff9091168152602001610203565b61021461023c366004613d42565b50600854700100000000000000000000000000000000900468ffffffffffffffffff1690565b6101a5610270366004613dd1565b6108d7565b6101a5610a90565b6101a5610b92565b6101a56102933660046139d4565b610d92565b6102a0610de2565b6040516102039190613e5b565b6101f6610e51565b6102ff60015460025463ffffffff74010000000000000000000000000000000000000000830481169378010000000000000000000000000000000000000000000000009093041691565b6040805163ffffffff948516815293909216602084015290820152606001610203565b6101a5610330366004613e6e565b610f22565b60005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610203565b61037061036b366004613e87565b610fd4565b6040516102039190613fdc565b604080516001815260006020820181905291810191909152606001610203565b6101a56103ab366004614030565b611175565b61051b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915250604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830152700100000000000000000000000000000000810468ffffffffffffffffff166080830152790100000000000000000000000000000000000000000000000000810464ffffffffff1660a08301527e01000000000000000000000000000000000000000000000000000000000000900461ffff1660c08201526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08301527c0100000000000000000000000000000000000000000000000000000000900490911661010082015290565b60405161020391906140e7565b61053b6105363660046141d7565b611785565b6040516bffffffffffffffffffffffff9091168152602001610203565b6101f66118e5565b6101a561056e3660046142f0565b61193c565b61057b6124b8565b604051908152602001610203565b6101a56105973660046143bd565b612711565b6105a4612725565b60008190036105df576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d6105ec828483614473565b505050565b6105f96127a8565b80516008805460208401516040808601516060870151608088015160a089015160c08a015161ffff167e01000000000000000000000000000000000000000000000000000000000000027dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff64ffffffffff909216790100000000000000000000000000000000000000000000000000027fffff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffff68ffffffffffffffffff90941670010000000000000000000000000000000002939093167fffff0000000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9586166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff9787166801000000000000000002979097167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998716640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909b169c87169c909c1799909917979097169990991793909317959095169390931793909317929092169390931790915560e08301516101008401519092167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff90921691909117600955517f5f32d06f5e83eda3a68e0e964ef2e6af5cb613e8117aa103c2d6bca5f5184862906108369083906140e7565b60405180910390a150565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632a905ccc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d29190614599565b905090565b6108df6127b0565b806bffffffffffffffffffffffff166000036109195750336000908152600a60205260409020546bffffffffffffffffffffffff16610973565b336000908152600a60205260409020546bffffffffffffffffffffffff80831691161015610973576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600a6020526040812080548392906109a09084906bffffffffffffffffffffffff166145e5565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506109f57f000000000000000000000000000000000000000000000000000000000000000090565b6040517f66316d8d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301526bffffffffffffffffffffffff8416602483015291909116906366316d8d90604401600060405180830381600087803b158015610a7457600080fd5b505af1158015610a88573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610b16576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610b9a6127a8565b610ba26127b0565b6000610bac610de2565b905060005b8151811015610d8e576000600a6000848481518110610bd257610bd261460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252810191909152604001600020546bffffffffffffffffffffffff1690508015610d7d576000600a6000858581518110610c3157610c3161460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550610cc87f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff166366316d8d848481518110610cf557610cf561460a565b6020026020010151836040518363ffffffff1660e01b8152600401610d4a92919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b600060405180830381600087803b158015610d6457600080fd5b505af1158015610d78573d6000803e3d6000fd5b505050505b50610d8781614639565b9050610bb1565b5050565b610d9a612725565b6000819003610dd5576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c6105ec828483614473565b60606006805480602002602001604051908101604052809291908181526020018280548015610e4757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610e1c575b5050505050905090565b6060600d8054610e60906143da565b9050600003610e9b576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d8054610ea8906143da565b80601f0160208091040260200160405190810160405280929190818152602001828054610ed4906143da565b8015610e475780601f10610ef657610100808354040283529160200191610e47565b820191906000526020600020905b815481529060010190602001808311610f0457509395945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610f91576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526007602052604080822091909155517f8a4b97add3359bd6bcf5e82874363670eb5ad0f7615abddbd0ed0a3a98f0f416906108369083815260200190565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091523373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461109c576040517fc41a5b0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110ad6110a883614671565b61295c565b90506110bf60608301604084016143bd565b815173ffffffffffffffffffffffffffffffffffffffff91909116907fbf50768ccf13bd0110ca6d53a9c4f1f3271abdd4c24a56878863ed25b20598ff3261110d60c0870160a0880161475e565b61111f610160880161014089016143bd565b611129888061477b565b61113b6101208b016101008c016147e0565b60208b01356111516101008d0160e08e016147fb565b8b60405161116799989796959493929190614818565b60405180910390a35b919050565b60005a604080518b3580825262ffffff6020808f0135600881901c929092169084015293945092917fb04e63db38c49950639fa09d29872f21f5d49d614f3a969d8adf3d4b52e41a62910160405180910390a16111d68a8a8a8a8a8a612dfa565b6003546000906002906111f49060ff808216916101009004166148c0565b6111fe9190614908565b6112099060016148c0565b60ff169050878114611277576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f77726f6e67206e756d626572206f66207369676e6174757265730000000000006044820152606401610b0d565b878614611306576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f7265706f727420727320616e64207373206d757374206265206f66206571756160448201527f6c206c656e6774680000000000000000000000000000000000000000000000006064820152608401610b0d565b3360009081526004602090815260408083208151808301909252805460ff808216845292939192918401916101009091041660028111156113495761134961492a565b600281111561135a5761135a61492a565b90525090506002816020015160028111156113775761137761492a565b141580156113c057506006816000015160ff168154811061139a5761139a61460a565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff163314155b15611427576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f756e617574686f72697a6564207472616e736d697474657200000000000000006044820152606401610b0d565b5050505061143361396c565b6000808a8a604051611446929190614959565b60405190819003812061145d918e90602001614969565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120838301909252600080845290830152915060005b898110156117675760006001848984602081106114c6576114c661460a565b6114d391901a601b6148c0565b8e8e868181106114e5576114e561460a565b905060200201358d8d878181106114fe576114fe61460a565b905060200201356040516000815260200160405260405161153b949392919093845260ff9290921660208401526040830152606082015260800190565b6020604051602081039080840390855afa15801561155d573d6000803e3d6000fd5b5050604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081015173ffffffffffffffffffffffffffffffffffffffff811660009081526004602090815290849020838501909452835460ff808216855292965092945084019161010090041660028111156115dd576115dd61492a565b60028111156115ee576115ee61492a565b905250925060018360200151600281111561160b5761160b61492a565b14611672576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e00006044820152606401610b0d565b8251600090879060ff16601f811061168c5761168c61460a565b602002015173ffffffffffffffffffffffffffffffffffffffff161461170e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6e6f6e2d756e69717565207369676e61747572650000000000000000000000006044820152606401610b0d565b8086846000015160ff16601f81106117285761172861460a565b73ffffffffffffffffffffffffffffffffffffffff90921660209290920201526117536001866148c0565b9450508061176090614639565b90506114a7565b505050611778833383858e8e612eb1565b5050505050505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006040517f10fc49c100000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8816600482015263ffffffff8516602482015273ffffffffffffffffffffffffffffffffffffffff91909116906310fc49c19060440160006040518083038186803b15801561182557600080fd5b505afa158015611839573d6000803e3d6000fd5b5050505066038d7ea4c6800082111561187e576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611888610841565b905060006118cb87878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061023c92505050565b90506118d9858583856130b0565b98975050505050505050565b6060600c80546118f4906143da565b905060000361192f576040517f4f42be3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8054610ea8906143da565b855185518560ff16601f8311156119af576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f746f6f206d616e79207369676e657273000000000000000000000000000000006044820152606401610b0d565b80600003611a19576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f66206d75737420626520706f73697469766500000000000000000000000000006044820152606401610b0d565b818314611aa7576040517f89a61989000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f6f7261636c6520616464726573736573206f7574206f6620726567697374726160448201527f74696f6e000000000000000000000000000000000000000000000000000000006064820152608401610b0d565b611ab281600361497d565b8311611b1a576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661756c74792d6f7261636c65206620746f6f206869676800000000000000006044820152606401610b0d565b611b22612725565b6040805160c0810182528a8152602081018a905260ff89169181018290526060810188905267ffffffffffffffff8716608082015260a0810186905290611b69908861321d565b60055415611d1e57600554600090611b8390600190614994565b9050600060058281548110611b9a57611b9a61460a565b60009182526020822001546006805473ffffffffffffffffffffffffffffffffffffffff90921693509084908110611bd457611bd461460a565b600091825260208083209091015473ffffffffffffffffffffffffffffffffffffffff85811684526004909252604080842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000090811690915592909116808452922080549091169055600580549192509080611c5457611c546149a7565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190556006805480611cbd57611cbd6149a7565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff000000000000000000000000000000000000000016905501905550611b69915050565b60005b8151518110156122d557815180516000919083908110611d4357611d4361460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611dc8576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f7369676e6572206d757374206e6f7420626520656d70747900000000000000006044820152606401610b0d565b600073ffffffffffffffffffffffffffffffffffffffff1682602001518281518110611df657611df661460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1603611e7b576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f7472616e736d6974746572206d757374206e6f7420626520656d7074790000006044820152606401610b0d565b60006004600084600001518481518110611e9757611e9761460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff166002811115611ee157611ee161492a565b14611f48576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f7265706561746564207369676e657220616464726573730000000000000000006044820152606401610b0d565b6040805180820190915260ff82168152600160208201528251805160049160009185908110611f7957611f7961460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000161761010083600281111561201a5761201a61492a565b02179055506000915061202a9050565b60046000846020015184815181106120445761204461460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff16825281019190915260400160002054610100900460ff16600281111561208e5761208e61492a565b146120f5576040517f89a6198900000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f7265706561746564207472616e736d69747465722061646472657373000000006044820152606401610b0d565b6040805180820190915260ff8216815260208101600281525060046000846020015184815181106121285761212861460a565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040016000208251815460ff9091167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082168117835592840151919283917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000016176101008360028111156121c9576121c961492a565b0217905550508251805160059250839081106121e7576121e761460a565b602090810291909101810151825460018101845560009384529282902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90931692909217909155820151805160069190839081106122635761226361460a565b60209081029190910181015182546001810184556000938452919092200180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055806122cd81614639565b915050611d21565b506040810151600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff909216919091179055600180547fffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff8116780100000000000000000000000000000000000000000000000063ffffffff438116820292909217808555920481169291829160149161238d918491740100000000000000000000000000000000000000009004166149d6565b92506101000a81548163ffffffff021916908363ffffffff1602179055506123ec4630600160149054906101000a900463ffffffff1663ffffffff16856000015186602001518760400151886060015189608001518a60a00151613236565b600281905582518051600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010060ff9093169290920291909117905560015460208501516040808701516060880151608089015160a08a015193517f1591690b8638f5fb2dbec82ac741805ac5da8b45dc5263f4875b0496fdce4e05986124a3988b9891977401000000000000000000000000000000000000000090920463ffffffff169690959194919391926149f3565b60405180910390a15050505050505050505050565b604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000080830482166060850152700100000000000000000000000000000000830468ffffffffffffffffff166080850152790100000000000000000000000000000000000000000000000000830464ffffffffff1660a0808601919091527e0100000000000000000000000000000000000000000000000000000000000090930461ffff1660c08501526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08601527c01000000000000000000000000000000000000000000000000000000009004909116610100840152600b5484517ffeaf968c00000000000000000000000000000000000000000000000000000000815294516000958694859490930473ffffffffffffffffffffffffffffffffffffffff169263feaf968c926004808401938290030181865afa158015612646573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061266a9190614aa3565b50935050925050804261267d9190614994565b836020015163ffffffff1610801561269f57506000836020015163ffffffff16115b156126cd57505060e001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16919050565b6000821361270a576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610b0d565b5092915050565b612719612725565b612722816132e1565b50565b60005473ffffffffffffffffffffffffffffffffffffffff1633146127a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610b0d565b565b6127a6612725565b600b546bffffffffffffffffffffffff166000036127ca57565b60006127d4610de2565b80519091506000819003612814576040517f30274b3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b546000906128339083906bffffffffffffffffffffffff16614af3565b905060005b828110156128fe5781600a60008684815181106128575761285761460a565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282829054906101000a90046bffffffffffffffffffffffff166128bf9190614b1e565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806128f790614639565b9050612838565b506129098282614b43565b600b80546000906129299084906bffffffffffffffffffffffff166145e5565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550505050565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152604080516101208101825260085463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c0100000000000000000000000081048316606083015268ffffffffffffffffff700100000000000000000000000000000000820416608083015264ffffffffff79010000000000000000000000000000000000000000000000000082041660a083015261ffff7e01000000000000000000000000000000000000000000000000000000000000909104811660c083018190526009547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff811660e08501527c0100000000000000000000000000000000000000000000000000000000900490931661010080840191909152850151919291161115612b17576040517fdada758700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600854600090700100000000000000000000000000000000900468ffffffffffffffffff1690506000612b548560e001513a8488608001516130b0565b9050806bffffffffffffffffffffffff1685606001516bffffffffffffffffffffffff161015612bb0576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083610100015163ffffffff1642612bc99190614b6b565b905060003087604001518860a001518960c001516001612be99190614b7e565b8a5180516020918201206101008d015160e08e0151604051612c9d98979695948c918c9132910173ffffffffffffffffffffffffffffffffffffffff9a8b168152988a1660208a015267ffffffffffffffff97881660408a0152959096166060880152608087019390935261ffff9190911660a086015263ffffffff90811660c08601526bffffffffffffffffffffffff9190911660e0850152919091166101008301529091166101208201526101400190565b6040516020818303038152906040528051906020012090506040518061016001604052808281526020013073ffffffffffffffffffffffffffffffffffffffff168152602001846bffffffffffffffffffffffff168152602001886040015173ffffffffffffffffffffffffffffffffffffffff1681526020018860a0015167ffffffffffffffff1681526020018860e0015163ffffffff168152602001886080015168ffffffffffffffffff1681526020018568ffffffffffffffffff168152602001866040015163ffffffff1664ffffffffff168152602001866060015163ffffffff1664ffffffffff1681526020018363ffffffff16815250955085604051602001612dac9190613fdc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012060009384526007909252909120555092949350505050565b6000612e0782602061497d565b612e1285602061497d565b612e1e88610144614b6b565b612e289190614b6b565b612e329190614b6b565b612e3d906000614b6b565b9050368114612ea8576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f63616c6c64617461206c656e677468206d69736d6174636800000000000000006044820152606401610b0d565b50505050505050565b600080808080612ec386880188614c7a565b84519499509297509095509350915060ff16801580612ee3575084518114155b80612eef575083518114155b80612efb575082518114155b80612f07575081518114155b15612f6e576040517f660bd4ba00000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4669656c6473206d75737420626520657175616c206c656e67746800000000006044820152606401610b0d565b60005b818110156130a1576000613006888381518110612f9057612f9061460a565b6020026020010151888481518110612faa57612faa61460a565b6020026020010151888581518110612fc457612fc461460a565b6020026020010151888681518110612fde57612fde61460a565b6020026020010151888781518110612ff857612ff861460a565b6020026020010151886133d6565b9050600081600681111561301c5761301c61492a565b1480613039575060018160068111156130375761303761492a565b145b15613090578782815181106130505761305061460a565b60209081029190910181015160405133815290917fc708e0440951fd63499c0f7a73819b469ee5dd3ecc356c0ab4eb7f18389009d9910160405180910390a25b5061309a81614639565b9050612f71565b50505050505050505050505050565b600854600090790100000000000000000000000000000000000000000000000000900464ffffffffff1684101561310b57600854790100000000000000000000000000000000000000000000000000900464ffffffffff1693505b600854600090612710906131259063ffffffff168761497d565b61312f9190614d4c565b6131399086614b6b565b60085490915060009087906131729063ffffffff6c010000000000000000000000008204811691680100000000000000009004166149d6565b61317c91906149d6565b63ffffffff16905060006131c66000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136ea92505050565b905060006131e7826131d8858761497d565b6131e29190614b6b565b61382c565b9050600061320368ffffffffffffffffff808916908a16614b1e565b905061320f8183614b1e565b9a9950505050505050505050565b6000613227610de2565b511115610d8e57610d8e6127b0565b6000808a8a8a8a8a8a8a8a8a60405160200161325a99989796959493929190614d60565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101207dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e01000000000000000000000000000000000000000000000000000000000000179150509998505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff821603613360576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610b0d565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b600080848060200190518101906133ed9190614e2c565b905060003a8261012001518361010001516134089190614ef4565b64ffffffffff16613419919061497d565b905060008460ff166134616000368080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506136ea92505050565b61346b9190614d4c565b9050600061347c6131e28385614b6b565b905060006134893a61382c565b90506000807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663330605298e8e868b60e0015168ffffffffffffffffff16896134e89190614b1e565b338d6040518763ffffffff1660e01b815260040161350b96959493929190614f12565b60408051808303816000875af1158015613529573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061354d9190614f8e565b909250905060008260068111156135665761356661492a565b1480613583575060018260068111156135815761358161492a565b145b156136d95760008e8152600760205260408120556135a18185614b1e565b336000908152600a6020526040812080547fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166bffffffffffffffffffffffff93841617905560e0890151600b805468ffffffffffffffffff9092169390929161360d91859116614b1e565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508d7f90815c2e624694e8010bffad2bcefaf96af282ef1bc2ebc0042d1b89a585e0468487848b60c0015168ffffffffffffffffff168c60e0015168ffffffffffffffffff16878b61368c9190614b1e565b6136969190614b1e565b6136a09190614b1e565b604080516bffffffffffffffffffffffff9586168152602081019490945291841683830152909216606082015290519081900360800190a25b509c9b505050505050505050505050565b6000466136f681613860565b1561377257606c73ffffffffffffffffffffffffffffffffffffffff1663c6f7de0e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613747573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061376b9190614fc1565b9392505050565b61377b81613883565b156138235773420000000000000000000000000000000000000f73ffffffffffffffffffffffffffffffffffffffff166349948e0e8460405180608001604052806048815260200161500a604891396040516020016137db929190614fda565b6040516020818303038152906040526040518263ffffffff1660e01b81526004016138069190613ca1565b602060405180830381865afa158015613747573d6000803e3d6000fd5b50600092915050565b600061385a6138396124b8565b61384b84670de0b6b3a764000061497d565b6138559190614d4c565b6138ca565b92915050565b600061a4b1821480613874575062066eed82145b8061385a57505062066eee1490565b6000600a82148061389557506101a482145b806138a2575062aa37dc82145b806138ae575061210582145b806138bb575062014a3382145b8061385a57505062014a341490565b60006bffffffffffffffffffffffff821115613968576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201527f36206269747300000000000000000000000000000000000000000000000000006064820152608401610b0d565b5090565b604051806103e00160405280601f906020820280368337509192915050565b60008083601f84011261399d57600080fd5b50813567ffffffffffffffff8111156139b557600080fd5b6020830191508360208285010111156139cd57600080fd5b9250929050565b600080602083850312156139e757600080fd5b823567ffffffffffffffff8111156139fe57600080fd5b613a0a8582860161398b565b90969095509350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610120810167ffffffffffffffff81118282101715613a6957613a69613a16565b60405290565b604051610160810167ffffffffffffffff81118282101715613a6957613a69613a16565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613ada57613ada613a16565b604052919050565b63ffffffff8116811461272257600080fd5b803561117081613ae2565b68ffffffffffffffffff8116811461272257600080fd5b803561117081613aff565b64ffffffffff8116811461272257600080fd5b803561117081613b21565b803561ffff8116811461117057600080fd5b80357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461117057600080fd5b60006101208284031215613b9057600080fd5b613b98613a45565b613ba183613af4565b8152613baf60208401613af4565b6020820152613bc060408401613af4565b6040820152613bd160608401613af4565b6060820152613be260808401613b16565b6080820152613bf360a08401613b34565b60a0820152613c0460c08401613b3f565b60c0820152613c1560e08401613b51565b60e0820152610100613c28818501613af4565b908201529392505050565b60005b83811015613c4e578181015183820152602001613c36565b50506000910152565b60008151808452613c6f816020860160208601613c33565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061376b6020830184613c57565b600082601f830112613cc557600080fd5b813567ffffffffffffffff811115613cdf57613cdf613a16565b613d1060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613a93565b818152846020838601011115613d2557600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613d5457600080fd5b813567ffffffffffffffff811115613d6b57600080fd5b613d7784828501613cb4565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461272257600080fd5b803561117081613d7f565b6bffffffffffffffffffffffff8116811461272257600080fd5b803561117081613dac565b60008060408385031215613de457600080fd5b8235613def81613d7f565b91506020830135613dff81613dac565b809150509250929050565b600081518084526020808501945080840160005b83811015613e5057815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e1e565b509495945050505050565b60208152600061376b6020830184613e0a565b600060208284031215613e8057600080fd5b5035919050565b600060208284031215613e9957600080fd5b813567ffffffffffffffff811115613eb057600080fd5b8201610160818503121561376b57600080fd5b805182526020810151613eee602084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040810151613f0e60408401826bffffffffffffffffffffffff169052565b506060810151613f36606084018273ffffffffffffffffffffffffffffffffffffffff169052565b506080810151613f52608084018267ffffffffffffffff169052565b5060a0810151613f6a60a084018263ffffffff169052565b5060c0810151613f8760c084018268ffffffffffffffffff169052565b5060e0810151613fa460e084018268ffffffffffffffffff169052565b506101008181015164ffffffffff9081169184019190915261012080830151909116908301526101409081015163ffffffff16910152565b610160810161385a8284613ec3565b60008083601f840112613ffd57600080fd5b50813567ffffffffffffffff81111561401557600080fd5b6020830191508360208260051b85010111156139cd57600080fd5b60008060008060008060008060e0898b03121561404c57600080fd5b606089018a81111561405d57600080fd5b8998503567ffffffffffffffff8082111561407757600080fd5b6140838c838d0161398b565b909950975060808b013591508082111561409c57600080fd5b6140a88c838d01613feb565b909750955060a08b01359150808211156140c157600080fd5b506140ce8b828c01613feb565b999c989b50969995989497949560c00135949350505050565b815163ffffffff90811682526020808401518216908301526040808401518216908301526060808401519182169083015261012082019050608083015161413b608084018268ffffffffffffffffff169052565b5060a083015161415460a084018264ffffffffff169052565b5060c083015161416a60c084018261ffff169052565b5060e083015161419a60e08401827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff169052565b506101008381015163ffffffff8116848301525b505092915050565b67ffffffffffffffff8116811461272257600080fd5b8035611170816141b6565b6000806000806000608086880312156141ef57600080fd5b85356141fa816141b6565b9450602086013567ffffffffffffffff81111561421657600080fd5b6142228882890161398b565b909550935050604086013561423681613ae2565b949793965091946060013592915050565b600067ffffffffffffffff82111561426157614261613a16565b5060051b60200190565b600082601f83011261427c57600080fd5b8135602061429161428c83614247565b613a93565b82815260059290921b840181019181810190868411156142b057600080fd5b8286015b848110156142d45780356142c781613d7f565b83529183019183016142b4565b509695505050505050565b803560ff8116811461117057600080fd5b60008060008060008060c0878903121561430957600080fd5b863567ffffffffffffffff8082111561432157600080fd5b61432d8a838b0161426b565b9750602089013591508082111561434357600080fd5b61434f8a838b0161426b565b965061435d60408a016142df565b9550606089013591508082111561437357600080fd5b61437f8a838b01613cb4565b945061438d60808a016141cc565b935060a08901359150808211156143a357600080fd5b506143b089828a01613cb4565b9150509295509295509295565b6000602082840312156143cf57600080fd5b813561376b81613d7f565b600181811c908216806143ee57607f821691505b602082108103614427577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156105ec57600081815260208120601f850160051c810160208610156144545750805b601f850160051c820191505b81811015610a8857828155600101614460565b67ffffffffffffffff83111561448b5761448b613a16565b61449f8361449983546143da565b8361442d565b6000601f8411600181146144f157600085156144bb5750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355614587565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b828110156145405786850135825560209485019460019092019101614520565b508682101561457b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b805161117081613aff565b6000602082840312156145ab57600080fd5b815161376b81613aff565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff82811682821603908082111561270a5761270a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361466a5761466a6145b6565b5060010190565b6000610160823603121561468457600080fd5b61468c613a6f565b823567ffffffffffffffff8111156146a357600080fd5b6146af36828601613cb4565b825250602083013560208201526146c860408401613da1565b60408201526146d960608401613dc6565b60608201526146ea60808401613b16565b60808201526146fb60a084016141cc565b60a082015261470c60c084016141cc565b60c082015261471d60e08401613af4565b60e0820152610100614730818501613b3f565b908201526101206147428482016141cc565b90820152610140614754848201613da1565b9082015292915050565b60006020828403121561477057600080fd5b813561376b816141b6565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147b057600080fd5b83018035915067ffffffffffffffff8211156147cb57600080fd5b6020019150368190038213156139cd57600080fd5b6000602082840312156147f257600080fd5b61376b82613b3f565b60006020828403121561480d57600080fd5b813561376b81613ae2565b73ffffffffffffffffffffffffffffffffffffffff8a8116825267ffffffffffffffff8a166020830152881660408201526102406060820181905281018690526000610260878982850137600083890182015261ffff8716608084015260a0830186905263ffffffff851660c0840152601f88017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016830101905061320f60e0830184613ec3565b60ff818116838216019081111561385a5761385a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061491b5761491b6148d9565b8060ff84160491505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183823760009101908152919050565b828152606082602083013760800192915050565b808202811582820484141761385a5761385a6145b6565b8181038181111561385a5761385a6145b6565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b63ffffffff81811683821601908082111561270a5761270a6145b6565b600061012063ffffffff808d1684528b6020850152808b16604085015250806060840152614a238184018a613e0a565b90508281036080840152614a378189613e0a565b905060ff871660a084015282810360c0840152614a548187613c57565b905067ffffffffffffffff851660e0840152828103610100840152614a798185613c57565b9c9b505050505050505050505050565b805169ffffffffffffffffffff8116811461117057600080fd5b600080600080600060a08688031215614abb57600080fd5b614ac486614a89565b9450602086015193506040860151925060608601519150614ae760808701614a89565b90509295509295909350565b60006bffffffffffffffffffffffff80841680614b1257614b126148d9565b92169190910492915050565b6bffffffffffffffffffffffff81811683821601908082111561270a5761270a6145b6565b6bffffffffffffffffffffffff8181168382160280821691908281146141ae576141ae6145b6565b8082018082111561385a5761385a6145b6565b67ffffffffffffffff81811683821601908082111561270a5761270a6145b6565b600082601f830112614bb057600080fd5b81356020614bc061428c83614247565b82815260059290921b84018101918181019086841115614bdf57600080fd5b8286015b848110156142d45780358352918301918301614be3565b600082601f830112614c0b57600080fd5b81356020614c1b61428c83614247565b82815260059290921b84018101918181019086841115614c3a57600080fd5b8286015b848110156142d457803567ffffffffffffffff811115614c5e5760008081fd5b614c6c8986838b0101613cb4565b845250918301918301614c3e565b600080600080600060a08688031215614c9257600080fd5b853567ffffffffffffffff80821115614caa57600080fd5b614cb689838a01614b9f565b96506020880135915080821115614ccc57600080fd5b614cd889838a01614bfa565b95506040880135915080821115614cee57600080fd5b614cfa89838a01614bfa565b94506060880135915080821115614d1057600080fd5b614d1c89838a01614bfa565b93506080880135915080821115614d3257600080fd5b50614d3f88828901614bfa565b9150509295509295909350565b600082614d5b57614d5b6148d9565b500490565b60006101208b835273ffffffffffffffffffffffffffffffffffffffff8b16602084015267ffffffffffffffff808b166040850152816060850152614da78285018b613e0a565b91508382036080850152614dbb828a613e0a565b915060ff881660a085015283820360c0850152614dd88288613c57565b90861660e08501528381036101008501529050614a798185613c57565b805161117081613d7f565b805161117081613dac565b8051611170816141b6565b805161117081613ae2565b805161117081613b21565b60006101608284031215614e3f57600080fd5b614e47613a6f565b82518152614e5760208401614df5565b6020820152614e6860408401614e00565b6040820152614e7960608401614df5565b6060820152614e8a60808401614e0b565b6080820152614e9b60a08401614e16565b60a0820152614eac60c0840161458e565b60c0820152614ebd60e0840161458e565b60e0820152610100614ed0818501614e21565b90820152610120614ee2848201614e21565b90820152610140613c28848201614e16565b64ffffffffff81811683821601908082111561270a5761270a6145b6565b6000610200808352614f268184018a613c57565b90508281036020840152614f3a8189613c57565b6bffffffffffffffffffffffff88811660408601528716606085015273ffffffffffffffffffffffffffffffffffffffff861660808501529150614f83905060a0830184613ec3565b979650505050505050565b60008060408385031215614fa157600080fd5b825160078110614fb057600080fd5b6020840151909250613dff81613dac565b600060208284031215614fd357600080fd5b5051919050565b60008351614fec818460208801613c33565b835190830190615000818360208801613c33565b0194935050505056fe307866666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666a164736f6c6343000813000a", } var FunctionsCoordinatorABI = FunctionsCoordinatorMetaData.ABI diff --git a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 93c4e64a3af..ac1fc4e83d6 100644 --- a/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/functions/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -4,7 +4,7 @@ functions_allow_list: ../../../contracts/solc/v0.8.19/functions/v1_X/TermsOfServ functions_billing_registry_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsBillingRegistryEventsMock.bin 50deeb883bd9c3729702be335c0388f9d8553bab4be5e26ecacac496a89e2b77 functions_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClient.bin 2368f537a04489c720a46733f8596c4fc88a31062ecfa966d05f25dd98608aca functions_client_example: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsClientExample.bin abf32e69f268f40e8530eb8d8e96bf310b798a4c0049a58022d9d2fb527b601b -functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 4e05ca5e624b7a1e604b81b84bc088818b376d533f556ba1c2ee586b7eb38b68 +functions_coordinator: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsCoordinator.bin 97aa7c56d78c703056990eff102279af86b97b11b5855b059e8dd658dc15da8a functions_load_test_client: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsLoadTestClient.bin c8dbbd5ebb34435800d6674700068837c3a252db60046a14b0e61e829db517de functions_oracle_events_mock: ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.abi ../../../contracts/solc/v0.8.6/functions/v0_0_0/FunctionsOracleEventsMock.bin 3ca70f966f8fe751987f0ccb50bebb6aa5be77e4a9f835d1ae99e0e9bfb7d52c functions_router: ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.abi ../../../contracts/solc/v0.8.19/functions/v1_X/FunctionsRouter.bin 9dedd3a36043605fd9bedf821e7ec5b4281a5c7ae2e4a1955f37aff8ba13519f From aa0223133f392f0f02b4e1e563347ad4b3a58afc Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:15:46 -0600 Subject: [PATCH 140/271] Decouple job pipeline tables from the TXM DB (#11173) * Decoupled job pipeline tables from the TXM DB * Updated errors to return better context * Updated method comment --- common/txmgr/broadcaster.go | 7 +- common/txmgr/confirmer.go | 6 +- common/txmgr/types/mocks/tx_store.go | 66 ++++++++------ common/txmgr/types/tx.go | 8 ++ common/txmgr/types/tx_store.go | 8 +- core/chains/evm/txmgr/broadcaster_test.go | 1 + core/chains/evm/txmgr/confirmer_test.go | 48 +++++++++-- core/chains/evm/txmgr/evm_tx_store.go | 47 +++++++--- core/chains/evm/txmgr/evm_tx_store_test.go | 85 ++++++++++++++++--- core/chains/evm/txmgr/mocks/evm_tx_store.go | 66 ++++++++------ core/services/pipeline/orm.go | 6 +- core/services/pipeline/runner.go | 2 +- core/services/pipeline/task.eth_tx.go | 1 + core/services/pipeline/task.eth_tx_test.go | 8 ++ ...resume_pipeline_task_flags_to_evm_txes.sql | 15 ++++ 15 files changed, 281 insertions(+), 93 deletions(-) create mode 100644 core/store/migrate/migrations/0209_add_resume_pipeline_task_flags_to_evm_txes.sql diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index d68b5091011..00522abf229 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -775,12 +775,17 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) save // Now we have an errored pipeline even though the tx succeeded. This case // is relatively benign and probably nobody will ever run into it in // practice, but something to be aware of. - if etx.PipelineTaskRunID.Valid && eb.resumeCallback != nil { + if etx.PipelineTaskRunID.Valid && eb.resumeCallback != nil && etx.SignalCallback { err := eb.resumeCallback(etx.PipelineTaskRunID.UUID, nil, errors.Errorf("fatal error while sending transaction: %s", etx.Error.String)) if errors.Is(err, sql.ErrNoRows) { lgr.Debugw("callback missing or already resumed", "etxID", etx.ID) } else if err != nil { return errors.Wrap(err, "failed to resume pipeline") + } else { + // Mark tx as having completed callback + if err := eb.txStore.UpdateTxCallbackCompleted(ctx, etx.PipelineTaskRunID.UUID, eb.chainID); err != nil { + return err + } } } return eb.txStore.UpdateTxFatalError(ctx, etx) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 1d7446d9d2d..afb2b3003a1 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -1083,7 +1083,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) sen // ResumePendingTaskRuns issues callbacks to task runs that are pending waiting for receipts func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ResumePendingTaskRuns(ctx context.Context, head types.Head[BLOCK_HASH]) error { - receiptsPlus, err := ec.txStore.FindReceiptsPendingConfirmation(ctx, head.BlockNumber(), ec.chainID) + receiptsPlus, err := ec.txStore.FindTxesPendingCallback(ctx, head.BlockNumber(), ec.chainID) if err != nil { return err @@ -1105,6 +1105,10 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Res ec.lggr.Debugw("Callback: resuming tx with receipt", "output", output, "taskErr", taskErr, "pipelineTaskRunID", data.ID) if err := ec.resumeCallback(data.ID, output, taskErr); err != nil { + return fmt.Errorf("failed to resume suspended pipeline run: %w", err) + } + // Mark tx as having completed callback + if err := ec.txStore.UpdateTxCallbackCompleted(ctx, data.ID, ec.chainID); err != nil { return err } } diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index 7da51de606b..0e344b9b6f9 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -180,32 +180,6 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindNextUns return r0 } -// FindReceiptsPendingConfirmation provides a mock function with given fields: ctx, blockNum, chainID -func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindReceiptsPendingConfirmation(ctx context.Context, blockNum int64, chainID CHAIN_ID) ([]txmgrtypes.ReceiptPlus[R], error) { - ret := _m.Called(ctx, blockNum, chainID) - - var r0 []txmgrtypes.ReceiptPlus[R] - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, CHAIN_ID) ([]txmgrtypes.ReceiptPlus[R], error)); ok { - return rf(ctx, blockNum, chainID) - } - if rf, ok := ret.Get(0).(func(context.Context, int64, CHAIN_ID) []txmgrtypes.ReceiptPlus[R]); ok { - r0 = rf(ctx, blockNum, chainID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]txmgrtypes.ReceiptPlus[R]) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int64, CHAIN_ID) error); ok { - r1 = rf(ctx, blockNum, chainID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // FindTransactionsConfirmedInBlockRange provides a mock function with given fields: ctx, highBlockNumber, lowBlockNumber, chainID func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber int64, lowBlockNumber int64, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, highBlockNumber, lowBlockNumber, chainID) @@ -388,6 +362,32 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesByM return r0, r1 } +// FindTxesPendingCallback provides a mock function with given fields: ctx, blockNum, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID CHAIN_ID) ([]txmgrtypes.ReceiptPlus[R], error) { + ret := _m.Called(ctx, blockNum, chainID) + + var r0 []txmgrtypes.ReceiptPlus[R] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, CHAIN_ID) ([]txmgrtypes.ReceiptPlus[R], error)); ok { + return rf(ctx, blockNum, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, CHAIN_ID) []txmgrtypes.ReceiptPlus[R]); ok { + r0 = rf(ctx, blockNum, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]txmgrtypes.ReceiptPlus[R]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, CHAIN_ID) error); ok { + r1 = rf(ctx, blockNum, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindTxesWithAttemptsAndReceiptsByIdsAndState provides a mock function with given fields: ctx, ids, states, chainID func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, ids, states, chainID) @@ -814,6 +814,20 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxAtt return r0 } +// UpdateTxCallbackCompleted provides a mock function with given fields: ctx, pipelineTaskRunRid, chainId +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId CHAIN_ID) error { + ret := _m.Called(ctx, pipelineTaskRunRid, chainId) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, CHAIN_ID) error); ok { + r0 = rf(ctx, pipelineTaskRunRid, chainId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateTxFatalError provides a mock function with given fields: ctx, etx func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) UpdateTxFatalError(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx) diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index d95f07afabc..11017bd0325 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -91,6 +91,9 @@ type TxRequest[ADDR types.Hashable, TX_HASH types.Hashable] struct { // Checker defines the check that should be run before a transaction is submitted on chain. Checker TransmitCheckerSpec[ADDR] + + // Mark tx requiring callback + SignalCallback bool } // TransmitCheckerSpec defines the check that should be performed before a transaction is submitted @@ -217,6 +220,11 @@ type Tx[ // TransmitChecker defines the check that should be performed before a transaction is submitted on // chain. TransmitChecker *datatypes.JSON + + // Marks tx requiring callback + SignalCallback bool + // Marks tx callback as signaled + CallbackCompleted bool } func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetError() error { diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index 83cb4b85ee6..c2dfeee4146 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -35,8 +35,10 @@ type TxStore[ TxHistoryReaper[CHAIN_ID] TransactionStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, SEQ, FEE] - // methods for saving & retreiving receipts - FindReceiptsPendingConfirmation(ctx context.Context, blockNum int64, chainID CHAIN_ID) (receiptsPlus []ReceiptPlus[R], err error) + // Find confirmed txes beyond the minConfirmations param that require callback but have not yet been signaled + FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID CHAIN_ID) (receiptsPlus []ReceiptPlus[R], err error) + // Update tx to mark that its callback has been signaled + UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId CHAIN_ID) error SaveFetchedReceipts(ctx context.Context, receipts []R, chainID CHAIN_ID) (err error) // additional methods for tx store management @@ -93,6 +95,8 @@ type TransactionStore[ SetBroadcastBeforeBlockNum(ctx context.Context, blockNum int64, chainID CHAIN_ID) error UpdateBroadcastAts(ctx context.Context, now time.Time, etxIDs []int64) error UpdateTxAttemptInProgressToBroadcast(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], NewAttemptState TxAttemptState) error + // Update tx to mark that its callback has been signaled + UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId CHAIN_ID) error UpdateTxsUnconfirmed(ctx context.Context, ids []int64) error UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt *TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error UpdateTxFatalError(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index fcbc7a1f4c2..460f9629fb8 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -1055,6 +1055,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { FeeLimit: gasLimit, State: txmgrcommon.TxUnstarted, PipelineTaskRunID: uuid.NullUUID{UUID: tr.ID, Valid: true}, + SignalCallback: true, } t.Run("with erroring callback bails out", func(t *testing.T) { diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 1385250a206..3a0d33f7ba0 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -2933,11 +2933,12 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 1, 1, fromAddress) cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) - pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2 WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + // Setting both signal_callback and callback_completed to TRUE to simulate a completed pipeline task + // It would only be in a state past suspended if the resume callback was called and callback_completed was set to TRUE + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE, callback_completed = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) err := ec.ResumePendingTaskRuns(testutils.Context(t), &head) require.NoError(t, err) - }) t.Run("doesn't process task runs where the receipt is younger than minConfirmations", func(t *testing.T) { @@ -2952,15 +2953,15 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 2, 1, fromAddress) cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, etx.TxAttempts[0].Hash) - pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2 WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) err := ec.ResumePendingTaskRuns(testutils.Context(t), &head) require.NoError(t, err) - }) t.Run("processes eth_txes with receipts older than minConfirmations", func(t *testing.T) { ch := make(chan interface{}) + nonce := evmtypes.Nonce(3) var err error ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { err = thisErr @@ -2972,15 +2973,19 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) pgtest.MustExec(t, db, `UPDATE pipeline_runs SET state = 'suspended' WHERE id = $1`, run.ID) - etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 3, 1, fromAddress) + etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, int64(nonce), 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": true}'`) receipt := cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) - pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2 WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) go func() { err2 := ec.ResumePendingTaskRuns(testutils.Context(t), &head) require.NoError(t, err2) + // Retrieve Tx to check if callback completed flag was set to true + updateTx, err3 := txStore.FindTxWithSequence(testutils.Context(t), fromAddress, nonce) + require.NoError(t, err3) + require.Equal(t, true, updateTx.CallbackCompleted) }() select { @@ -3000,6 +3005,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { t.Run("processes eth_txes with receipt older than minConfirmations that reverted", func(t *testing.T) { ch := make(chan interface{}) + nonce := evmtypes.Nonce(4) var err error ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { err = thisErr @@ -3011,17 +3017,21 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) pgtest.MustExec(t, db, `UPDATE pipeline_runs SET state = 'suspended' WHERE id = $1`, run.ID) - etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 4, 1, fromAddress) + etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, int64(nonce), 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": true}'`) // receipt is not passed through as a value since it reverted and caused an error cltest.MustInsertRevertedEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) - pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2 WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) go func() { err2 := ec.ResumePendingTaskRuns(testutils.Context(t), &head) require.NoError(t, err2) + // Retrieve Tx to check if callback completed flag was set to true + updateTx, err3 := txStore.FindTxWithSequence(testutils.Context(t), fromAddress, nonce) + require.NoError(t, err3) + require.Equal(t, true, updateTx.CallbackCompleted) }() select { @@ -3036,6 +3046,28 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { t.Fatal("no value received") } }) + + t.Run("does not mark callback complete if callback fails", func(t *testing.T) { + nonce := evmtypes.Nonce(5) + ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { + return errors.New("error") + }) + + run := cltest.MustInsertPipelineRun(t, db) + tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) + + etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, int64(nonce), 1, fromAddress) + cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) + + err := ec.ResumePendingTaskRuns(testutils.Context(t), &head) + require.Error(t, err) + + // Retrieve Tx to check if callback completed flag was left unchanged + updateTx, err := txStore.FindTxWithSequence(testutils.Context(t), fromAddress, nonce) + require.NoError(t, err) + require.Equal(t, false, updateTx.CallbackCompleted) + }) } func ptr[T any](t T) *T { return &t } diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 4db7989b466..c3371fee80b 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -115,7 +115,7 @@ type rawOnchainReceipt = evmtypes.Receipt // Does not map to a single database table. // It's comprised of fields from different tables. type dbReceiptPlus struct { - ID uuid.UUID `db:"id"` + ID uuid.UUID `db:"pipeline_task_run_id"` Receipt evmtypes.Receipt `db:"receipt"` FailOnRevert bool `db:"FailOnRevert"` } @@ -180,6 +180,10 @@ type DbEthTx struct { // chain. TransmitChecker *datatypes.JSON InitialBroadcastAt *time.Time + // Marks tx requiring callback + SignalCallback bool + // Marks tx callback as signaled + CallbackCompleted bool } func (db *DbEthTx) FromTx(tx *Tx) { @@ -200,6 +204,8 @@ func (db *DbEthTx) FromTx(tx *Tx) { db.MinConfirmations = tx.MinConfirmations db.TransmitChecker = tx.TransmitChecker db.InitialBroadcastAt = tx.InitialBroadcastAt + db.SignalCallback = tx.SignalCallback + db.CallbackCompleted = tx.CallbackCompleted if tx.ChainID != nil { db.EVMChainID = *utils.NewBig(tx.ChainID) @@ -233,6 +239,8 @@ func (db DbEthTx) ToTx(tx *Tx) { tx.ChainID = db.EVMChainID.ToInt() tx.TransmitChecker = db.TransmitChecker tx.InitialBroadcastAt = db.InitialBroadcastAt + tx.SignalCallback = db.SignalCallback + tx.CallbackCompleted = db.CallbackCompleted } func dbEthTxsToEvmEthTxs(dbEthTxs []DbEthTx) []Tx { @@ -512,8 +520,8 @@ func (o *evmTxStore) InsertTx(etx *Tx) error { if etx.CreatedAt == (time.Time{}) { etx.CreatedAt = time.Now() } - const insertEthTxSQL = `INSERT INTO evm.txes (nonce, from_address, to_address, encoded_payload, value, gas_limit, error, broadcast_at, initial_broadcast_at, created_at, state, meta, subject, pipeline_task_run_id, min_confirmations, evm_chain_id, transmit_checker, idempotency_key) VALUES ( -:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit, :error, :broadcast_at, :initial_broadcast_at, :created_at, :state, :meta, :subject, :pipeline_task_run_id, :min_confirmations, :evm_chain_id, :transmit_checker, :idempotency_key + const insertEthTxSQL = `INSERT INTO evm.txes (nonce, from_address, to_address, encoded_payload, value, gas_limit, error, broadcast_at, initial_broadcast_at, created_at, state, meta, subject, pipeline_task_run_id, min_confirmations, evm_chain_id, transmit_checker, idempotency_key, signal_callback, callback_completed) VALUES ( +:nonce, :from_address, :to_address, :encoded_payload, :value, :gas_limit, :error, :broadcast_at, :initial_broadcast_at, :created_at, :state, :meta, :subject, :pipeline_task_run_id, :min_confirmations, :evm_chain_id, :transmit_checker, :idempotency_key, :signal_callback, :callback_completed ) RETURNING *` var dbTx DbEthTx dbTx.FromTx(etx) @@ -941,25 +949,40 @@ WHERE evm.tx_attempts.state = 'in_progress' AND evm.txes.from_address = $1 AND e return attempts, pkgerrors.Wrap(err, "getInProgressEthTxAttempts failed") } -func (o *evmTxStore) FindReceiptsPendingConfirmation(ctx context.Context, blockNum int64, chainID *big.Int) (receiptsPlus []ReceiptPlus, err error) { +// Find confirmed txes requiring callback but have not yet been signaled +func (o *evmTxStore) FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID *big.Int) (receiptsPlus []ReceiptPlus, err error) { var rs []dbReceiptPlus var cancel context.CancelFunc ctx, cancel = o.mergeContexts(ctx) defer cancel() err = o.q.SelectContext(ctx, &rs, ` - SELECT pipeline_task_runs.id, evm.receipts.receipt, COALESCE((evm.txes.meta->>'FailOnRevert')::boolean, false) "FailOnRevert" FROM pipeline_task_runs - INNER JOIN pipeline_runs ON pipeline_runs.id = pipeline_task_runs.pipeline_run_id - INNER JOIN evm.txes ON evm.txes.pipeline_task_run_id = pipeline_task_runs.id + SELECT evm.txes.pipeline_task_run_id, evm.receipts.receipt, COALESCE((evm.txes.meta->>'FailOnRevert')::boolean, false) "FailOnRevert" FROM evm.txes INNER JOIN evm.tx_attempts ON evm.txes.id = evm.tx_attempts.eth_tx_id INNER JOIN evm.receipts ON evm.tx_attempts.hash = evm.receipts.tx_hash - WHERE pipeline_runs.state = 'suspended' AND evm.receipts.block_number <= ($1 - evm.txes.min_confirmations) AND evm.txes.evm_chain_id = $2 + WHERE evm.txes.pipeline_task_run_id IS NOT NULL AND evm.txes.signal_callback = TRUE AND evm.txes.callback_completed = FALSE + AND evm.receipts.block_number <= ($1 - evm.txes.min_confirmations) AND evm.txes.evm_chain_id = $2 `, blockNum, chainID.String()) - + if err != nil { + return nil, fmt.Errorf("failed to retrieve transactions pending pipeline resume callback: %w", err) + } receiptsPlus = fromDBReceiptsPlus(rs) return } +// Update tx to mark that its callback has been signaled +func (o *evmTxStore) UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunId uuid.UUID, chainId *big.Int) error { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + _, err := qq.Exec(`UPDATE evm.txes SET callback_completed = TRUE WHERE pipeline_task_run_id = $1 AND evm_chain_id = $2`, pipelineTaskRunId, chainId.String()) + if err != nil { + return fmt.Errorf("failed to mark callback completed for transaction: %w", err) + } + return nil +} + func (o *evmTxStore) FindLatestSequence(ctx context.Context, fromAddress common.Address, chainId *big.Int) (nonce evmtypes.Nonce, err error) { var cancel context.CancelFunc ctx, cancel = o.mergeContexts(ctx) @@ -1661,12 +1684,12 @@ func (o *evmTxStore) CreateTransaction(ctx context.Context, txRequest TxRequest, } } err = tx.Get(&dbEtx, ` -INSERT INTO evm.txes (from_address, to_address, encoded_payload, value, gas_limit, state, created_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id, transmit_checker, idempotency_key) +INSERT INTO evm.txes (from_address, to_address, encoded_payload, value, gas_limit, state, created_at, meta, subject, evm_chain_id, min_confirmations, pipeline_task_run_id, transmit_checker, idempotency_key, signal_callback) VALUES ( -$1,$2,$3,$4,$5,'unstarted',NOW(),$6,$7,$8,$9,$10,$11,$12 +$1,$2,$3,$4,$5,'unstarted',NOW(),$6,$7,$8,$9,$10,$11,$12,$13 ) RETURNING "txes".* -`, txRequest.FromAddress, txRequest.ToAddress, txRequest.EncodedPayload, assets.Eth(txRequest.Value), txRequest.FeeLimit, txRequest.Meta, txRequest.Strategy.Subject(), chainID.String(), txRequest.MinConfirmations, txRequest.PipelineTaskRunID, txRequest.Checker, txRequest.IdempotencyKey) +`, txRequest.FromAddress, txRequest.ToAddress, txRequest.EncodedPayload, assets.Eth(txRequest.Value), txRequest.FeeLimit, txRequest.Meta, txRequest.Strategy.Subject(), chainID.String(), txRequest.MinConfirmations, txRequest.PipelineTaskRunID, txRequest.Checker, txRequest.IdempotencyKey, txRequest.SignalCallback) if err != nil { return pkgerrors.Wrap(err, "CreateEthTransaction failed to insert evm tx") } diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index ba02f118cf5..f8798f9f836 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -617,7 +618,7 @@ func TestORM_GetInProgressTxAttempts(t *testing.T) { assert.Equal(t, etx.TxAttempts[0].ID, attempts[0].ID) } -func TestORM_FindReceiptsPendingConfirmation(t *testing.T) { +func TestORM_FindTxesPendingCallback(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) @@ -645,21 +646,50 @@ func TestORM_FindReceiptsPendingConfirmation(t *testing.T) { minConfirmations := int64(2) - run := cltest.MustInsertPipelineRun(t, db) - tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) - pgtest.MustExec(t, db, `UPDATE pipeline_runs SET state = 'suspended' WHERE id = $1`, run.ID) - - etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 3, 1, fromAddress) + // Suspended run waiting for callback + run1 := cltest.MustInsertPipelineRun(t, db) + tr1 := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run1.ID) + pgtest.MustExec(t, db, `UPDATE pipeline_runs SET state = 'suspended' WHERE id = $1`, run1.ID) + etx1 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 3, 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": true}'`) - attempt := etx.TxAttempts[0] - cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt.Hash) - - pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2 WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) - - receiptsPlus, err := txStore.FindReceiptsPendingConfirmation(testutils.Context(t), head.Number, ethClient.ConfiguredChainID()) + attempt1 := etx1.TxAttempts[0] + cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt1.Hash) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr1.ID, minConfirmations, etx1.ID) + + // Callback to pipeline service completed. Should be ignored + run2 := cltest.MustInsertPipelineRunWithStatus(t, db, 0, pipeline.RunStatusCompleted) + tr2 := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run2.ID) + etx2 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 4, 1, fromAddress) + pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": false}'`) + attempt2 := etx2.TxAttempts[0] + cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt2.Hash) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE, callback_completed = TRUE WHERE id = $3`, &tr2.ID, minConfirmations, etx2.ID) + + // Suspended run younger than minConfirmations. Should be ignored + run3 := cltest.MustInsertPipelineRun(t, db) + tr3 := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run3.ID) + pgtest.MustExec(t, db, `UPDATE pipeline_runs SET state = 'suspended' WHERE id = $1`, run3.ID) + etx3 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 5, 1, fromAddress) + pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": false}'`) + attempt3 := etx3.TxAttempts[0] + cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt3.Hash) + pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr3.ID, minConfirmations, etx3.ID) + + // Tx not marked for callback. Should be ignore + etx4 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 6, 1, fromAddress) + attempt4 := etx4.TxAttempts[0] + cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt4.Hash) + pgtest.MustExec(t, db, `UPDATE evm.txes SET min_confirmations = $1 WHERE id = $2`, minConfirmations, etx4.ID) + + // Unconfirmed Tx without receipts. Should be ignored + etx5 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 7, 1, fromAddress) + pgtest.MustExec(t, db, `UPDATE evm.txes SET min_confirmations = $1 WHERE id = $2`, minConfirmations, etx5.ID) + + // Search evm.txes table for tx requiring callback + receiptsPlus, err := txStore.FindTxesPendingCallback(testutils.Context(t), head.Number, ethClient.ConfiguredChainID()) require.NoError(t, err) assert.Len(t, receiptsPlus, 1) - assert.Equal(t, tr.ID, receiptsPlus[0].ID) + assert.Equal(t, tr1.ID, receiptsPlus[0].ID) } func Test_FindTxWithIdempotencyKey(t *testing.T) { @@ -1569,6 +1599,35 @@ func TestORM_CreateTransaction(t *testing.T) { assert.Equal(t, tx1.GetID(), tx2.GetID()) }) + + t.Run("sets signal callback flag", func(t *testing.T) { + subject := uuid.New() + strategy := newMockTxStrategy(t) + strategy.On("Subject").Return(uuid.NullUUID{UUID: subject, Valid: true}) + strategy.On("PruneQueue", mock.Anything, mock.AnythingOfType("*txmgr.evmTxStore")).Return(int64(0), nil) + etx, err := txStore.CreateTransaction(testutils.Context(t), txmgr.TxRequest{ + FromAddress: fromAddress, + ToAddress: toAddress, + EncodedPayload: payload, + FeeLimit: gasLimit, + Meta: nil, + Strategy: strategy, + SignalCallback: true, + }, ethClient.ConfiguredChainID()) + assert.NoError(t, err) + + assert.Greater(t, etx.ID, int64(0)) + assert.Equal(t, fromAddress, etx.FromAddress) + assert.Equal(t, true, etx.SignalCallback) + + cltest.AssertCount(t, db, "evm.txes", 3) + + var dbEthTx txmgr.DbEthTx + require.NoError(t, db.Get(&dbEthTx, `SELECT * FROM evm.txes ORDER BY id DESC LIMIT 1`)) + + assert.Equal(t, fromAddress, dbEthTx.FromAddress) + assert.Equal(t, true, dbEthTx.SignalCallback) + }) } func TestORM_PruneUnstartedTxQueue(t *testing.T) { diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index 4632a8ae342..f491bda40bb 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -183,32 +183,6 @@ func (_m *EvmTxStore) FindNextUnstartedTransactionFromAddress(ctx context.Contex return r0 } -// FindReceiptsPendingConfirmation provides a mock function with given fields: ctx, blockNum, chainID -func (_m *EvmTxStore) FindReceiptsPendingConfirmation(ctx context.Context, blockNum int64, chainID *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error) { - ret := _m.Called(ctx, blockNum, chainID) - - var r0 []types.ReceiptPlus[*evmtypes.Receipt] - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error)); ok { - return rf(ctx, blockNum, chainID) - } - if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) []types.ReceiptPlus[*evmtypes.Receipt]); ok { - r0 = rf(ctx, blockNum, chainID) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]types.ReceiptPlus[*evmtypes.Receipt]) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, int64, *big.Int) error); ok { - r1 = rf(ctx, blockNum, chainID) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // FindTransactionsConfirmedInBlockRange provides a mock function with given fields: ctx, highBlockNumber, lowBlockNumber, chainID func (_m *EvmTxStore) FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber int64, lowBlockNumber int64, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, highBlockNumber, lowBlockNumber, chainID) @@ -493,6 +467,32 @@ func (_m *EvmTxStore) FindTxesByMetaFieldAndStates(ctx context.Context, metaFiel return r0, r1 } +// FindTxesPendingCallback provides a mock function with given fields: ctx, blockNum, chainID +func (_m *EvmTxStore) FindTxesPendingCallback(ctx context.Context, blockNum int64, chainID *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error) { + ret := _m.Called(ctx, blockNum, chainID) + + var r0 []types.ReceiptPlus[*evmtypes.Receipt] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) ([]types.ReceiptPlus[*evmtypes.Receipt], error)); ok { + return rf(ctx, blockNum, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, *big.Int) []types.ReceiptPlus[*evmtypes.Receipt]); ok { + r0 = rf(ctx, blockNum, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.ReceiptPlus[*evmtypes.Receipt]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, *big.Int) error); ok { + r1 = rf(ctx, blockNum, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindTxesWithAttemptsAndReceiptsByIdsAndState provides a mock function with given fields: ctx, ids, states, chainID func (_m *EvmTxStore) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []types.TxState, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, ids, states, chainID) @@ -1018,6 +1018,20 @@ func (_m *EvmTxStore) UpdateTxAttemptInProgressToBroadcast(ctx context.Context, return r0 } +// UpdateTxCallbackCompleted provides a mock function with given fields: ctx, pipelineTaskRunRid, chainId +func (_m *EvmTxStore) UpdateTxCallbackCompleted(ctx context.Context, pipelineTaskRunRid uuid.UUID, chainId *big.Int) error { + ret := _m.Called(ctx, pipelineTaskRunRid, chainId) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, *big.Int) error); ok { + r0 = rf(ctx, pipelineTaskRunRid, chainId) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateTxFatalError provides a mock function with given fields: ctx, etx func (_m *EvmTxStore) UpdateTxFatalError(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx) diff --git a/core/services/pipeline/orm.go b/core/services/pipeline/orm.go index d60050700f7..056a7deab28 100644 --- a/core/services/pipeline/orm.go +++ b/core/services/pipeline/orm.go @@ -306,13 +306,13 @@ func (o *orm) UpdateTaskRunResult(taskID uuid.UUID, result Result) (run Run, sta WHERE pipeline_task_runs.id = $1 AND pipeline_runs.state in ('running', 'suspended') FOR UPDATE` if err = tx.Get(&run, sql, taskID); err != nil { - return err + return fmt.Errorf("failed to find pipeline run for ID %s: %w", taskID.String(), err) } // Update the task with result sql = `UPDATE pipeline_task_runs SET output = $2, error = $3, finished_at = $4 WHERE id = $1` if _, err = tx.Exec(sql, taskID, result.OutputDB(), result.ErrorDB(), time.Now()); err != nil { - return errors.Wrap(err, "UpdateTaskRunResult") + return fmt.Errorf("failed to update pipeline task run: %w", err) } if run.State == RunStatusSuspended { @@ -321,7 +321,7 @@ func (o *orm) UpdateTaskRunResult(taskID uuid.UUID, result Result) (run Run, sta sql = `UPDATE pipeline_runs SET state = $2 WHERE id = $1` if _, err = tx.Exec(sql, run.ID, run.State); err != nil { - return errors.Wrap(err, "UpdateTaskRunResult") + return fmt.Errorf("failed to update pipeline run state: %w", err) } } diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 3dbe94747e7..d33913b4753 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -609,7 +609,7 @@ func (r *runner) ResumeRun(taskID uuid.UUID, value interface{}, err error) error Error: err, }) if err != nil { - return err + return fmt.Errorf("failed to update task run result: %w", err) } // TODO: Should probably replace this with a listener to update events diff --git a/core/services/pipeline/task.eth_tx.go b/core/services/pipeline/task.eth_tx.go index 57f1c0a7ed8..384c86446e7 100644 --- a/core/services/pipeline/task.eth_tx.go +++ b/core/services/pipeline/task.eth_tx.go @@ -155,6 +155,7 @@ func (t *ETHTxTask) Run(ctx context.Context, lggr logger.Logger, vars Vars, inpu ForwarderAddress: forwarderAddress, Strategy: strategy, Checker: transmitChecker, + SignalCallback: true, } if minOutgoingConfirmations > 0 { diff --git a/core/services/pipeline/task.eth_tx_test.go b/core/services/pipeline/task.eth_tx_test.go index e5f50bc29e5..a0ff54d4448 100644 --- a/core/services/pipeline/task.eth_tx_test.go +++ b/core/services/pipeline/task.eth_tx_test.go @@ -95,6 +95,7 @@ func TestETHTxTask(t *testing.T) { CheckerType: txmgr.TransmitCheckerTypeVRFV2, VRFCoordinatorAddress: &addr, }, + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -138,6 +139,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: gasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -215,6 +217,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: gasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -260,6 +263,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: gasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -290,6 +294,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: gasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -324,6 +329,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: drJobTypeGasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -358,6 +364,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: specGasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, nil) }, nil, nil, "", pipeline.RunInfo{}, @@ -423,6 +430,7 @@ func TestETHTxTask(t *testing.T) { FeeLimit: gasLimit, Meta: txMeta, Strategy: txmgrcommon.NewSendEveryStrategy(), + SignalCallback: true, }).Return(txmgr.Tx{}, errors.New("uh oh")) }, nil, pipeline.ErrTaskRunFailed, "while creating transaction", pipeline.RunInfo{IsRetryable: true}, diff --git a/core/store/migrate/migrations/0209_add_resume_pipeline_task_flags_to_evm_txes.sql b/core/store/migrate/migrations/0209_add_resume_pipeline_task_flags_to_evm_txes.sql new file mode 100644 index 00000000000..dbe7e91b9f6 --- /dev/null +++ b/core/store/migrate/migrations/0209_add_resume_pipeline_task_flags_to_evm_txes.sql @@ -0,0 +1,15 @@ +-- +goose Up +ALTER TABLE evm.txes ADD COLUMN "signal_callback" BOOL DEFAULT FALSE; +ALTER TABLE evm.txes ADD COLUMN "callback_completed" BOOL DEFAULT FALSE; + +UPDATE evm.txes +SET signal_callback = TRUE AND callback_completed = FALSE +WHERE evm.txes.pipeline_task_run_id IN ( + SELECT pipeline_task_runs.id FROM pipeline_task_runs + INNER JOIN pipeline_runs ON pipeline_runs.id = pipeline_task_runs.pipeline_run_id + WHERE pipeline_runs.state = 'suspended' +); + +-- +goose Down +ALTER TABLE evm.txes DROP COLUMN "signal_callback"; +ALTER TABLE evm.txes DROP COLUMN "callback_completed"; From 6fb7fae8511edb4b9c7ef3fbc3373f7de5dd90a0 Mon Sep 17 00:00:00 2001 From: Patrick Date: Tue, 14 Nov 2023 05:57:27 -0500 Subject: [PATCH 141/271] feature/tracing-data: Trace data as artifact in CI (#11113) * WIP: generate traces as artifact * Splitting otel collector into dev and ci environments * feature/tracing-data: minor cleanup --- .github/tracing/README.md | 43 ++++++++- .../tracing/local-smoke-docker-compose.yaml | 4 +- .github/tracing/otel-collector-ci.yaml | 22 +++++ ...collector.yaml => otel-collector-dev.yaml} | 5 + .github/tracing/replay.sh | 6 ++ .github/workflows/integration-tests.yml | 92 +++++++------------ 6 files changed, 111 insertions(+), 61 deletions(-) create mode 100644 .github/tracing/otel-collector-ci.yaml rename .github/tracing/{otel-collector.yaml => otel-collector-dev.yaml} (67%) create mode 100644 .github/tracing/replay.sh diff --git a/.github/tracing/README.md b/.github/tracing/README.md index 6988383ca7b..eb757384295 100644 --- a/.github/tracing/README.md +++ b/.github/tracing/README.md @@ -1,5 +1,44 @@ # Distributed Tracing -These config files are for an OTEL collector, grafana Tempo, and a grafana UI instance to run as containers on the same network. +As part of the LOOP plugin effort, we've added distributed tracing to the core node. This is helpful for initial development and maintenance of LOOPs, but will also empower product teams building on top of core. + +## Dev environment + +One way to generate traces locally today is with the OCR2 basic smoke test. + +1. navigate to `.github/tracing/` and then run `docker compose --file local-smoke-docker-compose.yaml up` +2. setup a local docker registry at `127.0.0.1:5000` (https://www.docker.com/blog/how-to-use-your-own-registry-2/) +3. run `make build_push_plugin_docker_image` in `chainlink/integration-tests/Makefile` +4. run `SELECTED_NETWORKS=SIMULATED CHAINLINK_IMAGE="127.0.0.1:5000/chainlink" CHAINLINK_VERSION="develop" go test -run TestOCRv2Basic ./smoke/ocr2_test.go` +5. navigate to `localhost:3000/explore` in a web browser to query for traces + +Core and the median plugins are instrumented with open telemetry traces, which are sent to the OTEL collector and forwarded to the Tempo backend. The grafana UI can then read the trace data from the Tempo backend. + + -A localhost client can send gRPC calls to the server. The gRPC server is instrumented with open telemetry traces, which are sent to the OTEL collector and forwarded to the Tempo backend. The grafana UI can then read the trace data from the Tempo backend. \ No newline at end of file +## CI environment + +Another way to generate traces is by enabling traces for PRs. This will instrument traces for `TestOCRv2Basic` in the CI run. + +1. Cut a PR in the core repo +2. Add the `enable tracing` label to the PR +3. Navigate to `Integration Tests / ETH Smoke Tests ocr2-plugins (pull_request)` details +4. Navigate to the summary of the integration tests +5. After the test completes, the generated trace data will be saved as an artifact, currently called `trace-data` +6. Download the artifact to this directory (`chainlink/.github/tracing`) +7. `docker compose --file local-smoke-docker-compose.yaml up` +8. Run `sh replay.sh` to replay those traces to the otel-collector container that was spun up in the last step. +9. navigate to `localhost:3000/explore` in a web browser to query for traces + +The artifact is not json encoded - each individual line is a well formed and complete json object. + +## Configuration +This folder contains the following config files: +* otel-collector-ci.yaml +* otel-collector-dev.yaml +* tempo.yaml +* grafana-datasources.yaml + +These config files are for an OTEL collector, grafana Tempo, and a grafana UI instance to run as containers on the same network. +`otel-collector-dev.yaml` is the configuration for dev (i.e. your local machine) environments, and forwards traces from the otel collector to the grafana tempo instance on the same network. +`otel-collector-ci.yaml` is the configuration for the CI runs, and exports the trace data to the artifact from the github run. \ No newline at end of file diff --git a/.github/tracing/local-smoke-docker-compose.yaml b/.github/tracing/local-smoke-docker-compose.yaml index e0e60a675e5..744ba88ef69 100644 --- a/.github/tracing/local-smoke-docker-compose.yaml +++ b/.github/tracing/local-smoke-docker-compose.yaml @@ -6,9 +6,11 @@ services: image: otel/opentelemetry-collector:0.61.0 command: [ "--config=/etc/otel-collector.yaml" ] volumes: - - ./otel-collector.yaml:/etc/otel-collector.yaml + - ./otel-collector-dev.yaml:/etc/otel-collector.yaml + - ../../integration-tests/smoke/traces/trace-data.json:/etc/trace-data.json # local trace data stored consistent with smoke/logs ports: - "4317:4317" # otlp grpc + - "3100:3100" depends_on: - tempo networks: diff --git a/.github/tracing/otel-collector-ci.yaml b/.github/tracing/otel-collector-ci.yaml new file mode 100644 index 00000000000..0bf123d29b5 --- /dev/null +++ b/.github/tracing/otel-collector-ci.yaml @@ -0,0 +1,22 @@ +receivers: + otlp: + protocols: + grpc: + endpoint: "0.0.0.0:4317" + http: + endpoint: "0.0.0.0:3100" +exporters: + file: + path: /tracing/trace-data.json + otlp: + endpoint: tempo:4317 + tls: + insecure: true +service: + telemetry: + logs: + level: "debug" # Set log level to debug + pipelines: + traces: + receivers: [otlp] + exporters: [file,otlp] \ No newline at end of file diff --git a/.github/tracing/otel-collector.yaml b/.github/tracing/otel-collector-dev.yaml similarity index 67% rename from .github/tracing/otel-collector.yaml rename to .github/tracing/otel-collector-dev.yaml index fb8721cba20..dd059127b81 100644 --- a/.github/tracing/otel-collector.yaml +++ b/.github/tracing/otel-collector-dev.yaml @@ -3,12 +3,17 @@ receivers: protocols: grpc: endpoint: "0.0.0.0:4317" + http: + endpoint: "0.0.0.0:3100" exporters: otlp: endpoint: tempo:4317 tls: insecure: true service: + telemetry: + logs: + level: "debug" # Set log level to debug pipelines: traces: receivers: [otlp] diff --git a/.github/tracing/replay.sh b/.github/tracing/replay.sh new file mode 100644 index 00000000000..b2e564567c4 --- /dev/null +++ b/.github/tracing/replay.sh @@ -0,0 +1,6 @@ +# Read JSON file and loop through each trace +while IFS= read -r trace; do + curl -X POST http://localhost:3100/v1/traces \ + -H "Content-Type: application/json" \ + -d "$trace" +done < "trace-data" diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9294dceae6d..17f571fd636 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -368,67 +368,33 @@ jobs: # Create network docker network create --driver bridge tracing - # Start Grafana - cd ./.github/tracing - docker run -d --network=tracing --name=grafana -p 3000:3000 -v $PWD/grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml -e GF_AUTH_ANONYMOUS_ENABLED=true -e GF_AUTH_ANONYMOUS_ORG_ROLE=Admin -e GF_AUTH_DISABLE_LOGIN_FORM=true -e GF_FEATURE_TOGGLES_ENABLE=traceqlEditor grafana/grafana:9.4.3 + # Make trace directory + cd integration-tests/smoke/ + mkdir ./traces + chmod -R 777 ./traces - # Start Tempo - docker run -d --network=tracing --name=tempo -v ./tempo.yaml:/etc/tempo.yaml -v $PWD/tempo-data:/tmp/tempo grafana/tempo:latest -config.file=/etc/tempo.yaml + # Switch directory + cd ../../.github/tracing - # Start OpenTelemetry Collector - docker run -d --network=tracing --name=otel-collector -v $PWD/otel-collector.yaml:/etc/otel-collector.yaml -p 4317:4317 otel/opentelemetry-collector:0.61.0 --config=/etc/otel-collector.yaml - - - name: Generate port - id: generate-port - if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' - env: - GITHUB_PR_NUMBER: ${{ github.event.number }} - run: | - PORT_BASE=3001 - MAX_PORT=8000 + # Create a Docker volume for traces + # docker volume create otel-traces - # Use PR number as offset. Given GitHub PRs are incremental, this guarantees uniqueness for at least 5000 PRs. - OFFSET=$GITHUB_PR_NUMBER - echo "PR Number: $OFFSET" - - # Ensure that we don't exceed the max port - if (( OFFSET > (MAX_PORT - PORT_BASE) )); then - OFFSET=$((OFFSET % (MAX_PORT - PORT_BASE))) - fi - - # Map the offset to the port range - REMOTE_PORT=$((PORT_BASE + OFFSET)) - echo "REMOTE_PORT=$REMOTE_PORT" >> $GITHUB_OUTPUT - - name: Reverse SSH Tunneling - if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' - env: - TRACING_SSH_KEY: ${{ secrets.TRACING_SSH_KEY }} - TRACING_SSH_SERVER: ${{ secrets.TRACING_SSH_SERVER }} - REMOTE_PORT: ${{ steps.generate-port.outputs.REMOTE_PORT }} - run: | - eval $(ssh-agent) - echo "test" - echo "$TRACING_SSH_KEY" | wc -c - echo "$TRACING_SSH_KEY" | tr -d '\r' | wc -c - echo "$TRACING_SSH_KEY" | tr -d '\r' | base64 --decode | ssh-add - - # f: background process - # N: do not execute a remote command - # R: remote port forwarding - ssh -o StrictHostKeyChecking=no -f -N -R $REMOTE_PORT:127.0.0.1:3000 user-gha@$TRACING_SSH_SERVER - echo "To view Grafana locally:" - echo "ssh -N -L 8000:localhost:$REMOTE_PORT user-gha@$TRACING_SSH_SERVER" - echo "Then visit http://localhost:8000 in a browser." - echo "If you are unable to connect, check with the security team that you have access to the tracing server." - - name: Show Grafana Logs - if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' + # Start OpenTelemetry Collector + # Note the user must be set to the same user as the runner for the trace data to be accessible + docker run -d --network=tracing --name=otel-collector \ + -v $PWD/otel-collector-ci.yaml:/etc/otel-collector.yaml \ + -v $PWD/../../integration-tests/smoke/traces:/tracing \ + --user "$(id -u):$(id -g)" \ + -p 4317:4317 otel/opentelemetry-collector:0.88.0 --config=/etc/otel-collector.yaml + - name: Locate Docker Volume + id: locate-volume + if: false run: | - docker logs grafana - docker logs tempo - docker logs otel-collector - - name: Set sleep time to use in future steps + echo "VOLUME_PATH=$(docker volume inspect --format '{{ .Mountpoint }}' otel-traces)" >> $GITHUB_OUTPUT + - name: Show Otel-Collector Logs if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | - echo "SLEEP_TIME=2400" >> "$GITHUB_ENV" + docker logs otel-collector ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' @@ -465,6 +431,10 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - name: Show Otel-Collector Logs + if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' + run: | + docker logs otel-collector - name: Collect Metrics if: always() id: collect-gha-metrics @@ -475,11 +445,17 @@ jobs: this-job-name: ETH Smoke Tests ${{ matrix.product.name }}${{ matrix.product.tag_suffix }} test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true - - name: Keep action running to view traces + - name: Permissions on traces if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' run: | - echo "Sleeping for $SLEEP_TIME seconds..." - sleep $SLEEP_TIME + ls -l ./integration-tests/smoke/traces + - name: Upload Trace Data + if: steps.check-label.outputs.trace == 'true' && matrix.product.name == 'ocr2' && matrix.product.tag_suffix == '-plugins' + uses: actions/upload-artifact@v3 + with: + name: trace-data + path: ./integration-tests/smoke/traces/trace-data.json + ### Used to check the required checks box when the matrix completes eth-smoke-tests: From ac94719b7d4f7a73ad132db4910d515bb6a633e2 Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Tue, 14 Nov 2023 13:19:29 +0100 Subject: [PATCH 142/271] Use resty as http response in E2E tests and fix if testing.T context is nil (#11271) * Use resty as http response * Fix for nil testing.T context --- integration-tests/client/chainlink.go | 31 ++++++++++++++++++++++----- integration-tests/utils/common.go | 4 ++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 3638fa11c7f..ef7bd061426 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -117,11 +117,11 @@ func (c *ChainlinkClient) MustCreateJob(spec JobSpec) (*Job, error) { if err != nil { return nil, err } - return job, VerifyStatusCode(resp.StatusCode, http.StatusOK) + return job, VerifyStatusCode(resp.RawResponse.StatusCode, http.StatusOK) } // CreateJob creates a Chainlink job based on the provided spec struct -func (c *ChainlinkClient) CreateJob(spec JobSpec) (*Job, *http.Response, error) { +func (c *ChainlinkClient) CreateJob(spec JobSpec) (*Job, *resty.Response, error) { job := &Job{} specString, err := spec.String() if err != nil { @@ -138,7 +138,7 @@ func (c *ChainlinkClient) CreateJob(spec JobSpec) (*Job, *http.Response, error) if err != nil { return nil, nil, err } - return job, resp.RawResponse, err + return job, resp, err } // ReadJobs reads all jobs from the Chainlink node @@ -881,8 +881,16 @@ func (c *ChainlinkClient) CreateCSAKey() (*CSAKey, *http.Response, error) { return csaKey, resp.RawResponse, err } +func (c *ChainlinkClient) MustReadCSAKeys() (*CSAKeys, *resty.Response, error) { + csaKeys, res, err := c.ReadCSAKeys() + if err != nil { + return nil, res, err + } + return csaKeys, res, VerifyStatusCodeWithResponse(res, http.StatusOK) +} + // ReadCSAKeys reads CSA keys from the Chainlink node -func (c *ChainlinkClient) ReadCSAKeys() (*CSAKeys, *http.Response, error) { +func (c *ChainlinkClient) ReadCSAKeys() (*CSAKeys, *resty.Response, error) { csaKeys := &CSAKeys{} c.l.Info().Str(NodeURL, c.Config.URL).Msg("Reading CSA Keys") resp, err := c.APIClient.R(). @@ -894,7 +902,7 @@ func (c *ChainlinkClient) ReadCSAKeys() (*CSAKeys, *http.Response, error) { if err != nil { return nil, nil, err } - return csaKeys, resp.RawResponse, err + return csaKeys, resp, err } // CreateEI creates an EI on the Chainlink node based on the provided attributes and returns the respective secrets @@ -1105,6 +1113,19 @@ func VerifyStatusCode(actStatusCd, expStatusCd int) error { return nil } +func VerifyStatusCodeWithResponse(res *resty.Response, expStatusCd int) error { + actStatusCd := res.RawResponse.StatusCode + if actStatusCd != expStatusCd { + return fmt.Errorf( + "unexpected response code, got %d, expected %d, response: %s", + actStatusCd, + expStatusCd, + res.Body(), + ) + } + return nil +} + func CreateNodeKeysBundle(nodes []*ChainlinkClient, chainName string, chainId string) ([]NodeKeysBundle, []*CLNodesWithKeys, error) { nkb := make([]NodeKeysBundle, 0) var clNodes []*CLNodesWithKeys diff --git a/integration-tests/utils/common.go b/integration-tests/utils/common.go index 5ef3209c920..f13c71cfd91 100644 --- a/integration-tests/utils/common.go +++ b/integration-tests/utils/common.go @@ -42,6 +42,10 @@ func TestContext(tb testing.TB) context.Context { var cancel func() switch t := tb.(type) { case *testing.T: + // Return background context if testing.T not set + if t == nil { + return ctx + } if d, ok := t.Deadline(); ok { ctx, cancel = context.WithDeadline(ctx, d) } From b7f042ceafe543937310fa1e47f2909a86dcf797 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 14 Nov 2023 06:47:24 -0600 Subject: [PATCH 143/271] core/services/pg: simplify TxOptions (#11276) --- core/services/pg/transaction.go | 38 ++++----------------------------- core/services/pg/utils.go | 6 ------ 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/core/services/pg/transaction.go b/core/services/pg/transaction.go index d237c20d4c6..92d72b3d81b 100644 --- a/core/services/pg/transaction.go +++ b/core/services/pg/transaction.go @@ -7,20 +7,16 @@ import ( "time" "github.com/getsentry/sentry-go" + "github.com/jmoiron/sqlx" "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" ) type TxOptions struct { sql.TxOptions - LockTimeout time.Duration - IdleInTxSessionTimeout time.Duration } // NOTE: In an ideal world the timeouts below would be set to something sane in @@ -39,27 +35,14 @@ func OptReadOnlyTx() TxOptions { return TxOptions{TxOptions: sql.TxOptions{ReadOnly: true}} } -func applyDefaults(optss []TxOptions) (lockTimeout, idleInTxSessionTimeout time.Duration, txOpts sql.TxOptions) { - lockTimeout = defaultLockTimeout - idleInTxSessionTimeout = defaultIdleInTxSessionTimeout - txIsolation := DefaultIsolation +func applyDefaults(optss []TxOptions) (txOpts sql.TxOptions) { readOnly := false if len(optss) > 0 { opts := optss[0] - if opts.LockTimeout != 0 { - lockTimeout = opts.LockTimeout - } - if opts.IdleInTxSessionTimeout != 0 { - idleInTxSessionTimeout = opts.IdleInTxSessionTimeout - } - if opts.Isolation != 0 { - txIsolation = opts.Isolation - } readOnly = opts.ReadOnly } txOpts = sql.TxOptions{ - Isolation: txIsolation, - ReadOnly: readOnly, + ReadOnly: readOnly, } return } @@ -86,7 +69,7 @@ type TxBeginner interface { } func sqlxTransactionQ(ctx context.Context, db TxBeginner, lggr logger.Logger, fn func(q Queryer) error, optss ...TxOptions) (err error) { - lockTimeout, idleInTxSessionTimeout, txOpts := applyDefaults(optss) + txOpts := applyDefaults(optss) var tx *sqlx.Tx tx, err = db.BeginTxx(ctx, &txOpts) @@ -126,19 +109,6 @@ func sqlxTransactionQ(ctx context.Context, db TxBeginner, lggr logger.Logger, fn } }() - if lockTimeout != defaultLockTimeout { - _, err = tx.Exec(fmt.Sprintf(`SET LOCAL lock_timeout = %d`, lockTimeout.Milliseconds())) - if err != nil { - return errors.Wrap(err, "error setting transaction local lock_timeout") - } - } - if idleInTxSessionTimeout != defaultIdleInTxSessionTimeout { - _, err = tx.Exec(fmt.Sprintf(`SET LOCAL idle_in_transaction_session_timeout = %d`, idleInTxSessionTimeout.Milliseconds())) - if err != nil { - return errors.Wrap(err, "error setting transaction local idle_in_transaction_session_timeout") - } - } - err = fn(tx) return diff --git a/core/services/pg/utils.go b/core/services/pg/utils.go index 5be2a4915bb..eb53c261296 100644 --- a/core/services/pg/utils.go +++ b/core/services/pg/utils.go @@ -12,12 +12,6 @@ const ( DefaultQueryTimeout = 10 * time.Second // longQueryTimeout is a bigger upper bound for how long a SQL query should take longQueryTimeout = 1 * time.Minute - // defaultLockTimeout controls the max time we will wait for any kind of database lock. - // It's good to set this to _something_ because waiting for locks forever is really bad. - defaultLockTimeout = 15 * time.Second - // defaultIdleInTxSessionTimeout controls the max time we leave a transaction open and idle. - // It's good to set this to _something_ because leaving transactions open forever is really bad. - defaultIdleInTxSessionTimeout = 1 * time.Hour ) var _ driver.Valuer = Limit(-1) From b36d9ebe3f2e35ccbe26f7c83774e589b369d7fc Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:02:42 +0100 Subject: [PATCH 144/271] Add ReadBridges() to E2E tests core client (#11282) --- integration-tests/client/chainlink.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index ef7bd061426..7d5825c261a 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -302,6 +302,19 @@ func (c *ChainlinkClient) ReadBridge(name string) (*BridgeType, *http.Response, return &bt, resp.RawResponse, err } +// ReadBridges reads bridges from the Chainlink node +func (c *ChainlinkClient) ReadBridges() (*ResponseSlice, *resty.Response, error) { + result := &ResponseSlice{} + c.l.Info().Str(NodeURL, c.Config.URL).Msg("Getting all bridges") + resp, err := c.APIClient.R(). + SetResult(&result). + Get("/v2/bridge_types") + if err != nil { + return nil, nil, err + } + return result, resp, err +} + // DeleteBridge deletes a bridge on the Chainlink node based on the provided name func (c *ChainlinkClient) DeleteBridge(name string) (*http.Response, error) { c.l.Info().Str(NodeURL, c.Config.URL).Str("Name", name).Msg("Deleting Bridge") From 7be17c91539f2db930ed339117b589d4f7d500eb Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 14 Nov 2023 07:30:49 -0600 Subject: [PATCH 145/271] core/utils: remove unused LazyLoad (#11277) --- core/utils/lazy.go | 36 --------------------- core/utils/lazy_test.go | 71 ----------------------------------------- 2 files changed, 107 deletions(-) delete mode 100644 core/utils/lazy.go delete mode 100644 core/utils/lazy_test.go diff --git a/core/utils/lazy.go b/core/utils/lazy.go deleted file mode 100644 index 43e84808159..00000000000 --- a/core/utils/lazy.go +++ /dev/null @@ -1,36 +0,0 @@ -package utils - -import ( - "sync" -) - -type LazyLoad[T any] struct { - f func() (T, error) - state T - ok bool - lock sync.Mutex -} - -func NewLazyLoad[T any](f func() (T, error)) *LazyLoad[T] { - return &LazyLoad[T]{ - f: f, - } -} - -func (l *LazyLoad[T]) Get() (out T, err error) { - l.lock.Lock() - defer l.lock.Unlock() - - if l.ok { - return l.state, nil - } - l.state, err = l.f() - l.ok = err == nil - return l.state, err -} - -func (l *LazyLoad[T]) Reset() { - l.lock.Lock() - defer l.lock.Unlock() - l.ok = false -} diff --git a/core/utils/lazy_test.go b/core/utils/lazy_test.go deleted file mode 100644 index 88d02eea7f2..00000000000 --- a/core/utils/lazy_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package utils - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLazyLoad(t *testing.T) { - var clientwg sync.WaitGroup - - tc := func() (int, error) { - clientwg.Done() - return 10, nil - } - - // Get should only request a client once, use cached afterward - t.Run("get", func(t *testing.T) { - clientwg.Add(1) // expect one call to get client - c := NewLazyLoad(tc) - rw, err := c.Get() - assert.NoError(t, err) - assert.NotNil(t, rw) - assert.NotNil(t, c.state) - - // used cached client - rw, err = c.Get() - assert.NoError(t, err) - assert.NotNil(t, rw) - clientwg.Wait() - }) - - // Clear removes the cached client, should refetch - t.Run("clear", func(t *testing.T) { - clientwg.Add(2) // expect two calls to get client - - c := NewLazyLoad(tc) - rw, err := c.Get() - assert.NotNil(t, rw) - assert.NoError(t, err) - - c.Reset() - - rw, err = c.Get() - assert.NotNil(t, rw) - assert.NoError(t, err) - clientwg.Wait() - }) - - // Race checks a race condition of Getting and Clearing a new client - t.Run("race", func(t *testing.T) { - clientwg.Add(1) // expect one call to get client - - c := NewLazyLoad(tc) - var wg sync.WaitGroup - wg.Add(2) - go func() { - rw, err := c.Get() - assert.NoError(t, err) - assert.NotNil(t, rw) - wg.Done() - }() - go func() { - c.Reset() - wg.Done() - }() - wg.Wait() - clientwg.Wait() - }) -} From 9471f2eb833225310e90da93148e89650f15edfd Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:12:12 +0000 Subject: [PATCH 146/271] Attempt to fix streams lookup race condition (#11284) --- .../ocr2/plugins/ocr2keeper/evm21/streams_lookup.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go index fb2821a74b7..af7ff42b930 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go @@ -151,11 +151,13 @@ func (r *EvmRegistry) streamsLookup(ctx context.Context, checkResults []ocr2keep var wg sync.WaitGroup for i, lookup := range lookups { - i := i wg.Add(1) - r.threadCtrl.Go(func(ctx context.Context) { - r.doLookup(ctx, &wg, lookup, i, checkResults, lggr) - }) + func(i int, lookup *StreamsLookup) { + r.threadCtrl.Go(func(ctx context.Context) { + r.doLookup(ctx, &wg, lookup, i, checkResults, lggr) + }) + }(i, lookup) + } wg.Wait() From 039ffa93794203bd0b0f8f43278fb49f53aa597e Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Tue, 14 Nov 2023 11:56:17 -0500 Subject: [PATCH 147/271] [Functions] Use @eth-optimism/contracts-bedrock GasPriceOracle (#11275) * (chore): Add @eth-optimism/contracts-bedrock v0.16.2 to vendor * Use GasPriceOracle in Functions ChainSpecificUtil --- .../dev/v1_X/libraries/ChainSpecificUtil.sol | 8 +- .../v0.16.2/src/L2/GasPriceOracle.sol | 99 +++++++++++++++++++ .../v0.16.2/src/L2/L1Block.sol | 76 ++++++++++++++ .../v0.16.2/src/libraries/Predeploys.sol | 77 +++++++++++++++ .../v0.16.2/src/universal/ISemver.sol | 13 +++ 5 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol create mode 100644 contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/L1Block.sol create mode 100644 contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/libraries/Predeploys.sol create mode 100644 contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/universal/ISemver.sol diff --git a/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol b/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol index d6569a256bf..574d1bf1645 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/libraries/ChainSpecificUtil.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import {ArbGasInfo} from "../../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; -import {OVM_GasPriceOracle} from "../../../../vendor/@eth-optimism/contracts/v0.8.9/contracts/L2/predeploys/OVM_GasPriceOracle.sol"; +import {GasPriceOracle} from "../../../../vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol"; /// @dev A library that abstracts out opcodes that behave differently across chains. /// @dev The methods below return values that are pertinent to the given chain. @@ -24,10 +24,10 @@ library ChainSpecificUtil { /// @dev L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism bytes internal constant L1_FEE_DATA_PADDING = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; - /// @dev OVM_GASPRICEORACLE_ADDR is the address of the OVM_GasPriceOracle precompile on Optimism. + /// @dev OVM_GASPRICEORACLE_ADDR is the address of the GasPriceOracle precompile on Optimism. /// @dev reference: https://community.optimism.io/docs/developers/build/transaction-fees/#estimating-the-l1-data-fee address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); - OVM_GasPriceOracle private constant OVM_GASPRICEORACLE = OVM_GasPriceOracle(OVM_GASPRICEORACLE_ADDR); + GasPriceOracle private constant OVM_GASPRICEORACLE = GasPriceOracle(OVM_GASPRICEORACLE_ADDR); uint256 private constant OP_MAINNET_CHAIN_ID = 10; uint256 private constant OP_GOERLI_CHAIN_ID = 420; @@ -44,7 +44,7 @@ library ChainSpecificUtil { /// @notice for the current transaction. /// @notice When on a known Arbitrum chain, it uses ArbGas.getCurrentTxL1GasFees to get the fees. /// @notice On Arbitrum, the provided calldata is not used to calculate the fees. - /// @notice On Optimism, the provided calldata is passed to the OVM_GasPriceOracle predeploy + /// @notice On Optimism, the provided calldata is passed to the GasPriceOracle predeploy /// @notice and getL1Fee is called to get the fees. function _getCurrentTxL1GasFees(bytes memory txCallData) internal view returns (uint256 l1FeeWei) { uint256 chainid = block.chainid; diff --git a/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol new file mode 100644 index 00000000000..aebc1f747a1 --- /dev/null +++ b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import { ISemver } from "../universal/ISemver.sol"; +import { Predeploys } from "../libraries/Predeploys.sol"; +import { L1Block } from "./L1Block.sol"; + +/// @custom:proxied +/// @custom:predeploy 0x420000000000000000000000000000000000000F +/// @title GasPriceOracle +/// @notice This contract maintains the variables responsible for computing the L1 portion of the +/// total fee charged on L2. Before Bedrock, this contract held variables in state that were +/// read during the state transition function to compute the L1 portion of the transaction +/// fee. After Bedrock, this contract now simply proxies the L1Block contract, which has +/// the values used to compute the L1 portion of the fee in its state. +/// +/// The contract exposes an API that is useful for knowing how large the L1 portion of the +/// transaction fee will be. The following events were deprecated with Bedrock: +/// - event OverheadUpdated(uint256 overhead); +/// - event ScalarUpdated(uint256 scalar); +/// - event DecimalsUpdated(uint256 decimals); +contract GasPriceOracle is ISemver { + /// @notice Number of decimals used in the scalar. + uint256 public constant DECIMALS = 6; + + /// @notice Semantic version. + /// @custom:semver 1.1.0 + string public constant version = "1.1.0"; + + /// @notice Computes the L1 portion of the fee based on the size of the rlp encoded input + /// transaction, the current L1 base fee, and the various dynamic parameters. + /// @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for. + /// @return L1 fee that should be paid for the tx + function getL1Fee(bytes memory _data) external view returns (uint256) { + uint256 l1GasUsed = getL1GasUsed(_data); + uint256 l1Fee = l1GasUsed * l1BaseFee(); + uint256 divisor = 10 ** DECIMALS; + uint256 unscaled = l1Fee * scalar(); + uint256 scaled = unscaled / divisor; + return scaled; + } + + /// @notice Retrieves the current gas price (base fee). + /// @return Current L2 gas price (base fee). + function gasPrice() public view returns (uint256) { + return block.basefee; + } + + /// @notice Retrieves the current base fee. + /// @return Current L2 base fee. + function baseFee() public view returns (uint256) { + return block.basefee; + } + + /// @notice Retrieves the current fee overhead. + /// @return Current fee overhead. + function overhead() public view returns (uint256) { + return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead(); + } + + /// @notice Retrieves the current fee scalar. + /// @return Current fee scalar. + function scalar() public view returns (uint256) { + return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar(); + } + + /// @notice Retrieves the latest known L1 base fee. + /// @return Latest known L1 base fee. + function l1BaseFee() public view returns (uint256) { + return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee(); + } + + /// @custom:legacy + /// @notice Retrieves the number of decimals used in the scalar. + /// @return Number of decimals used in the scalar. + function decimals() public pure returns (uint256) { + return DECIMALS; + } + + /// @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which + /// represents the per-transaction gas overhead of posting the transaction and state + /// roots to L1. Adds 68 bytes of padding to account for the fact that the input does + /// not have a signature. + /// @param _data Unsigned fully RLP-encoded transaction to get the L1 gas for. + /// @return Amount of L1 gas used to publish the transaction. + function getL1GasUsed(bytes memory _data) public view returns (uint256) { + uint256 total = 0; + uint256 length = _data.length; + for (uint256 i = 0; i < length; i++) { + if (_data[i] == 0) { + total += 4; + } else { + total += 16; + } + } + uint256 unsigned = total + overhead(); + return unsigned + (68 * 16); + } +} \ No newline at end of file diff --git a/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/L1Block.sol b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/L1Block.sol new file mode 100644 index 00000000000..7722b53b30c --- /dev/null +++ b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/L1Block.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +import { ISemver } from "../universal/ISemver.sol"; + +/// @custom:proxied +/// @custom:predeploy 0x4200000000000000000000000000000000000015 +/// @title L1Block +/// @notice The L1Block predeploy gives users access to information about the last known L1 block. +/// Values within this contract are updated once per epoch (every L1 block) and can only be +/// set by the "depositor" account, a special system address. Depositor account transactions +/// are created by the protocol whenever we move to a new epoch. +contract L1Block is ISemver { + /// @notice Address of the special depositor account. + address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; + + /// @notice The latest L1 block number known by the L2 system. + uint64 public number; + + /// @notice The latest L1 timestamp known by the L2 system. + uint64 public timestamp; + + /// @notice The latest L1 basefee. + uint256 public basefee; + + /// @notice The latest L1 blockhash. + bytes32 public hash; + + /// @notice The number of L2 blocks in the same epoch. + uint64 public sequenceNumber; + + /// @notice The versioned hash to authenticate the batcher by. + bytes32 public batcherHash; + + /// @notice The overhead value applied to the L1 portion of the transaction fee. + uint256 public l1FeeOverhead; + + /// @notice The scalar value applied to the L1 portion of the transaction fee. + uint256 public l1FeeScalar; + + /// @custom:semver 1.1.0 + string public constant version = "1.1.0"; + + /// @notice Updates the L1 block values. + /// @param _number L1 blocknumber. + /// @param _timestamp L1 timestamp. + /// @param _basefee L1 basefee. + /// @param _hash L1 blockhash. + /// @param _sequenceNumber Number of L2 blocks since epoch start. + /// @param _batcherHash Versioned hash to authenticate batcher by. + /// @param _l1FeeOverhead L1 fee overhead. + /// @param _l1FeeScalar L1 fee scalar. + function setL1BlockValues( + uint64 _number, + uint64 _timestamp, + uint256 _basefee, + bytes32 _hash, + uint64 _sequenceNumber, + bytes32 _batcherHash, + uint256 _l1FeeOverhead, + uint256 _l1FeeScalar + ) + external + { + require(msg.sender == DEPOSITOR_ACCOUNT, "L1Block: only the depositor account can set L1 block values"); + + number = _number; + timestamp = _timestamp; + basefee = _basefee; + hash = _hash; + sequenceNumber = _sequenceNumber; + batcherHash = _batcherHash; + l1FeeOverhead = _l1FeeOverhead; + l1FeeScalar = _l1FeeScalar; + } +} \ No newline at end of file diff --git a/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/libraries/Predeploys.sol b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/libraries/Predeploys.sol new file mode 100644 index 00000000000..4a0d399ce71 --- /dev/null +++ b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/libraries/Predeploys.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title Predeploys +/// @notice Contains constant addresses for contracts that are pre-deployed to the L2 system. +library Predeploys { + /// @notice Address of the L2ToL1MessagePasser predeploy. + address internal constant L2_TO_L1_MESSAGE_PASSER = 0x4200000000000000000000000000000000000016; + + /// @notice Address of the L2CrossDomainMessenger predeploy. + address internal constant L2_CROSS_DOMAIN_MESSENGER = 0x4200000000000000000000000000000000000007; + + /// @notice Address of the L2StandardBridge predeploy. + address internal constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010; + + /// @notice Address of the L2ERC721Bridge predeploy. + address internal constant L2_ERC721_BRIDGE = 0x4200000000000000000000000000000000000014; + + //// @notice Address of the SequencerFeeWallet predeploy. + address internal constant SEQUENCER_FEE_WALLET = 0x4200000000000000000000000000000000000011; + + /// @notice Address of the OptimismMintableERC20Factory predeploy. + address internal constant OPTIMISM_MINTABLE_ERC20_FACTORY = 0x4200000000000000000000000000000000000012; + + /// @notice Address of the OptimismMintableERC721Factory predeploy. + address internal constant OPTIMISM_MINTABLE_ERC721_FACTORY = 0x4200000000000000000000000000000000000017; + + /// @notice Address of the L1Block predeploy. + address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015; + + /// @notice Address of the GasPriceOracle predeploy. Includes fee information + /// and helpers for computing the L1 portion of the transaction fee. + address internal constant GAS_PRICE_ORACLE = 0x420000000000000000000000000000000000000F; + + /// @custom:legacy + /// @notice Address of the L1MessageSender predeploy. Deprecated. Use L2CrossDomainMessenger + /// or access tx.origin (or msg.sender) in a L1 to L2 transaction instead. + address internal constant L1_MESSAGE_SENDER = 0x4200000000000000000000000000000000000001; + + /// @custom:legacy + /// @notice Address of the DeployerWhitelist predeploy. No longer active. + address internal constant DEPLOYER_WHITELIST = 0x4200000000000000000000000000000000000002; + + /// @custom:legacy + /// @notice Address of the LegacyERC20ETH predeploy. Deprecated. Balances are migrated to the + /// state trie as of the Bedrock upgrade. Contract has been locked and write functions + /// can no longer be accessed. + address internal constant LEGACY_ERC20_ETH = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000; + + /// @custom:legacy + /// @notice Address of the L1BlockNumber predeploy. Deprecated. Use the L1Block predeploy + /// instead, which exposes more information about the L1 state. + address internal constant L1_BLOCK_NUMBER = 0x4200000000000000000000000000000000000013; + + /// @custom:legacy + /// @notice Address of the LegacyMessagePasser predeploy. Deprecate. Use the updated + /// L2ToL1MessagePasser contract instead. + address internal constant LEGACY_MESSAGE_PASSER = 0x4200000000000000000000000000000000000000; + + /// @notice Address of the ProxyAdmin predeploy. + address internal constant PROXY_ADMIN = 0x4200000000000000000000000000000000000018; + + /// @notice Address of the BaseFeeVault predeploy. + address internal constant BASE_FEE_VAULT = 0x4200000000000000000000000000000000000019; + + /// @notice Address of the L1FeeVault predeploy. + address internal constant L1_FEE_VAULT = 0x420000000000000000000000000000000000001A; + + /// @notice Address of the GovernanceToken predeploy. + address internal constant GOVERNANCE_TOKEN = 0x4200000000000000000000000000000000000042; + + /// @notice Address of the SchemaRegistry predeploy. + address internal constant SCHEMA_REGISTRY = 0x4200000000000000000000000000000000000020; + + /// @notice Address of the EAS predeploy. + address internal constant EAS = 0x4200000000000000000000000000000000000021; +} \ No newline at end of file diff --git a/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/universal/ISemver.sol b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/universal/ISemver.sol new file mode 100644 index 00000000000..ae9569a0505 --- /dev/null +++ b/contracts/src/v0.8/vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/universal/ISemver.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @title ISemver +/// @notice ISemver is a simple contract for ensuring that contracts are +/// versioned using semantic versioning. +interface ISemver { + /// @notice Getter for the semantic version of the contract. This is not + /// meant to be used onchain but instead meant to be used by offchain + /// tooling. + /// @return Semver contract version as a string. + function version() external view returns (string memory); +} \ No newline at end of file From 5bcd414622294d8142e6cfff08f8c1b0c2dc72df Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Tue, 14 Nov 2023 15:26:34 -0400 Subject: [PATCH 148/271] =?UTF-8?q?VRF-749:=20updating=20setup-env=20scrip?= =?UTF-8?q?t=20for=20VRF=20to=20include=20chainId=20when=20cr=E2=80=A6=20(?= =?UTF-8?q?#11286)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-749: updating setup-env script for VRF to include chainId when creating eth keys; README update * VRF-749: updating README.md --- core/scripts/common/vrf/setup-envs/README.md | 83 ++++++++++++++++++-- core/scripts/common/vrf/setup-envs/main.go | 2 + 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/core/scripts/common/vrf/setup-envs/README.md b/core/scripts/common/vrf/setup-envs/README.md index f3b391f0eed..9aa76ffbbb7 100644 --- a/core/scripts/common/vrf/setup-envs/README.md +++ b/core/scripts/common/vrf/setup-envs/README.md @@ -15,6 +15,7 @@ export ETH_CHAIN_ID= export ACCOUNT_KEY= ``` 5. execute from `core/scripts/common/vrf/setup-envs` folder + * `--vrf-version` - "v2" or "v2plus" ``` go run . \ --vrf-version="v2plus" \ @@ -28,24 +29,96 @@ go run . \ --bhs-bk-creds-file \ --bhf-node-url=http://localhost:6614 \ --bhf-creds-file \ +--num-eth-keys=1 \ +--num-vrf-keys=1 \ +--sending-key-funding-amount="1e17" \ --deploy-contracts-and-create-jobs="true" \ --subscription-balance="1e19" \ --subscription-balance-native="1e18" \ --batch-fulfillment-enabled="true" \ --min-confs=3 \ ---num-eth-keys=1 \ ---num-vrf-keys=1 \ ---sending-key-funding-amount="1e17" \ --register-vrf-key-against-address= ``` -Optional parameters - will not be deployed if specified (NOT WORKING YET) +Optional parameters - will not be deployed if specified ``` --link-address

\ --link-eth-feed
\ +``` + +WIP - Not working yet: +``` --bhs-address
\ --batch-bhs-address
\ --coordinator-address
\ --batch-coordinator-address
-``` \ No newline at end of file +``` + + +## Process Example + +1. If the CL nodes do not have needed amount of ETH and VRF keys, you need to create them first: +``` +go run . \ +--vrf-version="v2" \ +--vrf-primary-node-url= \ +--vrf-primary-creds-file \ +--bhs-node-url= \ +--bhs-creds-file \ +--num-eth-keys=3 \ +--num-vrf-keys=1 \ +--sending-key-funding-amount="1e17" \ +--deploy-contracts-and-create-jobs="false" +``` +Then update corresponding deployment scripts with the new ETH addresses, specifying max gas price for each key + +e.g.: +``` +[[EVM.KeySpecific]] +Key = '' +GasEstimator.PriceMax = '30 gwei' +``` + +2. If the CL nodes already have needed amount of ETH and VRF keys, you can deploy contracts and create jobs with the following command: +NOTE - nodes will be funded at least to the amount specified in `--sending-key-funding-amount` parameter. +``` +go run . \ +--vrf-version="v2" \ +--vrf-primary-node-url= \ +--vrf-primary-creds-file \ +--bhs-node-url= \ +--bhs-creds-file \ +--num-eth-keys=3 \ +--num-vrf-keys=1 \ +--sending-key-funding-amount="1e17" \ +--deploy-contracts-and-create-jobs="true" \ +--subscription-balance="1e19" \ +--subscription-balance-native="1e18" \ +--batch-fulfillment-enabled="true" \ +--min-confs=3 \ +--register-vrf-key-against-address="" \ +--link-address "" \ +--link-eth-feed "" +``` + + +3. We can run sample rand request to see if the setup works. + After previous script was done, we should see the command to run in the console: + + e.g. to trigger rand request: + 1. navigate to `core/scripts/vrfv2plus/testnet` or `core/scripts/vrfv2/testnet` folder + 2. set needed env variables + ``` + export ETH_URL= + export ETH_CHAIN_ID= + export ACCOUNT_KEY= + ``` + 3. Trigger rand request (get this command from the console after running `setup-envs` script ) + ```bash + go run . eoa-load-test-request-with-metrics --consumer-address= --sub-id=1 --key-hash= --request-confirmations <> --requests 1 --runs 1 --cb-gas-limit 1_000_000 + ``` + 4. Then to check that rand request was fulfilled (get this command from the console after running `setup-envs` script ) + ```bash + go run . eoa-load-test-read-metrics --consumer-address= + ``` \ No newline at end of file diff --git a/core/scripts/common/vrf/setup-envs/main.go b/core/scripts/common/vrf/setup-envs/main.go index 7c2530ffd47..94662aa1831 100644 --- a/core/scripts/common/vrf/setup-envs/main.go +++ b/core/scripts/common/vrf/setup-envs/main.go @@ -506,6 +506,8 @@ func createETHKeysIfNeeded(client *clcmd.Shell, app *cli.App, output *bytes.Buff if *maxGasPriceGwei > 0 { helpers.PanicErr(flagSet.Set("max-gas-price-gwei", fmt.Sprintf("%d", *maxGasPriceGwei))) } + err := flagSet.Parse([]string{"-evm-chain-id", os.Getenv("ETH_CHAIN_ID")}) + helpers.PanicErr(err) err = client.CreateETHKey(cli.NewContext(app, flagSet, nil)) helpers.PanicErr(err) helpers.PanicErr(json.Unmarshal(output.Bytes(), &newKey)) From e7e0d42ee76ddc019abae7072187e99f98ba699e Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 14 Nov 2023 13:53:54 -0700 Subject: [PATCH 149/271] [TT-707] chainlink-tests image build workflow dispatch (#11288) --- .github/actions/build-test-image/action.yml | 6 +++++- .../workflows/integration-tests-publish.yml | 20 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index a241f51d920..252b292d031 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -17,6 +17,9 @@ inputs: description: The test suites to build into the image default: chaos migration performance reorg smoke soak benchmark required: false + base_image_tag: + description: The test base image version to use, if not provided it will use the version from the ./integration-tests/go.mod file + required: false QA_AWS_ROLE_TO_ASSUME: description: The AWS role to assume as the CD user, if any. Used in configuring the docker/login-action required: true @@ -31,6 +34,7 @@ runs: using: composite steps: - name: Get CTF Version + if: ${{ inputs.base_image_tag == '' }} id: version uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: @@ -55,7 +59,7 @@ runs: file: ./integration-tests/test.Dockerfile build-args: | BASE_IMAGE=${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image - IMAGE_VERSION=${{ steps.version.outputs.version }} + IMAGE_VERSION=${{ inputs.base_image_tag || steps.version.outputs.version }} SUITES="${{ inputs.suites }}" AWS_REGION: ${{ inputs.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index a66ea612281..9579b83b98e 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -5,9 +5,20 @@ on: push: branches: - develop + workflow_dispatch: + inputs: + chainlink-tests-tag: + description: 'The tag to be pushed' + required: true + ctf-base-image-tag: + description: | + 'The tag of the CTF base image to be used, + typically something like v1.18.6 from https://github.com/smartcontractkit/chainlink-testing-framework/releases + or a custom tag or branch you have pushed.' + required: true env: - ECR_TAG: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:develop + ECR_TAG_BASE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests jobs: publish-integration-test-image: @@ -16,7 +27,7 @@ jobs: id-token: write contents: read name: Publish Integration Test Image - runs-on: ubuntu-latest + runs-on: ubuntu20.04-16cores-64GB steps: - name: Collect Metrics id: collect-gha-metrics @@ -29,11 +40,12 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: - ref: ${{ github.event.pull_request.head.sha }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Build Image uses: ./.github/actions/build-test-image with: - other_tags: ${{ env.ECR_TAG }} + other_tags: "${{ env.ECR_TAG_BASE }}:${{ inputs.chainlink-tests-tag || 'develop' }}" + base_image_tag: ${{ inputs.ctf-base-image-tag }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} From 3a38e9052fa49df94fe309efa719ef44268f5545 Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Tue, 14 Nov 2023 16:21:09 -0800 Subject: [PATCH 150/271] [Functions] Offchain heartbeat support in Listener and OCR2 plugin (#11274) 1. Add new API structs 2. Mark offchain requests in Listener 3. Introduce a simple OffchainTransmitter to pass responses from the plugin --- core/services/functions/listener.go | 42 +++++++++++++ core/services/functions/listener_test.go | 40 +++++++++++++ .../functions/mocks/offchain_transmitter.go | 59 ++++++++++++++++++ .../functions/offchain_transmitter.go | 39 ++++++++++++ .../functions/offchain_transmitter_test.go | 31 ++++++++++ core/services/functions/request.go | 15 +++++ .../services/ocr2/plugins/functions/plugin.go | 17 +++--- .../ocr2/plugins/functions/reporting.go | 48 +++++++++------ .../ocr2/plugins/functions/reporting_test.go | 60 +++++++++++++------ 9 files changed, 308 insertions(+), 43 deletions(-) create mode 100644 core/services/functions/mocks/offchain_transmitter.go create mode 100644 core/services/functions/offchain_transmitter.go create mode 100644 core/services/functions/offchain_transmitter_test.go diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index 773ae610408..5614c5331d4 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -111,6 +111,9 @@ const ( DefaultPruneCheckFrequencySec uint32 = 60 * 10 DefaultPruneBatchSize uint32 = 500 + // Used in place of OnchainMetadata for all offchain requests. + OffchainRequestMarker string = "OFFCHAIN_REQUEST" + FlagCBORMaxSize uint32 = 1 FlagSecretsMaxSize uint32 = 2 ) @@ -280,6 +283,45 @@ func (l *FunctionsListener) getMaxSecretsSize(flags RequestFlags) uint32 { return l.pluginConfig.MaxSecretsSizesList[idx] } +func (l *FunctionsListener) HandleOffchainRequest(ctx context.Context, request *OffchainRequest) error { + if request == nil { + return errors.New("HandleOffchainRequest: received nil request") + } + if len(request.RequestId) != RequestIDLength { + return fmt.Errorf("HandleOffchainRequest: invalid request ID length %d", len(request.RequestId)) + } + if len(request.SubscriptionOwner) != common.AddressLength || len(request.RequestInitiator) != common.AddressLength { + return fmt.Errorf("HandleOffchainRequest: SubscriptionOwner and RequestInitiator must be set to valid addresses") + } + + var requestId RequestID + copy(requestId[:], request.RequestId[:32]) + subscriptionOwner := common.BytesToAddress(request.SubscriptionOwner) + senderAddr := common.BytesToAddress(request.RequestInitiator) + emptyTxHash := common.Hash{} + zeroCallbackGasLimit := uint32(0) + newReq := &Request{ + RequestID: requestId, + RequestTxHash: &emptyTxHash, + ReceivedAt: time.Now(), + Flags: []byte{}, + CallbackGasLimit: &zeroCallbackGasLimit, + // use sender address in place of coordinator contract to keep batches uniform + CoordinatorContractAddress: &senderAddr, + OnchainMetadata: []byte(OffchainRequestMarker), + } + if err := l.pluginORM.CreateRequest(newReq, pg.WithParentCtx(ctx)); err != nil { + if errors.Is(err, ErrDuplicateRequestID) { + l.logger.Warnw("HandleOffchainRequest: received duplicate request ID", "requestID", formatRequestId(requestId), "err", err) + } else { + l.logger.Errorw("HandleOffchainRequest: failed to create a DB entry for new request", "requestID", formatRequestId(requestId), "err", err) + } + return err + } + l.handleRequest(ctx, requestId, request.SubscriptionId, subscriptionOwner, RequestFlags{}, &request.Data) + return nil +} + func (l *FunctionsListener) handleOracleRequestV1(request *evmrelayTypes.OracleRequest) { defer l.shutdownWaitGroup.Done() l.logger.Infow("handleOracleRequestV1: oracle request v1 received", "requestID", formatRequestId(request.RequestId)) diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index 3b7ed46988d..ac2bc64184d 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -179,6 +179,46 @@ func TestFunctionsListener_HandleOracleRequestV1_Success(t *testing.T) { uni.service.Close() } +func TestFunctionsListener_HandleOffchainRequest_Success(t *testing.T) { + testutils.SkipShortDB(t) + t.Parallel() + + uni := NewFunctionsListenerUniverse(t, 0, 1_000_000) + + uni.pluginORM.On("CreateRequest", mock.Anything, mock.Anything).Return(nil) + uni.bridgeAccessor.On("NewExternalAdapterClient").Return(uni.eaClient, nil) + uni.eaClient.On("RunComputation", mock.Anything, RequestIDStr, mock.Anything, SubscriptionOwner.Hex(), SubscriptionID, mock.Anything, mock.Anything, mock.Anything).Return(ResultBytes, nil, nil, nil) + uni.pluginORM.On("SetResult", RequestID, ResultBytes, mock.Anything, mock.Anything).Return(nil) + + request := &functions_service.OffchainRequest{ + RequestId: RequestID[:], + RequestInitiator: SubscriptionOwner.Bytes(), + SubscriptionId: uint64(SubscriptionID), + SubscriptionOwner: SubscriptionOwner.Bytes(), + Data: functions_service.RequestData{}, + } + require.NoError(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) +} + +func TestFunctionsListener_HandleOffchainRequest_Invalid(t *testing.T) { + testutils.SkipShortDB(t) + t.Parallel() + uni := NewFunctionsListenerUniverse(t, 0, 1_000_000) + + request := &functions_service.OffchainRequest{ + RequestId: RequestID[:], + RequestInitiator: []byte("invalid_address"), + SubscriptionId: uint64(SubscriptionID), + SubscriptionOwner: SubscriptionOwner.Bytes(), + Data: functions_service.RequestData{}, + } + require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) + + request.RequestInitiator = SubscriptionOwner.Bytes() + request.SubscriptionOwner = []byte("invalid_address") + require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) +} + func TestFunctionsListener_HandleOracleRequestV1_ComputationError(t *testing.T) { testutils.SkipShortDB(t) t.Parallel() diff --git a/core/services/functions/mocks/offchain_transmitter.go b/core/services/functions/mocks/offchain_transmitter.go new file mode 100644 index 00000000000..d9a7be04dd4 --- /dev/null +++ b/core/services/functions/mocks/offchain_transmitter.go @@ -0,0 +1,59 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package mocks + +import ( + context "context" + + functions "github.com/smartcontractkit/chainlink/v2/core/services/functions" + mock "github.com/stretchr/testify/mock" +) + +// OffchainTransmitter is an autogenerated mock type for the OffchainTransmitter type +type OffchainTransmitter struct { + mock.Mock +} + +// ReportChannel provides a mock function with given fields: +func (_m *OffchainTransmitter) ReportChannel() chan *functions.OffchainResponse { + ret := _m.Called() + + var r0 chan *functions.OffchainResponse + if rf, ok := ret.Get(0).(func() chan *functions.OffchainResponse); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(chan *functions.OffchainResponse) + } + } + + return r0 +} + +// TransmitReport provides a mock function with given fields: ctx, report +func (_m *OffchainTransmitter) TransmitReport(ctx context.Context, report *functions.OffchainResponse) error { + ret := _m.Called(ctx, report) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *functions.OffchainResponse) error); ok { + r0 = rf(ctx, report) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewOffchainTransmitter creates a new instance of OffchainTransmitter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewOffchainTransmitter(t interface { + mock.TestingT + Cleanup(func()) +}) *OffchainTransmitter { + mock := &OffchainTransmitter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/services/functions/offchain_transmitter.go b/core/services/functions/offchain_transmitter.go new file mode 100644 index 00000000000..63527937f92 --- /dev/null +++ b/core/services/functions/offchain_transmitter.go @@ -0,0 +1,39 @@ +package functions + +import ( + "context" + + "github.com/pkg/errors" +) + +// Simple wrapper around a channel to transmit offchain reports between +// OCR plugin and Gateway connector +// +//go:generate mockery --quiet --name OffchainTransmitter --output ./mocks/ --case=underscore +type OffchainTransmitter interface { + TransmitReport(ctx context.Context, report *OffchainResponse) error + ReportChannel() chan *OffchainResponse +} + +type offchainTransmitter struct { + reportCh chan *OffchainResponse +} + +func NewOffchainTransmitter(chanSize uint32) OffchainTransmitter { + return &offchainTransmitter{ + reportCh: make(chan *OffchainResponse, chanSize), + } +} + +func (t *offchainTransmitter) TransmitReport(ctx context.Context, report *OffchainResponse) error { + select { + case t.reportCh <- report: + return nil + case <-ctx.Done(): + return errors.New("context cancelled") + } +} + +func (t *offchainTransmitter) ReportChannel() chan *OffchainResponse { + return t.reportCh +} diff --git a/core/services/functions/offchain_transmitter_test.go b/core/services/functions/offchain_transmitter_test.go new file mode 100644 index 00000000000..bec639bf27d --- /dev/null +++ b/core/services/functions/offchain_transmitter_test.go @@ -0,0 +1,31 @@ +package functions_test + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/functions" +) + +func TestOffchainTransmitter(t *testing.T) { + t.Parallel() + + transmitter := functions.NewOffchainTransmitter(1) + ch := transmitter.ReportChannel() + report := &functions.OffchainResponse{RequestId: []byte("testID")} + ctx := testutils.Context(t) + + require.NoError(t, transmitter.TransmitReport(ctx, report)) + require.Equal(t, report, <-ch) + + require.NoError(t, transmitter.TransmitReport(ctx, report)) + + ctxTimeout, cancel := context.WithTimeout(ctx, time.Millisecond*20) + defer cancel() + // should not freeze + require.Error(t, transmitter.TransmitReport(ctxTimeout, report)) +} diff --git a/core/services/functions/request.go b/core/services/functions/request.go index 181058b83d0..14c0b0d0e5a 100644 --- a/core/services/functions/request.go +++ b/core/services/functions/request.go @@ -9,6 +9,14 @@ const ( type RequestFlags [32]byte +type OffchainRequest struct { + RequestId []byte `json:"requestId"` + RequestInitiator []byte `json:"requestInitiator"` + SubscriptionId uint64 `json:"subscriptionId"` + SubscriptionOwner []byte `json:"subscriptionOwner"` + Data RequestData `json:"data"` +} + type RequestData struct { Source string `json:"source" cbor:"source"` Language int `json:"language" cbor:"language"` @@ -19,6 +27,13 @@ type RequestData struct { BytesArgs [][]byte `json:"bytesArgs,omitempty" cbor:"bytesArgs"` } +// NOTE: to be extended with raw report and signatures when needed +type OffchainResponse struct { + RequestId []byte `json:"requestId"` + Result []byte `json:"result"` + Error []byte `json:"error"` +} + type DONHostedSecrets struct { SlotID uint `json:"slotId" cbor:"slotId"` Version uint64 `json:"version" cbor:"version"` diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 26cffac5abf..8597b8ad4cc 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -50,9 +50,10 @@ type FunctionsServicesConfig struct { } const ( - FunctionsBridgeName string = "ea_bridge" - FunctionsS4Namespace string = "functions" - MaxAdapterResponseBytes int64 = 1_000_000 + FunctionsBridgeName string = "ea_bridge" + FunctionsS4Namespace string = "functions" + MaxAdapterResponseBytes int64 = 1_000_000 + DefaultOffchainTransmitterChannelSize uint32 = 1000 ) // Create all OCR2 plugin Oracles and all extra services needed to run a Functions job. @@ -100,6 +101,7 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs s4Storage = s4.NewStorage(conf.Logger, *pluginConfig.S4Constraints, s4ORM, utils.NewRealClock()) } + offchainTransmitter := functions.NewOffchainTransmitter(DefaultOffchainTransmitterChannelSize) listenerLogger := conf.Logger.Named("FunctionsListener") bridgeAccessor := functions.NewBridgeAccessor(conf.BridgeORM, FunctionsBridgeName, MaxAdapterResponseBytes) functionsListener := functions.NewFunctionsListener( @@ -118,10 +120,11 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs allServices = append(allServices, functionsListener) functionsOracleArgs.ReportingPluginFactory = FunctionsReportingPluginFactory{ - Logger: functionsOracleArgs.Logger, - PluginORM: pluginORM, - JobID: conf.Job.ExternalJobID, - ContractVersion: pluginConfig.ContractVersion, + Logger: functionsOracleArgs.Logger, + PluginORM: pluginORM, + JobID: conf.Job.ExternalJobID, + ContractVersion: pluginConfig.ContractVersion, + OffchainTransmitter: offchainTransmitter, } functionsReportingPluginOracle, err := libocr2.NewOracle(*functionsOracleArgs) if err != nil { diff --git a/core/services/ocr2/plugins/functions/reporting.go b/core/services/ocr2/plugins/functions/reporting.go index dfa8f575d1b..36e8a882734 100644 --- a/core/services/ocr2/plugins/functions/reporting.go +++ b/core/services/ocr2/plugins/functions/reporting.go @@ -1,6 +1,7 @@ package functions import ( + "bytes" "context" "fmt" @@ -21,22 +22,24 @@ import ( ) type FunctionsReportingPluginFactory struct { - Logger commontypes.Logger - PluginORM functions.ORM - JobID uuid.UUID - ContractVersion uint32 + Logger commontypes.Logger + PluginORM functions.ORM + JobID uuid.UUID + ContractVersion uint32 + OffchainTransmitter functions.OffchainTransmitter } var _ types.ReportingPluginFactory = (*FunctionsReportingPluginFactory)(nil) type functionsReporting struct { - logger commontypes.Logger - pluginORM functions.ORM - jobID uuid.UUID - reportCodec encoding.ReportCodec - genericConfig *types.ReportingPluginConfig - specificConfig *config.ReportingPluginConfigWrapper - contractVersion uint32 + logger commontypes.Logger + pluginORM functions.ORM + jobID uuid.UUID + reportCodec encoding.ReportCodec + genericConfig *types.ReportingPluginConfig + specificConfig *config.ReportingPluginConfigWrapper + contractVersion uint32 + offchainTransmitter functions.OffchainTransmitter } var _ types.ReportingPlugin = &functionsReporting{} @@ -112,13 +115,14 @@ func (f FunctionsReportingPluginFactory) NewReportingPlugin(rpConfig types.Repor }, } plugin := functionsReporting{ - logger: f.Logger, - pluginORM: f.PluginORM, - jobID: f.JobID, - reportCodec: codec, - genericConfig: &rpConfig, - specificConfig: pluginConfig, - contractVersion: f.ContractVersion, + logger: f.Logger, + pluginORM: f.PluginORM, + jobID: f.JobID, + reportCodec: codec, + genericConfig: &rpConfig, + specificConfig: pluginConfig, + contractVersion: f.ContractVersion, + offchainTransmitter: f.OffchainTransmitter, } promReportingPlugins.WithLabelValues(f.JobID.String()).Inc() return &plugin, info, nil @@ -437,6 +441,14 @@ func (r *functionsReporting) ShouldAcceptFinalizedReport(ctx context.Context, ts r.logger.Debug("FunctionsReporting ShouldAcceptFinalizedReport: state couldn't be changed to FINALIZED. Not transmitting.", commontypes.LogFields{"requestID": reqIdStr, "err": err}) continue } + if bytes.Equal(item.OnchainMetadata, []byte(functions.OffchainRequestMarker)) { + r.logger.Debug("FunctionsReporting ShouldAcceptFinalizedReport: transmitting offchain", commontypes.LogFields{"requestID": reqIdStr}) + result := functions.OffchainResponse{RequestId: item.RequestID, Result: item.Result, Error: item.Error} + if err := r.offchainTransmitter.TransmitReport(ctx, &result); err != nil { + r.logger.Error("FunctionsReporting ShouldAcceptFinalizedReport: unable to transmit offchain", commontypes.LogFields{"requestID": reqIdStr, "err": err}) + } + continue // doesn't need onchain transmission + } needTransmissionIds = append(needTransmissionIds, reqIdStr) } r.logger.Debug("FunctionsReporting ShouldAcceptFinalizedReport end", commontypes.LogFields{ diff --git a/core/services/ocr2/plugins/functions/reporting_test.go b/core/services/ocr2/plugins/functions/reporting_test.go index 24b430abd93..860492bfc52 100644 --- a/core/services/ocr2/plugins/functions/reporting_test.go +++ b/core/services/ocr2/plugins/functions/reporting_test.go @@ -22,14 +22,16 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/encoding" ) -func preparePlugin(t *testing.T, batchSize uint32, maxTotalGasLimit uint32) (types.ReportingPlugin, *functions_mocks.ORM, encoding.ReportCodec) { +func preparePlugin(t *testing.T, batchSize uint32, maxTotalGasLimit uint32) (types.ReportingPlugin, *functions_mocks.ORM, encoding.ReportCodec, *functions_mocks.OffchainTransmitter) { lggr := logger.TestLogger(t) ocrLogger := relaylogger.NewOCRWrapper(lggr, true, func(msg string) {}) orm := functions_mocks.NewORM(t) + offchainTransmitter := functions_mocks.NewOffchainTransmitter(t) factory := functions.FunctionsReportingPluginFactory{ - Logger: ocrLogger, - PluginORM: orm, - ContractVersion: 1, + Logger: ocrLogger, + PluginORM: orm, + ContractVersion: 1, + OffchainTransmitter: offchainTransmitter, } pluginConfig := config.ReportingPluginConfigWrapper{ @@ -48,7 +50,7 @@ func preparePlugin(t *testing.T, batchSize uint32, maxTotalGasLimit uint32) (typ require.NoError(t, err) codec, err := encoding.NewReportCodec(1) require.NoError(t, err) - return plugin, orm, codec + return plugin, orm, codec, offchainTransmitter } func newRequestID() functions_srv.RequestID { @@ -130,7 +132,7 @@ func newObservation(t *testing.T, observerId uint8, requests ...*encoding.Proces func TestFunctionsReporting_Query(t *testing.T) { t.Parallel() const batchSize = 10 - plugin, orm, _ := preparePlugin(t, batchSize, 0) + plugin, orm, _, _ := preparePlugin(t, batchSize, 0) reqs := []functions_srv.Request{newRequest(), newRequest()} orm.On("FindOldestEntriesByState", functions_srv.RESULT_READY, uint32(batchSize), mock.Anything).Return(reqs, nil) @@ -148,7 +150,7 @@ func TestFunctionsReporting_Query(t *testing.T) { func TestFunctionsReporting_Query_HandleCoordinatorMismatch(t *testing.T) { t.Parallel() const batchSize = 10 - plugin, orm, _ := preparePlugin(t, batchSize, 1000000) + plugin, orm, _, _ := preparePlugin(t, batchSize, 1000000) reqs := []functions_srv.Request{newRequest(), newRequest()} reqs[0].CoordinatorContractAddress = &common.Address{1} reqs[1].CoordinatorContractAddress = &common.Address{2} @@ -167,7 +169,7 @@ func TestFunctionsReporting_Query_HandleCoordinatorMismatch(t *testing.T) { func TestFunctionsReporting_Observation(t *testing.T) { t.Parallel() - plugin, orm, _ := preparePlugin(t, 10, 0) + plugin, orm, _, _ := preparePlugin(t, 10, 0) req1 := newRequestWithResult([]byte("abc")) req2 := newRequest() @@ -202,7 +204,7 @@ func TestFunctionsReporting_Observation(t *testing.T) { func TestFunctionsReporting_Observation_IncorrectQuery(t *testing.T) { t.Parallel() - plugin, orm, _ := preparePlugin(t, 10, 0) + plugin, orm, _, _ := preparePlugin(t, 10, 0) req1 := newRequestWithResult([]byte("abc")) invalidId := []byte("invalid") @@ -229,7 +231,7 @@ func TestFunctionsReporting_Observation_IncorrectQuery(t *testing.T) { func TestFunctionsReporting_Report(t *testing.T) { t.Parallel() - plugin, _, codec := preparePlugin(t, 10, 1000000) + plugin, _, codec, _ := preparePlugin(t, 10, 1000000) reqId1, reqId2, reqId3 := newRequestID(), newRequestID(), newRequestID() compResult := []byte("aaa") procReq1 := newProcessedRequest(reqId1, compResult, []byte{}) @@ -266,7 +268,7 @@ func TestFunctionsReporting_Report(t *testing.T) { func TestFunctionsReporting_Report_WithGasLimitAndMetadata(t *testing.T) { t.Parallel() - plugin, _, codec := preparePlugin(t, 10, 300000) + plugin, _, codec, _ := preparePlugin(t, 10, 300000) reqId1, reqId2, reqId3 := newRequestID(), newRequestID(), newRequestID() compResult := []byte("aaa") gasLimit1, gasLimit2 := uint32(100_000), uint32(200_000) @@ -307,7 +309,7 @@ func TestFunctionsReporting_Report_WithGasLimitAndMetadata(t *testing.T) { func TestFunctionsReporting_Report_HandleCoordinatorMismatch(t *testing.T) { t.Parallel() - plugin, _, codec := preparePlugin(t, 10, 300000) + plugin, _, codec, _ := preparePlugin(t, 10, 300000) reqId1, reqId2, reqId3 := newRequestID(), newRequestID(), newRequestID() compResult, meta := []byte("aaa"), []byte("meta") coordinatorContractA, coordinatorContractB := common.Address{1}, common.Address{2} @@ -337,7 +339,7 @@ func TestFunctionsReporting_Report_HandleCoordinatorMismatch(t *testing.T) { func TestFunctionsReporting_Report_CallbackGasLimitExceeded(t *testing.T) { t.Parallel() - plugin, _, codec := preparePlugin(t, 10, 200000) + plugin, _, codec, _ := preparePlugin(t, 10, 200000) reqId1, reqId2 := newRequestID(), newRequestID() compResult := []byte("aaa") gasLimit1, gasLimit2 := uint32(100_000), uint32(200_000) @@ -368,7 +370,7 @@ func TestFunctionsReporting_Report_CallbackGasLimitExceeded(t *testing.T) { func TestFunctionsReporting_Report_DeterministicOrderOfRequests(t *testing.T) { t.Parallel() - plugin, _, codec := preparePlugin(t, 10, 0) + plugin, _, codec, _ := preparePlugin(t, 10, 0) reqId1, reqId2, reqId3 := newRequestID(), newRequestID(), newRequestID() compResult := []byte("aaa") @@ -397,7 +399,7 @@ func TestFunctionsReporting_Report_DeterministicOrderOfRequests(t *testing.T) { func TestFunctionsReporting_Report_IncorrectObservation(t *testing.T) { t.Parallel() - plugin, _, _ := preparePlugin(t, 10, 0) + plugin, _, _, _ := preparePlugin(t, 10, 0) reqId1 := newRequestID() compResult := []byte("aaa") @@ -416,7 +418,14 @@ func getReportBytes(t *testing.T, codec encoding.ReportCodec, reqs ...functions_ var report []*encoding.ProcessedRequest for _, req := range reqs { req := req - report = append(report, &encoding.ProcessedRequest{RequestID: req.RequestID[:], Result: req.Result}) + report = append(report, &encoding.ProcessedRequest{ + RequestID: req.RequestID[:], + Result: req.Result, + Error: req.Error, + CallbackGasLimit: *req.CallbackGasLimit, + CoordinatorContract: req.CoordinatorContractAddress[:], + OnchainMetadata: req.OnchainMetadata, + }) } reportBytes, err := codec.EncodeReport(report) require.NoError(t, err) @@ -425,7 +434,7 @@ func getReportBytes(t *testing.T, codec encoding.ReportCodec, reqs ...functions_ func TestFunctionsReporting_ShouldAcceptFinalizedReport(t *testing.T) { t.Parallel() - plugin, orm, codec := preparePlugin(t, 10, 0) + plugin, orm, codec, _ := preparePlugin(t, 10, 0) req1 := newRequestWithResult([]byte("xxx")) // nonexistent req2 := newRequestWithResult([]byte("abc")) @@ -462,9 +471,24 @@ func TestFunctionsReporting_ShouldAcceptFinalizedReport(t *testing.T) { require.True(t, should) } +func TestFunctionsReporting_ShouldAcceptFinalizedReport_OffchainTransmission(t *testing.T) { + t.Parallel() + plugin, orm, codec, offchainTransmitter := preparePlugin(t, 10, 0) + req1 := newRequestWithResult([]byte("abc")) + req1.OnchainMetadata = []byte(functions_srv.OffchainRequestMarker) + + orm.On("FindById", req1.RequestID, mock.Anything).Return(&req1, nil) + orm.On("SetFinalized", req1.RequestID, mock.Anything, mock.Anything, mock.Anything).Return(nil) + offchainTransmitter.On("TransmitReport", mock.Anything, mock.Anything).Return(nil) + + should, err := plugin.ShouldAcceptFinalizedReport(testutils.Context(t), types.ReportTimestamp{}, getReportBytes(t, codec, req1)) + require.NoError(t, err) + require.False(t, should) +} + func TestFunctionsReporting_ShouldTransmitAcceptedReport(t *testing.T) { t.Parallel() - plugin, orm, codec := preparePlugin(t, 10, 0) + plugin, orm, codec, _ := preparePlugin(t, 10, 0) req1 := newRequestWithResult([]byte("xxx")) // nonexistent req2 := newRequestWithResult([]byte("abc")) From 75c70f730b08ce07bc4fbf2ec77d619b39d55968 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 15 Nov 2023 06:01:48 -0600 Subject: [PATCH 151/271] core/services/pg: simplify API (#11296) --- core/services/pg/connection.go | 2 +- core/services/pg/q.go | 2 +- core/services/pg/sqlx.go | 2 +- core/services/pg/transaction.go | 54 +++++++++++---------------------- 4 files changed, 20 insertions(+), 40 deletions(-) diff --git a/core/services/pg/connection.go b/core/services/pg/connection.go index 0bafd5dcd0f..ee345bb6259 100644 --- a/core/services/pg/connection.go +++ b/core/services/pg/connection.go @@ -42,7 +42,7 @@ func NewConnection(uri string, dialect dialects.DialectName, config ConnectionCo lockTimeout := config.DefaultLockTimeout().Milliseconds() idleInTxSessionTimeout := config.DefaultIdleInTxSessionTimeout().Milliseconds() stmt := fmt.Sprintf(`SET TIME ZONE 'UTC'; SET lock_timeout = %d; SET idle_in_transaction_session_timeout = %d; SET default_transaction_isolation = %q`, - lockTimeout, idleInTxSessionTimeout, DefaultIsolation.String()) + lockTimeout, idleInTxSessionTimeout, defaultIsolation.String()) if _, err = db.Exec(stmt); err != nil { return nil, err } diff --git a/core/services/pg/q.go b/core/services/pg/q.go index 470d39c825c..9c9c15d9838 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -165,7 +165,7 @@ func (q Q) Context() (context.Context, context.CancelFunc) { return context.WithTimeout(q.ParentCtx, q.QueryTimeout) } -func (q Q) Transaction(fc func(q Queryer) error, txOpts ...TxOptions) error { +func (q Q) Transaction(fc func(q Queryer) error, txOpts ...TxOption) error { ctx, cancel := q.Context() defer cancel() return SqlxTransaction(ctx, q.Queryer, q.originalLogger(), fc, txOpts...) diff --git a/core/services/pg/sqlx.go b/core/services/pg/sqlx.go index 820cd51712e..c371c292138 100644 --- a/core/services/pg/sqlx.go +++ b/core/services/pg/sqlx.go @@ -35,7 +35,7 @@ func WrapDbWithSqlx(rdb *sql.DB) *sqlx.DB { return db } -func SqlxTransaction(ctx context.Context, q Queryer, lggr logger.Logger, fc func(q Queryer) error, txOpts ...TxOptions) (err error) { +func SqlxTransaction(ctx context.Context, q Queryer, lggr logger.Logger, fc func(q Queryer) error, txOpts ...TxOption) (err error) { switch db := q.(type) { case *sqlx.Tx: // nested transaction: just use the outer transaction diff --git a/core/services/pg/transaction.go b/core/services/pg/transaction.go index 92d72b3d81b..74841d010bf 100644 --- a/core/services/pg/transaction.go +++ b/core/services/pg/transaction.go @@ -15,44 +15,21 @@ import ( corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" ) -type TxOptions struct { - sql.TxOptions -} +// NOTE: This is the default level in Postgres anyway, we just make it +// explicit here +const defaultIsolation = sql.LevelReadCommitted -// NOTE: In an ideal world the timeouts below would be set to something sane in -// the postgres configuration by the user. Since we do not live in an ideal -// world, it is necessary to override them here. -// -// They cannot easily be set at a session level due to how Go's connection -// pooling works. -const ( - // NOTE: This is the default level in Postgres anyway, we just make it - // explicit here - DefaultIsolation = sql.LevelReadCommitted -) +// TxOption is a functional option for SQL transactions. +type TxOption func(*sql.TxOptions) -func OptReadOnlyTx() TxOptions { - return TxOptions{TxOptions: sql.TxOptions{ReadOnly: true}} -} - -func applyDefaults(optss []TxOptions) (txOpts sql.TxOptions) { - readOnly := false - if len(optss) > 0 { - opts := optss[0] - readOnly = opts.ReadOnly - } - txOpts = sql.TxOptions{ - ReadOnly: readOnly, +func OptReadOnlyTx() TxOption { + return func(opts *sql.TxOptions) { + opts.ReadOnly = true } - return } -func SqlTransaction(ctx context.Context, rdb *sql.DB, lggr logger.Logger, fn func(tx *sqlx.Tx) error, optss ...TxOptions) (err error) { +func SqlTransaction(ctx context.Context, rdb *sql.DB, lggr logger.Logger, fn func(tx *sqlx.Tx) error, opts ...TxOption) (err error) { db := WrapDbWithSqlx(rdb) - return sqlxTransaction(ctx, db, lggr, fn, optss...) -} - -func sqlxTransaction(ctx context.Context, db *sqlx.DB, lggr logger.Logger, fn func(tx *sqlx.Tx) error, optss ...TxOptions) (err error) { wrapFn := func(q Queryer) error { tx, ok := q.(*sqlx.Tx) if !ok { @@ -60,16 +37,19 @@ func sqlxTransaction(ctx context.Context, db *sqlx.DB, lggr logger.Logger, fn fu } return fn(tx) } - return sqlxTransactionQ(ctx, db, lggr, wrapFn, optss...) + return sqlxTransactionQ(ctx, db, lggr, wrapFn, opts...) } -// TxBeginner can be a db or a conn, anything that implements BeginTxx -type TxBeginner interface { +// txBeginner can be a db or a conn, anything that implements BeginTxx +type txBeginner interface { BeginTxx(context.Context, *sql.TxOptions) (*sqlx.Tx, error) } -func sqlxTransactionQ(ctx context.Context, db TxBeginner, lggr logger.Logger, fn func(q Queryer) error, optss ...TxOptions) (err error) { - txOpts := applyDefaults(optss) +func sqlxTransactionQ(ctx context.Context, db txBeginner, lggr logger.Logger, fn func(q Queryer) error, opts ...TxOption) (err error) { + var txOpts sql.TxOptions + for _, o := range opts { + o(&txOpts) + } var tx *sqlx.Tx tx, err = db.BeginTxx(ctx, &txOpts) From 50fbfd2bce762745122e514329fa6be93f867e28 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 15 Nov 2023 06:03:41 -0600 Subject: [PATCH 152/271] core/store/migrate: add context and error; remove panics (#11295) --- core/cmd/shell.go | 6 ++--- core/cmd/shell_local.go | 34 +++++++++++++++-------- core/store/migrate/migrate.go | 43 +++++++++++++++++------------- core/store/migrate/migrate_test.go | 11 ++++---- 4 files changed, 56 insertions(+), 38 deletions(-) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index e1ac0b99caa..07cd2185dc2 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -145,7 +145,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G return nil, err } - err = handleNodeVersioning(db, appLggr, cfg.RootDir(), cfg.Database(), cfg.WebServer().HTTPPort()) + err = handleNodeVersioning(ctx, db, appLggr, cfg.RootDir(), cfg.Database(), cfg.WebServer().HTTPPort()) if err != nil { return nil, err } @@ -229,7 +229,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G } // handleNodeVersioning is a setup-time helper to encapsulate version changes and db migration -func handleNodeVersioning(db *sqlx.DB, appLggr logger.Logger, rootDir string, cfg config.Database, healthReportPort uint16) error { +func handleNodeVersioning(ctx context.Context, db *sqlx.DB, appLggr logger.Logger, rootDir string, cfg config.Database, healthReportPort uint16) error { var err error // Set up the versioning Configs verORM := versioning.NewORM(db, appLggr, cfg.DefaultQueryTimeout()) @@ -260,7 +260,7 @@ func handleNodeVersioning(db *sqlx.DB, appLggr logger.Logger, rootDir string, cf // Migrate the database if cfg.MigrateDatabase() { - if err = migrate.Migrate(db.DB, appLggr); err != nil { + if err = migrate.Migrate(ctx, db.DB, appLggr); err != nil { return fmt.Errorf("initializeORM#Migrate: %w", err) } } diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 954cead5c37..70d75f761b5 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -705,9 +705,17 @@ func (s *Shell) validateDB(c *cli.Context) error { return s.configExitErr(s.Config.ValidateDB) } +// ctx returns a context.Context that will be cancelled when SIGINT|SIGTERM is received +func (s *Shell) ctx() context.Context { + ctx, cancel := context.WithCancel(context.Background()) + go shutdown.HandleShutdown(func(_ string) { cancel() }) + return ctx +} + // ResetDatabase drops, creates and migrates the database specified by CL_DATABASE_URL or Database.URL // in secrets TOML. This is useful to set up the database for testing func (s *Shell) ResetDatabase(c *cli.Context) error { + ctx := s.ctx() cfg := s.Config.Database() parsed := cfg.URL() if parsed.String() == "" { @@ -727,7 +735,7 @@ func (s *Shell) ResetDatabase(c *cli.Context) error { return s.errorOut(err) } lggr.Debugf("Migrating database: %#v", parsed.String()) - if err := migrateDB(cfg, lggr); err != nil { + if err := migrateDB(ctx, cfg, lggr); err != nil { return s.errorOut(err) } schema, err := dumpSchema(parsed) @@ -736,7 +744,7 @@ func (s *Shell) ResetDatabase(c *cli.Context) error { } lggr.Debugf("Testing rollback and re-migrate for database: %#v", parsed.String()) var baseVersionID int64 = 54 - if err := downAndUpDB(cfg, lggr, baseVersionID); err != nil { + if err := downAndUpDB(ctx, cfg, lggr, baseVersionID); err != nil { return s.errorOut(err) } if err := checkSchema(parsed, schema); err != nil { @@ -828,6 +836,7 @@ func (s *Shell) PrepareTestDatabaseUserOnly(c *cli.Context) error { // MigrateDatabase migrates the database func (s *Shell) MigrateDatabase(_ *cli.Context) error { + ctx := s.ctx() cfg := s.Config.Database() parsed := cfg.URL() if parsed.String() == "" { @@ -840,7 +849,7 @@ func (s *Shell) MigrateDatabase(_ *cli.Context) error { } s.Logger.Infof("Migrating database: %#v", parsed.String()) - if err := migrateDB(cfg, s.Logger); err != nil { + if err := migrateDB(ctx, cfg, s.Logger); err != nil { return s.errorOut(err) } return nil @@ -848,6 +857,7 @@ func (s *Shell) MigrateDatabase(_ *cli.Context) error { // RollbackDatabase rolls back the database via down migrations. func (s *Shell) RollbackDatabase(c *cli.Context) error { + ctx := s.ctx() var version null.Int if c.Args().Present() { arg := c.Args().First() @@ -863,7 +873,7 @@ func (s *Shell) RollbackDatabase(c *cli.Context) error { return fmt.Errorf("failed to initialize orm: %v", err) } - if err := migrate.Rollback(db.DB, s.Logger, version); err != nil { + if err := migrate.Rollback(ctx, db.DB, s.Logger, version); err != nil { return fmt.Errorf("migrateDB failed: %v", err) } @@ -872,12 +882,13 @@ func (s *Shell) RollbackDatabase(c *cli.Context) error { // VersionDatabase displays the current database version. func (s *Shell) VersionDatabase(_ *cli.Context) error { + ctx := s.ctx() db, err := newConnection(s.Config.Database()) if err != nil { return fmt.Errorf("failed to initialize orm: %v", err) } - version, err := migrate.Current(db.DB, s.Logger) + version, err := migrate.Current(ctx, db.DB, s.Logger) if err != nil { return fmt.Errorf("migrateDB failed: %v", err) } @@ -888,12 +899,13 @@ func (s *Shell) VersionDatabase(_ *cli.Context) error { // StatusDatabase displays the database migration status func (s *Shell) StatusDatabase(_ *cli.Context) error { + ctx := s.ctx() db, err := newConnection(s.Config.Database()) if err != nil { return fmt.Errorf("failed to initialize orm: %v", err) } - if err = migrate.Status(db.DB, s.Logger); err != nil { + if err = migrate.Status(ctx, db.DB, s.Logger); err != nil { return fmt.Errorf("Status failed: %v", err) } return nil @@ -1017,27 +1029,27 @@ func dropAndCreatePristineDB(db *sqlx.DB, template string) (err error) { return nil } -func migrateDB(config dbConfig, lggr logger.Logger) error { +func migrateDB(ctx context.Context, config dbConfig, lggr logger.Logger) error { db, err := newConnection(config) if err != nil { return fmt.Errorf("failed to initialize orm: %v", err) } - if err = migrate.Migrate(db.DB, lggr); err != nil { + if err = migrate.Migrate(ctx, db.DB, lggr); err != nil { return fmt.Errorf("migrateDB failed: %v", err) } return db.Close() } -func downAndUpDB(cfg dbConfig, lggr logger.Logger, baseVersionID int64) error { +func downAndUpDB(ctx context.Context, cfg dbConfig, lggr logger.Logger, baseVersionID int64) error { db, err := newConnection(cfg) if err != nil { return fmt.Errorf("failed to initialize orm: %v", err) } - if err = migrate.Rollback(db.DB, lggr, null.IntFrom(baseVersionID)); err != nil { + if err = migrate.Rollback(ctx, db.DB, lggr, null.IntFrom(baseVersionID)); err != nil { return fmt.Errorf("test rollback failed: %v", err) } - if err = migrate.Migrate(db.DB, lggr); err != nil { + if err = migrate.Migrate(ctx, db.DB, lggr); err != nil { return fmt.Errorf("second migrateDB failed: %v", err) } return db.Close() diff --git a/core/store/migrate/migrate.go b/core/store/migrate/migrate.go index 1e58d7a0b05..04da4c48e92 100644 --- a/core/store/migrate/migrate.go +++ b/core/store/migrate/migrate.go @@ -36,22 +36,22 @@ func init() { } // Ensure we migrated from v1 migrations to goose_migrations -func ensureMigrated(db *sql.DB, lggr logger.Logger) { +func ensureMigrated(ctx context.Context, db *sql.DB, lggr logger.Logger) error { sqlxDB := pg.WrapDbWithSqlx(db) var names []string - err := sqlxDB.Select(&names, `SELECT id FROM migrations`) + err := sqlxDB.SelectContext(ctx, &names, `SELECT id FROM migrations`) if err != nil { // already migrated - return + return nil } - err = pg.SqlTransaction(context.Background(), db, lggr, func(tx *sqlx.Tx) error { + err = pg.SqlTransaction(ctx, db, lggr, func(tx *sqlx.Tx) error { // ensure that no legacy job specs are present: we _must_ bail out early if // so because otherwise we run the risk of dropping working jobs if the // user has not read the release notes return migrations.CheckNoLegacyJobs(tx.Tx) }) if err != nil { - panic(err) + return err } // Look for the squashed migration. If not present, the db needs to be migrated on an earlier release first @@ -62,18 +62,18 @@ func ensureMigrated(db *sql.DB, lggr logger.Logger) { } } if !found { - panic("Database state is too old. Need to migrate to chainlink version 0.9.10 first before upgrading to this version. This upgrade is NOT REVERSIBLE, so it is STRONGLY RECOMMENDED that you take a database backup before continuing.") + return errors.New("database state is too old. Need to migrate to chainlink version 0.9.10 first before upgrading to this version. This upgrade is NOT REVERSIBLE, so it is STRONGLY RECOMMENDED that you take a database backup before continuing") } // ensure a goose migrations table exists with it's initial v0 if _, err = goose.GetDBVersion(db); err != nil { - panic(err) + return err } // insert records for existing migrations //nolint sql := fmt.Sprintf(`INSERT INTO %s (version_id, is_applied) VALUES ($1, true);`, goose.TableName()) - err = pg.SqlTransaction(context.Background(), db, lggr, func(tx *sqlx.Tx) error { + return pg.SqlTransaction(ctx, db, lggr, func(tx *sqlx.Tx) error { for _, name := range names { var id int64 // the first migration doesn't follow the naming convention @@ -100,33 +100,38 @@ func ensureMigrated(db *sql.DB, lggr logger.Logger) { _, err = db.Exec("DROP TABLE migrations;") return err }) - if err != nil { - panic(err) - } } -func Migrate(db *sql.DB, lggr logger.Logger) error { - ensureMigrated(db, lggr) +func Migrate(ctx context.Context, db *sql.DB, lggr logger.Logger) error { + if err := ensureMigrated(ctx, db, lggr); err != nil { + return err + } // WithAllowMissing is necessary when upgrading from 0.10.14 since it // includes out-of-order migrations return goose.Up(db, MIGRATIONS_DIR, goose.WithAllowMissing()) } -func Rollback(db *sql.DB, lggr logger.Logger, version null.Int) error { - ensureMigrated(db, lggr) +func Rollback(ctx context.Context, db *sql.DB, lggr logger.Logger, version null.Int) error { + if err := ensureMigrated(ctx, db, lggr); err != nil { + return err + } if version.Valid { return goose.DownTo(db, MIGRATIONS_DIR, version.Int64) } return goose.Down(db, MIGRATIONS_DIR) } -func Current(db *sql.DB, lggr logger.Logger) (int64, error) { - ensureMigrated(db, lggr) +func Current(ctx context.Context, db *sql.DB, lggr logger.Logger) (int64, error) { + if err := ensureMigrated(ctx, db, lggr); err != nil { + return -1, err + } return goose.EnsureDBVersion(db) } -func Status(db *sql.DB, lggr logger.Logger) error { - ensureMigrated(db, lggr) +func Status(ctx context.Context, db *sql.DB, lggr logger.Logger) error { + if err := ensureMigrated(ctx, db, lggr); err != nil { + return err + } return goose.Status(db, MIGRATIONS_DIR) } diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index ef105c75ff6..0ff09000161 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -391,25 +391,26 @@ func TestMigrate_101_GenericOCR2(t *testing.T) { } func TestMigrate(t *testing.T) { + ctx := testutils.Context(t) lggr := logger.TestLogger(t) _, db := heavyweight.FullTestDBEmptyV2(t, nil) err := goose.UpTo(db.DB, migrationDir, 100) require.NoError(t, err) - err = migrate.Status(db.DB, lggr) + err = migrate.Status(ctx, db.DB, lggr) require.NoError(t, err) - ver, err := migrate.Current(db.DB, lggr) + ver, err := migrate.Current(ctx, db.DB, lggr) require.NoError(t, err) require.Equal(t, int64(100), ver) - err = migrate.Migrate(db.DB, lggr) + err = migrate.Migrate(ctx, db.DB, lggr) require.NoError(t, err) - err = migrate.Rollback(db.DB, lggr, null.IntFrom(99)) + err = migrate.Rollback(ctx, db.DB, lggr, null.IntFrom(99)) require.NoError(t, err) - ver, err = migrate.Current(db.DB, lggr) + ver, err = migrate.Current(ctx, db.DB, lggr) require.NoError(t, err) require.Equal(t, int64(99), ver) } From 5fd94b0c36556dfa6b81153214f30a8c75ae0529 Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 15 Nov 2023 13:38:05 +0000 Subject: [PATCH 153/271] [BCF-2788] Correctly unmarshal the plugin config (#11283) --- core/services/ocr2/delegate.go | 2 +- core/services/ocr2/validate/validate.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index bbb3b5cf7ae..20c13512978 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -617,7 +617,7 @@ func (d *Delegate) newServicesGenericPlugin( Command: command, ProviderType: cconf.ProviderType, TelemetryType: cconf.TelemetryType, - PluginConfig: string(p.PluginConfig), + PluginConfig: p.PluginConfig, } pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner) diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index 78802f6559c..65b95cb31a1 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -134,7 +134,7 @@ type coreConfig struct { type OCR2GenericPluginConfig struct { CoreConfig coreConfig `json:"coreConfig"` - PluginConfig json.RawMessage + PluginConfig string } func validateOCR2GenericPluginSpec(jsonConfig job.JSONConfig) error { From 66d9e23219831c8ecd937431ead528dc0e9afcbb Mon Sep 17 00:00:00 2001 From: george-dorin <120329946+george-dorin@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:00:10 +0200 Subject: [PATCH 154/271] Test job creation and replacement (#9934) * Initial draft * - Add OCR, OCR2, Automation, VRF and cron tests * - Update keeper test * Fix merge * - Split functions into DeleteJobs and DeleteBridges * Use standardCleanup * Fix merge conflict * Update error style * Remove unused method --- .../actions/ocr2_helpers_local.go | 47 ++++++++ integration-tests/client/chainlink.go | 7 +- integration-tests/client/chainlink_models.go | 5 + integration-tests/smoke/cron_test.go | 71 +++++++++++ integration-tests/smoke/keeper_test.go | 66 +++++++++++ .../smoke/keeper_test.go_test_list.json | 3 + integration-tests/smoke/ocr2_test.go | 93 ++++++++++++++- integration-tests/smoke/ocr_test.go | 66 ++++++++++- integration-tests/smoke/vrf_test.go | 111 ++++++++++++++++++ 9 files changed, 463 insertions(+), 6 deletions(-) diff --git a/integration-tests/actions/ocr2_helpers_local.go b/integration-tests/actions/ocr2_helpers_local.go index 65e0a466bee..e1b470bd0d4 100644 --- a/integration-tests/actions/ocr2_helpers_local.go +++ b/integration-tests/actions/ocr2_helpers_local.go @@ -19,6 +19,7 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -273,3 +274,49 @@ func GetOracleIdentitiesWithKeyIndexLocal( return S, oracleIdentities, eg.Wait() } + +// DeleteJobs will delete ALL jobs from the nodes +func DeleteJobs(nodes []*client.ChainlinkClient) error { + for _, node := range nodes { + if node == nil { + return fmt.Errorf("found a nil chainlink node in the list of chainlink nodes while tearing down: %v", nodes) + } + jobs, _, err := node.ReadJobs() + if err != nil { + return fmt.Errorf("error reading jobs from chainlink node: %w", err) + } + for _, maps := range jobs.Data { + if _, ok := maps["id"]; !ok { + return fmt.Errorf("error reading job id from chainlink node's jobs %+v", jobs.Data) + } + id := maps["id"].(string) + _, err2 := node.DeleteJob(id) + if err2 != nil { + return fmt.Errorf("error deleting job from chainlink node: %w", err) + } + } + } + return nil +} + +// DeleteBridges will delete ALL bridges from the nodes +func DeleteBridges(nodes []*client.ChainlinkClient) error { + for _, node := range nodes { + if node == nil { + return fmt.Errorf("found a nil chainlink node in the list of chainlink nodes while tearing down: %v", nodes) + } + + bridges, _, err := node.ReadBridges() + if err != nil { + return err + } + for _, b := range bridges.Data { + _, err = node.DeleteBridge(b.Attributes.Name) + if err != nil { + return err + } + } + + } + return nil +} diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 7d5825c261a..4603679ff85 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -5,12 +5,11 @@ import ( "fmt" "math/big" "net/http" + "os" "strings" "sync" "time" - "os" - "github.com/ethereum/go-ethereum/common" "github.com/go-resty/resty/v2" "github.com/rs/zerolog" @@ -303,8 +302,8 @@ func (c *ChainlinkClient) ReadBridge(name string) (*BridgeType, *http.Response, } // ReadBridges reads bridges from the Chainlink node -func (c *ChainlinkClient) ReadBridges() (*ResponseSlice, *resty.Response, error) { - result := &ResponseSlice{} +func (c *ChainlinkClient) ReadBridges() (*Bridges, *resty.Response, error) { + result := &Bridges{} c.l.Info().Str(NodeURL, c.Config.URL).Msg("Getting all bridges") resp, err := c.APIClient.R(). SetResult(&result). diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index c6d1209d2ea..17a0a50845b 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -107,6 +107,11 @@ type BridgeTypeData struct { Attributes BridgeTypeAttributes `json:"attributes"` } +// Bridges is the model that represents the bridges when read on a Chainlink node +type Bridges struct { + Data []BridgeTypeData `json:"data"` +} + // BridgeTypeAttributes is the model that represents the bridge when read or created on a Chainlink node type BridgeTypeAttributes struct { Name string `json:"name"` diff --git a/integration-tests/smoke/cron_test.go b/integration-tests/smoke/cron_test.go index e9d20f588e2..013e4c631c7 100644 --- a/integration-tests/smoke/cron_test.go +++ b/integration-tests/smoke/cron_test.go @@ -60,3 +60,74 @@ func TestCronBasic(t *testing.T) { } }, "2m", "3s").Should(gomega.Succeed()) } + +func TestCronJobReplacement(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithMockAdapter(). + WithCLNodes(1). + WithStandardCleanup(). + Build() + require.NoError(t, err) + + err = env.MockAdapter.SetAdapterBasedIntValuePath("/variable", []string{http.MethodGet, http.MethodPost}, 5) + require.NoError(t, err, "Setting value path in mockserver shouldn't fail") + + bta := &client.BridgeTypeAttributes{ + Name: fmt.Sprintf("variable-%s", uuid.NewString()), + URL: fmt.Sprintf("%s/variable", env.MockAdapter.InternalEndpoint), + RequestData: "{}", + } + err = env.ClCluster.Nodes[0].API.MustCreateBridge(bta) + require.NoError(t, err, "Creating bridge in chainlink node shouldn't fail") + + // CRON job creation and replacement + job, err := env.ClCluster.Nodes[0].API.MustCreateJob(&client.CronJobSpec{ + Schedule: "CRON_TZ=UTC * * * * * *", + ObservationSource: client.ObservationSourceSpecBridge(bta), + }) + require.NoError(t, err, "Creating Cron Job in chainlink node shouldn't fail") + + gom := gomega.NewWithT(t) + gom.Eventually(func(g gomega.Gomega) { + jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) + if err != nil { + l.Info().Err(err).Msg("error while waiting for job runs") + } + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Reading Job run data shouldn't fail") + + g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 5), "Expected number of job runs to be greater than 5, but got %d", len(jobRuns.Data)) + + for _, jr := range jobRuns.Data { + g.Expect(jr.Attributes.Errors).Should(gomega.Equal([]interface{}{nil}), "Job run %s shouldn't have errors", jr.ID) + } + }, "3m", "3s").Should(gomega.Succeed()) + + err = env.ClCluster.Nodes[0].API.MustDeleteJob(job.Data.ID) + require.NoError(t, err) + + job, err = env.ClCluster.Nodes[0].API.MustCreateJob(&client.CronJobSpec{ + Schedule: "CRON_TZ=UTC * * * * * *", + ObservationSource: client.ObservationSourceSpecBridge(bta), + }) + require.NoError(t, err, "Recreating Cron Job in chainlink node shouldn't fail") + + gom.Eventually(func(g gomega.Gomega) { + jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) + if err != nil { + l.Info().Err(err).Msg("error while waiting for job runs") + } + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Reading Job run data shouldn't fail") + + g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 5), "Expected number of job runs to be greater than 5, but got %d", len(jobRuns.Data)) + + for _, jr := range jobRuns.Data { + g.Expect(jr.Attributes.Errors).Should(gomega.Equal([]interface{}{nil}), "Job run %s shouldn't have errors", jr.ID) + } + }, "3m", "3s").Should(gomega.Succeed()) + +} diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index b28ab1ff101..29a819af136 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1126,3 +1126,69 @@ func setupKeeperTest(t *testing.T) ( return env.EVMClient, env.ClCluster.NodeAPIs(), env.ContractDeployer, linkTokenContract, env } + +func TestKeeperJobReplacement(t *testing.T) { + t.Parallel() + registryVersion := ethereum.RegistryVersion_1_3 + + l := logging.GetTestLogger(t) + chainClient, chainlinkNodes, contractDeployer, linkToken, _ := setupKeeperTest(t) + registry, _, consumers, upkeepIDs := actions.DeployKeeperContracts( + t, + registryVersion, + keeperDefaultRegistryConfig, + keeperDefaultUpkeepsToDeploy, + keeperDefaultUpkeepGasLimit, + linkToken, + contractDeployer, + chainClient, + big.NewInt(keeperDefaultLinkFunds), + ) + gom := gomega.NewGomegaWithT(t) + + _, err := actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + require.NoError(t, err, "Error creating keeper jobs") + err = chainClient.WaitForEvents() + require.NoError(t, err, "Error creating keeper jobs") + + gom.Eventually(func(g gomega.Gomega) error { + // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 + for i := 0; i < len(upkeepIDs); i++ { + counter, err := consumers[i].Counter(utils.TestContext(t)) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) + g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), + "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) + l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Number of upkeeps performed") + } + return nil + }, "5m", "1s").Should(gomega.Succeed()) + + for _, n := range chainlinkNodes { + jobs, _, err := n.ReadJobs() + require.NoError(t, err) + for _, maps := range jobs.Data { + _, ok := maps["id"] + require.Equal(t, true, ok) + id := maps["id"].(string) + _, err := n.DeleteJob(id) + require.NoError(t, err) + } + } + + _, err = actions.CreateKeeperJobsLocal(l, chainlinkNodes, registry, contracts.OCRv2Config{}, chainClient.GetChainID().String()) + require.NoError(t, err, "Error creating keeper jobs") + err = chainClient.WaitForEvents() + require.NoError(t, err, "Error creating keeper jobs") + + gom.Eventually(func(g gomega.Gomega) error { + // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 + for i := 0; i < len(upkeepIDs); i++ { + counter, err := consumers[i].Counter(utils.TestContext(t)) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) + g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), + "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) + l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Number of upkeeps performed") + } + return nil + }, "5m", "1s").Should(gomega.Succeed()) +} diff --git a/integration-tests/smoke/keeper_test.go_test_list.json b/integration-tests/smoke/keeper_test.go_test_list.json index dfa2c49358c..b874bc65ee0 100644 --- a/integration-tests/smoke/keeper_test.go_test_list.json +++ b/integration-tests/smoke/keeper_test.go_test_list.json @@ -53,6 +53,9 @@ }, { "name": "TestKeeperUpdateCheckData" + }, + { + "name": "TestKeeperJobReplacement" } ] } diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 5950e9febb6..bec82e633f8 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" @@ -92,3 +91,95 @@ func TestOCRv2Basic(t *testing.T) { roundData.Answer.Int64(), ) } + +func TestOCRv2JobReplacement(t *testing.T) { + l := logging.GetTestLogger(t) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithMockAdapter(). + WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), + node.WithOCR2(), + node.WithP2Pv2(), + node.WithTracing(), + )). + WithCLNodes(6). + WithFunding(big.NewFloat(.1)). + WithStandardCleanup(). + Build() + require.NoError(t, err) + + env.ParallelTransactions(true) + + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + linkToken, err := env.ContractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + + err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + require.NoError(t, err, "Error funding Chainlink nodes") + + // Gather transmitters + var transmitters []string + for _, node := range workerNodes { + addr, err := node.PrimaryEthAddress() + if err != nil { + require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) + } + transmitters = append(transmitters, addr) + } + + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() + aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) + require.NoError(t, err, "Error building OCRv2 config") + + err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) + require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") + + err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err := aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(1)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 5 but got %d", + roundData.Answer.Int64(), + ) + + err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) + require.NoError(t, err) + err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err) + + roundData, err = aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(2)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 10 but got %d", + roundData.Answer.Int64(), + ) + + err = actions.DeleteJobs(nodeClients) + require.NoError(t, err) + + err = actions.DeleteBridges(nodeClients) + require.NoError(t, err) + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + err = actions.StartNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err = aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(3)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(15), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 15 but got %d", + roundData.Answer.Int64(), + ) +} diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 45205565e21..8fbc1109e2b 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/utils" @@ -59,3 +58,68 @@ func TestOCRBasic(t *testing.T) { require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } + +func TestOCRJobReplacement(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithMockAdapter(). + WithCLNodes(6). + WithFunding(big.NewFloat(.1)). + WithStandardCleanup(). + Build() + require.NoError(t, err) + + env.ParallelTransactions(true) + + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + linkTokenContract, err := env.ContractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + + ocrInstances, err := actions.DeployOCRContractsLocal(1, linkTokenContract, env.ContractDeployer, workerNodes, env.EVMClient) + require.NoError(t, err) + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, "Error waiting for events") + + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) + require.NoError(t, err) + + err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) + require.NoError(t, err) + + answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) + + err = actions.SetAllAdapterResponsesToTheSameValueLocal(10, ocrInstances, workerNodes, env.MockAdapter) + require.NoError(t, err) + err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) + require.NoError(t, err) + + answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + require.NoError(t, err, "Error getting latest OCR answer") + require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) + + err = actions.DeleteJobs(nodeClients) + require.NoError(t, err) + + err = actions.DeleteBridges(nodeClients) + require.NoError(t, err) + + //Recreate job + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) + require.NoError(t, err) + + err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) + require.NoError(t, err) + + answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) + +} diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index 61d2c5cdd70..e477d459264 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -107,3 +107,114 @@ func TestVRFBasic(t *testing.T) { }, timeout, "1s").Should(gomega.Succeed()) } } + +func TestVRFJobReplacement(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(1). + WithFunding(big.NewFloat(.1)). + WithStandardCleanup(). + Build() + require.NoError(t, err) + env.ParallelTransactions(true) + + lt, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + contracts, err := vrfv1.DeployVRFContracts(env.ContractDeployer, env.EVMClient, lt) + require.NoError(t, err, "Deploying VRF Contracts shouldn't fail") + + err = lt.Transfer(contracts.Consumer.Address(), big.NewInt(2e18)) + require.NoError(t, err, "Funding consumer contract shouldn't fail") + _, err = env.ContractDeployer.DeployVRFContract() + require.NoError(t, err, "Deploying VRF contract shouldn't fail") + err = env.EVMClient.WaitForEvents() + require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") + + for _, n := range env.ClCluster.Nodes { + nodeKey, err := n.API.MustCreateVRFKey() + require.NoError(t, err, "Creating VRF key shouldn't fail") + l.Debug().Interface("Key JSON", nodeKey).Msg("Created proving key") + pubKeyCompressed := nodeKey.Data.ID + jobUUID := uuid.New() + os := &client.VRFTxPipelineSpec{ + Address: contracts.Coordinator.Address(), + } + ost, err := os.String() + require.NoError(t, err, "Building observation source spec shouldn't fail") + job, err := n.API.MustCreateJob(&client.VRFJobSpec{ + Name: fmt.Sprintf("vrf-%s", jobUUID), + CoordinatorAddress: contracts.Coordinator.Address(), + MinIncomingConfirmations: 1, + PublicKey: pubKeyCompressed, + ExternalJobID: jobUUID.String(), + EVMChainID: env.EVMClient.GetChainID().String(), + ObservationSource: ost, + }) + require.NoError(t, err, "Creating VRF Job shouldn't fail") + + oracleAddr, err := n.API.PrimaryEthAddress() + require.NoError(t, err, "Getting primary ETH address of chainlink node shouldn't fail") + provingKey, err := actions.EncodeOnChainVRFProvingKey(*nodeKey) + require.NoError(t, err, "Encoding on-chain VRF Proving key shouldn't fail") + err = contracts.Coordinator.RegisterProvingKey( + big.NewInt(1), + oracleAddr, + provingKey, + actions.EncodeOnChainExternalJobID(jobUUID), + ) + require.NoError(t, err, "Registering the on-chain VRF Proving key shouldn't fail") + encodedProvingKeys := make([][2]*big.Int, 0) + encodedProvingKeys = append(encodedProvingKeys, provingKey) + + requestHash, err := contracts.Coordinator.HashOfKey(utils.TestContext(t), encodedProvingKeys[0]) + require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") + err = contracts.Consumer.RequestRandomness(requestHash, big.NewInt(1)) + require.NoError(t, err, "Requesting randomness shouldn't fail") + + gom := gomega.NewGomegaWithT(t) + timeout := time.Minute * 2 + gom.Eventually(func(g gomega.Gomega) { + jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") + + out, err := contracts.Consumer.RandomnessOutput(utils.TestContext(t)) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") + // Checks that the job has actually run + g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), + fmt.Sprintf("Expected the VRF job to run once or more after %s", timeout)) + + g.Expect(out.Uint64()).ShouldNot(gomega.BeNumerically("==", 0), "Expected the VRF job give an answer other than 0") + l.Debug().Uint64("Output", out.Uint64()).Msg("Randomness fulfilled") + }, timeout, "1s").Should(gomega.Succeed()) + + err = n.API.MustDeleteJob(job.Data.ID) + require.NoError(t, err) + + job, err = n.API.MustCreateJob(&client.VRFJobSpec{ + Name: fmt.Sprintf("vrf-%s", jobUUID), + CoordinatorAddress: contracts.Coordinator.Address(), + MinIncomingConfirmations: 1, + PublicKey: pubKeyCompressed, + ExternalJobID: jobUUID.String(), + EVMChainID: env.EVMClient.GetChainID().String(), + ObservationSource: ost, + }) + require.NoError(t, err, "Recreating VRF Job shouldn't fail") + gom.Eventually(func(g gomega.Gomega) { + jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") + + out, err := contracts.Consumer.RandomnessOutput(utils.TestContext(t)) + g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") + // Checks that the job has actually run + g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), + fmt.Sprintf("Expected the VRF job to run once or more after %s", timeout)) + g.Expect(out.Uint64()).ShouldNot(gomega.BeNumerically("==", 0), "Expected the VRF job give an answer other than 0") + l.Debug().Uint64("Output", out.Uint64()).Msg("Randomness fulfilled") + }, timeout, "1s").Should(gomega.Succeed()) + } +} From a75f900e0536f3eb9282284ac5cfec57206a3ec6 Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 15 Nov 2023 14:04:05 -0700 Subject: [PATCH 155/271] [TT-707] Add chainlink image build to test image publish for workflow dispatch (#11304) This allows teams to run this one dispatch and not have to make a work in progress PR before they start testing. --- .../workflows/integration-tests-publish.yml | 62 +++++++++++++++++-- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index 9579b83b98e..9eb2673db2d 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -7,9 +7,6 @@ on: - develop workflow_dispatch: inputs: - chainlink-tests-tag: - description: 'The tag to be pushed' - required: true ctf-base-image-tag: description: | 'The tag of the CTF base image to be used, @@ -18,7 +15,8 @@ on: required: true env: - ECR_TAG_BASE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests + ECR_TAG: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:develop + CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink jobs: publish-integration-test-image: @@ -41,19 +39,71 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Setup Other Tags + id: tags + env: + BASE_IMAGE_TAG: ${{ inputs.ctf-base-image-tag}} + run: | + if [ -z "${BASE_IMAGE_TAG+x}" ]; then + echo "ctf-base-image-tag is not set, we are part of a merge and want to push the develop tag" + echo "other_tags=${ECR_TAG}" >> $GITHUB_OUTPUT + fi - name: Build Image uses: ./.github/actions/build-test-image with: - other_tags: "${{ env.ECR_TAG_BASE }}:${{ inputs.chainlink-tests-tag || 'develop' }}" + other_tags: ${{ steps.tags.outputs.other_tags }} base_image_tag: ${{ inputs.ctf-base-image-tag }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - name: Notify Slack - if: failure() + # Only run this notification for merge to develop failures + if: failure() && github.event_name != 'workflow_dispatch' uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} with: channel-id: "#team-test-tooling-internal" slack-message: ":x: :mild-panic-intensifies: Publish Integration Test Image failed: ${{ job.html_url }}\n${{ format('https://github.com/smartcontractkit/chainlink/actions/runs/{0}', github.run_id) }}" + build-chainlink-image: + environment: integration + # Only run this build for workflow_dispatch + if: ${{ inputs.ctf-base-image-tag != '' }} + permissions: + id-token: write + contents: read + strategy: + matrix: + image: + - name: "" + dockerfile: core/chainlink.Dockerfile + tag-suffix: "" + # uncomment in the future if we end up needing to soak test the plugins image + # - name: (plugins) + # dockerfile: plugins/chainlink.Dockerfile + # tag-suffix: -plugins + name: Build Chainlink Image ${{ matrix.image.name }} + runs-on: ubuntu20.04-8cores-32GB + steps: + - name: Collect Metrics + 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: Build Chainlink Image ${{ matrix.image.name }} + continue-on-error: true + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.sha }} + - name: Build Chainlink Image + uses: ./.github/actions/build-chainlink-image + with: + tag_suffix: ${{ matrix.image.tag-suffix }} + dockerfile: ${{ matrix.image.dockerfile }} + git_commit_sha: ${{ github.sha }} + GRAFANA_CLOUD_BASIC_AUTH: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} + GRAFANA_CLOUD_HOST: ${{ secrets.GRAFANA_CLOUD_HOST }} + AWS_REGION: ${{ secrets.QA_AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} From 2a947ccf50536601a0032650920056d7d68a0421 Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Wed, 15 Nov 2023 19:17:44 -0500 Subject: [PATCH 156/271] [Functions] (test): Add Functions HasUniqueGlobalRequestId foundry test (#11306) * (test): Add Functions HasUniqueGlobalRequestId foundry test * Regenerate gas snapshot --- .../gas-snapshots/functions.gas-snapshot | 35 ++++++++-------- .../v0.8/functions/tests/v1_X/BaseTest.t.sol | 2 +- .../tests/v1_X/FunctionsBilling.t.sol | 40 +++++++++++++++++-- .../src/v0.8/functions/tests/v1_X/Setup.t.sol | 5 ++- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/contracts/gas-snapshots/functions.gas-snapshot b/contracts/gas-snapshots/functions.gas-snapshot index 82b5b494a74..4e6cf83cd1a 100644 --- a/contracts/gas-snapshots/functions.gas-snapshot +++ b/contracts/gas-snapshots/functions.gas-snapshot @@ -1,12 +1,12 @@ -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14577815) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14577793) -ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14577809) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14589229) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14589206) -ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14589178) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14589129) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14589118) -ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14589162) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumGoerli() (gas: 14578318) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumMainnet() (gas: 14578296) +ChainSpecificUtil__getCurrentTxL1GasFees_Arbitrum:test__getCurrentTxL1GasFees_SuccessWhenArbitrumSepolia() (gas: 14578312) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseGoerli() (gas: 14589732) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseMainnet() (gas: 14589709) +ChainSpecificUtil__getCurrentTxL1GasFees_Base:test__getCurrentTxL1GasFees_SuccessWhenBaseSepolia() (gas: 14589681) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismGoerli() (gas: 14589632) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismMainnet() (gas: 14589621) +ChainSpecificUtil__getCurrentTxL1GasFees_Optimism:test__getCurrentTxL1GasFees_SuccessWhenOptimismSepolia() (gas: 14589665) FunctionsBilling_Constructor:test_Constructor_Success() (gas: 14812) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_RevertIfNotRouter() (gas: 13282) FunctionsBilling_DeleteCommitment:test_DeleteCommitment_Success() (gas: 15897) @@ -28,6 +28,7 @@ FunctionsBilling_UpdateConfig:test_UpdateConfig_Success() (gas: 38251) FunctionsBilling__DisperseFeePool:test__DisperseFeePool_RevertIfNotSet() (gas: 8810) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_RevertIfInvalidCommitment() (gas: 13302) FunctionsBilling__FulfillAndBill:test__FulfillAndBill_Success() (gas: 180763) +FunctionsBilling__StartBilling:test__FulfillAndBill_HasUniqueGlobalRequestId() (gas: 398312) FunctionsClient_Constructor:test_Constructor_Success() (gas: 7573) FunctionsClient_FulfillRequest:test_FulfillRequest_MaximumGas() (gas: 497786) FunctionsClient_FulfillRequest:test_FulfillRequest_MinimumGas() (gas: 198990) @@ -51,17 +52,17 @@ FunctionsRequest_DEFAULT_BUFFER_SIZE:test_DEFAULT_BUFFER_SIZE() (gas: 246) FunctionsRequest_EncodeCBOR:test_EncodeCBOR_Success() (gas: 223) FunctionsRequest_REQUEST_DATA_VERSION:test_REQUEST_DATA_VERSION() (gas: 225) FunctionsRouter_Constructor:test_Constructor_Success() (gas: 12007) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 167459) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 157790) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedCostExceedsCommitment() (gas: 167477) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInsufficientGas() (gas: 157808) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidCommitment() (gas: 38115) FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedInvalidRequestId() (gas: 35238) -FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 175935) +FunctionsRouter_Fulfill:test_Fulfill_RequestNotProcessedSubscriptionBalanceInvariant() (gas: 175953) FunctionsRouter_Fulfill:test_Fulfill_RevertIfNotCommittedCoordinator() (gas: 28086) -FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 151478) -FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 321037) -FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 334658) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2509939) -FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 540418) +FunctionsRouter_Fulfill:test_Fulfill_RevertIfPaused() (gas: 151496) +FunctionsRouter_Fulfill:test_Fulfill_SuccessClientNoLongerExists() (gas: 321059) +FunctionsRouter_Fulfill:test_Fulfill_SuccessFulfilled() (gas: 334680) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackReverts() (gas: 2509962) +FunctionsRouter_Fulfill:test_Fulfill_SuccessUserCallbackRunsOutOfGas() (gas: 540441) FunctionsRouter_GetAdminFee:test_GetAdminFee_Success() (gas: 17983) FunctionsRouter_GetAllowListId:test_GetAllowListId_Success() (gas: 12904) FunctionsRouter_GetConfig:test_GetConfig_Success() (gas: 37159) diff --git a/contracts/src/v0.8/functions/tests/v1_X/BaseTest.t.sol b/contracts/src/v0.8/functions/tests/v1_X/BaseTest.t.sol index b012732897f..ffa684e84ce 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/BaseTest.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/BaseTest.t.sol @@ -19,7 +19,7 @@ contract BaseTest is Test { // BaseTest.setUp is often called multiple times from tests' setUp due to inheritance. if (s_baseTestInitialized) return; s_baseTestInitialized = true; - // Set msg.sender to OWNER until stopPrank is called + // Set msg.sender and tx.origin to OWNER until stopPrank is called vm.startPrank(OWNER_ADDRESS, OWNER_ADDRESS); } } diff --git a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol index 6e94e4fc5f7..739521c5305 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/FunctionsBilling.t.sol @@ -8,7 +8,7 @@ import {FunctionsResponse} from "../../dev/v1_X/libraries/FunctionsResponse.sol" import {FunctionsSubscriptions} from "../../dev/v1_X/FunctionsSubscriptions.sol"; import {Routable} from "../../dev/v1_X/Routable.sol"; -import {FunctionsRouterSetup, FunctionsSubscriptionSetup, FunctionsClientRequestSetup, FunctionsMultipleFulfillmentsSetup} from "./Setup.t.sol"; +import {FunctionsRouterSetup, FunctionsSubscriptionSetup, FunctionsClientRequestSetup, FunctionsFulfillmentSetup, FunctionsMultipleFulfillmentsSetup} from "./Setup.t.sol"; /// @notice #constructor contract FunctionsBilling_Constructor is FunctionsSubscriptionSetup { @@ -217,8 +217,42 @@ contract FunctionsBilling__CalculateCostEstimate { } /// @notice #_startBilling -contract FunctionsBilling__StartBilling { - // TODO: make contract internal function helper +contract FunctionsBilling__StartBilling is FunctionsFulfillmentSetup { + function test__FulfillAndBill_HasUniqueGlobalRequestId() public { + // Variables that go into a requestId: + // - Coordinator address + // - Consumer contract + // - Subscription ID, + // - Consumer initiated requests + // - Request data + // - Request data version + // - Request callback gas limit + // - Estimated total cost in Juels + // - Request timeout timestamp + // - tx.origin + + // Request #1 has already been fulfilled by the test setup + + // Reset the nonce (initiatedRequests) by removing and re-adding the consumer + s_functionsRouter.removeConsumer(s_subscriptionId, address(s_functionsClient)); + assertEq(s_functionsRouter.getSubscription(s_subscriptionId).consumers.length, 0); + s_functionsRouter.addConsumer(s_subscriptionId, address(s_functionsClient)); + assertEq(s_functionsRouter.getSubscription(s_subscriptionId).consumers[0], address(s_functionsClient)); + + // Make Request #2 + _sendAndStoreRequest( + 2, + s_requests[1].requestData.sourceCode, + s_requests[1].requestData.secrets, + s_requests[1].requestData.args, + s_requests[1].requestData.bytesArgs, + s_requests[1].requestData.callbackGasLimit + ); + + // Request #1 and #2 should have different request IDs, because the request timeout timestamp has advanced. + // A request cannot be fulfilled in the same block, which prevents removing a consumer in the same block + assertNotEq(s_requests[1].requestId, s_requests[2].requestId); + } } /// @notice #_fulfillAndBill diff --git a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol index 97418958bc2..41ee663e8b0 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/Setup.t.sol @@ -525,7 +525,7 @@ contract FunctionsClientRequestSetup is FunctionsSubscriptionSetup { // Return prank to Owner vm.stopPrank(); - vm.startPrank(OWNER_ADDRESS); + vm.startPrank(OWNER_ADDRESS, OWNER_ADDRESS); } /// @notice Provide a response from the DON to fulfill one or more requests and store the updated balances of the DON & Admin @@ -604,6 +604,9 @@ contract FunctionsFulfillmentSetup is FunctionsClientRequestSetup { function setUp() public virtual override { FunctionsClientRequestSetup.setUp(); + // Fast forward time by 30 seconds to simulate the DON executing the computation + vm.warp(block.timestamp + 30); + // Fulfill request 1 uint256[] memory requestNumberKeys = new uint256[](1); requestNumberKeys[0] = 1; From b012cc426d0ec5515b7c40a10d5924734df760a8 Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Wed, 15 Nov 2023 23:20:24 -0800 Subject: [PATCH 157/271] [Functions] Require minimum balance only for secrets_set, not for list (#11309) Additionally refactor a helper method in connector_handler.go. --- core/services/functions/connector_handler.go | 41 ++++++++----------- .../functions/connector_handler_test.go | 3 -- .../handlers/functions/handler.functions.go | 2 +- .../functions/handler.functions_test.go | 3 +- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 8a8710e6ea6..c5dbff6f10f 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -76,24 +76,21 @@ func (h *functionsConnectorHandler) HandleGatewayMessage(ctx context.Context, ga h.lggr.Errorw("request rate-limited", "id", gatewayId, "address", fromAddr) return } - if balance, err := h.subscriptions.GetMaxUserBalance(fromAddr); err != nil || balance.Cmp(h.minimumBalance.ToInt()) < 0 { - h.lggr.Errorw("user subscription has insufficient balance", "id", gatewayId, "address", fromAddr, "balance", balance, "minBalance", h.minimumBalance) - response := functions.SecretsResponseBase{ - Success: false, - ErrorMessage: "user subscription has insufficient balance", - } - if err := h.sendResponse(ctx, gatewayId, body, response); err != nil { - h.lggr.Errorw("failed to send response to gateway", "id", gatewayId, "error", err) - } - return - } - h.lggr.Debugw("handling gateway request", "id", gatewayId, "method", body.Method) switch body.Method { case functions.MethodSecretsList: h.handleSecretsList(ctx, gatewayId, body, fromAddr) case functions.MethodSecretsSet: + if balance, err := h.subscriptions.GetMaxUserBalance(fromAddr); err != nil || balance.Cmp(h.minimumBalance.ToInt()) < 0 { + h.lggr.Errorw("user subscription has insufficient balance", "id", gatewayId, "address", fromAddr, "balance", balance, "minBalance", h.minimumBalance) + response := functions.SecretsResponseBase{ + Success: false, + ErrorMessage: "user subscription has insufficient balance", + } + h.sendResponseAndLog(ctx, gatewayId, body, response) + return + } h.handleSecretsSet(ctx, gatewayId, body, fromAddr) default: h.lggr.Errorw("unsupported method", "id", gatewayId, "method", body.Method) @@ -133,10 +130,7 @@ func (h *functionsConnectorHandler) handleSecretsList(ctx context.Context, gatew } else { response.ErrorMessage = fmt.Sprintf("Failed to list secrets: %v", err) } - - if err := h.sendResponse(ctx, gatewayId, body, response); err != nil { - h.lggr.Errorw("failed to send response to gateway", "id", gatewayId, "error", err) - } + h.sendResponseAndLog(ctx, gatewayId, body, response) } func (h *functionsConnectorHandler) handleSecretsSet(ctx context.Context, gatewayId string, body *api.MessageBody, fromAddr ethCommon.Address) { @@ -163,9 +157,15 @@ func (h *functionsConnectorHandler) handleSecretsSet(ctx context.Context, gatewa } else { response.ErrorMessage = fmt.Sprintf("Bad request to set secret: %v", err) } + h.sendResponseAndLog(ctx, gatewayId, body, response) +} - if err := h.sendResponse(ctx, gatewayId, body, response); err != nil { +func (h *functionsConnectorHandler) sendResponseAndLog(ctx context.Context, gatewayId string, requestBody *api.MessageBody, payload any) { + err := h.sendResponse(ctx, gatewayId, requestBody, payload) + if err != nil { h.lggr.Errorw("failed to send response to gateway", "id", gatewayId, "error", err) + } else { + h.lggr.Debugw("sent to gateway", "id", gatewayId, "messageId", requestBody.MessageId, "donId", requestBody.DonId, "method", requestBody.Method) } } @@ -187,10 +187,5 @@ func (h *functionsConnectorHandler) sendResponse(ctx context.Context, gatewayId if err = msg.Sign(h.signerKey); err != nil { return err } - - err = h.connector.SendToGateway(ctx, gatewayId, msg) - if err == nil { - h.lggr.Debugw("sent to gateway", "id", gatewayId, "messageId", requestBody.MessageId, "donId", requestBody.DonId, "method", requestBody.Method) - } - return err + return h.connector.SendToGateway(ctx, gatewayId, msg) } diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index 7bf98d7501d..fa9f74712be 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -78,7 +78,6 @@ func TestFunctionsConnectorHandler(t *testing.T) { } storage.On("List", ctx, addr).Return(snapshot, nil).Once() allowlist.On("Allow", addr).Return(true).Once() - subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -91,7 +90,6 @@ func TestFunctionsConnectorHandler(t *testing.T) { t.Run("orm error", func(t *testing.T) { storage.On("List", ctx, addr).Return(nil, errors.New("boom")).Once() allowlist.On("Allow", addr).Return(true).Once() - subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() connector.On("SendToGateway", ctx, "gw1", mock.Anything).Run(func(args mock.Arguments) { msg, ok := args[2].(*api.Message) require.True(t, ok) @@ -218,7 +216,6 @@ func TestFunctionsConnectorHandler(t *testing.T) { require.NoError(t, msg.Sign(privateKey)) allowlist.On("Allow", addr).Return(true).Once() - subscriptions.On("GetMaxUserBalance", mock.Anything).Return(big.NewInt(100), nil).Once() handler.HandleGatewayMessage(testutils.Context(t), "gw1", &msg) }) }) diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index d0011145d40..bb6812c1f9b 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -178,7 +178,7 @@ func (h *functionsHandler) HandleUserMessage(ctx context.Context, msg *api.Messa promHandlerError.WithLabelValues(h.donConfig.DonId, ErrRateLimited.Error()).Inc() return ErrRateLimited } - if h.subscriptions != nil && h.minimumBalance != nil { + if msg.Body.Method == MethodSecretsSet && h.subscriptions != nil && h.minimumBalance != nil { balance, err := h.subscriptions.GetMaxUserBalance(sender) if err != nil { h.lggr.Debugw("error getting max user balance", "sender", msg.Body.Sender, "err", err) diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 1446bc84571..49fdae2bb24 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -148,11 +148,10 @@ func TestFunctionsHandler_HandleUserMessage_InvalidMethod(t *testing.T) { t.Parallel() nodes, user := gc.NewTestNodes(t, 4), gc.NewTestNodes(t, 1)[0] - handler, _, allowlist, subscriptions := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24) + handler, _, allowlist, _ := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24) userRequestMsg := newSignedMessage(t, "1234", "secrets_reveal_all_please", "don_id", user.PrivateKey) allowlist.On("Allow", common.HexToAddress(user.Address)).Return(true, nil) - subscriptions.On("GetMaxUserBalance", common.HexToAddress(user.Address)).Return(big.NewInt(1000), nil) err := handler.HandleUserMessage(testutils.Context(t), &userRequestMsg, make(chan handlers.UserCallbackPayload)) require.Error(t, err) } From bd5a35b661caed4a4ce1afe8d275d41acc372c76 Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 16 Nov 2023 10:41:45 +0000 Subject: [PATCH 158/271] [BCF-2738] Modify build-publish-develop ECR (#11279) - push images to chainlink-develop in production rather than chainlink in the sdlc environment. --- .github/workflows/build-publish-develop.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-publish-develop.yml b/.github/workflows/build-publish-develop.yml index b8859722378..a7220deec4c 100644 --- a/.github/workflows/build-publish-develop.yml +++ b/.github/workflows/build-publish-develop.yml @@ -47,9 +47,9 @@ jobs: publish: true aws-role-to-assume: ${{ secrets.AWS_OIDC_IAM_ROLE_ARN }} aws-role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS }} - aws-region: ${{ secrets.AWS_REGION }} - ecr-hostname: ${{ secrets.AWS_DEVELOP_ECR_HOSTNAME }} - ecr-image-name: chainlink + aws-region: us-west-2 + ecr-hostname: 804282218731.dkr.ecr.us-west-2.com + ecr-image-name: chainlink-develop/chainlink ecr-tag-suffix: ${{ matrix.image.tag-suffix }} dockerfile: ${{ matrix.image.dockerfile }} dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} From af2f91996db2a67f69f7c2582e69e1b5e6493d4d Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 16 Nov 2023 11:05:47 +0000 Subject: [PATCH 159/271] Revert "[BCF-2738] Modify build-publish-develop ECR (#11279)" (#11310) This reverts commit bd5a35b661caed4a4ce1afe8d275d41acc372c76. --- .github/workflows/build-publish-develop.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-publish-develop.yml b/.github/workflows/build-publish-develop.yml index a7220deec4c..b8859722378 100644 --- a/.github/workflows/build-publish-develop.yml +++ b/.github/workflows/build-publish-develop.yml @@ -47,9 +47,9 @@ jobs: publish: true aws-role-to-assume: ${{ secrets.AWS_OIDC_IAM_ROLE_ARN }} aws-role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS }} - aws-region: us-west-2 - ecr-hostname: 804282218731.dkr.ecr.us-west-2.com - ecr-image-name: chainlink-develop/chainlink + aws-region: ${{ secrets.AWS_REGION }} + ecr-hostname: ${{ secrets.AWS_DEVELOP_ECR_HOSTNAME }} + ecr-image-name: chainlink ecr-tag-suffix: ${{ matrix.image.tag-suffix }} dockerfile: ${{ matrix.image.dockerfile }} dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} From 26e5e3740c8629c01c330e15c9fc35f55ca841ca Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Thu, 16 Nov 2023 17:15:56 +0200 Subject: [PATCH 160/271] Extract Link and move Eth to evm folder (#11287) * Extract Link and move Eth to evm folder * Move ChainType to common folder * Fix lint * Fix currency relay imports * Fix relay import dependency * Fix lint * Address comments --- common/client/mock_rpc_test.go | 2 +- common/client/multi_node.go | 4 +- common/client/multi_node_test.go | 2 +- common/client/types.go | 2 +- {core => common}/config/chaintype.go | 0 core/assets/currencies.go | 301 --------------- core/assets/currencies_test.go | 357 ------------------ core/bridges/bridge_type.go | 2 +- core/bridges/bridge_type_test.go | 2 +- core/chains/evm/assets/assets.go | 117 ++++++ core/chains/evm/assets/assets_test.go | 116 ++++++ core/{ => chains/evm}/assets/units.go | 0 core/{ => chains/evm}/assets/units_test.go | 2 +- core/{ => chains/evm}/assets/wei.go | 0 core/{ => chains/evm}/assets/wei_test.go | 0 core/chains/evm/chain.go | 3 +- core/chains/evm/client/chain_client.go | 7 +- core/chains/evm/client/client.go | 4 +- core/chains/evm/client/helpers_test.go | 2 +- core/chains/evm/client/mocks/client.go | 2 +- core/chains/evm/client/null_client.go | 2 +- core/chains/evm/client/pool.go | 2 +- core/chains/evm/client/rpc_client.go | 9 +- core/chains/evm/client/send_only_node_test.go | 2 +- .../evm/client/simulated_backend_client.go | 2 +- core/chains/evm/config/chain_scoped.go | 13 +- .../evm/config/chain_scoped_gas_estimator.go | 2 +- core/chains/evm/config/config.go | 8 +- core/chains/evm/config/config_test.go | 7 +- core/chains/evm/config/mocks/gas_estimator.go | 2 +- core/chains/evm/config/toml/config.go | 7 +- core/chains/evm/config/toml/defaults.go | 2 +- core/chains/evm/gas/arbitrum_estimator.go | 2 +- .../chains/evm/gas/arbitrum_estimator_test.go | 2 +- .../chains/evm/gas/block_history_estimator.go | 4 +- .../evm/gas/block_history_estimator_test.go | 4 +- core/chains/evm/gas/chain_specific.go | 4 +- core/chains/evm/gas/cmd/arbgas/main.go | 2 +- core/chains/evm/gas/fixed_price_estimator.go | 2 +- .../evm/gas/fixed_price_estimator_test.go | 2 +- core/chains/evm/gas/gas_test.go | 2 +- core/chains/evm/gas/helpers_test.go | 4 +- core/chains/evm/gas/mocks/config.go | 2 +- core/chains/evm/gas/mocks/evm_estimator.go | 2 +- .../chains/evm/gas/mocks/evm_fee_estimator.go | 2 +- core/chains/evm/gas/models.go | 4 +- core/chains/evm/gas/models_test.go | 2 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 4 +- .../gas/rollups/l1_gas_price_oracle_test.go | 4 +- .../chains/evm/gas/rollups/mocks/l1_oracle.go | 2 +- core/chains/evm/gas/rollups/models.go | 2 +- .../evm/gas/suggested_price_estimator.go | 2 +- .../evm/gas/suggested_price_estimator_test.go | 2 +- core/chains/evm/mocks/balance_monitor.go | 2 +- core/chains/evm/monitor/balance.go | 2 +- core/chains/evm/monitor/balance_test.go | 2 +- core/chains/evm/txmgr/attempts.go | 2 +- core/chains/evm/txmgr/attempts_test.go | 2 +- core/chains/evm/txmgr/broadcaster_test.go | 2 +- core/chains/evm/txmgr/config.go | 6 +- core/chains/evm/txmgr/confirmer_test.go | 2 +- core/chains/evm/txmgr/evm_tx_store.go | 2 +- core/chains/evm/txmgr/evm_tx_store_test.go | 2 +- core/chains/evm/txmgr/mocks/config.go | 2 +- core/chains/evm/txmgr/test_helpers.go | 15 +- core/chains/evm/txmgr/transmitchecker_test.go | 2 +- core/chains/evm/txmgr/txmgr_test.go | 2 +- .../evm/types/block_json_benchmark_test.go | 2 +- core/chains/evm/types/models.go | 2 +- core/chains/evm/types/models_test.go | 2 +- core/cmd/cosmos_node_commands_test.go | 4 +- core/cmd/eth_keys_commands_test.go | 13 +- core/cmd/evm_transaction_commands.go | 2 +- core/cmd/evm_transaction_commands_test.go | 2 +- core/cmd/shell_local.go | 2 +- core/cmd/solana_node_commands_test.go | 6 +- core/cmd/starknet_node_commands_test.go | 6 +- core/config/docs/docs_test.go | 2 +- core/config/parse/parsers.go | 9 +- core/internal/cltest/cltest.go | 2 +- core/internal/cltest/factories.go | 2 +- core/internal/features/features_test.go | 2 +- .../features/ocr2/features_ocr2_test.go | 2 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/scripts/ocr2vrf/main.go | 2 +- core/scripts/vrfv2/testnet/main.go | 2 +- core/scripts/vrfv2plus/testnet/main.go | 2 +- core/services/chainlink/config_test.go | 71 ++-- .../relayer_chain_interoperators_test.go | 18 +- core/services/directrequest/delegate.go | 2 +- core/services/directrequest/delegate_test.go | 2 +- core/services/directrequest/validate.go | 2 +- core/services/fluxmonitorv2/config.go | 2 +- .../fluxmonitorv2/flux_monitor_test.go | 2 +- .../fluxmonitorv2/integrations_test.go | 2 +- .../services/fluxmonitorv2/payment_checker.go | 2 +- .../fluxmonitorv2/payment_checker_test.go | 2 +- core/services/fluxmonitorv2/validate_test.go | 2 +- core/services/functions/connector_handler.go | 2 +- .../functions/connector_handler_test.go | 2 +- .../handlers/functions/handler.functions.go | 2 +- .../functions/handler.functions_test.go | 2 +- core/services/job/job_orm_test.go | 2 +- core/services/job/models.go | 7 +- core/services/keeper/integration_test.go | 2 +- core/services/keeper/upkeep_executer.go | 2 +- core/services/keeper/upkeep_executer_test.go | 2 +- .../keeper/upkeep_executer_unit_test.go | 2 +- core/services/keystore/keys/ethkey/address.go | 4 +- core/services/ocr/contract_tracker.go | 2 +- core/services/ocr/validate.go | 2 +- .../ocr2/plugins/functions/config/config.go | 2 +- .../v1/internal/testutils.go | 2 +- .../services/ocr2/plugins/functions/plugin.go | 2 +- .../ocr2/plugins/functions/plugin_test.go | 2 +- .../ocr2/plugins/mercury/integration_test.go | 2 +- .../evm21/logprovider/integration_test.go | 2 +- .../plugins/ocr2keeper/integration_21_test.go | 2 +- .../plugins/ocr2keeper/integration_test.go | 2 +- .../internal/ocr2vrf_integration_test.go | 2 +- .../reasonable_gas_price_provider.go | 2 +- .../reasonable_gas_price_test.go | 2 +- core/services/ocrcommon/block_translator.go | 2 +- core/services/ocrcommon/config.go | 2 +- .../relay/evm/mercury/v1/data_source_test.go | 2 +- .../services/transmission/integration_test.go | 2 +- core/services/vrf/delegate.go | 2 +- core/services/vrf/delegate_test.go | 2 +- core/services/vrf/mocks/fee_config.go | 2 +- .../services/vrf/proof/proof_response_test.go | 2 +- .../vrf_coordinator_interface.go | 2 +- .../vrf_coordinator_interface_test.go | 7 +- .../vrf_hash_to_curve_cost_test.go | 2 +- .../vrf_solidity_crosscheck_test.go | 2 +- .../vrf_v08_solidity_crosscheck_test.go | 2 +- core/services/vrf/v2/bhs_feeder_test.go | 2 +- .../vrf/v2/integration_helpers_test.go | 2 +- .../vrf/v2/integration_v2_plus_test.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 5 +- core/services/vrf/v2/listener_v2.go | 2 +- .../vrf/v2/listener_v2_helpers_test.go | 2 +- core/services/vrf/vrfcommon/types.go | 2 +- core/services/vrf/vrfcommon/validate.go | 2 +- core/services/vrf/vrfcommon/validate_test.go | 2 +- core/services/vrf/vrftesthelpers/helpers.go | 2 +- core/store/models/common.go | 2 +- core/testdata/testspecs/v2_specs.go | 2 +- core/utils/big.go | 3 +- core/utils/utils.go | 16 - core/web/bridge_types_controller.go | 2 +- core/web/bridge_types_controller_test.go | 2 +- core/web/eth_keys_controller.go | 9 +- core/web/eth_keys_controller_test.go | 2 +- core/web/evm_transactions_controller_test.go | 2 +- core/web/evm_transfer_controller.go | 2 +- core/web/evm_transfer_controller_test.go | 2 +- core/web/presenters/bridges.go | 2 +- core/web/presenters/bridges_test.go | 2 +- core/web/presenters/eth_key.go | 21 +- core/web/presenters/eth_key_test.go | 7 +- core/web/presenters/eth_tx.go | 2 +- core/web/presenters/eth_tx_test.go | 2 +- core/web/presenters/job.go | 7 +- core/web/presenters/job_test.go | 2 +- core/web/resolver/bridge_test.go | 2 +- core/web/resolver/eth_key_test.go | 9 +- core/web/resolver/eth_transaction.go | 2 +- core/web/resolver/eth_transaction_test.go | 2 +- core/web/resolver/helpers.go | 2 +- core/web/resolver/mutation.go | 2 +- core/web/resolver/spec_test.go | 9 +- core/web/solana_chains_controller_test.go | 6 +- go.mod | 2 +- go.sum | 4 +- .../actions/vrfv2plus/vrfv2plus_steps.go | 13 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- integration-tests/types/config/node/core.go | 7 +- tools/flakeytests/runner_test.go | 88 ++--- 180 files changed, 598 insertions(+), 1017 deletions(-) rename {core => common}/config/chaintype.go (100%) delete mode 100644 core/assets/currencies.go delete mode 100644 core/assets/currencies_test.go create mode 100644 core/chains/evm/assets/assets.go create mode 100644 core/chains/evm/assets/assets_test.go rename core/{ => chains/evm}/assets/units.go (100%) rename core/{ => chains/evm}/assets/units_test.go (93%) rename core/{ => chains/evm}/assets/wei.go (100%) rename core/{ => chains/evm}/assets/wei_test.go (100%) diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index c378b9384e4..8f171302a2a 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -5,7 +5,7 @@ package client import ( big "math/big" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink-relay/pkg/assets" context "context" diff --git a/common/client/multi_node.go b/common/client/multi_node.go index c268cfb23cd..df91c61a9d8 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -11,12 +11,12 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 1fddbc3be3c..3621d6d862d 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -14,8 +14,8 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/common/client/types.go b/common/client/types.go index 0e52f1db72c..ef9f94dafea 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -4,9 +4,9 @@ import ( "context" "math/big" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/config/chaintype.go b/common/config/chaintype.go similarity index 100% rename from core/config/chaintype.go rename to common/config/chaintype.go diff --git a/core/assets/currencies.go b/core/assets/currencies.go deleted file mode 100644 index 1201f0be35c..00000000000 --- a/core/assets/currencies.go +++ /dev/null @@ -1,301 +0,0 @@ -package assets - -import ( - "database/sql/driver" - "fmt" - "math/big" - "strings" - - "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - "github.com/shopspring/decimal" -) - -var ErrNoQuotesForCurrency = errors.New("cannot unmarshal json.Number into currency") - -// getDenominator returns 10**precision. -func getDenominator(precision int) *big.Int { - x := big.NewInt(10) - return new(big.Int).Exp(x, big.NewInt(int64(precision)), nil) -} - -func format(i *big.Int, precision int) string { - r := big.NewRat(1, 1).SetFrac(i, getDenominator(precision)) - return fmt.Sprintf("%v", r.FloatString(precision)) -} - -// Link contains a field to represent the smallest units of LINK -type Link big.Int - -// NewLinkFromJuels returns a new struct to represent LINK from it's smallest unit -func NewLinkFromJuels(w int64) *Link { - return (*Link)(big.NewInt(w)) -} - -// String returns Link formatted as a string. -func (l *Link) String() string { - if l == nil { - return "0" - } - return fmt.Sprintf("%v", (*big.Int)(l)) -} - -// Link returns Link formatted as a string, in LINK units -func (l *Link) Link() string { - if l == nil { - return "0" - } - return format((*big.Int)(l), 18) -} - -// SetInt64 delegates to *big.Int.SetInt64 -func (l *Link) SetInt64(w int64) *Link { - return (*Link)((*big.Int)(l).SetInt64(w)) -} - -// ToInt returns the Link value as a *big.Int. -func (l *Link) ToInt() *big.Int { - return (*big.Int)(l) -} - -// ToHash returns a 32 byte representation of this value -func (l *Link) ToHash() common.Hash { - return common.BigToHash((*big.Int)(l)) -} - -// Set delegates to *big.Int.Set -func (l *Link) Set(x *Link) *Link { - il := (*big.Int)(l) - ix := (*big.Int)(x) - - w := il.Set(ix) - return (*Link)(w) -} - -// SetString delegates to *big.Int.SetString -func (l *Link) SetString(s string, base int) (*Link, bool) { - w, ok := (*big.Int)(l).SetString(s, base) - return (*Link)(w), ok -} - -// Cmp defers to big.Int Cmp -func (l *Link) Cmp(y *Link) int { - return (*big.Int)(l).Cmp((*big.Int)(y)) -} - -// Add defers to big.Int Add -func (l *Link) Add(x, y *Link) *Link { - il := (*big.Int)(l) - ix := (*big.Int)(x) - iy := (*big.Int)(y) - - return (*Link)(il.Add(ix, iy)) -} - -// Text defers to big.Int Text -func (l *Link) Text(base int) string { - return (*big.Int)(l).Text(base) -} - -var linkFmtThreshold = (*Link)(new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil)) - -// MarshalText implements the encoding.TextMarshaler interface. -func (l *Link) MarshalText() ([]byte, error) { - if l.Cmp(linkFmtThreshold) >= 0 { - return []byte(fmt.Sprintf("%s link", decimal.NewFromBigInt(l.ToInt(), -18))), nil - } - return (*big.Int)(l).MarshalText() -} - -// MarshalJSON implements the json.Marshaler interface. -func (l Link) MarshalJSON() ([]byte, error) { - value, err := l.MarshalText() - if err != nil { - return nil, err - } - return []byte(fmt.Sprintf(`"%s"`, value)), nil -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (l *Link) UnmarshalJSON(data []byte) error { - if utils.IsQuoted(data) { - return l.UnmarshalText(utils.RemoveQuotes(data)) - } - return ErrNoQuotesForCurrency -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (l *Link) UnmarshalText(text []byte) error { - s := string(text) - if strings.HasSuffix(s, "link") { - s = strings.TrimSuffix(s, "link") - s = strings.TrimSuffix(s, " ") - d, err := decimal.NewFromString(s) - if err != nil { - return errors.Wrapf(err, "assets: cannot unmarshal %q into a *assets.Link", text) - } - d = d.Mul(decimal.New(1, 18)) - if !d.IsInteger() { - err := errors.New("maximum precision is juels") - return errors.Wrapf(err, "assets: cannot unmarshal %q into a *assets.Link", text) - } - l.Set((*Link)(d.Rat().Num())) - return nil - } - if strings.HasSuffix(s, "juels") { - s = strings.TrimSuffix(s, "juels") - s = strings.TrimSuffix(s, " ") - } - if _, ok := l.SetString(s, 10); !ok { - return errors.Errorf("assets: cannot unmarshal %q into a *assets.Link", text) - } - return nil -} - -// IsZero returns true when the value is 0 and false otherwise -func (l *Link) IsZero() bool { - zero := big.NewInt(0) - return (*big.Int)(l).Cmp(zero) == 0 -} - -// Symbol returns LINK -func (*Link) Symbol() string { - return "LINK" -} - -// Value returns the Link value for serialization to database. -func (l Link) Value() (driver.Value, error) { - b := (big.Int)(l) - return b.String(), nil -} - -// Scan reads the database value and returns an instance. -func (l *Link) Scan(value interface{}) error { - switch v := value.(type) { - case string: - decoded, ok := l.SetString(v, 10) - if !ok { - return fmt.Errorf("unable to set string %v of %T to base 10 big.Int for Link", value, value) - } - *l = *decoded - case []uint8: - // The SQL library returns numeric() types as []uint8 of the string representation - decoded, ok := l.SetString(string(v), 10) - if !ok { - return fmt.Errorf("unable to set string %v of %T to base 10 big.Int for Link", value, value) - } - *l = *decoded - case int64: - return fmt.Errorf("unable to convert %v of %T to Link, is the sql type set to varchar?", value, value) - default: - return fmt.Errorf("unable to convert %v of %T to Link", value, value) - } - - return nil -} - -// Eth contains a field to represent the smallest units of ETH -type Eth big.Int - -// NewEth returns a new struct to represent ETH from it's smallest unit (wei) -func NewEth(w int64) *Eth { - return (*Eth)(big.NewInt(w)) -} - -// NewEthValue returns a new struct to represent ETH from it's smallest unit (wei) -func NewEthValue(w int64) Eth { - eth := NewEth(w) - return *eth -} - -// NewEthValueS returns a new struct to represent ETH from a string value of Eth (not wei) -// the underlying value is still wei -func NewEthValueS(s string) (Eth, error) { - e, err := decimal.NewFromString(s) - if err != nil { - return Eth{}, err - } - w := e.Mul(decimal.RequireFromString("10").Pow(decimal.RequireFromString("18"))) - return *(*Eth)(w.BigInt()), nil -} - -// Cmp delegates to *big.Int.Cmp -func (e *Eth) Cmp(y *Eth) int { - return e.ToInt().Cmp(y.ToInt()) -} - -func (e *Eth) String() string { - if e == nil { - return "" - } - return format(e.ToInt(), 18) -} - -// SetInt64 delegates to *big.Int.SetInt64 -func (e *Eth) SetInt64(w int64) *Eth { - return (*Eth)(e.ToInt().SetInt64(w)) -} - -// SetString delegates to *big.Int.SetString -func (e *Eth) SetString(s string, base int) (*Eth, bool) { - w, ok := e.ToInt().SetString(s, base) - return (*Eth)(w), ok -} - -// MarshalJSON implements the json.Marshaler interface. -func (e Eth) MarshalJSON() ([]byte, error) { - value, err := e.MarshalText() - if err != nil { - return nil, err - } - return []byte(fmt.Sprintf(`"%s"`, value)), nil -} - -// MarshalText implements the encoding.TextMarshaler interface. -func (e *Eth) MarshalText() ([]byte, error) { - return e.ToInt().MarshalText() -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (e *Eth) UnmarshalJSON(data []byte) error { - if utils.IsQuoted(data) { - return e.UnmarshalText(utils.RemoveQuotes(data)) - } - return ErrNoQuotesForCurrency -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (e *Eth) UnmarshalText(text []byte) error { - if _, ok := e.SetString(string(text), 10); !ok { - return fmt.Errorf("assets: cannot unmarshal %q into a *assets.Eth", text) - } - return nil -} - -// IsZero returns true when the value is 0 and false otherwise -func (e *Eth) IsZero() bool { - zero := big.NewInt(0) - return e.ToInt().Cmp(zero) == 0 -} - -// Symbol returns ETH -func (*Eth) Symbol() string { - return "ETH" -} - -// ToInt returns the Eth value as a *big.Int. -func (e *Eth) ToInt() *big.Int { - return (*big.Int)(e) -} - -// Scan reads the database value and returns an instance. -func (e *Eth) Scan(value interface{}) error { - return (*utils.Big)(e).Scan(value) -} - -// Value returns the Eth value for serialization to database. -func (e Eth) Value() (driver.Value, error) { - return (utils.Big)(e).Value() -} diff --git a/core/assets/currencies_test.go b/core/assets/currencies_test.go deleted file mode 100644 index 5f2a00223f5..00000000000 --- a/core/assets/currencies_test.go +++ /dev/null @@ -1,357 +0,0 @@ -package assets_test - -import ( - "encoding/json" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/ethereum/go-ethereum/common" - - "github.com/smartcontractkit/chainlink/v2/core/assets" -) - -func TestAssets_NewLinkAndString(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(0) - - assert.Equal(t, "0", link.String()) - - link.SetInt64(1) - assert.Equal(t, "1", link.String()) - - link.SetString("900000000000000000", 10) - assert.Equal(t, "900000000000000000", link.String()) - - link.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10) - assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457584007913129639935", link.String()) - - var nilLink *assets.Link - assert.Equal(t, "0", nilLink.String()) -} - -func TestAssets_NewLinkAndLink(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(0) - - assert.Equal(t, "0.000000000000000000", link.Link()) - - link.SetInt64(1) - assert.Equal(t, "0.000000000000000001", link.Link()) - - link.SetString("900000000000000000", 10) - assert.Equal(t, "0.900000000000000000", link.Link()) - - link.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10) - assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457.584007913129639935", link.Link()) - - link.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639936", 10) - assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457.584007913129639936", link.Link()) - - var nilLink *assets.Link - assert.Equal(t, "0", nilLink.Link()) -} - -func TestAssets_Link_MarshalJson(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(1) - - b, err := json.Marshal(link) - assert.NoError(t, err) - assert.Equal(t, []byte(`"1"`), b) -} - -func TestAssets_Link_UnmarshalJsonOk(t *testing.T) { - t.Parallel() - - link := assets.Link{} - - err := json.Unmarshal([]byte(`"1"`), &link) - assert.NoError(t, err) - assert.Equal(t, "0.000000000000000001", link.Link()) -} - -func TestAssets_Link_UnmarshalJsonError(t *testing.T) { - t.Parallel() - - link := assets.Link{} - - err := json.Unmarshal([]byte(`"x"`), &link) - assert.EqualError(t, err, "assets: cannot unmarshal \"x\" into a *assets.Link") - - err = json.Unmarshal([]byte(`1`), &link) - assert.Equal(t, assets.ErrNoQuotesForCurrency, err) -} - -func TestAssets_NewEthAndString(t *testing.T) { - t.Parallel() - - eth := assets.NewEth(0) - - assert.Equal(t, "0.000000000000000000", eth.String()) - - eth.SetInt64(1) - assert.Equal(t, "0.000000000000000001", eth.String()) - - eth.SetString("900000000000000000", 10) - assert.Equal(t, "0.900000000000000000", eth.String()) - - eth.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10) - assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457.584007913129639935", eth.String()) - - eth.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639936", 10) - assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457.584007913129639936", eth.String()) -} - -func TestAssets_Eth_IsZero(t *testing.T) { - t.Parallel() - - zeroEth := assets.NewEth(0) - assert.True(t, zeroEth.IsZero()) - - oneLink := assets.NewEth(1) - assert.False(t, oneLink.IsZero()) -} - -func TestAssets_Eth_MarshalJson(t *testing.T) { - t.Parallel() - - eth := assets.NewEth(1) - - b, err := json.Marshal(eth) - assert.NoError(t, err) - assert.Equal(t, []byte(`"1"`), b) -} - -func TestAssets_Eth_UnmarshalJsonOk(t *testing.T) { - t.Parallel() - - eth := assets.Eth{} - - err := json.Unmarshal([]byte(`"1"`), ð) - assert.NoError(t, err) - assert.Equal(t, "0.000000000000000001", eth.String()) -} - -func TestAssets_Eth_UnmarshalJsonError(t *testing.T) { - t.Parallel() - - eth := assets.Eth{} - - err := json.Unmarshal([]byte(`"x"`), ð) - assert.EqualError(t, err, "assets: cannot unmarshal \"x\" into a *assets.Eth") - - err = json.Unmarshal([]byte(`1`), ð) - assert.Equal(t, assets.ErrNoQuotesForCurrency, err) -} - -func TestAssets_LinkToInt(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(0) - assert.Equal(t, big.NewInt(0), link.ToInt()) - - link = assets.NewLinkFromJuels(123) - assert.Equal(t, big.NewInt(123), link.ToInt()) -} - -func TestAssets_LinkToHash(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(123) - expected := common.BigToHash((*big.Int)(link)) - assert.Equal(t, expected, link.ToHash()) -} - -func TestAssets_LinkSetLink(t *testing.T) { - t.Parallel() - - link1 := assets.NewLinkFromJuels(123) - link2 := assets.NewLinkFromJuels(321) - link3 := link1.Set(link2) - assert.Equal(t, link3, link2) -} - -func TestAssets_LinkCmpLink(t *testing.T) { - t.Parallel() - - link1 := assets.NewLinkFromJuels(123) - link2 := assets.NewLinkFromJuels(321) - assert.NotZero(t, link1.Cmp(link2)) - - link3 := assets.NewLinkFromJuels(321) - assert.Zero(t, link3.Cmp(link2)) -} - -func TestAssets_LinkAddLink(t *testing.T) { - t.Parallel() - - link1 := assets.NewLinkFromJuels(123) - link2 := assets.NewLinkFromJuels(321) - sum := assets.NewLinkFromJuels(123 + 321) - assert.Equal(t, sum, link1.Add(link1, link2)) -} - -func TestAssets_LinkText(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(123) - assert.Equal(t, "123", link.Text(10)) - assert.Equal(t, "7b", link.Text(16)) -} - -func TestAssets_LinkIsZero(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(123) - assert.False(t, link.IsZero()) - - link = assets.NewLinkFromJuels(0) - assert.True(t, link.IsZero()) -} - -func TestAssets_LinkSymbol(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(123) - assert.Equal(t, "LINK", link.Symbol()) -} - -func TestAssets_LinkScanValue(t *testing.T) { - t.Parallel() - - link := assets.NewLinkFromJuels(123) - v, err := link.Value() - assert.NoError(t, err) - - link2 := assets.NewLinkFromJuels(0) - err = link2.Scan(v) - assert.NoError(t, err) - assert.Equal(t, link2, link) - - err = link2.Scan("123") - assert.NoError(t, err) - assert.Equal(t, link2, link) - - err = link2.Scan([]uint8{'1', '2', '3'}) - assert.NoError(t, err) - assert.Equal(t, link2, link) - - assert.ErrorContains(t, link2.Scan([]uint8{'x'}), "unable to set string") - assert.ErrorContains(t, link2.Scan("123.56"), "unable to set string") - assert.ErrorContains(t, link2.Scan(1.5), "unable to convert") - assert.ErrorContains(t, link2.Scan(int64(123)), "unable to convert") -} - -func TestAssets_NewEth(t *testing.T) { - t.Parallel() - - ethRef := assets.NewEth(123) - ethVal := assets.NewEthValue(123) - ethStr, err := assets.NewEthValueS(ethRef.String()) - assert.NoError(t, err) - assert.Equal(t, *ethRef, ethVal) - assert.Equal(t, *ethRef, ethStr) -} - -func TestAssets_EthSymbol(t *testing.T) { - t.Parallel() - - eth := assets.NewEth(123) - assert.Equal(t, "ETH", eth.Symbol()) -} - -func TestAssets_EthScanValue(t *testing.T) { - t.Parallel() - - eth := assets.NewEth(123) - v, err := eth.Value() - assert.NoError(t, err) - - eth2 := assets.NewEth(0) - err = eth2.Scan(v) - assert.NoError(t, err) - - assert.Equal(t, eth, eth2) -} - -func TestAssets_EthCmpEth(t *testing.T) { - t.Parallel() - - eth1 := assets.NewEth(123) - eth2 := assets.NewEth(321) - assert.NotZero(t, eth1.Cmp(eth2)) - - eth3 := assets.NewEth(321) - assert.Zero(t, eth3.Cmp(eth2)) -} - -func TestLink(t *testing.T) { - for _, tt := range []struct { - input string - exp string - }{ - {"0", "0"}, - {"1", "1"}, - {"1 juels", "1"}, - {"100000000000", "100000000000"}, - {"0.0000001 link", "100000000000"}, - {"1000000000000", "0.000001 link"}, - {"100000000000000", "0.0001 link"}, - {"0.0001 link", "0.0001 link"}, - {"10000000000000000", "0.01 link"}, - {"0.01 link", "0.01 link"}, - {"100000000000000000", "0.1 link"}, - {"0.1 link", "0.1 link"}, - {"1.0 link", "1 link"}, - {"1000000000000000000", "1 link"}, - {"1000000000000000000 juels", "1 link"}, - {"1100000000000000000", "1.1 link"}, - {"1.1link", "1.1 link"}, - {"1.1 link", "1.1 link"}, - } { - t.Run(tt.input, func(t *testing.T) { - var l assets.Link - err := l.UnmarshalText([]byte(tt.input)) - require.NoError(t, err) - b, err := l.MarshalText() - require.NoError(t, err) - assert.Equal(t, tt.exp, string(b)) - }) - } -} - -func FuzzLink(f *testing.F) { - f.Add("1") - f.Add("1 link") - f.Add("1.1link") - f.Add("2.3") - f.Add("2.3 link") - f.Add("00005 link") - f.Add("0.0005link") - f.Add("1100000000000000000000000000000") - f.Add("1100000000000000000000000000000 juels") - f.Fuzz(func(t *testing.T, v string) { - if len(v) > 1_000 { - t.Skip() - } - var l assets.Link - err := l.UnmarshalText([]byte(v)) - if err != nil { - t.Skip() - } - - b, err := l.MarshalText() - require.NoErrorf(t, err, "failed to marshal %v after unmarshaling from %q", l, v) - - var l2 assets.Link - err = l2.UnmarshalText(b) - require.NoErrorf(t, err, "failed to unmarshal %s after marshaling from %v", string(b), l) - require.Equal(t, l, l2, "unequal values after marshal/unmarshal") - }) -} diff --git a/core/bridges/bridge_type.go b/core/bridges/bridge_type.go index 9b3a8570ca2..9031541f22b 100644 --- a/core/bridges/bridge_type.go +++ b/core/bridges/bridge_type.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/bridges/bridge_type_test.go b/core/bridges/bridge_type_test.go index 8c4a7cbace0..c04aba5c2c7 100644 --- a/core/bridges/bridge_type_test.go +++ b/core/bridges/bridge_type_test.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/store/models" diff --git a/core/chains/evm/assets/assets.go b/core/chains/evm/assets/assets.go new file mode 100644 index 00000000000..c6c81b5ab48 --- /dev/null +++ b/core/chains/evm/assets/assets.go @@ -0,0 +1,117 @@ +package assets + +import ( + "database/sql/driver" + "fmt" + "math/big" + + "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/utils/bytes" + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/shopspring/decimal" +) + +// Eth contains a field to represent the smallest units of ETH +type Eth big.Int + +// NewEth returns a new struct to represent ETH from it's smallest unit (wei) +func NewEth(w int64) *Eth { + return (*Eth)(big.NewInt(w)) +} + +// NewEthValue returns a new struct to represent ETH from it's smallest unit (wei) +func NewEthValue(w int64) Eth { + eth := NewEth(w) + return *eth +} + +// NewEthValueS returns a new struct to represent ETH from a string value of Eth (not wei) +// the underlying value is still wei +func NewEthValueS(s string) (Eth, error) { + e, err := decimal.NewFromString(s) + if err != nil { + return Eth{}, err + } + w := e.Mul(decimal.RequireFromString("10").Pow(decimal.RequireFromString("18"))) + return *(*Eth)(w.BigInt()), nil +} + +// Cmp delegates to *big.Int.Cmp +func (e *Eth) Cmp(y *Eth) int { + return e.ToInt().Cmp(y.ToInt()) +} + +func (e *Eth) String() string { + if e == nil { + return "" + } + return assets.Format(e.ToInt(), 18) +} + +// SetInt64 delegates to *big.Int.SetInt64 +func (e *Eth) SetInt64(w int64) *Eth { + return (*Eth)(e.ToInt().SetInt64(w)) +} + +// SetString delegates to *big.Int.SetString +func (e *Eth) SetString(s string, base int) (*Eth, bool) { + w, ok := e.ToInt().SetString(s, base) + return (*Eth)(w), ok +} + +// MarshalJSON implements the json.Marshaler interface. +func (e Eth) MarshalJSON() ([]byte, error) { + value, err := e.MarshalText() + if err != nil { + return nil, err + } + return []byte(fmt.Sprintf(`"%s"`, value)), nil +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (e *Eth) MarshalText() ([]byte, error) { + return e.ToInt().MarshalText() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (e *Eth) UnmarshalJSON(data []byte) error { + if bytes.HasQuotes(data) { + return e.UnmarshalText(bytes.TrimQuotes(data)) + } + return assets.ErrNoQuotesForCurrency +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (e *Eth) UnmarshalText(text []byte) error { + if _, ok := e.SetString(string(text), 10); !ok { + return fmt.Errorf("assets: cannot unmarshal %q into a *assets.Eth", text) + } + return nil +} + +// IsZero returns true when the value is 0 and false otherwise +func (e *Eth) IsZero() bool { + zero := big.NewInt(0) + return e.ToInt().Cmp(zero) == 0 +} + +// Symbol returns ETH +func (*Eth) Symbol() string { + return "ETH" +} + +// ToInt returns the Eth value as a *big.Int. +func (e *Eth) ToInt() *big.Int { + return (*big.Int)(e) +} + +// Scan reads the database value and returns an instance. +func (e *Eth) Scan(value interface{}) error { + return (*utils.Big)(e).Scan(value) +} + +// Value returns the Eth value for serialization to database. +func (e Eth) Value() (driver.Value, error) { + return (utils.Big)(e).Value() +} diff --git a/core/chains/evm/assets/assets_test.go b/core/chains/evm/assets/assets_test.go new file mode 100644 index 00000000000..9496554f116 --- /dev/null +++ b/core/chains/evm/assets/assets_test.go @@ -0,0 +1,116 @@ +package assets_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" +) + +func TestAssets_NewEthAndString(t *testing.T) { + t.Parallel() + + eth := assets.NewEth(0) + + assert.Equal(t, "0.000000000000000000", eth.String()) + + eth.SetInt64(1) + assert.Equal(t, "0.000000000000000001", eth.String()) + + eth.SetString("900000000000000000", 10) + assert.Equal(t, "0.900000000000000000", eth.String()) + + eth.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639935", 10) + assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457.584007913129639935", eth.String()) + + eth.SetString("115792089237316195423570985008687907853269984665640564039457584007913129639936", 10) + assert.Equal(t, "115792089237316195423570985008687907853269984665640564039457.584007913129639936", eth.String()) +} + +func TestAssets_Eth_IsZero(t *testing.T) { + t.Parallel() + + zeroEth := assets.NewEth(0) + assert.True(t, zeroEth.IsZero()) + + oneLink := assets.NewEth(1) + assert.False(t, oneLink.IsZero()) +} + +func TestAssets_Eth_MarshalJson(t *testing.T) { + t.Parallel() + + eth := assets.NewEth(1) + + b, err := json.Marshal(eth) + assert.NoError(t, err) + assert.Equal(t, []byte(`"1"`), b) +} + +func TestAssets_Eth_UnmarshalJsonOk(t *testing.T) { + t.Parallel() + + eth := assets.Eth{} + + err := json.Unmarshal([]byte(`"1"`), ð) + assert.NoError(t, err) + assert.Equal(t, "0.000000000000000001", eth.String()) +} + +func TestAssets_Eth_UnmarshalJsonError(t *testing.T) { + t.Parallel() + + eth := assets.Eth{} + + err := json.Unmarshal([]byte(`"x"`), ð) + assert.EqualError(t, err, "assets: cannot unmarshal \"x\" into a *assets.Eth") + + err = json.Unmarshal([]byte(`1`), ð) + assert.Equal(t, relayassets.ErrNoQuotesForCurrency, err) +} + +func TestAssets_NewEth(t *testing.T) { + t.Parallel() + + ethRef := assets.NewEth(123) + ethVal := assets.NewEthValue(123) + ethStr, err := assets.NewEthValueS(ethRef.String()) + assert.NoError(t, err) + assert.Equal(t, *ethRef, ethVal) + assert.Equal(t, *ethRef, ethStr) +} + +func TestAssets_EthSymbol(t *testing.T) { + t.Parallel() + + eth := assets.NewEth(123) + assert.Equal(t, "ETH", eth.Symbol()) +} + +func TestAssets_EthScanValue(t *testing.T) { + t.Parallel() + + eth := assets.NewEth(123) + v, err := eth.Value() + assert.NoError(t, err) + + eth2 := assets.NewEth(0) + err = eth2.Scan(v) + assert.NoError(t, err) + + assert.Equal(t, eth, eth2) +} + +func TestAssets_EthCmpEth(t *testing.T) { + t.Parallel() + + eth1 := assets.NewEth(123) + eth2 := assets.NewEth(321) + assert.NotZero(t, eth1.Cmp(eth2)) + + eth3 := assets.NewEth(321) + assert.Zero(t, eth3.Cmp(eth2)) +} diff --git a/core/assets/units.go b/core/chains/evm/assets/units.go similarity index 100% rename from core/assets/units.go rename to core/chains/evm/assets/units.go diff --git a/core/assets/units_test.go b/core/chains/evm/assets/units_test.go similarity index 93% rename from core/assets/units_test.go rename to core/chains/evm/assets/units_test.go index 7b23072033d..37eb1393257 100644 --- a/core/assets/units_test.go +++ b/core/chains/evm/assets/units_test.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) func TestAssets_Units(t *testing.T) { diff --git a/core/assets/wei.go b/core/chains/evm/assets/wei.go similarity index 100% rename from core/assets/wei.go rename to core/chains/evm/assets/wei.go diff --git a/core/assets/wei_test.go b/core/chains/evm/assets/wei_test.go similarity index 100% rename from core/assets/wei_test.go rename to core/chains/evm/assets/wei_test.go diff --git a/core/chains/evm/chain.go b/core/chains/evm/chain.go index b5896393d3c..f21bf2525a0 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/evm/chain.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" "github.com/smartcontractkit/chainlink-relay/pkg/types" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -473,7 +474,7 @@ func (c *chain) Logger() logger.Logger { return c.logger } func (c *chain) BalanceMonitor() monitor.BalanceMonitor { return c.balanceMonitor } func (c *chain) GasEstimator() gas.EvmFeeEstimator { return c.gasEstimator } -func newEthClientFromChain(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType config.ChainType, nodes []*toml.Node) (evmclient.Client, error) { +func newEthClientFromChain(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType commonconfig.ChainType, nodes []*toml.Node) (evmclient.Client, error) { var primaries []evmclient.Node var sendonlys []evmclient.SendOnlyNode for i, node := range nodes { diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 4c5108745c5..79a91dfc057 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -10,10 +10,11 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -182,7 +183,7 @@ func (c *chainClient) IsL2() bool { return c.multiNode.IsL2() } -func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*assets.Link, error) { +func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*relayassets.Link, error) { return c.multiNode.LINKBalance(ctx, address, linkAddress) } diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index fb8a39f3798..988a7404c9b 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -6,11 +6,11 @@ import ( "strings" "time" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + "github.com/smartcontractkit/chainlink/v2/common/config" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index 2820ba992c3..b1d477b1a29 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -10,9 +10,9 @@ import ( "github.com/pkg/errors" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - commonconfig "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index 7617a7c05f9..f1ff5fab456 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -5,7 +5,7 @@ package mocks import ( big "math/big" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink-relay/pkg/assets" common "github.com/ethereum/go-ethereum/common" diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index 286f62b3b8b..45876d9259c 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -9,8 +9,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 2d679ab3d73..473ab6d1eb0 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -17,8 +17,8 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 04b9fad1fcd..f952c04d5bb 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -17,9 +17,10 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -868,12 +869,12 @@ func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, co } // LINKBalance returns the balance of LINK at the given address -func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*assets.Link, error) { +func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*relayassets.Link, error) { balance, err := r.TokenBalance(ctx, address, linkAddress) if err != nil { - return assets.NewLinkFromJuels(0), err + return relayassets.NewLinkFromJuels(0), err } - return (*assets.Link)(balance), nil + return (*relayassets.Link)(balance), nil } func (r *rpcClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { diff --git a/core/chains/evm/client/send_only_node_test.go b/core/chains/evm/client/send_only_node_test.go index b37ee142533..876ae9bc4da 100644 --- a/core/chains/evm/client/send_only_node_test.go +++ b/core/chains/evm/client/send_only_node_test.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index f4ad6a65a1a..33ecc3d0d5f 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -18,8 +18,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index 7971a18d4db..6030991179b 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -9,13 +9,14 @@ import ( ocr "github.com/smartcontractkit/libocr/offchainreporting" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" - gencfg "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" ) -func NewTOMLChainScopedConfig(appCfg gencfg.AppConfig, tomlConfig *toml.EVMConfig, lggr logger.Logger) *ChainScoped { +func NewTOMLChainScopedConfig(appCfg config.AppConfig, tomlConfig *toml.EVMConfig, lggr logger.Logger) *ChainScoped { return &ChainScoped{ AppConfig: appCfg, evmConfig: &evmConfig{c: tomlConfig}, @@ -24,7 +25,7 @@ func NewTOMLChainScopedConfig(appCfg gencfg.AppConfig, tomlConfig *toml.EVMConfi // ChainScoped implements config.ChainScopedConfig with a gencfg.BasicConfig and EVMConfig. type ChainScoped struct { - gencfg.AppConfig + config.AppConfig lggr logger.Logger evmConfig *evmConfig @@ -140,11 +141,11 @@ func (e *evmConfig) BlockEmissionIdleWarningThreshold() time.Duration { return e.c.NoNewHeadsThreshold.Duration() } -func (e *evmConfig) ChainType() gencfg.ChainType { +func (e *evmConfig) ChainType() commonconfig.ChainType { if e.c.ChainType == nil { return "" } - return gencfg.ChainType(*e.c.ChainType) + return commonconfig.ChainType(*e.c.ChainType) } func (e *evmConfig) ChainID() *big.Int { diff --git a/core/chains/evm/config/chain_scoped_gas_estimator.go b/core/chains/evm/config/chain_scoped_gas_estimator.go index cc28ce75f03..0c52cf0a468 100644 --- a/core/chains/evm/config/chain_scoped_gas_estimator.go +++ b/core/chains/evm/config/chain_scoped_gas_estimator.go @@ -3,7 +3,7 @@ package config import ( gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" ) diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index f8ec030969e..ec90797d7fa 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -6,7 +6,9 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/config" ) @@ -24,7 +26,7 @@ type EVM interface { BlockBackfillSkip() bool BlockEmissionIdleWarningThreshold() time.Duration ChainID() *big.Int - ChainType() config.ChainType + ChainType() commonconfig.ChainType FinalityDepth() uint32 FinalityTagEnabled() bool FlagsContractAddress() string @@ -32,7 +34,7 @@ type EVM interface { LogBackfillBatchSize() uint32 LogKeepBlocksDepth() uint32 LogPollInterval() time.Duration - MinContractPayment() *assets.Link + MinContractPayment() *relayassets.Link MinIncomingConfirmations() uint32 NonceAutoSync() bool OperatorFactoryAddress() string diff --git a/core/chains/evm/config/config_test.go b/core/chains/evm/config/config_test.go index 0a3fc5f41e6..d34d1eae63e 100644 --- a/core/chains/evm/config/config_test.go +++ b/core/chains/evm/config/config_test.go @@ -11,7 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -402,7 +403,7 @@ func Test_chainScopedConfig_Validate(t *testing.T) { t.Run("arbitrum-estimator", func(t *testing.T) { t.Run("custom", func(t *testing.T) { cfg := configWithChains(t, 0, &toml.Chain{ - ChainType: ptr(string(config.ChainArbitrum)), + ChainType: ptr(string(commonconfig.ChainArbitrum)), GasEstimator: toml.GasEstimator{ Mode: ptr("BlockHistory"), }, @@ -433,7 +434,7 @@ func Test_chainScopedConfig_Validate(t *testing.T) { t.Run("optimism-estimator", func(t *testing.T) { t.Run("custom", func(t *testing.T) { cfg := configWithChains(t, 0, &toml.Chain{ - ChainType: ptr(string(config.ChainOptimismBedrock)), + ChainType: ptr(string(commonconfig.ChainOptimismBedrock)), GasEstimator: toml.GasEstimator{ Mode: ptr("BlockHistory"), }, diff --git a/core/chains/evm/config/mocks/gas_estimator.go b/core/chains/evm/config/mocks/gas_estimator.go index 8cb54132f58..6260b3cd501 100644 --- a/core/chains/evm/config/mocks/gas_estimator.go +++ b/core/chains/evm/config/mocks/gas_estimator.go @@ -4,7 +4,7 @@ package mocks import ( common "github.com/ethereum/go-ethereum/common" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" config "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index cf2cde460e5..ae0fb41c5ea 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -12,12 +12,13 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "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" @@ -354,7 +355,7 @@ type Chain struct { LogPollInterval *models.Duration LogKeepBlocksDepth *uint32 MinIncomingConfirmations *uint32 - MinContractPayment *assets.Link + MinContractPayment *relayassets.Link NonceAutoSync *bool NoNewHeadsThreshold *models.Duration OperatorFactoryAddress *ethkey.EIP55Address diff --git a/core/chains/evm/config/toml/defaults.go b/core/chains/evm/config/toml/defaults.go index 68513383585..d362e9ac3dc 100644 --- a/core/chains/evm/config/toml/defaults.go +++ b/core/chains/evm/config/toml/defaults.go @@ -8,7 +8,7 @@ import ( "slices" "strings" - "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/utils" configutils "github.com/smartcontractkit/chainlink/v2/core/utils/config" ) diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index 78d93243bbe..860126a5888 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/chains/evm/gas/arbitrum_estimator_test.go b/core/chains/evm/gas/arbitrum_estimator_test.go index a226368edf2..894b531dc97 100644 --- a/core/chains/evm/gas/arbitrum_estimator_test.go +++ b/core/chains/evm/gas/arbitrum_estimator_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 80ae19f109f..eb35cd27511 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -16,12 +16,12 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index c8b193c4435..2747ea067d6 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -18,12 +18,12 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "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" diff --git a/core/chains/evm/gas/chain_specific.go b/core/chains/evm/gas/chain_specific.go index 4f0d2e6b2f8..c989cd0aa98 100644 --- a/core/chains/evm/gas/chain_specific.go +++ b/core/chains/evm/gas/chain_specific.go @@ -1,9 +1,9 @@ package gas import ( - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" ) // chainSpecificIsUsable allows for additional logic specific to a particular diff --git a/core/chains/evm/gas/cmd/arbgas/main.go b/core/chains/evm/gas/cmd/arbgas/main.go index 4ceeb0009e7..6b79ff2f130 100644 --- a/core/chains/evm/gas/cmd/arbgas/main.go +++ b/core/chains/evm/gas/cmd/arbgas/main.go @@ -12,7 +12,7 @@ import ( "go.uber.org/zap/zapcore" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/gas/fixed_price_estimator.go b/core/chains/evm/gas/fixed_price_estimator.go index a45df741a27..7eb7454dad9 100644 --- a/core/chains/evm/gas/fixed_price_estimator.go +++ b/core/chains/evm/gas/fixed_price_estimator.go @@ -7,7 +7,7 @@ import ( commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/gas/fixed_price_estimator_test.go b/core/chains/evm/gas/fixed_price_estimator_test.go index a6cb313e5e2..9fa0997c103 100644 --- a/core/chains/evm/gas/fixed_price_estimator_test.go +++ b/core/chains/evm/gas/fixed_price_estimator_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/gas/gas_test.go b/core/chains/evm/gas/gas_test.go index 4c6be2cf504..a3f7224a094 100644 --- a/core/chains/evm/gas/gas_test.go +++ b/core/chains/evm/gas/gas_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/gas/helpers_test.go b/core/chains/evm/gas/helpers_test.go index ac1f5129e09..9a5076be6d6 100644 --- a/core/chains/evm/gas/helpers_test.go +++ b/core/chains/evm/gas/helpers_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" ) func init() { diff --git a/core/chains/evm/gas/mocks/config.go b/core/chains/evm/gas/mocks/config.go index eabbd0f03e4..c09005b5e3d 100644 --- a/core/chains/evm/gas/mocks/config.go +++ b/core/chains/evm/gas/mocks/config.go @@ -3,7 +3,7 @@ package mocks import ( - config "github.com/smartcontractkit/chainlink/v2/core/config" + config "github.com/smartcontractkit/chainlink/v2/common/config" mock "github.com/stretchr/testify/mock" ) diff --git a/core/chains/evm/gas/mocks/evm_estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go index 80ab3c68cc0..f2cb51f8560 100644 --- a/core/chains/evm/gas/mocks/evm_estimator.go +++ b/core/chains/evm/gas/mocks/evm_estimator.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/gas/mocks/evm_fee_estimator.go b/core/chains/evm/gas/mocks/evm_fee_estimator.go index 42ea96cd50b..b9b16367796 100644 --- a/core/chains/evm/gas/mocks/evm_fee_estimator.go +++ b/core/chains/evm/gas/mocks/evm_fee_estimator.go @@ -5,7 +5,7 @@ package mocks import ( big "math/big" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" context "context" diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 299d7d54734..50cbddcd9bc 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -11,16 +11,16 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index c1dd9e44ffc..8ac94a2269c 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" rollupMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index d990017bd0f..09244e9d319 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -13,9 +13,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go index 320c9cb71da..801d72919e7 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go @@ -13,8 +13,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" - "github.com/smartcontractkit/chainlink/v2/core/assets" - "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/chains/evm/gas/rollups/mocks/l1_oracle.go b/core/chains/evm/gas/rollups/mocks/l1_oracle.go index 31407300b64..f6f8cc736af 100644 --- a/core/chains/evm/gas/rollups/mocks/l1_oracle.go +++ b/core/chains/evm/gas/rollups/mocks/l1_oracle.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" mock "github.com/stretchr/testify/mock" ) diff --git a/core/chains/evm/gas/rollups/models.go b/core/chains/evm/gas/rollups/models.go index 83ae29f4ea9..1659436071b 100644 --- a/core/chains/evm/gas/rollups/models.go +++ b/core/chains/evm/gas/rollups/models.go @@ -3,7 +3,7 @@ package rollups import ( "context" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services" ) diff --git a/core/chains/evm/gas/suggested_price_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go index a4ffb80997e..24cf20f172e 100644 --- a/core/chains/evm/gas/suggested_price_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/gas/suggested_price_estimator_test.go b/core/chains/evm/gas/suggested_price_estimator_test.go index 808b28a3a6b..3b6b8184e3e 100644 --- a/core/chains/evm/gas/suggested_price_estimator_test.go +++ b/core/chains/evm/gas/suggested_price_estimator_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/chains/evm/mocks/balance_monitor.go b/core/chains/evm/mocks/balance_monitor.go index 2a5e66bbc02..cdda18b7f03 100644 --- a/core/chains/evm/mocks/balance_monitor.go +++ b/core/chains/evm/mocks/balance_monitor.go @@ -4,7 +4,7 @@ package mocks import ( common "github.com/ethereum/go-ethereum/common" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" context "context" diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index 5f5b8a243b0..476d3c7019d 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/monitor/balance_test.go b/core/chains/evm/monitor/balance_test.go index c80d64e7ef7..6a62549e493 100644 --- a/core/chains/evm/monitor/balance_test.go +++ b/core/chains/evm/monitor/balance_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/chains/evm/txmgr/attempts.go b/core/chains/evm/txmgr/attempts.go index 7b2a05a34c4..06c1126b869 100644 --- a/core/chains/evm/txmgr/attempts.go +++ b/core/chains/evm/txmgr/attempts.go @@ -12,7 +12,7 @@ import ( feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index 863eae47236..11304384ab8 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 460f9629fb8..48b68f9b55c 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -25,7 +25,7 @@ import ( commonclient "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" diff --git a/core/chains/evm/txmgr/config.go b/core/chains/evm/txmgr/config.go index 0e6b46574ae..8346a0d0551 100644 --- a/core/chains/evm/txmgr/config.go +++ b/core/chains/evm/txmgr/config.go @@ -5,9 +5,9 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink/v2/common/config" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" - coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) // ChainConfig encompasses config used by txmgr package @@ -15,7 +15,7 @@ import ( // //go:generate mockery --quiet --recursive --name ChainConfig --output ./mocks/ --case=underscore --structname Config --filename config.go type ChainConfig interface { - ChainType() coreconfig.ChainType + ChainType() config.ChainType FinalityDepth() uint32 FinalityTagEnabled() bool NonceAutoSync() bool diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 3a0d33f7ba0..31464f8f521 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -24,7 +24,7 @@ import ( commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index c3371fee80b..9262c85a833 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index f8798f9f836..73bfc6fc85a 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -9,7 +9,7 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/txmgr/mocks/config.go b/core/chains/evm/txmgr/mocks/config.go index d6f961ccb50..ab98d686c85 100644 --- a/core/chains/evm/txmgr/mocks/config.go +++ b/core/chains/evm/txmgr/mocks/config.go @@ -3,7 +3,7 @@ package mocks import ( - config "github.com/smartcontractkit/chainlink/v2/core/config" + config "github.com/smartcontractkit/chainlink/v2/common/config" mock "github.com/stretchr/testify/mock" ) diff --git a/core/chains/evm/txmgr/test_helpers.go b/core/chains/evm/txmgr/test_helpers.go index f9c0423a620..e279dddf5fa 100644 --- a/core/chains/evm/txmgr/test_helpers.go +++ b/core/chains/evm/txmgr/test_helpers.go @@ -6,7 +6,8 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -136,12 +137,12 @@ func (c *MockConfig) EVM() evmconfig.EVM { return c.EvmConfig } -func (c *MockConfig) NonceAutoSync() bool { return true } -func (c *MockConfig) ChainType() config.ChainType { return "" } -func (c *MockConfig) FinalityDepth() uint32 { return c.finalityDepth } -func (c *MockConfig) SetFinalityDepth(fd uint32) { c.finalityDepth = fd } -func (c *MockConfig) FinalityTagEnabled() bool { return c.finalityTagEnabled } -func (c *MockConfig) RPCDefaultBatchSize() uint32 { return c.RpcDefaultBatchSize } +func (c *MockConfig) NonceAutoSync() bool { return true } +func (c *MockConfig) ChainType() commonconfig.ChainType { return "" } +func (c *MockConfig) FinalityDepth() uint32 { return c.finalityDepth } +func (c *MockConfig) SetFinalityDepth(fd uint32) { c.finalityDepth = fd } +func (c *MockConfig) FinalityTagEnabled() bool { return c.finalityTagEnabled } +func (c *MockConfig) RPCDefaultBatchSize() uint32 { return c.RpcDefaultBatchSize } func MakeTestConfigs(t *testing.T) (*MockConfig, *TestDatabaseConfig, *TestEvmConfig) { db := &TestDatabaseConfig{defaultQueryTimeout: pg.DefaultQueryTimeout} diff --git a/core/chains/evm/txmgr/transmitchecker_test.go b/core/chains/evm/txmgr/transmitchecker_test.go index 79868e6a641..b43fcb4f459 100644 --- a/core/chains/evm/txmgr/transmitchecker_test.go +++ b/core/chains/evm/txmgr/transmitchecker_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/require" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 4e201b9c6fe..e27cea137b5 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -19,7 +19,7 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" diff --git a/core/chains/evm/types/block_json_benchmark_test.go b/core/chains/evm/types/block_json_benchmark_test.go index 8c5f766d632..21c58bd1987 100644 --- a/core/chains/evm/types/block_json_benchmark_test.go +++ b/core/chains/evm/types/block_json_benchmark_test.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" ) diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index c2d61e00703..83b90b4d539 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -18,7 +18,7 @@ import ( htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types/internal/blocks" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/chains/evm/types/models_test.go b/core/chains/evm/types/models_test.go index 2911e426e86..3507b6a1318 100644 --- a/core/chains/evm/types/models_test.go +++ b/core/chains/evm/types/models_test.go @@ -17,7 +17,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "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" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/cmd/cosmos_node_commands_test.go b/core/cmd/cosmos_node_commands_test.go index 9ac7dfb2ba0..591160629ec 100644 --- a/core/cmd/cosmos_node_commands_test.go +++ b/core/cmd/cosmos_node_commands_test.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" + relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -35,7 +35,7 @@ func TestShell_IndexCosmosNodes(t *testing.T) { chainID := cosmostest.RandomChainID() node := config.Node{ Name: ptr("second"), - TendermintURL: utils.MustParseURL("http://tender.mint.test/bombay-12"), + TendermintURL: relaycfg.MustParseURL("http://tender.mint.test/bombay-12"), } chain := config.TOMLConfig{ ChainID: ptr(chainID), diff --git a/core/cmd/eth_keys_commands_test.go b/core/cmd/eth_keys_commands_test.go index 630e76783a2..d74ae231d72 100644 --- a/core/cmd/eth_keys_commands_test.go +++ b/core/cmd/eth_keys_commands_test.go @@ -12,7 +12,8 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -34,7 +35,7 @@ func TestEthKeysPresenter_RenderTable(t *testing.T) { var ( address = "0x5431F5F973781809D18643b87B44921b11355d81" ethBalance = assets.NewEth(1) - linkBalance = assets.NewLinkFromJuels(2) + linkBalance = relayassets.NewLinkFromJuels(2) isDisabled = true createdAt = time.Now() updatedAt = time.Now().Add(time.Second) @@ -89,7 +90,7 @@ func TestShell_ListETHKeys(t *testing.T) { ethClient := newEthMock(t) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(assets.NewLinkFromJuels(13), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(13), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil) app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].Enabled = ptr(true) @@ -168,7 +169,7 @@ func TestShell_CreateETHKey(t *testing.T) { ethClient := newEthMock(t) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(assets.NewLinkFromJuels(42), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(42), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil) app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { @@ -243,7 +244,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { ethClient := newEthMock(t) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(assets.NewLinkFromJuels(42), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(42), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil) app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].Enabled = ptr(true) @@ -361,7 +362,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { ethClient.On("Dial", mock.Anything).Maybe() ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(assets.NewLinkFromJuels(42), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(42), nil) set := flag.NewFlagSet("test", 0) cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") diff --git a/core/cmd/evm_transaction_commands.go b/core/cmd/evm_transaction_commands.go index 68fffde303e..d702bc3b799 100644 --- a/core/cmd/evm_transaction_commands.go +++ b/core/cmd/evm_transaction_commands.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" diff --git a/core/cmd/evm_transaction_commands_test.go b/core/cmd/evm_transaction_commands_test.go index 484b1ccd3da..e071d875f03 100644 --- a/core/cmd/evm_transaction_commands_test.go +++ b/core/cmd/evm_transaction_commands_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "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" "github.com/smartcontractkit/chainlink/v2/core/cmd" diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index 70d75f761b5..f7e17ef7fc2 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -31,8 +31,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/build" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/cmd/solana_node_commands_test.go b/core/cmd/solana_node_commands_test.go index 3f95ebc0d84..7c88557c6de 100644 --- a/core/cmd/solana_node_commands_test.go +++ b/core/cmd/solana_node_commands_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" + "github.com/smartcontractkit/chainlink-relay/pkg/config" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -35,11 +35,11 @@ func TestShell_IndexSolanaNodes(t *testing.T) { id := solanatest.RandomChainID() node1 := solcfg.Node{ Name: ptr("first"), - URL: utils.MustParseURL("https://solana1.example"), + URL: config.MustParseURL("https://solana1.example"), } node2 := solcfg.Node{ Name: ptr("second"), - URL: utils.MustParseURL("https://solana2.example"), + URL: config.MustParseURL("https://solana2.example"), } chain := solana.TOMLConfig{ ChainID: &id, diff --git a/core/cmd/starknet_node_commands_test.go b/core/cmd/starknet_node_commands_test.go index e799e5aac07..9d7c6fcaf4c 100644 --- a/core/cmd/starknet_node_commands_test.go +++ b/core/cmd/starknet_node_commands_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" + relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" @@ -34,11 +34,11 @@ func TestShell_IndexStarkNetNodes(t *testing.T) { id := "starknet chain ID" node1 := config.Node{ Name: ptr("first"), - URL: utils.MustParseURL("https://starknet1.example"), + URL: relaycfg.MustParseURL("https://starknet1.example"), } node2 := config.Node{ Name: ptr("second"), - URL: utils.MustParseURL("https://starknet2.example"), + URL: relaycfg.MustParseURL("https://starknet2.example"), } chain := config.TOMLConfig{ ChainID: &id, diff --git a/core/config/docs/docs_test.go b/core/config/docs/docs_test.go index 927592e448d..74fa6682c96 100644 --- a/core/config/docs/docs_test.go +++ b/core/config/docs/docs_test.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-solana/pkg/solana" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/docs" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" diff --git a/core/config/parse/parsers.go b/core/config/parse/parsers.go index 93e519064bf..e2f6978187c 100644 --- a/core/config/parse/parsers.go +++ b/core/config/parse/parsers.go @@ -13,7 +13,8 @@ import ( "github.com/pkg/errors" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -22,10 +23,10 @@ func String(str string) (string, error) { return str, nil } -func Link(str string) (*assets.Link, error) { - i, ok := new(assets.Link).SetString(str, 10) +func Link(str string) (*relayassets.Link, error) { + i, ok := new(relayassets.Link).SetString(str, 10) if !ok { - return i, fmt.Errorf("unable to parse '%v' into *assets.Link(base 10)", str) + return i, fmt.Errorf("unable to parse '%v' into *relayassets.Link(base 10)", str) } return i, nil } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 778ccbdb154..644c516c21b 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -47,10 +47,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index a52b9a5d06b..741afe828f9 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -25,9 +25,9 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index b5f42d8bf3e..7749b173c55 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -41,9 +41,9 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/internal/features/ocr2/features_ocr2_test.go b/core/internal/features/ocr2/features_ocr2_test.go index 3e220935685..387b15f76c8 100644 --- a/core/internal/features/ocr2/features_ocr2_test.go +++ b/core/internal/features/ocr2/features_ocr2_test.go @@ -34,8 +34,8 @@ import ( confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" diff --git a/core/scripts/go.mod b/core/scripts/go.mod index eb41312a6ab..7bf60aff8b4 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a // indirect + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 35e85fe2c97..7139f0efa4e 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1464,8 +1464,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a h1:G/pD8uI1PULRJU8Y3eLLzjqQBp9ruG9hj+wWxtyrgTo= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd h1:PRVJxNK67pQWufXuB1cxckH/xZkcQFDy8KjN9ZYqong= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd/go.mod h1:rOayi690YxLlkQy959PD8INhOAIAUi9LoN0G+J/CEf4= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/core/scripts/ocr2vrf/main.go b/core/scripts/ocr2vrf/main.go index 60260c54d9d..c698fcd730d 100644 --- a/core/scripts/ocr2vrf/main.go +++ b/core/scripts/ocr2vrf/main.go @@ -15,7 +15,7 @@ import ( ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" ) diff --git a/core/scripts/vrfv2/testnet/main.go b/core/scripts/vrfv2/testnet/main.go index 677c0b105ea..5856256504b 100644 --- a/core/scripts/vrfv2/testnet/main.go +++ b/core/scripts/vrfv2/testnet/main.go @@ -24,7 +24,7 @@ import ( "github.com/jmoiron/sqlx" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" diff --git a/core/scripts/vrfv2plus/testnet/main.go b/core/scripts/vrfv2plus/testnet/main.go index b7940d6fda0..6b1ef585a5a 100644 --- a/core/scripts/vrfv2plus/testnet/main.go +++ b/core/scripts/vrfv2plus/testnet/main.go @@ -29,7 +29,7 @@ import ( "github.com/jmoiron/sqlx" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2plus" diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 34fcc4bbe91..0caae3607f7 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -17,12 +17,13 @@ import ( ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - relayutils "github.com/smartcontractkit/chainlink-relay/pkg/utils" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" legacy "github.com/smartcontractkit/chainlink/v2/core/config" @@ -144,7 +145,7 @@ var ( MaxMsgsPerBatch: ptr[int64](13), }, Nodes: []*coscfg.Node{ - {Name: ptr("primary"), TendermintURL: relayutils.MustParseURL("http://columbus.cosmos.com")}, + {Name: ptr("primary"), TendermintURL: relaycfg.MustParseURL("http://columbus.cosmos.com")}, }}, { ChainID: ptr("Malaga-420"), @@ -152,7 +153,7 @@ var ( BlocksUntilTxTimeout: ptr[int64](20), }, Nodes: []*coscfg.Node{ - {Name: ptr("secondary"), TendermintURL: relayutils.MustParseURL("http://bombay.cosmos.com")}, + {Name: ptr("secondary"), TendermintURL: relaycfg.MustParseURL("http://bombay.cosmos.com")}, }}, }, Solana: []*solana.TOMLConfig{ @@ -162,16 +163,16 @@ var ( MaxRetries: ptr[int64](12), }, Nodes: []*solcfg.Node{ - {Name: ptr("primary"), URL: relayutils.MustParseURL("http://mainnet.solana.com")}, + {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://mainnet.solana.com")}, }, }, { ChainID: ptr("testnet"), Chain: solcfg.Chain{ - OCR2CachePollPeriod: relayutils.MustNewDuration(time.Minute), + OCR2CachePollPeriod: relaycfg.MustNewDuration(time.Minute), }, Nodes: []*solcfg.Node{ - {Name: ptr("secondary"), URL: relayutils.MustParseURL("http://testnet.solana.com")}, + {Name: ptr("secondary"), URL: relaycfg.MustParseURL("http://testnet.solana.com")}, }, }, }, @@ -179,10 +180,10 @@ var ( { ChainID: ptr("foobar"), Chain: stkcfg.Chain{ - ConfirmationPoll: relayutils.MustNewDuration(time.Hour), + ConfirmationPoll: relaycfg.MustNewDuration(time.Hour), }, Nodes: []*stkcfg.Node{ - {Name: ptr("primary"), URL: relayutils.MustParseURL("http://stark.node")}, + {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://stark.node")}, }, }, }, @@ -537,7 +538,7 @@ func TestConfig_Marshal(t *testing.T) { LogBackfillBatchSize: ptr[uint32](17), LogPollInterval: &minute, LogKeepBlocksDepth: ptr[uint32](100000), - MinContractPayment: assets.NewLinkFromJuels(math.MaxInt64), + MinContractPayment: relayassets.NewLinkFromJuels(math.MaxInt64), MinIncomingConfirmations: ptr[uint32](13), NonceAutoSync: ptr(true), NoNewHeadsThreshold: &minute, @@ -602,13 +603,13 @@ func TestConfig_Marshal(t *testing.T) { ChainID: ptr("mainnet"), Enabled: ptr(false), Chain: solcfg.Chain{ - BalancePollPeriod: relayutils.MustNewDuration(time.Minute), - ConfirmPollPeriod: relayutils.MustNewDuration(time.Second), - OCR2CachePollPeriod: relayutils.MustNewDuration(time.Minute), - OCR2CacheTTL: relayutils.MustNewDuration(time.Hour), - TxTimeout: relayutils.MustNewDuration(time.Hour), - TxRetryTimeout: relayutils.MustNewDuration(time.Minute), - TxConfirmTimeout: relayutils.MustNewDuration(time.Second), + BalancePollPeriod: relaycfg.MustNewDuration(time.Minute), + ConfirmPollPeriod: relaycfg.MustNewDuration(time.Second), + OCR2CachePollPeriod: relaycfg.MustNewDuration(time.Minute), + OCR2CacheTTL: relaycfg.MustNewDuration(time.Hour), + TxTimeout: relaycfg.MustNewDuration(time.Hour), + TxRetryTimeout: relaycfg.MustNewDuration(time.Minute), + TxConfirmTimeout: relaycfg.MustNewDuration(time.Second), SkipPreflight: ptr(true), Commitment: ptr("banana"), MaxRetries: ptr[int64](7), @@ -616,12 +617,12 @@ func TestConfig_Marshal(t *testing.T) { ComputeUnitPriceMax: ptr[uint64](1000), ComputeUnitPriceMin: ptr[uint64](10), ComputeUnitPriceDefault: ptr[uint64](100), - FeeBumpPeriod: relayutils.MustNewDuration(time.Minute), + FeeBumpPeriod: relaycfg.MustNewDuration(time.Minute), }, Nodes: []*solcfg.Node{ - {Name: ptr("primary"), URL: relayutils.MustParseURL("http://solana.web")}, - {Name: ptr("foo"), URL: relayutils.MustParseURL("http://solana.foo")}, - {Name: ptr("bar"), URL: relayutils.MustParseURL("http://solana.bar")}, + {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://solana.web")}, + {Name: ptr("foo"), URL: relaycfg.MustParseURL("http://solana.foo")}, + {Name: ptr("bar"), URL: relaycfg.MustParseURL("http://solana.bar")}, }, }, } @@ -630,14 +631,14 @@ func TestConfig_Marshal(t *testing.T) { ChainID: ptr("foobar"), Enabled: ptr(true), Chain: stkcfg.Chain{ - OCR2CachePollPeriod: relayutils.MustNewDuration(6 * time.Hour), - OCR2CacheTTL: relayutils.MustNewDuration(3 * time.Minute), - RequestTimeout: relayutils.MustNewDuration(time.Minute + 3*time.Second), - TxTimeout: relayutils.MustNewDuration(13 * time.Second), - ConfirmationPoll: relayutils.MustNewDuration(42 * time.Second), + OCR2CachePollPeriod: relaycfg.MustNewDuration(6 * time.Hour), + OCR2CacheTTL: relaycfg.MustNewDuration(3 * time.Minute), + RequestTimeout: relaycfg.MustNewDuration(time.Minute + 3*time.Second), + TxTimeout: relaycfg.MustNewDuration(13 * time.Second), + ConfirmationPoll: relaycfg.MustNewDuration(42 * time.Second), }, Nodes: []*stkcfg.Node{ - {Name: ptr("primary"), URL: relayutils.MustParseURL("http://stark.node")}, + {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://stark.node")}, }, }, } @@ -647,21 +648,21 @@ func TestConfig_Marshal(t *testing.T) { Enabled: ptr(true), Chain: coscfg.Chain{ Bech32Prefix: ptr("wasm"), - BlockRate: relayutils.MustNewDuration(time.Minute), + BlockRate: relaycfg.MustNewDuration(time.Minute), BlocksUntilTxTimeout: ptr[int64](12), - ConfirmPollPeriod: relayutils.MustNewDuration(time.Second), + ConfirmPollPeriod: relaycfg.MustNewDuration(time.Second), FallbackGasPrice: mustDecimal("0.001"), GasToken: ptr("ucosm"), GasLimitMultiplier: mustDecimal("1.2"), MaxMsgsPerBatch: ptr[int64](17), - OCR2CachePollPeriod: relayutils.MustNewDuration(time.Minute), - OCR2CacheTTL: relayutils.MustNewDuration(time.Hour), - TxMsgTimeout: relayutils.MustNewDuration(time.Second), + OCR2CachePollPeriod: relaycfg.MustNewDuration(time.Minute), + OCR2CacheTTL: relaycfg.MustNewDuration(time.Hour), + TxMsgTimeout: relaycfg.MustNewDuration(time.Second), }, Nodes: []*coscfg.Node{ - {Name: ptr("primary"), TendermintURL: relayutils.MustParseURL("http://tender.mint")}, - {Name: ptr("foo"), TendermintURL: relayutils.MustParseURL("http://foo.url")}, - {Name: ptr("bar"), TendermintURL: relayutils.MustParseURL("http://bar.web")}, + {Name: ptr("primary"), TendermintURL: relaycfg.MustParseURL("http://tender.mint")}, + {Name: ptr("foo"), TendermintURL: relaycfg.MustParseURL("http://foo.url")}, + {Name: ptr("bar"), TendermintURL: relaycfg.MustParseURL("http://bar.web")}, }, }, } diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 87293069646..293cc298c8d 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/require" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" + relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink-relay/pkg/loop" - relayutils "github.com/smartcontractkit/chainlink-relay/pkg/utils" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" @@ -84,7 +84,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: solcfg.Chain{}, Nodes: []*solcfg.Node{{ Name: ptr("solana chain 1 node 1"), - URL: ((*relayutils.URL)(models.MustParseURL("http://localhost:8547").URL())), + URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8547").URL())), }}, }, &solana.TOMLConfig{ @@ -93,7 +93,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: solcfg.Chain{}, Nodes: []*solcfg.Node{{ Name: ptr("solana chain 2 node 1"), - URL: ((*relayutils.URL)(models.MustParseURL("http://localhost:8527").URL())), + URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8527").URL())), }}, }, } @@ -106,15 +106,15 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: []*stkcfg.Node{ { Name: ptr("starknet chain 1 node 1"), - URL: ((*relayutils.URL)(models.MustParseURL("http://localhost:8547").URL())), + URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8547").URL())), }, { Name: ptr("starknet chain 1 node 2"), - URL: ((*relayutils.URL)(models.MustParseURL("http://localhost:8548").URL())), + URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8548").URL())), }, { Name: ptr("starknet chain 1 node 3"), - URL: ((*relayutils.URL)(models.MustParseURL("http://localhost:8549").URL())), + URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8549").URL())), }, }, }, @@ -125,7 +125,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: []*stkcfg.Node{ { Name: ptr("starknet chain 2 node 1"), - URL: ((*relayutils.URL)(models.MustParseURL("http://localhost:3547").URL())), + URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:3547").URL())), }, }, }, @@ -143,7 +143,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 1 node 1"), - TendermintURL: (*relayutils.URL)(models.MustParseURL("http://localhost:9548").URL()), + TendermintURL: (*relaycfg.URL)(models.MustParseURL("http://localhost:9548").URL()), }, }, }, @@ -158,7 +158,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 2 node 1"), - TendermintURL: (*relayutils.URL)(models.MustParseURL("http://localhost:9598").URL()), + TendermintURL: (*relaycfg.URL)(models.MustParseURL("http://localhost:9598").URL()), }, }, }, diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index 920f94b4d60..10943308b39 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -9,9 +9,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index 34c79a0afbb..1c7929d94d3 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" diff --git a/core/services/directrequest/validate.go b/core/services/directrequest/validate.go index 95ea9b60232..cdb478e8aae 100644 --- a/core/services/directrequest/validate.go +++ b/core/services/directrequest/validate.go @@ -4,7 +4,7 @@ import ( "github.com/pelletier/go-toml" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "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" diff --git a/core/services/fluxmonitorv2/config.go b/core/services/fluxmonitorv2/config.go index 5e5e56e768b..0360e9ce03a 100644 --- a/core/services/fluxmonitorv2/config.go +++ b/core/services/fluxmonitorv2/config.go @@ -3,7 +3,7 @@ package fluxmonitorv2 import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index e81e1ba9e63..e8bbf739bbb 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -20,8 +20,8 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/services/fluxmonitorv2/integrations_test.go b/core/services/fluxmonitorv2/integrations_test.go index 38c73d3ad74..729bcd76eba 100644 --- a/core/services/fluxmonitorv2/integrations_test.go +++ b/core/services/fluxmonitorv2/integrations_test.go @@ -26,8 +26,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flags_wrapper" faw "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/flux_aggregator_wrapper" diff --git a/core/services/fluxmonitorv2/payment_checker.go b/core/services/fluxmonitorv2/payment_checker.go index 6b11ec13433..b5dbd73c064 100644 --- a/core/services/fluxmonitorv2/payment_checker.go +++ b/core/services/fluxmonitorv2/payment_checker.go @@ -3,7 +3,7 @@ package fluxmonitorv2 import ( "math/big" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" ) // MinFundedRounds defines the minimum number of rounds that needs to be paid diff --git a/core/services/fluxmonitorv2/payment_checker_test.go b/core/services/fluxmonitorv2/payment_checker_test.go index c987fdf3604..48a06553cb9 100644 --- a/core/services/fluxmonitorv2/payment_checker_test.go +++ b/core/services/fluxmonitorv2/payment_checker_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" ) diff --git a/core/services/fluxmonitorv2/validate_test.go b/core/services/fluxmonitorv2/validate_test.go index 78dda2e3d31..b397e6d3749 100644 --- a/core/services/fluxmonitorv2/validate_test.go +++ b/core/services/fluxmonitorv2/validate_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils/tomlutils" diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index c5dbff6f10f..5b67e333d2b 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -10,9 +10,9 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/common" diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index fa9f74712be..1bf9f8e5938 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -7,7 +7,7 @@ import ( "math/big" "testing" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/functions" diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index bb6812c1f9b..a4301ef7e9c 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -13,8 +13,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 49fdae2bb24..4c8ba5bec3c 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index f4471e75c68..c2fc425918a 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -16,8 +16,8 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/job/models.go b/core/services/job/models.go index a474040dd41..0b3e622f59b 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -14,10 +14,11 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -433,7 +434,7 @@ type DirectRequestSpec struct { ContractAddress ethkey.EIP55Address `toml:"contractAddress"` MinIncomingConfirmations clnull.Uint32 `toml:"minIncomingConfirmations"` Requesters models.AddressCollection `toml:"requesters"` - MinContractPayment *assets.Link `toml:"minContractPaymentLinkJuels"` + MinContractPayment *relayassets.Link `toml:"minContractPaymentLinkJuels"` EVMChainID *utils.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` @@ -474,7 +475,7 @@ type FluxMonitorSpec struct { DrumbeatSchedule string DrumbeatRandomDelay time.Duration DrumbeatEnabled bool - MinPayment *assets.Link + MinPayment *relayassets.Link EVMChainID *utils.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` diff --git a/core/services/keeper/integration_test.go b/core/services/keeper/integration_test.go index f76ef935741..29a0b68702d 100644 --- a/core/services/keeper/integration_test.go +++ b/core/services/keeper/integration_test.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers/link_token_interface" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 33ad8b7d773..30e9f363579 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -13,7 +13,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 32b1d2c191d..a064c3ed4b6 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -15,8 +15,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" diff --git a/core/services/keeper/upkeep_executer_unit_test.go b/core/services/keeper/upkeep_executer_unit_test.go index f4e99341ca0..a8fc46319cd 100644 --- a/core/services/keeper/upkeep_executer_unit_test.go +++ b/core/services/keeper/upkeep_executer_unit_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" diff --git a/core/services/keystore/keys/ethkey/address.go b/core/services/keystore/keys/ethkey/address.go index 4a161045ea0..c14b602c597 100644 --- a/core/services/keystore/keys/ethkey/address.go +++ b/core/services/keystore/keys/ethkey/address.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/utils" + "github.com/smartcontractkit/chainlink-relay/pkg/utils/bytes" ) // EIP55Address is a new type for string which persists an ethereum address in @@ -85,7 +85,7 @@ func (a *EIP55Address) UnmarshalText(input []byte) error { // UnmarshalJSON parses a hash from a JSON string func (a *EIP55Address) UnmarshalJSON(input []byte) error { - input = utils.RemoveQuotes(input) + input = bytes.TrimQuotes(input) return a.UnmarshalText(input) } diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index db19bdd4f0a..3e614fef4ae 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -22,11 +22,11 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/offchain_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" diff --git a/core/services/ocr/validate.go b/core/services/ocr/validate.go index 07fdc15912e..145bb7597ec 100644 --- a/core/services/ocr/validate.go +++ b/core/services/ocr/validate.go @@ -10,9 +10,9 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/libocr/offchainreporting" + "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/config" "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/ocrcommon" diff --git a/core/services/ocr2/plugins/functions/config/config.go b/core/services/ocr2/plugins/functions/config/config.go index 0978500deb5..a179a22ce44 100644 --- a/core/services/ocr2/plugins/functions/config/config.go +++ b/core/services/ocr2/plugins/functions/config/config.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" diff --git a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go index 9f63d60eef6..e576e829677 100644 --- a/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go +++ b/core/services/ocr2/plugins/functions/integration_tests/v1/internal/testutils.go @@ -29,8 +29,8 @@ import ( confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_allow_list" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_client_example" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 8597b8ad4cc..14d7415cab5 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/functions/plugin_test.go b/core/services/ocr2/plugins/functions/plugin_test.go index 2e35672e920..eea751789a0 100644 --- a/core/services/ocr2/plugins/functions/plugin_test.go +++ b/core/services/ocr2/plugins/functions/plugin_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index e7e059289a2..ae2b4ca9742 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -39,8 +39,8 @@ import ( relaycodecv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" relaycodecv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" token "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/fee_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/reward_manager" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index 9a1e99610c1..0df774d5dfd 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -21,7 +21,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index 562f972bc42..ee8b7cd6e9a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -33,7 +33,7 @@ import ( "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" automationForwarderLogic "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_forwarder_logic" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/basic_upkeep_contract" diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index f50321631ce..569dc20753b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -34,7 +34,7 @@ import ( "github.com/stretchr/testify/require" "github.com/umbracle/ethgo/abi" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/config/toml" diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index 0dbb6a5915e..57d13a69ec5 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -31,7 +31,7 @@ import ( "github.com/smartcontractkit/ocr2vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go index 43262d6ad50..c0e969a2879 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go @@ -6,7 +6,7 @@ import ( "github.com/smartcontractkit/ocr2vrf/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" ) diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_test.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_test.go index a33c146564b..ec8b085dea8 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) func Test_ReasonableGasPrice(t *testing.T) { diff --git a/core/services/ocrcommon/block_translator.go b/core/services/ocrcommon/block_translator.go index 48b96416998..dcac83fd2bd 100644 --- a/core/services/ocrcommon/block_translator.go +++ b/core/services/ocrcommon/block_translator.go @@ -4,9 +4,9 @@ import ( "context" "math/big" + "github.com/smartcontractkit/chainlink/v2/common/config" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/services/ocrcommon/config.go b/core/services/ocrcommon/config.go index a61a47519cc..2fcc877610c 100644 --- a/core/services/ocrcommon/config.go +++ b/core/services/ocrcommon/config.go @@ -5,7 +5,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/chainlink/v2/core/config" + "github.com/smartcontractkit/chainlink/v2/common/config" ) type Config interface { diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 40542c2631a..3aea503ae6a 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -18,7 +18,7 @@ import ( relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/transmission/integration_test.go b/core/services/transmission/integration_test.go index 0484b1d8cd6..58521dcdf84 100644 --- a/core/services/transmission/integration_test.go +++ b/core/services/transmission/integration_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/mock_v3_aggregator_contract" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_consumer_interface_v08" diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index e976d01b995..3b91f783b4a 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -12,8 +12,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 389e1159be1..3d544027d40 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -8,9 +8,9 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" diff --git a/core/services/vrf/mocks/fee_config.go b/core/services/vrf/mocks/fee_config.go index 55a6360c339..067ce7e4455 100644 --- a/core/services/vrf/mocks/fee_config.go +++ b/core/services/vrf/mocks/fee_config.go @@ -4,7 +4,7 @@ package mocks import ( common "github.com/ethereum/go-ethereum/common" - assets "github.com/smartcontractkit/chainlink/v2/core/assets" + assets "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" config "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" diff --git a/core/services/vrf/proof/proof_response_test.go b/core/services/vrf/proof/proof_response_test.go index 24df77d4b32..c547be2be2c 100644 --- a/core/services/vrf/proof/proof_response_test.go +++ b/core/services/vrf/proof/proof_response_test.go @@ -16,7 +16,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" ) diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go index f0c398f8cf0..a8e662c9318 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go index 9fab3737ae2..2601f800e9e 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/solidity_cross_tests" ) @@ -18,14 +17,14 @@ var ( jobID = common.BytesToHash([]byte("1234567890abcdef1234567890abcdef")) seed = big.NewInt(1) sender = common.HexToAddress("0xecfcab0a285d3380e488a39b4bb21e777f8a4eac") - fee = assets.NewLinkFromJuels(100) + fee = big.NewInt(100) requestID = common.HexToHash("0xcafe") raw = solidity_cross_tests.RawRandomnessRequestLog{ KeyHash: keyHash, Seed: seed, JobID: jobID, Sender: sender, - Fee: (*big.Int)(fee), + Fee: fee, RequestID: requestID, Raw: types.Log{ // A raw, on-the-wire RandomnessRequestLog is the concat of fields as uint256's @@ -33,7 +32,7 @@ var ( keyHash.Bytes(), common.BigToHash(seed).Bytes()...), sender.Hash().Bytes()...), - fee.ToHash().Bytes()...), + common.BigToHash(fee).Bytes()...), requestID.Bytes()...), Topics: []common.Hash{{}, jobID}, }, diff --git a/core/services/vrf/solidity_cross_tests/vrf_hash_to_curve_cost_test.go b/core/services/vrf/solidity_cross_tests/vrf_hash_to_curve_cost_test.go index 38675e2651d..29d1db437d1 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_hash_to_curve_cost_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_hash_to_curve_cost_test.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/eth/ethconfig" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" diff --git a/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go index c2896d42314..06875edd74e 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_solidity_crosscheck_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/require" "go.dedis.ch/kyber/v3" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" diff --git a/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go b/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go index 695a9dfd2f1..d1b21b58647 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go +++ b/core/services/vrf/solidity_cross_tests/vrf_v08_solidity_crosscheck_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" diff --git a/core/services/vrf/v2/bhs_feeder_test.go b/core/services/vrf/v2/bhs_feeder_test.go index 219fe1c8fd2..f388f80f561 100644 --- a/core/services/vrf/v2/bhs_feeder_test.go +++ b/core/services/vrf/v2/bhs_feeder_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index a086cbbb09f..a5737371919 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -17,7 +17,7 @@ import ( "github.com/stretchr/testify/require" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" v2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index 75026423f4b..e45e650dc8d 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -16,7 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 1f607da2f26..df886924aa1 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -30,10 +30,11 @@ import ( "github.com/jmoiron/sqlx" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -1461,7 +1462,7 @@ func simulatedOverrides(t *testing.T, defaultGasPrice *assets.Wei, ks ...toml.Ke c.EVM[0].FinalityDepth = ptr[uint32](15) c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) - c.EVM[0].MinContractPayment = assets.NewLinkFromJuels(100) + c.EVM[0].MinContractPayment = relayassets.NewLinkFromJuels(100) c.EVM[0].KeySpecific = ks } } diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 17cb9ec96e4..3480f63f092 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -28,8 +28,8 @@ import ( "github.com/smartcontractkit/chainlink-relay/pkg/services" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/vrf/v2/listener_v2_helpers_test.go b/core/services/vrf/v2/listener_v2_helpers_test.go index fc34a115b1c..401a5bcc577 100644 --- a/core/services/vrf/v2/listener_v2_helpers_test.go +++ b/core/services/vrf/v2/listener_v2_helpers_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" v2 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" diff --git a/core/services/vrf/vrfcommon/types.go b/core/services/vrf/vrfcommon/types.go index 03de4eb562f..175b362b5aa 100644 --- a/core/services/vrf/vrfcommon/types.go +++ b/core/services/vrf/vrfcommon/types.go @@ -5,7 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" ) diff --git a/core/services/vrf/vrfcommon/validate.go b/core/services/vrf/vrfcommon/validate.go index d417336562c..07e8676ddb7 100644 --- a/core/services/vrf/vrfcommon/validate.go +++ b/core/services/vrf/vrfcommon/validate.go @@ -9,7 +9,7 @@ import ( "github.com/pelletier/go-toml" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" diff --git a/core/services/vrf/vrfcommon/validate_test.go b/core/services/vrf/vrfcommon/validate_test.go index 03d544f8189..efb1f22216f 100644 --- a/core/services/vrf/vrfcommon/validate_test.go +++ b/core/services/vrf/vrfcommon/validate_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/job" ) diff --git a/core/services/vrf/vrftesthelpers/helpers.go b/core/services/vrf/vrftesthelpers/helpers.go index f36fb1ea027..2f269fbff03 100644 --- a/core/services/vrf/vrftesthelpers/helpers.go +++ b/core/services/vrf/vrftesthelpers/helpers.go @@ -17,7 +17,7 @@ import ( "github.com/shopspring/decimal" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_consumer_interface" diff --git a/core/store/models/common.go b/core/store/models/common.go index fc7f7762046..10f391861e1 100644 --- a/core/store/models/common.go +++ b/core/store/models/common.go @@ -17,7 +17,7 @@ import ( "github.com/tidwall/gjson" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index beda7354566..0155dc0a53a 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -8,7 +8,7 @@ import ( "github.com/google/uuid" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/utils/big.go b/core/utils/big.go index 6bdb95c1217..f0f9a2d96df 100644 --- a/core/utils/big.go +++ b/core/utils/big.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/smartcontractkit/chainlink-relay/pkg/utils/bytes" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) @@ -80,7 +81,7 @@ func (b Big) MarshalJSON() ([]byte, error) { // UnmarshalText implements encoding.TextUnmarshaler. func (b *Big) UnmarshalText(input []byte) error { - input = RemoveQuotes(input) + input = bytes.TrimQuotes(input) str := string(input) if HasHexPrefix(str) { decoded, err := hexutil.DecodeBig(str) diff --git a/core/utils/utils.go b/core/utils/utils.go index e5541ecf558..6ea7164df1e 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -305,22 +305,6 @@ func Sha256(in string) (string, error) { return hex.EncodeToString(hasher.Sum(nil)), nil } -// IsQuoted checks if the first and last characters are either " or '. -func IsQuoted(input []byte) bool { - return len(input) >= 2 && - ((input[0] == '"' && input[len(input)-1] == '"') || - (input[0] == '\'' && input[len(input)-1] == '\'')) -} - -// RemoveQuotes removes the first and last character if they are both either -// " or ', otherwise it is a noop. -func RemoveQuotes(input []byte) []byte { - if IsQuoted(input) { - return input[1 : len(input)-1] - } - return input -} - // EIP55CapitalizedAddress returns true iff possibleAddressString has the correct // capitalization for an Ethereum address, per EIP 55 func EIP55CapitalizedAddress(possibleAddressString string) bool { diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index eca99f1a20a..57a79e2b611 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -8,7 +8,7 @@ import ( "github.com/jackc/pgconn" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" diff --git a/core/web/bridge_types_controller_test.go b/core/web/bridge_types_controller_test.go index 6459cad0545..a65362ba9aa 100644 --- a/core/web/bridge_types_controller_test.go +++ b/core/web/bridge_types_controller_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index 28afe8c43bf..6e2a3b2efc4 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -9,8 +9,9 @@ import ( "strconv" "strings" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" @@ -364,13 +365,13 @@ func (ekc *ETHKeysController) getEthBalance(ctx context.Context, state ethkey.St } -func (ekc *ETHKeysController) setLinkBalance(bal *assets.Link) presenters.NewETHKeyOption { +func (ekc *ETHKeysController) setLinkBalance(bal *relayassets.Link) presenters.NewETHKeyOption { return presenters.SetETHKeyLinkBalance(bal) } // queries the EthClient for the LINK balance at the address associated with state -func (ekc *ETHKeysController) getLinkBalance(ctx context.Context, state ethkey.State) *assets.Link { - var bal *assets.Link +func (ekc *ETHKeysController) getLinkBalance(ctx context.Context, state ethkey.State) *relayassets.Link { + var bal *relayassets.Link chainID := state.EVMChainID.ToInt() chain, err := ekc.app.GetRelayers().LegacyEVMChains().Get(chainID.String()) if err != nil { diff --git a/core/web/eth_keys_controller_test.go b/core/web/eth_keys_controller_test.go index e3a39d541a4..8941c2d2fdc 100644 --- a/core/web/eth_keys_controller_test.go +++ b/core/web/eth_keys_controller_test.go @@ -8,9 +8,9 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/web/evm_transactions_controller_test.go b/core/web/evm_transactions_controller_test.go index 9d8336325e2..9135be432de 100644 --- a/core/web/evm_transactions_controller_test.go +++ b/core/web/evm_transactions_controller_test.go @@ -6,7 +6,7 @@ import ( "testing" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index f5973a20f2f..7f09bc9fdc6 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -11,8 +11,8 @@ import ( "github.com/pkg/errors" commontxmgr "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" diff --git a/core/web/evm_transfer_controller_test.go b/core/web/evm_transfer_controller_test.go index 14259637b4f..c41219e1894 100644 --- a/core/web/evm_transfer_controller_test.go +++ b/core/web/evm_transfer_controller_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "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" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/web/presenters/bridges.go b/core/web/presenters/bridges.go index 3317895e166..440e444c613 100644 --- a/core/web/presenters/bridges.go +++ b/core/web/presenters/bridges.go @@ -3,7 +3,7 @@ package presenters import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" ) diff --git a/core/web/presenters/bridges_test.go b/core/web/presenters/bridges_test.go index b9fefd022d2..f6ce3af7561 100644 --- a/core/web/presenters/bridges_test.go +++ b/core/web/presenters/bridges_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/core/web/presenters/eth_key.go b/core/web/presenters/eth_key.go index 167679513ef..92654427619 100644 --- a/core/web/presenters/eth_key.go +++ b/core/web/presenters/eth_key.go @@ -3,7 +3,8 @@ package presenters import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -12,14 +13,14 @@ import ( // representation of the address plus its ETH & LINK balances type ETHKeyResource struct { JAID - EVMChainID utils.Big `json:"evmChainID"` - Address string `json:"address"` - EthBalance *assets.Eth `json:"ethBalance"` - LinkBalance *assets.Link `json:"linkBalance"` - Disabled bool `json:"disabled"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - MaxGasPriceWei *utils.Big `json:"maxGasPriceWei"` + EVMChainID utils.Big `json:"evmChainID"` + Address string `json:"address"` + EthBalance *assets.Eth `json:"ethBalance"` + LinkBalance *relayassets.Link `json:"linkBalance"` + Disabled bool `json:"disabled"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + MaxGasPriceWei *utils.Big `json:"maxGasPriceWei"` } // GetName implements the api2go EntityNamer interface @@ -62,7 +63,7 @@ func SetETHKeyEthBalance(ethBalance *assets.Eth) NewETHKeyOption { } } -func SetETHKeyLinkBalance(linkBalance *assets.Link) NewETHKeyOption { +func SetETHKeyLinkBalance(linkBalance *relayassets.Link) NewETHKeyOption { return func(r *ETHKeyResource) { r.LinkBalance = linkBalance } diff --git a/core/web/presenters/eth_key_test.go b/core/web/presenters/eth_key_test.go index 7afb55068fb..0e68fbc90c9 100644 --- a/core/web/presenters/eth_key_test.go +++ b/core/web/presenters/eth_key_test.go @@ -5,7 +5,8 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -39,12 +40,12 @@ func TestETHKeyResource(t *testing.T) { r := NewETHKeyResource(key, state, SetETHKeyEthBalance(assets.NewEth(1)), - SetETHKeyLinkBalance(assets.NewLinkFromJuels(1)), + SetETHKeyLinkBalance(relayassets.NewLinkFromJuels(1)), SetETHKeyMaxGasPriceWei(utils.NewBigI(12345)), ) assert.Equal(t, assets.NewEth(1), r.EthBalance) - assert.Equal(t, assets.NewLinkFromJuels(1), r.LinkBalance) + assert.Equal(t, relayassets.NewLinkFromJuels(1), r.LinkBalance) assert.Equal(t, utils.NewBigI(12345), r.MaxGasPriceWei) b, err := jsonapi.Marshal(r) diff --git a/core/web/presenters/eth_tx.go b/core/web/presenters/eth_tx.go index 8878733d708..2c2b5b90ff2 100644 --- a/core/web/presenters/eth_tx.go +++ b/core/web/presenters/eth_tx.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/web/presenters/eth_tx_test.go b/core/web/presenters/eth_tx_test.go index 10f4ceab006..2ed8e23c76a 100644 --- a/core/web/presenters/eth_tx_test.go +++ b/core/web/presenters/eth_tx_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index 06b9950755f..d9ec1844aea 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -7,7 +7,8 @@ import ( "github.com/lib/pq" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" 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" @@ -43,7 +44,7 @@ const ( type DirectRequestSpec struct { ContractAddress ethkey.EIP55Address `json:"contractAddress"` MinIncomingConfirmations clnull.Uint32 `json:"minIncomingConfirmations"` - MinContractPayment *assets.Link `json:"minContractPaymentLinkJuels"` + MinContractPayment *relayassets.Link `json:"minContractPaymentLinkJuels"` Requesters models.AddressCollection `json:"requesters"` Initiator string `json:"initiator"` CreatedAt time.Time `json:"createdAt"` @@ -80,7 +81,7 @@ type FluxMonitorSpec struct { DrumbeatEnabled bool `json:"drumbeatEnabled"` DrumbeatSchedule *string `json:"drumbeatSchedule"` DrumbeatRandomDelay *string `json:"drumbeatRandomDelay"` - MinPayment *assets.Link `json:"minPayment"` + MinPayment *relayassets.Link `json:"minPayment"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` EVMChainID *utils.Big `json:"evmChainID"` diff --git a/core/web/presenters/job_test.go b/core/web/presenters/job_test.go index bb79c8e953c..46c765a38b2 100644 --- a/core/web/presenters/job_test.go +++ b/core/web/presenters/job_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" 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" diff --git a/core/web/resolver/bridge_test.go b/core/web/resolver/bridge_test.go index b12186e1121..e708ac92a42 100644 --- a/core/web/resolver/bridge_test.go +++ b/core/web/resolver/bridge_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index a7f8ce56d9f..f8f417ca44b 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -9,8 +9,9 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/mock" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -95,7 +96,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) - f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(assets.NewLinkFromJuels(12), nil) + f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(relayassets.NewLinkFromJuels(12), nil) f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) f.Mocks.balM.On("GetEthBalance", address).Return(assets.NewEth(1)) f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) @@ -300,7 +301,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) - f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(assets.NewLinkFromJuels(12), gError) + f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(relayassets.NewLinkFromJuels(12), gError) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) @@ -353,7 +354,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) - f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(assets.NewLinkFromJuels(12), nil) + f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(relayassets.NewLinkFromJuels(12), nil) f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) f.Mocks.chain.On("BalanceMonitor").Return(nil) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) diff --git a/core/web/resolver/eth_transaction.go b/core/web/resolver/eth_transaction.go index 31a96727a9c..1292b6bc104 100644 --- a/core/web/resolver/eth_transaction.go +++ b/core/web/resolver/eth_transaction.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/graph-gophers/graphql-go" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" "github.com/smartcontractkit/chainlink/v2/core/web/loader" diff --git a/core/web/resolver/eth_transaction_test.go b/core/web/resolver/eth_transaction_test.go index 8a1685e4f72..a719c838e81 100644 --- a/core/web/resolver/eth_transaction_test.go +++ b/core/web/resolver/eth_transaction_test.go @@ -10,7 +10,7 @@ import ( gqlerrors "github.com/graph-gophers/graphql-go/errors" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/web/resolver/helpers.go b/core/web/resolver/helpers.go index 5c855a1c165..2dc865674e2 100644 --- a/core/web/resolver/helpers.go +++ b/core/web/resolver/helpers.go @@ -8,7 +8,7 @@ import ( "github.com/graph-gophers/graphql-go" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" ) diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index f9eee0734a3..52f914e0a84 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -13,7 +13,7 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index 8e4095e171e..fd369088bb8 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -9,9 +9,10 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" 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" @@ -96,7 +97,7 @@ func TestResolver_DirectRequestSpec(t *testing.T) { CreatedAt: f.Timestamp(), EVMChainID: utils.NewBigI(42), MinIncomingConfirmations: clnull.NewUint32(1, true), - MinContractPayment: assets.NewLinkFromJuels(1000), + MinContractPayment: relayassets.NewLinkFromJuels(1000), Requesters: models.AddressCollection{requesterAddress}, }, }, nil) @@ -163,7 +164,7 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { DrumbeatEnabled: false, IdleTimerDisabled: false, IdleTimerPeriod: time.Duration(1 * time.Hour), - MinPayment: assets.NewLinkFromJuels(1000), + MinPayment: relayassets.NewLinkFromJuels(1000), PollTimerDisabled: false, PollTimerPeriod: time.Duration(1 * time.Minute), }, @@ -232,7 +233,7 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { DrumbeatSchedule: "CRON_TZ=UTC 0 0 1 1 *", IdleTimerDisabled: true, IdleTimerPeriod: time.Duration(1 * time.Hour), - MinPayment: assets.NewLinkFromJuels(1000), + MinPayment: relayassets.NewLinkFromJuels(1000), PollTimerDisabled: true, PollTimerPeriod: time.Duration(1 * time.Minute), }, diff --git a/core/web/solana_chains_controller_test.go b/core/web/solana_chains_controller_test.go index 724d5cd2c3d..c4023f166bb 100644 --- a/core/web/solana_chains_controller_test.go +++ b/core/web/solana_chains_controller_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink-relay/pkg/types" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -84,7 +84,7 @@ Nodes = [] ChainID: ptr(validId), Chain: config.Chain{ SkipPreflight: ptr(false), - TxTimeout: utils.MustNewDuration(time.Hour), + TxTimeout: relaycfg.MustNewDuration(time.Hour), }, }) @@ -114,7 +114,7 @@ func Test_SolanaChainsController_Index(t *testing.T) { chainA := &solana.TOMLConfig{ ChainID: ptr(fmt.Sprintf("ChainlinktestA-%d", rand.Int31n(999999))), Chain: config.Chain{ - TxTimeout: utils.MustNewDuration(time.Hour), + TxTimeout: relaycfg.MustNewDuration(time.Hour), }, } chainB := &solana.TOMLConfig{ diff --git a/go.mod b/go.mod index 0a85fe7f488..40803d504ce 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/go.sum b/go.sum index 7fe91a6b123..52975e722a4 100644 --- a/go.sum +++ b/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a h1:G/pD8uI1PULRJU8Y3eLLzjqQBp9ruG9hj+wWxtyrgTo= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd h1:PRVJxNK67pQWufXuB1cxckH/xZkcQFDy8KjN9ZYqong= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd/go.mod h1:rOayi690YxLlkQy959PD8INhOAIAUi9LoN0G+J/CEf4= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 28fb2635ff3..254f2ca6ce6 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -9,7 +9,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils" - "github.com/smartcontractkit/chainlink/v2/core/assets" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" "github.com/ethereum/go-ethereum/common" @@ -868,7 +869,7 @@ func retreiveLoadTestMetrics( func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2_5.GetSubscription, subID *big.Int, coordinator contracts.VRFCoordinatorV2_5) { l.Debug(). Str("Coordinator", coordinator.Address()). - Str("Link Balance", (*assets.Link)(subscription.Balance).Link()). + Str("Link Balance", (*relayassets.Link)(subscription.Balance).Link()). Str("Native Token Balance", assets.FormatWei(subscription.NativeBalance)). Str("Subscription ID", subID.String()). Str("Subscription Owner", subscription.Owner.String()). @@ -971,11 +972,11 @@ func LogFulfillmentDetailsLinkBilling( randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, ) { l.Debug(). - Str("Consumer Balance Before Request (Link)", (*assets.Link)(wrapperConsumerJuelsBalanceBeforeRequest).Link()). - Str("Consumer Balance After Request (Link)", (*assets.Link)(wrapperConsumerJuelsBalanceAfterRequest).Link()). + Str("Consumer Balance Before Request (Link)", (*relayassets.Link)(wrapperConsumerJuelsBalanceBeforeRequest).Link()). + Str("Consumer Balance After Request (Link)", (*relayassets.Link)(wrapperConsumerJuelsBalanceAfterRequest).Link()). Bool("Fulfilment Status", consumerStatus.Fulfilled). - Str("Paid by Consumer Contract (Link)", (*assets.Link)(consumerStatus.Paid).Link()). - Str("Paid by Coordinator Sub (Link)", (*assets.Link)(randomWordsFulfilledEvent.Payment).Link()). + Str("Paid by Consumer Contract (Link)", (*relayassets.Link)(consumerStatus.Paid).Link()). + Str("Paid by Coordinator Sub (Link)", (*relayassets.Link)(randomWordsFulfilledEvent.Payment).Link()). Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). diff --git a/integration-tests/go.mod b/integration-tests/go.mod index a943e1c41a9..a450dd235ad 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,6 +22,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 + github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd github.com/smartcontractkit/chainlink-testing-framework v1.18.6 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 @@ -387,7 +388,6 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 5719c36b5a8..f882a7bb570 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2369,8 +2369,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a h1:G/pD8uI1PULRJU8Y3eLLzjqQBp9ruG9hj+wWxtyrgTo= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231113174149-046d4ddaca1a/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd h1:PRVJxNK67pQWufXuB1cxckH/xZkcQFDy8KjN9ZYqong= +github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd/go.mod h1:rOayi690YxLlkQy959PD8INhOAIAUi9LoN0G+J/CEf4= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 37047cdb667..7ddddafdd5f 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -11,8 +11,9 @@ import ( "github.com/segmentio/ksuid" + relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink/v2/core/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -167,7 +168,7 @@ func SetChainConfig( chainConfig = evmcfg.Chain{ AutoCreateKey: utils2.Ptr(true), FinalityDepth: utils2.Ptr[uint32](1), - MinContractPayment: assets.NewLinkFromJuels(0), + MinContractPayment: relayassets.NewLinkFromJuels(0), } } cfg.EVM = evmcfg.EVMConfigs{ @@ -193,7 +194,7 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { Chain: evmcfg.Chain{ AutoCreateKey: utils2.Ptr(true), FinalityDepth: utils2.Ptr[uint32](50), - MinContractPayment: assets.NewLinkFromJuels(0), + MinContractPayment: relayassets.NewLinkFromJuels(0), LogPollInterval: models.MustNewDuration(1 * time.Second), HeadTracker: evmcfg.HeadTracker{ HistoryDepth: utils2.Ptr(uint32(100)), diff --git a/tools/flakeytests/runner_test.go b/tools/flakeytests/runner_test.go index c4509ff2cc9..31f300dcbee 100644 --- a/tools/flakeytests/runner_test.go +++ b/tools/flakeytests/runner_test.go @@ -25,7 +25,7 @@ func newMockReporter() *mockReporter { } func TestParser(t *testing.T) { - output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} + output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} ` r := strings.NewReader(output) @@ -33,14 +33,14 @@ func TestParser(t *testing.T) { require.NoError(t, err) assert.Len(t, ts, 1) - assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/assets"], 1) - assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"], 1) + assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"], 1) + assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"], 1) } func TestParser_SkipsNonJSON(t *testing.T) { output := `Failed tests and panics: ------- -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} ` r := strings.NewReader(output) @@ -48,13 +48,13 @@ func TestParser_SkipsNonJSON(t *testing.T) { require.NoError(t, err) assert.Len(t, ts, 1) - assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/assets"], 1) - assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"], 1) + assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"], 1) + assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"], 1) } func TestParser_PanicDueToLogging(t *testing.T) { output := ` -{"Time":"2023-09-07T16:01:40.649849+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_LinkScanValue","Output":"panic: foo\n"} +{"Time":"2023-09-07T16:01:40.649849+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_LinkScanValue","Output":"panic: foo\n"} ` r := strings.NewReader(output) @@ -62,24 +62,24 @@ func TestParser_PanicDueToLogging(t *testing.T) { require.NoError(t, err) assert.Len(t, ts, 1) - assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/assets"], 1) - assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestAssets_LinkScanValue"], 1) + assert.Len(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"], 1) + assert.Equal(t, ts["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestAssets_LinkScanValue"], 1) } func TestParser_SuccessfulOutput(t *testing.T) { output := ` -{"Time":"2023-09-07T16:22:52.556853+01:00","Action":"start","Package":"github.com/smartcontractkit/chainlink/v2/core/assets"} -{"Time":"2023-09-07T16:22:52.762353+01:00","Action":"run","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString"} -{"Time":"2023-09-07T16:22:52.762456+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString","Output":"=== RUN TestAssets_NewLinkAndString\n"} -{"Time":"2023-09-07T16:22:52.76249+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString","Output":"=== PAUSE TestAssets_NewLinkAndString\n"} -{"Time":"2023-09-07T16:22:52.7625+01:00","Action":"pause","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString"} -{"Time":"2023-09-07T16:22:52.762511+01:00","Action":"cont","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString"} -{"Time":"2023-09-07T16:22:52.762528+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString","Output":"=== CONT TestAssets_NewLinkAndString\n"} -{"Time":"2023-09-07T16:22:52.762546+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString","Output":"--- PASS: TestAssets_NewLinkAndString (0.00s)\n"} -{"Time":"2023-09-07T16:22:52.762557+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestAssets_NewLinkAndString","Elapsed":0} -{"Time":"2023-09-07T16:22:52.762566+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Output":"PASS\n"} -{"Time":"2023-09-07T16:22:52.762955+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Output":"ok \tgithub.com/smartcontractkit/chainlink/v2/core/assets\t0.206s\n"} -{"Time":"2023-09-07T16:22:52.765598+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Elapsed":0.209} +{"Time":"2023-09-07T16:22:52.556853+01:00","Action":"start","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"} +{"Time":"2023-09-07T16:22:52.762353+01:00","Action":"run","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString"} +{"Time":"2023-09-07T16:22:52.762456+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString","Output":"=== RUN TestAssets_NewLinkAndString\n"} +{"Time":"2023-09-07T16:22:52.76249+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString","Output":"=== PAUSE TestAssets_NewLinkAndString\n"} +{"Time":"2023-09-07T16:22:52.7625+01:00","Action":"pause","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString"} +{"Time":"2023-09-07T16:22:52.762511+01:00","Action":"cont","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString"} +{"Time":"2023-09-07T16:22:52.762528+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString","Output":"=== CONT TestAssets_NewLinkAndString\n"} +{"Time":"2023-09-07T16:22:52.762546+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString","Output":"--- PASS: TestAssets_NewLinkAndString (0.00s)\n"} +{"Time":"2023-09-07T16:22:52.762557+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestAssets_NewLinkAndString","Elapsed":0} +{"Time":"2023-09-07T16:22:52.762566+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Output":"PASS\n"} +{"Time":"2023-09-07T16:22:52.762955+01:00","Action":"output","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Output":"ok \tgithub.com/smartcontractkit/chainlink/v2/core/assets\t0.206s\n"} +{"Time":"2023-09-07T16:22:52.765598+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Elapsed":0.209} ` r := strings.NewReader(output) @@ -95,9 +95,9 @@ func (t testAdapter) test(pkg string, tests []string, out io.Writer) error { } func TestRunner_WithFlake(t *testing.T) { - initialOutput := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}` + initialOutput := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}` outputs := []string{ - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, ``, } m := newMockReporter() @@ -120,18 +120,18 @@ func TestRunner_WithFlake(t *testing.T) { err := r.Run() require.NoError(t, err) assert.Len(t, m.entries, 1) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"] + _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } func TestRunner_WithFailedPackage(t *testing.T) { initialOutput := ` -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Elapsed":0} ` outputs := []string{` -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Elapsed":0} `, ``, } @@ -155,16 +155,16 @@ func TestRunner_WithFailedPackage(t *testing.T) { err := r.Run() require.NoError(t, err) assert.Len(t, m.entries, 1) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"] + _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } func TestRunner_AllFailures(t *testing.T) { - output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}` + output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}` rerunOutput := ` -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} ` m := newMockReporter() r := &Runner{ @@ -184,11 +184,11 @@ func TestRunner_AllFailures(t *testing.T) { } func TestRunner_RerunSuccessful(t *testing.T) { - output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}` + output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}` rerunOutputs := []string{ - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, } m := newMockReporter() i := 0 @@ -206,7 +206,7 @@ func TestRunner_RerunSuccessful(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"] + _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } @@ -233,11 +233,11 @@ func TestRunner_RootLevelTest(t *testing.T) { } func TestRunner_RerunFailsWithNonzeroExitCode(t *testing.T) { - output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}` + output := `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}` rerunOutputs := []string{ - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, } m := newMockReporter() i := 0 @@ -255,14 +255,14 @@ func TestRunner_RerunFailsWithNonzeroExitCode(t *testing.T) { err := r.Run() require.NoError(t, err) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"] + _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } func TestRunner_RerunWithNonZeroExitCodeDoesntStopCommand(t *testing.T) { outputs := []io.Reader{ strings.NewReader(` -{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0} +{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0} `), strings.NewReader(` {"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2","Test":"TestMaybeReservedLinkV2","Elapsed":0} @@ -270,8 +270,8 @@ func TestRunner_RerunWithNonZeroExitCodeDoesntStopCommand(t *testing.T) { } rerunOutputs := []string{ - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, - `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, + `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"pass","Package":"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets","Test":"TestLink","Elapsed":0}`, `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2","Test":"TestMaybeReservedLinkV2","Elapsed":0}`, `{"Time":"2023-09-07T15:39:46.378315+01:00","Action":"fail","Package":"github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2","Test":"TestMaybeReservedLinkV2","Elapsed":0}`, } @@ -295,7 +295,7 @@ func TestRunner_RerunWithNonZeroExitCodeDoesntStopCommand(t *testing.T) { calls := index assert.Equal(t, 4, calls) - _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/assets"]["TestLink"] + _, ok := m.entries["github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"]["TestLink"] assert.True(t, ok) } From 6fd7be8e9e565ee3358d125f88c6f67017248e57 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 16 Nov 2023 10:35:07 -0500 Subject: [PATCH 161/271] [TT-688] Batch Keeper Benchmark Read Requests (#11294) * Batch Keeper Benchmark Read Requests * Actually add number --- .../testsetups/keeper_benchmark.go | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index bb6c582c137..d9b389a9650 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -293,32 +293,54 @@ func (k *KeeperBenchmarkTest) Run() { require.NoError(k.t, err, "Error waiting for keeper subscriptions") // Collect logs for each registry to calculate test metrics - registryLogs := make([][]types.Log, len(k.keeperRegistries)) + // This test generates a LOT of logs, and we need to break up our reads, or risk getting rate-limited by the node + var ( + endBlock = big.NewInt(0).Add(k.startingBlock, big.NewInt(u.BlockRange)) + registryLogs = make([][]types.Log, len(k.keeperRegistries)) + blockBatchSize int64 = 100 + ) for rIndex := range k.keeperRegistries { + // Variables for the full registry var ( - logs []types.Log - timeout = 5 * time.Second - addr = k.keeperRegistries[rIndex].Address() - filterQuery = geth.FilterQuery{ + logs []types.Log + timeout = 5 * time.Second + addr = k.keeperRegistries[rIndex].Address() + queryStartBlock = big.NewInt(0).Set(k.startingBlock) + ) + + // Gather logs from the registry in 100 block chunks to avoid read limits + for queryStartBlock.Cmp(endBlock) < 0 { + filterQuery := geth.FilterQuery{ Addresses: []common.Address{common.HexToAddress(addr)}, - FromBlock: k.startingBlock, + FromBlock: queryStartBlock, + ToBlock: big.NewInt(0).Add(queryStartBlock, big.NewInt(blockBatchSize)), } + + // This RPC call can possibly time out or otherwise die. Failure is not an option, keep retrying to get our stats. err = fmt.Errorf("initial error") // to ensure our for loop runs at least once - ) - for err != nil { // This RPC call can possibly time out or otherwise die. Failure is not an option, keep retrying to get our stats. - ctx, cancel := context.WithTimeout(utils.TestContext(k.t), timeout) - logs, err = k.chainClient.FilterLogs(ctx, filterQuery) - cancel() - if err != nil { - k.log.Error().Err(err). - Interface("Filter Query", filterQuery). - Str("Timeout", timeout.String()). - Msg("Error getting logs from chain, trying again") - } else { - k.log.Info().Int("Log Count", len(logs)).Str("Registry Address", addr).Msg("Collected logs") + for err != nil { + ctx, cancel := context.WithTimeout(utils.TestContext(k.t), timeout) + logs, err = k.chainClient.FilterLogs(ctx, filterQuery) + cancel() + if err != nil { + k.log.Error(). + Err(err). + Interface("Filter Query", filterQuery). + Str("Timeout", timeout.String()). + Msg("Error getting logs from chain, trying again") + timeout = time.Duration(math.Min(float64(timeout)*2, float64(2*time.Minute))) + continue + } + k.log.Info(). + Uint64("From Block", queryStartBlock.Uint64()). + Uint64("To Block", filterQuery.ToBlock.Uint64()). + Int("Log Count", len(logs)). + Str("Registry Address", addr). + Msg("Collected logs") + queryStartBlock.Add(queryStartBlock, big.NewInt(blockBatchSize)) + registryLogs[rIndex] = append(registryLogs[rIndex], logs...) } } - registryLogs[rIndex] = logs } // Count reverts and stale upkeeps From 01fbf8e445b2ae4db616d0fc8e10fa4d44895e0b Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Thu, 16 Nov 2023 10:34:08 -0600 Subject: [PATCH 162/271] Update loading next sequence map to avoid startup failure (#11307) * Updated loading next sequence map to avoid startup failure * Moved logic to populate next sequence map during runtime * Added changelog * Addressed feedback * Addressed feedback --- common/txmgr/broadcaster.go | 77 ++++++---- core/chains/evm/txmgr/broadcaster_test.go | 175 +++++++++++++++------- docs/CHANGELOG.md | 6 + 3 files changed, 176 insertions(+), 82 deletions(-) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 00522abf229..1e3b2fa0a95 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "slices" "sync" "time" @@ -228,10 +229,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) star eb.sequenceLock.Lock() defer eb.sequenceLock.Unlock() - eb.nextSequenceMap, err = eb.loadNextSequenceMap(eb.enabledAddresses) - if err != nil { - return errors.Wrap(err, "Broadcaster: failed to load next sequence map") - } + eb.nextSequenceMap = eb.loadNextSequenceMap(eb.enabledAddresses) eb.isStarted = true return nil @@ -287,30 +285,38 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Trig } // Load the next sequence map using the tx table or on-chain (if not found in tx table) -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) loadNextSequenceMap(addresses []ADDR) (map[ADDR]SEQ, error) { +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) loadNextSequenceMap(addresses []ADDR) map[ADDR]SEQ { ctx, cancel := eb.chStop.NewCtx() defer cancel() nextSequenceMap := make(map[ADDR]SEQ) for _, address := range addresses { - // Get the highest sequence from the tx table - // Will need to be incremented since this sequence is already used - seq, err := eb.txStore.FindLatestSequence(ctx, address, eb.chainID) - if err != nil { - // Look for nonce on-chain if no tx found for address in TxStore or if error occurred - // Returns the nonce that should be used for the next transaction so no need to increment - seq, err = eb.client.PendingSequenceAt(ctx, address) - if err != nil { - return nil, errors.New("failed to retrieve next sequence from on-chain causing failure to load next sequence map on broadcaster startup") - } - + seq, err := eb.getSequenceForAddr(ctx, address) + if err == nil { nextSequenceMap[address] = seq - } else { - nextSequenceMap[address] = eb.generateNextSequence(seq) } } - return nextSequenceMap, nil + return nextSequenceMap +} + +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) getSequenceForAddr(ctx context.Context, address ADDR) (seq SEQ, err error) { + // Get the highest sequence from the tx table + // Will need to be incremented since this sequence is already used + seq, err = eb.txStore.FindLatestSequence(ctx, address, eb.chainID) + if err == nil { + seq = eb.generateNextSequence(seq) + return seq, nil + } + // Look for nonce on-chain if no tx found for address in TxStore or if error occurred + // Returns the nonce that should be used for the next transaction so no need to increment + seq, err = eb.client.PendingSequenceAt(ctx, address) + if err == nil { + return seq, nil + } + eb.logger.Criticalw("failed to retrieve next sequence from on-chain for address: ", "address", address.String()) + return seq, err + } func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) newSequenceSyncBackoff() backoff.Backoff { @@ -393,7 +399,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) moni // syncSequence tries to sync the key sequence, retrying indefinitely until success or stop signal is sent func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) SyncSequence(ctx context.Context, addr ADDR) { sequenceSyncRetryBackoff := eb.newSequenceSyncBackoff() - localSequence, err := eb.GetNextSequence(addr) + localSequence, err := eb.GetNextSequence(ctx, addr) // Address not found in map so skip sync if err != nil { eb.logger.Criticalw("Failed to retrieve local next sequence for address", "address", addr.String(), "err", err) @@ -607,7 +613,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand observeTimeUntilBroadcast(eb.chainID, etx.CreatedAt, time.Now()) // Check if from_address exists in map to ensure it is valid before broadcasting var sequence SEQ - sequence, err = eb.GetNextSequence(etx.FromAddress) + sequence, err = eb.GetNextSequence(ctx, etx.FromAddress) if err != nil { return err, true } @@ -665,7 +671,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // Check if from_address exists in map to ensure it is valid before broadcasting var sequence SEQ - sequence, err = eb.GetNextSequence(etx.FromAddress) + sequence, err = eb.GetNextSequence(ctx, etx.FromAddress) if err != nil { return err, true } @@ -702,7 +708,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) next return nil, errors.Wrap(err, "findNextUnstartedTransactionFromAddress failed") } - sequence, err := eb.GetNextSequence(etx.FromAddress) + sequence, err := eb.GetNextSequence(ctx, etx.FromAddress) if err != nil { return nil, err } @@ -792,15 +798,32 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) save } // Used to get the next usable sequence for a transaction -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetNextSequence(address ADDR) (seq SEQ, err error) { +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetNextSequence(ctx context.Context, address ADDR) (seq SEQ, err error) { eb.sequenceLock.Lock() defer eb.sequenceLock.Unlock() // Get next sequence from map seq, exists := eb.nextSequenceMap[address] - if !exists { - return seq, errors.New(fmt.Sprint("address not found in next sequence map: ", address)) + if exists { + return seq, nil + } + + eb.logger.Infow("address not found in local next sequence map. Attempting to search and populate sequence.", "address", address.String()) + // Check if address is in the enabled address list + if !slices.Contains(eb.enabledAddresses, address) { + return seq, fmt.Errorf("address disabled: %s", address) } - return seq, nil + + // Try to retrieve next sequence from tx table or on-chain to load the map + // A scenario could exist where loading the map during startup failed (e.g. All configured RPC's are unreachable at start) + // The expectation is that the node does not fail startup so sequences need to be loaded during runtime + foundSeq, err := eb.getSequenceForAddr(ctx, address) + if err != nil { + return seq, fmt.Errorf("failed to find next sequence for address: %s", address) + } + + // Set sequence in map + eb.nextSequenceMap[address] = foundSeq + return foundSeq, nil } // Used to increment the sequence in the mapping to have the next usable one available for the next transaction diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 48b68f9b55c..7967478e624 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -130,6 +130,38 @@ func TestEthBroadcaster_Lifecycle(t *testing.T) { require.NoError(t, eb.XXXTestCloseInternal()) } +// Failure to load next sequnce map should not fail Broadcaster startup +func TestEthBroadcaster_LoadNextSequenceMapFailure_StartupSuccess(t *testing.T) { + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewTestGeneralConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + evmcfg := evmtest.NewChainScopedConfig(t, cfg) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + estimator := gasmocks.NewEvmFeeEstimator(t) + txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), evmcfg.EVM().GasEstimator(), ethKeyStore, estimator) + ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), errors.New("Getting on-chain nonce failed")) + eb := txmgr.NewEvmBroadcaster( + txStore, + txmgr.NewEvmTxmClient(ethClient), + txmgr.NewEvmTxmConfig(evmcfg.EVM()), + txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), + evmcfg.EVM().Transactions(), + evmcfg.Database().Listener(), + ethKeyStore, + txBuilder, + nil, + logger.TestLogger(t), + &testCheckerFactory{}, + false, + ) + + // Instance starts without error even if loading next sequence map fails + err := eb.Start(testutils.Context(t)) + require.NoError(t, err) +} + func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewTestGeneralConfig(t) @@ -946,7 +978,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { } func getLocalNextNonce(t *testing.T, eb *txmgr.Broadcaster, fromAddress gethCommon.Address) uint64 { - n, err := eb.GetNextSequence(fromAddress) + n, err := eb.GetNextSequence(testutils.Context(t), fromAddress) require.NoError(t, err) require.NotNil(t, n) return uint64(n) @@ -972,6 +1004,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) + ctx := testutils.Context(t) require.NoError(t, utils.JustError(db.Exec(`SET CONSTRAINTS pipeline_runs_pipeline_spec_id_fkey DEFERRED`))) @@ -983,7 +1016,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Successful, errors.New("replacement transaction underpriced")).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1019,7 +1052,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1036,7 +1069,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // Check that the key had its nonce reset var nonce evmtypes.Nonce - nonce, err = eb.GetNextSequence(fromAddress) + nonce, err = eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) // Saved NextNonce must be the same as before because this transaction // was not accepted by the eth node and never can be @@ -1070,7 +1103,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), "something exploded in the callback") assert.True(t, retryable) @@ -1092,7 +1125,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() { - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) } @@ -1105,7 +1138,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce = getLocalNextNonce(t, eb, fromAddress) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() eb2 := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(evmcfg.EVM()), txmgr.NewEvmTxmFeeConfig(evmcfg.EVM().GasEstimator()), evmcfg.EVM().Transactions(), evmcfg.Database().Listener(), ethKeyStore, txBuilder, nil, lggr, &testCheckerFactory{}, false) - retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) }) @@ -1127,7 +1160,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // another node even if the primary one returns "exceeds the configured // cap" - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) assert.Contains(t, err.Error(), "tx fee (1.10 ether) exceeds the configured cap (1.00 ether)") assert.Contains(t, err.Error(), "error while sending transaction") @@ -1147,7 +1180,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // Check that the key had its nonce reset var nonce evmtypes.Nonce - nonce, err = eb.GetNextSequence(fromAddress) + nonce, err = eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) // Saved NextNonce must be the same as before because this transaction // was not accepted by the eth node and never can be @@ -1156,7 +1189,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // On the second try, the tx has been accepted into the mempool ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce+1), nil).Once() - retryable, err = eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1184,7 +1217,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), retryableErrorExample) assert.True(t, retryable) @@ -1207,7 +1240,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Successful, nil).Once() - retryable, err = eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1235,7 +1268,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), errors.New("pending nonce fetch failed")).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), retryableErrorExample) require.Contains(t, err.Error(), "pending nonce fetch failed") @@ -1259,7 +1292,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Successful, nil).Once() - retryable, err = eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err = eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1288,7 +1321,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce+1), nil).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.NoError(t, err) assert.False(t, retryable) @@ -1330,7 +1363,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Successful, nil).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.NoError(t, err) assert.False(t, retryable) @@ -1366,7 +1399,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Retryable, failedToReachNodeError).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) assert.Contains(t, err.Error(), "context deadline exceeded") assert.True(t, retryable) @@ -1397,7 +1430,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Successful, errors.New(temporarilyUnderpricedError)).Once() // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -1437,7 +1470,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Do the thing - retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), "bumped fee price of 20 gwei is equal to original fee price of 20 gwei. ACTION REQUIRED: This is a configuration error, you must increase either FeeEstimator.BumpPercent or FeeEstimator.BumpMin") assert.True(t, retryable) @@ -1454,7 +1487,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.InsufficientFunds, errors.New(insufficientEthError)).Once() - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) assert.Contains(t, err.Error(), "insufficient funds for transfer") assert.True(t, retryable) @@ -1484,7 +1517,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Retryable, errors.New(nonceGapError)).Once() - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) assert.Contains(t, err.Error(), nonceGapError) assert.True(t, retryable) @@ -1529,7 +1562,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { }), fromAddress).Return(commonclient.Underpriced, errors.New(underpricedError)).Once() // Check gas tip cap verification - retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), "bumped gas tip cap of 1 wei is less than or equal to original gas tip cap of 1 wei") assert.True(t, retryable) @@ -1553,7 +1586,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(localNextNonce, nil).Once() eb2 := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg2, &testCheckerFactory{}, false) - retryable, err := eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb2.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), "specified gas tip cap of 0 is below min configured gas tip of 1 wei for key") assert.True(t, retryable) @@ -1580,7 +1613,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { return tx.Nonce() == localNextNonce && tx.GasTipCap().Cmp(big.NewInt(0).Add(gasTipCapDefault.ToInt(), big.NewInt(0).Mul(evmcfg2.EVM().GasEstimator().BumpMin().ToInt(), big.NewInt(2)))) == 0 }), fromAddress).Return(commonclient.Successful, nil).Once() - retryable, err = eb2.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err = eb2.ProcessUnstartedTxs(ctx, fromAddress) require.NoError(t, err) assert.False(t, retryable) @@ -1612,7 +1645,8 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_KeystoreErrors(t *testing.T) { kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, kst, evmcfg, &testCheckerFactory{}, false) - _, err := eb.GetNextSequence(fromAddress) + ctx := testutils.Context(t) + _, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) t.Run("tx signing fails", func(t *testing.T) { @@ -1626,7 +1660,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_KeystoreErrors(t *testing.T) { })).Return(&tx, errors.New("could not sign transaction")) // Do the thing - retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) + retryable, err := eb.ProcessUnstartedTxs(ctx, fromAddress) require.Error(t, err) require.Contains(t, err.Error(), "could not sign transaction") assert.True(t, retryable) @@ -1640,7 +1674,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_KeystoreErrors(t *testing.T) { // Check that the key did not have its nonce incremented var nonce types.Nonce - nonce, err = eb.GetNextSequence(fromAddress) + nonce, err = eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) require.Equal(t, int64(localNonce), int64(nonce)) }) @@ -1678,12 +1712,13 @@ func TestEthBroadcaster_IncrementNextNonce(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, kst, evmcfg, &testCheckerFactory{}, false) - nonce, err := eb.GetNextSequence(fromAddress) + ctx := testutils.Context(t) + nonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) eb.IncrementNextSequence(fromAddress, nonce) // Nonce bumped to 1 - nonce, err = eb.GetNextSequence(fromAddress) + nonce, err = eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) require.Equal(t, int64(1), int64(nonce)) } @@ -1737,7 +1772,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Once() ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() eb := txmgr.NewEvmBroadcaster(txStore, txmgr.NewEvmTxmClient(ethClient), evmTxmCfg, txmgr.NewEvmTxmFeeConfig(ge), evmcfg.EVM().Transactions(), cfg.Database().Listener(), kst, txBuilder, nil, lggr, checkerFactory, false) - err := eb.Start(testutils.Context(t)) + err := eb.Start(ctx) assert.NoError(t, err) defer func() { assert.NoError(t, eb.Close()) }() @@ -1763,12 +1798,12 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { testutils.WaitForLogMessage(t, observed, "Fast-forward sequence") // Check nextSequenceMap to make sure it has correct nonce assigned - nonce, err := eb.GetNextSequence(fromAddress) + nonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) - assert.Equal(t, strconv.FormatUint(ethNodeNonce, 10), nonce.String()) + require.Equal(t, strconv.FormatUint(ethNodeNonce, 10), nonce.String()) // The disabled key did not get updated - _, err = eb.GetNextSequence(disabledAddress) + _, err = eb.GetNextSequence(ctx, disabledAddress) require.Error(t, err) }) @@ -1797,19 +1832,19 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { testutils.WaitForLogMessage(t, observed, "Fast-forward sequence") // Check keyState to make sure it has correct nonce assigned - nonce, err := eb.GetNextSequence(fromAddress) + nonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) assert.Equal(t, int64(ethNodeNonce), int64(nonce)) // The disabled key did not get updated - _, err = eb.GetNextSequence(disabledAddress) + _, err = eb.GetNextSequence(ctx, disabledAddress) require.Error(t, err) }) - } func Test_LoadSequenceMap(t *testing.T) { t.Parallel() + ctx := testutils.Context(t) t.Run("set next nonce using entries from tx table", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewTestGeneralConfig(t) @@ -1824,9 +1859,9 @@ func Test_LoadSequenceMap(t *testing.T) { cltest.MustInsertUnconfirmedEthTx(t, txStore, int64(1), fromAddress) eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) - nonce, err := eb.GetNextSequence(fromAddress) + nonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) - assert.Equal(t, int64(2), int64(nonce)) + require.Equal(t, int64(2), int64(nonce)) }) t.Run("set next nonce using client when not found in tx table", func(t *testing.T) { @@ -1842,9 +1877,9 @@ func Test_LoadSequenceMap(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(10), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) - nonce, err := eb.GetNextSequence(fromAddress) + nonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) - assert.Equal(t, int64(10), int64(nonce)) + require.Equal(t, int64(10), int64(nonce)) }) } @@ -1863,25 +1898,53 @@ func Test_NextNonce(t *testing.T) { _, addr1 := cltest.MustInsertRandomKey(t, ks) ethClient.On("PendingNonceAt", mock.Anything, addr1).Return(uint64(randNonce), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) - + ctx := testutils.Context(t) cltest.MustInsertRandomKey(t, ks, *utils.NewBig(testutils.FixtureChainID)) - nonce, err := eb.GetNextSequence(addr1) + nonce, err := eb.GetNextSequence(ctx, addr1) require.NoError(t, err) - assert.Equal(t, randNonce, int64(nonce)) + require.Equal(t, randNonce, int64(nonce)) randAddr1 := utils.RandomAddress() - _, err = eb.GetNextSequence(randAddr1) + _, err = eb.GetNextSequence(ctx, randAddr1) require.Error(t, err) - assert.Contains(t, err.Error(), fmt.Sprintf("address not found in next sequence map: %s", randAddr1.Hex())) + require.Contains(t, err.Error(), fmt.Sprintf("address disabled: %s", randAddr1.Hex())) randAddr2 := utils.RandomAddress() - _, err = eb.GetNextSequence(randAddr2) + _, err = eb.GetNextSequence(ctx, randAddr2) require.Error(t, err) - assert.Contains(t, err.Error(), fmt.Sprintf("address not found in next sequence map: %s", randAddr2.Hex())) + require.Contains(t, err.Error(), fmt.Sprintf("address disabled: %s", randAddr2.Hex())) } +func Test_SetNonceAfterInit(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewTestGeneralConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ks := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + evmcfg := evmtest.NewChainScopedConfig(t, cfg) + checkerFactory := &txmgr.CheckerFactory{Client: ethClient} + randNonce := testutils.NewRandomPositiveInt64() + _, addr1 := cltest.MustInsertRandomKey(t, ks) + ethClient.On("PendingNonceAt", mock.Anything, addr1).Return(uint64(0), errors.New("failed to retrieve nonce at startup")).Once() + ethClient.On("PendingNonceAt", mock.Anything, addr1).Return(uint64(randNonce), nil).Once() + eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) + + ctx := testutils.Context(t) + nonce, err := eb.GetNextSequence(ctx, addr1) + require.NoError(t, err) + require.Equal(t, randNonce, int64(nonce)) + + // Test that the new nonce is set in the map and does not need a client call to retrieve on subsequent calls + nonce, err = eb.GetNextSequence(ctx, addr1) + require.NoError(t, err) + require.Equal(t, randNonce, int64(nonce)) +} + func Test_IncrementNextNonce(t *testing.T) { t.Parallel() @@ -1898,26 +1961,27 @@ func Test_IncrementNextNonce(t *testing.T) { ethClient.On("PendingNonceAt", mock.Anything, addr1).Return(uint64(randNonce), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) - nonce, err := eb.GetNextSequence(addr1) + ctx := testutils.Context(t) + nonce, err := eb.GetNextSequence(ctx, addr1) require.NoError(t, err) eb.IncrementNextSequence(addr1, nonce) - nonce, err = eb.GetNextSequence(addr1) + nonce, err = eb.GetNextSequence(ctx, addr1) require.NoError(t, err) assert.Equal(t, randNonce+1, int64(nonce)) eb.IncrementNextSequence(addr1, nonce) - nonce, err = eb.GetNextSequence(addr1) + nonce, err = eb.GetNextSequence(ctx, addr1) require.NoError(t, err) assert.Equal(t, randNonce+2, int64(nonce)) randAddr1 := utils.RandomAddress() - _, err = eb.GetNextSequence(randAddr1) + _, err = eb.GetNextSequence(ctx, randAddr1) require.Error(t, err) - assert.Contains(t, err.Error(), fmt.Sprintf("address not found in next sequence map: %s", randAddr1.Hex())) + assert.Contains(t, err.Error(), fmt.Sprintf("address disabled: %s", randAddr1.Hex())) // verify it didnt get changed by any erroring calls - nonce, err = eb.GetNextSequence(addr1) + nonce, err = eb.GetNextSequence(ctx, addr1) require.NoError(t, err) assert.Equal(t, randNonce+2, int64(nonce)) } @@ -1936,14 +2000,15 @@ func Test_SetNextNonce(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKey(t, ks) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(0), nil).Once() eb := NewTestEthBroadcaster(t, txStore, ethClient, ks, evmcfg, checkerFactory, false) + ctx := testutils.Context(t) t.Run("update next nonce", func(t *testing.T) { - nonce, err := eb.GetNextSequence(fromAddress) + nonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) assert.Equal(t, int64(0), int64(nonce)) eb.SetNextSequence(fromAddress, evmtypes.Nonce(24)) - newNextNonce, err := eb.GetNextSequence(fromAddress) + newNextNonce, err := eb.GetNextSequence(ctx, fromAddress) require.NoError(t, err) assert.Equal(t, int64(24), int64(newNextNonce)) }) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a10f9dd1c6d..1351c421340 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -36,6 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ... +## 2.7.1 - UNRELEASED + +### Fixed + +- Fixed a bug that causes the node to shutdown if all configured RPC's are unreachable during startup. + ## 2.7.0 - UNRELEASED ### Added From 101d5deab2db52a09744ad12c7693372a9fd8bac Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 16 Nov 2023 12:00:54 -0600 Subject: [PATCH 163/271] replace all context.TODO()s (#11313) --- common/txmgr/confirmer.go | 8 ++++---- core/chains/evm/txmgr/confirmer_test.go | 10 +++++----- core/cmd/shell_local.go | 7 ++++--- core/internal/cltest/cltest.go | 1 + .../chainlink/relayer_chain_interoperators.go | 2 +- core/services/chainlink/relayer_factory.go | 2 +- .../ocr2/plugins/dkg/persistence/db_test.go | 14 +++++++------- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index afb2b3003a1..4d3626ffacd 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -1025,7 +1025,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) mar // This operates completely orthogonal to the normal Confirmer and can result in untracked attempts! // Only for emergency usage. // This is in case of some unforeseen scenario where the node is refusing to release the lock. KISS. -func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ForceRebroadcast(seqs []SEQ, fee FEE, address ADDR, overrideGasLimit uint32) error { +func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) ForceRebroadcast(ctx context.Context, seqs []SEQ, fee FEE, address ADDR, overrideGasLimit uint32) error { if len(seqs) == 0 { ec.lggr.Infof("ForceRebroadcast: No sequences provided. Skipping") return nil @@ -1034,13 +1034,13 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) For for _, seq := range seqs { - etx, err := ec.txStore.FindTxWithSequence(context.TODO(), address, seq) + etx, err := ec.txStore.FindTxWithSequence(ctx, address, seq) if err != nil { return errors.Wrap(err, "ForceRebroadcast failed") } if etx == nil { ec.lggr.Debugf("ForceRebroadcast: no tx found with sequence %s, will rebroadcast empty transaction", seq) - hashStr, err := ec.sendEmptyTransaction(context.TODO(), address, seq, overrideGasLimit, fee) + hashStr, err := ec.sendEmptyTransaction(ctx, address, seq, overrideGasLimit, fee) if err != nil { ec.lggr.Errorw("ForceRebroadcast: failed to send empty transaction", "sequence", seq, "err", err) continue @@ -1058,7 +1058,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) For } attempt.Tx = *etx // for logging ec.lggr.Debugw("Sending transaction", "txAttemptID", attempt.ID, "txHash", attempt.Hash, "err", err, "meta", etx.Meta, "feeLimit", etx.FeeLimit, "attempt", attempt) - if errCode, err := ec.client.SendTransactionReturnCode(context.TODO(), *etx, attempt, ec.lggr); errCode != client.Successful && err != nil { + if errCode, err := ec.client.SendTransactionReturnCode(ctx, *etx, attempt, ec.lggr); errCode != client.Successful && err != nil { ec.lggr.Errorw(fmt.Sprintf("ForceRebroadcast: failed to rebroadcast tx %v with sequence %v and gas limit %v: %s", etx.ID, *etx.Sequence, etx.FeeLimit, err.Error()), "err", err, "fee", attempt.TxFee) continue } diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 31464f8f521..e17e7993cf9 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -2819,7 +2819,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { tx.To().String() == etx1.ToAddress.String() }), mock.Anything).Return(commonclient.Successful, nil).Once() - require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{1}, gasPriceWei, fromAddress, overrideGasLimit)) + require.NoError(t, ec.ForceRebroadcast(testutils.Context(t), []evmtypes.Nonce{1}, gasPriceWei, fromAddress, overrideGasLimit)) }) t.Run("uses default gas limit if overrideGasLimit is 0", func(t *testing.T) { @@ -2834,7 +2834,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { tx.To().String() == etx1.ToAddress.String() }), mock.Anything).Return(commonclient.Successful, nil).Once() - require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{(1)}, gasPriceWei, fromAddress, 0)) + require.NoError(t, ec.ForceRebroadcast(testutils.Context(t), []evmtypes.Nonce{(1)}, gasPriceWei, fromAddress, 0)) }) t.Run("rebroadcasts several eth_txes in nonce range", func(t *testing.T) { @@ -2848,7 +2848,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { return tx.Nonce() == uint64(*etx2.Sequence) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && tx.Gas() == uint64(overrideGasLimit) }), mock.Anything).Return(commonclient.Successful, nil).Once() - require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{(1), (2)}, gasPriceWei, fromAddress, overrideGasLimit)) + require.NoError(t, ec.ForceRebroadcast(testutils.Context(t), []evmtypes.Nonce{(1), (2)}, gasPriceWei, fromAddress, overrideGasLimit)) }) t.Run("broadcasts zero transactions if eth_tx doesn't exist for that nonce", func(t *testing.T) { @@ -2874,7 +2874,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { } nonces := []evmtypes.Nonce{(1), (2), (3), (4), (5)} - require.NoError(t, ec.ForceRebroadcast(nonces, gasPriceWei, fromAddress, overrideGasLimit)) + require.NoError(t, ec.ForceRebroadcast(testutils.Context(t), nonces, gasPriceWei, fromAddress, overrideGasLimit)) }) t.Run("zero transactions use default gas limit if override wasn't specified", func(t *testing.T) { @@ -2885,7 +2885,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { return tx.Nonce() == uint64(0) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && uint32(tx.Gas()) == config.EVM().GasEstimator().LimitDefault() }), mock.Anything).Return(commonclient.Successful, nil).Once() - require.NoError(t, ec.ForceRebroadcast([]evmtypes.Nonce{(0)}, gasPriceWei, fromAddress, 0)) + require.NoError(t, ec.ForceRebroadcast(testutils.Context(t), []evmtypes.Nonce{(0)}, gasPriceWei, fromAddress, 0)) }) } diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index f7e17ef7fc2..cbd0feccbf1 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -558,6 +558,7 @@ func checkFilePermissions(lggr logger.Logger, rootDir string) error { // RebroadcastTransactions run locally to force manual rebroadcasting of // transactions in a given nonce range. func (s *Shell) RebroadcastTransactions(c *cli.Context) (err error) { + ctx := s.ctx() beginningNonce := c.Int64("beginningNonce") endingNonce := c.Int64("endingNonce") gasPriceWei := c.Uint64("gasPriceWei") @@ -587,7 +588,7 @@ func (s *Shell) RebroadcastTransactions(c *cli.Context) (err error) { } defer lggr.ErrorIfFn(db.Close, "Error closing db") - app, err := s.AppFactory.NewApplication(context.TODO(), s.Config, lggr, db) + app, err := s.AppFactory.NewApplication(ctx, s.Config, lggr, db) if err != nil { return s.errorOut(errors.Wrap(err, "fatal error instantiating application")) } @@ -603,7 +604,7 @@ func (s *Shell) RebroadcastTransactions(c *cli.Context) (err error) { ethClient := chain.Client() - err = ethClient.Dial(context.TODO()) + err = ethClient.Dial(ctx) if err != nil { return err } @@ -642,7 +643,7 @@ func (s *Shell) RebroadcastTransactions(c *cli.Context) (err error) { for i := int64(0); i < totalNonces; i++ { nonces[i] = evmtypes.Nonce(beginningNonce + i) } - err = ec.ForceRebroadcast(nonces, gas.EvmFee{Legacy: assets.NewWeiI(int64(gasPriceWei))}, address, uint32(overrideGasLimit)) + err = ec.ForceRebroadcast(ctx, nonces, gas.EvmFee{Legacy: assets.NewWeiI(int64(gasPriceWei))}, address, uint32(overrideGasLimit)) return s.errorOut(err) } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 644c516c21b..83a97833bd1 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -40,6 +40,7 @@ import ( "github.com/urfave/cli" "github.com/jmoiron/sqlx" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-relay/pkg/loop" diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index b2ec0822d44..1183277ac0b 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -121,7 +121,7 @@ func InitEVM(ctx context.Context, factory RelayerFactory, config EVMFactoryConfi // InitCosmos is a option for instantiating Cosmos relayers func InitCosmos(ctx context.Context, factory RelayerFactory, config CosmosFactoryConfig) CoreRelayerChainInitFunc { return func(op *CoreRelayerChainInteroperators) (err error) { - adapters, err2 := factory.NewCosmos(ctx, config) + adapters, err2 := factory.NewCosmos(config) if err2 != nil { return fmt.Errorf("failed to setup Cosmos relayer: %w", err2) } diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index d452decda1e..4bbabea4c82 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -250,7 +250,7 @@ func (c CosmosFactoryConfig) Validate() error { return err } -func (r *RelayerFactory) NewCosmos(ctx context.Context, config CosmosFactoryConfig) (map[relay.ID]CosmosLoopRelayerChainer, error) { +func (r *RelayerFactory) NewCosmos(config CosmosFactoryConfig) (map[relay.ID]CosmosLoopRelayerChainer, error) { err := config.Validate() if err != nil { return nil, fmt.Errorf("cannot create Cosmos relayer: %w", err) diff --git a/core/services/ocr2/plugins/dkg/persistence/db_test.go b/core/services/ocr2/plugins/dkg/persistence/db_test.go index 4e029c1cb2a..d4a4546fb93 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db_test.go +++ b/core/services/ocr2/plugins/dkg/persistence/db_test.go @@ -1,18 +1,18 @@ package persistence import ( - "context" "fmt" "math/big" "testing" "github.com/ethereum/go-ethereum/crypto" "github.com/jmoiron/sqlx" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" - "github.com/smartcontractkit/ocr2vrf/types/hash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + "github.com/smartcontractkit/ocr2vrf/types/hash" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -50,7 +50,7 @@ func TestShareDB_WriteShareRecords(t *testing.T) { expectedRecords = append(expectedRecords, rec) } - err := shareDB.WriteShareRecords(context.TODO(), configDigest, keyID, expectedRecords) + err := shareDB.WriteShareRecords(testutils.Context(t), configDigest, keyID, expectedRecords) require.NoError(tt, err) rows, err := db.Query(`SELECT COUNT(*) AS count FROM dkg_shares`) @@ -79,7 +79,7 @@ func TestShareDB_WriteShareRecords(t *testing.T) { } // no error, but there will be no rows inserted in the db - err = shareDB.WriteShareRecords(context.TODO(), configDigest, keyID, records) + err = shareDB.WriteShareRecords(testutils.Context(t), configDigest, keyID, records) require.NoError(tt, err) rows, err := db.Query(`SELECT COUNT(*) AS count FROM dkg_shares`) @@ -116,7 +116,7 @@ func TestShareDB_WriteShareRecords(t *testing.T) { records = append(records, rec) } - err := shareDB.WriteShareRecords(context.TODO(), configDigest, keyID, records) + err := shareDB.WriteShareRecords(testutils.Context(t), configDigest, keyID, records) require.Error(tt, err) // no rows should have been inserted @@ -160,7 +160,7 @@ func TestShareDBE2E(t *testing.T) { expectedRecordsMap[*playerIdx] = rec } - err := shareDB.WriteShareRecords(context.TODO(), configDigest, keyID, expectedRecords) + err := shareDB.WriteShareRecords(testutils.Context(t), configDigest, keyID, expectedRecords) require.NoError(t, err) actualRecords, err := shareDB.ReadShareRecords(configDigest, keyID) From e030073e8cfbb12ef86321f9755b2488a472c4f8 Mon Sep 17 00:00:00 2001 From: Lei Date: Thu, 16 Nov 2023 10:10:23 -0800 Subject: [PATCH 164/271] refactor mercury (#11137) * refactor mercury, rebase on top of PR 10878 * change back to use eth_call, sigh, simulated backend in integration test doesnt like using the registry contract directly * fixes and include new threadctl changes * address comments, add time in logs * rebase and add a test --- core/scripts/chaincli/handler/debug.go | 31 +- core/scripts/chaincli/handler/keeper.go | 5 +- .../handler/mercury_lookup_handler.go | 6 +- .../ocr2keeper/evm21/block_subscriber.go | 5 + .../ocr2keeper/evm21/encoding/interface.go | 61 +- .../ocr2keeper/evm21/encoding/packer.go | 25 +- .../ocr2keeper/evm21/encoding/packer_test.go | 12 +- .../ocr2keeper/evm21/mercury/mercury.go | 122 ++ .../ocr2keeper/evm21/mercury/mercury_test.go | 49 + .../evm21/mercury/streams/streams.go | 330 ++++ .../evm21/mercury/streams/streams_test.go | 726 +++++++++ .../evm21/mercury/upkeep_failure_reasons.go | 15 + .../ocr2keeper/evm21/mercury/upkeep_states.go | 14 + .../ocr2keeper/evm21/mercury/v02/request.go | 203 +++ .../evm21/mercury/v02/v02_request_test.go | 468 ++++++ .../ocr2keeper/evm21/mercury/v03/request.go | 252 +++ .../evm21/mercury/v03/v03_request_test.go | 536 +++++++ .../ocr2/plugins/ocr2keeper/evm21/registry.go | 89 +- .../evm21/registry_check_pipeline.go | 6 +- .../evm21/registry_check_pipeline_test.go | 56 +- .../ocr2keeper/evm21/streams_lookup.go | 642 -------- .../ocr2keeper/evm21/streams_lookup_test.go | 1354 ----------------- .../plugins/ocr2keeper/integration_21_test.go | 9 +- integration-tests/smoke/automation_test.go | 10 +- 24 files changed, 2928 insertions(+), 2098 deletions(-) create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury_test.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_failure_reasons.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_states.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go delete mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go delete mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index daf012ee16e..8f65b1f8814 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -18,16 +18,18 @@ 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/ocr2keepers/pkg/v3/types" + "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" - evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" ) const ( @@ -227,6 +229,13 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { 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) if err == nil { message("upkeep reverted with StreamsLookup") @@ -240,7 +249,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } allowed := false if len(cfg) > 0 { - var privilegeConfig evm.UpkeepPrivilegeConfig + var privilegeConfig streams.UpkeepPrivilegeConfig if err := json.Unmarshal(cfg, &privilegeConfig); err != nil { failUnknown("failed to unmarshal privilege config ", err) } @@ -307,6 +316,22 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { } } +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 diff --git a/core/scripts/chaincli/handler/keeper.go b/core/scripts/chaincli/handler/keeper.go index ba8276efb56..453ef43a566 100644 --- a/core/scripts/chaincli/handler/keeper.go +++ b/core/scripts/chaincli/handler/keeper.go @@ -14,6 +14,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/umbracle/ethgo/abi" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/cmd" @@ -34,7 +36,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/verifiable_load_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keeper" - evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" ) // Keeper is the keepers commands handler @@ -719,7 +720,7 @@ func (k *Keeper) deployUpkeeps(ctx context.Context, registryAddr common.Address, log.Printf("registry version is %s", v) log.Printf("active upkeep ids: %v", activeUpkeepIds) - adminBytes, err := json.Marshal(evm.UpkeepPrivilegeConfig{ + adminBytes, err := json.Marshal(streams.UpkeepPrivilegeConfig{ MercuryEnabled: true, }) if err != nil { diff --git a/core/scripts/chaincli/handler/mercury_lookup_handler.go b/core/scripts/chaincli/handler/mercury_lookup_handler.go index 0db142df9f9..1bd4b2e183c 100644 --- a/core/scripts/chaincli/handler/mercury_lookup_handler.go +++ b/core/scripts/chaincli/handler/mercury_lookup_handler.go @@ -3,7 +3,9 @@ package handler import ( "context" "crypto/hmac" + "crypto/sha256" "encoding/hex" + "encoding/json" "fmt" "io" "math/big" @@ -13,10 +15,6 @@ import ( "strings" "time" - "crypto/sha256" - - "encoding/json" - "github.com/avast/retry-go" ethabi "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go index d97156ed180..6cc19a4d02e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go @@ -12,6 +12,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/smartcontractkit/chainlink-relay/pkg/services" + httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -56,6 +57,10 @@ type BlockSubscriber struct { lggr logger.Logger } +func (bs *BlockSubscriber) LatestBlock() *ocr2keepers.BlockKey { + return bs.latestBlock.Load() +} + var _ ocr2keepers.BlockSubscriber = &BlockSubscriber{} func NewBlockSubscriber(hb httypes.HeadBroadcaster, lp logpoller.LogPoller, finalityDepth uint32, lggr logger.Logger) *BlockSubscriber { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go index 217f0dcb571..e21ecfb800d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go @@ -5,56 +5,55 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "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" ) -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 + UpkeepFailureReasonNone uint8 = 0 + UpkeepFailureReasonUpkeepCancelled uint8 = 1 + UpkeepFailureReasonUpkeepPaused uint8 = 2 + UpkeepFailureReasonTargetCheckReverted uint8 = 3 + UpkeepFailureReasonUpkeepNotNeeded uint8 = 4 + UpkeepFailureReasonPerformDataExceedsLimit uint8 = 5 + UpkeepFailureReasonInsufficientBalance uint8 = 6 + UpkeepFailureReasonMercuryCallbackReverted uint8 = 7 + UpkeepFailureReasonRevertDataExceedsLimit uint8 = 8 + UpkeepFailureReasonRegistryPaused uint8 = 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 + UpkeepFailureReasonMercuryAccessNotAllowed uint8 = 32 + UpkeepFailureReasonTxHashNoLongerExists uint8 = 33 + UpkeepFailureReasonInvalidRevertDataInput uint8 = 34 + UpkeepFailureReasonSimulationFailed uint8 = 35 + UpkeepFailureReasonTxHashReorged uint8 = 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 + NoPipelineError uint8 = 0 + CheckBlockTooOld uint8 = 1 + CheckBlockInvalid uint8 = 2 + RpcFlakyFailure uint8 = 3 + MercuryFlakyFailure uint8 = 4 + PackUnpackDecodeFailed uint8 = 5 + MercuryUnmarshalError uint8 = 6 + InvalidMercuryRequest uint8 = 7 + InvalidMercuryResponse uint8 = 8 // this will only happen if Mercury server sends bad responses + UpkeepNotAuthorized uint8 = 9 ) type UpkeepInfo = iregistry21.KeeperRegistryBase21UpkeepInfo type Packer interface { UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw string) (ocr2keepers.CheckResult, error) - UnpackCheckCallbackResult(callbackResp []byte) (PipelineExecutionState, bool, []byte, uint8, *big.Int, error) - UnpackPerformResult(raw string) (PipelineExecutionState, bool, 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) (*StreamsLookupError, error) + DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go index 9e070b2838c..28c6c4a9759 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go @@ -6,8 +6,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common/hexutil" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ) @@ -80,7 +83,7 @@ func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) return bts, nil } -func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (PipelineExecutionState, bool, []byte, uint8, *big.Int, error) { +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)) @@ -94,7 +97,7 @@ func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (PipelineExec return NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil } -func (p *abiPacker) UnpackPerformResult(raw string) (PipelineExecutionState, bool, error) { +func (p *abiPacker) UnpackPerformResult(raw string) (uint8, bool, error) { b, err := hexutil.Decode(raw) if err != nil { return PackUnpackDecodeFailed, false, err @@ -161,16 +164,8 @@ func (p *abiPacker) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistr return report, nil } -type StreamsLookupError struct { - FeedParamKey string - Feeds []string - TimeParamKey string - Time *big.Int - ExtraData []byte -} - // DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) -func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) { +func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) { e := p.streamsABI.Errors["StreamsLookup"] unpack, err := e.Unpack(data) if err != nil { @@ -178,7 +173,7 @@ func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError } errorParameters := unpack.([]interface{}) - return &StreamsLookupError{ + 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), @@ -188,10 +183,10 @@ func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError } // GetIneligibleCheckResultWithoutPerformData returns an ineligible check result with ineligibility reason and pipeline execution state but without perform data -func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason UpkeepFailureReason, state PipelineExecutionState, retryable bool) ocr2keepers.CheckResult { +func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason uint8, state uint8, retryable bool) ocr2keepers.CheckResult { return ocr2keepers.CheckResult{ - IneligibilityReason: uint8(reason), - PipelineExecutionState: uint8(state), + IneligibilityReason: reason, + PipelineExecutionState: state, Retryable: retryable, UpkeepID: p.UpkeepID, Trigger: p.Trigger, diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go index 1801b018572..03b55bfccc7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "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/evm21/core" @@ -210,7 +212,7 @@ func TestPacker_UnpackPerformResult(t *testing.T) { tests := []struct { Name string RawData string - State PipelineExecutionState + State uint8 }{ { Name: "unpack success", @@ -238,7 +240,7 @@ func TestPacker_UnpackCheckCallbackResult(t *testing.T) { FailureReason uint8 GasUsed *big.Int ErrorString string - State PipelineExecutionState + State uint8 }{ { Name: "unpack upkeep needed", @@ -441,14 +443,14 @@ func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { tests := []struct { name string data []byte - expected *StreamsLookupError - state PipelineExecutionState + expected *mercury.StreamsLookupError + state uint8 err error }{ { name: "success - decode to streams lookup", data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - expected: &StreamsLookupError{ + expected: &mercury.StreamsLookupError{ FeedParamKey: "feedIdHex", Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, TimeParamKey: "blockNumber", diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury.go new file mode 100644 index 00000000000..cf6ebeafc6e --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury.go @@ -0,0 +1,122 @@ +package mercury + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" + "math/big" + "net/http" + "time" + + "github.com/patrickmn/go-cache" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" +) + +const ( + FeedIDs = "feedIDs" // valid for v0.3 + FeedIdHex = "feedIdHex" // valid for v0.2 + BlockNumber = "blockNumber" // valid for v0.2 + Timestamp = "timestamp" // valid for v0.3 + totalFastPluginRetries = 5 + totalMediumPluginRetries = 10 +) + +var GenerateHMACFn = func(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 +} + +// CalculateRetryConfig returns plugin retry interval based on how many times plugin has retried this work +var CalculateRetryConfigFn = func(prk string, mercuryConfig MercuryConfigProvider) time.Duration { + var retryInterval time.Duration + var retries int + totalAttempts, ok := mercuryConfig.GetPluginRetry(prk) + if ok { + retries = totalAttempts.(int) + if retries < totalFastPluginRetries { + retryInterval = 1 * time.Second + } else if retries < totalMediumPluginRetries { + retryInterval = 5 * time.Second + } + // if the core node has retried totalMediumPluginRetries times, do not set retry interval and plugin will use + // the default interval + } else { + retryInterval = 1 * time.Second + } + mercuryConfig.SetPluginRetry(prk, retries+1, cache.DefaultExpiration) + return retryInterval +} + +type MercuryData struct { + Index int + Error error + Retryable bool + Bytes [][]byte + 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) + SetUpkeepAllowed(string, interface{}, time.Duration) + GetPluginRetry(string) (interface{}, bool) + SetPluginRetry(string, interface{}, time.Duration) +} + +type HttpClient interface { + Do(req *http.Request) (*http.Response, error) +} + +type MercuryClient interface { + DoRequest(ctx context.Context, streamsLookup *StreamsLookup, pluginRetryKey string) (MercuryUpkeepState, MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) +} + +type StreamsLookupError struct { + FeedParamKey string + Feeds []string + TimeParamKey string + Time *big.Int + ExtraData []byte +} + +type StreamsLookup struct { + *StreamsLookupError + UpkeepId *big.Int + Block uint64 +} + +func (l *StreamsLookup) IsMercuryVersionUnkown() bool { + return l.FeedParamKey != FeedIDs +} + +func (l *StreamsLookup) IsMercuryV02() bool { + return l.FeedParamKey == FeedIdHex && l.TimeParamKey == BlockNumber +} + +func (l *StreamsLookup) IsMercuryV03() bool { + return l.FeedParamKey == FeedIDs +} + +func (l *StreamsLookup) IsMercuryUsingBatchPathV03() bool { + return l.TimeParamKey == BlockNumber +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury_test.go new file mode 100644 index 00000000000..baa939dbecc --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury_test.go @@ -0,0 +1,49 @@ +package mercury + +import ( + "testing" +) + +func TestGenerateHMACFn(t *testing.T) { + testCases := []struct { + name string + method string + path string + body []byte + clientId string + secret string + ts int64 + expected string + }{ + { + name: "generate hmac function", + method: "GET", + path: "/example", + body: []byte(""), + clientId: "yourClientId", + secret: "yourSecret", + ts: 1234567890, + expected: "17b0bb6b14f7b48ef9d24f941ff8f33ad2d5e94ac343380be02c2f1ca32fdbd8", + }, + { + name: "generate hmac function with non-empty body", + method: "POST", + path: "/api", + body: []byte("request body"), + clientId: "anotherClientId", + secret: "anotherSecret", + ts: 1597534567, + expected: "d326c168c50c996e271d6b3b4c97944db01163994090f73fcf4fd42f23f06bbb", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := GenerateHMACFn(tc.method, tc.path, tc.body, tc.clientId, tc.secret, tc.ts) + + if result != tc.expected { + t.Errorf("Expected: %s, Got: %s", tc.expected, result) + } + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go new file mode 100644 index 00000000000..b83aca8a02a --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go @@ -0,0 +1,330 @@ +package streams + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "net/http" + "sync" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/patrickmn/go-cache" + + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + "github.com/smartcontractkit/chainlink-relay/pkg/services" + + 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/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" + v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type Lookup interface { + Lookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult +} + +type latestBlockProvider interface { + LatestBlock() *ocr2keepers.BlockKey +} + +type streamsRegistry interface { + GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + CheckCallback(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) + Address() common.Address +} + +type contextCaller interface { + CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error +} + +type streams struct { + services.StateMachine + packer mercury.Packer + mercuryConfig mercury.MercuryConfigProvider + abi abi.ABI + blockSubscriber latestBlockProvider + registry streamsRegistry + client contextCaller + lggr logger.Logger + threadCtrl utils.ThreadControl + v02Client mercury.MercuryClient + v03Client mercury.MercuryClient +} + +// 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"` +} + +func NewStreamsLookup( + packer mercury.Packer, + mercuryConfig mercury.MercuryConfigProvider, + blockSubscriber latestBlockProvider, + client contextCaller, + registry streamsRegistry, + lggr logger.Logger) *streams { + httpClient := http.DefaultClient + threadCtrl := utils.NewThreadControl() + return &streams{ + packer: packer, + mercuryConfig: mercuryConfig, + abi: core.RegistryABI, + blockSubscriber: blockSubscriber, + registry: registry, + client: client, + lggr: lggr, + threadCtrl: threadCtrl, + v02Client: v02.NewClient(mercuryConfig, httpClient, threadCtrl, lggr), + v03Client: v03.NewClient(mercuryConfig, httpClient, threadCtrl, lggr), + } +} + +// Lookup looks through check upkeep results looking for any that need off chain lookup +func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult { + lookups := map[int]*mercury.StreamsLookup{} + for i, checkResult := range checkResults { + s.buildResult(ctx, i, checkResult, checkResults, lookups) + } + + var wg sync.WaitGroup + for i, lookup := range lookups { + wg.Add(1) + func(i int, lookup *mercury.StreamsLookup) { + s.threadCtrl.Go(func(ctx context.Context) { + s.doLookup(ctx, &wg, lookup, i, checkResults) + }) + }(i, lookup) + } + wg.Wait() + + // don't surface error to plugin bc StreamsLookup process should be self-contained. + return checkResults +} + +// buildResult checks if the upkeep is allowed by Mercury and builds a streams lookup request from the check result +func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keepers.CheckResult, checkResults []ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { + lookupLggr := s.lggr.With("where", "StreamsLookup") + if checkResult.IneligibilityReason != uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted) { + // Streams Lookup only works when upkeep target check reverts + return + } + + block := big.NewInt(int64(checkResult.Trigger.BlockNumber)) + upkeepId := checkResult.UpkeepID + + if s.mercuryConfig.Credentials() == nil { + lookupLggr.Errorf("at block %d upkeep %s tries to access mercury server but mercury credential is not configured", block, upkeepId) + return + } + + // Try to decode the revert error into streams lookup format. User upkeeps can revert with any reason, see if they + // 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 + return + } + streamsLookupResponse := &mercury.StreamsLookup{StreamsLookupError: streamsLookupErr} + + if len(streamsLookupResponse.Feeds) == 0 { + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + lookupLggr.Debugf("at block %s upkeep %s has empty feeds array", block, upkeepId) + return + } + + // mercury permission checking for v0.3 is done by mercury server + 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 { + 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) + checkResults[i].Retryable = retryable + return + } else if !allowed { + lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) + return + } + } else if streamsLookupResponse.IsMercuryVersionUnkown() { + // if mercury version cannot be determined, set failure reason + lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + return + } + + streamsLookupResponse.UpkeepId = upkeepId.BigInt() + // the block here is exclusively used to call checkCallback at this block, not to be confused with the block number + // in the revert for mercury v0.2, which is denoted by time in the struct bc starting from v0.3, only timestamp will be supported + streamsLookupResponse.Block = uint64(block.Int64()) + lookupLggr.Infof("at block %d upkeep %s DecodeStreamsLookupRequest feedKey=%s timeKey=%s feeds=%v time=%s extraData=%s", block, upkeepId, streamsLookupResponse.FeedParamKey, streamsLookupResponse.TimeParamKey, streamsLookupResponse.Feeds, streamsLookupResponse.Time, hexutil.Encode(streamsLookupResponse.ExtraData)) + lookups[i] = streamsLookupResponse +} + +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) + + 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 { + 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 + } + + 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)) + } + + 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 + } + 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 + } + + 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 + } + + 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 + } + + 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)) +} + +// 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) { + allowed, ok := s.mercuryConfig.IsUpkeepAllowed(upkeepId.String()) + if ok { + return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonNone, false, allowed.(bool), nil + } + + payload, err := s.packer.PackGetUpkeepPrivilegeConfig(upkeepId) + if err != nil { + // pack error, no retryable + s.lggr.Warnf("failed to pack getUpkeepPrivilegeConfig data for upkeepId %s: %s", upkeepId, err) + + return mercury.PackUnpackDecodeFailed, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to pack upkeepId: %w", err) + } + + var resultBytes 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(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) + } + + 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) + } + + if len(upkeepPrivilegeConfigBytes) == 0 { + s.mercuryConfig.SetUpkeepAllowed(upkeepId.String(), false, cache.DefaultExpiration) + return mercury.NoPipelineError, mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed, false, false, fmt.Errorf("upkeep privilege config is empty") + } + + var privilegeConfig UpkeepPrivilegeConfig + if err = json.Unmarshal(upkeepPrivilegeConfigBytes, &privilegeConfig); err != nil { + return mercury.MercuryUnmarshalError, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to unmarshal privilege config: %v", err) + } + + s.mercuryConfig.SetUpkeepAllowed(upkeepId.String(), privilegeConfig.MercuryEnabled, cache.DefaultExpiration) + + 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, + } + + if block == nil || block.Int64() == 0 { + if latestBlock := s.blockSubscriber.LatestBlock(); latestBlock != nil && latestBlock.Number != 0 { + opts.BlockNumber = big.NewInt(int64(latestBlock.Number)) + } + } else { + opts.BlockNumber = block + } + + return &opts +} + +// generatePluginRetryKey returns a plugin retry cache key +func generatePluginRetryKey(workID string, block uint64) string { + return workID + "|" + fmt.Sprintf("%d", block) +} + +func (s *streams) Close() error { + return s.StopOnce("streams_lookup", func() error { + s.threadCtrl.Close() + return nil + }) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go new file mode 100644 index 00000000000..194d74febba --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go @@ -0,0 +1,726 @@ +package streams + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "math/big" + "net/http" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + + "github.com/stretchr/testify/assert" + + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/stretchr/testify/mock" + + evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + 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/evm21/mercury" + v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" + v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" + + "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/evm21/encoding" +) + +type MockMercuryConfigProvider struct { + mock.Mock +} + +func (m *MockMercuryConfigProvider) Credentials() *models.MercuryCredentials { + mc := &models.MercuryCredentials{ + LegacyURL: "https://google.old.com", + URL: "https://google.com", + Username: "FakeClientID", + Password: "FakeClientKey", + } + return mc +} + +func (m *MockMercuryConfigProvider) IsUpkeepAllowed(s string) (interface{}, bool) { + args := m.Called(s) + return args.Get(0), args.Bool(1) +} + +func (m *MockMercuryConfigProvider) SetUpkeepAllowed(s string, i interface{}, d time.Duration) { + m.Called(s, i, d) +} + +func (m *MockMercuryConfigProvider) GetPluginRetry(s string) (interface{}, bool) { + args := m.Called(s) + return args.Get(0), args.Bool(1) +} + +func (m *MockMercuryConfigProvider) SetPluginRetry(s string, i interface{}, d time.Duration) { + m.Called(s, i, d) +} + +type MockBlockSubscriber struct { + mock.Mock +} + +func (b *MockBlockSubscriber) LatestBlock() *ocr2keepers.BlockKey { + return nil +} + +type MockHttpClient struct { + mock.Mock +} + +func (mock *MockHttpClient) Do(req *http.Request) (*http.Response, error) { + args := mock.Called(req) + return args.Get(0).(*http.Response), args.Error(1) +} + +type mockRegistry struct { + GetUpkeepPrivilegeConfigFn func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) + CheckCallbackFn func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) +} + +func (r *mockRegistry) Address() common.Address { + return common.HexToAddress("0x6cA639822c6C241Fa9A7A6b5032F6F7F1C513CAD") +} + +func (r *mockRegistry) GetUpkeepPrivilegeConfig(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return r.GetUpkeepPrivilegeConfigFn(opts, upkeepId) +} + +func (r *mockRegistry) CheckCallback(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return r.CheckCallbackFn(opts, id, values, extraData) +} + +// 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, + registry, + lggr, + ) + return streams +} + +func TestStreams_CheckCallback(t *testing.T) { + upkeepId := big.NewInt(123456789) + bn := uint64(999) + bs := []byte{183, 114, 215, 10, 0, 0, 0, 0, 0, 0} + values := [][]byte{bs} + tests := []struct { + name string + lookup *mercury.StreamsLookup + values [][]byte + statusCode int + + callbackResp []byte + callbackErr error + + upkeepNeeded bool + performData []byte + wantErr assert.ErrorAssertionFunc + + state mercury.MercuryUpkeepState + retryable bool + registry streamsRegistry + }{ + { + name: "success - empty extra data", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"ETD-USD", "BTC-ETH"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(100), + ExtraData: []byte{48, 120, 48, 48}, + }, + UpkeepId: upkeepId, + Block: bn, + }, + 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}, + upkeepNeeded: true, + performData: []byte{48, 120, 48, 48}, + wantErr: assert.NoError, + 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: []byte{48, 120, 48, 48}, + }, nil + }, + }, + }, + { + name: "success - with extra data", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(18952430), + // this is the address of precompile contract ArbSys(0x0000000000000000000000000000000000000064) + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + Block: bn, + }, + 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}, + upkeepNeeded: true, + performData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + wantErr: assert.NoError, + 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: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, nil + }, + }, + }, + { + name: "failure - bad response", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"ETD-USD", "BTC-ETH"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(100), + ExtraData: []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, 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, 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}, + }, + UpkeepId: upkeepId, + Block: bn, + }, + values: values, + statusCode: http.StatusOK, + callbackResp: []byte{}, + callbackErr: errors.New("bad response"), + wantErr: assert.Error, + state: mercury.RpcFlakyFailure, + retryable: true, + 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{}, errors.New("bad response") + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := setupStreams(t) + defer r.Close() + r.registry = tt.registry + + client := new(evmClientMocks.Client) + s := setupStreams(t) + payload, err := s.abi.Pack("checkCallback", tt.lookup.UpkeepId, values, tt.lookup.ExtraData) + require.Nil(t, err) + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), + } + client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, hexutil.EncodeUint64(tt.lookup.Block)).Return(tt.callbackErr). + Run(func(args mock.Arguments) { + by := args.Get(1).(*hexutil.Bytes) + *by = tt.callbackResp + }).Once() + s.client = client + + state, retryable, _, err := s.checkCallback(context.Background(), 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) + }) + } +} + +func TestStreams_AllowedToUseMercury(t *testing.T) { + upkeepId, ok := new(big.Int).SetString("71022726777042968814359024671382968091267501884371696415772139504780367423725", 10) + assert.True(t, ok, t.Name()) + tests := []struct { + name string + cached bool + allowed bool + ethCallErr error + err error + state mercury.MercuryUpkeepState + reason mercury.MercuryUpkeepFailureReason + registry streamsRegistry + retryable bool + config []byte + }{ + { + name: "success - allowed via cache", + cached: true, + allowed: true, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{}, nil + }, + }, + }, + { + name: "success - allowed via fetching privilege config", + allowed: true, + 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{}, nil + }, + }, + }, + { + name: "success - not allowed via cache", + cached: true, + allowed: false, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{}, nil + }, + }, + }, + { + name: "success - not allowed via fetching privilege config", + allowed: false, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return []byte(`{"mercuryEnabled":false}`), nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{}, nil + }, + }, + }, + { + name: "failure - cannot unmarshal privilege config", + err: fmt.Errorf("failed to unmarshal privilege config: invalid character '\\x00' looking for beginning of value"), + state: mercury.MercuryUnmarshalError, + config: []byte{0, 1}, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return []byte(`{`), nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{}, nil + }, + }, + }, + { + name: "failure - flaky RPC", + retryable: true, + err: fmt.Errorf("failed to get upkeep privilege config: flaky RPC"), + state: mercury.RpcFlakyFailure, + ethCallErr: fmt.Errorf("flaky RPC"), + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return nil, errors.New("flaky RPC") + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{}, nil + }, + }, + }, + { + name: "failure - empty upkeep privilege config", + err: fmt.Errorf("upkeep privilege config is empty"), + reason: mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed, + config: []byte{}, + registry: &mockRegistry{ + GetUpkeepPrivilegeConfigFn: func(opts *bind.CallOpts, upkeepId *big.Int) ([]byte, error) { + return []byte(``), nil + }, + CheckCallbackFn: func(opts *bind.CallOpts, id *big.Int, values [][]byte, extraData []byte) (iregistry21.CheckCallback, error) { + return iregistry21.CheckCallback{}, nil + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := setupStreams(t) + defer s.Close() + s.registry = tt.registry + + client := new(evmClientMocks.Client) + s.client = client + + mc := new(MockMercuryConfigProvider) + mc.On("IsUpkeepAllowed", mock.Anything).Return(tt.allowed, tt.cached).Once() + mc.On("SetUpkeepAllowed", mock.Anything, mock.Anything, mock.Anything).Return().Once() + s.mercuryConfig = mc + + if !tt.cached { + if tt.err != nil { + bContractCfg, err := s.abi.Methods["getUpkeepPrivilegeConfig"].Outputs.PackValues([]interface{}{tt.config}) + require.Nil(t, err) + + payload, err := s.abi.Pack("getUpkeepPrivilegeConfig", upkeepId) + require.Nil(t, err) + + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), + } + + client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, mock.AnythingOfType("string")). + Return(tt.ethCallErr). + Run(func(args mock.Arguments) { + b := args.Get(1).(*hexutil.Bytes) + *b = bContractCfg + }).Once() + } else { + cfg := UpkeepPrivilegeConfig{MercuryEnabled: tt.allowed} + bCfg, err := json.Marshal(cfg) + require.Nil(t, err) + + bContractCfg, err := s.abi.Methods["getUpkeepPrivilegeConfig"].Outputs.PackValues([]interface{}{bCfg}) + require.Nil(t, err) + + payload, err := s.abi.Pack("getUpkeepPrivilegeConfig", upkeepId) + require.Nil(t, err) + + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), + } + + client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, mock.AnythingOfType("string")).Return(nil). + Run(func(args mock.Arguments) { + b := args.Get(1).(*hexutil.Bytes) + *b = bContractCfg + }).Once() + } + } + + opts := &bind.CallOpts{ + BlockNumber: big.NewInt(10), + } + + 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) + assert.Equal(t, tt.reason, reason) + assert.Equal(t, tt.retryable, retryable) + }) + } +} + +func TestStreams_StreamsLookup(t *testing.T) { + upkeepId, ok := new(big.Int).SetString("71022726777042968814359024671382968091267501884371696415772139504780367423725", 10) + var upkeepIdentifier [32]byte + copy(upkeepIdentifier[:], upkeepId.Bytes()) + assert.True(t, ok, t.Name()) + blockNum := ocr2keepers.BlockNumber(37974374) + tests := []struct { + name string + input []ocr2keepers.CheckResult + blobs map[string]string + callbackResp []byte + expectedResults []ocr2keepers.CheckResult + callbackNeeded bool + extraData []byte + checkCallbackResp []byte + values [][]byte + cachedAdminCfg bool + hasError bool + hasPermission bool + v3 bool + registry streamsRegistry + }{ + { + name: "success - happy path no cache", + input: []ocr2keepers.CheckResult{ + { + 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: 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{ + { + PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000766656564494473000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), + }, + }, + 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: true, + PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: blockNum, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonNone), + }, + }, + hasPermission: true, + v3: true, + 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: "skip - failure reason is insufficient balance", + input: []ocr2keepers.CheckResult{ + { + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), + }, + }, + expectedResults: []ocr2keepers.CheckResult{ + { + Eligible: false, + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), + }, + }, + hasError: true, + 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: []byte{}, + }, nil + }, + }, + }, + { + name: "skip - invalid revert data", + input: []ocr2keepers.CheckResult{ + { + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), + }, + }, + expectedResults: []ocr2keepers.CheckResult{ + { + Eligible: false, + PerformData: []byte{}, + UpkeepID: upkeepIdentifier, + Trigger: ocr2keepers.Trigger{ + BlockNumber: 26046145, + }, + IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), + }, + }, + hasError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := setupStreams(t) + defer s.Close() + s.registry = tt.registry + + client := new(evmClientMocks.Client) + s.client = client + + mc := new(MockMercuryConfigProvider) + mc.On("IsUpkeepAllowed", mock.Anything).Return(tt.hasPermission, tt.cachedAdminCfg).Once() + mc.On("SetUpkeepAllowed", mock.Anything, mock.Anything, mock.Anything).Return().Once() + s.mercuryConfig = mc + + if !tt.cachedAdminCfg && !tt.hasError { + cfg := UpkeepPrivilegeConfig{MercuryEnabled: tt.hasPermission} + bCfg, err := json.Marshal(cfg) + require.Nil(t, err) + + bContractCfg, err := s.abi.Methods["getUpkeepPrivilegeConfig"].Outputs.PackValues([]interface{}{bCfg}) + require.Nil(t, err) + + payload, err := s.abi.Pack("getUpkeepPrivilegeConfig", upkeepId) + require.Nil(t, err) + + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), + } + + client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, mock.AnythingOfType("string")).Return(nil). + Run(func(args mock.Arguments) { + b := args.Get(1).(*hexutil.Bytes) + *b = bContractCfg + }).Once() + } + + if len(tt.blobs) > 0 { + if tt.v3 { + hc03 := new(MockHttpClient) + v03HttpClient := v03.NewClient(s.mercuryConfig, hc03, s.threadCtrl, s.lggr) + s.v03Client = v03HttpClient + + mr1 := v03.MercuryV03Response{ + Reports: []v03.MercuryV03Report{{FullReport: tt.blobs["0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"]}, {FullReport: tt.blobs["0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"]}}} + b1, err := json.Marshal(mr1) + assert.Nil(t, err) + resp1 := &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(b1)), + } + hc03.On("Do", mock.Anything).Return(resp1, nil).Once() + } else { + hc02 := new(MockHttpClient) + v02HttpClient := v02.NewClient(s.mercuryConfig, hc02, s.threadCtrl, s.lggr) + s.v02Client = v02HttpClient + + mr1 := v02.MercuryV02Response{ChainlinkBlob: tt.blobs["0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"]} + b1, err := json.Marshal(mr1) + assert.Nil(t, err) + resp1 := &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(b1)), + } + mr2 := v02.MercuryV02Response{ChainlinkBlob: tt.blobs["0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"]} + b2, err := json.Marshal(mr2) + assert.Nil(t, err) + resp2 := &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(b2)), + } + + hc02.On("Do", mock.MatchedBy(func(req *http.Request) bool { + return strings.Contains(req.URL.String(), "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000") + })).Return(resp2, nil).Once() + + hc02.On("Do", mock.MatchedBy(func(req *http.Request) bool { + return strings.Contains(req.URL.String(), "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000") + })).Return(resp1, nil).Once() + } + } + + if tt.callbackNeeded { + payload, err := s.abi.Pack("checkCallback", upkeepId, tt.values, tt.extraData) + require.Nil(t, err) + args := map[string]interface{}{ + "to": s.registry.Address().Hex(), + "data": hexutil.Bytes(payload), + } + client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, hexutil.EncodeUint64(uint64(blockNum))).Return(nil). + Run(func(args mock.Arguments) { + b := args.Get(1).(*hexutil.Bytes) + *b = tt.checkCallbackResp + }).Once() + } + + got := s.Lookup(context.Background(), tt.input) + assert.Equal(t, tt.expectedResults, got, tt.name) + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_failure_reasons.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_failure_reasons.go new file mode 100644 index 00000000000..261fc33bd46 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_failure_reasons.go @@ -0,0 +1,15 @@ +package mercury + +type MercuryUpkeepFailureReason uint8 + +const ( + // upkeep failure onchain reasons + MercuryUpkeepFailureReasonNone MercuryUpkeepFailureReason = 0 + MercuryUpkeepFailureReasonTargetCheckReverted MercuryUpkeepFailureReason = 3 + MercuryUpkeepFailureReasonUpkeepNotNeeded MercuryUpkeepFailureReason = 4 + MercuryUpkeepFailureReasonMercuryCallbackReverted MercuryUpkeepFailureReason = 7 + // leaving a gap here for more onchain failure reasons in the future + // upkeep failure offchain reasons + MercuryUpkeepFailureReasonMercuryAccessNotAllowed MercuryUpkeepFailureReason = 32 + MercuryUpkeepFailureReasonInvalidRevertDataInput MercuryUpkeepFailureReason = 34 +) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_states.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_states.go new file mode 100644 index 00000000000..4e9cd14aee3 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_states.go @@ -0,0 +1,14 @@ +package mercury + +type MercuryUpkeepState uint8 + +const ( + NoPipelineError MercuryUpkeepState = 0 + RpcFlakyFailure MercuryUpkeepState = 3 + MercuryFlakyFailure MercuryUpkeepState = 4 + PackUnpackDecodeFailed MercuryUpkeepState = 5 + MercuryUnmarshalError MercuryUpkeepState = 6 + InvalidMercuryRequest MercuryUpkeepState = 7 + InvalidMercuryResponse MercuryUpkeepState = 8 // this will only happen if Mercury server sends bad responses + UpkeepNotAuthorized MercuryUpkeepState = 9 +) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go new file mode 100644 index 00000000000..55436937d11 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go @@ -0,0 +1,203 @@ +package v02 + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/avast/retry-go/v4" + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +const ( + mercuryPathV02 = "/client?" // only used to access mercury v0.2 server + retryDelay = 500 * time.Millisecond + totalAttempt = 3 + contentTypeHeader = "Content-Type" + authorizationHeader = "Authorization" + timestampHeader = "X-Authorization-Timestamp" + signatureHeader = "X-Authorization-Signature-SHA256" +) + +type MercuryV02Response struct { + ChainlinkBlob string `json:"chainlinkBlob"` +} + +type client struct { + utils.StartStopOnce + mercuryConfig mercury.MercuryConfigProvider + httpClient mercury.HttpClient + threadCtrl utils.ThreadControl + lggr logger.Logger +} + +func NewClient(mercuryConfig mercury.MercuryConfigProvider, httpClient mercury.HttpClient, threadCtrl utils.ThreadControl, lggr logger.Logger) *client { + return &client{ + mercuryConfig: mercuryConfig, + httpClient: httpClient, + threadCtrl: threadCtrl, + lggr: lggr, + } +} + +func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (mercury.MercuryUpkeepState, mercury.MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) { + resultLen := len(streamsLookup.Feeds) + ch := make(chan mercury.MercuryData, resultLen) + if len(streamsLookup.Feeds) == 0 { + return 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", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) + } + for i := range streamsLookup.Feeds { + // TODO (AUTO-7209): limit the number of concurrent requests + i := i + c.threadCtrl.Go(func(ctx context.Context) { + c.singleFeedRequest(ctx, ch, i, streamsLookup) + }) + } + + var reqErr error + var retryInterval time.Duration + results := make([][]byte, len(streamsLookup.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 := mercury.NoPipelineError + for i := 0; i < resultLen; i++ { + m := <-ch + if m.Error != nil { + reqErr = errors.Join(reqErr, m.Error) + retryable = retryable && m.Retryable + allSuccess = false + if m.State != mercury.NoPipelineError { + state = mercury.MercuryUpkeepState(m.State) + } + continue + } + results[m.Index] = m.Bytes[0] + } + if retryable && !allSuccess { + retryInterval = mercury.CalculateRetryConfigFn(pluginRetryKey, c.mercuryConfig) + } + // only retry when not all successful AND none are not retryable + return state, mercury.MercuryUpkeepFailureReasonNone, results, retryable && !allSuccess, retryInterval, reqErr +} + +func (c *client) singleFeedRequest(ctx context.Context, ch chan<- mercury.MercuryData, index int, sl *mercury.StreamsLookup) { + var httpRequest *http.Request + var err error + + q := url.Values{ + sl.FeedParamKey: {sl.Feeds[index]}, + sl.TimeParamKey: {sl.Time.String()}, + } + mercuryURL := c.mercuryConfig.Credentials().LegacyURL + reqUrl := fmt.Sprintf("%s%s%s", mercuryURL, mercuryPathV02, q.Encode()) + c.lggr.Debugf("request URL for upkeep %s feed %s: %s", sl.UpkeepId.String(), sl.Feeds[index], reqUrl) + + httpRequest, err = http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) + if err != nil { + ch <- mercury.MercuryData{Index: index, Error: err, Retryable: false, State: mercury.InvalidMercuryRequest} + return + } + + ts := time.Now().UTC().UnixMilli() + signature := mercury.GenerateHMACFn(http.MethodGet, mercuryPathV02+q.Encode(), []byte{}, c.mercuryConfig.Credentials().Username, c.mercuryConfig.Credentials().Password, ts) + httpRequest.Header.Set(contentTypeHeader, "application/json") + httpRequest.Header.Set(authorizationHeader, c.mercuryConfig.Credentials().Username) + httpRequest.Header.Set(timestampHeader, strconv.FormatInt(ts, 10)) + httpRequest.Header.Set(signatureHeader, signature) + + // in the case of multiple retries here, use the last attempt's data + state := mercury.NoPipelineError + retryable := false + sent := false + retryErr := retry.Do( + func() error { + var httpResponse *http.Response + var responseBody []byte + var blobBytes []byte + + retryable = false + if httpResponse, err = c.httpClient.Do(httpRequest); err != nil { + c.lggr.Warnf("at block %s upkeep %s GET request fails for feed %s: %v", sl.Time.String(), sl.UpkeepId.String(), sl.Feeds[index], err) + retryable = true + state = mercury.MercuryFlakyFailure + return err + } + defer httpResponse.Body.Close() + + if responseBody, err = io.ReadAll(httpResponse.Body); err != nil { + state = mercury.InvalidMercuryResponse + return err + } + + switch httpResponse.StatusCode { + case http.StatusNotFound, http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: + c.lggr.Warnf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.UpkeepId.String(), httpResponse.StatusCode, sl.Feeds[index]) + retryable = true + state = mercury.MercuryFlakyFailure + return errors.New(strconv.FormatInt(int64(httpResponse.StatusCode), 10)) + case http.StatusOK: + // continue + default: + state = mercury.InvalidMercuryRequest + return fmt.Errorf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.UpkeepId.String(), httpResponse.StatusCode, sl.Feeds[index]) + } + + c.lggr.Debugf("at block %s upkeep %s received status code %d from mercury v0.2 with BODY=%s", sl.Time.String(), sl.UpkeepId.String(), httpResponse.StatusCode, hexutil.Encode(responseBody)) + + var m MercuryV02Response + if err = json.Unmarshal(responseBody, &m); err != nil { + c.lggr.Warnf("at block %s upkeep %s failed to unmarshal body to MercuryV02Response for feed %s: %v", sl.Time.String(), sl.UpkeepId.String(), sl.Feeds[index], err) + state = mercury.MercuryUnmarshalError + return err + } + if blobBytes, err = hexutil.Decode(m.ChainlinkBlob); err != nil { + c.lggr.Warnf("at block %s upkeep %s failed to decode chainlinkBlob %s for feed %s: %v", sl.Time.String(), sl.UpkeepId.String(), m.ChainlinkBlob, sl.Feeds[index], err) + state = mercury.InvalidMercuryResponse + return err + } + ch <- mercury.MercuryData{ + Index: index, + Bytes: [][]byte{blobBytes}, + Retryable: false, + State: mercury.NoPipelineError, + } + sent = true + return nil + }, + // only retry when the error is 404 Not Found, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout + retry.RetryIf(func(err error) bool { + return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) || err.Error() == fmt.Sprintf("%d", http.StatusBadGateway) || err.Error() == fmt.Sprintf("%d", http.StatusServiceUnavailable) || err.Error() == fmt.Sprintf("%d", http.StatusGatewayTimeout) + }), + retry.Context(ctx), + retry.Delay(retryDelay), + retry.Attempts(totalAttempt), + ) + + if !sent { + ch <- mercury.MercuryData{ + Index: index, + Bytes: [][]byte{}, + Retryable: retryable, + Error: fmt.Errorf("failed to request feed for %s: %w", sl.Feeds[index], retryErr), + State: state, + } + } +} + +func (c *client) Close() error { + return c.StopOnce("v02_request", func() error { + c.threadCtrl.Close() + return nil + }) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go new file mode 100644 index 00000000000..17ef8515fd1 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go @@ -0,0 +1,468 @@ +package v02 + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "io" + "math/big" + "net/http" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/patrickmn/go-cache" + "github.com/stretchr/testify/mock" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" +) + +const ( + defaultPluginRetryExpiration = 30 * time.Minute + cleanupInterval = 5 * time.Minute +) + +type MockMercuryConfigProvider struct { + cache *cache.Cache + mock.Mock +} + +func NewMockMercuryConfigProvider() *MockMercuryConfigProvider { + return &MockMercuryConfigProvider{ + cache: cache.New(defaultPluginRetryExpiration, cleanupInterval), + } +} + +func (m *MockMercuryConfigProvider) Credentials() *models.MercuryCredentials { + mc := &models.MercuryCredentials{ + LegacyURL: "https://google.old.com", + URL: "https://google.com", + Username: "FakeClientID", + Password: "FakeClientKey", + } + return mc +} + +func (m *MockMercuryConfigProvider) IsUpkeepAllowed(s string) (interface{}, bool) { + args := m.Called(s) + return args.Get(0), args.Bool(1) +} + +func (m *MockMercuryConfigProvider) SetUpkeepAllowed(s string, i interface{}, d time.Duration) { + m.Called(s, i, d) +} + +func (m *MockMercuryConfigProvider) GetPluginRetry(s string) (interface{}, bool) { + if value, found := m.cache.Get(s); found { + return value, true + } + + return nil, false +} + +func (m *MockMercuryConfigProvider) SetPluginRetry(s string, i interface{}, d time.Duration) { + m.cache.Set(s, i, d) +} + +type MockHttpClient struct { + mock.Mock +} + +func (mock *MockHttpClient) Do(req *http.Request) (*http.Response, error) { + args := mock.Called(req) + return args.Get(0).(*http.Response), args.Error(1) +} + +// setups up a client object for tests. +func setupClient(t *testing.T) *client { + lggr := logger.TestLogger(t) + mockHttpClient := new(MockHttpClient) + mercuryConfig := NewMockMercuryConfigProvider() + threadCtl := utils.NewThreadControl() + + client := NewClient( + mercuryConfig, + mockHttpClient, + threadCtl, + lggr, + ) + return client +} + +func TestV02_SingleFeedRequest(t *testing.T) { + upkeepId := big.NewInt(123456789) + tests := []struct { + name string + index int + lookup *mercury.StreamsLookup + blob string + statusCode int + lastStatusCode int + retryNumber int + retryable bool + errorMessage string + }{ + { + name: "success - mercury responds in the first try", + index: 0, + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + blob: "0xab2123dc00000012", + }, + { + name: "success - retry for 404", + index: 0, + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + blob: "0xab2123dcbabbad", + retryNumber: 1, + statusCode: http.StatusNotFound, + lastStatusCode: http.StatusOK, + }, + { + name: "success - retry for 500", + index: 0, + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + blob: "0xab2123dcbbabad", + retryNumber: 2, + statusCode: http.StatusInternalServerError, + lastStatusCode: http.StatusOK, + }, + { + name: "failure - returns retryable", + index: 0, + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + blob: "0xab2123dc", + retryNumber: totalAttempt, + statusCode: http.StatusNotFound, + retryable: true, + errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 404\n#2: 404\n#3: 404", + }, + { + name: "failure - returns retryable and then non-retryable", + index: 0, + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + blob: "0xab2123dc", + retryNumber: 1, + statusCode: http.StatusNotFound, + lastStatusCode: http.StatusTooManyRequests, + errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 404\n#2: at block 123456 upkeep 123456789 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + }, + { + name: "failure - returns not retryable", + index: 0, + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + blob: "0xab2123dc", + statusCode: http.StatusConflict, + errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 123456 upkeep 123456789 received status code 409 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := setupClient(t) + defer c.Close() + hc := new(MockHttpClient) + + mr := MercuryV02Response{ChainlinkBlob: tt.blob} + b, err := json.Marshal(mr) + assert.Nil(t, err) + + if tt.retryNumber == 0 { + if tt.errorMessage != "" { + resp := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } else { + resp := &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } + } else if tt.retryNumber > 0 && tt.retryNumber < totalAttempt { + retryResp := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(retryResp, nil).Times(tt.retryNumber) + + resp := &http.Response{ + StatusCode: tt.lastStatusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } else { + resp := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Times(tt.retryNumber) + } + c.httpClient = hc + + ch := make(chan mercury.MercuryData, 1) + c.singleFeedRequest(context.Background(), ch, tt.index, tt.lookup) + + m := <-ch + assert.Equal(t, tt.index, m.Index) + assert.Equal(t, tt.retryable, m.Retryable) + if tt.retryNumber >= totalAttempt || tt.errorMessage != "" { + assert.Equal(t, tt.errorMessage, m.Error.Error()) + assert.Equal(t, [][]byte{}, m.Bytes) + } else { + blobBytes, err := hexutil.Decode(tt.blob) + assert.Nil(t, err) + assert.Nil(t, m.Error) + assert.Equal(t, [][]byte{blobBytes}, m.Bytes) + } + }) + } +} + +func TestV02_DoMercuryRequestV02(t *testing.T) { + upkeepId, _ := new(big.Int).SetString("88786950015966611018675766524283132478093844178961698330929478019253453382042", 10) + + tests := []struct { + name string + lookup *mercury.StreamsLookup + mockHttpStatusCode int + mockChainlinkBlobs []string + pluginRetries int + pluginRetryKey string + expectedValues [][]byte + expectedRetryable bool + expectedRetryInterval time.Duration + expectedError error + state mercury.MercuryUpkeepState + reason mercury.MercuryUpkeepFailureReason + }{ + { + name: "success", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + mockHttpStatusCode: http.StatusOK, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{{0, 6, 109, 252, 209, 237, 45, 149, 177, 140, 148, 141, 188, 91, 214, 76, 104, 122, 254, 147, 228, 202, 125, 102, 61, 222, 193, 76, 32, 9, 10, 216, 0, 0, 0, 0, 0, 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, 20, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 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, 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, 2, 128, 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, 0, 0, 0, 0, 0, 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, 69, 84, 72, 45, 85, 83, 68, 45, 65, 82, 66, 73, 84, 82, 85, 77, 45, 84, 69, 83, 84, 78, 69, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 216, 211, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 207, 11, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 155, 61, 164, 0, 0, 0, 0, 0, 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, 138, 231, 206, 116, 217, 250, 37, 42, 137, 131, 151, 110, 171, 96, 13, 199, 89, 12, 119, 141, 4, 129, 52, 48, 132, 27, 198, 231, 101, 195, 76, 216, 26, 22, 141, 0, 0, 0, 0, 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, 138, 231, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 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, 96, 65, 43, 148, 229, 37, 202, 108, 237, 201, 245, 68, 253, 134, 247, 118, 6, 213, 47, 231, 49, 165, 208, 105, 219, 232, 54, 168, 191, 192, 251, 140, 145, 25, 99, 176, 174, 122, 20, 151, 31, 59, 70, 33, 191, 251, 128, 46, 240, 96, 83, 146, 185, 166, 200, 156, 127, 171, 29, 248, 99, 58, 90, 222, 0, 0, 0, 0, 0, 0, 0, 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, 69, 0, 194, 245, 33, 248, 63, 186, 94, 252, 43, 243, 239, 250, 174, 221, 228, 61, 10, 74, 223, 247, 133, 193, 33, 59, 113, 42, 58, 237, 13, 129, 87, 100, 42, 132, 50, 77, 176, 207, 150, 149, 235, 210, 119, 8, 212, 96, 142, 176, 51, 126, 13, 216, 123, 14, 67, 240, 250, 112, 199, 0, 217, 17}}, + expectedRetryable: false, + expectedError: nil, + }, + { + name: "failure - retryable and interval is 1s", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + mockHttpStatusCode: http.StatusInternalServerError, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{nil}, + expectedRetryable: true, + pluginRetries: 0, + expectedRetryInterval: 1 * time.Second, + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), + state: mercury.MercuryFlakyFailure, + }, + { + name: "failure - retryable and interval is 5s", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + pluginRetries: 5, + mockHttpStatusCode: http.StatusInternalServerError, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{nil}, + expectedRetryable: true, + expectedRetryInterval: 5 * time.Second, + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), + state: mercury.MercuryFlakyFailure, + }, + { + name: "failure - not retryable because there are many plugin retries already", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + pluginRetries: 10, + mockHttpStatusCode: http.StatusInternalServerError, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{nil}, + expectedRetryable: true, + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), + state: mercury.MercuryFlakyFailure, + }, + { + name: "failure - not retryable", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + mockHttpStatusCode: http.StatusTooManyRequests, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{nil}, + expectedRetryable: false, + expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 25880526 upkeep 88786950015966611018675766524283132478093844178961698330929478019253453382042 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"), + state: mercury.InvalidMercuryRequest, + }, + { + name: "failure - no feeds", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIdHex, + Feeds: []string{}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + expectedValues: [][]byte{}, + reason: mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, + }, + { + name: "failure - invalid revert data", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + expectedValues: [][]byte{}, + reason: mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := setupClient(t) + defer c.Close() + if tt.pluginRetries != 0 { + c.mercuryConfig.SetPluginRetry(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) + } + hc := new(MockHttpClient) + + for _, blob := range tt.mockChainlinkBlobs { + mr := MercuryV02Response{ChainlinkBlob: blob} + b, err := json.Marshal(mr) + assert.Nil(t, err) + + resp := &http.Response{ + StatusCode: tt.mockHttpStatusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + if tt.expectedError != nil && tt.expectedRetryable || tt.pluginRetries > 0 { + hc.On("Do", mock.Anything).Return(resp, nil).Times(totalAttempt) + } else { + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } + } + c.httpClient = hc + + state, reason, values, retryable, retryInterval, reqErr := c.DoRequest(context.Background(), tt.lookup, tt.pluginRetryKey) + assert.Equal(t, tt.expectedValues, values) + assert.Equal(t, tt.expectedRetryable, retryable) + if retryable { + newRetries, _ := c.mercuryConfig.GetPluginRetry(tt.pluginRetryKey) + assert.Equal(t, tt.pluginRetries+1, newRetries.(int)) + } + assert.Equal(t, tt.expectedRetryInterval, retryInterval) + assert.Equal(t, tt.state, state) + assert.Equal(t, tt.reason, reason) + if tt.expectedError != nil { + assert.True(t, strings.HasPrefix(reqErr.Error(), "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000")) + } + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go new file mode 100644 index 00000000000..3697dca53cd --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go @@ -0,0 +1,252 @@ +package v03 + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strconv" + "strings" + "time" + + "github.com/avast/retry-go/v4" + "github.com/ethereum/go-ethereum/common/hexutil" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +const ( + mercuryBatchPathV03 = "/api/v1/reports/bulk?" // only used to access mercury v0.3 server + mercuryBatchPathV03BlockNumber = "/api/v1gmx/reports/bulk?" // only used to access mercury v0.3 server with blockNumber + retryDelay = 500 * time.Millisecond + totalAttempt = 3 + contentTypeHeader = "Content-Type" + authorizationHeader = "Authorization" + timestampHeader = "X-Authorization-Timestamp" + signatureHeader = "X-Authorization-Signature-SHA256" + upkeepIDHeader = "X-Authorization-Upkeep-Id" +) + +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 +} + +type client struct { + utils.StartStopOnce + mercuryConfig mercury.MercuryConfigProvider + httpClient mercury.HttpClient + threadCtrl utils.ThreadControl + lggr logger.Logger +} + +func NewClient(mercuryConfig mercury.MercuryConfigProvider, httpClient mercury.HttpClient, threadCtrl utils.ThreadControl, lggr logger.Logger) *client { + return &client{ + mercuryConfig: mercuryConfig, + httpClient: httpClient, + threadCtrl: threadCtrl, + lggr: lggr, + } +} + +func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (mercury.MercuryUpkeepState, mercury.MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) { + resultLen := len(streamsLookup.Feeds) + ch := make(chan mercury.MercuryData, resultLen) + if len(streamsLookup.Feeds) == 0 { + return 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", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) + } + resultLen = 1 + c.threadCtrl.Go(func(ctx context.Context) { + c.multiFeedsRequest(ctx, ch, streamsLookup) + }) + + var reqErr error + var retryInterval time.Duration + results := make([][]byte, len(streamsLookup.Feeds)) + retryable := true + allSuccess := true + state := mercury.NoPipelineError + + for i := 0; i < resultLen; i++ { + m := <-ch + if m.Error != nil { + reqErr = errors.Join(reqErr, m.Error) + retryable = retryable && m.Retryable + allSuccess = false + if m.State != mercury.NoPipelineError { + state = m.State + } + continue + } + results = m.Bytes + } + // only retry when not all successful AND none are not retryable + if retryable && !allSuccess { + retryInterval = mercury.CalculateRetryConfigFn(pluginRetryKey, c.mercuryConfig) + } + // only retry when not all successful AND none are not retryable + return state, mercury.MercuryUpkeepFailureReasonNone, results, retryable && !allSuccess, retryInterval, reqErr +} + +func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.MercuryData, sl *mercury.StreamsLookup) { + // this won't work bc q.Encode() will encode commas as '%2C' but the server is strictly expecting a comma separated list + //q := url.Values{ + // feedIDs: {strings.Join(sl.Feeds, ",")}, + // timestamp: {sl.Time.String()}, + //} + + params := fmt.Sprintf("%s=%s&%s=%s", mercury.FeedIDs, strings.Join(sl.Feeds, ","), mercury.Timestamp, sl.Time.String()) + batchPathV03 := mercuryBatchPathV03 + if sl.IsMercuryUsingBatchPathV03() { + batchPathV03 = mercuryBatchPathV03BlockNumber + } + reqUrl := fmt.Sprintf("%s%s%s", c.mercuryConfig.Credentials().URL, batchPathV03, params) + + c.lggr.Debugf("request URL for upkeep %s userId %s: %s", sl.UpkeepId.String(), c.mercuryConfig.Credentials().Username, reqUrl) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) + if err != nil { + ch <- mercury.MercuryData{Index: 0, Error: err, Retryable: false, State: mercury.InvalidMercuryRequest} + return + } + + ts := time.Now().UTC().UnixMilli() + signature := mercury.GenerateHMACFn(http.MethodGet, mercuryBatchPathV03+params, []byte{}, c.mercuryConfig.Credentials().Username, c.mercuryConfig.Credentials().Password, ts) + + req.Header.Set(contentTypeHeader, "application/json") + // username here is often referred to as user id + req.Header.Set(authorizationHeader, c.mercuryConfig.Credentials().Username) + req.Header.Set(timestampHeader, strconv.FormatInt(ts, 10)) + req.Header.Set(signatureHeader, 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(upkeepIDHeader, sl.UpkeepId.String()) + + // in the case of multiple retries here, use the last attempt's data + state := mercury.NoPipelineError + retryable := false + sent := false + retryErr := retry.Do( + func() error { + retryable = false + resp, err := c.httpClient.Do(req) + if err != nil { + c.lggr.Warnf("at timestamp %s upkeep %s GET request fails from mercury v0.3: %v", sl.Time.String(), sl.UpkeepId.String(), err) + retryable = true + state = mercury.MercuryFlakyFailure + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + retryable = false + state = mercury.InvalidMercuryResponse + return err + } + + c.lggr.Infof("at timestamp %s upkeep %s received status code %d from mercury v0.3", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) + switch resp.StatusCode { + case http.StatusUnauthorized: + retryable = false + state = mercury.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", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) + case http.StatusBadRequest: + retryable = false + state = mercury.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", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) + case http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout: + retryable = true + state = mercury.MercuryFlakyFailure + return fmt.Errorf("%d", resp.StatusCode) + case http.StatusPartialContent: + // TODO (AUTO-5044): handle response code 206 entirely with errors field parsing + c.lggr.Warnf("at timestamp %s upkeep %s requested [%s] feeds but mercury v0.3 server returned 206 status, treating it as 404 and retrying", sl.Time.String(), sl.UpkeepId.String(), sl.Feeds) + retryable = true + state = mercury.MercuryFlakyFailure + return fmt.Errorf("%d", http.StatusPartialContent) + case http.StatusOK: + // continue + default: + retryable = false + state = mercury.InvalidMercuryRequest + return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode) + } + c.lggr.Debugf("at block %s upkeep %s received status code %d from mercury v0.3 with BODY=%s", sl.Time.String(), sl.UpkeepId.String(), resp.StatusCode, hexutil.Encode(body)) + + var response MercuryV03Response + if err := json.Unmarshal(body, &response); err != nil { + c.lggr.Warnf("at timestamp %s upkeep %s failed to unmarshal body to MercuryV03Response from mercury v0.3: %v", sl.Time.String(), sl.UpkeepId.String(), err) + retryable = false + state = mercury.MercuryUnmarshalError + return err + } + + // 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(sl.Feeds) { + var receivedFeeds []string + for _, f := range response.Reports { + receivedFeeds = append(receivedFeeds, f.FeedID) + } + c.lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server returned 206 status with [%s] reports while we requested [%s] feeds, retrying", sl.Time.String(), sl.UpkeepId.String(), receivedFeeds, sl.Feeds) + retryable = true + state = mercury.MercuryFlakyFailure + return fmt.Errorf("%d", http.StatusNotFound) + } + var reportBytes [][]byte + for _, rsp := range response.Reports { + b, err := hexutil.Decode(rsp.FullReport) + if err != nil { + c.lggr.Warnf("at timestamp %s upkeep %s failed to decode reportBlob %s: %v", sl.Time.String(), sl.UpkeepId.String(), rsp.FullReport, err) + retryable = false + state = mercury.InvalidMercuryResponse + return err + } + reportBytes = append(reportBytes, b) + } + ch <- mercury.MercuryData{ + Index: 0, + Bytes: reportBytes, + Retryable: false, + State: mercury.NoPipelineError, + } + sent = true + return nil + }, + // only retry when the error is 206 Partial Content, 404 Not Found, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout + retry.RetryIf(func(err error) bool { + return err.Error() == fmt.Sprintf("%d", http.StatusPartialContent) || err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) || err.Error() == fmt.Sprintf("%d", http.StatusBadGateway) || err.Error() == fmt.Sprintf("%d", http.StatusServiceUnavailable) || err.Error() == fmt.Sprintf("%d", http.StatusGatewayTimeout) + }), + retry.Context(ctx), + retry.Delay(retryDelay), + retry.Attempts(totalAttempt), + ) + + if !sent { + ch <- mercury.MercuryData{ + Index: 0, + Bytes: [][]byte{}, + Retryable: retryable, + Error: retryErr, + State: state, + } + } +} + +func (c *client) Close() error { + return c.StopOnce("v03_request", func() error { + c.threadCtrl.Close() + return nil + }) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go new file mode 100644 index 00000000000..bef2cdac58a --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go @@ -0,0 +1,536 @@ +package v03 + +import ( + "bytes" + "context" + "encoding/json" + "io" + "math/big" + "net/http" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/patrickmn/go-cache" + "github.com/stretchr/testify/mock" + + "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/evm21/mocks" + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" +) + +const ( + defaultPluginRetryExpiration = 30 * time.Minute + cleanupInterval = 5 * time.Minute +) + +type MockMercuryConfigProvider struct { + cache *cache.Cache + mock.Mock +} + +func NewMockMercuryConfigProvider() *MockMercuryConfigProvider { + return &MockMercuryConfigProvider{ + cache: cache.New(defaultPluginRetryExpiration, cleanupInterval), + } +} + +func (m *MockMercuryConfigProvider) Credentials() *models.MercuryCredentials { + mc := &models.MercuryCredentials{ + LegacyURL: "https://google.old.com", + URL: "https://google.com", + Username: "FakeClientID", + Password: "FakeClientKey", + } + return mc +} + +func (m *MockMercuryConfigProvider) IsUpkeepAllowed(s string) (interface{}, bool) { + args := m.Called(s) + return args.Get(0), args.Bool(1) +} + +func (m *MockMercuryConfigProvider) SetUpkeepAllowed(s string, i interface{}, d time.Duration) { + m.Called(s, i, d) +} + +func (m *MockMercuryConfigProvider) GetPluginRetry(s string) (interface{}, bool) { + if value, found := m.cache.Get(s); found { + return value, true + } + + return nil, false +} + +func (m *MockMercuryConfigProvider) SetPluginRetry(s string, i interface{}, d time.Duration) { + m.cache.Set(s, i, d) +} + +type MockHttpClient struct { + mock.Mock +} + +func (mock *MockHttpClient) Do(req *http.Request) (*http.Response, error) { + args := mock.Called(req) + return args.Get(0).(*http.Response), args.Error(1) +} + +// setups up a client object for tests. +func setupClient(t *testing.T) *client { + lggr := logger.TestLogger(t) + mockHttpClient := new(MockHttpClient) + mercuryConfig := NewMockMercuryConfigProvider() + threadCtl := utils.NewThreadControl() + + client := NewClient( + mercuryConfig, + mockHttpClient, + threadCtl, + lggr, + ) + return client +} + +func TestV03_DoMercuryRequestV03(t *testing.T) { + upkeepId, _ := new(big.Int).SetString("88786950015966611018675766524283132478093844178961698330929478019253453382042", 10) + + tests := []struct { + name string + lookup *mercury.StreamsLookup + mockHttpStatusCode int + mockChainlinkBlobs []string + pluginRetryKey string + expectedValues [][]byte + expectedRetryable bool + expectedRetryInterval time.Duration + expectedError error + state mercury.MercuryUpkeepState + reason mercury.MercuryUpkeepFailureReason + }{ + { + name: "success v0.3", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(25880526), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + UpkeepId: upkeepId, + }, + mockHttpStatusCode: http.StatusOK, + mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, + expectedValues: [][]byte{{0, 6, 109, 252, 209, 237, 45, 149, 177, 140, 148, 141, 188, 91, 214, 76, 104, 122, 254, 147, 228, 202, 125, 102, 61, 222, 193, 76, 32, 9, 10, 216, 0, 0, 0, 0, 0, 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, 20, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 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, 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, 2, 128, 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, 0, 0, 0, 0, 0, 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, 69, 84, 72, 45, 85, 83, 68, 45, 65, 82, 66, 73, 84, 82, 85, 77, 45, 84, 69, 83, 84, 78, 69, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 216, 211, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 207, 11, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 155, 61, 164, 0, 0, 0, 0, 0, 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, 138, 231, 206, 116, 217, 250, 37, 42, 137, 131, 151, 110, 171, 96, 13, 199, 89, 12, 119, 141, 4, 129, 52, 48, 132, 27, 198, 231, 101, 195, 76, 216, 26, 22, 141, 0, 0, 0, 0, 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, 138, 231, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 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, 96, 65, 43, 148, 229, 37, 202, 108, 237, 201, 245, 68, 253, 134, 247, 118, 6, 213, 47, 231, 49, 165, 208, 105, 219, 232, 54, 168, 191, 192, 251, 140, 145, 25, 99, 176, 174, 122, 20, 151, 31, 59, 70, 33, 191, 251, 128, 46, 240, 96, 83, 146, 185, 166, 200, 156, 127, 171, 29, 248, 99, 58, 90, 222, 0, 0, 0, 0, 0, 0, 0, 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, 69, 0, 194, 245, 33, 248, 63, 186, 94, 252, 43, 243, 239, 250, 174, 221, 228, 61, 10, 74, 223, 247, 133, 193, 33, 59, 113, 42, 58, 237, 13, 129, 87, 100, 42, 132, 50, 77, 176, 207, 150, 149, 235, 210, 119, 8, 212, 96, 142, 176, 51, 126, 13, 216, 123, 14, 67, 240, 250, 112, 199, 0, 217, 17}}, + expectedRetryable: false, + expectedError: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := setupClient(t) + defer c.Close() + hc := mocks.NewHttpClient(t) + + mr := MercuryV03Response{} + for i, blob := range tt.mockChainlinkBlobs { + r := MercuryV03Report{ + FeedID: tt.lookup.Feeds[i], + ValidFromTimestamp: 0, + ObservationsTimestamp: 0, + FullReport: blob, + } + mr.Reports = append(mr.Reports, r) + } + + b, err := json.Marshal(mr) + assert.Nil(t, err) + resp := &http.Response{ + StatusCode: tt.mockHttpStatusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + if tt.expectedError != nil && tt.expectedRetryable { + hc.On("Do", mock.Anything).Return(resp, nil).Times(totalAttempt) + } else { + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } + c.httpClient = hc + + state, reason, values, retryable, retryInterval, reqErr := c.DoRequest(context.Background(), tt.lookup, tt.pluginRetryKey) + + assert.Equal(t, tt.expectedValues, values) + assert.Equal(t, tt.expectedRetryable, retryable) + assert.Equal(t, tt.expectedRetryInterval, retryInterval) + assert.Equal(t, tt.state, state) + assert.Equal(t, tt.reason, reason) + if tt.expectedError != nil { + assert.Equal(t, tt.expectedError.Error(), reqErr.Error()) + } + }) + } +} + +func TestV03_MultiFeedRequest(t *testing.T) { + upkeepId := big.NewInt(123456789) + tests := []struct { + name string + lookup *mercury.StreamsLookup + statusCode int + lastStatusCode int + pluginRetries int + pluginRetryKey string + retryNumber int + retryable bool + errorMessage string + firstResponse *MercuryV03Response + response *MercuryV03Response + }{ + { + name: "success - mercury responds in the first try", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000016", + }, + }, + }, + statusCode: http.StatusOK, + }, + { + name: "success - mercury responds in the first try with blocknumber", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.BlockNumber, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000016", + }, + }, + }, + statusCode: http.StatusOK, + }, + { + name: "success - retry 206", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + firstResponse: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + }, + }, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000019", + }, + }, + }, + retryNumber: 1, + statusCode: http.StatusPartialContent, + lastStatusCode: http.StatusOK, + }, + { + name: "success - retry for 500", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + retryNumber: 2, + statusCode: http.StatusInternalServerError, + lastStatusCode: http.StatusOK, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000019", + }, + }, + }, + }, + { + name: "failure - fail to decode reportBlob", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "qerwiu", // invalid hex blob + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000016", + }, + }, + }, + statusCode: http.StatusOK, + retryable: false, + errorMessage: "All attempts fail:\n#1: hex string without 0x prefix", + }, + { + name: "failure - returns retryable with 1s plugin retry interval", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + retryNumber: totalAttempt, + statusCode: http.StatusInternalServerError, + retryable: true, + errorMessage: "All attempts fail:\n#1: 500\n#2: 500\n#3: 500", + }, + { + name: "failure - returns retryable with 5s plugin retry interval", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + pluginRetries: 6, + retryNumber: totalAttempt, + statusCode: http.StatusInternalServerError, + retryable: true, + errorMessage: "All attempts fail:\n#1: 500\n#2: 500\n#3: 500", + }, + { + name: "failure - returns retryable and then non-retryable", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + retryNumber: 1, + statusCode: http.StatusInternalServerError, + lastStatusCode: http.StatusUnauthorized, + errorMessage: "All attempts fail:\n#1: 500\n#2: at timestamp 123456 upkeep 123456789 received status code 401 from mercury v0.3, most likely this is caused by unauthorized upkeep", + }, + { + name: "failure - returns status code 422 not retryable", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + statusCode: http.StatusUnprocessableEntity, + errorMessage: "All attempts fail:\n#1: at timestamp 123456 upkeep 123456789 received status code 422 from mercury v0.3", + }, + { + name: "success - retry when reports length does not match feeds length", + lookup: &mercury.StreamsLookup{ + StreamsLookupError: &mercury.StreamsLookupError{ + FeedParamKey: mercury.FeedIDs, + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: mercury.Timestamp, + Time: big.NewInt(123456), + }, + UpkeepId: upkeepId, + }, + firstResponse: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + }, + }, + response: &MercuryV03Response{ + Reports: []MercuryV03Report{ + { + FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123456, + ObservationsTimestamp: 123456, + FullReport: "0xab2123dc00000012", + }, + { + FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", + ValidFromTimestamp: 123458, + ObservationsTimestamp: 123458, + FullReport: "0xab2123dc00000019", + }, + }, + }, + retryNumber: 1, + statusCode: http.StatusOK, + lastStatusCode: http.StatusOK, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := setupClient(t) + defer c.Close() + if tt.pluginRetries != 0 { + c.mercuryConfig.SetPluginRetry(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) + } + + hc := new(MockHttpClient) + b, err := json.Marshal(tt.response) + assert.Nil(t, err) + + if tt.retryNumber == 0 { + resp := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } else if tt.retryNumber < totalAttempt { + if tt.firstResponse != nil && tt.response != nil { + b0, err := json.Marshal(tt.firstResponse) + assert.Nil(t, err) + resp0 := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b0)), + } + b1, err := json.Marshal(tt.response) + assert.Nil(t, err) + resp1 := &http.Response{ + StatusCode: tt.lastStatusCode, + Body: io.NopCloser(bytes.NewReader(b1)), + } + hc.On("Do", mock.Anything).Return(resp0, nil).Once().On("Do", mock.Anything).Return(resp1, nil).Once() + } else { + retryResp := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(retryResp, nil).Times(tt.retryNumber) + + resp := &http.Response{ + StatusCode: tt.lastStatusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Once() + } + } else { + resp := &http.Response{ + StatusCode: tt.statusCode, + Body: io.NopCloser(bytes.NewReader(b)), + } + hc.On("Do", mock.Anything).Return(resp, nil).Times(tt.retryNumber) + } + c.httpClient = hc + + ch := make(chan mercury.MercuryData, 1) + c.multiFeedsRequest(context.Background(), ch, tt.lookup) + + m := <-ch + assert.Equal(t, 0, m.Index) + assert.Equal(t, tt.retryable, m.Retryable) + if tt.retryNumber >= totalAttempt || tt.errorMessage != "" { + assert.Equal(t, tt.errorMessage, m.Error.Error()) + assert.Equal(t, [][]byte{}, m.Bytes) + } else { + assert.Nil(t, m.Error) + var reports [][]byte + for _, rsp := range tt.response.Reports { + b, _ := hexutil.Decode(rsp.FullReport) + reports = append(reports, b) + } + assert.Equal(t, reports, m.Bytes) + } + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 73e2bc0a9c0..252d2d91c79 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -17,9 +17,9 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -42,8 +42,6 @@ const ( // cleanupInterval decides when the expired items in cache will be deleted. cleanupInterval = 5 * time.Minute logTriggerRefreshBatchSize = 32 - totalFastPluginRetries = 5 - totalMediumPluginRetries = 10 ) var ( @@ -89,30 +87,34 @@ func NewEvmRegistry( blockSub *BlockSubscriber, finalityDepth uint32, ) *EvmRegistry { + mercuryConfig := &MercuryConfig{ + cred: mc, + Abi: core.StreamsCompatibleABI, + AllowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), + pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), + } + + hc := http.DefaultClient + return &EvmRegistry{ - ctx: context.Background(), - threadCtrl: utils.NewThreadControl(), - lggr: lggr.Named(RegistryServiceName), - poller: client.LogPoller(), - addr: addr, - client: client.Client(), - logProcessed: make(map[string]bool), - registry: registry, - abi: core.RegistryABI, - active: al, - packer: packer, - headFunc: func(ocr2keepers.BlockKey) {}, - chLog: make(chan logpoller.Log, 1000), - mercury: &MercuryConfig{ - cred: mc, - abi: core.StreamsCompatibleABI, - allowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), - pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), - }, - hc: http.DefaultClient, + ctx: context.Background(), + threadCtrl: utils.NewThreadControl(), + lggr: lggr.Named(RegistryServiceName), + poller: client.LogPoller(), + addr: addr, + client: client.Client(), + logProcessed: make(map[string]bool), + registry: registry, + abi: core.RegistryABI, + active: al, + packer: packer, + headFunc: func(ocr2keepers.BlockKey) {}, + chLog: make(chan logpoller.Log, 1000), + hc: hc, logEventProvider: logEventProvider, bs: blockSub, finalityDepth: finalityDepth, + streams: streams.NewStreamsLookup(packer, mercuryConfig, blockSub, client.Client(), registry, lggr), } } @@ -128,15 +130,43 @@ var upkeepStateEvents = []common.Hash{ type MercuryConfig struct { cred *models.MercuryCredentials - abi abi.ABI - // allowListCache stores the upkeeps privileges. In 2.1, this only includes a JSON bytes for allowed to use mercury - allowListCache *cache.Cache - + Abi abi.ABI + // AllowListCache stores the upkeeps privileges. In 2.1, this only includes a JSON bytes for allowed to use mercury + AllowListCache *cache.Cache pluginRetryCache *cache.Cache } +func NewMercuryConfig(credentials *models.MercuryCredentials, abi abi.ABI) *MercuryConfig { + return &MercuryConfig{ + cred: credentials, + Abi: abi, + AllowListCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), + pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), + } +} + +func (c *MercuryConfig) Credentials() *models.MercuryCredentials { + return c.cred +} + +func (c *MercuryConfig) IsUpkeepAllowed(k string) (interface{}, bool) { + return c.AllowListCache.Get(k) +} + +func (c *MercuryConfig) SetUpkeepAllowed(k string, v interface{}, d time.Duration) { + c.AllowListCache.Set(k, v, d) +} + +func (c *MercuryConfig) GetPluginRetry(k string) (interface{}, bool) { + return c.pluginRetryCache.Get(k) +} + +func (c *MercuryConfig) SetPluginRetry(k string, v interface{}, d time.Duration) { + c.pluginRetryCache.Set(k, v, d) +} + type EvmRegistry struct { - services.StateMachine + utils.StartStopOnce threadCtrl utils.ThreadControl lggr logger.Logger poller logpoller.LogPoller @@ -158,6 +188,7 @@ type EvmRegistry struct { bs *BlockSubscriber logEventProvider logprovider.LogEventProvider finalityDepth uint32 + streams streams.Lookup } func (r *EvmRegistry) Name() string { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go index c9752ea14db..ad31c8feb06 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go @@ -70,7 +70,7 @@ func (r *EvmRegistry) doCheck(ctx context.Context, keys []ocr2keepers.UpkeepPayl return } - upkeepResults = r.streamsLookup(ctx, upkeepResults) + upkeepResults = r.streams.Lookup(ctx, upkeepResults) upkeepResults, err = r.simulatePerformUpkeeps(ctx, upkeepResults) if err != nil { @@ -104,7 +104,7 @@ func (r *EvmRegistry) getBlockHash(blockNumber *big.Int) (common.Hash, error) { } // verifyCheckBlock checks that the check block and hash are valid, returns the pipeline execution state and retryable -func (r *EvmRegistry) verifyCheckBlock(_ context.Context, checkBlock, upkeepId *big.Int, checkHash common.Hash) (state encoding.PipelineExecutionState, retryable bool) { +func (r *EvmRegistry) verifyCheckBlock(_ context.Context, checkBlock, upkeepId *big.Int, checkHash common.Hash) (state uint8, retryable bool) { // verify check block number and hash are valid h, ok := r.bs.queryBlocksMap(checkBlock.Int64()) // if this block number/hash combo exists in block subscriber, this check block and hash still exist on chain and are valid @@ -127,7 +127,7 @@ func (r *EvmRegistry) verifyCheckBlock(_ context.Context, checkBlock, upkeepId * } // verifyLogExists checks that the log still exists on chain, returns failure reason, pipeline error, and retryable -func (r *EvmRegistry) verifyLogExists(upkeepId *big.Int, p ocr2keepers.UpkeepPayload) (encoding.UpkeepFailureReason, encoding.PipelineExecutionState, bool) { +func (r *EvmRegistry) verifyLogExists(upkeepId *big.Int, p ocr2keepers.UpkeepPayload) (uint8, uint8, bool) { logBlockNumber := int64(p.Trigger.LogTriggerExtension.BlockNumber) logBlockHash := common.BytesToHash(p.Trigger.LogTriggerExtension.BlockHash[:]) checkBlockHash := common.BytesToHash(p.Trigger.BlockHash[:]) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go index 5ea2bdc6670..2e39892e478 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go @@ -4,12 +4,23 @@ import ( "context" "fmt" "math/big" + "strings" "sync/atomic" "testing" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/patrickmn/go-cache" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -81,7 +92,7 @@ func TestRegistry_VerifyCheckBlock(t *testing.T) { payload ocr2keepers.UpkeepPayload blocks map[int64]string poller logpoller.LogPoller - state encoding.PipelineExecutionState + state uint8 retryable bool makeEthCall bool }{ @@ -239,8 +250,8 @@ func TestRegistry_VerifyLogExists(t *testing.T) { payload ocr2keepers.UpkeepPayload blocks map[int64]string makeEthCall bool - reason encoding.UpkeepFailureReason - state encoding.PipelineExecutionState + reason uint8 + state uint8 retryable bool ethCallErr error receipt *types.Receipt @@ -645,5 +656,44 @@ func TestRegistry_SimulatePerformUpkeeps(t *testing.T) { assert.Equal(t, tc.err, err) }) } +} +// setups up an evm registry for tests. +func setupEVMRegistry(t *testing.T) *EvmRegistry { + lggr := logger.TestLogger(t) + addr := common.HexToAddress("0x6cA639822c6C241Fa9A7A6b5032F6F7F1C513CAD") + keeperRegistryABI, err := abi.JSON(strings.NewReader(i_keeper_registry_master_wrapper_2_1.IKeeperRegistryMasterABI)) + require.Nil(t, err, "need registry abi") + streamsLookupCompatibleABI, err := abi.JSON(strings.NewReader(streams_lookup_compatible_interface.StreamsLookupCompatibleInterfaceABI)) + require.Nil(t, err, "need mercury abi") + var logPoller logpoller.LogPoller + mockReg := mocks.NewRegistry(t) + mockHttpClient := mocks.NewHttpClient(t) + client := evmClientMocks.NewClient(t) + + r := &EvmRegistry{ + lggr: lggr, + poller: logPoller, + addr: addr, + client: client, + logProcessed: make(map[string]bool), + registry: mockReg, + abi: keeperRegistryABI, + active: NewActiveUpkeepList(), + packer: encoding.NewAbiPacker(), + headFunc: func(ocr2keepers.BlockKey) {}, + chLog: make(chan logpoller.Log, 1000), + mercury: &MercuryConfig{ + cred: &models.MercuryCredentials{ + LegacyURL: "https://google.old.com", + URL: "https://google.com", + Username: "FakeClientID", + Password: "FakeClientKey", + }, + Abi: streamsLookupCompatibleABI, + AllowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), + }, + hc: mockHttpClient, + } + return r } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go deleted file mode 100644 index af7ff42b930..00000000000 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go +++ /dev/null @@ -1,642 +0,0 @@ -package evm - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "math/big" - "net/http" - "net/url" - "strconv" - "strings" - "sync" - "time" - - "github.com/avast/retry-go/v4" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/patrickmn/go-cache" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" -) - -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" - mercuryPathV02 = "/client?" // only used to access mercury v0.2 server - mercuryBatchPathV03 = "/api/v1/reports/bulk?" // only used to access mercury v0.3 server - mercuryBatchPathV03BlockNumber = "/api/v1gmx/reports/bulk?" // only used to access mercury v0.3 server with blockNumber - retryDelay = 500 * time.Millisecond - timestamp = "timestamp" // valid for v0.3 - totalAttempt = 3 -) - -type StreamsLookup struct { - *encoding.StreamsLookupError - upkeepId *big.Int - block uint64 -} - -// 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 -} - -type MercuryData struct { - Index int - Error error - Retryable bool - Bytes [][]byte - State encoding.PipelineExecutionState -} - -// 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"` -} - -// streamsLookup looks through check upkeep results looking for any that need off chain lookup -func (r *EvmRegistry) streamsLookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult { - lggr := r.lggr.With("where", "StreamsLookup") - lookups := map[int]*StreamsLookup{} - for i, res := range checkResults { - if res.IneligibilityReason != uint8(encoding.UpkeepFailureReasonTargetCheckReverted) { - // Streams Lookup only works when upkeep target check reverts - continue - } - - block := big.NewInt(int64(res.Trigger.BlockNumber)) - upkeepId := res.UpkeepID - - // Try to decode the revert error into streams lookup format. User upkeeps can revert with any reason, see if they - // tried to call mercury - lggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) - streamsLookupErr, err := r.packer.DecodeStreamsLookupRequest(res.PerformData) - if err != nil { - lggr.Debugf("at block %d upkeep %s DecodeStreamsLookupRequest failed: %v", block, upkeepId, err) - // user contract did not revert with StreamsLookup error - continue - } - l := &StreamsLookup{StreamsLookupError: streamsLookupErr} - if r.mercury.cred == nil { - lggr.Errorf("at block %d upkeep %s tries to access mercury server but mercury credential is not configured", block, upkeepId) - continue - } - - if len(l.Feeds) == 0 { - checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput) - lggr.Debugf("at block %s upkeep %s has empty feeds array", block, upkeepId) - continue - } - // mercury permission checking for v0.3 is done by mercury server - if l.FeedParamKey == feedIdHex && l.TimeParamKey == blockNumber { - // check permission on the registry for mercury v0.2 - opts := r.buildCallOpts(ctx, block) - state, reason, retryable, allowed, err := r.allowedToUseMercury(opts, upkeepId.BigInt()) - if err != nil { - lggr.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) - checkResults[i].Retryable = retryable - continue - } - - if !allowed { - lggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonMercuryAccessNotAllowed) - continue - } - } else if l.FeedParamKey != feedIDs { - // if mercury version cannot be determined, set failure reason - lggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonInvalidRevertDataInput) - continue - } - - l.upkeepId = upkeepId.BigInt() - // the block here is exclusively used to call checkCallback at this block, not to be confused with the block number - // in the revert for mercury v0.2, which is denoted by time in the struct bc starting from v0.3, only timestamp will be supported - l.block = uint64(block.Int64()) - lggr.Infof("at block %d upkeep %s DecodeStreamsLookupRequest feedKey=%s timeKey=%s feeds=%v time=%s extraData=%s", block, upkeepId, l.FeedParamKey, l.TimeParamKey, l.Feeds, l.Time, hexutil.Encode(l.ExtraData)) - lookups[i] = l - } - - var wg sync.WaitGroup - - for i, lookup := range lookups { - wg.Add(1) - func(i int, lookup *StreamsLookup) { - r.threadCtrl.Go(func(ctx context.Context) { - r.doLookup(ctx, &wg, lookup, i, checkResults, lggr) - }) - }(i, lookup) - - } - - wg.Wait() - - // don't surface error to plugin bc StreamsLookup process should be self-contained. - return checkResults -} - -func (r *EvmRegistry) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *StreamsLookup, i int, checkResults []ocr2keepers.CheckResult, lggr logger.Logger) { - defer wg.Done() - - state, reason, values, retryable, ri, err := r.doMercuryRequest(ctx, lookup, generatePluginRetryKey(checkResults[i].WorkID, lookup.block), lggr) - if err != nil { - lggr.Errorf("upkeep %s retryable %v retryInterval %s doMercuryRequest: %s", lookup.upkeepId, retryable, ri, err.Error()) - checkResults[i].Retryable = retryable - checkResults[i].RetryInterval = ri - checkResults[i].PipelineExecutionState = uint8(state) - checkResults[i].IneligibilityReason = uint8(reason) - return - } - - for j, v := range values { - lggr.Infof("upkeep %s doMercuryRequest values[%d]: %s", lookup.upkeepId, j, hexutil.Encode(v)) - } - - state, retryable, mercuryBytes, err := r.checkCallback(ctx, values, lookup) - if err != nil { - 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 - } - lggr.Infof("checkCallback mercuryBytes=%s", hexutil.Encode(mercuryBytes)) - - state, needed, performData, failureReason, _, err := r.packer.UnpackCheckCallbackResult(mercuryBytes) - if err != nil { - lggr.Errorf("at block %d upkeep %s UnpackCheckCallbackResult err: %s", lookup.block, lookup.upkeepId, err.Error()) - checkResults[i].PipelineExecutionState = uint8(state) - return - } - - if failureReason == uint8(encoding.UpkeepFailureReasonMercuryCallbackReverted) { - checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonMercuryCallbackReverted) - lggr.Debugf("at block %d upkeep %s mercury callback reverts", lookup.block, lookup.upkeepId) - return - } - - if !needed { - checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonUpkeepNotNeeded) - lggr.Debugf("at block %d upkeep %s callback reports upkeep not needed", lookup.block, lookup.upkeepId) - return - } - - checkResults[i].IneligibilityReason = uint8(encoding.UpkeepFailureReasonNone) - checkResults[i].Eligible = true - checkResults[i].PerformData = performData - lggr.Infof("at block %d upkeep %s successful with perform data: %s", lookup.block, lookup.upkeepId, hexutil.Encode(performData)) -} - -// allowedToUseMercury retrieves upkeep's administrative offchain config and decode a mercuryEnabled bool to indicate if -// this upkeep is allowed to use Mercury service. -func (r *EvmRegistry) allowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (state encoding.PipelineExecutionState, reason encoding.UpkeepFailureReason, retryable bool, allow bool, err error) { - allowed, ok := r.mercury.allowListCache.Get(upkeepId.String()) - if ok { - return encoding.NoPipelineError, encoding.UpkeepFailureReasonNone, false, allowed.(bool), nil - } - - payload, err := r.packer.PackGetUpkeepPrivilegeConfig(upkeepId) - if err != nil { - // pack error, no retryable - r.lggr.Warnf("failed to pack getUpkeepPrivilegeConfig data for upkeepId %s: %s", upkeepId, err) - - return encoding.PackUnpackDecodeFailed, encoding.UpkeepFailureReasonNone, false, false, fmt.Errorf("failed to pack upkeepId: %w", err) - } - - var resultBytes hexutil.Bytes - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - err = r.client.CallContext(opts.Context, &resultBytes, "eth_call", args, hexutil.EncodeBig(opts.BlockNumber)) - if err != nil { - return encoding.RpcFlakyFailure, encoding.UpkeepFailureReasonNone, true, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) - } - - cfg, err := r.packer.UnpackGetUpkeepPrivilegeConfig(resultBytes) - if err != nil { - return encoding.PackUnpackDecodeFailed, encoding.UpkeepFailureReasonNone, false, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) - } - - if len(cfg) == 0 { - r.mercury.allowListCache.Set(upkeepId.String(), false, cache.DefaultExpiration) - return encoding.NoPipelineError, encoding.UpkeepFailureReasonMercuryAccessNotAllowed, false, false, fmt.Errorf("upkeep privilege config is empty") - } - - var privilegeConfig UpkeepPrivilegeConfig - if err := json.Unmarshal(cfg, &privilegeConfig); err != nil { - return encoding.MercuryUnmarshalError, encoding.UpkeepFailureReasonNone, false, false, fmt.Errorf("failed to unmarshal privilege config: %v", err) - } - - r.mercury.allowListCache.Set(upkeepId.String(), privilegeConfig.MercuryEnabled, cache.DefaultExpiration) - - return encoding.NoPipelineError, encoding.UpkeepFailureReasonNone, false, privilegeConfig.MercuryEnabled, nil -} - -func (r *EvmRegistry) checkCallback(ctx context.Context, values [][]byte, lookup *StreamsLookup) (encoding.PipelineExecutionState, bool, hexutil.Bytes, error) { - payload, err := r.abi.Pack("checkCallback", lookup.upkeepId, values, lookup.ExtraData) - if err != nil { - return encoding.PackUnpackDecodeFailed, false, nil, err - } - - var b hexutil.Bytes - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - - // call checkCallback function at the block which OCR3 has agreed upon - err = r.client.CallContext(ctx, &b, "eth_call", args, hexutil.EncodeUint64(lookup.block)) - if err != nil { - return encoding.RpcFlakyFailure, true, nil, err - } - return encoding.NoPipelineError, false, b, nil -} - -// doMercuryRequest sends requests to Mercury API to retrieve mercury data. -func (r *EvmRegistry) doMercuryRequest(ctx context.Context, sl *StreamsLookup, prk string, lggr logger.Logger) (encoding.PipelineExecutionState, encoding.UpkeepFailureReason, [][]byte, bool, time.Duration, error) { - var isMercuryV03 bool - resultLen := len(sl.Feeds) - ch := make(chan MercuryData, resultLen) - if len(sl.Feeds) == 0 { - return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) - } - if sl.FeedParamKey == feedIdHex && sl.TimeParamKey == blockNumber { - // only mercury v0.2 - for i := range sl.Feeds { - i := i - r.threadCtrl.Go(func(ctx context.Context) { - r.singleFeedRequest(ctx, ch, i, sl, lggr) - }) - } - } else if sl.FeedParamKey == feedIDs { - // only mercury v0.3 - resultLen = 1 - isMercuryV03 = true - ch = make(chan MercuryData, resultLen) - r.threadCtrl.Go(func(ctx context.Context) { - r.multiFeedsRequest(ctx, ch, sl, lggr) - }) - } else { - return encoding.NoPipelineError, encoding.UpkeepFailureReasonInvalidRevertDataInput, [][]byte{}, false, 0 * time.Second, fmt.Errorf("invalid revert data input: feed param key %s, time param key %s, feeds %s", sl.FeedParamKey, sl.TimeParamKey, sl.Feeds) - } - - var reqErr error - var ri time.Duration - results := make([][]byte, len(sl.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 := encoding.NoPipelineError - for i := 0; i < resultLen; i++ { - m := <-ch - if m.Error != nil { - reqErr = errors.Join(reqErr, m.Error) - retryable = retryable && m.Retryable - allSuccess = false - if m.State != encoding.NoPipelineError { - state = m.State - } - continue - } - if isMercuryV03 { - results = m.Bytes - } else { - results[m.Index] = m.Bytes[0] - } - } - if retryable && !allSuccess { - ri = r.calculateRetryConfig(prk) - } - // only retry when not all successful AND none are not retryable - return state, encoding.UpkeepFailureReasonNone, results, retryable && !allSuccess, ri, reqErr -} - -// singleFeedRequest sends a v0.2 Mercury request for a single feed report. -func (r *EvmRegistry) singleFeedRequest(ctx context.Context, ch chan<- MercuryData, index int, sl *StreamsLookup, lggr logger.Logger) { - q := url.Values{ - sl.FeedParamKey: {sl.Feeds[index]}, - sl.TimeParamKey: {sl.Time.String()}, - } - mercuryURL := r.mercury.cred.LegacyURL - reqUrl := fmt.Sprintf("%s%s%s", mercuryURL, mercuryPathV02, q.Encode()) - lggr.Debugf("request URL for upkeep %s feed %s: %s", sl.upkeepId.String(), sl.Feeds[index], reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: index, Error: err, Retryable: false, State: encoding.InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := r.generateHMAC(http.MethodGet, mercuryPathV02+q.Encode(), []byte{}, r.mercury.cred.Username, r.mercury.cred.Password, ts) - req.Header.Set(headerContentType, applicationJson) - req.Header.Set(headerAuthorization, r.mercury.cred.Username) - 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 := encoding.NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := r.hc.Do(req) - if err1 != nil { - lggr.Warnf("at block %s upkeep %s GET request fails for feed %s: %v", sl.Time.String(), sl.upkeepId.String(), sl.Feeds[index], err1) - retryable = true - state = encoding.MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err = Body.Close() - if err != nil { - lggr.Warnf("failed to close mercury response Body: %s", err) - } - }(resp.Body) - - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = encoding.InvalidMercuryResponse - return err1 - } - - if resp.StatusCode == http.StatusNotFound || resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusBadGateway || resp.StatusCode == http.StatusServiceUnavailable || resp.StatusCode == http.StatusGatewayTimeout { - lggr.Warnf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, sl.Feeds[index]) - retryable = true - state = encoding.MercuryFlakyFailure - return errors.New(strconv.FormatInt(int64(resp.StatusCode), 10)) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = encoding.InvalidMercuryRequest - return fmt.Errorf("at block %s upkeep %s received status code %d for feed %s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, sl.Feeds[index]) - } - - lggr.Debugf("at block %s upkeep %s received status code %d from mercury v0.2 with BODY=%s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, hexutil.Encode(body)) - - var m MercuryV02Response - err1 = json.Unmarshal(body, &m) - if err1 != nil { - lggr.Warnf("at block %s upkeep %s failed to unmarshal body to MercuryV02Response for feed %s: %v", sl.Time.String(), sl.upkeepId.String(), sl.Feeds[index], err1) - retryable = false - state = encoding.MercuryUnmarshalError - return err1 - } - blobBytes, err1 := hexutil.Decode(m.ChainlinkBlob) - if err1 != nil { - lggr.Warnf("at block %s upkeep %s failed to decode chainlinkBlob %s for feed %s: %v", sl.Time.String(), sl.upkeepId.String(), m.ChainlinkBlob, sl.Feeds[index], err1) - retryable = false - state = encoding.InvalidMercuryResponse - return err1 - } - ch <- MercuryData{ - Index: index, - Bytes: [][]byte{blobBytes}, - Retryable: false, - State: encoding.NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 404 Not Found, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) || err.Error() == fmt.Sprintf("%d", http.StatusBadGateway) || err.Error() == fmt.Sprintf("%d", http.StatusServiceUnavailable) || err.Error() == fmt.Sprintf("%d", http.StatusGatewayTimeout) - }), - 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", sl.Feeds[index], retryErr), - State: state, - } - ch <- md - } -} - -// multiFeedsRequest sends a Mercury v0.3 request for a multi-feed report -func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryData, sl *StreamsLookup, lggr logger.Logger) { - // this won't work bc q.Encode() will encode commas as '%2C' but the server is strictly expecting a comma separated list - //q := url.Values{ - // feedIDs: {strings.Join(sl.Feeds, ",")}, - // timestamp: {sl.Time.String()}, - //} - params := fmt.Sprintf("%s=%s&%s=%s", feedIDs, strings.Join(sl.Feeds, ","), sl.TimeParamKey, sl.Time.String()) - batchPathV03 := mercuryBatchPathV03 - if sl.TimeParamKey == blockNumber { - batchPathV03 = mercuryBatchPathV03BlockNumber - } - reqUrl := fmt.Sprintf("%s%s%s", r.mercury.cred.URL, batchPathV03, params) - lggr.Debugf("request URL for upkeep %s userId %s: %s", sl.upkeepId.String(), r.mercury.cred.Username, reqUrl) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqUrl, nil) - if err != nil { - ch <- MercuryData{Index: 0, Error: err, Retryable: false, State: encoding.InvalidMercuryRequest} - return - } - - ts := time.Now().UTC().UnixMilli() - signature := r.generateHMAC(http.MethodGet, batchPathV03+params, []byte{}, r.mercury.cred.Username, r.mercury.cred.Password, ts) - req.Header.Set(headerContentType, applicationJson) - // username here is often referred to as user id - req.Header.Set(headerAuthorization, r.mercury.cred.Username) - 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, sl.upkeepId.String()) - - // in the case of multiple retries here, use the last attempt's data - state := encoding.NoPipelineError - retryable := false - sent := false - retryErr := retry.Do( - func() error { - retryable = false - resp, err1 := r.hc.Do(req) - if err1 != nil { - lggr.Warnf("at timestamp %s upkeep %s GET request fails from mercury v0.3: %v", sl.Time.String(), sl.upkeepId.String(), err1) - retryable = true - state = encoding.MercuryFlakyFailure - return err1 - } - defer func(Body io.ReadCloser) { - err = Body.Close() - if err != nil { - lggr.Warnf("failed to close mercury response Body: %s", err) - } - }(resp.Body) - - body, err1 := io.ReadAll(resp.Body) - if err1 != nil { - retryable = false - state = encoding.InvalidMercuryResponse - return err1 - } - - lggr.Infof("at timestamp %s upkeep %s received status code %d from mercury v0.3", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode) - if resp.StatusCode == http.StatusUnauthorized { - retryable = false - state = encoding.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", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode) - } else if resp.StatusCode == http.StatusBadRequest { - retryable = false - state = encoding.InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3 with message: %s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, string(body)) - } else if resp.StatusCode == http.StatusInternalServerError || resp.StatusCode == http.StatusBadGateway || resp.StatusCode == http.StatusServiceUnavailable || resp.StatusCode == http.StatusGatewayTimeout { - retryable = true - state = encoding.MercuryFlakyFailure - return fmt.Errorf("%d", resp.StatusCode) - } else if resp.StatusCode == http.StatusPartialContent { - // TODO (AUTO-5044): handle response code 206 entirely with errors field parsing - lggr.Warnf("at timestamp %s upkeep %s requested [%s] feeds but mercury v0.3 server returned 206 status, treating it as 404 and retrying", sl.Time.String(), sl.upkeepId.String(), sl.Feeds) - retryable = true - state = encoding.MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusPartialContent) - } else if resp.StatusCode != http.StatusOK { - retryable = false - state = encoding.InvalidMercuryRequest - return fmt.Errorf("at timestamp %s upkeep %s received status code %d from mercury v0.3", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode) - } - - lggr.Debugf("at block %s upkeep %s received status code %d from mercury v0.3 with BODY=%s", sl.Time.String(), sl.upkeepId.String(), resp.StatusCode, hexutil.Encode(body)) - - var response MercuryV03Response - err1 = json.Unmarshal(body, &response) - if err1 != nil { - lggr.Warnf("at timestamp %s upkeep %s failed to unmarshal body to MercuryV03Response from mercury v0.3: %v", sl.Time.String(), sl.upkeepId.String(), err1) - retryable = false - state = encoding.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(sl.Feeds) { - var receivedFeeds []string - for _, f := range response.Reports { - receivedFeeds = append(receivedFeeds, f.FeedID) - } - lggr.Warnf("at timestamp %s upkeep %s mercury v0.3 server returned 206 status with [%s] reports while we requested [%s] feeds, retrying", sl.Time.String(), sl.upkeepId.String(), receivedFeeds, sl.Feeds) - retryable = true - state = encoding.MercuryFlakyFailure - return fmt.Errorf("%d", http.StatusNotFound) - } - var reportBytes [][]byte - for _, rsp := range response.Reports { - b, err := hexutil.Decode(rsp.FullReport) - if err != nil { - lggr.Warnf("at timestamp %s upkeep %s failed to decode reportBlob %s: %v", sl.Time.String(), sl.upkeepId.String(), rsp.FullReport, err) - retryable = false - state = encoding.InvalidMercuryResponse - return err - } - reportBytes = append(reportBytes, b) - } - ch <- MercuryData{ - Index: 0, - Bytes: reportBytes, - Retryable: false, - State: encoding.NoPipelineError, - } - sent = true - return nil - }, - // only retry when the error is 206 Partial Content, 404 Not Found, 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout - retry.RetryIf(func(err error) bool { - return err.Error() == fmt.Sprintf("%d", http.StatusPartialContent) || err.Error() == fmt.Sprintf("%d", http.StatusNotFound) || err.Error() == fmt.Sprintf("%d", http.StatusInternalServerError) || err.Error() == fmt.Sprintf("%d", http.StatusBadGateway) || err.Error() == fmt.Sprintf("%d", http.StatusServiceUnavailable) || err.Error() == fmt.Sprintf("%d", http.StatusGatewayTimeout) - }), - 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 - } -} - -// generateHMAC calculates a user HMAC for Mercury server authentication. -func (r *EvmRegistry) 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 -} - -// calculateRetryConfig returns plugin retry interval based on how many times plugin has retried this work -func (r *EvmRegistry) calculateRetryConfig(prk string) time.Duration { - var ri time.Duration - var retries int - totalAttempts, ok := r.mercury.pluginRetryCache.Get(prk) - if ok { - retries = totalAttempts.(int) - if retries < totalFastPluginRetries { - ri = 1 * time.Second - } else if retries < totalMediumPluginRetries { - ri = 5 * time.Second - } - // if the core node has retried totalMediumPluginRetries times, do not set retry interval and plugin will use - // the default interval - } else { - ri = 1 * time.Second - } - r.mercury.pluginRetryCache.Set(prk, retries+1, cache.DefaultExpiration) - return ri -} - -// generatePluginRetryKey returns a plugin retry cache key -func generatePluginRetryKey(workID string, block uint64) string { - return workID + "|" + fmt.Sprintf("%d", block) -} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go deleted file mode 100644 index 145d701454d..00000000000 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup_test.go +++ /dev/null @@ -1,1354 +0,0 @@ -package evm - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "math/big" - "net/http" - "strings" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/patrickmn/go-cache" - "github.com/pkg/errors" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" - - evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" - "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" -) - -// setups up an evm registry for tests. -func setupEVMRegistry(t *testing.T) *EvmRegistry { - lggr := logger.TestLogger(t) - addr := common.HexToAddress("0x6cA639822c6C241Fa9A7A6b5032F6F7F1C513CAD") - keeperRegistryABI, err := abi.JSON(strings.NewReader(i_keeper_registry_master_wrapper_2_1.IKeeperRegistryMasterABI)) - require.Nil(t, err, "need registry abi") - streamsLookupCompatibleABI, err := abi.JSON(strings.NewReader(streams_lookup_compatible_interface.StreamsLookupCompatibleInterfaceABI)) - require.Nil(t, err, "need mercury abi") - var logPoller logpoller.LogPoller - mockReg := mocks.NewRegistry(t) - mockHttpClient := mocks.NewHttpClient(t) - client := evmClientMocks.NewClient(t) - - r := &EvmRegistry{ - lggr: lggr, - poller: logPoller, - addr: addr, - client: client, - logProcessed: make(map[string]bool), - registry: mockReg, - abi: keeperRegistryABI, - active: NewActiveUpkeepList(), - packer: encoding.NewAbiPacker(), - headFunc: func(ocr2keepers.BlockKey) {}, - chLog: make(chan logpoller.Log, 1000), - mercury: &MercuryConfig{ - cred: &models.MercuryCredentials{ - LegacyURL: "https://google.old.com", - URL: "https://google.com", - Username: "FakeClientID", - Password: "FakeClientKey", - }, - abi: streamsLookupCompatibleABI, - allowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), - pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), - }, - hc: mockHttpClient, - threadCtrl: utils.NewThreadControl(), - } - return r -} - -func TestEvmRegistry_StreamsLookup(t *testing.T) { - upkeepId, ok := new(big.Int).SetString("71022726777042968814359024671382968091267501884371696415772139504780367423725", 10) - var upkeepIdentifier [32]byte - copy(upkeepIdentifier[:], upkeepId.Bytes()) - assert.True(t, ok, t.Name()) - blockNum := ocr2keepers.BlockNumber(37974374) - tests := []struct { - name string - input []ocr2keepers.CheckResult - blobs map[string]string - callbackResp []byte - expectedResults []ocr2keepers.CheckResult - callbackNeeded bool - extraData []byte - checkCallbackResp []byte - values [][]byte - cachedAdminCfg bool - hasError bool - hasPermission bool - v3 bool - }{ - { - name: "success - happy path no cache", - input: []ocr2keepers.CheckResult{ - { - PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: blockNum, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), - }, - }, - 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: true, - PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: blockNum, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonNone), - }, - }, - hasPermission: true, - }, - { - name: "success - happy path no cache - v0.3", - input: []ocr2keepers.CheckResult{ - { - PerformData: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000766656564494473000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: blockNum, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), - }, - }, - 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: true, - PerformData: hexutil.MustDecode("0x000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000002e000066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000004555638000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000000269ecbb83b000000000000000000000000000000000000000000000000000000269e4a4e14000000000000000000000000000000000000000000000000000000269f4d0edb000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002381e91cffa9502c20de1ddcee350db3f715a5ab449448e3184a5b03c682356c6e2115f20663b3731e373cf33465a96da26f2876debb548f281e62e48f64c374200000000000000000000000000000000000000000000000000000000000000027db99e34135098d4e0bb9ae143ec9cd72fd63150c6d0cc5b38f4aa1aa42408377e8fe8e5ac489c9b7f62ff5aa7b05d2e892e7dee4cac631097247969b3b03fa300000000000000000000000000000000000000000000000000000000000002e00006da4a86c4933dd4a87b21dd2871aea29f706bcde43c70039355ac5b664fb5000000000000000000000000000000000000000000000000000000000454d118000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204254432d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064f0d4a0000000000000000000000000000000000000000000000000000002645f00877a000000000000000000000000000000000000000000000000000002645e1e1010000000000000000000000000000000000000000000000000000002645fe2fee4000000000000000000000000000000000000000000000000000000000243716664b42d20423a47fb13ad3098b49b37f667548e6745fff958b663afe25a845f6100000000000000000000000000000000000000000000000000000000024371660000000000000000000000000000000000000000000000000000000064f0d4a00000000000000000000000000000000000000000000000000000000000000002a0373c0bce7393673f819eb9681cac2773c2d718ce933eb858252195b17a9c832d7b0bee173c02c3c25fb65912b8b13b9302ede8423bab3544cb7a8928d5eb3600000000000000000000000000000000000000000000000000000000000000027d7b79d7646383a5dbf51edf14d53bd3ad0a9f3ca8affab3165e89d3ddce9cb17b58e892fafe4ecb24d2fde07c6a756029e752a5114c33c173df4e7d309adb4d00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: blockNum, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonNone), - }, - }, - hasPermission: true, - v3: true, - }, - { - name: "skip - failure reason is insufficient balance", - input: []ocr2keepers.CheckResult{ - { - PerformData: []byte{}, - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: 26046145, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), - }, - }, - expectedResults: []ocr2keepers.CheckResult{ - { - Eligible: false, - PerformData: []byte{}, - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: 26046145, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonInsufficientBalance), - }, - }, - hasError: true, - }, - { - name: "skip - invalid revert data", - input: []ocr2keepers.CheckResult{ - { - PerformData: []byte{}, - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: 26046145, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), - }, - }, - expectedResults: []ocr2keepers.CheckResult{ - { - Eligible: false, - PerformData: []byte{}, - UpkeepID: upkeepIdentifier, - Trigger: ocr2keepers.Trigger{ - BlockNumber: 26046145, - }, - IneligibilityReason: uint8(encoding.UpkeepFailureReasonTargetCheckReverted), - }, - }, - hasError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := setupEVMRegistry(t) - defer r.Close() - client := new(evmClientMocks.Client) - r.client = client - - if !tt.cachedAdminCfg && !tt.hasError { - cfg := UpkeepPrivilegeConfig{MercuryEnabled: tt.hasPermission} - bCfg, err := json.Marshal(cfg) - require.Nil(t, err) - - bContractCfg, err := r.abi.Methods["getUpkeepPrivilegeConfig"].Outputs.PackValues([]interface{}{bCfg}) - require.Nil(t, err) - - payload, err := r.abi.Pack("getUpkeepPrivilegeConfig", upkeepId) - require.Nil(t, err) - - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - - client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, mock.AnythingOfType("string")).Return(nil). - Run(func(args mock.Arguments) { - b := args.Get(1).(*hexutil.Bytes) - *b = bContractCfg - }).Once() - } - - if len(tt.blobs) > 0 { - if tt.v3 { - hc := mocks.NewHttpClient(t) - mr1 := MercuryV03Response{ - Reports: []MercuryV03Report{{FullReport: tt.blobs["0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"]}, {FullReport: tt.blobs["0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"]}}} - b1, err := json.Marshal(mr1) - assert.Nil(t, err) - resp1 := &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(bytes.NewReader(b1)), - } - hc.On("Do", mock.Anything).Return(resp1, nil).Once() - r.hc = hc - } else { - hc := mocks.NewHttpClient(t) - mr1 := MercuryV02Response{ChainlinkBlob: tt.blobs["0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"]} - b1, err := json.Marshal(mr1) - assert.Nil(t, err) - resp1 := &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(bytes.NewReader(b1)), - } - mr2 := MercuryV02Response{ChainlinkBlob: tt.blobs["0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"]} - b2, err := json.Marshal(mr2) - assert.Nil(t, err) - resp2 := &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(bytes.NewReader(b2)), - } - hc.On("Do", mock.MatchedBy(func(req *http.Request) bool { - return strings.Contains(req.URL.String(), "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000") - })).Return(resp2, nil).Once() - - hc.On("Do", mock.MatchedBy(func(req *http.Request) bool { - return strings.Contains(req.URL.String(), "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000") - })).Return(resp1, nil).Once() - r.hc = hc - } - } - - if tt.callbackNeeded { - payload, err := r.abi.Pack("checkCallback", upkeepId, tt.values, tt.extraData) - require.Nil(t, err) - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, hexutil.EncodeUint64(uint64(blockNum))).Return(nil). - Run(func(args mock.Arguments) { - b := args.Get(1).(*hexutil.Bytes) - *b = tt.checkCallbackResp - }).Once() - } - - got := r.streamsLookup(context.Background(), tt.input) - assert.Equal(t, tt.expectedResults, got, tt.name) - }) - } -} - -func TestEvmRegistry_AllowedToUseMercury(t *testing.T) { - upkeepId, ok := new(big.Int).SetString("71022726777042968814359024671382968091267501884371696415772139504780367423725", 10) - assert.True(t, ok, t.Name()) - tests := []struct { - name string - cached bool - allowed bool - ethCallErr error - err error - state encoding.PipelineExecutionState - reason encoding.UpkeepFailureReason - retryable bool - config []byte - }{ - { - name: "success - allowed via cache", - cached: true, - allowed: true, - }, - { - name: "success - allowed via fetching privilege config", - allowed: true, - }, - { - name: "success - not allowed via cache", - cached: true, - allowed: false, - }, - { - name: "success - not allowed via fetching privilege config", - allowed: false, - }, - { - name: "failure - cannot unmarshal privilege config", - err: fmt.Errorf("failed to unmarshal privilege config: invalid character '\\x00' looking for beginning of value"), - state: encoding.MercuryUnmarshalError, - config: []byte{0, 1}, - }, - { - name: "failure - flaky RPC", - retryable: true, - err: fmt.Errorf("failed to get upkeep privilege config: flaky RPC"), - state: encoding.RpcFlakyFailure, - ethCallErr: fmt.Errorf("flaky RPC"), - }, - { - name: "failure - empty upkeep privilege config", - err: fmt.Errorf("upkeep privilege config is empty"), - reason: encoding.UpkeepFailureReasonMercuryAccessNotAllowed, - config: []byte{}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := setupEVMRegistry(t) - defer r.Close() - - client := new(evmClientMocks.Client) - r.client = client - - if tt.cached { - r.mercury.allowListCache.Set(upkeepId.String(), tt.allowed, cache.DefaultExpiration) - } else { - if tt.err != nil { - bContractCfg, err := r.abi.Methods["getUpkeepPrivilegeConfig"].Outputs.PackValues([]interface{}{tt.config}) - require.Nil(t, err) - - payload, err := r.abi.Pack("getUpkeepPrivilegeConfig", upkeepId) - require.Nil(t, err) - - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - - client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, mock.AnythingOfType("string")). - Return(tt.ethCallErr). - Run(func(args mock.Arguments) { - b := args.Get(1).(*hexutil.Bytes) - *b = bContractCfg - }).Once() - } else { - cfg := UpkeepPrivilegeConfig{MercuryEnabled: tt.allowed} - bCfg, err := json.Marshal(cfg) - require.Nil(t, err) - - bContractCfg, err := r.abi.Methods["getUpkeepPrivilegeConfig"].Outputs.PackValues([]interface{}{bCfg}) - require.Nil(t, err) - - payload, err := r.abi.Pack("getUpkeepPrivilegeConfig", upkeepId) - require.Nil(t, err) - - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - - client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, mock.AnythingOfType("string")).Return(nil). - Run(func(args mock.Arguments) { - b := args.Get(1).(*hexutil.Bytes) - *b = bContractCfg - }).Once() - } - } - - opts := &bind.CallOpts{ - BlockNumber: big.NewInt(10), - } - - state, reason, retryable, allowed, err := r.allowedToUseMercury(opts, upkeepId) - assert.Equal(t, tt.err, err) - assert.Equal(t, tt.allowed, allowed) - assert.Equal(t, tt.state, state) - assert.Equal(t, tt.reason, reason) - assert.Equal(t, tt.retryable, retryable) - }) - } -} - -func TestEvmRegistry_DoMercuryRequestV02(t *testing.T) { - upkeepId, _ := new(big.Int).SetString("88786950015966611018675766524283132478093844178961698330929478019253453382042", 10) - - tests := []struct { - name string - lookup *StreamsLookup - mockHttpStatusCode int - mockChainlinkBlobs []string - pluginRetries int - pluginRetryKey string - expectedValues [][]byte - expectedRetryable bool - expectedRetryInterval time.Duration - expectedError error - state encoding.PipelineExecutionState - reason encoding.UpkeepFailureReason - }{ - { - name: "success", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - mockHttpStatusCode: http.StatusOK, - mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, - expectedValues: [][]byte{{0, 6, 109, 252, 209, 237, 45, 149, 177, 140, 148, 141, 188, 91, 214, 76, 104, 122, 254, 147, 228, 202, 125, 102, 61, 222, 193, 76, 32, 9, 10, 216, 0, 0, 0, 0, 0, 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, 20, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 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, 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, 2, 128, 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, 0, 0, 0, 0, 0, 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, 69, 84, 72, 45, 85, 83, 68, 45, 65, 82, 66, 73, 84, 82, 85, 77, 45, 84, 69, 83, 84, 78, 69, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 216, 211, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 207, 11, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 155, 61, 164, 0, 0, 0, 0, 0, 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, 138, 231, 206, 116, 217, 250, 37, 42, 137, 131, 151, 110, 171, 96, 13, 199, 89, 12, 119, 141, 4, 129, 52, 48, 132, 27, 198, 231, 101, 195, 76, 216, 26, 22, 141, 0, 0, 0, 0, 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, 138, 231, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 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, 96, 65, 43, 148, 229, 37, 202, 108, 237, 201, 245, 68, 253, 134, 247, 118, 6, 213, 47, 231, 49, 165, 208, 105, 219, 232, 54, 168, 191, 192, 251, 140, 145, 25, 99, 176, 174, 122, 20, 151, 31, 59, 70, 33, 191, 251, 128, 46, 240, 96, 83, 146, 185, 166, 200, 156, 127, 171, 29, 248, 99, 58, 90, 222, 0, 0, 0, 0, 0, 0, 0, 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, 69, 0, 194, 245, 33, 248, 63, 186, 94, 252, 43, 243, 239, 250, 174, 221, 228, 61, 10, 74, 223, 247, 133, 193, 33, 59, 113, 42, 58, 237, 13, 129, 87, 100, 42, 132, 50, 77, 176, 207, 150, 149, 235, 210, 119, 8, 212, 96, 142, 176, 51, 126, 13, 216, 123, 14, 67, 240, 250, 112, 199, 0, 217, 17}}, - expectedRetryable: false, - expectedError: nil, - }, - { - name: "failure - retryable and interval is 1s", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - mockHttpStatusCode: http.StatusInternalServerError, - mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, - expectedValues: [][]byte{nil}, - expectedRetryable: true, - pluginRetries: 0, - expectedRetryInterval: 1 * time.Second, - expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), - state: encoding.MercuryFlakyFailure, - }, - { - name: "failure - retryable and interval is 5s", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - pluginRetries: 5, - mockHttpStatusCode: http.StatusInternalServerError, - mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, - expectedValues: [][]byte{nil}, - expectedRetryable: true, - expectedRetryInterval: 5 * time.Second, - expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), - state: encoding.MercuryFlakyFailure, - }, - { - name: "failure - not retryable because there are many plugin retries already", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - pluginRetries: 10, - mockHttpStatusCode: http.StatusInternalServerError, - mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, - expectedValues: [][]byte{nil}, - expectedRetryable: true, - expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 500\n#2: 500\n#3: 500"), - state: encoding.MercuryFlakyFailure, - }, - { - name: "failure - not retryable", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - mockHttpStatusCode: http.StatusTooManyRequests, - mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, - expectedValues: [][]byte{nil}, - expectedRetryable: false, - expectedError: errors.New("failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 25880526 upkeep 88786950015966611018675766524283132478093844178961698330929478019253453382042 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"), - state: encoding.InvalidMercuryRequest, - }, - { - name: "failure - no feeds", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - expectedValues: [][]byte{}, - reason: encoding.UpkeepFailureReasonInvalidRevertDataInput, - }, - { - name: "failure - invalid revert data", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - expectedValues: [][]byte{}, - reason: encoding.UpkeepFailureReasonInvalidRevertDataInput, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := setupEVMRegistry(t) - defer r.Close() - - if tt.pluginRetries != 0 { - r.mercury.pluginRetryCache.Set(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) - } - - hc := mocks.NewHttpClient(t) - - for _, blob := range tt.mockChainlinkBlobs { - mr := MercuryV02Response{ChainlinkBlob: blob} - b, err := json.Marshal(mr) - assert.Nil(t, err) - - resp := &http.Response{ - StatusCode: tt.mockHttpStatusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - if tt.expectedError != nil && tt.expectedRetryable || tt.pluginRetries > 0 { - hc.On("Do", mock.Anything).Return(resp, nil).Times(totalAttempt) - } else { - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } - } - r.hc = hc - - state, reason, values, retryable, ri, reqErr := r.doMercuryRequest(context.Background(), tt.lookup, tt.pluginRetryKey, r.lggr) - assert.Equal(t, tt.expectedValues, values) - assert.Equal(t, tt.expectedRetryable, retryable) - if retryable { - newRetries, _ := r.mercury.pluginRetryCache.Get(tt.pluginRetryKey) - assert.Equal(t, tt.pluginRetries+1, newRetries.(int)) - } - assert.Equal(t, tt.expectedRetryInterval, ri) - assert.Equal(t, tt.state, state) - assert.Equal(t, tt.reason, reason) - if tt.expectedError != nil { - assert.True(t, strings.HasPrefix(reqErr.Error(), "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000")) - } - }) - } -} - -func TestEvmRegistry_DoMercuryRequestV03(t *testing.T) { - upkeepId, _ := new(big.Int).SetString("88786950015966611018675766524283132478093844178961698330929478019253453382042", 10) - - tests := []struct { - name string - lookup *StreamsLookup - mockHttpStatusCode int - mockChainlinkBlobs []string - pluginRetryKey string - expectedValues [][]byte - expectedRetryable bool - expectedRetryInterval time.Duration - expectedError error - state encoding.PipelineExecutionState - reason encoding.UpkeepFailureReason - }{ - { - name: "success v0.3", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(25880526), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - }, - mockHttpStatusCode: http.StatusOK, - mockChainlinkBlobs: []string{"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}, - expectedValues: [][]byte{{0, 6, 109, 252, 209, 237, 45, 149, 177, 140, 148, 141, 188, 91, 214, 76, 104, 122, 254, 147, 228, 202, 125, 102, 61, 222, 193, 76, 32, 9, 10, 216, 0, 0, 0, 0, 0, 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, 20, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 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, 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, 2, 128, 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, 0, 0, 0, 0, 0, 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, 69, 84, 72, 45, 85, 83, 68, 45, 65, 82, 66, 73, 84, 82, 85, 77, 45, 84, 69, 83, 84, 78, 69, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 216, 211, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 154, 207, 11, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 155, 61, 164, 0, 0, 0, 0, 0, 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, 138, 231, 206, 116, 217, 250, 37, 42, 137, 131, 151, 110, 171, 96, 13, 199, 89, 12, 119, 141, 4, 129, 52, 48, 132, 27, 198, 231, 101, 195, 76, 216, 26, 22, 141, 0, 0, 0, 0, 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, 138, 231, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 137, 28, 152, 0, 0, 0, 0, 0, 0, 0, 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, 96, 65, 43, 148, 229, 37, 202, 108, 237, 201, 245, 68, 253, 134, 247, 118, 6, 213, 47, 231, 49, 165, 208, 105, 219, 232, 54, 168, 191, 192, 251, 140, 145, 25, 99, 176, 174, 122, 20, 151, 31, 59, 70, 33, 191, 251, 128, 46, 240, 96, 83, 146, 185, 166, 200, 156, 127, 171, 29, 248, 99, 58, 90, 222, 0, 0, 0, 0, 0, 0, 0, 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, 69, 0, 194, 245, 33, 248, 63, 186, 94, 252, 43, 243, 239, 250, 174, 221, 228, 61, 10, 74, 223, 247, 133, 193, 33, 59, 113, 42, 58, 237, 13, 129, 87, 100, 42, 132, 50, 77, 176, 207, 150, 149, 235, 210, 119, 8, 212, 96, 142, 176, 51, 126, 13, 216, 123, 14, 67, 240, 250, 112, 199, 0, 217, 17}}, - expectedRetryable: false, - expectedError: nil, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := setupEVMRegistry(t) - hc := mocks.NewHttpClient(t) - - mr := MercuryV03Response{} - for i, blob := range tt.mockChainlinkBlobs { - r := MercuryV03Report{ - FeedID: tt.lookup.Feeds[i], - ValidFromTimestamp: 0, - ObservationsTimestamp: 0, - FullReport: blob, - } - mr.Reports = append(mr.Reports, r) - } - - b, err := json.Marshal(mr) - assert.Nil(t, err) - resp := &http.Response{ - StatusCode: tt.mockHttpStatusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - if tt.expectedError != nil && tt.expectedRetryable { - hc.On("Do", mock.Anything).Return(resp, nil).Times(totalAttempt) - } else { - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } - r.hc = hc - - state, reason, values, retryable, ri, reqErr := r.doMercuryRequest(context.Background(), tt.lookup, tt.pluginRetryKey, r.lggr) - assert.Equal(t, tt.expectedValues, values) - assert.Equal(t, tt.expectedRetryable, retryable) - assert.Equal(t, tt.expectedRetryInterval, ri) - assert.Equal(t, tt.state, state) - assert.Equal(t, tt.reason, reason) - if tt.expectedError != nil { - assert.Equal(t, tt.expectedError.Error(), reqErr.Error()) - } - }) - } -} - -func TestEvmRegistry_SingleFeedRequest(t *testing.T) { - upkeepId := big.NewInt(123456789) - tests := []struct { - name string - index int - lookup *StreamsLookup - pluginRetryKey string - blob string - statusCode int - lastStatusCode int - retryNumber int - retryable bool - errorMessage string - }{ - { - name: "success - mercury responds in the first try", - index: 0, - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - blob: "0xab2123dc00000012", - }, - { - name: "success - retry for 404", - index: 0, - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - blob: "0xab2123dcbabbad", - retryNumber: 1, - statusCode: http.StatusNotFound, - lastStatusCode: http.StatusOK, - }, - { - name: "success - retry for 500", - index: 0, - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - blob: "0xab2123dcbbabad", - retryNumber: 2, - statusCode: http.StatusInternalServerError, - lastStatusCode: http.StatusOK, - }, - { - name: "failure - returns retryable", - index: 0, - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - blob: "0xab2123dc", - retryNumber: totalAttempt, - statusCode: http.StatusNotFound, - retryable: true, - errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 404\n#2: 404\n#3: 404", - }, - { - name: "failure - returns retryable and then non-retryable", - index: 0, - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - blob: "0xab2123dc", - retryNumber: 1, - statusCode: http.StatusNotFound, - lastStatusCode: http.StatusTooManyRequests, - errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: 404\n#2: at block 123456 upkeep 123456789 received status code 429 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - }, - { - name: "failure - returns not retryable", - index: 0, - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - blob: "0xab2123dc", - statusCode: http.StatusConflict, - errorMessage: "failed to request feed for 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000: All attempts fail:\n#1: at block 123456 upkeep 123456789 received status code 409 for feed 0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := setupEVMRegistry(t) - defer r.Close() - - hc := mocks.NewHttpClient(t) - - mr := MercuryV02Response{ChainlinkBlob: tt.blob} - b, err := json.Marshal(mr) - assert.Nil(t, err) - - if tt.retryNumber == 0 { - if tt.errorMessage != "" { - resp := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } else { - resp := &http.Response{ - StatusCode: http.StatusOK, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } - } else if tt.retryNumber > 0 && tt.retryNumber < totalAttempt { - retryResp := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(retryResp, nil).Times(tt.retryNumber) - - resp := &http.Response{ - StatusCode: tt.lastStatusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } else { - resp := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Times(tt.retryNumber) - } - r.hc = hc - - ch := make(chan MercuryData, 1) - r.singleFeedRequest(context.Background(), ch, tt.index, tt.lookup, r.lggr) - - m := <-ch - assert.Equal(t, tt.index, m.Index) - assert.Equal(t, tt.retryable, m.Retryable) - if tt.retryNumber >= totalAttempt || tt.errorMessage != "" { - assert.Equal(t, tt.errorMessage, m.Error.Error()) - assert.Equal(t, [][]byte{}, m.Bytes) - } else { - blobBytes, err := hexutil.Decode(tt.blob) - assert.Nil(t, err) - assert.Nil(t, m.Error) - assert.Equal(t, [][]byte{blobBytes}, m.Bytes) - } - }) - } -} - -func TestEvmRegistry_MultiFeedRequest(t *testing.T) { - upkeepId := big.NewInt(123456789) - tests := []struct { - name string - lookup *StreamsLookup - statusCode int - lastStatusCode int - pluginRetries int - pluginRetryKey string - retryNumber int - retryable bool - errorMessage string - firstResponse *MercuryV03Response - response *MercuryV03Response - }{ - { - name: "success - mercury responds in the first try", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - response: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - { - FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123458, - ObservationsTimestamp: 123458, - FullReport: "0xab2123dc00000016", - }, - }, - }, - statusCode: http.StatusOK, - }, - { - name: "success - mercury responds in the first try with blocknumber", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - response: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - { - FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123458, - ObservationsTimestamp: 123458, - FullReport: "0xab2123dc00000016", - }, - }, - }, - statusCode: http.StatusOK, - }, - { - name: "success - retry 206", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - firstResponse: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - }, - }, - response: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - { - FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123458, - ObservationsTimestamp: 123458, - FullReport: "0xab2123dc00000019", - }, - }, - }, - retryNumber: 1, - statusCode: http.StatusPartialContent, - lastStatusCode: http.StatusOK, - }, - { - name: "success - retry for 500", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - retryNumber: 2, - statusCode: http.StatusInternalServerError, - lastStatusCode: http.StatusOK, - response: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - { - FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123458, - ObservationsTimestamp: 123458, - FullReport: "0xab2123dc00000019", - }, - }, - }, - }, - { - name: "failure - fail to decode reportBlob", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - response: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "qerwiu", // invalid hex blob - }, - { - FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123458, - ObservationsTimestamp: 123458, - FullReport: "0xab2123dc00000016", - }, - }, - }, - statusCode: http.StatusOK, - retryable: false, - errorMessage: "All attempts fail:\n#1: hex string without 0x prefix", - }, - { - name: "failure - returns retryable with 1s plugin retry interval", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - retryNumber: totalAttempt, - statusCode: http.StatusInternalServerError, - retryable: true, - errorMessage: "All attempts fail:\n#1: 500\n#2: 500\n#3: 500", - }, - { - name: "failure - returns retryable with 5s plugin retry interval", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - pluginRetries: 6, - retryNumber: totalAttempt, - statusCode: http.StatusInternalServerError, - retryable: true, - errorMessage: "All attempts fail:\n#1: 500\n#2: 500\n#3: 500", - }, - { - name: "failure - returns retryable and then non-retryable", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - retryNumber: 1, - statusCode: http.StatusInternalServerError, - lastStatusCode: http.StatusUnauthorized, - errorMessage: "All attempts fail:\n#1: 500\n#2: at timestamp 123456 upkeep 123456789 received status code 401 from mercury v0.3, most likely this is caused by unauthorized upkeep", - }, - { - name: "failure - returns status code 422 not retryable", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - statusCode: http.StatusUnprocessableEntity, - errorMessage: "All attempts fail:\n#1: at timestamp 123456 upkeep 123456789 received status code 422 from mercury v0.3", - }, - { - name: "success - retry when reports length does not match feeds length", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIDs, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: timestamp, - Time: big.NewInt(123456), - }, - upkeepId: upkeepId, - }, - firstResponse: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - }, - }, - response: &MercuryV03Response{ - Reports: []MercuryV03Report{ - { - FeedID: "0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123456, - ObservationsTimestamp: 123456, - FullReport: "0xab2123dc00000012", - }, - { - FeedID: "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000", - ValidFromTimestamp: 123458, - ObservationsTimestamp: 123458, - FullReport: "0xab2123dc00000019", - }, - }, - }, - retryNumber: 1, - statusCode: http.StatusOK, - lastStatusCode: http.StatusOK, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := setupEVMRegistry(t) - defer r.Close() - - if tt.pluginRetries != 0 { - r.mercury.pluginRetryCache.Set(tt.pluginRetryKey, tt.pluginRetries, cache.DefaultExpiration) - } - - hc := mocks.NewHttpClient(t) - b, err := json.Marshal(tt.response) - assert.Nil(t, err) - - if tt.retryNumber == 0 { - resp := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } else if tt.retryNumber < totalAttempt { - if tt.firstResponse != nil && tt.response != nil { - b0, err := json.Marshal(tt.firstResponse) - assert.Nil(t, err) - resp0 := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b0)), - } - b1, err := json.Marshal(tt.response) - assert.Nil(t, err) - resp1 := &http.Response{ - StatusCode: tt.lastStatusCode, - Body: io.NopCloser(bytes.NewReader(b1)), - } - hc.On("Do", mock.Anything).Return(resp0, nil).Once().On("Do", mock.Anything).Return(resp1, nil).Once() - } else { - retryResp := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(retryResp, nil).Times(tt.retryNumber) - - resp := &http.Response{ - StatusCode: tt.lastStatusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Once() - } - } else { - resp := &http.Response{ - StatusCode: tt.statusCode, - Body: io.NopCloser(bytes.NewReader(b)), - } - hc.On("Do", mock.Anything).Return(resp, nil).Times(tt.retryNumber) - } - r.hc = hc - - ch := make(chan MercuryData, 1) - r.multiFeedsRequest(context.Background(), ch, tt.lookup, r.lggr) - - m := <-ch - assert.Equal(t, 0, m.Index) - assert.Equal(t, tt.retryable, m.Retryable) - if tt.retryNumber >= totalAttempt || tt.errorMessage != "" { - assert.Equal(t, tt.errorMessage, m.Error.Error()) - assert.Equal(t, [][]byte{}, m.Bytes) - } else { - assert.Nil(t, m.Error) - var reports [][]byte - for _, rsp := range tt.response.Reports { - b, _ := hexutil.Decode(rsp.FullReport) - reports = append(reports, b) - } - assert.Equal(t, reports, m.Bytes) - } - }) - } -} - -func TestEvmRegistry_CheckCallback(t *testing.T) { - upkeepId := big.NewInt(123456789) - bn := uint64(999) - bs := []byte{183, 114, 215, 10, 0, 0, 0, 0, 0, 0} - values := [][]byte{bs} - tests := []struct { - name string - lookup *StreamsLookup - values [][]byte - statusCode int - - callbackResp []byte - callbackErr error - - upkeepNeeded bool - performData []byte - wantErr assert.ErrorAssertionFunc - - state encoding.PipelineExecutionState - retryable bool - }{ - { - name: "success - empty extra data", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"ETD-USD", "BTC-ETH"}, - TimeParamKey: blockNumber, - Time: big.NewInt(100), - ExtraData: []byte{48, 120, 48, 48}, - }, - upkeepId: upkeepId, - block: bn, - }, - 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}, - upkeepNeeded: true, - performData: []byte{48, 120, 48, 48}, - wantErr: assert.NoError, - }, - { - name: "success - with extra data", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: blockNumber, - Time: big.NewInt(18952430), - // this is the address of precompile contract ArbSys(0x0000000000000000000000000000000000000064) - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - upkeepId: upkeepId, - block: bn, - }, - 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}, - upkeepNeeded: true, - performData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - wantErr: assert.NoError, - }, - { - name: "failure - bad response", - lookup: &StreamsLookup{ - StreamsLookupError: &encoding.StreamsLookupError{ - FeedParamKey: feedIdHex, - Feeds: []string{"ETD-USD", "BTC-ETH"}, - TimeParamKey: blockNumber, - Time: big.NewInt(100), - ExtraData: []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, 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, 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}, - }, - upkeepId: upkeepId, - block: bn, - }, - values: values, - statusCode: http.StatusOK, - callbackResp: []byte{}, - callbackErr: errors.New("bad response"), - wantErr: assert.Error, - state: encoding.RpcFlakyFailure, - retryable: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - client := new(evmClientMocks.Client) - r := setupEVMRegistry(t) - defer r.Close() - - payload, err := r.abi.Pack("checkCallback", tt.lookup.upkeepId, values, tt.lookup.ExtraData) - require.Nil(t, err) - args := map[string]interface{}{ - "to": r.addr.Hex(), - "data": hexutil.Bytes(payload), - } - client.On("CallContext", mock.Anything, mock.AnythingOfType("*hexutil.Bytes"), "eth_call", args, hexutil.EncodeUint64(tt.lookup.block)).Return(tt.callbackErr). - Run(func(args mock.Arguments) { - by := args.Get(1).(*hexutil.Bytes) - *by = tt.callbackResp - }).Once() - r.client = client - - state, retryable, _, err := r.checkCallback(context.Background(), 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) - }) - } -} diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index ee8b7cd6e9a..f4fbf24f419 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -26,6 +26,8 @@ import ( "github.com/stretchr/testify/require" "github.com/umbracle/ethgo/abi" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" @@ -33,7 +35,9 @@ import ( "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" automationForwarderLogic "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_forwarder_logic" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/basic_upkeep_contract" @@ -52,7 +56,6 @@ import ( "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/ocr2/plugins/ocr2keeper" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) @@ -884,7 +887,7 @@ func (c *feedLookupUpkeepController) EnableMercury( registry *iregistry21.IKeeperRegistryMaster, registryOwner *bind.TransactOpts, ) error { - adminBytes, _ := json.Marshal(evm21.UpkeepPrivilegeConfig{ + adminBytes, _ := json.Marshal(streams.UpkeepPrivilegeConfig{ MercuryEnabled: true, }) @@ -908,7 +911,7 @@ func (c *feedLookupUpkeepController) EnableMercury( return err } - var checkBytes evm21.UpkeepPrivilegeConfig + var checkBytes streams.UpkeepPrivilegeConfig if err := json.Unmarshal(bts, &checkBytes); err != nil { require.NoError(t, err) diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 1a093a88159..87074e786dc 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -10,12 +10,16 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + + "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" @@ -23,8 +27,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/store/models" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" - "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -152,7 +154,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { if isMercury { // Set privilege config to enable mercury - privilegeConfigBytes, _ := json.Marshal(evm21.UpkeepPrivilegeConfig{ + privilegeConfigBytes, _ := json.Marshal(streams.UpkeepPrivilegeConfig{ MercuryEnabled: true, }) if err := registry.SetUpkeepPrivilegeConfig(upkeepIDs[i], privilegeConfigBytes); err != nil { From 96498b692963bbea6b9b1d1e7d5bcaf6f0786542 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 16 Nov 2023 15:10:14 -0500 Subject: [PATCH 165/271] [TT-695] Deprecate P2Pv1 in Tests (#11305) * Deprecating P2Pv1 in Tests * P2Pv2 Swap * Lint * Stupid typose --- integration-tests/actions/ocr_helpers_local.go | 8 ++++---- integration-tests/client/chainlink.go | 5 ++++- integration-tests/client/chainlink_models.go | 14 +++++--------- .../docker/test_env/test_env_builder.go | 5 +++-- integration-tests/smoke/keeper_test.go | 2 +- integration-tests/smoke/ocr_test.go | 7 ++++--- integration-tests/types/config/node/core.go | 2 ++ integration-tests/universal/log_poller/helpers.go | 3 --- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/integration-tests/actions/ocr_helpers_local.go b/integration-tests/actions/ocr_helpers_local.go index e6dd5ae77f6..fb0fd0bd47d 100644 --- a/integration-tests/actions/ocr_helpers_local.go +++ b/integration-tests/actions/ocr_helpers_local.go @@ -146,7 +146,7 @@ func CreateOCRJobsLocal( workerNodes []*client.ChainlinkClient, mockValue int, mockAdapter *test_env.Killgrave, - evmChainID string, + evmChainID *big.Int, ) error { for _, ocrInstance := range ocrInstances { bootstrapP2PIds, err := bootstrapNode.MustReadP2PKeys() @@ -157,7 +157,7 @@ func CreateOCRJobsLocal( bootstrapSpec := &client.OCRBootstrapJobSpec{ Name: fmt.Sprintf("bootstrap-%s", uuid.New().String()), ContractAddress: ocrInstance.Address(), - EVMChainID: evmChainID, + EVMChainID: evmChainID.String(), P2PPeerID: bootstrapP2PId, IsBootstrapPeer: true, } @@ -202,7 +202,7 @@ func CreateOCRJobsLocal( bootstrapPeers := []*client.ChainlinkClient{bootstrapNode} ocrSpec := &client.OCRTaskJobSpec{ ContractAddress: ocrInstance.Address(), - EVMChainID: evmChainID, + EVMChainID: evmChainID.String(), P2PPeerID: nodeP2PId, P2PBootstrapPeers: bootstrapPeers, KeyBundleID: nodeOCRKeyId, @@ -211,7 +211,7 @@ func CreateOCRJobsLocal( } _, err = node.MustCreateJob(ocrSpec) if err != nil { - return fmt.Errorf("creating OCR task job on OCR node have failed: %w", err) + return fmt.Errorf("creating OCR job on OCR node failed: %w", err) } } } diff --git a/integration-tests/client/chainlink.go b/integration-tests/client/chainlink.go index 4603679ff85..16299a1ac8a 100644 --- a/integration-tests/client/chainlink.go +++ b/integration-tests/client/chainlink.go @@ -116,7 +116,7 @@ func (c *ChainlinkClient) MustCreateJob(spec JobSpec) (*Job, error) { if err != nil { return nil, err } - return job, VerifyStatusCode(resp.RawResponse.StatusCode, http.StatusOK) + return job, VerifyStatusCodeWithResponse(resp, http.StatusOK) } // CreateJob creates a Chainlink job based on the provided spec struct @@ -1114,6 +1114,7 @@ func (c *ChainlinkClient) SetPageSize(size int) { c.pageSize = size } +// VerifyStatusCode verifies the status code of the response. Favor VerifyStatusCodeWithResponse over this for better errors func VerifyStatusCode(actStatusCd, expStatusCd int) error { if actStatusCd != expStatusCd { return fmt.Errorf( @@ -1125,6 +1126,8 @@ func VerifyStatusCode(actStatusCd, expStatusCd int) error { return nil } +// VerifyStatusCodeWithResponse verifies the status code of the response and returns the response as part of the error. +// Favor this over VerifyStatusCode func VerifyStatusCodeWithResponse(res *resty.Response, expStatusCd int) error { actStatusCd := res.RawResponse.StatusCode if actStatusCd != expStatusCd { diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index 17a0a50845b..8ff8494155b 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -889,7 +889,7 @@ contractConfigConfirmations ={{if not .ContractConfirmations}} 3 {{el contractConfigTrackerPollInterval ={{if not .TrackerPollInterval}} "1m" {{else}} {{.TrackerPollInterval}} {{end}} contractConfigTrackerSubscribeInterval ={{if not .TrackerSubscribeInterval}} "2m" {{else}} {{.TrackerSubscribeInterval}} {{end}} contractAddress = "{{.ContractAddress}}" -evmChainID = "{{.EVMChainID}}" +evmChainID = "{{.EVMChainID}}" p2pBootstrapPeers = [] isBootstrapPeer = {{.IsBootstrapPeer}} p2pPeerID = "{{.P2PPeerID}}"` @@ -988,22 +988,18 @@ contractConfigConfirmations ={{if not .ContractConfirmations}} 3 {{el contractConfigTrackerPollInterval ={{if not .TrackerPollInterval}} "1m" {{else}} {{.TrackerPollInterval}} {{end}} contractConfigTrackerSubscribeInterval ={{if not .TrackerSubscribeInterval}} "2m" {{else}} {{.TrackerSubscribeInterval}} {{end}} contractAddress = "{{.ContractAddress}}" -evmChainID = "{{.EVMChainID}}" +evmChainID = "{{.EVMChainID}}" {{if .P2PBootstrapPeers}} -p2pBootstrapPeers = [ - {{range $peer := .P2PBootstrapPeers}} - "/dns4/{{$peer.InternalIP}}/tcp/6690/p2p/{{$peer.PeerID}}", - {{end}} -] +p2pv2Bootstrappers = [{{range $peer := .P2PBootstrapPeers}}"{{$peer.PeerID}}@{{$peer.InternalIP}}:6690",{{end}}] {{else}} -p2pBootstrapPeers = [] +p2pv2Bootstrappers = [] {{end}} isBootstrapPeer = {{.IsBootstrapPeer}} p2pPeerID = "{{.P2PPeerID}}" keyBundleID = "{{.KeyBundleID}}" monitoringEndpoint ={{if not .MonitoringEndpoint}} "chain.link:4321" {{else}} "{{.MonitoringEndpoint}}" {{end}} transmitterAddress = "{{.TransmitterAddress}}" -forwardingAllowed = {{.ForwardingAllowed}} +forwardingAllowed = {{.ForwardingAllowed}} observationSource = """ {{.ObservationSource}} """` diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 77c56690155..32f84991eef 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -17,9 +17,10 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" + evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" ) type CleanUpType string @@ -324,7 +325,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } else { cfg = node.NewConfig(node.NewBaseConfig(), node.WithOCR1(), - node.WithP2Pv1(), + node.WithP2Pv2(), ) } diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 29a819af136..6f892e60aca 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -1098,7 +1098,7 @@ func setupKeeperTest(t *testing.T) ( contracts.LinkToken, *test_env.CLClusterTestEnv, ) { - clNodeConfig := node.NewConfig(node.NewBaseConfig(), node.WithP2Pv1()) + clNodeConfig := node.NewConfig(node.NewBaseConfig(), node.WithP2Pv2()) turnLookBack := int64(0) syncInterval := models.MustMakeDuration(5 * time.Second) performGasOverhead := uint32(150000) diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index 8fbc1109e2b..a078b8ca419 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/utils" @@ -39,7 +40,7 @@ func TestOCRBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) require.NoError(t, err) err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) @@ -86,7 +87,7 @@ func TestOCRJobReplacement(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) require.NoError(t, err) err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) @@ -112,7 +113,7 @@ func TestOCRJobReplacement(t *testing.T) { require.NoError(t, err) //Recreate job - err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID().String()) + err = actions.CreateOCRJobsLocal(ocrInstances, bootstrapNode, workerNodes, 5, env.MockAdapter, env.EVMClient.GetChainID()) require.NoError(t, err) err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 7ddddafdd5f..e044d1ba32a 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -109,6 +109,8 @@ func WithOCR2() NodeConfigOpt { } } +// Deprecated: P2Pv1 is soon to be fully deprecated +// WithP2Pv1 enables P2Pv1 and disables P2Pv2 func WithP2Pv1() NodeConfigOpt { return func(c *chainlink.Config) { c.P2P.V1 = toml.P2PV1{ diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 08ceb4a7be4..505337f0324 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -27,7 +27,6 @@ import ( ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -39,12 +38,10 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) From a9d81ae52ccd4e76ed414768ad7adc4532d3facd Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 16 Nov 2023 14:01:31 -0700 Subject: [PATCH 166/271] [TT-653] More integration-test lib migration to chainlink-testing-framework (#11317) * [TT-653] Migrate more integration-test libs to chainlink-testing-framework * testing * merge conflict fix * bump ctf version --- integration-tests/actions/actions.go | 4 +- .../actions/ocr2vrf_actions/ocr2vrf_steps.go | 6 +- .../actions/operator_forwarder_helpers.go | 12 +-- .../actions/vrfv2plus/vrfv2plus_steps.go | 6 +- integration-tests/benchmark/keeper_test.go | 3 +- .../chaos/automation_chaos_test.go | 26 ++--- integration-tests/chaos/ocr2vrf_chaos_test.go | 26 ++--- integration-tests/chaos/ocr_chaos_test.go | 24 ++--- .../client/chainlink_config_builder.go | 52 ---------- integration-tests/docker/test_env/cl_node.go | 19 ++-- integration-tests/docker/test_env/test_env.go | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- .../load/vrfv2/onchain_monitoring.go | 5 +- .../load/vrfv2plus/vrfv2plus_test.go | 6 +- .../migration/upgrade_version_test.go | 6 +- integration-tests/performance/cron_test.go | 2 +- .../performance/directrequest_test.go | 6 +- integration-tests/performance/flux_test.go | 12 +-- integration-tests/performance/keeper_test.go | 10 +- integration-tests/performance/ocr_test.go | 9 +- integration-tests/performance/vrf_test.go | 8 +- .../reorg/automation_reorg_test.go | 10 +- integration-tests/reorg/reorg_confirmer.go | 7 +- integration-tests/reorg/reorg_test.go | 12 +-- integration-tests/smoke/automation_test.go | 87 ++++++++--------- integration-tests/smoke/flux_test.go | 10 +- integration-tests/smoke/forwarder_ocr_test.go | 6 +- .../smoke/forwarders_ocr2_test.go | 6 +- integration-tests/smoke/keeper_test.go | 96 +++++++++---------- integration-tests/smoke/ocr2_test.go | 12 +-- integration-tests/smoke/ocr2vrf_test.go | 8 +- integration-tests/smoke/ocr_test.go | 13 ++- integration-tests/smoke/runlog_test.go | 4 +- integration-tests/smoke/vrf_test.go | 12 +-- integration-tests/smoke/vrfv2_test.go | 6 +- integration-tests/smoke/vrfv2plus_test.go | 96 +++++++++---------- integration-tests/soak/forwarder_ocr_test.go | 2 +- integration-tests/soak/ocr_test.go | 2 +- .../testsetups/keeper_benchmark.go | 10 +- integration-tests/testsetups/ocr.go | 14 +-- integration-tests/testsetups/vrfv2.go | 4 +- integration-tests/types/config/node/core.go | 91 +++++++++--------- .../universal/log_poller/helpers.go | 16 ++-- .../universal/log_poller/scenarios.go | 14 +-- integration-tests/utils/common.go | 25 ----- 46 files changed, 368 insertions(+), 447 deletions(-) delete mode 100644 integration-tests/client/chainlink_config_builder.go diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 02a25234774..438c36a9f0e 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -23,7 +23,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/testreporters" - "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" @@ -48,7 +48,7 @@ func FundChainlinkNodes( msg := ethereum.CallMsg{ From: common.HexToAddress(client.GetDefaultWallet().Address()), To: &recipient, - Value: utils.EtherToWei(amount), + Value: conversions.EtherToWei(amount), } gasEstimates, err := client.EstimateGas(msg) if err != nil { diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go index 72d668076e9..2904162f495 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func SetAndWaitForVRFBeaconProcessToFinish(t *testing.T, ocr2VRFPluginConfig *OCR2VRFPluginConfig, vrfBeacon contracts.VRFBeacon) { @@ -273,14 +273,14 @@ func RequestRandomnessFulfillmentAndWaitForFulfilment( } func getRequestId(t *testing.T, consumer contracts.VRFBeaconConsumer, receipt *types.Receipt, confirmationDelay *big.Int) *big.Int { - periodBlocks, err := consumer.IBeaconPeriodBlocks(utils.TestContext(t)) + periodBlocks, err := consumer.IBeaconPeriodBlocks(testcontext.Get(t)) require.NoError(t, err, "Error getting Beacon Period block count") blockNumber := receipt.BlockNumber periodOffset := new(big.Int).Mod(blockNumber, periodBlocks) nextBeaconOutputHeight := new(big.Int).Sub(new(big.Int).Add(blockNumber, periodBlocks), periodOffset) - requestID, err := consumer.GetRequestIdsBy(utils.TestContext(t), nextBeaconOutputHeight, confirmationDelay) + requestID, err := consumer.GetRequestIdsBy(testcontext.Get(t), nextBeaconOutputHeight, confirmationDelay) require.NoError(t, err, "Error getting requestID from consumer contract") return requestID diff --git a/integration-tests/actions/operator_forwarder_helpers.go b/integration-tests/actions/operator_forwarder_helpers.go index a1d7135416c..e308cd8fd5b 100644 --- a/integration-tests/actions/operator_forwarder_helpers.go +++ b/integration-tests/actions/operator_forwarder_helpers.go @@ -12,11 +12,11 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func DeployForwarderContracts( @@ -67,7 +67,7 @@ func AcceptAuthorizedReceiversOperator( err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for events in nodes shouldn't fail") - senders, err := forwarderInstance.GetAuthorizedSenders(utils.TestContext(t)) + senders, err := forwarderInstance.GetAuthorizedSenders(testcontext.Get(t)) require.NoError(t, err, "Getting authorized senders shouldn't fail") var nodesAddrs []string for _, o := range nodeAddresses { @@ -75,7 +75,7 @@ func AcceptAuthorizedReceiversOperator( } require.Equal(t, nodesAddrs, senders, "Senders addresses should match node addresses") - owner, err := forwarderInstance.Owner(utils.TestContext(t)) + owner, err := forwarderInstance.Owner(testcontext.Get(t)) require.NoError(t, err, "Getting authorized forwarder owner shouldn't fail") require.Equal(t, operator.Hex(), owner, "Forwarder owner should match operator") } @@ -139,7 +139,7 @@ func SubscribeOperatorFactoryEvents( l := logging.GetTestLogger(t) contractABI, err := operator_factory.OperatorFactoryMetaData.GetAbi() require.NoError(t, err, "Getting contract abi for OperatorFactory shouldn't fail") - latestBlockNum, err := chainClient.LatestBlockNumber(utils.TestContext(t)) + latestBlockNum, err := chainClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Subscribing to contract event log for OperatorFactory instance shouldn't fail") query := geth.FilterQuery{ FromBlock: big.NewInt(0).SetUint64(latestBlockNum), @@ -147,7 +147,7 @@ func SubscribeOperatorFactoryEvents( } eventLogs := make(chan types.Log) - sub, err := chainClient.SubscribeFilterLogs(utils.TestContext(t), query, eventLogs) + sub, err := chainClient.SubscribeFilterLogs(testcontext.Get(t), query, eventLogs) require.NoError(t, err, "Subscribing to contract event log for OperatorFactory instance shouldn't fail") go func() { defer sub.Unsubscribe() @@ -158,7 +158,7 @@ func SubscribeOperatorFactoryEvents( l.Error().Err(err).Msg("Error while watching for new contract events. Retrying Subscription") sub.Unsubscribe() - sub, err = chainClient.SubscribeFilterLogs(utils.TestContext(t), query, eventLogs) + sub, err = chainClient.SubscribeFilterLogs(testcontext.Get(t), query, eventLogs) require.NoError(t, err, "Subscribing to contract event log for OperatorFactory instance shouldn't fail") case vLog := <-eventLogs: eventDetails, err := contractABI.EventByID(vLog.Topics[0]) diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 254f2ca6ce6..f48d4157922 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -599,7 +599,7 @@ func FundSubscriptions( ) error { for _, subID := range subIDs { //Native Billing - amountWei := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) + amountWei := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountNative)) err := coordinator.FundSubscriptionWithNative( subID, amountWei, @@ -608,7 +608,7 @@ func FundSubscriptions( return fmt.Errorf("%s, err %w", ErrFundSubWithNativeToken, err) } //Link Billing - amountJuels := utils.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) + amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2PlusConfig.SubscriptionFundingAmountLink)) err = FundVRFCoordinatorV2_5Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) if err != nil { return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go index 9342f3629b9..6d398c685e0 100644 --- a/integration-tests/benchmark/keeper_test.go +++ b/integration-tests/benchmark/keeper_test.go @@ -22,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" @@ -434,7 +433,7 @@ func SetupAutomationBenchmarkEnv(t *testing.T) (*environment.Environment, blockc testNetwork.HTTPURLs = []string{internalHttpURLs[i]} testNetwork.URLs = []string{internalWsURLs[i]} testEnvironment.AddHelm(chainlink.New(i, map[string]any{ - "toml": client.AddNetworkDetailedConfig(keeperBenchmarkBaseTOML, networkDetailTOML, testNetwork), + "toml": networks.AddNetworkDetailedConfig(keeperBenchmarkBaseTOML, networkDetailTOML, testNetwork), "chainlink": chainlinkResources, "db": dbResources, })) diff --git a/integration-tests/chaos/automation_chaos_test.go b/integration-tests/chaos/automation_chaos_test.go index 6ebf14d806e..06352a93d72 100644 --- a/integration-tests/chaos/automation_chaos_test.go +++ b/integration-tests/chaos/automation_chaos_test.go @@ -18,13 +18,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" eth_contracts "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -41,7 +41,7 @@ ListenAddresses = ["0.0.0.0:6690"]` defaultAutomationSettings = map[string]interface{}{ "replicas": 6, - "toml": client.AddNetworksConfig(baseTOML, networks.MustGetSelectedNetworksFromEnv()[0]), + "toml": networks.AddNetworksConfig(baseTOML, networks.MustGetSelectedNetworksFromEnv()[0]), "db": map[string]interface{}{ "stateful": true, "capacity": "1Gi", @@ -132,7 +132,7 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -141,7 +141,7 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -150,9 +150,9 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, DurationStr: "1m", - ContainerNames: &[]*string{utils.Ptr("chainlink-db")}, + ContainerNames: &[]*string{ptr.Ptr("chainlink-db")}, }, }, NetworkChaosFailMajorityNetwork: { @@ -160,8 +160,8 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, - ToLabels: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, + FromLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, + ToLabels: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -170,8 +170,8 @@ func TestAutomationChaos(t *testing.T) { chainlink.New(0, defaultAutomationSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{"app": utils.Ptr("geth")}, - ToLabels: &map[string]*string{ChaosGroupMajorityPlus: utils.Ptr("1")}, + FromLabels: &map[string]*string{"app": ptr.Ptr("geth")}, + ToLabels: &map[string]*string{ChaosGroupMajorityPlus: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -269,7 +269,7 @@ func TestAutomationChaos(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(it_utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") @@ -284,7 +284,7 @@ func TestAutomationChaos(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(it_utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 10 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") diff --git a/integration-tests/chaos/ocr2vrf_chaos_test.go b/integration-tests/chaos/ocr2vrf_chaos_test.go index 8739a5960af..200114ddafb 100644 --- a/integration-tests/chaos/ocr2vrf_chaos_test.go +++ b/integration-tests/chaos/ocr2vrf_chaos_test.go @@ -17,7 +17,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/ocr2vrf_actions" @@ -25,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCR2VRFChaos(t *testing.T) { @@ -35,7 +35,7 @@ func TestOCR2VRFChaos(t *testing.T) { defaultOCR2VRFSettings := map[string]interface{}{ "replicas": 6, - "toml": client.AddNetworkDetailedConfig( + "toml": networks.AddNetworkDetailedConfig( config.BaseOCR2Config, config.DefaultOCR2VRFNetworkDetailTomlConfig, loadedNetwork, @@ -68,7 +68,7 @@ func TestOCR2VRFChaos(t *testing.T) { chainlink.New(0, defaultOCR2VRFSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -78,7 +78,7 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewFailPods, // &chaos.Props{ - // LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + // LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, // DurationStr: "1m", // }, //}, @@ -88,9 +88,9 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewFailPods, // &chaos.Props{ - // LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + // LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, // DurationStr: "1m", - // ContainerNames: &[]*string{utils.Ptr("chainlink-db")}, + // ContainerNames: &[]*string{ptr.Ptr("chainlink-db")}, // }, //}, //NetworkChaosFailMajorityNetwork: { @@ -98,8 +98,8 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewNetworkPartition, // &chaos.Props{ - // FromLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, - // ToLabels: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, + // FromLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, + // ToLabels: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, // DurationStr: "1m", // }, //}, @@ -108,8 +108,8 @@ func TestOCR2VRFChaos(t *testing.T) { // chainlink.New(0, defaultOCR2VRFSettings), // chaos.NewNetworkPartition, // &chaos.Props{ - // FromLabels: &map[string]*string{"app": utils.Ptr("geth")}, - // ToLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + // FromLabels: &map[string]*string{"app": ptr.Ptr("geth")}, + // ToLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, // DurationStr: "1m", // }, //}, @@ -186,7 +186,7 @@ func TestOCR2VRFChaos(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(testcontext.Get(t), requestID, big.NewInt(int64(i))) require.NoError(t, err) l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") @@ -213,7 +213,7 @@ func TestOCR2VRFChaos(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(testcontext.Get(t), requestID, big.NewInt(int64(i))) require.NoError(t, err, "Error getting Randomness result from Consumer Contract") l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 76e25d92000..5368997daab 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -20,13 +20,13 @@ import ( mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -52,7 +52,7 @@ var ( ) func TestMain(m *testing.M) { - defaultOCRSettings["toml"] = client.AddNetworksConfig(config.BaseOCRP2PV1Config, networks.MustGetSelectedNetworksFromEnv()[0]) + defaultOCRSettings["toml"] = networks.AddNetworksConfig(config.BaseOCRP2PV1Config, networks.MustGetSelectedNetworksFromEnv()[0]) os.Exit(m.Run()) } @@ -80,8 +80,8 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, - ToLabels: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, + FromLabels: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, + ToLabels: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -90,8 +90,8 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewNetworkPartition, &chaos.Props{ - FromLabels: &map[string]*string{"app": utils.Ptr("geth")}, - ToLabels: &map[string]*string{ChaosGroupMajorityPlus: utils.Ptr("1")}, + FromLabels: &map[string]*string{"app": ptr.Ptr("geth")}, + ToLabels: &map[string]*string{ChaosGroupMajorityPlus: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -100,7 +100,7 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMinority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMinority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -109,7 +109,7 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, DurationStr: "1m", }, }, @@ -118,9 +118,9 @@ func TestOCRChaos(t *testing.T) { chainlink.New(0, defaultOCRSettings), chaos.NewFailPods, &chaos.Props{ - LabelsSelector: &map[string]*string{ChaosGroupMajority: utils.Ptr("1")}, + LabelsSelector: &map[string]*string{ChaosGroupMajority: ptr.Ptr("1")}, DurationStr: "1m", - ContainerNames: &[]*string{utils.Ptr("chainlink-db")}, + ContainerNames: &[]*string{ptr.Ptr("chainlink-db")}, }, }, } @@ -195,7 +195,7 @@ func TestOCRChaos(t *testing.T) { err := ocr.RequestNewRound() require.NoError(t, err, "Error requesting new round") } - round, err := ocrInstances[0].GetLatestRound(it_utils.TestContext(t)) + round, err := ocrInstances[0].GetLatestRound(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred()) l.Info().Int64("RoundID", round.RoundId.Int64()).Msg("Latest OCR Round") if round.RoundId.Int64() == chaosStartRound && !chaosApplied { diff --git a/integration-tests/client/chainlink_config_builder.go b/integration-tests/client/chainlink_config_builder.go deleted file mode 100644 index 13cc1e7fe93..00000000000 --- a/integration-tests/client/chainlink_config_builder.go +++ /dev/null @@ -1,52 +0,0 @@ -package client - -import ( - "fmt" - "os" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" -) - -const ( - pyroscopeTOML = `[Pyroscope] -ServerAddress = '%s' -Environment = '%s'` - - secretTOML = ` -[Mercury.Credentials.cred1] -URL = '%s' -Username = '%s' -Password = '%s' -` -) - -// AddNetworksConfig adds EVM network configurations to a base config TOML. Useful for adding networks with default -// settings. See AddNetworkDetailedConfig for adding more detailed network configuration. -func AddNetworksConfig(baseTOML string, networks ...blockchain.EVMNetwork) string { - networksToml := "" - for _, network := range networks { - networksToml = fmt.Sprintf("%s\n\n%s", networksToml, network.MustChainlinkTOML("")) - } - return fmt.Sprintf("%s\n\n%s\n\n%s", baseTOML, pyroscopeSettings(), networksToml) -} - -func AddSecretTomlConfig(url, username, password string) string { - return fmt.Sprintf(secretTOML, url, username, password) -} - -// AddNetworkDetailedConfig adds EVM config to a base TOML. Also takes a detailed network config TOML where values like -// using transaction forwarders can be included. -// See https://github.com/smartcontractkit/chainlink/blob/develop/docs/CONFIG.md#EVM -func AddNetworkDetailedConfig(baseTOML, detailedNetworkConfig string, network blockchain.EVMNetwork) string { - return fmt.Sprintf("%s\n\n%s\n\n%s", baseTOML, pyroscopeSettings(), network.MustChainlinkTOML(detailedNetworkConfig)) -} - -func pyroscopeSettings() string { - pyroscopeServer := os.Getenv(config.EnvVarPyroscopeServer) - pyroscopeEnv := os.Getenv(config.EnvVarPyroscopeEnvironment) - if pyroscopeServer == "" { - return "" - } - return fmt.Sprintf(pyroscopeTOML, pyroscopeServer, pyroscopeEnv) -} diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index 3c0a6d3af76..c781ddfc1b8 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -23,11 +23,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logwatch" + "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" "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/utils" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/utils/templates" ) @@ -112,7 +113,7 @@ func (n *ClNode) SetTestLogger(t *testing.T) { // Restart restarts only CL node, DB container is reused func (n *ClNode) Restart(cfg *chainlink.Config) error { - if err := n.Container.Terminate(utils.TestContext(n.t)); err != nil { + if err := n.Container.Terminate(testcontext.Get(n.t)); err != nil { return err } n.NodeConfig = cfg @@ -138,7 +139,7 @@ func (n *ClNode) PrimaryETHAddress() (string, error) { func (n *ClNode) AddBootstrapJob(verifierAddr common.Address, chainId int64, feedId [32]byte) (*client.Job, error) { - spec := utils.BuildBootstrapSpec(verifierAddr, chainId, feedId) + spec := it_utils.BuildBootstrapSpec(verifierAddr, chainId, feedId) return n.API.MustCreateJob(spec) } @@ -166,7 +167,7 @@ func (n *ClNode) AddMercuryOCRJob(verifierAddr common.Address, fromBlock uint64, } } - bridges := utils.BuildBridges(eaUrls) + bridges := it_utils.BuildBridges(eaUrls) for index := range bridges { err = n.API.MustCreateBridge(&bridges[index]) if err != nil { @@ -181,7 +182,7 @@ func (n *ClNode) AddMercuryOCRJob(verifierAddr common.Address, fromBlock uint64, allowedFaults = 2 } - spec := utils.BuildOCRSpec( + spec := it_utils.BuildOCRSpec( verifierAddr, chainId, fromBlock, feedId, bridges, csaPubKey, mercuryServerUrl, mercuryServerPubKey, nodeOCRKeyId[0], bootstrapUrl, allowedFaults) @@ -190,7 +191,7 @@ func (n *ClNode) AddMercuryOCRJob(verifierAddr common.Address, fromBlock uint64, } func (n *ClNode) GetContainerName() string { - name, err := n.Container.Name(utils.TestContext(n.t)) + name, err := n.Container.Name(testcontext.Get(n.t)) if err != nil { return "" } @@ -282,15 +283,15 @@ func (n *ClNode) StartContainer() error { return fmt.Errorf("%s err: %w", ErrStartCLNodeContainer, err) } if n.lw != nil { - if err := n.lw.ConnectContainer(utils.TestContext(n.t), container, "cl-node", true); err != nil { + if err := n.lw.ConnectContainer(testcontext.Get(n.t), container, "cl-node", true); err != nil { return err } } - clEndpoint, err := test_env.GetEndpoint(utils.TestContext(n.t), container, "http") + clEndpoint, err := test_env.GetEndpoint(testcontext.Get(n.t), container, "http") if err != nil { return err } - ip, err := container.ContainerIP(utils.TestContext(n.t)) + ip, err := container.ContainerIP(testcontext.Get(n.t)) if err != nil { return err } diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 9987bab2fe0..6278ec13987 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -22,11 +22,11 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/logwatch" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -266,7 +266,7 @@ func (te *CLClusterTestEnv) collectTestLogs() error { return err } defer logFile.Close() - logReader, err := node.Container.Logs(utils.TestContext(te.t)) + logReader, err := node.Container.Logs(testcontext.Get(te.t)) if err != nil { return err } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index a450dd235ad..99dd990bec5 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd - github.com/smartcontractkit/chainlink-testing-framework v1.18.6 + github.com/smartcontractkit/chainlink-testing-framework v1.19.0 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f882a7bb570..0402b12f3f8 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2375,8 +2375,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.18.6 h1:UL3DxsPflSRALP62rsg5v3NdOsa8RHGhHMUImoWDD6k= -github.com/smartcontractkit/chainlink-testing-framework v1.18.6/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.19.0 h1:ungyY1gYcXCtmdX2yCon8pkx9HgPPLg4aNAhKNZFP5c= +github.com/smartcontractkit/chainlink-testing-framework v1.19.0/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/load/vrfv2/onchain_monitoring.go b/integration-tests/load/vrfv2/onchain_monitoring.go index 879c7089e16..66af1807acf 100644 --- a/integration-tests/load/vrfv2/onchain_monitoring.go +++ b/integration-tests/load/vrfv2/onchain_monitoring.go @@ -7,8 +7,9 @@ import ( "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ @@ -36,7 +37,7 @@ func MonitorLoadStats(t *testing.T, vrfv2Contracts *vrfv2_actions.VRFV2Contracts } for { time.Sleep(1 * time.Second) - metrics, err := vrfv2Contracts.LoadTestConsumer.GetLoadTestMetrics(utils.TestContext(t)) + metrics, err := vrfv2Contracts.LoadTestConsumer.GetLoadTestMetrics(testcontext.Get(t)) if err != nil { log.Error().Err(err).Msg(ErrMetrics) } diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index e7734fee0d5..11e160297f0 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -14,8 +14,8 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" @@ -99,7 +99,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { Str("Returning funds from SubID", subID.String()). Str("Returning funds to", eoaWalletAddress). Msg("Canceling subscription and returning funds to subscription owner") - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(utils.TestContext(t), subID) + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subID) if err != nil { l.Error().Err(err).Msg("Error checking if pending requests exist") } @@ -230,7 +230,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") for _, subID := range subIDs { - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information for subscription %s", subID.String()) vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) } diff --git a/integration-tests/migration/upgrade_version_test.go b/integration-tests/migration/upgrade_version_test.go index c851f36ec62..d1f07a52b74 100644 --- a/integration-tests/migration/upgrade_version_test.go +++ b/integration-tests/migration/upgrade_version_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" ) @@ -19,9 +19,9 @@ func TestVersionUpgrade(t *testing.T) { Build() require.NoError(t, err) - upgradeImage, err := utils.GetEnv("UPGRADE_IMAGE") + upgradeImage, err := osutil.GetEnv("UPGRADE_IMAGE") require.NoError(t, err, "Error getting upgrade image") - upgradeVersion, err := utils.GetEnv("UPGRADE_VERSION") + upgradeVersion, err := osutil.GetEnv("UPGRADE_VERSION") require.NoError(t, err, "Error getting upgrade version") _ = os.Setenv("CHAINLINK_IMAGE", upgradeImage) diff --git a/integration-tests/performance/cron_test.go b/integration-tests/performance/cron_test.go index 7e90d29221d..38d861cee07 100644 --- a/integration-tests/performance/cron_test.go +++ b/integration-tests/performance/cron_test.go @@ -121,7 +121,7 @@ func setupCronTest(t *testing.T) (testEnvironment *environment.Environment) { HTTPWriteTimout = '300s'` cd := chainlink.New(0, map[string]interface{}{ "replicas": 1, - "toml": client.AddNetworksConfig(baseTOML, network), + "toml": networks.AddNetworksConfig(baseTOML, network), }) testEnvironment = environment.New(&environment.Config{ diff --git a/integration-tests/performance/directrequest_test.go b/integration-tests/performance/directrequest_test.go index 1a3f1d2a010..0fe1ca37e15 100644 --- a/integration-tests/performance/directrequest_test.go +++ b/integration-tests/performance/directrequest_test.go @@ -19,12 +19,12 @@ import ( mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" - "github.com/smartcontractkit/chainlink/integration-tests/utils" "github.com/google/uuid" ) @@ -108,7 +108,7 @@ func TestDirectRequestPerformance(t *testing.T) { gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(utils.TestContext(t)) + d, err := consumer.Data(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") l.Debug().Int64("Data", d.Int64()).Msg("Found on chain") @@ -142,7 +142,7 @@ func setupDirectRequestTest(t *testing.T) (testEnvironment *environment.Environm HTTPWriteTimout = '300s'` cd := chainlink.New(0, map[string]interface{}{ "replicas": 1, - "toml": client.AddNetworksConfig(baseTOML, network), + "toml": networks.AddNetworksConfig(baseTOML, network), }) testEnvironment = environment.New(&environment.Config{ diff --git a/integration-tests/performance/flux_test.go b/integration-tests/performance/flux_test.go index 18b13ab9076..5fa31b626ae 100644 --- a/integration-tests/performance/flux_test.go +++ b/integration-tests/performance/flux_test.go @@ -20,12 +20,12 @@ import ( mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestFluxPerformance(t *testing.T) { @@ -83,7 +83,7 @@ func TestFluxPerformance(t *testing.T) { require.NoError(t, err, "Setting oracle options in the Flux Aggregator contract shouldn't fail") err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - oracles, err := fluxInstance.GetOracles(utils.TestContext(t)) + oracles, err := fluxInstance.GetOracles(testcontext.Get(t)) require.NoError(t, err, "Getting oracle details from the Flux aggregator contract shouldn't fail") l.Info().Str("Oracles", strings.Join(oracles, ",")).Msg("Oracles set") @@ -120,7 +120,7 @@ func TestFluxPerformance(t *testing.T) { chainClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err := fluxInstance.GetContractData(utils.TestContext(t)) + data, err := fluxInstance.GetContractData(testcontext.Get(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") l.Info().Interface("Data", data).Msg("Round data") require.Equal(t, int64(1e5), data.LatestRoundData.Answer.Int64(), @@ -140,7 +140,7 @@ func TestFluxPerformance(t *testing.T) { require.NoError(t, err, "Setting value path in mock server shouldn't fail") err = chainClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err = fluxInstance.GetContractData(utils.TestContext(t)) + data, err = fluxInstance.GetContractData(testcontext.Get(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") require.Equal(t, int64(1e10), data.LatestRoundData.Answer.Int64(), "Expected latest round answer to be %d, but found %d", int64(1e10), data.LatestRoundData.Answer.Int64()) @@ -153,7 +153,7 @@ func TestFluxPerformance(t *testing.T) { l.Info().Interface("data", data).Msg("Round data") for _, oracleAddr := range nodeAddresses { - payment, _ := fluxInstance.WithdrawablePayment(utils.TestContext(t), oracleAddr) + payment, _ := fluxInstance.WithdrawablePayment(testcontext.Get(t), oracleAddr) require.Equal(t, int64(2), payment.Int64(), "Expected flux aggregator contract's withdrawable payment to be %d, but found %d", int64(2), payment.Int64()) } @@ -189,7 +189,7 @@ HTTPWriteTimout = '300s' Enabled = true` cd := chainlink.New(0, map[string]interface{}{ "replicas": 3, - "toml": client.AddNetworksConfig(baseTOML, testNetwork), + "toml": networks.AddNetworksConfig(baseTOML, testNetwork), }) testEnvironment = environment.New(&environment.Config{ diff --git a/integration-tests/performance/keeper_test.go b/integration-tests/performance/keeper_test.go index 8e273a96f69..ac6e9e6a579 100644 --- a/integration-tests/performance/keeper_test.go +++ b/integration-tests/performance/keeper_test.go @@ -19,13 +19,13 @@ import ( mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var keeperDefaultRegistryConfig = contracts.KeeperRegistrySettings{ @@ -74,7 +74,7 @@ func TestKeeperPerformance(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) @@ -95,7 +95,7 @@ func TestKeeperPerformance(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterCancellation[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Index", i).Int64("Upkeeps Performed", countersAfterCancellation[i].Int64()).Msg("Cancelled Upkeep") } @@ -103,7 +103,7 @@ func TestKeeperPerformance(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant because the upkeep was cancelled, so it shouldn't increase anymore - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterCancellation[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -155,7 +155,7 @@ PerformGasOverhead = 150_000` networkName := strings.ReplaceAll(strings.ToLower(network.Name), " ", "-") cd := chainlink.New(0, map[string]interface{}{ "replicas": 5, - "toml": client.AddNetworksConfig(baseTOML, network), + "toml": networks.AddNetworksConfig(baseTOML, network), }) testEnvironment := environment.New( diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go index 47879cebb81..7f91f4321ac 100644 --- a/integration-tests/performance/ocr_test.go +++ b/integration-tests/performance/ocr_test.go @@ -17,14 +17,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver" mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCRBasic(t *testing.T) { @@ -64,7 +63,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(1, ocrInstances, chainClient, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -73,7 +72,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(2, ocrInstances, chainClient, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } @@ -113,7 +112,7 @@ ListenIP = '0.0.0.0' ListenPort = 6690` cd := chainlink.New(0, map[string]interface{}{ "replicas": 6, - "toml": client.AddNetworksConfig(baseTOML, testNetwork), + "toml": networks.AddNetworksConfig(baseTOML, testNetwork), }) testEnvironment = environment.New(&environment.Config{ diff --git a/integration-tests/performance/vrf_test.go b/integration-tests/performance/vrf_test.go index 7a38a454955..cad4ea5eebd 100644 --- a/integration-tests/performance/vrf_test.go +++ b/integration-tests/performance/vrf_test.go @@ -17,12 +17,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFBasic(t *testing.T) { @@ -97,7 +97,7 @@ func TestVRFBasic(t *testing.T) { encodedProvingKeys := make([][2]*big.Int, 0) encodedProvingKeys = append(encodedProvingKeys, provingKey) - requestHash, err := coordinator.HashOfKey(utils.TestContext(t), encodedProvingKeys[0]) + requestHash, err := coordinator.HashOfKey(testcontext.Get(t), encodedProvingKeys[0]) require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") err = consumer.RequestRandomness(requestHash, big.NewInt(1)) require.NoError(t, err, "Requesting randomness shouldn't fail") @@ -108,7 +108,7 @@ func TestVRFBasic(t *testing.T) { jobRuns, err := chainlinkNodes[0].MustReadRunsByJob(job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - out, err := consumer.RandomnessOutput(utils.TestContext(t)) + out, err := consumer.RandomnessOutput(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") // Checks that the job has actually run g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), @@ -147,7 +147,7 @@ func setupVRFTest(t *testing.T) (testEnvironment *environment.Environment, testN baseTOML := `[WebServer] HTTPWriteTimout = '300s'` cd := chainlink.New(0, map[string]interface{}{ - "toml": client.AddNetworksConfig(baseTOML, testNetwork), + "toml": networks.AddNetworksConfig(baseTOML, testNetwork), }) testEnvironment = environment.New(&environment.Config{ diff --git a/integration-tests/reorg/automation_reorg_test.go b/integration-tests/reorg/automation_reorg_test.go index 58cd147201e..dae977d3eea 100644 --- a/integration-tests/reorg/automation_reorg_test.go +++ b/integration-tests/reorg/automation_reorg_test.go @@ -18,12 +18,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -49,7 +49,7 @@ Mode = 'FixedPrice' LimitDefault = 5_000_000` activeEVMNetwork = networks.MustGetSelectedNetworksFromEnv()[0] defaultAutomationSettings = map[string]interface{}{ - "toml": client.AddNetworkDetailedConfig(baseTOML, networkTOML, activeEVMNetwork), + "toml": networks.AddNetworkDetailedConfig(baseTOML, networkTOML, activeEVMNetwork), "db": map[string]interface{}{ "stateful": false, "capacity": "1Gi", @@ -210,7 +210,7 @@ func TestAutomationReorg(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(it_utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") @@ -241,7 +241,7 @@ func TestAutomationReorg(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they reach 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(it_utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 10 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") @@ -258,7 +258,7 @@ func TestAutomationReorg(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they reach 20 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(it_utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 20 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") diff --git a/integration-tests/reorg/reorg_confirmer.go b/integration-tests/reorg/reorg_confirmer.go index 2193131680a..a5659e66783 100644 --- a/integration-tests/reorg/reorg_confirmer.go +++ b/integration-tests/reorg/reorg_confirmer.go @@ -15,8 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/chaos" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" - - "github.com/smartcontractkit/chainlink/integration-tests/utils" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" ) // The steps are: @@ -232,8 +231,8 @@ func (rc *ReorgController) forkNetwork(header blockchain.NodeHeader) error { rc.cfg.Env.Cfg.Namespace, &chaos.Props{ DurationStr: "999h", - FromLabels: &map[string]*string{"app": utils.Ptr(reorg.TXNodesAppLabel)}, - ToLabels: &map[string]*string{"app": utils.Ptr(reorg.MinerNodesAppLabel)}, + FromLabels: &map[string]*string{"app": ptr.Ptr(reorg.TXNodesAppLabel)}, + ToLabels: &map[string]*string{"app": ptr.Ptr(reorg.MinerNodesAppLabel)}, }, )) rc.chaosExperimentName = expName diff --git a/integration-tests/reorg/reorg_test.go b/integration-tests/reorg/reorg_test.go index d5fefdbc562..fc35047d0e4 100644 --- a/integration-tests/reorg/reorg_test.go +++ b/integration-tests/reorg/reorg_test.go @@ -9,6 +9,8 @@ import ( "time" "github.com/google/uuid" + "github.com/onsi/gomega" + "github.com/rs/zerolog/log" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -21,16 +23,12 @@ import ( mockservercfg "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mockserver-cfg" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/reorg" "github.com/smartcontractkit/chainlink-testing-framework/logging" - - "github.com/onsi/gomega" - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -127,7 +125,7 @@ func TestDirectRequestReorg(t *testing.T) { netCfg := fmt.Sprintf(networkDRTOML, EVMFinalityDepth, EVMTrackerHistoryDepth) chainlinkDeployment := chainlink.New(0, map[string]interface{}{ "replicas": 1, - "toml": client.AddNetworkDetailedConfig(baseDRTOML, netCfg, activeEVMNetwork), + "toml": networks.AddNetworkDetailedConfig(baseDRTOML, netCfg, activeEVMNetwork), }) err = testEnvironment.AddHelm(chainlinkDeployment).Run() @@ -221,7 +219,7 @@ func TestDirectRequestReorg(t *testing.T) { gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(it_utils.TestContext(t)) + d, err := consumer.Data(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") log.Debug().Int64("Data", d.Int64()).Msg("Found on chain") diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 87074e786dc..05d19d5ccd4 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -22,6 +22,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" @@ -33,7 +35,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var utilsABI = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) @@ -171,7 +172,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") @@ -193,7 +194,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are increasing by 5 in each step within 5 minutes for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep ID", i).Msg("Number of upkeeps performed") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">=", int64(expect)), @@ -216,7 +217,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterCancellation[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeep Count", countersAfterCancellation[i].Int64()).Int("Upkeep Index", i).Msg("Cancelled upkeep") } @@ -225,7 +226,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant (At most increase by 1 to account for stale performs) because the upkeep was cancelled - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterCancellation[i].Int64()+1), "Expected consumer counter to remain less than or equal to %d, but got %d", @@ -271,7 +272,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := 5 l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") @@ -328,7 +329,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { time.Sleep(10 * time.Second) for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterSetNoMatch[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterSetNoMatch[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeep Count", countersAfterSetNoMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") } @@ -338,7 +339,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant (At most increase by 2 to account for stale performs) because the upkeep trigger config is not met bufferCount := int64(2) - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterSetNoMatch[i].Int64()+bufferCount), "Expected consumer counter to remain less than or equal to %d, but got %d", @@ -374,7 +375,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterSetMatch[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterSetMatch[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int64("Upkeep Count", countersAfterSetMatch[i].Int64()).Int("Upkeep Index", i).Msg("Upkeep") } @@ -393,7 +394,7 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) expect := int64(5) l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") @@ -424,7 +425,7 @@ func TestAutomationAddFunds(t *testing.T) { gom := gomega.NewGomegaWithT(t) // Since the upkeep is currently underfunded, check that it doesn't get executed gom.Consistently(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(utils.TestContext(t)) + counter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), "Expected consumer counter to remain zero, but got %d", counter.Int64()) @@ -444,7 +445,7 @@ func TestAutomationAddFunds(t *testing.T) { // Now the new upkeep should be performing because we added enough funds gom.Eventually(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(utils.TestContext(t)) + counter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -476,7 +477,7 @@ func TestAutomationPauseUnPause(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)), "Expected consumer counter to be greater than 5, but got %d", counter.Int64()) @@ -496,7 +497,7 @@ func TestAutomationPauseUnPause(t *testing.T) { var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterPause[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Upkeep Index", i).Int64("Upkeeps Performed", countersAfterPause[i].Int64()).Msg("Paused Upkeep") } @@ -505,7 +506,7 @@ func TestAutomationPauseUnPause(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // In most cases counters should remain constant, but there might be a straggling perform tx which // gets committed later and increases counter by 1 - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterPause[i].Int64()+1), "Expected consumer counter not have increased more than %d, but got %d", @@ -525,7 +526,7 @@ func TestAutomationPauseUnPause(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 + numbers of performing before pause for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", countersAfterPause[i].Int64()+1), "Expected consumer counter to be greater than %d, but got %d", countersAfterPause[i].Int64()+1, counter.Int64()) @@ -561,7 +562,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { // store the value of their initial counters in order to compare later on that the value increased. gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -581,7 +582,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { // Test that the newly registered upkeep is also performing. gom.Eventually(func(g gomega.Gomega) { - counter, err := newUpkeep.Counter(utils.TestContext(t)) + counter, err := newUpkeep.Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling newly deployed upkeep's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -590,7 +591,7 @@ func TestAutomationRegisterUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(utils.TestContext(t)) + currentCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info(). @@ -630,7 +631,7 @@ func TestAutomationPauseRegistry(t *testing.T) { // Observe that the upkeeps which are initially registered are performing gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d") @@ -646,7 +647,7 @@ func TestAutomationPauseRegistry(t *testing.T) { // Store how many times each upkeep performed once the registry was successfully paused var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterPause[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) } @@ -654,7 +655,7 @@ func TestAutomationPauseRegistry(t *testing.T) { // because they are no longer getting serviced gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterPause[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -691,7 +692,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // Watch upkeeps being performed and store their counters in order to compare them later in the test gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -710,7 +711,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // Assert that upkeeps are still performed and their counters have increased gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(utils.TestContext(t)) + currentCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", initialCounters[i].Int64()), "Expected counter to have increased from initial value of %s, but got %s", @@ -731,7 +732,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // See how many times each upkeep was executed var countersAfterNoMoreNodes = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterNoMoreNodes[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterNoMoreNodes[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Upkeep Index", i).Int64("Performed", countersAfterNoMoreNodes[i].Int64()).Msg("Upkeeps Performed") } @@ -740,7 +741,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // all the nodes were taken down gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterNoMoreNodes[i].Int64()+1), "Expected consumer counter to not have increased more than %d, but got %d", @@ -789,7 +790,7 @@ func TestAutomationPerformSimulation(t *testing.T) { // Initially performGas is set high, so performUpkeep reverts and no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { // Consumer count should remain at 0 - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.Equal(int64(0)), "Expected consumer counter to remain constant at %d, but got %d", 0, cnt.Int64(), @@ -797,14 +798,14 @@ func TestAutomationPerformSimulation(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Set performGas on consumer to be low, so that performUpkeep starts becoming successful - err := consumerPerformance.SetPerformGasToBurn(utils.TestContext(t), big.NewInt(100000)) + err := consumerPerformance.SetPerformGasToBurn(testcontext.Get(t), big.NewInt(100000)) require.NoError(t, err, "Perform gas should be set successfully on consumer") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for set perform gas tx") // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -854,7 +855,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Initially performGas is set higher than defaultUpkeepGasLimit, so no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.Equal(int64(0)), @@ -870,7 +871,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -878,19 +879,19 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m to perform once, 1m buffer // Now increase the checkGasBurn on consumer, upkeep should stop performing - err = consumerPerformance.SetCheckGasToBurn(utils.TestContext(t), big.NewInt(3000000)) + err = consumerPerformance.SetCheckGasToBurn(testcontext.Get(t), big.NewInt(3000000)) require.NoError(t, err, "Check gas burn should be set successfully on consumer") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetCheckGasToBurn tx") // Get existing performed count - existingCnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + existingCnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) require.NoError(t, err, "Calling consumer's counter shouldn't fail") l.Info().Int64("Upkeep counter", existingCnt.Int64()).Msg("Upkeep counter when check gas increased") // In most cases count should remain constant, but it might increase by upto 1 due to pending perform gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.BeNumerically("<=", existingCnt.Int64()+1), @@ -898,7 +899,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { ) }, "1m", "1s").Should(gomega.Succeed()) - existingCnt, err = consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + existingCnt, err = consumerPerformance.GetUpkeepCount(testcontext.Get(t)) require.NoError(t, err, "Calling consumer's counter shouldn't fail") existingCntInt := existingCnt.Int64() l.Info().Int64("Upkeep counter", existingCntInt).Msg("Upkeep counter when consistently block finished") @@ -918,7 +919,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Upkeep should start performing again, and it should get regularly performed gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", existingCntInt), "Expected consumer counter to be greater than %d, but got %d", existingCntInt, cnt.Int64(), @@ -962,7 +963,7 @@ func TestUpdateCheckData(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { // expect the counter to remain 0 because perform data does not match for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(utils.TestContext(t)) + counter, err := performDataChecker[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker"+ " for upkeep at index "+strconv.Itoa(i)) g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), @@ -981,7 +982,7 @@ func TestUpdateCheckData(t *testing.T) { // retrieve new check data for all upkeeps for i := 0; i < len(upkeepIDs); i++ { - upkeep, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepIDs[i]) + upkeep, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepIDs[i]) require.NoError(t, err, "Failed to get upkeep info at index %d", i) require.Equal(t, []byte(automationExpectedData), upkeep.CheckData, "Upkeep data not as expected") } @@ -989,7 +990,7 @@ func TestUpdateCheckData(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(utils.TestContext(t)) + counter, err := performDataChecker[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker counter"+ " for upkeep at index "+strconv.Itoa(i)) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -1030,11 +1031,11 @@ func setupAutomationTestDocker( // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) syncInterval := models.MustMakeDuration(5 * time.Minute) - clNodeConfig.Feature.LogPoller = utils.Ptr[bool](true) - clNodeConfig.OCR2.Enabled = utils.Ptr[bool](true) - clNodeConfig.Keeper.TurnLookBack = utils.Ptr[int64](int64(0)) + clNodeConfig.Feature.LogPoller = ptr.Ptr[bool](true) + clNodeConfig.OCR2.Enabled = ptr.Ptr[bool](true) + clNodeConfig.Keeper.TurnLookBack = ptr.Ptr[int64](int64(0)) clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval - clNodeConfig.Keeper.Registry.PerformGasOverhead = utils.Ptr[uint32](uint32(150000)) + clNodeConfig.Keeper.Registry.PerformGasOverhead = ptr.Ptr[uint32](uint32(150000)) clNodeConfig.P2P.V2.AnnounceAddresses = &[]string{"0.0.0.0:6690"} clNodeConfig.P2P.V2.ListenAddresses = &[]string{"0.0.0.0:6690"} diff --git a/integration-tests/smoke/flux_test.go b/integration-tests/smoke/flux_test.go index 2997ff1c74a..72dc15e7b11 100644 --- a/integration-tests/smoke/flux_test.go +++ b/integration-tests/smoke/flux_test.go @@ -13,12 +13,12 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestFluxBasic(t *testing.T) { @@ -74,7 +74,7 @@ func TestFluxBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - oracles, err := fluxInstance.GetOracles(utils.TestContext(t)) + oracles, err := fluxInstance.GetOracles(testcontext.Get(t)) require.NoError(t, err, "Getting oracle details from the Flux aggregator contract shouldn't fail") l.Info().Str("Oracles", strings.Join(oracles, ",")).Msg("Oracles set") @@ -108,7 +108,7 @@ func TestFluxBasic(t *testing.T) { env.EVMClient.AddHeaderEventSubscription(fluxInstance.Address(), fluxRound) err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err := fluxInstance.GetContractData(utils.TestContext(t)) + data, err := fluxInstance.GetContractData(testcontext.Get(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") require.Equal(t, int64(1e5), data.LatestRoundData.Answer.Int64(), "Expected latest round answer to be %d, but found %d", int64(1e5), data.LatestRoundData.Answer.Int64()) @@ -127,7 +127,7 @@ func TestFluxBasic(t *testing.T) { require.NoError(t, err, "Setting value path in mock server shouldn't fail") err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Waiting for event subscriptions in nodes shouldn't fail") - data, err = fluxInstance.GetContractData(utils.TestContext(t)) + data, err = fluxInstance.GetContractData(testcontext.Get(t)) require.NoError(t, err, "Getting contract data from flux aggregator contract shouldn't fail") require.Equal(t, int64(1e10), data.LatestRoundData.Answer.Int64(), "Expected latest round answer to be %d, but found %d", int64(1e10), data.LatestRoundData.Answer.Int64()) @@ -140,7 +140,7 @@ func TestFluxBasic(t *testing.T) { l.Info().Interface("data", data).Msg("Round data") for _, oracleAddr := range nodeAddresses { - payment, _ := fluxInstance.WithdrawablePayment(utils.TestContext(t), oracleAddr) + payment, _ := fluxInstance.WithdrawablePayment(testcontext.Get(t), oracleAddr) require.Equal(t, int64(2), payment.Int64(), "Expected flux aggregator contract's withdrawable payment to be %d, but found %d", int64(2), payment.Int64()) } diff --git a/integration-tests/smoke/forwarder_ocr_test.go b/integration-tests/smoke/forwarder_ocr_test.go index 7203e031780..d4f9b5884a2 100644 --- a/integration-tests/smoke/forwarder_ocr_test.go +++ b/integration-tests/smoke/forwarder_ocr_test.go @@ -8,10 +8,10 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestForwarderOCRBasic(t *testing.T) { @@ -72,7 +72,7 @@ func TestForwarderOCRBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -83,7 +83,7 @@ func TestForwarderOCRBasic(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") - answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index be87eb56292..c9fe3cb11d9 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestForwarderOCR2Basic(t *testing.T) { @@ -92,7 +92,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.StartNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCRv2 contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCRw contract to be 5 but got %d", answer.Int64()) @@ -103,7 +103,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.StartNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Error getting latest OCRv2 answer") require.Equal(t, int64(ocrRoundVal), answer.Int64(), fmt.Sprintf("Expected latest answer from OCRv2 contract to be %d but got %d", ocrRoundVal, answer.Int64())) } diff --git a/integration-tests/smoke/keeper_test.go b/integration-tests/smoke/keeper_test.go index 6f892e60aca..bcf64a5febd 100644 --- a/integration-tests/smoke/keeper_test.go +++ b/integration-tests/smoke/keeper_test.go @@ -13,6 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -109,7 +109,7 @@ func TestKeeperBasicSmoke(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) @@ -131,7 +131,7 @@ func TestKeeperBasicSmoke(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterCancellation[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterCancellation[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Failed to retrieve consumer counter for upkeep at index %d", i) l.Info().Int("Index", i).Int64("Upkeeps Performed", countersAfterCancellation[i].Int64()).Msg("Cancelled Upkeep") } @@ -139,7 +139,7 @@ func TestKeeperBasicSmoke(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { // Expect the counter to remain constant because the upkeep was cancelled, so it shouldn't increase anymore - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterCancellation[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -187,11 +187,11 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { // Wait for upkeep to be performed twice by different keepers (buddies) gom.Eventually(func(g gomega.Gomega) error { - counter, err := consumers[0].Counter(utils.TestContext(t)) + counter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Number of upkeeps performed") - upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -205,7 +205,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { }, "1m", "1s").Should(gomega.Succeed()) gom.Eventually(func(g gomega.Gomega) error { - upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -219,7 +219,7 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { // Expect no new keepers to perform for a while gom.Consistently(func(g gomega.Gomega) { - upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -235,11 +235,11 @@ func TestKeeperBlockCountPerTurn(t *testing.T) { // Expect a new keeper to perform gom.Eventually(func(g gomega.Gomega) error { - counter, err := consumers[0].Counter(utils.TestContext(t)) + counter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info().Int64("Upkeep counter", counter.Int64()).Msg("Num upkeeps performed") - upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") latestKeeper := upkeepInfo.LastKeeper @@ -296,7 +296,7 @@ func TestKeeperSimulation(t *testing.T) { // Initially performGas is set high, so performUpkeep reverts and no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { // Consumer count should remain at 0 - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.Equal(int64(0)), @@ -304,20 +304,20 @@ func TestKeeperSimulation(t *testing.T) { ) // Not even reverted upkeeps should be performed. Last keeper for the upkeep should be 0 address - upkeepInfo, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepID) + upkeepInfo, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Registry's getUpkeep shouldn't fail") g.Expect(upkeepInfo.LastKeeper).Should(gomega.Equal(actions.ZeroAddress.String()), "Last keeper should be zero address") }, "1m", "1s").Should(gomega.Succeed()) // Set performGas on consumer to be low, so that performUpkeep starts becoming successful - err = consumerPerformance.SetPerformGasToBurn(utils.TestContext(t), big.NewInt(100000)) + err = consumerPerformance.SetPerformGasToBurn(testcontext.Get(t), big.NewInt(100000)) require.NoError(t, err, "Error setting PerformGasToBurn") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting to set PerformGasToBurn") // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) error { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -368,7 +368,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // Initially performGas is set higher than defaultUpkeepGasLimit, so no upkeep should be performed gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.Equal(int64(0)), @@ -384,7 +384,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // Upkeep should now start performing gom.Eventually(func(g gomega.Gomega) error { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", cnt.Int64(), @@ -393,13 +393,13 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { }, "1m", "1s").Should(gomega.Succeed()) // Now increase the checkGasBurn on consumer, upkeep should stop performing - err = consumerPerformance.SetCheckGasToBurn(utils.TestContext(t), big.NewInt(3000000)) + err = consumerPerformance.SetCheckGasToBurn(testcontext.Get(t), big.NewInt(3000000)) require.NoError(t, err, "Error setting CheckGasToBurn") err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetCheckGasToBurn tx") // Get existing performed count - existingCnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + existingCnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) require.NoError(t, err, "Error calling consumer's counter") l.Info().Int64("Upkeep counter", existingCnt.Int64()).Msg("Check Gas Increased") @@ -407,7 +407,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // gets committed later. Since every keeper node cannot have more than 1 straggling tx, it // is sufficient to check that the upkeep count does not increase by more than 6. gom.Consistently(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(cnt.Int64()).Should( gomega.BeNumerically("<=", existingCnt.Int64()+numUpkeepsAllowedForStragglingTxs), @@ -415,7 +415,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { ) }, "3m", "1s").Should(gomega.Succeed()) - existingCnt, err = consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + existingCnt, err = consumerPerformance.GetUpkeepCount(testcontext.Get(t)) require.NoError(t, err, "Error calling consumer's counter") existingCntInt := existingCnt.Int64() l.Info().Int64("Upkeep counter", existingCntInt).Msg("Upkeep counter when consistently block finished") @@ -430,7 +430,7 @@ func TestKeeperCheckPerformGasLimit(t *testing.T) { // Upkeep should start performing again, and it should get regularly performed gom.Eventually(func(g gomega.Gomega) { - cnt, err := consumerPerformance.GetUpkeepCount(utils.TestContext(t)) + cnt, err := consumerPerformance.GetUpkeepCount(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's Counter shouldn't fail") g.Expect(cnt.Int64()).Should(gomega.BeNumerically(">", existingCntInt), "Expected consumer counter to be greater than %d, but got %d", existingCntInt, cnt.Int64(), @@ -478,7 +478,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { // store the value of their initial counters in order to compare later on that the value increased. gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter"+ " for upkeep at index "+strconv.Itoa(i)) @@ -500,7 +500,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { // Test that the newly registered upkeep is also performing. gom.Eventually(func(g gomega.Gomega) error { - counter, err := newUpkeep.Counter(utils.TestContext(t)) + counter, err := newUpkeep.Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling newly deployed upkeep's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -510,7 +510,7 @@ func TestKeeperRegisterUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(utils.TestContext(t)) + currentCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") l.Info(). @@ -563,7 +563,7 @@ func TestKeeperAddFunds(t *testing.T) { // Since the upkeep is currently underfunded, check that it doesn't get executed gom.Consistently(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(utils.TestContext(t)) + counter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), "Expected consumer counter to remain zero, but got %d", counter.Int64()) @@ -583,7 +583,7 @@ func TestKeeperAddFunds(t *testing.T) { // Now the new upkeep should be performing because we added enough funds gom.Eventually(func(g gomega.Gomega) { - counter, err := consumers[0].Counter(utils.TestContext(t)) + counter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected newly registered upkeep's counter to be greater than 0, but got %d", counter.Int64()) @@ -628,7 +628,7 @@ func TestKeeperRemove(t *testing.T) { // Make sure the upkeeps are running before we remove a keeper gom.Eventually(func(g gomega.Gomega) error { for upkeepID := 0; upkeepID < len(upkeepIDs); upkeepID++ { - counter, err := consumers[upkeepID].Counter(utils.TestContext(t)) + counter, err := consumers[upkeepID].Counter(testcontext.Get(t)) initialCounters[upkeepID] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter"+ " for upkeep with ID "+strconv.Itoa(upkeepID)) @@ -637,7 +637,7 @@ func TestKeeperRemove(t *testing.T) { return nil }, "1m", "1s").Should(gomega.Succeed()) - keepers, err := registry.GetKeeperList(utils.TestContext(t)) + keepers, err := registry.GetKeeperList(testcontext.Get(t)) require.NoError(t, err, "Error getting list of Keepers") // Remove the first keeper from the list @@ -660,7 +660,7 @@ func TestKeeperRemove(t *testing.T) { // The upkeeps should still perform and their counters should have increased compared to the first check gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Cmp(initialCounters[i]) == 1, "Expected consumer counter to be greater "+ "than initial counter which was %s, but got %s", initialCounters[i], counter) @@ -705,7 +705,7 @@ func TestKeeperPauseRegistry(t *testing.T) { // Observe that the upkeeps which are initially registered are performing gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d") @@ -722,7 +722,7 @@ func TestKeeperPauseRegistry(t *testing.T) { // Store how many times each upkeep performed once the registry was successfully paused var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterPause[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Error retrieving consumer at index %d", i) } @@ -730,7 +730,7 @@ func TestKeeperPauseRegistry(t *testing.T) { // because they are no longer getting serviced gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Error retrieving consumer contract at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.Equal(countersAfterPause[i].Int64()), "Expected consumer counter to remain constant at %d, but got %d", @@ -791,7 +791,7 @@ func TestKeeperMigrateRegistry(t *testing.T) { // Check that the first upkeep from the first registry is performing (before being migrated) gom.Eventually(func(g gomega.Gomega) error { - counterBeforeMigration, err := consumers[0].Counter(utils.TestContext(t)) + counterBeforeMigration, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(counterBeforeMigration.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %s", counterBeforeMigration) @@ -810,12 +810,12 @@ func TestKeeperMigrateRegistry(t *testing.T) { err = chainClient.WaitForEvents() require.NoError(t, err, "Error waiting to pause first registry") - counterAfterMigration, err := consumers[0].Counter(utils.TestContext(t)) + counterAfterMigration, err := consumers[0].Counter(testcontext.Get(t)) require.NoError(t, err, "Error calling consumer's counter") // Check that once we migrated the upkeep, the counter has increased gom.Eventually(func(g gomega.Gomega) error { - currentCounter, err := consumers[0].Counter(utils.TestContext(t)) + currentCounter, err := consumers[0].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Calling consumer's counter shouldn't fail") g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", counterAfterMigration.Int64()), "Expected counter to have increased, but stayed constant at %s", counterAfterMigration) @@ -860,7 +860,7 @@ func TestKeeperNodeDown(t *testing.T) { // Watch upkeeps being performed and store their counters in order to compare them later in the test gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) initialCounters[i] = counter g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), @@ -882,7 +882,7 @@ func TestKeeperNodeDown(t *testing.T) { // Assert that upkeeps are still performed and their counters have increased gom.Eventually(func(g gomega.Gomega) error { for i := 0; i < len(upkeepIDs); i++ { - currentCounter, err := consumers[i].Counter(utils.TestContext(t)) + currentCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", initialCounters[i].Int64()), "Expected counter to have increased from initial value of %s, but got %s", @@ -908,7 +908,7 @@ func TestKeeperNodeDown(t *testing.T) { // See how many times each upkeep was executed var countersAfterNoMoreNodes = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { - countersAfterNoMoreNodes[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterNoMoreNodes[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Error retrieving consumer counter %d", i) l.Info(). Int("Index", i). @@ -921,7 +921,7 @@ func TestKeeperNodeDown(t *testing.T) { // so a +6 on the upper limit side should be sufficient. gom.Consistently(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterNoMoreNodes[i].Int64()+numUpkeepsAllowedForStragglingTxs, @@ -964,7 +964,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)), "Expected consumer counter to be greater than 5, but got %d", counter.Int64()) @@ -985,7 +985,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { var countersAfterPause = make([]*big.Int, len(upkeepIDs)) for i := 0; i < len(upkeepIDs); i++ { // Obtain the amount of times the upkeep has been executed so far - countersAfterPause[i], err = consumers[i].Counter(utils.TestContext(t)) + countersAfterPause[i], err = consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Error retrieving upkeep count at index %d", i) l.Info(). Int("Index", i). @@ -998,7 +998,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { // In most cases counters should remain constant, but there might be a straggling perform tx which // gets committed later. Since every keeper node cannot have more than 1 straggling tx, it // is sufficient to check that the upkeep count does not increase by more than 6. - latestCounter, err := consumers[i].Counter(utils.TestContext(t)) + latestCounter, err := consumers[i].Counter(testcontext.Get(t)) require.NoError(t, err, "Error retrieving counter at index %d", i) g.Expect(latestCounter.Int64()).Should(gomega.BeNumerically("<=", countersAfterPause[i].Int64()+numUpkeepsAllowedForStragglingTxs), "Expected consumer counter not have increased more than %d, but got %d", @@ -1018,7 +1018,7 @@ func TestKeeperPauseUnPauseUpkeep(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 + numbers of performing before pause for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter"+ " for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)+countersAfterPause[i].Int64()), @@ -1055,7 +1055,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { gom.Consistently(func(g gomega.Gomega) { // expect the counter to remain 0 because perform data does not match for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(utils.TestContext(t)) + counter, err := performDataChecker[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.Equal(int64(0)), "Expected perform data checker counter to be 0, but got %d", counter.Int64()) @@ -1073,7 +1073,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { // retrieve new check data for all upkeeps for i := 0; i < len(upkeepIDs); i++ { - upkeep, err := registry.GetUpkeepInfo(utils.TestContext(t), upkeepIDs[i]) + upkeep, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepIDs[i]) require.NoError(t, err, "Error getting upkeep info from index %d", i) require.Equal(t, []byte(keeperExpectedData), upkeep.CheckData, "Check data not as expected") } @@ -1081,7 +1081,7 @@ func TestKeeperUpdateCheckData(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analysing their counters and checking they are greater than 5 for i := 0; i < len(upkeepIDs); i++ { - counter, err := performDataChecker[i].Counter(utils.TestContext(t)) + counter, err := performDataChecker[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve perform data checker counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(5)), "Expected perform data checker counter to be greater than 5, but got %d", counter.Int64()) @@ -1154,7 +1154,7 @@ func TestKeeperJobReplacement(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) @@ -1183,7 +1183,7 @@ func TestKeeperJobReplacement(t *testing.T) { gom.Eventually(func(g gomega.Gomega) error { // Check if the upkeeps are performing multiple times by analyzing their counters and checking they are greater than 10 for i := 0; i < len(upkeepIDs); i++ { - counter, err := consumers[i].Counter(utils.TestContext(t)) + counter, err := consumers[i].Counter(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(10)), "Expected consumer counter to be greater than 10, but got %d", counter.Int64()) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index bec82e633f8..0676ed03004 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // Tests a basic OCRv2 median feed @@ -72,7 +72,7 @@ func TestOCRv2Basic(t *testing.T) { err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(1)) + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", @@ -84,7 +84,7 @@ func TestOCRv2Basic(t *testing.T) { err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) - roundData, err = aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(2)) + roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", @@ -146,7 +146,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") - roundData, err := aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(1)) + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", @@ -158,7 +158,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) - roundData, err = aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(2)) + roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", @@ -176,7 +176,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.StartNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) require.NoError(t, err, "Error starting new OCR2 round") - roundData, err = aggregatorContracts[0].GetRound(utils.TestContext(t), big.NewInt(3)) + roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(15), roundData.Answer.Int64(), "Expected latest answer from OCR contract to be 15 but got %d", diff --git a/integration-tests/smoke/ocr2vrf_test.go b/integration-tests/smoke/ocr2vrf_test.go index 57bd5412b14..773476826c7 100644 --- a/integration-tests/smoke/ocr2vrf_test.go +++ b/integration-tests/smoke/ocr2vrf_test.go @@ -15,6 +15,7 @@ import ( eth "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/ocr2vrf_actions" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCR2VRFRedeemModel(t *testing.T) { @@ -80,7 +80,7 @@ func TestOCR2VRFRedeemModel(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(testcontext.Get(t), requestID, big.NewInt(int64(i))) require.NoError(t, err) l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness retrieved from Consumer contract give an answer other than 0") @@ -141,7 +141,7 @@ func TestOCR2VRFFulfillmentModel(t *testing.T) { ) for i := uint16(0); i < ocr2vrf_constants.NumberOfRandomWordsToRequest; i++ { - randomness, err := consumerContract.GetRandomnessByRequestId(it_utils.TestContext(t), requestID, big.NewInt(int64(i))) + randomness, err := consumerContract.GetRandomnessByRequestId(testcontext.Get(t), requestID, big.NewInt(int64(i))) require.NoError(t, err, "Error getting Randomness result from Consumer Contract") l.Info().Interface("Random Number", randomness).Interface("Randomness Number Index", i).Msg("Randomness Fulfillment retrieved from Consumer contract") require.NotEqual(t, 0, randomness.Uint64(), "Randomness Fulfillment retrieved from Consumer contract give an answer other than 0") @@ -161,7 +161,7 @@ func setupOCR2VRFEnvironment(t *testing.T) (testEnvironment *environment.Environ cd := chainlink.New(0, map[string]interface{}{ "replicas": 6, - "toml": client.AddNetworkDetailedConfig( + "toml": networks.AddNetworkDetailedConfig( config.BaseOCR2Config, config.DefaultOCR2VRFNetworkDetailTomlConfig, testNetwork, diff --git a/integration-tests/smoke/ocr_test.go b/integration-tests/smoke/ocr_test.go index a078b8ca419..9ed692700ad 100644 --- a/integration-tests/smoke/ocr_test.go +++ b/integration-tests/smoke/ocr_test.go @@ -7,10 +7,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" - + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestOCRBasic(t *testing.T) { @@ -46,7 +45,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -55,7 +54,7 @@ func TestOCRBasic(t *testing.T) { err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) } @@ -93,7 +92,7 @@ func TestOCRJobReplacement(t *testing.T) { err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err := ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(5), answer.Int64(), "Expected latest answer from OCR contract to be 5 but got %d", answer.Int64()) @@ -102,7 +101,7 @@ func TestOCRJobReplacement(t *testing.T) { err = actions.StartNewRound(2, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Error getting latest OCR answer") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) @@ -119,7 +118,7 @@ func TestOCRJobReplacement(t *testing.T) { err = actions.StartNewRound(1, ocrInstances, env.EVMClient, l) require.NoError(t, err) - answer, err = ocrInstances[0].GetLatestAnswer(utils.TestContext(t)) + answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") require.Equal(t, int64(10), answer.Int64(), "Expected latest answer from OCR contract to be 10 but got %d", answer.Int64()) diff --git a/integration-tests/smoke/runlog_test.go b/integration-tests/smoke/runlog_test.go index 20389da378f..e2cd28b3320 100644 --- a/integration-tests/smoke/runlog_test.go +++ b/integration-tests/smoke/runlog_test.go @@ -12,10 +12,10 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestRunLogBasic(t *testing.T) { @@ -87,7 +87,7 @@ func TestRunLogBasic(t *testing.T) { gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { - d, err := consumer.Data(utils.TestContext(t)) + d, err := consumer.Data(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting data from consumer contract shouldn't fail") g.Expect(d).ShouldNot(gomega.BeNil(), "Expected the initial on chain data to be nil") l.Debug().Int64("Data", d.Int64()).Msg("Found on chain") diff --git a/integration-tests/smoke/vrf_test.go b/integration-tests/smoke/vrf_test.go index e477d459264..9c24d97b13b 100644 --- a/integration-tests/smoke/vrf_test.go +++ b/integration-tests/smoke/vrf_test.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv1" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFBasic(t *testing.T) { @@ -81,7 +81,7 @@ func TestVRFBasic(t *testing.T) { encodedProvingKeys := make([][2]*big.Int, 0) encodedProvingKeys = append(encodedProvingKeys, provingKey) - requestHash, err := contracts.Coordinator.HashOfKey(utils.TestContext(t), encodedProvingKeys[0]) + requestHash, err := contracts.Coordinator.HashOfKey(testcontext.Get(t), encodedProvingKeys[0]) require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") err = contracts.Consumer.RequestRandomness(requestHash, big.NewInt(1)) require.NoError(t, err, "Requesting randomness shouldn't fail") @@ -92,7 +92,7 @@ func TestVRFBasic(t *testing.T) { jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - out, err := contracts.Consumer.RandomnessOutput(utils.TestContext(t)) + out, err := contracts.Consumer.RandomnessOutput(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") // Checks that the job has actually run g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), @@ -170,7 +170,7 @@ func TestVRFJobReplacement(t *testing.T) { encodedProvingKeys := make([][2]*big.Int, 0) encodedProvingKeys = append(encodedProvingKeys, provingKey) - requestHash, err := contracts.Coordinator.HashOfKey(utils.TestContext(t), encodedProvingKeys[0]) + requestHash, err := contracts.Coordinator.HashOfKey(testcontext.Get(t), encodedProvingKeys[0]) require.NoError(t, err, "Getting Hash of encoded proving keys shouldn't fail") err = contracts.Consumer.RequestRandomness(requestHash, big.NewInt(1)) require.NoError(t, err, "Requesting randomness shouldn't fail") @@ -181,7 +181,7 @@ func TestVRFJobReplacement(t *testing.T) { jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - out, err := contracts.Consumer.RandomnessOutput(utils.TestContext(t)) + out, err := contracts.Consumer.RandomnessOutput(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") // Checks that the job has actually run g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), @@ -208,7 +208,7 @@ func TestVRFJobReplacement(t *testing.T) { jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Job execution shouldn't fail") - out, err := contracts.Consumer.RandomnessOutput(utils.TestContext(t)) + out, err := contracts.Consumer.RandomnessOutput(testcontext.Get(t)) g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Getting the randomness output of the consumer shouldn't fail") // Checks that the job has actually run g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically(">=", 1), diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 714ed752a36..09024b28ba7 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -9,13 +9,13 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFv2Basic(t *testing.T) { @@ -97,11 +97,11 @@ func TestVRFv2Basic(t *testing.T) { jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfV2jobs[0].Job.Data.ID) g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically("==", 1)) - lastRequestID, err = vrfv2Contracts.LoadTestConsumer.GetLastRequestId(utils.TestContext(t)) + lastRequestID, err = vrfv2Contracts.LoadTestConsumer.GetLastRequestId(testcontext.Get(t)) l.Debug().Interface("Last Request ID", lastRequestID).Msg("Last Request ID Received") g.Expect(err).ShouldNot(gomega.HaveOccurred()) - status, err := vrfv2Contracts.LoadTestConsumer.GetRequestStatus(utils.TestContext(t), lastRequestID) + status, err := vrfv2Contracts.LoadTestConsumer.GetRequestStatus(testcontext.Get(t), lastRequestID) g.Expect(err).ShouldNot(gomega.HaveOccurred()) g.Expect(status.Fulfilled).Should(gomega.BeTrue()) l.Debug().Interface("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index cfeca0a66a3..b171ea65f99 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -6,19 +6,19 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/integration-tests/utils" - "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2plus/vrfv2plus_config" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func TestVRFv2Plus(t *testing.T) { @@ -54,7 +54,7 @@ func TestVRFv2Plus(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) @@ -82,7 +82,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := subscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) @@ -91,7 +91,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -124,7 +124,7 @@ func TestVRFv2Plus(t *testing.T) { ) require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceWei := new(big.Int).Sub(subNativeTokenBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + subscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err) subBalanceAfterRequest := subscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) @@ -133,7 +133,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error reading job runs") require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) - status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) + status, err := vrfv2PlusContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, status.Fulfilled) l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") @@ -161,10 +161,10 @@ func TestVRFv2Plus(t *testing.T) { testConfig := vrfv2PlusConfig var isNativeBilling = false - wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(utils.TestContext(t), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceBeforeRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) + wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.Balance @@ -181,18 +181,18 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) + wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.Balance require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) expectedWrapperConsumerJuelsBalance := new(big.Int).Sub(wrapperConsumerJuelsBalanceBeforeRequest, consumerStatus.Paid) - wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(utils.TestContext(t), wrapperContracts.LoadTestConsumers[0].Address()) + wrapperConsumerJuelsBalanceAfterRequest, err := linkToken.BalanceOf(testcontext.Get(t), wrapperContracts.LoadTestConsumers[0].Address()) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerJuelsBalance, wrapperConsumerJuelsBalanceAfterRequest) @@ -210,10 +210,10 @@ func TestVRFv2Plus(t *testing.T) { testConfig := vrfv2PlusConfig var isNativeBilling = true - wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + wrapperConsumerBalanceBeforeRequestWei, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") - wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) + wrapperSubscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceBeforeRequest := wrapperSubscription.NativeBalance @@ -230,18 +230,18 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err, "error requesting randomness and waiting for fulfilment") expectedSubBalanceWei := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) - wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), wrapperSubID) + wrapperSubscription, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), wrapperSubID) require.NoError(t, err, "error getting subscription information") subBalanceAfterRequest := wrapperSubscription.NativeBalance require.Equal(t, expectedSubBalanceWei, subBalanceAfterRequest) - consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(utils.TestContext(t), randomWordsFulfilledEvent.RequestId) + consumerStatus, err := wrapperContracts.LoadTestConsumers[0].GetRequestStatus(testcontext.Get(t), randomWordsFulfilledEvent.RequestId) require.NoError(t, err, "error getting rand request status") require.True(t, consumerStatus.Fulfilled) expectedWrapperConsumerWeiBalance := new(big.Int).Sub(wrapperConsumerBalanceBeforeRequestWei, consumerStatus.Paid) - wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) + wrapperConsumerBalanceAfterRequestWei, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(wrapperContracts.LoadTestConsumers[0].Address())) require.NoError(t, err, "error getting wrapper consumer balance") require.Equal(t, expectedWrapperConsumerWeiBalance, wrapperConsumerBalanceAfterRequestWei) @@ -272,13 +272,13 @@ func TestVRFv2Plus(t *testing.T) { testWalletAddress, err := actions.GenerateWallet() require.NoError(t, err) - testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), testWalletAddress) + testWalletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), testWalletAddress) require.NoError(t, err) - testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), testWalletAddress.String()) + testWalletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) - subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) + subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -317,14 +317,14 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), testWalletAddress) + testWalletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), testWalletAddress) require.NoError(t, err) - testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), testWalletAddress.String()) + testWalletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), testWalletAddress.String()) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") subFundsReturnedNativeActual := new(big.Int).Sub(testWalletBalanceNativeAfterSubCancelling, testWalletBalanceNativeBeforeSubCancelling) @@ -366,17 +366,17 @@ func TestVRFv2Plus(t *testing.T) { subIDForCancelling := subIDsForCancelling[0] - subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) + subscriptionForCancelling, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetails(l, subscriptionForCancelling, subIDForCancelling, vrfv2PlusContracts.Coordinator) - activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsBeforeSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err) - require.True(t, utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subIDForCancelling)) + require.True(t, it_utils.BigIntSliceContains(activeSubscriptionIdsBeforeSubCancellation, subIDForCancelling)) - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(utils.TestContext(t), subIDForCancelling) + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") @@ -408,17 +408,17 @@ func TestVRFv2Plus(t *testing.T) { require.Error(t, err, "error should occur for waiting for fulfilment due to low sub balance") - pendingRequestsExist, err = vrfv2PlusContracts.Coordinator.PendingRequestsExist(utils.TestContext(t), subIDForCancelling) + pendingRequestsExist, err = vrfv2PlusContracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subIDForCancelling) require.NoError(t, err) require.True(t, pendingRequestsExist, "Pending requests should exist after unfulfilled rand requests due to low sub balance") - walletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) + walletBalanceNativeBeforeSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) + walletBalanceLinkBeforeSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) - subscriptionForCancelling, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) + subscriptionForCancelling, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) require.NoError(t, err, "error getting subscription information") subBalanceLink := subscriptionForCancelling.Balance @@ -457,14 +457,14 @@ func TestVRFv2Plus(t *testing.T) { require.Equal(t, subBalanceNative, subscriptionCanceledEvent.AmountNative, "SubscriptionCanceled event native amount is not equal to sub amount while canceling subscription") require.Equal(t, subBalanceLink, subscriptionCanceledEvent.AmountLink, "SubscriptionCanceled event LINK amount is not equal to sub amount while canceling subscription") - walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) + walletBalanceNativeAfterSubCancelling, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) + walletBalanceLinkAfterSubCancelling, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) //Verify that sub was deleted from Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subIDForCancelling) + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subIDForCancelling) fmt.Println("err", err) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") @@ -493,12 +493,12 @@ func TestVRFv2Plus(t *testing.T) { //require.Equal(t, subFundsReturnedNativeExpected, subFundsReturnedNativeActual, "Returned funds are not equal to sub balance that was cancelled") require.Equal(t, 0, subBalanceLink.Cmp(subFundsReturnedLinkActual), "Returned LINK funds are not equal to sub balance that was cancelled") - activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) + activeSubscriptionIdsAfterSubCancellation, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error getting active subscription ids") require.False( t, - utils.BigIntSliceContains(activeSubscriptionIdsAfterSubCancellation, subIDForCancelling), + it_utils.BigIntSliceContains(activeSubscriptionIdsAfterSubCancellation, subIDForCancelling), "Active subscription ids should not contain sub id after sub cancellation", ) }) @@ -542,10 +542,10 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) amountToWithdrawLink := fulfilledEventLink.Payment - defaultWalletBalanceNativeBeforeOracleWithdraw, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeBeforeOracleWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) + defaultWalletBalanceLinkBeforeOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) l.Info(). @@ -574,10 +574,10 @@ func TestVRFv2Plus(t *testing.T) { err = env.EVMClient.WaitForEvents() require.NoError(t, err, vrfv2plus.ErrWaitTXsComplete) - defaultWalletBalanceNativeAfterOracleWithdraw, err := env.EVMClient.BalanceAt(utils.TestContext(t), common.HexToAddress(defaultWalletAddress)) + defaultWalletBalanceNativeAfterOracleWithdraw, err := env.EVMClient.BalanceAt(testcontext.Get(t), common.HexToAddress(defaultWalletAddress)) require.NoError(t, err) - defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(utils.TestContext(t), defaultWalletAddress) + defaultWalletBalanceLinkAfterOracleWithdraw, err := linkToken.BalanceOf(testcontext.Get(t), defaultWalletAddress) require.NoError(t, err) //not possible to verify exact amount of Native/LINK returned as defaultWallet is used in other tests in parallel which might affect the balance @@ -617,17 +617,17 @@ func TestVRFv2PlusMigration(t *testing.T) { subID := subIDs[0] - subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) - activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) + activeSubIdsOldCoordinatorBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error occurred getting active sub ids") require.Len(t, activeSubIdsOldCoordinatorBeforeMigration, 1, "Active Sub Ids length is not equal to 1") require.Equal(t, subID, activeSubIdsOldCoordinatorBeforeMigration[0]) - oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + oldSubscriptionBeforeMigration, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") //Migration Process @@ -698,14 +698,14 @@ func TestVRFv2PlusMigration(t *testing.T) { migratedCoordinatorLinkTotalBalanceAfterMigration, migratedCoordinatorEthTotalBalanceAfterMigration, err := vrfv2plus.GetUpgradedCoordinatorTotalBalance(newCoordinator) require.NoError(t, err) - migratedSubscription, err := newCoordinator.GetSubscription(utils.TestContext(t), subID) + migratedSubscription, err := newCoordinator.GetSubscription(testcontext.Get(t), subID) require.NoError(t, err, "error getting subscription information") vrfv2plus.LogSubDetailsAfterMigration(l, newCoordinator, subID, migratedSubscription) //Verify that Coordinators were updated in Consumers for _, consumer := range vrfv2PlusContracts.LoadTestConsumers { - coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(utils.TestContext(t)) + coordinatorAddressInConsumerAfterMigration, err := consumer.GetCoordinator(testcontext.Get(t)) require.NoError(t, err, "error getting Coordinator from Consumer contract") require.Equal(t, newCoordinator.Address(), coordinatorAddressInConsumerAfterMigration.String()) l.Debug(). @@ -721,13 +721,13 @@ func TestVRFv2PlusMigration(t *testing.T) { require.Equal(t, oldSubscriptionBeforeMigration.Consumers, migratedSubscription.Consumers) //Verify that old sub was deleted from old Coordinator - _, err = vrfv2PlusContracts.Coordinator.GetSubscription(utils.TestContext(t), subID) + _, err = vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) require.Error(t, err, "error not occurred when trying to get deleted subscription from old Coordinator after sub migration") - _, err = vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) + _, err = vrfv2PlusContracts.Coordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.Error(t, err, "error not occurred getting active sub ids. Should occur since it should revert when sub id array is empty") - activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(utils.TestContext(t), big.NewInt(0), big.NewInt(0)) + activeSubIdsMigratedCoordinator, err := newCoordinator.GetActiveSubscriptionIds(testcontext.Get(t), big.NewInt(0), big.NewInt(0)) require.NoError(t, err, "error occurred getting active sub ids") require.Len(t, activeSubIdsMigratedCoordinator, 1, "Active Sub Ids length is not equal to 1 for Migrated Coordinator after migration") require.Equal(t, subID, activeSubIdsMigratedCoordinator[0]) diff --git a/integration-tests/soak/forwarder_ocr_test.go b/integration-tests/soak/forwarder_ocr_test.go index bc02f367892..538f5ff1569 100644 --- a/integration-tests/soak/forwarder_ocr_test.go +++ b/integration-tests/soak/forwarder_ocr_test.go @@ -18,7 +18,7 @@ func TestForwarderOCRSoak(t *testing.T) { ForwardersEnabled = true` // Uncomment below for debugging TOML issues on the node // fmt.Println("Using Chainlink TOML\n---------------------") - // fmt.Println(client.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) + // fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) // fmt.Println("---------------------") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, true) diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index 9973c23808e..c7d4bc80f61 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -18,7 +18,7 @@ func TestOCRSoak(t *testing.T) { // Uncomment below for debugging TOML issues on the node // network := networks.MustGetSelectedNetworksFromEnv()[0] // fmt.Println("Using Chainlink TOML\n---------------------") - // fmt.Println(client.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) + // fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) // fmt.Println("---------------------") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, false) diff --git a/integration-tests/testsetups/keeper_benchmark.go b/integration-tests/testsetups/keeper_benchmark.go index d9b389a9650..575ed3e7de5 100644 --- a/integration-tests/testsetups/keeper_benchmark.go +++ b/integration-tests/testsetups/keeper_benchmark.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/logging" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper1_1" @@ -37,7 +38,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // KeeperBenchmarkTest builds a test to check that chainlink nodes are able to upkeep a specified amount of Upkeep @@ -230,7 +230,7 @@ func (k *KeeperBenchmarkTest) Run() { "NumberOfRegistries": len(k.keeperRegistries), } inputs := k.Inputs - startingBlock, err := k.chainClient.LatestBlockNumber(utils.TestContext(k.t)) + startingBlock, err := k.chainClient.LatestBlockNumber(testcontext.Get(k.t)) require.NoError(k.t, err, "Error getting latest block number") k.startingBlock = big.NewInt(0).SetUint64(startingBlock) startTime := time.Now() @@ -319,7 +319,7 @@ func (k *KeeperBenchmarkTest) Run() { // This RPC call can possibly time out or otherwise die. Failure is not an option, keep retrying to get our stats. err = fmt.Errorf("initial error") // to ensure our for loop runs at least once for err != nil { - ctx, cancel := context.WithTimeout(utils.TestContext(k.t), timeout) + ctx, cancel := context.WithTimeout(testcontext.Get(k.t), timeout) logs, err = k.chainClient.FilterLogs(ctx, filterQuery) cancel() if err != nil { @@ -430,7 +430,7 @@ func (k *KeeperBenchmarkTest) observeUpkeepEvents() { FromBlock: k.startingBlock, } - ctx, cancel := context.WithTimeout(utils.TestContext(k.t), 5*time.Second) + ctx, cancel := context.WithTimeout(testcontext.Get(k.t), 5*time.Second) sub, err := k.chainClient.SubscribeFilterLogs(ctx, filterQuery, eventLogs) cancel() require.NoError(k.t, err, "Subscribing to upkeep performed events log shouldn't fail") @@ -453,7 +453,7 @@ func (k *KeeperBenchmarkTest) observeUpkeepEvents() { Str("Backoff", backoff.String()). Msg("Error while subscribing to Keeper Event Logs. Resubscribing...") - ctx, cancel := context.WithTimeout(utils.TestContext(k.t), backoff) + ctx, cancel := context.WithTimeout(testcontext.Get(k.t), backoff) sub, err = k.chainClient.SubscribeFilterLogs(ctx, filterQuery, eventLogs) cancel() if err != nil { diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index 3fb9dd9844a..dfeb4bb916d 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -36,13 +36,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) const ( @@ -142,7 +142,7 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { cd := chainlink.New(0, map[string]any{ "replicas": 6, - "toml": client.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customChainlinkNetworkTOML, network), + "toml": networks.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customChainlinkNetworkTOML, network), "db": map[string]any{ "stateful": true, // stateful DB by default for soak tests }, @@ -258,7 +258,7 @@ func (o *OCRSoakTest) Setup() { // Run starts the OCR soak test func (o *OCRSoakTest) Run() { - ctx, cancel := context.WithTimeout(utils.TestContext(o.t), time.Second*5) + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), time.Second*5) latestBlockNum, err := o.chainClient.LatestBlockNumber(ctx) cancel() require.NoError(o.t, err, "Error getting current block number") @@ -559,7 +559,7 @@ func (o *OCRSoakTest) setFilterQuery() { // WARNING: Should only be used for observation and logging. This is not a reliable way to collect events. func (o *OCRSoakTest) observeOCREvents() error { eventLogs := make(chan types.Log) - ctx, cancel := context.WithTimeout(utils.TestContext(o.t), 5*time.Second) + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), 5*time.Second) eventSub, err := o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { @@ -593,7 +593,7 @@ func (o *OCRSoakTest) observeOCREvents() error { Str("Backoff", backoff.String()). Interface("Query", o.filterQuery). Msg("Error while subscribed to OCR Logs. Resubscribing") - ctx, cancel = context.WithTimeout(utils.TestContext(o.t), backoff) + ctx, cancel = context.WithTimeout(testcontext.Get(o.t), backoff) eventSub, err = o.chainClient.SubscribeFilterLogs(ctx, o.filterQuery, eventLogs) cancel() if err != nil { @@ -646,12 +646,12 @@ func (o *OCRSoakTest) collectEvents() error { timeout := time.Second * 15 o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") - ctx, cancel := context.WithTimeout(utils.TestContext(o.t), timeout) + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), timeout) contractEvents, err := o.chainClient.FilterLogs(ctx, o.filterQuery) cancel() for err != nil { o.log.Info().Interface("Filter Query", o.filterQuery).Str("Timeout", timeout.String()).Msg("Retrieving on-chain events") - ctx, cancel := context.WithTimeout(utils.TestContext(o.t), timeout) + ctx, cancel := context.WithTimeout(testcontext.Get(o.t), timeout) contractEvents, err = o.chainClient.FilterLogs(ctx, o.filterQuery) cancel() if err != nil { diff --git a/integration-tests/testsetups/vrfv2.go b/integration-tests/testsetups/vrfv2.go index 8c5fde72168..ef18bd267de 100644 --- a/integration-tests/testsetups/vrfv2.go +++ b/integration-tests/testsetups/vrfv2.go @@ -18,11 +18,11 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/logging" reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - "github.com/smartcontractkit/chainlink/integration-tests/utils" ) // VRFV2SoakTest defines a typical VRFV2 soak test @@ -88,7 +88,7 @@ func (v *VRFV2SoakTest) Run(t *testing.T) { Msg("Starting VRFV2 Soak Test") // set the requests to only run for a certain amount of time - ctx := utils.TestContext(t) + ctx := testcontext.Get(t) testContext, testCancel := context.WithTimeout(ctx, v.Inputs.TestDuration) defer testCancel() diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index e044d1ba32a..6da1d94371a 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -13,6 +13,7 @@ import ( relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config/toml" @@ -23,42 +24,42 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils/config" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" - utils2 "github.com/smartcontractkit/chainlink/integration-tests/utils" + it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) func NewBaseConfig() *chainlink.Config { return &chainlink.Config{ Core: toml.Core{ - RootDir: utils2.Ptr("/home/chainlink"), + RootDir: ptr.Ptr("/home/chainlink"), Database: toml.Database{ - MaxIdleConns: utils2.Ptr(int64(20)), - MaxOpenConns: utils2.Ptr(int64(40)), - MigrateOnStartup: utils2.Ptr(true), + MaxIdleConns: ptr.Ptr(int64(20)), + MaxOpenConns: ptr.Ptr(int64(40)), + MigrateOnStartup: ptr.Ptr(true), }, Log: toml.Log{ - Level: utils2.Ptr(toml.LogLevel(zapcore.DebugLevel)), - JSONConsole: utils2.Ptr(true), + Level: ptr.Ptr(toml.LogLevel(zapcore.DebugLevel)), + JSONConsole: ptr.Ptr(true), File: toml.LogFile{ - MaxSize: utils2.Ptr(utils.FileSize(0)), + MaxSize: ptr.Ptr(utils.FileSize(0)), }, }, WebServer: toml.WebServer{ - AllowOrigins: utils2.Ptr("*"), - HTTPPort: utils2.Ptr[uint16](6688), - SecureCookies: utils2.Ptr(false), + AllowOrigins: ptr.Ptr("*"), + HTTPPort: ptr.Ptr[uint16](6688), + SecureCookies: ptr.Ptr(false), SessionTimeout: models.MustNewDuration(time.Hour * 999), TLS: toml.WebServerTLS{ - HTTPSPort: utils2.Ptr[uint16](0), + HTTPSPort: ptr.Ptr[uint16](0), }, RateLimit: toml.WebServerRateLimit{ - Authenticated: utils2.Ptr(int64(2000)), - Unauthenticated: utils2.Ptr(int64(100)), + Authenticated: ptr.Ptr(int64(2000)), + Unauthenticated: ptr.Ptr(int64(100)), }, }, Feature: toml.Feature{ - LogPoller: utils2.Ptr(true), - FeedsManager: utils2.Ptr(true), - UICSAKeys: utils2.Ptr(true), + LogPoller: ptr.Ptr(true), + FeedsManager: ptr.Ptr(true), + UICSAKeys: ptr.Ptr(true), }, P2P: toml.P2P{}, }, @@ -96,7 +97,7 @@ func NewConfigFromToml(tomlFile string, opts ...NodeConfigOpt) (*chainlink.Confi func WithOCR1() NodeConfigOpt { return func(c *chainlink.Config) { c.OCR = toml.OCR{ - Enabled: utils2.Ptr(true), + Enabled: ptr.Ptr(true), } } } @@ -104,7 +105,7 @@ func WithOCR1() NodeConfigOpt { func WithOCR2() NodeConfigOpt { return func(c *chainlink.Config) { c.OCR2 = toml.OCR2{ - Enabled: utils2.Ptr(true), + Enabled: ptr.Ptr(true), } } } @@ -114,12 +115,12 @@ func WithOCR2() NodeConfigOpt { func WithP2Pv1() NodeConfigOpt { return func(c *chainlink.Config) { c.P2P.V1 = toml.P2PV1{ - Enabled: utils2.Ptr(true), - ListenIP: utils2.MustIP("0.0.0.0"), - ListenPort: utils2.Ptr[uint16](6690), + Enabled: ptr.Ptr(true), + ListenIP: it_utils.MustIP("0.0.0.0"), + ListenPort: ptr.Ptr[uint16](6690), } // disabled default - c.P2P.V2 = toml.P2PV2{Enabled: utils2.Ptr(false)} + c.P2P.V2 = toml.P2PV2{Enabled: ptr.Ptr(false)} } } @@ -134,14 +135,14 @@ func WithP2Pv2() NodeConfigOpt { func WithTracing() NodeConfigOpt { return func(c *chainlink.Config) { c.Tracing = toml.Tracing{ - Enabled: utils2.Ptr(true), - CollectorTarget: utils2.Ptr("otel-collector:4317"), + Enabled: ptr.Ptr(true), + CollectorTarget: ptr.Ptr("otel-collector:4317"), // ksortable unique id - NodeID: utils2.Ptr(ksuid.New().String()), + NodeID: ptr.Ptr(ksuid.New().String()), Attributes: map[string]string{ "env": "smoke", }, - SamplingRatio: utils2.Ptr(1.0), + SamplingRatio: ptr.Ptr(1.0), } } } @@ -157,10 +158,10 @@ func SetChainConfig( var nodes []*evmcfg.Node for i := range wsUrls { node := evmcfg.Node{ - Name: utils2.Ptr(fmt.Sprintf("node_%d_%s", i, chain.Name)), - WSURL: utils2.MustURL(wsUrls[i]), - HTTPURL: utils2.MustURL(httpUrls[i]), - SendOnly: utils2.Ptr(false), + Name: ptr.Ptr(fmt.Sprintf("node_%d_%s", i, chain.Name)), + WSURL: it_utils.MustURL(wsUrls[i]), + HTTPURL: it_utils.MustURL(httpUrls[i]), + SendOnly: ptr.Ptr(false), } nodes = append(nodes, &node) @@ -168,8 +169,8 @@ func SetChainConfig( var chainConfig evmcfg.Chain if chain.Simulated { chainConfig = evmcfg.Chain{ - AutoCreateKey: utils2.Ptr(true), - FinalityDepth: utils2.Ptr[uint32](1), + AutoCreateKey: ptr.Ptr(true), + FinalityDepth: ptr.Ptr[uint32](1), MinContractPayment: relayassets.NewLinkFromJuels(0), } } @@ -182,7 +183,7 @@ func SetChainConfig( } if forwarders { cfg.EVM[0].Transactions = evmcfg.Transactions{ - ForwardersEnabled: utils2.Ptr(true), + ForwardersEnabled: ptr.Ptr(true), } } } @@ -194,25 +195,25 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { evmConfigs = append(evmConfigs, &evmcfg.EVMConfig{ ChainID: utils.NewBig(big.NewInt(network.ChainID)), Chain: evmcfg.Chain{ - AutoCreateKey: utils2.Ptr(true), - FinalityDepth: utils2.Ptr[uint32](50), + AutoCreateKey: ptr.Ptr(true), + FinalityDepth: ptr.Ptr[uint32](50), MinContractPayment: relayassets.NewLinkFromJuels(0), LogPollInterval: models.MustNewDuration(1 * time.Second), HeadTracker: evmcfg.HeadTracker{ - HistoryDepth: utils2.Ptr(uint32(100)), + HistoryDepth: ptr.Ptr(uint32(100)), }, GasEstimator: evmcfg.GasEstimator{ - LimitDefault: utils2.Ptr(uint32(6000000)), + LimitDefault: ptr.Ptr(uint32(6000000)), PriceMax: assets.GWei(200), FeeCapDefault: assets.GWei(200), }, }, Nodes: []*evmcfg.Node{ { - Name: utils2.Ptr(network.Name), - WSURL: utils2.MustURL(network.URLs[0]), - HTTPURL: utils2.MustURL(network.HTTPURLs[0]), - SendOnly: utils2.Ptr(false), + Name: ptr.Ptr(network.Name), + WSURL: it_utils.MustURL(network.URLs[0]), + HTTPURL: it_utils.MustURL(network.HTTPURLs[0]), + SendOnly: ptr.Ptr(false), }, }, }) @@ -227,17 +228,17 @@ func WithVRFv2EVMEstimator(addr string) NodeConfigOpt { return func(c *chainlink.Config) { c.EVM[0].KeySpecific = evmcfg.KeySpecificConfig{ { - Key: utils2.Ptr(ethkey.EIP55Address(addr)), + Key: ptr.Ptr(ethkey.EIP55Address(addr)), GasEstimator: evmcfg.KeySpecificGasEstimator{ PriceMax: est, }, }, } c.EVM[0].Chain.GasEstimator = evmcfg.GasEstimator{ - LimitDefault: utils2.Ptr[uint32](3500000), + LimitDefault: ptr.Ptr[uint32](3500000), } c.EVM[0].Chain.Transactions = evmcfg.Transactions{ - MaxQueued: utils2.Ptr[uint32](10000), + MaxQueued: ptr.Ptr[uint32](10000), } } } diff --git a/integration-tests/universal/log_poller/helpers.go b/integration-tests/universal/log_poller/helpers.go index 505337f0324..7bf1657316f 100644 --- a/integration-tests/universal/log_poller/helpers.go +++ b/integration-tests/universal/log_poller/helpers.go @@ -27,6 +27,7 @@ import ( ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -42,7 +43,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" - it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) var ( @@ -1010,12 +1010,12 @@ func setupLogPollerTestDocker( // build the node config clNodeConfig := node.NewConfig(node.NewBaseConfig()) syncInterval := models.MustMakeDuration(5 * time.Minute) - clNodeConfig.Feature.LogPoller = it_utils.Ptr[bool](true) - clNodeConfig.OCR2.Enabled = it_utils.Ptr[bool](true) - clNodeConfig.Keeper.TurnLookBack = it_utils.Ptr[int64](int64(0)) + clNodeConfig.Feature.LogPoller = ptr.Ptr[bool](true) + clNodeConfig.OCR2.Enabled = ptr.Ptr[bool](true) + clNodeConfig.Keeper.TurnLookBack = ptr.Ptr[int64](int64(0)) clNodeConfig.Keeper.Registry.SyncInterval = &syncInterval - clNodeConfig.Keeper.Registry.PerformGasOverhead = it_utils.Ptr[uint32](uint32(150000)) - clNodeConfig.P2P.V2.Enabled = it_utils.Ptr[bool](true) + clNodeConfig.Keeper.Registry.PerformGasOverhead = ptr.Ptr[uint32](uint32(150000)) + clNodeConfig.P2P.V2.Enabled = ptr.Ptr[bool](true) clNodeConfig.P2P.V2.AnnounceAddresses = &[]string{"0.0.0.0:6690"} clNodeConfig.P2P.V2.ListenAddresses = &[]string{"0.0.0.0:6690"} @@ -1027,8 +1027,8 @@ func setupLogPollerTestDocker( var logPolllerSettingsFn = func(chain *evmcfg.Chain) *evmcfg.Chain { chain.LogPollInterval = models.MustNewDuration(lpPollingInterval) - chain.FinalityDepth = it_utils.Ptr[uint32](uint32(finalityDepth)) - chain.FinalityTagEnabled = it_utils.Ptr[bool](finalityTagEnabled) + chain.FinalityDepth = ptr.Ptr[uint32](uint32(finalityDepth)) + chain.FinalityTagEnabled = ptr.Ptr[bool](finalityTagEnabled) return chain } diff --git a/integration-tests/universal/log_poller/scenarios.go b/integration-tests/universal/log_poller/scenarios.go index 886547d46e1..5331c63f752 100644 --- a/integration-tests/universal/log_poller/scenarios.go +++ b/integration-tests/universal/log_poller/scenarios.go @@ -10,10 +10,10 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" - "github.com/smartcontractkit/chainlink/integration-tests/utils" core_logger "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -100,7 +100,7 @@ func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) + sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -122,7 +122,7 @@ func ExecuteBasicLogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) + eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) @@ -229,7 +229,7 @@ func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string time.Sleep(5 * time.Second) // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) + sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -243,7 +243,7 @@ func ExecuteLogPollerReplay(t *testing.T, cfg *Config, consistencyTimeout string l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) + eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) @@ -419,7 +419,7 @@ func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Count", len(expectedFilters)).Msg("Expected filters count") // Save block number before starting to emit events, so that we can later use it when querying logs - sb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) + sb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") startBlock := int64(sb) @@ -441,7 +441,7 @@ func ExecuteCILogPollerTest(t *testing.T, cfg *Config) { l.Info().Int("Total logs emitted", totalLogsEmitted).Int64("Expected total logs emitted", expectedLogsEmitted).Str("Duration", fmt.Sprintf("%d sec", duration)).Str("LPS", fmt.Sprintf("%d/sec", totalLogsEmitted/duration)).Msg("FINISHED EVENT EMISSION") // Save block number after finishing to emit events, so that we can later use it when querying logs - eb, err := testEnv.EVMClient.LatestBlockNumber(utils.TestContext(t)) + eb, err := testEnv.EVMClient.LatestBlockNumber(testcontext.Get(t)) require.NoError(t, err, "Error getting latest block number") endBlock, err := GetEndBlockToWaitFor(int64(eb), testEnv.EVMClient.GetChainID().Int64(), cfg) diff --git a/integration-tests/utils/common.go b/integration-tests/utils/common.go index f13c71cfd91..34a40e396d2 100644 --- a/integration-tests/utils/common.go +++ b/integration-tests/utils/common.go @@ -1,16 +1,12 @@ package utils import ( - "context" "math/big" "net" - "testing" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) -func Ptr[T any](t T) *T { return &t } - func MustURL(s string) *models.URL { var u models.URL if err := u.UnmarshalText([]byte(s)); err != nil { @@ -35,24 +31,3 @@ func BigIntSliceContains(slice []*big.Int, b *big.Int) bool { } return false } - -// TestContext returns a context with the test's deadline, if available. -func TestContext(tb testing.TB) context.Context { - ctx := context.Background() - var cancel func() - switch t := tb.(type) { - case *testing.T: - // Return background context if testing.T not set - if t == nil { - return ctx - } - if d, ok := t.Deadline(); ok { - ctx, cancel = context.WithDeadline(ctx, d) - } - } - if cancel == nil { - ctx, cancel = context.WithCancel(ctx) - } - tb.Cleanup(cancel) - return ctx -} From 4eb170b51d67be85b950efa0aa8e5daad3bed00d Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Fri, 17 Nov 2023 02:54:02 +0300 Subject: [PATCH 167/271] Devspace update + Cluster dashboard (#11222) * fix chainlink config * init dashboard * bump CTF * move dashboard rows * dashboard * comments * exclude dashboard from SonarQube * common dashboard * extend opts * rg filter --- charts/chainlink-cluster/README.md | 21 +- .../dashboard/cmd/dashboard_deploy.go | 42 ++ .../chainlink-cluster/dashboard/dashboard.go | 392 ++++++++++++++++++ charts/chainlink-cluster/devspace.yaml | 2 + charts/chainlink-cluster/go.mod | 15 + charts/chainlink-cluster/go.sum | 20 + .../templates/chainlink-cm.yaml | 5 +- .../templates/chainlink-deployment.yaml | 2 + .../templates/chainlink-pod-monitor.yaml | 16 +- sonar-project.properties | 2 +- 10 files changed, 505 insertions(+), 12 deletions(-) create mode 100644 charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go create mode 100644 charts/chainlink-cluster/dashboard/dashboard.go create mode 100644 charts/chainlink-cluster/go.mod create mode 100644 charts/chainlink-cluster/go.sum diff --git a/charts/chainlink-cluster/README.md b/charts/chainlink-cluster/README.md index f7d4c45fa5f..46c337dc724 100644 --- a/charts/chainlink-cluster/README.md +++ b/charts/chainlink-cluster/README.md @@ -34,7 +34,7 @@ If you don't need a build use devspace deploy --skip-build ``` -Connect to your environment +Connect to your environment, by replacing container with label `node-1` with your local repository files ``` devspace dev -p node make chainlink @@ -117,4 +117,23 @@ helm test cl-cluster ## Uninstall ``` helm uninstall cl-cluster +``` + +# Grafana dashboard +We are using [Grabana]() lib to create dashboards programmatically +``` +export GRAFANA_URL=... +export GRAFANA_TOKEN=... +export LOKI_DATA_SOURCE_NAME=Loki +export PROMETHEUS_DATA_SOURCE_NAME=Thanos +export DASHBOARD_FOLDER=CLClusterEphemeralDevspace +export DASHBOARD_NAME=ChainlinkCluster + +cd dashboard/cmd && go run dashboard_deploy.go +``` +Open Grafana folder `CLClusterEphemeralDevspace` and find dashboard `ChainlinkCluster` + +If you'd like to add more metrics or verify that all of them are added you can have the full list using IDE search or `ripgrep`: +``` +rg -U ".*promauto.*\n.*Name: \"(.*)\"" ../.. > metrics.txt ``` \ No newline at end of file diff --git a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go new file mode 100644 index 00000000000..c752794f53f --- /dev/null +++ b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go @@ -0,0 +1,42 @@ +package main + +import ( + "os" + + "github.com/smartcontractkit/chainlink/v2/dashboard/dashboard" +) + +func main() { + name := os.Getenv("DASHBOARD_NAME") + if name == "" { + panic("DASHBOARD_NAME must be provided") + } + ldsn := os.Getenv("LOKI_DATA_SOURCE_NAME") + if ldsn == "" { + panic("DATA_SOURCE_NAME must be provided") + } + pdsn := os.Getenv("PROMETHEUS_DATA_SOURCE_NAME") + if ldsn == "" { + panic("DATA_SOURCE_NAME must be provided") + } + dbf := os.Getenv("DASHBOARD_FOLDER") + if dbf == "" { + panic("DASHBOARD_FOLDER must be provided") + } + grafanaURL := os.Getenv("GRAFANA_URL") + if grafanaURL == "" { + panic("GRAFANA_URL must be provided") + } + grafanaToken := os.Getenv("GRAFANA_TOKEN") + if grafanaToken == "" { + panic("GRAFANA_TOKEN must be provided") + } + // if you'll use this dashboard base in other projects, you can add your own opts here to extend it + db, err := dashboard.NewCLClusterDashboard(name, ldsn, pdsn, dbf, grafanaURL, grafanaToken, nil) + if err != nil { + panic(err) + } + if err := db.Deploy(); err != nil { + panic(err) + } +} diff --git a/charts/chainlink-cluster/dashboard/dashboard.go b/charts/chainlink-cluster/dashboard/dashboard.go new file mode 100644 index 00000000000..7918b996dd0 --- /dev/null +++ b/charts/chainlink-cluster/dashboard/dashboard.go @@ -0,0 +1,392 @@ +package dashboard + +import ( + "context" + "fmt" + "net/http" + + "github.com/K-Phoen/grabana" + "github.com/K-Phoen/grabana/dashboard" + "github.com/K-Phoen/grabana/logs" + "github.com/K-Phoen/grabana/row" + "github.com/K-Phoen/grabana/stat" + "github.com/K-Phoen/grabana/target/prometheus" + "github.com/K-Phoen/grabana/timeseries" + "github.com/K-Phoen/grabana/timeseries/axis" + "github.com/K-Phoen/grabana/variable/interval" + "github.com/K-Phoen/grabana/variable/query" + "github.com/pkg/errors" +) + +const ( + ErrFailedToCreateDashboard = "failed to create dashboard" + ErrFailedToCreateFolder = "failed to create folder" +) + +// CLClusterDashboard is a dashboard for a Chainlink cluster +type CLClusterDashboard struct { + Name string + LokiDataSourceName string + PrometheusDataSourceName string + Folder string + GrafanaURL string + GrafanaToken string + opts []dashboard.Option + extendedOpts []dashboard.Option + builder dashboard.Builder +} + +// NewCLClusterDashboard returns a new dashboard for a Chainlink cluster, can be used as a base for more complex plugin based dashboards +func NewCLClusterDashboard(name, ldsn, pdsn, dbf, grafanaURL, grafanaToken string, opts []dashboard.Option) (*CLClusterDashboard, error) { + db := &CLClusterDashboard{ + Name: name, + Folder: dbf, + LokiDataSourceName: ldsn, + PrometheusDataSourceName: pdsn, + GrafanaURL: grafanaURL, + GrafanaToken: grafanaToken, + extendedOpts: opts, + } + if err := db.generate(); err != nil { + return db, err + } + return db, nil +} + +func (m *CLClusterDashboard) Opts() []dashboard.Option { + return m.opts +} + +// logsRowOption returns a row option for a node's logs with name and instance selector +func (m *CLClusterDashboard) logsRowOption(name, instanceSelector string) row.Option { + return row.WithLogs( + name, + logs.DataSource(m.LokiDataSourceName), + logs.Span(12), + logs.Height("300px"), + logs.Transparent(), + logs.WithLokiTarget(fmt.Sprintf(` + {namespace="${namespace}", app="app", instance="%s", container="node"} + `, instanceSelector)), + ) +} + +// timeseriesRowOption returns a row option for a timeseries with name, axis unit, query and legend template +func (m *CLClusterDashboard) timeseriesRowOption(name, axisUnit, query, legendTemplate string) row.Option { + var tsq timeseries.Option + if legendTemplate != "" { + tsq = timeseries.WithPrometheusTarget( + query, + prometheus.Legend(legendTemplate), + ) + } else { + tsq = timeseries.WithPrometheusTarget(query) + } + var au timeseries.Option + if axisUnit != "" { + au = timeseries.Axis( + axis.Unit(axisUnit), + ) + } else { + au = timeseries.Axis() + } + return row.WithTimeSeries( + name, + timeseries.Span(6), + timeseries.Height("300px"), + timeseries.DataSource(m.PrometheusDataSourceName), + au, + tsq, + ) +} + +// statRowOption returns a row option for a stat with name, prometheus target and legend template +func (m *CLClusterDashboard) statRowOption(name, target, legend string) row.Option { + return row.WithStat( + name, + stat.Transparent(), + stat.DataSource(m.PrometheusDataSourceName), + stat.Text(stat.TextValueAndName), + stat.Orientation(stat.OrientationVertical), + stat.TitleFontSize(12), + stat.ValueFontSize(20), + stat.Span(12), + stat.Height("100px"), + stat.WithPrometheusTarget(target, prometheus.Legend(legend)), + ) +} + +// generate generates the dashboard, adding extendedOpts to the default options +func (m *CLClusterDashboard) generate() error { + opts := []dashboard.Option{ + dashboard.AutoRefresh("10s"), + dashboard.Tags([]string{"generated"}), + dashboard.VariableAsQuery( + "namespace", + query.DataSource(m.LokiDataSourceName), + query.Multiple(), + query.IncludeAll(), + query.Request(fmt.Sprintf("label_values(%s)", "namespace")), + query.Sort(query.NumericalAsc), + ), + dashboard.VariableAsInterval( + "interval", + interval.Values([]string{"30s", "1m", "5m", "10m", "30m", "1h", "6h", "12h"}), + ), + dashboard.Row( + "Cluster health", + m.statRowOption( + "App Version", + `version{namespace="${namespace}"}`, + "{{pod}} - {{version}}", + ), + row.WithTimeSeries( + "Restarts", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `sum(increase(kube_pod_container_status_restarts_total{namespace=~"${namespace}"}[5m])) by (pod)`, + prometheus.Legend("{{pod}}"), + ), + ), + row.WithTimeSeries( + "Service Components Health", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `health{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - {{service_id}}"), + ), + ), + row.WithTimeSeries( + "Log Counters", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `log_panic_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - panic"), + ), + timeseries.WithPrometheusTarget( + `log_fatal_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - fatal"), + ), + timeseries.WithPrometheusTarget( + `log_critical_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - critical"), + ), + timeseries.WithPrometheusTarget( + `log_error_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - error"), + ), + ), + row.WithTimeSeries( + "ETH Balance", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `eth_balance{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - {{account}}"), + ), + ), + ), + // logs + dashboard.Row( + "Logs", + row.Collapse(), + m.logsRowOption("Node 1", "node-1"), + m.logsRowOption("Node 2", "node-2"), + m.logsRowOption("Node 3", "node-3"), + m.logsRowOption("Node 4", "node-4"), + ), + // DON report metrics + dashboard.Row("DON Report metrics", + row.Collapse(), + m.timeseriesRowOption( + "Plugin Query() count", + "Count", + `sum(rate(ocr2_reporting_plugin_query_count{namespace="${namespace}", app="app"}[$__rate_interval])) by (service)`, + "", + ), + m.timeseriesRowOption( + "Plugin Observation() time (95th)", + "Sec", + `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_observation_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, + "", + ), + m.timeseriesRowOption( + "Plugin ShouldAcceptReport() time (95th)", + "Sec", + `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_should_accept_report_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, + "", + ), + m.timeseriesRowOption( + "Plugin Report() time (95th)", + "Sec", + `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_report_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, + "", + ), + m.timeseriesRowOption( + "Plugin ShouldTransmitReport() time (95th)", + "Sec", + `histogram_quantile(0.95, sum(rate(ocr2_reporting_plugin_should_transmit_report_time_bucket{namespace="${namespace}", app="app"}[$__rate_interval])) by (le, service)) / 1e9`, + "", + ), + ), + dashboard.Row( + "DB Connection Metrics (App)", + row.Collapse(), + m.timeseriesRowOption( + "DB Connections MAX", + "Conn", + `db_conns_max{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "DB Connections Open", + "Conn", + `db_conns_open{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "DB Connections Used", + "Conn", + `db_conns_used{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "DB Connections Wait", + "Conn", + `db_conns_wait{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "DB Wait time", + "Sec", + `db_wait_time_seconds{namespace="${namespace}"}`, + "{{pod}}", + ), + ), + dashboard.Row( + "EVM Pool RPC Node Metrics (App)", + row.Collapse(), + m.timeseriesRowOption( + "EVM Pool RPC Node Calls Success", + "", + `evm_pool_rpc_node_calls_success{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Calls Total", + "", + `evm_pool_rpc_node_calls_total{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Dials Success", + "", + `evm_pool_rpc_node_dials_success{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Dials Total", + "", + `evm_pool_rpc_node_dials_total{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Highest Seen Block", + "", + `evm_pool_rpc_node_highest_seen_block{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Total Transitions to Alive", + "", + `evm_pool_rpc_node_num_transitions_to_alive{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Polls Success", + "", + `evm_pool_rpc_node_polls_success{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Polls Total", + "", + `evm_pool_rpc_node_polls_total{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node States", + "", + `evm_pool_rpc_node_states{namespace="${namespace}"}`, + "{{pod}} - {{evmChainID}} - {{state}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Verifies Total", + "", + `evm_pool_rpc_node_verifies{namespace="${namespace}"}`, + "{{pod}} - {{evmChainID}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Node Verifies Success", + "", + `evm_pool_rpc_node_verifies_success{namespace="${namespace}"}`, + "{{pod}} - {{evmChainID}}", + ), + ), + dashboard.Row( + "EVM Pool RPC Node Latencies (App)", + row.Collapse(), + m.timeseriesRowOption( + "EVM Pool RPC Node Calls Latency 0.95 quantile", + "ms", + `histogram_quantile(0.95, sum(rate(evm_pool_rpc_node_rpc_call_time_bucket{namespace="${namespace}"}[$__rate_interval])) by (le, rpcCallName)) / 1e6`, + "{{pod}}", + ), + ), + dashboard.Row( + "Pipeline Tasks Metrics (App)", + row.Collapse(), + m.timeseriesRowOption( + "Pipeline Runs Queued", + "", + `pipeline_runs_queued{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "Pipeline Runs Tasks Queued", + "", + `pipeline_task_runs_queued{namespace="${namespace}"}`, + "{{pod}}", + ), + ), + } + opts = append(opts, m.extendedOpts...) + builder, err := dashboard.New( + "Chainlink Cluster Dashboard", + opts..., + ) + m.opts = opts + m.builder = builder + return err +} + +// Deploy deploys the dashboard to Grafana +func (m *CLClusterDashboard) Deploy() error { + ctx := context.Background() + client := grabana.NewClient(&http.Client{}, m.GrafanaURL, grabana.WithAPIToken(m.GrafanaToken)) + folder, err := client.FindOrCreateFolder(ctx, m.Folder) + if err != nil { + return errors.Wrap(err, ErrFailedToCreateFolder) + } + if _, err := client.UpsertDashboard(ctx, folder, m.builder); err != nil { + return errors.Wrap(err, ErrFailedToCreateDashboard) + } + return nil +} diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index 54b5f9f01e9..688660d918e 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -32,6 +32,7 @@ images: deployments: app: helm: + releaseName: "app" chart: name: cl-cluster path: . @@ -68,6 +69,7 @@ deployments: - name: node-4 image: ${DEVSPACE_IMAGE} version: latest + prometheusMonitor: "true" podAnnotations: { } nodeSelector: { } tolerations: [ ] diff --git a/charts/chainlink-cluster/go.mod b/charts/chainlink-cluster/go.mod new file mode 100644 index 00000000000..990ebbf713e --- /dev/null +++ b/charts/chainlink-cluster/go.mod @@ -0,0 +1,15 @@ +module github.com/smartcontractkit/chainlink/v2/dashboard + +go 1.21 + +require ( + github.com/K-Phoen/grabana v0.21.19 + github.com/pkg/errors v0.9.1 +) + +require ( + github.com/K-Phoen/sdk v0.12.3 // indirect + github.com/gosimple/slug v1.13.1 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect + github.com/prometheus/common v0.39.0 // indirect +) diff --git a/charts/chainlink-cluster/go.sum b/charts/chainlink-cluster/go.sum new file mode 100644 index 00000000000..093a42d6081 --- /dev/null +++ b/charts/chainlink-cluster/go.sum @@ -0,0 +1,20 @@ +github.com/K-Phoen/grabana v0.21.19 h1:tJjRO8nN9JrFjLoQGtOB9P5ILoqENZZGAtt3nK+Ry2Y= +github.com/K-Phoen/grabana v0.21.19/go.mod h1:B7gxVxacQUgHWmgqduf4WPZoKYHO1mvZnRVCoyQiwdw= +github.com/K-Phoen/sdk v0.12.3 h1:ScutEQASc9VEKJCm3OjIMD82BIS9B2XtNg3gEf6Gs+M= +github.com/K-Phoen/sdk v0.12.3/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= +github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/charts/chainlink-cluster/templates/chainlink-cm.yaml b/charts/chainlink-cluster/templates/chainlink-cm.yaml index fa9c2c2657d..736a3322048 100644 --- a/charts/chainlink-cluster/templates/chainlink-cm.yaml +++ b/charts/chainlink-cluster/templates/chainlink-cm.yaml @@ -29,10 +29,9 @@ data: [OCR] Enabled = true [P2P] - [P2P.V1] + [P2P.V2] Enabled = true - ListenIP = '0.0.0.0' - ListenPort = 6690 + ListenAddresses = ["0.0.0.0:6690"] [[EVM]] ChainID = '1337' MinContractPayment = '0' diff --git a/charts/chainlink-cluster/templates/chainlink-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-deployment.yaml index 16665916f59..b434c9894b0 100644 --- a/charts/chainlink-cluster/templates/chainlink-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-deployment.yaml @@ -37,6 +37,8 @@ spec: {{- end }} annotations: prometheus.io/scrape: 'true' + app.kubernetes.io/managed-by: "Helm" + meta.helm.sh/release-namespace: "{{ $.Release.Namespace }}" {{- range $key, $value := $.Values.podAnnotations }} {{ $key }}: {{ $value | quote }} {{- end }} diff --git a/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml b/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml index 4a74ff6c454..2cd9c3df2b6 100644 --- a/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml +++ b/charts/chainlink-cluster/templates/chainlink-pod-monitor.yaml @@ -1,16 +1,18 @@ -{{- range $cfg := .Values.chainlink.nodes }} {{- if $.Values.prometheusMonitor }} apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: + name: {{ $.Release.Name }}-pod-monitor labels: release: grafana-agent spec: - selector: - matchLabels: - release: {{ $.Release.Name }}-{{ $cfg.name }} + namespaceSelector: + matchNames: + - "cl-cluster" podMetricsEndpoints: - port: access - {{- end }} ---- -{{- end }} \ No newline at end of file + selector: + matchLabels: + app: {{ $.Release.Name }} +{{- end }} +--- \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties index ea142ce17fe..c40b5f361e1 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -12,4 +12,4 @@ sonar.cpd.exclusions=**/contracts/**/*.sol, **/config.go, /core/services/ocr2/pl # Tests' root folder, inclusions (tests to check and count) and exclusions sonar.tests=. sonar.test.inclusions=**/*_test.go, **/*.test.ts -sonar.test.exclusions=**/integration-tests/**/* +sonar.test.exclusions=**/integration-tests/**/*, **/charts/chainlink-cluster/dashboard/cmd/* From 55d4d9c88667065f56257a71c133d3b700690097 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:10:37 +0400 Subject: [PATCH 168/271] [AUTO-7258] Setup log trigger load test using wasp (#11267) * add simple log upkeep counter contract * WIP- add automationv2_1 load test * update contract * working state * add config * try GHA * disable reads inside loadgen * updates * add specs * fix linkfunds * detach runner, add loglevel config * increase geth capacity * fix batching - test end FilterLogs * fix batching * fix log filter context and batching * increase upkeep funding, fix event count calc * increase GasLimitPerReport to 10.3M * Decrease PerformLockoutWindow to 80k * reduce sleep time - log filter batch * use ConcurrentEVMClients * increase RR CPU and Mem * add pyroscope, add slack notifications * add tag and image to test config * fix dashbord URL * contracts prettier * run lint * add new load test workflow * fix AddNetworksConfig * undo changes to benchmark test workflow * add load/automationv2_1 to build test image steps * lint --- .github/actions/build-test-image/action.yml | 2 +- .../workflows/automation-benchmark-tests.yml | 3 +- .github/workflows/automation-load-tests.yml | 105 +++ CODEOWNERS | 1 + .../native_solc_compile_all_automation | 1 + .../testhelpers/SimpleLogUpkeepCounter.sol | 45 ++ .../simple_log_upkeep_counter_wrapper.go | 504 ++++++++++++++ ...rapper-dependency-versions-do-not-edit.txt | 1 + core/gethwrappers/go_generate.go | 1 + .../contracts/contract_deployer.go | 21 + .../contracts/ethereum_keeper_contracts.go | 27 + .../automationv2_1/automationv2_1_test.go | 621 ++++++++++++++++++ integration-tests/load/automationv2_1/gun.go | 43 ++ .../load/automationv2_1/helpers.go | 71 ++ .../testreporters/keeper_benchmark.go | 6 +- 15 files changed, 1447 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/automation-load-tests.yml create mode 100644 contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol create mode 100644 core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go create mode 100644 integration-tests/load/automationv2_1/automationv2_1_test.go create mode 100644 integration-tests/load/automationv2_1/gun.go create mode 100644 integration-tests/load/automationv2_1/helpers.go diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index 252b292d031..4b1dce6ee15 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -15,7 +15,7 @@ inputs: required: false suites: description: The test suites to build into the image - default: chaos migration performance reorg smoke soak benchmark + default: chaos migration performance reorg smoke soak benchmark load/automationv2_1 required: false base_image_tag: description: The test base image version to use, if not provided it will use the version from the ./integration-tests/go.mod file diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index a4338d642bc..0fff36f8df4 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -6,7 +6,7 @@ on: description: Chainlink image version to use required: true type: string - default: 2.5.0 + default: 2.6.0 chainlinkImage: description: Chainlink image repo to use required: true @@ -108,6 +108,7 @@ jobs: QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + suites: benchmark load/automationv2_1 chaos reorg - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 env: diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml new file mode 100644 index 00000000000..eebd87322cf --- /dev/null +++ b/.github/workflows/automation-load-tests.yml @@ -0,0 +1,105 @@ +name: Automation Load Test +on: + workflow_dispatch: + inputs: + chainlinkVersion: + description: Chainlink image version to use + required: true + type: string + default: 2.6.0 + chainlinkImage: + description: Chainlink image repo to use + required: true + type: string + default: public.ecr.aws/chainlink/chainlink + network: + description: Network to run tests on + required: true + type: choice + options: + - SIMULATED + TestInputs: + description: TestInputs + required: false + type: string + slackMemberID: + description: Notifies test results (Not your @) + required: true + default: U02Q14G80TY + type: string + +jobs: + automation_load: + environment: integration + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + name: ${{ inputs.network }} Automation Load Test + runs-on: ubuntu20.04-16cores-64GB + env: + SELECTED_NETWORKS: ${{ inputs.network }} + SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} + SLACK_CHANNEL: C03KJ5S7KEK + TEST_INPUTS: ${{ inputs.TestInputs }} + CHAINLINK_ENV_USER: ${{ github.actor }} + REF_NAME: ${{ github.head_ref || github.ref_name }} + steps: + - name: Setup Push Tag + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + + - name: Add mask + run: | + SLACK_USER=$(jq -r '.inputs.slackMemberID' $GITHUB_EVENT_PATH) + echo ::add-mask::$SLACK_USER + echo SLACK_USER=$SLACK_USER >> $GITHUB_ENV + - name: Checkout the repo + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ env.REF_NAME }} + - name: Build Test Image + uses: ./.github/actions/build-test-image + with: + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + suites: benchmark load/automationv2_1 chaos reorg + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + env: + RR_CPU: 4000m + RR_MEM: 4Gi + DETACH_RUNNER: true + TEST_SUITE: automationv2_1 + TEST_ARGS: -test.timeout 720h + ENV_JOB_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:${{ github.sha }} + INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com + PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + with: + test_command_to_run: cd integration-tests && go test -timeout 1h -v -run TestLogTrigger ./load/automationv2_1 -count=1 + test_download_vendor_packages_command: make gomod + cl_repo: ${{ inputs.chainlinkImage }} + cl_image_tag: ${{ inputs.chainlinkVersion }} + token: ${{ secrets.GITHUB_TOKEN }} + should_cleanup: false + go_mod_path: ./integration-tests/go.mod + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + - 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: ${{ inputs.network }} Automation Load Test + test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + continue-on-error: true diff --git a/CODEOWNERS b/CODEOWNERS index 8ec42ed6dd4..5f33e68e514 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -104,6 +104,7 @@ core/scripts/gateway @bolekk @pinebit /.github/workflows/performance-tests.yml @smartcontractkit/test-tooling-team /.github/workflows/automation-ondemand-tests.yml @smartcontractkit/keepers /.github/workflows/automation-benchmark-tests.yml @smartcontractkit/keepers +/.github/workflows/automation-load-tests.yml @smartcontractkit/keepers /core/chainlink.Dockerfile @smartcontractkit/prodsec-public diff --git a/contracts/scripts/native_solc_compile_all_automation b/contracts/scripts/native_solc_compile_all_automation index 1c54d677135..ddf6c2c8bfa 100755 --- a/contracts/scripts/native_solc_compile_all_automation +++ b/contracts/scripts/native_solc_compile_all_automation @@ -41,6 +41,7 @@ compileContract automation/v2_0/KeeperRegistryLogic2_0.sol compileContract automation/UpkeepTranscoder.sol compileContract automation/mocks/MockAggregatorProxy.sol compileContract automation/testhelpers/LogUpkeepCounter.sol +compileContract automation/testhelpers/SimpleLogUpkeepCounter.sol compileContract automation/mocks/KeeperRegistrar1_2Mock.sol compileContract automation/mocks/KeeperRegistryCheckUpkeepGasUsageWrapper1_2Mock.sol diff --git a/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol new file mode 100644 index 00000000000..563c1354b66 --- /dev/null +++ b/contracts/src/v0.8/automation/testhelpers/SimpleLogUpkeepCounter.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.6; + +import {ILogAutomation, Log} from "../interfaces/ILogAutomation.sol"; + +contract SimpleLogUpkeepCounter is ILogAutomation { + event PerformingUpkeep( + address indexed from, + uint256 initialBlock, + uint256 lastBlock, + uint256 previousBlock, + uint256 counter, + uint256 timeToPerform + ); + + uint256 public lastBlock; + uint256 public previousPerformBlock; + uint256 public initialBlock; + uint256 public counter; + uint256 public timeToPerform; + + constructor() { + previousPerformBlock = 0; + lastBlock = block.number; + initialBlock = 0; + counter = 0; + } + + function checkLog(Log calldata log, bytes memory) external view override returns (bool, bytes memory) { + return (true, abi.encode(log)); + } + + function performUpkeep(bytes calldata performData) external override { + if (initialBlock == 0) { + initialBlock = block.number; + } + lastBlock = block.number; + counter = counter + 1; + previousPerformBlock = lastBlock; + Log memory log = abi.decode(performData, (Log)); + timeToPerform = block.timestamp - log.timestamp; + emit PerformingUpkeep(tx.origin, initialBlock, lastBlock, previousPerformBlock, counter, timeToPerform); + } +} diff --git a/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go new file mode 100644 index 00000000000..bb28c8bd6c4 --- /dev/null +++ b/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper/simple_log_upkeep_counter_wrapper.go @@ -0,0 +1,504 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package simple_log_upkeep_counter_wrapper + +import ( + "errors" + "fmt" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +type Log struct { + Index *big.Int + Timestamp *big.Int + TxHash [32]byte + BlockNumber *big.Int + BlockHash [32]byte + Source common.Address + Topics [][32]byte + Data []byte +} + +var SimpleLogUpkeepCounterMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"initialBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lastBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBlock\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"counter\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"timeToPerform\",\"type\":\"uint256\"}],\"name\":\"PerformingUpkeep\",\"type\":\"event\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"txHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"source\",\"type\":\"address\"},{\"internalType\":\"bytes32[]\",\"name\":\"topics\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structLog\",\"name\":\"log\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"checkLog\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"counter\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"lastBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"performData\",\"type\":\"bytes\"}],\"name\":\"performUpkeep\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"previousPerformBlock\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"timeToPerform\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b5060006001819055438155600281905560035561088a806100326000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806361bc221a1161005b57806361bc221a146100d4578063806b984f146100dd578063917d895f146100e6578063c6066f0d146100ef57600080fd5b80632cb158641461008257806340691db41461009e5780634585e33b146100bf575b600080fd5b61008b60025481565b6040519081526020015b60405180910390f35b6100b16100ac366004610384565b6100f8565b60405161009592919061055e565b6100d26100cd366004610312565b61012a565b005b61008b60035481565b61008b60005481565b61008b60015481565b61008b60045481565b6000606060018460405160200161010f91906105db565b604051602081830303815290604052915091505b9250929050565b60025461013657436002555b436000556003546101489060016107f0565b6003556000805460015561015e828401846103f1565b90508060200151426101709190610808565b6004819055600254600054600154600354604080519485526020850193909352918301526060820152608081019190915232907f4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd659060a00160405180910390a2505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101f957600080fd5b919050565b600082601f83011261020f57600080fd5b8135602067ffffffffffffffff82111561022b5761022b61084e565b8160051b61023a8282016106d6565b83815282810190868401838801850189101561025557600080fd5b600093505b8584101561027857803583526001939093019291840191840161025a565b50979650505050505050565b600082601f83011261029557600080fd5b813567ffffffffffffffff8111156102af576102af61084e565b6102e060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016106d6565b8181528460208386010111156102f557600080fd5b816020850160208301376000918101602001919091529392505050565b6000806020838503121561032557600080fd5b823567ffffffffffffffff8082111561033d57600080fd5b818501915085601f83011261035157600080fd5b81358181111561036057600080fd5b86602082850101111561037257600080fd5b60209290920196919550909350505050565b6000806040838503121561039757600080fd5b823567ffffffffffffffff808211156103af57600080fd5b9084019061010082870312156103c457600080fd5b909250602084013590808211156103da57600080fd5b506103e785828601610284565b9150509250929050565b60006020828403121561040357600080fd5b813567ffffffffffffffff8082111561041b57600080fd5b90830190610100828603121561043057600080fd5b6104386106ac565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015261047060a084016101d5565b60a082015260c08301358281111561048757600080fd5b610493878286016101fe565b60c08301525060e0830135828111156104ab57600080fd5b6104b787828601610284565b60e08301525095945050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156104f857600080fd5b8260051b8083602087013760009401602001938452509192915050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b821515815260006020604081840152835180604085015260005b8181101561059457858101830151858201606001528201610578565b818111156105a6576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b6020815281356020820152602082013560408201526040820135606082015260608201356080820152608082013560a082015273ffffffffffffffffffffffffffffffffffffffff61062f60a084016101d5565b1660c0820152600061064460c0840184610725565b6101008060e086015261065c610120860183856104c6565b925061066b60e087018761078c565b92507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086850301828701526106a1848483610515565b979650505050505050565b604051610100810167ffffffffffffffff811182821017156106d0576106d061084e565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561071d5761071d61084e565b604052919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261075a57600080fd5b830160208101925035905067ffffffffffffffff81111561077a57600080fd5b8060051b360383131561012357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126107c157600080fd5b830160208101925035905067ffffffffffffffff8111156107e157600080fd5b80360383131561012357600080fd5b600082198211156108035761080361081f565b500190565b60008282101561081a5761081a61081f565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a", +} + +var SimpleLogUpkeepCounterABI = SimpleLogUpkeepCounterMetaData.ABI + +var SimpleLogUpkeepCounterBin = SimpleLogUpkeepCounterMetaData.Bin + +func DeploySimpleLogUpkeepCounter(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SimpleLogUpkeepCounter, error) { + parsed, err := SimpleLogUpkeepCounterMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SimpleLogUpkeepCounterBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &SimpleLogUpkeepCounter{address: address, abi: *parsed, SimpleLogUpkeepCounterCaller: SimpleLogUpkeepCounterCaller{contract: contract}, SimpleLogUpkeepCounterTransactor: SimpleLogUpkeepCounterTransactor{contract: contract}, SimpleLogUpkeepCounterFilterer: SimpleLogUpkeepCounterFilterer{contract: contract}}, nil +} + +type SimpleLogUpkeepCounter struct { + address common.Address + abi abi.ABI + SimpleLogUpkeepCounterCaller + SimpleLogUpkeepCounterTransactor + SimpleLogUpkeepCounterFilterer +} + +type SimpleLogUpkeepCounterCaller struct { + contract *bind.BoundContract +} + +type SimpleLogUpkeepCounterTransactor struct { + contract *bind.BoundContract +} + +type SimpleLogUpkeepCounterFilterer struct { + contract *bind.BoundContract +} + +type SimpleLogUpkeepCounterSession struct { + Contract *SimpleLogUpkeepCounter + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type SimpleLogUpkeepCounterCallerSession struct { + Contract *SimpleLogUpkeepCounterCaller + CallOpts bind.CallOpts +} + +type SimpleLogUpkeepCounterTransactorSession struct { + Contract *SimpleLogUpkeepCounterTransactor + TransactOpts bind.TransactOpts +} + +type SimpleLogUpkeepCounterRaw struct { + Contract *SimpleLogUpkeepCounter +} + +type SimpleLogUpkeepCounterCallerRaw struct { + Contract *SimpleLogUpkeepCounterCaller +} + +type SimpleLogUpkeepCounterTransactorRaw struct { + Contract *SimpleLogUpkeepCounterTransactor +} + +func NewSimpleLogUpkeepCounter(address common.Address, backend bind.ContractBackend) (*SimpleLogUpkeepCounter, error) { + abi, err := abi.JSON(strings.NewReader(SimpleLogUpkeepCounterABI)) + if err != nil { + return nil, err + } + contract, err := bindSimpleLogUpkeepCounter(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounter{address: address, abi: abi, SimpleLogUpkeepCounterCaller: SimpleLogUpkeepCounterCaller{contract: contract}, SimpleLogUpkeepCounterTransactor: SimpleLogUpkeepCounterTransactor{contract: contract}, SimpleLogUpkeepCounterFilterer: SimpleLogUpkeepCounterFilterer{contract: contract}}, nil +} + +func NewSimpleLogUpkeepCounterCaller(address common.Address, caller bind.ContractCaller) (*SimpleLogUpkeepCounterCaller, error) { + contract, err := bindSimpleLogUpkeepCounter(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterCaller{contract: contract}, nil +} + +func NewSimpleLogUpkeepCounterTransactor(address common.Address, transactor bind.ContractTransactor) (*SimpleLogUpkeepCounterTransactor, error) { + contract, err := bindSimpleLogUpkeepCounter(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterTransactor{contract: contract}, nil +} + +func NewSimpleLogUpkeepCounterFilterer(address common.Address, filterer bind.ContractFilterer) (*SimpleLogUpkeepCounterFilterer, error) { + contract, err := bindSimpleLogUpkeepCounter(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterFilterer{contract: contract}, nil +} + +func bindSimpleLogUpkeepCounter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := SimpleLogUpkeepCounterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SimpleLogUpkeepCounter.Contract.SimpleLogUpkeepCounterCaller.contract.Call(opts, result, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.SimpleLogUpkeepCounterTransactor.contract.Transfer(opts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.SimpleLogUpkeepCounterTransactor.contract.Transact(opts, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SimpleLogUpkeepCounter.Contract.contract.Call(opts, result, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.contract.Transfer(opts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.contract.Transact(opts, method, params...) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) CheckLog(opts *bind.CallOpts, log Log, arg1 []byte) (bool, []byte, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "checkLog", log, arg1) + + if err != nil { + return *new(bool), *new([]byte), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + out1 := *abi.ConvertType(out[1], new([]byte)).(*[]byte) + + return out0, out1, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) CheckLog(log Log, arg1 []byte) (bool, []byte, error) { + return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, arg1) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) CheckLog(log Log, arg1 []byte) (bool, []byte, error) { + return _SimpleLogUpkeepCounter.Contract.CheckLog(&_SimpleLogUpkeepCounter.CallOpts, log, arg1) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) Counter(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "counter") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) Counter() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.Counter(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) Counter() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.Counter(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) InitialBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "initialBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) InitialBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.InitialBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) InitialBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.InitialBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) LastBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "lastBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) LastBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.LastBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) LastBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.LastBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) PreviousPerformBlock(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "previousPerformBlock") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) PreviousPerformBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.PreviousPerformBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) PreviousPerformBlock() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.PreviousPerformBlock(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCaller) TimeToPerform(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SimpleLogUpkeepCounter.contract.Call(opts, &out, "timeToPerform") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) TimeToPerform() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.TimeToPerform(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterCallerSession) TimeToPerform() (*big.Int, error) { + return _SimpleLogUpkeepCounter.Contract.TimeToPerform(&_SimpleLogUpkeepCounter.CallOpts) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactor) PerformUpkeep(opts *bind.TransactOpts, performData []byte) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.contract.Transact(opts, "performUpkeep", performData) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterSession) PerformUpkeep(performData []byte) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.PerformUpkeep(&_SimpleLogUpkeepCounter.TransactOpts, performData) +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterTransactorSession) PerformUpkeep(performData []byte) (*types.Transaction, error) { + return _SimpleLogUpkeepCounter.Contract.PerformUpkeep(&_SimpleLogUpkeepCounter.TransactOpts, performData) +} + +type SimpleLogUpkeepCounterPerformingUpkeepIterator struct { + Event *SimpleLogUpkeepCounterPerformingUpkeep + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *SimpleLogUpkeepCounterPerformingUpkeepIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *SimpleLogUpkeepCounterPerformingUpkeepIterator) Error() error { + return it.fail +} + +func (it *SimpleLogUpkeepCounterPerformingUpkeepIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type SimpleLogUpkeepCounterPerformingUpkeep struct { + From common.Address + InitialBlock *big.Int + LastBlock *big.Int + PreviousBlock *big.Int + Counter *big.Int + TimeToPerform *big.Int + Raw types.Log +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterFilterer) FilterPerformingUpkeep(opts *bind.FilterOpts, from []common.Address) (*SimpleLogUpkeepCounterPerformingUpkeepIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _SimpleLogUpkeepCounter.contract.FilterLogs(opts, "PerformingUpkeep", fromRule) + if err != nil { + return nil, err + } + return &SimpleLogUpkeepCounterPerformingUpkeepIterator{contract: _SimpleLogUpkeepCounter.contract, event: "PerformingUpkeep", logs: logs, sub: sub}, nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterFilterer) WatchPerformingUpkeep(opts *bind.WatchOpts, sink chan<- *SimpleLogUpkeepCounterPerformingUpkeep, from []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + + logs, sub, err := _SimpleLogUpkeepCounter.contract.WatchLogs(opts, "PerformingUpkeep", fromRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := _SimpleLogUpkeepCounter.contract.UnpackLog(event, "PerformingUpkeep", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounterFilterer) ParsePerformingUpkeep(log types.Log) (*SimpleLogUpkeepCounterPerformingUpkeep, error) { + event := new(SimpleLogUpkeepCounterPerformingUpkeep) + if err := _SimpleLogUpkeepCounter.contract.UnpackLog(event, "PerformingUpkeep", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) ParseLog(log types.Log) (generated.AbigenLog, error) { + switch log.Topics[0] { + case _SimpleLogUpkeepCounter.abi.Events["PerformingUpkeep"].ID: + return _SimpleLogUpkeepCounter.ParsePerformingUpkeep(log) + + default: + return nil, fmt.Errorf("abigen wrapper received unknown log topic: %v", log.Topics[0]) + } +} + +func (SimpleLogUpkeepCounterPerformingUpkeep) Topic() common.Hash { + return common.HexToHash("0x4874b8dd61a40fe23599b4360a9a824d7081742fca9f555bcee3d389c4f4bd65") +} + +func (_SimpleLogUpkeepCounter *SimpleLogUpkeepCounter) Address() common.Address { + return _SimpleLogUpkeepCounter.address +} + +type SimpleLogUpkeepCounterInterface interface { + CheckLog(opts *bind.CallOpts, log Log, arg1 []byte) (bool, []byte, error) + + Counter(opts *bind.CallOpts) (*big.Int, error) + + InitialBlock(opts *bind.CallOpts) (*big.Int, error) + + LastBlock(opts *bind.CallOpts) (*big.Int, error) + + PreviousPerformBlock(opts *bind.CallOpts) (*big.Int, error) + + TimeToPerform(opts *bind.CallOpts) (*big.Int, error) + + PerformUpkeep(opts *bind.TransactOpts, performData []byte) (*types.Transaction, error) + + FilterPerformingUpkeep(opts *bind.FilterOpts, from []common.Address) (*SimpleLogUpkeepCounterPerformingUpkeepIterator, error) + + WatchPerformingUpkeep(opts *bind.WatchOpts, sink chan<- *SimpleLogUpkeepCounterPerformingUpkeep, from []common.Address) (event.Subscription, error) + + ParsePerformingUpkeep(log types.Log) (*SimpleLogUpkeepCounterPerformingUpkeep, error) + + ParseLog(log types.Log) (generated.AbigenLog, error) + + Address() common.Address +} diff --git a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt index 6482c01cf88..6efc75fce98 100644 --- a/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt +++ b/core/gethwrappers/generation/generated-wrapper-dependency-versions-do-not-edit.txt @@ -50,6 +50,7 @@ operator_factory: ../../contracts/solc/v0.8.19/OperatorFactory/OperatorFactory.a operator_wrapper: ../../contracts/solc/v0.8.19/Operator/Operator.abi ../../contracts/solc/v0.8.19/Operator/Operator.bin d7abd0e67f30a3a4c9c04c896124391306fa364fcf579fa6df04dbf912b48568 oracle_wrapper: ../../contracts/solc/v0.6/Oracle/Oracle.abi ../../contracts/solc/v0.6/Oracle/Oracle.bin 7af2fbac22a6e8c2847e8e685a5400cac5101d72ddf5365213beb79e4dede43a perform_data_checker_wrapper: ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.abi ../../contracts/solc/v0.8.16/PerformDataChecker/PerformDataChecker.bin 48d8309c2117c29a24e1155917ab0b780956b2cd6a8a39ef06ae66a7f6d94f73 +simple_log_upkeep_counter_wrapper: ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin 0a7a0cc4da7dc2a3d0a0c36c746b1adc044af5cad1838367356a0604f3255a01 solidity_vrf_consumer_interface: ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.6/VRFConsumer/VRFConsumer.bin ecc99378aa798014de9db42b2eb81320778b0663dbe208008dad75ccdc1d4366 solidity_vrf_consumer_interface_v08: ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.abi ../../contracts/solc/v0.8.6/VRFConsumer/VRFConsumer.bin b14f9136b15e3dc9d6154d5700f3ed4cf88ddc4f70f20c3bb57fc46050904c8f solidity_vrf_coordinator_interface: ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.abi ../../contracts/solc/v0.6/VRFCoordinator/VRFCoordinator.bin a23d3c395156804788c7f6fbda2994e8f7184304c0f0c9f2c4ddeaf073d346d2 diff --git a/core/gethwrappers/go_generate.go b/core/gethwrappers/go_generate.go index 3965c159080..07e4fa9b8f3 100644 --- a/core/gethwrappers/go_generate.go +++ b/core/gethwrappers/go_generate.go @@ -59,6 +59,7 @@ package gethwrappers //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin AutomationUtils automation_utils_2_1 //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.abi ../../contracts/solc/v0.8.16/AutomationForwarderLogic/AutomationForwarderLogic.bin AutomationForwarderLogic automation_forwarder_logic //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.abi ../../contracts/solc/v0.8.6/LogUpkeepCounter/LogUpkeepCounter.bin LogUpkeepCounter log_upkeep_counter_wrapper +//go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.abi ../../contracts/solc/v0.8.6/SimpleLogUpkeepCounter/SimpleLogUpkeepCounter.bin SimpleLogUpkeepCounter simple_log_upkeep_counter_wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.abi ../../contracts/solc/v0.8.16/LogTriggeredStreamsLookup/LogTriggeredStreamsLookup.bin LogTriggeredStreamsLookup log_triggered_streams_lookup_wrapper //go:generate go run ./generation/generate/wrap.go ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.abi ../../contracts/solc/v0.8.16/DummyProtocol/DummyProtocol.bin DummyProtocol dummy_protocol_wrapper diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 45195d327ee..000fe7b2b81 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -55,6 +55,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_factory" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/oracle_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/perform_data_checker_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/test_api_consumer_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_counter_wrapper" @@ -92,6 +93,7 @@ type ContractDeployer interface { LoadKeeperRegistry(address common.Address, registryVersion eth_contracts.KeeperRegistryVersion) (KeeperRegistry, error) DeployKeeperConsumer(updateInterval *big.Int) (KeeperConsumer, error) DeployAutomationLogTriggerConsumer(testInterval *big.Int) (KeeperConsumer, error) + DeployAutomationSimpleLogTriggerConsumer() (KeeperConsumer, error) DeployAutomationStreamsLookupUpkeepConsumer(testRange *big.Int, interval *big.Int, useArbBlock bool, staging bool, verify bool) (KeeperConsumer, error) DeployAutomationLogTriggeredStreamsLookupUpkeepConsumer() (KeeperConsumer, error) DeployKeeperConsumerPerformance( @@ -1292,6 +1294,25 @@ func (e *EthereumContractDeployer) DeployAutomationLogTriggerConsumer(testInterv }, err } +func (e *EthereumContractDeployer) DeployAutomationSimpleLogTriggerConsumer() (KeeperConsumer, error) { + address, _, instance, err := e.client.DeployContract("SimpleLogUpkeepCounter", func( + auth *bind.TransactOpts, + backend bind.ContractBackend, + ) (common.Address, *types.Transaction, interface{}, error) { + return simple_log_upkeep_counter_wrapper.DeploySimpleLogUpkeepCounter( + auth, backend, + ) + }) + if err != nil { + return nil, err + } + return &EthereumAutomationSimpleLogCounterConsumer{ + client: e.client, + consumer: instance.(*simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounter), + address: address, + }, err +} + func (e *EthereumContractDeployer) DeployAutomationStreamsLookupUpkeepConsumer(testRange *big.Int, interval *big.Int, useArbBlock bool, staging bool, verify bool) (KeeperConsumer, error) { address, _, instance, err := e.client.DeployContract("StreamsLookupUpkeep", func( auth *bind.TransactOpts, diff --git a/integration-tests/contracts/ethereum_keeper_contracts.go b/integration-tests/contracts/ethereum_keeper_contracts.go index 2c0250e7454..7519b5de4cf 100644 --- a/integration-tests/contracts/ethereum_keeper_contracts.go +++ b/integration-tests/contracts/ethereum_keeper_contracts.go @@ -35,6 +35,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_triggered_streams_lookup_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/perform_data_checker_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_upkeep_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_perform_counter_restrictive_wrapper" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/upkeep_transcoder" @@ -1821,6 +1822,32 @@ func (v *EthereumAutomationLogCounterConsumer) Counter(ctx context.Context) (*bi return cnt, nil } +type EthereumAutomationSimpleLogCounterConsumer struct { + client blockchain.EVMClient + consumer *simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounter + address *common.Address +} + +func (v *EthereumAutomationSimpleLogCounterConsumer) Address() string { + return v.address.Hex() +} + +func (v *EthereumAutomationSimpleLogCounterConsumer) Start() error { + return nil +} + +func (v *EthereumAutomationSimpleLogCounterConsumer) Counter(ctx context.Context) (*big.Int, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + cnt, err := v.consumer.Counter(opts) + if err != nil { + return nil, err + } + return cnt, nil +} + // EthereumKeeperConsumerPerformance represents a more complicated keeper consumer contract, one intended only for // performance tests. type EthereumKeeperConsumerPerformance struct { diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go new file mode 100644 index 00000000000..dfef099c175 --- /dev/null +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -0,0 +1,621 @@ +package automationv2_1 + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + "os" + "strconv" + "strings" + "testing" + "time" + + geth "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/slack-go/slack" + ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" +) + +const ( + StartupWaitTime = 30 * time.Second + StopWaitTime = 60 * time.Second +) + +var ( + baseTOML = `[Feature] +LogPoller = true + +[OCR2] +Enabled = true + +[P2P] +[P2P.V2] +Enabled = true +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"]` + + minimumNodeSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "2000m", + "memory": "4Gi", + }, + "limits": map[string]interface{}{ + "cpu": "2000m", + "memory": "4Gi", + }, + }, + } + + minimumDbSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "1000m", + "memory": "1Gi", + }, + "limits": map[string]interface{}{ + "cpu": "1000m", + "memory": "1Gi", + }, + }, + "stateful": true, + "capacity": "5Gi", + } + + recNodeSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "4000m", + "memory": "8Gi", + }, + "limits": map[string]interface{}{ + "cpu": "4000m", + "memory": "8Gi", + }, + }, + } + + recDbSpec = map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "2000m", + "memory": "2Gi", + }, + "limits": map[string]interface{}{ + "cpu": "2000m", + "memory": "2Gi", + }, + }, + "stateful": true, + "capacity": "10Gi", + } +) + +var ( + numberofNodes, _ = strconv.Atoi(getEnv("NUMBEROFNODES", "6")) // Number of nodes in the DON + numberOfUpkeeps, _ = strconv.Atoi(getEnv("NUMBEROFUPKEEPS", "100")) // Number of log triggered upkeeps + duration, _ = strconv.Atoi(getEnv("DURATION", "900")) // Test duration in seconds + blockTime, _ = strconv.Atoi(getEnv("BLOCKTIME", "1")) // Block time in seconds for geth simulated dev network + numberOfEvents, _ = strconv.Atoi(getEnv("NUMBEROFEVENTS", "1")) // Number of events to emit per trigger + specType = getEnv("SPECTYPE", "minimum") // minimum, recommended, local specs for the test + logLevel = getEnv("LOGLEVEL", "info") // log level for the chainlink nodes + pyroscope, _ = strconv.ParseBool(getEnv("PYROSCOPE", "false")) // enable pyroscope for the chainlink nodes +) + +func TestLogTrigger(t *testing.T) { + l := logging.GetTestLogger(t) + + l.Info().Msg("Starting automation v2.1 log trigger load test") + l.Info().Str("TEST_INPUTS", os.Getenv("TEST_INPUTS")).Int("Number of Nodes", numberofNodes). + Int("Number of Upkeeps", numberOfUpkeeps). + Int("Duration", duration). + Int("Block Time", blockTime). + Int("Number of Events", numberOfEvents). + Str("Spec Type", specType). + Str("Log Level", logLevel). + Str("Image", os.Getenv(config.EnvVarCLImage)). + Str("Tag", os.Getenv(config.EnvVarCLTag)). + Msg("Test Config") + + testConfig := fmt.Sprintf("Number of Nodes: %d\nNumber of Upkeeps: %d\nDuration: %d\nBlock Time: %d\n"+ + "Number of Events: %d\nSpec Type: %s\nLog Level: %s\nImage: %s\nTag: %s\n", numberofNodes, numberOfUpkeeps, duration, + blockTime, numberOfEvents, specType, logLevel, os.Getenv(config.EnvVarCLImage), os.Getenv(config.EnvVarCLTag)) + + testNetwork := networks.MustGetSelectedNetworksFromEnv()[0] + testType := "load" + loadDuration := time.Duration(duration) * time.Second + automationDefaultLinkFunds := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(10000))) //10000 LINK + automationDefaultUpkeepGasLimit := uint32(1_000_000) + + registrySettings := &contracts.KeeperRegistrySettings{ + PaymentPremiumPPB: uint32(0), + FlatFeeMicroLINK: uint32(40_000), + BlockCountPerTurn: big.NewInt(100), + CheckGasLimit: uint32(45_000_000), //45M + StalenessSeconds: big.NewInt(90_000), + GasCeilingMultiplier: uint16(2), + MaxPerformGas: uint32(5_000_000), + MinUpkeepSpend: big.NewInt(0), + FallbackGasPrice: big.NewInt(2e11), + FallbackLinkPrice: big.NewInt(2e18), + MaxCheckDataSize: uint32(5_000), + MaxPerformDataSize: uint32(5_000), + RegistryVersion: contractseth.RegistryVersion_2_1, + } + + testEnvironment := environment.New(&environment.Config{ + TTL: time.Hour * 24, // 1 day, + NamespacePrefix: fmt.Sprintf( + "automation-%s-%s", + testType, + strings.ReplaceAll(strings.ToLower(testNetwork.Name), " ", "-"), + ), + Test: t, + PreventPodEviction: true, + }) + + if testEnvironment.WillUseRemoteRunner() { + key := "TEST_INPUTS" + err := os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable TEST_INPUTS for remote runner") + + key = config.EnvVarPyroscopeServer + err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable PYROSCOPE_SERVER for remote runner") + + key = config.EnvVarPyroscopeKey + err = os.Setenv(fmt.Sprintf("TEST_%s", key), os.Getenv(key)) + require.NoError(t, err, "failed to set the environment variable PYROSCOPE_KEY for remote runner") + + key = "GRAFANA_DASHBOARD_URL" + err = os.Setenv(fmt.Sprintf("TEST_%s", key), getEnv(key, "")) + require.NoError(t, err, "failed to set the environment variable GRAFANA_DASHBOARD_URL for remote runner") + } + + testEnvironment. + AddHelm(ethereum.New(ðereum.Props{ + NetworkName: testNetwork.Name, + Simulated: testNetwork.Simulated, + WsURLs: testNetwork.URLs, + Values: map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "4000m", + "memory": "4Gi", + }, + "limits": map[string]interface{}{ + "cpu": "8000m", + "memory": "8Gi", + }, + }, + "geth": map[string]interface{}{ + "blocktime": blockTime, + "capacity": "10Gi", + }, + }, + })) + + err := testEnvironment.Run() + require.NoError(t, err, "Error launching test environment") + + if testEnvironment.WillUseRemoteRunner() { + return + } + + var ( + nodeSpec = minimumNodeSpec + dbSpec = minimumDbSpec + ) + + switch specType { + case "recommended": + nodeSpec = recNodeSpec + dbSpec = recDbSpec + case "local": + nodeSpec = map[string]interface{}{} + dbSpec = map[string]interface{}{"stateful": true} + default: + // minimum: + + } + + if !pyroscope { + err = os.Setenv(config.EnvVarPyroscopeServer, "") + require.NoError(t, err, "Error setting pyroscope server env var") + } + + err = os.Setenv(config.EnvVarPyroscopeEnvironment, testEnvironment.Cfg.Namespace) + require.NoError(t, err, "Error setting pyroscope environment env var") + + for i := 0; i < numberofNodes+1; i++ { // +1 for the OCR boot node + var nodeTOML string + if i == 1 || i == 3 { + nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"%s\"", baseTOML, logLevel) + } else { + nodeTOML = fmt.Sprintf("%s\n\n[Log]\nLevel = \"info\"", baseTOML) + } + nodeTOML = networks.AddNetworksConfig(nodeTOML, testNetwork) + testEnvironment.AddHelm(chainlink.New(i, map[string]any{ + "toml": nodeTOML, + "chainlink": nodeSpec, + "db": dbSpec, + })) + } + + err = testEnvironment.Run() + require.NoError(t, err, "Error running chainlink DON") + + chainClient, err := blockchain.NewEVMClient(testNetwork, testEnvironment, l) + require.NoError(t, err, "Error building chain client") + + contractDeployer, err := contracts.NewContractDeployer(chainClient, l) + require.NoError(t, err, "Error building contract deployer") + + chainlinkNodes, err := client.ConnectChainlinkNodes(testEnvironment) + require.NoError(t, err, "Error connecting to chainlink nodes") + + chainClient.ParallelTransactions(true) + + linkToken, err := contractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Error deploying link token contract") + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for contracts to deploy") + + registry, registrar := actions.DeployAutoOCRRegistryAndRegistrar( + t, contractseth.RegistryVersion_2_1, *registrySettings, linkToken, contractDeployer, chainClient, + ) + + err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(100), 0) + require.NoError(t, err, "Error funding chainlink nodes") + + actions.CreateOCRKeeperJobs( + t, + chainlinkNodes, + registry.Address(), + chainClient.GetChainID().Int64(), + 0, + contractseth.RegistryVersion_2_1, + ) + + S, oracleIdentities, err := actions.GetOracleIdentities(chainlinkNodes) + require.NoError(t, err, "Error getting oracle identities") + offC, err := json.Marshal(ocr2keepers30config.OffchainConfig{ + TargetProbability: "0.999", + TargetInRounds: 1, + PerformLockoutWindow: 80_000, // Copied from arbitrum mainnet prod value + GasLimitPerReport: 10_300_000, + GasOverheadPerUpkeep: 300_000, + MinConfirmations: 0, + MaxUpkeepBatchSize: 10, + }) + require.NoError(t, err, "Error marshalling offchain config") + + signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err := ocr3.ContractSetConfigArgsForTests( + 10*time.Second, // deltaProgress time.Duration, + 15*time.Second, // deltaResend time.Duration, + 500*time.Millisecond, // deltaInitial time.Duration, + 1000*time.Millisecond, // deltaRound time.Duration, + 200*time.Millisecond, // deltaGrace time.Duration, + 300*time.Millisecond, // deltaCertifiedCommitRequest time.Duration + 15*time.Second, // deltaStage time.Duration, + 24, // rMax uint64, + S, // s []int, + oracleIdentities, // oracles []OracleIdentityExtra, + offC, // reportingPluginConfig []byte, + 20*time.Millisecond, // maxDurationQuery time.Duration, + 20*time.Millisecond, // maxDurationObservation time.Duration, // good to here + 1200*time.Millisecond, // maxDurationShouldAcceptAttestedReport time.Duration, + 20*time.Millisecond, // maxDurationShouldTransmitAcceptedReport time.Duration, + 1, // f int, + nil, // onchainConfig []byte, + ) + require.NoError(t, err, "Error setting OCR config vars") + + var signers []common.Address + for _, signer := range signerOnchainPublicKeys { + require.Equal(t, 20, len(signer), "OnChainPublicKey '%v' has wrong length for address", signer) + signers = append(signers, common.BytesToAddress(signer)) + } + + var transmitters []common.Address + for _, transmitter := range transmitterAccounts { + require.True(t, common.IsHexAddress(string(transmitter)), "TransmitAccount '%s' is not a valid Ethereum address", string(transmitter)) + transmitters = append(transmitters, common.HexToAddress(string(transmitter))) + } + + onchainConfig, err := registrySettings.EncodeOnChainConfig(registrar.Address(), common.HexToAddress(chainClient.GetDefaultWallet().Address())) + require.NoError(t, err, "Error encoding onchain config") + l.Info().Msg("Done building OCR config") + ocrConfig := contracts.OCRv2Config{ + Signers: signers, + Transmitters: transmitters, + F: f, + OnchainConfig: onchainConfig, + OffchainConfigVersion: offchainConfigVersion, + OffchainConfig: offchainConfig, + } + + err = registry.SetConfig(*registrySettings, ocrConfig) + require.NoError(t, err, "Error setting registry config") + + consumerContracts := make([]contracts.KeeperConsumer, 0) + triggerContracts := make([]contracts.LogEmitter, 0) + + utilsABI, err := automation_utils_2_1.AutomationUtilsMetaData.GetAbi() + require.NoError(t, err, "Error getting automation utils abi") + registrarABI, err := registrar21.AutomationRegistrarMetaData.GetAbi() + require.NoError(t, err, "Error getting automation registrar abi") + emitterABI, err := log_emitter.LogEmitterMetaData.GetAbi() + require.NoError(t, err, "Error getting log emitter abi") + consumerABI, err := simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounterMetaData.GetAbi() + require.NoError(t, err, "Error getting consumer abi") + + var bytes0 = [32]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, + } + registrationTxHashes := make([]common.Hash, 0) + upkeepIds := make([]*big.Int, 0) + + for i := 0; i < numberOfUpkeeps; i++ { + consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() + require.NoError(t, err, "Error deploying automation consumer contract") + consumerContracts = append(consumerContracts, consumerContract) + l.Debug(). + Str("Contract Address", consumerContract.Address()). + Int("Number", i+1). + Int("Out Of", numberOfUpkeeps). + Msg("Deployed Automation Log Trigger Consumer Contract") + + cEVMClient, err := blockchain.ConcurrentEVMClient(testNetwork, testEnvironment, chainClient, l) + require.NoError(t, err, "Error building concurrent chain client") + + cContractDeployer, err := contracts.NewContractDeployer(cEVMClient, l) + require.NoError(t, err, "Error building concurrent contract deployer") + + triggerContract, err := cContractDeployer.DeployLogEmitterContract() + require.NoError(t, err, "Error deploying log emitter contract") + triggerContracts = append(triggerContracts, triggerContract) + l.Debug(). + Str("Contract Address", triggerContract.Address().Hex()). + Int("Number", i+1). + Int("Out Of", numberOfUpkeeps). + Msg("Deployed Automation Log Trigger Emitter Contract") + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for contracts to deploy") + + for i, consumerContract := range consumerContracts { + logTriggerConfigStruct := automation_utils_2_1.LogTriggerConfig{ + ContractAddress: triggerContracts[i].Address(), + FilterSelector: 0, + Topic0: emitterABI.Events["Log1"].ID, + Topic1: bytes0, + Topic2: bytes0, + Topic3: bytes0, + } + encodedLogTriggerConfig, err := utilsABI.Methods["_logTriggerConfig"].Inputs.Pack(&logTriggerConfigStruct) + require.NoError(t, err, "Error encoding log trigger config") + l.Debug().Bytes("Encoded Log Trigger Config", encodedLogTriggerConfig).Msg("Encoded Log Trigger Config") + + registrationRequest, err := registrarABI.Pack( + "register", + fmt.Sprintf("LogTriggerUpkeep-%d", i), + []byte("test@mail.com"), + common.HexToAddress(consumerContract.Address()), + automationDefaultUpkeepGasLimit, + common.HexToAddress(chainClient.GetDefaultWallet().Address()), + uint8(1), + []byte("0"), + encodedLogTriggerConfig, + []byte("0"), + automationDefaultLinkFunds, + common.HexToAddress(chainClient.GetDefaultWallet().Address()), + ) + require.NoError(t, err, "Error encoding upkeep registration request") + tx, err := linkToken.TransferAndCall(registrar.Address(), automationDefaultLinkFunds, registrationRequest) + require.NoError(t, err, "Error sending upkeep registration request") + registrationTxHashes = append(registrationTxHashes, tx.Hash()) + } + + err = chainClient.WaitForEvents() + require.NoError(t, err, "Failed waiting for upkeeps to be registered") + + for _, txHash := range registrationTxHashes { + receipt, err := chainClient.GetTxReceipt(txHash) + require.NoError(t, err, "Registration tx should be completed") + var upkeepId *big.Int + for _, rawLog := range receipt.Logs { + parsedUpkeepId, err := registry.ParseUpkeepIdFromRegisteredLog(rawLog) + if err == nil { + upkeepId = parsedUpkeepId + break + } + } + require.NotNil(t, upkeepId, "Upkeep ID should be found after registration") + l.Debug(). + Str("TxHash", txHash.String()). + Str("Upkeep ID", upkeepId.String()). + Msg("Found upkeepId in tx hash") + upkeepIds = append(upkeepIds, upkeepId) + } + l.Info().Msg("Successfully registered all Automation Consumer Contracts") + l.Info().Interface("Upkeep IDs", upkeepIds).Msg("Upkeep IDs") + l.Info().Str("STARTUP_WAIT_TIME", StartupWaitTime.String()).Msg("Waiting for plugin to start") + time.Sleep(StartupWaitTime) + + startBlock, err := chainClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + + p := wasp.NewProfile() + + for i, triggerContract := range triggerContracts { + g, err := wasp.NewGenerator(&wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: fmt.Sprintf("log_trigger_gen_%s", triggerContract.Address().String()), + CallTimeout: time.Second * 10, + Schedule: wasp.Plain( + 1, + loadDuration, + ), + Gun: NewLogTriggerUser( + triggerContract, + consumerContracts[i], + l, + numberOfEvents, + ), + CallResultBufLen: 1000000, + }) + p.Add(g, err) + } + + l.Info().Msg("Starting load generators") + startTime := time.Now() + err = sendSlackNotification("Started", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + strconv.FormatInt(startTime.UnixMilli(), 10), "now", + []slack.Block{extraBlockWithText("\bTest Config\b\n```" + testConfig + "```")}) + if err != nil { + l.Error().Err(err).Msg("Error sending slack notification") + } + _, err = p.Run(true) + require.NoError(t, err, "Error running load generators") + + l.Info().Msg("Finished load generators") + l.Info().Str("STOP_WAIT_TIME", StopWaitTime.String()).Msg("Waiting for upkeeps to be performed") + time.Sleep(StopWaitTime) + l.Info().Msg("Finished waiting 60s for upkeeps to be performed") + endTime := time.Now() + testDuration := endTime.Sub(startTime) + l.Info().Str("Duration", testDuration.String()).Msg("Test Duration") + endBlock, err := chainClient.LatestBlockNumber(context.Background()) + require.NoError(t, err, "Error getting latest block number") + l.Info().Uint64("Starting Block", startBlock).Uint64("Ending Block", endBlock).Msg("Test Block Range") + + upkeepDelays := make([][]int64, 0) + var numberOfEventsEmitted int + var batchSize = 500 + + for _, gen := range p.Generators { + numberOfEventsEmitted += len(gen.GetData().OKData.Data) + } + numberOfEventsEmitted = numberOfEventsEmitted * numberOfEvents + l.Info().Int("Number of Events Emitted", numberOfEventsEmitted).Msg("Number of Events Emitted") + + if endBlock-startBlock < uint64(batchSize) { + batchSize = int(endBlock - startBlock) + } + + for cIter, consumerContract := range consumerContracts { + var ( + logs []types.Log + address = common.HexToAddress(consumerContract.Address()) + timeout = 5 * time.Second + ) + for fromBlock := startBlock; fromBlock < endBlock; fromBlock += uint64(batchSize) + 1 { + var ( + filterQuery = geth.FilterQuery{ + Addresses: []common.Address{address}, + FromBlock: big.NewInt(0).SetUint64(fromBlock), + ToBlock: big.NewInt(0).SetUint64(fromBlock + uint64(batchSize)), + Topics: [][]common.Hash{{consumerABI.Events["PerformingUpkeep"].ID}}, + } + ) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + logsInBatch, err := chainClient.FilterLogs(ctx, filterQuery) + cancel() + if err != nil { + l.Error().Err(err). + Interface("FilterQuery", filterQuery). + Str("Contract Address", consumerContract.Address()). + Str("Timeout", timeout.String()). + Msg("Error getting logs") + } + logs = append(logs, logsInBatch...) + time.Sleep(time.Millisecond * 500) + } + + if len(logs) > 0 { + delay := make([]int64, 0) + for _, log := range logs { + eventDetails, err := consumerABI.EventByID(log.Topics[0]) + require.NoError(t, err, "Error getting event details") + consumer, err := simple_log_upkeep_counter_wrapper.NewSimpleLogUpkeepCounter( + address, chainClient.Backend(), + ) + require.NoError(t, err, "Error getting consumer contract") + if eventDetails.Name == "PerformingUpkeep" { + parsedLog, err := consumer.ParsePerformingUpkeep(log) + require.NoError(t, err, "Error parsing log") + delay = append(delay, parsedLog.TimeToPerform.Int64()) + } + } + upkeepDelays = append(upkeepDelays, delay) + } + if (cIter+1)%batchSize == 0 { + time.Sleep(time.Millisecond * 500) + } + } + + l.Info().Interface("Upkeep Delays", upkeepDelays).Msg("Upkeep Delays") + + var allUpkeepDelays []int64 + + for _, upkeepDelay := range upkeepDelays { + allUpkeepDelays = append(allUpkeepDelays, upkeepDelay...) + } + + avg, median, ninetyPct, ninetyNinePct, maximum := testreporters.IntListStats(allUpkeepDelays) + l.Info(). + Float64("Average", avg).Int64("Median", median). + Int64("90th Percentile", ninetyPct).Int64("99th Percentile", ninetyNinePct). + Int64("Max", maximum).Msg("Upkeep Delays in seconds") + + l.Info(). + Int("Total Perform Count", len(allUpkeepDelays)). + Int("Total Events Emitted", numberOfEventsEmitted). + Int("Total Events Missed", numberOfEventsEmitted-len(allUpkeepDelays)). + Msg("Test completed") + + testReport := fmt.Sprintf("Upkeep Delays in seconds\nAverage: %f\nMedian: %d\n90th Percentile: %d\n"+ + "99th Percentile: %d\nMax: %d\nTotal Perform Count: %d\n\nTotal Events Emitted: %d\nTotal Events Missed: %d\nTest Duration: %s\n", + avg, median, ninetyPct, ninetyNinePct, maximum, len(allUpkeepDelays), numberOfEventsEmitted, + numberOfEventsEmitted-len(allUpkeepDelays), testDuration.String()) + + err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), + strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(endTime.UnixMilli(), 10), + []slack.Block{extraBlockWithText("\bTest Report\b\n```" + testReport + "```")}) + if err != nil { + l.Error().Err(err).Msg("Error sending slack notification") + } + + t.Cleanup(func() { + if err = actions.TeardownRemoteSuite(t, testEnvironment.Cfg.Namespace, chainlinkNodes, nil, chainClient); err != nil { + l.Error().Err(err).Msg("Error when tearing down remote suite") + } + }) + +} diff --git a/integration-tests/load/automationv2_1/gun.go b/integration-tests/load/automationv2_1/gun.go new file mode 100644 index 00000000000..a2863a6064b --- /dev/null +++ b/integration-tests/load/automationv2_1/gun.go @@ -0,0 +1,43 @@ +package automationv2_1 + +import ( + "github.com/rs/zerolog" + "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +type LogTriggerGun struct { + triggerContract contracts.LogEmitter + upkeepContract contracts.KeeperConsumer + logger zerolog.Logger + numberOfEvents int +} + +func NewLogTriggerUser( + triggerContract contracts.LogEmitter, + upkeepContract contracts.KeeperConsumer, + logger zerolog.Logger, + numberOfEvents int, +) *LogTriggerGun { + return &LogTriggerGun{ + triggerContract: triggerContract, + upkeepContract: upkeepContract, + logger: logger, + numberOfEvents: numberOfEvents, + } +} + +func (m *LogTriggerGun) Call(_ *wasp.Generator) *wasp.CallResult { + m.logger.Debug().Str("Trigger address", m.triggerContract.Address().String()).Msg("Triggering upkeep") + payload := make([]int, 0) + for i := 0; i < m.numberOfEvents; i++ { + payload = append(payload, 1) + } + _, err := m.triggerContract.EmitLogInts(payload) + if err != nil { + return &wasp.CallResult{Error: err.Error(), Failed: true} + } + + return &wasp.CallResult{} +} diff --git a/integration-tests/load/automationv2_1/helpers.go b/integration-tests/load/automationv2_1/helpers.go new file mode 100644 index 00000000000..3c08199a9cf --- /dev/null +++ b/integration-tests/load/automationv2_1/helpers.go @@ -0,0 +1,71 @@ +package automationv2_1 + +import ( + "fmt" + "os" + "strings" + + "github.com/rs/zerolog" + "github.com/slack-go/slack" + + "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" +) + +func getEnv(key, fallback string) string { + if inputs, ok := os.LookupEnv("TEST_INPUTS"); ok { + values := strings.Split(inputs, ",") + for _, value := range values { + if strings.Contains(value, key) { + return strings.Split(value, "=")[1] + } + } + } + return fallback +} + +func extraBlockWithText(text string) slack.Block { + return slack.NewSectionBlock(slack.NewTextBlockObject( + "mrkdwn", text, false, false), nil, nil) +} + +func sendSlackNotification(header string, l zerolog.Logger, namespace string, numberOfNodes, + startingTime string, endingTime string, extraBlocks []slack.Block) error { + slackClient := slack.New(reportModel.SlackAPIKey) + + headerText := ":chainlink-keepers: Automation Load Test " + header + " :white_check_mark:" + + formattedDashboardUrl := fmt.Sprintf("%s?orgId=1&from=%s&to=%s&var-namespace=%s&var-number_of_nodes=%s", testreporters.DashboardUrl, startingTime, endingTime, namespace, numberOfNodes) + l.Info().Str("Dashboard", formattedDashboardUrl).Msg("Dashboard URL") + + pyroscopeServer := os.Getenv(config.EnvVarPyroscopeServer) + pyroscopeEnvironment := os.Getenv(config.EnvVarPyroscopeEnvironment) + + formattedPyroscopeUrl := fmt.Sprintf("%s/?query=chainlink-node.cpu{Environment=\"%s\"}&from=%s&to=%s", pyroscopeServer, pyroscopeEnvironment, startingTime, endingTime) + + var notificationBlocks []slack.Block + + notificationBlocks = append(notificationBlocks, + slack.NewHeaderBlock(slack.NewTextBlockObject("plain_text", headerText, true, false))) + notificationBlocks = append(notificationBlocks, + slack.NewContextBlock("context_block", slack.NewTextBlockObject("plain_text", namespace, false, false))) + notificationBlocks = append(notificationBlocks, slack.NewDividerBlock()) + if pyroscopeServer != "" { + l.Info().Str("Pyroscope", formattedPyroscopeUrl).Msg("Dashboard URL") + notificationBlocks = append(notificationBlocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", + fmt.Sprintf("<%s|Pyroscope>", + formattedPyroscopeUrl), false, true), nil, nil)) + } + notificationBlocks = append(notificationBlocks, slack.NewSectionBlock(slack.NewTextBlockObject("mrkdwn", + fmt.Sprintf("<%s|Test Dashboard> \nNotifying <@%s>", + formattedDashboardUrl, reportModel.SlackUserID), false, true), nil, nil)) + + if len(extraBlocks) > 0 { + notificationBlocks = append(notificationBlocks, extraBlocks...) + } + + ts, err := reportModel.SendSlackMessage(slackClient, slack.MsgOptionBlocks(notificationBlocks...)) + l.Info().Str("ts", ts).Msg("Sent Slack Message") + return err +} diff --git a/integration-tests/testreporters/keeper_benchmark.go b/integration-tests/testreporters/keeper_benchmark.go index e9f2eaad7c5..5db6cca3ec4 100644 --- a/integration-tests/testreporters/keeper_benchmark.go +++ b/integration-tests/testreporters/keeper_benchmark.go @@ -133,7 +133,7 @@ func (k *KeeperBenchmarkTestReporter) WriteReport(folderLocation string) error { if err != nil { return err } - avg, median, ninetyPct, ninetyNinePct, max := intListStats(allDelays) + avg, median, ninetyPct, ninetyNinePct, max := IntListStats(allDelays) err = keeperReportWriter.Write([]string{ fmt.Sprint(totalEligibleCount), fmt.Sprint(totalPerformed), @@ -183,7 +183,7 @@ func (k *KeeperBenchmarkTestReporter) WriteReport(folderLocation string) error { } for contractIndex, report := range k.Reports { - avg, median, ninetyPct, ninetyNinePct, max = intListStats(report.AllCheckDelays) + avg, median, ninetyPct, ninetyNinePct, max = IntListStats(report.AllCheckDelays) err = keeperReportWriter.Write([]string{ fmt.Sprint(contractIndex), report.RegistryAddress, @@ -307,7 +307,7 @@ func (k *KeeperBenchmarkTestReporter) SendSlackNotification(t *testing.T, slackC // intListStats helper calculates some statistics on an int list: avg, median, 90pct, 99pct, max // //nolint:revive -func intListStats(in []int64) (float64, int64, int64, int64, int64) { +func IntListStats(in []int64) (float64, int64, int64, int64, int64) { length := len(in) if length == 0 { return 0, 0, 0, 0, 0 From 3ed1689b9c50083770c550f9b5e072b7e283df58 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 17 Nov 2023 10:59:03 -0500 Subject: [PATCH 169/271] Fixes Live Test Blocker (#11322) --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 99dd990bec5..aed5e0682cb 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd - github.com/smartcontractkit/chainlink-testing-framework v1.19.0 + github.com/smartcontractkit/chainlink-testing-framework v1.19.1 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0402b12f3f8..896ae8c6d27 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2375,8 +2375,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= -github.com/smartcontractkit/chainlink-testing-framework v1.19.0 h1:ungyY1gYcXCtmdX2yCon8pkx9HgPPLg4aNAhKNZFP5c= -github.com/smartcontractkit/chainlink-testing-framework v1.19.0/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.19.1 h1:MdGM5jIrBi858Cv7qzfl1Qon93YW8InohAlDQqFoIb4= +github.com/smartcontractkit/chainlink-testing-framework v1.19.1/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= From 738146e8d39eeaeef8e1479a7a2879b24cf7930d Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Fri, 17 Nov 2023 10:11:52 -0800 Subject: [PATCH 170/271] [Functions] Minor Listener refactor (#11323) 1. Add an interface type to make Listener mockable 2. Return internal errors from handleRequest() --- core/services/functions/listener.go | 74 +++++++++++-------- core/services/functions/listener_test.go | 21 +++++- .../functions/mocks/functions_listener.go | 71 ++++++++++++++++++ 3 files changed, 134 insertions(+), 32 deletions(-) create mode 100644 core/services/functions/mocks/functions_listener.go diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index 5614c5331d4..efb40330cb4 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -29,8 +29,6 @@ import ( ) var ( - _ job.ServiceCtx = &FunctionsListener{} - sizeBuckets = []float64{ 1024, 1024 * 4, @@ -118,7 +116,14 @@ const ( FlagSecretsMaxSize uint32 = 2 ) -type FunctionsListener struct { +//go:generate mockery --quiet --name FunctionsListener --output ./mocks/ --case=underscore +type FunctionsListener interface { + job.ServiceCtx + + HandleOffchainRequest(ctx context.Context, request *OffchainRequest) error +} + +type functionsListener struct { services.StateMachine client client.Client contractAddressHex string @@ -137,11 +142,13 @@ type FunctionsListener struct { logPollerWrapper evmrelayTypes.LogPollerWrapper } -func (l *FunctionsListener) HealthReport() map[string]error { +var _ FunctionsListener = &functionsListener{} + +func (l *functionsListener) HealthReport() map[string]error { return map[string]error{l.Name(): l.Healthy()} } -func (l *FunctionsListener) Name() string { return l.logger.Name() } +func (l *functionsListener) Name() string { return l.logger.Name() } func formatRequestId(requestId [32]byte) string { return fmt.Sprintf("0x%x", requestId) @@ -159,8 +166,8 @@ func NewFunctionsListener( urlsMonEndpoint commontypes.MonitoringEndpoint, decryptor threshold.Decryptor, logPollerWrapper evmrelayTypes.LogPollerWrapper, -) *FunctionsListener { - return &FunctionsListener{ +) *functionsListener { + return &functionsListener{ client: client, contractAddressHex: contractAddressHex, job: job, @@ -177,7 +184,7 @@ func NewFunctionsListener( } // Start complies with job.Service -func (l *FunctionsListener) Start(context.Context) error { +func (l *functionsListener) Start(context.Context) error { return l.StartOnce("FunctionsListener", func() error { l.serviceContext, l.serviceCancel = context.WithCancel(context.Background()) @@ -204,7 +211,7 @@ func (l *FunctionsListener) Start(context.Context) error { } // Close complies with job.Service -func (l *FunctionsListener) Close() error { +func (l *functionsListener) Close() error { return l.StopOnce("FunctionsListener", func() error { l.serviceCancel() close(l.chStop) @@ -213,7 +220,7 @@ func (l *FunctionsListener) Close() error { }) } -func (l *FunctionsListener) processOracleEventsV1() { +func (l *functionsListener) processOracleEventsV1() { defer l.shutdownWaitGroup.Done() freqMillis := l.pluginConfig.ListenerEventsCheckFrequencyMillis if freqMillis == 0 { @@ -247,7 +254,7 @@ func (l *FunctionsListener) processOracleEventsV1() { } } -func (l *FunctionsListener) getNewHandlerContext() (context.Context, context.CancelFunc) { +func (l *functionsListener) getNewHandlerContext() (context.Context, context.CancelFunc) { timeoutSec := l.pluginConfig.ListenerEventHandlerTimeoutSec if timeoutSec == 0 { return context.WithCancel(l.serviceContext) @@ -255,7 +262,7 @@ func (l *FunctionsListener) getNewHandlerContext() (context.Context, context.Can return context.WithTimeout(l.serviceContext, time.Duration(timeoutSec)*time.Second) } -func (l *FunctionsListener) setError(ctx context.Context, requestId RequestID, errType ErrType, errBytes []byte) { +func (l *functionsListener) setError(ctx context.Context, requestId RequestID, errType ErrType, errBytes []byte) { if errType == INTERNAL_ERROR { promRequestInternalError.WithLabelValues(l.contractAddressHex).Inc() } else { @@ -267,7 +274,7 @@ func (l *FunctionsListener) setError(ctx context.Context, requestId RequestID, e } } -func (l *FunctionsListener) getMaxCBORsize(flags RequestFlags) uint32 { +func (l *functionsListener) getMaxCBORsize(flags RequestFlags) uint32 { idx := flags[FlagCBORMaxSize] if int(idx) >= len(l.pluginConfig.MaxRequestSizesList) { return l.pluginConfig.MaxRequestSizeBytes // deprecated @@ -275,7 +282,7 @@ func (l *FunctionsListener) getMaxCBORsize(flags RequestFlags) uint32 { return l.pluginConfig.MaxRequestSizesList[idx] } -func (l *FunctionsListener) getMaxSecretsSize(flags RequestFlags) uint32 { +func (l *functionsListener) getMaxSecretsSize(flags RequestFlags) uint32 { idx := flags[FlagSecretsMaxSize] if int(idx) >= len(l.pluginConfig.MaxSecretsSizesList) { return math.MaxUint32 // not enforced if not configured @@ -283,7 +290,7 @@ func (l *FunctionsListener) getMaxSecretsSize(flags RequestFlags) uint32 { return l.pluginConfig.MaxSecretsSizesList[idx] } -func (l *FunctionsListener) HandleOffchainRequest(ctx context.Context, request *OffchainRequest) error { +func (l *functionsListener) HandleOffchainRequest(ctx context.Context, request *OffchainRequest) error { if request == nil { return errors.New("HandleOffchainRequest: received nil request") } @@ -318,11 +325,10 @@ func (l *FunctionsListener) HandleOffchainRequest(ctx context.Context, request * } return err } - l.handleRequest(ctx, requestId, request.SubscriptionId, subscriptionOwner, RequestFlags{}, &request.Data) - return nil + return l.handleRequest(ctx, requestId, request.SubscriptionId, subscriptionOwner, RequestFlags{}, &request.Data) } -func (l *FunctionsListener) handleOracleRequestV1(request *evmrelayTypes.OracleRequest) { +func (l *functionsListener) handleOracleRequestV1(request *evmrelayTypes.OracleRequest) { defer l.shutdownWaitGroup.Done() l.logger.Infow("handleOracleRequestV1: oracle request v1 received", "requestID", formatRequestId(request.RequestId)) ctx, cancel := l.getNewHandlerContext() @@ -354,10 +360,13 @@ func (l *FunctionsListener) handleOracleRequestV1(request *evmrelayTypes.OracleR l.setError(ctx, request.RequestId, USER_ERROR, []byte(err.Error())) return } - l.handleRequest(ctx, request.RequestId, request.SubscriptionId, request.SubscriptionOwner, request.Flags, requestData) + err = l.handleRequest(ctx, request.RequestId, request.SubscriptionId, request.SubscriptionOwner, request.Flags, requestData) + if err != nil { + l.logger.Errorw("handleOracleRequestV1: error in handleRequest()", "requestID", formatRequestId(request.RequestId), "err", err) + } } -func (l *FunctionsListener) parseCBOR(requestId RequestID, cborData []byte, maxSizeBytes uint32) (*RequestData, error) { +func (l *functionsListener) parseCBOR(requestId RequestID, cborData []byte, maxSizeBytes uint32) (*RequestData, error) { if maxSizeBytes > 0 && uint32(len(cborData)) > maxSizeBytes { l.logger.Errorw("request too big", "requestID", formatRequestId(requestId), "requestSize", len(cborData), "maxRequestSize", maxSizeBytes) return nil, fmt.Errorf("request too big (max %d bytes)", maxSizeBytes) @@ -372,7 +381,8 @@ func (l *FunctionsListener) parseCBOR(requestId RequestID, cborData []byte, maxS return &requestData, nil } -func (l *FunctionsListener) handleRequest(ctx context.Context, requestID RequestID, subscriptionId uint64, subscriptionOwner common.Address, flags RequestFlags, requestData *RequestData) { +// Handle secret fetching/decryption and functions computation. Return error only for internal errors. +func (l *functionsListener) handleRequest(ctx context.Context, requestID RequestID, subscriptionId uint64, subscriptionOwner common.Address, flags RequestFlags, requestData *RequestData) error { startTime := time.Now() defer func() { duration := time.Since(startTime) @@ -385,26 +395,26 @@ func (l *FunctionsListener) handleRequest(ctx context.Context, requestID Request if err != nil { l.logger.Errorw("failed to create ExternalAdapterClient", "requestID", requestIDStr, "err", err) l.setError(ctx, requestID, INTERNAL_ERROR, []byte(err.Error())) - return + return err } nodeProvidedSecrets, userErr, internalErr := l.getSecrets(ctx, eaClient, requestID, subscriptionOwner, requestData) if internalErr != nil { l.logger.Errorw("internal error during getSecrets", "requestID", requestIDStr, "err", internalErr) l.setError(ctx, requestID, INTERNAL_ERROR, []byte(internalErr.Error())) - return + return internalErr } if userErr != nil { l.logger.Debugw("user error during getSecrets", "requestID", requestIDStr, "err", userErr) l.setError(ctx, requestID, USER_ERROR, []byte(userErr.Error())) - return + return nil // user error } maxSecretsSize := l.getMaxSecretsSize(flags) if uint32(len(nodeProvidedSecrets)) > maxSecretsSize { l.logger.Errorw("secrets size too big", "requestID", requestIDStr, "secretsSize", len(nodeProvidedSecrets), "maxSecretsSize", maxSecretsSize) l.setError(ctx, requestID, USER_ERROR, []byte("secrets size too big")) - return + return nil // user error } computationResult, computationError, domains, err := eaClient.RunComputation(ctx, requestIDStr, l.job.Name.ValueOrZero(), subscriptionOwner.Hex(), subscriptionId, flags, nodeProvidedSecrets, requestData) @@ -412,7 +422,7 @@ func (l *FunctionsListener) handleRequest(ctx context.Context, requestID Request if err != nil { l.logger.Errorw("internal adapter error", "requestID", requestIDStr, "err", err) l.setError(ctx, requestID, INTERNAL_ERROR, []byte(err.Error())) - return + return err } if len(computationError) == 0 && len(computationResult) == 0 { @@ -438,11 +448,13 @@ func (l *FunctionsListener) handleRequest(ctx context.Context, requestID Request l.logger.Debugw("saving computation result", "requestID", requestIDStr) if err2 := l.pluginORM.SetResult(requestID, computationResult, time.Now(), pg.WithParentCtx(ctx)); err2 != nil { l.logger.Errorw("call to SetResult failed", "requestID", requestIDStr, "err", err2) + return err2 } } + return nil } -func (l *FunctionsListener) handleOracleResponseV1(response *evmrelayTypes.OracleResponse) { +func (l *functionsListener) handleOracleResponseV1(response *evmrelayTypes.OracleResponse) { defer l.shutdownWaitGroup.Done() l.logger.Infow("oracle response v1 received", "requestID", formatRequestId(response.RequestId)) @@ -454,7 +466,7 @@ func (l *FunctionsListener) handleOracleResponseV1(response *evmrelayTypes.Oracl promRequestConfirmed.WithLabelValues(l.contractAddressHex).Inc() } -func (l *FunctionsListener) timeoutRequests() { +func (l *functionsListener) timeoutRequests() { defer l.shutdownWaitGroup.Done() timeoutSec, freqSec, batchSize := l.pluginConfig.RequestTimeoutSec, l.pluginConfig.RequestTimeoutCheckFrequencySec, l.pluginConfig.RequestTimeoutBatchLookupSize if timeoutSec == 0 || freqSec == 0 || batchSize == 0 { @@ -490,7 +502,7 @@ func (l *FunctionsListener) timeoutRequests() { } } -func (l *FunctionsListener) pruneRequests() { +func (l *functionsListener) pruneRequests() { defer l.shutdownWaitGroup.Done() maxStoredRequests, freqSec, batchSize := l.pluginConfig.PruneMaxStoredRequests, l.pluginConfig.PruneCheckFrequencySec, l.pluginConfig.PruneBatchSize if maxStoredRequests == 0 { @@ -532,7 +544,7 @@ func (l *FunctionsListener) pruneRequests() { } } -func (l *FunctionsListener) reportSourceCodeDomains(requestId RequestID, domains []string) { +func (l *functionsListener) reportSourceCodeDomains(requestId RequestID, domains []string) { r := &telem.FunctionsRequest{ RequestId: formatRequestId(requestId), NodeAddress: l.job.OCR2OracleSpec.TransmitterID.ValueOrZero(), @@ -547,7 +559,7 @@ func (l *FunctionsListener) reportSourceCodeDomains(requestId RequestID, domains } } -func (l *FunctionsListener) getSecrets(ctx context.Context, eaClient ExternalAdapterClient, requestID RequestID, subscriptionOwner common.Address, requestData *RequestData) (decryptedSecrets string, userError, internalError error) { +func (l *functionsListener) getSecrets(ctx context.Context, eaClient ExternalAdapterClient, requestID RequestID, subscriptionOwner common.Address, requestData *RequestData) (decryptedSecrets string, userError, internalError error) { if l.decryptor == nil { l.logger.Warn("Decryptor not configured") return "", nil, nil diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index ac2bc64184d..ecad9e4cceb 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -46,7 +46,7 @@ import ( ) type FunctionsListenerUniverse struct { - service *functions_service.FunctionsListener + service functions_service.FunctionsListener bridgeAccessor *functions_mocks.BridgeAccessor eaClient *functions_mocks.ExternalAdapterClient pluginORM *functions_mocks.ORM @@ -219,6 +219,25 @@ func TestFunctionsListener_HandleOffchainRequest_Invalid(t *testing.T) { require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) } +func TestFunctionsListener_HandleOffchainRequest_InternalError(t *testing.T) { + testutils.SkipShortDB(t) + t.Parallel() + uni := NewFunctionsListenerUniverse(t, 0, 1_000_000) + uni.pluginORM.On("CreateRequest", mock.Anything, mock.Anything).Return(nil) + uni.bridgeAccessor.On("NewExternalAdapterClient").Return(uni.eaClient, nil) + uni.eaClient.On("RunComputation", mock.Anything, RequestIDStr, mock.Anything, SubscriptionOwner.Hex(), SubscriptionID, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil, nil, errors.New("error")) + uni.pluginORM.On("SetError", RequestID, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + request := &functions_service.OffchainRequest{ + RequestId: RequestID[:], + RequestInitiator: SubscriptionOwner.Bytes(), + SubscriptionId: uint64(SubscriptionID), + SubscriptionOwner: SubscriptionOwner.Bytes(), + Data: functions_service.RequestData{}, + } + require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) +} + func TestFunctionsListener_HandleOracleRequestV1_ComputationError(t *testing.T) { testutils.SkipShortDB(t) t.Parallel() diff --git a/core/services/functions/mocks/functions_listener.go b/core/services/functions/mocks/functions_listener.go new file mode 100644 index 00000000000..d2aeb2ddab8 --- /dev/null +++ b/core/services/functions/mocks/functions_listener.go @@ -0,0 +1,71 @@ +// Code generated by mockery v2.35.4. DO NOT EDIT. + +package mocks + +import ( + context "context" + + functions "github.com/smartcontractkit/chainlink/v2/core/services/functions" + mock "github.com/stretchr/testify/mock" +) + +// FunctionsListener is an autogenerated mock type for the FunctionsListener type +type FunctionsListener struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *FunctionsListener) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// HandleOffchainRequest provides a mock function with given fields: ctx, request +func (_m *FunctionsListener) HandleOffchainRequest(ctx context.Context, request *functions.OffchainRequest) error { + ret := _m.Called(ctx, request) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *functions.OffchainRequest) error); ok { + r0 = rf(ctx, request) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Start provides a mock function with given fields: _a0 +func (_m *FunctionsListener) Start(_a0 context.Context) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewFunctionsListener creates a new instance of FunctionsListener. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFunctionsListener(t interface { + mock.TestingT + Cleanup(func()) +}) *FunctionsListener { + mock := &FunctionsListener{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 5f09e55075e98e8863fe686f41682d4feca268b5 Mon Sep 17 00:00:00 2001 From: Tate Date: Fri, 17 Nov 2023 12:10:28 -0700 Subject: [PATCH 171/271] [TT-707] Build Test Base Image As Needed in CI (#11329) --- .github/actions/build-test-image/action.yml | 62 +++++++++++++++++-- .../workflows/integration-tests-publish.yml | 20 ++---- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index 4b1dce6ee15..e6c759109be 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -17,9 +17,6 @@ inputs: description: The test suites to build into the image default: chaos migration performance reorg smoke soak benchmark load/automationv2_1 required: false - base_image_tag: - description: The test base image version to use, if not provided it will use the version from the ./integration-tests/go.mod file - required: false QA_AWS_ROLE_TO_ASSUME: description: The AWS role to assume as the CD user, if any. Used in configuring the docker/login-action required: true @@ -33,14 +30,66 @@ inputs: runs: using: composite steps: + + # Base Test Image Logic - name: Get CTF Version - if: ${{ inputs.base_image_tag == '' }} id: version uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: go-project-path: ./integration-tests module-name: github.com/smartcontractkit/chainlink-testing-framework - enforce-semantic-tag: "true" # it has to be in the form of v1.2.3 or the image won't exist + enforce-semantic-tag: false + - name: Get CTF sha + if: steps.version.outputs.is_semantic == 'false' + id: short_sha + env: + VERSION: ${{ steps.version.outputs.version }} + shell: bash + run: | + short_sha="${VERSION##*-}" + echo "short sha is: ${short_sha}" + echo "short_sha=${short_sha}" >> "$GITHUB_OUTPUT" + - name: Checkout chainlink-testing-framework + if: steps.version.outputs.is_semantic == 'false' + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + repository: smartcontractkit/chainlink-testing-framework + ref: main + fetch-depth: 0 + path: ctf + - name: Get long sha + if: steps.version.outputs.is_semantic == 'false' + id: long_sha + env: + SHORT_SHA: ${{ steps.short_sha.outputs.short_sha }} + shell: bash + run: | + cd ctf + long_sha=$(git rev-parse ${SHORT_SHA}) + echo "sha is: ${long_sha}" + echo "long_sha=${long_sha}" >> "$GITHUB_OUTPUT" + - name: Check if test base image exists + if: steps.version.outputs.is_semantic == 'false' + id: check-base-image + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@00c6214deb10a3f374c6d3430c32c5202015d463 # v2.2.12 + with: + repository: ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image + tag: ${{ steps.long_sha.outputs.long_sha }} + AWS_REGION: ${{ inputs.QA_AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} + - name: Build Base Image + if: steps.version.outputs.is_semantic == 'false' && steps.check-base-image.outputs.exists == 'false' + uses: smartcontractkit/chainlink-github-actions/docker/build-push@ce87f8986ca18336cc5015df75916c2ec0a7c4b3 # v2.1.2 + env: + BASE_IMAGE_NAME: ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image:${{ steps.long_sha.outputs.long_sha }} + with: + tags: ${{ env.BASE_IMAGE_NAME }} + file: ctf/k8s/Dockerfile.base + AWS_REGION: ${{ inputs.QA_AWS_REGION }} + AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} + # End Base Image Logic + + # Test Runner Logic - name: Check if image exists id: check-image uses: smartcontractkit/chainlink-github-actions/docker/image-exists@00c6214deb10a3f374c6d3430c32c5202015d463 # v2.2.12 @@ -59,7 +108,7 @@ runs: file: ./integration-tests/test.Dockerfile build-args: | BASE_IMAGE=${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image - IMAGE_VERSION=${{ inputs.base_image_tag || steps.version.outputs.version }} + IMAGE_VERSION=${{ steps.long_sha.outputs.long_sha || steps.version.outputs.version }} SUITES="${{ inputs.suites }}" AWS_REGION: ${{ inputs.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} @@ -71,3 +120,4 @@ runs: run: | echo "### ${INPUTS_REPOSITORY} image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY echo "\`${INPUTS_TAG}\`" >>$GITHUB_STEP_SUMMARY + # End Test Runner Logic diff --git a/.github/workflows/integration-tests-publish.yml b/.github/workflows/integration-tests-publish.yml index 9eb2673db2d..176947a092d 100644 --- a/.github/workflows/integration-tests-publish.yml +++ b/.github/workflows/integration-tests-publish.yml @@ -6,13 +6,6 @@ on: branches: - develop workflow_dispatch: - inputs: - ctf-base-image-tag: - description: | - 'The tag of the CTF base image to be used, - typically something like v1.18.6 from https://github.com/smartcontractkit/chainlink-testing-framework/releases - or a custom tag or branch you have pushed.' - required: true env: ECR_TAG: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-tests:develop @@ -39,20 +32,15 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Setup Other Tags + - name: Setup Other Tags If Not Workflow Dispatch id: tags - env: - BASE_IMAGE_TAG: ${{ inputs.ctf-base-image-tag}} + if: github.event_name != 'workflow_dispatch' run: | - if [ -z "${BASE_IMAGE_TAG+x}" ]; then - echo "ctf-base-image-tag is not set, we are part of a merge and want to push the develop tag" - echo "other_tags=${ECR_TAG}" >> $GITHUB_OUTPUT - fi + echo "other_tags=${ECR_TAG}" >> $GITHUB_OUTPUT - name: Build Image uses: ./.github/actions/build-test-image with: other_tags: ${{ steps.tags.outputs.other_tags }} - base_image_tag: ${{ inputs.ctf-base-image-tag }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} @@ -68,7 +56,7 @@ jobs: build-chainlink-image: environment: integration # Only run this build for workflow_dispatch - if: ${{ inputs.ctf-base-image-tag != '' }} + if: github.event_name == 'workflow_dispatch' permissions: id-token: write contents: read From b2c1a17cf529317d70674aa9f73596a4a426f418 Mon Sep 17 00:00:00 2001 From: JonWong203 <82334955+JonWong203@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:19:54 -0500 Subject: [PATCH 172/271] Automation Telemetry: Send BlockNumber and Node Version (#9927) * rebase on toml feature flag * fixes + send node version rebase on feature flag PR * fixes * comments * minor changes * lint * constant added * logger * toggle by feature flag * minor fixes * marshal error exits start() * marshal error fix * add automation custom telem to job * rename toml tag * test * removed print statement * merge * configDigest go routine getter * configDigest var * refactor1 * send NodeVersion msg every new ConfigDigest * lint * refactor * move registry creation outside delegate.go * plugin config bool flag * custom telem for 2.1 * block subscriber + thread controller * ContractConfigTracker * reset forge-std * use toml config flag * set toml flag default to true * plugin config bool flag * make generate * refactor * hourly node version msg * fix tests * lint fix * goimports fixed * goimport fix 2 --------- Co-authored-by: FelixFan1992 --- core/config/docs/core.toml | 2 +- core/services/chainlink/config_ocr2_test.go | 2 +- core/services/chainlink/config_test.go | 4 +- .../testdata/config-empty-effective.toml | 2 +- .../chainlink/testdata/config-full.toml | 2 +- .../config-multi-chain-effective.toml | 2 +- core/services/ocr2/delegate.go | 23 ++- .../ocr2/plugins/ocr2keeper/config.go | 2 + .../plugins/ocr2keeper/custom_telemetry.go | 156 +++++++++++++++++ .../ocr2keeper/custom_telemetry_test.go | 17 ++ .../evm21/autotelemetry21/custom_telemetry.go | 160 ++++++++++++++++++ .../autotelemetry21/custom_telemetry_test.go | 56 ++++++ .../testdata/config-empty-effective.toml | 2 +- core/web/resolver/testdata/config-full.toml | 2 +- .../config-multi-chain-effective.toml | 2 +- docs/CONFIG.md | 4 +- testdata/scripts/node/validate/default.txtar | 2 +- .../disk-based-logging-disabled.txtar | 2 +- .../validate/disk-based-logging-no-dir.txtar | 2 +- .../node/validate/disk-based-logging.txtar | 2 +- testdata/scripts/node/validate/invalid.txtar | 2 +- testdata/scripts/node/validate/valid.txtar | 2 +- testdata/scripts/node/validate/warnings.txtar | 2 +- 23 files changed, 431 insertions(+), 21 deletions(-) create mode 100644 core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go create mode 100644 core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 0a8e6aba3be..148de90cd95 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -352,7 +352,7 @@ KeyBundleID = '7a5f66bbe6594259325bf2b4f5b1a9c900000000000000000000000000000000' # CaptureEATelemetry toggles collecting extra information from External Adaptares CaptureEATelemetry = false # Default # CaptureAutomationCustomTelemetry toggles collecting automation specific telemetry -CaptureAutomationCustomTelemetry = false # Default +CaptureAutomationCustomTelemetry = true # Default # DefaultTransactionQueueDepth controls the queue size for `DropOldestStrategy` in OCR2. Set to 0 to use `SendEvery` strategy instead. DefaultTransactionQueueDepth = 1 # Default # SimulateTransactions enables transaction simulation for OCR2. diff --git a/core/services/chainlink/config_ocr2_test.go b/core/services/chainlink/config_ocr2_test.go index 66427489241..5bf84934d13 100644 --- a/core/services/chainlink/config_ocr2_test.go +++ b/core/services/chainlink/config_ocr2_test.go @@ -38,7 +38,7 @@ func TestOCR2Config(t *testing.T) { require.Equal(t, false, ocr2Cfg.TraceLogging()) require.Equal(t, uint32(1), ocr2Cfg.DefaultTransactionQueueDepth()) require.Equal(t, false, ocr2Cfg.CaptureEATelemetry()) - require.Equal(t, false, ocr2Cfg.CaptureAutomationCustomTelemetry()) + require.Equal(t, true, ocr2Cfg.CaptureAutomationCustomTelemetry()) keyBundleID, err := ocr2Cfg.KeyBundleID() require.NoError(t, err) diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 0caae3607f7..fbadb379cad 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -385,7 +385,7 @@ func TestConfig_Marshal(t *testing.T) { DatabaseTimeout: models.MustNewDuration(8 * time.Second), KeyBundleID: ptr(models.MustSha256HashFromHex("7a5f66bbe6594259325bf2b4f5b1a9c9")), CaptureEATelemetry: ptr(false), - CaptureAutomationCustomTelemetry: ptr(false), + CaptureAutomationCustomTelemetry: ptr(true), DefaultTransactionQueueDepth: ptr[uint32](1), SimulateTransactions: ptr(false), TraceLogging: ptr(false), @@ -848,7 +848,7 @@ ContractTransmitterTransmitTimeout = '1m0s' DatabaseTimeout = '8s' KeyBundleID = '7a5f66bbe6594259325bf2b4f5b1a9c900000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index f5d775fe744..b897fba7f10 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -137,7 +137,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 5ede10ef695..531c98d7344 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -143,7 +143,7 @@ ContractTransmitterTransmitTimeout = '1m0s' DatabaseTimeout = '8s' KeyBundleID = '7a5f66bbe6594259325bf2b4f5b1a9c900000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 9dd0be8f5d2..c743601ced8 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -137,7 +137,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '20s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 20c13512978..944c04c8d44 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -49,6 +49,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21" ocr2keeper21core "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ocr2vrfconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/config" ocr2coordinator "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/coordinator" @@ -174,6 +175,7 @@ type ocr2Config interface { DatabaseTimeout() time.Duration KeyBundleID() (string, error) TraceLogging() bool + CaptureAutomationCustomTelemetry() bool } type insecureConfig interface { @@ -1161,7 +1163,7 @@ func (d *Delegate) newServicesOCR2Keepers21( d.cfg.JobPipeline().MaxSuccessfulRuns(), ) - return []job.ServiceCtx{ + automationServices := []job.ServiceCtx{ runResultSaver, keeperProvider, services.Registry(), @@ -1171,7 +1173,24 @@ func (d *Delegate) newServicesOCR2Keepers21( services.UpkeepStateStore(), services.TransmitEventProvider(), pluginService, - }, nil + } + + if cfg.CaptureAutomationCustomTelemetry != nil && *cfg.CaptureAutomationCustomTelemetry || + cfg.CaptureAutomationCustomTelemetry == nil && d.cfg.OCR2().CaptureAutomationCustomTelemetry() { + endpoint := d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.AutomationCustom) + customTelemService, custErr := autotelemetry21.NewAutomationCustomTelemetryService( + endpoint, + lggr, + services.BlockSubscriber(), + keeperProvider.ContractConfigTracker(), + ) + if custErr != nil { + return nil, errors.Wrap(custErr, "Error when creating AutomationCustomTelemetryService") + } + automationServices = append(automationServices, customTelemService) + } + + return automationServices, nil } func (d *Delegate) newServicesOCR2Keepers20( diff --git a/core/services/ocr2/plugins/ocr2keeper/config.go b/core/services/ocr2/plugins/ocr2keeper/config.go index d3035878ece..ec56f9c6993 100644 --- a/core/services/ocr2/plugins/ocr2keeper/config.go +++ b/core/services/ocr2/plugins/ocr2keeper/config.go @@ -58,6 +58,8 @@ type PluginConfig struct { ServiceQueueLength int `json:"serviceQueueLength"` // ContractVersion is the contract version ContractVersion string `json:"contractVersion"` + // CaptureAutomationCustomTelemetry is a bool flag to toggle Custom Telemetry Service + CaptureAutomationCustomTelemetry *bool `json:"captureAutomationCustomTelemetry,omitempty"` } func ValidatePluginConfig(cfg PluginConfig) error { diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go new file mode 100644 index 00000000000..0f03ae5bd06 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go @@ -0,0 +1,156 @@ +package ocr2keeper + +import ( + "context" + "encoding/hex" + "time" + + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "google.golang.org/protobuf/proto" + + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" + "github.com/smartcontractkit/chainlink/v2/core/static" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type AutomationCustomTelemetryService struct { + utils.StartStopOnce + monitoringEndpoint commontypes.MonitoringEndpoint + blockSubscriber *evm21.BlockSubscriber + blockSubChanID int + threadCtrl utils.ThreadControl + lggr logger.Logger + configDigest [32]byte + contractConfigTracker types.ContractConfigTracker +} + +// NewAutomationCustomTelemetryService creates a telemetry service for new blocks and node version +func NewAutomationCustomTelemetryService(me commontypes.MonitoringEndpoint, + lggr logger.Logger, blocksub *evm21.BlockSubscriber, configTracker types.ContractConfigTracker) (*AutomationCustomTelemetryService, error) { + return &AutomationCustomTelemetryService{ + monitoringEndpoint: me, + threadCtrl: utils.NewThreadControl(), + lggr: lggr.Named("AutomationCustomTelem"), + contractConfigTracker: configTracker, + blockSubscriber: blocksub, + }, nil +} + +// Start starts Custom Telemetry Service, sends 1 NodeVersion message to endpoint at start and sends new BlockNumber messages +func (e *AutomationCustomTelemetryService) Start(ctx context.Context) error { + return e.StartOnce("AutomationCustomTelemetryService", func() error { + e.lggr.Infof("Starting: Custom Telemetry Service") + _, configDetails, err := e.contractConfigTracker.LatestConfigDetails(ctx) + if err != nil { + e.lggr.Errorf("Error occurred while getting newestConfigDetails for initialization %s", err) + } else { + e.configDigest = configDetails + e.sendNodeVersionMsg() + } + e.threadCtrl.Go(func(ctx context.Context) { + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + for { + select { + case <-ticker.C: + _, newConfigDigest, err := e.contractConfigTracker.LatestConfigDetails(ctx) + if err != nil { + e.lggr.Errorf("Error occurred while getting newestConfigDetails in configDigest loop %s", err) + } + if newConfigDigest != e.configDigest { + e.configDigest = newConfigDigest + e.sendNodeVersionMsg() + } + case <-ctx.Done(): + return + } + } + }) + + chanID, blockSubscriberChan, blockSubErr := e.blockSubscriber.Subscribe() + if blockSubErr != nil { + e.lggr.Errorf("Block Subscriber Error: Subscribe(): %s", blockSubErr) + + } else { + e.blockSubChanID = chanID + e.threadCtrl.Go(func(ctx context.Context) { + e.lggr.Infof("Started: Sending BlockNumber Messages") + for { + select { + case blockHistory := <-blockSubscriberChan: + latestBlockKey, err := blockHistory.Latest() + if err != nil { + e.lggr.Errorf("BlockSubscriber BlockHistory.Latest() failed: %s", err) + continue + } + e.sendBlockNumberMsg(latestBlockKey) + case <-ctx.Done(): + return + } + } + }) + } + return nil + }) +} + +// Close stops go routines and closes channels +func (e *AutomationCustomTelemetryService) Close() error { + // use utils + return e.StopOnce("AutomationCustomTelemetryService", func() error { + e.lggr.Infof("Stopping: custom telemetry service") + e.threadCtrl.Close() + err := e.blockSubscriber.Unsubscribe(e.blockSubChanID) + if err != nil { + return err + } + e.lggr.Infof("Stopped: Custom telemetry service") + return nil + }) +} + +func (e *AutomationCustomTelemetryService) sendNodeVersionMsg() { + vMsg := &telem.NodeVersion{ + Timestamp: uint64(time.Now().UTC().UnixMilli()), + NodeVersion: static.Version, + ConfigDigest: e.configDigest[:], + } + wrappedVMsg := &telem.AutomationTelemWrapper{ + Msg: &telem.AutomationTelemWrapper_NodeVersion{ + NodeVersion: vMsg, + }, + } + bytes, err := proto.Marshal(wrappedVMsg) + if err != nil { + e.lggr.Errorf("Error occurred while marshalling the Node Version Message %s: %v", wrappedVMsg.String(), err) + } else { + e.monitoringEndpoint.SendLog(bytes) + e.lggr.Infof("NodeVersion Message Sent to Endpoint: %d", vMsg.Timestamp) + } +} + +func (e *AutomationCustomTelemetryService) sendBlockNumberMsg(blockKey ocr2keepers.BlockKey) { + blockNumMsg := &telem.BlockNumber{ + Timestamp: uint64(time.Now().UTC().UnixMilli()), + BlockNumber: uint64(blockKey.Number), + BlockHash: hex.EncodeToString(blockKey.Hash[:]), + ConfigDigest: e.configDigest[:], + } + wrappedBlockNumMsg := &telem.AutomationTelemWrapper{ + Msg: &telem.AutomationTelemWrapper_BlockNumber{ + BlockNumber: blockNumMsg, + }, + } + b, err := proto.Marshal(wrappedBlockNumMsg) + if err != nil { + e.lggr.Errorf("Error occurred while marshalling the Block Num Message %s: %v", wrappedBlockNumMsg.String(), err) + } else { + e.monitoringEndpoint.SendLog(b) + e.lggr.Infof("BlockNumber Message Sent to Endpoint: %d", blockNumMsg.Timestamp) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go new file mode 100644 index 00000000000..a40a3f3525d --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go @@ -0,0 +1,17 @@ +package ocr2keeper + +import ( + "testing" +) + +func TestNewAutomationCustomTelemetryService(t *testing.T) { + // me := &MockMonitoringEndpoint{} + // lggr := &MockLogger{} + // blocksub := &MockBlockSubscriber{} + // configTracker := &MockContractConfigTracker{} + + // service, err := NewAutomationCustomTelemetryService(me, lggr, blocksub, configTracker) + // if err != nil { + // t.Errorf("Expected no error, but got: %v", err) + // } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go new file mode 100644 index 00000000000..93f35ce0d24 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go @@ -0,0 +1,160 @@ +package autotelemetry21 + +import ( + "context" + "encoding/hex" + "time" + + "github.com/smartcontractkit/libocr/commontypes" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "google.golang.org/protobuf/proto" + + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + "github.com/smartcontractkit/chainlink/v2/core/logger" + evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" + "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" + "github.com/smartcontractkit/chainlink/v2/core/static" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type AutomationCustomTelemetryService struct { + utils.StartStopOnce + monitoringEndpoint commontypes.MonitoringEndpoint + blockSubscriber *evm21.BlockSubscriber + blockSubChanID int + threadCtrl utils.ThreadControl + lggr logger.Logger + configDigest [32]byte + contractConfigTracker types.ContractConfigTracker +} + +// NewAutomationCustomTelemetryService creates a telemetry service for new blocks and node version +func NewAutomationCustomTelemetryService(me commontypes.MonitoringEndpoint, + lggr logger.Logger, blocksub *evm21.BlockSubscriber, configTracker types.ContractConfigTracker) (*AutomationCustomTelemetryService, error) { + return &AutomationCustomTelemetryService{ + monitoringEndpoint: me, + threadCtrl: utils.NewThreadControl(), + lggr: lggr.Named("AutomationCustomTelem"), + contractConfigTracker: configTracker, + blockSubscriber: blocksub, + }, nil +} + +// Start starts Custom Telemetry Service, sends 1 NodeVersion message to endpoint at start and sends new BlockNumber messages +func (e *AutomationCustomTelemetryService) Start(ctx context.Context) error { + return e.StartOnce("AutomationCustomTelemetryService", func() error { + e.lggr.Infof("Starting: Custom Telemetry Service") + _, configDetails, err := e.contractConfigTracker.LatestConfigDetails(ctx) + if err != nil { + e.lggr.Errorf("Error occurred while getting newestConfigDetails for initialization %s", err) + } else { + e.configDigest = configDetails + e.sendNodeVersionMsg() + } + e.threadCtrl.Go(func(ctx context.Context) { + minuteTicker := time.NewTicker(1 * time.Minute) + hourTicker := time.NewTicker(1 * time.Hour) + defer minuteTicker.Stop() + defer hourTicker.Stop() + for { + select { + case <-minuteTicker.C: + _, newConfigDigest, err := e.contractConfigTracker.LatestConfigDetails(ctx) + if err != nil { + e.lggr.Errorf("Error occurred while getting newestConfigDetails in configDigest loop %s", err) + } + if newConfigDigest != e.configDigest { + e.configDigest = newConfigDigest + e.sendNodeVersionMsg() + } + case <-hourTicker.C: + e.sendNodeVersionMsg() + case <-ctx.Done(): + return + } + } + }) + + chanID, blockSubscriberChan, blockSubErr := e.blockSubscriber.Subscribe() + if blockSubErr != nil { + e.lggr.Errorf("Block Subscriber Error: Subscribe(): %s", blockSubErr) + return blockSubErr + } + e.blockSubChanID = chanID + e.threadCtrl.Go(func(ctx context.Context) { + e.lggr.Debug("Started: Sending BlockNumber Messages") + for { + select { + case blockHistory := <-blockSubscriberChan: + // Exploratory: Debounce blocks to avoid overflow in case of re-org + latestBlockKey, err := blockHistory.Latest() + if err != nil { + e.lggr.Errorf("BlockSubscriber BlockHistory.Latest() failed: %s", err) + continue + } + e.sendBlockNumberMsg(latestBlockKey) + case <-ctx.Done(): + return + } + } + }) + return nil + }) +} + +// Close stops go routines and closes channels +func (e *AutomationCustomTelemetryService) Close() error { + return e.StopOnce("AutomationCustomTelemetryService", func() error { + e.lggr.Debug("Stopping: custom telemetry service") + e.threadCtrl.Close() + err := e.blockSubscriber.Unsubscribe(e.blockSubChanID) + if err != nil { + e.lggr.Errorf("Custom telemetry service encounters error %v when stopping", err) + return err + } + e.lggr.Infof("Stopped: Custom telemetry service") + return nil + }) +} + +func (e *AutomationCustomTelemetryService) sendNodeVersionMsg() { + vMsg := &telem.NodeVersion{ + Timestamp: uint64(time.Now().UTC().UnixMilli()), + NodeVersion: static.Version, + ConfigDigest: e.configDigest[:], + } + wrappedVMsg := &telem.AutomationTelemWrapper{ + Msg: &telem.AutomationTelemWrapper_NodeVersion{ + NodeVersion: vMsg, + }, + } + bytes, err := proto.Marshal(wrappedVMsg) + if err != nil { + e.lggr.Errorf("Error occurred while marshalling the Node Version Message %s: %v", wrappedVMsg.String(), err) + } else { + e.monitoringEndpoint.SendLog(bytes) + e.lggr.Debugf("NodeVersion Message Sent to Endpoint: %d", vMsg.Timestamp) + } +} + +func (e *AutomationCustomTelemetryService) sendBlockNumberMsg(blockKey ocr2keepers.BlockKey) { + blockNumMsg := &telem.BlockNumber{ + Timestamp: uint64(time.Now().UTC().UnixMilli()), + BlockNumber: uint64(blockKey.Number), + BlockHash: hex.EncodeToString(blockKey.Hash[:]), + ConfigDigest: e.configDigest[:], + } + wrappedBlockNumMsg := &telem.AutomationTelemWrapper{ + Msg: &telem.AutomationTelemWrapper_BlockNumber{ + BlockNumber: blockNumMsg, + }, + } + b, err := proto.Marshal(wrappedBlockNumMsg) + if err != nil { + e.lggr.Errorf("Error occurred while marshalling the Block Num Message %s: %v", wrappedBlockNumMsg.String(), err) + } else { + e.monitoringEndpoint.SendLog(b) + e.lggr.Debugf("BlockNumber Message Sent to Endpoint: %d", blockNumMsg.Timestamp) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go new file mode 100644 index 00000000000..4318d9aac60 --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go @@ -0,0 +1,56 @@ +package autotelemetry21 + +import ( + "sync" + "testing" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + + headtracker "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/logger" + evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" +) + +// const historySize = 4 +// const blockSize = int64(4) +const finality = uint32(4) + +func TestNewAutomationCustomTelemetryService(t *testing.T) { + me := &MockMonitoringEndpoint{} + lggr := logger.TestLogger(t) + var hb headtracker.HeadBroadcaster + var lp logpoller.LogPoller + + bs := evm.NewBlockSubscriber(hb, lp, finality, lggr) + // configTracker := &MockContractConfigTracker{} + var configTracker types.ContractConfigTracker + + service, err := NewAutomationCustomTelemetryService(me, lggr, bs, configTracker) + if err != nil { + t.Errorf("Expected no error, but got: %v", err) + } + service.monitoringEndpoint.SendLog([]byte("test")) + assert.Equal(t, me.LogCount(), 1) + service.monitoringEndpoint.SendLog([]byte("test2")) + assert.Equal(t, me.LogCount(), 2) + service.Close() +} + +type MockMonitoringEndpoint struct { + sentLogs [][]byte + lock sync.RWMutex +} + +func (me *MockMonitoringEndpoint) SendLog(log []byte) { + me.lock.Lock() + defer me.lock.Unlock() + me.sentLogs = append(me.sentLogs, log) +} + +func (me *MockMonitoringEndpoint) LogCount() int { + me.lock.RLock() + defer me.lock.RUnlock() + return len(me.sentLogs) +} diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index f5d775fe744..b897fba7f10 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -137,7 +137,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 95d898c353b..6cd6eaabc3c 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -143,7 +143,7 @@ ContractTransmitterTransmitTimeout = '1m0s' DatabaseTimeout = '8s' KeyBundleID = '7a5f66bbe6594259325bf2b4f5b1a9c900000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 9dd0be8f5d2..c743601ced8 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -137,7 +137,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '20s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 1eb9cd5023d..5b93c7061e8 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -890,7 +890,7 @@ ContractTransmitterTransmitTimeout = '10s' # Default DatabaseTimeout = '10s' # Default KeyBundleID = '7a5f66bbe6594259325bf2b4f5b1a9c900000000000000000000000000000000' # Example CaptureEATelemetry = false # Default -CaptureAutomationCustomTelemetry = false # Default +CaptureAutomationCustomTelemetry = true # Default DefaultTransactionQueueDepth = 1 # Default SimulateTransactions = false # Default TraceLogging = false # Default @@ -987,7 +987,7 @@ CaptureEATelemetry toggles collecting extra information from External Adaptares ### CaptureAutomationCustomTelemetry ```toml -CaptureAutomationCustomTelemetry = false # Default +CaptureAutomationCustomTelemetry = true # Default ``` CaptureAutomationCustomTelemetry toggles collecting automation specific telemetry diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 8a3b1af96fa..01e96ac944d 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -149,7 +149,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 31fded1b423..1f6901e9ffd 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -193,7 +193,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 78fc976912c..4c1a1c75fc3 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -193,7 +193,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 226a7bbb3b4..536b7d8ac08 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -193,7 +193,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 5cd3d567467..89f59574fcc 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -183,7 +183,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index fd24150b587..2d32b39a644 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -190,7 +190,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index 828d953da9a..e478203e00e 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -186,7 +186,7 @@ ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' KeyBundleID = '0000000000000000000000000000000000000000000000000000000000000000' CaptureEATelemetry = false -CaptureAutomationCustomTelemetry = false +CaptureAutomationCustomTelemetry = true DefaultTransactionQueueDepth = 1 SimulateTransactions = false TraceLogging = false From 29315fff30d0aa112036c01b6ccd98ef47e831d4 Mon Sep 17 00:00:00 2001 From: Tate Date: Fri, 17 Nov 2023 14:59:39 -0700 Subject: [PATCH 173/271] Bump chainlink-github-actions refs to be up to date (#11332) --- .github/actions/build-chainlink-image/action.yml | 4 ++-- .github/actions/build-test-image/action.yml | 8 ++++---- .github/actions/version-file-bump/action.yml | 2 +- .github/workflows/automation-benchmark-tests.yml | 2 +- .github/workflows/automation-load-tests.yml | 2 +- .github/workflows/automation-ondemand-tests.yml | 6 +++--- .github/workflows/ci-core.yml | 2 +- .github/workflows/integration-chaos-tests.yml | 6 +++--- .github/workflows/integration-tests.yml | 16 ++++++++-------- .github/workflows/live-testnet-tests.yml | 6 +++--- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- .../on-demand-vrfv2plus-performance-test.yml | 2 +- .github/workflows/performance-tests.yml | 2 +- 13 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/actions/build-chainlink-image/action.yml b/.github/actions/build-chainlink-image/action.yml index ac29a3d7b8d..d5839cc79bf 100644 --- a/.github/actions/build-chainlink-image/action.yml +++ b/.github/actions/build-chainlink-image/action.yml @@ -25,7 +25,7 @@ runs: steps: - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: repository: chainlink tag: ${{ inputs.git_commit_sha }}${{ inputs.tag_suffix }} @@ -33,7 +33,7 @@ runs: AWS_ROLE_TO_ASSUME: ${{ inputs.AWS_ROLE_TO_ASSUME }} - name: Build Image if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: cl_repo: smartcontractkit/chainlink cl_ref: ${{ inputs.git_commit_sha }} diff --git a/.github/actions/build-test-image/action.yml b/.github/actions/build-test-image/action.yml index e6c759109be..b7a7948d2cf 100644 --- a/.github/actions/build-test-image/action.yml +++ b/.github/actions/build-test-image/action.yml @@ -71,7 +71,7 @@ runs: - name: Check if test base image exists if: steps.version.outputs.is_semantic == 'false' id: check-base-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@00c6214deb10a3f374c6d3430c32c5202015d463 # v2.2.12 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: repository: ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image tag: ${{ steps.long_sha.outputs.long_sha }} @@ -79,7 +79,7 @@ runs: AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} - name: Build Base Image if: steps.version.outputs.is_semantic == 'false' && steps.check-base-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/docker/build-push@ce87f8986ca18336cc5015df75916c2ec0a7c4b3 # v2.1.2 + uses: smartcontractkit/chainlink-github-actions/docker/build-push@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: BASE_IMAGE_NAME: ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/test-base-image:${{ steps.long_sha.outputs.long_sha }} with: @@ -92,7 +92,7 @@ runs: # Test Runner Logic - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@00c6214deb10a3f374c6d3430c32c5202015d463 # v2.2.12 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: repository: ${{ inputs.repository }} tag: ${{ inputs.tag }} @@ -100,7 +100,7 @@ runs: AWS_ROLE_TO_ASSUME: ${{ inputs.QA_AWS_ROLE_TO_ASSUME }} - name: Build and Publish Test Runner if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/docker/build-push@00c6214deb10a3f374c6d3430c32c5202015d463 # v2.2.12 + uses: smartcontractkit/chainlink-github-actions/docker/build-push@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: tags: | ${{ inputs.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ inputs.QA_AWS_REGION }}.amazonaws.com/${{ inputs.repository }}:${{ inputs.tag }} diff --git a/.github/actions/version-file-bump/action.yml b/.github/actions/version-file-bump/action.yml index 73a374762d6..20832174003 100644 --- a/.github/actions/version-file-bump/action.yml +++ b/.github/actions/version-file-bump/action.yml @@ -31,7 +31,7 @@ runs: current_version=$(head -n1 ./VERSION) echo "current_version=${current_version}" | tee -a "$GITHUB_OUTPUT" - name: Compare semantic versions - uses: smartcontractkit/chainlink-github-actions/semver-compare@v2.2.0 + uses: smartcontractkit/chainlink-github-actions/semver-compare@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 id: compare with: version1: ${{ steps.get-current-version.outputs.current_version }} diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml index 0fff36f8df4..efe6d2eb59d 100644 --- a/.github/workflows/automation-benchmark-tests.yml +++ b/.github/workflows/automation-benchmark-tests.yml @@ -110,7 +110,7 @@ jobs: QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} suites: benchmark load/automationv2_1 chaos reorg - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: DETACH_RUNNER: true TEST_SUITE: benchmark diff --git a/.github/workflows/automation-load-tests.yml b/.github/workflows/automation-load-tests.yml index eebd87322cf..b19ac8fd24c 100644 --- a/.github/workflows/automation-load-tests.yml +++ b/.github/workflows/automation-load-tests.yml @@ -71,7 +71,7 @@ jobs: QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} suites: benchmark load/automationv2_1 chaos reorg - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: RR_CPU: 4000m RR_MEM: 4Gi diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index 88c2c126dc6..5cd2182ff62 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -59,7 +59,7 @@ jobs: - name: Check if image exists if: inputs.chainlinkImage == '' id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: repository: chainlink tag: ${{ github.sha }}${{ matrix.image.tag-suffix }} @@ -67,7 +67,7 @@ jobs: AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - name: Build Image if: steps.check-image.outputs.exists == 'false' && inputs.chainlinkImage == '' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: cl_repo: smartcontractkit/chainlink cl_ref: ${{ github.sha }} @@ -172,7 +172,7 @@ jobs: echo "version=${{ inputs.chainlinkVersionUpdate }}" >>$GITHUB_OUTPUT fi - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ matrix.tests.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.tests.pyroscope_env }} diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index d0bae664801..e69fc64cc7b 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -84,7 +84,7 @@ jobs: run: ./tools/bin/${{ matrix.cmd }} ./... - name: Print Filtered Test Results if: ${{ failure() && matrix.cmd == 'go_core_tests' }} - uses: smartcontractkit/chainlink-github-actions/go/go-test-results-parsing@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/go/go-test-results-parsing@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: results-file: ./output.txt output-file: ./output-short.txt diff --git a/.github/workflows/integration-chaos-tests.yml b/.github/workflows/integration-chaos-tests.yml index 892a43e76f0..10c62810996 100644 --- a/.github/workflows/integration-chaos-tests.yml +++ b/.github/workflows/integration-chaos-tests.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: repository: chainlink tag: ${{ github.sha }} @@ -38,7 +38,7 @@ jobs: AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - name: Build Image if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: cl_repo: smartcontractkit/chainlink cl_ref: ${{ github.sha }} @@ -109,7 +109,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 11 ./chaos 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 17f571fd636..9550be83ced 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -233,7 +233,7 @@ jobs: ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} @@ -398,7 +398,7 @@ jobs: ## Run this step when changes that require tests to be run are made - name: Run Tests if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ matrix.product.pyroscope_env == '' && '' || !startsWith(github.ref, 'refs/tags/') && '' || secrets.QA_PYROSCOPE_INSTANCE }} # Avoid sending blank envs https://github.com/orgs/community/discussions/25725 PYROSCOPE_ENVIRONMENT: ${{ matrix.product.pyroscope_env }} @@ -422,7 +422,7 @@ jobs: ## Run this step when changes that do not need the test to run are made - name: Run Setup if: needs.changes.outputs.src == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_download_vendor_packages_command: cd ./integration-tests && go mod download go_mod_path: ./integration-tests/go.mod @@ -521,7 +521,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Run Setup - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-go@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_download_vendor_packages_command: | cd ./integration-tests @@ -569,7 +569,7 @@ jobs: run: | echo "Running migration tests from version '${{ steps.get_latest_version.outputs.latest_version }}' to: '${{ github.sha }}'" - name: Run Migration Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json ./migration 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download @@ -669,7 +669,7 @@ jobs: steps: - name: Check if image exists id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@eccde1970eca69f079d3efb3409938a72ade8497 # v2.2.13 + uses: smartcontractkit/chainlink-github-actions/docker/image-exists@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: repository: chainlink-solana-tests tag: ${{ needs.get_solana_sha.outputs.sha }} @@ -814,7 +814,7 @@ jobs: ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Run Setup if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/setup-run-tests-environment@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: go_mod_path: ./integration-tests/go.mod cache_restore_only: true @@ -843,7 +843,7 @@ jobs: docker rm "$CONTAINER_ID" - name: Run Tests if: needs.changes.outputs.src == 'true' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: export ENV_JOB_IMAGE=${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-solana-tests:${{ needs.get_solana_sha.outputs.sha }} && make test_smoke cl_repo: ${{ env.CHAINLINK_IMAGE }} diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 23e9b3c04cf..7eb1669b35f 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -78,7 +78,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia @@ -133,7 +133,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-goerli @@ -188,7 +188,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-goerli diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index 4ea10cd4823..b17fdc6beb4 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -133,7 +133,7 @@ jobs: QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 env: DETACH_RUNNER: true TEST_SUITE: soak diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index b4f9f46de02..2e765ab79bd 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -118,7 +118,7 @@ jobs: with: fetch-depth: 0 - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 6h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download diff --git a/.github/workflows/performance-tests.yml b/.github/workflows/performance-tests.yml index 57907fe6c2d..4b6dd1a2280 100644 --- a/.github/workflows/performance-tests.yml +++ b/.github/workflows/performance-tests.yml @@ -57,7 +57,7 @@ jobs: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: test_command_to_run: cd integration-tests && go test -timeout 1h -count=1 -json -test.parallel 10 ./performance 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: make gomod From 6662c1ccef948be947c225f58b7c8372b5577e74 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 17 Nov 2023 16:07:01 -0600 Subject: [PATCH 174/271] switch from chainlink-relay to chainlink-common (#11320) --- common/client/mock_rpc_test.go | 2 +- common/client/multi_node.go | 4 +- common/client/multi_node_test.go | 2 +- common/client/node.go | 2 +- common/client/node_lifecycle_test.go | 2 +- common/client/send_only_node.go | 2 +- common/client/send_only_node_test.go | 2 +- common/client/types.go | 2 +- common/headtracker/head_broadcaster.go | 2 +- common/headtracker/head_tracker.go | 2 +- common/txmgr/broadcaster.go | 4 +- common/txmgr/confirmer.go | 4 +- common/txmgr/resender.go | 2 +- common/txmgr/txmgr.go | 2 +- core/bridges/bridge_type.go | 2 +- core/bridges/bridge_type_test.go | 2 +- core/chains/chain_kv.go | 2 +- core/chains/chain_kv_test.go | 2 +- core/chains/evm/assets/assets.go | 4 +- core/chains/evm/assets/assets_test.go | 4 +- core/chains/evm/chain.go | 10 +-- core/chains/evm/client/chain_client.go | 4 +- core/chains/evm/client/client.go | 2 +- core/chains/evm/client/mocks/client.go | 2 +- core/chains/evm/client/node.go | 2 +- core/chains/evm/client/null_client.go | 2 +- core/chains/evm/client/pool.go | 2 +- core/chains/evm/client/rpc_client.go | 8 +-- core/chains/evm/client/send_only_node.go | 2 +- .../evm/client/simulated_backend_client.go | 2 +- core/chains/evm/config/chain_scoped.go | 2 +- core/chains/evm/config/config.go | 4 +- core/chains/evm/config/toml/config.go | 22 +++--- .../evm/forwarders/forwarder_manager.go | 2 +- core/chains/evm/gas/arbitrum_estimator.go | 2 +- .../chains/evm/gas/block_history_estimator.go | 2 +- core/chains/evm/gas/models.go | 2 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 2 +- .../evm/gas/suggested_price_estimator.go | 2 +- .../evm/headtracker/head_tracker_test.go | 2 +- core/chains/evm/log/broadcaster.go | 2 +- core/chains/evm/logpoller/log_poller.go | 2 +- core/chains/evm/mocks/chain.go | 2 +- core/chains/evm/monitor/balance.go | 2 +- core/chains/evm/types/types.go | 2 +- core/cmd/cosmos_node_commands_test.go | 11 ++- core/cmd/cosmos_transaction_commands_test.go | 6 +- core/cmd/eth_keys_commands_test.go | 12 ++-- core/cmd/shell.go | 2 +- core/cmd/solana_node_commands_test.go | 2 +- core/cmd/solana_transaction_commands_test.go | 6 +- core/cmd/starknet_node_commands_test.go | 6 +- core/config/parse/parsers.go | 8 +-- core/internal/cltest/cltest.go | 2 +- core/internal/testutils/evmtest/evmtest.go | 2 +- core/logger/logger.go | 4 +- core/scripts/go.mod | 8 +-- core/scripts/go.sum | 16 ++--- core/services/blockhashstore/delegate.go | 2 +- core/services/blockheaderfeeder/delegate.go | 2 +- core/services/chainlink/application.go | 6 +- core/services/chainlink/config_test.go | 70 +++++++++---------- .../mocks/relayer_chain_interoperators.go | 4 +- .../chainlink/relayer_chain_interoperators.go | 4 +- .../relayer_chain_interoperators_test.go | 20 +++--- core/services/chainlink/relayer_factory.go | 2 +- core/services/directrequest/delegate.go | 4 +- core/services/directrequest/delegate_test.go | 2 +- core/services/directrequest/validate.go | 2 +- core/services/feeds/service.go | 2 +- core/services/fluxmonitorv2/config.go | 2 +- core/services/fluxmonitorv2/flux_monitor.go | 2 +- .../fluxmonitorv2/flux_monitor_test.go | 2 +- .../services/fluxmonitorv2/payment_checker.go | 2 +- .../fluxmonitorv2/payment_checker_test.go | 2 +- core/services/fluxmonitorv2/validate_test.go | 2 +- core/services/functions/connector_handler.go | 4 +- .../functions/connector_handler_test.go | 2 +- core/services/functions/listener.go | 2 +- core/services/gateway/connectionmanager.go | 2 +- core/services/gateway/connector/connector.go | 2 +- core/services/gateway/gateway.go | 2 +- .../gateway/handlers/functions/allowlist.go | 2 +- .../handlers/functions/handler.functions.go | 4 +- .../functions/handler.functions_test.go | 2 +- .../handlers/functions/subscriptions.go | 2 +- core/services/gateway/network/httpserver.go | 2 +- core/services/gateway/network/wsconnection.go | 2 +- core/services/gateway/network/wsserver.go | 2 +- core/services/health.go | 2 +- core/services/job/job_orm_test.go | 2 +- core/services/job/models.go | 8 +-- core/services/job/orm.go | 2 +- core/services/job/spawner.go | 8 +-- core/services/job/spawner_test.go | 4 +- .../keeper/registry_synchronizer_core.go | 2 +- core/services/keeper/upkeep_executer.go | 2 +- core/services/keystore/cosmos.go | 4 +- core/services/keystore/keys/ethkey/address.go | 2 +- core/services/keystore/starknet.go | 8 +-- core/services/mocks/checker.go | 2 +- core/services/multi.go | 2 +- core/services/nurse.go | 2 +- core/services/ocr/config_overrider.go | 2 +- core/services/ocr/contract_tracker.go | 2 +- core/services/ocr/delegate.go | 4 +- core/services/ocr2/delegate.go | 15 ++-- core/services/ocr2/delegate_test.go | 2 +- .../ocr2/plugins/functions/config/config.go | 2 +- .../services/ocr2/plugins/functions/plugin.go | 2 +- .../ocr2/plugins/functions/plugin_test.go | 2 +- .../ocr2/plugins/functions/reporting_test.go | 4 +- .../generic/pipeline_runner_adapter.go | 2 +- .../generic/pipeline_runner_adapter_test.go | 2 +- .../ocr2/plugins/generic/telemetry_adapter.go | 2 +- core/services/ocr2/plugins/median/plugin.go | 8 +-- core/services/ocr2/plugins/median/services.go | 4 +- .../ocr2/plugins/mercury/integration_test.go | 8 +-- core/services/ocr2/plugins/mercury/plugin.go | 10 +-- .../plugins/ocr2keeper/evm20/log_provider.go | 2 +- .../ocr2/plugins/ocr2keeper/evm20/registry.go | 2 +- .../ocr2keeper/evm21/block_subscriber.go | 2 +- .../ocr2keeper/evm21/logprovider/provider.go | 2 +- .../ocr2keeper/evm21/logprovider/recoverer.go | 2 +- .../evm21/mercury/streams/streams.go | 2 +- .../evm21/transmit/event_provider.go | 2 +- .../ocr2keeper/evm21/upkeepstate/store.go | 2 +- .../plugins/ocr2keeper/integration_21_test.go | 11 ++- .../plugins/ocr2keeper/integration_test.go | 10 +-- core/services/ocr2/plugins/ocr2keeper/util.go | 3 +- .../ocr2vrf/coordinator/coordinator_test.go | 12 ++-- core/services/ocr2/plugins/s4/factory_test.go | 4 +- .../ocr2/plugins/s4/integration_test.go | 4 +- core/services/ocr2/plugins/s4/plugin_test.go | 19 ++--- core/services/ocr2/validate/validate.go | 2 +- core/services/ocrbootstrap/delegate.go | 9 +-- core/services/ocrcommon/peer_wrapper.go | 7 +- core/services/ocrcommon/peerstore.go | 2 +- core/services/ocrcommon/run_saver.go | 2 +- core/services/ocrcommon/telemetry.go | 8 +-- core/services/ocrcommon/telemetry_test.go | 9 +-- core/services/periodicbackup/backup.go | 2 +- core/services/pg/event_broadcaster.go | 2 +- core/services/pg/q.go | 2 +- core/services/pg/sqlx.go | 2 +- core/services/pg/transaction.go | 2 +- core/services/pipeline/orm.go | 2 +- core/services/pipeline/runner.go | 2 +- core/services/promreporter/prom_reporter.go | 2 +- core/services/relay/evm/config_poller.go | 2 +- core/services/relay/evm/evm.go | 22 +++--- core/services/relay/evm/functions.go | 12 ++-- .../relay/evm/functions/logpoller_wrapper.go | 2 +- core/services/relay/evm/loop_impl.go | 2 +- .../relay/evm/mercury/persistence_manager.go | 2 +- core/services/relay/evm/mercury/queue.go | 2 +- .../services/relay/evm/mercury/transmitter.go | 5 +- .../relay/evm/mercury/v1/data_source.go | 4 +- .../relay/evm/mercury/v1/data_source_test.go | 4 +- .../mercury/v1/reportcodec/report_codec.go | 3 +- .../v1/reportcodec/report_codec_test.go | 2 +- .../relay/evm/mercury/v2/data_source.go | 4 +- .../relay/evm/mercury/v2/data_source_test.go | 4 +- .../mercury/v2/reportcodec/report_codec.go | 3 +- .../v2/reportcodec/report_codec_test.go | 2 +- .../relay/evm/mercury/v3/data_source.go | 4 +- .../relay/evm/mercury/v3/data_source_test.go | 4 +- .../mercury/v3/reportcodec/report_codec.go | 3 +- .../v3/reportcodec/report_codec_test.go | 2 +- .../relay/evm/mercury/wsrpc/client.go | 2 +- core/services/relay/evm/mercury_provider.go | 14 ++-- .../relay/evm/mocks/loop_relay_adapter.go | 2 +- core/services/relay/evm/ocr2keeper.go | 15 ++-- core/services/relay/evm/ocr2vrf.go | 17 ++--- core/services/relay/evm/relayer_extender.go | 8 +-- .../relay/evm/request_round_tracker.go | 3 +- core/services/relay/evm/types/types.go | 12 ++-- core/services/relay/grpc_provider_server.go | 4 +- .../relay/grpc_provider_server_test.go | 2 +- core/services/relay/relay.go | 4 +- core/services/relay/relay_test.go | 7 +- core/services/service.go | 2 +- .../telemetry_ingress_batch_client.go | 2 +- .../telemetry_ingress_client.go | 2 +- core/services/telemetry/manager.go | 2 +- core/services/telemetry/manager_test.go | 2 +- .../vrf_coordinator_interface.go | 2 +- core/services/vrf/v1/listener_v1.go | 2 +- core/services/vrf/v2/integration_v2_test.go | 4 +- core/services/vrf/v2/listener_v2.go | 2 +- core/store/migrate/migrate_test.go | 2 +- core/utils/big.go | 2 +- core/utils/config/validate.go | 2 +- core/utils/mailbox_prom.go | 2 +- core/utils/sleeper_task.go | 2 +- core/utils/utils.go | 2 +- core/web/bridge_types_controller.go | 2 +- core/web/bridge_types_controller_test.go | 2 +- core/web/chains_controller.go | 2 +- core/web/cosmos_chains_controller_test.go | 2 +- core/web/eth_keys_controller.go | 8 +-- core/web/eth_keys_controller_test.go | 2 +- core/web/loader/getters.go | 6 +- core/web/loader/loader_test.go | 22 +++--- core/web/loader/node.go | 2 +- core/web/loop_registry_test.go | 2 +- core/web/nodes_controller.go | 2 +- core/web/presenters/bridges.go | 2 +- core/web/presenters/bridges_test.go | 2 +- core/web/presenters/cosmos_chain.go | 2 +- core/web/presenters/eth_key.go | 20 +++--- core/web/presenters/eth_key_test.go | 6 +- core/web/presenters/evm_chain.go | 2 +- core/web/presenters/job.go | 6 +- core/web/presenters/job_test.go | 2 +- core/web/presenters/solana_chain.go | 2 +- core/web/presenters/starknet_chain.go | 2 +- core/web/resolver/bridge_test.go | 2 +- core/web/resolver/chain.go | 2 +- core/web/resolver/eth_key_test.go | 8 +-- core/web/resolver/helpers.go | 2 +- core/web/resolver/mutation.go | 2 +- core/web/resolver/node.go | 2 +- core/web/resolver/node_test.go | 2 +- core/web/resolver/query.go | 2 +- core/web/resolver/spec_test.go | 10 +-- core/web/solana_chains_controller_test.go | 8 +-- go.mod | 8 +-- go.sum | 16 ++--- .../actions/vrfv2plus/vrfv2plus_steps.go | 12 ++-- integration-tests/go.mod | 8 +-- integration-tests/go.sum | 16 ++--- integration-tests/types/config/node/core.go | 6 +- plugins/cmd/chainlink-median/main.go | 2 +- plugins/cmd/chainlink-medianpoc/main.go | 6 +- plugins/config.go | 2 +- plugins/loop_registry.go | 4 +- plugins/medianpoc/data_source.go | 4 +- plugins/medianpoc/data_source_test.go | 5 +- plugins/medianpoc/plugin.go | 10 +-- plugins/medianpoc/plugin_test.go | 2 +- 241 files changed, 551 insertions(+), 538 deletions(-) diff --git a/common/client/mock_rpc_test.go b/common/client/mock_rpc_test.go index 8f171302a2a..d5e8db82836 100644 --- a/common/client/mock_rpc_test.go +++ b/common/client/mock_rpc_test.go @@ -5,7 +5,7 @@ package client import ( big "math/big" - assets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + assets "github.com/smartcontractkit/chainlink-common/pkg/assets" context "context" diff --git a/common/client/multi_node.go b/common/client/multi_node.go index df91c61a9d8..acab47f0836 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -11,8 +11,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 3621d6d862d..4c0ebb1db93 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/common/types" diff --git a/common/client/node.go b/common/client/node.go index f28a171a558..5faaa5da627 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -11,7 +11,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 564c08bbdcc..0dffe935fe0 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/mock" "go.uber.org/zap" - "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/common/types/mocks" diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index fa793a826a6..767fff5aee2 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -6,7 +6,7 @@ import ( "net/url" "sync" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go index bfe55153656..3034b3f0a11 100644 --- a/common/client/send_only_node_test.go +++ b/common/client/send_only_node_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/smartcontractkit/chainlink-relay/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/common/client/types.go b/common/client/types.go index ef9f94dafea..6d3a22cee77 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -4,7 +4,7 @@ import ( "context" "math/big" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/common/headtracker/head_broadcaster.go b/common/headtracker/head_broadcaster.go index e9ae93419bb..3efe64e1c3f 100644 --- a/common/headtracker/head_broadcaster.go +++ b/common/headtracker/head_broadcaster.go @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 54262dd93f7..bf63675128f 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -10,7 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/common/types" diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index 1e3b2fa0a95..ba01fb9e2ad 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -15,8 +15,8 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/chains/label" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/chains/label" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index 4d3626ffacd..bf356115828 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -14,8 +14,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/chains/label" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/chains/label" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index 75781c08407..e604a960bf8 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/chains/label" + "github.com/smartcontractkit/chainlink-common/pkg/chains/label" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 5b7afd32242..24d2428f61a 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -11,7 +11,7 @@ import ( "github.com/google/uuid" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" diff --git a/core/bridges/bridge_type.go b/core/bridges/bridge_type.go index 9031541f22b..b2e86df4b9f 100644 --- a/core/bridges/bridge_type.go +++ b/core/bridges/bridge_type.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/bridges/bridge_type_test.go b/core/bridges/bridge_type_test.go index c04aba5c2c7..6a719d5b539 100644 --- a/core/bridges/bridge_type_test.go +++ b/core/bridges/bridge_type_test.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/store/models" diff --git a/core/chains/chain_kv.go b/core/chains/chain_kv.go index 5094f2885e5..4365a859bb2 100644 --- a/core/chains/chain_kv.go +++ b/core/chains/chain_kv.go @@ -6,7 +6,7 @@ import ( "golang.org/x/exp/maps" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) type ChainsKV[T types.ChainService] struct { diff --git a/core/chains/chain_kv_test.go b/core/chains/chain_kv_test.go index a30de3090b8..205ee693d6f 100644 --- a/core/chains/chain_kv_test.go +++ b/core/chains/chain_kv_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains" ) diff --git a/core/chains/evm/assets/assets.go b/core/chains/evm/assets/assets.go index c6c81b5ab48..377e92a855a 100644 --- a/core/chains/evm/assets/assets.go +++ b/core/chains/evm/assets/assets.go @@ -5,8 +5,8 @@ import ( "fmt" "math/big" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/utils/bytes" + "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/shopspring/decimal" diff --git a/core/chains/evm/assets/assets_test.go b/core/chains/evm/assets/assets_test.go index 9496554f116..09eb7b6888f 100644 --- a/core/chains/evm/assets/assets_test.go +++ b/core/chains/evm/assets/assets_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" ) @@ -69,7 +69,7 @@ func TestAssets_Eth_UnmarshalJsonError(t *testing.T) { assert.EqualError(t, err, "assets: cannot unmarshal \"x\" into a *assets.Eth") err = json.Unmarshal([]byte(`1`), ð) - assert.Equal(t, relayassets.ErrNoQuotesForCurrency, err) + assert.Equal(t, commonassets.ErrNoQuotesForCurrency, err) } func TestAssets_NewEth(t *testing.T) { diff --git a/core/chains/evm/chain.go b/core/chains/evm/chain.go index f21bf2525a0..1e52bed5cb6 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/evm/chain.go @@ -13,9 +13,9 @@ import ( "github.com/jmoiron/sqlx" - relaychains "github.com/smartcontractkit/chainlink-relay/pkg/chains" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + common "github.com/smartcontractkit/chainlink-common/pkg/chains" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains" @@ -422,7 +422,7 @@ func (c *chain) listNodeStatuses(start, end int) ([]types.NodeStatus, int, error nodes := c.cfg.Nodes() total := len(nodes) if start >= total { - return nil, total, relaychains.ErrOutOfRange + return nil, total, common.ErrOutOfRange } if end > total { end = total @@ -459,7 +459,7 @@ func (c *chain) listNodeStatuses(start, end int) ([]types.NodeStatus, int, error } func (c *chain) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) (stats []types.NodeStatus, nextPageToken string, total int, err error) { - return relaychains.ListNodeStatuses(int(pageSize), pageToken, c.listNodeStatuses) + return common.ListNodeStatuses(int(pageSize), pageToken, c.listNodeStatuses) } func (c *chain) ID() *big.Int { return c.id } diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 79a91dfc057..0f15b35ee9e 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -183,7 +183,7 @@ func (c *chainClient) IsL2() bool { return c.multiNode.IsL2() } -func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*relayassets.Link, error) { +func (c *chainClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { return c.multiNode.LINKBalance(ctx, address, linkAddress) } diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index 988a7404c9b..5263c74de15 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/common/config" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" diff --git a/core/chains/evm/client/mocks/client.go b/core/chains/evm/client/mocks/client.go index f1ff5fab456..22498370a2a 100644 --- a/core/chains/evm/client/mocks/client.go +++ b/core/chains/evm/client/mocks/client.go @@ -5,7 +5,7 @@ package mocks import ( big "math/big" - assets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + assets "github.com/smartcontractkit/chainlink-common/pkg/assets" common "github.com/ethereum/go-ethereum/common" diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index 80bac25448d..b6907202409 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -19,7 +19,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index 45876d9259c..e25fed6d964 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 473ab6d1eb0..18f59b172d7 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index f952c04d5bb..785acbb2b7d 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -17,7 +17,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -869,12 +869,12 @@ func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, co } // LINKBalance returns the balance of LINK at the given address -func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*relayassets.Link, error) { +func (r *rpcClient) LINKBalance(ctx context.Context, address common.Address, linkAddress common.Address) (*commonassets.Link, error) { balance, err := r.TokenBalance(ctx, address, linkAddress) if err != nil { - return relayassets.NewLinkFromJuels(0), err + return commonassets.NewLinkFromJuels(0), err } - return (*relayassets.Link)(balance), nil + return (*commonassets.Link)(balance), nil } func (r *rpcClient) FilterEvents(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { diff --git a/core/chains/evm/client/send_only_node.go b/core/chains/evm/client/send_only_node.go index 3f2481c1899..beb12dbc4da 100644 --- a/core/chains/evm/client/send_only_node.go +++ b/core/chains/evm/client/send_only_node.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 33ecc3d0d5f..e922715eb9c 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -18,7 +18,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index 6030991179b..804c354e0ef 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -9,7 +9,7 @@ import ( ocr "github.com/smartcontractkit/libocr/offchainreporting" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index ec90797d7fa..2dd2d4704c3 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -6,7 +6,7 @@ import ( gethcommon "github.com/ethereum/go-ethereum/common" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/config" @@ -34,7 +34,7 @@ type EVM interface { LogBackfillBatchSize() uint32 LogKeepBlocksDepth() uint32 LogPollInterval() time.Duration - MinContractPayment() *relayassets.Link + MinContractPayment() *commonassets.Link MinIncomingConfirmations() uint32 NonceAutoSync() bool OperatorFactoryAddress() string diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index ae0fb41c5ea..26587cd3b0e 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -12,8 +12,8 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains" @@ -107,7 +107,7 @@ func (cs EVMConfigs) totalChains() int { } return total } -func (cs EVMConfigs) Chains(ids ...relay.ChainID) (r []relaytypes.ChainStatus, total int, err error) { +func (cs EVMConfigs) Chains(ids ...relay.ChainID) (r []commontypes.ChainStatus, total int, err error) { total = cs.totalChains() for _, ch := range cs { if ch == nil { @@ -126,7 +126,7 @@ func (cs EVMConfigs) Chains(ids ...relay.ChainID) (r []relaytypes.ChainStatus, t continue } } - ch2 := relaytypes.ChainStatus{ + ch2 := commontypes.ChainStatus{ ID: ch.ChainID.String(), Enabled: ch.IsEnabled(), } @@ -150,7 +150,7 @@ func (cs EVMConfigs) Node(name string) (types.Node, error) { return types.Node{}, fmt.Errorf("node %s: %w", name, chains.ErrNotFound) } -func (cs EVMConfigs) NodeStatus(name string) (relaytypes.NodeStatus, error) { +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 { @@ -158,7 +158,7 @@ func (cs EVMConfigs) NodeStatus(name string) (relaytypes.NodeStatus, error) { } } } - return relaytypes.NodeStatus{}, fmt.Errorf("node %s: %w", name, chains.ErrNotFound) + return commontypes.NodeStatus{}, fmt.Errorf("node %s: %w", name, chains.ErrNotFound) } func legacyNode(n *Node, chainID *utils.Big) (v2 types.Node) { @@ -179,13 +179,13 @@ func legacyNode(n *Node, chainID *utils.Big) (v2 types.Node) { return } -func nodeStatus(n *Node, chainID relay.ChainID) (relaytypes.NodeStatus, error) { - var s relaytypes.NodeStatus +func nodeStatus(n *Node, chainID relay.ChainID) (commontypes.NodeStatus, error) { + var s commontypes.NodeStatus s.ChainID = chainID s.Name = *n.Name b, err := toml.Marshal(n) if err != nil { - return relaytypes.NodeStatus{}, err + return commontypes.NodeStatus{}, err } s.Config = string(b) return s, nil @@ -220,7 +220,7 @@ func (cs EVMConfigs) Nodes(chainID relay.ChainID) (ns []types.Node, err error) { return } -func (cs EVMConfigs) NodeStatuses(chainIDs ...relay.ChainID) (ns []relaytypes.NodeStatus, err error) { +func (cs EVMConfigs) NodeStatuses(chainIDs ...relay.ChainID) (ns []commontypes.NodeStatus, err error) { if len(chainIDs) == 0 { for i := range cs { for _, n := range cs[i].Nodes { @@ -355,7 +355,7 @@ type Chain struct { LogPollInterval *models.Duration LogKeepBlocksDepth *uint32 MinIncomingConfirmations *uint32 - MinContractPayment *relayassets.Link + MinContractPayment *commonassets.Link NonceAutoSync *bool NoNewHeadsThreshold *models.Duration OperatorFactoryAddress *ethkey.EIP55Address diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index 934da487fd7..819fb31951e 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -12,7 +12,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmlogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index 860126a5888..c79202c7312 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index eb35cd27511..a3f3520b365 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 50cbddcd9bc..b6f34ab87a5 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index 09244e9d319..88c61c49344 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/chains/evm/gas/suggested_price_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go index 24cf20f172e..dadec6210c7 100644 --- a/core/chains/evm/gas/suggested_price_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index 8af344098f8..d734b230e1b 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -20,7 +20,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index 9c4050fd796..11c282a4d2e 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index b86ede5dbcb..6676b694b0f 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -20,7 +20,7 @@ import ( "github.com/pkg/errors" "golang.org/x/exp/maps" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/chains/evm/mocks/chain.go b/core/chains/evm/mocks/chain.go index f0f202b0938..3a686887d76 100644 --- a/core/chains/evm/mocks/chain.go +++ b/core/chains/evm/mocks/chain.go @@ -30,7 +30,7 @@ import ( txmgr "github.com/smartcontractkit/chainlink/v2/common/txmgr" - types "github.com/smartcontractkit/chainlink-relay/pkg/types" + types "github.com/smartcontractkit/chainlink-common/pkg/types" ) // Chain is an autogenerated mock type for the Chain type diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index 476d3c7019d..b12346ac008 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -13,7 +13,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/chains/evm/types/types.go b/core/chains/evm/types/types.go index 7d756485d00..d0e7292b204 100644 --- a/core/chains/evm/types/types.go +++ b/core/chains/evm/types/types.go @@ -12,7 +12,7 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/services/relay" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/cmd/cosmos_node_commands_test.go b/core/cmd/cosmos_node_commands_test.go index 591160629ec..728be9396f9 100644 --- a/core/cmd/cosmos_node_commands_test.go +++ b/core/cmd/cosmos_node_commands_test.go @@ -9,9 +9,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" + "github.com/smartcontractkit/chainlink-common/pkg/config" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -33,14 +32,14 @@ func TestShell_IndexCosmosNodes(t *testing.T) { t.Parallel() chainID := cosmostest.RandomChainID() - node := config.Node{ + node := coscfg.Node{ Name: ptr("second"), - TendermintURL: relaycfg.MustParseURL("http://tender.mint.test/bombay-12"), + TendermintURL: config.MustParseURL("http://tender.mint.test/bombay-12"), } - chain := config.TOMLConfig{ + chain := coscfg.TOMLConfig{ ChainID: ptr(chainID), Enabled: ptr(true), - Nodes: config.Nodes{&node}, + Nodes: coscfg.Nodes{&node}, } app := cosmosStartNewApplication(t, &chain) client, r := app.NewShellAndRenderer() diff --git a/core/cmd/cosmos_transaction_commands_test.go b/core/cmd/cosmos_transaction_commands_test.go index 67b014af2c1..f54ccaf4a68 100644 --- a/core/cmd/cosmos_transaction_commands_test.go +++ b/core/cmd/cosmos_transaction_commands_test.go @@ -13,16 +13,16 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" + "github.com/smartcontractkit/chainlink-common/pkg/config" cosmosclient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" cosmosdb "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/db" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/denom" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/params" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" ) @@ -50,7 +50,7 @@ func TestShell_SendCosmosCoins(t *testing.T) { nodes := coscfg.Nodes{ &coscfg.Node{ Name: ptr("random"), - TendermintURL: utils.MustParseURL(url), + TendermintURL: config.MustParseURL(url), }, } chainConfig := coscfg.TOMLConfig{ChainID: &chainID, Enabled: ptr(true), Chain: cosmosChain, Nodes: nodes} diff --git a/core/cmd/eth_keys_commands_test.go b/core/cmd/eth_keys_commands_test.go index d74ae231d72..293a2d3f6da 100644 --- a/core/cmd/eth_keys_commands_test.go +++ b/core/cmd/eth_keys_commands_test.go @@ -12,7 +12,7 @@ import ( "github.com/pkg/errors" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -35,7 +35,7 @@ func TestEthKeysPresenter_RenderTable(t *testing.T) { var ( address = "0x5431F5F973781809D18643b87B44921b11355d81" ethBalance = assets.NewEth(1) - linkBalance = relayassets.NewLinkFromJuels(2) + linkBalance = commonassets.NewLinkFromJuels(2) isDisabled = true createdAt = time.Now() updatedAt = time.Now().Add(time.Second) @@ -90,7 +90,7 @@ func TestShell_ListETHKeys(t *testing.T) { ethClient := newEthMock(t) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(13), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(commonassets.NewLinkFromJuels(13), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil) app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].Enabled = ptr(true) @@ -169,7 +169,7 @@ func TestShell_CreateETHKey(t *testing.T) { ethClient := newEthMock(t) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(42), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(commonassets.NewLinkFromJuels(42), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil) app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { @@ -244,7 +244,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { ethClient := newEthMock(t) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(42), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(commonassets.NewLinkFromJuels(42), nil) ethClient.On("PendingNonceAt", mock.Anything, mock.Anything).Return(uint64(0), nil) app := startNewApplicationV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].Enabled = ptr(true) @@ -362,7 +362,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { ethClient.On("Dial", mock.Anything).Maybe() ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(42), nil) - ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(relayassets.NewLinkFromJuels(42), nil) + ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(commonassets.NewLinkFromJuels(42), nil) set := flag.NewFlagSet("test", 0) cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 07cd2185dc2..52c90907362 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -31,7 +31,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/build" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/cmd/solana_node_commands_test.go b/core/cmd/solana_node_commands_test.go index 7c88557c6de..316cf16212d 100644 --- a/core/cmd/solana_node_commands_test.go +++ b/core/cmd/solana_node_commands_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/config" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" diff --git a/core/cmd/solana_transaction_commands_test.go b/core/cmd/solana_transaction_commands_test.go index cdb182cba41..f019616cb85 100644 --- a/core/cmd/solana_transaction_commands_test.go +++ b/core/cmd/solana_transaction_commands_test.go @@ -15,11 +15,11 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" - "github.com/smartcontractkit/chainlink-relay/pkg/utils" + "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-solana/pkg/solana" solanaClient "github.com/smartcontractkit/chainlink-solana/pkg/solana/client" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" - "github.com/smartcontractkit/chainlink-solana/pkg/solana" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" ) @@ -29,7 +29,7 @@ func TestShell_SolanaSendSol(t *testing.T) { url := solanaClient.SetupLocalSolNode(t) node := solcfg.Node{ Name: ptr(t.Name()), - URL: utils.MustParseURL(url), + URL: config.MustParseURL(url), } cfg := solana.TOMLConfig{ ChainID: &chainID, diff --git a/core/cmd/starknet_node_commands_test.go b/core/cmd/starknet_node_commands_test.go index 9d7c6fcaf4c..0347cdd18f7 100644 --- a/core/cmd/starknet_node_commands_test.go +++ b/core/cmd/starknet_node_commands_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" + commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" @@ -34,11 +34,11 @@ func TestShell_IndexStarkNetNodes(t *testing.T) { id := "starknet chain ID" node1 := config.Node{ Name: ptr("first"), - URL: relaycfg.MustParseURL("https://starknet1.example"), + URL: commoncfg.MustParseURL("https://starknet1.example"), } node2 := config.Node{ Name: ptr("second"), - URL: relaycfg.MustParseURL("https://starknet2.example"), + URL: commoncfg.MustParseURL("https://starknet2.example"), } chain := config.TOMLConfig{ ChainID: &id, diff --git a/core/config/parse/parsers.go b/core/config/parse/parsers.go index e2f6978187c..6243b74dd52 100644 --- a/core/config/parse/parsers.go +++ b/core/config/parse/parsers.go @@ -13,7 +13,7 @@ import ( "github.com/pkg/errors" "go.uber.org/zap/zapcore" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -23,10 +23,10 @@ func String(str string) (string, error) { return str, nil } -func Link(str string) (*relayassets.Link, error) { - i, ok := new(relayassets.Link).SetString(str, 10) +func Link(str string) (*commonassets.Link, error) { + i, ok := new(commonassets.Link).SetString(str, 10) if !ok { - return i, fmt.Errorf("unable to parse '%v' into *relayassets.Link(base 10)", str) + return i, fmt.Errorf("unable to parse '%s'", str) } return i, nil } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 83a97833bd1..02aa2de0cc0 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -43,7 +43,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 80237d218d7..7674650c010 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains" diff --git a/core/logger/logger.go b/core/logger/logger.go index 8e847a99ac0..1feaf0f5d6d 100644 --- a/core/logger/logger.go +++ b/core/logger/logger.go @@ -12,7 +12,7 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + common "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -49,7 +49,7 @@ func init() { } } -var _ relaylogger.Logger = (Logger)(nil) +var _ common.Logger = (Logger)(nil) //go:generate mockery --quiet --name Logger --output . --filename logger_mock_test.go --inpackage --case=underscore //go:generate mockery --quiet --name Logger --output ./mocks/ --case=underscore diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7bf60aff8b4..1d8ba40b82f 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -303,10 +303,10 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7139f0efa4e..13496adb7a2 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1462,14 +1462,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd h1:PRVJxNK67pQWufXuB1cxckH/xZkcQFDy8KjN9ZYqong= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd/go.mod h1:rOayi690YxLlkQy959PD8INhOAIAUi9LoN0G+J/CEf4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/core/services/blockhashstore/delegate.go b/core/services/blockhashstore/delegate.go index c8e55e47c3c..1a84323b6f0 100644 --- a/core/services/blockhashstore/delegate.go +++ b/core/services/blockhashstore/delegate.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" diff --git a/core/services/blockheaderfeeder/delegate.go b/core/services/blockheaderfeeder/delegate.go index 971a691d773..3de42d7a9e9 100644 --- a/core/services/blockheaderfeeder/delegate.go +++ b/core/services/blockheaderfeeder/delegate.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 0d479b1f1ab..29679ee92fb 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -18,8 +18,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - relayservices "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/build" @@ -417,7 +417,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { globalLogger.Debug("Off-chain reporting v2 disabled") } - healthChecker := relayservices.NewChecker() + healthChecker := commonservices.NewChecker() var lbs []utils.DependentAwaiter for _, c := range legacyEVMChains.Slice() { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index fbadb379cad..891c0a490fb 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -16,9 +16,9 @@ import ( ocrcommontypes "github.com/smartcontractkit/libocr/commontypes" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" - relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" @@ -145,7 +145,7 @@ var ( MaxMsgsPerBatch: ptr[int64](13), }, Nodes: []*coscfg.Node{ - {Name: ptr("primary"), TendermintURL: relaycfg.MustParseURL("http://columbus.cosmos.com")}, + {Name: ptr("primary"), TendermintURL: commoncfg.MustParseURL("http://columbus.cosmos.com")}, }}, { ChainID: ptr("Malaga-420"), @@ -153,7 +153,7 @@ var ( BlocksUntilTxTimeout: ptr[int64](20), }, Nodes: []*coscfg.Node{ - {Name: ptr("secondary"), TendermintURL: relaycfg.MustParseURL("http://bombay.cosmos.com")}, + {Name: ptr("secondary"), TendermintURL: commoncfg.MustParseURL("http://bombay.cosmos.com")}, }}, }, Solana: []*solana.TOMLConfig{ @@ -163,16 +163,16 @@ var ( MaxRetries: ptr[int64](12), }, Nodes: []*solcfg.Node{ - {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://mainnet.solana.com")}, + {Name: ptr("primary"), URL: commoncfg.MustParseURL("http://mainnet.solana.com")}, }, }, { ChainID: ptr("testnet"), Chain: solcfg.Chain{ - OCR2CachePollPeriod: relaycfg.MustNewDuration(time.Minute), + OCR2CachePollPeriod: commoncfg.MustNewDuration(time.Minute), }, Nodes: []*solcfg.Node{ - {Name: ptr("secondary"), URL: relaycfg.MustParseURL("http://testnet.solana.com")}, + {Name: ptr("secondary"), URL: commoncfg.MustParseURL("http://testnet.solana.com")}, }, }, }, @@ -180,10 +180,10 @@ var ( { ChainID: ptr("foobar"), Chain: stkcfg.Chain{ - ConfirmationPoll: relaycfg.MustNewDuration(time.Hour), + ConfirmationPoll: commoncfg.MustNewDuration(time.Hour), }, Nodes: []*stkcfg.Node{ - {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://stark.node")}, + {Name: ptr("primary"), URL: commoncfg.MustParseURL("http://stark.node")}, }, }, }, @@ -538,7 +538,7 @@ func TestConfig_Marshal(t *testing.T) { LogBackfillBatchSize: ptr[uint32](17), LogPollInterval: &minute, LogKeepBlocksDepth: ptr[uint32](100000), - MinContractPayment: relayassets.NewLinkFromJuels(math.MaxInt64), + MinContractPayment: commonassets.NewLinkFromJuels(math.MaxInt64), MinIncomingConfirmations: ptr[uint32](13), NonceAutoSync: ptr(true), NoNewHeadsThreshold: &minute, @@ -603,13 +603,13 @@ func TestConfig_Marshal(t *testing.T) { ChainID: ptr("mainnet"), Enabled: ptr(false), Chain: solcfg.Chain{ - BalancePollPeriod: relaycfg.MustNewDuration(time.Minute), - ConfirmPollPeriod: relaycfg.MustNewDuration(time.Second), - OCR2CachePollPeriod: relaycfg.MustNewDuration(time.Minute), - OCR2CacheTTL: relaycfg.MustNewDuration(time.Hour), - TxTimeout: relaycfg.MustNewDuration(time.Hour), - TxRetryTimeout: relaycfg.MustNewDuration(time.Minute), - TxConfirmTimeout: relaycfg.MustNewDuration(time.Second), + BalancePollPeriod: commoncfg.MustNewDuration(time.Minute), + ConfirmPollPeriod: commoncfg.MustNewDuration(time.Second), + OCR2CachePollPeriod: commoncfg.MustNewDuration(time.Minute), + OCR2CacheTTL: commoncfg.MustNewDuration(time.Hour), + TxTimeout: commoncfg.MustNewDuration(time.Hour), + TxRetryTimeout: commoncfg.MustNewDuration(time.Minute), + TxConfirmTimeout: commoncfg.MustNewDuration(time.Second), SkipPreflight: ptr(true), Commitment: ptr("banana"), MaxRetries: ptr[int64](7), @@ -617,12 +617,12 @@ func TestConfig_Marshal(t *testing.T) { ComputeUnitPriceMax: ptr[uint64](1000), ComputeUnitPriceMin: ptr[uint64](10), ComputeUnitPriceDefault: ptr[uint64](100), - FeeBumpPeriod: relaycfg.MustNewDuration(time.Minute), + FeeBumpPeriod: commoncfg.MustNewDuration(time.Minute), }, Nodes: []*solcfg.Node{ - {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://solana.web")}, - {Name: ptr("foo"), URL: relaycfg.MustParseURL("http://solana.foo")}, - {Name: ptr("bar"), URL: relaycfg.MustParseURL("http://solana.bar")}, + {Name: ptr("primary"), URL: commoncfg.MustParseURL("http://solana.web")}, + {Name: ptr("foo"), URL: commoncfg.MustParseURL("http://solana.foo")}, + {Name: ptr("bar"), URL: commoncfg.MustParseURL("http://solana.bar")}, }, }, } @@ -631,14 +631,14 @@ func TestConfig_Marshal(t *testing.T) { ChainID: ptr("foobar"), Enabled: ptr(true), Chain: stkcfg.Chain{ - OCR2CachePollPeriod: relaycfg.MustNewDuration(6 * time.Hour), - OCR2CacheTTL: relaycfg.MustNewDuration(3 * time.Minute), - RequestTimeout: relaycfg.MustNewDuration(time.Minute + 3*time.Second), - TxTimeout: relaycfg.MustNewDuration(13 * time.Second), - ConfirmationPoll: relaycfg.MustNewDuration(42 * time.Second), + OCR2CachePollPeriod: commoncfg.MustNewDuration(6 * time.Hour), + OCR2CacheTTL: commoncfg.MustNewDuration(3 * time.Minute), + RequestTimeout: commoncfg.MustNewDuration(time.Minute + 3*time.Second), + TxTimeout: commoncfg.MustNewDuration(13 * time.Second), + ConfirmationPoll: commoncfg.MustNewDuration(42 * time.Second), }, Nodes: []*stkcfg.Node{ - {Name: ptr("primary"), URL: relaycfg.MustParseURL("http://stark.node")}, + {Name: ptr("primary"), URL: commoncfg.MustParseURL("http://stark.node")}, }, }, } @@ -648,21 +648,21 @@ func TestConfig_Marshal(t *testing.T) { Enabled: ptr(true), Chain: coscfg.Chain{ Bech32Prefix: ptr("wasm"), - BlockRate: relaycfg.MustNewDuration(time.Minute), + BlockRate: commoncfg.MustNewDuration(time.Minute), BlocksUntilTxTimeout: ptr[int64](12), - ConfirmPollPeriod: relaycfg.MustNewDuration(time.Second), + ConfirmPollPeriod: commoncfg.MustNewDuration(time.Second), FallbackGasPrice: mustDecimal("0.001"), GasToken: ptr("ucosm"), GasLimitMultiplier: mustDecimal("1.2"), MaxMsgsPerBatch: ptr[int64](17), - OCR2CachePollPeriod: relaycfg.MustNewDuration(time.Minute), - OCR2CacheTTL: relaycfg.MustNewDuration(time.Hour), - TxMsgTimeout: relaycfg.MustNewDuration(time.Second), + OCR2CachePollPeriod: commoncfg.MustNewDuration(time.Minute), + OCR2CacheTTL: commoncfg.MustNewDuration(time.Hour), + TxMsgTimeout: commoncfg.MustNewDuration(time.Second), }, Nodes: []*coscfg.Node{ - {Name: ptr("primary"), TendermintURL: relaycfg.MustParseURL("http://tender.mint")}, - {Name: ptr("foo"), TendermintURL: relaycfg.MustParseURL("http://foo.url")}, - {Name: ptr("bar"), TendermintURL: relaycfg.MustParseURL("http://bar.web")}, + {Name: ptr("primary"), TendermintURL: commoncfg.MustParseURL("http://tender.mint")}, + {Name: ptr("foo"), TendermintURL: commoncfg.MustParseURL("http://foo.url")}, + {Name: ptr("bar"), TendermintURL: commoncfg.MustParseURL("http://bar.web")}, }, }, } diff --git a/core/services/chainlink/mocks/relayer_chain_interoperators.go b/core/services/chainlink/mocks/relayer_chain_interoperators.go index 81f112f7663..f778f61b0cb 100644 --- a/core/services/chainlink/mocks/relayer_chain_interoperators.go +++ b/core/services/chainlink/mocks/relayer_chain_interoperators.go @@ -9,11 +9,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/services/relay" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) // FakeRelayerChainInteroperators is a fake chainlink.RelayerChainInteroperators. diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index 1183277ac0b..3be1395694d 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -7,10 +7,10 @@ import ( "sort" "sync" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index 293cc298c8d..da1246c7bfe 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/loop" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" @@ -84,7 +84,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: solcfg.Chain{}, Nodes: []*solcfg.Node{{ Name: ptr("solana chain 1 node 1"), - URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8547").URL())), + URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8547").URL())), }}, }, &solana.TOMLConfig{ @@ -93,7 +93,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Chain: solcfg.Chain{}, Nodes: []*solcfg.Node{{ Name: ptr("solana chain 2 node 1"), - URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8527").URL())), + URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8527").URL())), }}, }, } @@ -106,15 +106,15 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: []*stkcfg.Node{ { Name: ptr("starknet chain 1 node 1"), - URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8547").URL())), + URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8547").URL())), }, { Name: ptr("starknet chain 1 node 2"), - URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8548").URL())), + URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8548").URL())), }, { Name: ptr("starknet chain 1 node 3"), - URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:8549").URL())), + URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:8549").URL())), }, }, }, @@ -125,7 +125,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: []*stkcfg.Node{ { Name: ptr("starknet chain 2 node 1"), - URL: ((*relaycfg.URL)(models.MustParseURL("http://localhost:3547").URL())), + URL: ((*commoncfg.URL)(models.MustParseURL("http://localhost:3547").URL())), }, }, }, @@ -143,7 +143,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 1 node 1"), - TendermintURL: (*relaycfg.URL)(models.MustParseURL("http://localhost:9548").URL()), + TendermintURL: (*commoncfg.URL)(models.MustParseURL("http://localhost:9548").URL()), }, }, }, @@ -158,7 +158,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Nodes: coscfg.Nodes{ &coscfg.Node{ Name: ptr("cosmos chain 2 node 1"), - TendermintURL: (*relaycfg.URL)(models.MustParseURL("http://localhost:9598").URL()), + TendermintURL: (*commoncfg.URL)(models.MustParseURL("http://localhost:9598").URL()), }, }, }, diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 4bbabea4c82..6376839c700 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -9,9 +9,9 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" "github.com/smartcontractkit/chainlink-solana/pkg/solana" pkgsolana "github.com/smartcontractkit/chainlink-solana/pkg/solana" pkgstarknet "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink" diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index 10943308b39..9da84fd3ee5 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -9,8 +9,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" diff --git a/core/services/directrequest/delegate_test.go b/core/services/directrequest/delegate_test.go index 1c7929d94d3..56c28e57458 100644 --- a/core/services/directrequest/delegate_test.go +++ b/core/services/directrequest/delegate_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" diff --git a/core/services/directrequest/validate.go b/core/services/directrequest/validate.go index cdb478e8aae..bc31f09b685 100644 --- a/core/services/directrequest/validate.go +++ b/core/services/directrequest/validate.go @@ -4,7 +4,7 @@ import ( "github.com/pelletier/go-toml" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "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" diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index f6e8952d6b1..da19a33abc8 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -17,7 +17,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/fluxmonitorv2/config.go b/core/services/fluxmonitorv2/config.go index 0360e9ce03a..2680f30a777 100644 --- a/core/services/fluxmonitorv2/config.go +++ b/core/services/fluxmonitorv2/config.go @@ -3,7 +3,7 @@ package fluxmonitorv2 import ( "time" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/core/services/fluxmonitorv2/flux_monitor.go b/core/services/fluxmonitorv2/flux_monitor.go index 5dbeaeafc31..ea853d879d4 100644 --- a/core/services/fluxmonitorv2/flux_monitor.go +++ b/core/services/fluxmonitorv2/flux_monitor.go @@ -15,7 +15,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/fluxmonitorv2/flux_monitor_test.go b/core/services/fluxmonitorv2/flux_monitor_test.go index e8bbf739bbb..1a14fb8bd0a 100644 --- a/core/services/fluxmonitorv2/flux_monitor_test.go +++ b/core/services/fluxmonitorv2/flux_monitor_test.go @@ -20,7 +20,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" diff --git a/core/services/fluxmonitorv2/payment_checker.go b/core/services/fluxmonitorv2/payment_checker.go index b5dbd73c064..e4cc40c96a3 100644 --- a/core/services/fluxmonitorv2/payment_checker.go +++ b/core/services/fluxmonitorv2/payment_checker.go @@ -3,7 +3,7 @@ package fluxmonitorv2 import ( "math/big" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" ) // MinFundedRounds defines the minimum number of rounds that needs to be paid diff --git a/core/services/fluxmonitorv2/payment_checker_test.go b/core/services/fluxmonitorv2/payment_checker_test.go index 48a06553cb9..baec5642339 100644 --- a/core/services/fluxmonitorv2/payment_checker_test.go +++ b/core/services/fluxmonitorv2/payment_checker_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" ) diff --git a/core/services/fluxmonitorv2/validate_test.go b/core/services/fluxmonitorv2/validate_test.go index b397e6d3749..94dc8b6b709 100644 --- a/core/services/fluxmonitorv2/validate_test.go +++ b/core/services/fluxmonitorv2/validate_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils/tomlutils" diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 5b67e333d2b..343980afdd5 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -10,8 +10,8 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index 1bf9f8e5938..82c3dab3afc 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -7,7 +7,7 @@ import ( "math/big" "testing" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/functions" diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index efb40330cb4..3a308431807 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/cbor" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/gateway/connectionmanager.go b/core/services/gateway/connectionmanager.go index 278c4beaaae..ce4a54f4c2b 100644 --- a/core/services/gateway/connectionmanager.go +++ b/core/services/gateway/connectionmanager.go @@ -15,7 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/gateway/connector/connector.go b/core/services/gateway/connector/connector.go index 55786819448..0694e9ad15f 100644 --- a/core/services/gateway/connector/connector.go +++ b/core/services/gateway/connector/connector.go @@ -10,7 +10,7 @@ import ( "github.com/gorilla/websocket" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/gateway/gateway.go b/core/services/gateway/gateway.go index 42e03107f3e..79ddf0a5c69 100644 --- a/core/services/gateway/gateway.go +++ b/core/services/gateway/gateway.go @@ -13,7 +13,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" diff --git a/core/services/gateway/handlers/functions/allowlist.go b/core/services/gateway/handlers/functions/allowlist.go index 914a933eb15..3ba9a65d57a 100644 --- a/core/services/gateway/handlers/functions/allowlist.go +++ b/core/services/gateway/handlers/functions/allowlist.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_allow_list" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index a4301ef7e9c..32a132c075f 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -13,8 +13,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 4c8ba5bec3c..00334d30682 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" diff --git a/core/services/gateway/handlers/functions/subscriptions.go b/core/services/gateway/handlers/functions/subscriptions.go index c7a6519e693..7a59e05731e 100644 --- a/core/services/gateway/handlers/functions/subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions.go @@ -11,7 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_router" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/gateway/network/httpserver.go b/core/services/gateway/network/httpserver.go index 3cae8dc2763..d4340a92e98 100644 --- a/core/services/gateway/network/httpserver.go +++ b/core/services/gateway/network/httpserver.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" ) diff --git a/core/services/gateway/network/wsconnection.go b/core/services/gateway/network/wsconnection.go index 9215d183d18..813b644282f 100644 --- a/core/services/gateway/network/wsconnection.go +++ b/core/services/gateway/network/wsconnection.go @@ -5,7 +5,7 @@ import ( "errors" "sync/atomic" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/gorilla/websocket" diff --git a/core/services/gateway/network/wsserver.go b/core/services/gateway/network/wsserver.go index 86812a313eb..58b2dfe663c 100644 --- a/core/services/gateway/network/wsserver.go +++ b/core/services/gateway/network/wsserver.go @@ -10,7 +10,7 @@ import ( "github.com/gorilla/websocket" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" ) diff --git a/core/services/health.go b/core/services/health.go index 568823fa324..32e97fd8db3 100644 --- a/core/services/health.go +++ b/core/services/health.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index c2fc425918a..87ee15873d5 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/services/job/models.go b/core/services/job/models.go index 0b3e622f59b..05dcab831f1 100644 --- a/core/services/job/models.go +++ b/core/services/job/models.go @@ -14,8 +14,8 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -434,7 +434,7 @@ type DirectRequestSpec struct { ContractAddress ethkey.EIP55Address `toml:"contractAddress"` MinIncomingConfirmations clnull.Uint32 `toml:"minIncomingConfirmations"` Requesters models.AddressCollection `toml:"requesters"` - MinContractPayment *relayassets.Link `toml:"minContractPaymentLinkJuels"` + MinContractPayment *commonassets.Link `toml:"minContractPaymentLinkJuels"` EVMChainID *utils.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` @@ -475,7 +475,7 @@ type FluxMonitorSpec struct { DrumbeatSchedule string DrumbeatRandomDelay time.Duration DrumbeatEnabled bool - MinPayment *relayassets.Link + MinPayment *commonassets.Link EVMChainID *utils.Big `toml:"evmChainID"` CreatedAt time.Time `toml:"-"` UpdatedAt time.Time `toml:"-"` diff --git a/core/services/job/orm.go b/core/services/job/orm.go index fb897bc9281..ba102c6bb8b 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -18,7 +18,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" diff --git a/core/services/job/spawner.go b/core/services/job/spawner.go index 03ee8cee13a..5656011e14d 100644 --- a/core/services/job/spawner.go +++ b/core/services/job/spawner.go @@ -11,7 +11,7 @@ import ( "github.com/jmoiron/sqlx" - relayservices "github.com/smartcontractkit/chainlink-relay/pkg/services" + commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" @@ -43,7 +43,7 @@ type ( } spawner struct { - relayservices.StateMachine + commonservices.StateMachine orm ORM config Config checker services.Checker @@ -170,7 +170,7 @@ func (js *spawner) stopService(jobID int32) { for i := len(aj.services) - 1; i >= 0; i-- { service := aj.services[i] sLggr := lggr.With("subservice", i, "serviceType", reflect.TypeOf(service)) - if c, ok := service.(relayservices.HealthReporter); ok { + if c, ok := service.(commonservices.HealthReporter); ok { if err := js.checker.Unregister(c.Name()); err != nil { sLggr.Warnw("Failed to unregister service from health checker", "err", err) } @@ -230,7 +230,7 @@ func (js *spawner) StartService(ctx context.Context, jb Job, qopts ...pg.QOpt) e lggr.Criticalw("Error starting service for job", "err", err) return err } - if c, ok := srv.(relayservices.HealthReporter); ok { + if c, ok := srv.(commonservices.HealthReporter); ok { err = js.checker.Register(c) if err != nil { lggr.Errorw("Error registering service with health checker", "err", err) diff --git a/core/services/job/spawner_test.go b/core/services/job/spawner_test.go index cfe646d8660..0ad76491438 100644 --- a/core/services/job/spawner_test.go +++ b/core/services/job/spawner_test.go @@ -11,8 +11,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" diff --git a/core/services/keeper/registry_synchronizer_core.go b/core/services/keeper/registry_synchronizer_core.go index 761958ce194..db7cca1763f 100644 --- a/core/services/keeper/registry_synchronizer_core.go +++ b/core/services/keeper/registry_synchronizer_core.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 30e9f363579..ece6f85b068 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -12,7 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" diff --git a/core/services/keystore/cosmos.go b/core/services/keystore/cosmos.go index c06dfcdbcea..e3549fdb932 100644 --- a/core/services/keystore/cosmos.go +++ b/core/services/keystore/cosmos.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" ) @@ -147,7 +147,7 @@ func (ks *cosmos) getByID(id string) (cosmoskey.Key, error) { return key, nil } -// CosmosLoopKeystore implements the [github.com/smartcontractkit/chainlink-relay/pkg/loop.Keystore] interface and +// CosmosLoopKeystore implements the [github.com/smartcontractkit/chainlink-common/pkg/loop.Keystore] interface and // handles signing for Cosmos messages. type CosmosLoopKeystore struct { Cosmos diff --git a/core/services/keystore/keys/ethkey/address.go b/core/services/keystore/keys/ethkey/address.go index c14b602c597..1b26413f634 100644 --- a/core/services/keystore/keys/ethkey/address.go +++ b/core/services/keystore/keys/ethkey/address.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/chainlink-relay/pkg/utils/bytes" + "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" ) // EIP55Address is a new type for string which persists an ethereum address in diff --git a/core/services/keystore/starknet.go b/core/services/keystore/starknet.go index 7bf454004d0..251c74d0e00 100644 --- a/core/services/keystore/starknet.go +++ b/core/services/keystore/starknet.go @@ -9,8 +9,8 @@ import ( "github.com/smartcontractkit/caigo" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - adapters "github.com/smartcontractkit/chainlink-relay/pkg/loop/adapters/starknet" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + adapters "github.com/smartcontractkit/chainlink-common/pkg/loop/adapters/starknet" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/starkkey" ) @@ -155,7 +155,7 @@ func (ks *starknet) getByID(id string) (starkkey.Key, error) { return key, nil } -// StarknetLooppSigner implements [github.com/smartcontractkit/chainlink-relay/pkg/loop.Keystore] interface and the requirements +// StarknetLooppSigner implements [github.com/smartcontractkit/chainlink-common/pkg/loop.Keystore] interface and the requirements // of signature d/encoding of the [github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/txm.NewKeystoreAdapter] type StarknetLooppSigner struct { StarkNet @@ -165,7 +165,7 @@ var _ loop.Keystore = &StarknetLooppSigner{} // Sign implements [loop.Keystore] // hash is expected to be the byte representation of big.Int -// the returned []byte is an encoded [github.com/smartcontractkit/chainlink-relay/pkg/loop/adapters/starknet.Signature]. +// the returned []byte is an encoded [github.com/smartcontractkit/chainlink-common/pkg/loop/adapters/starknet.Signature]. // this enables compatibility with [github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/txm.NewKeystoreAdapter] func (lk *StarknetLooppSigner) Sign(ctx context.Context, id string, hash []byte) ([]byte, error) { diff --git a/core/services/mocks/checker.go b/core/services/mocks/checker.go index 354812d0212..e0c209d8afb 100644 --- a/core/services/mocks/checker.go +++ b/core/services/mocks/checker.go @@ -3,7 +3,7 @@ package mocks import ( - pkgservices "github.com/smartcontractkit/chainlink-relay/pkg/services" + pkgservices "github.com/smartcontractkit/chainlink-common/pkg/services" mock "github.com/stretchr/testify/mock" ) diff --git a/core/services/multi.go b/core/services/multi.go index 4ea263f5a30..1e465d5e724 100644 --- a/core/services/multi.go +++ b/core/services/multi.go @@ -3,7 +3,7 @@ package services import ( "io" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" ) // StartClose is a subset of the ServiceCtx interface. diff --git a/core/services/nurse.go b/core/services/nurse.go index e414ca280e7..3d896a80ff3 100644 --- a/core/services/nurse.go +++ b/core/services/nurse.go @@ -17,7 +17,7 @@ import ( "github.com/google/pprof/profile" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/services/ocr/config_overrider.go b/core/services/ocr/config_overrider.go index 5b2ac20c00c..b1acf9a7d73 100644 --- a/core/services/ocr/config_overrider.go +++ b/core/services/ocr/config_overrider.go @@ -12,7 +12,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 3e614fef4ae..5fecbe86288 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -20,7 +20,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting/confighelper" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index 0559469abb4..c8de3ec33c4 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -11,7 +11,7 @@ import ( "github.com/jmoiron/sqlx" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/gethwrappers/offchainaggregator" ocrnetworking "github.com/smartcontractkit/libocr/networking" @@ -167,7 +167,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e v2Bootstrappers = peerWrapper.P2PConfig().V2().DefaultBootstrappers() } - ocrLogger := relaylogger.NewOCRWrapper(lggr, chain.Config().OCR().TraceLogging(), func(msg string) { + ocrLogger := commonlogger.NewOCRWrapper(lggr, chain.Config().OCR().TraceLogging(), func(msg string) { d.jobORM.TryRecordError(jb.ID, msg) }) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 944c04c8d44..4b5932cd703 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -15,6 +15,7 @@ import ( "github.com/pkg/errors" "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -29,10 +30,10 @@ import ( dkgpkg "github.com/smartcontractkit/ocr2vrf/dkg" "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -387,7 +388,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { return nil, errors.New("peerWrapper is not started. OCR2 jobs require a started and running p2p v2 peer") } - ocrLogger := relaylogger.NewOCRWrapper(lggr, d.cfg.OCR2().TraceLogging(), func(msg string) { + ocrLogger := commonlogger.NewOCRWrapper(lggr, d.cfg.OCR2().TraceLogging(), func(msg string) { lggr.ErrorIf(d.jobORM.RecordError(jb.ID, msg), "unable to record error") }) @@ -958,11 +959,11 @@ func (d *Delegate) newServicesOCR2VRF( "jobName", jb.Name.ValueOrZero(), "jobID", jb.ID, ) - vrfLogger := relaylogger.NewOCRWrapper(l.With( + vrfLogger := commonlogger.NewOCRWrapper(l.With( "vrfContractID", spec.ContractID), d.cfg.OCR2().TraceLogging(), func(msg string) { lggr.ErrorIf(d.jobORM.RecordError(jb.ID, msg), "unable to record error") }) - dkgLogger := relaylogger.NewOCRWrapper(l.With( + dkgLogger := commonlogger.NewOCRWrapper(l.With( "dkgContractID", cfg.DKGContractAddress), d.cfg.OCR2().TraceLogging(), func(msg string) { lggr.ErrorIf(d.jobORM.RecordError(jb.ID, msg), "unable to record error") }) diff --git a/core/services/ocr2/delegate_test.go b/core/services/ocr2/delegate_test.go index daffac3f96b..b55e128119d 100644 --- a/core/services/ocr2/delegate_test.go +++ b/core/services/ocr2/delegate_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" diff --git a/core/services/ocr2/plugins/functions/config/config.go b/core/services/ocr2/plugins/functions/config/config.go index a179a22ce44..13e02042506 100644 --- a/core/services/ocr2/plugins/functions/config/config.go +++ b/core/services/ocr2/plugins/functions/config/config.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/types" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions" diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 14d7415cab5..475cf0a2af7 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/functions/plugin_test.go b/core/services/ocr2/plugins/functions/plugin_test.go index eea751789a0..453d4b67aa8 100644 --- a/core/services/ocr2/plugins/functions/plugin_test.go +++ b/core/services/ocr2/plugins/functions/plugin_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" diff --git a/core/services/ocr2/plugins/functions/reporting_test.go b/core/services/ocr2/plugins/functions/reporting_test.go index 860492bfc52..5b9f59ccb23 100644 --- a/core/services/ocr2/plugins/functions/reporting_test.go +++ b/core/services/ocr2/plugins/functions/reporting_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" functions_srv "github.com/smartcontractkit/chainlink/v2/core/services/functions" @@ -24,7 +24,7 @@ import ( func preparePlugin(t *testing.T, batchSize uint32, maxTotalGasLimit uint32) (types.ReportingPlugin, *functions_mocks.ORM, encoding.ReportCodec, *functions_mocks.OffchainTransmitter) { lggr := logger.TestLogger(t) - ocrLogger := relaylogger.NewOCRWrapper(lggr, true, func(msg string) {}) + ocrLogger := commonlogger.NewOCRWrapper(lggr, true, func(msg string) {}) orm := functions_mocks.NewORM(t) offchainTransmitter := functions_mocks.NewOffchainTransmitter(t) factory := functions.FunctionsReportingPluginFactory{ diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go index def33114e8c..872f83d3c35 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go index ef0e7421b50..f70e0dd443a 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" _ "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter.go b/core/services/ocr2/plugins/generic/telemetry_adapter.go index 51d94f5cfe7..a2ec6ba20cf 100644 --- a/core/services/ocr2/plugins/generic/telemetry_adapter.go +++ b/core/services/ocr2/plugins/generic/telemetry_adapter.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) var _ types.TelemetryService = (*TelemetryAdapter)(nil) diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go index f8517386b36..4f83c4b5dda 100644 --- a/core/services/ocr2/plugins/median/plugin.go +++ b/core/services/ocr2/plugins/median/plugin.go @@ -6,10 +6,10 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 8d121083f3f..9d65921ef2b 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -9,8 +9,8 @@ import ( libocr "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/mercury/integration_test.go b/core/services/ocr2/plugins/mercury/integration_test.go index ae2b4ca9742..e8adb55b397 100644 --- a/core/services/ocr2/plugins/mercury/integration_test.go +++ b/core/services/ocr2/plugins/mercury/integration_test.go @@ -34,10 +34,10 @@ import ( ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/wsrpc/credentials" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaycodecv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - relaycodecv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" - relaycodecv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaycodecv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + relaycodecv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + relaycodecv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index ddef1374a4c..bd68ccd8b72 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -7,10 +7,10 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" - relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -30,7 +30,7 @@ type Config interface { func NewServices( jb job.Job, - ocr2Provider relaytypes.MercuryProvider, + ocr2Provider commontypes.MercuryProvider, pipelineRunner pipeline.Runner, runResults chan *pipeline.Run, lggr logger.Logger, diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go index 4044bb5f2a4..d8941d505be 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go @@ -15,7 +15,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" "github.com/smartcontractkit/ocr2keepers/pkg/v2/encoding" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go index 2d49a91e98f..7fe4087cfa2 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go @@ -19,7 +19,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go index 6cc19a4d02e..ae407b0ea95 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go @@ -11,7 +11,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go index 349db2902b6..d3f069a9bf9 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go @@ -18,7 +18,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go index d6e7ad51d13..c6566568158 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/ocr2keepers/pkg/v3/random" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go index b83aca8a02a..d34787f501e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go @@ -17,7 +17,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go index 8f84ca1495c..000d40f4072 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go @@ -10,7 +10,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go index 6c5f767bd36..f5e3969bc79 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go @@ -10,7 +10,7 @@ import ( ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index f4fbf24f419..109a644ca09 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -18,7 +18,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" + gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/hashicorp/consul/sdk/freeport" "github.com/onsi/gomega" @@ -34,10 +34,9 @@ import ( ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" automationForwarderLogic "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_forwarder_logic" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/basic_upkeep_contract" @@ -67,7 +66,7 @@ func TestFilterNamesFromSpec21(t *testing.T) { address := common.HexToAddress(hexutil.Encode(b)) spec := &job.OCR2OracleSpec{ - PluginType: relaytypes.OCR2Keeper, + PluginType: types.OCR2Keeper, ContractID: address.String(), // valid contract addr } @@ -79,7 +78,7 @@ func TestFilterNamesFromSpec21(t *testing.T) { assert.Equal(t, logpoller.FilterName("KeeperRegistry Events", address), names[1]) spec = &job.OCR2OracleSpec{ - PluginType: relaytypes.OCR2Keeper, + PluginType: types.OCR2Keeper, ContractID: "0x5431", // invalid contract addr } _, err = ocr2keeper.FilterNamesFromSpec21(spec) @@ -723,7 +722,7 @@ func deployKeeper21Registry( return registryMaster } -func getUpkeepIdFromTx21(t *testing.T, registry *iregistry21.IKeeperRegistryMaster, registrationTx *types.Transaction, backend *backends.SimulatedBackend) *big.Int { +func getUpkeepIdFromTx21(t *testing.T, registry *iregistry21.IKeeperRegistryMaster, registrationTx *gethtypes.Transaction, backend *backends.SimulatedBackend) *big.Int { receipt, err := backend.TransactionReceipt(testutils.Context(t), registrationTx.Hash()) require.NoError(t, err) parsedLog, err := registry.ParseUpkeepRegistered(*receipt.Logs[0]) diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index 569dc20753b..a2184d92aec 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -19,7 +19,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" + gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/hashicorp/consul/sdk/freeport" "github.com/onsi/gomega" @@ -62,7 +62,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) const ( @@ -196,7 +196,7 @@ func accountsToAddress(accounts []ocrTypes.Account) (addresses []common.Address, return addresses, nil } -func getUpkeepIdFromTx(t *testing.T, registry *keeper_registry_wrapper2_0.KeeperRegistry, registrationTx *types.Transaction, backend *backends.SimulatedBackend) *big.Int { +func getUpkeepIdFromTx(t *testing.T, registry *keeper_registry_wrapper2_0.KeeperRegistry, registrationTx *gethtypes.Transaction, backend *backends.SimulatedBackend) *big.Int { receipt, err := backend.TransactionReceipt(testutils.Context(t), registrationTx.Hash()) require.NoError(t, err) parsedLog, err := registry.ParseUpkeepRegistered(*receipt.Logs[0]) @@ -714,7 +714,7 @@ func TestFilterNamesFromSpec20(t *testing.T) { address := common.HexToAddress(hexutil.Encode(b)) spec := &job.OCR2OracleSpec{ - PluginType: relaytypes.OCR2Keeper, + PluginType: types.OCR2Keeper, ContractID: address.String(), // valid contract addr } @@ -726,7 +726,7 @@ func TestFilterNamesFromSpec20(t *testing.T) { assert.Equal(t, logpoller.FilterName("EvmRegistry - Upkeep events for", address), names[1]) spec = &job.OCR2OracleSpec{ - PluginType: relaytypes.OCR2Keeper, + PluginType: types.OCR2Keeper, ContractID: "0x5431", // invalid contract addr } _, err = ocr2keeper.FilterNamesFromSpec20(spec) diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index fca98d87005..504b6267c60 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -4,13 +4,14 @@ import ( "fmt" "github.com/jmoiron/sqlx" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocr2keepers20 "github.com/smartcontractkit/ocr2keepers/pkg/v2" ocr2keepers20coordinator "github.com/smartcontractkit/ocr2keepers/pkg/v2/coordinator" ocr2keepers20polling "github.com/smartcontractkit/ocr2keepers/pkg/v2/observer/polling" ocr2keepers20runner "github.com/smartcontractkit/ocr2keepers/pkg/v2/runner" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ocr2keepers21 "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go index dc489b4958a..f634ee0c01a 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go @@ -12,7 +12,7 @@ import ( "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/core/types" + gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -25,7 +25,7 @@ import ( "github.com/smartcontractkit/ocr2vrf/ocr2vrf" ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -1469,7 +1469,7 @@ func newRandomnessRequestedLog( SubID: big.NewInt(1), CostJuels: big.NewInt(50_000), NewSubBalance: big.NewInt(100_000), - Raw: types.Log{ + Raw: gethtypes.Log{ BlockNumber: requestBlock, }, } @@ -1544,7 +1544,7 @@ func newRandomnessFulfillmentRequestedLog( Requester: common.HexToAddress("0x1234567890"), CostJuels: big.NewInt(50_000), NewSubBalance: big.NewInt(100_000), - Raw: types.Log{ + Raw: gethtypes.Log{ BlockNumber: requestBlock, }, } @@ -1763,7 +1763,7 @@ func TestFilterNamesFromSpec(t *testing.T) { spec := &job.OCR2OracleSpec{ ContractID: beaconAddress.String(), - PluginType: relaytypes.OCR2VRF, + PluginType: types.OCR2VRF, PluginConfig: job.JSONConfig{ "VRFCoordinatorAddress": coordinatorAddress.String(), "DKGContractAddress": dkgAddress.String(), @@ -1777,7 +1777,7 @@ func TestFilterNamesFromSpec(t *testing.T) { assert.Equal(t, logpoller.FilterName("VRF Coordinator", beaconAddress, coordinatorAddress, dkgAddress), names[0]) spec = &job.OCR2OracleSpec{ - PluginType: relaytypes.OCR2VRF, + PluginType: types.OCR2VRF, ContractID: beaconAddress.String(), PluginConfig: nil, // missing coordinator & dkg addresses } diff --git a/core/services/ocr2/plugins/s4/factory_test.go b/core/services/ocr2/plugins/s4/factory_test.go index 1b75988d83d..13a36a53823 100644 --- a/core/services/ocr2/plugins/s4/factory_test.go +++ b/core/services/ocr2/plugins/s4/factory_test.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/s4" s4_mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/offchainreporting2/types" @@ -18,7 +18,7 @@ import ( func TestS4ReportingPluginFactory_NewReportingPlugin(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) orm := s4_mocks.NewORM(t) f := s4.S4ReportingPluginFactory{ diff --git a/core/services/ocr2/plugins/s4/integration_test.go b/core/services/ocr2/plugins/s4/integration_test.go index 98ccd312e4b..54f0f02ad98 100644 --- a/core/services/ocr2/plugins/s4/integration_test.go +++ b/core/services/ocr2/plugins/s4/integration_test.go @@ -18,7 +18,7 @@ import ( s4_svc "github.com/smartcontractkit/chainlink/v2/core/services/s4" "github.com/smartcontractkit/chainlink/v2/core/utils" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2/types" @@ -56,7 +56,7 @@ func newDON(t *testing.T, size int, config *s4.PluginConfig) *don { orm := s4_svc.NewPostgresORM(db, logger, pgtest.NewQConfig(false), s4_svc.SharedTableName, ns) orms[i] = orm - ocrLogger := relaylogger.NewOCRWrapper(logger, true, func(msg string) {}) + ocrLogger := commonlogger.NewOCRWrapper(logger, true, func(msg string) {}) plugin, err := s4.NewReportingPlugin(ocrLogger, config, orm) require.NoError(t, err) plugins[i] = plugin diff --git a/core/services/ocr2/plugins/s4/plugin_test.go b/core/services/ocr2/plugins/s4/plugin_test.go index 94c876a4f7f..e2b5d21b847 100644 --- a/core/services/ocr2/plugins/s4/plugin_test.go +++ b/core/services/ocr2/plugins/s4/plugin_test.go @@ -13,13 +13,14 @@ import ( s4_mocks "github.com/smartcontractkit/chainlink/v2/core/services/s4/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "google.golang.org/protobuf/proto" + + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ) func createPluginConfig(maxEntries uint) *s4.PluginConfig { @@ -121,7 +122,7 @@ func rowsToShapshotRows(rows []*s4_svc.Row) []*s4_svc.SnapshotRow { func TestPlugin_NewReportingPlugin(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) orm := s4_mocks.NewORM(t) t.Run("ErrInvalidIntervals", func(t *testing.T) { @@ -167,7 +168,7 @@ func TestPlugin_NewReportingPlugin(t *testing.T) { func TestPlugin_Close(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) config := createPluginConfig(10) orm := s4_mocks.NewORM(t) plugin, err := s4.NewReportingPlugin(logger, config, orm) @@ -180,7 +181,7 @@ func TestPlugin_Close(t *testing.T) { func TestPlugin_ShouldTransmitAcceptedReport(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) config := createPluginConfig(10) orm := s4_mocks.NewORM(t) plugin, err := s4.NewReportingPlugin(logger, config, orm) @@ -194,7 +195,7 @@ func TestPlugin_ShouldTransmitAcceptedReport(t *testing.T) { func TestPlugin_ShouldAcceptFinalizedReport(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) config := createPluginConfig(10) orm := s4_mocks.NewORM(t) plugin, err := s4.NewReportingPlugin(logger, config, orm) @@ -255,7 +256,7 @@ func TestPlugin_ShouldAcceptFinalizedReport(t *testing.T) { func TestPlugin_Query(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) config := createPluginConfig(10) orm := s4_mocks.NewORM(t) plugin, err := s4.NewReportingPlugin(logger, config, orm) @@ -332,7 +333,7 @@ func TestPlugin_Query(t *testing.T) { func TestPlugin_Observation(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) config := createPluginConfig(10) orm := s4_mocks.NewORM(t) plugin, err := s4.NewReportingPlugin(logger, config, orm) @@ -465,7 +466,7 @@ func TestPlugin_Observation(t *testing.T) { func TestPlugin_Report(t *testing.T) { t.Parallel() - logger := relaylogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) + logger := commonlogger.NewOCRWrapper(logger.TestLogger(t), true, func(msg string) {}) config := createPluginConfig(10) orm := s4_mocks.NewORM(t) plugin, err := s4.NewReportingPlugin(logger, config, orm) diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index 65b95cb31a1..c97d23dca09 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -12,7 +12,7 @@ import ( libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/services/job" dkgconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/config" mercuryconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury/config" diff --git a/core/services/ocrbootstrap/delegate.go b/core/services/ocrbootstrap/delegate.go index 34e3ee0a710..7912741802c 100644 --- a/core/services/ocrbootstrap/delegate.go +++ b/core/services/ocrbootstrap/delegate.go @@ -8,11 +8,12 @@ import ( "github.com/pkg/errors" "github.com/jmoiron/sqlx" + ocr "github.com/smartcontractkit/libocr/offchainreporting2plus" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -165,7 +166,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e ContractConfigTracker: configProvider.ContractConfigTracker(), Database: NewDB(d.db.DB, spec.ID, lggr), LocalConfig: lc, - Logger: relaylogger.NewOCRWrapper(lggr.Named("OCRBootstrap"), d.ocr2Cfg.TraceLogging(), func(msg string) { + Logger: commonlogger.NewOCRWrapper(lggr.Named("OCRBootstrap"), d.ocr2Cfg.TraceLogging(), func(msg string) { logger.Sugared(lggr).ErrorIf(d.jobORM.RecordError(jb.ID, msg), "unable to record error") }), OffchainConfigDigester: configProvider.OffchainConfigDigester(), diff --git a/core/services/ocrcommon/peer_wrapper.go b/core/services/ocrcommon/peer_wrapper.go index 1daa84b7212..a7d510ef901 100644 --- a/core/services/ocrcommon/peer_wrapper.go +++ b/core/services/ocrcommon/peer_wrapper.go @@ -12,13 +12,14 @@ import ( "go.uber.org/multierr" "github.com/jmoiron/sqlx" + ocrnetworking "github.com/smartcontractkit/libocr/networking" ocrnetworkingtypes "github.com/smartcontractkit/libocr/networking/types" ocr1types "github.com/smartcontractkit/libocr/offchainreporting/types" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaylogger "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -194,7 +195,7 @@ func (p *SingletonPeerWrapper) peerConfig() (ocrnetworking.PeerConfig, error) { peerConfig := ocrnetworking.PeerConfig{ NetworkingStack: config.NetworkStack(), PrivKey: key.PrivKey, - Logger: relaylogger.NewOCRWrapper(p.lggr, p.ocrCfg.TraceLogging(), func(string) {}), + Logger: commonlogger.NewOCRWrapper(p.lggr, p.ocrCfg.TraceLogging(), func(string) {}), // V1 config V1ListenIP: config.V1().ListenIP(), V1ListenPort: p2pPort, diff --git a/core/services/ocrcommon/peerstore.go b/core/services/ocrcommon/peerstore.go index 02a4d90f578..1d859184ab5 100644 --- a/core/services/ocrcommon/peerstore.go +++ b/core/services/ocrcommon/peerstore.go @@ -14,7 +14,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" diff --git a/core/services/ocrcommon/run_saver.go b/core/services/ocrcommon/run_saver.go index d8dcc343588..184226605fe 100644 --- a/core/services/ocrcommon/run_saver.go +++ b/core/services/ocrcommon/run_saver.go @@ -3,7 +3,7 @@ package ocrcommon import ( "context" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" ) diff --git a/core/services/ocrcommon/telemetry.go b/core/services/ocrcommon/telemetry.go index be139723ef2..c9d3e85cd2c 100644 --- a/core/services/ocrcommon/telemetry.go +++ b/core/services/ocrcommon/telemetry.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -21,9 +21,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/utils" - relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" - relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" + relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" ) type eaTelemetry struct { diff --git a/core/services/ocrcommon/telemetry_test.go b/core/services/ocrcommon/telemetry_test.go index 9e3dedce8a8..24c798259d9 100644 --- a/core/services/ocrcommon/telemetry_test.go +++ b/core/services/ocrcommon/telemetry_test.go @@ -6,16 +6,17 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" "google.golang.org/protobuf/proto" - "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - mercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - mercury_v2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + mercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + mercury_v2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/periodicbackup/backup.go b/core/services/periodicbackup/backup.go index f43698bbdb0..b1bcf40ee32 100644 --- a/core/services/periodicbackup/backup.go +++ b/core/services/periodicbackup/backup.go @@ -11,7 +11,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/pg/event_broadcaster.go b/core/services/pg/event_broadcaster.go index f18ac3251a8..70008f4c0de 100644 --- a/core/services/pg/event_broadcaster.go +++ b/core/services/pg/event_broadcaster.go @@ -11,7 +11,7 @@ import ( "github.com/lib/pq" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/static" diff --git a/core/services/pg/q.go b/core/services/pg/q.go index 9c9c15d9838..050606c7937 100644 --- a/core/services/pg/q.go +++ b/core/services/pg/q.go @@ -17,7 +17,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) var promSQLQueryTime = promauto.NewHistogram(prometheus.HistogramOpts{ diff --git a/core/services/pg/sqlx.go b/core/services/pg/sqlx.go index c371c292138..c252edf9f5a 100644 --- a/core/services/pg/sqlx.go +++ b/core/services/pg/sqlx.go @@ -9,7 +9,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) type Queryer interface { diff --git a/core/services/pg/transaction.go b/core/services/pg/transaction.go index 74841d010bf..fd7e74baca3 100644 --- a/core/services/pg/transaction.go +++ b/core/services/pg/transaction.go @@ -11,7 +11,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" corelogger "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/services/pipeline/orm.go b/core/services/pipeline/orm.go index 056a7deab28..eb242e62765 100644 --- a/core/services/pipeline/orm.go +++ b/core/services/pipeline/orm.go @@ -13,7 +13,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index d33913b4753..edb1d337afd 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -14,7 +14,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/services/promreporter/prom_reporter.go b/core/services/promreporter/prom_reporter.go index fd6afeeb8ea..2306640bea6 100644 --- a/core/services/promreporter/prom_reporter.go +++ b/core/services/promreporter/prom_reporter.go @@ -13,7 +13,7 @@ import ( "go.uber.org/multierr" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/services/relay/evm/config_poller.go b/core/services/relay/evm/config_poller.go index daccf400ea7..fe39ed0e343 100644 --- a/core/services/relay/evm/config_poller.go +++ b/core/services/relay/evm/config_poller.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers2/ocrconfigurationstoreevmsimple" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index aa1d1d774bd..e8267c9a842 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -20,8 +20,8 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -44,7 +44,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) -var _ relaytypes.Relayer = &Relayer{} //nolint:staticcheck +var _ commontypes.Relayer = &Relayer{} //nolint:staticcheck type Relayer struct { db *sqlx.DB @@ -130,7 +130,7 @@ func (r *Relayer) HealthReport() (report map[string]error) { return } -func (r *Relayer) NewMercuryProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (relaytypes.MercuryProvider, error) { +func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.MercuryProvider, error) { lggr := r.lggr.Named("MercuryProvider").Named(rargs.ExternalJobID.String()) relayOpts := types.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() @@ -193,13 +193,13 @@ func (r *Relayer) NewMercuryProvider(rargs relaytypes.RelayArgs, pargs relaytype return NewMercuryProvider(cw, transmitter, reportCodecV1, reportCodecV2, reportCodecV3, chainReader, lggr), nil } -func (r *Relayer) NewFunctionsProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (relaytypes.FunctionsProvider, error) { +func (r *Relayer) NewFunctionsProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { lggr := r.lggr.Named("FunctionsProvider").Named(rargs.ExternalJobID.String()) // TODO(FUN-668): Not ready yet (doesn't implement FunctionsEvents() properly) return NewFunctionsProvider(r.chain, rargs, pargs, lggr, r.ks.Eth(), functions.FunctionsPlugin) } -func (r *Relayer) NewConfigProvider(args relaytypes.RelayArgs) (relaytypes.ConfigProvider, error) { +func (r *Relayer) NewConfigProvider(args commontypes.RelayArgs) (commontypes.ConfigProvider, error) { lggr := r.lggr.Named("ConfigProvider").Named(args.ExternalJobID.String()) relayOpts := types.NewRelayOpts(args) relayConfig, err := relayOpts.RelayConfig() @@ -219,7 +219,7 @@ func (r *Relayer) NewConfigProvider(args relaytypes.RelayArgs) (relaytypes.Confi return configProvider, err } -func FilterNamesFromRelayArgs(args relaytypes.RelayArgs) (filterNames []string, err error) { +func FilterNamesFromRelayArgs(args commontypes.RelayArgs) (filterNames []string, err error) { var addr ethkey.EIP55Address if addr, err = ethkey.NewEIP55Address(args.ContractID); err != nil { return nil, err @@ -373,7 +373,7 @@ func newConfigProvider(lggr logger.Logger, chain evm.Chain, opts *types.RelayOpt return newConfigWatcher(lggr, aggregatorAddress, contractABI, offchainConfigDigester, cp, chain, relayConfig.FromBlock, opts.New), nil } -func newContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayArgs, transmitterID string, configWatcher *configWatcher, ethKeystore keystore.Eth) (*contractTransmitter, error) { +func newContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, transmitterID string, configWatcher *configWatcher, ethKeystore keystore.Eth) (*contractTransmitter, error) { var relayConfig types.RelayConfig if err := json.Unmarshal(rargs.RelayConfig, &relayConfig); err != nil { return nil, err @@ -442,7 +442,7 @@ func newContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayArgs, tran ) } -func newPipelineContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayArgs, transmitterID string, pluginGasLimit *uint32, configWatcher *configWatcher, spec job.Job, pr pipeline.Runner) (*contractTransmitter, error) { +func newPipelineContractTransmitter(lggr logger.Logger, rargs commontypes.RelayArgs, transmitterID string, pluginGasLimit *uint32, configWatcher *configWatcher, spec job.Job, pr pipeline.Runner) (*contractTransmitter, error) { var relayConfig types.RelayConfig if err := json.Unmarshal(rargs.RelayConfig, &relayConfig); err != nil { return nil, err @@ -491,7 +491,7 @@ func newPipelineContractTransmitter(lggr logger.Logger, rargs relaytypes.RelayAr ) } -func (r *Relayer) NewMedianProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (relaytypes.MedianProvider, error) { +func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.MedianProvider, error) { lggr := r.lggr.Named("MedianProvider").Named(rargs.ExternalJobID.String()) relayOpts := types.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() @@ -526,7 +526,7 @@ func (r *Relayer) NewMedianProvider(rargs relaytypes.RelayArgs, pargs relaytypes }, nil } -var _ relaytypes.MedianProvider = (*medianProvider)(nil) +var _ commontypes.MedianProvider = (*medianProvider)(nil) type medianProvider struct { configWatcher *configWatcher diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index fdd6201c697..88f9d22099e 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -16,8 +16,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" @@ -46,7 +46,7 @@ func (p *functionsProvider) LogPollerWrapper() evmRelayTypes.LogPollerWrapper { return p.logPollerWrapper } -func (p *functionsProvider) FunctionsEvents() relaytypes.FunctionsEvents { +func (p *functionsProvider) FunctionsEvents() commontypes.FunctionsEvents { // TODO (FUN-668): implement return nil } @@ -85,7 +85,7 @@ func (p *functionsProvider) Name() string { return p.configWatcher.Name() } -func NewFunctionsProvider(chain evm.Chain, rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { +func NewFunctionsProvider(chain evm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { relayOpts := evmRelayTypes.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() if err != nil { @@ -130,7 +130,7 @@ func NewFunctionsProvider(chain evm.Chain, rargs relaytypes.RelayArgs, pargs rel }, nil } -func newFunctionsConfigProvider(pluginType functionsRelay.FunctionsPluginType, chain evm.Chain, args relaytypes.RelayArgs, fromBlock uint64, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (*configWatcher, error) { +func newFunctionsConfigProvider(pluginType functionsRelay.FunctionsPluginType, chain evm.Chain, args commontypes.RelayArgs, fromBlock uint64, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (*configWatcher, error) { if !common.IsHexAddress(args.ContractID) { return nil, errors.Errorf("invalid contractID, expected hex address") } @@ -153,7 +153,7 @@ func newFunctionsConfigProvider(pluginType functionsRelay.FunctionsPluginType, c return newConfigWatcher(lggr, routerContractAddress, contractABI, offchainConfigDigester, cp, chain, fromBlock, args.New), nil } -func newFunctionsContractTransmitter(contractVersion uint32, rargs relaytypes.RelayArgs, transmitterID string, configWatcher *configWatcher, ethKeystore keystore.Eth, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (ContractTransmitter, error) { +func newFunctionsContractTransmitter(contractVersion uint32, rargs commontypes.RelayArgs, transmitterID string, configWatcher *configWatcher, ethKeystore keystore.Eth, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (ContractTransmitter, error) { var relayConfig evmRelayTypes.RelayConfig if err := json.Unmarshal(rargs.RelayConfig, &relayConfig); err != nil { return nil, err diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index 6193f4ba862..230185d0ad7 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/functions/generated/functions_coordinator" diff --git a/core/services/relay/evm/loop_impl.go b/core/services/relay/evm/loop_impl.go index f69660373e6..309b5e24f62 100644 --- a/core/services/relay/evm/loop_impl.go +++ b/core/services/relay/evm/loop_impl.go @@ -1,7 +1,7 @@ package evm import ( - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/services/relay" diff --git a/core/services/relay/evm/mercury/persistence_manager.go b/core/services/relay/evm/mercury/persistence_manager.go index 9de1f80c6b5..69dfce9c16d 100644 --- a/core/services/relay/evm/mercury/persistence_manager.go +++ b/core/services/relay/evm/mercury/persistence_manager.go @@ -7,7 +7,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" diff --git a/core/services/relay/evm/mercury/queue.go b/core/services/relay/evm/mercury/queue.go index 3d20b3f2b0c..07ef8a97426 100644 --- a/core/services/relay/evm/mercury/queue.go +++ b/core/services/relay/evm/mercury/queue.go @@ -13,7 +13,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index 269f28b122d..73aa10243f8 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -17,11 +17,12 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 0bdfb67de78..79eb32af441 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -13,8 +13,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 3aea503ae6a..635658d7863 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -15,8 +15,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go index fefddd6395b..28688e3b17a 100644 --- a/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v1/reportcodec/report_codec.go @@ -8,9 +8,10 @@ import ( "github.com/ethereum/go-ethereum/common" pkgerrors "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - reportcodec "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" + reportcodec "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" diff --git a/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go index 3f4838c3e7c..f630b4522b4 100644 --- a/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v1/reportcodec/report_codec_test.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" + relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/relay/evm/mercury/v2/data_source.go b/core/services/relay/evm/mercury/v2/data_source.go index 17bc4a6670c..f7b78925130 100644 --- a/core/services/relay/evm/mercury/v2/data_source.go +++ b/core/services/relay/evm/mercury/v2/data_source.go @@ -10,8 +10,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/relay/evm/mercury/v2/data_source_test.go b/core/services/relay/evm/mercury/v2/data_source_test.go index b66355ac7fb..02492031939 100644 --- a/core/services/relay/evm/mercury/v2/data_source_test.go +++ b/core/services/relay/evm/mercury/v2/data_source_test.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" diff --git a/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go index 0e1dfe9c46f..6b13b3b6ef8 100644 --- a/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v2/reportcodec/report_codec.go @@ -6,9 +6,10 @@ import ( "math/big" pkgerrors "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - reportcodec "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" + reportcodec "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" diff --git a/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go index 8cf16a5dab4..3a58337b4ce 100644 --- a/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v2/reportcodec/report_codec_test.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" + relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" ) func newValidReportFields() relaymercuryv2.ReportFields { diff --git a/core/services/relay/evm/mercury/v3/data_source.go b/core/services/relay/evm/mercury/v3/data_source.go index 6f2b2eb6bde..34dbd13be99 100644 --- a/core/services/relay/evm/mercury/v3/data_source.go +++ b/core/services/relay/evm/mercury/v3/data_source.go @@ -11,8 +11,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/core/services/relay/evm/mercury/v3/data_source_test.go b/core/services/relay/evm/mercury/v3/data_source_test.go index aefc766996e..c4b5b4c6100 100644 --- a/core/services/relay/evm/mercury/v3/data_source_test.go +++ b/core/services/relay/evm/mercury/v3/data_source_test.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" diff --git a/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go b/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go index 4c0b3756d7b..6b379dc1948 100644 --- a/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go +++ b/core/services/relay/evm/mercury/v3/reportcodec/report_codec.go @@ -7,9 +7,10 @@ import ( "math/big" pkgerrors "github.com/pkg/errors" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - reportcodec "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" + reportcodec "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" diff --git a/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go b/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go index 98b81edb002..88416f7ea61 100644 --- a/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go +++ b/core/services/relay/evm/mercury/v3/reportcodec/report_codec_test.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" + relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" ) func newValidReportFields() relaymercuryv3.ReportFields { diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index f6ed1d0db8e..c4db80a58d0 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/connectivity" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index bba5e699bc6..90253808171 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -6,19 +6,19 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaymercury "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury" - relaymercuryv1 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v1" - relaymercuryv2 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v2" - relaymercuryv3 "github.com/smartcontractkit/chainlink-relay/pkg/reportingplugins/mercury/v3" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + relaymercury "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury" + relaymercuryv1 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v1" + relaymercuryv2 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v2" + relaymercuryv3 "github.com/smartcontractkit/chainlink-common/pkg/reportingplugins/mercury/v3" + "github.com/smartcontractkit/chainlink-common/pkg/services" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" ) -var _ relaytypes.MercuryProvider = (*mercuryProvider)(nil) +var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { configWatcher *configWatcher diff --git a/core/services/relay/evm/mocks/loop_relay_adapter.go b/core/services/relay/evm/mocks/loop_relay_adapter.go index 13107fe8ae5..13a73036a1d 100644 --- a/core/services/relay/evm/mocks/loop_relay_adapter.go +++ b/core/services/relay/evm/mocks/loop_relay_adapter.go @@ -9,7 +9,7 @@ import ( evm "github.com/smartcontractkit/chainlink/v2/core/chains/evm" mock "github.com/stretchr/testify/mock" - types "github.com/smartcontractkit/chainlink-relay/pkg/types" + types "github.com/smartcontractkit/chainlink-common/pkg/types" ) // LoopRelayAdapter is an autogenerated mock type for the LoopRelayAdapter type diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index a284d677ebf..2e8f9cbf487 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -10,12 +10,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -33,19 +34,19 @@ var ( // OCR2KeeperProviderOpts is the custom options to create a keeper provider type OCR2KeeperProviderOpts struct { - RArgs relaytypes.RelayArgs - PArgs relaytypes.PluginArgs + RArgs commontypes.RelayArgs + PArgs commontypes.PluginArgs InstanceID int } // OCR2KeeperProvider provides all components needed for a OCR2Keeper plugin. type OCR2KeeperProvider interface { - relaytypes.Plugin + commontypes.Plugin } // OCR2KeeperRelayer contains the relayer and instantiating functions for OCR2Keeper providers. type OCR2KeeperRelayer interface { - NewOCR2KeeperProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (OCR2KeeperProvider, error) + NewOCR2KeeperProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (OCR2KeeperProvider, error) } // ocr2keeperRelayer is the relayer with added DKG and OCR2Keeper provider functions. @@ -68,7 +69,7 @@ func NewOCR2KeeperRelayer(db *sqlx.DB, chain evm.Chain, pr pipeline.Runner, spec } } -func (r *ocr2keeperRelayer) NewOCR2KeeperProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (OCR2KeeperProvider, error) { +func (r *ocr2keeperRelayer) NewOCR2KeeperProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (OCR2KeeperProvider, error) { cfgWatcher, err := newOCR2KeeperConfigProvider(r.lggr, r.chain, rargs) if err != nil { return nil, err @@ -129,7 +130,7 @@ func (c *ocr2keeperProvider) ContractTransmitter() ocrtypes.ContractTransmitter return c.contractTransmitter } -func newOCR2KeeperConfigProvider(lggr logger.Logger, chain evm.Chain, rargs relaytypes.RelayArgs) (*configWatcher, error) { +func newOCR2KeeperConfigProvider(lggr logger.Logger, chain evm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) if err != nil { diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index 0c9414068e3..66ce42b7d8d 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -9,11 +9,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -24,18 +25,18 @@ import ( // DKGProvider provides all components needed for a DKG plugin. type DKGProvider interface { - relaytypes.Plugin + commontypes.Plugin } // OCR2VRFProvider provides all components needed for a OCR2VRF plugin. type OCR2VRFProvider interface { - relaytypes.Plugin + commontypes.Plugin } // OCR2VRFRelayer contains the relayer and instantiating functions for OCR2VRF providers. type OCR2VRFRelayer interface { - NewDKGProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (DKGProvider, error) - NewOCR2VRFProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (OCR2VRFProvider, error) + NewDKGProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (DKGProvider, error) + NewOCR2VRFProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (OCR2VRFProvider, error) } var ( @@ -61,7 +62,7 @@ func NewOCR2VRFRelayer(db *sqlx.DB, chain evm.Chain, lggr logger.Logger, ethKeys } } -func (r *ocr2vrfRelayer) NewDKGProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (DKGProvider, error) { +func (r *ocr2vrfRelayer) NewDKGProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (DKGProvider, error) { configWatcher, err := newOCR2VRFConfigProvider(r.lggr, r.chain, rargs) if err != nil { return nil, err @@ -84,7 +85,7 @@ func (r *ocr2vrfRelayer) NewDKGProvider(rargs relaytypes.RelayArgs, pargs relayt }, nil } -func (r *ocr2vrfRelayer) NewOCR2VRFProvider(rargs relaytypes.RelayArgs, pargs relaytypes.PluginArgs) (OCR2VRFProvider, error) { +func (r *ocr2vrfRelayer) NewOCR2VRFProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (OCR2VRFProvider, error) { configWatcher, err := newOCR2VRFConfigProvider(r.lggr, r.chain, rargs) if err != nil { return nil, err @@ -118,7 +119,7 @@ func (c *ocr2vrfProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } -func newOCR2VRFConfigProvider(lggr logger.Logger, chain evm.Chain, rargs relaytypes.RelayArgs) (*configWatcher, error) { +func newOCR2VRFConfigProvider(lggr logger.Logger, chain evm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) if err != nil { diff --git a/core/services/relay/evm/relayer_extender.go b/core/services/relay/evm/relayer_extender.go index b6ca4d75fb7..43626037a17 100644 --- a/core/services/relay/evm/relayer_extender.go +++ b/core/services/relay/evm/relayer_extender.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmchain "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -75,11 +75,11 @@ type ChainRelayerExt struct { var _ EVMChainRelayerExtender = &ChainRelayerExt{} -func (s *ChainRelayerExt) GetChainStatus(ctx context.Context) (relaytypes.ChainStatus, error) { +func (s *ChainRelayerExt) GetChainStatus(ctx context.Context) (commontypes.ChainStatus, error) { return s.chain.GetChainStatus(ctx) } -func (s *ChainRelayerExt) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) (stats []relaytypes.NodeStatus, nextPageToken string, total int, err error) { +func (s *ChainRelayerExt) ListNodeStatuses(ctx context.Context, pageSize int32, pageToken string) (stats []commontypes.NodeStatus, nextPageToken string, total int, err error) { return s.chain.ListNodeStatuses(ctx, pageSize, pageToken) } diff --git a/core/services/relay/evm/request_round_tracker.go b/core/services/relay/evm/request_round_tracker.go index c1c3a49e0e4..1f1ed71fc31 100644 --- a/core/services/relay/evm/request_round_tracker.go +++ b/core/services/relay/evm/request_round_tracker.go @@ -10,10 +10,11 @@ import ( "github.com/pkg/errors" "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index 6a1e66bd096..d2edef8b118 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -13,9 +13,9 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink-relay/pkg/types" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -36,7 +36,7 @@ type RelayConfig struct { type RelayOpts struct { // TODO BCF-2508 -- should anyone ever get the raw config bytes that are embedded in args? if not, // make this private and wrap the arg fields with funcs on RelayOpts - relaytypes.RelayArgs + commontypes.RelayArgs c *RelayConfig } @@ -71,9 +71,9 @@ type ConfigPoller interface { Replay(ctx context.Context, fromBlock int64) error } -// TODO(FUN-668): Migrate this fully into relaytypes.FunctionsProvider +// TODO(FUN-668): Migrate this fully into commontypes.FunctionsProvider type FunctionsProvider interface { - relaytypes.FunctionsProvider + commontypes.FunctionsProvider LogPollerWrapper() LogPollerWrapper } diff --git a/core/services/relay/grpc_provider_server.go b/core/services/relay/grpc_provider_server.go index 943af0e6362..67bbb8c6c2a 100644 --- a/core/services/relay/grpc_provider_server.go +++ b/core/services/relay/grpc_provider_server.go @@ -8,8 +8,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/services/relay/grpc_provider_server_test.go b/core/services/relay/grpc_provider_server_test.go index e7ee8d7f150..fafe20ef12a 100644 --- a/core/services/relay/grpc_provider_server_test.go +++ b/core/services/relay/grpc_provider_server_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" ) diff --git a/core/services/relay/relay.go b/core/services/relay/relay.go index 5c7bf5cab57..eb6d3faf4fd 100644 --- a/core/services/relay/relay.go +++ b/core/services/relay/relay.go @@ -5,8 +5,8 @@ import ( "fmt" "regexp" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) type Network = string diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index 5bcd14c64a0..fc9e273e302 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -4,12 +4,13 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) func TestIdentifier_UnmarshalString(t *testing.T) { diff --git a/core/services/service.go b/core/services/service.go index 066405ac012..19ca1183738 100644 --- a/core/services/service.go +++ b/core/services/service.go @@ -1,7 +1,7 @@ package services import ( - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" ) type ServiceCtx = services.Service diff --git a/core/services/synchronization/telemetry_ingress_batch_client.go b/core/services/synchronization/telemetry_ingress_batch_client.go index 26abda65d33..c551ac85b3e 100644 --- a/core/services/synchronization/telemetry_ingress_batch_client.go +++ b/core/services/synchronization/telemetry_ingress_batch_client.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/examples/simple/keys" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" diff --git a/core/services/synchronization/telemetry_ingress_client.go b/core/services/synchronization/telemetry_ingress_client.go index 9458b7627c9..b889b3fc97a 100644 --- a/core/services/synchronization/telemetry_ingress_client.go +++ b/core/services/synchronization/telemetry_ingress_client.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/wsrpc" "github.com/smartcontractkit/wsrpc/examples/simple/keys" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" diff --git a/core/services/telemetry/manager.go b/core/services/telemetry/manager.go index cc14a956c12..d760d13b2b0 100644 --- a/core/services/telemetry/manager.go +++ b/core/services/telemetry/manager.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 2d51d9f4491..d24fb348b75 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go index a8e662c9318..35556c6b45f 100644 --- a/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go +++ b/core/services/vrf/solidity_cross_tests/vrf_coordinator_interface.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index b1f9bbb5034..566e5ac9bd8 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -16,7 +16,7 @@ import ( heaps "github.com/theodesp/go-heaps" "github.com/theodesp/go-heaps/pairing" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index df886924aa1..15121ba306c 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -30,7 +30,7 @@ import ( "github.com/jmoiron/sqlx" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -1462,7 +1462,7 @@ func simulatedOverrides(t *testing.T, defaultGasPrice *assets.Wei, ks ...toml.Ke c.EVM[0].FinalityDepth = ptr[uint32](15) c.EVM[0].MinIncomingConfirmations = ptr[uint32](1) - c.EVM[0].MinContractPayment = relayassets.NewLinkFromJuels(100) + c.EVM[0].MinContractPayment = commonassets.NewLinkFromJuels(100) c.EVM[0].KeySpecific = ks } } diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 3480f63f092..5b73ac9e24c 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -25,7 +25,7 @@ import ( "github.com/theodesp/go-heaps/pairing" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/store/migrate/migrate_test.go b/core/store/migrate/migrate_test.go index 0ff09000161..43ddd41d56f 100644 --- a/core/store/migrate/migrate_test.go +++ b/core/store/migrate/migrate_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" evmcfg "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/utils/big.go b/core/utils/big.go index f0f9a2d96df..22cd8e64e55 100644 --- a/core/utils/big.go +++ b/core/utils/big.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/smartcontractkit/chainlink-relay/pkg/utils/bytes" + "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/utils/config/validate.go b/core/utils/config/validate.go index 32cb94b5205..5fbae24ad53 100644 --- a/core/utils/config/validate.go +++ b/core/utils/config/validate.go @@ -9,7 +9,7 @@ import ( "github.com/Masterminds/semver/v3" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink-relay/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/utils/mailbox_prom.go b/core/utils/mailbox_prom.go index dc20db84d9d..33cbb2357b1 100644 --- a/core/utils/mailbox_prom.go +++ b/core/utils/mailbox_prom.go @@ -10,7 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" ) var mailboxLoad = promauto.NewGaugeVec(prometheus.GaugeOpts{ diff --git a/core/utils/sleeper_task.go b/core/utils/sleeper_task.go index fcec2542493..d84457e9325 100644 --- a/core/utils/sleeper_task.go +++ b/core/utils/sleeper_task.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" ) // SleeperTask represents a task that waits in the background to process some work. diff --git a/core/utils/utils.go b/core/utils/utils.go index 6ea7164df1e..a2e418fe046 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -32,7 +32,7 @@ import ( "golang.org/x/crypto/bcrypt" "golang.org/x/crypto/sha3" - "github.com/smartcontractkit/chainlink-relay/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" ) const ( diff --git a/core/web/bridge_types_controller.go b/core/web/bridge_types_controller.go index 57a79e2b611..f320a525234 100644 --- a/core/web/bridge_types_controller.go +++ b/core/web/bridge_types_controller.go @@ -8,7 +8,7 @@ import ( "github.com/jackc/pgconn" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" diff --git a/core/web/bridge_types_controller_test.go b/core/web/bridge_types_controller_test.go index a65362ba9aa..a7873ad9f75 100644 --- a/core/web/bridge_types_controller_test.go +++ b/core/web/bridge_types_controller_test.go @@ -6,7 +6,7 @@ import ( "net/http" "testing" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/web/chains_controller.go b/core/web/chains_controller.go index c11d457c49c..e547cf0150e 100644 --- a/core/web/chains_controller.go +++ b/core/web/chains_controller.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/manyminds/api2go/jsonapi" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" diff --git a/core/web/cosmos_chains_controller_test.go b/core/web/cosmos_chains_controller_test.go index f8dbe4614fa..5491b33c359 100644 --- a/core/web/cosmos_chains_controller_test.go +++ b/core/web/cosmos_chains_controller_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/types" coscfg "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/config" - "github.com/smartcontractkit/chainlink-relay/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index 6e2a3b2efc4..b202d90f21e 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/config/toml" @@ -365,13 +365,13 @@ func (ekc *ETHKeysController) getEthBalance(ctx context.Context, state ethkey.St } -func (ekc *ETHKeysController) setLinkBalance(bal *relayassets.Link) presenters.NewETHKeyOption { +func (ekc *ETHKeysController) setLinkBalance(bal *commonassets.Link) presenters.NewETHKeyOption { return presenters.SetETHKeyLinkBalance(bal) } // queries the EthClient for the LINK balance at the address associated with state -func (ekc *ETHKeysController) getLinkBalance(ctx context.Context, state ethkey.State) *relayassets.Link { - var bal *relayassets.Link +func (ekc *ETHKeysController) getLinkBalance(ctx context.Context, state ethkey.State) *commonassets.Link { + var bal *commonassets.Link chainID := state.EVMChainID.ToInt() chain, err := ekc.app.GetRelayers().LegacyEVMChains().Get(chainID.String()) if err != nil { diff --git a/core/web/eth_keys_controller_test.go b/core/web/eth_keys_controller_test.go index 8941c2d2fdc..d0ad27262f2 100644 --- a/core/web/eth_keys_controller_test.go +++ b/core/web/eth_keys_controller_test.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" diff --git a/core/web/loader/getters.go b/core/web/loader/getters.go index 8ca29cc9624..27a39181ff8 100644 --- a/core/web/loader/getters.go +++ b/core/web/loader/getters.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -21,7 +21,7 @@ import ( var ErrInvalidType = errors.New("invalid type") // GetChainByID fetches the chain by it's id. -func GetChainByID(ctx context.Context, id string) (*relaytypes.ChainStatus, error) { +func GetChainByID(ctx context.Context, id string) (*commontypes.ChainStatus, error) { ldr := For(ctx) thunk := ldr.ChainsByIDLoader.Load(ctx, dataloader.StringKey(id)) @@ -30,7 +30,7 @@ func GetChainByID(ctx context.Context, id string) (*relaytypes.ChainStatus, erro return nil, err } - chain, ok := result.(relaytypes.ChainStatus) + chain, ok := result.(commontypes.ChainStatus) if !ok { return nil, ErrInvalidType } diff --git a/core/web/loader/loader_test.go b/core/web/loader/loader_test.go index 984aa9f6189..9bd1feb05bf 100644 --- a/core/web/loader/loader_test.go +++ b/core/web/loader/loader_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - relaytypes "github.com/smartcontractkit/chainlink-relay/pkg/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -50,12 +50,12 @@ func TestLoader_Chains(t *testing.T) { assert.Len(t, results, 3) config2, err := chain2.TOMLString() require.NoError(t, err) - want2 := relaytypes.ChainStatus{ID: "2", Enabled: true, Config: config2} - assert.Equal(t, want2, results[0].Data.(relaytypes.ChainStatus)) + want2 := commontypes.ChainStatus{ID: "2", Enabled: true, Config: config2} + assert.Equal(t, want2, results[0].Data.(commontypes.ChainStatus)) config1, err := chain.TOMLString() require.NoError(t, err) - want1 := relaytypes.ChainStatus{ID: "1", Enabled: true, Config: config1} - assert.Equal(t, want1, results[1].Data.(relaytypes.ChainStatus)) + want1 := commontypes.ChainStatus{ID: "1", Enabled: true, Config: config1} + assert.Equal(t, want1, results[1].Data.(commontypes.ChainStatus)) assert.Nil(t, results[2].Data) assert.Error(t, results[2].Error) assert.ErrorIs(t, results[2].Error, chains.ErrNotFound) @@ -69,13 +69,13 @@ func TestLoader_Nodes(t *testing.T) { chainID1, chainID2, notAnID := big.NewInt(1), big.NewInt(2), big.NewInt(3) - genNodeStat := func(id string) relaytypes.NodeStatus { - return relaytypes.NodeStatus{ + genNodeStat := func(id string) commontypes.NodeStatus { + return commontypes.NodeStatus{ Name: "test-node-" + id, ChainID: id, } } - rcInterops := &chainlinkmocks.FakeRelayerChainInteroperators{Nodes: []relaytypes.NodeStatus{ + rcInterops := &chainlinkmocks.FakeRelayerChainInteroperators{Nodes: []commontypes.NodeStatus{ genNodeStat(chainID2.String()), genNodeStat(chainID1.String()), }} @@ -86,9 +86,9 @@ func TestLoader_Nodes(t *testing.T) { found := batcher.loadByChainIDs(ctx, keys) require.Len(t, found, 3) - assert.Equal(t, []relaytypes.NodeStatus{genNodeStat(chainID2.String())}, found[0].Data) - assert.Equal(t, []relaytypes.NodeStatus{genNodeStat(chainID1.String())}, found[1].Data) - assert.Equal(t, []relaytypes.NodeStatus{}, found[2].Data) + assert.Equal(t, []commontypes.NodeStatus{genNodeStat(chainID2.String())}, found[0].Data) + assert.Equal(t, []commontypes.NodeStatus{genNodeStat(chainID1.String())}, found[1].Data) + assert.Equal(t, []commontypes.NodeStatus{}, found[2].Data) } func TestLoader_FeedsManagers(t *testing.T) { diff --git a/core/web/loader/node.go b/core/web/loader/node.go index 9ea6062dc29..ef8e363d9f3 100644 --- a/core/web/loader/node.go +++ b/core/web/loader/node.go @@ -5,7 +5,7 @@ import ( "github.com/graph-gophers/dataloader" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/relay" diff --git a/core/web/loop_registry_test.go b/core/web/loop_registry_test.go index ea766725641..94e2bc856d6 100644 --- a/core/web/loop_registry_test.go +++ b/core/web/loop_registry_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" diff --git a/core/web/nodes_controller.go b/core/web/nodes_controller.go index 3547b150bc7..04c4693983f 100644 --- a/core/web/nodes_controller.go +++ b/core/web/nodes_controller.go @@ -7,7 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/manyminds/api2go/jsonapi" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" diff --git a/core/web/presenters/bridges.go b/core/web/presenters/bridges.go index 440e444c613..82f4a961c94 100644 --- a/core/web/presenters/bridges.go +++ b/core/web/presenters/bridges.go @@ -3,7 +3,7 @@ package presenters import ( "time" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" ) diff --git a/core/web/presenters/bridges_test.go b/core/web/presenters/bridges_test.go index f6ce3af7561..746d361958a 100644 --- a/core/web/presenters/bridges_test.go +++ b/core/web/presenters/bridges_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/core/web/presenters/cosmos_chain.go b/core/web/presenters/cosmos_chain.go index d65a110287d..c3b006e5c7e 100644 --- a/core/web/presenters/cosmos_chain.go +++ b/core/web/presenters/cosmos_chain.go @@ -1,7 +1,7 @@ package presenters import ( - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) // CosmosChainResource is an Cosmos chain JSONAPI resource. diff --git a/core/web/presenters/eth_key.go b/core/web/presenters/eth_key.go index 92654427619..3d952dabeda 100644 --- a/core/web/presenters/eth_key.go +++ b/core/web/presenters/eth_key.go @@ -3,7 +3,7 @@ package presenters import ( "time" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -13,14 +13,14 @@ import ( // representation of the address plus its ETH & LINK balances type ETHKeyResource struct { JAID - EVMChainID utils.Big `json:"evmChainID"` - Address string `json:"address"` - EthBalance *assets.Eth `json:"ethBalance"` - LinkBalance *relayassets.Link `json:"linkBalance"` - Disabled bool `json:"disabled"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - MaxGasPriceWei *utils.Big `json:"maxGasPriceWei"` + EVMChainID utils.Big `json:"evmChainID"` + Address string `json:"address"` + EthBalance *assets.Eth `json:"ethBalance"` + LinkBalance *commonassets.Link `json:"linkBalance"` + Disabled bool `json:"disabled"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + MaxGasPriceWei *utils.Big `json:"maxGasPriceWei"` } // GetName implements the api2go EntityNamer interface @@ -63,7 +63,7 @@ func SetETHKeyEthBalance(ethBalance *assets.Eth) NewETHKeyOption { } } -func SetETHKeyLinkBalance(linkBalance *relayassets.Link) NewETHKeyOption { +func SetETHKeyLinkBalance(linkBalance *commonassets.Link) NewETHKeyOption { return func(r *ETHKeyResource) { r.LinkBalance = linkBalance } diff --git a/core/web/presenters/eth_key_test.go b/core/web/presenters/eth_key_test.go index 0e68fbc90c9..85d005cf610 100644 --- a/core/web/presenters/eth_key_test.go +++ b/core/web/presenters/eth_key_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -40,12 +40,12 @@ func TestETHKeyResource(t *testing.T) { r := NewETHKeyResource(key, state, SetETHKeyEthBalance(assets.NewEth(1)), - SetETHKeyLinkBalance(relayassets.NewLinkFromJuels(1)), + SetETHKeyLinkBalance(commonassets.NewLinkFromJuels(1)), SetETHKeyMaxGasPriceWei(utils.NewBigI(12345)), ) assert.Equal(t, assets.NewEth(1), r.EthBalance) - assert.Equal(t, relayassets.NewLinkFromJuels(1), r.LinkBalance) + assert.Equal(t, commonassets.NewLinkFromJuels(1), r.LinkBalance) assert.Equal(t, utils.NewBigI(12345), r.MaxGasPriceWei) b, err := jsonapi.Marshal(r) diff --git a/core/web/presenters/evm_chain.go b/core/web/presenters/evm_chain.go index 25862875ee9..8cc6da46a77 100644 --- a/core/web/presenters/evm_chain.go +++ b/core/web/presenters/evm_chain.go @@ -1,6 +1,6 @@ package presenters -import "github.com/smartcontractkit/chainlink-relay/pkg/types" +import "github.com/smartcontractkit/chainlink-common/pkg/types" // EVMChainResource is an EVM chain JSONAPI resource. type EVMChainResource struct { diff --git a/core/web/presenters/job.go b/core/web/presenters/job.go index d9ec1844aea..dc5bf623330 100644 --- a/core/web/presenters/job.go +++ b/core/web/presenters/job.go @@ -7,7 +7,7 @@ import ( "github.com/lib/pq" "gopkg.in/guregu/null.v4" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -44,7 +44,7 @@ const ( type DirectRequestSpec struct { ContractAddress ethkey.EIP55Address `json:"contractAddress"` MinIncomingConfirmations clnull.Uint32 `json:"minIncomingConfirmations"` - MinContractPayment *relayassets.Link `json:"minContractPaymentLinkJuels"` + MinContractPayment *commonassets.Link `json:"minContractPaymentLinkJuels"` Requesters models.AddressCollection `json:"requesters"` Initiator string `json:"initiator"` CreatedAt time.Time `json:"createdAt"` @@ -81,7 +81,7 @@ type FluxMonitorSpec struct { DrumbeatEnabled bool `json:"drumbeatEnabled"` DrumbeatSchedule *string `json:"drumbeatSchedule"` DrumbeatRandomDelay *string `json:"drumbeatRandomDelay"` - MinPayment *relayassets.Link `json:"minPayment"` + MinPayment *commonassets.Link `json:"minPayment"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` EVMChainID *utils.Big `json:"evmChainID"` diff --git a/core/web/presenters/job_test.go b/core/web/presenters/job_test.go index 46c765a38b2..7e997c899c5 100644 --- a/core/web/presenters/job_test.go +++ b/core/web/presenters/job_test.go @@ -13,7 +13,7 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" 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" diff --git a/core/web/presenters/solana_chain.go b/core/web/presenters/solana_chain.go index ba6cace7c3d..f04d2b65d55 100644 --- a/core/web/presenters/solana_chain.go +++ b/core/web/presenters/solana_chain.go @@ -1,7 +1,7 @@ package presenters import ( - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) // SolanaChainResource is an Solana chain JSONAPI resource. diff --git a/core/web/presenters/starknet_chain.go b/core/web/presenters/starknet_chain.go index 2a9e49e74c5..ec1cd453a55 100644 --- a/core/web/presenters/starknet_chain.go +++ b/core/web/presenters/starknet_chain.go @@ -1,7 +1,7 @@ package presenters import ( - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) // StarkNetChainResource is an StarkNet chain JSONAPI resource. diff --git a/core/web/resolver/bridge_test.go b/core/web/resolver/bridge_test.go index e708ac92a42..71ef4a2b340 100644 --- a/core/web/resolver/bridge_test.go +++ b/core/web/resolver/bridge_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/store/models" ) diff --git a/core/web/resolver/chain.go b/core/web/resolver/chain.go index 53f1016d72b..32e9a8caac3 100644 --- a/core/web/resolver/chain.go +++ b/core/web/resolver/chain.go @@ -3,7 +3,7 @@ package resolver import ( "github.com/graph-gophers/graphql-go" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) // ChainResolver resolves the Chain type. diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index f8f417ca44b..d75282e0fbd 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/mock" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -96,7 +96,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) - f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(relayassets.NewLinkFromJuels(12), nil) + f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(commonassets.NewLinkFromJuels(12), nil) f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) f.Mocks.balM.On("GetEthBalance", address).Return(assets.NewEth(1)) f.Mocks.chain.On("BalanceMonitor").Return(f.Mocks.balM) @@ -301,7 +301,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) f.Mocks.keystore.On("Eth").Return(f.Mocks.ethKs) - f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(relayassets.NewLinkFromJuels(12), gError) + f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(commonassets.NewLinkFromJuels(12), gError) f.Mocks.legacyEVMChains.On("Get", states[0].EVMChainID.String()).Return(f.Mocks.chain, nil) f.Mocks.relayerChainInterops.EVMChains = f.Mocks.legacyEVMChains f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) @@ -354,7 +354,7 @@ func TestResolver_ETHKeys(t *testing.T) { f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) f.Mocks.ethKs.On("GetAll").Return(keys, nil) - f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(relayassets.NewLinkFromJuels(12), nil) + f.Mocks.ethClient.On("LINKBalance", mock.Anything, address, linkAddr).Return(commonassets.NewLinkFromJuels(12), nil) f.Mocks.chain.On("Client").Return(f.Mocks.ethClient) f.Mocks.chain.On("BalanceMonitor").Return(nil) f.Mocks.chain.On("Config").Return(f.Mocks.scfg) diff --git a/core/web/resolver/helpers.go b/core/web/resolver/helpers.go index 2dc865674e2..36080a8b546 100644 --- a/core/web/resolver/helpers.go +++ b/core/web/resolver/helpers.go @@ -8,7 +8,7 @@ import ( "github.com/graph-gophers/graphql-go" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/utils/stringutils" ) diff --git a/core/web/resolver/mutation.go b/core/web/resolver/mutation.go index 52f914e0a84..990a6c08058 100644 --- a/core/web/resolver/mutation.go +++ b/core/web/resolver/mutation.go @@ -13,7 +13,7 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" - "github.com/smartcontractkit/chainlink-relay/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" diff --git a/core/web/resolver/node.go b/core/web/resolver/node.go index 39a85e83b13..8e01b7056be 100644 --- a/core/web/resolver/node.go +++ b/core/web/resolver/node.go @@ -7,7 +7,7 @@ import ( "github.com/graph-gophers/graphql-go" "github.com/pelletier/go-toml/v2" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" evmtoml "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/web/loader" diff --git a/core/web/resolver/node_test.go b/core/web/resolver/node_test.go index 9f34b274201..24a31b986f1 100644 --- a/core/web/resolver/node_test.go +++ b/core/web/resolver/node_test.go @@ -6,7 +6,7 @@ import ( gqlerrors "github.com/graph-gophers/graphql-go/errors" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" diff --git a/core/web/resolver/query.go b/core/web/resolver/query.go index e9fd18cf19a..da15b7f7c26 100644 --- a/core/web/resolver/query.go +++ b/core/web/resolver/query.go @@ -10,7 +10,7 @@ import ( "github.com/graph-gophers/graphql-go" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" diff --git a/core/web/resolver/spec_test.go b/core/web/resolver/spec_test.go index fd369088bb8..4de66f1dcb1 100644 --- a/core/web/resolver/spec_test.go +++ b/core/web/resolver/spec_test.go @@ -9,8 +9,8 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" clnull "github.com/smartcontractkit/chainlink/v2/core/null" @@ -97,7 +97,7 @@ func TestResolver_DirectRequestSpec(t *testing.T) { CreatedAt: f.Timestamp(), EVMChainID: utils.NewBigI(42), MinIncomingConfirmations: clnull.NewUint32(1, true), - MinContractPayment: relayassets.NewLinkFromJuels(1000), + MinContractPayment: commonassets.NewLinkFromJuels(1000), Requesters: models.AddressCollection{requesterAddress}, }, }, nil) @@ -164,7 +164,7 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { DrumbeatEnabled: false, IdleTimerDisabled: false, IdleTimerPeriod: time.Duration(1 * time.Hour), - MinPayment: relayassets.NewLinkFromJuels(1000), + MinPayment: commonassets.NewLinkFromJuels(1000), PollTimerDisabled: false, PollTimerPeriod: time.Duration(1 * time.Minute), }, @@ -233,7 +233,7 @@ func TestResolver_FluxMonitorSpec(t *testing.T) { DrumbeatSchedule: "CRON_TZ=UTC 0 0 1 1 *", IdleTimerDisabled: true, IdleTimerPeriod: time.Duration(1 * time.Hour), - MinPayment: relayassets.NewLinkFromJuels(1000), + MinPayment: commonassets.NewLinkFromJuels(1000), PollTimerDisabled: true, PollTimerPeriod: time.Duration(1 * time.Minute), }, diff --git a/core/web/solana_chains_controller_test.go b/core/web/solana_chains_controller_test.go index c4023f166bb..1377cb65aba 100644 --- a/core/web/solana_chains_controller_test.go +++ b/core/web/solana_chains_controller_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - relaycfg "github.com/smartcontractkit/chainlink-relay/pkg/config" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + commoncfg "github.com/smartcontractkit/chainlink-common/pkg/config" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" @@ -84,7 +84,7 @@ Nodes = [] ChainID: ptr(validId), Chain: config.Chain{ SkipPreflight: ptr(false), - TxTimeout: relaycfg.MustNewDuration(time.Hour), + TxTimeout: commoncfg.MustNewDuration(time.Hour), }, }) @@ -114,7 +114,7 @@ func Test_SolanaChainsController_Index(t *testing.T) { chainA := &solana.TOMLConfig{ ChainID: ptr(fmt.Sprintf("ChainlinktestA-%d", rand.Int31n(999999))), Chain: config.Chain{ - TxTimeout: relaycfg.MustNewDuration(time.Hour), + TxTimeout: commoncfg.MustNewDuration(time.Hour), }, } chainB := &solana.TOMLConfig{ diff --git a/go.mod b/go.mod index 40803d504ce..784d3ff78e1 100644 --- a/go.mod +++ b/go.mod @@ -65,10 +65,10 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 diff --git a/go.sum b/go.sum index 52975e722a4..fb09e792d6d 100644 --- a/go.sum +++ b/go.sum @@ -1463,14 +1463,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd h1:PRVJxNK67pQWufXuB1cxckH/xZkcQFDy8KjN9ZYqong= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd/go.mod h1:rOayi690YxLlkQy959PD8INhOAIAUi9LoN0G+J/CEf4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index f48d4157922..fd15d51af4e 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -9,7 +9,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" @@ -869,7 +869,7 @@ func retreiveLoadTestMetrics( func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2_5.GetSubscription, subID *big.Int, coordinator contracts.VRFCoordinatorV2_5) { l.Debug(). Str("Coordinator", coordinator.Address()). - Str("Link Balance", (*relayassets.Link)(subscription.Balance).Link()). + Str("Link Balance", (*commonassets.Link)(subscription.Balance).Link()). Str("Native Token Balance", assets.FormatWei(subscription.NativeBalance)). Str("Subscription ID", subID.String()). Str("Subscription Owner", subscription.Owner.String()). @@ -972,11 +972,11 @@ func LogFulfillmentDetailsLinkBilling( randomWordsFulfilledEvent *vrf_coordinator_v2_5.VRFCoordinatorV25RandomWordsFulfilled, ) { l.Debug(). - Str("Consumer Balance Before Request (Link)", (*relayassets.Link)(wrapperConsumerJuelsBalanceBeforeRequest).Link()). - Str("Consumer Balance After Request (Link)", (*relayassets.Link)(wrapperConsumerJuelsBalanceAfterRequest).Link()). + Str("Consumer Balance Before Request (Link)", (*commonassets.Link)(wrapperConsumerJuelsBalanceBeforeRequest).Link()). + Str("Consumer Balance After Request (Link)", (*commonassets.Link)(wrapperConsumerJuelsBalanceAfterRequest).Link()). Bool("Fulfilment Status", consumerStatus.Fulfilled). - Str("Paid by Consumer Contract (Link)", (*relayassets.Link)(consumerStatus.Paid).Link()). - Str("Paid by Coordinator Sub (Link)", (*relayassets.Link)(randomWordsFulfilledEvent.Payment).Link()). + Str("Paid by Consumer Contract (Link)", (*commonassets.Link)(consumerStatus.Paid).Link()). + Str("Paid by Coordinator Sub (Link)", (*commonassets.Link)(randomWordsFulfilledEvent.Payment).Link()). Str("RequestTimestamp", consumerStatus.RequestTimestamp.String()). Str("FulfilmentTimestamp", consumerStatus.FulfilmentTimestamp.String()). Str("RequestBlockNumber", consumerStatus.RequestBlockNumber.String()). diff --git a/integration-tests/go.mod b/integration-tests/go.mod index aed5e0682cb..3b1a5423df8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,7 +22,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e github.com/smartcontractkit/chainlink-testing-framework v1.19.1 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 @@ -387,9 +387,9 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 896ae8c6d27..427ad1421c9 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2367,14 +2367,14 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255 h1:Pt6c7bJU9wIN6PQQnmN8UmYYH6lpfiQ6U/B8yEC2s5s= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231109141932-cb1ea9020255/go.mod h1:EHppaccd/LTlTMI2o4dmBHe4BknEgEFFDjDGMNuGb3k= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd h1:PRVJxNK67pQWufXuB1cxckH/xZkcQFDy8KjN9ZYqong= -github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231115124244-8303409abccd/go.mod h1:rOayi690YxLlkQy959PD8INhOAIAUi9LoN0G+J/CEf4= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/chainlink-testing-framework v1.19.1 h1:MdGM5jIrBi858Cv7qzfl1Qon93YW8InohAlDQqFoIb4= github.com/smartcontractkit/chainlink-testing-framework v1.19.1/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 6da1d94371a..ad85506a04c 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -11,7 +11,7 @@ import ( "github.com/segmentio/ksuid" - relayassets "github.com/smartcontractkit/chainlink-relay/pkg/assets" + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -171,7 +171,7 @@ func SetChainConfig( chainConfig = evmcfg.Chain{ AutoCreateKey: ptr.Ptr(true), FinalityDepth: ptr.Ptr[uint32](1), - MinContractPayment: relayassets.NewLinkFromJuels(0), + MinContractPayment: commonassets.NewLinkFromJuels(0), } } cfg.EVM = evmcfg.EVMConfigs{ @@ -197,7 +197,7 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { Chain: evmcfg.Chain{ AutoCreateKey: ptr.Ptr(true), FinalityDepth: ptr.Ptr[uint32](50), - MinContractPayment: relayassets.NewLinkFromJuels(0), + MinContractPayment: commonassets.NewLinkFromJuels(0), LogPollInterval: models.MustNewDuration(1 * time.Second), HeadTracker: evmcfg.HeadTracker{ HistoryDepth: ptr.Ptr(uint32(100)), diff --git a/plugins/cmd/chainlink-median/main.go b/plugins/cmd/chainlink-median/main.go index 87815d24d99..e95bfbb221b 100644 --- a/plugins/cmd/chainlink-median/main.go +++ b/plugins/cmd/chainlink-median/main.go @@ -3,7 +3,7 @@ package main import ( "github.com/hashicorp/go-plugin" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" ) diff --git a/plugins/cmd/chainlink-medianpoc/main.go b/plugins/cmd/chainlink-medianpoc/main.go index 325de6538fa..eaef96d1b0a 100644 --- a/plugins/cmd/chainlink-medianpoc/main.go +++ b/plugins/cmd/chainlink-medianpoc/main.go @@ -3,9 +3,9 @@ package main import ( "github.com/hashicorp/go-plugin" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/plugins/medianpoc" ) diff --git a/plugins/config.go b/plugins/config.go index 938cfd0d00c..01574d82099 100644 --- a/plugins/config.go +++ b/plugins/config.go @@ -3,7 +3,7 @@ package plugins import ( "os/exec" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop" ) // RegistrarConfig generates contains static configuration inher diff --git a/plugins/loop_registry.go b/plugins/loop_registry.go index f402fc6fa17..17ad7cba5ad 100644 --- a/plugins/loop_registry.go +++ b/plugins/loop_registry.go @@ -5,8 +5,8 @@ import ( "sort" "sync" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/config" ) diff --git a/plugins/medianpoc/data_source.go b/plugins/medianpoc/data_source.go index 7b20f1e5eb3..92d4b04ba6c 100644 --- a/plugins/medianpoc/data_source.go +++ b/plugins/medianpoc/data_source.go @@ -10,8 +10,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/plugins/medianpoc/data_source_test.go b/plugins/medianpoc/data_source_test.go index e9a7945cee4..5848705b7b9 100644 --- a/plugins/medianpoc/data_source_test.go +++ b/plugins/medianpoc/data_source_test.go @@ -6,13 +6,14 @@ import ( "math/big" "testing" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" ) type mockPipelineRunner struct { diff --git a/plugins/medianpoc/plugin.go b/plugins/medianpoc/plugin.go index ceea1eb84f5..62b6acc043a 100644 --- a/plugins/medianpoc/plugin.go +++ b/plugins/medianpoc/plugin.go @@ -9,11 +9,11 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink-relay/pkg/logger" - "github.com/smartcontractkit/chainlink-relay/pkg/loop" - "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins" - "github.com/smartcontractkit/chainlink-relay/pkg/services" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/loop" + "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index 74a0695c6c9..569fcb464bc 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" - "github.com/smartcontractkit/chainlink-relay/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/logger" ) From 3d25a9e6b858438035e46691cce6e192f6d3fe46 Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 17 Nov 2023 17:35:05 -0500 Subject: [PATCH 175/271] [TT-724] Fix P2Pv2 For Soak Tests (#11331) * Fix P2Pv2 For Soak Tests * Update default * Tidy * Fix all OCR1 Configs * Tidy? * Tweaked the right setting --- .github/workflows/on-demand-ocr-soak-test.yml | 2 +- integration-tests/chaos/ocr_chaos_test.go | 2 +- integration-tests/config/config.go | 10 +++------- integration-tests/performance/ocr_test.go | 8 ++------ integration-tests/soak/ocr_test.go | 11 +++++++---- integration-tests/testsetups/ocr.go | 2 +- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml index b17fdc6beb4..567d9510de9 100644 --- a/.github/workflows/on-demand-ocr-soak-test.yml +++ b/.github/workflows/on-demand-ocr-soak-test.yml @@ -54,7 +54,7 @@ on: chainlinkVersion: description: Container image version for the Chainlink nodes required: true - default: "1.11.0" + default: "2.7.0" testDuration: description: Duration of the test (time string) required: false diff --git a/integration-tests/chaos/ocr_chaos_test.go b/integration-tests/chaos/ocr_chaos_test.go index 5368997daab..a59a0a028c2 100644 --- a/integration-tests/chaos/ocr_chaos_test.go +++ b/integration-tests/chaos/ocr_chaos_test.go @@ -52,7 +52,7 @@ var ( ) func TestMain(m *testing.M) { - defaultOCRSettings["toml"] = networks.AddNetworksConfig(config.BaseOCRP2PV1Config, networks.MustGetSelectedNetworksFromEnv()[0]) + defaultOCRSettings["toml"] = networks.AddNetworksConfig(config.BaseOCR1Config, networks.MustGetSelectedNetworksFromEnv()[0]) os.Exit(m.Run()) } diff --git a/integration-tests/config/config.go b/integration-tests/config/config.go index 1da8254e0ed..6d00ceb32bc 100644 --- a/integration-tests/config/config.go +++ b/integration-tests/config/config.go @@ -1,17 +1,13 @@ package config var ( - BaseOCRP2PV1Config = `[OCR] + BaseOCR1Config = `[OCR] Enabled = true [P2P] [P2P.V2] -Enabled = false - -[P2P.V1] -Enabled = true -ListenIP = '0.0.0.0' -ListenPort = 6690` +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"]` BaseOCR2Config = `[Feature] LogPoller = true diff --git a/integration-tests/performance/ocr_test.go b/integration-tests/performance/ocr_test.go index 7f91f4321ac..7e0ed0111d8 100644 --- a/integration-tests/performance/ocr_test.go +++ b/integration-tests/performance/ocr_test.go @@ -103,13 +103,9 @@ Enabled = true [P2P] [P2P.V2] -Enabled = false +AnnounceAddresses = ["0.0.0.0:6690"] +ListenAddresses = ["0.0.0.0:6690"]` -[P2P] -[P2P.V1] -Enabled = true -ListenIP = '0.0.0.0' -ListenPort = 6690` cd := chainlink.New(0, map[string]interface{}{ "replicas": 6, "toml": networks.AddNetworksConfig(baseTOML, testNetwork), diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index c7d4bc80f61..4f42ff803e5 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -1,13 +1,16 @@ package soak import ( + "fmt" "testing" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -16,10 +19,10 @@ func TestOCRSoak(t *testing.T) { // Use this variable to pass in any custom EVM specific TOML values to your Chainlink nodes customNetworkTOML := `` // Uncomment below for debugging TOML issues on the node - // network := networks.MustGetSelectedNetworksFromEnv()[0] - // fmt.Println("Using Chainlink TOML\n---------------------") - // fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customNetworkTOML, network)) - // fmt.Println("---------------------") + network := networks.MustGetSelectedNetworksFromEnv()[0] + fmt.Println("Using Chainlink TOML\n---------------------") + fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCR1Config, customNetworkTOML, network)) + fmt.Println("---------------------") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, false) require.NoError(t, err, "Error creating soak test") diff --git a/integration-tests/testsetups/ocr.go b/integration-tests/testsetups/ocr.go index dfeb4bb916d..a35d915ea92 100644 --- a/integration-tests/testsetups/ocr.go +++ b/integration-tests/testsetups/ocr.go @@ -142,7 +142,7 @@ func (o *OCRSoakTest) DeployEnvironment(customChainlinkNetworkTOML string) { cd := chainlink.New(0, map[string]any{ "replicas": 6, - "toml": networks.AddNetworkDetailedConfig(config.BaseOCRP2PV1Config, customChainlinkNetworkTOML, network), + "toml": networks.AddNetworkDetailedConfig(config.BaseOCR1Config, customChainlinkNetworkTOML, network), "db": map[string]any{ "stateful": true, // stateful DB by default for soak tests }, From 7e1b9cec1c453713779c3b2358050e95e4c30782 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 20 Nov 2023 10:47:36 +0100 Subject: [PATCH 176/271] bump packages (#11325) --- contracts/package.json | 26 +-- contracts/pnpm-lock.yaml | 382 +++++++++++++++++++-------------------- 2 files changed, 201 insertions(+), 207 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index 6d0b7af6ccd..2ceb1602dca 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 371 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 369 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", @@ -47,32 +47,32 @@ "@typechain/ethers-v5": "^7.2.0", "@typechain/hardhat": "^7.0.0", "@types/cbor": "5.0.1", - "@types/chai": "^4.3.9", - "@types/debug": "^4.1.10", - "@types/deep-equal-in-any-order": "^1.0.2", - "@types/mocha": "^10.0.3", - "@types/node": "^16.18.58", - "@typescript-eslint/eslint-plugin": "^6.8.0", - "@typescript-eslint/parser": "^6.8.0", + "@types/chai": "^4.3.10", + "@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", "abi-to-sol": "^0.6.6", "cbor": "^5.2.0", "chai": "^4.3.10", "debug": "^4.3.4", - "eslint": "^8.51.0", + "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.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.18.1", + "hardhat": "~2.19.1", "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.0.3", - "prettier-plugin-solidity": "1.1.4-dev", + "prettier": "^3.1.0", + "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", @@ -80,7 +80,7 @@ "solidity-coverage": "^0.8.5", "ts-node": "^10.9.1", "tslib": "^2.6.2", - "typechain": "^8.2.0", + "typechain": "^8.2.1", "typescript": "^5.2.2" }, "dependencies": { diff --git a/contracts/pnpm-lock.yaml b/contracts/pnpm-lock.yaml index ac4efd5f59f..fbae71fb3d4 100644 --- a/contracts/pnpm-lock.yaml +++ b/contracts/pnpm-lock.yaml @@ -39,52 +39,52 @@ devDependencies: version: 5.7.0 '@nomicfoundation/hardhat-network-helpers': specifier: ^1.0.9 - version: 1.0.9(hardhat@2.18.1) + version: 1.0.9(hardhat@2.19.1) '@nomiclabs/hardhat-ethers': specifier: ^2.2.3 - version: 2.2.3(ethers@5.7.2)(hardhat@2.18.1) + version: 2.2.3(ethers@5.7.2)(hardhat@2.19.1) '@nomiclabs/hardhat-etherscan': specifier: ^3.1.7 - version: 3.1.7(hardhat@2.18.1) + version: 3.1.7(hardhat@2.19.1) '@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.18.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.1) '@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.18.1) + version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(ethers@5.7.2)(hardhat@2.19.1) '@openzeppelin/test-helpers': specifier: ^0.5.16 version: 0.5.16(bn.js@4.12.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.2.0)(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.2.2) '@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.18.1)(typechain@8.2.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) '@types/cbor': specifier: 5.0.1 version: 5.0.1 '@types/chai': - specifier: ^4.3.9 - version: 4.3.9 + specifier: ^4.3.10 + version: 4.3.10 '@types/debug': - specifier: ^4.1.10 - version: 4.1.10 + specifier: ^4.1.12 + version: 4.1.12 '@types/deep-equal-in-any-order': - specifier: ^1.0.2 - version: 1.0.2 + specifier: ^1.0.3 + version: 1.0.3 '@types/mocha': - specifier: ^10.0.3 - version: 10.0.3 + specifier: ^10.0.4 + version: 10.0.4 '@types/node': - specifier: ^16.18.58 - version: 16.18.58 + specifier: ^16.18.61 + version: 16.18.61 '@typescript-eslint/eslint-plugin': - specifier: ^6.8.0 - version: 6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.51.0)(typescript@5.2.2) + specifier: ^6.11.0 + version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2) '@typescript-eslint/parser': - specifier: ^6.8.0 - version: 6.8.0(eslint@8.51.0)(typescript@5.2.2) + specifier: ^6.11.0 + version: 6.11.0(eslint@8.53.0)(typescript@5.2.2) abi-to-sol: specifier: ^0.6.6 version: 0.6.6 @@ -101,14 +101,14 @@ devDependencies: specifier: ^2.0.6 version: 2.0.6 eslint: - specifier: ^8.51.0 - version: 8.51.0 + specifier: ^8.53.0 + version: 8.53.0 eslint-config-prettier: specifier: ^9.0.0 - version: 9.0.0(eslint@8.51.0) + version: 9.0.0(eslint@8.53.0) eslint-plugin-prettier: specifier: ^5.0.1 - version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.51.0)(prettier@3.0.3) + version: 5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.1.0) ethereum-waffle: specifier: ^3.4.4 version: 3.4.4(typescript@5.2.2) @@ -116,17 +116,17 @@ devDependencies: specifier: ~5.7.2 version: 5.7.2 hardhat: - specifier: ~2.18.1 - version: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + specifier: ~2.19.1 + version: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) hardhat-abi-exporter: specifier: ^2.10.1 - version: 2.10.1(hardhat@2.18.1) + version: 2.10.1(hardhat@2.19.1) hardhat-contract-sizer: specifier: ^2.10.0 - version: 2.10.0(hardhat@2.18.1) + version: 2.10.0(hardhat@2.19.1) hardhat-gas-reporter: specifier: ^1.0.9 - version: 1.0.9(debug@4.3.4)(hardhat@2.18.1) + version: 1.0.9(debug@4.3.4)(hardhat@2.19.1) hardhat-ignore-warnings: specifier: ^0.2.6 version: 0.2.9 @@ -137,11 +137,11 @@ devDependencies: specifier: ^2.29.4 version: 2.29.4 prettier: - specifier: ^3.0.3 - version: 3.0.3 + specifier: ^3.1.0 + version: 3.1.0 prettier-plugin-solidity: - specifier: 1.1.4-dev - version: 1.1.4-dev(prettier@3.0.3) + specifier: 1.2.0 + version: 1.2.0(prettier@3.1.0) rlp: specifier: ^2.2.7 version: 2.2.7 @@ -153,19 +153,19 @@ devDependencies: version: github.com/smartcontractkit/chainlink-solhint-rules/cfc50b32f95b730304a50deb2e27e88d87115874 solhint-plugin-prettier: specifier: ^0.1.0 - version: 0.1.0(prettier-plugin-solidity@1.1.4-dev)(prettier@3.0.3) + version: 0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.0) solidity-coverage: specifier: ^0.8.5 - version: 0.8.5(hardhat@2.18.1) + version: 0.8.5(hardhat@2.19.1) ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@16.18.58)(typescript@5.2.2) + version: 10.9.1(@types/node@16.18.61)(typescript@5.2.2) tslib: specifier: ^2.6.2 version: 2.6.2 typechain: - specifier: ^8.2.0 - version: 8.2.0(typescript@5.2.2) + specifier: ^8.2.1 + version: 8.3.2(typescript@5.2.2) typescript: specifier: ^5.2.2 version: 5.2.2 @@ -324,13 +324,13 @@ packages: deprecated: Please use @ensdomains/ens-contracts dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.51.0): + /@eslint-community/eslint-utils@4.4.0(eslint@8.53.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.51.0 + eslint: 8.53.0 eslint-visitor-keys: 3.4.3 dev: true @@ -339,8 +339,8 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc@2.1.2: - resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + /@eslint/eslintrc@2.1.3: + resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 @@ -356,8 +356,8 @@ packages: - supports-color dev: true - /@eslint/js@8.51.0: - resolution: {integrity: sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==} + /@eslint/js@8.53.0: + resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true @@ -789,11 +789,11 @@ packages: '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 - /@humanwhocodes/config-array@0.11.11: - resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + /@humanwhocodes/config-array@0.11.13: + resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 1.2.1 + '@humanwhocodes/object-schema': 2.0.1 debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: @@ -805,8 +805,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + /@humanwhocodes/object-schema@2.0.1: + resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true /@jridgewell/resolve-uri@3.1.1: @@ -1037,13 +1037,13 @@ packages: - utf-8-validate dev: true - /@nomicfoundation/hardhat-network-helpers@1.0.9(hardhat@2.18.1): + /@nomicfoundation/hardhat-network-helpers@1.0.9(hardhat@2.19.1): resolution: {integrity: sha512-OXWCv0cHpwLUO2u7bFxBna6dQtCC2Gg/aN/KtJLO7gmuuA28vgmVKYFRCDUqrbjujzgfwQ2aKyZ9Y3vSmDqS7Q==} peerDependencies: hardhat: ^2.9.5 dependencies: ethereumjs-util: 7.1.5 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) dev: true /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0: @@ -1152,17 +1152,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.18.1): + /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.19.1): resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} peerDependencies: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) dev: true - /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.18.1): + /@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.19.1): resolution: {integrity: sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==} peerDependencies: hardhat: ^2.0.4 @@ -1173,7 +1173,7 @@ packages: chalk: 2.4.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 7.0.1 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) lodash: 4.17.21 semver: 6.3.0 table: 6.8.1 @@ -1182,7 +1182,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.18.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.1): resolution: {integrity: sha512-+Wz0hwmJGSI17B+BhU/qFRZ1l6/xMW82QGXE/Gi+WTmwgJrQefuBs1lIf7hzQ1hLk6hpkvb/zwcNkpVKRYTQYg==} peerDependencies: '@nomiclabs/hardhat-ethers': ^2.0.0 @@ -1191,11 +1191,11 @@ packages: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.18.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.1) '@types/sinon-chai': 3.2.8 ethereum-waffle: 3.4.4(typescript@5.2.2) ethers: 5.7.2 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) dev: true /@openzeppelin/contract-loader@0.6.3: @@ -1226,7 +1226,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.18.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.1): resolution: {integrity: sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ==} hasBin: true peerDependencies: @@ -1239,15 +1239,15 @@ packages: '@nomiclabs/harhdat-etherscan': optional: true dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.18.1) - '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.18.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.19.1) + '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.19.1) '@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.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) proper-lockfile: 4.1.2 transitivePeerDependencies: - encoding @@ -1338,12 +1338,12 @@ packages: config-chain: 1.1.13 dev: true - /@prettier/sync@0.3.0(prettier@3.0.3): + /@prettier/sync@0.3.0(prettier@3.1.0): resolution: {integrity: sha512-3dcmCyAxIcxy036h1I7MQU/uEEBq8oLwf1CE3xeze+MPlgkdlb/+w6rGR/1dhp6Hqi17fRS6nvwnOzkESxEkOw==} peerDependencies: prettier: ^3.0.0 dependencies: - prettier: 3.0.3 + prettier: 3.1.0 dev: true /@resolver-engine/core@0.3.3: @@ -1506,8 +1506,8 @@ packages: antlr4ts: 0.5.0-alpha.4 dev: true - /@solidity-parser/parser@0.16.1: - resolution: {integrity: sha512-PdhRFNhbTtu3x8Axm0uYpqOy/lODYQK+MlYSgqIsq2L8SFYEHJPHNUiOTAJbDGzNjjr1/n9AcIayxafR/fWmYw==} + /@solidity-parser/parser@0.16.2: + resolution: {integrity: sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==} dependencies: antlr4ts: 0.5.0-alpha.4 dev: true @@ -1663,7 +1663,7 @@ packages: typechain: 3.0.0(typescript@5.2.2) 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.2.0)(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.2.2): resolution: {integrity: sha512-jfcmlTvaaJjng63QsT49MT6R1HFhtO/TBMWbyzPFSzMmVIqb2tL6prnKBs4ZJrSvmgIXWy+ttSjpaxCTq8D/Tw==} peerDependencies: '@ethersproject/abi': ^5.0.0 @@ -1679,11 +1679,11 @@ packages: ethers: 5.7.2 lodash: 4.17.21 ts-essentials: 7.0.3(typescript@5.2.2) - typechain: 8.2.0(typescript@5.2.2) + typechain: 8.3.2(typescript@5.2.2) typescript: 5.2.2 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.18.1)(typechain@8.2.0): + /@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): resolution: {integrity: sha512-XB79i5ewg9Met7gMVGfgVkmypicbnI25T5clJBEooMoW2161p4zvKFpoS2O+lBppQyMrPIZkdvl2M3LMDayVcA==} peerDependencies: '@ethersproject/abi': ^5.4.7 @@ -1695,23 +1695,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.2.0)(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.2.2) ethers: 5.7.2 fs-extra: 9.1.0 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) - typechain: 8.2.0(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) + typechain: 8.3.2(typescript@5.2.2) dev: true /@types/bn.js@4.11.6: resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/cacheable-request@6.0.2: @@ -1719,34 +1719,34 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 16.18.58 + '@types/node': 16.18.61 '@types/responselike': 1.0.0 dev: true /@types/cbor@5.0.1: resolution: {integrity: sha512-zVqJy2KzusZPLOgyGJDnOIbu3DxIGGqxYbEwtEEe4Z+la8jwIhOyb+GMrlHafs5tvKruwf8f8qOYP6zTvse/pw==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true - /@types/chai@4.3.9: - resolution: {integrity: sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==} + /@types/chai@4.3.10: + resolution: {integrity: sha512-of+ICnbqjmFCiixUnqRulbylyXQrPqIGf/B3Jax1wIF3DvSheysQxAWvqHhZiW3IQrycvokcLcFQlveGp+vyNg==} dev: true /@types/concat-stream@1.6.1: resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true - /@types/debug@4.1.10: - resolution: {integrity: sha512-tOSCru6s732pofZ+sMv9o4o3Zc+Sa8l3bxd/tweTQudFn06vAzb13ZX46Zi6m6EJ+RUbRTHvgQJ1gBtSgkaUYA==} + /@types/debug@4.1.12: + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: '@types/ms': 0.7.31 dev: true - /@types/deep-equal-in-any-order@1.0.2: - resolution: {integrity: sha512-IUjUMsroT9qb8d7ySAl5V+iT/7UsJDpIFspTLxBWxCLqeJwwptSvmjvDBEEaZt0qodMyUSoOQkLhqZAkqt6dLg==} + /@types/deep-equal-in-any-order@1.0.3: + resolution: {integrity: sha512-jT0O3hAILDKeKbdWJ9FZLD0Xdfhz7hMvfyFlRWpirjiEVr8G+GZ4kVIzPIqM6x6Rpp93TNPgOAed4XmvcuV6Qg==} dev: true /@types/events@3.0.0: @@ -1756,7 +1756,7 @@ packages: /@types/form-data@0.0.33: resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/glob@7.1.1: @@ -1764,7 +1764,7 @@ packages: dependencies: '@types/events': 3.0.0 '@types/minimatch': 3.0.3 - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/http-cache-semantics@4.0.1: @@ -1778,7 +1778,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/lru-cache@5.1.1: @@ -1792,11 +1792,11 @@ packages: /@types/mkdirp@0.5.2: resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true - /@types/mocha@10.0.3: - resolution: {integrity: sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==} + /@types/mocha@10.0.4: + resolution: {integrity: sha512-xKU7bUjiFTIttpWaIZ9qvgg+22O1nmbA+HRxdlR+u6TWsGfmFdXrheJoK4fFxrHNVIOBDvDNKZG+LYBpMHpX3w==} dev: true /@types/ms@0.7.31: @@ -1806,7 +1806,7 @@ packages: /@types/node-fetch@2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 form-data: 3.0.1 dev: true @@ -1818,8 +1818,8 @@ packages: resolution: {integrity: sha512-7xHmXm/QJ7cbK2laF+YYD7gb5MggHIIQwqyjin3bpEGiSuvScMQ5JZZXPvRipi1MwckTQbJZROMns/JxdnIL1Q==} dev: true - /@types/node@16.18.58: - resolution: {integrity: sha512-YGncyA25/MaVtQkjWW9r0EFBukZ+JulsLcVZBlGUfIb96OBMjkoRWwQo5IEWJ8Fj06Go3GHw+bjYDitv6BaGsA==} + /@types/node@16.18.61: + resolution: {integrity: sha512-k0N7BqGhJoJzdh6MuQg1V1ragJiXTh8VUBAZTWjJ9cUq23SG0F0xavOwZbhiP4J3y20xd6jxKx+xNUhkMAi76Q==} dev: true /@types/node@8.10.66: @@ -1829,7 +1829,7 @@ packages: /@types/pbkdf2@3.1.0: resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/prettier@2.7.1: @@ -1843,26 +1843,26 @@ packages: /@types/readable-stream@2.3.15: resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 safe-buffer: 5.1.2 dev: true /@types/resolve@0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/secp256k1@4.0.3: resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} dependencies: - '@types/node': 16.18.58 + '@types/node': 16.18.61 dev: true /@types/semver@7.5.0: @@ -1872,7 +1872,7 @@ packages: /@types/sinon-chai@3.2.8: resolution: {integrity: sha512-d4ImIQbT/rKMG8+AXpmcan5T2/PNeSjrYhvkwet6z0p8kzYtfgA32xzOBlbU0yqJfq+/0Ml805iFoODO0LP5/g==} dependencies: - '@types/chai': 4.3.9 + '@types/chai': 4.3.10 '@types/sinon': 10.0.13 dev: true @@ -1886,8 +1886,8 @@ packages: resolution: {integrity: sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==} dev: true - /@typescript-eslint/eslint-plugin@6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==} + /@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==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -1898,13 +1898,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.8.0 - '@typescript-eslint/parser': 6.8.0(eslint@8.51.0)(typescript@5.2.2) - '@typescript-eslint/scope-manager': 6.8.0 - '@typescript-eslint/type-utils': 6.8.0(eslint@8.51.0)(typescript@5.2.2) - '@typescript-eslint/utils': 6.8.0(eslint@8.51.0)(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.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 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.51.0 + eslint: 8.53.0 graphemer: 1.4.0 ignore: 5.2.4 natural-compare: 1.4.0 @@ -1915,8 +1915,8 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@6.8.0(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==} + /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1925,27 +1925,27 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.8.0 - '@typescript-eslint/types': 6.8.0 - '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2) - '@typescript-eslint/visitor-keys': 6.8.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) + '@typescript-eslint/visitor-keys': 6.11.0 debug: 4.3.4(supports-color@8.1.1) - eslint: 8.51.0 + eslint: 8.53.0 typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@6.8.0: - resolution: {integrity: sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==} + /@typescript-eslint/scope-manager@6.11.0: + resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.8.0 - '@typescript-eslint/visitor-keys': 6.8.0 + '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/visitor-keys': 6.11.0 dev: true - /@typescript-eslint/type-utils@6.8.0(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==} + /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -1954,23 +1954,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2) - '@typescript-eslint/utils': 6.8.0(eslint@8.51.0)(typescript@5.2.2) + '@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) debug: 4.3.4(supports-color@8.1.1) - eslint: 8.51.0 + eslint: 8.53.0 ts-api-utils: 1.0.3(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@6.8.0: - resolution: {integrity: sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==} + /@typescript-eslint/types@6.11.0: + resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==} engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/typescript-estree@6.8.0(typescript@5.2.2): - resolution: {integrity: sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==} + /@typescript-eslint/typescript-estree@6.11.0(typescript@5.2.2): + resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -1978,8 +1978,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.8.0 - '@typescript-eslint/visitor-keys': 6.8.0 + '@typescript-eslint/types': 6.11.0 + '@typescript-eslint/visitor-keys': 6.11.0 debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 @@ -1990,33 +1990,37 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@6.8.0(eslint@8.51.0)(typescript@5.2.2): - resolution: {integrity: sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==} + /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.2.2): + resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==} 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.51.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.8.0 - '@typescript-eslint/types': 6.8.0 - '@typescript-eslint/typescript-estree': 6.8.0(typescript@5.2.2) - eslint: 8.51.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 semver: 7.5.4 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@6.8.0: - resolution: {integrity: sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==} + /@typescript-eslint/visitor-keys@6.11.0: + resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.8.0 + '@typescript-eslint/types': 6.11.0 eslint-visitor-keys: 3.4.3 dev: true + /@ungap/structured-clone@1.2.0: + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + dev: true + /@yarnpkg/lockfile@1.1.0: resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} dev: true @@ -2042,7 +2046,7 @@ packages: source-map-support: 0.5.21 optionalDependencies: prettier: 2.8.8 - prettier-plugin-solidity: 1.1.3(prettier@2.8.8) + prettier-plugin-solidity: 1.2.0(prettier@2.8.8) transitivePeerDependencies: - supports-color dev: true @@ -4703,16 +4707,16 @@ packages: source-map: 0.2.0 dev: true - /eslint-config-prettier@9.0.0(eslint@8.51.0): + /eslint-config-prettier@9.0.0(eslint@8.53.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.51.0 + eslint: 8.53.0 dev: true - /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.51.0)(prettier@3.0.3): + /eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.1.0): resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4726,9 +4730,9 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.51.0 - eslint-config-prettier: 9.0.0(eslint@8.51.0) - prettier: 3.0.3 + eslint: 8.53.0 + eslint-config-prettier: 9.0.0(eslint@8.53.0) + prettier: 3.1.0 prettier-linter-helpers: 1.0.0 synckit: 0.8.5 dev: true @@ -4746,18 +4750,19 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.51.0: - resolution: {integrity: sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==} + /eslint@8.53.0: + resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.51.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) '@eslint-community/regexpp': 4.8.0 - '@eslint/eslintrc': 2.1.2 - '@eslint/js': 8.51.0 - '@humanwhocodes/config-array': 0.11.11 + '@eslint/eslintrc': 2.1.3 + '@eslint/js': 8.53.0 + '@humanwhocodes/config-array': 0.11.13 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 @@ -6206,7 +6211,7 @@ packages: har-schema: 2.0.0 dev: true - /hardhat-abi-exporter@2.10.1(hardhat@2.18.1): + /hardhat-abi-exporter@2.10.1(hardhat@2.19.1): resolution: {integrity: sha512-X8GRxUTtebMAd2k4fcPyVnCdPa6dYK4lBsrwzKP5yiSq4i+WadWPIumaLfce53TUf/o2TnLpLOduyO1ylE2NHQ==} engines: {node: '>=14.14.0'} peerDependencies: @@ -6214,28 +6219,28 @@ packages: dependencies: '@ethersproject/abi': 5.7.0 delete-empty: 3.0.0 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) dev: true - /hardhat-contract-sizer@2.10.0(hardhat@2.18.1): + /hardhat-contract-sizer@2.10.0(hardhat@2.19.1): 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.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) strip-ansi: 6.0.1 dev: true - /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.18.1): + /hardhat-gas-reporter@1.0.9(debug@4.3.4)(hardhat@2.19.1): 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.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' @@ -6252,8 +6257,8 @@ packages: solidity-comments: 0.0.2 dev: true - /hardhat@2.18.1(ts-node@10.9.1)(typescript@5.2.2): - resolution: {integrity: sha512-b55rW7Ka+fvJeg6oWuBTXoYQEUurevCCankjGNTwczwD3GnkhV9GEei7KUT+9IKmWx3lC+zyxlFxeDbg0gUoHw==} + /hardhat@2.19.1(ts-node@10.9.1)(typescript@5.2.2): + resolution: {integrity: sha512-bsWa63g1GB78ZyMN08WLhFElLPA+J+pShuKD1BFO2+88g3l+BL3R07vj9deIi9dMbssxgE714Gof1dBEDGqnCw==} hasBin: true peerDependencies: ts-node: '*' @@ -6308,7 +6313,7 @@ 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.58)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@16.18.61)(typescript@5.2.2) tsort: 0.0.1 typescript: 5.2.2 undici: 5.19.1 @@ -8878,28 +8883,27 @@ packages: fast-diff: 1.2.0 dev: true - /prettier-plugin-solidity@1.1.3(prettier@2.8.8): - resolution: {integrity: sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==} - engines: {node: '>=12'} - requiresBuild: true + /prettier-plugin-solidity@1.2.0(prettier@2.8.8): + resolution: {integrity: sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==} + engines: {node: '>=16'} peerDependencies: - prettier: '>=2.3.0 || >=3.0.0-alpha.0' + prettier: '>=2.3.0' dependencies: - '@solidity-parser/parser': 0.16.0 + '@solidity-parser/parser': 0.16.2 prettier: 2.8.8 - semver: 7.5.0 + semver: 7.5.4 solidity-comments-extractor: 0.0.7 dev: true optional: true - /prettier-plugin-solidity@1.1.4-dev(prettier@3.0.3): - resolution: {integrity: sha512-SIDnHIPLN/Pod/dZoyJL07ViEcDxrXoT47ROQshpA/WFgyq/rRzLIc3oWkKfWiicHOD493Y/L1n9ds1GbwPoKQ==} + /prettier-plugin-solidity@1.2.0(prettier@3.1.0): + resolution: {integrity: sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==} engines: {node: '>=16'} peerDependencies: prettier: '>=2.3.0' dependencies: - '@solidity-parser/parser': 0.16.1 - prettier: 3.0.3 + '@solidity-parser/parser': 0.16.2 + prettier: 3.1.0 semver: 7.5.4 solidity-comments-extractor: 0.0.7 dev: true @@ -8910,8 +8914,8 @@ packages: hasBin: true dev: true - /prettier@3.0.3: - resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} + /prettier@3.1.0: + resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==} engines: {node: '>=14'} hasBin: true dev: true @@ -9637,16 +9641,6 @@ packages: lru-cache: 6.0.0 dev: true - /semver@7.5.0: - resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} - engines: {node: '>=10'} - hasBin: true - requiresBuild: true - dependencies: - lru-cache: 6.0.0 - dev: true - optional: true - /semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} @@ -9953,16 +9947,16 @@ packages: - debug dev: true - /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.1.4-dev)(prettier@3.0.3): + /solhint-plugin-prettier@0.1.0(prettier-plugin-solidity@1.2.0)(prettier@3.1.0): 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.0.3) - prettier: 3.0.3 + '@prettier/sync': 0.3.0(prettier@3.1.0) + prettier: 3.1.0 prettier-linter-helpers: 1.0.0 - prettier-plugin-solidity: 1.1.4-dev(prettier@3.0.3) + prettier-plugin-solidity: 1.2.0(prettier@3.1.0) dev: true /solhint@4.0.0: @@ -10107,7 +10101,7 @@ packages: solidity-comments-win32-x64-msvc: 0.0.2 dev: true - /solidity-coverage@0.8.5(hardhat@2.18.1): + /solidity-coverage@0.8.5(hardhat@2.19.1): resolution: {integrity: sha512-6C6N6OV2O8FQA0FWA95FdzVH+L16HU94iFgg5wAFZ29UpLFkgNI/DRR2HotG1bC0F4gAc/OMs2BJI44Q/DYlKQ==} hasBin: true peerDependencies: @@ -10123,7 +10117,7 @@ packages: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.18.1(ts-node@10.9.1)(typescript@5.2.2) + hardhat: 2.19.1(ts-node@10.9.1)(typescript@5.2.2) jsonschema: 1.4.0 lodash: 4.17.21 mocha: 10.2.0 @@ -10766,7 +10760,7 @@ packages: ts-essentials: 1.0.4 dev: true - /ts-node@10.9.1(@types/node@16.18.58)(typescript@5.2.2): + /ts-node@10.9.1(@types/node@16.18.61)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -10785,7 +10779,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 16.18.58 + '@types/node': 16.18.61 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -10893,8 +10887,8 @@ packages: - typescript dev: true - /typechain@8.2.0(typescript@5.2.2): - resolution: {integrity: sha512-tZqhqjxJ9xAS/Lh32jccTjMkpx7sTdUVVHAy5Bf0TIer5QFNYXotiX74oCvoVYjyxUKDK3MXHtMFzMyD3kE+jg==} + /typechain@8.3.2(typescript@5.2.2): + resolution: {integrity: sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==} hasBin: true peerDependencies: typescript: '>=4.3.0' From e42562cc435d6136925383e2368e99dbd1663a59 Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Mon, 20 Nov 2023 14:26:13 +0100 Subject: [PATCH 177/271] fix solidity codeowners (#11300) --- CODEOWNERS | 33 ++++++++++++++++------- contracts/scripts/native_solc_compile_all | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5f33e68e514..bd2d0419cf7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -73,23 +73,38 @@ core/scripts/gateway @bolekk @pinebit # Contracts /contracts/ @se3000 @connorwstein @RensR -/contracts/srv/v0.8/automation @smartcontractkit/keepers +# First we match on project names to catch files like the compilation scripts, +# gas snapshots and other files not places in the project directories. +# This could give some false positives, so afterwards we match on the project directories +# to ensure the entire directory is always owned by the correct team. + +/contracts/**/*shared* @RensR /contracts/**/*keeper* @smartcontractkit/keepers /contracts/**/*upkeep* @smartcontractkit/keepers /contracts/**/*automation* @smartcontractkit/keepers -/contracts/gas-snapshots/automation.gas-snapshot @smartcontractkit/keepers - -/contracts/src/v0.8/functions @smartcontractkit/functions /contracts/**/*functions* @smartcontractkit/functions -/contracts/gas-snapshots/functions.gas-snapshot @smartcontractkit/functions +/contracts/**/*llo-feeds* @austinborn @Fletch153 +/contracts/**/*vrf* @smartcontractkit/vrf-team +/contracts/**/*l2ep* @simsonraj +/contracts/**/*operatorforwarder* @essamhassan +/contracts/src/v0.8/automation @smartcontractkit/keepers +/contracts/src/v0.8/functions @smartcontractkit/functions +# TODO: interfaces folder, folder should be removed and files moved to the correct folders +/contracts/src/v0.8/l2ep @simsonraj /contracts/src/v0.8/llo-feeds @austinborn @Fletch153 -/contracts/gas-snapshots/llo-feeds.gas-snapshot @austinborn @Fletch153 - +# TODO: mocks folder, folder should be removed and files moved to the correct folders +/contracts/src/v0.8/operatorforwarder @essamhassan +/contracts/src/v0.8/shared @RensR +# TODO: tests folder, folder should be removed and files moved to the correct folders +# TODO: transmission folder, owner should be found /contracts/src/v0.8/vrf @smartcontractkit/vrf-team -/contracts/**/*vrf* @smartcontractkit/vrf-team -/contracts/src/v0.8/l2ep @simsonraj + + +# At the end, match any files missed by the patterns above +/contracts/scripts/native_solc_compile_all_events_mock @smartcontractkit/functions + # Tests /integration-tests/ @smartcontractkit/test-tooling-team diff --git a/contracts/scripts/native_solc_compile_all b/contracts/scripts/native_solc_compile_all index a2f2f1a0bc3..cf1226a2d5c 100755 --- a/contracts/scripts/native_solc_compile_all +++ b/contracts/scripts/native_solc_compile_all @@ -12,7 +12,7 @@ python3 -m pip install --require-hashes -r $SCRIPTPATH/requirements.txt # 6 and 7 are legacy contracts, for each other product we have a native_solc_compile_all_$product script # These scripts can be run individually, or all together with this script. # To add new CL products, simply write a native_solc_compile_all_$product script and add it to the list below. -for product in 6 7 feeds functions llo-feeds transmission vrf automation operatorforwarder logpoller events_mock shared +for product in 6 7 automation events_mock feeds functions llo-feeds logpoller operatorforwarder shared transmission vrf do $SCRIPTPATH/native_solc_compile_all_$product done From 7e0fa2313580c7497f3a956a4f690c85ede678bb Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Mon, 20 Nov 2023 07:14:46 -0800 Subject: [PATCH 178/271] [Functions] Rename a few secrets-specific objects to more generic names (#11340) These structs/functions are generic enough to be used for non-secrets related requests. --- core/services/functions/connector_handler.go | 2 +- .../gateway/handlers/functions/api.go | 10 +++---- .../handlers/functions/handler.functions.go | 28 +++++++++---------- .../functions/handler.functions_test.go | 4 +-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 343980afdd5..76608b8ada3 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -84,7 +84,7 @@ func (h *functionsConnectorHandler) HandleGatewayMessage(ctx context.Context, ga case functions.MethodSecretsSet: if balance, err := h.subscriptions.GetMaxUserBalance(fromAddr); err != nil || balance.Cmp(h.minimumBalance.ToInt()) < 0 { h.lggr.Errorw("user subscription has insufficient balance", "id", gatewayId, "address", fromAddr, "balance", balance, "minBalance", h.minimumBalance) - response := functions.SecretsResponseBase{ + response := functions.ResponseBase{ Success: false, ErrorMessage: "user subscription has insufficient balance", } diff --git a/core/services/gateway/handlers/functions/api.go b/core/services/gateway/handlers/functions/api.go index 979cdb939b6..202fa99e414 100644 --- a/core/services/gateway/handlers/functions/api.go +++ b/core/services/gateway/handlers/functions/api.go @@ -17,17 +17,17 @@ type SecretsSetRequest struct { // SecretsListRequest has empty payload -type SecretsResponseBase struct { +type ResponseBase struct { Success bool `json:"success"` ErrorMessage string `json:"error_message,omitempty"` } type SecretsSetResponse struct { - SecretsResponseBase + ResponseBase } type SecretsListResponse struct { - SecretsResponseBase + ResponseBase Rows []SecretsListRow `json:"rows,omitempty"` } @@ -38,7 +38,7 @@ type SecretsListRow struct { } // Gateway -> User response, which combines responses from several nodes -type CombinedSecretsResponse struct { - SecretsResponseBase +type CombinedResponse struct { + ResponseBase NodeResponses []*api.Message `json:"node_responses"` } diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 32a132c075f..6cc4581a505 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -75,7 +75,7 @@ type functionsHandler struct { handlerConfig FunctionsHandlerConfig donConfig *config.DONConfig don handlers.DON - pendingRequests hc.RequestCache[PendingSecretsRequest] + pendingRequests hc.RequestCache[PendingRequest] allowlist OnchainAllowlist subscriptions OnchainSubscriptions minimumBalance *assets.Link @@ -85,7 +85,7 @@ type functionsHandler struct { lggr logger.Logger } -type PendingSecretsRequest struct { +type PendingRequest struct { request *api.Message responses map[string]*api.Message successful []*api.Message @@ -136,7 +136,7 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con return nil, err2 } } - pendingRequestsCache := hc.NewRequestCache[PendingSecretsRequest](time.Millisecond*time.Duration(cfg.RequestTimeoutMillis), cfg.MaxPendingRequests) + pendingRequestsCache := hc.NewRequestCache[PendingRequest](time.Millisecond*time.Duration(cfg.RequestTimeoutMillis), cfg.MaxPendingRequests) return NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, cfg.MinimumSubscriptionBalance, userRateLimiter, nodeRateLimiter, lggr), nil } @@ -144,7 +144,7 @@ func NewFunctionsHandler( cfg FunctionsHandlerConfig, donConfig *config.DONConfig, don handlers.DON, - pendingRequestsCache hc.RequestCache[PendingSecretsRequest], + pendingRequestsCache hc.RequestCache[PendingRequest], allowlist OnchainAllowlist, subscriptions OnchainSubscriptions, minimumBalance *assets.Link, @@ -193,7 +193,7 @@ func (h *functionsHandler) HandleUserMessage(ctx context.Context, msg *api.Messa } switch msg.Body.Method { case MethodSecretsSet, MethodSecretsList: - return h.handleSecretsRequest(ctx, msg, callbackCh) + return h.handleRequest(ctx, msg, callbackCh) default: h.lggr.Debugw("unsupported method", "method", msg.Body.Method) promHandlerError.WithLabelValues(h.donConfig.DonId, ErrUnsupportedMethod.Error()).Inc() @@ -201,11 +201,11 @@ func (h *functionsHandler) HandleUserMessage(ctx context.Context, msg *api.Messa } } -func (h *functionsHandler) handleSecretsRequest(ctx context.Context, msg *api.Message, callbackCh chan<- handlers.UserCallbackPayload) error { - h.lggr.Debugw("handleSecretsRequest: processing message", "sender", msg.Body.Sender, "messageId", msg.Body.MessageId) - err := h.pendingRequests.NewRequest(msg, callbackCh, &PendingSecretsRequest{request: msg, responses: make(map[string]*api.Message)}) +func (h *functionsHandler) handleRequest(ctx context.Context, msg *api.Message, callbackCh chan<- handlers.UserCallbackPayload) error { + h.lggr.Debugw("handleRequest: processing message", "sender", msg.Body.Sender, "messageId", msg.Body.MessageId) + err := h.pendingRequests.NewRequest(msg, callbackCh, &PendingRequest{request: msg, responses: make(map[string]*api.Message)}) if err != nil { - h.lggr.Warnw("handleSecretsRequest: error adding new request", "sender", msg.Body.Sender, "err", err) + h.lggr.Warnw("handleRequest: error adding new request", "sender", msg.Body.Sender, "err", err) promHandlerError.WithLabelValues(h.donConfig.DonId, err.Error()).Inc() return err } @@ -213,7 +213,7 @@ func (h *functionsHandler) handleSecretsRequest(ctx context.Context, msg *api.Me for _, member := range h.donConfig.Members { err := h.don.SendToNode(ctx, member.Address, msg) if err != nil { - h.lggr.Debugw("handleSecretsRequest: failed to send to a node", "node", member.Address, "err", err) + h.lggr.Debugw("handleRequest: failed to send to a node", "node", member.Address, "err", err) } } return nil @@ -234,8 +234,8 @@ func (h *functionsHandler) HandleNodeMessage(ctx context.Context, msg *api.Messa } } -// Conforms to ResponseProcessor[*PendingSecretsRequest] -func (h *functionsHandler) processSecretsResponse(response *api.Message, responseData *PendingSecretsRequest) (*handlers.UserCallbackPayload, *PendingSecretsRequest, error) { +// Conforms to ResponseProcessor[*PendingRequest] +func (h *functionsHandler) processSecretsResponse(response *api.Message, responseData *PendingRequest) (*handlers.UserCallbackPayload, *PendingRequest, error) { if _, exists := responseData.responses[response.Body.Sender]; exists { return nil, nil, errors.New("duplicate response") } @@ -243,7 +243,7 @@ func (h *functionsHandler) processSecretsResponse(response *api.Message, respons return nil, responseData, errors.New("invalid method") } responseData.responses[response.Body.Sender] = response - var responsePayload SecretsResponseBase + var responsePayload ResponseBase err := json.Unmarshal(response.Body.Payload, &responsePayload) if err != nil { responseData.errors = append(responseData.errors, response) @@ -270,7 +270,7 @@ func (h *functionsHandler) processSecretsResponse(response *api.Message, respons } func newSecretsResponse(request *api.Message, success bool, responses []*api.Message) (*handlers.UserCallbackPayload, error) { - payload := CombinedSecretsResponse{SecretsResponseBase: SecretsResponseBase{Success: success}, NodeResponses: responses} + payload := CombinedResponse{ResponseBase: ResponseBase{Success: success}, NodeResponses: responses} payloadJson, err := json.Marshal(payload) if err != nil { return nil, err diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 00334d30682..402823df173 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -47,7 +47,7 @@ func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTi require.NoError(t, err) nodeRateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) - pendingRequestsCache := hc.NewRequestCache[functions.PendingSecretsRequest](requestTimeout, 1000) + pendingRequestsCache := hc.NewRequestCache[functions.PendingRequest](requestTimeout, 1000) handler := functions.NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, minBalance, userRateLimiter, nodeRateLimiter, logger.TestLogger(t)) return handler, don, allowlist, subscriptions } @@ -128,7 +128,7 @@ func TestFunctionsHandler_HandleUserMessage_SecretsSet(t *testing.T) { response := <-callbachCh require.Equal(t, api.NoError, response.ErrCode) require.Equal(t, userRequestMsg.Body.MessageId, response.Msg.Body.MessageId) - var payload functions.CombinedSecretsResponse + var payload functions.CombinedResponse require.NoError(t, json.Unmarshal(response.Msg.Body.Payload, &payload)) require.Equal(t, test.expectedGatewayResult, payload.Success) require.Equal(t, test.expectedNodeMessageCount, len(payload.NodeResponses)) From c9312c6120809c53418233fb5109d1217b427a8f Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 20 Nov 2023 09:21:42 -0600 Subject: [PATCH 179/271] core/utils: StopChan & StartStopOnce cleanup (#11341) * core/utils: make StopChan an alias to common * use services.StopChan; deprecate utils.StopChan * use services.StateMachine instead of deprecated utils.StartStopOnce --- common/client/multi_node.go | 4 +- common/client/send_only_node.go | 3 +- common/headtracker/head_broadcaster.go | 2 +- common/headtracker/head_listener.go | 4 +- common/headtracker/head_tracker.go | 2 +- common/txmgr/broadcaster.go | 2 +- common/txmgr/reaper.go | 8 +- common/txmgr/txmgr.go | 4 +- core/chains/evm/client/node.go | 2 +- core/chains/evm/client/pool.go | 2 +- core/chains/evm/client/send_only_node.go | 3 +- core/chains/evm/gas/arbitrum_estimator.go | 2 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 2 +- .../evm/gas/suggested_price_estimator.go | 2 +- core/chains/evm/log/broadcaster.go | 2 +- core/chains/evm/log/eth_subscriber.go | 4 +- core/chains/evm/txmgr/broadcaster_test.go | 4 +- core/chains/evm/txmgr/confirmer_test.go | 4 +- core/logger/audit/audit_logger.go | 14 ++-- core/services/cron/cron.go | 5 +- core/services/directrequest/delegate.go | 10 +-- core/services/fluxmonitorv2/flux_monitor.go | 8 +- core/services/gateway/connectionmanager.go | 6 +- core/services/gateway/connector/connector.go | 6 +- .../gateway/handlers/functions/allowlist.go | 4 +- .../handlers/functions/handler.functions.go | 5 +- .../handlers/functions/subscriptions.go | 4 +- core/services/job/spawner.go | 24 +++--- core/services/keeper/upkeep_executer.go | 4 +- core/services/ocr/contract_tracker.go | 4 +- core/services/ocr2/plugins/median/plugin.go | 6 +- .../plugins/ocr2keeper/custom_telemetry.go | 7 +- .../evm21/autotelemetry21/custom_telemetry.go | 7 +- .../ocr2keeper/evm21/mercury/v02/request.go | 4 +- .../ocr2keeper/evm21/mercury/v03/request.go | 4 +- .../ocr2/plugins/ocr2keeper/evm21/registry.go | 5 +- core/services/pipeline/runner.go | 2 +- core/services/promreporter/prom_reporter.go | 2 +- .../relay/evm/functions/logpoller_wrapper.go | 4 +- .../relay/evm/mercury/persistence_manager.go | 4 +- .../services/relay/evm/mercury/transmitter.go | 4 +- .../relay/evm/mercury/wsrpc/client.go | 4 +- .../telemetry_ingress_batch_worker.go | 8 +- .../telemetry_ingress_client.go | 7 +- core/services/vrf/v1/listener_v1.go | 2 +- core/services/vrf/v2/listener_v2.go | 4 +- core/services/webhook/delegate.go | 5 +- core/utils/thread_control.go | 4 +- core/utils/utils.go | 60 +++------------ core/utils/utils_example_test.go | 48 ------------ core/utils/utils_test.go | 73 ------------------- plugins/medianpoc/plugin.go | 5 +- 52 files changed, 138 insertions(+), 281 deletions(-) delete mode 100644 core/utils/utils_example_test.go diff --git a/common/client/multi_node.go b/common/client/multi_node.go index acab47f0836..48a4d37ad8c 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -99,7 +99,7 @@ type multiNode[ activeMu sync.RWMutex activeNode Node[CHAIN_ID, HEAD, RPC_CLIENT] - chStop utils.StopChan + chStop services.StopChan wg sync.WaitGroup sendOnlyErrorParser func(err error) SendTxReturnCode @@ -146,7 +146,7 @@ func NewMultiNode[ selectionMode: selectionMode, noNewHeadsThreshold: noNewHeadsThreshold, nodeSelector: nodeSelector, - chStop: make(chan struct{}), + chStop: make(services.StopChan), leaseDuration: leaseDuration, chainFamily: chainFamily, sendOnlyErrorParser: sendOnlyErrorParser, diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index 767fff5aee2..904916122f1 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -10,7 +10,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name sendOnlyClient --structname mockSendOnlyClient --filename "mock_send_only_client_test.go" --inpackage --case=underscore @@ -59,7 +58,7 @@ type sendOnlyNode[ log logger.Logger name string chainID CHAIN_ID - chStop utils.StopChan + chStop services.StopChan wg sync.WaitGroup } diff --git a/common/headtracker/head_broadcaster.go b/common/headtracker/head_broadcaster.go index 3efe64e1c3f..62b2f47b68a 100644 --- a/common/headtracker/head_broadcaster.go +++ b/common/headtracker/head_broadcaster.go @@ -31,7 +31,7 @@ type HeadBroadcaster[H types.Head[BLOCK_HASH], BLOCK_HASH types.Hashable] struct callbacks callbackSet[H, BLOCK_HASH] mailbox *utils.Mailbox[H] mutex sync.Mutex - chClose utils.StopChan + chClose services.StopChan wgDone sync.WaitGroup latest H lastCallbackID int diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index ee4969497a8..a3f262f4b73 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -9,6 +9,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/services" + htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -35,7 +37,7 @@ type HeadListener[ config htrktypes.Config client htrktypes.Client[HTH, S, ID, BLOCK_HASH] logger logger.Logger - chStop utils.StopChan + chStop services.StopChan chHeaders chan HTH headSubscription types.Subscription connected atomic.Bool diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index bf63675128f..810e749a2dc 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -53,7 +53,7 @@ type HeadTracker[ backfillMB *utils.Mailbox[HTH] broadcastMB *utils.Mailbox[HTH] headListener types.HeadListener[HTH, BLOCK_HASH] - chStop utils.StopChan + chStop services.StopChan wgDone sync.WaitGroup getNilHead func() HTH } diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index ba01fb9e2ad..d9a72e367ac 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -135,7 +135,7 @@ type Broadcaster[ // Each key has its own trigger triggers map[ADDR]chan struct{} - chStop utils.StopChan + chStop services.StopChan wg sync.WaitGroup initSync sync.Mutex diff --git a/common/txmgr/reaper.go b/common/txmgr/reaper.go index 96bc4860d71..7286efa3a80 100644 --- a/common/txmgr/reaper.go +++ b/common/txmgr/reaper.go @@ -5,6 +5,8 @@ import ( "sync/atomic" "time" + "github.com/smartcontractkit/chainlink-common/pkg/services" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -20,7 +22,7 @@ type Reaper[CHAIN_ID types.ID] struct { log logger.Logger latestBlockNum atomic.Int64 trigger chan struct{} - chStop chan struct{} + chStop services.StopChan chDone chan struct{} } @@ -34,7 +36,7 @@ func NewReaper[CHAIN_ID types.ID](lggr logger.Logger, store txmgrtypes.TxHistory lggr.Named("Reaper"), atomic.Int64{}, make(chan struct{}, 1), - make(chan struct{}), + make(services.StopChan), make(chan struct{}), } r.latestBlockNum.Store(-1) @@ -97,7 +99,7 @@ func (r *Reaper[CHAIN_ID]) SetLatestBlockNum(latestBlockNum int64) { // ReapTxes deletes old txes func (r *Reaper[CHAIN_ID]) ReapTxes(headNum int64) error { - ctx, cancel := utils.StopChan(r.chStop).NewCtx() + ctx, cancel := r.chStop.NewCtx() defer cancel() threshold := r.txConfig.ReaperThreshold() if threshold == 0 { diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 24d2428f61a..b49c2b72f15 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -90,7 +90,7 @@ type Txm[ reset chan reset resumeCallback ResumeCallback - chStop chan struct{} + chStop services.StopChan chSubbed chan struct{} wg sync.WaitGroup @@ -228,7 +228,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Reset(addr // - marks all pending and inflight transactions fatally errored (note: at this point all transactions are either confirmed or fatally errored) // this must not be run while Broadcaster or Confirmer are running func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) abandon(addr ADDR) (err error) { - ctx, cancel := utils.StopChan(b.chStop).NewCtx() + ctx, cancel := services.StopChan(b.chStop).NewCtx() defer cancel() if err = b.txStore.Abandon(ctx, b.chainID, addr); err != nil { return fmt.Errorf("abandon failed to update txes for key %s: %w", addr.String(), err) diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index b6907202409..b3ce489cf50 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -1101,7 +1101,7 @@ func (n *node) makeQueryCtx(ctx context.Context) (context.Context, context.Cance // 1. Passed in ctx cancels // 2. Passed in channel is closed // 3. Default timeout is reached (queryTimeout) -func makeQueryCtx(ctx context.Context, ch utils.StopChan) (context.Context, context.CancelFunc) { +func makeQueryCtx(ctx context.Context, ch services.StopChan) (context.Context, context.CancelFunc) { var chCancel, timeoutCancel context.CancelFunc ctx, chCancel = ch.Ctx(ctx) ctx, timeoutCancel = context.WithTimeout(ctx, queryTimeout) diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 18f59b172d7..289a402a1c6 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -72,7 +72,7 @@ type Pool struct { activeMu sync.RWMutex activeNode Node - chStop utils.StopChan + chStop services.StopChan wg sync.WaitGroup } diff --git a/core/chains/evm/client/send_only_node.go b/core/chains/evm/client/send_only_node.go index beb12dbc4da..02f04881c44 100644 --- a/core/chains/evm/client/send_only_node.go +++ b/core/chains/evm/client/send_only_node.go @@ -16,7 +16,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) //go:generate mockery --quiet --name SendOnlyNode --output ../mocks/ --case=underscore @@ -69,7 +68,7 @@ type sendOnlyNode struct { dialed bool name string chainID *big.Int - chStop utils.StopChan + chStop services.StopChan wg sync.WaitGroup } diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index c79202c7312..480abfe721d 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -48,7 +48,7 @@ type arbitrumEstimator struct { chForceRefetch chan (chan struct{}) chInitialised chan struct{} - chStop utils.StopChan + chStop services.StopChan chDone chan struct{} } diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index 88c61c49344..1a0fe8b8b24 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -38,7 +38,7 @@ type l1GasPriceOracle struct { l1GasPrice *assets.Wei chInitialised chan struct{} - chStop utils.StopChan + chStop services.StopChan chDone chan struct{} } diff --git a/core/chains/evm/gas/suggested_price_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go index dadec6210c7..cd5acbc6942 100644 --- a/core/chains/evm/gas/suggested_price_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -40,7 +40,7 @@ type SuggestedPriceEstimator struct { chForceRefetch chan (chan struct{}) chInitialised chan struct{} - chStop utils.StopChan + chStop services.StopChan chDone chan struct{} } diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index 11c282a4d2e..d69fd696fd7 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -110,7 +110,7 @@ type ( utils.DependentAwaiter - chStop utils.StopChan + chStop services.StopChan wgDone sync.WaitGroup trackedAddressesCount atomic.Uint32 replayChannel chan replayRequest diff --git a/core/chains/evm/log/eth_subscriber.go b/core/chains/evm/log/eth_subscriber.go index cd1b18b474f..b4d386140e7 100644 --- a/core/chains/evm/log/eth_subscriber.go +++ b/core/chains/evm/log/eth_subscriber.go @@ -10,6 +10,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" @@ -21,7 +23,7 @@ type ( ethClient evmclient.Client config Config logger logger.Logger - chStop utils.StopChan + chStop services.StopChan } ) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 7967478e624..bf480c66af6 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -112,10 +112,10 @@ func TestEthBroadcaster_Lifecycle(t *testing.T) { err = eb.Close() require.NoError(t, err) - // Can't start more than once (Broadcaster implements utils.StartStopOnce) + // Can't start more than once (Broadcaster uses services.StateMachine) err = eb.Start(ctx) require.Error(t, err) - // Can't close more than once (Broadcaster implements utils.StartStopOnce) + // Can't close more than once (Broadcaster uses services.StateMachine) err = eb.Close() require.Error(t, err) diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index e17e7993cf9..60d0648a541 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -157,10 +157,10 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { err = ec.Close() require.NoError(t, err) - // Can't start more than once (Confirmer implements utils.StartStopOnce) + // Can't start more than once (Confirmer uses services.StateMachine) err = ec.Start(ctx) require.Error(t, err) - // Can't close more than once (Confirmer implements utils.StartStopOnce) + // Can't close more than once (Confirmer use services.StateMachine) err = ec.Close() require.Error(t, err) diff --git a/core/logger/audit/audit_logger.go b/core/logger/audit/audit_logger.go index 38afd77a284..ef66a063a55 100644 --- a/core/logger/audit/audit_logger.go +++ b/core/logger/audit/audit_logger.go @@ -4,6 +4,8 @@ import ( "bytes" "context" "encoding/json" + "errors" + "fmt" "io" "net" "net/http" @@ -11,13 +13,11 @@ import ( "os" "time" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/store/models" - "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/pkg/errors" ) const bufferCapacity = 2048 @@ -26,7 +26,7 @@ const webRequestTimeout = 10 type Data = map[string]any type AuditLogger interface { - services.ServiceCtx + services.Service Audit(eventID EventID, data Data) } @@ -47,7 +47,7 @@ type AuditLoggerService struct { loggingClient HTTPAuditLoggerInterface // Abstract type for sending logs onward loggingChannel chan wrappedAuditLog - chStop utils.StopChan + chStop services.StopChan chDone chan struct{} } @@ -72,7 +72,7 @@ func NewAuditLogger(logger logger.Logger, config config.AuditLogger) (AuditLogge hostname, err := os.Hostname() if err != nil { - return nil, errors.Errorf("initialization error - unable to get hostname: %s", err) + return nil, fmt.Errorf("initialization error - unable to get hostname: %w", err) } forwardToUrl, err := config.ForwardToUrl() diff --git a/core/services/cron/cron.go b/core/services/cron/cron.go index e89dd1ceabd..500192554fb 100644 --- a/core/services/cron/cron.go +++ b/core/services/cron/cron.go @@ -6,10 +6,11 @@ import ( "github.com/robfig/cron/v3" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Cron runs a cron jobSpec from a CronSpec @@ -18,7 +19,7 @@ type Cron struct { logger logger.Logger jobSpec job.Job pipelineRunner pipeline.Runner - chStop utils.StopChan + chStop services.StopChan } // NewCronFromJobSpec instantiates a job that executes on a predefined schedule. diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index 9da84fd3ee5..687e1cea675 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -129,7 +129,7 @@ type listener struct { pipelineORM pipeline.ORM mailMon *utils.MailboxMonitor job job.Job - runs sync.Map // map[string]utils.StopChan + runs sync.Map // map[string]services.StopChan shutdownWaitGroup sync.WaitGroup mbOracleRequests *utils.Mailbox[log.Broadcast] mbOracleCancelRequests *utils.Mailbox[log.Broadcast] @@ -178,7 +178,7 @@ func (l *listener) Start(context.Context) error { func (l *listener) Close() error { return l.StopOnce("DirectRequestListener", func() error { l.runs.Range(func(key, runCloserChannelIf interface{}) bool { - runCloserChannel := runCloserChannelIf.(utils.StopChan) + runCloserChannel := runCloserChannelIf.(services.StopChan) close(runCloserChannel) return true }) @@ -338,10 +338,10 @@ func (l *listener) handleOracleRequest(request *operator_wrapper.OperatorOracleR meta := make(map[string]interface{}) meta["oracleRequest"] = oracleRequestToMap(request) - runCloserChannel := make(utils.StopChan) + runCloserChannel := make(services.StopChan) runCloserChannelIf, loaded := l.runs.LoadOrStore(formatRequestId(request.RequestId), runCloserChannel) if loaded { - runCloserChannel = runCloserChannelIf.(utils.StopChan) + runCloserChannel = runCloserChannelIf.(services.StopChan) } ctx, cancel := runCloserChannel.NewCtx() defer cancel() @@ -398,7 +398,7 @@ func (l *listener) allowRequester(requester common.Address) bool { func (l *listener) handleCancelOracleRequest(request *operator_wrapper.OperatorCancelOracleRequest, lb log.Broadcast) { runCloserChannelIf, loaded := l.runs.LoadAndDelete(formatRequestId(request.RequestId)) if loaded { - close(runCloserChannelIf.(utils.StopChan)) + close(runCloserChannelIf.(services.StopChan)) } l.markLogConsumed(lb) } diff --git a/core/services/fluxmonitorv2/flux_monitor.go b/core/services/fluxmonitorv2/flux_monitor.go index ea853d879d4..79dd44c8014 100644 --- a/core/services/fluxmonitorv2/flux_monitor.go +++ b/core/services/fluxmonitorv2/flux_monitor.go @@ -83,7 +83,7 @@ type FluxMonitor struct { backlog *utils.BoundedPriorityQueue[log.Broadcast] chProcessLogs chan struct{} - chStop chan struct{} + chStop services.StopChan waitOnStop chan struct{} } @@ -137,7 +137,7 @@ func NewFluxMonitor( PriorityFlagChangedLog: 2, }), chProcessLogs: make(chan struct{}, 1), - chStop: make(chan struct{}), + chStop: make(services.StopChan), waitOnStop: make(chan struct{}), } @@ -588,7 +588,7 @@ func (fm *FluxMonitor) respondToAnswerUpdatedLog(log flux_aggregator_wrapper.Flu // need to poll and submit an answer to the contract regardless of the deviation. func (fm *FluxMonitor) respondToNewRoundLog(log flux_aggregator_wrapper.FluxAggregatorNewRound, lb log.Broadcast) { started := time.Now() - ctx, cancel := utils.StopChan(fm.chStop).NewCtx() + ctx, cancel := fm.chStop.NewCtx() defer cancel() newRoundLogger := fm.logger.With( @@ -814,7 +814,7 @@ func (fm *FluxMonitor) checkEligibilityAndAggregatorFunding(roundState flux_aggr func (fm *FluxMonitor) pollIfEligible(pollReq PollRequestType, deviationChecker *DeviationChecker, broadcast log.Broadcast) { started := time.Now() - ctx, cancel := utils.StopChan(fm.chStop).NewCtx() + ctx, cancel := fm.chStop.NewCtx() defer cancel() l := fm.logger.With( diff --git a/core/services/gateway/connectionmanager.go b/core/services/gateway/connectionmanager.go index ce4a54f4c2b..9f88b51e7b5 100644 --- a/core/services/gateway/connectionmanager.go +++ b/core/services/gateway/connectionmanager.go @@ -72,7 +72,7 @@ type donConnectionManager struct { handler handlers.Handler codec api.Codec closeWait sync.WaitGroup - shutdownCh chan struct{} + shutdownCh services.StopChan lggr logger.Logger } @@ -271,7 +271,7 @@ func (m *donConnectionManager) SendToNode(ctx context.Context, nodeAddress strin } func (m *donConnectionManager) readLoop(nodeAddress string, nodeState *nodeState) { - ctx, _ := utils.StopChan(m.shutdownCh).NewCtx() + ctx, _ := m.shutdownCh.NewCtx() for { select { case <-m.shutdownCh: @@ -296,7 +296,7 @@ func (m *donConnectionManager) readLoop(nodeAddress string, nodeState *nodeState } func (m *donConnectionManager) heartbeatLoop(intervalSec uint32) { - ctx, _ := utils.StopChan(m.shutdownCh).NewCtx() + ctx, _ := m.shutdownCh.NewCtx() defer m.closeWait.Done() if intervalSec == 0 { diff --git a/core/services/gateway/connector/connector.go b/core/services/gateway/connector/connector.go index 0694e9ad15f..27db8fd44b6 100644 --- a/core/services/gateway/connector/connector.go +++ b/core/services/gateway/connector/connector.go @@ -57,7 +57,7 @@ type gatewayConnector struct { gateways map[string]*gatewayState urlToId map[string]string closeWait sync.WaitGroup - shutdownCh chan struct{} + shutdownCh services.StopChan lggr logger.Logger } @@ -143,7 +143,7 @@ func (c *gatewayConnector) SendToGateway(ctx context.Context, gatewayId string, } func (c *gatewayConnector) readLoop(gatewayState *gatewayState) { - ctx, cancel := utils.StopChan(c.shutdownCh).NewCtx() + ctx, cancel := c.shutdownCh.NewCtx() defer cancel() for { @@ -168,7 +168,7 @@ func (c *gatewayConnector) readLoop(gatewayState *gatewayState) { func (c *gatewayConnector) reconnectLoop(gatewayState *gatewayState) { redialBackoff := utils.NewRedialBackoff() - ctx, cancel := utils.StopChan(c.shutdownCh).NewCtx() + ctx, cancel := c.shutdownCh.NewCtx() defer cancel() for { diff --git a/core/services/gateway/handlers/functions/allowlist.go b/core/services/gateway/handlers/functions/allowlist.go index 3ba9a65d57a..0a18d6e6d87 100644 --- a/core/services/gateway/handlers/functions/allowlist.go +++ b/core/services/gateway/handlers/functions/allowlist.go @@ -55,7 +55,7 @@ type onchainAllowlist struct { blockConfirmations *big.Int lggr logger.Logger closeWait sync.WaitGroup - stopCh utils.StopChan + stopCh services.StopChan } func NewOnchainAllowlist(client evmclient.Client, config OnchainAllowlistConfig, lggr logger.Logger) (OnchainAllowlist, error) { @@ -78,7 +78,7 @@ func NewOnchainAllowlist(client evmclient.Client, config OnchainAllowlistConfig, contractV1: contractV1, blockConfirmations: big.NewInt(int64(config.BlockConfirmations)), lggr: lggr.Named("OnchainAllowlist"), - stopCh: make(utils.StopChan), + stopCh: make(services.StopChan), } emptyMap := make(map[common.Address]struct{}) allowlist.allowlist.Store(&emptyMap) diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 6cc4581a505..3269caa2d6a 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -21,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -81,7 +80,7 @@ type functionsHandler struct { minimumBalance *assets.Link userRateLimiter *hc.RateLimiter nodeRateLimiter *hc.RateLimiter - chStop utils.StopChan + chStop services.StopChan lggr logger.Logger } @@ -161,7 +160,7 @@ func NewFunctionsHandler( minimumBalance: minimumBalance, userRateLimiter: userRateLimiter, nodeRateLimiter: nodeRateLimiter, - chStop: make(utils.StopChan), + chStop: make(services.StopChan), lggr: lggr, } } diff --git a/core/services/gateway/handlers/functions/subscriptions.go b/core/services/gateway/handlers/functions/subscriptions.go index 7a59e05731e..ebffbbdd206 100644 --- a/core/services/gateway/handlers/functions/subscriptions.go +++ b/core/services/gateway/handlers/functions/subscriptions.go @@ -49,7 +49,7 @@ type onchainSubscriptions struct { lggr logger.Logger closeWait sync.WaitGroup rwMutex sync.RWMutex - stopCh utils.StopChan + stopCh services.StopChan } func NewOnchainSubscriptions(client evmclient.Client, config OnchainSubscriptionsConfig, lggr logger.Logger) (OnchainSubscriptions, error) { @@ -70,7 +70,7 @@ func NewOnchainSubscriptions(client evmclient.Client, config OnchainSubscription router: router, blockConfirmations: big.NewInt(int64(config.BlockConfirmations)), lggr: lggr.Named("OnchainSubscriptions"), - stopCh: make(utils.StopChan), + stopCh: make(services.StopChan), }, nil } diff --git a/core/services/job/spawner.go b/core/services/job/spawner.go index 5656011e14d..5ed017b8743 100644 --- a/core/services/job/spawner.go +++ b/core/services/job/spawner.go @@ -11,10 +11,9 @@ import ( "github.com/jmoiron/sqlx" - commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -26,7 +25,7 @@ type ( // services that perform the work described by job specs. Each active job spec // has 1 or more of these services associated with it. Spawner interface { - services.ServiceCtx + services.Service // CreateJob creates a new job and starts services. // All services must start without errors for the job to be active. @@ -42,18 +41,23 @@ type ( StartService(ctx context.Context, spec Job, qopts ...pg.QOpt) error } + Checker interface { + Register(service services.HealthReporter) error + Unregister(name string) error + } + spawner struct { - commonservices.StateMachine + services.StateMachine orm ORM config Config - checker services.Checker + checker Checker jobTypeDelegates map[Type]Delegate activeJobs map[int32]activeJob activeJobsMu sync.RWMutex q pg.Q lggr logger.Logger - chStop utils.StopChan + chStop services.StopChan lbDependentAwaiters []utils.DependentAwaiter } @@ -86,7 +90,7 @@ type ( var _ Spawner = (*spawner)(nil) -func NewSpawner(orm ORM, config Config, checker services.Checker, jobTypeDelegates map[Type]Delegate, db *sqlx.DB, lggr logger.Logger, lbDependentAwaiters []utils.DependentAwaiter) *spawner { +func NewSpawner(orm ORM, config Config, checker Checker, jobTypeDelegates map[Type]Delegate, db *sqlx.DB, lggr logger.Logger, lbDependentAwaiters []utils.DependentAwaiter) *spawner { namedLogger := lggr.Named("JobSpawner") s := &spawner{ orm: orm, @@ -96,7 +100,7 @@ func NewSpawner(orm ORM, config Config, checker services.Checker, jobTypeDelegat q: pg.NewQ(db, namedLogger, config), lggr: namedLogger, activeJobs: make(map[int32]activeJob), - chStop: make(chan struct{}), + chStop: make(services.StopChan), lbDependentAwaiters: lbDependentAwaiters, } return s @@ -170,7 +174,7 @@ func (js *spawner) stopService(jobID int32) { for i := len(aj.services) - 1; i >= 0; i-- { service := aj.services[i] sLggr := lggr.With("subservice", i, "serviceType", reflect.TypeOf(service)) - if c, ok := service.(commonservices.HealthReporter); ok { + if c, ok := service.(services.HealthReporter); ok { if err := js.checker.Unregister(c.Name()); err != nil { sLggr.Warnw("Failed to unregister service from health checker", "err", err) } @@ -230,7 +234,7 @@ func (js *spawner) StartService(ctx context.Context, jb Job, qopts ...pg.QOpt) e lggr.Criticalw("Error starting service for job", "err", err) return err } - if c, ok := srv.(commonservices.HealthReporter); ok { + if c, ok := srv.(services.HealthReporter); ok { err = js.checker.Register(c) if err != nil { lggr.Errorw("Error registering service with health checker", "err", err) diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index ece6f85b068..84349ba2dca 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -55,7 +55,7 @@ type UpkeepExecuterConfig interface { // UpkeepExecuter implements the logic to communicate with KeeperRegistry type UpkeepExecuter struct { services.StateMachine - chStop utils.StopChan + chStop services.StopChan ethClient evmclient.Client config UpkeepExecuterConfig executionQueue chan struct{} @@ -83,7 +83,7 @@ func NewUpkeepExecuter( effectiveKeeperAddress common.Address, ) *UpkeepExecuter { return &UpkeepExecuter{ - chStop: make(chan struct{}), + chStop: make(services.StopChan), ethClient: ethClient, executionQueue: make(chan struct{}, executionQueueSize), headBroadcaster: headBroadcaster, diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go index 5fecbe86288..4f79bcfc31a 100644 --- a/core/services/ocr/contract_tracker.go +++ b/core/services/ocr/contract_tracker.go @@ -74,7 +74,7 @@ type ( unsubscribeHeads func() // Start/Stop lifecycle - chStop utils.StopChan + chStop services.StopChan wg sync.WaitGroup unsubscribeLogs func() @@ -134,7 +134,7 @@ func NewOCRContractTracker( cfg: cfg, mailMon: mailMon, headBroadcaster: headBroadcaster, - chStop: make(chan struct{}), + chStop: make(services.StopChan), latestRoundRequested: offchainaggregator.OffchainAggregatorRoundRequested{}, configsMB: utils.NewMailbox[ocrtypes.ContractConfig](configMailboxSanityLimit), chConfigs: make(chan ocrtypes.ContractConfig), diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go index 4f83c4b5dda..11197f09175 100644 --- a/core/services/ocr2/plugins/median/plugin.go +++ b/core/services/ocr2/plugins/median/plugin.go @@ -10,17 +10,15 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" - - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type Plugin struct { loop.Plugin - stop utils.StopChan + stop services.StopChan } func NewPlugin(lggr logger.Logger) *Plugin { - return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(utils.StopChan)} + return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(services.StopChan)} } func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProvider, dataSource, juelsPerFeeCoin median.DataSource, errorLog loop.ErrorLog) (loop.ReportingPluginFactory, error) { diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go index 0f03ae5bd06..6d52c0e8d25 100644 --- a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go @@ -5,12 +5,15 @@ import ( "encoding/hex" "time" + "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "google.golang.org/protobuf/proto" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" @@ -19,7 +22,7 @@ import ( ) type AutomationCustomTelemetryService struct { - utils.StartStopOnce + services.StateMachine monitoringEndpoint commontypes.MonitoringEndpoint blockSubscriber *evm21.BlockSubscriber blockSubChanID int diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go index 93f35ce0d24..0cf0bbef5cd 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go @@ -5,12 +5,15 @@ import ( "encoding/hex" "time" + "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "google.golang.org/protobuf/proto" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" @@ -19,7 +22,7 @@ import ( ) type AutomationCustomTelemetryService struct { - utils.StartStopOnce + services.StateMachine monitoringEndpoint commontypes.MonitoringEndpoint blockSubscriber *evm21.BlockSubscriber blockSubChanID int diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go index 55436937d11..4ce7893a0b6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go @@ -14,6 +14,8 @@ import ( "github.com/avast/retry-go/v4" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -34,7 +36,7 @@ type MercuryV02Response struct { } type client struct { - utils.StartStopOnce + services.StateMachine mercuryConfig mercury.MercuryConfigProvider httpClient mercury.HttpClient threadCtrl utils.ThreadControl diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go index 3697dca53cd..1c607889741 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go @@ -14,6 +14,8 @@ import ( "github.com/avast/retry-go/v4" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -43,7 +45,7 @@ type MercuryV03Report struct { } type client struct { - utils.StartStopOnce + services.StateMachine mercuryConfig mercury.MercuryConfigProvider httpClient mercury.HttpClient threadCtrl utils.ThreadControl diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 252d2d91c79..afb1a7cf6c3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -17,7 +17,7 @@ import ( "github.com/pkg/errors" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "github.com/smartcontractkit/chainlink-common/pkg/services" ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" @@ -31,6 +31,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -166,7 +167,7 @@ func (c *MercuryConfig) SetPluginRetry(k string, v interface{}, d time.Duration) } type EvmRegistry struct { - utils.StartStopOnce + services.StateMachine threadCtrl utils.ThreadControl lggr logger.Logger poller logpoller.LogPoller diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index edb1d337afd..388f7358ef3 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -68,7 +68,7 @@ type runner struct { // test helper runFinished func(*Run) - chStop utils.StopChan + chStop services.StopChan wgDone sync.WaitGroup } diff --git a/core/services/promreporter/prom_reporter.go b/core/services/promreporter/prom_reporter.go index 2306640bea6..d115b1022c1 100644 --- a/core/services/promreporter/prom_reporter.go +++ b/core/services/promreporter/prom_reporter.go @@ -27,7 +27,7 @@ type ( lggr logger.Logger backend PrometheusBackend newHeads *utils.Mailbox[*evmtypes.Head] - chStop utils.StopChan + chStop services.StopChan wgDone sync.WaitGroup reportPeriod time.Duration } diff --git a/core/services/relay/evm/functions/logpoller_wrapper.go b/core/services/relay/evm/functions/logpoller_wrapper.go index 230185d0ad7..95f45022ab3 100644 --- a/core/services/relay/evm/functions/logpoller_wrapper.go +++ b/core/services/relay/evm/functions/logpoller_wrapper.go @@ -39,7 +39,7 @@ type logPollerWrapper struct { detectedResponses detectedEvents mu sync.Mutex closeWait sync.WaitGroup - stopCh utils.StopChan + stopCh services.StopChan lggr logger.Logger } @@ -106,7 +106,7 @@ func NewLogPollerWrapper(routerContractAddress common.Address, pluginConfig conf logPoller: logPoller, client: client, subscribers: make(map[string]evmRelayTypes.RouteUpdateSubscriber), - stopCh: make(utils.StopChan), + stopCh: make(services.StopChan), lggr: lggr, }, nil } diff --git a/core/services/relay/evm/mercury/persistence_manager.go b/core/services/relay/evm/mercury/persistence_manager.go index 69dfce9c16d..779e275f154 100644 --- a/core/services/relay/evm/mercury/persistence_manager.go +++ b/core/services/relay/evm/mercury/persistence_manager.go @@ -24,7 +24,7 @@ type PersistenceManager struct { orm ORM once services.StateMachine - stopCh utils.StopChan + stopCh services.StopChan wg sync.WaitGroup deleteMu sync.Mutex @@ -41,7 +41,7 @@ func NewPersistenceManager(lggr logger.Logger, orm ORM, jobID int32, maxTransmit return &PersistenceManager{ lggr: lggr.Named("MercuryPersistenceManager"), orm: orm, - stopCh: make(chan struct{}), + stopCh: make(services.StopChan), jobID: jobID, maxTransmitQueueSize: maxTransmitQueueSize, flushDeletesFrequency: flushDeletesFrequency, diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go index 73aa10243f8..d5346ad28cc 100644 --- a/core/services/relay/evm/mercury/transmitter.go +++ b/core/services/relay/evm/mercury/transmitter.go @@ -115,7 +115,7 @@ type mercuryTransmitter struct { jobID int32 fromAccount string - stopCh utils.StopChan + stopCh services.StopChan queue *TransmitQueue wg sync.WaitGroup @@ -161,7 +161,7 @@ func NewTransmitter(lggr logger.Logger, cfgTracker ConfigTracker, rpcClient wsrp feedID, jobID, fmt.Sprintf("%x", fromAccount), - make(chan (struct{})), + make(services.StopChan), nil, sync.WaitGroup{}, make(chan *pb.TransmitRequest, maxDeleteQueueSize), diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index c4db80a58d0..c04c00074a2 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -84,7 +84,7 @@ type client struct { consecutiveTimeoutCnt atomic.Int32 wg sync.WaitGroup - chStop utils.StopChan + chStop services.StopChan chResetTransport chan struct{} timeoutCountMetric prometheus.Counter @@ -106,7 +106,7 @@ func newClient(lggr logger.Logger, clientPrivKey csakey.KeyV2, serverPubKey []by serverURL: serverURL, logger: lggr.Named("WSRPC").With("mercuryServerURL", serverURL), chResetTransport: make(chan struct{}, 1), - chStop: make(chan struct{}), + chStop: make(services.StopChan), timeoutCountMetric: timeoutCount.WithLabelValues(serverURL), dialCountMetric: dialCount.WithLabelValues(serverURL), dialSuccessCountMetric: dialSuccessCount.WithLabelValues(serverURL), diff --git a/core/services/synchronization/telemetry_ingress_batch_worker.go b/core/services/synchronization/telemetry_ingress_batch_worker.go index 141eb30c812..e7ea6595811 100644 --- a/core/services/synchronization/telemetry_ingress_batch_worker.go +++ b/core/services/synchronization/telemetry_ingress_batch_worker.go @@ -6,23 +6,23 @@ import ( "sync/atomic" "time" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // telemetryIngressBatchWorker pushes telemetry in batches to the ingress server via wsrpc. // A worker is created per ContractID. type telemetryIngressBatchWorker struct { - services.ServiceCtx + services.Service telemMaxBatchSize uint telemSendInterval time.Duration telemSendTimeout time.Duration telemClient telemPb.TelemClient wgDone *sync.WaitGroup - chDone utils.StopChan + chDone services.StopChan chTelemetry chan TelemPayload contractID string telemType TelemetryType diff --git a/core/services/synchronization/telemetry_ingress_client.go b/core/services/synchronization/telemetry_ingress_client.go index b889b3fc97a..b566199fdc0 100644 --- a/core/services/synchronization/telemetry_ingress_client.go +++ b/core/services/synchronization/telemetry_ingress_client.go @@ -15,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" telemPb "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type NoopTelemetryIngressClient struct{} @@ -46,7 +45,7 @@ type telemetryIngressClient struct { lggr logger.Logger wgDone sync.WaitGroup - chDone chan struct{} + chDone services.StopChan dropMessageCount atomic.Uint32 chTelemetry chan TelemPayload } @@ -61,7 +60,7 @@ func NewTelemetryIngressClient(url *url.URL, serverPubKeyHex string, ks keystore logging: logging, lggr: lggr.Named("TelemetryIngressClient"), chTelemetry: make(chan TelemPayload, telemBufferSize), - chDone: make(chan struct{}), + chDone: make(services.StopChan), } } @@ -132,7 +131,7 @@ func (tc *telemetryIngressClient) connect(ctx context.Context, clientPrivKey []b func (tc *telemetryIngressClient) handleTelemetry() { go func() { - ctx, cancel := utils.StopChan(tc.chDone).NewCtx() + ctx, cancel := tc.chDone.NewCtx() defer cancel() for { select { diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 566e5ac9bd8..3e958801cdf 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -58,7 +58,7 @@ type Listener struct { GethKs vrfcommon.GethKeyStore MailMon *utils.MailboxMonitor ReqLogs *utils.Mailbox[log.Broadcast] - ChStop utils.StopChan + ChStop services.StopChan WaitOnStop chan struct{} NewHead chan struct{} LatestHead uint64 diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 5b73ac9e24c..7f23e022771 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -128,7 +128,7 @@ func New( q: q, gethks: gethks, reqLogs: reqLogs, - chStop: make(chan struct{}), + chStop: make(services.StopChan), reqAdded: reqAdded, blockNumberToReqID: pairing.New(), latestHeadMu: sync.RWMutex{}, @@ -183,7 +183,7 @@ type listenerV2 struct { q pg.Q gethks keystore.Eth reqLogs *utils.Mailbox[log.Broadcast] - chStop utils.StopChan + chStop services.StopChan // We can keep these pending logs in memory because we // only mark them confirmed once we send a corresponding fulfillment transaction. // So on node restart in the middle of processing, the lb will resend them. diff --git a/core/services/webhook/delegate.go b/core/services/webhook/delegate.go index f5a8d553f23..237245b81c9 100644 --- a/core/services/webhook/delegate.go +++ b/core/services/webhook/delegate.go @@ -8,11 +8,12 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type ( @@ -111,7 +112,7 @@ func newWebhookJobRunner(runner pipeline.Runner, lggr logger.Logger) *webhookJob type registeredJob struct { job.Job - chRemove utils.StopChan + chRemove services.StopChan } func (r *webhookJobRunner) addSpec(spec job.Job) error { diff --git a/core/utils/thread_control.go b/core/utils/thread_control.go index 8f7fff42496..52cda82797a 100644 --- a/core/utils/thread_control.go +++ b/core/utils/thread_control.go @@ -3,6 +3,8 @@ package utils import ( "context" "sync" + + "github.com/smartcontractkit/chainlink-common/pkg/services" ) var _ ThreadControl = &threadControl{} @@ -25,7 +27,7 @@ func NewThreadControl() *threadControl { type threadControl struct { threadsWG sync.WaitGroup - stop StopChan + stop services.StopChan } func (tc *threadControl) Go(fn func(context.Context)) { diff --git a/core/utils/utils.go b/core/utils/utils.go index a2e418fe046..69597fb9e4a 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -399,68 +399,28 @@ func WaitGroupChan(wg *sync.WaitGroup) <-chan struct{} { } // WithCloseChan wraps a context so that it is canceled if the passed in channel is closed. -// Deprecated: Call StopChan.Ctx directly +// Deprecated: Call [services.StopChan.Ctx] directly func WithCloseChan(parentCtx context.Context, chStop chan struct{}) (context.Context, context.CancelFunc) { - return StopChan(chStop).Ctx(parentCtx) + return services.StopChan(chStop).Ctx(parentCtx) } // ContextFromChan creates a context that finishes when the provided channel receives or is closed. -// Deprecated: Call StopChan.NewCtx directly. +// Deprecated: Call [services.StopChan.NewCtx] directly. func ContextFromChan(chStop chan struct{}) (context.Context, context.CancelFunc) { - return StopChan(chStop).NewCtx() + return services.StopChan(chStop).NewCtx() } // ContextFromChanWithTimeout creates a context with a timeout that finishes when the provided channel receives or is closed. -// Deprecated: Call StopChan.CtxCancel directly +// Deprecated: Call [services.StopChan.CtxCancel] directly func ContextFromChanWithTimeout(chStop chan struct{}, timeout time.Duration) (context.Context, context.CancelFunc) { - return StopChan(chStop).CtxCancel(context.WithTimeout(context.Background(), timeout)) + return services.StopChan(chStop).CtxCancel(context.WithTimeout(context.Background(), timeout)) } -// A StopChan signals when some work should stop. -type StopChan chan struct{} +// Deprecated: use services.StopChan +type StopChan = services.StopChan -// NewCtx returns a background [context.Context] that is cancelled when StopChan is closed. -func (s StopChan) NewCtx() (context.Context, context.CancelFunc) { - return StopRChan((<-chan struct{})(s)).NewCtx() -} - -// Ctx cancels a [context.Context] when StopChan is closed. -func (s StopChan) Ctx(ctx context.Context) (context.Context, context.CancelFunc) { - return StopRChan((<-chan struct{})(s)).Ctx(ctx) -} - -// CtxCancel cancels a [context.Context] when StopChan is closed. -// Returns ctx and cancel unmodified, for convenience. -func (s StopChan) CtxCancel(ctx context.Context, cancel context.CancelFunc) (context.Context, context.CancelFunc) { - return StopRChan((<-chan struct{})(s)).CtxCancel(ctx, cancel) -} - -// A StopRChan signals when some work should stop. -// This version is receive-only. -type StopRChan <-chan struct{} - -// NewCtx returns a background [context.Context] that is cancelled when StopChan is closed. -func (s StopRChan) NewCtx() (context.Context, context.CancelFunc) { - return s.Ctx(context.Background()) -} - -// Ctx cancels a [context.Context] when StopChan is closed. -func (s StopRChan) Ctx(ctx context.Context) (context.Context, context.CancelFunc) { - return s.CtxCancel(context.WithCancel(ctx)) -} - -// CtxCancel cancels a [context.Context] when StopChan is closed. -// Returns ctx and cancel unmodified, for convenience. -func (s StopRChan) CtxCancel(ctx context.Context, cancel context.CancelFunc) (context.Context, context.CancelFunc) { - go func() { - select { - case <-s: - cancel() - case <-ctx.Done(): - } - }() - return ctx, cancel -} +// Deprecated: use services.StopRChan +type StopRChan = services.StopRChan // DependentAwaiter contains Dependent funcs type DependentAwaiter interface { diff --git a/core/utils/utils_example_test.go b/core/utils/utils_example_test.go deleted file mode 100644 index 0ca9d0be88d..00000000000 --- a/core/utils/utils_example_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package utils - -import ( - "context" - "sync" - "time" -) - -func ExampleStopChan() { - stopCh := make(StopChan) - - work := func(context.Context) {} - - a := func(ctx context.Context, done func()) { - defer done() - ctx, cancel := stopCh.Ctx(ctx) - defer cancel() - work(ctx) - } - - b := func(ctx context.Context, done func()) { - defer done() - ctx, cancel := stopCh.CtxCancel(context.WithTimeout(ctx, time.Second)) - defer cancel() - work(ctx) - } - - c := func(ctx context.Context, done func()) { - defer done() - ctx, cancel := stopCh.CtxCancel(context.WithDeadline(ctx, time.Now().Add(5*time.Second))) - defer cancel() - work(ctx) - } - - ctx, cancel := stopCh.NewCtx() - defer cancel() - - var wg sync.WaitGroup - wg.Add(3) - go a(ctx, wg.Done) - go b(ctx, wg.Done) - go c(ctx, wg.Done) - - time.AfterFunc(time.Second, func() { close(stopCh) }) - - wg.Wait() - // Output: -} diff --git a/core/utils/utils_test.go b/core/utils/utils_test.go index 04802feb3a7..e11c01a8e4f 100644 --- a/core/utils/utils_test.go +++ b/core/utils/utils_test.go @@ -374,79 +374,6 @@ func Test_WithJitter(t *testing.T) { } } -func Test_StartStopOnce_StopWaitsForStartToFinish(t *testing.T) { - t.Parallel() - - once := utils.StartStopOnce{} - - ch := make(chan int, 3) - - ready := make(chan bool) - - go func() { - assert.NoError(t, once.StartOnce("slow service", func() (err error) { - ch <- 1 - ready <- true - <-time.After(time.Millisecond * 500) // wait for StopOnce to happen - ch <- 2 - - return nil - })) - }() - - go func() { - <-ready // try stopping halfway through startup - assert.NoError(t, once.StopOnce("slow service", func() (err error) { - ch <- 3 - - return nil - })) - }() - - require.Equal(t, 1, <-ch) - require.Equal(t, 2, <-ch) - require.Equal(t, 3, <-ch) -} - -func Test_StartStopOnce_MultipleStartNoBlock(t *testing.T) { - t.Parallel() - - once := utils.StartStopOnce{} - - ch := make(chan int, 3) - - ready := make(chan bool) - next := make(chan bool) - - go func() { - ch <- 1 - assert.NoError(t, once.StartOnce("slow service", func() (err error) { - ready <- true - <-next // continue after the other StartOnce call fails - - return nil - })) - <-next - ch <- 2 - - }() - - go func() { - <-ready // try starting halfway through startup - assert.Error(t, once.StartOnce("slow service", func() (err error) { - return nil - })) - next <- true - ch <- 3 - next <- true - - }() - - require.Equal(t, 1, <-ch) - require.Equal(t, 3, <-ch) // 3 arrives before 2 because it returns immediately - require.Equal(t, 2, <-ch) -} - func TestAllEqual(t *testing.T) { t.Parallel() diff --git a/plugins/medianpoc/plugin.go b/plugins/medianpoc/plugin.go index 62b6acc043a..af4ec41ab8f 100644 --- a/plugins/medianpoc/plugin.go +++ b/plugins/medianpoc/plugin.go @@ -14,20 +14,19 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func NewPlugin(lggr logger.Logger) *Plugin { return &Plugin{ Plugin: loop.Plugin{Logger: lggr}, MedianProviderServer: reportingplugins.MedianProviderServer{}, - stop: make(utils.StopChan), + stop: make(services.StopChan), } } type Plugin struct { loop.Plugin - stop utils.StopChan + stop services.StopChan reportingplugins.MedianProviderServer } From f7949c5994b8c653072ea49747cce84dfe886df4 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 20 Nov 2023 18:13:50 -0600 Subject: [PATCH 180/271] replace context.Background() (#11334) --- .../dashboard/cmd/dashboard_deploy.go | 4 +- .../chainlink-cluster/dashboard/dashboard.go | 3 +- common/client/send_only_node_lifecycle.go | 7 +- .../evm/client/send_only_node_lifecycle.go | 7 +- core/chains/evm/gas/models_test.go | 4 +- core/chains/evm/logpoller/log_poller.go | 11 +- .../evm/logpoller/log_poller_internal_test.go | 147 ++++++++---------- core/chains/evm/logpoller/log_poller_test.go | 10 +- core/chains/evm/txmgr/evm_tx_store.go | 18 ++- core/cmd/ocr2vrf_configure_commands.go | 30 ++-- core/internal/cltest/cltest.go | 2 +- core/internal/testutils/pgtest/txdb_test.go | 4 +- core/services/blockhashstore/bhs_test.go | 8 +- core/services/feeds/service.go | 2 +- .../gateway/handlers/handler.dummy_test.go | 9 +- core/services/job/job_orm_test.go | 2 +- core/services/job/mocks/orm.go | 18 +-- core/services/job/orm.go | 6 +- core/services/keystore/starknet_test.go | 8 +- .../generic/pipeline_runner_adapter_test.go | 9 +- .../plugins/generic/telemetry_adapter_test.go | 7 +- .../ocr2/plugins/mercury/helpers_test.go | 4 +- .../plugins/ocr2keeper/evm20/registry_test.go | 7 +- .../ocr2keeper/evm21/block_subscriber_test.go | 6 +- .../evm21/logprovider/block_time_test.go | 5 +- .../evm21/logprovider/integration_test.go | 13 +- .../logprovider/provider_life_cycle_test.go | 11 +- .../evm21/logprovider/provider_test.go | 7 +- .../evm21/logprovider/recoverer_test.go | 11 +- .../evm21/mercury/streams/streams_test.go | 9 +- .../evm21/mercury/v02/v02_request_test.go | 6 +- .../evm21/mercury/v03/v03_request_test.go | 6 +- .../ocr2keeper/evm21/payload_builder_test.go | 6 +- .../evm21/registry_check_pipeline_test.go | 12 +- .../evm21/transmit/event_provider_test.go | 8 +- .../evm21/upkeepstate/store_test.go | 13 +- .../plugins/ocr2keeper/integration_21_test.go | 5 +- .../plugins/ocr2keeper/integration_test.go | 5 +- .../internal/ocr2vrf_integration_test.go | 8 +- .../ocr2/plugins/promwrapper/plugin_test.go | 16 +- .../evm/mercury/persistence_manager_test.go | 10 +- .../relay/evm/mercury/v1/data_source_test.go | 4 +- .../relay/grpc_provider_server_test.go | 4 +- core/services/relay/relay_test.go | 5 +- core/services/telemetry/manager_test.go | 3 +- .../services/transmission/integration_test.go | 3 +- core/services/vrf/v2/integration_v2_test.go | 10 +- core/web/jobs_controller.go | 2 +- .../automationv2_1/automationv2_1_test.go | 12 +- plugins/medianpoc/data_source_test.go | 7 +- plugins/medianpoc/plugin_test.go | 4 +- 51 files changed, 269 insertions(+), 269 deletions(-) diff --git a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go index c752794f53f..170ffa02883 100644 --- a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go +++ b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go @@ -1,12 +1,14 @@ package main import ( + "context" "os" "github.com/smartcontractkit/chainlink/v2/dashboard/dashboard" ) func main() { + ctx := context.Background() name := os.Getenv("DASHBOARD_NAME") if name == "" { panic("DASHBOARD_NAME must be provided") @@ -36,7 +38,7 @@ func main() { if err != nil { panic(err) } - if err := db.Deploy(); err != nil { + if err := db.Deploy(ctx); err != nil { panic(err) } } diff --git a/charts/chainlink-cluster/dashboard/dashboard.go b/charts/chainlink-cluster/dashboard/dashboard.go index 7918b996dd0..293cded2b0c 100644 --- a/charts/chainlink-cluster/dashboard/dashboard.go +++ b/charts/chainlink-cluster/dashboard/dashboard.go @@ -378,8 +378,7 @@ func (m *CLClusterDashboard) generate() error { } // Deploy deploys the dashboard to Grafana -func (m *CLClusterDashboard) Deploy() error { - ctx := context.Background() +func (m *CLClusterDashboard) Deploy(ctx context.Context) error { client := grabana.NewClient(&http.Client{}, m.GrafanaURL, grabana.WithAPIToken(m.GrafanaToken)) folder, err := client.FindOrCreateFolder(ctx, m.Folder) if err != nil { diff --git a/common/client/send_only_node_lifecycle.go b/common/client/send_only_node_lifecycle.go index 0f663eab30e..4d5b102b5bd 100644 --- a/common/client/send_only_node_lifecycle.go +++ b/common/client/send_only_node_lifecycle.go @@ -1,7 +1,6 @@ package client import ( - "context" "fmt" "time" @@ -14,15 +13,17 @@ import ( // It will continue checking until success and then exit permanently. func (s *sendOnlyNode[CHAIN_ID, RPC]) verifyLoop() { defer s.wg.Done() + ctx, cancel := s.chStop.NewCtx() + defer cancel() backoff := utils.NewRedialBackoff() for { select { - case <-s.chStop: + case <-ctx.Done(): return case <-time.After(backoff.Duration()): } - chainID, err := s.rpc.ChainID(context.Background()) + chainID, err := s.rpc.ChainID(ctx) if err != nil { ok := s.IfStarted(func() { if changed := s.setState(nodeStateUnreachable); changed { diff --git a/core/chains/evm/client/send_only_node_lifecycle.go b/core/chains/evm/client/send_only_node_lifecycle.go index 509be53c8a3..9d704e49389 100644 --- a/core/chains/evm/client/send_only_node_lifecycle.go +++ b/core/chains/evm/client/send_only_node_lifecycle.go @@ -1,7 +1,6 @@ package client import ( - "context" "fmt" "time" @@ -14,12 +13,14 @@ import ( // It will continue checking until success and then exit permanently. func (s *sendOnlyNode) verifyLoop() { defer s.wg.Done() + ctx, cancel := s.chStop.NewCtx() + defer cancel() backoff := utils.NewRedialBackoff() for { select { case <-time.After(backoff.Duration()): - chainID, err := s.sender.ChainID(context.Background()) + chainID, err := s.sender.ChainID(ctx) if err != nil { ok := s.IfStarted(func() { if changed := s.setState(NodeStateUnreachable); changed { @@ -60,7 +61,7 @@ func (s *sendOnlyNode) verifyLoop() { s.log.Infow("Sendonly RPC Node is online", "nodeState", s.state) return } - case <-s.chStop: + case <-ctx.Done(): return } } diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index 8ac94a2269c..a2dce58ee3f 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -1,7 +1,6 @@ package gas_test import ( - "context" "math/big" "testing" @@ -14,11 +13,12 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" rollupMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) func TestWrappedEvmEstimator(t *testing.T) { t.Parallel() - ctx := context.Background() + ctx := testutils.Context(t) // fee values gasLimit := uint32(10) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 6676b694b0f..bb93db40378 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -130,8 +130,10 @@ type logPoller struct { // support chain, polygon, which has 2s block times, we need RPCs roughly with <= 500ms latency func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Duration, useFinalityTag bool, finalityDepth int64, backfillBatchSize int64, rpcBatchSize int64, keepFinalizedBlocksDepth int64) *logPoller { - + ctx, cancel := context.WithCancel(context.Background()) return &logPoller{ + ctx: ctx, + cancel: cancel, ec: ec, orm: orm, lggr: lggr.Named("LogPoller"), @@ -371,18 +373,15 @@ func (lp *logPoller) recvReplayComplete() { func (lp *logPoller) ReplayAsync(fromBlock int64) { lp.wg.Add(1) go func() { - if err := lp.Replay(context.Background(), fromBlock); err != nil { + if err := lp.Replay(lp.ctx, fromBlock); err != nil { lp.lggr.Error(err) } lp.wg.Done() }() } -func (lp *logPoller) Start(parentCtx context.Context) error { +func (lp *logPoller) Start(context.Context) error { return lp.StartOnce("LogPoller", func() error { - ctx, cancel := context.WithCancel(parentCtx) - lp.ctx = ctx - lp.cancel = cancel lp.wg.Add(1) go lp.run() return nil diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index c0d081582f7..2ef276802ba 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/services" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" @@ -233,7 +234,6 @@ func TestLogPoller_BackupPollerStartup(t *testing.T) { func TestLogPoller_Replay(t *testing.T) { t.Parallel() addr := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbc") - tctx := testutils.Context(t) lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.ErrorLevel) chainID := testutils.FixtureChainID @@ -259,48 +259,39 @@ func TestLogPoller_Replay(t *testing.T) { lp := NewLogPoller(orm, ec, lggr, time.Hour, false, 3, 3, 3, 20) // process 1 log in block 3 - lp.PollAndSaveLogs(tctx, 4) + lp.PollAndSaveLogs(testutils.Context(t), 4) latest, err := lp.LatestBlock() require.NoError(t, err) require.Equal(t, int64(4), latest.BlockNumber) t.Run("abort before replayStart received", func(t *testing.T) { // Replay() should abort immediately if caller's context is cancelled before request signal is read - ctx, cancel := context.WithCancel(tctx) + ctx, cancel := context.WithCancel(testutils.Context(t)) cancel() err = lp.Replay(ctx, 3) assert.ErrorIs(t, err, ErrReplayRequestAborted) }) - recvStartReplay := func(parentCtx context.Context, block int64, withTimeout bool) { - var err error - var ctx context.Context - var cancel context.CancelFunc - if withTimeout { - ctx, cancel = context.WithTimeout(parentCtx, testutils.WaitTimeout(t)) - } else { - ctx, cancel = context.WithCancel(parentCtx) - } - defer cancel() + recvStartReplay := func(ctx context.Context, block int64) { select { case fromBlock := <-lp.replayStart: assert.Equal(t, block, fromBlock) case <-ctx.Done(): - err = ctx.Err() + assert.NoError(t, ctx.Err(), "Timed out waiting to receive replay request from lp.replayStart") } - assert.NoError(t, err, "Timed out waiting to receive replay request from lp.replayStart") } // Replay() should return error code received from replayComplete t.Run("returns error code on replay complete", func(t *testing.T) { + ctx := testutils.Context(t) anyErr := errors.New("any error") done := make(chan struct{}) go func() { defer close(done) - recvStartReplay(tctx, 1, true) + recvStartReplay(ctx, 1) lp.replayComplete <- anyErr }() - assert.ErrorIs(t, lp.Replay(tctx, 1), anyErr) + assert.ErrorIs(t, lp.Replay(ctx, 1), anyErr) <-done }) @@ -310,7 +301,7 @@ func TestLogPoller_Replay(t *testing.T) { done := make(chan struct{}) go func() { defer close(done) - recvStartReplay(ctx, 4, false) + recvStartReplay(ctx, 4) cancel() }() assert.ErrorIs(t, lp.Replay(ctx, 4), ErrReplayInProgress) @@ -323,29 +314,33 @@ func TestLogPoller_Replay(t *testing.T) { t.Run("client abort doesnt hang run loop", func(t *testing.T) { lp.backupPollerNextBlock = 0 - timeLeft := testutils.WaitTimeout(t) - timeout := time.After(timeLeft) - ctx, cancel := context.WithCancel(tctx) + ctx := testutils.Context(t) - var wg sync.WaitGroup pass := make(chan struct{}) cancelled := make(chan struct{}) + rctx, rcancel := context.WithCancel(testutils.Context(t)) + var wg sync.WaitGroup + defer func() { wg.Wait() }() ec.On("FilterLogs", mock.Anything, mock.Anything).Once().Return([]types.Log{log1}, nil).Run(func(args mock.Arguments) { wg.Add(1) go func() { defer wg.Done() - assert.ErrorIs(t, lp.Replay(ctx, 4), ErrReplayInProgress) + assert.ErrorIs(t, lp.Replay(rctx, 4), ErrReplayInProgress) close(cancelled) }() }) ec.On("FilterLogs", mock.Anything, mock.Anything).Once().Return([]types.Log{log1}, nil).Run(func(args mock.Arguments) { - cancel() + rcancel() wg.Add(1) go func() { defer wg.Done() - lp.replayStart <- 4 - close(pass) + select { + case lp.replayStart <- 4: + close(pass) + case <-ctx.Done(): + return + } }() // We cannot return until we're sure that Replay() received the cancellation signal, // otherwise replayComplete<- might be sent first @@ -354,24 +349,13 @@ func TestLogPoller_Replay(t *testing.T) { ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil).Maybe() // in case task gets delayed by >= 100ms - lp.ctx, lp.cancel = context.WithCancel(tctx) - lp.wg.Add(1) - defer func() { - select { - case <-lp.replayStart: - default: - } - wg.Wait() - lp.cancel() - lp.wg.Wait() - }() + t.Cleanup(lp.reset) + require.NoError(t, lp.Start(ctx)) + t.Cleanup(func() { assert.NoError(t, lp.Close()) }) - go func() { - lp.run() - }() select { - case <-timeout: - assert.Failf(t, "lp.run() got stuck--failed to respond to second replay event within %s", timeLeft.String()) + case <-ctx.Done(): + t.Errorf("timed out waiting for lp.run() to respond to second replay event") case <-pass: } }) @@ -383,13 +367,18 @@ func TestLogPoller_Replay(t *testing.T) { t.Run("shutdown during replay", func(t *testing.T) { lp.backupPollerNextBlock = 0 - safeToExit := make(chan struct{}) pass := make(chan struct{}) + done := make(chan struct{}) + defer func() { <-done }() + ctx := testutils.Context(t) ec.On("FilterLogs", mock.Anything, mock.Anything).Once().Return([]types.Log{log1}, nil).Run(func(args mock.Arguments) { go func() { - lp.replayStart <- 4 - close(safeToExit) + defer close(done) + select { + case lp.replayStart <- 4: + case <-ctx.Done(): + } }() }) ec.On("FilterLogs", mock.Anything, mock.Anything).Once().Return([]types.Log{log1}, nil).Run(func(args mock.Arguments) { @@ -398,71 +387,57 @@ func TestLogPoller_Replay(t *testing.T) { }) ec.On("FilterLogs", mock.Anything, mock.Anything).Return([]types.Log{log1}, nil).Maybe() // in case task gets delayed by >= 100ms - timeLeft := testutils.WaitTimeout(t) - timeout := time.After(timeLeft) - require.NoError(t, lp.Start(tctx)) - - defer func() { - select { - case <-lp.replayStart: // unblock replayStart<- goroutine if it's stuck - default: - } - <-safeToExit - lp.Close() - }() + t.Cleanup(lp.reset) + require.NoError(t, lp.Start(ctx)) + t.Cleanup(func() { assert.NoError(t, lp.Close()) }) select { - case <-timeout: - assert.Failf(t, "lp.run() failed to respond to shutdown event during replay within %s", timeLeft.String()) + case <-ctx.Done(): + t.Error("timed out waiting for lp.run() to respond to shutdown event during replay") case <-pass: } }) // ReplayAsync should return as soon as replayStart is received t.Run("ReplayAsync success", func(t *testing.T) { - lp.ctx, lp.cancel = context.WithTimeout(tctx, testutils.WaitTimeout(t)) - defer func() { - lp.replayComplete <- nil - lp.cancel() - lp.wg.Wait() - }() + t.Cleanup(lp.reset) + require.NoError(t, lp.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, lp.Close()) }) - done := make(chan struct{}) - go func() { - lp.ReplayAsync(1) - close(done) - }() - recvStartReplay(tctx, 1, true) - <-done + lp.ReplayAsync(1) + + recvStartReplay(testutils.Context(t), 1) }) t.Run("ReplayAsync error", func(t *testing.T) { - timeLeft := testutils.WaitTimeout(t) - lp.ctx, lp.cancel = context.WithTimeout(tctx, timeLeft) - defer func() { - lp.cancel() - lp.wg.Wait() - }() + t.Cleanup(lp.reset) + require.NoError(t, lp.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, lp.Close()) }) + anyErr := errors.New("async error") observedLogs.TakeAll() lp.ReplayAsync(4) - recvStartReplay(tctx, 4, true) + recvStartReplay(testutils.Context(t), 4) select { case lp.replayComplete <- anyErr: time.Sleep(2 * time.Second) case <-lp.ctx.Done(): - assert.Failf(t, "failed to receive replayComplete signal within %s", timeLeft.String()) + t.Error("timed out waiting to send replaceComplete") } require.Equal(t, 1, observedLogs.Len()) assert.Equal(t, observedLogs.All()[0].Message, anyErr.Error()) }) } +func (lp *logPoller) reset() { + lp.StateMachine = services.StateMachine{} + lp.ctx, lp.cancel = context.WithCancel(context.Background()) +} + func Test_latestBlockAndFinalityDepth(t *testing.T) { - tctx := testutils.Context(t) - lggr, _ := logger.TestLoggerObserved(t, zapcore.ErrorLevel) + lggr := logger.TestLogger(t) chainID := testutils.FixtureChainID db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) @@ -474,7 +449,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { ec.On("HeadByNumber", mock.Anything, mock.Anything).Return(&head, nil) lp := NewLogPoller(orm, ec, lggr, time.Hour, false, finalityDepth, 3, 3, 20) - latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(tctx) + latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(testutils.Context(t)) require.NoError(t, err) require.Equal(t, latestBlock.Number, head.Number) require.Equal(t, finalityDepth, latestBlock.Number-lastFinalizedBlockNumber) @@ -499,7 +474,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20) - latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(tctx) + latestBlock, lastFinalizedBlockNumber, err := lp.latestBlocks(testutils.Context(t)) require.NoError(t, err) require.Equal(t, expectedLatestBlockNumber, latestBlock.Number) require.Equal(t, expectedLastFinalizedBlockNumber, lastFinalizedBlockNumber) @@ -516,7 +491,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { }) lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20) - _, _, err := lp.latestBlocks(tctx) + _, _, err := lp.latestBlocks(testutils.Context(t)) require.Error(t, err) }) @@ -525,7 +500,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { ec.On("BatchCallContext", mock.Anything, mock.Anything).Return(fmt.Errorf("some error")) lp := NewLogPoller(orm, ec, lggr, time.Hour, true, 3, 3, 3, 20) - _, _, err := lp.latestBlocks(tctx) + _, _, err := lp.latestBlocks(testutils.Context(t)) require.Error(t, err) }) }) diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 5f013ca9140..94589f505a6 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -626,19 +626,19 @@ func TestLogPoller_BlockTimestamps(t *testing.T) { require.Len(t, gethLogs, 2) lb, _ := th.LogPoller.LatestBlock(pg.WithParentCtx(testutils.Context(t))) - th.PollAndSaveLogs(context.Background(), lb.BlockNumber+1) + th.PollAndSaveLogs(ctx, lb.BlockNumber+1) lg1, err := th.LogPoller.Logs(0, 20, EmitterABI.Events["Log1"].ID, th.EmitterAddress1, - pg.WithParentCtx(testutils.Context(t))) + pg.WithParentCtx(ctx)) require.NoError(t, err) lg2, err := th.LogPoller.Logs(0, 20, EmitterABI.Events["Log2"].ID, th.EmitterAddress2, - pg.WithParentCtx(testutils.Context(t))) + pg.WithParentCtx(ctx)) require.NoError(t, err) // Logs should have correct timestamps - b, _ := th.Client.BlockByHash(context.Background(), lg1[0].BlockHash) + b, _ := th.Client.BlockByHash(ctx, lg1[0].BlockHash) t.Log(len(lg1), lg1[0].BlockTimestamp) assert.Equal(t, int64(b.Time()), lg1[0].BlockTimestamp.UTC().Unix(), time1) - b2, _ := th.Client.BlockByHash(context.Background(), lg2[0].BlockHash) + b2, _ := th.Client.BlockByHash(ctx, lg2[0].BlockHash) assert.Equal(t, int64(b2.Time()), lg2[0].BlockTimestamp.UTC().Unix(), time2) } diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 9262c85a833..0e08d32b77a 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1107,11 +1107,9 @@ ORDER BY nonce ASC return etxs, pkgerrors.Wrap(err, "FindTransactionsConfirmedInBlockRange failed") } -func saveAttemptWithNewState(q pg.Queryer, timeout time.Duration, logger logger.Logger, attempt TxAttempt, broadcastAt time.Time) error { - ctx, cancel := context.WithTimeout(context.Background(), timeout) +func saveAttemptWithNewState(ctx context.Context, q pg.Queryer, logger logger.Logger, attempt TxAttempt, broadcastAt time.Time) error { var dbAttempt DbEthTxAttempt dbAttempt.FromTxAttempt(&attempt) - defer cancel() return pg.SqlxTransaction(ctx, q, logger, func(tx pg.Queryer) error { // In case of null broadcast_at (shouldn't happen) we don't want to // update anyway because it indicates a state where broadcast_at makes @@ -1133,15 +1131,19 @@ func (o *evmTxStore) SaveInsufficientFundsAttempt(ctx context.Context, timeout t return errors.New("expected state to be either in_progress or insufficient_eth") } attempt.State = txmgrtypes.TxAttemptInsufficientFunds - return pkgerrors.Wrap(saveAttemptWithNewState(qq, timeout, o.logger, *attempt, broadcastAt), "saveInsufficientEthAttempt failed") + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + return pkgerrors.Wrap(saveAttemptWithNewState(ctx, qq, o.logger, *attempt, broadcastAt), "saveInsufficientEthAttempt failed") } -func saveSentAttempt(q pg.Queryer, timeout time.Duration, logger logger.Logger, attempt *TxAttempt, broadcastAt time.Time) error { +func saveSentAttempt(ctx context.Context, q pg.Queryer, timeout time.Duration, logger logger.Logger, attempt *TxAttempt, broadcastAt time.Time) error { if attempt.State != txmgrtypes.TxAttemptInProgress { return errors.New("expected state to be in_progress") } attempt.State = txmgrtypes.TxAttemptBroadcast - return pkgerrors.Wrap(saveAttemptWithNewState(q, timeout, logger, *attempt, broadcastAt), "saveSentAttempt failed") + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return pkgerrors.Wrap(saveAttemptWithNewState(ctx, q, logger, *attempt, broadcastAt), "saveSentAttempt failed") } func (o *evmTxStore) SaveSentAttempt(ctx context.Context, timeout time.Duration, attempt *TxAttempt, broadcastAt time.Time) error { @@ -1149,7 +1151,7 @@ func (o *evmTxStore) SaveSentAttempt(ctx context.Context, timeout time.Duration, ctx, cancel = o.mergeContexts(ctx) defer cancel() qq := o.q.WithOpts(pg.WithParentCtx(ctx)) - return saveSentAttempt(qq, timeout, o.logger, attempt, broadcastAt) + return saveSentAttempt(ctx, qq, timeout, o.logger, attempt, broadcastAt) } func (o *evmTxStore) SaveConfirmedMissingReceiptAttempt(ctx context.Context, timeout time.Duration, attempt *TxAttempt, broadcastAt time.Time) error { @@ -1158,7 +1160,7 @@ func (o *evmTxStore) SaveConfirmedMissingReceiptAttempt(ctx context.Context, tim defer cancel() qq := o.q.WithOpts(pg.WithParentCtx(ctx)) err := qq.Transaction(func(tx pg.Queryer) error { - if err := saveSentAttempt(tx, timeout, o.logger, attempt, broadcastAt); err != nil { + if err := saveSentAttempt(ctx, tx, timeout, o.logger, attempt, broadcastAt); err != nil { return err } if _, err := tx.Exec(`UPDATE evm.txes SET state = 'confirmed_missing_receipt' WHERE id = $1`, attempt.TxID); err != nil { diff --git a/core/cmd/ocr2vrf_configure_commands.go b/core/cmd/ocr2vrf_configure_commands.go index bb4cef4708b..cf014d5e5dc 100644 --- a/core/cmd/ocr2vrf_configure_commands.go +++ b/core/cmd/ocr2vrf_configure_commands.go @@ -126,6 +126,7 @@ chainID = %d const forwarderAdditionalEOACount = 4 func (s *Shell) ConfigureOCR2VRFNode(c *cli.Context, owner *bind.TransactOpts, ec *ethclient.Client) (*SetupOCR2VRFNodePayload, error) { + ctx := s.ctx() lggr := logger.Sugared(s.Logger.Named("ConfigureOCR2VRFNode")) lggr.Infow( fmt.Sprintf("Configuring Chainlink Node for job type %s %s at commit %s", c.String("job-type"), static.Version, static.Sha), @@ -156,15 +157,13 @@ func (s *Shell) ConfigureOCR2VRFNode(c *cli.Context, owner *bind.TransactOpts, e cfg := s.Config ldb := pg.NewLockedDB(cfg.AppID(), cfg.Database(), cfg.Database().Lock(), lggr) - rootCtx, cancel := context.WithCancel(context.Background()) - defer cancel() - if err = ldb.Open(rootCtx); err != nil { + if err = ldb.Open(ctx); err != nil { return nil, s.errorOut(errors.Wrap(err, "opening db")) } defer lggr.ErrorIfFn(ldb.Close, "Error closing db") - app, err := s.AppFactory.NewApplication(rootCtx, s.Config, lggr, ldb.DB()) + app, err := s.AppFactory.NewApplication(ctx, s.Config, lggr, ldb.DB()) if err != nil { return nil, s.errorOut(errors.Wrap(err, "fatal error instantiating application")) } @@ -179,7 +178,7 @@ func (s *Shell) ConfigureOCR2VRFNode(c *cli.Context, owner *bind.TransactOpts, e } // Start application. - err = app.Start(rootCtx) + err = app.Start(ctx) if err != nil { return nil, s.errorOut(err) } @@ -243,10 +242,10 @@ func (s *Shell) ConfigureOCR2VRFNode(c *cli.Context, owner *bind.TransactOpts, e if c.Bool("isBootstrapper") { // Set up bootstrapper job if bootstrapper. - err = createBootstrapperJob(lggr, c, app) + err = createBootstrapperJob(ctx, lggr, c, app) } else if c.String("job-type") == "DKG" { // Set up DKG job. - err = createDKGJob(lggr, app, dkgTemplateArgs{ + err = createDKGJob(ctx, lggr, app, dkgTemplateArgs{ contractID: c.String("contractID"), ocrKeyBundleID: ocr2.ID(), p2pv2BootstrapperPeerID: peerID, @@ -260,7 +259,7 @@ func (s *Shell) ConfigureOCR2VRFNode(c *cli.Context, owner *bind.TransactOpts, e }) } else if c.String("job-type") == "OCR2VRF" { // Set up OCR2VRF job. - err = createOCR2VRFJob(lggr, app, ocr2vrfTemplateArgs{ + err = createOCR2VRFJob(ctx, lggr, app, ocr2vrfTemplateArgs{ dkgTemplateArgs: dkgTemplateArgs{ contractID: c.String("dkg-address"), ocrKeyBundleID: ocr2.ID(), @@ -320,12 +319,13 @@ func (s *Shell) appendForwarders(chainID int64, ks keystore.Eth, sendingKeys []s } func (s *Shell) authorizeForwarder(c *cli.Context, db *sqlx.DB, lggr logger.Logger, chainID int64, ec *ethclient.Client, owner *bind.TransactOpts, sendingKeysAddresses []common.Address) error { + ctx := s.ctx() // Replace the transmitter ID with the forwarder address. forwarderAddress := c.String("forwarder-address") // We have to set the authorized senders on-chain here, otherwise the job spawner will fail as the // forwarder will not be recognized. - ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second) + ctx, cancel := context.WithTimeout(ctx, 300*time.Second) defer cancel() f, err := authorized_forwarder.NewAuthorizedForwarder(common.HexToAddress(forwarderAddress), ec) if err != nil { @@ -400,7 +400,7 @@ func setupKeystore(cli *Shell, app chainlink.Application, keyStore keystore.Mast return nil } -func createBootstrapperJob(lggr logger.Logger, c *cli.Context, app chainlink.Application) error { +func createBootstrapperJob(ctx context.Context, lggr logger.Logger, c *cli.Context, app chainlink.Application) error { sp := fmt.Sprintf(BootstrapTemplate, c.Int64("chainID"), c.String("contractID"), @@ -418,7 +418,7 @@ func createBootstrapperJob(lggr logger.Logger, c *cli.Context, app chainlink.App } jb.BootstrapSpec = &os - err = app.AddJobV2(context.Background(), &jb) + err = app.AddJobV2(ctx, &jb) if err != nil { return errors.Wrap(err, "failed to add job") } @@ -430,7 +430,7 @@ func createBootstrapperJob(lggr logger.Logger, c *cli.Context, app chainlink.App return nil } -func createDKGJob(lggr logger.Logger, app chainlink.Application, args dkgTemplateArgs) error { +func createDKGJob(ctx context.Context, lggr logger.Logger, app chainlink.Application, args dkgTemplateArgs) error { sp := fmt.Sprintf(DKGTemplate, args.contractID, args.ocrKeyBundleID, @@ -455,7 +455,7 @@ func createDKGJob(lggr logger.Logger, app chainlink.Application, args dkgTemplat } jb.OCR2OracleSpec = &os - err = app.AddJobV2(context.Background(), &jb) + err = app.AddJobV2(ctx, &jb) if err != nil { return errors.Wrap(err, "failed to add job") } @@ -464,7 +464,7 @@ func createDKGJob(lggr logger.Logger, app chainlink.Application, args dkgTemplat return nil } -func createOCR2VRFJob(lggr logger.Logger, app chainlink.Application, args ocr2vrfTemplateArgs) error { +func createOCR2VRFJob(ctx context.Context, lggr logger.Logger, app chainlink.Application, args ocr2vrfTemplateArgs) error { var sendingKeysString = fmt.Sprintf(`"%s"`, args.sendingKeys[0]) for x := 1; x < len(args.sendingKeys); x++ { sendingKeysString = fmt.Sprintf(`%s,"%s"`, sendingKeysString, args.sendingKeys[x]) @@ -498,7 +498,7 @@ func createOCR2VRFJob(lggr logger.Logger, app chainlink.Application, args ocr2vr } jb.OCR2OracleSpec = &os - err = app.AddJobV2(context.Background(), &jb) + err = app.AddJobV2(ctx, &jb) if err != nil { return errors.Wrap(err, "failed to add job") } diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 02aa2de0cc0..cf3f4f5c073 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -1076,7 +1076,7 @@ type TransactionReceipter interface { func RequireTxSuccessful(t testing.TB, client TransactionReceipter, txHash common.Hash) *types.Receipt { t.Helper() - r, err := client.TransactionReceipt(context.Background(), txHash) + r, err := client.TransactionReceipt(testutils.Context(t), txHash) require.NoError(t, err) require.NotNil(t, r) require.Equal(t, uint64(1), r.Status) diff --git a/core/internal/testutils/pgtest/txdb_test.go b/core/internal/testutils/pgtest/txdb_test.go index c1aeef4b8c2..37339bf28be 100644 --- a/core/internal/testutils/pgtest/txdb_test.go +++ b/core/internal/testutils/pgtest/txdb_test.go @@ -8,6 +8,8 @@ import ( "github.com/google/uuid" "github.com/jmoiron/sqlx" "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) func TestTxDBDriver(t *testing.T) { @@ -35,7 +37,7 @@ func TestTxDBDriver(t *testing.T) { ensureValuesPresent(t, db) t.Run("Cancel of tx's context does not trigger rollback of driver's tx", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(testutils.Context(t)) _, err := db.BeginTx(ctx, nil) assert.NoError(t, err) cancel() diff --git a/core/services/blockhashstore/bhs_test.go b/core/services/blockhashstore/bhs_test.go index 5c501a62ac9..44205ec7b86 100644 --- a/core/services/blockhashstore/bhs_test.go +++ b/core/services/blockhashstore/bhs_test.go @@ -1,7 +1,6 @@ package blockhashstore_test import ( - "context" "testing" "github.com/ethereum/go-ethereum/common" @@ -12,6 +11,7 @@ import ( txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "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" @@ -66,9 +66,11 @@ func TestStoreRotatesFromAddresses(t *testing.T) { return tx.FromAddress.String() == k2.Address.String() })).Once().Return(txmgr.Tx{}, nil) + ctx := testutils.Context(t) + // store 2 blocks - err = bhs.Store(context.Background(), 1) + err = bhs.Store(ctx, 1) require.NoError(t, err) - err = bhs.Store(context.Background(), 2) + err = bhs.Store(ctx, 2) require.NoError(t, err) } diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index da19a33abc8..32a8432f876 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -186,7 +186,7 @@ func (s *service) RegisterManager(ctx context.Context, params RegisterManagerPar } var id int64 - q := s.q.WithOpts(pg.WithParentCtx(context.Background())) + q := s.q.WithOpts(pg.WithParentCtx(ctx)) err = q.Transaction(func(tx pg.Queryer) error { var txerr error diff --git a/core/services/gateway/handlers/handler.dummy_test.go b/core/services/gateway/handlers/handler.dummy_test.go index c2c7a7a12a3..c3f23935e67 100644 --- a/core/services/gateway/handlers/handler.dummy_test.go +++ b/core/services/gateway/handlers/handler.dummy_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" @@ -41,15 +42,17 @@ func TestDummyHandler_BasicFlow(t *testing.T) { require.NoError(t, err) connMgr.SetHandler(handler) + ctx := testutils.Context(t) + // User request msg := api.Message{Body: api.MessageBody{MessageId: "1234"}} callbackCh := make(chan handlers.UserCallbackPayload, 1) - require.NoError(t, handler.HandleUserMessage(context.Background(), &msg, callbackCh)) + require.NoError(t, handler.HandleUserMessage(ctx, &msg, callbackCh)) require.Equal(t, 2, connMgr.sendCounter) // Responses from both nodes - require.NoError(t, handler.HandleNodeMessage(context.Background(), &msg, "addr_1")) - require.NoError(t, handler.HandleNodeMessage(context.Background(), &msg, "addr_2")) + require.NoError(t, handler.HandleNodeMessage(ctx, &msg, "addr_1")) + require.NoError(t, handler.HandleNodeMessage(ctx, &msg, "addr_2")) response := <-callbackCh require.Equal(t, "1234", response.Msg.Body.MessageId) } diff --git a/core/services/job/job_orm_test.go b/core/services/job/job_orm_test.go index 87ee15873d5..21035140f54 100644 --- a/core/services/job/job_orm_test.go +++ b/core/services/job/job_orm_test.go @@ -1615,7 +1615,7 @@ func Test_FindJobWithoutSpecErrors(t *testing.T) { jb, err = orm.FindJobWithoutSpecErrors(jobSpec.ID) require.NoError(t, err) - jbWithErrors, err := orm.FindJobTx(jobSpec.ID) + jbWithErrors, err := orm.FindJobTx(testutils.Context(t), jobSpec.ID) require.NoError(t, err) assert.Equal(t, len(jb.JobSpecErrors), 0) diff --git a/core/services/job/mocks/orm.go b/core/services/job/mocks/orm.go index 3d858f81320..9e18573f4e5 100644 --- a/core/services/job/mocks/orm.go +++ b/core/services/job/mocks/orm.go @@ -247,23 +247,23 @@ func (_m *ORM) FindJobIDsWithBridge(name string) ([]int32, error) { return r0, r1 } -// FindJobTx provides a mock function with given fields: id -func (_m *ORM) FindJobTx(id int32) (job.Job, error) { - ret := _m.Called(id) +// FindJobTx provides a mock function with given fields: ctx, id +func (_m *ORM) FindJobTx(ctx context.Context, id int32) (job.Job, error) { + ret := _m.Called(ctx, id) var r0 job.Job var r1 error - if rf, ok := ret.Get(0).(func(int32) (job.Job, error)); ok { - return rf(id) + if rf, ok := ret.Get(0).(func(context.Context, int32) (job.Job, error)); ok { + return rf(ctx, id) } - if rf, ok := ret.Get(0).(func(int32) job.Job); ok { - r0 = rf(id) + if rf, ok := ret.Get(0).(func(context.Context, int32) job.Job); ok { + r0 = rf(ctx, id) } else { r0 = ret.Get(0).(job.Job) } - if rf, ok := ret.Get(1).(func(int32) error); ok { - r1 = rf(id) + if rf, ok := ret.Get(1).(func(context.Context, int32) error); ok { + r1 = rf(ctx, id) } else { r1 = ret.Error(1) } diff --git a/core/services/job/orm.go b/core/services/job/orm.go index ba102c6bb8b..c5f533c3d20 100644 --- a/core/services/job/orm.go +++ b/core/services/job/orm.go @@ -49,7 +49,7 @@ type ORM interface { InsertJob(job *Job, qopts ...pg.QOpt) error CreateJob(jb *Job, qopts ...pg.QOpt) error FindJobs(offset, limit int) ([]Job, int, error) - FindJobTx(id int32) (Job, error) + FindJobTx(ctx context.Context, id int32) (Job, error) FindJob(ctx context.Context, id int32) (Job, error) FindJobByExternalJobID(uuid uuid.UUID, qopts ...pg.QOpt) (Job, error) FindJobIDByAddress(address ethkey.EIP55Address, evmChainID *utils.Big, qopts ...pg.QOpt) (int32, error) @@ -782,8 +782,8 @@ func LoadConfigVarsOCR(evmOcrCfg evmconfig.OCR, ocrCfg OCRConfig, os OCROracleSp return LoadConfigVarsLocalOCR(evmOcrCfg, os, ocrCfg), nil } -func (o *orm) FindJobTx(id int32) (Job, error) { - ctx, cancel := context.WithTimeout(context.Background(), o.cfg.DefaultQueryTimeout()) +func (o *orm) FindJobTx(ctx context.Context, id int32) (Job, error) { + ctx, cancel := context.WithTimeout(ctx, o.cfg.DefaultQueryTimeout()) defer cancel() return o.FindJob(ctx, id) } diff --git a/core/services/keystore/starknet_test.go b/core/services/keystore/starknet_test.go index df9516f8710..7fc5718bac0 100644 --- a/core/services/keystore/starknet_test.go +++ b/core/services/keystore/starknet_test.go @@ -1,7 +1,6 @@ package keystore_test import ( - "context" "fmt" "math/big" "testing" @@ -13,6 +12,7 @@ import ( "github.com/smartcontractkit/caigo" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "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/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -121,13 +121,13 @@ func TestStarknetSigner(t *testing.T) { // on existing sender id t.Run("key exists", func(t *testing.T) { baseKs.On("Get", starknetSenderAddr).Return(starkKey, nil) - signed, err := lk.Sign(context.Background(), starknetSenderAddr, nil) + signed, err := lk.Sign(testutils.Context(t), starknetSenderAddr, nil) require.Nil(t, signed) require.NoError(t, err) }) t.Run("key doesn't exists", func(t *testing.T) { baseKs.On("Get", mock.Anything).Return(starkkey.Key{}, fmt.Errorf("key doesn't exist")) - signed, err := lk.Sign(context.Background(), "not an address", nil) + signed, err := lk.Sign(testutils.Context(t), "not an address", nil) require.Nil(t, signed) require.Error(t, err) }) @@ -140,7 +140,7 @@ func TestStarknetSigner(t *testing.T) { baseKs.On("Get", starknetSenderAddr).Return(starkKey, nil) hash, err := caigo.Curve.PedersenHash([]*big.Int{big.NewInt(42)}) require.NoError(t, err) - r, s, err := adapter.Sign(context.Background(), starknetSenderAddr, hash) + r, s, err := adapter.Sign(testutils.Context(t), starknetSenderAddr, hash) require.NoError(t, err) require.NotNil(t, r) require.NotNil(t, s) diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go index f70e0dd443a..c2060a92905 100644 --- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go +++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter_test.go @@ -14,6 +14,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" + "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/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -53,7 +54,7 @@ func TestAdapter_Integration(t *testing.T) { http.DefaultClient, ) pra := generic.NewPipelineRunnerAdapter(logger, job.Job{}, pr) - results, err := pra.ExecuteRun(context.Background(), spec, types.Vars{Vars: map[string]interface{}{"val": 1}}, types.Options{}) + results, err := pra.ExecuteRun(testutils.Context(t), spec, types.Vars{Vars: map[string]interface{}{"val": 1}}, types.Options{}) require.NoError(t, err) finalResult := results[0].Value.(decimal.Decimal) @@ -85,7 +86,7 @@ func TestAdapter_AddsDefaultVars(t *testing.T) { jobID, externalJobID, name := int32(100), uuid.New(), null.StringFrom("job-name") pra := generic.NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name}, mpr) - _, err := pra.ExecuteRun(context.Background(), spec, types.Vars{}, types.Options{}) + _, err := pra.ExecuteRun(testutils.Context(t), spec, types.Vars{}, types.Options{}) require.NoError(t, err) gotName, err := mpr.vars.Get("jb.name") @@ -107,8 +108,8 @@ func TestPipelineRunnerAdapter_SetsVarsOnSpec(t *testing.T) { jobID, externalJobID, name, jobType := int32(100), uuid.New(), null.StringFrom("job-name"), job.Type("generic") pra := generic.NewPipelineRunnerAdapter(logger, job.Job{ID: jobID, ExternalJobID: externalJobID, Name: name, Type: jobType}, mpr) - maxDuration := time.Duration(100 * time.Second) - _, err := pra.ExecuteRun(context.Background(), spec, types.Vars{}, types.Options{MaxTaskDuration: maxDuration}) + maxDuration := 100 * time.Second + _, err := pra.ExecuteRun(testutils.Context(t), spec, types.Vars{}, types.Options{MaxTaskDuration: maxDuration}) require.NoError(t, err) assert.Equal(t, jobID, mpr.spec.JobID) diff --git a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go index e137343f2b4..24f422bf0cb 100644 --- a/core/services/ocr2/plugins/generic/telemetry_adapter_test.go +++ b/core/services/ocr2/plugins/generic/telemetry_adapter_test.go @@ -1,13 +1,14 @@ package generic_test import ( - "context" "testing" - "github.com/smartcontractkit/libocr/commontypes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/libocr/commontypes" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" ) @@ -88,7 +89,7 @@ func TestTelemetryAdapter(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - err := ta.Send(context.Background(), test.networkID, test.chainID, test.contractID, test.telemetryType, test.payload) + err := ta.Send(testutils.Context(t), test.networkID, test.chainID, test.contractID, test.telemetryType, test.payload) if test.errorMsg != "" { assert.ErrorContains(t, err, test.errorMsg) } else { diff --git a/core/services/ocr2/plugins/mercury/helpers_test.go b/core/services/ocr2/plugins/mercury/helpers_test.go index 588f772120e..ed59213840c 100644 --- a/core/services/ocr2/plugins/mercury/helpers_test.go +++ b/core/services/ocr2/plugins/mercury/helpers_test.go @@ -138,14 +138,14 @@ func (node *Node) AddJob(t *testing.T, spec string) { c := node.App.GetConfig() job, err := validate.ValidatedOracleSpecToml(c.OCR2(), c.Insecure(), spec) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &job) + err = node.App.AddJobV2(testutils.Context(t), &job) require.NoError(t, err) } func (node *Node) AddBootstrapJob(t *testing.T, spec string) { job, err := ocrbootstrap.ValidatedBootstrapSpecToml(spec) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &job) + err = node.App.AddJobV2(testutils.Context(t), &job) require.NoError(t, err) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go index 8662bfd0475..340bd923577 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go @@ -1,21 +1,22 @@ package evm import ( - "context" "fmt" "math/big" "testing" "time" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -54,7 +55,7 @@ func TestGetActiveUpkeepKeys(t *testing.T) { active: actives, } - keys, err := rg.GetActiveUpkeepIDs(context.Background()) + keys, err := rg.GetActiveUpkeepIDs(testutils.Context(t)) if test.ExpectedErr != nil { assert.ErrorIs(t, err, test.ExpectedErr) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go index 004b5fac6cc..afb9d4a0919 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go @@ -1,16 +1,16 @@ package evm import ( - "context" "fmt" "testing" "time" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -304,7 +304,7 @@ func TestBlockSubscriber_Start(t *testing.T) { bs := NewBlockSubscriber(hb, lp, finality, lggr) bs.blockHistorySize = historySize bs.blockSize = blockSize - err := bs.Start(context.Background()) + err := bs.Start(testutils.Context(t)) assert.Nil(t, err) h97 := evmtypes.Head{ diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go index 7009cfaa9b2..683ba378940 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go @@ -1,7 +1,6 @@ package logprovider import ( - "context" "fmt" "testing" "time" @@ -11,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) func TestBlockTimeResolver_BlockTime(t *testing.T) { @@ -63,8 +63,7 @@ func TestBlockTimeResolver_BlockTime(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) lp := new(lpmocks.LogPoller) resolver := newBlockTimeResolver(lp) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index 0df774d5dfd..c7db01d8788 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -614,10 +614,11 @@ func deployUpkeepCounter( backend *backends.SimulatedBackend, account *bind.TransactOpts, logProvider logprovider.LogEventProvider, -) ([]*big.Int, []common.Address, []*log_upkeep_counter_wrapper.LogUpkeepCounter) { - var ids []*big.Int - var contracts []*log_upkeep_counter_wrapper.LogUpkeepCounter - var contractsAddrs []common.Address +) ( + ids []*big.Int, + contractsAddrs []common.Address, + contracts []*log_upkeep_counter_wrapper.LogUpkeepCounter, +) { for i := 0; i < n; i++ { upkeepAddr, _, upkeepContract, err := log_upkeep_counter_wrapper.DeployLogUpkeepCounter( account, backend, @@ -633,7 +634,7 @@ func deployUpkeepCounter( upkeepID := ocr2keepers.UpkeepIdentifier(append(common.LeftPadBytes([]byte{1}, 16), upkeepAddr[:16]...)) id := upkeepID.BigInt() ids = append(ids, id) - b, err := ethClient.BlockByHash(context.Background(), backend.Commit()) + b, err := ethClient.BlockByHash(ctx, backend.Commit()) require.NoError(t, err) bn := b.Number() err = logProvider.RegisterFilter(ctx, logprovider.FilterOptions{ @@ -643,7 +644,7 @@ func deployUpkeepCounter( }) require.NoError(t, err) } - return ids, contractsAddrs, contracts + return } func newPlainLogTriggerConfig(upkeepAddr common.Address) logprovider.LogTriggerConfig { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go index 03395cb5b5f..b28b45fcb42 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go @@ -1,19 +1,20 @@ package logprovider import ( - "context" "fmt" "math/big" "testing" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ) @@ -103,8 +104,7 @@ func TestLogEventProvider_LifeCycle(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) if tc.mockPoller { lp := new(mocks.LogPoller) @@ -144,8 +144,7 @@ func TestLogEventProvider_LifeCycle(t *testing.T) { } func TestEventLogProvider_RefreshActiveUpkeeps(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) mp := new(mocks.LogPoller) mp.On("RegisterFilter", mock.Anything).Return(nil) mp.On("UnregisterFilter", mock.Anything).Return(nil) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go index a8e33ba23b7..c6ebb1c51a3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "golang.org/x/time/rate" @@ -174,8 +175,7 @@ func TestLogEventProvider_ScheduleReadJobs(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) readInterval := 10 * time.Millisecond opts := NewOptions(200) @@ -239,8 +239,7 @@ func TestLogEventProvider_ScheduleReadJobs(t *testing.T) { } func TestLogEventProvider_ReadLogs(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) mp := new(mocks.LogPoller) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go index c882a22bc1a..77b0eec5454 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go @@ -11,11 +11,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" lpmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" @@ -29,8 +30,7 @@ import ( ) func TestLogRecoverer_GetRecoverables(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) lp := &lpmocks.LogPoller{} lp.On("LatestBlock", mock.Anything).Return(logpoller.LogPollerBlock{BlockNumber: 100}, nil) r := NewLogRecoverer(logger.TestLogger(t), lp, nil, nil, nil, nil, NewOptions(200)) @@ -213,8 +213,7 @@ func TestLogRecoverer_Clean(t *testing.T) { } func TestLogRecoverer_Recover(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) tests := []struct { name string @@ -1079,7 +1078,7 @@ func TestLogRecoverer_GetProposalData(t *testing.T) { recoverer.states = tc.stateReader } - b, err := recoverer.GetProposalData(context.Background(), tc.proposal) + b, err := recoverer.GetProposalData(testutils.Context(t), tc.proposal) if tc.expectErr { assert.Error(t, err) assert.Equal(t, tc.wantErr.Error(), err.Error()) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go index 194d74febba..0653796f413 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go @@ -2,7 +2,6 @@ package streams import ( "bytes" - "context" "encoding/json" "fmt" "io" @@ -20,11 +19,13 @@ import ( "github.com/stretchr/testify/assert" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/mock" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" @@ -257,7 +258,7 @@ func TestStreams_CheckCallback(t *testing.T) { }).Once() s.client = client - state, retryable, _, err := s.checkCallback(context.Background(), tt.values, tt.lookup) + 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) @@ -719,7 +720,7 @@ func TestStreams_StreamsLookup(t *testing.T) { }).Once() } - got := s.Lookup(context.Background(), tt.input) + got := s.Lookup(testutils.Context(t), tt.input) assert.Equal(t, tt.expectedResults, got, tt.name) }) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go index 17ef8515fd1..2aecc0df771 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go @@ -2,7 +2,6 @@ package v02 import ( "bytes" - "context" "encoding/json" "errors" "io" @@ -16,6 +15,7 @@ import ( "github.com/patrickmn/go-cache" "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -258,7 +258,7 @@ func TestV02_SingleFeedRequest(t *testing.T) { c.httpClient = hc ch := make(chan mercury.MercuryData, 1) - c.singleFeedRequest(context.Background(), ch, tt.index, tt.lookup) + c.singleFeedRequest(testutils.Context(t), ch, tt.index, tt.lookup) m := <-ch assert.Equal(t, tt.index, m.Index) @@ -450,7 +450,7 @@ func TestV02_DoMercuryRequestV02(t *testing.T) { } c.httpClient = hc - state, reason, values, retryable, retryInterval, reqErr := c.DoRequest(context.Background(), tt.lookup, tt.pluginRetryKey) + state, reason, values, retryable, retryInterval, reqErr := c.DoRequest(testutils.Context(t), tt.lookup, tt.pluginRetryKey) assert.Equal(t, tt.expectedValues, values) assert.Equal(t, tt.expectedRetryable, retryable) if retryable { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go index bef2cdac58a..049448f8f76 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go @@ -2,7 +2,6 @@ package v03 import ( "bytes" - "context" "encoding/json" "io" "math/big" @@ -14,6 +13,7 @@ import ( "github.com/patrickmn/go-cache" "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "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/evm21/mocks" @@ -162,7 +162,7 @@ func TestV03_DoMercuryRequestV03(t *testing.T) { } c.httpClient = hc - state, reason, values, retryable, retryInterval, reqErr := c.DoRequest(context.Background(), tt.lookup, tt.pluginRetryKey) + state, reason, values, retryable, retryInterval, reqErr := c.DoRequest(testutils.Context(t), tt.lookup, tt.pluginRetryKey) assert.Equal(t, tt.expectedValues, values) assert.Equal(t, tt.expectedRetryable, retryable) @@ -514,7 +514,7 @@ func TestV03_MultiFeedRequest(t *testing.T) { c.httpClient = hc ch := make(chan mercury.MercuryData, 1) - c.multiFeedsRequest(context.Background(), ch, tt.lookup) + c.multiFeedsRequest(testutils.Context(t), ch, tt.lookup) m := <-ch assert.Equal(t, 0, m.Index) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go index e75084ff968..e68e316ae30 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go @@ -6,9 +6,11 @@ import ( "testing" "github.com/pkg/errors" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" @@ -191,7 +193,7 @@ func TestNewPayloadBuilder(t *testing.T) { t.Run(tc.name, func(t *testing.T) { lggr, _ := logger.NewLogger() builder := NewPayloadBuilder(tc.activeList, tc.recoverer, lggr) - payloads, err := builder.BuildPayloads(context.Background(), tc.proposals...) + payloads, err := builder.BuildPayloads(testutils.Context(t), tc.proposals...) assert.NoError(t, err) assert.Equal(t, tc.wantPayloads, payloads) }) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go index 2e39892e478..d4e38637d8c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go @@ -18,13 +18,15 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -205,7 +207,7 @@ func TestRegistry_VerifyCheckBlock(t *testing.T) { e.client = client } - state, retryable := e.verifyCheckBlock(context.Background(), tc.checkBlock, tc.upkeepId, tc.checkHash) + state, retryable := e.verifyCheckBlock(testutils.Context(t), tc.checkBlock, tc.upkeepId, tc.checkHash) assert.Equal(t, tc.state, state) assert.Equal(t, tc.retryable, retryable) }) @@ -350,7 +352,7 @@ func TestRegistry_VerifyLogExists(t *testing.T) { e := &EvmRegistry{ lggr: lggr, bs: bs, - ctx: context.Background(), + ctx: testutils.Context(t), } if tc.makeEthCall { @@ -530,7 +532,7 @@ func TestRegistry_CheckUpkeeps(t *testing.T) { } e.client = client - results, err := e.checkUpkeeps(context.Background(), tc.inputs) + results, err := e.checkUpkeeps(testutils.Context(t), tc.inputs) assert.Equal(t, tc.results, results) assert.Equal(t, tc.err, err) }) @@ -651,7 +653,7 @@ func TestRegistry_SimulatePerformUpkeeps(t *testing.T) { }).Once() e.client = client - results, err := e.simulatePerformUpkeeps(context.Background(), tc.inputs) + results, err := e.simulatePerformUpkeeps(testutils.Context(t), tc.inputs) assert.Equal(t, tc.results, results) assert.Equal(t, tc.err, err) }) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go index 58e95bc423e..a33056977ac 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go @@ -1,29 +1,29 @@ package transmit import ( - "context" "math/big" "runtime" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ) func TestTransmitEventProvider_Sanity(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := testutils.Context(t) lp := new(mocks.LogPoller) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go index 8e2e77f7fb4..579d8757921 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go @@ -357,7 +357,7 @@ func TestUpkeepStateStore_SetSelectIntegration(t *testing.T) { }) for _, insert := range test.storedValues { - require.NoError(t, store.SetUpkeepState(context.Background(), insert.result, insert.state), "storing states should not produce an error") + require.NoError(t, store.SetUpkeepState(ctx, insert.result, insert.state), "storing states should not produce an error") } tickerCh <- time.Now() @@ -408,7 +408,7 @@ func TestUpkeepStateStore_emptyDB(t *testing.T) { scanner := &mockScanner{} store := NewUpkeepStateStore(orm, lggr, scanner) - states, err := store.SelectByWorkIDs(context.Background(), []string{"0x1", "0x2", "0x3", "0x4"}...) + states, err := store.SelectByWorkIDs(testutils.Context(t), []string{"0x1", "0x2", "0x3", "0x4"}...) assert.NoError(t, err) assert.Equal(t, []ocr2keepers.UpkeepState{ ocr2keepers.UnknownState, @@ -454,6 +454,7 @@ func TestUpkeepStateStore_Upsert(t *testing.T) { } func TestUpkeepStateStore_Service(t *testing.T) { + ctx := testutils.Context(t) orm := &mockORM{ onDelete: func(tm time.Time) { @@ -466,10 +467,10 @@ func TestUpkeepStateStore_Service(t *testing.T) { store.retention = 500 * time.Millisecond store.cleanCadence = 100 * time.Millisecond - assert.NoError(t, store.Start(context.Background()), "no error from starting service") + assert.NoError(t, store.Start(ctx), "no error from starting service") // add a value to set up the test - require.NoError(t, store.SetUpkeepState(context.Background(), ocr2keepers.CheckResult{ + require.NoError(t, store.SetUpkeepState(ctx, ocr2keepers.CheckResult{ Eligible: false, WorkID: "0x2", Trigger: ocr2keepers.Trigger{ @@ -481,7 +482,7 @@ func TestUpkeepStateStore_Service(t *testing.T) { time.Sleep(110 * time.Millisecond) // select from store to ensure values still exist - values, err := store.SelectByWorkIDs(context.Background(), "0x2") + values, err := store.SelectByWorkIDs(ctx, "0x2") require.NoError(t, err, "no error from selecting states") require.Equal(t, []ocr2keepers.UpkeepState{ocr2keepers.Ineligible}, values, "selected values should match expected") @@ -489,7 +490,7 @@ func TestUpkeepStateStore_Service(t *testing.T) { time.Sleep(700 * time.Millisecond) // select from store to ensure cached values were removed - values, err = store.SelectByWorkIDs(context.Background(), "0x2") + values, err = store.SelectByWorkIDs(ctx, "0x2") require.NoError(t, err, "no error from selecting states") require.Equal(t, []ocr2keepers.UpkeepState{ocr2keepers.UnknownState}, values, "selected values should match expected") diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index 109a644ca09..d2f35a3e37a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -890,6 +890,7 @@ func (c *feedLookupUpkeepController) EnableMercury( MercuryEnabled: true, }) + ctx := testutils.Context(t) for _, id := range c.upkeepIds { if _, err := registry.SetUpkeepPrivilegeConfig(registryOwner, id, adminBytes); err != nil { require.NoError(t, err) @@ -900,7 +901,7 @@ func (c *feedLookupUpkeepController) EnableMercury( callOpts := &bind.CallOpts{ Pending: true, From: registryOwner.From, - Context: context.Background(), + Context: ctx, } bts, err := registry.GetUpkeepPrivilegeConfig(callOpts, id) @@ -989,7 +990,7 @@ func (c *feedLookupUpkeepController) EmitEvents( backend.Commit() // verify event was emitted - block, _ := backend.BlockByHash(context.Background(), backend.Commit()) + block, _ := backend.BlockByHash(ctx, backend.Commit()) t.Logf("block number after emit event: %d", block.NumberU64()) iter, _ := c.protocol.FilterLimitOrderExecuted( diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index a2184d92aec..3eda8867968 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -1,7 +1,6 @@ package ocr2keeper_test import ( - "context" "crypto/rand" "encoding/hex" "encoding/json" @@ -171,14 +170,14 @@ func (node *Node) AddJob(t *testing.T, spec string) { c := node.App.GetConfig() jb, err := validate.ValidatedOracleSpecToml(c.OCR2(), c.Insecure(), spec) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &jb) + err = node.App.AddJobV2(testutils.Context(t), &jb) require.NoError(t, err) } func (node *Node) AddBootstrapJob(t *testing.T, spec string) { jb, err := ocrbootstrap.ValidatedBootstrapSpecToml(spec) require.NoError(t, err) - err = node.App.AddJobV2(context.Background(), &jb) + err = node.App.AddJobV2(testutils.Context(t), &jb) require.NoError(t, err) } diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index 57d13a69ec5..38d7acf7e9b 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -1,7 +1,6 @@ package internal_test import ( - "context" "crypto/rand" "encoding/hex" "errors" @@ -336,6 +335,7 @@ func TestIntegration_OCR2VRF(t *testing.T) { } func runOCR2VRFTest(t *testing.T, useForwarders bool) { + ctx := testutils.Context(t) keyID := randomKeyID(t) uni := setupOCR2VRFContracts(t, 5, keyID, false) @@ -409,7 +409,7 @@ func runOCR2VRFTest(t *testing.T, useForwarders bool) { } }() - blockBeforeConfig, err := uni.backend.BlockByNumber(context.Background(), nil) + blockBeforeConfig, err := uni.backend.BlockByNumber(ctx, nil) require.NoError(t, err) t.Log("Setting DKG config before block:", blockBeforeConfig.Number().String()) @@ -447,7 +447,7 @@ fromBlock = %d t.Log("Creating bootstrap job:", bootstrapJobSpec) ocrJob, err := ocrbootstrap.ValidatedBootstrapSpecToml(bootstrapJobSpec) require.NoError(t, err) - err = bootstrapNode.app.AddJobV2(context.Background(), &ocrJob) + err = bootstrapNode.app.AddJobV2(ctx, &ocrJob) require.NoError(t, err) t.Log("Creating OCR2VRF jobs") @@ -499,7 +499,7 @@ linkEthFeedAddress = "%s" t.Log("Creating OCR2VRF job with spec:", jobSpec) ocrJob2, err2 := validate.ValidatedOracleSpecToml(apps[i].Config.OCR2(), apps[i].Config.Insecure(), jobSpec) require.NoError(t, err2) - err2 = apps[i].AddJobV2(context.Background(), &ocrJob2) + err2 = apps[i].AddJobV2(ctx, &ocrJob2) require.NoError(t, err2) } diff --git a/core/services/ocr2/plugins/promwrapper/plugin_test.go b/core/services/ocr2/plugins/promwrapper/plugin_test.go index 5c12c18f852..b4de7f027f3 100644 --- a/core/services/ocr2/plugins/promwrapper/plugin_test.go +++ b/core/services/ocr2/plugins/promwrapper/plugin_test.go @@ -8,10 +8,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/promwrapper/mocks" ) @@ -194,14 +196,16 @@ func TestPlugin_GetLatencies(t *testing.T) { ).(*promPlugin) require.NotEqual(t, nil, promPlugin) + ctx := testutils.Context(t) + // Run OCR methods. - _, err := promPlugin.Query(context.Background(), reportTimestamp) + _, err := promPlugin.Query(ctx, reportTimestamp) require.NoError(t, err) _, ok := promPlugin.queryEndTimes.Load(reportTimestamp) require.Equal(t, true, ok) time.Sleep(qToOLatency) - _, err = promPlugin.Observation(context.Background(), reportTimestamp, nil) + _, err = promPlugin.Observation(ctx, reportTimestamp, nil) require.NoError(t, err) _, ok = promPlugin.queryEndTimes.Load(reportTimestamp) require.Equal(t, false, ok) @@ -209,7 +213,7 @@ func TestPlugin_GetLatencies(t *testing.T) { require.Equal(t, true, ok) time.Sleep(oToRLatency) - _, _, err = promPlugin.Report(context.Background(), reportTimestamp, nil, nil) + _, _, err = promPlugin.Report(ctx, reportTimestamp, nil, nil) require.NoError(t, err) _, ok = promPlugin.observationEndTimes.Load(reportTimestamp) require.Equal(t, false, ok) @@ -217,7 +221,7 @@ func TestPlugin_GetLatencies(t *testing.T) { require.Equal(t, true, ok) time.Sleep(rToALatency) - _, err = promPlugin.ShouldAcceptFinalizedReport(context.Background(), reportTimestamp, nil) + _, err = promPlugin.ShouldAcceptFinalizedReport(ctx, reportTimestamp, nil) require.NoError(t, err) _, ok = promPlugin.reportEndTimes.Load(reportTimestamp) require.Equal(t, false, ok) @@ -225,7 +229,7 @@ func TestPlugin_GetLatencies(t *testing.T) { require.Equal(t, true, ok) time.Sleep(aToTLatency) - _, err = promPlugin.ShouldTransmitAcceptedReport(context.Background(), reportTimestamp, nil) + _, err = promPlugin.ShouldTransmitAcceptedReport(ctx, reportTimestamp, nil) require.NoError(t, err) _, ok = promPlugin.acceptFinalizedReportEndTimes.Load(reportTimestamp) require.Equal(t, false, ok) diff --git a/core/services/relay/evm/mercury/persistence_manager_test.go b/core/services/relay/evm/mercury/persistence_manager_test.go index dbdb9777252..755d64a5a23 100644 --- a/core/services/relay/evm/mercury/persistence_manager_test.go +++ b/core/services/relay/evm/mercury/persistence_manager_test.go @@ -1,18 +1,18 @@ package mercury import ( - "context" "testing" "time" "github.com/cometbft/cometbft/libs/rand" "github.com/jmoiron/sqlx" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -30,7 +30,7 @@ func TestPersistenceManager(t *testing.T) { jobID1 := rand.Int32() jobID2 := jobID1 + 1 - ctx := context.Background() + ctx := testutils.Context(t) db := pgtest.NewSqlxDB(t) pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) @@ -69,7 +69,7 @@ func TestPersistenceManager(t *testing.T) { } func TestPersistenceManagerAsyncDelete(t *testing.T) { - ctx := context.Background() + ctx := testutils.Context(t) jobID := rand.Int32() db := pgtest.NewSqlxDB(t) pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) @@ -120,7 +120,7 @@ func TestPersistenceManagerPrune(t *testing.T) { pgtest.MustExec(t, db, `SET CONSTRAINTS mercury_transmit_requests_job_id_fkey DEFERRED`) pgtest.MustExec(t, db, `SET CONSTRAINTS feed_latest_reports_job_id_fkey DEFERRED`) - ctx := context.Background() + ctx := testutils.Context(t) reports := make([][]byte, 25) for i := 0; i < 25; i++ { diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 635658d7863..6eb9f430c60 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -432,7 +432,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { ds.chainReader = evm.NewChainReader(headTracker) obs := relaymercuryv1.Observation{} - err := ds.setLatestBlocks(context.Background(), &obs) + err := ds.setLatestBlocks(testutils.Context(t), &obs) assert.NoError(t, err) assert.Equal(t, h.Number, obs.CurrentBlockNum.Val) @@ -450,7 +450,7 @@ func TestMercury_SetLatestBlocks(t *testing.T) { ds.chainReader = evm.NewChainReader(headTracker) obs := relaymercuryv1.Observation{} - err := ds.setLatestBlocks(context.Background(), &obs) + err := ds.setLatestBlocks(testutils.Context(t), &obs) assert.NoError(t, err) assert.Zero(t, obs.CurrentBlockNum.Val) diff --git a/core/services/relay/grpc_provider_server_test.go b/core/services/relay/grpc_provider_server_test.go index fafe20ef12a..6aff32f5e32 100644 --- a/core/services/relay/grpc_provider_server_test.go +++ b/core/services/relay/grpc_provider_server_test.go @@ -1,19 +1,19 @@ package relay import ( - "context" "testing" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestProviderServer(t *testing.T) { r := &mockRelayer{} sa := NewServerAdapter(r, mockRelayerExt{}) - mp, _ := sa.NewPluginProvider(context.Background(), types.RelayArgs{ProviderType: string(types.Median)}, types.PluginArgs{}) + mp, _ := sa.NewPluginProvider(testutils.Context(t), types.RelayArgs{ProviderType: string(types.Median)}, types.PluginArgs{}) lggr := logger.TestLogger(t) _, err := NewProviderServer(mp, "unsupported-type", lggr) diff --git a/core/services/relay/relay_test.go b/core/services/relay/relay_test.go index fc9e273e302..18a7b1b44ea 100644 --- a/core/services/relay/relay_test.go +++ b/core/services/relay/relay_test.go @@ -1,7 +1,6 @@ package relay import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -11,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" ) func TestIdentifier_UnmarshalString(t *testing.T) { @@ -160,9 +160,10 @@ func TestRelayerServerAdapter(t *testing.T) { }, } + ctx := testutils.Context(t) for _, tc := range testCases { pp, err := sa.NewPluginProvider( - context.Background(), + ctx, types.RelayArgs{ProviderType: tc.ProviderType}, types.PluginArgs{}, ) diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index d24fb348b75..31f7ea74c19 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -1,7 +1,6 @@ package telemetry import ( - "context" "fmt" "math/big" "net/url" @@ -190,7 +189,7 @@ func TestNewManager(t *testing.T) { require.Equal(t, "TelemetryManager", m.Name()) - require.Nil(t, m.Start(context.Background())) + require.Nil(t, m.Start(testutils.Context(t))) testutils.WaitForLogMessageCount(t, logObs, "error connecting error while dialing dial tcp", 3) hr := m.HealthReport() diff --git a/core/services/transmission/integration_test.go b/core/services/transmission/integration_test.go index 58521dcdf84..c8c6137cad7 100644 --- a/core/services/transmission/integration_test.go +++ b/core/services/transmission/integration_test.go @@ -1,7 +1,6 @@ package transmission_test import ( - "context" "math/big" "testing" @@ -398,7 +397,7 @@ func Test4337WithLinkTokenVRFRequestAndPaymaster(t *testing.T) { ) require.NoError(t, err) backend.Commit() - _, err = bind.WaitMined(context.Background(), backend, tx) + _, err = bind.WaitMined(testutils.Context(t), backend, tx) require.NoError(t, err) // Generate encoded paymaster data to fund the VRF consumer. diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 15121ba306c..6880fa17992 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -1,7 +1,6 @@ package v2_test import ( - "context" "encoding/hex" "encoding/json" "fmt" @@ -422,21 +421,22 @@ func deployOldCoordinator( common.Address, *vrf_coordinator_v2.VRFCoordinatorV2, ) { + ctx := testutils.Context(t) bytecode := hexutil.MustDecode("0x60e06040523480156200001157600080fd5b506040516200608c3803806200608c8339810160408190526200003491620001b1565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be81620000e8565b5050506001600160601b0319606093841b811660805290831b811660a052911b1660c052620001fb565b6001600160a01b038116331415620001435760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b80516001600160a01b0381168114620001ac57600080fd5b919050565b600080600060608486031215620001c757600080fd5b620001d28462000194565b9250620001e26020850162000194565b9150620001f26040850162000194565b90509250925092565b60805160601c60a05160601c60c05160601c615e2762000265600039600081816105260152613bd901526000818161061d015261402401526000818161036d01528181611599015281816125960152818161302c0152818161318201526138360152615e276000f3fe608060405234801561001057600080fd5b506004361061025b5760003560e01c80636f64f03f11610145578063ad178361116100bd578063d2f9f9a71161008c578063e72f6e3011610071578063e72f6e30146106fa578063e82ad7d41461070d578063f2fde38b1461073057600080fd5b8063d2f9f9a7146106d4578063d7ae1d30146106e757600080fd5b8063ad17836114610618578063af198b971461063f578063c3f909d41461066f578063caf70c4a146106c157600080fd5b80638da5cb5b11610114578063a21a23e4116100f9578063a21a23e4146105da578063a47c7696146105e2578063a4c0ed361461060557600080fd5b80638da5cb5b146105a95780639f87fad7146105c757600080fd5b80636f64f03f146105685780637341c10c1461057b57806379ba50971461058e578063823597401461059657600080fd5b8063356dac71116101d85780635fbbc0d2116101a757806366316d8d1161018c57806366316d8d1461050e578063689c45171461052157806369bcdb7d1461054857600080fd5b80635fbbc0d21461040057806364d51a2a1461050657600080fd5b8063356dac71146103b457806340d6bb82146103bc5780634cb48a54146103da5780635d3b1d30146103ed57600080fd5b806308821d581161022f57806315c48b841161021457806315c48b841461030e578063181f5a77146103295780631b6b6d231461036857600080fd5b806308821d58146102cf57806312b58349146102e257600080fd5b80620122911461026057806302bcc5b61461028057806304c357cb1461029557806306bfa637146102a8575b600080fd5b610268610743565b60405161027793929190615964565b60405180910390f35b61029361028e366004615792565b6107bf565b005b6102936102a33660046157ad565b61086b565b60055467ffffffffffffffff165b60405167ffffffffffffffff9091168152602001610277565b6102936102dd3660046154a3565b610a60565b6005546801000000000000000090046bffffffffffffffffffffffff165b604051908152602001610277565b61031660c881565b60405161ffff9091168152602001610277565b604080518082018252601681527f565246436f6f7264696e61746f72563220312e302e30000000000000000000006020820152905161027791906158f1565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610277565b600a54610300565b6103c56101f481565b60405163ffffffff9091168152602001610277565b6102936103e836600461563c565b610c3f565b6103006103fb366004615516565b611036565b600c546040805163ffffffff80841682526401000000008404811660208301526801000000000000000084048116928201929092526c010000000000000000000000008304821660608201527001000000000000000000000000000000008304909116608082015262ffffff740100000000000000000000000000000000000000008304811660a0830152770100000000000000000000000000000000000000000000008304811660c08301527a0100000000000000000000000000000000000000000000000000008304811660e08301527d01000000000000000000000000000000000000000000000000000000000090920490911661010082015261012001610277565b610316606481565b61029361051c36600461545b565b611444565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b610300610556366004615779565b60009081526009602052604090205490565b6102936105763660046153a0565b6116ad565b6102936105893660046157ad565b6117f7565b610293611a85565b6102936105a4366004615792565b611b82565b60005473ffffffffffffffffffffffffffffffffffffffff1661038f565b6102936105d53660046157ad565b611d7c565b6102b66121fd565b6105f56105f0366004615792565b6123ed565b6040516102779493929190615b02565b6102936106133660046153d4565b612537565b61038f7f000000000000000000000000000000000000000000000000000000000000000081565b61065261064d366004615574565b6127a8565b6040516bffffffffffffffffffffffff9091168152602001610277565b600b546040805161ffff8316815263ffffffff6201000084048116602083015267010000000000000084048116928201929092526b010000000000000000000000909204166060820152608001610277565b6103006106cf3660046154bf565b612c6d565b6103c56106e2366004615792565b612c9d565b6102936106f53660046157ad565b612e92565b610293610708366004615385565b612ff3565b61072061071b366004615792565b613257565b6040519015158152602001610277565b61029361073e366004615385565b6134ae565b600b546007805460408051602080840282018101909252828152600094859460609461ffff8316946201000090930463ffffffff169391928391908301828280156107ad57602002820191906000526020600020905b815481526020019060010190808311610799575b50505050509050925092509250909192565b6107c76134bf565b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff1661082d576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205461086890829073ffffffffffffffffffffffffffffffffffffffff16613542565b50565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff16806108d4576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614610940576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024015b60405180910390fd5b600b546601000000000000900460ff1615610987576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff848116911614610a5a5767ffffffffffffffff841660008181526003602090815260409182902060010180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091558251338152918201527f69436ea6df009049404f564eff6622cd00522b0bd6a89efd9e52a355c4a879be91015b60405180910390a25b50505050565b610a686134bf565b604080518082018252600091610a97919084906002908390839080828437600092019190915250612c6d915050565b60008181526006602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1680610af9576040517f77f5b84c00000000000000000000000000000000000000000000000000000000815260048101839052602401610937565b600082815260066020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b600754811015610be9578260078281548110610b4c57610b4c615dbc565b90600052602060002001541415610bd7576007805460009190610b7190600190615c76565b81548110610b8157610b81615dbc565b906000526020600020015490508060078381548110610ba257610ba2615dbc565b6000918252602090912001556007805480610bbf57610bbf615d8d565b60019003818190600052602060002001600090559055505b80610be181615cba565b915050610b2e565b508073ffffffffffffffffffffffffffffffffffffffff167f72be339577868f868798bac2c93e52d6f034fef4689a9848996c14ebb7416c0d83604051610c3291815260200190565b60405180910390a2505050565b610c476134bf565b60c861ffff87161115610c9a576040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff871660048201819052602482015260c86044820152606401610937565b60008213610cd7576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101839052602401610937565b6040805160a0808201835261ffff891680835263ffffffff89811660208086018290526000868801528a831660608088018290528b85166080988901819052600b80547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001690971762010000909502949094177fffffffffffffffffffffffffffffffffff000000000000000000ffffffffffff166701000000000000009092027fffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff16919091176b010000000000000000000000909302929092179093558651600c80549489015189890151938a0151978a0151968a015160c08b015160e08c01516101008d01519588167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009099169890981764010000000093881693909302929092177fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff1668010000000000000000958716959095027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff16949094176c0100000000000000000000000098861698909802979097177fffffffffffffffffff00000000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000096909416959095027fffffffffffffffffff000000ffffffffffffffffffffffffffffffffffffffff16929092177401000000000000000000000000000000000000000062ffffff92831602177fffffff000000000000ffffffffffffffffffffffffffffffffffffffffffffff1677010000000000000000000000000000000000000000000000958216959095027fffffff000000ffffffffffffffffffffffffffffffffffffffffffffffffffff16949094177a01000000000000000000000000000000000000000000000000000092851692909202919091177cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167d0100000000000000000000000000000000000000000000000000000000009390911692909202919091178155600a84905590517fc21e3bd2e0b339d2848f0dd956947a88966c242c0c0c582a33137a5c1ceb5cb2916110269189918991899189918991906159c3565b60405180910390a1505050505050565b600b546000906601000000000000900460ff1615611080576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff851660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff166110e6576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260026020908152604080832067ffffffffffffffff808a1685529252909120541680611156576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff87166004820152336024820152604401610937565b600b5461ffff9081169086161080611172575060c861ffff8616115b156111c257600b546040517fa738697600000000000000000000000000000000000000000000000000000000815261ffff8088166004830152909116602482015260c86044820152606401610937565b600b5463ffffffff620100009091048116908516111561122957600b546040517ff5d7e01e00000000000000000000000000000000000000000000000000000000815263ffffffff8087166004830152620100009092049091166024820152604401610937565b6101f463ffffffff8416111561127b576040517f47386bec00000000000000000000000000000000000000000000000000000000815263ffffffff841660048201526101f46024820152604401610937565b6000611288826001615bd2565b6040805160208082018c9052338284015267ffffffffffffffff808c16606084015284166080808401919091528351808403909101815260a08301845280519082012060c083018d905260e080840182905284518085039091018152610100909301909352815191012091925060009182916040805160208101849052439181019190915267ffffffffffffffff8c16606082015263ffffffff808b166080830152891660a08201523360c0820152919350915060e001604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0018152828252805160209182012060008681526009835283902055848352820183905261ffff8a169082015263ffffffff808916606083015287166080820152339067ffffffffffffffff8b16908c907f63373d1c4696214b898952999c9aaec57dac1ee2723cec59bea6888f489a97729060a00160405180910390a45033600090815260026020908152604080832067ffffffffffffffff808d16855292529091208054919093167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000009091161790915591505095945050505050565b600b546601000000000000900460ff161561148b576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152600860205260409020546bffffffffffffffffffffffff808316911610156114e5576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260086020526040812080548392906115129084906bffffffffffffffffffffffff16615c8d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555080600560088282829054906101000a90046bffffffffffffffffffffffff166115699190615c8d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b815260040161162192919073ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b602060405180830381600087803b15801561163b57600080fd5b505af115801561164f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167391906154db565b6116a9576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b6116b56134bf565b6040805180820182526000916116e4919084906002908390839080828437600092019190915250612c6d915050565b60008181526006602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1615611746576040517f4a0b8fa700000000000000000000000000000000000000000000000000000000815260048101829052602401610937565b600081815260066020908152604080832080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff88169081179091556007805460018101825594527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688909301849055518381527fe729ae16526293f74ade739043022254f1489f616295a25bf72dfb4511ed73b89101610c32565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680611860576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff8216146118c7576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610937565b600b546601000000000000900460ff161561190e576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff841660009081526003602052604090206002015460641415611965576040517f05a48e0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff808916855292529091205416156119ac57610a5a565b73ffffffffffffffffffffffffffffffffffffffff8316600081815260026020818152604080842067ffffffffffffffff8a1680865290835281852080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001908117909155600384528286209094018054948501815585529382902090920180547fffffffffffffffffffffffff00000000000000000000000000000000000000001685179055905192835290917f43dc749a04ac8fb825cbd514f7c0e13f13bc6f2ee66043b76629d51776cff8e09101610a51565b60015473ffffffffffffffffffffffffffffffffffffffff163314611b06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610937565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b600b546601000000000000900460ff1615611bc9576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090205473ffffffffffffffffffffffffffffffffffffffff16611c2f576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff811660009081526003602052604090206001015473ffffffffffffffffffffffffffffffffffffffff163314611cd15767ffffffffffffffff8116600090815260036020526040908190206001015490517fd084e97500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401610937565b67ffffffffffffffff81166000818152600360209081526040918290208054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560019093018054909316909255835173ffffffffffffffffffffffffffffffffffffffff909116808252928101919091529092917f6f1dc65165ffffedfd8e507b4a0f1fcfdada045ed11f6c26ba27cedfe87802f0910160405180910390a25050565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680611de5576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614611e4c576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610937565b600b546601000000000000900460ff1615611e93576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260026020908152604080832067ffffffffffffffff808916855292529091205416611f2e576040517ff0019fe600000000000000000000000000000000000000000000000000000000815267ffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff84166024820152604401610937565b67ffffffffffffffff8416600090815260036020908152604080832060020180548251818502810185019093528083529192909190830182828015611fa957602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611f7e575b50505050509050600060018251611fc09190615c76565b905060005b825181101561215f578573ffffffffffffffffffffffffffffffffffffffff16838281518110611ff757611ff7615dbc565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16141561214d57600083838151811061202f5761202f615dbc565b6020026020010151905080600360008a67ffffffffffffffff1667ffffffffffffffff168152602001908152602001600020600201838154811061207557612075615dbc565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925567ffffffffffffffff8a1681526003909152604090206002018054806120ef576120ef615d8d565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190555061215f565b8061215781615cba565b915050611fc5565b5073ffffffffffffffffffffffffffffffffffffffff8516600081815260026020908152604080832067ffffffffffffffff8b168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690555192835290917f182bff9831466789164ca77075fffd84916d35a8180ba73c27e45634549b445b91015b60405180910390a2505050505050565b600b546000906601000000000000900460ff1615612247576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005805467ffffffffffffffff1690600061226183615cf3565b82546101009290920a67ffffffffffffffff8181021990931691831602179091556005541690506000806040519080825280602002602001820160405280156122b4578160200160208202803683370190505b506040805180820182526000808252602080830182815267ffffffffffffffff888116808552600484528685209551865493516bffffffffffffffffffffffff9091167fffffffffffffffffffffffff0000000000000000000000000000000000000000948516176c010000000000000000000000009190931602919091179094558451606081018652338152808301848152818701888152958552600384529590932083518154831673ffffffffffffffffffffffffffffffffffffffff918216178255955160018201805490931696169590951790559151805194955090936123a592600285019201906150c5565b505060405133815267ffffffffffffffff841691507f464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf9060200160405180910390a250905090565b67ffffffffffffffff81166000908152600360205260408120548190819060609073ffffffffffffffffffffffffffffffffffffffff1661245a576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff80861660009081526004602090815260408083205460038352928190208054600290910180548351818602810186019094528084526bffffffffffffffffffffffff8616966c010000000000000000000000009096049095169473ffffffffffffffffffffffffffffffffffffffff90921693909291839183018282801561252157602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116124f6575b5050505050905093509350935093509193509193565b600b546601000000000000900460ff161561257e576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146125ed576040517f44b0e3c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60208114612627576040517f8129bbcd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061263582840184615792565b67ffffffffffffffff811660009081526003602052604090205490915073ffffffffffffffffffffffffffffffffffffffff1661269e576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff8116600090815260046020526040812080546bffffffffffffffffffffffff16918691906126d58385615bfe565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555084600560088282829054906101000a90046bffffffffffffffffffffffff1661272c9190615bfe565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055508167ffffffffffffffff167fd39ec07f4e209f627a4c427971473820dc129761ba28de8906bd56f57101d4f88287846127939190615bba565b604080519283526020830191909152016121ed565b600b546000906601000000000000900460ff16156127f2576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005a9050600080600061280687876139b5565b9250925092506000866060015163ffffffff1667ffffffffffffffff81111561283157612831615deb565b60405190808252806020026020018201604052801561285a578160200160208202803683370190505b50905060005b876060015163ffffffff168110156128ce5760408051602081018590529081018290526060016040516020818303038152906040528051906020012060001c8282815181106128b1576128b1615dbc565b6020908102919091010152806128c681615cba565b915050612860565b506000838152600960205260408082208290555181907f1fe543e300000000000000000000000000000000000000000000000000000000906129169087908690602401615ab4565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff166601000000000000179055908a015160808b01519192506000916129e49163ffffffff169084613d04565b600b80547fffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffff1690556020808c01805167ffffffffffffffff9081166000908152600490935260408084205492518216845290922080549394506c01000000000000000000000000918290048316936001939192600c92612a68928692900416615bd2565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000612abf8a600b600001600b9054906101000a900463ffffffff1663ffffffff16612ab985612c9d565b3a613d52565b6020808e015167ffffffffffffffff166000908152600490915260409020549091506bffffffffffffffffffffffff80831691161015612b2b576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6020808d015167ffffffffffffffff1660009081526004909152604081208054839290612b679084906bffffffffffffffffffffffff16615c8d565b82546101009290920a6bffffffffffffffffffffffff81810219909316918316021790915560008b81526006602090815260408083205473ffffffffffffffffffffffffffffffffffffffff1683526008909152812080548594509092612bd091859116615bfe565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550877f7dffc5ae5ee4e2e4df1651cf6ad329a73cebdb728f37ea0187b9b17e036756e4888386604051612c53939291909283526bffffffffffffffffffffffff9190911660208301521515604082015260600190565b60405180910390a299505050505050505050505b92915050565b600081604051602001612c8091906158e3565b604051602081830303815290604052805190602001209050919050565b6040805161012081018252600c5463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c010000000000000000000000008104831660608301527001000000000000000000000000000000008104909216608082015262ffffff740100000000000000000000000000000000000000008304811660a08301819052770100000000000000000000000000000000000000000000008404821660c08401527a0100000000000000000000000000000000000000000000000000008404821660e08401527d0100000000000000000000000000000000000000000000000000000000009093041661010082015260009167ffffffffffffffff841611612dbb575192915050565b8267ffffffffffffffff168160a0015162ffffff16108015612df057508060c0015162ffffff168367ffffffffffffffff1611155b15612dff576020015192915050565b8267ffffffffffffffff168160c0015162ffffff16108015612e3457508060e0015162ffffff168367ffffffffffffffff1611155b15612e43576040015192915050565b8267ffffffffffffffff168160e0015162ffffff16108015612e79575080610100015162ffffff168367ffffffffffffffff1611155b15612e88576060015192915050565b6080015192915050565b67ffffffffffffffff8216600090815260036020526040902054829073ffffffffffffffffffffffffffffffffffffffff1680612efb576040517f1f6a65b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff821614612f62576040517fd8a3fb5200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610937565b600b546601000000000000900460ff1615612fa9576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612fb284613257565b15612fe9576040517fb42f66e800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a5a8484613542565b612ffb6134bf565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a082319060240160206040518083038186803b15801561308357600080fd5b505afa158015613097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130bb91906154fd565b6005549091506801000000000000000090046bffffffffffffffffffffffff168181111561311f576040517fa99da3020000000000000000000000000000000000000000000000000000000081526004810182905260248101839052604401610937565b818110156132525760006131338284615c76565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390529192507f00000000000000000000000000000000000000000000000000000000000000009091169063a9059cbb90604401602060405180830381600087803b1580156131c857600080fd5b505af11580156131dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061320091906154db565b506040805173ffffffffffffffffffffffffffffffffffffffff86168152602081018390527f59bfc682b673f8cbf945f1e454df9334834abf7dfe7f92237ca29ecb9b436600910160405180910390a1505b505050565b67ffffffffffffffff811660009081526003602090815260408083208151606081018352815473ffffffffffffffffffffffffffffffffffffffff9081168252600183015416818501526002820180548451818702810187018652818152879693958601939092919083018282801561330657602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116132db575b505050505081525050905060005b8160400151518110156134a45760005b60075481101561349157600061345a6007838154811061334657613346615dbc565b90600052602060002001548560400151858151811061336757613367615dbc565b602002602001015188600260008960400151898151811061338a5761338a615dbc565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff808f168352935220541660408051602080820187905273ffffffffffffffffffffffffffffffffffffffff959095168183015267ffffffffffffffff9384166060820152919092166080808301919091528251808303909101815260a08201835280519084012060c082019490945260e080820185905282518083039091018152610100909101909152805191012091565b506000818152600960205260409020549091501561347e5750600195945050505050565b508061348981615cba565b915050613324565b508061349c81615cba565b915050613314565b5060009392505050565b6134b66134bf565b61086881613e5a565b60005473ffffffffffffffffffffffffffffffffffffffff163314613540576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610937565b565b600b546601000000000000900460ff1615613589576040517fed3ba6a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff821660009081526003602090815260408083208151606081018352815473ffffffffffffffffffffffffffffffffffffffff90811682526001830154168185015260028201805484518187028101870186528181529295939486019383018282801561363457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311613609575b5050509190925250505067ffffffffffffffff80851660009081526004602090815260408083208151808301909252546bffffffffffffffffffffffff81168083526c01000000000000000000000000909104909416918101919091529293505b83604001515181101561373b5760026000856040015183815181106136bc576136bc615dbc565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff168252818101929092526040908101600090812067ffffffffffffffff8a168252909252902080547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001690558061373381615cba565b915050613695565b5067ffffffffffffffff8516600090815260036020526040812080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081168255600182018054909116905590613796600283018261514f565b505067ffffffffffffffff8516600090815260046020526040902080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600580548291906008906138069084906801000000000000000090046bffffffffffffffffffffffff16615c8d565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb85836bffffffffffffffffffffffff166040518363ffffffff1660e01b81526004016138be92919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b602060405180830381600087803b1580156138d857600080fd5b505af11580156138ec573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061391091906154db565b613946576040517ff4d678b800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff861681526bffffffffffffffffffffffff8316602082015267ffffffffffffffff8716917fe8ed5b475a5b5987aa9165e8731bb78043f39eee32ec5a1169a89e27fcd49815910160405180910390a25050505050565b60008060006139c78560000151612c6d565b60008181526006602052604090205490935073ffffffffffffffffffffffffffffffffffffffff1680613a29576040517f77f5b84c00000000000000000000000000000000000000000000000000000000815260048101859052602401610937565b6080860151604051613a48918691602001918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600990935291205490935080613ac5576040517f3688124a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85516020808801516040808a015160608b015160808c01519251613b3e968b96909594910195865267ffffffffffffffff948516602087015292909316604085015263ffffffff908116606085015291909116608083015273ffffffffffffffffffffffffffffffffffffffff1660a082015260c00190565b604051602081830303815290604052805190602001208114613b8c576040517fd529142c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b855167ffffffffffffffff164080613cb05786516040517fe9413d3800000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063e9413d389060240160206040518083038186803b158015613c3057600080fd5b505afa158015613c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c6891906154fd565b905080613cb05786516040517f175dadad00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff9091166004820152602401610937565b6000886080015182604051602001613cd2929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c9050613cf78982613f50565b9450505050509250925092565b60005a611388811015613d1657600080fd5b611388810390508460408204820311613d2e57600080fd5b50823b613d3a57600080fd5b60008083516020850160008789f190505b9392505050565b600080613d5d613fd9565b905060008113613d9c576040517f43d4cf6600000000000000000000000000000000000000000000000000000000815260048101829052602401610937565b6000815a613daa8989615bba565b613db49190615c76565b613dc686670de0b6b3a7640000615c39565b613dd09190615c39565b613dda9190615c25565b90506000613df363ffffffff871664e8d4a51000615c39565b9050613e0b816b033b2e3c9fd0803ce8000000615c76565b821115613e44576040517fe80fa38100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b613e4e8183615bba565b98975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116331415613eda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610937565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000613f848360000151846020015185604001518660600151868860a001518960c001518a60e001518b61010001516140ed565b60038360200151604051602001613f9c929190615aa0565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b600b54604080517ffeaf968c0000000000000000000000000000000000000000000000000000000081529051600092670100000000000000900463ffffffff169182151591849182917f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169163feaf968c9160048083019260a0929190829003018186803b15801561407f57600080fd5b505afa158015614093573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140b791906157d7565b5094509092508491505080156140db57506140d28242615c76565b8463ffffffff16105b156140e55750600a545b949350505050565b6140f6896143c4565b61415c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f7075626c6963206b6579206973206e6f74206f6e2063757276650000000000006044820152606401610937565b614165886143c4565b6141cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f67616d6d61206973206e6f74206f6e20637572766500000000000000000000006044820152606401610937565b6141d4836143c4565b61423a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6347616d6d615769746e657373206973206e6f74206f6e2063757276650000006044820152606401610937565b614243826143c4565b6142a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f73486173685769746e657373206973206e6f74206f6e206375727665000000006044820152606401610937565b6142b5878a888761451f565b61431b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f6164647228632a706b2b732a6729213d5f755769746e657373000000000000006044820152606401610937565b60006143278a876146c2565b9050600061433a898b878b868989614726565b9050600061434b838d8d8a866148ae565b9050808a146143b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c69642070726f6f66000000000000000000000000000000000000006044820152606401610937565b505050505050505050505050565b80516000907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f11614451576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420782d6f7264696e61746500000000000000000000000000006044820152606401610937565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f116144de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f696e76616c696420792d6f7264696e61746500000000000000000000000000006044820152606401610937565b60208201517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f9080096145188360005b602002015161490c565b1492915050565b600073ffffffffffffffffffffffffffffffffffffffff821661459e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f626164207769746e6573730000000000000000000000000000000000000000006044820152606401610937565b6020840151600090600116156145b557601c6145b8565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418587600060200201510986517ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141918203925060009190890987516040805160008082526020820180845287905260ff88169282019290925260608101929092526080820183905291925060019060a0016020604051602081039080840390855afa15801561466f573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015173ffffffffffffffffffffffffffffffffffffffff9081169088161495505050505050949350505050565b6146ca61516d565b6146f7600184846040516020016146e3939291906158c2565b604051602081830303815290604052614964565b90505b614703816143c4565b612c6757805160408051602081019290925261471f91016146e3565b90506146fa565b61472e61516d565b825186517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f90819006910614156147c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f706f696e747320696e2073756d206d7573742062652064697374696e637400006044820152606401610937565b6147cc8789886149cd565b614832576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4669727374206d756c20636865636b206661696c6564000000000000000000006044820152606401610937565b61483d8486856149cd565b6148a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5365636f6e64206d756c20636865636b206661696c65640000000000000000006044820152606401610937565b613e4e868484614b5a565b6000600286868685876040516020016148cc96959493929190615850565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209695505050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80848509840990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f600782089392505050565b61496c61516d565b61497582614c89565b815261498a61498582600061450e565b614cde565b6020820181905260029006600114156149c8576020810180517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0390525b919050565b600082614a36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f7a65726f207363616c61720000000000000000000000000000000000000000006044820152606401610937565b83516020850151600090614a4c90600290615d1b565b15614a5857601c614a5b565b601b5b905060007ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641418387096040805160008082526020820180845281905260ff86169282019290925260608101869052608081018390529192509060019060a0016020604051602081039080840390855afa158015614adb573d6000803e3d6000fd5b505050602060405103519050600086604051602001614afa919061583e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012073ffffffffffffffffffffffffffffffffffffffff92831692169190911498975050505050505050565b614b6261516d565b835160208086015185519186015160009384938493614b8393909190614d18565b919450925090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f858209600114614c17576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f696e765a206d75737420626520696e7665727365206f66207a000000000000006044820152606401610937565b60405180604001604052807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f80614c5057614c50615d5e565b87860981526020017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8785099052979650505050505050565b805160208201205b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f81106149c857604080516020808201939093528151808203840181529082019091528051910120614c91565b6000612c67826002614d117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f6001615bba565b901c614eae565b60008080600180827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f897ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038808905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f038a0890506000614dc083838585614fa2565b9098509050614dd188828e88614ffa565b9098509050614de288828c87614ffa565b90985090506000614df58d878b85614ffa565b9098509050614e0688828686614fa2565b9098509050614e1788828e89614ffa565b9098509050818114614e9a577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f818a0998507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f82890997507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183099650614e9e565b8196505b5050505050509450945094915050565b600080614eb961518b565b6020808252818101819052604082015260608101859052608081018490527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f60a0820152614f056151a9565b60208160c08460057ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa925082614f98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6269674d6f64457870206661696c7572652100000000000000000000000000006044820152606401610937565b5195945050505050565b6000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487097ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8487099097909650945050505050565b600080807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f878509905060007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f87877ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f030990507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f8183087ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f86890990999098509650505050505050565b82805482825590600052602060002090810192821561513f579160200282015b8281111561513f57825182547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9091161782556020909201916001909101906150e5565b5061514b9291506151c7565b5090565b508054600082559060005260206000209081019061086891906151c7565b60405180604001604052806002906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b5b8082111561514b57600081556001016151c8565b803573ffffffffffffffffffffffffffffffffffffffff811681146149c857600080fd5b8060408101831015612c6757600080fd5b600082601f83011261522257600080fd5b6040516040810181811067ffffffffffffffff8211171561524557615245615deb565b806040525080838560408601111561525c57600080fd5b60005b600281101561527e57813583526020928301929091019060010161525f565b509195945050505050565b600060a0828403121561529b57600080fd5b60405160a0810181811067ffffffffffffffff821117156152be576152be615deb565b6040529050806152cd83615353565b81526152db60208401615353565b60208201526152ec6040840161533f565b60408201526152fd6060840161533f565b606082015261530e608084016151dc565b60808201525092915050565b803561ffff811681146149c857600080fd5b803562ffffff811681146149c857600080fd5b803563ffffffff811681146149c857600080fd5b803567ffffffffffffffff811681146149c857600080fd5b805169ffffffffffffffffffff811681146149c857600080fd5b60006020828403121561539757600080fd5b613d4b826151dc565b600080606083850312156153b357600080fd5b6153bc836151dc565b91506153cb8460208501615200565b90509250929050565b600080600080606085870312156153ea57600080fd5b6153f3856151dc565b935060208501359250604085013567ffffffffffffffff8082111561541757600080fd5b818701915087601f83011261542b57600080fd5b81358181111561543a57600080fd5b88602082850101111561544c57600080fd5b95989497505060200194505050565b6000806040838503121561546e57600080fd5b615477836151dc565b915060208301356bffffffffffffffffffffffff8116811461549857600080fd5b809150509250929050565b6000604082840312156154b557600080fd5b613d4b8383615200565b6000604082840312156154d157600080fd5b613d4b8383615211565b6000602082840312156154ed57600080fd5b81518015158114613d4b57600080fd5b60006020828403121561550f57600080fd5b5051919050565b600080600080600060a0868803121561552e57600080fd5b8535945061553e60208701615353565b935061554c6040870161531a565b925061555a6060870161533f565b91506155686080870161533f565b90509295509295909350565b60008082840361024081121561558957600080fd5b6101a08082121561559957600080fd5b6155a1615b90565b91506155ad8686615211565b82526155bc8660408701615211565b60208301526080850135604083015260a0850135606083015260c085013560808301526155eb60e086016151dc565b60a08301526101006155ff87828801615211565b60c0840152615612876101408801615211565b60e0840152610180860135818401525081935061563186828701615289565b925050509250929050565b6000806000806000808688036101c081121561565757600080fd5b6156608861531a565b965061566e6020890161533f565b955061567c6040890161533f565b945061568a6060890161533f565b935060808801359250610120807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60830112156156c557600080fd5b6156cd615b90565b91506156db60a08a0161533f565b82526156e960c08a0161533f565b60208301526156fa60e08a0161533f565b604083015261010061570d818b0161533f565b606084015261571d828b0161533f565b608084015261572f6101408b0161532c565b60a08401526157416101608b0161532c565b60c08401526157536101808b0161532c565b60e08401526157656101a08b0161532c565b818401525050809150509295509295509295565b60006020828403121561578b57600080fd5b5035919050565b6000602082840312156157a457600080fd5b613d4b82615353565b600080604083850312156157c057600080fd5b6157c983615353565b91506153cb602084016151dc565b600080600080600060a086880312156157ef57600080fd5b6157f88661536b565b94506020860151935060408601519250606086015191506155686080870161536b565b8060005b6002811015610a5a57815184526020938401939091019060010161581f565b615848818361581b565b604001919050565b868152615860602082018761581b565b61586d606082018661581b565b61587a60a082018561581b565b61588760e082018461581b565b60609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166101208201526101340195945050505050565b8381526158d2602082018461581b565b606081019190915260800192915050565b60408101612c67828461581b565b600060208083528351808285015260005b8181101561591e57858101830151858201604001528201615902565b81811115615930576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60006060820161ffff86168352602063ffffffff86168185015260606040850152818551808452608086019150828701935060005b818110156159b557845183529383019391830191600101615999565b509098975050505050505050565b60006101c08201905061ffff8816825263ffffffff808816602084015280871660408401528086166060840152846080840152835481811660a0850152615a1760c08501838360201c1663ffffffff169052565b615a2e60e08501838360401c1663ffffffff169052565b615a466101008501838360601c1663ffffffff169052565b615a5e6101208501838360801c1663ffffffff169052565b62ffffff60a082901c811661014086015260b882901c811661016086015260d082901c1661018085015260e81c6101a090930192909252979650505050505050565b82815260608101613d4b602083018461581b565b6000604082018483526020604081850152818551808452606086019150828701935060005b81811015615af557845183529383019391830191600101615ad9565b5090979650505050505050565b6000608082016bffffffffffffffffffffffff87168352602067ffffffffffffffff87168185015273ffffffffffffffffffffffffffffffffffffffff80871660408601526080606086015282865180855260a087019150838801945060005b81811015615b80578551841683529484019491840191600101615b62565b50909a9950505050505050505050565b604051610120810167ffffffffffffffff81118282101715615bb457615bb4615deb565b60405290565b60008219821115615bcd57615bcd615d2f565b500190565b600067ffffffffffffffff808316818516808303821115615bf557615bf5615d2f565b01949350505050565b60006bffffffffffffffffffffffff808316818516808303821115615bf557615bf5615d2f565b600082615c3457615c34615d5e565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615c7157615c71615d2f565b500290565b600082821015615c8857615c88615d2f565b500390565b60006bffffffffffffffffffffffff83811690831681811015615cb257615cb2615d2f565b039392505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415615cec57615cec615d2f565b5060010190565b600067ffffffffffffffff80831681811415615d1157615d11615d2f565b6001019392505050565b600082615d2a57615d2a615d5e565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fdfea164736f6c6343000806000a") ctorArgs, err := utils.ABIEncode(`[{"type":"address"}, {"type":"address"}, {"type":"address"}]`, linkAddress, bhsAddress, linkEthFeed) require.NoError(t, err) bytecode = append(bytecode, ctorArgs...) - nonce, err := backend.PendingNonceAt(context.Background(), neil.From) + nonce, err := backend.PendingNonceAt(ctx, neil.From) require.NoError(t, err) - gasPrice, err := backend.SuggestGasPrice(context.Background()) + gasPrice, err := backend.SuggestGasPrice(ctx) require.NoError(t, err) unsignedTx := gethtypes.NewContractCreation(nonce, big.NewInt(0), 15e6, gasPrice, bytecode) signedTx, err := neil.Signer(neil.From, unsignedTx) require.NoError(t, err) - err = backend.SendTransaction(context.Background(), signedTx) + err = backend.SendTransaction(ctx, signedTx) require.NoError(t, err, "could not deploy old vrf coordinator to simulated blockchain") backend.Commit() - receipt, err := backend.TransactionReceipt(context.Background(), signedTx.Hash()) + receipt, err := backend.TransactionReceipt(ctx, signedTx.Hash()) require.NoError(t, err) oldRootContractAddress := receipt.ContractAddress require.NotEqual(t, common.HexToAddress("0x0"), oldRootContractAddress, "old vrf coordinator address equal to zero address, deployment failed") diff --git a/core/web/jobs_controller.go b/core/web/jobs_controller.go index 4c8a77d370e..0f97e0b53d3 100644 --- a/core/web/jobs_controller.go +++ b/core/web/jobs_controller.go @@ -71,7 +71,7 @@ func (jc *JobsController) Show(c *gin.Context) { jobSpec, err = jc.App.JobORM().FindJobByExternalJobID(externalJobID, pg.WithParentCtx(c.Request.Context())) } else if pErr = jobSpec.SetID(c.Param("ID")); pErr == nil { // Find a job by job ID - jobSpec, err = jc.App.JobORM().FindJobTx(jobSpec.ID) + jobSpec, err = jc.App.JobORM().FindJobTx(c, jobSpec.ID) } else { jsonAPIError(c, http.StatusUnprocessableEntity, pErr) return diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index dfef099c175..06c2624d0f0 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -15,11 +15,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/slack-go/slack" + "github.com/stretchr/testify/require" + ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" "github.com/smartcontractkit/wasp" - "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" @@ -125,6 +127,7 @@ var ( ) func TestLogTrigger(t *testing.T) { + ctx := tests.Context(t) l := logging.GetTestLogger(t) l.Info().Msg("Starting automation v2.1 log trigger load test") @@ -467,7 +470,7 @@ func TestLogTrigger(t *testing.T) { l.Info().Str("STARTUP_WAIT_TIME", StartupWaitTime.String()).Msg("Waiting for plugin to start") time.Sleep(StartupWaitTime) - startBlock, err := chainClient.LatestBlockNumber(context.Background()) + startBlock, err := chainClient.LatestBlockNumber(ctx) require.NoError(t, err, "Error getting latest block number") p := wasp.NewProfile() @@ -511,7 +514,7 @@ func TestLogTrigger(t *testing.T) { endTime := time.Now() testDuration := endTime.Sub(startTime) l.Info().Str("Duration", testDuration.String()).Msg("Test Duration") - endBlock, err := chainClient.LatestBlockNumber(context.Background()) + endBlock, err := chainClient.LatestBlockNumber(ctx) require.NoError(t, err, "Error getting latest block number") l.Info().Uint64("Starting Block", startBlock).Uint64("Ending Block", endBlock).Msg("Test Block Range") @@ -544,7 +547,8 @@ func TestLogTrigger(t *testing.T) { Topics: [][]common.Hash{{consumerABI.Events["PerformingUpkeep"].ID}}, } ) - ctx, cancel := context.WithTimeout(context.Background(), timeout) + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, timeout) logsInBatch, err := chainClient.FilterLogs(ctx, filterQuery) cancel() if err != nil { diff --git a/plugins/medianpoc/data_source_test.go b/plugins/medianpoc/data_source_test.go index 5848705b7b9..9977daef3d0 100644 --- a/plugins/medianpoc/data_source_test.go +++ b/plugins/medianpoc/data_source_test.go @@ -11,6 +11,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -60,7 +61,7 @@ func TestDataSource(t *testing.T) { spec: spec, lggr: lggr, } - res, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{}) + res, err := ds.Observe(tests.Context(t), ocrtypes.ReportTimestamp{}) require.NoError(t, err) assert.Equal(t, big.NewInt(expect), res) assert.Equal(t, spec, pr.spec) @@ -86,7 +87,7 @@ func TestDataSource_ResultErrors(t *testing.T) { spec: spec, lggr: lggr, } - _, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{}) + _, err := ds.Observe(tests.Context(t), ocrtypes.ReportTimestamp{}) assert.ErrorContains(t, err, "something went wrong") } @@ -111,6 +112,6 @@ func TestDataSource_ResultNotAnInt(t *testing.T) { spec: spec, lggr: lggr, } - _, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{}) + _, err := ds.Observe(tests.Context(t), ocrtypes.ReportTimestamp{}) assert.ErrorContains(t, err, "cannot convert observation to decimal") } diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index 569fcb464bc..bc6af7ae5d3 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -1,7 +1,6 @@ package medianpoc import ( - "context" "fmt" "testing" @@ -13,6 +12,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -89,7 +89,7 @@ func TestNewPlugin(t *testing.T) { prov := provider{} f, err := p.newFactory( - context.Background(), + tests.Context(t), config, prov, pr, From 5e3d68e39b10917c0ed87c9097063ea9017ca56b Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Tue, 21 Nov 2023 08:52:30 +0100 Subject: [PATCH 181/271] Style update shared contracts (#11343) * style update shared ocr2 * fix comment syntax in shared * add struct packing comments and make natspec --------- Co-authored-by: Austin Born --- .../automation/v2_0/KeeperRegistry2_0.sol | 2 +- .../automation/v2_1/KeeperRegistry2_1.sol | 2 +- .../src/v0.8/shared/access/ConfirmedOwner.sol | 6 +- .../access/ConfirmedOwnerWithProposal.sol | 31 ++-- .../access/SimpleReadAccessController.sol | 26 ++-- .../access/SimpleWriteAccessController.sol | 40 ++---- .../src/v0.8/shared/ocr2/OCR2Abstract.sol | 100 ++++++------- contracts/src/v0.8/shared/ocr2/OCR2Base.sol | 136 ++++++++---------- 8 files changed, 136 insertions(+), 207 deletions(-) diff --git a/contracts/src/v0.8/automation/v2_0/KeeperRegistry2_0.sol b/contracts/src/v0.8/automation/v2_0/KeeperRegistry2_0.sol index 7db02e72e73..bd3c78e45a7 100644 --- a/contracts/src/v0.8/automation/v2_0/KeeperRegistry2_0.sol +++ b/contracts/src/v0.8/automation/v2_0/KeeperRegistry2_0.sol @@ -281,7 +281,7 @@ contract KeeperRegistry2_0 is uint64 offchainConfigVersion, bytes memory offchainConfig ) external override onlyOwner { - if (signers.length > maxNumOracles) revert TooManyOracles(); + if (signers.length > MAX_NUM_ORACLES) revert TooManyOracles(); if (f == 0) revert IncorrectNumberOfFaultyOracles(); if (signers.length != transmitters.length || signers.length <= 3 * f) revert IncorrectNumberOfSigners(); diff --git a/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol b/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol index 2f96df6d572..7c88f12f5a1 100644 --- a/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol +++ b/contracts/src/v0.8/automation/v2_1/KeeperRegistry2_1.sol @@ -250,7 +250,7 @@ contract KeeperRegistry2_1 is KeeperRegistryBase2_1, OCR2Abstract, Chainable, IE uint64 offchainConfigVersion, bytes memory offchainConfig ) public onlyOwner { - if (signers.length > maxNumOracles) revert TooManyOracles(); + if (signers.length > MAX_NUM_ORACLES) revert TooManyOracles(); if (f == 0) revert IncorrectNumberOfFaultyOracles(); if (signers.length != transmitters.length || signers.length <= 3 * f) revert IncorrectNumberOfSigners(); diff --git a/contracts/src/v0.8/shared/access/ConfirmedOwner.sol b/contracts/src/v0.8/shared/access/ConfirmedOwner.sol index a25d92ffd67..5b0c1593ccc 100644 --- a/contracts/src/v0.8/shared/access/ConfirmedOwner.sol +++ b/contracts/src/v0.8/shared/access/ConfirmedOwner.sol @@ -3,10 +3,8 @@ pragma solidity ^0.8.0; import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol"; -/** - * @title The ConfirmedOwner contract - * @notice A contract with helpers for basic contract ownership. - */ +/// @title The ConfirmedOwner contract +/// @notice A contract with helpers for basic contract ownership. contract ConfirmedOwner is ConfirmedOwnerWithProposal { constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {} } diff --git a/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol b/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol index a6757c38869..7b68418754a 100644 --- a/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol +++ b/contracts/src/v0.8/shared/access/ConfirmedOwnerWithProposal.sol @@ -3,10 +3,8 @@ pragma solidity ^0.8.0; import {IOwnable} from "../interfaces/IOwnable.sol"; -/** - * @title The ConfirmedOwner contract - * @notice A contract with helpers for basic contract ownership. - */ +/// @title The ConfirmedOwner contract +/// @notice A contract with helpers for basic contract ownership. contract ConfirmedOwnerWithProposal is IOwnable { address private s_owner; address private s_pendingOwner; @@ -24,17 +22,12 @@ contract ConfirmedOwnerWithProposal is IOwnable { } } - /** - * @notice Allows an owner to begin transferring ownership to a new address, - * pending. - */ + /// @notice Allows an owner to begin transferring ownership to a new address. function transferOwnership(address to) public override onlyOwner { _transferOwnership(to); } - /** - * @notice Allows an ownership transfer to be completed by the recipient. - */ + /// @notice Allows an ownership transfer to be completed by the recipient. function acceptOwnership() external override { // solhint-disable-next-line custom-errors require(msg.sender == s_pendingOwner, "Must be proposed owner"); @@ -46,16 +39,12 @@ contract ConfirmedOwnerWithProposal is IOwnable { emit OwnershipTransferred(oldOwner, msg.sender); } - /** - * @notice Get the current owner - */ + /// @notice Get the current owner function owner() public view override returns (address) { return s_owner; } - /** - * @notice validate, transfer ownership, and emit relevant events - */ + /// @notice validate, transfer ownership, and emit relevant events function _transferOwnership(address to) private { // solhint-disable-next-line custom-errors require(to != msg.sender, "Cannot transfer to self"); @@ -65,17 +54,13 @@ contract ConfirmedOwnerWithProposal is IOwnable { emit OwnershipTransferRequested(s_owner, to); } - /** - * @notice validate access - */ + /// @notice validate access function _validateOwnership() internal view { // solhint-disable-next-line custom-errors require(msg.sender == s_owner, "Only callable by owner"); } - /** - * @notice Reverts if called by anyone other than the contract owner. - */ + /// @notice Reverts if called by anyone other than the contract owner. modifier onlyOwner() { _validateOwnership(); _; diff --git a/contracts/src/v0.8/shared/access/SimpleReadAccessController.sol b/contracts/src/v0.8/shared/access/SimpleReadAccessController.sol index b36fa4e4b60..f4ea905bf9d 100644 --- a/contracts/src/v0.8/shared/access/SimpleReadAccessController.sol +++ b/contracts/src/v0.8/shared/access/SimpleReadAccessController.sol @@ -3,22 +3,18 @@ pragma solidity ^0.8.0; import {SimpleWriteAccessController} from "./SimpleWriteAccessController.sol"; -/** - * @title SimpleReadAccessController - * @notice Gives access to: - * - any externally owned account (note that off-chain actors can always read - * any contract storage regardless of on-chain access control measures, so this - * does not weaken the access control while improving usability) - * - accounts explicitly added to an access list - * @dev SimpleReadAccessController is not suitable for access controlling writes - * since it grants any externally owned account access! See - * SimpleWriteAccessController for that. - */ +/// @title SimpleReadAccessController +/// @notice Gives access to: +/// - any externally owned account (note that off-chain actors can always read +/// any contract storage regardless of on-chain access control measures, so this +/// does not weaken the access control while improving usability) +/// - accounts explicitly added to an access list +/// @dev SimpleReadAccessController is not suitable for access controlling writes +/// since it grants any externally owned account access! See +/// SimpleWriteAccessController for that. contract SimpleReadAccessController is SimpleWriteAccessController { - /** - * @notice Returns the access of an address - * @param _user The address to query - */ + /// @notice Returns the access of an address + /// @param _user The address to query function hasAccess(address _user, bytes memory _calldata) public view virtual override returns (bool) { // solhint-disable-next-line avoid-tx-origin return super.hasAccess(_user, _calldata) || _user == tx.origin; diff --git a/contracts/src/v0.8/shared/access/SimpleWriteAccessController.sol b/contracts/src/v0.8/shared/access/SimpleWriteAccessController.sol index 117d2e77dd6..b431331bc84 100644 --- a/contracts/src/v0.8/shared/access/SimpleWriteAccessController.sol +++ b/contracts/src/v0.8/shared/access/SimpleWriteAccessController.sol @@ -4,13 +4,9 @@ pragma solidity ^0.8.0; import {ConfirmedOwner} from "./ConfirmedOwner.sol"; import {AccessControllerInterface} from "../interfaces/AccessControllerInterface.sol"; -/** - * @title SimpleWriteAccessController - * @notice Gives access to accounts explicitly added to an access list by the - * controller's owner. - * @dev does not make any special permissions for externally, see - * SimpleReadAccessController for that. - */ +/// @title SimpleWriteAccessController +/// @notice Gives access to accounts explicitly added to an access list by the controller's owner. +/// @dev does not make any special permissions for externally, see SimpleReadAccessController for that. contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwner { bool public checkEnabled; mapping(address => bool) internal s_accessList; @@ -24,18 +20,14 @@ contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwne checkEnabled = true; } - /** - * @notice Returns the access of an address - * @param _user The address to query - */ + /// @notice Returns the access of an address + /// @param _user The address to query function hasAccess(address _user, bytes memory) public view virtual override returns (bool) { return s_accessList[_user] || !checkEnabled; } - /** - * @notice Adds an address to the access list - * @param _user The address to add - */ + /// @notice Adds an address to the access list + /// @param _user The address to add function addAccess(address _user) external onlyOwner { if (!s_accessList[_user]) { s_accessList[_user] = true; @@ -44,10 +36,8 @@ contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwne } } - /** - * @notice Removes an address from the access list - * @param _user The address to remove - */ + /// @notice Removes an address from the access list + /// @param _user The address to remove function removeAccess(address _user) external onlyOwner { if (s_accessList[_user]) { s_accessList[_user] = false; @@ -56,9 +46,7 @@ contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwne } } - /** - * @notice makes the access check enforced - */ + /// @notice makes the access check enforced function enableAccessCheck() external onlyOwner { if (!checkEnabled) { checkEnabled = true; @@ -67,9 +55,7 @@ contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwne } } - /** - * @notice makes the access check unenforced - */ + /// @notice makes the access check unenforced function disableAccessCheck() external onlyOwner { if (checkEnabled) { checkEnabled = false; @@ -78,9 +64,7 @@ contract SimpleWriteAccessController is AccessControllerInterface, ConfirmedOwne } } - /** - * @dev reverts if the caller does not have access - */ + /// @dev reverts if the caller does not have access modifier checkAccess() { // solhint-disable-next-line custom-errors require(hasAccess(msg.sender, msg.data), "No access"); diff --git a/contracts/src/v0.8/shared/ocr2/OCR2Abstract.sol b/contracts/src/v0.8/shared/ocr2/OCR2Abstract.sol index 1f19d0de54e..cd3f197113f 100644 --- a/contracts/src/v0.8/shared/ocr2/OCR2Abstract.sol +++ b/contracts/src/v0.8/shared/ocr2/OCR2Abstract.sol @@ -4,26 +4,20 @@ pragma solidity ^0.8.0; import {ITypeAndVersion} from "../interfaces/ITypeAndVersion.sol"; abstract contract OCR2Abstract is ITypeAndVersion { - // Maximum number of oracles the offchain reporting protocol is designed for - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - uint256 internal constant maxNumOracles = 31; - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - uint256 private constant prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 - // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables - uint256 private constant prefix = 0x0001 << (256 - 16); // 0x000100..00 + uint256 internal constant MAX_NUM_ORACLES = 31; + uint256 private constant PREFIX_MASK = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 private constant PREFIX = 0x0001 << (256 - 16); // 0x000100..00 - /** - * @notice triggers a new run of the offchain reporting protocol - * @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis - * @param configDigest configDigest of this configuration - * @param configCount ordinal number of this config setting among all config settings over the life of this contract - * @param signers ith element is address ith oracle uses to sign a report - * @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method - * @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly - * @param onchainConfig serialized configuration used by the contract (and possibly oracles) - * @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter - * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract - */ + /// @notice triggers a new run of the offchain reporting protocol + /// @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis + /// @param configDigest configDigest of this configuration + /// @param configCount ordinal number of this config setting among all config settings over the life of this contract + /// @param signers ith element is address ith oracle uses to sign a report + /// @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method + /// @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly + /// @param onchainConfig serialized configuration used by the contract (and possibly oracles) + /// @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter + /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract event ConfigSet( uint32 previousConfigBlockNumber, bytes32 configDigest, @@ -36,15 +30,13 @@ abstract contract OCR2Abstract is ITypeAndVersion { bytes offchainConfig ); - /** - * @notice sets offchain reporting protocol configuration incl. participating oracles - * @param signers addresses with which oracles sign the reports - * @param transmitters addresses oracles use to transmit the reports - * @param f number of faulty oracles the system can tolerate - * @param onchainConfig serialized configuration used by the contract (and possibly oracles) - * @param offchainConfigVersion version number for offchainEncoding schema - * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract - */ + /// @notice sets offchain reporting protocol configuration incl. participating oracles + /// @param signers addresses with which oracles sign the reports + /// @param transmitters addresses oracles use to transmit the reports + /// @param f number of faulty oracles the system can tolerate + /// @param onchainConfig serialized configuration used by the contract (and possibly oracles) + /// @param offchainConfigVersion version number for offchainEncoding schema + /// @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract function setConfig( address[] memory signers, address[] memory transmitters, @@ -54,12 +46,10 @@ abstract contract OCR2Abstract is ITypeAndVersion { bytes memory offchainConfig ) external virtual; - /** - * @notice information about current offchain reporting protocol configuration - * @return configCount ordinal number of current config, out of all configs applied to this contract so far - * @return blockNumber block at which this config was set - * @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) - */ + /// @notice information about current offchain reporting protocol configuration + /// @return configCount ordinal number of current config, out of all configs applied to this contract so far + /// @return blockNumber block at which this config was set + /// @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) function latestConfigDetails() external view @@ -92,40 +82,34 @@ abstract contract OCR2Abstract is ITypeAndVersion { ) ) ); - return bytes32((prefix & prefixMask) | (h & ~prefixMask)); + return bytes32((PREFIX & PREFIX_MASK) | (h & ~PREFIX_MASK)); } - /** - * @notice optionally emited to indicate the latest configDigest and epoch for - which a report was successfully transmited. Alternatively, the contract may - use latestConfigDigestAndEpoch with scanLogs set to false. - */ + /// @notice optionally emitted to indicate the latest configDigest and epoch for + /// which a report was successfully transmitted. Alternatively, the contract may + /// use latestConfigDigestAndEpoch with scanLogs set to false. event Transmitted(bytes32 configDigest, uint32 epoch); - /** - * @notice optionally returns the latest configDigest and epoch for which a - report was successfully transmitted. Alternatively, the contract may return - scanLogs set to true and use Transmitted events to provide this information - to offchain watchers. - * @return scanLogs indicates whether to rely on the configDigest and epoch - returned or whether to scan logs for the Transmitted event instead. - * @return configDigest - * @return epoch - */ + /// @notice optionally returns the latest configDigest and epoch for which a + /// report was successfully transmitted. Alternatively, the contract may return + /// scanLogs set to true and use Transmitted events to provide this information + /// to offchain watchers. + /// @return scanLogs indicates whether to rely on the configDigest and epoch + /// returned or whether to scan logs for the Transmitted event instead. + /// @return configDigest + /// @return epoch function latestConfigDigestAndEpoch() external view virtual returns (bool scanLogs, bytes32 configDigest, uint32 epoch); - /** - * @notice transmit is called to post a new report to the contract - * @param reportContext [0]: ConfigDigest, [1]: 27 byte padding, 4-byte epoch and 1-byte round, [2]: ExtraHash - * @param report serialized report, which the signatures are signing. - * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries - * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries - * @param rawVs ith element is the the V component of the ith signature - */ + /// @notice transmit is called to post a new report to the contract + /// @param reportContext [0]: ConfigDigest, [1]: 27 byte padding, 4-byte epoch and 1-byte round, [2]: ExtraHash + /// @param report serialized report, which the signatures are signing. + /// @param rs ith element is the R components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries + /// @param ss ith element is the S components of the ith signature on report. Must have at most MAX_NUM_ORACLES entries + /// @param rawVs ith element is the the V component of the ith signature function transmit( // NOTE: If these parameters are changed, expectedMsgDataLength and/or // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly diff --git a/contracts/src/v0.8/shared/ocr2/OCR2Base.sol b/contracts/src/v0.8/shared/ocr2/OCR2Base.sol index 0f0317602d3..baedac7710a 100644 --- a/contracts/src/v0.8/shared/ocr2/OCR2Base.sol +++ b/contracts/src/v0.8/shared/ocr2/OCR2Base.sol @@ -1,47 +1,45 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {ConfirmedOwner} from "../access/ConfirmedOwner.sol"; +import {OwnerIsCreator} from "../access/OwnerIsCreator.sol"; import {OCR2Abstract} from "./OCR2Abstract.sol"; -/** - * @notice Onchain verification of reports from the offchain reporting protocol - * @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. - * @dev For details on its operation, see the offchain reporting protocol design - * doc, which refers to this contract as simply the "contract". - * @dev This contract is meant to aid rapid development of new applications based on OCR2. - * However, for actual production contracts, it is expected that most of the logic of this contract - * will be folded directly into the application contract. Inheritance prevents us from doing lots - * of juicy storage layout optimizations, leading to a substantial increase in gas cost. - */ +/// @notice Onchain verification of reports from the offchain reporting protocol +/// @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. +/// @dev For details on its operation, see the offchain reporting protocol design +/// doc, which refers to this contract as simply the "contract". +/// @dev This contract is meant to aid rapid development of new applications based on OCR2. +/// However, for actual production contracts, it is expected that most of the logic of this contract +/// will be folded directly into the application contract. Inheritance prevents us from doing lots +/// of juicy storage layout optimizations, leading to a substantial increase in gas cost. // solhint-disable custom-errors -abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { +abstract contract OCR2Base is OwnerIsCreator, OCR2Abstract { error ReportInvalid(); bool internal immutable i_uniqueReports; - constructor(bool uniqueReports) ConfirmedOwner(msg.sender) { + constructor(bool uniqueReports) OwnerIsCreator() { i_uniqueReports = uniqueReports; } - // Storing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a single SLOAD. If any further fields are - // added, make sure that storage of the struct still takes at most 32 bytes. + /// @dev Storing these fields used on the hot path in a ConfigInfo variable reduces + /// the retrieval of all of them to a single SLOAD. If any further fields are + /// added, make sure that storage of the struct still takes at most 32 bytes. struct ConfigInfo { bytes32 latestConfigDigest; - uint8 f; // TODO: could be optimized by squeezing into one slot - uint8 n; + uint8 f; // ───╮ + uint8 n; // ───╯ } ConfigInfo internal s_configInfo; - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. + /// @dev Incremented each time a new config is posted. This count is incorporated + /// into the config digest, to prevent replay attacks. uint32 internal s_configCount; - uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems - // to extract config from logs. + /// @dev Makes it easier for offchain systems to extract config from logs. + uint32 internal s_latestConfigBlockNumber; - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. + /// @dev Used for s_oracles[a].role, where a is an address, to track the purpose + /// of the address, or to indicate that the address is unset. enum Role { // No oracle role has been set for address a Unset, @@ -55,30 +53,26 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { } struct Oracle { - uint8 index; // Index of oracle in s_signers/s_transmitters - Role role; // Role of the address which mapped to this struct + uint8 index; // ───╮ Index of oracle in s_signers/s_transmitters + Role role; // ─────╯ Role of the address which mapped to this struct } mapping(address => Oracle) /* signer OR transmitter address */ internal s_oracles; - // s_signers contains the signing address of each oracle + /// @notice Contains the signing address of each oracle address[] internal s_signers; - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from + /// @notice Contains the transmission address of each oracle, + /// i.e. the address the oracle actually sends transactions to the contract from address[] internal s_transmitters; - /* - * Config logic - */ - - // Reverts transaction if config args are invalid + /// @dev Reverts transaction if config args are invalid modifier checkConfigValid( uint256 _numSigners, uint256 _numTransmitters, uint256 _f ) { - require(_numSigners <= maxNumOracles, "too many signers"); + require(_numSigners <= MAX_NUM_ORACLES, "too many signers"); require(_f > 0, "f must be positive"); require(_numSigners == _numTransmitters, "oracle addresses out of registration"); require(_numSigners > 3 * _f, "faulty-oracle f too high"); @@ -105,15 +99,13 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { return (true, bytes32(0), uint32(0)); } - /** - * @notice sets offchain reporting protocol configuration incl. participating oracles - * @param _signers addresses with which oracles sign the reports - * @param _transmitters addresses oracles use to transmit the reports - * @param _f number of faulty oracles the system can tolerate - * @param _onchainConfig encoded on-chain contract configuration - * @param _offchainConfigVersion version number for offchainEncoding schema - * @param _offchainConfig encoded off-chain oracle configuration - */ + /// @notice sets offchain reporting protocol configuration incl. participating oracles + /// @param _signers addresses with which oracles sign the reports + /// @param _transmitters addresses oracles use to transmit the reports + /// @param _f number of faulty oracles the system can tolerate + /// @param _onchainConfig encoded on-chain contract configuration + /// @param _offchainConfigVersion version number for offchainEncoding schema + /// @param _offchainConfig encoded off-chain oracle configuration function setConfig( address[] memory _signers, address[] memory _transmitters, @@ -187,12 +179,10 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { _afterSetConfig(args.f, args.onchainConfig); } - /** - * @notice information about current offchain reporting protocol configuration - * @return configCount ordinal number of current config, out of all configs applied to this contract so far - * @return blockNumber block at which this config was set - * @return configDigest domain-separation tag for current config (see configDigestFromConfigData) - */ + /// @notice information about current offchain reporting protocol configuration + /// @return configCount ordinal number of current config, out of all configs applied to this contract so far + /// @return blockNumber block at which this config was set + /// @return configDigest domain-separation tag for current config (see configDigestFromConfigData) function latestConfigDetails() external view @@ -202,10 +192,8 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); } - /** - * @return list of addresses permitted to transmit reports to this contract - * @dev The list will match the order used to specify the transmitter during setConfig - */ + /// @return list of addresses permitted to transmit reports to this contract + /// @dev The list will match the order used to specify the transmitter during setConfig function transmitters() external view returns (address[] memory) { return s_transmitters; } @@ -214,31 +202,27 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { function _afterSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - /** - * @dev hook to allow additional validation of the report by the extending contract - * @param configDigest separation tag for current config (see configDigestFromConfigData) - * @param epochAndRound 27 byte padding, 4-byte epoch and 1-byte round - * @param report serialized report - */ + /// @dev hook to allow additional validation of the report by the extending contract + /// @param configDigest separation tag for current config (see configDigestFromConfigData) + /// @param epochAndRound 27 byte padding, 4-byte epoch and 1-byte round + /// @param report serialized report function _validateReport( bytes32 configDigest, uint40 epochAndRound, bytes memory report ) internal virtual returns (bool); - /** - * @dev hook called after the report has been fully validated - * for the extending contract to handle additional logic, such as oracle payment - * @param initialGas the amount of gas before validation - * @param transmitter the address of the account that submitted the report - * @param signers the addresses of all signing accounts - * @param report serialized report - */ + /// @dev hook called after the report has been fully validated + /// for the extending contract to handle additional logic, such as oracle payment + /// @param initialGas the amount of gas before validation + /// @param transmitter the address of the account that submitted the report + /// @param signers the addresses of all signing accounts + /// @param report serialized report function _report( uint256 initialGas, address transmitter, uint8 signerCount, - address[maxNumOracles] memory signers, + address[MAX_NUM_ORACLES] memory signers, bytes calldata report ) internal virtual; @@ -274,13 +258,11 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { require(msg.data.length == expected, "calldata length mismatch"); } - /** - * @notice transmit is called to post a new report to the contract - * @param report serialized report, which the signatures are signing. - * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries - * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries - * @param rawVs ith element is the the V component of the ith signature - */ + /// @notice transmit is called to post a new report to the contract + /// @param report serialized report, which the signatures are signing. + /// @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries + /// @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries + /// @param rawVs ith element is the the V component of the ith signature function transmit( // NOTE: If these parameters are changed, expectedMsgDataLength and/or // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly @@ -328,7 +310,7 @@ abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { ); } - address[maxNumOracles] memory signed; + address[MAX_NUM_ORACLES] memory signed; uint8 signerCount = 0; { From 0c8c1cd000131018dff070279b4e1a18b3562d53 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 21 Nov 2023 06:05:58 -0600 Subject: [PATCH 182/271] switch from ocr2vrf to chainlink-vrf (#11346) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/scripts/ocr2vrf/main.go | 3 ++- core/scripts/ocr2vrf/util.go | 9 +++++---- core/scripts/ocr2vrf/verify.go | 5 +++-- core/services/keystore/keys/dkgencryptkey/key.go | 3 ++- core/services/ocr2/delegate.go | 15 +++++++-------- core/services/ocr2/plugins/dkg/key_consumer.go | 2 +- .../services/ocr2/plugins/dkg/onchain_contract.go | 6 +++--- core/services/ocr2/plugins/dkg/persistence/db.go | 6 ++++-- .../ocr2/plugins/dkg/persistence/db_test.go | 4 ++-- core/services/ocr2/plugins/dkg/plugin.go | 6 ++++-- .../plugins/ocr2vrf/coordinator/coordinator.go | 10 ++++++---- .../ocr2vrf/coordinator/coordinator_test.go | 7 ++++--- .../ocr2vrf/internal/ocr2vrf_integration_test.go | 9 +++++---- .../juelsfeecoin/link_eth_price_provider.go | 3 ++- .../reasonable_gas_price_provider.go | 2 +- .../ocr2vrf/reportserializer/report_serializer.go | 4 ++-- .../reportserializer/report_serializer_test.go | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- .../ocr2vrf_actions/ocr2vrf_config_helpers.go | 9 +++++---- .../ocr2vrf_constants/ocr2vrf_constants.go | 2 +- .../actions/ocr2vrf_actions/ocr2vrf_models.go | 2 +- .../actions/ocr2vrf_actions/ocr2vrf_steps.go | 2 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 27 files changed, 72 insertions(+), 59 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 1d8ba40b82f..09c76dd195c 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,10 +21,10 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 + github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 - github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 13496adb7a2..b1a08701144 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1470,6 +1470,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= @@ -1478,8 +1480,6 @@ github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XO github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= -github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= -github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/core/scripts/ocr2vrf/main.go b/core/scripts/ocr2vrf/main.go index c698fcd730d..532fbd3f22c 100644 --- a/core/scripts/ocr2vrf/main.go +++ b/core/scripts/ocr2vrf/main.go @@ -12,7 +12,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/shopspring/decimal" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" diff --git a/core/scripts/ocr2vrf/util.go b/core/scripts/ocr2vrf/util.go index a2ff55524d3..4ec78472e50 100644 --- a/core/scripts/ocr2vrf/util.go +++ b/core/scripts/ocr2vrf/util.go @@ -21,10 +21,11 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - "github.com/smartcontractkit/ocr2vrf/dkg" - "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + "github.com/smartcontractkit/chainlink-vrf/dkg" + "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" diff --git a/core/scripts/ocr2vrf/verify.go b/core/scripts/ocr2vrf/verify.go index 4b91148a8c2..7d7fb94496a 100644 --- a/core/scripts/ocr2vrf/verify.go +++ b/core/scripts/ocr2vrf/verify.go @@ -9,12 +9,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" bn256 "github.com/ethereum/go-ethereum/crypto/bn256/google" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/group/mod" "go.dedis.ch/kyber/v3/pairing" + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" dkgContract "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/dkg" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ocr2vrf/generated/vrf_beacon" diff --git a/core/services/keystore/keys/dkgencryptkey/key.go b/core/services/keystore/keys/dkgencryptkey/key.go index 461dc4e2ade..e94f2a6bdf4 100644 --- a/core/services/keystore/keys/dkgencryptkey/key.go +++ b/core/services/keystore/keys/dkgencryptkey/key.go @@ -6,9 +6,10 @@ import ( "math/big" "github.com/pkg/errors" - "github.com/smartcontractkit/ocr2vrf/altbn_128" "go.dedis.ch/kyber/v3" "go.dedis.ch/kyber/v3/pairing" + + "github.com/smartcontractkit/chainlink-vrf/altbn_128" ) var suite pairing.Suite = &altbn_128.PairingSuite{} diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 4b5932cd703..2f2a9033398 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -8,13 +8,11 @@ import ( "log" "time" - "google.golang.org/grpc" - "gopkg.in/guregu/null.v4" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - "github.com/jmoiron/sqlx" + "github.com/pkg/errors" + "google.golang.org/grpc" + "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" @@ -26,9 +24,10 @@ import ( ocr2keepers20runner "github.com/smartcontractkit/ocr2keepers/pkg/v2/runner" ocr2keepers21config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" ocr2keepers21 "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - dkgpkg "github.com/smartcontractkit/ocr2vrf/dkg" - "github.com/smartcontractkit/ocr2vrf/ocr2vrf" + + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + dkgpkg "github.com/smartcontractkit/chainlink-vrf/dkg" + "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" commonlogger "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/loop" diff --git a/core/services/ocr2/plugins/dkg/key_consumer.go b/core/services/ocr2/plugins/dkg/key_consumer.go index 99115af70de..23ca7e795bf 100644 --- a/core/services/ocr2/plugins/dkg/key_consumer.go +++ b/core/services/ocr2/plugins/dkg/key_consumer.go @@ -4,7 +4,7 @@ import ( "encoding/hex" "fmt" - "github.com/smartcontractkit/ocr2vrf/dkg" + "github.com/smartcontractkit/chainlink-vrf/dkg" ) type dummyKeyConsumer struct{} diff --git a/core/services/ocr2/plugins/dkg/onchain_contract.go b/core/services/ocr2/plugins/dkg/onchain_contract.go index 5f23f33c5d9..c6ea84de235 100644 --- a/core/services/ocr2/plugins/dkg/onchain_contract.go +++ b/core/services/ocr2/plugins/dkg/onchain_contract.go @@ -8,9 +8,9 @@ import ( "github.com/pkg/errors" "go.dedis.ch/kyber/v3/sign/anon" - "github.com/smartcontractkit/ocr2vrf/dkg" - dkgwrapper "github.com/smartcontractkit/ocr2vrf/gethwrappers/dkg" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + "github.com/smartcontractkit/chainlink-vrf/dkg" + dkgwrapper "github.com/smartcontractkit/chainlink-vrf/gethwrappers/dkg" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" ) diff --git a/core/services/ocr2/plugins/dkg/persistence/db.go b/core/services/ocr2/plugins/dkg/persistence/db.go index c020a68cbe7..304422ace2d 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db.go +++ b/core/services/ocr2/plugins/dkg/persistence/db.go @@ -13,9 +13,11 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" - "github.com/smartcontractkit/ocr2vrf/types/hash" + + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + "github.com/smartcontractkit/chainlink-vrf/types/hash" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" diff --git a/core/services/ocr2/plugins/dkg/persistence/db_test.go b/core/services/ocr2/plugins/dkg/persistence/db_test.go index d4a4546fb93..b4fa000cb99 100644 --- a/core/services/ocr2/plugins/dkg/persistence/db_test.go +++ b/core/services/ocr2/plugins/dkg/persistence/db_test.go @@ -10,8 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" - "github.com/smartcontractkit/ocr2vrf/types/hash" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + "github.com/smartcontractkit/chainlink-vrf/types/hash" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" diff --git a/core/services/ocr2/plugins/dkg/plugin.go b/core/services/ocr2/plugins/dkg/plugin.go index 92910ff7bbe..0310122655f 100644 --- a/core/services/ocr2/plugins/dkg/plugin.go +++ b/core/services/ocr2/plugins/dkg/plugin.go @@ -8,10 +8,12 @@ import ( "github.com/jmoiron/sqlx" "github.com/pkg/errors" + "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - "github.com/smartcontractkit/ocr2vrf/dkg" + + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + "github.com/smartcontractkit/chainlink-vrf/dkg" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go index 1b58a017322..63538941532 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator.go @@ -17,13 +17,15 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/libocr/commontypes" - ocr2Types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2vrf/dkg" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" "golang.org/x/exp/maps" "google.golang.org/protobuf/proto" + "github.com/smartcontractkit/libocr/commontypes" + ocr2Types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-vrf/dkg" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go index f634ee0c01a..418e4356c68 100644 --- a/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/coordinator/coordinator_test.go @@ -21,9 +21,10 @@ import ( "github.com/smartcontractkit/libocr/commontypes" ocr2Types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2vrf/dkg" - "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + + "github.com/smartcontractkit/chainlink-vrf/dkg" + "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink-common/pkg/types" diff --git a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go index 38d7acf7e9b..e93824283ba 100644 --- a/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/internal/ocr2vrf_integration_test.go @@ -25,10 +25,11 @@ import ( "github.com/smartcontractkit/libocr/commontypes" confighelper2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrtypes2 "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - ocr2dkg "github.com/smartcontractkit/ocr2vrf/dkg" - "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + ocr2dkg "github.com/smartcontractkit/chainlink-vrf/dkg" + "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" diff --git a/core/services/ocr2/plugins/ocr2vrf/juelsfeecoin/link_eth_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/juelsfeecoin/link_eth_price_provider.go index d2330d87e4d..2601ae14668 100644 --- a/core/services/ocr2/plugins/ocr2vrf/juelsfeecoin/link_eth_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/juelsfeecoin/link_eth_price_provider.go @@ -10,7 +10,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/ocr2vrf/types" + + "github.com/smartcontractkit/chainlink-vrf/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go index c0e969a2879..880b8084338 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go @@ -4,7 +4,7 @@ import ( "math/big" "time" - "github.com/smartcontractkit/ocr2vrf/types" + "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" diff --git a/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer.go b/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer.go index 19b625ff6d8..e4112b55438 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer.go +++ b/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer.go @@ -4,8 +4,8 @@ import ( "github.com/pkg/errors" "go.dedis.ch/kyber/v3" - "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - types "github.com/smartcontractkit/ocr2vrf/types" + "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" + "github.com/smartcontractkit/chainlink-vrf/types" ) type reportSerializer struct { diff --git a/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer_test.go b/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer_test.go index dcfd627ac9f..32704a55b0f 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer_test.go +++ b/core/services/ocr2/plugins/ocr2vrf/reportserializer/report_serializer_test.go @@ -7,8 +7,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - "github.com/smartcontractkit/ocr2vrf/types" + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/reportserializer" ) diff --git a/go.mod b/go.mod index 784d3ff78e1..c13f476bf2f 100644 --- a/go.mod +++ b/go.mod @@ -69,9 +69,9 @@ require ( github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 + github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 - github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wsrpc v0.7.2 diff --git a/go.sum b/go.sum index fb09e792d6d..4f465dc8f90 100644 --- a/go.sum +++ b/go.sum @@ -1471,6 +1471,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= @@ -1479,8 +1481,6 @@ github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XO github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= -github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= -github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go index e424aaa11b3..58e394cb797 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_config_helpers.go @@ -18,10 +18,11 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2vrf/altbn_128" - "github.com/smartcontractkit/ocr2vrf/dkg" - "github.com/smartcontractkit/ocr2vrf/ocr2vrf" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + + "github.com/smartcontractkit/chainlink-vrf/altbn_128" + "github.com/smartcontractkit/chainlink-vrf/dkg" + "github.com/smartcontractkit/chainlink-vrf/ocr2vrf" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink/v2/core/services/job" diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants/ocr2vrf_constants.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants/ocr2vrf_constants.go index 7caceec9414..7f17be3fd84 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants/ocr2vrf_constants.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_constants/ocr2vrf_constants.go @@ -4,7 +4,7 @@ import ( "math/big" "time" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" ) var ( diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_models.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_models.go index 8f218ab5c7d..08580203380 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_models.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_models.go @@ -1,6 +1,6 @@ package ocr2vrf_actions -import ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" +import ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" type DKGKeyConfig struct { DKGEncryptionPublicKey string diff --git a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go index 2904162f495..c3add16ec67 100644 --- a/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go +++ b/integration-tests/actions/ocr2vrf_actions/ocr2vrf_steps.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - ocr2vrftypes "github.com/smartcontractkit/ocr2vrf/types" + ocr2vrftypes "github.com/smartcontractkit/chainlink-vrf/types" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 3b1a5423df8..9e99f39b445 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,10 +24,10 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e github.com/smartcontractkit/chainlink-testing-framework v1.19.1 + github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/ocr2keepers v0.7.28 - github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.3.0 github.com/spf13/cobra v1.6.1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 427ad1421c9..87cceb9b99a 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2377,6 +2377,8 @@ github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.202311 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/chainlink-testing-framework v1.19.1 h1:MdGM5jIrBi858Cv7qzfl1Qon93YW8InohAlDQqFoIb4= github.com/smartcontractkit/chainlink-testing-framework v1.19.1/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= +github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU= @@ -2385,8 +2387,6 @@ github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XO github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= -github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687 h1:NwC3SOc25noBTe1KUQjt45fyTIuInhoE2UfgcHAdihM= -github.com/smartcontractkit/ocr2vrf v0.0.0-20230804151440-2f1eb1e20687/go.mod h1:YYZq52t4wcHoMQeITksYsorD+tZcOyuVU5+lvot3VFM= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= From 98ac92b9e57c8ba386999fe739ca09f0b4886ffb Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 21 Nov 2023 08:08:17 -0600 Subject: [PATCH 183/271] core/services/job: close test peer wrappers (#11349) --- core/services/job/orm_test.go | 2 +- core/services/job/runner_integration_test.go | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/services/job/orm_test.go b/core/services/job/orm_test.go index 41d02dba060..2e19669417a 100644 --- a/core/services/job/orm_test.go +++ b/core/services/job/orm_test.go @@ -23,7 +23,7 @@ import ( func NewTestORM(t *testing.T, db *sqlx.DB, pipelineORM pipeline.ORM, bridgeORM bridges.ORM, keyStore keystore.Master, cfg pg.QConfig) job.ORM { o := job.NewORM(db, pipelineORM, bridgeORM, keyStore, logger.TestLogger(t), cfg) - t.Cleanup(func() { o.Close() }) + t.Cleanup(func() { assert.NoError(t, o.Close()) }) return o } diff --git a/core/services/job/runner_integration_test.go b/core/services/job/runner_integration_test.go index deb4bff6b08..eb6af3607f3 100644 --- a/core/services/job/runner_integration_test.go +++ b/core/services/job/runner_integration_test.go @@ -450,6 +450,7 @@ answer1 [type=median index=0]; assert.NoError(t, err) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) require.NoError(t, pw.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, pw.Close()) }) sd := ocr.NewDelegate( db, jobORM, @@ -484,6 +485,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) require.NoError(t, pw.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, pw.Close()) }) sd := ocr.NewDelegate( db, jobORM, @@ -512,6 +514,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) require.NoError(t, pw.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, pw.Close()) }) sd := ocr.NewDelegate( db, jobORM, @@ -566,6 +569,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) require.NoError(t, pw.Start(testutils.Context(t))) + t.Cleanup(func() { assert.NoError(t, pw.Close()) }) sd := ocr.NewDelegate( db, jobORM, @@ -610,7 +614,7 @@ answer1 [type=median index=0]; lggr := logger.TestLogger(t) pw := ocrcommon.NewSingletonPeerWrapper(keyStore, config.P2P(), config.OCR(), config.Database(), db, lggr) require.NoError(t, pw.Start(testutils.Context(t))) - + t.Cleanup(func() { assert.NoError(t, pw.Close()) }) sd := ocr.NewDelegate( db, jobORM, From 530225a1918556cbee65bbe2e5aff6bfaeb05abd Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Tue, 21 Nov 2023 10:06:19 -0500 Subject: [PATCH 184/271] [TT-524] [TT-729] More Products and Reporting for Live Tests (#11327) * More Specific Tests * Disable sepolia * Specify automation test * Don't give up * Reduce default funding * Fix test name * Messing with jq * Change all OCR config * Fiddling with jq more * Little better reports * I think I got the formatting now * Ensure prerequisites * Debugging * Remove Sepolia * Formatting * Remove if (we'll make it pretty later) * Strip out quotes * Fix GHA label * Remove debug * More accurate colors * Ease the debugging process * More debugging * Fix needs check * Check fail * Check fail better * Learned some tricks * Successes * Extra paren * Fix emojis * Purty * Remove debug and mocks * Cleanup --- .github/workflows/live-testnet-tests.yml | 205 ++++++++++++++--------- integration-tests/soak/ocr_test.go | 11 +- 2 files changed, 128 insertions(+), 88 deletions(-) diff --git a/.github/workflows/live-testnet-tests.yml b/.github/workflows/live-testnet-tests.yml index 7eb1669b35f..f174e8847bd 100644 --- a/.github/workflows/live-testnet-tests.yml +++ b/.github/workflows/live-testnet-tests.yml @@ -11,12 +11,16 @@ env: CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com MOD_CACHE_VERSION: 2 + CHAINLINK_NODE_FUNDING: .1 CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} TEST_LOG_LEVEL: debug EVM_KEYS: ${{ secrets.QA_EVM_KEYS }} + SEPOLIA_URLS: ${{ secrets.QA_SEPOLIA_URLS }} + SEPOLIA_HTTP_URLS: ${{ secrets.QA_SEPOLIA_HTTP_URLS }} + OPTIMISM_GOERLI_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_URLS }} OPTIMISM_GOERLI_HTTP_URLS: ${{ secrets.QA_OPTIMISM_GOERLI_HTTP_URLS }} @@ -55,61 +59,66 @@ jobs: AWS_REGION: ${{ secrets.QA_AWS_REGION }} AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - - sepolia-smoke-tests: - environment: integration - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - needs: [build-chainlink] - env: - SELECTED_NETWORKS: SEPOLIA - strategy: - fail-fast: false - matrix: - product: [ocr, automation] - name: Sepolia ${{ matrix.product }} Tests - runs-on: ubuntu-latest - steps: - - name: Checkout the repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 - env: - PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} - PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia - PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} - with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/${{ matrix.product }}_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt - test_download_vendor_packages_command: cd ./integration-tests && go mod download - cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} - dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} - artifacts_location: ./integration-tests/smoke/logs - publish_check_name: Seplia ${{ matrix.product }} Smoke Test Results - token: ${{ secrets.GITHUB_TOKEN }} - go_mod_path: ./integration-tests/go.mod - cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} - cache_restore_only: "true" - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - - 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: Sepolia ${{ matrix.product }} Tests - test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' - continue-on-error: true + # TODO: Re-enable when we have secrets properly configured + # sepolia-smoke-tests: + # environment: integration + # permissions: + # checks: write + # pull-requests: write + # id-token: write + # contents: read + # needs: [build-chainlink] + # env: + # SELECTED_NETWORKS: SEPOLIA + # strategy: + # max-parallel: 1 + # fail-fast: false + # matrix: + # include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + # - product: OCR + # test: TestOCRBasic + # - product: Automation + # test: TestAutomationBasic/registry_2_0 + # name: Sepolia ${{ matrix.product }} Tests + # runs-on: ubuntu-latest + # steps: + # - name: Checkout the repo + # uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + # with: + # ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + # - name: Run Tests + # uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 + # env: + # PYROSCOPE_SERVER: ${{ secrets.QA_PYROSCOPE_INSTANCE }} + # PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-sepolia + # PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} + # with: + # test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 -run ${{ matrix.test }} ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt + # test_download_vendor_packages_command: cd ./integration-tests && go mod download + # cl_repo: ${{ env.CHAINLINK_IMAGE }} + # cl_image_tag: ${{ github.sha }} + # aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + # dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + # dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + # artifacts_location: ./integration-tests/smoke/logs + # publish_check_name: Seplia ${{ matrix.product }} Smoke Test Results + # token: ${{ secrets.GITHUB_TOKEN }} + # go_mod_path: ./integration-tests/go.mod + # cache_key_id: core-e2e-${{ env.MOD_CACHE_VERSION }} + # cache_restore_only: "true" + # QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + # QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + # QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} + # - 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: Sepolia ${{ matrix.product }} Tests + # test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' + # continue-on-error: true optimism-goerli-smoke-tests: environment: integration @@ -123,8 +132,13 @@ jobs: SELECTED_NETWORKS: OPTIMISM_GOERLI strategy: fail-fast: false + max-parallel: 1 matrix: - product: [ocr, automation] + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation + test: TestAutomationBasic/registry_2_0 name: Optimism Goerli ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: @@ -139,7 +153,7 @@ jobs: PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-optimism-goerli PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/${{ matrix.product }}_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 -run ${{ matrix.test }} ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} @@ -177,9 +191,14 @@ jobs: env: SELECTED_NETWORKS: ARBITRUM_GOERLI strategy: + max-parallel: 1 fail-fast: false matrix: - product: [ocr, automation] + include: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#example-adding-configurations + - product: OCR + test: TestOCRBasic + - product: Automation + test: TestAutomationBasic/registry_2_0 name: Arbitrum Goerli ${{ matrix.product }} Tests runs-on: ubuntu-latest steps: @@ -194,7 +213,7 @@ jobs: PYROSCOPE_ENVIRONMENT: ci-smoke-${{ matrix.product }}-arbitrum-goerli PYROSCOPE_KEY: ${{ secrets.QA_PYROSCOPE_KEY }} with: - test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 ./smoke/${{ matrix.product }}_test.go 2>&1 | tee /tmp/gotest.log | gotestfmt + test_command_to_run: cd ./integration-tests && go test -timeout 30m -count=1 -json -test.parallel=1 -run ${{ matrix.test }} ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ env.CHAINLINK_IMAGE }} cl_image_tag: ${{ github.sha }} @@ -233,10 +252,10 @@ jobs: id-token: write contents: read runs-on: ubuntu-latest - needs: [sepolia-smoke-tests, optimism-goerli-smoke-tests, arbitrum-goerli-smoke-tests] + needs: [optimism-goerli-smoke-tests, arbitrum-goerli-smoke-tests] steps: - name: Debug Result - run: echo ${{needs.*.result}} + run: echo ${{ join(needs.*.result, ',') }} - name: Main Slack Notification uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 id: slack @@ -246,13 +265,13 @@ jobs: { "attachments": [ { - "color": "${{ needs.*.result == 'success' && '#2E7D32' || '#C62828' }}", + "color": "${{ contains(join(needs.*.result, ','), 'failure') && '#C62828' || '#2E7D32' }}", "blocks": [ { "type": "header", "text": { "type": "plain_text", - "text": "Live Smoke Test Results ${{ needs.*.result == 'success' && ':white_check_mark:' || ':x:'}}", + "text": "Live Smoke Test Results ${{ contains(join(needs.*.result, ','), 'failure') && ':x:' || ':white_check_mark:'}}", "emoji": true } }, @@ -274,7 +293,7 @@ jobs: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} testnet-smoke-tests-results: - name: Post Test Results + name: Post Test Results for ${{ matrix.network }} if: ${{ always() && needs.*.result != 'skipped' && needs.*.result != 'cancelled' }} environment: integration permissions: @@ -287,23 +306,53 @@ jobs: strategy: fail-fast: false matrix: - testnet: [sepolia, optimism-goerli, arbitrum-goerli] + network: [Optimism Goerli, Arbitrum Goerli] steps: - name: Get Results id: test-results run: | + # I feel like there's some clever, fully jq way to do this, but I ain't got the motivation to figure it out echo "Querying test results" - echo "status=$(curl \ + PARSED_RESULTS=$(curl \ -H "Authorization: Bearer ${{ github.token }}" \ 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ - | jq -r '.jobs[] | select(.name == "Live Testnet Smoke Tests ${{ matrix.testnet }}-smoke-tests").steps[] | select(.name == "Run Tests").conclusion')" >> $GITHUB_OUTPUT + | jq -r --arg pattern "${{ matrix.network }} (?\\w+) Tests" '.jobs[] + | select(.name | test($pattern)) as $job + | $job.steps[] + | select(.name == "Run Tests") + | { conclusion: (if .conclusion == "success" then ":white_check_mark:" else ":x:" end), product: ("*" + ($job.name | capture($pattern).product) + "*") }') - echo "status=$(curl \ - -H "Authorization: Bearer ${{ github.token }}" \ - 'https://api.github.com/repos/${{github.repository}}/actions/runs/${{ github.run_id }}/jobs' \ - | jq -r '.jobs[] | select(.name == "Live Testnet Smoke Tests ${{ matrix.testnet }}-smoke-tests"").steps[] | select(.name == "Run Tests").conclusion')" - echo "thread_ts=${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}" + echo "Parsed Results:" + echo $PARSED_RESULTS + + ALL_SUCCESS=true + for row in $(echo "$PARSED_RESULTS" | jq -s | jq -r '.[] | select(.conclusion != ":white_check_mark:")'); do + success=false + break + done + + echo all_success=$ALL_SUCCESS >> $GITHUB_OUTPUT + + FORMATTED_RESULTS=$(echo $PARSED_RESULTS | jq -s '[.[] + | { + conclusion: .conclusion, + product: .product + } + ] + | map("{\"type\": \"section\", \"text\": {\"type\": \"mrkdwn\", \"text\": \"\(.product): \(.conclusion)\"}}") + | join(",")') + + echo "Formatted Results:" + echo $FORMATTED_RESULTS + + # Cleans out backslashes and quotes from jq + CLEAN_RESULTS=$(echo "$FORMATTED_RESULTS" | sed 's/\\\"/"/g' | sed 's/^"//;s/"$//') + + echo "Clean Results" + echo $CLEAN_RESULTS + + echo results=$CLEAN_RESULTS >> $GITHUB_OUTPUT - name: Test Details uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 @@ -314,26 +363,20 @@ jobs: "thread_ts": "${{ needs.testnet-smoke-tests-notify.outputs.thread_ts }}", "attachments": [ { - "color": "${{ steps.test-results.outputs.status == 'success' && '#2E7D32' || '#C62828' }}", + "color": "${{ steps.test-results.outputs.all_success && '#2E7D32' || '#C62828' }}", "blocks": [ { "type": "header", "text": { "type": "plain_text", - "text": "${{ matrix.testnet }} Smoke Test Results ${{ steps.test-results.outputs.status == 'success' && ':white_check_mark:' || ':x:'}}", + "text": "${{ matrix.network }} ${{ steps.test-results.outputs.all_success && ':white_check_mark:' || ':x: Notifying <@U01Q4N37KFG>'}}", "emoji": true } }, { "type": "divider" }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "OCR ${{ steps.test-results.outputs.status == 'success' && ':white_check_mark:' || ':x:'}}" - } - } + ${{ steps.test-results.outputs.results }} ] } ] diff --git a/integration-tests/soak/ocr_test.go b/integration-tests/soak/ocr_test.go index 4f42ff803e5..4d7971d678e 100644 --- a/integration-tests/soak/ocr_test.go +++ b/integration-tests/soak/ocr_test.go @@ -1,16 +1,13 @@ package soak import ( - "fmt" "testing" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/config" "github.com/smartcontractkit/chainlink/integration-tests/testsetups" ) @@ -19,10 +16,10 @@ func TestOCRSoak(t *testing.T) { // Use this variable to pass in any custom EVM specific TOML values to your Chainlink nodes customNetworkTOML := `` // Uncomment below for debugging TOML issues on the node - network := networks.MustGetSelectedNetworksFromEnv()[0] - fmt.Println("Using Chainlink TOML\n---------------------") - fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCR1Config, customNetworkTOML, network)) - fmt.Println("---------------------") + // network := networks.MustGetSelectedNetworksFromEnv()[0] + // fmt.Println("Using Chainlink TOML\n---------------------") + // fmt.Println(networks.AddNetworkDetailedConfig(config.BaseOCR1Config, customNetworkTOML, network)) + // fmt.Println("---------------------") ocrSoakTest, err := testsetups.NewOCRSoakTest(t, false) require.NoError(t, err, "Error creating soak test") From f34b581ae7bee8529635e9526eecd4bdc30b26e5 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 21 Nov 2023 10:13:03 -0600 Subject: [PATCH 185/271] switch from ocr2keepers to chainlink-automation (#11333) --- contracts/src/v0.8/automation/v2_1/LICENSE | 2 +- core/scripts/chaincli/handler/debug.go | 3 ++- .../chaincli/handler/keeper_deployer.go | 7 ++++--- core/scripts/chaincli/handler/ocr2_config.go | 4 +++- core/scripts/chaincli/handler/report.go | 4 +++- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/ocr2/delegate.go | 16 +++++++-------- .../plugins/ocr2keeper/custom_telemetry.go | 2 +- .../ocr2/plugins/ocr2keeper/evm20/abi.go | 3 ++- .../ocr2/plugins/ocr2keeper/evm20/abi_test.go | 3 ++- .../ocr2/plugins/ocr2keeper/evm20/encoder.go | 5 +++-- .../plugins/ocr2keeper/evm20/encoder_test.go | 3 ++- .../ocr2/plugins/ocr2keeper/evm20/head.go | 2 +- .../plugins/ocr2keeper/evm20/log_provider.go | 6 +++--- .../ocr2/plugins/ocr2keeper/evm20/registry.go | 2 +- .../plugins/ocr2keeper/evm20/registry_test.go | 2 +- .../plugins/ocr2keeper/evm21/active_list.go | 2 +- .../ocr2keeper/evm21/active_list_test.go | 3 ++- .../evm21/autotelemetry21/custom_telemetry.go | 2 +- .../ocr2keeper/evm21/block_subscriber.go | 2 +- .../ocr2keeper/evm21/block_subscriber_test.go | 2 +- .../ocr2keeper/evm21/core/interfaces.go | 2 +- .../evm21/core/mocks/upkeep_state_reader.go | 2 +- .../plugins/ocr2keeper/evm21/core/payload.go | 3 ++- .../ocr2keeper/evm21/core/payload_test.go | 4 ++-- .../plugins/ocr2keeper/evm21/core/testutil.go | 3 ++- .../plugins/ocr2keeper/evm21/core/trigger.go | 2 +- .../ocr2keeper/evm21/core/trigger_test.go | 3 ++- .../plugins/ocr2keeper/evm21/core/type.go | 2 +- .../ocr2keeper/evm21/core/type_test.go | 4 ++-- .../ocr2keeper/evm21/encoding/encoder.go | 2 +- .../ocr2keeper/evm21/encoding/encoder_test.go | 3 ++- .../ocr2keeper/evm21/encoding/interface.go | 2 +- .../ocr2keeper/evm21/encoding/packer.go | 2 +- .../ocr2keeper/evm21/encoding/packer_test.go | 3 ++- .../ocr2/plugins/ocr2keeper/evm21/keyring.go | 3 ++- .../plugins/ocr2keeper/evm21/keyring_test.go | 6 ++++-- .../ocr2keeper/evm21/logprovider/buffer.go | 4 ++-- .../evm21/logprovider/buffer_test.go | 3 ++- .../evm21/logprovider/integration_test.go | 5 ++--- .../ocr2keeper/evm21/logprovider/provider.go | 2 +- .../logprovider/provider_life_cycle_test.go | 2 +- .../evm21/logprovider/provider_test.go | 5 ++--- .../ocr2keeper/evm21/logprovider/recoverer.go | 4 ++-- .../evm21/logprovider/recoverer_test.go | 2 +- .../evm21/mercury/streams/streams.go | 2 +- .../evm21/mercury/streams/streams_test.go | 13 +++++------- .../ocr2keeper/evm21/payload_builder.go | 2 +- .../ocr2keeper/evm21/payload_builder_test.go | 2 +- .../ocr2/plugins/ocr2keeper/evm21/registry.go | 2 +- .../evm21/registry_check_pipeline.go | 3 ++- .../evm21/registry_check_pipeline_test.go | 20 ++++++++----------- .../plugins/ocr2keeper/evm21/registry_test.go | 3 ++- .../ocr2/plugins/ocr2keeper/evm21/services.go | 6 ++++-- .../ocr2keeper/evm21/transmit/cache.go | 2 +- .../ocr2keeper/evm21/transmit/cache_test.go | 3 ++- .../ocr2keeper/evm21/transmit/encoding.go | 2 +- .../evm21/transmit/encoding_test.go | 3 ++- .../evm21/transmit/event_provider.go | 2 +- .../evm21/transmit/event_provider_test.go | 2 +- .../ocr2keeper/evm21/upkeep_provider.go | 2 +- .../ocr2keeper/evm21/upkeep_provider_test.go | 3 ++- .../ocr2keeper/evm21/upkeepstate/store.go | 2 +- .../evm21/upkeepstate/store_test.go | 2 +- .../plugins/ocr2keeper/integration_21_test.go | 6 +++--- .../plugins/ocr2keeper/integration_test.go | 11 +++++----- core/services/ocr2/plugins/ocr2keeper/util.go | 12 +++++------ core/services/relay/evm/ocr2keeper.go | 6 +++--- core/services/telemetry/manager_test.go | 5 +++-- go.mod | 2 +- go.sum | 4 ++-- .../actions/automation_ocr_helpers.go | 5 +++-- .../actions/automation_ocr_helpers_local.go | 8 +++++--- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- .../automationv2_1/automationv2_1_test.go | 13 +++++++----- 77 files changed, 166 insertions(+), 142 deletions(-) diff --git a/contracts/src/v0.8/automation/v2_1/LICENSE b/contracts/src/v0.8/automation/v2_1/LICENSE index 03ba03d7792..7d09d647f5b 100644 --- a/contracts/src/v0.8/automation/v2_1/LICENSE +++ b/contracts/src/v0.8/automation/v2_1/LICENSE @@ -12,7 +12,7 @@ Licensor: SmartContract Chainlink Limited SEZC Licensed Work: Automation v2.1 The Licensed Work is (c) 2023 SmartContract Chainlink Limited SEZC -Additional Use Grant: Any uses listed and defined at https://github.com/smartcontractkit/ocr2keepers/tree/main/pkg/v3/Automation_v2.1_Grants.md +Additional Use Grant: Any uses listed and defined at https://github.com/smartcontractkit/chainlink-automation/tree/main/pkg/v3/Automation_v2.1_Grants.md Change Date: September 12, 2027 diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 8f65b1f8814..4938907669f 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -19,7 +19,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" diff --git a/core/scripts/chaincli/handler/keeper_deployer.go b/core/scripts/chaincli/handler/keeper_deployer.go index eae8c68d332..7c532753426 100644 --- a/core/scripts/chaincli/handler/keeper_deployer.go +++ b/core/scripts/chaincli/handler/keeper_deployer.go @@ -13,14 +13,15 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/umbracle/ethgo/abi" + ocr2config "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - ocr3confighelper "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/umbracle/ethgo/abi" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" - offchain20config "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" + offchain20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/scripts/chaincli/handler/ocr2_config.go b/core/scripts/chaincli/handler/ocr2_config.go index 7cf25364058..21b2ad414a3 100644 --- a/core/scripts/chaincli/handler/ocr2_config.go +++ b/core/scripts/chaincli/handler/ocr2_config.go @@ -11,8 +11,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/olekukonko/tablewriter" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" - ocr2keepers20config "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" + + ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" ) diff --git a/core/scripts/chaincli/handler/report.go b/core/scripts/chaincli/handler/report.go index 90f2be96548..8381bbc1374 100644 --- a/core/scripts/chaincli/handler/report.go +++ b/core/scripts/chaincli/handler/report.go @@ -16,8 +16,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" "github.com/olekukonko/tablewriter" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers20 "github.com/smartcontractkit/ocr2keepers/pkg/v2" + + ocr2keepers20 "github.com/smartcontractkit/chainlink-automation/pkg/v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm20" diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 09c76dd195c..0d78e15d596 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,10 +21,10 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 + github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 - github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index b1a08701144..3dd48f7445a 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1462,6 +1462,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= +github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= +github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= @@ -1478,8 +1480,6 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= -github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= -github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 2f2a9033398..7a90e3b07e4 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -17,13 +17,14 @@ import ( "github.com/smartcontractkit/libocr/commontypes" libocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers20 "github.com/smartcontractkit/ocr2keepers/pkg/v2" - ocr2keepers20config "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" - ocr2keepers20coordinator "github.com/smartcontractkit/ocr2keepers/pkg/v2/coordinator" - ocr2keepers20polling "github.com/smartcontractkit/ocr2keepers/pkg/v2/observer/polling" - ocr2keepers20runner "github.com/smartcontractkit/ocr2keepers/pkg/v2/runner" - ocr2keepers21config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" - ocr2keepers21 "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" + + ocr2keepers20 "github.com/smartcontractkit/chainlink-automation/pkg/v2" + ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" + ocr2keepers20coordinator "github.com/smartcontractkit/chainlink-automation/pkg/v2/coordinator" + ocr2keepers20polling "github.com/smartcontractkit/chainlink-automation/pkg/v2/observer/polling" + ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" + ocr2keepers21config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" + ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" "github.com/smartcontractkit/chainlink-vrf/altbn_128" dkgpkg "github.com/smartcontractkit/chainlink-vrf/dkg" @@ -66,7 +67,6 @@ import ( functionsRelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/functions" evmmercury "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" - evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go index 6d52c0e8d25..fd43747f52a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/abi.go b/core/services/ocr2/plugins/ocr2keeper/evm20/abi.go index 4c0beee8fd6..98aeed66787 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/abi.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/abi.go @@ -8,7 +8,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" ) type evmRegistryPackerV2_0 struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/abi_test.go b/core/services/ocr2/plugins/ocr2keeper/evm20/abi_test.go index 7efc3feb1f0..c63c0d00f33 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/abi_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/abi_test.go @@ -8,9 +8,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" "github.com/stretchr/testify/assert" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/encoder.go b/core/services/ocr2/plugins/ocr2keeper/evm20/encoder.go index 3984cb7496b..aa98fd44cec 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/encoder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/encoder.go @@ -6,8 +6,9 @@ import ( "reflect" "github.com/ethereum/go-ethereum/accounts/abi" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" - "github.com/smartcontractkit/ocr2keepers/pkg/v2/encoding" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" + "github.com/smartcontractkit/chainlink-automation/pkg/v2/encoding" ) type EVMAutomationEncoder20 struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/encoder_test.go b/core/services/ocr2/plugins/ocr2keeper/evm20/encoder_test.go index 74be6b46ee5..76212892657 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/encoder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/encoder_test.go @@ -5,8 +5,9 @@ import ( "testing" "github.com/pkg/errors" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" "github.com/stretchr/testify/assert" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" ) func TestEVMAutomationEncoder20(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/head.go b/core/services/ocr2/plugins/ocr2keeper/evm20/head.go index 1023bff5dd2..78983173558 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/head.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/head.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go index d8941d505be..7ab641d2bf7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go @@ -11,9 +11,9 @@ import ( "github.com/ethereum/go-ethereum/common" gethtypes "github.com/ethereum/go-ethereum/core/types" - pluginutils "github.com/smartcontractkit/ocr2keepers/pkg/util" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" - "github.com/smartcontractkit/ocr2keepers/pkg/v2/encoding" + pluginutils "github.com/smartcontractkit/chainlink-automation/pkg/util" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" + "github.com/smartcontractkit/chainlink-automation/pkg/v2/encoding" "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go index 7fe4087cfa2..3cb91ab8dcb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "go.uber.org/multierr" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go index 340bd923577..0e0ceba7160 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v2" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go b/core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go index 899dd398d03..9f825556331 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go @@ -4,7 +4,7 @@ import ( "math/big" "sync" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go index 76ca6bfdf19..06b9979eefc 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go @@ -5,9 +5,10 @@ import ( "sort" "testing" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go index 0cf0bbef5cd..0ed4c84dbb5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go index ae407b0ea95..134a75fc2c7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go @@ -9,7 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go index afb9d4a0919..41a43b951e7 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" commonmocks "github.com/smartcontractkit/chainlink/v2/common/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/interfaces.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/interfaces.go index d9417c72a19..49975855b54 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/interfaces.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/interfaces.go @@ -3,7 +3,7 @@ package core import ( "context" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) // UpkeepStateReader is the interface for reading the current state of upkeeps. diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks/upkeep_state_reader.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks/upkeep_state_reader.go index 40b63611c90..b036fbb26c3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks/upkeep_state_reader.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks/upkeep_state_reader.go @@ -7,7 +7,7 @@ import ( mock "github.com/stretchr/testify/mock" - types "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + types "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) // UpkeepStateReader is an autogenerated mock type for the UpkeepStateReader type diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload.go index d7336737674..ed5530ae7b5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload.go @@ -6,7 +6,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/crypto" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) var ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload_test.go index 1ca280c0d2d..cb3a67dde0c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/stretchr/testify/assert" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) func TestWorkID(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/testutil.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/testutil.go index 47935b56f68..3c0eeb1c44e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/testutil.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/testutil.go @@ -4,7 +4,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) // GenUpkeepID generates an ocr2keepers.UpkeepIdentifier with a specific UpkeepType and some random string diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger.go index 79273479596..02e6946c8fb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger_test.go index 0233e40679d..366d59b6397 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger_test.go @@ -7,8 +7,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) func TestPackUnpackTrigger(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/type.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/type.go index aeb56a16f62..7820b47e6eb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/type.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/type.go @@ -3,7 +3,7 @@ package core import ( "math/big" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) const ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/type_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/type_test.go index 8a2a51533e5..6f81ca7690e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/type_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/type_test.go @@ -4,9 +4,9 @@ import ( "math/big" "testing" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" - "github.com/stretchr/testify/assert" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) func TestGetUpkeepType(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go index 239de099c01..fadf55633d0 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go index 578153e07a4..63ff2650be2 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go @@ -5,9 +5,10 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go index e21ecfb800d..cf98115d76f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go @@ -3,7 +3,7 @@ package encoding import ( "math/big" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go index 28c6c4a9759..0157d1baf8e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go @@ -7,7 +7,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go index 03b55bfccc7..b52b777dbe5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go @@ -9,10 +9,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/keyring.go b/core/services/ocr2/plugins/ocr2keeper/evm21/keyring.go index df4e3a86502..2d84f096378 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/keyring.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/keyring.go @@ -3,7 +3,8 @@ package evm import ( "github.com/smartcontractkit/libocr/offchainreporting2/types" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" + + "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" ) var _ ocr3types.OnchainKeyring[plugin.AutomationReportInfo] = &onchainKeyringV3Wrapper{} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/keyring_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/keyring_test.go index 14c42a810c1..92a05c2d760 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/keyring_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/keyring_test.go @@ -3,10 +3,12 @@ package evm import ( "testing" + "github.com/stretchr/testify/assert" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" - "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" ) func TestNewOnchainKeyringV3Wrapper(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer.go index b06a3ca809f..81f51311d44 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer.go @@ -7,8 +7,8 @@ import ( "sync" "sync/atomic" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/random" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/random" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go index 5c8908f9be7..a973ea6e19c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go @@ -7,9 +7,10 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go index c7db01d8788..dd90b5e00bf 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go @@ -12,14 +12,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/jmoiron/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "golang.org/x/time/rate" - "github.com/jmoiron/sqlx" - - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go index d3f069a9bf9..a3887cbe787 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go @@ -16,7 +16,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go index b28b45fcb42..c58bd859510 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go index c6ebb1c51a3..81774e26387 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go @@ -11,15 +11,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "golang.org/x/time/rate" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - - "golang.org/x/time/rate" ) func TestLogEventProvider_GetFilters(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go index c6566568158..98eb0b291bc 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go @@ -16,8 +16,8 @@ import ( "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/random" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/random" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go index 77b0eec5454..10e58bf26b3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go index d34787f501e..f98d4ed2df4 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go index 0653796f413..baa277e0357 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go @@ -15,24 +15,21 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" - v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" - v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" - "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/evm21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" + v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" ) type MockMercuryConfigProvider struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go b/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go index b14e687b5d1..e138f440e49 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go @@ -3,7 +3,7 @@ package evm import ( "context" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go index e68e316ae30..d7c2312d193 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index afb1a7cf6c3..18795eb0736 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go index ad31c8feb06..b0db3f7dd13 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go @@ -9,7 +9,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go index d4e38637d8c..4eaf334f7c6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go @@ -8,31 +8,27 @@ import ( "sync/atomic" "testing" - "github.com/stretchr/testify/require" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/patrickmn/go-cache" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" - - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" - + "github.com/patrickmn/go-cache" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/streams_lookup_compatible_interface" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "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/evm21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go index 4be0ccce4e9..fdc6675da85 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go @@ -11,10 +11,11 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" coreTypes "github.com/ethereum/go-ethereum/core/types" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + types3 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go b/core/services/ocr2/plugins/ocr2keeper/evm21/services.go index d178a9af574..18c9ed6d39d 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/services.go @@ -5,10 +5,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + + "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache.go index 5e0dadbdc4f..99e1f1dc164 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache.go @@ -3,7 +3,7 @@ package transmit import ( "sync" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) // transmitEventCache holds a ring buffer of the last visited blocks (transmit block), diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache_test.go index 55d245c168f..7f4a1296567 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache_test.go @@ -3,8 +3,9 @@ package transmit import ( "testing" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/require" + + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" ) func TestTransmitEventCache_Sanity(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding.go index ec709d04bd8..17d3cd439f8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go index f081f82f2a2..7415b3701f8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go @@ -4,9 +4,10 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" 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/evm21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go index 000d40f4072..95f5ec0bf11 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go @@ -8,7 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go index a33056977ac..8d56fec22a5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmClientMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go index 5cb60d5dc1d..5b6d4ac7032 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go index ac49675812e..69bfabb259f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go @@ -5,9 +5,10 @@ import ( "sync/atomic" "testing" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" "github.com/stretchr/testify/require" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go index f5e3969bc79..505959b8f39 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go @@ -8,7 +8,7 @@ import ( "sync" "time" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go index 579d8757921..138b9ffd782 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - ocr2keepers "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index d2f35a3e37a..01418b9f761 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -26,13 +26,12 @@ import ( "github.com/stretchr/testify/require" "github.com/umbracle/ethgo/abi" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" - "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" + + "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -55,6 +54,7 @@ import ( "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/ocr2/plugins/ocr2keeper" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_test.go index 3eda8867968..1ab9194c496 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_test.go @@ -23,15 +23,16 @@ import ( "github.com/hashicorp/consul/sdk/freeport" "github.com/onsi/gomega" "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/umbracle/ethgo/abi" "github.com/smartcontractkit/libocr/commontypes" "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/umbracle/ethgo/abi" + "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" @@ -60,8 +61,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" - - "github.com/smartcontractkit/chainlink-common/pkg/types" ) const ( diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 504b6267c60..2c477e7a6a1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -6,14 +6,14 @@ import ( "github.com/jmoiron/sqlx" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers20 "github.com/smartcontractkit/ocr2keepers/pkg/v2" - ocr2keepers20coordinator "github.com/smartcontractkit/ocr2keepers/pkg/v2/coordinator" - ocr2keepers20polling "github.com/smartcontractkit/ocr2keepers/pkg/v2/observer/polling" - ocr2keepers20runner "github.com/smartcontractkit/ocr2keepers/pkg/v2/runner" - "github.com/smartcontractkit/chainlink-common/pkg/types" + ocr2keepers20 "github.com/smartcontractkit/chainlink-automation/pkg/v2" + ocr2keepers20coordinator "github.com/smartcontractkit/chainlink-automation/pkg/v2/coordinator" + ocr2keepers20polling "github.com/smartcontractkit/chainlink-automation/pkg/v2/observer/polling" + ocr2keepers20runner "github.com/smartcontractkit/chainlink-automation/pkg/v2/runner" + ocr2keepers21 "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - ocr2keepers21 "github.com/smartcontractkit/ocr2keepers/pkg/v3/types" + "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 2e8f9cbf487..2b484edb798 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -14,11 +14,11 @@ import ( "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/ocr2keepers/pkg/v3/plugin" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/logger" diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index 31f7ea74c19..f09e4d3482f 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -190,12 +190,13 @@ func TestNewManager(t *testing.T) { require.Equal(t, "TelemetryManager", m.Name()) require.Nil(t, m.Start(testutils.Context(t))) + t.Cleanup(func() { + require.NoError(t, m.Close()) + }) testutils.WaitForLogMessageCount(t, logObs, "error connecting error while dialing dial tcp", 3) hr := m.HealthReport() require.Equal(t, 4, len(hr)) - require.Nil(t, m.Close()) - time.Sleep(time.Second * 1) } func TestCorrectEndpointRouting(t *testing.T) { diff --git a/go.mod b/go.mod index c13f476bf2f..b3bdeb74cf1 100644 --- a/go.mod +++ b/go.mod @@ -65,13 +65,13 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 + github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 - github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wsrpc v0.7.2 diff --git a/go.sum b/go.sum index 4f465dc8f90..bdfbe2107ec 100644 --- a/go.sum +++ b/go.sum @@ -1463,6 +1463,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= +github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= +github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= @@ -1479,8 +1481,6 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= -github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= -github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/actions/automation_ocr_helpers.go b/integration-tests/actions/automation_ocr_helpers.go index e1635902db5..38df2e00b5c 100644 --- a/integration-tests/actions/automation_ocr_helpers.go +++ b/integration-tests/actions/automation_ocr_helpers.go @@ -17,8 +17,9 @@ import ( ocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers20config "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" - ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" + + ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" + ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" diff --git a/integration-tests/actions/automation_ocr_helpers_local.go b/integration-tests/actions/automation_ocr_helpers_local.go index f541594c4d2..e591e75a983 100644 --- a/integration-tests/actions/automation_ocr_helpers_local.go +++ b/integration-tests/actions/automation_ocr_helpers_local.go @@ -9,12 +9,14 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" "github.com/rs/zerolog" + "gopkg.in/guregu/null.v4" + ocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - ocr2keepers20config "github.com/smartcontractkit/ocr2keepers/pkg/v2/config" - ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" - "gopkg.in/guregu/null.v4" + + ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" + ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 9e99f39b445..cdd42deda7b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -22,12 +22,12 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 + github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e github.com/smartcontractkit/chainlink-testing-framework v1.19.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 - github.com/smartcontractkit/ocr2keepers v0.7.28 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 github.com/smartcontractkit/wasp v0.3.0 github.com/spf13/cobra v1.6.1 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 87cceb9b99a..08b772c9cec 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2367,6 +2367,8 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= +github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= +github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= @@ -2385,8 +2387,6 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJ github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 h1:21V61XOYSxpFmFqlhr5IaEh1uQ1F6CewJ30D/U/P34c= github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7/go.mod h1:2lyRkw/qLQgUWlrWWmq5nj0y90rWeO6Y+v+fCakRgb0= -github.com/smartcontractkit/ocr2keepers v0.7.28 h1:dufAiYl4+uly9aH0+6GkS2jYzHGujq7tg0LYQE+x6JU= -github.com/smartcontractkit/ocr2keepers v0.7.28/go.mod h1:1QGzJURnoWpysguPowOe2bshV0hNp1YX10HHlhDEsas= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 h1:yiKnypAqP8l0OX0P3klzZ7SCcBUxy5KqTAKZmQOvSQE= github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index 06c2624d0f0..1f39d5e4b0e 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -18,9 +18,10 @@ import ( "github.com/stretchr/testify/require" ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" - ocr2keepers30config "github.com/smartcontractkit/ocr2keepers/pkg/v3/config" "github.com/smartcontractkit/wasp" + ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" @@ -29,15 +30,17 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/ethereum" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" + + registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" - registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" ) const ( From 4342e167b5f4b3ace7be3f374a64d047dd7492cc Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:29:38 -0800 Subject: [PATCH 186/271] release/2.7.0 -> develop (#11291) * Bump version and update CHANGELOG for core v2.7.0 * core: log a warning when deprecated P2P.V1 config is set in TOML (#11073) * core: log a warning when deprecated P2P.V1 config is set in TOML * change default P2P from V1 to V2 * bump operator ui (cherry picked from commit 5808e734cf024a5e8c5450e939e1f2d5b3cba546) * operator-ui deprecation warnings (#11104) Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Co-authored-by: github-merge-queue[bot] Co-authored-by: Jordan Krage * chore: bump sigstore/cosign-installer from 2.1.0 to 3.1.2 * Finalize date on changelog for 2.7.0 --------- Co-authored-by: Jordan Krage Co-authored-by: chainchad <96362174+chainchad@users.noreply.github.com> Co-authored-by: george-dorin <120329946+george-dorin@users.noreply.github.com> Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Co-authored-by: github-merge-queue[bot] Co-authored-by: Erik Burton --- docs/CHANGELOG.md | 4 ++-- integration-tests/smoke/automation_test.go | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1351c421340..8e016347c43 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -36,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ... + + ## 2.7.1 - UNRELEASED ### Fixed @@ -74,8 +76,6 @@ Starting in `v2.9.0`: - Removed the ability to set a next nonce value for an address through CLI - - ## 2.6.0 - 2023-10-18 ### Added diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 05d19d5ccd4..cdc8748676f 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -10,16 +10,12 @@ import ( "testing" "time" - "github.com/kelseyhightower/envconfig" - - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" - "github.com/ethereum/go-ethereum/common" + "github.com/kelseyhightower/envconfig" "github.com/onsi/gomega" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" @@ -27,6 +23,7 @@ import ( cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/integration-tests/actions" From 4c6d0fe9d0bc4e4c450b0c528e08db000b745e9f Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:49:47 -0500 Subject: [PATCH 187/271] Avoid checking version file bump on forks in CI (#11351) --- .github/workflows/build-publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-publish.yml b/.github/workflows/build-publish.yml index 1bda6957a2a..de33663d88d 100644 --- a/.github/workflows/build-publish.yml +++ b/.github/workflows/build-publish.yml @@ -17,7 +17,8 @@ jobs: - name: Checkout repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Check for VERSION file bump on tags - if: ${{ startsWith(github.ref, 'refs/tags/v') }} + # Avoids checking VERSION file bump on forks. + if: ${{ github.repository == 'smartcontractkit/chainlink' && startsWith(github.ref, 'refs/tags/v') }} uses: ./.github/actions/version-file-bump with: github-token: ${{ secrets.GITHUB_TOKEN }} From aca537daff2cd5c01cc0f204b1b345a7e6c1385c Mon Sep 17 00:00:00 2001 From: amit-momin <108959691+amit-momin@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:04:20 -0600 Subject: [PATCH 188/271] Remove dependencies on services package in common (#11321) * Removed dependencies on services package in common * Marked json data type as deprecated and cleaned up test vars * Replaced json data type with alias --- common/txmgr/types/forwarder_manager.go | 4 +- common/txmgr/types/tx.go | 6 +- common/txmgr/types/tx_attempt_builder.go | 4 +- common/txmgr/types/tx_store.go | 3 - common/types/head_tracker.go | 6 +- core/chains/evm/txmgr/broadcaster_test.go | 4 +- core/chains/evm/txmgr/evm_tx_store.go | 7 +-- core/chains/evm/txmgr/transmitchecker_test.go | 6 +- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/keystore/keys/ethkey/key.go | 10 ++-- core/services/pg/datatypes/json.go | 56 +------------------ core/services/vrf/v2/integration_v2_test.go | 17 +++--- core/services/vrf/v2/listener_v2_test.go | 10 ++-- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 18 files changed, 49 insertions(+), 102 deletions(-) diff --git a/common/txmgr/types/forwarder_manager.go b/common/txmgr/types/forwarder_manager.go index 8d58f91295e..4d70b730004 100644 --- a/common/txmgr/types/forwarder_manager.go +++ b/common/txmgr/types/forwarder_manager.go @@ -1,13 +1,13 @@ package types import ( + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/services" ) //go:generate mockery --quiet --name ForwarderManager --output ./mocks/ --case=underscore type ForwarderManager[ADDR types.Hashable] interface { - services.ServiceCtx + services.Service ForwarderFor(addr ADDR) (forwarder ADDR, err error) // Converts payload to be forwarder-friendly ConvertPayload(dest ADDR, origPayload []byte) ([]byte, error) diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index 11017bd0325..78f6fba4592 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -13,11 +13,11 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" clnull "github.com/smartcontractkit/chainlink/v2/core/null" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" ) // TxStrategy controls how txes are queued and sent @@ -210,7 +210,7 @@ type Tx[ // Marshalled TxMeta // Used for additional context around transactions which you want to log // at send time. - Meta *datatypes.JSON + Meta *sqlutil.JSON Subject uuid.NullUUID ChainID CHAIN_ID @@ -219,7 +219,7 @@ type Tx[ // TransmitChecker defines the check that should be performed before a transaction is submitted on // chain. - TransmitChecker *datatypes.JSON + TransmitChecker *sqlutil.JSON // Marks tx requiring callback SignalCallback bool diff --git a/common/txmgr/types/tx_attempt_builder.go b/common/txmgr/types/tx_attempt_builder.go index 887219c490e..75712fc0c37 100644 --- a/common/txmgr/types/tx_attempt_builder.go +++ b/common/txmgr/types/tx_attempt_builder.go @@ -3,10 +3,10 @@ package types import ( "context" + "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services" ) // TxAttemptBuilder takes the base unsigned transaction + optional parameters (tx type, gas parameters) @@ -23,7 +23,7 @@ type TxAttemptBuilder[ FEE feetypes.Fee, // FEE - chain fee type ] interface { // interfaces for running the underlying estimator - services.ServiceCtx + services.Service types.HeadTrackable[HEAD, BLOCK_HASH] // NewTxAttempt builds a transaction using the configured transaction type and fee estimator (new estimation) diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index c2dfeee4146..f731031f926 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -9,7 +9,6 @@ import ( feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) // TxStore is a superset of all the needed persistence layer methods @@ -118,8 +117,6 @@ type ReceiptPlus[R any] struct { FailOnRevert bool `db:"fail_on_revert"` } -type QueryerFunc = func(tx pg.Queryer) error - type ChainReceipt[TX_HASH, BLOCK_HASH types.Hashable] interface { GetStatus() uint64 GetTxHash() TX_HASH diff --git a/common/types/head_tracker.go b/common/types/head_tracker.go index 811298f8956..d8fa8011783 100644 --- a/common/types/head_tracker.go +++ b/common/types/head_tracker.go @@ -3,7 +3,7 @@ package types import ( "context" - "github.com/smartcontractkit/chainlink/v2/core/services" + "github.com/smartcontractkit/chainlink-common/pkg/services" ) // HeadTracker holds and stores the block experienced by a particular node in a thread safe manner. @@ -11,7 +11,7 @@ import ( // //go:generate mockery --quiet --name HeadTracker --output ../mocks/ --case=underscore type HeadTracker[H Head[BLOCK_HASH], BLOCK_HASH Hashable] interface { - services.ServiceCtx + services.Service // Backfill given a head will fill in any missing heads up to the given depth // (used for testing) Backfill(ctx context.Context, headWithChain H, depth uint) (err error) @@ -68,7 +68,7 @@ type NewHeadHandler[H Head[BLOCK_HASH], BLOCK_HASH Hashable] func(ctx context.Co // //go:generate mockery --quiet --name HeadBroadcaster --output ../mocks/ --case=underscore type HeadBroadcaster[H Head[BLOCK_HASH], BLOCK_HASH Hashable] interface { - services.ServiceCtx + services.Service BroadcastNewLongestChain(H) HeadBroadcasterRegistry[H, BLOCK_HASH] } diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index bf480c66af6..d1e26c6c969 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -22,6 +22,7 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -43,7 +44,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -255,7 +255,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { tr := int32(99) b, err := json.Marshal(txmgr.TxMeta{JobID: &tr}) require.NoError(t, err) - meta := datatypes.JSON(b) + meta := sqlutil.JSON(b) earlierEthTx := txmgr.Tx{ FromAddress: fromAddress, ToAddress: toAddress, diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 0e08d32b77a..84fe9a02c6a 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -18,6 +18,7 @@ import ( pkgerrors "github.com/pkg/errors" nullv4 "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -27,7 +28,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -171,14 +171,14 @@ type DbEthTx struct { // Marshalled EvmTxMeta // Used for additional context around transactions which you want to log // at send time. - Meta *datatypes.JSON + Meta *sqlutil.JSON Subject uuid.NullUUID PipelineTaskRunID uuid.NullUUID MinConfirmations null.Uint32 EVMChainID utils.Big // TransmitChecker defines the check that should be performed before a transaction is submitted on // chain. - TransmitChecker *datatypes.JSON + TransmitChecker *sqlutil.JSON InitialBroadcastAt *time.Time // Marks tx requiring callback SignalCallback bool @@ -1451,7 +1451,6 @@ func (o *evmTxStore) UpdateTxFatalError(ctx context.Context, etx *Tx) error { } // Updates eth attempt from in_progress to broadcast. Also updates the eth tx to unconfirmed. -// One of the more complicated signatures. We have to accept variable pg.QOpt and QueryerFunc arguments func (o *evmTxStore) UpdateTxAttemptInProgressToBroadcast(ctx context.Context, etx *Tx, attempt TxAttempt, NewAttemptState txmgrtypes.TxAttemptState) error { var cancel context.CancelFunc ctx, cancel = o.mergeContexts(ctx) diff --git a/core/chains/evm/txmgr/transmitchecker_test.go b/core/chains/evm/txmgr/transmitchecker_test.go index b43fcb4f459..5ebf5f32e38 100644 --- a/core/chains/evm/txmgr/transmitchecker_test.go +++ b/core/chains/evm/txmgr/transmitchecker_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -29,7 +30,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" ) func TestFactory(t *testing.T) { @@ -192,7 +192,7 @@ func TestTransmitCheckers(t *testing.T) { b, err := json.Marshal(meta) require.NoError(t, err) - metaJson := datatypes.JSON(b) + metaJson := sqlutil.JSON(b) tx := txmgr.Tx{ FromAddress: common.HexToAddress("0xfe0629509E6CB8dfa7a99214ae58Ceb465d5b5A9"), @@ -296,7 +296,7 @@ func TestTransmitCheckers(t *testing.T) { b, err := json.Marshal(meta) require.NoError(t, err) - metaJson := datatypes.JSON(b) + metaJson := sqlutil.JSON(b) tx := txmgr.Tx{ FromAddress: common.HexToAddress("0xfe0629509E6CB8dfa7a99214ae58Ceb465d5b5A9"), diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 0d78e15d596..e9fa432d4cd 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -303,7 +303,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 3dd48f7445a..3d9962474b9 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1464,8 +1464,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= diff --git a/core/services/keystore/keys/ethkey/key.go b/core/services/keystore/keys/ethkey/key.go index 4201251e34f..6335ed55adc 100644 --- a/core/services/keystore/keys/ethkey/key.go +++ b/core/services/keystore/keys/ethkey/key.go @@ -3,7 +3,7 @@ package ethkey import ( "time" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" ) // NOTE: This model refers to the OLD key and is only used for migrations @@ -15,10 +15,10 @@ import ( type Key struct { ID int32 Address EIP55Address - JSON datatypes.JSON `json:"-"` - CreatedAt time.Time `json:"-"` - UpdatedAt time.Time `json:"-"` - DeletedAt *time.Time `json:"-"` + JSON sqlutil.JSON `json:"-"` + CreatedAt time.Time `json:"-"` + UpdatedAt time.Time `json:"-"` + DeletedAt *time.Time `json:"-"` // IsFunding marks the address as being used for rescuing the node and the pending transactions // Only one key can be IsFunding=true at a time. IsFunding bool diff --git a/core/services/pg/datatypes/json.go b/core/services/pg/datatypes/json.go index d84c89269cc..ac72a5a5ed4 100644 --- a/core/services/pg/datatypes/json.go +++ b/core/services/pg/datatypes/json.go @@ -1,59 +1,9 @@ package datatypes import ( - "database/sql/driver" - "encoding/json" - "errors" - "fmt" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" ) // JSON defined JSON data type, need to implements driver.Valuer, sql.Scanner interface -type JSON json.RawMessage - -// Value return json value, implement driver.Valuer interface -func (j JSON) Value() (driver.Value, error) { - if len(j) == 0 { - return nil, nil - } - bytes, err := json.RawMessage(j).MarshalJSON() - return string(bytes), err -} - -// Scan scan value into Jsonb, implements sql.Scanner interface -func (j *JSON) Scan(value interface{}) error { - if value == nil { - *j = JSON("null") - return nil - } - var bytes []byte - switch v := value.(type) { - case []byte: - bytes = v - case string: - bytes = []byte(v) - default: - return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value)) - } - - result := json.RawMessage{} - err := json.Unmarshal(bytes, &result) - *j = JSON(result) - return err -} - -// MarshalJSON to output non base64 encoded []byte -func (j JSON) MarshalJSON() ([]byte, error) { - return json.RawMessage(j).MarshalJSON() -} - -// UnmarshalJSON to deserialize []byte -func (j *JSON) UnmarshalJSON(b []byte) error { - result := json.RawMessage{} - err := result.UnmarshalJSON(b) - *j = JSON(result) - return err -} - -func (j JSON) String() string { - return string(j) -} +// Deprecated: Use sqlutil.JSON instead +type JSON = sqlutil.JSON diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 6880fa17992..f57cb640f67 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -30,6 +30,7 @@ import ( "github.com/jmoiron/sqlx" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -70,7 +71,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" @@ -2038,13 +2038,13 @@ func TestStartingCountsV1(t *testing.T) { } md1, err := json.Marshal(&m1) require.NoError(t, err) - md1_ := datatypes.JSON(md1) + md1SQL := sqlutil.JSON(md1) reqID2 := utils.PadByteToHash(0x11) m2 := txmgr.TxMeta{ RequestID: &reqID2, } md2, err := json.Marshal(&m2) - md2_ := datatypes.JSON(md2) + md2SQL := sqlutil.JSON(md2) require.NoError(t, err) chainID := utils.NewBig(testutils.SimulatedChainID) confirmedTxes := []txmgr.Tx{ @@ -2056,7 +2056,7 @@ func TestStartingCountsV1(t *testing.T) { InitialBroadcastAt: &b, CreatedAt: b, State: txmgrcommon.TxConfirmed, - Meta: &datatypes.JSON{}, + Meta: &sqlutil.JSON{}, EncodedPayload: []byte{}, ChainID: chainID.ToInt(), }, @@ -2068,7 +2068,7 @@ func TestStartingCountsV1(t *testing.T) { InitialBroadcastAt: &b, CreatedAt: b, State: txmgrcommon.TxConfirmed, - Meta: &md1_, + Meta: &md1SQL, EncodedPayload: []byte{}, ChainID: chainID.ToInt(), }, @@ -2080,7 +2080,7 @@ func TestStartingCountsV1(t *testing.T) { InitialBroadcastAt: &b, CreatedAt: b, State: txmgrcommon.TxConfirmed, - Meta: &md2_, + Meta: &md2SQL, EncodedPayload: []byte{}, ChainID: chainID.ToInt(), }, @@ -2092,7 +2092,7 @@ func TestStartingCountsV1(t *testing.T) { InitialBroadcastAt: &b, CreatedAt: b, State: txmgrcommon.TxConfirmed, - Meta: &md2_, + Meta: &md2SQL, EncodedPayload: []byte{}, ChainID: chainID.ToInt(), }, @@ -2105,6 +2105,7 @@ func TestStartingCountsV1(t *testing.T) { RequestID: &reqID3, }) require.NoError(t, err2) + mdSQL := sqlutil.JSON(md) newNonce := evmtypes.Nonce(i + 1) unconfirmedTxes = append(unconfirmedTxes, txmgr.Tx{ Sequence: &newNonce, @@ -2114,7 +2115,7 @@ func TestStartingCountsV1(t *testing.T) { State: txmgrcommon.TxUnconfirmed, BroadcastAt: &b, InitialBroadcastAt: &b, - Meta: (*datatypes.JSON)(&md), + Meta: &mdSQL, EncodedPayload: []byte{}, ChainID: chainID.ToInt(), }) diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index 17615feb63a..513aa01ef65 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -17,9 +17,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/pg/datatypes" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" + "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm" @@ -77,7 +77,7 @@ func addEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, from common.Address, s RequestTxHash: &reqTxHash, }) require.NoError(t, err) - meta := datatypes.JSON(b) + meta := sqlutil.JSON(b) tx := &txmgr.Tx{ FromAddress: from, ToAddress: from, @@ -103,7 +103,7 @@ func addConfirmedEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, from common.A GlobalSubID: txMetaGlobalSubID, }) require.NoError(t, err) - meta := datatypes.JSON(b) + meta := sqlutil.JSON(b) now := time.Now() tx := &txmgr.Tx{ @@ -135,7 +135,7 @@ func addEthTxNativePayment(t *testing.T, txStore txmgr.TestEvmTxStore, from comm RequestTxHash: &reqTxHash, }) require.NoError(t, err) - meta := datatypes.JSON(b) + meta := sqlutil.JSON(b) tx := &txmgr.Tx{ FromAddress: from, ToAddress: from, @@ -161,7 +161,7 @@ func addConfirmedEthTxNativePayment(t *testing.T, txStore txmgr.TestEvmTxStore, GlobalSubID: txMetaGlobalSubID, }) require.NoError(t, err) - meta := datatypes.JSON(b) + meta := sqlutil.JSON(b) now := time.Now() tx := &txmgr.Tx{ Sequence: &nonce, diff --git a/go.mod b/go.mod index b3bdeb74cf1..e6bb7347ffd 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 diff --git a/go.sum b/go.sum index bdfbe2107ec..24d662390d0 100644 --- a/go.sum +++ b/go.sum @@ -1465,8 +1465,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index cdd42deda7b..d626b38cca4 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 github.com/smartcontractkit/chainlink-testing-framework v1.19.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 08b772c9cec..b8a84de26ed 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2369,8 +2369,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e h1:Fsx5IJDD14wdCAe2lEI1xgztIvzjiE2iVHvYzg/grew= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231117021201-6814387d8d3e/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= From f7c254bc9c7320506536b1c23bd5005476e17ee7 Mon Sep 17 00:00:00 2001 From: cfal Date: Wed, 22 Nov 2023 20:12:32 +0900 Subject: [PATCH 189/271] core/cmd/shell.go: pass DB and QConfig fields to CosmosFactoryConfig (#11360) --- core/cmd/shell.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 52c90907362..b82bd85e3ed 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -176,6 +176,8 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G cosmosCfg := chainlink.CosmosFactoryConfig{ Keystore: keyStore.Cosmos(), TOMLConfigs: cfg.CosmosConfigs(), + DB: db, + QConfig: cfg.Database(), } initOps = append(initOps, chainlink.InitCosmos(ctx, relayerFactory, cosmosCfg)) } From 60cb43a46b8d9d1eb7d3820687aa911249228bde Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Wed, 22 Nov 2023 11:09:29 -0500 Subject: [PATCH 190/271] Update CTF (#11367) --- integration-tests/go.mod | 14 ++++++++------ integration-tests/go.sum | 21 ++++++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d626b38cca4..ba90493d299 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,12 +24,12 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 - github.com/smartcontractkit/chainlink-testing-framework v1.19.1 + github.com/smartcontractkit/chainlink-testing-framework v1.19.4 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 - github.com/smartcontractkit/wasp v0.3.0 + github.com/smartcontractkit/wasp v0.3.6 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.23.0 @@ -85,6 +85,7 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee // indirect + github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect @@ -205,7 +206,7 @@ require ( github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 // indirect - github.com/grafana/loki v1.6.2-0.20230403212622-90888a0cc737 // indirect + github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 // indirect github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 // indirect github.com/grafana/pyroscope-go v1.0.4 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.4 // indirect @@ -360,6 +361,7 @@ require ( github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/otiai10/copy v1.14.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect @@ -381,7 +383,7 @@ require ( github.com/russross/blackfriday v1.6.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect - github.com/sercand/kuberesolver v2.4.0+incompatible // indirect + github.com/sercand/kuberesolver/v4 v4.0.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -420,7 +422,7 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 // indirect github.com/valyala/fastjson v1.4.1 // indirect - github.com/weaveworks/common v0.0.0-20221201103051-7c2720a9024d // indirect + github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 // indirect github.com/weaveworks/promrus v1.2.0 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect @@ -513,5 +515,5 @@ replace ( github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 - github.com/sercand/kuberesolver v2.4.0+incompatible => github.com/sercand/kuberesolver/v5 v5.1.1 + github.com/sercand/kuberesolver/v4 => github.com/sercand/kuberesolver/v5 v5.1.1 ) diff --git a/integration-tests/go.sum b/integration-tests/go.sum index b8a84de26ed..849df2afcab 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -721,6 +721,8 @@ github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I= github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 h1:rvc39Ol6z3MvaBzXkxFC6Nfsnixq/dRypushKDd7Nc0= github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5/go.mod h1:R/pdNYDYFQk+tuuOo7QES1kkv6OLmp5ze2XBZQIVffM= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -1360,8 +1362,8 @@ github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6 github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 h1:IOks+FXJ6iO/pfbaVEf4efNw+YzYBYNCkCabyrbkFTM= github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2/go.mod h1:zj+5BNZAVmQafV583uLTAOzRr963KPdEm4d6NPmtbwg= -github.com/grafana/loki v1.6.2-0.20230403212622-90888a0cc737 h1:o45+fZAYRtTjx+9fFml9LZxsCmLX65l39eWDgvdkIr0= -github.com/grafana/loki v1.6.2-0.20230403212622-90888a0cc737/go.mod h1:kxNnWCr4EMobhndjy7a2Qpm7jkLPnJW2ariYvY77hLE= +github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 h1:V5PspEXlSlNh22sMyGkgfSOVVLTsSmhbmsp1VPt8Fdc= +github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6/go.mod h1:+aWr7OBDuZMT+p0rKmLfW5saO2m3YOGBnt++IlgLhVk= github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 h1:VXitROTlmZtLzvokNe8ZbUKpmwldM4Hy1zdNRO32jKU= github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765/go.mod h1:DhJMrd2QInI/1CNtTN43BZuTmkccdizW1jZ+F6aHkhY= github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= @@ -1377,7 +1379,6 @@ github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLt github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.0 h1:f4tggROQKKcnh4eItay6z/HbHLqghBxS8g7pyMhmDio= @@ -2197,6 +2198,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -2377,8 +2380,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= -github.com/smartcontractkit/chainlink-testing-framework v1.19.1 h1:MdGM5jIrBi858Cv7qzfl1Qon93YW8InohAlDQqFoIb4= -github.com/smartcontractkit/chainlink-testing-framework v1.19.1/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk= +github.com/smartcontractkit/chainlink-testing-framework v1.19.4 h1:a5zG5k3MNbDBWiBdxF2cBLaMyB+WD0ROWPiZ0DVJQMc= +github.com/smartcontractkit/chainlink-testing-framework v1.19.4/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= @@ -2391,8 +2394,8 @@ github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235- github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:q6f4fe39oZPdsh1i57WznEZgxd8siidMaSFq3wdPmVg= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 h1:Dai1bn+Q5cpeGMQwRdjOdVjG8mmFFROVkSKuUgBErRQ= github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1/go.mod h1:G5Sd/yzHWf26rQ+X0nG9E0buKPqRGPMJAfk2gwCzOOw= -github.com/smartcontractkit/wasp v0.3.0 h1:mueeLvpb6HyGNwILxCOKShDR6q18plQn7Gb1j3G/Qkk= -github.com/smartcontractkit/wasp v0.3.0/go.mod h1:skquNdMbKxIrHi5O8Kyukf66AaaXuEpEEaSTxfHbhak= +github.com/smartcontractkit/wasp v0.3.6 h1:1TLWfrTzqZwNvyyoKzPZ8FLQat2lNz640eM+mMh2YxM= +github.com/smartcontractkit/wasp v0.3.6/go.mod h1:L/cyUGfpaWxy/2twOVJLRt2mySJEIqGrFj9nyvRLpSo= github.com/smartcontractkit/wsrpc v0.7.2 h1:iBXzMeg7vc5YoezIQBq896y25BARw7OKbhrb6vPbtRQ= github.com/smartcontractkit/wsrpc v0.7.2/go.mod h1:sj7QX2NQibhkhxTfs3KOhAj/5xwgqMipTvJVSssT9i0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -2530,8 +2533,8 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/weaveworks/common v0.0.0-20221201103051-7c2720a9024d h1:9Z/HiqeGN+LOnmotAMpFEQjuXZ4AGAVFG0rC1laP5Go= -github.com/weaveworks/common v0.0.0-20221201103051-7c2720a9024d/go.mod h1:Fnq3+U51tMkPRMC6Wr7zKGUeFFYX4YjNrNK50iU0fcE= +github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 h1:gjb7t9LCnRu14LHubyLIgrE+EYlAaREiPn/VknV7R3s= +github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205/go.mod h1:O9wmSPNVSuqxzUZPFlHnPQ8xnyvx0qBnKGFfGbj95uY= github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= From 543d94633499d592c2cd44f7048912eb38f7bae6 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 22 Nov 2023 10:17:51 -0600 Subject: [PATCH 191/271] core/web: go:generate operator-ui assets (#11239) --- .gitignore | 3 - .goreleaser.develop.yaml | 2 - GNUmakefile | 2 +- core/web/assets/9f6d832ef97e8493764e.svg | 1 + core/web/assets/9f6d832ef97e8493764e.svg.gz | Bin 0 -> 193 bytes core/web/assets/ba8bbf16ebf8e1d05bef.svg | 1 + core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz | Bin 0 -> 1739 bytes core/web/assets/index.html | 1 + core/web/assets/index.html.gz | Bin 0 -> 420 bytes core/web/assets/main.b0b6f79f7f4a94560e37.js | 187 ++++++++++++++++++ .../assets/main.b0b6f79f7f4a94560e37.js.gz | Bin 0 -> 1190421 bytes core/web/middleware.go | 1 + 12 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 core/web/assets/9f6d832ef97e8493764e.svg create mode 100644 core/web/assets/9f6d832ef97e8493764e.svg.gz create mode 100644 core/web/assets/ba8bbf16ebf8e1d05bef.svg create mode 100644 core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz create mode 100644 core/web/assets/index.html.gz create mode 100644 core/web/assets/main.b0b6f79f7f4a94560e37.js create mode 100644 core/web/assets/main.b0b6f79f7f4a94560e37.js.gz diff --git a/.gitignore b/.gitignore index 48e228eb836..3f016503a81 100644 --- a/.gitignore +++ b/.gitignore @@ -55,9 +55,6 @@ golangci-lint-output.txt # can be left behind by tests core/cmd/vrfkey1 -# left behind by webpack - compiled frontend assets -core/web/assets - # Integration Tests integration-tests/**/logs/ tests-*.xml diff --git a/.goreleaser.develop.yaml b/.goreleaser.develop.yaml index 53c927c9f97..20312491651 100644 --- a/.goreleaser.develop.yaml +++ b/.goreleaser.develop.yaml @@ -10,8 +10,6 @@ env: before: hooks: - - go mod tidy - - make operator-ui - ./tools/bin/goreleaser_utils before_hook # See https://goreleaser.com/customization/build/ diff --git a/GNUmakefile b/GNUmakefile index 2801f949682..cb665498a3a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -79,7 +79,7 @@ docker-plugins: .PHONY: operator-ui operator-ui: ## Fetch the frontend - ./operator_ui/install.sh + go generate ./core/web .PHONY: abigen abigen: ## Build & install abigen. diff --git a/core/web/assets/9f6d832ef97e8493764e.svg b/core/web/assets/9f6d832ef97e8493764e.svg new file mode 100644 index 00000000000..e35f1df284c --- /dev/null +++ b/core/web/assets/9f6d832ef97e8493764e.svg @@ -0,0 +1 @@ + diff --git a/core/web/assets/9f6d832ef97e8493764e.svg.gz b/core/web/assets/9f6d832ef97e8493764e.svg.gz new file mode 100644 index 0000000000000000000000000000000000000000..44cd870b0bbe539c0d37376c0006f03325f13e24 GIT binary patch literal 193 zcmV;y06za8iwFP!0000212vC34}&lag!ldmE1hv34m4DRp);)PRo_xtP(cLo>ze@G z?(?0`a?y_unBMNwTlO4=?#Q|K9z9|EeQ+g&;32_aeBJ!1oh%a|fDULt6K1R|y7Taa zt7Ww`n1gVM28C2rE_o-SHkGRDgyK2+|0?+AM<`2YX_KeSc1 literal 0 HcmV?d00001 diff --git a/core/web/assets/ba8bbf16ebf8e1d05bef.svg b/core/web/assets/ba8bbf16ebf8e1d05bef.svg new file mode 100644 index 00000000000..3ca546a8aaa --- /dev/null +++ b/core/web/assets/ba8bbf16ebf8e1d05bef.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz b/core/web/assets/ba8bbf16ebf8e1d05bef.svg.gz new file mode 100644 index 0000000000000000000000000000000000000000..7e4f235f1b9263b49c03bad9b2924a6355913d21 GIT binary patch literal 1739 zcmV;+1~mB}iwFP!000021I1WNZxlxm{wpiz+S8t@eiMU4eDT3KA^G4-G+7&LX$*+9 z)}Sc=J>OS7yBI7M!Xc4GZ%x0d>Z@mSve~{_-CjLEI-fpWfA~X=R#(&gG`yYeu9vwL z=04udZ#U16Ztm~jKRZ5tczEy+!N2?P=2*vZJjNAAtL^Re!*B0CK0g{)<4Q2S!kQn` z(TkI->(`qXC!70E^YshAo;QR1_4;<6pZ%g!ny#i_|2{eHizml@>;3ILzFvN~|MT7S z;c6u($9?AH{d9k`TF=u4!q`O+_xaK9ItHJLYt6^J9tRzLs7{jCG6eNGxgb6*B&KU~ zqtoO=aLtG8w0cPnr?kz3j4ohuJ&3PqC_Xg|xegL+#MV6xHtLKW?#0f9Qt+wZAkp{% zgv_}pWKrTmV?J?H#<}c^!_8TFbIEhVckoh9HRHzYL?KLp$l@h90ap#lOYS622I*wS zL8SQ_-D6ZPkB+~MUR7UsEu=fsqCQ>Z?5o^BL=6{>yw~cR3=pnC(NczF2rCipB(yP| z6GSIyosx3VuMO~ph>sCQsW1_K+MqtPq4^Ys>TBp!@;L(2N{OP^2&xIpROFBfAp~3x zDY75yECS=@3YlXWd`QF+=Au+a1wX7m^gF-5$)VLH!=rn4F2dV|POI)PNMoKshN^cVZZ*bL2f0wa%Nc#nJmv=a#g1buF z0nzNxFczoASz@J883@v~)d4hd%eSCubLFkjT zA4)+#gL}$dEL`7asZOJ>XDK6d>3K>BHMlwA2LVOK78qO6vlJ0p`Lm$_4YER*tu9D` zcxS015D&4=5y{or6>FS}3`SJE%EG}^*J+f$lwakPsUE9EKL%9+$OASnr72z*+IcD^R<*~`GP%>Ba&DsoL-Akl;P}{0z zTI3FdQAJwT8`Qf7=coZWU#w1%&zKqhjR`rIg;XxVM;akvo$XYw$s^Zmg))6ARZ6O| z(9Ee!up>JI;1lWoU;>K^^Lp0HJNp?D$F(M8Cj4ZtGmj+S1D zRS^KFk@!WC8BLhhyP9rob~Q~Gx|bq)gtHRfu?vAgOPl$&t7gg;NAp=>ai|kDHFcMA z2(p;|D(jBB^=d<~N;g%TRJxW*`njxA;{|3 z=obwRQ6@O3?oP8sgbrvE3s~=s9O)*DCE*~lTHu7Pr#n`pE#vmgX19o2 z_&*;m)ud#yVTnOsM{2T;!}YOaZ9xJ%ZV}rEzHIv0*r7Jb<+FD~x;C(cFC@o{W<|^P zEP0sS-#YfEK<>WPq*K82qHqbx`miTAvqi)E2P#=YfkQ-lH!<%jW7y~oKk!6o#>q&V zM_5PO2-n(Am-31iH)Q?&{YW26N?2RHS3WImfRxNQro!CFzLrLOhYxS_1|QeSY03Y876$sE+efF5u9-35Us|h z8F|1ZImaS9c@{oA+;Zm!FVcBnQt3N($9Yv?G&p#K1oKR!s1a==BGB0**r8F0ZT6!j z4|EX5rzaJxip4%sQ9P$tp~piZkonOxg4Operator UIChainlink
\ No newline at end of file diff --git a/core/web/assets/index.html.gz b/core/web/assets/index.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..24cc3068f6e66871c3c3479164b48ea3e892d6ee GIT binary patch literal 420 zcmV;V0bBkbiwFP!000021C^3bZ`3dl#lMQN#EEVk=(bC%I6YKCDiT7X^uTdqPc{bs zi9EAu_uJzn+m(=z5QjMNi{I~!$8O%(WcWCu7&!R0IgJRmZ2~d~Ge9O}EuX%B+I*Di zBu5CS<>c^rOqr!HDKf^g?Aci!w8hC8+$@s|7acqB8#3Tgzn>ZG*kk*3#0;FWczS5m zDmC84Um~N|l7>Py2Ntftr5G~yS`N%3-6}-^%Fhy-!Eoim-n~>2S(S2KoEcSd-NAvA zHYto5iQay=?^6!Ia{)`tpUA$@sM@Er_Xwk-su-0ay6Yi0f7IVnmpI*C*7e1&5Ak zJ+@6uC;3M@h=^zfCxH<x2 literal 0 HcmV?d00001 diff --git a/core/web/assets/main.b0b6f79f7f4a94560e37.js b/core/web/assets/main.b0b6f79f7f4a94560e37.js new file mode 100644 index 00000000000..6c9f23d1cca --- /dev/null +++ b/core/web/assets/main.b0b6f79f7f4a94560e37.js @@ -0,0 +1,187 @@ +(()=>{var __webpack_modules__={23564(e,t,n){"use strict";n.d(t,{Jh:()=>u,ZT:()=>i,_T:()=>o,ev:()=>c,mG:()=>s,pi:()=>a});/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&Object.prototype.propertyIsEnumerable.call(e,r[i])&&(n[r[i]]=e[r[i]]);return n}function s(e,t,n,r){function i(e){return e instanceof n?e:new n(function(t){t(e)})}return new(n||(n=Promise))(function(n,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?n(e.value):i(e.value).then(o,s)}u((r=r.apply(e,t||[])).next())})}function u(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return u([e,t])}}function u(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}e.exports=i,e.exports.default=e.exports,e.exports.__esModule=!0},37316(e){function t(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}e.exports=t,e.exports.default=e.exports,e.exports.__esModule=!0},78585(e,t,n){var r=n(50008).default,i=n(81506);function a(e,t){return t&&("object"===r(t)||"function"==typeof t)?t:i(e)}e.exports=a,e.exports.default=e.exports,e.exports.__esModule=!0},99489(e){function t(n,r){return e.exports=t=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},e.exports.default=e.exports,e.exports.__esModule=!0,t(n,r)}e.exports=t,e.exports.default=e.exports,e.exports.__esModule=!0},319(e,t,n){var r=n(23646),i=n(46860),a=n(60379),o=n(98206);function s(e){return r(e)||i(e)||a(e)||o()}e.exports=s,e.exports.default=e.exports,e.exports.__esModule=!0},50008(e){function t(n){return"function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?(e.exports=t=function(e){return typeof e},e.exports.default=e.exports,e.exports.__esModule=!0):(e.exports=t=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.default=e.exports,e.exports.__esModule=!0),t(n)}e.exports=t,e.exports.default=e.exports,e.exports.__esModule=!0},60379(e,t,n){var r=n(67228);function i(e,t){if(e){if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}}e.exports=i,e.exports.default=e.exports,e.exports.__esModule=!0},98925(e,t,n){"use strict";let r=n(98633),i=n.g.Date;class a extends i{constructor(e){super(e),this.isDate=!0}toISOString(){return`${this.getUTCFullYear()}-${r(2,this.getUTCMonth()+1)}-${r(2,this.getUTCDate())}`}}e.exports=e=>{let t=new a(e);if(!isNaN(t))return t;throw TypeError("Invalid Datetime")}},86595(e,t,n){"use strict";let r=n(98633);class i extends Date{constructor(e){super(e+"Z"),this.isFloating=!0}toISOString(){let e=`${this.getUTCFullYear()}-${r(2,this.getUTCMonth()+1)}-${r(2,this.getUTCDate())}`,t=`${r(2,this.getUTCHours())}:${r(2,this.getUTCMinutes())}:${r(2,this.getUTCSeconds())}.${r(3,this.getUTCMilliseconds())}`;return`${e}T${t}`}}e.exports=e=>{let t=new i(e);if(!isNaN(t))return t;throw TypeError("Invalid Datetime")}},76114(e){"use strict";e.exports=e=>{let t=new Date(e);if(!isNaN(t))return t;throw TypeError("Invalid Datetime")}},99439(e,t,n){"use strict";let r=n(98633);class i extends Date{constructor(e){super(`0000-01-01T${e}Z`),this.isTime=!0}toISOString(){return`${r(2,this.getUTCHours())}:${r(2,this.getUTCMinutes())}:${r(2,this.getUTCSeconds())}.${r(3,this.getUTCMilliseconds())}`}}e.exports=e=>{let t=new i(e);if(!isNaN(t))return t;throw TypeError("Invalid Datetime")}},98633(e){"use strict";e.exports=(e,t)=>{for(t=String(t);t.length{let t=new TomlError(e.message);return t.code=e.code,t.wrapped=e,t},module.exports.TomlError=TomlError;let createDateTime=__webpack_require__(76114),createDateTimeFloat=__webpack_require__(86595),createDate=__webpack_require__(98925),createTime=__webpack_require__(99439),CTRL_I=9,CTRL_J=10,CTRL_M=13,CTRL_CHAR_BOUNDARY=31,CHAR_SP=32,CHAR_QUOT=34,CHAR_NUM=35,CHAR_APOS=39,CHAR_PLUS=43,CHAR_COMMA=44,CHAR_HYPHEN=45,CHAR_PERIOD=46,CHAR_0=48,CHAR_1=49,CHAR_7=55,CHAR_9=57,CHAR_COLON=58,CHAR_EQUALS=61,CHAR_A=65,CHAR_E=69,CHAR_F=70,CHAR_T=84,CHAR_U=85,CHAR_Z=90,CHAR_LOWBAR=95,CHAR_a=97,CHAR_b=98,CHAR_e=101,CHAR_f=102,CHAR_i=105,CHAR_l=108,CHAR_n=110,CHAR_o=111,CHAR_r=114,CHAR_s=115,CHAR_t=116,CHAR_u=117,CHAR_x=120,CHAR_z=122,CHAR_LCUB=123,CHAR_RCUB=125,CHAR_LSQB=91,CHAR_BSOL=92,CHAR_RSQB=93,CHAR_DEL=127,SURROGATE_FIRST=55296,SURROGATE_LAST=57343,escapes={[CHAR_b]:"\b",[CHAR_t]:" ",[CHAR_n]:"\n",[CHAR_f]:"\f",[CHAR_r]:"\r",[CHAR_QUOT]:'"',[CHAR_BSOL]:"\\"};function isDigit(e){return e>=CHAR_0&&e<=CHAR_9}function isHexit(e){return e>=CHAR_A&&e<=CHAR_F||e>=CHAR_a&&e<=CHAR_f||e>=CHAR_0&&e<=CHAR_9}function isBit(e){return e===CHAR_1||e===CHAR_0}function isOctit(e){return e>=CHAR_0&&e<=CHAR_7}function isAlphaNumQuoteHyphen(e){return e>=CHAR_A&&e<=CHAR_Z||e>=CHAR_a&&e<=CHAR_z||e>=CHAR_0&&e<=CHAR_9||e===CHAR_APOS||e===CHAR_QUOT||e===CHAR_LOWBAR||e===CHAR_HYPHEN}function isAlphaNumHyphen(e){return e>=CHAR_A&&e<=CHAR_Z||e>=CHAR_a&&e<=CHAR_z||e>=CHAR_0&&e<=CHAR_9||e===CHAR_LOWBAR||e===CHAR_HYPHEN}let _type=Symbol("type"),_declared=Symbol("declared"),hasOwnProperty=Object.prototype.hasOwnProperty,defineProperty=Object.defineProperty,descriptor={configurable:!0,enumerable:!0,writable:!0,value:void 0};function hasKey(e,t){return!!hasOwnProperty.call(e,t)||("__proto__"===t&&defineProperty(e,"__proto__",descriptor),!1)}let INLINE_TABLE=Symbol("inline-table");function InlineTable(){return Object.defineProperties({},{[_type]:{value:INLINE_TABLE}})}function isInlineTable(e){return null!==e&&"object"==typeof e&&e[_type]===INLINE_TABLE}let TABLE=Symbol("table");function Table(){return Object.defineProperties({},{[_type]:{value:TABLE},[_declared]:{value:!1,writable:!0}})}function isTable(e){return null!==e&&"object"==typeof e&&e[_type]===TABLE}let _contentType=Symbol("content-type"),INLINE_LIST=Symbol("inline-list");function InlineList(e){return Object.defineProperties([],{[_type]:{value:INLINE_LIST},[_contentType]:{value:e}})}function isInlineList(e){return null!==e&&"object"==typeof e&&e[_type]===INLINE_LIST}let LIST=Symbol("list");function List(){return Object.defineProperties([],{[_type]:{value:LIST}})}function isList(e){return null!==e&&"object"==typeof e&&e[_type]===LIST}let _custom;try{let utilInspect=eval("require('util').inspect");_custom=utilInspect.custom}catch(_){}let _inspect=_custom||"inspect";class BoxedBigInt{constructor(e){try{this.value=__webpack_require__.g.BigInt.asIntN(64,e)}catch(t){this.value=null}Object.defineProperty(this,_type,{value:INTEGER})}isNaN(){return null===this.value}toString(){return String(this.value)}[_inspect](){return`[BigInt: ${this.toString()}]}`}valueOf(){return this.value}}let INTEGER=Symbol("integer");function Integer(e){let t=Number(e);return(Object.is(t,-0)&&(t=0),__webpack_require__.g.BigInt&&!Number.isSafeInteger(t))?new BoxedBigInt(e):Object.defineProperties(new Number(t),{isNaN:{value:function(){return isNaN(this)}},[_type]:{value:INTEGER},[_inspect]:{value:()=>`[Integer: ${e}]`}})}function isInteger(e){return null!==e&&"object"==typeof e&&e[_type]===INTEGER}let FLOAT=Symbol("float");function Float(e){return Object.defineProperties(new Number(e),{[_type]:{value:FLOAT},[_inspect]:{value:()=>`[Float: ${e}]`}})}function isFloat(e){return null!==e&&"object"==typeof e&&e[_type]===FLOAT}function tomlType(e){let t=typeof e;if("object"===t){if(null===e)return"null";if(e instanceof Date)return"datetime";if(_type in e)switch(e[_type]){case INLINE_TABLE:return"inline-table";case INLINE_LIST:return"inline-list";case TABLE:return"table";case LIST:return"list";case FLOAT:return"float";case INTEGER:return"integer"}}return t}function makeParserClass(e){class t extends e{constructor(){super(),this.ctx=this.obj=Table()}atEndOfWord(){return this.char===CHAR_NUM||this.char===CTRL_I||this.char===CHAR_SP||this.atEndOfLine()}atEndOfLine(){return this.char===e.END||this.char===CTRL_J||this.char===CTRL_M}parseStart(){if(this.char===e.END)return null;if(this.char===CHAR_LSQB)return this.call(this.parseTableOrList);if(this.char===CHAR_NUM)return this.call(this.parseComment);if(this.char===CTRL_J||this.char===CHAR_SP||this.char===CTRL_I||this.char===CTRL_M)return null;if(isAlphaNumQuoteHyphen(this.char))return this.callNow(this.parseAssignStatement);else throw this.error(new TomlError(`Unknown character "${this.char}"`))}parseWhitespaceToEOL(){if(this.char===CHAR_SP||this.char===CTRL_I||this.char===CTRL_M)return null;if(this.char===CHAR_NUM)return this.goto(this.parseComment);if(this.char===e.END||this.char===CTRL_J)return this.return();throw this.error(new TomlError("Unexpected character, expected only whitespace or comments till end of line"))}parseAssignStatement(){return this.callNow(this.parseAssign,this.recordAssignStatement)}recordAssignStatement(e){let t=this.ctx,n=e.key.pop();for(let r of e.key){if(hasKey(t,r)&&!isTable(t[r]))throw this.error(new TomlError("Can't redefine existing key"));t=t[r]=t[r]||Table()}if(hasKey(t,n))throw this.error(new TomlError("Can't redefine existing key"));return t[_declared]=!0,isInteger(e.value)||isFloat(e.value)?t[n]=e.value.valueOf():t[n]=e.value,this.goto(this.parseWhitespaceToEOL)}parseAssign(){return this.callNow(this.parseKeyword,this.recordAssignKeyword)}recordAssignKeyword(e){return this.state.resultTable?this.state.resultTable.push(e):this.state.resultTable=[e],this.goto(this.parseAssignKeywordPreDot)}parseAssignKeywordPreDot(){return this.char===CHAR_PERIOD?this.next(this.parseAssignKeywordPostDot):this.char!==CHAR_SP&&this.char!==CTRL_I?this.goto(this.parseAssignEqual):void 0}parseAssignKeywordPostDot(){if(this.char!==CHAR_SP&&this.char!==CTRL_I)return this.callNow(this.parseKeyword,this.recordAssignKeyword)}parseAssignEqual(){if(this.char===CHAR_EQUALS)return this.next(this.parseAssignPreValue);throw this.error(new TomlError('Invalid character, expected "="'))}parseAssignPreValue(){return this.char===CHAR_SP||this.char===CTRL_I?null:this.callNow(this.parseValue,this.recordAssignValue)}recordAssignValue(e){return this.returnNow({key:this.state.resultTable,value:e})}parseComment(){do{if(this.char===e.END||this.char===CTRL_J)return this.return();if(this.char===CHAR_DEL||this.char<=CTRL_CHAR_BOUNDARY&&this.char!==CTRL_I)throw this.errorControlCharIn("comments")}while(this.nextChar())}parseTableOrList(){if(this.char!==CHAR_LSQB)return this.goto(this.parseTable);this.next(this.parseList)}parseTable(){return this.ctx=this.obj,this.goto(this.parseTableNext)}parseTableNext(){return this.char===CHAR_SP||this.char===CTRL_I?null:this.callNow(this.parseKeyword,this.parseTableMore)}parseTableMore(e){if(this.char===CHAR_SP||this.char===CTRL_I)return null;if(this.char===CHAR_RSQB){if(hasKey(this.ctx,e)&&(!isTable(this.ctx[e])||this.ctx[e][_declared]))throw this.error(new TomlError("Can't redefine existing key"));return this.ctx=this.ctx[e]=this.ctx[e]||Table(),this.ctx[_declared]=!0,this.next(this.parseWhitespaceToEOL)}if(this.char===CHAR_PERIOD){if(hasKey(this.ctx,e)){if(isTable(this.ctx[e]))this.ctx=this.ctx[e];else if(isList(this.ctx[e]))this.ctx=this.ctx[e][this.ctx[e].length-1];else throw this.error(new TomlError("Can't redefine existing key"))}else this.ctx=this.ctx[e]=Table();return this.next(this.parseTableNext)}throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]"))}parseList(){return this.ctx=this.obj,this.goto(this.parseListNext)}parseListNext(){return this.char===CHAR_SP||this.char===CTRL_I?null:this.callNow(this.parseKeyword,this.parseListMore)}parseListMore(e){if(this.char===CHAR_SP||this.char===CTRL_I)return null;if(this.char===CHAR_RSQB){if(hasKey(this.ctx,e)||(this.ctx[e]=List()),isInlineList(this.ctx[e]))throw this.error(new TomlError("Can't extend an inline array"));if(isList(this.ctx[e])){let t=Table();this.ctx[e].push(t),this.ctx=t}else throw this.error(new TomlError("Can't redefine an existing key"));return this.next(this.parseListEnd)}if(this.char===CHAR_PERIOD){if(hasKey(this.ctx,e)){if(isInlineList(this.ctx[e]))throw this.error(new TomlError("Can't extend an inline array"));if(isInlineTable(this.ctx[e]))throw this.error(new TomlError("Can't extend an inline table"));else if(isList(this.ctx[e]))this.ctx=this.ctx[e][this.ctx[e].length-1];else if(isTable(this.ctx[e]))this.ctx=this.ctx[e];else throw this.error(new TomlError("Can't redefine an existing key"))}else this.ctx=this.ctx[e]=Table();return this.next(this.parseListNext)}throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]"))}parseListEnd(e){if(this.char===CHAR_RSQB)return this.next(this.parseWhitespaceToEOL);throw this.error(new TomlError("Unexpected character, expected whitespace, . or ]"))}parseValue(){if(this.char===e.END)throw this.error(new TomlError("Key without value"));if(this.char===CHAR_QUOT)return this.next(this.parseDoubleString);if(this.char===CHAR_APOS)return this.next(this.parseSingleString);if(this.char===CHAR_HYPHEN||this.char===CHAR_PLUS)return this.goto(this.parseNumberSign);if(this.char===CHAR_i)return this.next(this.parseInf);if(this.char===CHAR_n)return this.next(this.parseNan);if(isDigit(this.char))return this.goto(this.parseNumberOrDateTime);else if(this.char===CHAR_t||this.char===CHAR_f)return this.goto(this.parseBoolean);else if(this.char===CHAR_LSQB)return this.call(this.parseInlineList,this.recordValue);else if(this.char===CHAR_LCUB)return this.call(this.parseInlineTable,this.recordValue);else throw this.error(new TomlError("Unexpected character, expecting string, number, datetime, boolean, inline array or inline table"))}recordValue(e){return this.returnNow(e)}parseInf(){if(this.char===CHAR_n)return this.next(this.parseInf2);throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"'))}parseInf2(){if(this.char===CHAR_f)return"-"===this.state.buf?this.return(-1/0):this.return(1/0);throw this.error(new TomlError('Unexpected character, expected "inf", "+inf" or "-inf"'))}parseNan(){if(this.char===CHAR_a)return this.next(this.parseNan2);throw this.error(new TomlError('Unexpected character, expected "nan"'))}parseNan2(){if(this.char===CHAR_n)return this.return(NaN);throw this.error(new TomlError('Unexpected character, expected "nan"'))}parseKeyword(){return this.char===CHAR_QUOT?this.next(this.parseBasicString):this.char===CHAR_APOS?this.next(this.parseLiteralString):this.goto(this.parseBareKey)}parseBareKey(){do{if(this.char===e.END)throw this.error(new TomlError("Key ended without value"));if(isAlphaNumHyphen(this.char))this.consume();else if(0!==this.state.buf.length)return this.returnNow();else throw this.error(new TomlError("Empty bare keys are not allowed"))}while(this.nextChar())}parseSingleString(){return this.char===CHAR_APOS?this.next(this.parseLiteralMultiStringMaybe):this.goto(this.parseLiteralString)}parseLiteralString(){do{if(this.char===CHAR_APOS)return this.return();if(this.atEndOfLine())throw this.error(new TomlError("Unterminated string"));if(this.char===CHAR_DEL||this.char<=CTRL_CHAR_BOUNDARY&&this.char!==CTRL_I)throw this.errorControlCharIn("strings");else this.consume()}while(this.nextChar())}parseLiteralMultiStringMaybe(){return this.char===CHAR_APOS?this.next(this.parseLiteralMultiString):this.returnNow()}parseLiteralMultiString(){return this.char===CTRL_M?null:this.char===CTRL_J?this.next(this.parseLiteralMultiStringContent):this.goto(this.parseLiteralMultiStringContent)}parseLiteralMultiStringContent(){do{if(this.char===CHAR_APOS)return this.next(this.parseLiteralMultiEnd);if(this.char===e.END)throw this.error(new TomlError("Unterminated multi-line string"));if(this.char===CHAR_DEL||this.char<=CTRL_CHAR_BOUNDARY&&this.char!==CTRL_I&&this.char!==CTRL_J&&this.char!==CTRL_M)throw this.errorControlCharIn("strings");else this.consume()}while(this.nextChar())}parseLiteralMultiEnd(){return this.char===CHAR_APOS?this.next(this.parseLiteralMultiEnd2):(this.state.buf+="'",this.goto(this.parseLiteralMultiStringContent))}parseLiteralMultiEnd2(){return this.char===CHAR_APOS?this.next(this.parseLiteralMultiEnd3):(this.state.buf+="''",this.goto(this.parseLiteralMultiStringContent))}parseLiteralMultiEnd3(){return this.char===CHAR_APOS?(this.state.buf+="'",this.next(this.parseLiteralMultiEnd4)):this.returnNow()}parseLiteralMultiEnd4(){return this.char===CHAR_APOS?(this.state.buf+="'",this.return()):this.returnNow()}parseDoubleString(){return this.char===CHAR_QUOT?this.next(this.parseMultiStringMaybe):this.goto(this.parseBasicString)}parseBasicString(){do{if(this.char===CHAR_BSOL)return this.call(this.parseEscape,this.recordEscapeReplacement);if(this.char===CHAR_QUOT)return this.return();if(this.atEndOfLine())throw this.error(new TomlError("Unterminated string"));else if(this.char===CHAR_DEL||this.char<=CTRL_CHAR_BOUNDARY&&this.char!==CTRL_I)throw this.errorControlCharIn("strings");else this.consume()}while(this.nextChar())}recordEscapeReplacement(e){return this.state.buf+=e,this.goto(this.parseBasicString)}parseMultiStringMaybe(){return this.char===CHAR_QUOT?this.next(this.parseMultiString):this.returnNow()}parseMultiString(){return this.char===CTRL_M?null:this.char===CTRL_J?this.next(this.parseMultiStringContent):this.goto(this.parseMultiStringContent)}parseMultiStringContent(){do{if(this.char===CHAR_BSOL)return this.call(this.parseMultiEscape,this.recordMultiEscapeReplacement);if(this.char===CHAR_QUOT)return this.next(this.parseMultiEnd);if(this.char===e.END)throw this.error(new TomlError("Unterminated multi-line string"));else if(this.char===CHAR_DEL||this.char<=CTRL_CHAR_BOUNDARY&&this.char!==CTRL_I&&this.char!==CTRL_J&&this.char!==CTRL_M)throw this.errorControlCharIn("strings");else this.consume()}while(this.nextChar())}errorControlCharIn(e){let t="\\u00";return this.char<16&&(t+="0"),t+=this.char.toString(16),this.error(new TomlError(`Control characters (codes < 0x1f and 0x7f) are not allowed in ${e}, use ${t} instead`))}recordMultiEscapeReplacement(e){return this.state.buf+=e,this.goto(this.parseMultiStringContent)}parseMultiEnd(){return this.char===CHAR_QUOT?this.next(this.parseMultiEnd2):(this.state.buf+='"',this.goto(this.parseMultiStringContent))}parseMultiEnd2(){return this.char===CHAR_QUOT?this.next(this.parseMultiEnd3):(this.state.buf+='""',this.goto(this.parseMultiStringContent))}parseMultiEnd3(){return this.char===CHAR_QUOT?(this.state.buf+='"',this.next(this.parseMultiEnd4)):this.returnNow()}parseMultiEnd4(){return this.char===CHAR_QUOT?(this.state.buf+='"',this.return()):this.returnNow()}parseMultiEscape(){return this.char===CTRL_M||this.char===CTRL_J?this.next(this.parseMultiTrim):this.char===CHAR_SP||this.char===CTRL_I?this.next(this.parsePreMultiTrim):this.goto(this.parseEscape)}parsePreMultiTrim(){if(this.char===CHAR_SP||this.char===CTRL_I)return null;if(this.char===CTRL_M||this.char===CTRL_J)return this.next(this.parseMultiTrim);throw this.error(new TomlError("Can't escape whitespace"))}parseMultiTrim(){return this.char===CTRL_J||this.char===CHAR_SP||this.char===CTRL_I||this.char===CTRL_M?null:this.returnNow()}parseEscape(){if(this.char in escapes)return this.return(escapes[this.char]);if(this.char===CHAR_u)return this.call(this.parseSmallUnicode,this.parseUnicodeReturn);if(this.char===CHAR_U)return this.call(this.parseLargeUnicode,this.parseUnicodeReturn);throw this.error(new TomlError("Unknown escape character: "+this.char))}parseUnicodeReturn(e){try{let t=parseInt(e,16);if(t>=SURROGATE_FIRST&&t<=SURROGATE_LAST)throw this.error(new TomlError("Invalid unicode, character in range 0xD800 - 0xDFFF is reserved"));return this.returnNow(String.fromCodePoint(t))}catch(n){throw this.error(TomlError.wrap(n))}}parseSmallUnicode(){if(isHexit(this.char)){if(this.consume(),this.state.buf.length>=4)return this.return()}else throw this.error(new TomlError("Invalid character in unicode sequence, expected hex"))}parseLargeUnicode(){if(isHexit(this.char)){if(this.consume(),this.state.buf.length>=8)return this.return()}else throw this.error(new TomlError("Invalid character in unicode sequence, expected hex"))}parseNumberSign(){return this.consume(),this.next(this.parseMaybeSignedInfOrNan)}parseMaybeSignedInfOrNan(){return this.char===CHAR_i?this.next(this.parseInf):this.char===CHAR_n?this.next(this.parseNan):this.callNow(this.parseNoUnder,this.parseNumberIntegerStart)}parseNumberIntegerStart(){return this.char===CHAR_0?(this.consume(),this.next(this.parseNumberIntegerExponentOrDecimal)):this.goto(this.parseNumberInteger)}parseNumberIntegerExponentOrDecimal(){return this.char===CHAR_PERIOD?(this.consume(),this.call(this.parseNoUnder,this.parseNumberFloat)):this.char===CHAR_E||this.char===CHAR_e?(this.consume(),this.next(this.parseNumberExponentSign)):this.returnNow(Integer(this.state.buf))}parseNumberInteger(){if(isDigit(this.char))this.consume();else{if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnder);if(this.char===CHAR_E||this.char===CHAR_e)return this.consume(),this.next(this.parseNumberExponentSign);if(this.char===CHAR_PERIOD)return this.consume(),this.call(this.parseNoUnder,this.parseNumberFloat);let e=Integer(this.state.buf);if(!e.isNaN())return this.returnNow(e);throw this.error(new TomlError("Invalid number"))}}parseNoUnder(){if(this.char===CHAR_LOWBAR||this.char===CHAR_PERIOD||this.char===CHAR_E||this.char===CHAR_e)throw this.error(new TomlError("Unexpected character, expected digit"));if(this.atEndOfWord())throw this.error(new TomlError("Incomplete number"));return this.returnNow()}parseNoUnderHexOctBinLiteral(){if(this.char===CHAR_LOWBAR||this.char===CHAR_PERIOD)throw this.error(new TomlError("Unexpected character, expected digit"));if(this.atEndOfWord())throw this.error(new TomlError("Incomplete number"));return this.returnNow()}parseNumberFloat(){if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnder,this.parseNumberFloat);if(isDigit(this.char))this.consume();else if(this.char===CHAR_E||this.char===CHAR_e)return this.consume(),this.next(this.parseNumberExponentSign);else return this.returnNow(Float(this.state.buf))}parseNumberExponentSign(){if(isDigit(this.char))return this.goto(this.parseNumberExponent);if(this.char===CHAR_HYPHEN||this.char===CHAR_PLUS)this.consume(),this.call(this.parseNoUnder,this.parseNumberExponent);else throw this.error(new TomlError("Unexpected character, expected -, + or digit"))}parseNumberExponent(){if(isDigit(this.char))this.consume();else if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnder);else return this.returnNow(Float(this.state.buf))}parseNumberOrDateTime(){return this.char===CHAR_0?(this.consume(),this.next(this.parseNumberBaseOrDateTime)):this.goto(this.parseNumberOrDateTimeOnly)}parseNumberOrDateTimeOnly(){if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnder,this.parseNumberInteger);if(isDigit(this.char))this.consume(),this.state.buf.length>4&&this.next(this.parseNumberInteger);else if(this.char===CHAR_E||this.char===CHAR_e)return this.consume(),this.next(this.parseNumberExponentSign);else if(this.char===CHAR_PERIOD)return this.consume(),this.call(this.parseNoUnder,this.parseNumberFloat);else if(this.char===CHAR_HYPHEN)return this.goto(this.parseDateTime);else if(this.char===CHAR_COLON)return this.goto(this.parseOnlyTimeHour);else return this.returnNow(Integer(this.state.buf))}parseDateTimeOnly(){if(this.state.buf.length<4){if(isDigit(this.char))return this.consume();if(this.char===CHAR_COLON)return this.goto(this.parseOnlyTimeHour);throw this.error(new TomlError("Expected digit while parsing year part of a date"))}if(this.char===CHAR_HYPHEN)return this.goto(this.parseDateTime);throw this.error(new TomlError("Expected hyphen (-) while parsing year part of date"))}parseNumberBaseOrDateTime(){if(this.char===CHAR_b)return this.consume(),this.call(this.parseNoUnderHexOctBinLiteral,this.parseIntegerBin);if(this.char===CHAR_o)return this.consume(),this.call(this.parseNoUnderHexOctBinLiteral,this.parseIntegerOct);if(this.char===CHAR_x)return this.consume(),this.call(this.parseNoUnderHexOctBinLiteral,this.parseIntegerHex);if(this.char===CHAR_PERIOD)return this.goto(this.parseNumberInteger);if(isDigit(this.char))return this.goto(this.parseDateTimeOnly);else return this.returnNow(Integer(this.state.buf))}parseIntegerHex(){if(isHexit(this.char))this.consume();else{if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnderHexOctBinLiteral);let e=Integer(this.state.buf);if(!e.isNaN())return this.returnNow(e);throw this.error(new TomlError("Invalid number"))}}parseIntegerOct(){if(isOctit(this.char))this.consume();else{if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnderHexOctBinLiteral);let e=Integer(this.state.buf);if(!e.isNaN())return this.returnNow(e);throw this.error(new TomlError("Invalid number"))}}parseIntegerBin(){if(isBit(this.char))this.consume();else{if(this.char===CHAR_LOWBAR)return this.call(this.parseNoUnderHexOctBinLiteral);let e=Integer(this.state.buf);if(!e.isNaN())return this.returnNow(e);throw this.error(new TomlError("Invalid number"))}}parseDateTime(){if(this.state.buf.length<4)throw this.error(new TomlError("Years less than 1000 must be zero padded to four characters"));return this.state.result=this.state.buf,this.state.buf="",this.next(this.parseDateMonth)}parseDateMonth(){if(this.char===CHAR_HYPHEN){if(this.state.buf.length<2)throw this.error(new TomlError("Months less than 10 must be zero padded to two characters"));return this.state.result+="-"+this.state.buf,this.state.buf="",this.next(this.parseDateDay)}if(isDigit(this.char))this.consume();else throw this.error(new TomlError("Incomplete datetime"))}parseDateDay(){if(this.char===CHAR_T||this.char===CHAR_SP){if(this.state.buf.length<2)throw this.error(new TomlError("Days less than 10 must be zero padded to two characters"));return this.state.result+="-"+this.state.buf,this.state.buf="",this.next(this.parseStartTimeHour)}if(this.atEndOfWord())return this.returnNow(createDate(this.state.result+"-"+this.state.buf));if(isDigit(this.char))this.consume();else throw this.error(new TomlError("Incomplete datetime"))}parseStartTimeHour(){return this.atEndOfWord()?this.returnNow(createDate(this.state.result)):this.goto(this.parseTimeHour)}parseTimeHour(){if(this.char===CHAR_COLON){if(this.state.buf.length<2)throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters"));return this.state.result+="T"+this.state.buf,this.state.buf="",this.next(this.parseTimeMin)}if(isDigit(this.char))this.consume();else throw this.error(new TomlError("Incomplete datetime"))}parseTimeMin(){if(this.state.buf.length<2&&isDigit(this.char))this.consume();else if(2===this.state.buf.length&&this.char===CHAR_COLON)return this.state.result+=":"+this.state.buf,this.state.buf="",this.next(this.parseTimeSec);else throw this.error(new TomlError("Incomplete datetime"))}parseTimeSec(){if(isDigit(this.char)){if(this.consume(),2===this.state.buf.length)return this.state.result+=":"+this.state.buf,this.state.buf="",this.next(this.parseTimeZoneOrFraction)}else throw this.error(new TomlError("Incomplete datetime"))}parseOnlyTimeHour(){if(this.char===CHAR_COLON){if(this.state.buf.length<2)throw this.error(new TomlError("Hours less than 10 must be zero padded to two characters"));return this.state.result=this.state.buf,this.state.buf="",this.next(this.parseOnlyTimeMin)}throw this.error(new TomlError("Incomplete time"))}parseOnlyTimeMin(){if(this.state.buf.length<2&&isDigit(this.char))this.consume();else if(2===this.state.buf.length&&this.char===CHAR_COLON)return this.state.result+=":"+this.state.buf,this.state.buf="",this.next(this.parseOnlyTimeSec);else throw this.error(new TomlError("Incomplete time"))}parseOnlyTimeSec(){if(isDigit(this.char)){if(this.consume(),2===this.state.buf.length)return this.next(this.parseOnlyTimeFractionMaybe)}else throw this.error(new TomlError("Incomplete time"))}parseOnlyTimeFractionMaybe(){if(this.state.result+=":"+this.state.buf,this.char!==CHAR_PERIOD)return this.return(createTime(this.state.result));this.state.buf="",this.next(this.parseOnlyTimeFraction)}parseOnlyTimeFraction(){if(isDigit(this.char))this.consume();else if(this.atEndOfWord()){if(0===this.state.buf.length)throw this.error(new TomlError("Expected digit in milliseconds"));return this.returnNow(createTime(this.state.result+"."+this.state.buf))}else throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z"))}parseTimeZoneOrFraction(){if(this.char===CHAR_PERIOD)this.consume(),this.next(this.parseDateTimeFraction);else if(this.char===CHAR_HYPHEN||this.char===CHAR_PLUS)this.consume(),this.next(this.parseTimeZoneHour);else if(this.char===CHAR_Z)return this.consume(),this.return(createDateTime(this.state.result+this.state.buf));else if(this.atEndOfWord())return this.returnNow(createDateTimeFloat(this.state.result+this.state.buf));else throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z"))}parseDateTimeFraction(){if(isDigit(this.char))this.consume();else if(1===this.state.buf.length)throw this.error(new TomlError("Expected digit in milliseconds"));else if(this.char===CHAR_HYPHEN||this.char===CHAR_PLUS)this.consume(),this.next(this.parseTimeZoneHour);else if(this.char===CHAR_Z)return this.consume(),this.return(createDateTime(this.state.result+this.state.buf));else if(this.atEndOfWord())return this.returnNow(createDateTimeFloat(this.state.result+this.state.buf));else throw this.error(new TomlError("Unexpected character in datetime, expected period (.), minus (-), plus (+) or Z"))}parseTimeZoneHour(){if(isDigit(this.char)){if(this.consume(),/\d\d$/.test(this.state.buf))return this.next(this.parseTimeZoneSep)}else throw this.error(new TomlError("Unexpected character in datetime, expected digit"))}parseTimeZoneSep(){if(this.char===CHAR_COLON)this.consume(),this.next(this.parseTimeZoneMin);else throw this.error(new TomlError("Unexpected character in datetime, expected colon"))}parseTimeZoneMin(){if(isDigit(this.char)){if(this.consume(),/\d\d$/.test(this.state.buf))return this.return(createDateTime(this.state.result+this.state.buf))}else throw this.error(new TomlError("Unexpected character in datetime, expected digit"))}parseBoolean(){return this.char===CHAR_t?(this.consume(),this.next(this.parseTrue_r)):this.char===CHAR_f?(this.consume(),this.next(this.parseFalse_a)):void 0}parseTrue_r(){if(this.char===CHAR_r)return this.consume(),this.next(this.parseTrue_u);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseTrue_u(){if(this.char===CHAR_u)return this.consume(),this.next(this.parseTrue_e);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseTrue_e(){if(this.char===CHAR_e)return this.return(!0);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseFalse_a(){if(this.char===CHAR_a)return this.consume(),this.next(this.parseFalse_l);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseFalse_l(){if(this.char===CHAR_l)return this.consume(),this.next(this.parseFalse_s);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseFalse_s(){if(this.char===CHAR_s)return this.consume(),this.next(this.parseFalse_e);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseFalse_e(){if(this.char===CHAR_e)return this.return(!1);throw this.error(new TomlError("Invalid boolean, expected true or false"))}parseInlineList(){if(this.char===CHAR_SP||this.char===CTRL_I||this.char===CTRL_M||this.char===CTRL_J)return null;if(this.char===e.END)throw this.error(new TomlError("Unterminated inline array"));return this.char===CHAR_NUM?this.call(this.parseComment):this.char===CHAR_RSQB?this.return(this.state.resultArr||InlineList()):this.callNow(this.parseValue,this.recordInlineListValue)}recordInlineListValue(e){return this.state.resultArr||(this.state.resultArr=InlineList(tomlType(e))),isFloat(e)||isInteger(e)?this.state.resultArr.push(e.valueOf()):this.state.resultArr.push(e),this.goto(this.parseInlineListNext)}parseInlineListNext(){if(this.char===CHAR_SP||this.char===CTRL_I||this.char===CTRL_M||this.char===CTRL_J)return null;if(this.char===CHAR_NUM)return this.call(this.parseComment);if(this.char===CHAR_COMMA)return this.next(this.parseInlineList);if(this.char===CHAR_RSQB)return this.goto(this.parseInlineList);throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])"))}parseInlineTable(){if(this.char===CHAR_SP||this.char===CTRL_I)return null;if(this.char===e.END||this.char===CHAR_NUM||this.char===CTRL_J||this.char===CTRL_M)throw this.error(new TomlError("Unterminated inline array"));return this.char===CHAR_RCUB?this.return(this.state.resultTable||InlineTable()):(this.state.resultTable||(this.state.resultTable=InlineTable()),this.callNow(this.parseAssign,this.recordInlineTableValue))}recordInlineTableValue(e){let t=this.state.resultTable,n=e.key.pop();for(let r of e.key){if(hasKey(t,r)&&(!isTable(t[r])||t[r][_declared]))throw this.error(new TomlError("Can't redefine existing key"));t=t[r]=t[r]||Table()}if(hasKey(t,n))throw this.error(new TomlError("Can't redefine existing key"));return isInteger(e.value)||isFloat(e.value)?t[n]=e.value.valueOf():t[n]=e.value,this.goto(this.parseInlineTableNext)}parseInlineTableNext(){if(this.char===CHAR_SP||this.char===CTRL_I)return null;if(this.char===e.END||this.char===CHAR_NUM||this.char===CTRL_J||this.char===CTRL_M)throw this.error(new TomlError("Unterminated inline array"));if(this.char===CHAR_COMMA)return this.next(this.parseInlineTablePostComma);if(this.char===CHAR_RCUB)return this.goto(this.parseInlineTable);throw this.error(new TomlError("Invalid character, expected whitespace, comma (,) or close bracket (])"))}parseInlineTablePostComma(){if(this.char===CHAR_SP||this.char===CTRL_I)return null;if(this.char===e.END||this.char===CHAR_NUM||this.char===CTRL_J||this.char===CTRL_M)throw this.error(new TomlError("Unterminated inline array"));if(this.char===CHAR_COMMA)throw this.error(new TomlError("Empty elements in inline tables are not permitted"));if(this.char!==CHAR_RCUB)return this.goto(this.parseInlineTable);throw this.error(new TomlError("Trailing commas in inline tables are not permitted"))}}return t}},90560(e,t,n){"use strict";e.exports=a;let r=n(8676),i=n(22418);function a(e,t){t||(t={});let n=0,a=t.blocksize||40960,o=new r;return new Promise((e,t)=>{setImmediate(s,n,a,e,t)});function s(t,n,r,a){if(t>=e.length)try{return r(o.finish())}catch(u){return a(i(u,e))}try{o.parse(e.slice(t,t+n)),setImmediate(s,t+n,n,r,a)}catch(c){a(i(c,e))}}}},22418(e){"use strict";function t(e,t){if(null==e.pos||null==e.line)return e;let n=e.message;if(n+=` at row ${e.line+1}, col ${e.col+1}, pos ${e.pos}: +`,t&&t.split){let r=t.split(/\n/),i=String(Math.min(r.length,e.line+3)).length,a=" ";for(;a.length "+r[o]+"\n",n+=a+" ";for(let u=0;u{let i,a=!1,o=!1;function s(){if(a=!0,!i)try{n(t.finish())}catch(e){r(e)}}function u(e){o=!0,r(e)}function c(){i=!0;let n;for(;null!==(n=e.read());)try{t.parse(n)}catch(r){return u(r)}if(i=!1,a)return s();o||e.once("readable",c)}e.once("end",s),e.once("error",u),c()})}function s(){let e=new i;return new r.Transform({objectMode:!0,transform(t,n,r){try{e.parse(t.toString(n))}catch(i){this.emit("error",i)}r()},flush(t){try{this.push(e.finish())}catch(n){this.emit("error",n)}t()}})}},56530(e,t,n){"use strict";e.exports=a;let r=n(8676),i=n(22418);function a(e){n.g.Buffer&&n.g.Buffer.isBuffer(e)&&(e=e.toString("utf8"));let t=new r;try{return t.parse(e),t.finish()}catch(a){throw i(a,e)}}},83512(e,t,n){"use strict";e.exports=n(56530),e.exports.async=n(90560),e.exports.stream=n(6435),e.exports.prettyError=n(22418)},36921(e){"use strict";function t(e){if(null===e)throw n("null");if(void 0===e)throw n("undefined");if("object"!=typeof e)throw n(typeof e);if("function"==typeof e.toJSON&&(e=e.toJSON()),null==e)return null;let t=u(e);if("table"!==t)throw n(t);return o("","",e)}function n(e){return Error("Can only stringify objects, not "+e)}function r(e){return Object.keys(e).filter(t=>s(e[t]))}function i(e){return Object.keys(e).filter(t=>!s(e[t]))}function a(e){let t=Array.isArray(e)?[]:Object.prototype.hasOwnProperty.call(e,"__proto__")?{["__proto__"]:void 0}:{};for(let n of Object.keys(e))!e[n]||"function"!=typeof e[n].toJSON||"toISOString"in e[n]?t[n]=e[n]:t[n]=e[n].toJSON();return t}function o(e,t,n){let o,s;o=r(n=a(n)),s=i(n);let l=[],f=t||"";o.forEach(e=>{var t=u(n[e]);"undefined"!==t&&"null"!==t&&l.push(f+c(e)+" = "+b(n[e],!0))}),l.length>0&&l.push("");let d=e&&o.length>0?t+" ":"";return s.forEach(t=>{l.push(S(e,d,t,n[t]))}),l.join("\n")}function s(e){switch(u(e)){case"undefined":case"null":case"integer":case"nan":case"float":case"boolean":case"string":case"datetime":return!0;case"array":return 0===e.length||"table"!==u(e[0]);case"table":return 0===Object.keys(e).length;default:return!1}}function u(e){if(void 0===e)return"undefined";if(null===e)return"null";if("bigint"==typeof e||Number.isInteger(e)&&!Object.is(e,-0))return"integer";if("number"==typeof e)return"float";if("boolean"==typeof e)return"boolean";else if("string"==typeof e)return"string";else if("toISOString"in e)return isNaN(e)?"undefined":"datetime";else if(Array.isArray(e))return"array";else return"table"}function c(e){let t=String(e);return/^[-A-Za-z0-9_]+$/.test(t)?t:l(t)}function l(e){return'"'+h(e).replace(/"/g,'\\"')+'"'}function f(e){return"'"+e+"'"}function d(e,t){for(;t.length"\\u"+d(4,e.codePointAt(0).toString(16)))}function p(e){let t=e.split(/\n/).map(e=>h(e).replace(/"(?="")/g,'\\"')).join("\n");return'"'===t.slice(-1)&&(t+="\\\n"),'"""\n'+t+'"""'}function b(e,t){let n=u(e);return"string"===n&&(t&&/\n/.test(e)?n="string-multiline":!/[\b\t\n\f\r']/.test(e)&&/"/.test(e)&&(n="string-literal")),m(e,n)}function m(e,t){switch(t||(t=u(e)),t){case"string-multiline":return p(e);case"string":return l(e);case"string-literal":return f(e);case"integer":return g(e);case"float":return v(e);case"boolean":return y(e);case"datetime":return w(e);case"array":return _(e.filter(e=>"null"!==u(e)&&"undefined"!==u(e)&&"nan"!==u(e)));case"table":return E(e);default:throw n(t)}}function g(e){return String(e).replace(/\B(?=(\d{3})+(?!\d))/g,"_")}function v(e){if(e===1/0)return"inf";if(e===-1/0)return"-inf";if(Object.is(e,NaN))return"nan";if(Object.is(e,-0))return"-0.0";let[t,n]=String(e).split(".");return g(t)+"."+n}function y(e){return String(e)}function w(e){return e.toISOString()}function _(e){e=a(e);let t="[",n=e.map(e=>m(e));return n.join(", ").length>60||/\n/.test(n)?t+="\n "+n.join(",\n ")+"\n":t+=" "+n.join(", ")+(n.length>0?" ":""),t+"]"}function E(e){e=a(e);let t=[];return Object.keys(e).forEach(n=>{t.push(c(n)+" = "+b(e[n],!1))}),"{ "+t.join(", ")+(t.length>0?" ":"")+"}"}function S(e,t,r,i){let a=u(i);if("array"===a)return k(e,t,r,i);if("table"===a)return x(e,t,r,i);throw n(a)}function k(e,t,r,i){i=a(i);let s=u(i[0]);if("table"!==s)throw n(s);let l=e+c(r),f="";return i.forEach(e=>{f.length>0&&(f+="\n"),f+=t+"[["+l+"]]\n",f+=o(l+".",t,e)}),f}function x(e,t,n,i){let a=e+c(n),s="";return r(i).length>0&&(s+=t+"["+a+"]\n"),s+o(a+".",t,i)}e.exports=t,e.exports.value=m},5022(e,t,n){"use strict";t.parse=n(83512),t.stringify=n(36921)},46515(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184)),c=r(n(78252)),l=n(98741),f=r(n(68821)),d=function(e){var t="light"===e.palette.type?e.palette.grey[100]:e.palette.grey[900];return{root:{display:"flex",flexDirection:"column",width:"100%",boxSizing:"border-box",zIndex:e.zIndex.appBar,flexShrink:0},positionFixed:{position:"fixed",top:0,left:"auto",right:0},positionAbsolute:{position:"absolute",top:0,left:"auto",right:0},positionSticky:{position:"sticky",top:0,left:"auto",right:0},positionStatic:{position:"static"},positionRelative:{position:"relative"},colorDefault:{backgroundColor:t,color:e.palette.getContrastText(t)},colorPrimary:{backgroundColor:e.palette.primary.main,color:e.palette.primary.contrastText},colorSecondary:{backgroundColor:e.palette.secondary.main,color:e.palette.secondary.contrastText}}};function h(e){var t,n=e.children,r=e.classes,c=e.className,d=e.color,h=e.position,p=(0,o.default)(e,["children","classes","className","color","position"]),b=(0,u.default)(r.root,r["position".concat((0,l.capitalize)(h))],(t={},(0,a.default)(t,r["color".concat((0,l.capitalize)(d))],"inherit"!==d),(0,a.default)(t,"mui-fixed","fixed"===h),t),c);return s.default.createElement(f.default,(0,i.default)({square:!0,component:"header",elevation:4,className:b},p),n)}t.styles=d,h.defaultProps={color:"primary",position:"fixed"};var p=(0,c.default)(d,{name:"MuiAppBar"})(h);t.default=p},95880(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(46515))},68477(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(59713)),a=r(n(67154)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=function(e){return{root:{position:"relative",display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0,width:40,height:40,fontFamily:e.typography.fontFamily,fontSize:e.typography.pxToRem(20),borderRadius:"50%",overflow:"hidden",userSelect:"none"},colorDefault:{color:e.palette.background.default,backgroundColor:"light"===e.palette.type?e.palette.grey[400]:e.palette.grey[600]},img:{width:"100%",height:"100%",textAlign:"center",objectFit:"cover"}}};function f(e){var t=e.alt,n=e.children,r=e.childrenClassName,c=e.classes,l=e.className,f=e.component,d=e.imgProps,h=e.sizes,p=e.src,b=e.srcSet,m=(0,o.default)(e,["alt","children","childrenClassName","classes","className","component","imgProps","sizes","src","srcSet"]),g=null,v=p||b;return g=v?s.default.createElement("img",(0,a.default)({alt:t,src:p,srcSet:b,sizes:h,className:c.img},d)):r&&s.default.isValidElement(n)?s.default.cloneElement(n,{className:(0,u.default)(r,n.props.className)}):n,s.default.createElement(f,(0,a.default)({className:(0,u.default)(c.root,c.system,(0,i.default)({},c.colorDefault,!v),l)},m),g)}t.styles=l,f.defaultProps={component:"div"};var d=(0,c.default)(l,{name:"MuiAvatar"})(f);t.default=d},90338(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(68477))},9211(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184)),c=r(n(78252)),l=r(n(46408)),f={root:{zIndex:-1,position:"fixed",right:0,bottom:0,top:0,left:0,backgroundColor:"rgba(0, 0, 0, 0.5)",WebkitTapHighlightColor:"transparent",touchAction:"none"},invisible:{backgroundColor:"transparent"}};function d(e){var t=e.classes,n=e.className,r=e.invisible,c=e.open,f=e.transitionDuration,d=(0,o.default)(e,["classes","className","invisible","open","transitionDuration"]);return s.default.createElement(l.default,(0,i.default)({in:c,timeout:f},d),s.default.createElement("div",{className:(0,u.default)(t.root,(0,a.default)({},t.invisible,r),n),"aria-hidden":"true"}))}t.styles=f,d.defaultProps={invisible:!1};var h=(0,c.default)(f,{name:"MuiBackdrop"})(d);t.default=h},14983(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(9211))},84732(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=n(98741),f=10,d=function(e){return{root:{position:"relative",display:"inline-flex",verticalAlign:"middle"},badge:{display:"flex",flexDirection:"row",flexWrap:"wrap",justifyContent:"center",alignContent:"center",alignItems:"center",position:"absolute",top:0,right:0,boxSizing:"border-box",fontFamily:e.typography.fontFamily,fontWeight:e.typography.fontWeightMedium,fontSize:e.typography.pxToRem(12),minWidth:2*f,padding:"0 4px",height:2*f,borderRadius:f,backgroundColor:e.palette.color,color:e.palette.textColor,zIndex:1,transform:"scale(1) translate(50%, -50%)",transformOrigin:"100% 0%",transition:e.transitions.create("transform",{easing:e.transitions.easing.easeInOut,duration:e.transitions.duration.enteringScreen})},colorPrimary:{backgroundColor:e.palette.primary.main,color:e.palette.primary.contrastText},colorSecondary:{backgroundColor:e.palette.secondary.main,color:e.palette.secondary.contrastText},colorError:{backgroundColor:e.palette.error.main,color:e.palette.error.contrastText},invisible:{transition:e.transitions.create("transform",{easing:e.transitions.easing.easeInOut,duration:e.transitions.duration.leavingScreen}),transform:"scale(0) translate(50%, -50%)",transformOrigin:"100% 0%"},dot:{height:6,minWidth:6,padding:0}}};function h(e){var t,n=e.badgeContent,r=e.children,c=e.classes,f=e.className,d=e.color,h=e.component,p=e.invisible,b=e.showZero,m=e.max,g=e.variant,v=(0,o.default)(e,["badgeContent","children","classes","className","color","component","invisible","showZero","max","variant"]),y=p;null!=p||0!==Number(n)||b||(y=!0);var w=(0,u.default)(c.badge,(t={},(0,a.default)(t,c["color".concat((0,l.capitalize)(d))],"default"!==d),(0,a.default)(t,c.invisible,y),(0,a.default)(t,c.dot,"dot"===g),t)),_="";return"dot"!==g&&(_=n>m?"".concat(m,"+"):n),s.default.createElement(h,(0,i.default)({className:(0,u.default)(c.root,f)},v),r,s.default.createElement("span",{className:w},_))}t.styles=d,h.defaultProps={color:"default",component:"span",max:99,showZero:!1,variant:"standard"};var p=(0,c.default)(d,{name:"MuiBadge"})(h);t.default=p},70398(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(84732))},21783(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(59713)),a=r(n(6479)),o=r(n(67154)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=n(59114),f=r(n(16070)),d=n(98741),h=function(e){return{root:(0,o.default)({lineHeight:1.75},e.typography.button,{boxSizing:"border-box",minWidth:64,padding:"6px 16px",borderRadius:e.shape.borderRadius,color:e.palette.text.primary,transition:e.transitions.create(["background-color","box-shadow","border"],{duration:e.transitions.duration.short}),"&:hover":{textDecoration:"none",backgroundColor:(0,l.fade)(e.palette.text.primary,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"},"&$disabled":{backgroundColor:"transparent"}},"&$disabled":{color:e.palette.action.disabled}}),label:{width:"100%",display:"inherit",alignItems:"inherit",justifyContent:"inherit"},text:{padding:"6px 8px"},textPrimary:{color:e.palette.primary.main,"&:hover":{backgroundColor:(0,l.fade)(e.palette.primary.main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}}},textSecondary:{color:e.palette.secondary.main,"&:hover":{backgroundColor:(0,l.fade)(e.palette.secondary.main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}}},flat:{},flatPrimary:{},flatSecondary:{},outlined:{padding:"5px 16px",border:"1px solid ".concat("light"===e.palette.type?"rgba(0, 0, 0, 0.23)":"rgba(255, 255, 255, 0.23)"),"&$disabled":{border:"1px solid ".concat(e.palette.action.disabled)}},outlinedPrimary:{color:e.palette.primary.main,border:"1px solid ".concat((0,l.fade)(e.palette.primary.main,.5)),"&:hover":{border:"1px solid ".concat(e.palette.primary.main),backgroundColor:(0,l.fade)(e.palette.primary.main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}}},outlinedSecondary:{color:e.palette.secondary.main,border:"1px solid ".concat((0,l.fade)(e.palette.secondary.main,.5)),"&:hover":{border:"1px solid ".concat(e.palette.secondary.main),backgroundColor:(0,l.fade)(e.palette.secondary.main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}},"&$disabled":{border:"1px solid ".concat(e.palette.action.disabled)}},contained:{color:e.palette.getContrastText(e.palette.grey[300]),backgroundColor:e.palette.grey[300],boxShadow:e.shadows[2],"&$focusVisible":{boxShadow:e.shadows[6]},"&:active":{boxShadow:e.shadows[8]},"&$disabled":{color:e.palette.action.disabled,boxShadow:e.shadows[0],backgroundColor:e.palette.action.disabledBackground},"&:hover":{backgroundColor:e.palette.grey.A100,"@media (hover: none)":{backgroundColor:e.palette.grey[300]},"&$disabled":{backgroundColor:e.palette.action.disabledBackground}}},containedPrimary:{color:e.palette.primary.contrastText,backgroundColor:e.palette.primary.main,"&:hover":{backgroundColor:e.palette.primary.dark,"@media (hover: none)":{backgroundColor:e.palette.primary.main}}},containedSecondary:{color:e.palette.secondary.contrastText,backgroundColor:e.palette.secondary.main,"&:hover":{backgroundColor:e.palette.secondary.dark,"@media (hover: none)":{backgroundColor:e.palette.secondary.main}}},raised:{},raisedPrimary:{},raisedSecondary:{},fab:{borderRadius:"50%",padding:0,minWidth:0,width:56,height:56,boxShadow:e.shadows[6],"&:active":{boxShadow:e.shadows[12]}},extendedFab:{borderRadius:24,padding:"0 16px",width:"auto",minWidth:48,height:48},focusVisible:{},disabled:{},colorInherit:{color:"inherit",borderColor:"currentColor"},mini:{width:40,height:40},sizeSmall:{padding:"4px 8px",minWidth:64,fontSize:e.typography.pxToRem(13)},sizeLarge:{padding:"8px 24px",fontSize:e.typography.pxToRem(15)},fullWidth:{width:"100%"}}};function p(e){var t,n=e.children,r=e.classes,c=e.className,l=e.color,h=e.disabled,p=e.disableFocusRipple,b=e.focusVisibleClassName,m=e.fullWidth,g=e.mini,v=e.size,y=e.variant,w=(0,a.default)(e,["children","classes","className","color","disabled","disableFocusRipple","focusVisibleClassName","fullWidth","mini","size","variant"]),_="fab"===y||"extendedFab"===y,E="contained"===y||"raised"===y,S="text"===y||"flat"===y,k=(0,u.default)(r.root,(t={},(0,i.default)(t,r.fab,_),(0,i.default)(t,r.mini,_&&g),(0,i.default)(t,r.extendedFab,"extendedFab"===y),(0,i.default)(t,r.text,S),(0,i.default)(t,r.textPrimary,S&&"primary"===l),(0,i.default)(t,r.textSecondary,S&&"secondary"===l),(0,i.default)(t,r.flat,S),(0,i.default)(t,r.flatPrimary,S&&"primary"===l),(0,i.default)(t,r.flatSecondary,S&&"secondary"===l),(0,i.default)(t,r.contained,E||_),(0,i.default)(t,r.containedPrimary,(E||_)&&"primary"===l),(0,i.default)(t,r.containedSecondary,(E||_)&&"secondary"===l),(0,i.default)(t,r.raised,E||_),(0,i.default)(t,r.raisedPrimary,(E||_)&&"primary"===l),(0,i.default)(t,r.raisedSecondary,(E||_)&&"secondary"===l),(0,i.default)(t,r.outlined,"outlined"===y),(0,i.default)(t,r.outlinedPrimary,"outlined"===y&&"primary"===l),(0,i.default)(t,r.outlinedSecondary,"outlined"===y&&"secondary"===l),(0,i.default)(t,r["size".concat((0,d.capitalize)(v))],"medium"!==v),(0,i.default)(t,r.disabled,h),(0,i.default)(t,r.fullWidth,m),(0,i.default)(t,r.colorInherit,"inherit"===l),t),c);return s.default.createElement(f.default,(0,o.default)({className:k,disabled:h,focusRipple:!p,focusVisibleClassName:(0,u.default)(r.focusVisible,b)},w),s.default.createElement("span",{className:r.label},n))}t.styles=h,p.defaultProps={color:"default",component:"button",disabled:!1,disableFocusRipple:!1,fullWidth:!1,mini:!1,size:"medium",type:"button",variant:"text"};var b=(0,c.default)(h,{name:"MuiButton"})(p);t.default=b},83638(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(21783))},74610(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(34575)),u=r(n(93913)),c=r(n(78585)),l=r(n(29754)),f=r(n(2205)),d=r(n(81506)),h=r(n(67294));r(n(45697));var p=r(n(73935)),b=r(n(94184));n(55252);var m=r(n(62614)),g=r(n(78252)),v=r(n(78582)),y=n(32252),w=r(n(65406)),_=r(n(83673)),E={root:{display:"inline-flex",alignItems:"center",justifyContent:"center",position:"relative",WebkitTapHighlightColor:"transparent",backgroundColor:"transparent",outline:"none",border:0,margin:0,borderRadius:0,padding:0,cursor:"pointer",userSelect:"none",verticalAlign:"middle","-moz-appearance":"none","-webkit-appearance":"none",textDecoration:"none",color:"inherit","&::-moz-focus-inner":{borderStyle:"none"},"&$disabled":{pointerEvents:"none",cursor:"default"}},disabled:{},focusVisible:{}};t.styles=E;var S=function(e){function t(){(0,s.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;a0&&void 0!==arguments[0]?arguments[0]:{},o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=arguments.length>2?arguments[2]:void 0,u=o.pulsate,c=void 0!==u&&u,l=o.center,f=void 0===l?n.props.center||o.pulsate:l,h=o.fakeElement,b=void 0!==h&&h;if("mousedown"===a.type&&n.ignoringMouseDown){n.ignoringMouseDown=!1;return}"touchstart"===a.type&&(n.ignoringMouseDown=!0);var m=b?null:p.default.findDOMNode((0,d.default)((0,d.default)(n))),g=m?m.getBoundingClientRect():{width:0,height:0,left:0,top:0};if(!f&&(0!==a.clientX||0!==a.clientY)&&(a.clientX||a.touches)){var v=a.clientX?a.clientX:a.touches[0].clientX,y=a.clientY?a.clientY:a.touches[0].clientY;t=Math.round(v-g.left),r=Math.round(y-g.top)}else t=Math.round(g.width/2),r=Math.round(g.height/2);if(f)(i=Math.sqrt((2*Math.pow(g.width,2)+Math.pow(g.height,2))/3))%2==0&&(i+=1);else{i=Math.sqrt(Math.pow(2*Math.max(Math.abs((m?m.clientWidth:0)-t),t)+2,2)+Math.pow(2*Math.max(Math.abs((m?m.clientHeight:0)-r),r)+2,2))}a.touches?(n.startTimerCommit=function(){n.startCommit({pulsate:c,rippleX:t,rippleY:r,rippleSize:i,cb:s})},n.startTimer=setTimeout(function(){n.startTimerCommit&&(n.startTimerCommit(),n.startTimerCommit=null)},w)):n.startCommit({pulsate:c,rippleX:t,rippleY:r,rippleSize:i,cb:s})},n.startCommit=function(e){var t=e.pulsate,r=e.rippleX,i=e.rippleY,a=e.rippleSize,s=e.cb;n.setState(function(e){return{nextKey:e.nextKey+1,ripples:[].concat((0,o.default)(e.ripples),[h.default.createElement(v.default,{key:e.nextKey,classes:n.props.classes,timeout:{exit:y,enter:y},pulsate:t,rippleX:r,rippleY:i,rippleSize:a})])}},s)},n.stop=function(e,t){clearTimeout(n.startTimer);var r=n.state.ripples;if("touchend"===e.type&&n.startTimerCommit){e.persist(),n.startTimerCommit(),n.startTimerCommit=null,n.startTimer=setTimeout(function(){n.stop(e,t)});return}n.startTimerCommit=null,r&&r.length&&n.setState({ripples:r.slice(1)},t)},n}return(0,f.default)(t,e),(0,u.default)(t,[{key:"componentWillUnmount",value:function(){clearTimeout(this.startTimer)}},{key:"render",value:function(){var e=this.props,t=(e.center,e.classes),n=e.className,r=(0,a.default)(e,["center","classes","className"]);return h.default.createElement(b.default,(0,i.default)({component:"span",enter:!0,exit:!0,className:(0,m.default)(t.root,n)},r),this.state.ripples)}}]),t}(h.default.PureComponent);E.defaultProps={center:!1};var S=(0,g.default)(_,{flip:!1,name:"MuiTouchRipple"})(E);t.default=S},83673(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=function(e,t,n,r){return function(i){r&&r.call(e,i);var a=!1;return i.defaultPrevented&&(a=!0),e.props.disableTouchRipple&&"Blur"!==t&&(a=!0),!a&&e.ripple&&e.ripple[n](i),"function"==typeof e.props["on".concat(t)]&&e.props["on".concat(t)](i),!0}};"undefined"==typeof window&&(n=function(){return function(){}});var r=n;t.default=r},32252(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.detectFocusVisible=s,t.listenForFocusKeys=f,r(n(42473));var i=r(n(16143)),a={focusKeyPressed:!1,keyUpEventTimeout:-1};function o(e){for(var t=e.activeElement;t&&t.shadowRoot&&t.shadowRoot.activeElement;)t=t.shadowRoot.activeElement;return t}function s(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1;e.focusVisibleTimeout=setTimeout(function(){var u=(0,i.default)(t),c=o(u);a.focusKeyPressed&&(c===t||t.contains(c))?n():r-1}var l=function(e){c(e)&&(a.focusKeyPressed=!0,clearTimeout(a.keyUpEventTimeout),a.keyUpEventTimeout=setTimeout(function(){a.focusKeyPressed=!1},500))};function f(e){e.addEventListener("keyup",l)}},16070(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(74610))},46003(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184)),u=r(n(68821)),c=r(n(78252)),l={root:{overflow:"hidden"}};function f(e){var t=e.classes,n=e.className,r=e.raised,c=(0,a.default)(e,["classes","className","raised"]);return o.default.createElement(u.default,(0,i.default)({className:(0,s.default)(t.root,n),elevation:r?8:1},c))}t.styles=l,f.defaultProps={raised:!1};var d=(0,c.default)(l,{name:"MuiCard"})(f);t.default=d},82204(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(46003))},5780(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184));n(55252);var u=r(n(78252)),c={root:{padding:16,"&:last-child":{paddingBottom:24}}};function l(e){var t=e.classes,n=e.className,r=e.component,u=(0,a.default)(e,["classes","className","component"]);return o.default.createElement(r,(0,i.default)({className:(0,s.default)(t.root,n)},u))}t.styles=c,l.defaultProps={component:"div"};var f=(0,u.default)(c,{name:"MuiCardContent"})(l);t.default=f},30060(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(5780))},50704(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184));n(55252);var u=r(n(78252)),c=r(n(71426)),l={root:{display:"flex",alignItems:"center",padding:16},avatar:{flex:"0 0 auto",marginRight:16},action:{flex:"0 0 auto",alignSelf:"flex-start",marginTop:-8,marginRight:-8},content:{flex:"1 1 auto"},title:{},subheader:{}};function f(e){var t=e.action,n=e.avatar,r=e.classes,u=e.className,l=e.component,f=e.disableTypography,d=e.subheader,h=e.subheaderTypographyProps,p=e.title,b=e.titleTypographyProps,m=(0,a.default)(e,["action","avatar","classes","className","component","disableTypography","subheader","subheaderTypographyProps","title","titleTypographyProps"]),g=p;null==g||g.type===c.default||f||(g=o.default.createElement(c.default,(0,i.default)({variant:n?"body2":"headline",internalDeprecatedVariant:!0,className:r.title,component:"span"},b),g));var v=d;return null==v||v.type===c.default||f||(v=o.default.createElement(c.default,(0,i.default)({variant:n?"body2":"body1",className:r.subheader,color:"textSecondary",component:"span"},h),v)),o.default.createElement(l,(0,i.default)({className:(0,s.default)(r.root,u)},m),n&&o.default.createElement("div",{className:r.avatar},n),o.default.createElement("div",{className:r.content},g,v),t&&o.default.createElement("div",{className:r.action},t))}t.styles=l,f.defaultProps={component:"div",disableTypography:!1};var d=(0,u.default)(l,{name:"MuiCardHeader"})(f);t.default=d},52658(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(50704))},82811(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184)),c=r(n(85609)),l=r(n(42159)),f=r(n(41549)),d=r(n(61486)),h=n(98741),p=r(n(78252)),b=function(e){return{root:{color:e.palette.text.secondary},checked:{},disabled:{},indeterminate:{},colorPrimary:{"&$checked":{color:e.palette.primary.main},"&$disabled":{color:e.palette.action.disabled}},colorSecondary:{"&$checked":{color:e.palette.secondary.main},"&$disabled":{color:e.palette.action.disabled}}}};function m(e){var t=e.checkedIcon,n=e.classes,r=e.className,l=e.color,f=e.icon,d=e.indeterminate,p=e.indeterminateIcon,b=e.inputProps,m=(0,o.default)(e,["checkedIcon","classes","className","color","icon","indeterminate","indeterminateIcon","inputProps"]);return s.default.createElement(c.default,(0,i.default)({type:"checkbox",checkedIcon:d?p:t,className:(0,u.default)((0,a.default)({},n.indeterminate,d),r),classes:{root:(0,u.default)(n.root,n["color".concat((0,h.capitalize)(l))]),checked:n.checked,disabled:n.disabled},inputProps:(0,i.default)({"data-indeterminate":d},b),icon:d?p:f},m))}t.styles=b,m.defaultProps={checkedIcon:s.default.createElement(f.default,null),color:"secondary",icon:s.default.createElement(l.default,null),indeterminate:!1,indeterminateIcon:s.default.createElement(d.default,null)};var g=(0,p.default)(b,{name:"MuiCheckbox"})(m);t.default=g},71209(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(82811))},16444(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(34575)),u=r(n(93913)),c=r(n(78585)),l=r(n(29754)),f=r(n(2205)),d=r(n(67294));r(n(45697));var h=r(n(94184));r(n(42473)),n(55252);var p=r(n(99781)),b=r(n(78252)),m=n(59114);r(n(21677));var g=n(98741);n(68477);var v=function(e){var t=32,n="light"===e.palette.type?e.palette.grey[300]:e.palette.grey[700],r=(0,m.fade)(e.palette.text.primary,.26);return{root:{fontFamily:e.typography.fontFamily,fontSize:e.typography.pxToRem(13),display:"inline-flex",alignItems:"center",justifyContent:"center",height:t,color:e.palette.getContrastText(n),backgroundColor:n,borderRadius:t/2,whiteSpace:"nowrap",transition:e.transitions.create(["background-color","box-shadow"]),cursor:"default",outline:"none",textDecoration:"none",border:"none",padding:0,verticalAlign:"middle",boxSizing:"border-box"},colorPrimary:{backgroundColor:e.palette.primary.main,color:e.palette.primary.contrastText},colorSecondary:{backgroundColor:e.palette.secondary.main,color:e.palette.secondary.contrastText},clickable:{WebkitTapHighlightColor:"transparent",cursor:"pointer","&:hover, &:focus":{backgroundColor:(0,m.emphasize)(n,.08)},"&:active":{boxShadow:e.shadows[1],backgroundColor:(0,m.emphasize)(n,.12)}},clickableColorPrimary:{"&:hover, &:focus":{backgroundColor:(0,m.emphasize)(e.palette.primary.main,.08)},"&:active":{backgroundColor:(0,m.emphasize)(e.palette.primary.main,.12)}},clickableColorSecondary:{"&:hover, &:focus":{backgroundColor:(0,m.emphasize)(e.palette.secondary.main,.08)},"&:active":{backgroundColor:(0,m.emphasize)(e.palette.secondary.main,.12)}},deletable:{"&:focus":{backgroundColor:(0,m.emphasize)(n,.08)}},deletableColorPrimary:{"&:focus":{backgroundColor:(0,m.emphasize)(e.palette.primary.main,.2)}},deletableColorSecondary:{"&:focus":{backgroundColor:(0,m.emphasize)(e.palette.secondary.main,.2)}},outlined:{backgroundColor:"transparent",border:"1px solid ".concat("light"===e.palette.type?"rgba(0, 0, 0, 0.23)":"rgba(255, 255, 255, 0.23)"),"$clickable&:hover, $clickable&:focus, $deletable&:focus":{backgroundColor:(0,m.fade)(e.palette.text.primary,e.palette.action.hoverOpacity)},"& $avatar":{marginLeft:-1}},outlinedPrimary:{color:e.palette.primary.main,border:"1px solid ".concat(e.palette.primary.main),"$clickable&:hover, $clickable&:focus, $deletable&:focus":{backgroundColor:(0,m.fade)(e.palette.primary.main,e.palette.action.hoverOpacity)}},outlinedSecondary:{color:e.palette.secondary.main,border:"1px solid ".concat(e.palette.secondary.main),"$clickable&:hover, $clickable&:focus, $deletable&:focus":{backgroundColor:(0,m.fade)(e.palette.secondary.main,e.palette.action.hoverOpacity)}},avatar:{marginRight:-4,width:t,height:t,color:"light"===e.palette.type?e.palette.grey[700]:e.palette.grey[300],fontSize:e.typography.pxToRem(16)},avatarColorPrimary:{color:e.palette.primary.contrastText,backgroundColor:e.palette.primary.dark},avatarColorSecondary:{color:e.palette.secondary.contrastText,backgroundColor:e.palette.secondary.dark},avatarChildren:{width:19,height:19},icon:{color:"light"===e.palette.type?e.palette.grey[700]:e.palette.grey[300],marginLeft:4,marginRight:-8},iconColorPrimary:{color:"inherit"},iconColorSecondary:{color:"inherit"},label:{display:"flex",alignItems:"center",paddingLeft:12,paddingRight:12,userSelect:"none",whiteSpace:"nowrap",cursor:"inherit"},deleteIcon:{WebkitTapHighlightColor:"transparent",color:r,cursor:"pointer",height:"auto",margin:"0 4px 0 -8px","&:hover":{color:(0,m.fade)(r,.4)}},deleteIconColorPrimary:{color:(0,m.fade)(e.palette.primary.contrastText,.7),"&:hover, &:active":{color:e.palette.primary.contrastText}},deleteIconColorSecondary:{color:(0,m.fade)(e.palette.secondary.contrastText,.7),"&:hover, &:active":{color:e.palette.secondary.contrastText}},deleteIconOutlinedColorPrimary:{color:(0,m.fade)(e.palette.primary.main,.7),"&:hover, &:active":{color:e.palette.primary.main}},deleteIconOutlinedColorSecondary:{color:(0,m.fade)(e.palette.secondary.main,.7),"&:hover, &:active":{color:e.palette.secondary.main}}}};t.styles=v;var y=function(e){function t(){(0,s.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;a :last-child":{paddingRight:32},"&$expanded":{margin:"20px 0"}},expandIcon:{position:"absolute",top:"50%",right:8,transform:"translateY(-50%) rotate(0deg)",transition:e.transitions.create("transform",t),"&:hover":{backgroundColor:"transparent"},"&$expanded":{transform:"translateY(-50%) rotate(180deg)"}}}};t.styles=g;var v=function(e){function t(){(0,s.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;a $item":{padding:e/2}})}),n}var b=function(e){return(0,o.default)({container:{boxSizing:"border-box",display:"flex",flexWrap:"wrap",width:"100%"},item:{boxSizing:"border-box",margin:"0"},zeroMinWidth:{minWidth:0},"direction-xs-column":{flexDirection:"column"},"direction-xs-column-reverse":{flexDirection:"column-reverse"},"direction-xs-row-reverse":{flexDirection:"row-reverse"},"wrap-xs-nowrap":{flexWrap:"nowrap"},"wrap-xs-wrap-reverse":{flexWrap:"wrap-reverse"},"align-items-xs-center":{alignItems:"center"},"align-items-xs-flex-start":{alignItems:"flex-start"},"align-items-xs-flex-end":{alignItems:"flex-end"},"align-items-xs-baseline":{alignItems:"baseline"},"align-content-xs-center":{alignContent:"center"},"align-content-xs-flex-start":{alignContent:"flex-start"},"align-content-xs-flex-end":{alignContent:"flex-end"},"align-content-xs-space-between":{alignContent:"space-between"},"align-content-xs-space-around":{alignContent:"space-around"},"justify-xs-center":{justifyContent:"center"},"justify-xs-flex-end":{justifyContent:"flex-end"},"justify-xs-space-between":{justifyContent:"space-between"},"justify-xs-space-around":{justifyContent:"space-around"},"justify-xs-space-evenly":{justifyContent:"space-evenly"}},p(e,"xs"),l.keys.reduce(function(t,n){return h(t,e,n),t},{}))};function m(e){var t,n=e.alignContent,r=e.alignItems,c=e.classes,l=e.className,f=e.component,d=e.container,h=e.direction,p=e.item,b=e.justify,g=e.lg,v=e.md,y=e.sm,w=e.spacing,_=e.wrap,E=e.xl,S=e.xs,k=e.zeroMinWidth,x=(0,a.default)(e,["alignContent","alignItems","classes","className","component","container","direction","item","justify","lg","md","sm","spacing","wrap","xl","xs","zeroMinWidth"]),T=(0,u.default)((t={},(0,i.default)(t,c.container,d),(0,i.default)(t,c.item,p),(0,i.default)(t,c.zeroMinWidth,k),(0,i.default)(t,c["spacing-xs-".concat(String(w))],d&&0!==w),(0,i.default)(t,c["direction-xs-".concat(String(h))],h!==m.defaultProps.direction),(0,i.default)(t,c["wrap-xs-".concat(String(_))],_!==m.defaultProps.wrap),(0,i.default)(t,c["align-items-xs-".concat(String(r))],r!==m.defaultProps.alignItems),(0,i.default)(t,c["align-content-xs-".concat(String(n))],n!==m.defaultProps.alignContent),(0,i.default)(t,c["justify-xs-".concat(String(b))],b!==m.defaultProps.justify),(0,i.default)(t,c["grid-xs-".concat(String(S))],!1!==S),(0,i.default)(t,c["grid-sm-".concat(String(y))],!1!==y),(0,i.default)(t,c["grid-md-".concat(String(v))],!1!==v),(0,i.default)(t,c["grid-lg-".concat(String(g))],!1!==g),(0,i.default)(t,c["grid-xl-".concat(String(E))],!1!==E),t),l);return s.default.createElement(f,(0,o.default)({className:T},x))}t.styles=b,m.defaultProps={alignContent:"stretch",alignItems:"stretch",component:"div",container:!1,direction:"row",item:!1,justify:"flex-start",lg:!1,md:!1,sm:!1,spacing:0,wrap:"wrap",xl:!1,xs:!1,zeroMinWidth:!1};var g,v=(0,c.default)(b,{name:"MuiGrid"})(m);t.default=v},97779(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(27973))},57205(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(60644)),h=r(n(82313)),p=n(41929);function b(e){return"scale(".concat(e,", ").concat(Math.pow(e,2),")")}var m={entering:{opacity:1,transform:b(1)},entered:{opacity:1,transform:"".concat(b(1)," translateZ(0)")}},g=function(e){function t(){(0,o.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;a=Number(e.rows)&&(n=Math.min(Number(e.rowsMax)*t,n)),n=Math.max(n,t),Math.abs(this.state.height-n)>1&&this.setState({height:n}))}}},{key:"render",value:function(){var e=this.props,t=e.classes,n=e.className,r=e.defaultValue,o=(e.onChange,e.rows),s=(e.rowsMax,e.style),u=(e.textareaRef,e.value),c=(0,a.default)(e,["classes","className","defaultValue","onChange","rows","rowsMax","style","textareaRef","value"]);return f.default.createElement("div",{className:t.root},f.default.createElement(p.default,{target:"window",onResize:this.handleResize}),f.default.createElement("textarea",{"aria-hidden":"true",className:(0,d.default)(t.textarea,t.shadow),readOnly:!0,ref:this.handleRefSinglelineShadow,rows:"1",tabIndex:-1,value:""}),f.default.createElement("textarea",{"aria-hidden":"true",className:(0,d.default)(t.textarea,t.shadow),defaultValue:r,readOnly:!0,ref:this.handleRefShadow,rows:o,tabIndex:-1,value:u}),f.default.createElement("textarea",(0,i.default)({rows:o,className:(0,d.default)(t.textarea,n),defaultValue:r,value:u,onChange:this.handleChange,ref:this.handleRefInput,style:(0,i.default)({height:this.state.height},s)},c)))}}]),t}(f.default.Component);y.defaultProps={rows:1};var w=(0,b.default)(v,{name:"MuiPrivateTextarea"})(y);t.default=w},67598(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(62010))},78586(e,t){"use strict";function n(e){return null!=e&&!(Array.isArray(e)&&0===e.length)}function r(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return e&&(n(e.value)&&""!==e.value||t&&n(e.defaultValue)&&""!==e.defaultValue)}function i(e){return e.startAdornment}Object.defineProperty(t,"__esModule",{value:!0}),t.hasValue=n,t.isFilled=r,t.isAdornedStart=i},56030(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184)),c=r(n(58189)),l=r(n(52598)),f=r(n(78252)),d=r(n(69645)),h=function(e){return{root:{transformOrigin:"top left"},focused:{},disabled:{},error:{},required:{},formControl:{position:"absolute",left:0,top:0,transform:"translate(0, 24px) scale(1)"},marginDense:{transform:"translate(0, 21px) scale(1)"},shrink:{transform:"translate(0, 1.5px) scale(0.75)",transformOrigin:"top left"},animated:{transition:e.transitions.create(["color","transform"],{duration:e.transitions.duration.shorter,easing:e.transitions.easing.easeOut})},filled:{zIndex:1,pointerEvents:"none",transform:"translate(12px, 20px) scale(1)","&$marginDense":{transform:"translate(12px, 17px) scale(1)"},"&$shrink":{transform:"translate(12px, 10px) scale(0.75)","&$marginDense":{transform:"translate(12px, 7px) scale(0.75)"}}},outlined:{zIndex:1,pointerEvents:"none",transform:"translate(14px, 20px) scale(1)","&$marginDense":{transform:"translate(14px, 17px) scale(1)"},"&$shrink":{transform:"translate(14px, -6px) scale(0.75)"}}}};function p(e){var t,n=e.children,r=e.classes,l=e.className,f=e.disableAnimation,h=e.FormLabelClasses,p=(e.margin,e.muiFormControl),b=e.shrink,m=(e.variant,(0,o.default)(e,["children","classes","className","disableAnimation","FormLabelClasses","margin","muiFormControl","shrink","variant"])),g=b;void 0===g&&p&&(g=p.filled||p.focused||p.adornedStart);var v=(0,c.default)({props:e,muiFormControl:p,states:["margin","variant"]}),y=(0,u.default)(r.root,(t={},(0,a.default)(t,r.formControl,p),(0,a.default)(t,r.animated,!f),(0,a.default)(t,r.shrink,g),(0,a.default)(t,r.marginDense,"dense"===v.margin),(0,a.default)(t,r.filled,"filled"===v.variant),(0,a.default)(t,r.outlined,"outlined"===v.variant),t),l);return s.default.createElement(d.default,(0,i.default)({"data-shrink":g,className:y,classes:(0,i.default)({focused:r.focused,disabled:r.disabled,error:r.error,required:r.required},h)},m),n)}t.styles=h,p.defaultProps={disableAnimation:!1};var b=(0,f.default)(h,{name:"MuiInputLabel"})((0,l.default)(p));t.default=b},23153(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(56030))},46616(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));r(n(42473));var c=r(n(78252)),l=n(59114),f=4,d=function(e){return{root:{position:"relative",overflow:"hidden",height:4},colorPrimary:{backgroundColor:(0,l.lighten)(e.palette.primary.light,.6)},colorSecondary:{backgroundColor:(0,l.lighten)(e.palette.secondary.light,.4)},determinate:{},indeterminate:{},buffer:{backgroundColor:"transparent"},query:{transform:"rotate(180deg)"},dashed:{position:"absolute",marginTop:0,height:"100%",width:"100%",animation:"buffer 3s infinite linear",animationName:"$buffer"},dashedColorPrimary:{backgroundImage:"radial-gradient(".concat((0,l.lighten)(e.palette.primary.light,.6)," 0%, ").concat((0,l.lighten)(e.palette.primary.light,.6)," 16%, transparent 42%)"),backgroundSize:"10px 10px",backgroundPosition:"0px -23px"},dashedColorSecondary:{backgroundImage:"radial-gradient(".concat((0,l.lighten)(e.palette.secondary.light,.4)," 0%, ").concat((0,l.lighten)(e.palette.secondary.light,.6)," 16%, transparent 42%)"),backgroundSize:"10px 10px",backgroundPosition:"0px -23px"},bar:{width:"100%",position:"absolute",left:0,bottom:0,top:0,transition:"transform 0.2s linear",transformOrigin:"left"},barColorPrimary:{backgroundColor:e.palette.primary.main},barColorSecondary:{backgroundColor:e.palette.secondary.main},bar1Indeterminate:{width:"auto",animation:"mui-indeterminate1 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite",animationName:"$mui-indeterminate1"},bar1Determinate:{transition:"transform .".concat(f,"s linear")},bar1Buffer:{zIndex:1,transition:"transform .".concat(f,"s linear")},bar2Indeterminate:{width:"auto",animation:"mui-indeterminate2 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite",animationName:"$mui-indeterminate2",animationDelay:"1.15s"},bar2Buffer:{transition:"transform .".concat(f,"s linear")},"@keyframes mui-indeterminate1":{"0%":{left:"-35%",right:"100%"},"60%":{left:"100%",right:"-90%"},"100%":{left:"100%",right:"-90%"}},"@keyframes mui-indeterminate2":{"0%":{left:"-200%",right:"100%"},"60%":{left:"107%",right:"-8%"},"100%":{left:"107%",right:"-8%"}},"@keyframes buffer":{"0%":{opacity:1,backgroundPosition:"0px -23px"},"50%":{opacity:0,backgroundPosition:"0px -23px"},"100%":{opacity:1,backgroundPosition:"-200px -23px"}}}};function h(e){var t,n,r,c,l=e.classes,f=e.className,d=e.color,h=e.value,p=e.valueBuffer,b=e.variant,m=(0,o.default)(e,["classes","className","color","value","valueBuffer","variant"]),g=(0,u.default)(l.root,(t={},(0,a.default)(t,l.colorPrimary,"primary"===d),(0,a.default)(t,l.colorSecondary,"secondary"===d),(0,a.default)(t,l.determinate,"determinate"===b),(0,a.default)(t,l.indeterminate,"indeterminate"===b),(0,a.default)(t,l.buffer,"buffer"===b),(0,a.default)(t,l.query,"query"===b),t),f),v=(0,u.default)(l.dashed,(n={},(0,a.default)(n,l.dashedColorPrimary,"primary"===d),(0,a.default)(n,l.dashedColorSecondary,"secondary"===d),n)),y=(0,u.default)(l.bar,(r={},(0,a.default)(r,l.barColorPrimary,"primary"===d),(0,a.default)(r,l.barColorSecondary,"secondary"===d),(0,a.default)(r,l.bar1Indeterminate,"indeterminate"===b||"query"===b),(0,a.default)(r,l.bar1Determinate,"determinate"===b),(0,a.default)(r,l.bar1Buffer,"buffer"===b),r)),w=(0,u.default)(l.bar,(c={},(0,a.default)(c,l.barColorPrimary,"primary"===d&&"buffer"!==b),(0,a.default)(c,l.colorPrimary,"primary"===d&&"buffer"===b),(0,a.default)(c,l.barColorSecondary,"secondary"===d&&"buffer"!==b),(0,a.default)(c,l.colorSecondary,"secondary"===d&&"buffer"===b),(0,a.default)(c,l.bar2Indeterminate,"indeterminate"===b||"query"===b),(0,a.default)(c,l.bar2Buffer,"buffer"===b),c)),_={},E={bar1:{},bar2:{}};return("determinate"===b||"buffer"===b)&&void 0!==h&&(_["aria-valuenow"]=Math.round(h),E.bar1.transform="scaleX(".concat(h/100,")")),"buffer"===b&&void 0!==p&&(E.bar2.transform="scaleX(".concat((p||0)/100,")")),s.default.createElement("div",(0,i.default)({className:g,role:"progressbar"},_,m),"buffer"===b?s.default.createElement("div",{className:v}):null,s.default.createElement("div",{className:y,style:E.bar1}),"determinate"===b?null:s.default.createElement("div",{className:w,style:E.bar2}))}t.styles=d,h.defaultProps={color:"primary",variant:"indeterminate"};var p=(0,c.default)(d,{name:"MuiLinearProgress"})(h);t.default=p},79424(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(46616))},74080(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=r(n(47457)),f={root:{listStyle:"none",margin:0,padding:0,position:"relative"},padding:{paddingTop:8,paddingBottom:8},dense:{paddingTop:4,paddingBottom:4},subheader:{paddingTop:0}};function d(e){var t,n=e.children,r=e.classes,c=e.className,f=e.component,d=e.dense,h=e.disablePadding,p=e.subheader,b=(0,o.default)(e,["children","classes","className","component","dense","disablePadding","subheader"]);return s.default.createElement(f,(0,i.default)({className:(0,u.default)(r.root,(t={},(0,a.default)(t,r.dense,d&&!h),(0,a.default)(t,r.padding,!h),(0,a.default)(t,r.subheader,p),t),c)},b),s.default.createElement(l.default.Provider,{value:{dense:d}},p,n))}t.styles=f,d.defaultProps={component:"ul",dense:!1,disablePadding:!1};var h=(0,c.default)(f,{name:"MuiList"})(d);t.default=h},47457(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)).default.createContext({});t.default=i},3022(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(74080))},29936(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=r(n(16070)),f=n(44370),d=r(n(671)),h=function(e){return{root:{display:"flex",justifyContent:"flex-start",alignItems:"center",position:"relative",textDecoration:"none",width:"100%",boxSizing:"border-box",textAlign:"left",paddingTop:11,paddingBottom:11,"&$selected, &$selected:hover, &$selected:focus":{backgroundColor:e.palette.action.selected}},container:{position:"relative"},focusVisible:{},default:{},dense:{paddingTop:8,paddingBottom:8},alignItemsFlexStart:{alignItems:"flex-start"},disabled:{opacity:.5},divider:{borderBottom:"1px solid ".concat(e.palette.divider),backgroundClip:"padding-box"},gutters:{paddingLeft:16,paddingRight:16},button:{transition:e.transitions.create("background-color",{duration:e.transitions.duration.shortest}),"&:hover":{textDecoration:"none",backgroundColor:e.palette.action.hover,"@media (hover: none)":{backgroundColor:"transparent"}},"&:focus":{backgroundColor:e.palette.action.hover}},secondaryAction:{paddingRight:32},selected:{}}};function p(e){var t=e.alignItems,n=e.button,r=e.children,c=e.classes,h=e.className,p=e.component,b=e.ContainerComponent,m=e.ContainerProps,g=(m=void 0===m?{}:m).className,v=(0,o.default)(m,["className"]),y=e.dense,w=e.disabled,_=e.disableGutters,E=e.divider,S=e.focusVisibleClassName,k=e.selected,x=(0,o.default)(e,["alignItems","button","children","classes","className","component","ContainerComponent","ContainerProps","dense","disabled","disableGutters","divider","focusVisibleClassName","selected"]);return s.default.createElement(d.default,{dense:y,alignItems:t},function(e){var o,d=e.dense,m=s.default.Children.toArray(r),y=m.some(function(e){return(0,f.isMuiElement)(e,["ListItemAvatar"])}),T=m.length&&(0,f.isMuiElement)(m[m.length-1],["ListItemSecondaryAction"]),M=(0,u.default)(c.root,c.default,(o={},(0,a.default)(o,c.dense,d||y),(0,a.default)(o,c.gutters,!_),(0,a.default)(o,c.divider,E),(0,a.default)(o,c.disabled,w),(0,a.default)(o,c.button,n),(0,a.default)(o,c.alignItemsFlexStart,"flex-start"===t),(0,a.default)(o,c.secondaryAction,T),(0,a.default)(o,c.selected,k),o),h),O=(0,i.default)({className:M,disabled:w},x),A=p||"li";return(n&&(O.component=p||"div",O.focusVisibleClassName=(0,u.default)(c.focusVisible,S),A=l.default),T)?(A=O.component||p?A:"div","li"===b&&("li"===A?A="div":"li"===O.component&&(O.component="div")),s.default.createElement(b,(0,i.default)({className:(0,u.default)(c.container,g)},v),s.default.createElement(A,O,m),m.pop())):s.default.createElement(A,O,m)})}t.styles=h,p.defaultProps={alignItems:"center",button:!1,ContainerComponent:"li",dense:!1,disabled:!1,disableGutters:!1,divider:!1,selected:!1};var b=(0,c.default)(h,{name:"MuiListItem"})(p);t.default=b},671(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294));r(n(45697));var a=r(n(47457));function o(e){var t=e.alignItems,n=e.children,r=e.dense;return i.default.createElement(a.default.Consumer,null,function(e){var o={dense:r||e.dense||!1,alignItems:t};return i.default.createElement(a.default.Provider,{value:o},n(o))})}var s=o;t.default=s},60323(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(29936))},69394(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184)),u=r(n(78252)),c=function(e){return{root:{marginRight:16,color:e.palette.action.active,flexShrink:0,display:"inline-flex"}}};function l(e){var t=e.children,n=e.classes,r=e.className,u=(0,a.default)(e,["children","classes","className"]);return o.default.createElement("div",(0,i.default)({className:(0,s.default)(n.root,r)},u),t)}t.styles=c;var f=(0,u.default)(c,{name:"MuiListItemIcon"})(l);t.default=f},11186(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(69394))},73390(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184)),c=r(n(78252)),l=r(n(71426)),f=r(n(47457)),d=function(e){return{root:{flex:"1 1 auto",minWidth:0,padding:"0 16px","&:first-child":{paddingLeft:0}},inset:{"&:first-child":{paddingLeft:56}},dense:{fontSize:e.typography.pxToRem(13)},primary:{"&$textDense":{fontSize:"inherit"}},secondary:{"&$textDense":{fontSize:"inherit"}},textDense:{}}};function h(e){var t=e.children,n=e.classes,r=e.className,c=e.disableTypography,d=e.inset,h=e.primary,p=e.primaryTypographyProps,b=e.secondary,m=e.secondaryTypographyProps,g=e.theme,v=(0,o.default)(e,["children","classes","className","disableTypography","inset","primary","primaryTypographyProps","secondary","secondaryTypographyProps","theme"]);return s.default.createElement(f.default.Consumer,null,function(e){var o,f=e.dense,y=null!=h?h:t;null==y||y.type===l.default||c||(y=s.default.createElement(l.default,(0,i.default)({variant:g.typography.useNextVariants?"body1":"subheading",className:(0,u.default)(n.primary,(0,a.default)({},n.textDense,f)),component:"span"},p),y));var w=b;return null==w||w.type===l.default||c||(w=s.default.createElement(l.default,(0,i.default)({className:(0,u.default)(n.secondary,(0,a.default)({},n.textDense,f)),color:"textSecondary"},m),w)),s.default.createElement("div",(0,i.default)({className:(0,u.default)(n.root,(o={},(0,a.default)(o,n.dense,f),(0,a.default)(o,n.inset,d),o),r)},v),y,w)})}t.styles=d,h.defaultProps={disableTypography:!1,inset:!1};var p=(0,c.default)(d,{name:"MuiListItemText",withTheme:!0})(h);t.default=p},87591(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(73390))},95890(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(73935)),h=r(n(44825)),p=r(n(78252)),b=r(n(50810)),m=r(n(34980)),g={vertical:"top",horizontal:"right"},v={vertical:"top",horizontal:"left"},y={paper:{maxHeight:"calc(100% - 96px)",WebkitOverflowScrolling:"touch"}};t.styles=y;var w=function(e){function t(){(0,o.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;a=0?t.children[e].focus():t.firstChild.focus())}},{key:"resetTabIndex",value:function(){for(var e=this.listRef,t=(0,h.default)(e).activeElement,n=[],r=0;r0&&void 0!==arguments[0]?arguments[0]:{};(0,i.default)(this,e);var n=t.hideSiblingNodes,r=void 0===n||n,a=t.handleContainerOverflow,o=void 0===a||a;this.hideSiblingNodes=r,this.handleContainerOverflow=o,this.modals=[],this.data=[]}return(0,a.default)(e,[{key:"add",value:function(e,t){var n=this.modals.indexOf(e);if(-1!==n)return n;n=this.modals.length,this.modals.push(e),e.modalRef&&(0,l.ariaHidden)(e.modalRef,!1),this.hideSiblingNodes&&(0,l.ariaHiddenSiblings)(t,e.mountNode,e.modalRef,!0);var r=f(this.data,function(e){return e.container===t});if(-1!==r)return this.data[r].modals.push(e),n;var i={modals:[e],container:t,overflowing:(0,c.default)(t),prevPaddings:[]};return this.data.push(i),n}},{key:"mount",value:function(e){var t=f(this.data,function(t){return -1!==t.modals.indexOf(e)}),n=this.data[t];!n.style&&this.handleContainerOverflow&&h(n)}},{key:"remove",value:function(e){var t=this.modals.indexOf(e);if(-1===t)return t;var n=f(this.data,function(t){return -1!==t.modals.indexOf(e)}),r=this.data[n];if(r.modals.splice(r.modals.indexOf(e),1),this.modals.splice(t,1),0===r.modals.length)this.handleContainerOverflow&&p(r),e.modalRef&&(0,l.ariaHidden)(e.modalRef,!0),this.hideSiblingNodes&&(0,l.ariaHiddenSiblings)(r.container,e.mountNode,e.modalRef,!1),this.data.splice(n,1);else if(this.hideSiblingNodes){var i=r.modals[r.modals.length-1];i.modalRef&&(0,l.ariaHidden)(i.modalRef,!1)}return t}},{key:"isTopModal",value:function(e){return!!this.modals.length&&this.modals[this.modals.length-1]===e}}]),e}();t.default=b},55536(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"ModalManager",{enumerable:!0,get:function(){return a.default}});var i=r(n(58228)),a=r(n(2158))},16575(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.isBody=s,t.default=u;var i=r(n(7624)),a=r(n(16143)),o=r(n(62614));function s(e){return e&&"body"===e.tagName.toLowerCase()}function u(e){var t=(0,a.default)(e),n=(0,o.default)(t);if(!(0,i.default)(t)&&!s(e))return e.scrollHeight>e.clientHeight;var r=n.getComputedStyle(t.body),u=parseInt(r.getPropertyValue("margin-left"),10),c=parseInt(r.getPropertyValue("margin-right"),10);return u+t.body.clientWidth+c0?.75*r+8:0;return s.default.createElement("fieldset",(0,a.default)({"aria-hidden":!0,style:(0,a.default)((0,i.default)({},"padding".concat((0,l.capitalize)(p)),8+(c?0:b/2)),f),className:(0,u.default)(t.root,n)},h),s.default.createElement("legend",{className:t.legend,style:{width:c?b:.01}},s.default.createElement("span",{dangerouslySetInnerHTML:{__html:"​"}})))}t.styles=f;var h=(0,c.withStyles)(f,{name:"MuiPrivateNotchedOutline",withTheme:!0})(d);t.default=h},96405(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184));n(55252);var u=r(n(67598)),c=r(n(21142)),l=r(n(78252)),f=function(e){var t="light"===e.palette.type?"rgba(0, 0, 0, 0.23)":"rgba(255, 255, 255, 0.23)";return{root:{position:"relative","& $notchedOutline":{borderColor:t},"&:hover:not($disabled):not($focused):not($error) $notchedOutline":{borderColor:e.palette.text.primary,"@media (hover: none)":{borderColor:t}},"&$focused $notchedOutline":{borderColor:e.palette.primary.main,borderWidth:2},"&$error $notchedOutline":{borderColor:e.palette.error.main},"&$disabled $notchedOutline":{borderColor:e.palette.action.disabled}},focused:{},disabled:{},adornedStart:{paddingLeft:14},adornedEnd:{paddingRight:14},error:{},multiline:{padding:"18.5px 14px",boxSizing:"border-box"},notchedOutline:{},input:{padding:"18.5px 14px"},inputMarginDense:{paddingTop:15,paddingBottom:15},inputMultiline:{padding:0},inputAdornedStart:{paddingLeft:0},inputAdornedEnd:{paddingRight:0}}};function d(e){var t=e.classes,n=e.labelWidth,r=e.notched,l=(0,a.default)(e,["classes","labelWidth","notched"]);return o.default.createElement(u.default,(0,i.default)({renderPrefix:function(e){return o.default.createElement(c.default,{className:t.notchedOutline,labelWidth:n,notched:void 0!==r?r:Boolean(e.startAdornment||e.filled||e.focused)})},classes:(0,i.default)({},t,{root:(0,s.default)(t.root,t.underline),notchedOutline:null})},l))}t.styles=f,u.default.defaultProps={fullWidth:!1,inputComponent:"input",multiline:!1,type:"text"},d.muiName="Input";var h=(0,l.default)(f,{name:"MuiOutlinedInput"})(d);t.default=h},59537(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(96405))},30083(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(59713)),a=r(n(6479)),o=r(n(67154)),s=r(n(67294));r(n(45697));var u=r(n(94184));r(n(42473)),n(55252);var c=r(n(78252)),l=function(e){var t={};return e.shadows.forEach(function(e,n){t["elevation".concat(n)]={boxShadow:e}}),(0,o.default)({root:{backgroundColor:e.palette.background.paper},rounded:{borderRadius:e.shape.borderRadius}},t)};function f(e){var t=e.classes,n=e.className,r=e.component,c=e.square,l=e.elevation,f=(0,a.default)(e,["classes","className","component","square","elevation"]),d=(0,u.default)(t.root,t["elevation".concat(l)],(0,i.default)({},t.rounded,!c),n);return s.default.createElement(r,(0,o.default)({className:d},f))}t.styles=l,f.defaultProps={component:"div",elevation:2,square:!1};var d=(0,c.default)(l,{name:"MuiPaper"})(f);t.default=d},68821(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(30083))},64224(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(73935));r(n(42473));var h=r(n(20296)),p=r(n(96421));n(55252);var b=r(n(16143)),m=r(n(62614)),g=n(98741),v=r(n(78252)),y=r(n(55536)),w=r(n(261)),_=r(n(68821));function E(e,t){var n=0;return"number"==typeof t?n=t:"center"===t?n=e.height/2:"bottom"===t&&(n=e.height),n}function S(e,t){var n=0;return"number"==typeof t?n=t:"center"===t?n=e.width/2:"right"===t&&(n=e.width),n}function k(e){return[e.horizontal,e.vertical].map(function(e){return"number"==typeof e?"".concat(e,"px"):e}).join(" ")}function x(e,t){for(var n=t,r=0;n&&n!==e;)r+=(n=n.parentNode).scrollTop;return r}function T(e){return"function"==typeof e?e():e}var M={paper:{position:"absolute",overflowY:"auto",overflowX:"hidden",minWidth:16,minHeight:16,maxWidth:"calc(100% - 32px)",maxHeight:"calc(100% - 32px)",outline:"none"}};t.styles=M;var O=function(e){function t(){var e;return(0,o.default)(this,t),(e=(0,u.default)(this,(0,c.default)(t).call(this))).handleGetOffsetTop=E,e.handleGetOffsetLeft=S,e.componentWillUnmount=function(){e.handleResize.clear()},e.setPositioningStyles=function(t){var n=e.getPositioningStyle(t);null!==n.top&&(t.style.top=n.top),null!==n.left&&(t.style.left=n.left),t.style.transformOrigin=n.transformOrigin},e.getPositioningStyle=function(t){var n=e.props,r=n.anchorEl,i=n.anchorReference,a=n.marginThreshold,o=e.getContentAnchorOffset(t),s={width:t.offsetWidth,height:t.offsetHeight},u=e.getTransformOrigin(s,o);if("none"===i)return{top:null,left:null,transformOrigin:k(u)};var c=e.getAnchorOffset(o),l=c.top-u.vertical,f=c.left-u.horizontal,d=l+s.height,h=f+s.width,p=(0,m.default)(T(r)),b=p.innerHeight-a,g=p.innerWidth-a;if(lb){var y=d-b;l-=y,u.vertical+=y}if(fg){var _=h-g;f-=_,u.horizontal+=_}return{top:"".concat(l,"px"),left:"".concat(f,"px"),transformOrigin:k(u)}},e.handleEntering=function(t){e.props.onEntering&&e.props.onEntering(t),e.setPositioningStyles(t)},"undefined"!=typeof window&&(e.handleResize=(0,h.default)(function(){e.props.open&&e.setPositioningStyles(e.paperRef)},166)),e}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){this.props.action&&this.props.action({updatePosition:this.handleResize})}},{key:"getAnchorOffset",value:function(e){var t=this.props,n=t.anchorEl,r=t.anchorOrigin,i=t.anchorReference,a=t.anchorPosition;if("anchorPosition"===i)return a;var o=(T(n)||(0,b.default)(this.paperRef).body).getBoundingClientRect(),s=0===e?r.vertical:"center";return{top:o.top+this.handleGetOffsetTop(o,s),left:o.left+this.handleGetOffsetLeft(o,r.horizontal)}}},{key:"getContentAnchorOffset",value:function(e){var t=this.props,n=t.getContentAnchorEl,r=t.anchorReference,i=0;if(n&&"anchorEl"===r){var a=n(e);if(a&&e.contains(a)){var o=x(e,a);i=a.offsetTop+a.clientHeight/2-o||0}}return i}},{key:"getTransformOrigin",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=this.props.transformOrigin;return{vertical:this.handleGetOffsetTop(e,n.vertical)+t,horizontal:this.handleGetOffsetLeft(e,n.horizontal)}}},{key:"render",value:function(){var e=this,t=this.props,n=(t.action,t.anchorEl),r=(t.anchorOrigin,t.anchorPosition,t.anchorReference,t.children),o=t.classes,s=t.container,u=t.elevation,c=(t.getContentAnchorEl,t.marginThreshold,t.ModalClasses),l=t.onEnter,h=t.onEntered,m=(t.onEntering,t.onExit),v=t.onExited,w=t.onExiting,E=t.open,S=t.PaperProps,k=t.role,x=(t.transformOrigin,t.TransitionComponent),M=t.transitionDuration,O=t.TransitionProps,A=void 0===O?{}:O,L=(0,a.default)(t,["action","anchorEl","anchorOrigin","anchorPosition","anchorReference","children","classes","container","elevation","getContentAnchorEl","marginThreshold","ModalClasses","onEnter","onEntered","onEntering","onExit","onExited","onExiting","open","PaperProps","role","transformOrigin","TransitionComponent","transitionDuration","TransitionProps"]),C=M;"auto"!==M||x.muiSupportAuto||(C=void 0);var I=s||(n?(0,b.default)(T(n)).body:void 0);return f.default.createElement(y.default,(0,i.default)({classes:c,container:I,open:E,BackdropProps:{invisible:!0}},L),f.default.createElement(x,(0,i.default)({appear:!0,in:E,onEnter:l,onEntered:h,onExit:m,onExited:v,onExiting:w,role:k,timeout:C},A,{onEntering:(0,g.createChainedFunction)(this.handleEntering,A.onEntering)}),f.default.createElement(_.default,(0,i.default)({className:o.paper,elevation:u,ref:function(t){e.paperRef=d.default.findDOMNode(t)}},S),f.default.createElement(p.default,{target:"window",onResize:this.handleResize}),r)))}}]),t}(f.default.Component);O.defaultProps={anchorReference:"anchorEl",anchorOrigin:{vertical:"top",horizontal:"left"},elevation:8,marginThreshold:16,transformOrigin:{vertical:"top",horizontal:"left"},TransitionComponent:w.default,transitionDuration:"auto"};var A=(0,v.default)(M,{name:"MuiPopover"})(O);t.default=A},50810(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(64224))},24693(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(6479)),a=r(n(67154)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(81506)),d=r(n(67294)),h=r(n(73935));r(n(45697));var p=r(n(28981)),b=r(n(25649));function m(e){if("rtl"!==("undefined"!=typeof window&&document.body.getAttribute("dir")||"ltr"))return e;switch(e){case"bottom-end":return"bottom-start";case"bottom-start":return"bottom-end";case"top-end":return"top-start";case"top-start":return"top-end";default:return e}}function g(e){return"function"==typeof e?e():e}var v=function(e){function t(e){var n;return(0,o.default)(this,t),(n=(0,u.default)(this,(0,c.default)(t).call(this))).handleOpen=function(){var e=n.props,t=e.anchorEl,r=e.modifiers,i=e.open,o=e.placement,s=e.popperOptions,u=void 0===s?{}:s,c=e.disablePortal,l=h.default.findDOMNode((0,f.default)((0,f.default)(n)));l&&t&&i&&(n.popper&&(n.popper.destroy(),n.popper=null),n.popper=new p.default(g(t),l,(0,a.default)({placement:m(o)},u,{modifiers:(0,a.default)({},c?{}:{preventOverflow:{boundariesElement:"window"}},r,u.modifiers),onCreate:n.handlePopperUpdate,onUpdate:n.handlePopperUpdate})))},n.handlePopperUpdate=function(e){e.placement!==n.state.placement&&n.setState({placement:e.placement})},n.handleExited=function(){n.setState({exited:!0}),n.handleClose()},n.handleClose=function(){n.popper&&(n.popper.destroy(),n.popper=null)},n.state={exited:!e.open},n}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidUpdate",value:function(e){e.open===this.props.open||this.props.open||this.props.transition||this.handleClose(),(e.open!==this.props.open||e.anchorEl!==this.props.anchorEl||e.popperOptions!==this.props.popperOptions||e.modifiers!==this.props.modifiers||e.disablePortal!==this.props.disablePortal||e.placement!==this.props.placement)&&this.handleOpen()}},{key:"componentWillUnmount",value:function(){this.handleClose()}},{key:"render",value:function(){var e=this.props,t=(e.anchorEl,e.children),n=e.container,r=e.disablePortal,o=e.keepMounted,s=(e.modifiers,e.open),u=e.placement,c=(e.popperOptions,e.transition),l=(0,i.default)(e,["anchorEl","children","container","disablePortal","keepMounted","modifiers","open","placement","popperOptions","transition"]),f=this.state,h=f.exited,p=f.placement;if(!o&&!s&&(!c||h))return null;var g={placement:p||m(u)};return c&&(g.TransitionProps={in:s,onExited:this.handleExited}),d.default.createElement(b.default,{onRendered:this.handleOpen,disablePortal:r,container:n},d.default.createElement("div",(0,a.default)({role:"tooltip",style:{position:"absolute"}},l),"function"==typeof t?t(g):t))}}],[{key:"getDerivedStateFromProps",value:function(e){return e.open?{exited:!1}:e.transition?null:{exited:!0}}}]),t}(d.default.Component);v.defaultProps={disablePortal:!1,placement:"bottom",transition:!1};var y=v;t.default=y},60111(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(24693))},92261(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(34575)),a=r(n(93913)),o=r(n(78585)),s=r(n(29754)),u=r(n(2205)),c=r(n(67294)),l=r(n(73935));r(n(45697));var f=r(n(16143));function d(e,t){return e="function"==typeof e?e():e,l.default.findDOMNode(e)||t}function h(e){return(0,f.default)(l.default.findDOMNode(e))}n(55252);var p=function(e){function t(){(0,i.default)(this,t);for(var e,n,r=arguments.length,a=Array(r),u=0;u1;n.state.labelWrapped!==e&&n.setState({labelWrapped:e})}},n}return(0,c.default)(t,e),(0,o.default)(t,[{key:"componentDidMount",value:function(){this.checkTextWrap()}},{key:"componentDidUpdate",value:function(e,t){this.state.labelWrapped===t.labelWrapped&&this.checkTextWrap()}},{key:"render",value:function(){var e,t,n=this,r=this.props,a=r.classes,o=r.className,s=r.disabled,u=r.fullWidth,c=r.icon,p=r.indicator,g=r.label,v=(r.onChange,r.selected),y=r.textColor,w=(r.value,(0,i.default)(r,["classes","className","disabled","fullWidth","icon","indicator","label","onChange","selected","textColor","value"]));return void 0!==g&&(e=d.default.createElement("span",{className:a.labelContainer},d.default.createElement("span",{className:(0,h.default)(a.label,(0,l.default)({},a.labelWrapped,this.state.labelWrapped)),ref:function(e){n.labelRef=e}},g))),d.default.createElement(b.default,(0,f.default)({focusRipple:!0,className:(0,h.default)(a.root,a["textColor".concat((0,m.capitalize)(y))],(t={},(0,l.default)(t,a.disabled,s),(0,l.default)(t,a.selected,v),(0,l.default)(t,a.labelIcon,c&&e),(0,l.default)(t,a.fullWidth,u),t),o),role:"tab","aria-selected":v,disabled:s},w,{onClick:this.handleChange}),d.default.createElement("span",{className:a.wrapper},c,e),p)}}]),t}(d.default.Component);v.defaultProps={disabled:!1,textColor:"inherit"};var y=(0,p.default)(g,{name:"MuiTab"})(v);t.default=y},75759(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(70201))},7575(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(94184));n(55252);var h=r(n(78252)),p=r(n(82577)),b=function(e){return{root:{display:"table",fontFamily:e.typography.fontFamily,width:"100%",borderCollapse:"collapse",borderSpacing:0}}};t.styles=b;var m=function(e){function t(){(0,o.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;ai&&n(null,i)}},{key:"render",value:function(){var e,t=this.props,n=t.ActionsComponent,r=t.backIconButtonProps,o=t.classes,s=t.colSpan,u=t.component,c=t.count,l=t.labelDisplayedRows,d=t.labelRowsPerPage,y=t.nextIconButtonProps,w=t.onChangePage,_=t.onChangeRowsPerPage,E=t.page,S=t.rowsPerPage,k=t.rowsPerPageOptions,x=t.SelectProps,T=void 0===x?{}:x,M=(0,a.default)(t,["ActionsComponent","backIconButtonProps","classes","colSpan","component","count","labelDisplayedRows","labelRowsPerPage","nextIconButtonProps","onChangePage","onChangeRowsPerPage","page","rowsPerPage","rowsPerPageOptions","SelectProps"]);(u===m.default||"td"===u)&&(e=s||1e3);var O=T.native?"option":p.default;return f.default.createElement(u,(0,i.default)({className:o.root,colSpan:e},M),f.default.createElement(g.default,{className:o.toolbar},f.default.createElement("div",{className:o.spacer}),k.length>1&&f.default.createElement(v.default,{color:"inherit",variant:"caption",className:o.caption},d),k.length>1&&f.default.createElement(b.default,(0,i.default)({classes:{root:o.selectRoot,select:o.select,icon:o.selectIcon},input:f.default.createElement(h.default,{className:o.input}),value:S,onChange:_},T),k.map(function(e){return f.default.createElement(O,{className:o.menuItem,key:e,value:e},e)})),f.default.createElement(v.default,{color:"inherit",variant:"caption",className:o.caption},l({from:0===c?0:E*S+1,to:Math.min(c,(E+1)*S),count:c,page:E})),f.default.createElement(n,{className:o.actions,backIconButtonProps:r,count:c,nextIconButtonProps:y,onChangePage:w,page:E,rowsPerPage:S})))}}]),t}(f.default.Component);_.defaultProps={ActionsComponent:y.default,component:m.default,labelDisplayedRows:function(e){var t=e.from,n=e.to,r=e.count;return"".concat(t,"-").concat(n," of ").concat(r)},labelRowsPerPage:"Rows per page:",rowsPerPageOptions:[10,25,50,100]};var E=(0,d.default)(w,{name:"MuiTablePagination"})(_);t.default=E},32844(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(86861)),h=r(n(43836)),p=r(n(82313)),b=r(n(81701)),m=f.default.createElement(h.default,null),g=f.default.createElement(d.default,null),v=f.default.createElement(d.default,null),y=f.default.createElement(h.default,null),w=function(e){function t(){(0,o.default)(this,t);for(var e,n,r=arguments.length,i=Array(r),a=0;a=Math.ceil(n/s)-1,color:"inherit"},r),"rtl"===u.direction?v:y))}}]),t}(f.default.Component),_=(0,p.default)()(w);t.default=_},18217(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(71744))},86424(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=r(n(27628)),f=function(e){return{root:{color:"inherit",display:"table-row",height:48,verticalAlign:"middle",outline:"none","&$selected":{backgroundColor:"light"===e.palette.type?"rgba(0, 0, 0, 0.04)":"rgba(255, 255, 255, 0.08)"},"&$hover:hover":{backgroundColor:"light"===e.palette.type?"rgba(0, 0, 0, 0.07)":"rgba(255, 255, 255, 0.14)"}},selected:{},hover:{},head:{height:56},footer:{height:56}}};function d(e){var t=e.classes,n=e.className,r=e.component,c=e.hover,f=e.selected,d=(0,o.default)(e,["classes","className","component","hover","selected"]);return s.default.createElement(l.default.Consumer,null,function(e){var o,l=(0,u.default)(t.root,(o={},(0,a.default)(o,t.head,e&&"head"===e.variant),(0,a.default)(o,t.footer,e&&"footer"===e.variant),(0,a.default)(o,t.hover,c),(0,a.default)(o,t.selected,f),o),n);return s.default.createElement(r,(0,i.default)({className:l},d))})}t.styles=f,d.defaultProps={component:"tr",hover:!1,selected:!1};var h=(0,c.default)(f,{name:"MuiTableRow"})(d);t.default=h},17175(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(86424))},28550(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(34575)),a=r(n(93913)),o=r(n(78585)),s=r(n(29754)),u=r(n(2205)),c=r(n(67294));r(n(45697));var l,f=r(n(96421)),d=r(n(20296)),h={width:90,height:90,position:"absolute",top:-9e3,overflow:"scroll",msOverflowStyle:"scrollbar"},p=function(e){function t(){var e;return(0,i.default)(this,t),(e=(0,o.default)(this,(0,s.default)(t).call(this))).handleRef=function(t){e.nodeRef=t},e.setMeasurements=function(){var t=e.nodeRef;t&&(e.scrollbarHeight=t.offsetHeight-t.clientHeight)},"undefined"!=typeof window&&(e.handleResize=(0,d.default)(function(){var t=e.scrollbarHeight;e.setMeasurements(),t!==e.scrollbarHeight&&e.props.onChange(e.scrollbarHeight)},166)),e}return(0,u.default)(t,e),(0,a.default)(t,[{key:"componentDidMount",value:function(){this.setMeasurements(),this.props.onChange(this.scrollbarHeight)}},{key:"componentWillUnmount",value:function(){this.handleResize.clear()}},{key:"render",value:function(){return c.default.createElement(c.default.Fragment,null,c.default.createElement(f.default,{target:"window",onResize:this.handleResize}),c.default.createElement("div",{style:h,ref:this.handleRef}))}}]),t}(c.default.Component);t.default=p},12417(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184)),u=r(n(78252)),c=n(98741),l=function(e){return{root:{position:"absolute",height:2,bottom:0,width:"100%",transition:e.transitions.create()},colorPrimary:{backgroundColor:e.palette.primary.main},colorSecondary:{backgroundColor:e.palette.secondary.main}}};function f(e){var t=e.classes,n=e.className,r=e.color,u=(0,a.default)(e,["classes","className","color"]);return o.default.createElement("span",(0,i.default)({className:(0,s.default)(t.root,t["color".concat((0,c.capitalize)(r))],n)},u))}t.styles=l;var d=(0,u.default)(l,{name:"MuiPrivateTabIndicator"})(f);t.default=d},69583(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(67294));r(n(45697));var s=r(n(94184)),u=r(n(86861)),c=r(n(43836)),l=r(n(78252)),f=r(n(16070)),d={root:{color:"inherit",width:56,flexShrink:0}};t.styles=d;var h=o.default.createElement(u.default,null),p=o.default.createElement(c.default,null);function b(e){var t=e.classes,n=e.className,r=e.direction,u=e.onClick,c=e.visible,l=(0,a.default)(e,["classes","className","direction","onClick","visible"]),d=(0,s.default)(t.root,n);return c?o.default.createElement(f.default,(0,i.default)({className:d,onClick:u,tabIndex:-1},l),"left"===r?h:p):o.default.createElement("div",{className:d})}b.defaultProps={visible:!0};var m=(0,l.default)(d,{name:"MuiPrivateTabScrollButton"})(b);t.default=m},89172(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(59713)),d=r(n(67294));r(n(45697)),r(n(42473));var h=r(n(94184)),p=r(n(96421)),b=r(n(20296)),m=n(46417);n(55252);var g=r(n(13329)),v=r(n(28550)),y=r(n(78252)),w=r(n(12417)),_=r(n(69583));r(n(346));var E=function(e){return{root:{overflow:"hidden",minHeight:48,WebkitOverflowScrolling:"touch"},flexContainer:{display:"flex"},centered:{justifyContent:"center"},scroller:{position:"relative",display:"inline-block",flex:"1 1 auto",whiteSpace:"nowrap"},fixed:{overflowX:"hidden",width:"100%"},scrollable:{overflowX:"scroll"},scrollButtons:{},scrollButtonsAuto:(0,f.default)({},e.breakpoints.down("xs"),{display:"none"}),indicator:{}}};t.styles=E;var S=function(e){function t(){var e;return(0,o.default)(this,t),(e=(0,u.default)(this,(0,c.default)(t).call(this))).state={indicatorStyle:{},scrollerStyle:{marginBottom:0},showLeftScroll:!1,showRightScroll:!1,mounted:!1},e.getConditionalElements=function(){var t=e.props,n=t.classes,r=t.scrollable,i=t.ScrollButtonComponent,a=t.scrollButtons,o=t.theme,s=t.variant,u={},c="scrollable"===s||r;u.scrollbarSizeListener=c?d.default.createElement(v.default,{onChange:e.handleScrollbarSizeChange}):null;var l=c&&("auto"===a||"on"===a);return u.scrollButtonLeft=l?d.default.createElement(i,{direction:o&&"rtl"===o.direction?"right":"left",onClick:e.handleLeftScrollClick,visible:e.state.showLeftScroll,className:(0,h.default)(n.scrollButtons,(0,f.default)({},n.scrollButtonsAuto,"auto"===a))}):null,u.scrollButtonRight=l?d.default.createElement(i,{direction:o&&"rtl"===o.direction?"left":"right",onClick:e.handleRightScrollClick,visible:e.state.showRightScroll,className:(0,h.default)(n.scrollButtons,(0,f.default)({},n.scrollButtonsAuto,"auto"===a))}):null,u},e.getTabsMeta=function(t,n){if(e.tabsRef){var r,i,a=e.tabsRef.getBoundingClientRect();r={clientWidth:e.tabsRef.clientWidth,scrollLeft:e.tabsRef.scrollLeft,scrollLeftNormalized:(0,m.getNormalizedScrollLeft)(e.tabsRef,n),scrollWidth:e.tabsRef.scrollWidth,left:a.left,right:a.right}}if(e.tabsRef&&!1!==t){var o=e.tabsRef.children[0].children;if(o.length>0){var s=o[e.valueToIndex.get(t)];i=s?s.getBoundingClientRect():null}}return{tabsMeta:r,tabMeta:i}},e.handleLeftScrollClick=function(){e.moveTabsScroll(-e.tabsRef.clientWidth)},e.handleRightScrollClick=function(){e.moveTabsScroll(e.tabsRef.clientWidth)},e.handleScrollbarSizeChange=function(t){e.setState({scrollerStyle:{marginBottom:-t}})},e.moveTabsScroll=function(t){var n=e.props.theme,r="rtl"===n.direction?-1:1,i=e.tabsRef.scrollLeft+t*r,a="rtl"===n.direction&&"reverse"===(0,m.detectScrollType)()?-1:1;e.scroll(a*i)},e.scrollSelectedIntoView=function(){var t=e.props,n=t.theme,r=t.value,i=e.getTabsMeta(r,n.direction),a=i.tabsMeta,o=i.tabMeta;if(o&&a){if(o.lefta.right){var u=a.scrollLeft+(o.right-a.right);e.scroll(u)}}},e.scroll=function(t){(0,g.default)("scrollLeft",e.tabsRef,t)},e.updateScrollButtonState=function(){var t=e.props,n=t.scrollable,r=t.scrollButtons,i=t.theme;if(("scrollable"===t.variant||n)&&"off"!==r){var a=e.tabsRef,o=a.scrollWidth,s=a.clientWidth,u=(0,m.getNormalizedScrollLeft)(e.tabsRef,i.direction),c="rtl"===i.direction?o>s+u:u>0,l="rtl"===i.direction?u>0:o>s+u;(c!==e.state.showLeftScroll||l!==e.state.showRightScroll)&&e.setState({showLeftScroll:c,showRightScroll:l})}},"undefined"!=typeof window&&(e.handleResize=(0,b.default)(function(){e.updateIndicatorState(e.props),e.updateScrollButtonState()},166),e.handleTabsScroll=(0,b.default)(function(){e.updateScrollButtonState()},166)),e}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){this.setState({mounted:!0}),this.updateIndicatorState(this.props),this.updateScrollButtonState(),this.props.action&&this.props.action({updateIndicator:this.handleResize})}},{key:"componentDidUpdate",value:function(e,t){this.updateIndicatorState(this.props),this.updateScrollButtonState(),this.state.indicatorStyle!==t.indicatorStyle&&this.scrollSelectedIntoView()}},{key:"componentWillUnmount",value:function(){this.handleResize.clear(),this.handleTabsScroll.clear()}},{key:"updateIndicatorState",value:function(e){var t=e.theme,n=e.value,r=this.getTabsMeta(n,t.direction),i=r.tabsMeta,a=r.tabMeta,o=0;if(a&&i){var s="rtl"===t.direction?i.scrollLeftNormalized+i.clientWidth-i.scrollWidth:i.scrollLeft;o=Math.round(a.left-i.left+s)}var u={left:o,width:a?Math.round(a.width):0};u.left===this.state.indicatorStyle.left&&u.width===this.state.indicatorStyle.width||isNaN(u.left)||isNaN(u.width)||this.setState({indicatorStyle:u})}},{key:"render",value:function(){var e,t=this,n=this.props,r=(n.action,n.centered),o=n.children,s=n.classes,u=n.className,c=n.component,l=n.fullWidth,b=void 0!==l&&l,m=n.indicatorColor,g=n.onChange,v=n.scrollable,y=void 0!==v&&v,_=(n.ScrollButtonComponent,n.scrollButtons,n.TabIndicatorProps),E=void 0===_?{}:_,S=n.textColor,k=(n.theme,n.value),x=n.variant,T=(0,a.default)(n,["action","centered","children","classes","className","component","fullWidth","indicatorColor","onChange","scrollable","ScrollButtonComponent","scrollButtons","TabIndicatorProps","textColor","theme","value","variant"]),M="scrollable"===x||y,O=(0,h.default)(s.root,u),A=(0,h.default)(s.flexContainer,(0,f.default)({},s.centered,r&&!M)),L=(0,h.default)(s.scroller,(e={},(0,f.default)(e,s.fixed,!M),(0,f.default)(e,s.scrollable,M),e)),C=d.default.createElement(w.default,(0,i.default)({className:s.indicator,color:m},E,{style:(0,i.default)({},this.state.indicatorStyle,E.style)}));this.valueToIndex=new Map;var I=0,D=d.default.Children.map(o,function(e){if(!d.default.isValidElement(e))return null;var n=void 0===e.props.value?I:e.props.value;t.valueToIndex.set(n,I);var r=n===k;return I+=1,d.default.cloneElement(e,{fullWidth:"fullWidth"===x||b,indicator:r&&!t.state.mounted&&C,selected:r,onChange:g,textColor:S,value:n})}),N=this.getConditionalElements();return d.default.createElement(c,(0,i.default)({className:O},T),d.default.createElement(p.default,{target:"window",onResize:this.handleResize}),N.scrollbarSizeListener,d.default.createElement("div",{className:s.flexContainer},N.scrollButtonLeft,d.default.createElement("div",{className:L,style:this.state.scrollerStyle,ref:function(e){t.tabsRef=e},role:"tablist",onScroll:this.handleTabsScroll},d.default.createElement("div",{className:A},D),this.state.mounted&&C),N.scrollButtonRight))}}]),t}(d.default.Component);S.defaultProps={centered:!1,component:"div",indicatorColor:"secondary",ScrollButtonComponent:_.default,scrollButtons:"auto",textColor:"inherit",variant:"standard"};var k=(0,y.default)(E,{name:"MuiTabs",withTheme:!0})(S);t.default=k},12794(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(89172))},78592(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294)),d=r(n(73935));r(n(42473)),r(n(45697));var h=r(n(54846)),p=r(n(1402)),b=r(n(59537)),m=r(n(23153)),g=r(n(85461)),v=r(n(76023)),y=r(n(11970)),w={standard:h.default,filled:p.default,outlined:b.default},_=function(e){function t(e){var n;return(0,o.default)(this,t),(n=(0,u.default)(this,(0,c.default)(t).call(this,e))).labelRef=f.default.createRef(),n}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){"outlined"===this.props.variant&&(this.labelNode=d.default.findDOMNode(this.labelRef.current),this.forceUpdate())}},{key:"render",value:function(){var e=this.props,t=e.autoComplete,n=e.autoFocus,r=e.children,o=e.className,s=e.defaultValue,u=e.error,c=e.FormHelperTextProps,l=e.fullWidth,d=e.helperText,h=e.id,p=e.InputLabelProps,b=e.inputProps,_=e.InputProps,E=e.inputRef,S=e.label,k=e.multiline,x=e.name,T=e.onBlur,M=e.onChange,O=e.onFocus,A=e.placeholder,L=e.required,C=e.rows,I=e.rowsMax,D=e.select,N=e.SelectProps,P=e.type,R=e.value,j=e.variant,F=(0,a.default)(e,["autoComplete","autoFocus","children","className","defaultValue","error","FormHelperTextProps","fullWidth","helperText","id","InputLabelProps","inputProps","InputProps","inputRef","label","multiline","name","onBlur","onChange","onFocus","placeholder","required","rows","rowsMax","select","SelectProps","type","value","variant"]),Y={};"outlined"===j&&(p&&void 0!==p.shrink&&(Y.notched=p.shrink),Y.labelWidth=this.labelNode&&this.labelNode.offsetWidth||0);var B=d&&h?"".concat(h,"-helper-text"):void 0,U=w[j],H=f.default.createElement(U,(0,i.default)({"aria-describedby":B,autoComplete:t,autoFocus:n,defaultValue:s,fullWidth:l,multiline:k,name:x,rows:C,rowsMax:I,type:P,value:R,id:h,inputRef:E,onBlur:T,onChange:M,onFocus:O,placeholder:A,inputProps:b},Y,_));return f.default.createElement(g.default,(0,i.default)({className:o,error:u,fullWidth:l,required:L,variant:j},F),S&&f.default.createElement(m.default,(0,i.default)({htmlFor:h,ref:this.labelRef},p),S),D?f.default.createElement(y.default,(0,i.default)({"aria-describedby":B,value:R,input:H},N),r):H,d&&f.default.createElement(v.default,(0,i.default)({id:B},c),d))}}]),t}(f.default.Component);_.defaultProps={required:!1,select:!1,variant:"standard"};var E=_;t.default=E},60520(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(78592))},48596(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184)),c=r(n(78252)),l=function(e){return{root:{position:"relative",display:"flex",alignItems:"center"},gutters:e.mixins.gutters(),regular:e.mixins.toolbar,dense:{minHeight:48}}};function f(e){var t=e.children,n=e.classes,r=e.className,c=e.disableGutters,l=e.variant,f=(0,o.default)(e,["children","classes","className","disableGutters","variant"]),d=(0,u.default)(n.root,n[l],(0,a.default)({},n.gutters,!c),r);return s.default.createElement("div",(0,i.default)({className:d},f),t)}t.styles=l,f.defaultProps={disableGutters:!1,variant:"regular"};var d=(0,c.default)(l,{name:"MuiToolbar"})(f);t.default=d},28902(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(48596))},83065(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(59713)),d=r(n(67294));r(n(45697)),r(n(42473));var h=r(n(94184));n(55252);var p=r(n(39737)),b=r(n(78252)),m=n(98741),g=r(n(261)),v=r(n(60111)),y=function(e){return{popper:{zIndex:e.zIndex.tooltip,opacity:.9,pointerEvents:"none"},popperInteractive:{pointerEvents:"auto"},tooltip:{backgroundColor:e.palette.grey[700],borderRadius:e.shape.borderRadius,color:e.palette.common.white,fontFamily:e.typography.fontFamily,padding:"4px 8px",fontSize:e.typography.pxToRem(10),lineHeight:"".concat(e.typography.round(1.4),"em"),maxWidth:300},touch:{padding:"8px 16px",fontSize:e.typography.pxToRem(14),lineHeight:"".concat(e.typography.round(16/14),"em")},tooltipPlacementLeft:(0,f.default)({transformOrigin:"right center",margin:"0 24px "},e.breakpoints.up("sm"),{margin:"0 14px"}),tooltipPlacementRight:(0,f.default)({transformOrigin:"left center",margin:"0 24px"},e.breakpoints.up("sm"),{margin:"0 14px"}),tooltipPlacementTop:(0,f.default)({transformOrigin:"center bottom",margin:"24px 0"},e.breakpoints.up("sm"),{margin:"14px 0"}),tooltipPlacementBottom:(0,f.default)({transformOrigin:"center top",margin:"24px 0"},e.breakpoints.up("sm"),{margin:"14px 0"})}};t.styles=y;var w=function(e){function t(e){var n;return(0,o.default)(this,t),(n=(0,u.default)(this,(0,c.default)(t).call(this))).ignoreNonTouchEvents=!1,n.onRootRef=function(e){n.childrenRef=e},n.handleFocus=function(e){n.childrenRef||(n.childrenRef=e.currentTarget),n.handleEnter(e);var t=n.props.children.props;t.onFocus&&t.onFocus(e)},n.handleEnter=function(e){var t=n.props,r=t.children,i=t.enterDelay,a=r.props;"mouseover"===e.type&&a.onMouseOver&&a.onMouseOver(e),(!n.ignoreNonTouchEvents||"touchstart"===e.type)&&(n.childrenRef.setAttribute("title",""),clearTimeout(n.enterTimer),clearTimeout(n.leaveTimer),i?(e.persist(),n.enterTimer=setTimeout(function(){n.handleOpen(e)},i)):n.handleOpen(e))},n.handleOpen=function(e){n.isControlled||n.state.open||n.setState({open:!0}),n.props.onOpen&&n.props.onOpen(e)},n.handleLeave=function(e){var t=n.props,r=t.children,i=t.leaveDelay,a=r.props;"blur"===e.type&&a.onBlur&&a.onBlur(e),"mouseleave"===e.type&&a.onMouseLeave&&a.onMouseLeave(e),clearTimeout(n.enterTimer),clearTimeout(n.leaveTimer),i?(e.persist(),n.leaveTimer=setTimeout(function(){n.handleClose(e)},i)):n.handleClose(e)},n.handleClose=function(e){n.isControlled||n.setState({open:!1}),n.props.onClose&&n.props.onClose(e),clearTimeout(n.closeTimer),n.closeTimer=setTimeout(function(){n.ignoreNonTouchEvents=!1},n.props.theme.transitions.duration.shortest)},n.handleTouchStart=function(e){n.ignoreNonTouchEvents=!0;var t=n.props,r=t.children,i=t.enterTouchDelay;r.props.onTouchStart&&r.props.onTouchStart(e),clearTimeout(n.leaveTimer),clearTimeout(n.closeTimer),clearTimeout(n.touchTimer),e.persist(),n.touchTimer=setTimeout(function(){n.handleEnter(e)},i)},n.handleTouchEnd=function(e){var t=n.props,r=t.children,i=t.leaveTouchDelay;r.props.onTouchEnd&&r.props.onTouchEnd(e),clearTimeout(n.touchTimer),clearTimeout(n.leaveTimer),e.persist(),n.leaveTimer=setTimeout(function(){n.handleClose(e)},i)},n.isControlled=null!=e.open,n.state={open:null},n.isControlled||(n.state.open=!1),n}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){this.defaultId="mui-tooltip-".concat(Math.round(1e5*Math.random())),this.props.open&&this.forceUpdate()}},{key:"componentWillUnmount",value:function(){clearTimeout(this.closeTimer),clearTimeout(this.enterTimer),clearTimeout(this.focusTimer),clearTimeout(this.leaveTimer),clearTimeout(this.touchTimer)}},{key:"render",value:function(){var e=this,t=this.props,n=t.children,r=t.classes,o=t.disableFocusListener,s=t.disableHoverListener,u=t.disableTouchListener,c=(t.enterDelay,t.enterTouchDelay,t.id),l=t.interactive,b=(t.leaveDelay,t.leaveTouchDelay,t.onClose,t.onOpen,t.open),g=t.placement,y=t.PopperProps,w=t.theme,_=t.title,E=t.TransitionComponent,S=t.TransitionProps,k=(0,a.default)(t,["children","classes","disableFocusListener","disableHoverListener","disableTouchListener","enterDelay","enterTouchDelay","id","interactive","leaveDelay","leaveTouchDelay","onClose","onOpen","open","placement","PopperProps","theme","title","TransitionComponent","TransitionProps"]),x=this.isControlled?b:this.state.open;""===_&&(x=!1);var T=!x&&!s,M=(0,i.default)({"aria-describedby":x?c||this.defaultId:null,title:T&&"string"==typeof _?_:null},k,n.props,{className:(0,h.default)(k.className,n.props.className)});u||(M.onTouchStart=this.handleTouchStart,M.onTouchEnd=this.handleTouchEnd),s||(M.onMouseOver=this.handleEnter,M.onMouseLeave=this.handleLeave),o||(M.onFocus=this.handleFocus,M.onBlur=this.handleLeave);var O=l?{onMouseOver:M.onMouseOver,onMouseLeave:M.onMouseLeave,onFocus:M.onFocus,onBlur:M.onBlur}:{};return d.default.createElement(d.default.Fragment,null,d.default.createElement(p.default,{rootRef:this.onRootRef},d.default.cloneElement(n,M)),d.default.createElement(v.default,(0,i.default)({className:(0,h.default)(r.popper,(0,f.default)({},r.popperInteractive,l)),placement:g,anchorEl:this.childrenRef,open:x,id:M["aria-describedby"],transition:!0},O,y),function(t){var n=t.placement,a=t.TransitionProps;return d.default.createElement(E,(0,i.default)({timeout:w.transitions.duration.shorter},a,S),d.default.createElement("div",{className:(0,h.default)(r.tooltip,(0,f.default)({},r.touch,e.ignoreNonTouchEvents),r["tooltipPlacement".concat((0,m.capitalize)(n.split("-")[0]))])},_))}))}}]),t}(d.default.Component);w.defaultProps={disableFocusListener:!1,disableHoverListener:!1,disableTouchListener:!1,enterDelay:0,enterTouchDelay:1e3,interactive:!1,leaveDelay:0,leaveTouchDelay:1500,placement:"bottom",TransitionComponent:g.default};var _=(0,b.default)(y,{name:"MuiTooltip",withTheme:!0})(w);t.default=_},31657(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"Z",{enumerable:!0,get:function(){return a.default}});var a=i(n(83065))},49476(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(67294));r(n(45697));var u=r(n(94184));n(55252);var c=r(n(78252)),l=n(98741),f=function(e){return{root:{display:"block",margin:0},display4:e.typography.display4,display3:e.typography.display3,display2:e.typography.display2,display1:e.typography.display1,headline:e.typography.headline,title:e.typography.title,subheading:e.typography.subheading,body2:e.typography.body2,body1:e.typography.body1,caption:e.typography.caption,button:e.typography.button,h1:e.typography.h1,h2:e.typography.h2,h3:e.typography.h3,h4:e.typography.h4,h5:e.typography.h5,h6:e.typography.h6,subtitle1:e.typography.subtitle1,subtitle2:e.typography.subtitle2,overline:e.typography.overline,srOnly:{position:"absolute",height:1,width:1,overflow:"hidden"},alignLeft:{textAlign:"left"},alignCenter:{textAlign:"center"},alignRight:{textAlign:"right"},alignJustify:{textAlign:"justify"},noWrap:{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},gutterBottom:{marginBottom:"0.35em"},paragraph:{marginBottom:16},colorInherit:{color:"inherit"},colorPrimary:{color:e.palette.primary.main},colorSecondary:{color:e.palette.secondary.main},colorTextPrimary:{color:e.palette.text.primary},colorTextSecondary:{color:e.palette.text.secondary},colorError:{color:e.palette.error.main},inline:{display:"inline"}}};t.styles=f;var d={display4:"h1",display3:"h2",display2:"h3",display1:"h4",headline:"h5",title:"h6",subheading:"subtitle1"};function h(e,t){var n=e.typography,r=t;return r||(r=n.useNextVariants?"body2":"body1"),n.useNextVariants&&(r=d[r]||r),r}var p={h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",h6:"h6",subtitle1:"h6",subtitle2:"h6",body1:"p",body2:"p",display4:"h1",display3:"h1",display2:"h1",display1:"h1",headline:"h1",title:"h2",subheading:"h3"};function b(e){var t,n=e.align,r=e.classes,c=e.className,f=e.color,d=e.component,b=e.gutterBottom,m=e.headlineMapping,g=e.inline,v=(e.internalDeprecatedVariant,e.noWrap),y=e.paragraph,w=e.theme,_=e.variant,E=(0,o.default)(e,["align","classes","className","color","component","gutterBottom","headlineMapping","inline","internalDeprecatedVariant","noWrap","paragraph","theme","variant"]),S=h(w,_),k=(0,u.default)(r.root,(t={},(0,a.default)(t,r[S],"inherit"!==S),(0,a.default)(t,r["color".concat((0,l.capitalize)(f))],"default"!==f),(0,a.default)(t,r.noWrap,v),(0,a.default)(t,r.gutterBottom,b),(0,a.default)(t,r.paragraph,y),(0,a.default)(t,r["align".concat((0,l.capitalize)(n))],"inherit"!==n),(0,a.default)(t,r.inline,g),t),c),x=d||(y?"p":m[S]||p[S])||"span";return s.default.createElement(x,(0,i.default)({className:k},E))}b.defaultProps={align:"inherit",color:"default",gutterBottom:!1,headlineMapping:p,inline:!1,noWrap:!1,paragraph:!1};var m=(0,c.default)(f,{name:"MuiTypography",withTheme:!0})(b);t.default=m},71426(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return i.default}});var i=r(n(49476))},8070(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#fff8e1",100:"#ffecb3",200:"#ffe082",300:"#ffd54f",400:"#ffca28",500:"#ffc107",600:"#ffb300",700:"#ffa000",800:"#ff8f00",900:"#ff6f00",A100:"#ffe57f",A200:"#ffd740",A400:"#ffc400",A700:"#ffab00"};t.default=n},63259(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#e3f2fd",100:"#bbdefb",200:"#90caf9",300:"#64b5f6",400:"#42a5f5",500:"#2196f3",600:"#1e88e5",700:"#1976d2",800:"#1565c0",900:"#0d47a1",A100:"#82b1ff",A200:"#448aff",A400:"#2979ff",A700:"#2962ff"};t.default=n},38236(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#eceff1",100:"#cfd8dc",200:"#b0bec5",300:"#90a4ae",400:"#78909c",500:"#607d8b",600:"#546e7a",700:"#455a64",800:"#37474f",900:"#263238",A100:"#cfd8dc",A200:"#b0bec5",A400:"#78909c",A700:"#455a64"};t.default=n},60169(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#efebe9",100:"#d7ccc8",200:"#bcaaa4",300:"#a1887f",400:"#8d6e63",500:"#795548",600:"#6d4c41",700:"#5d4037",800:"#4e342e",900:"#3e2723",A100:"#d7ccc8",A200:"#bcaaa4",A400:"#8d6e63",A700:"#5d4037"};t.default=n},515(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={black:"#000",white:"#fff"};t.default=n},57646(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#e0f7fa",100:"#b2ebf2",200:"#80deea",300:"#4dd0e1",400:"#26c6da",500:"#00bcd4",600:"#00acc1",700:"#0097a7",800:"#00838f",900:"#006064",A100:"#84ffff",A200:"#18ffff",A400:"#00e5ff",A700:"#00b8d4"};t.default=n},50173(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#fbe9e7",100:"#ffccbc",200:"#ffab91",300:"#ff8a65",400:"#ff7043",500:"#ff5722",600:"#f4511e",700:"#e64a19",800:"#d84315",900:"#bf360c",A100:"#ff9e80",A200:"#ff6e40",A400:"#ff3d00",A700:"#dd2c00"};t.default=n},45018(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#ede7f6",100:"#d1c4e9",200:"#b39ddb",300:"#9575cd",400:"#7e57c2",500:"#673ab7",600:"#5e35b1",700:"#512da8",800:"#4527a0",900:"#311b92",A100:"#b388ff",A200:"#7c4dff",A400:"#651fff",A700:"#6200ea"};t.default=n},47559(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853"};t.default=n},70167(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#d5d5d5",A200:"#aaaaaa",A400:"#303030",A700:"#616161"};t.default=n},19350(e,t,n){"use strict";var r,i=n(95318);r={value:!0},Object.defineProperty(t,"y0",{enumerable:!0,get:function(){return a.default}}),r={enumerable:!0,get:function(){return o.default}},r={enumerable:!0,get:function(){return s.default}},r={enumerable:!0,get:function(){return u.default}},r={enumerable:!0,get:function(){return c.default}},r={enumerable:!0,get:function(){return l.default}},r={enumerable:!0,get:function(){return f.default}},r={enumerable:!0,get:function(){return d.default}},r={enumerable:!0,get:function(){return h.default}},r={enumerable:!0,get:function(){return p.default}},Object.defineProperty(t,"ek",{enumerable:!0,get:function(){return b.default}}),r={enumerable:!0,get:function(){return m.default}},r={enumerable:!0,get:function(){return g.default}},r={enumerable:!0,get:function(){return v.default}},r={enumerable:!0,get:function(){return y.default}},r={enumerable:!0,get:function(){return w.default}},r={enumerable:!0,get:function(){return _.default}},r={enumerable:!0,get:function(){return E.default}},Object.defineProperty(t,"BA",{enumerable:!0,get:function(){return S.default}}),r={enumerable:!0,get:function(){return k.default}};var a=i(n(515)),o=i(n(83165)),s=i(n(124)),u=i(n(18118)),c=i(n(45018)),l=i(n(78768)),f=i(n(63259)),d=i(n(4923)),h=i(n(57646)),p=i(n(91605)),b=i(n(47559)),m=i(n(40192)),g=i(n(98567)),v=i(n(74578)),y=i(n(8070)),w=i(n(36594)),_=i(n(50173)),E=i(n(60169)),S=i(n(70167)),k=i(n(38236))},78768(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#e8eaf6",100:"#c5cae9",200:"#9fa8da",300:"#7986cb",400:"#5c6bc0",500:"#3f51b5",600:"#3949ab",700:"#303f9f",800:"#283593",900:"#1a237e",A100:"#8c9eff",A200:"#536dfe",A400:"#3d5afe",A700:"#304ffe"};t.default=n},4923(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#e1f5fe",100:"#b3e5fc",200:"#81d4fa",300:"#4fc3f7",400:"#29b6f6",500:"#03a9f4",600:"#039be5",700:"#0288d1",800:"#0277bd",900:"#01579b",A100:"#80d8ff",A200:"#40c4ff",A400:"#00b0ff",A700:"#0091ea"};t.default=n},40192(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#f1f8e9",100:"#dcedc8",200:"#c5e1a5",300:"#aed581",400:"#9ccc65",500:"#8bc34a",600:"#7cb342",700:"#689f38",800:"#558b2f",900:"#33691e",A100:"#ccff90",A200:"#b2ff59",A400:"#76ff03",A700:"#64dd17"};t.default=n},98567(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#f9fbe7",100:"#f0f4c3",200:"#e6ee9c",300:"#dce775",400:"#d4e157",500:"#cddc39",600:"#c0ca33",700:"#afb42b",800:"#9e9d24",900:"#827717",A100:"#f4ff81",A200:"#eeff41",A400:"#c6ff00",A700:"#aeea00"};t.default=n},36594(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#fff3e0",100:"#ffe0b2",200:"#ffcc80",300:"#ffb74d",400:"#ffa726",500:"#ff9800",600:"#fb8c00",700:"#f57c00",800:"#ef6c00",900:"#e65100",A100:"#ffd180",A200:"#ffab40",A400:"#ff9100",A700:"#ff6d00"};t.default=n},124(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#fce4ec",100:"#f8bbd0",200:"#f48fb1",300:"#f06292",400:"#ec407a",500:"#e91e63",600:"#d81b60",700:"#c2185b",800:"#ad1457",900:"#880e4f",A100:"#ff80ab",A200:"#ff4081",A400:"#f50057",A700:"#c51162"};t.default=n},18118(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#f3e5f5",100:"#e1bee7",200:"#ce93d8",300:"#ba68c8",400:"#ab47bc",500:"#9c27b0",600:"#8e24aa",700:"#7b1fa2",800:"#6a1b9a",900:"#4a148c",A100:"#ea80fc",A200:"#e040fb",A400:"#d500f9",A700:"#aa00ff"};t.default=n},83165(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000"};t.default=n},91605(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#e0f2f1",100:"#b2dfdb",200:"#80cbc4",300:"#4db6ac",400:"#26a69a",500:"#009688",600:"#00897b",700:"#00796b",800:"#00695c",900:"#004d40",A100:"#a7ffeb",A200:"#64ffda",A400:"#1de9b6",A700:"#00bfa5"};t.default=n},74578(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={50:"#fffde7",100:"#fff9c4",200:"#fff59d",300:"#fff176",400:"#ffee58",500:"#ffeb3b",600:"#fdd835",700:"#fbc02d",800:"#f9a825",900:"#f57f17",A100:"#ffff8d",A200:"#ffff00",A400:"#ffea00",A700:"#ffd600"};t.default=n},85609(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.styles=void 0;var i=r(n(67154)),a=r(n(59713)),o=r(n(6479)),s=r(n(34575)),u=r(n(93913)),c=r(n(78585)),l=r(n(29754)),f=r(n(2205)),d=r(n(67294));r(n(45697));var h=r(n(94184)),p=r(n(52598)),b=r(n(78252)),m=r(n(81701)),g={root:{display:"inline-flex",alignItems:"center",transition:"none","&:hover":{backgroundColor:"transparent"}},checked:{},disabled:{},input:{cursor:"inherit",position:"absolute",opacity:0,width:"100%",height:"100%",top:0,left:0,margin:0,padding:0}};t.styles=g;var v=function(e){function t(e){var n;return(0,s.default)(this,t),(n=(0,c.default)(this,(0,l.default)(t).call(this))).handleFocus=function(e){n.props.onFocus&&n.props.onFocus(e);var t=n.props.muiFormControl;t&&t.onFocus&&t.onFocus(e)},n.handleBlur=function(e){n.props.onBlur&&n.props.onBlur(e);var t=n.props.muiFormControl;t&&t.onBlur&&t.onBlur(e)},n.handleInputChange=function(e){var t=e.target.checked;n.isControlled||n.setState({checked:t}),n.props.onChange&&n.props.onChange(e,t)},n.isControlled=null!=e.checked,n.state={},n.isControlled||(n.state.checked=void 0!==e.defaultChecked&&e.defaultChecked),n}return(0,f.default)(t,e),(0,u.default)(t,[{key:"render",value:function(){var e,t=this.props,n=t.autoFocus,r=t.checked,s=t.checkedIcon,u=t.classes,c=t.className,l=t.defaultChecked,f=t.disabled,p=t.icon,b=t.id,g=t.inputProps,v=t.inputRef,y=t.muiFormControl,w=t.name,_=(t.onBlur,t.onChange,t.onFocus,t.readOnly),E=t.required,S=t.tabIndex,k=t.type,x=t.value,T=(0,o.default)(t,["autoFocus","checked","checkedIcon","classes","className","defaultChecked","disabled","icon","id","inputProps","inputRef","muiFormControl","name","onBlur","onChange","onFocus","readOnly","required","tabIndex","type","value"]),M=f;y&&void 0===M&&(M=y.disabled);var O=this.isControlled?r:this.state.checked,A="checkbox"===k||"radio"===k;return d.default.createElement(m.default,(0,i.default)({component:"span",className:(0,h.default)(u.root,(e={},(0,a.default)(e,u.checked,O),(0,a.default)(e,u.disabled,M),e),c),disabled:M,tabIndex:null,role:void 0,onFocus:this.handleFocus,onBlur:this.handleBlur},T),O?s:p,d.default.createElement("input",(0,i.default)({autoFocus:n,checked:r,defaultChecked:l,className:u.input,disabled:M,id:A&&b,name:w,onChange:this.handleInputChange,readOnly:_,ref:v,required:E,tabIndex:S,type:k,value:x},g)))}}]),t}(d.default.Component),y=(0,b.default)(g,{name:"MuiPrivateSwitchBase"})((0,p.default)(v));t.default=y},13329(e,t){"use strict";function n(e){return(1+Math.sin(Math.PI*e-Math.PI/2))/2}function r(e,t,r){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:function(){},o=i.ease,s=void 0===o?n:o,u=i.duration,c=void 0===u?300:u,l=null,f=t[e],d=!1,h=function(){d=!0},p=function n(i){if(d){a(Error("Animation cancelled"));return}null===l&&(l=i);var o=Math.min(1,(i-l)/c);if(t[e]=s(o)*(r-f)+f,o>=1){requestAnimationFrame(function(){a(null)});return}requestAnimationFrame(n)};return f===r?(a(Error("Element already at target position")),h):(requestAnimationFrame(p),h)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r;t.default=i},74622(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M7 10l5 5 5-5z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},99781(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},41549(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},42159(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},61486(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},86861(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},43836(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67294)),a=r(n(46949)),o=r(n(40577)),s=i.default.createElement("path",{d:"M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"}),u=function(e){return i.default.createElement(o.default,e,s)};(u=(0,a.default)(u)).muiName="SvgIcon";var c=u;t.default=c},93078(e,t,n){"use strict";/*! + * is-plain-object + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ var r=n(47798);function i(e){return!0===r(e)&&"[object Object]"===Object.prototype.toString.call(e)}e.exports=function(e){var t,n;return!1!==i(e)&&"function"==typeof(t=e.constructor)&&!1!==i(n=t.prototype)&&!1!==n.hasOwnProperty("isPrototypeOf")}},72366(e,t,n){"use strict";var r=n(20862),i=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.MuiThemeProviderOld=void 0;var a=i(n(67154)),o=i(n(59713)),s=i(n(34575)),u=i(n(93913)),c=i(n(78585)),l=i(n(29754)),f=i(n(2205)),d=i(n(67294)),h=i(n(45697));i(n(42473));var p=i(n(43890)),b=n(55252),m=r(n(51067)),g=function(e){function t(e,n){var r;return(0,s.default)(this,t),(r=(0,c.default)(this,(0,l.default)(t).call(this))).broadcast=(0,p.default)(),r.outerTheme=m.default.initial(n),r.broadcast.setState(r.mergeOuterLocalTheme(e.theme)),r}return(0,f.default)(t,e),(0,u.default)(t,[{key:"getChildContext",value:function(){var e,t=this.props,n=t.disableStylesGeneration,r=t.sheetsCache,i=t.sheetsManager,a=this.context.muiThemeProviderOptions||{};return void 0!==n&&(a.disableStylesGeneration=n),void 0!==r&&(a.sheetsCache=r),void 0!==i&&(a.sheetsManager=i),e={},(0,o.default)(e,m.CHANNEL,this.broadcast),(0,o.default)(e,"muiThemeProviderOptions",a),e}},{key:"componentDidMount",value:function(){var e=this;this.unsubscribeId=m.default.subscribe(this.context,function(t){e.outerTheme=t,e.broadcast.setState(e.mergeOuterLocalTheme(e.props.theme))})}},{key:"componentDidUpdate",value:function(e){this.props.theme!==e.theme&&this.broadcast.setState(this.mergeOuterLocalTheme(this.props.theme))}},{key:"componentWillUnmount",value:function(){null!==this.unsubscribeId&&m.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"mergeOuterLocalTheme",value:function(e){return"function"==typeof e?e(this.outerTheme):this.outerTheme?(0,a.default)({},this.outerTheme,e):e}},{key:"render",value:function(){return this.props.children}}]),t}(d.default.Component);t.MuiThemeProviderOld=g,g.childContextTypes=(0,a.default)({},m.default.contextTypes,{muiThemeProviderOptions:h.default.object}),g.contextTypes=(0,a.default)({},m.default.contextTypes,{muiThemeProviderOptions:h.default.object}),b.ponyfillGlobal.__MUI_STYLES__||(b.ponyfillGlobal.__MUI_STYLES__={}),b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider||(b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider=g);var v=b.ponyfillGlobal.__MUI_STYLES__.MuiThemeProvider;t.default=v},59114(e,t,n){"use strict";var r=n(95318);function i(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return en?n:e}function a(e){e=e.substr(1);var t=RegExp(".{1,".concat(e.length/3,"}"),"g"),n=e.match(t);return n&&1===n[0].length&&(n=n.map(function(e){return e+e})),n?"rgb(".concat(n.map(function(e){return parseInt(e,16)}).join(", "),")"):""}function o(e){if(0===e.indexOf("#"))return e;function t(e){var t=e.toString(16);return 1===t.length?"0".concat(t):t}var n=s(e).values;return n=n.map(function(e){return t(e)}),"#".concat(n.join(""))}function s(e){if("#"===e.charAt(0))return s(a(e));var t=e.indexOf("("),n=e.substring(0,t),r=e.substring(t+1,e.length-1).split(",");return r=r.map(function(e){return parseFloat(e)}),{type:n,values:r}}function u(e){var t=e.type,n=e.values;return -1!==t.indexOf("rgb")&&(n=n.map(function(e,t){return t<3?parseInt(e,10):e})),-1!==t.indexOf("hsl")&&(n[1]="".concat(n[1],"%"),n[2]="".concat(n[2],"%")),"".concat(e.type,"(").concat(n.join(", "),")")}function c(e,t){var n=l(e),r=l(t);return(Math.max(n,r)+.05)/(Math.min(n,r)+.05)}function l(e){var t=s(e);if(-1!==t.type.indexOf("rgb")){var n=t.values.map(function(e){return(e/=255)<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)});return Number((.2126*n[0]+.7152*n[1]+.0722*n[2]).toFixed(3))}return t.values[2]/100}function f(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.15;return l(e)>.5?h(e,t):p(e,t)}function d(e,t){return e?(e=s(e),t=i(t),("rgb"===e.type||"hsl"===e.type)&&(e.type+="a"),e.values[3]=t,u(e)):e}function h(e,t){if(!e)return e;if(e=s(e),t=i(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]*=1-t;return u(e)}function p(e,t){if(!e)return e;if(e=s(e),t=i(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]+=(255-e.values[n])*t;return u(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.convertHexToRGB=a,t.rgbToHex=o,t.decomposeColor=s,t.recomposeColor=u,t.getContrastRatio=c,t.getLuminance=l,t.emphasize=f,t.fade=d,t.darken=h,t.lighten=p,r(n(42473))},94811(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=s,t.keys=void 0;var i=r(n(67154)),a=r(n(6479)),o=["xs","sm","md","lg","xl"];function s(e){var t=e.values,n=void 0===t?{xs:0,sm:600,md:960,lg:1280,xl:1920}:t,r=e.unit,s=void 0===r?"px":r,u=e.step,c=void 0===u?5:u,l=(0,a.default)(e,["values","unit","step"]);function f(e){var t="number"==typeof n[e]?n[e]:e;return"@media (min-width:".concat(t).concat(s,")")}function d(e){var t=o.indexOf(e)+1,r=n[o[t]];if(t===o.length)return f("xs");var i="number"==typeof r&&t>0?r:e;return"@media (max-width:".concat(i-c/100).concat(s,")")}function h(e,t){var r=o.indexOf(t)+1;return r===o.length?f(e):"@media (min-width:".concat(n[e]).concat(s,") and ")+"(max-width:".concat(n[o[r]]-c/100).concat(s,")")}function p(e){return h(e,e)}function b(e){return n[e]}return(0,i.default)({keys:o,values:n,up:f,down:d,between:h,only:p,width:b},l)}t.keys=o},20237(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=o,r(n(42473));var i=/([[\].#*$><+~=|^:(),"'`\s])/g;function a(e){var t;return String(e).replace(i,"-")}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.dangerouslyUseGlobalCSS,n=void 0!==t&&t,r=e.productionPrefix,i=void 0===r?"jss":r,o=e.seed,s=void 0===o?"":o,u=0;return function(e,t){return(u+=1,n&&t&&t.options.name)?"".concat(a(t.options.name),"-").concat(e.key):"".concat(i).concat(s).concat(u)}}},40226(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=o;var i=r(n(59713)),a=r(n(67154));function o(e,t,n){var r;return(0,a.default)({gutters:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(0,a.default)({paddingLeft:2*t.unit,paddingRight:2*t.unit},n,(0,i.default)({},e.up("sm"),(0,a.default)({paddingLeft:3*t.unit,paddingRight:3*t.unit},n[e.up("sm")])))},toolbar:(r={minHeight:56},(0,i.default)(r,"".concat(e.up("xs")," and (orientation: landscape)"),{minHeight:48}),(0,i.default)(r,e.up("sm"),{minHeight:64}),r)},n)}},71615(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,r(n(59713));var i=r(n(67154)),a=r(n(6479)),o=r(n(94863)),s=r(n(93078));r(n(42473));var u=r(n(94811)),c=r(n(40226)),l=r(n(21091)),f=r(n(45184)),d=r(n(80743)),h=r(n(59591)),p=r(n(5324)),b=r(n(15406)),m=r(n(88676));function g(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.breakpoints,r=void 0===n?{}:n,g=t.mixins,v=void 0===g?{}:g,y=t.palette,w=void 0===y?{}:y,_=t.shadows,E=t.spacing,S=void 0===E?{}:E,k=t.typography,x=void 0===k?{}:k,T=(0,a.default)(t,["breakpoints","mixins","palette","shadows","spacing","typography"]),M=(0,l.default)(w),O=(0,u.default)(r),A=(0,i.default)({},p.default,S);return(0,i.default)({breakpoints:O,direction:"ltr",mixins:(0,c.default)(O,A,v),overrides:{},palette:M,props:{},shadows:_||d.default,typography:(0,f.default)(M,x)},(0,o.default)({shape:h.default,spacing:A,transitions:b.default,zIndex:m.default},T,{isMergeableObject:s.default}))}var v=g;t.default=v},21091(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=m,t.dark=t.light=void 0;var i=r(n(67154)),a=r(n(6479));r(n(42473));var o=r(n(94863)),s=r(n(78768)),u=r(n(124)),c=r(n(70167)),l=r(n(83165)),f=r(n(515)),d=n(59114),h={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.54)",disabled:"rgba(0, 0, 0, 0.38)",hint:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:f.default.white,default:c.default[50]},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.08)",hoverOpacity:.08,selected:"rgba(0, 0, 0, 0.14)",disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)"}};t.light=h;var p={text:{primary:f.default.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",hint:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:c.default[800],default:"#303030"},action:{active:f.default.white,hover:"rgba(255, 255, 255, 0.1)",hoverOpacity:.1,selected:"rgba(255, 255, 255, 0.2)",disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)"}};function b(e,t,n,r){e[t]||(e.hasOwnProperty(n)?e[t]=e[n]:"light"===t?e.light=(0,d.lighten)(e.main,r):"dark"===t&&(e.dark=(0,d.darken)(e.main,1.5*r)))}function m(e){var t=e.primary,n=void 0===t?{light:s.default[300],main:s.default[500],dark:s.default[700]}:t,r=e.secondary,m=void 0===r?{light:u.default.A200,main:u.default.A400,dark:u.default.A700}:r,g=e.error,v=void 0===g?{light:l.default[300],main:l.default[500],dark:l.default[700]}:g,y=e.type,w=void 0===y?"light":y,_=e.contrastThreshold,E=void 0===_?3:_,S=e.tonalOffset,k=void 0===S?.2:S,x=(0,a.default)(e,["primary","secondary","error","type","contrastThreshold","tonalOffset"]);function T(e){var t;return(0,d.getContrastRatio)(e,p.text.primary)>=E?p.text.primary:h.text.primary}function M(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:500,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:700;return!e.main&&e[t]&&(e.main=e[t]),b(e,"light",n,k),b(e,"dark",r,k),e.contrastText||(e.contrastText=T(e.main)),e}M(n),M(m,"A400","A200","A700"),M(v);var O={dark:p,light:h};return(0,o.default)((0,i.default)({common:f.default,type:w,primary:n,secondary:m,error:v,grey:c.default,contrastThreshold:E,getContrastText:T,augmentColor:M,tonalOffset:k},O[w]),x,{clone:!1})}t.dark=p},16059(e,t){"use strict";function n(e){return e}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},45184(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=f;var i=r(n(67154)),a=r(n(6479)),o=r(n(94863));r(n(42473));var s=n(55252);function u(e){return Math.round(1e5*e)/1e5}var c={textTransform:"uppercase"},l='"Roboto", "Helvetica", "Arial", sans-serif';function f(e,t){var n="function"==typeof t?t(e):t,r=n.fontFamily,f=void 0===r?l:r,d=n.fontSize,h=void 0===d?14:d,p=n.fontWeightLight,b=void 0===p?300:p,m=n.fontWeightRegular,g=void 0===m?400:m,v=n.fontWeightMedium,y=void 0===v?500:v,w=n.htmlFontSize,_=void 0===w?16:w,E=n.useNextVariants,S=void 0===E?Boolean(s.ponyfillGlobal.__MUI_USE_NEXT_TYPOGRAPHY_VARIANTS__):E,k=(n.suppressWarning,n.allVariants),x=(0,a.default)(n,["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","htmlFontSize","useNextVariants","suppressWarning","allVariants"]),T=h/14,M=function(e){return"".concat(e/_*T,"rem")},O=function(t,n,r,a,o){return(0,i.default)({color:e.text.primary,fontFamily:f,fontWeight:t,fontSize:M(n),lineHeight:r},f===l?{letterSpacing:"".concat(u(a/n),"em")}:{},o,k)},A={h1:O(b,96,1,-1.5),h2:O(b,60,1,-.5),h3:O(g,48,1.04,0),h4:O(g,34,1.17,.25),h5:O(g,24,1.33,0),h6:O(y,20,1.6,.15),subtitle1:O(g,16,1.75,.15),subtitle2:O(y,14,1.57,.1),body1Next:O(g,16,1.5,.15),body2Next:O(g,14,1.5,.15),buttonNext:O(y,14,1.75,.4,c),captionNext:O(g,12,1.66,.4),overline:O(g,12,2.66,1,c)},L={display4:(0,i.default)({fontSize:M(112),fontWeight:b,fontFamily:f,letterSpacing:"-.04em",lineHeight:"".concat(u(128/112),"em"),marginLeft:"-.04em",color:e.text.secondary},k),display3:(0,i.default)({fontSize:M(56),fontWeight:g,fontFamily:f,letterSpacing:"-.02em",lineHeight:"".concat(u(73/56),"em"),marginLeft:"-.02em",color:e.text.secondary},k),display2:(0,i.default)({fontSize:M(45),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(51/45),"em"),marginLeft:"-.02em",color:e.text.secondary},k),display1:(0,i.default)({fontSize:M(34),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(41/34),"em"),color:e.text.secondary},k),headline:(0,i.default)({fontSize:M(24),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(32.5/24),"em"),color:e.text.primary},k),title:(0,i.default)({fontSize:M(21),fontWeight:y,fontFamily:f,lineHeight:"".concat(u(24.5/21),"em"),color:e.text.primary},k),subheading:(0,i.default)({fontSize:M(16),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(1.5),"em"),color:e.text.primary},k),body2:(0,i.default)({fontSize:M(14),fontWeight:y,fontFamily:f,lineHeight:"".concat(u(24/14),"em"),color:e.text.primary},k),body1:(0,i.default)({fontSize:M(14),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(20.5/14),"em"),color:e.text.primary},k),caption:(0,i.default)({fontSize:M(12),fontWeight:g,fontFamily:f,lineHeight:"".concat(u(1.375),"em"),color:e.text.secondary},k),button:(0,i.default)({fontSize:M(14),textTransform:"uppercase",fontWeight:y,fontFamily:f,color:e.text.primary},k)};return(0,o.default)((0,i.default)({pxToRem:M,round:u,fontFamily:f,fontSize:h,fontWeightLight:b,fontWeightRegular:g,fontWeightMedium:y},L,A,S?{body1:A.body1Next,body2:A.body2Next,button:A.buttonNext,caption:A.captionNext}:{},{useNextVariants:S}),x,{clone:!1})}},42458(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154));r(n(50008)),r(n(42473));var a=r(n(94863));function o(e,t){return t}function s(e){var t="function"==typeof e;function n(n,r){var s=t?e(n):e;if(!r||!n.overrides||!n.overrides[r])return s;var u=n.overrides[r],c=(0,i.default)({},s);return Object.keys(u).forEach(function(e){c[e]=(0,a.default)(c[e],u[e],{arrayMerge:o})}),c}return{create:n,options:{},themingEnabled:t}}var u=s;t.default=u},58057(e,t){"use strict";function n(e){var t,n=e.theme,r=e.name,i=e.props;if(!n.props||!r||!n.props[r])return i;var a=n.props[r];for(t in a)void 0===i[t]&&(i[t]=a[t]);return i}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},32316(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"createGenerateClassName",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"createMuiTheme",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"jssPreset",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"MuiThemeProvider",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"createStyles",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"withStyles",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"withTheme",{enumerable:!0,get:function(){return l.default}});var i=r(n(20237)),a=r(n(71615)),o=r(n(9399)),s=r(n(72366)),u=r(n(16059)),c=r(n(78252)),l=r(n(82313))},9399(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(29059)),a=r(n(28752)),o=r(n(35828)),s=r(n(50462)),u=r(n(65926)),c=r(n(89347));function l(){return{plugins:[(0,i.default)(),(0,a.default)(),(0,o.default)(),(0,s.default)(),"undefined"==typeof window?null:(0,u.default)(),(0,c.default)()]}}var f=l;t.default=f},35199(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i=r(n(67154));function a(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.baseClasses,n=e.newClasses;if(e.Component,!n)return t;var r=(0,i.default)({},t);return Object.keys(n).forEach(function(e){n[e]&&(r[e]="".concat(t[e]," ").concat(n[e]))}),r}r(n(42473)),n(55252);var o=a;t.default=o},88693(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={set:function(e,t,n,r){var i=e.get(t);i||(i=new Map,e.set(t,i)),i.set(n,r)},get:function(e,t,n){var r=e.get(t);return r?r.get(n):void 0},delete:function(e,t,n){e.get(t).delete(n)}};t.default=n},31898(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={jss:"64a55d578f856d258dc345b094a2a2b3",sheetsRegistry:"d4bd0baacbc52bbd48bbb9eb24344ecd",sheetOptions:"6fc570d6bd61383819d0f9e7407c452d"};t.default=n},80743(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=.2,r=.14,i=.12;function a(){return["".concat(arguments.length<=0?void 0:arguments[0],"px ").concat(arguments.length<=1?void 0:arguments[1],"px ").concat(arguments.length<=2?void 0:arguments[2],"px ").concat(arguments.length<=3?void 0:arguments[3],"px rgba(0,0,0,").concat(n,")"),"".concat(arguments.length<=4?void 0:arguments[4],"px ").concat(arguments.length<=5?void 0:arguments[5],"px ").concat(arguments.length<=6?void 0:arguments[6],"px ").concat(arguments.length<=7?void 0:arguments[7],"px rgba(0,0,0,").concat(r,")"),"".concat(arguments.length<=8?void 0:arguments[8],"px ").concat(arguments.length<=9?void 0:arguments[9],"px ").concat(arguments.length<=10?void 0:arguments[10],"px ").concat(arguments.length<=11?void 0:arguments[11],"px rgba(0,0,0,").concat(i,")")].join(",")}var o=["none",a(0,1,3,0,0,1,1,0,0,2,1,-1),a(0,1,5,0,0,2,2,0,0,3,1,-2),a(0,1,8,0,0,3,4,0,0,3,3,-2),a(0,2,4,-1,0,4,5,0,0,1,10,0),a(0,3,5,-1,0,5,8,0,0,1,14,0),a(0,3,5,-1,0,6,10,0,0,1,18,0),a(0,4,5,-2,0,7,10,1,0,2,16,1),a(0,5,5,-3,0,8,10,1,0,3,14,2),a(0,5,6,-3,0,9,12,1,0,3,16,2),a(0,6,6,-3,0,10,14,1,0,4,18,3),a(0,6,7,-4,0,11,15,1,0,4,20,3),a(0,7,8,-4,0,12,17,2,0,5,22,4),a(0,7,8,-4,0,13,19,2,0,5,24,4),a(0,7,9,-4,0,14,21,2,0,5,26,4),a(0,8,9,-5,0,15,22,2,0,6,28,5),a(0,8,10,-5,0,16,24,2,0,6,30,5),a(0,8,11,-5,0,17,26,2,0,6,32,5),a(0,9,11,-5,0,18,28,2,0,7,34,6),a(0,9,12,-6,0,19,29,2,0,7,36,6),a(0,10,13,-6,0,20,31,3,0,8,38,7),a(0,10,13,-6,0,21,33,3,0,8,40,7),a(0,10,14,-6,0,22,35,3,0,8,42,7),a(0,11,14,-7,0,23,36,3,0,9,44,8),a(0,11,15,-7,0,24,38,3,0,9,46,8)];t.default=o},59591(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={borderRadius:4};t.default=n},5324(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={unit:8};t.default=n},51067(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.CHANNEL=void 0;var i=r(n(59713)),a="__THEMING__";t.CHANNEL=a;var o={contextTypes:(0,i.default)({},a,function(){}),initial:function(e){return e[a]?e[a].getState():null},subscribe:function(e,t){return e[a]?e[a].subscribe(t):null},unsubscribe:function(e,t){e[a]&&e[a].unsubscribe(t)}};t.default=o},15406(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.isNumber=t.isString=t.formatMs=t.duration=t.easing=void 0;var i=r(n(6479));r(n(42473));var a={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"};t.easing=a;var o={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};t.duration=o;var s=function(e){return"".concat(Math.round(e),"ms")};t.formatMs=s;var u=function(e){return"string"==typeof e};t.isString=u;var c=function(e){return!isNaN(parseFloat(e))};t.isNumber=c;var l={easing:a,duration:o,create:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["all"],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.duration,r=void 0===n?o.standard:n,u=t.easing,c=void 0===u?a.easeInOut:u,l=t.delay,f=void 0===l?0:l;return(0,i.default)(t,["duration","easing","delay"]),(Array.isArray(e)?e:[e]).map(function(e){return"".concat(e," ").concat("string"==typeof r?r:s(r)," ").concat(c," ").concat("string"==typeof f?f:s(f))}).join(",")},getAutoHeightDuration:function(e){if(!e)return 0;var t=e/36;return Math.round((4+15*Math.pow(t,.25)+t/5)*10)}};t.default=l},78252(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.sheetsManager=void 0;var i=r(n(59713)),a=r(n(67154)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(6479)),d=r(n(67294)),h=r(n(45697));r(n(42473));var p=r(n(8679)),b=n(55252),m=n(55690),g=r(n(31898)),v=r(n(9399)),y=r(n(35199)),w=r(n(88693)),_=r(n(71615)),E=r(n(51067)),S=r(n(20237)),k=r(n(42458)),x=r(n(58057)),T=(0,m.create)((0,v.default)()),M=(0,S.default)(),O=-1e11,A=new Map;t.sheetsManager=A;var L={},C=(0,_.default)({typography:{suppressWarning:!0}}),I=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return function(n){var r,b=t.withTheme,m=void 0!==b&&b,v=t.flip,_=void 0===v?null:v,S=t.name,I=(0,f.default)(t,["withTheme","flip","name"]),D=(0,k.default)(e),N=D.themingEnabled||"string"==typeof S||m;O+=1,D.options.index=O;var P=function(e){function t(e,n){(0,o.default)(this,t),(r=(0,u.default)(this,(0,c.default)(t).call(this,e,n))).jss=n[g.default.jss]||T,r.sheetsManager=A,r.unsubscribeId=null;var r,i=n.muiThemeProviderOptions;return i&&(i.sheetsManager&&(r.sheetsManager=i.sheetsManager),r.sheetsCache=i.sheetsCache,r.disableStylesGeneration=i.disableStylesGeneration),r.stylesCreatorSaved=D,r.sheetOptions=(0,a.default)({generateClassName:M},n[g.default.sheetOptions]),r.theme=N?E.default.initial(n)||C:L,r.attach(r.theme),r.cacheClasses={value:null,lastProp:null,lastJSS:{}},r}return(0,l.default)(t,e),(0,s.default)(t,[{key:"componentDidMount",value:function(){var e=this;N&&(this.unsubscribeId=E.default.subscribe(this.context,function(t){var n=e.theme;e.theme=t,e.attach(e.theme),e.setState({},function(){e.detach(n)})}))}},{key:"componentDidUpdate",value:function(){this.stylesCreatorSaved}},{key:"componentWillUnmount",value:function(){this.detach(this.theme),null!==this.unsubscribeId&&E.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"getClasses",value:function(){if(this.disableStylesGeneration)return this.props.classes||{};var e=!1,t=w.default.get(this.sheetsManager,this.stylesCreatorSaved,this.theme);return t.sheet.classes!==this.cacheClasses.lastJSS&&(this.cacheClasses.lastJSS=t.sheet.classes,e=!0),this.props.classes!==this.cacheClasses.lastProp&&(this.cacheClasses.lastProp=this.props.classes,e=!0),e&&(this.cacheClasses.value=(0,y.default)({baseClasses:this.cacheClasses.lastJSS,newClasses:this.props.classes,Component:n})),this.cacheClasses.value}},{key:"attach",value:function(e){if(!this.disableStylesGeneration){var t=this.stylesCreatorSaved,n=w.default.get(this.sheetsManager,t,e);if(n||(n={refs:0,sheet:null},w.default.set(this.sheetsManager,t,e,n)),0===n.refs){this.sheetsCache&&(r=w.default.get(this.sheetsCache,t,e)),!r&&((r=this.createSheet(e)).attach(),this.sheetsCache&&w.default.set(this.sheetsCache,t,e,r)),n.sheet=r;var r,i=this.context[g.default.sheetsRegistry];i&&i.add(r)}n.refs+=1}}},{key:"createSheet",value:function(e){var t=this.stylesCreatorSaved.create(e,S),r=S;return this.jss.createStyleSheet(t,(0,a.default)({meta:r,classNamePrefix:r,flip:"boolean"==typeof _?_:"rtl"===e.direction,link:!1},this.sheetOptions,this.stylesCreatorSaved.options,{name:S||n.displayName},I))}},{key:"detach",value:function(e){if(!this.disableStylesGeneration){var t=w.default.get(this.sheetsManager,this.stylesCreatorSaved,e);if(t.refs-=1,0===t.refs){w.default.delete(this.sheetsManager,this.stylesCreatorSaved,e),this.jss.removeStyleSheet(t.sheet);var n=this.context[g.default.sheetsRegistry];n&&n.remove(t.sheet)}}}},{key:"render",value:function(){var e=this.props,t=(e.classes,e.innerRef),r=(0,f.default)(e,["classes","innerRef"]),i=(0,x.default)({theme:this.theme,name:S,props:r});return m&&!i.theme&&(i.theme=this.theme),d.default.createElement(n,(0,a.default)({},i,{classes:this.getClasses(),ref:t}))}}]),t}(d.default.Component);return P.contextTypes=(0,a.default)((r={muiThemeProviderOptions:h.default.object},(0,i.default)(r,g.default.jss,h.default.object),(0,i.default)(r,g.default.sheetOptions,h.default.object),(0,i.default)(r,g.default.sheetsRegistry,h.default.object),r),N?E.default.contextTypes:{}),(0,p.default)(P,n),P}};b.ponyfillGlobal.__MUI_STYLES__||(b.ponyfillGlobal.__MUI_STYLES__={}),b.ponyfillGlobal.__MUI_STYLES__.withStyles||(b.ponyfillGlobal.__MUI_STYLES__.withStyles=I);var D=function(e,t){return b.ponyfillGlobal.__MUI_STYLES__.withStyles(e,(0,a.default)({defaultTheme:C},t))};t.default=D},82313(e,t,n){"use strict";var r,i=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=i(n(67154)),o=i(n(6479)),s=i(n(34575)),u=i(n(93913)),c=i(n(78585)),l=i(n(29754)),f=i(n(2205)),d=i(n(67294));i(n(45697));var h=i(n(8679)),p=n(55252),b=i(n(71615)),m=i(n(51067));function g(){return r||(r=(0,b.default)({typography:{suppressWarning:!0}}))}var v=function(){return function(e){var t=function(t){function n(e,t){var r;return(0,s.default)(this,n),(r=(0,c.default)(this,(0,l.default)(n).call(this))).state={theme:m.default.initial(t)||g()},r}return(0,f.default)(n,t),(0,u.default)(n,[{key:"componentDidMount",value:function(){var e=this;this.unsubscribeId=m.default.subscribe(this.context,function(t){e.setState({theme:t})})}},{key:"componentWillUnmount",value:function(){null!==this.unsubscribeId&&m.default.unsubscribe(this.context,this.unsubscribeId)}},{key:"render",value:function(){var t=this.props,n=t.innerRef,r=(0,o.default)(t,["innerRef"]);return d.default.createElement(e,(0,a.default)({theme:this.state.theme,ref:n},r))}}]),n}(d.default.Component);return t.contextTypes=m.default.contextTypes,(0,h.default)(t,e),t}};p.ponyfillGlobal.__MUI_STYLES__||(p.ponyfillGlobal.__MUI_STYLES__={}),p.ponyfillGlobal.__MUI_STYLES__.withTheme||(p.ponyfillGlobal.__MUI_STYLES__.withTheme=v);var y=p.ponyfillGlobal.__MUI_STYLES__.withTheme;t.default=y},88676(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={mobileStepper:1e3,appBar:1100,drawer:1200,modal:1300,snackbar:1400,tooltip:1500};t.default=n},41929(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getTransitionProps=r,t.reflow=void 0;var n=function(e){return e.scrollTop};function r(e,t){var n=e.timeout,r=e.style,i=void 0===r?{}:r;return{duration:i.transitionDuration||"number"==typeof n?n:n[t.mode],delay:i.transitionDelay}}t.reflow=n},346(e,t){"use strict";function n(e,t){return function(){return null}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},98741(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.capitalize=a,t.contains=o,t.findIndex=s,t.find=u,t.createChainedFunction=c;var i=r(n(50008));function a(e){return e.charAt(0).toUpperCase()+e.slice(1)}function o(e,t){return Object.keys(t).every(function(n){return e.hasOwnProperty(n)&&e[n]===t[n]})}function s(e,t){for(var n=(0,i.default)(t),r=0;r-1?e[n]:void 0}function c(){for(var e=arguments.length,t=Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:window,n=(0,i.default)(e);return n.defaultView||n.parentView||t}var o=a;t.default=o},44370(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.cloneElementWithClassName=o,t.cloneChildrenWithClassName=s,t.isMuiElement=u,t.setRef=c;var i=r(n(67294)),a=r(n(94184));function o(e,t){return i.default.cloneElement(e,{className:(0,a.default)(e.props.className,t)})}function s(e,t){return i.default.Children.map(e,function(e){return i.default.isValidElement(e)&&o(e,t)})}function u(e,t){return i.default.isValidElement(e)&&-1!==t.indexOf(e.type.muiName)}function c(e,t){"function"==typeof e?e(t):e&&(e.current=t)}},47348(e,t){"use strict";function n(e){return function(){return null}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},21677(e,t){"use strict";function n(e,t,n,r,i){return null}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n;t.default=r},78290(e,t,n){"use strict";var r=n(20862);Object.defineProperty(t,"__esModule",{value:!0});var i={};Object.defineProperty(t,"default",{enumerable:!0,get:function(){return a.default}});var a=r(n(88446));Object.keys(a).forEach(function(e){"default"!==e&&"__esModule"!==e&&(Object.prototype.hasOwnProperty.call(i,e)||Object.defineProperty(t,e,{enumerable:!0,get:function(){return a[e]}}))})},88446(e,t,n){"use strict";var r=n(95318);Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.isWidthDown=t.isWidthUp=void 0;var i=r(n(67154)),a=r(n(6479)),o=r(n(34575)),s=r(n(93913)),u=r(n(78585)),c=r(n(29754)),l=r(n(2205)),f=r(n(67294));r(n(45697));var d=r(n(96421)),h=r(n(20296));n(55252);var p=r(n(8679)),b=r(n(82313)),m=n(94811),g=r(n(58057)),v=function(e,t){var n=!(arguments.length>2)||void 0===arguments[2]||arguments[2];return n?m.keys.indexOf(e)<=m.keys.indexOf(t):m.keys.indexOf(e)2)||void 0===arguments[2]||arguments[2];return n?m.keys.indexOf(t)<=m.keys.indexOf(e):m.keys.indexOf(t)0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=e.withTheme,r=void 0!==n&&n,v=e.noSSR,y=void 0!==v&&v,w=e.initialWidth,_=e.resizeInterval,E=void 0===_?166:_,S=function(e){function n(e){var t;return(0,o.default)(this,n),(t=(0,u.default)(this,(0,c.default)(n).call(this,e))).state={width:y?t.getWidth():void 0},"undefined"!=typeof window&&(t.handleResize=(0,h.default)(function(){var e=t.getWidth();e!==t.state.width&&t.setState({width:e})},E)),t}return(0,l.default)(n,e),(0,s.default)(n,[{key:"componentDidMount",value:function(){var e=this.getWidth();e!==this.state.width&&this.setState({width:e})}},{key:"componentWillUnmount",value:function(){this.handleResize.clear()}},{key:"getWidth",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.innerWidth,t=this.props.theme.breakpoints,n=null,r=1;null===n&&ri.Z,componentPropType:()=>r.Z,exactProp:()=>a.ZP,getDisplayName:()=>o.ZP,ponyfillGlobal:()=>s.Z});var r=n(78728),i=n(5477),a=n(43781),o=n(25189),s=n(34712);/** @license Material-UI v3.0.0-alpha.3 + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ },34712(e,t){"use strict";n={value:!0},t.Z=void 0;var n,r="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();t.Z=r},82152(e,t,n){"use strict";n.d(t,{D:()=>u});var r=Object.prototype,i=r.toString,a=r.hasOwnProperty,o=Function.prototype.toString,s=new Map;function u(e,t){try{return c(e,t)}finally{s.clear()}}function c(e,t){if(e===t)return!0;var n=i.call(e),r=i.call(t);if(n!==r)return!1;switch(n){case"[object Array]":if(e.length!==t.length)break;case"[object Object]":if(p(e,t))return!0;var s=l(e),u=l(t),f=s.length;if(f!==u.length)break;for(var b=0;b=0&&e.indexOf(t,n)===n}function p(e,t){var n=s.get(e);if(n){if(n.has(t))return!0}else s.set(e,n=new Set);return n.add(t),!1}},79742(e,t){"use strict";t.byteLength=c,t.toByteArray=f,t.fromByteArray=p;for(var n=[],r=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,s=a.length;o0)throw Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");-1===n&&(n=t);var r=n===t?0:4-n%4;return[n,r]}function c(e){var t=u(e),n=t[0],r=t[1];return(n+r)*3/4-r}function l(e,t,n){return(t+n)*3/4-n}function f(e){var t,n,a=u(e),o=a[0],s=a[1],c=new i(l(e,o,s)),f=0,d=s>0?o-4:o;for(n=0;n>16&255,c[f++]=t>>8&255,c[f++]=255&t;return 2===s&&(t=r[e.charCodeAt(n)]<<2|r[e.charCodeAt(n+1)]>>4,c[f++]=255&t),1===s&&(t=r[e.charCodeAt(n)]<<10|r[e.charCodeAt(n+1)]<<4|r[e.charCodeAt(n+2)]>>2,c[f++]=t>>8&255,c[f++]=255&t),c}function d(e){return n[e>>18&63]+n[e>>12&63]+n[e>>6&63]+n[63&e]}function h(e,t,n){for(var r,i=[],a=t;au?u:s+o));return 1===i?a.push(n[(t=e[r-1])>>2]+n[t<<4&63]+"=="):2===i&&a.push(n[(t=(e[r-2]<<8)+e[r-1])>>10]+n[t>>4&63]+n[t<<2&63]+"="),a.join("")}r["-".charCodeAt(0)]=62,r["_".charCodeAt(0)]=63},44431:function(e,t,n){var r;!function(i){"use strict";var a,o=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,s=Math.ceil,u=Math.floor,c="[BigNumber Error] ",l=c+"Number primitive has more than 15 significant digits: ",f=1e14,d=14,h=9007199254740991,p=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],b=1e7,m=1e9;function g(e){var t,n,r,i,a,x,T,M,O,A,L=$.prototype={constructor:$,toString:null,valueOf:null},C=new $(1),I=20,D=4,N=-7,P=21,R=-1e7,j=1e7,F=!1,Y=1,B=0,U={prefix:"",groupSize:3,secondaryGroupSize:0,groupSeparator:",",decimalSeparator:".",fractionGroupSize:0,fractionGroupSeparator:"\xa0",suffix:""},H="0123456789abcdefghijklmnopqrstuvwxyz";function $(e,t){var n,r,i,a,s,c,f,p,b=this;if(!(b instanceof $))return new $(e,t);if(null==t){if(e&&!0===e._isBigNumber){b.s=e.s,!e.c||e.e>j?b.c=b.e=null:e.e=10;s/=10,a++);a>j?b.c=b.e=null:(b.e=a,b.c=[e]);return}p=String(e)}else{if(!o.test(p=String(e)))return A(b,p,c);b.s=45==p.charCodeAt(0)?(p=p.slice(1),-1):1}(a=p.indexOf("."))>-1&&(p=p.replace(".","")),(s=p.search(/e/i))>0?(a<0&&(a=s),a+=+p.slice(s+1),p=p.substring(0,s)):a<0&&(a=p.length)}else{if(_(t,2,H.length,"Base"),10==t)return b=new $(e),K(b,I+b.e+1,D);if(p=String(e),c="number"==typeof e){if(0*e!=0)return A(b,p,c,t);if(b.s=1/e<0?(p=p.slice(1),-1):1,$.DEBUG&&p.replace(/^0\.0*|\./,"").length>15)throw Error(l+e)}else b.s=45===p.charCodeAt(0)?(p=p.slice(1),-1):1;for(n=H.slice(0,t),a=s=0,f=p.length;sn.indexOf(r=p.charAt(s))){if("."==r){if(s>a){a=f;continue}}else if(!i&&(p==p.toUpperCase()&&(p=p.toLowerCase())||p==p.toLowerCase()&&(p=p.toUpperCase()))){i=!0,s=-1,a=0;continue}return A(b,String(e),c,t)}c=!1,(a=(p=O(p,t,10,b.s)).indexOf("."))>-1?p=p.replace(".",""):a=p.length}for(s=0;48===p.charCodeAt(s);s++);for(f=p.length;48===p.charCodeAt(--f););if(p=p.slice(s,++f)){if(f-=s,c&&$.DEBUG&&f>15&&(e>h||e!==u(e)))throw Error(l+b.s*e);if((a=a-s-1)>j)b.c=b.e=null;else if(a=P)?S(u,o):k(u,o,"0");else if(a=(e=K(new $(e),t,n)).e,s=(u=y(e.c)).length,1==r||2==r&&(t<=a||a<=N)){for(;ss){if(--t>0)for(u+=".";t--;u+="0");}else if((t+=a-s)>0)for(a+1==s&&(u+=".");t--;u+="0");return e.s<0&&i?"-"+u:u}function G(e,t){for(var n,r=1,i=new $(e[0]);r=10;i/=10,r++);return(n=r+n*d-1)>j?e.c=e.e=null:n=10;c/=10,i++);if((a=t-i)<0)a+=d,o=t,b=(l=m[h=0])/g[i-o-1]%10|0;else if((h=s((a+1)/d))>=m.length){if(r){for(;m.length<=h;m.push(0));l=b=0,i=1,a%=d,o=a-d+1}else break out}else{for(i=1,l=c=m[h];c>=10;c/=10,i++);a%=d,b=(o=a-d+i)<0?0:l/g[i-o-1]%10|0}if(r=r||t<0||null!=m[h+1]||(o<0?l:l%g[i-o-1]),r=n<4?(b||r)&&(0==n||n==(e.s<0?3:2)):b>5||5==b&&(4==n||r||6==n&&(a>0?o>0?l/g[i-o]:0:m[h-1])%10&1||n==(e.s<0?8:7)),t<1||!m[0])return m.length=0,r?(t-=e.e+1,m[0]=g[(d-t%d)%d],e.e=-t||0):m[0]=e.e=0,e;if(0==a?(m.length=h,c=1,h--):(m.length=h+1,c=g[d-a],m[h]=o>0?u(l/g[i-o]%g[o])*c:0),r)for(;;){if(0==h){for(a=1,o=m[0];o>=10;o/=10,a++);for(o=m[0]+=c,c=1;o>=10;o/=10,c++);a!=c&&(e.e++,m[0]==f&&(m[0]=1));break}if(m[h]+=c,m[h]!=f)break;m[h--]=0,c=1}for(a=m.length;0===m[--a];m.pop());}e.e>j?e.c=e.e=null:e.e=P?S(t,n):k(t,n,"0"),e.s<0?"-"+t:t)}return $.clone=g,$.ROUND_UP=0,$.ROUND_DOWN=1,$.ROUND_CEIL=2,$.ROUND_FLOOR=3,$.ROUND_HALF_UP=4,$.ROUND_HALF_DOWN=5,$.ROUND_HALF_EVEN=6,$.ROUND_HALF_CEIL=7,$.ROUND_HALF_FLOOR=8,$.EUCLID=9,$.config=$.set=function(e){var t,n;if(null!=e){if("object"==typeof e){if(e.hasOwnProperty(t="DECIMAL_PLACES")&&(_(n=e[t],0,m,t),I=n),e.hasOwnProperty(t="ROUNDING_MODE")&&(_(n=e[t],0,8,t),D=n),e.hasOwnProperty(t="EXPONENTIAL_AT")&&((n=e[t])&&n.pop?(_(n[0],-m,0,t),_(n[1],0,m,t),N=n[0],P=n[1]):(_(n,-m,m,t),N=-(P=n<0?-n:n))),e.hasOwnProperty(t="RANGE")){if((n=e[t])&&n.pop)_(n[0],-m,-1,t),_(n[1],1,m,t),R=n[0],j=n[1];else if(_(n,-m,m,t),n)R=-(j=n<0?-n:n);else throw Error(c+t+" cannot be zero: "+n)}if(e.hasOwnProperty(t="CRYPTO")){if(!!(n=e[t])===n){if(n){if("undefined"!=typeof crypto&&crypto&&(crypto.getRandomValues||crypto.randomBytes))F=n;else throw F=!n,Error(c+"crypto unavailable")}else F=n}else throw Error(c+t+" not true or false: "+n)}if(e.hasOwnProperty(t="MODULO_MODE")&&(_(n=e[t],0,9,t),Y=n),e.hasOwnProperty(t="POW_PRECISION")&&(_(n=e[t],0,m,t),B=n),e.hasOwnProperty(t="FORMAT")){if("object"==typeof(n=e[t]))U=n;else throw Error(c+t+" not an object: "+n)}if(e.hasOwnProperty(t="ALPHABET")){if("string"!=typeof(n=e[t])||/^.?$|[+\-.\s]|(.).*\1/.test(n))throw Error(c+t+" invalid: "+n);H=n}}else throw Error(c+"Object expected: "+e)}return{DECIMAL_PLACES:I,ROUNDING_MODE:D,EXPONENTIAL_AT:[N,P],RANGE:[R,j],CRYPTO:F,MODULO_MODE:Y,POW_PRECISION:B,FORMAT:U,ALPHABET:H}},$.isBigNumber=function(e){if(!e||!0!==e._isBigNumber)return!1;if(!$.DEBUG)return!0;var t,n,r=e.c,i=e.e,a=e.s;out:if("[object Array]"==({}).toString.call(r)){if((1===a||-1===a)&&i>=-m&&i<=m&&i===u(i)){if(0===r[0]){if(0===i&&1===r.length)return!0;break out}if((t=(i+1)%d)<1&&(t+=d),String(r[0]).length==t){for(t=0;t=f||n!==u(n))break out;if(0!==n)return!0}}}else if(null===r&&null===i&&(null===a||1===a||-1===a))return!0;throw Error(c+"Invalid BigNumber: "+e)},$.maximum=$.max=function(){return G(arguments,L.lt)},$.minimum=$.min=function(){return G(arguments,L.gt)},$.random=(n=Math.random()*(t=9007199254740992)&2097151?function(){return u(Math.random()*t)}:function(){return(1073741824*Math.random()|0)*8388608+(8388608*Math.random()|0)},function(e){var t,r,i,a,o,l=0,f=[],h=new $(C);if(null==e?e=I:_(e,0,m),a=s(e/d),F){if(crypto.getRandomValues){for(t=crypto.getRandomValues(new Uint32Array(a*=2));l>>11))>=9e15?(r=crypto.getRandomValues(new Uint32Array(2)),t[l]=r[0],t[l+1]=r[1]):(f.push(o%1e14),l+=2);l=a/2}else if(crypto.randomBytes){for(t=crypto.randomBytes(a*=7);l=9e15?crypto.randomBytes(7).copy(t,l):(f.push(o%1e14),l+=7);l=a/7}else throw F=!1,Error(c+"crypto unavailable")}if(!F)for(;l=10;o/=10,l++);ln-1&&(null==o[i+1]&&(o[i+1]=0),o[i+1]+=o[i]/n|0,o[i]%=n)}return o.reverse()}return function(n,r,i,a,o){var s,u,c,l,f,d,h,p,b=n.indexOf("."),m=I,g=D;for(b>=0&&(l=B,B=0,n=n.replace(".",""),d=(p=new $(r)).pow(n.length-b),B=l,p.c=t(k(y(d.c),d.e,"0"),10,i,e),p.e=p.c.length),c=l=(h=t(n,r,i,o?(s=H,e):(s=e,H))).length;0==h[--l];h.pop());if(!h[0])return s.charAt(0);if(b<0?--c:(d.c=h,d.e=c,d.s=a,h=(d=M(d,p,m,g,i)).c,f=d.r,c=d.e),b=h[u=c+m+1],l=i/2,f=f||u<0||null!=h[u+1],f=g<4?(null!=b||f)&&(0==g||g==(d.s<0?3:2)):b>l||b==l&&(4==g||f||6==g&&1&h[u-1]||g==(d.s<0?8:7)),u<1||!h[0])n=f?k(s.charAt(1),-m,s.charAt(0)):s.charAt(0);else{if(h.length=u,f)for(--i;++h[--u]>i;)h[u]=0,u||(++c,h=[1].concat(h));for(l=h.length;!h[--l];);for(b=0,n="";b<=l;n+=s.charAt(h[b++]));n=k(n,c,s.charAt(0))}return n}}(),M=function(){function e(e,t,n){var r,i,a,o,s=0,u=e.length,c=t%b,l=t/b|0;for(e=e.slice();u--;)r=l*(a=e[u]%b)+(o=e[u]/b|0)*c,s=((i=c*a+r%b*b+s)/n|0)+(r/b|0)+l*o,e[u]=i%n;return s&&(e=[s].concat(e)),e}function t(e,t,n,r){var i,a;if(n!=r)a=n>r?1:-1;else for(i=a=0;it[i]?1:-1;break}return a}function n(e,t,n,r){for(var i=0;n--;)e[n]-=i,i=e[n]1;e.splice(0,1));}return function(r,i,a,o,s){var c,l,h,p,b,m,g,y,w,_,E,S,k,x,T,M,O,A=r.s==i.s?1:-1,L=r.c,C=i.c;if(!L||!L[0]||!C||!C[0])return new $(r.s&&i.s&&(L?!C||L[0]!=C[0]:C)?L&&0==L[0]||!C?0*A:A/0:NaN);for(w=(y=new $(A)).c=[],A=a+(l=r.e-i.e)+1,s||(s=f,l=v(r.e/d)-v(i.e/d),A=A/d|0),h=0;C[h]==(L[h]||0);h++);if(C[h]>(L[h]||0)&&l--,A<0)w.push(1),p=!0;else{for(x=L.length,M=C.length,h=0,A+=2,(b=u(s/(C[0]+1)))>1&&(C=e(C,b,s),L=e(L,b,s),M=C.length,x=L.length),k=M,E=(_=L.slice(0,M)).length;E=s/2&&T++;do{if(b=0,(c=t(C,_,M,E))<0){if(S=_[0],M!=E&&(S=S*s+(_[1]||0)),(b=u(S/T))>1)for(b>=s&&(b=s-1),g=(m=e(C,b,s)).length,E=_.length;1==t(m,_,g,E);)b--,n(m,Mt(C,_,M,E);)b++,n(_,M=10;A/=10,h++);K(y,a+(y.e=h+l*d-1)+1,o,p)}else y.e=l,y.r=+p;return y}}(),A=(r=/^(-?)0([xbo])(?=\w[\w.]*$)/i,i=/^([^.]+)\.$/,a=/^\.([^.]+)$/,x=/^-?(Infinity|NaN)$/,T=/^\s*\+(?=[\w.])|^\s+|\s+$/g,function(e,t,n,o){var s,u=n?t:t.replace(T,"");if(x.test(u))e.s=isNaN(u)?null:u<0?-1:1;else{if(!n&&(u=u.replace(r,function(e,t,n){return s="x"==(n=n.toLowerCase())?16:"b"==n?2:8,o&&o!=s?e:t}),o&&(s=o,u=u.replace(i,"$1").replace(a,"0.$1")),t!=u))return new $(u,s);if($.DEBUG)throw Error(c+"Not a"+(o?" base "+o:"")+" number: "+t);e.s=null}e.c=e.e=null}),L.absoluteValue=L.abs=function(){var e=new $(this);return e.s<0&&(e.s=1),e},L.comparedTo=function(e,t){return w(this,new $(e,t))},L.decimalPlaces=L.dp=function(e,t){var n,r,i,a=this;if(null!=e)return _(e,0,m),null==t?t=D:_(t,0,8),K(new $(a),e+a.e+1,t);if(!(n=a.c))return null;if(r=((i=n.length-1)-v(this.e/d))*d,i=n[i])for(;i%10==0;i/=10,r--);return r<0&&(r=0),r},L.dividedBy=L.div=function(e,t){return M(this,new $(e,t),I,D)},L.dividedToIntegerBy=L.idiv=function(e,t){return M(this,new $(e,t),0,1)},L.exponentiatedBy=L.pow=function(e,t){var n,r,i,a,o,l,f,h,p,b=this;if((e=new $(e)).c&&!e.isInteger())throw Error(c+"Exponent not an integer: "+V(e));if(null!=t&&(t=new $(t)),l=e.e>14,!b.c||!b.c[0]||1==b.c[0]&&!b.e&&1==b.c.length||!e.c||!e.c[0])return p=new $(Math.pow(+V(b),l?2-E(e):+V(e))),t?p.mod(t):p;if(f=e.s<0,t){if(t.c?!t.c[0]:!t.s)return new $(NaN);(r=!f&&b.isInteger()&&t.isInteger())&&(b=b.mod(t))}else{if(e.e>9&&(b.e>0||b.e<-1||(0==b.e?b.c[0]>1||l&&b.c[1]>=24e7:b.c[0]<8e13||l&&b.c[0]<=9999975e7)))return a=(b.s<0&&E(e),-0),b.e>-1&&(a=1/a),new $(f?1/a:a);B&&(a=s(B/d+2))}for(l?(n=new $(.5),f&&(e.s=1),h=E(e)):h=(i=Math.abs(+V(e)))%2,p=new $(C);;){if(h){if(!(p=p.times(b)).c)break;a?p.c.length>a&&(p.c.length=a):r&&(p=p.mod(t))}if(i){if(0===(i=u(i/2)))break;h=i%2}else if(K(e=e.times(n),e.e+1,1),e.e>14)h=E(e);else{if(0==(i=+V(e)))break;h=i%2}b=b.times(b),a?b.c&&b.c.length>a&&(b.c.length=a):r&&(b=b.mod(t))}return r?p:(f&&(p=C.div(p)),t?p.mod(t):a?K(p,B,D,o):p)},L.integerValue=function(e){var t=new $(this);return null==e?e=D:_(e,0,8),K(t,t.e+1,e)},L.isEqualTo=L.eq=function(e,t){return 0===w(this,new $(e,t))},L.isFinite=function(){return!!this.c},L.isGreaterThan=L.gt=function(e,t){return w(this,new $(e,t))>0},L.isGreaterThanOrEqualTo=L.gte=function(e,t){return 1===(t=w(this,new $(e,t)))||0===t},L.isInteger=function(){return!!this.c&&v(this.e/d)>this.c.length-2},L.isLessThan=L.lt=function(e,t){return 0>w(this,new $(e,t))},L.isLessThanOrEqualTo=L.lte=function(e,t){return -1===(t=w(this,new $(e,t)))||0===t},L.isNaN=function(){return!this.s},L.isNegative=function(){return this.s<0},L.isPositive=function(){return this.s>0},L.isZero=function(){return!!this.c&&0==this.c[0]},L.minus=function(e,t){var n,r,i,a,o=this,s=o.s;if(t=(e=new $(e,t)).s,!s||!t)return new $(NaN);if(s!=t)return e.s=-t,o.plus(e);var u=o.e/d,c=e.e/d,l=o.c,h=e.c;if(!u||!c){if(!l||!h)return l?(e.s=-t,e):new $(h?o:NaN);if(!l[0]||!h[0])return h[0]?(e.s=-t,e):new $(l[0]?o:-0)}if(u=v(u),c=v(c),l=l.slice(),s=u-c){for((a=s<0)?(s=-s,i=l):(c=u,i=h),i.reverse(),t=s;t--;i.push(0));i.reverse()}else for(r=(a=(s=l.length)<(t=h.length))?s:t,s=t=0;t0)for(;t--;l[n++]=0);for(t=f-1;r>s;){if(l[--r]=0;){for(n=0,p=S[i]%w,m=S[i]/w|0,a=i+(o=u);a>i;)s=m*(c=E[--o]%w)+(l=E[o]/w|0)*p,n=((c=p*c+s%w*w+g[a]+n)/y|0)+(s/w|0)+m*l,g[a--]=c%y;g[a]=n}return n?++r:g.splice(0,1),W(e,g,r)},L.negated=function(){var e=new $(this);return e.s=-e.s||null,e},L.plus=function(e,t){var n,r=this,i=r.s;if(t=(e=new $(e,t)).s,!i||!t)return new $(NaN);if(i!=t)return e.s=-t,r.minus(e);var a=r.e/d,o=e.e/d,s=r.c,u=e.c;if(!a||!o){if(!s||!u)return new $(i/0);if(!s[0]||!u[0])return u[0]?e:new $(s[0]?r:0*i)}if(a=v(a),o=v(o),s=s.slice(),i=a-o){for(i>0?(o=a,n=u):(i=-i,n=s),n.reverse();i--;n.push(0));n.reverse()}for((i=s.length)-(t=u.length)<0&&(n=u,u=s,s=n,t=i),i=0;t;)i=(s[--t]=s[t]+u[t]+i)/f|0,s[t]=f===s[t]?0:s[t]%f;return i&&(s=[i].concat(s),++o),W(e,s,o)},L.precision=L.sd=function(e,t){var n,r,i,a=this;if(null!=e&&!!e!==e)return _(e,1,m),null==t?t=D:_(t,0,8),K(new $(a),e,t);if(!(n=a.c))return null;if(r=(i=n.length-1)*d+1,i=n[i]){for(;i%10==0;i/=10,r--);for(i=n[0];i>=10;i/=10,r++);}return e&&a.e+1>r&&(r=a.e+1),r},L.shiftedBy=function(e){return _(e,-h,h),this.times("1e"+e)},L.squareRoot=L.sqrt=function(){var e,t,n,r,i,a=this,o=a.c,s=a.s,u=a.e,c=I+4,l=new $("0.5");if(1!==s||!o||!o[0])return new $(!s||s<0&&(!o||o[0])?NaN:o?a:1/0);if(0==(s=Math.sqrt(+V(a)))||s==1/0?(((t=y(o)).length+u)%2==0&&(t+="0"),s=Math.sqrt(+t),u=v((u+1)/2)-(u<0||u%2),t=s==1/0?"5e"+u:(t=s.toExponential()).slice(0,t.indexOf("e")+1)+u,n=new $(t)):n=new $(s+""),n.c[0]){for((s=(u=n.e)+c)<3&&(s=0);;)if(i=n,n=l.times(i.plus(M(a,i,c,1))),y(i.c).slice(0,s)===(t=y(n.c)).slice(0,s)){if(n.e0&&b>0){for(a=b%s||s,f=p.substr(0,a);a0&&(f+=l+p.slice(a)),h&&(f="-"+f)}r=d?f+(n.decimalSeparator||"")+((u=+n.fractionGroupSize)?d.replace(RegExp("\\d{"+u+"}\\B","g"),"$&"+(n.fractionGroupSeparator||"")):d):f}return(n.prefix||"")+r+(n.suffix||"")},L.toFraction=function(e){var t,n,r,i,a,o,s,u,l,f,h,b,m=this,g=m.c;if(null!=e&&(!(s=new $(e)).isInteger()&&(s.c||1!==s.s)||s.lt(C)))throw Error(c+"Argument "+(s.isInteger()?"out of range: ":"not an integer: ")+V(s));if(!g)return new $(m);for(t=new $(C),l=n=new $(C),r=u=new $(C),b=y(g),a=t.e=b.length-m.e-1,t.c[0]=p[(o=a%d)<0?d+o:o],e=!e||s.comparedTo(t)>0?a>0?t:l:s,o=j,j=1/0,s=new $(b),u.c[0]=0;f=M(s,t,0,1),1!=(i=n.plus(f.times(r))).comparedTo(e);)n=r,r=i,l=u.plus(f.times(i=l)),u=i,t=s.minus(f.times(i=t)),s=i;return i=M(e.minus(n),r,0,1),u=u.plus(i.times(l)),n=n.plus(i.times(r)),u.s=l.s=m.s,a*=2,h=1>M(l,r,a,D).minus(m).abs().comparedTo(M(u,n,a,D).minus(m).abs())?[l,r]:[u,n],j=o,h},L.toNumber=function(){return+V(this)},L.toPrecision=function(e,t){return null!=e&&_(e,1,m),z(this,e,t,2)},L.toString=function(e){var t,n=this,r=n.s,i=n.e;return null===i?r?(t="Infinity",r<0&&(t="-"+t)):t="NaN":(null==e?t=i<=N||i>=P?S(y(n.c),i):k(y(n.c),i,"0"):10===e?(n=K(new $(n),I+i+1,D),t=k(y(n.c),n.e,"0")):(_(e,2,H.length,"Base"),t=O(k(y(n.c),i,"0"),10,e,r,!0)),r<0&&n.c[0]&&(t="-"+t)),t},L.valueOf=L.toJSON=function(){return V(this)},L._isBigNumber=!0,null!=e&&$.set(e),$}function v(e){var t=0|e;return e>0||e===t?t:t-1}function y(e){for(var t,n,r=1,i=e.length,a=e[0]+"";rc^n?1:-1;for(o=0,s=(u=i.length)<(c=a.length)?u:c;oa[o]^n?1:-1;return u==c?0:u>c^n?1:-1}function _(e,t,n,r){if(en||e!==u(e))throw Error(c+(r||"Argument")+("number"==typeof e?en?" out of range: ":" not an integer: ":" not a primitive number: ")+String(e))}function E(e){var t=e.c.length-1;return v(e.e/d)==t&&e.c[t]%2!=0}function S(e,t){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(t<0?"e":"e+")+t}function k(e,t,n){var r,i;if(t<0){for(i=n+".";++t;i+=n);e=i+e}else if(r=e.length,++t>r){for(i=n,t-=r;--t;i+=n);e+=i}else ti});let i=r},48764(e,t,n){"use strict";/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ var r=n(79742),i=n(80645),a="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=c,t.SlowBuffer=w,t.INSPECT_MAX_BYTES=50;var o=2147483647;function s(){try{var e=new Uint8Array(1),t={foo:function(){return 42}};return Object.setPrototypeOf(t,Uint8Array.prototype),Object.setPrototypeOf(e,t),42===e.foo()}catch(n){return!1}}function u(e){if(e>o)throw RangeError('The value "'+e+'" is invalid for option "size"');var t=new Uint8Array(e);return Object.setPrototypeOf(t,c.prototype),t}function c(e,t,n){if("number"==typeof e){if("string"==typeof t)throw TypeError('The "string" argument must be of type string. Received type number');return h(e)}return l(e,t,n)}function l(e,t,n){if("string"==typeof e)return p(e,t);if(ArrayBuffer.isView(e))return m(e);if(null==e)throw TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(X(e,ArrayBuffer)||e&&X(e.buffer,ArrayBuffer)||"undefined"!=typeof SharedArrayBuffer&&(X(e,SharedArrayBuffer)||e&&X(e.buffer,SharedArrayBuffer)))return g(e,t,n);if("number"==typeof e)throw TypeError('The "value" argument must not be of type number. Received type number');var r=e.valueOf&&e.valueOf();if(null!=r&&r!==e)return c.from(r,t,n);var i=v(e);if(i)return i;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return c.from(e[Symbol.toPrimitive]("string"),t,n);throw TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function f(e){if("number"!=typeof e)throw TypeError('"size" argument must be of type number');if(e<0)throw RangeError('The value "'+e+'" is invalid for option "size"')}function d(e,t,n){return(f(e),e<=0)?u(e):void 0!==t?"string"==typeof n?u(e).fill(t,n):u(e).fill(t):u(e)}function h(e){return f(e),u(e<0?0:0|y(e))}function p(e,t){if(("string"!=typeof t||""===t)&&(t="utf8"),!c.isEncoding(t))throw TypeError("Unknown encoding: "+t);var n=0|_(e,t),r=u(n),i=r.write(e,t);return i!==n&&(r=r.slice(0,i)),r}function b(e){for(var t=e.length<0?0:0|y(e.length),n=u(t),r=0;r=o)throw RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+o.toString(16)+" bytes");return 0|e}function w(e){return+e!=e&&(e=0),c.alloc(+e)}function _(e,t){if(c.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||X(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);var n=e.length,r=arguments.length>2&&!0===arguments[2];if(!r&&0===n)return 0;for(var i=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":return W(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return q(e).length;default:if(i)return r?-1:W(e).length;t=(""+t).toLowerCase(),i=!0}}function E(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length||((void 0===n||n>this.length)&&(n=this.length),n<=0||(n>>>=0)<=(t>>>=0)))return"";for(e||(e="utf8");;)switch(e){case"hex":return j(this,t,n);case"utf8":case"utf-8":return I(this,t,n);case"ascii":return P(this,t,n);case"latin1":case"binary":return R(this,t,n);case"base64":return C(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return F(this,t,n);default:if(r)throw TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function S(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function k(e,t,n,r,i){if(0===e.length)return -1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),J(n=+n)&&(n=i?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(i)return -1;n=e.length-1}else if(n<0){if(!i)return -1;n=0}if("string"==typeof t&&(t=c.from(t,r)),c.isBuffer(t))return 0===t.length?-1:x(e,t,n,r,i);if("number"==typeof t)return(t&=255,"function"==typeof Uint8Array.prototype.indexOf)?i?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):x(e,[t],n,r,i);throw TypeError("val must be string, number or Buffer")}function x(e,t,n,r,i){var a,o=1,s=e.length,u=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return -1;o=2,s/=2,u/=2,n/=2}function c(e,t){return 1===o?e[t]:e.readUInt16BE(t*o)}if(i){var l=-1;for(a=n;as&&(n=s-u),a=n;a>=0;a--){for(var f=!0,d=0;di&&(r=i):r=i;var a=t.length;r>a/2&&(r=a/2);for(var o=0;o239?4:c>223?3:c>191?2:1;if(i+f<=n)switch(f){case 1:c<128&&(l=c);break;case 2:(192&(a=e[i+1]))==128&&(u=(31&c)<<6|63&a)>127&&(l=u);break;case 3:a=e[i+1],o=e[i+2],(192&a)==128&&(192&o)==128&&(u=(15&c)<<12|(63&a)<<6|63&o)>2047&&(u<55296||u>57343)&&(l=u);break;case 4:a=e[i+1],o=e[i+2],s=e[i+3],(192&a)==128&&(192&o)==128&&(192&s)==128&&(u=(15&c)<<18|(63&a)<<12|(63&o)<<6|63&s)>65535&&u<1114112&&(l=u)}null===l?(l=65533,f=1):l>65535&&(l-=65536,r.push(l>>>10&1023|55296),l=56320|1023&l),r.push(l),i+=f}return N(r)}t.kMaxLength=o,c.TYPED_ARRAY_SUPPORT=s(),c.TYPED_ARRAY_SUPPORT||"undefined"==typeof console||"function"!=typeof console.error||console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."),Object.defineProperty(c.prototype,"parent",{enumerable:!0,get:function(){if(c.isBuffer(this))return this.buffer}}),Object.defineProperty(c.prototype,"offset",{enumerable:!0,get:function(){if(c.isBuffer(this))return this.byteOffset}}),c.poolSize=8192,c.from=function(e,t,n){return l(e,t,n)},Object.setPrototypeOf(c.prototype,Uint8Array.prototype),Object.setPrototypeOf(c,Uint8Array),c.alloc=function(e,t,n){return d(e,t,n)},c.allocUnsafe=function(e){return h(e)},c.allocUnsafeSlow=function(e){return h(e)},c.isBuffer=function(e){return null!=e&&!0===e._isBuffer&&e!==c.prototype},c.compare=function(e,t){if(X(e,Uint8Array)&&(e=c.from(e,e.offset,e.byteLength)),X(t,Uint8Array)&&(t=c.from(t,t.offset,t.byteLength)),!c.isBuffer(e)||!c.isBuffer(t))throw TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(e===t)return 0;for(var n=e.length,r=t.length,i=0,a=Math.min(n,r);ir.length?c.from(a).copy(r,i):Uint8Array.prototype.set.call(r,a,i);else if(c.isBuffer(a))a.copy(r,i);else throw TypeError('"list" argument must be an Array of Buffers');i+=a.length}return r},c.byteLength=_,c.prototype._isBuffer=!0,c.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw RangeError("Buffer size must be a multiple of 16-bits");for(var t=0;tn&&(e+=" ... "),""},a&&(c.prototype[a]=c.prototype.inspect),c.prototype.compare=function(e,t,n,r,i){if(X(e,Uint8Array)&&(e=c.from(e,e.offset,e.byteLength)),!c.isBuffer(e))throw TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===i&&(i=this.length),t<0||n>e.length||r<0||i>this.length)throw RangeError("out of range index");if(r>=i&&t>=n)return 0;if(r>=i)return -1;if(t>=n)return 1;if(t>>>=0,n>>>=0,r>>>=0,i>>>=0,this===e)return 0;for(var a=i-r,o=n-t,s=Math.min(a,o),u=this.slice(r,i),l=e.slice(t,n),f=0;f>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0);else throw Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");var i=this.length-t;if((void 0===n||n>i)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var a=!1;;)switch(r){case"hex":return T(this,e,t,n);case"utf8":case"utf-8":return M(this,e,t,n);case"ascii":case"latin1":case"binary":return O(this,e,t,n);case"base64":return A(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return L(this,e,t,n);default:if(a)throw TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),a=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var D=4096;function N(e){var t=e.length;if(t<=D)return String.fromCharCode.apply(String,e);for(var n="",r=0;rr)&&(n=r);for(var i="",a=t;an)throw RangeError("Trying to access beyond buffer length")}function B(e,t,n,r,i,a){if(!c.isBuffer(e))throw TypeError('"buffer" argument must be a Buffer instance');if(t>i||te.length)throw RangeError("Index out of range")}function U(e,t,n,r,i,a){if(n+r>e.length||n<0)throw RangeError("Index out of range")}function H(e,t,n,r,a){return t=+t,n>>>=0,a||U(e,t,n,4,34028234663852886e22,-34028234663852886e22),i.write(e,t,n,r,23,4),n+4}function $(e,t,n,r,a){return t=+t,n>>>=0,a||U(e,t,n,8,17976931348623157e292,-17976931348623157e292),i.write(e,t,n,r,52,8),n+8}c.prototype.slice=function(e,t){var n=this.length;e=~~e,t=void 0===t?n:~~t,e<0?(e+=n)<0&&(e=0):e>n&&(e=n),t<0?(t+=n)<0&&(t=0):t>n&&(t=n),t>>=0,t>>>=0,n||Y(e,t,this.length);for(var r=this[e],i=1,a=0;++a>>=0,t>>>=0,n||Y(e,t,this.length);for(var r=this[e+--t],i=1;t>0&&(i*=256);)r+=this[e+--t]*i;return r},c.prototype.readUint8=c.prototype.readUInt8=function(e,t){return e>>>=0,t||Y(e,1,this.length),this[e]},c.prototype.readUint16LE=c.prototype.readUInt16LE=function(e,t){return e>>>=0,t||Y(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUint16BE=c.prototype.readUInt16BE=function(e,t){return e>>>=0,t||Y(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUint32LE=c.prototype.readUInt32LE=function(e,t){return e>>>=0,t||Y(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},c.prototype.readUint32BE=c.prototype.readUInt32BE=function(e,t){return e>>>=0,t||Y(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},c.prototype.readIntLE=function(e,t,n){e>>>=0,t>>>=0,n||Y(e,t,this.length);for(var r=this[e],i=1,a=0;++a=(i*=128)&&(r-=Math.pow(2,8*t)),r},c.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||Y(e,t,this.length);for(var r=t,i=1,a=this[e+--r];r>0&&(i*=256);)a+=this[e+--r]*i;return a>=(i*=128)&&(a-=Math.pow(2,8*t)),a},c.prototype.readInt8=function(e,t){return(e>>>=0,t||Y(e,1,this.length),128&this[e])?-((255-this[e]+1)*1):this[e]},c.prototype.readInt16LE=function(e,t){e>>>=0,t||Y(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},c.prototype.readInt16BE=function(e,t){e>>>=0,t||Y(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},c.prototype.readInt32LE=function(e,t){return e>>>=0,t||Y(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},c.prototype.readInt32BE=function(e,t){return e>>>=0,t||Y(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},c.prototype.readFloatLE=function(e,t){return e>>>=0,t||Y(e,4,this.length),i.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return e>>>=0,t||Y(e,4,this.length),i.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return e>>>=0,t||Y(e,8,this.length),i.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return e>>>=0,t||Y(e,8,this.length),i.read(this,e,!1,52,8)},c.prototype.writeUintLE=c.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t>>>=0,n>>>=0,!r){var i=Math.pow(2,8*n)-1;B(this,e,t,n,i,0)}var a=1,o=0;for(this[t]=255&e;++o>>=0,n>>>=0,!r){var i=Math.pow(2,8*n)-1;B(this,e,t,n,i,0)}var a=n-1,o=1;for(this[t+a]=255&e;--a>=0&&(o*=256);)this[t+a]=e/o&255;return t+n},c.prototype.writeUint8=c.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,1,255,0),this[t]=255&e,t+1},c.prototype.writeUint16LE=c.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},c.prototype.writeUint16BE=c.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},c.prototype.writeUint32LE=c.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},c.prototype.writeUint32BE=c.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},c.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){var i=Math.pow(2,8*n-1);B(this,e,t,n,i-1,-i)}var a=0,o=1,s=0;for(this[t]=255&e;++a>0)-s&255;return t+n},c.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){var i=Math.pow(2,8*n-1);B(this,e,t,n,i-1,-i)}var a=n-1,o=1,s=0;for(this[t+a]=255&e;--a>=0&&(o*=256);)e<0&&0===s&&0!==this[t+a+1]&&(s=1),this[t+a]=(e/o>>0)-s&255;return t+n},c.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},c.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},c.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},c.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},c.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||B(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},c.prototype.writeFloatLE=function(e,t,n){return H(this,e,t,!0,n)},c.prototype.writeFloatBE=function(e,t,n){return H(this,e,t,!1,n)},c.prototype.writeDoubleLE=function(e,t,n){return $(this,e,t,!0,n)},c.prototype.writeDoubleBE=function(e,t,n){return $(this,e,t,!1,n)},c.prototype.copy=function(e,t,n,r){if(!c.isBuffer(e))throw TypeError("argument should be a Buffer");if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw RangeError("Index out of range");if(r<0)throw RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!i){if(n>56319||o+1===r){(t-=3)>-1&&a.push(239,191,189);continue}i=n;continue}if(n<56320){(t-=3)>-1&&a.push(239,191,189),i=n;continue}n=(i-55296<<10|n-56320)+65536}else i&&(t-=3)>-1&&a.push(239,191,189);if(i=null,n<128){if((t-=1)<0)break;a.push(n)}else if(n<2048){if((t-=2)<0)break;a.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;a.push(n>>12|224,n>>6&63|128,63&n|128)}else if(n<1114112){if((t-=4)<0)break;a.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}else throw Error("Invalid code point")}return a}function K(e){for(var t=[],n=0;n>8,a.push(i=n%256),a.push(r);return a}function q(e){return r.toByteArray(G(e))}function Z(e,t,n,r){for(var i=0;i=t.length)&&!(i>=e.length);++i)t[i+n]=e[i];return i}function X(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function J(e){return e!=e}var Q=function(){for(var e="0123456789abcdef",t=Array(256),n=0;n<16;++n)for(var r=16*n,i=0;i<16;++i)t[r+i]=e[n]+e[i];return t}()},94184(e,t){var n,r; /*! + Copyright (c) 2018 Jed Watson. + Licensed under the MIT License (MIT), see + http://jedwatson.github.io/classnames +*/ !function(){"use strict";var i={}.hasOwnProperty;function a(){for(var e=[],t=0;t>8&255]},F=function(e){return[255&e,e>>8&255,e>>16&255,e>>24&255]},Y=function(e){return e[3]<<24|e[2]<<16|e[1]<<8|e[0]},B=function(e){return N(e,23,4)},U=function(e){return N(e,52,8)},H=function(e,t){g(e[x],t,{get:function(){return _(this)[t]}})},$=function(e,t,n,r){var i=d(n),a=_(e);if(i+t>a.byteLength)throw D(M);var o=_(a.buffer).bytes,s=i+a.byteOffset,u=o.slice(s,s+t);return r?u:u.reverse()},z=function(e,t,n,r,i,a){var o=d(n),s=_(e);if(o+t>s.byteLength)throw D(M);for(var u=_(s.buffer).bytes,c=o+s.byteOffset,l=r(+i),f=0;fV;)(G=K[V++])in A||o(A,G,O[G]);W.constructor=A}b&&p(C)!==I&&b(C,I);var q=new L(new A(2)),Z=C.setInt8;q.setInt8(0,2147483648),q.setInt8(1,2147483649),(q.getInt8(0)||!q.getInt8(1))&&s(C,{setInt8:function(e,t){Z.call(this,e,t<<24>>24)},setUint8:function(e,t){Z.call(this,e,t<<24>>24)}},{unsafe:!0})}else A=function(e){c(this,A,S);var t=d(e);E(this,{bytes:v.call(Array(t),0),byteLength:t}),i||(this.byteLength=t)},L=function(e,t,n){c(this,L,k),c(e,A,k);var r=_(e).byteLength,a=l(t);if(a<0||a>r)throw D("Wrong offset");if(n=void 0===n?r-a:f(n),a+n>r)throw D(T);E(this,{buffer:e,byteLength:n,byteOffset:a}),i||(this.buffer=e,this.byteLength=n,this.byteOffset=a)},i&&(H(A,"byteLength"),H(L,"buffer"),H(L,"byteLength"),H(L,"byteOffset")),s(L[x],{getInt8:function(e){return $(this,1,e)[0]<<24>>24},getUint8:function(e){return $(this,1,e)[0]},getInt16:function(e){var t=$(this,2,e,arguments.length>1?arguments[1]:void 0);return(t[1]<<8|t[0])<<16>>16},getUint16:function(e){var t=$(this,2,e,arguments.length>1?arguments[1]:void 0);return t[1]<<8|t[0]},getInt32:function(e){return Y($(this,4,e,arguments.length>1?arguments[1]:void 0))},getUint32:function(e){return Y($(this,4,e,arguments.length>1?arguments[1]:void 0))>>>0},getFloat32:function(e){return P($(this,4,e,arguments.length>1?arguments[1]:void 0),23)},getFloat64:function(e){return P($(this,8,e,arguments.length>1?arguments[1]:void 0),52)},setInt8:function(e,t){z(this,1,e,R,t)},setUint8:function(e,t){z(this,1,e,R,t)},setInt16:function(e,t){z(this,2,e,j,t,arguments.length>2?arguments[2]:void 0)},setUint16:function(e,t){z(this,2,e,j,t,arguments.length>2?arguments[2]:void 0)},setInt32:function(e,t){z(this,4,e,F,t,arguments.length>2?arguments[2]:void 0)},setUint32:function(e,t){z(this,4,e,F,t,arguments.length>2?arguments[2]:void 0)},setFloat32:function(e,t){z(this,4,e,B,t,arguments.length>2?arguments[2]:void 0)},setFloat64:function(e,t){z(this,8,e,U,t,arguments.length>2?arguments[2]:void 0)}});y(A,S),y(L,k),e.exports={ArrayBuffer:A,DataView:L}},1048(e,t,n){"use strict";var r=n(47908),i=n(51400),a=n(17466),o=Math.min;e.exports=[].copyWithin||function(e,t){var n=r(this),s=a(n.length),u=i(e,s),c=i(t,s),l=arguments.length>2?arguments[2]:void 0,f=o((void 0===l?s:i(l,s))-c,s-u),d=1;for(c0;)c in n?n[u]=n[c]:delete n[u],u+=d,c+=d;return n}},21285(e,t,n){"use strict";var r=n(47908),i=n(51400),a=n(17466);e.exports=function(e){for(var t=r(this),n=a(t.length),o=arguments.length,s=i(o>1?arguments[1]:void 0,n),u=o>2?arguments[2]:void 0,c=void 0===u?n:i(u,n);c>s;)t[s++]=e;return t}},18533(e,t,n){"use strict";var r=n(42092).forEach,i=n(9341)("forEach");e.exports=i?[].forEach:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}},97745(e){e.exports=function(e,t){for(var n=0,r=t.length,i=new e(r);r>n;)i[n]=t[n++];return i}},48457(e,t,n){"use strict";var r=n(49974),i=n(47908),a=n(53411),o=n(97659),s=n(17466),u=n(86135),c=n(18554),l=n(71246);e.exports=function(e){var t,n,f,d,h,p,b=i(e),m="function"==typeof this?this:Array,g=arguments.length,v=g>1?arguments[1]:void 0,y=void 0!==v,w=l(b),_=0;if(y&&(v=r(v,g>2?arguments[2]:void 0,2)),void 0==w||m==Array&&o(w))for(t=s(b.length),n=new m(t);t>_;_++)p=y?v(b[_],_):b[_],u(n,_,p);else for(h=(d=c(b,w)).next,n=new m;!(f=h.call(d)).done;_++)p=y?a(d,v,[f.value,_],!0):f.value,u(n,_,p);return n.length=_,n}},61386(e,t,n){var r=n(49974),i=n(68361),a=n(47908),o=n(17466),s=n(34948),u=n(70030),c=n(97745),l=[].push;e.exports=function(e,t,n,f){for(var d,h,p,b=a(e),m=i(b),g=r(t,n,3),v=u(null),y=o(m.length),w=0;y>w;w++)(h=s(g(p=m[w],w,b)))in v?l.call(v[h],p):v[h]=[p];if(f&&(d=f(b))!==Array)for(h in v)v[h]=c(d,v[h]);return v}},41318(e,t,n){var r=n(45656),i=n(17466),a=n(51400),o=function(e){return function(t,n,o){var s,u=r(t),c=i(u.length),l=a(o,c);if(e&&n!=n){for(;c>l;)if((s=u[l++])!=s)return!0}else for(;c>l;l++)if((e||l in u)&&u[l]===n)return e||l||0;return!e&&-1}};e.exports={includes:o(!0),indexOf:o(!1)}},9671(e,t,n){var r=n(49974),i=n(68361),a=n(47908),o=n(17466),s=function(e){var t=1==e;return function(n,s,u){for(var c,l,f=a(n),d=i(f),h=r(s,u,3),p=o(d.length);p-- >0;)if(l=h(c=d[p],p,f))switch(e){case 0:return c;case 1:return p}return t?-1:void 0}};e.exports={findLast:s(0),findLastIndex:s(1)}},42092(e,t,n){var r=n(49974),i=n(68361),a=n(47908),o=n(17466),s=n(65417),u=[].push,c=function(e){var t=1==e,n=2==e,c=3==e,l=4==e,f=6==e,d=7==e,h=5==e||f;return function(p,b,m,g){for(var v,y,w=a(p),_=i(w),E=r(b,m,3),S=o(_.length),k=0,x=g||s,T=t?x(p,S):n||d?x(p,0):void 0;S>k;k++)if((h||k in _)&&(y=E(v=_[k],k,w),e)){if(t)T[k]=y;else if(y)switch(e){case 3:return!0;case 5:return v;case 6:return k;case 2:u.call(T,v)}else switch(e){case 4:return!1;case 7:u.call(T,v)}}return f?-1:c||l?l:T}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6),filterReject:c(7)}},86583(e,t,n){"use strict";var r=n(45656),i=n(99958),a=n(17466),o=n(9341),s=Math.min,u=[].lastIndexOf,c=!!u&&1/[1].lastIndexOf(1,-0)<0,l=o("lastIndexOf"),f=c||!l;e.exports=f?function(e){if(c)return u.apply(this,arguments)||0;var t=r(this),n=a(t.length),o=n-1;for(arguments.length>1&&(o=s(o,i(arguments[1]))),o<0&&(o=n+o);o>=0;o--)if(o in t&&t[o]===e)return o||0;return -1}:u},81194(e,t,n){var r=n(47293),i=n(5112),a=n(7392),o=i("species");e.exports=function(e){return a>=51||!r(function(){var t=[];return(t.constructor={})[o]=function(){return{foo:1}},1!==t[e](Boolean).foo})}},9341(e,t,n){"use strict";var r=n(47293);e.exports=function(e,t){var n=[][e];return!!n&&r(function(){n.call(null,t||function(){throw 1},1)})}},53671(e,t,n){var r=n(13099),i=n(47908),a=n(68361),o=n(17466),s=function(e){return function(t,n,s,u){r(n);var c=i(t),l=a(c),f=o(c.length),d=e?f-1:0,h=e?-1:1;if(s<2)for(;;){if(d in l){u=l[d],d+=h;break}if(d+=h,e?d<0:f<=d)throw TypeError("Reduce of empty array with no initial value")}for(;e?d>=0:f>d;d+=h)d in l&&(u=n(u,l[d],d,c));return u}};e.exports={left:s(!1),right:s(!0)}},94362(e){var t=Math.floor,n=function(e,a){var o=e.length,s=t(o/2);return o<8?r(e,a):i(n(e.slice(0,s),a),n(e.slice(s),a),a)},r=function(e,t){for(var n,r,i=e.length,a=1;a0;)e[r]=e[--r];r!==a++&&(e[r]=n)}return e},i=function(e,t,n){for(var r=e.length,i=t.length,a=0,o=0,s=[];a=n(e[a],t[o])?e[a++]:t[o++]):s.push(a1?arguments[1]:void 0;return(r(this),(t=void 0!==c)&&r(c),void 0==e)?new this:(n=[],t?(o=0,s=i(c,u>2?arguments[2]:void 0,2),a(e,function(e){n.push(s(e,o++))})):a(e,n.push,{that:n}),new this(n))}},82044(e){"use strict";e.exports=function(){for(var e=arguments.length,t=Array(e);e--;)t[e]=arguments[e];return new this(t)}},95631(e,t,n){"use strict";var r=n(3070).f,i=n(70030),a=n(12248),o=n(49974),s=n(25787),u=n(20408),c=n(70654),l=n(96340),f=n(19781),d=n(62423).fastKey,h=n(29909),p=h.set,b=h.getterFor;e.exports={getConstructor:function(e,t,n,c){var l=e(function(e,r){s(e,l,t),p(e,{type:t,index:i(null),first:void 0,last:void 0,size:0}),f||(e.size=0),void 0!=r&&u(r,e[c],{that:e,AS_ENTRIES:n})}),h=b(t),m=function(e,t,n){var r,i,a=h(e),o=g(e,t);return o?o.value=n:(a.last=o={index:i=d(t,!0),key:t,value:n,previous:r=a.last,next:void 0,removed:!1},a.first||(a.first=o),r&&(r.next=o),f?a.size++:e.size++,"F"!==i&&(a.index[i]=o)),e},g=function(e,t){var n,r=h(e),i=d(t);if("F"!==i)return r.index[i];for(n=r.first;n;n=n.next)if(n.key==t)return n};return a(l.prototype,{clear:function(){for(var e=this,t=h(e),n=t.index,r=t.first;r;)r.removed=!0,r.previous&&(r.previous=r.previous.next=void 0),delete n[r.index],r=r.next;t.first=t.last=void 0,f?t.size=0:e.size=0},delete:function(e){var t=this,n=h(t),r=g(t,e);if(r){var i=r.next,a=r.previous;delete n.index[r.index],r.removed=!0,a&&(a.next=i),i&&(i.previous=a),n.first==r&&(n.first=i),n.last==r&&(n.last=a),f?n.size--:t.size--}return!!r},forEach:function(e){for(var t,n=h(this),r=o(e,arguments.length>1?arguments[1]:void 0,3);t=t?t.next:n.first;)for(r(t.value,t.key,this);t&&t.removed;)t=t.previous},has:function(e){return!!g(this,e)}}),a(l.prototype,n?{get:function(e){var t=g(this,e);return t&&t.value},set:function(e,t){return m(this,0===e?0:e,t)}}:{add:function(e){return m(this,e=0===e?0:e,e)}}),f&&r(l.prototype,"size",{get:function(){return h(this).size}}),l},setStrong:function(e,t,n){var r=t+" Iterator",i=b(t),a=b(r);c(e,t,function(e,t){p(this,{type:r,target:e,state:i(e),kind:t,last:void 0})},function(){for(var e=a(this),t=e.kind,n=e.last;n&&n.removed;)n=n.previous;return e.target&&(e.last=n=n?n.next:e.state.first)?"keys"==t?{value:n.key,done:!1}:"values"==t?{value:n.value,done:!1}:{value:[n.key,n.value],done:!1}:(e.target=void 0,{value:void 0,done:!0})},n?"entries":"values",!n,!0),l(t)}}},29320(e,t,n){"use strict";var r=n(12248),i=n(62423).getWeakData,a=n(19670),o=n(70111),s=n(25787),u=n(20408),c=n(42092),l=n(86656),f=n(29909),d=f.set,h=f.getterFor,p=c.find,b=c.findIndex,m=0,g=function(e){return e.frozen||(e.frozen=new v)},v=function(){this.entries=[]},y=function(e,t){return p(e.entries,function(e){return e[0]===t})};v.prototype={get:function(e){var t=y(this,e);if(t)return t[1]},has:function(e){return!!y(this,e)},set:function(e,t){var n=y(this,e);n?n[1]=t:this.entries.push([e,t])},delete:function(e){var t=b(this.entries,function(t){return t[0]===e});return~t&&this.entries.splice(t,1),!!~t}},e.exports={getConstructor:function(e,t,n,c){var f=e(function(e,r){s(e,f,t),d(e,{type:t,id:m++,frozen:void 0}),void 0!=r&&u(r,e[c],{that:e,AS_ENTRIES:n})}),p=h(t),b=function(e,t,n){var r=p(e),o=i(a(t),!0);return!0===o?g(r).set(t,n):o[r.id]=n,e};return r(f.prototype,{delete:function(e){var t=p(this);if(!o(e))return!1;var n=i(e);return!0===n?g(t).delete(e):n&&l(n,t.id)&&delete n[t.id]},has:function(e){var t=p(this);if(!o(e))return!1;var n=i(e);return!0===n?g(t).has(e):n&&l(n,t.id)}}),r(f.prototype,n?{get:function(e){var t=p(this);if(o(e)){var n=i(e);return!0===n?g(t).get(e):n?n[t.id]:void 0}},set:function(e,t){return b(this,e,t)}}:{add:function(e){return b(this,e,!0)}}),f}}},77710(e,t,n){"use strict";var r=n(82109),i=n(17854),a=n(54705),o=n(31320),s=n(62423),u=n(20408),c=n(25787),l=n(70111),f=n(47293),d=n(17072),h=n(58003),p=n(79587);e.exports=function(e,t,n){var b=-1!==e.indexOf("Map"),m=-1!==e.indexOf("Weak"),g=b?"set":"add",v=i[e],y=v&&v.prototype,w=v,_={},E=function(e){var t=y[e];o(y,e,"add"==e?function(e){return t.call(this,0===e?0:e),this}:"delete"==e?function(e){return(!m||!!l(e))&&t.call(this,0===e?0:e)}:"get"==e?function(e){return m&&!l(e)?void 0:t.call(this,0===e?0:e)}:"has"==e?function(e){return(!m||!!l(e))&&t.call(this,0===e?0:e)}:function(e,n){return t.call(this,0===e?0:e,n),this})};if(a(e,"function"!=typeof v||!(m||y.forEach&&!f(function(){new v().entries().next()}))))w=n.getConstructor(t,e,b,g),s.enable();else if(a(e,!0)){var S=new w,k=S[g](m?{}:-0,1)!=S,x=f(function(){S.has(1)}),T=d(function(e){new v(e)}),M=!m&&f(function(){for(var e=new v,t=5;t--;)e[g](t,t);return!e.has(-0)});T||((w=t(function(t,n){c(t,w,e);var r=p(new v,t,w);return void 0!=n&&u(n,r[g],{that:r,AS_ENTRIES:b}),r})).prototype=y,y.constructor=w),(x||M)&&(E("delete"),E("has"),b&&E("get")),(M||k)&&E(g),m&&y.clear&&delete y.clear}return _[e]=w,r({global:!0,forced:w!=v},_),h(w,e),m||n.setStrong(w,e,b),w}},10313(e,t,n){var r=n(51532),i=n(4129),a=n(70030),o=n(70111),s=function(){this.object=null,this.symbol=null,this.primitives=null,this.objectsByIndex=a(null)};s.prototype.get=function(e,t){return this[e]||(this[e]=t())},s.prototype.next=function(e,t,n){var a=n?this.objectsByIndex[e]||(this.objectsByIndex[e]=new i):this.primitives||(this.primitives=new r),o=a.get(t);return o||a.set(t,o=new s),o};var u=new s;e.exports=function(){var e,t,n=u,r=arguments.length;for(e=0;e"+s+""}},24994(e,t,n){"use strict";var r=n(13383).IteratorPrototype,i=n(70030),a=n(79114),o=n(58003),s=n(97497),u=function(){return this};e.exports=function(e,t,n){var c=t+" Iterator";return e.prototype=i(r,{next:a(1,n)}),o(e,c,!1,!0),s[c]=u,e}},68880(e,t,n){var r=n(19781),i=n(3070),a=n(79114);e.exports=r?function(e,t,n){return i.f(e,t,a(1,n))}:function(e,t,n){return e[t]=n,e}},79114(e){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},86135(e,t,n){"use strict";var r=n(34948),i=n(3070),a=n(79114);e.exports=function(e,t,n){var o=r(t);o in e?i.f(e,o,a(0,n)):e[o]=n}},85573(e,t,n){"use strict";var r=n(47293),i=n(76650).start,a=Math.abs,o=Date.prototype,s=o.getTime,u=o.toISOString;e.exports=r(function(){return"0385-07-25T07:06:39.999Z"!=u.call(new Date(-5e13-1))})||!r(function(){u.call(new Date(NaN))})?function(){if(!isFinite(s.call(this)))throw RangeError("Invalid time value");var e=this,t=e.getUTCFullYear(),n=e.getUTCMilliseconds(),r=t<0?"-":t>9999?"+":"";return r+i(a(t),r?6:4,0)+"-"+i(e.getUTCMonth()+1,2,0)+"-"+i(e.getUTCDate(),2,0)+"T"+i(e.getUTCHours(),2,0)+":"+i(e.getUTCMinutes(),2,0)+":"+i(e.getUTCSeconds(),2,0)+"."+i(n,3,0)+"Z"}:u},38709(e,t,n){"use strict";var r=n(19670),i=n(92140);e.exports=function(e){if(r(this),"string"===e||"default"===e)e="string";else if("number"!==e)throw TypeError("Incorrect hint");return i(this,e)}},70654(e,t,n){"use strict";var r=n(82109),i=n(24994),a=n(79518),o=n(27674),s=n(58003),u=n(68880),c=n(31320),l=n(5112),f=n(31913),d=n(97497),h=n(13383),p=h.IteratorPrototype,b=h.BUGGY_SAFARI_ITERATORS,m=l("iterator"),g="keys",v="values",y="entries",w=function(){return this};e.exports=function(e,t,n,l,h,_,E){i(n,t,l);var S,k,x,T=function(e){if(e===h&&C)return C;if(!b&&e in A)return A[e];switch(e){case g:case v:case y:return function(){return new n(this,e)}}return function(){return new n(this)}},M=t+" Iterator",O=!1,A=e.prototype,L=A[m]||A["@@iterator"]||h&&A[h],C=!b&&L||T(h),I="Array"==t&&A.entries||L;if(I&&(S=a(I.call(new e)),p!==Object.prototype&&S.next&&(f||a(S)===p||(o?o(S,p):"function"!=typeof S[m]&&u(S,m,w)),s(S,M,!0,!0),f&&(d[M]=w))),h==v&&L&&L.name!==v&&(O=!0,C=function(){return L.call(this)}),(!f||E)&&A[m]!==C&&u(A,m,C),d[t]=C,h){if(k={values:T(v),keys:_?C:T(g),entries:T(y)},E)for(x in k)!b&&!O&&x in A||c(A,x,k[x]);else r({target:t,proto:!0,forced:b||O},k)}return k}},97235(e,t,n){var r=n(40857),i=n(86656),a=n(6061),o=n(3070).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});i(t,e)||o(t,e,{value:a.f(e)})}},19781(e,t,n){var r=n(47293);e.exports=!r(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})},80317(e,t,n){var r=n(17854),i=n(70111),a=r.document,o=i(a)&&i(a.createElement);e.exports=function(e){return o?a.createElement(e):{}}},48324(e){e.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},68886(e,t,n){var r=n(88113).match(/firefox\/(\d+)/i);e.exports=!!r&&+r[1]},7871(e){e.exports="object"==typeof window},30256(e,t,n){var r=n(88113);e.exports=/MSIE|Trident/.test(r)},71528(e,t,n){var r=n(88113),i=n(17854);e.exports=/ipad|iphone|ipod/i.test(r)&&void 0!==i.Pebble},6833(e,t,n){var r=n(88113);e.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(r)},35268(e,t,n){var r=n(84326),i=n(17854);e.exports="process"==r(i.process)},71036(e,t,n){var r=n(88113);e.exports=/web0s(?!.*chrome)/i.test(r)},88113(e,t,n){var r=n(35005);e.exports=r("navigator","userAgent")||""},7392(e,t,n){var r,i,a=n(17854),o=n(88113),s=a.process,u=a.Deno,c=s&&s.versions||u&&u.version,l=c&&c.v8;l?i=(r=l.split("."))[0]<4?1:r[0]+r[1]:o&&(!(r=o.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=o.match(/Chrome\/(\d+)/))&&(i=r[1]),e.exports=i&&+i},98008(e,t,n){var r=n(88113).match(/AppleWebKit\/(\d+)\./);e.exports=!!r&&+r[1]},80748(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},82109(e,t,n){var r=n(17854),i=n(31236).f,a=n(68880),o=n(31320),s=n(83505),u=n(99920),c=n(54705);e.exports=function(e,t){var n,l,f,d,h,p,b=e.target,m=e.global,g=e.stat;if(l=m?r:g?r[b]||s(b,{}):(r[b]||{}).prototype)for(f in t){if(h=t[f],d=e.noTargetGet?(p=i(l,f))&&p.value:l[f],!(n=c(m?f:b+(g?".":"#")+f,e.forced))&&void 0!==d){if(typeof h==typeof d)continue;u(h,d)}(e.sham||d&&d.sham)&&a(h,"sham",!0),o(l,f,h,e)}}},47293(e){e.exports=function(e){try{return!!e()}catch(t){return!0}}},27007(e,t,n){"use strict";n(74916);var r=n(31320),i=n(22261),a=n(47293),o=n(5112),s=n(68880),u=o("species"),c=RegExp.prototype;e.exports=function(e,t,n,l){var f=o(e),d=!a(function(){var t={};return t[f]=function(){return 7},7!=""[e](t)}),h=d&&!a(function(){var t=!1,n=/a/;return"split"===e&&((n={}).constructor={},n.constructor[u]=function(){return n},n.flags="",n[f]=/./[f]),n.exec=function(){return t=!0,null},n[f](""),!t});if(!d||!h||n){var p=/./[f],b=t(f,""[e],function(e,t,n,r,a){var o=t.exec;return o===i||o===c.exec?d&&!a?{done:!0,value:p.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}});r(String.prototype,e,b[0]),r(c,f,b[1])}l&&s(c[f],"sham",!0)}},6790(e,t,n){"use strict";var r=n(43157),i=n(17466),a=n(49974),o=function(e,t,n,s,u,c,l,f){for(var d,h=u,p=0,b=!!l&&a(l,f,3);p0&&r(d))h=o(e,t,d,i(d.length),h,c-1)-1;else{if(h>=9007199254740991)throw TypeError("Exceed the acceptable array length");e[h]=d}h++}p++}return h};e.exports=o},76677(e,t,n){var r=n(47293);e.exports=!r(function(){return Object.isExtensible(Object.preventExtensions({}))})},49974(e,t,n){var r=n(13099);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},27065(e,t,n){"use strict";var r=n(13099),i=n(70111),a=[].slice,o={},s=function(e,t,n){if(!(t in o)){for(var r=[],i=0;i]*>)/g,s=/\$([$&'`]|\d{1,2})/g;e.exports=function(e,t,n,u,c,l){var f=n+e.length,d=u.length,h=s;return void 0!==c&&(c=r(c),h=o),a.call(l,h,function(r,a){var o;switch(a.charAt(0)){case"$":return"$";case"&":return e;case"`":return t.slice(0,n);case"'":return t.slice(f);case"<":o=c[a.slice(1,-1)];break;default:var s=+a;if(0===s)return r;if(s>d){var l=i(s/10);if(0===l)return r;if(l<=d)return void 0===u[l-1]?a.charAt(1):u[l-1]+a.charAt(1);return r}o=u[s-1]}return void 0===o?"":o})}},17854(e,t,n){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},86656(e,t,n){var r=n(47908),i={}.hasOwnProperty;e.exports=Object.hasOwn||function(e,t){return i.call(r(e),t)}},3501(e){e.exports={}},842(e,t,n){var r=n(17854);e.exports=function(e,t){var n=r.console;n&&n.error&&(1===arguments.length?n.error(e):n.error(e,t))}},60490(e,t,n){var r=n(35005);e.exports=r("document","documentElement")},64664(e,t,n){var r=n(19781),i=n(47293),a=n(80317);e.exports=!r&&!i(function(){return 7!=Object.defineProperty(a("div"),"a",{get:function(){return 7}}).a})},11179(e){var t=Math.abs,n=Math.pow,r=Math.floor,i=Math.log,a=Math.LN2,o=function(e,o,s){var u,c,l,f=Array(s),d=8*s-o-1,h=(1<>1,b=23===o?n(2,-24)-n(2,-77):0,m=e<0||0===e&&1/e<0?1:0,g=0;for((e=t(e))!=e||e===1/0?(c=e!=e?1:0,u=h):(u=r(i(e)/a),e*(l=n(2,-u))<1&&(u--,l*=2),u+p>=1?e+=b/l:e+=b*n(2,1-p),e*l>=2&&(u++,l/=2),u+p>=h?(c=0,u=h):u+p>=1?(c=(e*l-1)*n(2,o),u+=p):(c=e*n(2,p-1)*n(2,o),u=0));o>=8;f[g++]=255&c,c/=256,o-=8);for(u=u<0;f[g++]=255&u,u/=256,d-=8);return f[--g]|=128*m,f},s=function(e,t){var r,i=e.length,a=8*i-t-1,o=(1<>1,u=a-7,c=i-1,l=e[c--],f=127&l;for(l>>=7;u>0;f=256*f+e[c],c--,u-=8);for(r=f&(1<<-u)-1,f>>=-u,u+=t;u>0;r=256*r+e[c],c--,u-=8);if(0===f)f=1-s;else{if(f===o)return r?NaN:l?-1/0:1/0;r+=n(2,t),f-=s}return(l?-1:1)*r*n(2,f-t)};e.exports={pack:o,unpack:s}},68361(e,t,n){var r=n(47293),i=n(84326),a="".split;e.exports=r(function(){return!Object("z").propertyIsEnumerable(0)})?function(e){return"String"==i(e)?a.call(e,""):Object(e)}:Object},79587(e,t,n){var r=n(70111),i=n(27674);e.exports=function(e,t,n){var a,o;return i&&"function"==typeof(a=t.constructor)&&a!==n&&r(o=a.prototype)&&o!==n.prototype&&i(e,o),e}},42788(e,t,n){var r=n(5465),i=Function.toString;"function"!=typeof r.inspectSource&&(r.inspectSource=function(e){return i.call(e)}),e.exports=r.inspectSource},62423(e,t,n){var r=n(82109),i=n(3501),a=n(70111),o=n(86656),s=n(3070).f,u=n(8006),c=n(1156),l=n(69711),f=n(76677),d=!1,h=l("meta"),p=0,b=Object.isExtensible||function(){return!0},m=function(e){s(e,h,{value:{objectID:"O"+p++,weakData:{}}})},g=function(e,t){if(!a(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!o(e,h)){if(!b(e))return"F";if(!t)return"E";m(e)}return e[h].objectID},v=function(e,t){if(!o(e,h)){if(!b(e))return!0;if(!t)return!1;m(e)}return e[h].weakData},y=function(e){return f&&d&&b(e)&&!o(e,h)&&m(e),e},w=function(){_.enable=function(){},d=!0;var e=u.f,t=[].splice,n={};n[h]=1,e(n).length&&(u.f=function(n){for(var r=e(n),i=0,a=r.length;ih;h++)if((b=k(e[h]))&&b instanceof l)return b;return new l(!1)}f=s(e,d)}for(m=f.next;!(g=m.call(f)).done;){try{b=k(g.value)}catch(x){c(f,"throw",x)}if("object"==typeof b&&b&&b instanceof l)return b}return new l(!1)}},99212(e,t,n){var r=n(19670);e.exports=function(e,t,n){var i,a;r(e);try{if(void 0===(i=e.return)){if("throw"===t)throw n;return n}i=i.call(e)}catch(o){a=!0,i=o}if("throw"===t)throw n;if(a)throw i;return r(i),n}},54956(e,t,n){"use strict";var r=n(40857),i=n(13099),a=n(19670),o=n(70030),s=n(68880),u=n(12248),c=n(5112),l=n(29909),f=l.set,d=l.get,h=c("toStringTag");e.exports=function(e,t){var n=function(e){e.next=i(e.iterator.next),e.done=!1,e.ignoreArg=!t,f(this,e)};return n.prototype=u(o(r.Iterator.prototype),{next:function(n){var r=d(this),i=arguments.length?[r.ignoreArg?void 0:n]:t?[]:[void 0];r.ignoreArg=!1;var a=r.done?void 0:e.call(r,i);return{done:r.done,value:a}},return:function(e){var t=d(this).iterator;t.done=!0;var n=t.return;return{done:!0,value:void 0===n?e:a(n.call(t,e)).value}},throw:function(e){var t=d(this).iterator;t.done=!0;var n=t.throw;if(void 0===n)throw e;return n.call(t,e)}}),t||s(n.prototype,h,"Generator"),n}},13383(e,t,n){"use strict";var r,i,a,o=n(47293),s=n(79518),u=n(68880),c=n(86656),l=n(5112),f=n(31913),d=l("iterator"),h=!1,p=function(){return this};[].keys&&("next"in(a=[].keys())?(i=s(s(a)))!==Object.prototype&&(r=i):h=!0);var b=void 0==r||o(function(){var e={};return r[d].call(e)!==e});b&&(r={}),f&&!b||c(r,d)||u(r,d,p),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:h}},97497(e){e.exports={}},37502(e,t,n){"use strict";var r=n(19670);e.exports=function(e,t){var n=r(this),i=n.has(e)&&"update"in t?t.update(n.get(e),e,n):t.insert(e,n);return n.set(e,i),i}},8154(e,t,n){"use strict";var r=n(19670);e.exports=function(e,t){var n,i=r(this),a=arguments.length>2?arguments[2]:void 0;if("function"!=typeof t&&"function"!=typeof a)throw TypeError("At least one callback required");return i.has(e)?(n=i.get(e),"function"==typeof t&&(n=t(n),i.set(e,n))):"function"==typeof a&&(n=a(),i.set(e,n)),n}},66736(e){var t=Math.expm1,n=Math.exp;e.exports=!t||t(10)>22025.465794806718||22025.465794806718>t(10)||-.00000000000000002!=t(-.00000000000000002)?function(e){return 0==(e=+e)?e:e>-.000001&&e<1e-6?e+e*e/2:n(e)-1}:t},26130(e,t,n){var r=n(64310),i=Math.abs,a=Math.pow,o=a(2,-52),s=a(2,-23),u=a(2,127)*(2-s),c=a(2,-126),l=function(e){return e+1/o-1/o};e.exports=Math.fround||function(e){var t,n,a=i(e),f=r(e);return au||n!=n?f*(1/0):f*n}},26513(e){var t=Math.log;e.exports=Math.log1p||function(e){return(e=+e)>-.00000001&&e<1e-8?e-e*e/2:t(1+e)}},47103(e){e.exports=Math.scale||function(e,t,n,r,i){return 0===arguments.length||e!=e||t!=t||n!=n||r!=r||i!=i?NaN:e===1/0||e===-1/0?e:(e-t)*(i-r)/(n-t)+r}},64310(e){e.exports=Math.sign||function(e){return 0==(e=+e)||e!=e?e:e<0?-1:1}},95948(e,t,n){var r,i,a,o,s,u,c,l,f=n(17854),d=n(31236).f,h=n(20261).set,p=n(6833),b=n(71528),m=n(71036),g=n(35268),v=f.MutationObserver||f.WebKitMutationObserver,y=f.document,w=f.process,_=f.Promise,E=d(f,"queueMicrotask"),S=E&&E.value;S||(r=function(){var e,t;for(g&&(e=w.domain)&&e.exit();i;){t=i.fn,i=i.next;try{t()}catch(n){throw i?o():a=void 0,n}}a=void 0,e&&e.enter()},p||g||m||!v||!y?!b&&_&&_.resolve?((c=_.resolve(void 0)).constructor=_,l=c.then,o=function(){l.call(c,r)}):o=g?function(){w.nextTick(r)}:function(){h.call(f,r)}:(s=!0,u=y.createTextNode(""),new v(r).observe(u,{characterData:!0}),o=function(){u.data=s=!s})),e.exports=S||function(e){var t={fn:e,next:void 0};a&&(a.next=t),i||(i=t,o()),a=t}},13366(e,t,n){var r=n(17854);e.exports=r.Promise},30133(e,t,n){var r=n(7392),i=n(47293);e.exports=!!Object.getOwnPropertySymbols&&!i(function(){var e=Symbol();return!String(e)||!(Object(e) instanceof Symbol)||!Symbol.sham&&r&&r<41})},590(e,t,n){var r=n(47293),i=n(5112),a=n(31913),o=i("iterator");e.exports=!r(function(){var e=new URL("b?a=1&b=2&c=3","http://a"),t=e.searchParams,n="";return e.pathname="c%20d",t.forEach(function(e,r){t.delete("b"),n+=r+e}),a&&!e.toJSON||!t.sort||"http://a/c%20d?a=1&c=3"!==e.href||"3"!==t.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!t[o]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==n||"x"!==new URL("http://x",void 0).host})},68536(e,t,n){var r=n(17854),i=n(42788),a=r.WeakMap;e.exports="function"==typeof a&&/native code/.test(i(a))},78523(e,t,n){"use strict";var r=n(13099),i=function(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=r}),this.resolve=r(t),this.reject=r(n)};e.exports.f=function(e){return new i(e)}},3929(e,t,n){var r=n(47850);e.exports=function(e){if(r(e))throw TypeError("The method doesn't accept regular expressions");return e}},77023(e,t,n){var r=n(17854).isFinite;e.exports=Number.isFinite||function(e){return"number"==typeof e&&r(e)}},2814(e,t,n){var r=n(17854),i=n(41340),a=n(53111).trim,o=n(81361),s=r.parseFloat,u=1/s(o+"-0")!=-1/0;e.exports=u?function(e){var t=a(i(e)),n=s(t);return 0===n&&"-"==t.charAt(0)?-0:n}:s},83009(e,t,n){var r=n(17854),i=n(41340),a=n(53111).trim,o=n(81361),s=r.parseInt,u=/^[+-]?0[Xx]/,c=8!==s(o+"08")||22!==s(o+"0x16");e.exports=c?function(e,t){var n=a(i(e));return s(n,t>>>0||(u.test(n)?16:10))}:s},80430(e,t,n){"use strict";var r=n(29909),i=n(24994),a=n(70111),o=n(36048),s=n(19781),u="Incorrect Number.range arguments",c="NumericRangeIterator",l=r.set,f=r.getterFor(c),d=i(function(e,t,n,r,i,o){if(typeof e!=r||t!==1/0&&t!==-1/0&&typeof t!=r)throw TypeError(u);if(e===1/0||e===-1/0)throw RangeError(u);var f,d=t>e,h=!1;if(void 0===n)f=void 0;else if(a(n))f=n.step,h=!!n.inclusive;else if(typeof n==r)f=n;else throw TypeError(u);if(null==f&&(f=d?o:-o),typeof f!=r)throw TypeError(u);if(f===1/0||f===-1/0||f===i&&e!==t)throw RangeError(u);var p=e!=e||t!=t||f!=f||t>e!=f>i;l(this,{type:c,start:e,end:t,step:f,inclusiveEnd:h,hitsEnd:p,currentCount:i,zero:i}),s||(this.start=e,this.end=t,this.step=f,this.inclusive=h)},c,function(){var e,t=f(this);if(t.hitsEnd)return{value:void 0,done:!0};var n=t.start,r=t.end,i=n+t.step*t.currentCount++;i===r&&(t.hitsEnd=!0);var a=t.inclusiveEnd;return(e=r>n?a?i>r:i>=r:a?r>i:r>=i)?{value:void 0,done:t.hitsEnd=!0}:{value:i,done:!1}}),h=function(e){return{get:e,set:function(){},configurable:!0,enumerable:!1}};s&&o(d.prototype,{start:h(function(){return f(this).start}),end:h(function(){return f(this).end}),inclusive:h(function(){return f(this).inclusiveEnd}),step:h(function(){return f(this).step})}),e.exports=d},21574(e,t,n){"use strict";var r=n(19781),i=n(47293),a=n(81956),o=n(25181),s=n(55296),u=n(47908),c=n(68361),l=Object.assign,f=Object.defineProperty;e.exports=!l||i(function(){if(r&&1!==l({b:1},l(f({},"a",{enumerable:!0,get:function(){f(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var e={},t={},n=Symbol(),i="abcdefghijklmnopqrst";return e[n]=7,i.split("").forEach(function(e){t[e]=e}),7!=l({},e)[n]||a(l({},t)).join("")!=i})?function(e,t){for(var n=u(e),i=arguments.length,l=1,f=o.f,d=s.f;i>l;)for(var h,p=c(arguments[l++]),b=f?a(p).concat(f(p)):a(p),m=b.length,g=0;m>g;)h=b[g++],(!r||d.call(p,h))&&(n[h]=p[h]);return n}:l},70030(e,t,n){var r,i=n(19670),a=n(36048),o=n(80748),s=n(3501),u=n(60490),c=n(80317),l=n(6200),f=">",d="<",h="prototype",p="script",b=l("IE_PROTO"),m=function(){},g=function(e){return d+p+f+e+d+"/"+p+f},v=function(e){e.write(g("")),e.close();var t=e.parentWindow.Object;return e=null,t},y=function(){var e,t=c("iframe"),n="java"+p+":";return t.style.display="none",u.appendChild(t),t.src=String(n),(e=t.contentWindow.document).open(),e.write(g("document.F=Object")),e.close(),e.F},w=function(){try{r=new ActiveXObject("htmlfile")}catch(e){}w="undefined"!=typeof document?document.domain&&r?v(r):y():v(r);for(var t=o.length;t--;)delete w[h][o[t]];return w()};s[b]=!0,e.exports=Object.create||function(e,t){var n;return null!==e?(m[h]=i(e),n=new m,m[h]=null,n[b]=e):n=w(),void 0===t?n:a(n,t)}},36048(e,t,n){var r=n(19781),i=n(3070),a=n(19670),o=n(81956);e.exports=r?Object.defineProperties:function(e,t){a(e);for(var n,r=o(t),s=r.length,u=0;s>u;)i.f(e,n=r[u++],t[n]);return e}},3070(e,t,n){var r=n(19781),i=n(64664),a=n(19670),o=n(34948),s=Object.defineProperty;t.f=r?s:function(e,t,n){if(a(e),t=o(t),a(n),i)try{return s(e,t,n)}catch(r){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(e[t]=n.value),e}},31236(e,t,n){var r=n(19781),i=n(55296),a=n(79114),o=n(45656),s=n(34948),u=n(86656),c=n(64664),l=Object.getOwnPropertyDescriptor;t.f=r?l:function(e,t){if(e=o(e),t=s(t),c)try{return l(e,t)}catch(n){}if(u(e,t))return a(!i.f.call(e,t),e[t])}},1156(e,t,n){var r=n(45656),i=n(8006).f,a={}.toString,o="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(e){try{return i(e)}catch(t){return o.slice()}};e.exports.f=function(e){return o&&"[object Window]"==a.call(e)?s(e):i(r(e))}},8006(e,t,n){var r=n(16324),i=n(80748).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return r(e,i)}},25181(e,t){t.f=Object.getOwnPropertySymbols},79518(e,t,n){var r=n(86656),i=n(47908),a=n(6200),o=n(49920),s=a("IE_PROTO"),u=Object.prototype;e.exports=o?Object.getPrototypeOf:function(e){return(e=i(e),r(e,s))?e[s]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?u:null}},60996(e,t,n){"use strict";var r=n(29909),i=n(24994),a=n(86656),o=n(81956),s=n(47908),u="Object Iterator",c=r.set,l=r.getterFor(u);e.exports=i(function(e,t){var n=s(e);c(this,{type:u,mode:t,object:n,keys:o(n),index:0})},"Object",function(){for(var e=l(this),t=e.keys;;){if(null===t||e.index>=t.length)return e.object=e.keys=null,{value:void 0,done:!0};var n=t[e.index++],r=e.object;if(a(r,n)){switch(e.mode){case"keys":return{value:n,done:!1};case"values":return{value:r[n],done:!1}}return{value:[n,r[n]],done:!1}}}})},16324(e,t,n){var r=n(86656),i=n(45656),a=n(41318).indexOf,o=n(3501);e.exports=function(e,t){var n,s=i(e),u=0,c=[];for(n in s)!r(o,n)&&r(s,n)&&c.push(n);for(;t.length>u;)r(s,n=t[u++])&&(~a(c,n)||c.push(n));return c}},81956(e,t,n){var r=n(16324),i=n(80748);e.exports=Object.keys||function(e){return r(e,i)}},55296(e,t){"use strict";var n={}.propertyIsEnumerable,r=Object.getOwnPropertyDescriptor,i=r&&!n.call({1:2},1);t.f=i?function(e){var t=r(this,e);return!!t&&t.enumerable}:n},69026(e,t,n){"use strict";var r=n(31913),i=n(17854),a=n(47293),o=n(98008);e.exports=r||!a(function(){if(!o||!(o<535)){var e=Math.random();__defineSetter__.call(null,e,function(){}),delete i[e]}})},27674(e,t,n){var r=n(19670),i=n(96077);e.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var e,t=!1,n={};try{(e=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set).call(n,[]),t=n instanceof Array}catch(a){}return function(n,a){return r(n),i(a),t?e.call(n,a):n.__proto__=a,n}}():void 0)},44699(e,t,n){var r=n(19781),i=n(81956),a=n(45656),o=n(55296).f,s=function(e){return function(t){for(var n,s=a(t),u=i(s),c=u.length,l=0,f=[];c>l;)n=u[l++],(!r||o.call(s,n))&&f.push(e?[n,s[n]]:s[n]);return f}};e.exports={entries:s(!0),values:s(!1)}},90288(e,t,n){"use strict";var r=n(51694),i=n(70648);e.exports=r?({}).toString:function(){return"[object "+i(this)+"]"}},92140(e,t,n){var r=n(70111);e.exports=function(e,t){var n,i;if("string"===t&&"function"==typeof(n=e.toString)&&!r(i=n.call(e))||"function"==typeof(n=e.valueOf)&&!r(i=n.call(e))||"string"!==t&&"function"==typeof(n=e.toString)&&!r(i=n.call(e)))return i;throw TypeError("Can't convert object to primitive value")}},53887(e,t,n){var r=n(35005),i=n(8006),a=n(25181),o=n(19670);e.exports=r("Reflect","ownKeys")||function(e){var t=i.f(o(e)),n=a.f;return n?t.concat(n(e)):t}},40857(e,t,n){var r=n(17854);e.exports=r},12534(e){e.exports=function(e){try{return{error:!1,value:e()}}catch(t){return{error:!0,value:t}}}},69478(e,t,n){var r=n(19670),i=n(70111),a=n(78523);e.exports=function(e,t){if(r(e),i(t)&&t.constructor===e)return t;var n=a.f(e);return(0,n.resolve)(t),n.promise}},12248(e,t,n){var r=n(31320);e.exports=function(e,t,n){for(var i in t)r(e,i,t[i],n);return e}},31320(e,t,n){var r=n(17854),i=n(68880),a=n(86656),o=n(83505),s=n(42788),u=n(29909),c=u.get,l=u.enforce,f=String(String).split("String");(e.exports=function(e,t,n,s){var u,c=!!s&&!!s.unsafe,d=!!s&&!!s.enumerable,h=!!s&&!!s.noTargetGet;if("function"!=typeof n||("string"!=typeof t||a(n,"name")||i(n,"name",t),(u=l(n)).source||(u.source=f.join("string"==typeof t?t:""))),e===r){d?e[t]=n:o(t,n);return}c?!h&&e[t]&&(d=!0):delete e[t],d?e[t]=n:i(e,t,n)})(Function.prototype,"toString",function(){return"function"==typeof this&&c(this).source||s(this)})},38845(e,t,n){var r=n(51532),i=n(4129),a=n(72309)("metadata"),o=a.store||(a.store=new i),s=function(e,t,n){var i=o.get(e);if(!i){if(!n)return;o.set(e,i=new r)}var a=i.get(t);if(!a){if(!n)return;i.set(t,a=new r)}return a},u=function(e,t,n){var r=s(t,n,!1);return void 0!==r&&r.has(e)},c=function(e,t,n){var r=s(t,n,!1);return void 0===r?void 0:r.get(e)},l=function(e,t,n,r){s(n,r,!0).set(e,t)},f=function(e,t){var n=s(e,t,!1),r=[];return n&&n.forEach(function(e,t){r.push(t)}),r},d=function(e){return void 0===e||"symbol"==typeof e?e:String(e)};e.exports={store:o,getMap:s,has:u,get:c,set:l,keys:f,toKey:d}},97651(e,t,n){var r=n(84326),i=n(22261);e.exports=function(e,t){var n=e.exec;if("function"==typeof n){var a=n.call(e,t);if("object"!=typeof a)throw TypeError("RegExp exec method returned something other than an Object or null");return a}if("RegExp"!==r(e))throw TypeError("RegExp#exec called on incompatible receiver");return i.call(e,t)}},22261(e,t,n){"use strict";var r,i,a=n(41340),o=n(67066),s=n(52999),u=n(72309),c=n(70030),l=n(29909).get,f=n(9441),d=n(38173),h=RegExp.prototype.exec,p=u("native-string-replace",String.prototype.replace),b=h,m=(r=/a/,i=/b*/g,h.call(r,"a"),h.call(i,"a"),0!==r.lastIndex||0!==i.lastIndex),g=s.UNSUPPORTED_Y||s.BROKEN_CARET,v=void 0!==/()??/.exec("")[1];(m||v||g||f||d)&&(b=function(e){var t,n,r,i,s,u,f,d=this,y=l(d),w=a(e),_=y.raw;if(_)return _.lastIndex=d.lastIndex,t=b.call(_,w),d.lastIndex=_.lastIndex,t;var E=y.groups,S=g&&d.sticky,k=o.call(d),x=d.source,T=0,M=w;if(S&&(-1===(k=k.replace("y","")).indexOf("g")&&(k+="g"),M=w.slice(d.lastIndex),d.lastIndex>0&&(!d.multiline||d.multiline&&"\n"!==w.charAt(d.lastIndex-1))&&(x="(?: "+x+")",M=" "+M,T++),n=RegExp("^(?:"+x+")",k)),v&&(n=RegExp("^"+x+"$(?!\\s)",k)),m&&(r=d.lastIndex),i=h.call(S?n:d,M),S?i?(i.input=i.input.slice(T),i[0]=i[0].slice(T),i.index=d.lastIndex,d.lastIndex+=i[0].length):d.lastIndex=0:m&&i&&(d.lastIndex=d.global?i.index+i[0].length:r),v&&i&&i.length>1&&p.call(i[0],n,function(){for(s=1;sb)","g");return"b"!==e.exec("b").groups.a||"bc"!=="b".replace(e,"$c")})},84488(e){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},46465(e){e.exports=function(e,t){return e===t||e!=e&&t!=t}},81150(e){e.exports=Object.is||function(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}},83505(e,t,n){var r=n(17854);e.exports=function(e,t){try{Object.defineProperty(r,e,{value:t,configurable:!0,writable:!0})}catch(n){r[e]=t}return t}},96340(e,t,n){"use strict";var r=n(35005),i=n(3070),a=n(5112),o=n(19781),s=a("species");e.exports=function(e){var t=r(e),n=i.f;o&&t&&!t[s]&&n(t,s,{configurable:!0,get:function(){return this}})}},58003(e,t,n){var r=n(3070).f,i=n(86656),a=n(5112)("toStringTag");e.exports=function(e,t,n){e&&!i(e=n?e:e.prototype,a)&&r(e,a,{configurable:!0,value:t})}},6200(e,t,n){var r=n(72309),i=n(69711),a=r("keys");e.exports=function(e){return a[e]||(a[e]=i(e))}},5465(e,t,n){var r=n(17854),i=n(83505),a="__core-js_shared__",o=r[a]||i(a,{});e.exports=o},72309(e,t,n){var r=n(31913),i=n(5465);(e.exports=function(e,t){return i[e]||(i[e]=void 0!==t?t:{})})("versions",[]).push({version:"3.17.0",mode:r?"pure":"global",copyright:"\xa9 2021 Denis Pushkarev (zloirock.ru)"})},36707(e,t,n){var r=n(19670),i=n(13099),a=n(5112)("species");e.exports=function(e,t){var n,o=r(e).constructor;return void 0===o||void 0==(n=r(o)[a])?t:i(n)}},43429(e,t,n){var r=n(47293);e.exports=function(e){return r(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3})}},28710(e,t,n){var r=n(99958),i=n(41340),a=n(84488),o=function(e){return function(t,n){var o,s,u=i(a(t)),c=r(n),l=u.length;return c<0||c>=l?e?"":void 0:(o=u.charCodeAt(c))<55296||o>56319||c+1===l||(s=u.charCodeAt(c+1))<56320||s>57343?e?u.charAt(c):o:e?u.slice(c,c+2):(o-55296<<10)+(s-56320)+65536}};e.exports={codeAt:o(!1),charAt:o(!0)}},54986(e,t,n){var r=n(88113);e.exports=/Version\/10(?:\.\d+){1,2}(?: [\w./]+)?(?: Mobile\/\w+)? Safari\//.test(r)},76650(e,t,n){var r=n(17466),i=n(41340),a=n(38415),o=n(84488),s=Math.ceil,u=function(e){return function(t,n,u){var c,l,f=i(o(t)),d=f.length,h=void 0===u?" ":i(u),p=r(n);return p<=d||""==h?f:(c=p-d,(l=a.call(h,s(c/h.length))).length>c&&(l=l.slice(0,c)),e?f+l:l+f)}};e.exports={start:u(!1),end:u(!0)}},33197(e){"use strict";var t=2147483647,n=36,r=1,i=26,a=38,o=700,s=72,u=128,c="-",l=/[^\0-\u007E]/,f=/[.\u3002\uFF0E\uFF61]/g,d="Overflow: input needs wider integers to process",h=n-r,p=Math.floor,b=String.fromCharCode,m=function(e){for(var t=[],n=0,r=e.length;n=55296&&i<=56319&&n>1,e+=p(e/t);e>h*i>>1;s+=n)e=p(e/h);return p(s+(h+1)*e/(e+a))},y=function(e){var a,o,l=[],f=(e=m(e)).length,h=u,y=0,w=s;for(a=0;a=h&&op((t-y)/k))throw RangeError(d);for(y+=(S-h)*k,h=S,a=0;at)throw RangeError(d);if(o==h){for(var x=y,T=n;;T+=n){var M=T<=w?r:T>=w+i?i:T-w;if(x0;(o>>>=1)&&(t+=t))1&o&&(n+=t);return n}},76091(e,t,n){var r=n(47293),i=n(81361),a="​\x85᠎";e.exports=function(e){return r(function(){return!!i[e]()||a[e]()!=a||i[e].name!==e})}},53111(e,t,n){var r=n(84488),i=n(41340),a="["+n(81361)+"]",o=RegExp("^"+a+a+"*"),s=RegExp(a+a+"*$"),u=function(e){return function(t){var n=i(r(t));return 1&e&&(n=n.replace(o,"")),2&e&&(n=n.replace(s,"")),n}};e.exports={start:u(1),end:u(2),trim:u(3)}},20261(e,t,n){var r,i,a,o,s=n(17854),u=n(47293),c=n(49974),l=n(60490),f=n(80317),d=n(6833),h=n(35268),p=s.setImmediate,b=s.clearImmediate,m=s.process,g=s.MessageChannel,v=s.Dispatch,y=0,w={},_="onreadystatechange";try{r=s.location}catch(E){}var S=function(e){if(w.hasOwnProperty(e)){var t=w[e];delete w[e],t()}},k=function(e){return function(){S(e)}},x=function(e){S(e.data)},T=function(e){s.postMessage(String(e),r.protocol+"//"+r.host)};p&&b||(p=function(e){for(var t=[],n=arguments.length,r=1;n>r;)t.push(arguments[r++]);return w[++y]=function(){("function"==typeof e?e:Function(e)).apply(void 0,t)},i(y),y},b=function(e){delete w[e]},h?i=function(e){m.nextTick(k(e))}:v&&v.now?i=function(e){v.now(k(e))}:g&&!d?(o=(a=new g).port2,a.port1.onmessage=x,i=c(o.postMessage,o,1)):s.addEventListener&&"function"==typeof postMessage&&!s.importScripts&&r&&"file:"!==r.protocol&&!u(T)?(i=T,s.addEventListener("message",x,!1)):i=_ in f("script")?function(e){l.appendChild(f("script"))[_]=function(){l.removeChild(this),S(e)}}:function(e){setTimeout(k(e),0)}),e.exports={set:p,clear:b}},50863(e,t,n){var r=n(84326);e.exports=function(e){if("number"!=typeof e&&"Number"!=r(e))throw TypeError("Incorrect invocation");return+e}},51400(e,t,n){var r=n(99958),i=Math.max,a=Math.min;e.exports=function(e,t){var n=r(e);return n<0?i(n+t,0):a(n,t)}},57067(e,t,n){var r=n(99958),i=n(17466);e.exports=function(e){if(void 0===e)return 0;var t=r(e),n=i(t);if(t!==n)throw RangeError("Wrong length or index");return n}},45656(e,t,n){var r=n(68361),i=n(84488);e.exports=function(e){return r(i(e))}},99958(e){var t=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:t)(e)}},17466(e,t,n){var r=n(99958),i=Math.min;e.exports=function(e){return e>0?i(r(e),9007199254740991):0}},47908(e,t,n){var r=n(84488);e.exports=function(e){return Object(r(e))}},84590(e,t,n){var r=n(73002);e.exports=function(e,t){var n=r(e);if(n%t)throw RangeError("Wrong offset");return n}},73002(e,t,n){var r=n(99958);e.exports=function(e){var t=r(e);if(t<0)throw RangeError("The argument can't be less than 0");return t}},57593(e,t,n){var r=n(70111),i=n(52190),a=n(92140),o=n(5112)("toPrimitive");e.exports=function(e,t){if(!r(e)||i(e))return e;var n,s=e[o];if(void 0!==s){if(void 0===t&&(t="default"),!r(n=s.call(e,t))||i(n))return n;throw TypeError("Can't convert object to primitive value")}return void 0===t&&(t="number"),a(e,t)}},34948(e,t,n){var r=n(57593),i=n(52190);e.exports=function(e){var t=r(e,"string");return i(t)?t:String(t)}},51694(e,t,n){var r=n(5112)("toStringTag"),i={};i[r]="z",e.exports="[object z]"===String(i)},41340(e,t,n){var r=n(52190);e.exports=function(e){if(r(e))throw TypeError("Cannot convert a Symbol value to a string");return String(e)}},19843(e,t,n){"use strict";var r=n(82109),i=n(17854),a=n(19781),o=n(63832),s=n(90260),u=n(13331),c=n(25787),l=n(79114),f=n(68880),d=n(18730),h=n(17466),p=n(57067),b=n(84590),m=n(34948),g=n(86656),v=n(70648),y=n(70111),w=n(52190),_=n(70030),E=n(27674),S=n(8006).f,k=n(97321),x=n(42092).forEach,T=n(96340),M=n(3070),O=n(31236),A=n(29909),L=n(79587),C=A.get,I=A.set,D=M.f,N=O.f,P=Math.round,R=i.RangeError,j=u.ArrayBuffer,F=u.DataView,Y=s.NATIVE_ARRAY_BUFFER_VIEWS,B=s.TYPED_ARRAY_CONSTRUCTOR,U=s.TYPED_ARRAY_TAG,H=s.TypedArray,$=s.TypedArrayPrototype,z=s.aTypedArrayConstructor,G=s.isTypedArray,W="BYTES_PER_ELEMENT",K="Wrong length",V=function(e,t){for(var n=0,r=t.length,i=new(z(e))(r);r>n;)i[n]=t[n++];return i},q=function(e,t){D(e,t,{get:function(){return C(this)[t]}})},Z=function(e){var t;return e instanceof j||"ArrayBuffer"==(t=v(e))||"SharedArrayBuffer"==t},X=function(e,t){return G(e)&&!w(t)&&t in e&&d(+t)&&t>=0},J=function(e,t){return t=m(t),X(e,t)?l(2,e[t]):N(e,t)},Q=function(e,t,n){return(t=m(t),X(e,t)&&y(n)&&g(n,"value")&&!g(n,"get")&&!g(n,"set")&&!n.configurable&&(!g(n,"writable")||n.writable)&&(!g(n,"enumerable")||n.enumerable))?(e[t]=n.value,e):D(e,t,n)};a?(Y||(O.f=J,M.f=Q,q($,"buffer"),q($,"byteOffset"),q($,"byteLength"),q($,"length")),r({target:"Object",stat:!0,forced:!Y},{getOwnPropertyDescriptor:J,defineProperty:Q}),e.exports=function(e,t,n){var a=e.match(/\d+$/)[0]/8,s=e+(n?"Clamped":"")+"Array",u="get"+e,l="set"+e,d=i[s],m=d,g=m&&m.prototype,v={},w=function(e,t){var n=C(e);return n.view[u](t*a+n.byteOffset,!0)},M=function(e,t,r){var i=C(e);n&&(r=(r=P(r))<0?0:r>255?255:255&r),i.view[l](t*a+i.byteOffset,r,!0)},O=function(e,t){D(e,t,{get:function(){return w(this,t)},set:function(e){return M(this,t,e)},enumerable:!0})};Y?o&&(m=t(function(e,t,n,r){return c(e,m,s),L(y(t)?Z(t)?void 0!==r?new d(t,b(n,a),r):void 0!==n?new d(t,b(n,a)):new d(t):G(t)?V(m,t):k.call(m,t):new d(p(t)),e,m)}),E&&E(m,H),x(S(d),function(e){e in m||f(m,e,d[e])}),m.prototype=g):(m=t(function(e,t,n,r){c(e,m,s);var i,o,u,l=0,f=0;if(y(t)){if(Z(t)){i=t,f=b(n,a);var d=t.byteLength;if(void 0===r){if(d%a||(o=d-f)<0)throw R(K)}else if((o=h(r)*a)+f>d)throw R(K);u=o/a}else if(G(t))return V(m,t);else return k.call(m,t)}else o=(u=p(t))*a,i=new j(o);for(I(e,{buffer:i,byteOffset:f,byteLength:o,length:u,view:new F(i)});l1?arguments[1]:void 0,g=void 0!==m,v=o(p);if(void 0!=v&&!s(v))for(h=(d=a(p,v)).next,p=[];!(f=h.call(d)).done;)p.push(f.value);for(g&&b>2&&(m=u(m,arguments[2],2)),n=i(p.length),l=new(c(this))(n),t=0;n>t;t++)l[t]=g?m(p[t],t):p[t];return l}},66304(e,t,n){var r=n(90260),i=n(36707),a=r.TYPED_ARRAY_CONSTRUCTOR,o=r.aTypedArrayConstructor;e.exports=function(e){return o(i(e,e[a]))}},69711(e){var t=0,n=Math.random();e.exports=function(e){return"Symbol("+String(void 0===e?"":e)+")_"+(++t+n).toString(36)}},43307(e,t,n){var r=n(30133);e.exports=r&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},6061(e,t,n){var r=n(5112);t.f=r},5112(e,t,n){var r=n(17854),i=n(72309),a=n(86656),o=n(69711),s=n(30133),u=n(43307),c=i("wks"),l=r.Symbol,f=u?l:l&&l.withoutSetter||o;e.exports=function(e){return a(c,e)&&(s||"string"==typeof c[e])||(s&&a(l,e)?c[e]=l[e]:c[e]=f("Symbol."+e)),c[e]}},81361(e){e.exports=" \n\v\f\r \xa0               \u2028\u2029\uFEFF"},9170(e,t,n){"use strict";var r=n(82109),i=n(79518),a=n(27674),o=n(70030),s=n(68880),u=n(79114),c=n(20408),l=n(41340),f=function(e,t){var n=this;if(!(n instanceof f))return new f(e,t);a&&(n=a(Error(void 0),i(n))),void 0!==t&&s(n,"message",l(t));var r=[];return c(e,r.push,{that:r}),s(n,"errors",r),n};f.prototype=o(Error.prototype,{constructor:u(5,f),message:u(5,""),name:u(5,"AggregateError")}),r({global:!0},{AggregateError:f})},18264(e,t,n){"use strict";var r=n(82109),i=n(17854),a=n(13331),o=n(96340),s="ArrayBuffer",u=a[s];r({global:!0,forced:i[s]!==u},{ArrayBuffer:u}),o(s)},76938(e,t,n){var r=n(82109),i=n(90260);r({target:"ArrayBuffer",stat:!0,forced:!i.NATIVE_ARRAY_BUFFER_VIEWS},{isView:i.isView})},39575(e,t,n){"use strict";var r=n(82109),i=n(47293),a=n(13331),o=n(19670),s=n(51400),u=n(17466),c=n(36707),l=a.ArrayBuffer,f=a.DataView,d=l.prototype.slice,h=i(function(){return!new l(2).slice(1,void 0).byteLength});r({target:"ArrayBuffer",proto:!0,unsafe:!0,forced:h},{slice:function(e,t){if(void 0!==d&&void 0===t)return d.call(o(this),e);for(var n=o(this).byteLength,r=s(e,n),i=s(void 0===t?n:t,n),a=new(c(this,l))(u(i-r)),h=new f(this),p=new f(a),b=0;r=0?r:n+r;return s<0||s>=n?void 0:t[s]}}),s("at")},92222(e,t,n){"use strict";var r=n(82109),i=n(47293),a=n(43157),o=n(70111),s=n(47908),u=n(17466),c=n(86135),l=n(65417),f=n(81194),d=n(5112),h=n(7392),p=d("isConcatSpreadable"),b=9007199254740991,m="Maximum allowed index exceeded",g=h>=51||!i(function(){var e=[];return e[p]=!1,e.concat()[0]!==e}),v=f("concat"),y=function(e){if(!o(e))return!1;var t=e[p];return void 0!==t?!!t:a(e)};r({target:"Array",proto:!0,forced:!g||!v},{concat:function(e){var t,n,r,i,a,o=s(this),f=l(o,0),d=0;for(t=-1,r=arguments.length;tb)throw TypeError(m);for(n=0;n=b)throw TypeError(m);c(f,d++,a)}return f.length=d,f}})},50545(e,t,n){var r=n(82109),i=n(1048),a=n(51223);r({target:"Array",proto:!0},{copyWithin:i}),a("copyWithin")},26541(e,t,n){"use strict";var r=n(82109),i=n(42092).every,a=n(9341)("every");r({target:"Array",proto:!0,forced:!a},{every:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}})},43290(e,t,n){var r=n(82109),i=n(21285),a=n(51223);r({target:"Array",proto:!0},{fill:i}),a("fill")},57327(e,t,n){"use strict";var r=n(82109),i=n(42092).filter,a=n(81194)("filter");r({target:"Array",proto:!0,forced:!a},{filter:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}})},34553(e,t,n){"use strict";var r=n(82109),i=n(42092).findIndex,a=n(51223),o="findIndex",s=!0;o in[]&&[,][o](function(){s=!1}),r({target:"Array",proto:!0,forced:s},{findIndex:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}}),a(o)},69826(e,t,n){"use strict";var r=n(82109),i=n(42092).find,a=n(51223),o="find",s=!0;o in[]&&[,][o](function(){s=!1}),r({target:"Array",proto:!0,forced:s},{find:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}}),a(o)},86535(e,t,n){"use strict";var r=n(82109),i=n(6790),a=n(47908),o=n(17466),s=n(13099),u=n(65417);r({target:"Array",proto:!0},{flatMap:function(e){var t,n=a(this),r=o(n.length);return s(e),(t=u(n,0)).length=i(t,n,n,r,0,1,e,arguments.length>1?arguments[1]:void 0),t}})},84944(e,t,n){"use strict";var r=n(82109),i=n(6790),a=n(47908),o=n(17466),s=n(99958),u=n(65417);r({target:"Array",proto:!0},{flat:function(){var e=arguments.length?arguments[0]:void 0,t=a(this),n=o(t.length),r=u(t,0);return r.length=i(r,t,t,n,0,void 0===e?1:s(e)),r}})},89554(e,t,n){"use strict";var r=n(82109),i=n(18533);r({target:"Array",proto:!0,forced:[].forEach!=i},{forEach:i})},91038(e,t,n){var r=n(82109),i=n(48457),a=!n(17072)(function(e){Array.from(e)});r({target:"Array",stat:!0,forced:a},{from:i})},26699(e,t,n){"use strict";var r=n(82109),i=n(41318).includes,a=n(51223);r({target:"Array",proto:!0},{includes:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}}),a("includes")},82772(e,t,n){"use strict";var r=n(82109),i=n(41318).indexOf,a=n(9341),o=[].indexOf,s=!!o&&1/[1].indexOf(1,-0)<0,u=a("indexOf");r({target:"Array",proto:!0,forced:s||!u},{indexOf:function(e){return s?o.apply(this,arguments)||0:i(this,e,arguments.length>1?arguments[1]:void 0)}})},79753(e,t,n){var r=n(82109),i=n(43157);r({target:"Array",stat:!0},{isArray:i})},66992(e,t,n){"use strict";var r=n(45656),i=n(51223),a=n(97497),o=n(29909),s=n(70654),u="Array Iterator",c=o.set,l=o.getterFor(u);e.exports=s(Array,"Array",function(e,t){c(this,{type:u,target:r(e),index:0,kind:t})},function(){var e=l(this),t=e.target,n=e.kind,r=e.index++;return!t||r>=t.length?(e.target=void 0,{value:void 0,done:!0}):"keys"==n?{value:r,done:!1}:"values"==n?{value:t[r],done:!1}:{value:[r,t[r]],done:!1}},"values"),a.Arguments=a.Array,i("keys"),i("values"),i("entries")},69600(e,t,n){"use strict";var r=n(82109),i=n(68361),a=n(45656),o=n(9341),s=[].join,u=i!=Object,c=o("join",",");r({target:"Array",proto:!0,forced:u||!c},{join:function(e){return s.call(a(this),void 0===e?",":e)}})},94986(e,t,n){var r=n(82109),i=n(86583);r({target:"Array",proto:!0,forced:i!==[].lastIndexOf},{lastIndexOf:i})},21249(e,t,n){"use strict";var r=n(82109),i=n(42092).map,a=n(81194)("map");r({target:"Array",proto:!0,forced:!a},{map:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}})},26572(e,t,n){"use strict";var r=n(82109),i=n(47293),a=n(86135),o=i(function(){function e(){}return!(Array.of.call(e) instanceof e)});r({target:"Array",stat:!0,forced:o},{of:function(){for(var e=0,t=arguments.length,n=new("function"==typeof this?this:Array)(t);t>e;)a(n,e,arguments[e++]);return n.length=t,n}})},96644(e,t,n){"use strict";var r=n(82109),i=n(53671).right,a=n(9341),o=n(7392),s=n(35268),u=a("reduceRight"),c=!s&&o>79&&o<83;r({target:"Array",proto:!0,forced:!u||c},{reduceRight:function(e){return i(this,e,arguments.length,arguments.length>1?arguments[1]:void 0)}})},85827(e,t,n){"use strict";var r=n(82109),i=n(53671).left,a=n(9341),o=n(7392),s=n(35268),u=a("reduce"),c=!s&&o>79&&o<83;r({target:"Array",proto:!0,forced:!u||c},{reduce:function(e){return i(this,e,arguments.length,arguments.length>1?arguments[1]:void 0)}})},65069(e,t,n){"use strict";var r=n(82109),i=n(43157),a=[].reverse,o=[1,2];r({target:"Array",proto:!0,forced:String(o)===String(o.reverse())},{reverse:function(){return i(this)&&(this.length=this.length),a.call(this)}})},47042(e,t,n){"use strict";var r=n(82109),i=n(70111),a=n(43157),o=n(51400),s=n(17466),u=n(45656),c=n(86135),l=n(5112),f=n(81194)("slice"),d=l("species"),h=[].slice,p=Math.max;r({target:"Array",proto:!0,forced:!f},{slice:function(e,t){var n,r,l,f=u(this),b=s(f.length),m=o(e,b),g=o(void 0===t?b:t,b);if(a(f)&&("function"==typeof(n=f.constructor)&&(n===Array||a(n.prototype))?n=void 0:i(n)&&null===(n=n[d])&&(n=void 0),n===Array||void 0===n))return h.call(f,m,g);for(l=0,r=new(void 0===n?Array:n)(p(g-m,0));m1?arguments[1]:void 0)}})},2707(e,t,n){"use strict";var r=n(82109),i=n(13099),a=n(47908),o=n(17466),s=n(41340),u=n(47293),c=n(94362),l=n(9341),f=n(68886),d=n(30256),h=n(7392),p=n(98008),b=[],m=b.sort,g=u(function(){b.sort(void 0)}),v=u(function(){b.sort(null)}),y=l("sort"),w=!u(function(){if(h)return h<70;if(!f||!(f>3)){if(d)return!0;if(p)return p<603;var e,t,n,r,i="";for(e=65;e<76;e++){switch(t=String.fromCharCode(e),e){case 66:case 69:case 70:case 72:n=3;break;case 68:case 71:n=4;break;default:n=2}for(r=0;r<47;r++)b.push({k:t+r,v:n})}for(b.sort(function(e,t){return t.v-e.v}),r=0;rs(n)?1:-1}};r({target:"Array",proto:!0,forced:_},{sort:function(e){void 0!==e&&i(e);var t,n,r=a(this);if(w)return void 0===e?m.call(r):m.call(r,e);var s=[],u=o(r.length);for(n=0;nh)throw TypeError(p);for(b=0,l=u(v,r);by-r+n;b--)delete v[b-1]}else if(n>r)for(b=y-r;b>w;b--)m=b+r-1,g=b+n-1,m in v?v[g]=v[m]:delete v[g];for(b=0;b94906265.62425156?s(e)+c:a(e-1+u(e-1)*u(e+1))}})},82376(e,t,n){var r=n(82109),i=Math.asinh,a=Math.log,o=Math.sqrt;function s(e){return isFinite(e=+e)&&0!=e?e<0?-s(-e):a(e+o(e*e+1)):e}r({target:"Math",stat:!0,forced:!(i&&1/i(0)>0)},{asinh:s})},73181(e,t,n){var r=n(82109),i=Math.atanh,a=Math.log;r({target:"Math",stat:!0,forced:!(i&&1/i(-0)<0)},{atanh:function(e){return 0==(e=+e)?e:a((1+e)/(1-e))/2}})},23484(e,t,n){var r=n(82109),i=n(64310),a=Math.abs,o=Math.pow;r({target:"Math",stat:!0},{cbrt:function(e){return i(e=+e)*o(a(e),1/3)}})},2388(e,t,n){var r=n(82109),i=Math.floor,a=Math.log,o=Math.LOG2E;r({target:"Math",stat:!0},{clz32:function(e){return(e>>>=0)?31-i(a(e+.5)*o):32}})},88621(e,t,n){var r=n(82109),i=n(66736),a=Math.cosh,o=Math.abs,s=Math.E;r({target:"Math",stat:!0,forced:!a||a(710)===1/0},{cosh:function(e){var t=i(o(e)-1)+1;return(t+1/(t*s*s))*(s/2)}})},60403(e,t,n){var r=n(82109),i=n(66736);r({target:"Math",stat:!0,forced:i!=Math.expm1},{expm1:i})},84755(e,t,n){var r=n(82109),i=n(26130);r({target:"Math",stat:!0},{fround:i})},25438(e,t,n){var r=n(82109),i=Math.hypot,a=Math.abs,o=Math.sqrt,s=!!i&&i(1/0,NaN)!==1/0;r({target:"Math",stat:!0,forced:s},{hypot:function(e,t){for(var n,r,i=0,s=0,u=arguments.length,c=0;s0?i+=(r=n/c)*r:i+=n;return c===1/0?1/0:c*o(i)}})},90332(e,t,n){var r=n(82109),i=n(47293),a=Math.imul,o=i(function(){return -5!=a(4294967295,5)||2!=a.length});r({target:"Math",stat:!0,forced:o},{imul:function(e,t){var n=65535,r=+e,i=+t,a=n&r,o=n&i;return 0|a*o+((n&r>>>16)*o+a*(n&i>>>16)<<16>>>0)}})},40658(e,t,n){var r=n(82109),i=Math.log,a=Math.LOG10E;r({target:"Math",stat:!0},{log10:function(e){return i(e)*a}})},40197(e,t,n){var r=n(82109),i=n(26513);r({target:"Math",stat:!0},{log1p:i})},44914(e,t,n){var r=n(82109),i=Math.log,a=Math.LN2;r({target:"Math",stat:!0},{log2:function(e){return i(e)/a}})},52420(e,t,n){var r=n(82109),i=n(64310);r({target:"Math",stat:!0},{sign:i})},60160(e,t,n){var r=n(82109),i=n(47293),a=n(66736),o=Math.abs,s=Math.exp,u=Math.E,c=i(function(){return -.00000000000000002!=Math.sinh(-.00000000000000002)});r({target:"Math",stat:!0,forced:c},{sinh:function(e){return 1>o(e=+e)?(a(e)-a(-e))/2:(s(e-1)-s(-e-1))*(u/2)}})},60970(e,t,n){var r=n(82109),i=n(66736),a=Math.exp;r({target:"Math",stat:!0},{tanh:function(e){var t=i(e=+e),n=i(-e);return t==1/0?1:n==1/0?-1:(t-n)/(a(e)+a(-e))}})},10408(e,t,n){n(58003)(Math,"Math",!0)},73689(e,t,n){var r=n(82109),i=Math.ceil,a=Math.floor;r({target:"Math",stat:!0},{trunc:function(e){return(e>0?a:i)(e)}})},9653(e,t,n){"use strict";var r=n(19781),i=n(17854),a=n(54705),o=n(31320),s=n(86656),u=n(84326),c=n(79587),l=n(52190),f=n(57593),d=n(47293),h=n(70030),p=n(8006).f,b=n(31236).f,m=n(3070).f,g=n(53111).trim,v="Number",y=i[v],w=y.prototype,_=u(h(w))==v,E=function(e){if(l(e))throw TypeError("Cannot convert a Symbol value to a number");var t,n,r,i,a,o,s,u,c=f(e,"number");if("string"==typeof c&&c.length>2){if(43===(t=(c=g(c)).charCodeAt(0))||45===t){if(88===(n=c.charCodeAt(2))||120===n)return NaN}else if(48===t){switch(c.charCodeAt(1)){case 66:case 98:r=2,i=49;break;case 79:case 111:r=8,i=55;break;default:return+c}for(s=0,o=(a=c.slice(2)).length;si)return NaN;return parseInt(a,r)}}return+c};if(a(v,!y(" 0o1")||!y("0b1")||y("+0x1"))){for(var S,k=function(e){var t=arguments.length<1?0:e,n=this;return n instanceof k&&(_?d(function(){w.valueOf.call(n)}):u(n)!=v)?c(new y(E(t)),n,k):E(t)},x=r?p(y):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,fromString,range".split(","),T=0;x.length>T;T++)s(y,S=x[T])&&!s(k,S)&&m(k,S,b(y,S));k.prototype=w,w.constructor=k,o(i,v,k)}},93299(e,t,n){n(82109)({target:"Number",stat:!0},{EPSILON:2220446049250313e-31})},35192(e,t,n){var r=n(82109),i=n(77023);r({target:"Number",stat:!0},{isFinite:i})},33161(e,t,n){var r=n(82109),i=n(18730);r({target:"Number",stat:!0},{isInteger:i})},44048(e,t,n){n(82109)({target:"Number",stat:!0},{isNaN:function(e){return e!=e}})},78285(e,t,n){var r=n(82109),i=n(18730),a=Math.abs;r({target:"Number",stat:!0},{isSafeInteger:function(e){return i(e)&&9007199254740991>=a(e)}})},44363(e,t,n){n(82109)({target:"Number",stat:!0},{MAX_SAFE_INTEGER:9007199254740991})},55994(e,t,n){n(82109)({target:"Number",stat:!0},{MIN_SAFE_INTEGER:-9007199254740991})},61874(e,t,n){var r=n(82109),i=n(2814);r({target:"Number",stat:!0,forced:Number.parseFloat!=i},{parseFloat:i})},9494(e,t,n){var r=n(82109),i=n(83009);r({target:"Number",stat:!0,forced:Number.parseInt!=i},{parseInt:i})},56977(e,t,n){"use strict";var r=n(82109),i=n(99958),a=n(50863),o=n(38415),s=n(47293),u=1..toFixed,c=Math.floor,l=function(e,t,n){return 0===t?n:t%2==1?l(e,t-1,n*e):l(e*e,t/2,n)},f=function(e){for(var t=0,n=e;n>=4096;)t+=12,n/=4096;for(;n>=2;)t+=1,n/=2;return t},d=function(e,t,n){for(var r=-1,i=n;++r<6;)i+=t*e[r],e[r]=i%1e7,i=c(i/1e7)},h=function(e,t){for(var n=6,r=0;--n>=0;)r+=e[n],e[n]=c(r/t),r=r%t*1e7},p=function(e){for(var t=6,n="";--t>=0;)if(""!==n||0===t||0!==e[t]){var r=String(e[t]);n=""===n?r:n+o.call("0",7-r.length)+r}return n},b=!!u||!s(function(){u.call({})});r({target:"Number",proto:!0,forced:b},{toFixed:function(e){var t,n,r,s,u=a(this),c=i(e),b=[0,0,0,0,0,0],m="",g="0";if(c<0||c>20)throw RangeError("Incorrect fraction digits");if(u!=u)return"NaN";if(u<=-1e21||u>=1e21)return String(u);if(u<0&&(m="-",u=-u),u>1e-21){if(n=(t=f(u*l(2,69,1))-69)<0?u*l(2,-t,1):u/l(2,t,1),n*=4503599627370496,(t=52-t)>0){for(d(b,0,n),r=c;r>=7;)d(b,1e7,0),r-=7;for(d(b,l(10,r,1),0),r=t-1;r>=23;)h(b,8388608),r-=23;h(b,1<0?m+((s=g.length)<=c?"0."+o.call("0",c-s)+g:g.slice(0,s-c)+"."+g.slice(s-c)):m+g}})},55147(e,t,n){"use strict";var r=n(82109),i=n(47293),a=n(50863),o=1..toPrecision,s=i(function(){return"1"!==o.call(1,void 0)})||!i(function(){o.call({})});r({target:"Number",proto:!0,forced:s},{toPrecision:function(e){return void 0===e?o.call(a(this)):o.call(a(this),e)}})},19601(e,t,n){var r=n(82109),i=n(21574);r({target:"Object",stat:!0,forced:Object.assign!==i},{assign:i})},78011(e,t,n){var r=n(82109),i=n(19781),a=n(70030);r({target:"Object",stat:!0,sham:!i},{create:a})},59595(e,t,n){"use strict";var r=n(82109),i=n(19781),a=n(69026),o=n(47908),s=n(13099),u=n(3070);i&&r({target:"Object",proto:!0,forced:a},{__defineGetter__:function(e,t){u.f(o(this),e,{get:s(t),enumerable:!0,configurable:!0})}})},33321(e,t,n){var r=n(82109),i=n(19781),a=n(36048);r({target:"Object",stat:!0,forced:!i,sham:!i},{defineProperties:a})},69070(e,t,n){var r=n(82109),i=n(19781),a=n(3070);r({target:"Object",stat:!0,forced:!i,sham:!i},{defineProperty:a.f})},35500(e,t,n){"use strict";var r=n(82109),i=n(19781),a=n(69026),o=n(47908),s=n(13099),u=n(3070);i&&r({target:"Object",proto:!0,forced:a},{__defineSetter__:function(e,t){u.f(o(this),e,{set:s(t),enumerable:!0,configurable:!0})}})},69720(e,t,n){var r=n(82109),i=n(44699).entries;r({target:"Object",stat:!0},{entries:function(e){return i(e)}})},43371(e,t,n){var r=n(82109),i=n(76677),a=n(47293),o=n(70111),s=n(62423).onFreeze,u=Object.freeze,c=a(function(){u(1)});r({target:"Object",stat:!0,forced:c,sham:!i},{freeze:function(e){return u&&o(e)?u(s(e)):e}})},38559(e,t,n){var r=n(82109),i=n(20408),a=n(86135);r({target:"Object",stat:!0},{fromEntries:function(e){var t={};return i(e,function(e,n){a(t,e,n)},{AS_ENTRIES:!0}),t}})},38880(e,t,n){var r=n(82109),i=n(47293),a=n(45656),o=n(31236).f,s=n(19781),u=i(function(){o(1)}),c=!s||u;r({target:"Object",stat:!0,forced:c,sham:!s},{getOwnPropertyDescriptor:function(e,t){return o(a(e),t)}})},49337(e,t,n){var r=n(82109),i=n(19781),a=n(53887),o=n(45656),s=n(31236),u=n(86135);r({target:"Object",stat:!0,sham:!i},{getOwnPropertyDescriptors:function(e){for(var t,n,r=o(e),i=s.f,c=a(r),l={},f=0;c.length>f;)void 0!==(n=i(r,t=c[f++]))&&u(l,t,n);return l}})},36210(e,t,n){var r=n(82109),i=n(47293),a=n(1156).f,o=i(function(){return!Object.getOwnPropertyNames(1)});r({target:"Object",stat:!0,forced:o},{getOwnPropertyNames:a})},30489(e,t,n){var r=n(82109),i=n(47293),a=n(47908),o=n(79518),s=n(49920),u=i(function(){o(1)});r({target:"Object",stat:!0,forced:u,sham:!s},{getPrototypeOf:function(e){return o(a(e))}})},46314(e,t,n){var r=n(82109),i=n(86656);r({target:"Object",stat:!0},{hasOwn:i})},41825(e,t,n){var r=n(82109),i=n(47293),a=n(70111),o=Object.isExtensible,s=i(function(){o(1)});r({target:"Object",stat:!0,forced:s},{isExtensible:function(e){return!!a(e)&&(!o||o(e))}})},98410(e,t,n){var r=n(82109),i=n(47293),a=n(70111),o=Object.isFrozen,s=i(function(){o(1)});r({target:"Object",stat:!0,forced:s},{isFrozen:function(e){return!a(e)||!!o&&o(e)}})},72200(e,t,n){var r=n(82109),i=n(47293),a=n(70111),o=Object.isSealed,s=i(function(){o(1)});r({target:"Object",stat:!0,forced:s},{isSealed:function(e){return!a(e)||!!o&&o(e)}})},43304(e,t,n){var r=n(82109),i=n(81150);r({target:"Object",stat:!0},{is:i})},47941(e,t,n){var r=n(82109),i=n(47908),a=n(81956),o=n(47293)(function(){a(1)});r({target:"Object",stat:!0,forced:o},{keys:function(e){return a(i(e))}})},94869(e,t,n){"use strict";var r=n(82109),i=n(19781),a=n(69026),o=n(47908),s=n(34948),u=n(79518),c=n(31236).f;i&&r({target:"Object",proto:!0,forced:a},{__lookupGetter__:function(e){var t,n=o(this),r=s(e);do if(t=c(n,r))return t.get;while(n=u(n))}})},33952(e,t,n){"use strict";var r=n(82109),i=n(19781),a=n(69026),o=n(47908),s=n(34948),u=n(79518),c=n(31236).f;i&&r({target:"Object",proto:!0,forced:a},{__lookupSetter__:function(e){var t,n=o(this),r=s(e);do if(t=c(n,r))return t.set;while(n=u(n))}})},57227(e,t,n){var r=n(82109),i=n(70111),a=n(62423).onFreeze,o=n(76677),s=n(47293),u=Object.preventExtensions,c=s(function(){u(1)});r({target:"Object",stat:!0,forced:c,sham:!o},{preventExtensions:function(e){return u&&i(e)?u(a(e)):e}})},60514(e,t,n){var r=n(82109),i=n(70111),a=n(62423).onFreeze,o=n(76677),s=n(47293),u=Object.seal,c=s(function(){u(1)});r({target:"Object",stat:!0,forced:c,sham:!o},{seal:function(e){return u&&i(e)?u(a(e)):e}})},68304(e,t,n){var r=n(82109),i=n(27674);r({target:"Object",stat:!0},{setPrototypeOf:i})},41539(e,t,n){var r=n(51694),i=n(31320),a=n(90288);r||i(Object.prototype,"toString",a,{unsafe:!0})},26833(e,t,n){var r=n(82109),i=n(44699).values;r({target:"Object",stat:!0},{values:function(e){return i(e)}})},54678(e,t,n){var r=n(82109),i=n(2814);r({global:!0,forced:parseFloat!=i},{parseFloat:i})},91058(e,t,n){var r=n(82109),i=n(83009);r({global:!0,forced:parseInt!=i},{parseInt:i})},17922(e,t,n){"use strict";var r=n(82109),i=n(13099),a=n(78523),o=n(12534),s=n(20408);r({target:"Promise",stat:!0},{allSettled:function(e){var t=this,n=a.f(t),r=n.resolve,u=n.reject,c=o(function(){var n=i(t.resolve),a=[],o=0,u=1;s(e,function(e){var i=o++,s=!1;a.push(void 0),u++,n.call(t,e).then(function(e){!s&&(s=!0,a[i]={status:"fulfilled",value:e},--u||r(a))},function(e){!s&&(s=!0,a[i]={status:"rejected",reason:e},--u||r(a))})}),--u||r(a)});return c.error&&u(c.value),n.promise}})},34668(e,t,n){"use strict";var r=n(82109),i=n(13099),a=n(35005),o=n(78523),s=n(12534),u=n(20408),c="No one promise resolved";r({target:"Promise",stat:!0},{any:function(e){var t=this,n=o.f(t),r=n.resolve,l=n.reject,f=s(function(){var n=i(t.resolve),o=[],s=0,f=1,d=!1;u(e,function(e){var i=s++,u=!1;o.push(void 0),f++,n.call(t,e).then(function(e){u||d||(d=!0,r(e))},function(e){!u&&!d&&(u=!0,o[i]=e,--f||l(new(a("AggregateError"))(o,c)))})}),--f||l(new(a("AggregateError"))(o,c))});return f.error&&l(f.value),n.promise}})},17727(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(13366),o=n(47293),s=n(35005),u=n(36707),c=n(69478),l=n(31320),f=!!a&&o(function(){a.prototype.finally.call({then:function(){}},function(){})});if(r({target:"Promise",proto:!0,real:!0,forced:f},{finally:function(e){var t=u(this,s("Promise")),n="function"==typeof e;return this.then(n?function(n){return c(t,e()).then(function(){return n})}:e,n?function(n){return c(t,e()).then(function(){throw n})}:e)}}),!i&&"function"==typeof a){var d=s("Promise").prototype.finally;a.prototype.finally!==d&&l(a.prototype,"finally",d,{unsafe:!0})}},88674(e,t,n){"use strict";var r,i,a,o,s=n(82109),u=n(31913),c=n(17854),l=n(35005),f=n(13366),d=n(31320),h=n(12248),p=n(27674),b=n(58003),m=n(96340),g=n(70111),v=n(13099),y=n(25787),w=n(42788),_=n(20408),E=n(17072),S=n(36707),k=n(20261).set,x=n(95948),T=n(69478),M=n(842),O=n(78523),A=n(12534),L=n(29909),C=n(54705),I=n(5112),D=n(7871),N=n(35268),P=n(7392),R=I("species"),j="Promise",F=L.get,Y=L.set,B=L.getterFor(j),U=f&&f.prototype,H=f,$=U,z=c.TypeError,G=c.document,W=c.process,K=O.f,V=K,q=!!(G&&G.createEvent&&c.dispatchEvent),Z="function"==typeof PromiseRejectionEvent,X="unhandledrejection",J="rejectionhandled",Q=0,ee=1,et=2,en=1,er=2,ei=!1,ea=C(j,function(){var e=w(H),t=e!==String(H);if(!t&&66===P||u&&!$.finally)return!0;if(P>=51&&/native code/.test(e))return!1;var n=new H(function(e){e(1)}),r=function(e){e(function(){},function(){})};return(n.constructor={})[R]=r,!(ei=n.then(function(){}) instanceof r)||!t&&D&&!Z}),eo=ea||!E(function(e){H.all(e).catch(function(){})}),es=function(e){var t;return!!g(e)&&"function"==typeof(t=e.then)&&t},eu=function(e,t){if(!e.notified){e.notified=!0;var n=e.reactions;x(function(){for(var r=e.value,i=e.state==ee,a=0;n.length>a;){var o,s,u,c=n[a++],l=i?c.ok:c.fail,f=c.resolve,d=c.reject,h=c.domain;try{l?(i||(e.rejection===er&&ed(e),e.rejection=en),!0===l?o=r:(h&&h.enter(),o=l(r),h&&(h.exit(),u=!0)),o===c.promise?d(z("Promise-chain cycle")):(s=es(o))?s.call(o,f,d):f(o)):d(r)}catch(p){h&&!u&&h.exit(),d(p)}}e.reactions=[],e.notified=!1,t&&!e.rejection&&el(e)})}},ec=function(e,t,n){var r,i;q?((r=G.createEvent("Event")).promise=t,r.reason=n,r.initEvent(e,!1,!0),c.dispatchEvent(r)):r={promise:t,reason:n},!Z&&(i=c["on"+e])?i(r):e===X&&M("Unhandled promise rejection",n)},el=function(e){k.call(c,function(){var t,n=e.facade,r=e.value;if(ef(e)&&(t=A(function(){N?W.emit("unhandledRejection",r,n):ec(X,n,r)}),e.rejection=N||ef(e)?er:en,t.error))throw t.value})},ef=function(e){return e.rejection!==en&&!e.parent},ed=function(e){k.call(c,function(){var t=e.facade;N?W.emit("rejectionHandled",t):ec(J,t,e.value)})},eh=function(e,t,n){return function(r){e(t,r,n)}},ep=function(e,t,n){e.done||(e.done=!0,n&&(e=n),e.value=t,e.state=et,eu(e,!0))},eb=function(e,t,n){if(!e.done){e.done=!0,n&&(e=n);try{if(e.facade===t)throw z("Promise can't be resolved itself");var r=es(t);r?x(function(){var n={done:!1};try{r.call(t,eh(eb,n,e),eh(ep,n,e))}catch(i){ep(n,i,e)}}):(e.value=t,e.state=ee,eu(e,!1))}catch(i){ep({done:!1},i,e)}}};if(ea&&($=(H=function(e){y(this,H,j),v(e),r.call(this);var t=F(this);try{e(eh(eb,t),eh(ep,t))}catch(n){ep(t,n)}}).prototype,(r=function(e){Y(this,{type:j,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:Q,value:void 0})}).prototype=h($,{then:function(e,t){var n=B(this),r=K(S(this,H));return r.ok="function"!=typeof e||e,r.fail="function"==typeof t&&t,r.domain=N?W.domain:void 0,n.parent=!0,n.reactions.push(r),n.state!=Q&&eu(n,!1),r.promise},catch:function(e){return this.then(void 0,e)}}),i=function(){var e=new r,t=F(e);this.promise=e,this.resolve=eh(eb,t),this.reject=eh(ep,t)},O.f=K=function(e){return e===H||e===a?new i(e):V(e)},!u&&"function"==typeof f&&U!==Object.prototype)){o=U.then,ei||(d(U,"then",function(e,t){var n=this;return new H(function(e,t){o.call(n,e,t)}).then(e,t)},{unsafe:!0}),d(U,"catch",$.catch,{unsafe:!0}));try{delete U.constructor}catch(em){}p&&p(U,$)}s({global:!0,wrap:!0,forced:ea},{Promise:H}),b(H,j,!1,!0),m(j),a=l(j),s({target:j,stat:!0,forced:ea},{reject:function(e){var t=K(this);return t.reject.call(void 0,e),t.promise}}),s({target:j,stat:!0,forced:u||ea},{resolve:function(e){return T(u&&this===a?H:this,e)}}),s({target:j,stat:!0,forced:eo},{all:function(e){var t=this,n=K(t),r=n.resolve,i=n.reject,a=A(function(){var n=v(t.resolve),a=[],o=0,s=1;_(e,function(e){var u=o++,c=!1;a.push(void 0),s++,n.call(t,e).then(function(e){!c&&(c=!0,a[u]=e,--s||r(a))},i)}),--s||r(a)});return a.error&&i(a.value),n.promise},race:function(e){var t=this,n=K(t),r=n.reject,i=A(function(){var i=v(t.resolve);_(e,function(e){i.call(t,e).then(n.resolve,r)})});return i.error&&r(i.value),n.promise}})},36535(e,t,n){var r=n(82109),i=n(35005),a=n(13099),o=n(19670),s=n(47293),u=i("Reflect","apply"),c=Function.apply,l=!s(function(){u(function(){})});r({target:"Reflect",stat:!0,forced:l},{apply:function(e,t,n){return a(e),o(n),u?u(e,t,n):c.call(e,t,n)}})},12419(e,t,n){var r=n(82109),i=n(35005),a=n(13099),o=n(19670),s=n(70111),u=n(70030),c=n(27065),l=n(47293),f=i("Reflect","construct"),d=l(function(){function e(){}return!(f(function(){},[],e) instanceof e)}),h=!l(function(){f(function(){})}),p=d||h;r({target:"Reflect",stat:!0,forced:p,sham:p},{construct:function(e,t){a(e),o(t);var n=arguments.length<3?e:a(arguments[2]);if(h&&!d)return f(e,t,n);if(e==n){switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3])}var r=[null];return r.push.apply(r,t),new(c.apply(e,r))}var i=n.prototype,l=u(s(i)?i:Object.prototype),p=Function.apply.call(e,l,t);return s(p)?p:l}})},69596(e,t,n){var r=n(82109),i=n(19781),a=n(19670),o=n(34948),s=n(3070),u=n(47293)(function(){Reflect.defineProperty(s.f({},1,{value:1}),1,{value:2})});r({target:"Reflect",stat:!0,forced:u,sham:!i},{defineProperty:function(e,t,n){a(e);var r=o(t);a(n);try{return s.f(e,r,n),!0}catch(i){return!1}}})},52586(e,t,n){var r=n(82109),i=n(19670),a=n(31236).f;r({target:"Reflect",stat:!0},{deleteProperty:function(e,t){var n=a(i(e),t);return(!n||!!n.configurable)&&delete e[t]}})},95683(e,t,n){var r=n(82109),i=n(19781),a=n(19670),o=n(31236);r({target:"Reflect",stat:!0,sham:!i},{getOwnPropertyDescriptor:function(e,t){return o.f(a(e),t)}})},39361(e,t,n){var r=n(82109),i=n(19670),a=n(79518),o=n(49920);r({target:"Reflect",stat:!0,sham:!o},{getPrototypeOf:function(e){return a(i(e))}})},74819(e,t,n){var r=n(82109),i=n(70111),a=n(19670),o=n(45032),s=n(31236),u=n(79518);function c(e,t){var n,r,l=arguments.length<3?e:arguments[2];return a(e)===l?e[t]:(n=s.f(e,t))?o(n)?n.value:void 0===n.get?void 0:n.get.call(l):i(r=u(e))?c(r,t,l):void 0}r({target:"Reflect",stat:!0},{get:c})},51037(e,t,n){n(82109)({target:"Reflect",stat:!0},{has:function(e,t){return t in e}})},5898(e,t,n){var r=n(82109),i=n(19670),a=Object.isExtensible;r({target:"Reflect",stat:!0},{isExtensible:function(e){return i(e),!a||a(e)}})},67556(e,t,n){var r=n(82109),i=n(53887);r({target:"Reflect",stat:!0},{ownKeys:i})},14361(e,t,n){var r=n(82109),i=n(35005),a=n(19670),o=n(76677);r({target:"Reflect",stat:!0,sham:!o},{preventExtensions:function(e){a(e);try{var t=i("Object","preventExtensions");return t&&t(e),!0}catch(n){return!1}}})},39532(e,t,n){var r=n(82109),i=n(19670),a=n(96077),o=n(27674);o&&r({target:"Reflect",stat:!0},{setPrototypeOf:function(e,t){i(e),a(t);try{return o(e,t),!0}catch(n){return!1}}})},83593(e,t,n){var r=n(82109),i=n(19670),a=n(70111),o=n(45032),s=n(47293),u=n(3070),c=n(31236),l=n(79518),f=n(79114);function d(e,t,n){var r,s,h,p=arguments.length<4?e:arguments[3],b=c.f(i(e),t);if(!b){if(a(s=l(e)))return d(s,t,n,p);b=f(0)}if(o(b)){if(!1===b.writable||!a(p))return!1;if(r=c.f(p,t)){if(r.get||r.set||!1===r.writable)return!1;r.value=n,u.f(p,t,r)}else u.f(p,t,f(0,n))}else{if(void 0===(h=b.set))return!1;h.call(p,n)}return!0}var h=s(function(){var e=function(){},t=u.f(new e,"a",{configurable:!0});return!1!==Reflect.set(e.prototype,"a",1,t)});r({target:"Reflect",stat:!0,forced:h},{set:d})},81299(e,t,n){var r=n(82109),i=n(17854),a=n(58003);r({global:!0},{Reflect:{}}),a(i.Reflect,"Reflect",!0)},24603(e,t,n){var r=n(19781),i=n(17854),a=n(54705),o=n(79587),s=n(68880),u=n(3070).f,c=n(8006).f,l=n(47850),f=n(41340),d=n(67066),h=n(52999),p=n(31320),b=n(47293),m=n(86656),g=n(29909).enforce,v=n(96340),y=n(5112),w=n(9441),_=n(38173),E=y("match"),S=i.RegExp,k=S.prototype,x=/^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/,T=/a/g,M=/a/g,O=new S(T)!==T,A=h.UNSUPPORTED_Y,L=r&&(!O||A||w||_||b(function(){return M[E]=!1,S(T)!=T||S(M)==M||"/a/i"!=S(T,"i")})),C=function(e){for(var t,n=e.length,r=0,i="",a=!1;r<=n;r++){if("\\"===(t=e.charAt(r))){i+=t+e.charAt(++r);continue}a||"."!==t?("["===t?a=!0:"]"===t&&(a=!1),i+=t):i+="[\\s\\S]"}return i},I=function(e){for(var t,n=e.length,r=0,i="",a=[],o={},s=!1,u=!1,c=0,l="";r<=n;r++){if("\\"===(t=e.charAt(r)))t+=e.charAt(++r);else if("]"===t)s=!1;else if(!s)switch(!0){case"["===t:s=!0;break;case"("===t:x.test(e.slice(r+1))&&(r+=2,u=!0),i+=t,c++;continue;case">"===t&&u:if(""===l||m(o,l))throw SyntaxError("Invalid capture group name");o[l]=!0,a.push([l,c]),u=!1,l="";continue}u?l+=t:i+=t}return[i,a]};if(a("RegExp",L)){for(var D=function(e,t){var n,r,i,a,u,c,h=this instanceof D,p=l(e),b=void 0===t,m=[],v=e;if(!h&&p&&b&&e.constructor===D)return e;if((p||e instanceof D)&&(e=e.source,b&&(t=("flags"in v)?v.flags:d.call(v))),e=void 0===e?"":f(e),t=void 0===t?"":f(t),v=e,w&&("dotAll"in T)&&(r=!!t&&t.indexOf("s")>-1)&&(t=t.replace(/s/g,"")),n=t,A&&("sticky"in T)&&(i=!!t&&t.indexOf("y")>-1)&&(t=t.replace(/y/g,"")),_&&(e=(a=I(e))[0],m=a[1]),u=o(S(e,t),h?this:k,D),(r||i||m.length)&&(c=g(u),r&&(c.dotAll=!0,c.raw=D(C(e),n)),i&&(c.sticky=!0),m.length&&(c.groups=m)),e!==v)try{s(u,"source",""===v?"(?:)":v)}catch(y){}return u},N=function(e){(e in D)||u(D,e,{configurable:!0,get:function(){return S[e]},set:function(t){S[e]=t}})},P=c(S),R=0;P.length>R;)N(P[R++]);k.constructor=D,D.prototype=k,p(i,"RegExp",D)}v("RegExp")},28450(e,t,n){var r=n(19781),i=n(9441),a=n(3070).f,o=n(29909).get,s=RegExp.prototype;r&&i&&a(s,"dotAll",{configurable:!0,get:function(){if(this!==s){if(this instanceof RegExp)return!!o(this).dotAll;throw TypeError("Incompatible receiver, RegExp required")}}})},74916(e,t,n){"use strict";var r=n(82109),i=n(22261);r({target:"RegExp",proto:!0,forced:/./.exec!==i},{exec:i})},92087(e,t,n){var r=n(19781),i=n(3070),a=n(67066),o=n(47293);r&&o(function(){return"sy"!==Object.getOwnPropertyDescriptor(RegExp.prototype,"flags").get.call({dotAll:!0,sticky:!0})})&&i.f(RegExp.prototype,"flags",{configurable:!0,get:a})},88386(e,t,n){var r=n(19781),i=n(52999).UNSUPPORTED_Y,a=n(3070).f,o=n(29909).get,s=RegExp.prototype;r&&i&&a(s,"sticky",{configurable:!0,get:function(){if(this!==s){if(this instanceof RegExp)return!!o(this).sticky;throw TypeError("Incompatible receiver, RegExp required")}}})},77601(e,t,n){"use strict";n(74916);var r,i,a=n(82109),o=n(70111),s=(r=!1,(i=/[ac]/).exec=function(){return r=!0,/./.exec.apply(this,arguments)},!0===i.test("abc")&&r),u=/./.test;a({target:"RegExp",proto:!0,forced:!s},{test:function(e){if("function"!=typeof this.exec)return u.call(this,e);var t=this.exec(e);if(null!==t&&!o(t))throw Error("RegExp exec method returned something other than an Object or null");return!!t}})},39714(e,t,n){"use strict";var r=n(31320),i=n(19670),a=n(41340),o=n(47293),s=n(67066),u="toString",c=RegExp.prototype,l=c[u],f=o(function(){return"/a/b"!=l.call({source:"a",flags:"b"})}),d=l.name!=u;(f||d)&&r(RegExp.prototype,u,function(){var e=i(this),t=a(e.source),n=e.flags,r=a(void 0===n&&e instanceof RegExp&&!("flags"in c)?s.call(e):n);return"/"+t+"/"+r},{unsafe:!0})},70189(e,t,n){"use strict";var r=n(77710),i=n(95631);e.exports=r("Set",function(e){return function(){return e(this,arguments.length?arguments[0]:void 0)}},i)},15218(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("anchor")},{anchor:function(e){return i(this,"a","name",e)}})},24506(e,t,n){"use strict";var r=n(82109),i=n(84488),a=n(99958),o=n(17466),s=n(41340),u=n(47293)(function(){return"\uD842"!=="𠮷".at(0)});r({target:"String",proto:!0,forced:u},{at:function(e){var t=s(i(this)),n=o(t.length),r=a(e),u=r>=0?r:n+r;return u<0||u>=n?void 0:t.charAt(u)}})},74475(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("big")},{big:function(){return i(this,"big","","")}})},57929(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("blink")},{blink:function(){return i(this,"blink","","")}})},50915(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("bold")},{bold:function(){return i(this,"b","","")}})},79841(e,t,n){"use strict";var r=n(82109),i=n(28710).codeAt;r({target:"String",proto:!0},{codePointAt:function(e){return i(this,e)}})},27852(e,t,n){"use strict";var r,i=n(82109),a=n(31236).f,o=n(17466),s=n(41340),u=n(3929),c=n(84488),l=n(84964),f=n(31913),d="".endsWith,h=Math.min,p=l("endsWith"),b=!f&&!p&&!!(r=a(String.prototype,"endsWith"))&&!r.writable;i({target:"String",proto:!0,forced:!b&&!p},{endsWith:function(e){var t=s(c(this));u(e);var n=arguments.length>1?arguments[1]:void 0,r=o(t.length),i=void 0===n?r:h(o(n),r),a=s(e);return d?d.call(t,a,i):t.slice(i-a.length,i)===a}})},29253(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("fixed")},{fixed:function(){return i(this,"tt","","")}})},42125(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("fontcolor")},{fontcolor:function(e){return i(this,"font","color",e)}})},78830(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("fontsize")},{fontsize:function(e){return i(this,"font","size",e)}})},94953(e,t,n){var r=n(82109),i=n(51400),a=String.fromCharCode,o=String.fromCodePoint;r({target:"String",stat:!0,forced:!!o&&1!=o.length},{fromCodePoint:function(e){for(var t,n=[],r=arguments.length,o=0;r>o;){if(t=+arguments[o++],i(t,1114111)!==t)throw RangeError(t+" is not a valid code point");n.push(t<65536?a(t):a(((t-=65536)>>10)+55296,t%1024+56320))}return n.join("")}})},32023(e,t,n){"use strict";var r=n(82109),i=n(3929),a=n(84488),o=n(41340),s=n(84964);r({target:"String",proto:!0,forced:!s("includes")},{includes:function(e){return!!~o(a(this)).indexOf(o(i(e)),arguments.length>1?arguments[1]:void 0)}})},58734(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("italics")},{italics:function(){return i(this,"i","","")}})},78783(e,t,n){"use strict";var r=n(28710).charAt,i=n(41340),a=n(29909),o=n(70654),s="String Iterator",u=a.set,c=a.getterFor(s);o(String,"String",function(e){u(this,{type:s,string:i(e),index:0})},function(){var e,t=c(this),n=t.string,i=t.index;return i>=n.length?{value:void 0,done:!0}:(e=r(n,i),t.index+=e.length,{value:e,done:!1})})},29254(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("link")},{link:function(e){return i(this,"a","href",e)}})},76373(e,t,n){"use strict";var r=n(82109),i=n(24994),a=n(84488),o=n(17466),s=n(41340),u=n(13099),c=n(19670),l=n(84326),f=n(47850),d=n(67066),h=n(68880),p=n(47293),b=n(5112),m=n(36707),g=n(31530),v=n(29909),y=n(31913),w=b("matchAll"),_="RegExp String",E=_+" Iterator",S=v.set,k=v.getterFor(E),x=RegExp.prototype,T=x.exec,M="".matchAll,O=!!M&&!p(function(){"a".matchAll(/./)}),A=function(e,t){var n,r=e.exec;if("function"==typeof r){if("object"!=typeof(n=r.call(e,t)))throw TypeError("Incorrect exec result");return n}return T.call(e,t)},L=i(function(e,t,n,r){S(this,{type:E,regexp:e,string:t,global:n,unicode:r,done:!1})},_,function(){var e=k(this);if(e.done)return{value:void 0,done:!0};var t=e.regexp,n=e.string,r=A(t,n);return null===r?{value:void 0,done:e.done=!0}:e.global?(""===s(r[0])&&(t.lastIndex=g(n,o(t.lastIndex),e.unicode)),{value:r,done:!1}):(e.done=!0,{value:r,done:!1})}),C=function(e){var t,n,r,i,a,u,l=c(this),f=s(e);return t=m(l,RegExp),void 0===(n=l.flags)&&l instanceof RegExp&&!("flags"in x)&&(n=d.call(l)),r=void 0===n?"":s(n),i=new t(t===RegExp?l.source:l,r),a=!!~r.indexOf("g"),u=!!~r.indexOf("u"),i.lastIndex=o(l.lastIndex),new L(i,f,a,u)};r({target:"String",proto:!0,forced:O},{matchAll:function(e){var t,n,r,i,o=a(this);if(null!=e){if(f(e)&&!~(t=s(a("flags"in x?e.flags:d.call(e)))).indexOf("g"))throw TypeError("`.matchAll` does not allow non-global regexes");if(O)return M.apply(o,arguments);if(void 0===(r=e[w])&&y&&"RegExp"==l(e)&&(r=C),null!=r)return u(r).call(e,o)}else if(O)return M.apply(o,arguments);return n=s(o),i=RegExp(e,"g"),y?C.call(i,n):i[w](n)}}),y||w in x||h(x,w,C)},4723(e,t,n){"use strict";var r=n(27007),i=n(19670),a=n(17466),o=n(41340),s=n(84488),u=n(31530),c=n(97651);r("match",function(e,t,n){return[function(t){var n=s(this),r=void 0==t?void 0:t[e];return void 0!==r?r.call(t,n):RegExp(t)[e](o(n))},function(e){var r,s=i(this),l=o(e),f=n(t,s,l);if(f.done)return f.value;if(!s.global)return c(s,l);var d=s.unicode;s.lastIndex=0;for(var h=[],p=0;null!==(r=c(s,l));){var b=o(r[0]);h[p]=b,""===b&&(s.lastIndex=u(l,a(s.lastIndex),d)),p++}return 0===p?null:h}]})},66528(e,t,n){"use strict";var r=n(82109),i=n(76650).end,a=n(54986);r({target:"String",proto:!0,forced:a},{padEnd:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}})},83112(e,t,n){"use strict";var r=n(82109),i=n(76650).start,a=n(54986);r({target:"String",proto:!0,forced:a},{padStart:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}})},38992(e,t,n){var r=n(82109),i=n(45656),a=n(17466),o=n(41340);r({target:"String",stat:!0},{raw:function(e){for(var t=i(e.raw),n=a(t.length),r=arguments.length,s=[],u=0;n>u;)s.push(o(t[u++])),ue.length?-1:""===t?n:e.indexOf(t,n)};r({target:"String",proto:!0},{replaceAll:function(e,t){var n,r,c,b,m,g,v,y,w,_=i(this),E=0,S=0,k="";if(null!=e){if((n=a(e))&&!~(r=o(i("flags"in d?e.flags:s.call(e)))).indexOf("g"))throw TypeError("`.replaceAll` does not allow non-global regexes");if(void 0!==(c=e[f]))return c.call(e,_,t);if(l&&n)return o(_).replace(e,t)}for(b=o(_),m=o(e),(g="function"==typeof t)||(t=o(t)),y=h(1,v=m.length),E=p(b,m,0);-1!==E;)w=g?o(t(m,E,b)):u(m,b,E,[],void 0,t),k+=b.slice(S,E)+w,S=E+v,E=p(b,m,E+y);return S")});r("replace",function(e,t,n){var r=v?"$":"$0";return[function(e,n){var r=c(this),i=void 0==e?void 0:e[h];return void 0!==i?i.call(e,r,n):t.call(u(r),e,n)},function(e,i){var c=a(this),h=u(e);if("string"==typeof i&&-1===i.indexOf(r)&&-1===i.indexOf("$<")){var g=n(t,c,h,i);if(g.done)return g.value}var v="function"==typeof i;v||(i=u(i));var y=c.global;if(y){var w=c.unicode;c.lastIndex=0}for(var _=[];;){var E=d(c,h);if(null===E||(_.push(E),!y))break;""===u(E[0])&&(c.lastIndex=l(h,s(c.lastIndex),w))}for(var S="",k=0,x=0;x<_.length;x++){for(var T=u((E=_[x])[0]),M=p(b(o(E.index),h.length),0),O=[],A=1;A=k&&(S+=h.slice(k,M)+I,k=M+T.length)}return S+h.slice(k)}]},!y||!g||v)},64765(e,t,n){"use strict";var r=n(27007),i=n(19670),a=n(84488),o=n(81150),s=n(41340),u=n(97651);r("search",function(e,t,n){return[function(t){var n=a(this),r=void 0==t?void 0:t[e];return void 0!==r?r.call(t,n):RegExp(t)[e](s(n))},function(e){var r=i(this),a=s(e),c=n(t,r,a);if(c.done)return c.value;var l=r.lastIndex;o(l,0)||(r.lastIndex=0);var f=u(r,a);return o(r.lastIndex,l)||(r.lastIndex=l),null===f?-1:f.index}]})},37268(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("small")},{small:function(){return i(this,"small","","")}})},23123(e,t,n){"use strict";var r=n(27007),i=n(47850),a=n(19670),o=n(84488),s=n(36707),u=n(31530),c=n(17466),l=n(41340),f=n(97651),d=n(22261),h=n(52999),p=n(47293),b=h.UNSUPPORTED_Y,m=[].push,g=Math.min,v=4294967295,y=!p(function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2!==n.length||"a"!==n[0]||"b"!==n[1]});r("split",function(e,t,n){var r;return r="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(e,n){var r,a,s,u=l(o(this)),c=void 0===n?v:n>>>0;if(0===c)return[];if(void 0===e)return[u];if(!i(e))return t.call(u,e,c);for(var f=[],h=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(e.sticky?"y":""),p=0,b=RegExp(e.source,h+"g");(r=d.call(b,u))&&(!((a=b.lastIndex)>p)||(f.push(u.slice(p,r.index)),r.length>1&&r.index=c)));)b.lastIndex===r.index&&b.lastIndex++;return p===u.length?(s||!b.test(""))&&f.push(""):f.push(u.slice(p)),f.length>c?f.slice(0,c):f}:"0".split(void 0,0).length?function(e,n){return void 0===e&&0===n?[]:t.call(this,e,n)}:t,[function(t,n){var i=o(this),a=void 0==t?void 0:t[e];return void 0!==a?a.call(t,i,n):r.call(l(i),t,n)},function(e,i){var o=a(this),d=l(e),h=n(r,o,d,i,r!==t);if(h.done)return h.value;var p=s(o,RegExp),m=o.unicode,y=(o.ignoreCase?"i":"")+(o.multiline?"m":"")+(o.unicode?"u":"")+(b?"g":"y"),w=new p(b?"^(?:"+o.source+")":o,y),_=void 0===i?v:i>>>0;if(0===_)return[];if(0===d.length)return null===f(w,d)?[d]:[];for(var E=0,S=0,k=[];S1?arguments[1]:void 0,t.length)),r=s(e);return d?d.call(t,r,n):t.slice(n,n+r.length)===r}})},7397(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("strike")},{strike:function(){return i(this,"strike","","")}})},60086(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("sub")},{sub:function(){return i(this,"sub","","")}})},83650(e,t,n){"use strict";var r=n(82109),i=n(84488),a=n(99958),o=n(41340),s="".slice,u=Math.max,c=Math.min;r({target:"String",proto:!0},{substr:function(e,t){var n,r,l=o(i(this)),f=l.length,d=a(e);return(d===1/0&&(d=0),d<0&&(d=u(f+d,0)),(n=void 0===t?f:a(t))<=0||n===1/0)?"":(r=c(d+n,f),d>=r?"":s.call(l,d,r))}})},80623(e,t,n){"use strict";var r=n(82109),i=n(14230),a=n(43429);r({target:"String",proto:!0,forced:a("sup")},{sup:function(){return i(this,"sup","","")}})},48702(e,t,n){"use strict";var r=n(82109),i=n(53111).end,a=n(76091)("trimEnd"),o=a?function(){return i(this)}:"".trimEnd;r({target:"String",proto:!0,forced:a},{trimEnd:o,trimRight:o})},55674(e,t,n){"use strict";var r=n(82109),i=n(53111).start,a=n(76091)("trimStart"),o=a?function(){return i(this)}:"".trimStart;r({target:"String",proto:!0,forced:a},{trimStart:o,trimLeft:o})},73210(e,t,n){"use strict";var r=n(82109),i=n(53111).trim,a=n(76091);r({target:"String",proto:!0,forced:a("trim")},{trim:function(){return i(this)}})},72443(e,t,n){n(97235)("asyncIterator")},41817(e,t,n){"use strict";var r=n(82109),i=n(19781),a=n(17854),o=n(86656),s=n(70111),u=n(3070).f,c=n(99920),l=a.Symbol;if(i&&"function"==typeof l&&(!("description"in l.prototype)||void 0!==l().description)){var f={},d=function(){var e=arguments.length<1||void 0===arguments[0]?void 0:String(arguments[0]),t=this instanceof d?new l(e):void 0===e?l():l(e);return""===e&&(f[t]=!0),t};c(d,l);var h=d.prototype=l.prototype;h.constructor=d;var p=h.toString,b="Symbol(test)"==String(l("test")),m=/^Symbol\((.*)\)[^)]+$/;u(h,"description",{configurable:!0,get:function(){var e=s(this)?this.valueOf():this,t=p.call(e);if(o(f,e))return"";var n=b?t.slice(7,-1):t.replace(m,"$1");return""===n?void 0:n}}),r({global:!0,forced:!0},{Symbol:d})}},92401(e,t,n){n(97235)("hasInstance")},8722(e,t,n){n(97235)("isConcatSpreadable")},32165(e,t,n){n(97235)("iterator")},82526(e,t,n){"use strict";var r=n(82109),i=n(17854),a=n(35005),o=n(31913),s=n(19781),u=n(30133),c=n(47293),l=n(86656),f=n(43157),d=n(70111),h=n(52190),p=n(19670),b=n(47908),m=n(45656),g=n(34948),v=n(41340),y=n(79114),w=n(70030),_=n(81956),E=n(8006),S=n(1156),k=n(25181),x=n(31236),T=n(3070),M=n(55296),O=n(68880),A=n(31320),L=n(72309),C=n(6200),I=n(3501),D=n(69711),N=n(5112),P=n(6061),R=n(97235),j=n(58003),F=n(29909),Y=n(42092).forEach,B=C("hidden"),U="Symbol",H="prototype",$=N("toPrimitive"),z=F.set,G=F.getterFor(U),W=Object[H],K=i.Symbol,V=a("JSON","stringify"),q=x.f,Z=T.f,X=S.f,J=M.f,Q=L("symbols"),ee=L("op-symbols"),et=L("string-to-symbol-registry"),en=L("symbol-to-string-registry"),er=L("wks"),ei=i.QObject,ea=!ei||!ei[H]||!ei[H].findChild,eo=s&&c(function(){return 7!=w(Z({},"a",{get:function(){return Z(this,"a",{value:7}).a}})).a})?function(e,t,n){var r=q(W,t);r&&delete W[t],Z(e,t,n),r&&e!==W&&Z(W,t,r)}:Z,es=function(e,t){var n=Q[e]=w(K[H]);return z(n,{type:U,tag:e,description:t}),s||(n.description=t),n},eu=function(e,t,n){e===W&&eu(ee,t,n),p(e);var r=g(t);return(p(n),l(Q,r))?(n.enumerable?(l(e,B)&&e[B][r]&&(e[B][r]=!1),n=w(n,{enumerable:y(0,!1)})):(l(e,B)||Z(e,B,y(1,{})),e[B][r]=!0),eo(e,r,n)):Z(e,r,n)},ec=function(e,t){p(e);var n=m(t),r=_(n).concat(ep(n));return Y(r,function(t){(!s||ef.call(n,t))&&eu(e,t,n[t])}),e},el=function(e,t){return void 0===t?w(e):ec(w(e),t)},ef=function(e){var t=g(e),n=J.call(this,t);return(!(this===W&&l(Q,t))||!!l(ee,t))&&(!(n||!l(this,t)||!l(Q,t)||l(this,B)&&this[B][t])||n)},ed=function(e,t){var n=m(e),r=g(t);if(!(n===W&&l(Q,r))||l(ee,r)){var i=q(n,r);return i&&l(Q,r)&&!(l(n,B)&&n[B][r])&&(i.enumerable=!0),i}},eh=function(e){var t=X(m(e)),n=[];return Y(t,function(e){l(Q,e)||l(I,e)||n.push(e)}),n},ep=function(e){var t=e===W,n=X(t?ee:m(e)),r=[];return Y(n,function(e){l(Q,e)&&(!t||l(W,e))&&r.push(Q[e])}),r};if(u||(A((K=function(){if(this instanceof K)throw TypeError("Symbol is not a constructor");var e=arguments.length&&void 0!==arguments[0]?v(arguments[0]):void 0,t=D(e),n=function(e){this===W&&n.call(ee,e),l(this,B)&&l(this[B],t)&&(this[B][t]=!1),eo(this,t,y(1,e))};return s&&ea&&eo(W,t,{configurable:!0,set:n}),es(t,e)})[H],"toString",function(){return G(this).tag}),A(K,"withoutSetter",function(e){return es(D(e),e)}),M.f=ef,T.f=eu,x.f=ed,E.f=S.f=eh,k.f=ep,P.f=function(e){return es(N(e),e)},s&&(Z(K[H],"description",{configurable:!0,get:function(){return G(this).description}}),o||A(W,"propertyIsEnumerable",ef,{unsafe:!0}))),r({global:!0,wrap:!0,forced:!u,sham:!u},{Symbol:K}),Y(_(er),function(e){R(e)}),r({target:U,stat:!0,forced:!u},{for:function(e){var t=v(e);if(l(et,t))return et[t];var n=K(t);return et[t]=n,en[n]=t,n},keyFor:function(e){if(!h(e))throw TypeError(e+" is not a symbol");if(l(en,e))return en[e]},useSetter:function(){ea=!0},useSimple:function(){ea=!1}}),r({target:"Object",stat:!0,forced:!u,sham:!s},{create:el,defineProperty:eu,defineProperties:ec,getOwnPropertyDescriptor:ed}),r({target:"Object",stat:!0,forced:!u},{getOwnPropertyNames:eh,getOwnPropertySymbols:ep}),r({target:"Object",stat:!0,forced:c(function(){k.f(1)})},{getOwnPropertySymbols:function(e){return k.f(b(e))}}),V){var eb=!u||c(function(){var e=K();return"[null]"!=V([e])||"{}"!=V({a:e})||"{}"!=V(Object(e))});r({target:"JSON",stat:!0,forced:eb},{stringify:function(e,t,n){for(var r,i=[e],a=1;arguments.length>a;)i.push(arguments[a++]);if(r=t,!(!d(t)&&void 0===e||h(e)))return f(t)||(t=function(e,t){if("function"==typeof r&&(t=r.call(this,e,t)),!h(t))return t}),i[1]=t,V.apply(null,i)}})}K[H][$]||O(K[H],$,K[H].valueOf),j(K,U),I[B]=!0},16066(e,t,n){n(97235)("matchAll")},69007(e,t,n){n(97235)("match")},83510(e,t,n){n(97235)("replace")},41840(e,t,n){n(97235)("search")},6982(e,t,n){n(97235)("species")},32159(e,t,n){n(97235)("split")},96649(e,t,n){n(97235)("toPrimitive")},39341(e,t,n){n(97235)("toStringTag")},60543(e,t,n){n(97235)("unscopables")},48675(e,t,n){"use strict";var r=n(90260),i=n(17466),a=n(99958),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("at",function(e){var t=o(this),n=i(t.length),r=a(e),s=r>=0?r:n+r;return s<0||s>=n?void 0:t[s]})},92990(e,t,n){"use strict";var r=n(90260),i=n(1048),a=r.aTypedArray;(0,r.exportTypedArrayMethod)("copyWithin",function(e,t){return i.call(a(this),e,t,arguments.length>2?arguments[2]:void 0)})},18927(e,t,n){"use strict";var r=n(90260),i=n(42092).every,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("every",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},33105(e,t,n){"use strict";var r=n(90260),i=n(21285),a=r.aTypedArray;(0,r.exportTypedArrayMethod)("fill",function(e){return i.apply(a(this),arguments)})},35035(e,t,n){"use strict";var r=n(90260),i=n(42092).filter,a=n(43074),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("filter",function(e){var t=i(o(this),e,arguments.length>1?arguments[1]:void 0);return a(this,t)})},7174(e,t,n){"use strict";var r=n(90260),i=n(42092).findIndex,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("findIndex",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},74345(e,t,n){"use strict";var r=n(90260),i=n(42092).find,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("find",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},44197(e,t,n){n(19843)("Float32",function(e){return function(t,n,r){return e(this,t,n,r)}})},76495(e,t,n){n(19843)("Float64",function(e){return function(t,n,r){return e(this,t,n,r)}})},32846(e,t,n){"use strict";var r=n(90260),i=n(42092).forEach,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("forEach",function(e){i(a(this),e,arguments.length>1?arguments[1]:void 0)})},98145(e,t,n){"use strict";var r=n(63832),i=n(90260).exportTypedArrayStaticMethod,a=n(97321);i("from",a,r)},44731(e,t,n){"use strict";var r=n(90260),i=n(41318).includes,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("includes",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},77209(e,t,n){"use strict";var r=n(90260),i=n(41318).indexOf,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("indexOf",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},35109(e,t,n){n(19843)("Int16",function(e){return function(t,n,r){return e(this,t,n,r)}})},65125(e,t,n){n(19843)("Int32",function(e){return function(t,n,r){return e(this,t,n,r)}})},87145(e,t,n){n(19843)("Int8",function(e){return function(t,n,r){return e(this,t,n,r)}})},96319(e,t,n){"use strict";var r=n(17854),i=n(90260),a=n(66992),o=n(5112)("iterator"),s=r.Uint8Array,u=a.values,c=a.keys,l=a.entries,f=i.aTypedArray,d=i.exportTypedArrayMethod,h=s&&s.prototype[o],p=!!h&&("values"==h.name||void 0==h.name),b=function(){return u.call(f(this))};d("entries",function(){return l.call(f(this))}),d("keys",function(){return c.call(f(this))}),d("values",b,!p),d(o,b,!p)},58867(e,t,n){"use strict";var r=n(90260),i=r.aTypedArray,a=r.exportTypedArrayMethod,o=[].join;a("join",function(e){return o.apply(i(this),arguments)})},37789(e,t,n){"use strict";var r=n(90260),i=n(86583),a=r.aTypedArray;(0,r.exportTypedArrayMethod)("lastIndexOf",function(e){return i.apply(a(this),arguments)})},33739(e,t,n){"use strict";var r=n(90260),i=n(42092).map,a=n(66304),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("map",function(e){return i(o(this),e,arguments.length>1?arguments[1]:void 0,function(e,t){return new(a(e))(t)})})},95206(e,t,n){"use strict";var r=n(90260),i=n(63832),a=r.aTypedArrayConstructor;(0,r.exportTypedArrayStaticMethod)("of",function(){for(var e=0,t=arguments.length,n=new(a(this))(t);t>e;)n[e]=arguments[e++];return n},i)},14483(e,t,n){"use strict";var r=n(90260),i=n(53671).right,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("reduceRight",function(e){return i(a(this),e,arguments.length,arguments.length>1?arguments[1]:void 0)})},29368(e,t,n){"use strict";var r=n(90260),i=n(53671).left,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("reduce",function(e){return i(a(this),e,arguments.length,arguments.length>1?arguments[1]:void 0)})},12056(e,t,n){"use strict";var r=n(90260),i=r.aTypedArray,a=r.exportTypedArrayMethod,o=Math.floor;a("reverse",function(){for(var e,t=this,n=i(t).length,r=o(n/2),a=0;a1?arguments[1]:void 0,1),n=this.length,r=o(e),s=i(r.length),c=0;if(s+t>n)throw RangeError("Wrong length");for(;ca;)c[a]=n[a++];return c},c)},27462(e,t,n){"use strict";var r=n(90260),i=n(42092).some,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("some",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},33824(e,t,n){"use strict";var r=n(90260),i=n(17854),a=n(47293),o=n(13099),s=n(17466),u=n(94362),c=n(68886),l=n(30256),f=n(7392),d=n(98008),h=r.aTypedArray,p=r.exportTypedArrayMethod,b=i.Uint16Array,m=b&&b.prototype.sort,g=!!m&&!a(function(){var e=new b(2);e.sort(null),e.sort({})}),v=!!m&&!a(function(){if(f)return f<74;if(c)return c<67;if(l)return!0;if(d)return d<602;var e,t,n=new b(516),r=Array(516);for(e=0;e<516;e++)t=e%4,n[e]=515-e,r[e]=e-2*t+3;for(n.sort(function(e,t){return(e/4|0)-(t/4|0)}),e=0;e<516;e++)if(n[e]!==r[e])return!0}),y=function(e){return function(t,n){return void 0!==e?+e(t,n)||0:n!=n?-1:t!=t?1:0===t&&0===n?1/t>0&&1/n<0?1:-1:t>n}};p("sort",function(e){var t,n=this;if(void 0!==e&&o(e),v)return m.call(n,e);h(n);var r=s(n.length),i=Array(r);for(t=0;t1?arguments[1]:void 0)}}),a("filterOut")},34286(e,t,n){"use strict";var r=n(82109),i=n(42092).filterReject,a=n(51223);r({target:"Array",proto:!0},{filterReject:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}}),a("filterReject")},77461(e,t,n){"use strict";var r=n(82109),i=n(9671).findLastIndex,a=n(51223);r({target:"Array",proto:!0},{findLastIndex:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}}),a("findLastIndex")},3048(e,t,n){"use strict";var r=n(82109),i=n(9671).findLast,a=n(51223);r({target:"Array",proto:!0},{findLast:function(e){return i(this,e,arguments.length>1?arguments[1]:void 0)}}),a("findLast")},1999(e,t,n){"use strict";var r=n(82109),i=n(61386),a=n(77475),o=n(51223);r({target:"Array",proto:!0},{groupBy:function(e){var t=arguments.length>1?arguments[1]:void 0;return i(this,e,t,a)}}),o("groupBy")},8e4(e,t,n){var r=n(82109),i=n(43157),a=Object.isFrozen,o=function(e,t){if(!a||!i(e)||!a(e))return!1;for(var n,r=0,o=e.length;r1?arguments[1]:void 0,3);return!u(n,function(e,n,i){if(!r(n,e,t))return i()},{AS_ENTRIES:!0,IS_ITERATOR:!0,INTERRUPTED:!0}).stopped}})},71957(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(49974),c=n(36707),l=n(54647),f=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{filter:function(e){var t=o(this),n=l(t),r=u(e,arguments.length>1?arguments[1]:void 0,3),i=new(c(t,a("Map"))),d=s(i.set);return f(n,function(e,n){r(n,e,t)&&d.call(i,e,n)},{AS_ENTRIES:!0,IS_ITERATOR:!0}),i}})},103(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(49974),s=n(54647),u=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{findKey:function(e){var t=a(this),n=s(t),r=o(e,arguments.length>1?arguments[1]:void 0,3);return u(n,function(e,n,i){if(r(n,e,t))return i(e)},{AS_ENTRIES:!0,IS_ITERATOR:!0,INTERRUPTED:!0}).result}})},96306(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(49974),s=n(54647),u=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{find:function(e){var t=a(this),n=s(t),r=o(e,arguments.length>1?arguments[1]:void 0,3);return u(n,function(e,n,i){if(r(n,e,t))return i(n)},{AS_ENTRIES:!0,IS_ITERATOR:!0,INTERRUPTED:!0}).result}})},8582(e,t,n){var r=n(82109),i=n(27296);r({target:"Map",stat:!0},{from:i})},90618(e,t,n){"use strict";var r=n(82109),i=n(20408),a=n(13099);r({target:"Map",stat:!0},{groupBy:function(e,t){var n=new this;a(t);var r=a(n.has),o=a(n.get),s=a(n.set);return i(e,function(e){var i=t(e);r.call(n,i)?o.call(n,i).push(e):s.call(n,i,[e])}),n}})},74592(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(54647),s=n(46465),u=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{includes:function(e){return u(o(a(this)),function(t,n,r){if(s(n,e))return r()},{AS_ENTRIES:!0,IS_ITERATOR:!0,INTERRUPTED:!0}).stopped}})},88440(e,t,n){"use strict";var r=n(82109),i=n(20408),a=n(13099);r({target:"Map",stat:!0},{keyBy:function(e,t){var n=new this;a(t);var r=a(n.set);return i(e,function(e){r.call(n,t(e),e)}),n}})},58276(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(54647),s=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{keyOf:function(e){return s(o(a(this)),function(t,n,r){if(n===e)return r(t)},{AS_ENTRIES:!0,IS_ITERATOR:!0,INTERRUPTED:!0}).result}})},35082(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(49974),c=n(36707),l=n(54647),f=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{mapKeys:function(e){var t=o(this),n=l(t),r=u(e,arguments.length>1?arguments[1]:void 0,3),i=new(c(t,a("Map"))),d=s(i.set);return f(n,function(e,n){d.call(i,r(n,e,t),n)},{AS_ENTRIES:!0,IS_ITERATOR:!0}),i}})},12813(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(49974),c=n(36707),l=n(54647),f=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{mapValues:function(e){var t=o(this),n=l(t),r=u(e,arguments.length>1?arguments[1]:void 0,3),i=new(c(t,a("Map"))),d=s(i.set);return f(n,function(e,n){d.call(i,e,r(n,e,t))},{AS_ENTRIES:!0,IS_ITERATOR:!0}),i}})},18222(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(13099),s=n(20408);r({target:"Map",proto:!0,real:!0,forced:i},{merge:function(e){for(var t=a(this),n=o(t.set),r=arguments.length,i=0;i1?arguments[1]:void 0,3);return u(n,function(e,n,i){if(r(n,e,t))return i()},{AS_ENTRIES:!0,IS_ITERATOR:!0,INTERRUPTED:!0}).stopped}})},74442(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(8154);r({target:"Map",proto:!0,real:!0,forced:i},{updateOrInsert:a})},7512(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(13099);r({target:"Map",proto:!0,real:!0,forced:i},{update:function(e,t){var n=a(this),r=arguments.length;o(t);var i=n.has(e);if(!i&&r<3)throw TypeError("Updating absent value");var s=i?n.get(e):o(r>2?arguments[2]:void 0)(e,n);return n.set(e,t(s,e,n)),n}})},87713(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(8154);r({target:"Map",proto:!0,real:!0,forced:i},{upsert:a})},46603(e,t,n){var r=n(82109),i=Math.min,a=Math.max;r({target:"Math",stat:!0},{clamp:function(e,t,n){return i(n,a(t,e))}})},70100(e,t,n){n(82109)({target:"Math",stat:!0},{DEG_PER_RAD:Math.PI/180})},26429(e,t,n){var r=n(82109),i=180/Math.PI;r({target:"Math",stat:!0},{degrees:function(e){return e*i}})},13187(e,t,n){var r=n(82109),i=n(47103),a=n(26130);r({target:"Math",stat:!0},{fscale:function(e,t,n,r,o){return a(i(e,t,n,r,o))}})},60092(e,t,n){n(82109)({target:"Math",stat:!0},{iaddh:function(e,t,n,r){var i=e>>>0,a=n>>>0;return(t>>>0)+(r>>>0)+((i&a|(i|a)&~(i+a>>>0))>>>31)|0}})},19041(e,t,n){n(82109)({target:"Math",stat:!0},{imulh:function(e,t){var n=65535,r=+e,i=+t,a=r&n,o=i&n,s=r>>16,u=i>>16,c=(s*o>>>0)+(a*o>>>16);return s*u+(c>>16)+((a*u>>>0)+(c&n)>>16)}})},30666(e,t,n){n(82109)({target:"Math",stat:!0},{isubh:function(e,t,n,r){var i=e>>>0,a=n>>>0;return(t>>>0)-(r>>>0)-((~i&a|~(i^a)&i-a>>>0)>>>31)|0}})},51638(e,t,n){n(82109)({target:"Math",stat:!0},{RAD_PER_DEG:180/Math.PI})},62975(e,t,n){var r=n(82109),i=Math.PI/180;r({target:"Math",stat:!0},{radians:function(e){return e*i}})},15728(e,t,n){var r=n(82109),i=n(47103);r({target:"Math",stat:!0},{scale:i})},46056(e,t,n){var r=n(82109),i=n(19670),a=n(77023),o=n(24994),s=n(29909),u="Seeded Random",c=u+" Generator",l=s.set,f=s.getterFor(c),d='Math.seededPRNG() argument should have a "seed" field with a finite value.',h=o(function(e){l(this,{type:c,seed:e%2147483647})},u,function(){var e=f(this);return{value:(1073741823&(e.seed=(1103515245*e.seed+12345)%2147483647))/1073741823,done:!1}});r({target:"Math",stat:!0,forced:!0},{seededPRNG:function(e){var t=i(e).seed;if(!a(t))throw TypeError(d);return new h(t)}})},44299(e,t,n){n(82109)({target:"Math",stat:!0},{signbit:function(e){return(e=+e)==e&&0==e?1/e==-1/0:e<0}})},5162(e,t,n){n(82109)({target:"Math",stat:!0},{umulh:function(e,t){var n=65535,r=+e,i=+t,a=r&n,o=i&n,s=r>>>16,u=i>>>16,c=(s*o>>>0)+(a*o>>>16);return s*u+(c>>>16)+((a*u>>>0)+(c&n)>>>16)}})},50292(e,t,n){"use strict";var r=n(82109),i=n(99958),a=n(83009),o="Invalid number representation",s="Invalid radix",u=/^[\da-z]+$/;r({target:"Number",stat:!0},{fromString:function(e,t){var n,r,c=1;if("string"!=typeof e)throw TypeError(o);if(!e.length||"-"==e.charAt(0)&&(c=-1,!(e=e.slice(1)).length))throw SyntaxError(o);if((n=void 0===t?10:i(t))<2||n>36)throw RangeError(s);if(!u.test(e)||(r=a(e,n)).toString(n)!==e)throw SyntaxError(o);return c*r}})},29427(e,t,n){"use strict";var r=n(82109),i=n(80430);r({target:"Number",stat:!0},{range:function(e,t,n){return new i(e,t,n,"number",0,1)}})},96936(e,t,n){n(46314)},99964(e,t,n){"use strict";var r=n(82109),i=n(60996);r({target:"Object",stat:!0},{iterateEntries:function(e){return new i(e,"entries")}})},75238(e,t,n){"use strict";var r=n(82109),i=n(60996);r({target:"Object",stat:!0},{iterateKeys:function(e){return new i(e,"keys")}})},4987(e,t,n){"use strict";var r=n(82109),i=n(60996);r({target:"Object",stat:!0},{iterateValues:function(e){return new i(e,"values")}})},1025(e,t,n){"use strict";var r=n(82109),i=n(19781),a=n(96340),o=n(13099),s=n(19670),u=n(70111),c=n(25787),l=n(3070).f,f=n(68880),d=n(12248),h=n(18554),p=n(58173),b=n(20408),m=n(842),g=n(5112),v=n(29909),y=g("observable"),w=v.get,_=v.set,E=function(e){var t=e.cleanup;if(t){e.cleanup=void 0;try{t()}catch(n){m(n)}}},S=function(e){return void 0===e.observer},k=function(e){var t=e.facade;if(!i){t.closed=!0;var n=e.subscriptionObserver;n&&(n.closed=!0)}e.observer=void 0},x=function(e,t){var n,r=_(this,{cleanup:void 0,observer:s(e),subscriptionObserver:void 0});i||(this.closed=!1);try{(n=p(e.start))&&n.call(e,this)}catch(a){m(a)}if(!S(r)){var u=r.subscriptionObserver=new T(this);try{var c=t(u),l=c;null!=c&&(r.cleanup="function"==typeof c.unsubscribe?function(){l.unsubscribe()}:o(c))}catch(f){u.error(f);return}S(r)&&E(r)}};x.prototype=d({},{unsubscribe:function(){var e=w(this);S(e)||(k(e),E(e))}}),i&&l(x.prototype,"closed",{configurable:!0,get:function(){return S(w(this))}});var T=function(e){_(this,{subscription:e}),i||(this.closed=!1)};T.prototype=d({},{next:function(e){var t=w(w(this).subscription);if(!S(t)){var n=t.observer;try{var r=p(n.next);r&&r.call(n,e)}catch(i){m(i)}}},error:function(e){var t=w(w(this).subscription);if(!S(t)){var n=t.observer;k(t);try{var r=p(n.error);r?r.call(n,e):m(e)}catch(i){m(i)}E(t)}},complete:function(){var e=w(w(this).subscription);if(!S(e)){var t=e.observer;k(e);try{var n=p(t.complete);n&&n.call(t)}catch(r){m(r)}E(e)}}}),i&&l(T.prototype,"closed",{configurable:!0,get:function(){return S(w(w(this).subscription))}});var M=function(e){c(this,M,"Observable"),_(this,{subscriber:o(e)})};d(M.prototype,{subscribe:function(e){var t=arguments.length;return new x("function"==typeof e?{next:e,error:t>1?arguments[1]:void 0,complete:t>2?arguments[2]:void 0}:u(e)?e:{},w(this).subscriber)}}),d(M,{from:function(e){var t="function"==typeof this?this:M,n=p(s(e)[y]);if(n){var r=s(n.call(e));return r.constructor===t?r:new t(function(e){return r.subscribe(e)})}var i=h(e);return new t(function(e){b(i,function(t,n){if(e.next(t),e.closed)return n()},{IS_ITERATOR:!0,INTERRUPTED:!0}),e.complete()})},of:function(){for(var e="function"==typeof this?this:M,t=arguments.length,n=Array(t),r=0;r1?arguments[1]:void 0,3);return!u(n,function(e,n){if(!r(e,e,t))return n()},{IS_ITERATOR:!0,INTERRUPTED:!0}).stopped}})},64362(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(49974),c=n(36707),l=n(96767),f=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{filter:function(e){var t=o(this),n=l(t),r=u(e,arguments.length>1?arguments[1]:void 0,3),i=new(c(t,a("Set"))),d=s(i.add);return f(n,function(e){r(e,e,t)&&d.call(i,e)},{IS_ITERATOR:!0}),i}})},15389(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(49974),s=n(96767),u=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{find:function(e){var t=a(this),n=s(t),r=o(e,arguments.length>1?arguments[1]:void 0,3);return u(n,function(e,n){if(r(e,e,t))return n(e)},{IS_ITERATOR:!0,INTERRUPTED:!0}).result}})},46006(e,t,n){var r=n(82109),i=n(27296);r({target:"Set",stat:!0},{from:i})},90401(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(36707),c=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{intersection:function(e){var t=o(this),n=new(u(t,a("Set"))),r=s(t.has),i=s(n.add);return c(e,function(e){r.call(t,e)&&i.call(n,e)}),n}})},45164(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(13099),s=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{isDisjointFrom:function(e){var t=a(this),n=o(t.has);return!s(e,function(e,r){if(!0===n.call(t,e))return r()},{INTERRUPTED:!0}).stopped}})},91238(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(18554),c=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{isSubsetOf:function(e){var t=u(this),n=o(e),r=n.has;return"function"!=typeof r&&(n=new(a("Set"))(e),r=s(n.has)),!c(t,function(e,t){if(!1===r.call(n,e))return t()},{IS_ITERATOR:!0,INTERRUPTED:!0}).stopped}})},54837(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(13099),s=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{isSupersetOf:function(e){var t=a(this),n=o(t.has);return!s(e,function(e,r){if(!1===n.call(t,e))return r()},{INTERRUPTED:!0}).stopped}})},87485(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(96767),s=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{join:function(e){var t=a(this),n=o(t),r=void 0===e?",":String(e),i=[];return s(n,i.push,{that:i,IS_ITERATOR:!0}),i.join(r)}})},56767(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(49974),c=n(36707),l=n(96767),f=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{map:function(e){var t=o(this),n=l(t),r=u(e,arguments.length>1?arguments[1]:void 0,3),i=new(c(t,a("Set"))),d=s(i.add);return f(n,function(e){d.call(i,r(e,e,t))},{IS_ITERATOR:!0}),i}})},69916(e,t,n){var r=n(82109),i=n(82044);r({target:"Set",stat:!0},{of:i})},76651(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(13099),s=n(96767),u=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{reduce:function(e){var t=a(this),n=s(t),r=arguments.length<2,i=r?void 0:arguments[1];if(o(e),u(n,function(n){r?(r=!1,i=n):i=e(i,n,n,t)},{IS_ITERATOR:!0}),r)throw TypeError("Reduce of empty set with no initial value");return i}})},61437(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(19670),o=n(49974),s=n(96767),u=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{some:function(e){var t=a(this),n=s(t),r=o(e,arguments.length>1?arguments[1]:void 0,3);return u(n,function(e,n){if(r(e,e,t))return n()},{IS_ITERATOR:!0,INTERRUPTED:!0}).stopped}})},35285(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(36707),c=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{symmetricDifference:function(e){var t=o(this),n=new(u(t,a("Set")))(t),r=s(n.delete),i=s(n.add);return c(e,function(e){r.call(n,e)||i.call(n,e)}),n}})},39865(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(35005),o=n(19670),s=n(13099),u=n(36707),c=n(20408);r({target:"Set",proto:!0,real:!0,forced:i},{union:function(e){var t=o(this),n=new(u(t,a("Set")))(t);return c(e,s(n.add),{that:n}),n}})},86035(e,t,n){"use strict";var r=n(82109),i=n(28710).charAt,a=n(47293)(function(){return"𠮷"!=="𠮷".at(0)});r({target:"String",proto:!0,forced:a},{at:function(e){return i(this,e)}})},67501(e,t,n){"use strict";var r=n(82109),i=n(24994),a=n(84488),o=n(41340),s=n(29909),u=n(28710),c=u.codeAt,l=u.charAt,f="String Iterator",d=s.set,h=s.getterFor(f),p=i(function(e){d(this,{type:f,string:e,index:0})},"String",function(){var e,t=h(this),n=t.string,r=t.index;return r>=n.length?{value:void 0,done:!0}:(e=l(n,r),t.index+=e.length,{value:{codePoint:c(e,0),position:r},done:!1})});r({target:"String",proto:!0},{codePoints:function(){return new p(o(a(this)))}})},13728(e,t,n){n(76373)},27207(e,t,n){n(68757)},609(e,t,n){n(97235)("asyncDispose")},21568(e,t,n){n(97235)("dispose")},54534(e,t,n){n(97235)("matcher")},95090(e,t,n){n(97235)("metadata")},48824(e,t,n){n(97235)("observable")},44130(e,t,n){n(97235)("patternMatch")},35954(e,t,n){n(97235)("replaceAll")},38012(e,t,n){n(48675)},26182(e,t,n){"use strict";var r=n(90260),i=n(42092).filterReject,a=n(43074),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("filterOut",function(e){var t=i(o(this),e,arguments.length>1?arguments[1]:void 0);return a(this,t)})},8922(e,t,n){"use strict";var r=n(90260),i=n(42092).filterReject,a=n(43074),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("filterReject",function(e){var t=i(o(this),e,arguments.length>1?arguments[1]:void 0);return a(this,t)})},1118(e,t,n){"use strict";var r=n(90260),i=n(9671).findLastIndex,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("findLastIndex",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},37380(e,t,n){"use strict";var r=n(90260),i=n(9671).findLast,a=r.aTypedArray;(0,r.exportTypedArrayMethod)("findLast",function(e){return i(a(this),e,arguments.length>1?arguments[1]:void 0)})},5835(e,t,n){"use strict";var r=n(90260),i=n(61386),a=n(66304),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("groupBy",function(e){var t=arguments.length>1?arguments[1]:void 0;return i(o(this),e,t,a)})},84444(e,t,n){"use strict";var r=n(90260),i=n(60956),a=n(43074),o=r.aTypedArray;(0,r.exportTypedArrayMethod)("uniqueBy",function(e){return a(this,i.call(o(this),e))})},78206(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(34092);r({target:"WeakMap",proto:!0,real:!0,forced:i},{deleteAll:function(){return a.apply(this,arguments)}})},12714(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(37502);r({target:"WeakMap",proto:!0,real:!0,forced:i},{emplace:a})},76478(e,t,n){var r=n(82109),i=n(27296);r({target:"WeakMap",stat:!0},{from:i})},79715(e,t,n){var r=n(82109),i=n(82044);r({target:"WeakMap",stat:!0},{of:i})},5964(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(8154);r({target:"WeakMap",proto:!0,real:!0,forced:i},{upsert:a})},43561(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(31501);r({target:"WeakSet",proto:!0,real:!0,forced:i},{addAll:function(){return a.apply(this,arguments)}})},32049(e,t,n){"use strict";var r=n(82109),i=n(31913),a=n(34092);r({target:"WeakSet",proto:!0,real:!0,forced:i},{deleteAll:function(){return a.apply(this,arguments)}})},86020(e,t,n){var r=n(82109),i=n(27296);r({target:"WeakSet",stat:!0},{from:i})},56585(e,t,n){var r=n(82109),i=n(82044);r({target:"WeakSet",stat:!0},{of:i})},54747(e,t,n){var r=n(17854),i=n(48324),a=n(18533),o=n(68880);for(var s in i){var u=r[s],c=u&&u.prototype;if(c&&c.forEach!==a)try{o(c,"forEach",a)}catch(l){c.forEach=a}}},33948(e,t,n){var r=n(17854),i=n(48324),a=n(66992),o=n(68880),s=n(5112),u=s("iterator"),c=s("toStringTag"),l=a.values;for(var f in i){var d=r[f],h=d&&d.prototype;if(h){if(h[u]!==l)try{o(h,u,l)}catch(p){h[u]=l}if(h[c]||o(h,c,f),i[f]){for(var b in a)if(h[b]!==a[b])try{o(h,b,a[b])}catch(m){h[b]=a[b]}}}}},84633(e,t,n){var r=n(82109),i=n(17854),a=n(20261);r({global:!0,bind:!0,enumerable:!0,forced:!i.setImmediate||!i.clearImmediate},{setImmediate:a.set,clearImmediate:a.clear})},85844(e,t,n){var r=n(82109),i=n(17854),a=n(95948),o=n(35268),s=i.process;r({global:!0,enumerable:!0,noTargetGet:!0},{queueMicrotask:function(e){var t=o&&s.domain;a(t?t.bind(e):e)}})},32564(e,t,n){var r=n(82109),i=n(17854),a=n(88113),o=[].slice,s=/MSIE .\./.test(a),u=function(e){return function(t,n){var r=arguments.length>2,i=r?o.call(arguments,2):void 0;return e(r?function(){("function"==typeof t?t:Function(t)).apply(this,i)}:t,n)}};r({global:!0,bind:!0,forced:s},{setTimeout:u(i.setTimeout),setInterval:u(i.setInterval)})},41637(e,t,n){"use strict";n(66992);var r=n(82109),i=n(35005),a=n(590),o=n(31320),s=n(12248),u=n(58003),c=n(24994),l=n(29909),f=n(25787),d=n(86656),h=n(49974),p=n(70648),b=n(19670),m=n(70111),g=n(41340),v=n(70030),y=n(79114),w=n(18554),_=n(71246),E=n(5112),S=i("fetch"),k=i("Request"),x=k&&k.prototype,T=i("Headers"),M=E("iterator"),O="URLSearchParams",A=O+"Iterator",L=l.set,C=l.getterFor(O),I=l.getterFor(A),D=/\+/g,N=[,,,,],P=function(e){return N[e-1]||(N[e-1]=RegExp("((?:%[\\da-f]{2}){"+e+"})","gi"))},R=function(e){try{return decodeURIComponent(e)}catch(t){return e}},j=function(e){var t=e.replace(D," "),n=4;try{return decodeURIComponent(t)}catch(r){for(;n;)t=t.replace(P(n--),R);return t}},F=/[!'()~]|%20/g,Y={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"},B=function(e){return Y[e]},U=function(e){return encodeURIComponent(e).replace(F,B)},H=function(e,t){if(t)for(var n,r,i=t.split("&"),a=0;a0?arguments[0]:void 0,l=this,h=[];if(L(l,{type:O,entries:h,updateURL:function(){},updateSearchParams:$}),void 0!==c){if(m(c)){if("function"==typeof(e=_(c)))for(n=(t=w(c,e)).next;!(r=n.call(t)).done;){if((o=(a=(i=w(b(r.value))).next).call(i)).done||(s=a.call(i)).done||!a.call(i).done)throw TypeError("Expected sequence with length 2");h.push({key:g(o.value),value:g(s.value)})}else for(u in c)d(c,u)&&h.push({key:u,value:g(c[u])})}else H(h,"string"==typeof c?"?"===c.charAt(0)?c.slice(1):c:g(c))}},K=W.prototype;if(s(K,{append:function(e,t){z(arguments.length,2);var n=C(this);n.entries.push({key:g(e),value:g(t)}),n.updateURL()},delete:function(e){z(arguments.length,1);for(var t=C(this),n=t.entries,r=g(e),i=0;ie.key){i.splice(t,0,e);break}t===n&&i.push(e)}r.updateURL()},forEach:function(e){for(var t,n=C(this).entries,r=h(e,arguments.length>1?arguments[1]:void 0,3),i=0;i1?V(arguments[1]):{})}}),"function"==typeof k){var q=function(e){return f(this,q,"Request"),new k(e,arguments.length>1?V(arguments[1]):{})};x.constructor=q,q.prototype=x,r({global:!0,forced:!0},{Request:q})}}e.exports={URLSearchParams:W,getState:C}},60285(e,t,n){"use strict";n(78783);var r,i=n(82109),a=n(19781),o=n(590),s=n(17854),u=n(36048),c=n(31320),l=n(25787),f=n(86656),d=n(21574),h=n(48457),p=n(28710).codeAt,b=n(33197),m=n(41340),g=n(58003),v=n(41637),y=n(29909),w=s.URL,_=v.URLSearchParams,E=v.getState,S=y.set,k=y.getterFor("URL"),x=Math.floor,T=Math.pow,M="Invalid authority",O="Invalid scheme",A="Invalid host",L="Invalid port",C=/[A-Za-z]/,I=/[\d+-.A-Za-z]/,D=/\d/,N=/^0x/i,P=/^[0-7]+$/,R=/^\d+$/,j=/^[\dA-Fa-f]+$/,F=/[\0\t\n\r #%/:<>?@[\\\]^|]/,Y=/[\0\t\n\r #/:<>?@[\\\]^|]/,B=/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g,U=/[\t\n\r]/g,H=function(e,t){var n,r,i;if("["==t.charAt(0)){if("]"!=t.charAt(t.length-1)||!(n=z(t.slice(1,-1))))return A;e.host=n}else if(Q(e)){if(t=b(t),F.test(t)||null===(n=$(t)))return A;e.host=n}else{if(Y.test(t))return A;for(i=0,n="",r=h(t);i4)return e;for(r=0,n=[];r1&&"0"==i.charAt(0)&&(a=N.test(i)?16:8,i=i.slice(8==a?1:2)),""===i)o=0;else{if(!(10==a?R:8==a?P:j).test(i))return e;o=parseInt(i,a)}n.push(o)}for(r=0;r=T(256,5-t))return null}else if(o>255)return null;for(r=0,s=n.pop();r6))return;for(r=0;d();){if(i=null,r>0){if("."!=d()||!(r<4))return;f++}if(!D.test(d()))return;for(;D.test(d());){if(a=parseInt(d(),10),null===i)i=a;else{if(0==i)return;i=10*i+a}if(i>255)return;f++}u[c]=256*u[c]+i,(2==++r||4==r)&&c++}if(4!=r)return;break}if(":"==d()){if(f++,!d())return}else if(d())return;u[c++]=t}if(null!==l)for(o=c-l,c=7;0!=c&&o>0;)s=u[c],u[c--]=u[l+o-1],u[l+--o]=s;else if(8!=c)return;return u},G=function(e){for(var t=null,n=1,r=null,i=0,a=0;a<8;a++)0!==e[a]?(i>n&&(t=r,n=i),r=null,i=0):(null===r&&(r=a),++i);return i>n&&(t=r,n=i),t},W=function(e){var t,n,r,i;if("number"==typeof e){for(n=0,t=[];n<4;n++)t.unshift(e%256),e=x(e/256);return t.join(".")}if("object"==typeof e){for(n=0,t="",r=G(e);n<8;n++)(!i||0!==e[n])&&(i&&(i=!1),r===n?(t+=n?":":"::",i=!0):(t+=e[n].toString(16),n<7&&(t+=":")));return"["+t+"]"}return e},K={},V=d({},K,{" ":1,'"':1,"<":1,">":1,"`":1}),q=d({},V,{"#":1,"?":1,"{":1,"}":1}),Z=d({},q,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),X=function(e,t){var n=p(e,0);return n>32&&n<127&&!f(t,e)?e:encodeURIComponent(e)},J={ftp:21,file:null,http:80,https:443,ws:80,wss:443},Q=function(e){return f(J,e.scheme)},ee=function(e){return""!=e.username||""!=e.password},et=function(e){return!e.host||e.cannotBeABaseURL||"file"==e.scheme},en=function(e,t){var n;return 2==e.length&&C.test(e.charAt(0))&&(":"==(n=e.charAt(1))||!t&&"|"==n)},er=function(e){var t;return e.length>1&&en(e.slice(0,2))&&(2==e.length||"/"===(t=e.charAt(2))||"\\"===t||"?"===t||"#"===t)},ei=function(e){var t=e.path,n=t.length;n&&("file"!=e.scheme||1!=n||!en(t[0],!0))&&t.pop()},ea=function(e){return"."===e||"%2e"===e.toLowerCase()},eo=function(e){return".."===(e=e.toLowerCase())||"%2e."===e||".%2e"===e||"%2e%2e"===e},es={},eu={},ec={},el={},ef={},ed={},eh={},ep={},eb={},em={},eg={},ev={},ey={},ew={},e_={},eE={},eS={},ek={},ex={},eT={},eM={},eO=function(e,t,n,i){var a,o,s,u,c=n||es,l=0,d="",p=!1,b=!1,m=!1;for(n||(e.scheme="",e.username="",e.password="",e.host=null,e.port=null,e.path=[],e.query=null,e.fragment=null,e.cannotBeABaseURL=!1,t=t.replace(B,"")),t=t.replace(U,""),a=h(t);l<=a.length;){switch(o=a[l],c){case es:if(o&&C.test(o))d+=o.toLowerCase(),c=eu;else{if(n)return O;c=ec;continue}break;case eu:if(o&&(I.test(o)||"+"==o||"-"==o||"."==o))d+=o.toLowerCase();else if(":"==o){if(n&&(Q(e)!=f(J,d)||"file"==d&&(ee(e)||null!==e.port)||"file"==e.scheme&&!e.host))return;if(e.scheme=d,n){Q(e)&&J[e.scheme]==e.port&&(e.port=null);return}d="","file"==e.scheme?c=ew:Q(e)&&i&&i.scheme==e.scheme?c=el:Q(e)?c=ep:"/"==a[l+1]?(c=ef,l++):(e.cannotBeABaseURL=!0,e.path.push(""),c=ex)}else{if(n)return O;d="",c=ec,l=0;continue}break;case ec:if(!i||i.cannotBeABaseURL&&"#"!=o)return O;if(i.cannotBeABaseURL&&"#"==o){e.scheme=i.scheme,e.path=i.path.slice(),e.query=i.query,e.fragment="",e.cannotBeABaseURL=!0,c=eM;break}c="file"==i.scheme?ew:ed;continue;case el:if("/"==o&&"/"==a[l+1])c=eb,l++;else{c=ed;continue}break;case ef:if("/"==o){c=em;break}c=ek;continue;case ed:if(e.scheme=i.scheme,o==r)e.username=i.username,e.password=i.password,e.host=i.host,e.port=i.port,e.path=i.path.slice(),e.query=i.query;else if("/"==o||"\\"==o&&Q(e))c=eh;else if("?"==o)e.username=i.username,e.password=i.password,e.host=i.host,e.port=i.port,e.path=i.path.slice(),e.query="",c=eT;else if("#"==o)e.username=i.username,e.password=i.password,e.host=i.host,e.port=i.port,e.path=i.path.slice(),e.query=i.query,e.fragment="",c=eM;else{e.username=i.username,e.password=i.password,e.host=i.host,e.port=i.port,e.path=i.path.slice(),e.path.pop(),c=ek;continue}break;case eh:if(Q(e)&&("/"==o||"\\"==o))c=eb;else if("/"==o)c=em;else{e.username=i.username,e.password=i.password,e.host=i.host,e.port=i.port,c=ek;continue}break;case ep:if(c=eb,"/"!=o||"/"!=d.charAt(l+1))continue;l++;break;case eb:if("/"!=o&&"\\"!=o){c=em;continue}break;case em:if("@"==o){p&&(d="%40"+d),p=!0,s=h(d);for(var g=0;g65535)return L;e.port=Q(e)&&w===J[e.scheme]?null:w,d=""}if(n)return;c=eS;continue}break;case ew:if(e.scheme="file","/"==o||"\\"==o)c=e_;else if(i&&"file"==i.scheme){if(o==r)e.host=i.host,e.path=i.path.slice(),e.query=i.query;else if("?"==o)e.host=i.host,e.path=i.path.slice(),e.query="",c=eT;else if("#"==o)e.host=i.host,e.path=i.path.slice(),e.query=i.query,e.fragment="",c=eM;else{er(a.slice(l).join(""))||(e.host=i.host,e.path=i.path.slice(),ei(e)),c=ek;continue}}else{c=ek;continue}break;case e_:if("/"==o||"\\"==o){c=eE;break}i&&"file"==i.scheme&&!er(a.slice(l).join(""))&&(en(i.path[0],!0)?e.path.push(i.path[0]):e.host=i.host),c=ek;continue;case eE:if(o==r||"/"==o||"\\"==o||"?"==o||"#"==o){if(!n&&en(d))c=ek;else if(""==d){if(e.host="",n)return;c=eS}else{if(u=H(e,d))return u;if("localhost"==e.host&&(e.host=""),n)return;d="",c=eS}continue}d+=o;break;case eS:if(Q(e)){if(c=ek,"/"!=o&&"\\"!=o)continue}else if(n||"?"!=o){if(n||"#"!=o){if(o!=r&&(c=ek,"/"!=o))continue}else e.fragment="",c=eM}else e.query="",c=eT;break;case ek:if(o==r||"/"==o||"\\"==o&&Q(e)||!n&&("?"==o||"#"==o)){if(eo(d)?(ei(e),"/"==o||"\\"==o&&Q(e)||e.path.push("")):ea(d)?"/"==o||"\\"==o&&Q(e)||e.path.push(""):("file"==e.scheme&&!e.path.length&&en(d)&&(e.host&&(e.host=""),d=d.charAt(0)+":"),e.path.push(d)),d="","file"==e.scheme&&(o==r||"?"==o||"#"==o))for(;e.path.length>1&&""===e.path[0];)e.path.shift();"?"==o?(e.query="",c=eT):"#"==o&&(e.fragment="",c=eM)}else d+=X(o,q);break;case ex:"?"==o?(e.query="",c=eT):"#"==o?(e.fragment="",c=eM):o!=r&&(e.path[0]+=X(o,K));break;case eT:n||"#"!=o?o!=r&&("'"==o&&Q(e)?e.query+="%27":"#"==o?e.query+="%23":e.query+=X(o,K)):(e.fragment="",c=eM);break;case eM:o!=r&&(e.fragment+=X(o,V))}l++}},eA=function(e){var t,n,r=l(this,eA,"URL"),i=arguments.length>1?arguments[1]:void 0,o=m(e),s=S(r,{type:"URL"});if(void 0!==i){if(i instanceof eA)t=k(i);else if(n=eO(t={},m(i)))throw TypeError(n)}if(n=eO(s,o,null,t))throw TypeError(n);var u=s.searchParams=new _,c=E(u);c.updateSearchParams(s.query),c.updateURL=function(){s.query=String(u)||null},a||(r.href=eC.call(r),r.origin=eI.call(r),r.protocol=eD.call(r),r.username=eN.call(r),r.password=eP.call(r),r.host=eR.call(r),r.hostname=ej.call(r),r.port=eF.call(r),r.pathname=eY.call(r),r.search=eB.call(r),r.searchParams=eU.call(r),r.hash=eH.call(r))},eL=eA.prototype,eC=function(){var e=k(this),t=e.scheme,n=e.username,r=e.password,i=e.host,a=e.port,o=e.path,s=e.query,u=e.fragment,c=t+":";return null!==i?(c+="//",ee(e)&&(c+=n+(r?":"+r:"")+"@"),c+=W(i),null!==a&&(c+=":"+a)):"file"==t&&(c+="//"),c+=e.cannotBeABaseURL?o[0]:o.length?"/"+o.join("/"):"",null!==s&&(c+="?"+s),null!==u&&(c+="#"+u),c},eI=function(){var e=k(this),t=e.scheme,n=e.port;if("blob"==t)try{return new eA(t.path[0]).origin}catch(r){return"null"}return"file"!=t&&Q(e)?t+"://"+W(e.host)+(null!==n?":"+n:""):"null"},eD=function(){return k(this).scheme+":"},eN=function(){return k(this).username},eP=function(){return k(this).password},eR=function(){var e=k(this),t=e.host,n=e.port;return null===t?"":null===n?W(t):W(t)+":"+n},ej=function(){var e=k(this).host;return null===e?"":W(e)},eF=function(){var e=k(this).port;return null===e?"":String(e)},eY=function(){var e=k(this),t=e.path;return e.cannotBeABaseURL?t[0]:t.length?"/"+t.join("/"):""},eB=function(){var e=k(this).query;return e?"?"+e:""},eU=function(){return k(this).searchParams},eH=function(){var e=k(this).fragment;return e?"#"+e:""},e$=function(e,t){return{get:e,set:t,configurable:!0,enumerable:!0}};if(a&&u(eL,{href:e$(eC,function(e){var t=k(this),n=m(e),r=eO(t,n);if(r)throw TypeError(r);E(t.searchParams).updateSearchParams(t.query)}),origin:e$(eI),protocol:e$(eD,function(e){var t=k(this);eO(t,m(e)+":",es)}),username:e$(eN,function(e){var t=k(this),n=h(m(e));if(!et(t)){t.username="";for(var r=0;rc});var r={value:function(){}};function i(){for(var e,t=0,n=arguments.length,r={};t=0&&(n=e.slice(r+1),e=e.slice(0,r)),e&&!t.hasOwnProperty(e))throw Error("unknown type: "+e);return{type:e,name:n}})}function s(e,t){for(var n,r=0,i=e.length;r0)for(var n,r,i=Array(n),a=0;am,dragDisable:()=>u.Z,dragEnable:()=>u.D});var r=n(92626),i=n(25109),a=n(43095),o=n(94017),s=n(24793),u=n(44266),c=n(34299);function l(e){return function(){return e}}function f(e,t,n,r,i,a,o,s,u,c){this.target=e,this.type=t,this.subject=n,this.identifier=r,this.active=i,this.x=a,this.y=o,this.dx=s,this.dy=u,this._=c}function d(){return!i.B.ctrlKey&&!i.B.button}function h(){return this.parentNode}function p(e){return null==e?{x:i.B.x,y:i.B.y}:e}function b(){return navigator.maxTouchPoints||"ontouchstart"in this}function m(){var e,t,n,m,g=d,v=h,y=p,w=b,_={},E=(0,r.Z)("start","drag","end"),S=0,k=0;function x(e){e.on("mousedown.drag",T).filter(w).on("touchstart.drag",A).on("touchmove.drag",L).on("touchend.drag touchcancel.drag",C).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function T(){if(!m&&g.apply(this,arguments)){var r=I("mouse",v.apply(this,arguments),a.Z,this,arguments);r&&((0,o.Z)(i.B.view).on("mousemove.drag",M,!0).on("mouseup.drag",O,!0),(0,u.Z)(i.B.view),(0,c.r)(),n=!1,e=i.B.clientX,t=i.B.clientY,r("start"))}}function M(){if((0,c.Z)(),!n){var r=i.B.clientX-e,a=i.B.clientY-t;n=r*r+a*a>k}_.mouse("drag")}function O(){(0,o.Z)(i.B.view).on("mousemove.drag mouseup.drag",null),(0,u.D)(i.B.view,n),(0,c.Z)(),_.mouse("end")}function A(){if(g.apply(this,arguments)){var e,t,n=i.B.changedTouches,r=v.apply(this,arguments),a=n.length;for(e=0;eo,Z:()=>a});var r=n(94017),i=n(34299);function a(e){var t=e.document.documentElement,n=(0,r.Z)(e).on("dragstart.drag",i.Z,!0);"onselectstart"in t?n.on("selectstart.drag",i.Z,!0):(t.__noselect=t.style.MozUserSelect,t.style.MozUserSelect="none")}function o(e,t){var n=e.document.documentElement,a=(0,r.Z)(e).on("dragstart.drag",null);t&&(a.on("click.drag",i.Z,!0),setTimeout(function(){a.on("click.drag",null)},0)),"onselectstart"in n?a.on("selectstart.drag",null):(n.style.MozUserSelect=n.__noselect,delete n.__noselect)}},34299(e,t,n){"use strict";n.d(t,{Z:()=>a,r:()=>i});var r=n(25109);function i(){r.B.stopImmediatePropagation()}function a(){r.B.preventDefault(),r.B.stopImmediatePropagation()}},9893(e,t,n){"use strict";function r(e,t){var n;function r(){var r,i,a=n.length,o=0,s=0;for(r=0;r=(a=(b+g)/2))?b=a:g=a,(l=n>=(o=(m+v)/2))?m=o:v=o,i=h,!(h=h[f=l<<1|c]))return i[f]=p,e;if(s=+e._x.call(null,h.data),u=+e._y.call(null,h.data),t===s&&n===u)return p.next=h,i?i[f]=p:e._root=p,e;do i=i?i[f]=[,,,,]:e._root=[,,,,],(c=t>=(a=(b+g)/2))?b=a:g=a,(l=n>=(o=(m+v)/2))?m=o:v=o;while((f=l<<1|c)==(d=(u>=o)<<1|s>=a))return i[d]=h,i[f]=p,e}function u(e){var t,n,r,i,a=e.length,o=Array(a),u=Array(a),c=1/0,l=1/0,f=-1/0,d=-1/0;for(n=0;nf&&(f=r),id&&(d=i));if(c>f||l>d)return this;for(this.cover(c,l).cover(f,d),n=0;ne||e>=i||r>t||t>=a;)switch(s=(th)&&!((a=u.y0)>p)&&!((o=u.x1)=v)<<1|e>=g)&&(u=b[b.length-1],b[b.length-1]=b[b.length-1-c],b[b.length-1-c]=u)}else{var y=e-+this._x.call(null,m.data),w=t-+this._y.call(null,m.data),_=y*y+w*w;if(_=(s=(p+m)/2))?p=s:m=s,(l=o>=(u=(b+g)/2))?b=u:g=u,t=h,!(h=h[f=l<<1|c]))return this;if(!h.length)break;(t[f+1&3]||t[f+2&3]||t[f+3&3])&&(n=t,d=f)}for(;h.data!==e;)if(r=h,!(h=h.next))return this;return((i=h.next)&&delete h.next,r)?(i?r.next=i:delete r.next,this):t?(i?t[f]=i:delete t[f],(h=t[0]||t[1]||t[2]||t[3])&&h===(t[3]||t[2]||t[1]||t[0])&&!h.length&&(n?n[d]=h:this._root=h),this):(this._root=i,this)}function b(e){for(var t=0,n=e.length;tr,forceCollide:()=>L,forceLink:()=>B,forceManyBody:()=>V,forceRadial:()=>q,forceSimulation:()=>K,forceX:()=>Z,forceY:()=>X});var M=k.prototype=x.prototype;function O(e){return e.x+e.vx}function A(e){return e.y+e.vy}function L(e){var t,n,r=1,o=1;function s(){for(var e,i,s,c,l,f,d,h=t.length,p=0;ps.index){var b=c-u.x-u.vx,m=l-u.y-u.vy,g=b*b+m*m;gc+p||il+p||oe.r&&(e.r=e[t].r)}function c(){if(t){var r,i,a=t.length;for(r=0,n=Array(a);r1?(null==n?s.remove(e):s.set(e,h(n)),t):s.get(e)},find:function(t,n,r){var i,a,o,s,u,c=0,l=e.length;for(null==r?r=1/0:r*=r,c=0;c1?(c.on(e,n),t):c.on(e)}}}function V(){var e,t,n,r,o=i(-30),s=1,u=1/0,c=.81;function l(r){var i,a=e.length,o=k(e,$,z).visitAfter(d);for(n=r,i=0;i=u)){(e.data!==t||e.next)&&(0===f&&(p+=(f=a())*f),0===d&&(p+=(d=a())*d),ps});var r=n(73888),i=n(31986);function a(e){return function(){var t=this.ownerDocument,n=this.namespaceURI;return n===i.P&&t.documentElement.namespaceURI===i.P?t.createElement(e):t.createElementNS(n,e)}}function o(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}function s(e){var t=(0,r.Z)(e);return(t.local?o:a)(t)}},58556(e,t,n){"use strict";n.r(t),n.d(t,{clientPoint:()=>h.Z,create:()=>a,creator:()=>r.Z,customEvent:()=>S._H,event:()=>S.B,local:()=>s,matcher:()=>c.Z,mouse:()=>l.Z,namespace:()=>f.Z,namespaces:()=>d.Z,select:()=>i.Z,selectAll:()=>b,selection:()=>p.ZP,selector:()=>m.Z,selectorAll:()=>g.Z,style:()=>v.S,touch:()=>y.Z,touches:()=>_,window:()=>E.Z});var r=n(789),i=n(94017);function a(e){return(0,i.Z)((0,r.Z)(e).call(document.documentElement))}var o=0;function s(){return new u}function u(){this._="@"+(++o).toString(36)}u.prototype=s.prototype={constructor:u,get:function(e){for(var t=this._;!(t in e);)if(!(e=e.parentNode))return;return e[t]},set:function(e,t){return e[this._]=t},remove:function(e){return this._ in e&&delete e[this._]},toString:function(){return this._}};var c=n(3083),l=n(43095),f=n(73888),d=n(31986),h=n(42115),p=n(23817);function b(e){return"string"==typeof e?new p.Y1([document.querySelectorAll(e)],[document.documentElement]):new p.Y1([null==e?[]:e],p.Jz)}var m=n(82634),g=n(3545),v=n(49986),y=n(24793),w=n(45553);function _(e,t){null==t&&(t=(0,w.Z)().touches);for(var n=0,r=t?t.length:0,i=Array(r);nr})},43095(e,t,n){"use strict";n.d(t,{Z:()=>a});var r=n(45553),i=n(42115);function a(e){var t=(0,r.Z)();return t.changedTouches&&(t=t.changedTouches[0]),(0,i.Z)(e,t)}},73888(e,t,n){"use strict";n.d(t,{Z:()=>i});var r=n(31986);function i(e){var t=e+="",n=t.indexOf(":");return n>=0&&"xmlns"!==(t=e.slice(0,n))&&(e=e.slice(n+1)),r.Z.hasOwnProperty(t)?{space:r.Z[t],local:e}:e}},31986(e,t,n){"use strict";n.d(t,{P:()=>r,Z:()=>i});var r="http://www.w3.org/1999/xhtml";let i={svg:"http://www.w3.org/2000/svg",xhtml:r,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"}},42115(e,t,n){"use strict";function r(e,t){var n=e.ownerSVGElement||e;if(n.createSVGPoint){var r=n.createSVGPoint();return r.x=t.clientX,r.y=t.clientY,[(r=r.matrixTransform(e.getScreenCTM().inverse())).x,r.y]}var i=e.getBoundingClientRect();return[t.clientX-i.left-e.clientLeft,t.clientY-i.top-e.clientTop]}n.d(t,{Z:()=>r})},94017(e,t,n){"use strict";n.d(t,{Z:()=>i});var r=n(23817);function i(e){return"string"==typeof e?new r.Y1([[document.querySelector(e)]],[document.documentElement]):new r.Y1([[e]],r.Jz)}},23817(e,t,n){"use strict";n.d(t,{Y1:()=>eT,ZP:()=>eO,Jz:()=>ex});var r=n(82634);function i(e){"function"!=typeof e&&(e=(0,r.Z)(e));for(var t=this._groups,n=t.length,i=Array(n),a=0;a=k&&(k=S+1);!(E=y[k])&&++k=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this}function _(e){function t(t,n){return t&&n?e(t.__data__,n.__data__):!t-!n}e||(e=E);for(var n=this._groups,r=n.length,i=Array(r),a=0;at?1:e>=t?0:NaN}function S(){var e=arguments[0];return arguments[0]=this,e.apply(null,arguments),this}function k(){var e=Array(this.size()),t=-1;return this.each(function(){e[++t]=this}),e}function x(){for(var e=this._groups,t=0,n=e.length;t1?this.each((null==t?F:"function"==typeof t?B:Y)(e,t)):this.node()[e]}function H(e){return e.trim().split(/^|\s+/)}function $(e){return e.classList||new z(e)}function z(e){this._node=e,this._names=H(e.getAttribute("class")||"")}function G(e,t){for(var n=$(e),r=-1,i=t.length;++rthis._names.indexOf(e)&&(this._names.push(e),this._node.setAttribute("class",this._names.join(" ")))},remove:function(e){var t=this._names.indexOf(e);t>=0&&(this._names.splice(t,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(e){return this._names.indexOf(e)>=0}};var ec=n(789);function el(e){var t="function"==typeof e?e:(0,ec.Z)(e);return this.select(function(){return this.appendChild(t.apply(this,arguments))})}function ef(){return null}function ed(e,t){var n="function"==typeof e?e:(0,ec.Z)(e),i=null==t?ef:"function"==typeof t?t:(0,r.Z)(t);return this.select(function(){return this.insertBefore(n.apply(this,arguments),i.apply(this,arguments)||null)})}function eh(){var e=this.parentNode;e&&e.removeChild(this)}function ep(){return this.each(eh)}function eb(){var e=this.cloneNode(!1),t=this.parentNode;return t?t.insertBefore(e,this.nextSibling):e}function em(){var e=this.cloneNode(!0),t=this.parentNode;return t?t.insertBefore(e,this.nextSibling):e}function eg(e){return this.select(e?em:eb)}function ev(e){return arguments.length?this.property("__data__",e):this.node().__data__}var ey=n(25109),ew=n(85021);function e_(e,t,n){var r=(0,ew.Z)(e),i=r.CustomEvent;"function"==typeof i?i=new i(t,n):(i=r.document.createEvent("Event"),n?(i.initEvent(t,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(t,!1,!1)),e.dispatchEvent(i)}function eE(e,t){return function(){return e_(this,e,t)}}function eS(e,t){return function(){return e_(this,e,t.apply(this,arguments))}}function ek(e,t){return this.each(("function"==typeof t?eS:eE)(e,t))}var ex=[null];function eT(e,t){this._groups=e,this._parents=t}function eM(){return new eT([[document.documentElement]],ex)}eT.prototype=eM.prototype={constructor:eT,select:i,selectAll:o,filter:u,data:m,enter:l,exit:g,join:v,merge:y,order:w,sort:_,call:S,nodes:k,node:x,size:T,empty:M,each:O,attr:R,style:j.Z,property:U,classed:Z,text:ee,html:ei,raise:eo,lower:eu,append:el,insert:ed,remove:ep,clone:eg,datum:ev,on:ey.ZP,dispatch:ek};let eO=eM},25109(e,t,n){"use strict";n.d(t,{B:()=>i,ZP:()=>l,_H:()=>f});var r={},i=null;function a(e,t,n){return e=o(e,t,n),function(t){var n=t.relatedTarget;n&&(n===this||8&n.compareDocumentPosition(this))||e.call(this,t)}}function o(e,t,n){return function(r){var a=i;i=r;try{e.call(this,this.__data__,t,n)}finally{i=a}}}function s(e){return e.trim().split(/^|\s+/).map(function(e){var t="",n=e.indexOf(".");return n>=0&&(t=e.slice(n+1),e=e.slice(0,n)),{type:e,name:t}})}function u(e){return function(){var t=this.__on;if(t){for(var n,r=0,i=-1,a=t.length;ru,Z:()=>s});var r=n(85021);function i(e){return function(){this.style.removeProperty(e)}}function a(e,t,n){return function(){this.style.setProperty(e,t,n)}}function o(e,t,n){return function(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(e):this.style.setProperty(e,r,n)}}function s(e,t,n){return arguments.length>1?this.each((null==t?i:"function"==typeof t?o:a)(e,t,null==n?"":n)):u(this.node(),e)}function u(e,t){return e.style.getPropertyValue(t)||(0,r.Z)(e).getComputedStyle(e,null).getPropertyValue(t)}},82634(e,t,n){"use strict";function r(){}function i(e){return null==e?r:function(){return this.querySelector(e)}}n.d(t,{Z:()=>i})},3545(e,t,n){"use strict";function r(){return[]}function i(e){return null==e?r:function(){return this.querySelectorAll(e)}}n.d(t,{Z:()=>i})},45553(e,t,n){"use strict";n.d(t,{Z:()=>i});var r=n(25109);function i(){for(var e,t=r.B;e=t.sourceEvent;)t=e;return t}},24793(e,t,n){"use strict";n.d(t,{Z:()=>a});var r=n(45553),i=n(42115);function a(e,t,n){arguments.length<3&&(n=t,t=(0,r.Z)().changedTouches);for(var a,o=0,s=t?t.length:0;or})},71098(e,t,n){"use strict";n.r(t),n.d(t,{arc:()=>C,area:()=>j,areaRadial:()=>W,curveBasis:()=>eM,curveBasisClosed:()=>eA,curveBasisOpen:()=>eC,curveBundle:()=>eD,curveCardinal:()=>eR,curveCardinalClosed:()=>eF,curveCardinalOpen:()=>eB,curveCatmullRom:()=>e$,curveCatmullRomClosed:()=>eG,curveCatmullRomOpen:()=>eK,curveLinear:()=>D,curveLinearClosed:()=>eq,curveMonotoneX:()=>e3,curveMonotoneY:()=>e4,curveNatural:()=>e9,curveStep:()=>e7,curveStepAfter:()=>tt,curveStepBefore:()=>te,line:()=>R,lineRadial:()=>G,linkHorizontal:()=>et,linkRadial:()=>er,linkVertical:()=>en,pie:()=>B,pointRadial:()=>K,radialArea:()=>W,radialLine:()=>G,stack:()=>ta,stackOffsetDiverging:()=>ts,stackOffsetExpand:()=>to,stackOffsetNone:()=>tn,stackOffsetSilhouette:()=>tu,stackOffsetWiggle:()=>tc,stackOrderAppearance:()=>tl,stackOrderAscending:()=>td,stackOrderDescending:()=>tp,stackOrderInsideOut:()=>tb,stackOrderNone:()=>tr,stackOrderReverse:()=>tm,symbol:()=>eS,symbolCircle:()=>ei,symbolCross:()=>ea,symbolDiamond:()=>eu,symbolSquare:()=>ep,symbolStar:()=>eh,symbolTriangle:()=>em,symbolWye:()=>e_,symbols:()=>eE});var r=Math.PI,i=2*r,a=1e-6,o=i-a;function s(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function u(){return new s}s.prototype=u.prototype={constructor:s,moveTo:function(e,t){this._+="M"+(this._x0=this._x1=+e)+","+(this._y0=this._y1=+t)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(e,t){this._+="L"+(this._x1=+e)+","+(this._y1=+t)},quadraticCurveTo:function(e,t,n,r){this._+="Q"+ +e+","+ +t+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(e,t,n,r,i,a){this._+="C"+ +e+","+ +t+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(e,t,n,i,o){e=+e,t=+t,n=+n,i=+i,o=+o;var s=this._x1,u=this._y1,c=n-e,l=i-t,f=s-e,d=u-t,h=f*f+d*d;if(o<0)throw Error("negative radius: "+o);if(null===this._x1)this._+="M"+(this._x1=e)+","+(this._y1=t);else if(h>a){if(Math.abs(d*c-l*f)>a&&o){var p=n-s,b=i-u,m=c*c+l*l,g=Math.sqrt(m),v=Math.sqrt(h),y=o*Math.tan((r-Math.acos((m+h-(p*p+b*b))/(2*g*v)))/2),w=y/v,_=y/g;Math.abs(w-1)>a&&(this._+="L"+(e+w*f)+","+(t+w*d)),this._+="A"+o+","+o+",0,0,"+ +(d*p>f*b)+","+(this._x1=e+_*c)+","+(this._y1=t+_*l)}else this._+="L"+(this._x1=e)+","+(this._y1=t)}},arc:function(e,t,n,s,u,c){e=+e,t=+t,n=+n,c=!!c;var l=n*Math.cos(s),f=n*Math.sin(s),d=e+l,h=t+f,p=1^c,b=c?s-u:u-s;if(n<0)throw Error("negative radius: "+n);null===this._x1?this._+="M"+d+","+h:(Math.abs(this._x1-d)>a||Math.abs(this._y1-h)>a)&&(this._+="L"+d+","+h),n&&(b<0&&(b=b%i+i),b>o?this._+="A"+n+","+n+",0,1,"+p+","+(e-l)+","+(t-f)+"A"+n+","+n+",0,1,"+p+","+(this._x1=d)+","+(this._y1=h):b>a&&(this._+="A"+n+","+n+",0,"+ +(b>=r)+","+p+","+(this._x1=e+n*Math.cos(u))+","+(this._y1=t+n*Math.sin(u))))},rect:function(e,t,n,r){this._+="M"+(this._x0=this._x1=+e)+","+(this._y0=this._y1=+t)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};let c=u;function l(e){return function(){return e}}var f=Math.abs,d=Math.atan2,h=Math.cos,p=Math.max,b=Math.min,m=Math.sin,g=Math.sqrt,v=1e-12,y=Math.PI,w=y/2,_=2*y;function E(e){return e>1?0:e<-1?y:Math.acos(e)}function S(e){return e>=1?w:e<=-1?-w:Math.asin(e)}function k(e){return e.innerRadius}function x(e){return e.outerRadius}function T(e){return e.startAngle}function M(e){return e.endAngle}function O(e){return e&&e.padAngle}function A(e,t,n,r,i,a,o,s){var u=n-e,c=r-t,l=o-i,f=s-a,d=f*u-l*c;if(!(d*dI*I+D*D&&(T=O,M=A),{cx:T,cy:M,x01:-l,y01:-f,x11:T*(i/S-1),y11:M*(i/S-1)}}function C(){var e=k,t=x,n=l(0),r=null,i=T,a=M,o=O,s=null;function u(){var u,l,p=+e.apply(this,arguments),k=+t.apply(this,arguments),x=i.apply(this,arguments)-w,T=a.apply(this,arguments)-w,M=f(T-x),O=T>x;if(s||(s=u=c()),kv){if(M>_-v)s.moveTo(k*h(x),k*m(x)),s.arc(0,0,k,x,T,!O),p>v&&(s.moveTo(p*h(T),p*m(T)),s.arc(0,0,p,T,x,O));else{var C,I,D=x,N=T,P=x,R=T,j=M,F=M,Y=o.apply(this,arguments)/2,B=Y>v&&(r?+r.apply(this,arguments):g(p*p+k*k)),U=b(f(k-p)/2,+n.apply(this,arguments)),H=U,$=U;if(B>v){var z=S(B/p*m(Y)),G=S(B/k*m(Y));(j-=2*z)>v?(z*=O?1:-1,P+=z,R-=z):(j=0,P=R=(x+T)/2),(F-=2*G)>v?(G*=O?1:-1,D+=G,N-=G):(F=0,D=N=(x+T)/2)}var W=k*h(D),K=k*m(D),V=p*h(R),q=p*m(R);if(U>v){var Z,X=k*h(N),J=k*m(N),Q=p*h(P),ee=p*m(P);if(Mv?$>v?(C=L(Q,ee,W,K,k,$,O),I=L(X,J,V,q,k,$,O),s.moveTo(C.cx+C.x01,C.cy+C.y01),$v&&j>v?H>v?(C=L(V,q,X,J,p,-H,O),I=L(W,K,Q,ee,p,-H,O),s.lineTo(C.cx+C.x01,C.cy+C.y01),H=f;--d)s.point(g[d],v[d]);s.lineEnd(),s.areaEnd()}}m&&(g[l]=+e(h,l,u),v[l]=+n(h,l,u),s.point(t?+t(h,l,u):g[l],r?+r(h,l,u):v[l]))}if(p)return s=null,p+""||null}function f(){return R().defined(i).curve(o).context(a)}return u.x=function(n){return arguments.length?(e="function"==typeof n?n:l(+n),t=null,u):e},u.x0=function(t){return arguments.length?(e="function"==typeof t?t:l(+t),u):e},u.x1=function(e){return arguments.length?(t=null==e?null:"function"==typeof e?e:l(+e),u):t},u.y=function(e){return arguments.length?(n="function"==typeof e?e:l(+e),r=null,u):n},u.y0=function(e){return arguments.length?(n="function"==typeof e?e:l(+e),u):n},u.y1=function(e){return arguments.length?(r=null==e?null:"function"==typeof e?e:l(+e),u):r},u.lineX0=u.lineY0=function(){return f().x(e).y(n)},u.lineY1=function(){return f().x(e).y(r)},u.lineX1=function(){return f().x(t).y(n)},u.defined=function(e){return arguments.length?(i="function"==typeof e?e:l(!!e),u):i},u.curve=function(e){return arguments.length?(o=e,null!=a&&(s=o(a)),u):o},u.context=function(e){return arguments.length?(null==e?a=s=null:s=o(a=e),u):a},u}function F(e,t){return te?1:t>=e?0:NaN}function Y(e){return e}function B(){var e=Y,t=F,n=null,r=l(0),i=l(_),a=l(0);function o(o){var s,u,c,l,f,d=o.length,h=0,p=Array(d),b=Array(d),m=+r.apply(this,arguments),g=Math.min(_,Math.max(-_,i.apply(this,arguments)-m)),v=Math.min(Math.abs(g)/d,a.apply(this,arguments)),y=v*(g<0?-1:1);for(s=0;s0&&(h+=f);for(null!=t?p.sort(function(e,n){return t(b[e],b[n])}):null!=n&&p.sort(function(e,t){return n(o[e],o[t])}),s=0,c=h?(g-d*y)/h:0;s0?f*c:0)+y,b[u]={data:o[u],index:s,value:f,startAngle:m,endAngle:l,padAngle:v};return b}return o.value=function(t){return arguments.length?(e="function"==typeof t?t:l(+t),o):e},o.sortValues=function(e){return arguments.length?(t=e,n=null,o):t},o.sort=function(e){return arguments.length?(n=e,t=null,o):n},o.startAngle=function(e){return arguments.length?(r="function"==typeof e?e:l(+e),o):r},o.endAngle=function(e){return arguments.length?(i="function"==typeof e?e:l(+e),o):i},o.padAngle=function(e){return arguments.length?(a="function"==typeof e?e:l(+e),o):a},o}I.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:this._context.lineTo(e,t)}}};var U=$(D);function H(e){this._curve=e}function $(e){function t(t){return new H(e(t))}return t._curve=e,t}function z(e){var t=e.curve;return e.angle=e.x,delete e.x,e.radius=e.y,delete e.y,e.curve=function(e){return arguments.length?t($(e)):t()._curve},e}function G(){return z(R().curve(U))}function W(){var e=j().curve(U),t=e.curve,n=e.lineX0,r=e.lineX1,i=e.lineY0,a=e.lineY1;return e.angle=e.x,delete e.x,e.startAngle=e.x0,delete e.x0,e.endAngle=e.x1,delete e.x1,e.radius=e.y,delete e.y,e.innerRadius=e.y0,delete e.y0,e.outerRadius=e.y1,delete e.y1,e.lineStartAngle=function(){return z(n())},delete e.lineX0,e.lineEndAngle=function(){return z(r())},delete e.lineX1,e.lineInnerRadius=function(){return z(i())},delete e.lineY0,e.lineOuterRadius=function(){return z(a())},delete e.lineY1,e.curve=function(e){return arguments.length?t($(e)):t()._curve},e}function K(e,t){return[(t=+t)*Math.cos(e-=Math.PI/2),t*Math.sin(e)]}H.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(e,t){this._curve.point(t*Math.sin(e),-(t*Math.cos(e)))}};var V=Array.prototype.slice;function q(e){return e.source}function Z(e){return e.target}function X(e){var t=q,n=Z,r=N,i=P,a=null;function o(){var o,s=V.call(arguments),u=t.apply(this,s),l=n.apply(this,s);if(a||(a=o=c()),e(a,+r.apply(this,(s[0]=u,s)),+i.apply(this,s),+r.apply(this,(s[0]=l,s)),+i.apply(this,s)),o)return a=null,o+""||null}return o.source=function(e){return arguments.length?(t=e,o):t},o.target=function(e){return arguments.length?(n=e,o):n},o.x=function(e){return arguments.length?(r="function"==typeof e?e:l(+e),o):r},o.y=function(e){return arguments.length?(i="function"==typeof e?e:l(+e),o):i},o.context=function(e){return arguments.length?(a=null==e?null:e,o):a},o}function J(e,t,n,r,i){e.moveTo(t,n),e.bezierCurveTo(t=(t+r)/2,n,t,i,r,i)}function Q(e,t,n,r,i){e.moveTo(t,n),e.bezierCurveTo(t,n=(n+i)/2,r,n,r,i)}function ee(e,t,n,r,i){var a=K(t,n),o=K(t,n=(n+i)/2),s=K(r,n),u=K(r,i);e.moveTo(a[0],a[1]),e.bezierCurveTo(o[0],o[1],s[0],s[1],u[0],u[1])}function et(){return X(J)}function en(){return X(Q)}function er(){var e=X(ee);return e.angle=e.x,delete e.x,e.radius=e.y,delete e.y,e}let ei={draw:function(e,t){var n=Math.sqrt(t/y);e.moveTo(n,0),e.arc(0,0,n,0,_)}},ea={draw:function(e,t){var n=Math.sqrt(t/5)/2;e.moveTo(-3*n,-n),e.lineTo(-n,-n),e.lineTo(-n,-3*n),e.lineTo(n,-3*n),e.lineTo(n,-n),e.lineTo(3*n,-n),e.lineTo(3*n,n),e.lineTo(n,n),e.lineTo(n,3*n),e.lineTo(-n,3*n),e.lineTo(-n,n),e.lineTo(-3*n,n),e.closePath()}};var eo=Math.sqrt(1/3),es=2*eo;let eu={draw:function(e,t){var n=Math.sqrt(t/es),r=n*eo;e.moveTo(0,-n),e.lineTo(r,0),e.lineTo(0,n),e.lineTo(-r,0),e.closePath()}};var ec=.8908130915292852,el=Math.sin(y/10)/Math.sin(7*y/10),ef=Math.sin(_/10)*el,ed=-Math.cos(_/10)*el;let eh={draw:function(e,t){var n=Math.sqrt(t*ec),r=ef*n,i=ed*n;e.moveTo(0,-n),e.lineTo(r,i);for(var a=1;a<5;++a){var o=_*a/5,s=Math.cos(o),u=Math.sin(o);e.lineTo(u*n,-s*n),e.lineTo(s*r-u*i,u*r+s*i)}e.closePath()}},ep={draw:function(e,t){var n=Math.sqrt(t),r=-n/2;e.rect(r,r,n,n)}};var eb=Math.sqrt(3);let em={draw:function(e,t){var n=-Math.sqrt(t/(3*eb));e.moveTo(0,2*n),e.lineTo(-eb*n,-n),e.lineTo(eb*n,-n),e.closePath()}};var eg=-.5,ev=Math.sqrt(3)/2,ey=1/Math.sqrt(12),ew=(ey/2+1)*3;let e_={draw:function(e,t){var n=Math.sqrt(t/ew),r=n/2,i=n*ey,a=r,o=n*ey+n,s=-a,u=o;e.moveTo(r,i),e.lineTo(a,o),e.lineTo(s,u),e.lineTo(eg*r-ev*i,ev*r+eg*i),e.lineTo(eg*a-ev*o,ev*a+eg*o),e.lineTo(eg*s-ev*u,ev*s+eg*u),e.lineTo(eg*r+ev*i,eg*i-ev*r),e.lineTo(eg*a+ev*o,eg*o-ev*a),e.lineTo(eg*s+ev*u,eg*u-ev*s),e.closePath()}};var eE=[ei,ea,eu,ep,eh,em,e_];function eS(){var e=l(ei),t=l(64),n=null;function r(){var r;if(n||(n=r=c()),e.apply(this,arguments).draw(n,+t.apply(this,arguments)),r)return n=null,r+""||null}return r.type=function(t){return arguments.length?(e="function"==typeof t?t:l(t),r):e},r.size=function(e){return arguments.length?(t="function"==typeof e?e:l(+e),r):t},r.context=function(e){return arguments.length?(n=null==e?null:e,r):n},r}function ek(){}function ex(e,t,n){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+n)/6)}function eT(e){this._context=e}function eM(e){return new eT(e)}function eO(e){this._context=e}function eA(e){return new eO(e)}function eL(e){this._context=e}function eC(e){return new eL(e)}function eI(e,t){this._basis=new eT(e),this._beta=t}eT.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:ex(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:ex(this,e,t)}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}},eO.prototype={areaStart:ek,areaEnd:ek,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:ex(this,e,t)}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}},eL.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+e)/6,r=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:ex(this,e,t)}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}},eI.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var e=this._x,t=this._y,n=e.length-1;if(n>0)for(var r,i=e[0],a=t[0],o=e[n]-i,s=t[n]-a,u=-1;++u<=n;)r=u/n,this._basis.point(this._beta*e[u]+(1-this._beta)*(i+r*o),this._beta*t[u]+(1-this._beta)*(a+r*s));this._x=this._y=null,this._basis.lineEnd()},point:function(e,t){this._x.push(+e),this._y.push(+t)}};let eD=function e(t){function n(e){return 1===t?new eT(e):new eI(e,t)}return n.beta=function(t){return e(+t)},n}(.85);function eN(e,t,n){e._context.bezierCurveTo(e._x1+e._k*(e._x2-e._x0),e._y1+e._k*(e._y2-e._y0),e._x2+e._k*(e._x1-t),e._y2+e._k*(e._y1-n),e._x2,e._y2)}function eP(e,t){this._context=e,this._k=(1-t)/6}eP.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:eN(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2,this._x1=e,this._y1=t;break;case 2:this._point=3;default:eN(this,e,t)}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};let eR=function e(t){function n(e){return new eP(e,0)}return n.tension=function(t){return e(+t)},n}(0);function ej(e,t){this._context=e,this._k=(1-t)/6}ej.prototype={areaStart:ek,areaEnd:ek,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:eN(this,e,t)}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};let eF=function e(t){function n(e){return new ej(e,0)}return n.tension=function(t){return e(+t)},n}(0);function eY(e,t){this._context=e,this._k=(1-t)/6}eY.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:eN(this,e,t)}this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};let eB=function e(t){function n(e){return new eY(e,0)}return n.tension=function(t){return e(+t)},n}(0);function eU(e,t,n){var r=e._x1,i=e._y1,a=e._x2,o=e._y2;if(e._l01_a>v){var s=2*e._l01_2a+3*e._l01_a*e._l12_a+e._l12_2a,u=3*e._l01_a*(e._l01_a+e._l12_a);r=(r*s-e._x0*e._l12_2a+e._x2*e._l01_2a)/u,i=(i*s-e._y0*e._l12_2a+e._y2*e._l01_2a)/u}if(e._l23_a>v){var c=2*e._l23_2a+3*e._l23_a*e._l12_a+e._l12_2a,l=3*e._l23_a*(e._l23_a+e._l12_a);a=(a*c+e._x1*e._l23_2a-t*e._l12_2a)/l,o=(o*c+e._y1*e._l23_2a-n*e._l12_2a)/l}e._context.bezierCurveTo(r,i,a,o,e._x2,e._y2)}function eH(e,t){this._context=e,this._alpha=t}eH.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,r=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3;default:eU(this,e,t)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};let e$=function e(t){function n(e){return t?new eH(e,t):new eP(e,0)}return n.alpha=function(t){return e(+t)},n}(.5);function ez(e,t){this._context=e,this._alpha=t}ez.prototype={areaStart:ek,areaEnd:ek,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,r=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=e,this._y3=t;break;case 1:this._point=2,this._context.moveTo(this._x4=e,this._y4=t);break;case 2:this._point=3,this._x5=e,this._y5=t;break;default:eU(this,e,t)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};let eG=function e(t){function n(e){return t?new ez(e,t):new ej(e,0)}return n.alpha=function(t){return e(+t)},n}(.5);function eW(e,t){this._context=e,this._alpha=t}eW.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){if(e=+e,t=+t,this._point){var n=this._x2-e,r=this._y2-t;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:eU(this,e,t)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=e,this._y0=this._y1,this._y1=this._y2,this._y2=t}};let eK=function e(t){function n(e){return t?new eW(e,t):new eY(e,0)}return n.alpha=function(t){return e(+t)},n}(.5);function eV(e){this._context=e}function eq(e){return new eV(e)}function eZ(e){return e<0?-1:1}function eX(e,t,n){var r=e._x1-e._x0,i=t-e._x1,a=(e._y1-e._y0)/(r||i<0&&-0),o=(n-e._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(eZ(a)+eZ(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function eJ(e,t){var n=e._x1-e._x0;return n?(3*(e._y1-e._y0)/n-t)/2:t}function eQ(e,t,n){var r=e._x0,i=e._y0,a=e._x1,o=e._y1,s=(a-r)/3;e._context.bezierCurveTo(r+s,i+s*t,a-s,o-s*n,a,o)}function e1(e){this._context=e}function e0(e){this._context=new e2(e)}function e2(e){this._context=e}function e3(e){return new e1(e)}function e4(e){return new e0(e)}function e5(e){this._context=e}function e6(e){var t,n,r=e.length-1,i=Array(r),a=Array(r),o=Array(r);for(i[0]=0,a[0]=2,o[0]=e[0]+2*e[1],t=1;t=0;--t)i[t]=(o[t]-i[t+1])/a[t];for(t=0,a[r-1]=(e[r]+i[r-1])/2;t1)for(var n,r,i,a=1,o=e[t[0]],s=o.length;a=0;)n[t]=t;return n}function ti(e,t){return e[t]}function ta(){var e=l([]),t=tr,n=tn,r=ti;function i(i){var a,o,s=e.apply(this,arguments),u=i.length,c=s.length,l=Array(c);for(a=0;a0){for(var n,r,i,a=0,o=e[0].length;a0)for(var n,r,i,a,o,s,u=0,c=e[t[0]].length;u0?(r[0]=a,r[1]=a+=i):i<0?(r[1]=o,r[0]=o+=i):(r[0]=0,r[1]=i)}function tu(e,t){if((n=e.length)>0){for(var n,r=0,i=e[t[0]],a=i.length;r0&&(r=(n=e[t[0]]).length)>0){for(var n,r,i,a=0,o=1;oa&&(a=t,r=n);return r}function td(e){var t=e.map(th);return tr(e).sort(function(e,n){return t[e]-t[n]})}function th(e){for(var t,n=0,r=-1,i=e.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var n=this._x*(1-this._t)+e*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,t)}}this._x=e,this._y=t}}},35374(e,t,n){"use strict";n.d(t,{B7:()=>m,HT:()=>g,zO:()=>p});var r,i,a=0,o=0,s=0,u=1e3,c=0,l=0,f=0,d="object"==typeof performance&&performance.now?performance:Date,h="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(e){setTimeout(e,17)};function p(){return l||(h(b),l=d.now()+f)}function b(){l=0}function m(){this._call=this._time=this._next=null}function g(e,t,n){var r=new m;return r.restart(e,t,n),r}function v(){p(),++a;for(var e,t=r;t;)(e=l-t._time)>=0&&t._call.call(null,e),t=t._next;--a}function y(){l=(c=d.now())+f,a=o=0;try{v()}finally{a=0,_(),l=0}}function w(){var e=d.now(),t=e-c;t>u&&(f-=t,c=e)}function _(){for(var e,t,n=r,a=1/0;n;)n._call?(a>n._time&&(a=n._time),e=n,n=n._next):(t=n._next,n._next=null,n=e?e._next=t:r=t);i=e,E(a)}function E(e){if(!a){var t;o&&(o=clearTimeout(o)),e-l>24?(e<1/0&&(o=setTimeout(y,e-d.now()-f)),s&&(s=clearInterval(s))):(s||(c=d.now(),s=setInterval(w,u)),a=1,h(y))}}m.prototype=g.prototype={constructor:m,restart:function(e,t,n){if("function"!=typeof e)throw TypeError("callback is not a function");n=(null==n?p():+n)+(null==t?0:+t),this._next||i===this||(i?i._next=this:r=this,i=this),this._call=e,this._time=n,E()},stop:function(){this._call&&(this._call=null,this._time=1/0,E())}}},76626(e,t,n){"use strict";n.r(t),n.d(t,{zoom:()=>t5,zoomIdentity:()=>tq,zoomTransform:()=>tZ});var r,i,a,o,s=n(92626),u=n(44266),c=Math.SQRT2,l=2,f=4,d=1e-12;function h(e){return((e=Math.exp(e))+1/e)/2}function p(e){return((e=Math.exp(e))-1/e)/2}function b(e){return((e=Math.exp(2*e))-1)/(e+1)}function m(e,t){var n,r,i=e[0],a=e[1],o=e[2],s=t[0],u=t[1],m=t[2],g=s-i,v=u-a,y=g*g+v*v;if(yT)throw Error("too late; already scheduled");return n}function P(e,t){var n=R(e,t);if(n.state>A)throw Error("too late; already running");return n}function R(e,t){var n=e.__transition;if(!n||!(n=n[t]))throw Error("transition not found");return n}function j(e,t,n){var r,i=e.__transition;function a(e){n.state=M,n.timer.restart(o,n.delay,n.time),n.delay<=e&&o(e-n.delay)}function o(a){var c,l,f,d;if(n.state!==M)return u();for(c in i)if((d=i[c]).name===n.name){if(d.state===A)return S(o);d.state===L?(d.state=I,d.timer.stop(),d.on.call("interrupt",e,e.__data__,d.index,d.group),delete i[c]):+cO&&n.state180?t+=360:t-e>180&&(e+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:B(e,t)})):t&&n.push(i(n)+"rotate("+t+r)}function s(e,t,n,a){e!==t?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:B(e,t)}):t&&n.push(i(n)+"skewX("+t+r)}function u(e,t,n,r,a,o){if(e!==n||t!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:B(e,n)},{i:s-2,x:B(t,r)})}else(1!==n||1!==r)&&a.push(i(a)+"scale("+n+","+r+")")}return function(t,n){var r=[],i=[];return t=e(t),n=e(n),a(t.translateX,t.translateY,n.translateX,n.translateY,r,i),o(t.rotate,n.rotate,r,i),s(t.skewX,n.skewX,r,i),u(t.scaleX,t.scaleY,n.scaleX,n.scaleY,r,i),t=n=null,function(e){for(var t,n=-1,a=i.length;++n>8&15|t>>4&240,t>>4&15|240&t,(15&t)<<4|15&t,1):8===n?e_(t>>24&255,t>>16&255,t>>8&255,(255&t)/255):4===n?e_(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|240&t,((15&t)<<4|15&t)/255):null):(t=ec.exec(e))?new ek(t[1],t[2],t[3],1):(t=el.exec(e))?new ek(255*t[1]/100,255*t[2]/100,255*t[3]/100,1):(t=ef.exec(e))?e_(t[1],t[2],t[3],t[4]):(t=ed.exec(e))?e_(255*t[1]/100,255*t[2]/100,255*t[3]/100,t[4]):(t=eh.exec(e))?eO(t[1],t[2]/100,t[3]/100,1):(t=ep.exec(e))?eO(t[1],t[2]/100,t[3]/100,t[4]):eb.hasOwnProperty(e)?ew(eb[e]):"transparent"===e?new ek(NaN,NaN,NaN,0):null}function ew(e){return new ek(e>>16&255,e>>8&255,255&e,1)}function e_(e,t,n,r){return r<=0&&(e=t=n=NaN),new ek(e,t,n,r)}function eE(e){return(e instanceof en||(e=ey(e)),e)?(e=e.rgb(),new ek(e.r,e.g,e.b,e.opacity)):new ek}function eS(e,t,n,r){return 1===arguments.length?eE(e):new ek(e,t,n,null==r?1:r)}function ek(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}function ex(){return"#"+eM(this.r)+eM(this.g)+eM(this.b)}function eT(){var e=this.opacity;return(1===(e=isNaN(e)?1:Math.max(0,Math.min(1,e)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===e?")":", "+e+")")}function eM(e){return((e=Math.max(0,Math.min(255,Math.round(e)||0)))<16?"0":"")+e.toString(16)}function eO(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new eC(e,t,n,r)}function eA(e){if(e instanceof eC)return new eC(e.h,e.s,e.l,e.opacity);if(e instanceof en||(e=ey(e)),!e)return new eC;if(e instanceof eC)return e;var t=(e=e.rgb()).r/255,n=e.g/255,r=e.b/255,i=Math.min(t,n,r),a=Math.max(t,n,r),o=NaN,s=a-i,u=(a+i)/2;return s?(o=t===a?(n-r)/s+(n0&&u<1?0:o,new eC(o,s,u,e.opacity)}function eL(e,t,n,r){return 1===arguments.length?eA(e):new eC(e,t,n,null==r?1:r)}function eC(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}function eI(e,t,n){return(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)*255}function eD(e,t,n,r,i){var a=e*e,o=a*e;return((1-3*e+3*a-o)*t+(4-6*a+3*o)*n+(1+3*e+3*a-3*o)*r+o*i)/6}function eN(e){var t=e.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,t-1):Math.floor(n*t),i=e[r],a=e[r+1],o=r>0?e[r-1]:2*i-a,s=r=240?e-240:e+120,i,r),eI(e,i,r),eI(e<120?e+240:e-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var e=this.opacity;return(1===(e=isNaN(e)?1:Math.max(0,Math.min(1,e)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===e?")":", "+e+")")}}));let eU=function e(t){var n=eY(1);function r(e,t){var r=n((e=eS(e)).r,(t=eS(t)).r),i=n(e.g,t.g),a=n(e.b,t.b),o=eB(e.opacity,t.opacity);return function(t){return e.r=r(t),e.g=i(t),e.b=a(t),e.opacity=o(t),e+""}}return r.gamma=e,r}(1);function eH(e){return function(t){var n,r,i=t.length,a=Array(i),o=Array(i),s=Array(i);for(n=0;na&&(i=t.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,u.push({i:o,x:B(n,r)})),a=ez.lastIndex;return a=0&&(e=e.slice(0,t)),!e||"start"===e})}function tc(e,t,n){var r,i,a=tu(t)?N:P;return function(){var o=a(this,e),s=o.on;s!==r&&(i=(r=s).copy()).on(t,n),o.on=i}}function tl(e,t){var n=this._id;return arguments.length<2?R(this.node(),n).on.on(e):this.each(tc(n,e,t))}function tf(e){return function(){var t=this.parentNode;for(var n in this.__transition)if(+n!==e)return;t&&t.removeChild(this)}}function td(){return this.on("end.remove",tf(this._id))}var th=n(82634);function tp(e){var t=this._name,n=this._id;"function"!=typeof e&&(e=(0,th.Z)(e));for(var r=this._groups,i=r.length,a=Array(i),o=0;or?(r+i)/2:Math.min(0,r)||Math.max(0,i),o>a?(a+o)/2:Math.min(0,a)||Math.max(0,o))}function t5(){var e,t,n=tQ,r=t1,i=t4,a=t2,o=t3,c=[0,1/0],l=[[-1/0,-1/0],[1/0,1/0]],f=250,d=m,h=(0,s.Z)("start","zoom","end"),p=500,b=150,_=0;function E(e){e.property("__zoom",t0).on("wheel.zoom",A).on("mousedown.zoom",L).on("dblclick.zoom",C).filter(o).on("touchstart.zoom",I).on("touchmove.zoom",D).on("touchend.zoom touchcancel.zoom",N).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function S(e,t){return(t=Math.max(c[0],Math.min(c[1],t)))===e.k?e:new tV(t,e.x,e.y)}function k(e,t,n){var r=t[0]-n[0]*e.k,i=t[1]-n[1]*e.k;return r===e.x&&i===e.y?e:new tV(e.k,r,i)}function x(e){return[(+e[0][0]+ +e[1][0])/2,(+e[0][1]+ +e[1][1])/2]}function T(e,t,n){e.on("start.zoom",function(){M(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){M(this,arguments).end()}).tween("zoom",function(){var e=this,i=arguments,a=M(e,i),o=r.apply(e,i),s=null==n?x(o):"function"==typeof n?n.apply(e,i):n,u=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),c=e.__zoom,l="function"==typeof t?t.apply(e,i):t,f=d(c.invert(s).concat(u/c.k),l.invert(s).concat(u/l.k));return function(e){if(1===e)e=l;else{var t=f(e),n=u/t[2];e=new tV(n,s[0]-t[0]*n,s[1]-t[1]*n)}a.zoom(null,e)}})}function M(e,t,n){return!n&&e.__zooming||new O(e,t)}function O(e,t){this.that=e,this.args=t,this.active=0,this.extent=r.apply(e,t),this.taps=0}function A(){if(n.apply(this,arguments)){var e=M(this,arguments),t=this.__zoom,r=Math.max(c[0],Math.min(c[1],t.k*Math.pow(2,a.apply(this,arguments)))),o=(0,v.Z)(this);if(e.wheel)(e.mouse[0][0]!==o[0]||e.mouse[0][1]!==o[1])&&(e.mouse[1]=t.invert(e.mouse[0]=o)),clearTimeout(e.wheel);else{if(t.k===r)return;e.mouse=[o,t.invert(o)],F(this),e.start()}tJ(),e.wheel=setTimeout(s,b),e.zoom("mouse",i(k(S(t,r),e.mouse[0],e.mouse[1]),e.extent,l))}function s(){e.wheel=null,e.end()}}function L(){if(!t&&n.apply(this,arguments)){var e=M(this,arguments,!0),r=(0,y.Z)(g.B.view).on("mousemove.zoom",c,!0).on("mouseup.zoom",f,!0),a=(0,v.Z)(this),o=g.B.clientX,s=g.B.clientY;(0,u.Z)(g.B.view),tX(),e.mouse=[a,this.__zoom.invert(a)],F(this),e.start()}function c(){if(tJ(),!e.moved){var t=g.B.clientX-o,n=g.B.clientY-s;e.moved=t*t+n*n>_}e.zoom("mouse",i(k(e.that.__zoom,e.mouse[0]=(0,v.Z)(e.that),e.mouse[1]),e.extent,l))}function f(){r.on("mousemove.zoom mouseup.zoom",null),(0,u.D)(g.B.view,e.moved),tJ(),e.end()}}function C(){if(n.apply(this,arguments)){var e=this.__zoom,t=(0,v.Z)(this),a=e.invert(t),o=e.k*(g.B.shiftKey?.5:2),s=i(k(S(e,o),t,a),r.apply(this,arguments),l);tJ(),f>0?(0,y.Z)(this).transition().duration(f).call(T,s,t):(0,y.Z)(this).call(E.transform,s)}}function I(){if(n.apply(this,arguments)){var t,r,i,a,o=g.B.touches,s=o.length,u=M(this,arguments,g.B.changedTouches.length===s);for(tX(),r=0;r=0?i=setTimeout(r,t-c):(i=null,n||(u=e.apply(o,a),o=a=null))}null==t&&(t=100);var i,a,o,s,u,c=function(){o=this,a=arguments,s=Date.now();var c=n&&!i;return i||(i=setTimeout(r,t)),c&&(u=e.apply(o,a),o=a=null),u};return c.clear=function(){i&&(clearTimeout(i),i=null)},c.flush=function(){i&&(u=e.apply(o,a),o=a=null,clearTimeout(i),i=null)},c}t.debounce=t,e.exports=t},94863:function(e){var t,n;t=this,n=function(){"use strict";var e=function(e){return t(e)&&!n(e)};function t(e){return!!e&&"object"==typeof e}function n(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||i(e)}var r="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function i(e){return e.$$typeof===r}function a(e){return Array.isArray(e)?[]:{}}function o(e,t){return!1!==t.clone&&t.isMergeableObject(e)?d(a(e),e,t):e}function s(e,t,n){return e.concat(t).map(function(e){return o(e,n)})}function u(e,t){if(!t.customMerge)return d;var n=t.customMerge(e);return"function"==typeof n?n:d}function c(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter(function(t){return e.propertyIsEnumerable(t)}):[]}function l(e){return Object.keys(e).concat(c(e))}function f(e,t,n){var r={};return n.isMergeableObject(e)&&l(e).forEach(function(t){r[t]=o(e[t],n)}),l(t).forEach(function(i){n.isMergeableObject(t[i])&&e[i]?r[i]=u(i,n)(e[i],t[i],n):r[i]=o(t[i],n)}),r}function d(t,n,r){(r=r||{}).arrayMerge=r.arrayMerge||s,r.isMergeableObject=r.isMergeableObject||e;var i=Array.isArray(n);return i!==Array.isArray(t)?o(n,r):i?r.arrayMerge(t,n,r):f(t,n,r)}return d.all=function(e,t){if(!Array.isArray(e))throw Error("first argument should be an array");return e.reduce(function(e,n){return d(e,n,t)},{})},d},e.exports=n()},7624(e,t){"use strict";function n(e){return e===e.window?e:9===e.nodeType&&(e.defaultView||e.parentWindow)}t.__esModule=!0,t.default=n,e.exports=t.default},87797(e,t,n){"use strict";var r=n(95318);t.__esModule=!0,t.default=s;var i=r(n(53497)),a=/^(top|right|bottom|left)$/,o=/^([+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|))(?!px)[a-z%]+$/i;function s(e){if(!e)throw TypeError("No Element passed to `getComputedStyle()`");var t=e.ownerDocument;return"defaultView"in t?t.defaultView.opener?e.ownerDocument.defaultView.getComputedStyle(e,null):window.getComputedStyle(e,null):{getPropertyValue:function(t){var n=e.style;"float"==(t=(0,i.default)(t))&&(t="styleFloat");var r=e.currentStyle[t]||null;if(null==r&&n&&n[t]&&(r=n[t]),o.test(r)&&!a.test(t)){var s=n.left,u=e.runtimeStyle,c=u&&u.left;c&&(u.left=e.currentStyle.left),n.left="fontSize"===t?"1em":r,r=n.pixelLeft+"px",n.left=s,c&&(u.left=c)}return r}}}e.exports=t.default},10162(e,t,n){"use strict";var r=n(95318);t.__esModule=!0,t.default=l;var i=r(n(53497)),a=r(n(24403)),o=r(n(87797)),s=r(n(91760)),u=n(20702),c=r(n(43293));function l(e,t,n){var r="",l="",f=t;if("string"==typeof t){if(void 0===n)return e.style[(0,i.default)(t)]||(0,o.default)(e).getPropertyValue((0,a.default)(t));(f={})[t]=n}Object.keys(f).forEach(function(t){var n=f[t];n||0===n?(0,c.default)(t)?l+=t+"("+n+") ":r+=(0,a.default)(t)+": "+n+";":(0,s.default)(e,(0,a.default)(t))}),l&&(r+=u.transform+": "+l+";"),e.style.cssText+=";"+r}e.exports=t.default},91760(e,t){"use strict";function n(e,t){return"removeProperty"in e.style?e.style.removeProperty(t):e.style.removeAttribute(t)}t.__esModule=!0,t.default=n,e.exports=t.default},43293(e,t){"use strict";t.__esModule=!0,t.default=r;var n=/^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i;function r(e){return!!(e&&n.test(e))}e.exports=t.default},20702(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d,h,p=n(95318);t.__esModule=!0,t.default=t.animationEnd=t.animationDelay=t.animationTiming=t.animationDuration=t.animationName=t.transitionEnd=t.transitionDuration=t.transitionDelay=t.transitionTiming=t.transitionProperty=t.transform=void 0;var b=p(n(50139)),m="transform";if(t.transform=m,t.animationEnd=a,t.transitionEnd=i,t.transitionDelay=c,t.transitionTiming=u,t.transitionDuration=s,t.transitionProperty=o,t.animationDelay=h,t.animationTiming=d,t.animationDuration=f,t.animationName=l,b.default){var g=y();r=g.prefix,t.transitionEnd=i=g.transitionEnd,t.animationEnd=a=g.animationEnd,t.transform=m=r+"-"+m,t.transitionProperty=o=r+"-transition-property",t.transitionDuration=s=r+"-transition-duration",t.transitionDelay=c=r+"-transition-delay",t.transitionTiming=u=r+"-transition-timing-function",t.animationName=l=r+"-animation-name",t.animationDuration=f=r+"-animation-duration",t.animationTiming=d=r+"-animation-delay",t.animationDelay=h=r+"-animation-timing-function"}var v={transform:m,end:i,property:o,timing:u,delay:c,duration:s};function y(){for(var e,t,n=document.createElement("div").style,r={O:function(e){return"o"+e.toLowerCase()},Moz:function(e){return e.toLowerCase()},Webkit:function(e){return"webkit"+e},ms:function(e){return"MS"+e}},i=Object.keys(r),a="",o=0;o0&&void 0!==arguments[0]?arguments[0]:{},r=n.defaultLayoutOptions,a=void 0===r?{}:r,s=n.algorithms,u=void 0===s?["layered","stress","mrtree","radial","force","disco","sporeOverlap","sporeCompaction","rectpacking"]:s,c=n.workerFactory,l=n.workerUrl;if(i(this,e),this.defaultLayoutOptions=a,this.initialized=!1,void 0===l&&void 0===c)throw Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");var f=c;void 0!==l&&void 0===c&&(f=function(e){return new Worker(e)});var d=f(l);if("function"!=typeof d.postMessage)throw TypeError("Created worker does not provide the required 'postMessage' function.");this.worker=new o(d),this.worker.postMessage({cmd:"register",algorithms:u}).then(function(e){return t.initialized=!0}).catch(console.err)}return r(e,[{key:"layout",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.layoutOptions,r=void 0===n?this.defaultLayoutOptions:n,i=t.logging,a=void 0!==i&&i,o=t.measureExecutionTime,s=void 0!==o&&o;return e?this.worker.postMessage({cmd:"layout",graph:e,layoutOptions:r,options:{logging:a,measureExecutionTime:s}}):Promise.reject(Error("Missing mandatory parameter 'graph'."))}},{key:"knownLayoutAlgorithms",value:function(){return this.worker.postMessage({cmd:"algorithms"})}},{key:"knownLayoutOptions",value:function(){return this.worker.postMessage({cmd:"options"})}},{key:"knownLayoutCategories",value:function(){return this.worker.postMessage({cmd:"categories"})}},{key:"terminateWorker",value:function(){this.worker.terminate()}}]),e}();n.default=a;var o=function(){function e(t){var n=this;if(i(this,e),void 0===t)throw Error("Missing mandatory parameter 'worker'.");this.resolvers={},this.worker=t,this.worker.onmessage=function(e){setTimeout(function(){n.receive(n,e)},0)}}return r(e,[{key:"postMessage",value:function(e){var t=this.id||0;this.id=t+1,e.id=t;var n=this;return new Promise(function(r,i){n.resolvers[t]=function(e,t){e?(n.convertGwtStyleError(e),i(e)):r(t)},n.worker.postMessage(e)})}},{key:"receive",value:function(e,t){var n=t.data,r=e.resolvers[n.id];r&&(delete e.resolvers[n.id],n.error?r(n.error):r(null,n.data))}},{key:"terminate",value:function(){this.worker.terminate&&this.worker.terminate()}},{key:"convertGwtStyleError",value:function(e){if(e){var t=e.__java$exception;t&&(t.cause&&t.cause.backingJsObject&&(e.cause=t.cause.backingJsObject,this.convertGwtStyleError(e.cause)),delete e.__java$exception)}}}]),e}()},{}],2:[function(e,t,n){"use strict";var r=e("./elk-api.js").default;Object.defineProperty(t.exports,"__esModule",{value:!0}),t.exports=r,r.default=r},{"./elk-api.js":1}]},{},[2])(2)},e.exports=t()},55273(e,t,n){"use strict";function r(){}function i(){}function a(){}function o(){}function s(){}function u(){}function c(){}function l(){}function f(){}function d(){}function h(){}function p(){}function b(){}function m(){}function g(){}function v(){}function y(){}function w(){}function _(){}function E(){}function S(){}function k(){}function x(){}function T(){}function M(){}function O(){}function A(){}function L(){}function C(){}function I(){}function D(){}function N(){}function P(){}function R(){}function j(){}function F(){}function Y(){}function B(){}function U(){}function H(){}function $(){}function z(){}function G(){}function W(){}function K(){}function V(){}function q(){}function Z(){}function X(){}function J(){}function Q(){}function ee(){}function et(){}function en(){}function er(){}function ei(){}function ea(){}function eo(){}function es(){}function eu(){}function ec(){}function el(){}function ef(){}function ed(){}function eh(){}function ep(){}function eb(){}function em(){}function eg(){}function ev(){}function ey(){}function ew(){}function e_(){}function eE(){}function eS(){}function ek(){}function ex(){}function eT(){}function eM(){}function eO(){}function eA(){}function eL(){}function eC(){}function eI(){}function eD(){}function eN(){}function eP(){}function eR(){}function ej(){}function eF(){}function eY(){}function eB(){}function eU(){}function eH(){}function e$(){}function ez(){}function eG(){}function eW(){}function eK(){}function eV(){}function eq(){}function eZ(){}function eX(){}function eJ(){}function eQ(){}function e1(){}function e0(){}function e2(){}function e3(){}function e4(){}function e5(){}function e6(){}function e9(){}function e8(){}function e7(){}function te(){}function tt(){}function tn(){}function tr(){}function ti(){}function ta(){}function to(){}function ts(){}function tu(){}function tc(){}function tl(){}function tf(){}function td(){}function th(){}function tp(){}function tb(){}function tm(){}function tg(){}function tv(){}function ty(){}function tw(){}function t_(){}function tE(){}function tS(){}function tk(){}function tx(){}function tT(){}function tM(){}function tO(){}function tA(){}function tL(){}function tC(){}function tI(){}function tD(){}function tN(){}function tP(){}function tR(){}function tj(){}function tF(){}function tY(){}function tB(){}function tU(){}function tH(){}function t$(){}function tz(){}function tG(){}function tW(){}function tK(){}function tV(){}function tq(){}function tZ(){}function tX(){}function tJ(){}function tQ(){}function t1(){}function t0(){}function t2(){}function t3(){}function t4(){}function t5(){}function t6(){}function t9(){}function t8(){}function t7(){}function ne(){}function nt(){}function nn(){}function nr(){}function ni(){}function na(){}function no(){}function ns(){}function nu(){}function nc(){}function nl(){}function nf(){}function nd(){}function nh(){}function np(){}function nb(){}function nm(){}function ng(){}function nv(){}function ny(){}function nw(){}function n_(){}function nE(){}function nS(){}function nk(){}function nx(){}function nT(){}function nM(){}function nO(){}function nA(){}function nL(){}function nC(){}function nI(){}function nD(){}function nN(){}function nP(){}function nR(){}function nj(){}function nF(){}function nY(){}function nB(){}function nU(){}function nH(){}function n$(){}function nz(){}function nG(){}function nW(){}function nK(){}function nV(){}function nq(){}function nZ(){}function nX(){}function nJ(){}function nQ(){}function n1(){}function n0(){}function n2(){}function n3(){}function n4(){}function n5(){}function n6(){}function n9(){}function n8(){}function n7(){}function re(){}function rt(){}function rn(){}function rr(){}function ri(){}function ra(){}function ro(){}function rs(){}function ru(){}function rc(){}function rl(){}function rf(){}function rd(){}function rh(){}function rp(){}function rb(){}function rm(){}function rg(){}function rv(){}function ry(){}function rw(){}function r_(){}function rE(){}function rS(){}function rk(){}function rx(){}function rT(){}function rM(){}function rO(){}function rA(){}function rL(){}function rC(){}function rI(){}function rD(){}function rN(){}function rP(){}function rR(){}function rj(){}function rF(){}function rY(){}function rB(){}function rU(){}function rH(){}function r$(){}function rz(){}function rG(){}function rW(){}function rK(){}function rV(){}function rq(){}function rZ(){}function rX(){}function rJ(){}function rQ(){}function r1(){}function r0(){}function r2(){}function r3(){}function r4(){}function r5(){}function r6(){}function r9(){}function r8(){}function r7(){}function ie(){}function it(){}function ir(){}function ii(){}function ia(){}function io(){}function is(){}function iu(){}function ic(){}function il(){}function id(){}function ih(){}function ip(){}function ib(){}function im(){}function ig(){}function iv(){}function iy(){}function iw(){}function i_(){}function iE(){}function iS(){}function ik(){}function ix(){}function iT(){}function iM(){}function iO(){}function iA(){}function iL(){}function iC(){}function iI(){}function iD(){}function iN(){}function iP(){}function iR(){}function ij(){}function iF(){}function iY(){}function iB(){}function iU(){}function iH(){}function i$(){}function iz(){}function iG(){}function iW(){}function iK(){}function iV(){}function iq(){}function iZ(){}function iX(){}function iJ(){}function iQ(){}function i1(){}function i0(){}function i2(){}function i3(){}function i4(){}function i5(){}function i6(){}function i9(){}function i8(){}function i7(){}function ae(){}function at(){}function an(){}function ar(){}function ai(){}function aa(){}function ao(){}function as(){}function au(){}function ac(){}function al(){}function af(){}function ad(){}function ah(){}function ap(){}function ab(){}function am(){}function ag(){}function av(){}function ay(){}function aw(){}function a_(){}function aE(){}function aS(){}function ak(){}function ax(){}function aT(){}function aM(){}function aO(){}function aA(){}function aL(){}function aC(){}function aI(){}function aD(){}function aN(){}function aP(){}function aR(){}function aj(){}function aF(){}function aY(){}function aB(){}function aU(){}function aH(){}function a$(){}function az(){}function aG(){}function aW(){}function aK(){}function aV(){}function aq(){}function aZ(){}function aX(){}function aJ(){}function aQ(){}function a1(){}function a0(){}function a2(){}function a3(){}function a4(){}function a5(){}function a6(){}function a9(){}function a8(){}function a7(){}function oe(){}function ot(){}function on(){}function or(){}function oi(){}function oa(){}function oo(){}function os(){}function ou(){}function oc(){}function ol(){}function of(){}function od(){}function oh(){}function op(){}function ob(){}function om(){}function og(){}function ov(){}function oy(){}function ow(){}function o_(){}function oE(){}function oS(){}function ok(){}function ox(){}function oT(){}function oM(){}function oO(){}function oA(){}function oL(){}function oC(){}function oI(){}function oD(){}function oN(){}function oP(){}function oR(){}function oj(){}function oF(){}function oY(){}function oB(){}function oU(){}function oH(){}function o$(){}function oz(){}function oG(){}function oW(){}function oK(){}function oV(){}function oq(){}function oZ(){}function oX(){}function oJ(){}function oQ(){}function o1(){}function o0(){}function o2(){}function o3(){}function o4(){}function o5(){}function o6(){}function o9(){}function o8(){}function o7(){}function se(){}function st(){}function sn(){}function sr(){}function si(){}function sa(){}function so(){}function ss(){}function su(){}function sc(){}function sl(){}function sf(){}function sd(){}function sh(){}function sp(){}function sb(){}function sm(){}function sg(){}function sv(){}function sy(){}function sw(){}function s_(){}function sE(){}function sS(){}function sk(){}function sx(){}function sT(){}function sM(){}function sO(){}function sA(){}function sL(){}function sC(){}function sI(){}function sD(){}function sN(){}function sP(){}function sR(){}function sj(){}function sF(){}function sY(){}function sB(){}function sU(){}function sH(){}function s$(){}function sz(){}function sG(){}function sW(){}function sK(){}function sV(){}function sq(){}function sZ(){}function sX(){}function sJ(){}function sQ(){}function s1(){}function s0(){}function s2(){}function s3(){}function s4(){}function s5(){}function s6(){}function s9(){}function s8(){}function s7(){}function ue(){}function ut(){}function un(){}function ur(){}function ui(){}function ua(){}function uo(){}function us(){}function uu(){}function uc(){}function ul(){}function uf(){}function ud(){}function uh(){}function up(){}function ub(){}function um(){}function ug(){}function uv(){}function uy(){}function uw(){}function u_(){}function uE(){}function uS(){}function uk(){}function ux(){}function uT(){}function uM(){}function uO(){}function uA(){}function uL(){}function uC(){}function uI(){}function uD(){}function uN(){}function uP(){}function uR(){}function uj(){}function uF(){}function uY(){}function uB(){}function uU(){}function uH(){}function u$(){}function uz(){}function uG(){}function uW(){}function uK(){}function uV(){}function uq(){}function uZ(){}function uX(){}function uJ(){}function uQ(){}function u1(){}function u0(){}function u2(){}function u3(){}function u4(){}function u5(){}function u6(){}function u9(){}function u8(){}function u7(){}function ce(){}function ct(){}function cn(e){}function cr(e){}function ci(){m4()}function ca(){eug()}function co(){epz()}function cs(){evw()}function cu(){eEg()}function cc(){eCk()}function cl(){egA()}function cf(){egq()}function cd(){_O()}function ch(){_k()}function cp(){DR()}function cb(){_A()}function cm(){erJ()}function cg(){_C()}function cv(){Xi()}function cy(){en6()}function cw(){Jb()}function c_(){Gw()}function cE(){euv()}function cS(){e_z()}function ck(){en9()}function cx(){K9()}function cT(){eBH()}function cM(){egP()}function cO(){G_()}function cA(){eBy()}function cL(){Gv()}function cC(){en8()}function cI(){eoz()}function cD(){Gx()}function cN(){JK()}function cP(){_I()}function cR(){eTK()}function cj(){egj()}function cF(){eiQ()}function cY(){e_L()}function cB(){eCT()}function cU(){ebJ()}function cH(){eTj()}function c$(){eaB()}function cz(){GS()}function cG(){eDn()}function cW(){eTU()}function cK(){eMK()}function cV(){J1()}function cq(){e_C()}function cZ(){eBB()}function cX(){euw()}function cJ(){ed5()}function cQ(){ePm()}function c1(){De()}function c0(){eiM()}function c2(){eD4()}function c3(e){BJ(e)}function c4(e){this.a=e}function c5(e){this.a=e}function c6(e){this.a=e}function c9(e){this.a=e}function c8(e){this.a=e}function c7(e){this.a=e}function le(e){this.a=e}function lt(e){this.a=e}function ln(e){this.a=e}function lr(e){this.a=e}function li(e){this.a=e}function la(e){this.a=e}function lo(e){this.a=e}function ls(e){this.a=e}function lu(e){this.a=e}function lc(e){this.a=e}function ll(e){this.a=e}function lf(e){this.a=e}function ld(e){this.a=e}function lh(e){this.a=e}function lp(e){this.a=e}function lb(e){this.b=e}function lm(e){this.c=e}function lg(e){this.a=e}function lv(e){this.a=e}function ly(e){this.a=e}function lw(e){this.a=e}function l_(e){this.a=e}function lE(e){this.a=e}function lS(e){this.a=e}function lk(e){this.a=e}function lx(e){this.a=e}function lT(e){this.a=e}function lM(e){this.a=e}function lO(e){this.a=e}function lA(e){this.a=e}function lL(e){this.a=e}function lC(e){this.a=e}function lI(e){this.a=e}function lD(e){this.a=e}function lN(){this.a=[]}function lP(e,t){e.a=t}function lR(e,t){e.a=t}function lj(e,t){e.b=t}function lF(e,t){e.b=t}function lY(e,t){e.b=t}function lB(e,t){e.j=t}function lU(e,t){e.g=t}function lH(e,t){e.i=t}function l$(e,t){e.c=t}function lz(e,t){e.d=t}function lG(e,t){e.d=t}function lW(e,t){e.c=t}function lK(e,t){e.k=t}function lV(e,t){e.c=t}function lq(e,t){e.c=t}function lZ(e,t){e.a=t}function lX(e,t){e.a=t}function lJ(e,t){e.f=t}function lQ(e,t){e.a=t}function l1(e,t){e.b=t}function l0(e,t){e.d=t}function l2(e,t){e.i=t}function l3(e,t){e.o=t}function l4(e,t){e.r=t}function l5(e,t){e.a=t}function l6(e,t){e.b=t}function l9(e,t){e.e=t}function l8(e,t){e.f=t}function l7(e,t){e.g=t}function fe(e,t){e.e=t}function ft(e,t){e.f=t}function fn(e,t){e.f=t}function fr(e,t){e.n=t}function fi(e,t){e.a=t}function fa(e,t){e.a=t}function fo(e,t){e.c=t}function fs(e,t){e.c=t}function fu(e,t){e.d=t}function fc(e,t){e.e=t}function fl(e,t){e.g=t}function ff(e,t){e.a=t}function fd(e,t){e.c=t}function fh(e,t){e.d=t}function fp(e,t){e.e=t}function fb(e,t){e.f=t}function fm(e,t){e.j=t}function fg(e,t){e.a=t}function fv(e,t){e.b=t}function fy(e,t){e.a=t}function fw(e){e.b=e.a}function f_(e){e.c=e.d.d}function fE(e){this.d=e}function fS(e){this.a=e}function fk(e){this.a=e}function fx(e){this.a=e}function fT(e){this.a=e}function fM(e){this.a=e}function fO(e){this.a=e}function fA(e){this.a=e}function fL(e){this.a=e}function fC(e){this.a=e}function fI(e){this.a=e}function fD(e){this.a=e}function fN(e){this.a=e}function fP(e){this.a=e}function fR(e){this.a=e}function fj(e){this.b=e}function fF(e){this.b=e}function fY(e){this.b=e}function fB(e){this.a=e}function fU(e){this.a=e}function fH(e){this.a=e}function f$(e){this.c=e}function fz(e){this.c=e}function fG(e){this.c=e}function fW(e){this.a=e}function fK(e){this.a=e}function fV(e){this.a=e}function fq(e){this.a=e}function fZ(e){this.a=e}function fX(e){this.a=e}function fJ(e){this.a=e}function fQ(e){this.a=e}function f1(e){this.a=e}function f0(e){this.a=e}function f2(e){this.a=e}function f3(e){this.a=e}function f4(e){this.a=e}function f5(e){this.a=e}function f6(e){this.a=e}function f9(e){this.a=e}function f8(e){this.a=e}function f7(e){this.a=e}function de(e){this.a=e}function dt(e){this.a=e}function dn(e){this.a=e}function dr(e){this.a=e}function di(e){this.a=e}function da(e){this.a=e}function ds(e){this.a=e}function du(e){this.a=e}function dc(e){this.a=e}function dl(e){this.a=e}function df(e){this.a=e}function dd(e){this.a=e}function dh(e){this.a=e}function dp(e){this.a=e}function db(e){this.a=e}function dm(e){this.a=e}function dg(e){this.a=e}function dv(e){this.a=e}function dy(e){this.a=e}function dw(e){this.a=e}function d_(e){this.a=e}function dE(e){this.a=e}function dS(e){this.a=e}function dk(e){this.a=e}function dx(e){this.a=e}function dT(e){this.a=e}function dM(e){this.a=e}function dO(e){this.e=e}function dA(e){this.a=e}function dL(e){this.a=e}function dC(e){this.a=e}function dI(e){this.a=e}function dD(e){this.a=e}function dN(e){this.a=e}function dP(e){this.a=e}function dR(e){this.a=e}function dj(e){this.a=e}function dF(e){this.a=e}function dY(e){this.a=e}function dB(e){this.a=e}function dU(e){this.a=e}function dH(e){this.a=e}function d$(e){this.a=e}function dz(e){this.a=e}function dG(e){this.a=e}function dW(e){this.a=e}function dK(e){this.a=e}function dV(e){this.a=e}function dq(e){this.a=e}function dZ(e){this.a=e}function dX(e){this.a=e}function dJ(e){this.a=e}function dQ(e){this.a=e}function d1(e){this.a=e}function d0(e){this.a=e}function d2(e){this.a=e}function d3(e){this.a=e}function d4(e){this.a=e}function d5(e){this.a=e}function d6(e){this.a=e}function d9(e){this.a=e}function d8(e){this.a=e}function d7(e){this.a=e}function he(e){this.a=e}function ht(e){this.a=e}function hn(e){this.a=e}function hr(e){this.a=e}function hi(e){this.a=e}function ha(e){this.a=e}function ho(e){this.a=e}function hs(e){this.a=e}function hu(e){this.a=e}function hc(e){this.a=e}function hl(e){this.a=e}function hf(e){this.a=e}function hd(e){this.a=e}function hh(e){this.a=e}function hp(e){this.a=e}function hb(e){this.a=e}function hm(e){this.a=e}function hg(e){this.a=e}function hv(e){this.c=e}function hy(e){this.b=e}function hw(e){this.a=e}function h_(e){this.a=e}function hE(e){this.a=e}function hS(e){this.a=e}function hk(e){this.a=e}function hx(e){this.a=e}function hT(e){this.a=e}function hM(e){this.a=e}function hO(e){this.a=e}function hA(e){this.a=e}function hL(e){this.a=e}function hC(e){this.a=e}function hI(e){this.a=e}function hD(e){this.a=e}function hN(e){this.a=e}function hP(e){this.a=e}function hR(e){this.a=e}function hj(e){this.a=e}function hF(e){this.a=e}function hY(e){this.a=e}function hB(e){this.a=e}function hU(e){this.a=e}function hH(e){this.a=e}function h$(e){this.a=e}function hz(e){this.a=e}function hG(e){this.a=e}function hW(e){this.a=e}function hK(e){this.a=e}function hV(e){this.a=e}function hq(e){this.a=e}function hZ(e){this.a=e}function hX(e){this.a=e}function hJ(e){this.a=e}function hQ(e){this.a=e}function h1(e){this.a=e}function h0(e){this.a=e}function h2(e){this.a=e}function h3(e){this.a=e}function h4(e){this.a=e}function h5(e){this.a=e}function h6(e){this.a=e}function h9(e){this.a=e}function h8(e){this.a=e}function h7(e){this.a=e}function pe(e){this.a=e}function pt(e){this.a=e}function pn(e){this.a=e}function pr(e){this.a=e}function pi(e){this.a=e}function pa(e){this.a=e}function po(e){this.a=e}function ps(e){this.a=e}function pu(e){this.a=e}function pc(e){this.a=e}function pl(e){this.a=e}function pf(e){this.a=e}function pd(e){this.a=e}function ph(e){this.a=e}function pp(e){this.a=e}function pb(e){this.a=e}function pm(e){this.a=e}function pg(e){this.a=e}function pv(e){this.a=e}function py(e){this.a=e}function pw(e){this.a=e}function p_(e){this.a=e}function pE(e){this.a=e}function pS(e){this.a=e}function pk(e){this.a=e}function px(e){this.a=e}function pT(e){this.a=e}function pM(e){this.a=e}function pO(e){this.b=e}function pA(e){this.f=e}function pL(e){this.a=e}function pC(e){this.a=e}function pI(e){this.a=e}function pD(e){this.a=e}function pN(e){this.a=e}function pP(e){this.a=e}function pR(e){this.a=e}function pj(e){this.a=e}function pF(e){this.a=e}function pY(e){this.a=e}function pB(e){this.a=e}function pU(e){this.b=e}function pH(e){this.c=e}function p$(e){this.e=e}function pz(e){this.a=e}function pG(e){this.a=e}function pW(e){this.a=e}function pK(e){this.a=e}function pV(e){this.a=e}function pq(e){this.d=e}function pZ(e){this.a=e}function pX(e){this.a=e}function pJ(e){this.e=e}function pQ(){this.a=0}function p1(){TG(this)}function p0(){Tz(this)}function p2(){Yy(this)}function p3(){UP(this)}function p4(){cn(this)}function p5(){this.c=tgK}function p6(e,t){t.Wb(e)}function p9(e,t){e.b+=t}function p8(e){e.b=new gQ}function p7(e){return e.e}function be(e){return e.a}function bt(e){return e.a}function bn(e){return e.a}function br(e){return e.a}function bi(e){return e.a}function ba(){return null}function bo(){return null}function bs(){yC(),eY2()}function bu(e){e.b.tf(e.e)}function bc(e,t){e.b=t-e.b}function bl(e,t){e.a=t-e.a}function bf(e,t){t.ad(e.a)}function bd(e,t){ekv(t,e)}function bh(e,t,n){e.Od(n,t)}function bp(e,t){e.e=t,t.b=e}function bb(e){Dn(),this.a=e}function bm(e){Dn(),this.a=e}function bg(e){Dn(),this.a=e}function bv(e){Bx(),this.a=e}function by(e){$O(),e0E.be(e)}function bw(){O5.call(this)}function b_(){O5.call(this)}function bE(){bw.call(this)}function bS(){bw.call(this)}function bk(){bw.call(this)}function bx(){bw.call(this)}function bT(){bw.call(this)}function bM(){bw.call(this)}function bO(){bw.call(this)}function bA(){bw.call(this)}function bL(){bw.call(this)}function bC(){bw.call(this)}function bI(){bw.call(this)}function bD(){this.a=this}function bN(){this.Bb|=256}function bP(){this.b=new xW}function bR(){bR=A,new p2}function bj(){bE.call(this)}function bF(e,t){e.length=t}function bY(e,t){P_(e.a,t)}function bB(e,t){eEU(e.c,t)}function bU(e,t){Yf(e.b,t)}function bH(e,t){ebB(e.a,t)}function b$(e,t){elj(e.a,t)}function bz(e,t){eam(e.e,t)}function bG(e){exZ(e.c,e.b)}function bW(e,t){e.kc().Nb(t)}function bK(e){this.a=efh(e)}function bV(){this.a=new p2}function bq(){this.a=new p2}function bZ(){this.a=new p0}function bX(){this.a=new p0}function bJ(){this.a=new p0}function bQ(){this.a=new ey}function b1(){this.a=new Z6}function b0(){this.a=new tt}function b2(){this.a=new w7}function b3(){this.a=new W9}function b4(){this.a=new zZ}function b5(){this.a=new Cz}function b6(){this.a=new p0}function b9(){this.a=new p0}function b8(){this.a=new p0}function b7(){this.a=new p0}function me(){this.d=new p0}function mt(){this.a=new bV}function mn(){this.a=new p2}function mr(){this.b=new p2}function mi(){this.b=new p0}function ma(){this.e=new p0}function mo(){this.d=new p0}function ms(){this.a=new cS}function mu(){p0.call(this)}function mc(){bZ.call(this)}function ml(){CK.call(this)}function mf(){b9.call(this)}function md(){mh.call(this)}function mh(){p4.call(this)}function mp(){p4.call(this)}function mb(){mp.call(this)}function mm(){$m.call(this)}function mg(){$m.call(this)}function mv(){mq.call(this)}function my(){mq.call(this)}function mw(){mq.call(this)}function m_(){mZ.call(this)}function mE(){_n.call(this)}function mS(){oZ.call(this)}function mk(){oZ.call(this)}function mx(){m0.call(this)}function mT(){m0.call(this)}function mM(){p2.call(this)}function mO(){p2.call(this)}function mA(){p2.call(this)}function mL(){bV.call(this)}function mC(){en0.call(this)}function mI(){bN.call(this)}function mD(){Oy.call(this)}function mN(){Oy.call(this)}function mP(){p2.call(this)}function mR(){p2.call(this)}function mj(){p2.call(this)}function mF(){sr.call(this)}function mY(){sr.call(this)}function mB(){mF.call(this)}function mU(){u7.call(this)}function mH(e){eti.call(this,e)}function m$(e){eti.call(this,e)}function mz(e){ln.call(this,e)}function mG(e){wB.call(this,e)}function mW(e){mG.call(this,e)}function mK(e){wB.call(this,e)}function mV(){this.a=new _n}function mq(){this.a=new bV}function mZ(){this.a=new p2}function mX(){this.a=new p0}function mJ(){this.j=new p0}function mQ(){this.a=new aX}function m1(){this.a=new y4}function m0(){this.a=new sn}function m2(){m2=A,e0d=new vm}function m3(){m3=A,e0f=new vb}function m4(){m4=A,e0l=new i}function m5(){m5=A,e0m=new OV}function m6(e){mG.call(this,e)}function m9(e){mG.call(this,e)}function m8(e){ql.call(this,e)}function m7(e){ql.call(this,e)}function ge(e){IJ.call(this,e)}function gt(e){eEb.call(this,e)}function gn(e){w$.call(this,e)}function gr(e){wG.call(this,e)}function gi(e){wG.call(this,e)}function ga(e){wG.call(this,e)}function go(e){Fu.call(this,e)}function gs(e){go.call(this,e)}function gu(){lD.call(this,{})}function gc(e){Og(),this.a=e}function gl(e){e.b=null,e.c=0}function gf(e,t){e.e=t,eA9(e,t)}function gd(e,t){e.a=t,eSG(e)}function gh(e,t,n){e.a[t.g]=n}function gp(e,t,n){evq(n,e,t)}function gb(e,t){In(t.i,e.n)}function gm(e,t){esW(e).td(t)}function gg(e,t){return e*e/t}function gv(e,t){return e.g-t.g}function gy(e){return new lI(e)}function gw(e){return new B_(e)}function g_(e){go.call(this,e)}function gE(e){go.call(this,e)}function gS(e){go.call(this,e)}function gk(e){Fu.call(this,e)}function gx(e){eiJ(),this.a=e}function gT(e){I7(),this.a=e}function gM(e){jK(),this.f=e}function gO(e){jK(),this.f=e}function gA(e){go.call(this,e)}function gL(e){go.call(this,e)}function gC(e){go.call(this,e)}function gI(e){go.call(this,e)}function gD(e){go.call(this,e)}function gN(e){return BJ(e),e}function gP(e){return BJ(e),e}function gR(e){return BJ(e),e}function gj(e){return BJ(e),e}function gF(e){return BJ(e),e}function gY(e){return e.b==e.c}function gB(e){return!!e&&e.b}function gU(e){return!!e&&e.k}function gH(e){return!!e&&e.j}function g$(e){BJ(e),this.a=e}function gz(e){return esR(e),e}function gG(e){Ya(e,e.length)}function gW(e){go.call(this,e)}function gK(e){go.call(this,e)}function gV(e){go.call(this,e)}function gq(e){go.call(this,e)}function gZ(e){go.call(this,e)}function gX(e){go.call(this,e)}function gJ(e){AI.call(this,e,0)}function gQ(){G$.call(this,12,3)}function g1(){g1=A,e0_=new _}function g0(){g0=A,e0y=new r}function g2(){g2=A,e0k=new b}function g3(){g3=A,e0M=new g}function g4(){throw p7(new bO)}function g5(){throw p7(new bO)}function g6(){throw p7(new bO)}function g9(){throw p7(new bO)}function g8(){throw p7(new bO)}function g7(){throw p7(new bO)}function ve(){this.a=Lq(Y9(eUd))}function vt(e){Dn(),this.a=Y9(e)}function vn(e,t){e.Td(t),t.Sd(e)}function vr(e,t){e.a.ec().Mc(t)}function vi(e,t,n){e.c.lf(t,n)}function va(e){gE.call(this,e)}function vo(e){gL.call(this,e)}function vs(){fM.call(this,"")}function vu(){fM.call(this,"")}function vc(){fM.call(this,"")}function vl(){fM.call(this,"")}function vf(e){gE.call(this,e)}function vd(e){fF.call(this,e)}function vh(e){O2.call(this,e)}function vp(e){vd.call(this,e)}function vb(){ls.call(this,null)}function vm(){ls.call(this,null)}function vg(){vg=A,$O()}function vv(){vv=A,e2d=eyz()}function vy(e){return e.a?e.b:0}function vw(e){return e.a?e.b:0}function v_(e,t){return e.a-t.a}function vE(e,t){return e.a-t.a}function vS(e,t){return e.a-t.a}function vk(e,t){return QO(e,t)}function vx(e,t){return z9(e,t)}function vT(e,t){return t in e.a}function vM(e,t){return e.f=t,e}function vO(e,t){return e.b=t,e}function vA(e,t){return e.c=t,e}function vL(e,t){return e.g=t,e}function vC(e,t){return e.a=t,e}function vI(e,t){return e.f=t,e}function vD(e,t){return e.k=t,e}function vN(e,t){return e.a=t,e}function vP(e,t){return e.e=t,e}function vR(e,t){return e.e=t,e}function vj(e,t){return e.f=t,e}function vF(e,t){e.b=!0,e.d=t}function vY(e,t){e.b=new TS(t)}function vB(e,t,n){t.td(e.a[n])}function vU(e,t,n){t.we(e.a[n])}function vH(e,t){return e.b-t.b}function v$(e,t){return e.g-t.g}function vz(e,t){return e.s-t.s}function vG(e,t){return e?0:t-1}function vW(e,t){return e?0:t-1}function vK(e,t){return e?t-1:0}function vV(e,t){return t.Yf(e)}function vq(e,t){return e.b=t,e}function vZ(e,t){return e.a=t,e}function vX(e,t){return e.c=t,e}function vJ(e,t){return e.d=t,e}function vQ(e,t){return e.e=t,e}function v1(e,t){return e.f=t,e}function v0(e,t){return e.a=t,e}function v2(e,t){return e.b=t,e}function v3(e,t){return e.c=t,e}function v4(e,t){return e.c=t,e}function v5(e,t){return e.b=t,e}function v6(e,t){return e.d=t,e}function v9(e,t){return e.e=t,e}function v8(e,t){return e.f=t,e}function v7(e,t){return e.g=t,e}function ye(e,t){return e.a=t,e}function yt(e,t){return e.i=t,e}function yn(e,t){return e.j=t,e}function yr(e,t){return e.k=t,e}function yi(e,t){return e.j=t,e}function ya(e,t){e_z(),Gc(t,e)}function yo(e,t,n){jX(e.a,t,n)}function ys(e){U8.call(this,e)}function yu(e){U8.call(this,e)}function yc(e){I3.call(this,e)}function yl(e){efB.call(this,e)}function yf(e){eta.call(this,e)}function yd(e){HO.call(this,e)}function yh(e){HO.call(this,e)}function yp(){MA.call(this,"")}function yb(){this.a=0,this.b=0}function ym(){this.b=0,this.a=0}function yg(e,t){e.b=0,enh(e,t)}function yv(e,t){e.c=t,e.b=!0}function yy(e,t){return e.c._b(t)}function yw(e){return e.e&&e.e()}function y_(e){return e?e.d:null}function yE(e,t){return ecD(e.b,t)}function yS(e){return e?e.g:null}function yk(e){return e?e.i:null}function yx(e){return LW(e),e.o}function yT(){yT=A,tmc=evO()}function yM(){yM=A,tml=ewS()}function yO(){yO=A,tgg=evL()}function yA(){yA=A,tvE=evA()}function yL(){yL=A,tvS=eSH()}function yC(){yC=A,tmF=enF()}function yI(){throw p7(new bO)}function yD(){throw p7(new bO)}function yN(){throw p7(new bO)}function yP(){throw p7(new bO)}function yR(){throw p7(new bO)}function yj(){throw p7(new bO)}function yF(e){this.a=new w8(e)}function yY(e){eF7(),eBh(this,e)}function yB(e){this.a=new FG(e)}function yU(e,t){for(;e.ye(t););}function yH(e,t){for(;e.sd(t););}function y$(e,t){return e.a+=t,e}function yz(e,t){return e.a+=t,e}function yG(e,t){return e.a+=t,e}function yW(e,t){return e.a+=t,e}function yK(e){return B1(e),e.a}function yV(e){return e.b!=e.d.c}function yq(e){return e.l|e.m<<22}function yZ(e,t){return e.d[t.p]}function yX(e,t){return eA5(e,t)}function yJ(e,t,n){e.splice(t,n)}function yQ(e){e.c?eL3(e):eL4(e)}function y1(e){this.a=0,this.b=e}function y0(){this.a=new eAs(e5I)}function y2(){this.b=new eAs(e5T)}function y3(){this.b=new eAs(e5H)}function y4(){this.b=new eAs(e5H)}function y5(){throw p7(new bO)}function y6(){throw p7(new bO)}function y9(){throw p7(new bO)}function y8(){throw p7(new bO)}function y7(){throw p7(new bO)}function we(){throw p7(new bO)}function wt(){throw p7(new bO)}function wn(){throw p7(new bO)}function wr(){throw p7(new bO)}function wi(){throw p7(new bO)}function wa(){throw p7(new bC)}function wo(){throw p7(new bC)}function ws(e){this.a=new wu(e)}function wu(e){erh(this,e,ey0())}function wc(e){return!e||BV(e)}function wl(e){return -1!=tvJ[e]}function wf(){0!=e1Z&&(e1Z=0),e1J=-1}function wd(){null==eUn&&(eUn=[])}function wh(e,t){eTl(H9(e.a),t)}function wp(e,t){eTl(H9(e.a),t)}function wb(e,t){OC.call(this,e,t)}function wm(e,t){wb.call(this,e,t)}function wg(e,t){this.b=e,this.c=t}function wv(e,t){this.b=e,this.a=t}function wy(e,t){this.a=e,this.b=t}function ww(e,t){this.a=e,this.b=t}function w_(e,t){this.a=e,this.b=t}function wE(e,t){this.a=e,this.b=t}function wS(e,t){this.a=e,this.b=t}function wk(e,t){this.a=e,this.b=t}function wx(e,t){this.a=e,this.b=t}function wT(e,t){this.a=e,this.b=t}function wM(e,t){this.b=e,this.a=t}function wO(e,t){this.b=e,this.a=t}function wA(e,t){this.b=e,this.a=t}function wL(e,t){this.b=e,this.a=t}function wC(e,t){this.f=e,this.g=t}function wI(e,t){this.e=e,this.d=t}function wD(e,t){this.g=e,this.i=t}function wN(e,t){this.a=e,this.b=t}function wP(e,t){this.a=e,this.f=t}function wR(e,t){this.b=e,this.c=t}function wj(e,t){this.a=e,this.b=t}function wF(e,t){this.a=e,this.b=t}function wY(e,t){this.a=e,this.b=t}function wB(e){Oq(e.dc()),this.c=e}function wU(e){this.b=Pp(Y9(e),83)}function wH(e){this.a=Pp(Y9(e),83)}function w$(e){this.a=Pp(Y9(e),15)}function wz(e){this.a=Pp(Y9(e),15)}function wG(e){this.b=Pp(Y9(e),47)}function wW(){this.q=new eB4.Date}function wK(){wK=A,e0V=new L}function wV(){wV=A,e2o=new T}function wq(e){return e.f.c+e.g.c}function wZ(e,t){return e.b.Hc(t)}function wX(e,t){return e.b.Ic(t)}function wJ(e,t){return e.b.Qc(t)}function wQ(e,t){return e.b.Hc(t)}function w1(e,t){return e.c.uc(t)}function w0(e,t){return e.a._b(t)}function w2(e,t){return ecX(e.c,t)}function w3(e,t){return F9(e.b,t)}function w4(e,t){return e>t&&t0}function Ei(e,t){return 0>ecd(e,t)}function Ea(e,t){return e.a.get(t)}function Eo(e,t){return t.split(e)}function Es(e,t){return F9(e.e,t)}function Eu(e){return BJ(e),!1}function Ec(e){Gq.call(this,e,21)}function El(e,t){zL.call(this,e,t)}function Ef(e,t){wC.call(this,e,t)}function Ed(e,t){wC.call(this,e,t)}function Eh(e){BT(),IJ.call(this,e)}function Ep(e,t){jA(e,e.length,t)}function Eb(e,t){Yj(e,e.length,t)}function Em(e,t,n){t.ud(e.a.Ge(n))}function Eg(e,t,n){t.we(e.a.Fe(n))}function Ev(e,t,n){t.td(e.a.Kb(n))}function Ey(e,t,n){e.Mb(n)&&t.td(n)}function Ew(e,t,n){e.splice(t,0,n)}function E_(e,t){return Aa(e.e,t)}function EE(e,t){this.d=e,this.e=t}function ES(e,t){this.b=e,this.a=t}function Ek(e,t){this.b=e,this.a=t}function Ex(e,t){this.b=e,this.a=t}function ET(e,t){this.a=e,this.b=t}function EM(e,t){this.a=e,this.b=t}function EO(e,t){this.a=e,this.b=t}function EA(e,t){this.a=e,this.b=t}function EL(e,t){this.a=e,this.b=t}function EC(e,t){this.b=e,this.a=t}function EI(e,t){this.b=e,this.a=t}function ED(e,t){wC.call(this,e,t)}function EN(e,t){wC.call(this,e,t)}function EP(e,t){wC.call(this,e,t)}function ER(e,t){wC.call(this,e,t)}function Ej(e,t){wC.call(this,e,t)}function EF(e,t){wC.call(this,e,t)}function EY(e,t){wC.call(this,e,t)}function EB(e,t){wC.call(this,e,t)}function EU(e,t){wC.call(this,e,t)}function EH(e,t){wC.call(this,e,t)}function E$(e,t){wC.call(this,e,t)}function Ez(e,t){wC.call(this,e,t)}function EG(e,t){wC.call(this,e,t)}function EW(e,t){wC.call(this,e,t)}function EK(e,t){wC.call(this,e,t)}function EV(e,t){wC.call(this,e,t)}function Eq(e,t){wC.call(this,e,t)}function EZ(e,t){wC.call(this,e,t)}function EX(e,t){this.a=e,this.b=t}function EJ(e,t){this.a=e,this.b=t}function EQ(e,t){this.a=e,this.b=t}function E1(e,t){this.a=e,this.b=t}function E0(e,t){this.a=e,this.b=t}function E2(e,t){this.a=e,this.b=t}function E3(e,t){this.a=e,this.b=t}function E4(e,t){this.a=e,this.b=t}function E5(e,t){this.a=e,this.b=t}function E6(e,t){this.b=e,this.a=t}function E9(e,t){this.b=e,this.a=t}function E8(e,t){this.b=e,this.a=t}function E7(e,t){this.b=e,this.a=t}function Se(e,t){this.c=e,this.d=t}function St(e,t){this.e=e,this.d=t}function Sn(e,t){this.a=e,this.b=t}function Sr(e,t){this.b=t,this.c=e}function Si(e,t){wC.call(this,e,t)}function Sa(e,t){wC.call(this,e,t)}function So(e,t){wC.call(this,e,t)}function Ss(e,t){wC.call(this,e,t)}function Su(e,t){wC.call(this,e,t)}function Sc(e,t){wC.call(this,e,t)}function Sl(e,t){wC.call(this,e,t)}function Sf(e,t){wC.call(this,e,t)}function Sd(e,t){wC.call(this,e,t)}function Sh(e,t){wC.call(this,e,t)}function Sp(e,t){wC.call(this,e,t)}function Sb(e,t){wC.call(this,e,t)}function Sm(e,t){wC.call(this,e,t)}function Sg(e,t){wC.call(this,e,t)}function Sv(e,t){wC.call(this,e,t)}function Sy(e,t){wC.call(this,e,t)}function Sw(e,t){wC.call(this,e,t)}function S_(e,t){wC.call(this,e,t)}function SE(e,t){wC.call(this,e,t)}function SS(e,t){wC.call(this,e,t)}function Sk(e,t){wC.call(this,e,t)}function Sx(e,t){wC.call(this,e,t)}function ST(e,t){wC.call(this,e,t)}function SM(e,t){wC.call(this,e,t)}function SO(e,t){wC.call(this,e,t)}function SA(e,t){wC.call(this,e,t)}function SL(e,t){wC.call(this,e,t)}function SC(e,t){wC.call(this,e,t)}function SI(e,t){wC.call(this,e,t)}function SD(e,t){wC.call(this,e,t)}function SN(e,t){wC.call(this,e,t)}function SP(e,t){wC.call(this,e,t)}function SR(e,t){wC.call(this,e,t)}function Sj(e,t){wC.call(this,e,t)}function SF(e,t){this.b=e,this.a=t}function SY(e,t){this.a=e,this.b=t}function SB(e,t){this.a=e,this.b=t}function SU(e,t){this.a=e,this.b=t}function SH(e,t){this.a=e,this.b=t}function S$(e,t){wC.call(this,e,t)}function Sz(e,t){wC.call(this,e,t)}function SG(e,t){this.b=e,this.d=t}function SW(e,t){wC.call(this,e,t)}function SK(e,t){wC.call(this,e,t)}function SV(e,t){this.a=e,this.b=t}function Sq(e,t){this.a=e,this.b=t}function SZ(e,t){wC.call(this,e,t)}function SX(e,t){wC.call(this,e,t)}function SJ(e,t){wC.call(this,e,t)}function SQ(e,t){wC.call(this,e,t)}function S1(e,t){wC.call(this,e,t)}function S0(e,t){wC.call(this,e,t)}function S2(e,t){wC.call(this,e,t)}function S3(e,t){wC.call(this,e,t)}function S4(e,t){wC.call(this,e,t)}function S5(e,t){wC.call(this,e,t)}function S6(e,t){wC.call(this,e,t)}function S9(e,t){wC.call(this,e,t)}function S8(e,t){wC.call(this,e,t)}function S7(e,t){wC.call(this,e,t)}function ke(e,t){wC.call(this,e,t)}function kt(e,t){wC.call(this,e,t)}function kn(e,t){return Aa(e.c,t)}function kr(e,t){return Aa(t.b,e)}function ki(e,t){return-e.b.Je(t)}function ka(e,t){return Aa(e.g,t)}function ko(e,t){wC.call(this,e,t)}function ks(e,t){wC.call(this,e,t)}function ku(e,t){this.a=e,this.b=t}function kc(e,t){this.a=e,this.b=t}function kl(e,t){this.a=e,this.b=t}function kf(e,t){wC.call(this,e,t)}function kd(e,t){wC.call(this,e,t)}function kh(e,t){wC.call(this,e,t)}function kp(e,t){wC.call(this,e,t)}function kb(e,t){wC.call(this,e,t)}function km(e,t){wC.call(this,e,t)}function kg(e,t){wC.call(this,e,t)}function kv(e,t){wC.call(this,e,t)}function ky(e,t){wC.call(this,e,t)}function kw(e,t){wC.call(this,e,t)}function k_(e,t){wC.call(this,e,t)}function kE(e,t){wC.call(this,e,t)}function kS(e,t){wC.call(this,e,t)}function kk(e,t){wC.call(this,e,t)}function kx(e,t){wC.call(this,e,t)}function kT(e,t){wC.call(this,e,t)}function kM(e,t){this.a=e,this.b=t}function kO(e,t){this.a=e,this.b=t}function kA(e,t){this.a=e,this.b=t}function kL(e,t){this.a=e,this.b=t}function kC(e,t){this.a=e,this.b=t}function kI(e,t){this.a=e,this.b=t}function kD(e,t){this.a=e,this.b=t}function kN(e,t){wC.call(this,e,t)}function kP(e,t){this.a=e,this.b=t}function kR(e,t){this.a=e,this.b=t}function kj(e,t){this.a=e,this.b=t}function kF(e,t){this.a=e,this.b=t}function kY(e,t){this.a=e,this.b=t}function kB(e,t){this.a=e,this.b=t}function kU(e,t){this.b=e,this.a=t}function kH(e,t){this.b=e,this.a=t}function k$(e,t){this.b=e,this.a=t}function kz(e,t){this.b=e,this.a=t}function kG(e,t){this.a=e,this.b=t}function kW(e,t){this.a=e,this.b=t}function kK(e,t){eOU(e.a,Pp(t,56))}function kV(e,t){QM(e.a,Pp(t,11))}function kq(e,t){return Pj(),t!=e}function kZ(){return vv(),new e2d}function kX(){Gk(),this.b=new bV}function kJ(){eAV(),this.a=new bV}function kQ(){Gy(),jG.call(this)}function k1(e,t){wC.call(this,e,t)}function k0(e,t){this.a=e,this.b=t}function k2(e,t){this.a=e,this.b=t}function k3(e,t){this.a=e,this.b=t}function k4(e,t){this.a=e,this.b=t}function k5(e,t){this.a=e,this.b=t}function k6(e,t){this.a=e,this.b=t}function k9(e,t){this.d=e,this.b=t}function k8(e,t){this.d=e,this.e=t}function k7(e,t){this.f=e,this.c=t}function xe(e,t){this.b=e,this.c=t}function xt(e,t){this.i=e,this.g=t}function xn(e,t){this.e=e,this.a=t}function xr(e,t){this.a=e,this.b=t}function xi(e,t){e.i=null,erA(e,t)}function xa(e,t){e&&Um(tmR,e,t)}function xo(e,t){return edG(e.a,t)}function xs(e){return edK(e.c,e.b)}function xu(e){return e?e.dd():null}function xc(e){return null==e?null:e}function xl(e){return typeof e===eUi}function xf(e){return typeof e===eUa}function xd(e){return typeof e===eUo}function xh(e,t){return e.Hd().Xb(t)}function xp(e,t){return ei7(e.Kc(),t)}function xb(e,t){return 0==ecd(e,t)}function xm(e,t){return ecd(e,t)>=0}function xg(e,t){return 0!=ecd(e,t)}function xv(e){return""+(BJ(e),e)}function xy(e,t){return e.substr(t)}function xw(e){return efH(e),e.d.gc()}function x_(e){return eTe(e,e.c),e}function xE(e){return Rb(null==e),e}function xS(e,t){return e.a+=""+t,e}function xk(e,t){return e.a+=""+t,e}function xx(e,t){return e.a+=""+t,e}function xT(e,t){return e.a+=""+t,e}function xM(e,t){return e.a+=""+t,e}function xO(e,t){return e.a+=""+t,e}function xA(e,t){qQ(e,t,e.a,e.a.a)}function xL(e,t){qQ(e,t,e.c.b,e.c)}function xC(e,t,n){eyc(t,eSE(e,n))}function xI(e,t,n){eyc(t,eSE(e,n))}function xD(e,t){eeS(new Ow(e),t)}function xN(e,t){e.q.setTime(Kj(t))}function xP(e,t){FH.call(this,e,t)}function xR(e,t){FH.call(this,e,t)}function xj(e,t){FH.call(this,e,t)}function xF(e){Yy(this),eij(this,e)}function xY(e){return GK(e,0),null}function xB(e){return e.a=0,e.b=0,e}function xU(e,t){return e.a=t.g+1,e}function xH(e,t){return 2==e.j[t.p]}function x$(e){return YZ(Pp(e,79))}function xz(){xz=A,e4r=euY(epE())}function xG(){xG=A,e7$=euY(eAn())}function xW(){this.b=new w8(ee0(12))}function xK(){this.b=0,this.a=!1}function xV(){this.b=0,this.a=!1}function xq(e){this.a=e,ci.call(this)}function xZ(e){this.a=e,ci.call(this)}function xX(e,t){Cm.call(this,e,t)}function xJ(e,t){Ii.call(this,e,t)}function xQ(e,t){xt.call(this,e,t)}function x1(e,t){eaN.call(this,e,t)}function x0(e,t){AA.call(this,e,t)}function x2(e,t){_5(),Um(tmU,e,t)}function x3(e,t){return Az(e.a,0,t)}function x4(e,t){return e.a.a.a.cc(t)}function x5(e,t){return xc(e)===xc(t)}function x6(e,t){return elN(e.a,t.a)}function x9(e,t){return ME(e.a,t.a)}function x8(e,t){return YM(e.a,t.a)}function x7(e,t){return e.indexOf(t)}function Te(e,t){return e==t?0:e?1:-1}function Tt(e){return e<10?"0"+e:""+e}function Tn(e){return Y9(e),new xq(e)}function Tr(e){return Mk(e.l,e.m,e.h)}function Ti(e){return zy((BJ(e),e))}function Ta(e){return zy((BJ(e),e))}function To(e,t){return ME(e.g,t.g)}function Ts(e){return typeof e===eUa}function Tu(e){return e==e8f||e==e8p}function Tc(e){return e==e8f||e==e8d}function Tl(e){return QI(e.b.b,e,0)}function Tf(e){this.a=kZ(),this.b=e}function Td(e){this.a=kZ(),this.b=e}function Th(e,t){return P_(e.a,t),t}function Tp(e,t){return P_(e.c,t),e}function Tb(e,t){return eat(e.a,t),e}function Tm(e,t){return Dj(),t.a+=e}function Tg(e,t){return Dj(),t.a+=e}function Tv(e,t){return Dj(),t.c+=e}function Ty(e,t){Qe(e,0,e.length,t)}function Tw(){fJ.call(this,new qh)}function T_(){jp.call(this,0,0,0,0)}function TE(){Hr.call(this,0,0,0,0)}function TS(e){this.a=e.a,this.b=e.b}function Tk(e){return e==tpm||e==tpg}function Tx(e){return e==tpy||e==tpb}function TT(e){return e==tss||e==tso}function TM(e){return e!=tbc&&e!=tbl}function TO(e){return e.Lg()&&e.Mg()}function TA(e){return UB(Pp(e,118))}function TL(e){return eat(new K2,e)}function TC(e,t){return new eaN(t,e)}function TI(e,t){return new eaN(t,e)}function TD(e,t,n){ent(e,t),enn(e,n)}function TN(e,t,n){ena(e,t),eni(e,n)}function TP(e,t,n){eno(e,t),ens(e,n)}function TR(e,t,n){enr(e,t),enc(e,n)}function Tj(e,t,n){enu(e,t),enl(e,n)}function TF(e,t){euc(e,t),enp(e,e.D)}function TY(e){k7.call(this,e,!0)}function TB(e,t,n){L3.call(this,e,t,n)}function TU(e){eLQ(),ead.call(this,e)}function TH(){Ef.call(this,"Head",1)}function T$(){Ef.call(this,"Tail",3)}function Tz(e){e.c=Je(e1R,eUp,1,0,5,1)}function TG(e){e.a=Je(e1R,eUp,1,8,5,1)}function TW(e){ety(e.xf(),new dh(e))}function TK(e){return null!=e?esj(e):0}function TV(e,t){return etg(t,zY(e))}function Tq(e,t){return etg(t,zY(e))}function TZ(e,t){return e[e.length]=t}function TX(e,t){return e[e.length]=t}function TJ(e){return Ph(e.b.Kc(),e.a)}function TQ(e,t){return erb(Bi(e.d),t)}function T1(e,t){return erb(Bi(e.g),t)}function T0(e,t){return erb(Bi(e.j),t)}function T2(e,t){Cm.call(this,e.b,t)}function T3(e){jp.call(this,e,e,e,e)}function T4(e){return e.b&&ePE(e),e.a}function T5(e){return e.b&&ePE(e),e.c}function T6(e,t){!e2M&&(e.b=t)}function T9(e,t,n){return Bc(e,t,n),n}function T8(e,t,n){Bc(e.c[t.g],t.g,n)}function T7(e,t,n){Pp(e.c,69).Xh(t,n)}function Me(e,t,n){TP(n,n.i+e,n.j+t)}function Mt(e,t){JL(qt(e.a),Gj(t))}function Mn(e,t){JL(QX(e.a),GF(t))}function Mr(e){eBG(),pJ.call(this,e)}function Mi(e){return null==e?0:esj(e)}function Ma(){Ma=A,tuT=new efY(e59)}function Mo(){Mo=A,new Ms,new p0}function Ms(){new p2,new p2,new p2}function Mu(){Mu=A,bR(),e0S=new p2}function Mc(){Mc=A,eB4.Math.log(2)}function Ml(){Ml=A,tgZ=(_Z(),tmE)}function Mf(){throw p7(new gW(e1O))}function Md(){throw p7(new gW(e1O))}function Mh(){throw p7(new gW(e1A))}function Mp(){throw p7(new gW(e1A))}function Mb(e){this.a=e,PS.call(this,e)}function Mm(e){this.a=e,wU.call(this,e)}function Mg(e){this.a=e,wU.call(this,e)}function Mv(e,t){jM(e.c,e.c.length,t)}function My(e){return e.at?1:0}function MS(e,t){return ecd(e,t)>0?e:t}function Mk(e,t,n){return{l:e,m:t,h:n}}function Mx(e,t){null!=e.a&&kV(t,e.a)}function MT(e){e.a=new C,e.c=new C}function MM(e){this.b=e,this.a=new p0}function MO(e){this.b=new e1,this.a=e}function MA(e){CW.call(this),this.a=e}function ML(){Ef.call(this,"Range",2)}function MC(){evR(),this.a=new eAs(e4k)}function MI(e,t){Y9(t),Uz(e).Jc(new d)}function MD(e,t){return GE(),t.n.b+=e}function MN(e,t,n){return Um(e.g,n,t)}function MP(e,t,n){return Um(e.k,n,t)}function MR(e,t){return Um(e.a,t.a,t)}function Mj(e,t,n){return eho(t,n,e.c)}function MF(e){return new kl(e.c,e.d)}function MY(e){return new kl(e.c,e.d)}function MB(e){return new kl(e.a,e.b)}function MU(e,t){return ej8(e.a,t,null)}function MH(e){Gs(e,null),Go(e,null)}function M$(e){GA(e,null),GL(e,null)}function Mz(){AA.call(this,null,null)}function MG(){AL.call(this,null,null)}function MW(e){this.a=e,p2.call(this)}function MK(e){this.b=(Hj(),new f$(e))}function MV(e){e.j=Je(e18,eUP,310,0,0,1)}function Mq(e,t,n){e.c.Vc(t,Pp(n,133))}function MZ(e,t,n){e.c.ji(t,Pp(n,133))}function MX(e,t){eRT(e),e.Gc(Pp(t,15))}function MJ(e,t){return eR4(e.c,e.b,t)}function MQ(e,t){return new O6(e.Kc(),t)}function M1(e,t){return -1!=eoD(e.Kc(),t)}function M0(e,t){return null!=e.a.Bc(t)}function M2(e){return e.Ob()?e.Pb():null}function M3(e){return ehv(e,0,e.length)}function M4(e,t){return null!=e&&ebs(e,t)}function M5(e,t){e.q.setHours(t),eNq(e,t)}function M6(e,t){e.c&&(Re(t),zd(t))}function M9(e,t,n){Pp(e.Kb(n),164).Nb(t)}function M8(e,t,n){return ejq(e,t,n),n}function M7(e,t,n){e.a=1502^t,e.b=n^e$d}function Oe(e,t,n){return e.a[t.g][n.g]}function Ot(e,t){return e.a[t.c.p][t.p]}function On(e,t){return e.e[t.c.p][t.p]}function Or(e,t){return e.c[t.c.p][t.p]}function Oi(e,t){return e.j[t.p]=eOo(t)}function Oa(e,t){return ZZ(e.f,t.tg())}function Oo(e,t){return ZZ(e.b,t.tg())}function Os(e,t){return e.a0?t*t/e:t*t*100}function Li(e,t){return e>0?t/(e*e):100*t}function La(e,t,n){return P_(t,ef5(e,n))}function Lo(e,t,n){J1(),e.Xe(t)&&n.td(e)}function Ls(e,t,n){var r;(r=e.Zc(t)).Rb(n)}function Lu(e,t,n){return e.a+=t,e.b+=n,e}function Lc(e,t,n){return e.a*=t,e.b*=n,e}function Ll(e,t,n){return e.a-=t,e.b-=n,e}function Lf(e,t){return e.a=t.a,e.b=t.b,e}function Ld(e){return e.a=-e.a,e.b=-e.b,e}function Lh(e){this.c=e,this.a=1,this.b=1}function Lp(e){this.c=e,eno(e,0),ens(e,0)}function Lb(e){_n.call(this),enD(this,e)}function Lm(e){eBp(),p8(this),this.mf(e)}function Lg(e,t){_0(),AA.call(this,e,t)}function Lv(e,t){_2(),AL.call(this,e,t)}function Ly(e,t){_2(),AL.call(this,e,t)}function Lw(e,t){_2(),Lv.call(this,e,t)}function L_(e,t,n){JY.call(this,e,t,n,2)}function LE(e,t){Ml(),jd.call(this,e,t)}function LS(e,t){Ml(),LE.call(this,e,t)}function Lk(e,t){Ml(),LE.call(this,e,t)}function Lx(e,t){Ml(),Lk.call(this,e,t)}function LT(e,t){Ml(),jd.call(this,e,t)}function LM(e,t){Ml(),LT.call(this,e,t)}function LO(e,t){Ml(),jd.call(this,e,t)}function LA(e,t){return e.c.Fc(Pp(t,133))}function LL(e,t,n){return eP9(Qq(e,t),n)}function LC(e,t,n){return t.Qk(e.e,e.c,n)}function LI(e,t,n){return t.Rk(e.e,e.c,n)}function LD(e,t){return ecv(e.e,Pp(t,49))}function LN(e,t,n){elm(QX(e.a),t,GF(n))}function LP(e,t,n){elm(qt(e.a),t,Gj(n))}function LR(e,t){t.$modCount=e.$modCount}function Lj(){Lj=A,tcV=new pO("root")}function LF(){LF=A,tmB=new mx,new mT}function LY(){this.a=new zu,this.b=new zu}function LB(){en0.call(this),this.Bb|=eH3}function LU(){wC.call(this,"GROW_TREE",0)}function LH(e){return null==e?null:eYt(e)}function L$(e){return null==e?null:eEO(e)}function Lz(e){return null==e?null:efF(e)}function LG(e){return null==e?null:efF(e)}function LW(e){null==e.o&&eMb(e)}function LK(e){return Rb(null==e||xl(e)),e}function LV(e){return Rb(null==e||xf(e)),e}function Lq(e){return Rb(null==e||xd(e)),e}function LZ(e){this.q=new eB4.Date(Kj(e))}function LX(e,t){this.c=e,wI.call(this,e,t)}function LJ(e,t){this.a=e,LX.call(this,e,t)}function LQ(e,t){this.d=e,f_(this),this.b=t}function L1(e,t){Jo.call(this,e),this.a=t}function L0(e,t){Jo.call(this,e),this.a=t}function L2(e){edL.call(this,0,0),this.f=e}function L3(e,t,n){XS.call(this,e,t,n,null)}function L4(e,t,n){XS.call(this,e,t,n,null)}function L5(e,t,n){return 0>=e.ue(t,n)?n:t}function L6(e,t,n){return 0>=e.ue(t,n)?t:n}function L9(e,t){return Pp(eef(e.b,t),149)}function L8(e,t){return Pp(eef(e.c,t),229)}function L7(e){return Pp(RJ(e.a,e.b),287)}function Ce(e){return new kl(e.c,e.d+e.a)}function Ct(e){return GE(),TT(Pp(e,197))}function Cn(){Cn=A,e4i=el9((ed6(),tbq))}function Cr(e,t){t.a?eLc(e,t):Ai(e.a,t.b)}function Ci(e,t){!e2M&&P_(e.a,t)}function Ca(e,t){return _k(),eag(t.d.i,e)}function Co(e,t){return erJ(),new eIu(t,e)}function Cs(e,t){return $C(t,ezr),e.f=t,e}function Cu(e,t,n){return n=eDg(e,t,3,n)}function Cc(e,t,n){return n=eDg(e,t,6,n)}function Cl(e,t,n){return n=eDg(e,t,9,n)}function Cf(e,t,n){++e.j,e.Ki(),X8(e,t,n)}function Cd(e,t,n){++e.j,e.Hi(t,e.oi(t,n))}function Ch(e,t,n){var r;(r=e.Zc(t)).Rb(n)}function Cp(e,t,n){return ePT(e.c,e.b,t,n)}function Cb(e,t){return(t&eUu)%e.d.length}function Cm(e,t){pO.call(this,e),this.a=t}function Cg(e,t){pH.call(this,e),this.a=t}function Cv(e,t){pH.call(this,e),this.a=t}function Cy(e,t){this.c=e,eta.call(this,t)}function Cw(e,t){this.a=e,pU.call(this,t)}function C_(e,t){this.a=e,pU.call(this,t)}function CE(e){this.a=(enG(e,eU3),new XM(e))}function CS(e){this.a=(enG(e,eU3),new XM(e))}function Ck(e){return e.a||(e.a=new h),e.a}function Cx(e){return e>8?0:e+1}function CT(e,t){return OQ(),e==t?0:e?1:-1}function CM(e,t,n){return jT(e,Pp(t,22),n)}function CO(e,t,n){return e.apply(t,n)}function CA(e,t,n){return e.a+=ehv(t,0,n),e}function CL(e,t){var n;return n=e.e,e.e=t,n}function CC(e,t){var n;(n=e[e$c]).call(e,t)}function CI(e,t){var n;(n=e[e$c]).call(e,t)}function CD(e,t){e.a.Vc(e.b,t),++e.b,e.c=-1}function CN(e){Yy(e.e),e.d.b=e.d,e.d.a=e.d}function CP(e){e.b?CP(e.b):e.f.c.zc(e.e,e.d)}function CR(e,t,n){_w(),lP(e,t.Ce(e.a,n))}function Cj(e,t){return y_(ehn(e.a,t,!0))}function CF(e,t){return y_(ehr(e.a,t,!0))}function CY(e,t){return vk(Array(t),e)}function CB(e){return String.fromCharCode(e)}function CU(e){return null==e?null:e.message}function CH(){this.a=new p0,this.b=new p0}function C$(){this.a=new tt,this.b=new bP}function Cz(){this.b=new yb,this.c=new p0}function CG(){this.d=new yb,this.e=new yb}function CW(){this.n=new yb,this.o=new yb}function CK(){this.n=new mp,this.i=new TE}function CV(){this.a=new cg,this.b=new i_}function Cq(){this.a=new p0,this.d=new p0}function CZ(){this.b=new bV,this.a=new bV}function CX(){this.b=new p2,this.a=new p2}function CJ(){this.b=new y2,this.a=new ay}function CQ(){CK.call(this),this.a=new yb}function C1(e){eaD.call(this,e,(Qu(),e2D))}function C0(e,t,n,r){jp.call(this,e,t,n,r)}function C2(e,t,n){null!=n&&ern(t,emI(e,n))}function C3(e,t,n){null!=n&&err(t,emI(e,n))}function C4(e,t,n){return n=eDg(e,t,11,n)}function C5(e,t){return e.a+=t.a,e.b+=t.b,e}function C6(e,t){return e.a-=t.a,e.b-=t.b,e}function C9(e,t){return e.n.a=(BJ(t),t+10)}function C8(e,t){return e.n.a=(BJ(t),t+10)}function C7(e,t){return t==e||ev9(eOg(t),e)}function Ie(e,t){return null==Um(e.a,t,"")}function It(e,t){return _k(),!eag(t.d.i,e)}function In(e,t){Tk(e.f)?eMi(e,t):ewz(e,t)}function Ir(e,t){var n;return t.Hh(e.a)}function Ii(e,t){gE.call(this,eJT+e+eXH+t)}function Ia(e,t,n,r){FQ.call(this,e,t,n,r)}function Io(e,t,n,r){FQ.call(this,e,t,n,r)}function Is(e,t,n,r){Io.call(this,e,t,n,r)}function Iu(e,t,n,r){F1.call(this,e,t,n,r)}function Ic(e,t,n,r){F1.call(this,e,t,n,r)}function Il(e,t,n,r){F1.call(this,e,t,n,r)}function If(e,t,n,r){Ic.call(this,e,t,n,r)}function Id(e,t,n,r){Ic.call(this,e,t,n,r)}function Ih(e,t,n,r){Il.call(this,e,t,n,r)}function Ip(e,t,n,r){Id.call(this,e,t,n,r)}function Ib(e,t,n,r){FZ.call(this,e,t,n,r)}function Im(e,t,n){this.a=e,AI.call(this,t,n)}function Ig(e,t,n){this.c=t,this.b=n,this.a=e}function Iv(e,t,n){return e.d=Pp(t.Kb(n),164)}function Iy(e,t){return e.Aj().Nh().Kh(e,t)}function Iw(e,t){return e.Aj().Nh().Ih(e,t)}function I_(e,t){return BJ(e),xc(e)===xc(t)}function IE(e,t){return BJ(e),xc(e)===xc(t)}function IS(e,t){return y_(ehn(e.a,t,!1))}function Ik(e,t){return y_(ehr(e.a,t,!1))}function Ix(e,t){return e.b.sd(new EM(e,t))}function IT(e,t){return e.b.sd(new EO(e,t))}function IM(e,t){return e.b.sd(new EA(e,t))}function IO(e,t,n){return e.lastIndexOf(t,n)}function IA(e,t,n){return elN(e[t.b],e[n.b])}function IL(e,t){return eo3(t,(eBy(),tat),e)}function IC(e,t){return ME(t.a.d.p,e.a.d.p)}function II(e,t){return ME(e.a.d.p,t.a.d.p)}function ID(e,t){return elN(e.c-e.s,t.c-t.s)}function IN(e){return e.c?QI(e.c.a,e,0):-1}function IP(e){return e<100?null:new yf(e)}function IR(e){return e==tba||e==tbs||e==tbo}function Ij(e,t){return M4(t,15)&&eCc(e.c,t)}function IF(e,t){!e2M&&t&&(e.d=t)}function IY(e,t){var n;return!!esq(e,n=t)}function IB(e,t){this.c=e,YC.call(this,e,t)}function IU(e){this.c=e,xj.call(this,eUY,0)}function IH(e,t){Px.call(this,e,e.length,t)}function I$(e,t,n){return Pp(e.c,69).lk(t,n)}function Iz(e,t,n){return Pp(e.c,69).mk(t,n)}function IG(e,t,n){return LC(e,Pp(t,332),n)}function IW(e,t,n){return LI(e,Pp(t,332),n)}function IK(e,t,n){return ey1(e,Pp(t,332),n)}function IV(e,t,n){return e_t(e,Pp(t,332),n)}function Iq(e,t){return null==t?null:ecA(e.b,t)}function IZ(e){return xf(e)?(BJ(e),e):e.ke()}function IX(e){return!isNaN(e)&&!isFinite(e)}function IJ(e){Dn(),this.a=(Hj(),new vd(e))}function IQ(e){Pj(),this.d=e,this.a=new p1}function I1(e,t,n){this.a=e,this.b=t,this.c=n}function I0(e,t,n){this.a=e,this.b=t,this.c=n}function I2(e,t,n){this.d=e,this.b=n,this.a=t}function I3(e){MT(this),HC(this),er7(this,e)}function I4(e){Tz(this),PO(this.c,0,e.Pc())}function I5(e){BH(e.a),Jl(e.c,e.b),e.b=null}function I6(e){this.a=e,wK(),eap(Date.now())}function I9(){I9=A,e2G=new r,e2W=new r}function I8(){I8=A,e2h=new I,e2p=new D}function I7(){I7=A,tmY=Je(e1R,eUp,1,0,5,1)}function De(){De=A,tgH=Je(e1R,eUp,1,0,5,1)}function Dt(){Dt=A,tg$=Je(e1R,eUp,1,0,5,1)}function Dn(){Dn=A,new bb((Hj(),Hj(),e2r))}function Dr(e){return Qu(),eeM((Qc(),e2j),e)}function Di(e){return eum(),eeM((XC(),e2$),e)}function Da(e){return epC(),eeM((qk(),e3d),e)}function Do(e){return eeR(),eeM((qx(),e3b),e)}function Ds(e){return eCp(),eeM((eaF(),e3I),e)}function Du(e){return etx(),eeM((XO(),e3R),e)}function Dc(e){return Qs(),eeM((XA(),e3B),e)}function Dl(e){return QQ(),eeM((XL(),e3z),e)}function Df(e){return eBW(),eeM((xz(),e4r),e)}function Dd(e){return eaY(),eeM((Qf(),e4l),e)}function Dh(e){return ep7(),eeM((Qd(),e4b),e)}function Dp(e){return ebe(),eeM((Qh(),e6z),e)}function Db(e){return _y(),eeM((Vt(),e6W),e)}function Dm(e){return eej(),eeM((qT(),e9h),e)}function Dg(e){return QJ(),eeM((XI(),e96),e)}function Dv(e){return e_x(),eeM((eeW(),e8a),e)}function Dy(e){return eok(),eeM((Ql(),e8b),e)}function Dw(e){return ec4(),eeM((XD(),e8T),e)}function D_(e,t){if(!e)throw p7(new gL(t))}function DE(e){return eEn(),eeM((etQ(),e8R),e)}function DS(e){jp.call(this,e.d,e.c,e.a,e.b)}function Dk(e){jp.call(this,e.d,e.c,e.a,e.b)}function Dx(e,t,n){this.b=e,this.c=t,this.a=n}function DT(e,t,n){this.b=e,this.a=t,this.c=n}function DM(e,t,n){this.a=e,this.b=t,this.c=n}function DO(e,t,n){this.a=e,this.b=t,this.c=n}function DA(e,t,n){this.a=e,this.b=t,this.c=n}function DL(e,t,n){this.a=e,this.b=t,this.c=n}function DC(e,t,n){this.b=e,this.a=t,this.c=n}function DI(e,t,n){this.e=t,this.b=e,this.d=n}function DD(e,t,n){return _w(),e.a.Od(t,n),t}function DN(e){var t;return(t=new ew).e=e,t}function DP(e){var t;return(t=new me).b=e,t}function DR(){DR=A,e8V=new nd,e8q=new nh}function Dj(){Dj=A,e75=new rB,e76=new rU}function DF(e){return eoE(),eeM((Qb(),e7X),e)}function DY(e){return eoS(),eeM((Qg(),tet),e)}function DB(e){return eLz(),eeM((ei3(),tek),e)}function DU(e){return eSg(),eeM((et2(),teI),e)}function DH(e){return Jp(),eeM((qI(),teP),e)}function D$(e){return en7(),eeM((XN(),teY),e)}function Dz(e){return ey4(),eeM((eeU(),tes),e)}function DG(e){return erX(),eeM((Xj(),teb),e)}function DW(e){return enB(),eeM((XP(),te$),e)}function DK(e){return eb6(),eeM((eeY(),teq),e)}function DV(e){return eeF(),eeM((qO(),teJ),e)}function Dq(e){return eoG(),eeM((XR(),te2),e)}function DZ(e){return eEf(),eeM((et6(),te7),e)}function DX(e){return Qx(),eeM((qA(),ttn),e)}function DJ(e){return eyd(),eeM((et4(),ttc),e)}function DQ(e){return e_3(),eeM((et3(),ttm),e)}function D1(e){return eLR(),eeM((eoH(),ttM),e)}function D0(e){return eaU(),eeM((XY(),ttC),e)}function D2(e){return Q1(),eeM((XF(),ttP),e)}function D3(e){return K6(),eeM((qD(),ttF),e)}function D4(e){return ef_(),eeM((eeH(),tnF),e)}function D5(e){return ewY(),eeM((et5(),tst),e)}function D6(e){return euJ(),eeM((XB(),tsa),e)}function D9(e){return ebk(),eeM((Qv(),tsl),e)}function D8(e){return enY(),eeM((X$(),tsR),e)}function D7(e){return eOJ(),eeM((ei2(),tsx),e)}function Ne(e){return esn(),eeM((XH(),tsA),e)}function Nt(e){return Q0(),eeM((qC(),tsI),e)}function Nn(e){return ei0(),eeM((XU(),tsB),e)}function Nr(e){return ebG(),eeM((eeB(),tsm),e)}function Ni(e){return Xo(),eeM((qL(),ts$),e)}function Na(e){return euy(),eeM((XG(),tsK),e)}function No(e){return eiO(),eeM((XW(),tsX),e)}function Ns(e){return eox(),eeM((Xz(),ts0),e)}function Nu(e){return enU(),eeM((XK(),tuo),e)}function Nc(e){return qG(),eeM((qP(),tud),e)}function Nl(e){return zs(),eeM((qR(),tu_),e)}function Nf(e){return zQ(),eeM((qj(),tuk),e)}function Nd(e){return Xa(),eeM((qN(),tu$),e)}function Nh(e){return zo(),eeM((qF(),tuX),e)}function Np(e){return egR(),eeM((Qp(),tu2),e)}function Nb(e){return eS_(),eeM((et9(),tu7),e)}function Nm(e){return z1(),eeM((qU(),tcB),e)}function Ng(e){return erZ(),eeM((qB(),tcX),e)}function Nv(e){return Kn(),eeM((qY(),tc$),e)}function Ny(e){return efx(),eeM((XV(),tc0),e)}function Nw(e){return J0(),eeM((qH(),tc4),e)}function N_(e){return eub(),eeM((Xq(),tc8),e)}function NE(e){return emC(),eeM((Qm(),tlA),e)}function NS(e){return ei1(),eeM((XX(),tlD),e)}function Nk(e){return efS(),eeM((XZ(),tlj),e)}function Nx(e){return eOB(),eeM((eeG(),tfl),e)}function NT(e){return efk(),eeM((XJ(),tfp),e)}function NM(e){return _D(),eeM((K7(),tfm),e)}function NO(e){return _N(),eeM((K8(),tfv),e)}function NA(e){return Xs(),eeM((qz(),tf_),e)}function NL(e){return eEM(),eeM((ee$(),tfM),e)}function NC(e){return _P(),eeM((Ve(),tf7),e)}function NI(e){return eoT(),eeM((q$(),tdn),e)}function ND(e){return epx(),eeM((eez(),tdb),e)}function NN(e){return eSd(),eeM((ei4(),tdk),e)}function NP(e){return ebx(),eeM((et0(),tdD),e)}function NR(e){return eyY(),eeM((et1(),tdJ),e)}function Nj(e){return eB$(),eeM((xG(),e7$),e)}function NF(e){return erq(),eeM((qM(),e8K),e)}function NY(e){return ec3(),eeM((eeK(),tpw),e)}function NB(e){return etT(),eeM((X1(),tpk),e)}function NU(e){return efE(),eeM((Q_(),tpA),e)}function NH(e){return e_a(),eeM((et7(),tpR),e)}function N$(e){return eck(),eeM((XQ(),tpK),e)}function Nz(e){return egF(),eeM((Qw(),tpJ),e)}function NG(e){return eT7(),eeM((eaj(),tp8),e)}function NW(e){return epT(),eeM((eeV(),tbi),e)}function NK(e){return ewf(),eeM((etC(),tbf),e)}function NV(e){return ekU(),eeM((et8(),tbv),e)}function Nq(e){return ed6(),eeM((QS(),tbZ),e)}function NZ(e){return eI3(),eeM((eo$(),tb6),e)}function NX(e){return eYu(),eeM((eeq(),tbB),e)}function NJ(e){return edM(),eeM((QE(),tmt),e)}function NQ(e){return eup(),eeM((Qy(),tmo),e)}function N1(e){return eTy(),eeM((ei5(),tmP),e)}function N0(e,t){return BJ(e),e+(BJ(t),t)}function N2(e,t){return wK(),JL(H9(e.a),t)}function N3(e,t){return wK(),JL(H9(e.a),t)}function N4(e,t){this.c=e,this.a=t,this.b=t-e}function N5(e,t,n){this.a=e,this.b=t,this.c=n}function N6(e,t,n){this.a=e,this.b=t,this.c=n}function N9(e,t,n){this.a=e,this.b=t,this.c=n}function N8(e,t,n){this.a=e,this.b=t,this.c=n}function N7(e,t,n){this.a=e,this.b=t,this.c=n}function Pe(e,t,n){this.e=e,this.a=t,this.c=n}function Pt(e,t,n){Ml(),zl.call(this,e,t,n)}function Pn(e,t,n){Ml(),BP.call(this,e,t,n)}function Pr(e,t,n){Ml(),BP.call(this,e,t,n)}function Pi(e,t,n){Ml(),BP.call(this,e,t,n)}function Pa(e,t,n){Ml(),Pn.call(this,e,t,n)}function Po(e,t,n){Ml(),Pn.call(this,e,t,n)}function Ps(e,t,n){Ml(),Po.call(this,e,t,n)}function Pu(e,t,n){Ml(),Pr.call(this,e,t,n)}function Pc(e,t,n){Ml(),Pi.call(this,e,t,n)}function Pl(e,t){return Y9(e),Y9(t),new wx(e,t)}function Pf(e,t){return Y9(e),Y9(t),new Rn(e,t)}function Pd(e,t){return Y9(e),Y9(t),new Rr(e,t)}function Ph(e,t){return Y9(e),Y9(t),new wM(e,t)}function Pp(e,t){return Rb(null==e||ebs(e,t)),e}function Pb(e){var t;return t=new p0,eel(t,e),t}function Pm(e){var t;return t=new bV,eel(t,e),t}function Pg(e){var t;return ein(t=new b2,e),t}function Pv(e){var t;return ein(t=new _n,e),t}function Py(e){return e.e||(e.e=new p0),e.e}function Pw(e){return e.c||(e.c=new sk),e.c}function P_(e,t){return e.c[e.c.length]=t,!0}function PE(e,t){this.c=e,this.b=t,this.a=!1}function PS(e){this.d=e,f_(this),this.b=Ft(e.d)}function Pk(){this.a=";,;",this.b="",this.c=""}function Px(e,t,n){F$.call(this,t,n),this.a=e}function PT(e,t,n){this.b=e,xP.call(this,t,n)}function PM(e,t,n){this.c=e,EE.call(this,t,n)}function PO(e,t,n){ekp(n,0,e,t,n.length,!1)}function PA(e,t,n,r,i){e.b=t,e.c=n,e.d=r,e.a=i}function PL(e,t){t&&(e.b=t,e.a=(B1(t),t.a))}function PC(e,t,n,r,i){e.d=t,e.c=n,e.a=r,e.b=i}function PI(e){var t,n;t=e.b,n=e.c,e.b=n,e.c=t}function PD(e){var t,n;n=e.d,t=e.a,e.d=t,e.a=n}function PN(e){return eal(YE(Ts(e)?eaL(e):e))}function PP(e,t){return ME(Rx(e.d),Rx(t.d))}function PR(e,t){return t==(eYu(),tbY)?e.c:e.d}function Pj(){Pj=A,tuu=(eYu(),tbY),tuc=tby}function PF(){this.b=gP(LV(epB((eCk(),e9N))))}function PY(e){return _w(),Je(e1R,eUp,1,e,5,1)}function PB(e){return new kl(e.c+e.b,e.d+e.a)}function PU(e,t){return _C(),ME(e.d.p,t.d.p)}function PH(e){return A6(0!=e.b),etw(e,e.a.a)}function P$(e){return A6(0!=e.b),etw(e,e.c.b)}function Pz(e,t){if(!e)throw p7(new gS(t))}function PG(e,t){if(!e)throw p7(new gL(t))}function PW(e,t,n){Se.call(this,e,t),this.b=n}function PK(e,t,n){k8.call(this,e,t),this.c=n}function PV(e,t,n){etn.call(this,t,n),this.d=e}function Pq(e){Dt(),sr.call(this),this.th(e)}function PZ(e,t,n){this.a=e,xQ.call(this,t,n)}function PX(e,t,n){this.a=e,xQ.call(this,t,n)}function PJ(e,t,n){k8.call(this,e,t),this.c=n}function PQ(){ZE(),BY.call(this,(_Q(),tgp))}function P1(e){return null!=e&&!efz(e,tm1,tm0)}function P0(e,t){return(elt(e)<<4|elt(t))&eHd}function P2(e,t){return U_(),eb2(e,t),new Uf(e,t)}function P3(e,t){var n;e.n&&(n=t,P_(e.f,n))}function P4(e,t,n){var r;ee3(e,t,r=new B_(n))}function P5(e,t){var n;return n=e.c,ers(e,t),n}function P6(e,t){return t<0?e.g=-1:e.g=t,e}function P9(e,t){return etN(e),e.a*=t,e.b*=t,e}function P8(e,t,n,r,i){e.c=t,e.d=n,e.b=r,e.a=i}function P7(e,t){return qQ(e,t,e.c.b,e.c),!0}function Re(e){e.a.b=e.b,e.b.a=e.a,e.a=e.b=null}function Rt(e){this.b=e,this.a=Fc(this.b.a).Ed()}function Rn(e,t){this.b=e,this.a=t,ci.call(this)}function Rr(e,t){this.a=e,this.b=t,ci.call(this)}function Ri(e,t){F$.call(this,t,1040),this.a=e}function Ra(e){return 0==e||isNaN(e)?e:e<0?-1:1}function Ro(e){return HR(),e_I(e)==z$(e_P(e))}function Rs(e){return HR(),e_P(e)==z$(e_I(e))}function Ru(e,t){return eyE(e,new Se(t.a,t.b))}function Rc(e){return!q8(e)&&e.c.i.c==e.d.i.c}function Rl(e){var t;return t=e.n,e.a.b+t.d+t.a}function Rf(e){var t;return t=e.n,e.e.b+t.d+t.a}function Rd(e){var t;return t=e.n,e.e.a+t.b+t.c}function Rh(e){return eBG(),++tyv,new jb(0,e)}function Rp(e){return e.a?e.a:Hh(e)}function Rb(e){if(!e)throw p7(new gA(null))}function Rm(){Rm=A,tvm=(Hj(),new fB(eQU))}function Rg(){Rg=A,new ebw((m2(),e0d),(m3(),e0f))}function Rv(){Rv=A,e0B=Je(e15,eUP,19,256,0,1)}function Ry(e,t,n,r){ef3.call(this,e,t,n,r,0,0)}function Rw(e,t,n){return Um(e.b,Pp(n.b,17),t)}function R_(e,t,n){return Um(e.b,Pp(n.b,17),t)}function RE(e,t){return P_(e,new kl(t.a,t.b))}function RS(e,t){return e.c=t)throw p7(new bj)}function FR(e,t,n){return Bc(t,0,R5(t[0],n[0])),t}function Fj(e,t,n){t.Ye(n,gP(LV(Bp(e.b,n)))*e.a)}function FF(e,t,n){return eLG(),eiq(e,t)&&eiq(e,n)}function FY(e){return ekU(),!e.Hc(tbp)&&!e.Hc(tbm)}function FB(e){return new kl(e.c+e.b/2,e.d+e.a/2)}function FU(e,t){return t.kh()?ecv(e.b,Pp(t,49)):t}function FH(e,t){this.e=e,this.d=(64&t)!=0?t|eUR:t}function F$(e,t){this.c=0,this.d=e,this.b=64|t|eUR}function Fz(e){this.b=new XM(11),this.a=(HF(),e)}function FG(e){this.b=null,this.a=(HF(),e||e2s)}function FW(e){this.a=ebb(e.a),this.b=new I4(e.b)}function FK(e){this.b=e,AF.call(this,e),Op(this)}function FV(e){this.b=e,AB.call(this,e),Ob(this)}function Fq(e,t,n){this.a=e,Ia.call(this,t,n,5,6)}function FZ(e,t,n,r){this.b=e,O_.call(this,t,n,r)}function FX(e,t,n,r,i){JB.call(this,e,t,n,r,i,-1)}function FJ(e,t,n,r,i){JU.call(this,e,t,n,r,i,-1)}function FQ(e,t,n,r){O_.call(this,e,t,n),this.b=r}function F1(e,t,n,r){PK.call(this,e,t,n),this.b=r}function F0(e){k7.call(this,e,!1),this.a=!1}function F2(e,t){this.b=e,lm.call(this,e.b),this.a=t}function F3(e,t){Bx(),wj.call(this,e,ecT(new g$(t)))}function F4(e,t){return eBG(),++tyv,new BR(e,t,0)}function F5(e,t){return eBG(),++tyv,new BR(6,e,t)}function F6(e,t){return IE(e.substr(0,t.length),t)}function F9(e,t){return xd(t)?$r(e,t):!!$I(e.f,t)}function F8(e,t){for(BJ(t);e.Ob();)t.td(e.Pb())}function F7(e,t,n){eLQ(),this.e=e,this.d=t,this.a=n}function Ye(e,t,n,r){var i;(i=e.i).i=t,i.a=n,i.b=r}function Yt(e){var t;for(t=e;t.f;)t=t.f;return t}function Yn(e){var t;return A6(null!=(t=eso(e))),t}function Yr(e){var t;return A6(null!=(t=elT(e))),t}function Yi(e,t){var n;return ZQ(t,n=e.a.gc()),n-t}function Ya(e,t){var n;for(n=0;n0?eB4.Math.log(e/t):-100}function YM(e,t){return 0>ecd(e,t)?-1:ecd(e,t)>0?1:0}function YO(e,t,n){return ePQ(e,Pp(t,46),Pp(n,167))}function YA(e,t){return Pp(Ff(Fc(e.a)).Xb(t),42).cd()}function YL(e,t){return eto(t,e.length),new Ri(e,t)}function YC(e,t){this.d=e,Ow.call(this,e),this.e=t}function YI(e){this.d=(BJ(e),e),this.a=0,this.c=eUY}function YD(e,t){pJ.call(this,1),this.a=e,this.b=t}function YN(e,t){return e.c?YN(e.c,t):P_(e.b,t),e}function YP(e,t,n){var r;return r=eep(e,t),V7(e,t,n),r}function YR(e,t){var n;return QO(n=e.slice(0,t),e)}function Yj(e,t,n){var r;for(r=0;r=e.g}function BL(e,t,n){var r;return r=er$(e,t,n),eCK(e,r)}function BC(e,t){var n;n=e.a.length,eep(e,n),V7(e,n,t)}function BI(e,t){var n;(n=console[e]).call(console,t)}function BD(e,t){var n;++e.j,n=e.Vi(),e.Ii(e.oi(n,t))}function BN(e,t,n){Pp(t.b,65),ety(t.a,new N6(e,n,t))}function BP(e,t,n){p$.call(this,t),this.a=e,this.b=n}function BR(e,t,n){pJ.call(this,e),this.a=t,this.b=n}function Bj(e,t,n){this.a=e,pH.call(this,t),this.b=n}function BF(e,t,n){this.a=e,K3.call(this,8,t,null,n)}function BY(e){this.a=(BJ(eJ7),eJ7),this.b=e,new mP}function BB(e){this.c=e,this.b=this.c.a,this.a=this.c.e}function BU(e){this.c=e,this.b=e.a.d.a,LR(e.a.e,this)}function BH(e){A4(-1!=e.c),e.d.$c(e.c),e.b=e.c,e.c=-1}function B$(e){return eB4.Math.sqrt(e.a*e.a+e.b*e.b)}function Bz(e,t){return FP(t,e.a.c.length),RJ(e.a,t)}function BG(e,t){return xc(e)===xc(t)||null!=e&&ecX(e,t)}function BW(e){return 0>=e?new _e:erg(e-1)}function BK(e){return!!tyb&&$r(tyb,e)}function BV(e){return e?e.dc():!e.Kc().Ob()}function Bq(e){return!e.a&&e.c?e.c.b:e.a}function BZ(e){return e.a||(e.a=new O_(e6f,e,4)),e.a}function BX(e){return e.d||(e.d=new O_(tgr,e,1)),e.d}function BJ(e){if(null==e)throw p7(new bM);return e}function BQ(e){e.c?e.c.He():(e.d=!0,eAA(e))}function B1(e){e.c?B1(e.c):(el3(e),e.d=!0)}function B0(e){UG(e.a),e.b=Je(e1R,eUp,1,e.b.length,5,1)}function B2(e,t){return ME(t.j.c.length,e.j.c.length)}function B3(e,t){e.c<0||e.b.b=0?e.Bh(n):ekN(e,t)}function B5(e){var t,n;return(t=e.c.i.c)==(n=e.d.i.c)}function B6(e){if(4!=e.p)throw p7(new bT);return e.e}function B9(e){if(3!=e.p)throw p7(new bT);return e.e}function B8(e){if(6!=e.p)throw p7(new bT);return e.f}function B7(e){if(6!=e.p)throw p7(new bT);return e.k}function Ue(e){if(3!=e.p)throw p7(new bT);return e.j}function Ut(e){if(4!=e.p)throw p7(new bT);return e.j}function Un(e){return e.b||(e.b=new pG(new mR)),e.b}function Ur(e){return -2==e.c&&fd(e,e_d(e.g,e.b)),e.c}function Ui(e,t){var n;return(n=Y6("",e)).n=t,n.i=1,n}function Ua(e,t){jB(Pp(t.b,65),e),ety(t.a,new dv(e))}function Uo(e,t){JL((e.a||(e.a=new C_(e,e)),e.a),t)}function Us(e,t){this.b=e,YC.call(this,e,t),Op(this)}function Uu(e,t){this.b=e,IB.call(this,e,t),Ob(this)}function Uc(e,t,n,r){wD.call(this,e,t),this.d=n,this.a=r}function Ul(e,t,n,r){wD.call(this,e,n),this.a=t,this.f=r}function Uf(e,t){MK.call(this,erv(Y9(e),Y9(t))),this.a=t}function Ud(){e_w.call(this,eQB,(yA(),tvE)),ejt(this)}function Uh(){e_w.call(this,eQc,(yO(),tgg)),eP3(this)}function Up(){wC.call(this,"DELAUNAY_TRIANGULATION",0)}function Ub(e){return String.fromCharCode.apply(null,e)}function Um(e,t,n){return xd(t)?Ge(e,t,n):eS9(e.f,t,n)}function Ug(e){return Hj(),e?e.ve():(HF(),HF(),e2c)}function Uv(e,t,n){return eoM(),n.pg(e,Pp(t.cd(),146))}function Uy(e,t){return Rg(),new ebw(new OK(e),new OW(t))}function Uw(e){return enG(e,eU6),ee1(eft(eft(5,e),e/10|0))}function U_(){U_=A,e0p=new gt(eow(vx(e1$,1),eUK,42,0,[]))}function UE(e){return e.d||(e.d=new fF(e.c.Cc())),e.d}function US(e){return e.a||(e.a=new vp(e.c.vc())),e.a}function Uk(e){return e.b||(e.b=new vd(e.c.ec())),e.b}function Ux(e,t){for(;t-- >0;)e=e<<1|(e<0?1:0);return e}function UT(e,t){return xc(e)===xc(t)||null!=e&&ecX(e,t)}function UM(e,t){return OQ(),Pp(t.b,19).ar&&++r,r}function Hl(e){var t,n;return etV(n=t=new p5,e),n}function Hf(e){var t,n;return e_U(n=t=new p5,e),n}function Hd(e,t){var n;return n=Bp(e.f,t),eiX(t,n),null}function Hh(e){var t;return(t=erw(e))?t:null}function Hp(e){return e.b||(e.b=new FQ(e6g,e,12,3)),e.b}function Hb(e){return null!=e&&wZ(tm$,e.toLowerCase())}function Hm(e,t){return elN(jl(e)*jc(e),jl(t)*jc(t))}function Hg(e,t){return elN(jl(e)*jc(e),jl(t)*jc(t))}function Hv(e,t){return elN(e.d.c+e.d.b/2,t.d.c+t.d.b/2)}function Hy(e,t){return elN(e.g.c+e.g.b/2,t.g.c+t.g.b/2)}function Hw(e,t,n){n.a?ens(e,t.b-e.f/2):eno(e,t.a-e.g/2)}function H_(e,t,n,r){this.a=e,this.b=t,this.c=n,this.d=r}function HE(e,t,n,r){this.a=e,this.b=t,this.c=n,this.d=r}function HS(e,t,n,r){this.e=e,this.a=t,this.c=n,this.d=r}function Hk(e,t,n,r){this.a=e,this.c=t,this.d=n,this.b=r}function Hx(e,t,n,r){Ml(),ZU.call(this,t,n,r),this.a=e}function HT(e,t,n,r){Ml(),ZU.call(this,t,n,r),this.a=e}function HM(e,t){this.a=e,LQ.call(this,e,Pp(e.d,15).Zc(t))}function HO(e){this.f=e,this.c=this.f.e,e.f>0&&evH(this)}function HA(e,t,n,r){this.b=e,this.c=r,xj.call(this,t,n)}function HL(e){return A6(e.b=0&&IE(e.substr(n,t.length),t)}function $N(e,t,n,r,i,a,o){return new qu(e.e,t,n,r,i,a,o)}function $P(e,t,n,r,i,a){this.a=e,en1.call(this,t,n,r,i,a)}function $R(e,t,n,r,i,a){this.a=e,en1.call(this,t,n,r,i,a)}function $j(e,t){this.g=e,this.d=eow(vx(e4N,1),eGW,10,0,[t])}function $F(e,t){this.e=e,this.a=e1R,this.b=eCz(t),this.c=t}function $Y(e,t){CK.call(this),etk(this),this.a=e,this.c=t}function $B(e,t,n,r){Bc(e.c[t.g],n.g,r),Bc(e.c[n.g],t.g,r)}function $U(e,t,n,r){Bc(e.c[t.g],t.g,n),Bc(e.b[t.g],t.g,r)}function $H(){return Xo(),eow(vx(e5u,1),eU4,376,0,[tsH,tsU])}function $$(){return Qx(),eow(vx(e40,1),eU4,479,0,[ttt,tte])}function $z(){return eeF(),eow(vx(e4J,1),eU4,419,0,[teZ,teX])}function $G(){return Jp(),eow(vx(e4V,1),eU4,422,0,[teD,teN])}function $W(){return K6(),eow(vx(e49,1),eU4,420,0,[ttR,ttj])}function $K(){return Q0(),eow(vx(e5a,1),eU4,421,0,[tsL,tsC])}function $V(){return qG(),eow(vx(e5v,1),eU4,523,0,[tuf,tul])}function $q(){return Xa(),eow(vx(e5k,1),eU4,520,0,[tuH,tuU])}function $Z(){return zs(),eow(vx(e5E,1),eU4,516,0,[tuw,tuy])}function $X(){return zQ(),eow(vx(e5S,1),eU4,515,0,[tuE,tuS])}function $J(){return zo(),eow(vx(e5x,1),eU4,455,0,[tuq,tuZ])}function $Q(){return Kn(),eow(vx(e5C,1),eU4,425,0,[tcH,tcU])}function $1(){return z1(),eow(vx(e5L,1),eU4,480,0,[tcF,tcY])}function $0(){return erZ(),eow(vx(e5I,1),eU4,495,0,[tcq,tcZ])}function $2(){return J0(),eow(vx(e5N,1),eU4,426,0,[tc2,tc3])}function $3(){return eoT(),eow(vx(e5V,1),eU4,429,0,[tdt,tde])}function $4(){return Xs(),eow(vx(e5G,1),eU4,430,0,[tfw,tfy])}function $5(){return epC(),eow(vx(e2Q,1),eU4,428,0,[e3f,e3l])}function $6(){return eeR(),eow(vx(e21,1),eU4,427,0,[e3h,e3p])}function $9(){return eej(),eow(vx(e4E,1),eU4,424,0,[e9f,e9d])}function $8(){return erq(),eow(vx(e4F,1),eU4,511,0,[e8W,e8G])}function $7(e,t,n,r){return n>=0?e.jh(t,n,r):e.Sg(null,n,r)}function ze(e){return 0==e.b.b?e.a.$e():PH(e.b)}function zt(e){if(5!=e.p)throw p7(new bT);return jE(e.f)}function zn(e){if(5!=e.p)throw p7(new bT);return jE(e.k)}function zr(e){return xc(e.a)===xc((eiM(),tgW))&&eR1(e),e.a}function zi(e){this.a=Pp(Y9(e),271),this.b=(Hj(),new O4(e))}function za(e,t){l5(this,new kl(e.a,e.b)),l6(this,Pv(t))}function zo(){zo=A,tuq=new SK(ezt,0),tuZ=new SK(ezn,1)}function zs(){zs=A,tuw=new Sz(ezn,0),tuy=new Sz(ezt,1)}function zu(){m9.call(this,new w8(ee0(12))),Oq(!0),this.a=2}function zc(e,t,n){eBG(),pJ.call(this,e),this.b=t,this.a=n}function zl(e,t,n){Ml(),p$.call(this,t),this.a=e,this.b=n}function zf(e){CK.call(this),etk(this),this.a=e,this.c=!0}function zd(e){var t;t=e.c.d.b,e.b=t,e.a=e.c.d,t.a=e.c.d.b=e}function zh(e){var t;enZ(e.a),TW(e.a),efJ(t=new dp(e.a))}function zp(e,t){eC_(e,!0),ety(e.e.wf(),new Dx(e,!0,t))}function zb(e,t){return qe(t),enL(e,Je(ty_,eHT,25,t,15,1),t)}function zm(e,t){return HR(),e==z$(e_I(t))||e==z$(e_P(t))}function zg(e,t){return null==t?xu($I(e.f,null)):Ea(e.g,t)}function zv(e){return 0==e.b?null:(A6(0!=e.b),etw(e,e.a.a))}function zy(e){return 0|Math.max(Math.min(e,eUu),-2147483648)}function zw(e,t){var n=e0w[e.charCodeAt(0)];return null==n?e:n}function z_(e,t){return H5(e,"set1"),H5(t,"set2"),new wF(e,t)}function zE(e,t){var n;return C5(Ld(n=et$(e.f,t)),e.f.d)}function zS(e,t){var n,r;return ej4(e,n=t,r=new H),r.d}function zk(e,t,n,r){var i;i=new CQ,t.a[n.g]=i,jT(e.b,r,i)}function zx(e,t,n){var r;(r=e.Yg(t))>=0?e.sh(r,n):eOh(e,t,n)}function zT(e,t,n){z0(),e&&Um(tmj,e,t),e&&Um(tmR,e,n)}function zM(e,t,n){this.i=new p0,this.b=e,this.g=t,this.a=n}function zO(e,t,n){this.c=new p0,this.e=e,this.f=t,this.b=n}function zA(e,t,n){this.a=new p0,this.e=e,this.f=t,this.c=n}function zL(e,t){MV(this),this.f=t,this.g=e,HD(this),this._d()}function zC(e,t){var n;n=e.q.getHours(),e.q.setDate(t),eNq(e,n)}function zI(e,t){var n;for(Y9(t),n=e.a;n;n=n.c)t.Od(n.g,n.i)}function zD(e){var t;return esb(t=new yF(ee0(e.length)),e),t}function zN(e){function t(){}return t.prototype=e||{},new t}function zP(e,t){return!!eos(e,t)&&(enP(e),!0)}function zR(e,t){if(null==t)throw p7(new bM);return ehF(e,t)}function zj(e){return e.qe()?null:(0,eUt[e.n])}function zF(e){return e.Db>>16!=3?null:Pp(e.Cb,33)}function zY(e){return e.Db>>16!=9?null:Pp(e.Cb,33)}function zB(e){return e.Db>>16!=6?null:Pp(e.Cb,79)}function zU(e){return e.Db>>16!=7?null:Pp(e.Cb,235)}function zH(e){return e.Db>>16!=7?null:Pp(e.Cb,160)}function z$(e){return e.Db>>16!=11?null:Pp(e.Cb,33)}function zz(e,t){var n;return(n=e.Yg(t))>=0?e.lh(n):exu(e,t)}function zG(e,t){var n;return n=new RZ(t),e_h(n,e),new I4(n)}function zW(e){var t;return t=e.d,t=e.si(e.f),JL(e,t),t.Ob()}function zK(e,t){return e.b+=t.b,e.c+=t.c,e.d+=t.d,e.a+=t.a,e}function zV(e,t){return eB4.Math.abs(e)0}function zZ(){this.a=new Tw,this.e=new bV,this.g=0,this.i=0}function zX(e){this.a=e,this.b=Je(e5b,eUP,1944,e.e.length,0,2)}function zJ(e,t,n){var r;r=esg(e,t,n),e.b=new erH(r.c.length)}function zQ(){zQ=A,tuE=new S$(ezh,0),tuS=new S$("UP",1)}function z1(){z1=A,tcF=new SJ(eV2,0),tcY=new SJ("FAN",1)}function z0(){z0=A,tmj=new p2,tmR=new p2,xa(e0r,new o8)}function z2(e){if(0!=e.p)throw p7(new bT);return xg(e.f,0)}function z3(e){if(0!=e.p)throw p7(new bT);return xg(e.k,0)}function z4(e){return e.Db>>16!=3?null:Pp(e.Cb,147)}function z5(e){return e.Db>>16!=6?null:Pp(e.Cb,235)}function z6(e){return e.Db>>16!=17?null:Pp(e.Cb,26)}function z9(e,t){var n=e.a=e.a||[];return n[t]||(n[t]=e.le(t))}function z8(e,t){var n;return null==(n=e.a.get(t))?[]:n}function z7(e,t){var n;n=e.q.getHours(),e.q.setMonth(t),eNq(e,n)}function Ge(e,t,n){return null==t?eS9(e.f,null,n):efi(e.g,t,n)}function Gt(e,t,n,r,i,a){return new Q$(e.e,t,e.aj(),n,r,i,a)}function Gn(e,t,n){return e.a=Az(e.a,0,t)+""+n+xy(e.a,t),e}function Gr(e,t,n){return P_(e.a,(U_(),eb2(t,n),new wD(t,n))),e}function Gi(e){return OX(e.c),e.e=e.a=e.c,e.c=e.c.c,++e.d,e.a.f}function Ga(e){return OX(e.e),e.c=e.a=e.e,e.e=e.e.e,--e.d,e.a.f}function Go(e,t){e.d&&QA(e.d.e,e),e.d=t,e.d&&P_(e.d.e,e)}function Gs(e,t){e.c&&QA(e.c.g,e),e.c=t,e.c&&P_(e.c.g,e)}function Gu(e,t){e.c&&QA(e.c.a,e),e.c=t,e.c&&P_(e.c.a,e)}function Gc(e,t){e.i&&QA(e.i.j,e),e.i=t,e.i&&P_(e.i.j,e)}function Gl(e,t,n){this.a=t,this.c=e,this.b=(Y9(n),new I4(n))}function Gf(e,t,n){this.a=t,this.c=e,this.b=(Y9(n),new I4(n))}function Gd(e,t){this.a=e,this.c=MB(this.a),this.b=new $g(t)}function Gh(e){var t;return el3(e),t=new bV,UJ(e,new di(t))}function Gp(e,t){if(e<0||e>t)throw p7(new gE(e$O+e+e$A+t))}function Gb(e,t){return jR(e.a,t)?Yl(e,Pp(t,22).g,null):null}function Gm(e){return euQ(),OQ(),0!=Pp(e.a,81).d.e}function Gg(){Gg=A,e0g=euY((m5(),eow(vx(e1W,1),eU4,538,0,[e0m])))}function Gv(){Gv=A,ts2=j0(new K2,(e_x(),e8i),(eB$(),e7N))}function Gy(){Gy=A,ts3=j0(new K2,(e_x(),e8i),(eB$(),e7N))}function Gw(){Gw=A,ts5=j0(new K2,(e_x(),e8i),(eB$(),e7N))}function G_(){G_=A,tuh=RI(new K2,(e_x(),e8i),(eB$(),e7o))}function GE(){GE=A,tug=RI(new K2,(e_x(),e8i),(eB$(),e7o))}function GS(){GS=A,tuv=RI(new K2,(e_x(),e8i),(eB$(),e7o))}function Gk(){Gk=A,tux=RI(new K2,(e_x(),e8i),(eB$(),e7o))}function Gx(){Gx=A,tcz=j0(new K2,(egR(),tu0),(eS_(),tu3))}function GT(e,t,n,r){this.c=e,this.d=r,GA(this,t),GL(this,n)}function GM(e){this.c=new _n,this.b=e.b,this.d=e.c,this.a=e.a}function GO(e){this.a=eB4.Math.cos(e),this.b=eB4.Math.sin(e)}function GA(e,t){e.a&&QA(e.a.k,e),e.a=t,e.a&&P_(e.a.k,e)}function GL(e,t){e.b&&QA(e.b.f,e),e.b=t,e.b&&P_(e.b.f,e)}function GC(e,t){BN(e,e.b,e.c),Pp(e.b.b,65),t&&Pp(t.b,65).b}function GI(e,t){elJ(e,t),M4(e.Cb,88)&&eko(Zd(Pp(e.Cb,88)),2)}function GD(e,t){M4(e.Cb,88)&&eko(Zd(Pp(e.Cb,88)),4),er3(e,t)}function GN(e,t){M4(e.Cb,179)&&(Pp(e.Cb,179).tb=null),er3(e,t)}function GP(e,t){return _4(),eec(t)?new RA(t,e):new xe(t,e)}function GR(e,t){var n,r;(r=null!=(n=t.c))&&BC(e,new B_(t.c))}function Gj(e){var t,n;return n=(yO(),t=new p5),etV(n,e),n}function GF(e){var t,n;return n=(yO(),t=new p5),etV(n,e),n}function GY(e,t){var n;return n=new By(e),t.c[t.c.length]=n,n}function GB(e,t){var n;return(n=Pp(ecA(HU(e.a),t),14))?n.gc():0}function GU(e){var t;return el3(e),etc(e,t=(HF(),HF(),e2u))}function GH(e){for(var t;;)if(t=e.Pb(),!e.Ob())return t}function G$(e,t){mK.call(this,new w8(ee0(e))),enG(t,eUN),this.a=t}function Gz(e,t,n){ec5(t,n,e.gc()),this.c=e,this.a=t,this.b=n-t}function GG(e,t,n){var r;ec5(t,n,e.c.length),r=n-t,yJ(e.c,t,r)}function GW(e,t){M7(e,jE(WM(Fv(t,24),e$b)),jE(WM(t,e$b)))}function GK(e,t){if(e<0||e>=t)throw p7(new gE(e$O+e+e$A+t))}function GV(e,t){if(e<0||e>=t)throw p7(new vf(e$O+e+e$A+t))}function Gq(e,t){this.b=(BJ(e),e),this.a=(t&eH0)==0?64|t|eUR:t}function GZ(e){TG(this),bF(this.a,esi(eB4.Math.max(8,e))<<1)}function GX(e){return esp(eow(vx(e50,1),eUP,8,0,[e.i.n,e.n,e.a]))}function GJ(){return eum(),eow(vx(e2L,1),eU4,132,0,[e2B,e2U,e2H])}function GQ(){return etx(),eow(vx(e26,1),eU4,232,0,[e3D,e3N,e3P])}function G1(){return Qs(),eow(vx(e27,1),eU4,461,0,[e3F,e3j,e3Y])}function G0(){return QQ(),eow(vx(e3t,1),eU4,462,0,[e3$,e3H,e3U])}function G2(){return ec4(),eow(vx(e4L,1),eU4,423,0,[e8x,e8k,e8S])}function G3(){return QJ(),eow(vx(e4S,1),eU4,379,0,[e94,e93,e95])}function G4(){return euJ(),eow(vx(e5e,1),eU4,378,0,[tsn,tsr,tsi])}function G5(){return en7(),eow(vx(e4q,1),eU4,314,0,[tej,teR,teF])}function G6(){return enB(),eow(vx(e4Z,1),eU4,337,0,[teB,teH,teU])}function G9(){return eoG(),eow(vx(e4Q,1),eU4,450,0,[te1,teQ,te0])}function G8(){return erX(),eow(vx(e4G,1),eU4,361,0,[tep,teh,ted])}function G7(){return Q1(),eow(vx(e46,1),eU4,303,0,[ttD,ttN,ttI])}function We(){return eaU(),eow(vx(e45,1),eU4,292,0,[ttA,ttL,ttO])}function Wt(){return enY(),eow(vx(e5o,1),eU4,452,0,[tsP,tsD,tsN])}function Wn(){return esn(),eow(vx(e5i,1),eU4,339,0,[tsM,tsT,tsO])}function Wr(){return ei0(),eow(vx(e5s,1),eU4,375,0,[tsj,tsF,tsY])}function Wi(){return eox(),eow(vx(e5f,1),eU4,377,0,[tsQ,ts1,tsJ])}function Wa(){return euy(),eow(vx(e5c,1),eU4,336,0,[tsz,tsG,tsW])}function Wo(){return eiO(),eow(vx(e5l,1),eU4,338,0,[tsZ,tsV,tsq])}function Ws(){return enU(),eow(vx(e5p,1),eU4,454,0,[tur,tui,tua])}function Wu(){return efx(),eow(vx(e5D,1),eU4,442,0,[tc1,tcJ,tcQ])}function Wc(){return eub(),eow(vx(e5P,1),eU4,380,0,[tc5,tc6,tc9])}function Wl(){return efS(),eow(vx(e5Y,1),eU4,381,0,[tlP,tlR,tlN])}function Wf(){return ei1(),eow(vx(e5j,1),eU4,293,0,[tlC,tlI,tlL])}function Wd(){return efk(),eow(vx(e5H,1),eU4,437,0,[tff,tfd,tfh])}function Wh(){return eck(),eow(vx(e57,1),eU4,334,0,[tpG,tpz,tpW])}function Wp(){return etT(),eow(vx(e56,1),eU4,272,0,[tp_,tpE,tpS])}function Wb(e,t){return eMw(e,t,M4(t,99)&&(Pp(t,18).Bb&eH3)!=0)}function Wm(e,t,n){var r;return(r=ePI(e,t,!1)).b<=t&&r.a<=n}function Wg(e,t,n){var r;(r=new ac).b=t,r.a=n,++t.b,P_(e.d,r)}function Wv(e,t){var n;return A3(!!(n=(BJ(e),e).g)),BJ(t),n(t)}function Wy(e,t){var n,r;return r=Yi(e,t),n=e.a.Zc(r),new wR(e,n)}function Ww(e){return e.Db>>16!=6?null:Pp(eTp(e),235)}function W_(e){if(2!=e.p)throw p7(new bT);return jE(e.f)&eHd}function WE(e){if(2!=e.p)throw p7(new bT);return jE(e.k)&eHd}function WS(e){return e.a==(ZE(),tvd)&&ff(e,eM0(e.g,e.b)),e.a}function Wk(e){return e.d==(ZE(),tvd)&&fh(e,eIj(e.g,e.b)),e.d}function Wx(e){return A6(e.ar?1:0}function WY(e,t){var n,r;return r=n=QP(t),Pp(Bp(e.c,r),19).a}function WB(e,t){var n;for(n=e+"";n.length0&&0==e.a[--e.d];);0==e.a[e.d++]&&(e.e=0)}function Kc(e){return e.a?0==e.e.length?e.a.a:e.a.a+""+e.e:e.c}function Kl(e){return!!e.a&&0!=QX(e.a.a).i&&!(e.b&&ebq(e.b))}function Kf(e){return!!e.u&&0!=qt(e.u.a).i&&!(e.n&&ebV(e.n))}function Kd(e){return Rj(e.e.Hd().gc()*e.c.Hd().gc(),16,new c9(e))}function Kh(e,t){return YM(eap(e.q.getTime()),eap(t.q.getTime()))}function Kp(e){return Pp(epg(e,Je(e4C,eGG,17,e.c.length,0,1)),474)}function Kb(e){return Pp(epg(e,Je(e4N,eGW,10,e.c.length,0,1)),193)}function Km(e){return GE(),!q8(e)&&!(!q8(e)&&e.c.i.c==e.d.i.c)}function Kg(e,t,n){var r;r=(Y9(e),new I4(e)),egT(new Gl(r,t,n))}function Kv(e,t,n){var r;r=(Y9(e),new I4(e)),egM(new Gf(r,t,n))}function Ky(e,t){var n;return n=1-t,e.a[n]=erj(e.a[n],n),erj(e,t)}function Kw(e,t){var n;e.e=new mQ,n=eLj(t),Mv(n,e.c),eLJ(e,n,0)}function K_(e,t,n,r){var i;(i=new od).a=t,i.b=n,i.c=r,P7(e.a,i)}function KE(e,t,n,r){var i;(i=new od).a=t,i.b=n,i.c=r,P7(e.b,i)}function KS(e){var t,n,r;return n=eI4(t=new YQ,e),eFg(t),r=n}function Kk(){var e,t,n;return P_(tg6,t=n=e=new p5),t}function Kx(e){return e.j.c=Je(e1R,eUp,1,0,5,1),UG(e.c),Uj(e.a),e}function KT(e){return(_L(),M4(e.g,10))?Pp(e.g,10):null}function KM(e){return!Uz(e).dc()&&(MI(e,new v),!0)}function KO(e){if(!("stack"in e))try{throw e}catch(t){}return e}function KA(e,t){if(e<0||e>=t)throw p7(new gE(eku(e,t)));return e}function KL(e,t,n){if(e<0||tn)throw p7(new gE(eE3(e,t,n)))}function KC(e,t){if(Yf(e.a,t),t.d)throw p7(new go(e$P));t.d=e}function KI(e,t){if(t.$modCount!=e.$modCount)throw p7(new bA)}function KD(e,t){return!!M4(t,42)&&emT(e.a,Pp(t,42))}function KN(e,t){return!!M4(t,42)&&emT(e.a,Pp(t,42))}function KP(e,t){return!!M4(t,42)&&emT(e.a,Pp(t,42))}function KR(e,t){return e.a<=e.b&&(t.ud(e.a++),!0)}function Kj(e){var t;return Ts(e)?-0==(t=e)?0:t:eem(e)}function KF(e){var t;return B1(e),t=new Y,yU(e.a,new dn(t)),t}function KY(e){var t;return B1(e),t=new F,yU(e.a,new dt(t)),t}function KB(e,t){this.a=e,fE.call(this,e),Gp(t,e.gc()),this.b=t}function KU(e){this.e=e,this.b=this.e.a.entries(),this.a=[]}function KH(e){return Rj(e.e.Hd().gc()*e.c.Hd().gc(),273,new c6(e))}function K$(e){return new XM((enG(e,eU6),ee1(eft(eft(5,e),e/10|0))))}function Kz(e){return Pp(epg(e,Je(e4j,eGK,11,e.c.length,0,1)),1943)}function KG(e,t,n){return n.f.c.length>0?YO(e.a,t,n):YO(e.b,t,n)}function KW(e,t,n){e.d&&QA(e.d.e,e),e.d=t,e.d&&jO(e.d.e,n,e)}function KK(e,t){eY5(t,e),PD(e.d),PD(Pp(e_k(e,(eBy(),taq)),207))}function KV(e,t){eY4(t,e),PI(e.d),PI(Pp(e_k(e,(eBy(),taq)),207))}function Kq(e,t){var n,r;return n=zR(e,t),r=null,n&&(r=n.fe()),r}function KZ(e,t){var n,r;return n=eep(e,t),r=null,n&&(r=n.ie()),r}function KX(e,t){var n,r;return n=zR(e,t),r=null,n&&(r=n.ie()),r}function KJ(e,t){var n,r;return n=zR(e,t),r=null,n&&(r=eSa(n)),r}function KQ(e,t,n){var r;return r=ehM(n),eIg(e.g,r,t),eIg(e.i,t,n),t}function K1(e,t,n){var r;r=ehl();try{return CO(e,t,n)}finally{Vx(r)}}function K0(e){var t;t=e.Wg(),this.a=M4(t,69)?Pp(t,69).Zh():t.Kc()}function K2(){mJ.call(this),this.j.c=Je(e1R,eUp,1,0,5,1),this.a=-1}function K3(e,t,n,r){this.d=e,this.n=t,this.g=n,this.o=r,this.p=-1}function K4(e,t,n,r){this.e=r,this.d=null,this.c=e,this.a=t,this.b=n}function K5(e,t,n){this.d=new hg(this),this.e=e,this.i=t,this.f=n}function K6(){K6=A,ttR=new S_(e$8,0),ttj=new S_("TOP_LEFT",1)}function K9(){K9=A,ts7=Uy(ell(1),ell(4)),ts8=Uy(ell(1),ell(2))}function K8(){K8=A,tfv=euY((_N(),eow(vx(e5z,1),eU4,551,0,[tfg])))}function K7(){K7=A,tfm=euY((_D(),eow(vx(e5$,1),eU4,482,0,[tfb])))}function Ve(){Ve=A,tf7=euY((_P(),eow(vx(e5K,1),eU4,530,0,[tf8])))}function Vt(){Vt=A,e6W=euY((_y(),eow(vx(e4w,1),eU4,481,0,[e6G])))}function Vn(){return eaY(),eow(vx(e3r,1),eU4,406,0,[e4c,e4o,e4s,e4u])}function Vr(){return Qu(),eow(vx(e2E,1),eU4,297,0,[e2D,e2N,e2P,e2R])}function Vi(){return ebe(),eow(vx(e4y,1),eU4,394,0,[e6U,e6B,e6H,e6$])}function Va(){return ep7(),eow(vx(e3i,1),eU4,323,0,[e4d,e4f,e4h,e4p])}function Vo(){return eok(),eow(vx(e4A,1),eU4,405,0,[e8f,e8p,e8d,e8h])}function Vs(){return eoE(),eow(vx(e4U,1),eU4,360,0,[e7Z,e7V,e7q,e7K])}function Vu(e,t,n,r){return M4(n,54)?new A7(e,t,n,r):new Fo(e,t,n,r)}function Vc(){return eoS(),eow(vx(e4$,1),eU4,411,0,[e79,e78,e77,tee])}function Vl(e){var t;return e.j==(eYu(),tbj)&&(t=eTt(e),Aa(t,tby))}function Vf(e,t){var n;Gs(n=t.a,t.c.d),Go(n,t.d.d),etH(n.a,e.n)}function Vd(e,t){return Pp(Af(FT(Pp(Zq(e.k,t),15).Oc(),tex)),113)}function Vh(e,t){return Pp(Af(FM(Pp(Zq(e.k,t),15).Oc(),tex)),113)}function Vp(e){return new Gq(eip(Pp(e.a.dd(),14).gc(),e.a.cd()),16)}function Vb(e){return M4(e,14)?Pp(e,14).dc():!e.Kc().Ob()}function Vm(e){return(_L(),M4(e.g,145))?Pp(e.g,145):null}function Vg(e){if(e.e.g!=e.b)throw p7(new bA);return!!e.c&&e.d>0}function Vv(e){return A6(e.b!=e.d.c),e.c=e.b,e.b=e.b.a,++e.a,e.c.c}function Vy(e,t){BJ(t),Bc(e.a,e.c,t),e.c=e.c+1&e.a.length-1,ega(e)}function Vw(e,t){BJ(t),e.b=e.b-1&e.a.length-1,Bc(e.a,e.b,t),ega(e)}function V_(e,t){var n;for(n=e.j.c.length;n0&&ePD(e.g,0,t,0,e.i),t}function VB(e,t){var n;return _5(),!(n=Pp(Bp(tmU,e),55))||n.wj(t)}function VU(e){if(1!=e.p)throw p7(new bT);return jE(e.f)<<24>>24}function VH(e){if(1!=e.p)throw p7(new bT);return jE(e.k)<<24>>24}function V$(e){if(7!=e.p)throw p7(new bT);return jE(e.k)<<16>>16}function Vz(e){if(7!=e.p)throw p7(new bT);return jE(e.f)<<16>>16}function VG(e){var t;for(t=0;e.Ob();)e.Pb(),t=eft(t,1);return ee1(t)}function VW(e,t){var n;return n=new vl,e.xd(n),n.a+="..",t.yd(n),n.a}function VK(e,t,n){var r;r=Pp(Bp(e.g,n),57),P_(e.a.c,new kD(t,r))}function VV(e,t,n){return F_(LV(xu($I(e.f,t))),LV(xu($I(e.f,n))))}function Vq(e,t,n){return eNA(e,t,n,M4(t,99)&&(Pp(t,18).Bb&eH3)!=0)}function VZ(e,t,n){return eN1(e,t,n,M4(t,99)&&(Pp(t,18).Bb&eH3)!=0)}function VX(e,t,n){return eMN(e,t,n,M4(t,99)&&(Pp(t,18).Bb&eH3)!=0)}function VJ(e,t){return e==(eEn(),e8N)&&t==e8N?4:e==e8N||t==e8N?8:32}function VQ(e,t){return xc(t)===xc(e)?"(this Map)":null==t?eUg:efF(t)}function V1(e,t){return Pp(null==t?xu($I(e.f,null)):Ea(e.g,t),281)}function V0(e,t,n){var r;return r=ehM(n),Um(e.b,r,t),Um(e.c,t,n),t}function V2(e,t){var n;for(n=t;n;)Lu(e,n.i,n.j),n=z$(n);return e}function V3(e,t){var n;return n=$a(Pb(new Qj(e,t))),RG(new Qj(e,t)),n}function V4(e,t){var n;return _4(),eEy(n=Pp(e,66).Mj(),t),n.Ok(t)}function V5(e,t,n,r,i){var a;a=eMW(i,n,r),P_(t,eS4(i,a)),e_X(e,i,t)}function V6(e,t,n){e.i=0,e.e=0,t!=n&&(esC(e,t,n),esL(e,t,n))}function V9(e,t){var n;n=e.q.getHours(),e.q.setFullYear(t+eHx),eNq(e,n)}function V8(e,t,n){if(n){var r=n.ee();e.a[t]=r(n)}else delete e.a[t]}function V7(e,t,n){n=n?n.ee()(n):void 0,e.a[t]=n}function qe(e){if(e<0)throw p7(new gI("Negative array size: "+e))}function qt(e){return e.n||(Zd(e),e.n=new j4(e,tgr,e),$E(e)),e.n}function qn(e){return A6(e.a=0&&e.a[n]===t[n];n--);return n<0}function qy(e,t){var n;return(euv(),0!=(n=e.j.g-t.j.g))?n:0}function qw(e,t){return(BJ(t),null!=e.a)?jN(t.Kb(e.a)):e2b}function q_(e){var t;return e?new RZ(e):(t=new Tw,ein(t,e),t)}function qE(e,t){var n;return t.b.Kb(QD(e,t.c.Ee(),n=new ds(t)))}function qS(e){ewP(),M7(this,jE(WM(Fv(e,24),e$b)),jE(WM(e,e$b)))}function qk(){qk=A,e3d=euY((epC(),eow(vx(e2Q,1),eU4,428,0,[e3f,e3l])))}function qx(){qx=A,e3b=euY((eeR(),eow(vx(e21,1),eU4,427,0,[e3h,e3p])))}function qT(){qT=A,e9h=euY((eej(),eow(vx(e4E,1),eU4,424,0,[e9f,e9d])))}function qM(){qM=A,e8K=euY((erq(),eow(vx(e4F,1),eU4,511,0,[e8W,e8G])))}function qO(){qO=A,teJ=euY((eeF(),eow(vx(e4J,1),eU4,419,0,[teZ,teX])))}function qA(){qA=A,ttn=euY((Qx(),eow(vx(e40,1),eU4,479,0,[ttt,tte])))}function qL(){qL=A,ts$=euY((Xo(),eow(vx(e5u,1),eU4,376,0,[tsH,tsU])))}function qC(){qC=A,tsI=euY((Q0(),eow(vx(e5a,1),eU4,421,0,[tsL,tsC])))}function qI(){qI=A,teP=euY((Jp(),eow(vx(e4V,1),eU4,422,0,[teD,teN])))}function qD(){qD=A,ttF=euY((K6(),eow(vx(e49,1),eU4,420,0,[ttR,ttj])))}function qN(){qN=A,tu$=euY((Xa(),eow(vx(e5k,1),eU4,520,0,[tuH,tuU])))}function qP(){qP=A,tud=euY((qG(),eow(vx(e5v,1),eU4,523,0,[tuf,tul])))}function qR(){qR=A,tu_=euY((zs(),eow(vx(e5E,1),eU4,516,0,[tuw,tuy])))}function qj(){qj=A,tuk=euY((zQ(),eow(vx(e5S,1),eU4,515,0,[tuE,tuS])))}function qF(){qF=A,tuX=euY((zo(),eow(vx(e5x,1),eU4,455,0,[tuq,tuZ])))}function qY(){qY=A,tc$=euY((Kn(),eow(vx(e5C,1),eU4,425,0,[tcH,tcU])))}function qB(){qB=A,tcX=euY((erZ(),eow(vx(e5I,1),eU4,495,0,[tcq,tcZ])))}function qU(){qU=A,tcB=euY((z1(),eow(vx(e5L,1),eU4,480,0,[tcF,tcY])))}function qH(){qH=A,tc4=euY((J0(),eow(vx(e5N,1),eU4,426,0,[tc2,tc3])))}function q$(){q$=A,tdn=euY((eoT(),eow(vx(e5V,1),eU4,429,0,[tdt,tde])))}function qz(){qz=A,tf_=euY((Xs(),eow(vx(e5G,1),eU4,430,0,[tfw,tfy])))}function qG(){qG=A,tuf=new Sj("UPPER",0),tul=new Sj("LOWER",1)}function qW(e,t){var n;H1(n=new gu,"x",t.a),H1(n,"y",t.b),BC(e,n)}function qK(e,t){var n;H1(n=new gu,"x",t.a),H1(n,"y",t.b),BC(e,n)}function qV(e,t){var n,r;r=!1;do n=eo6(e,t),r|=n;while(n)return r}function qq(e,t){var n,r;for(n=t,r=0;n>0;)r+=e.a[n],n-=n&-n;return r}function qZ(e,t){var n;for(n=t;n;)Lu(e,-n.i,-n.j),n=z$(n);return e}function qX(e,t){var n,r;for(BJ(t),r=e.Kc();r.Ob();)n=r.Pb(),t.td(n)}function qJ(e,t){var n;return n=t.cd(),new wD(n,e.e.pc(n,Pp(t.dd(),14)))}function qQ(e,t,n,r){var i;(i=new C).c=t,i.b=n,i.a=r,r.b=n.a=i,++e.b}function q1(e,t,n){var r;return r=(GK(t,e.c.length),e.c[t]),e.c[t]=n,r}function q0(e,t,n){return Pp(null==t?eS9(e.f,null,n):efi(e.g,t,n),281)}function q2(e){return e.c&&e.d?WH(e.c)+"->"+WH(e.d):"e_"+Ao(e)}function q3(e,t){return(el3(e),yK(new R1(e,new Qa(t,e.a)))).sd(e2z)}function q4(){return e_x(),eow(vx(e4k,1),eU4,356,0,[e8e,e8t,e8n,e8r,e8i])}function q5(){return eYu(),eow(vx(e6a,1),eGj,61,0,[tbF,tbw,tby,tbj,tbY])}function q6(e){return vg(),function(){return K1(e,this,arguments)}}function q9(){return Date.now?Date.now():(new Date).getTime()}function q8(e){return!!e.c&&!!e.d&&!!e.c.i&&e.c.i==e.d.i}function q7(e){if(!e.c.Sb())throw p7(new bC);return e.a=!0,e.c.Ub()}function Ze(e){e.i=0,Eb(e.b,null),Eb(e.c,null),e.a=null,e.e=null,++e.g}function Zt(e){El.call(this,null==e?eUg:efF(e),M4(e,78)?Pp(e,78):null)}function Zn(e){eBD(),p8(this),this.a=new _n,esJ(this,e),P7(this.a,e)}function Zr(){Tz(this),this.b=new kl(eHQ,eHQ),this.a=new kl(eH1,eH1)}function Zi(e,t){this.c=0,this.b=t,xR.call(this,e,17493),this.a=this.c}function Za(e){Zo(),!e2M&&(this.c=e,this.e=!0,this.a=new p0)}function Zo(){Zo=A,e2M=!0,e2x=!1,e2T=!1,e2A=!1,e2O=!1}function Zs(e,t){return!!M4(t,149)&&IE(e.c,Pp(t,149).c)}function Zu(e,t){var n;return n=0,e&&(n+=e.f.a/2),t&&(n+=t.f.a/2),n}function Zc(e,t){var n;return(n=Pp(eef(e.d,t),23))||Pp(eef(e.e,t),23)}function Zl(e){this.b=e,Ow.call(this,e),this.a=Pp(eaS(this.b.a,4),126)}function Zf(e){this.b=e,AY.call(this,e),this.a=Pp(eaS(this.b.a,4),126)}function Zd(e){return e.t||(e.t=new pR(e),elm(new gT(e),0,e.t)),e.t}function Zh(){return ec3(),eow(vx(e55,1),eU4,103,0,[tpv,tpg,tpm,tpb,tpy])}function Zp(){return epT(),eow(vx(e6n,1),eU4,249,0,[tbt,tbr,tp7,tbe,tbn])}function Zb(){return epx(),eow(vx(e5Q,1),eU4,175,0,[tdh,tdd,tdl,tdp,tdf])}function Zm(){return eEM(),eow(vx(e5W,1),eU4,316,0,[tfE,tfS,tfT,tfk,tfx])}function Zg(){return ebG(),eow(vx(e5n,1),eU4,315,0,[tsb,tsd,tsh,tsf,tsp])}function Zv(){return eb6(),eow(vx(e4X,1),eU4,335,0,[teG,tez,teK,teV,teW])}function Zy(){return eOB(),eow(vx(e5U,1),eU4,355,0,[tfo,tfa,tfu,tfs,tfc])}function Zw(){return ey4(),eow(vx(e4z,1),eU4,363,0,[ter,tea,teo,tei,ten])}function Z_(){return ef_(),eow(vx(e48,1),eU4,163,0,[tnj,tnD,tnN,tnP,tnR])}function ZE(){var e,t;ZE=A,tvf=(yO(),t=new bN),tvd=e=new mC}function ZS(e){var t;return!e.c&&M4(t=e.r,88)&&(e.c=Pp(t,26)),e.c}function Zk(e){return e.e=3,e.d=e.Yb(),2!=e.e&&(e.e=0,!0)}function Zx(e){var t,n,r;return t=e&eHH,Mk(t,n=e>>22&eHH,r=e<0?eH$:0)}function ZT(e){var t,n,r,i;for(r=0,i=(n=e).length;r0?ehe(e,t):eA8(e,-t)}function ZL(e,t){return 0==t||0==e.e?e:t>0?eA8(e,t):ehe(e,-t)}function ZC(e){if(eTk(e))return e.c=e.a,e.a.Pb();throw p7(new bC)}function ZI(e){var t,n;return t=e.c.i,n=e.d.i,t.k==(eEn(),e8C)&&n.k==e8C}function ZD(e){var t;return t=new $b,eaW(t,e),eo3(t,(eBy(),taR),null),t}function ZN(e,t,n){var r;return(r=e.Yg(t))>=0?e._g(r,n,!0):exk(e,t,n)}function ZP(e,t,n,r){var i;for(i=0;it)throw p7(new gE(eS1(e,t,"index")));return e}function Z1(e,t,n,r){var i;return i=Je(ty_,eHT,25,t,15,1),ewD(i,e,t,n,r),i}function Z0(e,t){var n;n=e.q.getHours()+(t/60|0),e.q.setMinutes(t),eNq(e,n)}function Z2(e,t){return eB4.Math.min(Jh(t.a,e.d.d.c),Jh(t.b,e.d.d.c))}function Z3(e,t){return xd(t)?null==t?eTx(e.f,null):eaK(e.g,t):eTx(e.f,t)}function Z4(e){this.c=e,this.a=new fz(this.c.a),this.b=new fz(this.c.b)}function Z5(){this.e=new p0,this.c=new p0,this.d=new p0,this.b=new p0}function Z6(){this.g=new bJ,this.b=new bJ,this.a=new p0,this.k=new p0}function Z9(e,t,n){this.a=e,this.c=t,this.d=n,P_(t.e,this),P_(n.b,this)}function Z8(e,t){xP.call(this,t.rd(),-6&t.qd()),BJ(e),this.a=e,this.b=t}function Z7(e,t){xR.call(this,t.rd(),-6&t.qd()),BJ(e),this.a=e,this.b=t}function Xe(e,t){xj.call(this,t.rd(),-6&t.qd()),BJ(e),this.a=e,this.b=t}function Xt(e,t,n){this.a=e,this.b=t,this.c=n,P_(e.t,this),P_(t.i,this)}function Xn(){this.b=new _n,this.a=new _n,this.b=new _n,this.a=new _n}function Xr(){Xr=A,tdx=new pO("org.eclipse.elk.labels.labelManager")}function Xi(){Xi=A,e7W=new Cm("separateLayerConnections",(eoE(),e7Z))}function Xa(){Xa=A,tuH=new SW("REGULAR",0),tuU=new SW("CRITICAL",1)}function Xo(){Xo=A,tsH=new SI("STACKED",0),tsU=new SI("SEQUENCED",1)}function Xs(){Xs=A,tfw=new S7("FIXED",0),tfy=new S7("CENTER_NODE",1)}function Xu(e,t){var n;return n=ejH(e,t),e.b=new erH(n.c.length),eRj(e,n)}function Xc(e,t,n){var r;return++e.e,--e.f,(r=Pp(e.d[t].$c(n),133)).dd()}function Xl(e){var t;return!e.a&&M4(t=e.r,148)&&(e.a=Pp(t,148)),e.a}function Xf(e){return e.a?e.e?Xf(e.e):null:e}function Xd(e,t){return e.pt.p?-1:0}function Xh(e,t){return BJ(t),e.c=0,"Initial capacity must not be negative")}function XO(){XO=A,e3R=euY((etx(),eow(vx(e26,1),eU4,232,0,[e3D,e3N,e3P])))}function XA(){XA=A,e3B=euY((Qs(),eow(vx(e27,1),eU4,461,0,[e3F,e3j,e3Y])))}function XL(){XL=A,e3z=euY((QQ(),eow(vx(e3t,1),eU4,462,0,[e3$,e3H,e3U])))}function XC(){XC=A,e2$=euY((eum(),eow(vx(e2L,1),eU4,132,0,[e2B,e2U,e2H])))}function XI(){XI=A,e96=euY((QJ(),eow(vx(e4S,1),eU4,379,0,[e94,e93,e95])))}function XD(){XD=A,e8T=euY((ec4(),eow(vx(e4L,1),eU4,423,0,[e8x,e8k,e8S])))}function XN(){XN=A,teY=euY((en7(),eow(vx(e4q,1),eU4,314,0,[tej,teR,teF])))}function XP(){XP=A,te$=euY((enB(),eow(vx(e4Z,1),eU4,337,0,[teB,teH,teU])))}function XR(){XR=A,te2=euY((eoG(),eow(vx(e4Q,1),eU4,450,0,[te1,teQ,te0])))}function Xj(){Xj=A,teb=euY((erX(),eow(vx(e4G,1),eU4,361,0,[tep,teh,ted])))}function XF(){XF=A,ttP=euY((Q1(),eow(vx(e46,1),eU4,303,0,[ttD,ttN,ttI])))}function XY(){XY=A,ttC=euY((eaU(),eow(vx(e45,1),eU4,292,0,[ttA,ttL,ttO])))}function XB(){XB=A,tsa=euY((euJ(),eow(vx(e5e,1),eU4,378,0,[tsn,tsr,tsi])))}function XU(){XU=A,tsB=euY((ei0(),eow(vx(e5s,1),eU4,375,0,[tsj,tsF,tsY])))}function XH(){XH=A,tsA=euY((esn(),eow(vx(e5i,1),eU4,339,0,[tsM,tsT,tsO])))}function X$(){X$=A,tsR=euY((enY(),eow(vx(e5o,1),eU4,452,0,[tsP,tsD,tsN])))}function Xz(){Xz=A,ts0=euY((eox(),eow(vx(e5f,1),eU4,377,0,[tsQ,ts1,tsJ])))}function XG(){XG=A,tsK=euY((euy(),eow(vx(e5c,1),eU4,336,0,[tsz,tsG,tsW])))}function XW(){XW=A,tsX=euY((eiO(),eow(vx(e5l,1),eU4,338,0,[tsZ,tsV,tsq])))}function XK(){XK=A,tuo=euY((enU(),eow(vx(e5p,1),eU4,454,0,[tur,tui,tua])))}function XV(){XV=A,tc0=euY((efx(),eow(vx(e5D,1),eU4,442,0,[tc1,tcJ,tcQ])))}function Xq(){Xq=A,tc8=euY((eub(),eow(vx(e5P,1),eU4,380,0,[tc5,tc6,tc9])))}function XZ(){XZ=A,tlj=euY((efS(),eow(vx(e5Y,1),eU4,381,0,[tlP,tlR,tlN])))}function XX(){XX=A,tlD=euY((ei1(),eow(vx(e5j,1),eU4,293,0,[tlC,tlI,tlL])))}function XJ(){XJ=A,tfp=euY((efk(),eow(vx(e5H,1),eU4,437,0,[tff,tfd,tfh])))}function XQ(){XQ=A,tpK=euY((eck(),eow(vx(e57,1),eU4,334,0,[tpG,tpz,tpW])))}function X1(){X1=A,tpk=euY((etT(),eow(vx(e56,1),eU4,272,0,[tp_,tpE,tpS])))}function X0(){return ewf(),eow(vx(e6r,1),eU4,98,0,[tbl,tbc,tbu,tba,tbs,tbo])}function X2(e,t){return e.o||(e.o=new JY((eBa(),tmy),e6O,e,0)),edG(e.o,t)}function X3(e){return e.g||(e.g=new o2),e.g.d||(e.g.d=new pD(e)),e.g.d}function X4(e){return e.g||(e.g=new o2),e.g.a||(e.g.a=new pN(e)),e.g.a}function X5(e){return e.g||(e.g=new o2),e.g.b||(e.g.b=new pI(e)),e.g.b}function X6(e){return e.g||(e.g=new o2),e.g.c||(e.g.c=new pP(e)),e.g.c}function X9(e,t,n){var r,i;for(r=0,i=new eaN(t,e);rn||t=0?e._g(n,!0,!0):exk(e,t,!0)}function JW(e,t){return elN(gP(LV(e_k(e,(eBU(),tnv)))),gP(LV(e_k(t,tnv))))}function JK(){JK=A,tcG=ehY(ehY(_G(new K2,(egR(),tuQ)),(eS_(),tu8)),tu4)}function JV(e,t,n){var r;return r=esg(e,t,n),e.b=new erH(r.c.length),eLI(e,r)}function Jq(e){if(e.b<=0)throw p7(new bC);return--e.b,e.a-=e.c.c,ell(e.a)}function JZ(e){var t;if(!e.a)throw p7(new UD);return t=e.a,e.a=z$(e.a),t}function JX(e){for(;!e.a;)if(!IM(e.c,new dr(e)))return!1;return!0}function JJ(e){var t;return(Y9(e),M4(e,198))?t=Pp(e,198):new lp(e)}function JQ(e){J1(),Pp(e.We((eBB(),thJ)),174).Fc((ekU(),tbb)),e.Ye(thX,null)}function J1(){J1=A,tdo=new os,tdu=new ou,tds=es0((eBB(),thX),tdo,thL,tdu)}function J0(){J0=A,tc2=new S2("LEAF_NUMBER",0),tc3=new S2("NODE_SIZE",1)}function J2(e,t,n){e.a=t,e.c=n,e.b.a.$b(),HC(e.d),e.e.a.c=Je(e1R,eUp,1,0,5,1)}function J3(e){e.a=Je(ty_,eHT,25,e.b+1,15,1),e.c=Je(ty_,eHT,25,e.b,15,1),e.d=0}function J4(e,t){e.a.ue(t.d,e.b)>0&&(P_(e.c,new PW(t.c,t.d,e.d)),e.b=t.d)}function J5(e,t){if(null==e.g||t>=e.i)throw p7(new xJ(t,e.i));return e.g[t]}function J6(e,t,n){if(euu(e,n),null!=n&&!e.wj(n))throw p7(new bS);return n}function J9(e){var t;if(e.Ek())for(t=e.i-1;t>=0;--t)etj(e,t);return VY(e)}function J8(e){var t,n;if(!e.b)return null;for(n=e.b;t=n.a[0];)n=t;return n}function J7(e,t){var n,r;return qe(t),(n=QO(r=e.slice(0,t),e)).length=t,n}function Qe(e,t,n,r){var i;r=(HF(),r||e2s),eS0(i=e.slice(t,n),e,t,n,-t,r)}function Qt(e,t,n,r,i){return t<0?exk(e,n,r):Pp(n,66).Nj().Pj(e,e.yh(),t,r,i)}function Qn(e){return M4(e,172)?""+Pp(e,172).a:null==e?null:efF(e)}function Qr(e){return M4(e,172)?""+Pp(e,172).a:null==e?null:efF(e)}function Qi(e,t){if(t.a)throw p7(new go(e$P));Yf(e.a,t),t.a=e,e.j||(e.j=t)}function Qa(e,t){xj.call(this,t.rd(),-16449&t.qd()),BJ(e),this.a=e,this.c=t}function Qo(e,t){var n,r;return r=t/e.c.Hd().gc()|0,n=t%e.c.Hd().gc(),X_(e,r,n)}function Qs(){Qs=A,e3F=new EY(ezt,0),e3j=new EY(e$8,1),e3Y=new EY(ezn,2)}function Qu(){Qu=A,e2D=new Ef("All",0),e2N=new TH,e2P=new ML,e2R=new T$}function Qc(){Qc=A,e2j=euY((Qu(),eow(vx(e2E,1),eU4,297,0,[e2D,e2N,e2P,e2R])))}function Ql(){Ql=A,e8b=euY((eok(),eow(vx(e4A,1),eU4,405,0,[e8f,e8p,e8d,e8h])))}function Qf(){Qf=A,e4l=euY((eaY(),eow(vx(e3r,1),eU4,406,0,[e4c,e4o,e4s,e4u])))}function Qd(){Qd=A,e4b=euY((ep7(),eow(vx(e3i,1),eU4,323,0,[e4d,e4f,e4h,e4p])))}function Qh(){Qh=A,e6z=euY((ebe(),eow(vx(e4y,1),eU4,394,0,[e6U,e6B,e6H,e6$])))}function Qp(){Qp=A,tu2=euY((egR(),eow(vx(e5T,1),eU4,393,0,[tuJ,tuQ,tu1,tu0])))}function Qb(){Qb=A,e7X=euY((eoE(),eow(vx(e4U,1),eU4,360,0,[e7Z,e7V,e7q,e7K])))}function Qm(){Qm=A,tlA=euY((emC(),eow(vx(e5R,1),eU4,340,0,[tlO,tlT,tlM,tlx])))}function Qg(){Qg=A,tet=euY((eoS(),eow(vx(e4$,1),eU4,411,0,[e79,e78,e77,tee])))}function Qv(){Qv=A,tsl=euY((ebk(),eow(vx(e5t,1),eU4,197,0,[tsu,tsc,tss,tso])))}function Qy(){Qy=A,tmo=euY((eup(),eow(vx(e6l,1),eU4,396,0,[tmr,tmi,tmn,tma])))}function Qw(){Qw=A,tpJ=euY((egF(),eow(vx(e6e,1),eU4,285,0,[tpX,tpV,tpq,tpZ])))}function Q_(){Q_=A,tpA=euY((efE(),eow(vx(e59,1),eU4,218,0,[tpO,tpT,tpx,tpM])))}function QE(){QE=A,tmt=euY((edM(),eow(vx(e6u,1),eU4,311,0,[tme,tb9,tb7,tb8])))}function QS(){QS=A,tbZ=euY((ed6(),eow(vx(e6o,1),eU4,374,0,[tbV,tbq,tbK,tbW])))}function Qk(){Qk=A,ePm(),tvq=eHQ,tvV=eH1,tvX=new fL(eHQ),tvZ=new fL(eH1)}function Qx(){Qx=A,ttt=new Sb(eGR,0),tte=new Sb("IMPROVE_STRAIGHTNESS",1)}function QT(e,t){return Pj(),P_(e,new kD(t,ell(t.e.c.length+t.g.c.length)))}function QM(e,t){return Pj(),P_(e,new kD(t,ell(t.e.c.length+t.g.c.length)))}function QO(e,t){return 10!=eeg(t)&&eow(esF(t),t.hm,t.__elementTypeId$,eeg(t),e),e}function QA(e,t){var n;return -1!=(n=QI(e,t,0))&&(ZV(e,n),!0)}function QL(e,t){var n;return(n=Pp(Z3(e.e,t),387))?(Re(n),n.e):null}function QC(e){var t;return Ts(e)&&!isNaN(t=0-e)?t:eal(eoQ(e))}function QI(e,t,n){for(;n=0?ebl(e,n,!0,!0):exk(e,t,!0)}function Q8(e,t){var n,r;return _L(),n=Vm(e),r=Vm(t),!!n&&!!r&&!ep5(n.k,r.k)}function Q7(e,t){eno(e,null==t||IX((BJ(t),t))||isNaN((BJ(t),t))?0:(BJ(t),t))}function eee(e,t){ens(e,null==t||IX((BJ(t),t))||isNaN((BJ(t),t))?0:(BJ(t),t))}function eet(e,t){ena(e,null==t||IX((BJ(t),t))||isNaN((BJ(t),t))?0:(BJ(t),t))}function een(e,t){eni(e,null==t||IX((BJ(t),t))||isNaN((BJ(t),t))?0:(BJ(t),t))}function eer(e){(this.q?this.q:(Hj(),Hj(),e2i)).Ac(e.q?e.q:(Hj(),Hj(),e2i))}function eei(e,t){return M4(t,99)&&(Pp(t,18).Bb&eH3)!=0?new x1(t,e):new eaN(t,e)}function eea(e,t){return M4(t,99)&&(Pp(t,18).Bb&eH3)!=0?new x1(t,e):new eaN(t,e)}function eeo(e,t){e4g=new e0,e4v=t,Pp((e4m=e).b,65),Jr(e4m,e4g,null),eRk(e4m)}function ees(e,t,n){var r;return r=e.g[t],Of(e,t,e.oi(t,n)),e.gi(t,n,r),e.ci(),r}function eeu(e,t){var n;return(n=e.Xc(t))>=0&&(e.$c(n),!0)}function eec(e){var t;return e.d!=e.r&&(t=evl(e),e.e=!!t&&t.Cj()==eJK,e.d=t),e.e}function eel(e,t){var n;for(Y9(e),Y9(t),n=!1;t.Ob();)n|=e.Fc(t.Pb());return n}function eef(e,t){var n;return(n=Pp(Bp(e.e,t),387))?(M6(e,n),n.e):null}function eed(e){var t,n;return(t=e/60|0,0==(n=e%60))?""+t:""+t+":"+n}function eeh(e,t){var n,r;return el3(e),r=new Xe(t,e.a),n=new IU(r),new R1(e,n)}function eep(e,t){var n=e.a[t],r=(eoW(),e0O)[typeof n];return r?r(n):euV(typeof n)}function eeb(e){switch(e.g){case 0:return eUu;case 1:return -1;default:return 0}}function eem(e){return 0>evy(e,(Q2(),e0D))?-As(eoQ(e)):e.l+e.m*eHG+e.h*eHW}function eeg(e){return null==e.__elementTypeCategory$?10:e.__elementTypeCategory$}function eev(e){var t;return null!=(t=0==e.b.c.length?null:RJ(e.b,0))&&erD(e,0),t}function eey(e,t){for(;t[0]=0;)++t[0]}function eew(e,t){this.e=t,this.a=eaJ(e),this.a<54?this.f=Kj(e):this.c=ep_(e)}function ee_(e,t,n,r){eBG(),pJ.call(this,26),this.c=e,this.a=t,this.d=n,this.b=r}function eeE(e,t,n){var r,i;for(i=0,r=10;ie.a[r]&&(r=n);return r}function eeI(e,t){var n;return 0==(n=efT(e.e.c,t.e.c))?elN(e.e.d,t.e.d):n}function eeD(e,t){return 0==t.e||0==e.e?e08:(exX(),eAl(e,t))}function eeN(e,t){if(!e)throw p7(new gL(eAL("Enum constant undefined: %s",t)))}function eeP(){eeP=A,e8v=new tp,e8y=new td,e8m=new ty,e8g=new tw,e8w=new t_}function eeR(){eeR=A,e3h=new ER("BY_SIZE",0),e3p=new ER("BY_SIZE_AND_SHAPE",1)}function eej(){eej=A,e9f=new EH("EADES",0),e9d=new EH("FRUCHTERMAN_REINGOLD",1)}function eeF(){eeF=A,teZ=new Sd("READING_DIRECTION",0),teX=new Sd("ROTATION",1)}function eeY(){eeY=A,teq=euY((eb6(),eow(vx(e4X,1),eU4,335,0,[teG,tez,teK,teV,teW])))}function eeB(){eeB=A,tsm=euY((ebG(),eow(vx(e5n,1),eU4,315,0,[tsb,tsd,tsh,tsf,tsp])))}function eeU(){eeU=A,tes=euY((ey4(),eow(vx(e4z,1),eU4,363,0,[ter,tea,teo,tei,ten])))}function eeH(){eeH=A,tnF=euY((ef_(),eow(vx(e48,1),eU4,163,0,[tnj,tnD,tnN,tnP,tnR])))}function ee$(){ee$=A,tfM=euY((eEM(),eow(vx(e5W,1),eU4,316,0,[tfE,tfS,tfT,tfk,tfx])))}function eez(){eez=A,tdb=euY((epx(),eow(vx(e5Q,1),eU4,175,0,[tdh,tdd,tdl,tdp,tdf])))}function eeG(){eeG=A,tfl=euY((eOB(),eow(vx(e5U,1),eU4,355,0,[tfo,tfa,tfu,tfs,tfc])))}function eeW(){eeW=A,e8a=euY((e_x(),eow(vx(e4k,1),eU4,356,0,[e8e,e8t,e8n,e8r,e8i])))}function eeK(){eeK=A,tpw=euY((ec3(),eow(vx(e55,1),eU4,103,0,[tpv,tpg,tpm,tpb,tpy])))}function eeV(){eeV=A,tbi=euY((epT(),eow(vx(e6n,1),eU4,249,0,[tbt,tbr,tp7,tbe,tbn])))}function eeq(){eeq=A,tbB=euY((eYu(),eow(vx(e6a,1),eGj,61,0,[tbF,tbw,tby,tbj,tbY])))}function eeZ(e,t){var n;return(n=Pp(Bp(e.a,t),134))||(n=new eX,Um(e.a,t,n)),n}function eeX(e){var t;return!!(t=Pp(e_k(e,(eBU(),ttU)),305))&&t.a==e}function eeJ(e){var t;return!!(t=Pp(e_k(e,(eBU(),ttU)),305))&&t.i==e}function eeQ(e,t){return BJ(t),FD(e),!!e.d.Ob()&&(t.td(e.d.Pb()),!0)}function ee1(e){return ecd(e,eUu)>0?eUu:0>ecd(e,eHt)?eHt:jE(e)}function ee0(e){return e<3?(enG(e,eU0),e+1):e=0&&t=-.01&&e.a<=ezs&&(e.a=0),e.b>=-.01&&e.b<=ezs&&(e.b=0),e}function ee5(e,t){return t==(I8(),I8(),e2p)?e.toLocaleLowerCase():e.toLowerCase()}function ee6(e){return((2&e.i)!=0?"interface ":(1&e.i)!=0?"":"class ")+(LW(e),e.o)}function ee9(e){var t,n;n=t=new mD,JL((e.q||(e.q=new FQ(tgi,e,11,10)),e.q),n)}function ee8(e,t){var n;return n=t>0?t-1:t,yr(yi(eny(P6(new mV,n),e.n),e.j),e.k)}function ee7(e,t,n,r){var i;e.j=-1,ex8(e,eSu(e,t,n),(_4(),(i=Pp(t,66).Mj()).Ok(r)))}function ete(e){this.g=e,this.f=new p0,this.a=eB4.Math.min(this.g.c.c,this.g.d.c)}function ett(e){this.b=new p0,this.a=new p0,this.c=new p0,this.d=new p0,this.e=e}function etn(e,t){this.a=new p2,this.e=new p2,this.b=(euJ(),tsi),this.c=e,this.b=t}function etr(e,t,n){CK.call(this),etk(this),this.a=e,this.c=n,this.b=t.d,this.f=t.e}function eti(e){this.d=e,this.c=e.c.vc().Kc(),this.b=null,this.a=null,this.e=(m5(),e0m)}function eta(e){if(e<0)throw p7(new gL("Illegal Capacity: "+e));this.g=this.ri(e)}function eto(e,t){if(0>e||e>t)throw p7(new va("fromIndex: 0, toIndex: "+e+e$m+t))}function ets(e){var t;if(e.a==e.b.a)throw p7(new bC);return t=e.a,e.c=t,e.a=e.a.e,t}function etu(e){var t;A4(!!e.c),t=e.c.a,etw(e.d,e.c),e.b==e.c?e.b=t:--e.a,e.c=null}function etc(e,t){var n;return el3(e),n=new HA(e,e.a.rd(),4|e.a.qd(),t),new R1(e,n)}function etl(e,t){var n,r;return(n=Pp(ecA(e.d,t),14))?(r=t,e.e.pc(r,n)):null}function etf(e,t){var n,r;for(r=e.Kc();r.Ob();)eo3(n=Pp(r.Pb(),70),(eBU(),tnt),t)}function etd(e){var t;return(t=gP(LV(e_k(e,(eBy(),tak)))))<0&&eo3(e,tak,t=0),t}function eth(e,t,n){var r;ev_(n,r=eB4.Math.max(0,e.b/2-.5),1),P_(t,new EJ(n,r))}function etp(e,t,n){var r;return zy(Ra(r=e.a.e[Pp(t.a,10).p]-e.a.e[Pp(n.a,10).p]))}function etb(e,t,n,r,i,a){var o;o=ZD(r),Gs(o,i),Go(o,a),exg(e.a,r,new DT(o,t,n.f))}function etm(e,t){var n;if(!(n=eAh(e.Tg(),t)))throw p7(new gL(eZV+t+eZX));return n}function etg(e,t){var n;for(n=e;z$(n);)if((n=z$(n))==t)return!0;return!1}function etv(e,t){var n,r,i;for(i=0,r=t.a.cd(),n=Pp(t.a.dd(),14).gc();i0&&(e.a/=t,e.b/=t),e}function etP(e){var t;return e.w?e.w:((t=Ww(e))&&!t.kh()&&(e.w=t),t)}function etR(e){var t;return null==e?null:e_e(t=Pp(e,190),t.length)}function etj(e,t){if(null==e.g||t>=e.i)throw p7(new xJ(t,e.i));return e.li(t,e.g[t])}function etF(e){var t,n;for(t=e.a.d.j,n=e.c.d.j;t!=n;)erC(e.b,t),t=elI(t);erC(e.b,t)}function etY(e){var t;for(t=0;t=14&&t<=16)),e}function etW(e,t,n){var r=function(){return e.apply(r,arguments)};return t.apply(r,n),r}function etK(e,t,n){var r,i;r=t;do i=gP(e.p[r.p])+n,e.p[r.p]=i,r=e.a[r.p];while(r!=t)}function etV(e,t){var n,r;r=e.a,n=elr(e,t,null),r==t||e.e||(n=eFr(e,t,n)),n&&n.Fi()}function etq(e,t){return Mc(),enj(eHe),eB4.Math.abs(e-t)<=eHe||e==t||isNaN(e)&&isNaN(t)}function etZ(e,t){return Mc(),enj(eHe),eB4.Math.abs(e-t)<=eHe||e==t||isNaN(e)&&isNaN(t)}function etX(e,t){return e_z(),ME(e.b.c.length-e.e.c.length,t.b.c.length-t.e.c.length)}function etJ(e,t){return yk(eif(e,t,jE(efn(eUJ,Ux(jE(efn(null==t?0:esj(t),eUQ)),15)))))}function etQ(){etQ=A,e8R=euY((eEn(),eow(vx(e4P,1),eU4,267,0,[e8N,e8D,e8C,e8P,e8I,e8L])))}function et1(){et1=A,tdJ=euY((eyY(),eow(vx(e54,1),eU4,291,0,[tdX,tdZ,tdq,tdK,tdW,tdV])))}function et0(){et0=A,tdD=euY((ebx(),eow(vx(e53,1),eU4,248,0,[tdM,tdL,tdC,tdI,tdO,tdA])))}function et2(){et2=A,teI=euY((eSg(),eow(vx(e4K,1),eU4,227,0,[teO,teL,teM,teA,teC,teT])))}function et3(){et3=A,ttm=euY((e_3(),eow(vx(e43,1),eU4,275,0,[ttp,ttf,ttb,tth,ttd,ttl])))}function et4(){et4=A,ttc=euY((eyd(),eow(vx(e42,1),eU4,274,0,[tto,tta,ttu,tti,tts,ttr])))}function et5(){et5=A,tst=euY((ewY(),eow(vx(e47,1),eU4,313,0,[to7,to9,to5,to6,tse,to8])))}function et6(){et6=A,te7=euY((eEf(),eow(vx(e41,1),eU4,276,0,[te4,te3,te6,te5,te8,te9])))}function et9(){et9=A,tu7=euY((eS_(),eow(vx(e5A,1),eU4,327,0,[tu8,tu4,tu6,tu5,tu9,tu3])))}function et8(){et8=A,tbv=euY((ekU(),eow(vx(e6i,1),eU4,273,0,[tbm,tbp,tbb,tbh,tbd,tbg])))}function et7(){et7=A,tpR=euY((e_a(),eow(vx(e58,1),eU4,312,0,[tpN,tpI,tpP,tpL,tpD,tpC])))}function ene(){return eT7(),eow(vx(e6t,1),eU4,93,0,[tp1,tpQ,tp2,tp9,tp6,tp5,tp3,tp4,tp0])}function ent(e,t){var n;n=e.a,e.a=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,0,n,e.a))}function enn(e,t){var n;n=e.b,e.b=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,1,n,e.b))}function enr(e,t){var n;n=e.b,e.b=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,3,n,e.b))}function eni(e,t){var n;n=e.f,e.f=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,3,n,e.f))}function ena(e,t){var n;n=e.g,e.g=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,4,n,e.g))}function eno(e,t){var n;n=e.i,e.i=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,5,n,e.i))}function ens(e,t){var n;n=e.j,e.j=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,6,n,e.j))}function enu(e,t){var n;n=e.j,e.j=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,1,n,e.j))}function enc(e,t){var n;n=e.c,e.c=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,4,n,e.c))}function enl(e,t){var n;n=e.k,e.k=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qo(e,2,n,e.k))}function enf(e,t){var n;n=e.d,e.d=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qs(e,2,n,e.d))}function end(e,t){var n;n=e.s,e.s=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qs(e,4,n,e.s))}function enh(e,t){var n;n=e.t,e.t=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new qs(e,5,n,e.t))}function enp(e,t){var n;n=e.F,e.F=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,5,n,t))}function enb(e,t){var n;return(n=Pp(Bp((_5(),tmU),e),55))?n.xj(t):Je(e1R,eUp,1,t,5,1)}function enm(e,t){var n,r;return(n=t in e.a)&&(r=zR(e,t).he())?r.a:null}function eng(e,t){var n,r,i;return n=(r=(yT(),i=new o0),t&&eAu(r,t),r),eri(n,e),n}function env(e,t,n){if(euu(e,n),!e.Bk()&&null!=n&&!e.wj(n))throw p7(new bS);return n}function eny(e,t){return e.n=t,e.n?(e.f=new p0,e.e=new p0):(e.f=null,e.e=null),e}function enw(e,t,n,r,i,a){var o;return enA(n,o=Y6(e,t)),o.i=i?8:0,o.f=r,o.e=i,o.g=a,o}function en_(e,t,n,r,i){this.d=t,this.k=r,this.f=i,this.o=-1,this.p=1,this.c=e,this.a=n}function enE(e,t,n,r,i){this.d=t,this.k=r,this.f=i,this.o=-1,this.p=2,this.c=e,this.a=n}function enS(e,t,n,r,i){this.d=t,this.k=r,this.f=i,this.o=-1,this.p=6,this.c=e,this.a=n}function enk(e,t,n,r,i){this.d=t,this.k=r,this.f=i,this.o=-1,this.p=7,this.c=e,this.a=n}function enx(e,t,n,r,i){this.d=t,this.j=r,this.e=i,this.o=-1,this.p=4,this.c=e,this.a=n}function enT(e,t){var n,r,i,a;for(i=0,a=(r=t).length;i=0),0>ehP(e.d,e.c)&&(e.a=e.a-1&e.d.a.length-1,e.b=e.d.c),e.c=-1}function enR(e){return e.a<54?e.f<0?-1:e.f>0?1:0:(e.c||(e.c=euK(e.f)),e.c).e}function enj(e){if(!(e>=0))throw p7(new gL("tolerance ("+e+") must be >= 0"));return e}function enF(){return tdc||(tdc=new eC$,es4(tdc,eow(vx(e20,1),eUp,130,0,[new cZ]))),tdc}function enY(){enY=A,tsP=new SL(ezo,0),tsD=new SL("INPUT",1),tsN=new SL("OUTPUT",2)}function enB(){enB=A,teB=new Sl("ARD",0),teH=new Sl("MSD",1),teU=new Sl("MANUAL",2)}function enU(){enU=A,tur=new SR("BARYCENTER",0),tui=new SR(eG7,1),tua=new SR(eWe,2)}function enH(e,t){var n;if(n=e.gc(),t<0||t>n)throw p7(new Ii(t,n));return new IB(e,t)}function en$(e,t){var n;return M4(t,42)?e.c.Mc(t):(n=edG(e,t),ehx(e,t),n)}function enz(e,t,n){return eu2(e,t),er3(e,n),end(e,0),enh(e,1),els(e,!0),eli(e,!0),e}function enG(e,t){if(e<0)throw p7(new gL(t+" cannot be negative but was: "+e));return e}function enW(e,t){var n,r;for(n=0,r=e.gc();n0)?Pp(RJ(n.a,r-1),10):null}function ert(e,t){var n;n=e.k,e.k=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,2,n,e.k))}function ern(e,t){var n;n=e.f,e.f=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,8,n,e.f))}function err(e,t){var n;n=e.i,e.i=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,7,n,e.i))}function eri(e,t){var n;n=e.a,e.a=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,8,n,e.a))}function era(e,t){var n;n=e.b,e.b=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,0,n,e.b))}function ero(e,t){var n;n=e.b,e.b=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,0,n,e.b))}function ers(e,t){var n;n=e.c,e.c=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,1,n,e.c))}function eru(e,t){var n;n=e.c,e.c=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,1,n,e.c))}function erc(e,t){var n;n=e.c,e.c=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,4,n,e.c))}function erl(e,t){var n;n=e.d,e.d=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,1,n,e.d))}function erf(e,t){var n;n=e.D,e.D=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,2,n,e.D))}function erd(e,t){e.r>0&&e.c0&&0!=e.g&&erd(e.i,t/e.r*e.i.d))}function erh(e,t,n){var r;e.b=t,e.a=n,r=(512&e.a)==512?new mU:new u7,e.c=eLV(r,e.b,e.a)}function erp(e,t){return eLt(e.e,t)?(_4(),eec(t)?new RA(t,e):new xe(t,e)):new xr(t,e)}function erb(e,t){return yS(eid(e.a,t,jE(efn(eUJ,Ux(jE(efn(null==t?0:esj(t),eUQ)),15)))))}function erm(e,t,n){return Qz(e,new f9(t),new ea,new f8(n),eow(vx(e2L,1),eU4,132,0,[]))}function erg(e){var t,n;return 0>e?new _e:(t=e+1,n=new Zi(t,e),new L0(null,n))}function erv(e,t){var n;return Hj(),n=new w8(1),xd(e)?Ge(n,e,t):eS9(n.f,e,t),new f$(n)}function ery(e,t){var n,r;return(n=e.o+e.p)<(r=t.o+t.p)?-1:n==r?0:1}function erw(e){var t;return(t=e_k(e,(eBU(),tnc)),M4(t,160))?edo(Pp(t,160)):null}function er_(e){var t;return(t=esi(e=eB4.Math.max(e,2)),e>t)?(t<<=1)>0?t:eU2:t}function erE(e){switch(OZ(3!=e.e),e.e){case 2:return!1;case 0:return!0}return Zk(e)}function erS(e,t){var n;return!!M4(t,8)&&(n=Pp(t,8),e.a==n.a&&e.b==n.b)}function erk(e,t,n){var r,i,a;return a=t>>5,i=31&t,r=WM(Fy(e.n[n][a],jE(Fg(i,1))),3)}function erx(e,t){var n,r;for(r=t.vc().Kc();r.Ob();)evQ(e,(n=Pp(r.Pb(),42)).cd(),n.dd())}function erT(e,t){var n;n=new e0,Pp(t.b,65),Pp(t.b,65),Pp(t.b,65),ety(t.a,new N9(e,n,t))}function erM(e,t){var n;n=e.b,e.b=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,21,n,e.b))}function erO(e,t){var n;n=e.d,e.d=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,11,n,e.d))}function erA(e,t){var n;n=e.j,e.j=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,13,n,e.j))}function erL(e,t,n){var r,i,a;for(a=e.a.length-1,i=e.b,r=0;r>>31;0!=r&&(e[n]=r)}function eip(e,t){var n,r;for(Hj(),r=new p0,n=0;n0&&(this.g=this.ri(this.i+(this.i/8|0)+1),e.Qc(this.g))}function eiR(e,t){PJ.call(this,tgd,e,t),this.b=this,this.a=eAY(e.Tg(),ee2(this.e.Tg(),this.c))}function eij(e,t){var n,r;for(BJ(t),r=t.vc().Kc();r.Ob();)n=Pp(r.Pb(),42),e.zc(n.cd(),n.dd())}function eiF(e,t,n){var r;for(r=n.Kc();r.Ob();)if(!Vq(e,t,r.Pb()))return!1;return!0}function eiY(e,t,n,r,i){var a;return n&&(a=edv(t.Tg(),e.c),i=n.gh(t,-1-(-1==a?r:a),null,i)),i}function eiB(e,t,n,r,i){var a;return n&&(a=edv(t.Tg(),e.c),i=n.ih(t,-1-(-1==a?r:a),null,i)),i}function eiU(e){var t;if(-2==e.b){if(0==e.e)t=-1;else for(t=0;0==e.a[t];t++);e.b=t}return e.b}function eiH(e){switch(e.g){case 2:return eYu(),tbY;case 4:return eYu(),tby;default:return e}}function ei$(e){switch(e.g){case 1:return eYu(),tbj;case 3:return eYu(),tbw;default:return e}}function eiz(e){var t,n,r;return e.j==(eYu(),tbw)&&(t=eTt(e),n=Aa(t,tby),(r=Aa(t,tbY))||r&&n)}function eiG(e){var t,n;return t=Pp(e.e&&e.e(),9),n=Pp(YR(t,t.length),9),new I1(t,n,t.length)}function eiW(e,t){ewG(t,eG9,1),efJ(_p(new dp((__(),new U7(e,!1,!1,new tO))))),eEj(t)}function eiK(e,t){return OQ(),xd(e)?ZZ(e,Lq(t)):xf(e)?F_(e,LV(t)):xl(e)?Fw(e,LK(t)):e.wd(t)}function eiV(e,t){t.q=e,e.d=eB4.Math.max(e.d,t.r),e.b+=t.d+(0==e.a.c.length?0:e.c),P_(e.a,t)}function eiq(e,t){var n,r,i,a;return i=e.c,n=e.c+e.b,a=e.d,r=e.d+e.a,t.a>i&&t.aa&&t.b1||e.Ob())return++e.a,e.g=0,t=e.i,e.Ob(),t;throw p7(new bC)}function eaA(e){var t;return Ma(),En(tuT,e)||((t=new af).a=e,CM(tuT,e,t)),Pp(UA(tuT,e),635)}function eaL(e){var t,n,r,i;return r=0,(i=e)<0&&(i+=eHW,r=eH$),n=zy(i/eHG),Mk(t=zy(i-n*eHG),n,r)}function eaC(e){var t,n,r;for(r=0,n=new _t(e.a);n.aecd(e,0)&&(e=PN(e)),64-(0!=(t=jE(Fv(e,32)))?exv(t):exv(jE(e))+32)}function eaQ(e){var t;return t=Pp(e_k(e,(eBU(),tt1)),61),e.k==(eEn(),e8C)&&(t==(eYu(),tbY)||t==tby)}function ea1(e,t,n){var r,i;(i=Pp(e_k(e,(eBy(),taR)),74))&&(eu_(r=new mE,0,i),etH(r,n),er7(t,r))}function ea0(e,t,n){var r,i,a,o;r=(o=Bq(e)).d,i=o.c,a=e.n,t&&(a.a=a.a-r.b-i.a),n&&(a.b=a.b-r.d-i.b)}function ea2(e,t){var n,r;return(n=e.j)!=(r=t.j)?n.g-r.g:e.p==t.p?0:n==(eYu(),tbw)?e.p-t.p:t.p-e.p}function ea3(e){var t,n;for(eYp(e),n=new fz(e.d);n.a>22),i=e.h+t.h+(r>>22),Mk(n&eHH,r&eHH,i&eH$)}function eor(e,t){var n,r,i;return n=e.l-t.l,r=e.m-t.m+(n>>22),i=e.h-t.h+(r>>22),Mk(n&eHH,r&eHH,i&eH$)}function eoi(e){var t;return e<128?((t=(RH(),e0Y)[e])||(t=e0Y[e]=new fA(e)),t):new fA(e)}function eoa(e){var t;return M4(e,78)?e:((t=e&&e.__java$exception)||(t=new euq(e),by(t)),t)}function eoo(e){if(M4(e,186))return Pp(e,118);if(e)return null;throw p7(new gD(eXR))}function eos(e,t){if(null==t)return!1;for(;e.a!=e.b;)if(ecX(t,ecn(e)))return!0;return!1}function eou(e){return!!e.a.Ob()||e.a==e.d&&(e.a=new KU(e.e.f),e.a.Ob())}function eoc(e,t){var n,r;return 0!=(r=(n=t.Pc()).length)&&(PO(e.c,e.c.length,n),!0)}function eol(e,t,n){var r,i;for(i=t.vc().Kc();i.Ob();)r=Pp(i.Pb(),42),e.yc(r.cd(),r.dd(),n);return e}function eof(e,t){var n,r;for(r=new fz(e.b);r.a=0,"Negative initial capacity"),PG(t>=0,"Non-positive load factor"),Yy(this)}function eoV(e,t,n){return!(e>=128)&&(e<64?xg(WM(Fg(1,e),n),0):xg(WM(Fg(1,e-64),t),0))}function eoq(e,t){return!!e&&!!t&&e!=t&&0>efT(e.b.c,t.b.c+t.b.b)&&0>efT(t.b.c,e.b.c+e.b.b)}function eoZ(e){var t,n,r;return n=e.n,r=e.o,t=e.d,new Hr(n.a-t.b,n.b-t.d,r.a+(t.b+t.c),r.b+(t.d+t.a))}function eoX(e){var t,n,r,i;for(n=e.a,r=0,i=n.length;r(r=e.gc()))throw p7(new Ii(t,r));return e.hi()&&(n=zG(e,n)),e.Vh(t,n)}function eo2(e,t,n){return null==n?(e.q||(e.q=new p2),Z3(e.q,t)):(e.q||(e.q=new p2),Um(e.q,t,n)),e}function eo3(e,t,n){return null==n?(e.q||(e.q=new p2),Z3(e.q,t)):(e.q||(e.q=new p2),Um(e.q,t,n)),e}function eo4(e){var t,n;return n=new Z5,eaW(n,e),eo3(n,(erV(),e9j),e),t=new p2,eNY(e,n,t),eFS(e,n,t),n}function eo5(e){var t,n,r;for(eLG(),n=Je(e50,eUP,8,2,0,1),r=0,t=0;t<2;t++)r+=.5,n[t]=emh(r,e);return n}function eo6(e,t){var n,r,i,a;for(a=0,n=!1,r=e.a[t].length;a>=1);return t}function esa(e){var t,n;return 32==(n=exv(e.h))?32==(t=exv(e.m))?exv(e.l)+32:t+20-10:n-12}function eso(e){var t;return null==(t=e.a[e.b])?null:(Bc(e.a,e.b,null),e.b=e.b+1&e.a.length-1,t)}function ess(e){var t,n;return t=e.t-e.k[e.o.p]*e.d+e.j[e.o.p]>e.f,n=e.u+e.e[e.o.p]*e.d>e.f*e.s*e.d,t||n}function esu(e,t,n){var r,i;return r=new Js(t,n),i=new H,e.b=eLg(e,e.b,r,i),i.b||++e.c,e.b.b=!1,i.d}function esc(e,t,n){var r,i,a,o;for(o=ecZ(t,n),a=0,i=o.Kc();i.Ob();)r=Pp(i.Pb(),11),Um(e.c,r,ell(a++))}function esl(e){var t,n;for(n=new fz(e.a.b);n.an&&(n=e[t]);return n}function esg(e,t,n){var r;return r=new p0,eA0(e,t,r,(eYu(),tby),!0,!1),eA0(e,n,r,tbY,!1,!1),r}function esv(e,t,n){var r,i,a,o;return a=null,i=Kq(o=t,"labels"),a=(eT2((r=new kG(e,n)).a,r.b,i),i)}function esy(e,t,n,r){var i;return!(!(i=eMv(e,t,n,r))&&(i=elh(e,n,r)))||eR3(e,t,i)?i:null}function esw(e,t,n,r){var i;return!(!(i=eMy(e,t,n,r))&&(i=elp(e,n,r)))||eR3(e,t,i)?i:null}function es_(e,t){var n;for(n=0;n1||t>=0&&e.b<3)}function esP(e){var t,n,r;for(t=new mE,r=epL(e,0);r.b!=r.d.c;)n=Pp(Vv(r),8),Ls(t,0,new TS(n));return t}function esR(e){var t,n;for(n=new fz(e.a.b);n.ar?1:0}function esJ(e,t){return!!eO2(e,t)&&(exg(e.b,Pp(e_k(t,(eBU(),ttX)),21),t),P7(e.a,t),!0)}function esQ(e){var t,n;(t=Pp(e_k(e,(eBU(),tng)),10))&&(QA((n=t.c).a,t),0==n.a.c.length&&QA(Bq(t).b,n))}function es1(e){return e2M?Je(e2k,e$_,572,0,0,1):Pp(epg(e.a,Je(e2k,e$_,572,e.a.c.length,0,1)),842)}function es0(e,t,n,r){return U_(),new gt(eow(vx(e1$,1),eUK,42,0,[(eb2(e,t),new wD(e,t)),(eb2(n,r),new wD(n,r))]))}function es2(e,t,n){var r,i;return enz(i=r=new mD,t,n),JL((e.q||(e.q=new FQ(tgi,e,11,10)),e.q),i),i}function es3(e){var t,n,r,i;for(t=0,r=Je(e17,eUP,2,n=(i=Eo(tmx,e)).length,6,1);t=e.b.c.length)&&(es6(e,2*t+1),(n=2*t+2)=0&&e[r]===t[r];r--);return r<0?0:Ei(WM(e[r],eH8),WM(t[r],eH8))?-1:1}function es7(e,t){var n,r;for(r=epL(e,0);r.b!=r.d.c;)(n=Pp(Vv(r),214)).e.length>0&&(t.td(n),n.i&&elk(n))}function eue(e,t){var n,r;return r=Pp(eaS(e.a,4),126),n=Je(e6N,eJM,415,t,0,1),null!=r&&ePD(r,0,n,0,r.length),n}function eut(e,t){var n;return n=new eCg((256&e.f)!=0,e.i,e.a,e.d,(16&e.f)!=0,e.j,e.g,t),null!=e.e||(n.c=e),n}function eun(e,t){var n,r;for(r=e.Zb().Cc().Kc();r.Ob();)if((n=Pp(r.Pb(),14)).Hc(t))return!0;return!1}function eur(e,t,n,r,i){var a,o;for(o=n;o<=i;o++)for(a=t;a<=r;a++)if(emy(e,a,o))return!0;return!1}function eui(e,t,n){var r,i,a,o;for(BJ(n),o=!1,a=e.Zc(t),i=n.Kc();i.Ob();)r=i.Pb(),a.Rb(r),o=!0;return o}function eua(e,t){var n;return e===t||!!M4(t,83)&&(n=Pp(t,83),eEB(Fc(e),n.vc()))}function euo(e,t,n){var r,i;for(i=n.Kc();i.Ob();)if(r=Pp(i.Pb(),42),e.re(t,r.dd()))return!0;return!1}function eus(e,t,n){return e.d[t.p][n.p]||(ebp(e,t,n),e.d[t.p][n.p]=!0,e.d[n.p][t.p]=!0),e.a[t.p][n.p]}function euu(e,t){if(!e.ai()&&null==t)throw p7(new gL("The 'no null' constraint is violated"));return t}function euc(e,t){null==e.D&&null!=e.B&&(e.D=e.B,e.B=null),erf(e,null==t?null:(BJ(t),t)),e.C&&e.yk(null)}function eul(e,t){var n;return!!(e&&e!=t&&Ln(t,(eBU(),tt8)))&&(n=Pp(e_k(t,(eBU(),tt8)),10))!=e}function euf(e){switch(e.i){case 2:return!0;case 1:return!1;case -1:++e.c;default:return e.pl()}}function eud(e){switch(e.i){case -2:return!0;case -1:return!1;case 1:--e.c;default:return e.ql()}}function euh(e){zL.call(this,"The given string does not match the expected format for individual spacings.",e)}function eup(){eup=A,tmr=new kN("ELK",0),tmi=new kN("JSON",1),tmn=new kN("DOT",2),tma=new kN("SVG",3)}function eub(){eub=A,tc5=new S3(eGR,0),tc6=new S3("RADIAL_COMPACTION",1),tc9=new S3("WEDGE_COMPACTION",2)}function eum(){eum=A,e2B=new Ed("CONCURRENT",0),e2U=new Ed("IDENTITY_FINISH",1),e2H=new Ed("UNORDERED",2)}function eug(){eug=A,e6q=(_y(),e6G),e6V=new xX(ezj,e6q),e6K=new pO(ezF),e6Z=new pO(ezY),e6X=new pO(ezB)}function euv(){euv=A,e72=new n1,e73=new n0,e70=new n2,e71=new n3,e7J=(BJ(e7Q=new n4),new P)}function euy(){euy=A,tsz=new SD("CONSERVATIVE",0),tsG=new SD("CONSERVATIVE_SOFT",1),tsW=new SD("SLOPPY",2)}function euw(){euw=A,tpH=new T3(15),tpU=new T2((eBB(),thN),tpH),tp$=th3,tpj=td3,tpF=thx,tpB=thO,tpY=thM}function eu_(e,t,n){var r,i,a;for(r=new _n,a=epL(n,0);a.b!=a.d.c;)i=Pp(Vv(a),8),P7(r,new TS(i));eui(e,t,r)}function euE(e){var t,n,r;for(t=0,r=Je(e50,eUP,8,e.b,0,1),n=epL(e,0);n.b!=n.d.c;)r[t++]=Pp(Vv(n),8);return r}function euS(e){var t;return 0!=(t=(e.a||(e.a=new FQ(tgn,e,9,5)),e.a)).i?_K(Pp(etj(t,0),678)):null}function euk(e,t){var n;return(n=eft(e,t),Ei(WA(e,t),0)|xm(WA(e,n),0))?n:eft(eUY,WA(Fy(n,63),1))}function eux(e,t){var n;n=null!=epB((edk(),to3))&&null!=t.wg()?gP(LV(t.wg()))/gP(LV(epB(to3))):1,Um(e.b,t,n)}function euT(e,t){var n,r;return(n=Pp(e.d.Bc(t),14))?((r=e.e.hc()).Gc(n),e.e.d-=n.gc(),n.$b(),r):null}function euM(e,t){var n,r;if(0!=(r=e.c[t]))for(e.c[t]=0,e.d-=r,n=t+1;n0)return FP(t-1,e.a.c.length),ZV(e.a,t-1);throw p7(new bL)}function euA(e,t,n){if(t<0)throw p7(new gE(eq1+t));tt)throw p7(new gL(e$x+e+e$T+t));if(e<0||t>n)throw p7(new va(e$x+e+e$M+t+e$m+n))}function euC(e){if(!e.a||(8&e.a.i)==0)throw p7(new gC("Enumeration class expected for layout option "+e.f))}function euI(e){var t;++e.j,0==e.i?e.g=null:e.ieVq?e-n>eVq:n-e>eVq)}function euG(e,t){return!e||t&&!e.j||M4(e,124)&&0==Pp(e,124).a.b?0:e.Re()}function euW(e,t){return!e||t&&!e.k||M4(e,124)&&0==Pp(e,124).a.a?0:e.Se()}function euK(e){return(eLQ(),e<0)?-1!=e?new ep4(-1,-e):e03:e<=10?e05[zy(e)]:new ep4(1,e)}function euV(e){throw eoW(),p7(new gs("Unexpected typeof result '"+e+"'; please report this bug to the GWT team"))}function euq(e){g0(),MV(this),HD(this),this.e=e,eA9(this,e),this.g=null==e?eUg:efF(e),this.a="",this.b=e,this.a=""}function euZ(){this.a=new a4,this.f=new hW(this),this.b=new hK(this),this.i=new hV(this),this.e=new hq(this)}function euX(){m6.call(this,new Ju(ee0(16))),enG(2,eUN),this.b=2,this.a=new Uc(null,null,0,null),bp(this.a,this.a)}function euJ(){euJ=A,tsn=new SS("DUMMY_NODE_OVER",0),tsr=new SS("DUMMY_NODE_UNDER",1),tsi=new SS("EQUAL",2)}function euQ(){euQ=A,e8u=zD(eow(vx(e55,1),eU4,103,0,[(ec3(),tpm),tpg])),e8c=zD(eow(vx(e55,1),eU4,103,0,[tpy,tpb]))}function eu1(e){return(eYu(),tbC).Hc(e.j)?gP(LV(e_k(e,(eBU(),tnM)))):esp(eow(vx(e50,1),eUP,8,0,[e.i.n,e.n,e.a])).b}function eu0(e){var t,n,r,i;for(n=(r=e.b.a).a.ec().Kc();n.Ob();)t=Pp(n.Pb(),561),i=new eMq(t,e.e,e.f),P_(e.g,i)}function eu2(e,t){var n,r,i;r=e.nk(t,null),i=null,t&&(i=(yO(),n=new p5),etV(i,e.r)),(r=ew3(e,i,r))&&r.Fi()}function eu3(e,t){var n,r;for(r=0!=eMU(e.d,1),n=!0;n;)n=!1,n=t.c.Tf(t.e,r),n|=eAb(e,t,r,!1),r=!r;er0(e)}function eu4(e,t){var n,r,i;return r=!1,n=t.q.d,t.di&&(eyC(t.q,i),r=n!=t.q.d)),r}function eu5(e,t){var n,r,i,a,o,s,u,c;return u=t.i,c=t.j,i=(r=e.f).i,a=r.j,o=u-i,s=c-a,n=eB4.Math.sqrt(o*o+s*s)}function eu6(e,t){var n,r;return(r=ehO(e))||(tmT||(tmT=new sh),n=(eRe(),eSR(t)),JL((r=new pq(n)).Vk(),e)),r}function eu9(e,t){var n,r;return(n=Pp(e.c.Bc(t),14))?((r=e.hc()).Gc(n),e.d-=n.gc(),n.$b(),e.mc(r)):e.jc()}function eu8(e,t){var n;for(n=0;n=e.c.b:e.a<=e.c.b))throw p7(new bC);return t=e.a,e.a+=e.c.c,++e.b,ell(t)}function eci(e){var t;return t=new ete(e),Kv(e.a,e8w,new g$(eow(vx(e4M,1),eUp,369,0,[t]))),t.d&&P_(t.f,t.d),t.f}function eca(e){var t;return eaW(t=new MA(e.a),e),eo3(t,(eBU(),tnc),e),t.o.a=e.g,t.o.b=e.f,t.n.a=e.i,t.n.b=e.j,t}function eco(e,t,n,r){var i,a;for(a=e.Kc();a.Ob();)(i=Pp(a.Pb(),70)).n.a=t.a+(r.a-i.o.a)/2,i.n.b=t.b,t.b+=i.o.b+n}function ecs(e,t,n){var r,i;for(i=t.a.a.ec().Kc();i.Ob();)if($o(e,r=Pp(i.Pb(),57),n))return!0;return!1}function ecu(e){var t,n;for(n=new fz(e.r);n.a=0?t:-t;r>0;)r%2==0?(n*=n,r=r/2|0):(i*=n,r-=1);return t<0?1/i:i}function ecw(e,t){var n,r,i;for(i=1,n=e,r=t>=0?t:-t;r>0;)r%2==0?(n*=n,r=r/2|0):(i*=n,r-=1);return t<0?1/i:i}function ec_(e){var t,n,r,i;if(null!=e){for(n=0;n0&&esJ(n=Pp(RJ(e.a,e.a.c.length-1),570),t))&&P_(e.a,new Zn(t))}function ecP(e){var t,n;Dj(),t=e.d.c-e.e.c,ety((n=Pp(e.g,145)).b,new d7(t)),ety(n.c,new he(t)),qX(n.i,new ht(t))}function ecR(e){var t;return t=new vc,t.a+="VerticalSegment ",xT(t,e.e),t.a+=" ",xM(t,OU(new ve,new fz(e.k))),t.a}function ecj(e){var t;return(t=Pp(eef(e.c.c,""),229))||(t=new GM(v3(v2(new of,""),"Other")),epy(e.c.c,"",t)),t}function ecF(e){var t;return(64&e.Db)!=0?eMT(e):(t=new O1(eMT(e)),t.a+=" (name: ",xk(t,e.zb),t.a+=")",t.a)}function ecY(e,t,n){var r,i;return i=e.sb,e.sb=t,(4&e.Db)!=0&&(1&e.Db)==0&&(r=new FX(e,1,4,i,t),n?n.Ei(r):n=r),n}function ecB(e,t){var n,r,i;for(n=0,i=efr(e,t).Kc();i.Ob();)n+=null!=e_k(r=Pp(i.Pb(),11),(eBU(),tng))?1:0;return n}function ecU(e,t,n){var r,i,a;for(r=0,a=epL(e,0);a.b!=a.d.c&&!((i=gP(LV(Vv(a))))>n);)i>=t&&++r;return r}function ecH(e,t,n){var r,i;return r=new Q$(e.e,3,13,null,(i=t.c)||(eBK(),tgA),ebv(e,t),!1),n?n.Ei(r):n=r,n}function ec$(e,t,n){var r,i;return r=new Q$(e.e,4,13,(i=t.c)||(eBK(),tgA),null,ebv(e,t),!1),n?n.Ei(r):n=r,n}function ecz(e,t,n){var r,i;return i=e.r,e.r=t,(4&e.Db)!=0&&(1&e.Db)==0&&(r=new FX(e,1,8,i,e.r),n?n.Ei(r):n=r),n}function ecG(e,t){var n,r;return(r=(n=Pp(t,676)).vk())||n.wk(r=M4(t,88)?new k9(e,Pp(t,26)):new Ke(e,Pp(t,148))),r}function ecW(e,t,n){var r;e.qi(e.i+1),r=e.oi(t,n),t!=e.i&&ePD(e.g,t,e.g,t+1,e.i-t),Bc(e.g,t,r),++e.i,e.bi(t,n),e.ci()}function ecK(e,t){var n;return t.a&&(n=t.a.a.length,e.a?xM(e.a,e.b):e.a=new O0(e.d),Ka(e.a,t.a,t.d.length,n)),e}function ecV(e,t){var n,r,i,a;if(t.vi(e.a),null!=(a=Pp(eaS(e.a,8),1936)))for(r=0,i=(n=a).length;rn)throw p7(new gE(e$x+e+e$M+t+", size: "+n));if(e>t)throw p7(new gL(e$x+e+e$T+t))}function ec6(e,t,n){if(t<0)ekN(e,n);else{if(!n.Ij())throw p7(new gL(eZV+n.ne()+eZq));Pp(n,66).Nj().Vj(e,e.yh(),t)}}function ec9(e,t,n,r,i,a,o,s){var u;for(u=n;a=r||t=s.ue(e[t],e[u])?Bc(i,a++,e[t++]):Bc(i,a++,e[u++])}function ec8(e,t,n,r,i,a){this.e=new p0,this.f=(enY(),tsP),P_(this.e,e),this.d=t,this.a=n,this.b=r,this.f=i,this.c=a}function ec7(e,t){var n,r;for(r=new Ow(e);r.e!=r.i.gc();)if(n=Pp(epH(r),26),xc(t)===xc(n))return!0;return!1}function ele(e){var t,n,r,i;for(eBW(),n=epE(),r=0,i=n.length;r=65&&e<=70?e-65+10:e>=97&&e<=102?e-97+10:e>=48&&e<=57?e-48:0}function eln(e){var t;return(64&e.Db)!=0?eMT(e):(t=new O1(eMT(e)),t.a+=" (source: ",xk(t,e.d),t.a+=")",t.a)}function elr(e,t,n){var r,i;return i=e.a,e.a=t,(4&e.Db)!=0&&(1&e.Db)==0&&(r=new FX(e,1,5,i,e.a),n?ey7(n,r):n=r),n}function eli(e,t){var n;n=(256&e.Bb)!=0,t?e.Bb|=256:e.Bb&=-257,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,2,n,t))}function ela(e,t){var n;n=(256&e.Bb)!=0,t?e.Bb|=256:e.Bb&=-257,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,8,n,t))}function elo(e,t){var n;n=(256&e.Bb)!=0,t?e.Bb|=256:e.Bb&=-257,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,8,n,t))}function els(e,t){var n;n=(512&e.Bb)!=0,t?e.Bb|=512:e.Bb&=-513,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,3,n,t))}function elu(e,t){var n;n=(512&e.Bb)!=0,t?e.Bb|=512:e.Bb&=-513,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,9,n,t))}function elc(e,t){var n;return -1==e.b&&e.a&&(n=e.a.Gj(),e.b=n?e.c.Xg(e.a.aj(),n):edv(e.c.Tg(),e.a)),e.c.Og(e.b,t)}function ell(e){var t,n;return e>-129&&e<128?(t=e+128,(n=(Rv(),e0B)[t])||(n=e0B[t]=new fC(e)),n):new fC(e)}function elf(e){var t,n;return e>-129&&e<128?(t=e+128,(n=(RU(),e0K)[t])||(n=e0K[t]=new fD(e)),n):new fD(e)}function eld(e){var t,n;return(t=e.k)==(eEn(),e8C)&&((n=Pp(e_k(e,(eBU(),tt1)),61))==(eYu(),tbw)||n==tbj)}function elh(e,t,n){var r,i,a;return(a=i=eMC(e.b,t))&&(r=Pp(eP9(Qq(e,a),""),26))?eMv(e,r,t,n):null}function elp(e,t,n){var r,i,a;return(a=i=eMC(e.b,t))&&(r=Pp(eP9(Qq(e,a),""),26))?eMy(e,r,t,n):null}function elb(e,t){var n,r;for(r=new Ow(e);r.e!=r.i.gc();)if(n=Pp(epH(r),138),xc(t)===xc(n))return!0;return!1}function elm(e,t,n){var r;if(t>(r=e.gc()))throw p7(new Ii(t,r));if(e.hi()&&e.Hc(n))throw p7(new gL(eXB));e.Xh(t,n)}function elg(e,t){var n;if(null==(n=etJ(e.i,t)))throw p7(new gK("Node did not exist in input."));return eiX(t,n),null}function elv(e,t){var n;if(n=eAh(e,t),M4(n,322))return Pp(n,34);throw p7(new gL(eZV+t+"' is not a valid attribute"))}function ely(e,t,n){var r,i;for(r=0,i=M4(t,99)&&(Pp(t,18).Bb&eH3)!=0?new x1(t,e):new eaN(t,e);rt?1:e==t?0==e?elN(1/e,1/t):0:isNaN(e)?isNaN(t)?0:1:-1}function elP(e,t){ewG(t,"Sort end labels",1),_r(UJ(eeh(new R1(null,new Gq(e.b,16)),new t2),new t3),new t4),eEj(t)}function elR(e,t,n){var r,i;return e.ej()?(i=e.fj(),r=exm(e,t,n),e.$i(e.Zi(7,ell(n),r,t,i)),r):exm(e,t,n)}function elj(e,t){var n,r,i;null==e.d?(++e.e,--e.f):(i=t.cd(),r=((n=t.Sh())&eUu)%e.d.length,Xc(e,r,eML(e,r,n,i)))}function elF(e,t){var n;n=(e.Bb&eXt)!=0,t?e.Bb|=eXt:e.Bb&=-1025,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,10,n,t))}function elY(e,t){var n;n=(e.Bb&eH0)!=0,t?e.Bb|=eH0:e.Bb&=-4097,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,12,n,t))}function elB(e,t){var n;n=(e.Bb&eJV)!=0,t?e.Bb|=eJV:e.Bb&=-8193,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,15,n,t))}function elU(e,t){var n;n=(e.Bb&eJq)!=0,t?e.Bb|=eJq:e.Bb&=-2049,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new ZB(e,1,11,n,t))}function elH(e,t){var n;return 0!=(n=elN(e.b.c,t.b.c))||0!=(n=elN(e.a.a,t.a.a))?n:elN(e.a.b,t.a.b)}function el$(e,t){var n;if(null==(n=Bp(e.k,t)))throw p7(new gK("Port did not exist in input."));return eiX(t,n),null}function elz(e){var t,n;for(n=eM$(etP(e)).Kc();n.Ob();)if(eDM(e,t=Lq(n.Pb())))return qb((_X(),tgh),t);return null}function elG(e,t){var n,r,i,a,o;for(i=0,o=eAY(e.e.Tg(),t),a=0,n=Pp(e.g,119);i>10)+eH4&eHd,t[1]=(1023&e)+56320&eHd,ehv(t,0,t.length)}function el0(e){var t,n;return(n=Pp(e_k(e,(eBy(),tal)),103))==(ec3(),tpv)?(t=gP(LV(e_k(e,tiX))))>=1?tpg:tpb:n}function el2(e){switch(Pp(e_k(e,(eBy(),tag)),218).g){case 1:return new ig;case 3:return new iE;default:return new im}}function el3(e){if(e.c)el3(e.c);else if(e.d)throw p7(new gC("Stream already terminated, can't be modified or used"))}function el4(e){var t;return(64&e.Db)!=0?eMT(e):(t=new O1(eMT(e)),t.a+=" (identifier: ",xk(t,e.k),t.a+=")",t.a)}function el5(e,t,n){var r,i;return r=(yT(),i=new oJ),ent(r,t),enn(r,n),e&&JL((e.a||(e.a=new O_(e6h,e,5)),e.a),r),r}function el6(e,t,n,r){var i,a;return BJ(r),BJ(n),null==(a=null==(i=e.xc(t))?n:_i(Pp(i,15),Pp(n,14)))?e.Bc(t):e.zc(t,a),a}function el9(e){var t,n,r,i;return n=(t=Pp(yw((i=(r=e.gm).f)==e1G?r:i),9),new I1(t,Pp(CY(t,t.length),9),0)),erC(n,e),n}function el8(e,t,n){var r,i;for(i=e.a.ec().Kc();i.Ob();)if(r=Pp(i.Pb(),10),eot(n,Pp(RJ(t,r.p),14)))return r;return null}function el7(e,t,n){var r;try{esE(e,t,n)}catch(i){if(i=eoa(i),M4(i,597))throw r=i,p7(new Zt(r));throw p7(i)}return t}function efe(e,t){var n;return Ts(e)&&Ts(t)&&eHV<(n=e-t)&&n>1,e.k=n-1>>1}function efo(){var e,t,n;ewP(),n=e2w+++Date.now(),e=zy(eB4.Math.floor(n*e$h))&e$b,t=zy(n-e*e$p),this.a=1502^e,this.b=t^e$d}function efs(e){var t,n,r;for(t=new p0,r=new fz(e.j);r.a34028234663852886e22?eHQ:t<-34028234663852886e22?eH1:t}function efp(e){return e-=e>>1&1431655765,e=((e=(e>>2&858993459)+(858993459&e))>>4)+e&252645135,e+=e>>8,63&(e+=e>>16)}function efb(e){var t,n,r,i;for(t=new CS(e.Hd().gc()),i=0,r=JJ(e.Hd().Kc());r.Ob();)Gr(t,n=r.Pb(),ell(i++));return eEA(t.a)}function efm(e,t){var n,r,i;for(i=new p2,r=t.vc().Kc();r.Ob();)Um(i,(n=Pp(r.Pb(),42)).cd(),eab(e,Pp(n.dd(),15)));return i}function efg(e,t){0==e.n.c.length&&P_(e.n,new zO(e.s,e.t,e.i)),P_(e.b,t),eml(Pp(RJ(e.n,e.n.c.length-1),211),t),eNk(e,t)}function efv(e){return(e.c!=e.b.b||e.i!=e.g.b)&&(e.a.c=Je(e1R,eUp,1,0,5,1),eoc(e.a,e.b),eoc(e.a,e.g),e.c=e.b.b,e.i=e.g.b),e.a}function efy(e,t){var n,r,i;for(i=0,r=Pp(t.Kb(e),20).Kc();r.Ob();)gN(LK(e_k(n=Pp(r.Pb(),17),(eBU(),tnE))))||++i;return i}function efw(e,t){var n,r,i;i=gP(LV(ed$(r=KT(t),(eBy(),toO)))),ev_(t,n=eB4.Math.max(0,i/2-.5),1),P_(e,new E9(t,n))}function ef_(){ef_=A,tnj=new ST(eGR,0),tnD=new ST("FIRST",1),tnN=new ST(eWi,2),tnP=new ST("LAST",3),tnR=new ST(eWa,4)}function efE(){efE=A,tpO=new kb(ezo,0),tpT=new kb("POLYLINE",1),tpx=new kb("ORTHOGONAL",2),tpM=new kb("SPLINES",3)}function efS(){efS=A,tlP=new S6("ASPECT_RATIO_DRIVEN",0),tlR=new S6("MAX_SCALE_DRIVEN",1),tlN=new S6("AREA_DRIVEN",2)}function efk(){efk=A,tff=new S8("P1_STRUCTURE",0),tfd=new S8("P2_PROCESSING_ORDER",1),tfh=new S8("P3_EXECUTION",2)}function efx(){efx=A,tc1=new S0("OVERLAP_REMOVAL",0),tcJ=new S0("COMPACTION",1),tcQ=new S0("GRAPH_SIZE_CALCULATION",2)}function efT(e,t){return Mc(),enj(eHe),eB4.Math.abs(e-t)<=eHe||e==t||isNaN(e)&&isNaN(t)?0:et?1:Te(isNaN(e),isNaN(t))}function efM(e,t){var n,r;for(n=epL(e,0);n.b!=n.d.c;){if((r=gR(LV(Vv(n))))==t)return;if(r>t){Ks(n);break}}YU(n,t)}function efO(e,t){var n,r,i,a,o;if(n=t.f,epy(e.c.d,n,t),null!=t.g)for(i=t.g,a=0,o=i.length;at&&r.ue(e[a-1],e[a])>0;--a)o=e[a],Bc(e,a,e[a-1]),Bc(e,a-1,o)}function efL(e,t,n,r){if(t<0)eOh(e,n,r);else{if(!n.Ij())throw p7(new gL(eZV+n.ne()+eZq));Pp(n,66).Nj().Tj(e,e.yh(),t,r)}}function efC(e,t){if(t==e.d)return e.e;if(t==e.e)return e.d;throw p7(new gL("Node "+t+" not part of edge "+e))}function efI(e,t){switch(t.g){case 2:return e.b;case 1:return e.c;case 4:return e.d;case 3:return e.a;default:return!1}}function efD(e,t){switch(t.g){case 2:return e.b;case 1:return e.c;case 4:return e.d;case 3:return e.a;default:return!1}}function efN(e,t,n,r){switch(t){case 3:return e.f;case 4:return e.g;case 5:return e.i;case 6:return e.j}return ec2(e,t,n,r)}function efP(e){return e.k==(eEn(),e8N)&&q3(new R1(null,new YI(new Fa(OH(efc(e).a.Kc(),new c)))),new it)}function efR(e){return null==e.e?e:(e.c||(e.c=new eCg((256&e.f)!=0,e.i,e.a,e.d,(16&e.f)!=0,e.j,e.g,null)),e.c)}function efj(e,t){return e.h==eHz&&0==e.m&&0==e.l?(t&&(e0A=Mk(0,0,0)),Tr((Q2(),e0I))):(t&&(e0A=Mk(e.l,e.m,e.h)),Mk(0,0,0))}function efF(e){var t;return Array.isArray(e)&&e.im===O?yx(esF(e))+"@"+(t=esj(e)>>>0).toString(16):e.toString()}function efY(e){var t;this.a=(t=Pp(e.e&&e.e(),9),new I1(t,Pp(CY(t,t.length),9),0)),this.b=Je(e1R,eUp,1,this.a.a.length,5,1)}function efB(e){var t,n,r;for(this.a=new Tw,r=new fz(e);r.a0&&(GV(t-1,e.length),58==e.charCodeAt(t-1))&&!efz(e,tm1,tm0)}function efz(e,t,n){var r,i;for(r=0,i=e.length;r=i)return t.c+n;return t.c+t.b.gc()}function efK(e,t){var n,r,i,a;for(LF(),r=J9(e),i=t,Qe(r,0,r.length,i),n=0;n0&&(r+=i,++n);return n>1&&(r+=e.d*(n-1)),r}function efq(e){var t,n,r;for(r=new vs,r.a+="[",t=0,n=e.gc();t0&&this.b>0&&ji(this.c,this.b,this.a)}function ef4(e){edk(),this.c=ZW(eow(vx(e5Z,1),eUp,831,0,[to2])),this.b=new p2,this.a=e,Um(this.b,to3,1),ety(to4,new h4(this))}function ef5(e,t){var n;return e.d?F9(e.b,t)?Pp(Bp(e.b,t),51):(n=t.Kf(),Um(e.b,t,n),n):t.Kf()}function ef6(e,t){var n;return xc(e)===xc(t)||!!M4(t,91)&&(n=Pp(t,91),e.e==n.e&&e.d==n.d&&qv(e,n.a))}function ef9(e){switch(eYu(),e.g){case 4:return tbw;case 1:return tby;case 3:return tbj;case 2:return tbY;default:return tbF}}function ef8(e,t){switch(t){case 3:return 0!=e.f;case 4:return 0!=e.g;case 5:return 0!=e.i;case 6:return 0!=e.j}return eaT(e,t)}function ef7(e){switch(e.g){case 0:return new aV;case 1:return new aq;default:throw p7(new gL(eqa+(null!=e.f?e.f:""+e.g)))}}function ede(e){switch(e.g){case 0:return new aK;case 1:return new aZ;default:throw p7(new gL(eWt+(null!=e.f?e.f:""+e.g)))}}function edt(e){switch(e.g){case 0:return new mZ;case 1:return new m_;default:throw p7(new gL(eqN+(null!=e.f?e.f:""+e.g)))}}function edn(e){switch(e.g){case 1:return new aU;case 2:return new LY;default:throw p7(new gL(eqa+(null!=e.f?e.f:""+e.g)))}}function edr(e){var t,n;if(e.b)return e.b;for(n=e2M?null:e.d;n;){if(t=e2M?null:n.b)return t;n=e2M?null:n.d}return _g(),e2F}function edi(e){var t,n,r;return 0==e.e?0:(t=e.d<<5,n=e.a[e.d-1],e.e<0&&(r=eiU(e))==e.d-1&&(--n,n|=0),t-=exv(n))}function eda(e){var t,n,r;return e>5,t=31&e,(r=Je(ty_,eHT,25,n+1,15,1))[n]=1<3;)i*=10,--a;e=(e+(i>>1))/i|0}return r.i=e,!0}function edl(e){return euQ(),OQ(),!!(efD(Pp(e.a,81).j,Pp(e.b,103))||0!=Pp(e.a,81).d.e&&efD(Pp(e.a,81).j,Pp(e.b,103)))}function edf(e){J1(),Pp(e.We((eBB(),thL)),174).Hc((eI3(),tb4))&&(Pp(e.We(thJ),174).Fc((ekU(),tbg)),Pp(e.We(thL),174).Mc(tb4))}function edd(e,t){var n,r;if(!t)return!1;for(n=0;n=0;--r)for(i=0,t=n[r];i>1,this.k=t-1>>1}function edC(e,t){ewG(t,"End label post-processing",1),_r(UJ(eeh(new R1(null,new Gq(e.b,16)),new tV),new tq),new tZ),eEj(t)}function edI(e,t,n){var r,i;return r=gP(e.p[t.i.p])+gP(e.d[t.i.p])+t.n.b+t.a.b,(i=gP(e.p[n.i.p])+gP(e.d[n.i.p])+n.n.b+n.a.b)-r}function edD(e,t,n){var r,i;for(i=0,r=WM(n,eH8);0!=ecd(r,0)&&i0&&(GV(0,t.length),43==t.charCodeAt(0))?t.substr(1):t)}function edR(e){var t;return null==e?null:new TU((t=ePh(e,!0)).length>0&&(GV(0,t.length),43==t.charCodeAt(0))?t.substr(1):t)}function edj(e,t){var n;return e.i>0&&(t.lengthe.i&&Bc(t,e.i,null),t}function edF(e,t,n){var r,i,a;return e.ej()?(r=e.i,a=e.fj(),ecW(e,r,t),i=e.Zi(3,null,t,r,a),n?n.Ei(i):n=i):ecW(e,e.i,t),n}function edY(e,t,n){var r,i;return r=new Q$(e.e,4,10,M4(i=t.c,88)?Pp(i,26):(eBK(),tgI),null,ebv(e,t),!1),n?n.Ei(r):n=r,n}function edB(e,t,n){var r,i;return r=new Q$(e.e,3,10,null,M4(i=t.c,88)?Pp(i,26):(eBK(),tgI),ebv(e,t),!1),n?n.Ei(r):n=r,n}function edU(e){var t;return Cn(),t=new TS(Pp(e.e.We((eBB(),thO)),8)),e.B.Hc((eI3(),tbQ))&&(t.a<=0&&(t.a=20),t.b<=0&&(t.b=20)),t}function edH(e){var t;return ebk(),t=(e.q?e.q:(Hj(),Hj(),e2i))._b((eBy(),ta0))?Pp(e_k(e,ta0),197):Pp(e_k(Bq(e),ta2),197)}function ed$(e,t){var n,r;return r=null,Ln(e,(eBy(),toD))&&(n=Pp(e_k(e,toD),94)).Xe(t)&&(r=n.We(t)),null==r&&(r=e_k(Bq(e),t)),r}function edz(e,t){var n,r,i;return!!M4(t,42)&&(r=(n=Pp(t,42)).cd(),i=ecA(e.Rc(),r),BG(i,n.dd())&&(null!=i||e.Rc()._b(r)))}function edG(e,t){var n,r,i;return e.f>0&&(e.qj(),i=((r=null==t?0:esj(t))&eUu)%e.d.length,-1!=(n=eML(e,i,r,t)))}function edW(e,t){var n,r,i;return e.f>0&&(e.qj(),i=((r=null==t?0:esj(t))&eUu)%e.d.length,n=exx(e,i,r,t))?n.dd():null}function edK(e,t){var n,r,i,a;for(i=0,a=eAY(e.e.Tg(),t),n=Pp(e.g,119);i1?WO(Fg(t.a[1],32),WM(t.a[0],eH8)):WM(t.a[0],eH8),Kj(efn(t.e,n))))}function edQ(e,t){var n;return Ts(e)&&Ts(t)&&eHV<(n=e%t)&&n>5,t&=31,r=Je(ty_,eHT,25,i=e.d+n+(0==t?0:1),15,1),ewZ(r,e.a,n,t),a=new F7(e.e,i,r),Ku(a),a}function eht(e,t,n){var r,i;r=Pp(zg(tv4,t),117),i=Pp(zg(tv5,t),117),n?(Ge(tv4,e,r),Ge(tv5,e,i)):(Ge(tv5,e,r),Ge(tv4,e,i))}function ehn(e,t,n){var r,i,a;for(i=null,a=e.b;a;){if(r=e.a.ue(t,a.d),n&&0==r)return a;r>=0?a=a.a[1]:(i=a,a=a.a[0])}return i}function ehr(e,t,n){var r,i,a;for(i=null,a=e.b;a;){if(r=e.a.ue(t,a.d),n&&0==r)return a;r<=0?a=a.a[0]:(i=a,a=a.a[1])}return i}function ehi(e,t,n,r){var i,a,o;return i=!1,ejB(e.f,n,r)&&(epn(e.f,e.a[t][n],e.a[t][r]),o=(a=e.a[t])[r],a[r]=a[n],a[n]=o,i=!0),i}function eha(e,t,n,r,i){var a,o,s;for(o=i;t.b!=t.c;)a=Pp(Yn(t),10),s=Pp(efr(a,r).Xb(0),11),e.d[s.p]=o++,n.c[n.c.length]=s;return o}function eho(e,t,n){var r,i,a,o,s;return o=e.k,s=t.k,i=LV(ed$(e,r=n[o.g][s.g])),a=LV(ed$(t,r)),eB4.Math.max((BJ(i),i),(BJ(a),a))}function ehs(e,t,n){var r,i,a,o;for(r=n/e.c.length,i=0,o=new fz(e);o.a2e3&&(e1X=e,e1J=eB4.setTimeout(wf,10)),0==e1Z++&&(eeA((g1(),e0_)),!0)}function ehf(e,t){var n,r,i;for(r=new Fa(OH(efc(e).a.Kc(),new c));eTk(r);)if((i=(n=Pp(ZC(r),17)).d.i).c==t)return!1;return!0}function ehd(e,t){var n,r;if(M4(t,245)){r=Pp(t,245);try{return n=e.vd(r),0==n}catch(i){if(i=eoa(i),!M4(i,205))throw p7(i)}}return!1}function ehh(){return Error.stackTraceLimit>0?(eB4.Error.stackTraceLimit=Error.stackTraceLimit=64,!0):"stack"in Error()}function ehp(e,t){return Mc(),Mc(),enj(eHe),(eB4.Math.abs(e-t)<=eHe||e==t||isNaN(e)&&isNaN(t)?0:et?1:Te(isNaN(e),isNaN(t)))>0}function ehb(e,t){return Mc(),Mc(),enj(eHe),(eB4.Math.abs(e-t)<=eHe||e==t||isNaN(e)&&isNaN(t)?0:et?1:Te(isNaN(e),isNaN(t)))<0}function ehm(e,t){return Mc(),Mc(),enj(eHe),(eB4.Math.abs(e-t)<=eHe||e==t||isNaN(e)&&isNaN(t)?0:et?1:Te(isNaN(e),isNaN(t)))<=0}function ehg(e,t){for(var n=0;!t[n]||""==t[n];)n++;for(var r=t[n++];neH6)return n.fh();if((r=n.Zg())||n==e)break}return r}function ehA(e){return(z0(),M4(e,156))?Pp(Bp(tmR,e0r),288).vg(e):F9(tmR,esF(e))?Pp(Bp(tmR,esF(e)),288).vg(e):null}function ehL(e){if(ehZ(eq6,e))return OQ(),e0P;if(ehZ(eq9,e))return OQ(),e0N;throw p7(new gL("Expecting true or false"))}function ehC(e,t){if(t.c==e)return t.d;if(t.d==e)return t.c;throw p7(new gL("Input edge is not connected to the input port."))}function ehI(e,t){return e.e>t.e?1:e.et.d?e.e:e.d=48&&e<48+eB4.Math.min(10,10)?e-48:e>=97&&e<97?e-97+10:e>=65&&e<65?e-65+10:-1}function ehN(e,t){var n;return xc(t)===xc(e)||!!M4(t,21)&&(n=Pp(t,21)).gc()==e.gc()&&e.Ic(n)}function ehP(e,t){var n,r,i,a;return(r=e.a.length-1,n=t-e.b&r,a=e.c-t&r,A2(n<(i=e.c-e.b&r)),n>=a)?(euD(e,t),-1):(euN(e,t),1)}function ehR(e,t){var n,r;for(n=(GV(t,e.length),e.charCodeAt(t)),r=t+1;rt.e?1:e.ft.f?1:esj(e)-esj(t)}function ehZ(e,t){return BJ(e),null!=t&&(!!IE(e,t)||e.length==t.length&&IE(e.toLowerCase(),t.toLowerCase()))}function ehX(e,t){var n,r,i,a;for(r=0,i=t.gc();r0&&0>ecd(e,128)?(t=jE(e)+128,(n=(RB(),e0H)[t])||(n=e0H[t]=new fI(e)),n):new fI(e)}function eh1(e,t){var n,r;return(n=t.Hh(e.a))&&null!=(r=Lq(edW((n.b||(n.b=new L_((eBK(),tgF),tgf,n)),n.b),eXP)))?r:t.ne()}function eh0(e,t){var n,r;return(n=t.Hh(e.a))&&null!=(r=Lq(edW((n.b||(n.b=new L_((eBK(),tgF),tgf,n)),n.b),eXP)))?r:t.ne()}function eh2(e,t){var n,r;for(Gk(),r=new Fa(OH(efs(e).a.Kc(),new c));eTk(r);)if((n=Pp(ZC(r),17)).d.i==t||n.c.i==t)return n;return null}function eh3(e,t,n){this.c=e,this.f=new p0,this.e=new yb,this.j=new R$,this.n=new R$,this.b=t,this.g=new Hr(t.c,t.d,t.b,t.a),this.a=n}function eh4(e){var t,n,r,i;for(r=0,this.a=new Tw,this.d=new bV,this.e=0,i=(n=e).length;r0)}function ept(e){var t;xc(eT8(e,(eBB(),thl)))===xc((eck(),tpG))&&(z$(e)?(t=Pp(eT8(z$(e),thl),334),ebu(e,thl,t)):ebu(e,thl,tpW))}function epn(e,t,n){var r,i;e_m(e.e,t,n,(eYu(),tbY)),e_m(e.i,t,n,tby),e.a&&(i=Pp(e_k(t,(eBU(),tnc)),11),r=Pp(e_k(n,tnc),11),WW(e.g,i,r))}function epr(e,t,n){var r,i,a;r=t.c.p,a=t.p,e.b[r][a]=new $j(e,t),n&&(e.a[r][a]=new hv(t),(i=Pp(e_k(t,(eBU(),tt8)),10))&&exg(e.d,i,t))}function epi(e,t){var n,r,i;if(P_(e9n,e),t.Fc(e),n=Pp(Bp(e9t,e),21))for(i=n.Kc();i.Ob();)-1!=QI(e9n,r=Pp(i.Pb(),33),0)||epi(r,t)}function epa(e,t,n){var r;(e2x?(edr(e),0):e2T?(_g(),0):e2A?(_g(),0):!e2O||(_g(),1))||((r=new I6(t)).b=n,eEt(e,r))}function epo(e,t){var n;n=!e.A.Hc((ed6(),tbq))||e.q==(ewf(),tbo),e.u.Hc((ekU(),tbp))?n?eY_(e,t):eF3(e,t):e.u.Hc(tbm)&&(n?eFO(e,t):eYY(e,t))}function eps(e,t){var n,r;if(++e.j,null!=t&&exM(t,n=M4(r=e.a.Cb,97)?Pp(r,97).Jg():null)){ehU(e.a,4,n);return}ehU(e.a,4,Pp(t,126))}function epu(e,t,n){return new Hr(eB4.Math.min(e.a,t.a)-n/2,eB4.Math.min(e.b,t.b)-n/2,eB4.Math.abs(e.a-t.a)+n,eB4.Math.abs(e.b-t.b)+n)}function epc(e,t){var n,r;return 0!=(n=ME(e.a.c.p,t.a.c.p))?n:0!=(r=ME(e.a.d.i.p,t.a.d.i.p))?r:ME(t.a.d.p,e.a.d.p)}function epl(e,t,n){var r,i,a,o;return(a=t.j)!=(o=n.j)?a.g-o.g:(r=e.f[t.p],i=e.f[n.p],0==r&&0==i?0:0==r?-1:0==i?1:elN(r,i))}function epf(e,t,n){var r,i,a;if(!n[t.d])for(n[t.d]=!0,i=new fz(efv(t));i.a=(i=e.length))return i;for(t=t>0?t:0;tr&&Bc(t,r,null),t}function epv(e,t){var n,r;for(r=e.a.length,t.lengthr&&Bc(t,r,null),t}function epy(e,t,n){var r,i,a;return(i=Pp(Bp(e.e,t),387))?(a=CL(i,n),M6(e,i),a):(r=new PM(e,t,n),Um(e.e,t,r),zd(r),null)}function epw(e){var t;if(null==e)return null;if(null==(t=eMI(ePh(e,!0))))throw p7(new gV("Invalid hexBinary value: '"+e+"'"));return t}function ep_(e){return(eLQ(),0>ecd(e,0))?0!=ecd(e,-1)?new ey$(-1,QC(e)):e03:0>=ecd(e,10)?e05[jE(e)]:new ey$(1,e)}function epE(){return eBW(),eow(vx(e3n,1),eU4,159,0,[e4e,e37,e4t,e30,e31,e32,e35,e34,e33,e38,e39,e36,e3J,e3X,e3Q,e3q,e3V,e3Z,e3W,e3G,e3K,e4n])}function epS(e){var t;this.d=new p0,this.j=new yb,this.g=new yb,t=e.g.b,this.f=Pp(e_k(Bq(t),(eBy(),tal)),103),this.e=gP(LV(epj(t,toN)))}function epk(e){this.b=new p0,this.e=new p0,this.d=e,this.a=!yK(UJ(new R1(null,new YI(new Z4(e.b))),new f2(new ir))).sd((_w(),e2z))}function epx(){epx=A,tdh=new ko("PARENTS",0),tdd=new ko("NODES",1),tdl=new ko("EDGES",2),tdp=new ko("PORTS",3),tdf=new ko("LABELS",4)}function epT(){epT=A,tbt=new kw("DISTRIBUTED",0),tbr=new kw("JUSTIFIED",1),tp7=new kw("BEGIN",2),tbe=new kw(e$8,3),tbn=new kw("END",4)}function epM(e){var t;switch(t=e.yi(null)){case 10:return 0;case 15:return 1;case 14:return 2;case 11:return 3;case 21:return 4}return -1}function epO(e){switch(e.g){case 1:return ec3(),tpy;case 4:return ec3(),tpm;case 2:return ec3(),tpg;case 3:return ec3(),tpb}return ec3(),tpv}function epA(e,t,n){var r;switch((r=n.q.getFullYear()-eHx+eHx)<0&&(r=-r),t){case 1:e.a+=r;break;case 2:eeE(e,r%100,2);break;default:eeE(e,r,t)}}function epL(e,t){var n,r;if(Gp(t,e.b),t>=e.b>>1)for(r=e.c,n=e.b;n>t;--n)r=r.b;else for(n=0,r=e.a.a;n=64&&t<128&&(i=WO(i,Fg(1,t-64)));return i}function epj(e,t){var n,r;return r=null,Ln(e,(eBB(),tpa))&&(n=Pp(e_k(e,tpa),94)).Xe(t)&&(r=n.We(t)),null==r&&Bq(e)&&(r=e_k(Bq(e),t)),r}function epF(e,t){var n,r,i;(r=(i=t.d.i).k)!=(eEn(),e8N)&&r!=e8L&&(n=new Fa(OH(efc(i).a.Kc(),new c)),eTk(n)&&Um(e.k,t,Pp(ZC(n),17)))}function epY(e,t){var n,r,i;return r=ee2(e.Tg(),t),(n=t-e.Ah())<0?(i=e.Yg(r))>=0?e.lh(i):exu(e,r):n<0?exu(e,r):Pp(r,66).Nj().Sj(e,e.yh(),n)}function epB(e){var t;if(!M4(e.a,4))return e.a;if(null==(t=ehA(e.a)))throw p7(new gC(eq8+e.b+"'. "+eq4+(LW(e6D),e6D.k)+eq5));return t}function epU(e){var t;if(null==e)return null;if(null==(t=eYD(ePh(e,!0))))throw p7(new gV("Invalid base64Binary value: '"+e+"'"));return t}function epH(e){var t;try{return t=e.i.Xb(e.e),e.mj(),e.g=e.e++,t}catch(n){if(n=eoa(n),M4(n,73))throw e.mj(),p7(new bC);throw p7(n)}}function ep$(e){var t;try{return t=e.c.ki(e.e),e.mj(),e.g=e.e++,t}catch(n){if(n=eoa(n),M4(n,73))throw e.mj(),p7(new bC);throw p7(n)}}function epz(){epz=A,e67=(eBB(),tpt),e63=ths,e6J=td2,e64=thN,e69=(evw(),e3y),e66=e3g,e68=e3_,e65=e3m,e61=(eug(),e6V),e6Q=e6K,e60=e6Z,e62=e6X}function epG(e){switch(_M(),this.c=new p0,this.d=e,e.g){case 0:case 2:this.a=Ug(e8_),this.b=eHQ;break;case 3:case 1:this.a=e8_,this.b=eH1}}function epW(e,t,n){var r,i;if(e.c)eno(e.c,e.c.i+t),ens(e.c,e.c.j+n);else for(i=new fz(e.b);i.a0&&(P_(e.b,new PE(t.a,n)),0<(r=t.a.length)?t.a=t.a.substr(0,0):0>r&&(t.a+=M3(Je(tyw,eHl,25,-r,15,1))))}function epq(e,t){var n,r,i;for(n=e.o,i=Pp(Pp(Zq(e.r,t),21),84).Kc();i.Ob();)(r=Pp(i.Pb(),111)).e.a=ego(r,n.a),r.e.b=n.b*gP(LV(r.b.We(e4a)))}function epZ(e,t){var n,r,i,a;return i=e.k,n=gP(LV(e_k(e,(eBU(),tnv)))),a=t.k,r=gP(LV(e_k(t,tnv))),a!=(eEn(),e8C)?-1:i!=e8C?1:n==r?0:n=0?e.hh(t,n,r):(e.eh()&&(r=(i=e.Vg())>=0?e.Qg(r):e.eh().ih(e,-1-i,null,r)),e.Sg(t,n,r))}function ep2(e,t){switch(t){case 7:e.e||(e.e=new Ih(e6g,e,7,4)),eRT(e.e);return;case 8:e.d||(e.d=new Ih(e6g,e,8,5)),eRT(e.d);return}edS(e,t)}function ep3(e,t){var n;n=e.Zc(t);try{return n.Pb()}catch(r){if(r=eoa(r),M4(r,109))throw p7(new gE("Can't get element "+t));throw p7(r)}}function ep4(e,t){this.e=e,t=0&&(n.d=e.t);break;case 3:e.t>=0&&(n.a=e.t)}e.C&&(n.b=e.C.b,n.c=e.C.c)}function ep7(){ep7=A,e4d=new EN(ezb,0),e4f=new EN(ezm,1),e4h=new EN(ezg,2),e4p=new EN(ezv,3),e4d.a=!1,e4f.a=!0,e4h.a=!1,e4p.a=!0}function ebe(){ebe=A,e6U=new ED(ezb,0),e6B=new ED(ezm,1),e6H=new ED(ezg,2),e6$=new ED(ezv,3),e6U.a=!1,e6B.a=!0,e6H.a=!1,e6$.a=!0}function ebt(e){var t;t=e.a;do(t=Pp(ZC(new Fa(OH(efu(t).a.Kc(),new c))),17).c.i).k==(eEn(),e8D)&&e.b.Fc(t);while(t.k==(eEn(),e8D))e.b=eaa(e.b)}function ebn(e){var t,n,r;for(r=e.c.a,e.p=(Y9(r),new I4(r)),n=new fz(r);n.an.b))}function ebs(e,t){return xd(e)?!!e0c[t]:e.hm?!!e.hm[t]:xf(e)?!!e0u[t]:!!xl(e)&&!!e0s[t]}function ebu(e,t,n){return null==n?(e.o||(e.o=new JY((eBa(),tmy),e6O,e,0)),ehx(e.o,t)):(e.o||(e.o=new JY((eBa(),tmy),e6O,e,0)),evQ(e.o,t,n)),e}function ebc(e,t,n,r){var i,a;a=t.Xe((eBB(),thS))?Pp(t.We(thS),21):e.j,(i=ele(a))!=(eBW(),e4n)&&(!n||ehj(i))&&eEU(eMD(e,i,r),t)}function ebl(e,t,n,r){var i,a,o;return a=ee2(e.Tg(),t),(i=t-e.Ah())<0?(o=e.Yg(a))>=0?e._g(o,n,!0):exk(e,a,n):Pp(a,66).Nj().Pj(e,e.yh(),i,n,r)}function ebf(e,t,n,r){var i,a,o;n.mh(t)&&(_4(),eec(t)?ehX(e,i=Pp(n.ah(t),153)):(a=(o=t)?Pp(r,49).xh(o):null)&&p6(n.ah(t),a))}function ebd(e){switch(e.g){case 1:return eaY(),e4c;case 3:return eaY(),e4o;case 2:return eaY(),e4u;case 4:return eaY(),e4s;default:return null}}function ebh(e){switch(typeof e){case eUo:return ebA(e);case eUa:return zy(e);case eUi:return OQ(),e?1231:1237;default:return null==e?0:Ao(e)}}function ebp(e,t,n){if(e.e)switch(e.b){case 1:HJ(e.c,t,n);break;case 0:HQ(e.c,t,n)}else V6(e.c,t,n);e.a[t.p][n.p]=e.c.i,e.a[n.p][t.p]=e.c.e}function ebb(e){var t,n;if(null==e)return null;for(t=0,n=Je(e4N,eUP,193,e.length,0,2);t=0)return i;if(e.Fk()){for(r=0;r=(i=e.gc()))throw p7(new Ii(t,i));if(e.hi()&&(r=e.Xc(n))>=0&&r!=t)throw p7(new gL(eXB));return e.mi(t,n)}function ebw(e,t){if(this.a=Pp(Y9(e),245),this.b=Pp(Y9(t),245),e.vd(t)>0||e==(m3(),e0f)||t==(m2(),e0d))throw p7(new gL("Invalid range: "+VW(e,t)))}function eb_(e){var t,n;for(this.b=new p0,this.c=e,this.a=!1,n=new fz(e.a);n.a0),(t&-t)==t)return zy(t*eMU(e,31)*4656612873077393e-25);do r=(n=eMU(e,31))%t;while(n-r+(t-1)<0)return zy(r)}function ebA(e){var t,n,r;return(I9(),null!=(r=e2W[n=":"+e]))?zy((BJ(r),r)):(t=null==(r=e2G[n])?eAC(e):zy((BJ(r),r)),HB(),e2W[n]=t,t)}function ebL(e,t,n){ewG(n,"Compound graph preprocessor",1),e.a=new zu,eFC(e,t,null),eRs(e,t),eOz(e),eo3(t,(eBU(),ttW),e.a),e.a=null,Yy(e.b),eEj(n)}function ebC(e,t,n){switch(n.g){case 1:e.a=t.a/2,e.b=0;break;case 2:e.a=t.a,e.b=t.b/2;break;case 3:e.a=t.a/2,e.b=t.b;break;case 4:e.a=0,e.b=t.b/2}}function ebI(e){var t,n,r;for(r=Pp(Zq(e.a,(ey4(),tea)),15).Kc();r.Ob();)t=egD(n=Pp(r.Pb(),101)),Yz(e,n,t[0],(erX(),ted),0),Yz(e,n,t[1],tep,1)}function ebD(e){var t,n,r;for(r=Pp(Zq(e.a,(ey4(),teo)),15).Kc();r.Ob();)t=egD(n=Pp(r.Pb(),101)),Yz(e,n,t[0],(erX(),ted),0),Yz(e,n,t[1],tep,1)}function ebN(e){switch(e.g){case 0:return null;case 1:return new er1;case 2:return new mQ;default:throw p7(new gL(eqa+(null!=e.f?e.f:""+e.g)))}}function ebP(e,t,n){var r,i;for(eod(e,t-e.s,n-e.t),i=new fz(e.n);i.a1&&(a=ebE(e,t)),a}function ebj(e){var t;return e.f&&e.f.kh()&&(t=Pp(e.f,49),e.f=Pp(ecv(e,t),82),e.f!=t&&(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,9,8,t,e.f))),e.f}function ebF(e){var t;return e.i&&e.i.kh()&&(t=Pp(e.i,49),e.i=Pp(ecv(e,t),82),e.i!=t&&(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,9,7,t,e.i))),e.i}function ebY(e){var t;return e.b&&(64&e.b.Db)!=0&&(t=e.b,e.b=Pp(ecv(e,t),18),e.b!=t&&(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,9,21,t,e.b))),e.b}function ebB(e,t){var n,r,i;null==e.d?(++e.e,++e.f):(r=t.Sh(),eO1(e,e.f+1),i=(r&eUu)%e.d.length,(n=e.d[i])||(n=e.d[i]=e.uj()),n.Fc(t),++e.f)}function ebU(e,t,n){var r;return!t.Kj()&&(-2!=t.Zj()?null==(r=t.zj())?null==n:ecX(r,n):t.Hj()==e.e.Tg()&&null==n)}function ebH(){var e;enG(16,eU0),e=er_(16),this.b=Je(e1z,eU1,317,e,0,1),this.c=Je(e1z,eU1,317,e,0,1),this.a=null,this.e=null,this.i=0,this.f=e-1,this.g=0}function eb$(e){CW.call(this),this.k=(eEn(),e8N),this.j=(enG(6,eU3),new XM(6)),this.b=(enG(2,eU3),new XM(2)),this.d=new md,this.f=new mb,this.a=e}function ebz(e){var t,n;!(e.c.length<=1)&&(t=eLW(e,(eYu(),tbj)),eSe(e,Pp(t.a,19).a,Pp(t.b,19).a),n=eLW(e,tbY),eSe(e,Pp(n.a,19).a,Pp(n.b,19).a))}function ebG(){ebG=A,tsb=new Sx("SIMPLE",0),tsd=new Sx(eWg,1),tsh=new Sx("LINEAR_SEGMENTS",2),tsf=new Sx("BRANDES_KOEPF",3),tsp=new Sx(eVI,4)}function ebW(e,t,n){IR(Pp(e_k(t,(eBy(),tol)),98))||(Q3(e,t,eEC(t,n)),Q3(e,t,eEC(t,(eYu(),tbj))),Q3(e,t,eEC(t,tbw)),Hj(),Mv(t.j,new hm(e)))}function ebK(e,t,n,r){var i,a,o;for(o=(i=r?Pp(Zq(e.a,t),21):Pp(Zq(e.b,t),21)).Kc();o.Ob();)if(eL8(e,n,a=Pp(o.Pb(),33)))return!0;return!1}function ebV(e){var t,n;for(n=new Ow(e);n.e!=n.i.gc();)if((t=Pp(epH(n),87)).e||0!=(t.d||(t.d=new O_(tgr,t,1)),t.d).i)return!0;return!1}function ebq(e){var t,n;for(n=new Ow(e);n.e!=n.i.gc();)if((t=Pp(epH(n),87)).e||0!=(t.d||(t.d=new O_(tgr,t,1)),t.d).i)return!0;return!1}function ebZ(e){var t,n,r;for(t=0,r=new fz(e.c.a);r.a102?-1:e<=57?e-48:e<65?-1:e<=70?e-65+10:e<97?-1:e-97+10}function eb2(e,t){if(null==e)throw p7(new gD("null key in entry: null="+t));if(null==t)throw p7(new gD("null value in entry: "+e+"=null"))}function eb3(e,t){for(var n,r;e.Ob();)if(!t.Ob()||(n=e.Pb(),r=t.Pb(),!(xc(n)===xc(r)||null!=n&&ecX(n,r))))return!1;return!t.Ob()}function eb4(e,t){var n;return n=eow(vx(tyx,1),eH5,25,15,[euG(e.a[0],t),euG(e.a[1],t),euG(e.a[2],t)]),e.d&&(n[0]=eB4.Math.max(n[0],n[2]),n[2]=n[0]),n}function eb5(e,t){var n;return n=eow(vx(tyx,1),eH5,25,15,[euW(e.a[0],t),euW(e.a[1],t),euW(e.a[2],t)]),e.d&&(n[0]=eB4.Math.max(n[0],n[2]),n[2]=n[0]),n}function eb6(){eb6=A,teG=new Sf("GREEDY",0),tez=new Sf(eWv,1),teK=new Sf(eWg,2),teV=new Sf("MODEL_ORDER",3),teW=new Sf("GREEDY_MODEL_ORDER",4)}function eb9(e,t){var n,r,i;for(e.b[t.g]=1,r=epL(t.d,0);r.b!=r.d.c;)i=(n=Pp(Vv(r),188)).c,1==e.b[i.g]?P7(e.a,n):2==e.b[i.g]?e.b[i.g]=1:eb9(e,i)}function eb8(e,t){var n,r,i;for(i=new XM(t.gc()),r=t.Kc();r.Ob();)(n=Pp(r.Pb(),286)).c==n.f?eE5(e,n,n.c):eEQ(e,n)||(i.c[i.c.length]=n);return i}function eb7(e,t,n){var r,i,a,o,s;for(s=e.r+t,e.r+=t,e.d+=n,r=n/e.n.c.length,i=0,o=new fz(e.n);o.aa&&Bc(t,a,null),t}function emx(e,t){var n,r;if(r=e.gc(),null==t){for(n=0;n0&&(u+=i),c[l]=o,o+=s*(u+r)}function emj(e){var t,n,r;for(t=0,r=e.f,e.n=Je(tyx,eH5,25,r,15,1),e.d=Je(tyx,eH5,25,r,15,1);t0?e.c:0),++i;e.b=r,e.d=a}function emW(e,t){var n,r,i,a,o;for(r=0,i=0,n=0,o=new fz(t);o.a0?e.g:0),++n;e.c=i,e.d=r}function emK(e,t){var n;return n=eow(vx(tyx,1),eH5,25,15,[ebM(e,(etx(),e3D),t),ebM(e,e3N,t),ebM(e,e3P,t)]),e.f&&(n[0]=eB4.Math.max(n[0],n[2]),n[2]=n[0]),n}function emV(e,t,n){var r;try{eCQ(e,t+e.j,n+e.k,!1,!0)}catch(i){if(i=eoa(i),M4(i,73))throw r=i,p7(new gE(r.g+ezk+t+eUd+n+")."));throw p7(i)}}function emq(e,t,n){var r;try{eCQ(e,t+e.j,n+e.k,!0,!1)}catch(i){if(i=eoa(i),M4(i,73))throw r=i,p7(new gE(r.g+ezk+t+eUd+n+")."));throw p7(i)}}function emZ(e){var t;Ln(e,(eBy(),taZ))&&((t=Pp(e_k(e,taZ),21)).Hc((eT7(),tp1))?(t.Mc(tp1),t.Fc(tp2)):t.Hc(tp2)&&(t.Mc(tp2),t.Fc(tp1)))}function emX(e){var t;Ln(e,(eBy(),taZ))&&((t=Pp(e_k(e,taZ),21)).Hc((eT7(),tp9))?(t.Mc(tp9),t.Fc(tp5)):t.Hc(tp5)&&(t.Mc(tp5),t.Fc(tp9)))}function emJ(e,t,n){ewG(n,"Self-Loop ordering",1),_r(UQ(UJ(UJ(eeh(new R1(null,new Gq(t.b,16)),new n9),new n8),new n7),new re),new d1(e)),eEj(n)}function emQ(e,t,n,r){var i,a;for(i=t;i0&&(i.b+=t),i}function em8(e,t){var n,r,i;for(i=new yb,r=e.Kc();r.Ob();)eIn(n=Pp(r.Pb(),37),0,i.b),i.b+=n.f.b+t,i.a=eB4.Math.max(i.a,n.f.a);return i.a>0&&(i.a+=t),i}function em7(e){var t,n,r;for(r=eUu,n=new fz(e.a);n.a>16==6?e.Cb.ih(e,5,e6E,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||e.zh(),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function egr(e){$O();var t=e.e;if(t&&t.stack){var n=t.stack,r=t+"\n";return n.substring(0,r.length)==r&&(n=n.substring(r.length)),n.split("\n")}return[]}function egi(e){var t;return(t=(en4(),e0U))[e>>>28]|t[e>>24&15]<<4|t[e>>20&15]<<8|t[e>>16&15]<<12|t[e>>12&15]<<16|t[e>>8&15]<<20|t[e>>4&15]<<24|t[15&e]<<28}function ega(e){var t,n,r;e.b==e.c&&(r=e.a.length,n=esi(eB4.Math.max(8,r))<<1,0!=e.b?(t=CY(e.a,n),erL(e,t,r),e.a=t,e.b=0):bF(e.a,n),e.c=r)}function ego(e,t){var n;return(n=e.b).Xe((eBB(),thK))?n.Hf()==(eYu(),tbY)?-n.rf().a-gP(LV(n.We(thK))):t+gP(LV(n.We(thK))):n.Hf()==(eYu(),tbY)?-n.rf().a:t}function egs(e){var t;return 0!=e.b.c.length&&Pp(RJ(e.b,0),70).a?Pp(RJ(e.b,0),70).a:null!=(t=Hh(e))?t:""+(e.c?QI(e.c.a,e,0):-1)}function egu(e){var t;return 0!=e.f.c.length&&Pp(RJ(e.f,0),70).a?Pp(RJ(e.f,0),70).a:null!=(t=Hh(e))?t:""+(e.i?QI(e.i.j,e,0):-1)}function egc(e,t){var n,r;if(t<0||t>=e.gc())return null;for(n=t;n0?e.c:0),i=eB4.Math.max(i,t.d),++r;e.e=a,e.b=i}function egd(e){var t,n;if(!e.b)for(e.b=K$(Pp(e.f,118).Ag().i),n=new Ow(Pp(e.f,118).Ag());n.e!=n.i.gc();)t=Pp(epH(n),137),P_(e.b,new gO(t));return e.b}function egh(e,t){var n,r,i;if(t.dc())return LF(),LF(),tmB;for(n=new Cy(e,t.gc()),i=new Ow(e);i.e!=i.i.gc();)r=epH(i),t.Hc(r)&&JL(n,r);return n}function egp(e,t,n,r){return 0==t?r?(e.o||(e.o=new JY((eBa(),tmy),e6O,e,0)),e.o):(e.o||(e.o=new JY((eBa(),tmy),e6O,e,0)),X6(e.o)):ebl(e,t,n,r)}function egb(e){var t,n;if(e.rb)for(t=0,n=e.rb.i;t>22))>>22)<0)&&(e.l=n&eHH,e.m=r&eHH,e.h=i&eH$,!0))}function egw(e,t,n,r,i,a,o){var s,u;return!(t.Ae()&&((u=e.a.ue(n,r))<0||!i&&0==u)||t.Be()&&((s=e.a.ue(n,a))>0||!o&&0==s))}function eg_(e,t){var n;if(euv(),0!=(n=e.j.g-t.j.g))return 0;switch(e.j.g){case 2:return efy(t,e73)-efy(e,e73);case 4:return efy(e,e72)-efy(t,e72)}return 0}function egE(e){switch(e.g){case 0:return te3;case 1:return te4;case 2:return te5;case 3:return te6;case 4:return te9;case 5:return te8;default:return null}}function egS(e,t,n){var r,i;return r=(eu2(i=new mN,t),er3(i,n),JL((e.c||(e.c=new FQ(tga,e,12,10)),e.c),i),i),end(r,0),enh(r,1),els(r,!0),eli(r,!0),r}function egk(e,t){var n,r;if(t>=e.i)throw p7(new xJ(t,e.i));return++e.j,n=e.g[t],(r=e.i-t-1)>0&&ePD(e.g,t+1,e.g,t,r),Bc(e.g,--e.i,null),e.fi(t,n),e.ci(),n}function egx(e,t){var n,r;return e.Db>>16==17?e.Cb.ih(e,21,tm7,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||e.zh(),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function egT(e){var t,n,r,i;for(Hj(),Mv(e.c,e.a),i=new fz(e.c);i.an.a.c.length))throw p7(new gL("index must be >= 0 and <= layer node count"));e.c&&QA(e.c.a,e),e.c=n,n&&jO(n.a,t,e)}function egH(e,t){var n,r,i;for(r=new Fa(OH(efs(e).a.Kc(),new c));eTk(r);)return n=Pp(ZC(r),17),i=Pp(t.Kb(n),10),new c5(Y9(i.n.b+i.o.b/2));return m4(),m4(),e0l}function eg$(e,t){this.c=new p2,this.a=e,this.b=t,this.d=Pp(e_k(e,(eBU(),tnx)),304),xc(e_k(e,(eBy(),taX)))===xc((Qx(),tte))?this.e=new mg:this.e=new mm}function egz(e,t){var n,r,i,a;for(a=0,r=new fz(e);r.a>16==6?e.Cb.ih(e,6,e6g,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBa(),tmp),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function eg0(e,t){var n,r;return e.Db>>16==7?e.Cb.ih(e,1,e6p,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBa(),tmm),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function eg2(e,t){var n,r;return e.Db>>16==9?e.Cb.ih(e,9,e6k,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBa(),tmv),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function eg3(e,t){var n,r;return e.Db>>16==5?e.Cb.ih(e,9,tgt,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBK(),tgT),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function eg4(e,t){var n,r;return e.Db>>16==3?e.Cb.ih(e,0,e6y,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBK(),tgy),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function eg5(e,t){var n,r;return e.Db>>16==7?e.Cb.ih(e,6,e6E,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBK(),tgP),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function eg6(){this.a=new o6,this.g=new ebH,this.j=new ebH,this.b=new p2,this.d=new ebH,this.i=new ebH,this.k=new p2,this.c=new p2,this.e=new p2,this.f=new p2}function eg9(e,t,n){var r,i,a;for(n<0&&(n=0),a=e.i,i=n;ieH6)return eg7(e,r);if(r==e)return!0}}return!1}function eve(e){switch(Ab(),e.q.g){case 5:ekK(e,(eYu(),tbw)),ekK(e,tbj);break;case 4:eMz(e,(eYu(),tbw)),eMz(e,tbj);break;default:eYa(e,(eYu(),tbw)),eYa(e,tbj)}}function evt(e){switch(Ab(),e.q.g){case 5:exG(e,(eYu(),tby)),exG(e,tbY);break;case 4:epq(e,(eYu(),tby)),epq(e,tbY);break;default:eYo(e,(eYu(),tby)),eYo(e,tbY)}}function evn(e){var t,n;(t=Pp(e_k(e,(eCk(),e9O)),19))?0==(n=t.a)?eo3(e,(erV(),e9F),new efo):eo3(e,(erV(),e9F),new qS(n)):eo3(e,(erV(),e9F),new qS(1))}function evr(e,t){var n;switch(n=e.i,t.g){case 1:return-(e.n.b+e.o.b);case 2:return e.n.a-n.o.a;case 3:return e.n.b-n.o.b;case 4:return-(e.n.a+e.o.a)}return 0}function evi(e,t){switch(e.g){case 0:return t==(ef_(),tnN)?e7V:e7q;case 1:return t==(ef_(),tnN)?e7V:e7K;case 2:return t==(ef_(),tnN)?e7K:e7q;default:return e7K}}function eva(e,t){var n,r,i;for(QA(e.a,t),e.e-=t.r+(0==e.a.c.length?0:e.c),i=eqe,r=new fz(e.a);r.a>16==3?e.Cb.ih(e,12,e6k,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBa(),tmh),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function evs(e,t){var n,r;return e.Db>>16==11?e.Cb.ih(e,10,e6k,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBa(),tmg),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function evu(e,t){var n,r;return e.Db>>16==10?e.Cb.ih(e,11,tm7,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBK(),tgD),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function evc(e,t){var n,r;return e.Db>>16==10?e.Cb.ih(e,12,tgi,t):(r=ebY(Pp(ee2((n=Pp(eaS(e,16),26))||(eBK(),tgR),e.Db>>16),18)),e.Cb.ih(e,r.n,r.f,t))}function evl(e){var t;return(1&e.Bb)==0&&e.r&&e.r.kh()&&(t=Pp(e.r,49),e.r=Pp(ecv(e,t),138),e.r!=t&&(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,9,8,t,e.r))),e.r}function evf(e,t,n){var r;return r=eow(vx(tyx,1),eH5,25,15,[e_u(e,(etx(),e3D),t,n),e_u(e,e3N,t,n),e_u(e,e3P,t,n)]),e.f&&(r[0]=eB4.Math.max(r[0],r[2]),r[2]=r[0]),r}function evd(e,t){var n,r,i;if(0!=(i=eb8(e,t)).c.length)for(Mv(i,new nD),n=i.c.length,r=0;r>19)!=(c=t.h>>19)?c-u:(i=e.h)!=(s=t.h)?i-s:(r=e.m)!=(o=t.m)?r-o:(n=e.l)-(a=t.l)}function evw(){evw=A,e3E=(eCp(),e3A),e3_=new xX(e$J,e3E),e3w=(eeR(),e3p),e3y=new xX(e$Q,e3w),e3v=(epC(),e3f),e3g=new xX(e$1,e3v),e3m=new xX(e$0,(OQ(),!0))}function ev_(e,t,n){var r,i;r=t*n,M4(e.g,145)?(i=Vm(e)).f.d?i.f.a||(e.d.a+=r+ezs):(e.d.d-=r+ezs,e.d.a+=r+ezs):M4(e.g,10)&&(e.d.d-=r,e.d.a+=2*r)}function evE(e,t,n){var r,i,a,o,s;for(i=e[n.g],s=new fz(t.d);s.a0?e.g:0),++n;t.b=r,t.e=i}function evk(e){var t,n,r;if(r=e.b,w4(e.i,r.length)){for(n=2*r.length,e.b=Je(e1z,eU1,317,n,0,1),e.c=Je(e1z,eU1,317,n,0,1),e.f=n-1,e.i=0,t=e.a;t;t=t.c)ekT(e,t,t);++e.g}}function evx(e,t,n,r){var i,a,o,s;for(i=0;io&&(s=o/r),i>a&&(u=a/i),Ol(e,eB4.Math.min(s,u)),e}function evO(){var e,t;ePm();try{if(t=Pp(eyv((_Q(),tgp),eXe),2014))return t}catch(n){if(n=eoa(n),M4(n,102))e=n,Fi((Mo(),e));else throw p7(n)}return new o1}function evA(){var e,t;Qk();try{if(t=Pp(eyv((_Q(),tgp),eQB),2024))return t}catch(n){if(n=eoa(n),M4(n,102))e=n,Fi((Mo(),e));else throw p7(n)}return new uc}function evL(){var e,t;ePm();try{if(t=Pp(eyv((_Q(),tgp),eQc),1941))return t}catch(n){if(n=eoa(n),M4(n,102))e=n,Fi((Mo(),e));else throw p7(n)}return new sT}function evC(e,t,n){var r,i;return i=e.e,e.e=t,(4&e.Db)!=0&&(1&e.Db)==0&&(r=new FX(e,1,4,i,t),n?n.Ei(r):n=r),i!=t&&(n=t?eFr(e,eOl(e,t),n):eFr(e,e.a,n)),n}function evI(){wW.call(this),this.e=-1,this.a=!1,this.p=eHt,this.k=-1,this.c=-1,this.b=-1,this.g=!1,this.f=-1,this.j=-1,this.n=-1,this.i=-1,this.d=-1,this.o=eHt}function evD(e,t){var n,r,i;if(r=e.b.d.d,e.a||(r+=e.b.d.a),i=t.b.d.d,t.a||(i+=t.b.d.a),0==(n=elN(r,i))){if(!e.a&&t.a)return -1;if(!t.a&&e.a)return 1}return n}function evN(e,t){var n,r,i;if(r=e.b.b.d,e.a||(r+=e.b.b.a),i=t.b.b.d,t.a||(i+=t.b.b.a),0==(n=elN(r,i))){if(!e.a&&t.a)return -1;if(!t.a&&e.a)return 1}return n}function evP(e,t){var n,r,i;if(r=e.b.g.d,e.a||(r+=e.b.g.a),i=t.b.g.d,t.a||(i+=t.b.g.a),0==(n=elN(r,i))){if(!e.a&&t.a)return -1;if(!t.a&&e.a)return 1}return n}function evR(){evR=A,e99=j0(RI(RI(RI(new K2,(e_x(),e8r),(eB$(),e7f)),e8r,e7b),e8i,e7E),e8i,e87),e97=RI(RI(new K2,e8r,e8Q),e8r,e7e),e98=j0(new K2,e8i,e7n)}function evj(e){var t,n,r,i,a;for(t=Pp(e_k(e,(eBU(),ttq)),83),a=e.n,r=t.Cc().Kc();r.Ob();)i=(n=Pp(r.Pb(),306)).i,i.c+=a.a,i.d+=a.b,n.c?eL3(n):eL4(n);eo3(e,ttq,null)}function evF(e,t,n){var r,i;switch(r=(i=e.b).d,t.g){case 1:return-r.d-n;case 2:return i.o.a+r.c+n;case 3:return i.o.b+r.a+n;case 4:return-r.b-n;default:return -1}}function evY(e){var t,n,r,i,a;if(r=0,i=ezq,e.b)for(t=0;t<360;t++)n=.017453292519943295*t,eIq(e,e.d,0,0,eV7,n),(a=e.b.ig(e.d))0&&(o=(a&eUu)%e.d.length,i=exx(e,o,a,t)))?s=i.ed(n):(r=e.tj(a,t,n),e.c.Fc(r),null)}function ev1(e,t){var n,r,i,a;switch(ecG(e,t)._k()){case 3:case 2:for(i=0,a=(n=ePk(t)).i;i=0;r--)if(IE(e[r].d,t)||IE(e[r].d,n)){e.length>=r+1&&e.splice(0,r+1);break}return e}function eyt(e,t){var n;return Ts(e)&&Ts(t)&&eHV<(n=e/t)&&n0&&(e.b+=2,e.a+=r):(e.b+=1,e.a+=eB4.Math.min(r,i))}function eyc(e,t){var n,r;if(r=!1,xd(t)&&(r=!0,BC(e,new B_(Lq(t)))),!r&&M4(t,236)&&(r=!0,BC(e,(n=IZ(Pp(t,236)),new lI(n)))),!r)throw p7(new gk(eXE))}function eyl(e,t,n,r){var i,a,o;return i=new Q$(e.e,1,10,M4(o=t.c,88)?Pp(o,26):(eBK(),tgI),M4(a=n.c,88)?Pp(a,26):(eBK(),tgI),ebv(e,t),!1),r?r.Ei(i):r=i,r}function eyf(e){var t,n;switch(Pp(e_k(Bq(e),(eBy(),taP)),420).g){case 0:return t=e.n,n=e.o,new kl(t.a+n.a/2,t.b+n.b/2);case 1:return new TS(e.n);default:return null}}function eyd(){eyd=A,tto=new Sm(eGR,0),tta=new Sm("LEFTUP",1),ttu=new Sm("RIGHTUP",2),tti=new Sm("LEFTDOWN",3),tts=new Sm("RIGHTDOWN",4),ttr=new Sm("BALANCED",5)}function eyh(e,t,n){var r,i,a;if(0==(r=elN(e.a[t.p],e.a[n.p]))){if(i=Pp(e_k(t,(eBU(),tt7)),15),a=Pp(e_k(n,tt7),15),i.Hc(n))return -1;if(a.Hc(t))return 1}return r}function eyp(e){switch(e.g){case 1:return new a$;case 2:return new az;case 3:return new aH;case 0:return null;default:throw p7(new gL(eqa+(null!=e.f?e.f:""+e.g)))}}function eyb(e,t,n){switch(t){case 1:e.n||(e.n=new FQ(e6S,e,1,7)),eRT(e.n),e.n||(e.n=new FQ(e6S,e,1,7)),Y4(e.n,Pp(n,14));return;case 2:ert(e,Lq(n));return}esU(e,t,n)}function eym(e,t,n){switch(t){case 3:eni(e,gP(LV(n)));return;case 4:ena(e,gP(LV(n)));return;case 5:eno(e,gP(LV(n)));return;case 6:ens(e,gP(LV(n)));return}eyb(e,t,n)}function eyg(e,t,n){var r,i,a;(i=ew3(a=r=new mN,t,null))&&i.Fi(),er3(a,n),JL((e.c||(e.c=new FQ(tga,e,12,10)),e.c),a),end(a,0),enh(a,1),els(a,!0),eli(a,!0)}function eyv(e,t){var n,r,i;return M4(n=Ea(e.g,t),235)?((i=Pp(n,235)).Qh(),i.Nh()):M4(n,498)?i=(r=Pp(n,1938)).b:null}function eyy(e,t,n,r){var i,a;return Y9(t),Y9(n),a=Pp(Iq(e.d,t),19),QW(!!a,"Row %s not in %s",t,e.e),i=Pp(Iq(e.b,n),19),QW(!!i,"Column %s not in %s",n,e.c),eoy(e,a.a,i.a,r)}function eyw(e,t,n,r,i,a,o){var s,u,c,l,f;if(l=i[a],f=emH(s=(c=a==o-1)?r:0,l),10!=r&&eow(vx(e,o-a),t[a],n[a],s,f),!c)for(++a,u=0;u1||-1==s?(a=Pp(u,15),i.Wb(ehk(e,a))):i.Wb(eI4(e,Pp(u,56))))}function eyP(e,t,n,r){wd();var i=eUn;function a(){for(var e=0;eeVW);)i>-.000001&&++n;return n}function eyW(e,t){var n;t!=e.b?(n=null,e.b&&(n=$7(e.b,e,-4,n)),t&&(n=ep0(t,e,-4,n)),(n=ecm(e,t,n))&&n.Fi()):(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,3,t,t))}function eyK(e,t){var n;t!=e.f?(n=null,e.f&&(n=$7(e.f,e,-1,n)),t&&(n=ep0(t,e,-1,n)),(n=ecg(e,t,n))&&n.Fi()):(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,0,t,t))}function eyV(e){var t,n,r;if(null==e)return null;if((n=Pp(e,15)).dc())return"";for(r=new vs,t=n.Kc();t.Ob();)xk(r,(eR7(),Lq(t.Pb()))),r.a+=" ";return x3(r,r.a.length-1)}function eyq(e){var t,n,r;if(null==e)return null;if((n=Pp(e,15)).dc())return"";for(r=new vs,t=n.Kc();t.Ob();)xk(r,(eR7(),Lq(t.Pb()))),r.a+=" ";return x3(r,r.a.length-1)}function eyZ(e,t,n){var r,i;return(r=e.c[t.c.p][t.p],i=e.c[n.c.p][n.p],null!=r.a&&null!=i.a)?F_(r.a,i.a):null!=r.a?-1:null!=i.a?1:0}function eyX(e,t){var n,r,i,a,o,s;if(t)for(a=t.a.length,s=((n=new Fs(a)).b-n.a)*n.c<0?(_9(),eB3):new OR(n);s.Ob();)i=KZ(t,(o=Pp(s.Pb(),19)).a),UX((r=new pu(e)).a,i)}function eyJ(e,t){var n,r,i,a,o,s;if(t)for(a=t.a.length,s=((n=new Fs(a)).b-n.a)*n.c<0?(_9(),eB3):new OR(n);s.Ob();)i=KZ(t,(o=Pp(s.Pb(),19)).a),UZ((r=new h7(e)).a,i)}function eyQ(e){var t;if(null!=e&&e.length>0&&33==UI(e,e.length-1))try{return t=eSR(Az(e,0,e.length-1)),null==t.e}catch(n){if(n=eoa(n),!M4(n,32))throw p7(n)}return!1}function ey1(e,t,n){var r,i,a;return r=t.ak(),a=t.dd(),i=r.$j()?$N(e,3,r,null,a,eN1(e,r,a,M4(r,99)&&(Pp(r,18).Bb&eH3)!=0),!0):$N(e,1,r,r.zj(),a,-1,!0),n?n.Ei(i):n=i,n}function ey0(){var e,t,n;for(e=0,t=0;e<1;e++){if(0==(n=eTa((GV(e,1),"X".charCodeAt(e)))))throw p7(new gX("Unknown Option: "+"X".substr(e)));t|=n}return t}function ey2(e,t,n){var r,i,a;switch(i=el0(r=Bq(t)),a=new eES,Gc(a,t),n.g){case 1:ekv(a,elC(ef9(i)));break;case 2:ekv(a,ef9(i))}return eo3(a,(eBy(),toc),LV(e_k(e,toc))),a}function ey3(e){var t,n;return t=Pp(ZC(new Fa(OH(efu(e.a).a.Kc(),new c))),17),n=Pp(ZC(new Fa(OH(efc(e.a).a.Kc(),new c))),17),gN(LK(e_k(t,(eBU(),tnE))))||gN(LK(e_k(n,tnE)))}function ey4(){ey4=A,ter=new Sa("ONE_SIDE",0),tea=new Sa("TWO_SIDES_CORNER",1),teo=new Sa("TWO_SIDES_OPPOSING",2),tei=new Sa("THREE_SIDES",3),ten=new Sa("FOUR_SIDES",4)}function ey5(e,t,n,r,i){var a,o;a=Pp(qE(UJ(t.Oc(),new ih),JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[(eum(),e2U)]))),15),o=Pp(eay(e.b,n,r),15),0==i?o.Wc(0,a):o.Gc(a)}function ey6(e,t){var n,r,i,a,o;for(a=new fz(t.a);a.a0&&egL(this,this.c-1,(eYu(),tby)),this.c0&&e[0].length>0&&(this.c=gN(LK(e_k(Bq(e[0][0]),(eBU(),tne))))),this.a=Je(e5d,eUP,2018,e.length,0,2),this.b=Je(e5h,eUP,2019,e.length,0,2),this.d=new euX}function ewu(e){return 0!=e.c.length&&((GK(0,e.c.length),Pp(e.c[0],17)).c.i.k==(eEn(),e8D)||q3(UQ(new R1(null,new Gq(e,16)),new iJ),new iQ))}function ewc(e,t,n){return ewG(n,"Tree layout",1),Kx(e.b),Yb(e.b,(egR(),tuJ),tuJ),Yb(e.b,tuQ,tuQ),Yb(e.b,tu1,tu1),Yb(e.b,tu0,tu0),e.a=eRq(e.b,t),eAG(e,t,eiI(n,1)),eEj(n),t}function ewl(e,t){var n,r,i,a,o,s,u;for(s=eLj(t),a=t.f,u=t.g,o=eB4.Math.sqrt(a*a+u*u),i=0,r=new fz(s);r.a=0?(n=eyt(e,eHK),r=edQ(e,eHK)):(n=eyt(t=Fy(e,1),5e8),r=eft(Fg(r=edQ(t,5e8),1),WM(e,1))),WO(Fg(r,32),WM(n,eH8))}function ewM(e,t,n){var r,i;switch(r=(A6(0!=t.b),Pp(etw(t,t.a.a),8)),n.g){case 0:r.b=0;break;case 2:r.b=e.f;break;case 3:r.a=0;break;default:r.a=e.g}return YU(i=epL(t,0),r),t}function ewO(e,t,n,r){var i,a,o,s,u;switch(u=e.b,s=epd(o=(a=t.d).j,u.d[o.g],n),i=C5(MB(a.n),a.a),a.j.g){case 1:case 3:s.a+=i.a;break;case 2:case 4:s.b+=i.b}qQ(r,s,r.c.b,r.c)}function ewA(e,t,n){var r,i,a,o;for(o=QI(e.e,t,0),(a=new ma).b=n,r=new KB(e.e,o);r.b1;t>>=1)(1&t)!=0&&(r=eeD(r,n)),n=1==n.d?eeD(n,n):new eh5(eDE(n.a,n.d,Je(ty_,eHT,25,n.d<<1,15,1)));return eeD(r,n)}function ewP(){var e,t,n,r;for(t=32,ewP=A,e2v=Je(tyx,eH5,25,25,15,1),e2y=Je(tyx,eH5,25,33,15,1),r=152587890625e-16;t>=0;t--)e2y[t]=r,r*=.5;for(e=24,n=1;e>=0;e--)e2v[e]=n,n*=.5}function ewR(e){var t,n;if(gN(LK(eT8(e,(eBy(),taI))))){for(n=new Fa(OH(eOi(e).a.Kc(),new c));eTk(n);)if(t=Pp(ZC(n),79),exb(t)&&gN(LK(eT8(t,taD))))return!0}return!1}function ewj(e,t){var n,r,i;Yf(e.f,t)&&(t.b=e,r=t.c,-1!=QI(e.j,r,0)||P_(e.j,r),i=t.d,-1!=QI(e.j,i,0)||P_(e.j,i),0!=(n=t.a.b).c.length&&(e.i||(e.i=new epS(e)),ea_(e.i,n)))}function ewF(e){var t,n,r,i,a;return(r=(n=e.c.d).j)==(a=(i=e.d.d).j)?n.p=0&&IE(e.substr(t,3),"GMT")?(n[0]=t+3,eDh(e,n,r)):(t>=0&&IE(e.substr(t,3),"UTC")&&(n[0]=t+3),eDh(e,n,r))}function ewz(e,t){var n,r,i,a,o;for(a=e.g.a,o=e.g.b,r=new fz(e.d);r.an;a--)e[a]|=t[a-n-1]>>>o,e[a-1]=t[a-n-1]<=e.f)break;a.c[a.c.length]=n}return a}function ew1(e){var t,n,r,i;for(t=null,i=new fz(e.wf());i.a0&&ePD(e.g,t,e.g,t+r,s),o=n.Kc(),e.i+=r,i=0;ia&&F6(c,ee5(n[s],e2h))&&(i=s,a=u);return i>=0&&(r[0]=t+a),i}function ew9(e,t){var n;if(0!=(n=To(e.b.Hf(),t.b.Hf())))return n;switch(e.b.Hf().g){case 1:case 2:return ME(e.b.sf(),t.b.sf());case 3:case 4:return ME(t.b.sf(),e.b.sf())}return 0}function ew8(e){var t,n,r;for(r=e.e.c.length,e.a=RF(ty_,[eUP,eHT],[48,25],15,[r,r],2),n=new fz(e.c);n.a>4&15,a=15&e[r],o[i++]=tmk[n],o[i++]=tmk[a];return ehv(o,0,o.length)}function e_t(e,t,n){var r,i,a;return r=t.ak(),a=t.dd(),i=r.$j()?$N(e,4,r,a,null,eN1(e,r,a,M4(r,99)&&(Pp(r,18).Bb&eH3)!=0),!0):$N(e,r.Kj()?2:1,r,a,r.zj(),-1,!0),n?n.Ei(i):n=i,n}function e_n(e){var t,n;return e>=eH3?(t=eH4+(e-eH3>>10&1023)&eHd,n=56320+(e-eH3&1023)&eHd,String.fromCharCode(t)+""+String.fromCharCode(n)):String.fromCharCode(e&eHd)}function e_r(e,t){var n,r,i,a;return Cn(),(i=Pp(Pp(Zq(e.r,t),21),84)).gc()>=2&&(r=Pp(i.Kc().Pb(),111),n=e.u.Hc((ekU(),tbh)),a=e.u.Hc(tbg),!r.a&&!n&&(2==i.gc()||a))}function e_i(e,t,n,r,i){var a,o,s;for(a=eLx(e,t,n,r,i),s=!1;!a;)eME(e,i,!0),s=!0,a=eLx(e,t,n,r,i);s&&eME(e,i,!1),0!=(o=eoA(i)).c.length&&(e.d&&e.d.lg(o),e_i(e,i,n,r,o))}function e_a(){e_a=A,tpN=new km(eGR,0),tpI=new km("DIRECTED",1),tpP=new km("UNDIRECTED",2),tpL=new km("ASSOCIATION",3),tpD=new km("GENERALIZATION",4),tpC=new km("DEPENDENCY",5)}function e_o(e,t){var n;if(!zY(e))throw p7(new gC(eZL));switch(n=zY(e),t.g){case 1:return-(e.j+e.f);case 2:return e.i-n.g;case 3:return e.j-n.f;case 4:return-(e.i+e.g)}return 0}function e_s(e,t){var n,r;for(BJ(t),r=e.b.c.length,P_(e.b,t);r>0;){if(n=r,r=(r-1)/2|0,0>=e.a.ue(RJ(e.b,r),t))return q1(e.b,n,t),!0;q1(e.b,n,RJ(e.b,r))}return q1(e.b,r,t),!0}function e_u(e,t,n,r){var i,a;if(i=0,n)i=euW(e.a[n.g][t.g],r);else for(a=0;a=s)}function e_l(e,t,n,r){var i;if(i=!1,xd(r)&&(i=!0,P4(t,n,Lq(r))),!i&&xl(r)&&(i=!0,e_l(e,t,n,r)),!i&&M4(r,236)&&(i=!0,H1(t,n,Pp(r,236))),!i)throw p7(new gk(eXE))}function e_f(e,t){var n,r,i;if((n=t.Hh(e.a))&&null!=(i=edW((n.b||(n.b=new L_((eBK(),tgF),tgf,n)),n.b),eQe))){for(r=1;r<(eSp(),tvs).length;++r)if(IE(tvs[r],i))return r}return 0}function e_d(e,t){var n,r,i;if((n=t.Hh(e.a))&&null!=(i=edW((n.b||(n.b=new L_((eBK(),tgF),tgf,n)),n.b),eQe))){for(r=1;r<(eSp(),tvu).length;++r)if(IE(tvu[r],i))return r}return 0}function e_h(e,t){var n,r,i,a;if(BJ(t),(a=e.a.gc())0?1:0;a.a[i]!=n;)a=a.a[i],i=e.a.ue(n.d,a.d)>0?1:0;a.a[i]=r,r.b=n.b,r.a[0]=n.a[0],r.a[1]=n.a[1],n.a[0]=null,n.a[1]=null}function e_y(e){var t,n;return ekU(),t=jL(tbp,eow(vx(e6i,1),eU4,273,0,[tbm])),!(eaC(z_(t,e))>1)&&(n=jL(tbh,eow(vx(e6i,1),eU4,273,0,[tbd,tbg])),!(eaC(z_(n,e))>1))}function e_w(e,t){var n;M4(n=zg((_Q(),tgp),e),498)?Ge(tgp,e,new k5(this,t)):Ge(tgp,e,this),e_8(this,t),t==(yO(),tgg)?(this.wb=Pp(this,1939),Pp(t,1941)):this.wb=(BM(),tgv)}function e__(e){var t,n,r;if(null==e)return null;for(n=0,t=null;n=eHf?"error":r>=900?"warn":r>=800?"info":"log",e.a),e.b&&eAp(t,n,e.b,"Exception: ",!0))}function e_k(e,t){var n,r;return null!=(r=(e.q||(e.q=new p2),Bp(e.q,t)))?r:(M4(n=t.wg(),4)&&(null==n?(e.q||(e.q=new p2),Z3(e.q,t)):(e.q||(e.q=new p2),Um(e.q,t,n))),n)}function e_x(){e_x=A,e8e=new Ez("P1_CYCLE_BREAKING",0),e8t=new Ez("P2_LAYERING",1),e8n=new Ez("P3_NODE_ORDERING",2),e8r=new Ez("P4_NODE_PLACEMENT",3),e8i=new Ez("P5_EDGE_ROUTING",4)}function e_T(e,t){var n,r,i,a,o;for(r=(i=1==t?e8c:e8u).a.ec().Kc();r.Ob();)for(n=Pp(r.Pb(),103),o=Pp(Zq(e.f.c,n),21).Kc();o.Ob();)a=Pp(o.Pb(),46),QA(e.b.b,a.b),QA(e.b.a,Pp(a.b,81).d)}function e_M(e,t){var n;if(eeP(),e.c!=t.c)return elN(e.c,t.c);if(e.b==t.b||eiS(e.b,t.b)){if(n=Tu(e.b)?1:-1,e.a&&!t.a)return n;if(!e.a&&t.a)return-n}return ME(e.b.g,t.b.g)}function e_O(e,t){var n;ewG(t,"Hierarchical port position processing",1),(n=e.b).c.length>0&&eI6((GK(0,n.c.length),Pp(n.c[0],29)),e),n.c.length>1&&eI6(Pp(RJ(n,n.c.length-1),29),e),eEj(t)}function e_A(e,t){var n,r,i;if(e_Y(e,t))return!0;for(r=new fz(t);r.a=(i=e.Vi())||t<0)throw p7(new gE(eXU+t+eXH+i));if(n>=i||n<0)throw p7(new gE(eX$+n+eXH+i));return t!=n?(a=e.Ti(n),e.Hi(t,a),a):e.Oi(n)}function e_j(e){var t,n,r;if(r=e,e)for(t=0,n=e.Ug();n;n=n.Ug()){if(++t>eH6)return e_j(n);if(r=n,n==e)throw p7(new gC("There is a cycle in the containment hierarchy of "+e))}return r}function e_F(e){var t,n,r;for(r=new eaP(eUd,"[","]"),n=e.Kc();n.Ob();)ZJ(r,xc(t=n.Pb())===xc(e)?"(this Collection)":null==t?eUg:efF(t));return r.a?0==r.e.length?r.a.a:r.a.a+""+r.e:r.c}function e_Y(e,t){var n,r;if(r=!1,2>t.gc())return!1;for(n=0;n=e.charCodeAt(r));)++r;for(t=n;t>r&&(GV(t-1,e.length),32>=e.charCodeAt(t-1));)--t;return r>0||t1&&(e.j.b+=e.e)):(e.j.a+=n.a,e.j.b=eB4.Math.max(e.j.b,n.b),e.d.c.length>1&&(e.j.a+=e.e))}function e_z(){e_z=A,tec=eow(vx(e6a,1),eGj,61,0,[(eYu(),tbw),tby,tbj]),teu=eow(vx(e6a,1),eGj,61,0,[tby,tbj,tbY]),tel=eow(vx(e6a,1),eGj,61,0,[tbj,tbY,tbw]),tef=eow(vx(e6a,1),eGj,61,0,[tbY,tbw,tby])}function e_G(e,t,n,r){var i,a,o,s,u,c,l;if(o=e.c.d,s=e.d.d,o.j!=s.j)for(l=e.b,i=o.j,u=null;i!=s.j;)u=0==t?elI(i):elL(i),P7(r,C5(a=epd(i,l.d[i.g],n),c=epd(u,l.d[u.g],n))),i=u}function e_W(e,t,n,r){var i,a,o,s,u;return o=egN(e.a,t,n),s=Pp(o.a,19).a,a=Pp(o.b,19).a,r&&(u=Pp(e_k(t,(eBU(),tng)),10),i=Pp(e_k(n,tng),10),u&&i&&(V6(e.b,u,i),s+=e.b.i,a+=e.b.e)),s>a}function e_K(e){var t,n,r,i,a,o,s,u,c;for(r=0,this.a=ebb(e),this.b=new p0,i=(n=e).length;rL7(e.d).c?(e.i+=e.g.c,ed3(e.d)):L7(e.d).c>L7(e.g).c?(e.e+=e.d.c,ed3(e.g)):(e.i+=R6(e.g),e.e+=R6(e.d),ed3(e.g),ed3(e.d))}function e_X(e,t,n){var r,i,a,o;for(a=t.q,o=t.r,new GT((Xa(),tuU),t,a,1),new GT(tuU,a,o,1),i=new fz(n);i.as&&(u=s/r),i>a&&(c=a/i),o=eB4.Math.min(u,c),e.a+=o*(t.a-e.a),e.b+=o*(t.b-e.b)}function e_5(e,t,n,r,i){var a,o;for(o=!1,a=Pp(RJ(n.b,0),33);eNK(e,t,a,r,i)&&(o=!0,eyL(n,a),0!=n.b.c.length);)a=Pp(RJ(n.b,0),33);return 0==n.b.c.length&&eva(n.j,n),o&&emG(t.q),o}function e_6(e,t){var n,r,i,a;if(eLG(),t.b<2)return!1;for(r=n=Pp(Vv(a=epL(t,0)),8);a.b!=a.d.c;){if(eOV(e,r,i=Pp(Vv(a),8)))return!0;r=i}return!!eOV(e,r,n)}function e_9(e,t,n,r){var i,a;return 0==n?(e.o||(e.o=new JY((eBa(),tmy),e6O,e,0)),Iz(e.o,t,r)):(a=Pp(ee2((i=Pp(eaS(e,16),26))||e.zh(),n),66)).Nj().Rj(e,ehH(e),n-Y1(e.zh()),t,r)}function e_8(e,t){var n;t!=e.sb?(n=null,e.sb&&(n=Pp(e.sb,49).ih(e,1,e6w,n)),t&&(n=Pp(t,49).gh(e,1,e6w,n)),(n=ecY(e,t,n))&&n.Fi()):(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,4,t,t))}function e_7(e,t){var n,r,i,a;if(t)i=enm(t,"x"),enr((n=new pa(e)).a,(BJ(i),i)),a=enm(t,"y"),enc((r=new po(e)).a,(BJ(a),a));else throw p7(new gK("All edge sections need an end point."))}function eEe(e,t){var n,r,i,a;if(t)i=enm(t,"x"),enu((n=new pn(e)).a,(BJ(i),i)),a=enm(t,"y"),enl((r=new pr(e)).a,(BJ(a),a));else throw p7(new gK("All edge sections need a start point."))}function eEt(e,t){var n,r,i,a,o,s,u;for(r=es1(e),a=0,s=r.length;a>22-t,i=e.h<>22-t):t<44?(n=0,r=e.l<>44-t):(n=0,r=0,i=e.l<e))return 0==t||t==e?1:0==e?0:ev6(e)/(ev6(t)*ev6(e-t));throw p7(new gL("k must be smaller than n"))}function eEh(e,t){var n,r,i,a;for(n=new TY(e);null!=n.g||n.c?null==n.g||0!=n.i&&Pp(n.g[n.i-1],47).Ob():zW(n);)if(M4(a=Pp(eM5(n),56),160))for(i=0,r=Pp(a,160);i>4],t[2*n+1]=tv0[15&a];return ehv(t,0,t.length)}function eEA(e){var t,n,r;switch(U_(),r=e.c.length){case 0:return e0p;case 1:return P2((t=Pp(ekM(new fz(e)),42)).cd(),t.dd());default:return n=Pp(epg(e,Je(e1$,eUK,42,e.c.length,0,1)),165),new gt(n)}}function eEL(e){var t,n,r,i,a,o;for(t=new p1,n=new p1,Vw(t,e),Vw(n,e);n.b!=n.c;)for(i=Pp(Yn(n),37),o=new fz(i.a);o.a0&&eIl(e,n,t),i):exV(e,t,n)}function eEN(e,t,n){var r,i,a,o;if(0!=t.b){for(r=new _n,o=epL(t,0);o.b!=o.d.c;)er7(r,eoO(a=Pp(Vv(o),86))),(i=a.e).a=Pp(e_k(a,(eR6(),tcg)),19).a,i.b=Pp(e_k(a,tcv),19).a;eEN(e,r,eiI(n,r.b/e.a|0))}}function eEP(e,t){var n,r,i,a,o;if(e.e<=t||Wm(e,e.g,t))return e.g;for(a=e.r,r=e.g,o=e.r,i=(a-r)/2+r;r+11&&(e.e.b+=e.a)):(e.e.a+=n.a,e.e.b=eB4.Math.max(e.e.b,n.b),e.d.c.length>1&&(e.e.a+=e.a))}function eEH(e){var t,n,r,i;switch(t=(i=e.i).b,r=i.j,n=i.g,i.a.g){case 0:n.a=(e.g.b.o.a-r.a)/2;break;case 1:n.a=t.d.n.a+t.d.a.a;break;case 2:n.a=t.d.n.a+t.d.a.a-r.a;break;case 3:n.b=t.d.n.b+t.d.a.b}}function eE$(e,t,n,r,i){if(rr&&(e.a=r),e.bi&&(e.b=i),e}function eEz(e){if(M4(e,149))return eAi(Pp(e,149));if(M4(e,229))return efZ(Pp(e,229));if(M4(e,23))return eEa(Pp(e,23));throw p7(new gL(eXx+e_F(new g$(eow(vx(e1R,1),eUp,1,5,[e])))))}function eEG(e,t,n,r,i){var a,o,s;for(o=0,a=!0;o>>i|n[o+r+1]<>>i,++o}return a}function eEW(e,t,n,r){var i,a,o;if(t.k==(eEn(),e8D)){for(a=new Fa(OH(efu(t).a.Kc(),new c));eTk(a);)if((o=(i=Pp(ZC(a),17)).c.i.k)==e8D&&e.c.a[i.c.i.c.p]==r&&e.c.a[t.c.p]==n)return!0}return!1}function eEK(e,t){var n,r,i,a;return t&=63,n=e.h&eH$,t<22?(a=n>>>t,i=e.m>>t|n<<22-t,r=e.l>>t|e.m<<22-t):t<44?(a=0,i=n>>>t-22,r=e.m>>t-22|e.h<<44-t):(a=0,i=0,r=n>>>t-44),Mk(r&eHH,i&eHH,a&eH$)}function eEV(e,t,n,r){var i;this.b=r,this.e=e==(enU(),tui),i=t[n],this.d=RF(tyE,[eUP,e$5],[177,25],16,[i.length,i.length],2),this.a=RF(ty_,[eUP,eHT],[48,25],15,[i.length,i.length],2),this.c=new ewo(t,n)}function eEq(e){var t,n,r;for(e.k=new G$((eYu(),eow(vx(e6a,1),eGj,61,0,[tbF,tbw,tby,tbj,tbY])).length,e.j.c.length),r=new fz(e.j);r.a=n)return eE5(e,t,r.p),!0;return!1}function eE1(e){var t;return(64&e.Db)!=0?eEp(e):(t=new O0(eZ$),e.a&&xM(xM((t.a+=' "',t),e.a),'"'),xM(yW(xM(yW(xM(yW(xM(yW((t.a+=" (",t),e.i),","),e.j)," | "),e.g),","),e.f),")"),t.a)}function eE0(e,t,n){var r,i,a,o,s;for(o=0,s=eAY(e.e.Tg(),t),i=Pp(e.g,119),r=0;on?eS1(e,n,"start index"):t<0||t>n?eS1(t,n,"end index"):eCG("end index (%s) must not be less than start index (%s)",eow(vx(e1R,1),eUp,1,5,[ell(t),ell(e)]))}function eE4(e,t){var n,r,i,a;for(r=0,i=e.length;r0&&eE9(e,a,n));t.p=0}function eE8(e){var t;this.c=new _n,this.f=e.e,this.e=e.d,this.i=e.g,this.d=e.c,this.b=e.b,this.k=e.j,this.a=e.a,e.i?this.j=e.i:this.j=(t=Pp(yw(e5Q),9),new I1(t,Pp(CY(t,t.length),9),0)),this.g=e.f}function eE7(e){var t,n,r,i;for(t=Bd(xM(new O0("Predicates."),"and"),40),n=!0,i=new fE(e);i.b0?s[o-1]:Je(e4N,eGW,10,0,0,1),i=s[o],c=o=0?e.Bh(i):ekN(e,r);else throw p7(new gL(eZV+r.ne()+eZq))}else throw p7(new gL(eZJ+t+eZQ))}else ec6(e,n,r)}function eSa(e){var t,n;if(n=null,t=!1,M4(e,204)&&(t=!0,n=Pp(e,204).a),!t&&M4(e,258)&&(t=!0,n=""+Pp(e,258).a),!t&&M4(e,483)&&(t=!0,n=""+Pp(e,483).a),!t)throw p7(new gk(eXE));return n}function eSo(e,t){var n,r;if(!e.f)return t.Ob();for(;t.Ob();)if(M4(r=(n=Pp(t.Pb(),72)).ak(),99)&&(Pp(r,18).Bb&eZ1)!=0&&(!e.e||r.Gj()!=e6d||0!=r.aj())&&null!=n.dd())return t.Ub(),!0;return!1}function eSs(e,t){var n,r;if(!e.f)return t.Sb();for(;t.Sb();)if(M4(r=(n=Pp(t.Ub(),72)).ak(),99)&&(Pp(r,18).Bb&eZ1)!=0&&(!e.e||r.Gj()!=e6d||0!=r.aj())&&null!=n.dd())return t.Pb(),!0;return!1}function eSu(e,t,n){var r,i,a,o,s,u;for(o=0,u=eAY(e.e.Tg(),t),r=0,s=e.i,i=Pp(e.g,119);o1&&(t.c[t.c.length]=a)}function eSf(e){var t,n,r,i;for(er7(n=new _n,e.o),r=new mc;0!=n.b;)(i=eYP(e,t=Pp(0==n.b?null:(A6(0!=n.b),etw(n,n.a.a)),508),!0))&&P_(r.a,t);for(;0!=r.a.c.length;)eYP(e,t=Pp(euO(r),508),!1)}function eSd(){eSd=A,tdS=new ks(ezo,0),tdm=new ks("BOOLEAN",1),tdw=new ks("INT",2),tdE=new ks("STRING",3),tdg=new ks("DOUBLE",4),tdv=new ks("ENUM",5),tdy=new ks("ENUMSET",6),td_=new ks("OBJECT",7)}function eSh(e,t){var n,r,i,a,o;r=eB4.Math.min(e.c,t.c),a=eB4.Math.min(e.d,t.d),i=eB4.Math.max(e.c+e.b,t.c+t.b),o=eB4.Math.max(e.d+e.a,t.d+t.a),i=(i/2|0))for(this.e=r?r.c:null,this.d=i;n++0;)Gi(this);this.b=t,this.a=null}function eSk(e,t){var n,r;t.a?eAk(e,t):((n=Pp(Ik(e.b,t.b),57))&&n==e.a[t.b.f]&&n.a&&n.a!=t.b.a&&n.c.Fc(t.b),(r=Pp(IS(e.b,t.b),57))&&e.a[r.f]==t.b&&r.a&&r.a!=t.b.a&&t.b.c.Fc(r),Ai(e.b,t.b))}function eSx(e,t){var n,r;if(n=Pp(UA(e.b,t),124),Pp(Pp(Zq(e.r,t),21),84).dc()){n.n.b=0,n.n.c=0;return}n.n.b=e.C.b,n.n.c=e.C.c,e.A.Hc((ed6(),tbq))&&eCD(e,t),r=ebi(e,t),eLZ(e,t)==(epT(),tbt)&&(r+=2*e.w),n.a.a=r}function eST(e,t){var n,r;if(n=Pp(UA(e.b,t),124),Pp(Pp(Zq(e.r,t),21),84).dc()){n.n.d=0,n.n.a=0;return}n.n.d=e.C.d,n.n.a=e.C.a,e.A.Hc((ed6(),tbq))&&eCN(e,t),r=eba(e,t),eLZ(e,t)==(epT(),tbt)&&(r+=2*e.w),n.a.b=r}function eSM(e,t){var n,r,i,a;for(a=new p0,r=new fz(t);r.aeB4.Math.abs(r-i))}function eSU(e,t,n){var r,i,a,o,s,u;if(null!=(s=Pp(eaS(e.a,8),1936)))for(a=0,o=(i=s).length;an.a&&(r.Hc((eyY(),tdW))?i=(t.a-n.a)/2:r.Hc(tdV)&&(i=t.a-n.a)),t.b>n.b&&(r.Hc((eyY(),tdZ))?a=(t.b-n.b)/2:r.Hc(tdq)&&(a=t.b-n.b)),e_g(e,i,a)}function eSJ(e,t,n,r,i,a,o,s,u,c,l,f,d){M4(e.Cb,88)&&eko(Zd(Pp(e.Cb,88)),4),er3(e,n),e.f=o,elY(e,s),elU(e,u),elF(e,c),elB(e,l),els(e,f),elZ(e,d),eli(e,!0),end(e,i),e.ok(a),eu2(e,t),null!=r&&(e.i=null,erA(e,r))}function eSQ(e){var t,n;if(!e.f)return e.n>0;for(;e.n>0;){if(M4(n=(t=Pp(e.k.Xb(e.n-1),72)).ak(),99)&&(Pp(n,18).Bb&eZ1)!=0&&(!e.e||n.Gj()!=e6d||0!=n.aj())&&null!=t.dd())return!0;--e.n}return!1}function eS1(e,t,n){if(e<0)return eCG(eUh,eow(vx(e1R,1),eUp,1,5,[n,ell(e)]));if(!(t<0))return eCG("%s (%s) must not be greater than size (%s)",eow(vx(e1R,1),eUp,1,5,[n,ell(e),ell(t)]));throw p7(new gL(eUb+t))}function eS0(e,t,n,r,i,a){var o,s,u,c;if((o=r-n)<7){efA(t,n,r,a);return}if(c=(u=n+i)+((s=r+i)-u>>1),eS0(t,e,u,c,-i,a),eS0(t,e,c,s,-i,a),0>=a.ue(e[c-1],e[c])){for(;n=0?e.sh(a,n):eOh(e,i,n);else throw p7(new gL(eZV+i.ne()+eZq))}else throw p7(new gL(eZJ+t+eZQ))}else efL(e,r,i,n)}function eS6(e){var t,n,r,i;if(n=Pp(e,49).qh())try{if(r=null,(t=eMC((_Q(),tgp),eDv(efR(n))))&&(i=t.rh())&&(r=i.Wk(gF(n.e))),r&&r!=e)return eS6(r)}catch(a){if(a=eoa(a),!M4(a,60))throw p7(a)}return e}function eS9(e,t,n){var r,i,a,o;if(o=null==t?0:e.b.se(t),0==(i=null==(r=e.a.get(o))?[]:r).length)e.a.set(o,i);else if(a=euj(e,t,i))return a.ed(n);return Bc(i,i.length,new EE(t,n)),++e.c,$c(e.b),null}function eS8(e,t){var n,r;return Kx(e.a),Yb(e.a,(erZ(),tcq),tcq),Yb(e.a,tcZ,tcZ),r=new K2,RI(r,tcZ,(efx(),tc1)),xc(eT8(t,(egj(),tlf)))!==xc((eub(),tc5))&&RI(r,tcZ,tcJ),RI(r,tcZ,tcQ),Tb(e.a,r),n=eRq(e.a,t)}function eS7(e){if(!e)return g3(),e0M;var t=e.valueOf?e.valueOf():e;if(t!==e){var n=e0O[typeof t];return n?n(t):euV(typeof t)}return e instanceof Array||e instanceof eB4.Array?new lL(e):new lD(e)}function eke(e,t,n){var r,i,a;switch(a=e.o,(i=(r=Pp(UA(e.p,n),244)).i).b=ek0(r),i.a=ek1(r),i.b=eB4.Math.max(i.b,a.a),i.b>a.a&&!t&&(i.b=a.a),i.c=-(i.b-a.a)/2,n.g){case 1:i.d=-i.a;break;case 3:i.d=a.b}eNE(r),eNM(r)}function ekt(e,t,n){var r,i,a;switch(a=e.o,(i=(r=Pp(UA(e.p,n),244)).i).b=ek0(r),i.a=ek1(r),i.a=eB4.Math.max(i.a,a.b),i.a>a.b&&!t&&(i.a=a.b),i.d=-(i.a-a.b)/2,n.g){case 4:i.c=-i.b;break;case 2:i.c=a.a}eNE(r),eNM(r)}function ekn(e,t){var n,r,i,a,o;if(!t.dc()){if(i=Pp(t.Xb(0),128),1==t.gc()){eA1(e,i,i,1,0,t);return}for(n=1;n0)try{i=eDa(t,eHt,eUu)}catch(a){if(a=eoa(a),M4(a,127))throw r=a,p7(new QH(r));throw p7(a)}return i<(n=(e.a||(e.a=new pK(e)),e.a)).i&&i>=0?Pp(etj(n,i),56):null}function eku(e,t){if(e<0)return eCG(eUh,eow(vx(e1R,1),eUp,1,5,["index",ell(e)]));if(!(t<0))return eCG("%s (%s) must be less than size (%s)",eow(vx(e1R,1),eUp,1,5,["index",ell(e),ell(t)]));throw p7(new gL(eUb+t))}function ekc(e){var t,n,r,i,a;if(null==e)return eUg;for(r=0,a=new eaP(eUd,"[","]"),i=(n=e).length;re.a.ue(RJ(e.b,o),RJ(e.b,a))&&(s=o),s),!(0>e.a.ue(i,RJ(e.b,r))));)q1(e.b,t,RJ(e.b,r)),t=r;q1(e.b,t,i)}function ekp(e,t,n,r,i,a){var o,s,u,c,l;for(xc(e)===xc(n)&&(e=e.slice(t,t+i),t=0),u=n,s=t,c=t+i;s0)for(o=e.c.d,s=e.d.d,i=Ol(C6(new kl(s.a,s.b),o),1/(r+1)),a=new kl(o.a,o.b),n=new fz(e.a);n.a=0?e._g(n,!0,!0):exk(e,i,!0),153),Pp(r,215).ol(t);else throw p7(new gL(eZV+t.ne()+eZq))}function ekP(e){var t,n;return e>-140737488355328&&e<0x800000000000?0==e?0:((t=e<0)&&(e=-e),n=zy(eB4.Math.floor(eB4.Math.log(e)/.6931471805599453)),(!t||e!=eB4.Math.pow(2,n))&&++n,n):eaJ(eap(e))}function ekR(e){var t,n,r,i,a,o,s;for(a=new Tw,n=new fz(e);n.a2&&s.e.b+s.j.b<=2&&(i=s,r=o),a.a.zc(i,a),i.q=r);return a}function ekj(e,t){var n,r,i;return r=new eb$(e),eaW(r,t),eo3(r,(eBU(),ttQ),t),eo3(r,(eBy(),tol),(ewf(),tbo)),eo3(r,tiq,(ebx(),tdA)),lK(r,(eEn(),e8C)),n=new eES,Gc(n,r),ekv(n,(eYu(),tbY)),i=new eES,Gc(i,r),ekv(i,tby),r}function ekF(e){switch(e.g){case 0:return new gx((enU(),tur));case 1:return new cC;case 2:return new cF;default:throw p7(new gL("No implementation is available for the crossing minimizer "+(null!=e.f?e.f:""+e.g)))}}function ekY(e,t){var n,r,i,a,o;for(e.c[t.p]=!0,P_(e.a,t),o=new fz(t.j);o.a=(a=o.gc()))o.$b();else for(r=0,i=o.Kc();r0?g5():o<0&&ekJ(e,t,-o),!0)}function ek1(e){var t,n,r,i,a,o,s;if(s=0,0==e.b){for(i=0,o=eb4(e,!0),t=0,a=(r=o).length;i0&&(s+=n,++t);t>1&&(s+=e.c*(t-1))}else s=vy(eib(U1(UJ(Yw(e.a),new eS),new ek)));return s>0?s+e.n.d+e.n.a:0}function ek0(e){var t,n,r,i,a,o,s;if(s=0,0==e.b)s=vy(eib(U1(UJ(Yw(e.a),new e_),new eE)));else{for(i=0,o=eb5(e,!0),t=0,a=(r=o).length;i0&&(s+=n,++t);t>1&&(s+=e.c*(t-1))}return s>0?s+e.n.b+e.n.c:0}function ek2(e,t){var n,r,i,a;for(n=(a=Pp(UA(e.b,t),124)).a,i=Pp(Pp(Zq(e.r,t),21),84).Kc();i.Ob();)(r=Pp(i.Pb(),111)).c&&(n.a=eB4.Math.max(n.a,Rd(r.c)));if(n.a>0)switch(t.g){case 2:a.n.c=e.s;break;case 4:a.n.b=e.s}}function ek3(e,t){var n,r,i;return 0==(n=Pp(e_k(t,(eCk(),e9M)),19).a-Pp(e_k(e,e9M),19).a)?(r=C6(MB(Pp(e_k(e,(erV(),e9P)),8)),Pp(e_k(e,e9R),8)),i=C6(MB(Pp(e_k(t,e9P),8)),Pp(e_k(t,e9R),8)),elN(r.a*r.b,i.a*i.b)):n}function ek4(e,t){var n,r,i;return 0==(n=Pp(e_k(t,(eTj(),tcD)),19).a-Pp(e_k(e,tcD),19).a)?(r=C6(MB(Pp(e_k(e,(eR6(),tce)),8)),Pp(e_k(e,tct),8)),i=C6(MB(Pp(e_k(t,tce),8)),Pp(e_k(t,tct),8)),elN(r.a*r.b,i.a*i.b)):n}function ek5(e){var t,n;return n=new vc,n.a+="e_",null!=(t=eaZ(e))&&(n.a+=""+t),e.c&&e.d&&(xM((n.a+=" ",n),egu(e.c)),xM(xT((n.a+="[",n),e.c.i),"]"),xM((n.a+=eGH,n),egu(e.d)),xM(xT((n.a+="[",n),e.d.i),"]")),n.a}function ek6(e){switch(e.g){case 0:return new cD;case 1:return new cN;case 2:return new cI;case 3:return new cP;default:throw p7(new gL("No implementation is available for the layout phase "+(null!=e.f?e.f:""+e.g)))}}function ek9(e,t,n,r,i){var a;switch(a=0,i.g){case 1:a=eB4.Math.max(0,t.b+e.b-(n.b+r));break;case 3:a=eB4.Math.max(0,-e.b-r);break;case 2:a=eB4.Math.max(0,-e.a-r);break;case 4:a=eB4.Math.max(0,t.a+e.a-(n.a+r))}return a}function ek8(e,t,n){var r,i,a,o,s;if(n)for(i=n.a.length,s=((r=new Fs(i)).b-r.a)*r.c<0?(_9(),eB3):new OR(r);s.Ob();)eXh in(a=KZ(n,(o=Pp(s.Pb(),19)).a)).a||eXp in a.a?eId(e,a,t):eBe(e,a,t),Om(Pp(Bp(e.b,ehM(a)),79))}function ek7(e){var t,n;switch(e.b){case -1:return!0;case 0:if((n=e.t)>1||-1==n||(t=evl(e))&&(_4(),t.Cj()==eJK))return e.b=-1,!0;return e.b=1,!1;default:return!1}}function exe(e,t){var n,r,i,a,o;for(i=0,r=(t.s||(t.s=new FQ(tm6,t,21,17)),t.s),a=null,o=r.i;i=0&&r=0?e._g(n,!0,!0):exk(e,i,!0),153),Pp(r,215).ll(t);throw p7(new gL(eZV+t.ne()+eZX))}function exc(){var e;return(_6(),tg9)?Pp(eMC((_Q(),tgp),eQc),1939):(x2(e1$,new ut),ej9(),e=Pp(M4(zg((_Q(),tgp),eQc),547)?zg(tgp,eQc):new Uh,547),tg9=!0,eBY(e),eB0(e),Um((_1(),tgm),e,new sM),Ge(tgp,eQc,e),e)}function exl(e,t){var n,r,i,a;e.j=-1,TO(e.e)?(n=e.i,a=0!=e.i,Zz(e,t),r=new Q$(e.e,3,e.c,null,t,n,a),i=t.Qk(e.e,e.c,null),(i=ey1(e,t,i))?(i.Ei(r),i.Fi()):eam(e.e,r)):(Zz(e,t),(i=t.Qk(e.e,e.c,null))&&i.Fi())}function exf(e,t){var n,r,i;if(i=0,(r=t[0])>=e.length)return -1;for(n=(GV(r,e.length),e.charCodeAt(r));n>=48&&n<=57&&(i=10*i+(n-48),!(++r>=e.length));)n=(GV(r,e.length),e.charCodeAt(r));return r>t[0]?t[0]=r:i=-1,i}function exd(e){var t,n,r,i,a;return i=Pp(e.a,19).a,a=Pp(e.b,19).a,n=i,r=a,t=eB4.Math.max(eB4.Math.abs(i),eB4.Math.abs(a)),i<=0&&i==a?(n=0,r=a-1):i==-t&&a!=t?(n=a,r=i,a>=0&&++n):(n=-a,r=i),new kD(ell(n),ell(r))}function exh(e,t,n,r){var i,a,o,s,u,c;for(i=0;i=0&&c>=0&&u=e.i)throw p7(new gE(eXU+t+eXH+e.i));if(n>=e.i)throw p7(new gE(eX$+n+eXH+e.i));return r=e.g[n],t!=n&&(t>16))>>16&16),e>>=t,n+=t=(r=e-256)>>16&8,e<<=t,n+=t=(r=e-eH0)>>16&4,e<<=t,n+=t=(r=e-eUR)>>16&2,e<<=t,n+2-(t=(r=e>>14)&~(r>>1)))}function exy(e){var t,n,r,i;for(HR(),e9n=new p0,e9t=new p2,e9e=new p0,t=(e.a||(e.a=new FQ(e6k,e,10,11)),e.a),eYE(t),i=new Ow(t);i.e!=i.i.gc();)r=Pp(epH(i),33),-1==QI(e9n,r,0)&&(n=new p0,P_(e9e,n),epi(r,n));return e9e}function exw(e,t,n){var r,i,a,o;e.a=n.b.d,M4(t,352)?(i=eLO(Pp(t,79),!1,!1),a=eEF(i),qX(a,r=new d_(e)),eNI(a,i),null!=t.We((eBB(),thg))&&qX(Pp(t.We(thg),74),r)):((o=Pp(t,470)).Hg(o.Dg()+e.a.a),o.Ig(o.Eg()+e.a.b))}function ex_(e,t){var n,r,i,a,o,s,u,c;for(s=1,c=gP(LV(e_k(t,(eBy(),toH)))),u=e[0].n.a+e[0].o.a+e[0].d.c+c;s=0)?n:(s=B$(C6(new kl(o.c+o.b/2,o.d+o.a/2),new kl(a.c+a.b/2,a.d+a.a/2))),-(eDz(a,o)-1)*s)}function exS(e,t,n){var r;_r(new R1(null,(n.a||(n.a=new FQ(e6v,n,6,6)),new Gq(n.a,16))),new kC(e,t)),_r(new R1(null,(n.n||(n.n=new FQ(e6S,n,1,7)),new Gq(n.n,16))),new kI(e,t)),(r=Pp(eT8(n,(eBB(),thg)),74))&&eil(r,e,t)}function exk(e,t,n){var r,i,a;if(a=eR3((eSp(),tvc),e.Tg(),t))return _4(),Pp(a,66).Oj()||(a=Wk(QZ(tvc,a))),i=Pp((r=e.Yg(a))>=0?e._g(r,!0,!0):exk(e,a,!0),153),Pp(i,215).hl(t,n);throw p7(new gL(eZV+t.ne()+eZX))}function exx(e,t,n,r){var i,a,o,s,u;if(i=e.d[t]){if(a=i.g,u=i.i,null!=r){for(s=0;s=n&&(r=t,o=(c=(u.c+u.a)/2)-n,u.c<=c-n&&(i=new N4(u.c,o),jO(e,r++,i)),(s=c+n)<=u.a&&(a=new N4(s,u.a),Gp(r,e.c.length),Ew(e.c,r,a)))}function exI(e){var t;if(e.c||null!=e.g){if(null==e.g)return!0;if(0==e.i)return!1;t=Pp(e.g[e.i-1],47)}else e.d=e.si(e.f),JL(e,e.d),t=e.d;return t==e.b&&null.km>=null.jm()?(eM5(e),exI(e)):t.Ob()}function exD(e,t,n){var r,i,a,o,s;if((s=n)||(s=P6(new mV,0)),ewG(s,eGA,1),ejY(e.c,t),1==(o=ejz(e.a,t)).gc())eRd(Pp(o.Xb(0),37),s);else for(a=1/o.gc(),i=o.Kc();i.Ob();)eRd(r=Pp(i.Pb(),37),eiI(s,a));vi(e.a,o,t),eL7(t),eEj(s)}function exN(e){if(this.a=e,e.c.i.k==(eEn(),e8C))this.c=e.c,this.d=Pp(e_k(e.c.i,(eBU(),tt1)),61);else if(e.d.i.k==e8C)this.c=e.d,this.d=Pp(e_k(e.d.i,(eBU(),tt1)),61);else throw p7(new gL("Edge "+e+" is not an external edge."))}function exP(e,t){var n,r,i;i=e.b,e.b=t,(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,3,i,e.b)),t?t!=e&&(er3(e,t.zb),enf(e,t.d),erc(e,null==(n=null==(r=t.c)?t.zb:r)||IE(n,t.zb)?null:n)):(er3(e,null),enf(e,0),erc(e,null))}function exR(e){var t,n;if(!e.f)return e.n=(o=null==(n=Pp(eaS(e.a,4),126))?0:n.length))throw p7(new Ii(t,o));return i=n[t],1==o?r=null:(r=Je(e6N,eJM,415,o-1,0,1),ePD(n,0,r,0,t),(a=o-t-1)>0&&ePD(n,t+1,r,t,a)),eps(e,r),eSU(e,t,i),i}function ex$(){ex$=A,tvw=Pp(etj(H9((yL(),tvS).qb),6),34),tvg=Pp(etj(H9(tvS.qb),3),34),tvv=Pp(etj(H9(tvS.qb),4),34),tvy=Pp(etj(H9(tvS.qb),5),18),eyD(tvw),eyD(tvg),eyD(tvv),eyD(tvy),tv_=new g$(eow(vx(tm6,1),eJ4,170,0,[tvw,tvg]))}function exz(e,t){var n;this.d=new mh,this.b=t,this.e=new TS(t.qf()),n=e.u.Hc((ekU(),tbb)),e.u.Hc(tbp)?e.D?this.a=n&&!t.If():this.a=!0:e.u.Hc(tbm)&&n?this.a=!(t.zf().Kc().Ob()||t.Bf().Kc().Ob()):this.a=!1}function exG(e,t){var n,r,i,a;for(n=e.o.a,a=Pp(Pp(Zq(e.r,t),21),84).Kc();a.Ob();)(i=Pp(a.Pb(),111)).e.a=(r=i.b).Xe((eBB(),thK))?r.Hf()==(eYu(),tbY)?-r.rf().a-gP(LV(r.We(thK))):n+gP(LV(r.We(thK))):r.Hf()==(eYu(),tbY)?-r.rf().a:n}function exW(e,t){var n,r,i,a;n=Pp(e_k(e,(eBy(),tal)),103),a=Pp(eT8(t,tob),61),(i=Pp(e_k(e,tol),98))!=(ewf(),tbc)&&i!=tbl?a==(eYu(),tbF)&&(a=eNh(t,n))==tbF&&(a=ef9(n)):a=(r=eRl(t))>0?ef9(n):elC(ef9(n)),ebu(t,tob,a)}function exK(e,t){var n,r,i,a,o;for(o=e.j,t.a!=t.b&&Mv(o,new ia),i=o.c.length/2|0,r=0;r0&&eIl(e,n,t),a):null!=r.a?(eIl(e,t,n),-1):null!=i.a?(eIl(e,n,t),1):0}function exq(e,t){var n,r,i,a;e.ej()?(n=e.Vi(),a=e.fj(),++e.j,e.Hi(n,e.oi(n,t)),r=e.Zi(3,null,t,n,a),e.bj()&&(i=e.cj(t,null))?(i.Ei(r),i.Fi()):e.$i(r)):(BD(e,t),e.bj()&&(i=e.cj(t,null))&&i.Fi())}function exZ(e,t){var n,r,i,a,o;for(o=eAY(e.e.Tg(),t),i=new o7,n=Pp(e.g,119),a=e.i;--a>=0;)r=n[a],o.rl(r.ak())&&JL(i,r);!eYK(e,i)&&TO(e.e)&&bz(e,t.$j()?$N(e,6,t,(Hj(),e2r),null,-1,!1):$N(e,t.Kj()?2:1,t,null,null,-1,!1))}function exX(){var e,t;for(t=0,exX=A,e2t=Je(e0t,eUP,91,32,0,1),e2n=Je(e0t,eUP,91,32,0,1),e=1;t<=18;t++)e2t[t]=ep_(e),e2n[t]=ep_(Fg(e,t)),e=efn(e,5);for(;to)))&&(!t.q||(o=(r=t.C).c.c.a-r.o.a/2,!((i=r.n.a-n)>o))))}function exQ(e,t){var n;ewG(t,"Partition preprocessing",1),n=Pp(qE(UJ(eeh(UJ(new R1(null,new Gq(e.a,16)),new nZ),new nX),new nJ),JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[(eum(),e2U)]))),15),_r(n.Oc(),new nQ),eEj(t)}function ex1(e){var t,n,r,i,a,o,s;for(Gk(),n=new qh,i=new fz(e.e.b);i.a1?e.e*=gP(e.a):e.f/=gP(e.a),eu0(e),ehK(e),eCj(e),eo3(e.b,(epz(),e62),e.g)}function ex9(e,t,n){var r,i,a,o,s,u;for(r=0,u=n,t||(r=n*(e.c.length-1),u*=-1),a=new fz(e);a.a=0?(!t&&(t=new vu,r>0&&xk(t,e.substr(0,r))),t.a+="\\",Bf(t,n&eHd)):t&&Bf(t,n&eHd);return t?t.a:e}function eTh(e){var t;if(!e.a)throw p7(new gC("IDataType class expected for layout option "+e.f));if(null==(t=VN(e.a)))throw p7(new gC("Couldn't create new instance of property '"+e.f+"'. "+eq4+(LW(e6D),e6D.k)+eq5));return Pp(t,414)}function eTp(e){var t,n,r,i,a;return(a=e.eh())&&a.kh()&&(i=ecv(e,a))!=a?(n=e.Vg(),r=(t=e.Vg())>=0?e.Qg(null):e.eh().ih(e,-1-t,null,null),e.Rg(Pp(i,49),n),r&&r.Fi(),e.Lg()&&e.Mg()&&n>-1&&eam(e,new FX(e,9,n,a,i)),i):a}function eTb(e){var t,n,r,i,a,o,s,u;for(r=0,o=0,a=e.f.e;r>5)>=e.d)return e.e<0;if(n=e.a[i],t=1<<(31&t),e.e<0){if(i<(r=eiU(e)))return!1;n=r==i?-n:~n}return(n&t)!=0}function eT_(e,t,n,r){var i;Pp(n.b,65),Pp(n.b,65),Pp(r.b,65),Pp(r.b,65),P9(i=C6(MB(Pp(n.b,65).c),Pp(r.b,65).c),ekg(Pp(n.b,65),Pp(r.b,65),i)),Pp(r.b,65),Pp(r.b,65),Pp(r.b,65).c.a,i.a,Pp(r.b,65).c.b,i.b,Pp(r.b,65),ety(r.a,new N9(e,t,r))}function eTE(e,t){var n,r,i,a,o,s,u;if(a=t.e){for(o=0,n=eTp(a),r=Pp(e.g,674);o>16)),15).Xc(a))0&&(Tk(e.a.c)&&t.n.d||Tx(e.a.c)&&t.n.b||(t.g.d+=eB4.Math.max(0,r/2-.5)),Tk(e.a.c)&&t.n.a||Tx(e.a.c)&&t.n.c||(t.g.a-=r-1))}function eTO(e){var t,n,r,i,a;if(i=new p0,a=eDC(e,i),t=Pp(e_k(e,(eBU(),tng)),10))for(r=new fz(t.j);r.a>t,a=e.m>>t|n<<22-t,i=e.l>>t|e.m<<22-t):t<44?(o=r?eH$:0,a=n>>t-22,i=e.m>>t-22|n<<44-t):(o=r?eH$:0,a=r?eHH:0,i=n>>t-44),Mk(i&eHH,a&eHH,o&eH$)}function eTI(e){var t,n,r,i,a,o;for(this.c=new p0,this.d=e,r=eHQ,i=eHQ,t=eH1,n=eH1,o=epL(e,0);o.b!=o.d.c;)a=Pp(Vv(o),8),r=eB4.Math.min(r,a.a),i=eB4.Math.min(i,a.b),t=eB4.Math.max(t,a.a),n=eB4.Math.max(n,a.b);this.a=new Hr(r,i,t-r,n-i)}function eTD(e,t){var n,r,i,a,o,s;for(a=new fz(e.b);a.a0&&M4(t,42)&&(e.a.qj(),a=null==(u=(c=Pp(t,42)).cd())?0:esj(u),o=Cb(e.a,a),n=e.a.d[o])){for(s=0,r=Pp(n.g,367),l=n.i;s=2)for(t=LV((n=i.Kc()).Pb());n.Ob();)a=t,t=LV(n.Pb()),r=eB4.Math.min(r,(BJ(t),t-(BJ(a),a)));return r}function eTX(e,t){var n,r,i,a,o;qQ(r=new _n,t,r.c.b,r.c);do for(n=(A6(0!=r.b),Pp(etw(r,r.a.a),86)),e.b[n.g]=1,a=epL(n.d,0);a.b!=a.d.c;)o=(i=Pp(Vv(a),188)).c,1==e.b[o.g]?P7(e.a,i):2==e.b[o.g]?e.b[o.g]=1:qQ(r,o,r.c.b,r.c);while(0!=r.b)}function eTJ(e,t){var n,r,i;if(xc(t)===xc(Y9(e)))return!0;if(!M4(t,15)||(r=Pp(t,15),(i=e.gc())!=r.gc()))return!1;if(!M4(r,54))return eb3(e.Kc(),r.Kc());for(n=0;n0&&(i=n),o=new fz(e.f.e);o.a0?(t-=1,n-=1):r>=0&&i<0?(t+=1,n+=1):r>0&&i>=0?(t-=1,n+=1):(t+=1,n-=1),new kD(ell(t),ell(n))}function eMf(e,t){if(e.ct.c)return 1;if(e.bt.b)return 1;if(e.a!=t.a)return esj(e.a)-esj(t.a);else if(e.d==(qG(),tuf)&&t.d==tul)return -1;else if(e.d==tul&&t.d==tuf)return 1;return 0}function eMd(e,t){var n,r,i,a,o;return(o=(a=t.a).c.i==t.b?a.d:a.c,r=a.c.i==t.b?a.c:a.d,(i=edI(e.a,o,r))>0&&i0):i<0&&-i0)}function eMh(e,t,n,r){var i,a,o,s,u,c,l,f;for(i=(t-e.d)/e.c.c.length,a=0,e.a+=n,e.d=t,f=new fz(e.c);f.a>24;return o}function eMb(e){if(e.pe()){var t=e.c;t.qe()?e.o="["+t.n:t.pe()?e.o="["+t.ne():e.o="[L"+t.ne()+";",e.b=t.me()+"[]",e.k=t.oe()+"[]";return}var n=e.j,r=e.d;r=r.split("/"),e.o=ehg(".",[n,ehg("$",r)]),e.b=ehg(".",[n,ehg(".",r)]),e.k=r[r.length-1]}function eMm(e,t){var n,r,i,a,o;for(o=null,a=new fz(e.e.a);a.a=0;t-=2)for(n=0;n<=t;n+=2)(e.b[n]>e.b[n+2]||e.b[n]===e.b[n+2]&&e.b[n+1]>e.b[n+3])&&(r=e.b[n+2],e.b[n+2]=e.b[n],e.b[n]=r,r=e.b[n+3],e.b[n+3]=e.b[n+1],e.b[n+1]=r);e.c=!0}}function eMk(e,t){var n,r,i,a,o,s,u,c;for(a=(o=1==t?e8c:e8u).a.ec().Kc();a.Ob();)for(i=Pp(a.Pb(),103),u=Pp(Zq(e.f.c,i),21).Kc();u.Ob();)switch(s=Pp(u.Pb(),46),r=Pp(s.b,81),n=(c=Pp(s.a,189)).c,i.g){case 2:case 1:r.g.d+=n;break;case 4:case 3:r.g.c+=n}}function eMx(e,t){var n,r,i,a,o,s,u,c,l;for(s=0,c=-1,l=0,u=(o=e).length;s0&&++l;++c}return l}function eMT(e){var t,n;return n=new O0(yx(e.gm)),n.a+="@",xM(n,(t=esj(e)>>>0).toString(16)),e.kh()?(n.a+=" (eProxyURI: ",xT(n,e.qh()),e.$g()&&(n.a+=" eClass: ",xT(n,e.$g())),n.a+=")"):e.$g()&&(n.a+=" (eClass: ",xT(n,e.$g()),n.a+=")"),n.a}function eMM(e){var t,n,r,i;if(e.e)throw p7(new gC((LW(e2J),e$j+e2J.k+e$F)));for(e.d==(ec3(),tpv)&&eF_(e,tpm),n=new fz(e.a.a);n.a>24}return n}function eMD(e,t,n){var r,i,a;if(!(i=Pp(UA(e.i,t),306))){if(i=new etr(e.d,t,n),jT(e.i,t,i),ehj(t))Od(e.a,t.c,t.b,i);else switch(a=eSv(t),r=Pp(UA(e.p,a),244),a.g){case 1:case 3:i.j=!0,gh(r,t.b,i);break;case 4:case 2:i.k=!0,gh(r,t.c,i)}}return i}function eMN(e,t,n,r){var i,a,o,s,u,c;if(s=new o7,u=eAY(e.e.Tg(),t),i=Pp(e.g,119),_4(),Pp(t,66).Oj())for(o=0;o=0)return i;for(a=1,s=new fz(t.j);s.a0&&t.ue((GK(i-1,e.c.length),Pp(e.c[i-1],10)),a)>0;)q1(e,i,(GK(i-1,e.c.length),Pp(e.c[i-1],10))),--i;GK(i,e.c.length),e.c[i]=a}n.a=new p2,n.b=new p2}function eMj(e,t,n){var r,i,a,o,s,u,c,l;for(o=0,l=(r=Pp(t.e&&t.e(),9),new I1(r,Pp(CY(r,r.length),9),0)),s=(a=u=eIk(n,"[\\[\\]\\s,]+")).length;o0&&(Tk(e.a.c)&&t.n.d||Tx(e.a.c)&&t.n.b||(t.g.d-=eB4.Math.max(0,r/2-.5)),Tk(e.a.c)&&t.n.a||Tx(e.a.c)&&t.n.c||(t.g.a+=eB4.Math.max(0,r-1)))}function eMY(e,t,n){var r,i;if((e.c-e.b&e.a.length-1)==2)t==(eYu(),tbw)||t==tby?(etf(Pp(eso(e),15),(egF(),tpV)),etf(Pp(eso(e),15),tpq)):(etf(Pp(eso(e),15),(egF(),tpq)),etf(Pp(eso(e),15),tpV));else for(i=new UN(e);i.a!=i.b;)etf(r=Pp(ecn(i),15),n)}function eMB(e,t){var n,r,i,a,o,s,u;for(i=Pb(new pL(e)),s=new KB(i,i.c.length),a=Pb(new pL(t)),u=new KB(a,a.c.length),o=null;s.b>0&&u.b>0;)if((n=(A6(s.b>0),Pp(s.a.Xb(s.c=--s.b),33)))==(r=(A6(u.b>0),Pp(u.a.Xb(u.c=--u.b),33))))o=n;else break;return o}function eMU(e,t){var n,r,i,a,o,s;return(a=e.a*e$d+1502*e.b,s=e.b*e$d+11,a+=n=eB4.Math.floor(s*e$h),s-=n*e$p,a%=e$p,e.a=a,e.b=s,t<=24)?eB4.Math.floor(e.a*e2v[t]):((r=(i=e.a*(1<=2147483648&&(r-=eH7),r)}function eMH(e,t,n){var r,i,a,o;WY(e,t)>WY(e,n)?(r=efr(n,(eYu(),tby)),e.d=r.dc()?0:Rk(Pp(r.Xb(0),11)),o=efr(t,tbY),e.b=o.dc()?0:Rk(Pp(o.Xb(0),11))):(i=efr(n,(eYu(),tbY)),e.d=i.dc()?0:Rk(Pp(i.Xb(0),11)),a=efr(t,tby),e.b=a.dc()?0:Rk(Pp(a.Xb(0),11)))}function eM$(e){var t,n,r,i,a,o,s;if(e&&(t=e.Hh(eQc))&&null!=(o=Lq(edW((t.b||(t.b=new L_((eBK(),tgF),tgf,t)),t.b),"conversionDelegates")))){for(s=new p0,r=eIk(o,"\\w+"),i=0,a=r.length;ie.c);o++)i.a>=e.s&&(a<0&&(a=o),s=o);return u=(e.s+e.c)/2,a>=0&&(r=eIe(e,t,a,s),u=_V((GK(r,t.c.length),Pp(t.c[r],329))),exC(t,r,n)),u}function eMK(){eMK=A,tlK=new T2((eBB(),td2),1.3),tlX=thc,tfe=new T3(15),tl7=new T2(thN,tfe),tfr=new T2(tpl,15),tlV=td9,tl3=thx,tl4=thO,tl5=thL,tl2=thS,tl6=thD,tft=thJ,tl8=(eTU(),tl$),tl0=tlU,tl9=tlH,tfn=tlG,tlJ=tlB,tlQ=thb,tl1=thm,tlZ=tlY,tlq=tlF,tfi=tlW}function eMV(e,t,n){var r,i,a,o,s,u,c;for(erl(o=a=new sa,(BJ(t),t)),c=(o.b||(o.b=new L_((eBK(),tgF),tgf,o)),o.b),u=1;u0&&eRJ(this,i)}function eMZ(e,t,n,r,i,a){var o,s,u;if(!i[t.b]){for(i[t.b]=!0,(o=r)||(o=new Z5),P_(o.e,t),u=a[t.b].Kc();u.Ob();)(s=Pp(u.Pb(),282)).d!=n&&s.c!=n&&(s.c!=t&&eMZ(e,s.c,t,o,i,a),s.d!=t&&eMZ(e,s.d,t,o,i,a),P_(o.c,s),eoc(o.d,s.b));return o}return null}function eMX(e){var t,n,r,i,a,o,s;for(t=0,i=new fz(e.e);i.a=2}function eMJ(e,t){var n,r,i,a;for(ewG(t,"Self-Loop pre-processing",1),r=new fz(e.a);r.a1)&&(t=jL(tp1,eow(vx(e6t,1),eU4,93,0,[tpQ,tp2])),!(eaC(z_(t,e))>1)&&(r=jL(tp9,eow(vx(e6t,1),eU4,93,0,[tp6,tp5])),!(eaC(z_(r,e))>1)))}function eM0(e,t){var n,r,i;return(n=t.Hh(e.a))&&null!=(i=Lq(edW((n.b||(n.b=new L_((eBK(),tgF),tgf,n)),n.b),"affiliation")))?-1==(r=O8(i,e_n(35)))?elp(e,Fr(e,etP(t.Hj())),i):0==r?elp(e,null,i.substr(1)):elp(e,i.substr(0,r),i.substr(r+1)):null}function eM2(e){var t,n,r;try{return null==e?eUg:efF(e)}catch(i){if(i=eoa(i),M4(i,102))return t=i,r=yx(esF(e))+"@"+(n=(wK(),ebh(e)>>>0)).toString(16),epa(eob(),(_g(),"Exception during lenientFormat for "+r),t),"<"+r+" threw "+yx(t.gm)+">";throw p7(i)}}function eM3(e){switch(e.g){case 0:return new ck;case 1:return new cy;case 2:return new _j;case 3:return new i$;case 4:return new CZ;case 5:return new cx;default:throw p7(new gL("No implementation is available for the layerer "+(null!=e.f?e.f:""+e.g)))}}function eM4(e,t,n){var r,i,a;for(a=new fz(e.t);a.a0&&(r.b.n-=r.c,r.b.n<=0&&r.b.u>0&&P7(t,r.b));for(i=new fz(e.i);i.a0&&(r.a.u-=r.c,r.a.u<=0&&r.a.n>0&&P7(n,r.a))}function eM5(e){var t,n,r,i,a;if(null==e.g&&(e.d=e.si(e.f),JL(e,e.d),e.c))return e.f;if(i=(t=Pp(e.g[e.i-1],47)).Pb(),e.e=t,(n=e.si(i)).Ob())e.d=n,JL(e,n);else for(e.d=null;!t.Ob()&&(Bc(e.g,--e.i,null),0!=e.i);)t=r=Pp(e.g[e.i-1],47);return i}function eM6(e,t){var n,r,i,a,o,s;if(i=(r=t).ak(),eLt(e.e,i)){if(i.hi()&&Vq(e,i,r.dd()))return!1}else for(a=0,s=eAY(e.e.Tg(),i),n=Pp(e.g,119);a1||n>1)return 2;return t+n==1?2:0}function eOs(e,t,n){var r,i,a,o,s;for(ewG(n,"ELK Force",1),gN(LK(eT8(t,(eCk(),e9E))))||zh(r=new df((_q(),new gM(t)))),s=eo4(t),evn(s),esO(e,Pp(e_k(s,e9v),424)),a=(o=eNx(e.a,s)).Kc();a.Ob();)i=Pp(a.Pb(),231),eIL(e.b,i,eiI(n,1/o.gc()));s=eYC(o),eYh(s),eEj(n)}function eOu(e,t){var n,r,i,a,o;if(ewG(t,"Breaking Point Processor",1),eFM(e),gN(LK(e_k(e,(eBy(),toJ))))){for(i=new fz(e.b);i.a=0?e._g(r,!0,!0):exk(e,a,!0),153),Pp(i,215).ml(t,n)}else throw p7(new gL(eZV+t.ne()+eZq))}function eOp(e,t){var n,r,i,a,o;for(r=1,n=new p0,i=eeh(new R1(null,new Gq(e,16)),new aM),a=eeh(new R1(null,new Gq(e,16)),new aO),o=QN(Xg(U1(eAa(eow(vx(e2C,1),eUp,833,0,[i,a])),new aA)));r=2*t&&P_(n,new N4(o[r-1]+t,o[r]-t));return n}function eOb(e,t,n){ewG(n,"Eades radial",1),n.n&&t&&WG(n,KS(t),(eup(),tmr)),e.d=Pp(eT8(t,(Lj(),tcV)),33),e.c=gP(LV(eT8(t,(egj(),tl_)))),e.e=ebN(Pp(eT8(t,tlE),293)),e.a=ef7(Pp(eT8(t,tlk),426)),e.b=eyp(Pp(eT8(t,tlg),340)),evY(e),n.n&&t&&WG(n,KS(t),(eup(),tmr))}function eOm(e,t,n){var r,i,a,o,s,u,c,l;if(n)for(a=n.a.length,s=((r=new Fs(a)).b-r.a)*r.c<0?(_9(),eB3):new OR(r);s.Ob();)(i=KZ(n,(o=Pp(s.Pb(),19)).a))&&(eB8=null,u=Vj(e,(c=(yT(),l=new mk),t&&eOL(c,t),c),i),ert(u,KJ(i,eXS)),ewU(i,u),eka(i,u),esv(e,i,u))}function eOg(e){var t,n,r,i,a,o;if(!e.j){if(o=new sd,null==(a=(t=tgz).a.zc(e,t))){for(r=new Ow($E(e));r.e!=r.i.gc();)n=Pp(epH(r),26),i=eOg(n),Y4(o,i),JL(o,n);t.a.Bc(e)}euI(o),e.j=new xQ((Pp(etj(H9((BM(),tgv).o),11),18),o.i),o.g),Zd(e).b&=-33}return e.j}function eOv(e){var t,n,r,i;if(null==e)return null;if(r=ePh(e,!0),i=eQq.length,IE(r.substr(r.length-i,i),eQq)){if(4==(n=r.length)){if(43==(t=(GV(0,r.length),r.charCodeAt(0))))return tvX;if(45==t)return tvZ}else if(3==n)return tvX}return new bK(r)}function eOy(e){var t,n,r;return((n=e.l)&n-1)!=0||((r=e.m)&r-1)!=0||((t=e.h)&t-1)!=0||0==t&&0==r&&0==n?-1:0==t&&0==r&&0!=n?enq(n):0==t&&0!=r&&0==n?enq(r)+22:0!=t&&0==r&&0==n?enq(t)+44:-1}function eOw(e,t){var n,r,i,a,o;for(ewG(t,"Edge joining",1),n=gN(LK(e_k(e,(eBy(),toz)))),i=new fz(e.b);i.a1)for(i=new fz(e.a);i.a0),a.a.Xb(a.c=--a.b),CD(a,i),A6(a.becd(r,0)?(i=eHf-jE(edQ(QC(r),eHf)))==eHf&&(i=0):i=jE(edQ(r,eHf)),1==t?Bd(e,48+(i=eB4.Math.min((i+50)/100|0,9))&eHd):2==t?eeE(e,i=eB4.Math.min((i+5)/10|0,99),2):(eeE(e,i,3),t>3&&eeE(e,0,t-3))}function eOM(e){var t,n,r,i;return xc(e_k(e,(eBy(),taM)))===xc((eck(),tpz))?!e.e&&xc(e_k(e,tat))!==xc((eaU(),ttO)):(r=Pp(e_k(e,tan),292),i=gN(LK(e_k(e,tao)))||xc(e_k(e,tas))===xc((en7(),teR)),t=Pp(e_k(e,tae),19).a,n=e.a.c.length,!i&&r!=(eaU(),ttO)&&(0==t||t>n))}function eOO(e){var t,n;for(n=0;n0);n++);if(n>0&&n0);t++);return t>0&&n>16!=6&&t){if(eg7(e,t))throw p7(new gL(eZ4+ex2(e)));r=null,e.Cb&&(r=(n=e.Db>>16)>=0?eg1(e,r):e.Cb.ih(e,-1-n,null,r)),t&&(r=ep0(t,e,6,r)),(r=Cc(e,t,r))&&r.Fi()}else(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,6,t,t))}function eOL(e,t){var n,r;if(t!=e.Cb||e.Db>>16!=9&&t){if(eg7(e,t))throw p7(new gL(eZ4+eC5(e)));r=null,e.Cb&&(r=(n=e.Db>>16)>=0?eg2(e,r):e.Cb.ih(e,-1-n,null,r)),t&&(r=ep0(t,e,9,r)),(r=Cl(e,t,r))&&r.Fi()}else(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,9,t,t))}function eOC(e,t){var n,r;if(t!=e.Cb||e.Db>>16!=3&&t){if(eg7(e,t))throw p7(new gL(eZ4+ePY(e)));r=null,e.Cb&&(r=(n=e.Db>>16)>=0?evo(e,r):e.Cb.ih(e,-1-n,null,r)),t&&(r=ep0(t,e,12,r)),(r=Cu(e,t,r))&&r.Fi()}else(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,3,t,t))}function eOI(e){var t,n,r,i,a;if(r=evl(e),null==(a=e.j)&&r)return e.$j()?null:r.zj();if(M4(r,148)){if((n=r.Aj())&&(i=n.Nh())!=e.i){if((t=Pp(r,148)).Ej())try{e.g=i.Kh(t,a)}catch(o){if(o=eoa(o),M4(o,78))e.g=null;else throw p7(o)}e.i=i}return e.g}return null}function eOD(e){var t;return t=new p0,P_(t,new EL(new kl(e.c,e.d),new kl(e.c+e.b,e.d))),P_(t,new EL(new kl(e.c,e.d),new kl(e.c,e.d+e.a))),P_(t,new EL(new kl(e.c+e.b,e.d+e.a),new kl(e.c+e.b,e.d))),P_(t,new EL(new kl(e.c+e.b,e.d+e.a),new kl(e.c,e.d+e.a))),t}function eON(e,t,n,r){var i,a,o;if(o=eyn(t,n),r.c[r.c.length]=t,-1==e.j[o.p]||2==e.j[o.p]||e.a[t.p])return r;for(e.j[o.p]=-1,a=new Fa(OH(efs(o).a.Kc(),new c));eTk(a);)if(i=Pp(ZC(a),17),!q8(i)&&!(!q8(i)&&i.c.i.c==i.d.i.c)&&i!=t)return eON(e,i,o,r);return r}function eOP(e,t,n){var r,i,a;for(a=t.a.ec().Kc();a.Ob();)i=Pp(a.Pb(),79),(r=Pp(Bp(e.b,i),266))||(z$(e_I(i))==z$(e_P(i))?eLk(e,i,n):e_I(i)==z$(e_P(i))?null==Bp(e.c,i)&&null!=Bp(e.b,e_P(i))&&eFt(e,i,n,!1):null==Bp(e.d,i)&&null!=Bp(e.b,e_I(i))&&eFt(e,i,n,!0))}function eOR(e,t){var n,r,i,a,o,s,u;for(i=e.Kc();i.Ob();)for(r=Pp(i.Pb(),10),s=new eES,Gc(s,r),ekv(s,(eYu(),tby)),eo3(s,(eBU(),tnm),(OQ(),!0)),o=t.Kc();o.Ob();)a=Pp(o.Pb(),10),u=new eES,Gc(u,a),ekv(u,tbY),eo3(u,tnm,!0),n=new $b,eo3(n,tnm,!0),Gs(n,s),Go(n,u)}function eOj(e,t,n,r){var i,a,o,s;i=ehu(e,t,n),a=ehu(e,n,t),o=Pp(Bp(e.c,t),112),s=Pp(Bp(e.c,n),112),ir.b.g&&(a.c[a.c.length]=r);return a}function eOB(){eOB=A,tfo=new S9("CANDIDATE_POSITION_LAST_PLACED_RIGHT",0),tfa=new S9("CANDIDATE_POSITION_LAST_PLACED_BELOW",1),tfu=new S9("CANDIDATE_POSITION_WHOLE_DRAWING_RIGHT",2),tfs=new S9("CANDIDATE_POSITION_WHOLE_DRAWING_BELOW",3),tfc=new S9("WHOLE_DRAWING",4)}function eOU(e,t){if(M4(t,239))return elg(e,Pp(t,33));if(M4(t,186))return el$(e,Pp(t,118));if(M4(t,354))return Hd(e,Pp(t,137));if(M4(t,352))return eNP(e,Pp(t,79));if(t)return null;else throw p7(new gL(eXx+e_F(new g$(eow(vx(e1R,1),eUp,1,5,[t])))))}function eOH(e){var t,n,r,i,a,o,s;for(a=new _n,i=new fz(e.d.a);i.a1)for(t=Al((n=new b1,++e.b,n),e.d),s=epL(a,0);s.b!=s.d.c;)o=Pp(Vv(s),121),eAx(_f(_l(_d(_c(new bQ,1),0),t),o))}function eO$(e,t){var n,r;if(t!=e.Cb||e.Db>>16!=11&&t){if(eg7(e,t))throw p7(new gL(eZ4+eC4(e)));r=null,e.Cb&&(r=(n=e.Db>>16)>=0?evs(e,r):e.Cb.ih(e,-1-n,null,r)),t&&(r=ep0(t,e,10,r)),(r=C4(e,t,r))&&r.Fi()}else(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,11,t,t))}function eOz(e){var t,n,r,i;for(r=new esz(new fS(e.b).a);r.b;)n=etz(r),i=Pp(n.cd(),11),eo3(t=Pp(n.dd(),10),(eBU(),tnc),i),eo3(i,tng,t),eo3(i,tt6,(OQ(),!0)),ekv(i,Pp(e_k(t,tt1),61)),e_k(t,tt1),eo3(i.i,(eBy(),tol),(ewf(),tbu)),Pp(e_k(Bq(i.i),tt3),21).Fc((eLR(),ttS))}function eOG(e,t,n){var r,i,a,o,s,u;if(a=0,o=0,e.c)for(u=new fz(e.d.i.j);u.aa.a)?-1:i.a(u=null==e.d?0:e.d.length)))return!1;for(a=0,l=e.d,e.d=Je(e6C,eJA,63,2*u+4,0,1);a=0x7fffffffffffffff?(Q2(),e0L):(i=!1,e<0&&(i=!0,e=-e),r=0,e>=eHW&&(r=zy(e/eHW),e-=r*eHW),n=0,e>=eHG&&(n=zy(e/eHG),e-=n*eHG),a=Mk(t=zy(e),n,r),i&&esh(a),a)}function eO6(e,t){var n,r,i,a;for(n=!t||!e.u.Hc((ekU(),tbp)),a=0,i=new fz(e.e.Cf());i.a=-t&&r==t?new kD(ell(n-1),ell(r)):new kD(ell(n),ell(r-1))}function eAn(){return eB$(),eow(vx(e4B,1),eU4,77,0,[e85,e82,e86,e7d,e7C,e7m,e7j,e7_,e7A,e7s,e7x,e7w,e7L,e7r,e7Y,e8Z,e7k,e7D,e7h,e7I,e7U,e7M,e8X,e7O,e7H,e7P,e7B,e7p,e7e,e7b,e7f,e7F,e81,e88,e7v,e8Q,e7y,e7c,e7i,e7E,e7o,e83,e80,e7l,e7a,e7S,e7R,e8J,e7T,e7u,e7g,e7t,e87,e7N,e89,e7n,e84])}function eAr(e,t,n){e.d=0,e.b=0,t.k==(eEn(),e8P)&&n.k==e8P&&Pp(e_k(t,(eBU(),tnc)),10)==Pp(e_k(n,tnc),10)&&(QP(t).j==(eYu(),tbw)?eMH(e,t,n):eMH(e,n,t)),t.k==e8P&&n.k==e8D?QP(t).j==(eYu(),tbw)?e.d=1:e.b=1:n.k==e8P&&t.k==e8D&&(QP(n).j==(eYu(),tbw)?e.b=1:e.d=1),emu(e,t,n)}function eAi(e){var t,n,r,i,a,o,s,u,c,l,f;return f=ewW(e),(u=null!=(t=e.a))&&P4(f,"category",e.a),(o=!(i=wc(new fk(e.d))))&&(ee3(f,"knownOptions",c=new lN),n=new pS(c),qX(new fk(e.d),n)),(s=!(a=wc(e.g)))&&(ee3(f,"supportedFeatures",l=new lN),r=new pk(l),qX(e.g,r)),f}function eAa(e){var t,n,r,i,a,o,s,u,c;for(u=0,r=!1,t=336,n=0,a=new CE(e.length),c=(s=e).length;u>16!=7&&t){if(eg7(e,t))throw p7(new gL(eZ4+eE1(e)));r=null,e.Cb&&(r=(n=e.Db>>16)>=0?eg0(e,r):e.Cb.ih(e,-1-n,null,r)),t&&(r=Pp(t,49).gh(e,1,e6p,r)),(r=j2(e,t,r))&&r.Fi()}else(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,7,t,t))}function eAc(e,t){var n,r;if(t!=e.Cb||e.Db>>16!=3&&t){if(eg7(e,t))throw p7(new gL(eZ4+eln(e)));r=null,e.Cb&&(r=(n=e.Db>>16)>=0?eg4(e,r):e.Cb.ih(e,-1-n,null,r)),t&&(r=Pp(t,49).gh(e,0,e6y,r)),(r=j3(e,t,r))&&r.Fi()}else(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,3,t,t))}function eAl(e,t){var n,r,i,a,o,s,u,c,l;return(exX(),t.d>e.d&&(s=e,e=t,t=s),t.d<63)?eLm(e,t):(o=(-2&e.d)<<4,c=ZL(e,o),l=ZL(t,o),r=eNz(e,ZA(c,o)),i=eNz(t,ZA(l,o)),u=eAl(c,l),n=eAl(r,i),a=eAl(eNz(c,r),eNz(i,l)),a=eP5(eP5(a,u),n),a=ZA(a,o),u=ZA(u,o<<1),eP5(eP5(u,a),n))}function eAf(e,t,n){var r,i,a,o,s;for(o=ecZ(e,n),s=Je(e4N,eGW,10,t.length,0,1),r=0,a=o.Kc();a.Ob();)gN(LK(e_k(i=Pp(a.Pb(),11),(eBU(),tt6))))&&(s[r++]=Pp(e_k(i,tng),10));if(r=0;a+=n?1:-1)o|=t.c.Sf(u,a,n,r&&!gN(LK(e_k(t.j,(eBU(),tt2))))&&!gN(LK(e_k(t.j,(eBU(),tnS))))),o|=t.q._f(u,a,n),o|=eCA(e,u[a],n,r);return Yf(e.c,t),o}function eAm(e,t,n){var r,i,a,o,s,u,c,l,f,d;for(l=Kz(e.j),f=0,d=l.length;f1&&(e.a=!0),jU(Pp(n.b,65),C5(MB(Pp(t.b,65).c),Ol(C6(MB(Pp(n.b,65).a),Pp(t.b,65).a),i))),GC(e,t),eAy(e,n)}function eAw(e){var t,n,r,i,a,o,s;for(a=new fz(e.a.a);a.a0&&a>0?o.p=t++:r>0?o.p=n++:a>0?o.p=i++:o.p=n++}Hj(),Mv(e.j,new nG)}function eAE(e){var t,n;n=null,t=Pp(RJ(e.g,0),17);do{if(Ln(n=t.d.i,(eBU(),tna)))return Pp(e_k(n,tna),11).i;if(n.k!=(eEn(),e8N)&&eTk(new Fa(OH(efc(n).a.Kc(),new c))))t=Pp(ZC(new Fa(OH(efc(n).a.Kc(),new c))),17);else if(n.k!=e8N)return null}while(!!n&&n.k!=(eEn(),e8N))return n}function eAS(e,t){var n,r,i,a,o,s,u,c,l;for(a=1,s=t.j,o=t.g,c=em1(e,o,u=Pp(RJ(s,s.c.length-1),113),l=(GK(0,s.c.length),Pp(s.c[0],113)));ac&&(u=n,l=i,c=r);t.a=l,t.c=u}function eAk(e,t){var n,r;if(!(r=YB(e.b,t.b)))throw p7(new gC("Invalid hitboxes for scanline constraint calculation."));(eop(t.b,Pp(CF(e.b,t.b),57))||eop(t.b,Pp(Cj(e.b,t.b),57)))&&(wK(),t.b),e.a[t.b.f]=Pp(Ik(e.b,t.b),57),(n=Pp(IS(e.b,t.b),57))&&(e.a[n.f]=t.b)}function eAx(e){if(!e.a.d||!e.a.e)throw p7(new gC((LW(e23),e23.k+" must have a source and target "+(LW(e24),e24.k)+" specified.")));if(e.a.d==e.a.e)throw p7(new gC("Network simplex does not support self-loops: "+e.a+" "+e.a.d+" "+e.a.e));return Am(e.a.d.g,e.a),Am(e.a.e.b,e.a),e.a}function eAT(e,t,n){var r,i,a,o,s,u,c;for(c=new yB(new hA(e)),o=eow(vx(e4j,1),eGK,11,0,[t,n]),s=0,u=o.length;su-e.b&&su-e.a&&s0&&++h;++d}return h}function eAF(e,t){var n,r,i,a,o;for(o=Pp(e_k(t,(eTj(),tcN)),425),a=epL(t.b,0);a.b!=a.d.c;)if(i=Pp(Vv(a),86),0==e.b[i.g]){switch(o.g){case 0:eb9(e,i);break;case 1:eTX(e,i)}e.b[i.g]=2}for(r=epL(e.a,0);r.b!=r.d.c;)eds((n=Pp(Vv(r),188)).b.d,n,!0),eds(n.c.b,n,!0);eo3(t,(eR6(),tch),e.a)}function eAY(e,t){var n,r,i,a;return(_4(),t)?t==(eR7(),tvG)||(t==tvM||t==tvx||t==tvT)&&e!=tvk?new eF2(e,t):((n=(r=Pp(t,677)).pk())||(UH(QZ((eSp(),tvc),t)),n=r.pk()),a=(n.i||(n.i=new p2),n.i),(i=Pp(xu($I(a.f,e)),1942))||Um(a,e,i=new eF2(e,t)),i):tvb}function eAB(e,t){var n,r,i,a,o,s,u,c,l;for(a=0,u=Pp(e_k(e,(eBU(),tnc)),11),c=esp(eow(vx(e50,1),eUP,8,0,[u.i.n,u.n,u.a])).a,l=e.i.n.b,o=(i=n=Kp(e.e)).length;a0?a.a?n>(s=a.b.rf().a)&&(i=(n-s)/2,a.d.b=i,a.d.c=i):a.d.c=e.s+n:FY(e.u)&&((r=ew1(a.b)).c<0&&(a.d.b=-r.c),r.c+r.b>a.b.rf().a&&(a.d.c=r.c+r.b-a.b.rf().a))}function eAz(e,t){var n,r,i,a;for(ewG(t,"Semi-Interactive Crossing Minimization Processor",1),n=!1,i=new fz(e.b);i.a=0){if(t==n)return new kD(ell(-t-1),ell(-t-1));if(t==-n)return new kD(ell(-t),ell(n+1))}return eB4.Math.abs(t)>eB4.Math.abs(n)?t<0?new kD(ell(-t),ell(n)):new kD(ell(-t),ell(n+1)):new kD(ell(t+1),ell(n))}function eAK(e){var t,n;n=Pp(e_k(e,(eBy(),taY)),163),t=Pp(e_k(e,(eBU(),tt9)),303),n==(ef_(),tnN)?(eo3(e,taY,tnj),eo3(e,tt9,(Q1(),ttN))):n==tnR?(eo3(e,taY,tnj),eo3(e,tt9,(Q1(),ttI))):t==(Q1(),ttN)?(eo3(e,taY,tnN),eo3(e,tt9,ttD)):t==ttI&&(eo3(e,taY,tnR),eo3(e,tt9,ttD))}function eAV(){eAV=A,tuY=new ad,tuP=RI(new K2,(e_x(),e8n),(eB$(),e7h)),tuF=j0(RI(new K2,e8n,e7M),e8i,e7T),tuB=ehY(ehY(_G(j0(RI(new K2,e8e,e7j),e8i,e7R),e8r),e7P),e7F),tuR=j0(RI(RI(RI(new K2,e8t,e7m),e8r,e7v),e8r,e7y),e8i,e7g),tuj=j0(RI(RI(new K2,e8r,e7y),e8r,e88),e8i,e89)}function eAq(){eAq=A,tuz=RI(j0(new K2,(e_x(),e8i),(eB$(),e7t)),e8n,e7h),tuV=ehY(ehY(_G(j0(RI(new K2,e8e,e7j),e8i,e7R),e8r),e7P),e7F),tuG=j0(RI(RI(RI(new K2,e8t,e7m),e8r,e7v),e8r,e7y),e8i,e7g),tuK=RI(RI(new K2,e8n,e7M),e8i,e7T),tuW=j0(RI(RI(new K2,e8r,e7y),e8r,e88),e8i,e89)}function eAZ(e,t,n,r,i){var a,o;(q8(t)||t.c.i.c!=t.d.i.c)&&erS(esp(eow(vx(e50,1),eUP,8,0,[i.i.n,i.n,i.a])),n)||q8(t)||(t.c==i?Ls(t.a,0,new TS(n)):P7(t.a,new TS(n)),r&&!w0(e.a,n)&&((o=Pp(e_k(t,(eBy(),taR)),74))||eo3(t,taR,o=new mE),qQ(o,a=new TS(n),o.c.b,o.c),Yf(e.a,a)))}function eAX(e){var t,n;for(n=new Fa(OH(efu(e).a.Kc(),new c));eTk(n);)if((t=Pp(ZC(n),17)).c.i.k!=(eEn(),e8I))throw p7(new gq(eWr+egs(e)+"' has its layer constraint set to FIRST, but has at least one incoming edge that does not come from a FIRST_SEPARATE node. That must not happen."))}function eAJ(e,t,n){var r,i,a,o,s,u,c;if(0==(i=efp(254&e.Db)))e.Eb=n;else{if(1==i)s=Je(e1R,eUp,1,2,5,1),0==(a=emF(e,t))?(s[0]=n,s[1]=e.Eb):(s[0]=e.Eb,s[1]=n);else for(r=2,s=Je(e1R,eUp,1,i+1,5,1),o=etG(e.Eb),u=0,c=0;r<=128;r<<=1)r==t?s[c++]=n:(e.Db&r)!=0&&(s[c++]=o[u++]);e.Eb=s}e.Db|=t}function eAQ(e,t,n){var r,i,a,o;for(this.b=new p0,i=0,r=0,o=new fz(e);o.a0&&(i+=(a=Pp(RJ(this.b,0),167)).o,r+=a.p),i*=2,r*=2,t>1?i=zy(eB4.Math.ceil(i*t)):r=zy(eB4.Math.ceil(r/t)),this.a=new edL(i,r)}function eA1(e,t,n,r,i,a){var o,s,u,c,l,f,d,h,p,b,m,g;for(l=r,t.j&&t.o?(b=(h=Pp(Bp(e.f,t.A),57)).d.c+h.d.b,--l):b=t.a.c+t.a.b,f=i,n.q&&n.o?(c=(h=Pp(Bp(e.f,n.C),57)).d.c,++f):c=n.a.c,m=c-b,p=b+(s=m/(u=eB4.Math.max(2,f-l))),d=l;d=0;o+=i?1:-1){for(s=t[o],u=r==(eYu(),tby)?i?efr(s,r):eaa(efr(s,r)):i?eaa(efr(s,r)):efr(s,r),a&&(e.c[s.p]=u.gc()),f=u.Kc();f.Ob();)l=Pp(f.Pb(),11),e.d[l.p]=c++;eoc(n,u)}}function eA2(e,t,n){var r,i,a,o,s,u,c,l;for(a=gP(LV(e.b.Kc().Pb())),c=gP(LV(eaX(t.b))),l=C5(r=Ol(MB(e.a),c-n),i=Ol(MB(t.a),n-a)),Ol(l,1/(c-a)),this.a=l,this.b=new p0,s=!0,(o=e.b.Kc()).Pb();o.Ob();)u=gP(LV(o.Pb())),s&&u-n>eVW&&(this.b.Fc(n),s=!1),this.b.Fc(u);s&&this.b.Fc(n)}function eA3(e){var t,n,r,i;if(eIh(e,e.n),e.d.c.length>0){for(gG(e.c);eTT(e,Pp(Wx(new fz(e.e.a)),121))>5,t&=31,r>=e.d)return e.e<0?(eLQ(),e03):(eLQ(),e08);if(i=Je(ty_,eHT,25,(a=e.d-r)+1,15,1),eEG(i,a,e.a,r,t),e.e<0){for(n=0;n0&&e.a[n]<<32-t!=0){for(n=0;n=0)&&(!(n=eR3((eSp(),tvc),i,t))||((r=n.Zj())>1||-1==r)&&3!=Ur(QZ(tvc,n))))}function eLn(e,t,n,r){var i,a,o,s,u;return(s=ewH(Pp(etj((t.b||(t.b=new Ih(e6m,t,4,7)),t.b),0),82)),u=ewH(Pp(etj((t.c||(t.c=new Ih(e6m,t,5,8)),t.c),0),82)),z$(s)==z$(u)||etg(u,s))?null:(o=zF(t))==n?r:(a=Pp(Bp(e.a,o),10))&&(i=a.e)?i:null}function eLr(e,t){var n;switch(n=Pp(e_k(e,(eBy(),tam)),276),ewG(t,"Label side selection ("+n+")",1),n.g){case 0:eTD(e,(egF(),tpV));break;case 1:eTD(e,(egF(),tpq));break;case 2:eNW(e,(egF(),tpV));break;case 3:eNW(e,(egF(),tpq));break;case 4:eLL(e,(egF(),tpV));break;case 5:eLL(e,(egF(),tpq))}eEj(t)}function eLi(e,t,n){var r,i,a,o,s,u;if((o=e[r=vK(n,e.length)])[0].k==(eEn(),e8C))for(i=0,a=vW(n,o.length),u=t.j;i0&&(n[0]+=e.d,o-=n[0]),n[2]>0&&(n[2]+=e.d,o-=n[2]),a=eB4.Math.max(0,o),n[1]=eB4.Math.max(n[1],o),ZR(e,e3N,i.c+r.b+n[0]-(n[1]-o)/2,n),t==e3N&&(e.c.b=a,e.c.c=i.c+r.b+(a-o)/2)}function eLy(){this.c=Je(tyx,eH5,25,(eYu(),eow(vx(e6a,1),eGj,61,0,[tbF,tbw,tby,tbj,tbY])).length,15,1),this.b=Je(tyx,eH5,25,eow(vx(e6a,1),eGj,61,0,[tbF,tbw,tby,tbj,tbY]).length,15,1),this.a=Je(tyx,eH5,25,eow(vx(e6a,1),eGj,61,0,[tbF,tbw,tby,tbj,tbY]).length,15,1),Ep(this.c,eHQ),Ep(this.b,eH1),Ep(this.a,eH1)}function eLw(e,t,n){var r,i,a,o;if(t<=n?(i=t,a=n):(i=n,a=t),r=0,null==e.b)e.b=Je(ty_,eHT,25,2,15,1),e.b[0]=i,e.b[1]=a,e.c=!0;else{if(r=e.b.length,e.b[r-1]+1==i){e.b[r-1]=a;return}o=Je(ty_,eHT,25,r+2,15,1),ePD(e.b,0,o,0,r),e.b=o,e.b[r-1]>=i&&(e.c=!1,e.a=!1),e.b[r++]=i,e.b[r]=a,e.c||eMS(e)}}function eL_(e,t,n){var r,i,a,o,s,u,c;for(c=t.d,e.a=new XM(c.c.length),e.c=new p2,s=new fz(c);s.a=0?e._g(c,!1,!0):exk(e,n,!1),58);n:for(a=f.Kc();a.Ob();){for(l=0,i=Pp(a.Pb(),56);l1;)eLN(i,i.i-1);return r}function eLA(e,t){var n,r,i,a,o,s,u;for(ewG(t,"Comment post-processing",1),a=new fz(e.b);a.ae.d[o.p]&&(n+=qq(e.b,a),Vw(e.a,ell(a)));for(;!gY(e.a);)eek(e.b,Pp(Yn(e.a),19).a)}return n}function eLD(e,t,n){var r,i,a,o;for(a=(t.a||(t.a=new FQ(e6k,t,10,11)),t.a).i,i=new Ow((t.a||(t.a=new FQ(e6k,t,10,11)),t.a));i.e!=i.i.gc();)0==((r=Pp(epH(i),33)).a||(r.a=new FQ(e6k,r,10,11)),r.a).i||(a+=eLD(e,r,!1));if(n)for(o=z$(t);o;)a+=(o.a||(o.a=new FQ(e6k,o,10,11)),o.a).i,o=z$(o);return a}function eLN(e,t){var n,r,i,a;return e.ej()?(r=null,i=e.fj(),e.ij()&&(r=e.kj(e.pi(t),null)),n=e.Zi(4,a=egk(e,t),null,t,i),e.bj()&&null!=a?(r=e.dj(a,r))?(r.Ei(n),r.Fi()):e.$i(n):r?(r.Ei(n),r.Fi()):e.$i(n),a):(a=egk(e,t),e.bj()&&null!=a&&(r=e.dj(a,null))&&r.Fi(),a)}function eLP(e){var t,n,r,i,a,o,s,u,c,l;for(c=e.a,t=new bV,u=0,r=new fz(e.d);r.as.d&&(l=s.d+s.a+c));n.c.d=l,t.a.zc(n,t),u=eB4.Math.max(u,n.c.d+n.c.a)}return u}function eLR(){eLR=A,ttv=new Sv("COMMENTS",0),ttw=new Sv("EXTERNAL_PORTS",1),tt_=new Sv("HYPEREDGES",2),ttE=new Sv("HYPERNODES",3),ttS=new Sv("NON_FREE_PORTS",4),ttk=new Sv("NORTH_SOUTH_PORTS",5),ttT=new Sv(eWw,6),ttg=new Sv("CENTER_LABELS",7),tty=new Sv("END_LABELS",8),ttx=new Sv("PARTITIONS",9)}function eLj(e){var t,n,r,i,a;for(i=new p0,t=new Rq((e.a||(e.a=new FQ(e6k,e,10,11)),e.a)),r=new Fa(OH(eOi(e).a.Kc(),new c));eTk(r);)n=Pp(ZC(r),79),!M4(etj((n.b||(n.b=new Ih(e6m,n,4,7)),n.b),0),186)&&(a=ewH(Pp(etj((n.c||(n.c=new Ih(e6m,n,5,8)),n.c),0),82)),t.a._b(a)||(i.c[i.c.length]=a));return i}function eLF(e){var t,n,r,i,a,o;for(a=new bV,t=new Rq((e.a||(e.a=new FQ(e6k,e,10,11)),e.a)),i=new Fa(OH(eOi(e).a.Kc(),new c));eTk(i);)r=Pp(ZC(i),79),!M4(etj((r.b||(r.b=new Ih(e6m,r,4,7)),r.b),0),186)&&(o=ewH(Pp(etj((r.c||(r.c=new Ih(e6m,r,5,8)),r.c),0),82)),t.a._b(o)||(n=a.a.zc(o,a)));return a}function eLY(e,t,n,r,i){return r<0?((r=ew6(e,i,eow(vx(e17,1),eUP,2,6,[eHh,eHp,eHb,eHm,eHg,eHv,eHy,eHw,eH_,eHE,eHS,eHk]),t))<0&&(r=ew6(e,i,eow(vx(e17,1),eUP,2,6,["Jan","Feb","Mar","Apr",eHg,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),t)),!(r<0)&&(n.k=r,!0)):r>0&&(n.k=r-1,!0)}function eLB(e,t,n,r,i){return r<0?((r=ew6(e,i,eow(vx(e17,1),eUP,2,6,[eHh,eHp,eHb,eHm,eHg,eHv,eHy,eHw,eH_,eHE,eHS,eHk]),t))<0&&(r=ew6(e,i,eow(vx(e17,1),eUP,2,6,["Jan","Feb","Mar","Apr",eHg,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),t)),!(r<0)&&(n.k=r,!0)):r>0&&(n.k=r-1,!0)}function eLU(e,t,n,r,i,a){var o,s,u,c;if(s=32,r<0){if(t[0]>=e.length||43!=(s=UI(e,t[0]))&&45!=s||(++t[0],(r=exf(e,t))<0))return!1;45==s&&(r=-r)}return 32==s&&t[0]-n==2&&2==i.b&&(o=(c=(u=new wW).q.getFullYear()-eHx+eHx-80)%100,a.a=r==o,r+=(c/100|0)*100+(r=c&&(u=r);u&&(l=eB4.Math.max(l,u.a.o.a)),l>d&&(f=c,d=l)}return f}function eLV(e,t,n){var r,i,a;if(e.e=n,e.d=0,e.b=0,e.f=1,e.i=t,(16&e.e)==16&&(e.i=eIw(e.i)),e.j=e.i.length,eBM(e),a=ehT(e),e.d!=e.j)throw p7(new gX(eBJ((Mo(),eXV))));if(e.g){for(r=0;reqg?Mv(u,e.b):r<=eqg&&r>eqv?Mv(u,e.d):r<=eqv&&r>eqy?Mv(u,e.c):r<=eqy&&Mv(u,e.a),a=eLJ(e,u,a);return i}function eLQ(){var e;for(e=0,eLQ=A,e04=new XE(1,1),e06=new XE(1,10),e08=new XE(0,0),e03=new XE(-1,1),e05=eow(vx(e0t,1),eUP,91,0,[e08,e04,new XE(1,2),new XE(1,3),new XE(1,4),new XE(1,5),new XE(1,6),new XE(1,7),new XE(1,8),new XE(1,9),e06]),e09=Je(e0t,eUP,91,32,0,1);e1)&&(r=new kl(i,n.b),P7(t.a,r)),enD(t.a,eow(vx(e50,1),eUP,8,0,[d,f]))}function eL6(e){_Y(e,new ewB(vQ(vq(vJ(vX(new oc,eZA),"ELK Randomizer"),'Distributes the nodes randomly on the plane, leading to very obfuscating layouts. Can be useful to demonstrate the power of "real" layout algorithms.'),new oz))),KE(e,eZA,ezW,tb$),KE(e,eZA,eGi,15),KE(e,eZA,eGo,ell(0)),KE(e,eZA,ezG,eGt)}function eL9(){var e,t,n,r,i,a;for(t=0,eL9=A,tv1=Je(tyk,eZ8,25,255,15,1),tv0=Je(tyw,eHl,25,16,15,1);t<255;t++)tv1[t]=-1;for(n=57;n>=48;n--)tv1[n]=n-48<<24>>24;for(r=70;r>=65;r--)tv1[r]=r-65+10<<24>>24;for(i=102;i>=97;i--)tv1[i]=i-97+10<<24>>24;for(a=0;a<10;a++)tv0[a]=48+a&eHd;for(e=10;e<=15;e++)tv0[e]=65+e-10&eHd}function eL8(e,t,n){var r,i,a,o,s,u,c,l;return s=t.i-e.g/2,u=n.i-e.g/2,c=t.j-e.g/2,l=n.j-e.g/2,a=t.g+e.g/2,o=n.g+e.g/2,r=t.f+e.g/2,i=n.f+e.g/2,!!(s>19!=0)return"-"+eCr(eoQ(e));for(n=e,r="";!(0==n.l&&0==n.m&&0==n.h);){if(n=eRV(n,i=Zx(eHK),!0),t=""+yq(e0A),!(0==n.l&&0==n.m&&0==n.h))for(a=9-t.length;a>0;a--)t="0"+t;r=t+r}return r}function eCi(){if(!Object.create||!Object.getOwnPropertyNames)return!1;var e="__proto__",t=Object.create(null);return void 0===t[e]&&0==Object.getOwnPropertyNames(t).length&&(t[e]=42,42===t[e]&&0!=Object.getOwnPropertyNames(t).length)}function eCa(e){var t,n,r,i,a,o,s;for(t=!1,n=0,i=new fz(e.d.b);i.a=e.a||!ewg(t,n))return -1;if(Vb(Pp(r.Kb(t),20)))return 1;for(i=0,o=Pp(r.Kb(t),20).Kc();o.Ob();)if(-1==(s=eCu(e,u=(a=Pp(o.Pb(),17)).c.i==t?a.d.i:a.c.i,n,r))||(i=eB4.Math.max(i,s))>e.c-1)return -1;return i+1}function eCc(e,t){var n,r,i,a,o,s;if(xc(t)===xc(e))return!0;if(!M4(t,15)||(r=Pp(t,15),s=e.gc(),r.gc()!=s))return!1;if(o=r.Kc(),e.ni()){for(n=0;n0){if(e.qj(),null!=t){for(a=0;a>24;case 97:case 98:case 99:case 100:case 101:case 102:return e-97+10<<24>>24;case 65:case 66:case 67:case 68:case 69:case 70:return e-65+10<<24>>24;default:throw p7(new vo("Invalid hexadecimal"))}}function eCh(e,t,n){var r,i,a,o;for(ewG(n,"Processor order nodes",2),e.a=gP(LV(e_k(t,(eTj(),tcR)))),i=new _n,o=epL(t.b,0);o.b!=o.d.c;)gN(LK(e_k(a=Pp(Vv(o),86),(eR6(),tcm))))&&qQ(i,a,i.c.b,i.c);eRt(e,r=(A6(0!=i.b),Pp(i.a.a.c,86))),n.b||erd(n,1),eC1(e,r,0-gP(LV(e_k(r,(eR6(),tcu))))/2,0),n.b||erd(n,1),eEj(n)}function eCp(){eCp=A,e3C=new Ej("SPIRAL",0),e3T=new Ej("LINE_BY_LINE",1),e3M=new Ej("MANHATTAN",2),e3x=new Ej("JITTER",3),e3A=new Ej("QUADRANTS_LINE_BY_LINE",4),e3L=new Ej("QUADRANTS_MANHATTAN",5),e3O=new Ej("QUADRANTS_JITTER",6),e3k=new Ej("COMBINE_LINE_BY_LINE_MANHATTAN",7),e3S=new Ej("COMBINE_JITTER_MANHATTAN",8)}function eCb(e,t,n,r){var i,a,o,s,u,c;for(u=eya(e,n),c=eya(t,n),i=!1;u&&c;)if(r||egl(u,c,n))o=eya(u,n),s=eya(c,n),QB(t),QB(e),a=u.c,ejf(u,!1),ejf(c,!1),n?(egU(t,c.p,a),t.p=c.p,egU(e,u.p+1,a),e.p=u.p):(egU(e,u.p,a),e.p=u.p,egU(t,c.p+1,a),t.p=c.p),Gu(u,null),Gu(c,null),u=o,c=s,i=!0;else break;return i}function eCm(e,t,n,r){var i,a,o,s,u;for(i=!1,a=!1,s=new fz(r.j);s.a=t.length)throw p7(new gE("Greedy SwitchDecider: Free layer not in graph."));this.c=t[e],this.e=new IQ(r),er$(this.e,this.c,(eYu(),tbY)),this.i=new IQ(r),er$(this.i,this.c,tby),this.f=new jy(this.c),this.a=!a&&i.i&&!i.s&&this.c[0].k==(eEn(),e8C),this.a&&eSt(this,e,t.length)}function eC_(e,t){var n,r,i,a,o,s;a=!e.B.Hc((eI3(),tbX)),o=e.B.Hc(tb1),e.a=new edA(o,a,e.c),e.n&&HI(e.a.n,e.n),gh(e.g,(etx(),e3N),e.a),t||((r=new eh6(1,a,e.c)).n.a=e.k,jT(e.p,(eYu(),tbw),r),(i=new eh6(1,a,e.c)).n.d=e.k,jT(e.p,tbj,i),(s=new eh6(0,a,e.c)).n.c=e.k,jT(e.p,tbY,s),(n=new eh6(0,a,e.c)).n.b=e.k,jT(e.p,tby,n))}function eCE(e){var t,n,r;switch((t=Pp(e_k(e.d,(eBy(),tag)),218)).g){case 2:n=eBn(e);break;case 3:n=(r=new p0,_r(UJ(UQ(eeh(eeh(new R1(null,new Gq(e.d.b,16)),new rJ),new rQ),new r1),new rY),new ha(r)),r);break;default:throw p7(new gC("Compaction not supported for "+t+" edges."))}eRD(e,n),qX(new fk(e.g),new hr(e))}function eCS(e,t){var n;return(n=new eX,t&&eaW(n,Pp(Bp(e.a,e6p),94)),M4(t,470)&&eaW(n,Pp(Bp(e.a,e6b),94)),M4(t,354))?(eaW(n,Pp(Bp(e.a,e6S),94)),n):(M4(t,82)&&eaW(n,Pp(Bp(e.a,e6m),94)),M4(t,239))?(eaW(n,Pp(Bp(e.a,e6k),94)),n):M4(t,186)?(eaW(n,Pp(Bp(e.a,e6x),94)),n):(M4(t,352)&&eaW(n,Pp(Bp(e.a,e6g),94)),n)}function eCk(){eCk=A,e9M=new T2((eBB(),th4),ell(1)),e9D=new T2(tpl,80),e9I=new T2(tpr,5),e9p=new T2(td2,eGt),e9O=new T2(th5,ell(1)),e9C=new T2(th8,(OQ(),!0)),e9k=new T3(50),e9S=new T2(thN,e9k),e9m=thb,e9x=thV,e9b=new T2(thn,!1),e9E=thD,e9_=thL,e9w=thx,e9y=thS,e9T=thJ,e9v=(eEg(),e9i),e9N=e9c,e9g=e9r,e9A=e9o,e9L=e9u}function eCx(e){var t,n,r,i,a,o,s,u;for(u=new Zr,s=new fz(e.a);s.a0&&t=0)return!1;if(t.p=n.b,P_(n.e,t),i==(eEn(),e8D)||i==e8P){for(o=new fz(t.j);o.a1||-1==o)&&(a|=16),(i.Bb&eZ1)!=0&&(a|=64)),(n.Bb&eH3)!=0&&(a|=eJq),a|=eXt):M4(t,457)?a|=512:(r=t.Bj())&&(1&r.i)!=0&&(a|=256),(512&e.Bb)!=0&&(a|=128),a}function eCG(e,t){var n,r,i,a,o;for(i=0,e=null==e?eUg:(BJ(e),e);ie.d[s.p]&&(n+=qq(e.b,a),Vw(e.a,ell(a))):++o;for(n+=e.b.d*o;!gY(e.a);)eek(e.b,Pp(Yn(e.a),19).a)}return n}function eCV(e,t){var n;return e.f==tvm?(n=Ur(QZ((eSp(),tvc),t)),e.e?4==n&&t!=(ex$(),tvw)&&t!=(ex$(),tvg)&&t!=(ex$(),tvv)&&t!=(ex$(),tvy):2==n):!!(e.d&&(e.d.Hc(t)||e.d.Hc(Wk(QZ((eSp(),tvc),t)))||e.d.Hc(eR3((eSp(),tvc),e.b,t))))||!!(e.f&&eOq((eSp(),e.f),U$(QZ(tvc,t))))&&(n=Ur(QZ(tvc,t)),e.e?4==n:2==n)}function eCq(e,t,n,r){var i,a,o,s,u,c,l,f;return u=(o=Pp(eT8(n,(eBB(),th3)),8)).a,l=o.b+e,(i=eB4.Math.atan2(l,u))<0&&(i+=eV7),(i+=t)>eV7&&(i-=eV7),c=(s=Pp(eT8(r,th3),8)).a,f=s.b+e,(a=eB4.Math.atan2(f,c))<0&&(a+=eV7),(a+=t)>eV7&&(a-=eV7),Mc(),enj(1e-10),1e-10>=eB4.Math.abs(i-a)||i==a||isNaN(i)&&isNaN(a)?0:ia?1:Te(isNaN(i),isNaN(a))}function eCZ(e){var t,n,r,i,a,o,s;for(s=new p2,r=new fz(e.a.b);r.a=e.o)throw p7(new bj);s=t>>5,o=31&t,a=Fg(1,jE(Fg(o,1))),i?e.n[n][s]=WO(e.n[n][s],a):e.n[n][s]=WM(e.n[n][s],PN(a)),a=Fg(a,1),r?e.n[n][s]=WO(e.n[n][s],a):e.n[n][s]=WM(e.n[n][s],PN(a))}catch(u){if(u=eoa(u),M4(u,320))throw p7(new gE(ez_+e.o+"*"+e.p+ezE+t+eUd+n+ezS));throw p7(u)}}function eC1(e,t,n,r){var i,a,o;t&&(a=gP(LV(e_k(t,(eR6(),tcd))))+r,o=n+gP(LV(e_k(t,tcu)))/2,eo3(t,tcg,ell(jE(eap(eB4.Math.round(a))))),eo3(t,tcv,ell(jE(eap(eB4.Math.round(o))))),0==t.d.b||eC1(e,Pp(M2((i=epL(new hz(t).a.d,0),new hG(i))),86),n+gP(LV(e_k(t,tcu)))+e.a,r+gP(LV(e_k(t,tcc)))),null!=e_k(t,tcb)&&eC1(e,Pp(e_k(t,tcb),86),n,r))}function eC0(e,t){var n,r,i,a,o,s,u,c,l,f,d;for(i=2*gP(LV(e_k(u=Bq(t.a),(eBy(),toI)))),l=gP(LV(e_k(u,toY))),c=eB4.Math.max(i,l),a=Je(tyx,eH5,25,t.f-t.c+1,15,1),r=-c,n=0,s=t.b.Kc();s.Ob();)o=Pp(s.Pb(),10),r+=e.a[o.c.p]+c,a[n++]=r;for(r+=e.a[t.a.c.p]+c,a[n++]=r,d=new fz(t.e);d.a0&&(r=(e.n||(e.n=new FQ(e6S,e,1,7)),Pp(etj(e.n,0),137)).a)&&xM(xM((t.a+=' "',t),r),'"')),xM(yW(xM(yW(xM(yW(xM(yW((t.a+=" (",t),e.i),","),e.j)," | "),e.g),","),e.f),")"),t.a)}function eC5(e){var t,n,r;return(64&e.Db)!=0?eEp(e):(t=new O0(eZG),(n=e.k)?xM(xM((t.a+=' "',t),n),'"'):(e.n||(e.n=new FQ(e6S,e,1,7)),e.n.i>0&&(r=(e.n||(e.n=new FQ(e6S,e,1,7)),Pp(etj(e.n,0),137)).a)&&xM(xM((t.a+=' "',t),r),'"')),xM(yW(xM(yW(xM(yW(xM(yW((t.a+=" (",t),e.i),","),e.j)," | "),e.g),","),e.f),")"),t.a)}function eC6(e,t){var n,r,i,a,o,s,u;if(null==t||0==t.length)return null;if(!(i=Pp(zg(e.a,t),149))){for(r=(s=new fT(e.b).a.vc().Kc(),new fN(s));r.a.Ob();)if(o=(n=(a=Pp(r.a.Pb(),42),Pp(a.dd(),149))).c,u=t.length,IE(o.substr(o.length-u,u),t)&&(t.length==o.length||46==UI(o,o.length-t.length-1))){if(i)return null;i=n}i&&Ge(e.a,t,i)}return i}function eC9(e,t){var n,r,i,a;return(n=new eD,i=(r=Pp(qE(UQ(new R1(null,new Gq(e.f,16)),n),Qz(new q,new Z,new er,new ei,eow(vx(e2L,1),eU4,132,0,[(eum(),e2H),e2U]))),21)).gc(),a=(r=Pp(qE(UQ(new R1(null,new Gq(t.f,16)),n),Qz(new q,new Z,new er,new ei,eow(vx(e2L,1),eU4,132,0,[e2H,e2U]))),21)).gc(),ii.p?(ekv(a,tbj),a.d&&(s=a.o.b,t=a.a.b,a.a.b=s-t)):a.j==tbj&&i.p>e.p&&(ekv(a,tbw),a.d&&(s=a.o.b,t=a.a.b,a.a.b=-(s-t)));break}return i}function eIe(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p;if(a=n,n1)&&(r=new kl(i,n.b),P7(t.a,r)),enD(t.a,eow(vx(e50,1),eUP,8,0,[d,f]))}function eIy(e,t,n){var r,i,a,o,s,u;if(!t)return null;if(!(n<=-1))return ebY(Pp(ee2(e.Tg(),n),18));if(r=ee2(t.Tg(),-1-n),M4(r,99))return Pp(r,18);for(s=0,u=(o=Pp(t.ah(r),153)).gc();s0){for(i=u.length;i>0&&""==u[i-1];)--i;i=t.d.a.gc()){o=t.a.c,s=t.a.c+t.a.b,u=new kl(o+(s-o)/2,t.b),P7(Pp(t.d.a.ec().Kc().Pb(),17).a,u);continue}if((i=Pp(Bp(t.c,n),459)).b||i.c){eIv(e,n,t);continue}(a=e.d==(euy(),tsW)&&(i.d||i.e)&&exJ(e,t)&&1>=t.d.a.gc())?eFd(n,t):eL5(e,n,t)}t.k&&qX(t.d,new nn)}}function eIq(e,t,n,r,i,a){var o,s,u,c,l,f,d,h,p,b,m,g,v,y;for(s=(r+i)/2+(d=a),m=n*eB4.Math.cos(s),g=n*eB4.Math.sin(s),v=m-t.g/2,y=g-t.f/2,eno(t,v),ens(t,y),f=e.a.jg(t),(b=2*eB4.Math.acos(n/n+e.c))=40)&&eNo(e),eRi(e),eA3(e),n=elM(e),r=0;n&&r0&&P7(e.f,a)):(e.c[o]-=c+1,e.c[o]<=0&&e.a[o]>0&&P7(e.e,a))))}function eI1(e){var t,n,r,i,a,o,s,u,c;for(s=new yB(Pp(Y9(new eP),62)),c=eH1,n=new fz(e.d);n.a=0&&un?t:n;c<=f;++c)c==n?s=r++:(a=i[c],l=p.rl(a.ak()),c==t&&(u=c!=f||l?r:r-1),l&&++r);return d=Pp(elR(e,t,n),72),s!=u&&bz(e,new JU(e.e,7,o,ell(s),h.dd(),u)),d}return Pp(elR(e,t,n),72)}function eDe(e,t){var n,r,i,a,o,s,u;for(ewG(t,"Port order processing",1),u=Pp(e_k(e,(eBy(),tom)),421),r=new fz(e.b);r.a=0&&(!(s=egy(e,o))||(c<22?u.l|=1<>>1,o.m=l>>>1|(1&f)<<21,o.l=d>>>1|(1&l)<<21,--c;return n&&esh(u),a&&(r?(e0A=eoQ(e),i&&(e0A=eor(e0A,(Q2(),e0I)))):e0A=Mk(e.l,e.m,e.h)),u}function eDi(e,t){var n,r,i,a,o,s,u,c,l,f;for(c=e.e[t.c.p][t.p]+1,u=t.c.a.c.length+1,s=new fz(e.a);s.a0&&(GV(0,e.length),45==e.charCodeAt(0)||(GV(0,e.length),43==e.charCodeAt(0)))?1:0;rn)throw p7(new vo(eHJ+e+'"'));return s}function eDo(e){var t,n,r,i,a,o,s;for(o=new _n,a=new fz(e.a);a.a1)&&1==t&&Pp(e.a[e.b],10).k==(eEn(),e8I)?eD3(Pp(e.a[e.b],10),(egF(),tpV)):r&&(!n||(e.c-e.b&e.a.length-1)>1)&&1==t&&Pp(e.a[e.c-1&e.a.length-1],10).k==(eEn(),e8I)?eD3(Pp(e.a[e.c-1&e.a.length-1],10),(egF(),tpq)):(e.c-e.b&e.a.length-1)==2?(eD3(Pp(eso(e),10),(egF(),tpV)),eD3(Pp(eso(e),10),tpq)):eM8(e,i),qr(e)}function eDf(e,t,n){var r,i,a,o,s;for(a=0,i=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));i.e!=i.i.gc();)r=Pp(epH(i),33),o="",0==(r.n||(r.n=new FQ(e6S,r,1,7)),r.n).i||(o=Pp(etj((r.n||(r.n=new FQ(e6S,r,1,7)),r.n),0),137).a),eaW(s=new esH(a++,t,o),r),eo3(s,(eR6(),tcl),r),s.e.b=r.j+r.f/2,s.f.a=eB4.Math.max(r.g,1),s.e.a=r.i+r.g/2,s.f.b=eB4.Math.max(r.f,1),P7(t.b,s),eS9(n.f,r,s)}function eDd(e){var t,n,r,i,a;r=Pp(e_k(e,(eBU(),tnc)),33),a=Pp(eT8(r,(eBy(),ta4)),174).Hc((ed6(),tbq)),!e.e&&(i=Pp(e_k(e,tt3),21),t=new kl(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),i.Hc((eLR(),ttw))?(ebu(r,tol,(ewf(),tbo)),eYx(r,t.a,t.b,!1,!0)):gN(LK(eT8(r,ta5)))||eYx(r,t.a,t.b,!0,!0)),a?ebu(r,ta4,el9(tbq)):ebu(r,ta4,(n=Pp(yw(e6o),9),new I1(n,Pp(CY(n,n.length),9),0)))}function eDh(e,t,n){var r,i,a,o;if(t[0]>=e.length)return n.o=0,!0;switch(UI(e,t[0])){case 43:i=1;break;case 45:i=-1;break;default:return n.o=0,!0}if(++t[0],a=t[0],0==(o=exf(e,t))&&t[0]==a)return!1;if(t[0]=0&&s!=n&&(a=new FX(e,1,s,o,null),r?r.Ei(a):r=a),n>=0&&(a=new FX(e,1,n,s==n?o:null,t),r?r.Ei(a):r=a)),r}function eDv(e){var t,n,r;if(null==e.b){if(r=new vs,null!=e.i&&(xk(r,e.i),r.a+=":"),(256&e.f)!=0){for((256&e.f)!=0&&null!=e.a&&(Hb(e.i)||(r.a+="//"),xk(r,e.a)),null!=e.d&&(r.a+="/",xk(r,e.d)),(16&e.f)!=0&&(r.a+="/"),t=0,n=e.j.length;td)&&(f=(u=ePI(r,d,!1)).a,l+s+f<=t.b&&(JR(n,a-n.s),n.c=!0,JR(r,a-n.s),ebP(r,n.s,n.t+n.d+s),r.k=!0,eiV(n.q,r),h=!0,i&&(enN(t,r),r.j=t,e.c.length>o&&(eva((GK(o,e.c.length),Pp(e.c[o],200)),r),0==(GK(o,e.c.length),Pp(e.c[o],200)).a.c.length&&ZV(e,o)))),h)}function eDx(e,t){var n,r,i,a,o,s;if(ewG(t,"Partition midprocessing",1),i=new zu,_r(UJ(new R1(null,new Gq(e.a,16)),new nK),new dQ(i)),0!=i.d){for(r=(s=Pp(qE(GU((a=i.i,new R1(null,(a||(i.i=new OC(i,i.c))).Nc()))),JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[(eum(),e2U)]))),15)).Kc(),n=Pp(r.Pb(),19);r.Ob();)o=Pp(r.Pb(),19),eOR(Pp(Zq(i,n),21),Pp(Zq(i,o),21)),n=o;eEj(t)}}function eDT(e,t,n){var r,i,a,o,s,u,c,l;if(0==t.p){for(t.p=1,(o=n)||(i=new p0,a=(r=Pp(yw(e6a),9),new I1(r,Pp(CY(r,r.length),9),0)),o=new kD(i,a)),Pp(o.a,15).Fc(t),t.k==(eEn(),e8C)&&Pp(o.b,21).Fc(Pp(e_k(t,(eBU(),tt1)),61)),u=new fz(t.j);u.a0){if(i=Pp(e.Ab.g,1934),null==t){for(a=0;a1)for(r=new fz(i);r.an.s&&ss&&(s=i,f.c=Je(e1R,eUp,1,0,5,1)),i==s&&P_(f,new kD(n.c.i,n)));Hj(),Mv(f,e.c),jO(e.b,u.p,f)}}function eDR(e,t){var n,r,i,a,o,s,u,l,f;for(o=new fz(t.b);o.as&&(s=i,f.c=Je(e1R,eUp,1,0,5,1)),i==s&&P_(f,new kD(n.d.i,n)));Hj(),Mv(f,e.c),jO(e.f,u.p,f)}}function eDj(e){_Y(e,new ewB(vQ(vq(vJ(vX(new oc,eZn),"ELK Box"),"Algorithm for packing of unconnected boxes, i.e. graphs without edges."),new oA))),KE(e,eZn,ezW,td$),KE(e,eZn,eGi,15),KE(e,eZn,eGr,ell(0)),KE(e,eZn,eqC,epB(tdj)),KE(e,eZn,eGh,epB(tdY)),KE(e,eZn,eGd,epB(tdU)),KE(e,eZn,ezG,eZt),KE(e,eZn,eGu,epB(tdF)),KE(e,eZn,eGM,epB(tdB)),KE(e,eZn,eZr,epB(tdP)),KE(e,eZn,eVg,epB(tdR))}function eDF(e,t){var n,r,i,a,o,s,u,c,l;if(o=(i=e.i).o.a,a=i.o.b,o<=0&&a<=0)return eYu(),tbF;switch(c=e.n.a,l=e.n.b,s=e.o.a,n=e.o.b,t.g){case 2:case 1:if(c<0)return eYu(),tbY;if(c+s>o)return eYu(),tby;break;case 4:case 3:if(l<0)return eYu(),tbw;if(l+n>a)return eYu(),tbj}return(u=(c+s/2)/o)+(r=(l+n/2)/a)<=1&&u-r<=0?(eYu(),tbY):u+r>=1&&u-r>=0?(eYu(),tby):r<.5?(eYu(),tbw):(eYu(),tbj)}function eDY(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b;for(n=!1,l=gP(LV(e_k(t,(eBy(),toF)))),p=eHe*l,i=new fz(t.b);i.a(u=s.n.b-s.d.d+d.a)+p&&(b=f.g+d.g,d.a=(d.g*d.a+f.g*f.a)/b,d.g=b,f.f=d,n=!0)),a=s,f=d;return n}function eDB(e,t,n,r,i,a,o){var s,u,c,l,f,d;for(d=new TE,c=t.Kc();c.Ob();)for(s=Pp(c.Pb(),839),f=new fz(s.wf());f.a0?s.a?i>(c=s.b.rf().b)&&(e.v||1==s.c.d.c.length?(o=(i-c)/2,s.d.d=o,s.d.a=o):(r=((n=Pp(RJ(s.c.d,0),181).rf().b)-c)/2,s.d.d=eB4.Math.max(0,r),s.d.a=i-r-c)):s.d.a=e.t+i:FY(e.u)&&((a=ew1(s.b)).d<0&&(s.d.d=-a.d),a.d+a.a>s.b.rf().b&&(s.d.a=a.d+a.a-s.b.rf().b))}function eD$(e,t){var n;switch(eeg(e)){case 6:return xd(t);case 7:return xf(t);case 8:return xl(t);case 3:return Array.isArray(t)&&!((n=eeg(t))>=14&&n<=16);case 11:return null!=t&&typeof t===eUs;case 12:return null!=t&&(typeof t===eUr||typeof t==eUs);case 0:return ebs(t,e.__elementTypeId$);case 2:return YS(t)&&t.im!==O;case 1:return YS(t)&&t.im!==O||ebs(t,e.__elementTypeId$);default:return!0}}function eDz(e,t){var n,r,i,a;return(r=eB4.Math.min(eB4.Math.abs(e.c-(t.c+t.b)),eB4.Math.abs(e.c+e.b-t.c)),a=eB4.Math.min(eB4.Math.abs(e.d-(t.d+t.a)),eB4.Math.abs(e.d+e.a-t.d)),(n=eB4.Math.abs(e.c+e.b/2-(t.c+t.b/2)))>e.b/2+t.b/2||(i=eB4.Math.abs(e.d+e.a/2-(t.d+t.a/2)))>e.a/2+t.a/2)?1:0==n&&0==i?0:0==n?a/i+1:0==i?r/n+1:eB4.Math.min(r/n,a/i)+1}function eDG(e,t){var n,r,i,a,o,s;return(i=enR(e),s=enR(t),i!=s)?it.f?1:0:(r=e.e-t.e,(n=(e.d>0?e.d:eB4.Math.floor((e.a-1)*eH9)+1)-(t.d>0?t.d:eB4.Math.floor((t.a-1)*eH9)+1))>r+1)?i:n0&&(o=eeD(o,eN4(r))),ehI(a,o))}function eDW(e,t){var n,r,i,a,o,s,u;for(a=0,s=0,u=0,i=new fz(e.f.e);i.a0&&e.d!=(QJ(),e95)&&(s+=o*(r.d.a+e.a[t.b][r.b]*(t.d.a-r.d.a)/n)),n>0&&e.d!=(QJ(),e93)&&(u+=o*(r.d.b+e.a[t.b][r.b]*(t.d.b-r.d.b)/n)));switch(e.d.g){case 1:return new kl(s/a,t.d.b);case 2:return new kl(t.d.a,u/a);default:return new kl(s/a,u/a)}}function eDK(e,t){var n,r,i,a,o;if(euv(),o=Pp(e_k(e.i,(eBy(),tol)),98),0!=(a=e.j.g-t.j.g)||!(o==(ewf(),tba)||o==tbs||o==tbo))return 0;if(o==(ewf(),tba)&&(n=Pp(e_k(e,tof),19),r=Pp(e_k(t,tof),19),n&&r&&0!=(i=n.a-r.a)))return i;switch(e.j.g){case 1:return elN(e.n.a,t.n.a);case 2:return elN(e.n.b,t.n.b);case 3:return elN(t.n.a,e.n.a);case 4:return elN(t.n.b,e.n.b);default:throw p7(new gC(eGz))}}function eDV(e){var t,n,r,i,a,o;for(n=(e.a||(e.a=new O_(e6h,e,5)),e.a).i+2,o=new XM(n),P_(o,new kl(e.j,e.k)),_r(new R1(null,(e.a||(e.a=new O_(e6h,e,5)),new Gq(e.a,16))),new h6(o)),P_(o,new kl(e.b,e.c)),t=1;t0&&(eoY(u,!1,(ec3(),tpm)),eoY(u,!0,tpg)),ety(t.g,new E4(e,n)),Um(e.g,t,n)}function eDZ(){var e;for(e=2,eDZ=A,e0$=eow(vx(ty_,1),eHT,25,15,[-1,-1,30,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5]),e0z=Je(ty_,eHT,25,37,15,1),e0G=eow(vx(ty_,1),eHT,25,15,[-1,-1,63,40,32,28,25,23,21,20,19,19,18,18,17,17,16,16,16,15,15,15,15,14,14,14,14,14,14,13,13,13,13,13,13,13,13]),e0W=Je(tyS,eH2,25,37,14,1);e<=36;e++)e0z[e]=zy(eB4.Math.pow(e,e0$[e])),e0W[e]=eyt(eUY,e0z[e])}function eDX(e){var t;if(1!=(e.a||(e.a=new FQ(e6v,e,6,6)),e.a).i)throw p7(new gL(eZC+(e.a||(e.a=new FQ(e6v,e,6,6)),e.a).i));return t=new mE,eoo(Pp(etj((e.b||(e.b=new Ih(e6m,e,4,7)),e.b),0),82))&&er7(t,eBE(e,eoo(Pp(etj((e.b||(e.b=new Ih(e6m,e,4,7)),e.b),0),82)),!1)),eoo(Pp(etj((e.c||(e.c=new Ih(e6m,e,5,8)),e.c),0),82))&&er7(t,eBE(e,eoo(Pp(etj((e.c||(e.c=new Ih(e6m,e,5,8)),e.c),0),82)),!0)),t}function eDJ(e,t){var n,r,i,a,o;for(i=t.d?e.a.c==(zs(),tuw)?efu(t.b):efc(t.b):e.a.c==(zs(),tuy)?efu(t.b):efc(t.b),a=!1,r=new Fa(OH(i.a.Kc(),new c));eTk(r);)if(n=Pp(ZC(r),17),!(!(o=gN(e.a.f[e.a.g[t.b.p].p]))&&!q8(n)&&n.c.i.c==n.d.i.c||gN(e.a.n[e.a.g[t.b.p].p])||gN(e.a.n[e.a.g[t.b.p].p]))&&(a=!0,w0(e.b,e.a.g[emN(n,t.b).p])))return t.c=!0,t.a=n,t;return t.c=a,t.a=null,t}function eDQ(e,t,n,r,i){var a,o,s,u,c,l,f;for(Hj(),Mv(e,new oU),s=new KB(e,0),f=new p0,a=0;s.b2*a?(l=new etD(f),c=jl(o)/jc(o),u=eY9(l,t,new mp,n,r,i,c),C5(xB(l.e),u),f.c=Je(e1R,eUp,1,0,5,1),a=0,f.c[f.c.length]=l,f.c[f.c.length]=o,a=jl(l)*jc(l)+jl(o)*jc(o)):(f.c[f.c.length]=o,a+=jl(o)*jc(o));return f}function eD1(e,t,n){var r,i,a,o,s,u,c;if(0==(r=n.gc()))return!1;if(e.ej()){if(u=e.fj(),edu(e,t,n),o=1==r?e.Zi(3,null,n.Kc().Pb(),t,u):e.Zi(5,null,n,t,u),e.bj()){for(s=r<100?null:new yf(r),a=t+r,i=t;i0){for(o=0;o>16==-15&&e.Cb.nh()&&QU(new JB(e.Cb,9,13,n,e.c,ebv(QX(Pp(e.Cb,59)),e))):M4(e.Cb,88)&&e.Db>>16==-23&&e.Cb.nh()&&(M4(t=e.c,88)||(t=(eBK(),tgI)),M4(n,88)||(n=(eBK(),tgI)),QU(new JB(e.Cb,9,10,n,t,ebv(qt(Pp(e.Cb,26)),e)))))),e.c}function eD6(e,t){var n,r,i,a,o,s,u,c,l,f;for(ewG(t,"Hypernodes processing",1),i=new fz(e.b);i.an)return i}function eNe(e,t){var n,r,i;r=0!=eMU(e.d,1),(gN(LK(e_k(t.j,(eBU(),tt2))))||gN(LK(e_k(t.j,tnS))))&&xc(e_k(t.j,(eBy(),ti9)))!==xc((esn(),tsM))?r=gN(LK(e_k(t.j,tt2))):t.c.Tf(t.e,r),eAb(e,t,r,!0),gN(LK(e_k(t.j,tnS)))&&eo3(t.j,tnS,(OQ(),!1)),gN(LK(e_k(t.j,tt2)))&&(eo3(t.j,tt2,(OQ(),!1)),eo3(t.j,tnS,!0)),n=eSY(e,t);do{if(er0(e),0==n)return 0;r=!r,i=n,eAb(e,t,r,!1),n=eSY(e,t)}while(i>n)return i}function eNt(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p;if(t==n)return!0;if(t=eTE(e,t),n=eTE(e,n),!(r=eb1(t)))return(s=t.e)==(h=n.e);if((l=eb1(n))!=r)return!!l&&(u=r.Dj())==(p=l.Dj())&&null!=u;if(a=(o=(t.d||(t.d=new O_(tgr,t,1)),t.d)).i,d=(n.d||(n.d=new O_(tgr,n,1)),n.d),a==d.i){for(c=0;c0,s=efC(t,a),n?Ag(s.b,t):Ag(s.g,t),1==efv(s).c.length&&qQ(r,s,r.c.b,r.c),i=new kD(a,t),Vw(e.o,i),QA(e.e.a,a))}function eNs(e,t){var n,r,i,a,o,s,u;return r=eB4.Math.abs(FB(e.b).a-FB(t.b).a),s=eB4.Math.abs(FB(e.b).b-FB(t.b).b),i=0,u=0,n=1,o=1,r>e.b.b/2+t.b.b/2&&(n=1-(i=eB4.Math.min(eB4.Math.abs(e.b.c-(t.b.c+t.b.b)),eB4.Math.abs(e.b.c+e.b.b-t.b.c)))/r),s>e.b.a/2+t.b.a/2&&(o=1-(u=eB4.Math.min(eB4.Math.abs(e.b.d-(t.b.d+t.b.a)),eB4.Math.abs(e.b.d+e.b.a-t.b.d)))/s),(1-(a=eB4.Math.min(n,o)))*eB4.Math.sqrt(r*r+s*s)}function eNu(e){var t,n,r,i;for(eFX(e,e.e,e.f,(zo(),tuq),!0,e.c,e.i),eFX(e,e.e,e.f,tuq,!1,e.c,e.i),eFX(e,e.e,e.f,tuZ,!0,e.c,e.i),eFX(e,e.e,e.f,tuZ,!1,e.c,e.i),eNd(e,e.c,e.e,e.f,e.i),r=new KB(e.i,0);r.b=65;n--)tvJ[n]=n-65<<24>>24;for(r=122;r>=97;r--)tvJ[r]=r-97+26<<24>>24;for(i=57;i>=48;i--)tvJ[i]=i-48+52<<24>>24;for(a=0,tvJ[43]=62,tvJ[47]=63;a<=25;a++)tvQ[a]=65+a&eHd;for(o=26,u=0;o<=51;++o,u++)tvQ[o]=97+u&eHd;for(e=52,s=0;e<=61;++e,s++)tvQ[e]=48+s&eHd;tvQ[62]=43,tvQ[63]=47}function eNf(e,t){var n,r,i,a,o,s,u,c,l,f,d,h;if(e.dc())return new yb;for(c=0,f=0,i=e.Kc();i.Ob();)a=(r=Pp(i.Pb(),37)).f,c=eB4.Math.max(c,a.a),f+=a.a*a.b;for(c=eB4.Math.max(c,eB4.Math.sqrt(f)*gP(LV(e_k(Pp(e.Kc().Pb(),37),(eBy(),tiX))))),d=0,h=0,u=0,n=t,s=e.Kc();s.Ob();)d+(l=(o=Pp(s.Pb(),37)).f).a>c&&(d=0,h+=u+t,u=0),eIn(o,d,h),n=eB4.Math.max(n,d+l.a),u=eB4.Math.max(u,l.b),d+=l.a+t;return new kl(n+t,h+u+t)}function eNd(e,t,n,r,i){var a,o,s,u,c,l,f;for(o=new fz(t);o.aa)return eYu(),tby;break;case 4:case 3:if(u<0)return eYu(),tbw;if(u+e.f>i)return eYu(),tbj}return(o=(s+e.g/2)/a)+(n=(u+e.f/2)/i)<=1&&o-n<=0?(eYu(),tbY):o+n>=1&&o-n>=0?(eYu(),tby):n<.5?(eYu(),tbw):(eYu(),tbj)}function eNp(e,t,n,r,i){var a,o;if(a=eft(WM(t[0],eH8),WM(r[0],eH8)),e[0]=jE(a),a=Fv(a,32),n>=i){for(o=1;o0&&(i.b[o++]=0,i.b[o++]=a.b[0]-1),t=1;t0&&(l0(u,u.d-i.d),i.c==(Xa(),tuU)&&lQ(u,u.a-i.d),u.d<=0&&u.i>0&&qQ(t,u,t.c.b,t.c));for(a=new fz(e.f);a.a0&&(l2(s,s.i-i.d),i.c==(Xa(),tuU)&&l1(s,s.b-i.d),s.i<=0&&s.d>0&&qQ(n,s,n.c.b,n.c))}function eNv(e,t,n){var r,i,a,o,s,u,c,l;for(ewG(n,"Processor compute fanout",1),Yy(e.b),Yy(e.a),s=null,a=epL(t.b,0);!s&&a.b!=a.d.c;)gN(LK(e_k(c=Pp(Vv(a),86),(eR6(),tcm))))&&(s=c);for(qQ(u=new _n,s,u.c.b,u.c),eYc(e,u),l=epL(t.b,0);l.b!=l.d.c;)o=Lq(e_k(c=Pp(Vv(l),86),(eR6(),tca))),eo3(c,tci,ell(i=null!=zg(e.b,o)?Pp(zg(e.b,o),19).a:0)),eo3(c,tcn,ell(r=1+(null!=zg(e.a,o)?Pp(zg(e.a,o),19).a:0)));eEj(n)}function eNy(e,t,n,r,i){var a,o,s,u,c,l,f,d,h,p;for(u=0,d=eyG(e,n);u0),r.a.Xb(r.c=--r.b),f>d+u&&BH(r);for(o=new fz(h);o.a0),r.a.Xb(r.c=--r.b)}}function eNw(){var e,t,n,r,i,a;if(eBG(),tyg)return tyg;for(e=(++tyv,new WZ(4)),ePR(e,eYB(e1_,!0)),ej0(e,eYB("M",!0)),ej0(e,eYB("C",!0)),a=(++tyv,new WZ(4)),r=0;r<11;r++)eLw(a,r,r);return t=(++tyv,new WZ(4)),ePR(t,eYB("M",!0)),eLw(t,4448,4607),eLw(t,65438,65439),i=(++tyv,new Mr(2)),eRv(i,e),eRv(i,tye),(n=(++tyv,new Mr(2))).$l(jS(a,eYB("L",!0))),n.$l(t),n=(++tyv,new qa(3,n)),tyg=n=(++tyv,new YD(i,n))}function eN_(e){var t,n;if(t=Lq(eT8(e,(eBB(),tdQ))),!eae(t,e)&&!X2(e,th6)&&(0!=(e.a||(e.a=new FQ(e6k,e,10,11)),e.a).i||gN(LK(eT8(e,thh))))){if(null==t||0==e_H(t).length){if(!eae(eG1,e))throw eFh(e,n=xM(xM(new O0("Unable to load default layout algorithm "),eG1)," for unconfigured node ")),p7(new gq(n.a))}else throw eFh(e,n=xM(xM(new O0("Layout algorithm '"),t),"' not found for ")),p7(new gq(n.a))}}function eNE(e){var t,n,r,i,a,o,s,u,c,l,f,d,h;if(n=e.i,t=e.n,0==e.b)for(h=n.c+t.b,d=n.b-t.b-t.c,o=e.a,u=0,l=o.length;u0&&(f-=r[0]+e.c,r[0]+=e.c),r[2]>0&&(f-=r[2]+e.c),r[1]=eB4.Math.max(r[1],f),jQ(e.a[1],n.c+t.b+r[0]-(r[1]-f)/2,r[1]);for(a=e.a,s=0,c=a.length;s0?(e.n.c.length-1)*e.i:0,r=new fz(e.n);r.a1)for(r=epL(i,0);r.b!=r.d.c;)for(n=Pp(Vv(r),231),a=0,u=new fz(n.e);u.a0&&(t[0]+=e.c,f-=t[0]),t[2]>0&&(f-=t[2]+e.c),t[1]=eB4.Math.max(t[1],f),j1(e.a[1],r.d+n.d+t[0]-(t[1]-f)/2,t[1]);else for(p=r.d+n.d,h=r.a-n.d-n.a,o=e.a,u=0,l=o.length;u=0&&a!=n)throw p7(new gL(eXB));for(u=0,i=0;u=efT(e.b.c,i.b.c+i.b.b)&&0>=efT(i.b.c,e.b.c+e.b.b)&&0>=efT(e.b.d,i.b.d+i.b.a)&&0>=efT(i.b.d,e.b.d+e.b.a)){if(0==efT(i.b.c,e.b.c+e.b.b)&&r.a<0||0==efT(i.b.c+i.b.b,e.b.c)&&r.a>0||0==efT(i.b.d,e.b.d+e.b.a)&&r.b<0||0==efT(i.b.d+i.b.a,e.b.d)&&r.b>0){s=0;break}}else s=eB4.Math.min(s,ekg(e,i,r));s=eB4.Math.min(s,eNC(e,a,s,r))}return s}function eNI(e,t){var n,r,i,a,o,s,u;if(e.b<2)throw p7(new gL("The vector chain must contain at least a source and a target point."));for(Tj(t,(i=(A6(0!=e.b),Pp(e.a.a.c,8))).a,i.b),u=new AF((t.a||(t.a=new O_(e6h,t,5)),t.a)),o=epL(e,1);o.agP(Ot(o.g,o.d[0]).a)?(A6(u.b>0),u.a.Xb(u.c=--u.b),CD(u,o),i=!0):s.e&&s.e.gc()>0&&(a=(s.e||(s.e=new p0),s.e).Mc(t),c=(s.e||(s.e=new p0),s.e).Mc(n),(a||c)&&((s.e||(s.e=new p0),s.e).Fc(o),++o.c));i||(r.c[r.c.length]=o)}function eNH(e){var t,n,r;if(TM(Pp(e_k(e,(eBy(),tol)),98)))for(n=new fz(e.j);n.a>>0).toString(16),n.length-2,n.length):e>=eH3?"\\v"+Az(n="0"+(t=e>>>0).toString(16),n.length-6,n.length):""+String.fromCharCode(e&eHd)}return r}function eNz(e,t){var n,r,i,a,o,s,u,c,l,f;if(o=e.e,0==(u=t.e))return e;if(0==o)return 0==t.e?t:new F7(-t.e,t.d,t.a);if((a=e.d)+(s=t.d)==2)return n=WM(e.a[0],eH8),r=WM(t.a[0],eH8),o<0&&(n=QC(n)),u<0&&(r=QC(r)),ep_(efe(n,r));if(-1==(i=a!=s?a>s?1:-1:es8(e.a,t.a,a)))f=-u,l=o==u?Z1(t.a,s,e.a,a):X7(t.a,s,e.a,a);else if(f=o,o==u){if(0==i)return eLQ(),e08;l=Z1(e.a,a,t.a,s)}else l=X7(e.a,a,t.a,s);return c=new F7(f,l.length,l),Ku(c),c}function eNG(e){var t,n,r,i,a,o;for(this.e=new p0,this.a=new p0,n=e.b-1;n<3;n++)Ls(e,0,Pp(ep3(e,0),8));if(e.b<4)throw p7(new gL("At (least dimension + 1) control points are necessary!"));for(this.b=3,this.d=!0,this.c=!1,eMO(this,e.b+this.b-1),o=new p0,a=new fz(this.e),t=0;t=t.o&&n.f<=t.f||.5*t.a<=n.f&&1.5*t.a>=n.f){if((o=Pp(RJ(t.n,t.n.c.length-1),211)).e+o.d+n.g+i<=r&&((a=Pp(RJ(t.n,t.n.c.length-1),211)).f-e.f+n.f<=e.b||1==e.a.c.length))return efg(t,n),!0;if(t.s+n.g<=r&&(t.t+t.d+n.f+i<=e.b||1==e.a.c.length))return P_(t.b,n),s=Pp(RJ(t.n,t.n.c.length-1),211),P_(t.n,new zO(t.s,s.f+s.a+t.i,t.i)),eml(Pp(RJ(t.n,t.n.c.length-1),211),n),eNk(t,n),!0}return!1}function eNV(e,t,n){var r,i,a,o;return e.ej()?(i=null,a=e.fj(),r=e.Zi(1,o=ees(e,t,n),n,t,a),e.bj()&&!(e.ni()&&null!=o?ecX(o,n):xc(o)===xc(n))?(null!=o&&(i=e.dj(o,i)),i=e.cj(n,i),e.ij()&&(i=e.lj(o,n,i)),i?(i.Ei(r),i.Fi()):e.$i(r)):(e.ij()&&(i=e.lj(o,n,i)),i?(i.Ei(r),i.Fi()):e.$i(r)),o):(o=ees(e,t,n),e.bj()&&!(e.ni()&&null!=o?ecX(o,n):xc(o)===xc(n))&&(i=null,null!=o&&(i=e.dj(o,null)),(i=e.cj(n,i))&&i.Fi()),o)}function eNq(e,t){var n,r,i,a,o,s,u,c;t%=24,e.q.getHours()!=t&&((r=new eB4.Date(e.q.getTime())).setDate(r.getDate()+1),(s=e.q.getTimezoneOffset()-r.getTimezoneOffset())>0&&(u=s/60|0,c=s%60,i=e.q.getDate(),(n=e.q.getHours())+u>=24&&++i,a=new eB4.Date(e.q.getFullYear(),e.q.getMonth(),i,t+u,e.q.getMinutes()+c,e.q.getSeconds(),e.q.getMilliseconds()),e.q.setTime(a.getTime()))),o=e.q.getTime(),e.q.setTime(o+36e5),e.q.getHours()!=t&&e.q.setTime(o)}function eNZ(e,t){var n,r,i,a,o;if(ewG(t,"Path-Like Graph Wrapping",1),0==e.b.c.length||(n=(o=(null==(i=new eTN(e)).i&&(i.i=eis(i,new iP)),gP(i.i)*i.f))/(null==i.i&&(i.i=eis(i,new iP)),gP(i.i)),i.b>n)){eEj(t);return}switch(Pp(e_k(e,(eBy(),toq)),337).g){case 2:a=new iF;break;case 0:a=new iO;break;default:a=new iY}if(r=a.Vf(e,i),!a.Wf())switch(Pp(e_k(e,to0),338).g){case 2:r=ekE(i,r);break;case 1:r=ewQ(i,r)}eRw(e,i,r),eEj(t)}function eNX(e,t){var n,r,i,a;if(GW(e.d,e.e),e.c.a.$b(),0!=gP(LV(e_k(t.j,(eBy(),ti3))))||0!=gP(LV(e_k(t.j,ti3))))for(n=ezq,xc(e_k(t.j,ti9))!==xc((esn(),tsM))&&eo3(t.j,(eBU(),tt2),(OQ(),!0)),a=Pp(e_k(t.j,to$),19).a,i=0;i(i=(GK(s+1,t.c.length),Pp(t.c[s+1],19)).a-r)&&++c,P_(o,(GK(s+c,t.c.length),Pp(t.c[s+c],19))),u+=(GK(s+c,t.c.length),Pp(t.c[s+c],19)).a-r,++n;n1&&(u>jl(s)*jc(s)/2||0==o.b)&&(f=new etD(d),l=jl(s)/jc(s),c=eY9(f,t,new mp,n,r,i,l),C5(xB(f.e),c),s=f,h.c[h.c.length]=f,u=0,d.c=Je(e1R,eUp,1,0,5,1)));return eoc(h,d),h}function eN2(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p,b;if(n.mh(t)&&(l=(h=t)?Pp(r,49).xh(h):null)){if(b=n.bh(t,e.a),(p=t.t)>1||-1==p){if(f=Pp(b,69),d=Pp(l,69),f.dc())d.$b();else for(o=!!ebY(t),a=0,s=e.a?f.Kc():f.Zh();s.Ob();)c=Pp(s.Pb(),56),(i=Pp(eef(e,c),56))?(o?-1==(u=d.Xc(i))?d.Xh(a,i):a!=u&&d.ji(a,i):d.Xh(a,i),++a):e.b&&!o&&(d.Xh(a,c),++a)}else null==b?l.Wb(null):null==(i=eef(e,b))?e.b&&!ebY(t)&&l.Wb(b):l.Wb(i)}}function eN3(e,t){var n,r,i,a,o,s,u,l;for(n=new nf,i=new Fa(OH(efu(t).a.Kc(),new c));eTk(i);)if(r=Pp(ZC(i),17),!q8(r)&&ewg(s=r.c.i,e8q)){if(-1==(l=eCu(e,s,e8q,e8V)))continue;n.b=eB4.Math.max(n.b,l),n.a||(n.a=new p0),P_(n.a,s)}for(o=new Fa(OH(efc(t).a.Kc(),new c));eTk(o);)if(a=Pp(ZC(o),17),!q8(a)&&ewg(u=a.d.i,e8V)){if(-1==(l=eCu(e,u,e8V,e8q)))continue;n.d=eB4.Math.max(n.d,l),n.c||(n.c=new p0),P_(n.c,u)}return n}function eN4(e){var t,n,r,i;if(exX(),t=zy(e),e1e6)throw p7(new g_("power of ten too big"));if(e<=eUu)return ZA(exT(e2t[1],t),t);for(i=r=exT(e2t[1],eUu),n=eap(e-eUu),t=zy(e%eUu);ecd(n,eUu)>0;)i=eeD(i,r),n=efe(n,eUu);for(i=eeD(i,exT(e2t[1],t)),i=ZA(i,eUu),n=eap(e-eUu);ecd(n,eUu)>0;)i=ZA(i,eUu),n=efe(n,eUu);return ZA(i,t)}function eN5(e,t){var n,r,i,a,o,s,u,c,l;for(ewG(t,"Hierarchical port dummy size processing",1),u=new p0,l=new p0,n=2*(r=gP(LV(e_k(e,(eBy(),toA))))),a=new fz(e.b);a.ac&&r>c)l=s,c=gP(t.p[s.p])+gP(t.d[s.p])+s.o.b+s.d.a;else{i=!1,n.n&&P3(n,"bk node placement breaks on "+s+" which should have been after "+l);break}if(!i)break}return n.n&&P3(n,t+" is feasible: "+i),i}function ePr(e,t,n,r){var i,a,o,s,u,c,l;for(s=-1,l=new fz(e);l.a=m&&e.e[u.p]>p*e.b||y>=n*m)&&(d.c[d.c.length]=s,s=new p0,er7(o,a),a.a.$b(),c-=l,h=eB4.Math.max(h,c*e.b+b),c+=y,v=y,y=0,l=0,b=0);return new kD(h,d)}function ePs(e){var t,n,r,i,a,o,s,u,c,l,f,d,h;for(n=(c=new fT(e.c.b).a.vc().Kc(),new fN(c));n.a.Ob();)null==(i=(t=(s=Pp(n.a.Pb(),42),Pp(s.dd(),149))).a)&&(i=""),(r=L8(e.c,i))||0!=i.length||(r=ecj(e)),r&&!eds(r.c,t,!1)&&P7(r.c,t);for(o=epL(e.a,0);o.b!=o.d.c;)a=Pp(Vv(o),478),l=Zc(e.c,a.a),h=Zc(e.c,a.b),l&&h&&P7(l.c,new kD(h,a.c));for(HC(e.a),d=epL(e.b,0);d.b!=d.d.c;)f=Pp(Vv(d),478),t=L9(e.c,f.a),u=Zc(e.c,f.b),t&&u&&_U(t,u,f.c);HC(e.b)}function ePu(e,t,n){var r,i,a,o,s,u,c,l,f,d,h;a=new lD(e),o=new eg6,i=(Ze(o.g),Ze(o.j),Yy(o.b),Ze(o.d),Ze(o.i),Yy(o.k),Yy(o.c),Yy(o.e),h=ekH(o,a,null),eMA(o,a),h),t&&(s=ePA(c=new lD(t)),eEh(i,eow(vx(e5q,1),eUp,527,0,[s]))),d=!1,f=!1,n&&(eXW in(c=new lD(n)).a&&(d=zR(c,eXW).ge().a),eXK in c.a&&(f=zR(c,eXK).ge().a)),l=yr(eny(new mV,d),f),eER(new or,i,l),eXW in a.a&&ee3(a,eXW,null),(d||f)&&(eNj(l,u=new gu,d,f),ee3(a,eXW,u)),r=new pp(o),esA(new TY(i),r)}function ePc(e,t,n){var r,i,a,o,s,u,c,l,f;for(u=0,o=new evI,c=eow(vx(ty_,1),eHT,25,15,[0]),i=-1,a=0,r=0;u0){if(i<0&&l.a&&(i=u,a=c[0],r=0),i>=0){if(s=l.b,u==i&&0==(s-=r++))return 0;if(!eYw(t,c,l,s,o)){u=i-1,c[0]=a;continue}}else if(i=-1,!eYw(t,c,l,0,o))return 0}else{if(i=-1,32==UI(l.c,0)){if(f=c[0],eey(t,c),c[0]>f)continue}else if($D(t,l.c,c[0])){c[0]+=l.c.length;continue}return 0}return eYn(o,n)?c[0]:0}function ePl(e){var t,n,r,i,a,o,s,u;if(!e.f){if(u=new su,s=new su,null==(o=(t=tgz).a.zc(e,t))){for(a=new Ow($E(e));a.e!=a.i.gc();)i=Pp(epH(a),26),Y4(u,ePl(i));t.a.Bc(e),t.a.gc()}for(r=(e.s||(e.s=new FQ(tm6,e,21,17)),new Ow(e.s));r.e!=r.i.gc();)n=Pp(epH(r),170),M4(n,99)&&JL(s,Pp(n,18));euI(s),e.r=new PX(e,(Pp(etj(H9((BM(),tgv).o),6),18),s.i),s.g),Y4(u,e.r),euI(u),e.f=new xQ((Pp(etj(H9(tgv.o),5),18),u.i),u.g),Zd(e).b&=-3}return e.f}function ePf(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p;for(c=0,r=Je(ty_,eHT,25,o=e.o,15,1),i=Je(ty_,eHT,25,o,15,1),t=Je(ty_,eHT,25,n=e.p,15,1),a=Je(ty_,eHT,25,n,15,1);c=0&&!emy(e,l,f);)--f;i[l]=f}for(h=0;h=0&&!emy(e,s,p);)--s;a[p]=s}for(u=0;ut[d]&&dr[u]&&eCQ(e,u,d,!1,!0)}function ePd(e){var t,n,r,i,a,o,s,u;n=gN(LK(e_k(e,(eCk(),e9b)))),a=e.a.c.d,s=e.a.d.d,n?(o=Ol(C6(new kl(s.a,s.b),a),.5),u=Ol(MB(e.e),.5),t=C6(C5(new kl(a.a,a.b),o),u),Lf(e.d,t)):(i=gP(LV(e_k(e.a,e9I))),r=e.d,a.a>=s.a?a.b>=s.b?(r.a=s.a+(a.a-s.a)/2+i,r.b=s.b+(a.b-s.b)/2-i-e.e.b):(r.a=s.a+(a.a-s.a)/2+i,r.b=a.b+(s.b-a.b)/2+i):a.b>=s.b?(r.a=a.a+(s.a-a.a)/2+i,r.b=s.b+(a.b-s.b)/2+i):(r.a=a.a+(s.a-a.a)/2+i,r.b=a.b+(s.b-a.b)/2-i-e.e.b))}function ePh(e,t){var n,r,i,a,o,s,u;if(null==e)return null;if(0==(a=e.length))return"";for(u=Je(tyw,eHl,25,a,15,1),Ji(0,a,e.length),Ji(0,a,u.length),YF(e,0,a,u,0),n=null,s=t,i=0,o=0;i0?Az(n.a,0,a-1):"":e.substr(0,a-1):n?n.a:e}function ePp(e){_Y(e,new ewB(vQ(vq(vJ(vX(new oc,ezH),"ELK DisCo"),"Layouter for arranging unconnected subgraphs. The subgraphs themselves are, by default, not laid out."),new e4))),KE(e,ezH,ez$,epB(e67)),KE(e,ezH,ezz,epB(e63)),KE(e,ezH,ezG,epB(e6J)),KE(e,ezH,ezW,epB(e64)),KE(e,ezH,e$Q,epB(e69)),KE(e,ezH,e$1,epB(e66)),KE(e,ezH,e$J,epB(e68)),KE(e,ezH,e$0,epB(e65)),KE(e,ezH,ezj,epB(e61)),KE(e,ezH,ezF,epB(e6Q)),KE(e,ezH,ezY,epB(e60)),KE(e,ezH,ezB,epB(e62))}function ePb(e,t,n,r){var i,a,o,s,u,c,l,f,d;if(a=new eb$(e),lK(a,(eEn(),e8P)),eo3(a,(eBy(),tol),(ewf(),tbo)),i=0,t){for(o=new eES,eo3(o,(eBU(),tnc),t),eo3(a,tnc,t.i),ekv(o,(eYu(),tbY)),Gc(o,a),d=Kp(t.e),l=0,f=(c=d).length;lenR(e)?1:0,n=e.e,i=(r.length,eB4.Math.abs(zy(e.e)),new vl),1==t&&(i.a+="-"),e.e>0){if((n-=r.length-t)>=0){for(i.a+="0.";n>e0Z.length;n-=e0Z.length)RX(i,e0Z);CA(i,e0Z,zy(n)),xM(i,r.substr(t))}else n=t-n,xM(i,Az(r,t,zy(n))),i.a+=".",xM(i,xy(r,zy(n)))}else{for(xM(i,r.substr(t));n<-e0Z.length;n+=e0Z.length)RX(i,e0Z);CA(i,e0Z,zy(-n))}return i.a}function ePv(e,t,n,r){var i,a,o,s,u,c,l,f,d;return(c=(u=C6(new kl(n.a,n.b),e)).a*t.b-u.b*t.a,l=t.a*r.b-t.b*r.a,f=(u.a*r.b-u.b*r.a)/l,d=c/l,0!=l)?f>=0&&f<=1&&d>=0&&d<=1?C5(new kl(e.a,e.b),Ol(new kl(t.a,t.b),f)):null:0!=c?null:(a=Jh(e,i=C5(new kl(n.a,n.b),Ol(new kl(r.a,r.b),.5))),o=Jh(C5(new kl(e.a,e.b),t),i),s=.5*eB4.Math.sqrt(r.a*r.a+r.b*r.b),at.a&&(r.Hc((eyY(),tdW))?e.c.a+=(n.a-t.a)/2:r.Hc(tdV)&&(e.c.a+=n.a-t.a)),n.b>t.b&&(r.Hc((eyY(),tdZ))?e.c.b+=(n.b-t.b)/2:r.Hc(tdq)&&(e.c.b+=n.b-t.b)),Pp(e_k(e,(eBU(),tt3)),21).Hc((eLR(),ttw))&&(n.a>t.a||n.b>t.b))for(s=new fz(e.a);s.at.a&&(r.Hc((eyY(),tdW))?e.c.a+=(n.a-t.a)/2:r.Hc(tdV)&&(e.c.a+=n.a-t.a)),n.b>t.b&&(r.Hc((eyY(),tdZ))?e.c.b+=(n.b-t.b)/2:r.Hc(tdq)&&(e.c.b+=n.b-t.b)),Pp(e_k(e,(eBU(),tt3)),21).Hc((eLR(),ttw))&&(n.a>t.a||n.b>t.b))for(o=new fz(e.a);o.at&&(i=0,a+=l.b+n,f.c[f.c.length]=l,l=new W6(a,n),r=new es$(0,l.f,l,n),enN(l,r),i=0),0==r.b.c.length||u.f>=r.o&&u.f<=r.f||.5*r.a<=u.f&&1.5*r.a>=u.f?efg(r,u):(o=new es$(r.s+r.r+n,l.f,l,n),enN(l,o),efg(o,u)),i=u.i+u.g;return f.c[f.c.length]=l,f}function ePk(e){var t,n,r,i,a,o,s,u;if(!e.a){if(e.o=null,u=new pj(e),t=new sc,null==(s=(n=tgz).a.zc(e,n))){for(o=new Ow($E(e));o.e!=o.i.gc();)a=Pp(epH(o),26),Y4(u,ePk(a));n.a.Bc(e),n.a.gc()}for(i=(e.s||(e.s=new FQ(tm6,e,21,17)),new Ow(e.s));i.e!=i.i.gc();)r=Pp(epH(i),170),M4(r,322)&&JL(t,Pp(r,34));euI(t),e.k=new PZ(e,(Pp(etj(H9((BM(),tgv).o),7),18),t.i),t.g),Y4(u,e.k),euI(u),e.a=new xQ((Pp(etj(H9(tgv.o),4),18),u.i),u.g),Zd(e).b&=-2}return e.a}function ePx(e,t,n,r,i,a,o){var s,u,c,l,f,d;return f=!1,u=eO4(n.q,t.f+t.b-n.q.f),!((d=i-(n.q.e+u-o))=(GK(a,e.c.length),Pp(e.c[a],200)).e,(!((l=(s=ePI(r,d,!1)).a)>t.b)||!!c)&&((c||l<=t.b)&&(c&&l>t.b?(n.d=l,JR(n,eEP(n,l))):(eyC(n.q,u),n.c=!0),JR(r,i-(n.s+n.r)),ebP(r,n.q.e+n.q.d,t.f),enN(t,r),e.c.length>a&&(eva((GK(a,e.c.length),Pp(e.c[a],200)),r),0==(GK(a,e.c.length),Pp(e.c[a],200)).a.c.length&&ZV(e,a)),f=!0),f))}function ePT(e,t,n,r){var i,a,o,s,u,c,l;if(l=eAY(e.e.Tg(),t),i=0,a=Pp(e.g,119),u=null,_4(),Pp(t,66).Oj()){for(s=0;se.o.a&&(l=(u-e.o.a)/2,s.b=eB4.Math.max(s.b,l),s.c=eB4.Math.max(s.c,l))}}function ePA(e){var t,n,r,i,a,o,s,u;for(a=new W8,Tp(a,(eoM(),tdr)),r=(i=erG(e,Je(e17,eUP,2,0,6,1)),new fE(new g$(new wY(e,i).b)));r.b0?e.i:0)>t&&u>0&&(a=0,o+=u+e.i,i=eB4.Math.max(i,d),r+=u+e.i,u=0,d=0,n&&(++f,P_(e.n,new zO(e.s,o,e.i))),s=0),d+=c.g+(s>0?e.i:0),u=eB4.Math.max(u,c.f),n&&eml(Pp(RJ(e.n,f),211),c),a+=c.g+(s>0?e.i:0),++s;return i=eB4.Math.max(i,d),r+=u,n&&(e.r=i,e.d=r,egf(e.j)),new Hr(e.s,e.t,i,r)}function ePD(e,t,n,r,i){var a,o,s,u,c,l,f,d,h;if(wK(),Yh(e,"src"),Yh(n,"dest"),d=esF(e),u=esF(n),Pz((4&d.i)!=0,"srcType is not an array"),Pz((4&u.i)!=0,"destType is not an array"),f=d.c,o=u.c,Pz((1&f.i)!=0?f==o:(1&o.i)==0,"Array types don't match"),h=e.length,c=n.length,t<0||r<0||i<0||t+i>h||r+i>c)throw p7(new bE);if((1&f.i)==0&&d!=u){if(l=etG(e),a=etG(n),xc(e)===xc(n)&&tr;)Bc(a,s,l[--t]);else for(s=r+i;r0&&ekp(e,t,n,r,i,!0)}function ePN(){ePN=A,e07=eow(vx(ty_,1),eHT,25,15,[eHt,1162261467,eU2,1220703125,362797056,1977326743,eU2,387420489,eHK,214358881,429981696,815730721,1475789056,170859375,268435456,410338673,612220032,893871739,128e7,1801088541,113379904,148035889,191102976,244140625,308915776,387420489,481890304,594823321,729e6,887503681,eU2,1291467969,1544804416,1838265625,60466176]),e2e=eow(vx(ty_,1),eHT,25,15,[-1,-1,31,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5])}function ePP(e){var t,n,r,i,a,o,s,u;for(i=new fz(e.b);i.a=e.b.length?(a[i++]=o.b[r++],a[i++]=o.b[r++]):r>=o.b.length?(a[i++]=e.b[n++],a[i++]=e.b[n++]):o.b[r]0?e.i:0)),++t;for(efX(e.n,u),e.d=n,e.r=r,e.g=0,e.f=0,e.e=0,e.o=eHQ,e.p=eHQ,a=new fz(e.b);a.a0&&(i=(e.n||(e.n=new FQ(e6S,e,1,7)),Pp(etj(e.n,0),137)).a)&&xM(xM((t.a+=' "',t),i),'"')),(n=(e.b||(e.b=new Ih(e6m,e,4,7)),!(e.b.i<=1&&(e.c||(e.c=new Ih(e6m,e,5,8)),e.c.i<=1))))?(t.a+=" [",t):(t.a+=" ",t),xM(t,OU(new ve,new Ow(e.b))),n&&(t.a+="]"),t.a+=eGH,n&&(t.a+="["),xM(t,OU(new ve,new Ow(e.c))),n&&(t.a+="]"),t.a)}function ePB(e,t){var n,r,i,a,o,s,u;if(e.a){if(s=e.a.ne(),u=null,null!=s?t.a+=""+s:null!=(o=e.a.Dj())&&(-1!=(a=x7(o,e_n(91)))?(u=o.substr(a),t.a+=""+Az(null==o?eUg:(BJ(o),o),0,a)):t.a+=""+o),e.d&&0!=e.d.i){for(i=!0,t.a+="<",r=new Ow(e.d);r.e!=r.i.gc();)n=Pp(epH(r),87),i?i=!1:(t.a+=eUd,t),ePB(n,t);t.a+=">"}null!=u&&(t.a+=""+u)}else e.e?null!=(s=e.e.zb)&&(t.a+=""+s):(t.a+="?",e.b?(t.a+=" super ",ePB(e.b,t)):e.f&&(t.a+=" extends ",ePB(e.f,t)))}function ePU(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T;for(_=e.c,E=t.c,n=QI(_.a,e,0),r=QI(E.a,t,0),y=Pp(edE(e,(enY(),tsD)).Kc().Pb(),11),x=Pp(edE(e,tsN).Kc().Pb(),11),w=Pp(edE(t,tsD).Kc().Pb(),11),T=Pp(edE(t,tsN).Kc().Pb(),11),g=Kp(y.e),S=Kp(x.g),v=Kp(w.e),k=Kp(T.g),egU(e,r,E),l=0,p=(o=v).length;ll?new GT((Xa(),tuH),n,t,c-l):c>0&&l>0&&(new GT((Xa(),tuH),t,n,0),new GT(tuH,n,t,0))),o)}function ePz(e,t){var n,r,i,a,o,s;for(o=new esz(new fS(e.f.b).a);o.b;){if(a=etz(o),i=Pp(a.cd(),594),1==t){if(i.gf()!=(ec3(),tpy)&&i.gf()!=tpb)continue}else if(i.gf()!=(ec3(),tpm)&&i.gf()!=tpg)continue;switch(r=Pp(Pp(a.dd(),46).b,81),n=(s=Pp(Pp(a.dd(),46).a,189)).c,i.gf().g){case 2:r.g.c=e.e.a,r.g.b=eB4.Math.max(1,r.g.b+n);break;case 1:r.g.c=r.g.c+n,r.g.b=eB4.Math.max(1,r.g.b-n);break;case 4:r.g.d=e.e.b,r.g.a=eB4.Math.max(1,r.g.a+n);break;case 3:r.g.d=r.g.d+n,r.g.a=eB4.Math.max(1,r.g.a-n)}}}function ePG(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b;for(s=Je(ty_,eHT,25,t.b.c.length,15,1),c=Je(e4P,eU4,267,t.b.c.length,0,1),u=Je(e4N,eGW,10,t.b.c.length,0,1),f=e.a,d=0,h=f.length;d0&&u[r]&&(p=Mj(e.b,u[r],i)),b=eB4.Math.max(b,i.c.c.b+p);for(a=new fz(l.e);a.a1)throw p7(new gL(eQ$));u||(a=V4(t,r.Kc().Pb()),o.Fc(a))}return eo0(e,eSu(e,t,n),o)}function ePZ(e,t){var n,r,i,a;for(etY(t.b.j),_r(UQ(new R1(null,new Gq(t.d,16)),new iy),new iw),a=new fz(t.d);a.ae.o.b||(n=efr(e,tby),(s=t.d+t.a+(n.gc()-1)*o)>e.o.b)))}function eP5(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p;if(o=e.e,u=t.e,0==o)return t;if(0==u)return e;if((a=e.d)+(s=t.d)==2)return(n=WM(e.a[0],eH8),r=WM(t.a[0],eH8),o==u)?(p=jE(l=eft(n,r)),0==(h=jE(Fy(l,32)))?new XE(o,p):new F7(o,2,eow(vx(ty_,1),eHT,25,15,[p,h]))):ep_(o<0?efe(r,n):efe(n,r));if(o==u)d=o,f=a>=s?X7(e.a,a,t.a,s):X7(t.a,s,e.a,a);else{if(0==(i=a!=s?a>s?1:-1:es8(e.a,t.a,a)))return eLQ(),e08;1==i?(d=o,f=Z1(e.a,a,t.a,s)):(d=u,f=Z1(t.a,s,e.a,a))}return c=new F7(d,f.length,f),Ku(c),c}function eP6(e,t,n,r,i,a,o){var s,u,c,l,f,d,h;return f=gN(LK(e_k(t,(eBy(),taV)))),d=null,a==(enY(),tsD)&&r.c.i==n?d=r.c:a==tsN&&r.d.i==n&&(d=r.d),(c=o)&&f&&!d?(P_(c.e,r),h=eB4.Math.max(gP(LV(e_k(c.d,tak))),gP(LV(e_k(r,tak)))),eo3(c.d,tak,h)):(l=(eYu(),tbF),d?l=d.j:TM(Pp(e_k(n,tol),98))&&(l=a==tsD?tbY:tby),u=eP8(e,t,n,a,l,r),s=ZD((Bq(n),r)),a==tsD?(Gs(s,Pp(RJ(u.j,0),11)),Go(s,i)):(Gs(s,i),Go(s,Pp(RJ(u.j,0),11))),c=new ec8(r,s,u,Pp(e_k(u,(eBU(),tnc)),11),a,!d)),exg(e.a,r,new DT(c.d,t,a)),c}function eP9(e,t){var n,r,i,a,o,s,u,c,l,f;if(l=null,e.d&&(l=Pp(zg(e.d,t),138)),!l){if(f=(a=e.a.Mh()).i,!e.d||wq(e.d)!=f){for(u=new p2,e.d&&eij(u,e.d),s=c=u.f.c+u.g.c;s0?(h=(p-1)*n,s&&(h+=r),l&&(h+=r),!(h=e.b[i+1])i+=2;else if(n0)for(r=new I4(Pp(Zq(e.a,a),21)),Hj(),Mv(r,new dT(t)),i=new KB(a.b,0);i.b_)?(u=2,o=eUu):0==u?(u=1,o=S):(u=0,o=S):(h=S>=o||o-S0?1:Te(isNaN(r),isNaN(0)))>=0^(enj(eVU),(eB4.Math.abs(s)<=eVU||0==s||isNaN(s)&&isNaN(0)?0:s<0?-1:s>0?1:Te(isNaN(s),isNaN(0)))>=0))?eB4.Math.max(s,r):(enj(eVU),(eB4.Math.abs(r)<=eVU||0==r||isNaN(r)&&isNaN(0)?0:r<0?-1:r>0?1:Te(isNaN(r),isNaN(0)))>0)?eB4.Math.sqrt(s*s+r*r):-eB4.Math.sqrt(s*s+r*r)}function eRv(e,t){var n,r,i,a,o,s;if(t){if(e.a||(e.a=new bZ),2==e.e){bY(e.a,t);return}if(1==t.e){for(i=0;i=eH3?xk(n,el1(r)):Bf(n,r&eHd),o=(++tyv,new zc(10,null,0)),Yu(e.a,o,s-1)):xk(n=(o.bm().length,new vu),o.bm()),0==t.e?(r=t._l())>=eH3?xk(n,el1(r)):Bf(n,r&eHd):xk(n,t.bm()),Pp(o,521).b=n.a}}function eRy(e){var t,n,r,i,a;return null!=e.g?e.g:e.a<32?(e.g=eYS(eap(e.f),zy(e.e)),e.g):(i=eBw((e.c||(e.c=euK(e.f)),e.c),0),0==e.e)?i:(t=(e.c||(e.c=euK(e.f)),e.c).e<0?2:1,n=i.length,r=-e.e+n-t,a=new vc,a.a+=""+i,e.e>0&&r>=-6?r>=0?Gn(a,n-zy(e.e),"."):(a.a=Az(a.a,0,t-1)+"0."+xy(a.a,t-1),Gn(a,t+1,ehv(e0Z,0,-zy(r)-1))):(n-t>=1&&(Gn(a,t,"."),++n),Gn(a,n,"E"),r>0&&Gn(a,++n,"+"),Gn(a,++n,""+Fb(eap(r)))),e.g=a.a,e.g)}function eRw(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m;if(!n.dc()){for(s=0,d=0,p=Pp((r=n.Kc()).Pb(),19).a;s1&&(u=c.mg(u,e.a,s));return 1==u.c.length?Pp(RJ(u,u.c.length-1),220):2==u.c.length?eRr((GK(0,u.c.length),Pp(u.c[0],220)),(GK(1,u.c.length),Pp(u.c[1],220)),o,a):null}function eRk(e){var t,n,r,i,a,o;for(ety(e.a,new eJ),n=new fz(e.a);n.a=eB4.Math.abs(r.b)?(r.b=0,a.d+a.a>o.d&&a.do.c&&a.c0){if(t=new xt(e.i,e.g),a=(n=e.i)<100?null:new yf(n),e.ij())for(r=0;r0){for(s=e.g,c=e.i,ZG(e),a=c<100?null:new yf(c),r=0;r>13|(15&e.m)<<9,i=e.m>>4&8191,a=e.m>>17|(255&e.h)<<5,o=(1048320&e.h)>>8,s=8191&t.l,u=t.l>>13|(15&t.m)<<9,c=t.m>>4&8191,l=t.m>>17|(255&t.h)<<5,f=(1048320&t.h)>>8,k=n*s,x=r*s,T=i*s,M=a*s,O=o*s,0!=u&&(x+=n*u,T+=r*u,M+=i*u,O+=a*u),0!=c&&(T+=n*c,M+=r*c,O+=i*c),0!=l&&(M+=n*l,O+=r*l),0!=f&&(O+=n*f),d=(h=k&eHH)+(p=(511&x)<<13),m=k>>22,g=x>>9,b=m+g+(v=(262143&T)<<4)+(y=(31&M)<<17),_=T>>18,w=_+(E=M>>5)+(S=(4095&O)<<8),b+=d>>22,d&=eHH,w+=b>>22,Mk(d,b&=eHH,w&=eH$)}function eRA(e){var t,n,r,i,a,o,s;if(0!=(s=Pp(RJ(e.j,0),11)).g.c.length&&0!=s.e.c.length)throw p7(new gC("Interactive layout does not support NORTH/SOUTH ports with incoming _and_ outgoing edges."));if(0!=s.g.c.length){for(a=eHQ,n=new fz(s.g);n.a4){if(!e.wj(t))return!1;if(e.rk()){if(u=(r=(i=Pp(t,49)).Ug())==e.e&&(e.Dk()?i.Og(i.Vg(),e.zk())==e.Ak():-1-i.Vg()==e.aj()),e.Ek()&&!u&&!r&&i.Zg()){for(a=0;a0&&(c=e.n.a/a);break;case 2:case 4:(i=e.i.o.b)>0&&(c=e.n.b/i)}eo3(e,(eBU(),tnv),c)}if(u=e.o,o=e.a,r)o.a=r.a,o.b=r.b,e.d=!0;else if(t!=tbc&&t!=tbl&&s!=tbF)switch(s.g){case 1:o.a=u.a/2;break;case 2:o.a=u.a,o.b=u.b/2;break;case 3:o.a=u.a/2,o.b=u.b;break;case 4:o.b=u.b/2}else o.a=u.a/2,o.b=u.b/2}function eRP(e){var t,n,r,i,a,o,s,u,c,l;if(e.ej()){if(l=e.Vi(),u=e.fj(),l>0){if(t=new eiP(e.Gi()),a=(n=l)<100?null:new yf(n),Cf(e,n,t.g),i=1==n?e.Zi(4,etj(t,0),null,0,u):e.Zi(6,t,null,-1,u),e.bj()){for(r=new Ow(t);r.e!=r.i.gc();)a=e.dj(epH(r),a);a?(a.Ei(i),a.Fi()):e.$i(i)}else a?(a.Ei(i),a.Fi()):e.$i(i)}else Cf(e,e.Vi(),e.Wi()),e.$i(e.Zi(6,(Hj(),e2r),null,-1,u))}else if(e.bj()){if((l=e.Vi())>0){for(s=e.Wi(),c=l,Cf(e,l,s),a=c<100?null:new yf(c),r=0;re.d[o.p]&&(n+=qq(e.b,a)*Pp(u.b,19).a,Vw(e.a,ell(a)));for(;!gY(e.a);)eek(e.b,Pp(Yn(e.a),19).a)}return n}function eRF(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p,b,m;for((f=new TS(Pp(eT8(e,(e_C(),tdB)),8))).a=eB4.Math.max(f.a-n.b-n.c,0),f.b=eB4.Math.max(f.b-n.d-n.a,0),(null==(i=LV(eT8(e,tdN)))||(BJ(i),i<=0))&&(i=1.3),s=new p0,p=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));p.e!=p.i.gc();)h=Pp(epH(p),33),o=new Lp(h),s.c[s.c.length]=o;switch((d=Pp(eT8(e,tdP),311)).g){case 3:m=eDQ(s,t,f.a,f.b,(c=r,BJ(i),c));break;case 1:m=eN0(s,t,f.a,f.b,(l=r,BJ(i),l));break;default:m=eRH(s,t,f.a,f.b,(u=r,BJ(i),u))}a=new etD(m),b=eY9(a,t,n,f.a,f.b,r,(BJ(i),i)),eYx(e,b.a,b.b,!1,!0)}function eRY(e,t){var n,r,i,a;n=t.b,a=new I4(n.j),i=0,(r=n.j).c=Je(e1R,eUp,1,0,5,1),Y$(Pp(eay(e.b,(eYu(),tbw),(erX(),tep)),15),n),i=emQ(a,i,new r3,r),Y$(Pp(eay(e.b,tbw,teh),15),n),i=emQ(a,i,new r2,r),Y$(Pp(eay(e.b,tbw,ted),15),n),Y$(Pp(eay(e.b,tby,tep),15),n),Y$(Pp(eay(e.b,tby,teh),15),n),i=emQ(a,i,new r4,r),Y$(Pp(eay(e.b,tby,ted),15),n),Y$(Pp(eay(e.b,tbj,tep),15),n),i=emQ(a,i,new r5,r),Y$(Pp(eay(e.b,tbj,teh),15),n),i=emQ(a,i,new r6,r),Y$(Pp(eay(e.b,tbj,ted),15),n),Y$(Pp(eay(e.b,tbY,tep),15),n),i=emQ(a,i,new ic,r),Y$(Pp(eay(e.b,tbY,teh),15),n),Y$(Pp(eay(e.b,tbY,ted),15),n)}function eRB(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b;for(ewG(t,"Layer size calculation",1),l=eHQ,c=eH1,i=!1,s=new fz(e.b);s.a.5?g-=2*o*(p-.5):p<.5&&(g+=2*a*(.5-p)),g<(i=s.d.b)&&(g=i),b=s.d.c,g>m.a-b-l&&(g=m.a-b-l),s.n.a=t+g}}function eRH(e,t,n,r,i){var a,o,s,u,c,l,f,d,h,p,b,m;for(s=Je(tyx,eH5,25,e.c.length,15,1),d=new Fz(new oB),egV(d,e),c=0,b=new p0;0!=d.b.c.length;)if(o=Pp(0==d.b.c.length?null:RJ(d.b,0),157),c>1&&jl(o)*jc(o)/2>s[0]){for(a=0;as[a];)++a;p=new Gz(b,0,a+1),f=new etD(p),l=jl(o)/jc(o),u=eY9(f,t,new mp,n,r,i,l),C5(xB(f.e),u),Ja(e_s(d,f)),egV(d,h=new Gz(b,a+1,b.c.length)),b.c=Je(e1R,eUp,1,0,5,1),c=0,jA(s,s.length,0)}else null!=(m=0==d.b.c.length?null:RJ(d.b,0))&&erD(d,0),c>0&&(s[c]=s[c-1]),s[c]+=jl(o)*jc(o),++c,b.c[b.c.length]=o;return b}function eR$(e){var t,n,r,i,a;if((r=Pp(e_k(e,(eBy(),taY)),163))==(ef_(),tnN)){for(n=new Fa(OH(efu(e).a.Kc(),new c));eTk(n);)if(t=Pp(ZC(n),17),!ZI(t))throw p7(new gq(eWr+egs(e)+"' has its layer constraint set to FIRST_SEPARATE, but has at least one incoming edge. FIRST_SEPARATE nodes must not have incoming edges."))}else if(r==tnR){for(a=new Fa(OH(efc(e).a.Kc(),new c));eTk(a);)if(i=Pp(ZC(a),17),!ZI(i))throw p7(new gq(eWr+egs(e)+"' has its layer constraint set to LAST_SEPARATE, but has at least one outgoing edge. LAST_SEPARATE nodes must not have outgoing edges."))}}function eRz(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p;for(ewG(t,"Label dummy removal",1),r=gP(LV(e_k(e,(eBy(),toL)))),i=gP(LV(e_k(e,toN))),c=Pp(e_k(e,tal),103),u=new fz(e.b);u.a0&&eE9(e,s,f);for(i=new fz(f);i.a>19!=0&&(t=eoQ(t),u=!u),o=eOy(t),a=!1,i=!1,r=!1,e.h==eHz&&0==e.m&&0==e.l){if(i=!0,a=!0,-1!=o)return s=eTC(e,o),u&&esh(s),n&&(e0A=Mk(0,0,0)),s;e=Tr((Q2(),e0L)),r=!0,u=!u}else e.h>>19!=0&&(a=!0,e=eoQ(e),r=!0,u=!u);return -1!=o?esk(e,o,u,a,n):0>evy(e,t)?(n&&(e0A=a?eoQ(e):Mk(e.l,e.m,e.h)),Mk(0,0,0)):eDr(r?e:Mk(e.l,e.m,e.h),t,u,a,i,n)}function eRq(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p;if(e.e&&e.c.ct.f)&&!(t.g>e.f)){for(n=0,r=0,o=e.w.a.ec().Kc();o.Ob();)i=Pp(o.Pb(),11),euz(esp(eow(vx(e50,1),eUP,8,0,[i.i.n,i.n,i.a])).b,t.g,t.f)&&++n;for(s=e.r.a.ec().Kc();s.Ob();)i=Pp(s.Pb(),11),euz(esp(eow(vx(e50,1),eUP,8,0,[i.i.n,i.n,i.a])).b,t.g,t.f)&&--n;for(u=t.w.a.ec().Kc();u.Ob();)i=Pp(u.Pb(),11),euz(esp(eow(vx(e50,1),eUP,8,0,[i.i.n,i.n,i.a])).b,e.g,e.f)&&++r;for(a=t.r.a.ec().Kc();a.Ob();)i=Pp(a.Pb(),11),euz(esp(eow(vx(e50,1),eUP,8,0,[i.i.n,i.n,i.a])).b,e.g,e.f)&&--r;n=0)return i=efd(e,t.substr(1,o-1)),eYF(e,l=t.substr(o+1,u-(o+1)),i)}else{if(n=-1,null==e0F&&(e0F=RegExp("\\d")),e0F.test(String.fromCharCode(s))&&(n=IO(t,e_n(46),u-1))>=0){r=Pp(ZN(e,etm(e,t.substr(1,n-1)),!1),58),c=0;try{c=eDa(t.substr(n+1),eHt,eUu)}catch(d){if(d=eoa(d),M4(d,127))throw a=d,p7(new QH(a));throw p7(d)}if(c=0)return n;switch(Ur(QZ(e,n))){case 2:if(IE("",ecG(e,n.Hj()).ne())){if(u=U$(QZ(e,n)),s=UH(QZ(e,n)),l=eMv(e,t,u,s))return l;for(o=0,f=(i=eIx(e,t)).gc();o1)throw p7(new gL(eQ$));for(o=0,l=eAY(e.e.Tg(),t),r=Pp(e.g,119);o1,c=new Z4(d.b);My(c.a)||My(c.b);)f=(u=Pp(My(c.a)?Wx(c.a):Wx(c.b),17)).c==d?u.d:u.c,eB4.Math.abs(esp(eow(vx(e50,1),eUP,8,0,[f.i.n,f.n,f.a])).b-o.b)>1&&eAZ(e,u,o,a,d)}}function eR8(e){var t,n,r,i,a,o;if(i=new KB(e.e,0),r=new KB(e.a,0),e.d)for(n=0;neVW;){for(a=t,o=0;eB4.Math.abs(t-a)0),i.a.Xb(i.c=--i.b),eNy(e,e.b-o,a,r,i),A6(i.b0),r.a.Xb(r.c=--r.b)}if(!e.d)for(n=0;n0?(e.f[l.p]=h/(l.e.c.length+l.g.c.length),e.c=eB4.Math.min(e.c,e.f[l.p]),e.b=eB4.Math.max(e.b,e.f[l.p])):s&&(e.f[l.p]=h)}}function ejt(e){e.b=null,e.bb=null,e.fb=null,e.qb=null,e.a=null,e.c=null,e.d=null,e.e=null,e.f=null,e.n=null,e.M=null,e.L=null,e.Q=null,e.R=null,e.K=null,e.db=null,e.eb=null,e.g=null,e.i=null,e.j=null,e.k=null,e.gb=null,e.o=null,e.p=null,e.q=null,e.r=null,e.$=null,e.ib=null,e.S=null,e.T=null,e.t=null,e.s=null,e.u=null,e.v=null,e.w=null,e.B=null,e.A=null,e.C=null,e.D=null,e.F=null,e.G=null,e.H=null,e.I=null,e.J=null,e.P=null,e.Z=null,e.U=null,e.V=null,e.W=null,e.X=null,e.Y=null,e._=null,e.ab=null,e.cb=null,e.hb=null,e.nb=null,e.lb=null,e.mb=null,e.ob=null,e.pb=null,e.jb=null,e.kb=null,e.N=!1,e.O=!1}function ejn(e,t,n){var r,i,a,o;for(ewG(n,"Graph transformation ("+e.a+")",1),o=WC(t.a),a=new fz(t.b);a.a0&&(e.a=u+(p-1)*a,t.c.b+=e.a,t.f.b+=e.a),0!=b.a.gc()&&(p=ejF(h=new YJ(1,a),t,b,m,t.f.b+u-t.c.b))>0&&(t.f.b+=u+(p-1)*a)}function eji(e,t){var n,r,i,a;a=e.F,null==t?(e.F=null,euc(e,null)):(e.F=(BJ(t),t),-1!=(r=x7(t,e_n(60)))?(i=t.substr(0,r),-1!=x7(t,e_n(46))||IE(i,eUi)||IE(i,eJZ)||IE(i,eJX)||IE(i,eJJ)||IE(i,eJQ)||IE(i,eJ1)||IE(i,eJ0)||IE(i,eJ2)||(i=eJ3),-1!=(n=O8(t,e_n(62)))&&(i+=""+t.substr(n+1)),euc(e,i)):(i=t,-1==x7(t,e_n(46))&&(-1!=(r=x7(t,e_n(91)))&&(i=t.substr(0,r)),IE(i,eUi)||IE(i,eJZ)||IE(i,eJX)||IE(i,eJJ)||IE(i,eJQ)||IE(i,eJ1)||IE(i,eJ0)||IE(i,eJ2)?i=t:(i=eJ3,-1!=r&&(i+=""+t.substr(r)))),euc(e,i),i==t&&(e.F=e.D))),(4&e.Db)!=0&&(1&e.Db)==0&&eam(e,new FX(e,1,5,a,t))}function eja(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y;if(!((b=t.b.c.length)<3)){for(h=Je(ty_,eHT,25,b,15,1),f=0,l=new fz(t.b);l.ao)&&Yf(e.b,Pp(m.b,17));++s}a=o}}}function ejo(e,t){var n;if(null==t||IE(t,eUg)||0==t.length&&e.k!=(eSd(),tdy))return null;switch(e.k.g){case 1:return ehZ(t,eq6)?(OQ(),e0P):ehZ(t,eq9)?(OQ(),e0N):null;case 2:try{return ell(eDa(t,eHt,eUu))}catch(r){if(r=eoa(r),M4(r,127))return null;throw p7(r)}case 4:try{return eEu(t)}catch(i){if(i=eoa(i),M4(i,127))return null;throw p7(i)}case 3:return t;case 5:return euC(e),exs(e,t);case 6:return euC(e),eMj(e,e.a,t);case 7:try{return(n=eTh(e)).Jf(t),n}catch(a){if(a=eoa(a),M4(a,32))return null;throw p7(a)}default:throw p7(new gC("Invalid type set for this layout option."))}}function ejs(e){var t,n,r,i,a,o,s;for(eeP(),s=new b6,n=new fz(e);n.a=s.b.c)&&(s.b=t),(!s.c||t.c<=s.c.c)&&(s.d=s.c,s.c=t),(!s.e||t.d>=s.e.d)&&(s.e=t),(!s.f||t.d<=s.f.d)&&(s.f=t);return r=new epG((eok(),e8f)),Kv(e,e8y,new g$(eow(vx(e4M,1),eUp,369,0,[r]))),o=new epG(e8p),Kv(e,e8v,new g$(eow(vx(e4M,1),eUp,369,0,[o]))),i=new epG(e8d),Kv(e,e8g,new g$(eow(vx(e4M,1),eUp,369,0,[i]))),a=new epG(e8h),Kv(e,e8m,new g$(eow(vx(e4M,1),eUp,369,0,[a]))),eOk(r.c,e8f),eOk(i.c,e8d),eOk(a.c,e8h),eOk(o.c,e8p),s.a.c=Je(e1R,eUp,1,0,5,1),eoc(s.a,r.c),eoc(s.a,eaa(i.c)),eoc(s.a,a.c),eoc(s.a,eaa(o.c)),s}function eju(e){var t;switch(e.d){case 1:if(e.hj())return -2!=e.o;break;case 2:if(e.hj())return -2==e.o;break;case 3:case 5:case 4:case 6:case 7:return e.o>-2;default:return!1}switch(t=e.gj(),e.p){case 0:return null!=t&&gN(LK(t))!=xg(e.k,0);case 1:return null!=t&&Pp(t,217).a!=jE(e.k)<<24>>24;case 2:return null!=t&&Pp(t,172).a!=(jE(e.k)&eHd);case 6:return null!=t&&xg(Pp(t,162).a,e.k);case 5:return null!=t&&Pp(t,19).a!=jE(e.k);case 7:return null!=t&&Pp(t,184).a!=jE(e.k)<<16>>16;case 3:return null!=t&&gP(LV(t))!=e.j;case 4:return null!=t&&Pp(t,155).a!=e.j;default:return null==t?null!=e.n:!ecX(t,e.n)}}function ejc(e,t,n){var r,i,a,o;return e.Fk()&&e.Ek()&&(o=FU(e,Pp(n,56)),xc(o)!==xc(n))?(e.Oi(t),e.Ui(t,J6(e,t,o)),e.rk()&&(a=(i=Pp(n,49),e.Dk()?e.Bk()?i.ih(e.b,ebY(Pp(ee2($S(e.b),e.aj()),18)).n,Pp(ee2($S(e.b),e.aj()).Yj(),26).Bj(),null):i.ih(e.b,edv(i.Tg(),ebY(Pp(ee2($S(e.b),e.aj()),18))),null,null):i.ih(e.b,-1-e.aj(),null,null)),Pp(o,49).eh()||(a=(r=Pp(o,49),e.Dk()?e.Bk()?r.gh(e.b,ebY(Pp(ee2($S(e.b),e.aj()),18)).n,Pp(ee2($S(e.b),e.aj()).Yj(),26).Bj(),a):r.gh(e.b,edv(r.Tg(),ebY(Pp(ee2($S(e.b),e.aj()),18))),null,a):r.gh(e.b,-1-e.aj(),null,a))),a&&a.Fi()),TO(e.b)&&e.$i(e.Zi(9,n,o,t,!1)),o):n}function ejl(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w;for(l=gP(LV(e_k(e,(eBy(),toC)))),r=gP(LV(e_k(e,toG))),eo3(d=new oG,toC,l+r),g=(c=t).d,b=c.c.i,v=c.d.i,m=Tl(b.c),y=Tl(v.c),i=new p0,f=m;f<=y;f++)s=new eb$(e),lK(s,(eEn(),e8D)),eo3(s,(eBU(),tnc),c),eo3(s,tol,(ewf(),tbo)),eo3(s,toD,d),h=Pp(RJ(e.b,f),29),f==m?egU(s,h.a.c.length-n,h):Gu(s,h),(w=gP(LV(e_k(c,tak))))<0&&eo3(c,tak,w=0),s.o.b=w,p=eB4.Math.floor(w/2),o=new eES,ekv(o,(eYu(),tbY)),Gc(o,s),o.n.b=p,u=new eES,ekv(u,tby),Gc(u,s),u.n.b=p,Go(c,o),a=new $b,eaW(a,c),eo3(a,taR,null),Gs(a,u),Go(a,g),evT(s,c,a),i.c[i.c.length]=a,c=a;return i}function ejf(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y;for(u=Pp(eEC(e,(eYu(),tbY)).Kc().Pb(),11).e,h=Pp(eEC(e,tby).Kc().Pb(),11).g,s=u.c.length,y=GX(Pp(RJ(e.j,0),11));s-- >0;){for(b=(GK(0,u.c.length),Pp(u.c[0],17)),a=QI(v=(i=(GK(0,h.c.length),Pp(h.c[0],17))).d.e,i,0),KW(b,i.d,a),Gs(i,null),Go(i,null),p=b.a,t&&P7(p,new TS(y)),r=epL(i.a,0);r.b!=r.d.c;)n=Pp(Vv(r),8),P7(p,new TS(n));for(g=b.b,d=new fz(i.b);d.a0&&(o=eB4.Math.max(o,eix(e.C.b+r.d.b,i))),l=r,f=i,d=a;e.C&&e.C.c>0&&(h=d+e.C.c,c&&(h+=l.d.c),o=eB4.Math.max(o,(Mc(),enj(ezs),eB4.Math.abs(f-1)<=ezs||1==f||isNaN(f)&&isNaN(1)?0:h/(1-f)))),n.n.b=0,n.a.a=o}function ejh(e,t){var n,r,i,a,o,s,u,c,l,f,d,h;if(n=Pp(UA(e.b,t),124),(u=Pp(Pp(Zq(e.r,t),21),84)).dc()){n.n.d=0,n.n.a=0;return}for(c=e.u.Hc((ekU(),tbp)),o=0,e.A.Hc((ed6(),tbq))&&eCN(e,t),s=u.Kc(),l=null,d=0,f=0;s.Ob();)a=gP(LV((r=Pp(s.Pb(),111)).b.We((Ab(),e4a)))),i=r.b.rf().b,l?(h=f+l.d.a+e.w+r.d.d,o=eB4.Math.max(o,(Mc(),enj(ezs),eB4.Math.abs(d-a)<=ezs||d==a||isNaN(d)&&isNaN(a)?0:h/(a-d)))):e.C&&e.C.d>0&&(o=eB4.Math.max(o,eix(e.C.d+r.d.d,a))),l=r,d=a,f=i;e.C&&e.C.a>0&&(h=f+e.C.a,c&&(h+=l.d.a),o=eB4.Math.max(o,(Mc(),enj(ezs),eB4.Math.abs(d-1)<=ezs||1==d||isNaN(d)&&isNaN(1)?0:h/(1-d)))),n.n.d=0,n.a.b=o}function ejp(e,t,n){var r,i,a,o,s,u;for(o=0,this.g=e,s=t.d.length,u=n.d.length,this.d=Je(e4N,eGW,10,s+u,0,1);o0?etU(this,this.f/this.a):null!=Ot(t.g,t.d[0]).a&&null!=Ot(n.g,n.d[0]).a?etU(this,(gP(Ot(t.g,t.d[0]).a)+gP(Ot(n.g,n.d[0]).a))/2):null!=Ot(t.g,t.d[0]).a?etU(this,Ot(t.g,t.d[0]).a):null!=Ot(n.g,n.d[0]).a&&etU(this,Ot(n.g,n.d[0]).a)}function ejb(e,t){var n,r,i,a,o,s,u,c,l,f;for(e.a=new Bv(eiG(e55)),r=new fz(t.a);r.a=1&&(m-o>0&&f>=0?(u.n.a+=b,u.n.b+=a*o):m-o<0&&l>=0&&(u.n.a+=b*m,u.n.b+=a));e.o.a=t.a,e.o.b=t.b,eo3(e,(eBy(),ta4),(ed6(),r=Pp(yw(e6o),9),new I1(r,Pp(CY(r,r.length),9),0)))}function ej_(e,t,n,r,i,a){var o;if(!(null==t||!efz(t,tmJ,tmQ)))throw p7(new gL("invalid scheme: "+t));if(!e&&!(null!=n&&-1==x7(n,e_n(35))&&n.length>0&&(GV(0,n.length),47!=n.charCodeAt(0))))throw p7(new gL("invalid opaquePart: "+n));if(e&&!(null!=t&&wZ(tm$,t.toLowerCase()))&&!(null==n||!efz(n,tm1,tm0))||e&&null!=t&&wZ(tm$,t.toLowerCase())&&!eyQ(n))throw p7(new gL(eJI+n));if(!ef$(r))throw p7(new gL("invalid device: "+r));if(!ece(i))throw o=null==i?"invalid segments: null":"invalid segment: "+euR(i),p7(new gL(o));if(!(null==a||-1==x7(a,e_n(35))))throw p7(new gL("invalid query: "+a))}function ejE(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g;for(ewG(t,"Calculate Graph Size",1),t.n&&e&&WG(t,KS(e),(eup(),tmr)),s=ezq,u=ezq,a=eqe,o=eqe,f=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));f.e!=f.i.gc();)p=(c=Pp(epH(f),33)).i,b=c.j,g=c.g,r=c.f,i=Pp(eT8(c,(eBB(),thy)),142),s=eB4.Math.min(s,p-i.b),u=eB4.Math.min(u,b-i.d),a=eB4.Math.max(a,p+g+i.c),o=eB4.Math.max(o,b+r+i.a);for(h=Pp(eT8(e,(eBB(),thN)),116),d=new kl(s-h.b,u-h.d),l=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));l.e!=l.i.gc();)c=Pp(epH(l),33),eno(c,c.i-d.a),ens(c,c.j-d.b);m=a-s+(h.b+h.c),n=o-u+(h.d+h.a),ena(e,m),eni(e,n),t.n&&e&&WG(t,KS(e),(eup(),tmr))}function ejS(e){var t,n,r,i,a,o,s,u,c,l;for(r=new p0,o=new fz(e.e.a);o.a0){epV(e,n,0),n.a+=String.fromCharCode(r),epV(e,n,i=ehR(t,a)),a+=i-1;continue}39==r?a+11)for(b=Je(ty_,eHT,25,e.b.b.c.length,15,1),f=0,c=new fz(e.b.b);c.a=s&&i<=u)s<=i&&a<=u?(n[l++]=i,n[l++]=a,r+=2):s<=i?(n[l++]=i,n[l++]=u,e.b[r]=u+1,o+=2):a<=u?(n[l++]=s,n[l++]=a,r+=2):(n[l++]=s,n[l++]=u,e.b[r]=u+1);else if(ueHe)&&s<10)vR(e.c,new tf),ejM(e),Ym(e.c),ejv(e.f)}function ejL(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m;if(gN(LK(e_k(n,(eBy(),taI)))))for(s=new fz(n.j);s.a=2){for(o=Pp(Vv(u=epL(n,0)),8),s=Pp(Vv(u),8);s.a0&&eoY(l,!0,(ec3(),tpg)),s.k==(eEn(),e8C)&&UP(l),Um(e.f,s,t)}}function ejN(e,t,n){var r,i,a,o,s,u,c,l,f,d;switch(ewG(n,"Node promotion heuristic",1),e.g=t,eYs(e),e.q=Pp(e_k(t,(eBy(),taz)),260),l=Pp(e_k(e.g,ta$),19).a,a=new nH,e.q.g){case 2:case 1:default:eRn(e,a);break;case 3:for(e.q=(eOJ(),tsk),eRn(e,a),u=0,s=new fz(e.a);s.ae.j&&(e.q=tsv,eRn(e,a));break;case 4:for(e.q=(eOJ(),tsk),eRn(e,a),c=0,i=new fz(e.b);i.ae.k&&(e.q=ts_,eRn(e,a));break;case 6:d=zy(eB4.Math.ceil(e.f.length*l/100)),eRn(e,new dq(d));break;case 5:f=zy(eB4.Math.ceil(e.d*l/100)),eRn(e,new dZ(f))}eLC(e,t),eEj(n)}function ejP(e,t,n){var r,i,a,o;this.j=e,this.e=ewi(e),this.o=this.j.e,this.i=!!this.o,this.p=this.i?Pp(RJ(n,Bq(this.o).p),214):null,i=Pp(e_k(e,(eBU(),tt3)),21),this.g=i.Hc((eLR(),ttw)),this.b=new p0,this.d=new ed0(this.e),o=Pp(e_k(this.j,tnw),230),this.q=eaG(t,o,this.e),this.k=new zX(this),a=ZW(eow(vx(e4H,1),eUp,225,0,[this,this.d,this.k,this.q])),t!=(enU(),tur)||gN(LK(e_k(e,(eBy(),ti7))))?t==tur&&gN(LK(e_k(e,(eBy(),ti7))))?(r=new ews(this.e),a.c[a.c.length]=r,this.c=new erB(r,o,Pp(this.q,402))):this.c=new Sr(t,this):(r=new ews(this.e),a.c[a.c.length]=r,this.c=new K5(r,o,Pp(this.q,402))),P_(a,this.c),eP0(a,this.e),this.s=eY0(this.k)}function ejR(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w;for(p=(f=Pp(M2((o=epL(new hz(t).a.d,0),new hG(o))),86))?Pp(e_k(f,(eR6(),tco)),86):null,i=1;f&&p;){for(s=0,u=0,w=0,n=f,r=p;s=e.i?(++e.i,P_(e.a,ell(1)),P_(e.b,f)):(r=e.c[t.p][1],q1(e.a,l,ell(Pp(RJ(e.a,l),19).a+1-r)),q1(e.b,l,gP(LV(RJ(e.b,l)))+f-r*e.e)),(e.q==(eOJ(),tsv)&&(Pp(RJ(e.a,l),19).a>e.j||Pp(RJ(e.a,l-1),19).a>e.j)||e.q==ts_&&(gP(LV(RJ(e.b,l)))>e.k||gP(LV(RJ(e.b,l-1)))>e.k))&&(u=!1),o=new Fa(OH(efu(t).a.Kc(),new c));eTk(o);)s=(a=Pp(ZC(o),17)).c.i,e.f[s.p]==l&&(d=ejj(e,s),i+=Pp(d.a,19).a,u=u&&gN(LK(d.b)));return e.f[t.p]=l,i+=e.c[t.p][0],new kD(ell(i),(OQ(),!!u))}function ejF(e,t,n,r,i){var a,o,s,u,c,l,f,d,h,p,b,m,g;for(f=new p2,o=new p0,ekD(e,n,e.d.fg(),o,f),ekD(e,r,e.d.gg(),o,f),e.b=.2*(b=eTZ(eeh(new R1(null,new Gq(o,16)),new aL)),m=eTZ(eeh(new R1(null,new Gq(o,16)),new aC)),eB4.Math.min(b,m)),a=0,s=0;s=2&&(g=eOY(o,!0,d),e.e||(e.e=new h$(e)),ehB(e.e,g,o,e.b)),ewv(o,d),eFn(o),h=-1,l=new fz(o);l.as)}function ejU(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b;for(n=Pp(e_k(e,(eBy(),tol)),98),o=e.f,a=e.d,s=o.a+a.b+a.c,u=0-a.d-e.c.b,l=o.b+a.d+a.a-e.c.b,c=new p0,f=new p0,i=new fz(t);i.a0),Pp(l.a.Xb(l.c=--l.b),17));a!=r&&l.b>0;)e.a[a.p]=!0,e.a[r.p]=!0,a=(A6(l.b>0),Pp(l.a.Xb(l.c=--l.b),17));l.b>0&&BH(l)}}function ejZ(e,t,n){var r,i,a,o,s,u,c,l,f;if(e.a!=t.Aj())throw p7(new gL(eZ5+t.ne()+eZ6));if(r=ecG((eSp(),tvc),t).$k())return r.Aj().Nh().Ih(r,n);if(o=ecG(tvc,t).al()){if(null==n)return null;if((s=Pp(n,15)).dc())return"";for(f=new vs,a=s.Kc();a.Ob();)i=a.Pb(),xk(f,o.Aj().Nh().Ih(o,i)),f.a+=" ";return x3(f,f.a.length-1)}if(!(l=ecG(tvc,t).bl()).dc()){for(c=l.Kc();c.Ob();)if((u=Pp(c.Pb(),148)).wj(n))try{if(f=u.Aj().Nh().Ih(u,n),null!=f)return f}catch(d){if(d=eoa(d),!M4(d,102))throw p7(d)}throw p7(new gL("Invalid value: '"+n+"' for datatype :"+t.ne()))}return Pp(t,834).Fj(),null==n?null:M4(n,172)?""+Pp(n,172).a:esF(n)==e1Q?MU(tmS[0],Pp(n,199)):efF(n)}function ejX(e){var t,n,r,i,a,o,s,u,c,l;for(c=new _n,s=new _n,a=new fz(e);a.a-1){for(i=epL(s,0);i.b!=i.d.c;)(r=Pp(Vv(i),128)).v=o;for(;0!=s.b;)for(r=Pp(egW(s,0),128),n=new fz(r.i);n.a0&&(n+=u.n.a+u.o.a/2,++f),p=new fz(u.j);p.a0&&(n/=f),g=Je(tyx,eH5,25,r.a.c.length,15,1),s=0,c=new fz(r.a);c.a=s&&i<=u)s<=i&&a<=u?r+=2:s<=i?(e.b[r]=u+1,o+=2):a<=u?(n[l++]=i,n[l++]=s-1,r+=2):(n[l++]=i,n[l++]=s-1,e.b[r]=u+1,o+=2);else if(u0?i-=864e5:i+=864e5,u=new LZ(eft(eap(t.q.getTime()),i))),l=new vl,c=e.a.length,a=0;a=97&&r<=122||r>=65&&r<=90){for(o=a+1;o=c)throw p7(new gL("Missing trailing '"));o+10&&0==n.c&&(t||(t=new p0),t.c[t.c.length]=n);if(t)for(;0!=t.c.length;){if((n=Pp(ZV(t,0),233)).b&&n.b.c.length>0){for(a=(n.b||(n.b=new p0),new fz(n.b));a.aQI(e,n,0))return new kD(i,n)}else if(gP(Ot(i.g,i.d[0]).a)>gP(Ot(n.g,n.d[0]).a))return new kD(i,n)}for(s=(n.e||(n.e=new p0),n.e).Kc();s.Ob();)u=((o=Pp(s.Pb(),233)).b||(o.b=new p0),o.b),Gp(0,u.c.length),Ew(u.c,0,n),o.c==u.c.length&&(t.c[t.c.length]=o)}return null}function eFe(e,t){var n,r,i,a,o,s,u,c,l;if(null==e)return eUg;if(null!=(u=t.a.zc(e,t)))return"[...]";for(a=0,n=new eaP(eUd,"[","]"),o=(i=e).length;a=14&&l<=16)?t.a._b(r)?(n.a?xM(n.a,n.b):n.a=new O0(n.d),xx(n.a,"[...]")):ZJ(n,eFe(s=etG(r),c=new Rq(t))):M4(r,177)?ZJ(n,ekd(Pp(r,177))):M4(r,190)?ZJ(n,ewh(Pp(r,190))):M4(r,195)?ZJ(n,eEm(Pp(r,195))):M4(r,2012)?ZJ(n,ewp(Pp(r,2012))):M4(r,48)?ZJ(n,ekf(Pp(r,48))):M4(r,364)?ZJ(n,ekG(Pp(r,364))):M4(r,832)?ZJ(n,ekl(Pp(r,832))):M4(r,104)&&ZJ(n,ekc(Pp(r,104))):ZJ(n,null==r?eUg:efF(r));return n.a?0==n.e.length?n.a.a:n.a.a+""+n.e:n.c}function eFt(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y;for(s=eLO(t,!1,!1),g=eEF(s),r&&(g=esP(g)),y=gP(LV(eT8(t,(epz(),e63)))),m=(A6(0!=g.b),Pp(g.a.a.c,8)),f=Pp(ep3(g,1),8),g.b>2?(l=new p0,eoc(l,new Gz(g,1,g.b)),a=eBk(l,y+e.a),v=new eTI(a),eaW(v,t),n.c[n.c.length]=v):v=r?Pp(Bp(e.b,e_I(t)),266):Pp(Bp(e.b,e_P(t)),266),u=e_I(t),r&&(u=e_P(t)),o=eEJ(m,u),c=y+e.a,o.a?(c+=eB4.Math.abs(m.b-f.b),b=new kl(f.a,(f.b+m.b)/2)):(c+=eB4.Math.abs(m.a-f.a),b=new kl((f.a+m.a)/2,f.b)),r?Um(e.d,t,new emL(v,o,b,c)):Um(e.c,t,new emL(v,o,b,c)),Um(e.b,t,v),p=(t.n||(t.n=new FQ(e6S,t,1,7)),t.n),h=new Ow(p);h.e!=h.i.gc();)d=Pp(epH(h),137),i=eIt(e,d,!0,0,0),n.c[n.c.length]=i}function eFn(e){var t,n,r,i,a,o,s,u,c,l;for(c=new p0,s=new p0,o=new fz(e);o.a-1){for(a=new fz(s);a.a0)&&(l3(u,eB4.Math.min(u.o,i.o-1)),l2(u,u.i-1),0==u.i&&(s.c[s.c.length]=u))}}function eFr(e,t,n){var r,i,a,o,s,u,c;if(c=e.c,t||(t=tgK),e.c=t,(4&e.Db)!=0&&(1&e.Db)==0&&(u=new FX(e,1,2,c,e.c),n?n.Ei(u):n=u),c!=t){if(M4(e.Cb,284))e.Db>>16==-10?n=Pp(e.Cb,284).nk(t,n):e.Db>>16==-15&&(t||(t=(eBK(),tgA)),c||(c=(eBK(),tgA)),e.Cb.nh()&&(u=new Q$(e.Cb,1,13,c,t,ebv(QX(Pp(e.Cb,59)),e),!1),n?n.Ei(u):n=u));else if(M4(e.Cb,88))e.Db>>16==-23&&(M4(t,88)||(t=(eBK(),tgI)),M4(c,88)||(c=(eBK(),tgI)),e.Cb.nh()&&(u=new Q$(e.Cb,1,10,c,t,ebv(qt(Pp(e.Cb,26)),e),!1),n?n.Ei(u):n=u));else if(M4(e.Cb,444))for(o=((s=Pp(e.Cb,836)).b||(s.b=new pG(new mR)),s.b),a=(r=new esz(new fS(o.a).a),new pW(r));a.a.b;)n=eFr(i=Pp(etz(a.a).cd(),87),eOl(i,s),n)}return n}function eFi(e,t){var n,r,i,a,o,s,u,c,l,f,d;for(o=gN(LK(eT8(e,(eBy(),taI)))),d=Pp(eT8(e,toh),21),u=!1,c=!1,f=new Ow((e.c||(e.c=new FQ(e6x,e,9,9)),e.c));f.e!=f.i.gc()&&(!u||!c);){for(a=Pp(epH(f),118),s=0,i=Y_(enM(eow(vx(e1B,1),eUp,20,0,[(a.d||(a.d=new Ih(e6g,a,8,5)),a.d),(a.e||(a.e=new Ih(e6g,a,7,4)),a.e)])));eTk(i)&&(r=Pp(ZC(i),79),l=o&&exb(r)&&gN(LK(eT8(r,taD))),n=eRL((r.b||(r.b=new Ih(e6m,r,4,7)),r.b),a)?e==z$(ewH(Pp(etj((r.c||(r.c=new Ih(e6m,r,5,8)),r.c),0),82))):e==z$(ewH(Pp(etj((r.b||(r.b=new Ih(e6m,r,4,7)),r.b),0),82))),!((l||n)&&++s>1)););s>0?u=!0:d.Hc((ekU(),tbp))&&(a.n||(a.n=new FQ(e6S,a,1,7)),a.n).i>0&&(u=!0),s>1&&(c=!0)}u&&t.Fc((eLR(),ttw)),c&&t.Fc((eLR(),tt_))}function eFa(e){var t,n,r,i,a,o,s,u,c,l,f,d;if((d=Pp(eT8(e,(eBB(),thx)),21)).dc())return null;if(s=0,o=0,d.Hc((ed6(),tbV))){for(l=Pp(eT8(e,thV),98),r=2,n=2,i=2,a=2,t=z$(e)?Pp(eT8(z$(e),the),103):Pp(eT8(e,the),103),c=new Ow((e.c||(e.c=new FQ(e6x,e,9,9)),e.c));c.e!=c.i.gc();)if(u=Pp(epH(c),118),(f=Pp(eT8(u,th0),61))==(eYu(),tbF)&&(f=eNh(u,t),ebu(u,th0,f)),l==(ewf(),tbo))switch(f.g){case 1:r=eB4.Math.max(r,u.i+u.g);break;case 2:n=eB4.Math.max(n,u.j+u.f);break;case 3:i=eB4.Math.max(i,u.i+u.g);break;case 4:a=eB4.Math.max(a,u.j+u.f)}else switch(f.g){case 1:r+=u.g+2;break;case 2:n+=u.f+2;break;case 3:i+=u.g+2;break;case 4:a+=u.f+2}s=eB4.Math.max(r,i),o=eB4.Math.max(n,a)}return eYx(e,s,o,!0,!0)}function eFo(e,t,n,r,i){var a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w;for(v=Pp(qE(etc(UJ(new R1(null,new Gq(t.d,16)),new hc(n)),new hl(n)),JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[(eum(),e2U)]))),15),f=eUu,l=eHt,u=new fz(t.b.j);u.a0)?c&&(d=g.p,o?++d:--d,h=!(eOV(r=eoZ(f=Pp(RJ(g.c.a,d),10)),E,n[0])||FF(r,E,n[0]))):h=!0),p=!1,(_=t.D.i)&&_.c&&s.e&&((l=o&&_.p>0||!o&&_.p<_.c.a.c.length-1)?(d=_.p,o?--d:++d,p=!(eOV(r=eoZ(f=Pp(RJ(_.c.a,d),10)),n[0],k)||FF(r,n[0],k))):p=!0),h&&p&&P7(e.a,S),h||enD(e.a,eow(vx(e50,1),eUP,8,0,[b,m])),p||enD(e.a,eow(vx(e50,1),eUP,8,0,[w,y]))}function eFh(e,t){var n,r,i,a,o,s,u,c;if(M4(e.Ug(),160)?(eFh(Pp(e.Ug(),160),t),t.a+=" > "):t.a+="Root ",IE((n=e.Tg().zb).substr(0,3),"Elk")?xM(t,n.substr(3)):(t.a+=""+n,t),i=e.zg()){xM((t.a+=" ",t),i);return}if(M4(e,354)&&(c=Pp(e,137).a)){xM((t.a+=" ",t),c);return}for(o=new Ow(e.Ag());o.e!=o.i.gc();)if(c=(a=Pp(epH(o),137)).a){xM((t.a+=" ",t),c);return}if(M4(e,352)&&((r=Pp(e,79)).b||(r.b=new Ih(e6m,r,4,7)),0!=r.b.i&&(r.c||(r.c=new Ih(e6m,r,5,8)),0!=r.c.i))){for(t.a+=" (",s=new AF((r.b||(r.b=new Ih(e6m,r,4,7)),r.b));s.e!=s.i.gc();)s.e>0&&(t.a+=eUd),eFh(Pp(epH(s),160),t);for(t.a+=eGH,u=new AF((r.c||(r.c=new Ih(e6m,r,5,8)),r.c));u.e!=u.i.gc();)u.e>0&&(t.a+=eUd),eFh(Pp(epH(u),160),t);t.a+=")"}}function eFp(e,t,n){var r,i,a,o,s,u,c,l,f,d,h;if(a=Pp(e_k(e,(eBU(),tnc)),79)){for(r=e.a,C5(i=new TS(n),eyr(e)),eag(e.d.i,e.c.i)?(d=e.c,f=esp(eow(vx(e50,1),eUP,8,0,[d.n,d.a])),C6(f,n)):f=GX(e.c),qQ(r,f,r.a,r.a.a),h=GX(e.d),null!=e_k(e,tnC)&&C5(h,Pp(e_k(e,tnC),8)),qQ(r,h,r.c.b,r.c),etH(r,i),o=eLO(a,!0,!0),ern(o,Pp(etj((a.b||(a.b=new Ih(e6m,a,4,7)),a.b),0),82)),err(o,Pp(etj((a.c||(a.c=new Ih(e6m,a,5,8)),a.c),0),82)),eNI(r,o),l=new fz(e.b);l.a=0){for(u=null,s=new KB(l.a,c+1);s.bo?1:Te(isNaN(0),isNaN(o)))<0&&(enj(eVU),(eB4.Math.abs(o-1)<=eVU||1==o||isNaN(o)&&isNaN(1)?0:o<1?-1:o>1?1:Te(isNaN(o),isNaN(1)))<0)&&(enj(eVU),(eB4.Math.abs(0-s)<=eVU||0==s||isNaN(0)&&isNaN(s)?0:0s?1:Te(isNaN(0),isNaN(s)))<0)&&(enj(eVU),(eB4.Math.abs(s-1)<=eVU||1==s||isNaN(s)&&isNaN(1)?0:s<1?-1:s>1?1:Te(isNaN(s),isNaN(1)))<0)))}function eFg(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E;for(f=new BU(new fQ(e));f.b!=f.c.a.d;)for(b=0,s=Pp((l=JO(f)).d,56),t=Pp(l.e,56),w=(null==(o=s.Tg()).i&&eNT(o),o.i).length;b=0&&b=c.c.c.length?VJ((eEn(),e8N),e8D):VJ((eEn(),e8D),e8D),l*=2,a=n.a.g,n.a.g=eB4.Math.max(a,a+(l-a)),o=n.b.g,n.b.g=eB4.Math.max(o,o+(l-o)),i=t}}}function eFw(e,t,n,r,i){var a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_;for(_=Pg(e),l=new p0,f=(s=e.c.length)-1,d=s+1;0!=_.a.c;){for(;0!=n.b;)y=(A6(0!=n.b),Pp(etw(n,n.a.a),112)),zS(_.a,y),y.g=f--,eNg(y,t,n,r);for(;0!=t.b;)w=(A6(0!=t.b),Pp(etw(t,t.a.a),112)),zS(_.a,w),w.g=d++,eNg(w,t,n,r);for(c=eHt,g=(o=new C1(new Ap(new fP(_.a).a).b),new fR(o));Et(g.a.a);){if(m=(a=AJ(g.a),Pp(a.cd(),112)),!r&&m.b>0&&m.a<=0){l.c=Je(e1R,eUp,1,0,5,1),l.c[l.c.length]=m;break}(b=m.i-m.d)>=c&&(b>c&&(l.c=Je(e1R,eUp,1,0,5,1),c=b),l.c[l.c.length]=m)}0!=l.c.length&&(u=Pp(RJ(l,ebO(i,l.c.length)),112),zS(_.a,u),u.g=d++,eNg(u,t,n,r),l.c=Je(e1R,eUp,1,0,5,1))}for(v=e.c.length+1,p=new fz(e);p.a0&&(d.d+=l.n.d,d.d+=l.d),d.a>0&&(d.a+=l.n.a,d.a+=l.d),d.b>0&&(d.b+=l.n.b,d.b+=l.d),d.c>0&&(d.c+=l.n.c,d.c+=l.d),d}function eFx(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p;for(d=n.d,f=n.c,o=(a=new kl(n.f.a+n.d.b+n.d.c,n.f.b+n.d.d+n.d.a)).b,c=new fz(e.a);c.a=(l=Pp(Pp(Zq(e.r,t),21),84)).gc()||t==(eYu(),tby)||t==(eYu(),tbY)){eYY(e,t);return}for(b=e.u.Hc((ekU(),tbg)),n=t==(eYu(),tbw)?(eaY(),e4c):(eaY(),e4o),g=t==tbw?(QQ(),e3U):(QQ(),e3$),r=vN(DP(n),e.s),m=t==tbw?eHQ:eH1,c=l.Kc();c.Ob();)(s=Pp(c.Pb(),111)).c&&!(s.c.d.c.length<=0)&&(p=s.b.rf(),h=s.e,(d=(f=s.c).i).b=(a=f.n,f.e.a+a.b+a.c),d.a=(o=f.n,f.e.b+o.d+o.a),b?(d.c=h.a-(i=f.n,f.e.a+i.b+i.c)-e.s,b=!1):d.c=h.a+p.a+e.s,$C(g,ezr),f.f=g,JC(f,(Qs(),e3Y)),P_(r.d,new jH(d,elO(r,d))),m=t==tbw?eB4.Math.min(m,h.b):eB4.Math.max(m,h.b+s.b.rf().b));for(m+=t==tbw?-e.t:e.t,edp((r.e=m,r)),u=l.Kc();u.Ob();)(s=Pp(u.Pb(),111)).c&&!(s.c.d.c.length<=0)&&(d=s.c.i,d.c-=s.e.a,d.d-=s.e.b)}function eFA(e,t,n){var r;if(ewG(n,"StretchWidth layering",1),0==t.a.c.length){eEj(n);return}for(e.c=t,e.t=0,e.u=0,e.i=eHQ,e.g=eH1,e.d=gP(LV(e_k(t,(eBy(),toO)))),ebn(e),eTR(e),eTP(e),eyo(e),ed2(e),e.i=eB4.Math.max(1,e.i),e.g=eB4.Math.max(1,e.g),e.d=e.d/e.i,e.f=e.g/e.i,e.s=ebZ(e),r=new By(e.c),P_(e.c.b,r),e.r=WC(e.p),e.n=zb(e.k,e.k.length);0!=e.r.c.length;)e.o=ecu(e),!e.o||ess(e)&&0!=e.b.a.gc()?(ey6(e,r),r=new By(e.c),P_(e.c.b,r),er7(e.a,e.b),e.b.a.$b(),e.t=e.u,e.u=0):ess(e)?(e.c.b.c=Je(e1R,eUp,1,0,5,1),r=new By(e.c),P_(e.c.b,r),e.t=0,e.u=0,e.b.a.$b(),e.a.a.$b(),++e.f,e.r=WC(e.p),e.n=zb(e.k,e.k.length)):(Gu(e.o,r),QA(e.r,e.o),Yf(e.b,e.o),e.t=e.t-e.k[e.o.p]*e.d+e.j[e.o.p],e.u+=e.e[e.o.p]*e.d);t.a.c=Je(e1R,eUp,1,0,5,1),eSj(t.b),eEj(n)}function eFL(e){var t,n,r,i;for(_r(UJ(new R1(null,new Gq(e.a.b,16)),new rH),new r$),eyR(e),_r(UJ(new R1(null,new Gq(e.a.b,16)),new rz),new rG),e.c==(efE(),tpM)&&(_r(UJ(eeh(new R1(null,new Gq(new fk(e.f),1)),new rW),new rK),new hn(e)),_r(UJ(UQ(eeh(eeh(new R1(null,new Gq(e.d.b,16)),new rV),new rq),new rZ),new rX),new hi(e))),i=new kl(eHQ,eHQ),t=new kl(eH1,eH1),r=new fz(e.a.b);r.a0&&(e.c[t.c.p][t.p].d+=eMU(e.i,24)*e$h*.07000000029802322-.03500000014901161,e.c[t.c.p][t.p].a=e.c[t.c.p][t.p].d/e.c[t.c.p][t.p].b)}}function eFD(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m;for(p=new fz(e);p.ar.d,r.d=eB4.Math.max(r.d,t),s&&n&&(r.d=eB4.Math.max(r.d,r.a),r.a=r.d+i);break;case 3:n=t>r.a,r.a=eB4.Math.max(r.a,t),s&&n&&(r.a=eB4.Math.max(r.a,r.d),r.d=r.a+i);break;case 2:n=t>r.c,r.c=eB4.Math.max(r.c,t),s&&n&&(r.c=eB4.Math.max(r.b,r.c),r.b=r.c+i);break;case 4:n=t>r.b,r.b=eB4.Math.max(r.b,t),s&&n&&(r.b=eB4.Math.max(r.b,r.c),r.c=r.b+i)}}}function eFj(e){var t,n,r,i,a,o,s,u,c,l,f;for(c=new fz(e);c.a0||l.j==tbY&&l.e.c.length-l.g.c.length<0)){t=!1;break}for(i=new fz(l.g);i.a=c&&_>=m&&(d+=p.n.b+b.n.b+b.a.b-w,++s));if(n)for(o=new fz(v.e);o.a=c&&_>=m&&(d+=p.n.b+b.n.b+b.a.b-w,++s))}s>0&&(E+=d/s,++h)}h>0?(t.a=i*E/h,t.g=h):(t.a=0,t.g=0)}function eFY(e,t){var n,r,i,a,o,s,u,c,l,f,d;for(i=new fz(e.a.b);i.aeH1||t.o==tuE&&l0&&eno(g,w*E),_>0&&ens(g,_*S);for(ear(e.b,new te),t=new p0,s=new esz(new fS(e.c).a);s.b;)o=etz(s),r=Pp(o.cd(),79),n=Pp(o.dd(),395).a,i=eLO(r,!1,!1),f=ewM(e_I(r),eEF(i),n),eNI(f,i),(y=e_D(r))&&-1==QI(t,y,0)&&(t.c[t.c.length]=y,Hw(y,(A6(0!=f.b),Pp(f.a.a.c,8)),n));for(m=new esz(new fS(e.d).a);m.b;)b=etz(m),r=Pp(b.cd(),79),n=Pp(b.dd(),395).a,i=eLO(r,!1,!1),f=ewM(e_P(r),esP(eEF(i)),n),eNI(f=esP(f),i),(y=e_N(r))&&-1==QI(t,y,0)&&(t.c[t.c.length]=y,Hw(y,(A6(0!=f.b),Pp(f.c.b.c,8)),n))}function eFz(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k;if(0!=n.c.length){for(p=new p0,h=new fz(n);h.aeB4.Math.abs(v-m))continue;v1)for(h=new eRM(p,y,r),qX(y,new SV(e,h)),o.c[o.c.length]=h,f=y.a.ec().Kc();f.Ob();)QA(a,(l=Pp(f.Pb(),46)).b);if(s.a.gc()>1)for(h=new eRM(p,s,r),qX(s,new Sq(e,h)),o.c[o.c.length]=h,f=s.a.ec().Kc();f.Ob();)QA(a,(l=Pp(f.Pb(),46)).b)}}function eFJ(e){_Y(e,new ewB(vZ(vQ(vq(vJ(vX(new oc,eqp),"ELK Radial"),'A radial layout provider which is based on the algorithm of Peter Eades published in "Drawing free trees.", published by International Institute for Advanced Study of Social Information Science, Fujitsu Limited in 1991. The radial layouter takes a tree and places the nodes in radial order around the root. The nodes of the same tree level are placed on the same radius.'),new aW),eqp))),KE(e,eqp,eVT,epB(tlw)),KE(e,eqp,eGi,epB(tlS)),KE(e,eqp,eGh,epB(tlh)),KE(e,eqp,eGM,epB(tlp)),KE(e,eqp,eGd,epB(tlb)),KE(e,eqp,eGp,epB(tld)),KE(e,eqp,eGf,epB(tlm)),KE(e,eqp,eGb,epB(tly)),KE(e,eqp,eql,epB(tll)),KE(e,eqp,eqc,epB(tlf)),KE(e,eqp,eqh,epB(tlg)),KE(e,eqp,eqs,epB(tlv)),KE(e,eqp,equ,epB(tl_)),KE(e,eqp,eqf,epB(tlE)),KE(e,eqp,eqd,epB(tlk))}function eFQ(e){var t;if(this.r=U2(new ex,new eT),this.b=new efY(Pp(Y9(e6a),290)),this.p=new efY(Pp(Y9(e6a),290)),this.i=new efY(Pp(Y9(e3n),290)),this.e=e,this.o=new TS(e.rf()),this.D=e.Df()||gN(LK(e.We((eBB(),thh)))),this.A=Pp(e.We((eBB(),thx)),21),this.B=Pp(e.We(thL),21),this.q=Pp(e.We(thV),98),this.u=Pp(e.We(thJ),21),!e_y(this.u))throw p7(new gq("Invalid port label placement: "+this.u));if(this.v=gN(LK(e.We(th1))),this.j=Pp(e.We(thS),21),!eM1(this.j))throw p7(new gq("Invalid node label placement: "+this.j));this.n=Pp(egG(e,th_),116),this.k=gP(LV(egG(e,tps))),this.d=gP(LV(egG(e,tpo))),this.w=gP(LV(egG(e,tpp))),this.s=gP(LV(egG(e,tpu))),this.t=gP(LV(egG(e,tpc))),this.C=Pp(egG(e,tpd),142),this.c=2*this.d,t=!this.B.Hc((eI3(),tbX)),this.f=new eh6(0,t,0),this.g=new eh6(1,t,0),gh(this.f,(etx(),e3N),this.g)}function eF1(e,t,n,r,i){var a,o,s,u,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M;for(w=0,b=0,p=0,h=1,y=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));y.e!=y.i.gc();)g=Pp(epH(y),33),h+=VG(new Fa(OH(eOi(g).a.Kc(),new c))),x=g.g,b=eB4.Math.max(b,x),d=g.f,p=eB4.Math.max(p,d),w+=x*d;for(m=(e.a||(e.a=new FQ(e6k,e,10,11)),e.a).i,o=w+2*r*r*h*m,a=eB4.Math.sqrt(o),u=eB4.Math.max(a*n,b),s=eB4.Math.max(a/n,p),v=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));v.e!=v.i.gc();)g=Pp(epH(v),33),T=i.b+(eMU(t,26)*e$l+eMU(t,27)*e$f)*(u-g.g),M=i.b+(eMU(t,26)*e$l+eMU(t,27)*e$f)*(s-g.f),eno(g,T),ens(g,M);for(k=u+(i.b+i.c),S=s+(i.d+i.a),E=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));E.e!=E.i.gc();)for(_=Pp(epH(E),33),f=new Fa(OH(eOi(_).a.Kc(),new c));eTk(f);)l=Pp(ZC(f),79),eTc(l)||eBv(l,t,k,S);eYx(e,k+=i.b+i.c,S+=i.d+i.a,!1,!0)}function eF0(e){var t,n,r,i,a,o,s,u,c,l,f;if(null==e)throw p7(new vo(eUg));if(c=e,a=e.length,u=!1,a>0&&(45==(t=(GV(0,e.length),e.charCodeAt(0)))||43==t)&&(e=e.substr(1),--a,u=45==t),0==a)throw p7(new vo(eHJ+c+'"'));for(;e.length>0&&(GV(0,e.length),48==e.charCodeAt(0));)e=e.substr(1),--a;if(a>(eDZ(),e0G)[10])throw p7(new vo(eHJ+c+'"'));for(i=0;i0&&(f=-parseInt(e.substr(0,r),10),e=e.substr(r),a-=r,n=!1);a>=o;){if(r=parseInt(e.substr(0,o),10),e=e.substr(o),a-=o,n)n=!1;else{if(0>ecd(f,s))throw p7(new vo(eHJ+c+'"'));f=efn(f,l)}f=efe(f,r)}if(ecd(f,0)>0||!u&&(f=QC(f),0>ecd(f,0)))throw p7(new vo(eHJ+c+'"'));return f}function eF2(e,t){var n,r,i,a,o,s,u;if(Rm(),this.a=new MW(this),this.b=e,this.c=t,this.f=Yg(QZ((eSp(),tvc),t)),this.f.dc()){if((s=ev1(tvc,e))==t)for(this.e=!0,this.d=new p0,this.f=new o5,this.f.Fc(eQB),Pp(eP9(Qq(tvc,etP(e)),""),26)==e&&this.f.Fc(Fr(tvc,etP(e))),i=eIT(tvc,e).Kc();i.Ob();)switch(Ur(QZ(tvc,r=Pp(i.Pb(),170)))){case 4:this.d.Fc(r);break;case 5:this.f.Gc(Yg(QZ(tvc,r)))}else if(_4(),Pp(t,66).Oj())for(o=0,this.e=!0,this.f=null,this.d=new p0,u=(null==e.i&&eNT(e),e.i).length;o=0&&o0&&(Pp(UA(e.b,t),124).a.b=n)}function eF4(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g;for(ewG(t,"Comment pre-processing",1),n=0,u=new fz(e.a);u.a0&&64!=(u=(GV(0,t.length),t.charCodeAt(0)))){if(37==u&&(f=t.lastIndexOf("%"),c=!1,0!=f&&(f==d-1||(c=(GV(f+1,t.length),46==t.charCodeAt(f+1)))))){if(y=IE("%",o=t.substr(1,f-1))?null:eYy(o),r=0,c)try{r=eDa(t.substr(f+2),eHt,eUu)}catch(w){if(w=eoa(w),M4(w,127))throw s=w,p7(new QH(s));throw p7(w)}for(m=erW(e.Wg());m.Ob();)if(M4(p=eaO(m),510)&&(v=(i=Pp(p,590)).d,(null==y?null==v:IE(y,v))&&0==r--))return i;return null}if(h=-1==(l=t.lastIndexOf("."))?t:t.substr(0,l),n=0,-1!=l)try{n=eDa(t.substr(l+1),eHt,eUu)}catch(_){if(_=eoa(_),M4(_,127))h=t;else throw p7(_)}for(h=IE("%",h)?null:eYy(h),b=erW(e.Wg());b.Ob();)if(M4(p=eaO(b),191)&&(g=(a=Pp(p,191)).ne(),(null==h?null==g:IE(h,g))&&0==n--))return a;return null}return eR2(e,t)}function eF8(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M;for(E=new p0,p=new fz(e.b);p.a=e.length)return{done:!0};var r=e[n++];return{value:[r,t.get(r)],done:!1}}}},eCi()||(e.prototype.createObject=function(){return{}},e.prototype.get=function(e){return this.obj[":"+e]},e.prototype.set=function(e,t){this.obj[":"+e]=t},e.prototype[e$c]=function(e){delete this.obj[":"+e]},e.prototype.keys=function(){var e=[];for(var t in this.obj)58==t.charCodeAt(0)&&e.push(t.substring(1));return e}),e}function eYt(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m;if(eNl(),null==e)return null;if(0==(f=8*e.length))return"";for(u=0,s=f%24,h=f/24|0,a=null,a=Je(tyw,eHl,25,4*(d=0!=s?h+1:h),15,1),c=0,l=0,t=0,n=0,r=0,o=0,i=0;u>24,c=(3&t)<<24>>24,p=(-128&t)==0?t>>2<<24>>24:(t>>2^192)<<24>>24,b=(-128&n)==0?n>>4<<24>>24:(n>>4^240)<<24>>24,m=(-128&r)==0?r>>6<<24>>24:(r>>6^252)<<24>>24,a[o++]=tvQ[p],a[o++]=tvQ[b|c<<4],a[o++]=tvQ[l<<2|m],a[o++]=tvQ[63&r];return 8==s?(c=(3&(t=e[i]))<<24>>24,p=(-128&t)==0?t>>2<<24>>24:(t>>2^192)<<24>>24,a[o++]=tvQ[p],a[o++]=tvQ[c<<4],a[o++]=61,a[o++]=61):16==s&&(t=e[i],l=(15&(n=e[i+1]))<<24>>24,c=(3&t)<<24>>24,p=(-128&t)==0?t>>2<<24>>24:(t>>2^192)<<24>>24,b=(-128&n)==0?n>>4<<24>>24:(n>>4^240)<<24>>24,a[o++]=tvQ[p],a[o++]=tvQ[b|c<<4],a[o++]=tvQ[l<<2],a[o++]=61),ehv(a,0,a.length)}function eYn(e,t){var n,r,i,a,o,s,u;if(0==e.e&&e.p>0&&(e.p=-(e.p-1)),e.p>eHt&&V9(t,e.p-eHx),o=t.q.getDate(),zC(t,1),e.k>=0&&z7(t,e.k),e.c>=0?zC(t,e.c):e.k>=0?(r=35-(u=new est(t.q.getFullYear()-eHx,t.q.getMonth(),35)).q.getDate(),zC(t,eB4.Math.min(r,o))):zC(t,o),e.f<0&&(e.f=t.q.getHours()),e.b>0&&e.f<12&&(e.f+=12),M5(t,24==e.f&&e.g?0:e.f),e.j>=0&&Z0(t,e.j),e.n>=0&&Jf(t,e.n),e.i>=0&&xN(t,eft(efn(eyt(eap(t.q.getTime()),eHf),eHf),e.i)),e.a&&(V9(i=new wW,i.q.getFullYear()-eHx-80),Ei(eap(t.q.getTime()),eap(i.q.getTime()))&&V9(t,i.q.getFullYear()-eHx+100)),e.d>=0){if(-1==e.c)(n=(7+e.d-t.q.getDay())%7)>3&&(n-=7),s=t.q.getMonth(),zC(t,t.q.getDate()+n),t.q.getMonth()!=s&&zC(t,t.q.getDate()+(n>0?-7:7));else if(t.q.getDay()!=e.d)return!1}return e.o>eHt&&(a=t.q.getTimezoneOffset(),xN(t,eft(eap(t.q.getTime()),(e.o-a)*60*eHf))),!0}function eYr(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w;if(i=e_k(t,(eBU(),tnc)),M4(i,239)){for(p=Pp(i,33),b=t.e,d=new TS(t.c),a=t.d,d.a+=a.b,d.b+=a.d,w=Pp(eT8(p,(eBy(),ta9)),174),Aa(w,(eI3(),tbJ))&&(h=Pp(eT8(p,ta7),116),lR(h,a.a),lG(h,a.d),lj(h,a.b),lW(h,a.c)),n=new p0,l=new fz(t.a);l.a0&&P_(e.p,f),P_(e.o,f);t-=r,p=u+t,l+=t*e.e,q1(e.a,s,ell(p)),q1(e.b,s,l),e.j=eB4.Math.max(e.j,p),e.k=eB4.Math.max(e.k,l),e.d+=t,t+=m}}function eYu(){var e;eYu=A,tbF=new kS(ezo,0),tbw=new kS(ezb,1),tby=new kS(ezm,2),tbj=new kS(ezg,3),tbY=new kS(ezv,4),tbx=(Hj(),new vd((e=Pp(yw(e6a),9),new I1(e,Pp(CY(e,e.length),9),0)))),tbT=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[]))),tb_=ecO(jL(tby,eow(vx(e6a,1),eGj,61,0,[]))),tbN=ecO(jL(tbj,eow(vx(e6a,1),eGj,61,0,[]))),tbR=ecO(jL(tbY,eow(vx(e6a,1),eGj,61,0,[]))),tbC=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tbj]))),tbk=ecO(jL(tby,eow(vx(e6a,1),eGj,61,0,[tbY]))),tbD=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tbY]))),tbM=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tby]))),tbP=ecO(jL(tbj,eow(vx(e6a,1),eGj,61,0,[tbY]))),tbE=ecO(jL(tby,eow(vx(e6a,1),eGj,61,0,[tbj]))),tbL=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tby,tbY]))),tbS=ecO(jL(tby,eow(vx(e6a,1),eGj,61,0,[tbj,tbY]))),tbI=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tbj,tbY]))),tbO=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tby,tbj]))),tbA=ecO(jL(tbw,eow(vx(e6a,1),eGj,61,0,[tby,tbj,tbY])))}function eYc(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y;if(0!=t.b){for(h=new _n,s=null,p=null,r=zy(eB4.Math.floor(eB4.Math.log(t.b)*eB4.Math.LOG10E)+1),u=0,y=epL(t,0);y.b!=y.d.c;)for(g=Pp(Vv(y),86),xc(p)!==xc(e_k(g,(eR6(),tca)))&&(p=Lq(e_k(g,tca)),u=0),eo3(g,tca,s=null!=p?p+WB(u++,r):WB(u++,r)),m=(i=epL(new hz(g).a.d,0),new hG(i));yV(m.a);)qQ(h,b=Pp(Vv(m.a),188).c,h.c.b,h.c),eo3(b,tca,s);for(o=0,d=new p2;o=u){A6(g.b>0),g.a.Xb(g.c=--g.b);break}b.a>c&&(i?(eoc(i.b,b.b),i.a=eB4.Math.max(i.a,b.a),BH(g)):(P_(b.b,f),b.c=eB4.Math.min(b.c,c),b.a=eB4.Math.max(b.a,u),i=b))}i||((i=new mi).c=c,i.a=u,CD(g,i),P_(i.b,f))}for(s=t.b,l=0,m=new fz(r);m.as?1:0:(e.b&&(e.b._b(a)&&(i=Pp(e.b.xc(a),19).a),e.b._b(u)&&(s=Pp(e.b.xc(u),19).a)),is?1:0);return 0!=t.e.c.length&&0!=n.g.c.length?1:-1}function eYd(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S;for(ewG(t,eWo,1),b=new p0,E=new p0,c=new fz(e.b);c.a0&&(w-=p),eRU(o,w),f=0,h=new fz(o.a);h.a0),s.a.Xb(s.c=--s.b)),u=.4*r*f,!a&&s.bt.d.c){if((p=e.c[t.a.d])==(g=e.c[d.a.d]))continue;eAx(_f(_l(_d(_c(new bQ,1),100),p),g))}}}}}}function eYy(e){var t,n,r,i,a,o,s,u;if(eRe(),null==e)return null;if((i=x7(e,e_n(37)))<0)return e;for(u=new O0(e.substr(0,i)),t=Je(tyk,eZ8,25,4,15,1),s=0,r=0,o=e.length;ii+2&&eoV((GV(i+1,e.length),e.charCodeAt(i+1)),tmZ,tmX)&&eoV((GV(i+2,e.length),e.charCodeAt(i+2)),tmZ,tmX)){if(n=P0((GV(i+1,e.length),e.charCodeAt(i+1)),(GV(i+2,e.length),e.charCodeAt(i+2))),i+=2,r>0?(192&n)==128?t[s++]=n<<24>>24:r=0:n>=128&&((224&n)==192?(t[s++]=n<<24>>24,r=2):(240&n)==224?(t[s++]=n<<24>>24,r=3):(248&n)==240&&(t[s++]=n<<24>>24,r=4)),r>0){if(s==r){switch(s){case 2:Bd(u,((31&t[0])<<6|63&t[1])&eHd);break;case 3:Bd(u,((15&t[0])<<12|(63&t[1])<<6|63&t[2])&eHd)}s=0,r=0}}else{for(a=0;a0){if(o+r>e.length)return!1;s=exf(e.substr(0,o+r),t)}else s=exf(e,t)}switch(a){case 71:return s=ew6(e,o,eow(vx(e17,1),eUP,2,6,[eHM,eHO]),t),i.e=s,!0;case 77:return eLY(e,t,i,s,o);case 76:return eLB(e,t,i,s,o);case 69:return eS$(e,t,o,i);case 99:return eSz(e,t,o,i);case 97:return s=ew6(e,o,eow(vx(e17,1),eUP,2,6,["AM","PM"]),t),i.b=s,!0;case 121:return eLU(e,t,o,s,n,i);case 100:if(s<=0)return!1;return i.c=s,!0;case 83:if(s<0)return!1;return edc(s,o,t[0],i);case 104:12==s&&(s=0);case 75:case 72:if(s<0)return!1;return i.f=s,i.g=!1,!0;case 107:if(s<0)return!1;return i.f=s,i.g=!0,!0;case 109:if(s<0)return!1;return i.j=s,!0;case 115:if(s<0)return!1;return i.n=s,!0;case 90:if(oE&&(p.c=E-p.b),P_(o.d,new jH(p,elO(o,p))),v=t==tbw?eB4.Math.max(v,b.b+c.b.rf().b):eB4.Math.min(v,b.b));for(v+=t==tbw?e.t:-e.t,(y=edp((o.e=v,o)))>0&&(Pp(UA(e.b,t),124).a.b=y),l=d.Kc();l.Ob();)(c=Pp(l.Pb(),111)).c&&!(c.c.d.c.length<=0)&&(p=c.c.i,p.c-=c.e.a,p.d-=c.e.b)}function eYE(e){var t,n,r,i,a,o,s,u,l,f,d,h,p;for(t=new p2,u=new Ow(e);u.e!=u.i.gc();){for(s=Pp(epH(u),33),n=new bV,Um(e9t,s,n),p=new e5,i=Pp(qE(new R1(null,new YI(new Fa(OH(eOr(s).a.Kc(),new c)))),jD(p,JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[(eum(),e2U)])))),83),enC(n,Pp(i.xc((OQ(),!0)),14),new e6),o=(r=Pp(qE(UJ(Pp(i.xc(!1),15).Lc(),new e9),JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[e2U]))),15)).Kc();o.Ob();)(h=e_D(a=Pp(o.Pb(),79)))&&((l=Pp(xu($I(t.f,h)),21))||(l=eA7(h),eS9(t.f,h,l)),er7(n,l));for(i=Pp(qE(new R1(null,new YI(new Fa(OH(eOi(s).a.Kc(),new c)))),jD(p,JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[e2U])))),83),enC(n,Pp(i.xc(!0),14),new e8),d=(r=Pp(qE(UJ(Pp(i.xc(!1),15).Lc(),new e7),JF(new U,new B,new en,eow(vx(e2L,1),eU4,132,0,[e2U]))),15)).Kc();d.Ob();)(h=e_N(f=Pp(d.Pb(),79)))&&((l=Pp(xu($I(t.f,h)),21))||(l=eA7(h),eS9(t.f,h,l)),er7(n,l))}}function eYS(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b;if(ePN(),(u=0>ecd(e,0))&&(e=QC(e)),0==ecd(e,0))switch(t){case 0:return"0";case 1:return e$e;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return h=new vc,t<0?(h.a+="0E+",h):(h.a+="0E",h),h.a+=t==eHt?"2147483648":""+-t,h.a}f=Je(tyw,eHl,25,(l=18)+1,15,1),n=l,b=e;do c=b,b=eyt(b,10),f[--n]=jE(eft(48,efe(c,efn(b,10))))&eHd;while(0!=ecd(b,0))if(i=efe(efe(efe(l,n),t),1),0==t)return u&&(f[--n]=45),ehv(f,n,l-n);if(t>0&&ecd(i,-6)>=0){if(ecd(i,0)>=0){for(a=n+jE(i),s=l-1;s>=a;s--)f[s+1]=f[s];return f[++a]=46,u&&(f[--n]=45),ehv(f,n,l-n+1)}for(o=2;Ei(o,eft(QC(i),1));o++)f[--n]=48;return f[--n]=46,f[--n]=48,u&&(f[--n]=45),ehv(f,n,l-n)}return p=n+1,r=l,d=new vl,u&&(d.a+="-"),r-p>=1?(Bd(d,f[n]),d.a+=".",d.a+=ehv(f,n+1,l-n-1)):d.a+=ehv(f,n,l-n),d.a+="E",ecd(i,0)>0&&(d.a+="+"),d.a+=""+Fb(i),d.a}function eYk(e,t,n){var r,i,a,o,s,u,c,l,f,d,h;if(e.e.a.$b(),e.f.a.$b(),e.c.c=Je(e1R,eUp,1,0,5,1),e.i.c=Je(e1R,eUp,1,0,5,1),e.g.a.$b(),t)for(o=new fz(t.a);o.a=1&&(_-c>0&&p>=0?(eno(f,f.i+w),ens(f,f.j+u*c)):_-c<0&&h>=0&&(eno(f,f.i+w*_),ens(f,f.j+u)));return ebu(e,(eBB(),thx),(ed6(),a=Pp(yw(e6o),9),new I1(a,Pp(CY(a,a.length),9),0))),new kl(E,l)}function eYT(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p;if(h=z$(ewH(Pp(etj((e.b||(e.b=new Ih(e6m,e,4,7)),e.b),0),82))),p=z$(ewH(Pp(etj((e.c||(e.c=new Ih(e6m,e,5,8)),e.c),0),82))),f=h==p,s=new yb,(t=Pp(eT8(e,(euw(),tpj)),74))&&t.b>=2){if(0==(e.a||(e.a=new FQ(e6v,e,6,6)),e.a).i)n=(yT(),i=new oQ),JL((e.a||(e.a=new FQ(e6v,e,6,6)),e.a),n);else if((e.a||(e.a=new FQ(e6v,e,6,6)),e.a).i>1)for(d=new AF((e.a||(e.a=new FQ(e6v,e,6,6)),e.a));d.e!=d.i.gc();)ey_(d);eNI(t,Pp(etj((e.a||(e.a=new FQ(e6v,e,6,6)),e.a),0),202))}if(f)for(r=new Ow((e.a||(e.a=new FQ(e6v,e,6,6)),e.a));r.e!=r.i.gc();)for(n=Pp(epH(r),202),c=new Ow((n.a||(n.a=new O_(e6h,n,5)),n.a));c.e!=c.i.gc();)u=Pp(epH(c),469),s.a=eB4.Math.max(s.a,u.a),s.b=eB4.Math.max(s.b,u.b);for(o=new Ow((e.n||(e.n=new FQ(e6S,e,1,7)),e.n));o.e!=o.i.gc();)a=Pp(epH(o),137),(l=Pp(eT8(a,tp$),8))&&TP(a,l.a,l.b),f&&(s.a=eB4.Math.max(s.a,a.i+a.g),s.b=eB4.Math.max(s.b,a.j+a.f));return s}function eYM(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,k,x;for(s=0,y=t.c.length,i=new eIW(e.a,n,null,null),x=Je(tyx,eH5,25,y,15,1),b=Je(tyx,eH5,25,y,15,1),p=Je(tyx,eH5,25,y,15,1),m=0;sx[u]&&(m=u),f=new fz(e.a.b);f.ah&&(a&&(xL(E,d),xL(k,ell(c.b-1))),A=n.b,L+=d+t,d=0,l=eB4.Math.max(l,n.b+n.c+O)),eno(s,A),ens(s,L),l=eB4.Math.max(l,A+O+n.c),d=eB4.Math.max(d,f),A+=O+t;if(l=eB4.Math.max(l,r),(M=L+d+n.a)ez8,x=eB4.Math.abs(d.b-p.b)>ez8,(!n&&k&&x||n&&(k||x))&&P7(m.a,w)),er7(m.a,r),d=0==r.b?w:(A6(0!=r.b),Pp(r.c.b.c,8)),ea1(h,f,b),eiy(i)==S&&(Bq(S.i)!=i.a&&eSb(b=new yb,Bq(S.i),v),eo3(m,tnC,b)),eEw(h,m,v),l.a.zc(h,l);Gs(m,_),Go(m,S)}for(c=l.a.ec().Kc();c.Ob();)Gs(u=Pp(c.Pb(),17),null),Go(u,null);eEj(t)}function eYC(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w;if(1==e.gc())return Pp(e.Xb(0),231);if(0>=e.gc())return new Z5;for(i=e.Kc();i.Ob();){for(n=Pp(i.Pb(),231),p=0,l=eUu,f=eUu,u=eHt,c=eHt,h=new fz(n.e);h.as&&(y=0,w+=o+g,o=0),eIJ(b,n,y,w),t=eB4.Math.max(t,y+m.a),o=eB4.Math.max(o,m.b),y+=m.a+g;return b}function eYI(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p;switch(l=new mE,e.a.g){case 3:d=Pp(e_k(t.e,(eBU(),tnO)),15),h=Pp(e_k(t.j,tnO),15),p=Pp(e_k(t.f,tnO),15),n=Pp(e_k(t.e,tnT),15),r=Pp(e_k(t.j,tnT),15),i=Pp(e_k(t.f,tnT),15),o=new p0,eoc(o,d),h.Jc(new iN),eoc(o,M4(h,152)?ZK(Pp(h,152)):M4(h,131)?Pp(h,131).a:M4(h,54)?new gn(h):new w$(h)),eoc(o,p),a=new p0,eoc(a,n),eoc(a,M4(r,152)?ZK(Pp(r,152)):M4(r,131)?Pp(r,131).a:M4(r,54)?new gn(r):new w$(r)),eoc(a,i),eo3(t.f,tnO,o),eo3(t.f,tnT,a),eo3(t.f,tnA,t.f),eo3(t.e,tnO,null),eo3(t.e,tnT,null),eo3(t.j,tnO,null),eo3(t.j,tnT,null);break;case 1:er7(l,t.e.a),P7(l,t.i.n),er7(l,eaa(t.j.a)),P7(l,t.a.n),er7(l,t.f.a);break;default:er7(l,t.e.a),er7(l,eaa(t.j.a)),er7(l,t.f.a)}HC(t.f.a),er7(t.f.a,l),Gs(t.f,t.e.c),s=Pp(e_k(t.e,(eBy(),taR)),74),c=Pp(e_k(t.j,taR),74),u=Pp(e_k(t.f,taR),74),(s||c||u)&&(Yp(f=new mE,u),Yp(f,c),Yp(f,s),eo3(t.f,taR,f)),Gs(t.j,null),Go(t.j,null),Gs(t.e,null),Go(t.e,null),Gu(t.a,null),Gu(t.i,null),t.g&&eYI(e,t.g)}function eYD(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m;if(eNl(),null==e||(a=Q4(e),(p=elw(a))%4!=0))return null;if(0==(b=p/4|0))return Je(tyk,eZ8,25,0,15,1);for(f=null,t=0,n=0,r=0,i=0,o=0,s=0,u=0,c=0,h=0,d=0,l=0,f=Je(tyk,eZ8,25,3*b,15,1);h>4)<<24>>24,f[d++]=((15&n)<<4|r>>2&15)<<24>>24,f[d++]=(r<<6|i)<<24>>24}if(!wl(o=a[l++])||!wl(s=a[l++]))return null;if(t=tvJ[o],n=tvJ[s],u=a[l++],c=a[l++],-1==tvJ[u]||-1==tvJ[c])return 61==u&&61==c?(15&n)!=0?null:(m=Je(tyk,eZ8,25,3*h+1,15,1),ePD(f,0,m,0,3*h),m[d]=(t<<2|n>>4)<<24>>24,m):61==u||61!=c?null:(3&(r=tvJ[u]))!=0?null:(m=Je(tyk,eZ8,25,3*h+2,15,1),ePD(f,0,m,0,3*h),m[d++]=(t<<2|n>>4)<<24>>24,m[d]=((15&n)<<4|r>>2&15)<<24>>24,m);return r=tvJ[u],i=tvJ[c],f[d++]=(t<<2|n>>4)<<24>>24,f[d++]=((15&n)<<4|r>>2&15)<<24>>24,f[d++]=(r<<6|i)<<24>>24,f}function eYN(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_;for(ewG(t,eWo,1),p=Pp(e_k(e,(eBy(),tag)),218),i=new fz(e.b);i.a=2){for(b=!0,n=Pp(Wx(d=new fz(a.j)),11),h=null;d.a0&&(i=Pp(RJ(m.c.a,E-1),10),o=e.i[i.p],k=eB4.Math.ceil(Mj(e.n,i,m)),a=_.a.e-m.d.d-(o.a.e+i.o.b+i.d.a)-k),c=eHQ,E0&&S.a.e.e-S.a.a-(S.b.e.e-S.b.a)<0,p=y.a.e.e-y.a.a-(y.b.e.e-y.b.a)<0&&S.a.e.e-S.a.a-(S.b.e.e-S.b.a)>0,h=y.a.e.e+y.b.aS.b.e.e+S.a.a,w=0,!b&&!p&&(d?a+f>0?w=f:c-r>0&&(w=r):h&&(a+s>0?w=s:c-v>0&&(w=v))),_.a.e+=w,_.b&&(_.d.e+=w),!1))}function eYR(e,t,n){var r,i,a,o,s,u,c,l,f,d;if(r=new Hr(t.qf().a,t.qf().b,t.rf().a,t.rf().b),i=new TE,e.c)for(o=new fz(t.wf());o.ac&&(r.a+=M3(Je(tyw,eHl,25,-c,15,1))),r.a+="Is",x7(u,e_n(32))>=0)for(i=0;i=r.o.b/2}v?(g=Pp(e_k(r,(eBU(),tnI)),15))?d?a=g:(i=Pp(e_k(r,ttB),15))?a=g.gc()<=i.gc()?g:i:(a=new p0,eo3(r,ttB,a)):(a=new p0,eo3(r,tnI,a)):(i=Pp(e_k(r,(eBU(),ttB)),15))?f?a=i:(g=Pp(e_k(r,tnI),15))?a=i.gc()<=g.gc()?i:g:(a=new p0,eo3(r,tnI,a)):(a=new p0,eo3(r,ttB,a)),a.Fc(e),eo3(e,(eBU(),ttH),n),t.d==n?(Go(t,null),n.e.c.length+n.g.c.length==0&&Gc(n,null),esQ(n)):(Gs(t,null),n.e.c.length+n.g.c.length==0&&Gc(n,null)),HC(t.a)}function eYH(e,t){var n,r,i,a,o,s,u,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A,L;for(y=new KB(e.b,0),f=t.Kc(),b=0,l=Pp(f.Pb(),19).a,E=0,n=new bV,k=new Tw;y.b=e.a&&(r=eN3(e,y),f=eB4.Math.max(f,r.b),_=eB4.Math.max(_,r.d),P_(s,new kD(y,r)));for(l=0,x=new p0;l0),g.a.Xb(g.c=--g.b),T=new By(e.b),CD(g,T),A6(g.b0?(c=0,m&&(c+=s),c+=(x-1)*o,y&&(c+=s),k&&y&&(c=eB4.Math.max(c,eAD(y,o,v,S))),!(c0){for(i=0,d=l<100?null:new yf(l),p=(c=new eiP(t)).g,g=Je(ty_,eHT,25,l,15,1),r=0,w=new eta(l);i=0;)if(null!=h?ecX(h,p[u]):xc(h)===xc(p[u])){g.length<=r&&(m=g,g=Je(ty_,eHT,25,2*g.length,15,1),ePD(m,0,g,0,r)),g[r++]=i,JL(w,p[u]);break v}if(xc(h)===xc(s))break}}if(c=w,p=w.g,l=r,r>g.length&&(m=g,g=Je(ty_,eHT,25,r,15,1),ePD(m,0,g,0,r)),r>0){for(a=0,y=!0;a=0;)egk(e,g[o]);if(r!=l){for(i=l;--i>=r;)egk(c,i);m=g,g=Je(ty_,eHT,25,r,15,1),ePD(m,0,g,0,r)}t=c}}}else for(t=egh(e,t),i=e.i;--i>=0;)t.Hc(e.g[i])&&(egk(e,i),y=!0);if(!y)return!1;if(null!=g){for(f=1==(n=t.gc())?Gt(e,4,t.Kc().Pb(),null,g[0],b):Gt(e,6,t,g,g[0],b),d=n<100?null:new yf(n),i=t.Kc();i.Ob();)d=IW(e,Pp(h=i.Pb(),72),d);d?(d.Ei(f),d.Fi()):eam(e.e,f)}else{for(d=IP(t.gc()),i=t.Kc();i.Ob();)d=IW(e,Pp(h=i.Pb(),72),d);d&&d.Fi()}return!0}function eYV(e,t){var n,r,i,a,o,s,u,l,f,d,h,p,b,m,g,v,y,w;for((n=new eb_(t)).a||eDc(t),l=eCx(t),u=new zu,g=new eLy,m=new fz(t.a);m.a0||n.o==tuS&&i0?(f=Pp(RJ(d.c.a,o-1),10),k=Mj(e.b,d,f),m=d.n.b-d.d.d-(f.n.b+f.o.b+f.d.a+k)):m=d.n.b-d.d.d,c=eB4.Math.min(m,c),oo?eIc(e,t,n):eIc(e,n,t),io?1:0}return r=Pp(e_k(t,(eBU(),tnu)),19).a,a=Pp(e_k(n,tnu),19).a,r>a?eIc(e,t,n):eIc(e,n,t),ra?1:0}function eYQ(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p,b,m,g,v;if(gN(LK(eT8(t,(eBB(),thI))))||(c=0!=(t.a||(t.a=new FQ(e6k,t,10,11)),t.a).i,l=!(f=ekq(t)).dc(),!c&&!l))return Hj(),Hj(),e2r;if(!(i=Pp(eT8(t,th6),149)))throw p7(new gq("Resolved algorithm is not set; apply a LayoutAlgorithmResolver before computing layout."));if(v=ka(i,(eTy(),tmC)),ept(t),!c&&l&&!v)return Hj(),Hj(),e2r;if(u=new p0,xc(eT8(t,thl))===xc((eck(),tpz))&&(ka(i,tmO)||ka(i,tmM)))for(h=eCL(e,t),er7(p=new _n,(t.a||(t.a=new FQ(e6k,t,10,11)),t.a));0!=p.b;)ept(d=Pp(0==p.b?null:(A6(0!=p.b),etw(p,p.a.a)),33)),(g=xc(eT8(d,thl))===xc(tpW))||X2(d,tdQ)&&!Zs(i,eT8(d,th6))?(s=eYQ(e,d,n,r),eoc(u,s),ebu(d,thl,tpW),eIU(d)):er7(p,(d.a||(d.a=new FQ(e6k,d,10,11)),d.a));else for(h=(t.a||(t.a=new FQ(e6k,t,10,11)),t.a).i,o=new Ow((t.a||(t.a=new FQ(e6k,t,10,11)),t.a));o.e!=o.i.gc();)a=Pp(epH(o),33),s=eYQ(e,a,n,r),eoc(u,s),eIU(a);for(m=new fz(u);m.a=0?ef9(s):elC(ef9(s)),e.Ye(tob,h)),c=new yb,d=!1,e.Xe(tou)?(Lf(c,Pp(e.We(tou),8)),d=!0):Oc(c,o.a/2,o.b/2),h.g){case 4:eo3(l,taY,(ef_(),tnN)),eo3(l,ttV,(eoG(),te0)),l.o.b=o.b,b<0&&(l.o.a=-b),ekv(f,(eYu(),tby)),d||(c.a=o.a),c.a-=o.a;break;case 2:eo3(l,taY,(ef_(),tnR)),eo3(l,ttV,(eoG(),teQ)),l.o.b=o.b,b<0&&(l.o.a=-b),ekv(f,(eYu(),tbY)),d||(c.a=0);break;case 1:eo3(l,tt9,(Q1(),ttN)),l.o.a=o.a,b<0&&(l.o.b=-b),ekv(f,(eYu(),tbj)),d||(c.b=o.b),c.b-=o.b;break;case 3:eo3(l,tt9,(Q1(),ttI)),l.o.a=o.a,b<0&&(l.o.b=-b),ekv(f,(eYu(),tbw)),d||(c.b=0)}if(Lf(f.n,c),eo3(l,tou,c),t==tba||t==tbs||t==tbo){if(p=0,t==tba&&e.Xe(tof))switch(h.g){case 1:case 2:p=Pp(e.We(tof),19).a;break;case 3:case 4:p=-Pp(e.We(tof),19).a}else switch(h.g){case 4:case 2:p=a.b,t==tbs&&(p/=i.b);break;case 1:case 3:p=a.a,t==tbs&&(p/=i.a)}eo3(l,tnv,p)}return eo3(l,tt1,h),l}function eY0(e){var t,n,r,i,a,o,s,u,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T;if((n=gP(LV(e_k(e.a.j,(eBy(),tar)))))<-1||!e.a.i||IR(Pp(e_k(e.a.o,tol),98))||2>efr(e.a.o,(eYu(),tby)).gc()&&2>efr(e.a.o,tbY).gc())return!0;if(e.a.c.Rf())return!1;for(E=0,_=0,w=new p0,u=e.a.e,l=0,f=u.length;l=n}function eY2(){function n(e){var t=this;this.dispatch=function(t){var n=t.data;switch(n.cmd){case"algorithms":var r=edh((Hj(),new fF(new fT(tmF.b))));e.postMessage({id:n.id,data:r});break;case"categories":var i=edh((Hj(),new fF(new fT(tmF.c))));e.postMessage({id:n.id,data:i});break;case"options":var a=edh((Hj(),new fF(new fT(tmF.d))));e.postMessage({id:n.id,data:a});break;case"register":ejy(n.algorithms),e.postMessage({id:n.id});break;case"layout":ePu(n.graph,n.layoutOptions||{},n.options||{}),e.postMessage({id:n.id,data:n.graph})}},this.saveDispatch=function(n){try{t.dispatch(n)}catch(r){e.postMessage({id:n.data.id,error:r})}}}function r(e){var t=this;this.dispatcher=new n({postMessage:function(e){t.onmessage({data:e})}}),this.postMessage=function(e){setTimeout(function(){t.dispatcher.saveDispatch({data:e})},0)}}if(yC(),typeof document===e$E&&typeof self!==e$E){var i=new n(self);self.onmessage=i.saveDispatch}else"object"!==e$E&&e.exports&&(Object.defineProperty(t,"__esModule",{value:!0}),e.exports={default:r,Worker:r})}function eY3(e){e.N||(e.N=!0,e.b=eak(e,0),er6(e.b,0),er6(e.b,1),er6(e.b,2),e.bb=eak(e,1),er6(e.bb,0),er6(e.bb,1),e.fb=eak(e,2),er6(e.fb,3),er6(e.fb,4),er9(e.fb,5),e.qb=eak(e,3),er6(e.qb,0),er9(e.qb,1),er9(e.qb,2),er6(e.qb,3),er6(e.qb,4),er9(e.qb,5),er6(e.qb,6),e.a=eax(e,4),e.c=eax(e,5),e.d=eax(e,6),e.e=eax(e,7),e.f=eax(e,8),e.g=eax(e,9),e.i=eax(e,10),e.j=eax(e,11),e.k=eax(e,12),e.n=eax(e,13),e.o=eax(e,14),e.p=eax(e,15),e.q=eax(e,16),e.s=eax(e,17),e.r=eax(e,18),e.t=eax(e,19),e.u=eax(e,20),e.v=eax(e,21),e.w=eax(e,22),e.B=eax(e,23),e.A=eax(e,24),e.C=eax(e,25),e.D=eax(e,26),e.F=eax(e,27),e.G=eax(e,28),e.H=eax(e,29),e.J=eax(e,30),e.I=eax(e,31),e.K=eax(e,32),e.M=eax(e,33),e.L=eax(e,34),e.P=eax(e,35),e.Q=eax(e,36),e.R=eax(e,37),e.S=eax(e,38),e.T=eax(e,39),e.U=eax(e,40),e.V=eax(e,41),e.X=eax(e,42),e.W=eax(e,43),e.Y=eax(e,44),e.Z=eax(e,45),e.$=eax(e,46),e._=eax(e,47),e.ab=eax(e,48),e.cb=eax(e,49),e.db=eax(e,50),e.eb=eax(e,51),e.gb=eax(e,52),e.hb=eax(e,53),e.ib=eax(e,54),e.jb=eax(e,55),e.kb=eax(e,56),e.lb=eax(e,57),e.mb=eax(e,58),e.nb=eax(e,59),e.ob=eax(e,60),e.pb=eax(e,61))}function eY4(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w;if(v=0,0==t.f.a)for(m=new fz(e);m.ac&&0==(GK(c,t.c.length),Pp(t.c[c],200)).a.c.length;)QA(t,(GK(c,t.c.length),t.c[c]));if(!u){--a;continue}if(eDk(t,l,i,u,d,n,c,r)){f=!0;continue}if(d){if(ePx(t,l,i,u,n,c,r)){f=!0;continue}if(eu4(l,i)){i.c=!0,f=!0;continue}}else if(eu4(l,i)){i.c=!0,f=!0;continue}if(f)continue}if(eu4(l,i)){i.c=!0,f=!0,u&&(u.k=!1);continue}emG(i.q)}return f}function eY9(e,t,n,r,i,a,o){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A,L;for(b=0,T=0,c=new fz(e.b);c.ab&&(a&&(xL(E,h),xL(k,ell(l.b-1)),P_(e.d,p),s.c=Je(e1R,eUp,1,0,5,1)),A=n.b,L+=h+t,h=0,f=eB4.Math.max(f,n.b+n.c+O)),s.c[s.c.length]=u,epW(u,A,L),f=eB4.Math.max(f,A+O+n.c),h=eB4.Math.max(h,d),A+=O+t,p=u;if(eoc(e.a,s),P_(e.d,Pp(RJ(s,s.c.length-1),157)),f=eB4.Math.max(f,r),(M=L+h+n.a)1&&(o=eB4.Math.min(o,eB4.Math.abs(Pp(ep3(s.a,1),8).b-l.b)))));else for(b=new fz(t.j);b.ai&&(a=d.a-i,o=eUu,r.c=Je(e1R,eUp,1,0,5,1),i=d.a),d.a>=i&&(r.c[r.c.length]=s,s.a.b>1&&(o=eB4.Math.min(o,eB4.Math.abs(Pp(ep3(s.a,s.a.b-2),8).b-d.b)))));if(0!=r.c.length&&a>t.o.a/2&&o>t.o.b/2){for(h=new eES,Gc(h,t),ekv(h,(eYu(),tbw)),h.n.a=t.o.a/2,g=new eES,Gc(g,t),ekv(g,tbj),g.n.a=t.o.a/2,g.n.b=t.o.b,u=new fz(r);u.a=c.b?Gs(s,g):Gs(s,h)):(c=Pp(P$(s.a),8),(m=0==s.a.b?GX(s.c):Pp(AZ(s.a),8)).b>=c.b?Go(s,g):Go(s,h)),(f=Pp(e_k(s,(eBy(),taR)),74))&&eds(f,c,!0);t.n.a=i-t.o.a/2}}function eBe(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A,L,C,I;if(T=null,O=t,M=V0(e,VF(n),O),ert(M,KJ(O,eXS)),A=Pp(etJ(e.g,ekZ(zR(O,eXi))),33),d=zR(O,"sourcePort"),r=null,d&&(r=ekZ(d)),L=Pp(etJ(e.j,r),118),!A)throw b=(p="An edge must have a source node (edge id: '"+(s=ehM(O)))+eXO,p7(new gK(b));if(L&&!BG(zY(L),A))throw g=(m="The source port of an edge must be a port of the edge's source node (edge id: '"+(u=KJ(O,eXS)))+eXO,p7(new gK(g));if(k=(M.b||(M.b=new Ih(e6m,M,4,7)),M.b),a=null,JL(k,a=L||A),C=Pp(etJ(e.g,ekZ(zR(O,eXC))),33),h=zR(O,"targetPort"),i=null,h&&(i=ekZ(h)),I=Pp(etJ(e.j,i),118),!C)throw y=(v="An edge must have a target node (edge id: '"+(f=ehM(O)))+eXO,p7(new gK(y));if(I&&!BG(zY(I),C))throw _=(w="The target port of an edge must be a port of the edge's target node (edge id: '"+(c=KJ(O,eXS)))+eXO,p7(new gK(_));if(x=(M.c||(M.c=new Ih(e6m,M,5,8)),M.c),o=null,JL(x,o=I||C),0==(M.b||(M.b=new Ih(e6m,M,4,7)),M.b).i||0==(M.c||(M.c=new Ih(e6m,M,5,8)),M.c).i)throw S=(E=eXM+(l=KJ(O,eXS)))+eXO,p7(new gK(S));return ewU(O,M),eMu(O,M),T=esv(e,O,M)}function eBt(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T;return f=eNf(A_(e,(eYu(),tbx)),t),p=em9(A_(e,tbT),t),w=em9(A_(e,tbN),t),k=em8(A_(e,tbR),t),d=em8(A_(e,tb_),t),v=em9(A_(e,tbD),t),b=em9(A_(e,tbM),t),E=em9(A_(e,tbP),t),_=em9(A_(e,tbE),t),x=em8(A_(e,tbk),t),g=em9(A_(e,tbC),t),y=em9(A_(e,tbL),t),S=em9(A_(e,tbS),t),T=em8(A_(e,tbI),t),h=em8(A_(e,tbO),t),m=em9(A_(e,tbA),t),n=esm(eow(vx(tyx,1),eH5,25,15,[v.a,k.a,E.a,T.a])),r=esm(eow(vx(tyx,1),eH5,25,15,[p.a,f.a,w.a,m.a])),i=g.a,a=esm(eow(vx(tyx,1),eH5,25,15,[b.a,d.a,_.a,h.a])),c=esm(eow(vx(tyx,1),eH5,25,15,[v.b,p.b,b.b,y.b])),u=esm(eow(vx(tyx,1),eH5,25,15,[k.b,f.b,d.b,m.b])),l=x.b,s=esm(eow(vx(tyx,1),eH5,25,15,[E.b,w.b,_.b,S.b])),JD(A_(e,tbx),n+i,c+l),JD(A_(e,tbA),n+i,c+l),JD(A_(e,tbT),n+i,0),JD(A_(e,tbN),n+i,c+l+u),JD(A_(e,tbR),0,c+l),JD(A_(e,tb_),n+i+r,c+l),JD(A_(e,tbM),n+i+r,0),JD(A_(e,tbP),0,c+l+u),JD(A_(e,tbE),n+i+r,c+l+u),JD(A_(e,tbk),0,c),JD(A_(e,tbC),n,0),JD(A_(e,tbS),0,c+l+u),JD(A_(e,tbO),n+i+r,0),(o=new yb).a=esm(eow(vx(tyx,1),eH5,25,15,[n+r+i+a,x.a,y.a,S.a])),o.b=esm(eow(vx(tyx,1),eH5,25,15,[c+u+l+s,g.b,T.b,h.b])),o}function eBn(e){var t,n,r,i,a,o,s,u,l,f,d,h,p,b,m,g;for(m=new p0,h=new fz(e.d.b);h.ai.d.d+i.d.a?f.f.d=!0:(f.f.d=!0,f.f.a=!0))),r.b!=r.d.c&&(t=n);f&&(a=Pp(Bp(e.f,o.d.i),57),t.ba.d.d+a.d.a?f.f.d=!0:(f.f.d=!0,f.f.a=!0))}for(s=new Fa(OH(efu(p).a.Kc(),new c));eTk(s);)0!=(o=Pp(ZC(s),17)).a.b&&(t=Pp(AZ(o.a),8),o.d.j==(eYu(),tbw)&&((g=new ePe(t,new kl(t.a,i.d.d),i,o)).f.a=!0,g.a=o.d,m.c[m.c.length]=g),o.d.j==tbj&&((g=new ePe(t,new kl(t.a,i.d.d+i.d.a),i,o)).f.d=!0,g.a=o.d,m.c[m.c.length]=g))}return m}function eBr(e,t,n){var r,i,a,o,s,u,c,l,f;if(ewG(n,"Network simplex node placement",1),e.e=t,e.n=Pp(e_k(t,(eBU(),tnx)),304),eRx(e),ey8(e),_r(eeh(new R1(null,new Gq(e.e.b,16)),new i2),new hR(e)),_r(UJ(eeh(UJ(eeh(new R1(null,new Gq(e.e.b,16)),new aa),new ao),new as),new au),new hP(e)),gN(LK(e_k(e.e,(eBy(),taQ))))&&(o=eiI(n,1),ewG(o,"Straight Edges Pre-Processing",1),eFy(e),eEj(o)),ebR(e.f),a=Pp(e_k(t,to$),19).a*e.f.a.c.length,eIX(vC(vI(DN(e.f),a),!1),eiI(n,1)),0!=e.d.a.gc()){for(o=eiI(n,1),ewG(o,"Flexible Where Space Processing",1),s=Pp(Af(FM(UQ(new R1(null,new Gq(e.f.a,16)),new i3),new iZ)),19).a,c=(u=Pp(Af(FT(UQ(new R1(null,new Gq(e.f.a,16)),new i4),new iX)),19).a)-s,l=Al(new b1,e.f),f=Al(new b1,e.f),eAx(_f(_l(_c(_d(new bQ,2e4),c),l),f)),_r(UJ(UJ(Yw(e.i),new i5),new i6),new Hn(s,l,c,f)),i=e.d.a.ec().Kc();i.Ob();)(r=Pp(i.Pb(),213)).g=1;eIX(vC(vI(DN(e.f),a),!1),eiI(o,1)),eEj(o)}gN(LK(e_k(t,taQ)))&&(o=eiI(n,1),ewG(o,"Straight Edges Post-Processing",1),eSf(e),eEj(o)),ej3(e),e.e=null,e.f=null,e.i=null,e.c=null,Yy(e.k),e.j=null,e.a=null,e.o=null,e.d.a.$b(),eEj(n)}function eBi(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_;for(s=new fz(e.a.b);s.a0){if(r=f.gc(),c=zy(eB4.Math.floor((r+1)/2))-1,i=zy(eB4.Math.ceil((r+1)/2))-1,t.o==tuS)for(l=i;l>=c;l--)t.a[w.p]==w&&(b=Pp(f.Xb(l),46),p=Pp(b.a,10),!w0(n,b.b)&&h>e.b.e[p.p]&&(t.a[p.p]=w,t.g[w.p]=t.g[p.p],t.a[w.p]=t.g[w.p],t.f[t.g[w.p].p]=(OQ(),!!(gN(t.f[t.g[w.p].p])&w.k==(eEn(),e8D))),h=e.b.e[p.p]));else for(l=c;l<=i;l++)t.a[w.p]==w&&(g=Pp(f.Xb(l),46),m=Pp(g.a,10),!w0(n,g.b)&&h=p&&(v>p&&(h.c=Je(e1R,eUp,1,0,5,1),p=v),h.c[h.c.length]=o);0!=h.c.length&&(d=Pp(RJ(h,ebO(t,h.c.length)),128),M.a.Bc(d),d.s=b++,eM4(d,x,E),h.c=Je(e1R,eUp,1,0,5,1))}for(w=e.c.length+1,s=new fz(e);s.aT.s&&(BH(n),QA(T.i,r),r.c>0&&(r.a=T,P_(T.t,r),r.b=S,P_(S.i,r)))}function eBs(e){var t,n,r,i,a;switch(t=e.c){case 11:return e.Ml();case 12:return e.Ol();case 14:return e.Ql();case 15:return e.Tl();case 16:return e.Rl();case 17:return e.Ul();case 21:return eBM(e),eBG(),eBG(),tye;case 10:switch(e.a){case 65:return e.yl();case 90:return e.Dl();case 122:return e.Kl();case 98:return e.El();case 66:return e.zl();case 60:return e.Jl();case 62:return e.Hl()}}switch(a=eY8(e),t=e.c){case 3:return e.Zl(a);case 4:return e.Xl(a);case 5:return e.Yl(a);case 0:if(123==e.a&&e.d=48&&t<=57){for(r=t-48;i=48&&t<=57;)if((r=10*r+t-48)<0)throw p7(new gX(eBJ((Mo(),eJ_))))}else throw p7(new gX(eBJ((Mo(),eJg))));if(n=r,44==t){if(i>=e.j)throw p7(new gX(eBJ((Mo(),eJy))));if((t=UI(e.i,i++))>=48&&t<=57){for(n=t-48;i=48&&t<=57;)if((n=10*n+t-48)<0)throw p7(new gX(eBJ((Mo(),eJ_))));if(r>n)throw p7(new gX(eBJ((Mo(),eJw))))}else n=-1}if(125!=t)throw p7(new gX(eBJ((Mo(),eJv))));e.sl(i)?(a=(eBG(),eBG(),++tyv,new qa(9,a)),e.d=i+1):(a=(eBG(),eBG(),++tyv,new qa(3,a)),e.d=i),a.dm(r),a.cm(n),eBM(e)}}return a}function eBu(e,t,n,r,i){var a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M;for(b=new XM(t.b),w=new XM(t.b),d=new XM(t.b),k=new XM(t.b),m=new XM(t.b),S=epL(t,0);S.b!=S.d.c;)for(_=Pp(Vv(S),11),s=new fz(_.g);s.a0,g=_.g.c.length>0,c&&g?d.c[d.c.length]=_:c?b.c[b.c.length]=_:g&&(w.c[w.c.length]=_);for(p=new fz(b);p.aefT(Jh(y.d,x),Jh(y.d,y.a))&&(a.c[a.c.length]=y);for(n.c=Je(e1R,eUp,1,0,5,1),w=new fz(a);w.a1)for(p=new AF((e.a||(e.a=new FQ(e6v,e,6,6)),e.a));p.e!=p.i.gc();)ey_(p);for(o=Pp(etj((e.a||(e.a=new FQ(e6v,e,6,6)),e.a),0),202),m=A,A>_+w?m=_+w:A<_-w&&(m=_-w),g=L,L>E+b?g=E+b:L_-w&&m<_+w&&g>E-b&&gA+O?k=A+O:_L+S?x=L+S:EA-O&&kL-S&&xn&&(d=n-1),(h=P+eMU(t,24)*e$h*f-f/2)<0?h=1:h>r&&(h=r-1),i=(yT(),u=new oJ),ent(i,d),enn(i,h),JL((o.a||(o.a=new O_(e6h,o,5)),o.a),i)}function eBy(){eBy=A,tox=(eBB(),th7),toT=tpe,toM=tpt,toO=tpn,toL=tpr,toC=tpi,toN=tpo,toR=tpu,toj=tpc,toP=tps,toF=tpl,toB=tpf,toH=tpp,toD=tpa,tok=(eBH(),tih),toA=tip,toI=tib,toY=tim,tov=new T2(th4,ell(0)),toy=til,tow=tif,to_=tid,toQ=tiB,toG=tiy,toW=tiE,toq=tiL,toK=tix,toV=tiM,to0=tiG,to1=tiH,toX=tiR,toZ=tiN,toJ=tiF,ta0=tit,ta2=tin,taE=trE,taS=trx,toe=new T3(12),ta7=new T2(thN,toe),tav=(efE(),tpx),tag=new T2(tha,tav),toc=new T2(thK,0),toE=new T2(th5,ell(1)),tiX=new T2(td2,eGt),ta8=thI,tol=thV,tob=th0,tac=td7,tiq=td1,taM=thl,toS=new T2(th8,(OQ(),!0)),taI=thh,taD=thp,ta4=thx,ta9=thL,ta5=thM,tad=(ec3(),tpv),tal=new T2(the,tad),taZ=thS,taq=th_,toh=thJ,tod=thX,top=th1,tor=(epT(),tbr),new T2(thB,tor),toa=th$,too=thz,tos=thG,toi=thH,toz=tiv,taG=trZ,taz=trV,to$=tig,taY=trB,tau=trs,tas=tra,ti7=tn1,tae=tn0,tan=tn6,tat=tn2,tao=trr,taK=trJ,taV=trQ,taP=trD,ta3=tio,taJ=tr3,tax=trO,ta1=tr7,taw=trg,ta_=trw,ti8=td9,taX=tr1,ti0=tn$,ti1=tnU,tiQ=tnB,taA=trC,taO=trL,taL=trI,ta6=thO,taR=thg,tak=ths,tab=thr,tap=thn,tar=tn7,tof=thZ,tiJ=td6,taC=thd,tou=thW,tot=thR,ton=thF,taU=tr$,taH=trG,tog=th3,tiZ=tnY,ta$=trK,tam=trh,tah=trf,taW=thy,taj=trj,taQ=tr6,toU=tpd,taf=trc,tom=tiu,tay=trb,taF=trY,tai=trt,taN=thm,taB=trH,taa=trn,ti9=tnJ,ti5=tnq,ti3=tnK,ti4=tnV,ti6=tnX,ti2=tnG,taT=trA}function eBw(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;if(ePN(),k=e.e,p=e.d,i=e.a,0==k)switch(t){case 0:return"0";case 1:return e$e;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return E=new vc,t<0?(E.a+="0E+",E):(E.a+="0E",E),E.a+=-t,E.a}if(w=Je(tyw,eHl,25,(y=10*p+1+7)+1,15,1),n=y,1==p){if((s=i[0])<0){A=WM(s,eH8);do b=A,A=eyt(A,10),w[--n]=48+jE(efe(b,efn(A,10)))&eHd;while(0!=ecd(A,0))}else{A=s;do b=A,A=A/10|0,w[--n]=48+(b-10*A)&eHd;while(0!=A)}}else{T=Je(ty_,eHT,25,p,15,1),ePD(i,0,T,0,O=p);I:for(;;){for(S=0,c=O-1;c>=0;c--)g=ewT(M=eft(Fg(S,32),WM(T[c],eH8))),T[c]=jE(g),S=jE(Fv(g,32));v=jE(S),m=n;do w[--n]=48+v%10&eHd;while(0!=(v=v/10|0)&&0!=n)for(u=0,r=9-m+n;u0;u++)w[--n]=48;for(f=O-1;0==T[f];f--)if(0==f)break I;O=f+1}for(;48==w[n];)++n}if(h=k<0,o=y-n-t-1,0==t)return h&&(w[--n]=45),ehv(w,n,y-n);if(t>0&&o>=-6){if(o>=0){for(l=n+o,d=y-1;d>=l;d--)w[d+1]=w[d];return w[++l]=46,h&&(w[--n]=45),ehv(w,n,y-n+1)}for(f=2;f<-o+1;f++)w[--n]=48;return w[--n]=46,w[--n]=48,h&&(w[--n]=45),ehv(w,n,y-n)}return x=n+1,a=y,_=new vl,h&&(_.a+="-"),a-x>=1?(Bd(_,w[n]),_.a+=".",_.a+=ehv(w,n+1,y-n-1)):_.a+=ehv(w,n,y-n),_.a+="E",o>0&&(_.a+="+"),_.a+=""+o,_.a}function eB_(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E;switch(e.c=t,e.g=new p2,n=(_q(),new gM(e.c)),efJ(r=new dp(n)),y=Lq(eT8(e.c,(e_L(),tfD))),u=Pp(eT8(e.c,tfP),316),_=Pp(eT8(e.c,tfR),429),o=Pp(eT8(e.c,tfO),482),w=Pp(eT8(e.c,tfN),430),e.j=gP(LV(eT8(e.c,tfj))),s=e.a,u.g){case 0:s=e.a;break;case 1:s=e.b;break;case 2:s=e.i;break;case 3:s=e.e;break;case 4:s=e.f;break;default:throw p7(new gL(eqN+(null!=u.f?u.f:""+u.g)))}if(e.d=new zM(s,_,o),eo3(e.d,(ei6(),e6F),LK(eT8(e.c,tfL))),e.d.c=gN(LK(eT8(e.c,tfA))),0==H8(e.c).i)return e.d;for(f=new Ow(H8(e.c));f.e!=f.i.gc();){for(h=(l=Pp(epH(f),33)).g/2,d=l.f/2,E=new kl(l.i+h,l.j+d);F9(e.g,E);)Lu(E,(eB4.Math.random()-.5)*ez8,(eB4.Math.random()-.5)*ez8);b=Pp(eT8(l,(eBB(),thy)),142),m=new Gd(E,new Hr(E.a-h-e.j/2-b.b,E.b-d-e.j/2-b.d,l.g+e.j+(b.b+b.c),l.f+e.j+(b.d+b.a))),P_(e.d.i,m),Um(e.g,E,new kD(m,l))}switch(w.g){case 0:if(null==y)e.d.d=Pp(RJ(e.d.i,0),65);else for(v=new fz(e.d.i);v.a1&&qQ(l,g,l.c.b,l.c),etu(i)));g=v}return l}function eBS(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A,L,C,I,D;for(ewG(n,"Greedy cycle removal",1),D=(y=t.a).c.length,e.a=Je(ty_,eHT,25,D,15,1),e.c=Je(ty_,eHT,25,D,15,1),e.b=Je(ty_,eHT,25,D,15,1),c=0,g=new fz(y);g.a0?O+1:1);for(o=new fz(E.g);o.a0?O+1:1)}0==e.c[c]?P7(e.e,b):0==e.a[c]&&P7(e.f,b),++c}for(p=-1,h=1,f=new p0,e.d=Pp(e_k(t,(eBU(),tnw)),230);D>0;){for(;0!=e.e.b;)L=Pp(PH(e.e),10),e.b[L.p]=p--,eIQ(e,L),--D;for(;0!=e.f.b;)C=Pp(PH(e.f),10),e.b[C.p]=h++,eIQ(e,C),--D;if(D>0){for(d=eHt,v=new fz(y);v.a=d&&(w>d&&(f.c=Je(e1R,eUp,1,0,5,1),d=w),f.c[f.c.length]=b);l=e.Zf(f),e.b[l.p]=h++,eIQ(e,l),--D}}for(c=0,A=y.c.length+1;ce.b[I]&&(eNF(r,!0),eo3(t,ttK,(OQ(),!0)));e.a=null,e.c=null,e.b=null,HC(e.f),HC(e.e),eEj(n)}function eBk(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g;for(r=new p0,s=new p0,m=t/2,h=e.gc(),i=Pp(e.Xb(0),8),g=Pp(e.Xb(1),8),p=eT5(i.a,i.b,g.a,g.b,m),P_(r,(GK(0,p.c.length),Pp(p.c[0],8))),P_(s,(GK(1,p.c.length),Pp(p.c[1],8))),c=2;c=0;u--)P7(n,(GK(u,o.c.length),Pp(o.c[u],8)));return n}function eBx(e){var t,n,r,i,a,o,s,u,c,l,f,d,h;if(o=!0,f=null,r=null,i=null,t=!1,h=tmH,c=null,a=null,(u=epm(e,s=0,tmJ,tmQ))=0&&IE(e.substr(s,2),"//")?(s+=2,u=epm(e,s,tm1,tm0),r=e.substr(s,u-s),s=u):null!=f&&(s==e.length||(GV(s,e.length),47!=e.charCodeAt(s)))&&(o=!1,-1==(u=O7(e,e_n(35),s))&&(u=e.length),r=e.substr(s,u-s),s=u);if(!n&&s0&&58==UI(l,l.length-1)&&(i=l,s=u)),s=e.j){e.a=-1,e.c=1;return}if(t=UI(e.i,e.d++),e.a=t,1==e.b){switch(t){case 92:if(r=10,e.d>=e.j)throw p7(new gX(eBJ((Mo(),eXZ))));e.a=UI(e.i,e.d++);break;case 45:(512&e.e)==512&&e.d=e.j||63!=UI(e.i,e.d))break;if(++e.d>=e.j)throw p7(new gX(eBJ((Mo(),eXX))));switch(t=UI(e.i,e.d++)){case 58:r=13;break;case 61:r=14;break;case 33:r=15;break;case 91:r=19;break;case 62:r=18;break;case 60:if(e.d>=e.j)throw p7(new gX(eBJ((Mo(),eXX))));if(61==(t=UI(e.i,e.d++)))r=16;else if(33==t)r=17;else throw p7(new gX(eBJ((Mo(),eXJ))));break;case 35:for(;e.d=e.j)throw p7(new gX(eBJ((Mo(),eXZ))));e.a=UI(e.i,e.d++);break;default:r=0}e.c=r}function eBO(e){var t,n,r,i,a,o,s,u,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;if((k=Pp(e_k(e,(eBy(),tol)),98))!=(ewf(),tbc)&&k!=tbl){for(p=(b=e.b).c.length,f=new XM((enG(p+2,eU6),ee1(eft(eft(5,p+2),(p+2)/10|0)))),m=new XM((enG(p+2,eU6),ee1(eft(eft(5,p+2),(p+2)/10|0)))),P_(f,new p2),P_(f,new p2),P_(m,new p0),P_(m,new p0),S=new p0,t=0;t=E||!ehf(v,r))&&(r=GY(t,f)),Gu(v,r),a=new Fa(OH(efu(v).a.Kc(),new c));eTk(a);)i=Pp(ZC(a),17),!e.a[i.p]&&(m=i.c.i,--e.e[m.p],0==e.e[m.p]&&Ja(e_s(p,m)));for(l=f.c.length-1;l>=0;--l)P_(t.b,(GK(l,f.c.length),Pp(f.c[l],29)));t.a.c=Je(e1R,eUp,1,0,5,1),eEj(n)}function eBL(e){var t,n,r,i,a,o,s,u,c;for(e.b=1,eBM(e),t=null,0==e.c&&94==e.a?(eBM(e),t=(eBG(),eBG(),++tyv,new WZ(4)),eLw(t,0,e1f),s=(++tyv,new WZ(4))):s=(eBG(),eBG(),++tyv,new WZ(4)),i=!0;1!=(c=e.c);){if(0==c&&93==e.a&&!i){t&&(ej0(t,s),s=t);break}if(n=e.a,r=!1,10==c)switch(n){case 100:case 68:case 119:case 87:case 115:case 83:ePR(s,eDu(n)),r=!0;break;case 105:case 73:case 99:case 67:(n=(ePR(s,eDu(n)),-1))<0&&(r=!0);break;case 112:case 80:if(!(u=ext(e,n)))throw p7(new gX(eBJ((Mo(),eJe))));ePR(s,u),r=!0;break;default:n=eCn(e)}else if(24==c&&!i){if(t&&(ej0(t,s),s=t),a=eBL(e),ej0(s,a),0!=e.c||93!=e.a)throw p7(new gX(eBJ((Mo(),eJi))));break}if(eBM(e),!r){if(0==c){if(91==n)throw p7(new gX(eBJ((Mo(),eJa))));if(93==n)throw p7(new gX(eBJ((Mo(),eJo))));if(45==n&&!i&&93!=e.a)throw p7(new gX(eBJ((Mo(),eJs))))}if(0!=e.c||45!=e.a||45==n&&i)eLw(s,n,n);else{if(eBM(e),1==(c=e.c))throw p7(new gX(eBJ((Mo(),eJn))));if(0==c&&93==e.a)eLw(s,n,n),eLw(s,45,45);else if(0==c&&93==e.a||24==c)throw p7(new gX(eBJ((Mo(),eJs))));else{if(o=e.a,0==c){if(91==o)throw p7(new gX(eBJ((Mo(),eJa))));if(93==o)throw p7(new gX(eBJ((Mo(),eJo))));if(45==o)throw p7(new gX(eBJ((Mo(),eJs))))}else 10==c&&(o=eCn(e));if(eBM(e),n>o)throw p7(new gX(eBJ((Mo(),eJl))));eLw(s,n,o)}}}i=!1}if(1==e.c)throw p7(new gX(eBJ((Mo(),eJn))));return eMS(s),eRo(s),e.b=0,eBM(e),s}function eBC(e){eMV(e.c,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#decimal"])),eMV(e.d,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#integer"])),eMV(e.e,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#boolean"])),eMV(e.f,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EBoolean",eXP,"EBoolean:Object"])),eMV(e.i,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#byte"])),eMV(e.g,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#hexBinary"])),eMV(e.j,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EByte",eXP,"EByte:Object"])),eMV(e.n,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EChar",eXP,"EChar:Object"])),eMV(e.t,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#double"])),eMV(e.u,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EDouble",eXP,"EDouble:Object"])),eMV(e.F,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#float"])),eMV(e.G,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EFloat",eXP,"EFloat:Object"])),eMV(e.I,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#int"])),eMV(e.J,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EInt",eXP,"EInt:Object"])),eMV(e.N,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#long"])),eMV(e.O,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"ELong",eXP,"ELong:Object"])),eMV(e.Z,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#short"])),eMV(e.$,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"EShort",eXP,"EShort:Object"])),eMV(e._,eJ7,eow(vx(e17,1),eUP,2,6,[eQd,"http://www.w3.org/2001/XMLSchema#string"]))}function eBI(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O;if(1==e.c.length)return GK(0,e.c.length),Pp(e.c[0],135);if(e.c.length<=0)return new Xn;for(u=new fz(e);u.af&&(M=0,O+=l+S,l=0),eOd(_,o,M,O),t=eB4.Math.max(t,M+E.a),l=eB4.Math.max(l,E.b),M+=E.a+S;for(w=new p2,n=new p2,x=new fz(e);x.aeMg(a))&&(f=a);for(f||(f=(GK(0,m.c.length),Pp(m.c[0],180))),b=new fz(t.b);b.a=-1900?1:0,n>=4?xM(e,eow(vx(e17,1),eUP,2,6,[eHM,eHO])[s]):xM(e,eow(vx(e17,1),eUP,2,6,["BC","AD"])[s]);break;case 121:epA(e,n,r);break;case 77:eIZ(e,n,r);break;case 107:0==(u=i.q.getHours())?eeE(e,24,n):eeE(e,u,n);break;case 83:eOT(e,n,i);break;case 69:l=r.q.getDay(),5==n?xM(e,eow(vx(e17,1),eUP,2,6,["S","M","T","W","T","F","S"])[l]):4==n?xM(e,eow(vx(e17,1),eUP,2,6,[eHA,eHL,eHC,eHI,eHD,eHN,eHP])[l]):xM(e,eow(vx(e17,1),eUP,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[l]);break;case 97:i.q.getHours()>=12&&24>i.q.getHours()?xM(e,eow(vx(e17,1),eUP,2,6,["AM","PM"])[1]):xM(e,eow(vx(e17,1),eUP,2,6,["AM","PM"])[0]);break;case 104:0==(f=i.q.getHours()%12)?eeE(e,12,n):eeE(e,f,n);break;case 75:eeE(e,d=i.q.getHours()%12,n);break;case 72:eeE(e,h=i.q.getHours(),n);break;case 99:p=r.q.getDay(),5==n?xM(e,eow(vx(e17,1),eUP,2,6,["S","M","T","W","T","F","S"])[p]):4==n?xM(e,eow(vx(e17,1),eUP,2,6,[eHA,eHL,eHC,eHI,eHD,eHN,eHP])[p]):3==n?xM(e,eow(vx(e17,1),eUP,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[p]):eeE(e,p,1);break;case 76:b=r.q.getMonth(),5==n?xM(e,eow(vx(e17,1),eUP,2,6,["J","F","M","A","M","J","J","A","S","O","N","D"])[b]):4==n?xM(e,eow(vx(e17,1),eUP,2,6,[eHh,eHp,eHb,eHm,eHg,eHv,eHy,eHw,eH_,eHE,eHS,eHk])[b]):3==n?xM(e,eow(vx(e17,1),eUP,2,6,["Jan","Feb","Mar","Apr",eHg,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[b]):eeE(e,b+1,n);break;case 81:m=r.q.getMonth()/3|0,n<4?xM(e,eow(vx(e17,1),eUP,2,6,["Q1","Q2","Q3","Q4"])[m]):xM(e,eow(vx(e17,1),eUP,2,6,["1st quarter","2nd quarter","3rd quarter","4th quarter"])[m]);break;case 100:eeE(e,g=r.q.getDate(),n);break;case 109:eeE(e,c=i.q.getMinutes(),n);break;case 115:eeE(e,o=i.q.getSeconds(),n);break;case 122:n<4?xM(e,a.c[0]):xM(e,a.c[1]);break;case 118:xM(e,a.b);break;case 90:n<3?xM(e,ekA(a)):3==n?xM(e,ek$(a)):xM(e,ekz(a.a));break;default:return!1}return!0}function eBF(e,t,n,r){var i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;if(eIi(t),u=Pp(etj((t.b||(t.b=new Ih(e6m,t,4,7)),t.b),0),82),l=Pp(etj((t.c||(t.c=new Ih(e6m,t,5,8)),t.c),0),82),s=ewH(u),c=ewH(l),o=0==(t.a||(t.a=new FQ(e6v,t,6,6)),t.a).i?null:Pp(etj((t.a||(t.a=new FQ(e6v,t,6,6)),t.a),0),202),S=Pp(Bp(e.a,s),10),M=Pp(Bp(e.a,c),10),k=null,O=null,M4(u,186)&&(M4(E=Pp(Bp(e.a,u),299),11)?k=Pp(E,11):M4(E,10)&&(S=Pp(E,10),k=Pp(RJ(S.j,0),11))),M4(l,186)&&(M4(T=Pp(Bp(e.a,l),299),11)?O=Pp(T,11):M4(T,10)&&(M=Pp(T,10),O=Pp(RJ(M.j,0),11))),!S||!M)throw p7(new gZ("The source or the target of edge "+t+" could not be found. This usually happens when an edge connects a node laid out by ELK Layered to a node in another level of hierarchy laid out by either another instance of ELK Layered or another layout algorithm alltogether. The former can be solved by setting the hierarchyHandling option to INCLUDE_CHILDREN."));for(b=new $b,eaW(b,t),eo3(b,(eBU(),tnc),t),eo3(b,(eBy(),taR),null),h=Pp(e_k(r,tt3),21),S==M&&h.Fc((eLR(),ttT)),k||(_=(enY(),tsN),x=null,o&&TM(Pp(e_k(S,tol),98))&&(V2(x=new kl(o.j,o.k),zF(t)),qZ(x,n),etg(c,s)&&(_=tsD,C5(x,S.n))),k=ePH(S,x,_,r)),O||(_=(enY(),tsD),A=null,o&&TM(Pp(e_k(M,tol),98))&&(V2(A=new kl(o.b,o.c),zF(t)),qZ(A,n)),O=ePH(M,A,_,Bq(M))),Gs(b,k),Go(b,O),(k.e.c.length>1||k.g.c.length>1||O.e.c.length>1||O.g.c.length>1)&&h.Fc((eLR(),tt_)),d=new Ow((t.n||(t.n=new FQ(e6S,t,1,7)),t.n));d.e!=d.i.gc();)if(f=Pp(epH(d),137),!gN(LK(eT8(f,ta8)))&&f.a)switch(m=eca(f),P_(b.b,m),Pp(e_k(m,tab),272).g){case 1:case 2:h.Fc((eLR(),tty));break;case 0:h.Fc((eLR(),ttg)),eo3(m,tab,(etT(),tp_))}if(a=Pp(e_k(r,tas),314),g=Pp(e_k(r,ta3),315),i=a==(en7(),teR)||g==(ebG(),tsd),o&&0!=(o.a||(o.a=new O_(e6h,o,5)),o.a).i&&i){for(v=eEF(o),p=new mE,w=epL(v,0);w.b!=w.d.c;)y=Pp(Vv(w),8),P7(p,new TS(y));eo3(b,tnl,p)}return b}function eBY(e){e.gb||(e.gb=!0,e.b=eak(e,0),er6(e.b,18),er9(e.b,19),e.a=eak(e,1),er6(e.a,1),er9(e.a,2),er9(e.a,3),er9(e.a,4),er9(e.a,5),e.o=eak(e,2),er6(e.o,8),er6(e.o,9),er9(e.o,10),er9(e.o,11),er9(e.o,12),er9(e.o,13),er9(e.o,14),er9(e.o,15),er9(e.o,16),er9(e.o,17),er9(e.o,18),er9(e.o,19),er9(e.o,20),er9(e.o,21),er9(e.o,22),er9(e.o,23),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),ee9(e.o),e.p=eak(e,3),er6(e.p,2),er6(e.p,3),er6(e.p,4),er6(e.p,5),er9(e.p,6),er9(e.p,7),ee9(e.p),ee9(e.p),e.q=eak(e,4),er6(e.q,8),e.v=eak(e,5),er9(e.v,9),ee9(e.v),ee9(e.v),ee9(e.v),e.w=eak(e,6),er6(e.w,2),er6(e.w,3),er6(e.w,4),er9(e.w,5),e.B=eak(e,7),er9(e.B,1),ee9(e.B),ee9(e.B),ee9(e.B),e.Q=eak(e,8),er9(e.Q,0),ee9(e.Q),e.R=eak(e,9),er6(e.R,1),e.S=eak(e,10),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),ee9(e.S),e.T=eak(e,11),er9(e.T,10),er9(e.T,11),er9(e.T,12),er9(e.T,13),er9(e.T,14),ee9(e.T),ee9(e.T),e.U=eak(e,12),er6(e.U,2),er6(e.U,3),er9(e.U,4),er9(e.U,5),er9(e.U,6),er9(e.U,7),ee9(e.U),e.V=eak(e,13),er9(e.V,10),e.W=eak(e,14),er6(e.W,18),er6(e.W,19),er6(e.W,20),er9(e.W,21),er9(e.W,22),er9(e.W,23),e.bb=eak(e,15),er6(e.bb,10),er6(e.bb,11),er6(e.bb,12),er6(e.bb,13),er6(e.bb,14),er6(e.bb,15),er6(e.bb,16),er9(e.bb,17),ee9(e.bb),ee9(e.bb),e.eb=eak(e,16),er6(e.eb,2),er6(e.eb,3),er6(e.eb,4),er6(e.eb,5),er6(e.eb,6),er6(e.eb,7),er9(e.eb,8),er9(e.eb,9),e.ab=eak(e,17),er6(e.ab,0),er6(e.ab,1),e.H=eak(e,18),er9(e.H,0),er9(e.H,1),er9(e.H,2),er9(e.H,3),er9(e.H,4),er9(e.H,5),ee9(e.H),e.db=eak(e,19),er9(e.db,2),e.c=eax(e,20),e.d=eax(e,21),e.e=eax(e,22),e.f=eax(e,23),e.i=eax(e,24),e.g=eax(e,25),e.j=eax(e,26),e.k=eax(e,27),e.n=eax(e,28),e.r=eax(e,29),e.s=eax(e,30),e.t=eax(e,31),e.u=eax(e,32),e.fb=eax(e,33),e.A=eax(e,34),e.C=eax(e,35),e.D=eax(e,36),e.F=eax(e,37),e.G=eax(e,38),e.I=eax(e,39),e.J=eax(e,40),e.L=eax(e,41),e.M=eax(e,42),e.N=eax(e,43),e.O=eax(e,44),e.P=eax(e,45),e.X=eax(e,46),e.Y=eax(e,47),e.Z=eax(e,48),e.$=eax(e,49),e._=eax(e,50),e.cb=eax(e,51),e.K=eax(e,52))}function eBB(){var e,t;eBB=A,tdQ=new pO(eZi),th6=new pO(eZa),td0=(ebx(),tdM),td1=new xX(eVi,td0),new pQ,td2=new xX(ezG,null),td3=new pO(eZo),td8=(eyY(),jL(tdX,eow(vx(e54,1),eU4,291,0,[tdK]))),td9=new xX(eVg,td8),td7=new xX(eVr,(OQ(),!1)),tht=(ec3(),tpv),the=new xX(eVu,tht),tho=(efE(),tpO),tha=new xX(eKB,tho),thc=new xX(eqC,!1),thf=(eck(),tpG),thl=new xX(eKP,thf),thP=new T3(12),thN=new xX(ezW,thP),thb=new xX(eGu,!1),thm=new xX(eVA,!1),thD=new xX(eGf,!1),thq=(ewf(),tbl),thV=new xX(eGc,thq),th3=new pO(eVT),th4=new pO(eGr),th5=new pO(eGo),th8=new pO(eGs),thv=new mE,thg=new xX(eVv,thv),td6=new xX(eV_,!1),thd=new xX(eVE,!1),new pO(eZs),thw=new mh,thy=new xX(eVM,thw),thI=new xX(eVt,!1),new pQ,th9=new xX(eZu,1),new xX(eZc,!0),ell(0),new xX(eZl,ell(100)),new xX(eZf,!1),ell(0),new xX(eZd,ell(4e3)),ell(0),new xX(eZh,ell(400)),new xX(eZp,!1),new xX(eZb,!1),new xX(eZm,!0),new xX(eZg,!1),td5=(edM(),tme),td4=new xX(eZr,td5),th7=new xX(eKQ,10),tpe=new xX(eK1,10),tpt=new xX(ez$,20),tpn=new xX(eK0,10),tpr=new xX(eGa,2),tpi=new xX(eK2,10),tpo=new xX(eK3,0),tps=new xX(eK6,5),tpu=new xX(eK4,1),tpc=new xX(eK5,1),tpl=new xX(eGi,20),tpf=new xX(eK9,10),tpp=new xX(eK8,10),tpa=new pO(eK7),tph=new T_,tpd=new xX(eVO,tph),thF=new pO(eVx),thj=!1,thR=new xX(eVk,thj),thE=new T3(5),th_=new xX(eVc,thE),thk=(eT7(),t=Pp(yw(e6t),9),new I1(t,Pp(CY(t,t.length),9),0)),thS=new xX(eGp,thk),thU=(epT(),tbt),thB=new xX(eVd,thU),th$=new pO(eVh),thz=new pO(eVp),thG=new pO(eVb),thH=new pO(eVm),thT=(e=Pp(yw(e6o),9),new I1(e,Pp(CY(e,e.length),9),0)),thx=new xX(eGh,thT),thC=el9((eI3(),tbQ)),thL=new xX(eGd,thC),thA=new kl(0,0),thO=new xX(eGM,thA),thM=new xX(eVs,!1),thi=(etT(),tp_),thr=new xX(eVy,thi),thn=new xX(eGl,!1),new pO(eZv),ell(1),new xX(eZy,null),thW=new pO(eVS),thZ=new pO(eVw),th2=(eYu(),tbF),th0=new xX(eVn,th2),thK=new pO(eVe),thQ=(ekU(),el9(tbm)),thJ=new xX(eGb,thQ),thX=new xX(eVl,!1),th1=new xX(eVf,!0),thh=new xX(eVa,!1),thp=new xX(eVo,!1),ths=new xX(ezz,1),thu=(e_a(),tpN),new xX(eZw,thu),thY=!0}function eBU(){var e,t;eBU=A,tnc=new pO(eGm),ttz=new pO("coordinateOrigin"),tny=new pO("processors"),tt$=new Cm("compoundNode",(OQ(),!1)),tt6=new Cm("insideConnections",!1),tnl=new pO("originalBendpoints"),tnf=new pO("originalDummyNodePosition"),tnd=new pO("originalLabelEdge"),tn_=new pO("representedLabels"),ttq=new pO("endLabels"),ttZ=new pO("endLabel.origin"),tnt=new Cm("labelSide",(egF(),tpX)),tns=new Cm("maxEdgeThickness",0),tnE=new Cm("reversed",!1),tnw=new pO(eGg),tni=new Cm("longEdgeSource",null),tna=new Cm("longEdgeTarget",null),tnr=new Cm("longEdgeHasLabelDummies",!1),tnn=new Cm("longEdgeBeforeLabelDummy",!1),ttV=new Cm("edgeConstraint",(eoG(),te1)),tt8=new pO("inLayerLayoutUnit"),tt9=new Cm("inLayerConstraint",(Q1(),ttD)),tt7=new Cm("inLayerSuccessorConstraint",new p0),tne=new Cm("inLayerSuccessorConstraintBetweenNonDummies",!1),tng=new pO("portDummy"),ttG=new Cm("crossingHint",ell(0)),tt3=new Cm("graphProperties",(t=Pp(yw(e44),9),new I1(t,Pp(CY(t,t.length),9),0))),tt1=new Cm("externalPortSide",(eYu(),tbF)),tt0=new Cm("externalPortSize",new yb),ttJ=new pO("externalPortReplacedDummies"),ttQ=new pO("externalPortReplacedDummy"),ttX=new Cm("externalPortConnections",(e=Pp(yw(e6a),9),new I1(e,Pp(CY(e,e.length),9),0))),tnv=new Cm(ezf,0),ttY=new pO("barycenterAssociates"),tnI=new pO("TopSideComments"),ttB=new pO("BottomSideComments"),ttH=new pO("CommentConnectionPort"),tt5=new Cm("inputCollect",!1),tnb=new Cm("outputCollect",!1),ttK=new Cm("cyclic",!1),ttW=new pO("crossHierarchyMap"),tnC=new pO("targetOffset"),new Cm("splineLabelSize",new yb),tnx=new pO("spacings"),tnm=new Cm("partitionConstraint",!1),ttU=new pO("breakingPoint.info"),tnA=new pO("splines.survivingEdge"),tnO=new pO("splines.route.start"),tnT=new pO("splines.edgeChain"),tnp=new pO("originalPortConstraints"),tnk=new pO("selfLoopHolder"),tnM=new pO("splines.nsPortY"),tnu=new pO("modelOrder"),tno=new pO("longEdgeTargetNode"),tt2=new Cm(eW_,!1),tnS=new Cm(eW_,!1),tt4=new pO("layerConstraints.hiddenNodes"),tnh=new pO("layerConstraints.opposidePort"),tnL=new pO("targetNode.modelOrder")}function eBH(){eBH=A,trl=(eeF(),teZ),trc=new xX(eWE,trl),trO=new xX(eWS,(OQ(),!1)),trN=(K6(),ttR),trD=new xX(eWk,trN),trJ=new xX(eWx,!1),trQ=new xX(eWT,!0),tnY=new xX(eWM,!1),tic=(Q0(),tsL),tiu=new xX(eWO,tic),ell(1),tig=new xX(eWA,ell(7)),tiv=new xX(eWL,!1),trA=new xX(eWC,!1),tru=(eb6(),teG),trs=new xX(eWI,tru),trX=(ewY(),to7),trZ=new xX(eWD,trX),trU=(ef_(),tnj),trB=new xX(eWN,trU),ell(-1),trY=new xX(eWP,ell(-1)),ell(-1),trH=new xX(eWR,ell(-1)),ell(-1),tr$=new xX(eWj,ell(4)),ell(-1),trG=new xX(eWF,ell(2)),trq=(eOJ(),tsS),trV=new xX(eWY,trq),ell(0),trK=new xX(eWB,ell(0)),trj=new xX(eWU,ell(eUu)),tro=(en7(),tej),tra=new xX(eWH,tro),tn1=new xX(eW$,!1),tn7=new xX(eWz,.1),trr=new xX(eWG,!1),ell(-1),trt=new xX(eWW,ell(-1)),ell(-1),trn=new xX(eWK,ell(-1)),ell(0),tn0=new xX(eWV,ell(40)),tn9=(eaU(),ttL),tn6=new xX(eWq,tn9),tn3=ttO,tn2=new xX(eWZ,tn3),tis=(ebG(),tsf),tio=new xX(eWX,tis),tr6=new pO(eWJ),tr0=(Qx(),tte),tr1=new xX(eWQ,tr0),tr4=(eyd(),tto),tr3=new xX(eW1,tr4),new pQ,tr7=new xX(eW0,.3),tit=new pO(eW2),tir=(ebk(),tsu),tin=new xX(eW3,tir),trv=(ei0(),tsF),trg=new xX(eW4,trv),tr_=(Xo(),tsH),trw=new xX(eW5,tr_),trS=(euy(),tsW),trE=new xX(eW6,trS),trx=new xX(eW9,.2),trb=new xX(eW8,2),tih=new xX(eW7,null),tib=new xX(eKe,10),tip=new xX(eKt,10),tim=new xX(eKn,20),ell(0),til=new xX(eKr,ell(0)),ell(0),tif=new xX(eKi,ell(0)),ell(0),tid=new xX(eKa,ell(0)),tnB=new xX(eKo,!1),tnz=(e_3(),ttp),tn$=new xX(eKs,tnz),tnH=(Jp(),teN),tnU=new xX(eKu,tnH),trC=new xX(eKc,!1),ell(0),trL=new xX(eKl,ell(16)),ell(0),trI=new xX(eKf,ell(5)),tiU=(eox(),tsQ),tiB=new xX(eKd,tiU),tiy=new xX(eKh,10),tiE=new xX(eKp,1),tiC=(enB(),teH),tiL=new xX(eKb,tiC),tix=new pO(eKm),tiO=ell(1),ell(0),tiM=new xX(eKg,tiO),tiW=(eiO(),tsV),tiG=new xX(eKv,tiW),tiH=new pO(eKy),tiR=new xX(eKw,!0),tiN=new xX(eK_,2),tiF=new xX(eKE,!0),trp=(eEf(),te9),trh=new xX(eKS,trp),trd=(eSg(),teO),trf=new xX(eKk,trd),tnQ=(esn(),tsM),tnJ=new xX(eKx,tnQ),tnX=new xX(eKT,!1),tnW=(ec4(),e8x),tnG=new xX(eKM,tnW),tnZ=(euJ(),tsn),tnq=new xX(eKO,tnZ),tnK=new xX(eKA,0),tnV=new xX(eKL,0),trR=teK,trP=teR,trz=to8,trW=to8,trF=to5,tre=(eck(),tpz),tri=tej,tn8=tej,tn4=tej,tn5=tpz,tr9=tsp,tr8=tsf,tr2=tsf,tr5=tsf,tie=tsh,tia=tsp,tii=tsp,trk=(efE(),tpM),trT=tpM,trM=tsW,trm=tpT,tiw=ts1,ti_=tsJ,tiS=ts1,tik=tsJ,tiI=ts1,tiD=tsJ,tiT=teU,tiA=teH,tiK=ts1,tiV=tsJ,ti$=ts1,tiz=tsJ,tij=tsJ,tiP=tsJ,tiY=tsJ}function eB$(){eB$=A,e85=new Eq("DIRECTION_PREPROCESSOR",0),e82=new Eq("COMMENT_PREPROCESSOR",1),e86=new Eq("EDGE_AND_LAYER_CONSTRAINT_EDGE_REVERSER",2),e7d=new Eq("INTERACTIVE_EXTERNAL_PORT_POSITIONER",3),e7C=new Eq("PARTITION_PREPROCESSOR",4),e7m=new Eq("LABEL_DUMMY_INSERTER",5),e7j=new Eq("SELF_LOOP_PREPROCESSOR",6),e7_=new Eq("LAYER_CONSTRAINT_PREPROCESSOR",7),e7A=new Eq("PARTITION_MIDPROCESSOR",8),e7s=new Eq("HIGH_DEGREE_NODE_LAYER_PROCESSOR",9),e7x=new Eq("NODE_PROMOTION",10),e7w=new Eq("LAYER_CONSTRAINT_POSTPROCESSOR",11),e7L=new Eq("PARTITION_POSTPROCESSOR",12),e7r=new Eq("HIERARCHICAL_PORT_CONSTRAINT_PROCESSOR",13),e7Y=new Eq("SEMI_INTERACTIVE_CROSSMIN_PROCESSOR",14),e8Z=new Eq("BREAKING_POINT_INSERTER",15),e7k=new Eq("LONG_EDGE_SPLITTER",16),e7D=new Eq("PORT_SIDE_PROCESSOR",17),e7h=new Eq("INVERTED_PORT_PROCESSOR",18),e7I=new Eq("PORT_LIST_SORTER",19),e7U=new Eq("SORT_BY_INPUT_ORDER_OF_MODEL",20),e7M=new Eq("NORTH_SOUTH_PORT_PREPROCESSOR",21),e8X=new Eq("BREAKING_POINT_PROCESSOR",22),e7O=new Eq(eG7,23),e7H=new Eq(eWe,24),e7P=new Eq("SELF_LOOP_PORT_RESTORER",25),e7B=new Eq("SINGLE_EDGE_GRAPH_WRAPPER",26),e7p=new Eq("IN_LAYER_CONSTRAINT_PROCESSOR",27),e7e=new Eq("END_NODE_PORT_LABEL_MANAGEMENT_PROCESSOR",28),e7b=new Eq("LABEL_AND_NODE_SIZE_PROCESSOR",29),e7f=new Eq("INNERMOST_NODE_MARGIN_CALCULATOR",30),e7F=new Eq("SELF_LOOP_ROUTER",31),e81=new Eq("COMMENT_NODE_MARGIN_CALCULATOR",32),e88=new Eq("END_LABEL_PREPROCESSOR",33),e7v=new Eq("LABEL_DUMMY_SWITCHER",34),e8Q=new Eq("CENTER_LABEL_MANAGEMENT_PROCESSOR",35),e7y=new Eq("LABEL_SIDE_SELECTOR",36),e7c=new Eq("HYPEREDGE_DUMMY_MERGER",37),e7i=new Eq("HIERARCHICAL_PORT_DUMMY_SIZE_PROCESSOR",38),e7E=new Eq("LAYER_SIZE_AND_GRAPH_HEIGHT_CALCULATOR",39),e7o=new Eq("HIERARCHICAL_PORT_POSITION_PROCESSOR",40),e83=new Eq("CONSTRAINTS_POSTPROCESSOR",41),e80=new Eq("COMMENT_POSTPROCESSOR",42),e7l=new Eq("HYPERNODE_PROCESSOR",43),e7a=new Eq("HIERARCHICAL_PORT_ORTHOGONAL_EDGE_ROUTER",44),e7S=new Eq("LONG_EDGE_JOINER",45),e7R=new Eq("SELF_LOOP_POSTPROCESSOR",46),e8J=new Eq("BREAKING_POINT_REMOVER",47),e7T=new Eq("NORTH_SOUTH_PORT_POSTPROCESSOR",48),e7u=new Eq("HORIZONTAL_COMPACTOR",49),e7g=new Eq("LABEL_DUMMY_REMOVER",50),e7t=new Eq("FINAL_SPLINE_BENDPOINTS_CALCULATOR",51),e87=new Eq("END_LABEL_SORTER",52),e7N=new Eq("REVERSED_EDGE_RESTORER",53),e89=new Eq("END_LABEL_POSTPROCESSOR",54),e7n=new Eq("HIERARCHICAL_NODE_RESIZER",55),e84=new Eq("DIRECTION_POSTPROCESSOR",56)}function eBz(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A,L,C,I,D,N,P,R,j,F,Y,B,U,H,$,z,G,W,K,V,q,Z,X,J,Q,ee,et,en,er,ei,ea,eo;for(I=0,X=0,P=(A=t).length;I0&&(e.a[H.p]=X++)}for(D=0,en=0,R=(L=n).length;D0;){for(H=(A6(W.b>0),Pp(W.a.Xb(W.c=--W.b),11)),G=0,s=new fz(H.e);s.a0&&(H.j==(eYu(),tbw)?(e.a[H.p]=en,++en):(e.a[H.p]=en+j+Y,++Y))}en+=Y}for(C=0,z=new p2,p=new Tw,N=(O=t).length;Cc.b&&(c.b=K)):H.i.c==Z&&(Kc.c&&(c.c=K));for(Qe(b,0,b.length,null),et=Je(ty_,eHT,25,b.length,15,1),r=Je(ty_,eHT,25,en+1,15,1),g=0;g0;)S%2>0&&(i+=ea[S+1]),S=(S-1)/2|0,++ea[S];for(w=0,x=Je(e5g,eUp,362,2*b.length,0,1);w'?":IE(eXJ,e)?"'(?<' or '(? toIndex: ",e$M=", toIndex: ",e$O="Index: ",e$A=", Size: ",e$L="org.eclipse.elk.alg.common",e$C={62:1},e$I="org.eclipse.elk.alg.common.compaction",e$D="Scanline/EventHandler",e$N="org.eclipse.elk.alg.common.compaction.oned",e$P="CNode belongs to another CGroup.",e$R="ISpacingsHandler/1",e$j="The ",e$F=" instance has been finished already.",e$Y="The direction ",e$B=" is not supported by the CGraph instance.",e$U="OneDimensionalCompactor",e$H="OneDimensionalCompactor/lambda$0$Type",e$$="Quadruplet",e$z="ScanlineConstraintCalculator",e$G="ScanlineConstraintCalculator/ConstraintsScanlineHandler",e$W="ScanlineConstraintCalculator/ConstraintsScanlineHandler/lambda$0$Type",e$K="ScanlineConstraintCalculator/Timestamp",e$V="ScanlineConstraintCalculator/lambda$0$Type",e$q={169:1,45:1},e$Z="org.eclipse.elk.alg.common.compaction.options",e$X="org.eclipse.elk.core.data",e$J="org.eclipse.elk.polyomino.traversalStrategy",e$Q="org.eclipse.elk.polyomino.lowLevelSort",e$1="org.eclipse.elk.polyomino.highLevelSort",e$0="org.eclipse.elk.polyomino.fill",e$2={130:1},e$3="polyomino",e$4="org.eclipse.elk.alg.common.networksimplex",e$5={177:1,3:1,4:1},e$6="org.eclipse.elk.alg.common.nodespacing",e$9="org.eclipse.elk.alg.common.nodespacing.cellsystem",e$8="CENTER",e$7={212:1,326:1},eze={3:1,4:1,5:1,595:1},ezt="LEFT",ezn="RIGHT",ezr="Vertical alignment cannot be null",ezi="BOTTOM",eza="org.eclipse.elk.alg.common.nodespacing.internal",ezo="UNDEFINED",ezs=.01,ezu="org.eclipse.elk.alg.common.nodespacing.internal.algorithm",ezc="LabelPlacer/lambda$0$Type",ezl="LabelPlacer/lambda$1$Type",ezf="portRatioOrPosition",ezd="org.eclipse.elk.alg.common.overlaps",ezh="DOWN",ezp="org.eclipse.elk.alg.common.polyomino",ezb="NORTH",ezm="EAST",ezg="SOUTH",ezv="WEST",ezy="org.eclipse.elk.alg.common.polyomino.structures",ezw="Direction",ez_="Grid is only of size ",ezE=". Requested point (",ezS=") is out of bounds.",ezk=" Given center based coordinates were (",ezx="org.eclipse.elk.graph.properties",ezT="IPropertyHolder",ezM={3:1,94:1,134:1},ezO="org.eclipse.elk.alg.common.spore",ezA="org.eclipse.elk.alg.common.utils",ezL={209:1},ezC="org.eclipse.elk.core",ezI="Connected Components Compaction",ezD="org.eclipse.elk.alg.disco",ezN="org.eclipse.elk.alg.disco.graph",ezP="org.eclipse.elk.alg.disco.options",ezR="CompactionStrategy",ezj="org.eclipse.elk.disco.componentCompaction.strategy",ezF="org.eclipse.elk.disco.componentCompaction.componentLayoutAlgorithm",ezY="org.eclipse.elk.disco.debug.discoGraph",ezB="org.eclipse.elk.disco.debug.discoPolys",ezU="componentCompaction",ezH="org.eclipse.elk.disco",ez$="org.eclipse.elk.spacing.componentComponent",ezz="org.eclipse.elk.edge.thickness",ezG="org.eclipse.elk.aspectRatio",ezW="org.eclipse.elk.padding",ezK="org.eclipse.elk.alg.disco.transform",ezV=1.5707963267948966,ezq=17976931348623157e292,ezZ={3:1,4:1,5:1,192:1},ezX={3:1,6:1,4:1,5:1,106:1,120:1},ezJ="org.eclipse.elk.alg.force",ezQ="ComponentsProcessor",ez1="ComponentsProcessor/1",ez0="org.eclipse.elk.alg.force.graph",ez2="Component Layout",ez3="org.eclipse.elk.alg.force.model",ez4="org.eclipse.elk.force.model",ez5="org.eclipse.elk.force.iterations",ez6="org.eclipse.elk.force.repulsivePower",ez9="org.eclipse.elk.force.temperature",ez8=.001,ez7="org.eclipse.elk.force.repulsion",eGe="org.eclipse.elk.alg.force.options",eGt=1.600000023841858,eGn="org.eclipse.elk.force",eGr="org.eclipse.elk.priority",eGi="org.eclipse.elk.spacing.nodeNode",eGa="org.eclipse.elk.spacing.edgeLabel",eGo="org.eclipse.elk.randomSeed",eGs="org.eclipse.elk.separateConnectedComponents",eGu="org.eclipse.elk.interactive",eGc="org.eclipse.elk.portConstraints",eGl="org.eclipse.elk.edgeLabels.inline",eGf="org.eclipse.elk.omitNodeMicroLayout",eGd="org.eclipse.elk.nodeSize.options",eGh="org.eclipse.elk.nodeSize.constraints",eGp="org.eclipse.elk.nodeLabels.placement",eGb="org.eclipse.elk.portLabels.placement",eGm="origin",eGg="random",eGv="boundingBox.upLeft",eGy="boundingBox.lowRight",eGw="org.eclipse.elk.stress.fixed",eG_="org.eclipse.elk.stress.desiredEdgeLength",eGE="org.eclipse.elk.stress.dimension",eGS="org.eclipse.elk.stress.epsilon",eGk="org.eclipse.elk.stress.iterationLimit",eGx="org.eclipse.elk.stress",eGT="ELK Stress",eGM="org.eclipse.elk.nodeSize.minimum",eGO="org.eclipse.elk.alg.force.stress",eGA="Layered layout",eGL="org.eclipse.elk.alg.layered",eGC="org.eclipse.elk.alg.layered.compaction.components",eGI="org.eclipse.elk.alg.layered.compaction.oned",eGD="org.eclipse.elk.alg.layered.compaction.oned.algs",eGN="org.eclipse.elk.alg.layered.compaction.recthull",eGP="org.eclipse.elk.alg.layered.components",eGR="NONE",eGj={3:1,6:1,4:1,9:1,5:1,122:1},eGF={3:1,6:1,4:1,5:1,141:1,106:1,120:1},eGY="org.eclipse.elk.alg.layered.compound",eGB={51:1},eGU="org.eclipse.elk.alg.layered.graph",eGH=" -> ",eG$="Not supported by LGraph",eGz="Port side is undefined",eGG={3:1,6:1,4:1,5:1,474:1,141:1,106:1,120:1},eGW={3:1,6:1,4:1,5:1,141:1,193:1,203:1,106:1,120:1},eGK={3:1,6:1,4:1,5:1,141:1,1943:1,203:1,106:1,120:1},eGV="([{\"' \r\n",eGq=")]}\"' \r\n",eGZ="The given string contains parts that cannot be parsed as numbers.",eGX="org.eclipse.elk.core.math",eGJ={3:1,4:1,142:1,207:1,414:1},eGQ={3:1,4:1,116:1,207:1,414:1},eG1="org.eclipse.elk.layered",eG0="org.eclipse.elk.alg.layered.graph.transform",eG2="ElkGraphImporter",eG3="ElkGraphImporter/lambda$0$Type",eG4="ElkGraphImporter/lambda$1$Type",eG5="ElkGraphImporter/lambda$2$Type",eG6="ElkGraphImporter/lambda$4$Type",eG9="Node margin calculation",eG8="org.eclipse.elk.alg.layered.intermediate",eG7="ONE_SIDED_GREEDY_SWITCH",eWe="TWO_SIDED_GREEDY_SWITCH",eWt="No implementation is available for the layout processor ",eWn="IntermediateProcessorStrategy",eWr="Node '",eWi="FIRST_SEPARATE",eWa="LAST_SEPARATE",eWo="Odd port side processing",eWs="org.eclipse.elk.alg.layered.intermediate.compaction",eWu="org.eclipse.elk.alg.layered.intermediate.greedyswitch",eWc="org.eclipse.elk.alg.layered.p3order.counting",eWl={225:1},eWf="org.eclipse.elk.alg.layered.intermediate.loops",eWd="org.eclipse.elk.alg.layered.intermediate.loops.ordering",eWh="org.eclipse.elk.alg.layered.intermediate.loops.routing",eWp="org.eclipse.elk.alg.layered.intermediate.preserveorder",eWb="org.eclipse.elk.alg.layered.intermediate.wrapping",eWm="org.eclipse.elk.alg.layered.options",eWg="INTERACTIVE",eWv="DEPTH_FIRST",eWy="EDGE_LENGTH",eWw="SELF_LOOPS",eW_="firstTryWithInitialOrder",eWE="org.eclipse.elk.layered.directionCongruency",eWS="org.eclipse.elk.layered.feedbackEdges",eWk="org.eclipse.elk.layered.interactiveReferencePoint",eWx="org.eclipse.elk.layered.mergeEdges",eWT="org.eclipse.elk.layered.mergeHierarchyEdges",eWM="org.eclipse.elk.layered.allowNonFlowPortsToSwitchSides",eWO="org.eclipse.elk.layered.portSortingStrategy",eWA="org.eclipse.elk.layered.thoroughness",eWL="org.eclipse.elk.layered.unnecessaryBendpoints",eWC="org.eclipse.elk.layered.generatePositionAndLayerIds",eWI="org.eclipse.elk.layered.cycleBreaking.strategy",eWD="org.eclipse.elk.layered.layering.strategy",eWN="org.eclipse.elk.layered.layering.layerConstraint",eWP="org.eclipse.elk.layered.layering.layerChoiceConstraint",eWR="org.eclipse.elk.layered.layering.layerId",eWj="org.eclipse.elk.layered.layering.minWidth.upperBoundOnWidth",eWF="org.eclipse.elk.layered.layering.minWidth.upperLayerEstimationScalingFactor",eWY="org.eclipse.elk.layered.layering.nodePromotion.strategy",eWB="org.eclipse.elk.layered.layering.nodePromotion.maxIterations",eWU="org.eclipse.elk.layered.layering.coffmanGraham.layerBound",eWH="org.eclipse.elk.layered.crossingMinimization.strategy",eW$="org.eclipse.elk.layered.crossingMinimization.forceNodeModelOrder",eWz="org.eclipse.elk.layered.crossingMinimization.hierarchicalSweepiness",eWG="org.eclipse.elk.layered.crossingMinimization.semiInteractive",eWW="org.eclipse.elk.layered.crossingMinimization.positionChoiceConstraint",eWK="org.eclipse.elk.layered.crossingMinimization.positionId",eWV="org.eclipse.elk.layered.crossingMinimization.greedySwitch.activationThreshold",eWq="org.eclipse.elk.layered.crossingMinimization.greedySwitch.type",eWZ="org.eclipse.elk.layered.crossingMinimization.greedySwitchHierarchical.type",eWX="org.eclipse.elk.layered.nodePlacement.strategy",eWJ="org.eclipse.elk.layered.nodePlacement.favorStraightEdges",eWQ="org.eclipse.elk.layered.nodePlacement.bk.edgeStraightening",eW1="org.eclipse.elk.layered.nodePlacement.bk.fixedAlignment",eW0="org.eclipse.elk.layered.nodePlacement.linearSegments.deflectionDampening",eW2="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility",eW3="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility.default",eW4="org.eclipse.elk.layered.edgeRouting.selfLoopDistribution",eW5="org.eclipse.elk.layered.edgeRouting.selfLoopOrdering",eW6="org.eclipse.elk.layered.edgeRouting.splines.mode",eW9="org.eclipse.elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor",eW8="org.eclipse.elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth",eW7="org.eclipse.elk.layered.spacing.baseValue",eKe="org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers",eKt="org.eclipse.elk.layered.spacing.edgeEdgeBetweenLayers",eKn="org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers",eKr="org.eclipse.elk.layered.priority.direction",eKi="org.eclipse.elk.layered.priority.shortness",eKa="org.eclipse.elk.layered.priority.straightness",eKo="org.eclipse.elk.layered.compaction.connectedComponents",eKs="org.eclipse.elk.layered.compaction.postCompaction.strategy",eKu="org.eclipse.elk.layered.compaction.postCompaction.constraints",eKc="org.eclipse.elk.layered.highDegreeNodes.treatment",eKl="org.eclipse.elk.layered.highDegreeNodes.threshold",eKf="org.eclipse.elk.layered.highDegreeNodes.treeHeight",eKd="org.eclipse.elk.layered.wrapping.strategy",eKh="org.eclipse.elk.layered.wrapping.additionalEdgeSpacing",eKp="org.eclipse.elk.layered.wrapping.correctionFactor",eKb="org.eclipse.elk.layered.wrapping.cutting.strategy",eKm="org.eclipse.elk.layered.wrapping.cutting.cuts",eKg="org.eclipse.elk.layered.wrapping.cutting.msd.freedom",eKv="org.eclipse.elk.layered.wrapping.validify.strategy",eKy="org.eclipse.elk.layered.wrapping.validify.forbiddenIndices",eKw="org.eclipse.elk.layered.wrapping.multiEdge.improveCuts",eK_="org.eclipse.elk.layered.wrapping.multiEdge.distancePenalty",eKE="org.eclipse.elk.layered.wrapping.multiEdge.improveWrappedEdges",eKS="org.eclipse.elk.layered.edgeLabels.sideSelection",eKk="org.eclipse.elk.layered.edgeLabels.centerLabelPlacementStrategy",eKx="org.eclipse.elk.layered.considerModelOrder.strategy",eKT="org.eclipse.elk.layered.considerModelOrder.noModelOrder",eKM="org.eclipse.elk.layered.considerModelOrder.components",eKO="org.eclipse.elk.layered.considerModelOrder.longEdgeStrategy",eKA="org.eclipse.elk.layered.considerModelOrder.crossingCounterNodeInfluence",eKL="org.eclipse.elk.layered.considerModelOrder.crossingCounterPortInfluence",eKC="layering",eKI="layering.minWidth",eKD="layering.nodePromotion",eKN="crossingMinimization",eKP="org.eclipse.elk.hierarchyHandling",eKR="crossingMinimization.greedySwitch",eKj="nodePlacement",eKF="nodePlacement.bk",eKY="edgeRouting",eKB="org.eclipse.elk.edgeRouting",eKU="spacing",eKH="priority",eK$="compaction",eKz="compaction.postCompaction",eKG="Specifies whether and how post-process compaction is applied.",eKW="highDegreeNodes",eKK="wrapping",eKV="wrapping.cutting",eKq="wrapping.validify",eKZ="wrapping.multiEdge",eKX="edgeLabels",eKJ="considerModelOrder",eKQ="org.eclipse.elk.spacing.commentComment",eK1="org.eclipse.elk.spacing.commentNode",eK0="org.eclipse.elk.spacing.edgeEdge",eK2="org.eclipse.elk.spacing.edgeNode",eK3="org.eclipse.elk.spacing.labelLabel",eK4="org.eclipse.elk.spacing.labelPortHorizontal",eK5="org.eclipse.elk.spacing.labelPortVertical",eK6="org.eclipse.elk.spacing.labelNode",eK9="org.eclipse.elk.spacing.nodeSelfLoop",eK8="org.eclipse.elk.spacing.portPort",eK7="org.eclipse.elk.spacing.individual",eVe="org.eclipse.elk.port.borderOffset",eVt="org.eclipse.elk.noLayout",eVn="org.eclipse.elk.port.side",eVr="org.eclipse.elk.debugMode",eVi="org.eclipse.elk.alignment",eVa="org.eclipse.elk.insideSelfLoops.activate",eVo="org.eclipse.elk.insideSelfLoops.yo",eVs="org.eclipse.elk.nodeSize.fixedGraphSize",eVu="org.eclipse.elk.direction",eVc="org.eclipse.elk.nodeLabels.padding",eVl="org.eclipse.elk.portLabels.nextToPortIfPossible",eVf="org.eclipse.elk.portLabels.treatAsGroup",eVd="org.eclipse.elk.portAlignment.default",eVh="org.eclipse.elk.portAlignment.north",eVp="org.eclipse.elk.portAlignment.south",eVb="org.eclipse.elk.portAlignment.west",eVm="org.eclipse.elk.portAlignment.east",eVg="org.eclipse.elk.contentAlignment",eVv="org.eclipse.elk.junctionPoints",eVy="org.eclipse.elk.edgeLabels.placement",eVw="org.eclipse.elk.port.index",eV_="org.eclipse.elk.commentBox",eVE="org.eclipse.elk.hypernode",eVS="org.eclipse.elk.port.anchor",eVk="org.eclipse.elk.partitioning.activate",eVx="org.eclipse.elk.partitioning.partition",eVT="org.eclipse.elk.position",eVM="org.eclipse.elk.margins",eVO="org.eclipse.elk.spacing.portsSurrounding",eVA="org.eclipse.elk.interactiveLayout",eVL="org.eclipse.elk.core.util",eVC={3:1,4:1,5:1,593:1},eVI="NETWORK_SIMPLEX",eVD={123:1,51:1},eVN="org.eclipse.elk.alg.layered.p1cycles",eVP="org.eclipse.elk.alg.layered.p2layers",eVR={402:1,225:1},eVj={832:1,3:1,4:1},eVF="org.eclipse.elk.alg.layered.p3order",eVY="org.eclipse.elk.alg.layered.p4nodes",eVB={3:1,4:1,5:1,840:1},eVU=1e-5,eVH="org.eclipse.elk.alg.layered.p4nodes.bk",eV$="org.eclipse.elk.alg.layered.p5edges",eVz="org.eclipse.elk.alg.layered.p5edges.orthogonal",eVG="org.eclipse.elk.alg.layered.p5edges.orthogonal.direction",eVW=1e-6,eVK="org.eclipse.elk.alg.layered.p5edges.splines",eVV=.09999999999999998,eVq=1e-8,eVZ=4.71238898038469,eVX=3.141592653589793,eVJ="org.eclipse.elk.alg.mrtree",eVQ="org.eclipse.elk.alg.mrtree.graph",eV1="org.eclipse.elk.alg.mrtree.intermediate",eV0="Set neighbors in level",eV2="DESCENDANTS",eV3="org.eclipse.elk.mrtree.weighting",eV4="org.eclipse.elk.mrtree.searchOrder",eV5="org.eclipse.elk.alg.mrtree.options",eV6="org.eclipse.elk.mrtree",eV9="org.eclipse.elk.tree",eV8="org.eclipse.elk.alg.radial",eV7=6.283185307179586,eqe=5e-324,eqt="org.eclipse.elk.alg.radial.intermediate",eqn="org.eclipse.elk.alg.radial.intermediate.compaction",eqr={3:1,4:1,5:1,106:1},eqi="org.eclipse.elk.alg.radial.intermediate.optimization",eqa="No implementation is available for the layout option ",eqo="org.eclipse.elk.alg.radial.options",eqs="org.eclipse.elk.radial.orderId",equ="org.eclipse.elk.radial.radius",eqc="org.eclipse.elk.radial.compactor",eql="org.eclipse.elk.radial.compactionStepSize",eqf="org.eclipse.elk.radial.sorter",eqd="org.eclipse.elk.radial.wedgeCriteria",eqh="org.eclipse.elk.radial.optimizationCriteria",eqp="org.eclipse.elk.radial",eqb="org.eclipse.elk.alg.radial.p1position.wedge",eqm="org.eclipse.elk.alg.radial.sorting",eqg=5.497787143782138,eqv=3.9269908169872414,eqy=2.356194490192345,eqw="org.eclipse.elk.alg.rectpacking",eq_="org.eclipse.elk.alg.rectpacking.firstiteration",eqE="org.eclipse.elk.alg.rectpacking.options",eqS="org.eclipse.elk.rectpacking.optimizationGoal",eqk="org.eclipse.elk.rectpacking.lastPlaceShift",eqx="org.eclipse.elk.rectpacking.currentPosition",eqT="org.eclipse.elk.rectpacking.desiredPosition",eqM="org.eclipse.elk.rectpacking.onlyFirstIteration",eqO="org.eclipse.elk.rectpacking.rowCompaction",eqA="org.eclipse.elk.rectpacking.expandToAspectRatio",eqL="org.eclipse.elk.rectpacking.targetWidth",eqC="org.eclipse.elk.expandNodes",eqI="org.eclipse.elk.rectpacking",eqD="org.eclipse.elk.alg.rectpacking.util",eqN="No implementation available for ",eqP="org.eclipse.elk.alg.spore",eqR="org.eclipse.elk.alg.spore.options",eqj="org.eclipse.elk.sporeCompaction",eqF="org.eclipse.elk.underlyingLayoutAlgorithm",eqY="org.eclipse.elk.processingOrder.treeConstruction",eqB="org.eclipse.elk.processingOrder.spanningTreeCostFunction",eqU="org.eclipse.elk.processingOrder.preferredRoot",eqH="org.eclipse.elk.processingOrder.rootSelection",eq$="org.eclipse.elk.structure.structureExtractionStrategy",eqz="org.eclipse.elk.compaction.compactionStrategy",eqG="org.eclipse.elk.compaction.orthogonal",eqW="org.eclipse.elk.overlapRemoval.maxIterations",eqK="org.eclipse.elk.overlapRemoval.runScanline",eqV="processingOrder",eqq="overlapRemoval",eqZ="org.eclipse.elk.sporeOverlap",eqX="org.eclipse.elk.alg.spore.p1structure",eqJ="org.eclipse.elk.alg.spore.p2processingorder",eqQ="org.eclipse.elk.alg.spore.p3execution",eq1="Invalid index: ",eq0="org.eclipse.elk.core.alg",eq2={331:1},eq3={288:1},eq4="Make sure its type is registered with the ",eq5=" utility class.",eq6="true",eq9="false",eq8="Couldn't clone property '",eq7=.05,eZe="org.eclipse.elk.core.options",eZt=1.2999999523162842,eZn="org.eclipse.elk.box",eZr="org.eclipse.elk.box.packingMode",eZi="org.eclipse.elk.algorithm",eZa="org.eclipse.elk.resolvedAlgorithm",eZo="org.eclipse.elk.bendPoints",eZs="org.eclipse.elk.labelManager",eZu="org.eclipse.elk.scaleFactor",eZc="org.eclipse.elk.animate",eZl="org.eclipse.elk.animTimeFactor",eZf="org.eclipse.elk.layoutAncestors",eZd="org.eclipse.elk.maxAnimTime",eZh="org.eclipse.elk.minAnimTime",eZp="org.eclipse.elk.progressBar",eZb="org.eclipse.elk.validateGraph",eZm="org.eclipse.elk.validateOptions",eZg="org.eclipse.elk.zoomToFit",eZv="org.eclipse.elk.font.name",eZy="org.eclipse.elk.font.size",eZw="org.eclipse.elk.edge.type",eZ_="partitioning",eZE="nodeLabels",eZS="portAlignment",eZk="nodeSize",eZx="port",eZT="portLabels",eZM="insideSelfLoops",eZO="org.eclipse.elk.fixed",eZA="org.eclipse.elk.random",eZL="port must have a parent node to calculate the port side",eZC="The edge needs to have exactly one edge section. Found: ",eZI="org.eclipse.elk.core.util.adapters",eZD="org.eclipse.emf.ecore",eZN="org.eclipse.elk.graph",eZP="EMapPropertyHolder",eZR="ElkBendPoint",eZj="ElkGraphElement",eZF="ElkConnectableShape",eZY="ElkEdge",eZB="ElkEdgeSection",eZU="EModelElement",eZH="ENamedElement",eZ$="ElkLabel",eZz="ElkNode",eZG="ElkPort",eZW={92:1,90:1},eZK="org.eclipse.emf.common.notify.impl",eZV="The feature '",eZq="' is not a valid changeable feature",eZZ="Expecting null",eZX="' is not a valid feature",eZJ="The feature ID",eZQ=" is not a valid feature ID",eZ1=32768,eZ0={105:1,92:1,90:1,56:1,49:1,97:1},eZ2="org.eclipse.emf.ecore.impl",eZ3="org.eclipse.elk.graph.impl",eZ4="Recursive containment not allowed for ",eZ5="The datatype '",eZ6="' is not a valid classifier",eZ9="The value '",eZ8={190:1,3:1,4:1},eZ7="The class '",eXe="http://www.eclipse.org/elk/ElkGraph",eXt=1024,eXn="property",eXr="value",eXi="source",eXa="properties",eXo="identifier",eXs="height",eXu="width",eXc="parent",eXl="text",eXf="children",eXd="hierarchical",eXh="sources",eXp="targets",eXb="sections",eXm="bendPoints",eXg="outgoingShape",eXv="incomingShape",eXy="outgoingSections",eXw="incomingSections",eX_="org.eclipse.emf.common.util",eXE="Severe implementation error in the Json to ElkGraph importer.",eXS="id",eXk="org.eclipse.elk.graph.json",eXx="Unhandled parameter types: ",eXT="startPoint",eXM="An edge must have at least one source and one target (edge id: '",eXO="').",eXA="Referenced edge section does not exist: ",eXL=" (edge id: '",eXC="target",eXI="sourcePoint",eXD="targetPoint",eXN="group",eXP="name",eXR="connectableShape cannot be null",eXj="edge cannot be null",eXF="Passed edge is not 'simple'.",eXY="org.eclipse.elk.graph.util",eXB="The 'no duplicates' constraint is violated",eXU="targetIndex=",eXH=", size=",eX$="sourceIndex=",eXz={3:1,4:1,20:1,28:1,52:1,14:1,15:1,54:1,67:1,63:1,58:1},eXG={3:1,4:1,20:1,28:1,52:1,14:1,47:1,15:1,54:1,67:1,63:1,58:1,588:1},eXW="logging",eXK="measureExecutionTime",eXV="parser.parse.1",eXq="parser.parse.2",eXZ="parser.next.1",eXX="parser.next.2",eXJ="parser.next.3",eXQ="parser.next.4",eX1="parser.factor.1",eX0="parser.factor.2",eX2="parser.factor.3",eX3="parser.factor.4",eX4="parser.factor.5",eX5="parser.factor.6",eX6="parser.atom.1",eX9="parser.atom.2",eX8="parser.atom.3",eX7="parser.atom.4",eJe="parser.atom.5",eJt="parser.cc.1",eJn="parser.cc.2",eJr="parser.cc.3",eJi="parser.cc.5",eJa="parser.cc.6",eJo="parser.cc.7",eJs="parser.cc.8",eJu="parser.ope.1",eJc="parser.ope.2",eJl="parser.ope.3",eJf="parser.descape.1",eJd="parser.descape.2",eJh="parser.descape.3",eJp="parser.descape.4",eJb="parser.descape.5",eJm="parser.process.1",eJg="parser.quantifier.1",eJv="parser.quantifier.2",eJy="parser.quantifier.3",eJw="parser.quantifier.4",eJ_="parser.quantifier.5",eJE="org.eclipse.emf.common.notify",eJS={415:1,672:1},eJk={3:1,4:1,20:1,28:1,52:1,14:1,15:1,67:1,58:1},eJx={366:1,143:1},eJT="index=",eJM={3:1,4:1,5:1,126:1},eJO={3:1,4:1,20:1,28:1,52:1,14:1,15:1,54:1,67:1,58:1},eJA={3:1,6:1,4:1,5:1,192:1},eJL={3:1,4:1,5:1,165:1,367:1},eJC=";/?:@&=+$,",eJI="invalid authority: ",eJD="EAnnotation",eJN="ETypedElement",eJP="EStructuralFeature",eJR="EAttribute",eJj="EClassifier",eJF="EEnumLiteral",eJY="EGenericType",eJB="EOperation",eJU="EParameter",eJH="EReference",eJ$="ETypeParameter",eJz="org.eclipse.emf.ecore.util",eJG={76:1},eJW={3:1,20:1,14:1,15:1,58:1,589:1,76:1,69:1,95:1},eJK="org.eclipse.emf.ecore.util.FeatureMap$Entry",eJV=8192,eJq=2048,eJZ="byte",eJX="char",eJJ="double",eJQ="float",eJ1="int",eJ0="long",eJ2="short",eJ3="java.lang.Object",eJ4={3:1,4:1,5:1,247:1},eJ5={3:1,4:1,5:1,673:1},eJ6={3:1,4:1,20:1,28:1,52:1,14:1,15:1,54:1,67:1,63:1,58:1,69:1},eJ9={3:1,4:1,20:1,28:1,52:1,14:1,15:1,54:1,67:1,63:1,58:1,76:1,69:1,95:1},eJ8="mixed",eJ7="http:///org/eclipse/emf/ecore/util/ExtendedMetaData",eQe="kind",eQt={3:1,4:1,5:1,674:1},eQn={3:1,4:1,20:1,28:1,52:1,14:1,15:1,67:1,58:1,76:1,69:1,95:1},eQr={20:1,28:1,52:1,14:1,15:1,58:1,69:1},eQi={47:1,125:1,279:1},eQa={72:1,332:1},eQo="The value of type '",eQs="' must be of type '",eQu=1316,eQc="http://www.eclipse.org/emf/2002/Ecore",eQl=-32768,eQf="constraints",eQd="baseType",eQh="getEStructuralFeature",eQp="getFeatureID",eQb="feature",eQm="getOperationID",eQg="operation",eQv="defaultValue",eQy="eTypeParameters",eQw="isInstance",eQ_="getEEnumLiteral",eQE="eContainingClass",eQS={55:1},eQk={3:1,4:1,5:1,119:1},eQx="org.eclipse.emf.ecore.resource",eQT={92:1,90:1,591:1,1935:1},eQM="org.eclipse.emf.ecore.resource.impl",eQO="unspecified",eQA="simple",eQL="attribute",eQC="attributeWildcard",eQI="element",eQD="elementWildcard",eQN="collapse",eQP="itemType",eQR="namespace",eQj="##targetNamespace",eQF="whiteSpace",eQY="wildcards",eQB="http://www.eclipse.org/emf/2003/XMLType",eQU="##any",eQH="uninitialized",eQ$="The multiplicity constraint is violated",eQz="org.eclipse.emf.ecore.xml.type",eQG="ProcessingInstruction",eQW="SimpleAnyType",eQK="XMLTypeDocumentRoot",eQV="org.eclipse.emf.ecore.xml.type.impl",eQq="INF",eQZ="processing",eQX="ENTITIES_._base",eQJ="minLength",eQQ="ENTITY",eQ1="NCName",eQ0="IDREFS_._base",eQ2="integer",eQ3="token",eQ4="pattern",eQ5="[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*",eQ6="\\i\\c*",eQ9="[\\i-[:]][\\c-[:]]*",eQ8="nonPositiveInteger",eQ7="maxInclusive",e1e="NMTOKEN",e1t="NMTOKENS_._base",e1n="nonNegativeInteger",e1r="minInclusive",e1i="normalizedString",e1a="unsignedByte",e1o="unsignedInt",e1s="18446744073709551615",e1u="unsignedShort",e1c="processingInstruction",e1l="org.eclipse.emf.ecore.xml.type.internal",e1f=1114111,e1d="Internal Error: shorthands: \\u",e1h="xml:isDigit",e1p="xml:isWord",e1b="xml:isSpace",e1m="xml:isNameChar",e1g="xml:isInitialNameChar",e1v="09٠٩۰۹०९০৯੦੯૦૯୦୯௧௯౦౯೦೯൦൯๐๙໐໙༠༩",e1y="AZaz\xc0\xd6\xd8\xf6\xf8ıĴľŁňŊžƀǃǍǰǴǵǺȗɐʨʻˁΆΆΈΊΌΌΎΡΣώϐϖϚϚϜϜϞϞϠϠϢϳЁЌЎяёќўҁҐӄӇӈӋӌӐӫӮӵӸӹԱՖՙՙաֆאתװײءغفيٱڷںھۀێېۓەەۥۦअहऽऽक़ॡঅঌএঐওনপরললশহড়ঢ়য়ৡৰৱਅਊਏਐਓਨਪਰਲਲ਼ਵਸ਼ਸਹਖ਼ੜਫ਼ਫ਼ੲੴઅઋઍઍએઑઓનપરલળવહઽઽૠૠଅଌଏଐଓନପରଲଳଶହଽଽଡ଼ଢ଼ୟୡஅஊஎஐஒகஙசஜஜஞடணதநபமவஷஹఅఌఎఐఒనపళవహౠౡಅಌಎಐಒನಪಳವಹೞೞೠೡഅഌഎഐഒനപഹൠൡกฮะะาำเๅກຂຄຄງຈຊຊຍຍດທນຟມຣລລວວສຫອຮະະາຳຽຽເໄཀཇཉཀྵႠჅაჶᄀᄀᄂᄃᄅᄇᄉᄉᄋᄌᄎᄒᄼᄼᄾᄾᅀᅀᅌᅌᅎᅎᅐᅐᅔᅕᅙᅙᅟᅡᅣᅣᅥᅥᅧᅧᅩᅩᅭᅮᅲᅳᅵᅵᆞᆞᆨᆨᆫᆫᆮᆯᆷᆸᆺᆺᆼᇂᇫᇫᇰᇰᇹᇹḀẛẠỹἀἕἘἝἠὅὈὍὐὗὙὙὛὛὝὝὟώᾀᾴᾶᾼιιῂῄῆῌῐΐῖΊῠῬῲῴῶῼΩΩKÅ℮℮ↀↂ〇〇〡〩ぁゔァヺㄅㄬ一龥가힣",e1w="Private Use",e1_="ASSIGNED",e1E="\0\x7f\x80\xffĀſƀɏɐʯʰ˿̀ͯͰϿЀӿ԰֏֐׿؀ۿ܀ݏހ޿ऀॿঀ৿਀੿઀૿଀୿஀௿ఀ౿ಀ೿ഀൿ඀෿฀๿຀໿ༀ࿿က႟Ⴀჿᄀᇿሀ፿Ꭰ᏿᐀ᙿ ᚟ᚠ᛿ក៿᠀᢯Ḁỿἀ῿ ⁰₟₠⃏⃐⃿℀⅏⅐↏←⇿∀⋿⌀⏿␀␿⑀⑟①⓿─╿▀▟■◿☀⛿✀➿⠀⣿⺀⻿⼀⿟⿰⿿ 〿぀ゟ゠ヿ㄀ㄯ㄰㆏㆐㆟ㆠㆿ㈀㋿㌀㏿㐀䶵一鿿ꀀ꒏꒐꓏가힣豈﫿ffﭏﭐ﷿︠︯︰﹏﹐﹯ﹰ﻾\uFEFF\uFEFF＀￯",e1S="UNASSIGNED",e1k={3:1,117:1},e1x="org.eclipse.emf.ecore.xml.type.util",e1T={3:1,4:1,5:1,368:1},e1M="org.eclipse.xtext.xbase.lib",e1O="Cannot add elements to a Range",e1A="Cannot set elements in a Range",e1L="Cannot remove elements from a Range",e1C="locale",e1I="default",e1D="user.agent",e1N=null;eB4.goog=eB4.goog||{},eB4.goog.global=eB4.goog.global||eB4,e_Q(),eTS(1,null,{},r),eUe.Fb=function(e){return x5(this,e)},eUe.Gb=function(){return this.gm},eUe.Hb=function(){return Ao(this)},eUe.Ib=function(){var e;return yx(esF(this))+"@"+(e=esj(this)>>>0).toString(16)},eUe.equals=function(e){return this.Fb(e)},eUe.hashCode=function(){return this.Hb()},eUe.toString=function(){return this.Ib()},eTS(290,1,{290:1,2026:1},ese),eUe.le=function(e){var t;return(t=new ese).i=4,e>1?t.c=z9(this,e-1):t.c=this,t},eUe.me=function(){return LW(this),this.b},eUe.ne=function(){return yx(this)},eUe.oe=function(){return LW(this),this.k},eUe.pe=function(){return(4&this.i)!=0},eUe.qe=function(){return(1&this.i)!=0},eUe.Ib=function(){return ee6(this)},eUe.i=0;var e1P=1,e1R=Y5(eUc,"Object",1),e1j=Y5(eUc,"Class",290);eTS(1998,1,eUl),Y5(eUf,"Optional",1998),eTS(1170,1998,eUl,i),eUe.Fb=function(e){return e===this},eUe.Hb=function(){return 2040732332},eUe.Ib=function(){return"Optional.absent()"},eUe.Jb=function(e){return Y9(e),m4(),e0l},Y5(eUf,"Absent",1170),eTS(628,1,{},ve),Y5(eUf,"Joiner",628);var e1F=RL(eUf,"Predicate");eTS(582,1,{169:1,582:1,3:1,45:1},c4),eUe.Mb=function(e){return es_(this,e)},eUe.Lb=function(e){return es_(this,e)},eUe.Fb=function(e){var t;return!!M4(e,582)&&(t=Pp(e,582),eT$(this.a,t.a))},eUe.Hb=function(){return esS(this.a)+306654252},eUe.Ib=function(){return eE7(this.a)},Y5(eUf,"Predicates/AndPredicate",582),eTS(408,1998,{408:1,3:1},c5),eUe.Fb=function(e){var t;return!!M4(e,408)&&(t=Pp(e,408),ecX(this.a,t.a))},eUe.Hb=function(){return 1502476572+esj(this.a)},eUe.Ib=function(){return eUm+this.a+")"},eUe.Jb=function(e){return new c5(H5(e.Kb(this.a),"the Function passed to Optional.transform() must not return null."))},Y5(eUf,"Present",408),eTS(198,1,eUv),eUe.Nb=function(e){F8(this,e)},eUe.Qb=function(){g4()},Y5(eUy,"UnmodifiableIterator",198),eTS(1978,198,eUw),eUe.Qb=function(){g4()},eUe.Rb=function(e){throw p7(new bO)},eUe.Wb=function(e){throw p7(new bO)},Y5(eUy,"UnmodifiableListIterator",1978),eTS(386,1978,eUw),eUe.Ob=function(){return this.c0},eUe.Pb=function(){if(this.c>=this.d)throw p7(new bC);return this.Xb(this.c++)},eUe.Tb=function(){return this.c},eUe.Ub=function(){if(this.c<=0)throw p7(new bC);return this.Xb(--this.c)},eUe.Vb=function(){return this.c-1},eUe.c=0,eUe.d=0,Y5(eUy,"AbstractIndexedListIterator",386),eTS(699,198,eUv),eUe.Ob=function(){return erE(this)},eUe.Pb=function(){return QR(this)},eUe.e=1,Y5(eUy,"AbstractIterator",699),eTS(1986,1,{224:1}),eUe.Zb=function(){var e;return(e=this.f)||(this.f=this.ac())},eUe.Fb=function(e){return es5(this,e)},eUe.Hb=function(){return esj(this.Zb())},eUe.dc=function(){return 0==this.gc()},eUe.ec=function(){return Fh(this)},eUe.Ib=function(){return efF(this.Zb())},Y5(eUy,"AbstractMultimap",1986),eTS(726,1986,eU_),eUe.$b=function(){enK(this)},eUe._b=function(e){return yy(this,e)},eUe.ac=function(){return new wI(this,this.c)},eUe.ic=function(e){return this.hc()},eUe.bc=function(){return new OC(this,this.c)},eUe.jc=function(){return this.mc(this.hc())},eUe.kc=function(){return new m$(this)},eUe.lc=function(){return ew4(this.c.vc().Nc(),new o,64,this.d)},eUe.cc=function(e){return Zq(this,e)},eUe.fc=function(e){return eu9(this,e)},eUe.gc=function(){return this.d},eUe.mc=function(e){return Hj(),new fF(e)},eUe.nc=function(){return new mH(this)},eUe.oc=function(){return ew4(this.c.Cc().Nc(),new a,64,this.d)},eUe.pc=function(e,t){return new XS(this,e,t,null)},eUe.d=0,Y5(eUy,"AbstractMapBasedMultimap",726),eTS(1631,726,eU_),eUe.hc=function(){return new XM(this.a)},eUe.jc=function(){return Hj(),Hj(),e2r},eUe.cc=function(e){return Pp(Zq(this,e),15)},eUe.fc=function(e){return Pp(eu9(this,e),15)},eUe.Zb=function(){return HU(this)},eUe.Fb=function(e){return es5(this,e)},eUe.qc=function(e){return Pp(Zq(this,e),15)},eUe.rc=function(e){return Pp(eu9(this,e),15)},eUe.mc=function(e){return $a(Pp(e,15))},eUe.pc=function(e,t){return Vu(this,e,Pp(t,15),null)},Y5(eUy,"AbstractListMultimap",1631),eTS(732,1,eUE),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return this.c.Ob()||this.e.Ob()},eUe.Pb=function(){var e;return this.e.Ob()||(e=Pp(this.c.Pb(),42),this.b=e.cd(),this.a=Pp(e.dd(),14),this.e=this.a.Kc()),this.sc(this.b,this.e.Pb())},eUe.Qb=function(){this.e.Qb(),this.a.dc()&&this.c.Qb(),--this.d.d},Y5(eUy,"AbstractMapBasedMultimap/Itr",732),eTS(1099,732,eUE,mH),eUe.sc=function(e,t){return t},Y5(eUy,"AbstractMapBasedMultimap/1",1099),eTS(1100,1,{},a),eUe.Kb=function(e){return Pp(e,14).Nc()},Y5(eUy,"AbstractMapBasedMultimap/1methodref$spliterator$Type",1100),eTS(1101,732,eUE,m$),eUe.sc=function(e,t){return new wD(e,t)},Y5(eUy,"AbstractMapBasedMultimap/2",1101);var e1Y=RL(eUS,"Map");eTS(1967,1,eUk),eUe.wc=function(e){ear(this,e)},eUe.yc=function(e,t,n){return el6(this,e,t,n)},eUe.$b=function(){this.vc().$b()},eUe.tc=function(e){return emT(this,e)},eUe._b=function(e){return!!ewt(this,e,!1)},eUe.uc=function(e){var t,n,r;for(n=this.vc().Kc();n.Ob();)if(r=(t=Pp(n.Pb(),42)).dd(),xc(e)===xc(r)||null!=e&&ecX(e,r))return!0;return!1},eUe.Fb=function(e){var t,n,r;if(e===this)return!0;if(!M4(e,83)||(r=Pp(e,83),this.gc()!=r.gc()))return!1;for(n=r.vc().Kc();n.Ob();)if(t=Pp(n.Pb(),42),!this.tc(t))return!1;return!0},eUe.xc=function(e){return xu(ewt(this,e,!1))},eUe.Hb=function(){return eoP(this.vc())},eUe.dc=function(){return 0==this.gc()},eUe.ec=function(){return new fk(this)},eUe.zc=function(e,t){throw p7(new gW("Put not supported on this map"))},eUe.Ac=function(e){eij(this,e)},eUe.Bc=function(e){return xu(ewt(this,e,!0))},eUe.gc=function(){return this.vc().gc()},eUe.Ib=function(){return ewb(this)},eUe.Cc=function(){return new fT(this)},Y5(eUS,"AbstractMap",1967),eTS(1987,1967,eUk),eUe.bc=function(){return new wU(this)},eUe.vc=function(){return Fd(this)},eUe.ec=function(){var e;return(e=this.g)||(this.g=this.bc())},eUe.Cc=function(){var e;return(e=this.i)||(this.i=new wH(this))},Y5(eUy,"Maps/ViewCachingAbstractMap",1987),eTS(389,1987,eUk,wI),eUe.xc=function(e){return etl(this,e)},eUe.Bc=function(e){return euT(this,e)},eUe.$b=function(){this.d==this.e.c?this.e.$b():RG(new RK(this))},eUe._b=function(e){return ecD(this.d,e)},eUe.Ec=function(){return new c7(this)},eUe.Dc=function(){return this.Ec()},eUe.Fb=function(e){return this===e||ecX(this.d,e)},eUe.Hb=function(){return esj(this.d)},eUe.ec=function(){return this.e.ec()},eUe.gc=function(){return this.d.gc()},eUe.Ib=function(){return efF(this.d)},Y5(eUy,"AbstractMapBasedMultimap/AsMap",389);var e1B=RL(eUc,"Iterable");eTS(28,1,eUx),eUe.Jc=function(e){qX(this,e)},eUe.Lc=function(){return this.Oc()},eUe.Nc=function(){return new Gq(this,0)},eUe.Oc=function(){return new R1(null,this.Nc())},eUe.Fc=function(e){throw p7(new gW("Add not supported on this collection"))},eUe.Gc=function(e){return er7(this,e)},eUe.$b=function(){UG(this)},eUe.Hc=function(e){return eds(this,e,!1)},eUe.Ic=function(e){return eot(this,e)},eUe.dc=function(){return 0==this.gc()},eUe.Mc=function(e){return eds(this,e,!0)},eUe.Pc=function(){return Fn(this)},eUe.Qc=function(e){return emk(this,e)},eUe.Ib=function(){return e_F(this)},Y5(eUS,"AbstractCollection",28);var e1U=RL(eUS,"Set");eTS(eUT,28,eUM),eUe.Nc=function(){return new Gq(this,1)},eUe.Fb=function(e){return ehN(this,e)},eUe.Hb=function(){return eoP(this)},Y5(eUS,"AbstractSet",eUT),eTS(1970,eUT,eUM),Y5(eUy,"Sets/ImprovedAbstractSet",1970),eTS(1971,1970,eUM),eUe.$b=function(){this.Rc().$b()},eUe.Hc=function(e){return edz(this,e)},eUe.dc=function(){return this.Rc().dc()},eUe.Mc=function(e){var t;return!!this.Hc(e)&&(t=Pp(e,42),this.Rc().ec().Mc(t.cd()))},eUe.gc=function(){return this.Rc().gc()},Y5(eUy,"Maps/EntrySet",1971),eTS(1097,1971,eUM,c7),eUe.Hc=function(e){return ecC(this.a.d.vc(),e)},eUe.Kc=function(){return new RK(this.a)},eUe.Rc=function(){return this.a},eUe.Mc=function(e){var t;return!!ecC(this.a.d.vc(),e)&&(t=Pp(e,42),ZM(this.a.e,t.cd()),!0)},eUe.Nc=function(){return Pl(this.a.d.vc().Nc(),new le(this.a))},Y5(eUy,"AbstractMapBasedMultimap/AsMap/AsMapEntries",1097),eTS(1098,1,{},le),eUe.Kb=function(e){return qJ(this.a,Pp(e,42))},Y5(eUy,"AbstractMapBasedMultimap/AsMap/AsMapEntries/0methodref$wrapEntry$Type",1098),eTS(730,1,eUE,RK),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){var e;return e=Pp(this.b.Pb(),42),this.a=Pp(e.dd(),14),qJ(this.c,e)},eUe.Ob=function(){return this.b.Ob()},eUe.Qb=function(){eah(!!this.a),this.b.Qb(),this.c.e.d-=this.a.gc(),this.a.$b(),this.a=null},Y5(eUy,"AbstractMapBasedMultimap/AsMap/AsMapIterator",730),eTS(532,1970,eUM,wU),eUe.$b=function(){this.b.$b()},eUe.Hc=function(e){return this.b._b(e)},eUe.Jc=function(e){Y9(e),this.b.wc(new lk(e))},eUe.dc=function(){return this.b.dc()},eUe.Kc=function(){return new gr(this.b.vc().Kc())},eUe.Mc=function(e){return!!this.b._b(e)&&(this.b.Bc(e),!0)},eUe.gc=function(){return this.b.gc()},Y5(eUy,"Maps/KeySet",532),eTS(318,532,eUM,OC),eUe.$b=function(){var e;RG((e=this.b.vc().Kc(),new wg(this,e)))},eUe.Ic=function(e){return this.b.ec().Ic(e)},eUe.Fb=function(e){return this===e||ecX(this.b.ec(),e)},eUe.Hb=function(){return esj(this.b.ec())},eUe.Kc=function(){var e;return e=this.b.vc().Kc(),new wg(this,e)},eUe.Mc=function(e){var t,n;return n=0,(t=Pp(this.b.Bc(e),14))&&(n=t.gc(),t.$b(),this.a.d-=n),n>0},eUe.Nc=function(){return this.b.ec().Nc()},Y5(eUy,"AbstractMapBasedMultimap/KeySet",318),eTS(731,1,eUE,wg),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return this.c.Ob()},eUe.Pb=function(){return this.a=Pp(this.c.Pb(),42),this.a.cd()},eUe.Qb=function(){var e;eah(!!this.a),e=Pp(this.a.dd(),14),this.c.Qb(),this.b.a.d-=e.gc(),e.$b(),this.a=null},Y5(eUy,"AbstractMapBasedMultimap/KeySet/1",731),eTS(491,389,{83:1,161:1},LX),eUe.bc=function(){return this.Sc()},eUe.ec=function(){return this.Tc()},eUe.Sc=function(){return new wb(this.c,this.Uc())},eUe.Tc=function(){var e;return(e=this.b)||(this.b=this.Sc())},eUe.Uc=function(){return Pp(this.d,161)},Y5(eUy,"AbstractMapBasedMultimap/SortedAsMap",491),eTS(542,491,eUO,LJ),eUe.bc=function(){return new wm(this.a,Pp(Pp(this.d,161),171))},eUe.Sc=function(){return new wm(this.a,Pp(Pp(this.d,161),171))},eUe.ec=function(){var e;return Pp((e=this.b)||(this.b=new wm(this.a,Pp(Pp(this.d,161),171))),271)},eUe.Tc=function(){var e;return Pp((e=this.b)||(this.b=new wm(this.a,Pp(Pp(this.d,161),171))),271)},eUe.Uc=function(){return Pp(Pp(this.d,161),171)},Y5(eUy,"AbstractMapBasedMultimap/NavigableAsMap",542),eTS(490,318,eUA,wb),eUe.Nc=function(){return this.b.ec().Nc()},Y5(eUy,"AbstractMapBasedMultimap/SortedKeySet",490),eTS(388,490,eUL,wm),Y5(eUy,"AbstractMapBasedMultimap/NavigableKeySet",388),eTS(541,28,eUx,XS),eUe.Fc=function(e){var t,n;return efH(this),n=this.d.dc(),(t=this.d.Fc(e))&&(++this.f.d,n&&CP(this)),t},eUe.Gc=function(e){var t,n,r;return!e.dc()&&(r=(efH(this),this.d.gc()),(t=this.d.Gc(e))&&(n=this.d.gc(),this.f.d+=n-r,0==r&&CP(this)),t)},eUe.$b=function(){var e;0!=(e=(efH(this),this.d.gc()))&&(this.d.$b(),this.f.d-=e,jY(this))},eUe.Hc=function(e){return efH(this),this.d.Hc(e)},eUe.Ic=function(e){return efH(this),this.d.Ic(e)},eUe.Fb=function(e){return e===this||(efH(this),ecX(this.d,e))},eUe.Hb=function(){return efH(this),esj(this.d)},eUe.Kc=function(){return efH(this),new PS(this)},eUe.Mc=function(e){var t;return efH(this),(t=this.d.Mc(e))&&(--this.f.d,jY(this)),t},eUe.gc=function(){return xw(this)},eUe.Nc=function(){return efH(this),this.d.Nc()},eUe.Ib=function(){return efH(this),efF(this.d)},Y5(eUy,"AbstractMapBasedMultimap/WrappedCollection",541);var e1H=RL(eUS,"List");eTS(728,541,{20:1,28:1,14:1,15:1},Fo),eUe.ad=function(e){er8(this,e)},eUe.Nc=function(){return efH(this),this.d.Nc()},eUe.Vc=function(e,t){var n;efH(this),n=this.d.dc(),Pp(this.d,15).Vc(e,t),++this.a.d,n&&CP(this)},eUe.Wc=function(e,t){var n,r,i;return!t.dc()&&(i=(efH(this),this.d.gc()),(n=Pp(this.d,15).Wc(e,t))&&(r=this.d.gc(),this.a.d+=r-i,0==i&&CP(this)),n)},eUe.Xb=function(e){return efH(this),Pp(this.d,15).Xb(e)},eUe.Xc=function(e){return efH(this),Pp(this.d,15).Xc(e)},eUe.Yc=function(){return efH(this),new Mb(this)},eUe.Zc=function(e){return efH(this),new HM(this,e)},eUe.$c=function(e){var t;return efH(this),t=Pp(this.d,15).$c(e),--this.a.d,jY(this),t},eUe._c=function(e,t){return efH(this),Pp(this.d,15)._c(e,t)},eUe.bd=function(e,t){return efH(this),Vu(this.a,this.e,Pp(this.d,15).bd(e,t),this.b?this.b:this)},Y5(eUy,"AbstractMapBasedMultimap/WrappedList",728),eTS(1096,728,{20:1,28:1,14:1,15:1,54:1},A7),Y5(eUy,"AbstractMapBasedMultimap/RandomAccessWrappedList",1096),eTS(620,1,eUE,PS),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return UW(this),this.b.Ob()},eUe.Pb=function(){return UW(this),this.b.Pb()},eUe.Qb=function(){OG(this)},Y5(eUy,"AbstractMapBasedMultimap/WrappedCollection/WrappedIterator",620),eTS(729,620,eUC,Mb,HM),eUe.Qb=function(){OG(this)},eUe.Rb=function(e){var t;t=0==xw(this.a),(UW(this),Pp(this.b,125)).Rb(e),++this.a.a.d,t&&CP(this.a)},eUe.Sb=function(){return(UW(this),Pp(this.b,125)).Sb()},eUe.Tb=function(){return(UW(this),Pp(this.b,125)).Tb()},eUe.Ub=function(){return(UW(this),Pp(this.b,125)).Ub()},eUe.Vb=function(){return(UW(this),Pp(this.b,125)).Vb()},eUe.Wb=function(e){(UW(this),Pp(this.b,125)).Wb(e)},Y5(eUy,"AbstractMapBasedMultimap/WrappedList/WrappedListIterator",729),eTS(727,541,eUA,L3),eUe.Nc=function(){return efH(this),this.d.Nc()},Y5(eUy,"AbstractMapBasedMultimap/WrappedSortedSet",727),eTS(1095,727,eUL,TB),Y5(eUy,"AbstractMapBasedMultimap/WrappedNavigableSet",1095),eTS(1094,541,eUM,L4),eUe.Nc=function(){return efH(this),this.d.Nc()},Y5(eUy,"AbstractMapBasedMultimap/WrappedSet",1094),eTS(1103,1,{},o),eUe.Kb=function(e){return Xb(Pp(e,42))},Y5(eUy,"AbstractMapBasedMultimap/lambda$1$Type",1103),eTS(1102,1,{},lt),eUe.Kb=function(e){return new wD(this.a,e)},Y5(eUy,"AbstractMapBasedMultimap/lambda$2$Type",1102);var e1$=RL(eUS,"Map/Entry");eTS(345,1,eUI),eUe.Fb=function(e){var t;return!!M4(e,42)&&(t=Pp(e,42),BG(this.cd(),t.cd())&&BG(this.dd(),t.dd()))},eUe.Hb=function(){var e,t;return e=this.cd(),t=this.dd(),(null==e?0:esj(e))^(null==t?0:esj(t))},eUe.ed=function(e){throw p7(new bO)},eUe.Ib=function(){return this.cd()+"="+this.dd()},Y5(eUy,eUD,345),eTS(1988,28,eUx),eUe.$b=function(){this.fd().$b()},eUe.Hc=function(e){var t;return!!M4(e,42)&&(t=Pp(e,42),Kr(this.fd(),t.cd(),t.dd()))},eUe.Mc=function(e){var t;return!!M4(e,42)&&(t=Pp(e,42),Ki(this.fd(),t.cd(),t.dd()))},eUe.gc=function(){return this.fd().d},Y5(eUy,"Multimaps/Entries",1988),eTS(733,1988,eUx,ln),eUe.Kc=function(){return this.a.kc()},eUe.fd=function(){return this.a},eUe.Nc=function(){return this.a.lc()},Y5(eUy,"AbstractMultimap/Entries",733),eTS(734,733,eUM,mz),eUe.Nc=function(){return this.a.lc()},eUe.Fb=function(e){return eEB(this,e)},eUe.Hb=function(){return eie(this)},Y5(eUy,"AbstractMultimap/EntrySet",734),eTS(735,28,eUx,lr),eUe.$b=function(){this.a.$b()},eUe.Hc=function(e){return eun(this.a,e)},eUe.Kc=function(){return this.a.nc()},eUe.gc=function(){return this.a.d},eUe.Nc=function(){return this.a.oc()},Y5(eUy,"AbstractMultimap/Values",735),eTS(1989,28,{835:1,20:1,28:1,14:1}),eUe.Jc=function(e){Y9(e),Uz(this).Jc(new lS(e))},eUe.Nc=function(){var e;return ew4(e=Uz(this).Nc(),new y,64|1296&e.qd(),this.a.d)},eUe.Fc=function(e){return g5(),!0},eUe.Gc=function(e){return Y9(this),Y9(e),M4(e,543)?KM(Pp(e,835)):!e.dc()&&eel(this,e.Kc())},eUe.Hc=function(e){var t;return((t=Pp(ecA(HU(this.a),e),14))?t.gc():0)>0},eUe.Fb=function(e){return eMc(this,e)},eUe.Hb=function(){return esj(Uz(this))},eUe.dc=function(){return Uz(this).dc()},eUe.Mc=function(e){return ekJ(this,e,1)>0},eUe.Ib=function(){return efF(Uz(this))},Y5(eUy,"AbstractMultiset",1989),eTS(1991,1970,eUM),eUe.$b=function(){enK(this.a.a)},eUe.Hc=function(e){var t,n;return!!M4(e,492)&&(n=Pp(e,416),!(0>=Pp(n.a.dd(),14).gc())&&(t=GB(this.a,n.a.cd()))==Pp(n.a.dd(),14).gc())},eUe.Mc=function(e){var t,n,r,i;return!!M4(e,492)&&(t=(n=Pp(e,416)).a.cd(),0!=(r=Pp(n.a.dd(),14).gc()))&&ekQ(i=this.a,t,r)},Y5(eUy,"Multisets/EntrySet",1991),eTS(1109,1991,eUM,li),eUe.Kc=function(){return new ga(Fd(HU(this.a.a)).Kc())},eUe.gc=function(){return HU(this.a.a).gc()},Y5(eUy,"AbstractMultiset/EntrySet",1109),eTS(619,726,eU_),eUe.hc=function(){return this.gd()},eUe.jc=function(){return this.hd()},eUe.cc=function(e){return this.jd(e)},eUe.fc=function(e){return this.kd(e)},eUe.Zb=function(){var e;return(e=this.f)||(this.f=this.ac())},eUe.hd=function(){return Hj(),Hj(),e2a},eUe.Fb=function(e){return es5(this,e)},eUe.jd=function(e){return Pp(Zq(this,e),21)},eUe.kd=function(e){return Pp(eu9(this,e),21)},eUe.mc=function(e){return Hj(),new vd(Pp(e,21))},eUe.pc=function(e,t){return new L4(this,e,Pp(t,21))},Y5(eUy,"AbstractSetMultimap",619),eTS(1657,619,eU_),eUe.hc=function(){return new yB(this.b)},eUe.gd=function(){return new yB(this.b)},eUe.jc=function(){return Bo(new yB(this.b))},eUe.hd=function(){return Bo(new yB(this.b))},eUe.cc=function(e){return Pp(Pp(Zq(this,e),21),84)},eUe.jd=function(e){return Pp(Pp(Zq(this,e),21),84)},eUe.fc=function(e){return Pp(Pp(eu9(this,e),21),84)},eUe.kd=function(e){return Pp(Pp(eu9(this,e),21),84)},eUe.mc=function(e){return M4(e,271)?Bo(Pp(e,271)):(Hj(),new O4(Pp(e,84)))},eUe.Zb=function(){var e;return(e=this.f)||(this.f=M4(this.c,171)?new LJ(this,Pp(this.c,171)):M4(this.c,161)?new LX(this,Pp(this.c,161)):new wI(this,this.c))},eUe.pc=function(e,t){return M4(t,271)?new TB(this,e,Pp(t,271)):new L3(this,e,Pp(t,84))},Y5(eUy,"AbstractSortedSetMultimap",1657),eTS(1658,1657,eU_),eUe.Zb=function(){var e;return Pp(Pp((e=this.f)||(this.f=M4(this.c,171)?new LJ(this,Pp(this.c,171)):M4(this.c,161)?new LX(this,Pp(this.c,161)):new wI(this,this.c)),161),171)},eUe.ec=function(){var e;return Pp(Pp((e=this.i)||(this.i=M4(this.c,171)?new wm(this,Pp(this.c,171)):M4(this.c,161)?new wb(this,Pp(this.c,161)):new OC(this,this.c)),84),271)},eUe.bc=function(){return M4(this.c,171)?new wm(this,Pp(this.c,171)):M4(this.c,161)?new wb(this,Pp(this.c,161)):new OC(this,this.c)},Y5(eUy,"AbstractSortedKeySortedSetMultimap",1658),eTS(2010,1,{1947:1}),eUe.Fb=function(e){return ev7(this,e)},eUe.Hb=function(){var e;return eoP((e=this.g)||(this.g=new la(this)))},eUe.Ib=function(){var e;return ewb((e=this.f)||(this.f=new OP(this)))},Y5(eUy,"AbstractTable",2010),eTS(665,eUT,eUM,la),eUe.$b=function(){g6()},eUe.Hc=function(e){var t,n;return!!M4(e,468)&&(t=Pp(e,682),!!(n=Pp(ecA(Y7(this.a),xh(t.c.e,t.b)),83))&&ecC(n.vc(),new wD(xh(t.c.c,t.a),X_(t.c,t.b,t.a))))},eUe.Kc=function(){return $e(this.a)},eUe.Mc=function(e){var t,n;return!!M4(e,468)&&(t=Pp(e,682),!!(n=Pp(ecA(Y7(this.a),xh(t.c.e,t.b)),83))&&ecI(n.vc(),new wD(xh(t.c.c,t.a),X_(t.c,t.b,t.a))))},eUe.gc=function(){return R8(this.a)},eUe.Nc=function(){return KH(this.a)},Y5(eUy,"AbstractTable/CellSet",665),eTS(1928,28,eUx,lo),eUe.$b=function(){g6()},eUe.Hc=function(e){return ewx(this.a,e)},eUe.Kc=function(){return $t(this.a)},eUe.gc=function(){return R8(this.a)},eUe.Nc=function(){return Kd(this.a)},Y5(eUy,"AbstractTable/Values",1928),eTS(1632,1631,eU_),Y5(eUy,"ArrayListMultimapGwtSerializationDependencies",1632),eTS(513,1632,eU_,gQ,G$),eUe.hc=function(){return new XM(this.a)},eUe.a=0,Y5(eUy,"ArrayListMultimap",513),eTS(664,2010,{664:1,1947:1,3:1},exj),Y5(eUy,"ArrayTable",664),eTS(1924,386,eUw,OI),eUe.Xb=function(e){return new eo7(this.a,e)},Y5(eUy,"ArrayTable/1",1924),eTS(1925,1,{},c6),eUe.ld=function(e){return new eo7(this.a,e)},Y5(eUy,"ArrayTable/1methodref$getCell$Type",1925),eTS(2011,1,{682:1}),eUe.Fb=function(e){var t;return e===this||!!M4(e,468)&&(t=Pp(e,682),BG(xh(this.c.e,this.b),xh(t.c.e,t.b))&&BG(xh(this.c.c,this.a),xh(t.c.c,t.a))&&BG(X_(this.c,this.b,this.a),X_(t.c,t.b,t.a)))},eUe.Hb=function(){return euF(eow(vx(e1R,1),eUp,1,5,[xh(this.c.e,this.b),xh(this.c.c,this.a),X_(this.c,this.b,this.a)]))},eUe.Ib=function(){return"("+xh(this.c.e,this.b)+","+xh(this.c.c,this.a)+")="+X_(this.c,this.b,this.a)},Y5(eUy,"Tables/AbstractCell",2011),eTS(468,2011,{468:1,682:1},eo7),eUe.a=0,eUe.b=0,eUe.d=0,Y5(eUy,"ArrayTable/2",468),eTS(1927,1,{},c9),eUe.ld=function(e){return Qo(this.a,e)},Y5(eUy,"ArrayTable/2methodref$getValue$Type",1927),eTS(1926,386,eUw,OD),eUe.Xb=function(e){return Qo(this.a,e)},Y5(eUy,"ArrayTable/3",1926),eTS(1979,1967,eUk),eUe.$b=function(){RG(this.kc())},eUe.vc=function(){return new lx(this)},eUe.lc=function(){return new Uq(this.kc(),this.gc())},Y5(eUy,"Maps/IteratorBasedAbstractMap",1979),eTS(828,1979,eUk),eUe.$b=function(){throw p7(new bO)},eUe._b=function(e){return yE(this.c,e)},eUe.kc=function(){return new ON(this,this.c.b.c.gc())},eUe.lc=function(){return Rj(this.c.b.c.gc(),16,new c8(this))},eUe.xc=function(e){var t;return(t=Pp(Iq(this.c,e),19))?this.nd(t.a):null},eUe.dc=function(){return this.c.b.c.dc()},eUe.ec=function(){return Fl(this.c)},eUe.zc=function(e,t){var n;if(!(n=Pp(Iq(this.c,e),19)))throw p7(new gL(this.md()+" "+e+" not in "+Fl(this.c)));return this.od(n.a,t)},eUe.Bc=function(e){throw p7(new bO)},eUe.gc=function(){return this.c.b.c.gc()},Y5(eUy,"ArrayTable/ArrayMap",828),eTS(1923,1,{},c8),eUe.ld=function(e){return Bs(this.a,e)},Y5(eUy,"ArrayTable/ArrayMap/0methodref$getEntry$Type",1923),eTS(1921,345,eUI,wk),eUe.cd=function(){return OB(this.a,this.b)},eUe.dd=function(){return this.a.nd(this.b)},eUe.ed=function(e){return this.a.od(this.b,e)},eUe.b=0,Y5(eUy,"ArrayTable/ArrayMap/1",1921),eTS(1922,386,eUw,ON),eUe.Xb=function(e){return Bs(this.a,e)},Y5(eUy,"ArrayTable/ArrayMap/2",1922),eTS(1920,828,eUk,F2),eUe.md=function(){return"Column"},eUe.nd=function(e){return X_(this.b,this.a,e)},eUe.od=function(e,t){return eoy(this.b,this.a,e,t)},eUe.a=0,Y5(eUy,"ArrayTable/Row",1920),eTS(829,828,eUk,OP),eUe.nd=function(e){return new F2(this.a,e)},eUe.zc=function(e,t){return Pp(t,83),g9()},eUe.od=function(e,t){return Pp(t,83),g8()},eUe.md=function(){return"Row"},Y5(eUy,"ArrayTable/RowMap",829),eTS(1120,1,eUj,wx),eUe.qd=function(){return -262&this.a.qd()},eUe.rd=function(){return this.a.rd()},eUe.Nb=function(e){this.a.Nb(new ww(e,this.b))},eUe.sd=function(e){return this.a.sd(new wy(e,this.b))},Y5(eUy,"CollectSpliterators/1",1120),eTS(1121,1,eUF,wy),eUe.td=function(e){this.a.td(this.b.Kb(e))},Y5(eUy,"CollectSpliterators/1/lambda$0$Type",1121),eTS(1122,1,eUF,ww),eUe.td=function(e){this.a.td(this.b.Kb(e))},Y5(eUy,"CollectSpliterators/1/lambda$1$Type",1122),eTS(1123,1,eUj,K4),eUe.qd=function(){return this.a},eUe.rd=function(){return this.d&&(this.b=MS(this.b,this.d.rd())),MS(this.b,0)},eUe.Nb=function(e){this.d&&(this.d.Nb(e),this.d=null),this.c.Nb(new wv(this.e,e)),this.b=0},eUe.sd=function(e){for(;;){if(this.d&&this.d.sd(e))return xg(this.b,eUY)&&(this.b=efe(this.b,1)),!0;if(this.d=null,!this.c.sd(new w_(this,this.e)))return!1}},eUe.a=0,eUe.b=0,Y5(eUy,"CollectSpliterators/1FlatMapSpliterator",1123),eTS(1124,1,eUF,w_),eUe.td=function(e){Iv(this.a,this.b,e)},Y5(eUy,"CollectSpliterators/1FlatMapSpliterator/lambda$0$Type",1124),eTS(1125,1,eUF,wv),eUe.td=function(e){M9(this.b,this.a,e)},Y5(eUy,"CollectSpliterators/1FlatMapSpliterator/lambda$1$Type",1125),eTS(1117,1,eUj,Ig),eUe.qd=function(){return 16464|this.b},eUe.rd=function(){return this.a.rd()},eUe.Nb=function(e){this.a.xe(new wS(e,this.c))},eUe.sd=function(e){return this.a.ye(new wE(e,this.c))},eUe.b=0,Y5(eUy,"CollectSpliterators/1WithCharacteristics",1117),eTS(1118,1,eUB,wE),eUe.ud=function(e){this.a.td(this.b.ld(e))},Y5(eUy,"CollectSpliterators/1WithCharacteristics/lambda$0$Type",1118),eTS(1119,1,eUB,wS),eUe.ud=function(e){this.a.td(this.b.ld(e))},Y5(eUy,"CollectSpliterators/1WithCharacteristics/lambda$1$Type",1119),eTS(245,1,eUU),eUe.wd=function(e){return this.vd(Pp(e,245))},eUe.vd=function(e){var t;return e==(m2(),e0d)?1:e==(m3(),e0f)?-1:0!=(t=(Rg(),eiK(this.a,e.a)))?t:M4(this,519)==M4(e,519)?0:M4(this,519)?1:-1},eUe.zd=function(){return this.a},eUe.Fb=function(e){return ehd(this,e)},Y5(eUy,"Cut",245),eTS(1761,245,eUU,vb),eUe.vd=function(e){return e==this?0:1},eUe.xd=function(e){throw p7(new b_)},eUe.yd=function(e){e.a+="+∞)"},eUe.zd=function(){throw p7(new gC(eUH))},eUe.Hb=function(){return wK(),ebh(this)},eUe.Ad=function(e){return!1},eUe.Ib=function(){return"+∞"},Y5(eUy,"Cut/AboveAll",1761),eTS(519,245,{245:1,519:1,3:1,35:1},OW),eUe.xd=function(e){xT((e.a+="(",e),this.a)},eUe.yd=function(e){Bd(xT(e,this.a),93)},eUe.Hb=function(){return~esj(this.a)},eUe.Ad=function(e){return Rg(),0>eiK(this.a,e)},eUe.Ib=function(){return"/"+this.a+"\\"},Y5(eUy,"Cut/AboveValue",519),eTS(1760,245,eUU,vm),eUe.vd=function(e){return e==this?0:-1},eUe.xd=function(e){e.a+="(-∞"},eUe.yd=function(e){throw p7(new b_)},eUe.zd=function(){throw p7(new gC(eUH))},eUe.Hb=function(){return wK(),ebh(this)},eUe.Ad=function(e){return!0},eUe.Ib=function(){return"-∞"},Y5(eUy,"Cut/BelowAll",1760),eTS(1762,245,eUU,OK),eUe.xd=function(e){xT((e.a+="[",e),this.a)},eUe.yd=function(e){Bd(xT(e,this.a),41)},eUe.Hb=function(){return esj(this.a)},eUe.Ad=function(e){return Rg(),0>=eiK(this.a,e)},eUe.Ib=function(){return"\\"+this.a+"/"},Y5(eUy,"Cut/BelowValue",1762),eTS(537,1,eU$),eUe.Jc=function(e){qX(this,e)},eUe.Ib=function(){return elq(Pp(H5(this,"use Optional.orNull() instead of Optional.or(null)"),20).Kc())},Y5(eUy,"FluentIterable",537),eTS(433,537,eU$,xq),eUe.Kc=function(){return new Fa(OH(this.a.Kc(),new c))},Y5(eUy,"FluentIterable/2",433),eTS(1046,537,eU$,xZ),eUe.Kc=function(){return Y_(this)},Y5(eUy,"FluentIterable/3",1046),eTS(708,386,eUw,Oj),eUe.Xb=function(e){return this.a[e].Kc()},Y5(eUy,"FluentIterable/3/1",708),eTS(1972,1,{}),eUe.Ib=function(){return efF(this.Bd().b)},Y5(eUy,"ForwardingObject",1972),eTS(1973,1972,eUz),eUe.Bd=function(){return this.Cd()},eUe.Jc=function(e){qX(this,e)},eUe.Lc=function(){return this.Oc()},eUe.Nc=function(){return new Gq(this,0)},eUe.Oc=function(){return new R1(null,this.Nc())},eUe.Fc=function(e){return this.Cd(),yD()},eUe.Gc=function(e){return this.Cd(),yN()},eUe.$b=function(){this.Cd(),yP()},eUe.Hc=function(e){return this.Cd().Hc(e)},eUe.Ic=function(e){return this.Cd().Ic(e)},eUe.dc=function(){return this.Cd().b.dc()},eUe.Kc=function(){return this.Cd().Kc()},eUe.Mc=function(e){return this.Cd(),yR()},eUe.gc=function(){return this.Cd().b.gc()},eUe.Pc=function(){return this.Cd().Pc()},eUe.Qc=function(e){return this.Cd().Qc(e)},Y5(eUy,"ForwardingCollection",1973),eTS(1980,28,eUG),eUe.Kc=function(){return this.Ed()},eUe.Fc=function(e){throw p7(new bO)},eUe.Gc=function(e){throw p7(new bO)},eUe.$b=function(){throw p7(new bO)},eUe.Hc=function(e){return null!=e&&eds(this,e,!1)},eUe.Dd=function(){switch(this.gc()){case 0:return Bx(),Bx(),e0h;case 1:return Bx(),new Rz(Y9(this.Ed().Pb()));default:return new F3(this,this.Pc())}},eUe.Mc=function(e){throw p7(new bO)},Y5(eUy,"ImmutableCollection",1980),eTS(712,1980,eUG,bb),eUe.Kc=function(){return JJ(this.a.Kc())},eUe.Hc=function(e){return null!=e&&this.a.Hc(e)},eUe.Ic=function(e){return this.a.Ic(e)},eUe.dc=function(){return this.a.dc()},eUe.Ed=function(){return JJ(this.a.Kc())},eUe.gc=function(){return this.a.gc()},eUe.Pc=function(){return this.a.Pc()},eUe.Qc=function(e){return this.a.Qc(e)},eUe.Ib=function(){return efF(this.a)},Y5(eUy,"ForwardingImmutableCollection",712),eTS(152,1980,eUW),eUe.Kc=function(){return this.Ed()},eUe.Yc=function(){return this.Fd(0)},eUe.Zc=function(e){return this.Fd(e)},eUe.ad=function(e){er8(this,e)},eUe.Nc=function(){return new Gq(this,16)},eUe.bd=function(e,t){return this.Gd(e,t)},eUe.Vc=function(e,t){throw p7(new bO)},eUe.Wc=function(e,t){throw p7(new bO)},eUe.Fb=function(e){return eTJ(this,e)},eUe.Hb=function(){return eaI(this)},eUe.Xc=function(e){return null==e?-1:emx(this,e)},eUe.Ed=function(){return this.Fd(0)},eUe.Fd=function(e){return AR(this,e)},eUe.$c=function(e){throw p7(new bO)},eUe._c=function(e,t){throw p7(new bO)},eUe.Gd=function(e,t){var n;return ecT((n=new wz(this),new Gz(n,e,t)))},Y5(eUy,"ImmutableList",152),eTS(2006,152,eUW),eUe.Kc=function(){return JJ(this.Hd().Kc())},eUe.bd=function(e,t){return ecT(this.Hd().bd(e,t))},eUe.Hc=function(e){return null!=e&&this.Hd().Hc(e)},eUe.Ic=function(e){return this.Hd().Ic(e)},eUe.Fb=function(e){return ecX(this.Hd(),e)},eUe.Xb=function(e){return xh(this,e)},eUe.Hb=function(){return esj(this.Hd())},eUe.Xc=function(e){return this.Hd().Xc(e)},eUe.dc=function(){return this.Hd().dc()},eUe.Ed=function(){return JJ(this.Hd().Kc())},eUe.gc=function(){return this.Hd().gc()},eUe.Gd=function(e,t){return ecT(this.Hd().bd(e,t))},eUe.Pc=function(){return this.Hd().Qc(Je(e1R,eUp,1,this.Hd().gc(),5,1))},eUe.Qc=function(e){return this.Hd().Qc(e)},eUe.Ib=function(){return efF(this.Hd())},Y5(eUy,"ForwardingImmutableList",2006),eTS(714,1,eUV),eUe.vc=function(){return Fc(this)},eUe.wc=function(e){ear(this,e)},eUe.ec=function(){return Fl(this)},eUe.yc=function(e,t,n){return el6(this,e,t,n)},eUe.Cc=function(){return this.Ld()},eUe.$b=function(){throw p7(new bO)},eUe._b=function(e){return null!=this.xc(e)},eUe.uc=function(e){return this.Ld().Hc(e)},eUe.Jd=function(){return new bm(this)},eUe.Kd=function(){return new bg(this)},eUe.Fb=function(e){return eua(this,e)},eUe.Hb=function(){return Fc(this).Hb()},eUe.dc=function(){return 0==this.gc()},eUe.zc=function(e,t){return g7()},eUe.Bc=function(e){throw p7(new bO)},eUe.Ib=function(){return eEo(this)},eUe.Ld=function(){return this.e?this.e:this.e=this.Kd()},eUe.c=null,eUe.d=null,eUe.e=null,Y5(eUy,"ImmutableMap",714),eTS(715,714,eUV),eUe._b=function(e){return yE(this,e)},eUe.uc=function(e){return w1(this.b,e)},eUe.Id=function(){return ecM(new lu(this))},eUe.Jd=function(){return ecM(Uk(this.b))},eUe.Kd=function(){return Dn(),new bb(UE(this.b))},eUe.Fb=function(e){return w2(this.b,e)},eUe.xc=function(e){return Iq(this,e)},eUe.Hb=function(){return esj(this.b.c)},eUe.dc=function(){return this.b.c.dc()},eUe.gc=function(){return this.b.c.gc()},eUe.Ib=function(){return efF(this.b.c)},Y5(eUy,"ForwardingImmutableMap",715),eTS(1974,1973,eUq),eUe.Bd=function(){return this.Md()},eUe.Cd=function(){return this.Md()},eUe.Nc=function(){return new Gq(this,1)},eUe.Fb=function(e){return e===this||this.Md().Fb(e)},eUe.Hb=function(){return this.Md().Hb()},Y5(eUy,"ForwardingSet",1974),eTS(1069,1974,eUq,lu),eUe.Bd=function(){return US(this.a.b)},eUe.Cd=function(){return US(this.a.b)},eUe.Hc=function(e){if(M4(e,42)&&null==Pp(e,42).cd())return!1;try{return wQ(US(this.a.b),e)}catch(t){if(t=eoa(t),M4(t,205))return!1;throw p7(t)}},eUe.Md=function(){return US(this.a.b)},eUe.Qc=function(e){var t;return t=$L(US(this.a.b),e),US(this.a.b).b.gc()=0?"+":"")+(n/60|0),t=Tt(eB4.Math.abs(n)%60),(e_E(),e2l)[this.q.getDay()]+" "+e2f[this.q.getMonth()]+" "+Tt(this.q.getDate())+" "+Tt(this.q.getHours())+":"+Tt(this.q.getMinutes())+":"+Tt(this.q.getSeconds())+" GMT"+e+t+" "+this.q.getFullYear()};var e1Q=Y5(eUS,"Date",199);eTS(1915,199,eHB,evI),eUe.a=!1,eUe.b=0,eUe.c=0,eUe.d=0,eUe.e=0,eUe.f=0,eUe.g=!1,eUe.i=0,eUe.j=0,eUe.k=0,eUe.n=0,eUe.o=0,eUe.p=0,Y5("com.google.gwt.i18n.shared.impl","DateRecord",1915),eTS(1966,1,{}),eUe.fe=function(){return null},eUe.ge=function(){return null},eUe.he=function(){return null},eUe.ie=function(){return null},eUe.je=function(){return null},Y5(eHU,"JSONValue",1966),eTS(216,1966,{216:1},lN,lL),eUe.Fb=function(e){return!!M4(e,216)&&W$(this.a,Pp(e,216).a)},eUe.ee=function(){return be},eUe.Hb=function(){return $n(this.a)},eUe.fe=function(){return this},eUe.Ib=function(){var e,t,n;for(t=0,n=new O0("["),e=this.a.length;t0&&(n.a+=","),xT(n,eep(this,t));return n.a+="]",n.a},Y5(eHU,"JSONArray",216),eTS(483,1966,{483:1},lC),eUe.ee=function(){return bt},eUe.ge=function(){return this},eUe.Ib=function(){return OQ(),""+this.a},eUe.a=!1,Y5(eHU,"JSONBoolean",483),eTS(985,60,eHr,gs),Y5(eHU,"JSONException",985),eTS(1023,1966,{},g),eUe.ee=function(){return bo},eUe.Ib=function(){return eUg},Y5(eHU,"JSONNull",1023),eTS(258,1966,{258:1},lI),eUe.Fb=function(e){return!!M4(e,258)&&this.a==Pp(e,258).a},eUe.ee=function(){return bn},eUe.Hb=function(){return Ti(this.a)},eUe.he=function(){return this},eUe.Ib=function(){return this.a+""},eUe.a=0,Y5(eHU,"JSONNumber",258),eTS(183,1966,{183:1},gu,lD),eUe.Fb=function(e){return!!M4(e,183)&&W$(this.a,Pp(e,183).a)},eUe.ee=function(){return br},eUe.Hb=function(){return $n(this.a)},eUe.ie=function(){return this},eUe.Ib=function(){var e,t,n,r,i,a,o;for(r=0,o=new O0("{"),e=!0,i=(n=a=erG(this,Je(e17,eUP,2,0,6,1))).length;r=0?":"+this.c:"")+")"},eUe.c=0;var e18=Y5(eUc,"StackTraceElement",310);e0c={3:1,475:1,35:1,2:1};var e17=Y5(eUc,eHa,2);eTS(107,418,{475:1},vs,vu,O1),Y5(eUc,"StringBuffer",107),eTS(100,418,{475:1},vc,vl,O0),Y5(eUc,"StringBuilder",100),eTS(687,73,eHZ,vf),Y5(eUc,"StringIndexOutOfBoundsException",687),eTS(2043,1,{}),eTS(844,1,{},N),eUe.Kb=function(e){return Pp(e,78).e},Y5(eUc,"Throwable/lambda$0$Type",844),eTS(41,60,{3:1,102:1,60:1,78:1,41:1},bO,gW),Y5(eUc,"UnsupportedOperationException",41),eTS(240,236,{3:1,35:1,236:1,240:1},eew,yY),eUe.wd=function(e){return eDG(this,Pp(e,240))},eUe.ke=function(){return eEu(eRy(this))},eUe.Fb=function(e){var t;return this===e||!!M4(e,240)&&(t=Pp(e,240),this.e==t.e&&0==eDG(this,t))},eUe.Hb=function(){var e;return 0!=this.b?this.b:this.a<54?(e=eap(this.f),this.b=jE(WM(e,-1)),this.b=33*this.b+jE(WM(Fv(e,32),-1)),this.b=17*this.b+zy(this.e),this.b):(this.b=17*ect(this.c)+zy(this.e),this.b)},eUe.Ib=function(){return eRy(this)},eUe.a=0,eUe.b=0,eUe.d=0,eUe.e=0,eUe.f=0;var e0e=Y5("java.math","BigDecimal",240);eTS(91,236,{3:1,35:1,236:1,91:1},ep4,XE,F7,ey$,eh5,TU),eUe.wd=function(e){return ehI(this,Pp(e,91))},eUe.ke=function(){return eEu(eBw(this,0))},eUe.Fb=function(e){return ef6(this,e)},eUe.Hb=function(){return ect(this)},eUe.Ib=function(){return eBw(this,0)},eUe.b=-2,eUe.c=0,eUe.d=0,eUe.e=0;var e0t=Y5("java.math","BigInteger",91);eTS(488,1967,eUk),eUe.$b=function(){Yy(this)},eUe._b=function(e){return F9(this,e)},eUe.uc=function(e){return euo(this,e,this.g)||euo(this,e,this.f)},eUe.vc=function(){return new fS(this)},eUe.xc=function(e){return Bp(this,e)},eUe.zc=function(e,t){return Um(this,e,t)},eUe.Bc=function(e){return Z3(this,e)},eUe.gc=function(){return wq(this)},Y5(eUS,"AbstractHashMap",488),eTS(261,eUT,eUM,fS),eUe.$b=function(){this.a.$b()},eUe.Hc=function(e){return KN(this,e)},eUe.Kc=function(){return new esz(this.a)},eUe.Mc=function(e){var t;return!!KN(this,e)&&(t=Pp(e,42).cd(),this.a.Bc(t),!0)},eUe.gc=function(){return this.a.gc()},Y5(eUS,"AbstractHashMap/EntrySet",261),eTS(262,1,eUE,esz),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){return etz(this)},eUe.Ob=function(){return this.b},eUe.Qb=function(){JM(this)},eUe.b=!1,Y5(eUS,"AbstractHashMap/EntrySetIterator",262),eTS(417,1,eUE,fE),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return Et(this)},eUe.Pb=function(){return HL(this)},eUe.Qb=function(){BH(this)},eUe.b=0,eUe.c=-1,Y5(eUS,"AbstractList/IteratorImpl",417),eTS(96,417,eUC,KB),eUe.Qb=function(){BH(this)},eUe.Rb=function(e){CD(this,e)},eUe.Sb=function(){return this.b>0},eUe.Tb=function(){return this.b},eUe.Ub=function(){return A6(this.b>0),this.a.Xb(this.c=--this.b)},eUe.Vb=function(){return this.b-1},eUe.Wb=function(e){A4(-1!=this.c),this.a._c(this.c,e)},Y5(eUS,"AbstractList/ListIteratorImpl",96),eTS(219,52,eU5,Gz),eUe.Vc=function(e,t){Gp(e,this.b),this.c.Vc(this.a+e,t),++this.b},eUe.Xb=function(e){return GK(e,this.b),this.c.Xb(this.a+e)},eUe.$c=function(e){var t;return GK(e,this.b),t=this.c.$c(this.a+e),--this.b,t},eUe._c=function(e,t){return GK(e,this.b),this.c._c(this.a+e,t)},eUe.gc=function(){return this.b},eUe.a=0,eUe.b=0,Y5(eUS,"AbstractList/SubList",219),eTS(384,eUT,eUM,fk),eUe.$b=function(){this.a.$b()},eUe.Hc=function(e){return this.a._b(e)},eUe.Kc=function(){var e;return e=this.a.vc().Kc(),new fx(e)},eUe.Mc=function(e){return!!this.a._b(e)&&(this.a.Bc(e),!0)},eUe.gc=function(){return this.a.gc()},Y5(eUS,"AbstractMap/1",384),eTS(691,1,eUE,fx),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return this.a.Ob()},eUe.Pb=function(){var e;return(e=Pp(this.a.Pb(),42)).cd()},eUe.Qb=function(){this.a.Qb()},Y5(eUS,"AbstractMap/1/1",691),eTS(226,28,eUx,fT),eUe.$b=function(){this.a.$b()},eUe.Hc=function(e){return this.a.uc(e)},eUe.Kc=function(){var e;return e=this.a.vc().Kc(),new fN(e)},eUe.gc=function(){return this.a.gc()},Y5(eUS,"AbstractMap/2",226),eTS(294,1,eUE,fN),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return this.a.Ob()},eUe.Pb=function(){var e;return(e=Pp(this.a.Pb(),42)).dd()},eUe.Qb=function(){this.a.Qb()},Y5(eUS,"AbstractMap/2/1",294),eTS(484,1,{484:1,42:1}),eUe.Fb=function(e){var t;return!!M4(e,42)&&(t=Pp(e,42),UT(this.d,t.cd())&&UT(this.e,t.dd()))},eUe.cd=function(){return this.d},eUe.dd=function(){return this.e},eUe.Hb=function(){return TK(this.d)^TK(this.e)},eUe.ed=function(e){return CL(this,e)},eUe.Ib=function(){return this.d+"="+this.e},Y5(eUS,"AbstractMap/AbstractEntry",484),eTS(383,484,{484:1,383:1,42:1},EE),Y5(eUS,"AbstractMap/SimpleEntry",383),eTS(1984,1,e$t),eUe.Fb=function(e){var t;return!!M4(e,42)&&(t=Pp(e,42),UT(this.cd(),t.cd())&&UT(this.dd(),t.dd()))},eUe.Hb=function(){return TK(this.cd())^TK(this.dd())},eUe.Ib=function(){return this.cd()+"="+this.dd()},Y5(eUS,eUD,1984),eTS(1992,1967,eUO),eUe.tc=function(e){return ZO(this,e)},eUe._b=function(e){return IY(this,e)},eUe.vc=function(){return new fj(this)},eUe.xc=function(e){var t;return xu(esq(this,t=e))},eUe.ec=function(){return new fP(this)},Y5(eUS,"AbstractNavigableMap",1992),eTS(739,eUT,eUM,fj),eUe.Hc=function(e){return M4(e,42)&&ZO(this.b,Pp(e,42))},eUe.Kc=function(){return new C1(this.b)},eUe.Mc=function(e){var t;return!!M4(e,42)&&(t=Pp(e,42),Jl(this.b,t))},eUe.gc=function(){return this.b.c},Y5(eUS,"AbstractNavigableMap/EntrySet",739),eTS(493,eUT,eUL,fP),eUe.Nc=function(){return new Ec(this)},eUe.$b=function(){gl(this.a)},eUe.Hc=function(e){return IY(this.a,e)},eUe.Kc=function(){var e;return e=new C1(new Ap(this.a).b),new fR(e)},eUe.Mc=function(e){return!!IY(this.a,e)&&(zS(this.a,e),!0)},eUe.gc=function(){return this.a.c},Y5(eUS,"AbstractNavigableMap/NavigableKeySet",493),eTS(494,1,eUE,fR),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return Et(this.a.a)},eUe.Pb=function(){var e;return(e=AJ(this.a)).cd()},eUe.Qb=function(){I5(this.a)},Y5(eUS,"AbstractNavigableMap/NavigableKeySet/1",494),eTS(2004,28,eUx),eUe.Fc=function(e){return Ja(e_s(this,e)),!0},eUe.Gc=function(e){return BJ(e),PG(e!=this,"Can't add a queue to itself"),er7(this,e)},eUe.$b=function(){for(;null!=eev(this););},Y5(eUS,"AbstractQueue",2004),eTS(302,28,{4:1,20:1,28:1,14:1},p1,GZ),eUe.Fc=function(e){return Vy(this,e),!0},eUe.$b=function(){qr(this)},eUe.Hc=function(e){return eos(new UN(this),e)},eUe.dc=function(){return gY(this)},eUe.Kc=function(){return new UN(this)},eUe.Mc=function(e){return zP(new UN(this),e)},eUe.gc=function(){return this.c-this.b&this.a.length-1},eUe.Nc=function(){return new Gq(this,272)},eUe.Qc=function(e){var t;return t=this.c-this.b&this.a.length-1,e.lengtht&&Bc(e,t,null),e},eUe.b=0,eUe.c=0,Y5(eUS,"ArrayDeque",302),eTS(446,1,eUE,UN),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return this.a!=this.b},eUe.Pb=function(){return ecn(this)},eUe.Qb=function(){enP(this)},eUe.a=0,eUe.b=0,eUe.c=-1,Y5(eUS,"ArrayDeque/IteratorImpl",446),eTS(12,52,e$n,p0,XM,I4),eUe.Vc=function(e,t){jO(this,e,t)},eUe.Fc=function(e){return P_(this,e)},eUe.Wc=function(e,t){return euP(this,e,t)},eUe.Gc=function(e){return eoc(this,e)},eUe.$b=function(){this.c=Je(e1R,eUp,1,0,5,1)},eUe.Hc=function(e){return -1!=QI(this,e,0)},eUe.Jc=function(e){ety(this,e)},eUe.Xb=function(e){return RJ(this,e)},eUe.Xc=function(e){return QI(this,e,0)},eUe.dc=function(){return 0==this.c.length},eUe.Kc=function(){return new fz(this)},eUe.$c=function(e){return ZV(this,e)},eUe.Mc=function(e){return QA(this,e)},eUe.Ud=function(e,t){GG(this,e,t)},eUe._c=function(e,t){return q1(this,e,t)},eUe.gc=function(){return this.c.length},eUe.ad=function(e){Mv(this,e)},eUe.Pc=function(){return AW(this)},eUe.Qc=function(e){return epg(this,e)};var e0n=Y5(eUS,"ArrayList",12);eTS(7,1,eUE,fz),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return My(this)},eUe.Pb=function(){return Wx(this)},eUe.Qb=function(){Yv(this)},eUe.a=0,eUe.b=-1,Y5(eUS,"ArrayList/1",7),eTS(2013,eB4.Function,{},S),eUe.te=function(e,t){return elN(e,t)},eTS(154,52,e$r,g$),eUe.Hc=function(e){return -1!=enW(this,e)},eUe.Jc=function(e){var t,n,r,i;for(BJ(e),n=this.a,r=0,i=n.length;r>>0).toString(16))},eUe.f=0,eUe.i=eH1;var e2X=Y5(e$N,"CNode",57);eTS(814,1,{},b5),Y5(e$N,"CNode/CNodeBuilder",814),eTS(1525,1,{},eh),eUe.Oe=function(e,t){return 0},eUe.Pe=function(e,t){return 0},Y5(e$N,e$R,1525),eTS(1790,1,{},ep),eUe.Le=function(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b;for(c=eHQ,r=new fz(e.a.b);r.ar.d.c||r.d.c==a.d.c&&r.d.b0?e+this.n.d+this.n.a:0},eUe.Se=function(){var e,t,n,r,i;if(i=0,this.e)this.b?i=this.b.a:this.a[1][1]&&(i=this.a[1][1].Se());else if(this.g)i=efV(this,evf(this,null,!0));else for(t=(etx(),eow(vx(e26,1),eU4,232,0,[e3D,e3N,e3P])),n=0,r=t.length;n0?i+this.n.b+this.n.c:0},eUe.Te=function(){var e,t,n,r,i;if(this.g)for(e=evf(this,null,!1),n=(etx(),eow(vx(e26,1),eU4,232,0,[e3D,e3N,e3P])),r=0,i=n.length;r0&&(r[0]+=this.d,n-=r[0]),r[2]>0&&(r[2]+=this.d,n-=r[2]),this.c.a=eB4.Math.max(0,n),this.c.d=t.d+e.d+(this.c.a-n)/2,r[1]=eB4.Math.max(r[1],n),ZP(this,e3N,t.d+e.d+r[0]-(r[1]-n)/2,r)},eUe.b=null,eUe.d=0,eUe.e=!1,eUe.f=!1,eUe.g=!1;var e29=0,e28=0;Y5(e$9,"GridContainerCell",1473),eTS(461,22,{3:1,35:1,22:1,461:1},EY);var e27=enw(e$9,"HorizontalLabelAlignment",461,e1G,G1,Dc);eTS(306,212,{212:1,306:1},zf,etr,$Y),eUe.Re=function(){return Rf(this)},eUe.Se=function(){return Rd(this)},eUe.a=0,eUe.c=!1;var e3e=Y5(e$9,"LabelCell",306);eTS(244,326,{212:1,326:1,244:1},eh6),eUe.Re=function(){return ek1(this)},eUe.Se=function(){return ek0(this)},eUe.Te=function(){eNE(this)},eUe.Ue=function(){eNM(this)},eUe.b=0,eUe.c=0,eUe.d=!1,Y5(e$9,"StripContainerCell",244),eTS(1626,1,eU8,e_),eUe.Mb=function(e){return gU(Pp(e,212))},Y5(e$9,"StripContainerCell/lambda$0$Type",1626),eTS(1627,1,{},eE),eUe.Fe=function(e){return Pp(e,212).Se()},Y5(e$9,"StripContainerCell/lambda$1$Type",1627),eTS(1628,1,eU8,eS),eUe.Mb=function(e){return gH(Pp(e,212))},Y5(e$9,"StripContainerCell/lambda$2$Type",1628),eTS(1629,1,{},ek),eUe.Fe=function(e){return Pp(e,212).Re()},Y5(e$9,"StripContainerCell/lambda$3$Type",1629),eTS(462,22,{3:1,35:1,22:1,462:1},EB);var e3t=enw(e$9,"VerticalLabelAlignment",462,e1G,G0,Dl);eTS(789,1,{},eFQ),eUe.c=0,eUe.d=0,eUe.k=0,eUe.s=0,eUe.t=0,eUe.v=!1,eUe.w=0,eUe.D=!1,Y5(eza,"NodeContext",789),eTS(1471,1,e$C,ex),eUe.ue=function(e,t){return To(Pp(e,61),Pp(t,61))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eza,"NodeContext/0methodref$comparePortSides$Type",1471),eTS(1472,1,e$C,eT),eUe.ue=function(e,t){return ew9(Pp(e,111),Pp(t,111))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eza,"NodeContext/1methodref$comparePortContexts$Type",1472),eTS(159,22,{3:1,35:1,22:1,159:1},ei_);var e3n=enw(eza,"NodeLabelLocation",159,e1G,epE,Df);eTS(111,1,{111:1},exz),eUe.a=!1,Y5(eza,"PortContext",111),eTS(1476,1,eUF,eM),eUe.td=function(e){yQ(Pp(e,306))},Y5(ezu,ezc,1476),eTS(1477,1,eU8,eO),eUe.Mb=function(e){return!!Pp(e,111).c},Y5(ezu,ezl,1477),eTS(1478,1,eUF,eA),eUe.td=function(e){yQ(Pp(e,111).c)},Y5(ezu,"LabelPlacer/lambda$2$Type",1478),eTS(1475,1,eUF,eC),eUe.td=function(e){Cn(),bu(Pp(e,111))},Y5(ezu,"NodeLabelAndSizeUtilities/lambda$0$Type",1475),eTS(790,1,eUF,Dx),eUe.td=function(e){_H(this.b,this.c,this.a,Pp(e,181))},eUe.a=!1,eUe.c=!1,Y5(ezu,"NodeLabelCellCreator/lambda$0$Type",790),eTS(1474,1,eUF,db),eUe.td=function(e){bB(this.a,Pp(e,181))},Y5(ezu,"PortContextCreator/lambda$0$Type",1474),eTS(1829,1,{},eI),Y5(ezd,"GreedyRectangleStripOverlapRemover",1829),eTS(1830,1,e$C,eL),eUe.ue=function(e,t){return Ay(Pp(e,222),Pp(t,222))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezd,"GreedyRectangleStripOverlapRemover/0methodref$compareByYCoordinate$Type",1830),eTS(1786,1,{},me),eUe.a=5,eUe.e=0,Y5(ezd,"RectangleStripOverlapRemover",1786),eTS(1787,1,e$C,eN),eUe.ue=function(e,t){return Aw(Pp(e,222),Pp(t,222))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezd,"RectangleStripOverlapRemover/0methodref$compareLeftRectangleBorders$Type",1787),eTS(1789,1,e$C,eP),eUe.ue=function(e,t){return YY(Pp(e,222),Pp(t,222))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezd,"RectangleStripOverlapRemover/1methodref$compareRightRectangleBorders$Type",1789),eTS(406,22,{3:1,35:1,22:1,406:1},EU);var e3r=enw(ezd,"RectangleStripOverlapRemover/OverlapRemovalDirection",406,e1G,Vn,Dd);eTS(222,1,{222:1},jH),Y5(ezd,"RectangleStripOverlapRemover/RectangleNode",222),eTS(1788,1,eUF,dm),eUe.td=function(e){emA(this.a,Pp(e,222))},Y5(ezd,"RectangleStripOverlapRemover/lambda$1$Type",1788),eTS(1304,1,e$C,eR),eUe.ue=function(e,t){return eRu(Pp(e,167),Pp(t,167))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezp,"PolyominoCompactor/CornerCasesGreaterThanRestComparator",1304),eTS(1307,1,{},ej),eUe.Kb=function(e){return Pp(e,324).a},Y5(ezp,"PolyominoCompactor/CornerCasesGreaterThanRestComparator/lambda$0$Type",1307),eTS(1308,1,eU8,eF),eUe.Mb=function(e){return Pp(e,323).a},Y5(ezp,"PolyominoCompactor/CornerCasesGreaterThanRestComparator/lambda$1$Type",1308),eTS(1309,1,eU8,eY),eUe.Mb=function(e){return Pp(e,323).a},Y5(ezp,"PolyominoCompactor/CornerCasesGreaterThanRestComparator/lambda$2$Type",1309),eTS(1302,1,e$C,eB),eUe.ue=function(e,t){return eC9(Pp(e,167),Pp(t,167))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezp,"PolyominoCompactor/MinNumOfExtensionDirectionsComparator",1302),eTS(1305,1,{},eD),eUe.Kb=function(e){return Pp(e,324).a},Y5(ezp,"PolyominoCompactor/MinNumOfExtensionDirectionsComparator/lambda$0$Type",1305),eTS(767,1,e$C,eU),eUe.ue=function(e,t){return eaq(Pp(e,167),Pp(t,167))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezp,"PolyominoCompactor/MinNumOfExtensionsComparator",767),eTS(1300,1,e$C,eH),eUe.ue=function(e,t){return ery(Pp(e,321),Pp(t,321))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezp,"PolyominoCompactor/MinPerimeterComparator",1300),eTS(1301,1,e$C,e$),eUe.ue=function(e,t){return ebg(Pp(e,321),Pp(t,321))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezp,"PolyominoCompactor/MinPerimeterComparatorWithShape",1301),eTS(1303,1,e$C,ez),eUe.ue=function(e,t){return eIz(Pp(e,167),Pp(t,167))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezp,"PolyominoCompactor/SingleExtensionSideGreaterThanRestComparator",1303),eTS(1306,1,{},eG),eUe.Kb=function(e){return Pp(e,324).a},Y5(ezp,"PolyominoCompactor/SingleExtensionSideGreaterThanRestComparator/lambda$0$Type",1306),eTS(777,1,{},EC),eUe.Ce=function(e,t){return KG(this,Pp(e,46),Pp(t,167))},Y5(ezp,"SuccessorCombination",777),eTS(644,1,{},eW),eUe.Ce=function(e,t){var n;return exd((n=Pp(e,46),Pp(t,167),n))},Y5(ezp,"SuccessorJitter",644),eTS(643,1,{},eK),eUe.Ce=function(e,t){var n;return eAW((n=Pp(e,46),Pp(t,167),n))},Y5(ezp,"SuccessorLineByLine",643),eTS(568,1,{},eV),eUe.Ce=function(e,t){var n;return eMl((n=Pp(e,46),Pp(t,167),n))},Y5(ezp,"SuccessorManhattan",568),eTS(1356,1,{},eq),eUe.Ce=function(e,t){var n;return eAt((n=Pp(e,46),Pp(t,167),n))},Y5(ezp,"SuccessorMaxNormWindingInMathPosSense",1356),eTS(400,1,{},dg),eUe.Ce=function(e,t){return YO(this,e,t)},eUe.c=!1,eUe.d=!1,eUe.e=!1,eUe.f=!1,Y5(ezp,"SuccessorQuadrantsGeneric",400),eTS(1357,1,{},eZ),eUe.Kb=function(e){return Pp(e,324).a},Y5(ezp,"SuccessorQuadrantsGeneric/lambda$0$Type",1357),eTS(323,22,{3:1,35:1,22:1,323:1},EN),eUe.a=!1;var e3i=enw(ezy,ezw,323,e1G,Va,Dh);eTS(1298,1,{}),eUe.Ib=function(){var e,t,n,r,i,a;for(i=0,n=" ",e=ell(0);i=0?"b"+e+"["+q2(this.a)+"]":"b["+q2(this.a)+"]":"b_"+Ao(this)},Y5(ez0,"FBendpoint",559),eTS(282,134,{3:1,282:1,94:1,134:1},CH),eUe.Ib=function(){return q2(this)},Y5(ez0,"FEdge",282),eTS(231,134,{3:1,231:1,94:1,134:1},Z5);var e4_=Y5(ez0,"FGraph",231);eTS(447,357,{3:1,447:1,357:1,94:1,134:1},qp),eUe.Ib=function(){return null==this.b||0==this.b.length?"l["+q2(this.a)+"]":"l_"+this.b},Y5(ez0,"FLabel",447),eTS(144,357,{3:1,144:1,357:1,94:1,134:1},Bw),eUe.Ib=function(){return WH(this)},eUe.b=0,Y5(ez0,"FNode",144),eTS(2003,1,{}),eUe.bf=function(e){eD2(this,e)},eUe.cf=function(){emz(this)},eUe.d=0,Y5(ez3,"AbstractForceModel",2003),eTS(631,2003,{631:1},eaR),eUe.af=function(e,t){var n,r,i,a,o;return ekL(this.f,e,t),i=C6(MB(t.d),e.d),o=eB4.Math.sqrt(i.a*i.a+i.b*i.b),r=eB4.Math.max(0,o-B$(e.e)/2-B$(t.e)/2),a=(n=esT(this.e,e,t))>0?-YT(r,this.c)*n:Li(r,this.b)*Pp(e_k(e,(eCk(),e9M)),19).a,Ol(i,a/o),i},eUe.bf=function(e){eD2(this,e),this.a=Pp(e_k(e,(eCk(),e9g)),19).a,this.c=gP(LV(e_k(e,e9D))),this.b=gP(LV(e_k(e,e9A)))},eUe.df=function(e){return e0&&(a-=gg(r,this.a)*n),Ol(i,a*this.b/o),i},eUe.bf=function(e){var t,n,r,i,a,o,s;for(eD2(this,e),this.b=gP(LV(e_k(e,(eCk(),e9N)))),this.c=this.b/Pp(e_k(e,e9g),19).a,r=e.e.c.length,a=0,i=0,s=new fz(e.e);s.a0},eUe.a=0,eUe.b=0,eUe.c=0,Y5(ez3,"FruchtermanReingoldModel",632),eTS(849,1,e$2,cu),eUe.Qe=function(e){efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ez4),""),"Force Model"),"Determines the model for force calculation."),e9a),(eSd(),tdv)),e4E),el9((epx(),tdh))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ez5),""),"Iterations"),"The number of iterations on the force model."),ell(300)),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ez6),""),"Repulsive Power"),"Determines how many bend points are added to the edge; such bend points are regarded as repelling particles in the force model"),ell(0)),tdw),e15),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ez9),""),"FR Temperature"),"The temperature is used as a scaling factor for particle displacements."),ez8),tdg),e13),el9(tdh)))),K_(e,ez9,ez4,e9l),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ez7),""),"Eades Repulsion"),"Factor for repulsive forces in Eades' model."),5),tdg),e13),el9(tdh)))),K_(e,ez7,ez4,e9s),eYi((new cc,e))},Y5(eGe,"ForceMetaDataProvider",849),eTS(424,22,{3:1,35:1,22:1,424:1},EH);var e4E=enw(eGe,"ForceModelStrategy",424,e1G,$9,Dm);eTS(988,1,e$2,cc),eUe.Qe=function(e){eYi(e)},Y5(eGe,"ForceOptions",988),eTS(989,1,{},tr),eUe.$e=function(){return new b0},eUe._e=function(e){},Y5(eGe,"ForceOptions/ForceFactory",989),eTS(850,1,e$2,cl),eUe.Qe=function(e){efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGw),""),"Fixed Position"),"Prevent that the node is moved by the layout algorithm."),(OQ(),!1)),(eSd(),tdm)),e11),el9((epx(),tdd))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eG_),""),"Desired Edge Length"),"Either specified for parent nodes or for individual edges, where the latter takes higher precedence."),100),tdg),e13),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdl]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGE),""),"Layout Dimension"),"Dimensions that are permitted to be altered during layout."),e9U),tdv),e4S),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGS),""),"Stress Epsilon"),"Termination criterion for the iterative process."),ez8),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGk),""),"Iteration Limit"),"Maximum number of performed iterations. Takes higher precedence than 'epsilon'."),ell(eUu)),tdw),e15),el9(tdh)))),ejQ((new cf,e))},Y5(eGe,"StressMetaDataProvider",850),eTS(992,1,e$2,cf),eUe.Qe=function(e){ejQ(e)},Y5(eGe,"StressOptions",992),eTS(993,1,{},ti),eUe.$e=function(){return new C$},eUe._e=function(e){},Y5(eGe,"StressOptions/StressFactory",993),eTS(1128,209,ezL,C$),eUe.Ze=function(e,t){var n,r,i,a,o;for(ewG(t,eGT,1),gN(LK(eT8(e,(egq(),e9q))))?gN(LK(eT8(e,e90)))||zh(n=new df((_q(),new gM(e)))):eOs(new b0,e,eiI(t,1)),i=eo4(e),o=(r=eNx(this.a,i)).Kc();o.Ob();)!((a=Pp(o.Pb(),231)).e.c.length<=1)&&(eRa(this.b,a),eMn(this.b),ety(a.d,new ta));i=eYC(r),eYh(i),eEj(t)},Y5(eGO,"StressLayoutProvider",1128),eTS(1129,1,eUF,ta),eUe.td=function(e){ePd(Pp(e,447))},Y5(eGO,"StressLayoutProvider/lambda$0$Type",1129),eTS(990,1,{},bP),eUe.c=0,eUe.e=0,eUe.g=0,Y5(eGO,"StressMajorization",990),eTS(379,22,{3:1,35:1,22:1,379:1},E$);var e4S=enw(eGO,"StressMajorization/Dimension",379,e1G,G3,Dg);eTS(991,1,e$C,dE),eUe.ue=function(e,t){return IA(this.a,Pp(e,144),Pp(t,144))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGO,"StressMajorization/lambda$0$Type",991),eTS(1229,1,{},W9),Y5(eGL,"ElkLayered",1229),eTS(1230,1,eUF,to),eUe.td=function(e){exn(Pp(e,37))},Y5(eGL,"ElkLayered/lambda$0$Type",1230),eTS(1231,1,eUF,dS),eUe.td=function(e){IL(this.a,Pp(e,37))},Y5(eGL,"ElkLayered/lambda$1$Type",1231),eTS(1263,1,{},MC),Y5(eGL,"GraphConfigurator",1263),eTS(759,1,eUF,dk),eUe.td=function(e){e_1(this.a,Pp(e,10))},Y5(eGL,"GraphConfigurator/lambda$0$Type",759),eTS(760,1,{},ts),eUe.Kb=function(e){return evR(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eGL,"GraphConfigurator/lambda$1$Type",760),eTS(761,1,eUF,dx),eUe.td=function(e){e_1(this.a,Pp(e,10))},Y5(eGL,"GraphConfigurator/lambda$2$Type",761),eTS(1127,209,ezL,b3),eUe.Ze=function(e,t){var n;n=eN7(new mn,e),xc(eT8(e,(eBy(),taM)))===xc((eck(),tpz))?ef0(this.a,n,t):exD(this.a,n,t),eYr(new ch,n)},Y5(eGL,"LayeredLayoutProvider",1127),eTS(356,22,{3:1,35:1,22:1,356:1},Ez);var e4k=enw(eGL,"LayeredPhases",356,e1G,q4,Dv);eTS(1651,1,{},enX),eUe.i=0,Y5(eGC,"ComponentsToCGraphTransformer",1651),eTS(1652,1,{},tu),eUe.ef=function(e,t){return eB4.Math.min(null!=e.a?gP(e.a):e.c.i,null!=t.a?gP(t.a):t.c.i)},eUe.ff=function(e,t){return eB4.Math.min(null!=e.a?gP(e.a):e.c.i,null!=t.a?gP(t.a):t.c.i)},Y5(eGC,"ComponentsToCGraphTransformer/1",1652),eTS(81,1,{81:1}),eUe.i=0,eUe.k=!0,eUe.o=eH1;var e4x=Y5(eGI,"CNode",81);eTS(460,81,{460:1,81:1},Ah,eh3),eUe.Ib=function(){return""},Y5(eGC,"ComponentsToCGraphTransformer/CRectNode",460),eTS(1623,1,{},tc),Y5(eGC,"OneDimensionalComponentsCompaction",1623),eTS(1624,1,{},tl),eUe.Kb=function(e){return Gm(Pp(e,46))},eUe.Fb=function(e){return this===e},Y5(eGC,"OneDimensionalComponentsCompaction/lambda$0$Type",1624),eTS(1625,1,{},tf),eUe.Kb=function(e){return edl(Pp(e,46))},eUe.Fb=function(e){return this===e},Y5(eGC,"OneDimensionalComponentsCompaction/lambda$1$Type",1625),eTS(1654,1,{},Bv),Y5(eGI,"CGraph",1654),eTS(189,1,{189:1},eh4),eUe.b=0,eUe.c=0,eUe.e=0,eUe.g=!0,eUe.i=eH1,Y5(eGI,"CGroup",189),eTS(1653,1,{},tb),eUe.ef=function(e,t){return eB4.Math.max(null!=e.a?gP(e.a):e.c.i,null!=t.a?gP(t.a):t.c.i)},eUe.ff=function(e,t){return eB4.Math.max(null!=e.a?gP(e.a):e.c.i,null!=t.a?gP(t.a):t.c.i)},Y5(eGI,e$R,1653),eTS(1655,1,{},exO),eUe.d=!1;var e4T=Y5(eGI,e$U,1655);eTS(1656,1,{},tm),eUe.Kb=function(e){return _T(),OQ(),0!=Pp(Pp(e,46).a,81).d.e},eUe.Fb=function(e){return this===e},Y5(eGI,e$H,1656),eTS(823,1,{},R$),eUe.a=!1,eUe.b=!1,eUe.c=!1,eUe.d=!1,Y5(eGI,e$$,823),eTS(1825,1,{},j$),Y5(eGD,e$z,1825);var e4M=RL(eGN,e$D);eTS(1826,1,{369:1},$h),eUe.Ke=function(e){eLh(this,Pp(e,466))},Y5(eGD,e$G,1826),eTS(1827,1,e$C,tg),eUe.ue=function(e,t){return Hy(Pp(e,81),Pp(t,81))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGD,e$W,1827),eTS(466,1,{466:1},E6),eUe.a=!1,Y5(eGD,e$K,466),eTS(1828,1,e$C,tv),eUe.ue=function(e,t){return evP(Pp(e,466),Pp(t,466))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGD,e$V,1828),eTS(140,1,{140:1},Se,PW),eUe.Fb=function(e){var t;return null!=e&&e4O==esF(e)&&(t=Pp(e,140),UT(this.c,t.c)&&UT(this.d,t.d))},eUe.Hb=function(){return euF(eow(vx(e1R,1),eUp,1,5,[this.c,this.d]))},eUe.Ib=function(){return"("+this.c+eUd+this.d+(this.a?"cx":"")+this.b+")"},eUe.a=!0,eUe.c=0,eUe.d=0;var e4O=Y5(eGN,"Point",140);eTS(405,22,{3:1,35:1,22:1,405:1},EG);var e4A=enw(eGN,"Point/Quadrant",405,e1G,Vo,Dy);eTS(1642,1,{},b6),eUe.b=null,eUe.c=null,eUe.d=null,eUe.e=null,eUe.f=null,Y5(eGN,"RectilinearConvexHull",1642),eTS(574,1,{369:1},epG),eUe.Ke=function(e){J4(this,Pp(e,140))},eUe.b=0,Y5(eGN,"RectilinearConvexHull/MaximalElementsEventHandler",574),eTS(1644,1,e$C,th),eUe.ue=function(e,t){return U3(LV(e),LV(t))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGN,"RectilinearConvexHull/MaximalElementsEventHandler/lambda$0$Type",1644),eTS(1643,1,{369:1},ete),eUe.Ke=function(e){eAo(this,Pp(e,140))},eUe.a=0,eUe.b=null,eUe.c=null,eUe.d=null,eUe.e=null,Y5(eGN,"RectilinearConvexHull/RectangleEventHandler",1643),eTS(1645,1,e$C,tp),eUe.ue=function(e,t){return WI(Pp(e,140),Pp(t,140))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGN,"RectilinearConvexHull/lambda$0$Type",1645),eTS(1646,1,e$C,td),eUe.ue=function(e,t){return WD(Pp(e,140),Pp(t,140))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGN,"RectilinearConvexHull/lambda$1$Type",1646),eTS(1647,1,e$C,ty),eUe.ue=function(e,t){return WP(Pp(e,140),Pp(t,140))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGN,"RectilinearConvexHull/lambda$2$Type",1647),eTS(1648,1,e$C,tw),eUe.ue=function(e,t){return WN(Pp(e,140),Pp(t,140))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGN,"RectilinearConvexHull/lambda$3$Type",1648),eTS(1649,1,e$C,t_),eUe.ue=function(e,t){return e_M(Pp(e,140),Pp(t,140))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGN,"RectilinearConvexHull/lambda$4$Type",1649),eTS(1650,1,{},Gf),Y5(eGN,"Scanline",1650),eTS(2005,1,{}),Y5(eGP,"AbstractGraphPlacer",2005),eTS(325,1,{325:1},Lm),eUe.mf=function(e){return!!this.nf(e)&&(exg(this.b,Pp(e_k(e,(eBU(),ttX)),21),e),!0)},eUe.nf=function(e){var t,n,r,i;for(t=Pp(e_k(e,(eBU(),ttX)),21),r=(i=Pp(Zq(e8E,t),21)).Kc();r.Ob();)if(n=Pp(r.Pb(),21),!Pp(Zq(this.b,n),15).dc())return!1;return!0},Y5(eGP,"ComponentGroup",325),eTS(765,2005,{},b9),eUe.of=function(e){var t,n;for(n=new fz(this.a);n.ah&&(_=0,E+=d+i,d=0),m=o.c,eIn(o,_+m.a,E+m.b),xB(m),n=eB4.Math.max(n,_+v.a),d=eB4.Math.max(d,v.b),_+=v.a+i;if(t.f.a=n,t.f.b=E+d,gN(LK(e_k(a,tiQ)))){for(eBb(r=new tE,e,i),f=e.Kc();f.Ob();)C5(xB((l=Pp(f.Pb(),37)).c),r.e);C5(xB(t.f),r.a)}JN(t,e)},Y5(eGP,"SimpleRowGraphPlacer",1291),eTS(1292,1,e$C,tx),eUe.ue=function(e,t){return eaV(Pp(e,37),Pp(t,37))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGP,"SimpleRowGraphPlacer/1",1292),eTS(1262,1,e$q,tT),eUe.Lb=function(e){var t;return!!(t=Pp(e_k(Pp(e,243).b,(eBy(),taR)),74))&&0!=t.b},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){var t;return!!(t=Pp(e_k(Pp(e,243).b,(eBy(),taR)),74))&&0!=t.b},Y5(eGY,"CompoundGraphPostprocessor/1",1262),eTS(1261,1,eGB,mr),eUe.pf=function(e,t){ebL(this,Pp(e,37),t)},Y5(eGY,"CompoundGraphPreprocessor",1261),eTS(441,1,{441:1},ec8),eUe.c=!1,Y5(eGY,"CompoundGraphPreprocessor/ExternalPort",441),eTS(243,1,{243:1},DT),eUe.Ib=function(){return AV(this.c)+":"+ek5(this.b)},Y5(eGY,"CrossHierarchyEdge",243),eTS(763,1,e$C,dT),eUe.ue=function(e,t){return egB(this,Pp(e,243),Pp(t,243))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eGY,"CrossHierarchyEdgeComparator",763),eTS(299,134,{3:1,299:1,94:1,134:1}),eUe.p=0,Y5(eGU,"LGraphElement",299),eTS(17,299,{3:1,17:1,299:1,94:1,134:1},$b),eUe.Ib=function(){return ek5(this)};var e4C=Y5(eGU,"LEdge",17);eTS(37,299,{3:1,20:1,37:1,299:1,94:1,134:1},enJ),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){return new fz(this.b)},eUe.Ib=function(){return 0==this.b.c.length?"G-unlayered"+e_F(this.a):0==this.a.c.length?"G-layered"+e_F(this.b):"G[layerless"+e_F(this.a)+", layers"+e_F(this.b)+"]"};var e4I=Y5(eGU,"LGraph",37);eTS(657,1,{}),eUe.qf=function(){return this.e.n},eUe.We=function(e){return e_k(this.e,e)},eUe.rf=function(){return this.e.o},eUe.sf=function(){return this.e.p},eUe.Xe=function(e){return Ln(this.e,e)},eUe.tf=function(e){this.e.n.a=e.a,this.e.n.b=e.b},eUe.uf=function(e){this.e.o.a=e.a,this.e.o.b=e.b},eUe.vf=function(e){this.e.p=e},Y5(eGU,"LGraphAdapters/AbstractLShapeAdapter",657),eTS(577,1,{839:1},dM),eUe.wf=function(){var e,t;if(!this.b)for(this.b=AH(this.a.b.c.length),t=new fz(this.a.b);t.a0&&eu7((GV(t-1,e.length),e.charCodeAt(t-1)),eGq);)--t;if(a> ",e),egu(n)),xM(xT((e.a+="[",e),n.i),"]")),e.a},eUe.c=!0,eUe.d=!1;var e4j=Y5(eGU,"LPort",11);eTS(397,1,eU$,dA),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){var e;return e=new fz(this.a.e),new dL(e)},Y5(eGU,"LPort/1",397),eTS(1290,1,eUE,dL),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){return Pp(Wx(this.a),17).c},eUe.Ob=function(){return My(this.a)},eUe.Qb=function(){Yv(this.a)},Y5(eGU,"LPort/1/1",1290),eTS(359,1,eU$,dC),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){var e;return e=new fz(this.a.g),new dI(e)},Y5(eGU,"LPort/2",359),eTS(762,1,eUE,dI),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){return Pp(Wx(this.a),17).d},eUe.Ob=function(){return My(this.a)},eUe.Qb=function(){Yv(this.a)},Y5(eGU,"LPort/2/1",762),eTS(1283,1,eU$,E5),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){return new Z4(this)},Y5(eGU,"LPort/CombineIter",1283),eTS(201,1,eUE,Z4),eUe.Nb=function(e){F8(this,e)},eUe.Qb=function(){yI()},eUe.Ob=function(){return Ak(this)},eUe.Pb=function(){return My(this.a)?Wx(this.a):Wx(this.b)},Y5(eGU,"LPort/CombineIter/1",201),eTS(1285,1,e$q,tA),eUe.Lb=function(e){return FO(e)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eiA(),0!=Pp(e,11).e.c.length},Y5(eGU,"LPort/lambda$0$Type",1285),eTS(1284,1,e$q,tL),eUe.Lb=function(e){return FA(e)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eiA(),0!=Pp(e,11).g.c.length},Y5(eGU,"LPort/lambda$1$Type",1284),eTS(1286,1,e$q,tC),eUe.Lb=function(e){return eiA(),Pp(e,11).j==(eYu(),tbw)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eiA(),Pp(e,11).j==(eYu(),tbw)},Y5(eGU,"LPort/lambda$2$Type",1286),eTS(1287,1,e$q,tI),eUe.Lb=function(e){return eiA(),Pp(e,11).j==(eYu(),tby)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eiA(),Pp(e,11).j==(eYu(),tby)},Y5(eGU,"LPort/lambda$3$Type",1287),eTS(1288,1,e$q,tD),eUe.Lb=function(e){return eiA(),Pp(e,11).j==(eYu(),tbj)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eiA(),Pp(e,11).j==(eYu(),tbj)},Y5(eGU,"LPort/lambda$4$Type",1288),eTS(1289,1,e$q,tN),eUe.Lb=function(e){return eiA(),Pp(e,11).j==(eYu(),tbY)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eiA(),Pp(e,11).j==(eYu(),tbY)},Y5(eGU,"LPort/lambda$5$Type",1289),eTS(29,299,{3:1,20:1,299:1,29:1,94:1,134:1},By),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){return new fz(this.a)},eUe.Ib=function(){return"L_"+QI(this.b.b,this,0)+e_F(this.a)},Y5(eGU,"Layer",29),eTS(1342,1,{},mn),Y5(eG0,eG2,1342),eTS(1346,1,{},tP),eUe.Kb=function(e){return ewH(Pp(e,82))},Y5(eG0,"ElkGraphImporter/0methodref$connectableShapeToNode$Type",1346),eTS(1349,1,{},tR),eUe.Kb=function(e){return ewH(Pp(e,82))},Y5(eG0,"ElkGraphImporter/1methodref$connectableShapeToNode$Type",1349),eTS(1343,1,eUF,dD),eUe.td=function(e){exW(this.a,Pp(e,118))},Y5(eG0,eG3,1343),eTS(1344,1,eUF,dN),eUe.td=function(e){exW(this.a,Pp(e,118))},Y5(eG0,eG4,1344),eTS(1345,1,{},tj),eUe.Kb=function(e){return new R1(null,new Gq(UF(Pp(e,79)),16))},Y5(eG0,eG5,1345),eTS(1347,1,eU8,dP),eUe.Mb=function(e){return TV(this.a,Pp(e,33))},Y5(eG0,eG6,1347),eTS(1348,1,{},tF),eUe.Kb=function(e){return new R1(null,new Gq(UY(Pp(e,79)),16))},Y5(eG0,"ElkGraphImporter/lambda$5$Type",1348),eTS(1350,1,eU8,dR),eUe.Mb=function(e){return Tq(this.a,Pp(e,33))},Y5(eG0,"ElkGraphImporter/lambda$7$Type",1350),eTS(1351,1,eU8,tY),eUe.Mb=function(e){return HH(Pp(e,79))},Y5(eG0,"ElkGraphImporter/lambda$8$Type",1351),eTS(1278,1,{},ch),Y5(eG0,"ElkGraphLayoutTransferrer",1278),eTS(1279,1,eU8,dj),eUe.Mb=function(e){return It(this.a,Pp(e,17))},Y5(eG0,"ElkGraphLayoutTransferrer/lambda$0$Type",1279),eTS(1280,1,eUF,dF),eUe.td=function(e){_k(),P_(this.a,Pp(e,17))},Y5(eG0,"ElkGraphLayoutTransferrer/lambda$1$Type",1280),eTS(1281,1,eU8,dY),eUe.Mb=function(e){return Ca(this.a,Pp(e,17))},Y5(eG0,"ElkGraphLayoutTransferrer/lambda$2$Type",1281),eTS(1282,1,eUF,dB),eUe.td=function(e){_k(),P_(this.a,Pp(e,17))},Y5(eG0,"ElkGraphLayoutTransferrer/lambda$3$Type",1282),eTS(1485,1,eGB,tB),eUe.pf=function(e,t){eiu(Pp(e,37),t)},Y5(eG8,"CommentNodeMarginCalculator",1485),eTS(1486,1,{},tU),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"CommentNodeMarginCalculator/lambda$0$Type",1486),eTS(1487,1,eUF,tH),eUe.td=function(e){ePO(Pp(e,10))},Y5(eG8,"CommentNodeMarginCalculator/lambda$1$Type",1487),eTS(1488,1,eGB,t$),eUe.pf=function(e,t){eLA(Pp(e,37),t)},Y5(eG8,"CommentPostprocessor",1488),eTS(1489,1,eGB,tz),eUe.pf=function(e,t){eF4(Pp(e,37),t)},Y5(eG8,"CommentPreprocessor",1489),eTS(1490,1,eGB,tG),eUe.pf=function(e,t){eOf(Pp(e,37),t)},Y5(eG8,"ConstraintsPostprocessor",1490),eTS(1491,1,eGB,tW),eUe.pf=function(e,t){eau(Pp(e,37),t)},Y5(eG8,"EdgeAndLayerConstraintEdgeReverser",1491),eTS(1492,1,eGB,tK),eUe.pf=function(e,t){edC(Pp(e,37),t)},Y5(eG8,"EndLabelPostprocessor",1492),eTS(1493,1,{},tV),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"EndLabelPostprocessor/lambda$0$Type",1493),eTS(1494,1,eU8,tq),eUe.Mb=function(e){return $T(Pp(e,10))},Y5(eG8,"EndLabelPostprocessor/lambda$1$Type",1494),eTS(1495,1,eUF,tZ),eUe.td=function(e){evj(Pp(e,10))},Y5(eG8,"EndLabelPostprocessor/lambda$2$Type",1495),eTS(1496,1,eGB,tX),eUe.pf=function(e,t){eSF(Pp(e,37),t)},Y5(eG8,"EndLabelPreprocessor",1496),eTS(1497,1,{},tJ),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"EndLabelPreprocessor/lambda$0$Type",1497),eTS(1498,1,eUF,DA),eUe.td=function(e){_$(this.a,this.b,this.c,Pp(e,10))},eUe.a=0,eUe.b=0,eUe.c=!1,Y5(eG8,"EndLabelPreprocessor/lambda$1$Type",1498),eTS(1499,1,eU8,tQ),eUe.Mb=function(e){return xc(e_k(Pp(e,70),(eBy(),tab)))===xc((etT(),tpS))},Y5(eG8,"EndLabelPreprocessor/lambda$2$Type",1499),eTS(1500,1,eUF,dU),eUe.td=function(e){P7(this.a,Pp(e,70))},Y5(eG8,"EndLabelPreprocessor/lambda$3$Type",1500),eTS(1501,1,eU8,t1),eUe.Mb=function(e){return xc(e_k(Pp(e,70),(eBy(),tab)))===xc((etT(),tpE))},Y5(eG8,"EndLabelPreprocessor/lambda$4$Type",1501),eTS(1502,1,eUF,dH),eUe.td=function(e){P7(this.a,Pp(e,70))},Y5(eG8,"EndLabelPreprocessor/lambda$5$Type",1502),eTS(1551,1,eGB,cd),eUe.pf=function(e,t){elP(Pp(e,37),t)},Y5(eG8,"EndLabelSorter",1551),eTS(1552,1,e$C,t0),eUe.ue=function(e,t){return epc(Pp(e,456),Pp(t,456))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"EndLabelSorter/1",1552),eTS(456,1,{456:1},HP),Y5(eG8,"EndLabelSorter/LabelGroup",456),eTS(1553,1,{},t2),eUe.Kb=function(e){return _O(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"EndLabelSorter/lambda$0$Type",1553),eTS(1554,1,eU8,t3),eUe.Mb=function(e){return _O(),Pp(e,10).k==(eEn(),e8N)},Y5(eG8,"EndLabelSorter/lambda$1$Type",1554),eTS(1555,1,eUF,t4),eUe.td=function(e){eEr(Pp(e,10))},Y5(eG8,"EndLabelSorter/lambda$2$Type",1555),eTS(1556,1,eU8,t5),eUe.Mb=function(e){return _O(),xc(e_k(Pp(e,70),(eBy(),tab)))===xc((etT(),tpE))},Y5(eG8,"EndLabelSorter/lambda$3$Type",1556),eTS(1557,1,eU8,t6),eUe.Mb=function(e){return _O(),xc(e_k(Pp(e,70),(eBy(),tab)))===xc((etT(),tpS))},Y5(eG8,"EndLabelSorter/lambda$4$Type",1557),eTS(1503,1,eGB,t9),eUe.pf=function(e,t){eP2(this,Pp(e,37))},eUe.b=0,eUe.c=0,Y5(eG8,"FinalSplineBendpointsCalculator",1503),eTS(1504,1,{},t8),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"FinalSplineBendpointsCalculator/lambda$0$Type",1504),eTS(1505,1,{},t7),eUe.Kb=function(e){return new R1(null,new YI(new Fa(OH(efc(Pp(e,10)).a.Kc(),new c))))},Y5(eG8,"FinalSplineBendpointsCalculator/lambda$1$Type",1505),eTS(1506,1,eU8,ne),eUe.Mb=function(e){return!q8(Pp(e,17))},Y5(eG8,"FinalSplineBendpointsCalculator/lambda$2$Type",1506),eTS(1507,1,eU8,nt),eUe.Mb=function(e){return Ln(Pp(e,17),(eBU(),tnO))},Y5(eG8,"FinalSplineBendpointsCalculator/lambda$3$Type",1507),eTS(1508,1,eUF,d$),eUe.td=function(e){eIV(this.a,Pp(e,128))},Y5(eG8,"FinalSplineBendpointsCalculator/lambda$4$Type",1508),eTS(1509,1,eUF,nn),eUe.td=function(e){eSj(Pp(e,17).a)},Y5(eG8,"FinalSplineBendpointsCalculator/lambda$5$Type",1509),eTS(792,1,eGB,dz),eUe.pf=function(e,t){ejn(this,Pp(e,37),t)},Y5(eG8,"GraphTransformer",792),eTS(511,22,{3:1,35:1,22:1,511:1},EV);var e4F=enw(eG8,"GraphTransformer/Mode",511,e1G,$8,NF);eTS(1510,1,eGB,nr),eUe.pf=function(e,t){eAP(Pp(e,37),t)},Y5(eG8,"HierarchicalNodeResizingProcessor",1510),eTS(1511,1,eGB,ni),eUe.pf=function(e,t){erP(Pp(e,37),t)},Y5(eG8,"HierarchicalPortConstraintProcessor",1511),eTS(1512,1,e$C,na),eUe.ue=function(e,t){return epZ(Pp(e,10),Pp(t,10))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"HierarchicalPortConstraintProcessor/NodeComparator",1512),eTS(1513,1,eGB,no),eUe.pf=function(e,t){eN5(Pp(e,37),t)},Y5(eG8,"HierarchicalPortDummySizeProcessor",1513),eTS(1514,1,eGB,ns),eUe.pf=function(e,t){eCf(this,Pp(e,37),t)},eUe.a=0,Y5(eG8,"HierarchicalPortOrthogonalEdgeRouter",1514),eTS(1515,1,e$C,nu),eUe.ue=function(e,t){return Av(Pp(e,10),Pp(t,10))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"HierarchicalPortOrthogonalEdgeRouter/1",1515),eTS(1516,1,e$C,nc),eUe.ue=function(e,t){return JW(Pp(e,10),Pp(t,10))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"HierarchicalPortOrthogonalEdgeRouter/2",1516),eTS(1517,1,eGB,nl),eUe.pf=function(e,t){e_O(Pp(e,37),t)},Y5(eG8,"HierarchicalPortPositionProcessor",1517),eTS(1518,1,eGB,cp),eUe.pf=function(e,t){eYG(this,Pp(e,37))},eUe.a=0,eUe.c=0,Y5(eG8,"HighDegreeNodeLayeringProcessor",1518),eTS(571,1,{571:1},nf),eUe.b=-1,eUe.d=-1,Y5(eG8,"HighDegreeNodeLayeringProcessor/HighDegreeNodeInformation",571),eTS(1519,1,{},nd),eUe.Kb=function(e){return DR(),efu(Pp(e,10))},eUe.Fb=function(e){return this===e},Y5(eG8,"HighDegreeNodeLayeringProcessor/lambda$0$Type",1519),eTS(1520,1,{},nh),eUe.Kb=function(e){return DR(),efc(Pp(e,10))},eUe.Fb=function(e){return this===e},Y5(eG8,"HighDegreeNodeLayeringProcessor/lambda$1$Type",1520),eTS(1526,1,eGB,np),eUe.pf=function(e,t){eD8(this,Pp(e,37),t)},Y5(eG8,"HyperedgeDummyMerger",1526),eTS(793,1,{},DL),eUe.a=!1,eUe.b=!1,eUe.c=!1,Y5(eG8,"HyperedgeDummyMerger/MergeState",793),eTS(1527,1,{},nb),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"HyperedgeDummyMerger/lambda$0$Type",1527),eTS(1528,1,{},nm),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,10).j,16))},Y5(eG8,"HyperedgeDummyMerger/lambda$1$Type",1528),eTS(1529,1,eUF,ng),eUe.td=function(e){Pp(e,11).p=-1},Y5(eG8,"HyperedgeDummyMerger/lambda$2$Type",1529),eTS(1530,1,eGB,nv),eUe.pf=function(e,t){eD6(Pp(e,37),t)},Y5(eG8,"HypernodesProcessor",1530),eTS(1531,1,eGB,ny),eUe.pf=function(e,t){eD9(Pp(e,37),t)},Y5(eG8,"InLayerConstraintProcessor",1531),eTS(1532,1,eGB,nw),eUe.pf=function(e,t){eiW(Pp(e,37),t)},Y5(eG8,"InnermostNodeMarginCalculator",1532),eTS(1533,1,eGB,n_),eUe.pf=function(e,t){eFW(this,Pp(e,37))},eUe.a=eH1,eUe.b=eH1,eUe.c=eHQ,eUe.d=eHQ;var e4Y=Y5(eG8,"InteractiveExternalPortPositioner",1533);eTS(1534,1,{},nE),eUe.Kb=function(e){return Pp(e,17).d.i},eUe.Fb=function(e){return this===e},Y5(eG8,"InteractiveExternalPortPositioner/lambda$0$Type",1534),eTS(1535,1,{},dG),eUe.Kb=function(e){return AE(this.a,LV(e))},eUe.Fb=function(e){return this===e},Y5(eG8,"InteractiveExternalPortPositioner/lambda$1$Type",1535),eTS(1536,1,{},nS),eUe.Kb=function(e){return Pp(e,17).c.i},eUe.Fb=function(e){return this===e},Y5(eG8,"InteractiveExternalPortPositioner/lambda$2$Type",1536),eTS(1537,1,{},dW),eUe.Kb=function(e){return AS(this.a,LV(e))},eUe.Fb=function(e){return this===e},Y5(eG8,"InteractiveExternalPortPositioner/lambda$3$Type",1537),eTS(1538,1,{},dK),eUe.Kb=function(e){return C9(this.a,LV(e))},eUe.Fb=function(e){return this===e},Y5(eG8,"InteractiveExternalPortPositioner/lambda$4$Type",1538),eTS(1539,1,{},dV),eUe.Kb=function(e){return C8(this.a,LV(e))},eUe.Fb=function(e){return this===e},Y5(eG8,"InteractiveExternalPortPositioner/lambda$5$Type",1539),eTS(77,22,{3:1,35:1,22:1,77:1,234:1},Eq),eUe.Kf=function(){switch(this.g){case 15:return new iA;case 22:return new iL;case 47:return new iD;case 28:case 35:return new nN;case 32:return new tB;case 42:return new t$;case 1:return new tz;case 41:return new tG;case 56:return new dz((erq(),e8W));case 0:return new dz((erq(),e8G));case 2:return new tW;case 54:return new tK;case 33:return new tX;case 51:return new t9;case 55:return new nr;case 13:return new ni;case 38:return new no;case 44:return new ns;case 40:return new nl;case 9:return new cp;case 49:return new AU;case 37:return new np;case 43:return new nv;case 27:return new ny;case 30:return new nw;case 3:return new n_;case 18:return new nx;case 29:return new nT;case 5:return new cb;case 50:return new nk;case 34:return new cm;case 36:return new nP;case 52:return new cd;case 11:return new nj;case 7:return new cv;case 39:return new nF;case 45:return new nY;case 16:return new nB;case 10:return new nU;case 48:return new n$;case 21:return new nz;case 23:return new gx((enU(),tui));case 8:return new nW;case 12:return new nV;case 4:return new nq;case 19:return new cE;case 17:return new n5;case 53:return new n6;case 6:return new rc;case 25:return new ms;case 46:return new rn;case 31:return new CV;case 14:return new rg;case 26:return new iB;case 20:return new rE;case 24:return new gx((enU(),tua));default:throw p7(new gL(eWt+(null!=this.f?this.f:""+this.g)))}};var e4B=enw(eG8,eWn,77,e1G,eAn,Nj);eTS(1540,1,eGB,nx),eUe.pf=function(e,t){eFq(Pp(e,37),t)},Y5(eG8,"InvertedPortProcessor",1540),eTS(1541,1,eGB,nT),eUe.pf=function(e,t){eIR(Pp(e,37),t)},Y5(eG8,"LabelAndNodeSizeProcessor",1541),eTS(1542,1,eU8,nM),eUe.Mb=function(e){return Pp(e,10).k==(eEn(),e8N)},Y5(eG8,"LabelAndNodeSizeProcessor/lambda$0$Type",1542),eTS(1543,1,eU8,nO),eUe.Mb=function(e){return Pp(e,10).k==(eEn(),e8C)},Y5(eG8,"LabelAndNodeSizeProcessor/lambda$1$Type",1543),eTS(1544,1,eUF,DC),eUe.td=function(e){_z(this.b,this.a,this.c,Pp(e,10))},eUe.a=!1,eUe.c=!1,Y5(eG8,"LabelAndNodeSizeProcessor/lambda$2$Type",1544),eTS(1545,1,eGB,cb),eUe.pf=function(e,t){eFu(Pp(e,37),t)},Y5(eG8,"LabelDummyInserter",1545),eTS(1546,1,e$q,nA),eUe.Lb=function(e){return xc(e_k(Pp(e,70),(eBy(),tab)))===xc((etT(),tp_))},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return xc(e_k(Pp(e,70),(eBy(),tab)))===xc((etT(),tp_))},Y5(eG8,"LabelDummyInserter/1",1546),eTS(1547,1,eGB,nk),eUe.pf=function(e,t){eRz(Pp(e,37),t)},Y5(eG8,"LabelDummyRemover",1547),eTS(1548,1,eU8,nL),eUe.Mb=function(e){return gN(LK(e_k(Pp(e,70),(eBy(),tap))))},Y5(eG8,"LabelDummyRemover/lambda$0$Type",1548),eTS(1359,1,eGB,cm),eUe.pf=function(e,t){ejC(this,Pp(e,37),t)},eUe.a=null,Y5(eG8,"LabelDummySwitcher",1359),eTS(286,1,{286:1},eIu),eUe.c=0,eUe.d=null,eUe.f=0,Y5(eG8,"LabelDummySwitcher/LabelDummyInfo",286),eTS(1360,1,{},nC),eUe.Kb=function(e){return erJ(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"LabelDummySwitcher/lambda$0$Type",1360),eTS(1361,1,eU8,nI),eUe.Mb=function(e){return erJ(),Pp(e,10).k==(eEn(),e8I)},Y5(eG8,"LabelDummySwitcher/lambda$1$Type",1361),eTS(1362,1,{},dX),eUe.Kb=function(e){return Co(this.a,Pp(e,10))},Y5(eG8,"LabelDummySwitcher/lambda$2$Type",1362),eTS(1363,1,eUF,dJ),eUe.td=function(e){BO(this.a,Pp(e,286))},Y5(eG8,"LabelDummySwitcher/lambda$3$Type",1363),eTS(1364,1,e$C,nD),eUe.ue=function(e,t){return FL(Pp(e,286),Pp(t,286))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"LabelDummySwitcher/lambda$4$Type",1364),eTS(791,1,eGB,nN),eUe.pf=function(e,t){XT(Pp(e,37),t)},Y5(eG8,"LabelManagementProcessor",791),eTS(1549,1,eGB,nP),eUe.pf=function(e,t){eLr(Pp(e,37),t)},Y5(eG8,"LabelSideSelector",1549),eTS(1550,1,eU8,nR),eUe.Mb=function(e){return gN(LK(e_k(Pp(e,70),(eBy(),tap))))},Y5(eG8,"LabelSideSelector/lambda$0$Type",1550),eTS(1558,1,eGB,nj),eUe.pf=function(e,t){eN6(Pp(e,37),t)},Y5(eG8,"LayerConstraintPostprocessor",1558),eTS(1559,1,eGB,cv),eUe.pf=function(e,t){eMr(Pp(e,37),t)},Y5(eG8,"LayerConstraintPreprocessor",1559),eTS(360,22,{3:1,35:1,22:1,360:1},EZ);var e4U=enw(eG8,"LayerConstraintPreprocessor/HiddenNodeConnections",360,e1G,Vs,DF);eTS(1560,1,eGB,nF),eUe.pf=function(e,t){eRB(Pp(e,37),t)},Y5(eG8,"LayerSizeAndGraphHeightCalculator",1560),eTS(1561,1,eGB,nY),eUe.pf=function(e,t){eOw(Pp(e,37),t)},Y5(eG8,"LongEdgeJoiner",1561),eTS(1562,1,eGB,nB),eUe.pf=function(e,t){eRf(Pp(e,37),t)},Y5(eG8,"LongEdgeSplitter",1562),eTS(1563,1,eGB,nU),eUe.pf=function(e,t){ejN(this,Pp(e,37),t)},eUe.d=0,eUe.e=0,eUe.i=0,eUe.j=0,eUe.k=0,eUe.n=0,Y5(eG8,"NodePromotion",1563),eTS(1564,1,{},nH),eUe.Kb=function(e){return Pp(e,46),OQ(),!0},eUe.Fb=function(e){return this===e},Y5(eG8,"NodePromotion/lambda$0$Type",1564),eTS(1565,1,{},dq),eUe.Kb=function(e){return UM(this.a,Pp(e,46))},eUe.Fb=function(e){return this===e},eUe.a=0,Y5(eG8,"NodePromotion/lambda$1$Type",1565),eTS(1566,1,{},dZ),eUe.Kb=function(e){return UO(this.a,Pp(e,46))},eUe.Fb=function(e){return this===e},eUe.a=0,Y5(eG8,"NodePromotion/lambda$2$Type",1566),eTS(1567,1,eGB,n$),eUe.pf=function(e,t){eYN(Pp(e,37),t)},Y5(eG8,"NorthSouthPortPostprocessor",1567),eTS(1568,1,eGB,nz),eUe.pf=function(e,t){eYd(Pp(e,37),t)},Y5(eG8,"NorthSouthPortPreprocessor",1568),eTS(1569,1,e$C,nG),eUe.ue=function(e,t){return ea2(Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"NorthSouthPortPreprocessor/lambda$0$Type",1569),eTS(1570,1,eGB,nW),eUe.pf=function(e,t){eDx(Pp(e,37),t)},Y5(eG8,"PartitionMidprocessor",1570),eTS(1571,1,eU8,nK),eUe.Mb=function(e){return Ln(Pp(e,10),(eBy(),ton))},Y5(eG8,"PartitionMidprocessor/lambda$0$Type",1571),eTS(1572,1,eUF,dQ),eUe.td=function(e){H$(this.a,Pp(e,10))},Y5(eG8,"PartitionMidprocessor/lambda$1$Type",1572),eTS(1573,1,eGB,nV),eUe.pf=function(e,t){eO3(Pp(e,37),t)},Y5(eG8,"PartitionPostprocessor",1573),eTS(1574,1,eGB,nq),eUe.pf=function(e,t){exQ(Pp(e,37),t)},Y5(eG8,"PartitionPreprocessor",1574),eTS(1575,1,eU8,nZ),eUe.Mb=function(e){return Ln(Pp(e,10),(eBy(),ton))},Y5(eG8,"PartitionPreprocessor/lambda$0$Type",1575),eTS(1576,1,{},nX),eUe.Kb=function(e){return new R1(null,new YI(new Fa(OH(efc(Pp(e,10)).a.Kc(),new c))))},Y5(eG8,"PartitionPreprocessor/lambda$1$Type",1576),eTS(1577,1,eU8,nJ),eUe.Mb=function(e){return epe(Pp(e,17))},Y5(eG8,"PartitionPreprocessor/lambda$2$Type",1577),eTS(1578,1,eUF,nQ),eUe.td=function(e){eoL(Pp(e,17))},Y5(eG8,"PartitionPreprocessor/lambda$3$Type",1578),eTS(1579,1,eGB,cE),eUe.pf=function(e,t){eDe(Pp(e,37),t)},Y5(eG8,"PortListSorter",1579),eTS(1580,1,{},n1),eUe.Kb=function(e){return euv(),Pp(e,11).e},Y5(eG8,"PortListSorter/lambda$0$Type",1580),eTS(1581,1,{},n0),eUe.Kb=function(e){return euv(),Pp(e,11).g},Y5(eG8,"PortListSorter/lambda$1$Type",1581),eTS(1582,1,e$C,n2),eUe.ue=function(e,t){return qy(Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"PortListSorter/lambda$2$Type",1582),eTS(1583,1,e$C,n3),eUe.ue=function(e,t){return eg_(Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"PortListSorter/lambda$3$Type",1583),eTS(1584,1,e$C,n4),eUe.ue=function(e,t){return eDK(Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"PortListSorter/lambda$4$Type",1584),eTS(1585,1,eGB,n5),eUe.pf=function(e,t){eT3(Pp(e,37),t)},Y5(eG8,"PortSideProcessor",1585),eTS(1586,1,eGB,n6),eUe.pf=function(e,t){eCH(Pp(e,37),t)},Y5(eG8,"ReversedEdgeRestorer",1586),eTS(1591,1,eGB,ms),eUe.pf=function(e,t){emJ(this,Pp(e,37),t)},Y5(eG8,"SelfLoopPortRestorer",1591),eTS(1592,1,{},n9),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"SelfLoopPortRestorer/lambda$0$Type",1592),eTS(1593,1,eU8,n8),eUe.Mb=function(e){return Pp(e,10).k==(eEn(),e8N)},Y5(eG8,"SelfLoopPortRestorer/lambda$1$Type",1593),eTS(1594,1,eU8,n7),eUe.Mb=function(e){return Ln(Pp(e,10),(eBU(),tnk))},Y5(eG8,"SelfLoopPortRestorer/lambda$2$Type",1594),eTS(1595,1,{},re),eUe.Kb=function(e){return Pp(e_k(Pp(e,10),(eBU(),tnk)),403)},Y5(eG8,"SelfLoopPortRestorer/lambda$3$Type",1595),eTS(1596,1,eUF,d1),eUe.td=function(e){eE_(this.a,Pp(e,403))},Y5(eG8,"SelfLoopPortRestorer/lambda$4$Type",1596),eTS(794,1,eUF,rt),eUe.td=function(e){eEq(Pp(e,101))},Y5(eG8,"SelfLoopPortRestorer/lambda$5$Type",794),eTS(1597,1,eGB,rn),eUe.pf=function(e,t){ep1(Pp(e,37),t)},Y5(eG8,"SelfLoopPostProcessor",1597),eTS(1598,1,{},rr),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"SelfLoopPostProcessor/lambda$0$Type",1598),eTS(1599,1,eU8,ri),eUe.Mb=function(e){return Pp(e,10).k==(eEn(),e8N)},Y5(eG8,"SelfLoopPostProcessor/lambda$1$Type",1599),eTS(1600,1,eU8,ra),eUe.Mb=function(e){return Ln(Pp(e,10),(eBU(),tnk))},Y5(eG8,"SelfLoopPostProcessor/lambda$2$Type",1600),eTS(1601,1,eUF,ro),eUe.td=function(e){eyi(Pp(e,10))},Y5(eG8,"SelfLoopPostProcessor/lambda$3$Type",1601),eTS(1602,1,{},rs),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,101).f,1))},Y5(eG8,"SelfLoopPostProcessor/lambda$4$Type",1602),eTS(1603,1,eUF,d0),eUe.td=function(e){Vf(this.a,Pp(e,409))},Y5(eG8,"SelfLoopPostProcessor/lambda$5$Type",1603),eTS(1604,1,eU8,ru),eUe.Mb=function(e){return!!Pp(e,101).i},Y5(eG8,"SelfLoopPostProcessor/lambda$6$Type",1604),eTS(1605,1,eUF,d2),eUe.td=function(e){gb(this.a,Pp(e,101))},Y5(eG8,"SelfLoopPostProcessor/lambda$7$Type",1605),eTS(1587,1,eGB,rc),eUe.pf=function(e,t){eMJ(Pp(e,37),t)},Y5(eG8,"SelfLoopPreProcessor",1587),eTS(1588,1,{},rl),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,101).f,1))},Y5(eG8,"SelfLoopPreProcessor/lambda$0$Type",1588),eTS(1589,1,{},rf),eUe.Kb=function(e){return Pp(e,409).a},Y5(eG8,"SelfLoopPreProcessor/lambda$1$Type",1589),eTS(1590,1,eUF,rd),eUe.td=function(e){MH(Pp(e,17))},Y5(eG8,"SelfLoopPreProcessor/lambda$2$Type",1590),eTS(1606,1,eGB,CV),eUe.pf=function(e,t){eEi(this,Pp(e,37),t)},Y5(eG8,"SelfLoopRouter",1606),eTS(1607,1,{},rh),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,29).a,16))},Y5(eG8,"SelfLoopRouter/lambda$0$Type",1607),eTS(1608,1,eU8,rp),eUe.Mb=function(e){return Pp(e,10).k==(eEn(),e8N)},Y5(eG8,"SelfLoopRouter/lambda$1$Type",1608),eTS(1609,1,eU8,rb),eUe.Mb=function(e){return Ln(Pp(e,10),(eBU(),tnk))},Y5(eG8,"SelfLoopRouter/lambda$2$Type",1609),eTS(1610,1,{},rm),eUe.Kb=function(e){return Pp(e_k(Pp(e,10),(eBU(),tnk)),403)},Y5(eG8,"SelfLoopRouter/lambda$3$Type",1610),eTS(1611,1,eUF,EX),eUe.td=function(e){Hs(this.a,this.b,Pp(e,403))},Y5(eG8,"SelfLoopRouter/lambda$4$Type",1611),eTS(1612,1,eGB,rg),eUe.pf=function(e,t){eAz(Pp(e,37),t)},Y5(eG8,"SemiInteractiveCrossMinProcessor",1612),eTS(1613,1,eU8,rv),eUe.Mb=function(e){return Pp(e,10).k==(eEn(),e8N)},Y5(eG8,"SemiInteractiveCrossMinProcessor/lambda$0$Type",1613),eTS(1614,1,eU8,ry),eUe.Mb=function(e){return R9(Pp(e,10))._b((eBy(),tog))},Y5(eG8,"SemiInteractiveCrossMinProcessor/lambda$1$Type",1614),eTS(1615,1,e$C,rw),eUe.ue=function(e,t){return erF(Pp(e,10),Pp(t,10))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eG8,"SemiInteractiveCrossMinProcessor/lambda$2$Type",1615),eTS(1616,1,{},r_),eUe.Ce=function(e,t){return H4(Pp(e,10),Pp(t,10))},Y5(eG8,"SemiInteractiveCrossMinProcessor/lambda$3$Type",1616),eTS(1618,1,eGB,rE),eUe.pf=function(e,t){eN8(Pp(e,37),t)},Y5(eG8,"SortByInputModelProcessor",1618),eTS(1619,1,eU8,rS),eUe.Mb=function(e){return 0!=Pp(e,11).g.c.length},Y5(eG8,"SortByInputModelProcessor/lambda$0$Type",1619),eTS(1620,1,eUF,d3),eUe.td=function(e){eE6(this.a,Pp(e,11))},Y5(eG8,"SortByInputModelProcessor/lambda$1$Type",1620),eTS(1693,803,{},erY),eUe.Me=function(e){var t,n,r,i;switch(this.c=e,this.a.g){case 2:t=new p0,_r(UJ(new R1(null,new Gq(this.c.a.b,16)),new rj),new E2(this,t)),eS2(this,new rT),ety(t,new rM),t.c=Je(e1R,eUp,1,0,5,1),_r(UJ(new R1(null,new Gq(this.c.a.b,16)),new rO),new d5(t)),eS2(this,new rA),ety(t,new rL),t.c=Je(e1R,eUp,1,0,5,1),n=M_(eim(U1(new R1(null,new Gq(this.c.a.b,16)),new d6(this))),new rC),_r(new R1(null,new Gq(this.c.a.a,16)),new EQ(n,t)),eS2(this,new rD),ety(t,new rk),t.c=Je(e1R,eUp,1,0,5,1);break;case 3:r=new p0,eS2(this,new rx),i=M_(eim(U1(new R1(null,new Gq(this.c.a.b,16)),new d4(this))),new rI),_r(UJ(new R1(null,new Gq(this.c.a.b,16)),new rN),new E0(i,r)),eS2(this,new rP),ety(r,new rR),r.c=Je(e1R,eUp,1,0,5,1);break;default:throw p7(new bI)}},eUe.b=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation",1693),eTS(1694,1,e$q,rx),eUe.Lb=function(e){return M4(Pp(e,57).g,145)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return M4(Pp(e,57).g,145)},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$0$Type",1694),eTS(1695,1,{},d4),eUe.Fe=function(e){return eky(this.a,Pp(e,57))},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$1$Type",1695),eTS(1703,1,eU7,EJ),eUe.Vd=function(){ev_(this.a,this.b,-1)},eUe.b=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$10$Type",1703),eTS(1705,1,e$q,rT),eUe.Lb=function(e){return M4(Pp(e,57).g,145)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return M4(Pp(e,57).g,145)},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$11$Type",1705),eTS(1706,1,eUF,rM),eUe.td=function(e){Pp(e,365).Vd()},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$12$Type",1706),eTS(1707,1,eU8,rO),eUe.Mb=function(e){return M4(Pp(e,57).g,10)},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$13$Type",1707),eTS(1709,1,eUF,d5),eUe.td=function(e){efw(this.a,Pp(e,57))},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$14$Type",1709),eTS(1708,1,eU7,E9),eUe.Vd=function(){ev_(this.b,this.a,-1)},eUe.a=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$15$Type",1708),eTS(1710,1,e$q,rA),eUe.Lb=function(e){return M4(Pp(e,57).g,10)},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return M4(Pp(e,57).g,10)},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$16$Type",1710),eTS(1711,1,eUF,rL),eUe.td=function(e){Pp(e,365).Vd()},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$17$Type",1711),eTS(1712,1,{},d6),eUe.Fe=function(e){return ekw(this.a,Pp(e,57))},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$18$Type",1712),eTS(1713,1,{},rC),eUe.De=function(){return 0},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$19$Type",1713),eTS(1696,1,{},rI),eUe.De=function(){return 0},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$2$Type",1696),eTS(1715,1,eUF,EQ),eUe.td=function(e){jq(this.a,this.b,Pp(e,307))},eUe.a=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$20$Type",1715),eTS(1714,1,eU7,E1),eUe.Vd=function(){eT4(this.a,this.b,-1)},eUe.b=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$21$Type",1714),eTS(1716,1,e$q,rD),eUe.Lb=function(e){return Pp(e,57),!0},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return Pp(e,57),!0},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$22$Type",1716),eTS(1717,1,eUF,rk),eUe.td=function(e){Pp(e,365).Vd()},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$23$Type",1717),eTS(1697,1,eU8,rN),eUe.Mb=function(e){return M4(Pp(e,57).g,10)},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$3$Type",1697),eTS(1699,1,eUF,E0),eUe.td=function(e){jZ(this.a,this.b,Pp(e,57))},eUe.a=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$4$Type",1699),eTS(1698,1,eU7,E8),eUe.Vd=function(){ev_(this.b,this.a,-1)},eUe.a=0,Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$5$Type",1698),eTS(1700,1,e$q,rP),eUe.Lb=function(e){return Pp(e,57),!0},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return Pp(e,57),!0},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$6$Type",1700),eTS(1701,1,eUF,rR),eUe.td=function(e){Pp(e,365).Vd()},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$7$Type",1701),eTS(1702,1,eU8,rj),eUe.Mb=function(e){return M4(Pp(e,57).g,145)},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$8$Type",1702),eTS(1704,1,eUF,E2),eUe.td=function(e){eth(this.a,this.b,Pp(e,57))},Y5(eWs,"EdgeAwareScanlineConstraintCalculation/lambda$9$Type",1704),eTS(1521,1,eGB,AU),eUe.pf=function(e,t){eRE(this,Pp(e,37),t)},Y5(eWs,"HorizontalGraphCompactor",1521),eTS(1522,1,{},d9),eUe.Oe=function(e,t){var n,r,i;return Q8(e,t)?0:(n=KT(e),r=KT(t),n&&n.k==(eEn(),e8C)||r&&r.k==(eEn(),e8C))?0:(i=Pp(e_k(this.a.a,(eBU(),tnx)),304),Ax(i,n?n.k:(eEn(),e8D),r?r.k:(eEn(),e8D)))},eUe.Pe=function(e,t){var n,r,i;return Q8(e,t)?1:(n=KT(e),r=KT(t),i=Pp(e_k(this.a.a,(eBU(),tnx)),304),AT(i,n?n.k:(eEn(),e8D),r?r.k:(eEn(),e8D)))},Y5(eWs,"HorizontalGraphCompactor/1",1522),eTS(1523,1,{},rF),eUe.Ne=function(e,t){return _L(),0==e.a.i},Y5(eWs,"HorizontalGraphCompactor/lambda$0$Type",1523),eTS(1524,1,{},d8),eUe.Ne=function(e,t){return HZ(this.a,e,t)},Y5(eWs,"HorizontalGraphCompactor/lambda$1$Type",1524),eTS(1664,1,{},QF),Y5(eWs,"LGraphToCGraphTransformer",1664),eTS(1672,1,eU8,rY),eUe.Mb=function(e){return null!=e},Y5(eWs,"LGraphToCGraphTransformer/0methodref$nonNull$Type",1672),eTS(1665,1,{},rB),eUe.Kb=function(e){return Dj(),efF(e_k(Pp(Pp(e,57).g,10),(eBU(),tnc)))},Y5(eWs,"LGraphToCGraphTransformer/lambda$0$Type",1665),eTS(1666,1,{},rU),eUe.Kb=function(e){return Dj(),ecR(Pp(Pp(e,57).g,145))},Y5(eWs,"LGraphToCGraphTransformer/lambda$1$Type",1666),eTS(1675,1,eU8,rH),eUe.Mb=function(e){return Dj(),M4(Pp(e,57).g,10)},Y5(eWs,"LGraphToCGraphTransformer/lambda$10$Type",1675),eTS(1676,1,eUF,r$),eUe.td=function(e){Hq(Pp(e,57))},Y5(eWs,"LGraphToCGraphTransformer/lambda$11$Type",1676),eTS(1677,1,eU8,rz),eUe.Mb=function(e){return Dj(),M4(Pp(e,57).g,145)},Y5(eWs,"LGraphToCGraphTransformer/lambda$12$Type",1677),eTS(1681,1,eUF,rG),eUe.td=function(e){ecP(Pp(e,57))},Y5(eWs,"LGraphToCGraphTransformer/lambda$13$Type",1681),eTS(1678,1,eUF,d7),eUe.td=function(e){Tm(this.a,Pp(e,8))},eUe.a=0,Y5(eWs,"LGraphToCGraphTransformer/lambda$14$Type",1678),eTS(1679,1,eUF,he),eUe.td=function(e){Tv(this.a,Pp(e,110))},eUe.a=0,Y5(eWs,"LGraphToCGraphTransformer/lambda$15$Type",1679),eTS(1680,1,eUF,ht),eUe.td=function(e){Tg(this.a,Pp(e,8))},eUe.a=0,Y5(eWs,"LGraphToCGraphTransformer/lambda$16$Type",1680),eTS(1682,1,{},rW),eUe.Kb=function(e){return Dj(),new R1(null,new YI(new Fa(OH(efc(Pp(e,10)).a.Kc(),new c))))},Y5(eWs,"LGraphToCGraphTransformer/lambda$17$Type",1682),eTS(1683,1,eU8,rK),eUe.Mb=function(e){return Dj(),q8(Pp(e,17))},Y5(eWs,"LGraphToCGraphTransformer/lambda$18$Type",1683),eTS(1684,1,eUF,hn),eUe.td=function(e){eex(this.a,Pp(e,17))},Y5(eWs,"LGraphToCGraphTransformer/lambda$19$Type",1684),eTS(1668,1,eUF,hr),eUe.td=function(e){Wj(this.a,Pp(e,145))},Y5(eWs,"LGraphToCGraphTransformer/lambda$2$Type",1668),eTS(1685,1,{},rV),eUe.Kb=function(e){return Dj(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eWs,"LGraphToCGraphTransformer/lambda$20$Type",1685),eTS(1686,1,{},rq),eUe.Kb=function(e){return Dj(),new R1(null,new YI(new Fa(OH(efc(Pp(e,10)).a.Kc(),new c))))},Y5(eWs,"LGraphToCGraphTransformer/lambda$21$Type",1686),eTS(1687,1,{},rZ),eUe.Kb=function(e){return Dj(),Pp(e_k(Pp(e,17),(eBU(),tnO)),15)},Y5(eWs,"LGraphToCGraphTransformer/lambda$22$Type",1687),eTS(1688,1,eU8,rX),eUe.Mb=function(e){return AN(Pp(e,15))},Y5(eWs,"LGraphToCGraphTransformer/lambda$23$Type",1688),eTS(1689,1,eUF,hi),eUe.td=function(e){ekn(this.a,Pp(e,15))},Y5(eWs,"LGraphToCGraphTransformer/lambda$24$Type",1689),eTS(1667,1,eUF,E3),eUe.td=function(e){VK(this.a,this.b,Pp(e,145))},Y5(eWs,"LGraphToCGraphTransformer/lambda$3$Type",1667),eTS(1669,1,{},rJ),eUe.Kb=function(e){return Dj(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eWs,"LGraphToCGraphTransformer/lambda$4$Type",1669),eTS(1670,1,{},rQ),eUe.Kb=function(e){return Dj(),new R1(null,new YI(new Fa(OH(efc(Pp(e,10)).a.Kc(),new c))))},Y5(eWs,"LGraphToCGraphTransformer/lambda$5$Type",1670),eTS(1671,1,{},r1),eUe.Kb=function(e){return Dj(),Pp(e_k(Pp(e,17),(eBU(),tnO)),15)},Y5(eWs,"LGraphToCGraphTransformer/lambda$6$Type",1671),eTS(1673,1,eUF,ha),eUe.td=function(e){exr(this.a,Pp(e,15))},Y5(eWs,"LGraphToCGraphTransformer/lambda$8$Type",1673),eTS(1674,1,eUF,E4),eUe.td=function(e){MN(this.a,this.b,Pp(e,145))},Y5(eWs,"LGraphToCGraphTransformer/lambda$9$Type",1674),eTS(1663,1,{},r0),eUe.Le=function(e){var t,n,r,i,a;for(this.a=e,this.d=new bX,this.c=Je(e24,eUp,121,this.a.a.a.c.length,0,1),this.b=0,n=new fz(this.a.a.a);n.a=b&&(P_(a,ell(l)),v=eB4.Math.max(v,y[l-1]-f),s+=p,m+=y[l-1]-m,f=y[l-1],p=u[l]),p=eB4.Math.max(p,u[l]),++l;s+=p}(h=eB4.Math.min(1/v,1/t.b/s))>r&&(r=h,n=a)}return n},eUe.Wf=function(){return!1},Y5(eWb,"MSDCutIndexHeuristic",802),eTS(1617,1,eGB,iB),eUe.pf=function(e,t){eNZ(Pp(e,37),t)},Y5(eWb,"SingleEdgeGraphWrapper",1617),eTS(227,22,{3:1,35:1,22:1,227:1},Ss);var e4K=enw(eWm,"CenterEdgeLabelPlacementStrategy",227,e1G,Jv,DU);eTS(422,22,{3:1,35:1,22:1,422:1},Su);var e4V=enw(eWm,"ConstraintCalculationStrategy",422,e1G,$G,DH);eTS(314,22,{3:1,35:1,22:1,314:1,246:1,234:1},Sc),eUe.Kf=function(){return ekF(this)},eUe.Xf=function(){return ekF(this)};var e4q=enw(eWm,"CrossingMinimizationStrategy",314,e1G,G5,D$);eTS(337,22,{3:1,35:1,22:1,337:1},Sl);var e4Z=enw(eWm,"CuttingStrategy",337,e1G,G6,DW);eTS(335,22,{3:1,35:1,22:1,335:1,246:1,234:1},Sf),eUe.Kf=function(){return eTW(this)},eUe.Xf=function(){return eTW(this)};var e4X=enw(eWm,"CycleBreakingStrategy",335,e1G,Zv,DK);eTS(419,22,{3:1,35:1,22:1,419:1},Sd);var e4J=enw(eWm,"DirectionCongruency",419,e1G,$z,DV);eTS(450,22,{3:1,35:1,22:1,450:1},Sh);var e4Q=enw(eWm,"EdgeConstraint",450,e1G,G9,Dq);eTS(276,22,{3:1,35:1,22:1,276:1},Sp);var e41=enw(eWm,"EdgeLabelSideSelection",276,e1G,JE,DZ);eTS(479,22,{3:1,35:1,22:1,479:1},Sb);var e40=enw(eWm,"EdgeStraighteningStrategy",479,e1G,$$,DX);eTS(274,22,{3:1,35:1,22:1,274:1},Sm);var e42=enw(eWm,"FixedAlignment",274,e1G,Jw,DJ);eTS(275,22,{3:1,35:1,22:1,275:1},Sg);var e43=enw(eWm,"GraphCompactionStrategy",275,e1G,Jy,DQ);eTS(256,22,{3:1,35:1,22:1,256:1},Sv);var e44=enw(eWm,"GraphProperties",256,e1G,eiT,D1);eTS(292,22,{3:1,35:1,22:1,292:1},Sy);var e45=enw(eWm,"GreedySwitchType",292,e1G,We,D0);eTS(303,22,{3:1,35:1,22:1,303:1},Sw);var e46=enw(eWm,"InLayerConstraint",303,e1G,G7,D2);eTS(420,22,{3:1,35:1,22:1,420:1},S_);var e49=enw(eWm,"InteractiveReferencePoint",420,e1G,$W,D3);eTS(163,22,{3:1,35:1,22:1,163:1},ST);var e48=enw(eWm,"LayerConstraint",163,e1G,Z_,D4);eTS(848,1,e$2,cT),eUe.Qe=function(e){efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWE),""),"Direction Congruency"),"Specifies how drawings of the same graph with different layout directions compare to each other: either a natural reading direction is preserved or the drawings are rotated versions of each other."),trl),(eSd(),tdv)),e4J),el9((epx(),tdh))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWS),""),"Feedback Edges"),"Whether feedback edges should be highlighted by routing around the nodes."),(OQ(),!1)),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWk),""),"Interactive Reference Point"),"Determines which point of a node is considered by interactive layout phases."),trN),tdv),e49),el9(tdh)))),K_(e,eWk,eWI,trR),K_(e,eWk,eWH,trP),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWx),""),"Merge Edges"),"Edges that have no ports are merged so they touch the connected nodes at the same points. When this option is disabled, one port is created for each edge directly connected to a node. When it is enabled, all such incoming edges share an input port, and all outgoing edges share an output port."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWT),""),"Merge Hierarchy-Crossing Edges"),"If hierarchical layout is active, hierarchy-crossing edges use as few hierarchical ports as possible. They are broken by the algorithm, with hierarchical ports inserted as required. Usually, one such port is created for each edge at each hierarchy crossing point. With this option set to true, we try to create as few hierarchical ports as possible in the process. In particular, all edges that form a hyperedge can share a port."),!0),tdm),e11),el9(tdh)))),efO(e,new eE8(v8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWM),""),"Allow Non-Flow Ports To Switch Sides"),"Specifies whether non-flow ports may switch sides if their node's port constraints are either FIXED_SIDE or FIXED_ORDER. A non-flow port is a port on a side that is not part of the currently configured layout flow. For instance, given a left-to-right layout direction, north and south ports would be considered non-flow ports. Further note that the underlying criterium whether to switch sides or not solely relies on the minimization of edge crossings. Hence, edge length and other aesthetics criteria are not addressed."),!1),tdm),e11),el9(tdp)),eow(vx(e17,1),eUP,2,6,["org.eclipse.elk.layered.northOrSouthPort"])))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWO),""),"Port Sorting Strategy"),"Only relevant for nodes with FIXED_SIDE port constraints. Determines the way a node's ports are distributed on the sides of a node if their order is not prescribed. The option is set on parent nodes."),tic),tdv),e5a),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWA),""),"Thoroughness"),"How much effort should be spent to produce a nice layout."),ell(7)),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWL),""),"Add Unnecessary Bendpoints"),"Adds bend points even if an edge does not change direction. If true, each long edge dummy will contribute a bend point to its edges and hierarchy-crossing edges will always get a bend point where they cross hierarchy boundaries. By default, bend points are only added where an edge changes direction."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWC),""),"Generate Position and Layer IDs"),"If enabled position id and layer id are generated, which are usually only used internally when setting the interactiveLayout option. This option should be specified on the root node."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWI),"cycleBreaking"),"Cycle Breaking Strategy"),"Strategy for cycle breaking. Cycle breaking looks for cycles in the graph and determines which edges to reverse to break the cycles. Reversed edges will end up pointing to the opposite direction of regular edges (that is, reversed edges will point left if edges usually point right)."),tru),tdv),e4X),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWD),eKC),"Node Layering Strategy"),"Strategy for node layering."),trX),tdv),e47),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWN),eKC),"Layer Constraint"),"Determines a constraint on the placement of the node regarding the layering."),trU),tdv),e48),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWP),eKC),"Layer Choice Constraint"),"Allows to set a constraint regarding the layer placement of a node. Let i be the value of teh constraint. Assumed the drawing has n layers and i < n. If set to i, it expresses that the node should be placed in i-th layer. Should i>=n be true then the node is placed in the last layer of the drawing. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),ell(-1)),tdw),e15),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWR),eKC),"Layer ID"),"Layer identifier that was calculated by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ell(-1)),tdw),e15),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWj),eKI),"Upper Bound On Width [MinWidth Layerer]"),"Defines a loose upper bound on the width of the MinWidth layerer. If set to '-1' multiple values are tested and the best result is selected."),ell(4)),tdw),e15),el9(tdh)))),K_(e,eWj,eWD,trz),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWF),eKI),"Upper Layer Estimation Scaling Factor [MinWidth Layerer]"),"Multiplied with Upper Bound On Width for defining an upper bound on the width of layers which haven't been determined yet, but whose maximum width had been (roughly) estimated by the MinWidth algorithm. Compensates for too high estimations. If set to '-1' multiple values are tested and the best result is selected."),ell(2)),tdw),e15),el9(tdh)))),K_(e,eWF,eWD,trW),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWY),eKD),"Node Promotion Strategy"),"Reduces number of dummy nodes after layering phase (if possible)."),trq),tdv),e5r),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWB),eKD),"Max Node Promotion Iterations"),"Limits the number of iterations for node promotion."),ell(0)),tdw),e15),el9(tdh)))),K_(e,eWB,eWY,null),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWU),"layering.coffmanGraham"),"Layer Bound"),"The maximum number of nodes allowed per layer."),ell(eUu)),tdw),e15),el9(tdh)))),K_(e,eWU,eWD,trF),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWH),eKN),"Crossing Minimization Strategy"),"Strategy for crossing minimization."),tro),tdv),e4q),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW$),eKN),"Force Node Model Order"),"The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWz),eKN),"Hierarchical Sweepiness"),"How likely it is to use cross-hierarchy (1) vs bottom-up (-1)."),.1),tdg),e13),el9(tdh)))),K_(e,eWz,eKP,tre),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWG),eKN),"Semi-Interactive Crossing Minimization"),"Preserves the order of nodes within a layer but still minimizes crossings between edges connecting long edge dummies. Derives the desired order from positions specified by the 'org.eclipse.elk.position' layout option. Requires a crossing minimization strategy that is able to process 'in-layer' constraints."),!1),tdm),e11),el9(tdh)))),K_(e,eWG,eWH,tri),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWW),eKN),"Position Choice Constraint"),"Allows to set a constraint regarding the position placement of a node in a layer. Assumed the layer in which the node placed includes n other nodes and i < n. If set to i, it expresses that the node should be placed at the i-th position. Should i>=n be true then the node is placed at the last position in the layer. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),ell(-1)),tdw),e15),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWK),eKN),"Position ID"),"Position within a layer that was determined by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ell(-1)),tdw),e15),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWV),eKR),"Greedy Switch Activation Threshold"),"By default it is decided automatically if the greedy switch is activated or not. The decision is based on whether the size of the input graph (without dummy nodes) is smaller than the value of this option. A '0' enforces the activation."),ell(40)),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWq),eKR),"Greedy Switch Crossing Minimization"),"Greedy Switch strategy for crossing minimization. The greedy switch heuristic is executed after the regular crossing minimization as a post-processor. Note that if 'hierarchyHandling' is set to 'INCLUDE_CHILDREN', the 'greedySwitchHierarchical.type' option must be used."),tn9),tdv),e45),el9(tdh)))),K_(e,eWq,eWH,tn8),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWZ),"crossingMinimization.greedySwitchHierarchical"),"Greedy Switch Crossing Minimization (hierarchical)"),"Activates the greedy switch heuristic in case hierarchical layout is used. The differences to the non-hierarchical case (see 'greedySwitch.type') are: 1) greedy switch is inactive by default, 3) only the option value set on the node at which hierarchical layout starts is relevant, and 2) if it's activated by the user, it properly addresses hierarchy-crossing edges."),tn3),tdv),e45),el9(tdh)))),K_(e,eWZ,eWH,tn4),K_(e,eWZ,eKP,tn5),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWX),eKj),"Node Placement Strategy"),"Strategy for node placement."),tis),tdv),e5n),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eWJ),eKj),"Favor Straight Edges Over Balancing"),"Favor straight edges over a balanced node placement. The default behavior is determined automatically based on the used 'edgeRouting'. For an orthogonal style it is set to true, for all other styles to false."),tdm),e11),el9(tdh)))),K_(e,eWJ,eWX,tr9),K_(e,eWJ,eWX,tr8),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eWQ),eKF),"BK Edge Straightening"),"Specifies whether the Brandes Koepf node placer tries to increase the number of straight edges at the expense of diagram size. There is a subtle difference to the 'favorStraightEdges' option, which decides whether a balanced placement of the nodes is desired, or not. In bk terms this means combining the four alignments into a single balanced one, or not. This option on the other hand tries to straighten additional edges during the creation of each of the four alignments."),tr0),tdv),e40),el9(tdh)))),K_(e,eWQ,eWX,tr2),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW1),eKF),"BK Fixed Alignment"),"Tells the BK node placer to use a certain alignment (out of its four) instead of the one producing the smallest height, or the combination of all four."),tr4),tdv),e42),el9(tdh)))),K_(e,eW1,eWX,tr5),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW0),"nodePlacement.linearSegments"),"Linear Segments Deflection Dampening"),"Dampens the movement of nodes to keep the diagram from getting too large."),.3),tdg),e13),el9(tdh)))),K_(e,eW0,eWX,tie),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eW2),"nodePlacement.networkSimplex"),"Node Flexibility"),"Aims at shorter and straighter edges. Two configurations are possible: (a) allow ports to move freely on the side they are assigned to (the order is always defined beforehand), (b) additionally allow to enlarge a node wherever it helps. If this option is not configured for a node, the 'nodeFlexibility.default' value is used, which is specified for the node's parent."),tdv),e5t),el9(tdd)))),K_(e,eW2,eWX,tia),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW3),"nodePlacement.networkSimplex.nodeFlexibility"),"Node Flexibility Default"),"Default value of the 'nodeFlexibility' option for the children of a hierarchical node."),tir),tdv),e5t),el9(tdh)))),K_(e,eW3,eWX,tii),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW4),eKY),"Self-Loop Distribution"),"Alter the distribution of the loops around the node. It only takes effect for PortConstraints.FREE."),trv),tdv),e5s),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW5),eKY),"Self-Loop Ordering"),"Alter the ordering of the loops they can either be stacked or sequenced. It only takes effect for PortConstraints.FREE."),tr_),tdv),e5u),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW6),"edgeRouting.splines"),"Spline Routing Mode"),"Specifies the way control points are assembled for each individual edge. CONSERVATIVE ensures that edges are properly routed around the nodes but feels rather orthogonal at times. SLOPPY uses fewer control points to obtain curvier edge routes but may result in edges overlapping nodes."),trS),tdv),e5c),el9(tdh)))),K_(e,eW6,eKB,trk),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW9),"edgeRouting.splines.sloppy"),"Sloppy Spline Layer Spacing Factor"),"Spacing factor for routing area between layers when using sloppy spline routing."),.2),tdg),e13),el9(tdh)))),K_(e,eW9,eKB,trT),K_(e,eW9,eW6,trM),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eW8),"edgeRouting.polyline"),"Sloped Edge Zone Width"),"Width of the strip to the left and to the right of each layer where the polyline edge router is allowed to refrain from ensuring that edges are routed horizontally. This prevents awkward bend points for nodes that extent almost to the edge of their layer."),2),tdg),e13),el9(tdh)))),K_(e,eW8,eKB,trm),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eW7),eKU),"Spacing Base Value"),"An optional base value for all other layout options of the 'spacing' group. It can be used to conveniently alter the overall 'spaciousness' of the drawing. Whenever an explicit value is set for the other layout options, this base value will have no effect. The base value is not inherited, i.e. it must be set for each hierarchical node."),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKe),eKU),"Edge Node Between Layers Spacing"),"The spacing to be preserved between nodes and edges that are routed next to the node's layer. For the spacing between nodes and edges that cross the node's layer 'spacing.edgeNode' is used."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKt),eKU),"Edge Edge Between Layer Spacing"),"Spacing to be preserved between pairs of edges that are routed between the same pair of layers. Note that 'spacing.edgeEdge' is used for the spacing between pairs of edges crossing the same layer."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKn),eKU),"Node Node Between Layers Spacing"),"The spacing to be preserved between any pair of nodes of two adjacent layers. Note that 'spacing.nodeNode' is used for the spacing between nodes within the layer itself."),20),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKr),eKH),"Direction Priority"),"Defines how important it is to have a certain edge point into the direction of the overall layout. This option is evaluated during the cycle breaking phase."),ell(0)),tdw),e15),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKi),eKH),"Shortness Priority"),"Defines how important it is to keep an edge as short as possible. This option is evaluated during the layering phase."),ell(0)),tdw),e15),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKa),eKH),"Straightness Priority"),"Defines how important it is to keep an edge straight, i.e. aligned with one of the two axes. This option is evaluated during node placement."),ell(0)),tdw),e15),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKo),eK$),ezI),"Tries to further compact components (disconnected sub-graphs)."),!1),tdm),e11),el9(tdh)))),K_(e,eKo,eGs,!0),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKs),eKz),"Post Compaction Strategy"),eKG),tnz),tdv),e43),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKu),eKz),"Post Compaction Constraint Calculation"),eKG),tnH),tdv),e4V),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKc),eKW),"High Degree Node Treatment"),"Makes room around high degree nodes to place leafs and trees."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKl),eKW),"High Degree Node Threshold"),"Whether a node is considered to have a high degree."),ell(16)),tdw),e15),el9(tdh)))),K_(e,eKl,eKc,!0),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKf),eKW),"High Degree Node Maximum Tree Height"),"Maximum height of a subtree connected to a high degree node to be moved to separate layers."),ell(5)),tdw),e15),el9(tdh)))),K_(e,eKf,eKc,!0),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKd),eKK),"Graph Wrapping Strategy"),"For certain graphs and certain prescribed drawing areas it may be desirable to split the laid out graph into chunks that are placed side by side. The edges that connect different chunks are 'wrapped' around from the end of one chunk to the start of the other chunk. The points between the chunks are referred to as 'cuts'."),tiU),tdv),e5f),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKh),eKK),"Additional Wrapped Edges Spacing"),"To visually separate edges that are wrapped from regularly routed edges an additional spacing value can be specified in form of this layout option. The spacing is added to the regular edgeNode spacing."),10),tdg),e13),el9(tdh)))),K_(e,eKh,eKd,tiw),K_(e,eKh,eKd,ti_),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKp),eKK),"Correction Factor for Wrapping"),"At times and for certain types of graphs the executed wrapping may produce results that are consistently biased in the same fashion: either wrapping to often or to rarely. This factor can be used to correct the bias. Internally, it is simply multiplied with the 'aspect ratio' layout option."),1),tdg),e13),el9(tdh)))),K_(e,eKp,eKd,tiS),K_(e,eKp,eKd,tik),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKb),eKV),"Cutting Strategy"),"The strategy by which the layer indexes are determined at which the layering crumbles into chunks."),tiC),tdv),e4Z),el9(tdh)))),K_(e,eKb,eKd,tiI),K_(e,eKb,eKd,tiD),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eKm),eKV),"Manually Specified Cuts"),"Allows the user to specify her own cuts for a certain graph."),td_),e1H),el9(tdh)))),K_(e,eKm,eKb,tiT),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKg),"wrapping.cutting.msd"),"MSD Freedom"),"The MSD cutting strategy starts with an initial guess on the number of chunks the graph should be split into. The freedom specifies how much the strategy may deviate from this guess. E.g. if an initial number of 3 is computed, a freedom of 1 allows 2, 3, and 4 cuts."),tiO),tdw),e15),el9(tdh)))),K_(e,eKg,eKb,tiA),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKv),eKq),"Validification Strategy"),"When wrapping graphs, one can specify indices that are not allowed as split points. The validification strategy makes sure every computed split point is allowed."),tiW),tdv),e5l),el9(tdh)))),K_(e,eKv,eKd,tiK),K_(e,eKv,eKd,tiV),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eKy),eKq),"Valid Indices for Wrapping"),null),td_),e1H),el9(tdh)))),K_(e,eKy,eKd,ti$),K_(e,eKy,eKd,tiz),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKw),eKZ),"Improve Cuts"),"For general graphs it is important that not too many edges wrap backwards. Thus a compromise between evenly-distributed cuts and the total number of cut edges is sought."),!0),tdm),e11),el9(tdh)))),K_(e,eKw,eKd,tij),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK_),eKZ),"Distance Penalty When Improving Cuts"),null),2),tdg),e13),el9(tdh)))),K_(e,eK_,eKd,tiP),K_(e,eK_,eKw,!0),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKE),eKZ),"Improve Wrapped Edges"),"The initial wrapping is performed in a very simple way. As a consequence, edges that wrap from one chunk to another may be unnecessarily long. Activating this option tries to shorten such edges."),!0),tdm),e11),el9(tdh)))),K_(e,eKE,eKd,tiY),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKS),eKX),"Edge Label Side Selection"),"Method to decide on edge label sides."),trp),tdv),e41),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKk),eKX),"Edge Center Label Placement Strategy"),"Determines in which layer center labels of long edges should be placed."),trd),tdv),e4K),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdf]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKx),eKJ),"Consider Model Order"),"Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting."),tnQ),tdv),e5i),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKT),eKJ),"No Model Order"),"Set on a node to not set a model order for this node even though it is a real node."),!1),tdm),e11),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKM),eKJ),"Consider Model Order for Components"),"If set to NONE the usual ordering strategy (by cumulative node priority and size of nodes) is used. INSIDE_PORT_SIDES orders the components with external ports only inside the groups with the same port side. FORCE_MODEL_ORDER enforces the mode order on components. This option might produce bad alignments and sub optimal drawings in terms of used area since the ordering should be respected."),tnW),tdv),e4L),el9(tdh)))),K_(e,eKM,eGs,null),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKO),eKJ),"Long Edge Ordering Strategy"),"Indicates whether long edges are sorted under, over, or equal to nodes that have no connection to a previous layer in a left-to-right or right-to-left layout. Under and over changes to right and left in a vertical layout."),tnZ),tdv),e5e),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKA),eKJ),"Crossing Counter Node Order Influence"),"Indicates with what percentage (1 for 100%) violations of the node model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal node order. Defaults to no influence (0)."),0),tdg),e13),el9(tdh)))),K_(e,eKA,eKx,null),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKL),eKJ),"Crossing Counter Port Order Influence"),"Indicates with what percentage (1 for 100%) violations of the port model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal port order. Defaults to no influence (0)."),0),tdg),e13),el9(tdh)))),K_(e,eKL,eKx,null),eBq((new cA,e))},Y5(eWm,"LayeredMetaDataProvider",848),eTS(986,1,e$2,cA),eUe.Qe=function(e){eBq(e)},Y5(eWm,"LayeredOptions",986),eTS(987,1,{},iH),eUe.$e=function(){return new b3},eUe._e=function(e){},Y5(eWm,"LayeredOptions/LayeredFactory",987),eTS(1372,1,{}),eUe.a=0,Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder",1372),eTS(779,1372,{},ef4),Y5(eWm,"LayeredSpacings/LayeredSpacingsBuilder",779),eTS(313,22,{3:1,35:1,22:1,313:1,246:1,234:1},SE),eUe.Kf=function(){return eM3(this)},eUe.Xf=function(){return eM3(this)};var e47=enw(eWm,"LayeringStrategy",313,e1G,J_,D5);eTS(378,22,{3:1,35:1,22:1,378:1},SS);var e5e=enw(eWm,"LongEdgeOrderingStrategy",378,e1G,G4,D6);eTS(197,22,{3:1,35:1,22:1,197:1},Sk);var e5t=enw(eWm,"NodeFlexibility",197,e1G,VT,D9);eTS(315,22,{3:1,35:1,22:1,315:1,246:1,234:1},Sx),eUe.Kf=function(){return eTG(this)},eUe.Xf=function(){return eTG(this)};var e5n=enw(eWm,"NodePlacementStrategy",315,e1G,Zg,Nr);eTS(260,22,{3:1,35:1,22:1,260:1},SM);var e5r=enw(eWm,"NodePromotionStrategy",260,e1G,etL,D7);eTS(339,22,{3:1,35:1,22:1,339:1},SO);var e5i=enw(eWm,"OrderingStrategy",339,e1G,Wn,Ne);eTS(421,22,{3:1,35:1,22:1,421:1},SA);var e5a=enw(eWm,"PortSortingStrategy",421,e1G,$K,Nt);eTS(452,22,{3:1,35:1,22:1,452:1},SL);var e5o=enw(eWm,"PortType",452,e1G,Wt,D8);eTS(375,22,{3:1,35:1,22:1,375:1},SC);var e5s=enw(eWm,"SelfLoopDistributionStrategy",375,e1G,Wr,Nn);eTS(376,22,{3:1,35:1,22:1,376:1},SI);var e5u=enw(eWm,"SelfLoopOrderingStrategy",376,e1G,$H,Ni);eTS(304,1,{304:1},ejm),Y5(eWm,"Spacings",304),eTS(336,22,{3:1,35:1,22:1,336:1},SD);var e5c=enw(eWm,"SplineRoutingMode",336,e1G,Wa,Na);eTS(338,22,{3:1,35:1,22:1,338:1},SN);var e5l=enw(eWm,"ValidifyStrategy",338,e1G,Wo,No);eTS(377,22,{3:1,35:1,22:1,377:1},SP);var e5f=enw(eWm,"WrappingStrategy",377,e1G,Wi,Ns);eTS(1383,1,eVD,cL),eUe.Yf=function(e){return Pp(e,37),ts2},eUe.pf=function(e,t){eRb(this,Pp(e,37),t)},Y5(eVN,"DepthFirstCycleBreaker",1383),eTS(782,1,eVD,jG),eUe.Yf=function(e){return Pp(e,37),ts3},eUe.pf=function(e,t){eBS(this,Pp(e,37),t)},eUe.Zf=function(e){return Pp(RJ(e,ebO(this.d,e.c.length)),10)},Y5(eVN,"GreedyCycleBreaker",782),eTS(1386,782,eVD,kQ),eUe.Zf=function(e){var t,n,r,i;for(i=null,t=eUu,r=new fz(e);r.a1&&(gN(LK(e_k(Bq((GK(0,e.c.length),Pp(e.c[0],10))),(eBy(),ti7))))?eMR(e,this.d,Pp(this,660)):(Hj(),Mv(e,this.d)),eaz(this.e,e))},eUe.Sf=function(e,t,n,r){var i,a,o,s,u,c,l;for(t!=ja(n,e.length)&&(a=e[t-(n?1:-1)],Xy(this.f,a,n?(enY(),tsN):(enY(),tsD))),i=e[t][0],l=!r||i.k==(eEn(),e8C),c=ZW(e[t]),this.ag(c,l,!1,n),o=0,u=new fz(c);u.a"),e0?zJ(this.a,e[t-1],e[t]):!n&&t1&&(gN(LK(e_k(Bq((GK(0,e.c.length),Pp(e.c[0],10))),(eBy(),ti7))))?eMR(e,this.d,this):(Hj(),Mv(e,this.d)),gN(LK(e_k(Bq((GK(0,e.c.length),Pp(e.c[0],10))),ti7)))||eaz(this.e,e))},Y5(eVF,"ModelOrderBarycenterHeuristic",660),eTS(1803,1,e$C,hx),eUe.ue=function(e,t){return eED(this.a,Pp(e,10),Pp(t,10))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVF,"ModelOrderBarycenterHeuristic/lambda$0$Type",1803),eTS(1403,1,eVD,cF),eUe.Yf=function(e){var t;return Pp(e,37),t=TL(tus),RI(t,(e_x(),e8n),(eB$(),e7I)),t},eUe.pf=function(e,t){$w((Pp(e,37),t))},Y5(eVF,"NoCrossingMinimizer",1403),eTS(796,402,eVR,yu),eUe.$f=function(e,t,n){var r,i,a,o,s,u,c,l,f,d,h;switch(f=this.g,n.g){case 1:for(i=0,a=0,l=new fz(e.j);l.a1&&(i.j==(eYu(),tby)?this.b[e]=!0:i.j==tbY&&e>0&&(this.b[e-1]=!0))},eUe.f=0,Y5(eWc,"AllCrossingsCounter",1798),eTS(587,1,{},erH),eUe.b=0,eUe.d=0,Y5(eWc,"BinaryIndexedTree",587),eTS(524,1,{},IQ),Y5(eWc,"CrossingsCounter",524),eTS(1906,1,e$C,hT),eUe.ue=function(e,t){return je(this.a,Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eWc,"CrossingsCounter/lambda$0$Type",1906),eTS(1907,1,e$C,hM),eUe.ue=function(e,t){return jt(this.a,Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eWc,"CrossingsCounter/lambda$1$Type",1907),eTS(1908,1,e$C,hO),eUe.ue=function(e,t){return jn(this.a,Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eWc,"CrossingsCounter/lambda$2$Type",1908),eTS(1909,1,e$C,hA),eUe.ue=function(e,t){return jr(this.a,Pp(e,11),Pp(t,11))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eWc,"CrossingsCounter/lambda$3$Type",1909),eTS(1910,1,eUF,hL),eUe.td=function(e){QT(this.a,Pp(e,11))},Y5(eWc,"CrossingsCounter/lambda$4$Type",1910),eTS(1911,1,eU8,hC),eUe.Mb=function(e){return kq(this.a,Pp(e,11))},Y5(eWc,"CrossingsCounter/lambda$5$Type",1911),eTS(1912,1,eUF,hI),eUe.td=function(e){kV(this,e)},Y5(eWc,"CrossingsCounter/lambda$6$Type",1912),eTS(1913,1,eUF,SF),eUe.td=function(e){var t;Pj(),Vw(this.b,(t=this.a,Pp(e,11),t))},Y5(eWc,"CrossingsCounter/lambda$7$Type",1913),eTS(826,1,e$q,iq),eUe.Lb=function(e){return Pj(),Ln(Pp(e,11),(eBU(),tng))},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return Pj(),Ln(Pp(e,11),(eBU(),tng))},Y5(eWc,"CrossingsCounter/lambda$8$Type",826),eTS(1905,1,{},hD),Y5(eWc,"HyperedgeCrossingsCounter",1905),eTS(467,1,{35:1,467:1},Cq),eUe.wd=function(e){return ehq(this,Pp(e,467))},eUe.b=0,eUe.c=0,eUe.e=0,eUe.f=0;var e5m=Y5(eWc,"HyperedgeCrossingsCounter/Hyperedge",467);eTS(362,1,{35:1,362:1},He),eUe.wd=function(e){return eMf(this,Pp(e,362))},eUe.b=0,eUe.c=0;var e5g=Y5(eWc,"HyperedgeCrossingsCounter/HyperedgeCorner",362);eTS(523,22,{3:1,35:1,22:1,523:1},Sj);var e5v=enw(eWc,"HyperedgeCrossingsCounter/HyperedgeCorner/Type",523,e1G,$V,Nc);eTS(1405,1,eVD,cO),eUe.Yf=function(e){return Pp(e_k(Pp(e,37),(eBU(),tt3)),21).Hc((eLR(),ttw))?tuh:null},eUe.pf=function(e,t){evK(this,Pp(e,37),t)},Y5(eVY,"InteractiveNodePlacer",1405),eTS(1406,1,eVD,cM),eUe.Yf=function(e){return Pp(e_k(Pp(e,37),(eBU(),tt3)),21).Hc((eLR(),ttw))?tup:null},eUe.pf=function(e,t){emS(this,Pp(e,37),t)},Y5(eVY,"LinearSegmentsNodePlacer",1406),eTS(257,1,{35:1,257:1},ma),eUe.wd=function(e){return vH(this,Pp(e,257))},eUe.Fb=function(e){var t;return!!M4(e,257)&&(t=Pp(e,257),this.b==t.b)},eUe.Hb=function(){return this.b},eUe.Ib=function(){return"ls"+e_F(this.e)},eUe.a=0,eUe.b=0,eUe.c=-1,eUe.d=-1,eUe.g=0;var e5y=Y5(eVY,"LinearSegmentsNodePlacer/LinearSegment",257);eTS(1408,1,eVD,jW),eUe.Yf=function(e){return Pp(e_k(Pp(e,37),(eBU(),tt3)),21).Hc((eLR(),ttw))?tug:null},eUe.pf=function(e,t){eBr(this,Pp(e,37),t)},eUe.b=0,eUe.g=0,Y5(eVY,"NetworkSimplexPlacer",1408),eTS(1427,1,e$C,iZ),eUe.ue=function(e,t){return ME(Pp(e,19).a,Pp(t,19).a)},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVY,"NetworkSimplexPlacer/0methodref$compare$Type",1427),eTS(1429,1,e$C,iX),eUe.ue=function(e,t){return ME(Pp(e,19).a,Pp(t,19).a)},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVY,"NetworkSimplexPlacer/1methodref$compare$Type",1429),eTS(649,1,{649:1},SY);var e5w=Y5(eVY,"NetworkSimplexPlacer/EdgeRep",649);eTS(401,1,{401:1},Ht),eUe.b=!1;var e5_=Y5(eVY,"NetworkSimplexPlacer/NodeRep",401);eTS(508,12,{3:1,4:1,20:1,28:1,52:1,12:1,14:1,15:1,54:1,508:1},mu),Y5(eVY,"NetworkSimplexPlacer/Path",508),eTS(1409,1,{},iJ),eUe.Kb=function(e){return Pp(e,17).d.i.k},Y5(eVY,"NetworkSimplexPlacer/Path/lambda$0$Type",1409),eTS(1410,1,eU8,iQ),eUe.Mb=function(e){return Pp(e,267)==(eEn(),e8D)},Y5(eVY,"NetworkSimplexPlacer/Path/lambda$1$Type",1410),eTS(1411,1,{},i1),eUe.Kb=function(e){return Pp(e,17).d.i},Y5(eVY,"NetworkSimplexPlacer/Path/lambda$2$Type",1411),eTS(1412,1,eU8,hN),eUe.Mb=function(e){return Ct(edH(Pp(e,10)))},Y5(eVY,"NetworkSimplexPlacer/Path/lambda$3$Type",1412),eTS(1413,1,eU8,i0),eUe.Mb=function(e){return RM(Pp(e,11))},Y5(eVY,"NetworkSimplexPlacer/lambda$0$Type",1413),eTS(1414,1,eUF,SB),eUe.td=function(e){MP(this.a,this.b,Pp(e,11))},Y5(eVY,"NetworkSimplexPlacer/lambda$1$Type",1414),eTS(1423,1,eUF,hP),eUe.td=function(e){ekS(this.a,Pp(e,17))},Y5(eVY,"NetworkSimplexPlacer/lambda$10$Type",1423),eTS(1424,1,{},i2),eUe.Kb=function(e){return GE(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eVY,"NetworkSimplexPlacer/lambda$11$Type",1424),eTS(1425,1,eUF,hR),eUe.td=function(e){eCe(this.a,Pp(e,10))},Y5(eVY,"NetworkSimplexPlacer/lambda$12$Type",1425),eTS(1426,1,{},i3),eUe.Kb=function(e){return GE(),ell(Pp(e,121).e)},Y5(eVY,"NetworkSimplexPlacer/lambda$13$Type",1426),eTS(1428,1,{},i4),eUe.Kb=function(e){return GE(),ell(Pp(e,121).e)},Y5(eVY,"NetworkSimplexPlacer/lambda$15$Type",1428),eTS(1430,1,eU8,i5),eUe.Mb=function(e){return GE(),Pp(e,401).c.k==(eEn(),e8N)},Y5(eVY,"NetworkSimplexPlacer/lambda$17$Type",1430),eTS(1431,1,eU8,i6),eUe.Mb=function(e){return GE(),Pp(e,401).c.j.c.length>1},Y5(eVY,"NetworkSimplexPlacer/lambda$18$Type",1431),eTS(1432,1,eUF,Hn),eUe.td=function(e){ef2(this.c,this.b,this.d,this.a,Pp(e,401))},eUe.c=0,eUe.d=0,Y5(eVY,"NetworkSimplexPlacer/lambda$19$Type",1432),eTS(1415,1,{},i9),eUe.Kb=function(e){return GE(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eVY,"NetworkSimplexPlacer/lambda$2$Type",1415),eTS(1433,1,eUF,hj),eUe.td=function(e){MD(this.a,Pp(e,11))},eUe.a=0,Y5(eVY,"NetworkSimplexPlacer/lambda$20$Type",1433),eTS(1434,1,{},i8),eUe.Kb=function(e){return GE(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eVY,"NetworkSimplexPlacer/lambda$21$Type",1434),eTS(1435,1,eUF,hF),eUe.td=function(e){Oi(this.a,Pp(e,10))},Y5(eVY,"NetworkSimplexPlacer/lambda$22$Type",1435),eTS(1436,1,eU8,i7),eUe.Mb=function(e){return Ct(e)},Y5(eVY,"NetworkSimplexPlacer/lambda$23$Type",1436),eTS(1437,1,{},ae),eUe.Kb=function(e){return GE(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eVY,"NetworkSimplexPlacer/lambda$24$Type",1437),eTS(1438,1,eU8,hY),eUe.Mb=function(e){return xH(this.a,Pp(e,10))},Y5(eVY,"NetworkSimplexPlacer/lambda$25$Type",1438),eTS(1439,1,eUF,SU),eUe.td=function(e){eSl(this.a,this.b,Pp(e,10))},Y5(eVY,"NetworkSimplexPlacer/lambda$26$Type",1439),eTS(1440,1,eU8,at),eUe.Mb=function(e){return GE(),!q8(Pp(e,17))},Y5(eVY,"NetworkSimplexPlacer/lambda$27$Type",1440),eTS(1441,1,eU8,an),eUe.Mb=function(e){return GE(),!q8(Pp(e,17))},Y5(eVY,"NetworkSimplexPlacer/lambda$28$Type",1441),eTS(1442,1,{},hB),eUe.Ce=function(e,t){return M8(this.a,Pp(e,29),Pp(t,29))},Y5(eVY,"NetworkSimplexPlacer/lambda$29$Type",1442),eTS(1416,1,{},ar),eUe.Kb=function(e){return GE(),new R1(null,new YI(new Fa(OH(efc(Pp(e,10)).a.Kc(),new c))))},Y5(eVY,"NetworkSimplexPlacer/lambda$3$Type",1416),eTS(1417,1,eU8,ai),eUe.Mb=function(e){return GE(),Km(Pp(e,17))},Y5(eVY,"NetworkSimplexPlacer/lambda$4$Type",1417),eTS(1418,1,eUF,hU),eUe.td=function(e){eNB(this.a,Pp(e,17))},Y5(eVY,"NetworkSimplexPlacer/lambda$5$Type",1418),eTS(1419,1,{},aa),eUe.Kb=function(e){return GE(),new R1(null,new Gq(Pp(e,29).a,16))},Y5(eVY,"NetworkSimplexPlacer/lambda$6$Type",1419),eTS(1420,1,eU8,ao),eUe.Mb=function(e){return GE(),Pp(e,10).k==(eEn(),e8N)},Y5(eVY,"NetworkSimplexPlacer/lambda$7$Type",1420),eTS(1421,1,{},as),eUe.Kb=function(e){return GE(),new R1(null,new YI(new Fa(OH(efs(Pp(e,10)).a.Kc(),new c))))},Y5(eVY,"NetworkSimplexPlacer/lambda$8$Type",1421),eTS(1422,1,eU8,au),eUe.Mb=function(e){return GE(),Rc(Pp(e,17))},Y5(eVY,"NetworkSimplexPlacer/lambda$9$Type",1422),eTS(1404,1,eVD,cz),eUe.Yf=function(e){return Pp(e_k(Pp(e,37),(eBU(),tt3)),21).Hc((eLR(),ttw))?tuv:null},eUe.pf=function(e,t){ePV(Pp(e,37),t)},Y5(eVY,"SimpleNodePlacer",1404),eTS(180,1,{180:1},eIW),eUe.Ib=function(){var e;return e="",this.c==(zs(),tuw)?e+=ezn:this.c==tuy&&(e+=ezt),this.o==(zQ(),tuE)?e+=ezh:this.o==tuS?e+="UP":e+="BALANCED",e},Y5(eVH,"BKAlignedLayout",180),eTS(516,22,{3:1,35:1,22:1,516:1},Sz);var e5E=enw(eVH,"BKAlignedLayout/HDirection",516,e1G,$Z,Nl);eTS(515,22,{3:1,35:1,22:1,515:1},S$);var e5S=enw(eVH,"BKAlignedLayout/VDirection",515,e1G,$X,Nf);eTS(1634,1,{},SH),Y5(eVH,"BKAligner",1634),eTS(1637,1,{},eg$),Y5(eVH,"BKCompactor",1637),eTS(654,1,{654:1},ac),eUe.a=0,Y5(eVH,"BKCompactor/ClassEdge",654),eTS(458,1,{458:1},mo),eUe.a=null,eUe.b=0,Y5(eVH,"BKCompactor/ClassNode",458),eTS(1407,1,eVD,kX),eUe.Yf=function(e){return Pp(e_k(Pp(e,37),(eBU(),tt3)),21).Hc((eLR(),ttw))?tux:null},eUe.pf=function(e,t){eBP(this,Pp(e,37),t)},eUe.d=!1,Y5(eVH,"BKNodePlacer",1407),eTS(1635,1,{},al),eUe.d=0,Y5(eVH,"NeighborhoodInformation",1635),eTS(1636,1,e$C,hH),eUe.ue=function(e,t){return etp(this,Pp(e,46),Pp(t,46))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVH,"NeighborhoodInformation/NeighborComparator",1636),eTS(808,1,{}),Y5(eVH,"ThresholdStrategy",808),eTS(1763,808,{},mm),eUe.bg=function(e,t,n){return this.a.o==(zQ(),tuS)?eHQ:eH1},eUe.cg=function(){},Y5(eVH,"ThresholdStrategy/NullThresholdStrategy",1763),eTS(579,1,{579:1},SG),eUe.c=!1,eUe.d=!1,Y5(eVH,"ThresholdStrategy/Postprocessable",579),eTS(1764,808,{},mg),eUe.bg=function(e,t,n){var r,i,a;return(i=t==n,r=this.a.a[n.p]==t,i||r)?(a=e,this.a.c,zs(),i&&(a=ePX(this,t,!0)),isNaN(a)||isFinite(a)||!r||(a=ePX(this,n,!1)),a):e},eUe.cg=function(){for(var e,t,n,r,i;0!=this.d.b;){if((r=eDJ(this,i=Pp(zv(this.d),579))).a)e=r.a,((n=gN(this.a.f[this.a.g[i.b.p].p]))||q8(e)||e.c.i.c!=e.d.i.c)&&((t=eMd(this,i))||Th(this.e,i))}for(;0!=this.e.a.c.length;)eMd(this,Pp(euO(this.e),579))},Y5(eVH,"ThresholdStrategy/SimpleThresholdStrategy",1764),eTS(635,1,{635:1,246:1,234:1},af),eUe.Kf=function(){return eaM(this)},eUe.Xf=function(){return eaM(this)},Y5(eV$,"EdgeRouterFactory",635),eTS(1458,1,eVD,cG),eUe.Yf=function(e){return eLb(Pp(e,37))},eUe.pf=function(e,t){eP7(Pp(e,37),t)},Y5(eV$,"OrthogonalEdgeRouter",1458),eTS(1451,1,eVD,kJ),eUe.Yf=function(e){return ev4(Pp(e,37))},eUe.pf=function(e,t){eYg(this,Pp(e,37),t)},Y5(eV$,"PolylineEdgeRouter",1451),eTS(1452,1,e$q,ad),eUe.Lb=function(e){return eaQ(Pp(e,10))},eUe.Fb=function(e){return this===e},eUe.Mb=function(e){return eaQ(Pp(e,10))},Y5(eV$,"PolylineEdgeRouter/1",1452),eTS(1809,1,eU8,ah),eUe.Mb=function(e){return Pp(e,129).c==(Xa(),tuU)},Y5(eVz,"HyperEdgeCycleDetector/lambda$0$Type",1809),eTS(1810,1,{},ap),eUe.Ge=function(e){return Pp(e,129).d},Y5(eVz,"HyperEdgeCycleDetector/lambda$1$Type",1810),eTS(1811,1,eU8,ab),eUe.Mb=function(e){return Pp(e,129).c==(Xa(),tuU)},Y5(eVz,"HyperEdgeCycleDetector/lambda$2$Type",1811),eTS(1812,1,{},am),eUe.Ge=function(e){return Pp(e,129).d},Y5(eVz,"HyperEdgeCycleDetector/lambda$3$Type",1812),eTS(1813,1,{},ag),eUe.Ge=function(e){return Pp(e,129).d},Y5(eVz,"HyperEdgeCycleDetector/lambda$4$Type",1813),eTS(1814,1,{},av),eUe.Ge=function(e){return Pp(e,129).d},Y5(eVz,"HyperEdgeCycleDetector/lambda$5$Type",1814),eTS(112,1,{35:1,112:1},ea$),eUe.wd=function(e){return v$(this,Pp(e,112))},eUe.Fb=function(e){var t;return!!M4(e,112)&&(t=Pp(e,112),this.g==t.g)},eUe.Hb=function(){return this.g},eUe.Ib=function(){var e,t,n,r;for(e=new O0("{"),r=new fz(this.n);r.a"+this.b+" ("+AK(this.c)+")"},eUe.d=0,Y5(eVz,"HyperEdgeSegmentDependency",129),eTS(520,22,{3:1,35:1,22:1,520:1},SW);var e5k=enw(eVz,"HyperEdgeSegmentDependency/DependencyType",520,e1G,$q,Nd);eTS(1815,1,{},h$),Y5(eVz,"HyperEdgeSegmentSplitter",1815),eTS(1816,1,{},ym),eUe.a=0,eUe.b=0,Y5(eVz,"HyperEdgeSegmentSplitter/AreaRating",1816),eTS(329,1,{329:1},N4),eUe.a=0,eUe.b=0,eUe.c=0,Y5(eVz,"HyperEdgeSegmentSplitter/FreeArea",329),eTS(1817,1,e$C,aT),eUe.ue=function(e,t){return ID(Pp(e,112),Pp(t,112))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVz,"HyperEdgeSegmentSplitter/lambda$0$Type",1817),eTS(1818,1,eUF,Hi),eUe.td=function(e){V5(this.a,this.d,this.c,this.b,Pp(e,112))},eUe.b=0,Y5(eVz,"HyperEdgeSegmentSplitter/lambda$1$Type",1818),eTS(1819,1,{},aM),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,112).e,16))},Y5(eVz,"HyperEdgeSegmentSplitter/lambda$2$Type",1819),eTS(1820,1,{},aO),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,112).j,16))},Y5(eVz,"HyperEdgeSegmentSplitter/lambda$3$Type",1820),eTS(1821,1,{},aA),eUe.Fe=function(e){return gP(LV(e))},Y5(eVz,"HyperEdgeSegmentSplitter/lambda$4$Type",1821),eTS(655,1,{},YJ),eUe.a=0,eUe.b=0,eUe.c=0,Y5(eVz,"OrthogonalRoutingGenerator",655),eTS(1638,1,{},aL),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,112).e,16))},Y5(eVz,"OrthogonalRoutingGenerator/lambda$0$Type",1638),eTS(1639,1,{},aC),eUe.Kb=function(e){return new R1(null,new Gq(Pp(e,112).j,16))},Y5(eVz,"OrthogonalRoutingGenerator/lambda$1$Type",1639),eTS(661,1,{}),Y5(eVG,"BaseRoutingDirectionStrategy",661),eTS(1807,661,{},mv),eUe.dg=function(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b;if(!e.r||e.q)for(l=t+e.o*n,c=new fz(e.n);c.aez8&&(a=l,i=e,r=new kl(f,a),P7(o.a,r),eDD(this,o,i,r,!1),(d=e.r)&&(h=gP(LV(ep3(d.e,0))),r=new kl(h,a),P7(o.a,r),eDD(this,o,i,r,!1),a=t+d.o*n,i=d,r=new kl(h,a),P7(o.a,r),eDD(this,o,i,r,!1)),r=new kl(b,a),P7(o.a,r),eDD(this,o,i,r,!1)))},eUe.eg=function(e){return e.i.n.a+e.n.a+e.a.a},eUe.fg=function(){return eYu(),tbj},eUe.gg=function(){return eYu(),tbw},Y5(eVG,"NorthToSouthRoutingStrategy",1807),eTS(1808,661,{},my),eUe.dg=function(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b;if(!e.r||e.q)for(l=t-e.o*n,c=new fz(e.n);c.aez8&&(a=l,i=e,r=new kl(f,a),P7(o.a,r),eDD(this,o,i,r,!1),(d=e.r)&&(h=gP(LV(ep3(d.e,0))),r=new kl(h,a),P7(o.a,r),eDD(this,o,i,r,!1),a=t-d.o*n,i=d,r=new kl(h,a),P7(o.a,r),eDD(this,o,i,r,!1)),r=new kl(b,a),P7(o.a,r),eDD(this,o,i,r,!1)))},eUe.eg=function(e){return e.i.n.a+e.n.a+e.a.a},eUe.fg=function(){return eYu(),tbw},eUe.gg=function(){return eYu(),tbj},Y5(eVG,"SouthToNorthRoutingStrategy",1808),eTS(1806,661,{},mw),eUe.dg=function(e,t,n){var r,i,a,o,s,u,c,l,f,d,h,p,b;if(!e.r||e.q)for(l=t+e.o*n,c=new fz(e.n);c.aez8&&(a=l,i=e,r=new kl(a,f),P7(o.a,r),eDD(this,o,i,r,!0),(d=e.r)&&(h=gP(LV(ep3(d.e,0))),r=new kl(a,h),P7(o.a,r),eDD(this,o,i,r,!0),a=t+d.o*n,i=d,r=new kl(a,h),P7(o.a,r),eDD(this,o,i,r,!0)),r=new kl(a,b),P7(o.a,r),eDD(this,o,i,r,!0)))},eUe.eg=function(e){return e.i.n.b+e.n.b+e.a.b},eUe.fg=function(){return eYu(),tby},eUe.gg=function(){return eYu(),tbY},Y5(eVG,"WestToEastRoutingStrategy",1806),eTS(813,1,{},eNG),eUe.Ib=function(){return e_F(this.a)},eUe.b=0,eUe.c=!1,eUe.d=!1,eUe.f=0,Y5(eVK,"NubSpline",813),eTS(407,1,{407:1},eA2,za),Y5(eVK,"NubSpline/PolarCP",407),eTS(1453,1,eVD,egt),eUe.Yf=function(e){return ewy(Pp(e,37))},eUe.pf=function(e,t){eYW(this,Pp(e,37),t)},Y5(eVK,"SplineEdgeRouter",1453),eTS(268,1,{268:1},Xt),eUe.Ib=function(){return this.a+" ->("+this.c+") "+this.b},eUe.c=0,Y5(eVK,"SplineEdgeRouter/Dependency",268),eTS(455,22,{3:1,35:1,22:1,455:1},SK);var e5x=enw(eVK,"SplineEdgeRouter/SideToProcess",455,e1G,$J,Nh);eTS(1454,1,eU8,ak),eUe.Mb=function(e){return eAq(),!Pp(e,128).o},Y5(eVK,"SplineEdgeRouter/lambda$0$Type",1454),eTS(1455,1,{},aS),eUe.Ge=function(e){return eAq(),Pp(e,128).v+1},Y5(eVK,"SplineEdgeRouter/lambda$1$Type",1455),eTS(1456,1,eUF,SV),eUe.td=function(e){Rw(this.a,this.b,Pp(e,46))},Y5(eVK,"SplineEdgeRouter/lambda$2$Type",1456),eTS(1457,1,eUF,Sq),eUe.td=function(e){R_(this.a,this.b,Pp(e,46))},Y5(eVK,"SplineEdgeRouter/lambda$3$Type",1457),eTS(128,1,{35:1,128:1},eSB,eRM),eUe.wd=function(e){return vz(this,Pp(e,128))},eUe.b=0,eUe.e=!1,eUe.f=0,eUe.g=0,eUe.j=!1,eUe.k=!1,eUe.n=0,eUe.o=!1,eUe.p=!1,eUe.q=!1,eUe.s=0,eUe.u=0,eUe.v=0,eUe.F=0,Y5(eVK,"SplineSegment",128),eTS(459,1,{459:1},ax),eUe.a=0,eUe.b=!1,eUe.c=!1,eUe.d=!1,eUe.e=!1,eUe.f=0,Y5(eVK,"SplineSegment/EdgeInformation",459),eTS(1234,1,{},ay),Y5(eVJ,ezQ,1234),eTS(1235,1,e$C,aw),eUe.ue=function(e,t){return ek4(Pp(e,135),Pp(t,135))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVJ,ez1,1235),eTS(1233,1,{},y2),Y5(eVJ,"MrTree",1233),eTS(393,22,{3:1,35:1,22:1,393:1,246:1,234:1},SZ),eUe.Kf=function(){return ek6(this)},eUe.Xf=function(){return ek6(this)};var e5T=enw(eVJ,"TreeLayoutPhases",393,e1G,VM,Np);eTS(1130,209,ezL,CJ),eUe.Ze=function(e,t){var n,r,i,a,o,s,u;for(gN(LK(eT8(e,(eTj(),tcA))))||zh(n=new df((_q(),new gM(e)))),o=(eaW(s=new Xn,e),eo3(s,(eR6(),tcl),e),u=new p2,eDf(e,s,u),eDU(e,s,u),s),a=eDO(this.a,o),i=new fz(a);i.a"+WU(this.c):"e_"+esj(this)},Y5(eVQ,"TEdge",188),eTS(135,134,{3:1,135:1,94:1,134:1},Xn),eUe.Ib=function(){var e,t,n,r,i;for(i=null,r=epL(this.b,0);r.b!=r.d.c;)i+=(null==(n=Pp(Vv(r),86)).c||0==n.c.length?"n_"+n.g:"n_"+n.c)+"\n";for(t=epL(this.a,0);t.b!=t.d.c;)i+=((e=Pp(Vv(t),188)).b&&e.c?WU(e.b)+"->"+WU(e.c):"e_"+esj(e))+"\n";return i};var e5M=Y5(eVQ,"TGraph",135);eTS(633,502,{3:1,502:1,633:1,94:1,134:1}),Y5(eVQ,"TShape",633),eTS(86,633,{3:1,502:1,86:1,633:1,94:1,134:1},esH),eUe.Ib=function(){return WU(this)};var e5O=Y5(eVQ,"TNode",86);eTS(255,1,eU$,hz),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){var e;return e=epL(this.a.d,0),new hG(e)},Y5(eVQ,"TNode/2",255),eTS(358,1,eUE,hG),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){return Pp(Vv(this.a),188).c},eUe.Ob=function(){return yV(this.a)},eUe.Qb=function(){etu(this.a)},Y5(eVQ,"TNode/2/1",358),eTS(1840,1,eGB,CX),eUe.pf=function(e,t){eNv(this,Pp(e,135),t)},Y5(eV1,"FanProcessor",1840),eTS(327,22,{3:1,35:1,22:1,327:1,234:1},SX),eUe.Kf=function(){switch(this.g){case 0:return new mX;case 1:return new CX;case 2:return new aN;case 3:return new aI;case 4:return new aR;case 5:return new aj;default:throw p7(new gL(eWt+(null!=this.f?this.f:""+this.g)))}};var e5A=enw(eV1,eWn,327,e1G,JS,Nb);eTS(1843,1,eGB,aI),eUe.pf=function(e,t){eMo(this,Pp(e,135),t)},eUe.a=0,Y5(eV1,"LevelHeightProcessor",1843),eTS(1844,1,eU$,aD),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){return Hj(),wV(),e2o},Y5(eV1,"LevelHeightProcessor/1",1844),eTS(1841,1,eGB,aN),eUe.pf=function(e,t){eSP(this,Pp(e,135),t)},eUe.a=0,Y5(eV1,"NeighborsProcessor",1841),eTS(1842,1,eU$,aP),eUe.Jc=function(e){qX(this,e)},eUe.Kc=function(){return Hj(),wV(),e2o},Y5(eV1,"NeighborsProcessor/1",1842),eTS(1845,1,eGB,aR),eUe.pf=function(e,t){eMa(this,Pp(e,135),t)},eUe.a=0,Y5(eV1,"NodePositionProcessor",1845),eTS(1839,1,eGB,mX),eUe.pf=function(e,t){eRm(this,Pp(e,135))},Y5(eV1,"RootProcessor",1839),eTS(1846,1,eGB,aj),eUe.pf=function(e,t){elE(Pp(e,135))},Y5(eV1,"Untreeifyer",1846),eTS(851,1,e$2,c$),eUe.Qe=function(e){efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eV3),""),"Weighting of Nodes"),"Which weighting to use when computing a node order."),tcE),(eSd(),tdv)),e5L),el9((epx(),tdh))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eV4),""),"Search Order"),"Which search order to use when computing a spanning tree."),tcw),tdv),e5C),el9(tdh)))),ejG((new cH,e))},Y5(eV5,"MrTreeMetaDataProvider",851),eTS(994,1,e$2,cH),eUe.Qe=function(e){ejG(e)},Y5(eV5,"MrTreeOptions",994),eTS(995,1,{},aF),eUe.$e=function(){return new CJ},eUe._e=function(e){},Y5(eV5,"MrTreeOptions/MrtreeFactory",995),eTS(480,22,{3:1,35:1,22:1,480:1},SJ);var e5L=enw(eV5,"OrderWeighting",480,e1G,$1,Nm);eTS(425,22,{3:1,35:1,22:1,425:1},SQ);var e5C=enw(eV5,"TreeifyingOrder",425,e1G,$Q,Nv);eTS(1459,1,eVD,cD),eUe.Yf=function(e){return Pp(e,135),tcz},eUe.pf=function(e,t){eiD(this,Pp(e,135),t)},Y5("org.eclipse.elk.alg.mrtree.p1treeify","DFSTreeifyer",1459),eTS(1460,1,eVD,cN),eUe.Yf=function(e){return Pp(e,135),tcG},eUe.pf=function(e,t){eSZ(this,Pp(e,135),t)},Y5("org.eclipse.elk.alg.mrtree.p2order","NodeOrderer",1460),eTS(1461,1,eVD,cI),eUe.Yf=function(e){return Pp(e,135),tcW},eUe.pf=function(e,t){eCh(this,Pp(e,135),t)},eUe.a=0,Y5("org.eclipse.elk.alg.mrtree.p3place","NodePlacer",1461),eTS(1462,1,eVD,cP),eUe.Yf=function(e){return Pp(e,135),tcK},eUe.pf=function(e,t){evm(Pp(e,135),t)},Y5("org.eclipse.elk.alg.mrtree.p4route","EdgeRouter",1462),eTS(495,22,{3:1,35:1,22:1,495:1,246:1,234:1},S1),eUe.Kf=function(){return ede(this)},eUe.Xf=function(){return ede(this)};var e5I=enw(eV8,"RadialLayoutPhases",495,e1G,$0,Ng);eTS(1131,209,ezL,y0),eUe.Ze=function(e,t){var n,r,i,a,o,s;if(n=eS8(this,e),ewG(t,"Radial layout",n.c.length),gN(LK(eT8(e,(egj(),tlm))))||zh(r=new df((_q(),new gM(e)))),s=ewE(e),ebu(e,(Lj(),tcV),s),!s)throw p7(new gL("The given graph is not a tree!"));for(0==(i=gP(LV(eT8(e,tl_))))&&(i=ekB(e)),ebu(e,tl_,i),o=new fz(eS8(this,e));o.a0&&eu8((GV(t-1,e.length),e.charCodeAt(t-1)),eGq);)--t;if(r>=t)throw p7(new gL("The given string does not contain any numbers."));if(2!=(i=eIk(e.substr(r,t-r),",|;|\r|\n")).length)throw p7(new gL("Exactly two numbers are expected, "+i.length+" were found."));try{this.a=eEu(e_H(i[0])),this.b=eEu(e_H(i[1]))}catch(a){if(a=eoa(a),M4(a,127))throw n=a,p7(new gL(eGZ+n));throw p7(a)}},eUe.Ib=function(){return"("+this.a+","+this.b+")"},eUe.a=0,eUe.b=0;var e50=Y5(eGX,"KVector",8);eTS(74,68,{3:1,4:1,20:1,28:1,52:1,14:1,68:1,15:1,74:1,414:1},mE,yc,Lb),eUe.Pc=function(){return euE(this)},eUe.Jf=function(e){var t,n,r,i,a,o;r=eIk(e,",|;|\\(|\\)|\\[|\\]|\\{|\\}| | |\n"),HC(this);try{for(n=0,a=0,i=0,o=0;n0&&(a%2==0?i=eEu(r[n]):o=eEu(r[n]),a>0&&a%2!=0&&P7(this,new kl(i,o)),++a),++n}catch(s){if(s=eoa(s),M4(s,127))throw t=s,p7(new gL("The given string does not match the expected format for vectors."+t));throw p7(s)}},eUe.Ib=function(){var e,t,n;for(e=new O0("("),t=epL(this,0);t.b!=t.d.c;)xM(e,(n=Pp(Vv(t),8)).a+","+n.b),t.b!=t.d.c&&(e.a+="; ");return(e.a+=")",e).a};var e52=Y5(eGX,"KVectorChain",74);eTS(248,22,{3:1,35:1,22:1,248:1},kf);var e53=enw(eZe,"Alignment",248,e1G,Jg,NP);eTS(979,1,e$2,cq),eUe.Qe=function(e){eDj(e)},Y5(eZe,"BoxLayouterOptions",979),eTS(980,1,{},oA),eUe.$e=function(){return new oF},eUe._e=function(e){},Y5(eZe,"BoxLayouterOptions/BoxFactory",980),eTS(291,22,{3:1,35:1,22:1,291:1},kd);var e54=enw(eZe,"ContentAlignment",291,e1G,Jm,NR);eTS(684,1,e$2,cZ),eUe.Qe=function(e){efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eZi),""),"Layout Algorithm"),"Select a specific layout algorithm."),(eSd(),tdE)),e17),el9((epx(),tdh))))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eZa),""),"Resolved Layout Algorithm"),"Meta data associated with the selected algorithm."),td_),e5X),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVi),""),"Alignment"),"Alignment of the selected node relative to other nodes; the exact meaning depends on the used algorithm."),td0),tdv),e53),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,ezG),""),"Aspect Ratio"),"The desired aspect ratio of the drawing, that is the quotient of width by height."),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eZo),""),"Bend Points"),"A fixed list of bend points for the edge. This is used by the 'Fixed Layout' algorithm to specify a pre-defined routing for an edge. The vector chain must include the source point, any bend points, and the target point, so it must have at least two points."),td_),e52),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVg),""),"Content Alignment"),"Specifies how the content of a node are aligned. Each node can individually control the alignment of its contents. I.e. if a node should be aligned top left in its parent node, the parent node should specify that option."),td8),tdy),e54),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVr),""),"Debug Mode"),"Whether additional debug information shall be generated."),(OQ(),!1)),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVu),""),ezw),"Overall direction of edges: horizontal (right / left) or vertical (down / up)."),tht),tdv),e55),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKB),""),"Edge Routing"),"What kind of edge routing style should be applied for the content of a parent node. Algorithms may also set this option to single edges in order to mark them as splines. The bend point list of edges with this option set to SPLINES must be interpreted as control points for a piecewise cubic spline."),tho),tdv),e59),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eqC),""),"Expand Nodes"),"If active, nodes are expanded to fill the area of their parent."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKP),""),"Hierarchy Handling"),"Determines whether separate layout runs are triggered for different compound nodes in a hierarchical graph. Setting a node's hierarchy handling to `INCLUDE_CHILDREN` will lay out that node and all of its descendants in a single layout run, until a descendant is encountered which has its hierarchy handling set to `SEPARATE_CHILDREN`. In general, `SEPARATE_CHILDREN` will ensure that a new layout run is triggered for a node with that setting. Including multiple levels of hierarchy in a single layout run may allow cross-hierarchical edges to be laid out properly. If the root node is set to `INHERIT` (or not set at all), the default behavior is `SEPARATE_CHILDREN`."),thf),tdv),e57),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdd]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ezW),""),"Padding"),"The padding to be left to a parent element's border when placing child elements. This can also serve as an output option of a layout algorithm if node size calculation is setup appropriately."),thP),td_),e4R),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdd]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGu),""),"Interactive"),"Whether the algorithm should be run in interactive mode for the content of a parent node. What this means exactly depends on how the specific algorithm interprets this option. Usually in the interactive mode algorithms try to modify the current layout as little as possible."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVA),""),"interactive Layout"),"Whether the graph should be changeable interactively and by setting constraints"),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGf),""),"Omit Node Micro Layout"),"Node micro layout comprises the computation of node dimensions (if requested), the placement of ports and their labels, and the placement of node labels. The functionality is implemented independent of any specific layout algorithm and shouldn't have any negative impact on the layout algorithm's performance itself. Yet, if any unforeseen behavior occurs, this option allows to deactivate the micro layout."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGc),""),"Port Constraints"),"Defines constraints of the position of the ports of a node."),thq),tdv),e6r),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVT),""),"Position"),"The position of a node, port, or label. This is used by the 'Fixed Layout' algorithm to specify a pre-defined position."),td_),e50),jL(tdd,eow(vx(e5Q,1),eU4,175,0,[tdp,tdf]))))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eGr),""),"Priority"),"Defines the priority of an object; its meaning depends on the specific layout algorithm and the context where it is used."),tdw),e15),jL(tdd,eow(vx(e5Q,1),eU4,175,0,[tdl]))))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eGo),""),"Randomization Seed"),"Seed used for pseudo-random number generators to control the layout algorithm. If the value is 0, the seed shall be determined pseudo-randomly (e.g. from the system time)."),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eGs),""),"Separate Connected Components"),"Whether each connected component should be processed separately."),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVv),""),"Junction Points"),"This option is not used as option, but as output of the layout algorithms. It is attached to edges and determines the points where junction symbols should be drawn in order to represent hyperedges with orthogonal routing. Whether such points are computed depends on the chosen layout algorithm and edge routing style. The points are put into the vector chain with no specific order."),thv),td_),e52),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eV_),""),"Comment Box"),"Whether the node should be regarded as a comment box instead of a regular node. In that case its placement should be similar to how labels are handled. Any edges incident to a comment box specify to which graph elements the comment is related."),!1),tdm),e11),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVE),""),"Hypernode"),"Whether the node should be handled as a hypernode."),!1),tdm),e11),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eZs),""),"Label Manager"),"Label managers can shorten labels upon a layout algorithm's request."),td_),tyO),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdf]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVM),""),"Margins"),"Margins define additional space around the actual bounds of a graph element. For instance, ports or labels being placed on the outside of a node's border might introduce such a margin. The margin is used to guarantee non-overlap of other graph elements with those ports or labels."),thw),td_),e4D),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVt),""),"No Layout"),"No layout is done for the associated element. This is used to mark parts of a diagram to avoid their inclusion in the layout graph, or to mark parts of the layout graph to prevent layout engines from processing them. If you wish to exclude the contents of a compound node from automatic layout, while the node itself is still considered on its own layer, use the 'Fixed Layout' algorithm for that node."),!1),tdm),e11),jL(tdd,eow(vx(e5Q,1),eU4,175,0,[tdl,tdp,tdf]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZu),""),"Scale Factor"),"The scaling factor to be applied to the corresponding node in recursive layout. It causes the corresponding node's size to be adjusted, and its ports and labels to be sized and placed accordingly after the layout of that node has been determined (and before the node itself and its siblings are arranged). The scaling is not reverted afterwards, so the resulting layout graph contains the adjusted size and position data. This option is currently not supported if 'Layout Hierarchy' is set."),1),tdg),e13),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZc),""),"Animate"),"Whether the shift from the old layout to the new computed layout shall be animated."),!0),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZl),""),"Animation Time Factor"),"Factor for computation of animation time. The higher the value, the longer the animation time. If the value is 0, the resulting time is always equal to the minimum defined by 'Minimal Animation Time'."),ell(100)),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZf),""),"Layout Ancestors"),"Whether the hierarchy levels on the path from the selected element to the root of the diagram shall be included in the layout process."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZd),""),"Maximal Animation Time"),"The maximal time for animations, in milliseconds."),ell(4e3)),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZh),""),"Minimal Animation Time"),"The minimal time for animations, in milliseconds."),ell(400)),tdw),e15),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZp),""),"Progress Bar"),"Whether a progress bar shall be displayed during layout computations."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZb),""),"Validate Graph"),"Whether the graph shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZm),""),"Validate Options"),"Whether layout options shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!0),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZg),""),"Zoom to Fit"),"Whether the zoom level shall be set to view the whole diagram after layout."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZr),"box"),"Box Layout Mode"),"Configures the packing mode used by the {@link BoxLayoutProvider}. If SIMPLE is not required (neither priorities are used nor the interactive mode), GROUP_DEC can improve the packing and decrease the area. GROUP_MIXED and GROUP_INC may, in very specific scenarios, work better."),td5),tdv),e6u),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eKQ),eKU),"Comment Comment Spacing"),"Spacing to be preserved between a comment box and other comment boxes connected to the same node. The space left between comment boxes of different nodes is controlled by the node-node spacing."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK1),eKU),"Comment Node Spacing"),"Spacing to be preserved between a node and its connected comment boxes. The space left between a node and the comments of another node is controlled by the node-node spacing."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ez$),eKU),"Components Spacing"),"Spacing to be preserved between pairs of connected components. This option is only relevant if 'separateConnectedComponents' is activated."),20),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK0),eKU),"Edge Spacing"),"Spacing to be preserved between any two edges. Note that while this can somewhat easily be satisfied for the segments of orthogonally drawn edges, it is harder for general polylines or splines."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGa),eKU),"Edge Label Spacing"),"The minimal distance to be preserved between a label and the edge it is associated with. Note that the placement of a label is influenced by the 'edgelabels.placement' option."),2),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK2),eKU),"Edge Node Spacing"),"Spacing to be preserved between nodes and edges."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK3),eKU),"Label Spacing"),"Determines the amount of space to be left between two labels of the same graph element."),0),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK6),eKU),"Label Node Spacing"),"Spacing to be preserved between labels and the border of node they are associated with. Note that the placement of a label is influenced by the 'nodelabels.placement' option."),5),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK4),eKU),"Horizontal spacing between Label and Port"),"Horizontal spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK5),eKU),"Vertical spacing between Label and Port"),"Vertical spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGi),eKU),"Node Spacing"),"The minimal distance to be preserved between each two nodes."),20),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK9),eKU),"Node Self Loop Spacing"),"Spacing to be preserved between a node and its self loops."),10),tdg),e13),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eK8),eKU),"Port Spacing"),"Spacing between pairs of ports of the same node."),10),tdg),e13),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdd]))))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eK7),eKU),"Individual Spacing"),"Allows to specify individual spacing values for graph elements that shall be different from the value specified for the element's parent."),td_),e6c),jL(tdd,eow(vx(e5Q,1),eU4,175,0,[tdl,tdp,tdf]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVO),eKU),"Additional Port Space"),"Additional space around the sets of ports on each node side. For each side of a node, this option can reserve additional space before and after the ports on each side. For example, a top spacing of 20 makes sure that the first port on the western and eastern side is 20 units away from the northern border."),tph),td_),e4D),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVx),eZ_),"Layout Partition"),"Partition to which the node belongs. This requires Layout Partitioning to be active. Nodes with lower partition IDs will appear to the left of nodes with higher partition IDs (assuming a left-to-right layout direction)."),tdw),e15),jL(tdh,eow(vx(e5Q,1),eU4,175,0,[tdd]))))),K_(e,eVx,eVk,thY),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVk),eZ_),"Layout Partitioning"),"Whether to activate partitioned layout. This will allow to group nodes through the Layout Partition option. a pair of nodes with different partition indices is then placed such that the node with lower index is placed to the left of the other node (with left-to-right layout direction). Depending on the layout algorithm, this may only be guaranteed to work if all nodes have a layout partition configured, or at least if edges that cross partitions are not part of a partition-crossing cycle."),thj),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVc),eZE),"Node Label Padding"),"Define padding for node labels that are placed inside of a node."),thE),td_),e4R),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGp),eZE),"Node Label Placement"),"Hints for where node labels are to be placed; if empty, the node label's position is not modified."),thk),tdy),e6t),jL(tdd,eow(vx(e5Q,1),eU4,175,0,[tdf]))))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVd),eZS),"Port Alignment"),"Defines the default port distribution for a node. May be overridden for each side individually."),thU),tdv),e6n),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVh),eZS),"Port Alignment (North)"),"Defines how ports on the northern side are placed, overriding the node's general port alignment."),tdv),e6n),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVp),eZS),"Port Alignment (South)"),"Defines how ports on the southern side are placed, overriding the node's general port alignment."),tdv),e6n),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVb),eZS),"Port Alignment (West)"),"Defines how ports on the western side are placed, overriding the node's general port alignment."),tdv),e6n),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVm),eZS),"Port Alignment (East)"),"Defines how ports on the eastern side are placed, overriding the node's general port alignment."),tdv),e6n),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGh),eZk),"Node Size Constraints"),"What should be taken into account when calculating a node's size. Empty size constraints specify that a node's size is already fixed and should not be changed."),thT),tdy),e6o),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGd),eZk),"Node Size Options"),"Options modifying the behavior of the size constraints set on a node. Each member of the set specifies something that should be taken into account when calculating node sizes. The empty set corresponds to no further modifications."),thC),tdy),e6s),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGM),eZk),"Node Size Minimum"),"The minimal size to which a node can be reduced."),thA),td_),e50),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVs),eZk),"Fixed Graph Size"),"By default, the fixed layout provider will enlarge a graph until it is large enough to contain its children. If this option is set, it won't do so."),!1),tdm),e11),el9(tdh)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVy),eKX),"Edge Label Placement"),"Gives a hint on where to put edge labels."),thi),tdv),e56),el9(tdf)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGl),eKX),"Inline Edge Labels"),"If true, an edge label is placed directly on its edge. May only apply to center edge labels. This kind of label placement is only advisable if the label's rendering is such that it is not crossed by its edge and thus stays legible."),!1),tdm),e11),el9(tdf)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eZv),"font"),"Font Name"),"Font name used for a label."),tdE),e17),el9(tdf)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eZy),"font"),"Font Size"),"Font size used for a label."),tdw),e15),el9(tdf)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVS),eZx),"Port Anchor Offset"),"The offset to the port position where connections shall be attached."),td_),e50),el9(tdp)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVw),eZx),"Port Index"),"The index of a port in the fixed order around a node. The order is assumed as clockwise, starting with the leftmost port on the top side. This option must be set if 'Port Constraints' is set to FIXED_ORDER and no specific positions are given for the ports. Additionally, the option 'Port Side' must be defined in this case."),tdw),e15),el9(tdp)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVn),eZx),"Port Side"),"The side of a node on which a port is situated. This option must be set if 'Port Constraints' is set to FIXED_SIDE or FIXED_ORDER and no specific positions are given for the ports."),th2),tdv),e6a),el9(tdp)))),efO(e,new eE8(yt(ye(yn(v4(v7(v6(v9(new oN,eVe),eZx),"Port Border Offset"),"The offset of ports on the node border. With a positive offset the port is moved outside of the node, while with a negative offset the port is moved towards the inside. An offset of 0 means that the port is placed directly on the node border, i.e. if the port side is north, the port's south border touches the nodes's north border; if the port side is east, the port's west border touches the nodes's east border; if the port side is south, the port's north border touches the node's south border; if the port side is west, the port's east border touches the node's west border."),tdg),e13),el9(tdp)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eGb),eZT),"Port Label Placement"),"Decides on a placement method for port labels; if empty, the node label's position is not modified."),thQ),tdy),e6i),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVl),eZT),"Port Labels Next to Port"),"Use 'portLabels.placement': NEXT_TO_PORT_OF_POSSIBLE."),!1),tdm),e11),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVf),eZT),"Treat Port Labels as Group"),"If this option is true (default), the labels of a port will be treated as a group when it comes to centering them next to their port. If this option is false, only the first label will be centered next to the port, with the others being placed below. This only applies to labels of eastern and western ports and will have no effect if labels are not placed next to their port."),!0),tdm),e11),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVa),eZM),"Activate Inside Self Loops"),"Whether this node allows to route self loops inside of it instead of around it. If set to true, this will make the node a compound node if it isn't already, and will require the layout algorithm to support compound nodes with hierarchical ports."),!1),tdm),e11),el9(tdd)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eVo),eZM),"Inside Self Loop"),"Whether a self loop should be routed inside a node instead of around that node."),!1),tdm),e11),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,ezz),"edge"),"Edge Thickness"),"The thickness of an edge. This is a hint on the line width used to draw an edge, possibly requiring more space to be reserved for it."),1),tdg),e13),el9(tdl)))),efO(e,new eE8(yt(ye(yn(v5(v4(v7(v6(v9(new oN,eZw),"edge"),"Edge Type"),"The type of an edge. This is usually used for UML class diagrams, where associations must be handled differently from generalizations."),thu),tdv),e58),el9(tdl)))),_B(e,new GM(v0(v3(v2(new of,eG1),"Layered"),'The layer-based method was introduced by Sugiyama, Tagawa and Toda in 1981. It emphasizes the direction of edges by pointing as many edges as possible into the same direction. The nodes are arranged in layers, which are sometimes called "hierarchies", and then reordered such that the number of edge crossings is minimized. Afterwards, concrete coordinates are computed for the nodes and edge bend points.'))),_B(e,new GM(v0(v3(v2(new of,"org.eclipse.elk.orthogonal"),"Orthogonal"),'Orthogonal methods that follow the "topology-shape-metrics" approach by Batini, Nardelli and Tamassia \'86. The first phase determines the topology of the drawing by applying a planarization technique, which results in a planar representation of the graph. The orthogonal shape is computed in the second phase, which aims at minimizing the number of edge bends, and is called orthogonalization. The third phase leads to concrete coordinates for nodes and edge bend points by applying a compaction method, thus defining the metrics.'))),_B(e,new GM(v0(v3(v2(new of,eGn),"Force"),"Layout algorithms that follow physical analogies by simulating a system of attractive and repulsive forces. The first successful method of this kind was proposed by Eades in 1984."))),_B(e,new GM(v0(v3(v2(new of,"org.eclipse.elk.circle"),"Circle"),"Circular layout algorithms emphasize cycles or biconnected components of a graph by arranging them in circles. This is useful if a drawing is desired where such components are clearly grouped, or where cycles are shown as prominent OPTIONS of the graph."))),_B(e,new GM(v0(v3(v2(new of,eV9),"Tree"),"Specialized layout methods for trees, i.e. acyclic graphs. The regular structure of graphs that have no undirected cycles can be emphasized using an algorithm of this type."))),_B(e,new GM(v0(v3(v2(new of,"org.eclipse.elk.planar"),"Planar"),"Algorithms that require a planar or upward planar graph. Most of these algorithms are theoretically interesting, but not practically usable."))),_B(e,new GM(v0(v3(v2(new of,eqp),"Radial"),"Radial layout algorithms usually position the nodes of the graph on concentric circles."))),eIm((new cX,e)),eDj((new cq,e)),eL6((new cJ,e))},Y5(eZe,"CoreOptions",684),eTS(103,22,{3:1,35:1,22:1,103:1},kh);var e55=enw(eZe,ezw,103,e1G,Zh,NY);eTS(272,22,{3:1,35:1,22:1,272:1},kp);var e56=enw(eZe,"EdgeLabelPlacement",272,e1G,Wp,NB);eTS(218,22,{3:1,35:1,22:1,218:1},kb);var e59=enw(eZe,"EdgeRouting",218,e1G,VC,NU);eTS(312,22,{3:1,35:1,22:1,312:1},km);var e58=enw(eZe,"EdgeType",312,e1G,Jx,NH);eTS(977,1,e$2,cX),eUe.Qe=function(e){eIm(e)},Y5(eZe,"FixedLayouterOptions",977),eTS(978,1,{},o$),eUe.$e=function(){return new oR},eUe._e=function(e){},Y5(eZe,"FixedLayouterOptions/FixedFactory",978),eTS(334,22,{3:1,35:1,22:1,334:1},kg);var e57=enw(eZe,"HierarchyHandling",334,e1G,Wh,N$);eTS(285,22,{3:1,35:1,22:1,285:1},kv);var e6e=enw(eZe,"LabelSide",285,e1G,VL,Nz);eTS(93,22,{3:1,35:1,22:1,93:1},ky);var e6t=enw(eZe,"NodeLabelPlacement",93,e1G,ene,NG);eTS(249,22,{3:1,35:1,22:1,249:1},kw);var e6n=enw(eZe,"PortAlignment",249,e1G,Zp,NW);eTS(98,22,{3:1,35:1,22:1,98:1},k_);var e6r=enw(eZe,"PortConstraints",98,e1G,X0,NK);eTS(273,22,{3:1,35:1,22:1,273:1},kE);var e6i=enw(eZe,"PortLabelPlacement",273,e1G,Jk,NV);eTS(61,22,{3:1,35:1,22:1,61:1},kS);var e6a=enw(eZe,"PortSide",61,e1G,q5,NX);eTS(981,1,e$2,cJ),eUe.Qe=function(e){eL6(e)},Y5(eZe,"RandomLayouterOptions",981),eTS(982,1,{},oz),eUe.$e=function(){return new oV},eUe._e=function(e){},Y5(eZe,"RandomLayouterOptions/RandomFactory",982),eTS(374,22,{3:1,35:1,22:1,374:1},kk);var e6o=enw(eZe,"SizeConstraint",374,e1G,VA,Nq);eTS(259,22,{3:1,35:1,22:1,259:1},kx);var e6s=enw(eZe,"SizeOptions",259,e1G,en2,NZ);eTS(370,1,{1949:1},mV),eUe.b=!1,eUe.c=0,eUe.d=-1,eUe.e=null,eUe.f=null,eUe.g=-1,eUe.j=!1,eUe.k=!1,eUe.n=!1,eUe.o=0,eUe.q=0,eUe.r=0,Y5(eVL,"BasicProgressMonitor",370),eTS(972,209,ezL,oF),eUe.Ze=function(e,t){var n,r,i,a,o,s,u,c,l;(ewG(t,"Box layout",2),i=gR(LV(eT8(e,(e_C(),tdG)))),a=Pp(eT8(e,tdH),116),n=gN(LK(eT8(e,tdj))),r=gN(LK(eT8(e,tdF))),0===Pp(eT8(e,tdP),311).g)?(o=(s=new I4((e.a||(e.a=new FQ(e6k,e,10,11)),e.a)),Hj(),Mv(s,new h3(r)),s),u=eSI(e),(null==(c=LV(eT8(e,tdN)))||(BJ(c),c<=0))&&(c=1.3),l=eYA(o,i,a,u.a,u.b,n,(BJ(c),c)),eYx(e,l.a,l.b,!1,!0)):eRF(e,i,a,n),eEj(t)},Y5(eVL,"BoxLayoutProvider",972),eTS(973,1,e$C,h3),eUe.ue=function(e,t){return eOQ(this,Pp(e,33),Pp(t,33))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},eUe.a=!1,Y5(eVL,"BoxLayoutProvider/1",973),eTS(157,1,{157:1},etD,Lp),eUe.Ib=function(){return this.c?eC4(this.c):e_F(this.b)},Y5(eVL,"BoxLayoutProvider/Group",157),eTS(311,22,{3:1,35:1,22:1,311:1},kT);var e6u=enw(eVL,"BoxLayoutProvider/PackingMode",311,e1G,VI,NJ);eTS(974,1,e$C,oY),eUe.ue=function(e,t){return HK(Pp(e,157),Pp(t,157))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVL,"BoxLayoutProvider/lambda$0$Type",974),eTS(975,1,e$C,oB),eUe.ue=function(e,t){return Hm(Pp(e,157),Pp(t,157))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVL,"BoxLayoutProvider/lambda$1$Type",975),eTS(976,1,e$C,oU),eUe.ue=function(e,t){return Hg(Pp(e,157),Pp(t,157))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eVL,"BoxLayoutProvider/lambda$2$Type",976),eTS(1365,1,{831:1},oH),eUe.qg=function(e,t){return _R(),!M4(t,160)||yX((eoM(),Pp(e,160)),t)},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$0$Type",1365),eTS(1366,1,eUF,h4),eUe.td=function(e){eux(this.a,Pp(e,146))},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$1$Type",1366),eTS(1367,1,eUF,oj),eUe.td=function(e){Pp(e,94),_R()},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$2$Type",1367),eTS(1371,1,eUF,h5),eUe.td=function(e){erQ(this.a,Pp(e,94))},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$3$Type",1371),eTS(1369,1,eU8,kM),eUe.Mb=function(e){return esI(this.a,this.b,Pp(e,146))},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$4$Type",1369),eTS(1368,1,eU8,kO),eUe.Mb=function(e){return Lt(this.a,this.b,Pp(e,831))},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$5$Type",1368),eTS(1370,1,eUF,kA),eUe.td=function(e){Fj(this.a,this.b,Pp(e,146))},Y5(eVL,"ElkSpacings/AbstractSpacingsBuilder/lambda$6$Type",1370),eTS(935,1,{},oP),eUe.Kb=function(e){return TA(e)},eUe.Fb=function(e){return this===e},Y5(eVL,"ElkUtil/lambda$0$Type",935),eTS(936,1,eUF,kL),eUe.td=function(e){exS(this.a,this.b,Pp(e,79))},eUe.a=0,eUe.b=0,Y5(eVL,"ElkUtil/lambda$1$Type",936),eTS(937,1,eUF,kC),eUe.td=function(e){gp(this.a,this.b,Pp(e,202))},eUe.a=0,eUe.b=0,Y5(eVL,"ElkUtil/lambda$2$Type",937),eTS(938,1,eUF,kI),eUe.td=function(e){Me(this.a,this.b,Pp(e,137))},eUe.a=0,eUe.b=0,Y5(eVL,"ElkUtil/lambda$3$Type",938),eTS(939,1,eUF,h6),eUe.td=function(e){RE(this.a,Pp(e,469))},Y5(eVL,"ElkUtil/lambda$4$Type",939),eTS(342,1,{35:1,342:1},pQ),eUe.wd=function(e){return Os(this,Pp(e,236))},eUe.Fb=function(e){var t;return!!M4(e,342)&&(t=Pp(e,342),this.a==t.a)},eUe.Hb=function(){return zy(this.a)},eUe.Ib=function(){return this.a+" (exclusive)"},eUe.a=0,Y5(eVL,"ExclusiveBounds/ExclusiveLowerBound",342),eTS(1138,209,ezL,oR),eUe.Ze=function(e,t){var n,r,i,a,o,s,u,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x;for(ewG(t,"Fixed Layout",1),a=Pp(eT8(e,(eBB(),tha)),218),d=0,h=0,y=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));y.e!=y.i.gc();){for(g=Pp(epH(y),33),(x=Pp(eT8(g,(euw(),tp$)),8))&&(TP(g,x.a,x.b),Pp(eT8(g,tpF),174).Hc((ed6(),tbW))&&(p=Pp(eT8(g,tpB),8)).a>0&&p.b>0&&eYx(g,p.a,p.b,!0,!0)),d=eB4.Math.max(d,g.i+g.g),h=eB4.Math.max(h,g.j+g.f),l=new Ow((g.n||(g.n=new FQ(e6S,g,1,7)),g.n));l.e!=l.i.gc();)s=Pp(epH(l),137),(x=Pp(eT8(s,tp$),8))&&TP(s,x.a,x.b),d=eB4.Math.max(d,g.i+s.i+s.g),h=eB4.Math.max(h,g.j+s.j+s.f);for(E=new Ow((g.c||(g.c=new FQ(e6x,g,9,9)),g.c));E.e!=E.i.gc();)for(_=Pp(epH(E),118),(x=Pp(eT8(_,tp$),8))&&TP(_,x.a,x.b),S=g.i+_.i,k=g.j+_.j,d=eB4.Math.max(d,S+_.g),h=eB4.Math.max(h,k+_.f),u=new Ow((_.n||(_.n=new FQ(e6S,_,1,7)),_.n));u.e!=u.i.gc();)s=Pp(epH(u),137),(x=Pp(eT8(s,tp$),8))&&TP(s,x.a,x.b),d=eB4.Math.max(d,S+s.i+s.g),h=eB4.Math.max(h,k+s.j+s.f);for(i=new Fa(OH(eOi(g).a.Kc(),new c));eTk(i);)n=Pp(ZC(i),79),f=eYT(n),d=eB4.Math.max(d,f.a),h=eB4.Math.max(h,f.b);for(r=new Fa(OH(eOr(g).a.Kc(),new c));eTk(r);)n=Pp(ZC(r),79),z$(e_I(n))!=e&&(f=eYT(n),d=eB4.Math.max(d,f.a),h=eB4.Math.max(h,f.b))}if(a==(efE(),tpx))for(v=new Ow((e.a||(e.a=new FQ(e6k,e,10,11)),e.a));v.e!=v.i.gc();)for(g=Pp(epH(v),33),r=new Fa(OH(eOi(g).a.Kc(),new c));eTk(r);)n=Pp(ZC(r),79),0==(o=eDX(n)).b?ebu(n,thg,null):ebu(n,thg,o);gN(LK(eT8(e,(euw(),tpY))))||(w=Pp(eT8(e,tpU),116),eYx(e,m=d+w.b+w.c,b=h+w.d+w.a,!0,!0)),eEj(t)},Y5(eVL,"FixedLayoutProvider",1138),eTS(373,134,{3:1,414:1,373:1,94:1,134:1},oG,eer),eUe.Jf=function(e){var t,n,r,i,a,o,s,u,c;if(e)try{for(a=u=eIk(e,";,;"),o=0,s=a.length;o>16&eHd|t^r<<16},eUe.Kc=function(){return new h9(this)},eUe.Ib=function(){return null==this.a&&null==this.b?"pair(null,null)":null==this.a?"pair(null,"+efF(this.b)+")":null==this.b?"pair("+efF(this.a)+",null)":"pair("+efF(this.a)+","+efF(this.b)+")"},Y5(eVL,"Pair",46),eTS(983,1,eUE,h9),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return!this.c&&(!this.b&&null!=this.a.a||null!=this.a.b)},eUe.Pb=function(){if(!this.c&&!this.b&&null!=this.a.a)return this.b=!0,this.a.a;if(!this.c&&null!=this.a.b)return this.c=!0,this.a.b;throw p7(new bC)},eUe.Qb=function(){throw this.c&&null!=this.a.b?this.a.b=null:this.b&&null!=this.a.a&&(this.a.a=null),p7(new bT)},eUe.b=!1,eUe.c=!1,Y5(eVL,"Pair/1",983),eTS(448,1,{448:1},Ho),eUe.Fb=function(e){return UT(this.a,Pp(e,448).a)&&UT(this.c,Pp(e,448).c)&&UT(this.d,Pp(e,448).d)&&UT(this.b,Pp(e,448).b)},eUe.Hb=function(){return euF(eow(vx(e1R,1),eUp,1,5,[this.a,this.c,this.d,this.b]))},eUe.Ib=function(){return"("+this.a+eUd+this.c+eUd+this.d+eUd+this.b+")"},Y5(eVL,"Quadruple",448),eTS(1126,209,ezL,oV),eUe.Ze=function(e,t){var n,r,i,a,o;if(ewG(t,"Random Layout",1),0==(e.a||(e.a=new FQ(e6k,e,10,11)),e.a).i){eEj(t);return}i=(a=Pp(eT8(e,(ed5(),tbz)),19))&&0!=a.a?new qS(a.a):new efo,n=gR(LV(eT8(e,tbU))),o=gR(LV(eT8(e,tbG))),r=Pp(eT8(e,tbH),116),eF1(e,i,n,o,r),eEj(t)},Y5(eVL,"RandomLayoutProvider",1126),eTS(553,1,{}),eUe.qf=function(){return new kl(this.f.i,this.f.j)},eUe.We=function(e){return $k(e,(eBB(),thK))?eT8(this.f,tmu):eT8(this.f,e)},eUe.rf=function(){return new kl(this.f.g,this.f.f)},eUe.sf=function(){return this.g},eUe.Xe=function(e){return X2(this.f,e)},eUe.tf=function(e){eno(this.f,e.a),ens(this.f,e.b)},eUe.uf=function(e){ena(this.f,e.a),eni(this.f,e.b)},eUe.vf=function(e){this.g=e},eUe.g=0,Y5(eZI,"ElkGraphAdapters/AbstractElkGraphElementAdapter",553),eTS(554,1,{839:1},h8),eUe.wf=function(){var e,t;if(!this.b)for(this.b=K$(UB(this.a).i),t=new Ow(UB(this.a));t.e!=t.i.gc();)e=Pp(epH(t),137),P_(this.b,new gO(e));return this.b},eUe.b=null,Y5(eZI,"ElkGraphAdapters/ElkEdgeAdapter",554),eTS(301,553,{},gM),eUe.xf=function(){return em3(this)},eUe.a=null,Y5(eZI,"ElkGraphAdapters/ElkGraphAdapter",301),eTS(630,553,{181:1},gO),Y5(eZI,"ElkGraphAdapters/ElkLabelAdapter",630),eTS(629,553,{680:1},AC),eUe.wf=function(){return em0(this)},eUe.Af=function(){var e;return(e=Pp(eT8(this.f,(eBB(),thy)),142))||(e=new mh),e},eUe.Cf=function(){return em2(this)},eUe.Ef=function(e){var t;t=new Dk(e),ebu(this.f,(eBB(),thy),t)},eUe.Ff=function(e){ebu(this.f,(eBB(),thN),new DS(e))},eUe.yf=function(){return this.d},eUe.zf=function(){var e,t;if(!this.a)for(this.a=new p0,t=new Fa(OH(eOr(Pp(this.f,33)).a.Kc(),new c));eTk(t);)e=Pp(ZC(t),79),P_(this.a,new h8(e));return this.a},eUe.Bf=function(){var e,t;if(!this.c)for(this.c=new p0,t=new Fa(OH(eOi(Pp(this.f,33)).a.Kc(),new c));eTk(t);)e=Pp(ZC(t),79),P_(this.c,new h8(e));return this.c},eUe.Df=function(){return 0!=H8(Pp(this.f,33)).i||gN(LK(Pp(this.f,33).We((eBB(),thh))))},eUe.Gf=function(){QV(this,(_q(),tms))},eUe.a=null,eUe.b=null,eUe.c=null,eUe.d=null,eUe.e=null,Y5(eZI,"ElkGraphAdapters/ElkNodeAdapter",629),eTS(1266,553,{838:1},pA),eUe.wf=function(){return egd(this)},eUe.zf=function(){var e,t;if(!this.a)for(this.a=AH(Pp(this.f,118).xg().i),t=new Ow(Pp(this.f,118).xg());t.e!=t.i.gc();)e=Pp(epH(t),79),P_(this.a,new h8(e));return this.a},eUe.Bf=function(){var e,t;if(!this.c)for(this.c=AH(Pp(this.f,118).yg().i),t=new Ow(Pp(this.f,118).yg());t.e!=t.i.gc();)e=Pp(epH(t),79),P_(this.c,new h8(e));return this.c},eUe.Hf=function(){return Pp(Pp(this.f,118).We((eBB(),th0)),61)},eUe.If=function(){var e,t,n,r,i,a,o,s;for(r=zY(Pp(this.f,118)),n=new Ow(Pp(this.f,118).yg());n.e!=n.i.gc();)for(e=Pp(epH(n),79),s=new Ow((e.c||(e.c=new Ih(e6m,e,5,8)),e.c));s.e!=s.i.gc();)if(o=Pp(epH(s),82),etg(ewH(o),r)||ewH(o)==r&&gN(LK(eT8(e,(eBB(),thp)))))return!0;for(t=new Ow(Pp(this.f,118).xg());t.e!=t.i.gc();)for(e=Pp(epH(t),79),a=new Ow((e.b||(e.b=new Ih(e6m,e,4,7)),e.b));a.e!=a.i.gc();)if(i=Pp(epH(a),82),etg(ewH(i),r))return!0;return!1},eUe.a=null,eUe.b=null,eUe.c=null,Y5(eZI,"ElkGraphAdapters/ElkPortAdapter",1266),eTS(1267,1,e$C,oq),eUe.ue=function(e,t){return eC3(Pp(e,118),Pp(t,118))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(eZI,"ElkGraphAdapters/PortComparator",1267);var e6f=RL(eZD,"EObject"),e6d=RL(eZN,eZP),e6h=RL(eZN,eZR),e6p=RL(eZN,eZj),e6b=RL(eZN,"ElkShape"),e6m=RL(eZN,eZF),e6g=RL(eZN,eZY),e6v=RL(eZN,eZB),e6y=RL(eZD,eZU),e6w=RL(eZD,"EFactory"),e6_=RL(eZD,eZH),e6E=RL(eZD,"EPackage"),e6S=RL(eZN,eZ$),e6k=RL(eZN,eZz),e6x=RL(eZN,eZG);eTS(90,1,eZW),eUe.Jg=function(){return this.Kg(),null},eUe.Kg=function(){return null},eUe.Lg=function(){return this.Kg(),!1},eUe.Mg=function(){return!1},eUe.Ng=function(e){eam(this,e)},Y5(eZK,"BasicNotifierImpl",90),eTS(97,90,eZ0),eUe.nh=function(){return TO(this)},eUe.Og=function(e,t){return e},eUe.Pg=function(){throw p7(new bO)},eUe.Qg=function(e){var t;return t=ebY(Pp(ee2(this.Tg(),this.Vg()),18)),this.eh().ih(this,t.n,t.f,e)},eUe.Rg=function(e,t){throw p7(new bO)},eUe.Sg=function(e,t,n){return eDg(this,e,t,n)},eUe.Tg=function(){var e;return this.Pg()&&(e=this.Pg().ck())?e:this.zh()},eUe.Ug=function(){return eTp(this)},eUe.Vg=function(){throw p7(new bO)},eUe.Wg=function(){var e,t;return(t=this.ph().dk())||this.Pg().ik(t=(_0(),null==(e=zr(eNT(this.Tg())))?tgV:new AA(this,e))),t},eUe.Xg=function(e,t){return e},eUe.Yg=function(e){var t;return(t=e.Gj())?e.aj():edv(this.Tg(),e)},eUe.Zg=function(){var e;return(e=this.Pg())?e.fk():null},eUe.$g=function(){return this.Pg()?this.Pg().ck():null},eUe._g=function(e,t,n){return ebl(this,e,t,n)},eUe.ah=function(e){return JG(this,e)},eUe.bh=function(e,t){return ZN(this,e,t)},eUe.dh=function(){var e;return!!(e=this.Pg())&&e.gk()},eUe.eh=function(){throw p7(new bO)},eUe.fh=function(){return ehO(this)},eUe.gh=function(e,t,n,r){return ep0(this,e,t,r)},eUe.hh=function(e,t,n){var r;return(r=Pp(ee2(this.Tg(),t),66)).Nj().Qj(this,this.yh(),t-this.Ah(),e,n)},eUe.ih=function(e,t,n,r){return $7(this,e,t,r)},eUe.jh=function(e,t,n){var r;return(r=Pp(ee2(this.Tg(),t),66)).Nj().Rj(this,this.yh(),t-this.Ah(),e,n)},eUe.kh=function(){return!!this.Pg()&&!!this.Pg().ek()},eUe.lh=function(e){return epY(this,e)},eUe.mh=function(e){return zz(this,e)},eUe.oh=function(e){return eR2(this,e)},eUe.ph=function(){throw p7(new bO)},eUe.qh=function(){return this.Pg()?this.Pg().ek():null},eUe.rh=function(){return ehO(this)},eUe.sh=function(e,t){eS5(this,e,t)},eUe.th=function(e){this.ph().hk(e)},eUe.uh=function(e){this.ph().kk(e)},eUe.vh=function(e){this.ph().jk(e)},eUe.wh=function(e,t){var n,r,i,a;return(a=this.Zg())&&e&&(t=ep6(a.Vk(),this,t),a.Zk(this)),(r=this.eh())&&((eIy(this,this.eh(),this.Vg()).Bb&eH3)!=0?(i=r.fh())&&(e?a||i.Zk(this):i.Yk(this)):(t=(n=this.Vg())>=0?this.Qg(t):this.eh().ih(this,-1-n,null,t),t=this.Sg(null,-1,t))),this.uh(e),t},eUe.xh=function(e){var t,n,r,i,a,o,s,u;if((a=edv(n=this.Tg(),e))>=(t=this.Ah()))return Pp(e,66).Nj().Uj(this,this.yh(),a-t);if(a<=-1){if(o=eR3((eSp(),tvc),n,e)){if(_4(),Pp(o,66).Oj()||(o=Wk(QZ(tvc,o))),i=Pp((r=this.Yg(o))>=0?this._g(r,!0,!0):exk(this,o,!0),153),(u=o.Zj())>1||-1==u)return Pp(Pp(i,215).hl(e,!1),76)}else throw p7(new gL(eZV+e.ne()+eZX))}else if(e.$j())return Pp((r=this.Yg(e))>=0?this._g(r,!1,!0):exk(this,e,!1),76);return new k4(this,e)},eUe.yh=function(){return Q5(this)},eUe.zh=function(){return(BM(),tgv).S},eUe.Ah=function(){return Y1(this.zh())},eUe.Bh=function(e){eSi(this,e)},eUe.Ib=function(){return eMT(this)},Y5(eZ2,"BasicEObjectImpl",97),eTS(114,97,{105:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1}),eUe.Ch=function(e){var t;return(t=Q6(this))[e]},eUe.Dh=function(e,t){var n;n=Q6(this),Bc(n,e,t)},eUe.Eh=function(e){var t;t=Q6(this),Bc(t,e,null)},eUe.Jg=function(){return Pp(eaS(this,4),126)},eUe.Kg=function(){throw p7(new bO)},eUe.Lg=function(){return(4&this.Db)!=0},eUe.Pg=function(){throw p7(new bO)},eUe.Fh=function(e){ehU(this,2,e)},eUe.Rg=function(e,t){this.Db=t<<16|255&this.Db,this.Fh(e)},eUe.Tg=function(){return $S(this)},eUe.Vg=function(){return this.Db>>16},eUe.Wg=function(){var e,t;return _0(),null==(t=zr(eNT((e=Pp(eaS(this,16),26))||this.zh())))?tgV:new AA(this,t)},eUe.Mg=function(){return(1&this.Db)==0},eUe.Zg=function(){return Pp(eaS(this,128),1935)},eUe.$g=function(){return Pp(eaS(this,16),26)},eUe.dh=function(){return(32&this.Db)!=0},eUe.eh=function(){return Pp(eaS(this,2),49)},eUe.kh=function(){return(64&this.Db)!=0},eUe.ph=function(){throw p7(new bO)},eUe.qh=function(){return Pp(eaS(this,64),281)},eUe.th=function(e){ehU(this,16,e)},eUe.uh=function(e){ehU(this,128,e)},eUe.vh=function(e){ehU(this,64,e)},eUe.yh=function(){return ehH(this)},eUe.Db=0,Y5(eZ2,"MinimalEObjectImpl",114),eTS(115,114,{105:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1}),eUe.Fh=function(e){this.Cb=e},eUe.eh=function(){return this.Cb},Y5(eZ2,"MinimalEObjectImpl/Container",115),eTS(1985,115,{105:1,413:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1}),eUe._g=function(e,t,n){return egp(this,e,t,n)},eUe.jh=function(e,t,n){return e_9(this,e,t,n)},eUe.lh=function(e){return Wz(this,e)},eUe.sh=function(e,t){esU(this,e,t)},eUe.zh=function(){return eBa(),tm_},eUe.Bh=function(e){eoF(this,e)},eUe.Ve=function(){return epD(this)},eUe.We=function(e){return eT8(this,e)},eUe.Xe=function(e){return X2(this,e)},eUe.Ye=function(e,t){return ebu(this,e,t)},Y5(eZ3,"EMapPropertyHolderImpl",1985),eTS(567,115,{105:1,469:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1},oJ),eUe._g=function(e,t,n){switch(e){case 0:return this.a;case 1:return this.b}return ebl(this,e,t,n)},eUe.lh=function(e){switch(e){case 0:return 0!=this.a;case 1:return 0!=this.b}return epY(this,e)},eUe.sh=function(e,t){switch(e){case 0:ent(this,gP(LV(t)));return;case 1:enn(this,gP(LV(t)));return}eS5(this,e,t)},eUe.zh=function(){return eBa(),tmf},eUe.Bh=function(e){switch(e){case 0:ent(this,0);return;case 1:enn(this,0);return}eSi(this,e)},eUe.Ib=function(){var e;return(64&this.Db)!=0?eMT(this):(e=new O1(eMT(this)),e.a+=" (x: ",y$(e,this.a),e.a+=", y: ",y$(e,this.b),e.a+=")",e.a)},eUe.a=0,eUe.b=0,Y5(eZ3,"ElkBendPointImpl",567),eTS(723,1985,{105:1,413:1,160:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1}),eUe._g=function(e,t,n){return ec2(this,e,t,n)},eUe.hh=function(e,t,n){return ew0(this,e,t,n)},eUe.jh=function(e,t,n){return ea9(this,e,t,n)},eUe.lh=function(e){return eaT(this,e)},eUe.sh=function(e,t){eyb(this,e,t)},eUe.zh=function(){return eBa(),tmb},eUe.Bh=function(e){ecx(this,e)},eUe.zg=function(){return this.k},eUe.Ag=function(){return UB(this)},eUe.Ib=function(){return el4(this)},eUe.k=null,Y5(eZ3,"ElkGraphElementImpl",723),eTS(724,723,{105:1,413:1,160:1,470:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1}),eUe._g=function(e,t,n){return efN(this,e,t,n)},eUe.lh=function(e){return ef8(this,e)},eUe.sh=function(e,t){eym(this,e,t)},eUe.zh=function(){return eBa(),tmw},eUe.Bh=function(e){edS(this,e)},eUe.Bg=function(){return this.f},eUe.Cg=function(){return this.g},eUe.Dg=function(){return this.i},eUe.Eg=function(){return this.j},eUe.Fg=function(e,t){TN(this,e,t)},eUe.Gg=function(e,t){TP(this,e,t)},eUe.Hg=function(e){eno(this,e)},eUe.Ig=function(e){ens(this,e)},eUe.Ib=function(){return eEp(this)},eUe.f=0,eUe.g=0,eUe.i=0,eUe.j=0,Y5(eZ3,"ElkShapeImpl",724),eTS(725,724,{105:1,413:1,82:1,160:1,470:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1}),eUe._g=function(e,t,n){return ebQ(this,e,t,n)},eUe.hh=function(e,t,n){return evZ(this,e,t,n)},eUe.jh=function(e,t,n){return evX(this,e,t,n)},eUe.lh=function(e){return esM(this,e)},eUe.sh=function(e,t){eTH(this,e,t)},eUe.zh=function(){return eBa(),tmd},eUe.Bh=function(e){ep2(this,e)},eUe.xg=function(){return this.d||(this.d=new Ih(e6g,this,8,5)),this.d},eUe.yg=function(){return this.e||(this.e=new Ih(e6g,this,7,4)),this.e},Y5(eZ3,"ElkConnectableShapeImpl",725),eTS(352,723,{105:1,413:1,79:1,160:1,352:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1},oX),eUe.Qg=function(e){return evo(this,e)},eUe._g=function(e,t,n){switch(e){case 3:return zF(this);case 4:return this.b||(this.b=new Ih(e6m,this,4,7)),this.b;case 5:return this.c||(this.c=new Ih(e6m,this,5,8)),this.c;case 6:return this.a||(this.a=new FQ(e6v,this,6,6)),this.a;case 7:return OQ(),this.b||(this.b=new Ih(e6m,this,4,7)),!(this.b.i<=1)||(this.c||(this.c=new Ih(e6m,this,5,8)),!(this.c.i<=1));case 8:return OQ(),!!eTc(this);case 9:return OQ(),!!exb(this);case 10:return OQ(),this.b||(this.b=new Ih(e6m,this,4,7)),0!=this.b.i&&(this.c||(this.c=new Ih(e6m,this,5,8)),0!=this.c.i)}return ec2(this,e,t,n)},eUe.hh=function(e,t,n){var r;switch(t){case 3:return this.Cb&&(n=(r=this.Db>>16)>=0?evo(this,n):this.Cb.ih(this,-1-r,null,n)),Cu(this,Pp(e,33),n);case 4:return this.b||(this.b=new Ih(e6m,this,4,7)),edF(this.b,e,n);case 5:return this.c||(this.c=new Ih(e6m,this,5,8)),edF(this.c,e,n);case 6:return this.a||(this.a=new FQ(e6v,this,6,6)),edF(this.a,e,n)}return ew0(this,e,t,n)},eUe.jh=function(e,t,n){switch(t){case 3:return Cu(this,null,n);case 4:return this.b||(this.b=new Ih(e6m,this,4,7)),ep6(this.b,e,n);case 5:return this.c||(this.c=new Ih(e6m,this,5,8)),ep6(this.c,e,n);case 6:return this.a||(this.a=new FQ(e6v,this,6,6)),ep6(this.a,e,n)}return ea9(this,e,t,n)},eUe.lh=function(e){switch(e){case 3:return!!zF(this);case 4:return!!this.b&&0!=this.b.i;case 5:return!!this.c&&0!=this.c.i;case 6:return!!this.a&&0!=this.a.i;case 7:return this.b||(this.b=new Ih(e6m,this,4,7)),!(this.b.i<=1&&(this.c||(this.c=new Ih(e6m,this,5,8)),this.c.i<=1));case 8:return eTc(this);case 9:return exb(this);case 10:return this.b||(this.b=new Ih(e6m,this,4,7)),0!=this.b.i&&(this.c||(this.c=new Ih(e6m,this,5,8)),0!=this.c.i)}return eaT(this,e)},eUe.sh=function(e,t){switch(e){case 3:eOC(this,Pp(t,33));return;case 4:this.b||(this.b=new Ih(e6m,this,4,7)),eRT(this.b),this.b||(this.b=new Ih(e6m,this,4,7)),Y4(this.b,Pp(t,14));return;case 5:this.c||(this.c=new Ih(e6m,this,5,8)),eRT(this.c),this.c||(this.c=new Ih(e6m,this,5,8)),Y4(this.c,Pp(t,14));return;case 6:this.a||(this.a=new FQ(e6v,this,6,6)),eRT(this.a),this.a||(this.a=new FQ(e6v,this,6,6)),Y4(this.a,Pp(t,14));return}eyb(this,e,t)},eUe.zh=function(){return eBa(),tmh},eUe.Bh=function(e){switch(e){case 3:eOC(this,null);return;case 4:this.b||(this.b=new Ih(e6m,this,4,7)),eRT(this.b);return;case 5:this.c||(this.c=new Ih(e6m,this,5,8)),eRT(this.c);return;case 6:this.a||(this.a=new FQ(e6v,this,6,6)),eRT(this.a);return}ecx(this,e)},eUe.Ib=function(){return ePY(this)},Y5(eZ3,"ElkEdgeImpl",352),eTS(439,1985,{105:1,413:1,202:1,439:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1},oQ),eUe.Qg=function(e){return eg1(this,e)},eUe._g=function(e,t,n){switch(e){case 1:return this.j;case 2:return this.k;case 3:return this.b;case 4:return this.c;case 5:return this.a||(this.a=new O_(e6h,this,5)),this.a;case 6:return zB(this);case 7:if(t)return ebF(this);return this.i;case 8:if(t)return ebj(this);return this.f;case 9:return this.g||(this.g=new Ih(e6v,this,9,10)),this.g;case 10:return this.e||(this.e=new Ih(e6v,this,10,9)),this.e;case 11:return this.d}return egp(this,e,t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 6:return this.Cb&&(n=(i=this.Db>>16)>=0?eg1(this,n):this.Cb.ih(this,-1-i,null,n)),Cc(this,Pp(e,79),n);case 9:return this.g||(this.g=new Ih(e6v,this,9,10)),edF(this.g,e,n);case 10:return this.e||(this.e=new Ih(e6v,this,10,9)),edF(this.e,e,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBa(),tmp),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBa(),tmp)),e,n)},eUe.jh=function(e,t,n){switch(t){case 5:return this.a||(this.a=new O_(e6h,this,5)),ep6(this.a,e,n);case 6:return Cc(this,null,n);case 9:return this.g||(this.g=new Ih(e6v,this,9,10)),ep6(this.g,e,n);case 10:return this.e||(this.e=new Ih(e6v,this,10,9)),ep6(this.e,e,n)}return e_9(this,e,t,n)},eUe.lh=function(e){switch(e){case 1:return 0!=this.j;case 2:return 0!=this.k;case 3:return 0!=this.b;case 4:return 0!=this.c;case 5:return!!this.a&&0!=this.a.i;case 6:return!!zB(this);case 7:return!!this.i;case 8:return!!this.f;case 9:return!!this.g&&0!=this.g.i;case 10:return!!this.e&&0!=this.e.i;case 11:return null!=this.d}return Wz(this,e)},eUe.sh=function(e,t){switch(e){case 1:enu(this,gP(LV(t)));return;case 2:enl(this,gP(LV(t)));return;case 3:enr(this,gP(LV(t)));return;case 4:enc(this,gP(LV(t)));return;case 5:this.a||(this.a=new O_(e6h,this,5)),eRT(this.a),this.a||(this.a=new O_(e6h,this,5)),Y4(this.a,Pp(t,14));return;case 6:eOA(this,Pp(t,79));return;case 7:err(this,Pp(t,82));return;case 8:ern(this,Pp(t,82));return;case 9:this.g||(this.g=new Ih(e6v,this,9,10)),eRT(this.g),this.g||(this.g=new Ih(e6v,this,9,10)),Y4(this.g,Pp(t,14));return;case 10:this.e||(this.e=new Ih(e6v,this,10,9)),eRT(this.e),this.e||(this.e=new Ih(e6v,this,10,9)),Y4(this.e,Pp(t,14));return;case 11:erO(this,Lq(t));return}esU(this,e,t)},eUe.zh=function(){return eBa(),tmp},eUe.Bh=function(e){switch(e){case 1:enu(this,0);return;case 2:enl(this,0);return;case 3:enr(this,0);return;case 4:enc(this,0);return;case 5:this.a||(this.a=new O_(e6h,this,5)),eRT(this.a);return;case 6:eOA(this,null);return;case 7:err(this,null);return;case 8:ern(this,null);return;case 9:this.g||(this.g=new Ih(e6v,this,9,10)),eRT(this.g);return;case 10:this.e||(this.e=new Ih(e6v,this,10,9)),eRT(this.e);return;case 11:erO(this,null);return}eoF(this,e)},eUe.Ib=function(){return ex2(this)},eUe.b=0,eUe.c=0,eUe.d=null,eUe.j=0,eUe.k=0,Y5(eZ3,"ElkEdgeSectionImpl",439),eTS(150,115,{105:1,92:1,90:1,147:1,56:1,108:1,49:1,97:1,150:1,114:1,115:1}),eUe._g=function(e,t,n){var r;return 0==e?(this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab):Qt(this,e-Y1(this.zh()),ee2((r=Pp(eaS(this,16),26))||this.zh(),e),t,n)},eUe.hh=function(e,t,n){var r,i;return 0==t?(this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n)):(i=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Qj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.jh=function(e,t,n){var r,i;return 0==t?(this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n)):(i=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Rj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.lh=function(e){var t;return 0==e?!!this.Ab&&0!=this.Ab.i:VP(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.oh=function(e){return eF9(this,e)},eUe.sh=function(e,t){var n;if(0===e){this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return}efL(this,e-Y1(this.zh()),ee2((n=Pp(eaS(this,16),26))||this.zh(),e),t)},eUe.uh=function(e){ehU(this,128,e)},eUe.zh=function(){return eBK(),tgL},eUe.Bh=function(e){var t;if(0===e){this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return}ec6(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.Gh=function(){this.Bb|=1},eUe.Hh=function(e){return eDM(this,e)},eUe.Bb=0,Y5(eZ2,"EModelElementImpl",150),eTS(704,150,{105:1,92:1,90:1,471:1,147:1,56:1,108:1,49:1,97:1,150:1,114:1,115:1},cQ),eUe.Ih=function(e,t){return ejZ(this,e,t)},eUe.Jh=function(e){var t,n,r,i,a;if(this.a!=etP(e)||(256&e.Bb)!=0)throw p7(new gL(eZ7+e.zb+eZ6));for(r=$E(e);0!=qt(r.a).i;){if(n=Pp(ejc(r,0,(a=(t=Pp(etj(qt(r.a),0),87)).c,M4(a,88)?Pp(a,26):(eBK(),tgI))),26),em4(n))return i=etP(n).Nh().Jh(n),Pp(i,49).th(e),i;r=$E(n)}return(null!=e.D?e.D:e.B)=="java.util.Map$Entry"?new RO(e):new Pq(e)},eUe.Kh=function(e,t){return eBd(this,e,t)},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.a}return Qt(this,e-Y1((eBK(),tgM)),ee2((r=Pp(eaS(this,16),26))||tgM,e),t,n)},eUe.hh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 1:return this.a&&(n=Pp(this.a,49).ih(this,4,e6E,n)),ecb(this,Pp(e,235),n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgM),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgM)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 1:return ecb(this,null,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgM),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgM)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return!!this.a}return VP(this,e-Y1((eBK(),tgM)),ee2((t=Pp(eaS(this,16),26))||tgM,e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:e_B(this,Pp(t,235));return}efL(this,e-Y1((eBK(),tgM)),ee2((n=Pp(eaS(this,16),26))||tgM,e),t)},eUe.zh=function(){return eBK(),tgM},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:e_B(this,null);return}ec6(this,e-Y1((eBK(),tgM)),ee2((t=Pp(eaS(this,16),26))||tgM,e))},Y5(eZ2,"EFactoryImpl",704),eTS(eXt,704,{105:1,2014:1,92:1,90:1,471:1,147:1,56:1,108:1,49:1,97:1,150:1,114:1,115:1},o1),eUe.Ih=function(e,t){switch(e.yj()){case 12:return Pp(t,146).tg();case 13:return efF(t);default:throw p7(new gL(eZ5+e.ne()+eZ6))}},eUe.Jh=function(e){var t;switch(-1==e.G&&(e.G=(t=etP(e))?ebv(t.Mh(),e):-1),e.G){case 4:return new o0;case 6:return new mS;case 7:return new mk;case 8:return new oX;case 9:return new oJ;case 10:return new oQ;case 11:return new o3;default:throw p7(new gL(eZ7+e.zb+eZ6))}},eUe.Kh=function(e,t){switch(e.yj()){case 13:case 12:return null;default:throw p7(new gL(eZ5+e.ne()+eZ6))}},Y5(eZ3,"ElkGraphFactoryImpl",eXt),eTS(438,150,{105:1,92:1,90:1,147:1,191:1,56:1,108:1,49:1,97:1,150:1,114:1,115:1}),eUe.Wg=function(){var e,t;return null==(t=zr(eNT((e=Pp(eaS(this,16),26))||this.zh())))?(_0(),_0(),tgV):new Lg(this,t)},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.ne()}return Qt(this,e-Y1(this.zh()),ee2((r=Pp(eaS(this,16),26))||this.zh(),e),t,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb}return VP(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:this.Lh(Lq(t));return}efL(this,e-Y1(this.zh()),ee2((n=Pp(eaS(this,16),26))||this.zh(),e),t)},eUe.zh=function(){return eBK(),tgC},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:this.Lh(null);return}ec6(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.ne=function(){return this.zb},eUe.Lh=function(e){er3(this,e)},eUe.Ib=function(){return ecF(this)},eUe.zb=null,Y5(eZ2,"ENamedElementImpl",438),eTS(179,438,{105:1,92:1,90:1,147:1,191:1,56:1,235:1,108:1,49:1,97:1,150:1,179:1,114:1,115:1,675:1},$y),eUe.Qg=function(e){return eg5(this,e)},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.yb;case 3:return this.xb;case 4:return this.sb;case 5:return this.rb||(this.rb=new Fq(this,tm8,this)),this.rb;case 6:return this.vb||(this.vb=new Ia(e6E,this,6,7)),this.vb;case 7:if(t)return this.Db>>16==7?Pp(this.Cb,235):null;return zU(this)}return Qt(this,e-Y1((eBK(),tgP)),ee2((r=Pp(eaS(this,16),26))||tgP,e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 4:return this.sb&&(n=Pp(this.sb,49).ih(this,1,e6w,n)),ecY(this,Pp(e,471),n);case 5:return this.rb||(this.rb=new Fq(this,tm8,this)),edF(this.rb,e,n);case 6:return this.vb||(this.vb=new Ia(e6E,this,6,7)),edF(this.vb,e,n);case 7:return this.Cb&&(n=(i=this.Db>>16)>=0?eg5(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,7,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgP),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgP)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 4:return ecY(this,null,n);case 5:return this.rb||(this.rb=new Fq(this,tm8,this)),ep6(this.rb,e,n);case 6:return this.vb||(this.vb=new Ia(e6E,this,6,7)),ep6(this.vb,e,n);case 7:return eDg(this,null,7,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgP),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgP)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return null!=this.yb;case 3:return null!=this.xb;case 4:return!!this.sb;case 5:return!!this.rb&&0!=this.rb.i;case 6:return!!this.vb&&0!=this.vb.i;case 7:return!!zU(this)}return VP(this,e-Y1((eBK(),tgP)),ee2((t=Pp(eaS(this,16),26))||tgP,e))},eUe.oh=function(e){var t;return(t=eAd(this,e))||eF9(this,e)},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:er3(this,Lq(t));return;case 2:er5(this,Lq(t));return;case 3:er4(this,Lq(t));return;case 4:e_8(this,Pp(t,471));return;case 5:this.rb||(this.rb=new Fq(this,tm8,this)),eRT(this.rb),this.rb||(this.rb=new Fq(this,tm8,this)),Y4(this.rb,Pp(t,14));return;case 6:this.vb||(this.vb=new Ia(e6E,this,6,7)),eRT(this.vb),this.vb||(this.vb=new Ia(e6E,this,6,7)),Y4(this.vb,Pp(t,14));return}efL(this,e-Y1((eBK(),tgP)),ee2((n=Pp(eaS(this,16),26))||tgP,e),t)},eUe.vh=function(e){var t,n;if(e&&this.rb)for(n=new Ow(this.rb);n.e!=n.i.gc();)t=epH(n),M4(t,351)&&(Pp(t,351).w=null);ehU(this,64,e)},eUe.zh=function(){return eBK(),tgP},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:er3(this,null);return;case 2:er5(this,null);return;case 3:er4(this,null);return;case 4:e_8(this,null);return;case 5:this.rb||(this.rb=new Fq(this,tm8,this)),eRT(this.rb);return;case 6:this.vb||(this.vb=new Ia(e6E,this,6,7)),eRT(this.vb);return}ec6(this,e-Y1((eBK(),tgP)),ee2((t=Pp(eaS(this,16),26))||tgP,e))},eUe.Gh=function(){egb(this)},eUe.Mh=function(){return this.rb||(this.rb=new Fq(this,tm8,this)),this.rb},eUe.Nh=function(){return this.sb},eUe.Oh=function(){return this.ub},eUe.Ph=function(){return this.xb},eUe.Qh=function(){return this.yb},eUe.Rh=function(e){this.ub=e},eUe.Ib=function(){var e;return(64&this.Db)!=0?ecF(this):(e=new O1(ecF(this)),e.a+=" (nsURI: ",xk(e,this.yb),e.a+=", nsPrefix: ",xk(e,this.xb),e.a+=")",e.a)},eUe.xb=null,eUe.yb=null,Y5(eZ2,"EPackageImpl",179),eTS(555,179,{105:1,2016:1,555:1,92:1,90:1,147:1,191:1,56:1,235:1,108:1,49:1,97:1,150:1,179:1,114:1,115:1,675:1},eTv),eUe.q=!1,eUe.r=!1;var e6T=!1;Y5(eZ3,"ElkGraphPackageImpl",555),eTS(354,724,{105:1,413:1,160:1,137:1,470:1,354:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1},o0),eUe.Qg=function(e){return eg0(this,e)},eUe._g=function(e,t,n){switch(e){case 7:return zH(this);case 8:return this.a}return efN(this,e,t,n)},eUe.hh=function(e,t,n){var r;return 7===t?(this.Cb&&(n=(r=this.Db>>16)>=0?eg0(this,n):this.Cb.ih(this,-1-r,null,n)),j2(this,Pp(e,160),n)):ew0(this,e,t,n)},eUe.jh=function(e,t,n){return 7==t?j2(this,null,n):ea9(this,e,t,n)},eUe.lh=function(e){switch(e){case 7:return!!zH(this);case 8:return!IE("",this.a)}return ef8(this,e)},eUe.sh=function(e,t){switch(e){case 7:eAu(this,Pp(t,160));return;case 8:eri(this,Lq(t));return}eym(this,e,t)},eUe.zh=function(){return eBa(),tmm},eUe.Bh=function(e){switch(e){case 7:eAu(this,null);return;case 8:eri(this,"");return}edS(this,e)},eUe.Ib=function(){return eE1(this)},eUe.a="",Y5(eZ3,"ElkLabelImpl",354),eTS(239,725,{105:1,413:1,82:1,160:1,33:1,470:1,239:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1},mS),eUe.Qg=function(e){return evs(this,e)},eUe._g=function(e,t,n){switch(e){case 9:return this.c||(this.c=new FQ(e6x,this,9,9)),this.c;case 10:return this.a||(this.a=new FQ(e6k,this,10,11)),this.a;case 11:return z$(this);case 12:return this.b||(this.b=new FQ(e6g,this,12,3)),this.b;case 13:return OQ(),this.a||(this.a=new FQ(e6k,this,10,11)),this.a.i>0}return ebQ(this,e,t,n)},eUe.hh=function(e,t,n){var r;switch(t){case 9:return this.c||(this.c=new FQ(e6x,this,9,9)),edF(this.c,e,n);case 10:return this.a||(this.a=new FQ(e6k,this,10,11)),edF(this.a,e,n);case 11:return this.Cb&&(n=(r=this.Db>>16)>=0?evs(this,n):this.Cb.ih(this,-1-r,null,n)),C4(this,Pp(e,33),n);case 12:return this.b||(this.b=new FQ(e6g,this,12,3)),edF(this.b,e,n)}return evZ(this,e,t,n)},eUe.jh=function(e,t,n){switch(t){case 9:return this.c||(this.c=new FQ(e6x,this,9,9)),ep6(this.c,e,n);case 10:return this.a||(this.a=new FQ(e6k,this,10,11)),ep6(this.a,e,n);case 11:return C4(this,null,n);case 12:return this.b||(this.b=new FQ(e6g,this,12,3)),ep6(this.b,e,n)}return evX(this,e,t,n)},eUe.lh=function(e){switch(e){case 9:return!!this.c&&0!=this.c.i;case 10:return!!this.a&&0!=this.a.i;case 11:return!!z$(this);case 12:return!!this.b&&0!=this.b.i;case 13:return this.a||(this.a=new FQ(e6k,this,10,11)),this.a.i>0}return esM(this,e)},eUe.sh=function(e,t){switch(e){case 9:this.c||(this.c=new FQ(e6x,this,9,9)),eRT(this.c),this.c||(this.c=new FQ(e6x,this,9,9)),Y4(this.c,Pp(t,14));return;case 10:this.a||(this.a=new FQ(e6k,this,10,11)),eRT(this.a),this.a||(this.a=new FQ(e6k,this,10,11)),Y4(this.a,Pp(t,14));return;case 11:eO$(this,Pp(t,33));return;case 12:this.b||(this.b=new FQ(e6g,this,12,3)),eRT(this.b),this.b||(this.b=new FQ(e6g,this,12,3)),Y4(this.b,Pp(t,14));return}eTH(this,e,t)},eUe.zh=function(){return eBa(),tmg},eUe.Bh=function(e){switch(e){case 9:this.c||(this.c=new FQ(e6x,this,9,9)),eRT(this.c);return;case 10:this.a||(this.a=new FQ(e6k,this,10,11)),eRT(this.a);return;case 11:eO$(this,null);return;case 12:this.b||(this.b=new FQ(e6g,this,12,3)),eRT(this.b);return}ep2(this,e)},eUe.Ib=function(){return eC4(this)},Y5(eZ3,"ElkNodeImpl",239),eTS(186,725,{105:1,413:1,82:1,160:1,118:1,470:1,186:1,94:1,92:1,90:1,56:1,108:1,49:1,97:1,114:1,115:1},mk),eUe.Qg=function(e){return eg2(this,e)},eUe._g=function(e,t,n){return 9==e?zY(this):ebQ(this,e,t,n)},eUe.hh=function(e,t,n){var r;return 9===t?(this.Cb&&(n=(r=this.Db>>16)>=0?eg2(this,n):this.Cb.ih(this,-1-r,null,n)),Cl(this,Pp(e,33),n)):evZ(this,e,t,n)},eUe.jh=function(e,t,n){return 9==t?Cl(this,null,n):evX(this,e,t,n)},eUe.lh=function(e){return 9==e?!!zY(this):esM(this,e)},eUe.sh=function(e,t){if(9===e){eOL(this,Pp(t,33));return}eTH(this,e,t)},eUe.zh=function(){return eBa(),tmv},eUe.Bh=function(e){if(9===e){eOL(this,null);return}ep2(this,e)},eUe.Ib=function(){return eC5(this)},Y5(eZ3,"ElkPortImpl",186);var e6M=RL(eX_,"BasicEMap/Entry");eTS(1092,115,{105:1,42:1,92:1,90:1,133:1,56:1,108:1,49:1,97:1,114:1,115:1},o3),eUe.Fb=function(e){return this===e},eUe.cd=function(){return this.b},eUe.Hb=function(){return Ao(this)},eUe.Uh=function(e){era(this,Pp(e,146))},eUe._g=function(e,t,n){switch(e){case 0:return this.b;case 1:return this.c}return ebl(this,e,t,n)},eUe.lh=function(e){switch(e){case 0:return!!this.b;case 1:return null!=this.c}return epY(this,e)},eUe.sh=function(e,t){switch(e){case 0:era(this,Pp(t,146));return;case 1:eru(this,t);return}eS5(this,e,t)},eUe.zh=function(){return eBa(),tmy},eUe.Bh=function(e){switch(e){case 0:era(this,null);return;case 1:eru(this,null);return}eSi(this,e)},eUe.Sh=function(){var e;return -1==this.a&&(e=this.b,this.a=e?esj(e):0),this.a},eUe.dd=function(){return this.c},eUe.Th=function(e){this.a=e},eUe.ed=function(e){var t;return t=this.c,eru(this,e),t},eUe.Ib=function(){var e;return(64&this.Db)!=0?eMT(this):(xM(xM(xM(e=new vc,this.b?this.b.tg():eUg),eGH),Ae(this.c)),e.a)},eUe.a=-1,eUe.c=null;var e6O=Y5(eZ3,"ElkPropertyToValueMapEntryImpl",1092);eTS(984,1,{},o6),Y5(eXk,"JsonAdapter",984),eTS(210,60,eHr,gK),Y5(eXk,"JsonImportException",210),eTS(857,1,{},eg6),Y5(eXk,"JsonImporter",857),eTS(891,1,{},kP),Y5(eXk,"JsonImporter/lambda$0$Type",891),eTS(892,1,{},kR),Y5(eXk,"JsonImporter/lambda$1$Type",892),eTS(900,1,{},h7),Y5(eXk,"JsonImporter/lambda$10$Type",900),eTS(902,1,{},kj),Y5(eXk,"JsonImporter/lambda$11$Type",902),eTS(903,1,{},kF),Y5(eXk,"JsonImporter/lambda$12$Type",903),eTS(909,1,{},HE),Y5(eXk,"JsonImporter/lambda$13$Type",909),eTS(908,1,{},H_),Y5(eXk,"JsonImporter/lambda$14$Type",908),eTS(904,1,{},kY),Y5(eXk,"JsonImporter/lambda$15$Type",904),eTS(905,1,{},kB),Y5(eXk,"JsonImporter/lambda$16$Type",905),eTS(906,1,{},kU),Y5(eXk,"JsonImporter/lambda$17$Type",906),eTS(907,1,{},kH),Y5(eXk,"JsonImporter/lambda$18$Type",907),eTS(912,1,{},pe),Y5(eXk,"JsonImporter/lambda$19$Type",912),eTS(893,1,{},pt),Y5(eXk,"JsonImporter/lambda$2$Type",893),eTS(910,1,{},pn),Y5(eXk,"JsonImporter/lambda$20$Type",910),eTS(911,1,{},pr),Y5(eXk,"JsonImporter/lambda$21$Type",911),eTS(915,1,{},pi),Y5(eXk,"JsonImporter/lambda$22$Type",915),eTS(913,1,{},pa),Y5(eXk,"JsonImporter/lambda$23$Type",913),eTS(914,1,{},po),Y5(eXk,"JsonImporter/lambda$24$Type",914),eTS(917,1,{},ps),Y5(eXk,"JsonImporter/lambda$25$Type",917),eTS(916,1,{},pu),Y5(eXk,"JsonImporter/lambda$26$Type",916),eTS(918,1,eUF,k$),eUe.td=function(e){JH(this.b,this.a,Lq(e))},Y5(eXk,"JsonImporter/lambda$27$Type",918),eTS(919,1,eUF,kz),eUe.td=function(e){J$(this.b,this.a,Lq(e))},Y5(eXk,"JsonImporter/lambda$28$Type",919),eTS(920,1,{},kG),Y5(eXk,"JsonImporter/lambda$29$Type",920),eTS(896,1,{},pc),Y5(eXk,"JsonImporter/lambda$3$Type",896),eTS(921,1,{},kW),Y5(eXk,"JsonImporter/lambda$30$Type",921),eTS(922,1,{},pl),Y5(eXk,"JsonImporter/lambda$31$Type",922),eTS(923,1,{},pf),Y5(eXk,"JsonImporter/lambda$32$Type",923),eTS(924,1,{},pd),Y5(eXk,"JsonImporter/lambda$33$Type",924),eTS(925,1,{},ph),Y5(eXk,"JsonImporter/lambda$34$Type",925),eTS(859,1,{},pp),Y5(eXk,"JsonImporter/lambda$35$Type",859),eTS(929,1,{},N8),Y5(eXk,"JsonImporter/lambda$36$Type",929),eTS(926,1,eUF,pb),eUe.td=function(e){qW(this.a,Pp(e,469))},Y5(eXk,"JsonImporter/lambda$37$Type",926),eTS(927,1,eUF,k0),eUe.td=function(e){xC(this.a,this.b,Pp(e,202))},Y5(eXk,"JsonImporter/lambda$38$Type",927),eTS(928,1,eUF,k2),eUe.td=function(e){xI(this.a,this.b,Pp(e,202))},Y5(eXk,"JsonImporter/lambda$39$Type",928),eTS(894,1,{},pm),Y5(eXk,"JsonImporter/lambda$4$Type",894),eTS(930,1,eUF,pg),eUe.td=function(e){qK(this.a,Pp(e,8))},Y5(eXk,"JsonImporter/lambda$40$Type",930),eTS(895,1,{},pv),Y5(eXk,"JsonImporter/lambda$5$Type",895),eTS(899,1,{},py),Y5(eXk,"JsonImporter/lambda$6$Type",899),eTS(897,1,{},pw),Y5(eXk,"JsonImporter/lambda$7$Type",897),eTS(898,1,{},p_),Y5(eXk,"JsonImporter/lambda$8$Type",898),eTS(901,1,{},pE),Y5(eXk,"JsonImporter/lambda$9$Type",901),eTS(948,1,eUF,pS),eUe.td=function(e){BC(this.a,new B_(Lq(e)))},Y5(eXk,"JsonMetaDataConverter/lambda$0$Type",948),eTS(949,1,eUF,pk),eUe.td=function(e){Bm(this.a,Pp(e,237))},Y5(eXk,"JsonMetaDataConverter/lambda$1$Type",949),eTS(950,1,eUF,px),eUe.td=function(e){GR(this.a,Pp(e,149))},Y5(eXk,"JsonMetaDataConverter/lambda$2$Type",950),eTS(951,1,eUF,pT),eUe.td=function(e){Bg(this.a,Pp(e,175))},Y5(eXk,"JsonMetaDataConverter/lambda$3$Type",951),eTS(237,22,{3:1,35:1,22:1,237:1},k1);var e6A=enw(ezx,"GraphFeature",237,e1G,etM,N1);eTS(13,1,{35:1,146:1},pO,Cm,xX,T2),eUe.wd=function(e){return Oo(this,Pp(e,146))},eUe.Fb=function(e){return $k(this,e)},eUe.wg=function(){return epB(this)},eUe.tg=function(){return this.b},eUe.Hb=function(){return ebA(this.b)},eUe.Ib=function(){return this.b},Y5(ezx,"Property",13),eTS(818,1,e$C,pM),eUe.ue=function(e,t){return elW(this,Pp(e,94),Pp(t,94))},eUe.Fb=function(e){return this===e},eUe.ve=function(){return new fZ(this)},Y5(ezx,"PropertyHolderComparator",818),eTS(695,1,eUE,pL),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){return JZ(this)},eUe.Qb=function(){yI()},eUe.Ob=function(){return!!this.a},Y5(eXY,"ElkGraphUtil/AncestorIterator",695);var e6L=RL(eX_,"EList");eTS(67,52,{20:1,28:1,52:1,14:1,15:1,67:1,58:1}),eUe.Vc=function(e,t){elm(this,e,t)},eUe.Fc=function(e){return JL(this,e)},eUe.Wc=function(e,t){return eo0(this,e,t)},eUe.Gc=function(e){return Y4(this,e)},eUe.Zh=function(){return new AY(this)},eUe.$h=function(){return new AB(this)},eUe._h=function(e){return enH(this,e)},eUe.ai=function(){return!0},eUe.bi=function(e,t){},eUe.ci=function(){},eUe.di=function(e,t){X8(this,e,t)},eUe.ei=function(e,t,n){},eUe.fi=function(e,t){},eUe.gi=function(e,t,n){},eUe.Fb=function(e){return eCc(this,e)},eUe.Hb=function(){return eov(this)},eUe.hi=function(){return!1},eUe.Kc=function(){return new Ow(this)},eUe.Yc=function(){return new AF(this)},eUe.Zc=function(e){var t;if(t=this.gc(),e<0||e>t)throw p7(new Ii(e,t));return new YC(this,e)},eUe.ji=function(e,t){this.ii(e,this.Xc(t))},eUe.Mc=function(e){return eeu(this,e)},eUe.li=function(e,t){return t},eUe._c=function(e,t){return eby(this,e,t)},eUe.Ib=function(){return efq(this)},eUe.ni=function(){return!0},eUe.oi=function(e,t){return euu(this,t)},Y5(eX_,"AbstractEList",67),eTS(63,67,eXz,o7,eta,eiP),eUe.Vh=function(e,t){return ew2(this,e,t)},eUe.Wh=function(e){return emp(this,e)},eUe.Xh=function(e,t){ecW(this,e,t)},eUe.Yh=function(e){Zz(this,e)},eUe.pi=function(e){return J5(this,e)},eUe.$b=function(){ZG(this)},eUe.Hc=function(e){return ev9(this,e)},eUe.Xb=function(e){return etj(this,e)},eUe.qi=function(e){var t,n,r;++this.j,e>(n=null==this.g?0:this.g.length)&&(r=this.g,(t=n+(n/2|0)+4)=0&&(this.$c(t),!0)},eUe.mi=function(e,t){return this.Ui(e,this.oi(e,t))},eUe.gc=function(){return this.Vi()},eUe.Pc=function(){return this.Wi()},eUe.Qc=function(e){return this.Xi(e)},eUe.Ib=function(){return this.Yi()},Y5(eX_,"DelegatingEList",1995),eTS(1996,1995,eJk),eUe.Vh=function(e,t){return eD1(this,e,t)},eUe.Wh=function(e){return this.Vh(this.Vi(),e)},eUe.Xh=function(e,t){eTf(this,e,t)},eUe.Yh=function(e){exq(this,e)},eUe.ai=function(){return!this.bj()},eUe.$b=function(){eRP(this)},eUe.Zi=function(e,t,n,r,i){return new $P(this,e,t,n,r,i)},eUe.$i=function(e){eam(this.Ai(),e)},eUe._i=function(){return null},eUe.aj=function(){return -1},eUe.Ai=function(){return null},eUe.bj=function(){return!1},eUe.cj=function(e,t){return t},eUe.dj=function(e,t){return t},eUe.ej=function(){return!1},eUe.fj=function(){return!this.Ri()},eUe.ii=function(e,t){var n,r;return this.ej()?(r=this.fj(),n=e_R(this,e,t),this.$i(this.Zi(7,ell(t),n,e,r)),n):e_R(this,e,t)},eUe.$c=function(e){var t,n,r,i;return this.ej()?(n=null,r=this.fj(),t=this.Zi(4,i=RC(this,e),null,e,r),this.bj()&&i?(n=this.dj(i,n))?(n.Ei(t),n.Fi()):this.$i(t):n?(n.Ei(t),n.Fi()):this.$i(t),i):(i=RC(this,e),this.bj()&&i&&(n=this.dj(i,null))&&n.Fi(),i)},eUe.mi=function(e,t){return eD0(this,e,t)},Y5(eZK,"DelegatingNotifyingListImpl",1996),eTS(143,1,eJx),eUe.Ei=function(e){return ey7(this,e)},eUe.Fi=function(){QU(this)},eUe.xi=function(){return this.d},eUe._i=function(){return null},eUe.gj=function(){return null},eUe.yi=function(e){return -1},eUe.zi=function(){return eLo(this)},eUe.Ai=function(){return null},eUe.Bi=function(){return eLs(this)},eUe.Ci=function(){return this.o<0?this.o<-2?-2-this.o-1:-1:this.o},eUe.hj=function(){return!1},eUe.Di=function(e){var t,n,r,i,a,o,s,u,c,l,f;switch(this.d){case 1:case 2:switch(i=e.xi()){case 1:case 2:if(xc(a=e.Ai())===xc(this.Ai())&&this.yi(null)==e.yi(null))return this.g=e.zi(),1==e.xi()&&(this.d=1),!0}case 4:if(4===(i=e.xi())&&xc(a=e.Ai())===xc(this.Ai())&&this.yi(null)==e.yi(null))return c=eju(this),u=this.o<0?this.o<-2?-2-this.o-1:-1:this.o,o=e.Ci(),this.d=6,f=new eta(2),u<=o?(JL(f,this.n),JL(f,e.Bi()),this.g=eow(vx(ty_,1),eHT,25,15,[this.o=u,o+1])):(JL(f,e.Bi()),JL(f,this.n),this.g=eow(vx(ty_,1),eHT,25,15,[this.o=o,u])),this.n=f,c||(this.o=-2-this.o-1),!0;break;case 6:if(4===(i=e.xi())&&xc(a=e.Ai())===xc(this.Ai())&&this.yi(null)==e.yi(null)){for(c=eju(this),o=e.Ci(),r=Je(ty_,eHT,25,(l=Pp(this.g,48)).length+1,15,1),t=0;t>>0).toString(16)),r.a+=" (eventType: ",this.d){case 1:r.a+="SET";break;case 2:r.a+="UNSET";break;case 3:r.a+="ADD";break;case 5:r.a+="ADD_MANY";break;case 4:r.a+="REMOVE";break;case 6:r.a+="REMOVE_MANY";break;case 7:r.a+="MOVE";break;case 8:r.a+="REMOVING_ADAPTER";break;case 9:r.a+="RESOLVE";break;default:yz(r,this.d)}if(eIb(this)&&(r.a+=", touch: true"),r.a+=", position: ",yz(r,this.o<0?this.o<-2?-2-this.o-1:-1:this.o),r.a+=", notifier: ",xS(r,this.Ai()),r.a+=", feature: ",xS(r,this._i()),r.a+=", oldValue: ",xS(r,eLs(this)),r.a+=", newValue: ",6==this.d&&M4(this.g,48)){for(n=Pp(this.g,48),r.a+="[",e=0;e10?(this.b&&this.c.j==this.a||(this.b=new Rq(this),this.a=this.j),w0(this.b,e)):ev9(this,e)},eUe.ni=function(){return!0},eUe.a=0,Y5(eX_,"AbstractEList/1",953),eTS(295,73,eHZ,Ii),Y5(eX_,"AbstractEList/BasicIndexOutOfBoundsException",295),eTS(40,1,eUE,Ow),eUe.Nb=function(e){F8(this,e)},eUe.mj=function(){if(this.i.j!=this.f)throw p7(new bA)},eUe.nj=function(){return epH(this)},eUe.Ob=function(){return this.e!=this.i.gc()},eUe.Pb=function(){return this.nj()},eUe.Qb=function(){ey_(this)},eUe.e=0,eUe.f=0,eUe.g=-1,Y5(eX_,"AbstractEList/EIterator",40),eTS(278,40,eUC,AF,YC),eUe.Qb=function(){ey_(this)},eUe.Rb=function(e){edq(this,e)},eUe.oj=function(){var e;try{return e=this.d.Xb(--this.e),this.mj(),this.g=this.e,e}catch(t){if(t=eoa(t),M4(t,73))throw this.mj(),p7(new bC);throw p7(t)}},eUe.pj=function(e){emE(this,e)},eUe.Sb=function(){return 0!=this.e},eUe.Tb=function(){return this.e},eUe.Ub=function(){return this.oj()},eUe.Vb=function(){return this.e-1},eUe.Wb=function(e){this.pj(e)},Y5(eX_,"AbstractEList/EListIterator",278),eTS(341,40,eUE,AY),eUe.nj=function(){return ep$(this)},eUe.Qb=function(){throw p7(new bO)},Y5(eX_,"AbstractEList/NonResolvingEIterator",341),eTS(385,278,eUC,AB,IB),eUe.Rb=function(e){throw p7(new bO)},eUe.nj=function(){var e;try{return e=this.c.ki(this.e),this.mj(),this.g=this.e++,e}catch(t){if(t=eoa(t),M4(t,73))throw this.mj(),p7(new bC);throw p7(t)}},eUe.oj=function(){var e;try{return e=this.c.ki(--this.e),this.mj(),this.g=this.e,e}catch(t){if(t=eoa(t),M4(t,73))throw this.mj(),p7(new bC);throw p7(t)}},eUe.Qb=function(){throw p7(new bO)},eUe.Wb=function(e){throw p7(new bO)},Y5(eX_,"AbstractEList/NonResolvingEListIterator",385),eTS(1982,67,eJO),eUe.Vh=function(e,t){var n,r,i,a,o,s,u,c,l,f,d;if(0==(i=t.gc()))return++this.j,!1;for(r=eue(this,d=(l=null==(c=Pp(eaS(this.a,4),126))?0:c.length)+i),(f=l-e)>0&&ePD(c,e,r,e+i,f),u=t.Kc(),o=0;on)throw p7(new Ii(e,n));return new Uu(this,e)},eUe.$b=function(){var e,t;++this.j,t=null==(e=Pp(eaS(this.a,4),126))?0:e.length,eps(this,null),X8(this,t,e)},eUe.Hc=function(e){var t,n,r,i,a;if(null!=(t=Pp(eaS(this.a,4),126))){if(null!=e){for(i=0,a=(r=t).length;i=(n=null==(t=Pp(eaS(this.a,4),126))?0:t.length))throw p7(new Ii(e,n));return t[e]},eUe.Xc=function(e){var t,n,r;if(null!=(t=Pp(eaS(this.a,4),126))){if(null!=e){for(n=0,r=t.length;nn)throw p7(new Ii(e,n));return new Us(this,e)},eUe.ii=function(e,t){var n,r,i;if(i=null==(n=ehc(this))?0:n.length,e>=i)throw p7(new gE(eXU+e+eXH+i));if(t>=i)throw p7(new gE(eX$+t+eXH+i));return r=n[t],e!=t&&(e0&&ePD(e,0,t,0,n),t},eUe.Qc=function(e){var t,n,r;return(r=null==(t=Pp(eaS(this.a,4),126))?0:t.length)>0&&(e.lengthr&&Bc(e,r,null),e},Y5(eX_,"ArrayDelegatingEList",1982),eTS(1038,40,eUE,Zl),eUe.mj=function(){if(this.b.j!=this.f||xc(Pp(eaS(this.b.a,4),126))!==xc(this.a))throw p7(new bA)},eUe.Qb=function(){ey_(this),this.a=Pp(eaS(this.b.a,4),126)},Y5(eX_,"ArrayDelegatingEList/EIterator",1038),eTS(706,278,eUC,FK,Us),eUe.mj=function(){if(this.b.j!=this.f||xc(Pp(eaS(this.b.a,4),126))!==xc(this.a))throw p7(new bA)},eUe.pj=function(e){emE(this,e),this.a=Pp(eaS(this.b.a,4),126)},eUe.Qb=function(){ey_(this),this.a=Pp(eaS(this.b.a,4),126)},Y5(eX_,"ArrayDelegatingEList/EListIterator",706),eTS(1039,341,eUE,Zf),eUe.mj=function(){if(this.b.j!=this.f||xc(Pp(eaS(this.b.a,4),126))!==xc(this.a))throw p7(new bA)},Y5(eX_,"ArrayDelegatingEList/NonResolvingEIterator",1039),eTS(707,385,eUC,FV,Uu),eUe.mj=function(){if(this.b.j!=this.f||xc(Pp(eaS(this.b.a,4),126))!==xc(this.a))throw p7(new bA)},Y5(eX_,"ArrayDelegatingEList/NonResolvingEListIterator",707),eTS(606,295,eHZ,xJ),Y5(eX_,"BasicEList/BasicIndexOutOfBoundsException",606),eTS(696,63,eXz,xt),eUe.Vc=function(e,t){throw p7(new bO)},eUe.Fc=function(e){throw p7(new bO)},eUe.Wc=function(e,t){throw p7(new bO)},eUe.Gc=function(e){throw p7(new bO)},eUe.$b=function(){throw p7(new bO)},eUe.qi=function(e){throw p7(new bO)},eUe.Kc=function(){return this.Zh()},eUe.Yc=function(){return this.$h()},eUe.Zc=function(e){return this._h(e)},eUe.ii=function(e,t){throw p7(new bO)},eUe.ji=function(e,t){throw p7(new bO)},eUe.$c=function(e){throw p7(new bO)},eUe.Mc=function(e){throw p7(new bO)},eUe._c=function(e,t){throw p7(new bO)},Y5(eX_,"BasicEList/UnmodifiableEList",696),eTS(705,1,{3:1,20:1,14:1,15:1,58:1,589:1}),eUe.Vc=function(e,t){Mq(this,e,Pp(t,42))},eUe.Fc=function(e){return LA(this,Pp(e,42))},eUe.Jc=function(e){qX(this,e)},eUe.Xb=function(e){return Pp(etj(this.c,e),133)},eUe.ii=function(e,t){return Pp(this.c.ii(e,t),42)},eUe.ji=function(e,t){MZ(this,e,Pp(t,42))},eUe.Lc=function(){return new R1(null,new Gq(this,16))},eUe.$c=function(e){return Pp(this.c.$c(e),42)},eUe._c=function(e,t){return YV(this,e,Pp(t,42))},eUe.ad=function(e){er8(this,e)},eUe.Nc=function(){return new Gq(this,16)},eUe.Oc=function(){return new R1(null,new Gq(this,16))},eUe.Wc=function(e,t){return this.c.Wc(e,t)},eUe.Gc=function(e){return this.c.Gc(e)},eUe.$b=function(){this.c.$b()},eUe.Hc=function(e){return this.c.Hc(e)},eUe.Ic=function(e){return eot(this.c,e)},eUe.qj=function(){var e,t,n;if(null==this.d){for(this.d=Je(e6C,eJA,63,2*this.f+1,0,1),n=this.e,this.f=0,t=this.c.Kc();t.e!=t.i.gc();)ebB(this,e=Pp(t.nj(),133));this.e=n}},eUe.Fb=function(e){return Ij(this,e)},eUe.Hb=function(){return eov(this.c)},eUe.Xc=function(e){return this.c.Xc(e)},eUe.rj=function(){this.c=new pC(this)},eUe.dc=function(){return 0==this.f},eUe.Kc=function(){return this.c.Kc()},eUe.Yc=function(){return this.c.Yc()},eUe.Zc=function(e){return this.c.Zc(e)},eUe.sj=function(){return X6(this)},eUe.tj=function(e,t,n){return new N7(e,t,n)},eUe.uj=function(){return new st},eUe.Mc=function(e){return en$(this,e)},eUe.gc=function(){return this.f},eUe.bd=function(e,t){return new Gz(this.c,e,t)},eUe.Pc=function(){return this.c.Pc()},eUe.Qc=function(e){return this.c.Qc(e)},eUe.Ib=function(){return efq(this.c)},eUe.e=0,eUe.f=0,Y5(eX_,"BasicEMap",705),eTS(1033,63,eXz,pC),eUe.bi=function(e,t){bH(this,Pp(t,133))},eUe.ei=function(e,t,n){var r;++(r=this,Pp(t,133),r).a.e},eUe.fi=function(e,t){b$(this,Pp(t,133))},eUe.gi=function(e,t,n){AO(this,Pp(t,133),Pp(n,133))},eUe.di=function(e,t){eac(this.a)},Y5(eX_,"BasicEMap/1",1033),eTS(1034,63,eXz,st),eUe.ri=function(e){return Je(e6R,eJL,612,e,0,1)},Y5(eX_,"BasicEMap/2",1034),eTS(1035,eUT,eUM,pI),eUe.$b=function(){this.a.c.$b()},eUe.Hc=function(e){return edG(this.a,e)},eUe.Kc=function(){return 0==this.a.f?(LF(),tmB.a):new yd(this.a)},eUe.Mc=function(e){var t;return t=this.a.f,ehx(this.a,e),this.a.f!=t},eUe.gc=function(){return this.a.f},Y5(eX_,"BasicEMap/3",1035),eTS(1036,28,eUx,pD),eUe.$b=function(){this.a.c.$b()},eUe.Hc=function(e){return eCl(this.a,e)},eUe.Kc=function(){return 0==this.a.f?(LF(),tmB.a):new yh(this.a)},eUe.gc=function(){return this.a.f},Y5(eX_,"BasicEMap/4",1036),eTS(1037,eUT,eUM,pN),eUe.$b=function(){this.a.c.$b()},eUe.Hc=function(e){var t,n,r,i,a,o,s,u,c;if(this.a.f>0&&M4(e,42)&&(this.a.qj(),i=null==(s=(u=Pp(e,42)).cd())?0:esj(s),a=Cb(this.a,i),t=this.a.d[a])){for(o=0,n=Pp(t.g,367),c=t.i;o"+this.c},eUe.a=0;var e6R=Y5(eX_,"BasicEMap/EntryImpl",612);eTS(536,1,{},o2),Y5(eX_,"BasicEMap/View",536),eTS(768,1,{}),eUe.Fb=function(e){return eT$((Hj(),e2r),e)},eUe.Hb=function(){return esS((Hj(),e2r))},eUe.Ib=function(){return e_F((Hj(),e2r))},Y5(eX_,"ECollections/BasicEmptyUnmodifiableEList",768),eTS(1312,1,eUC,sn),eUe.Nb=function(e){F8(this,e)},eUe.Rb=function(e){throw p7(new bO)},eUe.Ob=function(){return!1},eUe.Sb=function(){return!1},eUe.Pb=function(){throw p7(new bC)},eUe.Tb=function(){return 0},eUe.Ub=function(){throw p7(new bC)},eUe.Vb=function(){return -1},eUe.Qb=function(){throw p7(new bO)},eUe.Wb=function(e){throw p7(new bO)},Y5(eX_,"ECollections/BasicEmptyUnmodifiableEList/1",1312),eTS(1310,768,{20:1,14:1,15:1,58:1},mx),eUe.Vc=function(e,t){y5()},eUe.Fc=function(e){return y6()},eUe.Wc=function(e,t){return y9()},eUe.Gc=function(e){return y8()},eUe.$b=function(){y7()},eUe.Hc=function(e){return!1},eUe.Ic=function(e){return!1},eUe.Jc=function(e){qX(this,e)},eUe.Xb=function(e){return xY((Hj(),e)),null},eUe.Xc=function(e){return -1},eUe.dc=function(){return!0},eUe.Kc=function(){return this.a},eUe.Yc=function(){return this.a},eUe.Zc=function(e){return this.a},eUe.ii=function(e,t){return we()},eUe.ji=function(e,t){wt()},eUe.Lc=function(){return new R1(null,new Gq(this,16))},eUe.$c=function(e){return wn()},eUe.Mc=function(e){return wr()},eUe._c=function(e,t){return wi()},eUe.gc=function(){return 0},eUe.ad=function(e){er8(this,e)},eUe.Nc=function(){return new Gq(this,16)},eUe.Oc=function(){return new R1(null,new Gq(this,16))},eUe.bd=function(e,t){return Hj(),new Gz(e2r,e,t)},eUe.Pc=function(){return Fn((Hj(),e2r))},eUe.Qc=function(e){return Hj(),emk(e2r,e)},Y5(eX_,"ECollections/EmptyUnmodifiableEList",1310),eTS(1311,768,{20:1,14:1,15:1,58:1,589:1},mT),eUe.Vc=function(e,t){y5()},eUe.Fc=function(e){return y6()},eUe.Wc=function(e,t){return y9()},eUe.Gc=function(e){return y8()},eUe.$b=function(){y7()},eUe.Hc=function(e){return!1},eUe.Ic=function(e){return!1},eUe.Jc=function(e){qX(this,e)},eUe.Xb=function(e){return xY((Hj(),e)),null},eUe.Xc=function(e){return -1},eUe.dc=function(){return!0},eUe.Kc=function(){return this.a},eUe.Yc=function(){return this.a},eUe.Zc=function(e){return this.a},eUe.ii=function(e,t){return we()},eUe.ji=function(e,t){wt()},eUe.Lc=function(){return new R1(null,new Gq(this,16))},eUe.$c=function(e){return wn()},eUe.Mc=function(e){return wr()},eUe._c=function(e,t){return wi()},eUe.gc=function(){return 0},eUe.ad=function(e){er8(this,e)},eUe.Nc=function(){return new Gq(this,16)},eUe.Oc=function(){return new R1(null,new Gq(this,16))},eUe.bd=function(e,t){return Hj(),new Gz(e2r,e,t)},eUe.Pc=function(){return Fn((Hj(),e2r))},eUe.Qc=function(e){return Hj(),emk(e2r,e)},eUe.sj=function(){return Hj(),Hj(),e2i},Y5(eX_,"ECollections/EmptyUnmodifiableEMap",1311);var e6j=RL(eX_,"Enumerator");eTS(281,1,{281:1},eCg),eUe.Fb=function(e){var t;return this===e||!!M4(e,281)&&(t=Pp(e,281),this.f==t.f&&jx(this.i,t.i)&&jk(this.a,(256&this.f)!=0?(256&t.f)!=0?t.a:null:(256&t.f)!=0?null:t.a)&&jk(this.d,t.d)&&jk(this.g,t.g)&&jk(this.e,t.e)&&epK(this,t))},eUe.Hb=function(){return this.f},eUe.Ib=function(){return eDv(this)},eUe.f=0;var e6F,e6Y,e6B,e6U,e6H,e6$,e6z,e6G,e6W,e6K,e6V,e6q,e6Z,e6X,e6J,e6Q,e61,e60,e62,e63,e64,e65,e66,e69,e68,e67,e9e,e9t,e9n,e9r,e9i,e9a,e9o,e9s,e9u,e9c,e9l,e9f,e9d,e9h,e9p,e9b,e9m,e9g,e9v,e9y,e9w,e9_,e9E,e9S,e9k,e9x,e9T,e9M,e9O,e9A,e9L,e9C,e9I,e9D,e9N,e9P,e9R,e9j,e9F,e9Y,e9B,e9U,e9H,e9$,e9z,e9G,e9W,e9K,e9V,e9q,e9Z,e9X,e9J,e9Q,e91,e90,e92,e93,e94,e95,e96,e99,e98,e97,e8e,e8t,e8n,e8r,e8i,e8a,e8o,e8s,e8u,e8c,e8l,e8f,e8d,e8h,e8p,e8b,e8m,e8g,e8v,e8y,e8w,e8_,e8E,e8S,e8k,e8x,e8T,e8M,e8O,e8A,e8L,e8C,e8I,e8D,e8N,e8P,e8R,e8j,e8F,e8Y,e8B,e8U,e8H,e8$,e8z,e8G,e8W,e8K,e8V,e8q,e8Z,e8X,e8J,e8Q,e81,e80,e82,e83,e84,e85,e86,e89,e88,e87,e7e,e7t,e7n,e7r,e7i,e7a,e7o,e7s,e7u,e7c,e7l,e7f,e7d,e7h,e7p,e7b,e7m,e7g,e7v,e7y,e7w,e7_,e7E,e7S,e7k,e7x,e7T,e7M,e7O,e7A,e7L,e7C,e7I,e7D,e7N,e7P,e7R,e7j,e7F,e7Y,e7B,e7U,e7H,e7$,e7z,e7G,e7W,e7K,e7V,e7q,e7Z,e7X,e7J,e7Q,e71,e70,e72,e73,e74,e75,e76,e79,e78,e77,tee,tet,ten,ter,tei,tea,teo,tes,teu,tec,tel,tef,ted,teh,tep,teb,tem,teg,tev,tey,tew,te_,teE,teS,tek,tex,teT,teM,teO,teA,teL,teC,teI,teD,teN,teP,teR,tej,teF,teY,teB,teU,teH,te$,tez,teG,teW,teK,teV,teq,teZ,teX,teJ,teQ,te1,te0,te2,te3,te4,te5,te6,te9,te8,te7,tte,ttt,ttn,ttr,tti,tta,tto,tts,ttu,ttc,ttl,ttf,ttd,tth,ttp,ttb,ttm,ttg,ttv,tty,ttw,tt_,ttE,ttS,ttk,ttx,ttT,ttM,ttO,ttA,ttL,ttC,ttI,ttD,ttN,ttP,ttR,ttj,ttF,ttY,ttB,ttU,ttH,tt$,ttz,ttG,ttW,ttK,ttV,ttq,ttZ,ttX,ttJ,ttQ,tt1,tt0,tt2,tt3,tt4,tt5,tt6,tt9,tt8,tt7,tne,tnt,tnn,tnr,tni,tna,tno,tns,tnu,tnc,tnl,tnf,tnd,tnh,tnp,tnb,tnm,tng,tnv,tny,tnw,tn_,tnE,tnS,tnk,tnx,tnT,tnM,tnO,tnA,tnL,tnC,tnI,tnD,tnN,tnP,tnR,tnj,tnF,tnY,tnB,tnU,tnH,tn$,tnz,tnG,tnW,tnK,tnV,tnq,tnZ,tnX,tnJ,tnQ,tn1,tn0,tn2,tn3,tn4,tn5,tn6,tn9,tn8,tn7,tre,trt,trn,trr,tri,tra,tro,trs,tru,trc,trl,trf,trd,trh,trp,trb,trm,trg,trv,trw,tr_,trE,trS,trk,trx,trT,trM,trO,trA,trL,trC,trI,trD,trN,trP,trR,trj,trF,trY,trB,trU,trH,tr$,trz,trG,trW,trK,trV,trq,trZ,trX,trJ,trQ,tr1,tr0,tr2,tr3,tr4,tr5,tr6,tr9,tr8,tr7,tie,tit,tin,tir,tii,tia,tio,tis,tiu,tic,til,tif,tid,tih,tip,tib,tim,tig,tiv,tiy,tiw,ti_,tiE,tiS,tik,tix,tiT,tiM,tiO,tiA,tiL,tiC,tiI,tiD,tiN,tiP,tiR,tij,tiF,tiY,tiB,tiU,tiH,ti$,tiz,tiG,tiW,tiK,tiV,tiq,tiZ,tiX,tiJ,tiQ,ti1,ti0,ti2,ti3,ti4,ti5,ti6,ti9,ti8,ti7,tae,tat,tan,tar,tai,taa,tao,tas,tau,tac,tal,taf,tad,tah,tap,tab,tam,tag,tav,tay,taw,ta_,taE,taS,tak,tax,taT,taM,taO,taA,taL,taC,taI,taD,taN,taP,taR,taj,taF,taY,taB,taU,taH,ta$,taz,taG,taW,taK,taV,taq,taZ,taX,taJ,taQ,ta1,ta0,ta2,ta3,ta4,ta5,ta6,ta9,ta8,ta7,toe,tot,ton,tor,toi,toa,too,tos,tou,toc,tol,tof,tod,toh,top,tob,tom,tog,tov,toy,tow,to_,toE,toS,tok,tox,toT,toM,toO,toA,toL,toC,toI,toD,toN,toP,toR,toj,toF,toY,toB,toU,toH,to$,toz,toG,toW,toK,toV,toq,toZ,toX,toJ,toQ,to1,to0,to2,to3,to4,to5,to6,to9,to8,to7,tse,tst,tsn,tsr,tsi,tsa,tso,tss,tsu,tsc,tsl,tsf,tsd,tsh,tsp,tsb,tsm,tsg,tsv,tsy,tsw,ts_,tsE,tsS,tsk,tsx,tsT,tsM,tsO,tsA,tsL,tsC,tsI,tsD,tsN,tsP,tsR,tsj,tsF,tsY,tsB,tsU,tsH,ts$,tsz,tsG,tsW,tsK,tsV,tsq,tsZ,tsX,tsJ,tsQ,ts1,ts0,ts2,ts3,ts4,ts5,ts6,ts9,ts8,ts7,tue,tut,tun,tur,tui,tua,tuo,tus,tuu,tuc,tul,tuf,tud,tuh,tup,tub,tum,tug,tuv,tuy,tuw,tu_,tuE,tuS,tuk,tux,tuT,tuM,tuO,tuA,tuL,tuC,tuI,tuD,tuN,tuP,tuR,tuj,tuF,tuY,tuB,tuU,tuH,tu$,tuz,tuG,tuW,tuK,tuV,tuq,tuZ,tuX,tuJ,tuQ,tu1,tu0,tu2,tu3,tu4,tu5,tu6,tu9,tu8,tu7,tce,tct,tcn,tcr,tci,tca,tco,tcs,tcu,tcc,tcl,tcf,tcd,tch,tcp,tcb,tcm,tcg,tcv,tcy,tcw,tc_,tcE,tcS,tck,tcx,tcT,tcM,tcO,tcA,tcL,tcC,tcI,tcD,tcN,tcP,tcR,tcj,tcF,tcY,tcB,tcU,tcH,tc$,tcz,tcG,tcW,tcK,tcV,tcq,tcZ,tcX,tcJ,tcQ,tc1,tc0,tc2,tc3,tc4,tc5,tc6,tc9,tc8,tc7,tle,tlt,tln,tlr,tli,tla,tlo,tls,tlu,tlc,tll,tlf,tld,tlh,tlp,tlb,tlm,tlg,tlv,tly,tlw,tl_,tlE,tlS,tlk,tlx,tlT,tlM,tlO,tlA,tlL,tlC,tlI,tlD,tlN,tlP,tlR,tlj,tlF,tlY,tlB,tlU,tlH,tl$,tlz,tlG,tlW,tlK,tlV,tlq,tlZ,tlX,tlJ,tlQ,tl1,tl0,tl2,tl3,tl4,tl5,tl6,tl9,tl8,tl7,tfe,tft,tfn,tfr,tfi,tfa,tfo,tfs,tfu,tfc,tfl,tff,tfd,tfh,tfp,tfb,tfm,tfg,tfv,tfy,tfw,tf_,tfE,tfS,tfk,tfx,tfT,tfM,tfO,tfA,tfL,tfC,tfI,tfD,tfN,tfP,tfR,tfj,tfF,tfY,tfB,tfU,tfH,tf$,tfz,tfG,tfW,tfK,tfV,tfq,tfZ,tfX,tfJ,tfQ,tf1,tf0,tf2,tf3,tf4,tf5,tf6,tf9,tf8,tf7,tde,tdt,tdn,tdr,tdi,tda,tdo,tds,tdu,tdc,tdl,tdf,tdd,tdh,tdp,tdb,tdm,tdg,tdv,tdy,tdw,td_,tdE,tdS,tdk,tdx,tdT,tdM,tdO,tdA,tdL,tdC,tdI,tdD,tdN,tdP,tdR,tdj,tdF,tdY,tdB,tdU,tdH,td$,tdz,tdG,tdW,tdK,tdV,tdq,tdZ,tdX,tdJ,tdQ,td1,td0,td2,td3,td4,td5,td6,td9,td8,td7,the,tht,thn,thr,thi,tha,tho,ths,thu,thc,thl,thf,thd,thh,thp,thb,thm,thg,thv,thy,thw,th_,thE,thS,thk,thx,thT,thM,thO,thA,thL,thC,thI,thD,thN,thP,thR,thj,thF,thY,thB,thU,thH,th$,thz,thG,thW,thK,thV,thq,thZ,thX,thJ,thQ,th1,th0,th2,th3,th4,th5,th6,th9,th8,th7,tpe,tpt,tpn,tpr,tpi,tpa,tpo,tps,tpu,tpc,tpl,tpf,tpd,tph,tpp,tpb,tpm,tpg,tpv,tpy,tpw,tp_,tpE,tpS,tpk,tpx,tpT,tpM,tpO,tpA,tpL,tpC,tpI,tpD,tpN,tpP,tpR,tpj,tpF,tpY,tpB,tpU,tpH,tp$,tpz,tpG,tpW,tpK,tpV,tpq,tpZ,tpX,tpJ,tpQ,tp1,tp0,tp2,tp3,tp4,tp5,tp6,tp9,tp8,tp7,tbe,tbt,tbn,tbr,tbi,tba,tbo,tbs,tbu,tbc,tbl,tbf,tbd,tbh,tbp,tbb,tbm,tbg,tbv,tby,tbw,tb_,tbE,tbS,tbk,tbx,tbT,tbM,tbO,tbA,tbL,tbC,tbI,tbD,tbN,tbP,tbR,tbj,tbF,tbY,tbB,tbU,tbH,tb$,tbz,tbG,tbW,tbK,tbV,tbq,tbZ,tbX,tbJ,tbQ,tb1,tb0,tb2,tb3,tb4,tb5,tb6,tb9,tb8,tb7,tme,tmt,tmn,tmr,tmi,tma,tmo,tms,tmu,tmc,tml,tmf,tmd,tmh,tmp,tmb,tmm,tmg,tmv,tmy,tmw,tm_,tmE,tmS,tmk,tmx,tmT,tmM,tmO,tmA,tmL,tmC,tmI,tmD,tmN,tmP,tmR,tmj,tmF,tmY,tmB,tmU,tmH,tm$,tmz,tmG=0,tmW=0,tmK=0,tmV=0,tmq=0,tmZ=0,tmX=0,tmJ=0,tmQ=0,tm1=0,tm0=0,tm2=0,tm3=0;Y5(eX_,"URI",281),eTS(1091,43,e$s,mM),eUe.zc=function(e,t){return Pp(Ge(this,Lq(e),Pp(t,281)),281)},Y5(eX_,"URI/URICache",1091),eTS(497,63,eXz,o5,jf),eUe.hi=function(){return!0},Y5(eX_,"UniqueEList",497),eTS(581,60,eHr,QH),Y5(eX_,"WrappedException",581);var tm4=RL(eZD,eJD),tm5=RL(eZD,eJN),tm6=RL(eZD,eJP),tm9=RL(eZD,eJR),tm8=RL(eZD,eJj),tm7=RL(eZD,"EClass"),tge=RL(eZD,"EDataType");eTS(1183,43,e$s,mO),eUe.xc=function(e){return xd(e)?zg(this,e):xu($I(this.f,e))},Y5(eZD,"EDataType/Internal/ConversionDelegate/Factory/Registry/Impl",1183);var tgt=RL(eZD,"EEnum"),tgn=RL(eZD,eJF),tgr=RL(eZD,eJY),tgi=RL(eZD,eJB),tga=RL(eZD,eJU),tgo=RL(eZD,eJH);eTS(1029,1,{},o4),eUe.Ib=function(){return"NIL"},Y5(eZD,"EStructuralFeature/Internal/DynamicValueHolder/1",1029),eTS(1028,43,e$s,mA),eUe.xc=function(e){return xd(e)?zg(this,e):xu($I(this.f,e))},Y5(eZD,"EStructuralFeature/Internal/SettingDelegate/Factory/Registry/Impl",1028);var tgs=RL(eZD,eJ$),tgu=RL(eZD,"EValidator/PatternMatcher"),tgc=RL(eJz,"FeatureMap/Entry");eTS(535,1,{72:1},k3),eUe.ak=function(){return this.a},eUe.dd=function(){return this.b},Y5(eZ2,"BasicEObjectImpl/1",535),eTS(1027,1,eJG,k4),eUe.Wj=function(e){return ZN(this.a,this.b,e)},eUe.fj=function(){return zz(this.a,this.b)},eUe.Wb=function(e){zx(this.a,this.b,e)},eUe.Xj=function(){B4(this.a,this.b)},Y5(eZ2,"BasicEObjectImpl/4",1027),eTS(1983,1,{108:1}),eUe.bk=function(e){this.e=0==e?tgH:Je(e1R,eUp,1,e,5,1)},eUe.Ch=function(e){return this.e[e]},eUe.Dh=function(e,t){this.e[e]=t},eUe.Eh=function(e){this.e[e]=null},eUe.ck=function(){return this.c},eUe.dk=function(){throw p7(new bO)},eUe.ek=function(){throw p7(new bO)},eUe.fk=function(){return this.d},eUe.gk=function(){return null!=this.e},eUe.hk=function(e){this.c=e},eUe.ik=function(e){throw p7(new bO)},eUe.jk=function(e){throw p7(new bO)},eUe.kk=function(e){this.d=e},Y5(eZ2,"BasicEObjectImpl/EPropertiesHolderBaseImpl",1983),eTS(185,1983,{108:1},c1),eUe.dk=function(){return this.a},eUe.ek=function(){return this.b},eUe.ik=function(e){this.a=e},eUe.jk=function(e){this.b=e},Y5(eZ2,"BasicEObjectImpl/EPropertiesHolderImpl",185),eTS(506,97,eZ0,sr),eUe.Kg=function(){return this.f},eUe.Pg=function(){return this.k},eUe.Rg=function(e,t){this.g=e,this.i=t},eUe.Tg=function(){return(2&this.j)==0?this.zh():this.ph().ck()},eUe.Vg=function(){return this.i},eUe.Mg=function(){return(1&this.j)!=0},eUe.eh=function(){return this.g},eUe.kh=function(){return(4&this.j)!=0},eUe.ph=function(){return this.k||(this.k=new c1),this.k},eUe.th=function(e){this.ph().hk(e),e?this.j|=2:this.j&=-3},eUe.vh=function(e){this.ph().jk(e),e?this.j|=4:this.j&=-5},eUe.zh=function(){return(BM(),tgv).S},eUe.i=0,eUe.j=1,Y5(eZ2,"EObjectImpl",506),eTS(780,506,{105:1,92:1,90:1,56:1,108:1,49:1,97:1},Pq),eUe.Ch=function(e){return this.e[e]},eUe.Dh=function(e,t){this.e[e]=t},eUe.Eh=function(e){this.e[e]=null},eUe.Tg=function(){return this.d},eUe.Yg=function(e){return edv(this.d,e)},eUe.$g=function(){return this.d},eUe.dh=function(){return null!=this.e},eUe.ph=function(){return this.k||(this.k=new si),this.k},eUe.th=function(e){this.d=e},eUe.yh=function(){var e;return null==this.e&&(e=Y1(this.d),this.e=0==e?tg$:Je(e1R,eUp,1,e,5,1)),this},eUe.Ah=function(){return 0},Y5(eZ2,"DynamicEObjectImpl",780),eTS(1376,780,{105:1,42:1,92:1,90:1,133:1,56:1,108:1,49:1,97:1},RO),eUe.Fb=function(e){return this===e},eUe.Hb=function(){return Ao(this)},eUe.th=function(e){this.d=e,this.b=eAh(e,"key"),this.c=eAh(e,eXr)},eUe.Sh=function(){var e;return -1==this.a&&(e=Q9(this,this.b),this.a=null==e?0:esj(e)),this.a},eUe.cd=function(){return Q9(this,this.b)},eUe.dd=function(){return Q9(this,this.c)},eUe.Th=function(e){this.a=e},eUe.Uh=function(e){zx(this,this.b,e)},eUe.ed=function(e){var t;return t=Q9(this,this.c),zx(this,this.c,e),t},eUe.a=0,Y5(eZ2,"DynamicEObjectImpl/BasicEMapEntry",1376),eTS(1377,1,{108:1},si),eUe.bk=function(e){throw p7(new bO)},eUe.Ch=function(e){throw p7(new bO)},eUe.Dh=function(e,t){throw p7(new bO)},eUe.Eh=function(e){throw p7(new bO)},eUe.ck=function(){throw p7(new bO)},eUe.dk=function(){return this.a},eUe.ek=function(){return this.b},eUe.fk=function(){return this.c},eUe.gk=function(){throw p7(new bO)},eUe.hk=function(e){throw p7(new bO)},eUe.ik=function(e){this.a=e},eUe.jk=function(e){this.b=e},eUe.kk=function(e){this.c=e},Y5(eZ2,"DynamicEObjectImpl/DynamicEPropertiesHolderImpl",1377),eTS(510,150,{105:1,92:1,90:1,590:1,147:1,56:1,108:1,49:1,97:1,510:1,150:1,114:1,115:1},sa),eUe.Qg=function(e){return eg4(this,e)},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.d;case 2:return n?(this.b||(this.b=new L_((eBK(),tgF),tgf,this)),this.b):(this.b||(this.b=new L_((eBK(),tgF),tgf,this)),X6(this.b));case 3:return z4(this);case 4:return this.a||(this.a=new O_(e6f,this,4)),this.a;case 5:return this.c||(this.c=new OT(e6f,this,5)),this.c}return Qt(this,e-Y1((eBK(),tgy)),ee2((r=Pp(eaS(this,16),26))||tgy,e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 3:return this.Cb&&(n=(i=this.Db>>16)>=0?eg4(this,n):this.Cb.ih(this,-1-i,null,n)),j3(this,Pp(e,147),n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgy),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgy)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 2:return this.b||(this.b=new L_((eBK(),tgF),tgf,this)),Iz(this.b,e,n);case 3:return j3(this,null,n);case 4:return this.a||(this.a=new O_(e6f,this,4)),ep6(this.a,e,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgy),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgy)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.d;case 2:return!!this.b&&0!=this.b.f;case 3:return!!z4(this);case 4:return!!this.a&&0!=this.a.i;case 5:return!!this.c&&0!=this.c.i}return VP(this,e-Y1((eBK(),tgy)),ee2((t=Pp(eaS(this,16),26))||tgy,e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:RN(this,Lq(t));return;case 2:this.b||(this.b=new L_((eBK(),tgF),tgf,this)),eai(this.b,t);return;case 3:eAc(this,Pp(t,147));return;case 4:this.a||(this.a=new O_(e6f,this,4)),eRT(this.a),this.a||(this.a=new O_(e6f,this,4)),Y4(this.a,Pp(t,14));return;case 5:this.c||(this.c=new OT(e6f,this,5)),eRT(this.c),this.c||(this.c=new OT(e6f,this,5)),Y4(this.c,Pp(t,14));return}efL(this,e-Y1((eBK(),tgy)),ee2((n=Pp(eaS(this,16),26))||tgy,e),t)},eUe.zh=function(){return eBK(),tgy},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:erl(this,null);return;case 2:this.b||(this.b=new L_((eBK(),tgF),tgf,this)),this.b.c.$b();return;case 3:eAc(this,null);return;case 4:this.a||(this.a=new O_(e6f,this,4)),eRT(this.a);return;case 5:this.c||(this.c=new OT(e6f,this,5)),eRT(this.c);return}ec6(this,e-Y1((eBK(),tgy)),ee2((t=Pp(eaS(this,16),26))||tgy,e))},eUe.Ib=function(){return eln(this)},eUe.d=null,Y5(eZ2,"EAnnotationImpl",510),eTS(151,705,eJW,JY),eUe.Xh=function(e,t){T7(this,e,Pp(t,42))},eUe.lk=function(e,t){return I$(this,Pp(e,42),t)},eUe.pi=function(e){return Pp(Pp(this.c,69).pi(e),133)},eUe.Zh=function(){return Pp(this.c,69).Zh()},eUe.$h=function(){return Pp(this.c,69).$h()},eUe._h=function(e){return Pp(this.c,69)._h(e)},eUe.mk=function(e,t){return Iz(this,e,t)},eUe.Wj=function(e){return Pp(this.c,76).Wj(e)},eUe.rj=function(){},eUe.fj=function(){return Pp(this.c,76).fj()},eUe.tj=function(e,t,n){var r;return(r=Pp(etP(this.b).Nh().Jh(this.b),133)).Th(e),r.Uh(t),r.ed(n),r},eUe.uj=function(){return new pZ(this)},eUe.Wb=function(e){eai(this,e)},eUe.Xj=function(){Pp(this.c,76).Xj()},Y5(eJz,"EcoreEMap",151),eTS(158,151,eJW,L_),eUe.qj=function(){var e,t,n,r,i,a;if(null==this.d){for(a=Je(e6C,eJA,63,2*this.f+1,0,1),n=this.c.Kc();n.e!=n.i.gc();)(e=a[i=((r=(t=Pp(n.nj(),133)).Sh())&eUu)%a.length])||(e=a[i]=new pZ(this)),e.Fc(t);this.d=a}},Y5(eZ2,"EAnnotationImpl/1",158),eTS(284,438,{105:1,92:1,90:1,147:1,191:1,56:1,108:1,472:1,49:1,97:1,150:1,284:1,114:1,115:1}),eUe._g=function(e,t,n){var r,i;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return OQ(),(256&this.Bb)!=0;case 3:return OQ(),(512&this.Bb)!=0;case 4:return ell(this.s);case 5:return ell(this.t);case 6:return OQ(),!!this.$j();case 7:return OQ(),(i=this.s)>=1;case 8:if(t)return evl(this);return this.r;case 9:return this.q}return Qt(this,e-Y1(this.zh()),ee2((r=Pp(eaS(this,16),26))||this.zh(),e),t,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 9:return Y3(this,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Rj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.lh=function(e){var t,n;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return(256&this.Bb)==0;case 3:return(512&this.Bb)==0;case 4:return 0!=this.s;case 5:return 1!=this.t;case 6:return this.$j();case 7:return(n=this.s)>=1;case 8:return!!this.r&&!this.q.e&&0==BX(this.q).i;case 9:return!!this.q&&!(this.r&&!this.q.e&&0==BX(this.q).i)}return VP(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.sh=function(e,t){var n,r;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:this.Lh(Lq(t));return;case 2:eli(this,gN(LK(t)));return;case 3:els(this,gN(LK(t)));return;case 4:end(this,Pp(t,19).a);return;case 5:this.ok(Pp(t,19).a);return;case 8:eu2(this,Pp(t,138));return;case 9:(r=ew3(this,Pp(t,87),null))&&r.Fi();return}efL(this,e-Y1(this.zh()),ee2((n=Pp(eaS(this,16),26))||this.zh(),e),t)},eUe.zh=function(){return eBK(),tgB},eUe.Bh=function(e){var t,n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:this.Lh(null);return;case 2:eli(this,!0);return;case 3:els(this,!0);return;case 4:end(this,0);return;case 5:this.ok(1);return;case 8:eu2(this,null);return;case 9:(n=ew3(this,null,null))&&n.Fi();return}ec6(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.Gh=function(){evl(this),this.Bb|=1},eUe.Yj=function(){return evl(this)},eUe.Zj=function(){return this.t},eUe.$j=function(){var e;return(e=this.t)>1||-1==e},eUe.hi=function(){return(512&this.Bb)!=0},eUe.nk=function(e,t){return ecz(this,e,t)},eUe.ok=function(e){enh(this,e)},eUe.Ib=function(){return ex3(this)},eUe.s=0,eUe.t=1,Y5(eZ2,"ETypedElementImpl",284),eTS(449,284,{105:1,92:1,90:1,147:1,191:1,56:1,170:1,66:1,108:1,472:1,49:1,97:1,150:1,449:1,284:1,114:1,115:1,677:1}),eUe.Qg=function(e){return egx(this,e)},eUe._g=function(e,t,n){var r,i;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return OQ(),(256&this.Bb)!=0;case 3:return OQ(),(512&this.Bb)!=0;case 4:return ell(this.s);case 5:return ell(this.t);case 6:return OQ(),!!this.$j();case 7:return OQ(),(i=this.s)>=1;case 8:if(t)return evl(this);return this.r;case 9:return this.q;case 10:return OQ(),(this.Bb&eXt)!=0;case 11:return OQ(),(this.Bb&eJq)!=0;case 12:return OQ(),(this.Bb&eH0)!=0;case 13:return this.j;case 14:return eOI(this);case 15:return OQ(),(this.Bb&eJV)!=0;case 16:return OQ(),(this.Bb&eUR)!=0;case 17:return z6(this)}return Qt(this,e-Y1(this.zh()),ee2((r=Pp(eaS(this,16),26))||this.zh(),e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 17:return this.Cb&&(n=(i=this.Db>>16)>=0?egx(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,17,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Qj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 9:return Y3(this,n);case 17:return eDg(this,null,17,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Rj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.lh=function(e){var t,n;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return(256&this.Bb)==0;case 3:return(512&this.Bb)==0;case 4:return 0!=this.s;case 5:return 1!=this.t;case 6:return this.$j();case 7:return(n=this.s)>=1;case 8:return!!this.r&&!this.q.e&&0==BX(this.q).i;case 9:return!!this.q&&!(this.r&&!this.q.e&&0==BX(this.q).i);case 10:return(this.Bb&eXt)==0;case 11:return(this.Bb&eJq)!=0;case 12:return(this.Bb&eH0)!=0;case 13:return null!=this.j;case 14:return null!=eOI(this);case 15:return(this.Bb&eJV)!=0;case 16:return(this.Bb&eUR)!=0;case 17:return!!z6(this)}return VP(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.sh=function(e,t){var n,r;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:GD(this,Lq(t));return;case 2:eli(this,gN(LK(t)));return;case 3:els(this,gN(LK(t)));return;case 4:end(this,Pp(t,19).a);return;case 5:this.ok(Pp(t,19).a);return;case 8:eu2(this,Pp(t,138));return;case 9:(r=ew3(this,Pp(t,87),null))&&r.Fi();return;case 10:elF(this,gN(LK(t)));return;case 11:elU(this,gN(LK(t)));return;case 12:elY(this,gN(LK(t)));return;case 13:xi(this,Lq(t));return;case 15:elB(this,gN(LK(t)));return;case 16:elZ(this,gN(LK(t)));return}efL(this,e-Y1(this.zh()),ee2((n=Pp(eaS(this,16),26))||this.zh(),e),t)},eUe.zh=function(){return eBK(),tgY},eUe.Bh=function(e){var t,n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:M4(this.Cb,88)&&eko(Zd(Pp(this.Cb,88)),4),er3(this,null);return;case 2:eli(this,!0);return;case 3:els(this,!0);return;case 4:end(this,0);return;case 5:this.ok(1);return;case 8:eu2(this,null);return;case 9:(n=ew3(this,null,null))&&n.Fi();return;case 10:elF(this,!0);return;case 11:elU(this,!1);return;case 12:elY(this,!1);return;case 13:this.i=null,erA(this,null);return;case 15:elB(this,!1);return;case 16:elZ(this,!1);return}ec6(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.Gh=function(){UH(QZ((eSp(),tvc),this)),evl(this),this.Bb|=1},eUe.Gj=function(){return this.f},eUe.zj=function(){return eOI(this)},eUe.Hj=function(){return z6(this)},eUe.Lj=function(){return null},eUe.pk=function(){return this.k},eUe.aj=function(){return this.n},eUe.Mj=function(){return eyD(this)},eUe.Nj=function(){var e,t,n,r,i,a,o,s,u;return this.p||((null==(n=z6(this)).i&&eNT(n),n.i).length,(r=this.Lj())&&Y1(z6(r)),e=(o=(i=evl(this)).Bj())?(1&o.i)!=0?o==tyE?e11:o==ty_?e15:o==tyT?e14:o==tyx?e13:o==tyS?e16:o==tyM?e19:o==tyk?e10:e12:o:null,t=eOI(this),s=i.zj(),efl(this),(this.Bb&eUR)!=0&&((a=ev1((eSp(),tvc),n))&&a!=this||(a=Wk(QZ(tvc,this))))?this.p=new k6(this,a):this.$j()?this.rk()?r?(this.Bb&eJV)!=0?e?this.sk()?this.p=new HS(47,e,this,r):this.p=new HS(5,e,this,r):this.sk()?this.p=new qc(46,this,r):this.p=new qc(4,this,r):e?this.sk()?this.p=new HS(49,e,this,r):this.p=new HS(7,e,this,r):this.sk()?this.p=new qc(48,this,r):this.p=new qc(6,this,r):(this.Bb&eJV)!=0?e?e==e1$?this.p=new Pe(50,e6M,this):this.sk()?this.p=new Pe(43,e,this):this.p=new Pe(1,e,this):this.sk()?this.p=new $F(42,this):this.p=new $F(0,this):e?e==e1$?this.p=new Pe(41,e6M,this):this.sk()?this.p=new Pe(45,e,this):this.p=new Pe(3,e,this):this.sk()?this.p=new $F(44,this):this.p=new $F(2,this):M4(i,148)?e==tgc?this.p=new $F(40,this):(512&this.Bb)!=0?(this.Bb&eJV)!=0?e?this.p=new Pe(9,e,this):this.p=new $F(8,this):e?this.p=new Pe(11,e,this):this.p=new $F(10,this):(this.Bb&eJV)!=0?e?this.p=new Pe(13,e,this):this.p=new $F(12,this):e?this.p=new Pe(15,e,this):this.p=new $F(14,this):r?(u=r.t)>1||-1==u?this.sk()?(this.Bb&eJV)!=0?e?this.p=new HS(25,e,this,r):this.p=new qc(24,this,r):e?this.p=new HS(27,e,this,r):this.p=new qc(26,this,r):(this.Bb&eJV)!=0?e?this.p=new HS(29,e,this,r):this.p=new qc(28,this,r):e?this.p=new HS(31,e,this,r):this.p=new qc(30,this,r):this.sk()?(this.Bb&eJV)!=0?e?this.p=new HS(33,e,this,r):this.p=new qc(32,this,r):e?this.p=new HS(35,e,this,r):this.p=new qc(34,this,r):(this.Bb&eJV)!=0?e?this.p=new HS(37,e,this,r):this.p=new qc(36,this,r):e?this.p=new HS(39,e,this,r):this.p=new qc(38,this,r):this.sk()?(this.Bb&eJV)!=0?e?this.p=new Pe(17,e,this):this.p=new $F(16,this):e?this.p=new Pe(19,e,this):this.p=new $F(18,this):(this.Bb&eJV)!=0?e?this.p=new Pe(21,e,this):this.p=new $F(20,this):e?this.p=new Pe(23,e,this):this.p=new $F(22,this):this.qk()?this.sk()?this.p=new Pt(Pp(i,26),this,r):this.p=new zl(Pp(i,26),this,r):M4(i,148)?e==tgc?this.p=new $F(40,this):(this.Bb&eJV)!=0?e?this.p=new j9(t,s,this,(edO(),o==ty_?tg2:o==tyE?tgX:o==tyS?tg3:o==tyT?tg0:o==tyx?tg1:o==tyM?tg5:o==tyk?tgJ:o==tyw?tgQ:tg4)):this.p=new HT(Pp(i,148),t,s,this):e?this.p=new j6(t,s,this,(edO(),o==ty_?tg2:o==tyE?tgX:o==tyS?tg3:o==tyT?tg0:o==tyx?tg1:o==tyM?tg5:o==tyk?tgJ:o==tyw?tgQ:tg4)):this.p=new Hx(Pp(i,148),t,s,this):this.rk()?r?(this.Bb&eJV)!=0?this.sk()?this.p=new Ps(Pp(i,26),this,r):this.p=new Po(Pp(i,26),this,r):this.sk()?this.p=new Pa(Pp(i,26),this,r):this.p=new Pn(Pp(i,26),this,r):(this.Bb&eJV)!=0?this.sk()?this.p=new Lx(Pp(i,26),this):this.p=new Lk(Pp(i,26),this):this.sk()?this.p=new LS(Pp(i,26),this):this.p=new LE(Pp(i,26),this):this.sk()?r?(this.Bb&eJV)!=0?this.p=new Pu(Pp(i,26),this,r):this.p=new Pr(Pp(i,26),this,r):(this.Bb&eJV)!=0?this.p=new LM(Pp(i,26),this):this.p=new LT(Pp(i,26),this):r?(this.Bb&eJV)!=0?this.p=new Pc(Pp(i,26),this,r):this.p=new Pi(Pp(i,26),this,r):(this.Bb&eJV)!=0?this.p=new LO(Pp(i,26),this):this.p=new jd(Pp(i,26),this)),this.p},eUe.Ij=function(){return(this.Bb&eXt)!=0},eUe.qk=function(){return!1},eUe.rk=function(){return!1},eUe.Jj=function(){return(this.Bb&eUR)!=0},eUe.Oj=function(){return eec(this)},eUe.sk=function(){return!1},eUe.Kj=function(){return(this.Bb&eJV)!=0},eUe.tk=function(e){this.k=e},eUe.Lh=function(e){GD(this,e)},eUe.Ib=function(){return eCR(this)},eUe.e=!1,eUe.n=0,Y5(eZ2,"EStructuralFeatureImpl",449),eTS(322,449,{105:1,92:1,90:1,34:1,147:1,191:1,56:1,170:1,66:1,108:1,472:1,49:1,97:1,322:1,150:1,449:1,284:1,114:1,115:1,677:1},mC),eUe._g=function(e,t,n){var r,i;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return OQ(),(256&this.Bb)!=0;case 3:return OQ(),(512&this.Bb)!=0;case 4:return ell(this.s);case 5:return ell(this.t);case 6:return OQ(),!!ek7(this);case 7:return OQ(),(i=this.s)>=1;case 8:if(t)return evl(this);return this.r;case 9:return this.q;case 10:return OQ(),(this.Bb&eXt)!=0;case 11:return OQ(),(this.Bb&eJq)!=0;case 12:return OQ(),(this.Bb&eH0)!=0;case 13:return this.j;case 14:return eOI(this);case 15:return OQ(),(this.Bb&eJV)!=0;case 16:return OQ(),(this.Bb&eUR)!=0;case 17:return z6(this);case 18:return OQ(),(this.Bb&eZ1)!=0;case 19:if(t)return eoe(this);return Xl(this)}return Qt(this,e-Y1((eBK(),tgw)),ee2((r=Pp(eaS(this,16),26))||tgw,e),t,n)},eUe.lh=function(e){var t,n;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return(256&this.Bb)==0;case 3:return(512&this.Bb)==0;case 4:return 0!=this.s;case 5:return 1!=this.t;case 6:return ek7(this);case 7:return(n=this.s)>=1;case 8:return!!this.r&&!this.q.e&&0==BX(this.q).i;case 9:return!!this.q&&!(this.r&&!this.q.e&&0==BX(this.q).i);case 10:return(this.Bb&eXt)==0;case 11:return(this.Bb&eJq)!=0;case 12:return(this.Bb&eH0)!=0;case 13:return null!=this.j;case 14:return null!=eOI(this);case 15:return(this.Bb&eJV)!=0;case 16:return(this.Bb&eUR)!=0;case 17:return!!z6(this);case 18:return(this.Bb&eZ1)!=0;case 19:return!!Xl(this)}return VP(this,e-Y1((eBK(),tgw)),ee2((t=Pp(eaS(this,16),26))||tgw,e))},eUe.sh=function(e,t){var n,r;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:GD(this,Lq(t));return;case 2:eli(this,gN(LK(t)));return;case 3:els(this,gN(LK(t)));return;case 4:end(this,Pp(t,19).a);return;case 5:yg(this,Pp(t,19).a);return;case 8:eu2(this,Pp(t,138));return;case 9:(r=ew3(this,Pp(t,87),null))&&r.Fi();return;case 10:elF(this,gN(LK(t)));return;case 11:elU(this,gN(LK(t)));return;case 12:elY(this,gN(LK(t)));return;case 13:xi(this,Lq(t));return;case 15:elB(this,gN(LK(t)));return;case 16:elZ(this,gN(LK(t)));return;case 18:elX(this,gN(LK(t)));return}efL(this,e-Y1((eBK(),tgw)),ee2((n=Pp(eaS(this,16),26))||tgw,e),t)},eUe.zh=function(){return eBK(),tgw},eUe.Bh=function(e){var t,n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:M4(this.Cb,88)&&eko(Zd(Pp(this.Cb,88)),4),er3(this,null);return;case 2:eli(this,!0);return;case 3:els(this,!0);return;case 4:end(this,0);return;case 5:this.b=0,enh(this,1);return;case 8:eu2(this,null);return;case 9:(n=ew3(this,null,null))&&n.Fi();return;case 10:elF(this,!0);return;case 11:elU(this,!1);return;case 12:elY(this,!1);return;case 13:this.i=null,erA(this,null);return;case 15:elB(this,!1);return;case 16:elZ(this,!1);return;case 18:elX(this,!1);return}ec6(this,e-Y1((eBK(),tgw)),ee2((t=Pp(eaS(this,16),26))||tgw,e))},eUe.Gh=function(){eoe(this),UH(QZ((eSp(),tvc),this)),evl(this),this.Bb|=1},eUe.$j=function(){return ek7(this)},eUe.nk=function(e,t){return this.b=0,this.a=null,ecz(this,e,t)},eUe.ok=function(e){yg(this,e)},eUe.Ib=function(){var e;return(64&this.Db)!=0?eCR(this):(e=new O1(eCR(this)),e.a+=" (iD: ",yG(e,(this.Bb&eZ1)!=0),e.a+=")",e.a)},eUe.b=0,Y5(eZ2,"EAttributeImpl",322),eTS(351,438,{105:1,92:1,90:1,138:1,147:1,191:1,56:1,108:1,49:1,97:1,351:1,150:1,114:1,115:1,676:1}),eUe.uk=function(e){return e.Tg()==this},eUe.Qg=function(e){return egn(this,e)},eUe.Rg=function(e,t){this.w=null,this.Db=t<<16|255&this.Db,this.Cb=e},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return null!=this.D?this.D:this.B;case 3:return em4(this);case 4:return this.zj();case 5:return this.F;case 6:if(t)return etP(this);return z5(this);case 7:return this.A||(this.A=new OS(tgs,this,7)),this.A}return Qt(this,e-Y1(this.zh()),ee2((r=Pp(eaS(this,16),26))||this.zh(),e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 6:return this.Cb&&(n=(i=this.Db>>16)>=0?egn(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,6,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Qj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 6:return eDg(this,null,6,n);case 7:return this.A||(this.A=new OS(tgs,this,7)),ep6(this.A,e,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||this.zh(),t),66)).Nj().Rj(this,ehH(this),t-Y1(this.zh()),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return null!=this.D&&this.D==this.F;case 3:return!!em4(this);case 4:return null!=this.zj();case 5:return null!=this.F&&this.F!=this.D&&this.F!=this.B;case 6:return!!z5(this);case 7:return!!this.A&&0!=this.A.i}return VP(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:GN(this,Lq(t));return;case 2:TF(this,Lq(t));return;case 5:eji(this,Lq(t));return;case 7:this.A||(this.A=new OS(tgs,this,7)),eRT(this.A),this.A||(this.A=new OS(tgs,this,7)),Y4(this.A,Pp(t,14));return}efL(this,e-Y1(this.zh()),ee2((n=Pp(eaS(this,16),26))||this.zh(),e),t)},eUe.zh=function(){return eBK(),tgE},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:M4(this.Cb,179)&&(Pp(this.Cb,179).tb=null),er3(this,null);return;case 2:euc(this,null),enp(this,this.D);return;case 5:eji(this,null);return;case 7:this.A||(this.A=new OS(tgs,this,7)),eRT(this.A);return}ec6(this,e-Y1(this.zh()),ee2((t=Pp(eaS(this,16),26))||this.zh(),e))},eUe.yj=function(){var e;return -1==this.G&&(this.G=(e=etP(this))?ebv(e.Mh(),this):-1),this.G},eUe.zj=function(){return null},eUe.Aj=function(){return etP(this)},eUe.vk=function(){return this.v},eUe.Bj=function(){return em4(this)},eUe.Cj=function(){return null!=this.D?this.D:this.B},eUe.Dj=function(){return this.F},eUe.wj=function(e){return eNc(this,e)},eUe.wk=function(e){this.v=e},eUe.xk=function(e){eia(this,e)},eUe.yk=function(e){this.C=e},eUe.Lh=function(e){GN(this,e)},eUe.Ib=function(){return edb(this)},eUe.C=null,eUe.D=null,eUe.G=-1,Y5(eZ2,"EClassifierImpl",351),eTS(88,351,{105:1,92:1,90:1,26:1,138:1,147:1,191:1,56:1,108:1,49:1,97:1,88:1,351:1,150:1,473:1,114:1,115:1,676:1},c0),eUe.uk=function(e){return C7(this,e.Tg())},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return null!=this.D?this.D:this.B;case 3:return em4(this);case 4:return null;case 5:return this.F;case 6:if(t)return etP(this);return z5(this);case 7:return this.A||(this.A=new OS(tgs,this,7)),this.A;case 8:return OQ(),(256&this.Bb)!=0;case 9:return OQ(),(512&this.Bb)!=0;case 10:return $E(this);case 11:return this.q||(this.q=new FQ(tgi,this,11,10)),this.q;case 12:return ePk(this);case 13:return ePl(this);case 14:return ePl(this),this.r;case 15:return ePk(this),this.k;case 16:return eSD(this);case 17:return eNQ(this);case 18:return eNT(this);case 19:return eOg(this);case 20:return ePk(this),this.o;case 21:return this.s||(this.s=new FQ(tm6,this,21,17)),this.s;case 22:return qt(this);case 23:return eCt(this)}return Qt(this,e-Y1((eBK(),tg_)),ee2((r=Pp(eaS(this,16),26))||tg_,e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 6:return this.Cb&&(n=(i=this.Db>>16)>=0?egn(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,6,n);case 11:return this.q||(this.q=new FQ(tgi,this,11,10)),edF(this.q,e,n);case 21:return this.s||(this.s=new FQ(tm6,this,21,17)),edF(this.s,e,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tg_),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tg_)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 6:return eDg(this,null,6,n);case 7:return this.A||(this.A=new OS(tgs,this,7)),ep6(this.A,e,n);case 11:return this.q||(this.q=new FQ(tgi,this,11,10)),ep6(this.q,e,n);case 21:return this.s||(this.s=new FQ(tm6,this,21,17)),ep6(this.s,e,n);case 22:return ep6(qt(this),e,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tg_),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tg_)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return null!=this.D&&this.D==this.F;case 3:return!!em4(this);case 4:return!1;case 5:return null!=this.F&&this.F!=this.D&&this.F!=this.B;case 6:return!!z5(this);case 7:return!!this.A&&0!=this.A.i;case 8:return(256&this.Bb)!=0;case 9:return(512&this.Bb)!=0;case 10:return!!this.u&&0!=qt(this.u.a).i&&!(this.n&&ebV(this.n));case 11:return!!this.q&&0!=this.q.i;case 12:return 0!=ePk(this).i;case 13:return 0!=ePl(this).i;case 14:return ePl(this),0!=this.r.i;case 15:return ePk(this),0!=this.k.i;case 16:return 0!=eSD(this).i;case 17:return 0!=eNQ(this).i;case 18:return 0!=eNT(this).i;case 19:return 0!=eOg(this).i;case 20:return ePk(this),!!this.o;case 21:return!!this.s&&0!=this.s.i;case 22:return!!this.n&&ebV(this.n);case 23:return 0!=eCt(this).i}return VP(this,e-Y1((eBK(),tg_)),ee2((t=Pp(eaS(this,16),26))||tg_,e))},eUe.oh=function(e){var t;return(t=null==this.i||this.q&&0!=this.q.i?null:eAh(this,e))||eF9(this,e)},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:GN(this,Lq(t));return;case 2:TF(this,Lq(t));return;case 5:eji(this,Lq(t));return;case 7:this.A||(this.A=new OS(tgs,this,7)),eRT(this.A),this.A||(this.A=new OS(tgs,this,7)),Y4(this.A,Pp(t,14));return;case 8:ela(this,gN(LK(t)));return;case 9:elu(this,gN(LK(t)));return;case 10:eRP($E(this)),Y4($E(this),Pp(t,14));return;case 11:this.q||(this.q=new FQ(tgi,this,11,10)),eRT(this.q),this.q||(this.q=new FQ(tgi,this,11,10)),Y4(this.q,Pp(t,14));return;case 21:this.s||(this.s=new FQ(tm6,this,21,17)),eRT(this.s),this.s||(this.s=new FQ(tm6,this,21,17)),Y4(this.s,Pp(t,14));return;case 22:eRT(qt(this)),Y4(qt(this),Pp(t,14));return}efL(this,e-Y1((eBK(),tg_)),ee2((n=Pp(eaS(this,16),26))||tg_,e),t)},eUe.zh=function(){return eBK(),tg_},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:M4(this.Cb,179)&&(Pp(this.Cb,179).tb=null),er3(this,null);return;case 2:euc(this,null),enp(this,this.D);return;case 5:eji(this,null);return;case 7:this.A||(this.A=new OS(tgs,this,7)),eRT(this.A);return;case 8:ela(this,!1);return;case 9:elu(this,!1);return;case 10:this.u&&eRP(this.u);return;case 11:this.q||(this.q=new FQ(tgi,this,11,10)),eRT(this.q);return;case 21:this.s||(this.s=new FQ(tm6,this,21,17)),eRT(this.s);return;case 22:this.n&&eRT(this.n);return}ec6(this,e-Y1((eBK(),tg_)),ee2((t=Pp(eaS(this,16),26))||tg_,e))},eUe.Gh=function(){var e,t;if(ePk(this),ePl(this),eSD(this),eNQ(this),eNT(this),eOg(this),eCt(this),ZG(Pw(Zd(this))),this.s)for(e=0,t=this.s.i;e=0;--t)etj(this,t);return edj(this,e)},eUe.Xj=function(){eRT(this)},eUe.oi=function(e,t){return env(this,e,t)},Y5(eJz,"EcoreEList",622),eTS(496,622,eJ9,PK),eUe.ai=function(){return!1},eUe.aj=function(){return this.c},eUe.bj=function(){return!1},eUe.Fk=function(){return!0},eUe.hi=function(){return!0},eUe.li=function(e,t){return t},eUe.ni=function(){return!1},eUe.c=0,Y5(eJz,"EObjectEList",496),eTS(85,496,eJ9,O_),eUe.bj=function(){return!0},eUe.Dk=function(){return!1},eUe.rk=function(){return!0},Y5(eJz,"EObjectContainmentEList",85),eTS(545,85,eJ9,OE),eUe.ci=function(){this.b=!0},eUe.fj=function(){return this.b},eUe.Xj=function(){var e;eRT(this),TO(this.e)?(e=this.b,this.b=!1,eam(this.e,new ZB(this.e,2,this.c,e,!1))):this.b=!1},eUe.b=!1,Y5(eJz,"EObjectContainmentEList/Unsettable",545),eTS(1140,545,eJ9,j4),eUe.ii=function(e,t){var n,r;return n=Pp(elR(this,e,t),87),TO(this.e)&&bz(this,new JU(this.a,7,(eBK(),tgS),ell(t),M4(r=n.c,88)?Pp(r,26):tgI,e)),n},eUe.jj=function(e,t){return edB(this,Pp(e,87),t)},eUe.kj=function(e,t){return edY(this,Pp(e,87),t)},eUe.lj=function(e,t,n){return eyl(this,Pp(e,87),Pp(t,87),n)},eUe.Zi=function(e,t,n,r,i){switch(e){case 3:return Gt(this,e,t,n,r,this.i>1);case 5:return Gt(this,e,t,n,r,this.i-Pp(n,15).gc()>0);default:return new Q$(this.e,e,this.c,t,n,r,!0)}},eUe.ij=function(){return!0},eUe.fj=function(){return ebV(this)},eUe.Xj=function(){eRT(this)},Y5(eZ2,"EClassImpl/1",1140),eTS(1154,1153,eJS),eUe.ui=function(e){var t,n,r,i,a,o,s;if(8!=(n=e.xi())){if(0==(r=epM(e)))switch(n){case 1:case 9:null!=(s=e.Bi())&&((t=Zd(Pp(s,473))).c||(t.c=new sk),eeu(t.c,e.Ai())),null!=(o=e.zi())&&(1&(i=Pp(o,473)).Bb)==0&&((t=Zd(i)).c||(t.c=new sk),JL(t.c,Pp(e.Ai(),26)));break;case 3:null!=(o=e.zi())&&(1&(i=Pp(o,473)).Bb)==0&&((t=Zd(i)).c||(t.c=new sk),JL(t.c,Pp(e.Ai(),26)));break;case 5:if(null!=(o=e.zi()))for(a=Pp(o,14).Kc();a.Ob();)(1&(i=Pp(a.Pb(),473)).Bb)==0&&((t=Zd(i)).c||(t.c=new sk),JL(t.c,Pp(e.Ai(),26)));break;case 4:null!=(s=e.Bi())&&(1&(i=Pp(s,473)).Bb)==0&&((t=Zd(i)).c||(t.c=new sk),eeu(t.c,e.Ai()));break;case 6:if(null!=(s=e.Bi()))for(a=Pp(s,14).Kc();a.Ob();)(1&(i=Pp(a.Pb(),473)).Bb)==0&&((t=Zd(i)).c||(t.c=new sk),eeu(t.c,e.Ai()))}this.Hk(r)}},eUe.Hk=function(e){eCO(this,e)},eUe.b=63,Y5(eZ2,"ESuperAdapter",1154),eTS(1155,1154,eJS,pR),eUe.Hk=function(e){eko(this,e)},Y5(eZ2,"EClassImpl/10",1155),eTS(1144,696,eJ9),eUe.Vh=function(e,t){return ew2(this,e,t)},eUe.Wh=function(e){return emp(this,e)},eUe.Xh=function(e,t){ecW(this,e,t)},eUe.Yh=function(e){Zz(this,e)},eUe.pi=function(e){return J5(this,e)},eUe.mi=function(e,t){return ees(this,e,t)},eUe.lk=function(e,t){throw p7(new bO)},eUe.Zh=function(){return new AY(this)},eUe.$h=function(){return new AB(this)},eUe._h=function(e){return enH(this,e)},eUe.mk=function(e,t){throw p7(new bO)},eUe.Wj=function(e){return this},eUe.fj=function(){return 0!=this.i},eUe.Wb=function(e){throw p7(new bO)},eUe.Xj=function(){throw p7(new bO)},Y5(eJz,"EcoreEList/UnmodifiableEList",1144),eTS(319,1144,eJ9,xQ),eUe.ni=function(){return!1},Y5(eJz,"EcoreEList/UnmodifiableEList/FastCompare",319),eTS(1147,319,eJ9,eo8),eUe.Xc=function(e){var t,n,r;if(M4(e,170)&&-1!=(n=(t=Pp(e,170)).aj())){for(r=this.i;n4){if(!this.wj(e))return!1;if(this.rk()){if(s=(n=(r=Pp(e,49)).Ug())==this.b&&(this.Dk()?r.Og(r.Vg(),Pp(ee2($S(this.b),this.aj()).Yj(),26).Bj())==ebY(Pp(ee2($S(this.b),this.aj()),18)).n:-1-r.Vg()==this.aj()),this.Ek()&&!s&&!n&&r.Zg()){for(i=0;i1||-1==r)},eUe.Dk=function(){var e,t,n;return t=ee2($S(this.b),this.aj()),!!M4(t,99)&&!!(n=ebY(e=Pp(t,18)))},eUe.Ek=function(){var e,t;return t=ee2($S(this.b),this.aj()),!!M4(t,99)&&((e=Pp(t,18)).Bb&eH3)!=0},eUe.Xc=function(e){var t,n,r,i;if((r=this.Qi(e))>=0)return r;if(this.Fk()){for(n=0,i=this.Vi();n=0;--e)ejc(this,e,this.Oi(e));return this.Wi()},eUe.Qc=function(e){var t;if(this.Ek())for(t=this.Vi()-1;t>=0;--t)ejc(this,t,this.Oi(t));return this.Xi(e)},eUe.Xj=function(){eRP(this)},eUe.oi=function(e,t){return J6(this,e,t)},Y5(eJz,"DelegatingEcoreEList",742),eTS(1150,742,eQn,Cw),eUe.Hi=function(e,t){LP(this,e,Pp(t,26))},eUe.Ii=function(e){Mt(this,Pp(e,26))},eUe.Oi=function(e){var t,n;return n=(t=Pp(etj(qt(this.a),e),87)).c,M4(n,88)?Pp(n,26):(eBK(),tgI)},eUe.Ti=function(e){var t,n;return n=(t=Pp(eLN(qt(this.a),e),87)).c,M4(n,88)?Pp(n,26):(eBK(),tgI)},eUe.Ui=function(e,t){return emm(this,e,Pp(t,26))},eUe.ai=function(){return!1},eUe.Zi=function(e,t,n,r,i){return null},eUe.Ji=function(){return new pF(this)},eUe.Ki=function(){eRT(qt(this.a))},eUe.Li=function(e){return ec7(this,e)},eUe.Mi=function(e){var t,n;for(n=e.Kc();n.Ob();)if(!ec7(this,t=n.Pb()))return!1;return!0},eUe.Ni=function(e){var t,n,r;if(M4(e,15)&&(r=Pp(e,15)).gc()==qt(this.a).i){for(t=r.Kc(),n=new Ow(this);t.Ob();)if(xc(t.Pb())!==xc(epH(n)))return!1;return!0}return!1},eUe.Pi=function(){var e,t,n,r,i;for(n=1,t=new Ow(qt(this.a));t.e!=t.i.gc();)e=Pp(epH(t),87),r=M4(i=e.c,88)?Pp(i,26):(eBK(),tgI),n=31*n+(r?Ao(r):0);return n},eUe.Qi=function(e){var t,n,r,i;for(r=0,n=new Ow(qt(this.a));n.e!=n.i.gc();){if(t=Pp(epH(n),87),xc(e)===xc(M4(i=t.c,88)?Pp(i,26):(eBK(),tgI)))return r;++r}return -1},eUe.Ri=function(){return 0==qt(this.a).i},eUe.Si=function(){return null},eUe.Vi=function(){return qt(this.a).i},eUe.Wi=function(){var e,t,n,r,i,a;for(a=qt(this.a).i,i=Je(e1R,eUp,1,a,5,1),n=0,t=new Ow(qt(this.a));t.e!=t.i.gc();)e=Pp(epH(t),87),i[n++]=M4(r=e.c,88)?Pp(r,26):(eBK(),tgI);return i},eUe.Xi=function(e){var t,n,r,i,a,o,s;for(s=qt(this.a).i,e.lengths&&Bc(e,s,null),r=0,n=new Ow(qt(this.a));n.e!=n.i.gc();)t=Pp(epH(n),87),a=M4(o=t.c,88)?Pp(o,26):(eBK(),tgI),Bc(e,r++,a);return e},eUe.Yi=function(){var e,t,n,r,i;for(i=new vs,i.a+="[",e=qt(this.a),t=0,r=qt(this.a).i;t>16)>=0?egn(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,6,n);case 9:return this.a||(this.a=new FQ(tgn,this,9,5)),edF(this.a,e,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgx),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgx)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 6:return eDg(this,null,6,n);case 7:return this.A||(this.A=new OS(tgs,this,7)),ep6(this.A,e,n);case 9:return this.a||(this.a=new FQ(tgn,this,9,5)),ep6(this.a,e,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgx),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgx)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return null!=this.D&&this.D==this.F;case 3:return!!em4(this);case 4:return!!euS(this);case 5:return null!=this.F&&this.F!=this.D&&this.F!=this.B;case 6:return!!z5(this);case 7:return!!this.A&&0!=this.A.i;case 8:return(256&this.Bb)==0;case 9:return!!this.a&&0!=this.a.i}return VP(this,e-Y1((eBK(),tgx)),ee2((t=Pp(eaS(this,16),26))||tgx,e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:GN(this,Lq(t));return;case 2:TF(this,Lq(t));return;case 5:eji(this,Lq(t));return;case 7:this.A||(this.A=new OS(tgs,this,7)),eRT(this.A),this.A||(this.A=new OS(tgs,this,7)),Y4(this.A,Pp(t,14));return;case 8:elo(this,gN(LK(t)));return;case 9:this.a||(this.a=new FQ(tgn,this,9,5)),eRT(this.a),this.a||(this.a=new FQ(tgn,this,9,5)),Y4(this.a,Pp(t,14));return}efL(this,e-Y1((eBK(),tgx)),ee2((n=Pp(eaS(this,16),26))||tgx,e),t)},eUe.zh=function(){return eBK(),tgx},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:M4(this.Cb,179)&&(Pp(this.Cb,179).tb=null),er3(this,null);return;case 2:euc(this,null),enp(this,this.D);return;case 5:eji(this,null);return;case 7:this.A||(this.A=new OS(tgs,this,7)),eRT(this.A);return;case 8:elo(this,!0);return;case 9:this.a||(this.a=new FQ(tgn,this,9,5)),eRT(this.a);return}ec6(this,e-Y1((eBK(),tgx)),ee2((t=Pp(eaS(this,16),26))||tgx,e))},eUe.Gh=function(){var e,t;if(this.a)for(e=0,t=this.a.i;e>16==5?Pp(this.Cb,671):null}return Qt(this,e-Y1((eBK(),tgT)),ee2((r=Pp(eaS(this,16),26))||tgT,e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 5:return this.Cb&&(n=(i=this.Db>>16)>=0?eg3(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,5,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgT),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgT)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 5:return eDg(this,null,5,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgT),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgT)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return 0!=this.d;case 3:return!!this.b;case 4:return null!=this.c;case 5:return!!(this.Db>>16==5?Pp(this.Cb,671):null)}return VP(this,e-Y1((eBK(),tgT)),ee2((t=Pp(eaS(this,16),26))||tgT,e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:er3(this,Lq(t));return;case 2:enf(this,Pp(t,19).a);return;case 3:exP(this,Pp(t,1940));return;case 4:erc(this,Lq(t));return}efL(this,e-Y1((eBK(),tgT)),ee2((n=Pp(eaS(this,16),26))||tgT,e),t)},eUe.zh=function(){return eBK(),tgT},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:er3(this,null);return;case 2:enf(this,0);return;case 3:exP(this,null);return;case 4:erc(this,null);return}ec6(this,e-Y1((eBK(),tgT)),ee2((t=Pp(eaS(this,16),26))||tgT,e))},eUe.Ib=function(){var e;return null==(e=this.c)?this.zb:e},eUe.b=null,eUe.c=null,eUe.d=0,Y5(eZ2,"EEnumLiteralImpl",573);var tgl=RL(eZ2,"EFactoryImpl/InternalEDateTimeFormat");eTS(489,1,{2015:1},pY),Y5(eZ2,"EFactoryImpl/1ClientInternalEDateTimeFormat",489),eTS(241,115,{105:1,92:1,90:1,87:1,56:1,108:1,49:1,97:1,241:1,114:1,115:1},p5),eUe.Sg=function(e,t,n){var r;return n=eDg(this,e,t,n),this.e&&M4(e,170)&&(r=eOl(this,this.e))!=this.c&&(n=eFr(this,r,n)),n},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.f;case 1:return this.d||(this.d=new O_(tgr,this,1)),this.d;case 2:if(t)return eD5(this);return this.c;case 3:return this.b;case 4:return this.e;case 5:if(t)return eb1(this);return this.a}return Qt(this,e-Y1((eBK(),tgO)),ee2((r=Pp(eaS(this,16),26))||tgO,e),t,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return ecg(this,null,n);case 1:return this.d||(this.d=new O_(tgr,this,1)),ep6(this.d,e,n);case 3:return ecm(this,null,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgO),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgO)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.f;case 1:return!!this.d&&0!=this.d.i;case 2:return!!this.c;case 3:return!!this.b;case 4:return!!this.e;case 5:return!!this.a}return VP(this,e-Y1((eBK(),tgO)),ee2((t=Pp(eaS(this,16),26))||tgO,e))},eUe.sh=function(e,t){var n;switch(e){case 0:eyK(this,Pp(t,87));return;case 1:this.d||(this.d=new O_(tgr,this,1)),eRT(this.d),this.d||(this.d=new O_(tgr,this,1)),Y4(this.d,Pp(t,14));return;case 3:eyW(this,Pp(t,87));return;case 4:e_U(this,Pp(t,836));return;case 5:etV(this,Pp(t,138));return}efL(this,e-Y1((eBK(),tgO)),ee2((n=Pp(eaS(this,16),26))||tgO,e),t)},eUe.zh=function(){return eBK(),tgO},eUe.Bh=function(e){var t;switch(e){case 0:eyK(this,null);return;case 1:this.d||(this.d=new O_(tgr,this,1)),eRT(this.d);return;case 3:eyW(this,null);return;case 4:e_U(this,null);return;case 5:etV(this,null);return}ec6(this,e-Y1((eBK(),tgO)),ee2((t=Pp(eaS(this,16),26))||tgO,e))},eUe.Ib=function(){var e;return e=new O0(eMT(this)),e.a+=" (expression: ",ePB(this,e),e.a+=")",e.a},Y5(eZ2,"EGenericTypeImpl",241),eTS(1969,1964,eQr),eUe.Xh=function(e,t){Ch(this,e,t)},eUe.lk=function(e,t){return Ch(this,this.gc(),e),t},eUe.pi=function(e){return ep3(this.Gi(),e)},eUe.Zh=function(){return this.$h()},eUe.Gi=function(){return new pV(this)},eUe.$h=function(){return this._h(0)},eUe._h=function(e){return this.Gi().Zc(e)},eUe.mk=function(e,t){return eds(this,e,!0),t},eUe.ii=function(e,t){var n,r;return r=egW(this,t),(n=this.Zc(e)).Rb(r),r},eUe.ji=function(e,t){var n;eds(this,t,!0),(n=this.Zc(e)).Rb(t)},Y5(eJz,"AbstractSequentialInternalEList",1969),eTS(486,1969,eQr,AA),eUe.pi=function(e){return ep3(this.Gi(),e)},eUe.Zh=function(){return null==this.b?(_2(),_2(),tgq):this.Jk()},eUe.Gi=function(){return new x0(this.a,this.b)},eUe.$h=function(){return null==this.b?(_2(),_2(),tgq):this.Jk()},eUe._h=function(e){var t,n;if(null==this.b){if(e<0||e>1)throw p7(new gE(eJT+e+", size=0"));return _2(),_2(),tgq}for(t=0,n=this.Jk();t0;)if(t=this.c[--this.d],(!this.e||t.Gj()!=e6d||0!=t.aj())&&(!this.Mk()||this.b.mh(t))){if(a=this.b.bh(t,this.Lk()),this.f=(_4(),Pp(t,66).Oj()),this.f||t.$j()){if(this.Lk()?(r=Pp(a,15),this.k=r):(r=Pp(a,69),this.k=this.j=r),M4(this.k,54)?(this.o=this.k.gc(),this.n=this.o):this.p=this.j?this.j._h(this.k.gc()):this.k.Zc(this.k.gc()),this.p?eSs(this,this.p):eSQ(this))return i=this.p?this.p.Ub():this.j?this.j.pi(--this.n):this.k.Xb(--this.n),this.f?((e=Pp(i,72)).ak(),n=e.dd(),this.i=n):(n=i,this.i=n),this.g=-3,!0}else if(null!=a)return this.k=null,this.p=null,n=a,this.i=n,this.g=-2,!0}return this.k=null,this.p=null,this.g=-1,!1}},eUe.Pb=function(){return eaO(this)},eUe.Tb=function(){return this.a},eUe.Ub=function(){var e;if(this.g<-1||this.Sb())return--this.a,this.g=0,e=this.i,this.Sb(),e;throw p7(new bC)},eUe.Vb=function(){return this.a-1},eUe.Qb=function(){throw p7(new bO)},eUe.Lk=function(){return!1},eUe.Wb=function(e){throw p7(new bO)},eUe.Mk=function(){return!0},eUe.a=0,eUe.d=0,eUe.f=!1,eUe.g=0,eUe.n=0,eUe.o=0,Y5(eJz,"EContentsEList/FeatureIteratorImpl",279),eTS(697,279,eQi,Lv),eUe.Lk=function(){return!0},Y5(eJz,"EContentsEList/ResolvingFeatureIteratorImpl",697),eTS(1157,697,eQi,Lw),eUe.Mk=function(){return!1},Y5(eZ2,"ENamedElementImpl/1/1",1157),eTS(1158,279,eQi,Ly),eUe.Mk=function(){return!1},Y5(eZ2,"ENamedElementImpl/1/2",1158),eTS(36,143,eJx,qo,qs,FX,JB,Q$,ZB,en_,WX,enE,WJ,Zj,WQ,enx,W1,ZF,W0,enS,W2,FJ,JU,H0,enk,W3,ZY,W4),eUe._i=function(){return JA(this)},eUe.gj=function(){var e;return(e=JA(this))?e.zj():null},eUe.yi=function(e){return -1==this.b&&this.a&&(this.b=this.c.Xg(this.a.aj(),this.a.Gj())),this.c.Og(this.b,e)},eUe.Ai=function(){return this.c},eUe.hj=function(){var e;return!!(e=JA(this))&&e.Kj()},eUe.b=-1,Y5(eZ2,"ENotificationImpl",36),eTS(399,284,{105:1,92:1,90:1,147:1,191:1,56:1,59:1,108:1,472:1,49:1,97:1,150:1,399:1,284:1,114:1,115:1},mD),eUe.Qg=function(e){return evu(this,e)},eUe._g=function(e,t,n){var r,i,a;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return OQ(),(256&this.Bb)!=0;case 3:return OQ(),(512&this.Bb)!=0;case 4:return ell(this.s);case 5:return ell(this.t);case 6:return OQ(),(a=this.t)>1||-1==a;case 7:return OQ(),(i=this.s)>=1;case 8:if(t)return evl(this);return this.r;case 9:return this.q;case 10:return this.Db>>16==10?Pp(this.Cb,26):null;case 11:return this.d||(this.d=new OS(tgs,this,11)),this.d;case 12:return this.c||(this.c=new FQ(tga,this,12,10)),this.c;case 13:return this.a||(this.a=new C_(this,this)),this.a;case 14:return QX(this)}return Qt(this,e-Y1((eBK(),tgD)),ee2((r=Pp(eaS(this,16),26))||tgD,e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 10:return this.Cb&&(n=(i=this.Db>>16)>=0?evu(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,10,n);case 12:return this.c||(this.c=new FQ(tga,this,12,10)),edF(this.c,e,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgD),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgD)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 9:return Y3(this,n);case 10:return eDg(this,null,10,n);case 11:return this.d||(this.d=new OS(tgs,this,11)),ep6(this.d,e,n);case 12:return this.c||(this.c=new FQ(tga,this,12,10)),ep6(this.c,e,n);case 14:return ep6(QX(this),e,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgD),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgD)),e,n)},eUe.lh=function(e){var t,n,r;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return(256&this.Bb)==0;case 3:return(512&this.Bb)==0;case 4:return 0!=this.s;case 5:return 1!=this.t;case 6:return(r=this.t)>1||-1==r;case 7:return(n=this.s)>=1;case 8:return!!this.r&&!this.q.e&&0==BX(this.q).i;case 9:return!!this.q&&!(this.r&&!this.q.e&&0==BX(this.q).i);case 10:return!!(this.Db>>16==10?Pp(this.Cb,26):null);case 11:return!!this.d&&0!=this.d.i;case 12:return!!this.c&&0!=this.c.i;case 13:return!!this.a&&0!=QX(this.a.a).i&&!(this.b&&ebq(this.b));case 14:return!!this.b&&ebq(this.b)}return VP(this,e-Y1((eBK(),tgD)),ee2((t=Pp(eaS(this,16),26))||tgD,e))},eUe.sh=function(e,t){var n,r;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:er3(this,Lq(t));return;case 2:eli(this,gN(LK(t)));return;case 3:els(this,gN(LK(t)));return;case 4:end(this,Pp(t,19).a);return;case 5:enh(this,Pp(t,19).a);return;case 8:eu2(this,Pp(t,138));return;case 9:(r=ew3(this,Pp(t,87),null))&&r.Fi();return;case 11:this.d||(this.d=new OS(tgs,this,11)),eRT(this.d),this.d||(this.d=new OS(tgs,this,11)),Y4(this.d,Pp(t,14));return;case 12:this.c||(this.c=new FQ(tga,this,12,10)),eRT(this.c),this.c||(this.c=new FQ(tga,this,12,10)),Y4(this.c,Pp(t,14));return;case 13:this.a||(this.a=new C_(this,this)),eRP(this.a),this.a||(this.a=new C_(this,this)),Y4(this.a,Pp(t,14));return;case 14:eRT(QX(this)),Y4(QX(this),Pp(t,14));return}efL(this,e-Y1((eBK(),tgD)),ee2((n=Pp(eaS(this,16),26))||tgD,e),t)},eUe.zh=function(){return eBK(),tgD},eUe.Bh=function(e){var t,n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:er3(this,null);return;case 2:eli(this,!0);return;case 3:els(this,!0);return;case 4:end(this,0);return;case 5:enh(this,1);return;case 8:eu2(this,null);return;case 9:(n=ew3(this,null,null))&&n.Fi();return;case 11:this.d||(this.d=new OS(tgs,this,11)),eRT(this.d);return;case 12:this.c||(this.c=new FQ(tga,this,12,10)),eRT(this.c);return;case 13:this.a&&eRP(this.a);return;case 14:this.b&&eRT(this.b);return}ec6(this,e-Y1((eBK(),tgD)),ee2((t=Pp(eaS(this,16),26))||tgD,e))},eUe.Gh=function(){var e,t;if(this.c)for(e=0,t=this.c.i;es&&Bc(e,s,null),r=0,n=new Ow(QX(this.a));n.e!=n.i.gc();)a=(o=(t=Pp(epH(n),87)).c)||(eBK(),tgA),Bc(e,r++,a);return e},eUe.Yi=function(){var e,t,n,r,i;for(i=new vs,i.a+="[",e=QX(this.a),t=0,r=QX(this.a).i;t1);case 5:return Gt(this,e,t,n,r,this.i-Pp(n,15).gc()>0);default:return new Q$(this.e,e,this.c,t,n,r,!0)}},eUe.ij=function(){return!0},eUe.fj=function(){return ebq(this)},eUe.Xj=function(){eRT(this)},Y5(eZ2,"EOperationImpl/2",1341),eTS(498,1,{1938:1,498:1},k5),Y5(eZ2,"EPackageImpl/1",498),eTS(16,85,eJ9,FQ),eUe.zk=function(){return this.d},eUe.Ak=function(){return this.b},eUe.Dk=function(){return!0},eUe.b=0,Y5(eJz,"EObjectContainmentWithInverseEList",16),eTS(353,16,eJ9,Ia),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectContainmentWithInverseEList/Resolving",353),eTS(298,353,eJ9,Fq),eUe.ci=function(){this.a.tb=null},Y5(eZ2,"EPackageImpl/2",298),eTS(1228,1,{},sh),Y5(eZ2,"EPackageImpl/3",1228),eTS(718,43,e$s,mP),eUe._b=function(e){return xd(e)?$r(this,e):!!$I(this.f,e)},Y5(eZ2,"EPackageRegistryImpl",718),eTS(509,284,{105:1,92:1,90:1,147:1,191:1,56:1,2017:1,108:1,472:1,49:1,97:1,150:1,509:1,284:1,114:1,115:1},mN),eUe.Qg=function(e){return evc(this,e)},eUe._g=function(e,t,n){var r,i,a;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return OQ(),(256&this.Bb)!=0;case 3:return OQ(),(512&this.Bb)!=0;case 4:return ell(this.s);case 5:return ell(this.t);case 6:return OQ(),(a=this.t)>1||-1==a;case 7:return OQ(),(i=this.s)>=1;case 8:if(t)return evl(this);return this.r;case 9:return this.q;case 10:return this.Db>>16==10?Pp(this.Cb,59):null}return Qt(this,e-Y1((eBK(),tgR)),ee2((r=Pp(eaS(this,16),26))||tgR,e),t,n)},eUe.hh=function(e,t,n){var r,i,a;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),edF(this.Ab,e,n);case 10:return this.Cb&&(n=(i=this.Db>>16)>=0?evc(this,n):this.Cb.ih(this,-1-i,null,n)),eDg(this,e,10,n)}return(a=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgR),t),66)).Nj().Qj(this,ehH(this),t-Y1((eBK(),tgR)),e,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 9:return Y3(this,n);case 10:return eDg(this,null,10,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgR),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgR)),e,n)},eUe.lh=function(e){var t,n,r;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return(256&this.Bb)==0;case 3:return(512&this.Bb)==0;case 4:return 0!=this.s;case 5:return 1!=this.t;case 6:return(r=this.t)>1||-1==r;case 7:return(n=this.s)>=1;case 8:return!!this.r&&!this.q.e&&0==BX(this.q).i;case 9:return!!this.q&&!(this.r&&!this.q.e&&0==BX(this.q).i);case 10:return!!(this.Db>>16==10?Pp(this.Cb,59):null)}return VP(this,e-Y1((eBK(),tgR)),ee2((t=Pp(eaS(this,16),26))||tgR,e))},eUe.zh=function(){return eBK(),tgR},Y5(eZ2,"EParameterImpl",509),eTS(99,449,{105:1,92:1,90:1,147:1,191:1,56:1,18:1,170:1,66:1,108:1,472:1,49:1,97:1,150:1,99:1,449:1,284:1,114:1,115:1,677:1},LB),eUe._g=function(e,t,n){var r,i,a,o;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return OQ(),(256&this.Bb)!=0;case 3:return OQ(),(512&this.Bb)!=0;case 4:return ell(this.s);case 5:return ell(this.t);case 6:return OQ(),(o=this.t)>1||-1==o;case 7:return OQ(),(i=this.s)>=1;case 8:if(t)return evl(this);return this.r;case 9:return this.q;case 10:return OQ(),(this.Bb&eXt)!=0;case 11:return OQ(),(this.Bb&eJq)!=0;case 12:return OQ(),(this.Bb&eH0)!=0;case 13:return this.j;case 14:return eOI(this);case 15:return OQ(),(this.Bb&eJV)!=0;case 16:return OQ(),(this.Bb&eUR)!=0;case 17:return z6(this);case 18:return OQ(),(this.Bb&eZ1)!=0;case 19:return OQ(),!!(a=ebY(this))&&(a.Bb&eZ1)!=0;case 20:return OQ(),(this.Bb&eH3)!=0;case 21:if(t)return ebY(this);return this.b;case 22:if(t)return esd(this);return ZS(this);case 23:return this.a||(this.a=new OT(tm9,this,23)),this.a}return Qt(this,e-Y1((eBK(),tgj)),ee2((r=Pp(eaS(this,16),26))||tgj,e),t,n)},eUe.lh=function(e){var t,n,r,i;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return(256&this.Bb)==0;case 3:return(512&this.Bb)==0;case 4:return 0!=this.s;case 5:return 1!=this.t;case 6:return(i=this.t)>1||-1==i;case 7:return(n=this.s)>=1;case 8:return!!this.r&&!this.q.e&&0==BX(this.q).i;case 9:return!!this.q&&!(this.r&&!this.q.e&&0==BX(this.q).i);case 10:return(this.Bb&eXt)==0;case 11:return(this.Bb&eJq)!=0;case 12:return(this.Bb&eH0)!=0;case 13:return null!=this.j;case 14:return null!=eOI(this);case 15:return(this.Bb&eJV)!=0;case 16:return(this.Bb&eUR)!=0;case 17:return!!z6(this);case 18:return(this.Bb&eZ1)!=0;case 19:return!!(r=ebY(this))&&(r.Bb&eZ1)!=0;case 20:return(this.Bb&eH3)==0;case 21:return!!this.b;case 22:return!!ZS(this);case 23:return!!this.a&&0!=this.a.i}return VP(this,e-Y1((eBK(),tgj)),ee2((t=Pp(eaS(this,16),26))||tgj,e))},eUe.sh=function(e,t){var n,r;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:GD(this,Lq(t));return;case 2:eli(this,gN(LK(t)));return;case 3:els(this,gN(LK(t)));return;case 4:end(this,Pp(t,19).a);return;case 5:enh(this,Pp(t,19).a);return;case 8:eu2(this,Pp(t,138));return;case 9:(r=ew3(this,Pp(t,87),null))&&r.Fi();return;case 10:elF(this,gN(LK(t)));return;case 11:elU(this,gN(LK(t)));return;case 12:elY(this,gN(LK(t)));return;case 13:xi(this,Lq(t));return;case 15:elB(this,gN(LK(t)));return;case 16:elZ(this,gN(LK(t)));return;case 18:GI(this,gN(LK(t)));return;case 20:elQ(this,gN(LK(t)));return;case 21:erM(this,Pp(t,18));return;case 23:this.a||(this.a=new OT(tm9,this,23)),eRT(this.a),this.a||(this.a=new OT(tm9,this,23)),Y4(this.a,Pp(t,14));return}efL(this,e-Y1((eBK(),tgj)),ee2((n=Pp(eaS(this,16),26))||tgj,e),t)},eUe.zh=function(){return eBK(),tgj},eUe.Bh=function(e){var t,n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:M4(this.Cb,88)&&eko(Zd(Pp(this.Cb,88)),4),er3(this,null);return;case 2:eli(this,!0);return;case 3:els(this,!0);return;case 4:end(this,0);return;case 5:enh(this,1);return;case 8:eu2(this,null);return;case 9:(n=ew3(this,null,null))&&n.Fi();return;case 10:elF(this,!0);return;case 11:elU(this,!1);return;case 12:elY(this,!1);return;case 13:this.i=null,erA(this,null);return;case 15:elB(this,!1);return;case 16:elZ(this,!1);return;case 18:elJ(this,!1),M4(this.Cb,88)&&eko(Zd(Pp(this.Cb,88)),2);return;case 20:elQ(this,!0);return;case 21:erM(this,null);return;case 23:this.a||(this.a=new OT(tm9,this,23)),eRT(this.a);return}ec6(this,e-Y1((eBK(),tgj)),ee2((t=Pp(eaS(this,16),26))||tgj,e))},eUe.Gh=function(){esd(this),UH(QZ((eSp(),tvc),this)),evl(this),this.Bb|=1},eUe.Lj=function(){return ebY(this)},eUe.qk=function(){var e;return!!(e=ebY(this))&&(e.Bb&eZ1)!=0},eUe.rk=function(){return(this.Bb&eZ1)!=0},eUe.sk=function(){return(this.Bb&eH3)!=0},eUe.nk=function(e,t){return this.c=null,ecz(this,e,t)},eUe.Ib=function(){var e;return(64&this.Db)!=0?eCR(this):(e=new O1(eCR(this)),e.a+=" (containment: ",yG(e,(this.Bb&eZ1)!=0),e.a+=", resolveProxies: ",yG(e,(this.Bb&eH3)!=0),e.a+=")",e.a)},Y5(eZ2,"EReferenceImpl",99),eTS(548,115,{105:1,42:1,92:1,90:1,133:1,56:1,108:1,49:1,97:1,548:1,114:1,115:1},sp),eUe.Fb=function(e){return this===e},eUe.cd=function(){return this.b},eUe.dd=function(){return this.c},eUe.Hb=function(){return Ao(this)},eUe.Uh=function(e){RP(this,Lq(e))},eUe.ed=function(e){return P5(this,Lq(e))},eUe._g=function(e,t,n){var r;switch(e){case 0:return this.b;case 1:return this.c}return Qt(this,e-Y1((eBK(),tgF)),ee2((r=Pp(eaS(this,16),26))||tgF,e),t,n)},eUe.lh=function(e){var t;switch(e){case 0:return null!=this.b;case 1:return null!=this.c}return VP(this,e-Y1((eBK(),tgF)),ee2((t=Pp(eaS(this,16),26))||tgF,e))},eUe.sh=function(e,t){var n;switch(e){case 0:RR(this,Lq(t));return;case 1:ers(this,Lq(t));return}efL(this,e-Y1((eBK(),tgF)),ee2((n=Pp(eaS(this,16),26))||tgF,e),t)},eUe.zh=function(){return eBK(),tgF},eUe.Bh=function(e){var t;switch(e){case 0:ero(this,null);return;case 1:ers(this,null);return}ec6(this,e-Y1((eBK(),tgF)),ee2((t=Pp(eaS(this,16),26))||tgF,e))},eUe.Sh=function(){var e;return -1==this.a&&(e=this.b,this.a=null==e?0:ebA(e)),this.a},eUe.Th=function(e){this.a=e},eUe.Ib=function(){var e;return(64&this.Db)!=0?eMT(this):(e=new O1(eMT(this)),e.a+=" (key: ",xk(e,this.b),e.a+=", value: ",xk(e,this.c),e.a+=")",e.a)},eUe.a=-1,eUe.b=null,eUe.c=null;var tgf=Y5(eZ2,"EStringToStringMapEntryImpl",548),tgd=RL(eJz,"FeatureMap/Entry/Internal");eTS(565,1,eQa),eUe.Ok=function(e){return this.Pk(Pp(e,49))},eUe.Pk=function(e){return this.Ok(e)},eUe.Fb=function(e){var t,n;return this===e||!!M4(e,72)&&(t=Pp(e,72)).ak()==this.c&&(null==(n=this.dd())?null==t.dd():ecX(n,t.dd()))},eUe.ak=function(){return this.c},eUe.Hb=function(){var e;return e=this.dd(),esj(this.c)^(null==e?0:esj(e))},eUe.Ib=function(){var e,t;return t=etP((e=this.c).Hj()).Ph(),e.ne(),(null!=t&&0!=t.length?t+":"+e.ne():e.ne())+"="+this.dd()},Y5(eZ2,"EStructuralFeatureImpl/BasicFeatureMapEntry",565),eTS(776,565,eQa,Cg),eUe.Pk=function(e){return new Cg(this.c,e)},eUe.dd=function(){return this.a},eUe.Qk=function(e,t,n){return eiY(this,e,this.a,t,n)},eUe.Rk=function(e,t,n){return eiB(this,e,this.a,t,n)},Y5(eZ2,"EStructuralFeatureImpl/ContainmentUpdatingFeatureMapEntry",776),eTS(1314,1,{},k6),eUe.Pj=function(e,t,n,r,i){var a;return(a=Pp(JG(e,this.b),215)).nl(this.a).Wj(r)},eUe.Qj=function(e,t,n,r,i){var a;return(a=Pp(JG(e,this.b),215)).el(this.a,r,i)},eUe.Rj=function(e,t,n,r,i){var a;return(a=Pp(JG(e,this.b),215)).fl(this.a,r,i)},eUe.Sj=function(e,t,n){var r;return(r=Pp(JG(e,this.b),215)).nl(this.a).fj()},eUe.Tj=function(e,t,n,r){var i;(i=Pp(JG(e,this.b),215)).nl(this.a).Wb(r)},eUe.Uj=function(e,t,n){return Pp(JG(e,this.b),215).nl(this.a)},eUe.Vj=function(e,t,n){var r;(r=Pp(JG(e,this.b),215)).nl(this.a).Xj()},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateFeatureMapDelegator",1314),eTS(89,1,{},Pe,HS,$F,qc),eUe.Pj=function(e,t,n,r,i){var a;if(null==(a=t.Ch(n))&&t.Dh(n,a=eBN(this,e)),!i)switch(this.e){case 50:case 41:return Pp(a,589).sj();case 40:return Pp(a,215).kl()}return a},eUe.Qj=function(e,t,n,r,i){var a,o;return null==(o=t.Ch(n))&&t.Dh(n,o=eBN(this,e)),a=Pp(o,69).lk(r,i)},eUe.Rj=function(e,t,n,r,i){var a;return null!=(a=t.Ch(n))&&(i=Pp(a,69).mk(r,i)),i},eUe.Sj=function(e,t,n){var r;return null!=(r=t.Ch(n))&&Pp(r,76).fj()},eUe.Tj=function(e,t,n,r){var i;(i=Pp(t.Ch(n),76))||t.Dh(n,i=eBN(this,e)),i.Wb(r)},eUe.Uj=function(e,t,n){var r,i;return(null==(i=t.Ch(n))&&t.Dh(n,i=eBN(this,e)),M4(i,76))?Pp(i,76):(r=Pp(t.Ch(n),15),new pz(r))},eUe.Vj=function(e,t,n){var r;(r=Pp(t.Ch(n),76))||t.Dh(n,r=eBN(this,e)),r.Xj()},eUe.b=0,eUe.e=0,Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateMany",89),eTS(504,1,{}),eUe.Qj=function(e,t,n,r,i){throw p7(new bO)},eUe.Rj=function(e,t,n,r,i){throw p7(new bO)},eUe.Uj=function(e,t,n){return new Hk(this,e,t,n)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingle",504),eTS(1331,1,eJG,Hk),eUe.Wj=function(e){return this.a.Pj(this.c,this.d,this.b,e,!0)},eUe.fj=function(){return this.a.Sj(this.c,this.d,this.b)},eUe.Wb=function(e){this.a.Tj(this.c,this.d,this.b,e)},eUe.Xj=function(){this.a.Vj(this.c,this.d,this.b)},eUe.b=0,Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingle/1",1331),eTS(769,504,{},zl),eUe.Pj=function(e,t,n,r,i){return eIy(e,e.eh(),e.Vg())==this.b?this.sk()&&r?eTp(e):e.eh():null},eUe.Qj=function(e,t,n,r,i){var a,o;return e.eh()&&(i=(a=e.Vg())>=0?e.Qg(i):e.eh().ih(e,-1-a,null,i)),o=edv(e.Tg(),this.e),e.Sg(r,o,i)},eUe.Rj=function(e,t,n,r,i){var a;return a=edv(e.Tg(),this.e),e.Sg(null,a,i)},eUe.Sj=function(e,t,n){var r;return r=edv(e.Tg(),this.e),!!e.eh()&&e.Vg()==r},eUe.Tj=function(e,t,n,r){var i,a,o,s,u;if(null!=r&&!eNc(this.a,r))throw p7(new gA(eQo+(M4(r,56)?eyB(Pp(r,56).Tg()):ee6(esF(r)))+eQs+this.a+"'"));if(i=e.eh(),o=edv(e.Tg(),this.e),xc(r)!==xc(i)||e.Vg()!=o&&null!=r){if(eg7(e,Pp(r,56)))throw p7(new gL(eZ4+e.Ib()));u=null,i&&(u=(a=e.Vg())>=0?e.Qg(u):e.eh().ih(e,-1-a,null,u)),(s=Pp(r,49))&&(u=s.gh(e,edv(s.Tg(),this.b),null,u)),(u=e.Sg(s,o,u))&&u.Fi()}else e.Lg()&&e.Mg()&&eam(e,new FX(e,1,o,r,r))},eUe.Vj=function(e,t,n){var r,i,a,o;(r=e.eh())?(o=(i=e.Vg())>=0?e.Qg(null):e.eh().ih(e,-1-i,null,null),a=edv(e.Tg(),this.e),(o=e.Sg(null,a,o))&&o.Fi()):e.Lg()&&e.Mg()&&eam(e,new FJ(e,1,this.e,null,null))},eUe.sk=function(){return!1},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainer",769),eTS(1315,769,{},Pt),eUe.sk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainerResolving",1315),eTS(563,504,{}),eUe.Pj=function(e,t,n,r,i){var a;return null==(a=t.Ch(n))?this.b:xc(a)===xc(tgZ)?null:a},eUe.Sj=function(e,t,n){var r;return null!=(r=t.Ch(n))&&(xc(r)===xc(tgZ)||!ecX(r,this.b))},eUe.Tj=function(e,t,n,r){var i,a;e.Lg()&&e.Mg()?(i=null==(a=t.Ch(n))?this.b:xc(a)===xc(tgZ)?null:a,null==r?null!=this.c?(t.Dh(n,null),r=this.b):null!=this.b?t.Dh(n,tgZ):t.Dh(n,null):(this.Sk(r),t.Dh(n,r)),eam(e,this.d.Tk(e,1,this.e,i,r))):null==r?null!=this.c?t.Dh(n,null):null!=this.b?t.Dh(n,tgZ):t.Dh(n,null):(this.Sk(r),t.Dh(n,r))},eUe.Vj=function(e,t,n){var r,i;e.Lg()&&e.Mg()?(r=null==(i=t.Ch(n))?this.b:xc(i)===xc(tgZ)?null:i,t.Eh(n),eam(e,this.d.Tk(e,1,this.e,r,this.b))):t.Eh(n)},eUe.Sk=function(e){throw p7(new bk)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData",563),eTS(eQu,1,{},sb),eUe.Tk=function(e,t,n,r,i){return new FJ(e,t,n,r,i)},eUe.Uk=function(e,t,n,r,i,a){return new H0(e,t,n,r,i,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator",eQu),eTS(1332,eQu,{},sm),eUe.Tk=function(e,t,n,r,i){return new ZY(e,t,n,gN(LK(r)),gN(LK(i)))},eUe.Uk=function(e,t,n,r,i,a){return new W4(e,t,n,gN(LK(r)),gN(LK(i)),a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/1",1332),eTS(1333,eQu,{},sg),eUe.Tk=function(e,t,n,r,i){return new en_(e,t,n,Pp(r,217).a,Pp(i,217).a)},eUe.Uk=function(e,t,n,r,i,a){return new WX(e,t,n,Pp(r,217).a,Pp(i,217).a,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/2",1333),eTS(1334,eQu,{},sv),eUe.Tk=function(e,t,n,r,i){return new enE(e,t,n,Pp(r,172).a,Pp(i,172).a)},eUe.Uk=function(e,t,n,r,i,a){return new WJ(e,t,n,Pp(r,172).a,Pp(i,172).a,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/3",1334),eTS(1335,eQu,{},sy),eUe.Tk=function(e,t,n,r,i){return new Zj(e,t,n,gP(LV(r)),gP(LV(i)))},eUe.Uk=function(e,t,n,r,i,a){return new WQ(e,t,n,gP(LV(r)),gP(LV(i)),a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/4",1335),eTS(1336,eQu,{},sw),eUe.Tk=function(e,t,n,r,i){return new enx(e,t,n,Pp(r,155).a,Pp(i,155).a)},eUe.Uk=function(e,t,n,r,i,a){return new W1(e,t,n,Pp(r,155).a,Pp(i,155).a,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/5",1336),eTS(1337,eQu,{},s_),eUe.Tk=function(e,t,n,r,i){return new ZF(e,t,n,Pp(r,19).a,Pp(i,19).a)},eUe.Uk=function(e,t,n,r,i,a){return new W0(e,t,n,Pp(r,19).a,Pp(i,19).a,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/6",1337),eTS(1338,eQu,{},sE),eUe.Tk=function(e,t,n,r,i){return new enS(e,t,n,Pp(r,162).a,Pp(i,162).a)},eUe.Uk=function(e,t,n,r,i,a){return new W2(e,t,n,Pp(r,162).a,Pp(i,162).a,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/7",1338),eTS(1339,eQu,{},sS),eUe.Tk=function(e,t,n,r,i){return new enk(e,t,n,Pp(r,184).a,Pp(i,184).a)},eUe.Uk=function(e,t,n,r,i,a){return new W3(e,t,n,Pp(r,184).a,Pp(i,184).a,a)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/8",1339),eTS(1317,563,{},Hx),eUe.Sk=function(e){if(!this.a.wj(e))throw p7(new gA(eQo+esF(e)+eQs+this.a+"'"))},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataDynamic",1317),eTS(1318,563,{},j6),eUe.Sk=function(e){},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataStatic",1318),eTS(770,563,{}),eUe.Sj=function(e,t,n){var r;return null!=(r=t.Ch(n))},eUe.Tj=function(e,t,n,r){var i,a;e.Lg()&&e.Mg()?(i=!0,null==(a=t.Ch(n))?(i=!1,a=this.b):xc(a)===xc(tgZ)&&(a=null),null==r?null!=this.c?(t.Dh(n,null),r=this.b):t.Dh(n,tgZ):(this.Sk(r),t.Dh(n,r)),eam(e,this.d.Uk(e,1,this.e,a,r,!i))):null==r?null!=this.c?t.Dh(n,null):t.Dh(n,tgZ):(this.Sk(r),t.Dh(n,r))},eUe.Vj=function(e,t,n){var r,i;e.Lg()&&e.Mg()?(r=!0,null==(i=t.Ch(n))?(r=!1,i=this.b):xc(i)===xc(tgZ)&&(i=null),t.Eh(n),eam(e,this.d.Uk(e,2,this.e,i,this.b,r))):t.Eh(n)},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettable",770),eTS(1319,770,{},HT),eUe.Sk=function(e){if(!this.a.wj(e))throw p7(new gA(eQo+esF(e)+eQs+this.a+"'"))},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableDynamic",1319),eTS(1320,770,{},j9),eUe.Sk=function(e){},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableStatic",1320),eTS(398,504,{},jd),eUe.Pj=function(e,t,n,r,i){var a,o,s,u,c;if(c=t.Ch(n),this.Kj()&&xc(c)===xc(tgZ))return null;if(!this.sk()||!r||null==c)return c;if((s=Pp(c,49)).kh()&&(u=ecv(e,s),s!=u)){if(!eNc(this.a,u))throw p7(new gA(eQo+esF(u)+eQs+this.a+"'"));t.Dh(n,c=u),this.rk()&&(a=Pp(u,49),o=s.ih(e,this.b?edv(s.Tg(),this.b):-1-edv(e.Tg(),this.e),null,null),a.eh()||(o=a.gh(e,this.b?edv(a.Tg(),this.b):-1-edv(e.Tg(),this.e),null,o)),o&&o.Fi()),e.Lg()&&e.Mg()&&eam(e,new FJ(e,9,this.e,s,u))}return c},eUe.Qj=function(e,t,n,r,i){var a,o;return xc(o=t.Ch(n))===xc(tgZ)&&(o=null),t.Dh(n,r),this.bj()?xc(o)!==xc(r)&&null!=o&&(i=(a=Pp(o,49)).ih(e,edv(a.Tg(),this.b),null,i)):this.rk()&&null!=o&&(i=Pp(o,49).ih(e,-1-edv(e.Tg(),this.e),null,i)),e.Lg()&&e.Mg()&&(i||(i=new yf(4)),i.Ei(new FJ(e,1,this.e,o,r))),i},eUe.Rj=function(e,t,n,r,i){var a;return xc(a=t.Ch(n))===xc(tgZ)&&(a=null),t.Eh(n),e.Lg()&&e.Mg()&&(i||(i=new yf(4)),this.Kj()?i.Ei(new FJ(e,2,this.e,a,null)):i.Ei(new FJ(e,1,this.e,a,null))),i},eUe.Sj=function(e,t,n){var r;return null!=(r=t.Ch(n))},eUe.Tj=function(e,t,n,r){var i,a,o,s,u;if(null!=r&&!eNc(this.a,r))throw p7(new gA(eQo+(M4(r,56)?eyB(Pp(r,56).Tg()):ee6(esF(r)))+eQs+this.a+"'"));s=null!=(u=t.Ch(n)),this.Kj()&&xc(u)===xc(tgZ)&&(u=null),o=null,this.bj()?xc(u)!==xc(r)&&(null!=u&&(o=(i=Pp(u,49)).ih(e,edv(i.Tg(),this.b),null,o)),null!=r&&(o=(i=Pp(r,49)).gh(e,edv(i.Tg(),this.b),null,o))):this.rk()&&xc(u)!==xc(r)&&(null!=u&&(o=Pp(u,49).ih(e,-1-edv(e.Tg(),this.e),null,o)),null!=r&&(o=Pp(r,49).gh(e,-1-edv(e.Tg(),this.e),null,o))),null==r&&this.Kj()?t.Dh(n,tgZ):t.Dh(n,r),e.Lg()&&e.Mg()?(a=new H0(e,1,this.e,u,r,this.Kj()&&!s),o?(o.Ei(a),o.Fi()):eam(e,a)):o&&o.Fi()},eUe.Vj=function(e,t,n){var r,i,a,o,s;o=null!=(s=t.Ch(n)),this.Kj()&&xc(s)===xc(tgZ)&&(s=null),a=null,null!=s&&(this.bj()?a=(r=Pp(s,49)).ih(e,edv(r.Tg(),this.b),null,a):this.rk()&&(a=Pp(s,49).ih(e,-1-edv(e.Tg(),this.e),null,a))),t.Eh(n),e.Lg()&&e.Mg()?(i=new H0(e,this.Kj()?2:1,this.e,s,null,o),a?(a.Ei(i),a.Fi()):eam(e,i)):a&&a.Fi()},eUe.bj=function(){return!1},eUe.rk=function(){return!1},eUe.sk=function(){return!1},eUe.Kj=function(){return!1},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObject",398),eTS(564,398,{},LE),eUe.rk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainment",564),eTS(1323,564,{},LS),eUe.sk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentResolving",1323),eTS(772,564,{},Lk),eUe.Kj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettable",772),eTS(1325,772,{},Lx),eUe.sk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettableResolving",1325),eTS(640,564,{},Pn),eUe.bj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverse",640),eTS(1324,640,{},Pa),eUe.sk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseResolving",1324),eTS(773,640,{},Po),eUe.Kj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettable",773),eTS(1326,773,{},Ps),eUe.sk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettableResolving",1326),eTS(641,398,{},LT),eUe.sk=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolving",641),eTS(1327,641,{},LM),eUe.Kj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingUnsettable",1327),eTS(774,641,{},Pr),eUe.bj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverse",774),eTS(1328,774,{},Pu),eUe.Kj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverseUnsettable",1328),eTS(1321,398,{},LO),eUe.Kj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectUnsettable",1321),eTS(771,398,{},Pi),eUe.bj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverse",771),eTS(1322,771,{},Pc),eUe.Kj=function(){return!0},Y5(eZ2,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverseUnsettable",1322),eTS(775,565,eQa,Bj),eUe.Pk=function(e){return new Bj(this.a,this.c,e)},eUe.dd=function(){return this.b},eUe.Qk=function(e,t,n){return Jt(this,e,this.b,n)},eUe.Rk=function(e,t,n){return Jn(this,e,this.b,n)},Y5(eZ2,"EStructuralFeatureImpl/InverseUpdatingFeatureMapEntry",775),eTS(1329,1,eJG,pz),eUe.Wj=function(e){return this.a},eUe.fj=function(){return M4(this.a,95)?Pp(this.a,95).fj():!this.a.dc()},eUe.Wb=function(e){this.a.$b(),this.a.Gc(Pp(e,15))},eUe.Xj=function(){M4(this.a,95)?Pp(this.a,95).Xj():this.a.$b()},Y5(eZ2,"EStructuralFeatureImpl/SettingMany",1329),eTS(1330,565,eQa,qf),eUe.Ok=function(e){return new Cv((eR7(),tvK),this.b.Ih(this.a,e))},eUe.dd=function(){return null},eUe.Qk=function(e,t,n){return n},eUe.Rk=function(e,t,n){return n},Y5(eZ2,"EStructuralFeatureImpl/SimpleContentFeatureMapEntry",1330),eTS(642,565,eQa,Cv),eUe.Ok=function(e){return new Cv(this.c,e)},eUe.dd=function(){return this.a},eUe.Qk=function(e,t,n){return n},eUe.Rk=function(e,t,n){return n},Y5(eZ2,"EStructuralFeatureImpl/SimpleFeatureMapEntry",642),eTS(391,497,eXz,sk),eUe.ri=function(e){return Je(tm7,eUp,26,e,0,1)},eUe.ni=function(){return!1},Y5(eZ2,"ESuperAdapter/1",391),eTS(444,438,{105:1,92:1,90:1,147:1,191:1,56:1,108:1,836:1,49:1,97:1,150:1,444:1,114:1,115:1},sx),eUe._g=function(e,t,n){var r;switch(e){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.a||(this.a=new jh(this,tgr,this)),this.a}return Qt(this,e-Y1((eBK(),tgU)),ee2((r=Pp(eaS(this,16),26))||tgU,e),t,n)},eUe.jh=function(e,t,n){var r,i;switch(t){case 0:return this.Ab||(this.Ab=new FQ(tm4,this,0,3)),ep6(this.Ab,e,n);case 2:return this.a||(this.a=new jh(this,tgr,this)),ep6(this.a,e,n)}return(i=Pp(ee2((r=Pp(eaS(this,16),26))||(eBK(),tgU),t),66)).Nj().Rj(this,ehH(this),t-Y1((eBK(),tgU)),e,n)},eUe.lh=function(e){var t;switch(e){case 0:return!!this.Ab&&0!=this.Ab.i;case 1:return null!=this.zb;case 2:return!!this.a&&0!=this.a.i}return VP(this,e-Y1((eBK(),tgU)),ee2((t=Pp(eaS(this,16),26))||tgU,e))},eUe.sh=function(e,t){var n;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab),this.Ab||(this.Ab=new FQ(tm4,this,0,3)),Y4(this.Ab,Pp(t,14));return;case 1:er3(this,Lq(t));return;case 2:this.a||(this.a=new jh(this,tgr,this)),eRT(this.a),this.a||(this.a=new jh(this,tgr,this)),Y4(this.a,Pp(t,14));return}efL(this,e-Y1((eBK(),tgU)),ee2((n=Pp(eaS(this,16),26))||tgU,e),t)},eUe.zh=function(){return eBK(),tgU},eUe.Bh=function(e){var t;switch(e){case 0:this.Ab||(this.Ab=new FQ(tm4,this,0,3)),eRT(this.Ab);return;case 1:er3(this,null);return;case 2:this.a||(this.a=new jh(this,tgr,this)),eRT(this.a);return}ec6(this,e-Y1((eBK(),tgU)),ee2((t=Pp(eaS(this,16),26))||tgU,e))},Y5(eZ2,"ETypeParameterImpl",444),eTS(445,85,eJ9,jh),eUe.cj=function(e,t){return ewV(this,Pp(e,87),t)},eUe.dj=function(e,t){return ewq(this,Pp(e,87),t)},Y5(eZ2,"ETypeParameterImpl/1",445),eTS(634,43,e$s,mR),eUe.ec=function(){return new pG(this)},Y5(eZ2,"ETypeParameterImpl/2",634),eTS(556,eUT,eUM,pG),eUe.Fc=function(e){return Ie(this,Pp(e,87))},eUe.Gc=function(e){var t,n,r;for(r=!1,n=e.Kc();n.Ob();)t=Pp(n.Pb(),87),null==Um(this.a,t,"")&&(r=!0);return r},eUe.$b=function(){Yy(this.a)},eUe.Hc=function(e){return F9(this.a,e)},eUe.Kc=function(){var e;return e=new esz(new fS(this.a).a),new pW(e)},eUe.Mc=function(e){return Xp(this,e)},eUe.gc=function(){return wq(this.a)},Y5(eZ2,"ETypeParameterImpl/2/1",556),eTS(557,1,eUE,pW),eUe.Nb=function(e){F8(this,e)},eUe.Pb=function(){return Pp(etz(this.a).cd(),87)},eUe.Ob=function(){return this.a.b},eUe.Qb=function(){JM(this.a)},Y5(eZ2,"ETypeParameterImpl/2/1/1",557),eTS(1276,43,e$s,mj),eUe._b=function(e){return xd(e)?$r(this,e):!!$I(this.f,e)},eUe.xc=function(e){var t,n;return M4(t=xd(e)?zg(this,e):xu($I(this.f,e)),837)?(t=(n=Pp(t,837))._j(),Um(this,Pp(e,235),t),t):null!=t?t:null==e?(_3(),tvh):null},Y5(eZ2,"EValidatorRegistryImpl",1276),eTS(1313,704,{105:1,92:1,90:1,471:1,147:1,56:1,108:1,1941:1,49:1,97:1,150:1,114:1,115:1},sT),eUe.Ih=function(e,t){switch(e.yj()){case 21:case 22:case 23:case 24:case 26:case 31:case 32:case 37:case 38:case 39:case 40:case 43:case 44:case 48:case 49:case 20:return null==t?null:efF(t);case 25:return etR(t);case 27:return Qn(t);case 28:return Qr(t);case 29:return null==t?null:MU(tmS[0],Pp(t,199));case 41:return null==t?"":yx(Pp(t,290));case 42:return efF(t);case 50:return Lq(t);default:throw p7(new gL(eZ5+e.ne()+eZ6))}},eUe.Jh=function(e){var t;switch(-1==e.G&&(e.G=(t=etP(e))?ebv(t.Mh(),e):-1),e.G){case 0:return new mC;case 1:return new sa;case 2:return new c0;case 4:return new bN;case 5:return new mI;case 6:return new bD;case 7:return new cQ;case 10:return new sr;case 11:return new mD;case 12:return new $y;case 13:return new mN;case 14:return new LB;case 17:return new sp;case 18:return new p5;case 19:return new sx;default:throw p7(new gL(eZ7+e.zb+eZ6))}},eUe.Kh=function(e,t){switch(e.yj()){case 20:return null==t?null:new yY(t);case 21:return null==t?null:new TU(t);case 23:case 22:return null==t?null:ehL(t);case 26:case 24:return null==t?null:eeT(eDa(t,-128,127)<<24>>24);case 25:return eMp(t);case 27:return egg(t);case 28:return egv(t);case 29:return e__(t);case 32:case 31:return null==t?null:eEu(t);case 38:case 37:return null==t?null:new bK(t);case 40:case 39:return null==t?null:ell(eDa(t,eHt,eUu));case 41:case 42:return null;case 44:case 43:return null==t?null:ehQ(eF0(t));case 49:case 48:return null==t?null:elf(eDa(t,eQl,32767)<<16>>16);case 50:return t;default:throw p7(new gL(eZ5+e.ne()+eZ6))}},Y5(eZ2,"EcoreFactoryImpl",1313),eTS(547,179,{105:1,92:1,90:1,147:1,191:1,56:1,235:1,108:1,1939:1,49:1,97:1,150:1,179:1,547:1,114:1,115:1,675:1},Uh),eUe.gb=!1,eUe.hb=!1;var tgh,tgp,tgb,tgm,tgg,tgv,tgy,tgw,tg_,tgE,tgS,tgk,tgx,tgT,tgM,tgO,tgA,tgL,tgC,tgI,tgD,tgN,tgP,tgR,tgj,tgF,tgY,tgB,tgU,tgH,tg$,tgz,tgG,tgW,tgK,tgV,tgq,tgZ,tgX,tgJ,tgQ,tg1,tg0,tg2,tg3,tg4,tg5,tg6,tg9=!1;Y5(eZ2,"EcorePackageImpl",547),eTS(1184,1,{837:1},sM),eUe._j=function(){return OJ(),tvp},Y5(eZ2,"EcorePackageImpl/1",1184),eTS(1193,1,eQS,sO),eUe.wj=function(e){return M4(e,147)},eUe.xj=function(e){return Je(e6y,eUp,147,e,0,1)},Y5(eZ2,"EcorePackageImpl/10",1193),eTS(1194,1,eQS,sA),eUe.wj=function(e){return M4(e,191)},eUe.xj=function(e){return Je(e6_,eUp,191,e,0,1)},Y5(eZ2,"EcorePackageImpl/11",1194),eTS(1195,1,eQS,sL),eUe.wj=function(e){return M4(e,56)},eUe.xj=function(e){return Je(e6f,eUp,56,e,0,1)},Y5(eZ2,"EcorePackageImpl/12",1195),eTS(1196,1,eQS,sC),eUe.wj=function(e){return M4(e,399)},eUe.xj=function(e){return Je(tgi,eJ5,59,e,0,1)},Y5(eZ2,"EcorePackageImpl/13",1196),eTS(1197,1,eQS,sI),eUe.wj=function(e){return M4(e,235)},eUe.xj=function(e){return Je(e6E,eUp,235,e,0,1)},Y5(eZ2,"EcorePackageImpl/14",1197),eTS(1198,1,eQS,sD),eUe.wj=function(e){return M4(e,509)},eUe.xj=function(e){return Je(tga,eUp,2017,e,0,1)},Y5(eZ2,"EcorePackageImpl/15",1198),eTS(1199,1,eQS,sN),eUe.wj=function(e){return M4(e,99)},eUe.xj=function(e){return Je(tgo,eJ4,18,e,0,1)},Y5(eZ2,"EcorePackageImpl/16",1199),eTS(1200,1,eQS,sP),eUe.wj=function(e){return M4(e,170)},eUe.xj=function(e){return Je(tm6,eJ4,170,e,0,1)},Y5(eZ2,"EcorePackageImpl/17",1200),eTS(1201,1,eQS,sR),eUe.wj=function(e){return M4(e,472)},eUe.xj=function(e){return Je(tm5,eUp,472,e,0,1)},Y5(eZ2,"EcorePackageImpl/18",1201),eTS(1202,1,eQS,sj),eUe.wj=function(e){return M4(e,548)},eUe.xj=function(e){return Je(tgf,eJL,548,e,0,1)},Y5(eZ2,"EcorePackageImpl/19",1202),eTS(1185,1,eQS,sF),eUe.wj=function(e){return M4(e,322)},eUe.xj=function(e){return Je(tm9,eJ4,34,e,0,1)},Y5(eZ2,"EcorePackageImpl/2",1185),eTS(1203,1,eQS,sY),eUe.wj=function(e){return M4(e,241)},eUe.xj=function(e){return Je(tgr,eQt,87,e,0,1)},Y5(eZ2,"EcorePackageImpl/20",1203),eTS(1204,1,eQS,sB),eUe.wj=function(e){return M4(e,444)},eUe.xj=function(e){return Je(tgs,eUp,836,e,0,1)},Y5(eZ2,"EcorePackageImpl/21",1204),eTS(1205,1,eQS,sU),eUe.wj=function(e){return xl(e)},eUe.xj=function(e){return Je(e11,eUP,476,e,8,1)},Y5(eZ2,"EcorePackageImpl/22",1205),eTS(1206,1,eQS,sH),eUe.wj=function(e){return M4(e,190)},eUe.xj=function(e){return Je(tyk,eUP,190,e,0,2)},Y5(eZ2,"EcorePackageImpl/23",1206),eTS(1207,1,eQS,s$),eUe.wj=function(e){return M4(e,217)},eUe.xj=function(e){return Je(e10,eUP,217,e,0,1)},Y5(eZ2,"EcorePackageImpl/24",1207),eTS(1208,1,eQS,sz),eUe.wj=function(e){return M4(e,172)},eUe.xj=function(e){return Je(e12,eUP,172,e,0,1)},Y5(eZ2,"EcorePackageImpl/25",1208),eTS(1209,1,eQS,sG),eUe.wj=function(e){return M4(e,199)},eUe.xj=function(e){return Je(e1Q,eUP,199,e,0,1)},Y5(eZ2,"EcorePackageImpl/26",1209),eTS(1210,1,eQS,sW),eUe.wj=function(e){return!1},eUe.xj=function(e){return Je(tyA,eUp,2110,e,0,1)},Y5(eZ2,"EcorePackageImpl/27",1210),eTS(1211,1,eQS,sK),eUe.wj=function(e){return xf(e)},eUe.xj=function(e){return Je(e13,eUP,333,e,7,1)},Y5(eZ2,"EcorePackageImpl/28",1211),eTS(1212,1,eQS,sV),eUe.wj=function(e){return M4(e,58)},eUe.xj=function(e){return Je(e6L,ezZ,58,e,0,1)},Y5(eZ2,"EcorePackageImpl/29",1212),eTS(1186,1,eQS,sq),eUe.wj=function(e){return M4(e,510)},eUe.xj=function(e){return Je(tm4,{3:1,4:1,5:1,1934:1},590,e,0,1)},Y5(eZ2,"EcorePackageImpl/3",1186),eTS(1213,1,eQS,sZ),eUe.wj=function(e){return M4(e,573)},eUe.xj=function(e){return Je(e6j,eUp,1940,e,0,1)},Y5(eZ2,"EcorePackageImpl/30",1213),eTS(1214,1,eQS,sX),eUe.wj=function(e){return M4(e,153)},eUe.xj=function(e){return Je(tg7,ezZ,153,e,0,1)},Y5(eZ2,"EcorePackageImpl/31",1214),eTS(1215,1,eQS,sJ),eUe.wj=function(e){return M4(e,72)},eUe.xj=function(e){return Je(tgc,eQk,72,e,0,1)},Y5(eZ2,"EcorePackageImpl/32",1215),eTS(1216,1,eQS,sQ),eUe.wj=function(e){return M4(e,155)},eUe.xj=function(e){return Je(e14,eUP,155,e,0,1)},Y5(eZ2,"EcorePackageImpl/33",1216),eTS(1217,1,eQS,s1),eUe.wj=function(e){return M4(e,19)},eUe.xj=function(e){return Je(e15,eUP,19,e,0,1)},Y5(eZ2,"EcorePackageImpl/34",1217),eTS(1218,1,eQS,s0),eUe.wj=function(e){return M4(e,290)},eUe.xj=function(e){return Je(e1j,eUp,290,e,0,1)},Y5(eZ2,"EcorePackageImpl/35",1218),eTS(1219,1,eQS,s2),eUe.wj=function(e){return M4(e,162)},eUe.xj=function(e){return Je(e16,eUP,162,e,0,1)},Y5(eZ2,"EcorePackageImpl/36",1219),eTS(1220,1,eQS,s3),eUe.wj=function(e){return M4(e,83)},eUe.xj=function(e){return Je(e1Y,eUp,83,e,0,1)},Y5(eZ2,"EcorePackageImpl/37",1220),eTS(1221,1,eQS,s4),eUe.wj=function(e){return M4(e,591)},eUe.xj=function(e){return Je(tg8,eUp,591,e,0,1)},Y5(eZ2,"EcorePackageImpl/38",1221),eTS(1222,1,eQS,s5),eUe.wj=function(e){return!1},eUe.xj=function(e){return Je(tyL,eUp,2111,e,0,1)},Y5(eZ2,"EcorePackageImpl/39",1222),eTS(1187,1,eQS,s6),eUe.wj=function(e){return M4(e,88)},eUe.xj=function(e){return Je(tm7,eUp,26,e,0,1)},Y5(eZ2,"EcorePackageImpl/4",1187),eTS(1223,1,eQS,s9),eUe.wj=function(e){return M4(e,184)},eUe.xj=function(e){return Je(e19,eUP,184,e,0,1)},Y5(eZ2,"EcorePackageImpl/40",1223),eTS(1224,1,eQS,s8),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eZ2,"EcorePackageImpl/41",1224),eTS(1225,1,eQS,s7),eUe.wj=function(e){return M4(e,588)},eUe.xj=function(e){return Je(e6I,eUp,588,e,0,1)},Y5(eZ2,"EcorePackageImpl/42",1225),eTS(1226,1,eQS,ue),eUe.wj=function(e){return!1},eUe.xj=function(e){return Je(tyC,eUP,2112,e,0,1)},Y5(eZ2,"EcorePackageImpl/43",1226),eTS(1227,1,eQS,ut),eUe.wj=function(e){return M4(e,42)},eUe.xj=function(e){return Je(e1$,eUK,42,e,0,1)},Y5(eZ2,"EcorePackageImpl/44",1227),eTS(1188,1,eQS,un),eUe.wj=function(e){return M4(e,138)},eUe.xj=function(e){return Je(tm8,eUp,138,e,0,1)},Y5(eZ2,"EcorePackageImpl/5",1188),eTS(1189,1,eQS,ur),eUe.wj=function(e){return M4(e,148)},eUe.xj=function(e){return Je(tge,eUp,148,e,0,1)},Y5(eZ2,"EcorePackageImpl/6",1189),eTS(1190,1,eQS,ui),eUe.wj=function(e){return M4(e,457)},eUe.xj=function(e){return Je(tgt,eUp,671,e,0,1)},Y5(eZ2,"EcorePackageImpl/7",1190),eTS(1191,1,eQS,ua),eUe.wj=function(e){return M4(e,573)},eUe.xj=function(e){return Je(tgn,eUp,678,e,0,1)},Y5(eZ2,"EcorePackageImpl/8",1191),eTS(1192,1,eQS,uo),eUe.wj=function(e){return M4(e,471)},eUe.xj=function(e){return Je(e6w,eUp,471,e,0,1)},Y5(eZ2,"EcorePackageImpl/9",1192),eTS(1025,1982,eJO,gT),eUe.bi=function(e,t){ecV(this,Pp(t,415))},eUe.fi=function(e,t){eSU(this,e,Pp(t,415))},Y5(eZ2,"MinimalEObjectImpl/1ArrayDelegatingAdapterList",1025),eTS(1026,143,eJx,BF),eUe.Ai=function(){return this.a.a},Y5(eZ2,"MinimalEObjectImpl/1ArrayDelegatingAdapterList/1",1026),eTS(1053,1052,{},Ms),Y5("org.eclipse.emf.ecore.plugin","EcorePlugin",1053);var tg8=RL(eQx,"Resource");eTS(781,1378,eQT),eUe.Yk=function(e){},eUe.Zk=function(e){},eUe.Vk=function(){return this.a||(this.a=new pK(this)),this.a},eUe.Wk=function(e){var t,n,r,i,a;if((r=e.length)>0){if(GV(0,e.length),47==e.charCodeAt(0)){for(t=1,a=new XM(4),i=1;t0&&(e=e.substr(0,n))}return ekX(this,e)},eUe.Xk=function(){return this.c},eUe.Ib=function(){var e;return yx(this.gm)+"@"+(e=esj(this)>>>0).toString(16)+" uri='"+this.d+"'"},eUe.b=!1,Y5(eQM,"ResourceImpl",781),eTS(1379,781,eQT,pq),Y5(eQM,"BinaryResourceImpl",1379),eTS(1169,694,eXG),eUe.si=function(e){return M4(e,56)?$x(this,Pp(e,56)):M4(e,591)?new Ow(Pp(e,591).Vk()):xc(e)===xc(this.f)?Pp(e,14).Kc():(LF(),tmB.a)},eUe.Ob=function(){return exI(this)},eUe.a=!1,Y5(eJz,"EcoreUtil/ContentTreeIterator",1169),eTS(1380,1169,eXG,F0),eUe.si=function(e){return xc(e)===xc(this.f)?Pp(e,15).Kc():new K0(Pp(e,56))},Y5(eQM,"ResourceImpl/5",1380),eTS(648,1994,eJ6,pK),eUe.Hc=function(e){return this.i<=4?ev9(this,e):M4(e,49)&&Pp(e,49).Zg()==this.a},eUe.bi=function(e,t){e==this.i-1&&(this.a.b||(this.a.b=!0))},eUe.di=function(e,t){0==e?this.a.b||(this.a.b=!0):X8(this,e,t)},eUe.fi=function(e,t){},eUe.gi=function(e,t,n){},eUe.aj=function(){return 2},eUe.Ai=function(){return this.a},eUe.bj=function(){return!0},eUe.cj=function(e,t){var n;return t=(n=Pp(e,49)).wh(this.a,t)},eUe.dj=function(e,t){var n;return(n=Pp(e,49)).wh(null,t)},eUe.ej=function(){return!1},eUe.hi=function(){return!0},eUe.ri=function(e){return Je(e6f,eUp,56,e,0,1)},eUe.ni=function(){return!1},Y5(eQM,"ResourceImpl/ContentsEList",648),eTS(957,1964,eU5,pV),eUe.Zc=function(e){return this.a._h(e)},eUe.gc=function(){return this.a.gc()},Y5(eJz,"AbstractSequentialInternalEList/1",957),eTS(624,1,{},PQ),Y5(eJz,"BasicExtendedMetaData",624),eTS(1160,1,{},k9),eUe.$k=function(){return null},eUe._k=function(){return -2==this.a&&fi(this,e_f(this.d,this.b)),this.a},eUe.al=function(){return null},eUe.bl=function(){return Hj(),Hj(),e2r},eUe.ne=function(){return this.c==eQH&&fo(this,eh1(this.d,this.b)),this.c},eUe.cl=function(){return 0},eUe.a=-2,eUe.c=eQH,Y5(eJz,"BasicExtendedMetaData/EClassExtendedMetaDataImpl",1160),eTS(1161,1,{},Ke),eUe.$k=function(){return this.a==(ZE(),tvf)&&fa(this,eO9(this.f,this.b)),this.a},eUe._k=function(){return 0},eUe.al=function(){return this.c==(ZE(),tvf)&&fs(this,eO8(this.f,this.b)),this.c},eUe.bl=function(){return this.d||fu(this,eIA(this.f,this.b)),this.d},eUe.ne=function(){return this.e==eQH&&fc(this,eh1(this.f,this.b)),this.e},eUe.cl=function(){return -2==this.g&&fl(this,ewd(this.f,this.b)),this.g},eUe.e=eQH,eUe.g=-2,Y5(eJz,"BasicExtendedMetaData/EDataTypeExtendedMetaDataImpl",1161),eTS(1159,1,{},xn),eUe.b=!1,eUe.c=!1,Y5(eJz,"BasicExtendedMetaData/EPackageExtendedMetaDataImpl",1159),eTS(1162,1,{},W7),eUe.c=-2,eUe.e=eQH,eUe.f=eQH,Y5(eJz,"BasicExtendedMetaData/EStructuralFeatureExtendedMetaDataImpl",1162),eTS(585,622,eJ9,PJ),eUe.aj=function(){return this.c},eUe.Fk=function(){return!1},eUe.li=function(e,t){return t},eUe.c=0,Y5(eJz,"EDataTypeEList",585);var tg7=RL(eJz,"FeatureMap");eTS(75,585,{3:1,4:1,20:1,28:1,52:1,14:1,15:1,54:1,67:1,63:1,58:1,76:1,153:1,215:1,1937:1,69:1,95:1},eiR),eUe.Vc=function(e,t){eO0(this,e,Pp(t,72))},eUe.Fc=function(e){return eM6(this,Pp(e,72))},eUe.Yh=function(e){Y2(this,Pp(e,72))},eUe.cj=function(e,t){return IG(this,Pp(e,72),t)},eUe.dj=function(e,t){return IW(this,Pp(e,72),t)},eUe.ii=function(e,t){return eI7(this,e,t)},eUe.li=function(e,t){return ejg(this,e,Pp(t,72))},eUe._c=function(e,t){return eA6(this,e,Pp(t,72))},eUe.jj=function(e,t){return IK(this,Pp(e,72),t)},eUe.kj=function(e,t){return IV(this,Pp(e,72),t)},eUe.lj=function(e,t,n){return eyU(this,Pp(e,72),Pp(t,72),n)},eUe.oi=function(e,t){return ewk(this,e,Pp(t,72))},eUe.dl=function(e,t){return eIF(this,e,t)},eUe.Wc=function(e,t){var n,r,i,a,o,s,u,c,l;for(c=new eta(t.gc()),i=t.Kc();i.Ob();)if(a=(r=Pp(i.Pb(),72)).ak(),eLt(this.e,a))a.hi()&&(Vq(this,a,r.dd())||ev9(c,r))||JL(c,r);else{for(s=0,l=eAY(this.e.Tg(),a),n=Pp(this.g,119),o=!0;s=0;)if(t=e[this.c],this.k.rl(t.ak()))return this.j=this.f?t:t.dd(),this.i=-2,!0;return this.i=-1,this.g=-1,!1},Y5(eJz,"BasicFeatureMap/FeatureEIterator",410),eTS(662,410,eUC,x1),eUe.Lk=function(){return!0},Y5(eJz,"BasicFeatureMap/ResolvingFeatureEIterator",662),eTS(955,486,eQr,Mz),eUe.Gi=function(){return this},Y5(eJz,"EContentsEList/1",955),eTS(956,486,eQr,x0),eUe.Lk=function(){return!1},Y5(eJz,"EContentsEList/2",956),eTS(954,279,eQi,MG),eUe.Nk=function(e){},eUe.Ob=function(){return!1},eUe.Sb=function(){return!1},Y5(eJz,"EContentsEList/FeatureIteratorImpl/1",954),eTS(825,585,eJ9,OM),eUe.ci=function(){this.a=!0},eUe.fj=function(){return this.a},eUe.Xj=function(){var e;eRT(this),TO(this.e)?(e=this.a,this.a=!1,eam(this.e,new ZB(this.e,2,this.c,e,!1))):this.a=!1},eUe.a=!1,Y5(eJz,"EDataTypeEList/Unsettable",825),eTS(1849,585,eJ9,OO),eUe.hi=function(){return!0},Y5(eJz,"EDataTypeUniqueEList",1849),eTS(1850,825,eJ9,OA),eUe.hi=function(){return!0},Y5(eJz,"EDataTypeUniqueEList/Unsettable",1850),eTS(139,85,eJ9,OS),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectContainmentEList/Resolving",139),eTS(1163,545,eJ9,Ok),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectContainmentEList/Unsettable/Resolving",1163),eTS(748,16,eJ9,Io),eUe.ci=function(){this.a=!0},eUe.fj=function(){return this.a},eUe.Xj=function(){var e;eRT(this),TO(this.e)?(e=this.a,this.a=!1,eam(this.e,new ZB(this.e,2,this.c,e,!1))):this.a=!1},eUe.a=!1,Y5(eJz,"EObjectContainmentWithInverseEList/Unsettable",748),eTS(1173,748,eJ9,Is),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectContainmentWithInverseEList/Unsettable/Resolving",1173),eTS(743,496,eJ9,Ox),eUe.ci=function(){this.a=!0},eUe.fj=function(){return this.a},eUe.Xj=function(){var e;eRT(this),TO(this.e)?(e=this.a,this.a=!1,eam(this.e,new ZB(this.e,2,this.c,e,!1))):this.a=!1},eUe.a=!1,Y5(eJz,"EObjectEList/Unsettable",743),eTS(328,496,eJ9,OT),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectResolvingEList",328),eTS(1641,743,eJ9,OL),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectResolvingEList/Unsettable",1641),eTS(1381,1,{},us),Y5(eJz,"EObjectValidator",1381),eTS(546,496,eJ9,F1),eUe.zk=function(){return this.d},eUe.Ak=function(){return this.b},eUe.bj=function(){return!0},eUe.Dk=function(){return!0},eUe.b=0,Y5(eJz,"EObjectWithInverseEList",546),eTS(1176,546,eJ9,Iu),eUe.Ck=function(){return!0},Y5(eJz,"EObjectWithInverseEList/ManyInverse",1176),eTS(625,546,eJ9,Ic),eUe.ci=function(){this.a=!0},eUe.fj=function(){return this.a},eUe.Xj=function(){var e;eRT(this),TO(this.e)?(e=this.a,this.a=!1,eam(this.e,new ZB(this.e,2,this.c,e,!1))):this.a=!1},eUe.a=!1,Y5(eJz,"EObjectWithInverseEList/Unsettable",625),eTS(1175,625,eJ9,If),eUe.Ck=function(){return!0},Y5(eJz,"EObjectWithInverseEList/Unsettable/ManyInverse",1175),eTS(749,546,eJ9,Il),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectWithInverseResolvingEList",749),eTS(31,749,eJ9,Ih),eUe.Ck=function(){return!0},Y5(eJz,"EObjectWithInverseResolvingEList/ManyInverse",31),eTS(750,625,eJ9,Id),eUe.Ek=function(){return!0},eUe.li=function(e,t){return ex7(this,e,Pp(t,56))},Y5(eJz,"EObjectWithInverseResolvingEList/Unsettable",750),eTS(1174,750,eJ9,Ip),eUe.Ck=function(){return!0},Y5(eJz,"EObjectWithInverseResolvingEList/Unsettable/ManyInverse",1174),eTS(1164,622,eJ9),eUe.ai=function(){return(1792&this.b)==0},eUe.ci=function(){this.b|=1},eUe.Bk=function(){return(4&this.b)!=0},eUe.bj=function(){return(40&this.b)!=0},eUe.Ck=function(){return(16&this.b)!=0},eUe.Dk=function(){return(8&this.b)!=0},eUe.Ek=function(){return(this.b&eJq)!=0},eUe.rk=function(){return(32&this.b)!=0},eUe.Fk=function(){return(this.b&eXt)!=0},eUe.wj=function(e){return this.d?VB(this.d,e):this.ak().Yj().wj(e)},eUe.fj=function(){return(2&this.b)!=0?(1&this.b)!=0:0!=this.i},eUe.hi=function(){return(128&this.b)!=0},eUe.Xj=function(){var e;eRT(this),(2&this.b)!=0&&(TO(this.e)?(e=(1&this.b)!=0,this.b&=-2,bz(this,new ZB(this.e,2,edv(this.e.Tg(),this.ak()),e,!1))):this.b&=-2)},eUe.ni=function(){return(1536&this.b)==0},eUe.b=0,Y5(eJz,"EcoreEList/Generic",1164),eTS(1165,1164,eJ9,H2),eUe.ak=function(){return this.a},Y5(eJz,"EcoreEList/Dynamic",1165),eTS(747,63,eXz,pZ),eUe.ri=function(e){return enb(this.a.a,e)},Y5(eJz,"EcoreEMap/1",747),eTS(746,85,eJ9,FZ),eUe.bi=function(e,t){ebB(this.b,Pp(t,133))},eUe.di=function(e,t){eac(this.b)},eUe.ei=function(e,t,n){var r;++(r=this.b,Pp(t,133),r).e},eUe.fi=function(e,t){elj(this.b,Pp(t,133))},eUe.gi=function(e,t,n){elj(this.b,Pp(n,133)),xc(n)===xc(t)&&Pp(n,133).Th(Mi(Pp(t,133).cd())),ebB(this.b,Pp(t,133))},Y5(eJz,"EcoreEMap/DelegateEObjectContainmentEList",746),eTS(1171,151,eJW,enQ),Y5(eJz,"EcoreEMap/Unsettable",1171),eTS(1172,746,eJ9,Ib),eUe.ci=function(){this.a=!0},eUe.fj=function(){return this.a},eUe.Xj=function(){var e;eRT(this),TO(this.e)?(e=this.a,this.a=!1,eam(this.e,new ZB(this.e,2,this.c,e,!1))):this.a=!1},eUe.a=!1,Y5(eJz,"EcoreEMap/Unsettable/UnsettableDelegateEObjectContainmentEList",1172),eTS(1168,228,e$s,YQ),eUe.a=!1,eUe.b=!1,Y5(eJz,"EcoreUtil/Copier",1168),eTS(745,1,eUE,K0),eUe.Nb=function(e){F8(this,e)},eUe.Ob=function(){return edV(this)},eUe.Pb=function(){var e;return edV(this),e=this.b,this.b=null,e},eUe.Qb=function(){this.a.Qb()},Y5(eJz,"EcoreUtil/ProperContentIterator",745),eTS(1382,1381,{},c2),Y5(eJz,"EcoreValidator",1382),RL(eJz,"FeatureMapUtil/Validator"),eTS(1260,1,{1942:1},uu),eUe.rl=function(e){return!0},Y5(eJz,"FeatureMapUtil/1",1260),eTS(757,1,{1942:1},eF2),eUe.rl=function(e){var t;return this.c==e||(null!=(t=LK(Bp(this.a,e)))?t==(OQ(),e0P):eCV(this,e)?(Z$(this.a,e,(OQ(),e0P)),!0):(Z$(this.a,e,(OQ(),e0N)),!1))},eUe.e=!1,Y5(eJz,"FeatureMapUtil/BasicValidator",757),eTS(758,43,e$s,MW),Y5(eJz,"FeatureMapUtil/BasicValidator/Cache",758),eTS(501,52,{20:1,28:1,52:1,14:1,15:1,58:1,76:1,69:1,95:1},xe),eUe.Vc=function(e,t){eLe(this.c,this.b,e,t)},eUe.Fc=function(e){return eIF(this.c,this.b,e)},eUe.Wc=function(e,t){return ePq(this.c,this.b,e,t)},eUe.Gc=function(e){return MJ(this,e)},eUe.Xh=function(e,t){ee7(this.c,this.b,e,t)},eUe.lk=function(e,t){return eCB(this.c,this.b,e,t)},eUe.pi=function(e){return ePL(this.c,this.b,e,!1)},eUe.Zh=function(){return TC(this.c,this.b)},eUe.$h=function(){return TI(this.c,this.b)},eUe._h=function(e){return X9(this.c,this.b,e)},eUe.mk=function(e,t){return Cp(this,e,t)},eUe.$b=function(){bG(this)},eUe.Hc=function(e){return Vq(this.c,this.b,e)},eUe.Ic=function(e){return eiF(this.c,this.b,e)},eUe.Xb=function(e){return ePL(this.c,this.b,e,!0)},eUe.Wj=function(e){return this},eUe.Xc=function(e){return VZ(this.c,this.b,e)},eUe.dc=function(){return xs(this)},eUe.fj=function(){return!edK(this.c,this.b)},eUe.Kc=function(){return eei(this.c,this.b)},eUe.Yc=function(){return eea(this.c,this.b)},eUe.Zc=function(e){return ely(this.c,this.b,e)},eUe.ii=function(e,t){return eNn(this.c,this.b,e,t)},eUe.ji=function(e,t){Xx(this.c,this.b,e,t)},eUe.$c=function(e){return eE0(this.c,this.b,e)},eUe.Mc=function(e){return eIC(this.c,this.b,e)},eUe._c=function(e,t){return eNL(this.c,this.b,e,t)},eUe.Wb=function(e){exZ(this.c,this.b),MJ(this,Pp(e,15))},eUe.gc=function(){return elG(this.c,this.b)},eUe.Pc=function(){return Wb(this.c,this.b)},eUe.Qc=function(e){return VX(this.c,this.b,e)},eUe.Ib=function(){var e,t;for(t=new vs,t.a+="[",e=TC(this.c,this.b);euf(e);)xk(t,Ae(ebm(e))),euf(e)&&(t.a+=eUd);return t.a+="]",t.a},eUe.Xj=function(){exZ(this.c,this.b)},Y5(eJz,"FeatureMapUtil/FeatureEList",501),eTS(627,36,eJx,qu),eUe.yi=function(e){return elc(this,e)},eUe.Di=function(e){var t,n,r,i,a,o,s;switch(this.d){case 1:case 2:if(xc(a=e.Ai())===xc(this.c)&&elc(this,null)==e.yi(null))return this.g=e.zi(),1==e.xi()&&(this.d=1),!0;break;case 3:if(3===(i=e.xi())&&xc(a=e.Ai())===xc(this.c)&&elc(this,null)==e.yi(null))return this.d=5,JL(t=new eta(2),this.g),JL(t,e.zi()),this.g=t,!0;break;case 5:if(3===(i=e.xi())&&xc(a=e.Ai())===xc(this.c)&&elc(this,null)==e.yi(null))return(n=Pp(this.g,14)).Fc(e.zi()),!0;break;case 4:switch(i=e.xi()){case 3:if(xc(a=e.Ai())===xc(this.c)&&elc(this,null)==e.yi(null))return this.d=1,this.g=e.zi(),!0;break;case 4:if(xc(a=e.Ai())===xc(this.c)&&elc(this,null)==e.yi(null))return this.d=6,JL(s=new eta(2),this.n),JL(s,e.Bi()),this.n=s,o=eow(vx(ty_,1),eHT,25,15,[this.o,e.Ci()]),this.g=o,!0}break;case 6:if(4===(i=e.xi())&&xc(a=e.Ai())===xc(this.c)&&elc(this,null)==e.yi(null))return(n=Pp(this.n,14)).Fc(e.Bi()),r=Je(ty_,eHT,25,(o=Pp(this.g,48)).length+1,15,1),ePD(o,0,r,0,o.length),r[o.length]=e.Ci(),this.g=r,!0}return!1},Y5(eJz,"FeatureMapUtil/FeatureENotificationImpl",627),eTS(552,501,{20:1,28:1,52:1,14:1,15:1,58:1,76:1,153:1,215:1,1937:1,69:1,95:1},RA),eUe.dl=function(e,t){return eIF(this.c,e,t)},eUe.el=function(e,t,n){return eCB(this.c,e,t,n)},eUe.fl=function(e,t,n){return ePT(this.c,e,t,n)},eUe.gl=function(){return this},eUe.hl=function(e,t){return ePC(this.c,e,t)},eUe.il=function(e){return Pp(ePL(this.c,this.b,e,!1),72).ak()},eUe.jl=function(e){return Pp(ePL(this.c,this.b,e,!1),72).dd()},eUe.kl=function(){return this.a},eUe.ll=function(e){return!edK(this.c,e)},eUe.ml=function(e,t){ePJ(this.c,e,t)},eUe.nl=function(e){return erp(this.c,e)},eUe.ol=function(e){emY(this.c,e)},Y5(eJz,"FeatureMapUtil/FeatureFeatureMap",552),eTS(1259,1,eJG,xr),eUe.Wj=function(e){return ePL(this.b,this.a,-1,e)},eUe.fj=function(){return!edK(this.b,this.a)},eUe.Wb=function(e){ePJ(this.b,this.a,e)},eUe.Xj=function(){exZ(this.b,this.a)},Y5(eJz,"FeatureMapUtil/FeatureValue",1259);var tve=RL(eQz,"AnyType");eTS(666,60,eHr,gV),Y5(eQz,"InvalidDatatypeValueException",666);var tvt=RL(eQz,eQG),tvn=RL(eQz,eQW),tvr=RL(eQz,eQK);eTS(830,506,{105:1,92:1,90:1,56:1,49:1,97:1,843:1},mF),eUe._g=function(e,t,n){switch(e){case 0:if(n)return this.c||(this.c=new eiR(this,0)),this.c;return this.c||(this.c=new eiR(this,0)),this.c.b;case 1:if(n)return this.c||(this.c=new eiR(this,0)),Pp(GP(this.c,(eR7(),tvx)),153);return(this.c||(this.c=new eiR(this,0)),Pp(Pp(GP(this.c,(eR7(),tvx)),153),215)).kl();case 2:if(n)return this.b||(this.b=new eiR(this,2)),this.b;return this.b||(this.b=new eiR(this,2)),this.b.b}return Qt(this,e-Y1(this.zh()),ee2((2&this.j)==0?this.zh():(this.k||(this.k=new c1),this.k).ck(),e),t,n)},eUe.jh=function(e,t,n){var r;switch(t){case 0:return this.c||(this.c=new eiR(this,0)),eIM(this.c,e,n);case 1:return(this.c||(this.c=new eiR(this,0)),Pp(Pp(GP(this.c,(eR7(),tvx)),153),69)).mk(e,n);case 2:return this.b||(this.b=new eiR(this,2)),eIM(this.b,e,n)}return(r=Pp(ee2((2&this.j)==0?this.zh():(this.k||(this.k=new c1),this.k).ck(),t),66)).Nj().Rj(this,Q5(this),t-Y1(this.zh()),e,n)},eUe.lh=function(e){switch(e){case 0:return!!this.c&&0!=this.c.i;case 1:return!(this.c||(this.c=new eiR(this,0)),Pp(GP(this.c,(eR7(),tvx)),153)).dc();case 2:return!!this.b&&0!=this.b.i}return VP(this,e-Y1(this.zh()),ee2((2&this.j)==0?this.zh():(this.k||(this.k=new c1),this.k).ck(),e))},eUe.sh=function(e,t){switch(e){case 0:this.c||(this.c=new eiR(this,0)),YH(this.c,t);return;case 1:(this.c||(this.c=new eiR(this,0)),Pp(Pp(GP(this.c,(eR7(),tvx)),153),215)).Wb(t);return;case 2:this.b||(this.b=new eiR(this,2)),YH(this.b,t);return}efL(this,e-Y1(this.zh()),ee2((2&this.j)==0?this.zh():(this.k||(this.k=new c1),this.k).ck(),e),t)},eUe.zh=function(){return eR7(),tvk},eUe.Bh=function(e){switch(e){case 0:this.c||(this.c=new eiR(this,0)),eRT(this.c);return;case 1:(this.c||(this.c=new eiR(this,0)),Pp(GP(this.c,(eR7(),tvx)),153)).$b();return;case 2:this.b||(this.b=new eiR(this,2)),eRT(this.b);return}ec6(this,e-Y1(this.zh()),ee2((2&this.j)==0?this.zh():(this.k||(this.k=new c1),this.k).ck(),e))},eUe.Ib=function(){var e;return(4&this.j)!=0?eMT(this):(e=new O1(eMT(this)),e.a+=" (mixed: ",xS(e,this.c),e.a+=", anyAttribute: ",xS(e,this.b),e.a+=")",e.a)},Y5(eQV,"AnyTypeImpl",830),eTS(667,506,{105:1,92:1,90:1,56:1,49:1,97:1,2021:1,667:1},ul),eUe._g=function(e,t,n){switch(e){case 0:return this.a;case 1:return this.b}return Qt(this,e-Y1((eR7(),tvj)),ee2((2&this.j)==0?tvj:(this.k||(this.k=new c1),this.k).ck(),e),t,n)},eUe.lh=function(e){switch(e){case 0:return null!=this.a;case 1:return null!=this.b}return VP(this,e-Y1((eR7(),tvj)),ee2((2&this.j)==0?tvj:(this.k||(this.k=new c1),this.k).ck(),e))},eUe.sh=function(e,t){switch(e){case 0:fg(this,Lq(t));return;case 1:fv(this,Lq(t));return}efL(this,e-Y1((eR7(),tvj)),ee2((2&this.j)==0?tvj:(this.k||(this.k=new c1),this.k).ck(),e),t)},eUe.zh=function(){return eR7(),tvj},eUe.Bh=function(e){switch(e){case 0:this.a=null;return;case 1:this.b=null;return}ec6(this,e-Y1((eR7(),tvj)),ee2((2&this.j)==0?tvj:(this.k||(this.k=new c1),this.k).ck(),e))},eUe.Ib=function(){var e;return(4&this.j)!=0?eMT(this):(e=new O1(eMT(this)),e.a+=" (data: ",xk(e,this.a),e.a+=", target: ",xk(e,this.b),e.a+=")",e.a)},eUe.a=null,eUe.b=null,Y5(eQV,"ProcessingInstructionImpl",667),eTS(668,830,{105:1,92:1,90:1,56:1,49:1,97:1,843:1,2022:1,668:1},mB),eUe._g=function(e,t,n){switch(e){case 0:if(n)return this.c||(this.c=new eiR(this,0)),this.c;return this.c||(this.c=new eiR(this,0)),this.c.b;case 1:if(n)return this.c||(this.c=new eiR(this,0)),Pp(GP(this.c,(eR7(),tvx)),153);return(this.c||(this.c=new eiR(this,0)),Pp(Pp(GP(this.c,(eR7(),tvx)),153),215)).kl();case 2:if(n)return this.b||(this.b=new eiR(this,2)),this.b;return this.b||(this.b=new eiR(this,2)),this.b.b;case 3:return this.c||(this.c=new eiR(this,0)),Lq(ePC(this.c,(eR7(),tvB),!0));case 4:return Iy(this.a,(this.c||(this.c=new eiR(this,0)),Lq(ePC(this.c,(eR7(),tvB),!0))));case 5:return this.a}return Qt(this,e-Y1((eR7(),tvY)),ee2((2&this.j)==0?tvY:(this.k||(this.k=new c1),this.k).ck(),e),t,n)},eUe.lh=function(e){switch(e){case 0:return!!this.c&&0!=this.c.i;case 1:return!(this.c||(this.c=new eiR(this,0)),Pp(GP(this.c,(eR7(),tvx)),153)).dc();case 2:return!!this.b&&0!=this.b.i;case 3:return this.c||(this.c=new eiR(this,0)),null!=Lq(ePC(this.c,(eR7(),tvB),!0));case 4:return null!=Iy(this.a,(this.c||(this.c=new eiR(this,0)),Lq(ePC(this.c,(eR7(),tvB),!0))));case 5:return!!this.a}return VP(this,e-Y1((eR7(),tvY)),ee2((2&this.j)==0?tvY:(this.k||(this.k=new c1),this.k).ck(),e))},eUe.sh=function(e,t){switch(e){case 0:this.c||(this.c=new eiR(this,0)),YH(this.c,t);return;case 1:(this.c||(this.c=new eiR(this,0)),Pp(Pp(GP(this.c,(eR7(),tvx)),153),215)).Wb(t);return;case 2:this.b||(this.b=new eiR(this,2)),YH(this.b,t);return;case 3:Kt(this,Lq(t));return;case 4:Kt(this,Iw(this.a,t));return;case 5:fy(this,Pp(t,148));return}efL(this,e-Y1((eR7(),tvY)),ee2((2&this.j)==0?tvY:(this.k||(this.k=new c1),this.k).ck(),e),t)},eUe.zh=function(){return eR7(),tvY},eUe.Bh=function(e){switch(e){case 0:this.c||(this.c=new eiR(this,0)),eRT(this.c);return;case 1:(this.c||(this.c=new eiR(this,0)),Pp(GP(this.c,(eR7(),tvx)),153)).$b();return;case 2:this.b||(this.b=new eiR(this,2)),eRT(this.b);return;case 3:this.c||(this.c=new eiR(this,0)),ePJ(this.c,(eR7(),tvB),null);return;case 4:Kt(this,Iw(this.a,null));return;case 5:this.a=null;return}ec6(this,e-Y1((eR7(),tvY)),ee2((2&this.j)==0?tvY:(this.k||(this.k=new c1),this.k).ck(),e))},Y5(eQV,"SimpleAnyTypeImpl",668),eTS(669,506,{105:1,92:1,90:1,56:1,49:1,97:1,2023:1,669:1},mY),eUe._g=function(e,t,n){switch(e){case 0:if(n)return this.a||(this.a=new eiR(this,0)),this.a;return this.a||(this.a=new eiR(this,0)),this.a.b;case 1:return n?(this.b||(this.b=new JY((eBK(),tgF),tgf,this,1)),this.b):(this.b||(this.b=new JY((eBK(),tgF),tgf,this,1)),X6(this.b));case 2:return n?(this.c||(this.c=new JY((eBK(),tgF),tgf,this,2)),this.c):(this.c||(this.c=new JY((eBK(),tgF),tgf,this,2)),X6(this.c));case 3:return this.a||(this.a=new eiR(this,0)),GP(this.a,(eR7(),tv$));case 4:return this.a||(this.a=new eiR(this,0)),GP(this.a,(eR7(),tvz));case 5:return this.a||(this.a=new eiR(this,0)),GP(this.a,(eR7(),tvW));case 6:return this.a||(this.a=new eiR(this,0)),GP(this.a,(eR7(),tvK))}return Qt(this,e-Y1((eR7(),tvH)),ee2((2&this.j)==0?tvH:(this.k||(this.k=new c1),this.k).ck(),e),t,n)},eUe.jh=function(e,t,n){var r;switch(t){case 0:return this.a||(this.a=new eiR(this,0)),eIM(this.a,e,n);case 1:return this.b||(this.b=new JY((eBK(),tgF),tgf,this,1)),Iz(this.b,e,n);case 2:return this.c||(this.c=new JY((eBK(),tgF),tgf,this,2)),Iz(this.c,e,n);case 5:return this.a||(this.a=new eiR(this,0)),Cp(GP(this.a,(eR7(),tvW)),e,n)}return(r=Pp(ee2((2&this.j)==0?(eR7(),tvH):(this.k||(this.k=new c1),this.k).ck(),t),66)).Nj().Rj(this,Q5(this),t-Y1((eR7(),tvH)),e,n)},eUe.lh=function(e){switch(e){case 0:return!!this.a&&0!=this.a.i;case 1:return!!this.b&&0!=this.b.f;case 2:return!!this.c&&0!=this.c.f;case 3:return this.a||(this.a=new eiR(this,0)),!xs(GP(this.a,(eR7(),tv$)));case 4:return this.a||(this.a=new eiR(this,0)),!xs(GP(this.a,(eR7(),tvz)));case 5:return this.a||(this.a=new eiR(this,0)),!xs(GP(this.a,(eR7(),tvW)));case 6:return this.a||(this.a=new eiR(this,0)),!xs(GP(this.a,(eR7(),tvK)))}return VP(this,e-Y1((eR7(),tvH)),ee2((2&this.j)==0?tvH:(this.k||(this.k=new c1),this.k).ck(),e))},eUe.sh=function(e,t){switch(e){case 0:this.a||(this.a=new eiR(this,0)),YH(this.a,t);return;case 1:this.b||(this.b=new JY((eBK(),tgF),tgf,this,1)),eai(this.b,t);return;case 2:this.c||(this.c=new JY((eBK(),tgF),tgf,this,2)),eai(this.c,t);return;case 3:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tv$))),this.a||(this.a=new eiR(this,0)),MJ(GP(this.a,tv$),Pp(t,14));return;case 4:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tvz))),this.a||(this.a=new eiR(this,0)),MJ(GP(this.a,tvz),Pp(t,14));return;case 5:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tvW))),this.a||(this.a=new eiR(this,0)),MJ(GP(this.a,tvW),Pp(t,14));return;case 6:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tvK))),this.a||(this.a=new eiR(this,0)),MJ(GP(this.a,tvK),Pp(t,14));return}efL(this,e-Y1((eR7(),tvH)),ee2((2&this.j)==0?tvH:(this.k||(this.k=new c1),this.k).ck(),e),t)},eUe.zh=function(){return eR7(),tvH},eUe.Bh=function(e){switch(e){case 0:this.a||(this.a=new eiR(this,0)),eRT(this.a);return;case 1:this.b||(this.b=new JY((eBK(),tgF),tgf,this,1)),this.b.c.$b();return;case 2:this.c||(this.c=new JY((eBK(),tgF),tgf,this,2)),this.c.c.$b();return;case 3:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tv$)));return;case 4:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tvz)));return;case 5:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tvW)));return;case 6:this.a||(this.a=new eiR(this,0)),bG(GP(this.a,(eR7(),tvK)));return}ec6(this,e-Y1((eR7(),tvH)),ee2((2&this.j)==0?tvH:(this.k||(this.k=new c1),this.k).ck(),e))},eUe.Ib=function(){var e;return(4&this.j)!=0?eMT(this):(e=new O1(eMT(this)),e.a+=" (mixed: ",xS(e,this.a),e.a+=")",e.a)},Y5(eQV,"XMLTypeDocumentRootImpl",669),eTS(1919,704,{105:1,92:1,90:1,471:1,147:1,56:1,108:1,49:1,97:1,150:1,114:1,115:1,2024:1},uc),eUe.Ih=function(e,t){switch(e.yj()){case 7:case 8:case 9:case 10:case 16:case 22:case 23:case 24:case 25:case 26:case 32:case 33:case 34:case 36:case 37:case 44:case 45:case 50:case 51:case 53:case 55:case 56:case 57:case 58:case 60:case 61:case 4:return null==t?null:efF(t);case 19:case 28:case 29:case 35:case 38:case 39:case 41:case 46:case 52:case 54:case 5:return Lq(t);case 6:return LH(Pp(t,190));case 12:case 47:case 49:case 11:return ejZ(this,e,t);case 13:return null==t?null:ePg(Pp(t,240));case 15:case 14:return null==t?null:Yk(gP(LV(t)));case 17:return eyV((eR7(),t));case 18:return eyV(t);case 21:case 20:return null==t?null:Yx(Pp(t,155).a);case 27:return L$(Pp(t,190));case 30:return emB((eR7(),Pp(t,15)));case 31:return emB(Pp(t,15));case 40:return LG((eR7(),t));case 42:return eyq((eR7(),t));case 43:return eyq(t);case 59:case 48:return Lz((eR7(),t));default:throw p7(new gL(eZ5+e.ne()+eZ6))}},eUe.Jh=function(e){var t;switch(-1==e.G&&(e.G=(t=etP(e))?ebv(t.Mh(),e):-1),e.G){case 0:return new mF;case 1:return new ul;case 2:return new mB;case 3:return new mY;default:throw p7(new gL(eZ7+e.zb+eZ6))}},eUe.Kh=function(e,t){var n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g;switch(e.yj()){case 5:case 52:case 4:return t;case 6:return epU(t);case 8:case 7:return null==t?null:ewe(t);case 9:return null==t?null:eeT(eDa((r=ePh(t,!0)).length>0&&(GV(0,r.length),43==r.charCodeAt(0))?r.substr(1):r,-128,127)<<24>>24);case 10:return null==t?null:eeT(eDa((i=ePh(t,!0)).length>0&&(GV(0,i.length),43==i.charCodeAt(0))?i.substr(1):i,-128,127)<<24>>24);case 11:return Lq(eBd(this,(eR7(),tvO),t));case 12:return Lq(eBd(this,(eR7(),tvA),t));case 13:return null==t?null:new yY(ePh(t,!0));case 15:case 14:return eOa(t);case 16:return Lq(eBd(this,(eR7(),tvL),t));case 17:return ehy((eR7(),t));case 18:return ehy(t);case 28:case 29:case 35:case 38:case 39:case 41:case 54:case 19:return ePh(t,!0);case 21:case 20:return eOv(t);case 22:return Lq(eBd(this,(eR7(),tvC),t));case 23:return Lq(eBd(this,(eR7(),tvI),t));case 24:return Lq(eBd(this,(eR7(),tvD),t));case 25:return Lq(eBd(this,(eR7(),tvN),t));case 26:return Lq(eBd(this,(eR7(),tvP),t));case 27:return epw(t);case 30:return ehw((eR7(),t));case 31:return ehw(t);case 32:return null==t?null:ell(eDa((l=ePh(t,!0)).length>0&&(GV(0,l.length),43==l.charCodeAt(0))?l.substr(1):l,eHt,eUu));case 33:return null==t?null:new TU((f=ePh(t,!0)).length>0&&(GV(0,f.length),43==f.charCodeAt(0))?f.substr(1):f);case 34:return null==t?null:ell(eDa((d=ePh(t,!0)).length>0&&(GV(0,d.length),43==d.charCodeAt(0))?d.substr(1):d,eHt,eUu));case 36:return null==t?null:ehQ(eF0((h=ePh(t,!0)).length>0&&(GV(0,h.length),43==h.charCodeAt(0))?h.substr(1):h));case 37:return null==t?null:ehQ(eF0((p=ePh(t,!0)).length>0&&(GV(0,p.length),43==p.charCodeAt(0))?p.substr(1):p));case 40:return edR((eR7(),t));case 42:return eh_((eR7(),t));case 43:return eh_(t);case 44:return null==t?null:new TU((b=ePh(t,!0)).length>0&&(GV(0,b.length),43==b.charCodeAt(0))?b.substr(1):b);case 45:return null==t?null:new TU((m=ePh(t,!0)).length>0&&(GV(0,m.length),43==m.charCodeAt(0))?m.substr(1):m);case 46:return ePh(t,!1);case 47:return Lq(eBd(this,(eR7(),tvR),t));case 59:case 48:return edP((eR7(),t));case 49:return Lq(eBd(this,(eR7(),tvF),t));case 50:return null==t?null:elf(eDa((g=ePh(t,!0)).length>0&&(GV(0,g.length),43==g.charCodeAt(0))?g.substr(1):g,eQl,32767)<<16>>16);case 51:return null==t?null:elf(eDa((a=ePh(t,!0)).length>0&&(GV(0,a.length),43==a.charCodeAt(0))?a.substr(1):a,eQl,32767)<<16>>16);case 53:return Lq(eBd(this,(eR7(),tvU),t));case 55:return null==t?null:elf(eDa((o=ePh(t,!0)).length>0&&(GV(0,o.length),43==o.charCodeAt(0))?o.substr(1):o,eQl,32767)<<16>>16);case 56:return null==t?null:elf(eDa((s=ePh(t,!0)).length>0&&(GV(0,s.length),43==s.charCodeAt(0))?s.substr(1):s,eQl,32767)<<16>>16);case 57:return null==t?null:ehQ(eF0((u=ePh(t,!0)).length>0&&(GV(0,u.length),43==u.charCodeAt(0))?u.substr(1):u));case 58:return null==t?null:ehQ(eF0((c=ePh(t,!0)).length>0&&(GV(0,c.length),43==c.charCodeAt(0))?c.substr(1):c));case 60:return null==t?null:ell(eDa((n=ePh(t,!0)).length>0&&(GV(0,n.length),43==n.charCodeAt(0))?n.substr(1):n,eHt,eUu));case 61:return null==t?null:ell(eDa(ePh(t,!0),eHt,eUu));default:throw p7(new gL(eZ5+e.ne()+eZ6))}},Y5(eQV,"XMLTypeFactoryImpl",1919),eTS(586,179,{105:1,92:1,90:1,147:1,191:1,56:1,235:1,108:1,49:1,97:1,150:1,179:1,114:1,115:1,675:1,1945:1,586:1},Ud),eUe.N=!1,eUe.O=!1;var tvi=!1;Y5(eQV,"XMLTypePackageImpl",586),eTS(1852,1,{837:1},uf),eUe._j=function(){return eD4(),eB2},Y5(eQV,"XMLTypePackageImpl/1",1852),eTS(1861,1,eQS,ud),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/10",1861),eTS(1862,1,eQS,uh),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/11",1862),eTS(1863,1,eQS,up),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/12",1863),eTS(1864,1,eQS,ub),eUe.wj=function(e){return xf(e)},eUe.xj=function(e){return Je(e13,eUP,333,e,7,1)},Y5(eQV,"XMLTypePackageImpl/13",1864),eTS(1865,1,eQS,um),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/14",1865),eTS(1866,1,eQS,ug),eUe.wj=function(e){return M4(e,15)},eUe.xj=function(e){return Je(e1H,ezZ,15,e,0,1)},Y5(eQV,"XMLTypePackageImpl/15",1866),eTS(1867,1,eQS,uv),eUe.wj=function(e){return M4(e,15)},eUe.xj=function(e){return Je(e1H,ezZ,15,e,0,1)},Y5(eQV,"XMLTypePackageImpl/16",1867),eTS(1868,1,eQS,uy),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/17",1868),eTS(1869,1,eQS,uw),eUe.wj=function(e){return M4(e,155)},eUe.xj=function(e){return Je(e14,eUP,155,e,0,1)},Y5(eQV,"XMLTypePackageImpl/18",1869),eTS(1870,1,eQS,u_),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/19",1870),eTS(1853,1,eQS,uE),eUe.wj=function(e){return M4(e,843)},eUe.xj=function(e){return Je(tve,eUp,843,e,0,1)},Y5(eQV,"XMLTypePackageImpl/2",1853),eTS(1871,1,eQS,uS),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/20",1871),eTS(1872,1,eQS,uk),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/21",1872),eTS(1873,1,eQS,ux),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/22",1873),eTS(1874,1,eQS,uT),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/23",1874),eTS(1875,1,eQS,uM),eUe.wj=function(e){return M4(e,190)},eUe.xj=function(e){return Je(tyk,eUP,190,e,0,2)},Y5(eQV,"XMLTypePackageImpl/24",1875),eTS(1876,1,eQS,uO),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/25",1876),eTS(1877,1,eQS,uA),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/26",1877),eTS(1878,1,eQS,uL),eUe.wj=function(e){return M4(e,15)},eUe.xj=function(e){return Je(e1H,ezZ,15,e,0,1)},Y5(eQV,"XMLTypePackageImpl/27",1878),eTS(1879,1,eQS,uC),eUe.wj=function(e){return M4(e,15)},eUe.xj=function(e){return Je(e1H,ezZ,15,e,0,1)},Y5(eQV,"XMLTypePackageImpl/28",1879),eTS(1880,1,eQS,uI),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/29",1880),eTS(1854,1,eQS,uD),eUe.wj=function(e){return M4(e,667)},eUe.xj=function(e){return Je(tvt,eUp,2021,e,0,1)},Y5(eQV,"XMLTypePackageImpl/3",1854),eTS(1881,1,eQS,uN),eUe.wj=function(e){return M4(e,19)},eUe.xj=function(e){return Je(e15,eUP,19,e,0,1)},Y5(eQV,"XMLTypePackageImpl/30",1881),eTS(1882,1,eQS,uP),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/31",1882),eTS(1883,1,eQS,uR),eUe.wj=function(e){return M4(e,162)},eUe.xj=function(e){return Je(e16,eUP,162,e,0,1)},Y5(eQV,"XMLTypePackageImpl/32",1883),eTS(1884,1,eQS,uj),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/33",1884),eTS(1885,1,eQS,uF),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/34",1885),eTS(1886,1,eQS,uY),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/35",1886),eTS(1887,1,eQS,uB),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/36",1887),eTS(1888,1,eQS,uU),eUe.wj=function(e){return M4(e,15)},eUe.xj=function(e){return Je(e1H,ezZ,15,e,0,1)},Y5(eQV,"XMLTypePackageImpl/37",1888),eTS(1889,1,eQS,uH),eUe.wj=function(e){return M4(e,15)},eUe.xj=function(e){return Je(e1H,ezZ,15,e,0,1)},Y5(eQV,"XMLTypePackageImpl/38",1889),eTS(1890,1,eQS,u$),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/39",1890),eTS(1855,1,eQS,uz),eUe.wj=function(e){return M4(e,668)},eUe.xj=function(e){return Je(tvn,eUp,2022,e,0,1)},Y5(eQV,"XMLTypePackageImpl/4",1855),eTS(1891,1,eQS,uG),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/40",1891),eTS(1892,1,eQS,uW),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/41",1892),eTS(1893,1,eQS,uK),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/42",1893),eTS(1894,1,eQS,uV),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/43",1894),eTS(1895,1,eQS,uq),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/44",1895),eTS(1896,1,eQS,uZ),eUe.wj=function(e){return M4(e,184)},eUe.xj=function(e){return Je(e19,eUP,184,e,0,1)},Y5(eQV,"XMLTypePackageImpl/45",1896),eTS(1897,1,eQS,uX),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/46",1897),eTS(1898,1,eQS,uJ),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/47",1898),eTS(1899,1,eQS,uQ),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/48",1899),eTS(eHx,1,eQS,u1),eUe.wj=function(e){return M4(e,184)},eUe.xj=function(e){return Je(e19,eUP,184,e,0,1)},Y5(eQV,"XMLTypePackageImpl/49",eHx),eTS(1856,1,eQS,u0),eUe.wj=function(e){return M4(e,669)},eUe.xj=function(e){return Je(tvr,eUp,2023,e,0,1)},Y5(eQV,"XMLTypePackageImpl/5",1856),eTS(1901,1,eQS,u2),eUe.wj=function(e){return M4(e,162)},eUe.xj=function(e){return Je(e16,eUP,162,e,0,1)},Y5(eQV,"XMLTypePackageImpl/50",1901),eTS(1902,1,eQS,u3),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/51",1902),eTS(1903,1,eQS,u4),eUe.wj=function(e){return M4(e,19)},eUe.xj=function(e){return Je(e15,eUP,19,e,0,1)},Y5(eQV,"XMLTypePackageImpl/52",1903),eTS(1857,1,eQS,u5),eUe.wj=function(e){return xd(e)},eUe.xj=function(e){return Je(e17,eUP,2,e,6,1)},Y5(eQV,"XMLTypePackageImpl/6",1857),eTS(1858,1,eQS,u6),eUe.wj=function(e){return M4(e,190)},eUe.xj=function(e){return Je(tyk,eUP,190,e,0,2)},Y5(eQV,"XMLTypePackageImpl/7",1858),eTS(1859,1,eQS,u9),eUe.wj=function(e){return xl(e)},eUe.xj=function(e){return Je(e11,eUP,476,e,8,1)},Y5(eQV,"XMLTypePackageImpl/8",1859),eTS(1860,1,eQS,u8),eUe.wj=function(e){return M4(e,217)},eUe.xj=function(e){return Je(e10,eUP,217,e,0,1)},Y5(eQV,"XMLTypePackageImpl/9",1860),eTS(50,60,eHr,gX),Y5(e1l,"RegEx/ParseException",50),eTS(820,1,{},u7),eUe.sl=function(e){return e16*n)throw p7(new gX(eBJ((Mo(),eJd))));n=16*n+i}if(125!=this.a)throw p7(new gX(eBJ((Mo(),eJh))));if(n>e1f)throw p7(new gX(eBJ((Mo(),eJp))));e=n}else{if(i=0,0!=this.c||(i=eb0(this.a))<0||(n=i,eBM(this),0!=this.c||(i=eb0(this.a))<0))throw p7(new gX(eBJ((Mo(),eJf))));e=n=16*n+i}break;case 117:if(r=0,eBM(this),0!=this.c||(r=eb0(this.a))<0||(t=r,eBM(this),0!=this.c||(r=eb0(this.a))<0)||(t=16*t+r,eBM(this),0!=this.c||(r=eb0(this.a))<0)||(t=16*t+r,eBM(this),0!=this.c||(r=eb0(this.a))<0))throw p7(new gX(eBJ((Mo(),eJf))));e=t=16*t+r;break;case 118:if(eBM(this),0!=this.c||(r=eb0(this.a))<0||(t=r,eBM(this),0!=this.c||(r=eb0(this.a))<0)||(t=16*t+r,eBM(this),0!=this.c||(r=eb0(this.a))<0)||(t=16*t+r,eBM(this),0!=this.c||(r=eb0(this.a))<0)||(t=16*t+r,eBM(this),0!=this.c||(r=eb0(this.a))<0)||(t=16*t+r,eBM(this),0!=this.c||(r=eb0(this.a))<0))throw p7(new gX(eBJ((Mo(),eJf))));if((t=16*t+r)>e1f)throw p7(new gX(eBJ((Mo(),"parser.descappe.4"))));e=t;break;case 65:case 90:case 122:throw p7(new gX(eBJ((Mo(),eJb))))}return e},eUe.ul=function(e){var t,n;switch(e){case 100:n=(32&this.e)==32?eYB("Nd",!0):(eBG(),tv8);break;case 68:n=(32&this.e)==32?eYB("Nd",!1):(eBG(),tyr);break;case 119:n=(32&this.e)==32?eYB("IsWord",!0):(eBG(),tyd);break;case 87:n=(32&this.e)==32?eYB("IsWord",!1):(eBG(),tya);break;case 115:n=(32&this.e)==32?eYB("IsSpace",!0):(eBG(),tys);break;case 83:n=(32&this.e)==32?eYB("IsSpace",!1):(eBG(),tyi);break;default:throw p7(new go(e1d+(t=e).toString(16)))}return n},eUe.vl=function(e){var t,n,r,i,a,o,s,u,c,l,f,d;for(this.b=1,eBM(this),t=null,0==this.c&&94==this.a?(eBM(this),e?l=(eBG(),eBG(),++tyv,new WZ(5)):(t=(eBG(),eBG(),++tyv,new WZ(4)),eLw(t,0,e1f),l=(++tyv,new WZ(4)))):l=(eBG(),eBG(),++tyv,new WZ(4)),i=!0;1!=(d=this.c)&&(0!=d||93!=this.a||i);){if(i=!1,n=this.a,r=!1,10==d)switch(n){case 100:case 68:case 119:case 87:case 115:case 83:ePR(l,this.ul(n)),r=!0;break;case 105:case 73:case 99:case 67:(n=this.Ll(l,n))<0&&(r=!0);break;case 112:case 80:if(!(f=ext(this,n)))throw p7(new gX(eBJ((Mo(),eJe))));ePR(l,f),r=!0;break;default:n=this.tl()}else if(20==d){if((o=AG(this.i,58,this.d))<0)throw p7(new gX(eBJ((Mo(),eJt))));if(s=!0,94==UI(this.i,this.d)&&(++this.d,s=!1),!(u=JI(a=Az(this.i,this.d,o),s,(512&this.e)==512)))throw p7(new gX(eBJ((Mo(),eJr))));if(ePR(l,u),r=!0,o+1>=this.j||93!=UI(this.i,o+1))throw p7(new gX(eBJ((Mo(),eJt))));this.d=o+2}if(eBM(this),!r){if(0!=this.c||45!=this.a)eLw(l,n,n);else{if(eBM(this),1==(d=this.c))throw p7(new gX(eBJ((Mo(),eJn))));0==d&&93==this.a?(eLw(l,n,n),eLw(l,45,45)):(c=this.a,10==d&&(c=this.tl()),eBM(this),eLw(l,n,c))}}(this.e&eXt)==eXt&&0==this.c&&44==this.a&&eBM(this)}if(1==this.c)throw p7(new gX(eBJ((Mo(),eJn))));return t&&(ej0(t,l),l=t),eMS(l),eRo(l),this.b=0,eBM(this),l},eUe.wl=function(){var e,t,n,r;for(n=this.vl(!1);7!=(r=this.c);)if(e=this.a,0==r&&(45==e||38==e)||4==r){if(eBM(this),9!=this.c)throw p7(new gX(eBJ((Mo(),eJu))));if(t=this.vl(!1),4==r)ePR(n,t);else if(45==e)ej0(n,t);else if(38==e)ejO(n,t);else throw p7(new go("ASSERT"))}else throw p7(new gX(eBJ((Mo(),eJc))));return eBM(this),n},eUe.xl=function(){var e,t;return e=this.a-48,t=(eBG(),eBG(),++tyv,new zc(12,null,e)),this.g||(this.g=new bZ),bY(this.g,new pX(e)),eBM(this),t},eUe.yl=function(){return eBM(this),eBG(),tyu},eUe.zl=function(){return eBM(this),eBG(),tyo},eUe.Al=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Bl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Cl=function(){return eBM(this),esV()},eUe.Dl=function(){return eBM(this),eBG(),tyl},eUe.El=function(){return eBM(this),eBG(),tyh},eUe.Fl=function(){var e;if(this.d>=this.j||(65504&(e=UI(this.i,this.d++)))!=64)throw p7(new gX(eBJ((Mo(),eX6))));return eBM(this),eBG(),eBG(),++tyv,new jb(0,e-64)},eUe.Gl=function(){return eBM(this),eNw()},eUe.Hl=function(){return eBM(this),eBG(),typ},eUe.Il=function(){var e;return e=(eBG(),eBG(),++tyv,new jb(0,105)),eBM(this),e},eUe.Jl=function(){return eBM(this),eBG(),tyf},eUe.Kl=function(){return eBM(this),eBG(),tyc},eUe.Ll=function(e,t){return this.tl()},eUe.Ml=function(){return eBM(this),eBG(),tyt},eUe.Nl=function(){var e,t,n,r,i;if(this.d+1>=this.j)throw p7(new gX(eBJ((Mo(),eX3))));if(r=-1,t=null,49<=(e=UI(this.i,this.d))&&e<=57){if(r=e-48,this.g||(this.g=new bZ),bY(this.g,new pX(r)),++this.d,41!=UI(this.i,this.d))throw p7(new gX(eBJ((Mo(),eX1))));++this.d}else switch(63==e&&--this.d,eBM(this),(t=eBs(this)).e){case 20:case 21:case 22:case 23:break;case 8:if(7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));break;default:throw p7(new gX(eBJ((Mo(),eX4))))}if(eBM(this),i=ehT(this),n=null,2==i.e){if(2!=i.em())throw p7(new gX(eBJ((Mo(),eX5))));n=i.am(1),i=i.am(0)}if(7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),eBG(),eBG(),++tyv,new ee_(r,t,i,n)},eUe.Ol=function(){return eBM(this),eBG(),tyn},eUe.Pl=function(){var e;if(eBM(this),e=F4(24,ehT(this)),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Ql=function(){var e;if(eBM(this),e=F4(20,ehT(this)),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Rl=function(){var e;if(eBM(this),e=F4(22,ehT(this)),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Sl=function(){var e,t,n,r,i;for(e=0,n=0,t=-1;this.d=this.j)throw p7(new gX(eBJ((Mo(),eX0))));if(45==t){for(++this.d;this.d=this.j)throw p7(new gX(eBJ((Mo(),eX0))))}if(58==t){if(++this.d,eBM(this),r=Bu(ehT(this),e,n),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));eBM(this)}else if(41==t)++this.d,eBM(this),r=Bu(ehT(this),e,n);else throw p7(new gX(eBJ((Mo(),eX2))));return r},eUe.Tl=function(){var e;if(eBM(this),e=F4(21,ehT(this)),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Ul=function(){var e;if(eBM(this),e=F4(23,ehT(this)),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Vl=function(){var e,t;if(eBM(this),e=this.f++,t=F5(ehT(this),e),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),t},eUe.Wl=function(){var e;if(eBM(this),e=F5(ehT(this),0),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Xl=function(e){return(eBM(this),5==this.c)?(eBM(this),jS(e,(eBG(),eBG(),++tyv,new qa(9,e)))):jS(e,(eBG(),eBG(),++tyv,new qa(3,e)))},eUe.Yl=function(e){var t;return eBM(this),t=(eBG(),eBG(),++tyv,new Mr(2)),5==this.c?(eBM(this),eRv(t,tye),eRv(t,e)):(eRv(t,e),eRv(t,tye)),t},eUe.Zl=function(e){return(eBM(this),5==this.c)?(eBM(this),eBG(),eBG(),++tyv,new qa(9,e)):(eBG(),eBG(),++tyv,new qa(3,e))},eUe.a=0,eUe.b=0,eUe.c=0,eUe.d=0,eUe.e=0,eUe.f=1,eUe.g=null,eUe.j=0,Y5(e1l,"RegEx/RegexParser",820),eTS(1824,820,{},mU),eUe.sl=function(e){return!1},eUe.tl=function(){return eCn(this)},eUe.ul=function(e){return eDu(e)},eUe.vl=function(e){return eBL(this)},eUe.wl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.xl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.yl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.zl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Al=function(){return eBM(this),eDu(67)},eUe.Bl=function(){return eBM(this),eDu(73)},eUe.Cl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Dl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.El=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Fl=function(){return eBM(this),eDu(99)},eUe.Gl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Hl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Il=function(){return eBM(this),eDu(105)},eUe.Jl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Kl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Ll=function(e,t){return ePR(e,eDu(t)),-1},eUe.Ml=function(){return eBM(this),eBG(),eBG(),++tyv,new jb(0,94)},eUe.Nl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Ol=function(){return eBM(this),eBG(),eBG(),++tyv,new jb(0,36)},eUe.Pl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Ql=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Rl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Sl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Tl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Ul=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Vl=function(){var e;if(eBM(this),e=F5(ehT(this),0),7!=this.c)throw p7(new gX(eBJ((Mo(),eX1))));return eBM(this),e},eUe.Wl=function(){throw p7(new gX(eBJ((Mo(),eJm))))},eUe.Xl=function(e){return eBM(this),jS(e,(eBG(),eBG(),++tyv,new qa(3,e)))},eUe.Yl=function(e){var t;return eBM(this),t=(eBG(),eBG(),++tyv,new Mr(2)),eRv(t,e),eRv(t,tye),t},eUe.Zl=function(e){return eBM(this),eBG(),eBG(),++tyv,new qa(3,e)};var tva=null,tvo=null;Y5(e1l,"RegEx/ParserForXMLSchema",1824),eTS(117,1,e1k,pJ),eUe.$l=function(e){throw p7(new go("Not supported."))},eUe._l=function(){return -1},eUe.am=function(e){return null},eUe.bm=function(){return null},eUe.cm=function(e){},eUe.dm=function(e){},eUe.em=function(){return 0},eUe.Ib=function(){return this.fm(0)},eUe.fm=function(e){return 11==this.e?".":""},eUe.e=0;var tvs,tvu,tvc,tvl,tvf,tvd,tvh,tvp,tvb,tvm,tvg,tvv,tvy,tvw,tv_,tvE,tvS,tvk,tvx,tvT,tvM,tvO,tvA,tvL,tvC,tvI,tvD,tvN,tvP,tvR,tvj,tvF,tvY,tvB,tvU,tvH,tv$,tvz,tvG,tvW,tvK,tvV,tvq,tvZ,tvX,tvJ,tvQ,tv1,tv0,tv2,tv3,tv4,tv5,tv6,tv9,tv8,tv7,tye,tyt,tyn,tyr,tyi,tya,tyo,tys,tyu,tyc,tyl,tyf,tyd,tyh,typ,tyb=null,tym=null,tyg=null,tyv=0,tyy=Y5(e1l,"RegEx/Token",117);eTS(136,117,{3:1,136:1,117:1},WZ),eUe.fm=function(e){var t,n,r;if(4==this.e){if(this==tv7)n=".";else if(this==tv8)n="\\d";else if(this==tyd)n="\\w";else if(this==tys)n="\\s";else{for(r=new vs,r.a+="[",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?xk(r,eN$(this.b[t])):(xk(r,eN$(this.b[t])),r.a+="-",xk(r,eN$(this.b[t+1])));r.a+="]",n=r.a}}else if(this==tyr)n="\\D";else if(this==tya)n="\\W";else if(this==tyi)n="\\S";else{for(r=new vs,r.a+="[^",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?xk(r,eN$(this.b[t])):(xk(r,eN$(this.b[t])),r.a+="-",xk(r,eN$(this.b[t+1])));r.a+="]",n=r.a}return n},eUe.a=!1,eUe.c=!1,Y5(e1l,"RegEx/RangeToken",136),eTS(584,1,{584:1},pX),eUe.a=0,Y5(e1l,"RegEx/RegexParser/ReferencePosition",584),eTS(583,1,{3:1,583:1},wu),eUe.Fb=function(e){var t;return!!(null!=e&&M4(e,583))&&(t=Pp(e,583),IE(this.b,t.b)&&this.a==t.a)},eUe.Hb=function(){return ebA(this.b+"/"+eAN(this.a))},eUe.Ib=function(){return this.c.fm(this.a)},eUe.a=0,Y5(e1l,"RegEx/RegularExpression",583),eTS(223,117,e1k,jb),eUe._l=function(){return this.a},eUe.fm=function(e){var t,n,r;switch(this.e){case 0:switch(this.a){case 124:case 42:case 43:case 63:case 40:case 41:case 46:case 91:case 123:case 92:r="\\"+CB(this.a&eHd);break;case 12:r="\\f";break;case 10:r="\\n";break;case 13:r="\\r";break;case 9:r="\\t";break;case 27:r="\\e";break;default:r=this.a>=eH3?"\\v"+Az(n="0"+(t=this.a>>>0).toString(16),n.length-6,n.length):""+CB(this.a&eHd)}break;case 8:r=this==tyt||this==tyn?""+CB(this.a&eHd):"\\"+CB(this.a&eHd);break;default:r=null}return r},eUe.a=0,Y5(e1l,"RegEx/Token/CharToken",223),eTS(309,117,e1k,qa),eUe.am=function(e){return this.a},eUe.cm=function(e){this.b=e},eUe.dm=function(e){this.c=e},eUe.em=function(){return 1},eUe.fm=function(e){var t;if(3==this.e){if(this.c<0&&this.b<0)t=this.a.fm(e)+"*";else if(this.c==this.b)t=this.a.fm(e)+"{"+this.c+"}";else if(this.c>=0&&this.b>=0)t=this.a.fm(e)+"{"+this.c+","+this.b+"}";else if(this.c>=0&&this.b<0)t=this.a.fm(e)+"{"+this.c+",}";else throw p7(new go("Token#toString(): CLOSURE "+this.c+eUd+this.b))}else if(this.c<0&&this.b<0)t=this.a.fm(e)+"*?";else if(this.c==this.b)t=this.a.fm(e)+"{"+this.c+"}?";else if(this.c>=0&&this.b>=0)t=this.a.fm(e)+"{"+this.c+","+this.b+"}?";else if(this.c>=0&&this.b<0)t=this.a.fm(e)+"{"+this.c+",}?";else throw p7(new go("Token#toString(): NONGREEDYCLOSURE "+this.c+eUd+this.b));return t},eUe.b=0,eUe.c=0,Y5(e1l,"RegEx/Token/ClosureToken",309),eTS(821,117,e1k,YD),eUe.am=function(e){return 0==e?this.a:this.b},eUe.em=function(){return 2},eUe.fm=function(e){var t;return 3==this.b.e&&this.b.am(0)==this.a?this.a.fm(e)+"+":9==this.b.e&&this.b.am(0)==this.a?this.a.fm(e)+"+?":this.a.fm(e)+""+this.b.fm(e)},Y5(e1l,"RegEx/Token/ConcatToken",821),eTS(1822,117,e1k,ee_),eUe.am=function(e){if(0==e)return this.d;if(1==e)return this.b;throw p7(new go("Internal Error: "+e))},eUe.em=function(){return this.b?2:1},eUe.fm=function(e){var t;return t=this.c>0?"(?("+this.c+")":8==this.a.e?"(?("+this.a+")":"(?"+this.a,this.b?t+=this.d+"|"+this.b+")":t+=this.d+")",t},eUe.c=0,Y5(e1l,"RegEx/Token/ConditionToken",1822),eTS(1823,117,e1k,Wq),eUe.am=function(e){return this.b},eUe.em=function(){return 1},eUe.fm=function(e){return"(?"+(0==this.a?"":eAN(this.a))+(0==this.c?"":eAN(this.c))+":"+this.b.fm(e)+")"},eUe.a=0,eUe.c=0,Y5(e1l,"RegEx/Token/ModifierToken",1823),eTS(822,117,e1k,BR),eUe.am=function(e){return this.a},eUe.em=function(){return 1},eUe.fm=function(e){var t;switch(t=null,this.e){case 6:t=0==this.b?"(?:"+this.a.fm(e)+")":"("+this.a.fm(e)+")";break;case 20:t="(?="+this.a.fm(e)+")";break;case 21:t="(?!"+this.a.fm(e)+")";break;case 22:t="(?<="+this.a.fm(e)+")";break;case 23:t="(?"+this.a.fm(e)+")"}return t},eUe.b=0,Y5(e1l,"RegEx/Token/ParenToken",822),eTS(521,117,{3:1,117:1,521:1},zc),eUe.bm=function(){return this.b},eUe.fm=function(e){return 12==this.e?"\\"+this.a:eTd(this.b)},eUe.a=0,Y5(e1l,"RegEx/Token/StringToken",521),eTS(465,117,e1k,Mr),eUe.$l=function(e){eRv(this,e)},eUe.am=function(e){return Pp(Bz(this.a,e),117)},eUe.em=function(){return this.a?this.a.a.c.length:0},eUe.fm=function(e){var t,n,r,i,a;if(1==this.e){if(2==this.a.a.c.length)t=Pp(Bz(this.a,0),117),i=3==(n=Pp(Bz(this.a,1),117)).e&&n.am(0)==t?t.fm(e)+"+":9==n.e&&n.am(0)==t?t.fm(e)+"+?":t.fm(e)+""+n.fm(e);else{for(r=0,a=new vs;r=this.c.b:this.a<=this.c.b},eUe.Sb=function(){return this.b>0},eUe.Tb=function(){return this.b},eUe.Vb=function(){return this.b-1},eUe.Qb=function(){throw p7(new gW(e1L))},eUe.a=0,eUe.b=0,Y5(e1M,"ExclusiveRange/RangeIterator",254);var tyw=Ui(eJX,"C"),ty_=Ui(eJ1,"I"),tyE=Ui(eUi,"Z"),tyS=Ui(eJ0,"J"),tyk=Ui(eJZ,"B"),tyx=Ui(eJJ,"D"),tyT=Ui(eJQ,"F"),tyM=Ui(eJ2,"S"),tyO=RL("org.eclipse.elk.core.labels","ILabelManager"),tyA=RL(eX_,"DiagnosticChain"),tyL=RL(eQx,"ResourceSet"),tyC=Y5(eX_,"InvocationTargetException",null),tyI=(vg(),q6),tyD=tyD=eyP;enI(bs),eiE("permProps",[[[e1C,e1I],[e1D,"gecko1_8"]],[[e1C,e1I],[e1D,"ie10"]],[[e1C,e1I],[e1D,"ie8"]],[[e1C,e1I],[e1D,"ie9"]],[[e1C,e1I],[e1D,"safari"]]]),tyD(null,"elk",null)},3379(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return t&&("object"==typeof t||"function"==typeof t)?t:e}function a(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}var o=function(e){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r(this,t);var a=Object.assign({},e),o=!1;try{o=!0}catch(s){}if(e.workerUrl){if(o){var u=n(84763);a.workerFactory=function(e){return new u(e)}}else console.warn("Web worker requested but 'web-worker' package not installed. \nConsider installing the package or pass your own 'workerFactory' to ELK's constructor.\n... Falling back to non-web worker version.")}if(!a.workerFactory){var c=n(55273).Worker;a.workerFactory=function(e){return new c(e)}}return i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,a))}return a(t,e),t}(n(4005).default);Object.defineProperty(e.exports,"__esModule",{value:!0}),e.exports=o,o.default=o},17187(e){"use strict";var t,n="object"==typeof Reflect?Reflect:null,r=n&&"function"==typeof n.apply?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};function i(e){console&&console.warn&&console.warn(e)}t=n&&"function"==typeof n.ownKeys?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var a=Number.isNaN||function(e){return e!=e};function o(){o.init.call(this)}e.exports=o,e.exports.once=v,o.EventEmitter=o,o.prototype._events=void 0,o.prototype._eventsCount=0,o.prototype._maxListeners=void 0;var s=10;function u(e){if("function"!=typeof e)throw TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function c(e){return void 0===e._maxListeners?o.defaultMaxListeners:e._maxListeners}function l(e,t,n,r){if(u(n),void 0===(o=e._events)?(o=e._events=Object.create(null),e._eventsCount=0):(void 0!==o.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),o=e._events),s=o[t]),void 0===s)s=o[t]=n,++e._eventsCount;else if("function"==typeof s?s=o[t]=r?[n,s]:[s,n]:r?s.unshift(n):s.push(n),(a=c(e))>0&&s.length>a&&!s.warned){s.warned=!0;var a,o,s,l=Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");l.name="MaxListenersExceededWarning",l.emitter=e,l.type=t,l.count=s.length,i(l)}return e}function f(){if(!this.fired)return(this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length)?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function d(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},i=f.bind(r);return i.listener=n,r.wrapFn=i,i}function h(e,t,n){var r=e._events;if(void 0===r)return[];var i=r[t];return void 0===i?[]:"function"==typeof i?n?[i.listener||i]:[i]:n?g(i):b(i,i.length)}function p(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function b(e,t){for(var n=Array(t),r=0;r0&&(o=t[0]),o instanceof Error)throw o;var o,s=Error("Unhandled error."+(o?" ("+o.message+")":""));throw s.context=o,s}var u=a[e];if(void 0===u)return!1;if("function"==typeof u)r(u,this,t);else for(var c=u.length,l=b(u,c),n=0;n=0;a--)if(n[a]===t||n[a].listener===t){o=n[a].listener,i=a;break}if(i<0)return this;0===i?n.shift():m(n,i),1===n.length&&(r[e]=n[0]),void 0!==r.removeListener&&this.emit("removeListener",e,o||t)}return this},o.prototype.off=o.prototype.removeListener,o.prototype.removeAllListeners=function(e){var t,n,r;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var i,a=Object.keys(n);for(r=0;r=0;r--)this.removeListener(e,t[r]);return this},o.prototype.listeners=function(e){return h(this,e,!0)},o.prototype.rawListeners=function(e){return h(this,e,!1)},o.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):p.call(e,t)},o.prototype.listenerCount=p,o.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}},16839(e,t,n){var r=n(25323),i=n(31744),a=n(98361),o=n(4514);e.exports={graphlib:n(32478),read:r,readMany:i,write:a,version:o,type:"dot",buffer:!1}},11100(e,t,n){"use strict";var r=n(47755),i=n(32478).Graph;function a(e){var t="graph"!==e.type,n=!e.strict,a=[{node:{},edge:{}}],s=e.id,u=new i({directed:t,multigraph:n,compound:!0});return u.setGraph(null===s?{}:{id:s}),r.each(e.stmts,function(e){o(u,e,a)}),u}function o(e,t,n,r){switch(t.type){case"node":s(e,t,n,r);break;case"edge":u(e,t,n,r);break;case"subgraph":c(e,t,n,r);break;case"attr":l(e,t,n);break;case"inlineAttr":f(e,t,n,r)}}function s(e,t,n,i){var a=t.id,o=t.attrs;h(e,a,n,i),r.merge(e.node(a),o)}function u(e,t,n,i){var a,s,u=t.attrs;r.each(t.elems,function(t){switch(o(e,t,n,i),t.type){case"node":s=[t.id];break;case"subgraph":s=p(t)}r.each(a,function(t){r.each(s,function(i){var a;e.hasEdge(t,i)&&e.isMultigraph()&&(a=r.uniqueId("edge")),e.hasEdge(t,i,a)||e.setEdge(t,i,r.clone(r.last(n).edge),a),r.merge(e.edge(t,i,a),u)})}),a=s})}function c(e,t,n,i){var a=t.id;void 0===a&&(a=d(e)),n.push(r.clone(r.last(n))),h(e,a,n,i),r.each(t.stmts,function(t){o(e,t,n,a)}),e.children(a).length||e.removeNode(a),n.pop()}function l(e,t,n){r.merge(r.last(n)[t.attrType],t.attrs)}function f(e,t,n,i){r.merge(i?e.node(i):e.graph(),t.attrs)}function d(e){var t;do t=r.uniqueId("sg");while(e.hasNode(t))return t}function h(e,t,n,i){e.hasNode(t)||(e.setNode(t,r.clone(r.last(n).node)),e.setParent(t,i))}function p(e){var t,n={},i=[],a=i.push.bind(i);for(a(e);i.length;)switch((t=i.pop()).type){case"node":n[t.id]=!0;break;case"edge":r.each(t.elems,a);break;case"subgraph":r.each(t.stmts,a)}return r.keys(n)}e.exports=a},4644(e,t,n){e.exports=function(){function e(e,t){function n(){this.constructor=e}n.prototype=t.prototype,e.prototype=new n}function t(e,t,n,r,i,a){this.message=e,this.expected=t,this.found=n,this.offset=r,this.line=i,this.column=a,this.name="SyntaxError"}function r(e){var r,i,a=arguments.length>1?arguments[1]:{},o={},s={start:tf,graphStmt:td},u=tf,c=o,l=null,f="{",d={type:"literal",value:"{",description:'"{"'},h="}",p={type:"literal",value:"}",description:'"}"'},b=function(e,t,n,r){return{type:t,id:n,strict:null!==e,stmts:r}},m=";",g={type:"literal",value:";",description:'";"'},v=function(e,t){for(var n=[e],r=0;r",description:'"->"'},U=function(e,t){var n=[e];if(t)for(var r=0;rt&&(tr=0,ti={line:1,column:1,seenCR:!1}),n(ti,tr,t),tr=t),ti}function tc(e){!(ttta&&(ta=tt,to=[]),to.push(e))}function tl(n,r,i){function a(e){var t=1;for(e.sort(function(e,t){return e.descriptiont.description?1:0});t1?o.slice(0,-1).join(", ")+" or "+o[e.length-1]:o[0])+" but "+(i=t?'"'+n(t)+'"':"end of input")+" found."}var s=tu(i),u=itt?(s=e.charAt(tt),tt++):(s=o,0===ts&&tc(te)),s!==o?i=a=[a,s]:(tt=i,i=c)):(tt=i,i=c);i!==o;)r.push(i),i=tt,a=tt,ts++,e.substr(tt,2)===e8?(s=e8,tt+=2):(s=o,0===ts&&tc(e7)),ts--,s===o?a=F:(tt=a,a=c),a!==o?(e.length>tt?(s=e.charAt(tt),tt++):(s=o,0===ts&&tc(te)),s!==o?i=a=[a,s]:(tt=i,i=c)):(tt=i,i=c);r!==o?(e.substr(tt,2)===e8?(i=e8,tt+=2):(i=o,0===ts&&tc(e7)),i!==o?t=n=[n,r,i]:(tt=t,t=c)):(tt=t,t=c)}else tt=t,t=c}return ts--,t===o&&(n=o,0===ts&&tc(e0)),t}function tY(){var e;return(e=tj())===o&&(e=tF()),e}var tB=n(47755);if((i=u())!==o&&tt===e.length)return i;throw i!==o&&tt":"--",n=new f;e.isMultigraph()||n.write("strict "),n.writeLine((e.isDirected()?"digraph":"graph")+" {"),n.indent();var i=e.graph();return r.isObject(i)&&r.each(i,function(e,t){n.writeLine(l(t)+"="+l(e)+";")}),o(e,void 0,n),e.edges().forEach(function(r){u(e,r,t,n)}),n.unindent(),n.writeLine("}"),n.toString()}function o(e,t,n){var i=e.isCompound()?e.children(t):e.nodes();r.each(i,function(t){e.isCompound()&&e.children(t).length?(n.writeLine("subgraph "+l(t)+" {"),n.indent(),r.isObject(e.node(t))&&r.map(e.node(t),function(e,t){n.writeLine(l(t)+"="+l(e)+";")}),o(e,t,n),n.unindent(),n.writeLine("}")):s(e,t,n)})}function s(e,t,n){n.write(l(t)),c(e.node(t),n),n.writeLine()}function u(e,t,n,r){var i=t.v,a=t.w,o=e.edge(t);r.write(l(i)+" "+n+" "+l(a)),c(o,r),r.writeLine()}function c(e,t){if(r.isObject(e)){var n=r.map(e,function(e,t){return l(t)+"="+l(e)});n.length&&t.write(" ["+n.join(",")+"]")}}function l(e){return"number"==typeof e||e.toString().match(i)?e:'"'+e.toString().replace(/"/g,'\\"')+'"'}function f(){this._indent="",this._content="",this._shouldIndent=!0}f.prototype.INDENT=" ",f.prototype.indent=function(){this._indent+=this.INDENT},f.prototype.unindent=function(){this._indent=this._indent.slice(this.INDENT.length)},f.prototype.writeLine=function(e){this.write((e||"")+"\n"),this._shouldIndent=!0},f.prototype.write=function(e){this._shouldIndent&&(this._shouldIndent=!1,this._content+=this._indent),this._content+=e},f.prototype.toString=function(){return this._content}},28282(e,t,n){var r=n(82354);e.exports={Graph:r.Graph,json:n(28974),alg:n(12440),version:r.version}},2842(e,t,n){var r=n(89126);function i(e){var t,n={},i=[];function a(i){r.has(n,i)||(n[i]=!0,t.push(i),r.each(e.successors(i),a),r.each(e.predecessors(i),a))}return r.each(e.nodes(),function(e){t=[],a(e),t.length&&i.push(t)}),i}e.exports=i},53984(e,t,n){var r=n(89126);function i(e,t,n){r.isArray(t)||(t=[t]);var i=(e.isDirected()?e.successors:e.neighbors).bind(e),o=[],s={};return r.each(t,function(t){if(!e.hasNode(t))throw Error("Graph does not have node: "+t);a(e,t,"post"===n,s,i,o)}),o}function a(e,t,n,i,o,s){!r.has(i,t)&&(i[t]=!0,n||s.push(t),r.each(o(t),function(t){a(e,t,n,i,o,s)}),n&&s.push(t))}e.exports=i},84847(e,t,n){var r=n(63763),i=n(89126);function a(e,t,n){return i.transform(e.nodes(),function(i,a){i[a]=r(e,a,t,n)},{})}e.exports=a},63763(e,t,n){var r=n(89126),i=n(75639);e.exports=o;var a=r.constant(1);function o(e,t,n,r){return s(e,String(t),n||a,r||function(t){return e.outEdges(t)})}function s(e,t,n,r){var a,o,s={},u=new i,c=function(e){var t=e.v!==a?e.v:e.w,r=s[t],i=n(e),c=o.distance+i;if(i<0)throw Error("dijkstra does not allow negative edge weights. Bad edge: "+e+" Weight: "+i);c0&&(o=s[a=u.removeMin()]).distance!==Number.POSITIVE_INFINITY;)r(a).forEach(c);return s}},9096(e,t,n){var r=n(89126),i=n(5023);function a(e){return r.filter(i(e),function(t){return t.length>1||1===t.length&&e.hasEdge(t[0],t[0])})}e.exports=a},38924(e,t,n){var r=n(89126);e.exports=a;var i=r.constant(1);function a(e,t,n){return o(e,t||i,n||function(t){return e.outEdges(t)})}function o(e,t,n){var r={},i=e.nodes();return i.forEach(function(e){r[e]={},r[e][e]={distance:0},i.forEach(function(t){e!==t&&(r[e][t]={distance:Number.POSITIVE_INFINITY})}),n(e).forEach(function(n){var i=n.v===e?n.w:n.v,a=t(n);r[e][i]={distance:a,predecessor:e}})}),i.forEach(function(e){var t=r[e];i.forEach(function(n){var a=r[n];i.forEach(function(n){var r=a[e],i=t[n],o=a[n],s=r.distance+i.distance;s0;){if(n=u.removeMin(),r.has(s,n))o.setEdge(n,s[n]);else if(l)throw Error("Input graph is not connected: "+e);else l=!0;e.nodeEdges(n).forEach(c)}return o}e.exports=o},5023(e,t,n){var r=n(89126);function i(e){var t=0,n=[],i={},a=[];function o(s){var u=i[s]={onStack:!0,lowlink:t,index:t++};if(n.push(s),e.successors(s).forEach(function(e){r.has(i,e)?i[e].onStack&&(u.lowlink=Math.min(u.lowlink,i[e].index)):(o(e),u.lowlink=Math.min(u.lowlink,i[e].lowlink))}),u.lowlink===u.index){var c,l=[];do i[c=n.pop()].onStack=!1,l.push(c);while(s!==c)a.push(l)}}return e.nodes().forEach(function(e){r.has(i,e)||o(e)}),a}e.exports=i},2166(e,t,n){var r=n(89126);function i(e){var t={},n={},i=[];function o(s){if(r.has(n,s))throw new a;r.has(t,s)||(n[s]=!0,t[s]=!0,r.each(e.predecessors(s),o),delete n[s],i.push(s))}if(r.each(e.sinks(),o),r.size(t)!==e.nodeCount())throw new a;return i}function a(){}e.exports=i,i.CycleException=a,a.prototype=Error()},75639(e,t,n){var r=n(89126);function i(){this._arr=[],this._keyIndices={}}e.exports=i,i.prototype.size=function(){return this._arr.length},i.prototype.keys=function(){return this._arr.map(function(e){return e.key})},i.prototype.has=function(e){return r.has(this._keyIndices,e)},i.prototype.priority=function(e){var t=this._keyIndices[e];if(void 0!==t)return this._arr[t].priority},i.prototype.min=function(){if(0===this.size())throw Error("Queue underflow");return this._arr[0].key},i.prototype.add=function(e,t){var n=this._keyIndices;if(e=String(e),!r.has(n,e)){var i=this._arr,a=i.length;return n[e]=a,i.push({key:e,priority:t}),this._decrease(a),!0}return!1},i.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var e=this._arr.pop();return delete this._keyIndices[e.key],this._heapify(0),e.key},i.prototype.decrease=function(e,t){var n=this._keyIndices[e];if(t>this._arr[n].priority)throw Error("New priority is greater than current priority. Key: "+e+" Old: "+this._arr[n].priority+" New: "+t);this._arr[n].priority=t,this._decrease(n)},i.prototype._heapify=function(e){var t=this._arr,n=2*e,r=n+1,i=e;n>1].priorityu){var c=s;s=u,u=c}return s+o+u+o+(r.isUndefined(a)?i:a)}function f(e,t,n,r){var i=""+t,a=""+n;if(!e&&i>a){var o=i;i=a,a=o}var s={v:i,w:a};return r&&(s.name=r),s}function d(e,t){return l(e,t.v,t.w,t.name)}s.prototype._nodeCount=0,s.prototype._edgeCount=0,s.prototype.isDirected=function(){return this._isDirected},s.prototype.isMultigraph=function(){return this._isMultigraph},s.prototype.isCompound=function(){return this._isCompound},s.prototype.setGraph=function(e){return this._label=e,this},s.prototype.graph=function(){return this._label},s.prototype.setDefaultNodeLabel=function(e){return r.isFunction(e)||(e=r.constant(e)),this._defaultNodeLabelFn=e,this},s.prototype.nodeCount=function(){return this._nodeCount},s.prototype.nodes=function(){return r.keys(this._nodes)},s.prototype.sources=function(){var e=this;return r.filter(this.nodes(),function(t){return r.isEmpty(e._in[t])})},s.prototype.sinks=function(){var e=this;return r.filter(this.nodes(),function(t){return r.isEmpty(e._out[t])})},s.prototype.setNodes=function(e,t){var n=arguments,i=this;return r.each(e,function(e){n.length>1?i.setNode(e,t):i.setNode(e)}),this},s.prototype.setNode=function(e,t){return r.has(this._nodes,e)?(arguments.length>1&&(this._nodes[e]=t),this):(this._nodes[e]=arguments.length>1?t:this._defaultNodeLabelFn(e),this._isCompound&&(this._parent[e]=a,this._children[e]={},this._children[a][e]=!0),this._in[e]={},this._preds[e]={},this._out[e]={},this._sucs[e]={},++this._nodeCount,this)},s.prototype.node=function(e){return this._nodes[e]},s.prototype.hasNode=function(e){return r.has(this._nodes,e)},s.prototype.removeNode=function(e){var t=this;if(r.has(this._nodes,e)){var n=function(e){t.removeEdge(t._edgeObjs[e])};delete this._nodes[e],this._isCompound&&(this._removeFromParentsChildList(e),delete this._parent[e],r.each(this.children(e),function(e){t.setParent(e)}),delete this._children[e]),r.each(r.keys(this._in[e]),n),delete this._in[e],delete this._preds[e],r.each(r.keys(this._out[e]),n),delete this._out[e],delete this._sucs[e],--this._nodeCount}return this},s.prototype.setParent=function(e,t){if(!this._isCompound)throw Error("Cannot set parent in a non-compound graph");if(r.isUndefined(t))t=a;else{t+="";for(var n=t;!r.isUndefined(n);n=this.parent(n))if(n===e)throw Error("Setting "+t+" as parent of "+e+" would create a cycle");this.setNode(t)}return this.setNode(e),this._removeFromParentsChildList(e),this._parent[e]=t,this._children[t][e]=!0,this},s.prototype._removeFromParentsChildList=function(e){delete this._children[this._parent[e]][e]},s.prototype.parent=function(e){if(this._isCompound){var t=this._parent[e];if(t!==a)return t}},s.prototype.children=function(e){if(r.isUndefined(e)&&(e=a),this._isCompound){var t=this._children[e];if(t)return r.keys(t)}else if(e===a)return this.nodes();else if(this.hasNode(e))return[]},s.prototype.predecessors=function(e){var t=this._preds[e];if(t)return r.keys(t)},s.prototype.successors=function(e){var t=this._sucs[e];if(t)return r.keys(t)},s.prototype.neighbors=function(e){var t=this.predecessors(e);if(t)return r.union(t,this.successors(e))},s.prototype.isLeaf=function(e){var t;return 0===(t=this.isDirected()?this.successors(e):this.neighbors(e)).length},s.prototype.filterNodes=function(e){var t=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});t.setGraph(this.graph());var n=this;r.each(this._nodes,function(n,r){e(r)&&t.setNode(r,n)}),r.each(this._edgeObjs,function(e){t.hasNode(e.v)&&t.hasNode(e.w)&&t.setEdge(e,n.edge(e))});var i={};function a(e){var r=n.parent(e);return void 0===r||t.hasNode(r)?(i[e]=r,r):r in i?i[r]:a(r)}return this._isCompound&&r.each(t.nodes(),function(e){t.setParent(e,a(e))}),t},s.prototype.setDefaultEdgeLabel=function(e){return r.isFunction(e)||(e=r.constant(e)),this._defaultEdgeLabelFn=e,this},s.prototype.edgeCount=function(){return this._edgeCount},s.prototype.edges=function(){return r.values(this._edgeObjs)},s.prototype.setPath=function(e,t){var n=this,i=arguments;return r.reduce(e,function(e,r){return i.length>1?n.setEdge(e,r,t):n.setEdge(e,r),r}),this},s.prototype.setEdge=function(){var e,t,n,i,a=!1,o=arguments[0];"object"==typeof o&&null!==o&&"v"in o?(e=o.v,t=o.w,n=o.name,2===arguments.length&&(i=arguments[1],a=!0)):(e=o,t=arguments[1],n=arguments[3],arguments.length>2&&(i=arguments[2],a=!0)),e=""+e,t=""+t,r.isUndefined(n)||(n=""+n);var s=l(this._isDirected,e,t,n);if(r.has(this._edgeLabels,s))return a&&(this._edgeLabels[s]=i),this;if(!r.isUndefined(n)&&!this._isMultigraph)throw Error("Cannot set a named edge when isMultigraph = false");this.setNode(e),this.setNode(t),this._edgeLabels[s]=a?i:this._defaultEdgeLabelFn(e,t,n);var c=f(this._isDirected,e,t,n);return e=c.v,t=c.w,Object.freeze(c),this._edgeObjs[s]=c,u(this._preds[t],e),u(this._sucs[e],t),this._in[t][s]=c,this._out[e][s]=c,this._edgeCount++,this},s.prototype.edge=function(e,t,n){var r=1===arguments.length?d(this._isDirected,arguments[0]):l(this._isDirected,e,t,n);return this._edgeLabels[r]},s.prototype.hasEdge=function(e,t,n){var i=1===arguments.length?d(this._isDirected,arguments[0]):l(this._isDirected,e,t,n);return r.has(this._edgeLabels,i)},s.prototype.removeEdge=function(e,t,n){var r=1===arguments.length?d(this._isDirected,arguments[0]):l(this._isDirected,e,t,n),i=this._edgeObjs[r];return i&&(e=i.v,t=i.w,delete this._edgeLabels[r],delete this._edgeObjs[r],c(this._preds[t],e),c(this._sucs[e],t),delete this._in[t][r],delete this._out[e][r],this._edgeCount--),this},s.prototype.inEdges=function(e,t){var n=this._in[e];if(n){var i=r.values(n);return t?r.filter(i,function(e){return e.v===t}):i}},s.prototype.outEdges=function(e,t){var n=this._out[e];if(n){var i=r.values(n);return t?r.filter(i,function(e){return e.w===t}):i}},s.prototype.nodeEdges=function(e,t){var n=this.inEdges(e,t);if(n)return n.concat(this.outEdges(e,t))}},82354(e,t,n){e.exports={Graph:n(30771),version:n(49631)}},28974(e,t,n){var r=n(89126),i=n(30771);function a(e){var t={options:{directed:e.isDirected(),multigraph:e.isMultigraph(),compound:e.isCompound()},nodes:o(e),edges:s(e)};return r.isUndefined(e.graph())||(t.value=r.clone(e.graph())),t}function o(e){return r.map(e.nodes(),function(t){var n=e.node(t),i=e.parent(t),a={v:t};return r.isUndefined(n)||(a.value=n),r.isUndefined(i)||(a.parent=i),a})}function s(e){return r.map(e.edges(),function(t){var n=e.edge(t),i={v:t.v,w:t.w};return r.isUndefined(t.name)||(i.name=t.name),r.isUndefined(n)||(i.value=n),i})}function u(e){var t=new i(e.options).setGraph(e.value);return r.each(e.nodes,function(e){t.setNode(e.v,e.value),e.parent&&t.setParent(e.v,e.parent)}),r.each(e.edges,function(e){t.setEdge({v:e.v,w:e.w,name:e.name},e.value)}),t}e.exports={write:a,read:u}},89126(e,t,n){var r;try{r={clone:n(66678),constant:n(75703),each:n(66073),filter:n(63105),has:n(18721),isArray:n(1469),isEmpty:n(41609),isFunction:n(23560),isUndefined:n(52353),keys:n(3674),map:n(35161),reduce:n(54061),size:n(84238),transform:n(68718),union:n(93386),values:n(52628)}}catch(i){}r||(r=window._),e.exports=r},49631(e){e.exports="2.1.8"},78892(e){"use strict";e.exports=n;var t=/[#.]/g;function n(e,n){for(var r,i,a,o=e||"",s=n||"div",u={},c=0;cC,q_:()=>F,ob:()=>y,PP:()=>B,Ep:()=>v,Hp:()=>w});var r=n(87462);function i(e){return"/"===e.charAt(0)}function a(e,t){for(var n=t,r=n+1,i=e.length;r=0;d--){var h=o[d];"."===h?a(o,d):".."===h?(a(o,d),f++):f&&(a(o,d),f--)}if(!c)for(;f--;f)o.unshift("..");!c||""===o[0]||o[0]&&i(o[0])||o.unshift("");var p=o.join("/");return n&&"/"!==p.substr(-1)&&(p+="/"),p}let s=o;function u(e){return e.valueOf?e.valueOf():Object.prototype.valueOf.call(e)}function c(e,t){if(e===t)return!0;if(null==e||null==t)return!1;if(Array.isArray(e))return Array.isArray(t)&&e.length===t.length&&e.every(function(e,n){return c(e,t[n])});if("object"==typeof e||"object"==typeof t){var n=u(e),r=u(t);return n!==e||r!==t?c(n,r):Object.keys(Object.assign({},e,t)).every(function(n){return c(e[n],t[n])})}return!1}let l=c;var f=n(2177);function d(e){return"/"===e.charAt(0)?e:"/"+e}function h(e){return"/"===e.charAt(0)?e.substr(1):e}function p(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}function b(e,t){return p(e,t)?e.substr(t.length):e}function m(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function g(e){var t=e||"/",n="",r="",i=t.indexOf("#");-1!==i&&(r=t.substr(i),t=t.substr(0,i));var a=t.indexOf("?");return -1!==a&&(n=t.substr(a),t=t.substr(0,a)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}function v(e){var t=e.pathname,n=e.search,r=e.hash,i=t||"/";return n&&"?"!==n&&(i+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(i+="#"===r.charAt(0)?r:"#"+r),i}function y(e,t,n,i){var a;"string"==typeof e?(a=g(e)).state=t:(void 0===(a=(0,r.Z)({},e)).pathname&&(a.pathname=""),a.search?"?"!==a.search.charAt(0)&&(a.search="?"+a.search):a.search="",a.hash?"#"!==a.hash.charAt(0)&&(a.hash="#"+a.hash):a.hash="",void 0!==t&&void 0===a.state&&(a.state=t));try{a.pathname=decodeURI(a.pathname)}catch(o){if(o instanceof URIError)throw URIError('Pathname "'+a.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.');throw o}return n&&(a.key=n),i?a.pathname?"/"!==a.pathname.charAt(0)&&(a.pathname=s(a.pathname,i.pathname)):a.pathname=i.pathname:a.pathname||(a.pathname="/"),a}function w(e,t){return e.pathname===t.pathname&&e.search===t.search&&e.hash===t.hash&&e.key===t.key&&l(e.state,t.state)}function _(){var e=null;function t(t){return e=t,function(){e===t&&(e=null)}}function n(t,n,r,i){if(null!=e){var a="function"==typeof e?e(t,n):e;"string"==typeof a?"function"==typeof r?r(a,i):i(!0):i(!1!==a)}else i(!0)}var r=[];function i(e){var t=!0;function n(){t&&e.apply(void 0,arguments)}return r.push(n),function(){t=!1,r=r.filter(function(e){return e!==n})}}function a(){for(var e=arguments.length,t=Array(e),n=0;nn?a.splice(n,a.length-n,i):a.push(i),f({action:r,location:i,index:n,entries:a})}})}function g(e,t){var r="REPLACE",i=y(e,t,d(),M.location);l.confirmTransitionTo(i,r,n,function(e){e&&(M.entries[M.index]=i,f({action:r,location:i}))})}function w(e){var t=Y(M.index+e,0,M.entries.length-1),r="POP",i=M.entries[t];l.confirmTransitionTo(i,r,n,function(e){e?f({action:r,location:i,index:t}):f()})}function E(){w(-1)}function S(){w(1)}function k(e){var t=M.index+e;return t>=0&&tu});var r=/[A-Z]/g,i=/^ms-/,a={};function o(e){return"-"+e.toLowerCase()}function s(e){if(a.hasOwnProperty(e))return a[e];var t=e.replace(r,o);return a[e]=i.test(t)?"-"+t:t}let u=s},80645(e,t){/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ t.read=function(e,t,n,r,i){var a,o,s=8*i-r-1,u=(1<>1,l=-7,f=n?i-1:0,d=n?-1:1,h=e[t+f];for(f+=d,a=h&(1<<-l)-1,h>>=-l,l+=s;l>0;a=256*a+e[t+f],f+=d,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=r;l>0;o=256*o+e[t+f],f+=d,l-=8);if(0===a)a=1-c;else{if(a===u)return o?NaN:(h?-1:1)*(1/0);o+=Math.pow(2,r),a-=c}return(h?-1:1)*o*Math.pow(2,a-r)},t.write=function(e,t,n,r,i,a){var o,s,u,c=8*a-i-1,l=(1<>1,d=23===i?5960464477539062e-23:0,h=r?0:a-1,p=r?1:-1,b=t<0||0===t&&1/t<0?1:0;for(isNaN(t=Math.abs(t))||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(u=Math.pow(2,-o))<1&&(o--,u*=2),o+f>=1?t+=d/u:t+=d*Math.pow(2,1-f),t*u>=2&&(o++,u/=2),o+f>=l?(s=0,o=l):o+f>=1?(s=(t*u-1)*Math.pow(2,i),o+=f):(s=t*Math.pow(2,f-1)*Math.pow(2,i),o=0));i>=8;e[n+h]=255&s,h+=p,s/=256,i-=8);for(o=o<0;e[n+h]=255&o,h+=p,o/=256,c-=8);e[n+h-p]|=128*b}},35717(e){"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},46260(e){"use strict";function t(e){var t="string"==typeof e?e.charCodeAt(0):e;return t>=97&&t<=122||t>=65&&t<=90}e.exports=t},7961(e,t,n){"use strict";var r=n(46260),i=n(46195);function a(e){return r(e)||i(e)}e.exports=a},46195(e){"use strict";function t(e){var t="string"==typeof e?e.charCodeAt(0):e;return t>=48&&t<=57}e.exports=t},79480(e){"use strict";function t(e){var t="string"==typeof e?e.charCodeAt(0):e;return t>=97&&t<=102||t>=65&&t<=70||t>=48&&t<=57}e.exports=t},33827(e,t,n){"use strict";n.r(t),n.d(t,{default:()=>a,isBrowser:()=>i});var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i=("undefined"==typeof window?"undefined":r(window))==="object"&&("undefined"==typeof document?"undefined":r(document))==="object"&&9===document.nodeType;let a=i},5826(e){e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},47798(e){"use strict";/*! + * isobject + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ e.exports=function(e){return null!=e&&"object"==typeof e&&!1===Array.isArray(e)}},80204(e,t,n){e.exports=self.fetch||(self.fetch=n(25869).default||n(25869))},5690(e,t,n){e.exports=n(67946)},8126(e,t,n){"use strict";n.d(t,{Z:()=>tl});var r,i="en",a={},o={};function s(){return i}function u(e){i=e}function c(e){return a[e]}function l(e){if(!e)throw Error("No locale data passed");a[e.locale]=e,o[e.locale.toLowerCase()]=e.locale}function f(e){return a[e]?e:o[e.toLowerCase()]?o[e.toLowerCase()]:void 0}function d(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.localeMatcher||"lookup";switch(n){case"lookup":case"best fit":return h(e);default:throw RangeError('Invalid "localeMatcher" option: '.concat(n))}}function h(e){var t=f(e);if(t)return t;for(var n=e.split("-");e.length>1;){n.pop();var r=f(e=n.join("-"));if(r)return r}}var p={af:function(e){return 1==e?"one":"other"},am:function(e){return e>=0&&e<=1?"one":"other"},ar:function(e){var t=String(e).split("."),n=Number(t[0])==e&&t[0].slice(-2);return 0==e?"zero":1==e?"one":2==e?"two":n>=3&&n<=10?"few":n>=11&&n<=99?"many":"other"},ast:function(e){var t=!String(e).split(".")[1];return 1==e&&t?"one":"other"},be:function(e){var t=String(e).split("."),n=Number(t[0])==e,r=n&&t[0].slice(-1),i=n&&t[0].slice(-2);return 1==r&&11!=i?"one":r>=2&&r<=4&&(i<12||i>14)?"few":n&&0==r||r>=5&&r<=9||i>=11&&i<=14?"many":"other"},br:function(e){var t=String(e).split("."),n=Number(t[0])==e,r=n&&t[0].slice(-1),i=n&&t[0].slice(-2),a=n&&t[0].slice(-6);return 1==r&&11!=i&&71!=i&&91!=i?"one":2==r&&12!=i&&72!=i&&92!=i?"two":(3==r||4==r||9==r)&&(i<10||i>19)&&(i<70||i>79)&&(i<90||i>99)?"few":0!=e&&n&&0==a?"many":"other"},bs:function(e){var t=String(e).split("."),n=t[0],r=t[1]||"",i=!t[1],a=n.slice(-1),o=n.slice(-2),s=r.slice(-1),u=r.slice(-2);return i&&1==a&&11!=o||1==s&&11!=u?"one":i&&a>=2&&a<=4&&(o<12||o>14)||s>=2&&s<=4&&(u<12||u>14)?"few":"other"},cs:function(e){var t=String(e).split("."),n=t[0],r=!t[1];return 1==e&&r?"one":n>=2&&n<=4&&r?"few":r?"other":"many"},cy:function(e){return 0==e?"zero":1==e?"one":2==e?"two":3==e?"few":6==e?"many":"other"},da:function(e){var t=String(e).split("."),n=t[0],r=Number(t[0])==e;return 1!=e&&(r||0!=n&&1!=n)?"other":"one"},dsb:function(e){var t=String(e).split("."),n=t[0],r=t[1]||"",i=!t[1],a=n.slice(-2),o=r.slice(-2);return i&&1==a||1==o?"one":i&&2==a||2==o?"two":i&&(3==a||4==a)||3==o||4==o?"few":"other"},dz:function(e){return"other"},fil:function(e){var t=String(e).split("."),n=t[0],r=t[1]||"",i=!t[1],a=n.slice(-1),o=r.slice(-1);return i&&(1==n||2==n||3==n)||i&&4!=a&&6!=a&&9!=a||!i&&4!=o&&6!=o&&9!=o?"one":"other"},fr:function(e){return e>=0&&e<2?"one":"other"},ga:function(e){var t=Number(String(e).split(".")[0])==e;return 1==e?"one":2==e?"two":t&&e>=3&&e<=6?"few":t&&e>=7&&e<=10?"many":"other"},gd:function(e){var t=Number(String(e).split(".")[0])==e;return 1==e||11==e?"one":2==e||12==e?"two":t&&e>=3&&e<=10||t&&e>=13&&e<=19?"few":"other"},he:function(e){var t=String(e).split("."),n=t[0],r=!t[1],i=Number(t[0])==e,a=i&&t[0].slice(-1);return 1==e&&r?"one":2==n&&r?"two":r&&(e<0||e>10)&&i&&0==a?"many":"other"},is:function(e){var t=String(e).split("."),n=t[0],r=Number(t[0])==e,i=n.slice(-1),a=n.slice(-2);return r&&1==i&&11!=a||!r?"one":"other"},ksh:function(e){return 0==e?"zero":1==e?"one":"other"},lt:function(e){var t=String(e).split("."),n=t[1]||"",r=Number(t[0])==e,i=r&&t[0].slice(-1),a=r&&t[0].slice(-2);return 1==i&&(a<11||a>19)?"one":i>=2&&i<=9&&(a<11||a>19)?"few":0!=n?"many":"other"},lv:function(e){var t=String(e).split("."),n=t[1]||"",r=n.length,i=Number(t[0])==e,a=i&&t[0].slice(-1),o=i&&t[0].slice(-2),s=n.slice(-2),u=n.slice(-1);return i&&0==a||o>=11&&o<=19||2==r&&s>=11&&s<=19?"zero":1==a&&11!=o||2==r&&1==u&&11!=s||2!=r&&1==u?"one":"other"},mk:function(e){var t=String(e).split("."),n=t[0],r=t[1]||"",i=!t[1],a=n.slice(-1),o=n.slice(-2),s=r.slice(-1),u=r.slice(-2);return i&&1==a&&11!=o||1==s&&11!=u?"one":"other"},mt:function(e){var t=String(e).split("."),n=Number(t[0])==e&&t[0].slice(-2);return 1==e?"one":0==e||n>=2&&n<=10?"few":n>=11&&n<=19?"many":"other"},pa:function(e){return 0==e||1==e?"one":"other"},pl:function(e){var t=String(e).split("."),n=t[0],r=!t[1],i=n.slice(-1),a=n.slice(-2);return 1==e&&r?"one":r&&i>=2&&i<=4&&(a<12||a>14)?"few":r&&1!=n&&(0==i||1==i)||r&&i>=5&&i<=9||r&&a>=12&&a<=14?"many":"other"},pt:function(e){var t=String(e).split(".")[0];return 0==t||1==t?"one":"other"},ro:function(e){var t=String(e).split("."),n=!t[1],r=Number(t[0])==e&&t[0].slice(-2);return 1==e&&n?"one":!n||0==e||1!=e&&r>=1&&r<=19?"few":"other"},ru:function(e){var t=String(e).split("."),n=t[0],r=!t[1],i=n.slice(-1),a=n.slice(-2);return r&&1==i&&11!=a?"one":r&&i>=2&&i<=4&&(a<12||a>14)?"few":r&&0==i||r&&i>=5&&i<=9||r&&a>=11&&a<=14?"many":"other"},se:function(e){return 1==e?"one":2==e?"two":"other"},si:function(e){var t=String(e).split("."),n=t[0],r=t[1]||"";return 0==e||1==e||0==n&&1==r?"one":"other"},sl:function(e){var t=String(e).split("."),n=t[0],r=!t[1],i=n.slice(-2);return r&&1==i?"one":r&&2==i?"two":r&&(3==i||4==i)||!r?"few":"other"}};p.as=p.am,p.az=p.af,p.bg=p.af,p.bn=p.am,p.ca=p.ast,p.ce=p.af,p.chr=p.af,p.de=p.ast,p.ee=p.af,p.el=p.af,p.en=p.ast,p.es=p.af,p.et=p.ast,p.eu=p.af,p.fa=p.am,p.fi=p.ast,p.fo=p.af,p.fur=p.af,p.fy=p.ast,p.gl=p.ast,p.gu=p.am,p.hi=p.am,p.hr=p.bs,p.hsb=p.dsb,p.hu=p.af,p.hy=p.fr,p.ia=p.ast,p.id=p.dz,p.it=p.ast,p.ja=p.dz,p.jgo=p.af,p.jv=p.dz,p.ka=p.af,p.kea=p.dz,p.kk=p.af,p.kl=p.af,p.km=p.dz,p.kn=p.am,p.ko=p.dz,p.ku=p.af,p.ky=p.af,p.lb=p.af,p.lkt=p.dz,p.lo=p.dz,p.ml=p.af,p.mn=p.af,p.mr=p.am,p.ms=p.dz,p.my=p.dz,p.nb=p.af,p.ne=p.af,p.nl=p.ast,p.nn=p.af,p.or=p.af,p.ps=p.af,p["pt-PT"]=p.ast,p.sah=p.dz,p.sd=p.af,p.sk=p.cs,p.so=p.af,p.sq=p.af,p.sr=p.bs,p.sv=p.ast,p.sw=p.ast,p.ta=p.af,p.te=p.af,p.th=p.dz,p.ti=p.pa,p.tk=p.af,p.to=p.dz,p.tr=p.af,p.ug=p.af,p.uk=p.ru,p.ur=p.ast,p.uz=p.af,p.vi=p.dz,p.wae=p.af,p.yi=p.ast,p.yue=p.dz,p.zh=p.dz,p.zu=p.am;let b=p;function m(e){return"pt-PT"===e?e:v(e)}var g=/^([a-z0-9]+)/i;function v(e){var t=e.match(g);if(!t)throw TypeError("Invalid locale: ".concat(e));return t[1]}function y(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function w(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};A(this,e),I(this,"numeric","always"),I(this,"style","long"),I(this,"localeMatcher","lookup");var r=n.numeric,i=n.style,a=n.localeMatcher;if(void 0!==r){if(0>N.indexOf(r))throw RangeError('Invalid "numeric" option: '.concat(r));this.numeric=r}if(void 0!==i){if(0>P.indexOf(i))throw RangeError('Invalid "style" option: '.concat(i));this.style=i}if(void 0!==a){if(0>R.indexOf(a))throw RangeError('Invalid "localeMatcher" option: '.concat(a));this.localeMatcher=a}if("string"==typeof t&&(t=[t]),t.push(s()),this.locale=e.supportedLocalesOf(t,{localeMatcher:this.localeMatcher})[0],!this.locale)throw Error("No supported locale was found");E.supportedLocalesOf(this.locale).length>0?this.pluralRules=new E(this.locale):console.warn('"'.concat(this.locale,'" locale is not supported')),"undefined"!=typeof Intl&&Intl.NumberFormat?(this.numberFormat=new Intl.NumberFormat(this.locale),this.numberingSystem=this.numberFormat.resolvedOptions().numberingSystem):this.numberingSystem="latn",this.locale=d(this.locale,{localeMatcher:this.localeMatcher})}return C(e,[{key:"format",value:function(){var e=z(arguments),t=x(e,2),n=t[0],r=t[1];return this.getRule(n,r).replace("{0}",this.formatNumber(Math.abs(n)))}},{key:"formatToParts",value:function(){var e=z(arguments),t=x(e,2),n=t[0],r=t[1],i=this.getRule(n,r),a=i.indexOf("{0}");if(a<0)return[{type:"literal",value:i}];var o=[];return a>0&&o.push({type:"literal",value:i.slice(0,a)}),o=o.concat(this.formatNumberToParts(Math.abs(n)).map(function(e){return k({},e,{unit:r})})),a+31&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e)e=[e];else if(!Array.isArray(e))throw TypeError('Invalid "locales" argument');return e.filter(function(e){return d(e,t)})},j.addLocale=l,j.setDefaultLocale=u,j.getDefaultLocale=s,j.PluralRules=E;var F='Invalid "unit" argument';function Y(e){if("symbol"===S(e))throw TypeError(F);if("string"!=typeof e||("s"===e[e.length-1]&&(e=e.slice(0,e.length-1)),0>D.indexOf(e)))throw RangeError("".concat(F,": ").concat(e));return e}var B='Invalid "number" argument';function U(e){if(e=Number(e),Number.isFinite&&!Number.isFinite(e))throw RangeError("".concat(B,": ").concat(e));return e}function H(e){return 1/e==-1/0}function $(e){return e<0||0===e&&H(e)}function z(e){if(e.length<2)throw TypeError('"unit" argument is required');return[U(e[0]),Y(e[1])]}function G(e){return(G="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function W(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function K(e,t){for(var n=0;n=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;if(t(o))return o;for(var s=o.split("-");s.length>1;)if(s.pop(),t(o=s.join("-")))return o}throw Error("No locale data has been registered for any of the locales: ".concat(e.join(", ")))}function Q(){return("undefined"==typeof Intl?"undefined":X(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var ee=60,et=60*ee,en=24*et,er=7*en,ei=30.44*en,ea=365.2425*en;function eo(e){switch(e){case"second":return 1;case"minute":return ee;case"hour":return et;case"day":return en;case"week":return er;case"month":return ei;case"year":return ea}}function es(e){return void 0!==e.factor?e.factor:eo(e.unit||e.formatAs)||1}function eu(e){return"floor"===e?Math.floor:(0,Math.round)}function ec(e){return"floor"===e?1:.5}function el(e){return(el="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function ef(e,t){var n,r=t.prevStep,i=t.timestamp,a=t.now,o=t.future,s=t.round;return r&&(r.id||r.unit)&&(n=e["threshold_for_".concat(r.id||r.unit)]),void 0===n&&void 0!==e.threshold&&"function"==typeof(n=e.threshold)&&(n=n(a,o)),void 0===n&&(n=e.minTime),"object"===el(n)&&(n=r&&r.id&&void 0!==n[r.id]?n[r.id]:n.default),"function"==typeof n&&(n=n(i,{future:o,getMinTimeForUnit:function(e,t){return ed(e,t||r&&r.formatAs,{round:s})}})),void 0===n&&e.test&&(n=e.test(i,{now:a,future:o})?0:9007199254740991),void 0===n&&(r?e.formatAs&&r.formatAs&&(n=ed(e.formatAs,r.formatAs,{round:s})):n=0),void 0===n&&console.warn("[javascript-time-ago] A step should specify `minTime`:\n"+JSON.stringify(e,null,2)),n}function ed(e,t,n){var r,i=n.round,a=eo(e);if(r="now"===t?eo(e):eo(t),void 0!==a&&void 0!==r)return a-r*(1-ec(i))}function eh(e){for(var t=1;t0?e[o-1]:s}}}function eg(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,i=ef(e[r],eh({prevStep:e[r-1],timestamp:n.now-1e3*t},n));return void 0===i||Math.abs(t)=0})}function ey(e,t,n){var r=n.now,i=n.round;if(eo(e)){var a=1e3*eo(e),o=t>r,s=Math.abs(t-r),u=eu(i)(s/a)*a;return o?u>0?s-u+e_(i,a):s-u+1:-(s-u)+ew(i,a)}}function ew(e,t){return ec(e)*t}function e_(e,t){return(1-ec(e))*t+1}var eE=31536e9;function eS(e,t,n){var r,i=n.prevStep,a=n.nextStep,o=n.now,s=n.future,u=n.round,c=e.getTime?e.getTime():e,l=function(e){return ey(e,c,{now:o,round:u})},f=ex(s?t:a,c,{future:s,now:o,round:u,prevStep:s?i:t});if(void 0!==f){if(t&&(t.getTimeToNextUpdate&&(r=t.getTimeToNextUpdate(c,{getTimeToNextUpdateForUnit:l,getRoundFunction:eu,now:o,future:s,round:u})),void 0===r)){var d=t.unit||t.formatAs;d&&(r=l(d))}return void 0===r?f:Math.min(r,f)}}function ek(e,t,n){var r,i=n.now,a=n.future,o=ef(e,{timestamp:t,now:i,future:a,round:n.round,prevStep:n.prevStep});return void 0===o?void 0:a?t-1e3*o+1:0===o&&t===i?eE:t+1e3*o}function ex(e,t,n){var r=n.now,i=n.future,a=n.round,o=n.prevStep;if(e){var s=ek(e,t,{now:r,future:i,round:a,prevStep:o});if(void 0===s)return;return s-r}return i?t-r+1:eE}var eT={};function eM(e){return eT[e]}function eO(e){if(!e)throw Error("[javascript-time-ago] No locale data passed.");eT[e.locale]=e}let eA=[{formatAs:"now"},{formatAs:"second"},{formatAs:"minute"},{formatAs:"hour"},{formatAs:"day"},{formatAs:"week"},{formatAs:"month"},{formatAs:"year"}],eL={steps:eA,labels:"long"};function eC(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:[],n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=n.polyfill;ts(this,e),"string"==typeof t&&(t=[t]),this.locale=J(t.concat(e.getDefaultLocale()),eM),"undefined"!=typeof Intl&&Intl.NumberFormat&&(this.numberFormat=new Intl.NumberFormat(this.locale)),!1===r?(this.IntlRelativeTimeFormat=Intl.RelativeTimeFormat,this.IntlPluralRules=Intl.PluralRules):(this.IntlRelativeTimeFormat=j,this.IntlPluralRules=j.PluralRules),this.relativeTimeFormatCache=new Z,this.pluralRulesCache=new Z}return tc(e,[{key:"format",value:function(e,t,n){n||(t&&!tv(t)?(n=t,t=void 0):n={}),t||(t=eD),"string"==typeof t&&(t=tt(t));var r,i=td(e),a=this.getLabels(t.flavour||t.labels),o=a.labels,s=a.labelsType;void 0!==t.now&&(r=t.now),void 0===r&&void 0!==n.now&&(r=n.now),void 0===r&&(r=Date.now());var u=(r-i)/1e3,c=n.future||u<0,l=tb(o,eM(this.locale).now,eM(this.locale).long,c);if(t.custom){var f=t.custom({now:r,date:new Date(i),time:i,elapsed:u,locale:this.locale});if(void 0!==f)return f}var d=tp(t.units,o,l),h=n.round||t.round,p=eb(t.gradation||t.steps||eD.steps,u,{now:r,units:d,round:h,future:c,getNextStep:!0}),b=tr(p,3),m=b[0],g=b[1],v=b[2],y=this.formatDateForStep(i,g,u,{labels:o,labelsType:s,nowLabel:l,now:r,future:c,round:h})||"";if(n.getTimeToNextUpdate){var w=eS(i,g,{nextStep:v,prevStep:m,now:r,future:c,round:h});return[y,w]}return y}},{key:"formatDateForStep",value:function(e,t,n,r){var i=this,a=r.labels,o=r.labelsType,s=r.nowLabel,u=r.now,c=r.future,l=r.round;if(t){if(t.format)return t.format(e,this.locale,{formatAs:function(e,t){return i.formatValue(t,e,{labels:a,future:c})},now:u,future:c});var f=t.unit||t.formatAs;if(!f)throw Error("[javascript-time-ago] Each step must define either `formatAs` or `format()`. Step: ".concat(JSON.stringify(t)));if("now"===f)return s;var d=Math.abs(n)/es(t);t.granularity&&(d=eu(l)(d/t.granularity)*t.granularity);var h=-1*Math.sign(n)*eu(l)(d);switch(0===h&&(h=0),o){case"long":case"short":case"narrow":return this.getFormatter(o).format(h,f);default:return this.formatValue(h,f,{labels:a,future:c})}}}},{key:"formatValue",value:function(e,t,n){var r=n.labels,i=n.future;return this.getFormattingRule(r,t,e,{future:i}).replace("{0}",this.formatNumber(Math.abs(e)))}},{key:"getFormattingRule",value:function(e,t,n,r){var i=r.future;if(this.locale,"string"==typeof(e=e[t]))return e;var a=e[0===n?i?"future":"past":n<0?"past":"future"]||e;return"string"==typeof a?a:a[this.getPluralRules().select(Math.abs(n))]||a.other}},{key:"formatNumber",value:function(e){return this.numberFormat?this.numberFormat.format(e):String(e)}},{key:"getFormatter",value:function(e){return this.relativeTimeFormatCache.get(this.locale,e)||this.relativeTimeFormatCache.put(this.locale,e,new this.IntlRelativeTimeFormat(this.locale,{style:e}))}},{key:"getPluralRules",value:function(){return this.pluralRulesCache.get(this.locale)||this.pluralRulesCache.put(this.locale,new this.IntlPluralRules(this.locale))}},{key:"getLabels",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];"string"==typeof e&&(e=[e]),e=(e=e.map(function(e){switch(e){case"tiny":case"mini-time":return"mini";default:return e}})).concat("long");for(var t=eM(this.locale),n=e,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;if(t[o])return{labelsType:o,labels:t[o]}}}}]),e}(),tf="en";function td(e){if(e.constructor===Date||th(e))return e.getTime();if("number"==typeof e)return e;throw Error("Unsupported relative time formatter input: ".concat(tn(e),", ").concat(e))}function th(e){return"object"===tn(e)&&"function"==typeof e.getTime}function tp(e,t,n){var r=Object.keys(t);return n&&r.push("now"),e&&(r=e.filter(function(e){return"now"===e||r.indexOf(e)>=0})),r}function tb(e,t,n,r){var i=e.now||t&&t.now;return i?"string"==typeof i?i:r?i.future:i.past:n&&n.second&&n.second.current?n.second.current:void 0}tl.getDefaultLocale=function(){return tf},tl.setDefaultLocale=function(e){return tf=e},tl.addDefaultLocale=function(e){if(r)throw Error("[javascript-time-ago] `TimeAgo.addDefaultLocale()` can only be called once. To add other locales, use `TimeAgo.addLocale()`.");r=!0,tl.setDefaultLocale(e.locale),tl.addLocale(e)},tl.addLocale=function(e){eO(e),j.addLocale(e)},tl.locale=tl.addLocale,tl.addLabels=function(e,t,n){var r=eM(e);r||(eO({locale:e}),r=eM(e)),r[t]=n};var tm={}.constructor;function tg(e){return void 0!==tn(e)&&null!==e&&e.constructor===tm}function tv(e){return"string"==typeof e||ty(e)}function ty(e){return tg(e)&&(Array.isArray(e.steps)||Array.isArray(e.gradation)||Array.isArray(e.flavour)||"string"==typeof e.flavour||Array.isArray(e.labels)||"string"==typeof e.labels||Array.isArray(e.units)||"function"==typeof e.custom)}},41800(e,t,n){e.exports=function(){"use strict";var e={121:function(e,t,r){r.r(t),r.d(t,{default:function(){return E}}),n(41539),n(21249),n(54747),n(15306),n(74916),n(47042),n(82526),n(41817),n(32165),n(78783),n(66992),n(33948),n(81486);var i=n(68929),a=r.n(i),o=n(1469),s=r.n(o),u=n(45220),c=r.n(u),l=n(3674),f=r.n(l),d=n(82492),h=r.n(d);function p(e){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function b(e){return s()(e)?e:[e]}function m(e){if(null===e||"object"!==p(e)||(t=e,"[object Date]"===Object.prototype.toString.call(t)))return e;if(s()(e))return e.map(m);var t,n={};return f()(e).forEach(function(t){n[a()(t)]=m(e[t])}),n}function g(e,t){var n=t.camelizeKeys,r=t.camelizeTypeValues,i={};return f()(e).forEach(function(t){var o=e[t],u=n?a()(t):t;i[u]={},void 0!==o.data&&(s()(o.data)?i[u].data=o.data.map(function(e){return{id:e.id,type:r?a()(e.type):e.type}}):c()(o.data)?i[u].data=o.data:i[u].data={id:o.data.id,type:r?a()(o.data.type):o.data.type}),o.links&&(i[u].links=n?m(o.links):o.links),o.meta&&(i[u].meta=n?m(o.meta):o.meta)}),i}function v(e,t){if(t.camelizeKeys){var n={};return f()(e).forEach(function(t){n[a()(t)]=m(e[t])}),n}return e}function y(e,t){var n=t.camelizeKeys,r=t.camelizeTypeValues,i={};return b(e).forEach(function(e){var t=n?a()(e.type):e.type;i[t]=i[t]||{},i[t][e.id]=i[t][e.id]||{id:e.id},i[t][e.id].type=r?a()(e.type):e.type,n?(i[t][e.id].attributes={},f()(e.attributes).forEach(function(n){i[t][e.id].attributes[a()(n)]=m(e.attributes[n])})):i[t][e.id].attributes=e.attributes,e.links&&(i[t][e.id].links={},f()(e.links).forEach(function(r){var o=n?a()(r):r;i[t][e.id].links[o]=e.links[r]})),e.relationships&&(i[t][e.id].relationships=g(e.relationships,{camelizeKeys:n,camelizeTypeValues:r})),e.meta&&(i[t][e.id].meta=v(e.meta,{camelizeKeys:n}))}),i}function w(e){return e.replace(/\?.*$/,"")}function _(e,t,n){var r,i=n.camelizeKeys,o=n.camelizeTypeValues,s={meta:{}};if(n.filterEndpoint)s.meta[t]={},r=s.meta[t];else{var u=w(t);s.meta[u]={},s.meta[u][t.slice(u.length)]={},r=s.meta[u][t.slice(u.length)]}if(r.data={},e.data){var c=[];b(e.data).forEach(function(e){var t={id:e.id,type:o?a()(e.type):e.type};e.relationships&&(t.relationships=g(e.relationships,{camelizeKeys:i,camelizeTypeValues:o})),c.push(t)}),r.data=c}return e.links&&(r.links=e.links,s.meta[w(t)].links=e.links),e.meta&&(r.meta=v(e.meta,{camelizeKeys:i})),s}function E(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.filterEndpoint,r=void 0===n||n,i=t.camelizeKeys,a=void 0===i||i,o=t.camelizeTypeValues,s=void 0===o||o,u=t.endpoint,c={};if(e.data&&h()(c,y(e.data,{camelizeKeys:a,camelizeTypeValues:s})),e.included&&h()(c,y(e.included,{camelizeKeys:a,camelizeTypeValues:s})),u){var l=r?w(u):u;h()(c,_(e,l,{camelizeKeys:a,camelizeTypeValues:s,filterEndpoint:r}))}return c}}},t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={exports:{}};return e[n](i,i.exports,r),i.exports}return r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r(121)}()},63731:function(e){var t,n;t="undefined"!=typeof self?self:this,n=function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t||4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,(function(t){return e[t]}).bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(1),i=/["'&<>]/,a=function(e){var t=i.exec(e);if(null!==t){var n,r="",a=void 0,o=0;for(a=t.index;a")},e.prototype.space=function(){this.buffer.push(" ")},e.prototype.indent=function(e){if(e>0){for(var t="",n=0;n'+a(e)+""),this.buffer.push('"')},e.prototype.printString=function(e){this.buffer.push('"'),this.buffer.push(''+a(e)+""),this.buffer.push('"')},e.prototype.printBoolean=function(e){this.buffer.push(''+e+"")},e.prototype.printNumber=function(e){this.buffer.push(''+e+"")},e.prototype.printSelectionStart=function(){this.buffer.push(""),this.buffer.push('
')},e.prototype.printSelectionEnd=function(){this.buffer.push("
"),this.buffer.push('
')},Object.defineProperty(e.prototype,"printSelectionEndAtNewLine",{set:function(e){this._printSelectionEndAtNewLine=e},enumerable:!0,configurable:!0}),e.prototype.toString=function(){return this.buffer.join("")},e}(),s=function(e,t,n,r,i){t.checkCircular(e),t.print("{"),t.newLine();for(var a=Object.keys(e),o=0;o'):a.print('
'),Array.isArray(e)?u(e,a,0,t,i):s(e,a,0,t,i),a.print("
"),a.toString()}return""}},function(e,t,n){"use strict";n.r(t),n.d(t,"__extends",function(){return i}),n.d(t,"__assign",function(){return a}),n.d(t,"__rest",function(){return o}),n.d(t,"__decorate",function(){return s}),n.d(t,"__param",function(){return u}),n.d(t,"__metadata",function(){return c}),n.d(t,"__awaiter",function(){return l}),n.d(t,"__generator",function(){return f}),n.d(t,"__exportStar",function(){return d}),n.d(t,"__values",function(){return h}),n.d(t,"__read",function(){return p}),n.d(t,"__spread",function(){return b}),n.d(t,"__await",function(){return m}),n.d(t,"__asyncGenerator",function(){return g}),n.d(t,"__asyncDelegator",function(){return v}),n.d(t,"__asyncValues",function(){return y}),n.d(t,"__makeTemplateObject",function(){return w}),n.d(t,"__importStar",function(){return _}),n.d(t,"__importDefault",function(){return E});/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ var r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])};function i(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]])}return n}function s(e,t,n,r){var i,a=arguments.length,o=a<3?t:null===r?r=Object.getOwnPropertyDescriptor(t,n):r;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(o=(a<3?i(o):a>3?i(t,n,o):i(t,n))||o);return a>3&&o&&Object.defineProperty(t,n,o),o}function u(e,t){return function(n,r){t(n,r,e)}}function c(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)}function l(e,t,n,r){return new(n||(n=Promise))(function(i,a){function o(e){try{u(r.next(e))}catch(t){a(t)}}function s(e){try{u(r.throw(e))}catch(t){a(t)}}function u(e){e.done?i(e.value):new n(function(t){t(e.value)}).then(o,s)}u((r=r.apply(e,t||[])).next())})}function f(e,t){var n,r,i,a,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return a={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function s(a){return function(s){return function(a){if(n)throw TypeError("Generator is already executing.");for(;o;)try{if(n=1,r&&(i=r[2&a[0]?"return":a[0]?"throw":"next"])&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[0,i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!(i=(i=o.trys).length>0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}function p(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,i,a=n.call(e),o=[];try{for(;(void 0===t||t-- >0)&&!(r=a.next()).done;)o.push(r.value)}catch(s){i={error:s}}finally{try{r&&!r.done&&(n=a.return)&&n.call(a)}finally{if(i)throw i.error}}return o}function b(){for(var e=[],t=0;t1||s(e,t)})})}function s(e,t){try{var n;(n=i[e](t)).value instanceof m?Promise.resolve(n.value.v).then(u,c):l(a[0][2],n)}catch(r){l(a[0][3],r)}}function u(e){s("next",e)}function c(e){s("throw",e)}function l(e,t){e(t),a.shift(),a.length&&s(a[0][0],a[0][1])}}function v(e){var t,n;return t={},r("next"),r("throw",function(e){throw e}),r("return"),t[Symbol.iterator]=function(){return this},t;function r(r,i){e[r]&&(t[r]=function(t){return(n=!n)?{value:m(e[r](t)),done:"return"===r}:i?i(t):t})}}function y(e){if(!Symbol.asyncIterator)throw TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator];return t?t.call(e):h(e)}function w(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e}function _(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function E(e){return e&&e.__esModule?e:{default:e}}}])},e.exports=n()},35828(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=s;var r=n(25477),i=a(r);function a(e){return e&&e.__esModule?e:{default:e}}function o(e){var t={};for(var n in e)t[(0,i.default)(n)]=e[n];return e.fallbacks&&(Array.isArray(e.fallbacks)?t.fallbacks=e.fallbacks.map(o):t.fallbacks=o(e.fallbacks)),t}function s(){function e(e){if(Array.isArray(e)){for(var t=0;t0&&void 0!==arguments[0]?arguments[0]:{},t=s(e);function n(e,n){if("style"!==n.type)return e;for(var r in e)e[r]=c(r,e[r],t);return e}function r(e,n){return c(n,e,t)}return{onProcessStyle:n,onChangeValue:r}}},29059(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};return e.createGenerateClassName&&(this.options.createGenerateClassName=e.createGenerateClassName,this.generateClassName=e.createGenerateClassName()),null!=e.insertionPoint&&(this.options.insertionPoint=e.insertionPoint),(e.virtual||e.Renderer)&&(this.options.Renderer=e.Renderer||(e.virtual?A.default:M.default)),e.plugins&&this.use.apply(this,e.plugins),this}},{key:"createStyleSheet",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.index;"number"!=typeof n&&(n=0===y.default.index?0:y.default.index+1);var r=new c.default(e,i({},t,{jss:this,generateClassName:t.generateClassName||this.generateClassName,insertionPoint:this.options.insertionPoint,Renderer:this.options.Renderer,index:n}));return this.plugins.onProcessSheet(r),r}},{key:"removeStyleSheet",value:function(e){return e.detach(),y.default.remove(e),this}},{key:"createRule",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};(void 0===e?"undefined":r(e))==="object"&&(n=t,t=e,e=void 0);var i=n;i.jss=this,i.Renderer=this.options.Renderer,i.generateClassName||(i.generateClassName=this.generateClassName),i.classes||(i.classes={});var a=(0,x.default)(e,t,i);return!i.selector&&a instanceof _.default&&(a.selector="."+i.generateClassName(a)),this.plugins.onProcessRule(a),a}},{key:"use",value:function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r0&&(this.refs[t]--,0===this.refs[t]&&this.sheets[t].detach())}},{key:"size",get:function(){return this.keys.length}}]),e}();t.default=u},92122(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var n=0;n=this.index){t.push(e);return}for(var r=0;rn){t.splice(r,0,e);return}}}},{key:"reset",value:function(){this.registry=[]}},{key:"remove",value:function(e){var t=this.registry.indexOf(e);this.registry.splice(t,1)}},{key:"toString",value:function(e){return this.registry.filter(function(e){return e.attached}).map(function(t){return t.toString(e)}).join("\n")}},{key:"index",get:function(){return 0===this.registry.length?0:this.registry[this.registry.length-1].options.index}}]),e}();t.default=i},26899(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:0;return e.substr(t,e.indexOf("{")-1)},function(e){if(e.type===y.STYLE_RULE)return e.selectorText;if(e.type===y.KEYFRAMES_RULE){var t=e.name;if(t)return"@keyframes "+t;var n=e.cssText;return"@"+v(n,n.indexOf("keyframes"))}return v(e.cssText)});function _(e,t){return e.selectorText=t,e.selectorText===t}var E,S,k=p(function(){return document.head||document.getElementsByTagName("head")[0]}),x=(E=void 0,S=!1,function(e){var t={};E||(E=document.createElement("style"));for(var n=0;nt.index&&r.options.insertionPoint===t.insertionPoint)return r}return null}function M(e,t){for(var n=e.length-1;n>=0;n--){var r=e[n];if(r.attached&&r.options.insertionPoint===t.insertionPoint)return r}return null}function O(e){for(var t=k(),n=0;n0){var n=T(t,e);if(n)return n.renderer.element;if(n=M(t,e))return n.renderer.element.nextElementSibling}var r=e.insertionPoint;if(r&&"string"==typeof r){var i=O(r);if(i)return i.nextSibling;(0,a.default)("jss"===r,'[JSS] Insertion point "%s" not found.',r)}return null}function L(e,t){var n=t.insertionPoint,r=A(t);if(r){var i=r.parentNode;i&&i.insertBefore(e,r);return}if(n&&"number"==typeof n.nodeType){var o=n,s=o.parentNode;s?s.insertBefore(e,o.nextSibling):(0,a.default)(!1,"[JSS] Insertion point is not in the DOM.");return}k().insertBefore(e,r)}var C=p(function(){var e=document.querySelector('meta[property="csp-nonce"]');return e?e.getAttribute("content"):null}),I=function(){function e(t){h(this,e),this.getPropertyValue=b,this.setProperty=m,this.removeProperty=g,this.setSelector=_,this.getKey=w,this.getUnescapedKeysMap=x,this.hasInsertedRules=!1,t&&s.default.add(t),this.sheet=t;var n=this.sheet?this.sheet.options:{},r=n.media,i=n.meta,a=n.element;this.element=a||document.createElement("style"),this.element.setAttribute("data-jss",""),r&&this.element.setAttribute("media",r),i&&this.element.setAttribute("data-meta",i);var o=C();o&&this.element.setAttribute("nonce",o)}return r(e,[{key:"attach",value:function(){!this.element.parentNode&&this.sheet&&(this.hasInsertedRules&&(this.deploy(),this.hasInsertedRules=!1),L(this.element,this.sheet.options))}},{key:"detach",value:function(){this.element.parentNode.removeChild(this.element)}},{key:"deploy",value:function(){this.sheet&&(this.element.textContent="\n"+this.sheet.toString()+"\n")}},{key:"insertRule",value:function(e,t){var n=this.element.sheet,r=n.cssRules,i=e.toString();if(t||(t=r.length),!i)return!1;try{n.insertRule(i,t)}catch(o){return(0,a.default)(!1,"[JSS] Can not insert an unsupported rule \n\r%s",e),!1}return this.hasInsertedRules=!0,r[t]}},{key:"deleteRule",value:function(e){var t=this.element.sheet,n=this.indexOf(e);return -1!==n&&(t.deleteRule(n),!0)}},{key:"indexOf",value:function(e){for(var t=this.element.sheet.cssRules,n=0;n0&&void 0!==arguments[0]?arguments[0]:{indent:1},t=this.rules.toString(e);return t?this.key+" {\n"+t+"\n}":""}}]),e}();t.default=c},12398(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{indent:1},t=this.rules.toString(e);return t&&(t+="\n"),this.key+" {\n"+t+"}"}}]),e}();t.default=c},3486(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var n=0;nc&&(0,i.default)(!1,"[JSS] You might have a memory leak. Rule counter is at %s.",e);var a=t,o="";return(r&&(a=r.options.classNamePrefix||t,null!=r.options.jss.id&&(o+=r.options.jss.id)),"production"===l)?""+a+s.default+o+e:a+n.key+"-"+s.default+(o&&"-"+o)+"-"+e}}},89380(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=l;var r=n(63189),i=c(r),a=n(15803),o=c(a),s=n(2808),u=c(s);function c(e){return e&&e.__esModule?e:{default:e}}function l(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"unnamed",t=arguments[1],n=arguments[2],r=n.jss,a=(0,u.default)(t),s=r.plugins.onCreateRule(e,a,n);return s||("@"===e[0]&&(0,i.default)(!1,"[JSS] Unknown at-rule %s",e),new o.default(e,a,n))}},55878(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n.g.CSS,i="production",a=/([[\].#*$><+~=|^:(),"'`])/g;t.default=function(e){return"production"===i?e:r&&r.escape?r.escape(e):e.replace(a,"\\$1")}},27343(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function r(e){var t=null;for(var i in e){var a=e[i],o=void 0===a?"undefined":n(a);if("function"===o)t||(t={}),t[i]=a;else if("object"===o&&null!==a&&!Array.isArray(a)){var s=r(a);s&&(t||(t={}),t[i]=s)}}return t}t.default=r},97628(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(67121),i=a(r);function a(e){return e&&e.__esModule?e:{default:e}}t.default=function(e){return e&&e[i.default]&&e===e[i.default]()}},94229(e,t){"use strict";function n(e,t){e.renderable=t,e.rules&&t.cssRules&&e.rules.link(t.cssRules)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},141(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r="2f1acc6c3a606b082e5eef5e54414ffb";null==n.g[r]&&(n.g[r]=0),t.default=n.g[r]++},70084(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=s;var r=n(16229),i=a(r);function a(e){return e&&e.__esModule?e:{default:e}}function o(e,t){for(var n="",r=0;r2&&void 0!==arguments[2]?arguments[2]:{},r="";if(!t)return r;var a=n.indent,s=void 0===a?0:a,u=t.fallbacks;if(s++,u){if(Array.isArray(u))for(var c=0;c1&&void 0!==arguments[1]&&arguments[1];if(!Array.isArray(e))return e;var r="";if(Array.isArray(e[0]))for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:{};s(this,e),this.cookieOptions=Object.assign({path:"/"},t),u=void 0===t.prefix?u:t.prefix}return r(e,[{key:"getItem",value:function(e){var t=a.default.parse(document.cookie);return t&&t.hasOwnProperty(u+e)?t[u+e]:null}},{key:"setItem",value:function(e,t){return document.cookie=a.default.serialize(u+e,t,this.cookieOptions),t}},{key:"removeItem",value:function(e){var t=Object.assign({},this.cookieOptions,{maxAge:-1});return document.cookie=a.default.serialize(u+e,"",t),null}},{key:"clear",value:function(){var e=a.default.parse(document.cookie);for(var t in e)0===t.indexOf(u)&&this.removeItem(t.substr(u.length));return null}}]),e}();function l(){var e=new c;try{var t="__test";e.setItem(t,"1");var n=e.getItem(t);return e.removeItem(t),"1"===n}catch(r){return!1}}t.default=c},90145(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:"localStorage",t=String(e).replace(/storage$/i,"").toLowerCase();if("local"===t)return a("localStorage");if("session"===t)return a("sessionStorage");if("cookie"===t)return(0,r.hasCookies)();if("memory"===t)return!0;throw Error("Storage method `"+e+"` is not available.\n Please use one of the following: localStorage, sessionStorage, cookieStorage, memoryStorage.")}},72426(e,t){"use strict";/*! + * cookie + * Copyright(c) 2012-2014 Roman Shtylman + * Copyright(c) 2015 Douglas Christopher Wilson + * MIT Licensed + */ t.parse=o,t.serialize=s;var n=decodeURIComponent,r=encodeURIComponent,i=/; */,a=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;function o(e,t){if("string"!=typeof e)throw TypeError("argument str must be a string");for(var r={},a=t||{},o=e.split(i),s=a.decode||n,c=0;cc});var r=n(56169);e=n.hmd(e);var i="object"==typeof exports&&exports&&!exports.nodeType&&exports,a=i&&e&&!e.nodeType&&e,o=a&&a.exports===i?r.Z.Buffer:void 0,s=o?o.allocUnsafe:void 0;function u(e,t){if(t)return e.slice();var n=e.length,r=s?s(n):new e.constructor(n);return e.copy(r),r}let c=u},48277(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;let i=r},79730(e,t,n){"use strict";n.d(t,{Z:()=>u});var r=n(48277);e=n.hmd(e);var i="object"==typeof exports&&exports&&!exports.nodeType&&exports,a=i&&e&&!e.nodeType&&e,o=a&&a.exports===i&&r.Z.process,s=function(){try{var e=a&&a.require&&a.require("util").types;if(e)return e;return o&&o.binding&&o.binding("util")}catch(t){}}();let u=s},56169(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=n(48277),i="object"==typeof self&&self&&self.Object===Object&&self,a=r.Z||i||Function("return this")();let o=a},29710(e,t,n){"use strict";n.d(t,{Z:()=>l});var r=n(56169);function i(){return!1}let a=i;e=n.hmd(e);var o="object"==typeof exports&&exports&&!exports.nodeType&&exports,s=o&&e&&!e.nodeType&&e,u=s&&s.exports===o?r.Z.Buffer:void 0,c=(u?u.isBuffer:void 0)||a;let l=c},18552(e,t,n){var r=n(10852),i=n(55639),a=r(i,"DataView");e.exports=a},1989(e,t,n){var r=n(51789),i=n(80401),a=n(57667),o=n(21327),s=n(81866);function u(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1}e.exports=i},1196(e){function t(e,t,n){for(var r=-1,i=null==e?0:e.length;++r0&&n(l)?t>1?a(l,t-1,n,o,s):r(s,l):o||(s[s.length]=l)}return s}e.exports=a},28483(e,t,n){var r=n(25063)();e.exports=r},47816(e,t,n){var r=n(28483),i=n(3674);function a(e,t){return e&&r(e,t,i)}e.exports=a},97786(e,t,n){var r=n(71811),i=n(40327);function a(e,t){t=r(t,e);for(var n=0,a=t.length;null!=e&&ni?0:i+t),(n=n>i?i:n)<0&&(n+=i),i=t>n?0:n-t>>>0,t>>>=0;for(var a=Array(i);++r=c){var m=t?null:s(e);if(m)return u(m);h=!1,f=o,b=new r}else b=t?[]:p;outer:for(;++l=i?e:r(e,t,n)}e.exports=i},74318(e,t,n){var r=n(11149);function i(e){var t=new e.constructor(e.byteLength);return new r(t).set(new r(e)),t}e.exports=i},64626(e,t,n){e=n.nmd(e);var r=n(55639),i=t&&!t.nodeType&&t,a=i&&e&&!e.nodeType&&e,o=a&&a.exports===i?r.Buffer:void 0,s=o?o.allocUnsafe:void 0;function u(e,t){if(t)return e.slice();var n=e.length,r=s?s(n):new e.constructor(n);return e.copy(r),r}e.exports=u},57157(e,t,n){var r=n(74318);function i(e,t){var n=t?r(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}e.exports=i},93147(e){var t=/\w*$/;function n(e){var n=new e.constructor(e.source,t.exec(e));return n.lastIndex=e.lastIndex,n}e.exports=n},40419(e,t,n){var r=n(62705),i=r?r.prototype:void 0,a=i?i.valueOf:void 0;function o(e){return a?Object(a.call(e)):{}}e.exports=o},77133(e,t,n){var r=n(74318);function i(e,t){var n=t?r(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}e.exports=i},278(e){function t(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n1?n[a-1]:void 0,s=a>2?n[2]:void 0;for(o=e.length>3&&"function"==typeof o?(a--,o):void 0,s&&i(n[0],n[1],s)&&(o=a<3?void 0:o,a=1),t=Object(t);++rd))return!1;var p=l.get(e),b=l.get(t);if(p&&b)return p==t&&b==e;var m=-1,g=!0,v=n&s?new r:void 0;for(l.set(e,t),l.set(t,e);++m-1&&e%1==0&&e-1}e.exports=i},13399(e,t,n){var r=n(18470);function i(e,t){var n=this.__data__,i=r(n,e);return i<0?(++this.size,n.push([e,t])):n[i][1]=t,this}e.exports=i},24785(e,t,n){var r=n(1989),i=n(38407),a=n(57071);function o(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}e.exports=o},11285(e,t,n){var r=n(45050);function i(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}e.exports=i},96e3(e,t,n){var r=n(45050);function i(e){return r(this,e).get(e)}e.exports=i},49916(e,t,n){var r=n(45050);function i(e){return r(this,e).has(e)}e.exports=i},95265(e,t,n){var r=n(45050);function i(e,t){var n=r(this,e),i=n.size;return n.set(e,t),this.size+=n.size==i?0:1,this}e.exports=i},68776(e){function t(e){var t=-1,n=Array(e.size);return e.forEach(function(e,r){n[++t]=[r,e]}),n}e.exports=t},42634(e){function t(e,t){return function(n){return null!=n&&n[e]===t&&(void 0!==t||e in Object(n))}}e.exports=t},24523(e,t,n){var r=n(88306),i=500;function a(e){var t=r(e,function(e){return n.size===i&&n.clear(),e}),n=t.cache;return t}e.exports=a},94536(e,t,n){var r=n(10852)(Object,"create");e.exports=r},86916(e,t,n){var r=n(5569)(Object.keys,Object);e.exports=r},33498(e){function t(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}e.exports=t},31167(e,t,n){e=n.nmd(e);var r=n(31957),i=t&&!t.nodeType&&t,a=i&&e&&!e.nodeType&&e,o=a&&a.exports===i&&r.process,s=function(){try{var e=a&&a.require&&a.require("util").types;if(e)return e;return o&&o.binding&&o.binding("util")}catch(t){}}();e.exports=s},2333(e){var t=Object.prototype.toString;function n(e){return t.call(e)}e.exports=n},5569(e){function t(e,t){return function(n){return e(t(n))}}e.exports=t},45357(e,t,n){var r=n(96874),i=Math.max;function a(e,t,n){return t=i(void 0===t?e.length-1:t,0),function(){for(var a=arguments,o=-1,s=i(a.length-t,0),u=Array(s);++o0){if(++i>=t)return arguments[0]}else i=0;return e.apply(void 0,arguments)}}e.exports=i},37465(e,t,n){var r=n(38407);function i(){this.__data__=new r,this.size=0}e.exports=i},63779(e){function t(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}e.exports=t},67599(e){function t(e){return this.__data__.get(e)}e.exports=t},44758(e){function t(e){return this.__data__.has(e)}e.exports=t},34309(e,t,n){var r=n(38407),i=n(57071),a=n(83369),o=200;function s(e,t){var n=this.__data__;if(n instanceof r){var s=n.__data__;if(!i||s.length=t||n<0||g&&r>=f}function S(){var e=i();if(E(e))return k(e);h=setTimeout(S,_(e))}function k(e){return(h=void 0,v&&c)?y(e):(c=l=void 0,d)}function x(){void 0!==h&&clearTimeout(h),b=0,c=p=l=h=void 0}function T(){return void 0===h?d:k(i())}function M(){var e=i(),n=E(e);if(c=arguments,l=this,p=e,n){if(void 0===h)return w(p);if(g)return clearTimeout(h),h=setTimeout(S,t),y(p)}return void 0===h&&(h=setTimeout(S,t)),d}return t=a(t)||0,r(n)&&(m=!!n.leading,f=(g="maxWait"in n)?s(a(n.maxWait)||0,t):f,v="trailing"in n?!!n.trailing:v),M.cancel=x,M.flush=T,M}e.exports=c},53816(e,t,n){var r=n(69389),i=n(79833),a=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,o=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");function s(e){return(e=i(e))&&e.replace(a,r).replace(o,"")}e.exports=s},66073(e,t,n){e.exports=n(84486)},77813(e){function t(e,t){return e===t||e!=e&&t!=t}e.exports=t},63105(e,t,n){var r=n(34963),i=n(80760),a=n(67206),o=n(1469);function s(e,t){return(o(e)?r:i)(e,a(t,3))}e.exports=s},85564(e,t,n){var r=n(21078);function i(e){return(null==e?0:e.length)?r(e,1):[]}e.exports=i},84486(e,t,n){var r=n(77412),i=n(89881),a=n(54290),o=n(1469);function s(e,t){return(o(e)?r:i)(e,a(t))}e.exports=s},27361(e,t,n){var r=n(97786);function i(e,t,n){var i=null==e?void 0:r(e,t);return void 0===i?n:i}e.exports=i},18721(e,t,n){var r=n(78565),i=n(222);function a(e,t){return null!=e&&i(e,t,r)}e.exports=a},79095(e,t,n){var r=n(13),i=n(222);function a(e,t){return null!=e&&i(e,t,r)}e.exports=a},6557(e){function t(e){return e}e.exports=t},35694(e,t,n){var r=n(9454),i=n(37005),a=Object.prototype,o=a.hasOwnProperty,s=a.propertyIsEnumerable,u=r(function(){return arguments}())?r:function(e){return i(e)&&o.call(e,"callee")&&!s.call(e,"callee")};e.exports=u},1469(e){var t=Array.isArray;e.exports=t},98612(e,t,n){var r=n(23560),i=n(41780);function a(e){return null!=e&&i(e.length)&&!r(e)}e.exports=a},29246(e,t,n){var r=n(98612),i=n(37005);function a(e){return i(e)&&r(e)}e.exports=a},44144(e,t,n){e=n.nmd(e);var r=n(55639),i=n(95062),a=t&&!t.nodeType&&t,o=a&&e&&!e.nodeType&&e,s=o&&o.exports===a?r.Buffer:void 0,u=(s?s.isBuffer:void 0)||i;e.exports=u},41609(e,t,n){var r=n(280),i=n(64160),a=n(35694),o=n(1469),s=n(98612),u=n(44144),c=n(25726),l=n(36719),f="[object Map]",d="[object Set]",h=Object.prototype.hasOwnProperty;function p(e){if(null==e)return!0;if(s(e)&&(o(e)||"string"==typeof e||"function"==typeof e.splice||u(e)||l(e)||a(e)))return!e.length;var t=i(e);if(t==f||t==d)return!e.size;if(c(e))return!r(e).length;for(var n in e)if(h.call(e,n))return!1;return!0}e.exports=p},23560(e,t,n){var r=n(44239),i=n(13218),a="[object AsyncFunction]",o="[object Function]",s="[object GeneratorFunction]",u="[object Proxy]";function c(e){if(!i(e))return!1;var t=r(e);return t==o||t==s||t==a||t==u}e.exports=c},41780(e){var t=9007199254740991;function n(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=t}e.exports=n},56688(e,t,n){var r=n(25588),i=n(7518),a=n(31167),o=a&&a.isMap,s=o?i(o):r;e.exports=s},45220(e){function t(e){return null===e}e.exports=t},13218(e){function t(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}e.exports=t},37005(e){function t(e){return null!=e&&"object"==typeof e}e.exports=t},68630(e,t,n){var r=n(44239),i=n(85924),a=n(37005),o="[object Object]",s=Function.prototype,u=Object.prototype,c=s.toString,l=u.hasOwnProperty,f=c.call(Object);function d(e){if(!a(e)||r(e)!=o)return!1;var t=i(e);if(null===t)return!0;var n=l.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&c.call(n)==f}e.exports=d},72928(e,t,n){var r=n(29221),i=n(7518),a=n(31167),o=a&&a.isSet,s=o?i(o):r;e.exports=s},47037(e,t,n){var r=n(44239),i=n(1469),a=n(37005),o="[object String]";function s(e){return"string"==typeof e||!i(e)&&a(e)&&r(e)==o}e.exports=s},33448(e,t,n){var r=n(44239),i=n(37005),a="[object Symbol]";function o(e){return"symbol"==typeof e||i(e)&&r(e)==a}e.exports=o},36719(e,t,n){var r=n(38749),i=n(7518),a=n(31167),o=a&&a.isTypedArray,s=o?i(o):r;e.exports=s},52353(e){function t(e){return void 0===e}e.exports=t},3674(e,t,n){var r=n(14636),i=n(280),a=n(98612);function o(e){return a(e)?r(e):i(e)}e.exports=o},81704(e,t,n){var r=n(14636),i=n(35014),a=n(98612);function o(e){return a(e)?r(e,!0):i(e)}e.exports=o},96486:function(e,t,n){var r;e=n.nmd(e),(function(){var i,a="4.17.21",o=200,s="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",u="Expected a function",c="Invalid `variable` option passed into `_.template`",l="__lodash_hash_undefined__",f=500,d="__lodash_placeholder__",h=1,p=2,b=4,m=1,g=2,v=1,y=2,w=4,_=8,E=16,S=32,k=64,x=128,T=256,M=512,O=30,A="...",L=800,C=16,I=1,D=2,N=3,P=1/0,R=9007199254740991,j=17976931348623157e292,F=0/0,Y=4294967295,B=Y-1,U=Y>>>1,H=[["ary",x],["bind",v],["bindKey",y],["curry",_],["curryRight",E],["flip",M],["partial",S],["partialRight",k],["rearg",T]],$="[object Arguments]",z="[object Array]",G="[object AsyncFunction]",W="[object Boolean]",K="[object Date]",V="[object DOMException]",q="[object Error]",Z="[object Function]",X="[object GeneratorFunction]",J="[object Map]",Q="[object Number]",ee="[object Null]",et="[object Object]",en="[object Promise]",er="[object Proxy]",ei="[object RegExp]",ea="[object Set]",eo="[object String]",es="[object Symbol]",eu="[object Undefined]",ec="[object WeakMap]",el="[object WeakSet]",ef="[object ArrayBuffer]",ed="[object DataView]",eh="[object Float32Array]",ep="[object Float64Array]",eb="[object Int8Array]",em="[object Int16Array]",eg="[object Int32Array]",ev="[object Uint8Array]",ey="[object Uint8ClampedArray]",ew="[object Uint16Array]",e_="[object Uint32Array]",eE=/\b__p \+= '';/g,eS=/\b(__p \+=) '' \+/g,ek=/(__e\(.*?\)|\b__t\)) \+\n'';/g,ex=/&(?:amp|lt|gt|quot|#39);/g,eT=/[&<>"']/g,eM=RegExp(ex.source),eO=RegExp(eT.source),eA=/<%-([\s\S]+?)%>/g,eL=/<%([\s\S]+?)%>/g,eC=/<%=([\s\S]+?)%>/g,eI=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,eD=/^\w*$/,eN=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,eP=/[\\^$.*+?()[\]{}|]/g,eR=RegExp(eP.source),ej=/^\s+/,eF=/\s/,eY=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,eB=/\{\n\/\* \[wrapped with (.+)\] \*/,eU=/,? & /,eH=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,e$=/[()=,{}\[\]\/\s]/,ez=/\\(\\)?/g,eG=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,eW=/\w*$/,eK=/^[-+]0x[0-9a-f]+$/i,eV=/^0b[01]+$/i,eq=/^\[object .+?Constructor\]$/,eZ=/^0o[0-7]+$/i,eX=/^(?:0|[1-9]\d*)$/,eJ=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,eQ=/($^)/,e1=/['\n\r\u2028\u2029\\]/g,e0="\ud800-\udfff",e2="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",e3="\\u2700-\\u27bf",e4="a-z\\xdf-\\xf6\\xf8-\\xff",e5="A-Z\\xc0-\\xd6\\xd8-\\xde",e6="\\ufe0e\\ufe0f",e9="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",e8="['’]",e7="["+e0+"]",te="["+e9+"]",tt="["+e2+"]",tn="\\d+",tr="["+e3+"]",ti="["+e4+"]",ta="[^"+e0+e9+tn+e3+e4+e5+"]",to="\ud83c[\udffb-\udfff]",ts="[^"+e0+"]",tu="(?:\ud83c[\udde6-\uddff]){2}",tc="[\ud800-\udbff][\udc00-\udfff]",tl="["+e5+"]",tf="\\u200d",td="(?:"+ti+"|"+ta+")",th="(?:"+tl+"|"+ta+")",tp="(?:"+e8+"(?:d|ll|m|re|s|t|ve))?",tb="(?:"+e8+"(?:D|LL|M|RE|S|T|VE))?",tm="(?:"+tt+"|"+to+")?",tg="["+e6+"]?",tv="(?:"+tf+"(?:"+[ts,tu,tc].join("|")+")"+tg+tm+")*",ty="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",tw="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",t_=tg+tm+tv,tE="(?:"+[tr,tu,tc].join("|")+")"+t_,tS="(?:"+[ts+tt+"?",tt,tu,tc,e7].join("|")+")",tk=RegExp(e8,"g"),tx=RegExp(tt,"g"),tT=RegExp(to+"(?="+to+")|"+tS+t_,"g"),tM=RegExp([tl+"?"+ti+"+"+tp+"(?="+[te,tl,"$"].join("|")+")",th+"+"+tb+"(?="+[te,tl+td,"$"].join("|")+")",tl+"?"+td+"+"+tp,tl+"+"+tb,tw,ty,tn,tE].join("|"),"g"),tO=RegExp("["+tf+e0+e2+e6+"]"),tA=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,tL=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],tC=-1,tI={};tI[eh]=tI[ep]=tI[eb]=tI[em]=tI[eg]=tI[ev]=tI[ey]=tI[ew]=tI[e_]=!0,tI[$]=tI[z]=tI[ef]=tI[W]=tI[ed]=tI[K]=tI[q]=tI[Z]=tI[J]=tI[Q]=tI[et]=tI[ei]=tI[ea]=tI[eo]=tI[ec]=!1;var tD={};tD[$]=tD[z]=tD[ef]=tD[ed]=tD[W]=tD[K]=tD[eh]=tD[ep]=tD[eb]=tD[em]=tD[eg]=tD[J]=tD[Q]=tD[et]=tD[ei]=tD[ea]=tD[eo]=tD[es]=tD[ev]=tD[ey]=tD[ew]=tD[e_]=!0,tD[q]=tD[Z]=tD[ec]=!1;var tN={À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"},tP={"&":"&","<":"<",">":">",'"':""","'":"'"},tR={"&":"&","<":"<",">":">",""":'"',"'":"'"},tj={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},tF=parseFloat,tY=parseInt,tB="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,tU="object"==typeof self&&self&&self.Object===Object&&self,tH=tB||tU||Function("return this")(),t$=t&&!t.nodeType&&t,tz=t$&&e&&!e.nodeType&&e,tG=tz&&tz.exports===t$,tW=tG&&tB.process,tK=function(){try{var e=tz&&tz.require&&tz.require("util").types;if(e)return e;return tW&&tW.binding&&tW.binding("util")}catch(t){}}(),tV=tK&&tK.isArrayBuffer,tq=tK&&tK.isDate,tZ=tK&&tK.isMap,tX=tK&&tK.isRegExp,tJ=tK&&tK.isSet,tQ=tK&&tK.isTypedArray;function t1(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function t0(e,t,n,r){for(var i=-1,a=null==e?0:e.length;++i-1}function t9(e,t,n){for(var r=-1,i=null==e?0:e.length;++r-1;);return n}function nk(e,t){for(var n=e.length;n--&&nu(t,e[n],0)>-1;);return n}function nx(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}var nT=nh(tN),nM=nh(tP);function nO(e){return"\\"+tj[e]}function nA(e,t){return null==e?i:e[t]}function nL(e){return tO.test(e)}function nC(e){return tA.test(e)}function nI(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}function nD(e){var t=-1,n=Array(e.size);return e.forEach(function(e,r){n[++t]=[r,e]}),n}function nN(e,t){return function(n){return e(t(n))}}function nP(e,t){for(var n=-1,r=e.length,i=0,a=[];++n-1}function rh(e,t){var n=this.__data__,r=rP(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}function rp(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function rH(e,t,n,r,a,o){var s,u=t&h,c=t&p,l=t&b;if(n&&(s=a?n(e,r,a,o):n(e)),s!==i)return s;if(!u1(e))return e;var f=uF(e);if(f){if(s=a9(e),!u)return al(e,s)}else{var d=a3(e),m=d==Z||d==X;if(u$(e))return ae(e,u);if(d==et||d==$||m&&!a){if(s=c||m?{}:a8(e),!u)return c?ah(e,rF(s,e)):ad(e,rj(s,e))}else{if(!tD[d])return a?e:{};s=a7(e,d,u)}}o||(o=new rS);var g=o.get(e);if(g)return g;o.set(e,s),cr(e)?e.forEach(function(r){s.add(rH(r,t,n,r,e,o))}):u2(e)&&e.forEach(function(r,i){s.set(i,rH(r,t,n,i,e,o))});var v=l?c?aW:aG:c?c$:cH,y=f?i:v(e);return t2(y||e,function(r,i){y&&(r=e[i=r]),rN(s,i,rH(r,t,n,i,e,o))}),s}function r$(e){var t=cH(e);return function(n){return rz(n,e,t)}}function rz(e,t,n){var r=n.length;if(null==e)return!r;for(e=e4(e);r--;){var a=n[r],o=t[a],s=e[a];if(s===i&&!(a in e)||!o(s))return!1}return!0}function rG(e,t,n){if("function"!=typeof e)throw new e9(u);return o_(function(){e.apply(i,n)},t)}function rW(e,t,n,r){var i=-1,a=t6,s=!0,u=e.length,c=[],l=t.length;if(!u)return c;n&&(t=t8(t,nw(n))),r?(a=t9,s=!1):t.length>=o&&(a=nE,s=!1,t=new rw(t));outer:for(;++ia?0:a+n),(r=r===i||r>a?a:cp(r))<0&&(r+=a),r=n>r?0:cb(r);n0&&n(s)?t>1?rQ(s,t-1,n,r,i):t7(i,s):r||(i[i.length]=s)}return i}var r1=ag(),r0=ag(!0);function r2(e,t){return e&&r1(e,t,cH)}function r3(e,t){return e&&r0(e,t,cH)}function r4(e,t){return t5(t,function(t){return uX(e[t])})}function r5(e,t){t=i6(t,e);for(var n=0,r=t.length;null!=e&&nt}function r7(e,t){return null!=e&&tr.call(e,t)}function ie(e,t){return null!=e&&t in e4(e)}function it(e,t,n){return e>=tU(t,n)&&e=120&&f.length>=120)?new rw(s&&f):i}f=e[0];var d=-1,h=u[0];outer:for(;++d-1;)s!==e&&tg.call(s,u,1),tg.call(e,u,1);return e}function iD(e,t){for(var n=e?t.length:0,r=n-1;n--;){var i=t[n];if(n==r||i!==a){var a=i;on(i)?tg.call(e,i,1):iJ(e,i)}}return e}function iN(e,t){return e+tO(tW()*(t-e+1))}function iP(e,t,n,r){for(var i=-1,a=tB(tM((t-e)/(n||1)),0),o=eF(a);a--;)o[r?a:++i]=e,e+=n;return o}function iR(e,t){var n="";if(!e||t<1||t>R)return n;do t%2&&(n+=e),(t=tO(t/2))&&(e+=e);while(t)return n}function ij(e,t){return oE(om(e,t,lB),e+"")}function iF(e){return rL(c9(e))}function iY(e,t){var n=c9(e);return ox(n,rU(t,0,n.length))}function iB(e,t,n,r){if(!u1(e))return e;t=i6(t,e);for(var a=-1,o=t.length,s=o-1,u=e;null!=u&&++ai?0:i+t),(n=n>i?i:n)<0&&(n+=i),i=t>n?0:n-t>>>0,t>>>=0;for(var a=eF(i);++r>>1,o=e[a];null!==o&&!ca(o)&&(n?o<=t:o=o){var l=t?null:aP(e);if(l)return nR(l);s=!1,i=nE,c=new rw}else c=t?[]:u;outer:for(;++r=r?e:iz(e,t,n)}var i7=tE||function(e){return tH.clearTimeout(e)};function ae(e,t){if(t)return e.slice();var n=e.length,r=th?th(n):new e.constructor(n);return e.copy(r),r}function at(e){var t=new e.constructor(e.byteLength);return new td(t).set(new td(e)),t}function an(e,t){var n=t?at(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}function ar(e){var t=new e.constructor(e.source,eW.exec(e));return t.lastIndex=e.lastIndex,t}function ai(e){return n2?e4(n2.call(e)):{}}function aa(e,t){var n=t?at(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function ao(e,t){if(e!==t){var n=e!==i,r=null===e,a=e==e,o=ca(e),s=t!==i,u=null===t,c=t==t,l=ca(t);if(!u&&!l&&!o&&e>t||o&&s&&c&&!u&&!l||r&&s&&c||!n&&c||!a)return 1;if(!r&&!o&&!l&&e=s)return u;return u*("desc"==n[r]?-1:1)}}return e.index-t.index}function au(e,t,n,r){for(var i=-1,a=e.length,o=n.length,s=-1,u=t.length,c=tB(a-o,0),l=eF(u+c),f=!r;++s1?n[a-1]:i,s=a>2?n[2]:i;for(o=e.length>3&&"function"==typeof o?(a--,o):i,s&&or(n[0],n[1],s)&&(o=a<3?i:o,a=1),t=e4(t);++r-1?a[o?t[s]:s]:i}}function ak(e){return az(function(t){var n=t.length,r=n,a=n9.prototype.thru;for(e&&t.reverse();r--;){var o=t[r];if("function"!=typeof o)throw new e9(u);if(a&&!s&&"wrapper"==aV(o))var s=new n9([],!0)}for(r=s?r:n;++r1&&v.reverse(),f&&cu))return!1;var l=o.get(e),f=o.get(t);if(l&&f)return l==t&&f==e;var d=-1,h=!0,p=n&g?new rw:i;for(o.set(e,t),o.set(t,e);++d1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(eY,"{\n/* [wrapped with "+t+"] */\n")}function ot(e){return uF(e)||uj(e)||!!(tv&&e&&e[tv])}function on(e,t){var n=typeof e;return!!(t=null==t?R:t)&&("number"==n||"symbol"!=n&&eX.test(e))&&e>-1&&e%1==0&&e0){if(++t>=L)return arguments[0]}else t=0;return e.apply(i,arguments)}}function ox(e,t){var n=-1,r=e.length,a=r-1;for(t=t===i?r:t;++n1?e[t-1]:i;return n="function"==typeof n?(e.pop(),n):i,sE(e,n)});function sC(e){var t=n4(e);return t.__chain__=!0,t}function sI(e,t){return t(e),e}function sD(e,t){return t(e)}var sN=az(function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,a=function(t){return rB(t,e)};return!(t>1)&&!this.__actions__.length&&r instanceof n8&&on(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:sD,args:[a],thisArg:i}),new n9(r,this.__chain__).thru(function(e){return t&&!e.length&&e.push(i),e})):this.thru(a)});function sP(){return sC(this)}function sR(){return new n9(this.value(),this.__chain__)}function sj(){i===this.__values__&&(this.__values__=cd(this.value()));var e=this.__index__>=this.__values__.length,t=e?i:this.__values__[this.__index__++];return{done:e,value:t}}function sF(){return this}function sY(e){for(var t,n=this;n instanceof n6;){var r=oL(n);r.__index__=0,r.__values__=i,t?a.__wrapped__=r:t=r;var a=r;n=n.__wrapped__}return a.__wrapped__=e,t}function sB(){var e=this.__wrapped__;if(e instanceof n8){var t=e;return this.__actions__.length&&(t=new n8(this)),(t=t.reverse()).__actions__.push({func:sD,args:[se],thisArg:i}),new n9(t,this.__chain__)}return this.thru(se)}function sU(){return i0(this.__wrapped__,this.__actions__)}var sH=ap(function(e,t,n){tr.call(e,n)?++e[n]:rY(e,n,1)});function s$(e,t,n){var r=uF(e)?t4:rq;return n&&or(e,t,n)&&(t=i),r(e,aZ(t,3))}function sz(e,t){return(uF(e)?t5:rJ)(e,aZ(t,3))}var sG=aS(oH),sW=aS(o$);function sK(e,t){return rQ(s2(e,t),1)}function sV(e,t){return rQ(s2(e,t),P)}function sq(e,t,n){return n=n===i?1:cp(n),rQ(s2(e,t),n)}function sZ(e,t){return(uF(e)?t2:rK)(e,aZ(t,3))}function sX(e,t){return(uF(e)?t3:rV)(e,aZ(t,3))}var sJ=ap(function(e,t,n){tr.call(e,n)?e[n].push(t):rY(e,n,[t])});function sQ(e,t,n,r){e=uB(e)?e:c9(e),n=n&&!r?cp(n):0;var i=e.length;return n<0&&(n=tB(i+n,0)),ci(e)?n<=i&&e.indexOf(t,n)>-1:!!i&&nu(e,t,n)>-1}var s1=ij(function(e,t,n){var r=-1,i="function"==typeof t,a=uB(e)?eF(e.length):[];return rK(e,function(e){a[++r]=i?t1(t,e,n):ia(e,t,n)}),a}),s0=ap(function(e,t,n){rY(e,n,t)});function s2(e,t){return(uF(e)?t8:iE)(e,aZ(t,3))}function s3(e,t,n,r){return null==e?[]:(uF(t)||(t=null==t?[]:[t]),n=r?i:n,uF(n)||(n=null==n?[]:[n]),iO(e,t,n))}var s4=ap(function(e,t,n){e[n?0:1].push(t)},function(){return[[],[]]});function s5(e,t,n){var r=uF(e)?ne:np,i=arguments.length<3;return r(e,aZ(t,4),n,i,rK)}function s6(e,t,n){var r=uF(e)?nt:np,i=arguments.length<3;return r(e,aZ(t,4),n,i,rV)}function s9(e,t){return(uF(e)?t5:rJ)(e,ug(aZ(t,3)))}function s8(e){return(uF(e)?rL:iF)(e)}function s7(e,t,n){return t=(n?or(e,t,n):t===i)?1:cp(t),(uF(e)?rC:iY)(e,t)}function ue(e){return(uF(e)?rI:i$)(e)}function ut(e){if(null==e)return 0;if(uB(e))return ci(e)?nB(e):e.length;var t=a3(e);return t==J||t==ea?e.size:iy(e).length}function un(e,t,n){var r=uF(e)?nn:iG;return n&&or(e,t,n)&&(t=i),r(e,aZ(t,3))}var ur=ij(function(e,t){if(null==e)return[];var n=t.length;return n>1&&or(e,t[0],t[1])?t=[]:n>2&&or(t[0],t[1],t[2])&&(t=[t[0]]),iO(e,rQ(t,1),[])}),ui=tS||function(){return tH.Date.now()};function ua(e,t){if("function"!=typeof t)throw new e9(u);return e=cp(e),function(){if(--e<1)return t.apply(this,arguments)}}function uo(e,t,n){return t=n?i:t,t=e&&null==t?e.length:t,aj(e,x,i,i,i,i,t)}function us(e,t){var n;if("function"!=typeof t)throw new e9(u);return e=cp(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=i),n}}var uu=ij(function(e,t,n){var r=v;if(n.length){var i=nP(n,aq(uu));r|=S}return aj(e,r,t,n,i)}),uc=ij(function(e,t,n){var r=v|y;if(n.length){var i=nP(n,aq(uc));r|=S}return aj(t,r,e,n,i)});function ul(e,t,n){t=n?i:t;var r=aj(e,_,i,i,i,i,i,t);return r.placeholder=ul.placeholder,r}function uf(e,t,n){t=n?i:t;var r=aj(e,E,i,i,i,i,i,t);return r.placeholder=uf.placeholder,r}function ud(e,t,n){var r,a,o,s,c,l,f=0,d=!1,h=!1,p=!0;if("function"!=typeof e)throw new e9(u);function b(t){var n=r,o=a;return r=a=i,f=t,s=e.apply(o,n)}function m(e){return f=e,c=o_(y,t),d?b(e):s}function g(e){var n=e-l,r=e-f,i=t-n;return h?tU(i,o-r):i}function v(e){var n=e-l,r=e-f;return l===i||n>=t||n<0||h&&r>=o}function y(){var e=ui();if(v(e))return w(e);c=o_(y,g(e))}function w(e){return(c=i,p&&r)?b(e):(r=a=i,s)}function _(){c!==i&&i7(c),f=0,r=l=a=c=i}function E(){return c===i?s:w(ui())}function S(){var e=ui(),n=v(e);if(r=arguments,a=this,l=e,n){if(c===i)return m(l);if(h)return i7(c),c=o_(y,t),b(l)}return c===i&&(c=o_(y,t)),s}return t=cm(t)||0,u1(n)&&(d=!!n.leading,o=(h="maxWait"in n)?tB(cm(n.maxWait)||0,t):o,p="trailing"in n?!!n.trailing:p),S.cancel=_,S.flush=E,S}var uh=ij(function(e,t){return rG(e,1,t)}),up=ij(function(e,t,n){return rG(e,cm(t)||0,n)});function ub(e){return aj(e,M)}function um(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new e9(u);var n=function(){var r=arguments,i=t?t.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=e.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(um.Cache||rp),n}function ug(e){if("function"!=typeof e)throw new e9(u);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}function uv(e){return us(2,e)}um.Cache=rp;var uy=i9(function(e,t){var n=(t=1==t.length&&uF(t[0])?t8(t[0],nw(aZ())):t8(rQ(t,1),nw(aZ()))).length;return ij(function(r){for(var i=-1,a=tU(r.length,n);++i=t}),uj=io(function(){return arguments}())?io:function(e){return u0(e)&&tr.call(e,"callee")&&!tm.call(e,"callee")},uF=eF.isArray,uY=tV?nw(tV):is;function uB(e){return null!=e&&uQ(e.length)&&!uX(e)}function uU(e){return u0(e)&&uB(e)}function uH(e){return!0===e||!1===e||u0(e)&&r9(e)==W}var u$=tN||l4,uz=tq?nw(tq):iu;function uG(e){return u0(e)&&1===e.nodeType&&!ce(e)}function uW(e){if(null==e)return!0;if(uB(e)&&(uF(e)||"string"==typeof e||"function"==typeof e.splice||u$(e)||co(e)||uj(e)))return!e.length;var t=a3(e);if(t==J||t==ea)return!e.size;if(oc(e))return!iy(e).length;for(var n in e)if(tr.call(e,n))return!1;return!0}function uK(e,t){return ic(e,t)}function uV(e,t,n){var r=(n="function"==typeof n?n:i)?n(e,t):i;return r===i?ic(e,t,i,n):!!r}function uq(e){if(!u0(e))return!1;var t=r9(e);return t==q||t==V||"string"==typeof e.message&&"string"==typeof e.name&&!ce(e)}function uZ(e){return"number"==typeof e&&tP(e)}function uX(e){if(!u1(e))return!1;var t=r9(e);return t==Z||t==X||t==G||t==er}function uJ(e){return"number"==typeof e&&e==cp(e)}function uQ(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=R}function u1(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function u0(e){return null!=e&&"object"==typeof e}var u2=tZ?nw(tZ):id;function u3(e,t){return e===t||ih(e,t,aJ(t))}function u4(e,t,n){return n="function"==typeof n?n:i,ih(e,t,aJ(t),n)}function u5(e){return u7(e)&&e!=+e}function u6(e){if(ou(e))throw new e0(s);return ip(e)}function u9(e){return null===e}function u8(e){return null==e}function u7(e){return"number"==typeof e||u0(e)&&r9(e)==Q}function ce(e){if(!u0(e)||r9(e)!=et)return!1;var t=tp(e);if(null===t)return!0;var n=tr.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&tn.call(n)==ts}var ct=tX?nw(tX):ib;function cn(e){return uJ(e)&&e>=-R&&e<=R}var cr=tJ?nw(tJ):im;function ci(e){return"string"==typeof e||!uF(e)&&u0(e)&&r9(e)==eo}function ca(e){return"symbol"==typeof e||u0(e)&&r9(e)==es}var co=tQ?nw(tQ):ig;function cs(e){return e===i}function cu(e){return u0(e)&&a3(e)==ec}function cc(e){return u0(e)&&r9(e)==el}var cl=aI(i_),cf=aI(function(e,t){return e<=t});function cd(e){if(!e)return[];if(uB(e))return ci(e)?nU(e):al(e);if(ty&&e[ty])return nI(e[ty]());var t=a3(e);return(t==J?nD:t==ea?nR:c9)(e)}function ch(e){return e?(e=cm(e))===P||e===-P?(e<0?-1:1)*j:e==e?e:0:0===e?e:0}function cp(e){var t=ch(e),n=t%1;return t==t?n?t-n:t:0}function cb(e){return e?rU(cp(e),0,Y):0}function cm(e){if("number"==typeof e)return e;if(ca(e))return F;if(u1(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=u1(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=ny(e);var n=eV.test(e);return n||eZ.test(e)?tY(e.slice(2),n?2:8):eK.test(e)?F:+e}function cg(e){return af(e,c$(e))}function cv(e){return e?rU(cp(e),-R,R):0===e?e:0}function cy(e){return null==e?"":iZ(e)}var cw=ab(function(e,t){if(oc(t)||uB(t)){af(t,cH(t),e);return}for(var n in t)tr.call(t,n)&&rN(e,n,t[n])}),c_=ab(function(e,t){af(t,c$(t),e)}),cE=ab(function(e,t,n,r){af(t,c$(t),e,r)}),cS=ab(function(e,t,n,r){af(t,cH(t),e,r)}),ck=az(rB);function cx(e,t){var n=n5(e);return null==t?n:rj(n,t)}var cT=ij(function(e,t){e=e4(e);var n=-1,r=t.length,a=r>2?t[2]:i;for(a&&or(t[0],t[1],a)&&(r=1);++n1),t}),af(e,aW(e),n),r&&(n=rH(n,h|p|b,aB));for(var i=t.length;i--;)iJ(n,t[i]);return n});function cq(e,t){return cX(e,ug(aZ(t)))}var cZ=az(function(e,t){return null==e?{}:iA(e,t)});function cX(e,t){if(null==e)return{};var n=t8(aW(e),function(e){return[e]});return t=aZ(t),iL(e,n,function(e,n){return t(e,n[0])})}function cJ(e,t,n){t=i6(t,e);var r=-1,a=t.length;for(a||(a=1,e=i);++rt){var r=e;e=t,t=r}if(n||e%1||t%1){var a=tW();return tU(e+a*(t-e+tF("1e-"+((a+"").length-1))),t)}return iN(e,t)}var ln=aw(function(e,t,n){return t=t.toLowerCase(),e+(n?lr(t):t)});function lr(e){return lL(cy(e).toLowerCase())}function li(e){return(e=cy(e))&&e.replace(eJ,nT).replace(tx,"")}function la(e,t,n){e=cy(e),t=iZ(t);var r=e.length,a=n=n===i?r:rU(cp(n),0,r);return(n-=t.length)>=0&&e.slice(n,a)==t}function lo(e){return(e=cy(e))&&eO.test(e)?e.replace(eT,nM):e}function ls(e){return(e=cy(e))&&eR.test(e)?e.replace(eP,"\\$&"):e}var lu=aw(function(e,t,n){return e+(n?"-":"")+t.toLowerCase()}),lc=aw(function(e,t,n){return e+(n?" ":"")+t.toLowerCase()}),ll=ay("toLowerCase");function lf(e,t,n){e=cy(e);var r=(t=cp(t))?nB(e):0;if(!t||r>=t)return e;var i=(t-r)/2;return aA(tO(i),n)+e+aA(tM(i),n)}function ld(e,t,n){e=cy(e);var r=(t=cp(t))?nB(e):0;return t&&r>>0)?(e=cy(e))&&("string"==typeof t||null!=t&&!ct(t))&&!(t=iZ(t))&&nL(e)?i8(nU(e),0,n):e.split(t,n):[]}var ly=aw(function(e,t,n){return e+(n?" ":"")+lL(t)});function lw(e,t,n){return e=cy(e),n=null==n?0:rU(cp(n),0,e.length),t=iZ(t),e.slice(n,n+t.length)==t}function l_(e,t,n){var r=n4.templateSettings;n&&or(e,t,n)&&(t=i),e=cy(e),t=cE({},t,r,aF);var a,o,s=cE({},t.imports,r.imports,aF),u=cH(s),l=n_(s,u),f=0,d=t.interpolate||eQ,h="__p += '",p=e5((t.escape||eQ).source+"|"+d.source+"|"+(d===eC?eG:eQ).source+"|"+(t.evaluate||eQ).source+"|$","g"),b="//# sourceURL="+(tr.call(t,"sourceURL")?(t.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++tC+"]")+"\n";e.replace(p,function(t,n,r,i,s,u){return r||(r=i),h+=e.slice(f,u).replace(e1,nO),n&&(a=!0,h+="' +\n__e("+n+") +\n'"),s&&(o=!0,h+="';\n"+s+";\n__p += '"),r&&(h+="' +\n((__t = ("+r+")) == null ? '' : __t) +\n'"),f=u+t.length,t}),h+="';\n";var m=tr.call(t,"variable")&&t.variable;if(m){if(e$.test(m))throw new e0(c)}else h="with (obj) {\n"+h+"\n}\n";h=(o?h.replace(eE,""):h).replace(eS,"$1").replace(ek,"$1;"),h="function("+(m||"obj")+") {\n"+(m?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(a?", __e = _.escape":"")+(o?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+h+"return __p\n}";var g=lI(function(){return e2(u,b+"return "+h).apply(i,l)});if(g.source=h,uq(g))throw g;return g}function lE(e){return cy(e).toLowerCase()}function lS(e){return cy(e).toUpperCase()}function lk(e,t,n){if((e=cy(e))&&(n||t===i))return ny(e);if(!e||!(t=iZ(t)))return e;var r=nU(e),a=nU(t),o=nS(r,a),s=nk(r,a)+1;return i8(r,o,s).join("")}function lx(e,t,n){if((e=cy(e))&&(n||t===i))return e.slice(0,nH(e)+1);if(!e||!(t=iZ(t)))return e;var r=nU(e),a=nk(r,nU(t))+1;return i8(r,0,a).join("")}function lT(e,t,n){if((e=cy(e))&&(n||t===i))return e.replace(ej,"");if(!e||!(t=iZ(t)))return e;var r=nU(e),a=nS(r,nU(t));return i8(r,a).join("")}function lM(e,t){var n=O,r=A;if(u1(t)){var a="separator"in t?t.separator:a;n="length"in t?cp(t.length):n,r="omission"in t?iZ(t.omission):r}var o=(e=cy(e)).length;if(nL(e)){var s=nU(e);o=s.length}if(n>=o)return e;var u=n-nB(r);if(u<1)return r;var c=s?i8(s,0,u).join(""):e.slice(0,u);if(a===i)return c+r;if(s&&(u+=c.length-u),ct(a)){if(e.slice(u).search(a)){var l,f=c;for(a.global||(a=e5(a.source,cy(eW.exec(a))+"g")),a.lastIndex=0;l=a.exec(f);)var d=l.index;c=c.slice(0,d===i?u:d)}}else if(e.indexOf(iZ(a),u)!=u){var h=c.lastIndexOf(a);h>-1&&(c=c.slice(0,h))}return c+r}function lO(e){return(e=cy(e))&&eM.test(e)?e.replace(ex,n$):e}var lA=aw(function(e,t,n){return e+(n?" ":"")+t.toUpperCase()}),lL=ay("toUpperCase");function lC(e,t,n){return(e=cy(e),i===(t=n?i:t))?nC(e)?nW(e):na(e):e.match(t)||[]}var lI=ij(function(e,t){try{return t1(e,i,t)}catch(n){return uq(n)?n:new e0(n)}}),lD=az(function(e,t){return t2(t,function(t){t=oM(t),rY(e,t,uu(e[t],e))}),e});function lN(e){var t=null==e?0:e.length,n=aZ();return e=t?t8(e,function(e){if("function"!=typeof e[1])throw new e9(u);return[n(e[0]),e[1]]}):[],ij(function(n){for(var r=-1;++rR)return[];var n=Y,r=tU(e,Y);t=aZ(t),e-=Y;for(var i=ng(r,t);++n0||t<0)?new n8(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),t!==i&&(n=(t=cp(t))<0?n.dropRight(-t):n.take(t-e)),n)},n8.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},n8.prototype.toArray=function(){return this.take(Y)},r2(n8.prototype,function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),a=n4[r?"take"+("last"==t?"Right":""):t],o=r||/^find/.test(t);a&&(n4.prototype[t]=function(){var t=this.__wrapped__,s=r?[1]:arguments,u=t instanceof n8,c=s[0],l=u||uF(t),f=function(e){var t=a.apply(n4,t7([e],s));return r&&d?t[0]:t};l&&n&&"function"==typeof c&&1!=c.length&&(u=l=!1);var d=this.__chain__,h=!!this.__actions__.length,p=o&&!d,b=u&&!h;if(!o&&l){t=b?t:new n8(this);var m=e.apply(t,s);return m.__actions__.push({func:sD,args:[f],thisArg:i}),new n9(m,d)}return p&&b?e.apply(this,s):(m=this.thru(f),p?r?m.value()[0]:m.value():m)})}),t2(["pop","push","shift","sort","splice","unshift"],function(e){var t=e8[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);n4.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var i=this.value();return t.apply(uF(i)?i:[],e)}return this[n](function(n){return t.apply(uF(n)?n:[],e)})}}),r2(n8.prototype,function(e,t){var n=n4[t];if(n){var r=n.name+"";tr.call(nq,r)||(nq[r]=[]),nq[r].push({name:t,func:n})}}),nq[ax(i,y).name]=[{name:"wrapper",func:i}],n8.prototype.clone=n7,n8.prototype.reverse=re,n8.prototype.value=rt,n4.prototype.at=sN,n4.prototype.chain=sP,n4.prototype.commit=sR,n4.prototype.next=sj,n4.prototype.plant=sY,n4.prototype.reverse=sB,n4.prototype.toJSON=n4.prototype.valueOf=n4.prototype.value=sU,n4.prototype.first=n4.prototype.head,ty&&(n4.prototype[ty]=sF),n4}();tH._=nK,i!==(r=(function(){return nK}).call(t,n,t,e))&&(e.exports=r)}).call(this)},35161(e,t,n){var r=n(29932),i=n(67206),a=n(69199),o=n(1469);function s(e,t){return(o(e)?r:a)(e,i(t,3))}e.exports=s},67523(e,t,n){var r=n(89465),i=n(47816),a=n(67206);function o(e,t){var n={};return t=a(t,3),i(e,function(e,i,a){r(n,t(e,i,a),e)}),n}e.exports=o},66604(e,t,n){var r=n(89465),i=n(47816),a=n(67206);function o(e,t){var n={};return t=a(t,3),i(e,function(e,i,a){r(n,i,t(e,i,a))}),n}e.exports=o},88306(e,t,n){var r=n(83369),i="Expected a function";function a(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw TypeError(i);var n=function(){var r=arguments,i=t?t.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=e.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(a.Cache||r),n}a.Cache=r,e.exports=a},82492(e,t,n){var r=n(42980),i=n(21463)(function(e,t,n){r(e,t,n)});e.exports=i},50308(e){function t(){}e.exports=t},7771(e,t,n){var r=n(55639),i=function(){return r.Date.now()};e.exports=i},78718(e,t,n){var r=n(25970),i=n(99021)(function(e,t){return null==e?{}:r(e,t)});e.exports=i},39601(e,t,n){var r=n(40371),i=n(79152),a=n(15403),o=n(40327);function s(e){return a(e)?r(o(e)):i(e)}e.exports=s},54061(e,t,n){var r=n(62663),i=n(89881),a=n(67206),o=n(10107),s=n(1469);function u(e,t,n){var u=s(e)?r:o,c=arguments.length<3;return u(e,a(t,4),n,c,i)}e.exports=u},84238(e,t,n){var r=n(280),i=n(64160),a=n(98612),o=n(47037),s=n(88016),u="[object Map]",c="[object Set]";function l(e){if(null==e)return 0;if(a(e))return o(e)?s(e):e.length;var t=i(e);return t==u||t==c?e.size:r(e).length}e.exports=l},11865(e,t,n){var r=n(35393)(function(e,t,n){return e+(n?"_":"")+t.toLowerCase()});e.exports=r},70479(e){function t(){return[]}e.exports=t},95062(e){function t(){return!1}e.exports=t},14841(e,t,n){var r=n(27561),i=n(13218),a=n(33448),o=0/0,s=/^[-+]0x[0-9a-f]+$/i,u=/^0b[01]+$/i,c=/^0o[0-7]+$/i,l=parseInt;function f(e){if("number"==typeof e)return e;if(a(e))return o;if(i(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=i(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var n=u.test(e);return n||c.test(e)?l(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=f},59881(e,t,n){var r=n(98363),i=n(81704);function a(e){return r(e,i(e))}e.exports=a},79833(e,t,n){var r=n(80531);function i(e){return null==e?"":r(e)}e.exports=i},68718(e,t,n){var r=n(77412),i=n(3118),a=n(47816),o=n(67206),s=n(85924),u=n(1469),c=n(44144),l=n(23560),f=n(13218),d=n(36719);function h(e,t,n){var h=u(e),p=h||c(e)||d(e);if(t=o(t,4),null==n){var b=e&&e.constructor;n=p?h?new b:[]:f(e)&&l(b)?i(s(e)):{}}return(p?r:a)(e,function(e,r,i){return t(n,e,r,i)}),n}e.exports=h},93386(e,t,n){var r=n(21078),i=n(5976),a=n(45652),o=n(29246),s=i(function(e){return a(r(e,1,o,!0))});e.exports=s},11700(e,t,n){var r=n(98805)("toUpperCase");e.exports=r},52628(e,t,n){var r=n(47415),i=n(3674);function a(e){return null==e?[]:r(e,i(e))}e.exports=a},58748(e,t,n){var r=n(49029),i=n(93157),a=n(79833),o=n(2757);function s(e,t,n){return(e=a(e),void 0===(t=n?void 0:t))?i(e)?o(e):r(e):e.match(t)||[]}e.exports=s},42786:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("af",{months:"Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des".split("_"),weekdays:"Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag".split("_"),weekdaysShort:"Son_Maa_Din_Woe_Don_Vry_Sat".split("_"),weekdaysMin:"So_Ma_Di_Wo_Do_Vr_Sa".split("_"),meridiemParse:/vm|nm/i,isPM:function(e){return/^nm$/i.test(e)},meridiem:function(e,t,n){return e<12?n?"vm":"VM":n?"nm":"NM"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Vandag om] LT",nextDay:"[M\xf4re om] LT",nextWeek:"dddd [om] LT",lastDay:"[Gister om] LT",lastWeek:"[Laas] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oor %s",past:"%s gelede",s:"'n paar sekondes",ss:"%d sekondes",m:"'n minuut",mm:"%d minute",h:"'n uur",hh:"%d ure",d:"'n dag",dd:"%d dae",M:"'n maand",MM:"%d maande",y:"'n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})})(n(30381))},14130:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},n={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية",],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة",],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة",],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم",],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر",],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام",]},r=function(e){return function(r,i,a,o){var s=t(r),u=n[e][t(r)];return 2===s&&(u=u[i?0:1]),u.replace(/%d/i,r)}},i=["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويلية","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر",];return e.defineLocale("ar-dz",{months:i,monthsShort:i,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:r("s"),ss:r("s"),m:r("m"),mm:r("m"),h:r("h"),hh:r("h"),d:r("d"),dd:r("d"),M:r("M"),MM:r("M"),y:r("y"),yy:r("y")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:0,doy:4}})})(n(30381))},96135:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ar-kw",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:0,doy:12}})})(n(30381))},56440:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9",0:"0"},n=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},r={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية",],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة",],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة",],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم",],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر",],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام",]},i=function(e){return function(t,i,a,o){var s=n(t),u=r[e][n(t)];return 2===s&&(u=u[i?0:1]),u.replace(/%d/i,t)}},a=["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر",];return e.defineLocale("ar-ly",{months:a,monthsShort:a,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:i("s"),ss:i("s"),m:i("m"),mm:i("m"),h:i("h"),hh:i("h"),d:i("d"),dd:i("d"),M:i("M"),MM:i("M"),y:i("y"),yy:i("y")},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}})})(n(30381))},47702:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ar-ma",{months:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}})})(n(30381))},16040:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"};return e.defineLocale("ar-sa",{months:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"،")},week:{dow:0,doy:6}})})(n(30381))},37100:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ar-tn",{months:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),monthsShort:"جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر".split("_"),weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[اليوم على الساعة] LT",nextDay:"[غدا على الساعة] LT",nextWeek:"dddd [على الساعة] LT",lastDay:"[أمس على الساعة] LT",lastWeek:"dddd [على الساعة] LT",sameElse:"L"},relativeTime:{future:"في %s",past:"منذ %s",s:"ثوان",ss:"%d ثانية",m:"دقيقة",mm:"%d دقائق",h:"ساعة",hh:"%d ساعات",d:"يوم",dd:"%d أيام",M:"شهر",MM:"%d أشهر",y:"سنة",yy:"%d سنوات"},week:{dow:1,doy:4}})})(n(30381))},30867:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},r=function(e){return 0===e?0:1===e?1:2===e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5},i={s:["أقل من ثانية","ثانية واحدة",["ثانيتان","ثانيتين"],"%d ثوان","%d ثانية","%d ثانية",],m:["أقل من دقيقة","دقيقة واحدة",["دقيقتان","دقيقتين"],"%d دقائق","%d دقيقة","%d دقيقة",],h:["أقل من ساعة","ساعة واحدة",["ساعتان","ساعتين"],"%d ساعات","%d ساعة","%d ساعة",],d:["أقل من يوم","يوم واحد",["يومان","يومين"],"%d أيام","%d يومًا","%d يوم",],M:["أقل من شهر","شهر واحد",["شهران","شهرين"],"%d أشهر","%d شهرا","%d شهر",],y:["أقل من عام","عام واحد",["عامان","عامين"],"%d أعوام","%d عامًا","%d عام",]},a=function(e){return function(t,n,a,o){var s=r(t),u=i[e][r(t)];return 2===s&&(u=u[n?0:1]),u.replace(/%d/i,t)}},o=["يناير","فبراير","مارس","أبريل","مايو","يونيو","يوليو","أغسطس","سبتمبر","أكتوبر","نوفمبر","ديسمبر",];return e.defineLocale("ar",{months:o,monthsShort:o,weekdays:"الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت".split("_"),weekdaysShort:"أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت".split("_"),weekdaysMin:"ح_ن_ث_ر_خ_ج_س".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/‏M/‏YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/ص|م/,isPM:function(e){return"م"===e},meridiem:function(e,t,n){return e<12?"ص":"م"},calendar:{sameDay:"[اليوم عند الساعة] LT",nextDay:"[غدًا عند الساعة] LT",nextWeek:"dddd [عند الساعة] LT",lastDay:"[أمس عند الساعة] LT",lastWeek:"dddd [عند الساعة] LT",sameElse:"L"},relativeTime:{future:"بعد %s",past:"منذ %s",s:a("s"),ss:a("s"),m:a("m"),mm:a("m"),h:a("h"),hh:a("h"),d:a("d"),dd:a("d"),M:a("M"),MM:a("M"),y:a("y"),yy:a("y")},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}})})(n(30381))},31083:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"-inci",5:"-inci",8:"-inci",70:"-inci",80:"-inci",2:"-nci",7:"-nci",20:"-nci",50:"-nci",3:"-\xfcnc\xfc",4:"-\xfcnc\xfc",100:"-\xfcnc\xfc",6:"-ncı",9:"-uncu",10:"-uncu",30:"-uncu",60:"-ıncı",90:"-ıncı"};return e.defineLocale("az",{months:"yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr".split("_"),monthsShort:"yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek".split("_"),weekdays:"Bazar_Bazar ertəsi_\xc7ərşənbə axşamı_\xc7ərşənbə_C\xfcmə axşamı_C\xfcmə_Şənbə".split("_"),weekdaysShort:"Baz_BzE_\xc7Ax_\xc7ər_CAx_C\xfcm_Şən".split("_"),weekdaysMin:"Bz_BE_\xc7A_\xc7ə_CA_C\xfc_Şə".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bug\xfcn saat] LT",nextDay:"[sabah saat] LT",nextWeek:"[gələn həftə] dddd [saat] LT",lastDay:"[d\xfcnən] LT",lastWeek:"[ke\xe7ən həftə] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s əvvəl",s:"bir ne\xe7ə saniyə",ss:"%d saniyə",m:"bir dəqiqə",mm:"%d dəqiqə",h:"bir saat",hh:"%d saat",d:"bir g\xfcn",dd:"%d g\xfcn",M:"bir ay",MM:"%d ay",y:"bir il",yy:"%d il"},meridiemParse:/gecə|səhər|gündüz|axşam/,isPM:function(e){return/^(gündüz|axşam)$/.test(e)},meridiem:function(e,t,n){return e<4?"gecə":e<12?"səhər":e<17?"g\xfcnd\xfcz":"axşam"},dayOfMonthOrdinalParse:/\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/,ordinal:function(e){if(0===e)return e+"-ıncı";var n=e%10,r=e%100-n,i=e>=100?100:null;return e+(t[n]||t[r]||t[i])},week:{dow:1,doy:7}})})(n(30381))},9808:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t){var n=e.split("_");return t%10==1&&t%100!=11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function n(e,n,r){var i={ss:n?"секунда_секунды_секунд":"секунду_секунды_секунд",mm:n?"хвіліна_хвіліны_хвілін":"хвіліну_хвіліны_хвілін",hh:n?"гадзіна_гадзіны_гадзін":"гадзіну_гадзіны_гадзін",dd:"дзень_дні_дзён",MM:"месяц_месяцы_месяцаў",yy:"год_гады_гадоў"};return"m"===r?n?"хвіліна":"хвіліну":"h"===r?n?"гадзіна":"гадзіну":e+" "+t(i[r],+e)}return e.defineLocale("be",{months:{format:"студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня".split("_"),standalone:"студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань".split("_")},monthsShort:"студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж".split("_"),weekdays:{format:"нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу".split("_"),standalone:"нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота".split("_"),isFormat:/\[ ?[Ууў] ?(?:мінулую|наступную)? ?\] ?dddd/},weekdaysShort:"нд_пн_ат_ср_чц_пт_сб".split("_"),weekdaysMin:"нд_пн_ат_ср_чц_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., HH:mm",LLLL:"dddd, D MMMM YYYY г., HH:mm"},calendar:{sameDay:"[Сёння ў] LT",nextDay:"[Заўтра ў] LT",lastDay:"[Учора ў] LT",nextWeek:function(){return"[У] dddd [ў] LT"},lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return"[У мінулую] dddd [ў] LT";case 1:case 2:case 4:return"[У мінулы] dddd [ў] LT"}},sameElse:"L"},relativeTime:{future:"праз %s",past:"%s таму",s:"некалькі секунд",m:n,mm:n,h:n,hh:n,d:"дзень",dd:n,M:"месяц",MM:n,y:"год",yy:n},meridiemParse:/ночы|раніцы|дня|вечара/,isPM:function(e){return/^(дня|вечара)$/.test(e)},meridiem:function(e,t,n){return e<4?"ночы":e<12?"раніцы":e<17?"дня":"вечара"},dayOfMonthOrdinalParse:/\d{1,2}-(і|ы|га)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return(e%10==2||e%10==3)&&e%100!=12&&e%100!=13?e+"-і":e+"-ы";case"D":return e+"-га";default:return e}},week:{dow:1,doy:7}})})(n(30381))},68338:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("bg",{months:"януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември".split("_"),monthsShort:"яну_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек".split("_"),weekdays:"неделя_понеделник_вторник_сряда_четвъртък_петък_събота".split("_"),weekdaysShort:"нед_пон_вто_сря_чет_пет_съб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Днес в] LT",nextDay:"[Утре в] LT",nextWeek:"dddd [в] LT",lastDay:"[Вчера в] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Миналата] dddd [в] LT";case 1:case 2:case 4:case 5:return"[Миналия] dddd [в] LT"}},sameElse:"L"},relativeTime:{future:"след %s",past:"преди %s",s:"няколко секунди",ss:"%d секунди",m:"минута",mm:"%d минути",h:"час",hh:"%d часа",d:"ден",dd:"%d дена",w:"седмица",ww:"%d седмици",M:"месец",MM:"%d месеца",y:"година",yy:"%d години"},dayOfMonthOrdinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var t=e%10,n=e%100;if(0===e)return e+"-ев";if(0===n)return e+"-ен";if(n>10&&n<20)return e+"-ти";if(1===t)return e+"-ви";if(2===t)return e+"-ри";else if(7===t||8===t)return e+"-ми";else return e+"-ти"},week:{dow:1,doy:7}})})(n(30381))},67438:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("bm",{months:"Zanwuyekalo_Fewuruyekalo_Marisikalo_Awirilikalo_Mɛkalo_Zuwɛnkalo_Zuluyekalo_Utikalo_Sɛtanburukalo_ɔkutɔburukalo_Nowanburukalo_Desanburukalo".split("_"),monthsShort:"Zan_Few_Mar_Awi_Mɛ_Zuw_Zul_Uti_Sɛt_ɔku_Now_Des".split("_"),weekdays:"Kari_Ntɛnɛn_Tarata_Araba_Alamisa_Juma_Sibiri".split("_"),weekdaysShort:"Kar_Ntɛ_Tar_Ara_Ala_Jum_Sib".split("_"),weekdaysMin:"Ka_Nt_Ta_Ar_Al_Ju_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"MMMM [tile] D [san] YYYY",LLL:"MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm",LLLL:"dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm"},calendar:{sameDay:"[Bi lɛrɛ] LT",nextDay:"[Sini lɛrɛ] LT",nextWeek:"dddd [don lɛrɛ] LT",lastDay:"[Kunu lɛrɛ] LT",lastWeek:"dddd [tɛmɛnen lɛrɛ] LT",sameElse:"L"},relativeTime:{future:"%s kɔnɔ",past:"a bɛ %s bɔ",s:"sanga dama dama",ss:"sekondi %d",m:"miniti kelen",mm:"miniti %d",h:"lɛrɛ kelen",hh:"lɛrɛ %d",d:"tile kelen",dd:"tile %d",M:"kalo kelen",MM:"kalo %d",y:"san kelen",yy:"san %d"},week:{dow:1,doy:4}})})(n(30381))},76225:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"},n={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"};return e.defineLocale("bn-bd",{months:"জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি".split("_"),weekdaysMin:"রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm সময়",LLLL:"dddd, D MMMM YYYY, A h:mm সময়"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কয়েক সেকেন্ড",ss:"%d সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(e){return e.replace(/[১২৩৪৫৬৭৮৯০]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/রাত|ভোর|সকাল|দুপুর|বিকাল|সন্ধ্যা|রাত/,meridiemHour:function(e,t){if(12===e&&(e=0),"রাত"===t)return e<4?e:e+12;if("ভোর"===t)return e;if("সকাল"===t)return e;if("দুপুর"===t)return e>=3?e:e+12;if("বিকাল"===t)return e+12;else if("সন্ধ্যা"===t)return e+12},meridiem:function(e,t,n){if(e<4)return"রাত";if(e<6)return"ভোর";if(e<12)return"সকাল";if(e<15)return"দুপুর";if(e<18)return"বিকাল";else if(e<20)return"সন্ধ্যা";else return"রাত"},week:{dow:0,doy:6}})})(n(30381))},8905:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"১",2:"২",3:"৩",4:"৪",5:"৫",6:"৬",7:"৭",8:"৮",9:"৯",0:"০"},n={"১":"1","২":"2","৩":"3","৪":"4","৫":"5","৬":"6","৭":"7","৮":"8","৯":"9","০":"0"};return e.defineLocale("bn",{months:"জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর".split("_"),monthsShort:"জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে".split("_"),weekdays:"রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার".split("_"),weekdaysShort:"রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি".split("_"),weekdaysMin:"রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি".split("_"),longDateFormat:{LT:"A h:mm সময়",LTS:"A h:mm:ss সময়",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm সময়",LLLL:"dddd, D MMMM YYYY, A h:mm সময়"},calendar:{sameDay:"[আজ] LT",nextDay:"[আগামীকাল] LT",nextWeek:"dddd, LT",lastDay:"[গতকাল] LT",lastWeek:"[গত] dddd, LT",sameElse:"L"},relativeTime:{future:"%s পরে",past:"%s আগে",s:"কয়েক সেকেন্ড",ss:"%d সেকেন্ড",m:"এক মিনিট",mm:"%d মিনিট",h:"এক ঘন্টা",hh:"%d ঘন্টা",d:"এক দিন",dd:"%d দিন",M:"এক মাস",MM:"%d মাস",y:"এক বছর",yy:"%d বছর"},preparse:function(e){return e.replace(/[১২৩৪৫৬৭৮৯০]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/রাত|সকাল|দুপুর|বিকাল|রাত/,meridiemHour:function(e,t){return(12===e&&(e=0),"রাত"===t&&e>=4||"দুপুর"===t&&e<5||"বিকাল"===t)?e+12:e},meridiem:function(e,t,n){return e<4?"রাত":e<10?"সকাল":e<17?"দুপুর":e<20?"বিকাল":"রাত"},week:{dow:0,doy:6}})})(n(30381))},11560:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"༡",2:"༢",3:"༣",4:"༤",5:"༥",6:"༦",7:"༧",8:"༨",9:"༩",0:"༠"},n={"༡":"1","༢":"2","༣":"3","༤":"4","༥":"5","༦":"6","༧":"7","༨":"8","༩":"9","༠":"0"};return e.defineLocale("bo",{months:"ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ".split("_"),monthsShort:"ཟླ་1_ཟླ་2_ཟླ་3_ཟླ་4_ཟླ་5_ཟླ་6_ཟླ་7_ཟླ་8_ཟླ་9_ཟླ་10_ཟླ་11_ཟླ་12".split("_"),monthsShortRegex:/^(ཟླ་\d{1,2})/,monthsParseExact:!0,weekdays:"གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་".split("_"),weekdaysShort:"ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་".split("_"),weekdaysMin:"ཉི_ཟླ_མིག_ལྷག_ཕུར_སངས_སྤེན".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[དི་རིང] LT",nextDay:"[སང་ཉིན] LT",nextWeek:"[བདུན་ཕྲག་རྗེས་མ], LT",lastDay:"[ཁ་སང] LT",lastWeek:"[བདུན་ཕྲག་མཐའ་མ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ལ་",past:"%s སྔན་ལ",s:"ལམ་སང",ss:"%d སྐར་ཆ།",m:"སྐར་མ་གཅིག",mm:"%d སྐར་མ",h:"ཆུ་ཚོད་གཅིག",hh:"%d ཆུ་ཚོད",d:"ཉིན་གཅིག",dd:"%d ཉིན་",M:"ཟླ་བ་གཅིག",MM:"%d ཟླ་བ",y:"ལོ་གཅིག",yy:"%d ལོ"},preparse:function(e){return e.replace(/[༡༢༣༤༥༦༧༨༩༠]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,meridiemHour:function(e,t){return(12===e&&(e=0),"མཚན་མོ"===t&&e>=4||"ཉིན་གུང"===t&&e<5||"དགོང་དག"===t)?e+12:e},meridiem:function(e,t,n){return e<4?"མཚན་མོ":e<10?"ཞོགས་ཀས":e<17?"ཉིན་གུང":e<20?"དགོང་དག":"མཚན་མོ"},week:{dow:0,doy:6}})})(n(30381))},1278:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n){return e+" "+i({mm:"munutenn",MM:"miz",dd:"devezh"}[n],e)}function n(e){switch(r(e)){case 1:case 3:case 4:case 5:case 9:return e+" bloaz";default:return e+" vloaz"}}function r(e){return e>9?r(e%10):e}function i(e,t){return 2===t?a(e):e}function a(e){var t={m:"v",b:"v",d:"z"};return void 0===t[e.charAt(0)]?e:t[e.charAt(0)]+e.substring(1)}var o=[/^gen/i,/^c[ʼ\']hwe/i,/^meu/i,/^ebr/i,/^mae/i,/^(mez|eve)/i,/^gou/i,/^eos/i,/^gwe/i,/^her/i,/^du/i,/^ker/i,],s=/^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu|gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,u=/^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu)/i,c=/^(gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,l=[/^sul/i,/^lun/i,/^meurzh/i,/^merc[ʼ\']her/i,/^yaou/i,/^gwener/i,/^sadorn/i,],f=[/^Sul/i,/^Lun/i,/^Meu/i,/^Mer/i,/^Yao/i,/^Gwe/i,/^Sad/i,],d=[/^Su/i,/^Lu/i,/^Me([^r]|$)/i,/^Mer/i,/^Ya/i,/^Gw/i,/^Sa/i,];return e.defineLocale("br",{months:"Genver_Cʼhwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu".split("_"),monthsShort:"Gen_Cʼhwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker".split("_"),weekdays:"Sul_Lun_Meurzh_Mercʼher_Yaou_Gwener_Sadorn".split("_"),weekdaysShort:"Sul_Lun_Meu_Mer_Yao_Gwe_Sad".split("_"),weekdaysMin:"Su_Lu_Me_Mer_Ya_Gw_Sa".split("_"),weekdaysParse:d,fullWeekdaysParse:l,shortWeekdaysParse:f,minWeekdaysParse:d,monthsRegex:s,monthsShortRegex:s,monthsStrictRegex:u,monthsShortStrictRegex:c,monthsParse:o,longMonthsParse:o,shortMonthsParse:o,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [a viz] MMMM YYYY",LLL:"D [a viz] MMMM YYYY HH:mm",LLLL:"dddd, D [a viz] MMMM YYYY HH:mm"},calendar:{sameDay:"[Hiziv da] LT",nextDay:"[Warcʼhoazh da] LT",nextWeek:"dddd [da] LT",lastDay:"[Decʼh da] LT",lastWeek:"dddd [paset da] LT",sameElse:"L"},relativeTime:{future:"a-benn %s",past:"%s ʼzo",s:"un nebeud segondenno\xf9",ss:"%d eilenn",m:"ur vunutenn",mm:t,h:"un eur",hh:"%d eur",d:"un devezh",dd:t,M:"ur miz",MM:t,y:"ur bloaz",yy:n},dayOfMonthOrdinalParse:/\d{1,2}(añ|vet)/,ordinal:function(e){var t=1===e?"a\xf1":"vet";return e+t},week:{dow:1,doy:4},meridiemParse:/a.m.|g.m./,isPM:function(e){return"g.m."===e},meridiem:function(e,t,n){return e<12?"a.m.":"g.m."}})})(n(30381))},80622:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n){var r=e+" ";switch(n){case"ss":return 1===e?r+="sekunda":2===e||3===e||4===e?r+="sekunde":r+="sekundi",r;case"m":return t?"jedna minuta":"jedne minute";case"mm":return 1===e?r+="minuta":2===e||3===e||4===e?r+="minute":r+="minuta",r;case"h":return t?"jedan sat":"jednog sata";case"hh":return 1===e?r+="sat":2===e||3===e||4===e?r+="sata":r+="sati",r;case"dd":return 1===e?r+="dan":r+="dana",r;case"MM":return 1===e?r+="mjesec":2===e||3===e||4===e?r+="mjeseca":r+="mjeseci",r;case"yy":return 1===e?r+="godina":2===e||3===e||4===e?r+="godine":r+="godina",r}}return e.defineLocale("bs",{months:"januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:case 3:return"[prošlu] dddd [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:t,mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},2468:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ca",{months:{standalone:"gener_febrer_mar\xe7_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre".split("_"),format:"de gener_de febrer_de mar\xe7_d'abril_de maig_de juny_de juliol_d'agost_de setembre_d'octubre_de novembre_de desembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._mar\xe7_abr._maig_juny_jul._ag._set._oct._nov._des.".split("_"),monthsParseExact:!0,weekdays:"diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dt._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dt_dc_dj_dv_ds".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a les] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a les] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:function(){return"[avui a "+(1!==this.hours()?"les":"la")+"] LT"},nextDay:function(){return"[dem\xe0 a "+(1!==this.hours()?"les":"la")+"] LT"},nextWeek:function(){return"dddd [a "+(1!==this.hours()?"les":"la")+"] LT"},lastDay:function(){return"[ahir a "+(1!==this.hours()?"les":"la")+"] LT"},lastWeek:function(){return"[el] dddd [passat a "+(1!==this.hours()?"les":"la")+"] LT"},sameElse:"L"},relativeTime:{future:"d'aqu\xed %s",past:"fa %s",s:"uns segons",ss:"%d segons",m:"un minut",mm:"%d minuts",h:"una hora",hh:"%d hores",d:"un dia",dd:"%d dies",M:"un mes",MM:"%d mesos",y:"un any",yy:"%d anys"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(e,t){var n=1===e?"r":2===e?"n":3===e?"r":4===e?"t":"\xe8";return("w"===t||"W"===t)&&(n="a"),e+n},week:{dow:1,doy:4}})})(n(30381))},5822:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="leden_\xfanor_březen_duben_květen_červen_červenec_srpen_z\xe1ř\xed_ř\xedjen_listopad_prosinec".split("_"),n="led_\xfano_bře_dub_kvě_čvn_čvc_srp_z\xe1ř_ř\xedj_lis_pro".split("_"),r=[/^led/i,/^úno/i,/^bře/i,/^dub/i,/^kvě/i,/^(čvn|červen$|června)/i,/^(čvc|červenec|července)/i,/^srp/i,/^zář/i,/^říj/i,/^lis/i,/^pro/i,],i=/^(leden|únor|březen|duben|květen|červenec|července|červen|června|srpen|září|říjen|listopad|prosinec|led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i;function a(e){return e>1&&e<5&&1!=~~(e/10)}function o(e,t,n,r){var i=e+" ";switch(n){case"s":return t||r?"p\xe1r sekund":"p\xe1r sekundami";case"ss":if(t||r)return i+(a(e)?"sekundy":"sekund");return i+"sekundami";case"m":return t?"minuta":r?"minutu":"minutou";case"mm":if(t||r)return i+(a(e)?"minuty":"minut");return i+"minutami";case"h":return t?"hodina":r?"hodinu":"hodinou";case"hh":if(t||r)return i+(a(e)?"hodiny":"hodin");return i+"hodinami";case"d":return t||r?"den":"dnem";case"dd":if(t||r)return i+(a(e)?"dny":"dn\xed");return i+"dny";case"M":return t||r?"měs\xedc":"měs\xedcem";case"MM":if(t||r)return i+(a(e)?"měs\xedce":"měs\xedců");return i+"měs\xedci";case"y":return t||r?"rok":"rokem";case"yy":if(t||r)return i+(a(e)?"roky":"let");return i+"lety"}}return e.defineLocale("cs",{months:t,monthsShort:n,monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(leden|ledna|února|únor|březen|března|duben|dubna|květen|května|červenec|července|červen|června|srpen|srpna|září|říjen|října|listopadu|listopad|prosinec|prosince)/i,monthsShortStrictRegex:/^(led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"neděle_ponděl\xed_\xfater\xfd_středa_čtvrtek_p\xe1tek_sobota".split("_"),weekdaysShort:"ne_po_\xfat_st_čt_p\xe1_so".split("_"),weekdaysMin:"ne_po_\xfat_st_čt_p\xe1_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm",l:"D. M. YYYY"},calendar:{sameDay:"[dnes v] LT",nextDay:"[z\xedtra v] LT",nextWeek:function(){switch(this.day()){case 0:return"[v neděli v] LT";case 1:case 2:return"[v] dddd [v] LT";case 3:return"[ve středu v] LT";case 4:return"[ve čtvrtek v] LT";case 5:return"[v p\xe1tek v] LT";case 6:return"[v sobotu v] LT"}},lastDay:"[včera v] LT",lastWeek:function(){switch(this.day()){case 0:return"[minulou neděli v] LT";case 1:case 2:return"[minul\xe9] dddd [v] LT";case 3:return"[minulou středu v] LT";case 4:case 5:return"[minul\xfd] dddd [v] LT";case 6:return"[minulou sobotu v] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"před %s",s:o,ss:o,m:o,mm:o,h:o,hh:o,d:o,dd:o,M:o,MM:o,y:o,yy:o},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},50877:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("cv",{months:"кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав".split("_"),monthsShort:"кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш".split("_"),weekdays:"вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун".split("_"),weekdaysShort:"выр_тун_ытл_юн_кӗҫ_эрн_шӑм".split("_"),weekdaysMin:"вр_тн_ыт_юн_кҫ_эр_шм".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]",LLL:"YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm",LLLL:"dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm"},calendar:{sameDay:"[Паян] LT [сехетре]",nextDay:"[Ыран] LT [сехетре]",lastDay:"[Ӗнер] LT [сехетре]",nextWeek:"[Ҫитес] dddd LT [сехетре]",lastWeek:"[Иртнӗ] dddd LT [сехетре]",sameElse:"L"},relativeTime:{future:function(e){var t=/сехет$/i.exec(e)?"рен":/ҫул$/i.exec(e)?"тан":"ран";return e+t},past:"%s каялла",s:"пӗр-ик ҫеккунт",ss:"%d ҫеккунт",m:"пӗр минут",mm:"%d минут",h:"пӗр сехет",hh:"%d сехет",d:"пӗр кун",dd:"%d кун",M:"пӗр уйӑх",MM:"%d уйӑх",y:"пӗр ҫул",yy:"%d ҫул"},dayOfMonthOrdinalParse:/\d{1,2}-мӗш/,ordinal:"%d-мӗш",week:{dow:1,doy:7}})})(n(30381))},47373:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("cy",{months:"Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr".split("_"),monthsShort:"Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag".split("_"),weekdays:"Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn".split("_"),weekdaysShort:"Sul_Llun_Maw_Mer_Iau_Gwe_Sad".split("_"),weekdaysMin:"Su_Ll_Ma_Me_Ia_Gw_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Heddiw am] LT",nextDay:"[Yfory am] LT",nextWeek:"dddd [am] LT",lastDay:"[Ddoe am] LT",lastWeek:"dddd [diwethaf am] LT",sameElse:"L"},relativeTime:{future:"mewn %s",past:"%s yn \xf4l",s:"ychydig eiliadau",ss:"%d eiliad",m:"munud",mm:"%d munud",h:"awr",hh:"%d awr",d:"diwrnod",dd:"%d diwrnod",M:"mis",MM:"%d mis",y:"blwyddyn",yy:"%d flynedd"},dayOfMonthOrdinalParse:/\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,ordinal:function(e){var t=e,n="",r=["","af","il","ydd","ydd","ed","ed","ed","fed","fed","fed","eg","fed","eg","eg","fed","eg","eg","fed","eg","fed"];return t>20?n=40===t||50===t||60===t||80===t||100===t?"fed":"ain":t>0&&(n=r[t]),e+n},week:{dow:1,doy:4}})})(n(30381))},24780:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("da",{months:"januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"s\xf8ndag_mandag_tirsdag_onsdag_torsdag_fredag_l\xf8rdag".split("_"),weekdaysShort:"s\xf8n_man_tir_ons_tor_fre_l\xf8r".split("_"),weekdaysMin:"s\xf8_ma_ti_on_to_fr_l\xf8".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd [d.] D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"p\xe5 dddd [kl.] LT",lastDay:"[i g\xe5r kl.] LT",lastWeek:"[i] dddd[s kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"f\xe5 sekunder",ss:"%d sekunder",m:"et minut",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dage",M:"en m\xe5ned",MM:"%d m\xe5neder",y:"et \xe5r",yy:"%d \xe5r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},60217:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?i[n][0]:i[n][1]}return e.defineLocale("de-at",{months:"J\xe4nner_Februar_M\xe4rz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"J\xe4n._Feb._M\xe4rz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},60894:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?i[n][0]:i[n][1]}return e.defineLocale("de-ch",{months:"Januar_Februar_M\xe4rz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._M\xe4rz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},59740:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[e+" Tage",e+" Tagen"],w:["eine Woche","einer Woche"],M:["ein Monat","einem Monat"],MM:[e+" Monate",e+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[e+" Jahre",e+" Jahren"]};return t?i[n][0]:i[n][1]}return e.defineLocale("de",{months:"Januar_Februar_M\xe4rz_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Feb._M\xe4rz_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY HH:mm",LLLL:"dddd, D. MMMM YYYY HH:mm"},calendar:{sameDay:"[heute um] LT [Uhr]",sameElse:"L",nextDay:"[morgen um] LT [Uhr]",nextWeek:"dddd [um] LT [Uhr]",lastDay:"[gestern um] LT [Uhr]",lastWeek:"[letzten] dddd [um] LT [Uhr]"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",ss:"%d Sekunden",m:t,mm:"%d Minuten",h:t,hh:"%d Stunden",d:t,dd:t,w:t,ww:"%d Wochen",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},5300:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=["ޖެނުއަރީ","ފެބްރުއަރީ","މާރިޗު","އޭޕްރީލު","މޭ","ޖޫން","ޖުލައި","އޯގަސްޓު","ސެޕްޓެމްބަރު","އޮކްޓޯބަރު","ނޮވެމްބަރު","ޑިސެމްބަރު",],n=["އާދިއްތަ","ހޯމަ","އަންގާރަ","ބުދަ","ބުރާސްފަތި","ހުކުރު","ހޮނިހިރު",];return e.defineLocale("dv",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:"އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"D/M/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},meridiemParse:/މކ|މފ/,isPM:function(e){return"މފ"===e},meridiem:function(e,t,n){return e<12?"މކ":"މފ"},calendar:{sameDay:"[މިއަދު] LT",nextDay:"[މާދަމާ] LT",nextWeek:"dddd LT",lastDay:"[އިއްޔެ] LT",lastWeek:"[ފާއިތުވި] dddd LT",sameElse:"L"},relativeTime:{future:"ތެރޭގައި %s",past:"ކުރިން %s",s:"ސިކުންތުކޮޅެއް",ss:"d% ސިކުންތު",m:"މިނިޓެއް",mm:"މިނިޓު %d",h:"ގަޑިއިރެއް",hh:"ގަޑިއިރު %d",d:"ދުވަހެއް",dd:"ދުވަސް %d",M:"މަހެއް",MM:"މަސް %d",y:"އަހަރެއް",yy:"އަހަރު %d"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:7,doy:12}})})(n(30381))},50837:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e){return"undefined"!=typeof Function&&e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}return e.defineLocale("el",{monthsNominativeEl:"Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος".split("_"),monthsGenitiveEl:"Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου".split("_"),months:function(e,t){return e?"string"==typeof t&&/D/.test(t.substring(0,t.indexOf("MMMM")))?this._monthsGenitiveEl[e.month()]:this._monthsNominativeEl[e.month()]:this._monthsNominativeEl},monthsShort:"Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ".split("_"),weekdays:"Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο".split("_"),weekdaysShort:"Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ".split("_"),weekdaysMin:"Κυ_Δε_Τρ_Τε_Πε_Πα_Σα".split("_"),meridiem:function(e,t,n){return e>11?n?"μμ":"ΜΜ":n?"πμ":"ΠΜ"},isPM:function(e){return"μ"===(e+"").toLowerCase()[0]},meridiemParse:/[ΠΜ]\.?Μ?\.?/i,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendarEl:{sameDay:"[Σήμερα {}] LT",nextDay:"[Αύριο {}] LT",nextWeek:"dddd [{}] LT",lastDay:"[Χθες {}] LT",lastWeek:function(){return 6===this.day()?"[το προηγούμενο] dddd [{}] LT":"[την προηγούμενη] dddd [{}] LT"},sameElse:"L"},calendar:function(e,n){var r=this._calendarEl[e],i=n&&n.hours();return t(r)&&(r=r.apply(n)),r.replace("{}",i%12==1?"στη":"στις")},relativeTime:{future:"σε %s",past:"%s πριν",s:"λίγα δευτερόλεπτα",ss:"%d δευτερόλεπτα",m:"ένα λεπτό",mm:"%d λεπτά",h:"μία ώρα",hh:"%d ώρες",d:"μία μέρα",dd:"%d μέρες",M:"ένας μήνας",MM:"%d μήνες",y:"ένας χρόνος",yy:"%d χρόνια"},dayOfMonthOrdinalParse:/\d{1,2}η/,ordinal:"%dη",week:{dow:1,doy:4}})})(n(30381))},78348:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-au",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:0,doy:4}})})(n(30381))},77925:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-ca",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"YYYY-MM-DD",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n}})})(n(30381))},22243:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-gb",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}})})(n(30381))},46436:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-ie",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}})})(n(30381))},47207:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-il",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n}})})(n(30381))},44175:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-in",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:0,doy:6}})})(n(30381))},76319:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-nz",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}})})(n(30381))},31662:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("en-sg",{months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}})})(n(30381))},92915:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("eo",{months:"januaro_februaro_marto_aprilo_majo_junio_julio_aŭgusto_septembro_oktobro_novembro_decembro".split("_"),monthsShort:"jan_feb_mart_apr_maj_jun_jul_aŭg_sept_okt_nov_dec".split("_"),weekdays:"dimanĉo_lundo_mardo_merkredo_ĵaŭdo_vendredo_sabato".split("_"),weekdaysShort:"dim_lun_mard_merk_ĵaŭ_ven_sab".split("_"),weekdaysMin:"di_lu_ma_me_ĵa_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"[la] D[-an de] MMMM, YYYY",LLL:"[la] D[-an de] MMMM, YYYY HH:mm",LLLL:"dddd[n], [la] D[-an de] MMMM, YYYY HH:mm",llll:"ddd, [la] D[-an de] MMM, YYYY HH:mm"},meridiemParse:/[ap]\.t\.m/i,isPM:function(e){return"p"===e.charAt(0).toLowerCase()},meridiem:function(e,t,n){return e>11?n?"p.t.m.":"P.T.M.":n?"a.t.m.":"A.T.M."},calendar:{sameDay:"[Hodiaŭ je] LT",nextDay:"[Morgaŭ je] LT",nextWeek:"dddd[n je] LT",lastDay:"[Hieraŭ je] LT",lastWeek:"[pasintan] dddd[n je] LT",sameElse:"L"},relativeTime:{future:"post %s",past:"antaŭ %s",s:"kelkaj sekundoj",ss:"%d sekundoj",m:"unu minuto",mm:"%d minutoj",h:"unu horo",hh:"%d horoj",d:"unu tago",dd:"%d tagoj",M:"unu monato",MM:"%d monatoj",y:"unu jaro",yy:"%d jaroj"},dayOfMonthOrdinalParse:/\d{1,2}a/,ordinal:"%da",week:{dow:1,doy:7}})})(n(30381))},55251:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),n="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),r=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i,],i=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;return e.defineLocale("es-do",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"domingo_lunes_martes_mi\xe9rcoles_jueves_viernes_s\xe1bado".split("_"),weekdaysShort:"dom._lun._mar._mi\xe9._jue._vie._s\xe1b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_s\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[ma\xf1ana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un d\xeda",dd:"%d d\xedas",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un a\xf1o",yy:"%d a\xf1os"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},96112:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),n="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),r=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i,],i=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;return e.defineLocale("es-mx",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"domingo_lunes_martes_mi\xe9rcoles_jueves_viernes_s\xe1bado".split("_"),weekdaysShort:"dom._lun._mar._mi\xe9._jue._vie._s\xe1b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_s\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[ma\xf1ana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un d\xeda",dd:"%d d\xedas",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un a\xf1o",yy:"%d a\xf1os"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:0,doy:4},invalidDate:"Fecha inv\xe1lida"})})(n(30381))},71146:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),n="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),r=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i,],i=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;return e.defineLocale("es-us",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"domingo_lunes_martes_mi\xe9rcoles_jueves_viernes_s\xe1bado".split("_"),weekdaysShort:"dom._lun._mar._mi\xe9._jue._vie._s\xe1b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_s\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"MM/DD/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY h:mm A",LLLL:"dddd, D [de] MMMM [de] YYYY h:mm A"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[ma\xf1ana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un d\xeda",dd:"%d d\xedas",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un a\xf1o",yy:"%d a\xf1os"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:0,doy:6}})})(n(30381))},55655:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),n="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_"),r=[/^ene/i,/^feb/i,/^mar/i,/^abr/i,/^may/i,/^jun/i,/^jul/i,/^ago/i,/^sep/i,/^oct/i,/^nov/i,/^dic/i,],i=/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre|ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i;return e.defineLocale("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(enero|febrero|marzo|abril|mayo|junio|julio|agosto|septiembre|octubre|noviembre|diciembre)/i,monthsShortStrictRegex:/^(ene\.?|feb\.?|mar\.?|abr\.?|may\.?|jun\.?|jul\.?|ago\.?|sep\.?|oct\.?|nov\.?|dic\.?)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"domingo_lunes_martes_mi\xe9rcoles_jueves_viernes_s\xe1bado".split("_"),weekdaysShort:"dom._lun._mar._mi\xe9._jue._vie._s\xe1b.".split("_"),weekdaysMin:"do_lu_ma_mi_ju_vi_s\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[ma\xf1ana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un d\xeda",dd:"%d d\xedas",w:"una semana",ww:"%d semanas",M:"un mes",MM:"%d meses",y:"un a\xf1o",yy:"%d a\xf1os"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4},invalidDate:"Fecha inv\xe1lida"})})(n(30381))},5603:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={s:["m\xf5ne sekundi","m\xf5ni sekund","paar sekundit"],ss:[e+"sekundi",e+"sekundit"],m:["\xfche minuti","\xfcks minut"],mm:[e+" minuti",e+" minutit"],h:["\xfche tunni","tund aega","\xfcks tund"],hh:[e+" tunni",e+" tundi"],d:["\xfche p\xe4eva","\xfcks p\xe4ev"],M:["kuu aja","kuu aega","\xfcks kuu"],MM:[e+" kuu",e+" kuud"],y:["\xfche aasta","aasta","\xfcks aasta"],yy:[e+" aasta",e+" aastat"]};return t?i[n][2]?i[n][2]:i[n][1]:r?i[n][0]:i[n][1]}return e.defineLocale("et",{months:"jaanuar_veebruar_m\xe4rts_aprill_mai_juuni_juuli_august_september_oktoober_november_detsember".split("_"),monthsShort:"jaan_veebr_m\xe4rts_apr_mai_juuni_juuli_aug_sept_okt_nov_dets".split("_"),weekdays:"p\xfchap\xe4ev_esmasp\xe4ev_teisip\xe4ev_kolmap\xe4ev_neljap\xe4ev_reede_laup\xe4ev".split("_"),weekdaysShort:"P_E_T_K_N_R_L".split("_"),weekdaysMin:"P_E_T_K_N_R_L".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[T\xe4na,] LT",nextDay:"[Homme,] LT",nextWeek:"[J\xe4rgmine] dddd LT",lastDay:"[Eile,] LT",lastWeek:"[Eelmine] dddd LT",sameElse:"L"},relativeTime:{future:"%s p\xe4rast",past:"%s tagasi",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:"%d p\xe4eva",M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},77763:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("eu",{months:"urtarrila_otsaila_martxoa_apirila_maiatza_ekaina_uztaila_abuztua_iraila_urria_azaroa_abendua".split("_"),monthsShort:"urt._ots._mar._api._mai._eka._uzt._abu._ira._urr._aza._abe.".split("_"),monthsParseExact:!0,weekdays:"igandea_astelehena_asteartea_asteazkena_osteguna_ostirala_larunbata".split("_"),weekdaysShort:"ig._al._ar._az._og._ol._lr.".split("_"),weekdaysMin:"ig_al_ar_az_og_ol_lr".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY[ko] MMMM[ren] D[a]",LLL:"YYYY[ko] MMMM[ren] D[a] HH:mm",LLLL:"dddd, YYYY[ko] MMMM[ren] D[a] HH:mm",l:"YYYY-M-D",ll:"YYYY[ko] MMM D[a]",lll:"YYYY[ko] MMM D[a] HH:mm",llll:"ddd, YYYY[ko] MMM D[a] HH:mm"},calendar:{sameDay:"[gaur] LT[etan]",nextDay:"[bihar] LT[etan]",nextWeek:"dddd LT[etan]",lastDay:"[atzo] LT[etan]",lastWeek:"[aurreko] dddd LT[etan]",sameElse:"L"},relativeTime:{future:"%s barru",past:"duela %s",s:"segundo batzuk",ss:"%d segundo",m:"minutu bat",mm:"%d minutu",h:"ordu bat",hh:"%d ordu",d:"egun bat",dd:"%d egun",M:"hilabete bat",MM:"%d hilabete",y:"urte bat",yy:"%d urte"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},76959:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"۱",2:"۲",3:"۳",4:"۴",5:"۵",6:"۶",7:"۷",8:"۸",9:"۹",0:"۰"},n={"۱":"1","۲":"2","۳":"3","۴":"4","۵":"5","۶":"6","۷":"7","۸":"8","۹":"9","۰":"0"};return e.defineLocale("fa",{months:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),monthsShort:"ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر".split("_"),weekdays:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysShort:"یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه".split("_"),weekdaysMin:"ی_د_س_چ_پ_ج_ش".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/قبل از ظهر|بعد از ظهر/,isPM:function(e){return/بعد از ظهر/.test(e)},meridiem:function(e,t,n){return e<12?"قبل از ظهر":"بعد از ظهر"},calendar:{sameDay:"[امروز ساعت] LT",nextDay:"[فردا ساعت] LT",nextWeek:"dddd [ساعت] LT",lastDay:"[دیروز ساعت] LT",lastWeek:"dddd [پیش] [ساعت] LT",sameElse:"L"},relativeTime:{future:"در %s",past:"%s پیش",s:"چند ثانیه",ss:"%d ثانیه",m:"یک دقیقه",mm:"%d دقیقه",h:"یک ساعت",hh:"%d ساعت",d:"یک روز",dd:"%d روز",M:"یک ماه",MM:"%d ماه",y:"یک سال",yy:"%d سال"},preparse:function(e){return e.replace(/[۰-۹]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"،")},dayOfMonthOrdinalParse:/\d{1,2}م/,ordinal:"%dم",week:{dow:6,doy:12}})})(n(30381))},11897:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="nolla yksi kaksi kolme nelj\xe4 viisi kuusi seitsem\xe4n kahdeksan yhdeks\xe4n".split(" "),n=["nolla","yhden","kahden","kolmen","nelj\xe4n","viiden","kuuden",t[7],t[8],t[9],];function r(e,t,n,r){var a="";switch(n){case"s":return r?"muutaman sekunnin":"muutama sekunti";case"ss":a=r?"sekunnin":"sekuntia";break;case"m":return r?"minuutin":"minuutti";case"mm":a=r?"minuutin":"minuuttia";break;case"h":return r?"tunnin":"tunti";case"hh":a=r?"tunnin":"tuntia";break;case"d":return r?"p\xe4iv\xe4n":"p\xe4iv\xe4";case"dd":a=r?"p\xe4iv\xe4n":"p\xe4iv\xe4\xe4";break;case"M":return r?"kuukauden":"kuukausi";case"MM":a=r?"kuukauden":"kuukautta";break;case"y":return r?"vuoden":"vuosi";case"yy":a=r?"vuoden":"vuotta"}return i(e,r)+" "+a}function i(e,r){return e<10?r?n[e]:t[e]:e}return e.defineLocale("fi",{months:"tammikuu_helmikuu_maaliskuu_huhtikuu_toukokuu_kes\xe4kuu_hein\xe4kuu_elokuu_syyskuu_lokakuu_marraskuu_joulukuu".split("_"),monthsShort:"tammi_helmi_maalis_huhti_touko_kes\xe4_hein\xe4_elo_syys_loka_marras_joulu".split("_"),weekdays:"sunnuntai_maanantai_tiistai_keskiviikko_torstai_perjantai_lauantai".split("_"),weekdaysShort:"su_ma_ti_ke_to_pe_la".split("_"),weekdaysMin:"su_ma_ti_ke_to_pe_la".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"Do MMMM[ta] YYYY",LLL:"Do MMMM[ta] YYYY, [klo] HH.mm",LLLL:"dddd, Do MMMM[ta] YYYY, [klo] HH.mm",l:"D.M.YYYY",ll:"Do MMM YYYY",lll:"Do MMM YYYY, [klo] HH.mm",llll:"ddd, Do MMM YYYY, [klo] HH.mm"},calendar:{sameDay:"[t\xe4n\xe4\xe4n] [klo] LT",nextDay:"[huomenna] [klo] LT",nextWeek:"dddd [klo] LT",lastDay:"[eilen] [klo] LT",lastWeek:"[viime] dddd[na] [klo] LT",sameElse:"L"},relativeTime:{future:"%s p\xe4\xe4st\xe4",past:"%s sitten",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},42549:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("fil",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})})(n(30381))},94694:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("fo",{months:"januar_februar_mars_apr\xedl_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan_feb_mar_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_"),weekdays:"sunnudagur_m\xe1nadagur_t\xfdsdagur_mikudagur_h\xf3sdagur_fr\xedggjadagur_leygardagur".split("_"),weekdaysShort:"sun_m\xe1n_t\xfds_mik_h\xf3s_fr\xed_ley".split("_"),weekdaysMin:"su_m\xe1_t\xfd_mi_h\xf3_fr_le".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D. MMMM, YYYY HH:mm"},calendar:{sameDay:"[\xcd dag kl.] LT",nextDay:"[\xcd morgin kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[\xcd gj\xe1r kl.] LT",lastWeek:"[s\xed\xf0stu] dddd [kl] LT",sameElse:"L"},relativeTime:{future:"um %s",past:"%s s\xed\xf0ani",s:"f\xe1 sekund",ss:"%d sekundir",m:"ein minuttur",mm:"%d minuttir",h:"ein t\xedmi",hh:"%d t\xedmar",d:"ein dagur",dd:"%d dagar",M:"ein m\xe1na\xf0ur",MM:"%d m\xe1na\xf0ir",y:"eitt \xe1r",yy:"%d \xe1r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},63049:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("fr-ca",{months:"janvier_f\xe9vrier_mars_avril_mai_juin_juillet_ao\xfbt_septembre_octobre_novembre_d\xe9cembre".split("_"),monthsShort:"janv._f\xe9vr._mars_avr._mai_juin_juil._ao\xfbt_sept._oct._nov._d\xe9c.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui \xe0] LT",nextDay:"[Demain \xe0] LT",nextWeek:"dddd [\xe0] LT",lastDay:"[Hier \xe0] LT",lastWeek:"dddd [dernier \xe0] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,t){switch(t){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(1===e?"er":"e");case"w":case"W":return e+(1===e?"re":"e")}}})})(n(30381))},52330:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("fr-ch",{months:"janvier_f\xe9vrier_mars_avril_mai_juin_juillet_ao\xfbt_septembre_octobre_novembre_d\xe9cembre".split("_"),monthsShort:"janv._f\xe9vr._mars_avr._mai_juin_juil._ao\xfbt_sept._oct._nov._d\xe9c.".split("_"),monthsParseExact:!0,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui \xe0] LT",nextDay:"[Demain \xe0] LT",nextWeek:"dddd [\xe0] LT",lastDay:"[Hier \xe0] LT",lastWeek:"dddd [dernier \xe0] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|e)/,ordinal:function(e,t){switch(t){default:case"M":case"Q":case"D":case"DDD":case"d":return e+(1===e?"er":"e");case"w":case"W":return e+(1===e?"re":"e")}},week:{dow:1,doy:4}})})(n(30381))},94470:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=/^(janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)/i,n=/(janv\.?|févr\.?|mars|avr\.?|mai|juin|juil\.?|août|sept\.?|oct\.?|nov\.?|déc\.?)/i,r=/(janv\.?|févr\.?|mars|avr\.?|mai|juin|juil\.?|août|sept\.?|oct\.?|nov\.?|déc\.?|janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre)/i,i=[/^janv/i,/^févr/i,/^mars/i,/^avr/i,/^mai/i,/^juin/i,/^juil/i,/^août/i,/^sept/i,/^oct/i,/^nov/i,/^déc/i,];return e.defineLocale("fr",{months:"janvier_f\xe9vrier_mars_avril_mai_juin_juillet_ao\xfbt_septembre_octobre_novembre_d\xe9cembre".split("_"),monthsShort:"janv._f\xe9vr._mars_avr._mai_juin_juil._ao\xfbt_sept._oct._nov._d\xe9c.".split("_"),monthsRegex:r,monthsShortRegex:r,monthsStrictRegex:t,monthsShortStrictRegex:n,monthsParse:i,longMonthsParse:i,shortMonthsParse:i,weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"di_lu_ma_me_je_ve_sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Aujourd’hui \xe0] LT",nextDay:"[Demain \xe0] LT",nextWeek:"dddd [\xe0] LT",lastDay:"[Hier \xe0] LT",lastWeek:"dddd [dernier \xe0] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",ss:"%d secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",w:"une semaine",ww:"%d semaines",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(er|)/,ordinal:function(e,t){switch(t){case"D":return e+(1===e?"er":"");default:case"M":case"Q":case"DDD":case"d":return e+(1===e?"er":"e");case"w":case"W":return e+(1===e?"re":"e")}},week:{dow:1,doy:4}})})(n(30381))},5044:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="jan._feb._mrt._apr._mai_jun._jul._aug._sep._okt._nov._des.".split("_"),n="jan_feb_mrt_apr_mai_jun_jul_aug_sep_okt_nov_des".split("_");return e.defineLocale("fy",{months:"jannewaris_febrewaris_maart_april_maaie_juny_july_augustus_septimber_oktober_novimber_desimber".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsParseExact:!0,weekdays:"snein_moandei_tiisdei_woansdei_tongersdei_freed_sneon".split("_"),weekdaysShort:"si._mo._ti._wo._to._fr._so.".split("_"),weekdaysMin:"Si_Mo_Ti_Wo_To_Fr_So".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[hjoed om] LT",nextDay:"[moarn om] LT",nextWeek:"dddd [om] LT",lastDay:"[juster om] LT",lastWeek:"[\xf4fr\xfbne] dddd [om] LT",sameElse:"L"},relativeTime:{future:"oer %s",past:"%s lyn",s:"in pear sekonden",ss:"%d sekonden",m:"ien min\xfat",mm:"%d minuten",h:"ien oere",hh:"%d oeren",d:"ien dei",dd:"%d dagen",M:"ien moanne",MM:"%d moannen",y:"ien jier",yy:"%d jierren"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})})(n(30381))},29295:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=["Ean\xe1ir","Feabhra","M\xe1rta","Aibre\xe1n","Bealtaine","Meitheamh","I\xfail","L\xfanasa","Me\xe1n F\xf3mhair","Deireadh F\xf3mhair","Samhain","Nollaig",],n=["Ean","Feabh","M\xe1rt","Aib","Beal","Meith","I\xfail","L\xfan","M.F.","D.F.","Samh","Noll",],r=["D\xe9 Domhnaigh","D\xe9 Luain","D\xe9 M\xe1irt","D\xe9 C\xe9adaoin","D\xe9ardaoin","D\xe9 hAoine","D\xe9 Sathairn",],i=["Domh","Luan","M\xe1irt","C\xe9ad","D\xe9ar","Aoine","Sath"],a=["Do","Lu","M\xe1","C\xe9","D\xe9","A","Sa"];return e.defineLocale("ga",{months:t,monthsShort:n,monthsParseExact:!0,weekdays:r,weekdaysShort:i,weekdaysMin:a,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Inniu ag] LT",nextDay:"[Am\xe1rach ag] LT",nextWeek:"dddd [ag] LT",lastDay:"[Inn\xe9 ag] LT",lastWeek:"dddd [seo caite] [ag] LT",sameElse:"L"},relativeTime:{future:"i %s",past:"%s \xf3 shin",s:"c\xfapla soicind",ss:"%d soicind",m:"n\xf3im\xe9ad",mm:"%d n\xf3im\xe9ad",h:"uair an chloig",hh:"%d uair an chloig",d:"l\xe1",dd:"%d l\xe1",M:"m\xed",MM:"%d m\xedonna",y:"bliain",yy:"%d bliain"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){var t=1===e?"d":e%10==2?"na":"mh";return e+t},week:{dow:1,doy:4}})})(n(30381))},2101:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=["Am Faoilleach","An Gearran","Am M\xe0rt","An Giblean","An C\xe8itean","An t-\xd2gmhios","An t-Iuchar","An L\xf9nastal","An t-Sultain","An D\xe0mhair","An t-Samhain","An D\xf9bhlachd",],n=["Faoi","Gear","M\xe0rt","Gibl","C\xe8it","\xd2gmh","Iuch","L\xf9n","Sult","D\xe0mh","Samh","D\xf9bh",],r=["Did\xf2mhnaich","Diluain","Dim\xe0irt","Diciadain","Diardaoin","Dihaoine","Disathairne",],i=["Did","Dil","Dim","Dic","Dia","Dih","Dis"],a=["D\xf2","Lu","M\xe0","Ci","Ar","Ha","Sa"];return e.defineLocale("gd",{months:t,monthsShort:n,monthsParseExact:!0,weekdays:r,weekdaysShort:i,weekdaysMin:a,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[An-diugh aig] LT",nextDay:"[A-m\xe0ireach aig] LT",nextWeek:"dddd [aig] LT",lastDay:"[An-d\xe8 aig] LT",lastWeek:"dddd [seo chaidh] [aig] LT",sameElse:"L"},relativeTime:{future:"ann an %s",past:"bho chionn %s",s:"beagan diogan",ss:"%d diogan",m:"mionaid",mm:"%d mionaidean",h:"uair",hh:"%d uairean",d:"latha",dd:"%d latha",M:"m\xecos",MM:"%d m\xecosan",y:"bliadhna",yy:"%d bliadhna"},dayOfMonthOrdinalParse:/\d{1,2}(d|na|mh)/,ordinal:function(e){var t=1===e?"d":e%10==2?"na":"mh";return e+t},week:{dow:1,doy:4}})})(n(30381))},38794:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("gl",{months:"xaneiro_febreiro_marzo_abril_maio_xu\xf1o_xullo_agosto_setembro_outubro_novembro_decembro".split("_"),monthsShort:"xan._feb._mar._abr._mai._xu\xf1._xul._ago._set._out._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"domingo_luns_martes_m\xe9rcores_xoves_venres_s\xe1bado".split("_"),weekdaysShort:"dom._lun._mar._m\xe9r._xov._ven._s\xe1b.".split("_"),weekdaysMin:"do_lu_ma_m\xe9_xo_ve_s\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY H:mm",LLLL:"dddd, D [de] MMMM [de] YYYY H:mm"},calendar:{sameDay:function(){return"[hoxe "+(1!==this.hours()?"\xe1s":"\xe1")+"] LT"},nextDay:function(){return"[ma\xf1\xe1 "+(1!==this.hours()?"\xe1s":"\xe1")+"] LT"},nextWeek:function(){return"dddd ["+(1!==this.hours()?"\xe1s":"a")+"] LT"},lastDay:function(){return"[onte "+(1!==this.hours()?"\xe1":"a")+"] LT"},lastWeek:function(){return"[o] dddd [pasado "+(1!==this.hours()?"\xe1s":"a")+"] LT"},sameElse:"L"},relativeTime:{future:function(e){return 0===e.indexOf("un")?"n"+e:"en "+e},past:"hai %s",s:"uns segundos",ss:"%d segundos",m:"un minuto",mm:"%d minutos",h:"unha hora",hh:"%d horas",d:"un d\xeda",dd:"%d d\xedas",M:"un mes",MM:"%d meses",y:"un ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},27884:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={s:["थोडया सॅकंडांनी","थोडे सॅकंड"],ss:[e+" सॅकंडांनी",e+" सॅकंड"],m:["एका मिणटान","एक मिनूट"],mm:[e+" मिणटांनी",e+" मिणटां"],h:["एका वरान","एक वर"],hh:[e+" वरांनी",e+" वरां"],d:["एका दिसान","एक दीस"],dd:[e+" दिसांनी",e+" दीस"],M:["एका म्हयन्यान","एक म्हयनो"],MM:[e+" म्हयन्यानी",e+" म्हयने"],y:["एका वर्सान","एक वर्स"],yy:[e+" वर्सांनी",e+" वर्सां"]};return r?i[n][0]:i[n][1]}return e.defineLocale("gom-deva",{months:{standalone:"जानेवारी_फेब्रुवारी_मार्च_एप्रील_मे_जून_जुलय_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),format:"जानेवारीच्या_फेब्रुवारीच्या_मार्चाच्या_एप्रीलाच्या_मेयाच्या_जूनाच्या_जुलयाच्या_ऑगस्टाच्या_सप्टेंबराच्या_ऑक्टोबराच्या_नोव्हेंबराच्या_डिसेंबराच्या".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"जाने._फेब्रु._मार्च_एप्री._मे_जून_जुल._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),monthsParseExact:!0,weekdays:"आयतार_सोमार_मंगळार_बुधवार_बिरेस्तार_सुक्रार_शेनवार".split("_"),weekdaysShort:"आयत._सोम._मंगळ._बुध._ब्रेस्त._सुक्र._शेन.".split("_"),weekdaysMin:"आ_सो_मं_बु_ब्रे_सु_शे".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"A h:mm [वाजतां]",LTS:"A h:mm:ss [वाजतां]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [वाजतां]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [वाजतां]",llll:"ddd, D MMM YYYY, A h:mm [वाजतां]"},calendar:{sameDay:"[आयज] LT",nextDay:"[फाल्यां] LT",nextWeek:"[फुडलो] dddd[,] LT",lastDay:"[काल] LT",lastWeek:"[फाटलो] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s आदीं",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(वेर)/,ordinal:function(e,t){return"D"===t?e+"वेर":e},week:{dow:0,doy:3},meridiemParse:/राती|सकाळीं|दनपारां|सांजे/,meridiemHour:function(e,t){return(12===e&&(e=0),"राती"===t)?e<4?e:e+12:"सकाळीं"===t?e:"दनपारां"===t?e>12?e:e+12:"सांजे"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"राती":e<12?"सकाळीं":e<16?"दनपारां":e<20?"सांजे":"राती"}})})(n(30381))},23168:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={s:["thoddea sekondamni","thodde sekond"],ss:[e+" sekondamni",e+" sekond"],m:["eka mintan","ek minut"],mm:[e+" mintamni",e+" mintam"],h:["eka voran","ek vor"],hh:[e+" voramni",e+" voram"],d:["eka disan","ek dis"],dd:[e+" disamni",e+" dis"],M:["eka mhoinean","ek mhoino"],MM:[e+" mhoineamni",e+" mhoine"],y:["eka vorsan","ek voros"],yy:[e+" vorsamni",e+" vorsam"]};return r?i[n][0]:i[n][1]}return e.defineLocale("gom-latn",{months:{standalone:"Janer_Febrer_Mars_Abril_Mai_Jun_Julai_Agost_Setembr_Otubr_Novembr_Dezembr".split("_"),format:"Janerachea_Febrerachea_Marsachea_Abrilachea_Maiachea_Junachea_Julaiachea_Agostachea_Setembrachea_Otubrachea_Novembrachea_Dezembrachea".split("_"),isFormat:/MMMM(\s)+D[oD]?/},monthsShort:"Jan._Feb._Mars_Abr._Mai_Jun_Jul._Ago._Set._Otu._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Aitar_Somar_Mongllar_Budhvar_Birestar_Sukrar_Son'var".split("_"),weekdaysShort:"Ait._Som._Mon._Bud._Bre._Suk._Son.".split("_"),weekdaysMin:"Ai_Sm_Mo_Bu_Br_Su_Sn".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"A h:mm [vazta]",LTS:"A h:mm:ss [vazta]",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY A h:mm [vazta]",LLLL:"dddd, MMMM Do, YYYY, A h:mm [vazta]",llll:"ddd, D MMM YYYY, A h:mm [vazta]"},calendar:{sameDay:"[Aiz] LT",nextDay:"[Faleam] LT",nextWeek:"[Fuddlo] dddd[,] LT",lastDay:"[Kal] LT",lastWeek:"[Fattlo] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%s",past:"%s adim",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}(er)/,ordinal:function(e,t){return"D"===t?e+"er":e},week:{dow:0,doy:3},meridiemParse:/rati|sokallim|donparam|sanje/,meridiemHour:function(e,t){return(12===e&&(e=0),"rati"===t)?e<4?e:e+12:"sokallim"===t?e:"donparam"===t?e>12?e:e+12:"sanje"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"rati":e<12?"sokallim":e<16?"donparam":e<20?"sanje":"rati"}})})(n(30381))},95349:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"૧",2:"૨",3:"૩",4:"૪",5:"૫",6:"૬",7:"૭",8:"૮",9:"૯",0:"૦"},n={"૧":"1","૨":"2","૩":"3","૪":"4","૫":"5","૬":"6","૭":"7","૮":"8","૯":"9","૦":"0"};return e.defineLocale("gu",{months:"જાન્યુઆરી_ફેબ્રુઆરી_માર્ચ_એપ્રિલ_મે_જૂન_જુલાઈ_ઑગસ્ટ_સપ્ટેમ્બર_ઑક્ટ્બર_નવેમ્બર_ડિસેમ્બર".split("_"),monthsShort:"જાન્યુ._ફેબ્રુ._માર્ચ_એપ્રિ._મે_જૂન_જુલા._ઑગ._સપ્ટે._ઑક્ટ્._નવે._ડિસે.".split("_"),monthsParseExact:!0,weekdays:"રવિવાર_સોમવાર_મંગળવાર_બુધ્વાર_ગુરુવાર_શુક્રવાર_શનિવાર".split("_"),weekdaysShort:"રવિ_સોમ_મંગળ_બુધ્_ગુરુ_શુક્ર_શનિ".split("_"),weekdaysMin:"ર_સો_મં_બુ_ગુ_શુ_શ".split("_"),longDateFormat:{LT:"A h:mm વાગ્યે",LTS:"A h:mm:ss વાગ્યે",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm વાગ્યે",LLLL:"dddd, D MMMM YYYY, A h:mm વાગ્યે"},calendar:{sameDay:"[આજ] LT",nextDay:"[કાલે] LT",nextWeek:"dddd, LT",lastDay:"[ગઇકાલે] LT",lastWeek:"[પાછલા] dddd, LT",sameElse:"L"},relativeTime:{future:"%s મા",past:"%s પહેલા",s:"અમુક પળો",ss:"%d સેકંડ",m:"એક મિનિટ",mm:"%d મિનિટ",h:"એક કલાક",hh:"%d કલાક",d:"એક દિવસ",dd:"%d દિવસ",M:"એક મહિનો",MM:"%d મહિનો",y:"એક વર્ષ",yy:"%d વર્ષ"},preparse:function(e){return e.replace(/[૧૨૩૪૫૬૭૮૯૦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/રાત|બપોર|સવાર|સાંજ/,meridiemHour:function(e,t){return(12===e&&(e=0),"રાત"===t)?e<4?e:e+12:"સવાર"===t?e:"બપોર"===t?e>=10?e:e+12:"સાંજ"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"રાત":e<10?"સવાર":e<17?"બપોર":e<20?"સાંજ":"રાત"},week:{dow:0,doy:6}})})(n(30381))},24206:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("he",{months:"ינואר_פברואר_מרץ_אפריל_מאי_יוני_יולי_אוגוסט_ספטמבר_אוקטובר_נובמבר_דצמבר".split("_"),monthsShort:"ינו׳_פבר׳_מרץ_אפר׳_מאי_יוני_יולי_אוג׳_ספט׳_אוק׳_נוב׳_דצמ׳".split("_"),weekdays:"ראשון_שני_שלישי_רביעי_חמישי_שישי_שבת".split("_"),weekdaysShort:"א׳_ב׳_ג׳_ד׳_ה׳_ו׳_ש׳".split("_"),weekdaysMin:"א_ב_ג_ד_ה_ו_ש".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [ב]MMMM YYYY",LLL:"D [ב]MMMM YYYY HH:mm",LLLL:"dddd, D [ב]MMMM YYYY HH:mm",l:"D/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[היום ב־]LT",nextDay:"[מחר ב־]LT",nextWeek:"dddd [בשעה] LT",lastDay:"[אתמול ב־]LT",lastWeek:"[ביום] dddd [האחרון בשעה] LT",sameElse:"L"},relativeTime:{future:"בעוד %s",past:"לפני %s",s:"מספר שניות",ss:"%d שניות",m:"דקה",mm:"%d דקות",h:"שעה",hh:function(e){return 2===e?"שעתיים":e+" שעות"},d:"יום",dd:function(e){return 2===e?"יומיים":e+" ימים"},M:"חודש",MM:function(e){return 2===e?"חודשיים":e+" חודשים"},y:"שנה",yy:function(e){return 2===e?"שנתיים":e%10==0&&10!==e?e+" שנה":e+" שנים"}},meridiemParse:/אחה"צ|לפנה"צ|אחרי הצהריים|לפני הצהריים|לפנות בוקר|בבוקר|בערב/i,isPM:function(e){return/^(אחה"צ|אחרי הצהריים|בערב)$/.test(e)},meridiem:function(e,t,n){return e<5?"לפנות בוקר":e<10?"בבוקר":e<12?n?'לפנה"צ':"לפני הצהריים":e<18?n?'אחה"צ':"אחרי הצהריים":"בערב"}})})(n(30381))},30094:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},n={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"},r=[/^जन/i,/^फ़र|फर/i,/^मार्च/i,/^अप्रै/i,/^मई/i,/^जून/i,/^जुल/i,/^अग/i,/^सितं|सित/i,/^अक्टू/i,/^नव|नवं/i,/^दिसं|दिस/i,],i=[/^जन/i,/^फ़र/i,/^मार्च/i,/^अप्रै/i,/^मई/i,/^जून/i,/^जुल/i,/^अग/i,/^सित/i,/^अक्टू/i,/^नव/i,/^दिस/i,];return e.defineLocale("hi",{months:{format:"जनवरी_फ़रवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितम्बर_अक्टूबर_नवम्बर_दिसम्बर".split("_"),standalone:"जनवरी_फरवरी_मार्च_अप्रैल_मई_जून_जुलाई_अगस्त_सितंबर_अक्टूबर_नवंबर_दिसंबर".split("_")},monthsShort:"जन._फ़र._मार्च_अप्रै._मई_जून_जुल._अग._सित._अक्टू._नव._दिस.".split("_"),weekdays:"रविवार_सोमवार_मंगलवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगल_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm बजे",LTS:"A h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm बजे",LLLL:"dddd, D MMMM YYYY, A h:mm बजे"},monthsParse:r,longMonthsParse:r,shortMonthsParse:i,monthsRegex:/^(जनवरी|जन\.?|फ़रवरी|फरवरी|फ़र\.?|मार्च?|अप्रैल|अप्रै\.?|मई?|जून?|जुलाई|जुल\.?|अगस्त|अग\.?|सितम्बर|सितंबर|सित\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर|नव\.?|दिसम्बर|दिसंबर|दिस\.?)/i,monthsShortRegex:/^(जनवरी|जन\.?|फ़रवरी|फरवरी|फ़र\.?|मार्च?|अप्रैल|अप्रै\.?|मई?|जून?|जुलाई|जुल\.?|अगस्त|अग\.?|सितम्बर|सितंबर|सित\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर|नव\.?|दिसम्बर|दिसंबर|दिस\.?)/i,monthsStrictRegex:/^(जनवरी?|फ़रवरी|फरवरी?|मार्च?|अप्रैल?|मई?|जून?|जुलाई?|अगस्त?|सितम्बर|सितंबर|सित?\.?|अक्टूबर|अक्टू\.?|नवम्बर|नवंबर?|दिसम्बर|दिसंबर?)/i,monthsShortStrictRegex:/^(जन\.?|फ़र\.?|मार्च?|अप्रै\.?|मई?|जून?|जुल\.?|अग\.?|सित\.?|अक्टू\.?|नव\.?|दिस\.?)/i,calendar:{sameDay:"[आज] LT",nextDay:"[कल] LT",nextWeek:"dddd, LT",lastDay:"[कल] LT",lastWeek:"[पिछले] dddd, LT",sameElse:"L"},relativeTime:{future:"%s में",past:"%s पहले",s:"कुछ ही क्षण",ss:"%d सेकंड",m:"एक मिनट",mm:"%d मिनट",h:"एक घंटा",hh:"%d घंटे",d:"एक दिन",dd:"%d दिन",M:"एक महीने",MM:"%d महीने",y:"एक वर्ष",yy:"%d वर्ष"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/रात|सुबह|दोपहर|शाम/,meridiemHour:function(e,t){return(12===e&&(e=0),"रात"===t)?e<4?e:e+12:"सुबह"===t?e:"दोपहर"===t?e>=10?e:e+12:"शाम"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"रात":e<10?"सुबह":e<17?"दोपहर":e<20?"शाम":"रात"},week:{dow:0,doy:6}})})(n(30381))},30316:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n){var r=e+" ";switch(n){case"ss":return 1===e?r+="sekunda":2===e||3===e||4===e?r+="sekunde":r+="sekundi",r;case"m":return t?"jedna minuta":"jedne minute";case"mm":return 1===e?r+="minuta":2===e||3===e||4===e?r+="minute":r+="minuta",r;case"h":return t?"jedan sat":"jednog sata";case"hh":return 1===e?r+="sat":2===e||3===e||4===e?r+="sata":r+="sati",r;case"dd":return 1===e?r+="dan":r+="dana",r;case"MM":return 1===e?r+="mjesec":2===e||3===e||4===e?r+="mjeseca":r+="mjeseci",r;case"yy":return 1===e?r+="godina":2===e||3===e||4===e?r+="godine":r+="godina",r}}return e.defineLocale("hr",{months:{format:"siječnja_veljače_ožujka_travnja_svibnja_lipnja_srpnja_kolovoza_rujna_listopada_studenoga_prosinca".split("_"),standalone:"siječanj_veljača_ožujak_travanj_svibanj_lipanj_srpanj_kolovoz_rujan_listopad_studeni_prosinac".split("_")},monthsShort:"sij._velj._ožu._tra._svi._lip._srp._kol._ruj._lis._stu._pro.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"Do MMMM YYYY",LLL:"Do MMMM YYYY H:mm",LLLL:"dddd, Do MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[jučer u] LT",lastWeek:function(){switch(this.day()){case 0:return"[prošlu] [nedjelju] [u] LT";case 3:return"[prošlu] [srijedu] [u] LT";case 6:return"[prošle] [subote] [u] LT";case 1:case 2:case 4:case 5:return"[prošli] dddd [u] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"par sekundi",ss:t,m:t,mm:t,h:t,hh:t,d:"dan",dd:t,M:"mjesec",MM:t,y:"godinu",yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},22138:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="vas\xe1rnap h\xe9tfőn kedden szerd\xe1n cs\xfct\xf6rt\xf6k\xf6n p\xe9nteken szombaton".split(" ");function n(e,t,n,r){var i=e;switch(n){case"s":return r||t?"n\xe9h\xe1ny m\xe1sodperc":"n\xe9h\xe1ny m\xe1sodperce";case"ss":return i+(r||t)?" m\xe1sodperc":" m\xe1sodperce";case"m":return"egy"+(r||t?" perc":" perce");case"mm":return i+(r||t?" perc":" perce");case"h":return"egy"+(r||t?" \xf3ra":" \xf3r\xe1ja");case"hh":return i+(r||t?" \xf3ra":" \xf3r\xe1ja");case"d":return"egy"+(r||t?" nap":" napja");case"dd":return i+(r||t?" nap":" napja");case"M":return"egy"+(r||t?" h\xf3nap":" h\xf3napja");case"MM":return i+(r||t?" h\xf3nap":" h\xf3napja");case"y":return"egy"+(r||t?" \xe9v":" \xe9ve");case"yy":return i+(r||t?" \xe9v":" \xe9ve")}return""}function r(e){return(e?"":"[m\xfalt] ")+"["+t[this.day()]+"] LT[-kor]"}return e.defineLocale("hu",{months:"janu\xe1r_febru\xe1r_m\xe1rcius_\xe1prilis_m\xe1jus_j\xfanius_j\xfalius_augusztus_szeptember_okt\xf3ber_november_december".split("_"),monthsShort:"jan._feb._m\xe1rc._\xe1pr._m\xe1j._j\xfan._j\xfal._aug._szept._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"vas\xe1rnap_h\xe9tfő_kedd_szerda_cs\xfct\xf6rt\xf6k_p\xe9ntek_szombat".split("_"),weekdaysShort:"vas_h\xe9t_kedd_sze_cs\xfct_p\xe9n_szo".split("_"),weekdaysMin:"v_h_k_sze_cs_p_szo".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY. MMMM D.",LLL:"YYYY. MMMM D. H:mm",LLLL:"YYYY. MMMM D., dddd H:mm"},meridiemParse:/de|du/i,isPM:function(e){return"u"===e.charAt(1).toLowerCase()},meridiem:function(e,t,n){return e<12?!0===n?"de":"DE":!0===n?"du":"DU"},calendar:{sameDay:"[ma] LT[-kor]",nextDay:"[holnap] LT[-kor]",nextWeek:function(){return r.call(this,!0)},lastDay:"[tegnap] LT[-kor]",lastWeek:function(){return r.call(this,!1)},sameElse:"L"},relativeTime:{future:"%s m\xfalva",past:"%s",s:n,ss:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},11423:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("hy-am",{months:{format:"հունվարի_փետրվարի_մարտի_ապրիլի_մայիսի_հունիսի_հուլիսի_օգոստոսի_սեպտեմբերի_հոկտեմբերի_նոյեմբերի_դեկտեմբերի".split("_"),standalone:"հունվար_փետրվար_մարտ_ապրիլ_մայիս_հունիս_հուլիս_օգոստոս_սեպտեմբեր_հոկտեմբեր_նոյեմբեր_դեկտեմբեր".split("_")},monthsShort:"հնվ_փտր_մրտ_ապր_մյս_հնս_հլս_օգս_սպտ_հկտ_նմբ_դկտ".split("_"),weekdays:"կիրակի_երկուշաբթի_երեքշաբթի_չորեքշաբթի_հինգշաբթի_ուրբաթ_շաբաթ".split("_"),weekdaysShort:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),weekdaysMin:"կրկ_երկ_երք_չրք_հնգ_ուրբ_շբթ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY թ.",LLL:"D MMMM YYYY թ., HH:mm",LLLL:"dddd, D MMMM YYYY թ., HH:mm"},calendar:{sameDay:"[այսօր] LT",nextDay:"[վաղը] LT",lastDay:"[երեկ] LT",nextWeek:function(){return"dddd [օրը ժամը] LT"},lastWeek:function(){return"[անցած] dddd [օրը ժամը] LT"},sameElse:"L"},relativeTime:{future:"%s հետո",past:"%s առաջ",s:"մի քանի վայրկյան",ss:"%d վայրկյան",m:"րոպե",mm:"%d րոպե",h:"ժամ",hh:"%d ժամ",d:"օր",dd:"%d օր",M:"ամիս",MM:"%d ամիս",y:"տարի",yy:"%d տարի"},meridiemParse:/գիշերվա|առավոտվա|ցերեկվա|երեկոյան/,isPM:function(e){return/^(ցերեկվա|երեկոյան)$/.test(e)},meridiem:function(e){return e<4?"գիշերվա":e<12?"առավոտվա":e<17?"ցերեկվա":"երեկոյան"},dayOfMonthOrdinalParse:/\d{1,2}|\d{1,2}-(ին|րդ)/,ordinal:function(e,t){switch(t){case"DDD":case"w":case"W":case"DDDo":if(1===e)return e+"-ին";return e+"-րդ";default:return e}},week:{dow:1,doy:7}})})(n(30381))},29218:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("id",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_November_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Agt_Sep_Okt_Nov_Des".split("_"),weekdays:"Minggu_Senin_Selasa_Rabu_Kamis_Jumat_Sabtu".split("_"),weekdaysShort:"Min_Sen_Sel_Rab_Kam_Jum_Sab".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|siang|sore|malam/,meridiemHour:function(e,t){return(12===e&&(e=0),"pagi"===t)?e:"siang"===t?e>=11?e:e+12:"sore"===t||"malam"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"pagi":e<15?"siang":e<19?"sore":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Besok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kemarin pukul] LT",lastWeek:"dddd [lalu pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lalu",s:"beberapa detik",ss:"%d detik",m:"semenit",mm:"%d menit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:0,doy:6}})})(n(30381))},90135:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e){if(e%100==11);else if(e%10==1)return!1;return!0}function n(e,n,r,i){var a=e+" ";switch(r){case"s":return n||i?"nokkrar sek\xfandur":"nokkrum sek\xfandum";case"ss":if(t(e))return a+(n||i?"sek\xfandur":"sek\xfandum");return a+"sek\xfanda";case"m":return n?"m\xedn\xfata":"m\xedn\xfatu";case"mm":if(t(e))return a+(n||i?"m\xedn\xfatur":"m\xedn\xfatum");if(n)return a+"m\xedn\xfata";return a+"m\xedn\xfatu";case"hh":if(t(e))return a+(n||i?"klukkustundir":"klukkustundum");return a+"klukkustund";case"d":if(n)return"dagur";return i?"dag":"degi";case"dd":if(t(e)){if(n)return a+"dagar";return a+(i?"daga":"d\xf6gum")}if(n)return a+"dagur";return a+(i?"dag":"degi");case"M":if(n)return"m\xe1nu\xf0ur";return i?"m\xe1nu\xf0":"m\xe1nu\xf0i";case"MM":if(t(e)){if(n)return a+"m\xe1nu\xf0ir";return a+(i?"m\xe1nu\xf0i":"m\xe1nu\xf0um")}if(n)return a+"m\xe1nu\xf0ur";return a+(i?"m\xe1nu\xf0":"m\xe1nu\xf0i");case"y":return n||i?"\xe1r":"\xe1ri";case"yy":if(t(e))return a+(n||i?"\xe1r":"\xe1rum");return a+(n||i?"\xe1r":"\xe1ri")}}return e.defineLocale("is",{months:"jan\xfaar_febr\xfaar_mars_apr\xedl_ma\xed_j\xfan\xed_j\xfal\xed_\xe1g\xfast_september_okt\xf3ber_n\xf3vember_desember".split("_"),monthsShort:"jan_feb_mar_apr_ma\xed_j\xfan_j\xfal_\xe1g\xfa_sep_okt_n\xf3v_des".split("_"),weekdays:"sunnudagur_m\xe1nudagur_\xferi\xf0judagur_mi\xf0vikudagur_fimmtudagur_f\xf6studagur_laugardagur".split("_"),weekdaysShort:"sun_m\xe1n_\xferi_mi\xf0_fim_f\xf6s_lau".split("_"),weekdaysMin:"Su_M\xe1_\xder_Mi_Fi_F\xf6_La".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd, D. MMMM YYYY [kl.] H:mm"},calendar:{sameDay:"[\xed dag kl.] LT",nextDay:"[\xe1 morgun kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[\xed g\xe6r kl.] LT",lastWeek:"[s\xed\xf0asta] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"eftir %s",past:"fyrir %s s\xed\xf0an",s:n,ss:n,m:n,mm:n,h:"klukkustund",hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},10150:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("it-ch",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_luned\xec_marted\xec_mercoled\xec_gioved\xec_venerd\xec_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[Oggi alle] LT",nextDay:"[Domani alle] LT",nextWeek:"dddd [alle] LT",lastDay:"[Ieri alle] LT",lastWeek:function(){return 0===this.day()?"[la scorsa] dddd [alle] LT":"[lo scorso] dddd [alle] LT"},sameElse:"L"},relativeTime:{future:function(e){return(/^[0-9].+$/.test(e)?"tra":"in")+" "+e},past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},90626:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("it",{months:"gennaio_febbraio_marzo_aprile_maggio_giugno_luglio_agosto_settembre_ottobre_novembre_dicembre".split("_"),monthsShort:"gen_feb_mar_apr_mag_giu_lug_ago_set_ott_nov_dic".split("_"),weekdays:"domenica_luned\xec_marted\xec_mercoled\xec_gioved\xec_venerd\xec_sabato".split("_"),weekdaysShort:"dom_lun_mar_mer_gio_ven_sab".split("_"),weekdaysMin:"do_lu_ma_me_gi_ve_sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:function(){return"[Oggi a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},nextDay:function(){return"[Domani a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},nextWeek:function(){return"dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},lastDay:function(){return"[Ieri a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},lastWeek:function(){return 0===this.day()?"[La scorsa] dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT":"[Lo scorso] dddd [a"+(this.hours()>1?"lle ":0===this.hours()?" ":"ll'")+"]LT"},sameElse:"L"},relativeTime:{future:"tra %s",past:"%s fa",s:"alcuni secondi",ss:"%d secondi",m:"un minuto",mm:"%d minuti",h:"un'ora",hh:"%d ore",d:"un giorno",dd:"%d giorni",w:"una settimana",ww:"%d settimane",M:"un mese",MM:"%d mesi",y:"un anno",yy:"%d anni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},39183:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ja",{eras:[{since:"2019-05-01",offset:1,name:"令和",narrow:"㋿",abbr:"R"},{since:"1989-01-08",until:"2019-04-30",offset:1,name:"平成",narrow:"㍻",abbr:"H"},{since:"1926-12-25",until:"1989-01-07",offset:1,name:"昭和",narrow:"㍼",abbr:"S"},{since:"1912-07-30",until:"1926-12-24",offset:1,name:"大正",narrow:"㍽",abbr:"T"},{since:"1873-01-01",until:"1912-07-29",offset:6,name:"明治",narrow:"㍾",abbr:"M"},{since:"0001-01-01",until:"1873-12-31",offset:1,name:"西暦",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"紀元前",narrow:"BC",abbr:"BC"},],eraYearOrdinalRegex:/(元|\d+)年/,eraYearOrdinalParse:function(e,t){return"元"===t[1]?1:parseInt(t[1]||e,10)},months:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"日曜日_月曜日_火曜日_水曜日_木曜日_金曜日_土曜日".split("_"),weekdaysShort:"日_月_火_水_木_金_土".split("_"),weekdaysMin:"日_月_火_水_木_金_土".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日 dddd HH:mm",l:"YYYY/MM/DD",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日(ddd) HH:mm"},meridiemParse:/午前|午後/i,isPM:function(e){return"午後"===e},meridiem:function(e,t,n){return e<12?"午前":"午後"},calendar:{sameDay:"[今日] LT",nextDay:"[明日] LT",nextWeek:function(e){return e.week()!==this.week()?"[来週]dddd LT":"dddd LT"},lastDay:"[昨日] LT",lastWeek:function(e){return this.week()!==e.week()?"[先週]dddd LT":"dddd LT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}日/,ordinal:function(e,t){switch(t){case"y":return 1===e?"元年":e+"年";case"d":case"D":case"DDD":return e+"日";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"数秒",ss:"%d秒",m:"1分",mm:"%d分",h:"1時間",hh:"%d時間",d:"1日",dd:"%d日",M:"1ヶ月",MM:"%dヶ月",y:"1年",yy:"%d年"}})})(n(30381))},24286:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("jv",{months:"Januari_Februari_Maret_April_Mei_Juni_Juli_Agustus_September_Oktober_Nopember_Desember".split("_"),monthsShort:"Jan_Feb_Mar_Apr_Mei_Jun_Jul_Ags_Sep_Okt_Nop_Des".split("_"),weekdays:"Minggu_Senen_Seloso_Rebu_Kemis_Jemuwah_Septu".split("_"),weekdaysShort:"Min_Sen_Sel_Reb_Kem_Jem_Sep".split("_"),weekdaysMin:"Mg_Sn_Sl_Rb_Km_Jm_Sp".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/enjing|siyang|sonten|ndalu/,meridiemHour:function(e,t){return(12===e&&(e=0),"enjing"===t)?e:"siyang"===t?e>=11?e:e+12:"sonten"===t||"ndalu"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"enjing":e<15?"siyang":e<19?"sonten":"ndalu"},calendar:{sameDay:"[Dinten puniko pukul] LT",nextDay:"[Mbenjang pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kala wingi pukul] LT",lastWeek:"dddd [kepengker pukul] LT",sameElse:"L"},relativeTime:{future:"wonten ing %s",past:"%s ingkang kepengker",s:"sawetawis detik",ss:"%d detik",m:"setunggal menit",mm:"%d menit",h:"setunggal jam",hh:"%d jam",d:"sedinten",dd:"%d dinten",M:"sewulan",MM:"%d wulan",y:"setaun",yy:"%d taun"},week:{dow:1,doy:7}})})(n(30381))},12105:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ka",{months:"იანვარი_თებერვალი_მარტი_აპრილი_მაისი_ივნისი_ივლისი_აგვისტო_სექტემბერი_ოქტომბერი_ნოემბერი_დეკემბერი".split("_"),monthsShort:"იან_თებ_მარ_აპრ_მაი_ივნ_ივლ_აგვ_სექ_ოქტ_ნოე_დეკ".split("_"),weekdays:{standalone:"კვირა_ორშაბათი_სამშაბათი_ოთხშაბათი_ხუთშაბათი_პარასკევი_შაბათი".split("_"),format:"კვირას_ორშაბათს_სამშაბათს_ოთხშაბათს_ხუთშაბათს_პარასკევს_შაბათს".split("_"),isFormat:/(წინა|შემდეგ)/},weekdaysShort:"კვი_ორშ_სამ_ოთხ_ხუთ_პარ_შაბ".split("_"),weekdaysMin:"კვ_ორ_სა_ოთ_ხუ_პა_შა".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[დღეს] LT[-ზე]",nextDay:"[ხვალ] LT[-ზე]",lastDay:"[გუშინ] LT[-ზე]",nextWeek:"[შემდეგ] dddd LT[-ზე]",lastWeek:"[წინა] dddd LT-ზე",sameElse:"L"},relativeTime:{future:function(e){return e.replace(/(წამ|წუთ|საათ|წელ|დღ|თვ)(ი|ე)/,function(e,t,n){return"ი"===n?t+"ში":t+n+"ში"})},past:function(e){return/(წამი|წუთი|საათი|დღე|თვე)/.test(e)?e.replace(/(ი|ე)$/,"ის წინ"):/წელი/.test(e)?e.replace(/წელი$/,"წლის წინ"):e},s:"რამდენიმე წამი",ss:"%d წამი",m:"წუთი",mm:"%d წუთი",h:"საათი",hh:"%d საათი",d:"დღე",dd:"%d დღე",M:"თვე",MM:"%d თვე",y:"წელი",yy:"%d წელი"},dayOfMonthOrdinalParse:/0|1-ლი|მე-\d{1,2}|\d{1,2}-ე/,ordinal:function(e){return 0===e?e:1===e?e+"-ლი":e<20||e<=100&&e%20==0||e%100==0?"მე-"+e:e+"-ე"},week:{dow:1,doy:7}})})(n(30381))},47772:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={0:"-ші",1:"-ші",2:"-ші",3:"-ші",4:"-ші",5:"-ші",6:"-шы",7:"-ші",8:"-ші",9:"-шы",10:"-шы",20:"-шы",30:"-шы",40:"-шы",50:"-ші",60:"-шы",70:"-ші",80:"-ші",90:"-шы",100:"-ші"};return e.defineLocale("kk",{months:"қаңтар_ақпан_наурыз_сәуір_мамыр_маусым_шілде_тамыз_қыркүйек_қазан_қараша_желтоқсан".split("_"),monthsShort:"қаң_ақп_нау_сәу_мам_мау_шіл_там_қыр_қаз_қар_жел".split("_"),weekdays:"жексенбі_дүйсенбі_сейсенбі_сәрсенбі_бейсенбі_жұма_сенбі".split("_"),weekdaysShort:"жек_дүй_сей_сәр_бей_жұм_сен".split("_"),weekdaysMin:"жк_дй_сй_ср_бй_жм_сн".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгін сағат] LT",nextDay:"[Ертең сағат] LT",nextWeek:"dddd [сағат] LT",lastDay:"[Кеше сағат] LT",lastWeek:"[Өткен аптаның] dddd [сағат] LT",sameElse:"L"},relativeTime:{future:"%s ішінде",past:"%s бұрын",s:"бірнеше секунд",ss:"%d секунд",m:"бір минут",mm:"%d минут",h:"бір сағат",hh:"%d сағат",d:"бір күн",dd:"%d күн",M:"бір ай",MM:"%d ай",y:"бір жыл",yy:"%d жыл"},dayOfMonthOrdinalParse:/\d{1,2}-(ші|шы)/,ordinal:function(e){var n=e%10,r=e>=100?100:null;return e+(t[e]||t[n]||t[r])},week:{dow:1,doy:7}})})(n(30381))},18758:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"១",2:"២",3:"៣",4:"៤",5:"៥",6:"៦",7:"៧",8:"៨",9:"៩",0:"០"},n={"១":"1","២":"2","៣":"3","៤":"4","៥":"5","៦":"6","៧":"7","៨":"8","៩":"9","០":"0"};return e.defineLocale("km",{months:"មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),monthsShort:"មករា_កុម្ភៈ_មីនា_មេសា_ឧសភា_មិថុនា_កក្កដា_សីហា_កញ្ញា_តុលា_វិច្ឆិកា_ធ្នូ".split("_"),weekdays:"អាទិត្យ_ច័ន្ទ_អង្គារ_ពុធ_ព្រហស្បតិ៍_សុក្រ_សៅរ៍".split("_"),weekdaysShort:"អា_ច_អ_ព_ព្រ_សុ_ស".split("_"),weekdaysMin:"អា_ច_អ_ព_ព្រ_សុ_ស".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/ព្រឹក|ល្ងាច/,isPM:function(e){return"ល្ងាច"===e},meridiem:function(e,t,n){return e<12?"ព្រឹក":"ល្ងាច"},calendar:{sameDay:"[ថ្ងៃនេះ ម៉ោង] LT",nextDay:"[ស្អែក ម៉ោង] LT",nextWeek:"dddd [ម៉ោង] LT",lastDay:"[ម្សិលមិញ ម៉ោង] LT",lastWeek:"dddd [សប្តាហ៍មុន] [ម៉ោង] LT",sameElse:"L"},relativeTime:{future:"%sទៀត",past:"%sមុន",s:"ប៉ុន្មានវិនាទី",ss:"%d វិនាទី",m:"មួយនាទី",mm:"%d នាទី",h:"មួយម៉ោង",hh:"%d ម៉ោង",d:"មួយថ្ងៃ",dd:"%d ថ្ងៃ",M:"មួយខែ",MM:"%d ខែ",y:"មួយឆ្នាំ",yy:"%d ឆ្នាំ"},dayOfMonthOrdinalParse:/ទី\d{1,2}/,ordinal:"ទី%d",preparse:function(e){return e.replace(/[១២៣៤៥៦៧៨៩០]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},week:{dow:1,doy:4}})})(n(30381))},79282:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"೧",2:"೨",3:"೩",4:"೪",5:"೫",6:"೬",7:"೭",8:"೮",9:"೯",0:"೦"},n={"೧":"1","೨":"2","೩":"3","೪":"4","೫":"5","೬":"6","೭":"7","೮":"8","೯":"9","೦":"0"};return e.defineLocale("kn",{months:"ಜನವರಿ_ಫೆಬ್ರವರಿ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂಬರ್_ಅಕ್ಟೋಬರ್_ನವೆಂಬರ್_ಡಿಸೆಂಬರ್".split("_"),monthsShort:"ಜನ_ಫೆಬ್ರ_ಮಾರ್ಚ್_ಏಪ್ರಿಲ್_ಮೇ_ಜೂನ್_ಜುಲೈ_ಆಗಸ್ಟ್_ಸೆಪ್ಟೆಂ_ಅಕ್ಟೋ_ನವೆಂ_ಡಿಸೆಂ".split("_"),monthsParseExact:!0,weekdays:"ಭಾನುವಾರ_ಸೋಮವಾರ_ಮಂಗಳವಾರ_ಬುಧವಾರ_ಗುರುವಾರ_ಶುಕ್ರವಾರ_ಶನಿವಾರ".split("_"),weekdaysShort:"ಭಾನು_ಸೋಮ_ಮಂಗಳ_ಬುಧ_ಗುರು_ಶುಕ್ರ_ಶನಿ".split("_"),weekdaysMin:"ಭಾ_ಸೋ_ಮಂ_ಬು_ಗು_ಶು_ಶ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[ಇಂದು] LT",nextDay:"[ನಾಳೆ] LT",nextWeek:"dddd, LT",lastDay:"[ನಿನ್ನೆ] LT",lastWeek:"[ಕೊನೆಯ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ನಂತರ",past:"%s ಹಿಂದೆ",s:"ಕೆಲವು ಕ್ಷಣಗಳು",ss:"%d ಸೆಕೆಂಡುಗಳು",m:"ಒಂದು ನಿಮಿಷ",mm:"%d ನಿಮಿಷ",h:"ಒಂದು ಗಂಟೆ",hh:"%d ಗಂಟೆ",d:"ಒಂದು ದಿನ",dd:"%d ದಿನ",M:"ಒಂದು ತಿಂಗಳು",MM:"%d ತಿಂಗಳು",y:"ಒಂದು ವರ್ಷ",yy:"%d ವರ್ಷ"},preparse:function(e){return e.replace(/[೧೨೩೪೫೬೭೮೯೦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ರಾತ್ರಿ|ಬೆಳಿಗ್ಗೆ|ಮಧ್ಯಾಹ್ನ|ಸಂಜೆ/,meridiemHour:function(e,t){return(12===e&&(e=0),"ರಾತ್ರಿ"===t)?e<4?e:e+12:"ಬೆಳಿಗ್ಗೆ"===t?e:"ಮಧ್ಯಾಹ್ನ"===t?e>=10?e:e+12:"ಸಂಜೆ"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"ರಾತ್ರಿ":e<10?"ಬೆಳಿಗ್ಗೆ":e<17?"ಮಧ್ಯಾಹ್ನ":e<20?"ಸಂಜೆ":"ರಾತ್ರಿ"},dayOfMonthOrdinalParse:/\d{1,2}(ನೇ)/,ordinal:function(e){return e+"ನೇ"},week:{dow:0,doy:6}})})(n(30381))},33730:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"YYYY.MM.DD.",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 A h:mm",LLLL:"YYYY년 MMMM D일 dddd A h:mm",l:"YYYY.MM.DD.",ll:"YYYY년 MMMM D일",lll:"YYYY년 MMMM D일 A h:mm",llll:"YYYY년 MMMM D일 dddd A h:mm"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇 초",ss:"%d초",m:"1분",mm:"%d분",h:"한 시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한 달",MM:"%d달",y:"일 년",yy:"%d년"},dayOfMonthOrdinalParse:/\d{1,2}(일|월|주)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"일";case"M":return e+"월";case"w":case"W":return e+"주";default:return e}},meridiemParse:/오전|오후/,isPM:function(e){return"오후"===e},meridiem:function(e,t,n){return e<12?"오전":"오후"}})})(n(30381))},1408:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"١",2:"٢",3:"٣",4:"٤",5:"٥",6:"٦",7:"٧",8:"٨",9:"٩",0:"٠"},n={"١":"1","٢":"2","٣":"3","٤":"4","٥":"5","٦":"6","٧":"7","٨":"8","٩":"9","٠":"0"},r=["کانونی دووەم","شوبات","ئازار","نیسان","ئایار","حوزەیران","تەمموز","ئاب","ئەیلوول","تشرینی یەكەم","تشرینی دووەم","كانونی یەکەم",];return e.defineLocale("ku",{months:r,monthsShort:r,weekdays:"یه‌كشه‌ممه‌_دووشه‌ممه‌_سێشه‌ممه‌_چوارشه‌ممه‌_پێنجشه‌ممه‌_هه‌ینی_شه‌ممه‌".split("_"),weekdaysShort:"یه‌كشه‌م_دووشه‌م_سێشه‌م_چوارشه‌م_پێنجشه‌م_هه‌ینی_شه‌ممه‌".split("_"),weekdaysMin:"ی_د_س_چ_پ_ه_ش".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},meridiemParse:/ئێواره‌|به‌یانی/,isPM:function(e){return/ئێواره‌/.test(e)},meridiem:function(e,t,n){return e<12?"به‌یانی":"ئێواره‌"},calendar:{sameDay:"[ئه‌مرۆ كاتژمێر] LT",nextDay:"[به‌یانی كاتژمێر] LT",nextWeek:"dddd [كاتژمێر] LT",lastDay:"[دوێنێ كاتژمێر] LT",lastWeek:"dddd [كاتژمێر] LT",sameElse:"L"},relativeTime:{future:"له‌ %s",past:"%s",s:"چه‌ند چركه‌یه‌ك",ss:"چركه‌ %d",m:"یه‌ك خوله‌ك",mm:"%d خوله‌ك",h:"یه‌ك كاتژمێر",hh:"%d كاتژمێر",d:"یه‌ك ڕۆژ",dd:"%d ڕۆژ",M:"یه‌ك مانگ",MM:"%d مانگ",y:"یه‌ك ساڵ",yy:"%d ساڵ"},preparse:function(e){return e.replace(/[١٢٣٤٥٦٧٨٩٠]/g,function(e){return n[e]}).replace(/،/g,",")},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]}).replace(/,/g,"،")},week:{dow:6,doy:12}})})(n(30381))},33291:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={0:"-чү",1:"-чи",2:"-чи",3:"-чү",4:"-чү",5:"-чи",6:"-чы",7:"-чи",8:"-чи",9:"-чу",10:"-чу",20:"-чы",30:"-чу",40:"-чы",50:"-чү",60:"-чы",70:"-чи",80:"-чи",90:"-чу",100:"-чү"};return e.defineLocale("ky",{months:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_"),monthsShort:"янв_фев_март_апр_май_июнь_июль_авг_сен_окт_ноя_дек".split("_"),weekdays:"Жекшемби_Дүйшөмбү_Шейшемби_Шаршемби_Бейшемби_Жума_Ишемби".split("_"),weekdaysShort:"Жек_Дүй_Шей_Шар_Бей_Жум_Ише".split("_"),weekdaysMin:"Жк_Дй_Шй_Шр_Бй_Жм_Иш".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Бүгүн саат] LT",nextDay:"[Эртең саат] LT",nextWeek:"dddd [саат] LT",lastDay:"[Кечээ саат] LT",lastWeek:"[Өткөн аптанын] dddd [күнү] [саат] LT",sameElse:"L"},relativeTime:{future:"%s ичинде",past:"%s мурун",s:"бирнече секунд",ss:"%d секунд",m:"бир мүнөт",mm:"%d мүнөт",h:"бир саат",hh:"%d саат",d:"бир күн",dd:"%d күн",M:"бир ай",MM:"%d ай",y:"бир жыл",yy:"%d жыл"},dayOfMonthOrdinalParse:/\d{1,2}-(чи|чы|чү|чу)/,ordinal:function(e){var n=e%10,r=e>=100?100:null;return e+(t[e]||t[n]||t[r])},week:{dow:1,doy:7}})})(n(30381))},36841:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i={m:["eng Minutt","enger Minutt"],h:["eng Stonn","enger Stonn"],d:["een Dag","engem Dag"],M:["ee Mount","engem Mount"],y:["ee Joer","engem Joer"]};return t?i[n][0]:i[n][1]}function n(e){return i(e.substr(0,e.indexOf(" ")))?"a "+e:"an "+e}function r(e){return i(e.substr(0,e.indexOf(" ")))?"viru "+e:"virun "+e}function i(e){if(e=parseInt(e,10),isNaN(e))return!1;if(e<0)return!0;if(e<10)return!!(4<=e)&&!!(e<=7);if(e<100){var t=e%10,n=e/10;return 0===t?i(n):i(t)}if(!(e<1e4))return i(e/=1e3);for(;e>=10;)e/=10;return i(e)}return e.defineLocale("lb",{months:"Januar_Februar_M\xe4erz_Abr\xebll_Mee_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Abr._Mee_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),monthsParseExact:!0,weekdays:"Sonndeg_M\xe9indeg_D\xebnschdeg_M\xebttwoch_Donneschdeg_Freideg_Samschdeg".split("_"),weekdaysShort:"So._M\xe9._D\xeb._M\xeb._Do._Fr._Sa.".split("_"),weekdaysMin:"So_M\xe9_D\xeb_M\xeb_Do_Fr_Sa".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm [Auer]",LTS:"H:mm:ss [Auer]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm [Auer]",LLLL:"dddd, D. MMMM YYYY H:mm [Auer]"},calendar:{sameDay:"[Haut um] LT",sameElse:"L",nextDay:"[Muer um] LT",nextWeek:"dddd [um] LT",lastDay:"[G\xebschter um] LT",lastWeek:function(){switch(this.day()){case 2:case 4:return"[Leschten] dddd [um] LT";default:return"[Leschte] dddd [um] LT"}}},relativeTime:{future:n,past:r,s:"e puer Sekonnen",ss:"%d Sekonnen",m:t,mm:"%d Minutten",h:t,hh:"%d Stonnen",d:t,dd:"%d Deeg",M:t,MM:"%d M\xe9int",y:t,yy:"%d Joer"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},55466:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("lo",{months:"ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ".split("_"),monthsShort:"ມັງກອນ_ກຸມພາ_ມີນາ_ເມສາ_ພຶດສະພາ_ມິຖຸນາ_ກໍລະກົດ_ສິງຫາ_ກັນຍາ_ຕຸລາ_ພະຈິກ_ທັນວາ".split("_"),weekdays:"ອາທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ".split("_"),weekdaysShort:"ທິດ_ຈັນ_ອັງຄານ_ພຸດ_ພະຫັດ_ສຸກ_ເສົາ".split("_"),weekdaysMin:"ທ_ຈ_ອຄ_ພ_ພຫ_ສກ_ສ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"ວັນdddd D MMMM YYYY HH:mm"},meridiemParse:/ຕອນເຊົ້າ|ຕອນແລງ/,isPM:function(e){return"ຕອນແລງ"===e},meridiem:function(e,t,n){return e<12?"ຕອນເຊົ້າ":"ຕອນແລງ"},calendar:{sameDay:"[ມື້ນີ້ເວລາ] LT",nextDay:"[ມື້ອື່ນເວລາ] LT",nextWeek:"[ວັນ]dddd[ໜ້າເວລາ] LT",lastDay:"[ມື້ວານນີ້ເວລາ] LT",lastWeek:"[ວັນ]dddd[ແລ້ວນີ້ເວລາ] LT",sameElse:"L"},relativeTime:{future:"ອີກ %s",past:"%sຜ່ານມາ",s:"ບໍ່ເທົ່າໃດວິນາທີ",ss:"%d ວິນາທີ",m:"1 ນາທີ",mm:"%d ນາທີ",h:"1 ຊົ່ວໂມງ",hh:"%d ຊົ່ວໂມງ",d:"1 ມື້",dd:"%d ມື້",M:"1 ເດືອນ",MM:"%d ເດືອນ",y:"1 ປີ",yy:"%d ປີ"},dayOfMonthOrdinalParse:/(ທີ່)\d{1,2}/,ordinal:function(e){return"ທີ່"+e}})})(n(30381))},57010:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={ss:"sekundė_sekundžių_sekundes",m:"minutė_minutės_minutę",mm:"minutės_minučių_minutes",h:"valanda_valandos_valandą",hh:"valandos_valandų_valandas",d:"diena_dienos_dieną",dd:"dienos_dienų_dienas",M:"mėnuo_mėnesio_mėnesį",MM:"mėnesiai_mėnesių_mėnesius",y:"metai_metų_metus",yy:"metai_metų_metus"};function n(e,t,n,r){return t?"kelios sekundės":r?"kelių sekundžių":"kelias sekundes"}function r(e,t,n,r){return t?a(n)[0]:r?a(n)[1]:a(n)[2]}function i(e){return e%10==0||e>10&&e<20}function a(e){return t[e].split("_")}function o(e,t,n,o){var s=e+" ";return 1===e?s+r(e,t,n[0],o):t?s+(i(e)?a(n)[1]:a(n)[0]):o?s+a(n)[1]:s+(i(e)?a(n)[1]:a(n)[2])}return e.defineLocale("lt",{months:{format:"sausio_vasario_kovo_balandžio_gegužės_birželio_liepos_rugpjūčio_rugsėjo_spalio_lapkričio_gruodžio".split("_"),standalone:"sausis_vasaris_kovas_balandis_gegužė_birželis_liepa_rugpjūtis_rugsėjis_spalis_lapkritis_gruodis".split("_"),isFormat:/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?|MMMM?(\[[^\[\]]*\]|\s)+D[oD]?/},monthsShort:"sau_vas_kov_bal_geg_bir_lie_rgp_rgs_spa_lap_grd".split("_"),weekdays:{format:"sekmadienį_pirmadienį_antradienį_trečiadienį_ketvirtadienį_penktadienį_šeštadienį".split("_"),standalone:"sekmadienis_pirmadienis_antradienis_trečiadienis_ketvirtadienis_penktadienis_šeštadienis".split("_"),isFormat:/dddd HH:mm/},weekdaysShort:"Sek_Pir_Ant_Tre_Ket_Pen_Šeš".split("_"),weekdaysMin:"S_P_A_T_K_Pn_Š".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY [m.] MMMM D [d.]",LLL:"YYYY [m.] MMMM D [d.], HH:mm [val.]",LLLL:"YYYY [m.] MMMM D [d.], dddd, HH:mm [val.]",l:"YYYY-MM-DD",ll:"YYYY [m.] MMMM D [d.]",lll:"YYYY [m.] MMMM D [d.], HH:mm [val.]",llll:"YYYY [m.] MMMM D [d.], ddd, HH:mm [val.]"},calendar:{sameDay:"[Šiandien] LT",nextDay:"[Rytoj] LT",nextWeek:"dddd LT",lastDay:"[Vakar] LT",lastWeek:"[Praėjusį] dddd LT",sameElse:"L"},relativeTime:{future:"po %s",past:"prieš %s",s:n,ss:o,m:r,mm:o,h:r,hh:o,d:r,dd:o,M:r,MM:o,y:r,yy:o},dayOfMonthOrdinalParse:/\d{1,2}-oji/,ordinal:function(e){return e+"-oji"},week:{dow:1,doy:4}})})(n(30381))},37595:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={ss:"sekundes_sekundēm_sekunde_sekundes".split("_"),m:"minūtes_minūtēm_minūte_minūtes".split("_"),mm:"minūtes_minūtēm_minūte_minūtes".split("_"),h:"stundas_stundām_stunda_stundas".split("_"),hh:"stundas_stundām_stunda_stundas".split("_"),d:"dienas_dienām_diena_dienas".split("_"),dd:"dienas_dienām_diena_dienas".split("_"),M:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),MM:"mēneša_mēnešiem_mēnesis_mēneši".split("_"),y:"gada_gadiem_gads_gadi".split("_"),yy:"gada_gadiem_gads_gadi".split("_")};function n(e,t,n){return n?t%10==1&&t%100!=11?e[2]:e[3]:t%10==1&&t%100!=11?e[0]:e[1]}function r(e,r,i){return e+" "+n(t[i],e,r)}function i(e,r,i){return n(t[i],e,r)}function a(e,t){return t?"dažas sekundes":"dažām sekundēm"}return e.defineLocale("lv",{months:"janvāris_februāris_marts_aprīlis_maijs_jūnijs_jūlijs_augusts_septembris_oktobris_novembris_decembris".split("_"),monthsShort:"jan_feb_mar_apr_mai_jūn_jūl_aug_sep_okt_nov_dec".split("_"),weekdays:"svētdiena_pirmdiena_otrdiena_trešdiena_ceturtdiena_piektdiena_sestdiena".split("_"),weekdaysShort:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysMin:"Sv_P_O_T_C_Pk_S".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY.",LL:"YYYY. [gada] D. MMMM",LLL:"YYYY. [gada] D. MMMM, HH:mm",LLLL:"YYYY. [gada] D. MMMM, dddd, HH:mm"},calendar:{sameDay:"[Šodien pulksten] LT",nextDay:"[Rīt pulksten] LT",nextWeek:"dddd [pulksten] LT",lastDay:"[Vakar pulksten] LT",lastWeek:"[Pagājušā] dddd [pulksten] LT",sameElse:"L"},relativeTime:{future:"pēc %s",past:"pirms %s",s:a,ss:r,m:i,mm:r,h:i,hh:r,d:i,dd:r,M:i,MM:r,y:i,yy:r},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},39861:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={words:{ss:["sekund","sekunda","sekundi"],m:["jedan minut","jednog minuta"],mm:["minut","minuta","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mjesec","mjeseca","mjeseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&e<=4?t[1]:t[2]},translate:function(e,n,r){var i=t.words[r];return 1===r.length?n?i[0]:i[1]:e+" "+t.correctGrammaticalCase(e,i)}};return e.defineLocale("me",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sri._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sjutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedjelju] [u] LT";case 3:return"[u] [srijedu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){return["[prošle] [nedjelje] [u] LT","[prošlog] [ponedjeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srijede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT",][this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"prije %s",s:"nekoliko sekundi",ss:t.translate,m:t.translate,mm:t.translate,h:t.translate,hh:t.translate,d:"dan",dd:t.translate,M:"mjesec",MM:t.translate,y:"godinu",yy:t.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},35493:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("mi",{months:"Kohi-tāte_Hui-tanguru_Poutū-te-rangi_Paenga-whāwhā_Haratua_Pipiri_Hōngoingoi_Here-turi-kōkā_Mahuru_Whiringa-ā-nuku_Whiringa-ā-rangi_Hakihea".split("_"),monthsShort:"Kohi_Hui_Pou_Pae_Hara_Pipi_Hōngoi_Here_Mahu_Whi-nu_Whi-ra_Haki".split("_"),monthsRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,3}/i,monthsShortStrictRegex:/(?:['a-z\u0101\u014D\u016B]+\-?){1,2}/i,weekdays:"Rātapu_Mane_Tūrei_Wenerei_Tāite_Paraire_Hātarei".split("_"),weekdaysShort:"Ta_Ma_Tū_We_Tāi_Pa_Hā".split("_"),weekdaysMin:"Ta_Ma_Tū_We_Tāi_Pa_Hā".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [i] HH:mm",LLLL:"dddd, D MMMM YYYY [i] HH:mm"},calendar:{sameDay:"[i teie mahana, i] LT",nextDay:"[apopo i] LT",nextWeek:"dddd [i] LT",lastDay:"[inanahi i] LT",lastWeek:"dddd [whakamutunga i] LT",sameElse:"L"},relativeTime:{future:"i roto i %s",past:"%s i mua",s:"te hēkona ruarua",ss:"%d hēkona",m:"he meneti",mm:"%d meneti",h:"te haora",hh:"%d haora",d:"he ra",dd:"%d ra",M:"he marama",MM:"%d marama",y:"he tau",yy:"%d tau"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},95966:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("mk",{months:"јануари_февруари_март_април_мај_јуни_јули_август_септември_октомври_ноември_декември".split("_"),monthsShort:"јан_фев_мар_апр_мај_јун_јул_авг_сеп_окт_ное_дек".split("_"),weekdays:"недела_понеделник_вторник_среда_четврток_петок_сабота".split("_"),weekdaysShort:"нед_пон_вто_сре_чет_пет_саб".split("_"),weekdaysMin:"нe_пo_вт_ср_че_пе_сa".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[Денес во] LT",nextDay:"[Утре во] LT",nextWeek:"[Во] dddd [во] LT",lastDay:"[Вчера во] LT",lastWeek:function(){switch(this.day()){case 0:case 3:case 6:return"[Изминатата] dddd [во] LT";case 1:case 2:case 4:case 5:return"[Изминатиот] dddd [во] LT"}},sameElse:"L"},relativeTime:{future:"за %s",past:"пред %s",s:"неколку секунди",ss:"%d секунди",m:"една минута",mm:"%d минути",h:"еден час",hh:"%d часа",d:"еден ден",dd:"%d дена",M:"еден месец",MM:"%d месеци",y:"една година",yy:"%d години"},dayOfMonthOrdinalParse:/\d{1,2}-(ев|ен|ти|ви|ри|ми)/,ordinal:function(e){var t=e%10,n=e%100;if(0===e)return e+"-ев";if(0===n)return e+"-ен";if(n>10&&n<20)return e+"-ти";if(1===t)return e+"-ви";if(2===t)return e+"-ри";else if(7===t||8===t)return e+"-ми";else return e+"-ти"},week:{dow:1,doy:7}})})(n(30381))},87341:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ml",{months:"ജനുവരി_ഫെബ്രുവരി_മാർച്ച്_ഏപ്രിൽ_മേയ്_ജൂൺ_ജൂലൈ_ഓഗസ്റ്റ്_സെപ്റ്റംബർ_ഒക്ടോബർ_നവംബർ_ഡിസംബർ".split("_"),monthsShort:"ജനു._ഫെബ്രു._മാർ._ഏപ്രി._മേയ്_ജൂൺ_ജൂലൈ._ഓഗ._സെപ്റ്റ._ഒക്ടോ._നവം._ഡിസം.".split("_"),monthsParseExact:!0,weekdays:"ഞായറാഴ്ച_തിങ്കളാഴ്ച_ചൊവ്വാഴ്ച_ബുധനാഴ്ച_വ്യാഴാഴ്ച_വെള്ളിയാഴ്ച_ശനിയാഴ്ച".split("_"),weekdaysShort:"ഞായർ_തിങ്കൾ_ചൊവ്വ_ബുധൻ_വ്യാഴം_വെള്ളി_ശനി".split("_"),weekdaysMin:"ഞാ_തി_ചൊ_ബു_വ്യാ_വെ_ശ".split("_"),longDateFormat:{LT:"A h:mm -നു",LTS:"A h:mm:ss -നു",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm -നു",LLLL:"dddd, D MMMM YYYY, A h:mm -നു"},calendar:{sameDay:"[ഇന്ന്] LT",nextDay:"[നാളെ] LT",nextWeek:"dddd, LT",lastDay:"[ഇന്നലെ] LT",lastWeek:"[കഴിഞ്ഞ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s കഴിഞ്ഞ്",past:"%s മുൻപ്",s:"അൽപ നിമിഷങ്ങൾ",ss:"%d സെക്കൻഡ്",m:"ഒരു മിനിറ്റ്",mm:"%d മിനിറ്റ്",h:"ഒരു മണിക്കൂർ",hh:"%d മണിക്കൂർ",d:"ഒരു ദിവസം",dd:"%d ദിവസം",M:"ഒരു മാസം",MM:"%d മാസം",y:"ഒരു വർഷം",yy:"%d വർഷം"},meridiemParse:/രാത്രി|രാവിലെ|ഉച്ച കഴിഞ്ഞ്|വൈകുന്നേരം|രാത്രി/i,meridiemHour:function(e,t){return(12===e&&(e=0),"രാത്രി"===t&&e>=4||"ഉച്ച കഴിഞ്ഞ്"===t||"വൈകുന്നേരം"===t)?e+12:e},meridiem:function(e,t,n){return e<4?"രാത്രി":e<12?"രാവിലെ":e<17?"ഉച്ച കഴിഞ്ഞ്":e<20?"വൈകുന്നേരം":"രാത്രി"}})})(n(30381))},5115:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){switch(n){case"s":return t?"хэдхэн секунд":"хэдхэн секундын";case"ss":return e+(t?" секунд":" секундын");case"m":case"mm":return e+(t?" минут":" минутын");case"h":case"hh":return e+(t?" цаг":" цагийн");case"d":case"dd":return e+(t?" өдөр":" өдрийн");case"M":case"MM":return e+(t?" сар":" сарын");case"y":case"yy":return e+(t?" жил":" жилийн");default:return e}}return e.defineLocale("mn",{months:"Нэгдүгээр сар_Хоёрдугаар сар_Гуравдугаар сар_Дөрөвдүгээр сар_Тавдугаар сар_Зургадугаар сар_Долдугаар сар_Наймдугаар сар_Есдүгээр сар_Аравдугаар сар_Арван нэгдүгээр сар_Арван хоёрдугаар сар".split("_"),monthsShort:"1 сар_2 сар_3 сар_4 сар_5 сар_6 сар_7 сар_8 сар_9 сар_10 сар_11 сар_12 сар".split("_"),monthsParseExact:!0,weekdays:"Ням_Даваа_Мягмар_Лхагва_Пүрэв_Баасан_Бямба".split("_"),weekdaysShort:"Ням_Дав_Мяг_Лха_Пүр_Баа_Бям".split("_"),weekdaysMin:"Ня_Да_Мя_Лх_Пү_Ба_Бя".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY оны MMMMын D",LLL:"YYYY оны MMMMын D HH:mm",LLLL:"dddd, YYYY оны MMMMын D HH:mm"},meridiemParse:/ҮӨ|ҮХ/i,isPM:function(e){return"ҮХ"===e},meridiem:function(e,t,n){return e<12?"ҮӨ":"ҮХ"},calendar:{sameDay:"[Өнөөдөр] LT",nextDay:"[Маргааш] LT",nextWeek:"[Ирэх] dddd LT",lastDay:"[Өчигдөр] LT",lastWeek:"[Өнгөрсөн] dddd LT",sameElse:"L"},relativeTime:{future:"%s дараа",past:"%s өмнө",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2} өдөр/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+" өдөр";default:return e}}})})(n(30381))},10370:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},n={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};function r(e,t,n,r){var i="";if(t)switch(n){case"s":i="काही सेकंद";break;case"ss":i="%d सेकंद";break;case"m":i="एक मिनिट";break;case"mm":i="%d मिनिटे";break;case"h":i="एक तास";break;case"hh":i="%d तास";break;case"d":i="एक दिवस";break;case"dd":i="%d दिवस";break;case"M":i="एक महिना";break;case"MM":i="%d महिने";break;case"y":i="एक वर्ष";break;case"yy":i="%d वर्षे"}else switch(n){case"s":i="काही सेकंदां";break;case"ss":i="%d सेकंदां";break;case"m":i="एका मिनिटा";break;case"mm":i="%d मिनिटां";break;case"h":i="एका तासा";break;case"hh":i="%d तासां";break;case"d":i="एका दिवसा";break;case"dd":i="%d दिवसां";break;case"M":i="एका महिन्या";break;case"MM":i="%d महिन्यां";break;case"y":i="एका वर्षा";break;case"yy":i="%d वर्षां"}return i.replace(/%d/i,e)}return e.defineLocale("mr",{months:"जानेवारी_फेब्रुवारी_मार्च_एप्रिल_मे_जून_जुलै_ऑगस्ट_सप्टेंबर_ऑक्टोबर_नोव्हेंबर_डिसेंबर".split("_"),monthsShort:"जाने._फेब्रु._मार्च._एप्रि._मे._जून._जुलै._ऑग._सप्टें._ऑक्टो._नोव्हें._डिसें.".split("_"),monthsParseExact:!0,weekdays:"रविवार_सोमवार_मंगळवार_बुधवार_गुरूवार_शुक्रवार_शनिवार".split("_"),weekdaysShort:"रवि_सोम_मंगळ_बुध_गुरू_शुक्र_शनि".split("_"),weekdaysMin:"र_सो_मं_बु_गु_शु_श".split("_"),longDateFormat:{LT:"A h:mm वाजता",LTS:"A h:mm:ss वाजता",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm वाजता",LLLL:"dddd, D MMMM YYYY, A h:mm वाजता"},calendar:{sameDay:"[आज] LT",nextDay:"[उद्या] LT",nextWeek:"dddd, LT",lastDay:"[काल] LT",lastWeek:"[मागील] dddd, LT",sameElse:"L"},relativeTime:{future:"%sमध्ये",past:"%sपूर्वी",s:r,ss:r,m:r,mm:r,h:r,hh:r,d:r,dd:r,M:r,MM:r,y:r,yy:r},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/पहाटे|सकाळी|दुपारी|सायंकाळी|रात्री/,meridiemHour:function(e,t){return(12===e&&(e=0),"पहाटे"===t||"सकाळी"===t)?e:"दुपारी"===t||"सायंकाळी"===t||"रात्री"===t?e>=12?e:e+12:void 0},meridiem:function(e,t,n){return e>=0&&e<6?"पहाटे":e<12?"सकाळी":e<17?"दुपारी":e<20?"सायंकाळी":"रात्री"},week:{dow:0,doy:6}})})(n(30381))},41237:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ms-my",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){return(12===e&&(e=0),"pagi"===t)?e:"tengahari"===t?e>=11?e:e+12:"petang"===t||"malam"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})})(n(30381))},9847:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ms",{months:"Januari_Februari_Mac_April_Mei_Jun_Julai_Ogos_September_Oktober_November_Disember".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ogs_Sep_Okt_Nov_Dis".split("_"),weekdays:"Ahad_Isnin_Selasa_Rabu_Khamis_Jumaat_Sabtu".split("_"),weekdaysShort:"Ahd_Isn_Sel_Rab_Kha_Jum_Sab".split("_"),weekdaysMin:"Ah_Is_Sl_Rb_Km_Jm_Sb".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [pukul] HH.mm",LLLL:"dddd, D MMMM YYYY [pukul] HH.mm"},meridiemParse:/pagi|tengahari|petang|malam/,meridiemHour:function(e,t){return(12===e&&(e=0),"pagi"===t)?e:"tengahari"===t?e>=11?e:e+12:"petang"===t||"malam"===t?e+12:void 0},meridiem:function(e,t,n){return e<11?"pagi":e<15?"tengahari":e<19?"petang":"malam"},calendar:{sameDay:"[Hari ini pukul] LT",nextDay:"[Esok pukul] LT",nextWeek:"dddd [pukul] LT",lastDay:"[Kelmarin pukul] LT",lastWeek:"dddd [lepas pukul] LT",sameElse:"L"},relativeTime:{future:"dalam %s",past:"%s yang lepas",s:"beberapa saat",ss:"%d saat",m:"seminit",mm:"%d minit",h:"sejam",hh:"%d jam",d:"sehari",dd:"%d hari",M:"sebulan",MM:"%d bulan",y:"setahun",yy:"%d tahun"},week:{dow:1,doy:7}})})(n(30381))},72126:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("mt",{months:"Jannar_Frar_Marzu_April_Mejju_Ġunju_Lulju_Awwissu_Settembru_Ottubru_Novembru_Diċembru".split("_"),monthsShort:"Jan_Fra_Mar_Apr_Mej_Ġun_Lul_Aww_Set_Ott_Nov_Diċ".split("_"),weekdays:"Il-Ħadd_It-Tnejn_It-Tlieta_L-Erbgħa_Il-Ħamis_Il-Ġimgħa_Is-Sibt".split("_"),weekdaysShort:"Ħad_Tne_Tli_Erb_Ħam_Ġim_Sib".split("_"),weekdaysMin:"Ħa_Tn_Tl_Er_Ħa_Ġi_Si".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Illum fil-]LT",nextDay:"[Għada fil-]LT",nextWeek:"dddd [fil-]LT",lastDay:"[Il-bieraħ fil-]LT",lastWeek:"dddd [li għadda] [fil-]LT",sameElse:"L"},relativeTime:{future:"f’ %s",past:"%s ilu",s:"ftit sekondi",ss:"%d sekondi",m:"minuta",mm:"%d minuti",h:"siegħa",hh:"%d siegħat",d:"ġurnata",dd:"%d ġranet",M:"xahar",MM:"%d xhur",y:"sena",yy:"%d sni"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},56165:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"၁",2:"၂",3:"၃",4:"၄",5:"၅",6:"၆",7:"၇",8:"၈",9:"၉",0:"၀"},n={"၁":"1","၂":"2","၃":"3","၄":"4","၅":"5","၆":"6","၇":"7","၈":"8","၉":"9","၀":"0"};return e.defineLocale("my",{months:"ဇန်နဝါရီ_ဖေဖော်ဝါရီ_မတ်_ဧပြီ_မေ_ဇွန်_ဇူလိုင်_သြဂုတ်_စက်တင်ဘာ_အောက်တိုဘာ_နိုဝင်ဘာ_ဒီဇင်ဘာ".split("_"),monthsShort:"ဇန်_ဖေ_မတ်_ပြီ_မေ_ဇွန်_လိုင်_သြ_စက်_အောက်_နို_ဒီ".split("_"),weekdays:"တနင်္ဂနွေ_တနင်္လာ_အင်္ဂါ_ဗုဒ္ဓဟူး_ကြာသပတေး_သောကြာ_စနေ".split("_"),weekdaysShort:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),weekdaysMin:"နွေ_လာ_ဂါ_ဟူး_ကြာ_သော_နေ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ယနေ.] LT [မှာ]",nextDay:"[မနက်ဖြန်] LT [မှာ]",nextWeek:"dddd LT [မှာ]",lastDay:"[မနေ.က] LT [မှာ]",lastWeek:"[ပြီးခဲ့သော] dddd LT [မှာ]",sameElse:"L"},relativeTime:{future:"လာမည့် %s မှာ",past:"လွန်ခဲ့သော %s က",s:"စက္ကန်.အနည်းငယ်",ss:"%d စက္ကန့်",m:"တစ်မိနစ်",mm:"%d မိနစ်",h:"တစ်နာရီ",hh:"%d နာရီ",d:"တစ်ရက်",dd:"%d ရက်",M:"တစ်လ",MM:"%d လ",y:"တစ်နှစ်",yy:"%d နှစ်"},preparse:function(e){return e.replace(/[၁၂၃၄၅၆၇၈၉၀]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},week:{dow:1,doy:4}})})(n(30381))},64924:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("nb",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"s\xf8ndag_mandag_tirsdag_onsdag_torsdag_fredag_l\xf8rdag".split("_"),weekdaysShort:"s\xf8._ma._ti._on._to._fr._l\xf8.".split("_"),weekdaysMin:"s\xf8_ma_ti_on_to_fr_l\xf8".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] HH:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[i dag kl.] LT",nextDay:"[i morgen kl.] LT",nextWeek:"dddd [kl.] LT",lastDay:"[i g\xe5r kl.] LT",lastWeek:"[forrige] dddd [kl.] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s siden",s:"noen sekunder",ss:"%d sekunder",m:"ett minutt",mm:"%d minutter",h:"en time",hh:"%d timer",d:"en dag",dd:"%d dager",w:"en uke",ww:"%d uker",M:"en m\xe5ned",MM:"%d m\xe5neder",y:"ett \xe5r",yy:"%d \xe5r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},16744:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"१",2:"२",3:"३",4:"४",5:"५",6:"६",7:"७",8:"८",9:"९",0:"०"},n={"१":"1","२":"2","३":"3","४":"4","५":"5","६":"6","७":"7","८":"8","९":"9","०":"0"};return e.defineLocale("ne",{months:"जनवरी_फेब्रुवरी_मार्च_अप्रिल_मई_जुन_जुलाई_अगष्ट_सेप्टेम्बर_अक्टोबर_नोभेम्बर_डिसेम्बर".split("_"),monthsShort:"जन._फेब्रु._मार्च_अप्रि._मई_जुन_जुलाई._अग._सेप्ट._अक्टो._नोभे._डिसे.".split("_"),monthsParseExact:!0,weekdays:"आइतबार_सोमबार_मङ्गलबार_बुधबार_बिहिबार_शुक्रबार_शनिबार".split("_"),weekdaysShort:"आइत._सोम._मङ्गल._बुध._बिहि._शुक्र._शनि.".split("_"),weekdaysMin:"आ._सो._मं._बु._बि._शु._श.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"Aको h:mm बजे",LTS:"Aको h:mm:ss बजे",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, Aको h:mm बजे",LLLL:"dddd, D MMMM YYYY, Aको h:mm बजे"},preparse:function(e){return e.replace(/[१२३४५६७८९०]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/राति|बिहान|दिउँसो|साँझ/,meridiemHour:function(e,t){return(12===e&&(e=0),"राति"===t)?e<4?e:e+12:"बिहान"===t?e:"दिउँसो"===t?e>=10?e:e+12:"साँझ"===t?e+12:void 0},meridiem:function(e,t,n){return e<3?"राति":e<12?"बिहान":e<16?"दिउँसो":e<20?"साँझ":"राति"},calendar:{sameDay:"[आज] LT",nextDay:"[भोलि] LT",nextWeek:"[आउँदो] dddd[,] LT",lastDay:"[हिजो] LT",lastWeek:"[गएको] dddd[,] LT",sameElse:"L"},relativeTime:{future:"%sमा",past:"%s अगाडि",s:"केही क्षण",ss:"%d सेकेण्ड",m:"एक मिनेट",mm:"%d मिनेट",h:"एक घण्टा",hh:"%d घण्टा",d:"एक दिन",dd:"%d दिन",M:"एक महिना",MM:"%d महिना",y:"एक बर्ष",yy:"%d बर्ष"},week:{dow:0,doy:6}})})(n(30381))},59814:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),n="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),r=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i,],i=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;return e.defineLocale("nl-be",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"\xe9\xe9n minuut",mm:"%d minuten",h:"\xe9\xe9n uur",hh:"%d uur",d:"\xe9\xe9n dag",dd:"%d dagen",M:"\xe9\xe9n maand",MM:"%d maanden",y:"\xe9\xe9n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})})(n(30381))},93901:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),n="jan_feb_mrt_apr_mei_jun_jul_aug_sep_okt_nov_dec".split("_"),r=[/^jan/i,/^feb/i,/^maart|mrt.?$/i,/^apr/i,/^mei$/i,/^jun[i.]?$/i,/^jul[i.]?$/i,/^aug/i,/^sep/i,/^okt/i,/^nov/i,/^dec/i,],i=/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december|jan\.?|feb\.?|mrt\.?|apr\.?|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i;return e.defineLocale("nl",{months:"januari_februari_maart_april_mei_juni_juli_augustus_september_oktober_november_december".split("_"),monthsShort:function(e,r){return e?/-MMM-/.test(r)?n[e.month()]:t[e.month()]:t},monthsRegex:i,monthsShortRegex:i,monthsStrictRegex:/^(januari|februari|maart|april|mei|ju[nl]i|augustus|september|oktober|november|december)/i,monthsShortStrictRegex:/^(jan\.?|feb\.?|mrt\.?|apr\.?|mei|ju[nl]\.?|aug\.?|sep\.?|okt\.?|nov\.?|dec\.?)/i,monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"zondag_maandag_dinsdag_woensdag_donderdag_vrijdag_zaterdag".split("_"),weekdaysShort:"zo._ma._di._wo._do._vr._za.".split("_"),weekdaysMin:"zo_ma_di_wo_do_vr_za".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD-MM-YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[vandaag om] LT",nextDay:"[morgen om] LT",nextWeek:"dddd [om] LT",lastDay:"[gisteren om] LT",lastWeek:"[afgelopen] dddd [om] LT",sameElse:"L"},relativeTime:{future:"over %s",past:"%s geleden",s:"een paar seconden",ss:"%d seconden",m:"\xe9\xe9n minuut",mm:"%d minuten",h:"\xe9\xe9n uur",hh:"%d uur",d:"\xe9\xe9n dag",dd:"%d dagen",w:"\xe9\xe9n week",ww:"%d weken",M:"\xe9\xe9n maand",MM:"%d maanden",y:"\xe9\xe9n jaar",yy:"%d jaar"},dayOfMonthOrdinalParse:/\d{1,2}(ste|de)/,ordinal:function(e){return e+(1===e||8===e||e>=20?"ste":"de")},week:{dow:1,doy:4}})})(n(30381))},83877:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("nn",{months:"januar_februar_mars_april_mai_juni_juli_august_september_oktober_november_desember".split("_"),monthsShort:"jan._feb._mars_apr._mai_juni_juli_aug._sep._okt._nov._des.".split("_"),monthsParseExact:!0,weekdays:"sundag_m\xe5ndag_tysdag_onsdag_torsdag_fredag_laurdag".split("_"),weekdaysShort:"su._m\xe5._ty._on._to._fr._lau.".split("_"),weekdaysMin:"su_m\xe5_ty_on_to_fr_la".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY [kl.] H:mm",LLLL:"dddd D. MMMM YYYY [kl.] HH:mm"},calendar:{sameDay:"[I dag klokka] LT",nextDay:"[I morgon klokka] LT",nextWeek:"dddd [klokka] LT",lastDay:"[I g\xe5r klokka] LT",lastWeek:"[F\xf8reg\xe5ande] dddd [klokka] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"%s sidan",s:"nokre sekund",ss:"%d sekund",m:"eit minutt",mm:"%d minutt",h:"ein time",hh:"%d timar",d:"ein dag",dd:"%d dagar",w:"ei veke",ww:"%d veker",M:"ein m\xe5nad",MM:"%d m\xe5nader",y:"eit \xe5r",yy:"%d \xe5r"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},92135:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("oc-lnc",{months:{standalone:"geni\xe8r_febri\xe8r_mar\xe7_abril_mai_junh_julhet_agost_setembre_oct\xf2bre_novembre_decembre".split("_"),format:"de geni\xe8r_de febri\xe8r_de mar\xe7_d'abril_de mai_de junh_de julhet_d'agost_de setembre_d'oct\xf2bre_de novembre_de decembre".split("_"),isFormat:/D[oD]?(\s)+MMMM/},monthsShort:"gen._febr._mar\xe7_abr._mai_junh_julh._ago._set._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"dimenge_diluns_dimars_dim\xe8cres_dij\xf2us_divendres_dissabte".split("_"),weekdaysShort:"dg._dl._dm._dc._dj._dv._ds.".split("_"),weekdaysMin:"dg_dl_dm_dc_dj_dv_ds".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [de] YYYY",ll:"D MMM YYYY",LLL:"D MMMM [de] YYYY [a] H:mm",lll:"D MMM YYYY, H:mm",LLLL:"dddd D MMMM [de] YYYY [a] H:mm",llll:"ddd D MMM YYYY, H:mm"},calendar:{sameDay:"[u\xe8i a] LT",nextDay:"[deman a] LT",nextWeek:"dddd [a] LT",lastDay:"[i\xe8r a] LT",lastWeek:"dddd [passat a] LT",sameElse:"L"},relativeTime:{future:"d'aqu\xed %s",past:"fa %s",s:"unas segondas",ss:"%d segondas",m:"una minuta",mm:"%d minutas",h:"una ora",hh:"%d oras",d:"un jorn",dd:"%d jorns",M:"un mes",MM:"%d meses",y:"un an",yy:"%d ans"},dayOfMonthOrdinalParse:/\d{1,2}(r|n|t|è|a)/,ordinal:function(e,t){var n=1===e?"r":2===e?"n":3===e?"r":4===e?"t":"\xe8";return("w"===t||"W"===t)&&(n="a"),e+n},week:{dow:1,doy:4}})})(n(30381))},15858:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"੧",2:"੨",3:"੩",4:"੪",5:"੫",6:"੬",7:"੭",8:"੮",9:"੯",0:"੦"},n={"੧":"1","੨":"2","੩":"3","੪":"4","੫":"5","੬":"6","੭":"7","੮":"8","੯":"9","੦":"0"};return e.defineLocale("pa-in",{months:"ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ".split("_"),monthsShort:"ਜਨਵਰੀ_ਫ਼ਰਵਰੀ_ਮਾਰਚ_ਅਪ੍ਰੈਲ_ਮਈ_ਜੂਨ_ਜੁਲਾਈ_ਅਗਸਤ_ਸਤੰਬਰ_ਅਕਤੂਬਰ_ਨਵੰਬਰ_ਦਸੰਬਰ".split("_"),weekdays:"ਐਤਵਾਰ_ਸੋਮਵਾਰ_ਮੰਗਲਵਾਰ_ਬੁਧਵਾਰ_ਵੀਰਵਾਰ_ਸ਼ੁੱਕਰਵਾਰ_ਸ਼ਨੀਚਰਵਾਰ".split("_"),weekdaysShort:"ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ".split("_"),weekdaysMin:"ਐਤ_ਸੋਮ_ਮੰਗਲ_ਬੁਧ_ਵੀਰ_ਸ਼ੁਕਰ_ਸ਼ਨੀ".split("_"),longDateFormat:{LT:"A h:mm ਵਜੇ",LTS:"A h:mm:ss ਵਜੇ",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm ਵਜੇ",LLLL:"dddd, D MMMM YYYY, A h:mm ਵਜੇ"},calendar:{sameDay:"[ਅਜ] LT",nextDay:"[ਕਲ] LT",nextWeek:"[ਅਗਲਾ] dddd, LT",lastDay:"[ਕਲ] LT",lastWeek:"[ਪਿਛਲੇ] dddd, LT",sameElse:"L"},relativeTime:{future:"%s ਵਿੱਚ",past:"%s ਪਿਛਲੇ",s:"ਕੁਝ ਸਕਿੰਟ",ss:"%d ਸਕਿੰਟ",m:"ਇਕ ਮਿੰਟ",mm:"%d ਮਿੰਟ",h:"ਇੱਕ ਘੰਟਾ",hh:"%d ਘੰਟੇ",d:"ਇੱਕ ਦਿਨ",dd:"%d ਦਿਨ",M:"ਇੱਕ ਮਹੀਨਾ",MM:"%d ਮਹੀਨੇ",y:"ਇੱਕ ਸਾਲ",yy:"%d ਸਾਲ"},preparse:function(e){return e.replace(/[੧੨੩੪੫੬੭੮੯੦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/ਰਾਤ|ਸਵੇਰ|ਦੁਪਹਿਰ|ਸ਼ਾਮ/,meridiemHour:function(e,t){return(12===e&&(e=0),"ਰਾਤ"===t)?e<4?e:e+12:"ਸਵੇਰ"===t?e:"ਦੁਪਹਿਰ"===t?e>=10?e:e+12:"ਸ਼ਾਮ"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"ਰਾਤ":e<10?"ਸਵੇਰ":e<17?"ਦੁਪਹਿਰ":e<20?"ਸ਼ਾਮ":"ਰਾਤ"},week:{dow:0,doy:6}})})(n(30381))},64495:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="styczeń_luty_marzec_kwiecień_maj_czerwiec_lipiec_sierpień_wrzesień_październik_listopad_grudzień".split("_"),n="stycznia_lutego_marca_kwietnia_maja_czerwca_lipca_sierpnia_września_października_listopada_grudnia".split("_"),r=[/^sty/i,/^lut/i,/^mar/i,/^kwi/i,/^maj/i,/^cze/i,/^lip/i,/^sie/i,/^wrz/i,/^paź/i,/^lis/i,/^gru/i,];function i(e){return e%10<5&&e%10>1&&~~(e/10)%10!=1}function a(e,t,n){var r=e+" ";switch(n){case"ss":return r+(i(e)?"sekundy":"sekund");case"m":return t?"minuta":"minutę";case"mm":return r+(i(e)?"minuty":"minut");case"h":return t?"godzina":"godzinę";case"hh":return r+(i(e)?"godziny":"godzin");case"ww":return r+(i(e)?"tygodnie":"tygodni");case"MM":return r+(i(e)?"miesiące":"miesięcy");case"yy":return r+(i(e)?"lata":"lat")}}return e.defineLocale("pl",{months:function(e,r){return e?/D MMMM/.test(r)?n[e.month()]:t[e.month()]:t},monthsShort:"sty_lut_mar_kwi_maj_cze_lip_sie_wrz_paź_lis_gru".split("_"),monthsParse:r,longMonthsParse:r,shortMonthsParse:r,weekdays:"niedziela_poniedziałek_wtorek_środa_czwartek_piątek_sobota".split("_"),weekdaysShort:"ndz_pon_wt_śr_czw_pt_sob".split("_"),weekdaysMin:"Nd_Pn_Wt_Śr_Cz_Pt_So".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Dziś o] LT",nextDay:"[Jutro o] LT",nextWeek:function(){switch(this.day()){case 0:return"[W niedzielę o] LT";case 2:return"[We wtorek o] LT";case 3:return"[W środę o] LT";case 6:return"[W sobotę o] LT";default:return"[W] dddd [o] LT"}},lastDay:"[Wczoraj o] LT",lastWeek:function(){switch(this.day()){case 0:return"[W zeszłą niedzielę o] LT";case 3:return"[W zeszłą środę o] LT";case 6:return"[W zeszłą sobotę o] LT";default:return"[W zeszły] dddd [o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"%s temu",s:"kilka sekund",ss:a,m:a,mm:a,h:a,hh:a,d:"1 dzień",dd:"%d dni",w:"tydzień",ww:a,M:"miesiąc",MM:a,y:"rok",yy:a},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},57971:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("pt-br",{months:"janeiro_fevereiro_mar\xe7o_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_ter\xe7a-feira_quarta-feira_quinta-feira_sexta-feira_s\xe1bado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_s\xe1b".split("_"),weekdaysMin:"do_2\xaa_3\xaa_4\xaa_5\xaa_6\xaa_s\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY [\xe0s] HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY [\xe0s] HH:mm"},calendar:{sameDay:"[Hoje \xe0s] LT",nextDay:"[Amanh\xe3 \xe0s] LT",nextWeek:"dddd [\xe0s] LT",lastDay:"[Ontem \xe0s] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[\xdaltimo] dddd [\xe0s] LT":"[\xdaltima] dddd [\xe0s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"h\xe1 %s",s:"poucos segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um m\xeas",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",invalidDate:"Data inv\xe1lida"})})(n(30381))},89520:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("pt",{months:"janeiro_fevereiro_mar\xe7o_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"Domingo_Segunda-feira_Ter\xe7a-feira_Quarta-feira_Quinta-feira_Sexta-feira_S\xe1bado".split("_"),weekdaysShort:"Dom_Seg_Ter_Qua_Qui_Sex_S\xe1b".split("_"),weekdaysMin:"Do_2\xaa_3\xaa_4\xaa_5\xaa_6\xaa_S\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY HH:mm",LLLL:"dddd, D [de] MMMM [de] YYYY HH:mm"},calendar:{sameDay:"[Hoje \xe0s] LT",nextDay:"[Amanh\xe3 \xe0s] LT",nextWeek:"dddd [\xe0s] LT",lastDay:"[Ontem \xe0s] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[\xdaltimo] dddd [\xe0s] LT":"[\xdaltima] dddd [\xe0s] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"h\xe1 %s",s:"segundos",ss:"%d segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",w:"uma semana",ww:"%d semanas",M:"um m\xeas",MM:"%d meses",y:"um ano",yy:"%d anos"},dayOfMonthOrdinalParse:/\d{1,2}º/,ordinal:"%d\xba",week:{dow:1,doy:4}})})(n(30381))},96459:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n){var r=" ";return(e%100>=20||e>=100&&e%100==0)&&(r=" de "),e+r+({ss:"secunde",mm:"minute",hh:"ore",dd:"zile",ww:"săptăm\xe2ni",MM:"luni",yy:"ani"})[n]}return e.defineLocale("ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._feb._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"duminică_luni_marți_miercuri_joi_vineri_s\xe2mbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_S\xe2m".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_S\xe2".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[m\xe2ine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s \xeen urmă",s:"c\xe2teva secunde",ss:t,m:"un minut",mm:t,h:"o oră",hh:t,d:"o zi",dd:t,w:"o săptăm\xe2nă",ww:t,M:"o lună",MM:t,y:"un an",yy:t},week:{dow:1,doy:7}})})(n(30381))},21793:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t){var n=e.split("_");return t%10==1&&t%100!=11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function n(e,n,r){var i={ss:n?"секунда_секунды_секунд":"секунду_секунды_секунд",mm:n?"минута_минуты_минут":"минуту_минуты_минут",hh:"час_часа_часов",dd:"день_дня_дней",ww:"неделя_недели_недель",MM:"месяц_месяца_месяцев",yy:"год_года_лет"};return"m"===r?n?"минута":"минуту":e+" "+t(i[r],+e)}var r=[/^янв/i,/^фев/i,/^мар/i,/^апр/i,/^ма[йя]/i,/^июн/i,/^июл/i,/^авг/i,/^сен/i,/^окт/i,/^ноя/i,/^дек/i,];return e.defineLocale("ru",{months:{format:"января_февраля_марта_апреля_мая_июня_июля_августа_сентября_октября_ноября_декабря".split("_"),standalone:"январь_февраль_март_апрель_май_июнь_июль_август_сентябрь_октябрь_ноябрь_декабрь".split("_")},monthsShort:{format:"янв._февр._мар._апр._мая_июня_июля_авг._сент._окт._нояб._дек.".split("_"),standalone:"янв._февр._март_апр._май_июнь_июль_авг._сент._окт._нояб._дек.".split("_")},weekdays:{standalone:"воскресенье_понедельник_вторник_среда_четверг_пятница_суббота".split("_"),format:"воскресенье_понедельник_вторник_среду_четверг_пятницу_субботу".split("_"),isFormat:/\[ ?[Вв] ?(?:прошлую|следующую|эту)? ?] ?dddd/},weekdaysShort:"вс_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"вс_пн_вт_ср_чт_пт_сб".split("_"),monthsParse:r,longMonthsParse:r,shortMonthsParse:r,monthsRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsShortRegex:/^(январ[ья]|янв\.?|феврал[ья]|февр?\.?|марта?|мар\.?|апрел[ья]|апр\.?|ма[йя]|июн[ья]|июн\.?|июл[ья]|июл\.?|августа?|авг\.?|сентябр[ья]|сент?\.?|октябр[ья]|окт\.?|ноябр[ья]|нояб?\.?|декабр[ья]|дек\.?)/i,monthsStrictRegex:/^(январ[яь]|феврал[яь]|марта?|апрел[яь]|ма[яй]|июн[яь]|июл[яь]|августа?|сентябр[яь]|октябр[яь]|ноябр[яь]|декабр[яь])/i,monthsShortStrictRegex:/^(янв\.|февр?\.|мар[т.]|апр\.|ма[яй]|июн[ья.]|июл[ья.]|авг\.|сент?\.|окт\.|нояб?\.|дек\.)/i,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY г.",LLL:"D MMMM YYYY г., H:mm",LLLL:"dddd, D MMMM YYYY г., H:mm"},calendar:{sameDay:"[Сегодня, в] LT",nextDay:"[Завтра, в] LT",lastDay:"[Вчера, в] LT",nextWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd, [в] LT":"[В] dddd, [в] LT";switch(this.day()){case 0:return"[В следующее] dddd, [в] LT";case 1:case 2:case 4:return"[В следующий] dddd, [в] LT";case 3:case 5:case 6:return"[В следующую] dddd, [в] LT"}},lastWeek:function(e){if(e.week()===this.week())return 2===this.day()?"[Во] dddd, [в] LT":"[В] dddd, [в] LT";switch(this.day()){case 0:return"[В прошлое] dddd, [в] LT";case 1:case 2:case 4:return"[В прошлый] dddd, [в] LT";case 3:case 5:case 6:return"[В прошлую] dddd, [в] LT"}},sameElse:"L"},relativeTime:{future:"через %s",past:"%s назад",s:"несколько секунд",ss:n,m:n,mm:n,h:"час",hh:n,d:"день",dd:n,w:"неделя",ww:n,M:"месяц",MM:n,y:"год",yy:n},meridiemParse:/ночи|утра|дня|вечера/i,isPM:function(e){return/^(дня|вечера)$/.test(e)},meridiem:function(e,t,n){return e<4?"ночи":e<12?"утра":e<17?"дня":"вечера"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го|я)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":return e+"-й";case"D":return e+"-го";case"w":case"W":return e+"-я";default:return e}},week:{dow:1,doy:4}})})(n(30381))},40950:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=["جنوري","فيبروري","مارچ","اپريل","مئي","جون","جولاءِ","آگسٽ","سيپٽمبر","آڪٽوبر","نومبر","ڊسمبر",],n=["آچر","سومر","اڱارو","اربع","خميس","جمع","ڇنڇر"];return e.defineLocale("sd",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd، D MMMM YYYY HH:mm"},meridiemParse:/صبح|شام/,isPM:function(e){return"شام"===e},meridiem:function(e,t,n){return e<12?"صبح":"شام"},calendar:{sameDay:"[اڄ] LT",nextDay:"[سڀاڻي] LT",nextWeek:"dddd [اڳين هفتي تي] LT",lastDay:"[ڪالهه] LT",lastWeek:"[گزريل هفتي] dddd [تي] LT",sameElse:"L"},relativeTime:{future:"%s پوء",past:"%s اڳ",s:"چند سيڪنڊ",ss:"%d سيڪنڊ",m:"هڪ منٽ",mm:"%d منٽ",h:"هڪ ڪلاڪ",hh:"%d ڪلاڪ",d:"هڪ ڏينهن",dd:"%d ڏينهن",M:"هڪ مهينو",MM:"%d مهينا",y:"هڪ سال",yy:"%d سال"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:4}})})(n(30381))},10490:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("se",{months:"ođđajagem\xe1nnu_guovvam\xe1nnu_njukčam\xe1nnu_cuoŋom\xe1nnu_miessem\xe1nnu_geassem\xe1nnu_suoidnem\xe1nnu_borgem\xe1nnu_čakčam\xe1nnu_golggotm\xe1nnu_sk\xe1bmam\xe1nnu_juovlam\xe1nnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_sk\xe1b_juov".split("_"),weekdays:"sotnabeaivi_vuoss\xe1rga_maŋŋeb\xe1rga_gaskavahkku_duorastat_bearjadat_l\xe1vvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_l\xe1v".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta m\xe1nnu",MM:"%d m\xe1nut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},90124:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("si",{months:"ජනවාරි_පෙබරවාරි_මාර්තු_අප්‍රේල්_මැයි_ජූනි_ජූලි_අගෝස්තු_සැප්තැම්බර්_ඔක්තෝබර්_නොවැම්බර්_දෙසැම්බර්".split("_"),monthsShort:"ජන_පෙබ_මාර්_අප්_මැයි_ජූනි_ජූලි_අගෝ_සැප්_ඔක්_නොවැ_දෙසැ".split("_"),weekdays:"ඉරිදා_සඳුදා_අඟහරුවාදා_බදාදා_බ්‍රහස්පතින්දා_සිකුරාදා_සෙනසුරාදා".split("_"),weekdaysShort:"ඉරි_සඳු_අඟ_බදා_බ්‍රහ_සිකු_සෙන".split("_"),weekdaysMin:"ඉ_ස_අ_බ_බ්‍ර_සි_සෙ".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"a h:mm",LTS:"a h:mm:ss",L:"YYYY/MM/DD",LL:"YYYY MMMM D",LLL:"YYYY MMMM D, a h:mm",LLLL:"YYYY MMMM D [වැනි] dddd, a h:mm:ss"},calendar:{sameDay:"[අද] LT[ට]",nextDay:"[හෙට] LT[ට]",nextWeek:"dddd LT[ට]",lastDay:"[ඊයේ] LT[ට]",lastWeek:"[පසුගිය] dddd LT[ට]",sameElse:"L"},relativeTime:{future:"%sකින්",past:"%sකට පෙර",s:"තත්පර කිහිපය",ss:"තත්පර %d",m:"මිනිත්තුව",mm:"මිනිත්තු %d",h:"පැය",hh:"පැය %d",d:"දිනය",dd:"දින %d",M:"මාසය",MM:"මාස %d",y:"වසර",yy:"වසර %d"},dayOfMonthOrdinalParse:/\d{1,2} වැනි/,ordinal:function(e){return e+" වැනි"},meridiemParse:/පෙර වරු|පස් වරු|පෙ.ව|ප.ව./,isPM:function(e){return"ප.ව."===e||"පස් වරු"===e},meridiem:function(e,t,n){return e>11?n?"ප.ව.":"පස් වරු":n?"පෙ.ව.":"පෙර වරු"}})})(n(30381))},64249:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="janu\xe1r_febru\xe1r_marec_apr\xedl_m\xe1j_j\xfan_j\xfal_august_september_okt\xf3ber_november_december".split("_"),n="jan_feb_mar_apr_m\xe1j_j\xfan_j\xfal_aug_sep_okt_nov_dec".split("_");function r(e){return e>1&&e<5}function i(e,t,n,i){var a=e+" ";switch(n){case"s":return t||i?"p\xe1r sek\xfand":"p\xe1r sekundami";case"ss":if(t||i)return a+(r(e)?"sekundy":"sek\xfand");return a+"sekundami";case"m":return t?"min\xfata":i?"min\xfatu":"min\xfatou";case"mm":if(t||i)return a+(r(e)?"min\xfaty":"min\xfat");return a+"min\xfatami";case"h":return t?"hodina":i?"hodinu":"hodinou";case"hh":if(t||i)return a+(r(e)?"hodiny":"hod\xedn");return a+"hodinami";case"d":return t||i?"deň":"dňom";case"dd":if(t||i)return a+(r(e)?"dni":"dn\xed");return a+"dňami";case"M":return t||i?"mesiac":"mesiacom";case"MM":if(t||i)return a+(r(e)?"mesiace":"mesiacov");return a+"mesiacmi";case"y":return t||i?"rok":"rokom";case"yy":if(t||i)return a+(r(e)?"roky":"rokov");return a+"rokmi"}}return e.defineLocale("sk",{months:t,monthsShort:n,weekdays:"nedeľa_pondelok_utorok_streda_štvrtok_piatok_sobota".split("_"),weekdaysShort:"ne_po_ut_st_št_pi_so".split("_"),weekdaysMin:"ne_po_ut_st_št_pi_so".split("_"),longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd D. MMMM YYYY H:mm"},calendar:{sameDay:"[dnes o] LT",nextDay:"[zajtra o] LT",nextWeek:function(){switch(this.day()){case 0:return"[v nedeľu o] LT";case 1:case 2:return"[v] dddd [o] LT";case 3:return"[v stredu o] LT";case 4:return"[vo štvrtok o] LT";case 5:return"[v piatok o] LT";case 6:return"[v sobotu o] LT"}},lastDay:"[včera o] LT",lastWeek:function(){switch(this.day()){case 0:return"[minul\xfa nedeľu o] LT";case 1:case 2:case 4:case 5:return"[minul\xfd] dddd [o] LT";case 3:return"[minul\xfa stredu o] LT";case 6:return"[minul\xfa sobotu o] LT"}},sameElse:"L"},relativeTime:{future:"za %s",past:"pred %s",s:i,ss:i,m:i,mm:i,h:i,hh:i,d:i,dd:i,M:i,MM:i,y:i,yy:i},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},14985:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t,n,r){var i=e+" ";switch(n){case"s":return t||r?"nekaj sekund":"nekaj sekundami";case"ss":return 1===e?i+=t?"sekundo":"sekundi":2===e?i+=t||r?"sekundi":"sekundah":e<5?i+=t||r?"sekunde":"sekundah":i+="sekund",i;case"m":return t?"ena minuta":"eno minuto";case"mm":return 1===e?i+=t?"minuta":"minuto":2===e?i+=t||r?"minuti":"minutama":e<5?i+=t||r?"minute":"minutami":i+=t||r?"minut":"minutami",i;case"h":return t?"ena ura":"eno uro";case"hh":return 1===e?i+=t?"ura":"uro":2===e?i+=t||r?"uri":"urama":e<5?i+=t||r?"ure":"urami":i+=t||r?"ur":"urami",i;case"d":return t||r?"en dan":"enim dnem";case"dd":return 1===e?i+=t||r?"dan":"dnem":2===e?i+=t||r?"dni":"dnevoma":i+=t||r?"dni":"dnevi",i;case"M":return t||r?"en mesec":"enim mesecem";case"MM":return 1===e?i+=t||r?"mesec":"mesecem":2===e?i+=t||r?"meseca":"mesecema":e<5?i+=t||r?"mesece":"meseci":i+=t||r?"mesecev":"meseci",i;case"y":return t||r?"eno leto":"enim letom";case"yy":return 1===e?i+=t||r?"leto":"letom":2===e?i+=t||r?"leti":"letoma":e<5?i+=t||r?"leta":"leti":i+=t||r?"let":"leti",i}}return e.defineLocale("sl",{months:"januar_februar_marec_april_maj_junij_julij_avgust_september_oktober_november_december".split("_"),monthsShort:"jan._feb._mar._apr._maj._jun._jul._avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljek_torek_sreda_četrtek_petek_sobota".split("_"),weekdaysShort:"ned._pon._tor._sre._čet._pet._sob.".split("_"),weekdaysMin:"ne_po_to_sr_če_pe_so".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD. MM. YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY H:mm",LLLL:"dddd, D. MMMM YYYY H:mm"},calendar:{sameDay:"[danes ob] LT",nextDay:"[jutri ob] LT",nextWeek:function(){switch(this.day()){case 0:return"[v] [nedeljo] [ob] LT";case 3:return"[v] [sredo] [ob] LT";case 6:return"[v] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[v] dddd [ob] LT"}},lastDay:"[včeraj ob] LT",lastWeek:function(){switch(this.day()){case 0:return"[prejšnjo] [nedeljo] [ob] LT";case 3:return"[prejšnjo] [sredo] [ob] LT";case 6:return"[prejšnjo] [soboto] [ob] LT";case 1:case 2:case 4:case 5:return"[prejšnji] dddd [ob] LT"}},sameElse:"L"},relativeTime:{future:"čez %s",past:"pred %s",s:t,ss:t,m:t,mm:t,h:t,hh:t,d:t,dd:t,M:t,MM:t,y:t,yy:t},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},51104:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("sq",{months:"Janar_Shkurt_Mars_Prill_Maj_Qershor_Korrik_Gusht_Shtator_Tetor_N\xebntor_Dhjetor".split("_"),monthsShort:"Jan_Shk_Mar_Pri_Maj_Qer_Kor_Gus_Sht_Tet_N\xebn_Dhj".split("_"),weekdays:"E Diel_E H\xebn\xeb_E Mart\xeb_E M\xebrkur\xeb_E Enjte_E Premte_E Shtun\xeb".split("_"),weekdaysShort:"Die_H\xebn_Mar_M\xebr_Enj_Pre_Sht".split("_"),weekdaysMin:"D_H_Ma_M\xeb_E_P_Sh".split("_"),weekdaysParseExact:!0,meridiemParse:/PD|MD/,isPM:function(e){return"M"===e.charAt(0)},meridiem:function(e,t,n){return e<12?"PD":"MD"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Sot n\xeb] LT",nextDay:"[Nes\xebr n\xeb] LT",nextWeek:"dddd [n\xeb] LT",lastDay:"[Dje n\xeb] LT",lastWeek:"dddd [e kaluar n\xeb] LT",sameElse:"L"},relativeTime:{future:"n\xeb %s",past:"%s m\xeb par\xeb",s:"disa sekonda",ss:"%d sekonda",m:"nj\xeb minut\xeb",mm:"%d minuta",h:"nj\xeb or\xeb",hh:"%d or\xeb",d:"nj\xeb dit\xeb",dd:"%d dit\xeb",M:"nj\xeb muaj",MM:"%d muaj",y:"nj\xeb vit",yy:"%d vite"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},79915:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={words:{ss:["секунда","секунде","секунди"],m:["један минут","једне минуте"],mm:["минут","минуте","минута"],h:["један сат","једног сата"],hh:["сат","сата","сати"],dd:["дан","дана","дана"],MM:["месец","месеца","месеци"],yy:["година","године","година"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&e<=4?t[1]:t[2]},translate:function(e,n,r){var i=t.words[r];return 1===r.length?n?i[0]:i[1]:e+" "+t.correctGrammaticalCase(e,i)}};return e.defineLocale("sr-cyrl",{months:"јануар_фебруар_март_април_мај_јун_јул_август_септембар_октобар_новембар_децембар".split("_"),monthsShort:"јан._феб._мар._апр._мај_јун_јул_авг._сеп._окт._нов._дец.".split("_"),monthsParseExact:!0,weekdays:"недеља_понедељак_уторак_среда_четвртак_петак_субота".split("_"),weekdaysShort:"нед._пон._уто._сре._чет._пет._суб.".split("_"),weekdaysMin:"не_по_ут_ср_че_пе_су".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[данас у] LT",nextDay:"[сутра у] LT",nextWeek:function(){switch(this.day()){case 0:return"[у] [недељу] [у] LT";case 3:return"[у] [среду] [у] LT";case 6:return"[у] [суботу] [у] LT";case 1:case 2:case 4:case 5:return"[у] dddd [у] LT"}},lastDay:"[јуче у] LT",lastWeek:function(){return["[прошле] [недеље] [у] LT","[прошлог] [понедељка] [у] LT","[прошлог] [уторка] [у] LT","[прошле] [среде] [у] LT","[прошлог] [четвртка] [у] LT","[прошлог] [петка] [у] LT","[прошле] [суботе] [у] LT",][this.day()]},sameElse:"L"},relativeTime:{future:"за %s",past:"пре %s",s:"неколико секунди",ss:t.translate,m:t.translate,mm:t.translate,h:t.translate,hh:t.translate,d:"дан",dd:t.translate,M:"месец",MM:t.translate,y:"годину",yy:t.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},49131:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={words:{ss:["sekunda","sekunde","sekundi"],m:["jedan minut","jedne minute"],mm:["minut","minute","minuta"],h:["jedan sat","jednog sata"],hh:["sat","sata","sati"],dd:["dan","dana","dana"],MM:["mesec","meseca","meseci"],yy:["godina","godine","godina"]},correctGrammaticalCase:function(e,t){return 1===e?t[0]:e>=2&&e<=4?t[1]:t[2]},translate:function(e,n,r){var i=t.words[r];return 1===r.length?n?i[0]:i[1]:e+" "+t.correctGrammaticalCase(e,i)}};return e.defineLocale("sr",{months:"januar_februar_mart_april_maj_jun_jul_avgust_septembar_oktobar_novembar_decembar".split("_"),monthsShort:"jan._feb._mar._apr._maj_jun_jul_avg._sep._okt._nov._dec.".split("_"),monthsParseExact:!0,weekdays:"nedelja_ponedeljak_utorak_sreda_četvrtak_petak_subota".split("_"),weekdaysShort:"ned._pon._uto._sre._čet._pet._sub.".split("_"),weekdaysMin:"ne_po_ut_sr_če_pe_su".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"D. M. YYYY.",LL:"D. MMMM YYYY.",LLL:"D. MMMM YYYY. H:mm",LLLL:"dddd, D. MMMM YYYY. H:mm"},calendar:{sameDay:"[danas u] LT",nextDay:"[sutra u] LT",nextWeek:function(){switch(this.day()){case 0:return"[u] [nedelju] [u] LT";case 3:return"[u] [sredu] [u] LT";case 6:return"[u] [subotu] [u] LT";case 1:case 2:case 4:case 5:return"[u] dddd [u] LT"}},lastDay:"[juče u] LT",lastWeek:function(){return["[prošle] [nedelje] [u] LT","[prošlog] [ponedeljka] [u] LT","[prošlog] [utorka] [u] LT","[prošle] [srede] [u] LT","[prošlog] [četvrtka] [u] LT","[prošlog] [petka] [u] LT","[prošle] [subote] [u] LT",][this.day()]},sameElse:"L"},relativeTime:{future:"za %s",past:"pre %s",s:"nekoliko sekundi",ss:t.translate,m:t.translate,mm:t.translate,h:t.translate,hh:t.translate,d:"dan",dd:t.translate,M:"mesec",MM:t.translate,y:"godinu",yy:t.translate},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:7}})})(n(30381))},85893:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ss",{months:"Bhimbidvwane_Indlovana_Indlov'lenkhulu_Mabasa_Inkhwekhweti_Inhlaba_Kholwane_Ingci_Inyoni_Imphala_Lweti_Ingongoni".split("_"),monthsShort:"Bhi_Ina_Inu_Mab_Ink_Inh_Kho_Igc_Iny_Imp_Lwe_Igo".split("_"),weekdays:"Lisontfo_Umsombuluko_Lesibili_Lesitsatfu_Lesine_Lesihlanu_Umgcibelo".split("_"),weekdaysShort:"Lis_Umb_Lsb_Les_Lsi_Lsh_Umg".split("_"),weekdaysMin:"Li_Us_Lb_Lt_Ls_Lh_Ug".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Namuhla nga] LT",nextDay:"[Kusasa nga] LT",nextWeek:"dddd [nga] LT",lastDay:"[Itolo nga] LT",lastWeek:"dddd [leliphelile] [nga] LT",sameElse:"L"},relativeTime:{future:"nga %s",past:"wenteka nga %s",s:"emizuzwana lomcane",ss:"%d mzuzwana",m:"umzuzu",mm:"%d emizuzu",h:"lihora",hh:"%d emahora",d:"lilanga",dd:"%d emalanga",M:"inyanga",MM:"%d tinyanga",y:"umnyaka",yy:"%d iminyaka"},meridiemParse:/ekuseni|emini|entsambama|ebusuku/,meridiem:function(e,t,n){return e<11?"ekuseni":e<15?"emini":e<19?"entsambama":"ebusuku"},meridiemHour:function(e,t){return(12===e&&(e=0),"ekuseni"===t)?e:"emini"===t?e>=11?e:e+12:"entsambama"===t||"ebusuku"===t?0===e?0:e+12:void 0},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:"%d",week:{dow:1,doy:4}})})(n(30381))},98760:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("sv",{months:"januari_februari_mars_april_maj_juni_juli_augusti_september_oktober_november_december".split("_"),monthsShort:"jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec".split("_"),weekdays:"s\xf6ndag_m\xe5ndag_tisdag_onsdag_torsdag_fredag_l\xf6rdag".split("_"),weekdaysShort:"s\xf6n_m\xe5n_tis_ons_tor_fre_l\xf6r".split("_"),weekdaysMin:"s\xf6_m\xe5_ti_on_to_fr_l\xf6".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"D MMMM YYYY",LLL:"D MMMM YYYY [kl.] HH:mm",LLLL:"dddd D MMMM YYYY [kl.] HH:mm",lll:"D MMM YYYY HH:mm",llll:"ddd D MMM YYYY HH:mm"},calendar:{sameDay:"[Idag] LT",nextDay:"[Imorgon] LT",lastDay:"[Ig\xe5r] LT",nextWeek:"[P\xe5] dddd LT",lastWeek:"[I] dddd[s] LT",sameElse:"L"},relativeTime:{future:"om %s",past:"f\xf6r %s sedan",s:"n\xe5gra sekunder",ss:"%d sekunder",m:"en minut",mm:"%d minuter",h:"en timme",hh:"%d timmar",d:"en dag",dd:"%d dagar",M:"en m\xe5nad",MM:"%d m\xe5nader",y:"ett \xe5r",yy:"%d \xe5r"},dayOfMonthOrdinalParse:/\d{1,2}(\:e|\:a)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?":e":1===t?":a":2===t?":a":":e";return e+n},week:{dow:1,doy:4}})})(n(30381))},91172:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("sw",{months:"Januari_Februari_Machi_Aprili_Mei_Juni_Julai_Agosti_Septemba_Oktoba_Novemba_Desemba".split("_"),monthsShort:"Jan_Feb_Mac_Apr_Mei_Jun_Jul_Ago_Sep_Okt_Nov_Des".split("_"),weekdays:"Jumapili_Jumatatu_Jumanne_Jumatano_Alhamisi_Ijumaa_Jumamosi".split("_"),weekdaysShort:"Jpl_Jtat_Jnne_Jtan_Alh_Ijm_Jmos".split("_"),weekdaysMin:"J2_J3_J4_J5_Al_Ij_J1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"hh:mm A",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[leo saa] LT",nextDay:"[kesho saa] LT",nextWeek:"[wiki ijayo] dddd [saat] LT",lastDay:"[jana] LT",lastWeek:"[wiki iliyopita] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s baadaye",past:"tokea %s",s:"hivi punde",ss:"sekunde %d",m:"dakika moja",mm:"dakika %d",h:"saa limoja",hh:"masaa %d",d:"siku moja",dd:"siku %d",M:"mwezi mmoja",MM:"miezi %d",y:"mwaka mmoja",yy:"miaka %d"},week:{dow:1,doy:7}})})(n(30381))},27333:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"௧",2:"௨",3:"௩",4:"௪",5:"௫",6:"௬",7:"௭",8:"௮",9:"௯",0:"௦"},n={"௧":"1","௨":"2","௩":"3","௪":"4","௫":"5","௬":"6","௭":"7","௮":"8","௯":"9","௦":"0"};return e.defineLocale("ta",{months:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),monthsShort:"ஜனவரி_பிப்ரவரி_மார்ச்_ஏப்ரல்_மே_ஜூன்_ஜூலை_ஆகஸ்ட்_செப்டெம்பர்_அக்டோபர்_நவம்பர்_டிசம்பர்".split("_"),weekdays:"ஞாயிற்றுக்கிழமை_திங்கட்கிழமை_செவ்வாய்கிழமை_புதன்கிழமை_வியாழக்கிழமை_வெள்ளிக்கிழமை_சனிக்கிழமை".split("_"),weekdaysShort:"ஞாயிறு_திங்கள்_செவ்வாய்_புதன்_வியாழன்_வெள்ளி_சனி".split("_"),weekdaysMin:"ஞா_தி_செ_பு_வி_வெ_ச".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, HH:mm",LLLL:"dddd, D MMMM YYYY, HH:mm"},calendar:{sameDay:"[இன்று] LT",nextDay:"[நாளை] LT",nextWeek:"dddd, LT",lastDay:"[நேற்று] LT",lastWeek:"[கடந்த வாரம்] dddd, LT",sameElse:"L"},relativeTime:{future:"%s இல்",past:"%s முன்",s:"ஒரு சில விநாடிகள்",ss:"%d விநாடிகள்",m:"ஒரு நிமிடம்",mm:"%d நிமிடங்கள்",h:"ஒரு மணி நேரம்",hh:"%d மணி நேரம்",d:"ஒரு நாள்",dd:"%d நாட்கள்",M:"ஒரு மாதம்",MM:"%d மாதங்கள்",y:"ஒரு வருடம்",yy:"%d ஆண்டுகள்"},dayOfMonthOrdinalParse:/\d{1,2}வது/,ordinal:function(e){return e+"வது"},preparse:function(e){return e.replace(/[௧௨௩௪௫௬௭௮௯௦]/g,function(e){return n[e]})},postformat:function(e){return e.replace(/\d/g,function(e){return t[e]})},meridiemParse:/யாமம்|வைகறை|காலை|நண்பகல்|எற்பாடு|மாலை/,meridiem:function(e,t,n){if(e<2)return" யாமம்";if(e<6)return" வைகறை";if(e<10)return" காலை";if(e<14)return" நண்பகல்";if(e<18)return" எற்பாடு";else if(e<22)return" மாலை";else return" யாமம்"},meridiemHour:function(e,t){return(12===e&&(e=0),"யாமம்"===t)?e<2?e:e+12:"வைகறை"===t||"காலை"===t?e:"நண்பகல்"===t?e>=10?e:e+12:e+12},week:{dow:0,doy:6}})})(n(30381))},23110:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("te",{months:"జనవరి_ఫిబ్రవరి_మార్చి_ఏప్రిల్_మే_జూన్_జులై_ఆగస్టు_సెప్టెంబర్_అక్టోబర్_నవంబర్_డిసెంబర్".split("_"),monthsShort:"జన._ఫిబ్ర._మార్చి_ఏప్రి._మే_జూన్_జులై_ఆగ._సెప్._అక్టో._నవ._డిసె.".split("_"),monthsParseExact:!0,weekdays:"ఆదివారం_సోమవారం_మంగళవారం_బుధవారం_గురువారం_శుక్రవారం_శనివారం".split("_"),weekdaysShort:"ఆది_సోమ_మంగళ_బుధ_గురు_శుక్ర_శని".split("_"),weekdaysMin:"ఆ_సో_మం_బు_గు_శు_శ".split("_"),longDateFormat:{LT:"A h:mm",LTS:"A h:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY, A h:mm",LLLL:"dddd, D MMMM YYYY, A h:mm"},calendar:{sameDay:"[నేడు] LT",nextDay:"[రేపు] LT",nextWeek:"dddd, LT",lastDay:"[నిన్న] LT",lastWeek:"[గత] dddd, LT",sameElse:"L"},relativeTime:{future:"%s లో",past:"%s క్రితం",s:"కొన్ని క్షణాలు",ss:"%d సెకన్లు",m:"ఒక నిమిషం",mm:"%d నిమిషాలు",h:"ఒక గంట",hh:"%d గంటలు",d:"ఒక రోజు",dd:"%d రోజులు",M:"ఒక నెల",MM:"%d నెలలు",y:"ఒక సంవత్సరం",yy:"%d సంవత్సరాలు"},dayOfMonthOrdinalParse:/\d{1,2}వ/,ordinal:"%dవ",meridiemParse:/రాత్రి|ఉదయం|మధ్యాహ్నం|సాయంత్రం/,meridiemHour:function(e,t){return(12===e&&(e=0),"రాత్రి"===t)?e<4?e:e+12:"ఉదయం"===t?e:"మధ్యాహ్నం"===t?e>=10?e:e+12:"సాయంత్రం"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"రాత్రి":e<10?"ఉదయం":e<17?"మధ్యాహ్నం":e<20?"సాయంత్రం":"రాత్రి"},week:{dow:0,doy:6}})})(n(30381))},52095:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("tet",{months:"Janeiru_Fevereiru_Marsu_Abril_Maiu_Ju\xf1u_Jullu_Agustu_Setembru_Outubru_Novembru_Dezembru".split("_"),monthsShort:"Jan_Fev_Mar_Abr_Mai_Jun_Jul_Ago_Set_Out_Nov_Dez".split("_"),weekdays:"Domingu_Segunda_Tersa_Kuarta_Kinta_Sesta_Sabadu".split("_"),weekdaysShort:"Dom_Seg_Ters_Kua_Kint_Sest_Sab".split("_"),weekdaysMin:"Do_Seg_Te_Ku_Ki_Ses_Sa".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Ohin iha] LT",nextDay:"[Aban iha] LT",nextWeek:"dddd [iha] LT",lastDay:"[Horiseik iha] LT",lastWeek:"dddd [semana kotuk] [iha] LT",sameElse:"L"},relativeTime:{future:"iha %s",past:"%s liuba",s:"segundu balun",ss:"segundu %d",m:"minutu ida",mm:"minutu %d",h:"oras ida",hh:"oras %d",d:"loron ida",dd:"loron %d",M:"fulan ida",MM:"fulan %d",y:"tinan ida",yy:"tinan %d"},dayOfMonthOrdinalParse:/\d{1,2}(st|nd|rd|th)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}})})(n(30381))},27321:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={0:"-ум",1:"-ум",2:"-юм",3:"-юм",4:"-ум",5:"-ум",6:"-ум",7:"-ум",8:"-ум",9:"-ум",10:"-ум",12:"-ум",13:"-ум",20:"-ум",30:"-юм",40:"-ум",50:"-ум",60:"-ум",70:"-ум",80:"-ум",90:"-ум",100:"-ум"};return e.defineLocale("tg",{months:{format:"январи_феврали_марти_апрели_майи_июни_июли_августи_сентябри_октябри_ноябри_декабри".split("_"),standalone:"январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр".split("_")},monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"якшанбе_душанбе_сешанбе_чоршанбе_панҷшанбе_ҷумъа_шанбе".split("_"),weekdaysShort:"яшб_дшб_сшб_чшб_пшб_ҷум_шнб".split("_"),weekdaysMin:"яш_дш_сш_чш_пш_ҷм_шб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[Имрӯз соати] LT",nextDay:"[Фардо соати] LT",lastDay:"[Дирӯз соати] LT",nextWeek:"dddd[и] [ҳафтаи оянда соати] LT",lastWeek:"dddd[и] [ҳафтаи гузашта соати] LT",sameElse:"L"},relativeTime:{future:"баъди %s",past:"%s пеш",s:"якчанд сония",m:"як дақиқа",mm:"%d дақиқа",h:"як соат",hh:"%d соат",d:"як рӯз",dd:"%d рӯз",M:"як моҳ",MM:"%d моҳ",y:"як сол",yy:"%d сол"},meridiemParse:/шаб|субҳ|рӯз|бегоҳ/,meridiemHour:function(e,t){return(12===e&&(e=0),"шаб"===t)?e<4?e:e+12:"субҳ"===t?e:"рӯз"===t?e>=11?e:e+12:"бегоҳ"===t?e+12:void 0},meridiem:function(e,t,n){return e<4?"шаб":e<11?"субҳ":e<16?"рӯз":e<19?"бегоҳ":"шаб"},dayOfMonthOrdinalParse:/\d{1,2}-(ум|юм)/,ordinal:function(e){var n=e%10,r=e>=100?100:null;return e+(t[e]||t[n]||t[r])},week:{dow:1,doy:7}})})(n(30381))},9041:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("th",{months:"มกราคม_กุมภาพันธ์_มีนาคม_เมษายน_พฤษภาคม_มิถุนายน_กรกฎาคม_สิงหาคม_กันยายน_ตุลาคม_พฤศจิกายน_ธันวาคม".split("_"),monthsShort:"ม.ค._ก.พ._มี.ค._เม.ย._พ.ค._มิ.ย._ก.ค._ส.ค._ก.ย._ต.ค._พ.ย._ธ.ค.".split("_"),monthsParseExact:!0,weekdays:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัสบดี_ศุกร์_เสาร์".split("_"),weekdaysShort:"อาทิตย์_จันทร์_อังคาร_พุธ_พฤหัส_ศุกร์_เสาร์".split("_"),weekdaysMin:"อา._จ._อ._พ._พฤ._ศ._ส.".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"H:mm",LTS:"H:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY เวลา H:mm",LLLL:"วันddddที่ D MMMM YYYY เวลา H:mm"},meridiemParse:/ก่อนเที่ยง|หลังเที่ยง/,isPM:function(e){return"หลังเที่ยง"===e},meridiem:function(e,t,n){return e<12?"ก่อนเที่ยง":"หลังเที่ยง"},calendar:{sameDay:"[วันนี้ เวลา] LT",nextDay:"[พรุ่งนี้ เวลา] LT",nextWeek:"dddd[หน้า เวลา] LT",lastDay:"[เมื่อวานนี้ เวลา] LT",lastWeek:"[วัน]dddd[ที่แล้ว เวลา] LT",sameElse:"L"},relativeTime:{future:"อีก %s",past:"%sที่แล้ว",s:"ไม่กี่วินาที",ss:"%d วินาที",m:"1 นาที",mm:"%d นาที",h:"1 ชั่วโมง",hh:"%d ชั่วโมง",d:"1 วัน",dd:"%d วัน",w:"1 สัปดาห์",ww:"%d สัปดาห์",M:"1 เดือน",MM:"%d เดือน",y:"1 ปี",yy:"%d ปี"}})})(n(30381))},19005:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"'inji",5:"'inji",8:"'inji",70:"'inji",80:"'inji",2:"'nji",7:"'nji",20:"'nji",50:"'nji",3:"'\xfcnji",4:"'\xfcnji",100:"'\xfcnji",6:"'njy",9:"'unjy",10:"'unjy",30:"'unjy",60:"'ynjy",90:"'ynjy"};return e.defineLocale("tk",{months:"\xddanwar_Fewral_Mart_Aprel_Ma\xfd_I\xfdun_I\xfdul_Awgust_Sent\xfdabr_Okt\xfdabr_No\xfdabr_Dekabr".split("_"),monthsShort:"\xddan_Few_Mar_Apr_Ma\xfd_I\xfdn_I\xfdl_Awg_Sen_Okt_No\xfd_Dek".split("_"),weekdays:"\xddekşenbe_Duşenbe_Sişenbe_\xc7arşenbe_Penşenbe_Anna_Şenbe".split("_"),weekdaysShort:"\xddek_Duş_Siş_\xc7ar_Pen_Ann_Şen".split("_"),weekdaysMin:"\xddk_Dş_Sş_\xc7r_Pn_An_Şn".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bug\xfcn sagat] LT",nextDay:"[ertir sagat] LT",nextWeek:"[indiki] dddd [sagat] LT",lastDay:"[d\xfc\xfdn] LT",lastWeek:"[ge\xe7en] dddd [sagat] LT",sameElse:"L"},relativeTime:{future:"%s soň",past:"%s \xf6ň",s:"birn\xe4\xe7e sekunt",m:"bir minut",mm:"%d minut",h:"bir sagat",hh:"%d sagat",d:"bir g\xfcn",dd:"%d g\xfcn",M:"bir a\xfd",MM:"%d a\xfd",y:"bir \xfdyl",yy:"%d \xfdyl"},ordinal:function(e,n){switch(n){case"d":case"D":case"Do":case"DD":return e;default:if(0===e)return e+"'unjy";var r=e%10,i=e%100-r,a=e>=100?100:null;return e+(t[r]||t[i]||t[a])}},week:{dow:1,doy:7}})})(n(30381))},75768:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("tl-ph",{months:"Enero_Pebrero_Marso_Abril_Mayo_Hunyo_Hulyo_Agosto_Setyembre_Oktubre_Nobyembre_Disyembre".split("_"),monthsShort:"Ene_Peb_Mar_Abr_May_Hun_Hul_Ago_Set_Okt_Nob_Dis".split("_"),weekdays:"Linggo_Lunes_Martes_Miyerkules_Huwebes_Biyernes_Sabado".split("_"),weekdaysShort:"Lin_Lun_Mar_Miy_Huw_Biy_Sab".split("_"),weekdaysMin:"Li_Lu_Ma_Mi_Hu_Bi_Sab".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"MM/D/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY HH:mm",LLLL:"dddd, MMMM DD, YYYY HH:mm"},calendar:{sameDay:"LT [ngayong araw]",nextDay:"[Bukas ng] LT",nextWeek:"LT [sa susunod na] dddd",lastDay:"LT [kahapon]",lastWeek:"LT [noong nakaraang] dddd",sameElse:"L"},relativeTime:{future:"sa loob ng %s",past:"%s ang nakalipas",s:"ilang segundo",ss:"%d segundo",m:"isang minuto",mm:"%d minuto",h:"isang oras",hh:"%d oras",d:"isang araw",dd:"%d araw",M:"isang buwan",MM:"%d buwan",y:"isang taon",yy:"%d taon"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})})(n(30381))},89444:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t="pagh_wa’_cha’_wej_loS_vagh_jav_Soch_chorgh_Hut".split("_");function n(e){var t=e;return -1!==e.indexOf("jaj")?t.slice(0,-3)+"leS":-1!==e.indexOf("jar")?t.slice(0,-3)+"waQ":-1!==e.indexOf("DIS")?t.slice(0,-3)+"nem":t+" pIq"}function r(e){var t=e;return -1!==e.indexOf("jaj")?t.slice(0,-3)+"Hu’":-1!==e.indexOf("jar")?t.slice(0,-3)+"wen":-1!==e.indexOf("DIS")?t.slice(0,-3)+"ben":t+" ret"}function i(e,t,n,r){var i=a(e);switch(n){case"ss":return i+" lup";case"mm":return i+" tup";case"hh":return i+" rep";case"dd":return i+" jaj";case"MM":return i+" jar";case"yy":return i+" DIS"}}function a(e){var n=Math.floor(e%1e3/100),r=Math.floor(e%100/10),i=e%10,a="";return n>0&&(a+=t[n]+"vatlh"),r>0&&(a+=(""!==a?" ":"")+t[r]+"maH"),i>0&&(a+=(""!==a?" ":"")+t[i]),""===a?"pagh":a}return e.defineLocale("tlh",{months:"tera’ jar wa’_tera’ jar cha’_tera’ jar wej_tera’ jar loS_tera’ jar vagh_tera’ jar jav_tera’ jar Soch_tera’ jar chorgh_tera’ jar Hut_tera’ jar wa’maH_tera’ jar wa’maH wa’_tera’ jar wa’maH cha’".split("_"),monthsShort:"jar wa’_jar cha’_jar wej_jar loS_jar vagh_jar jav_jar Soch_jar chorgh_jar Hut_jar wa’maH_jar wa’maH wa’_jar wa’maH cha’".split("_"),monthsParseExact:!0,weekdays:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysShort:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),weekdaysMin:"lojmItjaj_DaSjaj_povjaj_ghItlhjaj_loghjaj_buqjaj_ghInjaj".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[DaHjaj] LT",nextDay:"[wa’leS] LT",nextWeek:"LLL",lastDay:"[wa’Hu’] LT",lastWeek:"LLL",sameElse:"L"},relativeTime:{future:n,past:r,s:"puS lup",ss:i,m:"wa’ tup",mm:i,h:"wa’ rep",hh:i,d:"wa’ jaj",dd:i,M:"wa’ jar",MM:i,y:"wa’ DIS",yy:i},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}})})(n(30381))},72397:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t={1:"'inci",5:"'inci",8:"'inci",70:"'inci",80:"'inci",2:"'nci",7:"'nci",20:"'nci",50:"'nci",3:"'\xfcnc\xfc",4:"'\xfcnc\xfc",100:"'\xfcnc\xfc",6:"'ncı",9:"'uncu",10:"'uncu",30:"'uncu",60:"'ıncı",90:"'ıncı"};return e.defineLocale("tr",{months:"Ocak_Şubat_Mart_Nisan_Mayıs_Haziran_Temmuz_Ağustos_Eyl\xfcl_Ekim_Kasım_Aralık".split("_"),monthsShort:"Oca_Şub_Mar_Nis_May_Haz_Tem_Ağu_Eyl_Eki_Kas_Ara".split("_"),weekdays:"Pazar_Pazartesi_Salı_\xc7arşamba_Perşembe_Cuma_Cumartesi".split("_"),weekdaysShort:"Paz_Pts_Sal_\xc7ar_Per_Cum_Cts".split("_"),weekdaysMin:"Pz_Pt_Sa_\xc7a_Pe_Cu_Ct".split("_"),meridiem:function(e,t,n){return e<12?n?"\xf6\xf6":"\xd6\xd6":n?"\xf6s":"\xd6S"},meridiemParse:/öö|ÖÖ|ös|ÖS/,isPM:function(e){return"\xf6s"===e||"\xd6S"===e},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[bug\xfcn saat] LT",nextDay:"[yarın saat] LT",nextWeek:"[gelecek] dddd [saat] LT",lastDay:"[d\xfcn] LT",lastWeek:"[ge\xe7en] dddd [saat] LT",sameElse:"L"},relativeTime:{future:"%s sonra",past:"%s \xf6nce",s:"birka\xe7 saniye",ss:"%d saniye",m:"bir dakika",mm:"%d dakika",h:"bir saat",hh:"%d saat",d:"bir g\xfcn",dd:"%d g\xfcn",w:"bir hafta",ww:"%d hafta",M:"bir ay",MM:"%d ay",y:"bir yıl",yy:"%d yıl"},ordinal:function(e,n){switch(n){case"d":case"D":case"Do":case"DD":return e;default:if(0===e)return e+"'ıncı";var r=e%10,i=e%100-r,a=e>=100?100:null;return e+(t[r]||t[i]||t[a])}},week:{dow:1,doy:7}})})(n(30381))},28254:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=e.defineLocale("tzl",{months:"Januar_Fevraglh_Mar\xe7_Avr\xefu_Mai_G\xfcn_Julia_Guscht_Setemvar_Listop\xe4ts_Noemvar_Zecemvar".split("_"),monthsShort:"Jan_Fev_Mar_Avr_Mai_G\xfcn_Jul_Gus_Set_Lis_Noe_Zec".split("_"),weekdays:"S\xfaladi_L\xfane\xe7i_Maitzi_M\xe1rcuri_Xh\xfaadi_Vi\xe9ner\xe7i_S\xe1turi".split("_"),weekdaysShort:"S\xfal_L\xfan_Mai_M\xe1r_Xh\xfa_Vi\xe9_S\xe1t".split("_"),weekdaysMin:"S\xfa_L\xfa_Ma_M\xe1_Xh_Vi_S\xe1".split("_"),longDateFormat:{LT:"HH.mm",LTS:"HH.mm.ss",L:"DD.MM.YYYY",LL:"D. MMMM [dallas] YYYY",LLL:"D. MMMM [dallas] YYYY HH.mm",LLLL:"dddd, [li] D. MMMM [dallas] YYYY HH.mm"},meridiemParse:/d\'o|d\'a/i,isPM:function(e){return"d'o"===e.toLowerCase()},meridiem:function(e,t,n){return e>11?n?"d'o":"D'O":n?"d'a":"D'A"},calendar:{sameDay:"[oxhi \xe0] LT",nextDay:"[dem\xe0 \xe0] LT",nextWeek:"dddd [\xe0] LT",lastDay:"[ieiri \xe0] LT",lastWeek:"[s\xfcr el] dddd [lasteu \xe0] LT",sameElse:"L"},relativeTime:{future:"osprei %s",past:"ja%s",s:n,ss:n,m:n,mm:n,h:n,hh:n,d:n,dd:n,M:n,MM:n,y:n,yy:n},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});function n(e,t,n,r){var i={s:["viensas secunds","'iensas secunds"],ss:[e+" secunds",""+e+" secunds"],m:["'n m\xedut","'iens m\xedut"],mm:[e+" m\xeduts",""+e+" m\xeduts"],h:["'n \xfeora","'iensa \xfeora"],hh:[e+" \xfeoras",""+e+" \xfeoras"],d:["'n ziua","'iensa ziua"],dd:[e+" ziuas",""+e+" ziuas"],M:["'n mes","'iens mes"],MM:[e+" mesen",""+e+" mesen"],y:["'n ar","'iens ar"],yy:[e+" ars",""+e+" ars"]};return r?i[n][0]:t?i[n][0]:i[n][1]}return t})(n(30381))},30699:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("tzm-latn",{months:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),monthsShort:"innayr_brˤayrˤ_marˤsˤ_ibrir_mayyw_ywnyw_ywlywz_ɣwšt_šwtanbir_ktˤwbrˤ_nwwanbir_dwjnbir".split("_"),weekdays:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysShort:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),weekdaysMin:"asamas_aynas_asinas_akras_akwas_asimwas_asiḍyas".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[asdkh g] LT",nextDay:"[aska g] LT",nextWeek:"dddd [g] LT",lastDay:"[assant g] LT",lastWeek:"dddd [g] LT",sameElse:"L"},relativeTime:{future:"dadkh s yan %s",past:"yan %s",s:"imik",ss:"%d imik",m:"minuḍ",mm:"%d minuḍ",h:"saɛa",hh:"%d tassaɛin",d:"ass",dd:"%d ossan",M:"ayowr",MM:"%d iyyirn",y:"asgas",yy:"%d isgasn"},week:{dow:6,doy:12}})})(n(30381))},51106:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("tzm",{months:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),monthsShort:"ⵉⵏⵏⴰⵢⵔ_ⴱⵕⴰⵢⵕ_ⵎⴰⵕⵚ_ⵉⴱⵔⵉⵔ_ⵎⴰⵢⵢⵓ_ⵢⵓⵏⵢⵓ_ⵢⵓⵍⵢⵓⵣ_ⵖⵓⵛⵜ_ⵛⵓⵜⴰⵏⴱⵉⵔ_ⴽⵟⵓⴱⵕ_ⵏⵓⵡⴰⵏⴱⵉⵔ_ⴷⵓⵊⵏⴱⵉⵔ".split("_"),weekdays:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysShort:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),weekdaysMin:"ⴰⵙⴰⵎⴰⵙ_ⴰⵢⵏⴰⵙ_ⴰⵙⵉⵏⴰⵙ_ⴰⴽⵔⴰⵙ_ⴰⴽⵡⴰⵙ_ⴰⵙⵉⵎⵡⴰⵙ_ⴰⵙⵉⴹⵢⴰⵙ".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd D MMMM YYYY HH:mm"},calendar:{sameDay:"[ⴰⵙⴷⵅ ⴴ] LT",nextDay:"[ⴰⵙⴽⴰ ⴴ] LT",nextWeek:"dddd [ⴴ] LT",lastDay:"[ⴰⵚⴰⵏⵜ ⴴ] LT",lastWeek:"dddd [ⴴ] LT",sameElse:"L"},relativeTime:{future:"ⴷⴰⴷⵅ ⵙ ⵢⴰⵏ %s",past:"ⵢⴰⵏ %s",s:"ⵉⵎⵉⴽ",ss:"%d ⵉⵎⵉⴽ",m:"ⵎⵉⵏⵓⴺ",mm:"%d ⵎⵉⵏⵓⴺ",h:"ⵙⴰⵄⴰ",hh:"%d ⵜⴰⵙⵙⴰⵄⵉⵏ",d:"ⴰⵙⵙ",dd:"%d oⵙⵙⴰⵏ",M:"ⴰⵢoⵓⵔ",MM:"%d ⵉⵢⵢⵉⵔⵏ",y:"ⴰⵙⴳⴰⵙ",yy:"%d ⵉⵙⴳⴰⵙⵏ"},week:{dow:6,doy:12}})})(n(30381))},9288:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("ug-cn",{months:"يانۋار_فېۋرال_مارت_ئاپرېل_ماي_ئىيۇن_ئىيۇل_ئاۋغۇست_سېنتەبىر_ئۆكتەبىر_نويابىر_دېكابىر".split("_"),monthsShort:"يانۋار_فېۋرال_مارت_ئاپرېل_ماي_ئىيۇن_ئىيۇل_ئاۋغۇست_سېنتەبىر_ئۆكتەبىر_نويابىر_دېكابىر".split("_"),weekdays:"يەكشەنبە_دۈشەنبە_سەيشەنبە_چارشەنبە_پەيشەنبە_جۈمە_شەنبە".split("_"),weekdaysShort:"يە_دۈ_سە_چا_پە_جۈ_شە".split("_"),weekdaysMin:"يە_دۈ_سە_چا_پە_جۈ_شە".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY-MM-DD",LL:"YYYY-يىلىM-ئاينىڭD-كۈنى",LLL:"YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm",LLLL:"dddd، YYYY-يىلىM-ئاينىڭD-كۈنى، HH:mm"},meridiemParse:/يېرىم كېچە|سەھەر|چۈشتىن بۇرۇن|چۈش|چۈشتىن كېيىن|كەچ/,meridiemHour:function(e,t){return(12===e&&(e=0),"يېرىم كېچە"===t||"سەھەر"===t||"چۈشتىن بۇرۇن"===t)?e:"چۈشتىن كېيىن"===t||"كەچ"===t?e+12:e>=11?e:e+12},meridiem:function(e,t,n){var r=100*e+t;if(r<600)return"يېرىم كېچە";if(r<900)return"سەھەر";if(r<1130)return"چۈشتىن بۇرۇن";if(r<1230)return"چۈش";if(r<1800)return"چۈشتىن كېيىن";else return"كەچ"},calendar:{sameDay:"[بۈگۈن سائەت] LT",nextDay:"[ئەتە سائەت] LT",nextWeek:"[كېلەركى] dddd [سائەت] LT",lastDay:"[تۆنۈگۈن] LT",lastWeek:"[ئالدىنقى] dddd [سائەت] LT",sameElse:"L"},relativeTime:{future:"%s كېيىن",past:"%s بۇرۇن",s:"نەچچە سېكونت",ss:"%d سېكونت",m:"بىر مىنۇت",mm:"%d مىنۇت",h:"بىر سائەت",hh:"%d سائەت",d:"بىر كۈن",dd:"%d كۈن",M:"بىر ئاي",MM:"%d ئاي",y:"بىر يىل",yy:"%d يىل"},dayOfMonthOrdinalParse:/\d{1,2}(-كۈنى|-ئاي|-ھەپتە)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"-كۈنى";case"w":case"W":return e+"-ھەپتە";default:return e}},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:7}})})(n(30381))},67691:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +function t(e,t){var n=e.split("_");return t%10==1&&t%100!=11?n[0]:t%10>=2&&t%10<=4&&(t%100<10||t%100>=20)?n[1]:n[2]}function n(e,n,r){var i={ss:n?"секунда_секунди_секунд":"секунду_секунди_секунд",mm:n?"хвилина_хвилини_хвилин":"хвилину_хвилини_хвилин",hh:n?"година_години_годин":"годину_години_годин",dd:"день_дні_днів",MM:"місяць_місяці_місяців",yy:"рік_роки_років"};return"m"===r?n?"хвилина":"хвилину":"h"===r?n?"година":"годину":e+" "+t(i[r],+e)}function r(e,t){var n,r={nominative:"неділя_понеділок_вівторок_середа_четвер_п’ятниця_субота".split("_"),accusative:"неділю_понеділок_вівторок_середу_четвер_п’ятницю_суботу".split("_"),genitive:"неділі_понеділка_вівторка_середи_четверга_п’ятниці_суботи".split("_")};return!0===e?r.nominative.slice(1,7).concat(r.nominative.slice(0,1)):e?r[n=/(\[[ВвУу]\]) ?dddd/.test(t)?"accusative":/\[?(?:минулої|наступної)? ?\] ?dddd/.test(t)?"genitive":"nominative"][e.day()]:r.nominative}function i(e){return function(){return e+"о"+(11===this.hours()?"б":"")+"] LT"}}return e.defineLocale("uk",{months:{format:"січня_лютого_березня_квітня_травня_червня_липня_серпня_вересня_жовтня_листопада_грудня".split("_"),standalone:"січень_лютий_березень_квітень_травень_червень_липень_серпень_вересень_жовтень_листопад_грудень".split("_")},monthsShort:"січ_лют_бер_квіт_трав_черв_лип_серп_вер_жовт_лист_груд".split("_"),weekdays:r,weekdaysShort:"нд_пн_вт_ср_чт_пт_сб".split("_"),weekdaysMin:"нд_пн_вт_ср_чт_пт_сб".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"D MMMM YYYY р.",LLL:"D MMMM YYYY р., HH:mm",LLLL:"dddd, D MMMM YYYY р., HH:mm"},calendar:{sameDay:i("[Сьогодні "),nextDay:i("[Завтра "),lastDay:i("[Вчора "),nextWeek:i("[У] dddd ["),lastWeek:function(){switch(this.day()){case 0:case 3:case 5:case 6:return i("[Минулої] dddd [").call(this);case 1:case 2:case 4:return i("[Минулого] dddd [").call(this)}},sameElse:"L"},relativeTime:{future:"за %s",past:"%s тому",s:"декілька секунд",ss:n,m:n,mm:n,h:"годину",hh:n,d:"день",dd:n,M:"місяць",MM:n,y:"рік",yy:n},meridiemParse:/ночі|ранку|дня|вечора/,isPM:function(e){return/^(дня|вечора)$/.test(e)},meridiem:function(e,t,n){return e<4?"ночі":e<12?"ранку":e<17?"дня":"вечора"},dayOfMonthOrdinalParse:/\d{1,2}-(й|го)/,ordinal:function(e,t){switch(t){case"M":case"d":case"DDD":case"w":case"W":return e+"-й";case"D":return e+"-го";default:return e}},week:{dow:1,doy:7}})})(n(30381))},13795:function(e,t,n){var r,i;r=this,(i=function(e){"use strict";//! moment.js locale configuration +var t=["جنوری","فروری","مارچ","اپریل","مئی","جون","جولائی","اگست","ستمبر","اکتوبر","نومبر","دسمبر",],n=["اتوار","پیر","منگل","بدھ","جمعرات","جمعہ","ہفتہ"];return e.defineLocale("ur",{months:t,monthsShort:t,weekdays:n,weekdaysShort:n,weekdaysMin:n,longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd، D MMMM YYYY HH:mm"},meridiemParse:/صبح|شام/,isPM:function(e){return"شام"===e},meridiem:function(e,t,n){return e<12?"صبح":"شام"},calendar:{sameDay:"[آج بوقت] LT",nextDay:"[کل بوقت] LT",nextWeek:"dddd [بوقت] LT",lastDay:"[گذشتہ روز بوقت] LT",lastWeek:"[گذشتہ] dddd [بوقت] LT",sameElse:"L"},relativeTime:{future:"%s بعد",past:"%s قبل",s:"چند سیکنڈ",ss:"%d سیکنڈ",m:"ایک منٹ",mm:"%d منٹ",h:"ایک گھنٹہ",hh:"%d گھنٹے",d:"ایک دن",dd:"%d دن",M:"ایک ماہ",MM:"%d ماہ",y:"ایک سال",yy:"%d سال"},preparse:function(e){return e.replace(/،/g,",")},postformat:function(e){return e.replace(/,/g,"،")},week:{dow:1,doy:4}})})(n(30381))},60588:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("uz-latn",{months:"Yanvar_Fevral_Mart_Aprel_May_Iyun_Iyul_Avgust_Sentabr_Oktabr_Noyabr_Dekabr".split("_"),monthsShort:"Yan_Fev_Mar_Apr_May_Iyun_Iyul_Avg_Sen_Okt_Noy_Dek".split("_"),weekdays:"Yakshanba_Dushanba_Seshanba_Chorshanba_Payshanba_Juma_Shanba".split("_"),weekdaysShort:"Yak_Dush_Sesh_Chor_Pay_Jum_Shan".split("_"),weekdaysMin:"Ya_Du_Se_Cho_Pa_Ju_Sha".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Bugun soat] LT [da]",nextDay:"[Ertaga] LT [da]",nextWeek:"dddd [kuni soat] LT [da]",lastDay:"[Kecha soat] LT [da]",lastWeek:"[O'tgan] dddd [kuni soat] LT [da]",sameElse:"L"},relativeTime:{future:"Yaqin %s ichida",past:"Bir necha %s oldin",s:"soniya",ss:"%d soniya",m:"bir daqiqa",mm:"%d daqiqa",h:"bir soat",hh:"%d soat",d:"bir kun",dd:"%d kun",M:"bir oy",MM:"%d oy",y:"bir yil",yy:"%d yil"},week:{dow:1,doy:7}})})(n(30381))},6791:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("uz",{months:"январ_феврал_март_апрел_май_июн_июл_август_сентябр_октябр_ноябр_декабр".split("_"),monthsShort:"янв_фев_мар_апр_май_июн_июл_авг_сен_окт_ноя_дек".split("_"),weekdays:"Якшанба_Душанба_Сешанба_Чоршанба_Пайшанба_Жума_Шанба".split("_"),weekdaysShort:"Якш_Душ_Сеш_Чор_Пай_Жум_Шан".split("_"),weekdaysMin:"Як_Ду_Се_Чо_Па_Жу_Ша".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"D MMMM YYYY, dddd HH:mm"},calendar:{sameDay:"[Бугун соат] LT [да]",nextDay:"[Эртага] LT [да]",nextWeek:"dddd [куни соат] LT [да]",lastDay:"[Кеча соат] LT [да]",lastWeek:"[Утган] dddd [куни соат] LT [да]",sameElse:"L"},relativeTime:{future:"Якин %s ичида",past:"Бир неча %s олдин",s:"фурсат",ss:"%d фурсат",m:"бир дакика",mm:"%d дакика",h:"бир соат",hh:"%d соат",d:"бир кун",dd:"%d кун",M:"бир ой",MM:"%d ой",y:"бир йил",yy:"%d йил"},week:{dow:1,doy:7}})})(n(30381))},65666:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("vi",{months:"th\xe1ng 1_th\xe1ng 2_th\xe1ng 3_th\xe1ng 4_th\xe1ng 5_th\xe1ng 6_th\xe1ng 7_th\xe1ng 8_th\xe1ng 9_th\xe1ng 10_th\xe1ng 11_th\xe1ng 12".split("_"),monthsShort:"Thg 01_Thg 02_Thg 03_Thg 04_Thg 05_Thg 06_Thg 07_Thg 08_Thg 09_Thg 10_Thg 11_Thg 12".split("_"),monthsParseExact:!0,weekdays:"chủ nhật_thứ hai_thứ ba_thứ tư_thứ năm_thứ s\xe1u_thứ bảy".split("_"),weekdaysShort:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysMin:"CN_T2_T3_T4_T5_T6_T7".split("_"),weekdaysParseExact:!0,meridiemParse:/sa|ch/i,isPM:function(e){return/^ch$/i.test(e)},meridiem:function(e,t,n){return e<12?n?"sa":"SA":n?"ch":"CH"},longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"D MMMM [năm] YYYY",LLL:"D MMMM [năm] YYYY HH:mm",LLLL:"dddd, D MMMM [năm] YYYY HH:mm",l:"DD/M/YYYY",ll:"D MMM YYYY",lll:"D MMM YYYY HH:mm",llll:"ddd, D MMM YYYY HH:mm"},calendar:{sameDay:"[H\xf4m nay l\xfac] LT",nextDay:"[Ng\xe0y mai l\xfac] LT",nextWeek:"dddd [tuần tới l\xfac] LT",lastDay:"[H\xf4m qua l\xfac] LT",lastWeek:"dddd [tuần trước l\xfac] LT",sameElse:"L"},relativeTime:{future:"%s tới",past:"%s trước",s:"v\xe0i gi\xe2y",ss:"%d gi\xe2y",m:"một ph\xfat",mm:"%d ph\xfat",h:"một giờ",hh:"%d giờ",d:"một ng\xe0y",dd:"%d ng\xe0y",w:"một tuần",ww:"%d tuần",M:"một th\xe1ng",MM:"%d th\xe1ng",y:"một năm",yy:"%d năm"},dayOfMonthOrdinalParse:/\d{1,2}/,ordinal:function(e){return e},week:{dow:1,doy:4}})})(n(30381))},14378:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("x-pseudo",{months:"J~\xe1\xf1\xfa\xe1~r\xfd_F~\xe9br\xfa~\xe1r\xfd_~M\xe1rc~h_\xc1p~r\xedl_~M\xe1\xfd_~J\xfa\xf1\xe9~_J\xfal~\xfd_\xc1\xfa~g\xfast~_S\xe9p~t\xe9mb~\xe9r_\xd3~ct\xf3b~\xe9r_\xd1~\xf3v\xe9m~b\xe9r_~D\xe9c\xe9~mb\xe9r".split("_"),monthsShort:"J~\xe1\xf1_~F\xe9b_~M\xe1r_~\xc1pr_~M\xe1\xfd_~J\xfa\xf1_~J\xfal_~\xc1\xfag_~S\xe9p_~\xd3ct_~\xd1\xf3v_~D\xe9c".split("_"),monthsParseExact:!0,weekdays:"S~\xfa\xf1d\xe1~\xfd_M\xf3~\xf1d\xe1\xfd~_T\xfa\xe9~sd\xe1\xfd~_W\xe9d~\xf1\xe9sd~\xe1\xfd_T~h\xfars~d\xe1\xfd_~Fr\xedd~\xe1\xfd_S~\xe1t\xfar~d\xe1\xfd".split("_"),weekdaysShort:"S~\xfa\xf1_~M\xf3\xf1_~T\xfa\xe9_~W\xe9d_~Th\xfa_~Fr\xed_~S\xe1t".split("_"),weekdaysMin:"S~\xfa_M\xf3~_T\xfa_~W\xe9_T~h_Fr~_S\xe1".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~\xf3d\xe1~\xfd \xe1t] LT",nextDay:"[T~\xf3m\xf3~rr\xf3~w \xe1t] LT",nextWeek:"dddd [\xe1t] LT",lastDay:"[\xdd~\xe9st~\xe9rd\xe1~\xfd \xe1t] LT",lastWeek:"[L~\xe1st] dddd [\xe1t] LT",sameElse:"L"},relativeTime:{future:"\xed~\xf1 %s",past:"%s \xe1~g\xf3",s:"\xe1 ~f\xe9w ~s\xe9c\xf3~\xf1ds",ss:"%d s~\xe9c\xf3\xf1~ds",m:"\xe1 ~m\xed\xf1~\xfat\xe9",mm:"%d m~\xed\xf1\xfa~t\xe9s",h:"\xe1~\xf1 h\xf3~\xfar",hh:"%d h~\xf3\xfars",d:"\xe1 ~d\xe1\xfd",dd:"%d d~\xe1\xfds",M:"\xe1 ~m\xf3\xf1~th",MM:"%d m~\xf3\xf1t~hs",y:"\xe1 ~\xfd\xe9\xe1r",yy:"%d \xfd~\xe9\xe1rs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=1==~~(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n},week:{dow:1,doy:4}})})(n(30381))},75805:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("yo",{months:"Sẹ́rẹ́_Èrèlè_Ẹrẹ̀nà_Ìgbé_Èbibi_Òkùdu_Agẹmo_Ògún_Owewe_Ọ̀wàrà_Bélú_Ọ̀pẹ̀̀".split("_"),monthsShort:"Sẹ́r_Èrl_Ẹrn_Ìgb_Èbi_Òkù_Agẹ_Ògú_Owe_Ọ̀wà_Bél_Ọ̀pẹ̀̀".split("_"),weekdays:"Àìkú_Ajé_Ìsẹ́gun_Ọjọ́rú_Ọjọ́bọ_Ẹtì_Àbámẹ́ta".split("_"),weekdaysShort:"Àìk_Ajé_Ìsẹ́_Ọjr_Ọjb_Ẹtì_Àbá".split("_"),weekdaysMin:"Àì_Aj_Ìs_Ọr_Ọb_Ẹt_Àb".split("_"),longDateFormat:{LT:"h:mm A",LTS:"h:mm:ss A",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY h:mm A",LLLL:"dddd, D MMMM YYYY h:mm A"},calendar:{sameDay:"[Ònì ni] LT",nextDay:"[Ọ̀la ni] LT",nextWeek:"dddd [Ọsẹ̀ tón'bọ] [ni] LT",lastDay:"[Àna ni] LT",lastWeek:"dddd [Ọsẹ̀ tólọ́] [ni] LT",sameElse:"L"},relativeTime:{future:"ní %s",past:"%s kọjá",s:"ìsẹjú aayá die",ss:"aayá %d",m:"ìsẹjú kan",mm:"ìsẹjú %d",h:"wákati kan",hh:"wákati %d",d:"ọjọ́ kan",dd:"ọjọ́ %d",M:"osù kan",MM:"osù %d",y:"ọdún kan",yy:"ọdún %d"},dayOfMonthOrdinalParse:/ọjọ́\s\d{1,2}/,ordinal:"ọjọ́ %d",week:{dow:1,doy:4}})})(n(30381))},83839:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("zh-cn",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"周日_周一_周二_周三_周四_周五_周六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日Ah点mm分",LLLL:"YYYY年M月D日ddddAh点mm分",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return(12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t)?e:"下午"===t||"晚上"===t?e+12:e>=11?e:e+12},meridiem:function(e,t,n){var r=100*e+t;if(r<600)return"凌晨";if(r<900)return"早上";if(r<1130)return"上午";if(r<1230)return"中午";if(r<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:function(e){return e.week()!==this.week()?"[下]dddLT":"[本]dddLT"},lastDay:"[昨天]LT",lastWeek:function(e){return this.week()!==e.week()?"[上]dddLT":"[本]dddLT"},sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|周)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"周";default:return e}},relativeTime:{future:"%s后",past:"%s前",s:"几秒",ss:"%d 秒",m:"1 分钟",mm:"%d 分钟",h:"1 小时",hh:"%d 小时",d:"1 天",dd:"%d 天",w:"1 周",ww:"%d 周",M:"1 个月",MM:"%d 个月",y:"1 年",yy:"%d 年"},week:{dow:1,doy:4}})})(n(30381))},55726:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("zh-hk",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return(12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t)?e:"中午"===t?e>=11?e:e+12:"下午"===t||"晚上"===t?e+12:void 0},meridiem:function(e,t,n){var r=100*e+t;if(r<600)return"凌晨";if(r<900)return"早上";if(r<1200)return"上午";if(1200===r)return"中午";if(r<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天]LT",nextDay:"[明天]LT",nextWeek:"[下]ddddLT",lastDay:"[昨天]LT",lastWeek:"[上]ddddLT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})})(n(30381))},99807:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("zh-mo",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD/MM/YYYY",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"D/M/YYYY",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return(12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t)?e:"中午"===t?e>=11?e:e+12:"下午"===t||"晚上"===t?e+12:void 0},meridiem:function(e,t,n){var r=100*e+t;if(r<600)return"凌晨";if(r<900)return"早上";if(r<1130)return"上午";if(r<1230)return"中午";if(r<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天] LT",nextDay:"[明天] LT",nextWeek:"[下]dddd LT",lastDay:"[昨天] LT",lastWeek:"[上]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s內",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})})(n(30381))},74152:function(e,t,n){var r,i;r=this,(i=function(e){return e.defineLocale("zh-tw",{months:"一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月".split("_"),monthsShort:"1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_"),weekdays:"星期日_星期一_星期二_星期三_星期四_星期五_星期六".split("_"),weekdaysShort:"週日_週一_週二_週三_週四_週五_週六".split("_"),weekdaysMin:"日_一_二_三_四_五_六".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"YYYY/MM/DD",LL:"YYYY年M月D日",LLL:"YYYY年M月D日 HH:mm",LLLL:"YYYY年M月D日dddd HH:mm",l:"YYYY/M/D",ll:"YYYY年M月D日",lll:"YYYY年M月D日 HH:mm",llll:"YYYY年M月D日dddd HH:mm"},meridiemParse:/凌晨|早上|上午|中午|下午|晚上/,meridiemHour:function(e,t){return(12===e&&(e=0),"凌晨"===t||"早上"===t||"上午"===t)?e:"中午"===t?e>=11?e:e+12:"下午"===t||"晚上"===t?e+12:void 0},meridiem:function(e,t,n){var r=100*e+t;if(r<600)return"凌晨";if(r<900)return"早上";if(r<1130)return"上午";if(r<1230)return"中午";if(r<1800)return"下午";else return"晚上"},calendar:{sameDay:"[今天] LT",nextDay:"[明天] LT",nextWeek:"[下]dddd LT",lastDay:"[昨天] LT",lastWeek:"[上]dddd LT",sameElse:"L"},dayOfMonthOrdinalParse:/\d{1,2}(日|月|週)/,ordinal:function(e,t){switch(t){case"d":case"D":case"DDD":return e+"日";case"M":return e+"月";case"w":case"W":return e+"週";default:return e}},relativeTime:{future:"%s後",past:"%s前",s:"幾秒",ss:"%d 秒",m:"1 分鐘",mm:"%d 分鐘",h:"1 小時",hh:"%d 小時",d:"1 天",dd:"%d 天",M:"1 個月",MM:"%d 個月",y:"1 年",yy:"%d 年"}})})(n(30381))},46700(e,t,n){var r={"./af":42786,"./af.js":42786,"./ar":30867,"./ar-dz":14130,"./ar-dz.js":14130,"./ar-kw":96135,"./ar-kw.js":96135,"./ar-ly":56440,"./ar-ly.js":56440,"./ar-ma":47702,"./ar-ma.js":47702,"./ar-sa":16040,"./ar-sa.js":16040,"./ar-tn":37100,"./ar-tn.js":37100,"./ar.js":30867,"./az":31083,"./az.js":31083,"./be":9808,"./be.js":9808,"./bg":68338,"./bg.js":68338,"./bm":67438,"./bm.js":67438,"./bn":8905,"./bn-bd":76225,"./bn-bd.js":76225,"./bn.js":8905,"./bo":11560,"./bo.js":11560,"./br":1278,"./br.js":1278,"./bs":80622,"./bs.js":80622,"./ca":2468,"./ca.js":2468,"./cs":5822,"./cs.js":5822,"./cv":50877,"./cv.js":50877,"./cy":47373,"./cy.js":47373,"./da":24780,"./da.js":24780,"./de":59740,"./de-at":60217,"./de-at.js":60217,"./de-ch":60894,"./de-ch.js":60894,"./de.js":59740,"./dv":5300,"./dv.js":5300,"./el":50837,"./el.js":50837,"./en-au":78348,"./en-au.js":78348,"./en-ca":77925,"./en-ca.js":77925,"./en-gb":22243,"./en-gb.js":22243,"./en-ie":46436,"./en-ie.js":46436,"./en-il":47207,"./en-il.js":47207,"./en-in":44175,"./en-in.js":44175,"./en-nz":76319,"./en-nz.js":76319,"./en-sg":31662,"./en-sg.js":31662,"./eo":92915,"./eo.js":92915,"./es":55655,"./es-do":55251,"./es-do.js":55251,"./es-mx":96112,"./es-mx.js":96112,"./es-us":71146,"./es-us.js":71146,"./es.js":55655,"./et":5603,"./et.js":5603,"./eu":77763,"./eu.js":77763,"./fa":76959,"./fa.js":76959,"./fi":11897,"./fi.js":11897,"./fil":42549,"./fil.js":42549,"./fo":94694,"./fo.js":94694,"./fr":94470,"./fr-ca":63049,"./fr-ca.js":63049,"./fr-ch":52330,"./fr-ch.js":52330,"./fr.js":94470,"./fy":5044,"./fy.js":5044,"./ga":29295,"./ga.js":29295,"./gd":2101,"./gd.js":2101,"./gl":38794,"./gl.js":38794,"./gom-deva":27884,"./gom-deva.js":27884,"./gom-latn":23168,"./gom-latn.js":23168,"./gu":95349,"./gu.js":95349,"./he":24206,"./he.js":24206,"./hi":30094,"./hi.js":30094,"./hr":30316,"./hr.js":30316,"./hu":22138,"./hu.js":22138,"./hy-am":11423,"./hy-am.js":11423,"./id":29218,"./id.js":29218,"./is":90135,"./is.js":90135,"./it":90626,"./it-ch":10150,"./it-ch.js":10150,"./it.js":90626,"./ja":39183,"./ja.js":39183,"./jv":24286,"./jv.js":24286,"./ka":12105,"./ka.js":12105,"./kk":47772,"./kk.js":47772,"./km":18758,"./km.js":18758,"./kn":79282,"./kn.js":79282,"./ko":33730,"./ko.js":33730,"./ku":1408,"./ku.js":1408,"./ky":33291,"./ky.js":33291,"./lb":36841,"./lb.js":36841,"./lo":55466,"./lo.js":55466,"./lt":57010,"./lt.js":57010,"./lv":37595,"./lv.js":37595,"./me":39861,"./me.js":39861,"./mi":35493,"./mi.js":35493,"./mk":95966,"./mk.js":95966,"./ml":87341,"./ml.js":87341,"./mn":5115,"./mn.js":5115,"./mr":10370,"./mr.js":10370,"./ms":9847,"./ms-my":41237,"./ms-my.js":41237,"./ms.js":9847,"./mt":72126,"./mt.js":72126,"./my":56165,"./my.js":56165,"./nb":64924,"./nb.js":64924,"./ne":16744,"./ne.js":16744,"./nl":93901,"./nl-be":59814,"./nl-be.js":59814,"./nl.js":93901,"./nn":83877,"./nn.js":83877,"./oc-lnc":92135,"./oc-lnc.js":92135,"./pa-in":15858,"./pa-in.js":15858,"./pl":64495,"./pl.js":64495,"./pt":89520,"./pt-br":57971,"./pt-br.js":57971,"./pt.js":89520,"./ro":96459,"./ro.js":96459,"./ru":21793,"./ru.js":21793,"./sd":40950,"./sd.js":40950,"./se":10490,"./se.js":10490,"./si":90124,"./si.js":90124,"./sk":64249,"./sk.js":64249,"./sl":14985,"./sl.js":14985,"./sq":51104,"./sq.js":51104,"./sr":49131,"./sr-cyrl":79915,"./sr-cyrl.js":79915,"./sr.js":49131,"./ss":85893,"./ss.js":85893,"./sv":98760,"./sv.js":98760,"./sw":91172,"./sw.js":91172,"./ta":27333,"./ta.js":27333,"./te":23110,"./te.js":23110,"./tet":52095,"./tet.js":52095,"./tg":27321,"./tg.js":27321,"./th":9041,"./th.js":9041,"./tk":19005,"./tk.js":19005,"./tl-ph":75768,"./tl-ph.js":75768,"./tlh":89444,"./tlh.js":89444,"./tr":72397,"./tr.js":72397,"./tzl":28254,"./tzl.js":28254,"./tzm":51106,"./tzm-latn":30699,"./tzm-latn.js":30699,"./tzm.js":51106,"./ug-cn":9288,"./ug-cn.js":9288,"./uk":67691,"./uk.js":67691,"./ur":13795,"./ur.js":13795,"./uz":6791,"./uz-latn":60588,"./uz-latn.js":60588,"./uz.js":6791,"./vi":65666,"./vi.js":65666,"./x-pseudo":14378,"./x-pseudo.js":14378,"./yo":75805,"./yo.js":75805,"./zh-cn":83839,"./zh-cn.js":83839,"./zh-hk":55726,"./zh-hk.js":55726,"./zh-mo":99807,"./zh-mo.js":99807,"./zh-tw":74152,"./zh-tw.js":74152};function i(e){return n(a(e))}function a(e){if(!n.o(r,e)){var t=Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}i.keys=function(){return Object.keys(r)},i.resolve=a,e.exports=i,i.id=46700},30381:function(e,t,n){var r,i;e=n.nmd(e),r=this,i=function(){"use strict";function t(){return em.apply(null,arguments)}function r(e){em=e}function i(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function a(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function o(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function s(e){var t;if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(t in e)if(o(e,t))return!1;return!0}function u(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function l(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,r=[];for(n=0;n>>0;for(t=0;t0)for(n=0;n=0?n?"+":"":"-")+Math.pow(10,Math.max(0,t-i.length)).toString().substr(1)+i}var R=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,j=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,F={},Y={};function B(e,t,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),e&&(Y[e]=i),t&&(Y[t[0]]=function(){return P(i.apply(this,arguments),t[1],t[2])}),n&&(Y[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function U(e){return e.match(/\[[\s\S]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"")}function H(e){var t,n,r=e.match(R);for(t=0,n=r.length;t=0&&j.test(e);)e=e.replace(j,r),j.lastIndex=0,n-=1;return e}var G={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"};function W(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(R).map(function(e){return"MMMM"===e||"MM"===e||"DD"===e||"dddd"===e?e.slice(1):e}).join(""),this._longDateFormat[e])}var K="Invalid date";function V(){return this._invalidDate}var q="%d",Z=/\d{1,2}/;function X(e){return this._ordinal.replace("%d",e)}var J={future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function Q(e,t,n,r){var i=this._relativeTime[n];return A(i)?i(e,t,n,r):i.replace(/%d/i,e)}function ee(e,t){var n=this._relativeTime[e>0?"future":"past"];return A(n)?n(t):n.replace(/%s/i,t)}var et={};function en(e,t){var n=e.toLowerCase();et[n]=et[n+"s"]=et[t]=e}function er(e){return"string"==typeof e?et[e]||et[e.toLowerCase()]:void 0}function ei(e){var t,n,r={};for(n in e)o(e,n)&&(t=er(n))&&(r[t]=e[n]);return r}var ea={};function eo(e,t){ea[e]=t}function es(e){var t,n=[];for(t in e)o(e,t)&&n.push({unit:t,priority:ea[t]});return n.sort(function(e,t){return e.priority-t.priority}),n}function eu(e){return e%4==0&&e%100!=0||e%400==0}function ec(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function el(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=ec(t)),n}function ef(e,n){return function(r){return null!=r?(eh(this,e,r),t.updateOffset(this,n),this):ed(this,e)}}function ed(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function eh(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&eu(e.year())&&1===e.month()&&29===e.date()?(n=el(n),e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),e0(n,e.month()))):e._d["set"+(e._isUTC?"UTC":"")+t](n))}function ep(e){return A(this[e=er(e)])?this[e]():this}function eb(e,t){if("object"==typeof e){e=ei(e);var n,r=es(e);for(n=0;n68?1900:2e3)};var tu=ef("FullYear",!0);function tc(){return eu(this.year())}function tl(e,t,n,r,i,a,o){var s;return e<100&&e>=0?(s=new Date(e+400,t,n,r,i,a,o),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,r,i,a,o),s}function tf(e){var t,n;return e<100&&e>=0?(n=Array.prototype.slice.call(arguments),n[0]=e+400,t=new Date(Date.UTC.apply(null,n)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function td(e,t,n){var r=7+t-n;return-((7+tf(e,0,r).getUTCDay()-t)%7)+r-1}function th(e,t,n,r,i){var a,o,s=(7+n-r)%7,u=td(e,r,i),c=1+7*(t-1)+s+u;return c<=0?o=ts(a=e-1)+c:c>ts(e)?(a=e+1,o=c-ts(e)):(a=e,o=c),{year:a,dayOfYear:o}}function tp(e,t,n){var r,i,a=td(e.year(),t,n),o=Math.floor((e.dayOfYear()-a-1)/7)+1;return o<1?r=o+tb(i=e.year()-1,t,n):o>tb(e.year(),t,n)?(r=o-tb(e.year(),t,n),i=e.year()+1):(i=e.year(),r=o),{week:r,year:i}}function tb(e,t,n){var r=td(e,t,n),i=td(e+1,t,n);return(ts(e)-r+i)/7}function tm(e){return tp(e,this._week.dow,this._week.doy).week}B("w",["ww",2],"wo","week"),B("W",["WW",2],"Wo","isoWeek"),en("week","w"),en("isoWeek","W"),eo("week",5),eo("isoWeek",5),ej("w",ex),ej("ww",ex,e_),ej("W",ex),ej("WW",ex,e_),e$(["w","ww","W","WW"],function(e,t,n,r){t[r.substr(0,1)]=el(e)});var tg={dow:0,doy:6};function tv(){return this._week.dow}function ty(){return this._week.doy}function tw(e){var t=this.localeData().week(this);return null==e?t:this.add((e-t)*7,"d")}function t_(e){var t=tp(this,1,4).week;return null==e?t:this.add((e-t)*7,"d")}function tE(e,t){return"string"!=typeof e?e:isNaN(e)?"number"==typeof(e=t.weekdaysParse(e))?e:null:parseInt(e,10)}function tS(e,t){return"string"==typeof e?t.weekdaysParse(e)%7||7:isNaN(e)?null:e}function tk(e,t){return e.slice(t,7).concat(e.slice(0,t))}B("d",0,"do","day"),B("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),B("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),B("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),B("e",0,0,"weekday"),B("E",0,0,"isoWeekday"),en("day","d"),en("weekday","e"),en("isoWeekday","E"),eo("day",11),eo("weekday",11),eo("isoWeekday",11),ej("d",ex),ej("e",ex),ej("E",ex),ej("dd",function(e,t){return t.weekdaysMinRegex(e)}),ej("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ej("dddd",function(e,t){return t.weekdaysRegex(e)}),e$(["dd","ddd","dddd"],function(e,t,n,r){var i=n._locale.weekdaysParse(e,r,n._strict);null!=i?t.d=i:b(n).invalidWeekday=e}),e$(["d","e","E"],function(e,t,n,r){t[r]=el(e)});var tx="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),tT="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),tM="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),tO=eR,tA=eR,tL=eR;function tC(e,t){var n=i(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"];return!0===e?tk(n,this._week.dow):e?n[e.day()]:n}function tI(e){return!0===e?tk(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort}function tD(e){return!0===e?tk(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin}function tN(e,t,n){var r,i,a,o=e.toLocaleLowerCase();if(!this._weekdaysParse)for(r=0,this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[];r<7;++r)a=h([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(a,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(a,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(a,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=tX.call(this._weekdaysParse,o))?i:null:"ddd"===t?-1!==(i=tX.call(this._shortWeekdaysParse,o))?i:null:-1!==(i=tX.call(this._minWeekdaysParse,o))?i:null:"dddd"===t?-1!==(i=tX.call(this._weekdaysParse,o))||-1!==(i=tX.call(this._shortWeekdaysParse,o))?i:-1!==(i=tX.call(this._minWeekdaysParse,o))?i:null:"ddd"===t?-1!==(i=tX.call(this._shortWeekdaysParse,o))||-1!==(i=tX.call(this._weekdaysParse,o))?i:-1!==(i=tX.call(this._minWeekdaysParse,o))?i:null:-1!==(i=tX.call(this._minWeekdaysParse,o))||-1!==(i=tX.call(this._weekdaysParse,o))?i:-1!==(i=tX.call(this._shortWeekdaysParse,o))?i:null}function tP(e,t,n){var r,i,a;if(this._weekdaysParseExact)return tN.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=h([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(a="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=RegExp(a.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[r].test(e))return r;if(n&&"ddd"===t&&this._shortWeekdaysParse[r].test(e))return r;if(n&&"dd"===t&&this._minWeekdaysParse[r].test(e))return r;else if(!n&&this._weekdaysParse[r].test(e))return r}}function tR(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=tE(e,this.localeData()),this.add(e-t,"d")):t}function tj(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")}function tF(e){if(!this.isValid())return null!=e?this:NaN;if(null==e)return this.day()||7;var t=tS(e,this.localeData());return this.day(this.day()%7?t:t-7)}function tY(e){return this._weekdaysParseExact?(o(this,"_weekdaysRegex")||tH.call(this),e)?this._weekdaysStrictRegex:this._weekdaysRegex:(o(this,"_weekdaysRegex")||(this._weekdaysRegex=tO),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)}function tB(e){return this._weekdaysParseExact?(o(this,"_weekdaysRegex")||tH.call(this),e)?this._weekdaysShortStrictRegex:this._weekdaysShortRegex:(o(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=tA),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function tU(e){return this._weekdaysParseExact?(o(this,"_weekdaysRegex")||tH.call(this),e)?this._weekdaysMinStrictRegex:this._weekdaysMinRegex:(o(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=tL),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function tH(){function e(e,t){return t.length-e.length}var t,n,r,i,a,o=[],s=[],u=[],c=[];for(t=0;t<7;t++)n=h([2e3,1]).day(t),r=eB(this.weekdaysMin(n,"")),i=eB(this.weekdaysShort(n,"")),a=eB(this.weekdays(n,"")),o.push(r),s.push(i),u.push(a),c.push(r),c.push(i),c.push(a);o.sort(e),s.sort(e),u.sort(e),c.sort(e),this._weekdaysRegex=RegExp("^("+c.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=RegExp("^("+o.join("|")+")","i")}function t$(){return this.hours()%12||12}function tz(){return this.hours()||24}function tG(e,t){B(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function tW(e,t){return t._meridiemParse}function tK(e){return"p"===(e+"").toLowerCase().charAt(0)}B("H",["HH",2],0,"hour"),B("h",["hh",2],0,t$),B("k",["kk",2],0,tz),B("hmm",0,0,function(){return""+t$.apply(this)+P(this.minutes(),2)}),B("hmmss",0,0,function(){return""+t$.apply(this)+P(this.minutes(),2)+P(this.seconds(),2)}),B("Hmm",0,0,function(){return""+this.hours()+P(this.minutes(),2)}),B("Hmmss",0,0,function(){return""+this.hours()+P(this.minutes(),2)+P(this.seconds(),2)}),tG("a",!0),tG("A",!1),en("hour","h"),eo("hour",13),ej("a",tW),ej("A",tW),ej("H",ex),ej("h",ex),ej("k",ex),ej("HH",ex,e_),ej("hh",ex,e_),ej("kk",ex,e_),ej("hmm",eT),ej("hmmss",eM),ej("Hmm",eT),ej("Hmmss",eM),eH(["H","HH"],eV),eH(["k","kk"],function(e,t,n){var r=el(e);t[eV]=24===r?0:r}),eH(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),eH(["h","hh"],function(e,t,n){t[eV]=el(e),b(n).bigHour=!0}),eH("hmm",function(e,t,n){var r=e.length-2;t[eV]=el(e.substr(0,r)),t[eq]=el(e.substr(r)),b(n).bigHour=!0}),eH("hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[eV]=el(e.substr(0,r)),t[eq]=el(e.substr(r,2)),t[eZ]=el(e.substr(i)),b(n).bigHour=!0}),eH("Hmm",function(e,t,n){var r=e.length-2;t[eV]=el(e.substr(0,r)),t[eq]=el(e.substr(r))}),eH("Hmmss",function(e,t,n){var r=e.length-4,i=e.length-2;t[eV]=el(e.substr(0,r)),t[eq]=el(e.substr(r,2)),t[eZ]=el(e.substr(i))});var tV=/[ap]\.?m?\.?/i,tq=ef("Hours",!0);function tZ(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"}var tX,tJ,tQ={calendar:D,longDateFormat:G,invalidDate:K,ordinal:q,dayOfMonthOrdinalParse:Z,relativeTime:J,months:e2,monthsShort:e3,week:tg,weekdays:tx,weekdaysMin:tM,weekdaysShort:tT,meridiemParse:tV},t1={},t0={};function t2(e,t){var n,r=Math.min(e.length,t.length);for(n=0;n0;){if(r=t5(i.slice(0,t).join("-")))return r;if(n&&n.length>=t&&t2(i,n)>=t-1)break;t--}a++}return tJ}function t5(t){var r,i=null;if(void 0===t1[t]&&e&&e.exports)try{i=tJ._abbr,r=void 0,n(46700)("./"+t),t6(i)}catch(a){t1[t]=null}return t1[t]}function t6(e,t){var n;return e&&((n=u(t)?t7(e):t9(e,t))?tJ=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),tJ._abbr}function t9(e,t){if(null===t)return delete t1[e],null;var n,r=tQ;if(t.abbr=e,null!=t1[e])O("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=t1[e]._config;else if(null!=t.parentLocale){if(null!=t1[t.parentLocale])r=t1[t.parentLocale]._config;else{if(null==(n=t5(t.parentLocale)))return t0[t.parentLocale]||(t0[t.parentLocale]=[]),t0[t.parentLocale].push({name:e,config:t}),null;r=n._config}}return t1[e]=new I(C(r,t)),t0[e]&&t0[e].forEach(function(e){t9(e.name,e.config)}),t6(e),t1[e]}function t8(e,t){if(null!=t){var n,r,i=tQ;null!=t1[e]&&null!=t1[e].parentLocale?t1[e].set(C(t1[e]._config,t)):(null!=(r=t5(e))&&(i=r._config),t=C(i,t),null==r&&(t.abbr=e),(n=new I(t)).parentLocale=t1[e],t1[e]=n),t6(e)}else null!=t1[e]&&(null!=t1[e].parentLocale?(t1[e]=t1[e].parentLocale,e===t6()&&t6(e)):null!=t1[e]&&delete t1[e]);return t1[e]}function t7(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return tJ;if(!i(e)){if(t=t5(e))return t;e=[e]}return t4(e)}function ne(){return ev(t1)}function nt(e){var t,n=e._a;return n&&-2===b(e).overflow&&(t=n[eW]<0||n[eW]>11?eW:n[eK]<1||n[eK]>e0(n[eG],n[eW])?eK:n[eV]<0||n[eV]>24||24===n[eV]&&(0!==n[eq]||0!==n[eZ]||0!==n[eX])?eV:n[eq]<0||n[eq]>59?eq:n[eZ]<0||n[eZ]>59?eZ:n[eX]<0||n[eX]>999?eX:-1,b(e)._overflowDayOfYear&&(teK)&&(t=eK),b(e)._overflowWeeks&&-1===t&&(t=eJ),b(e)._overflowWeekday&&-1===t&&(t=eQ),b(e).overflow=t),e}var nn=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,nr=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ni=/Z|[+-]\d\d(?::?\d\d)?/,na=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1],],no=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/],],ns=/^\/?Date\((-?\d+)/i,nu=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,nc={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function nl(e){var t,n,r,i,a,o,s=e._i,u=nn.exec(s)||nr.exec(s);if(u){for(t=0,b(e).iso=!0,n=na.length;tts(a)||0===e._dayOfYear)&&(b(e)._overflowDayOfYear=!0),n=tf(a,0,e._dayOfYear),e._a[eW]=n.getUTCMonth(),e._a[eK]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=r[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[eV]&&0===e._a[eq]&&0===e._a[eZ]&&0===e._a[eX]&&(e._nextDay=!0,e._a[eV]=0),e._d=(e._useUTC?tf:tl).apply(null,o),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[eV]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(b(e).weekdayMismatch=!0)}}function n_(e){var t,n,r,i,a,o,s,u,c;null!=(t=e._w).GG||null!=t.W||null!=t.E?(a=1,o=4,n=nv(t.GG,e._a[eG],tp(nL(),1,4).year),r=nv(t.W,1),((i=nv(t.E,1))<1||i>7)&&(u=!0)):(a=e._locale._week.dow,o=e._locale._week.doy,c=tp(nL(),a,o),n=nv(t.gg,e._a[eG],c.year),r=nv(t.w,c.week),null!=t.d?((i=t.d)<0||i>6)&&(u=!0):null!=t.e?(i=t.e+a,(t.e<0||t.e>6)&&(u=!0)):i=a),r<1||r>tb(n,a,o)?b(e)._overflowWeeks=!0:null!=u?b(e)._overflowWeekday=!0:(s=th(n,r,i,a,o),e._a[eG]=s.year,e._dayOfYear=s.dayOfYear)}function nE(e){if(e._f===t.ISO_8601){nl(e);return}if(e._f===t.RFC_2822){nm(e);return}e._a=[],b(e).empty=!0;var n,r,i,a,o,s,u=""+e._i,c=u.length,l=0;for(n=0,i=z(e._f,e._locale).match(R)||[];n0&&b(e).unusedInput.push(o),u=u.slice(u.indexOf(r)+r.length),l+=r.length),Y[a]?(r?b(e).empty=!1:b(e).unusedTokens.push(a),ez(a,r,e)):e._strict&&!r&&b(e).unusedTokens.push(a);b(e).charsLeftOver=c-l,u.length>0&&b(e).unusedInput.push(u),e._a[eV]<=12&&!0===b(e).bigHour&&e._a[eV]>0&&(b(e).bigHour=void 0),b(e).parsedDateParts=e._a.slice(0),b(e).meridiem=e._meridiem,e._a[eV]=nS(e._locale,e._a[eV],e._meridiem),null!==(s=b(e).era)&&(e._a[eG]=e._locale.erasConvertYear(s,e._a[eG])),nw(e),nt(e)}function nS(e,t,n){var r;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):(null!=e.isPM&&((r=e.isPM(n))&&t<12&&(t+=12),r||12!==t||(t=0)),t)}function nk(e){var t,n,r,i,a,o,s=!1;if(0===e._f.length){b(e).invalidFormat=!0,e._d=new Date(NaN);return}for(i=0;ithis?this:e:g()});function nD(e,t){var n,r;if(1===t.length&&i(t[0])&&(t=t[0]),!t.length)return nL();for(r=1,n=t[0];rMath.abs(e)&&!r&&(e*=60);return!this._isUTC&&n&&(i=nq(this)),this._offset=e,this._isUTC=!0,null!=i&&this.add(i,"m"),a===e||(!n||this._changeInProgress?ri(this,n7(e-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,t.updateOffset(this,!0),this._changeInProgress=null)),this}function nX(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}function nJ(e){return this.utcOffset(0,e)}function nQ(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(nq(this),"m")),this}function n1(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var e=nK(eD,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this}function n0(e){return!!this.isValid()&&(e=e?nL(e).utcOffset():0,(this.utcOffset()-e)%60==0)}function n2(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function n3(){if(!u(this._isDSTShifted))return this._isDSTShifted;var e,t={};return E(t,this),(t=nM(t))._a?(e=t._isUTC?h(t._a):nL(t._a),this._isDSTShifted=this.isValid()&&nz(t._a,e.toArray())>0):this._isDSTShifted=!1,this._isDSTShifted}function n4(){return!!this.isValid()&&!this._isUTC}function n5(){return!!this.isValid()&&this._isUTC}function n6(){return!!this.isValid()&&this._isUTC&&0===this._offset}t.updateOffset=function(){};var n9=/^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/,n8=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function n7(e,t){var n,r,i,a=e,s=null;return nH(e)?a={ms:e._milliseconds,d:e._days,M:e._months}:c(e)||!isNaN(+e)?(a={},t?a[t]=+e:a.milliseconds=+e):(s=n9.exec(e))?(n="-"===s[1]?-1:1,a={y:0,d:el(s[eK])*n,h:el(s[eV])*n,m:el(s[eq])*n,s:el(s[eZ])*n,ms:el(n$(1e3*s[eX]))*n}):(s=n8.exec(e))?(n="-"===s[1]?-1:1,a={y:re(s[2],n),M:re(s[3],n),w:re(s[4],n),d:re(s[5],n),h:re(s[6],n),m:re(s[7],n),s:re(s[8],n)}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(i=rn(nL(a.from),nL(a.to)),(a={}).ms=i.milliseconds,a.M=i.months),r=new nU(a),nH(e)&&o(e,"_locale")&&(r._locale=e._locale),nH(e)&&o(e,"_isValid")&&(r._isValid=e._isValid),r}function re(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function rt(e,t){var n={};return n.months=t.month()-e.month()+(t.year()-e.year())*12,e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function rn(e,t){var n;return e.isValid()&&t.isValid()?(t=nV(t,e),e.isBefore(t)?n=rt(e,t):((n=rt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}function rr(e,t){return function(n,r){var i,a;return null===r||isNaN(+r)||(O(t,"moment()."+t+"(period, number) is deprecated. Please use moment()."+t+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),a=n,n=r,r=a),i=n7(n,r),ri(this,i,e),this}}function ri(e,n,r,i){var a=n._milliseconds,o=n$(n._days),s=n$(n._months);e.isValid()&&(i=null==i||i,s&&tt(e,ed(e,"Month")+s*r),o&&eh(e,"Date",ed(e,"Date")+o*r),a&&e._d.setTime(e._d.valueOf()+a*r),i&&t.updateOffset(e,o||s))}n7.fn=nU.prototype,n7.invalid=nB;var ra=rr(1,"add"),ro=rr(-1,"subtract");function rs(e){return"string"==typeof e||e instanceof String}function ru(e){return k(e)||l(e)||rs(e)||c(e)||rl(e)||rc(e)||null==e}function rc(e){var t,n,r=a(e)&&!s(e),i=!1,u=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms",];for(t=0;tn.valueOf():n.valueOf()n.year()||n.year()>9999?$(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):A(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+6e4*this.utcOffset()).toISOString().replace("Z",$(n,"Z")):$(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")}function rx(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e,t,n,r,i="moment",a="";return this.isLocal()||(i=0===this.utcOffset()?"moment.utc":"moment.parseZone",a="Z"),e="["+i+'("]',t=0<=this.year()&&9999>=this.year()?"YYYY":"YYYYYY",n="-MM-DD[T]HH:mm:ss.SSS",r=a+'[")]',this.format(e+t+n+r)}function rT(e){e||(e=this.isUtc()?t.defaultFormatUtc:t.defaultFormat);var n=$(this,e);return this.localeData().postformat(n)}function rM(e,t){return this.isValid()&&(k(e)&&e.isValid()||nL(e).isValid())?n7({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()}function rO(e){return this.from(nL(),e)}function rA(e,t){return this.isValid()&&(k(e)&&e.isValid()||nL(e).isValid())?n7({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()}function rL(e){return this.to(nL(),e)}function rC(e){var t;return void 0===e?this._locale._abbr:(null!=(t=t7(e))&&(this._locale=t),this)}t.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",t.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var rI=T("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});function rD(){return this._locale}var rN=1e3,rP=60*rN,rR=60*rP,rj=3506328*rR;function rF(e,t){return(e%t+t)%t}function rY(e,t,n){return e<100&&e>=0?new Date(e+400,t,n)-rj:new Date(e,t,n).valueOf()}function rB(e,t,n){return e<100&&e>=0?Date.UTC(e+400,t,n)-rj:Date.UTC(e,t,n)}function rU(e){var n,r;if(void 0===(e=er(e))||"millisecond"===e||!this.isValid())return this;switch(r=this._isUTC?rB:rY,e){case"year":n=r(this.year(),0,1);break;case"quarter":n=r(this.year(),this.month()-this.month()%3,1);break;case"month":n=r(this.year(),this.month(),1);break;case"week":n=r(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":n=r(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":n=r(this.year(),this.month(),this.date());break;case"hour":n=this._d.valueOf(),n-=rF(n+(this._isUTC?0:this.utcOffset()*rP),rR);break;case"minute":n=this._d.valueOf(),n-=rF(n,rP);break;case"second":n=this._d.valueOf(),n-=rF(n,rN)}return this._d.setTime(n),t.updateOffset(this,!0),this}function rH(e){var n,r;if(void 0===(e=er(e))||"millisecond"===e||!this.isValid())return this;switch(r=this._isUTC?rB:rY,e){case"year":n=r(this.year()+1,0,1)-1;break;case"quarter":n=r(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":n=r(this.year(),this.month()+1,1)-1;break;case"week":n=r(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":n=r(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":n=r(this.year(),this.month(),this.date()+1)-1;break;case"hour":n=this._d.valueOf(),n+=rR-rF(n+(this._isUTC?0:this.utcOffset()*rP),rR)-1;break;case"minute":n=this._d.valueOf(),n+=rP-rF(n,rP)-1;break;case"second":n=this._d.valueOf(),n+=rN-rF(n,rN)-1}return this._d.setTime(n),t.updateOffset(this,!0),this}function r$(){return this._d.valueOf()-6e4*(this._offset||0)}function rz(){return Math.floor(this.valueOf()/1e3)}function rG(){return new Date(this.valueOf())}function rW(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond(),]}function rK(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}}function rV(){return this.isValid()?this.toISOString():null}function rq(){return m(this)}function rZ(){return d({},b(this))}function rX(){return b(this).overflow}function rJ(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function rQ(e,n){var r,i,a,o=this._eras||t7("en")._eras;for(r=0,i=o.length;r=0)return u[r]}function r0(e,n){var r=e.since<=e.until?1:-1;return void 0===n?t(e.since).year():t(e.since).year()+(n-e.offset)*r}function r2(){var e,t,n,r=this.localeData().eras();for(e=0,t=r.length;ea&&(t=a),ip.call(this,e,t,n,r,i))}function ip(e,t,n,r,i){var a=th(e,t,n,r,i),o=tf(a.year,0,a.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}function ib(e){return null==e?Math.ceil((this.month()+1)/3):this.month((e-1)*3+this.month()%3)}B("N",0,0,"eraAbbr"),B("NN",0,0,"eraAbbr"),B("NNN",0,0,"eraAbbr"),B("NNNN",0,0,"eraName"),B("NNNNN",0,0,"eraNarrow"),B("y",["y",1],"yo","eraYear"),B("y",["yy",2],0,"eraYear"),B("y",["yyy",3],0,"eraYear"),B("y",["yyyy",4],0,"eraYear"),ej("N",r7),ej("NN",r7),ej("NNN",r7),ej("NNNN",ie),ej("NNNNN",it),eH(["N","NN","NNN","NNNN","NNNNN"],function(e,t,n,r){var i=n._locale.erasParse(e,r,n._strict);i?b(n).era=i:b(n).invalidEra=e}),ej("y",eC),ej("yy",eC),ej("yyy",eC),ej("yyyy",eC),ej("yo",ir),eH(["y","yy","yyy","yyyy"],eG),eH(["yo"],function(e,t,n,r){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[eG]=n._locale.eraYearOrdinalParse(e,i):t[eG]=parseInt(e,10)}),B(0,["gg",2],0,function(){return this.weekYear()%100}),B(0,["GG",2],0,function(){return this.isoWeekYear()%100}),ia("gggg","weekYear"),ia("ggggg","weekYear"),ia("GGGG","isoWeekYear"),ia("GGGGG","isoWeekYear"),en("weekYear","gg"),en("isoWeekYear","GG"),eo("weekYear",1),eo("isoWeekYear",1),ej("G",eI),ej("g",eI),ej("GG",ex,e_),ej("gg",ex,e_),ej("GGGG",eA,eS),ej("gggg",eA,eS),ej("GGGGG",eL,ek),ej("ggggg",eL,ek),e$(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,r){t[r.substr(0,2)]=el(e)}),e$(["gg","GG"],function(e,n,r,i){n[i]=t.parseTwoDigitYear(e)}),B("Q",0,"Qo","quarter"),en("quarter","Q"),eo("quarter",7),ej("Q",ew),eH("Q",function(e,t){t[eW]=(el(e)-1)*3}),B("D",["DD",2],"Do","date"),en("date","D"),eo("date",9),ej("D",ex),ej("DD",ex,e_),ej("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),eH(["D","DD"],eK),eH("Do",function(e,t){t[eK]=el(e.match(ex)[0])});var im=ef("Date",!0);function ig(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")}B("DDD",["DDDD",3],"DDDo","dayOfYear"),en("dayOfYear","DDD"),eo("dayOfYear",4),ej("DDD",eO),ej("DDDD",eE),eH(["DDD","DDDD"],function(e,t,n){n._dayOfYear=el(e)}),B("m",["mm",2],0,"minute"),en("minute","m"),eo("minute",14),ej("m",ex),ej("mm",ex,e_),eH(["m","mm"],eq);var iv=ef("Minutes",!1);B("s",["ss",2],0,"second"),en("second","s"),eo("second",15),ej("s",ex),ej("ss",ex,e_),eH(["s","ss"],eZ);var iy=ef("Seconds",!1);for(B("S",0,0,function(){return~~(this.millisecond()/100)}),B(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),B(0,["SSS",3],0,"millisecond"),B(0,["SSSS",4],0,function(){return 10*this.millisecond()}),B(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),B(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),B(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),B(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),B(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),en("millisecond","ms"),eo("millisecond",16),ej("S",eO,ew),ej("SS",eO,e_),ej("SSS",eO,eE),v="SSSS";v.length<=9;v+="S")ej(v,eC);function iw(e,t){t[eX]=el(("0."+e)*1e3)}for(v="S";v.length<=9;v+="S")eH(v,iw);function i_(){return this._isUTC?"UTC":""}function iE(){return this._isUTC?"Coordinated Universal Time":""}y=ef("Milliseconds",!1),B("z",0,0,"zoneAbbr"),B("zz",0,0,"zoneName");var iS=S.prototype;function ik(e){return nL(1e3*e)}function ix(){return nL.apply(null,arguments).parseZone()}function iT(e){return e}iS.add=ra,iS.calendar=rh,iS.clone=rp,iS.diff=r_,iS.endOf=rH,iS.format=rT,iS.from=rM,iS.fromNow=rO,iS.to=rA,iS.toNow=rL,iS.get=ep,iS.invalidAt=rX,iS.isAfter=rb,iS.isBefore=rm,iS.isBetween=rg,iS.isSame=rv,iS.isSameOrAfter=ry,iS.isSameOrBefore=rw,iS.isValid=rq,iS.lang=rI,iS.locale=rC,iS.localeData=rD,iS.max=nI,iS.min=nC,iS.parsingFlags=rZ,iS.set=eb,iS.startOf=rU,iS.subtract=ro,iS.toArray=rW,iS.toObject=rK,iS.toDate=rG,iS.toISOString=rk,iS.inspect=rx,"undefined"!=typeof Symbol&&null!=Symbol.for&&(iS[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),iS.toJSON=rV,iS.toString=rS,iS.unix=rz,iS.valueOf=r$,iS.creationData=rJ,iS.eraName=r2,iS.eraNarrow=r3,iS.eraAbbr=r4,iS.eraYear=r5,iS.year=tu,iS.isLeapYear=tc,iS.weekYear=io,iS.isoWeekYear=is,iS.quarter=iS.quarters=ib,iS.month=tn,iS.daysInMonth=tr,iS.week=iS.weeks=tw,iS.isoWeek=iS.isoWeeks=t_,iS.weeksInYear=il,iS.weeksInWeekYear=id,iS.isoWeeksInYear=iu,iS.isoWeeksInISOWeekYear=ic,iS.date=im,iS.day=iS.days=tR,iS.weekday=tj,iS.isoWeekday=tF,iS.dayOfYear=ig,iS.hour=iS.hours=tq,iS.minute=iS.minutes=iv,iS.second=iS.seconds=iy,iS.millisecond=iS.milliseconds=y,iS.utcOffset=nZ,iS.utc=nJ,iS.local=nQ,iS.parseZone=n1,iS.hasAlignedHourOffset=n0,iS.isDST=n2,iS.isLocal=n4,iS.isUtcOffset=n5,iS.isUtc=n6,iS.isUTC=n6,iS.zoneAbbr=i_,iS.zoneName=iE,iS.dates=T("dates accessor is deprecated. Use date instead.",im),iS.months=T("months accessor is deprecated. Use month instead",tn),iS.years=T("years accessor is deprecated. Use year instead",tu),iS.zone=T("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",nX),iS.isDSTShifted=T("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",n3);var iM=I.prototype;function iO(e,t,n,r){var i=t7(),a=h().set(r,t);return i[n](a,e)}function iA(e,t,n){if(c(e)&&(t=e,e=void 0),e=e||"",null!=t)return iO(e,t,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=iO(e,r,n,"month");return i}function iL(e,t,n,r){"boolean"==typeof e?(c(t)&&(n=t,t=void 0),t=t||""):(n=t=e,e=!1,c(t)&&(n=t,t=void 0),t=t||"");var i,a=t7(),o=e?a._week.dow:0,s=[];if(null!=n)return iO(t,(n+o)%7,r,"day");for(i=0;i<7;i++)s[i]=iO(t,(i+o)%7,r,"day");return s}function iC(e,t){return iA(e,t,"months")}function iI(e,t){return iA(e,t,"monthsShort")}function iD(e,t,n){return iL(e,t,n,"weekdays")}function iN(e,t,n){return iL(e,t,n,"weekdaysShort")}function iP(e,t,n){return iL(e,t,n,"weekdaysMin")}iM.calendar=N,iM.longDateFormat=W,iM.invalidDate=V,iM.ordinal=X,iM.preparse=iT,iM.postformat=iT,iM.relativeTime=Q,iM.pastFuture=ee,iM.set=L,iM.eras=rQ,iM.erasParse=r1,iM.erasConvertYear=r0,iM.erasAbbrRegex=r9,iM.erasNameRegex=r6,iM.erasNarrowRegex=r8,iM.months=e9,iM.monthsShort=e8,iM.monthsParse=te,iM.monthsRegex=ta,iM.monthsShortRegex=ti,iM.week=tm,iM.firstDayOfYear=ty,iM.firstDayOfWeek=tv,iM.weekdays=tC,iM.weekdaysMin=tD,iM.weekdaysShort=tI,iM.weekdaysParse=tP,iM.weekdaysRegex=tY,iM.weekdaysShortRegex=tB,iM.weekdaysMinRegex=tU,iM.isPM=tK,iM.meridiem=tZ,t6("en",{eras:[{since:"0001-01-01",until:Infinity,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"},],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10,n=1===el(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th";return e+n}}),t.lang=T("moment.lang is deprecated. Use moment.locale instead.",t6),t.langData=T("moment.langData is deprecated. Use moment.localeData instead.",t7);var iR=Math.abs;function ij(){var e=this._data;return this._milliseconds=iR(this._milliseconds),this._days=iR(this._days),this._months=iR(this._months),e.milliseconds=iR(e.milliseconds),e.seconds=iR(e.seconds),e.minutes=iR(e.minutes),e.hours=iR(e.hours),e.months=iR(e.months),e.years=iR(e.years),this}function iF(e,t,n,r){var i=n7(t,n);return e._milliseconds+=r*i._milliseconds,e._days+=r*i._days,e._months+=r*i._months,e._bubble()}function iY(e,t){return iF(this,e,t,1)}function iB(e,t){return iF(this,e,t,-1)}function iU(e){return e<0?Math.floor(e):Math.ceil(e)}function iH(){var e,t,n,r,i,a=this._milliseconds,o=this._days,s=this._months,u=this._data;return a>=0&&o>=0&&s>=0||a<=0&&o<=0&&s<=0||(a+=864e5*iU(iz(s)+o),o=0,s=0),u.milliseconds=a%1e3,e=ec(a/1e3),u.seconds=e%60,t=ec(e/60),u.minutes=t%60,n=ec(t/60),u.hours=n%24,o+=ec(n/24),s+=i=ec(i$(o)),o-=iU(iz(i)),r=ec(s/12),s%=12,u.days=o,u.months=s,u.years=r,this}function i$(e){return 4800*e/146097}function iz(e){return 146097*e/4800}function iG(e){if(!this.isValid())return NaN;var t,n,r=this._milliseconds;if("month"===(e=er(e))||"quarter"===e||"year"===e)switch(t=this._days+r/864e5,n=this._months+i$(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(iz(this._months)),e){case"week":return t/7+r/6048e5;case"day":return t+r/864e5;case"hour":return 24*t+r/36e5;case"minute":return 1440*t+r/6e4;case"second":return 86400*t+r/1e3;case"millisecond":return Math.floor(864e5*t)+r;default:throw Error("Unknown unit "+e)}}function iW(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*el(this._months/12):NaN}function iK(e){return function(){return this.as(e)}}var iV=iK("ms"),iq=iK("s"),iZ=iK("m"),iX=iK("h"),iJ=iK("d"),iQ=iK("w"),i1=iK("M"),i0=iK("Q"),i2=iK("y");function i3(){return n7(this)}function i4(e){return e=er(e),this.isValid()?this[e+"s"]():NaN}function i5(e){return function(){return this.isValid()?this._data[e]:NaN}}var i6=i5("milliseconds"),i9=i5("seconds"),i8=i5("minutes"),i7=i5("hours"),ae=i5("days"),at=i5("months"),an=i5("years");function ar(){return ec(this.days()/7)}var ai=Math.round,aa={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function ao(e,t,n,r,i){return i.relativeTime(t||1,!!n,e,r)}function as(e,t,n,r){var i=n7(e).abs(),a=ai(i.as("s")),o=ai(i.as("m")),s=ai(i.as("h")),u=ai(i.as("d")),c=ai(i.as("M")),l=ai(i.as("w")),f=ai(i.as("y")),d=a<=n.ss&&["s",a]||a0,d[4]=r,ao.apply(null,d)}function au(e){return void 0===e?ai:"function"==typeof e&&(ai=e,!0)}function ac(e,t){return void 0!==aa[e]&&(void 0===t?aa[e]:(aa[e]=t,"s"===e&&(aa.ss=t-1),!0))}function al(e,t){if(!this.isValid())return this.localeData().invalidDate();var n,r,i=!1,a=aa;return"object"==typeof e&&(t=e,e=!1),"boolean"==typeof e&&(i=e),"object"==typeof t&&(a=Object.assign({},aa,t),null!=t.s&&null==t.ss&&(a.ss=t.s-1)),r=as(this,!i,a,n=this.localeData()),i&&(r=n.pastFuture(+this,r)),n.postformat(r)}var af=Math.abs;function ad(e){return(e>0)-(e<0)||+e}function ah(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,r,i,a,o,s,u=af(this._milliseconds)/1e3,c=af(this._days),l=af(this._months),f=this.asSeconds();return f?(e=ec(u/60),t=ec(e/60),u%=60,e%=60,n=ec(l/12),l%=12,r=u?u.toFixed(3).replace(/\.?0+$/,""):"",i=f<0?"-":"",a=ad(this._months)!==ad(f)?"-":"",o=ad(this._days)!==ad(f)?"-":"",s=ad(this._milliseconds)!==ad(f)?"-":"",i+"P"+(n?a+n+"Y":"")+(l?a+l+"M":"")+(c?o+c+"D":"")+(t||e||u?"T":"")+(t?s+t+"H":"")+(e?s+e+"M":"")+(u?s+r+"S":"")):"P0D"}var ap=nU.prototype;return ap.isValid=nY,ap.abs=ij,ap.add=iY,ap.subtract=iB,ap.as=iG,ap.asMilliseconds=iV,ap.asSeconds=iq,ap.asMinutes=iZ,ap.asHours=iX,ap.asDays=iJ,ap.asWeeks=iQ,ap.asMonths=i1,ap.asQuarters=i0,ap.asYears=i2,ap.valueOf=iW,ap._bubble=iH,ap.clone=i3,ap.get=i4,ap.milliseconds=i6,ap.seconds=i9,ap.minutes=i8,ap.hours=i7,ap.days=ae,ap.weeks=ar,ap.months=at,ap.years=an,ap.humanize=al,ap.toISOString=ah,ap.toString=ah,ap.toJSON=ah,ap.locale=rC,ap.localeData=rD,ap.toIsoString=T("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ah),ap.lang=rI,B("X",0,0,"unix"),B("x",0,0,"valueOf"),ej("x",eI),ej("X",eP),eH("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e))}),eH("x",function(e,t,n){n._d=new Date(el(e))}),//! moment.js +t.version="2.29.1",r(nL),t.fn=iS,t.min=nN,t.max=nP,t.now=nR,t.utc=h,t.unix=ik,t.months=iC,t.isDate=l,t.locale=t6,t.invalid=g,t.duration=n7,t.isMoment=k,t.weekdays=iD,t.parseZone=ix,t.localeData=t7,t.isDuration=nH,t.monthsShort=iI,t.weekdaysMin=iP,t.defineLocale=t9,t.updateLocale=t8,t.locales=ne,t.weekdaysShort=iN,t.normalizeUnits=er,t.relativeTimeRounding=au,t.relativeTimeThreshold=ac,t.calendarFormat=rd,t.prototype=iS,t.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},t},e.exports=i()},46417(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,r=!!("undefined"!=typeof window&&window.document&&window.document.createElement);function i(e){n=e}function a(){if(n)return n;if(!r||!window.document.body)return"indeterminate";var e=window.document.createElement("div");return e.appendChild(document.createTextNode("ABCD")),e.dir="rtl",e.style.fontSize="14px",e.style.width="4px",e.style.height="1px",e.style.position="absolute",e.style.top="-1000px",e.style.overflow="scroll",document.body.appendChild(e),n="reverse",e.scrollLeft>0?n="default":(e.scrollLeft=1,0===e.scrollLeft&&(n="negative")),document.body.removeChild(e),n}function o(e,t){var n=e.scrollLeft;if("rtl"!==t)return n;var r=a();if("indeterminate"===r)return Number.NaN;switch(r){case"negative":return e.scrollWidth-e.clientWidth+n;case"reverse":return e.scrollWidth-e.clientWidth-n}return n}function s(e,t,n){if("rtl"!==n){e.scrollLeft=t;return}var r=a();if("indeterminate"!==r)switch(r){case"negative":e.scrollLeft=e.clientWidth-e.scrollWidth+t;break;case"reverse":e.scrollLeft=e.scrollWidth-e.clientWidth-t;break;default:e.scrollLeft=t}}t._setScrollType=i,t.detectScrollType=a,t.getNormalizedScrollLeft=o,t.setNormalizedScrollLeft=s},27418(e){"use strict";/* +object-assign +(c) Sindre Sorhus +@license MIT +*/ var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;function i(e){if(null==e)throw TypeError("Object.assign cannot be called with null or undefined");return Object(e)}function a(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;var r=Object.getOwnPropertyNames(t).map(function(e){return t[e]});if("0123456789"!==r.join(""))return!1;var i={};if("abcdefghijklmnopqrst".split("").forEach(function(e){i[e]=e}),"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},i)).join(""))return!1;return!0}catch(a){return!1}}e.exports=a()?Object.assign:function(e,a){for(var o,s,u=i(e),c=1;c65535&&(Y-=65536,G+=l(Y>>>10|55296),Y=56320|1023&Y),Y=G+l(Y))):q!==x&&$(D,Q)),Y?(ew(),X=ev(),ed=ee-1,ep+=ee-V+1,eg.push(Y),J=ev(),J.offset++,ei&&ei.call(es,Y,{start:X,end:J},e.slice(V-1,ee)),X=J):(em+=d=e.slice(V-1,ee),ep+=d.length,ed=ee-1)}else 10===F&&(eb++,eh++,ep=0),F==F?(em+=l(F),ep++):ew();return eg.join("");function ev(){return{line:eb,column:ep,offset:ed+(ec.offset||0)}}function ey(e,t){var n=ev();n.column+=t,n.offset+=t,ea.call(eu,j[e],n,e)}function ew(){em&&(eg.push(em),er&&er.call(eo,em,{start:X,end:ev()}),em="")}}function B(e){return e>=55296&&e<=57343||e>1114111}function U(e){return e>=1&&e<=8||11===e||e>=13&&e<=31||e>=127&&e<=159||e>=64976&&e<=65007||(65535&e)==65535||(65535&e)==65534}j[L]="Named character references must be terminated by a semicolon",j[C]="Numeric character references must be terminated by a semicolon",j[I]="Named character references cannot be empty",j[D]="Numeric character references cannot be empty",j[N]="Named character references must be known",j[P]="Numeric character references cannot be disallowed",j[R]="Numeric character references cannot be outside the permissible Unicode range"},14779(e){e.exports=b,e.exports.match=a,e.exports.regexpToFunction=o,e.exports.parse=r,e.exports.compile=i,e.exports.tokensToFunction=s,e.exports.tokensToRegExp=p;var t="/",n=RegExp("(\\\\.)|(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?","g");function r(e,r){for(var i,a=[],o=0,s=0,l="",f=r&&r.delimiter||t,d=r&&r.whitelist||void 0,h=!1;null!==(i=n.exec(e));){var p=i[0],b=i[1],m=i.index;if(l+=e.slice(s,m),s=m+p.length,b){l+=b[1],h=!0;continue}var g="",v=i[2],y=i[3],w=i[4],_=i[5];if(!h&&l.length){var E=l.length-1,S=l[E];(!d||d.indexOf(S)>-1)&&(g=S,l=l.slice(0,E))}l&&(a.push(l),l="",h=!1);var k="+"===_||"*"===_,x="?"===_||"*"===_,T=y||w,M=g||f;a.push({name:v||o++,prefix:g,delimiter:M,optional:x,repeat:k,pattern:T?c(T):"[^"+u(M===f?M:M+f)+"]+?"})}return(l||seM});/**! + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version 1.16.0 + * @license + * Copyright (c) 2016 Federico Zivolo and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ var r="undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof navigator,i=function(){for(var e=["Edge","Trident","Firefox"],t=0;t=0)return 1;return 0}();function a(e){var t=!1;return function(){!t&&(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}function o(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},i))}}var s=r&&window.Promise?a:o;function u(e){var t={};return e&&"[object Function]"===t.toString.call(e)}function c(e,t){if(1!==e.nodeType)return[];var n=e.ownerDocument.defaultView.getComputedStyle(e,null);return t?n[t]:n}function l(e){return"HTML"===e.nodeName?e:e.parentNode||e.host}function f(e){if(!e)return document.body;switch(e.nodeName){case"HTML":case"BODY":return e.ownerDocument.body;case"#document":return e.body}var t=c(e),n=t.overflow,r=t.overflowX,i=t.overflowY;return/(auto|scroll|overlay)/.test(n+i+r)?e:f(l(e))}function d(e){return e&&e.referenceNode?e.referenceNode:e}var h=r&&!!(window.MSInputMethodContext&&document.documentMode),p=r&&/MSIE 10/.test(navigator.userAgent);function b(e){return 11===e?h:10===e?p:h||p}function m(e){if(!e)return document.documentElement;for(var t=b(10)?document.body:null,n=e.offsetParent||null;n===t&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var r=n&&n.nodeName;return r&&"BODY"!==r&&"HTML"!==r?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===c(n,"position")?m(n):n:e?e.ownerDocument.documentElement:document.documentElement}function g(e){var t=e.nodeName;return"BODY"!==t&&("HTML"===t||m(e.firstElementChild)===e)}function v(e){return null!==e.parentNode?v(e.parentNode):e}function y(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var n=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,r=n?e:t,i=n?t:e,a=document.createRange();a.setStart(r,0),a.setEnd(i,0);var o=a.commonAncestorContainer;if(e!==o&&t!==o||r.contains(i))return g(o)?o:m(o);var s=v(e);return s.host?y(s.host,t):y(e,v(t).host)}function w(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===t?"scrollTop":"scrollLeft",r=e.nodeName;if("BODY"===r||"HTML"===r){var i=e.ownerDocument.documentElement;return(e.ownerDocument.scrollingElement||i)[n]}return e[n]}function _(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=w(t,"top"),i=w(t,"left"),a=n?-1:1;return e.top+=r*a,e.bottom+=r*a,e.left+=i*a,e.right+=i*a,e}function E(e,t){var n="x"===t?"Left":"Top",r="Left"===n?"Right":"Bottom";return parseFloat(e["border"+n+"Width"],10)+parseFloat(e["border"+r+"Width"],10)}function S(e,t,n,r){return Math.max(t["offset"+e],t["scroll"+e],n["client"+e],n["offset"+e],n["scroll"+e],b(10)?parseInt(n["offset"+e])+parseInt(r["margin"+("Height"===e?"Top":"Left")])+parseInt(r["margin"+("Height"===e?"Bottom":"Right")]):0)}function k(e){var t=e.body,n=e.documentElement,r=b(10)&&getComputedStyle(n);return{height:S("Height",t,n,r),width:S("Width",t,n,r)}}var x=function(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")},T=function(){function e(e,t){for(var n=0;n2&&void 0!==arguments[2]&&arguments[2],r=b(10),i="HTML"===t.nodeName,a=L(e),o=L(t),s=f(e),u=c(t),l=parseFloat(u.borderTopWidth,10),d=parseFloat(u.borderLeftWidth,10);n&&i&&(o.top=Math.max(o.top,0),o.left=Math.max(o.left,0));var h=A({top:a.top-o.top-l,left:a.left-o.left-d,width:a.width,height:a.height});if(h.marginTop=0,h.marginLeft=0,!r&&i){var p=parseFloat(u.marginTop,10),m=parseFloat(u.marginLeft,10);h.top-=l-p,h.bottom-=l-p,h.left-=d-m,h.right-=d-m,h.marginTop=p,h.marginLeft=m}return(r&&!n?t.contains(s):t===s&&"BODY"!==s.nodeName)&&(h=_(h,t)),h}function I(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=e.ownerDocument.documentElement,r=C(e,n),i=Math.max(n.clientWidth,window.innerWidth||0),a=Math.max(n.clientHeight,window.innerHeight||0),o=t?0:w(n),s=t?0:w(n,"left");return A({top:o-r.top+r.marginTop,left:s-r.left+r.marginLeft,width:i,height:a})}function D(e){var t=e.nodeName;if("BODY"===t||"HTML"===t)return!1;if("fixed"===c(e,"position"))return!0;var n=l(e);return!!n&&D(n)}function N(e){if(!e||!e.parentElement||b())return document.documentElement;for(var t=e.parentElement;t&&"none"===c(t,"transform");)t=t.parentElement;return t||document.documentElement}function P(e,t,n,r){var i=arguments.length>4&&void 0!==arguments[4]&&arguments[4],a={top:0,left:0},o=i?N(e):y(e,d(t));if("viewport"===r)a=I(o,i);else{var s=void 0;"scrollParent"===r?"BODY"===(s=f(l(t))).nodeName&&(s=e.ownerDocument.documentElement):s="window"===r?e.ownerDocument.documentElement:r;var u=C(s,o,i);if("HTML"!==s.nodeName||D(o))a=u;else{var c=k(e.ownerDocument),h=c.height,p=c.width;a.top+=u.top-u.marginTop,a.bottom=h+u.top,a.left+=u.left-u.marginLeft,a.right=p+u.left}}var b="number"==typeof(n=n||0);return a.left+=b?n:n.left||0,a.top+=b?n:n.top||0,a.right-=b?n:n.right||0,a.bottom-=b?n:n.bottom||0,a}function R(e){var t;return e.width*e.height}function j(e,t,n,r,i){var a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf("auto"))return e;var o=P(n,r,a,i),s={top:{width:o.width,height:t.top-o.top},right:{width:o.right-t.right,height:o.height},bottom:{width:o.width,height:o.bottom-t.bottom},left:{width:t.left-o.left,height:o.height}},u=Object.keys(s).map(function(e){return O({key:e},s[e],{area:R(s[e])})}).sort(function(e,t){return t.area-e.area}),c=u.filter(function(e){var t=e.width,r=e.height;return t>=n.clientWidth&&r>=n.clientHeight}),l=c.length>0?c[0].key:u[0].key,f=e.split("-")[1];return l+(f?"-"+f:"")}function F(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,i=r?N(t):y(t,d(n));return C(n,i,r)}function Y(e){var t=e.ownerDocument.defaultView.getComputedStyle(e),n=parseFloat(t.marginTop||0)+parseFloat(t.marginBottom||0),r=parseFloat(t.marginLeft||0)+parseFloat(t.marginRight||0);return{width:e.offsetWidth+r,height:e.offsetHeight+n}}function B(e){var t={left:"right",right:"left",bottom:"top",top:"bottom"};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function U(e,t,n){n=n.split("-")[0];var r=Y(e),i={width:r.width,height:r.height},a=-1!==["right","left"].indexOf(n),o=a?"top":"left",s=a?"left":"top",u=a?"height":"width",c=a?"width":"height";return i[o]=t[o]+t[u]/2-r[u]/2,n===s?i[s]=t[s]-r[c]:i[s]=t[B(s)],i}function H(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function $(e,t,n){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===n});var r=H(e,function(e){return e[t]===n});return e.indexOf(r)}function z(e,t,n){return(void 0===n?e:e.slice(0,$(e,"name",n))).forEach(function(e){e.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=e.function||e.fn;e.enabled&&u(n)&&(t.offsets.popper=A(t.offsets.popper),t.offsets.reference=A(t.offsets.reference),t=n(t,e))}),t}function G(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=F(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=j(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=U(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",e=z(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var n=e.name;return e.enabled&&n===t})}function K(e){for(var t=[!1,"ms","Webkit","Moz","O"],n=e.charAt(0).toUpperCase()+e.slice(1),r=0;ro[p]&&(e.offsets.popper[d]+=s[d]+b-o[p]),e.offsets.popper=A(e.offsets.popper);var m=s[d]+s[l]/2-b/2,g=c(e.instance.popper),v=parseFloat(g["margin"+f],10),y=parseFloat(g["border"+f+"Width"],10),w=m-e.offsets.popper[d]-v-y;return w=Math.max(Math.min(o[l]-b,w),0),e.arrowElement=r,e.offsets.arrow=(M(n={},d,Math.round(w)),M(n,h,""),n),e}function ef(e){return"end"===e?"start":"start"===e?"end":e}var ed=["auto-start","auto","auto-end","top-start","top","top-end","right-start","right","right-end","bottom-end","bottom","bottom-start","left-end","left","left-start"],eh=ed.slice(3);function ep(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=eh.indexOf(e),r=eh.slice(n+1).concat(eh.slice(0,n));return t?r.reverse():r}var eb={FLIP:"flip",CLOCKWISE:"clockwise",COUNTERCLOCKWISE:"counterclockwise"};function em(e,t){if(W(e.instance.modifiers,"inner")||e.flipped&&e.placement===e.originalPlacement)return e;var n=P(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),r=e.placement.split("-")[0],i=B(r),a=e.placement.split("-")[1]||"",o=[];switch(t.behavior){case eb.FLIP:o=[r,i];break;case eb.CLOCKWISE:o=ep(r);break;case eb.COUNTERCLOCKWISE:o=ep(r,!0);break;default:o=t.behavior}return o.forEach(function(s,u){if(r!==s||o.length===u+1)return e;i=B(r=e.placement.split("-")[0]);var c=e.offsets.popper,l=e.offsets.reference,f=Math.floor,d="left"===r&&f(c.right)>f(l.left)||"right"===r&&f(c.left)f(l.top)||"bottom"===r&&f(c.top)f(n.right),b=f(c.top)f(n.bottom),g="left"===r&&h||"right"===r&&p||"top"===r&&b||"bottom"===r&&m,v=-1!==["top","bottom"].indexOf(r),y=!!t.flipVariations&&(v&&"start"===a&&h||v&&"end"===a&&p||!v&&"start"===a&&b||!v&&"end"===a&&m),w=!!t.flipVariationsByContent&&(v&&"start"===a&&p||v&&"end"===a&&h||!v&&"start"===a&&m||!v&&"end"===a&&b),_=y||w;(d||g||_)&&(e.flipped=!0,(d||g)&&(r=o[u+1]),_&&(a=ef(a)),e.placement=r+(a?"-"+a:""),e.offsets.popper=O({},e.offsets.popper,U(e.instance.popper,e.offsets.reference,e.placement)),e=z(e.instance.modifiers,e,"flip"))}),e}function eg(e){var t=e.offsets,n=t.popper,r=t.reference,i=e.placement.split("-")[0],a=Math.floor,o=-1!==["top","bottom"].indexOf(i),s=o?"right":"bottom",u=o?"left":"top",c=o?"width":"height";return n[s]a(r[s])&&(e.offsets.popper[u]=a(r[s])),e}function ev(e,t,n,r){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),a=+i[1],o=i[2];if(!a)return e;if(0===o.indexOf("%")){var s=void 0;return A(s="%p"===o?n:r)[t]/100*a}if("vh"!==o&&"vw"!==o)return a;var u=void 0;return(u="vh"===o?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*a}function ey(e,t,n,r){var i=[0,0],a=-1!==["right","left"].indexOf(r),o=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=o.indexOf(H(o,function(e){return -1!==e.search(/,|\s/)}));o[s]&&-1===o[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var u=/\s*,\s*|\s+/,c=-1!==s?[o.slice(0,s).concat([o[s].split(u)[0]]),[o[s].split(u)[1]].concat(o.slice(s+1))]:[o];return(c=c.map(function(e,r){var i=(1===r?!a:a)?"height":"width",o=!1;return e.reduce(function(e,t){return""===e[e.length-1]&&-1!==["+","-"].indexOf(t)?(e[e.length-1]=t,o=!0,e):o?(e[e.length-1]+=t,o=!1,e):e.concat(t)},[]).map(function(e){return ev(e,i,t,n)})})).forEach(function(e,t){e.forEach(function(n,r){et(n)&&(i[t]+=n*("-"===e[r-1]?-1:1))})}),i}function ew(e,t){var n=t.offset,r=e.placement,i=e.offsets,a=i.popper,o=i.reference,s=r.split("-")[0],u=void 0;return u=et(+n)?[+n,0]:ey(n,a,o,s),"left"===s?(a.top+=u[0],a.left-=u[1]):"right"===s?(a.top+=u[0],a.left+=u[1]):"top"===s?(a.left+=u[0],a.top-=u[1]):"bottom"===s&&(a.left+=u[0],a.top+=u[1]),e.popper=a,e}function e_(e,t){var n=t.boundariesElement||m(e.instance.popper);e.instance.reference===n&&(n=m(n));var r=K("transform"),i=e.instance.popper.style,a=i.top,o=i.left,s=i[r];i.top="",i.left="",i[r]="";var u=P(e.instance.popper,e.instance.reference,t.padding,n,e.positionFixed);i.top=a,i.left=o,i[r]=s,t.boundaries=u;var c=t.priority,l=e.offsets.popper,f={primary:function(e){var n=l[e];return l[e]u[e]&&!t.escapeWithReference&&(r=Math.min(l[n],u[e]-("right"===e?l.width:l.height))),M({},n,r)}};return c.forEach(function(e){l=O({},l,f[-1!==["left","top"].indexOf(e)?"primary":"secondary"](e))}),e.offsets.popper=l,e}function eE(e){var t=e.placement,n=t.split("-")[0],r=t.split("-")[1];if(r){var i=e.offsets,a=i.reference,o=i.popper,s=-1!==["bottom","top"].indexOf(n),u=s?"left":"top",c=s?"width":"height",l={start:M({},u,a[u]),end:M({},u,a[u]+a[c]-o[c])};e.offsets.popper=O({},o,l[r])}return e}function eS(e){if(!ec(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=H(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.right2&&void 0!==arguments[2]?arguments[2]:{};x(this,e),this.scheduleUpdate=function(){return requestAnimationFrame(r.update)},this.update=s(this.update.bind(this)),this.options=O({},e.Defaults,i),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=t&&t.jquery?t[0]:t,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(O({},e.Defaults.modifiers,i.modifiers)).forEach(function(t){r.options.modifiers[t]=O({},e.Defaults.modifiers[t]||{},i.modifiers?i.modifiers[t]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return O({name:e},r.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(e){e.enabled&&u(e.onLoad)&&e.onLoad(r.reference,r.popper,r.options,e,r.state)}),this.update();var a=this.options.eventsEnabled;a&&this.enableEventListeners(),this.state.eventsEnabled=a}return T(e,[{key:"update",value:function(){return G.call(this)}},{key:"destroy",value:function(){return V.call(this)}},{key:"enableEventListeners",value:function(){return J.call(this)}},{key:"disableEventListeners",value:function(){return ee.call(this)}}]),e}();eT.Utils=("undefined"!=typeof window?window:n.g).PopperUtils,eT.placements=ed,eT.Defaults=ex;let eM=eT},92703(e,t,n){"use strict";var r=n(50414);function i(){}function a(){}a.resetWarningCache=i,e.exports=function(){function e(e,t,n,i,a,o){if(o!==r){var s=Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:a,resetWarningCache:i};return n.PropTypes=n,n}},45697(e,t,n){e.exports=n(92703)()},50414(e){"use strict";var t="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";e.exports=t},55760(e){"use strict";function t(e){this._maxSize=e,this.clear()}t.prototype.clear=function(){this._size=0,this._values=Object.create(null)},t.prototype.get=function(e){return this._values[e]},t.prototype.set=function(e,t){return this._size>=this._maxSize&&this.clear(),!(e in this._values)&&this._size++,this._values[e]=t};var n=/[^.^\]^[]+|(?=\[\]|\.\.)/g,r=/^\d+$/,i=/^\d/,a=/[~`!#$%\^&*+=\-\[\]\\';,/{}|\\":<>\?]/g,o=/^\s*(['"]?)(.*?)(\1)\s*$/,s=512,u=new t(s),c=new t(s),l=new t(s);function f(e){return u.get(e)||u.set(e,d(e).map(function(e){return e.replace(o,"$2")}))}function d(e){return e.match(n)}function h(e,t,n){var r,i,a,o,s=e.length;for(i=0;i4&&n.slice(0,4)===o&&s.test(t)&&("-"===t.charAt(4)?u=f(t):t=d(t),c=i),new c(u,t))}function f(e){var t=e.slice(5).replace(u,p);return o+t.charAt(0).toUpperCase()+t.slice(1)}function d(e){var t=e.slice(4);return u.test(t)?e:("-"!==(t=t.replace(c,h)).charAt(0)&&(t="-"+t),o+t)}function h(e){return"-"+e.toLowerCase()}function p(e){return e.charAt(1).toUpperCase()}},97247(e,t,n){"use strict";var r=n(19940),i=n(8289),a=n(5812),o=n(94397),s=n(67716),u=n(61805);e.exports=r([a,i,o,s,u])},67716(e,t,n){"use strict";var r=n(17e3),i=n(17596),a=r.booleanish,o=r.number,s=r.spaceSeparated;function u(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()}e.exports=i({transform:u,properties:{ariaActiveDescendant:null,ariaAtomic:a,ariaAutoComplete:null,ariaBusy:a,ariaChecked:a,ariaColCount:o,ariaColIndex:o,ariaColSpan:o,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:a,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:a,ariaFlowTo:s,ariaGrabbed:a,ariaHasPopup:null,ariaHidden:a,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:o,ariaLive:null,ariaModal:a,ariaMultiLine:a,ariaMultiSelectable:a,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:o,ariaPressed:a,ariaReadOnly:a,ariaRelevant:null,ariaRequired:a,ariaRoleDescription:s,ariaRowCount:o,ariaRowIndex:o,ariaRowSpan:o,ariaSelected:a,ariaSetSize:o,ariaSort:null,ariaValueMax:o,ariaValueMin:o,ariaValueNow:o,ariaValueText:null,role:null}})},61805(e,t,n){"use strict";var r=n(17e3),i=n(17596),a=n(10855),o=r.boolean,s=r.overloadedBoolean,u=r.booleanish,c=r.number,l=r.spaceSeparated,f=r.commaSeparated;e.exports=i({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:a,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:f,acceptCharset:l,accessKey:l,action:null,allow:null,allowFullScreen:o,allowPaymentRequest:o,allowUserMedia:o,alt:null,as:null,async:o,autoCapitalize:null,autoComplete:l,autoFocus:o,autoPlay:o,capture:o,charSet:null,checked:o,cite:null,className:l,cols:c,colSpan:null,content:null,contentEditable:u,controls:o,controlsList:l,coords:c|f,crossOrigin:null,data:null,dateTime:null,decoding:null,default:o,defer:o,dir:null,dirName:null,disabled:o,download:s,draggable:u,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:o,formTarget:null,headers:l,height:c,hidden:o,high:c,href:null,hrefLang:null,htmlFor:l,httpEquiv:l,id:null,imageSizes:null,imageSrcSet:f,inputMode:null,integrity:null,is:null,isMap:o,itemId:null,itemProp:l,itemRef:l,itemScope:o,itemType:l,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:o,low:c,manifest:null,max:null,maxLength:c,media:null,method:null,min:null,minLength:c,multiple:o,muted:o,name:null,nonce:null,noModule:o,noValidate:o,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:o,optimum:c,pattern:null,ping:l,placeholder:null,playsInline:o,poster:null,preload:null,readOnly:o,referrerPolicy:null,rel:l,required:o,reversed:o,rows:c,rowSpan:c,sandbox:l,scope:null,scoped:o,seamless:o,selected:o,shape:null,size:c,sizes:null,slot:null,span:c,spellCheck:u,src:null,srcDoc:null,srcLang:null,srcSet:f,start:c,step:null,style:null,tabIndex:c,target:null,title:null,translate:null,type:null,typeMustMatch:o,useMap:null,value:u,width:c,wrap:null,align:null,aLink:null,archive:l,axis:null,background:null,bgColor:null,border:c,borderColor:null,bottomMargin:c,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:o,declare:o,event:null,face:null,frame:null,frameBorder:null,hSpace:c,leftMargin:c,link:null,longDesc:null,lowSrc:null,marginHeight:c,marginWidth:c,noResize:o,noHref:o,noShade:o,noWrap:o,object:null,profile:null,prompt:null,rev:null,rightMargin:c,rules:null,scheme:null,scrolling:u,standby:null,summary:null,text:null,topMargin:c,valueType:null,version:null,vAlign:null,vLink:null,vSpace:c,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:o,disableRemotePlayback:o,prefix:null,property:null,results:c,security:null,unselectable:null}})},10855(e,t,n){"use strict";var r=n(28740);function i(e,t){return r(e,t.toLowerCase())}e.exports=i},28740(e){"use strict";function t(e,t){return t in e?e[t]:t}e.exports=t},17596(e,t,n){"use strict";var r=n(66632),i=n(99607),a=n(81674);function o(e){var t,n,o=e.space,s=e.mustUseProperty||[],u=e.attributes||{},c=e.properties,l=e.transform,f={},d={};for(t in c)n=new a(t,l(u,t),c[t],o),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),f[t]=n,d[r(t)]=t,d[r(n.attribute)]=t;return new i(f,d,o)}e.exports=o},81674(e,t,n){"use strict";var r=n(57643),i=n(17e3);e.exports=s,s.prototype=new r,s.prototype.defined=!0;var a=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],o=a.length;function s(e,t,n,s){var c,l=-1;for(u(this,"space",s),r.call(this,e,t);++l=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function l(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function d(e,t){for(var n=0;n1&&void 0!==arguments[1]&&arguments[1];return n._tick((0,d.updateNodeHighlightedValue)(n.state.nodes,n.state.links,n.state.config,e,t))}),O(S(n),"_tick",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;return t?n.setState(e,t):n.setState(e)}),O(S(n),"_zoomConfig",function(){var e=(0,o.select)("#".concat(n.state.id,"-").concat(u.default.GRAPH_WRAPPER_ID)),t=(0,s.zoom)().scaleExtent([n.state.config.minZoom,n.state.config.maxZoom]);n.state.config.freezeAllDragEvents||t.on("zoom",n._zoomed),null!==n.state.config.initialZoom&&t.scaleTo(e,n.state.config.initialZoom),e.call(t).on("dblclick.zoom",null)}),O(S(n),"_zoomed",function(){var e=o.event.transform;(0,o.selectAll)("#".concat(n.state.id,"-").concat(u.default.GRAPH_CONTAINER_ID)).attr("transform",e),n.state.config.panAndZoom&&n.setState({transform:e.k}),n.debouncedOnZoomChange&&n.state.previousZoom!==e.k&&(n.debouncedOnZoomChange(n.state.previousZoom,e.k),n.setState({previousZoom:e.k}))}),O(S(n),"onClickGraph",function(e){n.state.enableFocusAnimation&&n.setState({enableFocusAnimation:!1});var t,r,i,a=e.target&&e.target.tagName,o=null==e?void 0:null===(t=e.target)||void 0===t?void 0:null===(r=t.attributes)||void 0===r?void 0:null===(i=r.name)||void 0===i?void 0:i.value,s="svg-container-".concat(n.state.id);"SVG"===a.toUpperCase()&&o===s&&n.props.onClickGraph&&n.props.onClickGraph(e)}),O(S(n),"onClickNode",function(e){var t=n.state.nodes[e];if(n.state.config.collapsible){var r=(0,f.getTargetLeafConnections)(e,n.state.links,n.state.config),i=(0,f.toggleLinksMatrixConnections)(n.state.links,r,n.state.config),a=(0,f.toggleLinksConnections)(n.state.d3Links,i),o=null==r?void 0:r["0"],s=!1;o&&(s=1===i[o.source][o.target]),n._tick({links:i,d3Links:a},function(){n.props.onClickNode&&n.props.onClickNode(e,t),s&&n._graphNodeDragConfig()})}else n.nodeClickTimer?(n.props.onDoubleClickNode&&n.props.onDoubleClickNode(e,t),n.nodeClickTimer=clearTimeout(n.nodeClickTimer)):n.nodeClickTimer=setTimeout(function(){n.props.onClickNode&&n.props.onClickNode(e,t),n.nodeClickTimer=null},u.default.TTL_DOUBLE_CLICK_IN_MS)}),O(S(n),"onRightClickNode",function(e,t){var r=n.state.nodes[t];n.props.onRightClickNode&&n.props.onRightClickNode(e,t,r)}),O(S(n),"onMouseOverNode",function(e){if(!n.isDraggingNode){var t=n.state.nodes[e];n.props.onMouseOverNode&&n.props.onMouseOverNode(e,t),n.state.config.nodeHighlightBehavior&&n._setNodeHighlightedValue(e,!0)}}),O(S(n),"onMouseOutNode",function(e){if(!n.isDraggingNode){var t=n.state.nodes[e];n.props.onMouseOutNode&&n.props.onMouseOutNode(e,t),n.state.config.nodeHighlightBehavior&&n._setNodeHighlightedValue(e,!1)}}),O(S(n),"onMouseOverLink",function(e,t){if(n.props.onMouseOverLink&&n.props.onMouseOverLink(e,t),n.state.config.linkHighlightBehavior){var r={source:e,target:t};n._tick({highlightedLink:r})}}),O(S(n),"onMouseOutLink",function(e,t){if(n.props.onMouseOutLink&&n.props.onMouseOutLink(e,t),n.state.config.linkHighlightBehavior){var r=void 0;n._tick({highlightedLink:r})}}),O(S(n),"onNodePositionChange",function(e){if(n.props.onNodePositionChange){var t=e.id,r=e.x,i=e.y;n.props.onNodePositionChange(t,r,i)}}),O(S(n),"pauseSimulation",function(){return n.state.simulation.stop()}),O(S(n),"resetNodesPositions",function(){if(!n.state.config.staticGraph){var e=(0,d.initializeNodes)(n.props.data.nodes);for(var t in n.state.nodes){var r=n.state.nodes[t];if(r.fx&&r.fy&&(Reflect.deleteProperty(r,"fx"),Reflect.deleteProperty(r,"fy")),t in e){var i=e[t];r.x=i.x,r.y=i.y}}n.state.simulation.alphaTarget(n.state.config.d3.alphaTarget).restart(),n._tick()}}),O(S(n),"restartSimulation",function(){return!n.state.config.staticGraph&&n.state.simulation.restart()}),n.props.id||(0,p.throwErr)(n.constructor.name,l.default.GRAPH_NO_ID_PROP),n.focusAnimationTimeout=null,n.nodeClickTimer=null,n.isDraggingNode=!1,n.state=(0,d.initializeGraphState)(n.props,n.state),n.debouncedOnZoomChange=n.props.onZoomChange?(0,p.debounce)(n.props.onZoomChange,100):null,n}return T(t,e),x(t,[{key:"_graphLinkForceConfig",value:function(){var e=(0,a.forceLink)(this.state.d3Links).id(function(e){return e.id}).distance(this.state.config.d3.linkLength).strength(this.state.config.d3.linkStrength);this.state.simulation.force(u.default.LINK_CLASS_NAME,e)}},{key:"_graphNodeDragConfig",value:function(){var e=(0,i.drag)().on("start",this._onDragStart).on("drag",this._onDragMove).on("end",this._onDragEnd);(0,o.select)("#".concat(this.state.id,"-").concat(u.default.GRAPH_WRAPPER_ID)).selectAll(".node").call(e)}},{key:"_graphBindD3ToReactComponent",value:function(){this.state.config.d3.disableLinkForce||(this.state.simulation.nodes(this.state.d3Nodes).on("tick",this._tick),this._graphLinkForceConfig()),this.state.config.freezeAllDragEvents||this._graphNodeDragConfig()}}]),x(t,[{key:"UNSAFE_componentWillReceiveProps",value:function(e){var t=(0,d.checkForGraphElementsChanges)(e,this.state),n=t.graphElementsUpdated,r=t.newGraphElements,i=n?(0,d.initializeGraphState)(e,this.state):this.state,a=e.config||{},o=(0,d.checkForGraphConfigChanges)(e,this.state),s=o.configUpdated,l=o.d3ConfigUpdated,f=s?(0,p.merge)(c.default,a):this.state.config;r&&this.pauseSimulation();var h=a.panAndZoom!==this.state.config.panAndZoom?1:this.state.transform,b=e.data.focusedNodeId,m=this.state.d3Nodes.find(function(e){return"".concat(e.id)==="".concat(b)}),g="".concat(this.state.id,"-").concat(u.default.GRAPH_WRAPPER_ID),v=(0,d.getCenterAndZoomTransformation)(m,this.state.config,g)||this.state.focusTransformation,w=this.props.data.focusedNodeId!==e.data.focusedNodeId;e.onZoomChange&&(this.debouncedOnZoomChange=(0,p.debounce)(e.onZoomChange,100)),this.setState(y({},i,{config:f,configUpdated:s,d3ConfigUpdated:l,newGraphElements:r,transform:h,focusedNodeId:b,enableFocusAnimation:w,focusTransformation:v}))}},{key:"componentDidUpdate",value:function(){(this.state.config.staticGraph||this.state.config.staticGraphWithDragAndDrop)&&this.pauseSimulation(),!this.state.config.staticGraph&&(this.state.newGraphElements||this.state.d3ConfigUpdated)?(this._graphBindD3ToReactComponent(),this.state.config.staticGraphWithDragAndDrop||this.restartSimulation(),this.setState({newGraphElements:!1,d3ConfigUpdated:!1})):this.state.configUpdated&&this._graphNodeDragConfig(),this.state.configUpdated&&(this._zoomConfig(),this.setState({configUpdated:!1}))}},{key:"componentDidMount",value:function(){this.state.config.staticGraph||this._graphBindD3ToReactComponent(),this._zoomConfig()}},{key:"componentWillUnmount",value:function(){this.pauseSimulation(),this.nodeClickTimer&&(clearTimeout(this.nodeClickTimer),this.nodeClickTimer=null),this.focusAnimationTimeout&&(clearTimeout(this.focusAnimationTimeout),this.focusAnimationTimeout=null)}},{key:"render",value:function(){var e=(0,h.renderGraph)(this.state.nodes,{onClickNode:this.onClickNode,onDoubleClickNode:this.onDoubleClickNode,onRightClickNode:this.onRightClickNode,onMouseOverNode:this.onMouseOverNode,onMouseOut:this.onMouseOutNode},this.state.d3Links,this.state.links,{onClickLink:this.props.onClickLink,onRightClickLink:this.props.onRightClickLink,onMouseOverLink:this.onMouseOverLink,onMouseOutLink:this.onMouseOutLink},this.state.config,this.state.highlightedNode,this.state.highlightedLink,this.state.transform),t=e.nodes,n=e.links,i=e.defs,a={height:this.state.config.height,width:this.state.config.width},o=this._generateFocusAnimationProps();return r.default.createElement("div",{id:"".concat(this.state.id,"-").concat(u.default.GRAPH_WRAPPER_ID)},r.default.createElement("svg",{name:"svg-container-".concat(this.state.id),style:a,onClick:this.onClickGraph},i,r.default.createElement("g",g({id:"".concat(this.state.id,"-").concat(u.default.GRAPH_CONTAINER_ID)},o),n,t)))}}]),t}(r.default.Component);t.default=A},37973(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.computeNodeDegree=l,t.getTargetLeafConnections=f,t.isNodeVisible=d,t.toggleLinksConnections=h,t.toggleLinksMatrixConnections=p;var r=n(52694);function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function a(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return Object.keys(t).reduce(function(n,r){return t[r]?Object.keys(t[r]).reduce(function(n,i){return e===r&&(n.outDegree+=t[e][i]),e===i&&(n.inDegree+=t[r][e]),n},n):n},{inDegree:0,outDegree:0})}function f(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0,r=n.directed;return(t[e]?Object.keys(t[e]):[]).reduce(function(n,i){return c(i,t,r)&&n.push({source:e,target:i}),n},[])}function d(e,t,n){if(!t[e])return!1;if(t[e]._orphan)return!0;var r=l(e,n),i=r.inDegree,a=r.outDegree;return i>0||a>0}function h(e,t){return e.map(function(e){var n=e.source,i=e.target,o=(0,r.getId)(n),s=(0,r.getId)(i);return a({},e,{isHidden:!(t&&t[o]&&t[o][s])})})}function p(e,t,n){var r=n.directed;return t.reduce(function(e,t){e[t.source]||(e[t.source]={}),e[t.source][t.target]||(e[t.source][t.target]=0);var n=0===e[t.source][t.target]?1:0;return e[t.source][t.target]=n,r||(e[t.target][t.source]=n),e},a({},e))}n(69901)},99182(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.buildLinkProps=h,t.buildNodeProps=p;var r=s(n(53880)),i=n(37109),a=n(80362),o=n(52694);function s(e){return e&&e.__esModule?e:{default:e}}function u(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function c(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:{},i=arguments.length>3?arguments[3]:void 0,a=arguments.length>4?arguments[4]:void 0,o=arguments.length>5?arguments[5]:void 0,s=e.highlighted||e.id===(a&&a.source)||e.id===(a&&a.target),u=d(e,i,a,t),l=e.color||t.node.color;s&&t.node.highlightColor!==r.default.KEYWORDS.SAME&&(l=t.node.highlightColor);var h=e.strokeColor||t.node.strokeColor;s&&t.node.highlightStrokeColor!==r.default.KEYWORDS.SAME&&(h=t.node.highlightStrokeColor);var p=e[t.node.labelProperty]||e.id;"function"==typeof t.node.labelProperty&&(p=t.node.labelProperty(e));var b=e.labelPosition||t.node.labelPosition,m=e.strokeWidth||t.node.strokeWidth;s&&t.node.highlightStrokeWidth!==r.default.KEYWORDS.SAME&&(m=t.node.highlightStrokeWidth);var g=1/o,v=e.size||t.node.size,y="object"!==f(v),w=0;y?w=v:"top"===b||"bottom"===b?w=v.height:("right"===b||"left"===b)&&(w=v.width);var _=e.fontSize||t.node.fontSize,E=e.highlightFontSize||t.node.highlightFontSize,S=s?E:_,k=S*g+w/100+1.5,x=e.svg||t.node.svg,T=e.fontColor||t.node.fontColor,M=t.node.renderLabel;return void 0!==e.renderLabel&&"boolean"==typeof e.renderLabel&&(M=e.renderLabel),c({},e,{className:r.default.NODE_CLASS_NAME,cursor:t.node.mouseCursor,cx:(null==e?void 0:e.x)||"0",cy:(null==e?void 0:e.y)||"0",dx:k,fill:l,fontColor:T,fontSize:S*g,fontWeight:s?t.node.highlightFontWeight:t.node.fontWeight,id:e.id,label:p,labelPosition:b,opacity:u,overrideGlobalViewGenerator:!e.viewGenerator&&e.svg,renderLabel:M,size:y?v*g:{height:v.height*g,width:v.width*g},stroke:h,strokeWidth:m*g,svg:x,type:e.symbolType||t.node.symbolType,viewGenerator:e.viewGenerator||t.node.viewGenerator,onClickNode:n.onClickNode,onMouseOut:n.onMouseOut,onMouseOverNode:n.onMouseOverNode,onRightClickNode:n.onRightClickNode})}},98510(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={automaticRearrangeAfterDropNode:!1,collapsible:!1,directed:!1,focusAnimationDuration:.75,focusZoom:1,freezeAllDragEvents:!1,height:400,highlightDegree:1,highlightOpacity:1,linkHighlightBehavior:!1,maxZoom:8,minZoom:.1,initialZoom:null,nodeHighlightBehavior:!1,panAndZoom:!1,staticGraph:!1,staticGraphWithDragAndDrop:!1,width:800,d3:{alphaTarget:.05,gravity:-100,linkLength:100,linkStrength:1,disableLinkForce:!1},node:{color:"#d3d3d3",fontColor:"black",fontSize:8,fontWeight:"normal",highlightColor:"SAME",highlightFontSize:8,highlightFontWeight:"normal",highlightStrokeColor:"SAME",highlightStrokeWidth:"SAME",labelProperty:"id",labelPosition:null,mouseCursor:"pointer",opacity:1,renderLabel:!0,size:200,strokeColor:"none",strokeWidth:1.5,svg:"",symbolType:"circle",viewGenerator:null},link:{color:"#d3d3d3",fontColor:"black",fontSize:8,fontWeight:"normal",highlightColor:"SAME",highlightFontSize:8,highlightFontWeight:"normal",labelProperty:"label",mouseCursor:"pointer",opacity:1,renderLabel:!1,semanticStrokeWidth:!1,strokeWidth:1.5,markerHeight:6,markerWidth:6,type:"STRAIGHT",strokeDasharray:0,strokeDashoffset:0,strokeLinecap:"butt"}};t.default=n},53880(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=i(n(11041));function i(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function o(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3?arguments[3]:void 0,i=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{},a=n.find(function(t){return t.source.id===e.source&&t.target.id===e.target}),o=a&&(0,c.pick)(a,m),s=(0,c.antiPick)(e,["source","target"]);if(o){var u=i.config&&Object.prototype.hasOwnProperty.call(i.config,"directed")&&r.directed!==i.config.directed,l=h({index:t},o,{},s);return u?h({},l,{isHidden:!1}):r.collapsible?l:h({},l,{isHidden:!1})}var f=!1,d={id:e.source,highlighted:f},p={id:e.target,highlighted:f};return h({index:t,source:d,target:p},s)}function _(e,t){return Object.keys(e).reduce(function(n,r){var i=(0,l.computeNodeDegree)(r,t),a=i.inDegree,o=i.outDegree,s=e[r],u=0===a&&0===o?h({},s,{_orphan:!0}):s;return n[r]=u,n},{})}function E(e){e.nodes&&e.nodes.length||((0,c.logWarning)("Graph",u.default.INSUFFICIENT_DATA),e.nodes=[]),e.links||((0,c.logWarning)("Graph",u.default.INSUFFICIENT_LINKS),e.links=[]);for(var t=e.links.length,n=function(t){var n=e.links[t];e.nodes.find(function(e){return e.id===n.source})||(0,c.throwErr)("Graph","".concat(u.default.INVALID_LINKS,' - "').concat(n.source,'" is not a valid source node id')),e.nodes.find(function(e){return e.id===n.target})||(0,c.throwErr)("Graph","".concat(u.default.INVALID_LINKS,' - "').concat(n.target,'" is not a valid target node id')),n&&void 0!==n.value&&"number"!=typeof n.value&&(0,c.throwErr)("Graph","".concat(u.default.INVALID_LINK_VALUE,' - found in link with source "').concat(n.source,'" and target "').concat(n.target,'"'))},r=0;rx?o.focusZoom=x:T4&&void 0!==arguments[4]&&arguments[4],a=i?r:"",o=h({},e[r],{highlighted:i}),s=h({},e,p({},r,o));return t[r]&&0!==n.highlightDegree&&(s=Object.keys(t[r]).reduce(function(e,t){var n=h({},s[t],{highlighted:i});return e[t]=n,e},s)),{nodes:s,highlightedNode:a}}function I(e){var t=Math.sqrt(Math.pow(e.x,2)+Math.pow(e.y,2));return 0===t?e:{x:e.x/t,y:e.y/t}}var D=new Set([o.default.SYMBOLS.CIRCLE]);function N(e,t,n,r){var i=e.sourceId,a=e.targetId,s=e.sourceCoords,u=void 0===s?{}:s,c=e.targetCoords,l=void 0===c?{}:c,f=null==t?void 0:t[i],d=null==t?void 0:t[a];if(!f||!d||(null===(_=n.node)||void 0===_?void 0:_.viewGenerator)||(null==f?void 0:f.viewGenerator)||(null==d?void 0:d.viewGenerator))return{sourceCoords:u,targetCoords:l};var h=f.symbolType||(null===(E=n.node)||void 0===E?void 0:E.symbolType),p=d.symbolType||(null===(S=n.node)||void 0===S?void 0:S.symbolType);if(!D.has(h)&&!D.has(p))return{sourceCoords:u,targetCoords:l};var b=u.x,m=u.y,g=l.x,v=l.y,y=I({x:g-b,y:v-m});if(h===o.default.SYMBOLS.CIRCLE){var w=(null==f?void 0:f.size)||n.node.size;b+=(w=.95*Math.sqrt(w/Math.PI))*y.x,m+=w*y.y}if(p===o.default.SYMBOLS.CIRCLE){var _,E,S,k,x,T=r*Math.min((null===(k=n.link)||void 0===k?void 0:k.markerWidth)||0,(null===(x=n.link)||void 0===x?void 0:x.markerHeight)||0),M=(null==d?void 0:d.size)||n.node.size;g-=((M=.95*Math.sqrt(M/Math.PI))+(n.directed?T:0))*y.x,v-=(M+(n.directed?T:0))*y.y}return{sourceCoords:{x:b,y:m},targetCoords:{x:g,y:v}}}},75791(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.renderGraph=E;var r=h(n(67294)),i=h(n(53880)),a=n(7619),o=h(n(33938)),s=h(n(61740)),u=h(n(28017)),c=n(99182),l=n(52694),f=n(37973),d=n(80362);function h(e){return e&&e.__esModule?e:{default:e}}function p(){return(p=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:i.LINE_TYPES.STRAIGHT,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[],o=e.x,s=e.y,u=p(i.LINE_TYPES[n]||i.LINE_TYPES.STRAIGHT),c=[].concat(a(r),[t]),l=c.map(function(t,n){var r,i=t.x,a=t.y,o=n>0?c[n-1]:e,s=u(o.x,o.y,i,a);return" A".concat(s,",").concat(s," 0 0,1 ").concat(i,",").concat(a)}).join("");return"M".concat(o,",").concat(s).concat(l)}},28017(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=i(n(67294));function i(e){return e&&e.__esModule?e:{default:e}}function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function s(e,t){for(var n=0;n=t&&e0&&void 0!==arguments[0]?arguments[0]:i.default.DEFAULT_NODE_SIZE,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.default.SYMBOLS.CIRCLE;return(0,r.symbol)().size(function(){return e}).type(function(){return o(t)})()}function u(e,t){switch(t){case"right":return{dx:e?"".concat(e):i.default.NODE_LABEL_DX,dy:"0",dominantBaseline:"middle",textAnchor:"start"};case"left":return{dx:e?"".concat(-e):"-".concat(i.default.NODE_LABEL_DX),dy:"0",dominantBaseline:"middle",textAnchor:"end"};case"top":return{dx:"0",dy:e?"".concat(-e):"-".concat(i.default.NODE_LABEL_DX),dominantBaseline:"baseline",textAnchor:"middle"};case"bottom":return{dx:"0",dy:e?"".concat(e):i.default.NODE_LABEL_DX,dominantBaseline:"hanging",textAnchor:"middle"};case"center":return{dx:"0",dy:"0",dominantBaseline:"middle",textAnchor:"middle"};default:return{dx:e?"".concat(e):i.default.NODE_LABEL_DX,dy:i.default.NODE_LABEL_DY}}}var c={buildSvgSymbol:s,getLabelPlacementProps:u};t.default=c},11041(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={SYMBOLS:{CIRCLE:"circle",CROSS:"cross",DIAMOND:"diamond",SQUARE:"square",STAR:"star",TRIANGLE:"triangle",WYE:"wye"}};t.default=n},34214(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n={GRAPH_NO_ID_PROP:"id prop not defined! id property is mandatory and it should be unique.",INSUFFICIENT_LINKS:"you are passing invalid data to react-d3-graph. You must include a links array, even if empty, in the data object you're passing down to the component.",INVALID_LINKS:"you provided a invalid links data structure. Links source and target attributes must point to an existent node",INSUFFICIENT_DATA:"you have not provided enough data for react-d3-graph to render something. You need to provide at least one node",INVALID_LINK_VALUE:"links 'value' attribute must be of type number"};t.default=n},94164(e,t,n){"use strict";r={value:!0},Object.defineProperty(t,"kJ",{enumerable:!0,get:function(){return i.default}}),r={enumerable:!0,get:function(){return a.default}},r={enumerable:!0,get:function(){return o.default}};var r,i=s(n(82623)),a=s(n(61740)),o=s(n(33938));function s(e){return e&&e.__esModule?e:{default:e}}},69901(e,t){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.isDeepEqual=a,t.isEmptyObject=o,t.deepClone=s,t.merge=u,t.pick=c,t.antiPick=l,t.debounce=f,t.throwErr=h,t.logError=p,t.logWarning=b;var r=20;function i(e,t){return!!e&&Object.prototype.hasOwnProperty.call(e,t)&&"object"===n(e[t])&&null!==e[t]&&!o(e[t])}function a(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,s=[];if(0===n&&e===t)return!0;if(o(e)&&!o(t)||!o(e)&&o(t))return!1;var u=Object.keys(e),c=Object.keys(t);if(u.length!==c.length)return!1;for(var l=0,f=u;l1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a=Object.keys(e),o=0,u=a;o0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,a={};if(0===Object.keys(e||{}).length)return t&&!o(t)?t:{};for(var s=0,c=Object.keys(e);s1&&void 0!==arguments[1]?arguments[1]:[];return t.reduce(function(t,n){return Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]),t},{})}function l(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=Object.keys(e).filter(function(e){return!t.includes(e)});return c(e,n)}function f(e,t){var n;return function(){for(var r=arguments.length,i=Array(r),a=0;a0&&void 0!==arguments[0]?arguments[0]:"N/A",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"N/A";return"react-d3-graph :: ".concat(e," :: ").concat(t)}function h(e,t){throw Error(d(e,t))}function p(e,t){console.error(d(e,t))}function b(e,t){var n="react-d3-graph :: ".concat(e," :: ").concat(t);console.warn(n)}},64448(e,t,n){"use strict";/** @license React v16.12.0 + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ var r,i,a,o,s,u=n(67294),c=n(27418),l=n(63840);function f(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;nt}return!1}function eM(e,t,n,r,i,a){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=i,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=a}var eO={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){eO[e]=new eM(e,0,!1,e,null,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];eO[t]=new eM(t,1,!1,e[1],null,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){eO[e]=new eM(e,2,!1,e.toLowerCase(),null,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){eO[e]=new eM(e,2,!1,e,null,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){eO[e]=new eM(e,3,!1,e.toLowerCase(),null,!1)}),["checked","multiple","muted","selected"].forEach(function(e){eO[e]=new eM(e,3,!0,e,null,!1)}),["capture","download"].forEach(function(e){eO[e]=new eM(e,4,!1,e,null,!1)}),["cols","rows","size","span"].forEach(function(e){eO[e]=new eM(e,6,!1,e,null,!1)}),["rowSpan","start"].forEach(function(e){eO[e]=new eM(e,5,!1,e.toLowerCase(),null,!1)});var eA=/[\-:]([a-z])/g;function eL(e){return e[1].toUpperCase()}function eC(e){switch(typeof e){case"boolean":case"number":case"object":case"string":case"undefined":return e;default:return""}}function eI(e,t,n,r){var i=eO.hasOwnProperty(t)?eO[t]:null;(null!==i?0===i.type:!r&&2=t.length))throw Error(f(93));t=t[0]}n=t}null==n&&(n="")}e._wrapperState={initialValue:eC(n)}}function eV(e,t){var n=eC(t.value),r=eC(t.defaultValue);null!=n&&((n=""+n)!==e.value&&(e.value=n),null==t.defaultValue&&e.defaultValue!==n&&(e.defaultValue=n)),null!=r&&(e.defaultValue=""+r)}function eq(e){var t=e.textContent;t===e._wrapperState.initialValue&&""!==t&&null!==t&&(e.value=t)}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(eA,eL);eO[t]=new eM(t,1,!1,e,null,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(eA,eL);eO[t]=new eM(t,1,!1,e,"http://www.w3.org/1999/xlink",!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(eA,eL);eO[t]=new eM(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1)}),["tabIndex","crossOrigin"].forEach(function(e){eO[e]=new eM(e,1,!1,e.toLowerCase(),null,!1)}),eO.xlinkHref=new eM("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0),["src","href","action","formAction"].forEach(function(e){eO[e]=new eM(e,1,!1,e.toLowerCase(),null,!0)});var eZ={html:"http://www.w3.org/1999/xhtml",mathml:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg"};function eX(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function eJ(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?eX(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}var eQ,e1,e0=(eQ=function(e,t){if(e.namespaceURI!==eZ.svg||"innerHTML"in e)e.innerHTML=t;else{for((e1=e1||document.createElement("div")).innerHTML=""+t.valueOf().toString()+"",t=e1.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,t,n,r){MSApp.execUnsafeLocalFunction(function(){return eQ(e,t,n,r)})}:eQ);function e2(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType){n.nodeValue=t;return}}e.textContent=t}function e3(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var e4={animationend:e3("Animation","AnimationEnd"),animationiteration:e3("Animation","AnimationIteration"),animationstart:e3("Animation","AnimationStart"),transitionend:e3("Transition","TransitionEnd")},e5={},e6={};function e9(e){if(e5[e])return e5[e];if(!e4[e])return e;var t,n=e4[e];for(t in n)if(n.hasOwnProperty(t)&&t in e6)return e5[e]=n[t];return e}eo&&(e6=document.createElement("div").style,"AnimationEvent"in window||(delete e4.animationend.animation,delete e4.animationiteration.animation,delete e4.animationstart.animation),"TransitionEvent"in window||delete e4.transitionend.transition);var e8=e9("animationend"),e7=e9("animationiteration"),te=e9("animationstart"),tt=e9("transitionend"),tn="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split(" ");function tr(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do 0!=(1026&(t=e).effectTag)&&(n=t.return),e=t.return;while(e)}return 3===t.tag?n:null}function ti(e){if(13===e.tag){var t=e.memoizedState;if(null===t&&null!==(e=e.alternate)&&(t=e.memoizedState),null!==t)return t.dehydrated}return null}function ta(e){if(tr(e)!==e)throw Error(f(188))}function to(e){var t=e.alternate;if(!t){if(null===(t=tr(e)))throw Error(f(188));return t!==e?null:e}for(var n=e,r=t;;){var i=n.return;if(null===i)break;var a=i.alternate;if(null===a){if(null!==(r=i.return)){n=r;continue}break}if(i.child===a.child){for(a=i.child;a;){if(a===n)return ta(i),e;if(a===r)return ta(i),t;a=a.sibling}throw Error(f(188))}if(n.return!==r.return)n=i,r=a;else{for(var o=!1,s=i.child;s;){if(s===n){o=!0,n=i,r=a;break}if(s===r){o=!0,r=i,n=a;break}s=s.sibling}if(!o){for(s=a.child;s;){if(s===n){o=!0,n=a,r=i;break}if(s===r){o=!0,r=a,n=i;break}s=s.sibling}if(!o)throw Error(f(189))}}if(n.alternate!==r)throw Error(f(190))}if(3!==n.tag)throw Error(f(188));return n.stateNode.current===n?e:t}function ts(e){if(!(e=to(e)))return null;for(var t=e;;){if(5===t.tag||6===t.tag)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===e)break;for(;!t.sibling;){if(!t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}var tu,tc,tl,tf=!1,td=[],th=null,tp=null,tb=null,tm=new Map,tg=new Map,tv=[],ty="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput close cancel copy cut paste click change contextmenu reset submit".split(" "),tw="focus blur dragenter dragleave mouseover mouseout pointerover pointerout gotpointercapture lostpointercapture".split(" ");function t_(e){var t=nA(e);ty.forEach(function(n){nL(n,e,t)}),tw.forEach(function(n){nL(n,e,t)})}function tE(e,t,n,r){return{blockedOn:e,topLevelType:t,eventSystemFlags:32|n,nativeEvent:r}}function tS(e,t){switch(e){case"focus":case"blur":th=null;break;case"dragenter":case"dragleave":tp=null;break;case"mouseover":case"mouseout":tb=null;break;case"pointerover":case"pointerout":tm.delete(t.pointerId);break;case"gotpointercapture":case"lostpointercapture":tg.delete(t.pointerId)}}function tk(e,t,n,r,i){return null===e||e.nativeEvent!==i?(e=tE(t,n,r,i),null!==t&&null!==(t=n7(t))&&tc(t),e):(e.eventSystemFlags|=r,e)}function tx(e,t,n,r){switch(t){case"focus":return th=tk(th,e,t,n,r),!0;case"dragenter":return tp=tk(tp,e,t,n,r),!0;case"mouseover":return tb=tk(tb,e,t,n,r),!0;case"pointerover":var i=r.pointerId;return tm.set(i,tk(tm.get(i)||null,e,t,n,r)),!0;case"gotpointercapture":return i=r.pointerId,tg.set(i,tk(tg.get(i)||null,e,t,n,r)),!0}return!1}function tT(e){var t=n8(e.target);if(null!==t){var n=tr(t);if(null!==n){if(13===(t=n.tag)){if(null!==(t=ti(n))){e.blockedOn=t,l.unstable_runWithPriority(e.priority,function(){tl(n)});return}}else if(3===t&&n.stateNode.hydrate){e.blockedOn=3===n.tag?n.stateNode.containerInfo:null;return}}}e.blockedOn=null}function tM(e){if(null!==e.blockedOn)return!1;var t=nT(e.topLevelType,e.eventSystemFlags,e.nativeEvent);if(null!==t){var n=n7(t);return null!==n&&tc(n),e.blockedOn=t,!1}return!0}function tO(e,t,n){tM(e)&&n.delete(t)}function tA(){for(tf=!1;0this.eventPool.length&&this.eventPool.push(e)}function tz(e){e.eventPool=[],e.getPooled=tH,e.release=t$}c(tU.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=tY)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=tY)},persist:function(){this.isPersistent=tY},isPersistent:tB,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=tB,this._dispatchInstances=this._dispatchListeners=null}}),tU.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},tU.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var i=new t;return c(i,n.prototype),n.prototype=i,n.prototype.constructor=n,n.Interface=c({},r.Interface,e),n.extend=r.extend,tz(n),n},tz(tU);var tG=tU.extend({animationName:null,elapsedTime:null,pseudoElement:null}),tW=tU.extend({clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),tK=tU.extend({view:null,detail:null}),tV=tK.extend({relatedTarget:null});function tq(e){var t=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,10===e&&(e=13),32<=e||13===e?e:0}var tZ={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},tX={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},tJ={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function tQ(e){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(e):!!(e=tJ[e])&&!!t[e]}function t1(){return tQ}for(var t0=tK.extend({key:function(e){if(e.key){var t=tZ[e.key]||e.key;if("Unidentified"!==t)return t}return"keypress"===e.type?13===(e=tq(e))?"Enter":String.fromCharCode(e):"keydown"===e.type||"keyup"===e.type?tX[e.keyCode]||"Unidentified":""},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:t1,charCode:function(e){return"keypress"===e.type?tq(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?tq(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}}),t2=0,t3=0,t4=!1,t5=!1,t6=tK.extend({screenX:null,screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:t1,button:null,buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)},movementX:function(e){if(("movementX"in e))return e.movementX;var t=t2;return t2=e.screenX,t4?"mousemove"===e.type?e.screenX-t:0:(t4=!0,0)},movementY:function(e){if(("movementY"in e))return e.movementY;var t=t3;return t3=e.screenY,t5?"mousemove"===e.type?e.screenY-t:0:(t5=!0,0)}}),t9=t6.extend({pointerId:null,width:null,height:null,pressure:null,tangentialPressure:null,tiltX:null,tiltY:null,twist:null,pointerType:null,isPrimary:null}),t8=t6.extend({dataTransfer:null}),t7=tK.extend({touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:t1}),ne=tU.extend({propertyName:null,elapsedTime:null,pseudoElement:null}),nt=t6.extend({deltaX:function(e){return("deltaX"in e)?e.deltaX:("wheelDeltaX"in e)?-e.wheelDeltaX:0},deltaY:function(e){return("deltaY"in e)?e.deltaY:("wheelDeltaY"in e)?-e.wheelDeltaY:("wheelDelta"in e)?-e.wheelDelta:0},deltaZ:null,deltaMode:null}),nn=[["blur","blur",0],["cancel","cancel",0],["click","click",0],["close","close",0],["contextmenu","contextMenu",0],["copy","copy",0],["cut","cut",0],["auxclick","auxClick",0],["dblclick","doubleClick",0],["dragend","dragEnd",0],["dragstart","dragStart",0],["drop","drop",0],["focus","focus",0],["input","input",0],["invalid","invalid",0],["keydown","keyDown",0],["keypress","keyPress",0],["keyup","keyUp",0],["mousedown","mouseDown",0],["mouseup","mouseUp",0],["paste","paste",0],["pause","pause",0],["play","play",0],["pointercancel","pointerCancel",0],["pointerdown","pointerDown",0],["pointerup","pointerUp",0],["ratechange","rateChange",0],["reset","reset",0],["seeked","seeked",0],["submit","submit",0],["touchcancel","touchCancel",0],["touchend","touchEnd",0],["touchstart","touchStart",0],["volumechange","volumeChange",0],["drag","drag",1],["dragenter","dragEnter",1],["dragexit","dragExit",1],["dragleave","dragLeave",1],["dragover","dragOver",1],["mousemove","mouseMove",1],["mouseout","mouseOut",1],["mouseover","mouseOver",1],["pointermove","pointerMove",1],["pointerout","pointerOut",1],["pointerover","pointerOver",1],["scroll","scroll",1],["toggle","toggle",1],["touchmove","touchMove",1],["wheel","wheel",1],["abort","abort",2],[e8,"animationEnd",2],[e7,"animationIteration",2],[te,"animationStart",2],["canplay","canPlay",2],["canplaythrough","canPlayThrough",2],["durationchange","durationChange",2],["emptied","emptied",2],["encrypted","encrypted",2],["ended","ended",2],["error","error",2],["gotpointercapture","gotPointerCapture",2],["load","load",2],["loadeddata","loadedData",2],["loadedmetadata","loadedMetadata",2],["loadstart","loadStart",2],["lostpointercapture","lostPointerCapture",2],["playing","playing",2],["progress","progress",2],["seeking","seeking",2],["stalled","stalled",2],["suspend","suspend",2],["timeupdate","timeUpdate",2],[tt,"transitionEnd",2],["waiting","waiting",2]],nr={},ni={},na=0;na=t)return{node:r,offset:t-e};e=n}a:{for(;r;){if(r.nextSibling){r=r.nextSibling;break a}r=r.parentNode}r=void 0}r=nU(r)}}function n$(e,t){return!!e&&!!t&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?n$(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function nz(){for(var e=window,t=nB();t instanceof e.HTMLIFrameElement;){try{var n="string"==typeof t.contentWindow.location.href}catch(r){n=!1}if(n)e=t.contentWindow;else break;t=nB(e.document)}return t}function nG(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}var nW="$",nK="/$",nV="$?",nq="$!",nZ=null,nX=null;function nJ(e,t){switch(e){case"button":case"input":case"select":case"textarea":return!!t.autoFocus}return!1}function nQ(e,t){return"textarea"===e||"option"===e||"noscript"===e||"string"==typeof t.children||"number"==typeof t.children||"object"==typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var n1="function"==typeof setTimeout?setTimeout:void 0,n0="function"==typeof clearTimeout?clearTimeout:void 0;function n2(e){for(;null!=e;e=e.nextSibling){var t=e.nodeType;if(1===t||3===t)break}return e}function n3(e){e=e.previousSibling;for(var t=0;e;){if(8===e.nodeType){var n=e.data;if(n===nW||n===nq||n===nV){if(0===t)return e;t--}else n===nK&&t++}e=e.previousSibling}return null}var n4=Math.random().toString(36).slice(2),n5="__reactInternalInstance$"+n4,n6="__reactEventHandlers$"+n4,n9="__reactContainere$"+n4;function n8(e){var t=e[n5];if(t)return t;for(var n=e.parentNode;n;){if(t=n[n9]||n[n5]){if(n=t.alternate,null!==t.child||null!==n&&null!==n.child)for(e=n3(e);null!==e;){if(n=e[n5])return n;e=n3(e)}return t}n=(e=n).parentNode}return null}function n7(e){return(e=e[n5]||e[n9])&&(5===e.tag||6===e.tag||13===e.tag||3===e.tag)?e:null}function re(e){if(5===e.tag||6===e.tag)return e.stateNode;throw Error(f(33))}function rt(e){return e[n6]||null}var rn=null,rr=null,ri=null;function ra(){if(ri)return ri;var e,t,n=rr,r=n.length,i="value"in rn?rn.value:rn.textContent,a=i.length;for(e=0;e=rl),rh=" ",rp={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},rb=!1;function rm(e,t){switch(e){case"keyup":return -1!==ru.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function rg(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var rv=!1;function ry(e,t){switch(e){case"compositionend":return rg(t);case"keypress":if(32!==t.which)return null;return rb=!0,rh;case"textInput":return(e=t.data)===rh&&rb?null:e;default:return null}}function rw(e,t){if(rv)return"compositionend"===e||!rc&&rm(e,t)?(e=ra(),ri=rr=rn=null,rv=!1,e):null;switch(e){case"paste":default:return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=document.documentMode,rK={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:"blur contextmenu dragend focus keydown keyup mousedown mouseup selectionchange".split(" ")}},rV=null,rq=null,rZ=null,rX=!1;function rJ(e,t){var n=t.window===t?t.document:9===t.nodeType?t:t.ownerDocument;return rX||null==rV||rV!==nB(n)?null:(n="selectionStart"in(n=rV)&&nG(n)?{start:n.selectionStart,end:n.selectionEnd}:{anchorNode:(n=(n.ownerDocument&&n.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset},rZ&&rG(rZ,n)?null:(rZ=n,(e=tU.getPooled(rK.select,rq,e,t)).type="select",e.target=rV,tF(e),e))}var rQ={eventTypes:rK,extractEvents:function(e,t,n,r){var i,a=r.window===r?r.document:9===r.nodeType?r:r.ownerDocument;if(!(i=!a)){a:{a=nA(a),i=y.onSelect;for(var o=0;or2||(e.current=r0[r2],r0[r2]=null,r2--)}function r4(e,t){r0[++r2]=e.current,e.current=t}var r5={},r6={current:r5},r9={current:!1},r8=r5;function r7(e,t){var n=e.type.contextTypes;if(!n)return r5;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i,a={};for(i in n)a[i]=t[i];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=a),a}function ie(e){return null!=(e=e.childContextTypes)}function it(e){r3(r9,e),r3(r6,e)}function ir(e){r3(r9,e),r3(r6,e)}function ii(e,t,n){if(r6.current!==r5)throw Error(f(168));r4(r6,t,e),r4(r9,n,e)}function ia(e,t,n){var r=e.stateNode;if(e=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var i in r=r.getChildContext())if(!(i in e))throw Error(f(108,ei(t)||"Unknown",i));return c({},n,{},r)}function io(e){var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||r5,r8=r6.current,r4(r6,t,e),r4(r9,r9.current,e),!0}function is(e,t,n){var r=e.stateNode;if(!r)throw Error(f(169));n?(t=ia(e,t,r8),r.__reactInternalMemoizedMergedChildContext=t,r3(r9,e),r3(r6,e),r4(r6,t,e)):r3(r9,e),r4(r9,n,e)}var iu=l.unstable_runWithPriority,ic=l.unstable_scheduleCallback,il=l.unstable_cancelCallback,id=l.unstable_shouldYield,ih=l.unstable_requestPaint,ip=l.unstable_now,ib=l.unstable_getCurrentPriorityLevel,im=l.unstable_ImmediatePriority,ig=l.unstable_UserBlockingPriority,iv=l.unstable_NormalPriority,iy=l.unstable_LowPriority,iw=l.unstable_IdlePriority,i_={},iE=void 0!==ih?ih:function(){},iS=null,ik=null,ix=!1,iT=ip(),iM=1e4>iT?ip:function(){return ip()-iT};function iO(){switch(ib()){case im:return 99;case ig:return 98;case iv:return 97;case iy:return 96;case iw:return 95;default:throw Error(f(332))}}function iA(e){switch(e){case 99:return im;case 98:return ig;case 97:return iv;case 96:return iy;case 95:return iw;default:throw Error(f(332))}}function iL(e,t){return e=iA(e),iu(e,t)}function iC(e,t,n){return e=iA(e),ic(e,t,n)}function iI(e){return null===iS?(iS=[e],ik=ic(im,iN)):iS.push(e),i_}function iD(){if(null!==ik){var e=ik;ik=null,il(e)}iN()}function iN(){if(!ix&&null!==iS){ix=!0;var e=0;try{var t=iS;iL(99,function(){for(;e=t&&(oo=!0),e.firstContext=null)}function iK(e,t){if(iU!==e&&!1!==t&&0!==t){if(("number"!=typeof t||1073741823===t)&&(iU=e,t=1073741823),t={context:e,observedBits:t,next:null},null===iB){if(null===iY)throw Error(f(308));iB=t,iY.dependencies={expirationTime:0,firstContext:t,responders:null}}else iB=iB.next=t}return e._currentValue}var iV=!1;function iq(e){return{baseState:e,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function iZ(e){return{baseState:e.baseState,firstUpdate:e.firstUpdate,lastUpdate:e.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function iX(e,t){return{expirationTime:e,suspenseConfig:t,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function iJ(e,t){null===e.lastUpdate?e.firstUpdate=e.lastUpdate=t:(e.lastUpdate.next=t,e.lastUpdate=t)}function iQ(e,t){var n=e.alternate;if(null===n){var r=e.updateQueue,i=null;null===r&&(r=e.updateQueue=iq(e.memoizedState))}else r=e.updateQueue,i=n.updateQueue,null===r?null===i?(r=e.updateQueue=iq(e.memoizedState),i=n.updateQueue=iq(n.memoizedState)):r=e.updateQueue=iZ(i):null===i&&(i=n.updateQueue=iZ(r));null===i||r===i?iJ(r,t):null===r.lastUpdate||null===i.lastUpdate?(iJ(r,t),iJ(i,t)):(iJ(r,t),i.lastUpdate=t)}function i1(e,t){var n=e.updateQueue;null===(n=null===n?e.updateQueue=iq(e.memoizedState):i0(e,n)).lastCapturedUpdate?n.firstCapturedUpdate=n.lastCapturedUpdate=t:(n.lastCapturedUpdate.next=t,n.lastCapturedUpdate=t)}function i0(e,t){var n=e.alternate;return null!==n&&t===n.updateQueue&&(t=e.updateQueue=iZ(t)),t}function i2(e,t,n,r,i,a){switch(n.tag){case 1:return"function"==typeof(e=n.payload)?e.call(a,r,i):e;case 3:e.effectTag=-4097&e.effectTag|64;case 0:if(null==(i="function"==typeof(e=n.payload)?e.call(a,r,i):e))break;return c({},r,i);case 2:iV=!0}return r}function i3(e,t,n,r,i){iV=!1,t=i0(e,t);for(var a=t.baseState,o=null,s=0,u=t.firstUpdate,c=a;null!==u;){var l=u.expirationTime;lb?(m=f,f=null):m=f.sibling;var g=h(i,f,s[b],u);if(null===g){null===f&&(f=m);break}e&&f&&null===g.alternate&&t(i,f),o=a(g,o,b),null===l?c=g:l.sibling=g,l=g,f=m}if(b===s.length)return n(i,f),c;if(null===f){for(;bm?(g=b,b=null):g=b.sibling;var y=h(i,b,v.value,u);if(null===y){null===b&&(b=g);break}e&&b&&null===y.alternate&&t(i,b),o=a(y,o,m),null===l?c=y:l.sibling=y,l=y,b=g}if(v.done)return n(i,b),c;if(null===b){for(;!v.done;m++,v=s.next())null!==(v=d(i,v.value,u))&&(o=a(v,o,m),null===l?c=v:l.sibling=v,l=v);return c}for(b=r(i,b);!v.done;m++,v=s.next())null!==(v=p(b,i,m,v.value,u))&&(e&&null!==v.alternate&&b.delete(null===v.key?m:v.key),o=a(v,o,m),null===l?c=v:l.sibling=v,l=v);return e&&b.forEach(function(e){return t(i,e)}),c}return function(e,r,a,s){var u="object"==typeof a&&null!==a&&a.type===z&&null===a.key;u&&(a=a.props.children);var c="object"==typeof a&&null!==a;if(c)switch(a.$$typeof){case H:a:{for(c=a.key,u=r;null!==u;){if(u.key===c){if(7===u.tag?a.type===z:u.elementType===a.type){n(e,u.sibling),(r=i(u,a.type===z?a.props.children:a.props,s)).ref=aa(e,u,a),r.return=e,e=r;break a}n(e,u);break}t(e,u),u=u.sibling}a.type===z?((r=s1(a.props.children,e.mode,s,a.key)).return=e,e=r):((s=sQ(a.type,a.key,a.props,null,e.mode,s)).ref=aa(e,r,a),s.return=e,e=s)}return o(e);case $:a:{for(u=a.key;null!==r;){if(r.key===u){if(4===r.tag&&r.stateNode.containerInfo===a.containerInfo&&r.stateNode.implementation===a.implementation){n(e,r.sibling),(r=i(r,a.children||[],s)).return=e,e=r;break a}n(e,r);break}t(e,r),r=r.sibling}(r=s2(a,e.mode,s)).return=e,e=r}return o(e)}if("string"==typeof a||"number"==typeof a)return a=""+a,null!==r&&6===r.tag?(n(e,r.sibling),(r=i(r,a,s)).return=e,e=r):(n(e,r),(r=s0(a,e.mode,s)).return=e,e=r),o(e);if(ai(a))return b(e,r,a,s);if(en(a))return m(e,r,a,s);if(c&&ao(e,a),void 0===a&&!u)switch(e.tag){case 1:case 0:throw Error(f(152,(e=e.type).displayName||e.name||"Component"))}return n(e,r)}}var au=as(!0),ac=as(!1),al={},af={current:al},ad={current:al},ah={current:al};function ap(e){if(e===al)throw Error(f(174));return e}function ab(e,t){r4(ah,t,e),r4(ad,e,e),r4(af,al,e);var n=t.nodeType;switch(n){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:eJ(null,"");break;default:t=eJ(t=(n=8===n?t.parentNode:t).namespaceURI||null,n=n.tagName)}r3(af,e),r4(af,t,e)}function am(e){r3(af,e),r3(ad,e),r3(ah,e)}function ag(e){ap(ah.current);var t=ap(af.current),n=eJ(t,e.type);t!==n&&(r4(ad,e,e),r4(af,n,e))}function av(e){ad.current===e&&(r3(af,e),r3(ad,e))}var ay={current:0};function aw(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||n.data===nV||n.data===nq))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(0!=(64&t.effectTag))return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}function a_(e,t){return{responder:e,props:t}}var aE=Y.ReactCurrentDispatcher,aS=Y.ReactCurrentBatchConfig,ak=0,ax=null,aT=null,aM=null,aO=null,aA=null,aL=null,aC=0,aI=null,aD=0,aN=!1,aP=null,aR=0;function aj(){throw Error(f(321))}function aF(e,t){if(null===t)return!1;for(var n=0;naC&&sL(aC=l)):(sA(l,u.suspenseConfig),a=u.eagerReducer===e?u.eagerState:e(a,u.action)),o=u,u=u.next}while(null!==u&&u!==r)c||(s=o,i=a),r$(a,t.memoizedState)||(oo=!0),t.memoizedState=a,t.baseUpdate=s,t.baseState=i,n.lastRenderedState=a}return[t.memoizedState,n.dispatch]}function aG(e){var t=aU();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e=(e=t.queue={last:null,dispatch:null,lastRenderedReducer:a$,lastRenderedState:e}).dispatch=a2.bind(null,ax,e),[t.memoizedState,e]}function aW(e){return az(a$,e)}function aK(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===aI?(aI={lastEffect:null}).lastEffect=e.next=e:null===(t=aI.lastEffect)?aI.lastEffect=e.next=e:(n=t.next,t.next=e,e.next=n,aI.lastEffect=e),e}function aV(e,t,n,r){var i=aU();aD|=e,i.memoizedState=aK(t,n,void 0,void 0===r?null:r)}function aq(e,t,n,r){var i=aH();r=void 0===r?null:r;var a=void 0;if(null!==aT){var o=aT.memoizedState;if(a=o.destroy,null!==r&&aF(r,o.deps)){aK(0,n,a,r);return}}aD|=e,i.memoizedState=aK(t,n,a,r)}function aZ(e,t){return aV(516,192,e,t)}function aX(e,t){return aq(516,192,e,t)}function aJ(e,t){return"function"==typeof t?(t(e=e()),function(){t(null)}):null!=t?(e=e(),t.current=e,function(){t.current=null}):void 0}function aQ(){}function a1(e,t){return aU().memoizedState=[e,void 0===t?null:t],e}function a0(e,t){var n=aH();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&aF(t,r[1])?r[0]:(n.memoizedState=[e,t],e)}function a2(e,t,n){if(!(25>aR))throw Error(f(301));var r=e.alternate;if(e===ax||null!==r&&r===ax){if(aN=!0,e={expirationTime:ak,suspenseConfig:null,action:n,eagerReducer:null,eagerState:null,next:null},null===aP&&(aP=new Map),void 0===(n=aP.get(t)))aP.set(t,e);else{for(t=n;null!==t.next;)t=t.next;t.next=e}}else{var i=sb(),a=i6.suspense;a={expirationTime:i=sm(i,e,a),suspenseConfig:a,action:n,eagerReducer:null,eagerState:null,next:null};var o=t.last;if(null===o)a.next=a;else{var s=o.next;null!==s&&(a.next=s),o.next=a}if(t.last=a,0===e.expirationTime&&(null===r||0===r.expirationTime)&&null!==(r=t.lastRenderedReducer))try{var u=t.lastRenderedState,c=r(u,n);if(a.eagerReducer=r,a.eagerState=c,r$(c,u))return}catch(l){}finally{}sg(e,i)}}var a3={readContext:iK,useCallback:aj,useContext:aj,useEffect:aj,useImperativeHandle:aj,useLayoutEffect:aj,useMemo:aj,useReducer:aj,useRef:aj,useState:aj,useDebugValue:aj,useResponder:aj,useDeferredValue:aj,useTransition:aj},a4={readContext:iK,useCallback:a1,useContext:iK,useEffect:aZ,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,aV(4,36,aJ.bind(null,t,e),n)},useLayoutEffect:function(e,t){return aV(4,36,e,t)},useMemo:function(e,t){var n=aU();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=aU();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e=(e=r.queue={last:null,dispatch:null,lastRenderedReducer:e,lastRenderedState:t}).dispatch=a2.bind(null,ax,e),[r.memoizedState,e]},useRef:function(e){var t=aU();return e={current:e},t.memoizedState=e},useState:aG,useDebugValue:aQ,useResponder:a_,useDeferredValue:function(e,t){var n=aG(e),r=n[0],i=n[1];return aZ(function(){l.unstable_next(function(){var n=aS.suspense;aS.suspense=void 0===t?null:t;try{i(e)}finally{aS.suspense=n}})},[e,t]),r},useTransition:function(e){var t=aG(!1),n=t[0],r=t[1];return[a1(function(t){r(!0),l.unstable_next(function(){var n=aS.suspense;aS.suspense=void 0===e?null:e;try{r(!1),t()}finally{aS.suspense=n}})},[e,n]),n]}},a5={readContext:iK,useCallback:a0,useContext:iK,useEffect:aX,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,aq(4,36,aJ.bind(null,t,e),n)},useLayoutEffect:function(e,t){return aq(4,36,e,t)},useMemo:function(e,t){var n=aH();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&aF(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)},useReducer:az,useRef:function(){return aH().memoizedState},useState:aW,useDebugValue:aQ,useResponder:a_,useDeferredValue:function(e,t){var n=aW(e),r=n[0],i=n[1];return aX(function(){l.unstable_next(function(){var n=aS.suspense;aS.suspense=void 0===t?null:t;try{i(e)}finally{aS.suspense=n}})},[e,t]),r},useTransition:function(e){var t=aW(!1),n=t[0],r=t[1];return[a0(function(t){r(!0),l.unstable_next(function(){var n=aS.suspense;aS.suspense=void 0===e?null:e;try{r(!1),t()}finally{aS.suspense=n}})},[e,n]),n]}},a6=null,a9=null,a8=!1;function a7(e,t){var n=sq(5,null,null,0);n.elementType="DELETED",n.type="DELETED",n.stateNode=t,n.return=e,n.effectTag=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function oe(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,!0);case 6:return null!==(t=""===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,!0);default:return!1}}function ot(e){if(a8){var t=a9;if(t){var n=t;if(!oe(e,t)){if(!(t=n2(n.nextSibling))||!oe(e,t)){e.effectTag=-1025&e.effectTag|2,a8=!1,a6=e;return}a7(a6,n)}a6=e,a9=n2(t.firstChild)}else e.effectTag=-1025&e.effectTag|2,a8=!1,a6=e}}function on(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;a6=e}function or(e){if(e!==a6)return!1;if(!a8)return on(e),a8=!0,!1;var t=e.type;if(5!==e.tag||"head"!==t&&"body"!==t&&!nQ(t,e.memoizedProps))for(t=a9;t;)a7(e,t),t=n2(t.nextSibling);if(on(e),13===e.tag){if(!(e=null!==(e=e.memoizedState)?e.dehydrated:null))throw Error(f(317));a:{for(t=0,e=e.nextSibling;e;){if(8===e.nodeType){var n=e.data;if(n===nK){if(0===t){a9=n2(e.nextSibling);break a}t--}else n!==nW&&n!==nq&&n!==nV||t++}e=e.nextSibling}a9=null}}else a9=a6?n2(e.stateNode.nextSibling):null;return!0}function oi(){a9=a6=null,a8=!1}var oa=Y.ReactCurrentOwner,oo=!1;function os(e,t,n,r){t.child=null===e?ac(t,null,n,r):au(t,e.child,n,r)}function ou(e,t,n,r,i){n=n.render;var a=t.ref;return(iW(t,i),r=aY(e,t,n,r,a,i),null===e||oo)?(t.effectTag|=1,os(e,t,r,i),t.child):(t.updateQueue=e.updateQueue,t.effectTag&=-517,e.expirationTime<=i&&(e.expirationTime=0),o_(e,t,i))}function oc(e,t,n,r,i,a){if(null===e){var o=n.type;return"function"!=typeof o||sZ(o)||void 0!==o.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=sQ(n.type,null,r,null,t.mode,a)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=o,ol(e,t,o,r,i,a))}return(o=e.child,it)&&sf.set(e,t))}}function sv(e,t){e.expirationTime(e=e.nextKnownPendingLevel)?t:e:t}function sw(e){if(0!==e.lastExpiredTime)e.callbackExpirationTime=1073741823,e.callbackPriority=99,e.callbackNode=iI(sE.bind(null,e));else{var t=sy(e),n=e.callbackNode;if(0===t)null!==n&&(e.callbackNode=null,e.callbackExpirationTime=0,e.callbackPriority=90);else{var r=sb();if(r=1073741823===t?99:1===t||2===t?95:0>=(r=10*(1073741821-t)-10*(1073741821-r))?99:250>=r?98:5250>=r?97:95,null!==n){var i=e.callbackPriority;if(e.callbackExpirationTime===t&&i>=r)return;n!==i_&&il(n)}e.callbackExpirationTime=t,e.callbackPriority=r,t=1073741823===t?iI(sE.bind(null,e)):iC(r,s_.bind(null,e),{timeout:10*(1073741821-t)-iM()}),e.callbackNode=t}}}function s_(e,t){if(sp=0,t)return t=sb(),s9(e,t),sw(e),null;var n=sy(e);if(0!==n){if(t=e.callbackNode,(o0&(oK|oV))!==oG)throw Error(f(327));if(sY(),e===o2&&n===o4||sT(e,n),null!==o3){var r=o0;o0|=oK;for(var i=sO(e);;)try{sI();break}catch(a){sM(e,a)}if(iH(),o0=r,o$.current=i,o5===oZ)throw t=o6,sT(e,n),s5(e,n),sw(e),t;if(null===o3)switch(i=e.finishedWork=e.current.alternate,e.finishedExpirationTime=n,o2=null,r=o5){case oq:case oZ:throw Error(f(345));case oX:s9(e,2=n){e.lastPingedTime=n,sT(e,n);break}}if(0!==(o=sy(e))&&o!==n)break;if(0!==r&&r!==n){e.lastPingedTime=r;break}e.timeoutHandle=n1(sR.bind(null,e),i);break}sR(e);break;case oQ:if(s5(e,n),n===(r=e.lastSuspendedTime)&&(e.nextKnownPendingLevel=sP(i)),st&&(0===(i=e.lastPingedTime)||i>=n)){e.lastPingedTime=n,sT(e,n);break}if(0!==(i=sy(e))&&i!==n)break;if(0!==r&&r!==n){e.lastPingedTime=r;break}if(1073741823!==o8?r=10*(1073741821-o8)-iM():1073741823===o9?r=0:(r=10*(1073741821-o9)-5e3,n=10*(1073741821-n)-(i=iM()),0>(r=i-r)&&(r=0),n<(r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*oH(r/1960))-r)&&(r=n)),10=(r=0|s.busyMinDurationMs)?r=0:(i=0|s.busyDelayMs,r=(o=iM()-(10*(1073741821-o)-(0|s.timeoutMs||5e3)))<=i?0:i+r-o),10 component higher in the tree to provide a loading indicator or placeholder to display."+ea(i))}o5!==o1&&(o5=oX),a=ox(a,i),c=r;do{switch(c.tag){case 3:s=a,c.effectTag|=4096,c.expirationTime=t;var g=oB(c,s,t);i1(c,g);break a;case 1:s=a;var v=c.type,y=c.stateNode;if(0==(64&c.effectTag)&&("function"==typeof v.getDerivedStateFromError||null!==y&&"function"==typeof y.componentDidCatch&&(null===ss||!ss.has(y)))){c.effectTag|=4096,c.expirationTime=t;var w=oU(c,s,t);i1(c,w);break a}}c=c.return}while(null!==c)}o3=sN(o3)}catch(_){t=_;continue}break}}function sO(){var e=o$.current;return o$.current=a3,null===e?a3:e}function sA(e,t){ese&&(se=e)}function sC(){for(;null!==o3;)o3=sD(o3)}function sI(){for(;null!==o3&&!id();)o3=sD(o3)}function sD(e){var t=s(e.alternate,e,o4);return e.memoizedProps=e.pendingProps,null===t&&(t=sN(e)),oz.current=null,t}function sN(e){o3=e;do{var t=o3.alternate;if(e=o3.return,0==(2048&o3.effectTag)){a:{var n=t;t=o3;var s=o4,u=t.pendingProps;switch(t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:case 20:case 21:break;case 1:case 17:ie(t.type)&&it(t);break;case 3:am(t),ir(t),(u=t.stateNode).pendingContext&&(u.context=u.pendingContext,u.pendingContext=null),(null===n||null===n.child)&&or(t)&&oE(t),i(t);break;case 5:av(t),s=ap(ah.current);var l=t.type;if(null!==n&&null!=t.stateNode)a(n,t,l,u,s),n.ref!==t.ref&&(t.effectTag|=128);else if(u){var d=ap(af.current);if(or(t)){var h=(u=t).stateNode;n=u.type;var p=u.memoizedProps,b=s;switch(h[n5]=u,h[n6]=p,l=void 0,s=h,n){case"iframe":case"object":case"embed":nw("load",s);break;case"video":case"audio":for(h=0;h",h=p.removeChild(p.firstChild)):"string"==typeof p.is?h=h.createElement(b,{is:p.is}):(h=h.createElement(b),"select"===b&&(b=h,p.multiple?b.multiple=!0:p.size&&(b.size=p.size))):h=h.createElementNS(d,b),(p=h)[n5]=n,p[n6]=u,r(p,t,!1,!1),t.stateNode=p,b=l;var m=s,g=nj(b,n=u);switch(b){case"iframe":case"object":case"embed":nw("load",p),s=n;break;case"video":case"audio":for(s=0;su.tailExpiration&&1l&&(l=n),p>l&&(l=p),s=s.sibling;u.childExpirationTime=l}if(null!==t)return t;null!==e&&0==(2048&e.effectTag)&&(null===e.firstEffect&&(e.firstEffect=o3.firstEffect),null!==o3.lastEffect&&(null!==e.lastEffect&&(e.lastEffect.nextEffect=o3.firstEffect),e.lastEffect=o3.lastEffect),1(e=e.childExpirationTime)?t:e}function sR(e){var t=iO();return iL(99,sj.bind(null,e,t)),null}function sj(e,t){do sY();while(null!==sc)if((o0&(oK|oV))!==oG)throw Error(f(327));var n=e.finishedWork,r=e.finishedExpirationTime;if(null===n)return null;if(e.finishedWork=null,e.finishedExpirationTime=0,n===e.current)throw Error(f(177));e.callbackNode=null,e.callbackExpirationTime=0,e.callbackPriority=90,e.nextKnownPendingLevel=0;var i=sP(n);if(e.firstPendingTime=i,r<=e.lastSuspendedTime?e.firstSuspendedTime=e.lastSuspendedTime=e.nextKnownPendingLevel=0:r<=e.firstSuspendedTime&&(e.firstSuspendedTime=r-1),r<=e.lastPingedTime&&(e.lastPingedTime=0),r<=e.lastExpiredTime&&(e.lastExpiredTime=0),e===o2&&(o3=o2=null,o4=0),1s&&(l=s,s=o,o=l),l=nH(E,o),d=nH(E,s),l&&d&&(1!==k.rangeCount||k.anchorNode!==l.node||k.anchorOffset!==l.offset||k.focusNode!==d.node||k.focusOffset!==d.offset)&&((S=S.createRange()).setStart(l.node,l.offset),k.removeAllRanges(),o>s?(k.addRange(S),k.extend(d.node,d.offset)):(S.setEnd(d.node,d.offset),k.addRange(S))))),S=[],k=E;k=k.parentNode;)1===k.nodeType&&S.push({element:k,left:k.scrollLeft,top:k.scrollTop});for("function"==typeof E.focus&&E.focus(),E=0;E=n)return og(e,t,n);return r4(ay,1&ay.current,t),null!==(t=o_(e,t,n))?t.sibling:null}r4(ay,1&ay.current,t);break;case 19:if(r=t.childExpirationTime>=n,0!=(64&e.effectTag)){if(r)return ow(e,t,n);t.effectTag|=64}if(null!==(i=t.memoizedState)&&(i.rendering=null,i.tail=null),r4(ay,ay.current,t),!r)return null}return o_(e,t,n)}oo=!1}}else oo=!1;switch(t.expirationTime=0,t.tag){case 2:if(r=t.type,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,i=r7(t,r6.current),iW(t,n),i=aY(null,t,r,e,i,n),t.effectTag|=1,"object"==typeof i&&null!==i&&"function"==typeof i.render&&void 0===i.$$typeof){if(t.tag=1,aB(),ie(r)){var a=!0;io(t)}else a=!1;t.memoizedState=null!==i.state&&void 0!==i.state?i.state:null;var o=r.getDerivedStateFromProps;"function"==typeof o&&i8(t,r,o,e),i.updater=i7,t.stateNode=i,i._reactInternalFiber=t,ar(t,r,e,n),t=op(null,t,r,!0,a,n)}else t.tag=0,os(null,t,i,n),t=t.child;return t;case 16:if(i=t.elementType,null!==e&&(e.alternate=null,t.alternate=null,t.effectTag|=2),e=t.pendingProps,er(i),1!==i._status)throw i._result;switch(i=i._result,t.type=i,a=t.tag=sX(i),e=ij(i,e),a){case 0:t=od(null,t,i,e,n);break;case 1:t=oh(null,t,i,e,n);break;case 11:t=ou(null,t,i,e,n);break;case 14:t=oc(null,t,i,ij(i.type,e),r,n);break;default:throw Error(f(306,i,""))}return t;case 0:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:ij(r,i),od(e,t,r,i,n);case 1:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:ij(r,i),oh(e,t,r,i,n);case 3:if(ob(t),null===(r=t.updateQueue))throw Error(f(282));if(i=null!==(i=t.memoizedState)?i.element:null,i3(t,r,t.pendingProps,null,n),(r=t.memoizedState.element)===i)oi(),t=o_(e,t,n);else{if((i=t.stateNode.hydrate)&&(a9=n2(t.stateNode.containerInfo.firstChild),a6=t,i=a8=!0),i)for(n=ac(t,null,r,n),t.child=n;n;)n.effectTag=-3&n.effectTag|1024,n=n.sibling;else os(e,t,r,n),oi();t=t.child}return t;case 5:return ag(t),null===e&&ot(t),r=t.type,i=t.pendingProps,a=null!==e?e.memoizedProps:null,o=i.children,nQ(r,i)?o=null:null!==a&&nQ(r,a)&&(t.effectTag|=16),of(e,t),4&t.mode&&1!==n&&i.hidden?(t.expirationTime=t.childExpirationTime=1,t=null):(os(e,t,o,n),t=t.child),t;case 6:return null===e&&ot(t),null;case 13:return og(e,t,n);case 4:return ab(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=au(t,null,r,n):os(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:ij(r,i),ou(e,t,r,i,n);case 7:return os(e,t,t.pendingProps,n),t.child;case 8:case 12:return os(e,t,t.pendingProps.children,n),t.child;case 10:a:{if(r=t.type._context,i=t.pendingProps,o=t.memoizedProps,i$(t,a=i.value),null!==o){var s=o.value;if(0==(a=r$(s,a)?0:("function"==typeof r._calculateChangedBits?r._calculateChangedBits(s,a):1073741823)|0)){if(o.children===i.children&&!r9.current){t=o_(e,t,n);break a}}else for(null!==(s=t.child)&&(s.return=t);null!==s;){var u=s.dependencies;if(null!==u){o=s.child;for(var c=u.firstContext;null!==c;){if(c.context===r&&0!=(c.observedBits&a)){1===s.tag&&((c=iX(n,null)).tag=2,iQ(s,c)),s.expirationTime=t&&e<=t}function s5(e,t){var n=e.firstSuspendedTime,r=e.lastSuspendedTime;nt||0===n)&&(e.lastSuspendedTime=t),t<=e.lastPingedTime&&(e.lastPingedTime=0),t<=e.lastExpiredTime&&(e.lastExpiredTime=0)}function s6(e,t){t>e.firstPendingTime&&(e.firstPendingTime=t);var n=e.firstSuspendedTime;0!==n&&(t>=n?e.firstSuspendedTime=e.lastSuspendedTime=e.nextKnownPendingLevel=0:t>=e.lastSuspendedTime&&(e.lastSuspendedTime=t+1),t>e.nextKnownPendingLevel&&(e.nextKnownPendingLevel=t))}function s9(e,t){var n=e.lastExpiredTime;(0===n||n>t)&&(e.lastExpiredTime=t)}function s8(e,t,n,r){var i=t.current,a=sb(),o=i6.suspense;a=sm(a,i,o);a:if(n){n=n._reactInternalFiber;b:{if(tr(n)!==n||1!==n.tag)throw Error(f(170));var s=n;do{switch(s.tag){case 3:s=s.stateNode.context;break b;case 1:if(ie(s.type)){s=s.stateNode.__reactInternalMemoizedMergedChildContext;break b}}s=s.return}while(null!==s)throw Error(f(171))}if(1===n.tag){var u=n.type;if(ie(u)){n=ia(n,u,s);break a}}n=s}else n=r5;return null===t.context?t.context=n:t.pendingContext=n,(t=iX(a,o)).payload={element:e},null!==(r=void 0===r?null:r)&&(t.callback=r),iQ(i,t),sg(i,a),a}function s7(e){return(e=e.current).child?(e.child.tag,e.child.stateNode):null}function ue(e,t){null!==(e=e.memoizedState)&&null!==e.dehydrated&&e.retryTime1&&void 0!==arguments[1]?arguments[1]:this.props,n=t.target;if(n){var r=n;"string"==typeof n&&(r=window[n]),_(t,e.bind(null,r))}}},{key:"render",value:function(){return this.props.children||null}}]),t}(h.PureComponent);S.propTypes={},t.withOptions=E,t.default=S},69590(e){"use strict";var t=Array.isArray,n=Object.keys,r=Object.prototype.hasOwnProperty,i="undefined"!=typeof Element;function a(e,o){if(e===o)return!0;if(e&&o&&"object"==typeof e&&"object"==typeof o){var s,u,c,l=t(e),f=t(o);if(l&&f){if((u=e.length)!=o.length)return!1;for(s=u;0!=s--;)if(!a(e[s],o[s]))return!1;return!0}if(l!=f)return!1;var d=e instanceof Date,h=o instanceof Date;if(d!=h)return!1;if(d&&h)return e.getTime()==o.getTime();var p=e instanceof RegExp,b=o instanceof RegExp;if(p!=b)return!1;if(p&&b)return e.toString()==o.toString();var m=n(e);if((u=m.length)!==n(o).length)return!1;for(s=u;0!=s--;)if(!r.call(o,m[s]))return!1;if(i&&e instanceof Element&&o instanceof Element)return e===o;for(s=u;0!=s--;)if(("_owner"!==(c=m[s])||!e.$$typeof)&&!a(e[c],o[c]))return!1;return!0}return e!=e&&o!=o}e.exports=function(e,t){try{return a(e,t)}catch(n){if(n.message&&n.message.match(/stack|recursion/i)||-2146828260===n.number)return console.warn("Warning: react-fast-compare does not handle circular references.",n.name,n.message),!1;throw n}}},57209(e,t,n){"use strict";function r(e){return e&&"object"==typeof e&&"default"in e?e.default:e}i={value:!0};var i,a=r(n(67294));function o(e){return o.warnAboutHMRDisabled&&(o.warnAboutHMRDisabled=!0,console.error("React-Hot-Loader: misconfiguration detected, using production version in non-production environment."),console.error("React-Hot-Loader: Hot Module Replacement is not enabled.")),a.Children.only(e.children)}o.warnAboutHMRDisabled=!1;var s=function e(){return e.shouldWrapWithAppContainer?function(e){return function(t){return a.createElement(o,null,a.createElement(e,t))}}:function(e){return e}};s.shouldWrapWithAppContainer=!1;var u=function(e,t){return e===t},c=function(){},l=function(e){return e},f=function(){};t.zj=o,t.wU=s,i=u,i=c,i=l,i=f},69921(e,t){"use strict";/** @license React v16.13.1 + * react-is.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,i=n?Symbol.for("react.portal"):60106,a=n?Symbol.for("react.fragment"):60107,o=n?Symbol.for("react.strict_mode"):60108,s=n?Symbol.for("react.profiler"):60114,u=n?Symbol.for("react.provider"):60109,c=n?Symbol.for("react.context"):60110,l=n?Symbol.for("react.async_mode"):60111,f=n?Symbol.for("react.concurrent_mode"):60111,d=n?Symbol.for("react.forward_ref"):60112,h=n?Symbol.for("react.suspense"):60113,p=n?Symbol.for("react.suspense_list"):60120,b=n?Symbol.for("react.memo"):60115,m=n?Symbol.for("react.lazy"):60116,g=n?Symbol.for("react.block"):60121,v=n?Symbol.for("react.fundamental"):60117,y=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function _(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case l:case f:case a:case s:case o:case h:return e;default:switch(e=e&&e.$$typeof){case c:case d:case m:case b:case u:return e;default:return t}}case i:return t}}}function E(e){return _(e)===f}t.AsyncMode=l,t.ConcurrentMode=f,t.ContextConsumer=c,t.ContextProvider=u,t.Element=r,t.ForwardRef=d,t.Fragment=a,t.Lazy=m,t.Memo=b,t.Portal=i,t.Profiler=s,t.StrictMode=o,t.Suspense=h,t.isAsyncMode=function(e){return E(e)||_(e)===l},t.isConcurrentMode=E,t.isContextConsumer=function(e){return _(e)===c},t.isContextProvider=function(e){return _(e)===u},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return _(e)===d},t.isFragment=function(e){return _(e)===a},t.isLazy=function(e){return _(e)===m},t.isMemo=function(e){return _(e)===b},t.isPortal=function(e){return _(e)===i},t.isProfiler=function(e){return _(e)===s},t.isStrictMode=function(e){return _(e)===o},t.isSuspense=function(e){return _(e)===h},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===a||e===f||e===s||e===o||e===h||e===p||"object"==typeof e&&null!==e&&(e.$$typeof===m||e.$$typeof===b||e.$$typeof===u||e.$$typeof===c||e.$$typeof===d||e.$$typeof===v||e.$$typeof===y||e.$$typeof===w||e.$$typeof===g)},t.typeOf=_},59864(e,t,n){"use strict";e.exports=n(69921)},46871(e,t,n){"use strict";function r(){var e=this.constructor.getDerivedStateFromProps(this.props,this.state);null!=e&&this.setState(e)}function i(e){function t(t){var n=this.constructor.getDerivedStateFromProps(e,t);return null!=n?n:null}this.setState(t.bind(this))}function a(e,t){try{var n=this.props,r=this.state;this.props=e,this.state=t,this.__reactInternalSnapshotFlag=!0,this.__reactInternalSnapshot=this.getSnapshotBeforeUpdate(n,r)}finally{this.props=n,this.state=r}}function o(e){var t,n=e.prototype;if(!n||!n.isReactComponent)throw Error("Can only polyfill class components");if("function"!=typeof e.getDerivedStateFromProps&&"function"!=typeof n.getSnapshotBeforeUpdate)return e;var o=null,s=null,u=null;if("function"==typeof n.componentWillMount?o="componentWillMount":"function"==typeof n.UNSAFE_componentWillMount&&(o="UNSAFE_componentWillMount"),"function"==typeof n.componentWillReceiveProps?s="componentWillReceiveProps":"function"==typeof n.UNSAFE_componentWillReceiveProps&&(s="UNSAFE_componentWillReceiveProps"),"function"==typeof n.componentWillUpdate?u="componentWillUpdate":"function"==typeof n.UNSAFE_componentWillUpdate&&(u="UNSAFE_componentWillUpdate"),null!==o||null!==s||null!==u){throw Error("Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n"+(e.displayName||e.name)+" uses "+("function"==typeof e.getDerivedStateFromProps?"getDerivedStateFromProps()":"getSnapshotBeforeUpdate()")+" but also contains the following legacy lifecycles:"+(null!==o?"\n "+o:"")+(null!==s?"\n "+s:"")+(null!==u?"\n "+u:"")+"\n\nThe above lifecycles should be removed. Learn more about this warning here:\nhttps://fb.me/react-async-component-lifecycle-hooks")}if("function"==typeof e.getDerivedStateFromProps&&(n.componentWillMount=r,n.componentWillReceiveProps=i),"function"==typeof n.getSnapshotBeforeUpdate){if("function"!=typeof n.componentDidUpdate)throw Error("Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype");n.componentWillUpdate=a;var c=n.componentDidUpdate;n.componentDidUpdate=function(e,t,n){var r=this.__reactInternalSnapshotFlag?this.__reactInternalSnapshot:n;c.call(this,e,t,r)}}return e}n.r(t),n.d(t,{polyfill:()=>o}),r.__suppressDeprecationWarning=!0,i.__suppressDeprecationWarning=!0,a.__suppressDeprecationWarning=!0},55977(e,t,n){"use strict";n.d(t,{zt:()=>h,$j:()=>J,wU:()=>A,I0:()=>er,v9:()=>es});var r=n(67294);n(45697);var i=r.createContext(null);function a(e){e()}var o=a,s=function(e){return o=e},u=function(){return o},c={notify:function(){}};function l(){var e=u(),t=null,n=null;return{clear:function(){t=null,n=null},notify:function(){e(function(){for(var e=t;e;)e.callback(),e=e.next})},get:function(){for(var e=[],n=t;n;)e.push(n),n=n.next;return e},subscribe:function(e){var r=!0,i=n={callback:e,next:null,prev:n};return i.prev?i.prev.next=i:t=i,function(){r&&null!==t&&(r=!1,i.next?i.next.prev=i.prev:n=i.prev,i.prev?i.prev.next=i.next:t=i.next)}}}}var f=function(){function e(e,t){this.store=e,this.parentSub=t,this.unsubscribe=null,this.listeners=c,this.handleChangeWrapper=this.handleChangeWrapper.bind(this)}var t=e.prototype;return t.addNestedSub=function(e){return this.trySubscribe(),this.listeners.subscribe(e)},t.notifyNestedSubs=function(){this.listeners.notify()},t.handleChangeWrapper=function(){this.onStateChange&&this.onStateChange()},t.isSubscribed=function(){return Boolean(this.unsubscribe)},t.trySubscribe=function(){this.unsubscribe||(this.unsubscribe=this.parentSub?this.parentSub.addNestedSub(this.handleChangeWrapper):this.store.subscribe(this.handleChangeWrapper),this.listeners=l())},t.tryUnsubscribe=function(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null,this.listeners.clear(),this.listeners=c)},e}();function d(e){var t=e.store,n=e.context,a=e.children,o=(0,r.useMemo)(function(){var e=new f(t);return e.onStateChange=e.notifyNestedSubs,{store:t,subscription:e}},[t]),s=(0,r.useMemo)(function(){return t.getState()},[t]);(0,r.useEffect)(function(){var e=o.subscription;return e.trySubscribe(),s!==t.getState()&&e.notifyNestedSubs(),function(){e.tryUnsubscribe(),e.onStateChange=null}},[o,s]);var u=n||i;return r.createElement(u.Provider,{value:o},a)}let h=d;var p=n(87462);function b(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var m=n(8679),g=n.n(m),v=n(59864),y="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?r.useLayoutEffect:r.useEffect,w=[],_=[null,null];function E(e,t){var n=e[1];return[t.payload,n+1]}function S(e,t,n){y(function(){return e.apply(void 0,t)},n)}function k(e,t,n,r,i,a,o){e.current=r,t.current=i,n.current=!1,a.current&&(a.current=null,o())}function x(e,t,n,r,i,a,o,s,u,c){if(e){var l,f=!1,d=null,h=function(){if(!f){var e,n,l=t.getState();try{e=r(l,i.current)}catch(h){n=h,d=h}n||(d=null),e===a.current?o.current||u():(a.current=e,s.current=e,o.current=!0,c({type:"STORE_UPDATED",payload:{error:n}}))}};return n.onStateChange=h,n.trySubscribe(),h(),function(){if(f=!0,n.tryUnsubscribe(),n.onStateChange=null,d)throw d}}}var T=function(){return[null,0]};function M(e,t){void 0===t&&(t={});var n=t,a=n.getDisplayName,o=void 0===a?function(e){return"ConnectAdvanced("+e+")"}:a,s=n.methodName,u=void 0===s?"connectAdvanced":s,c=n.renderCountProp,l=void 0===c?void 0:c,d=n.shouldHandleStateChanges,h=void 0===d||d,m=n.storeKey,y=void 0===m?"store":m,M=(n.withRef,n.forwardRef),O=void 0!==M&&M,A=n.context,L=void 0===A?i:A,C=b(n,["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef","forwardRef","context"]),I=L;return function(t){var n=t.displayName||t.name||"Component",i=o(n),a=(0,p.Z)({},C,{getDisplayName:o,methodName:u,renderCountProp:l,shouldHandleStateChanges:h,storeKey:y,displayName:i,wrappedComponentName:n,WrappedComponent:t}),s=C.pure;function c(t){return e(t.dispatch,a)}var d=s?r.useMemo:function(e){return e()};function m(e){var n=(0,r.useMemo)(function(){var t=e.reactReduxForwardedRef,n=b(e,["reactReduxForwardedRef"]);return[e.context,t,n]},[e]),i=n[0],a=n[1],o=n[2],s=(0,r.useMemo)(function(){return i&&i.Consumer&&(0,v.isContextConsumer)(r.createElement(i.Consumer,null))?i:I},[i,I]),u=(0,r.useContext)(s),l=Boolean(e.store)&&Boolean(e.store.getState)&&Boolean(e.store.dispatch);Boolean(u)&&u.store;var m=l?e.store:u.store,g=(0,r.useMemo)(function(){return c(m)},[m]),y=(0,r.useMemo)(function(){if(!h)return _;var e=new f(m,l?null:u.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]},[m,l,u]),M=y[0],O=y[1],A=(0,r.useMemo)(function(){return l?u:(0,p.Z)({},u,{subscription:M})},[l,u,M]),L=(0,r.useReducer)(E,w,T),C=L[0][0],D=L[1];if(C&&C.error)throw C.error;var N=(0,r.useRef)(),P=(0,r.useRef)(o),R=(0,r.useRef)(),j=(0,r.useRef)(!1),F=d(function(){return R.current&&o===P.current?R.current:g(m.getState(),o)},[m,C,o]);S(k,[P,N,j,o,F,R,O]),S(x,[h,m,M,g,P,N,j,R,O,D],[m,M,g]);var Y=(0,r.useMemo)(function(){return r.createElement(t,(0,p.Z)({},F,{ref:a}))},[a,t,F]);return(0,r.useMemo)(function(){return h?r.createElement(s.Provider,{value:A},Y):Y},[s,Y,A])}var M=s?r.memo(m):m;if(M.WrappedComponent=t,M.displayName=i,O){var A=r.forwardRef(function(e,t){return r.createElement(M,(0,p.Z)({},e,{reactReduxForwardedRef:t}))});return A.displayName=i,A.WrappedComponent=t,g()(A,t)}return g()(M,t)}}function O(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function A(e,t){if(O(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(var i=0;i=0;r--){var i=t[r](e);if(i)return i}return function(t,r){throw Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function Z(e,t){return e===t}function X(e){var t=void 0===e?{}:e,n=t.connectHOC,r=void 0===n?M:n,i=t.mapStateToPropsFactories,a=void 0===i?B:i,o=t.mapDispatchToPropsFactories,s=void 0===o?j:o,u=t.mergePropsFactories,c=void 0===u?G:u,l=t.selectorFactory,f=void 0===l?V:l;return function(e,t,n,i){void 0===i&&(i={});var o=i,u=o.pure,l=void 0===u||u,d=o.areStatesEqual,h=void 0===d?Z:d,m=o.areOwnPropsEqual,g=void 0===m?A:m,v=o.areStatePropsEqual,y=void 0===v?A:v,w=o.areMergedPropsEqual,_=void 0===w?A:w,E=b(o,["pure","areStatesEqual","areOwnPropsEqual","areStatePropsEqual","areMergedPropsEqual"]),S=q(e,a,"mapStateToProps"),k=q(t,s,"mapDispatchToProps"),x=q(n,c,"mergeProps");return r(f,(0,p.Z)({methodName:"connect",getDisplayName:function(e){return"Connect("+e+")"},shouldHandleStateChanges:Boolean(e),initMapStateToProps:S,initMapDispatchToProps:k,initMergeProps:x,pure:l,areStatesEqual:h,areOwnPropsEqual:g,areStatePropsEqual:y,areMergedPropsEqual:_},E))}}let J=X();function Q(){var e;return(0,r.useContext)(i)}function ee(e){void 0===e&&(e=i);var t=e===i?Q:function(){return(0,r.useContext)(e)};return function(){return t().store}}var et=ee();function en(e){void 0===e&&(e=i);var t=e===i?et:ee(e);return function(){return t().dispatch}}var er=en(),ei=function(e,t){return e===t};function ea(e,t,n,i){var a,o=(0,r.useReducer)(function(e){return e+1},0)[1],s=(0,r.useMemo)(function(){return new f(n,i)},[n,i]),u=(0,r.useRef)(),c=(0,r.useRef)(),l=(0,r.useRef)(),d=(0,r.useRef)(),h=n.getState();try{a=e!==c.current||h!==l.current||u.current?e(h):d.current}catch(p){throw u.current&&(p.message+="\nThe error may be correlated with this previous error:\n"+u.current.stack+"\n\n"),p}return y(function(){c.current=e,l.current=h,d.current=a,u.current=void 0}),y(function(){function e(){try{var e=c.current(n.getState());if(t(e,d.current))return;d.current=e}catch(r){u.current=r}o()}return s.onStateChange=e,s.trySubscribe(),e(),function(){return s.tryUnsubscribe()}},[n,s]),a}function eo(e){void 0===e&&(e=i);var t=e===i?Q:function(){return(0,r.useContext)(e)};return function(e,n){void 0===n&&(n=ei);var i,a=t(),o=ea(e,n,a.store,a.subscription);return(0,r.useDebugValue)(o),o}}var es=eo();s(n(73935).unstable_batchedUpdates)},76(e,t,n){"use strict";n.d(t,{VK:()=>f,rU:()=>v});var r=n(47886);function i(e,t){return(i=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function a(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,i(e,t)}var o=n(67294),s=n(90071);function u(){return(u=Object.assign||function(e){for(var t=1;t=0||(i[n]=e[n]);return i}n(45697);var l=n(2177),f=function(e){function t(){for(var t,n=arguments.length,r=Array(n),i=0;iN,AW:()=>U,F0:()=>M,rs:()=>$,s6:()=>T,LX:()=>Y,k6:()=>G,TH:()=>W,UO:()=>K,$B:()=>V});var a=n(67294),o=n(45697),s=n.n(o),u=n(90071);function c(e,t){return(c=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function l(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,c(e,t)}var f=1073741823,d="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:void 0!==n.g?n.g:{};function h(){var e="__global_unique_id__";return d[e]=(d[e]||0)+1}function p(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}function b(e){var t=[];return{on:function(e){t.push(e)},off:function(e){t=t.filter(function(t){return t!==e})},get:function(){return e},set:function(n,r){e=n,t.forEach(function(t){return t(e,r)})}}}function m(e){return Array.isArray(e)?e[0]:e}function g(e,t){var n,r,i="__create-react-context-"+h()+"__",o=function(e){function n(){var t;return t=e.apply(this,arguments)||this,t.emitter=b(t.props.value),t}l(n,e);var r=n.prototype;return r.getChildContext=function(){var e;return(e={})[i]=this.emitter,e},r.componentWillReceiveProps=function(e){if(this.props.value!==e.value){var n,r=this.props.value,i=e.value;p(r,i)?n=0:(n="function"==typeof t?t(r,i):f,0!=(n|=0)&&this.emitter.set(e.value,n))}},r.render=function(){return this.props.children},n}(a.Component);o.childContextTypes=((n={})[i]=s().object.isRequired,n);var u=function(t){function n(){var e;return e=t.apply(this,arguments)||this,e.state={value:e.getValue()},e.onUpdate=function(t,n){((0|e.observedBits)&n)!=0&&e.setState({value:e.getValue()})},e}l(n,t);var r=n.prototype;return r.componentWillReceiveProps=function(e){var t=e.observedBits;this.observedBits=null==t?f:t},r.componentDidMount=function(){this.context[i]&&this.context[i].on(this.onUpdate);var e=this.props.observedBits;this.observedBits=null==e?f:e},r.componentWillUnmount=function(){this.context[i]&&this.context[i].off(this.onUpdate)},r.getValue=function(){return this.context[i]?this.context[i].get():e},r.render=function(){return m(this.props.children)(this.state.value)},n}(a.Component);return u.contextTypes=((r={})[i]=s().object,r),{Provider:o,Consumer:u}}var v=a.createContext||g;let y=v;var w=n(2177);function _(){return(_=Object.assign||function(e){for(var t=1;t=0||(i[n]=e[n]);return i}function l(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}n(54726);var f="unmounted";t.UNMOUNTED=f;var d="exited";t.EXITED=d;var h="entering";t.ENTERING=h;var p="entered";t.ENTERED=p;var b="exiting";t.EXITING=b;var m=function(e){function t(t,n){r=e.call(this,t,n)||this;var r,i,a=n.transitionGroup,o=a&&!a.isMounting?t.enter:t.appear;return r.appearStatus=null,t.in?o?(i=d,r.appearStatus=h):i=p:i=t.unmountOnExit||t.mountOnEnter?f:d,r.state={status:i},r.nextCallback=null,r}l(t,e);var n=t.prototype;return n.getChildContext=function(){return{transitionGroup:null}},t.getDerivedStateFromProps=function(e,t){return e.in&&t.status===f?{status:d}:null},n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?n!==h&&n!==p&&(t=h):(n===h||n===p)&&(t=b)}this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e,t,n,r=this.props.timeout;return e=t=n=r,null!=r&&"number"!=typeof r&&(e=r.exit,t=r.enter,n=void 0!==r.appear?r.appear:t),{exit:e,enter:t,appear:n}},n.updateStatus=function(e,t){if(void 0===e&&(e=!1),null!==t){this.cancelNextCallback();var n=a.default.findDOMNode(this);t===h?this.performEnter(n,e):this.performExit(n)}else this.props.unmountOnExit&&this.state.status===d&&this.setState({status:f})},n.performEnter=function(e,t){var n=this,r=this.props.enter,i=this.context.transitionGroup?this.context.transitionGroup.isMounting:t,a=this.getTimeouts(),o=i?a.appear:a.enter;if(!t&&!r){this.safeSetState({status:p},function(){n.props.onEntered(e)});return}this.props.onEnter(e,i),this.safeSetState({status:h},function(){n.props.onEntering(e,i),n.onTransitionEnd(e,o,function(){n.safeSetState({status:p},function(){n.props.onEntered(e,i)})})})},n.performExit=function(e){var t=this,n=this.props.exit,r=this.getTimeouts();if(!n){this.safeSetState({status:d},function(){t.props.onExited(e)});return}this.props.onExit(e),this.safeSetState({status:b},function(){t.props.onExiting(e),t.onTransitionEnd(e,r.exit,function(){t.safeSetState({status:d},function(){t.props.onExited(e)})})})},n.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(r){n&&(n=!1,t.nextCallback=null,e(r))},this.nextCallback.cancel=function(){n=!1},this.nextCallback},n.onTransitionEnd=function(e,t,n){this.setNextCallback(n);var r=null==t&&!this.props.addEndListener;if(!e||r){setTimeout(this.nextCallback,0);return}this.props.addEndListener&&this.props.addEndListener(e,this.nextCallback),null!=t&&setTimeout(this.nextCallback,t)},n.render=function(){var e=this.state.status;if(e===f)return null;var t=this.props,n=t.children,r=c(t,["children"]);if(delete r.in,delete r.mountOnEnter,delete r.unmountOnExit,delete r.appear,delete r.enter,delete r.exit,delete r.timeout,delete r.addEndListener,delete r.onEnter,delete r.onEntering,delete r.onEntered,delete r.onExit,delete r.onExiting,delete r.onExited,"function"==typeof n)return n(e,r);var a=i.default.Children.only(n);return i.default.cloneElement(a,r)},t}(i.default.Component);function g(){}m.contextTypes={transitionGroup:r.object},m.childContextTypes={transitionGroup:function(){}},m.propTypes={},m.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:g,onEntering:g,onEntered:g,onExit:g,onExiting:g,onExited:g},m.UNMOUNTED=0,m.EXITED=1,m.ENTERING=2,m.ENTERED=3,m.EXITING=4;var v=(0,o.polyfill)(m);t.default=v},92381(e,t,n){"use strict";t.__esModule=!0,t.default=void 0;var r=s(n(45697)),i=s(n(67294)),a=n(46871),o=n(40537);function s(e){return e&&e.__esModule?e:{default:e}}function u(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}function c(){return(c=Object.assign||function(e){for(var t=1;tI.length&&I.push(e)}function P(e,t,n,r){var i=typeof e;("undefined"===i||"boolean"===i)&&(e=null);var s=!1;if(null===e)s=!0;else switch(i){case"string":case"number":s=!0;break;case"object":switch(e.$$typeof){case a:case o:s=!0}}if(s)return n(r,e,""===t?"."+j(e,0):t),1;if(s=0,t=""===t?".":t+":",Array.isArray(e))for(var u=0;u2)?"one of ".concat(t," ").concat(e.slice(0,n-1).join(", "),", or ")+e[n-1]:2===n?"one of ".concat(t," ").concat(e[0]," or ").concat(e[1]):"of ".concat(t," ").concat(e[0])}function a(e,t,n){return e.substr(!n||n<0?0:+n,t.length)===t}function o(e,t,n){return(void 0===n||n>e.length)&&(n=e.length),e.substring(n-t.length,n)===t}function s(e,t,n){return"number"!=typeof n&&(n=0),!(n+t.length>e.length)&&-1!==e.indexOf(t,n)}r("ERR_INVALID_OPT_VALUE",function(e,t){return'The value "'+t+'" is invalid for option "'+e+'"'},TypeError),r("ERR_INVALID_ARG_TYPE",function(e,t,n){if("string"==typeof t&&a(t,"not ")?(r="must not be",t=t.replace(/^not /,"")):r="must be",o(e," argument"))u="The ".concat(e," ").concat(r," ").concat(i(t,"type"));else{var r,u,c=s(e,".")?"property":"argument";u='The "'.concat(e,'" ').concat(c," ").concat(r," ").concat(i(t,"type"))}return u+". Received type ".concat(typeof n)},TypeError),r("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),r("ERR_METHOD_NOT_IMPLEMENTED",function(e){return"The "+e+" method is not implemented"}),r("ERR_STREAM_PREMATURE_CLOSE","Premature close"),r("ERR_STREAM_DESTROYED",function(e){return"Cannot call "+e+" after a stream was destroyed"}),r("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),r("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),r("ERR_STREAM_WRITE_AFTER_END","write after end"),r("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),r("ERR_UNKNOWN_ENCODING",function(e){return"Unknown encoding: "+e},TypeError),r("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),e.exports.q=n},56753(e,t,n){"use strict";var r=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=c;var i=n(79481),a=n(64229);n(35717)(c,i);for(var o=r(a.prototype),s=0;s0){if("string"==typeof t||u.objectMode||Object.getPrototypeOf(t)===a.prototype||(t=s(t)),r)u.endEmitted?S(e,new E):A(e,u,t,!0);else if(u.ended)S(e,new w);else{if(u.destroyed)return!1;u.reading=!1,u.decoder&&!n?(t=u.decoder.write(t),u.objectMode||0!==t.length?A(e,u,t,!1):j(e,u)):A(e,u,t,!1)}}else r||(u.reading=!1,j(e,u));return!u.ended&&(u.length=C?e=C:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}function D(e,t){return e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!=e?t.flowing&&t.length?t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=I(e)),e<=t.length)?e:t.ended?t.length:(t.needReadable=!0,0)}function N(e,t){if(f("onEofChunk"),!t.ended){if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?P(e):(t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,R(e)))}}function P(e){var t=e._readableState;f("emitReadable",t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(f("emitReadable",t.flowing),t.emittedReadable=!0,process.nextTick(R,e))}function R(e){var t=e._readableState;f("emitReadable_",t.destroyed,t.length,t.ended),!t.destroyed&&(t.length||t.ended)&&(e.emit("readable"),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,z(e)}function j(e,t){t.readingMore||(t.readingMore=!0,process.nextTick(F,e,t))}function F(e,t){for(;!t.reading&&!t.ended&&(t.length0,t.resumeScheduled&&!t.paused?t.flowing=!0:e.listenerCount("data")>0&&e.resume()}function U(e){f("readable nexttick read 0"),e.read(0)}function H(e,t){t.resumeScheduled||(t.resumeScheduled=!0,process.nextTick($,e,t))}function $(e,t){f("resume",t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit("resume"),z(e),t.flowing&&!t.reading&&e.read(0)}function z(e){var t=e._readableState;for(f("flow",t.flowing);t.flowing&&null!==e.read(););}function G(e,t){var n;return 0===t.length?null:(t.objectMode?n=t.buffer.shift():!e||e>=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.first():t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n)}function W(e){var t=e._readableState;f("endReadable",t.endEmitted),t.endEmitted||(t.ended=!0,process.nextTick(K,t,e))}function K(e,t){if(f("endReadableNT",e.endEmitted,e.length),!e.endEmitted&&0===e.length&&(e.endEmitted=!0,t.readable=!1,t.emit("end"),e.autoDestroy)){var n=t._writableState;(!n||n.autoDestroy&&n.finished)&&t.destroy()}}function V(e,t){for(var n=0,r=e.length;n=n.highWaterMark:n.length>0)||n.ended))return f("read: emitReadable",n.length,n.ended),0===n.length&&n.ended?W(this):P(this),null;if(0===(e=D(e,n))&&n.ended)return 0===n.length&&W(this),null;var i=n.needReadable;return f("need readable",i),(0===n.length||n.length-e0?G(e,n):null)?(n.needReadable=n.length<=n.highWaterMark,e=0):(n.length-=e,n.awaitDrain=0),0===n.length&&(n.ended||(n.needReadable=!0),r!==e&&n.ended&&W(this)),null!==t&&this.emit("data",t),t},M.prototype._read=function(e){S(this,new _("_read()"))},M.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,f("pipe count=%d opts=%j",i.pipesCount,t);var a=t&&!1===t.end||e===process.stdout||e===process.stderr?m:s;function o(e,t){f("onunpipe"),e===n&&t&&!1===t.hasUnpiped&&(t.hasUnpiped=!0,l())}function s(){f("onend"),e.end()}i.endEmitted?process.nextTick(a):n.once("end",a),e.on("unpipe",o);var u=Y(n);e.on("drain",u);var c=!1;function l(){f("cleanup"),e.removeListener("close",p),e.removeListener("finish",b),e.removeListener("drain",u),e.removeListener("error",h),e.removeListener("unpipe",o),n.removeListener("end",s),n.removeListener("end",m),n.removeListener("data",d),c=!0,i.awaitDrain&&(!e._writableState||e._writableState.needDrain)&&u()}function d(t){f("ondata");var r=e.write(t);f("dest.write",r),!1===r&&((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==V(i.pipes,e))&&!c&&(f("false write response, pause",i.awaitDrain),i.awaitDrain++),n.pause())}function h(t){f("onerror",t),m(),e.removeListener("error",h),0===r(e,"error")&&S(e,t)}function p(){e.removeListener("finish",b),m()}function b(){f("onfinish"),e.removeListener("close",p),m()}function m(){f("unpipe"),n.unpipe(e)}return n.on("data",d),x(e,"error",h),e.once("close",p),e.once("finish",b),e.emit("pipe",n),i.flowing||(f("pipe resume"),n.resume()),e},M.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n)),this;if(!e){var r=t.pipes,i=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var a=0;a0,!1!==r.flowing&&this.resume()):"readable"!==e||r.endEmitted||r.readableListening||(r.readableListening=r.needReadable=!0,r.flowing=!1,r.emittedReadable=!1,f("on readable",r.length,r.reading),r.length?P(this):r.reading||process.nextTick(U,this)),n},M.prototype.addListener=M.prototype.on,M.prototype.removeListener=function(e,t){var n=i.prototype.removeListener.call(this,e,t);return"readable"===e&&process.nextTick(B,this),n},M.prototype.removeAllListeners=function(e){var t=i.prototype.removeAllListeners.apply(this,arguments);return("readable"===e||void 0===e)&&process.nextTick(B,this),t},M.prototype.resume=function(){var e=this._readableState;return e.flowing||(f("resume"),e.flowing=!e.readableListening,H(this,e)),e.paused=!1,this},M.prototype.pause=function(){return f("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(f("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},M.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var i in e.on("end",function(){if(f("wrapped end"),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)}),e.on("data",function(i){if(f("wrapped data"),n.decoder&&(i=n.decoder.write(i)),!n.objectMode||null!=i)(n.objectMode||i&&i.length)&&(t.push(i)||(r=!0,e.pause()))}),e)void 0===this[i]&&"function"==typeof e[i]&&(this[i]=function(t){return function(){return e[t].apply(e,arguments)}}(i));for(var a=0;a-1))throw new E(e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(T.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(T.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),T.prototype._write=function(e,t,n){n(new m("_write()"))},T.prototype._writev=null,T.prototype.end=function(e,t,n){var r=this._writableState;return"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||H(this,r,n),this},Object.defineProperty(T.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(T.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),T.prototype.destroy=d.destroy,T.prototype._undestroy=d.undestroy,T.prototype._destroy=function(e,t){t(e)}},45850(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var i,a=n(8610),o=Symbol("lastResolve"),s=Symbol("lastReject"),u=Symbol("error"),c=Symbol("ended"),l=Symbol("lastPromise"),f=Symbol("handlePromise"),d=Symbol("stream");function h(e,t){return{value:e,done:t}}function p(e){var t=e[o];if(null!==t){var n=e[d].read();null!==n&&(e[l]=null,e[o]=null,e[s]=null,t(h(n,!1)))}}function b(e){process.nextTick(p,e)}function m(e,t){return function(n,r){e.then(function(){if(t[c]){n(h(void 0,!0));return}t[f](n,r)},r)}}var g=Object.getPrototypeOf(function(){}),v=Object.setPrototypeOf((i={get stream(){return this[d]},next:function(){var e,t=this,n=this[u];if(null!==n)return Promise.reject(n);if(this[c])return Promise.resolve(h(void 0,!0));if(this[d].destroyed)return new Promise(function(e,n){process.nextTick(function(){t[u]?n(t[u]):e(h(void 0,!0))})});var r=this[l];if(r)e=new Promise(m(r,this));else{var i=this[d].read();if(null!==i)return Promise.resolve(h(i,!1));e=new Promise(this[f])}return this[l]=e,e}},r(i,Symbol.asyncIterator,function(){return this}),r(i,"return",function(){var e=this;return new Promise(function(t,n){e[d].destroy(null,function(e){if(e){n(e);return}t(h(void 0,!0))})})}),i),g),y=function(e){var t,n=Object.create(v,(r(t={},d,{value:e,writable:!0}),r(t,o,{value:null,writable:!0}),r(t,s,{value:null,writable:!0}),r(t,u,{value:null,writable:!0}),r(t,c,{value:e._readableState.endEmitted,writable:!0}),r(t,f,{value:function(e,t){var r=n[d].read();r?(n[l]=null,n[o]=null,n[s]=null,e(h(r,!1))):(n[o]=e,n[s]=t)},writable:!0}),t));return n[l]=null,a(e,function(e){if(e&&"ERR_STREAM_PREMATURE_CLOSE"!==e.code){var t=n[s];null!==t&&(n[l]=null,n[o]=null,n[s]=null,t(e)),n[u]=e;return}var r=n[o];null!==r&&(n[l]=null,n[o]=null,n[s]=null,r(h(void 0,!0))),n[c]=!0}),e.on("readable",b.bind(null,n)),n};e.exports=y},77086(e,t,n){"use strict";function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function i(e){for(var t=1;t0?this.tail.next=t:this.head=t,this.tail=t,++this.length}},{key:"unshift",value:function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length}},{key:"shift",value:function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n}},{key:"concat",value:function(e){if(0===this.length)return c.alloc(0);for(var t=c.allocUnsafe(e>>>0),n=this.head,r=0;n;)d(n.data,t,r),r+=n.data.length,n=n.next;return t}},{key:"consume",value:function(e,t){var n;return ei.length?i.length:e;if(a===i.length?r+=i:r+=i.slice(0,e),0==(e-=a)){a===i.length?(++n,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=i.slice(a));break}++n}return this.length-=n,r}},{key:"_getBuffer",value:function(e){var t=c.allocUnsafe(e),n=this.head,r=1;for(n.data.copy(t),e-=n.data.length;n=n.next;){var i=n.data,a=e>i.length?i.length:e;if(i.copy(t,t.length-e,0,a),0==(e-=a)){a===i.length?(++r,n.next?this.head=n.next:this.head=this.tail=null):(this.head=n,n.data=i.slice(a));break}++r}return this.length-=r,t}},{key:f,value:function(e,t){return l(this,i({},t,{depth:0,customInspect:!1}))}}]),e}()},61195(e){"use strict";function t(e,t){var i=this,o=this._readableState&&this._readableState.destroyed,s=this._writableState&&this._writableState.destroyed;return o||s?(t?t(e):e&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,process.nextTick(a,this,e)):process.nextTick(a,this,e)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,function(e){!t&&e?i._writableState?i._writableState.errorEmitted?process.nextTick(r,i):(i._writableState.errorEmitted=!0,process.nextTick(n,i,e)):process.nextTick(n,i,e):t?(process.nextTick(r,i),t(e)):process.nextTick(r,i)}),this)}function n(e,t){a(e,t),r(e)}function r(e){(!e._writableState||e._writableState.emitClose)&&(!e._readableState||e._readableState.emitClose)&&e.emit("close")}function i(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}function a(e,t){e.emit("error",t)}function o(e,t){var n=e._readableState,r=e._writableState;n&&n.autoDestroy||r&&r.autoDestroy?e.destroy(t):e.emit("error",t)}e.exports={destroy:t,undestroy:i,errorOrDestroy:o}},8610(e,t,n){"use strict";var r=n(94281).q.ERR_STREAM_PREMATURE_CLOSE;function i(e){var t=!1;return function(){if(!t){t=!0;for(var n=arguments.length,r=Array(n),i=0;i0,function(t){e||(e=t),t&&a.forEach(f),o||(a.forEach(f),i(e))})});return n.reduce(d)}e.exports=p},82457(e,t,n){"use strict";var r=n(94281).q.ERR_INVALID_OPT_VALUE;function i(e,t,n){return null!=e.highWaterMark?e.highWaterMark:t?e[n]:null}function a(e,t,n,a){var o=i(t,a,n);if(null!=o){if(!(isFinite(o)&&Math.floor(o)===o)||o<0){var s=a?n:"highWaterMark";throw new r(s,o)}return Math.floor(o)}return e.objectMode?16:16384}e.exports={getHighWaterMark:a}},22503(e,t,n){e.exports=n(17187).EventEmitter},61566(e,t){"use strict";t.__esModule=!0,t.default=void 0;var n=function(e){return"string"==typeof e?e:e?e.displayName||e.name||"Component":void 0};t.default=n},60375(e){"use strict";var t=Object.prototype.hasOwnProperty;function n(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function r(e,r){if(n(e,r))return!0;if("object"!=typeof e||null===e||"object"!=typeof r||null===r)return!1;var i=Object.keys(e),a=Object.keys(r);if(i.length!==a.length)return!1;for(var o=0;og,DE:()=>b,UY:()=>h,qC:()=>m,MT:()=>f});var s="function"==typeof Symbol&&Symbol.observable||"@@observable",u=function(){return Math.random().toString(36).substring(7).split("").join(".")},c={INIT:"@@redux/INIT"+u(),REPLACE:"@@redux/REPLACE"+u(),PROBE_UNKNOWN_ACTION:function(){return"@@redux/PROBE_UNKNOWN_ACTION"+u()}};function l(e){if("object"!=typeof e||null===e)return!1;for(var t=e;null!==Object.getPrototypeOf(t);)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}function f(e,t,n){if("function"==typeof t&&"function"==typeof n||"function"==typeof n&&"function"==typeof arguments[3])throw Error(o(0));if("function"==typeof t&&void 0===n&&(n=t,t=void 0),void 0!==n){if("function"!=typeof n)throw Error(o(1));return n(f)(e,t)}if("function"!=typeof e)throw Error(o(2));var r,i=e,a=t,u=[],d=u,h=!1;function p(){d===u&&(d=u.slice())}function b(){if(h)throw Error(o(3));return a}function m(e){if("function"!=typeof e)throw Error(o(4));if(h)throw Error(o(5));var t=!0;return p(),d.push(e),function(){if(t){if(h)throw Error(o(6));t=!1,p();var n=d.indexOf(e);d.splice(n,1),u=null}}}function g(e){if(!l(e))throw Error(o(7));if(void 0===e.type)throw Error(o(8));if(h)throw Error(o(9));try{h=!0,a=i(a,e)}finally{h=!1}for(var t=u=d,n=0;n]?|>=?|\?=|[-+\/=])(?=\s)/,lookbehind:!0},"string-operator":{pattern:/(\s)&&?(?=\s)/,lookbehind:!0,alias:"keyword"},"token-operator":[{pattern:/(\w)(?:->?|=>|[~|{}])(?=\w)/,lookbehind:!0,alias:"punctuation"},{pattern:/[|{}]/,alias:"punctuation"}],punctuation:/[,.:()]/}}e.exports=t,t.displayName="abap",t.aliases=[]},68313(e){"use strict";function t(e){var t,n;n="(?:ALPHA|BIT|CHAR|CR|CRLF|CTL|DIGIT|DQUOTE|HEXDIG|HTAB|LF|LWSP|OCTET|SP|VCHAR|WSP)",(t=e).languages.abnf={comment:/;.*/,string:{pattern:/(?:%[is])?"[^"\n\r]*"/,greedy:!0,inside:{punctuation:/^%[is]/}},range:{pattern:/%(?:b[01]+-[01]+|d\d+-\d+|x[A-F\d]+-[A-F\d]+)/i,alias:"number"},terminal:{pattern:/%(?:b[01]+(?:\.[01]+)*|d\d+(?:\.\d+)*|x[A-F\d]+(?:\.[A-F\d]+)*)/i,alias:"number"},repetition:{pattern:/(^|[^\w-])(?:\d*\*\d*|\d+)/,lookbehind:!0,alias:"operator"},definition:{pattern:/(^[ \t]*)(?:[a-z][\w-]*|<[^<>\r\n]*>)(?=\s*=)/m,lookbehind:!0,alias:"keyword",inside:{punctuation:/<|>/}},"core-rule":{pattern:RegExp("(?:(^|[^<\\w-])"+n+"|<"+n+">)(?![\\w-])","i"),lookbehind:!0,alias:["rule","constant"],inside:{punctuation:/<|>/}},rule:{pattern:/(^|[^<\w-])[a-z][\w-]*|<[^<>\r\n]*>/i,lookbehind:!0,inside:{punctuation:/<|>/}},operator:/=\/?|\//,punctuation:/[()\[\]]/}}e.exports=t,t.displayName="abnf",t.aliases=[]},5199(e){"use strict";function t(e){e.languages.actionscript=e.languages.extend("javascript",{keyword:/\b(?:as|break|case|catch|class|const|default|delete|do|else|extends|finally|for|function|if|implements|import|in|instanceof|interface|internal|is|native|new|null|package|private|protected|public|return|super|switch|this|throw|try|typeof|use|var|void|while|with|dynamic|each|final|get|include|namespace|override|set|static)\b/,operator:/\+\+|--|(?:[+\-*\/%^]|&&?|\|\|?|<>?>?|[!=]=?)=?|[~?@]/}),e.languages.actionscript["class-name"].alias="function",e.languages.markup&&e.languages.insertBefore("actionscript","string",{xml:{pattern:/(^|[^.])<\/?\w+(?:\s+[^\s>\/=]+=("|')(?:\\[\s\S]|(?!\2)[^\\])*\2)*\s*\/?>/,lookbehind:!0,inside:e.languages.markup}})}e.exports=t,t.displayName="actionscript",t.aliases=[]},89693(e){"use strict";function t(e){e.languages.ada={comment:/--.*/,string:/"(?:""|[^"\r\f\n])*"/i,number:[{pattern:/\b\d(?:_?\d)*#[\dA-F](?:_?[\dA-F])*(?:\.[\dA-F](?:_?[\dA-F])*)?#(?:E[+-]?\d(?:_?\d)*)?/i},{pattern:/\b\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:E[+-]?\d(?:_?\d)*)?\b/i}],"attr-name":/\b'\w+/i,keyword:/\b(?:abort|abs|abstract|accept|access|aliased|all|and|array|at|begin|body|case|constant|declare|delay|delta|digits|do|else|new|return|elsif|end|entry|exception|exit|for|function|generic|goto|if|in|interface|is|limited|loop|mod|not|null|of|others|out|overriding|package|pragma|private|procedure|protected|raise|range|record|rem|renames|requeue|reverse|select|separate|some|subtype|synchronized|tagged|task|terminate|then|type|until|use|when|while|with|xor)\b/i,boolean:/\b(?:true|false)\b/i,operator:/<[=>]?|>=?|=>?|:=|\/=?|\*\*?|[&+-]/,punctuation:/\.\.?|[,;():]/,char:/'.'/,variable:/\b[a-z](?:\w)*\b/i}}e.exports=t,t.displayName="ada",t.aliases=[]},24001(e){"use strict";function t(e){var t;(t=e).languages.agda={comment:/\{-[\s\S]*?(?:-\}|$)|--.*/,string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},punctuation:/[(){}⦃⦄.;@]/,"class-name":{pattern:/((?:data|record) +)\S+/,lookbehind:!0},function:{pattern:/(^[ \t]*)(?!\s)[^:\r\n]+(?=:)/m,lookbehind:!0},operator:{pattern:/(^\s*|\s)(?:[=|:∀→λ\\?_]|->)(?=\s)/,lookbehind:!0},keyword:/\b(?:Set|abstract|constructor|data|eta-equality|field|forall|hiding|import|in|inductive|infix|infixl|infixr|instance|let|macro|module|mutual|no-eta-equality|open|overlap|pattern|postulate|primitive|private|public|quote|quoteContext|quoteGoal|quoteTerm|record|renaming|rewrite|syntax|tactic|unquote|unquoteDecl|unquoteDef|using|variable|where|with)\b/}}e.exports=t,t.displayName="agda",t.aliases=[]},18018(e){"use strict";function t(e){e.languages.al={comment:/\/\/.*|\/\*[\s\S]*?\*\//,string:{pattern:/'(?:''|[^'\r\n])*'(?!')|"(?:""|[^"\r\n])*"(?!")/,greedy:!0},function:{pattern:/(\b(?:event|procedure|trigger)\s+|(?:^|[^.])\.\s*)[a-z_]\w*(?=\s*\()/i,lookbehind:!0},keyword:[/\b(?:array|asserterror|begin|break|case|do|downto|else|end|event|exit|for|foreach|function|if|implements|in|indataset|interface|internal|local|of|procedure|program|protected|repeat|runonclient|securityfiltering|suppressdispose|temporary|then|to|trigger|until|var|while|with|withevents)\b/i,/\b(?:action|actions|addafter|addbefore|addfirst|addlast|area|assembly|chartpart|codeunit|column|controladdin|cuegroup|customizes|dataitem|dataset|dotnet|elements|enum|enumextension|extends|field|fieldattribute|fieldelement|fieldgroup|fieldgroups|fields|filter|fixed|grid|group|key|keys|label|labels|layout|modify|moveafter|movebefore|movefirst|movelast|page|pagecustomization|pageextension|part|profile|query|repeater|report|requestpage|schema|separator|systempart|table|tableelement|tableextension|textattribute|textelement|type|usercontrol|value|xmlport)\b/i],number:/\b(?:0x[\da-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)(?:F|U(?:LL?)?|LL?)?\b/i,boolean:/\b(?:false|true)\b/i,variable:/\b(?:Curr(?:FieldNo|Page|Report)|RequestOptionsPage|x?Rec)\b/,"class-name":/\b(?:automation|biginteger|bigtext|blob|boolean|byte|char|clienttype|code|completiontriggererrorlevel|connectiontype|database|dataclassification|datascope|date|dateformula|datetime|decimal|defaultlayout|dialog|dictionary|dotnetassembly|dotnettypedeclaration|duration|errorinfo|errortype|executioncontext|executionmode|fieldclass|fieldref|fieldtype|file|filterpagebuilder|guid|httpclient|httpcontent|httpheaders|httprequestmessage|httpresponsemessage|instream|integer|joker|jsonarray|jsonobject|jsontoken|jsonvalue|keyref|list|moduledependencyinfo|moduleinfo|none|notification|notificationscope|objecttype|option|outstream|pageresult|record|recordid|recordref|reportformat|securityfilter|sessionsettings|tableconnectiontype|tablefilter|testaction|testfield|testfilterfield|testpage|testpermissions|testrequestpage|text|textbuilder|textconst|textencoding|time|transactionmodel|transactiontype|variant|verbosity|version|view|views|webserviceactioncontext|webserviceactionresultcode|xmlattribute|xmlattributecollection|xmlcdata|xmlcomment|xmldeclaration|xmldocument|xmldocumenttype|xmlelement|xmlnamespacemanager|xmlnametable|xmlnode|xmlnodelist|xmlprocessinginstruction|xmlreadoptions|xmltext|xmlwriteoptions)\b/i,operator:/\.\.|:[=:]|[-+*/]=?|<>|[<>]=?|=|\b(?:and|div|mod|not|or|xor)\b/i,punctuation:/[()\[\]{}:.;,]/}}e.exports=t,t.displayName="al",t.aliases=[]},36363(e){"use strict";function t(e){e.languages.antlr4={comment:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,string:{pattern:/'(?:\\.|[^\\'\r\n])*'/,greedy:!0},"character-class":{pattern:/\[(?:\\.|[^\\\]\r\n])*\]/,greedy:!0,alias:"regex",inside:{range:{pattern:/([^[]|(?:^|[^\\])(?:\\\\)*\\\[)-(?!\])/,lookbehind:!0,alias:"punctuation"},escape:/\\(?:u(?:[a-fA-F\d]{4}|\{[a-fA-F\d]+\})|[pP]\{[=\w-]+\}|[^\r\nupP])/,punctuation:/[\[\]]/}},action:{pattern:/\{(?:[^{}]|\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*\}/,greedy:!0,inside:{content:{pattern:/(\{)[\s\S]+(?=\})/,lookbehind:!0},punctuation:/[{}]/}},command:{pattern:/(->\s*(?!\s))(?:\s*(?:,\s*)?\b[a-z]\w*(?:\s*\([^()\r\n]*\))?)+(?=\s*;)/i,lookbehind:!0,inside:{function:/\b\w+(?=\s*(?:[,(]|$))/,punctuation:/[,()]/}},annotation:{pattern:/@\w+(?:::\w+)*/,alias:"keyword"},label:{pattern:/#[ \t]*\w+/,alias:"punctuation"},keyword:/\b(?:catch|channels|finally|fragment|grammar|import|lexer|locals|mode|options|parser|returns|throws|tokens)\b/,definition:[{pattern:/\b[a-z]\w*(?=\s*:)/,alias:["rule","class-name"]},{pattern:/\b[A-Z]\w*(?=\s*:)/,alias:["token","constant"]}],constant:/\b[A-Z][A-Z_]*\b/,operator:/\.\.|->|[|~]|[*+?]\??/,punctuation:/[;:()=]/},e.languages.g4=e.languages.antlr4}e.exports=t,t.displayName="antlr4",t.aliases=["g4"]},35281(e){"use strict";function t(e){e.languages.apacheconf={comment:/#.*/,"directive-inline":{pattern:/(^[\t ]*)\b(?:AcceptFilter|AcceptPathInfo|AccessFileName|Action|Add(?:Alt|AltByEncoding|AltByType|Charset|DefaultCharset|Description|Encoding|Handler|Icon|IconByEncoding|IconByType|InputFilter|Language|ModuleInfo|OutputFilter|OutputFilterByType|Type)|Alias|AliasMatch|Allow(?:CONNECT|EncodedSlashes|Methods|Override|OverrideList)?|Anonymous(?:_LogEmail|_MustGiveEmail|_NoUserID|_VerifyEmail)?|AsyncRequestWorkerFactor|Auth(?:BasicAuthoritative|BasicFake|BasicProvider|BasicUseDigestAlgorithm|DBDUserPWQuery|DBDUserRealmQuery|DBMGroupFile|DBMType|DBMUserFile|Digest(?:Algorithm|Domain|NonceLifetime|Provider|Qop|ShmemSize)|Form(?:Authoritative|Body|DisableNoStore|FakeBasicAuth|Location|LoginRequiredLocation|LoginSuccessLocation|LogoutLocation|Method|Mimetype|Password|Provider|SitePassphrase|Size|Username)|GroupFile|LDAP(?:AuthorizePrefix|BindAuthoritative|BindDN|BindPassword|CharsetConfig|CompareAsUser|CompareDNOnServer|DereferenceAliases|GroupAttribute|GroupAttributeIsDN|InitialBindAsUser|InitialBindPattern|MaxSubGroupDepth|RemoteUserAttribute|RemoteUserIsDN|SearchAsUser|SubGroupAttribute|SubGroupClass|Url)|Merging|Name|Type|UserFile|nCache(?:Context|Enable|ProvideFor|SOCache|Timeout)|nzFcgiCheckAuthnProvider|nzFcgiDefineProvider|zDBDLoginToReferer|zDBDQuery|zDBDRedirectQuery|zDBMType|zSendForbiddenOnFailure)|BalancerGrowth|BalancerInherit|BalancerMember|BalancerPersist|BrowserMatch|BrowserMatchNoCase|BufferSize|BufferedLogs|CGIDScriptTimeout|CGIMapExtension|Cache(?:DefaultExpire|DetailHeader|DirLength|DirLevels|Disable|Enable|File|Header|IgnoreCacheControl|IgnoreHeaders|IgnoreNoLastMod|IgnoreQueryString|IgnoreURLSessionIdentifiers|KeyBaseURL|LastModifiedFactor|Lock|LockMaxAge|LockPath|MaxExpire|MaxFileSize|MinExpire|MinFileSize|NegotiatedDocs|QuickHandler|ReadSize|ReadTime|Root|Socache(?:MaxSize|MaxTime|MinTime|ReadSize|ReadTime)?|StaleOnError|StoreExpired|StoreNoStore|StorePrivate)|CharsetDefault|CharsetOptions|CharsetSourceEnc|CheckCaseOnly|CheckSpelling|ChrootDir|ContentDigest|CookieDomain|CookieExpires|CookieName|CookieStyle|CookieTracking|CoreDumpDirectory|CustomLog|DBDExptime|DBDInitSQL|DBDKeep|DBDMax|DBDMin|DBDParams|DBDPersist|DBDPrepareSQL|DBDriver|DTracePrivileges|Dav|DavDepthInfinity|DavGenericLockDB|DavLockDB|DavMinTimeout|DefaultIcon|DefaultLanguage|DefaultRuntimeDir|DefaultType|Define|Deflate(?:BufferSize|CompressionLevel|FilterNote|InflateLimitRequestBody|InflateRatio(?:Burst|Limit)|MemLevel|WindowSize)|Deny|DirectoryCheckHandler|DirectoryIndex|DirectoryIndexRedirect|DirectorySlash|DocumentRoot|DumpIOInput|DumpIOOutput|EnableExceptionHook|EnableMMAP|EnableSendfile|Error|ErrorDocument|ErrorLog|ErrorLogFormat|Example|ExpiresActive|ExpiresByType|ExpiresDefault|ExtFilterDefine|ExtFilterOptions|ExtendedStatus|FallbackResource|FileETag|FilterChain|FilterDeclare|FilterProtocol|FilterProvider|FilterTrace|ForceLanguagePriority|ForceType|ForensicLog|GprofDir|GracefulShutdownTimeout|Group|Header|HeaderName|Heartbeat(?:Address|Listen|MaxServers|Storage)|HostnameLookups|ISAPI(?:AppendLogToErrors|AppendLogToQuery|CacheFile|FakeAsync|LogNotSupported|ReadAheadBuffer)|IdentityCheck|IdentityCheckTimeout|ImapBase|ImapDefault|ImapMenu|Include|IncludeOptional|Index(?:HeadInsert|Ignore|IgnoreReset|Options|OrderDefault|StyleSheet)|InputSed|KeepAlive|KeepAliveTimeout|KeptBodySize|LDAP(?:CacheEntries|CacheTTL|ConnectionPoolTTL|ConnectionTimeout|LibraryDebug|OpCacheEntries|OpCacheTTL|ReferralHopLimit|Referrals|Retries|RetryDelay|SharedCacheFile|SharedCacheSize|Timeout|TrustedClientCert|TrustedGlobalCert|TrustedMode|VerifyServerCert)|LanguagePriority|Limit(?:InternalRecursion|Request(?:Body|FieldSize|Fields|Line)|XMLRequestBody)|Listen|ListenBackLog|LoadFile|LoadModule|LogFormat|LogLevel|LogMessage|LuaAuthzProvider|LuaCodeCache|Lua(?:Hook(?:AccessChecker|AuthChecker|CheckUserID|Fixups|InsertFilter|Log|MapToStorage|TranslateName|TypeChecker)|Inherit|InputFilter|MapHandler|OutputFilter|PackageCPath|PackagePath|QuickHandler|Root|Scope)|MMapFile|Max(?:ConnectionsPerChild|KeepAliveRequests|MemFree|RangeOverlaps|RangeReversals|Ranges|RequestWorkers|SpareServers|SpareThreads|Threads)|MergeTrailers|MetaDir|MetaFiles|MetaSuffix|MimeMagicFile|MinSpareServers|MinSpareThreads|ModMimeUsePathInfo|ModemStandard|MultiviewsMatch|Mutex|NWSSLTrustedCerts|NWSSLUpgradeable|NameVirtualHost|NoProxy|Options|Order|OutputSed|PassEnv|PidFile|PrivilegesMode|Protocol|ProtocolEcho|Proxy(?:AddHeaders|BadHeader|Block|Domain|ErrorOverride|ExpressDBMFile|ExpressDBMType|ExpressEnable|FtpDirCharset|FtpEscapeWildcards|FtpListOnWildcard|HTML(?:BufSize|CharsetOut|DocType|Enable|Events|Extended|Fixups|Interp|Links|Meta|StripComments|URLMap)|IOBufferSize|MaxForwards|Pass(?:Inherit|InterpolateEnv|Match|Reverse|ReverseCookieDomain|ReverseCookiePath)?|PreserveHost|ReceiveBufferSize|Remote|RemoteMatch|Requests|SCGIInternalRedirect|SCGISendfile|Set|SourceAddress|Status|Timeout|Via)|RLimitCPU|RLimitMEM|RLimitNPROC|ReadmeName|ReceiveBufferSize|Redirect|RedirectMatch|RedirectPermanent|RedirectTemp|ReflectorHeader|RemoteIP(?:Header|InternalProxy|InternalProxyList|ProxiesHeader|TrustedProxy|TrustedProxyList)|RemoveCharset|RemoveEncoding|RemoveHandler|RemoveInputFilter|RemoveLanguage|RemoveOutputFilter|RemoveType|RequestHeader|RequestReadTimeout|Require|Rewrite(?:Base|Cond|Engine|Map|Options|Rule)|SSIETag|SSIEndTag|SSIErrorMsg|SSILastModified|SSILegacyExprParser|SSIStartTag|SSITimeFormat|SSIUndefinedEcho|SSL(?:CACertificateFile|CACertificatePath|CADNRequestFile|CADNRequestPath|CARevocationCheck|CARevocationFile|CARevocationPath|CertificateChainFile|CertificateFile|CertificateKeyFile|CipherSuite|Compression|CryptoDevice|Engine|FIPS|HonorCipherOrder|InsecureRenegotiation|OCSP(?:DefaultResponder|Enable|OverrideResponder|ResponderTimeout|ResponseMaxAge|ResponseTimeSkew|UseRequestNonce)|OpenSSLConfCmd|Options|PassPhraseDialog|Protocol|Proxy(?:CACertificateFile|CACertificatePath|CARevocation(?:Check|File|Path)|CheckPeer(?:CN|Expire|Name)|CipherSuite|Engine|MachineCertificate(?:ChainFile|File|Path)|Protocol|Verify|VerifyDepth)|RandomSeed|RenegBufferSize|Require|RequireSSL|SRPUnknownUserSeed|SRPVerifierFile|Session(?:Cache|CacheTimeout|TicketKeyFile|Tickets)|Stapling(?:Cache|ErrorCacheTimeout|FakeTryLater|ForceURL|ResponderTimeout|ResponseMaxAge|ResponseTimeSkew|ReturnResponderErrors|StandardCacheTimeout)|StrictSNIVHostCheck|UseStapling|UserName|VerifyClient|VerifyDepth)|Satisfy|ScoreBoardFile|Script(?:Alias|AliasMatch|InterpreterSource|Log|LogBuffer|LogLength|Sock)?|SecureListen|SeeRequestTail|SendBufferSize|Server(?:Admin|Alias|Limit|Name|Path|Root|Signature|Tokens)|Session(?:Cookie(?:Name|Name2|Remove)|Crypto(?:Cipher|Driver|Passphrase|PassphraseFile)|DBD(?:CookieName|CookieName2|CookieRemove|DeleteLabel|InsertLabel|PerUser|SelectLabel|UpdateLabel)|Env|Exclude|Header|Include|MaxAge)?|SetEnv|SetEnvIf|SetEnvIfExpr|SetEnvIfNoCase|SetHandler|SetInputFilter|SetOutputFilter|StartServers|StartThreads|Substitute|Suexec|SuexecUserGroup|ThreadLimit|ThreadStackSize|ThreadsPerChild|TimeOut|TraceEnable|TransferLog|TypesConfig|UnDefine|UndefMacro|UnsetEnv|Use|UseCanonicalName|UseCanonicalPhysicalPort|User|UserDir|VHostCGIMode|VHostCGIPrivs|VHostGroup|VHostPrivs|VHostSecure|VHostUser|Virtual(?:DocumentRoot|ScriptAlias)(?:IP)?|WatchdogInterval|XBitHack|xml2EncAlias|xml2EncDefault|xml2StartParse)\b/im,lookbehind:!0,alias:"property"},"directive-block":{pattern:/<\/?\b(?:Auth[nz]ProviderAlias|Directory|DirectoryMatch|Else|ElseIf|Files|FilesMatch|If|IfDefine|IfModule|IfVersion|Limit|LimitExcept|Location|LocationMatch|Macro|Proxy|Require(?:All|Any|None)|VirtualHost)\b.*>/i,inside:{"directive-block":{pattern:/^<\/?\w+/,inside:{punctuation:/^<\/?/},alias:"tag"},"directive-block-parameter":{pattern:/.*[^>]/,inside:{punctuation:/:/,string:{pattern:/("|').*\1/,inside:{variable:/[$%]\{?(?:\w\.?[-+:]?)+\}?/}}},alias:"attr-value"},punctuation:/>/},alias:"tag"},"directive-flags":{pattern:/\[(?:[\w=],?)+\]/,alias:"keyword"},string:{pattern:/("|').*\1/,inside:{variable:/[$%]\{?(?:\w\.?[-+:]?)+\}?/}},variable:/[$%]\{?(?:\w\.?[-+:]?)+\}?/,regex:/\^?.*\$|\^.*\$?/}}e.exports=t,t.displayName="apacheconf",t.aliases=[]},10433(e,t,n){"use strict";var r=n(11114);function i(e){e.register(r),function(e){var t=/\b(?:abstract|activate|and|any|array|as|asc|autonomous|begin|bigdecimal|blob|boolean|break|bulk|by|byte|case|cast|catch|char|class|collect|commit|const|continue|currency|date|datetime|decimal|default|delete|desc|do|double|else|end|enum|exception|exit|export|extends|final|finally|float|for|from|global|goto|group|having|hint|if|implements|import|in|inner|insert|instanceof|int|integer|interface|into|join|like|limit|list|long|loop|map|merge|new|not|null|nulls|number|object|of|on|or|outer|override|package|parallel|pragma|private|protected|public|retrieve|return|rollback|select|set|short|sObject|sort|static|string|super|switch|synchronized|system|testmethod|then|this|throw|time|transaction|transient|trigger|try|undelete|update|upsert|using|virtual|void|webservice|when|where|while|get(?=\s*[{};])|(?:after|before)(?=\s+[a-z])|(?:inherited|with|without)\s+sharing)\b/i,n=/\b(?:(?=[a-z_]\w*\s*[<\[])|(?!))[A-Z_]\w*(?:\s*\.\s*[A-Z_]\w*)*\b(?:\s*(?:\[\s*\]|<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>))*/.source.replace(//g,function(){return t.source});function r(e){return RegExp(e.replace(//g,function(){return n}),"i")}var i={keyword:t,punctuation:/[()\[\]{};,:.<>]/};e.languages.apex={comment:e.languages.clike.comment,string:e.languages.clike.string,sql:{pattern:/((?:[=,({:]|\breturn)\s*)\[[^\[\]]*\]/i,lookbehind:!0,greedy:!0,alias:"language-sql",inside:e.languages.sql},annotation:{pattern:/@\w+\b/,alias:"punctuation"},"class-name":[{pattern:r(/(\b(?:class|enum|extends|implements|instanceof|interface|new|trigger\s+\w+\s+on)\s+)/.source),lookbehind:!0,inside:i},{pattern:r(/(\(\s*)(?=\s*\)\s*[\w(])/.source),lookbehind:!0,inside:i},{pattern:r(/(?=\s*\w+\s*[;=,(){:])/.source),inside:i}],trigger:{pattern:/(\btrigger\s+)\w+\b/i,lookbehind:!0,alias:"class-name"},keyword:t,function:/\b[a-z_]\w*(?=\s*\()/i,boolean:/\b(?:false|true)\b/i,number:/(?:\B\.\d+|\b\d+(?:\.\d+|L)?)\b/i,operator:/[!=](?:==?)?|\?\.?|&&|\|\||--|\+\+|[-+*/^&|]=?|:|<{1,3}=?/,punctuation:/[()\[\]{};,.]/}}(e)}e.exports=i,i.displayName="apex",i.aliases=[]},84039(e){"use strict";function t(e){e.languages.apl={comment:/(?:⍝|#[! ]).*$/m,string:{pattern:/'(?:[^'\r\n]|'')*'/,greedy:!0},number:/¯?(?:\d*\.?\b\d+(?:e[+¯]?\d+)?|¯|∞)(?:j¯?(?:(?:\d+(?:\.\d+)?|\.\d+)(?:e[+¯]?\d+)?|¯|∞))?/i,statement:/:[A-Z][a-z][A-Za-z]*\b/,"system-function":{pattern:/⎕[A-Z]+/i,alias:"function"},constant:/[⍬⌾#⎕⍞]/,function:/[-+×÷⌈⌊∣|⍳⍸?*⍟○!⌹<≤=>≥≠≡≢∊⍷∪∩~∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⊆⊇⌷⍋⍒⊤⊥⍕⍎⊣⊢⍁⍂≈⍯↗¤→]/,"monadic-operator":{pattern:/[\\\/⌿⍀¨⍨⌶&∥]/,alias:"operator"},"dyadic-operator":{pattern:/[.⍣⍠⍤∘⌸@⌺⍥]/,alias:"operator"},assignment:{pattern:/←/,alias:"keyword"},punctuation:/[\[;\]()◇⋄]/,dfn:{pattern:/[{}⍺⍵⍶⍹∇⍫:]/,alias:"builtin"}}}e.exports=t,t.displayName="apl",t.aliases=[]},71336(e){"use strict";function t(e){e.languages.applescript={comment:[/\(\*(?:\(\*(?:[^*]|\*(?!\)))*\*\)|(?!\(\*)[\s\S])*?\*\)/,/--.+/,/#.+/],string:/"(?:\\.|[^"\\\r\n])*"/,number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e-?\d+)?\b/i,operator:[/[&=≠≤≥*+\-\/÷^]|[<>]=?/,/\b(?:(?:start|begin|end)s? with|(?:(?:does not|doesn't) contain|contains?)|(?:is|isn't|is not) (?:in|contained by)|(?:(?:is|isn't|is not) )?(?:greater|less) than(?: or equal)?(?: to)?|(?:(?:does not|doesn't) come|comes) (?:before|after)|(?:is|isn't|is not) equal(?: to)?|(?:(?:does not|doesn't) equal|equals|equal to|isn't|is not)|(?:a )?(?:ref(?: to)?|reference to)|(?:and|or|div|mod|as|not))\b/],keyword:/\b(?:about|above|after|against|apart from|around|aside from|at|back|before|beginning|behind|below|beneath|beside|between|but|by|considering|continue|copy|does|eighth|else|end|equal|error|every|exit|false|fifth|first|for|fourth|from|front|get|given|global|if|ignoring|in|instead of|into|is|it|its|last|local|me|middle|my|ninth|of|on|onto|out of|over|prop|property|put|repeat|return|returning|second|set|seventh|since|sixth|some|tell|tenth|that|the|then|third|through|thru|timeout|times|to|transaction|true|try|until|where|while|whose|with|without)\b/,class:{pattern:/\b(?:alias|application|boolean|class|constant|date|file|integer|list|number|POSIX file|real|record|reference|RGB color|script|text|centimetres|centimeters|feet|inches|kilometres|kilometers|metres|meters|miles|yards|square feet|square kilometres|square kilometers|square metres|square meters|square miles|square yards|cubic centimetres|cubic centimeters|cubic feet|cubic inches|cubic metres|cubic meters|cubic yards|gallons|litres|liters|quarts|grams|kilograms|ounces|pounds|degrees Celsius|degrees Fahrenheit|degrees Kelvin)\b/,alias:"builtin"},punctuation:/[{}():,¬«»《》]/}}e.exports=t,t.displayName="applescript",t.aliases=[]},4481(e){"use strict";function t(e){e.languages.aql={comment:/\/\/.*|\/\*[\s\S]*?\*\//,property:{pattern:/([{,]\s*)(?:(?!\d)\w+|(["'´`])(?:(?!\2)[^\\\r\n]|\\.)*\2)(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(["'´`])(?:(?!\1)[^\\\r\n]|\\.)*\1/,greedy:!0},variable:/@@?\w+/,keyword:[{pattern:/(\bWITH\s+)COUNT(?=\s+INTO\b)/i,lookbehind:!0},/\b(?:AGGREGATE|ALL|AND|ANY|ASC|COLLECT|DESC|DISTINCT|FILTER|FOR|GRAPH|IN|INBOUND|INSERT|INTO|K_PATHS|K_SHORTEST_PATHS|LET|LIKE|LIMIT|NONE|NOT|NULL|OR|OUTBOUND|REMOVE|REPLACE|RETURN|SHORTEST_PATH|SORT|UPDATE|UPSERT|WINDOW|WITH)\b/i,{pattern:/(^|[^\w.[])(?:KEEP|PRUNE|SEARCH|TO)\b/i,lookbehind:!0},{pattern:/(^|[^\w.[])(?:CURRENT|NEW|OLD)\b/,lookbehind:!0},{pattern:/\bOPTIONS(?=\s*\{)/i}],function:/\b(?!\d)\w+(?=\s*\()/,boolean:/\b(?:true|false)\b/i,range:{pattern:/\.\./,alias:"operator"},number:[/\b0b[01]+/i,/\b0x[0-9a-f]+/i,/(?:\B\.\d+|\b(?:0|[1-9]\d*)(?:\.\d+)?)(?:e[+-]?\d+)?/i],operator:/\*{2,}|[=!]~|[!=<>]=?|&&|\|\||[-+*/%]/,punctuation:/::|[?.:,;()[\]{}]/}}e.exports=t,t.displayName="aql",t.aliases=[]},2159(e,t,n){"use strict";var r=n(80096);function i(e){e.register(r),e.languages.arduino=e.languages.extend("cpp",{constant:/\b(?:DIGITAL_MESSAGE|FIRMATA_STRING|ANALOG_MESSAGE|REPORT_DIGITAL|REPORT_ANALOG|INPUT_PULLUP|SET_PIN_MODE|INTERNAL2V56|SYSTEM_RESET|LED_BUILTIN|INTERNAL1V1|SYSEX_START|INTERNAL|EXTERNAL|DEFAULT|OUTPUT|INPUT|HIGH|LOW)\b/,keyword:/\b(?:setup|if|else|while|do|for|return|in|instanceof|default|function|loop|goto|switch|case|new|try|throw|catch|finally|null|break|continue|boolean|bool|void|byte|word|string|String|array|int|long|integer|double)\b/,builtin:/\b(?:KeyboardController|MouseController|SoftwareSerial|EthernetServer|EthernetClient|LiquidCrystal|LiquidCrystal_I2C|RobotControl|GSMVoiceCall|EthernetUDP|EsploraTFT|HttpClient|RobotMotor|WiFiClient|GSMScanner|FileSystem|Scheduler|GSMServer|YunClient|YunServer|IPAddress|GSMClient|GSMModem|Keyboard|Ethernet|Console|GSMBand|Esplora|Stepper|Process|WiFiUDP|GSM_SMS|Mailbox|USBHost|Firmata|PImage|Client|Server|GSMPIN|FileIO|Bridge|Serial|EEPROM|Stream|Mouse|Audio|Servo|File|Task|GPRS|WiFi|Wire|TFT|GSM|SPI|SD|runShellCommandAsynchronously|analogWriteResolution|retrieveCallingNumber|printFirmwareVersion|analogReadResolution|sendDigitalPortPair|noListenOnLocalhost|readJoystickButton|setFirmwareVersion|readJoystickSwitch|scrollDisplayRight|getVoiceCallStatus|scrollDisplayLeft|writeMicroseconds|delayMicroseconds|beginTransmission|getSignalStrength|runAsynchronously|getAsynchronously|listenOnLocalhost|getCurrentCarrier|readAccelerometer|messageAvailable|sendDigitalPorts|lineFollowConfig|countryNameWrite|runShellCommand|readStringUntil|rewindDirectory|readTemperature|setClockDivider|readLightSensor|endTransmission|analogReference|detachInterrupt|countryNameRead|attachInterrupt|encryptionType|readBytesUntil|robotNameWrite|readMicrophone|robotNameRead|cityNameWrite|userNameWrite|readJoystickY|readJoystickX|mouseReleased|openNextFile|scanNetworks|noInterrupts|digitalWrite|beginSpeaker|mousePressed|isActionDone|mouseDragged|displayLogos|noAutoscroll|addParameter|remoteNumber|getModifiers|keyboardRead|userNameRead|waitContinue|processInput|parseCommand|printVersion|readNetworks|writeMessage|blinkVersion|cityNameRead|readMessage|setDataMode|parsePacket|isListening|setBitOrder|beginPacket|isDirectory|motorsWrite|drawCompass|digitalRead|clearScreen|serialEvent|rightToLeft|setTextSize|leftToRight|requestFrom|keyReleased|compassRead|analogWrite|interrupts|WiFiServer|disconnect|playMelody|parseFloat|autoscroll|getPINUsed|setPINUsed|setTimeout|sendAnalog|readSlider|analogRead|beginWrite|createChar|motorsStop|keyPressed|tempoWrite|readButton|subnetMask|debugPrint|macAddress|writeGreen|randomSeed|attachGPRS|readString|sendString|remotePort|releaseAll|mouseMoved|background|getXChange|getYChange|answerCall|getResult|voiceCall|endPacket|constrain|getSocket|writeJSON|getButton|available|connected|findUntil|readBytes|exitValue|readGreen|writeBlue|startLoop|isPressed|sendSysex|pauseMode|gatewayIP|setCursor|getOemKey|tuneWrite|noDisplay|loadImage|switchPIN|onRequest|onReceive|changePIN|playFile|noBuffer|parseInt|overflow|checkPIN|knobRead|beginTFT|bitClear|updateIR|bitWrite|position|writeRGB|highByte|writeRed|setSpeed|readBlue|noStroke|remoteIP|transfer|shutdown|hangCall|beginSMS|endWrite|attached|maintain|noCursor|checkReg|checkPUK|shiftOut|isValid|shiftIn|pulseIn|connect|println|localIP|pinMode|getIMEI|display|noBlink|process|getBand|running|beginSD|drawBMP|lowByte|setBand|release|bitRead|prepare|pointTo|readRed|setMode|noFill|remove|listen|stroke|detach|attach|noTone|exists|buffer|height|bitSet|circle|config|cursor|random|IRread|setDNS|endSMS|getKey|micros|millis|begin|print|write|ready|flush|width|isPIN|blink|clear|press|mkdir|rmdir|close|point|yield|image|BSSID|click|delay|read|text|move|peek|beep|rect|line|open|seek|fill|size|turn|stop|home|find|step|tone|sqrt|RSSI|SSID|end|bit|tan|cos|sin|pow|map|abs|max|min|get|run|put)\b/})}e.exports=i,i.displayName="arduino",i.aliases=[]},60274(e){"use strict";function t(e){e.languages.arff={comment:/%.*/,string:{pattern:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},keyword:/@(?:attribute|data|end|relation)\b/i,number:/\b\d+(?:\.\d+)?\b/,punctuation:/[{},]/}}e.exports=t,t.displayName="arff",t.aliases=[]},18738(e){"use strict";function t(e){!function(e){var t={pattern:/(^[ \t]*)\[(?!\[)(?:(["'$`])(?:(?!\2)[^\\]|\\.)*\2|\[(?:[^\[\]\\]|\\.)*\]|[^\[\]\\"'$`]|\\.)*\]/m,lookbehind:!0,inside:{quoted:{pattern:/([$`])(?:(?!\1)[^\\]|\\.)*\1/,inside:{punctuation:/^[$`]|[$`]$/}},interpreted:{pattern:/'(?:[^'\\]|\\.)*'/,inside:{punctuation:/^'|'$/}},string:/"(?:[^"\\]|\\.)*"/,variable:/\w+(?==)/,punctuation:/^\[|\]$|,/,operator:/=/,"attr-value":/(?!^\s+$).+/}},n=e.languages.asciidoc={"comment-block":{pattern:/^(\/{4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1/m,alias:"comment"},table:{pattern:/^\|={3,}(?:(?:\r?\n|\r(?!\n)).*)*?(?:\r?\n|\r)\|={3,}$/m,inside:{specifiers:{pattern:/(?!\|)(?:(?:(?:\d+(?:\.\d+)?|\.\d+)[+*])?(?:[<^>](?:\.[<^>])?|\.[<^>])?[a-z]*)(?=\|)/,alias:"attr-value"},punctuation:{pattern:/(^|[^\\])[|!]=*/,lookbehind:!0}}},"passthrough-block":{pattern:/^(\+{4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1$/m,inside:{punctuation:/^\++|\++$/}},"literal-block":{pattern:/^(-{4,}|\.{4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1$/m,inside:{punctuation:/^(?:-+|\.+)|(?:-+|\.+)$/}},"other-block":{pattern:/^(--|\*{4,}|_{4,}|={4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1$/m,inside:{punctuation:/^(?:-+|\*+|_+|=+)|(?:-+|\*+|_+|=+)$/}},"list-punctuation":{pattern:/(^[ \t]*)(?:-|\*{1,5}|\.{1,5}|(?:[a-z]|\d+)\.|[xvi]+\))(?= )/im,lookbehind:!0,alias:"punctuation"},"list-label":{pattern:/(^[ \t]*)[a-z\d].+(?::{2,4}|;;)(?=\s)/im,lookbehind:!0,alias:"symbol"},"indented-block":{pattern:/((\r?\n|\r)\2)([ \t]+)\S.*(?:(?:\r?\n|\r)\3.+)*(?=\2{2}|$)/,lookbehind:!0},comment:/^\/\/.*/m,title:{pattern:/^.+(?:\r?\n|\r)(?:={3,}|-{3,}|~{3,}|\^{3,}|\+{3,})$|^={1,5} .+|^\.(?![\s.]).*/m,alias:"important",inside:{punctuation:/^(?:\.|=+)|(?:=+|-+|~+|\^+|\++)$/}},"attribute-entry":{pattern:/^:[^:\r\n]+:(?: .*?(?: \+(?:\r?\n|\r).*?)*)?$/m,alias:"tag"},attributes:t,hr:{pattern:/^'{3,}$/m,alias:"punctuation"},"page-break":{pattern:/^<{3,}$/m,alias:"punctuation"},admonition:{pattern:/^(?:TIP|NOTE|IMPORTANT|WARNING|CAUTION):/m,alias:"keyword"},callout:[{pattern:/(^[ \t]*)/m,lookbehind:!0,alias:"symbol"},{pattern:/<\d+>/,alias:"symbol"}],macro:{pattern:/\b[a-z\d][a-z\d-]*::?(?:[^\s\[\]]*\[(?:[^\]\\"']|(["'])(?:(?!\1)[^\\]|\\.)*\1|\\.)*\])/,inside:{function:/^[a-z\d-]+(?=:)/,punctuation:/^::?/,attributes:{pattern:/(?:\[(?:[^\]\\"']|(["'])(?:(?!\1)[^\\]|\\.)*\1|\\.)*\])/,inside:t.inside}}},inline:{pattern:/(^|[^\\])(?:(?:\B\[(?:[^\]\\"']|(["'])(?:(?!\2)[^\\]|\\.)*\2|\\.)*\])?(?:\b_(?!\s)(?: _|[^_\\\r\n]|\\.)+(?:(?:\r?\n|\r)(?: _|[^_\\\r\n]|\\.)+)*_\b|\B``(?!\s).+?(?:(?:\r?\n|\r).+?)*''\B|\B`(?!\s)(?:[^`'\s]|\s+\S)+['`]\B|\B(['*+#])(?!\s)(?: \3|(?!\3)[^\\\r\n]|\\.)+(?:(?:\r?\n|\r)(?: \3|(?!\3)[^\\\r\n]|\\.)+)*\3\B)|(?:\[(?:[^\]\\"']|(["'])(?:(?!\4)[^\\]|\\.)*\4|\\.)*\])?(?:(__|\*\*|\+\+\+?|##|\$\$|[~^]).+?(?:(?:\r?\n|\r).+?)*\5|\{[^}\r\n]+\}|\[\[\[?.+?(?:(?:\r?\n|\r).+?)*\]?\]\]|<<.+?(?:(?:\r?\n|\r).+?)*>>|\(\(\(?.+?(?:(?:\r?\n|\r).+?)*\)?\)\)))/m,lookbehind:!0,inside:{attributes:t,url:{pattern:/^(?:\[\[\[?.+?\]?\]\]|<<.+?>>)$/,inside:{punctuation:/^(?:\[\[\[?|<<)|(?:\]\]\]?|>>)$/}},"attribute-ref":{pattern:/^\{.+\}$/,inside:{variable:{pattern:/(^\{)[a-z\d,+_-]+/,lookbehind:!0},operator:/^[=?!#%@$]|!(?=[:}])/,punctuation:/^\{|\}$|::?/}},italic:{pattern:/^(['_])[\s\S]+\1$/,inside:{punctuation:/^(?:''?|__?)|(?:''?|__?)$/}},bold:{pattern:/^\*[\s\S]+\*$/,inside:{punctuation:/^\*\*?|\*\*?$/}},punctuation:/^(?:``?|\+{1,3}|##?|\$\$|[~^]|\(\(\(?)|(?:''?|\+{1,3}|##?|\$\$|[~^`]|\)?\)\))$/}},replacement:{pattern:/\((?:C|TM|R)\)/,alias:"builtin"},entity:/&#?[\da-z]{1,8};/i,"line-continuation":{pattern:/(^| )\+$/m,lookbehind:!0,alias:"punctuation"}};function r(e){e=e.split(" ");for(var t={},r=0,i=e.length;r/i,alias:"tag",inside:{"page-directive":{pattern:/<%\s*@\s*(?:Assembly|Control|Implements|Import|Master(?:Type)?|OutputCache|Page|PreviousPageType|Reference|Register)?|%>/i,alias:"tag"},rest:e.languages.markup.tag.inside}},directive:{pattern:/<%.*%>/i,alias:"tag",inside:{directive:{pattern:/<%\s*?[$=%#:]{0,2}|%>/i,alias:"tag"},rest:e.languages.csharp}}}),e.languages.aspnet.tag.pattern=/<(?!%)\/?[^\s>\/]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,e.languages.insertBefore("inside","punctuation",{directive:e.languages.aspnet.directive},e.languages.aspnet.tag.inside["attr-value"]),e.languages.insertBefore("aspnet","comment",{"asp-comment":{pattern:/<%--[\s\S]*?--%>/,alias:["asp","comment"]}}),e.languages.insertBefore("aspnet",e.languages.javascript?"script":"tag",{"asp-script":{pattern:/(]*>)[\s\S]*?(?=<\/script>)/i,lookbehind:!0,alias:["asp","script"],inside:e.languages.csharp||{}}})}e.exports=i,i.displayName="aspnet",i.aliases=[]},6681(e){"use strict";function t(e){e.languages.autohotkey={comment:[{pattern:/(^|\s);.*/,lookbehind:!0},{pattern:/(^[\t ]*)\/\*(?:[\r\n](?![ \t]*\*\/)|[^\r\n])*(?:[\r\n][ \t]*\*\/)?/m,lookbehind:!0,greedy:!0}],tag:{pattern:/^([ \t]*)[^\s,`":]+(?=:[ \t]*$)/m,lookbehind:!0},string:/"(?:[^"\n\r]|"")*"/m,variable:/%\w+%/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/\?|\/\/?=?|:=|\|[=|]?|&[=&]?|\+[=+]?|-[=-]?|\*[=*]?|<(?:<=?|>|=)?|>>?=?|[.^!=~]=?|\b(?:AND|NOT|OR)\b/,boolean:/\b(?:true|false)\b/,selector:/\b(?:AutoTrim|BlockInput|Break|Click|ClipWait|Continue|Control|ControlClick|ControlFocus|ControlGet|ControlGetFocus|ControlGetPos|ControlGetText|ControlMove|ControlSend|ControlSendRaw|ControlSetText|CoordMode|Critical|DetectHiddenText|DetectHiddenWindows|Drive|DriveGet|DriveSpaceFree|EnvAdd|EnvDiv|EnvGet|EnvMult|EnvSet|EnvSub|EnvUpdate|Exit|ExitApp|FileAppend|FileCopy|FileCopyDir|FileCreateDir|FileCreateShortcut|FileDelete|FileEncoding|FileGetAttrib|FileGetShortcut|FileGetSize|FileGetTime|FileGetVersion|FileInstall|FileMove|FileMoveDir|FileRead|FileReadLine|FileRecycle|FileRecycleEmpty|FileRemoveDir|FileSelectFile|FileSelectFolder|FileSetAttrib|FileSetTime|FormatTime|GetKeyState|Gosub|Goto|GroupActivate|GroupAdd|GroupClose|GroupDeactivate|Gui|GuiControl|GuiControlGet|Hotkey|ImageSearch|IniDelete|IniRead|IniWrite|Input|InputBox|KeyWait|ListHotkeys|ListLines|ListVars|Loop|Menu|MouseClick|MouseClickDrag|MouseGetPos|MouseMove|MsgBox|OnExit|OutputDebug|Pause|PixelGetColor|PixelSearch|PostMessage|Process|Progress|Random|RegDelete|RegRead|RegWrite|Reload|Repeat|Return|Run|RunAs|RunWait|Send|SendEvent|SendInput|SendMessage|SendMode|SendPlay|SendRaw|SetBatchLines|SetCapslockState|SetControlDelay|SetDefaultMouseSpeed|SetEnv|SetFormat|SetKeyDelay|SetMouseDelay|SetNumlockState|SetRegView|SetScrollLockState|SetStoreCapslockMode|SetTimer|SetTitleMatchMode|SetWinDelay|SetWorkingDir|Shutdown|Sleep|Sort|SoundBeep|SoundGet|SoundGetWaveVolume|SoundPlay|SoundSet|SoundSetWaveVolume|SplashImage|SplashTextOff|SplashTextOn|SplitPath|StatusBarGetText|StatusBarWait|StringCaseSense|StringGetPos|StringLeft|StringLen|StringLower|StringMid|StringReplace|StringRight|StringSplit|StringTrimLeft|StringTrimRight|StringUpper|Suspend|SysGet|Thread|ToolTip|Transform|TrayTip|URLDownloadToFile|WinActivate|WinActivateBottom|WinClose|WinGet|WinGetActiveStats|WinGetActiveTitle|WinGetClass|WinGetPos|WinGetText|WinGetTitle|WinHide|WinKill|WinMaximize|WinMenuSelectItem|WinMinimize|WinMinimizeAll|WinMinimizeAllUndo|WinMove|WinRestore|WinSet|WinSetTitle|WinShow|WinWait|WinWaitActive|WinWaitClose|WinWaitNotActive)\b/i,constant:/\b(?:a_ahkpath|a_ahkversion|a_appdata|a_appdatacommon|a_autotrim|a_batchlines|a_caretx|a_carety|a_computername|a_controldelay|a_cursor|a_dd|a_ddd|a_dddd|a_defaultmousespeed|a_desktop|a_desktopcommon|a_detecthiddentext|a_detecthiddenwindows|a_endchar|a_eventinfo|a_exitreason|a_fileencoding|a_formatfloat|a_formatinteger|a_gui|a_guievent|a_guicontrol|a_guicontrolevent|a_guiheight|a_guiwidth|a_guix|a_guiy|a_hour|a_iconfile|a_iconhidden|a_iconnumber|a_icontip|a_index|a_ipaddress1|a_ipaddress2|a_ipaddress3|a_ipaddress4|a_is64bitos|a_isadmin|a_iscompiled|a_iscritical|a_ispaused|a_issuspended|a_isunicode|a_keydelay|a_language|a_lasterror|a_linefile|a_linenumber|a_loopfield|a_loopfileattrib|a_loopfiledir|a_loopfileext|a_loopfilefullpath|a_loopfilelongpath|a_loopfilename|a_loopfileshortname|a_loopfileshortpath|a_loopfilesize|a_loopfilesizekb|a_loopfilesizemb|a_loopfiletimeaccessed|a_loopfiletimecreated|a_loopfiletimemodified|a_loopreadline|a_loopregkey|a_loopregname|a_loopregsubkey|a_loopregtimemodified|a_loopregtype|a_mday|a_min|a_mm|a_mmm|a_mmmm|a_mon|a_mousedelay|a_msec|a_mydocuments|a_now|a_nowutc|a_numbatchlines|a_ostype|a_osversion|a_priorhotkey|a_priorkey|programfiles|a_programfiles|a_programs|a_programscommon|a_ptrsize|a_regview|a_screendpi|a_screenheight|a_screenwidth|a_scriptdir|a_scriptfullpath|a_scripthwnd|a_scriptname|a_sec|a_space|a_startmenu|a_startmenucommon|a_startup|a_startupcommon|a_stringcasesense|a_tab|a_temp|a_thisfunc|a_thishotkey|a_thislabel|a_thismenu|a_thismenuitem|a_thismenuitempos|a_tickcount|a_timeidle|a_timeidlephysical|a_timesincepriorhotkey|a_timesincethishotkey|a_titlematchmode|a_titlematchmodespeed|a_username|a_wday|a_windelay|a_windir|a_workingdir|a_yday|a_year|a_yweek|a_yyyy|clipboard|clipboardall|comspec|errorlevel)\b/i,builtin:/\b(?:abs|acos|asc|asin|atan|ceil|chr|class|comobjactive|comobjarray|comobjconnect|comobjcreate|comobjerror|comobjflags|comobjget|comobjquery|comobjtype|comobjvalue|cos|dllcall|exp|fileexist|Fileopen|floor|format|il_add|il_create|il_destroy|instr|substr|isfunc|islabel|IsObject|ln|log|lv_add|lv_delete|lv_deletecol|lv_getcount|lv_getnext|lv_gettext|lv_insert|lv_insertcol|lv_modify|lv_modifycol|lv_setimagelist|ltrim|rtrim|mod|onmessage|numget|numput|registercallback|regexmatch|regexreplace|round|sin|tan|sqrt|strlen|strreplace|sb_seticon|sb_setparts|sb_settext|strsplit|tv_add|tv_delete|tv_getchild|tv_getcount|tv_getnext|tv_get|tv_getparent|tv_getprev|tv_getselection|tv_gettext|tv_modify|varsetcapacity|winactive|winexist|__New|__Call|__Get|__Set)\b/i,symbol:/\b(?:alt|altdown|altup|appskey|backspace|browser_back|browser_favorites|browser_forward|browser_home|browser_refresh|browser_search|browser_stop|bs|capslock|ctrl|ctrlbreak|ctrldown|ctrlup|del|delete|down|end|enter|esc|escape|f1|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f2|f20|f21|f22|f23|f24|f3|f4|f5|f6|f7|f8|f9|home|ins|insert|joy1|joy10|joy11|joy12|joy13|joy14|joy15|joy16|joy17|joy18|joy19|joy2|joy20|joy21|joy22|joy23|joy24|joy25|joy26|joy27|joy28|joy29|joy3|joy30|joy31|joy32|joy4|joy5|joy6|joy7|joy8|joy9|joyaxes|joybuttons|joyinfo|joyname|joypov|joyr|joyu|joyv|joyx|joyy|joyz|lalt|launch_app1|launch_app2|launch_mail|launch_media|lbutton|lcontrol|lctrl|left|lshift|lwin|lwindown|lwinup|mbutton|media_next|media_play_pause|media_prev|media_stop|numlock|numpad0|numpad1|numpad2|numpad3|numpad4|numpad5|numpad6|numpad7|numpad8|numpad9|numpadadd|numpadclear|numpaddel|numpaddiv|numpaddot|numpaddown|numpadend|numpadenter|numpadhome|numpadins|numpadleft|numpadmult|numpadpgdn|numpadpgup|numpadright|numpadsub|numpadup|pgdn|pgup|printscreen|ralt|rbutton|rcontrol|rctrl|right|rshift|rwin|rwindown|rwinup|scrolllock|shift|shiftdown|shiftup|space|tab|up|volume_down|volume_mute|volume_up|wheeldown|wheelleft|wheelright|wheelup|xbutton1|xbutton2)\b/i,important:/#\b(?:AllowSameLineComments|ClipboardTimeout|CommentFlag|DerefChar|ErrorStdOut|EscapeChar|HotkeyInterval|HotkeyModifierTimeout|Hotstring|If|IfTimeout|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Include|IncludeAgain|InputLevel|InstallKeybdHook|InstallMouseHook|KeyHistory|MaxHotkeysPerInterval|MaxMem|MaxThreads|MaxThreadsBuffer|MaxThreadsPerHotkey|MenuMaskKey|NoEnv|NoTrayIcon|Persistent|SingleInstance|UseHook|Warn|WinActivateForce)\b/i,keyword:/\b(?:Abort|AboveNormal|Add|ahk_class|ahk_exe|ahk_group|ahk_id|ahk_pid|All|Alnum|Alpha|AltSubmit|AltTab|AltTabAndMenu|AltTabMenu|AltTabMenuDismiss|AlwaysOnTop|AutoSize|Background|BackgroundTrans|BelowNormal|between|BitAnd|BitNot|BitOr|BitShiftLeft|BitShiftRight|BitXOr|Bold|Border|Button|ByRef|Checkbox|Checked|CheckedGray|Choose|ChooseString|Close|Color|ComboBox|Contains|ControlList|Count|Date|DateTime|Days|DDL|Default|DeleteAll|Delimiter|Deref|Destroy|Digit|Disable|Disabled|DropDownList|Edit|Eject|Else|Enable|Enabled|Error|Exist|Expand|ExStyle|FileSystem|First|Flash|Float|FloatFast|Focus|Font|for|global|Grid|Group|GroupBox|GuiClose|GuiContextMenu|GuiDropFiles|GuiEscape|GuiSize|Hdr|Hidden|Hide|High|HKCC|HKCR|HKCU|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_LOCAL_MACHINE|HKEY_USERS|HKLM|HKU|Hours|HScroll|Icon|IconSmall|ID|IDLast|If|IfEqual|IfExist|IfGreater|IfGreaterOrEqual|IfInString|IfLess|IfLessOrEqual|IfMsgBox|IfNotEqual|IfNotExist|IfNotInString|IfWinActive|IfWinExist|IfWinNotActive|IfWinNotExist|Ignore|ImageList|in|Integer|IntegerFast|Interrupt|is|italic|Join|Label|LastFound|LastFoundExist|Limit|Lines|List|ListBox|ListView|local|Lock|Logoff|Low|Lower|Lowercase|MainWindow|Margin|Maximize|MaximizeBox|MaxSize|Minimize|MinimizeBox|MinMax|MinSize|Minutes|MonthCal|Mouse|Move|Multi|NA|No|NoActivate|NoDefault|NoHide|NoIcon|NoMainWindow|norm|Normal|NoSort|NoSortHdr|NoStandard|Not|NoTab|NoTimers|Number|Off|Ok|On|OwnDialogs|Owner|Parse|Password|Picture|Pixel|Pos|Pow|Priority|ProcessName|Radio|Range|Read|ReadOnly|Realtime|Redraw|REG_BINARY|REG_DWORD|REG_EXPAND_SZ|REG_MULTI_SZ|REG_SZ|Region|Relative|Rename|Report|Resize|Restore|Retry|RGB|Screen|Seconds|Section|Serial|SetLabel|ShiftAltTab|Show|Single|Slider|SortDesc|Standard|static|Status|StatusBar|StatusCD|strike|Style|Submit|SysMenu|Tab2|TabStop|Text|Theme|Tile|ToggleCheck|ToggleEnable|ToolWindow|Top|Topmost|TransColor|Transparent|Tray|TreeView|TryAgain|Throw|Try|Catch|Finally|Type|UnCheck|underline|Unicode|Unlock|Until|UpDown|Upper|Uppercase|UseErrorLevel|Vis|VisFirst|Visible|VScroll|Wait|WaitClose|WantCtrlA|WantF2|WantReturn|While|Wrap|Xdigit|xm|xp|xs|Yes|ym|yp|ys)\b/i,function:/[^(); \t,\n+*\-=?>:\\\/<&%\[\]]+(?=\()/m,punctuation:/[{}[\]():,]/}}e.exports=t,t.displayName="autohotkey",t.aliases=[]},53358(e){"use strict";function t(e){e.languages.autoit={comment:[/;.*/,{pattern:/(^[\t ]*)#(?:comments-start|cs)[\s\S]*?^[ \t]*#(?:comments-end|ce)/m,lookbehind:!0}],url:{pattern:/(^[\t ]*#include\s+)(?:<[^\r\n>]+>|"[^\r\n"]+")/m,lookbehind:!0},string:{pattern:/(["'])(?:\1\1|(?!\1)[^\r\n])*\1/,greedy:!0,inside:{variable:/([%$@])\w+\1/}},directive:{pattern:/(^[\t ]*)#\w+/m,lookbehind:!0,alias:"keyword"},function:/\b\w+(?=\()/,variable:/[$@]\w+/,keyword:/\b(?:Case|Const|Continue(?:Case|Loop)|Default|Dim|Do|Else(?:If)?|End(?:Func|If|Select|Switch|With)|Enum|Exit(?:Loop)?|For|Func|Global|If|In|Local|Next|Null|ReDim|Select|Static|Step|Switch|Then|To|Until|Volatile|WEnd|While|With)\b/i,number:/\b(?:0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b/i,boolean:/\b(?:True|False)\b/i,operator:/<[=>]?|[-+*\/=&>]=?|[?^]|\b(?:And|Or|Not)\b/i,punctuation:/[\[\]().,:]/}}e.exports=t,t.displayName="autoit",t.aliases=[]},6979(e){"use strict";function t(e){!function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|aptitude|apt-cache|apt-get|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:if|then|else|elif|fi|for|while|in|case|esac|function|select|do|done|until)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|break|cd|continue|eval|exec|exit|export|getopts|hash|pwd|readonly|return|shift|test|times|trap|umask|unset|alias|bind|builtin|caller|command|declare|echo|enable|help|let|local|logout|mapfile|printf|read|readarray|source|type|typeset|ulimit|unalias|set|shopt)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:true|false)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var i=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],a=r.variable[1].inside,o=0;o?^\w +\-.])*"/i,greedy:!0},number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:E[+-]?\d+)?/i,keyword:/\b(?:AS|BEEP|BLOAD|BSAVE|CALL(?: ABSOLUTE)?|CASE|CHAIN|CHDIR|CLEAR|CLOSE|CLS|COM|COMMON|CONST|DATA|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DIM|DO|DOUBLE|ELSE|ELSEIF|END|ENVIRON|ERASE|ERROR|EXIT|FIELD|FILES|FOR|FUNCTION|GET|GOSUB|GOTO|IF|INPUT|INTEGER|IOCTL|KEY|KILL|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|MKDIR|NAME|NEXT|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPTION BASE|OUT|POKE|PUT|READ|REDIM|REM|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SHARED|SINGLE|SELECT CASE|SHELL|SLEEP|STATIC|STEP|STOP|STRING|SUB|SWAP|SYSTEM|THEN|TIMER|TO|TROFF|TRON|TYPE|UNLOCK|UNTIL|USING|VIEW PRINT|WAIT|WEND|WHILE|WRITE)(?:\$|\b)/i,function:/\b(?:ABS|ACCESS|ACOS|ANGLE|AREA|ARITHMETIC|ARRAY|ASIN|ASK|AT|ATN|BASE|BEGIN|BREAK|CAUSE|CEIL|CHR|CLIP|COLLATE|COLOR|CON|COS|COSH|COT|CSC|DATE|DATUM|DEBUG|DECIMAL|DEF|DEG|DEGREES|DELETE|DET|DEVICE|DISPLAY|DOT|ELAPSED|EPS|ERASABLE|EXLINE|EXP|EXTERNAL|EXTYPE|FILETYPE|FIXED|FP|GO|GRAPH|HANDLER|IDN|IMAGE|IN|INT|INTERNAL|IP|IS|KEYED|LBOUND|LCASE|LEFT|LEN|LENGTH|LET|LINE|LINES|LOG|LOG10|LOG2|LTRIM|MARGIN|MAT|MAX|MAXNUM|MID|MIN|MISSING|MOD|NATIVE|NUL|NUMERIC|OF|OPTION|ORD|ORGANIZATION|OUTIN|OUTPUT|PI|POINT|POINTER|POINTS|POS|PRINT|PROGRAM|PROMPT|RAD|RADIANS|RANDOMIZE|RECORD|RECSIZE|RECTYPE|RELATIVE|REMAINDER|REPEAT|REST|RETRY|REWRITE|RIGHT|RND|ROUND|RTRIM|SAME|SEC|SELECT|SEQUENTIAL|SET|SETTER|SGN|SIN|SINH|SIZE|SKIP|SQR|STANDARD|STATUS|STR|STREAM|STYLE|TAB|TAN|TANH|TEMPLATE|TEXT|THERE|TIME|TIMEOUT|TRACE|TRANSFORM|TRUNCATE|UBOUND|UCASE|USE|VAL|VARIABLE|VIEWPORT|WHEN|WINDOW|WITH|ZER|ZONEWIDTH)(?:\$|\b)/i,operator:/<[=>]?|>=?|[+\-*\/^=&]|\b(?:AND|EQV|IMP|NOT|OR|XOR)\b/i,punctuation:/[,;:()]/}}e.exports=t,t.displayName="basic",t.aliases=[]},94781(e){"use strict";function t(e){var t,n,r,i,a;n=/%%?[~:\w]+%?|!\S+!/,r={pattern:/\/[a-z?]+(?=[ :]|$):?|-[a-z]\b|--[a-z-]+\b/im,alias:"attr-name",inside:{punctuation:/:/}},i=/"(?:[\\"]"|[^"])*"(?!")/,a=/(?:\b|-)\d+\b/,(t=e).languages.batch={comment:[/^::.*/m,{pattern:/((?:^|[&(])[ \t]*)rem\b(?:[^^&)\r\n]|\^(?:\r\n|[\s\S]))*/im,lookbehind:!0}],label:{pattern:/^:.*/m,alias:"property"},command:[{pattern:/((?:^|[&(])[ \t]*)for(?: \/[a-z?](?:[ :](?:"[^"]*"|[^\s"/]\S*))?)* \S+ in \([^)]+\) do/im,lookbehind:!0,inside:{keyword:/^for\b|\b(?:in|do)\b/i,string:i,parameter:r,variable:n,number:a,punctuation:/[()',]/}},{pattern:/((?:^|[&(])[ \t]*)if(?: \/[a-z?](?:[ :](?:"[^"]*"|[^\s"/]\S*))?)* (?:not )?(?:cmdextversion \d+|defined \w+|errorlevel \d+|exist \S+|(?:"[^"]*"|(?!")(?:(?!==)\S)+)?(?:==| (?:equ|neq|lss|leq|gtr|geq) )(?:"[^"]*"|[^\s"]\S*))/im,lookbehind:!0,inside:{keyword:/^if\b|\b(?:not|cmdextversion|defined|errorlevel|exist)\b/i,string:i,parameter:r,variable:n,number:a,operator:/\^|==|\b(?:equ|neq|lss|leq|gtr|geq)\b/i}},{pattern:/((?:^|[&()])[ \t]*)else\b/im,lookbehind:!0,inside:{keyword:/^else\b/i}},{pattern:/((?:^|[&(])[ \t]*)set(?: \/[a-z](?:[ :](?:"[^"]*"|[^\s"/]\S*))?)* (?:[^^&)\r\n]|\^(?:\r\n|[\s\S]))*/im,lookbehind:!0,inside:{keyword:/^set\b/i,string:i,parameter:r,variable:[n,/\w+(?=(?:[*\/%+\-&^|]|<<|>>)?=)/],number:a,operator:/[*\/%+\-&^|]=?|<<=?|>>=?|[!~_=]/,punctuation:/[()',]/}},{pattern:/((?:^|[&(])[ \t]*@?)\w+\b(?:"(?:[\\"]"|[^"])*"(?!")|[^"^&)\r\n]|\^(?:\r\n|[\s\S]))*/im,lookbehind:!0,inside:{keyword:/^\w+\b/i,string:i,parameter:r,label:{pattern:/(^\s*):\S+/m,lookbehind:!0,alias:"property"},variable:n,number:a,operator:/\^/}}],operator:/[&@]/,punctuation:/[()']/}}e.exports=t,t.displayName="batch",t.aliases=[]},62260(e){"use strict";function t(e){e.languages.bbcode={tag:{pattern:/\[\/?[^\s=\]]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'"\]=]+))?(?:\s+[^\s=\]]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'"\]=]+))*\s*\]/,inside:{tag:{pattern:/^\[\/?[^\s=\]]+/,inside:{punctuation:/^\[\/?/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'"\]=]+)/i,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\]/,"attr-name":/[^\s=\]]+/}}},e.languages.shortcode=e.languages.bbcode}e.exports=t,t.displayName="bbcode",t.aliases=["shortcode"]},59258(e){"use strict";function t(e){e.languages.birb=e.languages.extend("clike",{string:{pattern:/r?("|')(?:\\.|(?!\1)[^\\])*\1/,greedy:!0},"class-name":[/\b[A-Z](?:[\d_]*[a-zA-Z]\w*)?\b/,/\b[A-Z]\w*(?=\s+\w+\s*[;,=()])/],keyword:/\b(?:assert|break|case|class|const|default|else|enum|final|follows|for|grab|if|nest|next|new|noSeeb|return|static|switch|throw|var|void|while)\b/,operator:/\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?|:/,variable:/\b[a-z_]\w*\b/}),e.languages.insertBefore("birb","function",{metadata:{pattern:/<\w+>/,greedy:!0,alias:"symbol"}})}e.exports=t,t.displayName="birb",t.aliases=[]},62890(e,t,n){"use strict";var r=n(65806);function i(e){e.register(r),e.languages.bison=e.languages.extend("c",{}),e.languages.insertBefore("bison","comment",{bison:{pattern:/^(?:[^%]|%(?!%))*%%[\s\S]*?%%/,inside:{c:{pattern:/%\{[\s\S]*?%\}|\{(?:\{[^}]*\}|[^{}])*\}/,inside:{delimiter:{pattern:/^%?\{|%?\}$/,alias:"punctuation"},"bison-variable":{pattern:/[$@](?:<[^\s>]+>)?[\w$]+/,alias:"variable",inside:{punctuation:/<|>/}},rest:e.languages.c}},comment:e.languages.c.comment,string:e.languages.c.string,property:/\S+(?=:)/,keyword:/%\w+/,number:{pattern:/(^|[^@])\b(?:0x[\da-f]+|\d+)/i,lookbehind:!0},punctuation:/%[%?]|[|:;\[\]<>]/}}})}e.exports=i,i.displayName="bison",i.aliases=[]},15958(e){"use strict";function t(e){e.languages.bnf={string:{pattern:/"[^\r\n"]*"|'[^\r\n']*'/},definition:{pattern:/<[^<>\r\n\t]+>(?=\s*::=)/,alias:["rule","keyword"],inside:{punctuation:/^<|>$/}},rule:{pattern:/<[^<>\r\n\t]+>/,inside:{punctuation:/^<|>$/}},operator:/::=|[|()[\]{}*+?]|\.{3}/},e.languages.rbnf=e.languages.bnf}e.exports=t,t.displayName="bnf",t.aliases=["rbnf"]},61321(e){"use strict";function t(e){e.languages.brainfuck={pointer:{pattern:/<|>/,alias:"keyword"},increment:{pattern:/\+/,alias:"inserted"},decrement:{pattern:/-/,alias:"deleted"},branching:{pattern:/\[|\]/,alias:"important"},operator:/[.,]/,comment:/\S+/}}e.exports=t,t.displayName="brainfuck",t.aliases=[]},77856(e){"use strict";function t(e){e.languages.brightscript={comment:/(?:\brem|').*/i,"directive-statement":{pattern:/(^[\t ]*)#(?:const|else(?:[\t ]+if)?|end[\t ]+if|error|if).*/im,lookbehind:!0,alias:"property",inside:{"error-message":{pattern:/(^#error).+/,lookbehind:!0},directive:{pattern:/^#(?:const|else(?:[\t ]+if)?|end[\t ]+if|error|if)/,alias:"keyword"},expression:{pattern:/[\s\S]+/,inside:null}}},property:{pattern:/([\r\n{,][\t ]*)(?:(?!\d)\w+|"(?:[^"\r\n]|"")*"(?!"))(?=[ \t]*:)/,lookbehind:!0,greedy:!0},string:{pattern:/"(?:[^"\r\n]|"")*"(?!")/,greedy:!0},"class-name":{pattern:/(\bAs[\t ]+)\w+/i,lookbehind:!0},keyword:/\b(?:As|Dim|Each|Else|Elseif|End|Exit|For|Function|Goto|If|In|Print|Return|Step|Stop|Sub|Then|To|While)\b/i,boolean:/\b(?:true|false)\b/i,function:/\b(?!\d)\w+(?=[\t ]*\()/i,number:/(?:\b\d+(?:\.\d+)?(?:[ed][+-]\d+)?|&h[a-f\d]+)\b[%&!#]?/i,operator:/--|\+\+|>>=?|<<=?|<>|[-+*/\\<>]=?|[:^=?]|\b(?:and|mod|not|or)\b/i,punctuation:/[.,;()[\]{}]/,constant:/\b(?:LINE_NUM)\b/i},e.languages.brightscript["directive-statement"].inside.expression.inside=e.languages.brightscript}e.exports=t,t.displayName="brightscript",t.aliases=[]},90741(e){"use strict";function t(e){e.languages.bro={comment:{pattern:/(^|[^\\$])#.*/,lookbehind:!0,inside:{italic:/\b(?:TODO|FIXME|XXX)\b/}},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},boolean:/\b[TF]\b/,function:{pattern:/(?:function|hook|event) \w+(?:::\w+)?/,inside:{keyword:/^(?:function|hook|event)/}},variable:{pattern:/(?:global|local) \w+/i,inside:{keyword:/(?:global|local)/}},builtin:/(?:@(?:load(?:-(?:sigs|plugin))?|unload|prefixes|ifn?def|else|(?:end)?if|DIR|FILENAME))|(?:&?(?:redef|priority|log|optional|default|add_func|delete_func|expire_func|read_expire|write_expire|create_expire|synchronized|persistent|rotate_interval|rotate_size|encrypt|raw_output|mergeable|group|error_handler|type_column))/,constant:{pattern:/const \w+/i,inside:{keyword:/const/}},keyword:/\b(?:break|next|continue|alarm|using|of|add|delete|export|print|return|schedule|when|timeout|addr|any|bool|count|double|enum|file|int|interval|pattern|opaque|port|record|set|string|subnet|table|time|vector|for|if|else|in|module|function)\b/,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&|\|\|?|\?|\*|\/|~|\^|%/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,punctuation:/[{}[\];(),.:]/}}e.exports=t,t.displayName="bro",t.aliases=[]},83410(e){"use strict";function t(e){e.languages.bsl={comment:/\/\/.*/,string:[{pattern:/"(?:[^"]|"")*"(?!")/,greedy:!0},{pattern:/'(?:[^'\r\n\\]|\\.)*'/}],keyword:[{pattern:/(^|[^\w\u0400-\u0484\u0487-\u052f\u1d2b\u1d78\u2de0-\u2dff\ua640-\ua69f\ufe2e\ufe2f])(?:пока|для|новый|прервать|попытка|исключение|вызватьисключение|иначе|конецпопытки|неопределено|функция|перем|возврат|конецфункции|если|иначеесли|процедура|конецпроцедуры|тогда|знач|экспорт|конецесли|из|каждого|истина|ложь|по|цикл|конеццикла|выполнить)(?![\w\u0400-\u0484\u0487-\u052f\u1d2b\u1d78\u2de0-\u2dff\ua640-\ua69f\ufe2e\ufe2f])/i,lookbehind:!0},{pattern:/\b(?:while|for|new|break|try|except|raise|else|endtry|undefined|function|var|return|endfunction|null|if|elseif|procedure|endprocedure|then|val|export|endif|in|each|true|false|to|do|enddo|execute)\b/i}],number:{pattern:/(^(?=\d)|[^\w\u0400-\u0484\u0487-\u052f\u1d2b\u1d78\u2de0-\u2dff\ua640-\ua69f\ufe2e\ufe2f])(?:\d+(?:\.\d*)?|\.\d+)(?:E[+-]?\d+)?/i,lookbehind:!0},operator:[/[<>+\-*/]=?|[%=]/,{pattern:/(^|[^\w\u0400-\u0484\u0487-\u052f\u1d2b\u1d78\u2de0-\u2dff\ua640-\ua69f\ufe2e\ufe2f])(?:и|или|не)(?![\w\u0400-\u0484\u0487-\u052f\u1d2b\u1d78\u2de0-\u2dff\ua640-\ua69f\ufe2e\ufe2f])/i,lookbehind:!0},{pattern:/\b(?:and|or|not)\b/i}],punctuation:/\(\.|\.\)|[()\[\]:;,.]/,directive:[{pattern:/^(\s*)&.*/m,lookbehind:!0,alias:"important"},{pattern:/^\s*#.*/gm,alias:"important"}]},e.languages.oscript=e.languages.bsl}e.exports=t,t.displayName="bsl",t.aliases=[]},65806(e){"use strict";function t(e){e.languages.c=e.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:__attribute__|_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),e.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},e.languages.c.string],comment:e.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:e.languages.c}}},constant:/\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete e.languages.c.boolean}e.exports=t,t.displayName="c",t.aliases=[]},33039(e){"use strict";function t(e){e.languages.cfscript=e.languages.extend("clike",{comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,inside:{annotation:{pattern:/(?:^|[^.])@[\w\.]+/,alias:"punctuation"}}},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],keyword:/\b(?:abstract|break|catch|component|continue|default|do|else|extends|final|finally|for|function|if|in|include|package|private|property|public|remote|required|rethrow|return|static|switch|throw|try|var|while|xml)\b(?!\s*=)/,operator:[/\+\+|--|&&|\|\||::|=>|[!=]==|<=?|>=?|[-+*/%&|^!=<>]=?|\?(?:\.|:)?|[?:]/,/\b(?:and|contains|eq|equal|eqv|gt|gte|imp|is|lt|lte|mod|not|or|xor)\b/],scope:{pattern:/\b(?:application|arguments|cgi|client|cookie|local|session|super|this|variables)\b/,alias:"global"},type:{pattern:/\b(?:any|array|binary|boolean|date|guid|numeric|query|string|struct|uuid|void|xml)\b/,alias:"builtin"}}),e.languages.insertBefore("cfscript","keyword",{"function-variable":{pattern:/[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"}}),delete e.languages.cfscript["class-name"],e.languages.cfc=e.languages.cfscript}e.exports=t,t.displayName="cfscript",t.aliases=[]},85082(e,t,n){"use strict";var r=n(80096);function i(e){e.register(r),e.languages.chaiscript=e.languages.extend("clike",{string:{pattern:/(^|[^\\])'(?:[^'\\]|\\[\s\S])*'/,lookbehind:!0,greedy:!0},"class-name":[{pattern:/(\bclass\s+)\w+/,lookbehind:!0},{pattern:/(\b(?:attr|def)\s+)\w+(?=\s*::)/,lookbehind:!0}],keyword:/\b(?:attr|auto|break|case|catch|class|continue|def|default|else|finally|for|fun|global|if|return|switch|this|try|var|while)\b/,number:[e.languages.cpp.number,/\b(?:Infinity|NaN)\b/],operator:/>>=?|<<=?|\|\||&&|:[:=]?|--|\+\+|[=!<>+\-*/%|&^]=?|[?~]|`[^`\r\n]{1,4}`/}),e.languages.insertBefore("chaiscript","operator",{"parameter-type":{pattern:/([,(]\s*)\w+(?=\s+\w)/,lookbehind:!0,alias:"class-name"}}),e.languages.insertBefore("chaiscript","string",{"string-interpolation":{pattern:/(^|[^\\])"(?:[^"$\\]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*"/,lookbehind:!0,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}/,lookbehind:!0,inside:{"interpolation-expression":{pattern:/(^\$\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:e.languages.chaiscript},"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"}}},string:/[\s\S]+/}}})}e.exports=i,i.displayName="chaiscript",i.aliases=[]},79415(e){"use strict";function t(e){e.languages.cil={comment:/\/\/.*/,string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},directive:{pattern:/(^|\W)\.[a-z]+(?=\s)/,lookbehind:!0,alias:"class-name"},variable:/\[[\w\.]+\]/,keyword:/\b(?:abstract|ansi|assembly|auto|autochar|beforefieldinit|bool|bstr|byvalstr|catch|char|cil|class|currency|date|decimal|default|enum|error|explicit|extends|extern|famandassem|family|famorassem|final(?:ly)?|float32|float64|hidebysig|iant|idispatch|implements|import|initonly|instance|u?int(?:8|16|32|64)?|interface|iunknown|literal|lpstr|lpstruct|lptstr|lpwstr|managed|method|native(?:Type)?|nested|newslot|object(?:ref)?|pinvokeimpl|private|privatescope|public|reqsecobj|rtspecialname|runtime|sealed|sequential|serializable|specialname|static|string|struct|syschar|tbstr|unicode|unmanagedexp|unsigned|value(?:type)?|variant|virtual|void)\b/,function:/\b(?:(?:constrained|unaligned|volatile|readonly|tail|no)\.)?(?:conv\.(?:[iu][1248]?|ovf\.[iu][1248]?(?:\.un)?|r\.un|r4|r8)|ldc\.(?:i4(?:\.[0-9]+|\.[mM]1|\.s)?|i8|r4|r8)|ldelem(?:\.[iu][1248]?|\.r[48]|\.ref|a)?|ldind\.(?:[iu][1248]?|r[48]|ref)|stelem\.?(?:i[1248]?|r[48]|ref)?|stind\.(?:i[1248]?|r[48]|ref)?|end(?:fault|filter|finally)|ldarg(?:\.[0-3s]|a(?:\.s)?)?|ldloc(?:\.[0-9]+|\.s)?|sub(?:\.ovf(?:\.un)?)?|mul(?:\.ovf(?:\.un)?)?|add(?:\.ovf(?:\.un)?)?|stloc(?:\.[0-3s])?|refany(?:type|val)|blt(?:\.un)?(?:\.s)?|ble(?:\.un)?(?:\.s)?|bgt(?:\.un)?(?:\.s)?|bge(?:\.un)?(?:\.s)?|unbox(?:\.any)?|init(?:blk|obj)|call(?:i|virt)?|brfalse(?:\.s)?|bne\.un(?:\.s)?|ldloca(?:\.s)?|brzero(?:\.s)?|brtrue(?:\.s)?|brnull(?:\.s)?|brinst(?:\.s)?|starg(?:\.s)?|leave(?:\.s)?|shr(?:\.un)?|rem(?:\.un)?|div(?:\.un)?|clt(?:\.un)?|alignment|ldvirtftn|castclass|beq(?:\.s)?|mkrefany|localloc|ckfinite|rethrow|ldtoken|ldsflda|cgt\.un|arglist|switch|stsfld|sizeof|newobj|newarr|ldsfld|ldnull|ldflda|isinst|throw|stobj|stfld|ldstr|ldobj|ldlen|ldftn|ldfld|cpobj|cpblk|break|br\.s|xor|shl|ret|pop|not|nop|neg|jmp|dup|cgt|ceq|box|and|or|br)\b/,boolean:/\b(?:true|false)\b/,number:/\b-?(?:0x[0-9a-f]+|[0-9]+)(?:\.[0-9a-f]+)?\b/i,punctuation:/[{}[\];(),:=]|IL_[0-9A-Za-z]+/}}e.exports=t,t.displayName="cil",t.aliases=[]},29726(e){"use strict";function t(e){e.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/}}e.exports=t,t.displayName="clike",t.aliases=[]},62849(e){"use strict";function t(e){e.languages.clojure={comment:/;.*/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0},operator:/(?:::|[:|'])\b[a-z][\w*+!?-]*\b/i,keyword:{pattern:/([^\w+*'?-])(?:def|if|do|let|\.\.|quote|var|->>|->|fn|loop|recur|throw|try|monitor-enter|\.|new|set!|def-|defn|defn-|defmacro|defmulti|defmethod|defstruct|defonce|declare|definline|definterface|defprotocol|==|defrecord|>=|deftype|<=|defproject|ns|\*|\+|-|\/|<|=|>|accessor|agent|agent-errors|aget|alength|all-ns|alter|and|append-child|apply|array-map|aset|aset-boolean|aset-byte|aset-char|aset-double|aset-float|aset-int|aset-long|aset-short|assert|assoc|await|await-for|bean|binding|bit-and|bit-not|bit-or|bit-shift-left|bit-shift-right|bit-xor|boolean|branch\?|butlast|byte|cast|char|children|class|clear-agent-errors|comment|commute|comp|comparator|complement|concat|conj|cons|constantly|cond|if-not|construct-proxy|contains\?|count|create-ns|create-struct|cycle|dec|deref|difference|disj|dissoc|distinct|doall|doc|dorun|doseq|dosync|dotimes|doto|double|down|drop|drop-while|edit|end\?|ensure|eval|every\?|false\?|ffirst|file-seq|filter|find|find-doc|find-ns|find-var|first|float|flush|for|fnseq|frest|gensym|get-proxy-class|get|hash-map|hash-set|identical\?|identity|if-let|import|in-ns|inc|index|insert-child|insert-left|insert-right|inspect-table|inspect-tree|instance\?|int|interleave|intersection|into|into-array|iterate|join|key|keys|keyword|keyword\?|last|lazy-cat|lazy-cons|left|lefts|line-seq|list\*|list|load|load-file|locking|long|macroexpand|macroexpand-1|make-array|make-node|map|map-invert|map\?|mapcat|max|max-key|memfn|merge|merge-with|meta|min|min-key|name|namespace|neg\?|newline|next|nil\?|node|not|not-any\?|not-every\?|not=|ns-imports|ns-interns|ns-map|ns-name|ns-publics|ns-refers|ns-resolve|ns-unmap|nth|nthrest|or|parse|partial|path|peek|pop|pos\?|pr|pr-str|print|print-str|println|println-str|prn|prn-str|project|proxy|proxy-mappings|quot|rand|rand-int|range|re-find|re-groups|re-matcher|re-matches|re-pattern|re-seq|read|read-line|reduce|ref|ref-set|refer|rem|remove|remove-method|remove-ns|rename|rename-keys|repeat|replace|replicate|resolve|rest|resultset-seq|reverse|rfirst|right|rights|root|rrest|rseq|second|select|select-keys|send|send-off|seq|seq-zip|seq\?|set|short|slurp|some|sort|sort-by|sorted-map|sorted-map-by|sorted-set|special-symbol\?|split-at|split-with|str|string\?|struct|struct-map|subs|subvec|symbol|symbol\?|sync|take|take-nth|take-while|test|time|to-array|to-array-2d|tree-seq|true\?|union|up|update-proxy|val|vals|var-get|var-set|var\?|vector|vector-zip|vector\?|when|when-first|when-let|when-not|with-local-vars|with-meta|with-open|with-out-str|xml-seq|xml-zip|zero\?|zipmap|zipper)(?=[^\w+*'?-])/,lookbehind:!0},boolean:/\b(?:true|false|nil)\b/,number:/\b[\da-f]+\b/i,punctuation:/[{}\[\](),]/}}e.exports=t,t.displayName="clojure",t.aliases=[]},55773(e){"use strict";function t(e){e.languages.cmake={comment:/#.*/,string:{pattern:/"(?:[^\\"]|\\.)*"/,greedy:!0,inside:{interpolation:{pattern:/\$\{(?:[^{}$]|\$\{[^{}$]*\})*\}/,inside:{punctuation:/\$\{|\}/,variable:/\w+/}}}},variable:/\b(?:CMAKE_\w+|\w+_(?:VERSION(?:_MAJOR|_MINOR|_PATCH|_TWEAK)?|(?:BINARY|SOURCE)_DIR|DESCRIPTION|HOMEPAGE_URL|ROOT)|(?:ANDROID|APPLE|BORLAND|BUILD_SHARED_LIBS|CACHE|CPACK_(?:ABSOLUTE_DESTINATION_FILES|COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY|ERROR_ON_ABSOLUTE_INSTALL_DESTINATION|INCLUDE_TOPLEVEL_DIRECTORY|INSTALL_DEFAULT_DIRECTORY_PERMISSIONS|INSTALL_SCRIPT|PACKAGING_INSTALL_PREFIX|SET_DESTDIR|WARN_ON_ABSOLUTE_INSTALL_DESTINATION)|CTEST_(?:BINARY_DIRECTORY|BUILD_COMMAND|BUILD_NAME|BZR_COMMAND|BZR_UPDATE_OPTIONS|CHANGE_ID|CHECKOUT_COMMAND|CONFIGURATION_TYPE|CONFIGURE_COMMAND|COVERAGE_COMMAND|COVERAGE_EXTRA_FLAGS|CURL_OPTIONS|CUSTOM_(?:COVERAGE_EXCLUDE|ERROR_EXCEPTION|ERROR_MATCH|ERROR_POST_CONTEXT|ERROR_PRE_CONTEXT|MAXIMUM_FAILED_TEST_OUTPUT_SIZE|MAXIMUM_NUMBER_OF_(?:ERRORS|WARNINGS)|MAXIMUM_PASSED_TEST_OUTPUT_SIZE|MEMCHECK_IGNORE|POST_MEMCHECK|POST_TEST|PRE_MEMCHECK|PRE_TEST|TESTS_IGNORE|WARNING_EXCEPTION|WARNING_MATCH)|CVS_CHECKOUT|CVS_COMMAND|CVS_UPDATE_OPTIONS|DROP_LOCATION|DROP_METHOD|DROP_SITE|DROP_SITE_CDASH|DROP_SITE_PASSWORD|DROP_SITE_USER|EXTRA_COVERAGE_GLOB|GIT_COMMAND|GIT_INIT_SUBMODULES|GIT_UPDATE_CUSTOM|GIT_UPDATE_OPTIONS|HG_COMMAND|HG_UPDATE_OPTIONS|LABELS_FOR_SUBPROJECTS|MEMORYCHECK_(?:COMMAND|COMMAND_OPTIONS|SANITIZER_OPTIONS|SUPPRESSIONS_FILE|TYPE)|NIGHTLY_START_TIME|P4_CLIENT|P4_COMMAND|P4_OPTIONS|P4_UPDATE_OPTIONS|RUN_CURRENT_SCRIPT|SCP_COMMAND|SITE|SOURCE_DIRECTORY|SUBMIT_URL|SVN_COMMAND|SVN_OPTIONS|SVN_UPDATE_OPTIONS|TEST_LOAD|TEST_TIMEOUT|TRIGGER_SITE|UPDATE_COMMAND|UPDATE_OPTIONS|UPDATE_VERSION_ONLY|USE_LAUNCHERS)|CYGWIN|ENV|EXECUTABLE_OUTPUT_PATH|GHS-MULTI|IOS|LIBRARY_OUTPUT_PATH|MINGW|MSVC(?:10|11|12|14|60|70|71|80|90|_IDE|_TOOLSET_VERSION|_VERSION)?|MSYS|PROJECT_(?:BINARY_DIR|DESCRIPTION|HOMEPAGE_URL|NAME|SOURCE_DIR|VERSION|VERSION_(?:MAJOR|MINOR|PATCH|TWEAK))|UNIX|WIN32|WINCE|WINDOWS_PHONE|WINDOWS_STORE|XCODE|XCODE_VERSION))\b/,property:/\b(?:cxx_\w+|(?:ARCHIVE_OUTPUT_(?:DIRECTORY|NAME)|COMPILE_DEFINITIONS|COMPILE_PDB_NAME|COMPILE_PDB_OUTPUT_DIRECTORY|EXCLUDE_FROM_DEFAULT_BUILD|IMPORTED_(?:IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_LANGUAGES|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|NO_SONAME|OBJECTS|SONAME)|INTERPROCEDURAL_OPTIMIZATION|LIBRARY_OUTPUT_DIRECTORY|LIBRARY_OUTPUT_NAME|LINK_FLAGS|LINK_INTERFACE_LIBRARIES|LINK_INTERFACE_MULTIPLICITY|LOCATION|MAP_IMPORTED_CONFIG|OSX_ARCHITECTURES|OUTPUT_NAME|PDB_NAME|PDB_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_DIRECTORY|RUNTIME_OUTPUT_NAME|STATIC_LIBRARY_FLAGS|VS_CSHARP|VS_DOTNET_REFERENCEPROP|VS_DOTNET_REFERENCE|VS_GLOBAL_SECTION_POST|VS_GLOBAL_SECTION_PRE|VS_GLOBAL|XCODE_ATTRIBUTE)_\w+|\w+_(?:CLANG_TIDY|COMPILER_LAUNCHER|CPPCHECK|CPPLINT|INCLUDE_WHAT_YOU_USE|OUTPUT_NAME|POSTFIX|VISIBILITY_PRESET)|ABSTRACT|ADDITIONAL_MAKE_CLEAN_FILES|ADVANCED|ALIASED_TARGET|ALLOW_DUPLICATE_CUSTOM_TARGETS|ANDROID_(?:ANT_ADDITIONAL_OPTIONS|API|API_MIN|ARCH|ASSETS_DIRECTORIES|GUI|JAR_DEPENDENCIES|NATIVE_LIB_DEPENDENCIES|NATIVE_LIB_DIRECTORIES|PROCESS_MAX|PROGUARD|PROGUARD_CONFIG_PATH|SECURE_PROPS_PATH|SKIP_ANT_STEP|STL_TYPE)|ARCHIVE_OUTPUT_DIRECTORY|ATTACHED_FILES|ATTACHED_FILES_ON_FAIL|AUTOGEN_(?:BUILD_DIR|ORIGIN_DEPENDS|PARALLEL|SOURCE_GROUP|TARGETS_FOLDER|TARGET_DEPENDS)|AUTOMOC|AUTOMOC_(?:COMPILER_PREDEFINES|DEPEND_FILTERS|EXECUTABLE|MACRO_NAMES|MOC_OPTIONS|SOURCE_GROUP|TARGETS_FOLDER)|AUTORCC|AUTORCC_EXECUTABLE|AUTORCC_OPTIONS|AUTORCC_SOURCE_GROUP|AUTOUIC|AUTOUIC_EXECUTABLE|AUTOUIC_OPTIONS|AUTOUIC_SEARCH_PATHS|BINARY_DIR|BUILDSYSTEM_TARGETS|BUILD_RPATH|BUILD_RPATH_USE_ORIGIN|BUILD_WITH_INSTALL_NAME_DIR|BUILD_WITH_INSTALL_RPATH|BUNDLE|BUNDLE_EXTENSION|CACHE_VARIABLES|CLEAN_NO_CUSTOM|COMMON_LANGUAGE_RUNTIME|COMPATIBLE_INTERFACE_(?:BOOL|NUMBER_MAX|NUMBER_MIN|STRING)|COMPILE_(?:DEFINITIONS|FEATURES|FLAGS|OPTIONS|PDB_NAME|PDB_OUTPUT_DIRECTORY)|COST|CPACK_DESKTOP_SHORTCUTS|CPACK_NEVER_OVERWRITE|CPACK_PERMANENT|CPACK_STARTUP_SHORTCUTS|CPACK_START_MENU_SHORTCUTS|CPACK_WIX_ACL|CROSSCOMPILING_EMULATOR|CUDA_EXTENSIONS|CUDA_PTX_COMPILATION|CUDA_RESOLVE_DEVICE_SYMBOLS|CUDA_SEPARABLE_COMPILATION|CUDA_STANDARD|CUDA_STANDARD_REQUIRED|CXX_EXTENSIONS|CXX_STANDARD|CXX_STANDARD_REQUIRED|C_EXTENSIONS|C_STANDARD|C_STANDARD_REQUIRED|DEBUG_CONFIGURATIONS|DEFINE_SYMBOL|DEFINITIONS|DEPENDS|DEPLOYMENT_ADDITIONAL_FILES|DEPLOYMENT_REMOTE_DIRECTORY|DISABLED|DISABLED_FEATURES|ECLIPSE_EXTRA_CPROJECT_CONTENTS|ECLIPSE_EXTRA_NATURES|ENABLED_FEATURES|ENABLED_LANGUAGES|ENABLE_EXPORTS|ENVIRONMENT|EXCLUDE_FROM_ALL|EXCLUDE_FROM_DEFAULT_BUILD|EXPORT_NAME|EXPORT_PROPERTIES|EXTERNAL_OBJECT|EchoString|FAIL_REGULAR_EXPRESSION|FIND_LIBRARY_USE_LIB32_PATHS|FIND_LIBRARY_USE_LIB64_PATHS|FIND_LIBRARY_USE_LIBX32_PATHS|FIND_LIBRARY_USE_OPENBSD_VERSIONING|FIXTURES_CLEANUP|FIXTURES_REQUIRED|FIXTURES_SETUP|FOLDER|FRAMEWORK|Fortran_FORMAT|Fortran_MODULE_DIRECTORY|GENERATED|GENERATOR_FILE_NAME|GENERATOR_IS_MULTI_CONFIG|GHS_INTEGRITY_APP|GHS_NO_SOURCE_GROUP_FILE|GLOBAL_DEPENDS_DEBUG_MODE|GLOBAL_DEPENDS_NO_CYCLES|GNUtoMS|HAS_CXX|HEADER_FILE_ONLY|HELPSTRING|IMPLICIT_DEPENDS_INCLUDE_TRANSFORM|IMPORTED|IMPORTED_(?:COMMON_LANGUAGE_RUNTIME|CONFIGURATIONS|GLOBAL|IMPLIB|LIBNAME|LINK_DEPENDENT_LIBRARIES|LINK_INTERFACE_(?:LANGUAGES|LIBRARIES|MULTIPLICITY)|LOCATION|NO_SONAME|OBJECTS|SONAME)|IMPORT_PREFIX|IMPORT_SUFFIX|INCLUDE_DIRECTORIES|INCLUDE_REGULAR_EXPRESSION|INSTALL_NAME_DIR|INSTALL_RPATH|INSTALL_RPATH_USE_LINK_PATH|INTERFACE_(?:AUTOUIC_OPTIONS|COMPILE_DEFINITIONS|COMPILE_FEATURES|COMPILE_OPTIONS|INCLUDE_DIRECTORIES|LINK_DEPENDS|LINK_DIRECTORIES|LINK_LIBRARIES|LINK_OPTIONS|POSITION_INDEPENDENT_CODE|SOURCES|SYSTEM_INCLUDE_DIRECTORIES)|INTERPROCEDURAL_OPTIMIZATION|IN_TRY_COMPILE|IOS_INSTALL_COMBINED|JOB_POOLS|JOB_POOL_COMPILE|JOB_POOL_LINK|KEEP_EXTENSION|LABELS|LANGUAGE|LIBRARY_OUTPUT_DIRECTORY|LINKER_LANGUAGE|LINK_(?:DEPENDS|DEPENDS_NO_SHARED|DIRECTORIES|FLAGS|INTERFACE_LIBRARIES|INTERFACE_MULTIPLICITY|LIBRARIES|OPTIONS|SEARCH_END_STATIC|SEARCH_START_STATIC|WHAT_YOU_USE)|LISTFILE_STACK|LOCATION|MACOSX_BUNDLE|MACOSX_BUNDLE_INFO_PLIST|MACOSX_FRAMEWORK_INFO_PLIST|MACOSX_PACKAGE_LOCATION|MACOSX_RPATH|MACROS|MANUALLY_ADDED_DEPENDENCIES|MEASUREMENT|MODIFIED|NAME|NO_SONAME|NO_SYSTEM_FROM_IMPORTED|OBJECT_DEPENDS|OBJECT_OUTPUTS|OSX_ARCHITECTURES|OUTPUT_NAME|PACKAGES_FOUND|PACKAGES_NOT_FOUND|PARENT_DIRECTORY|PASS_REGULAR_EXPRESSION|PDB_NAME|PDB_OUTPUT_DIRECTORY|POSITION_INDEPENDENT_CODE|POST_INSTALL_SCRIPT|PREDEFINED_TARGETS_FOLDER|PREFIX|PRE_INSTALL_SCRIPT|PRIVATE_HEADER|PROCESSORS|PROCESSOR_AFFINITY|PROJECT_LABEL|PUBLIC_HEADER|REPORT_UNDEFINED_PROPERTIES|REQUIRED_FILES|RESOURCE|RESOURCE_LOCK|RULE_LAUNCH_COMPILE|RULE_LAUNCH_CUSTOM|RULE_LAUNCH_LINK|RULE_MESSAGES|RUNTIME_OUTPUT_DIRECTORY|RUN_SERIAL|SKIP_AUTOGEN|SKIP_AUTOMOC|SKIP_AUTORCC|SKIP_AUTOUIC|SKIP_BUILD_RPATH|SKIP_RETURN_CODE|SOURCES|SOURCE_DIR|SOVERSION|STATIC_LIBRARY_FLAGS|STATIC_LIBRARY_OPTIONS|STRINGS|SUBDIRECTORIES|SUFFIX|SYMBOLIC|TARGET_ARCHIVES_MAY_BE_SHARED_LIBS|TARGET_MESSAGES|TARGET_SUPPORTS_SHARED_LIBS|TESTS|TEST_INCLUDE_FILE|TEST_INCLUDE_FILES|TIMEOUT|TIMEOUT_AFTER_MATCH|TYPE|USE_FOLDERS|VALUE|VARIABLES|VERSION|VISIBILITY_INLINES_HIDDEN|VS_(?:CONFIGURATION_TYPE|COPY_TO_OUT_DIR|DEBUGGER_(?:COMMAND|COMMAND_ARGUMENTS|ENVIRONMENT|WORKING_DIRECTORY)|DEPLOYMENT_CONTENT|DEPLOYMENT_LOCATION|DOTNET_REFERENCES|DOTNET_REFERENCES_COPY_LOCAL|GLOBAL_KEYWORD|GLOBAL_PROJECT_TYPES|GLOBAL_ROOTNAMESPACE|INCLUDE_IN_VSIX|IOT_STARTUP_TASK|KEYWORD|RESOURCE_GENERATOR|SCC_AUXPATH|SCC_LOCALPATH|SCC_PROJECTNAME|SCC_PROVIDER|SDK_REFERENCES|SHADER_(?:DISABLE_OPTIMIZATIONS|ENABLE_DEBUG|ENTRYPOINT|FLAGS|MODEL|OBJECT_FILE_NAME|OUTPUT_HEADER_FILE|TYPE|VARIABLE_NAME)|STARTUP_PROJECT|TOOL_OVERRIDE|USER_PROPS|WINRT_COMPONENT|WINRT_EXTENSIONS|WINRT_REFERENCES|XAML_TYPE)|WILL_FAIL|WIN32_EXECUTABLE|WINDOWS_EXPORT_ALL_SYMBOLS|WORKING_DIRECTORY|WRAP_EXCLUDE|XCODE_(?:EMIT_EFFECTIVE_PLATFORM_NAME|EXPLICIT_FILE_TYPE|FILE_ATTRIBUTES|LAST_KNOWN_FILE_TYPE|PRODUCT_TYPE|SCHEME_(?:ADDRESS_SANITIZER|ADDRESS_SANITIZER_USE_AFTER_RETURN|ARGUMENTS|DISABLE_MAIN_THREAD_CHECKER|DYNAMIC_LIBRARY_LOADS|DYNAMIC_LINKER_API_USAGE|ENVIRONMENT|EXECUTABLE|GUARD_MALLOC|MAIN_THREAD_CHECKER_STOP|MALLOC_GUARD_EDGES|MALLOC_SCRIBBLE|MALLOC_STACK|THREAD_SANITIZER(?:_STOP)?|UNDEFINED_BEHAVIOUR_SANITIZER(?:_STOP)?|ZOMBIE_OBJECTS))|XCTEST)\b/,keyword:/\b(?:add_compile_definitions|add_compile_options|add_custom_command|add_custom_target|add_definitions|add_dependencies|add_executable|add_library|add_link_options|add_subdirectory|add_test|aux_source_directory|break|build_command|build_name|cmake_host_system_information|cmake_minimum_required|cmake_parse_arguments|cmake_policy|configure_file|continue|create_test_sourcelist|ctest_build|ctest_configure|ctest_coverage|ctest_empty_binary_directory|ctest_memcheck|ctest_read_custom_files|ctest_run_script|ctest_sleep|ctest_start|ctest_submit|ctest_test|ctest_update|ctest_upload|define_property|else|elseif|enable_language|enable_testing|endforeach|endfunction|endif|endmacro|endwhile|exec_program|execute_process|export|export_library_dependencies|file|find_file|find_library|find_package|find_path|find_program|fltk_wrap_ui|foreach|function|get_cmake_property|get_directory_property|get_filename_component|get_property|get_source_file_property|get_target_property|get_test_property|if|include|include_directories|include_external_msproject|include_guard|include_regular_expression|install|install_files|install_programs|install_targets|link_directories|link_libraries|list|load_cache|load_command|macro|make_directory|mark_as_advanced|math|message|option|output_required_files|project|qt_wrap_cpp|qt_wrap_ui|remove|remove_definitions|return|separate_arguments|set|set_directory_properties|set_property|set_source_files_properties|set_target_properties|set_tests_properties|site_name|source_group|string|subdir_depends|subdirs|target_compile_definitions|target_compile_features|target_compile_options|target_include_directories|target_link_directories|target_link_libraries|target_link_options|target_sources|try_compile|try_run|unset|use_mangled_mesa|utility_source|variable_requires|variable_watch|while|write_file)(?=\s*\()\b/,boolean:/\b(?:ON|OFF|TRUE|FALSE)\b/,namespace:/\b(?:PROPERTIES|SHARED|PRIVATE|STATIC|PUBLIC|INTERFACE|TARGET_OBJECTS)\b/,operator:/\b(?:NOT|AND|OR|MATCHES|LESS|GREATER|EQUAL|STRLESS|STRGREATER|STREQUAL|VERSION_LESS|VERSION_EQUAL|VERSION_GREATER|DEFINED)\b/,inserted:{pattern:/\b\w+::\w+\b/,alias:"class-name"},number:/\b\d+(?:\.\d+)*\b/,function:/\b[a-z_]\w*(?=\s*\()\b/i,punctuation:/[()>}]|\$[<{]/}}e.exports=t,t.displayName="cmake",t.aliases=[]},32762(e){"use strict";function t(e){e.languages.cobol={comment:{pattern:/\*>.*|(^[ \t]*)\*.*/m,lookbehind:!0,greedy:!0},string:{pattern:/[xzgn]?(?:"(?:[^\r\n"]|"")*"(?!")|'(?:[^\r\n']|'')*'(?!'))/i,greedy:!0},level:{pattern:/(^[ \t]*)\d+\b/m,lookbehind:!0,greedy:!0,alias:"number"},"class-name":{pattern:/(\bpic(?:ture)?\s+)(?:(?:[-\w$/,:*+<>]|\.(?!\s|$))(?:\(\d+\))?)+/i,lookbehind:!0,inside:{number:{pattern:/(\()\d+/,lookbehind:!0},punctuation:/[()]/}},keyword:{pattern:/(^|[^\w-])(?:ABORT|ACCEPT|ACCESS|ADD|ADDRESS|ADVANCING|AFTER|ALIGNED|ALL|ALPHABET|ALPHABETIC|ALPHABETIC-LOWER|ALPHABETIC-UPPER|ALPHANUMERIC|ALPHANUMERIC-EDITED|ALSO|ALTER|ALTERNATE|ANY|ARE|AREA|AREAS|AS|ASCENDING|ASCII|ASSIGN|ASSOCIATED-DATA|ASSOCIATED-DATA-LENGTH|AT|ATTRIBUTE|AUTHOR|AUTO|AUTO-SKIP|BACKGROUND-COLOR|BACKGROUND-COLOUR|BASIS|BEEP|BEFORE|BEGINNING|BELL|BINARY|BIT|BLANK|BLINK|BLOCK|BOUNDS|BOTTOM|BY|BYFUNCTION|BYTITLE|CALL|CANCEL|CAPABLE|CCSVERSION|CD|CF|CH|CHAINING|CHANGED|CHANNEL|CHARACTER|CHARACTERS|CLASS|CLASS-ID|CLOCK-UNITS|CLOSE|CLOSE-DISPOSITION|COBOL|CODE|CODE-SET|COLLATING|COL|COLUMN|COM-REG|COMMA|COMMITMENT|COMMON|COMMUNICATION|COMP|COMP-1|COMP-2|COMP-3|COMP-4|COMP-5|COMPUTATIONAL|COMPUTATIONAL-1|COMPUTATIONAL-2|COMPUTATIONAL-3|COMPUTATIONAL-4|COMPUTATIONAL-5|COMPUTE|CONFIGURATION|CONTAINS|CONTENT|CONTINUE|CONTROL|CONTROL-POINT|CONTROLS|CONVENTION|CONVERTING|COPY|CORR|CORRESPONDING|COUNT|CRUNCH|CURRENCY|CURSOR|DATA|DATA-BASE|DATE|DATE-COMPILED|DATE-WRITTEN|DAY|DAY-OF-WEEK|DBCS|DE|DEBUG-CONTENTS|DEBUG-ITEM|DEBUG-LINE|DEBUG-NAME|DEBUG-SUB-1|DEBUG-SUB-2|DEBUG-SUB-3|DEBUGGING|DECIMAL-POINT|DECLARATIVES|DEFAULT|DEFAULT-DISPLAY|DEFINITION|DELETE|DELIMITED|DELIMITER|DEPENDING|DESCENDING|DESTINATION|DETAIL|DFHRESP|DFHVALUE|DISABLE|DISK|DISPLAY|DISPLAY-1|DIVIDE|DIVISION|DONTCARE|DOUBLE|DOWN|DUPLICATES|DYNAMIC|EBCDIC|EGCS|EGI|ELSE|EMI|EMPTY-CHECK|ENABLE|END|END-ACCEPT|END-ADD|END-CALL|END-COMPUTE|END-DELETE|END-DIVIDE|END-EVALUATE|END-IF|END-MULTIPLY|END-OF-PAGE|END-PERFORM|END-READ|END-RECEIVE|END-RETURN|END-REWRITE|END-SEARCH|END-START|END-STRING|END-SUBTRACT|END-UNSTRING|END-WRITE|ENDING|ENTER|ENTRY|ENTRY-PROCEDURE|ENVIRONMENT|EOP|ERASE|ERROR|EOL|EOS|ESCAPE|ESI|EVALUATE|EVENT|EVERY|EXCEPTION|EXCLUSIVE|EXHIBIT|EXIT|EXPORT|EXTEND|EXTENDED|EXTERNAL|FD|FILE|FILE-CONTROL|FILLER|FINAL|FIRST|FOOTING|FOR|FOREGROUND-COLOR|FOREGROUND-COLOUR|FROM|FULL|FUNCTION|FUNCTIONNAME|FUNCTION-POINTER|GENERATE|GOBACK|GIVING|GLOBAL|GO|GRID|GROUP|HEADING|HIGHLIGHT|HIGH-VALUE|HIGH-VALUES|I-O|I-O-CONTROL|ID|IDENTIFICATION|IF|IMPLICIT|IMPORT|IN|INDEX|INDEXED|INDICATE|INITIAL|INITIALIZE|INITIATE|INPUT|INPUT-OUTPUT|INSPECT|INSTALLATION|INTEGER|INTO|INVALID|INVOKE|IS|JUST|JUSTIFIED|KANJI|KEPT|KEY|KEYBOARD|LABEL|LANGUAGE|LAST|LB|LD|LEADING|LEFT|LEFTLINE|LENGTH|LENGTH-CHECK|LIBACCESS|LIBPARAMETER|LIBRARY|LIMIT|LIMITS|LINAGE|LINAGE-COUNTER|LINE|LINES|LINE-COUNTER|LINKAGE|LIST|LOCAL|LOCAL-STORAGE|LOCK|LONG-DATE|LONG-TIME|LOWER|LOWLIGHT|LOW-VALUE|LOW-VALUES|MEMORY|MERGE|MESSAGE|MMDDYYYY|MODE|MODULES|MORE-LABELS|MOVE|MULTIPLE|MULTIPLY|NAMED|NATIONAL|NATIONAL-EDITED|NATIVE|NEGATIVE|NETWORK|NEXT|NO|NO-ECHO|NULL|NULLS|NUMBER|NUMERIC|NUMERIC-DATE|NUMERIC-EDITED|NUMERIC-TIME|OBJECT-COMPUTER|OCCURS|ODT|OF|OFF|OMITTED|ON|OPEN|OPTIONAL|ORDER|ORDERLY|ORGANIZATION|OTHER|OUTPUT|OVERFLOW|OVERLINE|OWN|PACKED-DECIMAL|PADDING|PAGE|PAGE-COUNTER|PASSWORD|PERFORM|PF|PH|PIC|PICTURE|PLUS|POINTER|POSITION|POSITIVE|PORT|PRINTER|PRINTING|PRIVATE|PROCEDURE|PROCEDURE-POINTER|PROCEDURES|PROCEED|PROCESS|PROGRAM|PROGRAM-ID|PROGRAM-LIBRARY|PROMPT|PURGE|QUEUE|QUOTE|QUOTES|RANDOM|READER|REMOTE|RD|REAL|READ|RECEIVE|RECEIVED|RECORD|RECORDING|RECORDS|RECURSIVE|REDEFINES|REEL|REF|REFERENCE|REFERENCES|RELATIVE|RELEASE|REMAINDER|REMARKS|REMOVAL|REMOVE|RENAMES|REPLACE|REPLACING|REPORT|REPORTING|REPORTS|REQUIRED|RERUN|RESERVE|REVERSE-VIDEO|RESET|RETURN|RETURN-CODE|RETURNING|REVERSED|REWIND|REWRITE|RF|RH|RIGHT|ROUNDED|RUN|SAME|SAVE|SCREEN|SD|SEARCH|SECTION|SECURE|SECURITY|SEGMENT|SEGMENT-LIMIT|SELECT|SEND|SENTENCE|SEPARATE|SEQUENCE|SEQUENTIAL|SET|SHARED|SHAREDBYALL|SHAREDBYRUNUNIT|SHARING|SHIFT-IN|SHIFT-OUT|SHORT-DATE|SIGN|SIZE|SORT|SORT-CONTROL|SORT-CORE-SIZE|SORT-FILE-SIZE|SORT-MERGE|SORT-MESSAGE|SORT-MODE-SIZE|SORT-RETURN|SOURCE|SOURCE-COMPUTER|SPACE|SPACES|SPECIAL-NAMES|STANDARD|STANDARD-1|STANDARD-2|START|STATUS|STOP|STRING|SUB-QUEUE-1|SUB-QUEUE-2|SUB-QUEUE-3|SUBTRACT|SUM|SUPPRESS|SYMBOL|SYMBOLIC|SYNC|SYNCHRONIZED|TABLE|TALLY|TALLYING|TASK|TAPE|TERMINAL|TERMINATE|TEST|TEXT|THEN|THREAD|THREAD-LOCAL|THROUGH|THRU|TIME|TIMER|TIMES|TITLE|TO|TODAYS-DATE|TODAYS-NAME|TOP|TRAILING|TRUNCATED|TYPE|TYPEDEF|UNDERLINE|UNIT|UNSTRING|UNTIL|UP|UPON|USAGE|USE|USING|VALUE|VALUES|VARYING|VIRTUAL|WAIT|WHEN|WHEN-COMPILED|WITH|WORDS|WORKING-STORAGE|WRITE|YEAR|YYYYMMDD|YYYYDDD|ZERO-FILL|ZEROS|ZEROES)(?![\w-])/i,lookbehind:!0},boolean:{pattern:/(^|[^\w-])(?:false|true)(?![\w-])/i,lookbehind:!0},number:{pattern:/(^|[^\w-])(?:[+-]?(?:(?:\d+(?:[.,]\d+)?|[.,]\d+)(?:e[+-]?\d+)?|zero))(?![\w-])/i,lookbehind:!0},operator:[/<>|[<>]=?|[=+*/&]/,{pattern:/(^|[^\w-])(?:-|and|equal|greater|less|not|or|than)(?![\w-])/i,lookbehind:!0}],punctuation:/[.:,()]/}}e.exports=t,t.displayName="cobol",t.aliases=[]},43576(e){"use strict";function t(e){var t,n,r;n=/#(?!\{).+/,r={pattern:/#\{[^}]+\}/,alias:"variable"},(t=e).languages.coffeescript=t.languages.extend("javascript",{comment:n,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:r}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),t.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:n,interpolation:r}}}),t.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:t.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:r}}]}),t.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete t.languages.coffeescript["template-string"],t.languages.coffee=t.languages.coffeescript}e.exports=t,t.displayName="coffeescript",t.aliases=["coffee"]},71794(e){"use strict";function t(e){e.languages.concurnas={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],langext:{pattern:/\b\w+\s*\|\|[\s\S]+?\|\|/,greedy:!0,alias:"string"},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/,lookbehind:!0},keyword:/\b(?:abstract|actor|also|annotation|assert|async|await|bool|boolean|break|byte|case|catch|changed|char|class|closed|constant|continue|def|default|del|double|elif|else|enum|every|extends|false|finally|float|for|from|global|gpudef|gpukernel|if|import|in|init|inject|int|lambda|local|long|loop|match|new|nodefault|null|of|onchange|open|out|override|package|parfor|parforsync|post|pre|private|protected|provide|provider|public|return|shared|short|single|size_t|sizeof|super|sync|this|throw|trait|trans|transient|true|try|typedef|unchecked|using|val|var|void|while|with)\b/,boolean:/\b(?:false|true)\b/,number:/\b0b[01][01_]*L?\b|\b0x(?:[\da-f_]*\.)?[\da-f_p+-]+\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfls]?/i,punctuation:/[{}[\];(),.:]/,operator:/<==|>==|=>|->|<-|<>|\^|&==|&<>|!|\?:?|\.\?|\+\+|--|[-+*/=<>]=?|\b(?:and|as|band|bor|bxor|comp|is|isnot|mod|or)\b=?/,annotation:{pattern:/@(?:\w+:)?(?:\w+|\[[^\]]+\])?/,alias:"builtin"}},e.languages.insertBefore("concurnas","langext",{string:{pattern:/[rs]?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:e.languages.concurnas},string:/[\s\S]+/}}}),e.languages.conc=e.languages.concurnas}e.exports=t,t.displayName="concurnas",t.aliases=["conc"]},1315(e){"use strict";function t(e){!function(e){for(var t=/\(\*(?:[^(*]|\((?!\*)|\*(?!\))|)*\*\)/.source,n=0;n<2;n++)t=t.replace(//g,function(){return t});t=t.replace(//g,"[]"),e.languages.coq={comment:RegExp(t),string:{pattern:/"(?:[^"]|"")*"(?!")/,greedy:!0},attribute:[{pattern:RegExp(/#\[(?:[^\]("]|"(?:[^"]|"")*"(?!")|\((?!\*)|)*\]/.source.replace(//g,function(){return t})),greedy:!0,alias:"attr-name",inside:{comment:RegExp(t),string:{pattern:/"(?:[^"]|"")*"(?!")/,greedy:!0},operator:/=/,punctuation:/^#\[|\]$|[,()]/}},{pattern:/\b(?:Cumulative|Global|Local|Monomorphic|NonCumulative|Polymorphic|Private|Program)\b/,alias:"attr-name"}],keyword:/\b(?:_|Abort|About|Add|Admit|Admitted|All|apply|Arguments|as|As|Assumptions|at|Axiom|Axioms|Back|BackTo|Backtrace|Bind|BinOp|BinOpSpec|BinRel|Blacklist|by|Canonical|Case|Cd|Check|Class|Classes|Close|Coercion|Coercions|cofix|CoFixpoint|CoInductive|Collection|Combined|Compute|Conjecture|Conjectures|Constant|Constants|Constraint|Constructors|Context|Corollary|Create|CstOp|Custom|Cut|Debug|Declare|Defined|Definition|Delimit|Dependencies|Dependent|Derive|Diffs|Drop|Elimination|else|end|End|Entry|Equality|Eval|Example|Existential|Existentials|Existing|exists|exists2|Export|Extern|Extraction|Fact|Fail|Field|File|Firstorder|fix|Fixpoint|Flags|Focus|for|forall|From|fun|Funclass|Function|Functional|GC|Generalizable|Goal|Grab|Grammar|Graph|Guarded|Haskell|Heap|Hide|Hint|HintDb|Hints|Hypotheses|Hypothesis|Identity|if|IF|Immediate|Implicit|Implicits|Import|in|Include|Induction|Inductive|Infix|Info|Initial|InjTyp|Inline|Inspect|Instance|Instances|Intro|Intros|Inversion|Inversion_clear|JSON|Language|Left|Lemma|let|Let|Lia|Libraries|Library|Load|LoadPath|Locate|Ltac|Ltac2|match|Match|measure|Method|Minimality|ML|Module|Modules|Morphism|move|Next|NoInline|Notation|Number|Obligation|Obligations|OCaml|Opaque|Open|Optimize|Parameter|Parameters|Parametric|Path|Paths|Prenex|Preterm|Primitive|Print|Profile|Projections|Proof|Prop|PropBinOp|Property|PropOp|Proposition|PropUOp|Pwd|Qed|Quit|Rec|Record|Recursive|Redirect|Reduction|Register|Relation|Remark|Remove|removed|Require|Reserved|Reset|Resolve|Restart|return|Rewrite|Right|Ring|Rings|Saturate|Save|Scheme|Scope|Scopes|Search|SearchHead|SearchPattern|SearchRewrite|Section|Separate|Set|Setoid|Show|Signatures|Solve|Solver|Sort|Sortclass|Sorted|Spec|SProp|Step|Strategies|Strategy|String|struct|Structure|SubClass|Subgraph|SuchThat|Tactic|Term|TestCompile|then|Theorem|Time|Timeout|To|Transparent|Type|Typeclasses|Types|Typing|Undelimit|Undo|Unfocus|Unfocused|Unfold|Universe|Universes|UnOp|UnOpSpec|Unshelve|using|Variable|Variables|Variant|Verbose|View|Visibility|wf|where|with|Zify)\b/,number:/\b(?:0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]+)?(?:p[+-]?\d[\d_]*)?|\d[\d_]*(?:\.[\d_]+)?(?:e[+-]?\d[\d_]*)?)\b/i,punct:{pattern:/@\{|\{\||\[=|:>/,alias:"punctuation"},operator:/\/\\|\\\/|\.{2,3}|:{1,2}=|\*\*|[-=]>|<(?:->?|[+:=>]|<:)|>(?:=|->)|\|[-|]?|[-!%&*+/<=>?@^~']/,punctuation:/\.\(|`\(|@\{|`\{|\{\||\[=|:>|[:.,;(){}\[\]]/}}(e)}e.exports=t,t.displayName="coq",t.aliases=[]},80096(e,t,n){"use strict";var r=n(65806);function i(e){var t,n,i;e.register(r),t=e,n=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,i=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,function(){return n.source}),t.languages.cpp=t.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,function(){return n.source})),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:n,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:true|false)\b/}),t.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:module|import)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,function(){return i})+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),t.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b[a-z_]\w*\s*<(?:[^<>]|<(?:[^<>])*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t.languages.cpp}}}}),t.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),t.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:t.languages.extend("cpp",{})}}),t.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},t.languages.cpp["base-clause"])}e.exports=i,i.displayName="cpp",i.aliases=[]},99176(e,t,n){"use strict";var r=n(56939);function i(e){var t;e.register(r),(t=e).languages.crystal=t.languages.extend("ruby",{keyword:[/\b(?:abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|rescue|return|require|select|self|sizeof|struct|super|then|type|typeof|uninitialized|union|unless|until|when|while|with|yield|__DIR__|__END_LINE__|__FILE__|__LINE__)\b/,{pattern:/(\.\s*)(?:is_a|responds_to)\?/,lookbehind:!0}],number:/\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[\da-fA-F_]*[\da-fA-F]|(?:\d(?:[\d_]*\d)?)(?:\.[\d_]*\d)?(?:[eE][+-]?[\d_]*\d)?)(?:_(?:[uif](?:8|16|32|64))?)?\b/}),t.languages.insertBefore("crystal","string",{attribute:{pattern:/@\[.+?\]/,alias:"attr-name",inside:{delimiter:{pattern:/^@\[|\]$/,alias:"tag"},rest:t.languages.crystal}},expansion:[{pattern:/\{\{.+?\}\}/,inside:{delimiter:{pattern:/^\{\{|\}\}$/,alias:"tag"},rest:t.languages.crystal}},{pattern:/\{%.+?%\}/,inside:{delimiter:{pattern:/^\{%|%\}$/,alias:"tag"},rest:t.languages.crystal}}]})}e.exports=i,i.displayName="crystal",i.aliases=[]},61958(e){"use strict";function t(e){!function(e){function t(e,t){return e.replace(/<<(\d+)>>/g,function(e,n){return"(?:"+t[+n]+")"})}function n(e,n,r){return RegExp(t(e,n),r||"")}function r(e,t){for(var n=0;n>/g,function(){return"(?:"+e+")"});return e.replace(/<>/g,"[^\\s\\S]")}var i={type:"bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",typeDeclaration:"class enum interface struct",contextual:"add alias and ascending async await by descending from get global group into join let nameof not notnull on or orderby partial remove select set unmanaged value when where",other:"abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield"};function a(e){return"\\b(?:"+e.trim().replace(/ /g,"|")+")\\b"}var o=a(i.typeDeclaration),s=RegExp(a(i.type+" "+i.typeDeclaration+" "+i.contextual+" "+i.other)),u=a(i.typeDeclaration+" "+i.contextual+" "+i.other),c=a(i.type+" "+i.typeDeclaration+" "+i.other),l=r(/<(?:[^<>;=+\-*/%&|^]|<>)*>/.source,2),f=r(/\((?:[^()]|<>)*\)/.source,2),d=/@?\b[A-Za-z_]\w*\b/.source,h=t(/<<0>>(?:\s*<<1>>)?/.source,[d,l]),p=t(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source,[u,h]),b=/\[\s*(?:,\s*)*\]/.source,m=t(/<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source,[p,b]),g=t(/[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source,[l,f,b]),v=t(/\(<<0>>+(?:,<<0>>+)+\)/.source,[g]),y=t(/(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source,[v,p,b]),w={keyword:s,punctuation:/[<>()?,.:[\]]/},_=/'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source,E=/"(?:\\.|[^\\"\r\n])*"/.source,S=/@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source;e.languages.csharp=e.languages.extend("clike",{string:[{pattern:n(/(^|[^$\\])<<0>>/.source,[S]),lookbehind:!0,greedy:!0},{pattern:n(/(^|[^@$\\])<<0>>/.source,[E]),lookbehind:!0,greedy:!0},{pattern:RegExp(_),greedy:!0,alias:"character"}],"class-name":[{pattern:n(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source,[p]),lookbehind:!0,inside:w},{pattern:n(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source,[d,y]),lookbehind:!0,inside:w},{pattern:n(/(\busing\s+)<<0>>(?=\s*=)/.source,[d]),lookbehind:!0},{pattern:n(/(\b<<0>>\s+)<<1>>/.source,[o,h]),lookbehind:!0,inside:w},{pattern:n(/(\bcatch\s*\(\s*)<<0>>/.source,[p]),lookbehind:!0,inside:w},{pattern:n(/(\bwhere\s+)<<0>>/.source,[d]),lookbehind:!0},{pattern:n(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source,[m]),lookbehind:!0,inside:w},{pattern:n(/\b<<0>>(?=\s+(?!<<1>>)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source,[y,c,d]),inside:w}],keyword:s,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:ul|lu|[dflmu])?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),e.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),e.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:n(/([(,]\s*)<<0>>(?=\s*:)/.source,[d]),lookbehind:!0,alias:"punctuation"}}),e.languages.insertBefore("csharp","class-name",{namespace:{pattern:n(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source,[d]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:n(/(\b(?:default|typeof|sizeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source,[f]),lookbehind:!0,alias:"class-name",inside:w},"return-type":{pattern:n(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source,[y,p]),inside:w,alias:"class-name"},"constructor-invocation":{pattern:n(/(\bnew\s+)<<0>>(?=\s*[[({])/.source,[y]),lookbehind:!0,inside:w,alias:"class-name"},"generic-method":{pattern:n(/<<0>>\s*<<1>>(?=\s*\()/.source,[d,l]),inside:{function:n(/^<<0>>/.source,[d]),generic:{pattern:RegExp(l),alias:"class-name",inside:w}}},"type-list":{pattern:n(/\b((?:<<0>>\s+<<1>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>)(?:\s*,\s*(?:<<3>>|<<4>>))*(?=\s*(?:where|[{;]|=>|$))/.source,[o,h,d,y,s.source]),lookbehind:!0,inside:{keyword:s,"class-name":{pattern:RegExp(y),greedy:!0,inside:w},punctuation:/,/}},preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(#)\b(?:define|elif|else|endif|endregion|error|if|line|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var k=E+"|"+_,x=t(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source,[k]),T=r(t(/[^"'/()]|<<0>>|\(<>*\)/.source,[x]),2),M=/\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source,O=t(/<<0>>(?:\s*\(<<1>>*\))?/.source,[p,T]);e.languages.insertBefore("csharp","class-name",{attribute:{pattern:n(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source,[M,O]),lookbehind:!0,greedy:!0,inside:{target:{pattern:n(/^<<0>>(?=\s*:)/.source,[M]),alias:"keyword"},"attribute-arguments":{pattern:n(/\(<<0>>*\)/.source,[T]),inside:e.languages.csharp},"class-name":{pattern:RegExp(p),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var A=/:[^}\r\n]+/.source,L=r(t(/[^"'/()]|<<0>>|\(<>*\)/.source,[x]),2),C=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[L,A]),I=r(t(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<>*\)/.source,[k]),2),D=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[I,A]);function N(t,r){return{interpolation:{pattern:n(/((?:^|[^{])(?:\{\{)*)<<0>>/.source,[t]),lookbehind:!0,inside:{"format-string":{pattern:n(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source,[r,A]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:e.languages.csharp}}},string:/[\s\S]+/}}e.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:n(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source,[C]),lookbehind:!0,greedy:!0,inside:N(C,L)},{pattern:n(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source,[D]),lookbehind:!0,greedy:!0,inside:N(D,I)}]})}(e),e.languages.dotnet=e.languages.cs=e.languages.csharp}e.exports=t,t.displayName="csharp",t.aliases=["dotnet","cs"]},65447(e){"use strict";function t(e){e.languages.csp={directive:{pattern:/(^|[^-\da-z])(?:base-uri|block-all-mixed-content|(?:child|connect|default|font|frame|img|manifest|media|object|prefetch|script|style|worker)-src|disown-opener|form-action|frame-(?:ancestors|options)|input-protection(?:-(?:clip|selectors))?|navigate-to|plugin-types|policy-uri|referrer|reflected-xss|report-(?:to|uri)|require-sri-for|sandbox|(?:script|style)-src-(?:attr|elem)|upgrade-insecure-requests)(?=[^-\da-z]|$)/i,lookbehind:!0,alias:"keyword"},safe:{pattern:/'(?:deny|none|report-sample|self|strict-dynamic|top-only|(?:nonce|sha(?:256|384|512))-[-+/\w=]+)'/i,alias:"selector"},unsafe:{pattern:/(?:'unsafe-(?:allow-redirects|dynamic|eval|hash-attributes|hashed-attributes|hashes|inline)'|\*)/i,alias:"function"}}}e.exports=t,t.displayName="csp",t.aliases=[]},4762(e){"use strict";function t(e){var t,n,r,i,a;r=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,(t=e).languages.css.selector={pattern:t.languages.css.selector.pattern,lookbehind:!0,inside:n={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+r.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[r,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},t.languages.css.atrule.inside["selector-function-argument"].inside=n,t.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}}),i={pattern:/(\b\d+)(?:%|[a-z]+\b)/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},t.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:rgb|hsl)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:rgb|hsl)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:i,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:i,number:a})}e.exports=t,t.displayName="cssExtras",t.aliases=[]},12049(e){"use strict";function t(e){var t,n,r;n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/,(t=e).languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},t.languages.css.atrule.inside.rest=t.languages.css,(r=t.languages.markup)&&(r.tag.addInlined("style","css"),r.tag.addAttribute("style","css"))}e.exports=t,t.displayName="css",t.aliases=[]},78090(e){"use strict";function t(e){e.languages.csv={value:/[^\r\n,"]+|"(?:[^"]|"")*"(?!")/,punctuation:/,/}}e.exports=t,t.displayName="csv",t.aliases=[]},40315(e){"use strict";function t(e){e.languages.cypher={comment:/\/\/.*/,string:{pattern:/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/,greedy:!0},"class-name":{pattern:/(:\s*)(?:\w+|`(?:[^`\\\r\n])*`)(?=\s*[{):])/,lookbehind:!0,greedy:!0},relationship:{pattern:/(-\[\s*(?:\w+\s*|`(?:[^`\\\r\n])*`\s*)?:\s*|\|\s*:\s*)(?:\w+|`(?:[^`\\\r\n])*`)/,lookbehind:!0,greedy:!0,alias:"property"},identifier:{pattern:/`(?:[^`\\\r\n])*`/,greedy:!0,alias:"symbol"},variable:/\$\w+/,keyword:/\b(?:ADD|ALL|AND|AS|ASC|ASCENDING|ASSERT|BY|CALL|CASE|COMMIT|CONSTRAINT|CONTAINS|CREATE|CSV|DELETE|DESC|DESCENDING|DETACH|DISTINCT|DO|DROP|ELSE|END|ENDS|EXISTS|FOR|FOREACH|IN|INDEX|IS|JOIN|KEY|LIMIT|LOAD|MANDATORY|MATCH|MERGE|NODE|NOT|OF|ON|OPTIONAL|OR|ORDER(?=\s+BY)|PERIODIC|REMOVE|REQUIRE|RETURN|SCALAR|SCAN|SET|SKIP|START|STARTS|THEN|UNION|UNIQUE|UNWIND|USING|WHEN|WHERE|WITH|XOR|YIELD)\b/i,function:/\b\w+\b(?=\s*\()/,boolean:/\b(?:true|false|null)\b/i,number:/\b(?:0x[\da-fA-F]+|\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/,operator:/:|<--?|--?>?|<>|=~?|[<>]=?|[+*/%^|]|\.\.\.?/,punctuation:/[()[\]{},;.]/}}e.exports=t,t.displayName="cypher",t.aliases=[]},7902(e){"use strict";function t(e){e.languages.d=e.languages.extend("clike",{comment:[{pattern:/^\s*#!.+/,greedy:!0},{pattern:RegExp(/(^|[^\\])/.source+"(?:"+[/\/\+(?:\/\+(?:[^+]|\+(?!\/))*\+\/|(?!\/\+)[\s\S])*?\+\//.source,/\/\/.*/.source,/\/\*[\s\S]*?\*\//.source].join("|")+")"),lookbehind:!0,greedy:!0}],string:[{pattern:RegExp([/\b[rx]"(?:\\[\s\S]|[^\\"])*"[cwd]?/.source,/\bq"(?:\[[\s\S]*?\]|\([\s\S]*?\)|<[\s\S]*?>|\{[\s\S]*?\})"/.source,/\bq"((?!\d)\w+)$[\s\S]*?^\1"/.source,/\bq"(.)[\s\S]*?\2"/.source,/'(?:\\(?:\W|\w+)|[^\\])'/.source,/(["`])(?:\\[\s\S]|(?!\3)[^\\])*\3[cwd]?/.source].join("|"),"m"),greedy:!0},{pattern:/\bq\{(?:\{[^{}]*\}|[^{}])*\}/,greedy:!0,alias:"token-string"}],keyword:/\$|\b(?:abstract|alias|align|asm|assert|auto|body|bool|break|byte|case|cast|catch|cdouble|cent|cfloat|char|class|const|continue|creal|dchar|debug|default|delegate|delete|deprecated|do|double|else|enum|export|extern|false|final|finally|float|for|foreach|foreach_reverse|function|goto|idouble|if|ifloat|immutable|import|inout|int|interface|invariant|ireal|lazy|long|macro|mixin|module|new|nothrow|null|out|override|package|pragma|private|protected|public|pure|real|ref|return|scope|shared|short|static|struct|super|switch|synchronized|template|this|throw|true|try|typedef|typeid|typeof|ubyte|ucent|uint|ulong|union|unittest|ushort|version|void|volatile|wchar|while|with|__(?:(?:FILE|MODULE|LINE|FUNCTION|PRETTY_FUNCTION|DATE|EOF|TIME|TIMESTAMP|VENDOR|VERSION)__|gshared|traits|vector|parameters)|string|wstring|dstring|size_t|ptrdiff_t)\b/,number:[/\b0x\.?[a-f\d_]+(?:(?!\.\.)\.[a-f\d_]*)?(?:p[+-]?[a-f\d_]+)?[ulfi]{0,4}/i,{pattern:/((?:\.\.)?)(?:\b0b\.?|\b|\.)\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:e[+-]?\d[\d_]*)?[ulfi]{0,4}/i,lookbehind:!0}],operator:/\|[|=]?|&[&=]?|\+[+=]?|-[-=]?|\.?\.\.|=[>=]?|!(?:i[ns]\b|<>?=?|>=?|=)?|\bi[ns]\b|(?:<[<>]?|>>?>?|\^\^|[*\/%^~])=?/}),e.languages.insertBefore("d","keyword",{property:/\B@\w*/}),e.languages.insertBefore("d","function",{register:{pattern:/\b(?:[ABCD][LHX]|E[ABCD]X|E?(?:BP|SP|DI|SI)|[ECSDGF]S|CR[0234]|DR[012367]|TR[3-7]|X?MM[0-7]|R[ABCD]X|[BS]PL|R[BS]P|[DS]IL|R[DS]I|R(?:[89]|1[0-5])[BWD]?|XMM(?:[89]|1[0-5])|YMM(?:1[0-5]|\d))\b|\bST(?:\([0-7]\)|\b)/,alias:"variable"}})}e.exports=t,t.displayName="d",t.aliases=[]},28651(e){"use strict";function t(e){var t,n,r,i;t=e,n=[/\b(?:async|sync|yield)\*/,/\b(?:abstract|assert|async|await|break|case|catch|class|const|continue|covariant|default|deferred|do|dynamic|else|enum|export|extension|external|extends|factory|final|finally|for|get|hide|if|implements|interface|import|in|library|mixin|new|null|on|operator|part|rethrow|return|set|show|static|super|switch|sync|this|throw|try|typedef|var|void|while|with|yield)\b/],i={pattern:RegExp((r=/(^|[^\w.])(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source)+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}}}},t.languages.dart=t.languages.extend("clike",{string:[{pattern:/r?("""|''')[\s\S]*?\1/,greedy:!0},{pattern:/r?(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0}],"class-name":[i,{pattern:RegExp(r+/[A-Z]\w*(?=\s+\w+\s*[;,=()])/.source),lookbehind:!0,inside:i.inside}],keyword:n,operator:/\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/}),t.languages.insertBefore("dart","function",{metadata:{pattern:/@\w+/,alias:"symbol"}}),t.languages.insertBefore("dart","class-name",{generics:{pattern:/<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/,inside:{"class-name":i,keyword:n,punctuation:/[<>(),.:]/,operator:/[?&|]/}}})}e.exports=t,t.displayName="dart",t.aliases=[]},55579(e){"use strict";function t(e){var t;(t=e).languages.dataweave={url:/\b[A-Za-z]+:\/\/[\w/:.?=&-]+|\burn:[\w:.?=&-]+/,property:{pattern:/(?:\b\w+#)?(?:"(?:\\.|[^\\"\r\n])*"|\b\w+)(?=\s*[:@])/,greedy:!0},string:{pattern:/(["'`])(?:\\[\s\S]|(?!\1)[^\\])*\1/,greedy:!0},"mime-type":/\b(?:text|audio|video|application|multipart|image)\/[\w+-]+/,date:{pattern:/\|[\w:+-]+\|/,greedy:!0},comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],regex:{pattern:/\/(?:[^\\\/\r\n]|\\[^\r\n])+\//,greedy:!0},function:/\b[A-Z_]\w*(?=\s*\()/i,number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\];(),.:@]/,operator:/<<|>>|->|[<>~=]=?|!=|--?-?|\+\+?|!|\?/,boolean:/\b(?:true|false)\b/,keyword:/\b(?:match|input|output|ns|type|update|null|if|else|using|unless|at|is|as|case|do|fun|var|not|and|or)\b/}}e.exports=t,t.displayName="dataweave",t.aliases=[]},93685(e){"use strict";function t(e){e.languages.dax={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/).*)/,lookbehind:!0},"data-field":{pattern:/'(?:[^']|'')*'(?!')(?:\[[ \w\xA0-\uFFFF]+\])?|\w+\[[ \w\xA0-\uFFFF]+\]/,alias:"symbol"},measure:{pattern:/\[[ \w\xA0-\uFFFF]+\]/,alias:"constant"},string:{pattern:/"(?:[^"]|"")*"(?!")/,greedy:!0},function:/\b(?:ABS|ACOS|ACOSH|ACOT|ACOTH|ADDCOLUMNS|ADDMISSINGITEMS|ALL|ALLCROSSFILTERED|ALLEXCEPT|ALLNOBLANKROW|ALLSELECTED|AND|APPROXIMATEDISTINCTCOUNT|ASIN|ASINH|ATAN|ATANH|AVERAGE|AVERAGEA|AVERAGEX|BETA\.DIST|BETA\.INV|BLANK|CALCULATE|CALCULATETABLE|CALENDAR|CALENDARAUTO|CEILING|CHISQ\.DIST|CHISQ\.DIST\.RT|CHISQ\.INV|CHISQ\.INV\.RT|CLOSINGBALANCEMONTH|CLOSINGBALANCEQUARTER|CLOSINGBALANCEYEAR|COALESCE|COMBIN|COMBINA|COMBINEVALUES|CONCATENATE|CONCATENATEX|CONFIDENCE\.NORM|CONFIDENCE\.T|CONTAINS|CONTAINSROW|CONTAINSSTRING|CONTAINSSTRINGEXACT|CONVERT|COS|COSH|COT|COTH|COUNT|COUNTA|COUNTAX|COUNTBLANK|COUNTROWS|COUNTX|CROSSFILTER|CROSSJOIN|CURRENCY|CURRENTGROUP|CUSTOMDATA|DATATABLE|DATE|DATEADD|DATEDIFF|DATESBETWEEN|DATESINPERIOD|DATESMTD|DATESQTD|DATESYTD|DATEVALUE|DAY|DEGREES|DETAILROWS|DISTINCT|DISTINCTCOUNT|DISTINCTCOUNTNOBLANK|DIVIDE|EARLIER|EARLIEST|EDATE|ENDOFMONTH|ENDOFQUARTER|ENDOFYEAR|EOMONTH|ERROR|EVEN|EXACT|EXCEPT|EXP|EXPON\.DIST|FACT|FALSE|FILTER|FILTERS|FIND|FIRSTDATE|FIRSTNONBLANK|FIRSTNONBLANKVALUE|FIXED|FLOOR|FORMAT|GCD|GENERATE|GENERATEALL|GENERATESERIES|GEOMEAN|GEOMEANX|GROUPBY|HASONEFILTER|HASONEVALUE|HOUR|IF|IF\.EAGER|IFERROR|IGNORE|INT|INTERSECT|ISBLANK|ISCROSSFILTERED|ISEMPTY|ISERROR|ISEVEN|ISFILTERED|ISINSCOPE|ISLOGICAL|ISNONTEXT|ISNUMBER|ISO\.CEILING|ISODD|ISONORAFTER|ISSELECTEDMEASURE|ISSUBTOTAL|ISTEXT|KEEPFILTERS|KEYWORDMATCH|LASTDATE|LASTNONBLANK|LASTNONBLANKVALUE|LCM|LEFT|LEN|LN|LOG|LOG10|LOOKUPVALUE|LOWER|MAX|MAXA|MAXX|MEDIAN|MEDIANX|MID|MIN|MINA|MINUTE|MINX|MOD|MONTH|MROUND|NATURALINNERJOIN|NATURALLEFTOUTERJOIN|NEXTDAY|NEXTMONTH|NEXTQUARTER|NEXTYEAR|NONVISUAL|NORM\.DIST|NORM\.INV|NORM\.S\.DIST|NORM\.S\.INV|NOT|NOW|ODD|OPENINGBALANCEMONTH|OPENINGBALANCEQUARTER|OPENINGBALANCEYEAR|OR|PARALLELPERIOD|PATH|PATHCONTAINS|PATHITEM|PATHITEMREVERSE|PATHLENGTH|PERCENTILE\.EXC|PERCENTILE\.INC|PERCENTILEX\.EXC|PERCENTILEX\.INC|PERMUT|PI|POISSON\.DIST|POWER|PREVIOUSDAY|PREVIOUSMONTH|PREVIOUSQUARTER|PREVIOUSYEAR|PRODUCT|PRODUCTX|QUARTER|QUOTIENT|RADIANS|RAND|RANDBETWEEN|RANK\.EQ|RANKX|RELATED|RELATEDTABLE|REMOVEFILTERS|REPLACE|REPT|RIGHT|ROLLUP|ROLLUPADDISSUBTOTAL|ROLLUPGROUP|ROLLUPISSUBTOTAL|ROUND|ROUNDDOWN|ROUNDUP|ROW|SAMEPERIODLASTYEAR|SAMPLE|SEARCH|SECOND|SELECTCOLUMNS|SELECTEDMEASURE|SELECTEDMEASUREFORMATSTRING|SELECTEDMEASURENAME|SELECTEDVALUE|SIGN|SIN|SINH|SQRT|SQRTPI|STARTOFMONTH|STARTOFQUARTER|STARTOFYEAR|STDEV\.P|STDEV\.S|STDEVX\.P|STDEVX\.S|SUBSTITUTE|SUBSTITUTEWITHINDEX|SUM|SUMMARIZE|SUMMARIZECOLUMNS|SUMX|SWITCH|T\.DIST|T\.DIST\.2T|T\.DIST\.RT|T\.INV|T\.INV\.2T|TAN|TANH|TIME|TIMEVALUE|TODAY|TOPN|TOPNPERLEVEL|TOPNSKIP|TOTALMTD|TOTALQTD|TOTALYTD|TREATAS|TRIM|TRUE|TRUNC|UNICHAR|UNICODE|UNION|UPPER|USERELATIONSHIP|USERNAME|USEROBJECTID|USERPRINCIPALNAME|UTCNOW|UTCTODAY|VALUE|VALUES|VAR\.P|VAR\.S|VARX\.P|VARX\.S|WEEKDAY|WEEKNUM|XIRR|XNPV|YEAR|YEARFRAC)(?=\s*\()/i,keyword:/\b(?:DEFINE|MEASURE|EVALUATE|ORDER\s+BY|RETURN|VAR|START\s+AT|ASC|DESC)\b/i,boolean:{pattern:/\b(?:TRUE|FALSE|NULL)\b/i,alias:"constant"},number:/\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/:=|[-+*\/=^]|&&?|\|\||<(?:=>?|<|>)?|>[>=]?|\b(?:IN|NOT)\b/i,punctuation:/[;\[\](){}`,.]/}}e.exports=t,t.displayName="dax",t.aliases=[]},13934(e){"use strict";function t(e){e.languages.dhall={comment:/--.*|\{-(?:[^-{]|-(?!\})|\{(?!-)|\{-(?:[^-{]|-(?!\})|\{(?!-))*-\})*-\}/,string:{pattern:/"(?:[^"\\]|\\.)*"|''(?:[^']|'(?!')|'''|''\$\{)*''(?!'|\$)/,greedy:!0,inside:{interpolation:{pattern:/\$\{[^{}]*\}/,inside:{expression:{pattern:/(^\$\{)[\s\S]+(?=\}$)/,lookbehind:!0,alias:"language-dhall",inside:null},punctuation:/\$\{|\}/}}}},label:{pattern:/`[^`]*`/,greedy:!0},url:{pattern:/\bhttps?:\/\/[\w.:%!$&'*+;=@~-]+(?:\/[\w.:%!$&'*+;=@~-]*)*(?:\?[/?\w.:%!$&'*+;=@~-]*)?/,greedy:!0},env:{pattern:/\benv:(?:(?!\d)\w+|"(?:[^"\\=]|\\.)*")/,greedy:!0,inside:{function:/^env/,operator:/^:/,variable:/[\s\S]+/}},hash:{pattern:/\bsha256:[\da-fA-F]{64}\b/,inside:{function:/sha256/,operator:/:/,number:/[\da-fA-F]{64}/}},keyword:/\b(?:as|assert|else|forall|if|in|let|merge|missing|then|toMap|using|with)\b|\u2200/,builtin:/\b(?:Some|None)\b/,boolean:/\b(?:False|True)\b/,number:/\bNaN\b|-?\bInfinity\b|[+-]?\b(?:0x[\da-fA-F]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b/,operator:/\/\\|\/\/\\\\|&&|\|\||===|[!=]=|\/\/|->|\+\+|::|[+*#@=:?<>|\\\u2227\u2a53\u2261\u2afd\u03bb\u2192]/,punctuation:/\.\.|[{}\[\](),./]/,"class-name":/\b[A-Z]\w*\b/},e.languages.dhall.string.inside.interpolation.inside.expression.inside=e.languages.dhall}e.exports=t,t.displayName="dhall",t.aliases=[]},93336(e){"use strict";function t(e){var t,n;(t=e).languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]},Object.keys(n={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"}).forEach(function(e){var r=n[e],i=[];/^\w+$/.test(e)||i.push(/\w+/.exec(e)[0]),"diff"===e&&i.push("bold"),t.languages.diff[e]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:i,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(e)[0]}}}}),Object.defineProperty(t.languages.diff,"PREFIXES",{value:n})}e.exports=t,t.displayName="diff",t.aliases=[]},13294(e,t,n){"use strict";var r=n(93205);function i(e){var t,n,i;e.register(r),(t=e).languages.django={comment:/^\{#[\s\S]*?#\}$/,tag:{pattern:/(^\{%[+-]?\s*)\w+/,lookbehind:!0,alias:"keyword"},delimiter:{pattern:/^\{[{%][+-]?|[+-]?[}%]\}$/,alias:"punctuation"},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},filter:{pattern:/(\|)\w+/,lookbehind:!0,alias:"function"},test:{pattern:/(\bis\s+(?:not\s+)?)(?!not\b)\w+/,lookbehind:!0,alias:"function"},function:/\b[a-z_]\w+(?=\s*\()/i,keyword:/\b(?:and|as|by|else|for|if|import|in|is|loop|not|or|recursive|with|without)\b/,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,number:/\b\d+(?:\.\d+)?\b/,boolean:/[Tt]rue|[Ff]alse|[Nn]one/,variable:/\b\w+?\b/,punctuation:/[{}[\](),.:;]/},n=/\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}|\{#[\s\S]*?#\}/g,i=t.languages["markup-templating"],t.hooks.add("before-tokenize",function(e){i.buildPlaceholders(e,"django",n)}),t.hooks.add("after-tokenize",function(e){i.tokenizePlaceholders(e,"django")}),t.languages.jinja2=t.languages.django,t.hooks.add("before-tokenize",function(e){i.buildPlaceholders(e,"jinja2",n)}),t.hooks.add("after-tokenize",function(e){i.tokenizePlaceholders(e,"jinja2")})}e.exports=i,i.displayName="django",i.aliases=["jinja2"]},38223(e){"use strict";function t(e){e.languages["dns-zone-file"]={comment:/;.*/,string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},variable:[{pattern:/(^\$ORIGIN[ \t]+)\S+/m,lookbehind:!0},{pattern:/(^|\s)@(?=\s|$)/,lookbehind:!0}],keyword:/^\$(?:ORIGIN|INCLUDE|TTL)(?=\s|$)/m,class:{pattern:/(^|\s)(?:IN|CH|CS|HS)(?=\s|$)/,lookbehind:!0,alias:"keyword"},type:{pattern:/(^|\s)(?:A|A6|AAAA|AFSDB|APL|ATMA|CAA|CDNSKEY|CDS|CERT|CNAME|DHCID|DLV|DNAME|DNSKEY|DS|EID|GID|GPOS|HINFO|HIP|IPSECKEY|ISDN|KEY|KX|LOC|MAILA|MAILB|MB|MD|MF|MG|MINFO|MR|MX|NAPTR|NB|NBSTAT|NIMLOC|NINFO|NS|NSAP|NSAP-PTR|NSEC|NSEC3|NSEC3PARAM|NULL|NXT|OPENPGPKEY|PTR|PX|RKEY|RP|RRSIG|RT|SIG|SINK|SMIMEA|SOA|SPF|SRV|SSHFP|TA|TKEY|TLSA|TSIG|TXT|UID|UINFO|UNSPEC|URI|WKS|X25)(?=\s|$)/,lookbehind:!0,alias:"keyword"},punctuation:/[()]/},e.languages["dns-zone"]=e.languages["dns-zone-file"]}e.exports=t,t.displayName="dnsZoneFile",t.aliases=[]},97266(e){"use strict";function t(e){!function(e){var t=/\\[\r\n](?:\s|\\[\r\n]|#.*(?!.))*(?![\s#]|\\[\r\n])/.source,n=/(?:[ \t]+(?![ \t])(?:)?|)/.source.replace(//g,function(){return t}),r=/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"|'(?:[^'\\\r\n]|\\(?:\r\n|[\s\S]))*'/.source,i=/--[\w-]+=(?:|(?!["'])(?:[^\s\\]|\\.)+)/.source.replace(//g,function(){return r}),a={pattern:RegExp(r),greedy:!0},o={pattern:/(^[ \t]*)#.*/m,lookbehind:!0,greedy:!0};function s(e,t){return e=e.replace(//g,function(){return i}).replace(//g,function(){return n}),RegExp(e,t)}e.languages.docker={instruction:{pattern:/(^[ \t]*)(?:ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|ONBUILD|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)(?=\s)(?:\\.|[^\r\n\\])*(?:\\$(?:\s|#.*$)*(?![\s#])(?:\\.|[^\r\n\\])*)*/im,lookbehind:!0,greedy:!0,inside:{options:{pattern:s(/(^(?:ONBUILD)?\w+)(?:)*/.source,"i"),lookbehind:!0,greedy:!0,inside:{property:{pattern:/(^|\s)--[\w-]+/,lookbehind:!0},string:[a,{pattern:/(=)(?!["'])(?:[^\s\\]|\\.)+/,lookbehind:!0}],operator:/\\$/m,punctuation:/=/}},keyword:[{pattern:s(/(^(?:ONBUILD)?HEALTHCHECK(?:)*)(?:CMD|NONE)\b/.source,"i"),lookbehind:!0,greedy:!0},{pattern:s(/(^(?:ONBUILD)?FROM(?:)*(?!--)[^ \t\\]+)AS/.source,"i"),lookbehind:!0,greedy:!0},{pattern:s(/(^ONBUILD)\w+/.source,"i"),lookbehind:!0,greedy:!0},{pattern:/^\w+/,greedy:!0}],comment:o,string:a,variable:/\$(?:\w+|\{[^{}"'\\]*\})/,operator:/\\$/m}},comment:o},e.languages.dockerfile=e.languages.docker}(e)}e.exports=t,t.displayName="docker",t.aliases=["dockerfile"]},80636(e){"use strict";function t(e){!function(e){var t="(?:"+[/[a-zA-Z_\x80-\uFFFF][\w\x80-\uFFFF]*/.source,/-?(?:\.\d+|\d+(?:\.\d*)?)/.source,/"[^"\\]*(?:\\[\s\S][^"\\]*)*"/.source,/<(?:[^<>]|(?!)*>/.source].join("|")+")",n={markup:{pattern:/(^<)[\s\S]+(?=>$)/,lookbehind:!0,alias:["language-markup","language-html","language-xml"],inside:e.languages.markup}};function r(e,n){return RegExp(e.replace(//g,function(){return t}),n)}e.languages.dot={comment:{pattern:/\/\/.*|\/\*[\s\S]*?\*\/|^#.*/m,greedy:!0},"graph-name":{pattern:r(/(\b(?:digraph|graph|subgraph)[ \t\r\n]+)/.source,"i"),lookbehind:!0,greedy:!0,alias:"class-name",inside:n},"attr-value":{pattern:r(/(=[ \t\r\n]*)/.source),lookbehind:!0,greedy:!0,inside:n},"attr-name":{pattern:r(/([\[;, \t\r\n])(?=[ \t\r\n]*=)/.source),lookbehind:!0,greedy:!0,inside:n},keyword:/\b(?:digraph|edge|graph|node|strict|subgraph)\b/i,"compass-point":{pattern:/(:[ \t\r\n]*)(?:[ns][ew]?|[ewc_])(?![\w\x80-\uFFFF])/,lookbehind:!0,alias:"builtin"},node:{pattern:r(/(^|[^-.\w\x80-\uFFFF\\])/.source),lookbehind:!0,greedy:!0,inside:n},operator:/[=:]|-[->]/,punctuation:/[\[\]{};,]/},e.languages.gv=e.languages.dot}(e)}e.exports=t,t.displayName="dot",t.aliases=["gv"]},36500(e){"use strict";function t(e){e.languages.ebnf={comment:/\(\*[\s\S]*?\*\)/,string:{pattern:/"[^"\r\n]*"|'[^'\r\n]*'/,greedy:!0},special:{pattern:/\?[^?\r\n]*\?/,greedy:!0,alias:"class-name"},definition:{pattern:/^([\t ]*)[a-z]\w*(?:[ \t]+[a-z]\w*)*(?=\s*=)/im,lookbehind:!0,alias:["rule","keyword"]},rule:/\b[a-z]\w*(?:[ \t]+[a-z]\w*)*\b/i,punctuation:/\([:/]|[:/]\)|[.,;()[\]{}]/,operator:/[-=|*/!]/}}e.exports=t,t.displayName="ebnf",t.aliases=[]},30296(e){"use strict";function t(e){e.languages.editorconfig={comment:/[;#].*/,section:{pattern:/(^[ \t]*)\[.+\]/m,lookbehind:!0,alias:"keyword",inside:{regex:/\\\\[\[\]{},!?.*]/,operator:/[!?]|\.\.|\*{1,2}/,punctuation:/[\[\]{},]/}},property:{pattern:/(^[ \t]*)[^\s=]+(?=[ \t]*=)/m,lookbehind:!0},value:{pattern:/=.*/,alias:"string",inside:{punctuation:/^=/}}}}e.exports=t,t.displayName="editorconfig",t.aliases=[]},50115(e){"use strict";function t(e){e.languages.eiffel={comment:/--.*/,string:[{pattern:/"([^[]*)\[[\s\S]*?\]\1"/,greedy:!0},{pattern:/"([^{]*)\{[\s\S]*?\}\1"/,greedy:!0},{pattern:/"(?:%(?:(?!\n)\s)*\n\s*%|%\S|[^%"\r\n])*"/,greedy:!0}],char:/'(?:%.|[^%'\r\n])+'/,keyword:/\b(?:across|agent|alias|all|and|attached|as|assign|attribute|check|class|convert|create|Current|debug|deferred|detachable|do|else|elseif|end|ensure|expanded|export|external|feature|from|frozen|if|implies|inherit|inspect|invariant|like|local|loop|not|note|obsolete|old|once|or|Precursor|redefine|rename|require|rescue|Result|retry|select|separate|some|then|undefine|until|variant|Void|when|xor)\b/i,boolean:/\b(?:True|False)\b/i,"class-name":{pattern:/\b[A-Z][\dA-Z_]*\b/,alias:"builtin"},number:[/\b0[xcb][\da-f](?:_*[\da-f])*\b/i,/(?:\b\d(?:_*\d)*)?\.(?:(?:\d(?:_*\d)*)?e[+-]?)?\d(?:_*\d)*\b|\b\d(?:_*\d)*\b\.?/i],punctuation:/:=|<<|>>|\(\||\|\)|->|\.(?=\w)|[{}[\];(),:?]/,operator:/\\\\|\|\.\.\||\.\.|\/[~\/=]?|[><]=?|[-+*^=~]/}}e.exports=t,t.displayName="eiffel",t.aliases=[]},20791(e,t,n){"use strict";var r=n(93205);function i(e){var t;e.register(r),(t=e).languages.ejs={delimiter:{pattern:/^<%[-_=]?|[-_]?%>$/,alias:"punctuation"},comment:/^#[\s\S]*/,"language-javascript":{pattern:/[\s\S]+/,inside:t.languages.javascript}},t.hooks.add("before-tokenize",function(e){var n=/<%(?!%)[\s\S]+?%>/g;t.languages["markup-templating"].buildPlaceholders(e,"ejs",n)}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"ejs")}),t.languages.eta=t.languages.ejs}e.exports=i,i.displayName="ejs",i.aliases=["eta"]},11974(e){"use strict";function t(e){e.languages.elixir={doc:{pattern:/@(?:doc|moduledoc)\s+(?:("""|''')[\s\S]*?\1|("|')(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2)/,inside:{attribute:/^@\w+/,string:/['"][\s\S]+/}},comment:{pattern:/#.*/m,greedy:!0},regex:{pattern:/~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,greedy:!0},string:[{pattern:/~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|#(?!\{)|[^#\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,greedy:!0,inside:{}},{pattern:/("""|''')[\s\S]*?\1/,greedy:!0,inside:{}},{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{}}],atom:{pattern:/(^|[^:]):\w+/,lookbehind:!0,alias:"symbol"},module:{pattern:/\b[A-Z]\w*\b/,alias:"class-name"},"attr-name":/\b\w+\??:(?!:)/,argument:{pattern:/(^|[^&])&\d+/,lookbehind:!0,alias:"variable"},attribute:{pattern:/@\w+/,alias:"variable"},function:/\b[_a-zA-Z]\w*[?!]?(?:(?=\s*(?:\.\s*)?\()|(?=\/\d))/,number:/\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,keyword:/\b(?:after|alias|and|case|catch|cond|def(?:callback|delegate|exception|impl|macro|module|n|np|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|quote|raise|require|rescue|try|unless|unquote|use|when)\b/,boolean:/\b(?:true|false|nil)\b/,operator:[/\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,{pattern:/([^<])<(?!<)/,lookbehind:!0},{pattern:/([^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,%\[\]{}()]/},e.languages.elixir.string.forEach(function(t){t.inside={interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:e.languages.elixir}}}})}e.exports=t,t.displayName="elixir",t.aliases=[]},8645(e){"use strict";function t(e){e.languages.elm={comment:/--.*|\{-[\s\S]*?-\}/,char:{pattern:/'(?:[^\\'\r\n]|\\(?:[abfnrtv\\']|\d+|x[0-9a-fA-F]+))'/,greedy:!0},string:[{pattern:/"""[\s\S]*?"""/,greedy:!0},{pattern:/"(?:[^\\"\r\n]|\\.)*"/,greedy:!0}],"import-statement":{pattern:/(^[\t ]*)import\s+[A-Z]\w*(?:\.[A-Z]\w*)*(?:\s+as\s+(?:[A-Z]\w*)(?:\.[A-Z]\w*)*)?(?:\s+exposing\s+)?/m,lookbehind:!0,inside:{keyword:/\b(?:import|as|exposing)\b/}},keyword:/\b(?:alias|as|case|else|exposing|if|in|infixl|infixr|let|module|of|then|type)\b/,builtin:/\b(?:abs|acos|always|asin|atan|atan2|ceiling|clamp|compare|cos|curry|degrees|e|flip|floor|fromPolar|identity|isInfinite|isNaN|logBase|max|min|negate|never|not|pi|radians|rem|round|sin|sqrt|tan|toFloat|toPolar|toString|truncate|turns|uncurry|xor)\b/,number:/\b(?:\d+(?:\.\d+)?(?:e[+-]?\d+)?|0x[0-9a-f]+)\b/i,operator:/\s\.\s|[+\-/*=.$<>:&|^?%#@~!]{2,}|[+\-/*=$<>:&|^?%#@~!]/,hvariable:/\b(?:[A-Z]\w*\.)*[a-z]\w*\b/,constant:/\b(?:[A-Z]\w*\.)*[A-Z]\w*\b/,punctuation:/[{}[\]|(),.:]/}}e.exports=t,t.displayName="elm",t.aliases=[]},84790(e,t,n){"use strict";var r=n(56939),i=n(93205);function a(e){var t;e.register(r),e.register(i),(t=e).languages.erb=t.languages.extend("ruby",{}),t.languages.insertBefore("erb","comment",{delimiter:{pattern:/^<%=?|%>$/,alias:"punctuation"}}),t.hooks.add("before-tokenize",function(e){var n=/<%=?(?:[^\r\n]|[\r\n](?!=begin)|[\r\n]=begin\s(?:[^\r\n]|[\r\n](?!=end))*[\r\n]=end)+?%>/gm;t.languages["markup-templating"].buildPlaceholders(e,"erb",n)}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"erb")})}e.exports=a,a.displayName="erb",a.aliases=[]},4502(e){"use strict";function t(e){e.languages.erlang={comment:/%.+/,string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},"quoted-function":{pattern:/'(?:\\.|[^\\'\r\n])+'(?=\()/,alias:"function"},"quoted-atom":{pattern:/'(?:\\.|[^\\'\r\n])+'/,alias:"atom"},boolean:/\b(?:true|false)\b/,keyword:/\b(?:fun|when|case|of|end|if|receive|after|try|catch)\b/,number:[/\$\\?./,/\b\d+#[a-z0-9]+/i,/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i],function:/\b[a-z][\w@]*(?=\()/,variable:{pattern:/(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,lookbehind:!0},operator:[/[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:bnot|div|rem|band|bor|bxor|bsl|bsr|not|and|or|xor|orelse|andalso)\b/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],atom:/\b[a-z][\w@]*/,punctuation:/[()[\]{}:;,.#|]|<<|>>/}}e.exports=t,t.displayName="erlang",t.aliases=[]},66055(e,t,n){"use strict";var r=n(59803),i=n(93205);function a(e){var t;e.register(r),e.register(i),(t=e).languages.etlua={delimiter:{pattern:/^<%[-=]?|-?%>$/,alias:"punctuation"},"language-lua":{pattern:/[\s\S]+/,inside:t.languages.lua}},t.hooks.add("before-tokenize",function(e){var n=/<%[\s\S]+?%>/g;t.languages["markup-templating"].buildPlaceholders(e,"etlua",n)}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"etlua")})}e.exports=a,a.displayName="etlua",a.aliases=[]},68876(e){"use strict";function t(e){e.languages["excel-formula"]={comment:{pattern:/(\bN\(\s*)"(?:[^"]|"")*"(?=\s*\))/i,lookbehind:!0,greedy:!0},string:{pattern:/"(?:[^"]|"")*"(?!")/,greedy:!0},reference:{pattern:/(?:'[^']*'|(?:[^\s()[\]{}<>*?"';,$&]*\[[^^\s()[\]{}<>*?"']+\])?\w+)!/,greedy:!0,alias:"string",inside:{operator:/!$/,punctuation:/'/,sheet:{pattern:/[^[\]]+$/,alias:"function"},file:{pattern:/\[[^[\]]+\]$/,inside:{punctuation:/[[\]]/}},path:/[\s\S]+/}},"function-name":{pattern:/\b[A-Z]\w*(?=\()/i,alias:"keyword"},range:{pattern:/\$?\b(?:[A-Z]+\$?\d+:\$?[A-Z]+\$?\d+|[A-Z]+:\$?[A-Z]+|\d+:\$?\d+)\b/i,alias:"property",inside:{operator:/:/,cell:/\$?[A-Z]+\$?\d+/i,column:/\$?[A-Z]+/i,row:/\$?\d+/}},cell:{pattern:/\b[A-Z]+\d+\b|\$[A-Za-z]+\$?\d+\b|\b[A-Za-z]+\$\d+\b/,alias:"property"},number:/(?:\b\d+(?:\.\d+)?|\B\.\d+)(?:e[+-]?\d+)?\b/i,boolean:/\b(?:TRUE|FALSE)\b/i,operator:/[-+*/^%=&,]|<[=>]?|>=?/,punctuation:/[[\]();{}|]/},e.languages.xlsx=e.languages.xls=e.languages["excel-formula"]}e.exports=t,t.displayName="excelFormula",t.aliases=[]},95126(e){"use strict";function t(e){var t,n,r,i,a,o,s,u;t=e,i={comment:[{pattern:/(^|\s)(?:! .*|!$)/,lookbehind:!0,inside:n={function:/\b(?:TODOS?|FIX(?:MES?)?|NOTES?|BUGS?|XX+|HACKS?|WARN(?:ING)?|\?{2,}|!{2,})\b/}},{pattern:/(^|\s)\/\*\s[\s\S]*?\*\/(?=\s|$)/,lookbehind:!0,greedy:!0,inside:n},{pattern:/(^|\s)!\[(={0,6})\[\s[\s\S]*?\]\2\](?=\s|$)/,lookbehind:!0,greedy:!0,inside:n}],number:[{pattern:/(^|\s)[+-]?\d+(?=\s|$)/,lookbehind:!0},{pattern:/(^|\s)[+-]?0(?:b[01]+|o[0-7]+|d\d+|x[\dA-F]+)(?=\s|$)/i,lookbehind:!0},{pattern:/(^|\s)[+-]?\d+\/\d+\.?(?=\s|$)/,lookbehind:!0},{pattern:/(^|\s)\+?\d+\+\d+\/\d+(?=\s|$)/,lookbehind:!0},{pattern:/(^|\s)-\d+-\d+\/\d+(?=\s|$)/,lookbehind:!0},{pattern:/(^|\s)[+-]?(?:\d*\.\d+|\d+\.\d*|\d+)(?:e[+-]?\d+)?(?=\s|$)/i,lookbehind:!0},{pattern:/(^|\s)NAN:\s+[\da-fA-F]+(?=\s|$)/,lookbehind:!0},{pattern:/(^|\s)[+-]?0(?:b1\.[01]*|o1\.[0-7]*|d1\.\d*|x1\.[\dA-F]*)p\d+(?=\s|$)/i,lookbehind:!0}],regexp:{pattern:/(^|\s)R\/\s(?:\\\S|[^\\/])*\/(?:[idmsr]*|[idmsr]+-[idmsr]+)(?=\s|$)/,lookbehind:!0,alias:"number",inside:{variable:/\\\S/,keyword:/[+?*\[\]^$(){}.|]/,operator:{pattern:/(\/)[idmsr]+(?:-[idmsr]+)?/,lookbehind:!0}}},boolean:{pattern:/(^|\s)[tf](?=\s|$)/,lookbehind:!0},"custom-string":{pattern:/(^|\s)[A-Z0-9\-]+"\s(?:\\\S|[^"\\])*"/,lookbehind:!0,greedy:!0,alias:"string",inside:{number:/\\\S|%\w|\//}},"multiline-string":[{pattern:/(^|\s)STRING:\s+\S+(?:\n|\r\n).*(?:\n|\r\n)\s*;(?=\s|$)/,lookbehind:!0,greedy:!0,alias:"string",inside:{number:(r={number:/\\[^\s']|%\w/}).number,"semicolon-or-setlocal":{pattern:/([\r\n][ \t]*);(?=\s|$)/,lookbehind:!0,alias:"function"}}},{pattern:/(^|\s)HEREDOC:\s+\S+(?:\n|\r\n).*(?:\n|\r\n)\s*\S+(?=\s|$)/,lookbehind:!0,greedy:!0,alias:"string",inside:r},{pattern:/(^|\s)\[(={0,6})\[\s[\s\S]*?\]\2\](?=\s|$)/,lookbehind:!0,greedy:!0,alias:"string",inside:r}],"special-using":{pattern:/(^|\s)USING:(?:\s\S+)*(?=\s+;(?:\s|$))/,lookbehind:!0,alias:"function",inside:{string:{pattern:/(\s)[^:\s]+/,lookbehind:!0}}},"stack-effect-delimiter":[{pattern:/(^|\s)(?:call|execute|eval)?\((?=\s)/,lookbehind:!0,alias:"operator"},{pattern:/(\s)--(?=\s)/,lookbehind:!0,alias:"operator"},{pattern:/(\s)\)(?=\s|$)/,lookbehind:!0,alias:"operator"}],combinators:{pattern:null,lookbehind:!0,alias:"keyword"},"kernel-builtin":{pattern:null,lookbehind:!0,alias:"variable"},"sequences-builtin":{pattern:null,lookbehind:!0,alias:"variable"},"math-builtin":{pattern:null,lookbehind:!0,alias:"variable"},"constructor-word":{pattern:/(^|\s)<(?!=+>|-+>)\S+>(?=\s|$)/,lookbehind:!0,alias:"keyword"},"other-builtin-syntax":{pattern:null,lookbehind:!0,alias:"operator"},"conventionally-named-word":{pattern:/(^|\s)(?!")(?:(?:set|change|with|new)-\S+|\$\S+|>[^>\s]+|[^:>\s]+>|[^>\s]+>[^>\s]+|\+[^+\s]+\+|[^?\s]+\?|\?[^?\s]+|[^>\s]+>>|>>[^>\s]+|[^<\s]+<<|\([^()\s]+\)|[^!\s]+!|[^*\s]\S*\*|[^.\s]\S*\.)(?=\s|$)/,lookbehind:!0,alias:"keyword"},"colon-syntax":{pattern:/(^|\s)(?:[A-Z0-9\-]+#?)?:{1,2}\s+(?:;\S+|(?!;)\S+)(?=\s|$)/,lookbehind:!0,greedy:!0,alias:"function"},"semicolon-or-setlocal":{pattern:/(\s)(?:;|:>)(?=\s|$)/,lookbehind:!0,alias:"function"},"curly-brace-literal-delimiter":[{pattern:/(^|\s)[a-z]*\{(?=\s)/i,lookbehind:!0,alias:"operator"},{pattern:/(\s)\}(?=\s|$)/,lookbehind:!0,alias:"operator"}],"quotation-delimiter":[{pattern:/(^|\s)\[(?=\s)/,lookbehind:!0,alias:"operator"},{pattern:/(\s)\](?=\s|$)/,lookbehind:!0,alias:"operator"}],"normal-word":{pattern:/(^|\s)[^"\s]\S*(?=\s|$)/,lookbehind:!0},string:{pattern:/"(?:\\\S|[^"\\])*"/,greedy:!0,inside:r}},a=function(e){return(e+"").replace(/([.?*+\^$\[\]\\(){}|\-])/g,"\\$1")},o=function(e){return RegExp("(^|\\s)(?:"+e.map(a).join("|")+")(?=\\s|$)")},Object.keys(s={"kernel-builtin":["or","2nipd","4drop","tuck","wrapper","nip","wrapper?","callstack>array","die","dupd","callstack","callstack?","3dup","hashcode","pick","4nip","build",">boolean","nipd","clone","5nip","eq?","?","=","swapd","2over","clear","2dup","get-retainstack","not","tuple?","dup","3nipd","call","-rotd","object","drop","assert=","assert?","-rot","execute","boa","get-callstack","curried?","3drop","pickd","overd","over","roll","3nip","swap","and","2nip","rotd","throw","(clone)","hashcode*","spin","reach","4dup","equal?","get-datastack","assert","2drop","","boolean?","identity-hashcode","identity-tuple?","null","composed?","new","5drop","rot","-roll","xor","identity-tuple","boolean"],"other-builtin-syntax":["=======","recursive","flushable",">>","<<<<<<","M\\","B","PRIVATE>","\\","======","final","inline","delimiter","deprecated",">>>>>","<<<<<<<","parse-complex","malformed-complex","read-only",">>>>>>>","call-next-method","<<","foldable","$","$[","${"],"sequences-builtin":["member-eq?","mismatch","append","assert-sequence=","longer","repetition","clone-like","3sequence","assert-sequence?","last-index-from","reversed","index-from","cut*","pad-tail","join-as","remove-eq!","concat-as","but-last","snip","nths","nth","sequence","longest","slice?","","remove-nth","tail-slice","empty?","tail*","member?","virtual-sequence?","set-length","drop-prefix","iota","unclip","bounds-error?","unclip-last-slice","non-negative-integer-expected","non-negative-integer-expected?","midpoint@","longer?","?set-nth","?first","rest-slice","prepend-as","prepend","fourth","sift","subseq-start","new-sequence","?last","like","first4","1sequence","reverse","slice","virtual@","repetition?","set-last","index","4sequence","max-length","set-second","immutable-sequence","first2","first3","supremum","unclip-slice","suffix!","insert-nth","tail","3append","short","suffix","concat","flip","immutable?","reverse!","2sequence","sum","delete-all","indices","snip-slice","","check-slice","sequence?","head","append-as","halves","sequence=","collapse-slice","?second","slice-error?","product","bounds-check?","bounds-check","immutable","virtual-exemplar","harvest","remove","pad-head","last","set-fourth","cartesian-product","remove-eq","shorten","shorter","reversed?","shorter?","shortest","head-slice","pop*","tail-slice*","but-last-slice","iota?","append!","cut-slice","new-resizable","head-slice*","sequence-hashcode","pop","set-nth","?nth","second","join","immutable-sequence?","","3append-as","virtual-sequence","subseq?","remove-nth!","length","last-index","lengthen","assert-sequence","copy","move","third","first","tail?","set-first","prefix","bounds-error","","exchange","surround","cut","min-length","set-third","push-all","head?","subseq-start-from","delete-slice","rest","sum-lengths","head*","infimum","remove!","glue","slice-error","subseq","push","replace-slice","subseq-as","unclip-last"],"math-builtin":["number=","next-power-of-2","?1+","fp-special?","imaginary-part","float>bits","number?","fp-infinity?","bignum?","fp-snan?","denominator","gcd","*","+","fp-bitwise=","-","u>=","/",">=","bitand","power-of-2?","log2-expects-positive","neg?","<","log2",">","integer?","number","bits>double","2/","zero?","bits>float","float?","shift","ratio?","rect>","even?","ratio","fp-sign","bitnot",">fixnum","complex?","/i","integer>fixnum","/f","sgn",">bignum","next-float","u<","u>","mod","recip","rational",">float","2^","integer","fixnum?","neg","fixnum","sq","bignum",">rect","bit?","fp-qnan?","simple-gcd","complex","","real",">fraction","double>bits","bitor","rem","fp-nan-payload","real-part","log2-expects-positive?","prev-float","align","unordered?","float","fp-nan?","abs","bitxor","integer>fixnum-strict","u<=","odd?","<=","/mod",">integer","real?","rational?","numerator"]}).forEach(function(e){i[e].pattern=o(s[e])}),u=["2bi","while","2tri","bi*","4dip","both?","same?","tri@","curry","prepose","3bi","?if","tri*","2keep","3keep","curried","2keepd","when","2bi*","2tri*","4keep","bi@","keepdd","do","unless*","tri-curry","if*","loop","bi-curry*","when*","2bi@","2tri@","with","2with","either?","bi","until","3dip","3curry","tri-curry*","tri-curry@","bi-curry","keepd","compose","2dip","if","3tri","unless","tuple","keep","2curry","tri","most","while*","dip","composed","bi-curry@","find-last-from","trim-head-slice","map-as","each-from","none?","trim-tail","partition","if-empty","accumulate*","reject!","find-from","accumulate-as","collector-for-as","reject","map","map-sum","accumulate!","2each-from","follow","supremum-by","map!","unless-empty","collector","padding","reduce-index","replicate-as","infimum-by","trim-tail-slice","count","find-index","filter","accumulate*!","reject-as","map-integers","map-find","reduce","selector","interleave","2map","filter-as","binary-reduce","map-index-as","find","produce","filter!","replicate","cartesian-map","cartesian-each","find-index-from","map-find-last","3map-as","3map","find-last","selector-as","2map-as","2map-reduce","accumulate","each","each-index","accumulate*-as","when-empty","all?","collector-as","push-either","new-like","collector-for","2selector","push-if","2all?","map-reduce","3each","any?","trim-slice","2reduce","change-nth","produce-as","2each","trim","trim-head","cartesian-find","map-index","if-zero","each-integer","unless-zero","(find-integer)","when-zero","find-last-integer","(all-integers?)","times","(each-integer)","find-integer","all-integers?","unless-negative","if-positive","when-positive","when-negative","unless-positive","if-negative","case","2cleave","cond>quot","case>quot","3cleave","wrong-values","to-fixed-point","alist>quot","cond","cleave","call-effect","recursive-hashcode","spread","deep-spread>quot","2||","0||","n||","0&&","2&&","3||","1||","1&&","n&&","3&&","smart-unless*","keep-inputs","reduce-outputs","smart-when*","cleave>array","smart-with","smart-apply","smart-if","inputs/outputs","output>sequence-n","map-outputs","map-reduce-outputs","dropping","output>array","smart-map-reduce","smart-2map-reduce","output>array-n","nullary","inputsequence"],i.combinators.pattern=o(u),t.languages.factor=i}e.exports=t,t.displayName="factor",t.aliases=[]},74644(e){"use strict";function t(e){var t;(t=e).languages.false={comment:{pattern:/\{[^}]*\}/},string:{pattern:/"[^"]*"/,greedy:!0},"character-code":{pattern:/'(?:[^\r]|\r\n?)/,alias:"number"},"assembler-code":{pattern:/\d+`/,alias:"important"},number:/\d+/,operator:/[-!#$%&'*+,./:;=>?@\\^_`|~ßø]/,punctuation:/\[|\]/,variable:/[a-z]/,"non-standard":{pattern:/[()!=]=?|[-+*/%]|\b(?:in|is)\b/}),delete e.languages["firestore-security-rules"]["class-name"],e.languages.insertBefore("firestore-security-rules","keyword",{path:{pattern:/(^|[\s(),])(?:\/(?:[\w\xA0-\uFFFF]+|\{[\w\xA0-\uFFFF]+(?:=\*\*)?\}|\$\([\w\xA0-\uFFFF.]+\)))+/,lookbehind:!0,greedy:!0,inside:{variable:{pattern:/\{[\w\xA0-\uFFFF]+(?:=\*\*)?\}|\$\([\w\xA0-\uFFFF.]+\)/,inside:{operator:/=/,keyword:/\*\*/,punctuation:/[.$(){}]/}},punctuation:/\//}},method:{pattern:/(\ballow\s+)[a-z]+(?:\s*,\s*[a-z]+)*(?=\s*[:;])/,lookbehind:!0,alias:"builtin",inside:{punctuation:/,/}}})}e.exports=t,t.displayName="firestoreSecurityRules",t.aliases=[]},37225(e){"use strict";function t(e){var t;(t=e).languages.flow=t.languages.extend("javascript",{}),t.languages.insertBefore("flow","keyword",{type:[{pattern:/\b(?:[Nn]umber|[Ss]tring|[Bb]oolean|Function|any|mixed|null|void)\b/,alias:"tag"}]}),t.languages.flow["function-variable"].pattern=/(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=\s*(?:function\b|(?:\([^()]*\)(?:\s*:\s*\w+)?|(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/i,delete t.languages.flow.parameter,t.languages.insertBefore("flow","operator",{"flow-punctuation":{pattern:/\{\||\|\}/,alias:"punctuation"}}),Array.isArray(t.languages.flow.keyword)||(t.languages.flow.keyword=[t.languages.flow.keyword]),t.languages.flow.keyword.unshift({pattern:/(^|[^$]\b)(?:type|opaque|declare|Class)\b(?!\$)/,lookbehind:!0},{pattern:/(^|[^$]\B)\$(?:await|Diff|Exact|Keys|ObjMap|PropertyType|Shape|Record|Supertype|Subtype|Enum)\b(?!\$)/,lookbehind:!0})}e.exports=t,t.displayName="flow",t.aliases=[]},16725(e){"use strict";function t(e){e.languages.fortran={"quoted-number":{pattern:/[BOZ](['"])[A-F0-9]+\1/i,alias:"number"},string:{pattern:/(?:\b\w+_)?(['"])(?:\1\1|&(?:\r\n?|\n)(?:[ \t]*!.*(?:\r\n?|\n)|(?![ \t]*!))|(?!\1).)*(?:\1|&)/,inside:{comment:{pattern:/(&(?:\r\n?|\n)\s*)!.*/,lookbehind:!0}}},comment:{pattern:/!.*/,greedy:!0},boolean:/\.(?:TRUE|FALSE)\.(?:_\w+)?/i,number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[ED][+-]?\d+)?(?:_\w+)?/i,keyword:[/\b(?:INTEGER|REAL|DOUBLE ?PRECISION|COMPLEX|CHARACTER|LOGICAL)\b/i,/\b(?:END ?)?(?:BLOCK ?DATA|DO|FILE|FORALL|FUNCTION|IF|INTERFACE|MODULE(?! PROCEDURE)|PROGRAM|SELECT|SUBROUTINE|TYPE|WHERE)\b/i,/\b(?:ALLOCATABLE|ALLOCATE|BACKSPACE|CALL|CASE|CLOSE|COMMON|CONTAINS|CONTINUE|CYCLE|DATA|DEALLOCATE|DIMENSION|DO|END|EQUIVALENCE|EXIT|EXTERNAL|FORMAT|GO ?TO|IMPLICIT(?: NONE)?|INQUIRE|INTENT|INTRINSIC|MODULE PROCEDURE|NAMELIST|NULLIFY|OPEN|OPTIONAL|PARAMETER|POINTER|PRINT|PRIVATE|PUBLIC|READ|RETURN|REWIND|SAVE|SELECT|STOP|TARGET|WHILE|WRITE)\b/i,/\b(?:ASSIGNMENT|DEFAULT|ELEMENTAL|ELSE|ELSEWHERE|ELSEIF|ENTRY|IN|INCLUDE|INOUT|KIND|NULL|ONLY|OPERATOR|OUT|PURE|RECURSIVE|RESULT|SEQUENCE|STAT|THEN|USE)\b/i],operator:[/\*\*|\/\/|=>|[=\/]=|[<>]=?|::|[+\-*=%]|\.[A-Z]+\./i,{pattern:/(^|(?!\().)\/(?!\))/,lookbehind:!0}],punctuation:/\(\/|\/\)|[(),;:&]/}}e.exports=t,t.displayName="fortran",t.aliases=[]},95559(e){"use strict";function t(e){e.languages.fsharp=e.languages.extend("clike",{comment:[{pattern:/(^|[^\\])\(\*(?!\))[\s\S]*?\*\)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(?:"""[\s\S]*?"""|@"(?:""|[^"])*"|"(?:\\[\s\S]|[^\\"])*")B?|'(?:[^\\']|\\(?:.|\d{3}|x[a-fA-F\d]{2}|u[a-fA-F\d]{4}|U[a-fA-F\d]{8}))'B?/,greedy:!0},"class-name":{pattern:/(\b(?:exception|inherit|interface|new|of|type)\s+|\w\s*:\s*|\s:\??>\s*)[.\w]+\b(?:\s*(?:->|\*)\s*[.\w]+\b)*(?!\s*[:.])/,lookbehind:!0,inside:{operator:/->|\*/,punctuation:/\./}},keyword:/\b(?:let|return|use|yield)(?:!\B|\b)|\b(?:abstract|and|as|assert|base|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|global|if|in|inherit|inline|interface|internal|lazy|match|member|module|mutable|namespace|new|not|null|of|open|or|override|private|public|rec|select|static|struct|then|to|true|try|type|upcast|val|void|when|while|with|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|include|method|mixin|object|parallel|process|protected|pure|sealed|tailcall|trait|virtual|volatile)\b/,number:[/\b0x[\da-fA-F]+(?:un|lf|LF)?\b/,/\b0b[01]+(?:y|uy)?\b/,/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[fm]|e[+-]?\d+)?\b/i,/\b\d+(?:[IlLsy]|u[lsy]?|UL)?\b/],operator:/([<>~&^])\1\1|([*.:<>&])\2|<-|->|[!=:]=|?|\??(?:<=|>=|<>|[-+*/%=<>])\??|[!?^&]|~[+~-]|:>|:\?>?/}),e.languages.insertBefore("fsharp","keyword",{preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(^#)\b(?:else|endif|if|light|line|nowarn)\b/,lookbehind:!0,alias:"keyword"}}}}),e.languages.insertBefore("fsharp","punctuation",{"computation-expression":{pattern:/\b[_a-z]\w*(?=\s*\{)/i,alias:"keyword"}}),e.languages.insertBefore("fsharp","string",{annotation:{pattern:/\[<.+?>\]/,inside:{punctuation:/^\[<|>\]$/,"class-name":{pattern:/^\w+$|(^|;\s*)[A-Z]\w*(?=\()/,lookbehind:!0},"annotation-content":{pattern:/[\s\S]+/,inside:e.languages.fsharp}}}})}e.exports=t,t.displayName="fsharp",t.aliases=[]},82114(e,t,n){"use strict";var r=n(93205);function i(e){e.register(r),function(e){for(var t=/[^<()"']|\((?:)*\)|<(?!#--)|<#--(?:[^-]|-(?!->))*-->|"(?:[^\\"]|\\.)*"|'(?:[^\\']|\\.)*'/.source,n=0;n<2;n++)t=t.replace(//g,function(){return t});t=t.replace(//g,/[^\s\S]/.source);var r={comment:/<#--[\s\S]*?-->/,string:[{pattern:/\br("|')(?:(?!\1)[^\\]|\\.)*\1/,greedy:!0},{pattern:RegExp(/("|')(?:(?!\1|\$\{)[^\\]|\\.|\$\{(?:(?!\})(?:))*\})*\1/.source.replace(//g,function(){return t})),greedy:!0,inside:{interpolation:{pattern:RegExp(/((?:^|[^\\])(?:\\\\)*)\$\{(?:(?!\})(?:))*\}/.source.replace(//g,function(){return t})),lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:null}}}}],keyword:/\b(?:as)\b/,boolean:/\b(?:true|false)\b/,"builtin-function":{pattern:/((?:^|[^?])\?\s*)\w+/,lookbehind:!0,alias:"function"},function:/\b\w+(?=\s*\()/,number:/\b\d+(?:\.\d+)?\b/,operator:/\.\.[<*!]?|->|--|\+\+|&&|\|\||\?{1,2}|[-+*/%!=<>]=?|\b(?:gt|gte|lt|lte)\b/,punctuation:/[,;.:()[\]{}]/};r.string[1].inside.interpolation.inside.rest=r,e.languages.ftl={"ftl-comment":{pattern:/^<#--[\s\S]*/,alias:"comment"},"ftl-directive":{pattern:/^<[\s\S]+>$/,inside:{directive:{pattern:/(^<\/?)[#@][a-z]\w*/i,lookbehind:!0,alias:"keyword"},punctuation:/^<\/?|\/?>$/,content:{pattern:/\s*\S[\s\S]*/,alias:"ftl",inside:r}}},"ftl-interpolation":{pattern:/^\$\{[\s\S]*\}$/,inside:{punctuation:/^\$\{|\}$/,content:{pattern:/\s*\S[\s\S]*/,alias:"ftl",inside:r}}}},e.hooks.add("before-tokenize",function(n){var r=RegExp(/<#--[\s\S]*?-->|<\/?[#@][a-zA-Z](?:)*?>|\$\{(?:)*?\}/.source.replace(//g,function(){return t}),"gi");e.languages["markup-templating"].buildPlaceholders(n,"ftl",r)}),e.hooks.add("after-tokenize",function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"ftl")})}(e)}e.exports=i,i.displayName="ftl",i.aliases=[]},12208(e){"use strict";function t(e){e.languages.gcode={comment:/;.*|\B\(.*?\)\B/,string:{pattern:/"(?:""|[^"])*"/,greedy:!0},keyword:/\b[GM]\d+(?:\.\d+)?\b/,property:/\b[A-Z]/,checksum:{pattern:/\*\d+/,alias:"punctuation"},punctuation:/:/}}e.exports=t,t.displayName="gcode",t.aliases=[]},62728(e){"use strict";function t(e){e.languages.gdscript={comment:/#.*/,string:{pattern:/@?(?:("|')(?:(?!\1)[^\n\\]|\\[\s\S])*\1(?!"|')|"""(?:[^\\]|\\[\s\S])*?""")/,greedy:!0},"class-name":{pattern:/(^(?:class_name|class|extends)[ \t]+|^export\([ \t]*|\bas[ \t]+|(?:\b(?:const|var)[ \t]|[,(])[ \t]*\w+[ \t]*:[ \t]*|->[ \t]*)[a-zA-Z_]\w*/m,lookbehind:!0},keyword:/\b(?:and|as|assert|break|breakpoint|class|class_name|const|continue|elif|else|enum|export|extends|for|func|if|in|is|master|mastersync|match|not|null|onready|or|pass|preload|puppet|puppetsync|remote|remotesync|return|self|setget|signal|static|tool|var|while|yield)\b/,function:/\b[a-z_]\w*(?=[ \t]*\()/i,variable:/\$\w+/,number:[/\b0b[01_]+\b|\b0x[\da-fA-F_]+\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.[\d_]+)(?:e[+-]?[\d_]+)?\b/,/\b(?:INF|NAN|PI|TAU)\b/],constant:/\b[A-Z][A-Z_\d]*\b/,boolean:/\b(?:false|true)\b/,operator:/->|:=|&&|\|\||<<|>>|[-+*/%&|!<>=]=?|[~^]/,punctuation:/[.:,;()[\]{}]/}}e.exports=t,t.displayName="gdscript",t.aliases=[]},81549(e){"use strict";function t(e){e.languages.gedcom={"line-value":{pattern:/(^[\t ]*\d+ +(?:@\w[\w!"$%&'()*+,\-./:;<=>?[\\\]^`{|}~\x80-\xfe #]*@ +)?\w+ ).+/m,lookbehind:!0,inside:{pointer:{pattern:/^@\w[\w!"$%&'()*+,\-./:;<=>?[\\\]^`{|}~\x80-\xfe #]*@$/,alias:"variable"}}},tag:{pattern:/(^[\t ]*\d+ +(?:@\w[\w!"$%&'()*+,\-./:;<=>?[\\\]^`{|}~\x80-\xfe #]*@ +)?)\w+/m,lookbehind:!0,alias:"string"},level:{pattern:/(^[\t ]*)\d+/m,lookbehind:!0,alias:"number"},pointer:{pattern:/@\w[\w!"$%&'()*+,\-./:;<=>?[\\\]^`{|}~\x80-\xfe #]*@/,alias:"variable"}}}e.exports=t,t.displayName="gedcom",t.aliases=[]},6024(e){"use strict";function t(e){var t,n;n=/(?:\r?\n|\r)[ \t]*\|.+\|(?:(?!\|).)*/.source,(t=e).languages.gherkin={pystring:{pattern:/("""|''')[\s\S]+?\1/,alias:"string"},comment:{pattern:/(^[ \t]*)#.*/m,lookbehind:!0},tag:{pattern:/(^[ \t]*)@\S*/m,lookbehind:!0},feature:{pattern:/((?:^|\r?\n|\r)[ \t]*)(?:Ability|Ahoy matey!|Arwedd|Aspekt|Besigheid Behoefte|Business Need|Caracteristica|Característica|Egenskab|Egenskap|Eiginleiki|Feature|Fīča|Fitur|Fonctionnalité|Fonksyonalite|Funcionalidade|Funcionalitat|Functionalitate|Funcţionalitate|Funcționalitate|Functionaliteit|Fungsi|Funkcia|Funkcija|Funkcionalitāte|Funkcionalnost|Funkcja|Funksie|Funktionalität|Funktionalitéit|Funzionalità|Hwaet|Hwæt|Jellemző|Karakteristik|laH|Lastnost|Mak|Mogucnost|Mogućnost|Moznosti|Možnosti|OH HAI|Omadus|Ominaisuus|Osobina|Özellik|perbogh|poQbogh malja'|Potrzeba biznesowa|Požadavek|Požiadavka|Pretty much|Qap|Qu'meH 'ut|Savybė|Tính năng|Trajto|Vermoë|Vlastnosť|Właściwość|Značilnost|Δυνατότητα|Λειτουργία|Могућност|Мөмкинлек|Особина|Свойство|Үзенчәлеклелек|Функционал|Функционалност|Функция|Функціонал|תכונה|خاصية|خصوصیت|صلاحیت|کاروبار کی ضرورت|وِیژگی|रूप लेख|ਖਾਸੀਅਤ|ਨਕਸ਼ ਨੁਹਾਰ|ਮੁਹਾਂਦਰਾ|గుణము|ಹೆಚ್ಚಳ|ความต้องการทางธุรกิจ|ความสามารถ|โครงหลัก|기능|フィーチャ|功能|機能):(?:[^:\r\n]+(?:\r?\n|\r|$))*/,lookbehind:!0,inside:{important:{pattern:/(:)[^\r\n]+/,lookbehind:!0},keyword:/[^:\r\n]+:/}},scenario:{pattern:/(^[ \t]*)(?:Abstract Scenario|Abstrakt Scenario|Achtergrond|Aer|Ær|Agtergrond|All y'all|Antecedentes|Antecedents|Atburðarás|Atburðarásir|Awww, look mate|B4|Background|Baggrund|Bakgrund|Bakgrunn|Bakgrunnur|Beispiele|Beispiller|Bối cảnh|Cefndir|Cenario|Cenário|Cenario de Fundo|Cenário de Fundo|Cenarios|Cenários|Contesto|Context|Contexte|Contexto|Conto|Contoh|Contone|Dæmi|Dasar|Dead men tell no tales|Delineacao do Cenario|Delineação do Cenário|Dis is what went down|Dữ liệu|Dyagram senaryo|Dyagram Senaryo|Egzanp|Ejemplos|Eksempler|Ekzemploj|Enghreifftiau|Esbozo do escenario|Escenari|Escenario|Esempi|Esquema de l'escenari|Esquema del escenario|Esquema do Cenario|Esquema do Cenário|Examples|EXAMPLZ|Exempel|Exemple|Exemples|Exemplos|First off|Fono|Forgatókönyv|Forgatókönyv vázlat|Fundo|Geçmiş|ghantoH|Grundlage|Hannergrond|Háttér|Heave to|Istorik|Juhtumid|Keadaan|Khung kịch bản|Khung tình huống|Kịch bản|Koncept|Konsep skenario|Kontèks|Kontekst|Kontekstas|Konteksts|Kontext|Konturo de la scenaro|Latar Belakang|lut|lut chovnatlh|lutmey|Lýsing Atburðarásar|Lýsing Dæma|Menggariskan Senario|MISHUN|MISHUN SRSLY|mo'|Náčrt Scenára|Náčrt Scénáře|Náčrt Scenáru|Oris scenarija|Örnekler|Osnova|Osnova Scenára|Osnova scénáře|Osnutek|Ozadje|Paraugs|Pavyzdžiai|Példák|Piemēri|Plan du scénario|Plan du Scénario|Plan senaryo|Plan Senaryo|Plang vum Szenario|Pozadí|Pozadie|Pozadina|Príklady|Příklady|Primer|Primeri|Primjeri|Przykłady|Raamstsenaarium|Reckon it's like|Rerefons|Scenár|Scénář|Scenarie|Scenarij|Scenarijai|Scenarijaus šablonas|Scenariji|Scenārijs|Scenārijs pēc parauga|Scenarijus|Scenario|Scénario|Scenario Amlinellol|Scenario Outline|Scenario Template|Scenariomal|Scenariomall|Scenarios|Scenariu|Scenariusz|Scenaro|Schema dello scenario|Se ðe|Se the|Se þe|Senario|Senaryo|Senaryo deskripsyon|Senaryo Deskripsyon|Senaryo taslağı|Shiver me timbers|Situācija|Situai|Situasie|Situasie Uiteensetting|Skenario|Skenario konsep|Skica|Structura scenariu|Structură scenariu|Struktura scenarija|Stsenaarium|Swa|Swa hwaer swa|Swa hwær swa|Szablon scenariusza|Szenario|Szenariogrundriss|Tapaukset|Tapaus|Tapausaihio|Taust|Tausta|Template Keadaan|Template Senario|Template Situai|The thing of it is|Tình huống|Variantai|Voorbeelde|Voorbeelden|Wharrimean is|Yo-ho-ho|You'll wanna|Założenia|Παραδείγματα|Περιγραφή Σεναρίου|Σενάρια|Σενάριο|Υπόβαθρο|Кереш|Контекст|Концепт|Мисаллар|Мисоллар|Основа|Передумова|Позадина|Предистория|Предыстория|Приклади|Пример|Примери|Примеры|Рамка на сценарий|Скица|Структура сценарија|Структура сценария|Структура сценарію|Сценарий|Сценарий структураси|Сценарийның төзелеше|Сценарији|Сценарио|Сценарій|Тарих|Үрнәкләр|דוגמאות|רקע|תבנית תרחיש|תרחיש|الخلفية|الگوی سناریو|امثلة|پس منظر|زمینه|سناریو|سيناريو|سيناريو مخطط|مثالیں|منظر نامے کا خاکہ|منظرنامہ|نمونه ها|उदाहरण|परिदृश्य|परिदृश्य रूपरेखा|पृष्ठभूमि|ਉਦਾਹਰਨਾਂ|ਪਟਕਥਾ|ਪਟਕਥਾ ਢਾਂਚਾ|ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ|ਪਿਛੋਕੜ|ఉదాహరణలు|కథనం|నేపథ్యం|సన్నివేశం|ಉದಾಹರಣೆಗಳು|ಕಥಾಸಾರಾಂಶ|ವಿವರಣೆ|ಹಿನ್ನೆಲೆ|โครงสร้างของเหตุการณ์|ชุดของตัวอย่าง|ชุดของเหตุการณ์|แนวคิด|สรุปเหตุการณ์|เหตุการณ์|배경|시나리오|시나리오 개요|예|サンプル|シナリオ|シナリオアウトライン|シナリオテンプレ|シナリオテンプレート|テンプレ|例|例子|剧本|剧本大纲|劇本|劇本大綱|场景|场景大纲|場景|場景大綱|背景):[^:\r\n]*/m,lookbehind:!0,inside:{important:{pattern:/(:)[^\r\n]*/,lookbehind:!0},keyword:/[^:\r\n]+:/}},"table-body":{pattern:RegExp("("+n+")(?:"+n+")+"),lookbehind:!0,inside:{outline:{pattern:/<[^>]+>/,alias:"variable"},td:{pattern:/\s*[^\s|][^|]*/,alias:"string"},punctuation:/\|/}},"table-head":{pattern:RegExp(n),inside:{th:{pattern:/\s*[^\s|][^|]*/,alias:"variable"},punctuation:/\|/}},atrule:{pattern:/(^[ \t]+)(?:'ach|'a|'ej|7|a|A také|A taktiež|A tiež|A zároveň|Aber|Ac|Adott|Akkor|Ak|Aleshores|Ale|Ali|Allora|Alors|Als|Ama|Amennyiben|Amikor|Ampak|an|AN|Ananging|And y'all|And|Angenommen|Anrhegedig a|An|Apabila|Atès|Atesa|Atunci|Avast!|Aye|A|awer|Bagi|Banjur|Bet|Biết|Blimey!|Buh|But at the end of the day I reckon|But y'all|But|BUT|Cal|Când|Cando|Cand|Ce|Cuando|Če|Ða ðe|Ða|Dadas|Dada|Dados|Dado|DaH ghu' bejlu'|dann|Dann|Dano|Dan|Dar|Dat fiind|Data|Date fiind|Date|Dati fiind|Dati|Daţi fiind|Dați fiind|Dato|DEN|Den youse gotta|Dengan|De|Diberi|Diyelim ki|Donada|Donat|Donitaĵo|Do|Dun|Duota|Ðurh|Eeldades|Ef|Eğer ki|Entao|Então|Entón|Entonces|En|Epi|E|És|Etant donnée|Etant donné|Et|Étant données|Étant donnée|Étant donné|Etant données|Etant donnés|Étant donnés|Fakat|Gangway!|Gdy|Gegeben seien|Gegeben sei|Gegeven|Gegewe|ghu' noblu'|Gitt|Given y'all|Given|Givet|Givun|Ha|Cho|I CAN HAZ|In|Ir|It's just unbelievable|I|Ja|Jeśli|Jeżeli|Kadar|Kada|Kad|Kai|Kaj|Když|Keď|Kemudian|Ketika|Khi|Kiedy|Ko|Kuid|Kui|Kun|Lan|latlh|Le sa a|Let go and haul|Le|Lè sa a|Lè|Logo|Lorsqu'<|Lorsque|mä|Maar|Mais|Mając|Majd|Maka|Manawa|Mas|Ma|Menawa|Men|Mutta|Nalikaning|Nalika|Nanging|Når|När|Nato|Nhưng|Niin|Njuk|O zaman|Og|Och|Oletetaan|Onda|Ond|Oraz|Pak|Pero|Però|Podano|Pokiaľ|Pokud|Potem|Potom|Privzeto|Pryd|qaSDI'|Quando|Quand|Quan|Så|Sed|Se|Siis|Sipoze ke|Sipoze Ke|Sipoze|Si|Şi|Și|Soit|Stel|Tada|Tad|Takrat|Tak|Tapi|Ter|Tetapi|Tha the|Tha|Then y'all|Then|Thì|Thurh|Toda|Too right|ugeholl|Und|Un|Và|vaj|Vendar|Ve|wann|Wanneer|WEN|Wenn|When y'all|When|Wtedy|Wun|Y'know|Yeah nah|Yna|Youse know like when|Youse know when youse got|Y|Za predpokladu|Za předpokladu|Zadani|Zadano|Zadan|Zadate|Zadato|Zakładając|Zaradi|Zatati|Þa þe|Þa|Þá|Þegar|Þurh|Αλλά|Δεδομένου|Και|Όταν|Τότε|А також|Агар|Але|Али|Аммо|А|Әгәр|Әйтик|Әмма|Бирок|Ва|Вә|Дадено|Дано|Допустим|Если|Задате|Задати|Задато|И|І|К тому же|Када|Кад|Когато|Когда|Коли|Ләкин|Лекин|Нәтиҗәдә|Нехай|Но|Онда|Припустимо, що|Припустимо|Пусть|Также|Та|Тогда|Тоді|То|Унда|Һәм|Якщо|אבל|אזי|אז|בהינתן|וגם|כאשר|آنگاه|اذاً|اگر|اما|اور|با فرض|بالفرض|بفرض|پھر|تب|ثم|جب|عندما|فرض کیا|لكن|لیکن|متى|هنگامی|و|अगर|और|कदा|किन्तु|चूंकि|जब|तथा|तदा|तब|परन्तु|पर|यदि|ਅਤੇ|ਜਦੋਂ|ਜਿਵੇਂ ਕਿ|ਜੇਕਰ|ਤਦ|ਪਰ|అప్పుడు|ఈ పరిస్థితిలో|కాని|చెప్పబడినది|మరియు|ಆದರೆ|ನಂತರ|ನೀಡಿದ|ಮತ್ತು|ಸ್ಥಿತಿಯನ್ನು|กำหนดให้|ดังนั้น|แต่|เมื่อ|และ|그러면<|그리고<|단<|만약<|만일<|먼저<|조건<|하지만<|かつ<|しかし<|ただし<|ならば<|もし<|並且<|但し<|但是<|假如<|假定<|假設<|假设<|前提<|同时<|同時<|并且<|当<|當<|而且<|那么<|那麼<)(?=[ \t])/m,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'/,inside:{outline:{pattern:/<[^>]+>/,alias:"variable"}}},outline:{pattern:/<[^>]+>/,alias:"variable"}}}e.exports=t,t.displayName="gherkin",t.aliases=[]},13600(e){"use strict";function t(e){e.languages.git={comment:/^#.*/m,deleted:/^[-–].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/m,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/m}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m}}e.exports=t,t.displayName="git",t.aliases=[]},3322(e,t,n){"use strict";var r=n(65806);function i(e){e.register(r),e.languages.glsl=e.languages.extend("c",{keyword:/\b(?:attribute|const|uniform|varying|buffer|shared|coherent|volatile|restrict|readonly|writeonly|atomic_uint|layout|centroid|flat|smooth|noperspective|patch|sample|break|continue|do|for|while|switch|case|default|if|else|subroutine|in|out|inout|float|double|int|void|bool|true|false|invariant|precise|discard|return|d?mat[234](?:x[234])?|[ibdu]?vec[234]|uint|lowp|mediump|highp|precision|[iu]?sampler[123]D|[iu]?samplerCube|sampler[12]DShadow|samplerCubeShadow|[iu]?sampler[12]DArray|sampler[12]DArrayShadow|[iu]?sampler2DRect|sampler2DRectShadow|[iu]?samplerBuffer|[iu]?sampler2DMS(?:Array)?|[iu]?samplerCubeArray|samplerCubeArrayShadow|[iu]?image[123]D|[iu]?image2DRect|[iu]?imageCube|[iu]?imageBuffer|[iu]?image[12]DArray|[iu]?imageCubeArray|[iu]?image2DMS(?:Array)?|struct|common|partition|active|asm|class|union|enum|typedef|template|this|resource|goto|inline|noinline|public|static|extern|external|interface|long|short|half|fixed|unsigned|superp|input|output|hvec[234]|fvec[234]|sampler3DRect|filter|sizeof|cast|namespace|using)\b/})}e.exports=i,i.displayName="glsl",i.aliases=[]},53877(e){"use strict";function t(e){e.languages.gamemakerlanguage=e.languages.gml=e.languages.extend("clike",{keyword:/\b(?:if|else|switch|case|default|break|for|repeat|while|do|until|continue|exit|return|globalvar|var|enum)\b/,number:/(?:\b0x[\da-f]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ulf]{0,4}/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not|with|at|xor)\b/,constant:/\b(?:self|other|all|noone|global|local|undefined|pointer_(?:invalid|null)|action_(?:stop|restart|continue|reverse)|pi|GM_build_date|GM_version|timezone_(?:local|utc)|gamespeed_(?:fps|microseconds)|ev_(?:create|destroy|step|alarm|keyboard|mouse|collision|other|draw|draw_(?:begin|end|pre|post)|keypress|keyrelease|trigger|(?:left|right|middle|no)_button|(?:left|right|middle)_press|(?:left|right|middle)_release|mouse_(?:enter|leave|wheel_up|wheel_down)|global_(?:left|right|middle)_button|global_(?:left|right|middle)_press|global_(?:left|right|middle)_release|joystick(?:1|2)_(?:left|right|up|down|button1|button2|button3|button4|button5|button6|button7|button8)|outside|boundary|game_start|game_end|room_start|room_end|no_more_lives|animation_end|end_of_path|no_more_health|user\d|step_(?:normal|begin|end)|gui|gui_begin|gui_end)|vk_(?:nokey|anykey|enter|return|shift|control|alt|escape|space|backspace|tab|pause|printscreen|left|right|up|down|home|end|delete|insert|pageup|pagedown|f\d|numpad\d|divide|multiply|subtract|add|decimal|lshift|lcontrol|lalt|rshift|rcontrol|ralt)|mb_(?:any|none|left|right|middle)|c_(?:aqua|black|blue|dkgray|fuchsia|gray|green|lime|ltgray|maroon|navy|olive|purple|red|silver|teal|white|yellow|orange)|fa_(?:left|center|right|top|middle|bottom|readonly|hidden|sysfile|volumeid|directory|archive)|pr_(?:pointlist|linelist|linestrip|trianglelist|trianglestrip|trianglefan)|bm_(?:complex|normal|add|max|subtract|zero|one|src_colour|inv_src_colour|src_color|inv_src_color|src_alpha|inv_src_alpha|dest_alpha|inv_dest_alpha|dest_colour|inv_dest_colour|dest_color|inv_dest_color|src_alpha_sat)|audio_(?:falloff_(?:none|inverse_distance|inverse_distance_clamped|linear_distance|linear_distance_clamped|exponent_distance|exponent_distance_clamped)|old_system|new_system|mono|stereo|3d)|cr_(?:default|none|arrow|cross|beam|size_nesw|size_ns|size_nwse|size_we|uparrow|hourglass|drag|appstart|handpoint|size_all)|asset_(?:object|unknown|sprite|sound|room|path|script|font|timeline|tiles|shader)|ds_type_(?:map|list|stack|queue|grid|priority)|ef_(?:explosion|ring|ellipse|firework|smoke|smokeup|star|spark|flare|cloud|rain|snow)|pt_shape_(?:pixel|disk|square|line|star|circle|ring|sphere|flare|spark|explosion|cloud|smoke|snow)|ps_(?:distr|shape)_(?:linear|gaussian|invgaussian|rectangle|ellipse|diamond|line)|ty_(?:real|string)|dll_(?:cdel|cdecl|stdcall)|matrix_(?:view|projection|world)|os_(?:win32|windows|macosx|ios|android|linux|unknown|winphone|win8native|psvita|ps4|xboxone|ps3|uwp)|browser_(?:not_a_browser|unknown|ie|firefox|chrome|safari|safari_mobile|opera|tizen|windows_store|ie_mobile)|device_ios_(?:unknown|iphone|iphone_retina|ipad|ipad_retina|iphone5|iphone6|iphone6plus)|device_(?:emulator|tablet)|display_(?:landscape|landscape_flipped|portrait|portrait_flipped)|of_challenge_(?:win|lose|tie)|leaderboard_type_(?:number|time_mins_secs)|cmpfunc_(?:never|less|equal|lessequal|greater|notequal|greaterequal|always)|cull_(?:noculling|clockwise|counterclockwise)|lighttype_(?:dir|point)|iap_(?:ev_storeload|ev_product|ev_purchase|ev_consume|ev_restore|storeload_ok|storeload_failed|status_uninitialised|status_unavailable|status_loading|status_available|status_processing|status_restoring|failed|unavailable|available|purchased|canceled|refunded)|fb_login_(?:default|fallback_to_webview|no_fallback_to_webview|forcing_webview|use_system_account|forcing_safari)|phy_joint_(?:anchor_1_x|anchor_1_y|anchor_2_x|anchor_2_y|reaction_force_x|reaction_force_y|reaction_torque|motor_speed|angle|motor_torque|max_motor_torque|translation|speed|motor_force|max_motor_force|length_1|length_2|damping_ratio|frequency|lower_angle_limit|upper_angle_limit|angle_limits|max_length|max_torque|max_force)|phy_debug_render_(?:aabb|collision_pairs|coms|core_shapes|joints|obb|shapes)|phy_particle_flag_(?:water|zombie|wall|spring|elastic|viscous|powder|tensile|colourmixing|colormixing)|phy_particle_group_flag_(?:solid|rigid)|phy_particle_data_flag_(?:typeflags|position|velocity|colour|color|category)|achievement_(?:our_info|friends_info|leaderboard_info|info|filter_(?:all_players|friends_only|favorites_only)|type_challenge|type_score_challenge|pic_loaded|show_(?:ui|profile|leaderboard|achievement|bank|friend_picker|purchase_prompt))|network_(?:socket_(?:tcp|udp|bluetooth)|type_(?:connect|disconnect|data|non_blocking_connect)|config_(?:connect_timeout|use_non_blocking_socket|enable_reliable_udp|disable_reliable_udp))|buffer_(?:fixed|grow|wrap|fast|vbuffer|network|u8|s8|u16|s16|u32|s32|u64|f16|f32|f64|bool|text|string|seek_start|seek_relative|seek_end|generalerror|outofspace|outofbounds|invalidtype)|gp_(?:face\d|shoulderl|shoulderr|shoulderlb|shoulderrb|select|start|stickl|stickr|padu|padd|padl|padr|axislh|axislv|axisrh|axisrv)|ov_(?:friends|community|players|settings|gamegroup|achievements)|lb_sort_(?:none|ascending|descending)|lb_disp_(?:none|numeric|time_sec|time_ms)|ugc_(?:result_success|filetype_(?:community|microtrans)|visibility_(?:public|friends_only|private)|query_RankedBy(?:Vote|PublicationDate|Trend|NumTimesReported|TotalVotesAsc|VotesUp|TextSearch)|query_(?:AcceptedForGameRankedByAcceptanceDate|FavoritedByFriendsRankedByPublicationDate|CreatedByFriendsRankedByPublicationDate|NotYetRated)|sortorder_CreationOrder(?:Desc|Asc)|sortorder_(?:TitleAsc|LastUpdatedDesc|SubscriptionDateDesc|VoteScoreDesc|ForModeration)|list_(?:Published|VotedOn|VotedUp|VotedDown|WillVoteLater|Favorited|Subscribed|UsedOrPlayed|Followed)|match_(?:Items|Items_Mtx|Items_ReadyToUse|Collections|Artwork|Videos|Screenshots|AllGuides|WebGuides|IntegratedGuides|UsableInGame|ControllerBindings))|vertex_usage_(?:position|colour|color|normal|texcoord|textcoord|blendweight|blendindices|psize|tangent|binormal|fog|depth|sample)|vertex_type_(?:float\d|colour|color|ubyte4)|layerelementtype_(?:undefined|background|instance|oldtilemap|sprite|tilemap|particlesystem|tile)|tile_(?:rotate|flip|mirror|index_mask)|input_type|se_(?:chorus|compressor|echo|equalizer|flanger|gargle|none|reverb)|text_type|(?:obj|scr|spr|rm)\w+)\b/,variable:/\b(?:x|y|(?:x|y)(?:previous|start)|(?:h|v)speed|direction|speed|friction|gravity|gravity_direction|path_(?:index|position|positionprevious|speed|scale|orientation|endaction)|object_index|id|solid|persistent|mask_index|instance_(?:count|id)|alarm|timeline_(?:index|position|speed|running|loop)|visible|sprite_(?:index|width|height|xoffset|yoffset)|image_(?:number|index|speed|depth|xscale|yscale|angle|alpha|blend)|bbox_(?:left|right|top|bottom)|layer|phy_(?:rotation|(?:position|linear_velocity|speed|com|collision|col_normal)_(?:x|y)|angular_(?:velocity|damping)|position_(?:x|y)previous|speed|linear_damping|bullet|fixed_rotation|active|mass|inertia|dynamic|kinematic|sleeping|collision_points)|working_directory|webgl_enabled|view_(?:(?:y|x|w|h)view|(?:y|x|w|h)port|(?:v|h)(?:speed|border)|visible|surface_id|object|enabled|current|angle)|undefined|transition_(?:steps|kind|color)|temp_directory|show_(?:score|lives|health)|secure_mode|score|room_(?:width|speed|persistent|last|height|first|caption)|room|pointer_(?:null|invalid)|os_(?:version|type|device|browser)|mouse_(?:y|x|lastbutton|button)|lives|keyboard_(?:string|lastkey|lastchar|key)|iap_data|health|gamemaker_(?:version|registered|pro)|game_(?:save|project|display)_(?:id|name)|fps_real|fps|event_(?:type|object|number|action)|error_(?:occurred|last)|display_aa|delta_time|debug_mode|cursor_sprite|current_(?:year|weekday|time|second|month|minute|hour|day)|caption_(?:score|lives|health)|browser_(?:width|height)|background_(?:yscale|y|xscale|x|width|vtiled|vspeed|visible|showcolour|showcolor|index|htiled|hspeed|height|foreground|colour|color|blend|alpha)|async_load|application_surface|argument(?:_relitive|_count|\d)|argument|global|local|self|other)\b/})}e.exports=t,t.displayName="gml",t.aliases=[]},51519(e){"use strict";function t(e){e.languages.go=e.languages.extend("clike",{string:{pattern:/(["'`])(?:\\[\s\S]|(?!\1)[^\\])*\1/,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|iota|nil|true|false)\b/,number:/(?:\b0x[a-f\d]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[-+]?\d+)?)i?/i,operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:bool|byte|complex(?:64|128)|error|float(?:32|64)|rune|string|u?int(?:8|16|32|64)?|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(?:ln)?|real|recover)\b/}),delete e.languages.go["class-name"]}e.exports=t,t.displayName="go",t.aliases=[]},94055(e){"use strict";function t(e){e.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:e.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:true|false)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/[A-Z]\w*Input(?=!?.*$)/m,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},e.hooks.add("after-tokenize",function(e){if("graphql"===e.language){for(var t=e.tokens.filter(function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type}),n=0;n0)){var s=d(/^\{$/,/^\}$/);if(-1===s)continue;for(var u=n;u=0&&h(c,"variable-input")}}}}}function l(e){return t[n+e]}function f(e,t){t=t||0;for(var n=0;n]?|\+[+=]?|!=?|<(?:<=?|=>?)?|>(?:>>?=?|=)?|&[&=]?|\|[|=]?|\/=?|\^=?|%=?)/,lookbehind:!0},punctuation:/\.+|[{}[\];(),:$]/}),e.languages.insertBefore("groovy","string",{shebang:{pattern:/#!.+/,alias:"comment"}}),e.languages.insertBefore("groovy","punctuation",{"spock-block":/\b(?:setup|given|when|then|and|cleanup|expect|where):/}),e.languages.insertBefore("groovy","function",{annotation:{pattern:/(^|[^.])@\w+/,lookbehind:!0,alias:"punctuation"}}),e.hooks.add("wrap",function(t){if("groovy"===t.language&&"string"===t.type){var n=t.content.value[0];if("'"!=n){var r=/([^\\])(?:\$(?:\{.*?\}|[\w.]+))/;"$"===n&&(r=/([^\$])(?:\$(?:\{.*?\}|[\w.]+))/),t.content.value=t.content.value.replace(/</g,"<").replace(/&/g,"&"),t.content=e.highlight(t.content.value,{expression:{pattern:r,lookbehind:!0,inside:e.languages.groovy}}),t.classes.push("/"===n?"regex":"gstring")}}})}e.exports=t,t.displayName="groovy",t.aliases=[]},29536(e,t,n){"use strict";var r=n(56939);function i(e){e.register(r),function(e){e.languages.haml={"multiline-comment":{pattern:/((?:^|\r?\n|\r)([\t ]*))(?:\/|-#).*(?:(?:\r?\n|\r)\2[\t ].+)*/,lookbehind:!0,alias:"comment"},"multiline-code":[{pattern:/((?:^|\r?\n|\r)([\t ]*)(?:[~-]|[&!]?=)).*,[\t ]*(?:(?:\r?\n|\r)\2[\t ].*,[\t ]*)*(?:(?:\r?\n|\r)\2[\t ].+)/,lookbehind:!0,inside:e.languages.ruby},{pattern:/((?:^|\r?\n|\r)([\t ]*)(?:[~-]|[&!]?=)).*\|[\t ]*(?:(?:\r?\n|\r)\2[\t ].*\|[\t ]*)*/,lookbehind:!0,inside:e.languages.ruby}],filter:{pattern:/((?:^|\r?\n|\r)([\t ]*)):[\w-]+(?:(?:\r?\n|\r)(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/,lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"}}},markup:{pattern:/((?:^|\r?\n|\r)[\t ]*)<.+/,lookbehind:!0,inside:e.languages.markup},doctype:{pattern:/((?:^|\r?\n|\r)[\t ]*)!!!(?: .+)?/,lookbehind:!0},tag:{pattern:/((?:^|\r?\n|\r)[\t ]*)[%.#][\w\-#.]*[\w\-](?:\([^)]+\)|\{(?:\{[^}]+\}|[^{}])+\}|\[[^\]]+\])*[\/<>]*/,lookbehind:!0,inside:{attributes:[{pattern:/(^|[^#])\{(?:\{[^}]+\}|[^{}])+\}/,lookbehind:!0,inside:e.languages.ruby},{pattern:/\([^)]+\)/,inside:{"attr-value":{pattern:/(=\s*)(?:"(?:\\.|[^\\"\r\n])*"|[^)\s]+)/,lookbehind:!0},"attr-name":/[\w:-]+(?=\s*!?=|\s*[,)])/,punctuation:/[=(),]/}},{pattern:/\[[^\]]+\]/,inside:e.languages.ruby}],punctuation:/[<>]/}},code:{pattern:/((?:^|\r?\n|\r)[\t ]*(?:[~-]|[&!]?=)).+/,lookbehind:!0,inside:e.languages.ruby},interpolation:{pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"},rest:e.languages.ruby}},punctuation:{pattern:/((?:^|\r?\n|\r)[\t ]*)[~=\-&!]+/,lookbehind:!0}};for(var t="((?:^|\\r?\\n|\\r)([\\t ]*)):{{filter_name}}(?:(?:\\r?\\n|\\r)(?:\\2[\\t ].+|\\s*?(?=\\r?\\n|\\r)))+",n=["css",{filter:"coffee",language:"coffeescript"},"erb","javascript","less","markdown","ruby","scss","textile"],r={},i=0,a=n.length;i@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},t.hooks.add("before-tokenize",function(e){var n=/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g;t.languages["markup-templating"].buildPlaceholders(e,"handlebars",n)}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"handlebars")}),t.languages.hbs=t.languages.handlebars}e.exports=i,i.displayName="handlebars",i.aliases=["hbs"]},58090(e){"use strict";function t(e){e.languages.haskell={comment:{pattern:/(^|[^-!#$%*+=?&@|~.:<>^\\\/])(?:--(?:(?=.)[^-!#$%*+=?&@|~.:<>^\\\/].*|$)|\{-[\s\S]*?-\})/m,lookbehind:!0},char:{pattern:/'(?:[^\\']|\\(?:[abfnrtv\\"'&]|\^[A-Z@[\]^_]|NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|\d+|o[0-7]+|x[0-9a-fA-F]+))'/,alias:"string"},string:{pattern:/"(?:[^\\"]|\\(?:\S|\s+\\))*"/,greedy:!0},keyword:/\b(?:case|class|data|deriving|do|else|if|in|infixl|infixr|instance|let|module|newtype|of|primitive|then|type|where)\b/,"import-statement":{pattern:/(^[\t ]*)import\s+(?:qualified\s+)?(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*(?:\s+as\s+(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*)?(?:\s+hiding\b)?/m,lookbehind:!0,inside:{keyword:/\b(?:import|qualified|as|hiding)\b/}},builtin:/\b(?:abs|acos|acosh|all|and|any|appendFile|approxRational|asTypeOf|asin|asinh|atan|atan2|atanh|basicIORun|break|catch|ceiling|chr|compare|concat|concatMap|const|cos|cosh|curry|cycle|decodeFloat|denominator|digitToInt|div|divMod|drop|dropWhile|either|elem|encodeFloat|enumFrom|enumFromThen|enumFromThenTo|enumFromTo|error|even|exp|exponent|fail|filter|flip|floatDigits|floatRadix|floatRange|floor|fmap|foldl|foldl1|foldr|foldr1|fromDouble|fromEnum|fromInt|fromInteger|fromIntegral|fromRational|fst|gcd|getChar|getContents|getLine|group|head|id|inRange|index|init|intToDigit|interact|ioError|isAlpha|isAlphaNum|isAscii|isControl|isDenormalized|isDigit|isHexDigit|isIEEE|isInfinite|isLower|isNaN|isNegativeZero|isOctDigit|isPrint|isSpace|isUpper|iterate|last|lcm|length|lex|lexDigits|lexLitChar|lines|log|logBase|lookup|map|mapM|mapM_|max|maxBound|maximum|maybe|min|minBound|minimum|mod|negate|not|notElem|null|numerator|odd|or|ord|otherwise|pack|pi|pred|primExitWith|print|product|properFraction|putChar|putStr|putStrLn|quot|quotRem|range|rangeSize|read|readDec|readFile|readFloat|readHex|readIO|readInt|readList|readLitChar|readLn|readOct|readParen|readSigned|reads|readsPrec|realToFrac|recip|rem|repeat|replicate|return|reverse|round|scaleFloat|scanl|scanl1|scanr|scanr1|seq|sequence|sequence_|show|showChar|showInt|showList|showLitChar|showParen|showSigned|showString|shows|showsPrec|significand|signum|sin|sinh|snd|sort|span|splitAt|sqrt|subtract|succ|sum|tail|take|takeWhile|tan|tanh|threadToIOResult|toEnum|toInt|toInteger|toLower|toRational|toUpper|truncate|uncurry|undefined|unlines|until|unwords|unzip|unzip3|userError|words|writeFile|zip|zip3|zipWith|zipWith3)\b/,number:/\b(?:\d+(?:\.\d+)?(?:e[+-]?\d+)?|0o[0-7]+|0x[0-9a-f]+)\b/i,operator:/\s\.\s|[-!#$%*+=?&@|~:<>^\\\/]*\.[-!#$%*+=?&@|~.:<>^\\\/]+|[-!#$%*+=?&@|~.:<>^\\\/]+\.[-!#$%*+=?&@|~:<>^\\\/]*|[-!#$%*+=?&@|~:<>^\\\/]+|`(?:[A-Z][\w']*\.)*[_a-z][\w']*`/,hvariable:/\b(?:[A-Z][\w']*\.)*[_a-z][\w']*\b/,constant:/\b(?:[A-Z][\w']*\.)*[A-Z][\w']*\b/,punctuation:/[{}[\];(),.:]/},e.languages.hs=e.languages.haskell}e.exports=t,t.displayName="haskell",t.aliases=["hs"]},95121(e){"use strict";function t(e){e.languages.haxe=e.languages.extend("clike",{string:{pattern:/(["'])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0,inside:{interpolation:{pattern:/(^|[^\\])\$(?:\w+|\{[^}]+\})/,lookbehind:!0,inside:{interpolation:{pattern:/^\$\w*/,alias:"variable"}}}}},keyword:/\bthis\b|\b(?:abstract|as|break|case|cast|catch|class|continue|default|do|dynamic|else|enum|extends|extern|from|for|function|if|implements|import|in|inline|interface|macro|new|null|override|public|private|return|static|super|switch|throw|to|try|typedef|using|var|while)(?!\.)\b/,operator:/\.{3}|\+\+?|-[->]?|[=!]=?|&&?|\|\|?|<[<=]?|>[>=]?|[*\/%~^]/}),e.languages.insertBefore("haxe","class-name",{regex:{pattern:/~\/(?:[^\/\\\r\n]|\\.)+\/[igmsu]*/,greedy:!0}}),e.languages.insertBefore("haxe","keyword",{preprocessor:{pattern:/#\w+/,alias:"builtin"},metadata:{pattern:/@:?\w+/,alias:"symbol"},reification:{pattern:/\$(?:\w+|(?=\{))/,alias:"variable"}}),e.languages.haxe.string.inside.interpolation.inside.rest=e.languages.haxe,delete e.languages.haxe["class-name"]}e.exports=t,t.displayName="haxe",t.aliases=[]},59904(e){"use strict";function t(e){e.languages.hcl={comment:/(?:\/\/|#).*|\/\*[\s\S]*?(?:\*\/|$)/,heredoc:{pattern:/<<-?(\w+\b)[\s\S]*?^[ \t]*\1/m,greedy:!0,alias:"string"},keyword:[{pattern:/(?:resource|data)\s+(?:"(?:\\[\s\S]|[^\\"])*")(?=\s+"[\w-]+"\s+\{)/i,inside:{type:{pattern:/(resource|data|\s+)(?:"(?:\\[\s\S]|[^\\"])*")/i,lookbehind:!0,alias:"variable"}}},{pattern:/(?:provider|provisioner|variable|output|module|backend)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+(?=\{)/i,inside:{type:{pattern:/(provider|provisioner|variable|output|module|backend)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+/i,lookbehind:!0,alias:"variable"}}},/[\w-]+(?=\s+\{)/],property:[/[-\w\.]+(?=\s*=(?!=))/,/"(?:\\[\s\S]|[^\\"])+"(?=\s*[:=])/],string:{pattern:/"(?:[^\\$"]|\\[\s\S]|\$(?:(?=")|\$+(?!\$)|[^"${])|\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\})*"/,greedy:!0,inside:{interpolation:{pattern:/(^|[^$])\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\}/,lookbehind:!0,inside:{type:{pattern:/(\b(?:terraform|var|self|count|module|path|data|local)\b\.)[\w\*]+/i,lookbehind:!0,alias:"variable"},keyword:/\b(?:terraform|var|self|count|module|path|data|local)\b/i,function:/\w+(?=\()/,string:{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,punctuation:/[!\$#%&'()*+,.\/;<=>@\[\\\]^`{|}~?:]/}}}},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,boolean:/\b(?:true|false)\b/i,punctuation:/[=\[\]{}]/}}e.exports=t,t.displayName="hcl",t.aliases=[]},9436(e,t,n){"use strict";var r=n(65806);function i(e){e.register(r),e.languages.hlsl=e.languages.extend("c",{"class-name":[e.languages.c["class-name"],/\b(?:AppendStructuredBuffer|BlendState|Buffer|ByteAddressBuffer|CompileShader|ComputeShader|ConsumeStructuredBuffer|DepthStencilState|DepthStencilView|DomainShader|GeometryShader|Hullshader|InputPatch|LineStream|OutputPatch|PixelShader|PointStream|RasterizerState|RenderTargetView|RWBuffer|RWByteAddressBuffer|RWStructuredBuffer|RWTexture(?:1D|1DArray|2D|2DArray|3D)|SamplerComparisonState|SamplerState|StructuredBuffer|Texture(?:1D|1DArray|2D|2DArray|2DMS|2DMSArray|3D|Cube|CubeArray)|TriangleStream|VertexShader)\b/],keyword:[/\b(?:asm|asm_fragment|auto|break|case|catch|cbuffer|centroid|char|class|column_major|compile|compile_fragment|const|const_cast|continue|default|delete|discard|do|dynamic_cast|else|enum|explicit|export|extern|for|friend|fxgroup|goto|groupshared|if|in|inline|inout|interface|line|lineadj|linear|long|matrix|mutable|namespace|new|nointerpolation|noperspective|operator|out|packoffset|pass|pixelfragment|point|precise|private|protected|public|register|reinterpret_cast|return|row_major|sample|sampler|shared|short|signed|sizeof|snorm|stateblock|stateblock_state|static|static_cast|string|struct|switch|tbuffer|technique|technique10|technique11|template|texture|this|throw|triangle|triangleadj|try|typedef|typename|uniform|union|unorm|unsigned|using|vector|vertexfragment|virtual|void|volatile|while)\b/,/\b(?:bool|double|dword|float|half|int|min(?:10float|12int|16(?:float|int|uint))|uint)(?:[1-4](?:x[1-4])?)?\b/],number:/(?:(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+)?|\b0x[\da-fA-F]+)[fFhHlLuU]?\b/,boolean:/\b(?:false|true)\b/})}e.exports=i,i.displayName="hlsl",i.aliases=[]},76942(e){"use strict";function t(e){e.languages.hpkp={directive:{pattern:/\b(?:(?:includeSubDomains|preload|strict)(?: |;)|pin-sha256="[a-zA-Z\d+=/]+"|(?:max-age|report-uri)=|report-to )/,alias:"keyword"},safe:{pattern:/\b\d{7,}\b/,alias:"selector"},unsafe:{pattern:/\b\d{1,6}\b/,alias:"function"}}}e.exports=t,t.displayName="hpkp",t.aliases=[]},60561(e){"use strict";function t(e){e.languages.hsts={directive:{pattern:/\b(?:max-age=|includeSubDomains|preload)/,alias:"keyword"},safe:{pattern:/\b\d{8,}\b/,alias:"selector"},unsafe:{pattern:/\b\d{1,7}\b/,alias:"function"}}}e.exports=t,t.displayName="hsts",t.aliases=[]},49660(e){"use strict";function t(e){!function(e){e.languages.http={"request-line":{pattern:/^(?:GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI|SEARCH)\s(?:https?:\/\/|\/)\S*\sHTTP\/[0-9.]+/m,inside:{method:{pattern:/^[A-Z]+\b/,alias:"property"},"request-target":{pattern:/^(\s)(?:https?:\/\/|\/)\S*(?=\s)/,lookbehind:!0,alias:"url",inside:e.languages.uri},"http-version":{pattern:/^(\s)HTTP\/[0-9.]+/,lookbehind:!0,alias:"property"}}},"response-status":{pattern:/^HTTP\/[0-9.]+ \d+ .+/m,inside:{"http-version":{pattern:/^HTTP\/[0-9.]+/,alias:"property"},"status-code":{pattern:/^(\s)\d+(?=\s)/,lookbehind:!0,alias:"number"},"reason-phrase":{pattern:/^(\s).+/,lookbehind:!0,alias:"string"}}},"header-name":{pattern:/^[\w-]+:(?=.)/m,alias:"keyword"}};var t,n=e.languages,r={"application/javascript":n.javascript,"application/json":n.json||n.javascript,"application/xml":n.xml,"text/xml":n.xml,"text/html":n.html,"text/css":n.css},i={"application/json":!0,"application/xml":!0};function a(e){var t="\\w+/(?:[\\w.-]+\\+)+"+e.replace(/^[a-z]+\//,"")+"(?![+\\w.-])";return"(?:"+e+"|"+t+")"}for(var o in r)if(r[o]){t=t||{};var s=i[o]?a(o):o;t[o.replace(/\//g,"-")]={pattern:RegExp("(content-type:\\s*"+s+"(?:(?:\\r\\n?|\\n).+)*)(?:\\r?\\n|\\r){2}[\\s\\S]*","i"),lookbehind:!0,inside:r[o]}}t&&e.languages.insertBefore("http","header-name",t)}(e)}e.exports=t,t.displayName="http",t.aliases=[]},30615(e){"use strict";function t(e){e.languages.ichigojam={comment:/(?:\B'|REM)(?:[^\n\r]*)/i,string:{pattern:/"(?:""|[!#$%&'()*,\/:;<=>?^\w +\-.])*"/i,greedy:!0},number:/\B#[0-9A-F]+|\B`[01]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:E[+-]?\d+)?/i,keyword:/\b(?:BEEP|BPS|CASE|CLEAR|CLK|CLO|CLP|CLS|CLT|CLV|CONT|COPY|ELSE|END|FILE|FILES|FOR|GOSUB|GSB|GOTO|IF|INPUT|KBD|LED|LET|LIST|LOAD|LOCATE|LRUN|NEW|NEXT|OUT|RIGHT|PLAY|POKE|PRINT|PWM|REM|RENUM|RESET|RETURN|RTN|RUN|SAVE|SCROLL|SLEEP|SRND|STEP|STOP|SUB|TEMPO|THEN|TO|UART|VIDEO|WAIT)(?:\$|\b)/i,function:/\b(?:ABS|ANA|ASC|BIN|BTN|DEC|END|FREE|HELP|HEX|I2CR|I2CW|IN|INKEY|LEN|LINE|PEEK|RND|SCR|SOUND|STR|TICK|USR|VER|VPEEK|ZER)(?:\$|\b)/i,label:/(?:\B@\S+)/i,operator:/<[=>]?|>=?|\|\||&&|[+\-*\/=|&^~!]|\b(?:AND|NOT|OR)\b/i,punctuation:/[\[,;:()\]]/}}e.exports=t,t.displayName="ichigojam",t.aliases=[]},93865(e){"use strict";function t(e){e.languages.icon={comment:/#.*/,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n_]|\\.|_(?!\1)(?:\r\n|[\s\S]))*\1/,greedy:!0},number:/\b(?:\d+r[a-z\d]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b|\.\d+\b/i,"builtin-keyword":{pattern:/&(?:allocated|ascii|clock|collections|cset|current|date|dateline|digits|dump|e|error(?:number|text|value)?|errout|fail|features|file|host|input|lcase|letters|level|line|main|null|output|phi|pi|pos|progname|random|regions|source|storage|subject|time|trace|ucase|version)\b/,alias:"variable"},directive:{pattern:/\$\w+/,alias:"builtin"},keyword:/\b(?:break|by|case|create|default|do|else|end|every|fail|global|if|initial|invocable|link|local|next|not|of|procedure|record|repeat|return|static|suspend|then|to|until|while)\b/,function:/\b(?!\d)\w+(?=\s*[({]|\s*!\s*\[)/,operator:/[+-]:(?!=)|(?:[\/?@^%&]|\+\+?|--?|==?=?|~==?=?|\*\*?|\|\|\|?|<(?:->?|>?=?)(?::=)?|:(?:=:?)?|[!.\\|~]/,punctuation:/[\[\](){},;]/}}e.exports=t,t.displayName="icon",t.aliases=[]},51078(e){"use strict";function t(e){!function(e){function t(e,n){return n<=0?/[]/.source:e.replace(//g,function(){return t(e,n-1)})}var n=/'[{}:=,](?:[^']|'')*'(?!')/,r={pattern:/''/,greedy:!0,alias:"operator"},i={pattern:n,greedy:!0,inside:{escape:r}},a=t(/\{(?:[^{}']|'(?![{},'])|''||)*\}/.source.replace(//g,function(){return n.source}),8),o={pattern:RegExp(a),inside:{message:{pattern:/^(\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:null},"message-delimiter":{pattern:/./,alias:"punctuation"}}};e.languages["icu-message-format"]={argument:{pattern:RegExp(a),greedy:!0,inside:{content:{pattern:/^(\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:{"argument-name":{pattern:/^(\s*)[^{}:=,\s]+/,lookbehind:!0},"choice-style":{pattern:/^(\s*,\s*choice\s*,\s*)\S(?:[\s\S]*\S)?/,lookbehind:!0,inside:{punctuation:/\|/,range:{pattern:/^(\s*)[+-]?(?:\d+(?:\.\d*)?|\u221e)\s*[<#\u2264]/,lookbehind:!0,inside:{operator:/[<#\u2264]/,number:/\S+/}},rest:null}},"plural-style":{pattern:/^(\s*,\s*(?:plural|selectordinal)\s*,\s*)\S(?:[\s\S]*\S)?/,lookbehind:!0,inside:{offset:/^offset:\s*\d+/,"nested-message":o,selector:{pattern:/=\d+|[^{}:=,\s]+/,inside:{keyword:/^(?:zero|one|two|few|many|other)$/}}}},"select-style":{pattern:/^(\s*,\s*select\s*,\s*)\S(?:[\s\S]*\S)?/,lookbehind:!0,inside:{"nested-message":o,selector:{pattern:/[^{}:=,\s]+/,inside:{keyword:/^other$/}}}},keyword:/\b(?:choice|plural|select|selectordinal)\b/,"arg-type":{pattern:/\b(?:number|date|time|spellout|ordinal|duration)\b/,alias:"keyword"},"arg-skeleton":{pattern:/(,\s*)::[^{}:=,\s]+/,lookbehind:!0},"arg-style":{pattern:/(,\s*)(?:short|medium|long|full|integer|currency|percent)(?=\s*$)/,lookbehind:!0},"arg-style-text":{pattern:RegExp(/(^\s*,\s*(?=\S))/.source+t(/(?:[^{}']|'[^']*'|\{(?:)?\})+/.source,8)+"$"),lookbehind:!0,alias:"string"},punctuation:/,/}},"argument-delimiter":{pattern:/./,alias:"operator"}}},escape:r,string:i},o.inside.message.inside=e.languages["icu-message-format"],e.languages["icu-message-format"].argument.inside.content.inside["choice-style"].inside.rest=e.languages["icu-message-format"]}(e)}e.exports=t,t.displayName="icuMessageFormat",t.aliases=[]},91178(e,t,n){"use strict";var r=n(58090);function i(e){e.register(r),e.languages.idris=e.languages.extend("haskell",{comment:{pattern:/(?:(?:--|\|\|\|).*$|\{-[\s\S]*?-\})/m},keyword:/\b(?:Type|case|class|codata|constructor|corecord|data|do|dsl|else|export|if|implementation|implicit|import|impossible|in|infix|infixl|infixr|instance|interface|let|module|mutual|namespace|of|parameters|partial|postulate|private|proof|public|quoteGoal|record|rewrite|syntax|then|total|using|where|with)\b/,"import-statement":{pattern:/(^\s*)import\s+(?:[A-Z][\w']*)(?:\.[A-Z][\w']*)*/m,lookbehind:!0},builtin:void 0}),e.languages.idr=e.languages.idris}e.exports=i,i.displayName="idris",i.aliases=["idr"]},40011(e){"use strict";function t(e){e.languages.iecst={comment:[{pattern:/(^|[^\\])(?:\/\*[\s\S]*?(?:\*\/|$)|\(\*[\s\S]*?(?:\*\)|$)|\{[\s\S]*?(?:\}|$))/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":/\b(?:END_)?(?:PROGRAM|CONFIGURATION|INTERFACE|FUNCTION_BLOCK|FUNCTION|ACTION|TRANSITION|TYPE|STRUCT|(?:INITIAL_)?STEP|NAMESPACE|LIBRARY|CHANNEL|FOLDER|RESOURCE|VAR_(?:GLOBAL|INPUT|PUTPUT|IN_OUT|ACCESS|TEMP|EXTERNAL|CONFIG)|VAR|METHOD|PROPERTY)\b/i,keyword:/\b(?:(?:END_)?(?:IF|WHILE|REPEAT|CASE|FOR)|ELSE|FROM|THEN|ELSIF|DO|TO|BY|PRIVATE|PUBLIC|PROTECTED|CONSTANT|RETURN|EXIT|CONTINUE|GOTO|JMP|AT|RETAIN|NON_RETAIN|TASK|WITH|UNTIL|USING|EXTENDS|IMPLEMENTS|GET|SET|__TRY|__CATCH|__FINALLY|__ENDTRY)\b/,variable:/\b(?:AT|BOOL|BYTE|(?:D|L)?WORD|U?(?:S|D|L)?INT|L?REAL|TIME(?:_OF_DAY)?|TOD|DT|DATE(?:_AND_TIME)?|STRING|ARRAY|ANY|POINTER)\b/,symbol:/%[IQM][XBWDL][\d.]*|%[IQ][\d.]*/,number:/\b(?:16#[\da-f]+|2#[01_]+|0x[\da-f]+)\b|\b(?:T|D|DT|TOD)#[\d_shmd:]*|\b[A-Z]*#[\d.,_]*|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,boolean:/\b(?:TRUE|FALSE|NULL)\b/,function:/\w+(?=\()/,operator:/(?:S?R?:?=>?|&&?|\*\*?|<=?|>=?|[-:^/+])|\b(?:OR|AND|MOD|NOT|XOR|LE|GE|EQ|NE|GT|LT)\b/,punctuation:/[();]/,type:{pattern:/#/,alias:"selector"}}}e.exports=t,t.displayName="iecst",t.aliases=[]},12017(e){"use strict";function t(e){var t;(t=e).languages.ignore={comment:/^#.*/m,entry:{pattern:/\S(?:.*(?:(?:\\ )|\S))?/,alias:"string",inside:{operator:/^!|\*\*?|\?/,regex:{pattern:/(^|[^\\])\[[^\[\]]*\]/,lookbehind:!0},punctuation:/\//}}},t.languages.gitignore=t.languages.ignore,t.languages.hgignore=t.languages.ignore,t.languages.npmignore=t.languages.ignore}e.exports=t,t.displayName="ignore",t.aliases=["gitignore","hgignore","npmignore"]},65175(e){"use strict";function t(e){e.languages.inform7={string:{pattern:/"[^"]*"/,inside:{substitution:{pattern:/\[[^\[\]]+\]/,inside:{delimiter:{pattern:/\[|\]/,alias:"punctuation"}}}}},comment:{pattern:/\[[^\[\]]+\]/,greedy:!0},title:{pattern:/^[ \t]*(?:volume|book|part(?! of)|chapter|section|table)\b.+/im,alias:"important"},number:{pattern:/(^|[^-])(?:\b\d+(?:\.\d+)?(?:\^\d+)?(?:(?!\d)\w+)?|\b(?:one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve))\b(?!-)/i,lookbehind:!0},verb:{pattern:/(^|[^-])\b(?:applying to|are|attacking|answering|asking|be(?:ing)?|burning|buying|called|carries|carry(?! out)|carrying|climbing|closing|conceal(?:s|ing)?|consulting|contain(?:s|ing)?|cutting|drinking|dropping|eating|enclos(?:es?|ing)|entering|examining|exiting|getting|giving|going|ha(?:ve|s|ving)|hold(?:s|ing)?|impl(?:y|ies)|incorporat(?:es?|ing)|inserting|is|jumping|kissing|listening|locking|looking|mean(?:s|ing)?|opening|provid(?:es?|ing)|pulling|pushing|putting|relat(?:es?|ing)|removing|searching|see(?:s|ing)?|setting|showing|singing|sleeping|smelling|squeezing|switching|support(?:s|ing)?|swearing|taking|tasting|telling|thinking|throwing|touching|turning|tying|unlock(?:s|ing)?|var(?:y|ies|ying)|waiting|waking|waving|wear(?:s|ing)?)\b(?!-)/i,lookbehind:!0,alias:"operator"},keyword:{pattern:/(^|[^-])\b(?:after|before|carry out|check|continue the action|definition(?= *:)|do nothing|else|end (?:if|unless|the story)|every turn|if|include|instead(?: of)?|let|move|no|now|otherwise|repeat|report|resume the story|rule for|running through|say(?:ing)?|stop the action|test|try(?:ing)?|understand|unless|use|when|while|yes)\b(?!-)/i,lookbehind:!0},property:{pattern:/(^|[^-])\b(?:adjacent(?! to)|carried|closed|concealed|contained|dark|described|edible|empty|enclosed|enterable|even|female|fixed in place|full|handled|held|improper-named|incorporated|inedible|invisible|lighted|lit|lock(?:able|ed)|male|marked for listing|mentioned|negative|neuter|non-(?:empty|full|recurring)|odd|opaque|open(?:able)?|plural-named|portable|positive|privately-named|proper-named|provided|publically-named|pushable between rooms|recurring|related|rubbing|scenery|seen|singular-named|supported|swinging|switch(?:able|ed(?: on| off)?)|touch(?:able|ed)|transparent|unconcealed|undescribed|unlit|unlocked|unmarked for listing|unmentioned|unopenable|untouchable|unvisited|variable|visible|visited|wearable|worn)\b(?!-)/i,lookbehind:!0,alias:"symbol"},position:{pattern:/(^|[^-])\b(?:above|adjacent to|back side of|below|between|down|east|everywhere|front side|here|in|inside(?: from)?|north(?:east|west)?|nowhere|on(?: top of)?|other side|outside(?: from)?|parts? of|regionally in|south(?:east|west)?|through|up|west|within)\b(?!-)/i,lookbehind:!0,alias:"keyword"},type:{pattern:/(^|[^-])\b(?:actions?|activit(?:y|ies)|actors?|animals?|backdrops?|containers?|devices?|directions?|doors?|holders?|kinds?|lists?|m[ae]n|nobody|nothing|nouns?|numbers?|objects?|people|persons?|player(?:'s holdall)?|regions?|relations?|rooms?|rule(?:book)?s?|scenes?|someone|something|supporters?|tables?|texts?|things?|time|vehicles?|wom[ae]n)\b(?!-)/i,lookbehind:!0,alias:"variable"},punctuation:/[.,:;(){}]/},e.languages.inform7.string.inside.substitution.inside.rest=e.languages.inform7,e.languages.inform7.string.inside.substitution.inside.rest.text={pattern:/\S(?:\s*\S)*/,alias:"comment"}}e.exports=t,t.displayName="inform7",t.aliases=[]},14970(e){"use strict";function t(e){e.languages.ini={comment:{pattern:/(^[ \f\t\v]*)[#;][^\n\r]*/m,lookbehind:!0},header:{pattern:/(^[ \f\t\v]*)\[[^\n\r\]]*\]?/m,lookbehind:!0,inside:{"section-name":{pattern:/(^\[[ \f\t\v]*)[^ \f\t\v\]]+(?:[ \f\t\v]+[^ \f\t\v\]]+)*/,lookbehind:!0,alias:"selector"},punctuation:/\[|\]/}},key:{pattern:/(^[ \f\t\v]*)[^ \f\n\r\t\v=]+(?:[ \f\t\v]+[^ \f\n\r\t\v=]+)*(?=[ \f\t\v]*=)/m,lookbehind:!0,alias:"attr-name"},value:{pattern:/(=[ \f\t\v]*)[^ \f\n\r\t\v]+(?:[ \f\t\v]+[^ \f\n\r\t\v]+)*/,lookbehind:!0,alias:"attr-value",inside:{"inner-value":{pattern:/^("|').+(?=\1$)/,lookbehind:!0}}},punctuation:/=/}}e.exports=t,t.displayName="ini",t.aliases=[]},30764(e){"use strict";function t(e){e.languages.io={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0},{pattern:/(^|[^\\])#.*/,lookbehind:!0}],"triple-quoted-string":{pattern:/"""(?:\\[\s\S]|(?!""")[^\\])*"""/,greedy:!0,alias:"string"},string:{pattern:/"(?:\\.|[^\\\r\n"])*"/,greedy:!0},keyword:/\b(?:activate|activeCoroCount|asString|block|break|catch|clone|collectGarbage|compileString|continue|do|doFile|doMessage|doString|else|elseif|exit|for|foreach|forward|getSlot|getEnvironmentVariable|hasSlot|if|ifFalse|ifNil|ifNilEval|ifTrue|isActive|isNil|isResumable|list|message|method|parent|pass|pause|perform|performWithArgList|print|println|proto|raise|raiseResumable|removeSlot|resend|resume|schedulerSleepSeconds|self|sender|setSchedulerSleepSeconds|setSlot|shallowCopy|slotNames|super|system|then|thisBlock|thisContext|call|try|type|uniqueId|updateSlot|wait|while|write|yield)\b/,builtin:/\b(?:Array|AudioDevice|AudioMixer|Block|Box|Buffer|CFunction|CGI|Color|Curses|DBM|DNSResolver|DOConnection|DOProxy|DOServer|Date|Directory|Duration|DynLib|Error|Exception|FFT|File|Fnmatch|Font|Future|GL|GLE|GLScissor|GLU|GLUCylinder|GLUQuadric|GLUSphere|GLUT|Host|Image|Importer|LinkList|List|Lobby|Locals|MD5|MP3Decoder|MP3Encoder|Map|Message|Movie|Notification|Number|Object|OpenGL|Point|Protos|Regex|SGML|SGMLElement|SGMLParser|SQLite|Server|Sequence|ShowMessage|SleepyCat|SleepyCatCursor|Socket|SocketManager|Sound|Soup|Store|String|Tree|UDPSender|UPDReceiver|URL|User|Warning|WeakLink|Random|BigNum)\b/,boolean:/\b(?:true|false|nil)\b/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e-?\d+)?/i,operator:/[=!*/%+\-^&|]=|>>?=?|<+*\-%$|,#][.:]?|[?^]\.?|[;\[]:?|[~}"i][.:]|[ACeEIjLor]\.|(?:[_\/\\qsux]|_?\d):)/,alias:"keyword"},number:/\b_?(?:(?!\d:)\d+(?:\.\d+)?(?:(?:[ejpx]|ad|ar)_?\d+(?:\.\d+)?)*(?:b_?[\da-z]+(?:\.[\da-z]+)?)?|_\b(?!\.))/,adverb:{pattern:/[~}]|[\/\\]\.?|[bfM]\.|t[.:]/,alias:"builtin"},operator:/[=a][.:]|_\./,conjunction:{pattern:/&(?:\.:?|:)?|[.:@][.:]?|[!D][.:]|[;dHT]\.|`:?|[\^LS]:|"/,alias:"variable"},punctuation:/[()]/}}e.exports=t,t.displayName="j",t.aliases=[]},15909(e){"use strict";function t(e){var t,n,r,i;t=e,n=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,i={pattern:RegExp((r=/(^|[^\w.])(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source)+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}},t.languages.java=t.languages.extend("clike",{"class-name":[i,{pattern:RegExp(r+/[A-Z]\w*(?=\s+\w+\s*[;,=()])/.source),lookbehind:!0,inside:i.inside}],keyword:n,function:[t.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0}}),t.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"}}),t.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":i,keyword:n,punctuation:/[<>(),.:]/,operator:/[?&|]/}},namespace:{pattern:RegExp(/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace(//g,function(){return n.source})),lookbehind:!0,inside:{punctuation:/\./}}})}e.exports=t,t.displayName="java",t.aliases=[]},36553(e,t,n){"use strict";var r=n(15909),i=n(9858);function a(e){var t,n,a,o;e.register(r),e.register(i),t=e,n=/(^(?:[\t ]*(?:\*\s*)*))[^*\s].*$/m,a=/#\s*\w+(?:\s*\([^()]*\))?/.source,o=/(?:\b[a-zA-Z]\w+\s*\.\s*)*\b[A-Z]\w*(?:\s*)?|/.source.replace(//g,function(){return a}),t.languages.javadoc=t.languages.extend("javadoclike",{}),t.languages.insertBefore("javadoc","keyword",{reference:{pattern:RegExp(/(@(?:exception|throws|see|link|linkplain|value)\s+(?:\*\s*)?)/.source+"(?:"+o+")"),lookbehind:!0,inside:{function:{pattern:/(#\s*)\w+(?=\s*\()/,lookbehind:!0},field:{pattern:/(#\s*)\w+/,lookbehind:!0},namespace:{pattern:/\b(?:[a-z]\w*\s*\.\s*)+/,inside:{punctuation:/\./}},"class-name":/\b[A-Z]\w*/,keyword:t.languages.java.keyword,punctuation:/[#()[\],.]/}},"class-name":{pattern:/(@param\s+)<[A-Z]\w*>/,lookbehind:!0,inside:{punctuation:/[.<>]/}},"code-section":[{pattern:/(\{@code\s+(?!\s))(?:[^\s{}]|\s+(?![\s}])|\{(?:[^{}]|\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*\})+(?=\s*\})/,lookbehind:!0,inside:{code:{pattern:n,lookbehind:!0,inside:t.languages.java,alias:"language-java"}}},{pattern:/(<(code|pre|tt)>(?!)\s*)\S(?:\S|\s+\S)*?(?=\s*<\/\2>)/,lookbehind:!0,inside:{line:{pattern:n,lookbehind:!0,inside:{tag:t.languages.markup.tag,entity:t.languages.markup.entity,code:{pattern:/.+/,inside:t.languages.java,alias:"language-java"}}}}}],tag:t.languages.markup.tag,entity:t.languages.markup.entity}),t.languages.javadoclike.addSupport("java",t.languages.javadoc)}e.exports=a,a.displayName="javadoc",a.aliases=[]},9858(e){"use strict";function t(e){!function(e){var t=e.languages.javadoclike={parameter:{pattern:/(^[\t ]*(?:\/{3}|\*|\/\*\*)\s*@(?:param|arg|arguments)\s+)\w+/m,lookbehind:!0},keyword:{pattern:/(^[\t ]*(?:\/{3}|\*|\/\*\*)\s*|\{)@[a-z][a-zA-Z-]+\b/m,lookbehind:!0},punctuation:/[{}]/};function n(t,n){var r="doc-comment",i=e.languages[t];if(i){var a=i[r];if(!a){var o={};o[r]={pattern:/(^|[^\\])\/\*\*[^/][\s\S]*?(?:\*\/|$)/,lookbehind:!0,alias:"comment"},a=(i=e.languages.insertBefore(t,"comment",o))[r]}if(a instanceof RegExp&&(a=i[r]={pattern:a}),Array.isArray(a))for(var s=0,u=a.length;s|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),e.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,e.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:e.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:e.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:e.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:e.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:e.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),e.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:e.languages.javascript}},string:/[\s\S]+/}}}),e.languages.markup&&(e.languages.markup.tag.addInlined("script","javascript"),e.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),e.languages.js=e.languages.javascript}e.exports=t,t.displayName="javascript",t.aliases=["js"]},11223(e){"use strict";function t(e){e.languages.javastacktrace={summary:{pattern:/^[\t ]*(?:(?:Caused by:|Suppressed:|Exception in thread "[^"]*")[\t ]+)?[\w$.]+(?::.*)?$/m,inside:{keyword:{pattern:/^(\s*)(?:(?:Caused by|Suppressed)(?=:)|Exception in thread)/m,lookbehind:!0},string:{pattern:/^(\s*)"[^"]*"/,lookbehind:!0},exceptions:{pattern:/^(:?\s*)[\w$.]+(?=:|$)/,lookbehind:!0,inside:{"class-name":/[\w$]+(?=$|:)/,namespace:/[a-z]\w*/,punctuation:/[.:]/}},message:{pattern:/(:\s*)\S.*/,lookbehind:!0,alias:"string"},punctuation:/:/}},"stack-frame":{pattern:/^[\t ]*at (?:[\w$./]|@[\w$.+-]*\/)+(?:)?\([^()]*\)/m,inside:{keyword:{pattern:/^(\s*)at(?= )/,lookbehind:!0},source:[{pattern:/(\()\w+\.\w+:\d+(?=\))/,lookbehind:!0,inside:{file:/^\w+\.\w+/,punctuation:/:/,"line-number":{pattern:/\d+/,alias:"number"}}},{pattern:/(\()[^()]*(?=\))/,lookbehind:!0,inside:{keyword:/^(?:Unknown Source|Native Method)$/}}],"class-name":/[\w$]+(?=\.(?:|[\w$]+)\()/,function:/(?:|[\w$]+)(?=\()/,"class-loader":{pattern:/(\s)[a-z]\w*(?:\.[a-z]\w*)*(?=\/[\w@$.]*\/)/,lookbehind:!0,alias:"namespace",inside:{punctuation:/\./}},module:{pattern:/([\s/])[a-z]\w*(?:\.[a-z]\w*)*(?:@[\w$.+-]*)?(?=\/)/,lookbehind:!0,inside:{version:{pattern:/(@)[\s\S]+/,lookbehind:!0,alias:"number"},punctuation:/[@.]/}},namespace:{pattern:/(?:[a-z]\w*\.)+/,inside:{punctuation:/\./}},punctuation:/[()/.]/}},more:{pattern:/^[\t ]*\.{3} \d+ [a-z]+(?: [a-z]+)*/m,inside:{punctuation:/\.{3}/,number:/\d+/,keyword:/\b[a-z]+(?: [a-z]+)*\b/}}}}e.exports=t,t.displayName="javastacktrace",t.aliases=[]},57957(e){"use strict";function t(e){e.languages.jexl={string:/(["'])(?:\\[\s\S]|(?!\1)[^\\])*\1/,transform:{pattern:/(\|\s*)[a-zA-Zа-яА-Я_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$][\wа-яА-Я\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$]*/,alias:"function",lookbehind:!0},function:/[a-zA-Zа-яА-Я_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$][\wа-яА-Я\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF$]*\s*(?=\()/,number:/\b\d+(?:\.\d+)?\b|\B\.\d+\b/,operator:/[<>!]=?|-|\+|&&|==|\|\|?|\/\/?|[?:*^%]/,boolean:/\b(?:true|false)\b/,keyword:/\bin\b/,punctuation:/[{}[\](),.]/}}e.exports=t,t.displayName="jexl",t.aliases=[]},75807(e){"use strict";function t(e){e.languages.jolie=e.languages.extend("clike",{string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},keyword:/\b(?:include|define|is_defined|undef|main|init|outputPort|inputPort|Location|Protocol|Interfaces|RequestResponse|OneWay|type|interface|extender|throws|cset|csets|forward|Aggregates|Redirects|embedded|courier|execution|sequential|concurrent|single|scope|install|throw|comp|cH|default|global|linkIn|linkOut|synchronized|this|new|for|if|else|while|in|Jolie|Java|Javascript|nullProcess|spawn|constants|with|provide|until|exit|foreach|instanceof|over|service)\b/,number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?l?/i,operator:/-[-=>]?|\+[+=]?|<[<=]?|[>=*!]=?|&&|\|\||[:?\/%^]/,punctuation:/[,.]/,builtin:/\b(?:undefined|string|int|void|long|Byte|bool|double|float|char|any)\b/,symbol:/[|;@]/}),delete e.languages.jolie["class-name"],e.languages.insertBefore("jolie","keyword",{function:{pattern:/((?:\b(?:outputPort|inputPort|in|service|courier)\b|@)\s*)\w+/,lookbehind:!0},aggregates:{pattern:/(\bAggregates\s*:\s*)(?:\w+(?:\s+with\s+\w+)?\s*,\s*)*\w+(?:\s+with\s+\w+)?/,lookbehind:!0,inside:{"with-extension":{pattern:/\bwith\s+\w+/,inside:{keyword:/\bwith\b/}},function:{pattern:/\w+/},punctuation:{pattern:/,/}}},redirects:{pattern:/(\bRedirects\s*:\s*)(?:\w+\s*=>\s*\w+\s*,\s*)*(?:\w+\s*=>\s*\w+)/,lookbehind:!0,inside:{punctuation:{pattern:/,/},function:{pattern:/\w+/},symbol:{pattern:/=>/}}}})}e.exports=t,t.displayName="jolie",t.aliases=[]},77935(e){"use strict";function t(e){var t,n,r,i,a;t=e,n=/\\\((?:[^()]|\([^()]*\))*\)/.source,r=RegExp(/"(?:[^"\r\n\\]|\\[^\r\n(]|__)*"/.source.replace(/__/g,function(){return n})),i={interpolation:{pattern:RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+n),lookbehind:!0,inside:{content:{pattern:/^(\\\()[\s\S]+(?=\)$)/,lookbehind:!0,inside:null},punctuation:/^\\\(|\)$/}}},a=t.languages.jq={comment:/#.*/,property:{pattern:RegExp(r.source+/(?=\s*:(?!:))/.source),greedy:!0,inside:i},string:{pattern:r,greedy:!0,inside:i},function:{pattern:/(\bdef\s+)[a-z_]\w+/i,lookbehind:!0},variable:/\B\$\w+/,"property-literal":{pattern:/\b[a-z_]\w*(?=\s*:(?!:))/i,alias:"property"},keyword:/\b(?:as|break|catch|def|elif|else|end|foreach|if|import|include|label|module|modulemeta|null|reduce|then|try|while)\b/,boolean:/\b(?:true|false)\b/,number:/(?:\b\d+\.|\B\.)?\b\d+(?:[eE][+-]?\d+)?\b/,operator:[{pattern:/\|=?/,alias:"pipe"},/\.\.|[!=<>]?=|\?\/\/|\/\/=?|[-+*/%]=?|[<>?]|\b(?:and|or|not)\b/],"c-style-function":{pattern:/\b[a-z_]\w*(?=\s*\()/i,alias:"function"},punctuation:/::|[()\[\]{},:;]|\.(?=\s*[\[\w$])/,dot:{pattern:/\./,alias:"important"}},i.interpolation.inside.content.inside=a}e.exports=t,t.displayName="jq",t.aliases=[]},46155(e){"use strict";function t(e){!function(e){function t(e,t){return RegExp(e.replace(//g,function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source}),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:(?:Uint|Int)(?:8|16|32)|Uint8Clamped|Float(?:32|64))?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|(?:Weak)?(?:Set|Map)|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|for|finally|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|location|navigator|performance|(?:local|session)Storage|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r=h.length)return;var n=e[t];if("string"==typeof n||"string"==typeof n.content){var r=h[o],i="string"==typeof n?n:n.content,a=i.indexOf(r);if(-1!==a){++o;var s=i.substring(0,a),u=c(l[r]),f=i.substring(a+r.length),d=[];if(s&&d.push(s),d.push(u),f){var b=[f];p(b),d.push.apply(d,b)}"string"==typeof n?(e.splice.apply(e,[t,1].concat(d)),t+=d.length-1):n.content=d}}else{var m=n.content;Array.isArray(m)?p(m):p([m])}}}return o=0,p(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[o("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),o("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),o("svg",/\bsvg/.source),o("markdown",/\b(?:md|markdown)/.source),o("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),o("sql",/\bsql/.source),t].filter(Boolean);var f={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function d(e){return"string"==typeof e?e:Array.isArray(e)?e.map(d).join(""):d(e.content)}e.hooks.add("after-tokenize",function(t){t.language in f&&n(t.tokens);function n(t){for(var r=0,i=t.length;r\s+)?)[A-Z]\w*(?:\.[A-Z]\w*)*/.source.replace(//g,function(){return a})),lookbehind:!0,inside:{punctuation:/\./}},{pattern:RegExp("(@[a-z]+\\s+)"+a),lookbehind:!0,inside:{string:n.string,number:n.number,boolean:n.boolean,keyword:t.languages.typescript.keyword,operator:/=>|\.\.\.|[&|?:*]/,punctuation:/[.,;=<>{}()[\]]/}}],example:{pattern:/(@example\s+(?!\s))(?:[^@\s]|\s+(?!\s))+?(?=\s*(?:\*\s*)?(?:@\w|\*\/))/,lookbehind:!0,inside:{code:{pattern:/^([\t ]*(?:\*\s*)?)\S.*$/m,lookbehind:!0,inside:n,alias:"language-javascript"}}}}),t.languages.javadoclike.addSupport("javascript",t.languages.jsdoc)}e.exports=a,a.displayName="jsdoc",a.aliases=[]},45950(e){"use strict";function t(e){e.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:true|false)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},e.languages.webmanifest=e.languages.json}e.exports=t,t.displayName="json",t.aliases=["webmanifest"]},50235(e,t,n){"use strict";var r=n(45950);function i(e){var t,n;e.register(r),n=/("|')(?:\\(?:\r\n?|\n|.)|(?!\1)[^\\\r\n])*\1/,(t=e).languages.json5=t.languages.extend("json",{property:[{pattern:RegExp(n.source+"(?=\\s*:)"),greedy:!0},{pattern:/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/,alias:"unquoted"}],string:{pattern:n,greedy:!0},number:/[+-]?\b(?:NaN|Infinity|0x[a-fA-F\d]+)\b|[+-]?(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+\b)?/})}e.exports=i,i.displayName="json5",i.aliases=[]},80963(e,t,n){"use strict";var r=n(45950);function i(e){e.register(r),e.languages.jsonp=e.languages.extend("json",{punctuation:/[{}[\]();,.]/}),e.languages.insertBefore("jsonp","punctuation",{function:/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*\()/})}e.exports=i,i.displayName="jsonp",i.aliases=[]},79358(e){"use strict";function t(e){e.languages.jsstacktrace={"error-message":{pattern:/^\S.*/m,alias:"string"},"stack-frame":{pattern:/(^[ \t]+)at[ \t].*/m,lookbehind:!0,inside:{"not-my-code":{pattern:/^at[ \t]+(?!\s)(?:node\.js||.*(?:node_modules|\(\)|\(|$|\(internal\/|\(node\.js)).*/m,alias:"comment"},filename:{pattern:/(\bat\s+(?!\s)|\()(?:[a-zA-Z]:)?[^():]+(?=:)/,lookbehind:!0,alias:"url"},function:{pattern:/(at\s+(?:new\s+)?)(?!\s)[_$a-zA-Z\xA0-\uFFFF<][.$\w\xA0-\uFFFF<>]*/,lookbehind:!0,inside:{punctuation:/\./}},punctuation:/[()]/,keyword:/\b(?:at|new)\b/,alias:{pattern:/\[(?:as\s+)?(?!\s)[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\]/,alias:"variable"},"line-number":{pattern:/:[0-9]+(?::[0-9]+)?\b/,alias:"number",inside:{punctuation:/:/}}}}}}e.exports=t,t.displayName="jsstacktrace",t.aliases=[]},96412(e){"use strict";function t(e){!function(e){var t=e.util.clone(e.languages.javascript),n=/(?:\s|\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))\*\/)/.source,r=/(?:\{(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])*\})/.source,i=/(?:\{*\.{3}(?:[^{}]|)*\})/.source;function a(e,t){return RegExp(e=e.replace(//g,function(){return n}).replace(//g,function(){return r}).replace(//g,function(){return i}),t)}i=a(i).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=a(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/i,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/i,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:a(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:a(/=/.source),inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx},alias:"language-javascript"}},e.languages.jsx.tag);var o=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(o).join(""):""},s=function(t){for(var n=[],r=0;r0&&n[n.length-1].tagName===o(i.content[0].content[1])&&n.pop():"/>"===i.content[i.content.length-1].content||n.push({tagName:o(i.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===i.type&&"{"===i.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===i.type&&"}"===i.content?n[n.length-1].openedBraces--:a=!0),(a||"string"==typeof i)&&n.length>0&&0===n[n.length-1].openedBraces){var u=o(i);r0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(u=o(t[r-1])+u,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",u,null,u)}i.content&&"string"!=typeof i.content&&s(i.content)}};e.hooks.add("after-tokenize",function(e){("jsx"===e.language||"tsx"===e.language)&&s(e.tokens)})}(e)}e.exports=t,t.displayName="jsx",t.aliases=[]},39259(e){"use strict";function t(e){e.languages.julia={comment:{pattern:/(^|[^\\])(?:#=(?:[^#=]|=(?!#)|#(?!=)|#=(?:[^#=]|=(?!#)|#(?!=))*=#)*=#|#.*)/,lookbehind:!0},regex:{pattern:/r"(?:\\.|[^"\\\r\n])*"[imsx]{0,4}/,greedy:!0},string:{pattern:/"""[\s\S]+?"""|(?:\b\w+)?"(?:\\.|[^"\\\r\n])*"|(^|[^\w'])'(?:\\[^\r\n][^'\r\n]*|[^\\\r\n])'|`(?:[^\\`\r\n]|\\.)*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:abstract|baremodule|begin|bitstype|break|catch|ccall|const|continue|do|else|elseif|end|export|finally|for|function|global|if|immutable|import|importall|in|let|local|macro|module|print|println|quote|return|struct|try|type|typealias|using|while)\b/,boolean:/\b(?:true|false)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[box])?(?:[\da-f]+(?:_[\da-f]+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[efp][+-]?\d+(?:_\d+)*)?j?/i,operator:/&&|\|\||[-+*^%÷⊻&$\\]=?|\/[\/=]?|!=?=?|\|[=>]?|<(?:<=?|[=:|])?|>(?:=|>>?=?)?|==?=?|[~≠≤≥'√∛]/,punctuation:/::?|[{}[\]();,.?]/,constant:/\b(?:(?:NaN|Inf)(?:16|32|64)?|im|pi)\b|[πℯ]/}}e.exports=t,t.displayName="julia",t.aliases=[]},35760(e){"use strict";function t(e){e.languages.keyman={comment:/\bc\s.*/i,function:/\[\s*(?:(?:CTRL|SHIFT|ALT|LCTRL|RCTRL|LALT|RALT|CAPS|NCAPS)\s+)*(?:[TKU]_[\w?]+|".+?"|'.+?')\s*\]/i,string:/("|').*?\1/,bold:[/&(?:baselayout|bitmap|capsononly|capsalwaysoff|shiftfreescaps|copyright|ethnologuecode|hotkey|includecodes|keyboardversion|kmw_embedcss|kmw_embedjs|kmw_helpfile|kmw_helptext|kmw_rtl|language|layer|layoutfile|message|mnemoniclayout|name|oldcharposmatching|platform|targets|version|visualkeyboard|windowslanguages)\b/i,/\b(?:bitmap|bitmaps|caps on only|caps always off|shift frees caps|copyright|hotkey|language|layout|message|name|version)\b/i],keyword:/\b(?:any|baselayout|beep|call|context|deadkey|dk|if|index|layer|notany|nul|outs|platform|return|reset|save|set|store|use)\b/i,atrule:/\b(?:ansi|begin|unicode|group|using keys|match|nomatch)\b/i,number:/\b(?:U\+[\dA-F]+|d\d+|x[\da-f]+|\d+)\b/i,operator:/[+>\\,()]/,tag:/\$(?:keyman|kmfl|weaver|keymanweb|keymanonly):/i}}e.exports=t,t.displayName="keyman",t.aliases=[]},19715(e){"use strict";function t(e){var t,n;(t=e).languages.kotlin=t.languages.extend("clike",{keyword:{pattern:/(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/,lookbehind:!0},function:[{pattern:/(?:`[^\r\n`]+`|\b\w+)(?=\s*\()/,greedy:!0},{pattern:/(\.)(?:`[^\r\n`]+`|\w+)(?=\s*\{)/,lookbehind:!0,greedy:!0}],number:/\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,operator:/\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/}),delete t.languages.kotlin["class-name"],t.languages.insertBefore("kotlin","string",{"raw-string":{pattern:/("""|''')[\s\S]*?\1/,alias:"string"}}),t.languages.insertBefore("kotlin","keyword",{annotation:{pattern:/\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,alias:"builtin"}}),t.languages.insertBefore("kotlin","function",{label:{pattern:/\b\w+@|@\w+\b/,alias:"symbol"}}),n=[{pattern:/\$\{[^}]+\}/,inside:{delimiter:{pattern:/^\$\{|\}$/,alias:"variable"},rest:t.languages.kotlin}},{pattern:/\$\w+/,alias:"variable"}],t.languages.kotlin.string.inside=t.languages.kotlin["raw-string"].inside={interpolation:n},t.languages.kt=t.languages.kotlin,t.languages.kts=t.languages.kotlin}e.exports=t,t.displayName="kotlin",t.aliases=["kt","kts"]},27614(e){"use strict";function t(e){!function(e){var t=/\s\x00-\x1f\x22-\x2f\x3a-\x3f\x5b-\x5e\x60\x7b-\x7e/.source;function n(e,n){return RegExp(e.replace(//g,t),n)}e.languages.kumir={comment:{pattern:/\|.*/},prolog:{pattern:/#.*/,greedy:!0},string:{pattern:/"[^\n\r"]*"|'[^\n\r']*'/,greedy:!0},boolean:{pattern:n(/(^|[])(?:да|нет)(?=[]|$)/.source),lookbehind:!0},"operator-word":{pattern:n(/(^|[])(?:и|или|не)(?=[]|$)/.source),lookbehind:!0,alias:"keyword"},"system-variable":{pattern:n(/(^|[])знач(?=[]|$)/.source),lookbehind:!0,alias:"keyword"},type:[{pattern:n(/(^|[])(?:вещ|лит|лог|сим|цел)(?:\x20*таб)?(?=[]|$)/.source),lookbehind:!0,alias:"builtin"},{pattern:n(/(^|[])(?:компл|сканкод|файл|цвет)(?=[]|$)/.source),lookbehind:!0,alias:"important"}],keyword:{pattern:n(/(^|[])(?:алг|арг(?:\x20*рез)?|ввод|ВКЛЮЧИТЬ|вс[её]|выбор|вывод|выход|дано|для|до|дс|если|иначе|исп|использовать|кон(?:(?:\x20+|_)исп)?|кц(?:(?:\x20+|_)при)?|надо|нач|нс|нц|от|пауза|пока|при|раза?|рез|стоп|таб|то|утв|шаг)(?=[]|$)/.source),lookbehind:!0},name:{pattern:n(/(^|[])[^\d][^]*(?:\x20+[^]+)*(?=[]|$)/.source),lookbehind:!0},number:{pattern:n(/(^|[])(?:\B\$[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)(?=[]|$)/.source,"i"),lookbehind:!0},punctuation:/:=|[(),:;\[\]]/,"operator-char":{pattern:/\*\*?|<[=>]?|>=?|[-+/=]/,alias:"operator"}},e.languages.kum=e.languages.kumir}(e)}e.exports=t,t.displayName="kumir",t.aliases=["kum"]},42876(e){"use strict";function t(e){var t,n,r;t=e,r={"equation-command":{pattern:n=/\\(?:[^a-z()[\]]|[a-z*]+)/i,alias:"regex"}},t.languages.latex={comment:/%.*/m,cdata:{pattern:/(\\begin\{((?:verbatim|lstlisting)\*?)\})[\s\S]*?(?=\\end\{\2\})/,lookbehind:!0},equation:[{pattern:/\$\$(?:\\[\s\S]|[^\\$])+\$\$|\$(?:\\[\s\S]|[^\\$])+\$|\\\([\s\S]*?\\\)|\\\[[\s\S]*?\\\]/,inside:r,alias:"string"},{pattern:/(\\begin\{((?:equation|math|eqnarray|align|multline|gather)\*?)\})[\s\S]*?(?=\\end\{\2\})/,lookbehind:!0,inside:r,alias:"string"}],keyword:{pattern:/(\\(?:begin|end|ref|cite|label|usepackage|documentclass)(?:\[[^\]]+\])?\{)[^}]+(?=\})/,lookbehind:!0},url:{pattern:/(\\url\{)[^}]+(?=\})/,lookbehind:!0},headline:{pattern:/(\\(?:part|chapter|section|subsection|frametitle|subsubsection|paragraph|subparagraph|subsubparagraph|subsubsubparagraph)\*?(?:\[[^\]]+\])?\{)[^}]+(?=\})/,lookbehind:!0,alias:"class-name"},function:{pattern:n,alias:"selector"},punctuation:/[[\]{}&]/},t.languages.tex=t.languages.latex,t.languages.context=t.languages.latex}e.exports=t,t.displayName="latex",t.aliases=["tex","context"]},2980(e,t,n){"use strict";var r=n(93205),i=n(88262);function a(e){var t,n;e.register(r),e.register(i),(t=e).languages.latte={comment:/^\{\*[\s\S]*/,ld:{pattern:/^\{(?:[=_]|\/?(?!\d|\w+\()\w+)?/,inside:{punctuation:/^\{\/?/,tag:{pattern:/.+/,alias:"important"}}},rd:{pattern:/\}$/,inside:{punctuation:/.+/}},php:{pattern:/\S(?:[\s\S]*\S)?/,alias:"language-php",inside:t.languages.php}},n=t.languages.extend("markup",{}),t.languages.insertBefore("inside","attr-value",{"n-attr":{pattern:/n:[\w-]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+))?/,inside:{"attr-name":{pattern:/^[^\s=]+/,alias:"important"},"attr-value":{pattern:/=[\s\S]+/,inside:{punctuation:[/^=/,{pattern:/^(\s*)["']|["']$/,lookbehind:!0}],php:{pattern:/\S(?:[\s\S]*\S)?/,inside:t.languages.php}}}}}},n.tag),t.hooks.add("before-tokenize",function(e){if("latte"===e.language){var r=/\{\*[\s\S]*?\*\}|\{[^'"\s{}*](?:[^"'/{}]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|\/\*(?:[^*]|\*(?!\/))*\*\/)*?\}/g;t.languages["markup-templating"].buildPlaceholders(e,"latte",r),e.grammar=n}}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"latte")})}e.exports=a,a.displayName="latte",a.aliases=[]},41701(e){"use strict";function t(e){e.languages.less=e.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/i,operator:/[+\-*\/]/}),e.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}})}e.exports=t,t.displayName="less",t.aliases=[]},42491(e,t,n){"use strict";var r=n(9997);function i(e){e.register(r),function(e){for(var t=/\((?:[^();"#\\]|\\[\s\S]|;.*(?!.)|"(?:[^"\\]|\\.)*"|#(?:\{(?:(?!#\})[\s\S])*#\}|[^{])|)*\)/.source,n=5,r=0;r/g,function(){return t});t=t.replace(//g,/[^\s\S]/.source);var i=e.languages.lilypond={comment:/%(?:(?!\{).*|\{[\s\S]*?%\})/,"embedded-scheme":{pattern:RegExp(/(^|[=\s])#(?:"(?:[^"\\]|\\.)*"|[^\s()"]*(?:[^\s()]|))/.source.replace(//g,function(){return t}),"m"),lookbehind:!0,greedy:!0,inside:{scheme:{pattern:/^(#)[\s\S]+$/,lookbehind:!0,alias:"language-scheme",inside:{"embedded-lilypond":{pattern:/#\{[\s\S]*?#\}/,greedy:!0,inside:{punctuation:/^#\{|#\}$/,lilypond:{pattern:/[\s\S]+/,alias:"language-lilypond",inside:null}}},rest:e.languages.scheme}},punctuation:/#/}},string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0},"class-name":{pattern:/(\\new\s+)[\w-]+/,lookbehind:!0},keyword:{pattern:/\\[a-z][-\w]*/i,inside:{punctuation:/^\\/}},operator:/[=|]|<<|>>/,punctuation:{pattern:/(^|[a-z\d])(?:'+|,+|[_^]?-[_^]?(?:[-+^!>._]|(?=\d))|[_^]\.?|[.!])|[{}()[\]<>^~]|\\[()[\]<>\\!]|--|__/,lookbehind:!0},number:/\b\d+(?:\/\d+)?\b/};i["embedded-scheme"].inside.scheme.inside["embedded-lilypond"].inside.lilypond.inside=i,e.languages.ly=i}(e)}e.exports=i,i.displayName="lilypond",i.aliases=[]},34927(e,t,n){"use strict";var r=n(93205);function i(e){e.register(r),e.languages.liquid={comment:{pattern:/(^\{%\s*comment\s*%\})[\s\S]+(?=\{%\s*endcomment\s*%\}$)/,lookbehind:!0},delimiter:{pattern:/^\{(?:\{\{|[%\{])-?|-?(?:\}\}|[%\}])\}$/,alias:"punctuation"},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},keyword:/\b(?:as|assign|break|continue|cycle|decrement|echo|else|elsif|(?:end)?(?:capture|case|comment|for|form|if|paginate|style|raw|tablerow|unless)|in|include|increment|limit|liquid|offset|range|render|reversed|section|when|with)\b/,function:[{pattern:/(\|\s*)\w+/,lookbehind:!0,alias:"filter"},{pattern:/(\.\s*)(?:first|last|size)/,lookbehind:!0}],boolean:/\b(?:true|false|nil)\b/,range:{pattern:/\.\./,alias:"operator"},number:/\b\d+(?:\.\d+)?\b/,operator:/[!=]=|<>|[<>]=?|[|?:=-]|\b(?:and|or|contains(?=\s))\b/,punctuation:/[.,\[\]()]/},e.hooks.add("before-tokenize",function(t){var n=/\{%\s*comment\s*%\}[\s\S]*?\{%\s*endcomment\s*%\}|\{(?:%[\s\S]*?%|\{\{[\s\S]*?\}\}|\{[\s\S]*?\})\}/g,r=!1;e.languages["markup-templating"].buildPlaceholders(t,"liquid",n,function(e){var t=/^\{%-?\s*(\w+)/.exec(e);if(t){var n=t[1];if("raw"===n&&!r)return r=!0,!0;if("endraw"===n)return r=!1,!0}return!r})}),e.hooks.add("after-tokenize",function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"liquid")})}e.exports=i,i.displayName="liquid",i.aliases=[]},3848(e){"use strict";function t(e){!function(e){function t(e){return RegExp("(\\()"+e+"(?=[\\s\\)])")}function n(e){return RegExp("([\\s([])"+e+"(?=[\\s)])")}var r="[-+*/_~!@$%^=<>{}\\w]+",i="&"+r,a="(\\()",o="(?=\\))",s="(?=\\s)",u={heading:{pattern:/;;;.*/,alias:["comment","title"]},comment:/;.*/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0,inside:{argument:/[-A-Z]+(?=[.,\s])/,symbol:RegExp("`"+r+"'")}},"quoted-symbol":{pattern:RegExp("#?'"+r),alias:["variable","symbol"]},"lisp-property":{pattern:RegExp(":"+r),alias:"property"},splice:{pattern:RegExp(",@?"+r),alias:["symbol","variable"]},keyword:[{pattern:RegExp(a+"(?:(?:lexical-)?let\\*?|(?:cl-)?letf|if|when|while|unless|cons|cl-loop|and|or|not|cond|setq|error|message|null|require|provide|use-package)"+s),lookbehind:!0},{pattern:RegExp(a+"(?:for|do|collect|return|finally|append|concat|in|by)"+s),lookbehind:!0}],declare:{pattern:t("declare"),lookbehind:!0,alias:"keyword"},interactive:{pattern:t("interactive"),lookbehind:!0,alias:"keyword"},boolean:{pattern:n("(?:t|nil)"),lookbehind:!0},number:{pattern:n("[-+]?\\d+(?:\\.\\d*)?"),lookbehind:!0},defvar:{pattern:RegExp(a+"def(?:var|const|custom|group)\\s+"+r),lookbehind:!0,inside:{keyword:/^def[a-z]+/,variable:RegExp(r)}},defun:{pattern:RegExp(a+"(?:cl-)?(?:defun\\*?|defmacro)\\s+"+r+"\\s+\\([\\s\\S]*?\\)"),lookbehind:!0,inside:{keyword:/^(?:cl-)?def\S+/,arguments:null,function:{pattern:RegExp("(^\\s)"+r),lookbehind:!0},punctuation:/[()]/}},lambda:{pattern:RegExp(a+"lambda\\s+\\(\\s*(?:&?"+r+"(?:\\s+&?"+r+")*\\s*)?\\)"),lookbehind:!0,inside:{keyword:/^lambda/,arguments:null,punctuation:/[()]/}},car:{pattern:RegExp(a+r),lookbehind:!0},punctuation:[/(?:['`,]?\(|[)\[\]])/,{pattern:/(\s)\.(?=\s)/,lookbehind:!0}]},c={"lisp-marker":RegExp(i),rest:{argument:{pattern:RegExp(r),alias:"variable"},varform:{pattern:RegExp(a+r+"\\s+\\S[\\s\\S]*"+o),lookbehind:!0,inside:{string:u.string,boolean:u.boolean,number:u.number,symbol:u.symbol,punctuation:/[()]/}}}},l="\\S+(?:\\s+\\S+)*",f={pattern:RegExp(a+"[\\s\\S]*"+o),lookbehind:!0,inside:{"rest-vars":{pattern:RegExp("&(?:rest|body)\\s+"+l),inside:c},"other-marker-vars":{pattern:RegExp("&(?:optional|aux)\\s+"+l),inside:c},keys:{pattern:RegExp("&key\\s+"+l+"(?:\\s+&allow-other-keys)?"),inside:c},argument:{pattern:RegExp(r),alias:"variable"},punctuation:/[()]/}};u.lambda.inside.arguments=f,u.defun.inside.arguments=e.util.clone(f),u.defun.inside.arguments.inside.sublist=f,e.languages.lisp=u,e.languages.elisp=u,e.languages.emacs=u,e.languages["emacs-lisp"]=u}(e)}e.exports=t,t.displayName="lisp",t.aliases=[]},41469(e){"use strict";function t(e){e.languages.livescript={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\])#.*/,lookbehind:!0}],"interpolated-string":{pattern:/(^|[^"])("""|")(?:\\[\s\S]|(?!\2)[^\\])*\2(?!")/,lookbehind:!0,greedy:!0,inside:{variable:{pattern:/(^|[^\\])#[a-z_](?:-?[a-z]|[\d_])*/m,lookbehind:!0},interpolation:{pattern:/(^|[^\\])#\{[^}]+\}/m,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^#\{|\}$/,alias:"variable"}}},string:/[\s\S]+/}},string:[{pattern:/('''|')(?:\\[\s\S]|(?!\1)[^\\])*\1/,greedy:!0},{pattern:/<\[[\s\S]*?\]>/,greedy:!0},/\\[^\s,;\])}]+/],regex:[{pattern:/\/\/(?:\[[^\r\n\]]*\]|\\.|(?!\/\/)[^\\\[])+\/\/[gimyu]{0,5}/,greedy:!0,inside:{comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0}}},{pattern:/\/(?:\[[^\r\n\]]*\]|\\.|[^/\\\r\n\[])+\/[gimyu]{0,5}/,greedy:!0}],keyword:{pattern:/(^|(?!-).)\b(?:break|case|catch|class|const|continue|default|do|else|extends|fallthrough|finally|for(?: ever)?|function|if|implements|it|let|loop|new|null|otherwise|own|return|super|switch|that|then|this|throw|try|unless|until|var|void|when|while|yield)(?!-)\b/m,lookbehind:!0},"keyword-operator":{pattern:/(^|[^-])\b(?:(?:delete|require|typeof)!|(?:and|by|delete|export|from|import(?: all)?|in|instanceof|is(?:nt| not)?|not|of|or|til|to|typeof|with|xor)(?!-)\b)/m,lookbehind:!0,alias:"operator"},boolean:{pattern:/(^|[^-])\b(?:false|no|off|on|true|yes)(?!-)\b/m,lookbehind:!0},argument:{pattern:/(^|(?!\.&\.)[^&])&(?!&)\d*/m,lookbehind:!0,alias:"variable"},number:/\b(?:\d+~[\da-z]+|\d[\d_]*(?:\.\d[\d_]*)?(?:[a-z]\w*)?)/i,identifier:/[a-z_](?:-?[a-z]|[\d_])*/i,operator:[{pattern:/( )\.(?= )/,lookbehind:!0},/\.(?:[=~]|\.\.?)|\.(?:[&|^]|<<|>>>?)\.|:(?:=|:=?)|&&|\|[|>]|<(?:<[>=?]?|-(?:->?|>)?|\+\+?|@@?|%%?|\*\*?|!(?:~?=|--?>|~?~>)?|~(?:~?>|=)?|==?|\^\^?|[\/?]/],punctuation:/[(){}\[\]|.,:;`]/},e.languages.livescript["interpolated-string"].inside.interpolation.inside.rest=e.languages.livescript}e.exports=t,t.displayName="livescript",t.aliases=[]},73070(e){"use strict";function t(e){var t;(t=e).languages.llvm={comment:/;.*/,string:{pattern:/"[^"]*"/,greedy:!0},boolean:/\b(?:true|false)\b/,variable:/[%@!#](?:(?!\d)(?:[-$.\w]|\\[a-f\d]{2})+|\d+)/i,label:/(?!\d)(?:[-$.\w]|\\[a-f\d]{2})+:/i,type:{pattern:/\b(?:double|float|fp128|half|i[1-9]\d*|label|metadata|ppc_fp128|token|void|x86_fp80|x86_mmx)\b/,alias:"class-name"},keyword:/\b[a-z_][a-z_0-9]*\b/,number:/[+-]?\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b|\b0x[\dA-Fa-f]+\b|\b0xK[\dA-Fa-f]{20}\b|\b0x[ML][\dA-Fa-f]{32}\b|\b0xH[\dA-Fa-f]{4}\b/,punctuation:/[{}[\];(),.!*=<>]/}}e.exports=t,t.displayName="llvm",t.aliases=[]},35049(e){"use strict";function t(e){e.languages.log={string:{pattern:/"(?:[^"\\\r\n]|\\.)*"|'(?![st] | \w)(?:[^'\\\r\n]|\\.)*'/,greedy:!0},level:[{pattern:/\b(?:ALERT|CRIT|CRITICAL|EMERG|EMERGENCY|ERR|ERROR|FAILURE|FATAL|SEVERE)\b/,alias:["error","important"]},{pattern:/\b(?:WARN|WARNING|WRN)\b/,alias:["warning","important"]},{pattern:/\b(?:DISPLAY|INF|INFO|NOTICE|STATUS)\b/,alias:["info","keyword"]},{pattern:/\b(?:DBG|DEBUG|FINE)\b/,alias:["debug","keyword"]},{pattern:/\b(?:FINER|FINEST|TRACE|TRC|VERBOSE|VRB)\b/,alias:["trace","comment"]}],property:{pattern:/((?:^|[\]|])[ \t]*)[a-z_](?:[\w-]|\b\/\b)*(?:[. ]\(?\w(?:[\w-]|\b\/\b)*\)?)*:(?=\s)/im,lookbehind:!0},separator:{pattern:/(^|[^-+])-{3,}|={3,}|\*{3,}|- - /m,lookbehind:!0,alias:"comment"},url:/\b(?:https?|ftp|file):\/\/[^\s|,;'"]*[^\s|,;'">.]/,email:{pattern:/(^|\s)[-\w+.]+@[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+(?=\s)/,lookbehind:!0,alias:"url"},"ip-address":{pattern:/\b(?:\d{1,3}(?:\.\d{1,3}){3})\b/i,alias:"constant"},"mac-address":{pattern:/\b[a-f0-9]{2}(?::[a-f0-9]{2}){5}\b/i,alias:"constant"},domain:{pattern:/(^|\s)[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*\.[a-z][a-z0-9-]+(?=\s)/,lookbehind:!0,alias:"constant"},uuid:{pattern:/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i,alias:"constant"},hash:{pattern:/\b(?:[a-f0-9]{32}){1,2}\b/i,alias:"constant"},"file-path":{pattern:/\b[a-z]:[\\/][^\s|,;:(){}\[\]"']+|(^|[\s:\[\](>|])\.{0,2}\/\w[^\s|,;:(){}\[\]"']*/i,lookbehind:!0,greedy:!0,alias:"string"},date:{pattern:RegExp(/\b\d{4}[-/]\d{2}[-/]\d{2}(?:T(?=\d{1,2}:)|(?=\s\d{1,2}:))/.source+"|"+/\b\d{1,4}[-/ ](?:\d{1,2}|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[-/ ]\d{2,4}T?\b/.source+"|"+/\b(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)(?:\s{1,2}(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))?|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s{1,2}\d{1,2}\b/.source,"i"),alias:"number"},time:{pattern:/\b\d{1,2}:\d{1,2}:\d{1,2}(?:[.,:]\d+)?(?:\s?[+-]\d{2}:?\d{2}|Z)?\b/,alias:"number"},boolean:/\b(?:true|false|null)\b/i,number:{pattern:/(^|[^.\w])(?:0x[a-f0-9]+|0o[0-7]+|0b[01]+|v?\d[\da-f]*(?:\.\d+)*(?:e[+-]?\d+)?[a-z]{0,3}\b)\b(?!\.\w)/i,lookbehind:!0},operator:/[;:?<=>~/@!$%&+\-|^(){}*#]/,punctuation:/[\[\].,]/}}e.exports=t,t.displayName="log",t.aliases=[]},8789(e){"use strict";function t(e){e.languages.lolcode={comment:[/\bOBTW\s[\s\S]*?\sTLDR\b/,/\bBTW.+/],string:{pattern:/"(?::.|[^":])*"/,inside:{variable:/:\{[^}]+\}/,symbol:[/:\([a-f\d]+\)/i,/:\[[^\]]+\]/,/:[)>o":]/]},greedy:!0},number:/(?:\B-)?(?:\b\d+(?:\.\d*)?|\B\.\d+)/,symbol:{pattern:/(^|\s)(?:A )?(?:YARN|NUMBR|NUMBAR|TROOF|BUKKIT|NOOB)(?=\s|,|$)/,lookbehind:!0,inside:{keyword:/A(?=\s)/}},label:{pattern:/((?:^|\s)(?:IM IN YR|IM OUTTA YR) )[a-zA-Z]\w*/,lookbehind:!0,alias:"string"},function:{pattern:/((?:^|\s)(?:I IZ|HOW IZ I|IZ) )[a-zA-Z]\w*/,lookbehind:!0},keyword:[{pattern:/(^|\s)(?:O HAI IM|KTHX|HAI|KTHXBYE|I HAS A|ITZ(?: A)?|R|AN|MKAY|SMOOSH|MAEK|IS NOW(?: A)?|VISIBLE|GIMMEH|O RLY\?|YA RLY|NO WAI|OIC|MEBBE|WTF\?|OMG|OMGWTF|GTFO|IM IN YR|IM OUTTA YR|FOUND YR|YR|TIL|WILE|UPPIN|NERFIN|I IZ|HOW IZ I|IF U SAY SO|SRS|HAS A|LIEK(?: A)?|IZ)(?=\s|,|$)/,lookbehind:!0},/'Z(?=\s|,|$)/],boolean:{pattern:/(^|\s)(?:WIN|FAIL)(?=\s|,|$)/,lookbehind:!0},variable:{pattern:/(^|\s)IT(?=\s|,|$)/,lookbehind:!0},operator:{pattern:/(^|\s)(?:NOT|BOTH SAEM|DIFFRINT|(?:SUM|DIFF|PRODUKT|QUOSHUNT|MOD|BIGGR|SMALLR|BOTH|EITHER|WON|ALL|ANY) OF)(?=\s|,|$)/,lookbehind:!0},punctuation:/\.{3}|…|,|!/}}e.exports=t,t.displayName="lolcode",t.aliases=[]},59803(e){"use strict";function t(e){e.languages.lua={comment:/^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[^z]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,greedy:!0},number:/\b0x[a-f\d]+(?:\.[a-f\d]*)?(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|(?:\.\d*)?(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,keyword:/\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,function:/(?!\d)\w+(?=\s*(?:[({]))/,operator:[/[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,{pattern:/(^|[^.])\.\.(?!\.)/,lookbehind:!0}],punctuation:/[\[\](){},;]|\.+|:+/}}e.exports=t,t.displayName="lua",t.aliases=[]},33055(e){"use strict";function t(e){e.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},builtin:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,symbol:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:[/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,{pattern:/(\()(?:addsuffix|abspath|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:s|list)?)(?=[ \t])/,lookbehind:!0}],operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/}}e.exports=t,t.displayName="makefile",t.aliases=[]},90542(e){"use strict";function t(e){!function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,function(){return t}),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,i=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,function(){return r}),a=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"font-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+i+a+"(?:"+i+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+i+a+")(?:"+i+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+i+")"+a+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+i+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach(function(t){["url","bold","italic","strike","code-snippet"].forEach(function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])})}),e.hooks.add("after-tokenize",function(e){("markdown"===e.language||"md"===e.language)&&t(e.tokens);function t(e){if(e&&"string"!=typeof e)for(var n=0,r=e.length;n=a.length);u++){var c=s[u];if("string"==typeof c||c.content&&"string"==typeof c.content){var l=a[i],f=n.tokenStack[l],d="string"==typeof c?c:c.content,h=t(r,l),p=d.indexOf(h);if(p>-1){++i;var b=d.substring(0,p),m=new e.Token(r,e.tokenize(f,n.grammar),"language-"+r,f),g=d.substring(p+h.length),v=[];b&&v.push.apply(v,o([b])),v.push(m),g&&v.push.apply(v,o([g])),"string"==typeof c?s.splice.apply(s,[u,1].concat(v)):c.content=v}}else c.content&&o(c.content)}return s}}}})}(e)}e.exports=t,t.displayName="markupTemplating",t.aliases=[]},2717(e){"use strict";function t(e){e.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/,name:/[^\s<>'"]+/}},cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},e.languages.markup.tag.inside["attr-value"].inside.entity=e.languages.markup.entity,e.languages.markup.doctype.inside["internal-subset"].inside=e.languages.markup,e.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.value.replace(/&/,"&"))}),Object.defineProperty(e.languages.markup.tag,"addInlined",{value:function(t,n){var r={};r["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:e.languages[n]},r.cdata=/^$/i;var i={"included-cdata":{pattern://i,inside:r}};i["language-"+n]={pattern:/[\s\S]+/,inside:e.languages[n]};var a={};a[t]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return t}),"i"),lookbehind:!0,greedy:!0,inside:i},e.languages.insertBefore("markup","cdata",a)}}),Object.defineProperty(e.languages.markup.tag,"addAttribute",{value:function(t,n){e.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+t+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:e.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),e.languages.html=e.languages.markup,e.languages.mathml=e.languages.markup,e.languages.svg=e.languages.markup,e.languages.xml=e.languages.extend("markup",{}),e.languages.ssml=e.languages.xml,e.languages.atom=e.languages.xml,e.languages.rss=e.languages.xml}e.exports=t,t.displayName="markup",t.aliases=["html","mathml","svg","xml","ssml","atom","rss"]},27992(e){"use strict";function t(e){e.languages.matlab={comment:[/%\{[\s\S]*?\}%/,/%.+/],string:{pattern:/\B'(?:''|[^'\r\n])*'/,greedy:!0},number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+)?(?:[ij])?|\b[ij]\b/,keyword:/\b(?:break|case|catch|continue|else|elseif|end|for|function|if|inf|NaN|otherwise|parfor|pause|pi|return|switch|try|while)\b/,function:/\b(?!\d)\w+(?=\s*\()/,operator:/\.?[*^\/\\']|[+\-:@]|[<>=~]=?|&&?|\|\|?/,punctuation:/\.{3}|[.,;\[\](){}!]/}}e.exports=t,t.displayName="matlab",t.aliases=[]},606(e){"use strict";function t(e){e.languages.mel={comment:/\/\/.*/,code:{pattern:/`(?:\\.|[^\\`\r\n])*`/,greedy:!0,alias:"italic",inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"}}},string:{pattern:/"(?:\\.|[^\\"\r\n])*"/,greedy:!0},variable:/\$\w+/,number:/\b0x[\da-fA-F]+\b|\b\d+(?:\.\d*)?|\B\.\d+/,flag:{pattern:/-[^\d\W]\w*/,alias:"operator"},keyword:/\b(?:break|case|continue|default|do|else|float|for|global|if|in|int|matrix|proc|return|string|switch|vector|while)\b/,function:/\b\w+(?=\()|\b(?:about|abs|addAttr|addAttributeEditorNodeHelp|addDynamic|addNewShelfTab|addPP|addPanelCategory|addPrefixToName|advanceToNextDrivenKey|affectedNet|affects|aimConstraint|air|alias|aliasAttr|align|alignCtx|alignCurve|alignSurface|allViewFit|ambientLight|angle|angleBetween|animCone|animCurveEditor|animDisplay|animView|annotate|appendStringArray|applicationName|applyAttrPreset|applyTake|arcLenDimContext|arcLengthDimension|arclen|arrayMapper|art3dPaintCtx|artAttrCtx|artAttrPaintVertexCtx|artAttrSkinPaintCtx|artAttrTool|artBuildPaintMenu|artFluidAttrCtx|artPuttyCtx|artSelectCtx|artSetPaintCtx|artUserPaintCtx|assignCommand|assignInputDevice|assignViewportFactories|attachCurve|attachDeviceAttr|attachSurface|attrColorSliderGrp|attrCompatibility|attrControlGrp|attrEnumOptionMenu|attrEnumOptionMenuGrp|attrFieldGrp|attrFieldSliderGrp|attrNavigationControlGrp|attrPresetEditWin|attributeExists|attributeInfo|attributeMenu|attributeQuery|autoKeyframe|autoPlace|bakeClip|bakeFluidShading|bakePartialHistory|bakeResults|bakeSimulation|basename|basenameEx|batchRender|bessel|bevel|bevelPlus|binMembership|bindSkin|blend2|blendShape|blendShapeEditor|blendShapePanel|blendTwoAttr|blindDataType|boneLattice|boundary|boxDollyCtx|boxZoomCtx|bufferCurve|buildBookmarkMenu|buildKeyframeMenu|button|buttonManip|CBG|cacheFile|cacheFileCombine|cacheFileMerge|cacheFileTrack|camera|cameraView|canCreateManip|canvas|capitalizeString|catch|catchQuiet|ceil|changeSubdivComponentDisplayLevel|changeSubdivRegion|channelBox|character|characterMap|characterOutlineEditor|characterize|chdir|checkBox|checkBoxGrp|checkDefaultRenderGlobals|choice|circle|circularFillet|clamp|clear|clearCache|clip|clipEditor|clipEditorCurrentTimeCtx|clipSchedule|clipSchedulerOutliner|clipTrimBefore|closeCurve|closeSurface|cluster|cmdFileOutput|cmdScrollFieldExecuter|cmdScrollFieldReporter|cmdShell|coarsenSubdivSelectionList|collision|color|colorAtPoint|colorEditor|colorIndex|colorIndexSliderGrp|colorSliderButtonGrp|colorSliderGrp|columnLayout|commandEcho|commandLine|commandPort|compactHairSystem|componentEditor|compositingInterop|computePolysetVolume|condition|cone|confirmDialog|connectAttr|connectControl|connectDynamic|connectJoint|connectionInfo|constrain|constrainValue|constructionHistory|container|containsMultibyte|contextInfo|control|convertFromOldLayers|convertIffToPsd|convertLightmap|convertSolidTx|convertTessellation|convertUnit|copyArray|copyFlexor|copyKey|copySkinWeights|cos|cpButton|cpCache|cpClothSet|cpCollision|cpConstraint|cpConvClothToMesh|cpForces|cpGetSolverAttr|cpPanel|cpProperty|cpRigidCollisionFilter|cpSeam|cpSetEdit|cpSetSolverAttr|cpSolver|cpSolverTypes|cpTool|cpUpdateClothUVs|createDisplayLayer|createDrawCtx|createEditor|createLayeredPsdFile|createMotionField|createNewShelf|createNode|createRenderLayer|createSubdivRegion|cross|crossProduct|ctxAbort|ctxCompletion|ctxEditMode|ctxTraverse|currentCtx|currentTime|currentTimeCtx|currentUnit|curve|curveAddPtCtx|curveCVCtx|curveEPCtx|curveEditorCtx|curveIntersect|curveMoveEPCtx|curveOnSurface|curveSketchCtx|cutKey|cycleCheck|cylinder|dagPose|date|defaultLightListCheckBox|defaultNavigation|defineDataServer|defineVirtualDevice|deformer|deg_to_rad|delete|deleteAttr|deleteShadingGroupsAndMaterials|deleteShelfTab|deleteUI|deleteUnusedBrushes|delrandstr|detachCurve|detachDeviceAttr|detachSurface|deviceEditor|devicePanel|dgInfo|dgdirty|dgeval|dgtimer|dimWhen|directKeyCtx|directionalLight|dirmap|dirname|disable|disconnectAttr|disconnectJoint|diskCache|displacementToPoly|displayAffected|displayColor|displayCull|displayLevelOfDetail|displayPref|displayRGBColor|displaySmoothness|displayStats|displayString|displaySurface|distanceDimContext|distanceDimension|doBlur|dolly|dollyCtx|dopeSheetEditor|dot|dotProduct|doubleProfileBirailSurface|drag|dragAttrContext|draggerContext|dropoffLocator|duplicate|duplicateCurve|duplicateSurface|dynCache|dynControl|dynExport|dynExpression|dynGlobals|dynPaintEditor|dynParticleCtx|dynPref|dynRelEdPanel|dynRelEditor|dynamicLoad|editAttrLimits|editDisplayLayerGlobals|editDisplayLayerMembers|editRenderLayerAdjustment|editRenderLayerGlobals|editRenderLayerMembers|editor|editorTemplate|effector|emit|emitter|enableDevice|encodeString|endString|endsWith|env|equivalent|equivalentTol|erf|error|eval|evalDeferred|evalEcho|event|exactWorldBoundingBox|exclusiveLightCheckBox|exec|executeForEachObject|exists|exp|expression|expressionEditorListen|extendCurve|extendSurface|extrude|fcheck|fclose|feof|fflush|fgetline|fgetword|file|fileBrowserDialog|fileDialog|fileExtension|fileInfo|filetest|filletCurve|filter|filterCurve|filterExpand|filterStudioImport|findAllIntersections|findAnimCurves|findKeyframe|findMenuItem|findRelatedSkinCluster|finder|firstParentOf|fitBspline|flexor|floatEq|floatField|floatFieldGrp|floatScrollBar|floatSlider|floatSlider2|floatSliderButtonGrp|floatSliderGrp|floor|flow|fluidCacheInfo|fluidEmitter|fluidVoxelInfo|flushUndo|fmod|fontDialog|fopen|formLayout|format|fprint|frameLayout|fread|freeFormFillet|frewind|fromNativePath|fwrite|gamma|gauss|geometryConstraint|getApplicationVersionAsFloat|getAttr|getClassification|getDefaultBrush|getFileList|getFluidAttr|getInputDeviceRange|getMayaPanelTypes|getModifiers|getPanel|getParticleAttr|getPluginResource|getenv|getpid|glRender|glRenderEditor|globalStitch|gmatch|goal|gotoBindPose|grabColor|gradientControl|gradientControlNoAttr|graphDollyCtx|graphSelectContext|graphTrackCtx|gravity|grid|gridLayout|group|groupObjectsByName|HfAddAttractorToAS|HfAssignAS|HfBuildEqualMap|HfBuildFurFiles|HfBuildFurImages|HfCancelAFR|HfConnectASToHF|HfCreateAttractor|HfDeleteAS|HfEditAS|HfPerformCreateAS|HfRemoveAttractorFromAS|HfSelectAttached|HfSelectAttractors|HfUnAssignAS|hardenPointCurve|hardware|hardwareRenderPanel|headsUpDisplay|headsUpMessage|help|helpLine|hermite|hide|hilite|hitTest|hotBox|hotkey|hotkeyCheck|hsv_to_rgb|hudButton|hudSlider|hudSliderButton|hwReflectionMap|hwRender|hwRenderLoad|hyperGraph|hyperPanel|hyperShade|hypot|iconTextButton|iconTextCheckBox|iconTextRadioButton|iconTextRadioCollection|iconTextScrollList|iconTextStaticLabel|ikHandle|ikHandleCtx|ikHandleDisplayScale|ikSolver|ikSplineHandleCtx|ikSystem|ikSystemInfo|ikfkDisplayMethod|illustratorCurves|image|imfPlugins|inheritTransform|insertJoint|insertJointCtx|insertKeyCtx|insertKnotCurve|insertKnotSurface|instance|instanceable|instancer|intField|intFieldGrp|intScrollBar|intSlider|intSliderGrp|interToUI|internalVar|intersect|iprEngine|isAnimCurve|isConnected|isDirty|isParentOf|isSameObject|isTrue|isValidObjectName|isValidString|isValidUiName|isolateSelect|itemFilter|itemFilterAttr|itemFilterRender|itemFilterType|joint|jointCluster|jointCtx|jointDisplayScale|jointLattice|keyTangent|keyframe|keyframeOutliner|keyframeRegionCurrentTimeCtx|keyframeRegionDirectKeyCtx|keyframeRegionDollyCtx|keyframeRegionInsertKeyCtx|keyframeRegionMoveKeyCtx|keyframeRegionScaleKeyCtx|keyframeRegionSelectKeyCtx|keyframeRegionSetKeyCtx|keyframeRegionTrackCtx|keyframeStats|lassoContext|lattice|latticeDeformKeyCtx|launch|launchImageEditor|layerButton|layeredShaderPort|layeredTexturePort|layout|layoutDialog|lightList|lightListEditor|lightListPanel|lightlink|lineIntersection|linearPrecision|linstep|listAnimatable|listAttr|listCameras|listConnections|listDeviceAttachments|listHistory|listInputDeviceAxes|listInputDeviceButtons|listInputDevices|listMenuAnnotation|listNodeTypes|listPanelCategories|listRelatives|listSets|listTransforms|listUnselected|listerEditor|loadFluid|loadNewShelf|loadPlugin|loadPluginLanguageResources|loadPrefObjects|localizedPanelLabel|lockNode|loft|log|longNameOf|lookThru|ls|lsThroughFilter|lsType|lsUI|Mayatomr|mag|makeIdentity|makeLive|makePaintable|makeRoll|makeSingleSurface|makeTubeOn|makebot|manipMoveContext|manipMoveLimitsCtx|manipOptions|manipRotateContext|manipRotateLimitsCtx|manipScaleContext|manipScaleLimitsCtx|marker|match|max|memory|menu|menuBarLayout|menuEditor|menuItem|menuItemToShelf|menuSet|menuSetPref|messageLine|min|minimizeApp|mirrorJoint|modelCurrentTimeCtx|modelEditor|modelPanel|mouse|movIn|movOut|move|moveIKtoFK|moveKeyCtx|moveVertexAlongDirection|multiProfileBirailSurface|mute|nParticle|nameCommand|nameField|namespace|namespaceInfo|newPanelItems|newton|nodeCast|nodeIconButton|nodeOutliner|nodePreset|nodeType|noise|nonLinear|normalConstraint|normalize|nurbsBoolean|nurbsCopyUVSet|nurbsCube|nurbsEditUV|nurbsPlane|nurbsSelect|nurbsSquare|nurbsToPoly|nurbsToPolygonsPref|nurbsToSubdiv|nurbsToSubdivPref|nurbsUVSet|nurbsViewDirectionVector|objExists|objectCenter|objectLayer|objectType|objectTypeUI|obsoleteProc|oceanNurbsPreviewPlane|offsetCurve|offsetCurveOnSurface|offsetSurface|openGLExtension|openMayaPref|optionMenu|optionMenuGrp|optionVar|orbit|orbitCtx|orientConstraint|outlinerEditor|outlinerPanel|overrideModifier|paintEffectsDisplay|pairBlend|palettePort|paneLayout|panel|panelConfiguration|panelHistory|paramDimContext|paramDimension|paramLocator|parent|parentConstraint|particle|particleExists|particleInstancer|particleRenderInfo|partition|pasteKey|pathAnimation|pause|pclose|percent|performanceOptions|pfxstrokes|pickWalk|picture|pixelMove|planarSrf|plane|play|playbackOptions|playblast|plugAttr|plugNode|pluginInfo|pluginResourceUtil|pointConstraint|pointCurveConstraint|pointLight|pointMatrixMult|pointOnCurve|pointOnSurface|pointPosition|poleVectorConstraint|polyAppend|polyAppendFacetCtx|polyAppendVertex|polyAutoProjection|polyAverageNormal|polyAverageVertex|polyBevel|polyBlendColor|polyBlindData|polyBoolOp|polyBridgeEdge|polyCacheMonitor|polyCheck|polyChipOff|polyClipboard|polyCloseBorder|polyCollapseEdge|polyCollapseFacet|polyColorBlindData|polyColorDel|polyColorPerVertex|polyColorSet|polyCompare|polyCone|polyCopyUV|polyCrease|polyCreaseCtx|polyCreateFacet|polyCreateFacetCtx|polyCube|polyCut|polyCutCtx|polyCylinder|polyCylindricalProjection|polyDelEdge|polyDelFacet|polyDelVertex|polyDuplicateAndConnect|polyDuplicateEdge|polyEditUV|polyEditUVShell|polyEvaluate|polyExtrudeEdge|polyExtrudeFacet|polyExtrudeVertex|polyFlipEdge|polyFlipUV|polyForceUV|polyGeoSampler|polyHelix|polyInfo|polyInstallAction|polyLayoutUV|polyListComponentConversion|polyMapCut|polyMapDel|polyMapSew|polyMapSewMove|polyMergeEdge|polyMergeEdgeCtx|polyMergeFacet|polyMergeFacetCtx|polyMergeUV|polyMergeVertex|polyMirrorFace|polyMoveEdge|polyMoveFacet|polyMoveFacetUV|polyMoveUV|polyMoveVertex|polyNormal|polyNormalPerVertex|polyNormalizeUV|polyOptUvs|polyOptions|polyOutput|polyPipe|polyPlanarProjection|polyPlane|polyPlatonicSolid|polyPoke|polyPrimitive|polyPrism|polyProjection|polyPyramid|polyQuad|polyQueryBlindData|polyReduce|polySelect|polySelectConstraint|polySelectConstraintMonitor|polySelectCtx|polySelectEditCtx|polySeparate|polySetToFaceNormal|polySewEdge|polyShortestPathCtx|polySmooth|polySoftEdge|polySphere|polySphericalProjection|polySplit|polySplitCtx|polySplitEdge|polySplitRing|polySplitVertex|polyStraightenUVBorder|polySubdivideEdge|polySubdivideFacet|polyToSubdiv|polyTorus|polyTransfer|polyTriangulate|polyUVSet|polyUnite|polyWedgeFace|popen|popupMenu|pose|pow|preloadRefEd|print|progressBar|progressWindow|projFileViewer|projectCurve|projectTangent|projectionContext|projectionManip|promptDialog|propModCtx|propMove|psdChannelOutliner|psdEditTextureFile|psdExport|psdTextureFile|putenv|pwd|python|querySubdiv|quit|rad_to_deg|radial|radioButton|radioButtonGrp|radioCollection|radioMenuItemCollection|rampColorPort|rand|randomizeFollicles|randstate|rangeControl|readTake|rebuildCurve|rebuildSurface|recordAttr|recordDevice|redo|reference|referenceEdit|referenceQuery|refineSubdivSelectionList|refresh|refreshAE|registerPluginResource|rehash|reloadImage|removeJoint|removeMultiInstance|removePanelCategory|rename|renameAttr|renameSelectionList|renameUI|render|renderGlobalsNode|renderInfo|renderLayerButton|renderLayerParent|renderLayerPostProcess|renderLayerUnparent|renderManip|renderPartition|renderQualityNode|renderSettings|renderThumbnailUpdate|renderWindowEditor|renderWindowSelectContext|renderer|reorder|reorderDeformers|requires|reroot|resampleFluid|resetAE|resetPfxToPolyCamera|resetTool|resolutionNode|retarget|reverseCurve|reverseSurface|revolve|rgb_to_hsv|rigidBody|rigidSolver|roll|rollCtx|rootOf|rot|rotate|rotationInterpolation|roundConstantRadius|rowColumnLayout|rowLayout|runTimeCommand|runup|sampleImage|saveAllShelves|saveAttrPreset|saveFluid|saveImage|saveInitialState|saveMenu|savePrefObjects|savePrefs|saveShelf|saveToolSettings|scale|scaleBrushBrightness|scaleComponents|scaleConstraint|scaleKey|scaleKeyCtx|sceneEditor|sceneUIReplacement|scmh|scriptCtx|scriptEditorInfo|scriptJob|scriptNode|scriptTable|scriptToShelf|scriptedPanel|scriptedPanelType|scrollField|scrollLayout|sculpt|searchPathArray|seed|selLoadSettings|select|selectContext|selectCurveCV|selectKey|selectKeyCtx|selectKeyframeRegionCtx|selectMode|selectPref|selectPriority|selectType|selectedNodes|selectionConnection|separator|setAttr|setAttrEnumResource|setAttrMapping|setAttrNiceNameResource|setConstraintRestPosition|setDefaultShadingGroup|setDrivenKeyframe|setDynamic|setEditCtx|setEditor|setFluidAttr|setFocus|setInfinity|setInputDeviceMapping|setKeyCtx|setKeyPath|setKeyframe|setKeyframeBlendshapeTargetWts|setMenuMode|setNodeNiceNameResource|setNodeTypeFlag|setParent|setParticleAttr|setPfxToPolyCamera|setPluginResource|setProject|setStampDensity|setStartupMessage|setState|setToolTo|setUITemplate|setXformManip|sets|shadingConnection|shadingGeometryRelCtx|shadingLightRelCtx|shadingNetworkCompare|shadingNode|shapeCompare|shelfButton|shelfLayout|shelfTabLayout|shellField|shortNameOf|showHelp|showHidden|showManipCtx|showSelectionInTitle|showShadingGroupAttrEditor|showWindow|sign|simplify|sin|singleProfileBirailSurface|size|sizeBytes|skinCluster|skinPercent|smoothCurve|smoothTangentSurface|smoothstep|snap2to2|snapKey|snapMode|snapTogetherCtx|snapshot|soft|softMod|softModCtx|sort|sound|soundControl|source|spaceLocator|sphere|sphrand|spotLight|spotLightPreviewPort|spreadSheetEditor|spring|sqrt|squareSurface|srtContext|stackTrace|startString|startsWith|stitchAndExplodeShell|stitchSurface|stitchSurfacePoints|strcmp|stringArrayCatenate|stringArrayContains|stringArrayCount|stringArrayInsertAtIndex|stringArrayIntersector|stringArrayRemove|stringArrayRemoveAtIndex|stringArrayRemoveDuplicates|stringArrayRemoveExact|stringArrayToString|stringToStringArray|strip|stripPrefixFromName|stroke|subdAutoProjection|subdCleanTopology|subdCollapse|subdDuplicateAndConnect|subdEditUV|subdListComponentConversion|subdMapCut|subdMapSewMove|subdMatchTopology|subdMirror|subdToBlind|subdToPoly|subdTransferUVsToCache|subdiv|subdivCrease|subdivDisplaySmoothness|substitute|substituteAllString|substituteGeometry|substring|surface|surfaceSampler|surfaceShaderList|swatchDisplayPort|switchTable|symbolButton|symbolCheckBox|sysFile|system|tabLayout|tan|tangentConstraint|texLatticeDeformContext|texManipContext|texMoveContext|texMoveUVShellContext|texRotateContext|texScaleContext|texSelectContext|texSelectShortestPathCtx|texSmudgeUVContext|texWinToolCtx|text|textCurves|textField|textFieldButtonGrp|textFieldGrp|textManip|textScrollList|textToShelf|textureDisplacePlane|textureHairColor|texturePlacementContext|textureWindow|threadCount|threePointArcCtx|timeControl|timePort|timerX|toNativePath|toggle|toggleAxis|toggleWindowVisibility|tokenize|tokenizeList|tolerance|tolower|toolButton|toolCollection|toolDropped|toolHasOptions|toolPropertyWindow|torus|toupper|trace|track|trackCtx|transferAttributes|transformCompare|transformLimits|translator|trim|trunc|truncateFluidCache|truncateHairCache|tumble|tumbleCtx|turbulence|twoPointArcCtx|uiRes|uiTemplate|unassignInputDevice|undo|undoInfo|ungroup|uniform|unit|unloadPlugin|untangleUV|untitledFileName|untrim|upAxis|updateAE|userCtx|uvLink|uvSnapshot|validateShelfName|vectorize|view2dToolCtx|viewCamera|viewClipPlane|viewFit|viewHeadOn|viewLookAt|viewManip|viewPlace|viewSet|visor|volumeAxis|vortex|waitCursor|warning|webBrowser|webBrowserPrefs|whatIs|window|windowPref|wire|wireContext|workspace|wrinkle|wrinkleContext|writeTake|xbmLangPathList|xform)\b/,operator:[/\+[+=]?|-[-=]?|&&|\|\||[<>]=|[*\/!=]=?|[%^]/,{pattern:/(^|[^<])<(?!<)/,lookbehind:!0},{pattern:/(^|[^>])>(?!>)/,lookbehind:!0}],punctuation:/<<|>>|[.,:;?\[\](){}]/},e.languages.mel.code.inside.rest=e.languages.mel}e.exports=t,t.displayName="mel",t.aliases=[]},23388(e){"use strict";function t(e){e.languages.mizar={comment:/::.+/,keyword:/@proof\b|\b(?:according|aggregate|all|and|antonym|are|as|associativity|assume|asymmetry|attr|be|begin|being|by|canceled|case|cases|clusters?|coherence|commutativity|compatibility|connectedness|consider|consistency|constructors|contradiction|correctness|def|deffunc|define|definitions?|defpred|do|does|equals|end|environ|ex|exactly|existence|for|from|func|given|hence|hereby|holds|idempotence|identity|iff?|implies|involutiveness|irreflexivity|is|it|let|means|mode|non|not|notations?|now|of|or|otherwise|over|per|pred|prefix|projectivity|proof|provided|qua|reconsider|redefine|reduce|reducibility|reflexivity|registrations?|requirements|reserve|sch|schemes?|section|selector|set|sethood|st|struct|such|suppose|symmetry|synonym|take|that|the|then|theorems?|thesis|thus|to|transitivity|uniqueness|vocabular(?:y|ies)|when|where|with|wrt)\b/,parameter:{pattern:/\$(?:10|\d)/,alias:"variable"},variable:/\b\w+(?=:)/,number:/(?:\b|-)\d+\b/,operator:/\.\.\.|->|&|\.?=/,punctuation:/\(#|#\)|[,:;\[\](){}]/}}e.exports=t,t.displayName="mizar",t.aliases=[]},90596(e){"use strict";function t(e){var t,n,r,i;t=e,r=["ObjectId","Code","BinData","DBRef","Timestamp","NumberLong","NumberDecimal","MaxKey","MinKey","RegExp","ISODate","UUID"],i="(?:"+(n=(n=["$eq","$gt","$gte","$in","$lt","$lte","$ne","$nin","$and","$not","$nor","$or","$exists","$type","$expr","$jsonSchema","$mod","$regex","$text","$where","$geoIntersects","$geoWithin","$near","$nearSphere","$all","$elemMatch","$size","$bitsAllClear","$bitsAllSet","$bitsAnyClear","$bitsAnySet","$comment","$elemMatch","$meta","$slice","$currentDate","$inc","$min","$max","$mul","$rename","$set","$setOnInsert","$unset","$addToSet","$pop","$pull","$push","$pullAll","$each","$position","$slice","$sort","$bit","$addFields","$bucket","$bucketAuto","$collStats","$count","$currentOp","$facet","$geoNear","$graphLookup","$group","$indexStats","$limit","$listLocalSessions","$listSessions","$lookup","$match","$merge","$out","$planCacheStats","$project","$redact","$replaceRoot","$replaceWith","$sample","$set","$skip","$sort","$sortByCount","$unionWith","$unset","$unwind","$abs","$accumulator","$acos","$acosh","$add","$addToSet","$allElementsTrue","$and","$anyElementTrue","$arrayElemAt","$arrayToObject","$asin","$asinh","$atan","$atan2","$atanh","$avg","$binarySize","$bsonSize","$ceil","$cmp","$concat","$concatArrays","$cond","$convert","$cos","$dateFromParts","$dateToParts","$dateFromString","$dateToString","$dayOfMonth","$dayOfWeek","$dayOfYear","$degreesToRadians","$divide","$eq","$exp","$filter","$first","$floor","$function","$gt","$gte","$hour","$ifNull","$in","$indexOfArray","$indexOfBytes","$indexOfCP","$isArray","$isNumber","$isoDayOfWeek","$isoWeek","$isoWeekYear","$last","$last","$let","$literal","$ln","$log","$log10","$lt","$lte","$ltrim","$map","$max","$mergeObjects","$meta","$min","$millisecond","$minute","$mod","$month","$multiply","$ne","$not","$objectToArray","$or","$pow","$push","$radiansToDegrees","$range","$reduce","$regexFind","$regexFindAll","$regexMatch","$replaceOne","$replaceAll","$reverseArray","$round","$rtrim","$second","$setDifference","$setEquals","$setIntersection","$setIsSubset","$setUnion","$size","$sin","$slice","$split","$sqrt","$stdDevPop","$stdDevSamp","$strcasecmp","$strLenBytes","$strLenCP","$substr","$substrBytes","$substrCP","$subtract","$sum","$switch","$tan","$toBool","$toDate","$toDecimal","$toDouble","$toInt","$toLong","$toObjectId","$toString","$toLower","$toUpper","$trim","$trunc","$type","$week","$year","$zip","$comment","$explain","$hint","$max","$maxTimeMS","$min","$orderby","$query","$returnKey","$showDiskLoc","$natural"]).map(function(e){return e.replace("$","\\$")})).join("|")+")\\b",t.languages.mongodb=t.languages.extend("javascript",{}),t.languages.insertBefore("mongodb","string",{property:{pattern:/(?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)(?=\s*:)/,greedy:!0,inside:{keyword:RegExp("^(['\"])?"+i+"(?:\\1)?$")}}}),t.languages.mongodb.string.inside={url:{pattern:/https?:\/\/[-\w@:%.+~#=]{1,256}\.[a-z0-9()]{1,6}\b[-\w()@:%+.~#?&/=]*/i,greedy:!0},entity:{pattern:/\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/,greedy:!0}},t.languages.insertBefore("mongodb","constant",{builtin:{pattern:RegExp("\\b(?:"+r.join("|")+")\\b"),alias:"keyword"}})}e.exports=t,t.displayName="mongodb",t.aliases=[]},95721(e){"use strict";function t(e){e.languages.monkey={string:/"[^"\r\n]*"/,comment:[{pattern:/^#Rem\s[\s\S]*?^#End/im,greedy:!0},{pattern:/'.+/,greedy:!0}],preprocessor:{pattern:/(^[ \t]*)#.+/m,lookbehind:!0,alias:"comment"},function:/\b\w+(?=\()/,"type-char":{pattern:/(\w)[?%#$]/,lookbehind:!0,alias:"variable"},number:{pattern:/((?:\.\.)?)(?:(?:\b|\B-\.?|\B\.)\d+(?:(?!\.\.)\.\d*)?|\$[\da-f]+)/i,lookbehind:!0},keyword:/\b(?:Void|Strict|Public|Private|Property|Bool|Int|Float|String|Array|Object|Continue|Exit|Import|Extern|New|Self|Super|Try|Catch|Eachin|True|False|Extends|Abstract|Final|Select|Case|Default|Const|Local|Global|Field|Method|Function|Class|End|If|Then|Else|ElseIf|EndIf|While|Wend|Repeat|Until|Forever|For|To|Step|Next|Return|Module|Interface|Implements|Inline|Throw|Null)\b/i,operator:/\.\.|<[=>]?|>=?|:?=|(?:[+\-*\/&~|]|\b(?:Mod|Shl|Shr)\b)=?|\b(?:And|Not|Or)\b/i,punctuation:/[.,:;()\[\]]/}}e.exports=t,t.displayName="monkey",t.aliases=[]},64262(e){"use strict";function t(e){e.languages.moonscript={comment:/--.*/,string:[{pattern:/'[^']*'|\[(=*)\[[\s\S]*?\]\1\]/,greedy:!0},{pattern:/"[^"]*"/,greedy:!0,inside:{interpolation:{pattern:/#\{[^{}]*\}/,inside:{moonscript:{pattern:/(^#\{)[\s\S]+(?=\})/,lookbehind:!0,inside:null},"interpolation-punctuation":{pattern:/#\{|\}/,alias:"punctuation"}}}}}],"class-name":[{pattern:/(\b(?:class|extends)[ \t]+)\w+/,lookbehind:!0},/\b[A-Z]\w*/],keyword:/\b(?:class|continue|do|else|elseif|export|extends|for|from|if|import|in|local|nil|return|self|super|switch|then|unless|using|when|while|with)\b/,variable:/@@?\w*/,property:{pattern:/\b(?!\d)\w+(?=:)|(:)(?!\d)\w+/,lookbehind:!0},function:{pattern:/\b(?:_G|_VERSION|assert|collectgarbage|coroutine\.(?:running|create|resume|status|wrap|yield)|debug\.(?:debug|gethook|getinfo|getlocal|getupvalue|setlocal|setupvalue|sethook|traceback|getfenv|getmetatable|getregistry|setfenv|setmetatable)|dofile|error|getfenv|getmetatable|io\.(?:stdin|stdout|stderr|close|flush|input|lines|open|output|popen|read|tmpfile|type|write)|ipairs|load|loadfile|loadstring|math\.(?:abs|acos|asin|atan|atan2|ceil|sin|cos|tan|deg|exp|floor|log|log10|max|min|fmod|modf|cosh|sinh|tanh|pow|rad|sqrt|frexp|ldexp|random|randomseed|pi)|module|next|os\.(?:clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname)|package\.(?:cpath|loaded|loadlib|path|preload|seeall)|pairs|pcall|print|rawequal|rawget|rawset|require|select|setfenv|setmetatable|string\.(?:byte|char|dump|find|len|lower|rep|sub|upper|format|gsub|gmatch|match|reverse)|table\.(?:maxn|concat|sort|insert|remove)|tonumber|tostring|type|unpack|xpcall)\b/,inside:{punctuation:/\./}},boolean:/\b(?:false|true)\b/,number:/(?:\B\.\d+|\b\d+\.\d+|\b\d+(?=[eE]))(?:[eE][-+]?\d+)?\b|\b(?:0x[a-fA-F\d]+|\d+)(?:U?LL)?\b/,operator:/\.{3}|[-=]>|~=|(?:[-+*/%<>!=]|\.\.)=?|[:#^]|\b(?:and|or)\b=?|\b(?:not)\b/,punctuation:/[.,()[\]{}\\]/},e.languages.moonscript.string[1].inside.interpolation.inside.moonscript.inside=e.languages.moonscript,e.languages.moon=e.languages.moonscript}e.exports=t,t.displayName="moonscript",t.aliases=["moon"]},18190(e){"use strict";function t(e){e.languages.n1ql={comment:/\/\*[\s\S]*?(?:$|\*\/)/,parameter:/\$[\w.]+/,string:{pattern:/(["'])(?:\\[\s\S]|(?!\1)[^\\]|\1\1)*\1/,greedy:!0},identifier:{pattern:/`(?:\\[\s\S]|[^\\`]|``)*`/,greedy:!0},function:/\b(?:ABS|ACOS|ARRAY_AGG|ARRAY_APPEND|ARRAY_AVG|ARRAY_CONCAT|ARRAY_CONTAINS|ARRAY_COUNT|ARRAY_DISTINCT|ARRAY_FLATTEN|ARRAY_IFNULL|ARRAY_INSERT|ARRAY_INTERSECT|ARRAY_LENGTH|ARRAY_MAX|ARRAY_MIN|ARRAY_POSITION|ARRAY_PREPEND|ARRAY_PUT|ARRAY_RANGE|ARRAY_REMOVE|ARRAY_REPEAT|ARRAY_REPLACE|ARRAY_REVERSE|ARRAY_SORT|ARRAY_STAR|ARRAY_SUM|ARRAY_SYMDIFF|ARRAY_SYMDIFFN|ARRAY_UNION|ASIN|ATAN|ATAN2|AVG|BASE64|BASE64_DECODE|BASE64_ENCODE|BITAND|BITCLEAR|BITNOT|BITOR|BITSET|BITSHIFT|BITTEST|BITXOR|CEIL|CLOCK_LOCAL|CLOCK_MILLIS|CLOCK_STR|CLOCK_TZ|CLOCK_UTC|CONTAINS|CONTAINS_TOKEN|CONTAINS_TOKEN_LIKE|CONTAINS_TOKEN_REGEXP|COS|COUNT|CURL|DATE_ADD_MILLIS|DATE_ADD_STR|DATE_DIFF_MILLIS|DATE_DIFF_STR|DATE_FORMAT_STR|DATE_PART_MILLIS|DATE_PART_STR|DATE_RANGE_MILLIS|DATE_RANGE_STR|DATE_TRUNC_MILLIS|DATE_TRUNC_STR|DECODE_JSON|DEGREES|DURATION_TO_STR|E|ENCODED_SIZE|ENCODE_JSON|EXP|FLOOR|GREATEST|HAS_TOKEN|IFINF|IFMISSING|IFMISSINGORNULL|IFNAN|IFNANORINF|IFNULL|INITCAP|ISARRAY|ISATOM|ISBOOLEAN|ISNUMBER|ISOBJECT|ISSTRING|IsBitSET|LEAST|LENGTH|LN|LOG|LOWER|LTRIM|MAX|META|MILLIS|MILLIS_TO_LOCAL|MILLIS_TO_STR|MILLIS_TO_TZ|MILLIS_TO_UTC|MILLIS_TO_ZONE_NAME|MIN|MISSINGIF|NANIF|NEGINFIF|NOW_LOCAL|NOW_MILLIS|NOW_STR|NOW_TZ|NOW_UTC|NULLIF|OBJECT_ADD|OBJECT_CONCAT|OBJECT_INNER_PAIRS|OBJECT_INNER_VALUES|OBJECT_LENGTH|OBJECT_NAMES|OBJECT_PAIRS|OBJECT_PUT|OBJECT_REMOVE|OBJECT_RENAME|OBJECT_REPLACE|OBJECT_UNWRAP|OBJECT_VALUES|PAIRS|PI|POLY_LENGTH|POSINFIF|POSITION|POWER|RADIANS|RANDOM|REGEXP_CONTAINS|REGEXP_LIKE|REGEXP_POSITION|REGEXP_REPLACE|REPEAT|REPLACE|REVERSE|ROUND|RTRIM|SIGN|SIN|SPLIT|SQRT|STR_TO_DURATION|STR_TO_MILLIS|STR_TO_TZ|STR_TO_UTC|STR_TO_ZONE_NAME|SUBSTR|SUFFIXES|SUM|TAN|TITLE|TOARRAY|TOATOM|TOBOOLEAN|TOKENS|TONUMBER|TOOBJECT|TOSTRING|TRIM|TRUNC|TYPE|UPPER|WEEKDAY_MILLIS|WEEKDAY_STR)(?=\s*\()/i,keyword:/\b(?:ALL|ALTER|ANALYZE|AS|ASC|BEGIN|BINARY|BOOLEAN|BREAK|BUCKET|BUILD|BY|CALL|CAST|CLUSTER|COLLATE|COLLECTION|COMMIT|CONNECT|CONTINUE|CORRELATE|COVER|CREATE|DATABASE|DATASET|DATASTORE|DECLARE|DECREMENT|DELETE|DERIVED|DESC|DESCRIBE|DISTINCT|DO|DROP|EACH|ELEMENT|EXCEPT|EXCLUDE|EXECUTE|EXPLAIN|FETCH|FLATTEN|FOR|FORCE|FROM|FUNCTION|GRANT|GROUP|GSI|HAVING|IF|IGNORE|ILIKE|INCLUDE|INCREMENT|INDEX|INFER|INLINE|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KEYS|KEYSPACE|KNOWN|LAST|LEFT|LET|LETTING|LIMIT|LSM|MAP|MAPPING|MATCHED|MATERIALIZED|MERGE|MINUS|MISSING|NAMESPACE|NEST|NULL|NUMBER|OBJECT|OFFSET|ON|OPTION|ORDER|OUTER|OVER|PARSE|PARTITION|PASSWORD|PATH|POOL|PREPARE|PRIMARY|PRIVATE|PRIVILEGE|PROCEDURE|PUBLIC|RAW|REALM|REDUCE|RENAME|RETURN|RETURNING|REVOKE|RIGHT|ROLE|ROLLBACK|SATISFIES|SCHEMA|SELECT|SELF|SEMI|SET|SHOW|SOME|START|STATISTICS|STRING|SYSTEM|TO|TRANSACTION|TRIGGER|TRUNCATE|UNDER|UNION|UNIQUE|UNKNOWN|UNNEST|UNSET|UPDATE|UPSERT|USE|USER|USING|VALIDATE|VALUE|VALUES|VIA|VIEW|WHERE|WHILE|WITH|WORK|XOR)\b/i,boolean:/\b(?:TRUE|FALSE)\b/i,number:/(?:\b\d+\.|\B\.)\d+e[+\-]?\d+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/%]|!=|==?|\|\||<[>=]?|>=?|\b(?:AND|ANY|ARRAY|BETWEEN|CASE|ELSE|END|EVERY|EXISTS|FIRST|IN|LIKE|NOT|OR|THEN|VALUED|WHEN|WITHIN)\b/i,punctuation:/[;[\](),.{}:]/}}e.exports=t,t.displayName="n1ql",t.aliases=[]},70896(e){"use strict";function t(e){e.languages.n4js=e.languages.extend("javascript",{keyword:/\b(?:any|Array|boolean|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|module|new|null|number|package|private|protected|public|return|set|static|string|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/}),e.languages.insertBefore("n4js","constant",{annotation:{pattern:/@+\w+/,alias:"operator"}}),e.languages.n4jsd=e.languages.n4js}e.exports=t,t.displayName="n4js",t.aliases=["n4jsd"]},42242(e){"use strict";function t(e){e.languages["nand2tetris-hdl"]={comment:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,keyword:/\b(?:CHIP|IN|OUT|PARTS|BUILTIN|CLOCKED)\b/,boolean:/\b(?:true|false)\b/,function:/\b[A-Za-z][A-Za-z0-9]*(?=\()/,number:/\b\d+\b/,operator:/=|\.\./,punctuation:/[{}[\];(),:]/}}e.exports=t,t.displayName="nand2tetrisHdl",t.aliases=[]},37943(e){"use strict";function t(e){!function(e){var t=/\{[^\r\n\[\]{}]*\}/,n={"quoted-string":{pattern:/"(?:[^"\\]|\\.)*"/,alias:"operator"},"command-param-id":{pattern:/(\s)\w+:/,lookbehind:!0,alias:"property"},"command-param-value":[{pattern:t,alias:"selector"},{pattern:/([\t ])\S+/,lookbehind:!0,greedy:!0,alias:"operator"},{pattern:/\S(?:.*\S)?/,alias:"operator"}]};function r(e){for(var t="[]{}",n=[],r=0;r.+/m,alias:"tag",inside:{value:{pattern:/(^>\w+[\t ]+)(?!\s)[^{}\r\n]+/,lookbehind:!0,alias:"operator"},key:{pattern:/(^>)\w+/,lookbehind:!0}}},label:{pattern:/^([\t ]*)#[\t ]*\w+[\t ]*$/m,lookbehind:!0,alias:"regex"},command:{pattern:/^([\t ]*)@\w+(?=[\t ]|$).*/m,lookbehind:!0,alias:"function",inside:{"command-name":/^@\w+/,expression:{pattern:t,greedy:!0,alias:"selector"},"command-params":{pattern:/\s*\S[\s\S]*/,inside:n}}},"generic-text":{pattern:/(^[ \t]*)[^#@>;\s].*/m,lookbehind:!0,alias:"punctuation",inside:{"escaped-char":/\\[{}\[\]"]/,expression:{pattern:t,greedy:!0,alias:"selector"},"inline-command":{pattern:/\[[\t ]*\w[^\r\n\[\]]*\]/,greedy:!0,alias:"function",inside:{"command-params":{pattern:/(^\[[\t ]*\w+\b)[\s\S]+(?=\]$)/,lookbehind:!0,inside:n},"command-param-name":{pattern:/^(\[[\t ]*)\w+/,lookbehind:!0,alias:"name"},"start-stop-char":/[\[\]]/}}}}},e.languages.nani=e.languages.naniscript,e.hooks.add("after-tokenize",function(e){e.tokens.forEach(function(e){if("string"!=typeof e&&"generic-text"===e.type){var t=i(e);r(t)||(e.type="bad-line",e.content=t)}})})}(e)}e.exports=t,t.displayName="naniscript",t.aliases=[]},293(e){"use strict";function t(e){e.languages.nasm={comment:/;.*$/m,string:/(["'`])(?:\\.|(?!\1)[^\\\r\n])*\1/,label:{pattern:/(^\s*)[A-Za-z._?$][\w.?$@~#]*:/m,lookbehind:!0,alias:"function"},keyword:[/\[?BITS (?:16|32|64)\]?/,{pattern:/(^\s*)section\s*[a-z.]+:?/im,lookbehind:!0},/(?:extern|global)[^;\r\n]*/i,/(?:CPU|FLOAT|DEFAULT).*$/m],register:{pattern:/\b(?:st\d|[xyz]mm\d\d?|[cdt]r\d|r\d\d?[bwd]?|[er]?[abcd]x|[abcd][hl]|[er]?(?:bp|sp|si|di)|[cdefgs]s)\b/i,alias:"variable"},number:/(?:\b|(?=\$))(?:0[hx](?:\.[\da-f]+|[\da-f]+(?:\.[\da-f]+)?)(?:p[+-]?\d+)?|\d[\da-f]+[hx]|\$\d[\da-f]*|0[oq][0-7]+|[0-7]+[oq]|0[by][01]+|[01]+[by]|0[dt]\d+|(?:\d+(?:\.\d+)?|\.\d+)(?:\.?e[+-]?\d+)?[dt]?)\b/i,operator:/[\[\]*+\-\/%<>=&|$!]/}}e.exports=t,t.displayName="nasm",t.aliases=[]},83873(e){"use strict";function t(e){e.languages.neon={comment:{pattern:/#.*/,greedy:!0},datetime:{pattern:/(^|[[{(=:,\s])\d\d\d\d-\d\d?-\d\d?(?:(?:[Tt]| +)\d\d?:\d\d:\d\d(?:\.\d*)? *(?:Z|[-+]\d\d?(?::?\d\d)?)?)?(?=$|[\]}),\s])/,lookbehind:!0,alias:"number"},key:{pattern:/(^|[[{(,\s])[^,:=[\]{}()'"\s]+(?=\s*:(?:$|[\]}),\s])|\s*=)/,lookbehind:!0,alias:"atrule"},number:{pattern:/(^|[[{(=:,\s])[+-]?(?:0x[\da-fA-F]+|0o[0-7]+|0b[01]+|(?:\d+(?:\.\d*)?|\.?\d+)(?:[eE][+-]?\d+)?)(?=$|[\]}),:=\s])/,lookbehind:!0},boolean:{pattern:/(^|[[{(=:,\s])(?:true|false|yes|no)(?=$|[\]}),:=\s])/i,lookbehind:!0},null:{pattern:/(^|[[{(=:,\s])(?:null)(?=$|[\]}),:=\s])/i,lookbehind:!0,alias:"keyword"},string:{pattern:/(^|[[{(=:,\s])(?:('''|""")\r?\n(?:(?:[^\r\n]|\r?\n(?![\t ]*\2))*\r?\n)?[\t ]*\2|'[^'\r\n]*'|"(?:\\.|[^\\"\r\n])*")/,lookbehind:!0,greedy:!0},literal:{pattern:/(^|[[{(=:,\s])(?:[^#"',:=[\]{}()\s`-]|[:-][^"',=[\]{}()\s])(?:[^,:=\]})(\s]|:(?![\s,\]})]|$)|[ \t]+[^#,:=\]})(\s])*/,lookbehind:!0,alias:"string"},punctuation:/[,:=[\]{}()-]/}}e.exports=t,t.displayName="neon",t.aliases=[]},75932(e){"use strict";function t(e){e.languages.nevod={comment:/\/\/.*|(?:\/\*[\s\S]*?(?:\*\/|$))/,string:{pattern:/(?:"(?:""|[^"])*"(?!")|'(?:''|[^'])*'(?!'))!?\*?/,greedy:!0,inside:{"string-attrs":/!$|!\*$|\*$/}},namespace:{pattern:/(@namespace\s+)[a-zA-Z0-9\-.]+(?=\s*\{)/,lookbehind:!0},pattern:{pattern:/(@pattern\s+)?#?[a-zA-Z0-9\-.]+(?:\s*\(\s*(?:~\s*)?[a-zA-Z0-9\-.]+\s*(?:,\s*(?:~\s*)?[a-zA-Z0-9\-.]*)*\))?(?=\s*=)/,lookbehind:!0,inside:{"pattern-name":{pattern:/^#?[a-zA-Z0-9\-.]+/,alias:"class-name"},fields:{pattern:/\(.*\)/,inside:{"field-name":{pattern:/[a-zA-Z0-9\-.]+/,alias:"variable"},punctuation:/[,()]/,operator:{pattern:/~/,alias:"field-hidden-mark"}}}}},search:{pattern:/(@search\s+|#)[a-zA-Z0-9\-.]+(?:\.\*)?(?=\s*;)/,alias:"function",lookbehind:!0},keyword:/@(?:require|namespace|pattern|search|inside|outside|having|where)\b/,"standard-pattern":{pattern:/\b(?:Word|Punct|Symbol|Space|LineBreak|Start|End|Alpha|AlphaNum|Num|NumAlpha|Blank|WordBreak|Any)(?:\([a-zA-Z0-9\-.,\s+]*\))?/,inside:{"standard-pattern-name":{pattern:/^[a-zA-Z0-9\-.]+/,alias:"builtin"},quantifier:{pattern:/\b\d+(?:\s*\+|\s*-\s*\d+)?(?!\w)/,alias:"number"},"standard-pattern-attr":{pattern:/[a-zA-Z0-9\-.]+/,alias:"builtin"},punctuation:/[,()]/}},quantifier:{pattern:/\b\d+(?:\s*\+|\s*-\s*\d+)?(?!\w)/,alias:"number"},operator:[{pattern:/=/,alias:"pattern-def"},{pattern:/&/,alias:"conjunction"},{pattern:/~/,alias:"exception"},{pattern:/\?/,alias:"optionality"},{pattern:/[[\]]/,alias:"repetition"},{pattern:/[{}]/,alias:"variation"},{pattern:/[+_]/,alias:"sequence"},{pattern:/\.{2,3}/,alias:"span"}],"field-capture":[{pattern:/([a-zA-Z0-9\-.]+\s*\()\s*[a-zA-Z0-9\-.]+\s*:\s*[a-zA-Z0-9\-.]+(?:\s*,\s*[a-zA-Z0-9\-.]+\s*:\s*[a-zA-Z0-9\-.]+)*(?=\s*\))/,lookbehind:!0,inside:{"field-name":{pattern:/[a-zA-Z0-9\-.]+/,alias:"variable"},colon:/:/}},{pattern:/[a-zA-Z0-9\-.]+\s*:/,inside:{"field-name":{pattern:/[a-zA-Z0-9\-.]+/,alias:"variable"},colon:/:/}}],punctuation:/[:;,()]/,name:/[a-zA-Z0-9\-.]+/}}e.exports=t,t.displayName="nevod",t.aliases=[]},60221(e){"use strict";function t(e){var t,n;n=/\$(?:\w[a-z\d]*(?:_[^\x00-\x1F\s"'\\()$]*)?|\{[^}\s"'\\]+\})/i,(t=e).languages.nginx={comment:{pattern:/(^|[\s{};])#.*/,lookbehind:!0},directive:{pattern:/(^|\s)\w(?:[^;{}"'\\\s]|\\.|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\s+(?:#.*(?!.)|(?![#\s])))*?(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:{string:{pattern:/((?:^|[^\\])(?:\\\\)*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,lookbehind:!0,inside:{escape:{pattern:/\\["'\\nrt]/,alias:"entity"},variable:n}},comment:{pattern:/(\s)#.*/,lookbehind:!0,greedy:!0},keyword:{pattern:/^\S+/,greedy:!0},boolean:{pattern:/(\s)(?:off|on)(?!\S)/,lookbehind:!0},number:{pattern:/(\s)\d+[a-z]*(?!\S)/i,lookbehind:!0},variable:n}},punctuation:/[{};]/}}e.exports=t,t.displayName="nginx",t.aliases=[]},44188(e){"use strict";function t(e){e.languages.nim={comment:/#.*/,string:{pattern:/(?:(?:\b(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+)?(?:"""[\s\S]*?"""(?!")|"(?:\\[\s\S]|""|[^"\\])*")|'(?:\\(?:\d+|x[\da-fA-F]{2}|.)|[^'])')/,greedy:!0},number:/\b(?:0[xXoObB][\da-fA-F_]+|\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:[eE][+-]?\d[\d_]*)?)(?:'?[iuf]\d*)?/,keyword:/\b(?:addr|as|asm|atomic|bind|block|break|case|cast|concept|const|continue|converter|defer|discard|distinct|do|elif|else|end|enum|except|export|finally|for|from|func|generic|if|import|include|interface|iterator|let|macro|method|mixin|nil|object|out|proc|ptr|raise|ref|return|static|template|try|tuple|type|using|var|when|while|with|without|yield)\b/,function:{pattern:/(?:(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+|`[^`\r\n]+`)\*?(?:\[[^\]]+\])?(?=\s*\()/,inside:{operator:/\*$/}},ignore:{pattern:/`[^`\r\n]+`/,inside:{punctuation:/`/}},operator:{pattern:/(^|[({\[](?=\.\.)|(?![({\[]\.).)(?:(?:[=+\-*\/<>@$~&%|!?^:\\]|\.\.|\.(?![)}\]]))+|\b(?:and|div|of|or|in|is|isnot|mod|not|notin|shl|shr|xor)\b)/m,lookbehind:!0},punctuation:/[({\[]\.|\.[)}\]]|[`(){}\[\],:]/}}e.exports=t,t.displayName="nim",t.aliases=[]},74426(e){"use strict";function t(e){e.languages.nix={comment:/\/\*[\s\S]*?\*\/|#.*/,string:{pattern:/"(?:[^"\\]|\\[\s\S])*"|''(?:(?!'')[\s\S]|''(?:'|\\|\$\{))*''/,greedy:!0,inside:{interpolation:{pattern:/(^|(?:^|(?!'').)[^\\])\$\{(?:[^{}]|\{[^}]*\})*\}/,lookbehind:!0,inside:{antiquotation:{pattern:/^\$(?=\{)/,alias:"variable"}}}}},url:[/\b(?:[a-z]{3,7}:\/\/)[\w\-+%~\/.:#=?&]+/,{pattern:/([^\/])(?:[\w\-+%~.:#=?&]*(?!\/\/)[\w\-+%~\/.:#=?&])?(?!\/\/)\/[\w\-+%~\/.:#=?&]*/,lookbehind:!0}],antiquotation:{pattern:/\$(?=\{)/,alias:"variable"},number:/\b\d+\b/,keyword:/\b(?:assert|builtins|else|if|in|inherit|let|null|or|then|with)\b/,function:/\b(?:abort|add|all|any|attrNames|attrValues|baseNameOf|compareVersions|concatLists|currentSystem|deepSeq|derivation|dirOf|div|elem(?:At)?|fetch(?:url|Tarball)|filter(?:Source)?|fromJSON|genList|getAttr|getEnv|hasAttr|hashString|head|import|intersectAttrs|is(?:Attrs|Bool|Function|Int|List|Null|String)|length|lessThan|listToAttrs|map|mul|parseDrvName|pathExists|read(?:Dir|File)|removeAttrs|replaceStrings|seq|sort|stringLength|sub(?:string)?|tail|throw|to(?:File|JSON|Path|String|XML)|trace|typeOf)\b|\bfoldl'\B/,boolean:/\b(?:true|false)\b/,operator:/[=!<>]=?|\+\+?|\|\||&&|\/\/|->?|[?@]/,punctuation:/[{}()[\].,:;]/},e.languages.nix.string.inside.interpolation.inside.rest=e.languages.nix}e.exports=t,t.displayName="nix",t.aliases=[]},88447(e){"use strict";function t(e){e.languages.nsis={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|[#;].*)/,lookbehind:!0},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},keyword:{pattern:/(^[\t ]*)(?:Abort|Add(?:BrandingImage|Size)|AdvSplash|Allow(?:RootDirInstall|SkipFiles)|AutoCloseWindow|Banner|BG(?:Font|Gradient|Image)|BrandingText|BringToFront|Call(?:InstDLL)?|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|Create(?:Directory|Font|ShortCut)|Delete(?:INISec|INIStr|RegKey|RegValue)?|Detail(?:Print|sButtonText)|Dialer|Dir(?:Text|Var|Verify)|EnableWindow|Enum(?:RegKey|RegValue)|Exch|Exec(?:Shell(?:Wait)?|Wait)?|ExpandEnvStrings|File(?:BufSize|Close|ErrorText|Open|Read|ReadByte|ReadUTF16LE|ReadWord|WriteUTF16LE|Seek|Write|WriteByte|WriteWord)?|Find(?:Close|First|Next|Window)|FlushINI|Get(?:CurInstType|CurrentAddress|DlgItem|DLLVersion(?:Local)?|ErrorLevel|FileTime(?:Local)?|FullPathName|Function(?:Address|End)?|InstDirError|LabelAddress|TempFileName)|Goto|HideWindow|Icon|If(?:Abort|Errors|FileExists|RebootFlag|Silent)|InitPluginsDir|Install(?:ButtonText|Colors|Dir(?:RegKey)?)|InstProgressFlags|Inst(?:Type(?:GetText|SetText)?)|Int(?:64|Ptr)?CmpU?|Int(?:64)?Fmt|Int(?:Ptr)?Op|IsWindow|Lang(?:DLL|String)|License(?:BkColor|Data|ForceSelection|LangString|Text)|LoadLanguageFile|LockWindow|Log(?:Set|Text)|Manifest(?:DPIAware|SupportedOS)|Math|MessageBox|MiscButtonText|Name|Nop|ns(?:Dialogs|Exec)|NSISdl|OutFile|Page(?:Callbacks)?|PE(?:DllCharacteristics|SubsysVer)|Pop|Push|Quit|Read(?:EnvStr|INIStr|RegDWORD|RegStr)|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|Section(?:End|GetFlags|GetInstTypes|GetSize|GetText|Group|In|SetFlags|SetInstTypes|SetSize|SetText)?|SendMessage|Set(?:AutoClose|BrandingImage|Compress|Compressor(?:DictSize)?|CtlColors|CurInstType|DatablockOptimize|DateSave|Details(?:Print|View)|ErrorLevel|Errors|FileAttributes|Font|OutPath|Overwrite|PluginUnload|RebootFlag|RegView|ShellVarContext|Silent)|Show(?:InstDetails|UninstDetails|Window)|Silent(?:Install|UnInstall)|Sleep|SpaceTexts|Splash|StartMenu|Str(?:CmpS?|Cpy|Len)|SubCaption|System|Unicode|Uninstall(?:ButtonText|Caption|Icon|SubCaption|Text)|UninstPage|UnRegDLL|UserInfo|Var|VI(?:AddVersionKey|FileVersion|ProductVersion)|VPatch|WindowIcon|Write(?:INIStr|Reg(?:Bin|DWORD|ExpandStr|MultiStr|None|Str)|Uninstaller)|XPStyle)\b/m,lookbehind:!0},property:/\b(?:admin|all|auto|both|colored|false|force|hide|highest|lastused|leave|listonly|none|normal|notset|off|on|open|print|show|silent|silentlog|smooth|textonly|true|user|ARCHIVE|FILE_(?:ATTRIBUTE_ARCHIVE|ATTRIBUTE_NORMAL|ATTRIBUTE_OFFLINE|ATTRIBUTE_READONLY|ATTRIBUTE_SYSTEM|ATTRIBUTE_TEMPORARY)|HK(?:(?:CR|CU|LM)(?:32|64)?|DD|PD|U)|HKEY_(?:CLASSES_ROOT|CURRENT_CONFIG|CURRENT_USER|DYN_DATA|LOCAL_MACHINE|PERFORMANCE_DATA|USERS)|ID(?:ABORT|CANCEL|IGNORE|NO|OK|RETRY|YES)|MB_(?:ABORTRETRYIGNORE|DEFBUTTON1|DEFBUTTON2|DEFBUTTON3|DEFBUTTON4|ICONEXCLAMATION|ICONINFORMATION|ICONQUESTION|ICONSTOP|OK|OKCANCEL|RETRYCANCEL|RIGHT|RTLREADING|SETFOREGROUND|TOPMOST|USERICON|YESNO)|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SYSTEM|TEMPORARY)\b/,constant:/\$\{[\w\.:\^-]+\}|\$\([\w\.:\^-]+\)/i,variable:/\$\w+/i,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--?|\+\+?|<=?|>=?|==?=?|&&?|\|\|?|[?*\/~^%]/,punctuation:/[{}[\];(),.:]/,important:{pattern:/(^[\t ]*)!(?:addincludedir|addplugindir|appendfile|cd|define|delfile|echo|else|endif|error|execute|finalize|getdllversion|gettlbversion|ifdef|ifmacrodef|ifmacrondef|ifndef|if|include|insertmacro|macroend|macro|makensis|packhdr|pragma|searchparse|searchreplace|system|tempfile|undef|verbose|warning)\b/im,lookbehind:!0}}}e.exports=t,t.displayName="nsis",t.aliases=[]},16032(e,t,n){"use strict";var r=n(65806);function i(e){e.register(r),e.languages.objectivec=e.languages.extend("c",{string:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|@"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,keyword:/\b(?:asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while|in|self|super)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete e.languages.objectivec["class-name"],e.languages.objc=e.languages.objectivec}e.exports=i,i.displayName="objectivec",i.aliases=["objc"]},33607(e){"use strict";function t(e){e.languages.ocaml={comment:/\(\*[\s\S]*?\*\)/,string:[{pattern:/"(?:\\.|[^\\\r\n"])*"/,greedy:!0},{pattern:/(['`])(?:\\(?:\d+|x[\da-f]+|.)|(?!\1)[^\\\r\n])\1/i,greedy:!0}],number:/\b(?:0x[\da-f][\da-f_]+|(?:0[bo])?\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?[\d_]+)?)/i,directive:{pattern:/\B#\w+/,alias:"important"},label:{pattern:/\B~\w+/,alias:"function"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"variable"},module:{pattern:/\b[A-Z]\w+/,alias:"variable"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,operator:/:=|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/[(){}\[\]|.,:;]|\b_\b/}}e.exports=t,t.displayName="ocaml",t.aliases=[]},22001(e,t,n){"use strict";var r=n(65806);function i(e){var t,n;e.register(r),(t=e).languages.opencl=t.languages.extend("c",{keyword:/\b(?:__attribute__|(?:__)?(?:constant|global|kernel|local|private|read_only|read_write|write_only)|auto|break|case|complex|const|continue|default|do|(?:float|double)(?:16(?:x(?:1|16|2|4|8))?|1x(?:1|16|2|4|8)|2(?:x(?:1|16|2|4|8))?|3|4(?:x(?:1|16|2|4|8))?|8(?:x(?:1|16|2|4|8))?)?|else|enum|extern|for|goto|(?:u?(?:char|short|int|long)|half|quad|bool)(?:2|3|4|8|16)?|if|imaginary|inline|packed|pipe|register|restrict|return|signed|sizeof|static|struct|switch|typedef|uniform|union|unsigned|void|volatile|while)\b/,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[fuhl]{0,4}/i,boolean:/\b(?:false|true)\b/,"constant-opencl-kernel":{pattern:/\b(?:CHAR_(?:BIT|MAX|MIN)|CLK_(?:ADDRESS_(?:CLAMP(?:_TO_EDGE)?|NONE|REPEAT)|FILTER_(?:LINEAR|NEAREST)|(?:LOCAL|GLOBAL)_MEM_FENCE|NORMALIZED_COORDS_(?:FALSE|TRUE))|CL_(?:BGRA|(?:HALF_)?FLOAT|INTENSITY|LUMINANCE|A?R?G?B?[Ax]?|(?:(?:UN)?SIGNED|[US]NORM)_(?:INT(?:8|16|32))|UNORM_(?:INT_101010|SHORT_(?:555|565)))|(?:DBL|FLT|HALF)_(?:DIG|EPSILON|MANT_DIG|(?:MIN|MAX)(?:(?:_10)?_EXP)?)|FLT_RADIX|HUGE_VALF?|INFINITY|(?:INT|LONG|SCHAR|SHRT)_(?:MAX|MIN)|(?:UCHAR|USHRT|UINT|ULONG)_MAX|MAXFLOAT|M_(?:[12]_PI|2_SQRTPI|E|LN(?:2|10)|LOG(?:10|2)E?|PI(?:_[24])?|SQRT(?:1_2|2))(?:_F|_H)?|NAN)\b/,alias:"constant"}}),t.languages.insertBefore("opencl","class-name",{"builtin-type":{pattern:/\b(?:_cl_(?:command_queue|context|device_id|event|kernel|mem|platform_id|program|sampler)|cl_(?:image_format|mem_fence_flags)|clk_event_t|event_t|image(?:1d_(?:array_|buffer_)?t|2d_(?:array_(?:depth_|msaa_depth_|msaa_)?|depth_|msaa_depth_|msaa_)?t|3d_t)|intptr_t|ndrange_t|ptrdiff_t|queue_t|reserve_id_t|sampler_t|size_t|uintptr_t)\b/,alias:"keyword"}}),n={"type-opencl-host":{pattern:/\b(?:cl_(?:GLenum|GLint|GLuin|addressing_mode|bitfield|bool|buffer_create_type|build_status|channel_(?:order|type)|(?:u?(?:char|short|int|long)|float|double)(?:2|3|4|8|16)?|command_(?:queue(?:_info|_properties)?|type)|context(?:_info|_properties)?|device_(?:exec_capabilities|fp_config|id|info|local_mem_type|mem_cache_type|type)|(?:event|sampler)(?:_info)?|filter_mode|half|image_info|kernel(?:_info|_work_group_info)?|map_flags|mem(?:_flags|_info|_object_type)?|platform_(?:id|info)|profiling_info|program(?:_build_info|_info)?))\b/,alias:"keyword"},"boolean-opencl-host":{pattern:/\bCL_(?:TRUE|FALSE)\b/,alias:"boolean"},"constant-opencl-host":{pattern:/\bCL_(?:A|ABGR|ADDRESS_(?:CLAMP(?:_TO_EDGE)?|MIRRORED_REPEAT|NONE|REPEAT)|ARGB|BGRA|BLOCKING|BUFFER_CREATE_TYPE_REGION|BUILD_(?:ERROR|IN_PROGRESS|NONE|PROGRAM_FAILURE|SUCCESS)|COMMAND_(?:ACQUIRE_GL_OBJECTS|BARRIER|COPY_(?:BUFFER(?:_RECT|_TO_IMAGE)?|IMAGE(?:_TO_BUFFER)?)|FILL_(?:BUFFER|IMAGE)|MAP(?:_BUFFER|_IMAGE)|MARKER|MIGRATE(?:_SVM)?_MEM_OBJECTS|NATIVE_KERNEL|NDRANGE_KERNEL|READ_(?:BUFFER(?:_RECT)?|IMAGE)|RELEASE_GL_OBJECTS|SVM_(?:FREE|MAP|MEMCPY|MEMFILL|UNMAP)|TASK|UNMAP_MEM_OBJECT|USER|WRITE_(?:BUFFER(?:_RECT)?|IMAGE))|COMPILER_NOT_AVAILABLE|COMPILE_PROGRAM_FAILURE|COMPLETE|CONTEXT_(?:DEVICES|INTEROP_USER_SYNC|NUM_DEVICES|PLATFORM|PROPERTIES|REFERENCE_COUNT)|DEPTH(?:_STENCIL)?|DEVICE_(?:ADDRESS_BITS|AFFINITY_DOMAIN_(?:L[1-4]_CACHE|NEXT_PARTITIONABLE|NUMA)|AVAILABLE|BUILT_IN_KERNELS|COMPILER_AVAILABLE|DOUBLE_FP_CONFIG|ENDIAN_LITTLE|ERROR_CORRECTION_SUPPORT|EXECUTION_CAPABILITIES|EXTENSIONS|GLOBAL_(?:MEM_(?:CACHELINE_SIZE|CACHE_SIZE|CACHE_TYPE|SIZE)|VARIABLE_PREFERRED_TOTAL_SIZE)|HOST_UNIFIED_MEMORY|IL_VERSION|IMAGE(?:2D_MAX_(?:HEIGHT|WIDTH)|3D_MAX_(?:DEPTH|HEIGHT|WIDTH)|_BASE_ADDRESS_ALIGNMENT|_MAX_ARRAY_SIZE|_MAX_BUFFER_SIZE|_PITCH_ALIGNMENT|_SUPPORT)|LINKER_AVAILABLE|LOCAL_MEM_SIZE|LOCAL_MEM_TYPE|MAX_(?:CLOCK_FREQUENCY|COMPUTE_UNITS|CONSTANT_ARGS|CONSTANT_BUFFER_SIZE|GLOBAL_VARIABLE_SIZE|MEM_ALLOC_SIZE|NUM_SUB_GROUPS|ON_DEVICE_(?:EVENTS|QUEUES)|PARAMETER_SIZE|PIPE_ARGS|READ_IMAGE_ARGS|READ_WRITE_IMAGE_ARGS|SAMPLERS|WORK_GROUP_SIZE|WORK_ITEM_DIMENSIONS|WORK_ITEM_SIZES|WRITE_IMAGE_ARGS)|MEM_BASE_ADDR_ALIGN|MIN_DATA_TYPE_ALIGN_SIZE|NAME|NATIVE_VECTOR_WIDTH_(?:CHAR|DOUBLE|FLOAT|HALF|INT|LONG|SHORT)|NOT_(?:AVAILABLE|FOUND)|OPENCL_C_VERSION|PARENT_DEVICE|PARTITION_(?:AFFINITY_DOMAIN|BY_AFFINITY_DOMAIN|BY_COUNTS|BY_COUNTS_LIST_END|EQUALLY|FAILED|MAX_SUB_DEVICES|PROPERTIES|TYPE)|PIPE_MAX_(?:ACTIVE_RESERVATIONS|PACKET_SIZE)|PLATFORM|PREFERRED_(?:GLOBAL_ATOMIC_ALIGNMENT|INTEROP_USER_SYNC|LOCAL_ATOMIC_ALIGNMENT|PLATFORM_ATOMIC_ALIGNMENT|VECTOR_WIDTH_(?:CHAR|DOUBLE|FLOAT|HALF|INT|LONG|SHORT))|PRINTF_BUFFER_SIZE|PROFILE|PROFILING_TIMER_RESOLUTION|QUEUE_(?:ON_(?:DEVICE_(?:MAX_SIZE|PREFERRED_SIZE|PROPERTIES)|HOST_PROPERTIES)|PROPERTIES)|REFERENCE_COUNT|SINGLE_FP_CONFIG|SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS|SVM_(?:ATOMICS|CAPABILITIES|COARSE_GRAIN_BUFFER|FINE_GRAIN_BUFFER|FINE_GRAIN_SYSTEM)|TYPE(?:_ACCELERATOR|_ALL|_CPU|_CUSTOM|_DEFAULT|_GPU)?|VENDOR(?:_ID)?|VERSION)|DRIVER_VERSION|EVENT_(?:COMMAND_(?:EXECUTION_STATUS|QUEUE|TYPE)|CONTEXT|REFERENCE_COUNT)|EXEC_(?:KERNEL|NATIVE_KERNEL|STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST)|FILTER_(?:LINEAR|NEAREST)|FLOAT|FP_(?:CORRECTLY_ROUNDED_DIVIDE_SQRT|DENORM|FMA|INF_NAN|ROUND_TO_INF|ROUND_TO_NEAREST|ROUND_TO_ZERO|SOFT_FLOAT)|GLOBAL|HALF_FLOAT|IMAGE_(?:ARRAY_SIZE|BUFFER|DEPTH|ELEMENT_SIZE|FORMAT|FORMAT_MISMATCH|FORMAT_NOT_SUPPORTED|HEIGHT|NUM_MIP_LEVELS|NUM_SAMPLES|ROW_PITCH|SLICE_PITCH|WIDTH)|INTENSITY|INVALID_(?:ARG_INDEX|ARG_SIZE|ARG_VALUE|BINARY|BUFFER_SIZE|BUILD_OPTIONS|COMMAND_QUEUE|COMPILER_OPTIONS|CONTEXT|DEVICE|DEVICE_PARTITION_COUNT|DEVICE_QUEUE|DEVICE_TYPE|EVENT|EVENT_WAIT_LIST|GLOBAL_OFFSET|GLOBAL_WORK_SIZE|GL_OBJECT|HOST_PTR|IMAGE_DESCRIPTOR|IMAGE_FORMAT_DESCRIPTOR|IMAGE_SIZE|KERNEL|KERNEL_ARGS|KERNEL_DEFINITION|KERNEL_NAME|LINKER_OPTIONS|MEM_OBJECT|MIP_LEVEL|OPERATION|PIPE_SIZE|PLATFORM|PROGRAM|PROGRAM_EXECUTABLE|PROPERTY|QUEUE_PROPERTIES|SAMPLER|VALUE|WORK_DIMENSION|WORK_GROUP_SIZE|WORK_ITEM_SIZE)|KERNEL_(?:ARG_(?:ACCESS_(?:NONE|QUALIFIER|READ_ONLY|READ_WRITE|WRITE_ONLY)|ADDRESS_(?:CONSTANT|GLOBAL|LOCAL|PRIVATE|QUALIFIER)|INFO_NOT_AVAILABLE|NAME|TYPE_(?:CONST|NAME|NONE|PIPE|QUALIFIER|RESTRICT|VOLATILE))|ATTRIBUTES|COMPILE_NUM_SUB_GROUPS|COMPILE_WORK_GROUP_SIZE|CONTEXT|EXEC_INFO_SVM_FINE_GRAIN_SYSTEM|EXEC_INFO_SVM_PTRS|FUNCTION_NAME|GLOBAL_WORK_SIZE|LOCAL_MEM_SIZE|LOCAL_SIZE_FOR_SUB_GROUP_COUNT|MAX_NUM_SUB_GROUPS|MAX_SUB_GROUP_SIZE_FOR_NDRANGE|NUM_ARGS|PREFERRED_WORK_GROUP_SIZE_MULTIPLE|PRIVATE_MEM_SIZE|PROGRAM|REFERENCE_COUNT|SUB_GROUP_COUNT_FOR_NDRANGE|WORK_GROUP_SIZE)|LINKER_NOT_AVAILABLE|LINK_PROGRAM_FAILURE|LOCAL|LUMINANCE|MAP_(?:FAILURE|READ|WRITE|WRITE_INVALIDATE_REGION)|MEM_(?:ALLOC_HOST_PTR|ASSOCIATED_MEMOBJECT|CONTEXT|COPY_HOST_PTR|COPY_OVERLAP|FLAGS|HOST_NO_ACCESS|HOST_PTR|HOST_READ_ONLY|HOST_WRITE_ONLY|KERNEL_READ_AND_WRITE|MAP_COUNT|OBJECT_(?:ALLOCATION_FAILURE|BUFFER|IMAGE1D|IMAGE1D_ARRAY|IMAGE1D_BUFFER|IMAGE2D|IMAGE2D_ARRAY|IMAGE3D|PIPE)|OFFSET|READ_ONLY|READ_WRITE|REFERENCE_COUNT|SIZE|SVM_ATOMICS|SVM_FINE_GRAIN_BUFFER|TYPE|USES_SVM_POINTER|USE_HOST_PTR|WRITE_ONLY)|MIGRATE_MEM_OBJECT_(?:CONTENT_UNDEFINED|HOST)|MISALIGNED_SUB_BUFFER_OFFSET|NONE|NON_BLOCKING|OUT_OF_(?:HOST_MEMORY|RESOURCES)|PIPE_(?:MAX_PACKETS|PACKET_SIZE)|PLATFORM_(?:EXTENSIONS|HOST_TIMER_RESOLUTION|NAME|PROFILE|VENDOR|VERSION)|PROFILING_(?:COMMAND_(?:COMPLETE|END|QUEUED|START|SUBMIT)|INFO_NOT_AVAILABLE)|PROGRAM_(?:BINARIES|BINARY_SIZES|BINARY_TYPE(?:_COMPILED_OBJECT|_EXECUTABLE|_LIBRARY|_NONE)?|BUILD_(?:GLOBAL_VARIABLE_TOTAL_SIZE|LOG|OPTIONS|STATUS)|CONTEXT|DEVICES|IL|KERNEL_NAMES|NUM_DEVICES|NUM_KERNELS|REFERENCE_COUNT|SOURCE)|QUEUED|QUEUE_(?:CONTEXT|DEVICE|DEVICE_DEFAULT|ON_DEVICE|ON_DEVICE_DEFAULT|OUT_OF_ORDER_EXEC_MODE_ENABLE|PROFILING_ENABLE|PROPERTIES|REFERENCE_COUNT|SIZE)|R|RA|READ_(?:ONLY|WRITE)_CACHE|RG|RGB|RGBA|RGBx|RGx|RUNNING|Rx|SAMPLER_(?:ADDRESSING_MODE|CONTEXT|FILTER_MODE|LOD_MAX|LOD_MIN|MIP_FILTER_MODE|NORMALIZED_COORDS|REFERENCE_COUNT)|(?:UN)?SIGNED_INT(?:8|16|32)|SNORM_INT(?:8|16)|SUBMITTED|SUCCESS|UNORM_INT(?:16|24|8|_101010|_101010_2)|UNORM_SHORT_(?:555|565)|VERSION_(?:1_0|1_1|1_2|2_0|2_1)|sBGRA|sRGB|sRGBA|sRGBx)\b/,alias:"constant"},"function-opencl-host":{pattern:/\bcl(?:BuildProgram|CloneKernel|CompileProgram|Create(?:Buffer|CommandQueue(?:WithProperties)?|Context|ContextFromType|Image|Image2D|Image3D|Kernel|KernelsInProgram|Pipe|ProgramWith(?:Binary|BuiltInKernels|IL|Source)|Sampler|SamplerWithProperties|SubBuffer|SubDevices|UserEvent)|Enqueue(?:(?:Barrier|Marker)(?:WithWaitList)?|Copy(?:Buffer(?:Rect|ToImage)?|Image(?:ToBuffer)?)|(?:Fill|Map)(?:Buffer|Image)|MigrateMemObjects|NDRangeKernel|NativeKernel|(?:Read|Write)(?:Buffer(?:Rect)?|Image)|SVM(?:Free|Map|MemFill|Memcpy|MigrateMem|Unmap)|Task|UnmapMemObject|WaitForEvents)|Finish|Flush|Get(?:CommandQueueInfo|ContextInfo|Device(?:AndHostTimer|IDs|Info)|Event(?:Profiling)?Info|ExtensionFunctionAddress(?:ForPlatform)?|HostTimer|ImageInfo|Kernel(?:ArgInfo|Info|SubGroupInfo|WorkGroupInfo)|MemObjectInfo|PipeInfo|Platform(?:IDs|Info)|Program(?:Build)?Info|SamplerInfo|SupportedImageFormats)|LinkProgram|(?:Release|Retain)(?:CommandQueue|Context|Device|Event|Kernel|MemObject|Program|Sampler)|SVM(?:Alloc|Free)|Set(?:CommandQueueProperty|DefaultDeviceCommandQueue|EventCallback|Kernel(?:Arg(?:SVMPointer)?|ExecInfo)|Kernel|MemObjectDestructorCallback|UserEventStatus)|Unload(?:Platform)?Compiler|WaitForEvents)\b/,alias:"function"}},t.languages.insertBefore("c","keyword",n),t.languages.cpp&&(n["type-opencl-host-cpp"]={pattern:/\b(?:Buffer|BufferGL|BufferRenderGL|CommandQueue|Context|Device|DeviceCommandQueue|EnqueueArgs|Event|Image|Image1D|Image1DArray|Image1DBuffer|Image2D|Image2DArray|Image2DGL|Image3D|Image3DGL|ImageFormat|ImageGL|Kernel|KernelFunctor|LocalSpaceArg|Memory|NDRange|Pipe|Platform|Program|Sampler|SVMAllocator|SVMTraitAtomic|SVMTraitCoarse|SVMTraitFine|SVMTraitReadOnly|SVMTraitReadWrite|SVMTraitWriteOnly|UserEvent)\b/,alias:"keyword"},t.languages.insertBefore("cpp","keyword",n))}e.exports=i,i.displayName="opencl",i.aliases=[]},22950(e){"use strict";function t(e){e.languages.openqasm={comment:/\/\*[\s\S]*?\*\/|\/\/.*/,string:{pattern:/"[^"\r\n\t]*"|'[^'\r\n\t]*'/,greedy:!0},keyword:/\b(?:barrier|boxas|boxto|break|const|continue|ctrl|def|defcal|defcalgrammar|delay|else|end|for|gate|gphase|if|in|include|inv|kernel|lengthof|let|measure|pow|reset|return|rotary|stretchinf|while|CX|OPENQASM|U)\b|#pragma\b/,"class-name":/\b(?:angle|bit|bool|creg|fixed|float|int|length|qreg|qubit|stretch|uint)\b/,function:/\b(?:sin|cos|tan|exp|ln|sqrt|rotl|rotr|popcount)\b(?=\s*\()/,constant:/\b(?:pi|tau|euler)\b|π|𝜏|ℇ/,number:{pattern:/(^|[^.\w$])(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?(?:dt|ns|us|µs|ms|s)?/i,lookbehind:!0},operator:/->|>>=?|<<=?|&&|\|\||\+\+|--|[!=<>&|~^+\-*/%]=?|@/,punctuation:/[(){}\[\];,:.]/},e.languages.qasm=e.languages.openqasm}e.exports=t,t.displayName="openqasm",t.aliases=["qasm"]},23254(e){"use strict";function t(e){e.languages.oz={comment:/\/\*[\s\S]*?\*\/|%.*/,string:{pattern:/"(?:[^"\\]|\\[\s\S])*"/,greedy:!0},atom:{pattern:/'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,alias:"builtin"},keyword:/\$|\[\]|\b(?:_|at|attr|case|catch|choice|class|cond|declare|define|dis|else(?:case|if)?|end|export|fail|false|feat|finally|from|fun|functor|if|import|in|local|lock|meth|nil|not|of|or|prepare|proc|prop|raise|require|self|skip|then|thread|true|try|unit)\b/,function:[/\b[a-z][A-Za-z\d]*(?=\()/,{pattern:/(\{)[A-Z][A-Za-z\d]*\b/,lookbehind:!0}],number:/\b(?:0[bx][\da-f]+|\d+(?:\.\d*)?(?:e~?\d+)?)\b|&(?:[^\\]|\\(?:\d{3}|.))/i,variable:/\b[A-Z][A-Za-z\d]*|`(?:[^`\\]|\\.)+`/,"attr-name":/\b\w+(?=:)/,operator:/:(?:=|::?)|<[-:=]?|=(?:=|=?:?|\\=:?|!!?|[|#+\-*\/,~^@]|\b(?:andthen|div|mod|orelse)\b/,punctuation:/[\[\](){}.:;?]/}}e.exports=t,t.displayName="oz",t.aliases=[]},92694(e){"use strict";function t(e){var t;e.languages.parigp={comment:/\/\*[\s\S]*?\*\/|\\\\.*/,string:{pattern:/"(?:[^"\\\r\n]|\\.)*"/,greedy:!0},keyword:RegExp("\\b(?:"+(t=(t=["breakpoint","break","dbg_down","dbg_err","dbg_up","dbg_x","forcomposite","fordiv","forell","forpart","forprime","forstep","forsubgroup","forvec","for","iferr","if","local","my","next","return","until","while"]).map(function(e){return e.split("").join(" *")}).join("|"))+")\\b"),function:/\b\w(?:[\w ]*\w)?(?= *\()/,number:{pattern:/((?:\. *\. *)?)(?:\b\d(?: *\d)*(?: *(?!\. *\.)\.(?: *\d)*)?|\. *\d(?: *\d)*)(?: *e *(?:[+-] *)?\d(?: *\d)*)?/i,lookbehind:!0},operator:/\. *\.|[*\/!](?: *=)?|%(?: *=|(?: *#)?(?: *')*)?|\+(?: *[+=])?|-(?: *[-=>])?|<(?: *>|(?: *<)?(?: *=)?)?|>(?: *>)?(?: *=)?|=(?: *=){0,2}|\\(?: *\/)?(?: *=)?|&(?: *&)?|\| *\||['#~^]/,punctuation:/[\[\]{}().,:;|]/}}e.exports=t,t.displayName="parigp",t.aliases=[]},43273(e){"use strict";function t(e){var t,n;n=(t=e).languages.parser=t.languages.extend("markup",{keyword:{pattern:/(^|[^^])(?:\^(?:case|eval|for|if|switch|throw)\b|@(?:BASE|CLASS|GET(?:_DEFAULT)?|OPTIONS|SET_DEFAULT|USE)\b)/,lookbehind:!0},variable:{pattern:/(^|[^^])\B\$(?:\w+|(?=[.{]))(?:(?:\.|::?)\w+)*(?:\.|::?)?/,lookbehind:!0,inside:{punctuation:/\.|:+/}},function:{pattern:/(^|[^^])\B[@^]\w+(?:(?:\.|::?)\w+)*(?:\.|::?)?/,lookbehind:!0,inside:{keyword:{pattern:/(^@)(?:GET_|SET_)/,lookbehind:!0},punctuation:/\.|:+/}},escape:{pattern:/\^(?:[$^;@()\[\]{}"':]|#[a-f\d]*)/i,alias:"builtin"},punctuation:/[\[\](){};]/}),n=t.languages.insertBefore("parser","keyword",{"parser-comment":{pattern:/(\s)#.*/,lookbehind:!0,alias:"comment"},expression:{pattern:/(^|[^^])\((?:[^()]|\((?:[^()]|\((?:[^()])*\))*\))*\)/,greedy:!0,lookbehind:!0,inside:{string:{pattern:/(^|[^^])(["'])(?:(?!\2)[^^]|\^[\s\S])*\2/,lookbehind:!0},keyword:n.keyword,variable:n.variable,function:n.function,boolean:/\b(?:true|false)\b/,number:/\b(?:0x[a-f\d]+|\d+(?:\.\d*)?(?:e[+-]?\d+)?)\b/i,escape:n.escape,operator:/[~+*\/\\%]|!(?:\|\|?|=)?|&&?|\|\|?|==|<[<=]?|>[>=]?|-[fd]?|\b(?:def|eq|ge|gt|in|is|le|lt|ne)\b/,punctuation:n.punctuation}}}),t.languages.insertBefore("inside","punctuation",{expression:n.expression,keyword:n.keyword,variable:n.variable,function:n.function,escape:n.escape,"parser-punctuation":{pattern:n.punctuation,alias:"punctuation"}},n.tag.inside["attr-value"])}e.exports=t,t.displayName="parser",t.aliases=[]},60718(e){"use strict";function t(e){e.languages.pascal={comment:[/\(\*[\s\S]+?\*\)/,/\{[\s\S]+?\}/,/\/\/.*/],string:{pattern:/(?:'(?:''|[^'\r\n])*'(?!')|#[&$%]?[a-f\d]+)+|\^[a-z]/i,greedy:!0},keyword:[{pattern:/(^|[^&])\b(?:absolute|array|asm|begin|case|const|constructor|destructor|do|downto|else|end|file|for|function|goto|if|implementation|inherited|inline|interface|label|nil|object|of|operator|packed|procedure|program|record|reintroduce|repeat|self|set|string|then|to|type|unit|until|uses|var|while|with)\b/i,lookbehind:!0},{pattern:/(^|[^&])\b(?:dispose|exit|false|new|true)\b/i,lookbehind:!0},{pattern:/(^|[^&])\b(?:class|dispinterface|except|exports|finalization|finally|initialization|inline|library|on|out|packed|property|raise|resourcestring|threadvar|try)\b/i,lookbehind:!0},{pattern:/(^|[^&])\b(?:absolute|abstract|alias|assembler|bitpacked|break|cdecl|continue|cppdecl|cvar|default|deprecated|dynamic|enumerator|experimental|export|external|far|far16|forward|generic|helper|implements|index|interrupt|iochecks|local|message|name|near|nodefault|noreturn|nostackframe|oldfpccall|otherwise|overload|override|pascal|platform|private|protected|public|published|read|register|reintroduce|result|safecall|saveregisters|softfloat|specialize|static|stdcall|stored|strict|unaligned|unimplemented|varargs|virtual|write)\b/i,lookbehind:!0}],number:[/(?:[&%]\d+|\$[a-f\d]+)/i,/\b\d+(?:\.\d+)?(?:e[+-]?\d+)?/i],operator:[/\.\.|\*\*|:=|<[<=>]?|>[>=]?|[+\-*\/]=?|[@^=]/i,{pattern:/(^|[^&])\b(?:and|as|div|exclude|in|include|is|mod|not|or|shl|shr|xor)\b/,lookbehind:!0}],punctuation:/\(\.|\.\)|[()\[\]:;,.]/},e.languages.objectpascal=e.languages.pascal}e.exports=t,t.displayName="pascal",t.aliases=["objectpascal"]},39303(e){"use strict";function t(e){var t,n,r,i,a;t=e,n=/\((?:[^()]|\((?:[^()]|\([^()]*\))*\))*\)/.source,r=/(?:\b\w+(?:)?|)/.source.replace(//g,function(){return n}),i=t.languages.pascaligo={comment:/\(\*[\s\S]+?\*\)|\/\/.*/,string:{pattern:/(["'`])(?:\\[\s\S]|(?!\1)[^\\])*\1|\^[a-z]/i,greedy:!0},"class-name":[{pattern:RegExp(/(\btype\s+\w+\s+is\s+)/.source.replace(//g,function(){return r}),"i"),lookbehind:!0,inside:null},{pattern:RegExp(/(?=\s+is\b)/.source.replace(//g,function(){return r}),"i"),inside:null},{pattern:RegExp(/(:\s*)/.source.replace(//g,function(){return r})),lookbehind:!0,inside:null}],keyword:{pattern:/(^|[^&])\b(?:begin|block|case|const|else|end|fail|for|from|function|if|is|nil|of|remove|return|skip|then|type|var|while|with)\b/i,lookbehind:!0},boolean:{pattern:/(^|[^&])\b(?:True|False)\b/i,lookbehind:!0},builtin:{pattern:/(^|[^&])\b(?:bool|int|list|map|nat|record|string|unit)\b/i,lookbehind:!0},function:/\b\w+(?=\s*\()/i,number:[/%[01]+|&[0-7]+|\$[a-f\d]+/i,/\b\d+(?:\.\d+)?(?:e[+-]?\d+)?(?:mtz|n)?/i],operator:/->|=\/=|\.\.|\*\*|:=|<[<=>]?|>[>=]?|[+\-*\/]=?|[@^=|]|\b(?:and|mod|or)\b/,punctuation:/\(\.|\.\)|[()\[\]:;,.{}]/},a=["comment","keyword","builtin","operator","punctuation"].reduce(function(e,t){return e[t]=i[t],e},{}),i["class-name"].forEach(function(e){e.inside=a})}e.exports=t,t.displayName="pascaligo",t.aliases=[]},77393(e){"use strict";function t(e){e.languages.pcaxis={string:/"[^"]*"/,keyword:{pattern:/((?:^|;)\s*)[-A-Z\d]+(?:\s*\[[-\w]+\])?(?:\s*\("[^"]*"(?:,\s*"[^"]*")*\))?(?=\s*=)/,lookbehind:!0,greedy:!0,inside:{keyword:/^[-A-Z\d]+/,language:{pattern:/^(\s*)\[[-\w]+\]/,lookbehind:!0,inside:{punctuation:/^\[|\]$/,property:/[-\w]+/}},"sub-key":{pattern:/^(\s*)\S[\s\S]*/,lookbehind:!0,inside:{parameter:{pattern:/"[^"]*"/,alias:"property"},punctuation:/^\(|\)$|,/}}}},operator:/=/,tlist:{pattern:/TLIST\s*\(\s*\w+(?:(?:\s*,\s*"[^"]*")+|\s*,\s*"[^"]*"-"[^"]*")?\s*\)/,greedy:!0,inside:{function:/^TLIST/,property:{pattern:/^(\s*\(\s*)\w+/,lookbehind:!0},string:/"[^"]*"/,punctuation:/[(),]/,operator:/-/}},punctuation:/[;,]/,number:{pattern:/(^|\s)\d+(?:\.\d+)?(?!\S)/,lookbehind:!0},boolean:/YES|NO/},e.languages.px=e.languages.pcaxis}e.exports=t,t.displayName="pcaxis",t.aliases=["px"]},19023(e){"use strict";function t(e){e.languages.peoplecode={comment:RegExp([/\/\*[\s\S]*?\*\//.source,/\bREM[^;]*;/.source,/<\*(?:[^<*]|\*(?!>)|<(?!\*)|<\*(?:(?!\*>)[\s\S])*\*>)*\*>/.source,/\/\+[\s\S]*?\+\//.source].join("|")),string:{pattern:/'(?:''|[^'\r\n])*'(?!')|"(?:""|[^"\r\n])*"(?!")/,greedy:!0},variable:/%\w+/,"function-definition":{pattern:/((?:^|[^\w-])(?:function|method)\s+)\w+/i,lookbehind:!0,alias:"function"},"class-name":{pattern:/((?:^|[^-\w])(?:as|catch|class|component|create|extends|global|implements|instance|local|of|property|returns)\s+)\w+(?::\w+)*/i,lookbehind:!0,inside:{punctuation:/:/}},keyword:/\b(?:abstract|alias|as|catch|class|component|constant|create|declare|else|end-(?:class|evaluate|for|function|get|if|method|set|try|while)|evaluate|extends|for|function|get|global|implements|import|instance|if|library|local|method|null|of|out|peopleCode|private|program|property|protected|readonly|ref|repeat|returns?|set|step|then|throw|to|try|until|value|when(?:-other)?|while)\b/i,"operator-keyword":{pattern:/\b(?:and|not|or)\b/i,alias:"operator"},function:/[_a-z]\w*(?=\s*\()/i,boolean:/\b(?:false|true)\b/i,number:/\b\d+(?:\.\d+)?\b/,operator:/<>|[<>]=?|!=|\*\*|[-+*/|=@]/,punctuation:/[:.;,()[\]]/},e.languages.pcode=e.languages.peoplecode}e.exports=t,t.displayName="peoplecode",t.aliases=["pcode"]},74212(e){"use strict";function t(e){e.languages.perl={comment:[{pattern:/(^\s*)=\w[\s\S]*?=cut.*/m,lookbehind:!0},{pattern:/(^|[^\\$])#.*/,lookbehind:!0}],string:[{pattern:/\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\((?:[^()\\]|\\[\s\S])*\)/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\{(?:[^{}\\]|\\[\s\S])*\}/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*\[(?:[^[\]\\]|\\[\s\S])*\]/,greedy:!0},{pattern:/\b(?:q|qq|qx|qw)\s*<(?:[^<>\\]|\\[\s\S])*>/,greedy:!0},{pattern:/("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/,greedy:!0},{pattern:/'(?:[^'\\\r\n]|\\.)*'/,greedy:!0}],regex:[{pattern:/\b(?:m|qr)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngc]*/,greedy:!0},{pattern:/\b(?:m|qr)\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngc]*/,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\((?:[^()\\]|\\[\s\S])*\)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\{(?:[^{}\\]|\\[\s\S])*\}\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*\[(?:[^[\]\\]|\\[\s\S])*\]\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^-]\b)(?:s|tr|y)\s*<(?:[^<>\\]|\\[\s\S])*>\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngcer]*/,lookbehind:!0,greedy:!0},{pattern:/\/(?:[^\/\\\r\n]|\\.)*\/[msixpodualngc]*(?=\s*(?:$|[\r\n,.;})&|\-+*~<>!?^]|(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor|x)\b))/,greedy:!0}],variable:[/[&*$@%]\{\^[A-Z]+\}/,/[&*$@%]\^[A-Z_]/,/[&*$@%]#?(?=\{)/,/[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+(?![\w$]))+(?:::)*/i,/[&*$@%]\d+/,/(?!%=)[$@%][!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/],filehandle:{pattern:/<(?![<=])\S*>|\b_\b/,alias:"symbol"},vstring:{pattern:/v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/,alias:"string"},function:{pattern:/sub \w+/i,inside:{keyword:/sub/}},keyword:/\b(?:any|break|continue|default|delete|die|do|else|elsif|eval|for|foreach|given|goto|if|last|local|my|next|our|package|print|redo|require|return|say|state|sub|switch|undef|unless|until|use|when|while)\b/,number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)\b/,operator:/-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor)\b/,punctuation:/[{}[\];(),:]/}}e.exports=t,t.displayName="perl",t.aliases=[]},5137(e,t,n){"use strict";var r=n(88262);function i(e){e.register(r),e.languages.insertBefore("php","variable",{this:/\$this\b/,global:/\$(?:_(?:SERVER|GET|POST|FILES|REQUEST|SESSION|ENV|COOKIE)|GLOBALS|HTTP_RAW_POST_DATA|argc|argv|php_errormsg|http_response_header)\b/,scope:{pattern:/\b[\w\\]+::/,inside:{keyword:/static|self|parent/,punctuation:/::|\\/}}})}e.exports=i,i.displayName="phpExtras",i.aliases=[]},88262(e,t,n){"use strict";var r=n(93205);function i(e){var t,n,i,a,o,s,u,c;e.register(r),n=/\/\*[\s\S]*?\*\/|\/\/.*|#(?!\[).*/,i=[{pattern:/\b(?:false|true)\b/i,alias:"boolean"},{pattern:/(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,greedy:!0,lookbehind:!0},{pattern:/(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,greedy:!0,lookbehind:!0},/\b(?:null)\b/i,/\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/],a=/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i,o=/|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/,s=/[{}\[\](),:;]/,(t=e).languages.php={delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"},comment:n,variable:/\$+(?:\w+\b|(?=\{))/i,package:{pattern:/(namespace\s+|use\s+(?:function\s+)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,lookbehind:!0,inside:{punctuation:/\\/}},"class-name-definition":{pattern:/(\b(?:class|enum|interface|trait)\s+)\b[a-z_]\w*(?!\\)\b/i,lookbehind:!0,alias:"class-name"},"function-definition":{pattern:/(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,lookbehind:!0,alias:"function"},keyword:[{pattern:/(\(\s*)\b(?:bool|boolean|int|integer|float|string|object|array)\b(?=\s*\))/i,alias:"type-casting",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)\b(?:bool|int|float|string|object|array(?!\s*\()|mixed|self|static|callable|iterable|(?:null|false)(?=\s*\|))\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*[\w|]\|\s*)(?:null|false)\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b(?:bool|int|float|string|object|void|array(?!\s*\()|mixed|self|static|callable|iterable|(?:null|false)(?=\s*\|))\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?[\w|]\|\s*)(?:null|false)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/\b(?:bool|int|float|string|object|void|array(?!\s*\()|mixed|iterable|(?:null|false)(?=\s*\|))\b/i,alias:"type-declaration",greedy:!0},{pattern:/(\|\s*)(?:null|false)\b/i,alias:"type-declaration",greedy:!0,lookbehind:!0},{pattern:/\b(?:parent|self|static)(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(\byield\s+)from\b/i,lookbehind:!0},/\bclass\b/i,{pattern:/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:__halt_compiler|abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|match|new|or|parent|print|private|protected|public|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/i,lookbehind:!0}],"argument-name":{pattern:/([(,]\s+)\b[a-z_]\w*(?=\s*:(?!:))/i,lookbehind:!0},"class-name":[{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/(\|\s*)\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/\b[a-z_]\w*(?!\\)\b(?=\s*\|)/i,greedy:!0},{pattern:/(\|\s*)(?:\\?\b[a-z_]\w*)+\b/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(?:\\?\b[a-z_]\w*)+\b(?=\s*\|)/i,alias:"class-name-fully-qualified",greedy:!0,inside:{punctuation:/\\/}},{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self\b|\s+static\b))\s+|\bcatch\s*\()(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*\$)/i,alias:"type-declaration",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-declaration"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*::)/i,alias:["class-name-fully-qualified","static-context"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/([(,?]\s*)[a-z_]\w*(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-hint"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b[a-z_]\w*(?!\\)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:["class-name-fully-qualified","return-type"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:i,function:{pattern:/(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,lookbehind:!0,inside:{punctuation:/\\/}},property:{pattern:/(->\s*)\w+/,lookbehind:!0},number:a,operator:o,punctuation:s},c=[{pattern:/<<<'([^']+)'[\r\n](?:.*[\r\n])*?\1;/,alias:"nowdoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},{pattern:/<<<(?:"([^"]+)"[\r\n](?:.*[\r\n])*?\1;|([a-z_]\w*)[\r\n](?:.*[\r\n])*?\2;)/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:u={pattern:/\{\$(?:\{(?:\{[^{}]+\}|[^{}]+)\}|[^{}])+\}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,lookbehind:!0,inside:t.languages.php}}},{pattern:/`(?:\\[\s\S]|[^\\`])*`/,alias:"backtick-quoted-string",greedy:!0},{pattern:/'(?:\\[\s\S]|[^\\'])*'/,alias:"single-quoted-string",greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,alias:"double-quoted-string",greedy:!0,inside:{interpolation:u}}],t.languages.insertBefore("php","variable",{string:c,attribute:{pattern:/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/im,greedy:!0,inside:{"attribute-content":{pattern:/^(#\[)[\s\S]+(?=\]$)/,lookbehind:!0,inside:{comment:n,string:c,"attribute-class-name":[{pattern:/([^:]|^)\b[a-z_]\w*(?!\\)\b/i,alias:"class-name",greedy:!0,lookbehind:!0},{pattern:/([^:]|^)(?:\\?\b[a-z_]\w*)+/i,alias:["class-name","class-name-fully-qualified"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:i,number:a,operator:o,punctuation:s}},delimiter:{pattern:/^#\[|\]$/,alias:"punctuation"}}}}),t.hooks.add("before-tokenize",function(e){if(/<\?/.test(e.code)){var n=/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#(?!\[))(?:[^?\n\r]|\?(?!>))*(?=$|\?>|[\r\n])|#\[|\/\*(?:[^*]|\*(?!\/))*(?:\*\/|$))*?(?:\?>|$)/gi;t.languages["markup-templating"].buildPlaceholders(e,"php",n)}}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"php")})}e.exports=i,i.displayName="php",i.aliases=[]},63632(e,t,n){"use strict";var r=n(88262),i=n(9858);function a(e){var t,n;e.register(r),e.register(i),n=/(?:\b[a-zA-Z]\w*|[|\\[\]])+/.source,(t=e).languages.phpdoc=t.languages.extend("javadoclike",{parameter:{pattern:RegExp("(@(?:global|param|property(?:-read|-write)?|var)\\s+(?:"+n+"\\s+)?)\\$\\w+"),lookbehind:!0}}),t.languages.insertBefore("phpdoc","keyword",{"class-name":[{pattern:RegExp("(@(?:global|package|param|property(?:-read|-write)?|return|subpackage|throws|var)\\s+)"+n),lookbehind:!0,inside:{keyword:/\b(?:callback|resource|boolean|integer|double|object|string|array|false|float|mixed|bool|null|self|true|void|int)\b/,punctuation:/[|\\[\]()]/}}]}),t.languages.javadoclike.addSupport("php",t.languages.phpdoc)}e.exports=a,a.displayName="phpdoc",a.aliases=[]},59149(e,t,n){"use strict";var r=n(11114);function i(e){var t,n,i,a;e.register(r),Array.isArray(i=(n=(t=e).languages.plsql=t.languages.extend("sql",{comment:[/\/\*[\s\S]*?\*\//,/--.*/]})).keyword)||(i=n.keyword=[i]),i.unshift(/\b(?:ACCESS|AGENT|AGGREGATE|ARRAY|ARROW|AT|ATTRIBUTE|AUDIT|AUTHID|BFILE_BASE|BLOB_BASE|BLOCK|BODY|BOTH|BOUND|BYTE|CALLING|CHAR_BASE|CHARSET(?:FORM|ID)|CLOB_BASE|COLAUTH|COLLECT|CLUSTERS?|COMPILED|COMPRESS|CONSTANT|CONSTRUCTOR|CONTEXT|CRASH|CUSTOMDATUM|DANGLING|DATE_BASE|DEFINE|DETERMINISTIC|DURATION|ELEMENT|EMPTY|EXCEPTIONS?|EXCLUSIVE|EXTERNAL|FINAL|FORALL|FORM|FOUND|GENERAL|HEAP|HIDDEN|IDENTIFIED|IMMEDIATE|INCLUDING|INCREMENT|INDICATOR|INDEXES|INDICES|INFINITE|INITIAL|ISOPEN|INSTANTIABLE|INTERFACE|INVALIDATE|JAVA|LARGE|LEADING|LENGTH|LIBRARY|LIKE[24C]|LIMITED|LONG|LOOP|MAP|MAXEXTENTS|MAXLEN|MEMBER|MINUS|MLSLABEL|MULTISET|NAME|NAN|NATIVE|NEW|NOAUDIT|NOCOMPRESS|NOCOPY|NOTFOUND|NOWAIT|NUMBER(?:_BASE)?|OBJECT|OCI(?:COLL|DATE|DATETIME|DURATION|INTERVAL|LOBLOCATOR|NUMBER|RAW|REF|REFCURSOR|ROWID|STRING|TYPE)|OFFLINE|ONLINE|ONLY|OPAQUE|OPERATOR|ORACLE|ORADATA|ORGANIZATION|ORL(?:ANY|VARY)|OTHERS|OVERLAPS|OVERRIDING|PACKAGE|PARALLEL_ENABLE|PARAMETERS?|PASCAL|PCTFREE|PIPE(?:LINED)?|PRAGMA|PRIOR|PRIVATE|RAISE|RANGE|RAW|RECORD|REF|REFERENCE|REM|REMAINDER|RESULT|RESOURCE|RETURNING|REVERSE|ROW(?:ID|NUM|TYPE)|SAMPLE|SB[124]|SEGMENT|SELF|SEPARATE|SEQUENCE|SHORT|SIZE(?:_T)?|SPARSE|SQL(?:CODE|DATA|NAME|STATE)|STANDARD|STATIC|STDDEV|STORED|STRING|STRUCT|STYLE|SUBMULTISET|SUBPARTITION|SUBSTITUTABLE|SUBTYPE|SUCCESSFUL|SYNONYM|SYSDATE|TABAUTH|TDO|THE|TIMEZONE_(?:ABBR|HOUR|MINUTE|REGION)|TRAILING|TRANSAC(?:TIONAL)?|TRUSTED|UB[124]|UID|UNDER|UNTRUSTED|VALIDATE|VALIST|VARCHAR2|VARIABLE|VARIANCE|VARRAY|VIEWS|VOID|WHENEVER|WRAPPED|ZONE)\b/i),Array.isArray(a=n.operator)||(a=n.operator=[a]),a.unshift(/:=/)}e.exports=i,i.displayName="plsql",i.aliases=[]},50256(e){"use strict";function t(e){e.languages.powerquery={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:\/\/).*)/,lookbehind:!0},"quoted-identifier":{pattern:/#"(?:[^"\r\n]|"")*"(?!")/,greedy:!0,alias:"variable"},string:{pattern:/"(?:[^"\r\n]|"")*"(?!")/,greedy:!0},constant:[/\bDay\.(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\b/,/\bTraceLevel\.(?:Critical|Error|Information|Verbose|Warning)\b/,/\bOccurrence\.(?:First|Last|All)\b/,/\bOrder\.(?:Ascending|Descending)\b/,/\bRoundingMode\.(?:AwayFromZero|Down|ToEven|TowardZero|Up)\b/,/\bMissingField\.(?:Error|Ignore|UseNull)\b/,/\bQuoteStyle\.(?:Csv|None)\b/,/\bJoinKind\.(?:Inner|LeftOuter|RightOuter|FullOuter|LeftAnti|RightAnti)\b/,/\bGroupKind\.(?:Global|Local)\b/,/\bExtraValues\.(?:List|Ignore|Error)\b/,/\bJoinAlgorithm\.(?:Dynamic|PairwiseHash|SortMerge|LeftHash|RightHash|LeftIndex|RightIndex)\b/,/\bJoinSide\.(?:Left|Right)\b/,/\bPrecision\.(?:Double|Decimal)\b/,/\bRelativePosition\.From(?:End|Start)\b/,/\bTextEncoding\.(?:Ascii|BigEndianUnicode|Unicode|Utf8|Utf16|Windows)\b/,/\b(?:Any|Binary|Date|DateTime|DateTimeZone|Duration|Int8|Int16|Int32|Int64|Function|List|Logical|None|Number|Record|Table|Text|Time)\.Type\b/,/\bnull\b/],boolean:/\b(?:true|false)\b/,keyword:/\b(?:and|as|each|else|error|if|in|is|let|meta|not|nullable|optional|or|otherwise|section|shared|then|try|type)\b|#(?:binary|date|datetime|datetimezone|duration|infinity|nan|sections|shared|table|time)\b/,function:{pattern:/(^|[^#\w.])(?!\d)[\w.]+(?=\s*\()/,lookbehind:!0},"data-type":{pattern:/\b(?:any|anynonnull|binary|date|datetime|datetimezone|duration|function|list|logical|none|number|record|table|text|time|type)\b/,alias:"variable"},number:{pattern:/\b0x[\da-f]+\b|(?:[+-]?(?:\b\d+\.)?\b\d+|[+-]\.\d+|(^|[^.])\B\.\d+)(?:e[+-]?\d+)?\b/i,lookbehind:!0},operator:/[-+*\/&?@^]|<(?:=>?|>)?|>=?|=>?|\.\.\.?/,punctuation:/[,;\[\](){}]/},e.languages.pq=e.languages.powerquery,e.languages.mscript=e.languages.powerquery}e.exports=t,t.displayName="powerquery",t.aliases=[]},61777(e){"use strict";function t(e){var t,n,r;(r=(n=(t=e).languages.powershell={comment:[{pattern:/(^|[^`])<#[\s\S]*?#>/,lookbehind:!0},{pattern:/(^|[^`])#.*/,lookbehind:!0}],string:[{pattern:/"(?:`[\s\S]|[^`"])*"/,greedy:!0,inside:{function:{pattern:/(^|[^`])\$\((?:\$\([^\r\n()]*\)|(?!\$\()[^\r\n)])*\)/,lookbehind:!0,inside:{}}}},{pattern:/'(?:[^']|'')*'/,greedy:!0}],namespace:/\[[a-z](?:\[(?:\[[^\]]*\]|[^\[\]])*\]|[^\[\]])*\]/i,boolean:/\$(?:true|false)\b/i,variable:/\$\w+\b/,function:[/\b(?:Add|Approve|Assert|Backup|Block|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|ForEach|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Sort|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Tee|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Where|Write)-[a-z]+\b/i,/\b(?:ac|cat|chdir|clc|cli|clp|clv|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|ebp|echo|epal|epcsv|epsn|erase|fc|fl|ft|fw|gal|gbp|gc|gci|gcs|gdr|gi|gl|gm|gp|gps|group|gsv|gu|gv|gwmi|iex|ii|ipal|ipcsv|ipsn|irm|iwmi|iwr|kill|lp|ls|measure|mi|mount|move|mp|mv|nal|ndr|ni|nv|ogv|popd|ps|pushd|pwd|rbp|rd|rdr|ren|ri|rm|rmdir|rni|rnp|rp|rv|rvpa|rwmi|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls|sort|sp|spps|spsv|start|sv|swmi|tee|trcm|type|write)\b/i],keyword:/\b(?:Begin|Break|Catch|Class|Continue|Data|Define|Do|DynamicParam|Else|ElseIf|End|Exit|Filter|Finally|For|ForEach|From|Function|If|InlineScript|Parallel|Param|Process|Return|Sequence|Switch|Throw|Trap|Try|Until|Using|Var|While|Workflow)\b/i,operator:{pattern:/(\W?)(?:!|-(?:eq|ne|gt|ge|lt|le|sh[lr]|not|b?(?:and|x?or)|(?:Not)?(?:Like|Match|Contains|In)|Replace|Join|is(?:Not)?|as)\b|-[-=]?|\+[+=]?|[*\/%]=?)/i,lookbehind:!0},punctuation:/[|{}[\];(),.]/}).string[0].inside).boolean=n.boolean,r.variable=n.variable,r.function.inside=n}e.exports=t,t.displayName="powershell",t.aliases=[]},3623(e){"use strict";function t(e){e.languages.processing=e.languages.extend("clike",{keyword:/\b(?:break|catch|case|class|continue|default|else|extends|final|for|if|implements|import|new|null|private|public|return|static|super|switch|this|try|void|while)\b/,operator:/<[<=]?|>[>=]?|&&?|\|\|?|[%?]|[!=+\-*\/]=?/}),e.languages.insertBefore("processing","number",{constant:/\b(?!XML\b)[A-Z][A-Z\d_]+\b/,type:{pattern:/\b(?:boolean|byte|char|color|double|float|int|[A-Z]\w*)\b/,alias:"variable"}}),e.languages.processing.function=/\b\w+(?=\s*\()/,e.languages.processing["class-name"].alias="variable"}e.exports=t,t.displayName="processing",t.aliases=[]},82707(e){"use strict";function t(e){e.languages.prolog={comment:[/%.+/,/\/\*[\s\S]*?\*\//],string:{pattern:/(["'])(?:\1\1|\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},builtin:/\b(?:fx|fy|xf[xy]?|yfx?)\b/,variable:/\b[A-Z_]\w*/,function:/\b[a-z]\w*(?:(?=\()|\/\d+)/,number:/\b\d+(?:\.\d*)?/,operator:/[:\\=><\-?*@\/;+^|!$.]+|\b(?:is|mod|not|xor)\b/,punctuation:/[(){}\[\],]/}}e.exports=t,t.displayName="prolog",t.aliases=[]},59338(e){"use strict";function t(e){var t,n,r;t=e,r=["sum","min","max","avg","group","stddev","stdvar","count","count_values","bottomk","topk","quantile"].concat(n=["on","ignoring","group_right","group_left","by","without"],["offset"]),t.languages.promql={comment:{pattern:/(^[ \t]*)#.*/m,lookbehind:!0},"vector-match":{pattern:RegExp("((?:"+n.join("|")+")\\s*)\\([^)]*\\)"),lookbehind:!0,inside:{"label-key":{pattern:/\b[^,]*\b/,alias:"attr-name"},punctuation:/[(),]/}},"context-labels":{pattern:/\{[^{}]*\}/,inside:{"label-key":{pattern:/\b[a-z_]\w*(?=\s*(?:=|![=~]))/,alias:"attr-name"},"label-value":{pattern:/(["'`])(?:\\[\s\S]|(?!\1)[^\\])*\1/,greedy:!0,alias:"attr-value"},punctuation:/\{|\}|=~?|![=~]|,/}},"context-range":[{pattern:/\[[\w\s:]+\]/,inside:{punctuation:/\[|\]|:/,"range-duration":{pattern:/\b(?:\d+(?:[smhdwy]|ms))+\b/i,alias:"number"}}},{pattern:/(\boffset\s+)\w+/,lookbehind:!0,inside:{"range-duration":{pattern:/\b(?:\d+(?:[smhdwy]|ms))+\b/i,alias:"number"}}}],keyword:RegExp("\\b(?:"+r.join("|")+")\\b","i"),function:/\b[a-z_]\w*(?=\s*\()/i,number:/[-+]?(?:(?:\b\d+(?:\.\d+)?|\B\.\d+)(?:e[-+]?\d+)?\b|\b(?:0x[0-9a-f]+|nan|inf)\b)/i,operator:/[\^*/%+-]|==|!=|<=|<|>=|>|\b(?:and|unless|or)\b/i,punctuation:/[{};()`,.[\]]/}}e.exports=t,t.displayName="promql",t.aliases=[]},56267(e){"use strict";function t(e){e.languages.properties={comment:/^[ \t]*[#!].*$/m,"attr-value":{pattern:/(^[ \t]*(?:\\(?:\r\n|[\s\S])|[^\\\s:=])+(?: *[=:] *(?! )| ))(?:\\(?:\r\n|[\s\S])|[^\\\r\n])+/m,lookbehind:!0},"attr-name":/^[ \t]*(?:\\(?:\r\n|[\s\S])|[^\\\s:=])+(?= *[=:]| )/m,punctuation:/[=:]/}}e.exports=t,t.displayName="properties",t.aliases=[]},98809(e){"use strict";function t(e){var t,n;n=/\b(?:double|float|[su]?int(?:32|64)|s?fixed(?:32|64)|bool|string|bytes)\b/,(t=e).languages.protobuf=t.languages.extend("clike",{"class-name":[{pattern:/(\b(?:enum|extend|message|service)\s+)[A-Za-z_]\w*(?=\s*\{)/,lookbehind:!0},{pattern:/(\b(?:rpc\s+\w+|returns)\s*\(\s*(?:stream\s+)?)\.?[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*(?=\s*\))/,lookbehind:!0}],keyword:/\b(?:enum|extend|extensions|import|message|oneof|option|optional|package|public|repeated|required|reserved|returns|rpc(?=\s+\w)|service|stream|syntax|to)\b(?!\s*=\s*\d)/,function:/\b[a-z_]\w*(?=\s*\()/i}),t.languages.insertBefore("protobuf","operator",{map:{pattern:/\bmap<\s*[\w.]+\s*,\s*[\w.]+\s*>(?=\s+[a-z_]\w*\s*[=;])/i,alias:"class-name",inside:{punctuation:/[<>.,]/,builtin:n}},builtin:n,"positional-class-name":{pattern:/(?:\b|\B\.)[a-z_]\w*(?:\.[a-z_]\w*)*(?=\s+[a-z_]\w*\s*[=;])/i,alias:"class-name",inside:{punctuation:/\./}},annotation:{pattern:/(\[\s*)[a-z_]\w*(?=\s*=)/i,lookbehind:!0}})}e.exports=t,t.displayName="protobuf",t.aliases=[]},37548(e){"use strict";function t(e){e.languages.psl={comment:{pattern:/#.*/,greedy:!0},string:{pattern:/"(?:\\.|[^\\"])*"/,greedy:!0,inside:{symbol:/\\[ntrbA-Z"\\]/}},"heredoc-string":{pattern:/<<<([a-zA-Z_]\w*)[\r\n](?:.*[\r\n])*?\1\b/,alias:"string",greedy:!0},keyword:/\b(?:__multi|__single|case|default|do|else|elsif|exit|export|for|foreach|function|if|last|line|local|next|requires|return|switch|until|while|word)\b/,constant:/\b(?:ALARM|CHART_ADD_GRAPH|CHART_DELETE_GRAPH|CHART_DESTROY|CHART_LOAD|CHART_PRINT|EOF|FALSE|False|false|NO|No|no|OFFLINE|OK|PSL_PROF_LOG|R_CHECK_HORIZ|R_CHECK_VERT|R_CLICKER|R_COLUMN|R_FRAME|R_ICON|R_LABEL|R_LABEL_CENTER|R_LIST_MULTIPLE|R_LIST_MULTIPLE_ND|R_LIST_SINGLE|R_LIST_SINGLE_ND|R_MENU|R_POPUP|R_POPUP_SCROLLED|R_RADIO_HORIZ|R_RADIO_VERT|R_ROW|R_SCALE_HORIZ|R_SCALE_VERT|R_SPINNER|R_TEXT_FIELD|R_TEXT_FIELD_LABEL|R_TOGGLE|TRIM_LEADING|TRIM_LEADING_AND_TRAILING|TRIM_REDUNDANT|TRIM_TRAILING|TRUE|True|true|VOID|WARN)\b/,variable:/\b(?:errno|exit_status|PslDebug)\b/,builtin:{pattern:/\b(?:acos|add_diary|annotate|annotate_get|asctime|asin|atan|atexit|ascii_to_ebcdic|batch_set|blackout|cat|ceil|chan_exists|change_state|close|code_cvt|cond_signal|cond_wait|console_type|convert_base|convert_date|convert_locale_date|cos|cosh|create|destroy_lock|dump_hist|date|destroy|difference|dget_text|dcget_text|ebcdic_to_ascii|encrypt|event_archive|event_catalog_get|event_check|event_query|event_range_manage|event_range_query|event_report|event_schedule|event_trigger|event_trigger2|execute|exists|exp|fabs|floor|fmod|full_discovery|file|fopen|ftell|fseek|grep|get_vars|getenv|get|get_chan_info|get_ranges|get_text|gethostinfo|getpid|getpname|history_get_retention|history|index|int|is_var|intersection|isnumber|internal|in_transition|join|kill|length|lines|lock|lock_info|log|loge|log10|matchline|msg_check|msg_get_format|msg_get_severity|msg_printf|msg_sprintf|ntharg|num_consoles|nthargf|nthline|nthlinef|num_bytes|print|proc_exists|process|popen|printf|pconfig|poplines|pow|PslExecute|PslFunctionCall|PslFunctionExists|PslSetOptions|random|read|readln|refresh_parameters|remote_check|remote_close|remote_event_query|remote_event_trigger|remote_file_send|remote_open|remove|replace|rindex|sec_check_priv|sec_store_get|sec_store_set|set_alarm_ranges|set_locale|share|sin|sinh|sleep|sopen|sqrt|srandom|subset|set|substr|system|sprintf|sort|snmp_agent_config|_snmp_debug|snmp_agent_stop|snmp_agent_start|snmp_h_set|snmp_h_get_next|snmp_h_get|snmp_set|snmp_walk|snmp_get_next|snmp_get|snmp_config|snmp_close|snmp_open|snmp_trap_receive|snmp_trap_ignore|snmp_trap_listen|snmp_trap_send|snmp_trap_raise_std_trap|snmp_trap_register_im|splitline|strcasecmp|str_repeat|trim|tail|tan|tanh|time|tmpnam|tolower|toupper|trace_psl_process|text_domain|unlock|unique|union|unset|va_arg|va_start|write)\b/,alias:"builtin-function"},"foreach-variable":{pattern:/(\bforeach\s+(?:(?:\w+\b|"(?:\\.|[^\\"])*")\s+){0,2})[_a-zA-Z]\w*(?=\s*\()/,lookbehind:!0,greedy:!0},function:{pattern:/\b[_a-z]\w*\b(?=\s*\()/i},number:/\b(?:0x[0-9a-f]+|[0-9]+(?:\.[0-9]+)?)\b/i,operator:/--|\+\+|&&=?|\|\|=?|<<=?|>>=?|[=!]~|[-+*/%&|^!=<>]=?|\.|[:?]/,punctuation:/[(){}\[\];,]/}}e.exports=t,t.displayName="psl",t.aliases=[]},82161(e){"use strict";function t(e){!function(e){e.languages.pug={comment:{pattern:/(^([\t ]*))\/\/.*(?:(?:\r?\n|\r)\2[\t ].+)*/m,lookbehind:!0},"multiline-script":{pattern:/(^([\t ]*)script\b.*\.[\t ]*)(?:(?:\r?\n|\r(?!\n))(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0,inside:e.languages.javascript},filter:{pattern:/(^([\t ]*)):.+(?:(?:\r?\n|\r(?!\n))(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"}}},"multiline-plain-text":{pattern:/(^([\t ]*)[\w\-#.]+\.[\t ]*)(?:(?:\r?\n|\r(?!\n))(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/m,lookbehind:!0},markup:{pattern:/(^[\t ]*)<.+/m,lookbehind:!0,inside:e.languages.markup},doctype:{pattern:/((?:^|\n)[\t ]*)doctype(?: .+)?/,lookbehind:!0},"flow-control":{pattern:/(^[\t ]*)(?:if|unless|else|case|when|default|each|while)\b(?: .+)?/m,lookbehind:!0,inside:{each:{pattern:/^each .+? in\b/,inside:{keyword:/\b(?:each|in)\b/,punctuation:/,/}},branch:{pattern:/^(?:if|unless|else|case|when|default|while)\b/,alias:"keyword"},rest:e.languages.javascript}},keyword:{pattern:/(^[\t ]*)(?:block|extends|include|append|prepend)\b.+/m,lookbehind:!0},mixin:[{pattern:/(^[\t ]*)mixin .+/m,lookbehind:!0,inside:{keyword:/^mixin/,function:/\w+(?=\s*\(|\s*$)/,punctuation:/[(),.]/}},{pattern:/(^[\t ]*)\+.+/m,lookbehind:!0,inside:{name:{pattern:/^\+\w+/,alias:"function"},rest:e.languages.javascript}}],script:{pattern:/(^[\t ]*script(?:(?:&[^(]+)?\([^)]+\))*[\t ]).+/m,lookbehind:!0,inside:e.languages.javascript},"plain-text":{pattern:/(^[\t ]*(?!-)[\w\-#.]*[\w\-](?:(?:&[^(]+)?\([^)]+\))*\/?[\t ]).+/m,lookbehind:!0},tag:{pattern:/(^[\t ]*)(?!-)[\w\-#.]*[\w\-](?:(?:&[^(]+)?\([^)]+\))*\/?:?/m,lookbehind:!0,inside:{attributes:[{pattern:/&[^(]+\([^)]+\)/,inside:e.languages.javascript},{pattern:/\([^)]+\)/,inside:{"attr-value":{pattern:/(=\s*(?!\s))(?:\{[^}]*\}|[^,)\r\n]+)/,lookbehind:!0,inside:e.languages.javascript},"attr-name":/[\w-]+(?=\s*!?=|\s*[,)])/,punctuation:/[!=(),]+/}}],punctuation:/:/,"attr-id":/#[\w\-]+/,"attr-class":/\.[\w\-]+/}},code:[{pattern:/(^[\t ]*(?:-|!?=)).+/m,lookbehind:!0,inside:e.languages.javascript}],punctuation:/[.\-!=|]+/};for(var t=/(^([\t ]*)):(?:(?:\r?\n|\r(?!\n))(?:\2[\t ].+|\s*?(?=\r?\n|\r)))+/.source,n=[{filter:"atpl",language:"twig"},{filter:"coffee",language:"coffeescript"},"ejs","handlebars","less","livescript","markdown",{filter:"sass",language:"scss"},"stylus"],r={},i=0,a=n.length;i",function(){return o.filter}),"m"),lookbehind:!0,inside:{"filter-name":{pattern:/^:[\w-]+/,alias:"variable"},rest:e.languages[o.language]}})}e.languages.insertBefore("pug","filter",r)}(e)}e.exports=t,t.displayName="pug",t.aliases=[]},80625(e){"use strict";function t(e){var t,n;(t=e).languages.puppet={heredoc:[{pattern:/(@\("([^"\r\n\/):]+)"(?:\/[nrts$uL]*)?\).*(?:\r?\n|\r))(?:.*(?:\r?\n|\r(?!\n)))*?[ \t]*(?:\|[ \t]*)?(?:-[ \t]*)?\2/,lookbehind:!0,alias:"string",inside:{punctuation:/(?=\S).*\S(?= *$)/}},{pattern:/(@\(([^"\r\n\/):]+)(?:\/[nrts$uL]*)?\).*(?:\r?\n|\r))(?:.*(?:\r?\n|\r(?!\n)))*?[ \t]*(?:\|[ \t]*)?(?:-[ \t]*)?\2/,lookbehind:!0,greedy:!0,alias:"string",inside:{punctuation:/(?=\S).*\S(?= *$)/}},{pattern:/@\("?(?:[^"\r\n\/):]+)"?(?:\/[nrts$uL]*)?\)/,alias:"string",inside:{punctuation:{pattern:/(\().+?(?=\))/,lookbehind:!0}}}],"multiline-comment":{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0,greedy:!0,alias:"comment"},regex:{pattern:/((?:\bnode\s+|[~=\(\[\{,]\s*|[=+]>\s*|^\s*))\/(?:[^\/\\]|\\[\s\S])+\/(?:[imx]+\b|\B)/,lookbehind:!0,greedy:!0,inside:{"extended-regex":{pattern:/^\/(?:[^\/\\]|\\[\s\S])+\/[im]*x[im]*$/,inside:{comment:/#.*/}}}},comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},string:{pattern:/(["'])(?:\$\{(?:[^'"}]|(["'])(?:(?!\2)[^\\]|\\[\s\S])*\2)+\}|\$(?!\{)|(?!\1)[^\\$]|\\[\s\S])*\1/,greedy:!0,inside:{"double-quoted":{pattern:/^"[\s\S]*"$/,inside:{}}}},variable:{pattern:/\$(?:::)?\w+(?:::\w+)*/,inside:{punctuation:/::/}},"attr-name":/(?:\b\w+|\*)(?=\s*=>)/,function:[{pattern:/(\.)(?!\d)\w+/,lookbehind:!0},/\b(?:contain|debug|err|fail|include|info|notice|realize|require|tag|warning)\b|\b(?!\d)\w+(?=\()/],number:/\b(?:0x[a-f\d]+|\d+(?:\.\d+)?(?:e-?\d+)?)\b/i,boolean:/\b(?:true|false)\b/,keyword:/\b(?:application|attr|case|class|consumes|default|define|else|elsif|function|if|import|inherits|node|private|produces|type|undef|unless)\b/,datatype:{pattern:/\b(?:Any|Array|Boolean|Callable|Catalogentry|Class|Collection|Data|Default|Enum|Float|Hash|Integer|NotUndef|Numeric|Optional|Pattern|Regexp|Resource|Runtime|Scalar|String|Struct|Tuple|Type|Undef|Variant)\b/,alias:"symbol"},operator:/=[=~>]?|![=~]?|<(?:<\|?|[=~|-])?|>[>=]?|->?|~>|\|>?>?|[*\/%+?]|\b(?:and|in|or)\b/,punctuation:/[\[\]{}().,;]|:+/},n=[{pattern:/(^|[^\\])\$\{(?:[^'"{}]|\{[^}]*\}|(["'])(?:(?!\2)[^\\]|\\[\s\S])*\2)+\}/,lookbehind:!0,inside:{"short-variable":{pattern:/(^\$\{)(?!\w+\()(?:::)?\w+(?:::\w+)*/,lookbehind:!0,alias:"variable",inside:{punctuation:/::/}},delimiter:{pattern:/^\$/,alias:"variable"},rest:t.languages.puppet}},{pattern:/(^|[^\\])\$(?:::)?\w+(?:::\w+)*/,lookbehind:!0,alias:"variable",inside:{punctuation:/::/}}],t.languages.puppet.heredoc[0].inside.interpolation=n,t.languages.puppet.string.inside["double-quoted"].inside.interpolation=n}e.exports=t,t.displayName="puppet",t.aliases=[]},88393(e){"use strict";function t(e){var t,n,r;(t=e).languages.pure={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0},/#!.+/],"inline-lang":{pattern:/%<[\s\S]+?%>/,greedy:!0,inside:{lang:{pattern:/(^%< *)-\*-.+?-\*-/,lookbehind:!0,alias:"comment"},delimiter:{pattern:/^%<.*|%>$/,alias:"punctuation"}}},string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},number:{pattern:/((?:\.\.)?)(?:\b(?:inf|nan)\b|\b0x[\da-f]+|(?:\b(?:0b)?\d+(?:\.\d+)?|\B\.\d+)(?:e[+-]?\d+)?L?)/i,lookbehind:!0},keyword:/\b(?:ans|break|bt|case|catch|cd|clear|const|def|del|dump|else|end|exit|extern|false|force|help|if|infix[lr]?|interface|let|ls|mem|namespace|nonfix|NULL|of|otherwise|outfix|override|postfix|prefix|private|public|pwd|quit|run|save|show|stats|then|throw|trace|true|type|underride|using|when|with)\b/,function:/\b(?:abs|add_(?:(?:fundef|interface|macdef|typedef)(?:_at)?|addr|constdef|vardef)|all|any|applp?|arity|bigintp?|blob(?:_crc|_size|p)?|boolp?|byte_(?:matrix|pointer)|byte_c?string(?:_pointer)?|calloc|cat|catmap|ceil|char[ps]?|check_ptrtag|chr|clear_sentry|clearsym|closurep?|cmatrixp?|cols?|colcat(?:map)?|colmap|colrev|colvector(?:p|seq)?|complex(?:_float_(?:matrix|pointer)|_matrix(?:_view)?|_pointer|p)?|conj|cookedp?|cst|cstring(?:_(?:dup|list|vector))?|curry3?|cyclen?|del_(?:constdef|fundef|interface|macdef|typedef|vardef)|delete|diag(?:mat)?|dim|dmatrixp?|do|double(?:_matrix(?:_view)?|_pointer|p)?|dowith3?|drop|dropwhile|eval(?:cmd)?|exactp|filter|fix|fixity|flip|float(?:_matrix|_pointer)|floor|fold[lr]1?|frac|free|funp?|functionp?|gcd|get(?:_(?:byte|constdef|double|float|fundef|int(?:64)?|interface(?:_typedef)?|long|macdef|pointer|ptrtag|short|sentry|string|typedef|vardef))?|globsym|hash|head|id|im|imatrixp?|index|inexactp|infp|init|insert|int(?:_matrix(?:_view)?|_pointer|p)?|int64_(?:matrix|pointer)|integerp?|iteraten?|iterwhile|join|keys?|lambdap?|last(?:err(?:pos)?)?|lcd|list[2p]?|listmap|make_ptrtag|malloc|map|matcat|matrixp?|max|member|min|nanp|nargs|nmatrixp?|null|numberp?|ord|pack(?:ed)?|pointer(?:_cast|_tag|_type|p)?|pow|pred|ptrtag|put(?:_(?:byte|double|float|int(?:64)?|long|pointer|short|string))?|rationalp?|re|realp?|realloc|recordp?|redim|reduce(?:_with)?|refp?|repeatn?|reverse|rlistp?|round|rows?|rowcat(?:map)?|rowmap|rowrev|rowvector(?:p|seq)?|same|scan[lr]1?|sentry|sgn|short_(?:matrix|pointer)|slice|smatrixp?|sort|split|str|strcat|stream|stride|string(?:_(?:dup|list|vector)|p)?|subdiag(?:mat)?|submat|subseq2?|substr|succ|supdiag(?:mat)?|symbolp?|tail|take|takewhile|thunkp?|transpose|trunc|tuplep?|typep|ubyte|uint(?:64)?|ulong|uncurry3?|unref|unzip3?|update|ushort|vals?|varp?|vector(?:p|seq)?|void|zip3?|zipwith3?)\b/,special:{pattern:/\b__[a-z]+__\b/i,alias:"builtin"},operator:/(?:[!"#$%&'*+,\-.\/:<=>?@\\^`|~\u00a1-\u00bf\u00d7-\u00f7\u20d0-\u2bff]|\b_+\b)+|\b(?:and|div|mod|not|or)\b/,punctuation:/[(){}\[\];,|]/},r=/%< *-\*- *\d* *-\*-[\s\S]+?%>/.source,(n=["c",{lang:"c++",alias:"cpp"},"fortran"]).forEach(function(e){var n=e;if("string"!=typeof e&&(n=e.alias,e=e.lang),t.languages[n]){var i={};i["inline-lang-"+n]={pattern:RegExp(r.replace("",e.replace(/([.+*?\/\\(){}\[\]])/g,"\\$1")),"i"),inside:t.util.clone(t.languages.pure["inline-lang"].inside)},i["inline-lang-"+n].inside.rest=t.util.clone(t.languages[n]),t.languages.insertBefore("pure","inline-lang",i)}}),t.languages.c&&(t.languages.pure["inline-lang"].inside.rest=t.util.clone(t.languages.c))}e.exports=t,t.displayName="pure",t.aliases=[]},78404(e){"use strict";function t(e){e.languages.purebasic=e.languages.extend("clike",{comment:/;.*/,keyword:/\b(?:declarecdll|declaredll|compilerselect|compilercase|compilerdefault|compilerendselect|compilererror|enableexplicit|disableexplicit|not|and|or|xor|calldebugger|debuglevel|enabledebugger|disabledebugger|restore|read|includepath|includebinary|threaded|runtime|with|endwith|structureunion|endstructureunion|align|newlist|newmap|interface|endinterface|extends|enumeration|endenumeration|swap|foreach|continue|fakereturn|goto|gosub|return|break|module|endmodule|declaremodule|enddeclaremodule|declare|declarec|prototype|prototypec|enableasm|disableasm|dim|redim|data|datasection|enddatasection|to|procedurereturn|debug|default|case|select|endselect|as|import|endimport|importc|compilerif|compilerelse|compilerendif|compilerelseif|end|structure|endstructure|while|wend|for|next|step|if|else|elseif|endif|repeat|until|procedure|proceduredll|procedurec|procedurecdll|endprocedure|protected|shared|static|global|define|includefile|xincludefile|macro|endmacro)\b/i,function:/\b\w+(?:\.\w+)?\s*(?=\()/,number:/(?:\$[\da-f]+|\b-?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)\b/i,operator:/(?:@\*?|\?|\*)\w+|-[>-]?|\+\+?|!=?|<>?=?|==?|&&?|\|?\||[~^%?*/@]/}),e.languages.insertBefore("purebasic","keyword",{tag:/#\w+/,asm:{pattern:/(^[\t ]*)!.*/m,lookbehind:!0,alias:"tag",inside:{comment:/;.*/,string:{pattern:/(["'`])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},"label-reference-anonymous":{pattern:/(!\s*j[a-z]+\s+)@[fb]/i,lookbehind:!0,alias:"fasm-label"},"label-reference-addressed":{pattern:/(!\s*j[a-z]+\s+)[A-Z._?$@][\w.?$@~#]*/i,lookbehind:!0,alias:"fasm-label"},function:{pattern:/^([\t ]*!\s*)[\da-z]+(?=\s|$)/im,lookbehind:!0},"function-inline":{pattern:/(:\s*)[\da-z]+(?=\s)/i,lookbehind:!0,alias:"function"},label:{pattern:/^([\t ]*!\s*)[A-Za-z._?$@][\w.?$@~#]*(?=:)/m,lookbehind:!0,alias:"fasm-label"},keyword:[/\b(?:extern|global)\b[^;\r\n]*/i,/\b(?:CPU|FLOAT|DEFAULT)\b.*/],register:/\b(?:st\d|[xyz]mm\d\d?|[cdt]r\d|r\d\d?[bwd]?|[er]?[abcd]x|[abcd][hl]|[er]?(?:bp|sp|si|di)|[cdefgs]s|mm\d+)\b/i,number:/(?:\b|-|(?=\$))(?:0[hx](?:[\da-f]*\.)?[\da-f]+(?:p[+-]?\d+)?|\d[\da-f]+[hx]|\$\d[\da-f]*|0[oq][0-7]+|[0-7]+[oq]|0[by][01]+|[01]+[by]|0[dt]\d+|(?:\d+(?:\.\d+)?|\.\d+)(?:\.?e[+-]?\d+)?[dt]?)\b/i,operator:/[\[\]*+\-/%<>=&|$!,.:]/}}}),delete e.languages.purebasic["class-name"],delete e.languages.purebasic.boolean,e.languages.pbfasm=e.languages.purebasic}e.exports=t,t.displayName="purebasic",t.aliases=[]},92923(e,t,n){"use strict";var r=n(58090);function i(e){e.register(r),e.languages.purescript=e.languages.extend("haskell",{keyword:/\b(?:ado|case|class|data|derive|do|else|forall|if|in|infixl|infixr|instance|let|module|newtype|of|primitive|then|type|where)\b/,"import-statement":{pattern:/(^[\t ]*)import\s+[A-Z][\w']*(?:\.[A-Z][\w']*)*(?:\s+as\s+[A-Z][\w']*(?:\.[A-Z][\w']*)*)?(?:\s+hiding\b)?/m,lookbehind:!0,inside:{keyword:/\b(?:import|as|hiding)\b/}},builtin:/\b(?:absurd|add|ap|append|apply|between|bind|bottom|clamp|compare|comparing|compose|conj|const|degree|discard|disj|div|eq|flap|flip|gcd|identity|ifM|join|lcm|liftA1|liftM1|map|max|mempty|min|mod|mul|negate|not|notEq|one|otherwise|recip|show|sub|top|unit|unless|unlessM|void|when|whenM|zero)\b/}),e.languages.purs=e.languages.purescript}e.exports=i,i.displayName="purescript",i.aliases=["purs"]},52992(e){"use strict";function t(e){e.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},"string-interpolation":{pattern:/(?:f|rf|fr)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|rb|br)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/im,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:True|False|None)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},e.languages.python["string-interpolation"].inside.interpolation.inside.rest=e.languages.python,e.languages.py=e.languages.python}e.exports=t,t.displayName="python",t.aliases=["py"]},55762(e){"use strict";function t(e){e.languages.q={string:/"(?:\\.|[^"\\\r\n])*"/,comment:[{pattern:/([\t )\]}])\/.*/,lookbehind:!0,greedy:!0},{pattern:/(^|\r?\n|\r)\/[\t ]*(?:(?:\r?\n|\r)(?:.*(?:\r?\n|\r(?!\n)))*?(?:\\(?=[\t ]*(?:\r?\n|\r))|$)|\S.*)/,lookbehind:!0,greedy:!0},{pattern:/^\\[\t ]*(?:\r?\n|\r)[\s\S]+/m,greedy:!0},{pattern:/^#!.+/m,greedy:!0}],symbol:/`(?::\S+|[\w.]*)/,datetime:{pattern:/0N[mdzuvt]|0W[dtz]|\d{4}\.\d\d(?:m|\.\d\d(?:T(?:\d\d(?::\d\d(?::\d\d(?:[.:]\d\d\d)?)?)?)?)?[dz]?)|\d\d:\d\d(?::\d\d(?:[.:]\d\d\d)?)?[uvt]?/,alias:"number"},number:/\b(?![01]:)(?:0[wn]|0W[hj]?|0N[hje]?|0x[\da-fA-F]+|\d+(?:\.\d*)?(?:e[+-]?\d+)?[hjfeb]?)/,keyword:/\\\w+\b|\b(?:abs|acos|aj0?|all|and|any|asc|asin|asof|atan|attr|avgs?|binr?|by|ceiling|cols|cor|cos|count|cov|cross|csv|cut|delete|deltas|desc|dev|differ|distinct|div|do|dsave|ej|enlist|eval|except|exec|exit|exp|fby|fills|first|fkeys|flip|floor|from|get|getenv|group|gtime|hclose|hcount|hdel|hopen|hsym|iasc|identity|idesc|if|ij|in|insert|inter|inv|keys?|last|like|list|ljf?|load|log|lower|lsq|ltime|ltrim|mavg|maxs?|mcount|md5|mdev|med|meta|mins?|mmax|mmin|mmu|mod|msum|neg|next|not|null|or|over|parse|peach|pj|plist|prds?|prev|prior|rand|rank|ratios|raze|read0|read1|reciprocal|reval|reverse|rload|rotate|rsave|rtrim|save|scan|scov|sdev|select|set|setenv|show|signum|sin|sqrt|ssr?|string|sublist|sums?|sv|svar|system|tables|tan|til|trim|txf|type|uj|ungroup|union|update|upper|upsert|value|var|views?|vs|wavg|where|while|within|wj1?|wsum|ww|xasc|xbar|xcols?|xdesc|xexp|xgroup|xkey|xlog|xprev|xrank)\b/,adverb:{pattern:/['\/\\]:?|\beach\b/,alias:"function"},verb:{pattern:/(?:\B\.\B|\b[01]:|<[=>]?|>=?|[:+\-*%,!?~=|$&#@^]):?|\b_\b:?/,alias:"operator"},punctuation:/[(){}\[\];.]/}}e.exports=t,t.displayName="q",t.aliases=[]},4137(e){"use strict";function t(e){!function(e){for(var t=/"(?:\\.|[^\\"\r\n])*"|'(?:\\.|[^\\'\r\n])*'/.source,n=/\/\/.*(?!.)|\/\*(?:[^*]|\*(?!\/))*\*\//.source,r=/(?:[^\\()[\]{}"'/]||\/(?![*/])||\(*\)|\[*\]|\{*\}|\\[\s\S])/.source.replace(//g,function(){return t}).replace(//g,function(){return n}),i=0;i<2;i++)r=r.replace(//g,function(){return r});r=r.replace(//g,"[^\\s\\S]"),e.languages.qml={comment:{pattern:/\/\/.*|\/\*[\s\S]*?\*\//,greedy:!0},"javascript-function":{pattern:RegExp(/((?:^|;)[ \t]*)function\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*\(*\)\s*\{*\}/.source.replace(//g,function(){return r}),"m"),lookbehind:!0,greedy:!0,alias:"language-javascript",inside:e.languages.javascript},"class-name":{pattern:/((?:^|[:;])[ \t]*)(?!\d)\w+(?=[ \t]*\{|[ \t]+on\b)/m,lookbehind:!0},property:[{pattern:/((?:^|[;{])[ \t]*)(?!\d)\w+(?:\.\w+)*(?=[ \t]*:)/m,lookbehind:!0},{pattern:/((?:^|[;{])[ \t]*)property[ \t]+(?!\d)\w+(?:\.\w+)*[ \t]+(?!\d)\w+(?:\.\w+)*(?=[ \t]*:)/m,lookbehind:!0,inside:{keyword:/^property/,property:/\w+(?:\.\w+)*/}}],"javascript-expression":{pattern:RegExp(/(:[ \t]*)(?![\s;}[])(?:(?!$|[;}]))+/.source.replace(//g,function(){return r}),"m"),lookbehind:!0,greedy:!0,alias:"language-javascript",inside:e.languages.javascript},string:/"(?:\\.|[^\\"\r\n])*"/,keyword:/\b(?:as|import|on)\b/,punctuation:/[{}[\]:;,]/}}(e)}e.exports=t,t.displayName="qml",t.aliases=[]},28260(e){"use strict";function t(e){e.languages.qore=e.languages.extend("clike",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:\/\/|#).*)/,lookbehind:!0},string:{pattern:/("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/,greedy:!0},keyword:/\b(?:abstract|any|assert|binary|bool|boolean|break|byte|case|catch|char|class|code|const|continue|data|default|do|double|else|enum|extends|final|finally|float|for|goto|hash|if|implements|import|inherits|instanceof|int|interface|long|my|native|new|nothing|null|object|our|own|private|reference|rethrow|return|short|soft(?:int|float|number|bool|string|date|list)|static|strictfp|string|sub|super|switch|synchronized|this|throw|throws|transient|try|void|volatile|while)\b/,boolean:/\b(?:true|false)\b/i,function:/\$?\b(?!\d)\w+(?=\()/,number:/\b(?:0b[01]+|0x(?:[\da-f]*\.)?[\da-fp\-]+|(?:\d+(?:\.\d+)?|\.\d+)(?:e\d+)?[df]|(?:\d+(?:\.\d+)?|\.\d+))\b/i,operator:{pattern:/(^|[^.])(?:\+[+=]?|-[-=]?|[!=](?:==?|~)?|>>?=?|<(?:=>?|<=?)?|&[&=]?|\|[|=]?|[*\/%^]=?|[~?])/,lookbehind:!0},variable:/\$(?!\d)\w+\b/})}e.exports=t,t.displayName="qore",t.aliases=[]},71360(e){"use strict";function t(e){!function(e){function t(e,t){return e.replace(/<<(\d+)>>/g,function(e,n){return"(?:"+t[+n]+")"})}function n(e,n,r){return RegExp(t(e,n),r||"")}function r(e,t){for(var n=0;n>/g,function(){return"(?:"+e+")"});return e.replace(/<>/g,"[^\\s\\S]")}var i={type:"Adj BigInt Bool Ctl Double false Int One Pauli PauliI PauliX PauliY PauliZ Qubit Range Result String true Unit Zero",other:"Adjoint adjoint apply as auto body borrow borrowing Controlled controlled distribute elif else fail fixup for function if in internal intrinsic invert is let mutable namespace new newtype open operation repeat return self set until use using while within"};function a(e){return"\\b(?:"+e.trim().replace(/ /g,"|")+")\\b"}var o=RegExp(a(i.type+" "+i.other)),s=/\b[A-Za-z_]\w*\b/.source,u=t(/<<0>>(?:\s*\.\s*<<0>>)*/.source,[s]),c={keyword:o,punctuation:/[<>()?,.:[\]]/},l=/"(?:\\.|[^\\"])*"/.source;e.languages.qsharp=e.languages.extend("clike",{comment:/\/\/.*/,string:[{pattern:n(/(^|[^$\\])<<0>>/.source,[l]),lookbehind:!0,greedy:!0}],"class-name":[{pattern:n(/(\b(?:as|open)\s+)<<0>>(?=\s*(?:;|as\b))/.source,[u]),lookbehind:!0,inside:c},{pattern:n(/(\bnamespace\s+)<<0>>(?=\s*\{)/.source,[u]),lookbehind:!0,inside:c}],keyword:o,number:/(?:\b0(?:x[\da-f]+|b[01]+|o[0-7]+)|(?:\B\.\d+|\b\d+(?:\.\d*)?)(?:e[-+]?\d+)?)l?\b/i,operator:/\band=|\bor=|\band\b|\bor\b|\bnot\b|<[-=]|[-=]>|>>>=?|<<<=?|\^\^\^=?|\|\|\|=?|&&&=?|w\/=?|~~~|[*\/+\-^=!%]=?/,punctuation:/::|[{}[\];(),.:]/}),e.languages.insertBefore("qsharp","number",{range:{pattern:/\.\./,alias:"operator"}});var f=r(t(/\{(?:[^"{}]|<<0>>|<>)*\}/.source,[l]),2);e.languages.insertBefore("qsharp","string",{"interpolation-string":{pattern:n(/\$"(?:\\.|<<0>>|[^\\"{])*"/.source,[f]),greedy:!0,inside:{interpolation:{pattern:n(/((?:^|[^\\])(?:\\\\)*)<<0>>/.source,[f]),lookbehind:!0,inside:{punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-qsharp",inside:e.languages.qsharp}}},string:/[\s\S]+/}}})}(e),e.languages.qs=e.languages.qsharp}e.exports=t,t.displayName="qsharp",t.aliases=["qs"]},29308(e){"use strict";function t(e){e.languages.r={comment:/#.*/,string:{pattern:/(['"])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},"percent-operator":{pattern:/%[^%\s]*%/,alias:"operator"},boolean:/\b(?:TRUE|FALSE)\b/,ellipsis:/\.\.(?:\.|\d+)/,number:[/\b(?:NaN|Inf)\b/,/(?:\b0x[\dA-Fa-f]+(?:\.\d*)?|\b\d+(?:\.\d*)?|\B\.\d+)(?:[EePp][+-]?\d+)?[iL]?/],keyword:/\b(?:if|else|repeat|while|function|for|in|next|break|NULL|NA|NA_integer_|NA_real_|NA_complex_|NA_character_)\b/,operator:/->?>?|<(?:=|=!]=?|::?|&&?|\|\|?|[+*\/^$@~]/,punctuation:/[(){}\[\],;]/}}e.exports=t,t.displayName="r",t.aliases=[]},32168(e,t,n){"use strict";var r=n(9997);function i(e){e.register(r),e.languages.racket=e.languages.extend("scheme",{"lambda-parameter":{pattern:/([(\[]lambda\s+[(\[])[^()\[\]'\s]+/,lookbehind:!0}}),e.languages.insertBefore("racket","string",{lang:{pattern:/^#lang.+/m,greedy:!0,alias:"keyword"}}),e.languages.rkt=e.languages.racket}e.exports=i,i.displayName="racket",i.aliases=["rkt"]},5755(e){"use strict";function t(e){e.languages.reason=e.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:mod|land|lor|lxor|lsl|lsr|asr)\b/}),e.languages.insertBefore("reason","class-name",{character:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,alias:"string"},constructor:{pattern:/\b[A-Z]\w*\b(?!\s*\.)/,alias:"variable"},label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete e.languages.reason.function}e.exports=t,t.displayName="reason",t.aliases=[]},54105(e){"use strict";function t(e){var t,n,r,i,a,o,s,u;t=e,n={pattern:/\\[\\(){}[\]^$+*?|.]/,alias:"escape"},i={pattern:/\.|\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},a={pattern:/\\[wsd]|\\p\{[^{}]+\}/i,alias:"class-name"},s=RegExp((o="(?:[^\\\\-]|"+(r=/\\(?:x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]+\}|c[a-zA-Z]|0[0-7]{0,2}|[123][0-7]{2}|.)/).source+")")+"-"+o),u={pattern:/(<|')[^<>']+(?=[>']$)/,lookbehind:!0,alias:"variable"},t.languages.regex={charset:{pattern:/((?:^|[^\\])(?:\\\\)*)\[(?:[^\\\]]|\\[\s\S])*\]/,lookbehind:!0,inside:{"charset-negation":{pattern:/(^\[)\^/,lookbehind:!0,alias:"operator"},"charset-punctuation":{pattern:/^\[|\]$/,alias:"punctuation"},range:{pattern:s,inside:{escape:r,"range-punctuation":{pattern:/-/,alias:"operator"}}},"special-escape":n,charclass:a,escape:r}},"special-escape":n,charclass:i,backreference:[{pattern:/\\(?![123][0-7]{2})[1-9]/,alias:"keyword"},{pattern:/\\k<[^<>']+>/,alias:"keyword",inside:{"group-name":u}}],anchor:{pattern:/[$^]|\\[ABbGZz]/,alias:"function"},escape:r,group:[{pattern:/\((?:\?(?:<[^<>']+>|'[^<>']+'|[>:]|:=]=?|!=|\b_\b/,punctuation:/[,;.\[\]{}()]/}}e.exports=t,t.displayName="rego",t.aliases=[]},35108(e){"use strict";function t(e){e.languages.renpy={comment:{pattern:/(^|[^\\])#.+/,lookbehind:!0},string:{pattern:/("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2|(?:^#?(?:[0-9a-fA-F]{6}|(?:[0-9a-fA-F]){3})$)/m,greedy:!0},function:/\b[a-z_]\w*(?=\()/i,property:/\b(?:insensitive|idle|hover|selected_idle|selected_hover|background|position|alt|xpos|ypos|pos|xanchor|yanchor|anchor|xalign|yalign|align|xcenter|ycenter|xofsset|yoffset|ymaximum|maximum|xmaximum|xminimum|yminimum|minimum|xsize|ysizexysize|xfill|yfill|area|antialias|black_color|bold|caret|color|first_indent|font|size|italic|justify|kerning|language|layout|line_leading|line_overlap_split|line_spacing|min_width|newline_indent|outlines|rest_indent|ruby_style|slow_cps|slow_cps_multiplier|strikethrough|text_align|underline|hyperlink_functions|vertical|hinting|foreground|left_margin|xmargin|top_margin|bottom_margin|ymargin|left_padding|right_padding|xpadding|top_padding|bottom_padding|ypadding|size_group|child|hover_sound|activate_sound|mouse|focus_mask|keyboard_focus|bar_vertical|bar_invert|bar_resizing|left_gutter|right_gutter|top_gutter|bottom_gutter|left_bar|right_bar|top_bar|bottom_bar|thumb|thumb_shadow|thumb_offset|unscrollable|spacing|first_spacing|box_reverse|box_wrap|order_reverse|fit_first|ysize|thumbnail_width|thumbnail_height|help|text_ypos|text_xpos|idle_color|hover_color|selected_idle_color|selected_hover_color|insensitive_color|alpha|insensitive_background|hover_background|zorder|value|width|xadjustment|xanchoraround|xaround|xinitial|xoffset|xzoom|yadjustment|yanchoraround|yaround|yinitial|yzoom|zoom|ground|height|text_style|text_y_fudge|selected_insensitive|has_sound|has_music|has_voice|focus|hovered|image_style|length|minwidth|mousewheel|offset|prefix|radius|range|right_margin|rotate|rotate_pad|developer|screen_width|screen_height|window_title|name|version|windows_icon|default_fullscreen|default_text_cps|default_afm_time|main_menu_music|sample_sound|enter_sound|exit_sound|save_directory|enter_transition|exit_transition|intra_transition|main_game_transition|game_main_transition|end_splash_transition|end_game_transition|after_load_transition|window_show_transition|window_hide_transition|adv_nvl_transition|nvl_adv_transition|enter_yesno_transition|exit_yesno_transition|enter_replay_transition|exit_replay_transition|say_attribute_transition|directory_name|executable_name|include_update|window_icon|modal|google_play_key|google_play_salt|drag_name|drag_handle|draggable|dragged|droppable|dropped|narrator_menu|action|default_afm_enable|version_name|version_tuple|inside|fadeout|fadein|layers|layer_clipping|linear|scrollbars|side_xpos|side_ypos|side_spacing|edgescroll|drag_joined|drag_raise|drop_shadow|drop_shadow_color|subpixel|easein|easeout|time|crop|auto|update|get_installed_packages|can_update|UpdateVersion|Update|overlay_functions|translations|window_left_padding|show_side_image|show_two_window)\b/,tag:/\b(?:label|image|menu|[hv]box|frame|text|imagemap|imagebutton|bar|vbar|screen|textbutton|buttoscreenn|fixed|grid|input|key|mousearea|side|timer|viewport|window|hotspot|hotbar|self|button|drag|draggroup|tag|mm_menu_frame|nvl|block|parallel)\b|\$/,keyword:/\b(?:as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|yield|adjustment|alignaround|allow|angle|around|box_layout|cache|changed|child_size|clicked|clipping|corner1|corner2|default|delay|exclude|scope|slow|slow_abortable|slow_done|sound|style_group|substitute|suffix|transform_anchor|transpose|unhovered|config|theme|mm_root|gm_root|rounded_window|build|disabled_text|disabled|widget_selected|widget_text|widget_hover|widget|updater|behind|call|expression|hide|init|jump|onlayer|python|renpy|scene|set|show|transform|play|queue|stop|pause|define|window|repeat|contains|choice|on|function|event|animation|clockwise|counterclockwise|circles|knot|null|None|random|has|add|use|fade|dissolve|style|store|id|voice|center|left|right|less_rounded|music|movie|clear|persistent|ui)\b/,boolean:/\b(?:[Tt]rue|[Ff]alse)\b/,number:/(?:\b(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*(?:\.\d*)?)|\B\.\d+)(?:e[+-]?\d+)?j?/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not|with|at)\b/,punctuation:/[{}[\];(),.:]/},e.languages.rpy=e.languages.renpy}e.exports=t,t.displayName="renpy",t.aliases=["rpy"]},46678(e){"use strict";function t(e){e.languages.rest={table:[{pattern:/(^[\t ]*)(?:\+[=-]+)+\+(?:\r?\n|\r)(?:\1[+|].+[+|](?:\r?\n|\r))+\1(?:\+[=-]+)+\+/m,lookbehind:!0,inside:{punctuation:/\||(?:\+[=-]+)+\+/}},{pattern:/(^[\t ]*)=+ [ =]*=(?:(?:\r?\n|\r)\1.+)+(?:\r?\n|\r)\1=+ [ =]*=(?=(?:\r?\n|\r){2}|\s*$)/m,lookbehind:!0,inside:{punctuation:/[=-]+/}}],"substitution-def":{pattern:/(^[\t ]*\.\. )\|(?:[^|\s](?:[^|]*[^|\s])?)\| [^:]+::/m,lookbehind:!0,inside:{substitution:{pattern:/^\|(?:[^|\s]|[^|\s][^|]*[^|\s])\|/,alias:"attr-value",inside:{punctuation:/^\||\|$/}},directive:{pattern:/( )(?! )[^:]+::/,lookbehind:!0,alias:"function",inside:{punctuation:/::$/}}}},"link-target":[{pattern:/(^[\t ]*\.\. )\[[^\]]+\]/m,lookbehind:!0,alias:"string",inside:{punctuation:/^\[|\]$/}},{pattern:/(^[\t ]*\.\. )_(?:`[^`]+`|(?:[^:\\]|\\.)+):/m,lookbehind:!0,alias:"string",inside:{punctuation:/^_|:$/}}],directive:{pattern:/(^[\t ]*\.\. )[^:]+::/m,lookbehind:!0,alias:"function",inside:{punctuation:/::$/}},comment:{pattern:/(^[\t ]*\.\.)(?:(?: .+)?(?:(?:\r?\n|\r).+)+| .+)(?=(?:\r?\n|\r){2}|$)/m,lookbehind:!0},title:[{pattern:/^(([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\2+)(?:\r?\n|\r).+(?:\r?\n|\r)\1$/m,inside:{punctuation:/^[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]+|[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]+$/,important:/.+/}},{pattern:/(^|(?:\r?\n|\r){2}).+(?:\r?\n|\r)([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\2+(?=\r?\n|\r|$)/,lookbehind:!0,inside:{punctuation:/[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]+$/,important:/.+/}}],hr:{pattern:/((?:\r?\n|\r){2})([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\2{3,}(?=(?:\r?\n|\r){2})/,lookbehind:!0,alias:"punctuation"},field:{pattern:/(^[\t ]*):[^:\r\n]+:(?= )/m,lookbehind:!0,alias:"attr-name"},"command-line-option":{pattern:/(^[\t ]*)(?:[+-][a-z\d]|(?:--|\/)[a-z\d-]+)(?:[ =](?:[a-z][\w-]*|<[^<>]+>))?(?:, (?:[+-][a-z\d]|(?:--|\/)[a-z\d-]+)(?:[ =](?:[a-z][\w-]*|<[^<>]+>))?)*(?=(?:\r?\n|\r)? {2,}\S)/im,lookbehind:!0,alias:"symbol"},"literal-block":{pattern:/::(?:\r?\n|\r){2}([ \t]+)(?![ \t]).+(?:(?:\r?\n|\r)\1.+)*/,inside:{"literal-block-punctuation":{pattern:/^::/,alias:"punctuation"}}},"quoted-literal-block":{pattern:/::(?:\r?\n|\r){2}([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~]).*(?:(?:\r?\n|\r)\1.*)*/,inside:{"literal-block-punctuation":{pattern:/^(?:::|([!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~])\1*)/m,alias:"punctuation"}}},"list-bullet":{pattern:/(^[\t ]*)(?:[*+\-•‣⁃]|\(?(?:\d+|[a-z]|[ivxdclm]+)\)|(?:\d+|[a-z]|[ivxdclm]+)\.)(?= )/im,lookbehind:!0,alias:"punctuation"},"doctest-block":{pattern:/(^[\t ]*)>>> .+(?:(?:\r?\n|\r).+)*/m,lookbehind:!0,inside:{punctuation:/^>>>/}},inline:[{pattern:/(^|[\s\-:\/'"<(\[{])(?::[^:]+:`.*?`|`.*?`:[^:]+:|(\*\*?|``?|\|)(?!\s)(?:(?!\2).)*\S\2(?=[\s\-.,:;!?\\\/'")\]}]|$))/m,lookbehind:!0,inside:{bold:{pattern:/(^\*\*).+(?=\*\*$)/,lookbehind:!0},italic:{pattern:/(^\*).+(?=\*$)/,lookbehind:!0},"inline-literal":{pattern:/(^``).+(?=``$)/,lookbehind:!0,alias:"symbol"},role:{pattern:/^:[^:]+:|:[^:]+:$/,alias:"function",inside:{punctuation:/^:|:$/}},"interpreted-text":{pattern:/(^`).+(?=`$)/,lookbehind:!0,alias:"attr-value"},substitution:{pattern:/(^\|).+(?=\|$)/,lookbehind:!0,alias:"attr-value"},punctuation:/\*\*?|``?|\|/}}],link:[{pattern:/\[[^\[\]]+\]_(?=[\s\-.,:;!?\\\/'")\]}]|$)/,alias:"string",inside:{punctuation:/^\[|\]_$/}},{pattern:/(?:\b[a-z\d]+(?:[_.:+][a-z\d]+)*_?_|`[^`]+`_?_|_`[^`]+`)(?=[\s\-.,:;!?\\\/'")\]}]|$)/i,alias:"string",inside:{punctuation:/^_?`|`$|`?_?_$/}}],punctuation:{pattern:/(^[\t ]*)(?:\|(?= |$)|(?:---?|—|\.\.|__)(?= )|\.\.$)/m,lookbehind:!0}}}e.exports=t,t.displayName="rest",t.aliases=[]},47496(e){"use strict";function t(e){e.languages.rip={comment:/#.*/,keyword:/(?:=>|->)|\b(?:class|if|else|switch|case|return|exit|try|catch|finally|raise)\b/,builtin:/@|\bSystem\b/,boolean:/\b(?:true|false)\b/,date:/\b\d{4}-\d{2}-\d{2}\b/,time:/\b\d{2}:\d{2}:\d{2}\b/,datetime:/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\b/,character:/\B`[^\s`'",.:;#\/\\()<>\[\]{}]\b/,regex:{pattern:/(^|[^/])\/(?!\/)(?:\[[^\n\r\]]*\]|\\.|[^/\\\r\n\[])+\/(?=\s*(?:$|[\r\n,.;})]))/,lookbehind:!0,greedy:!0},symbol:/:[^\d\s`'",.:;#\/\\()<>\[\]{}][^\s`'",.:;#\/\\()<>\[\]{}]*/,string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},number:/[+-]?\b(?:\d+\.\d+|\d+)\b/,punctuation:/(?:\.{2,3})|[`,.:;=\/\\()<>\[\]{}]/,reference:/[^\d\s`'",.:;#\/\\()<>\[\]{}][^\s`'",.:;#\/\\()<>\[\]{}]*/}}e.exports=t,t.displayName="rip",t.aliases=[]},30527(e){"use strict";function t(e){e.languages.roboconf={comment:/#.*/,keyword:{pattern:/(^|\s)(?:(?:facet|instance of)(?=[ \t]+[\w-]+[ \t]*\{)|(?:external|import)\b)/,lookbehind:!0},component:{pattern:/[\w-]+(?=[ \t]*\{)/,alias:"variable"},property:/[\w.-]+(?=[ \t]*:)/,value:{pattern:/(=[ \t]*(?![ \t]))[^,;]+/,lookbehind:!0,alias:"attr-value"},optional:{pattern:/\(optional\)/,alias:"builtin"},wildcard:{pattern:/(\.)\*/,lookbehind:!0,alias:"operator"},punctuation:/[{},.;:=]/}}e.exports=t,t.displayName="roboconf",t.aliases=[]},5261(e){"use strict";function t(e){!function(e){var t={pattern:/(^[ \t]*| {2}|\t)#.*/m,lookbehind:!0,greedy:!0},n={pattern:/((?:^|[^\\])(?:\\{2})*)[$@&%]\{(?:[^{}\r\n]|\{[^{}\r\n]*\})*\}/,lookbehind:!0,inside:{punctuation:/^[$@&%]\{|\}$/}};function r(e,r){var i={};for(var a in i["section-header"]={pattern:/^ ?\*{3}.+?\*{3}/,alias:"keyword"},r)i[a]=r[a];return i.tag={pattern:/([\r\n](?: {2}|\t)[ \t]*)\[[-\w]+\]/,lookbehind:!0,inside:{punctuation:/\[|\]/}},i.variable=n,i.comment=t,{pattern:RegExp(/^ ?\*{3}[ \t]*[ \t]*\*{3}(?:.|[\r\n](?!\*{3}))*/.source.replace(//g,function(){return e}),"im"),alias:"section",inside:i}}var i={pattern:/(\[Documentation\](?: {2}|\t)[ \t]*)(?![ \t]|#)(?:.|(?:\r\n?|\n)[ \t]*\.{3})+/,lookbehind:!0,alias:"string"},a={pattern:/([\r\n] ?)(?!#)(?:\S(?:[ \t]\S)*)+/,lookbehind:!0,alias:"function",inside:{variable:n}},o={pattern:/([\r\n](?: {2}|\t)[ \t]*)(?!\[|\.{3}|#)(?:\S(?:[ \t]\S)*)+/,lookbehind:!0,inside:{variable:n}};e.languages.robotframework={settings:r("Settings",{documentation:{pattern:/([\r\n] ?Documentation(?: {2}|\t)[ \t]*)(?![ \t]|#)(?:.|(?:\r\n?|\n)[ \t]*\.{3})+/,lookbehind:!0,alias:"string"},property:{pattern:/([\r\n] ?)(?!\.{3}|#)(?:\S(?:[ \t]\S)*)+/,lookbehind:!0}}),variables:r("Variables"),"test-cases":r("Test Cases",{"test-name":a,documentation:i,property:o}),keywords:r("Keywords",{"keyword-name":a,documentation:i,property:o}),tasks:r("Tasks",{"task-name":a,documentation:i,property:o}),comment:t},e.languages.robot=e.languages.robotframework}(e)}e.exports=t,t.displayName="robotframework",t.aliases=[]},56939(e){"use strict";function t(e){var t,n;(t=e).languages.ruby=t.languages.extend("clike",{comment:[/#.*/,{pattern:/^=begin\s[\s\S]*?^=end/m,greedy:!0}],"class-name":{pattern:/(\b(?:class)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|protected|private|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/}),n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:t.languages.ruby}},delete t.languages.ruby.function,t.languages.insertBefore("ruby","keyword",{regex:[{pattern:RegExp(/%r/.source+"(?:"+[/([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,/\((?:[^()\\]|\\[\s\S])*\)/.source,/\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/.source,/\[(?:[^\[\]\\]|\\[\s\S])*\]/.source,/<(?:[^<>\\]|\\[\s\S])*>/.source].join("|")+")"+/[egimnosux]{0,6}/.source),greedy:!0,inside:{interpolation:n}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:n}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:{pattern:/(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/,lookbehind:!0},"method-definition":{pattern:/(\bdef\s+)[\w.]+/,lookbehind:!0,inside:{function:/\w+$/,rest:t.languages.ruby}}}),t.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z]\w*(?:[?!]|\b)/}),t.languages.ruby.string=[{pattern:RegExp(/%[qQiIwWxs]?/.source+"(?:"+[/([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,/\((?:[^()\\]|\\[\s\S])*\)/.source,/\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/.source,/\[(?:[^\[\]\\]|\\[\s\S])*\]/.source,/<(?:[^<>\\]|\\[\s\S])*>/.source].join("|")+")"),greedy:!0,inside:{interpolation:n}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:n}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|[a-z_]\w*$/i,alias:"symbol",inside:{punctuation:/^<<[-~]?/}},interpolation:n}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|[a-z_]\w*$/i,alias:"symbol",inside:{punctuation:/^<<[-~]?'|'$/}}}}],t.languages.rb=t.languages.ruby}e.exports=t,t.displayName="ruby",t.aliases=["rb"]},83648(e){"use strict";function t(e){!function(e){for(var t=/\/\*(?:[^*/]|\*(?!\/)|\/(?!\*)|)*\*\//.source,n=0;n<2;n++)t=t.replace(//g,function(){return t});t=t.replace(//g,function(){return/[^\s\S]/.source}),e.languages.rust={comment:[{pattern:RegExp(/(^|[^\\])/.source+t),lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/b?"(?:\\[\s\S]|[^\\"])*"|b?r(#*)"(?:[^"]|"(?!\1))*"\1/,greedy:!0},char:{pattern:/b?'(?:\\(?:x[0-7][\da-fA-F]|u\{(?:[\da-fA-F]_*){1,6}\}|.)|[^\\\r\n\t'])'/,greedy:!0,alias:"string"},attribute:{pattern:/#!?\[(?:[^\[\]"]|"(?:\\[\s\S]|[^\\"])*")*\]/,greedy:!0,alias:"attr-name",inside:{string:null}},"closure-params":{pattern:/([=(,:]\s*|\bmove\s*)\|[^|]*\||\|[^|]*\|(?=\s*(?:\{|->))/,lookbehind:!0,greedy:!0,inside:{"closure-punctuation":{pattern:/^\||\|$/,alias:"punctuation"},rest:null}},"lifetime-annotation":{pattern:/'\w+/,alias:"symbol"},"fragment-specifier":{pattern:/(\$\w+:)[a-z]+/,lookbehind:!0,alias:"punctuation"},variable:/\$\w+/,"function-definition":{pattern:/(\bfn\s+)\w+/,lookbehind:!0,alias:"function"},"type-definition":{pattern:/(\b(?:enum|struct|union)\s+)\w+/,lookbehind:!0,alias:"class-name"},"module-declaration":[{pattern:/(\b(?:crate|mod)\s+)[a-z][a-z_\d]*/,lookbehind:!0,alias:"namespace"},{pattern:/(\b(?:crate|self|super)\s*)::\s*[a-z][a-z_\d]*\b(?:\s*::(?:\s*[a-z][a-z_\d]*\s*::)*)?/,lookbehind:!0,alias:"namespace",inside:{punctuation:/::/}}],keyword:[/\b(?:abstract|as|async|await|become|box|break|const|continue|crate|do|dyn|else|enum|extern|final|fn|for|if|impl|in|let|loop|macro|match|mod|move|mut|override|priv|pub|ref|return|self|Self|static|struct|super|trait|try|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,/\b(?:[ui](?:8|16|32|64|128|size)|f(?:32|64)|bool|char|str)\b/],function:/\b[a-z_]\w*(?=\s*(?:::\s*<|\())/,macro:{pattern:/\b\w+!/,alias:"property"},constant:/\b[A-Z_][A-Z_\d]+\b/,"class-name":/\b[A-Z]\w*\b/,namespace:{pattern:/(?:\b[a-z][a-z_\d]*\s*::\s*)*\b[a-z][a-z_\d]*\s*::(?!\s*<)/,inside:{punctuation:/::/}},number:/\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(?:(?:\d(?:_?\d)*)?\.)?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:[iu](?:8|16|32|64|size)?|f32|f64))?\b/,boolean:/\b(?:false|true)\b/,punctuation:/->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,operator:/[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<>?=?|[@?]/},e.languages.rust["closure-params"].inside.rest=e.languages.rust,e.languages.rust.attribute.inside.string=e.languages.rust.string}(e)}e.exports=t,t.displayName="rust",t.aliases=[]},16009(e){"use strict";function t(e){var t,n,r,i,a,o,s,u,c,l,f,d,h,p,b,m,g,v,y;t=e,n=/(?:"(?:""|[^"])*"(?!")|'(?:''|[^'])*'(?!'))/.source,r=/\b(?:\d[\da-f]*x|\d+(?:\.\d+)?(?:e[+-]?\d+)?)\b/i,i={pattern:RegExp(n+"[bx]"),alias:"number"},a={pattern:/&[a-z_]\w*/i},o={pattern:/((?:^|\s|=|\())%(?:ABORT|BY|CMS|COPY|DISPLAY|DO|ELSE|END|EVAL|GLOBAL|GO|GOTO|IF|INC|INCLUDE|INDEX|INPUT|KTRIM|LENGTH|LET|LIST|LOCAL|PUT|QKTRIM|QSCAN|QSUBSTR|QSYSFUNC|QUPCASE|RETURN|RUN|SCAN|SUBSTR|SUPERQ|SYMDEL|SYMGLOBL|SYMLOCAL|SYMEXIST|SYSCALL|SYSEVALF|SYSEXEC|SYSFUNC|SYSGET|SYSRPUT|THEN|TO|TSO|UNQUOTE|UNTIL|UPCASE|WHILE|WINDOW)\b/i,lookbehind:!0,alias:"keyword"},s={pattern:/(^|\s)(?:proc\s+\w+|quit|run|data(?!=))\b/i,alias:"keyword",lookbehind:!0},u=[/\/\*[\s\S]*?\*\//,{pattern:/(^[ \t]*|;\s*)\*[^;]*;/m,lookbehind:!0}],c={pattern:RegExp(n),greedy:!0},d={function:f={pattern:/%?\b\w+(?=\()/,alias:"keyword"},"arg-value":{pattern:/(=\s*)[A-Z\.]+/i,lookbehind:!0},operator:/=/,"macro-variable":a,arg:{pattern:/[A-Z]+/i,alias:"keyword"},number:r,"numeric-constant":i,punctuation:l=/[$%@.(){}\[\];,\\]/,string:c},h={pattern:/\b(?:format|put)\b=?[\w'$.]+/im,inside:{keyword:/^(?:format|put)(?==)/i,equals:/=/,format:{pattern:/(?:\w|\$\d)+\.\d?/i,alias:"number"}}},p={pattern:/\b(?:format|put)\s+[\w']+(?:\s+[$.\w]+)+(?=;)/i,inside:{keyword:/^(?:format|put)/i,format:{pattern:/[\w$]+\.\d?/,alias:"number"}}},b={pattern:/((?:^|\s)=?)(?:catname|checkpoint execute_always|dm|endsas|filename|footnote|%include|libname|%list|lock|missing|options|page|resetline|%run|sasfile|skip|sysecho|title\d?)\b/i,lookbehind:!0,alias:"keyword"},m={pattern:/(^|\s)(?:submit(?:\s+(?:load|parseonly|norun))?|endsubmit)\b/i,lookbehind:!0,alias:"keyword"},g=/accessControl|cdm|aggregation|aStore|ruleMining|audio|autotune|bayesianNetClassifier|bioMedImage|boolRule|builtins|cardinality|sccasl|clustering|copula|countreg|dataDiscovery|dataPreprocess|dataSciencePilot|dataStep|decisionTree|deepLearn|deepNeural|varReduce|simSystem|ds2|deduplication|ecm|entityRes|espCluster|explainModel|factmac|fastKnn|fcmpact|fedSql|freqTab|gam|gleam|graphSemiSupLearn|gVarCluster|hiddenMarkovModel|hyperGroup|image|iml|ica|kernalPca|langModel|ldaTopic|sparseML|mlTools|mixed|modelPublishing|mbc|network|optNetwork|neuralNet|nonlinear|nmf|nonParametricBayes|optimization|panel|pls|percentile|pca|phreg|qkb|qlim|quantreg|recommend|tsReconcile|deepRnn|regression|reinforcementLearn|robustPca|sampling|sparkEmbeddedProcess|search(?:Analytics)?|sentimentAnalysis|sequence|configuration|session(?:Prop)?|severity|simple|smartData|sandwich|spatialreg|stabilityMonitoring|spc|loadStreams|svDataDescription|svm|table|conditionalRandomFields|text(?:Rule(?:Develop|Score)|Mining|Parse|Topic|Util|Filters|Frequency)|tsInfo|timeData|transpose|uniTimeSeries/.source,v={pattern:RegExp(/(^|\s)(?:action\s+)?(?:)\.[a-z]+\b[^;]+/.source.replace(//g,function(){return g}),"i"),lookbehind:!0,inside:{keyword:RegExp(/(?:)\.[a-z]+\b/.source.replace(//g,function(){return g}),"i"),action:{pattern:/(?:action)/i,alias:"keyword"},comment:u,function:f,"arg-value":d["arg-value"],operator:d.operator,argument:d.arg,number:r,"numeric-constant":i,punctuation:l,string:c}},y={pattern:/((?:^|\s)=?)(?:after|analysis|and|array|barchart|barwidth|begingraph|by|call|cas|cbarline|cfill|class(?:lev)?|close|column|computed?|contains|continue|data(?==)|define|delete|describe|document|do\s+over|do|dol|drop|dul|end(?:source|comp)?|entryTitle|else|eval(?:uate)?|exec(?:ute)?|exit|fill(?:attrs)?|file(?:name)?|flist|fnc|function(?:list)?|goto|global|group(?:by)?|headline|headskip|histogram|if|infile|keep|keylabel|keyword|label|layout|leave|legendlabel|length|libname|loadactionset|merge|midpoints|name|noobs|nowd|_?null_|ods|options|or|otherwise|out(?:put)?|over(?:lay)?|plot|put|print|raise|ranexp|rannor|rbreak|retain|return|select|set|session|sessref|source|statgraph|sum|summarize|table|temp|terminate|then\s+do|then|title\d?|to|var|when|where|xaxisopts|yaxisopts|y2axisopts)\b/i,lookbehind:!0},t.languages.sas={datalines:{pattern:/^([ \t]*)(?:(?:data)?lines|cards);[\s\S]+?^[ \t]*;/im,lookbehind:!0,alias:"string",inside:{keyword:{pattern:/^(?:(?:data)?lines|cards)/i},punctuation:/;/}},"proc-sql":{pattern:/(^proc\s+(?:fed)?sql(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{sql:{pattern:RegExp(/^[ \t]*(?:select|alter\s+table|(?:create|describe|drop)\s+(?:index|table(?:\s+constraints)?|view)|create\s+unique\s+index|insert\s+into|update)(?:|[^;"'])+;/.source.replace(//g,function(){return n}),"im"),alias:"language-sql",inside:t.languages.sql},"global-statements":b,"sql-statements":{pattern:/(^|\s)(?:disconnect\s+from|exec(?:ute)?|begin|commit|rollback|reset|validate)\b/i,lookbehind:!0,alias:"keyword"},number:r,"numeric-constant":i,punctuation:l,string:c}},"proc-groovy":{pattern:/(^proc\s+groovy(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{comment:u,groovy:{pattern:RegExp(/(^[ \t]*submit(?:\s+(?:load|parseonly|norun))?)(?:|[^"'])+?(?=endsubmit;)/.source.replace(//g,function(){return n}),"im"),lookbehind:!0,alias:"language-groovy",inside:t.languages.groovy},keyword:y,"submit-statement":m,"global-statements":b,number:r,"numeric-constant":i,punctuation:l,string:c}},"proc-lua":{pattern:/(^proc\s+lua(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|run|data);|(?![\s\S]))/im,lookbehind:!0,inside:{comment:u,lua:{pattern:RegExp(/(^[ \t]*submit(?:\s+(?:load|parseonly|norun))?)(?:|[^"'])+?(?=endsubmit;)/.source.replace(//g,function(){return n}),"im"),lookbehind:!0,alias:"language-lua",inside:t.languages.lua},keyword:y,"submit-statement":m,"global-statements":b,number:r,"numeric-constant":i,punctuation:l,string:c}},"proc-cas":{pattern:/(^proc\s+cas(?:\s+[\w|=]+)?;)[\s\S]+?(?=^(?:proc\s+\w+|quit|data);|(?![\s\S]))/im,lookbehind:!0,inside:{comment:u,"statement-var":{pattern:/((?:^|\s)=?)saveresult\s[^;]+/im,lookbehind:!0,inside:{statement:{pattern:/^saveresult\s+\S+/i,inside:{keyword:/^(?:saveresult)/i}},rest:d}},"cas-actions":v,statement:{pattern:/((?:^|\s)=?)(?:default|(?:un)?set|on|output|upload)[^;]+/im,lookbehind:!0,inside:d},step:s,keyword:y,function:f,format:h,altformat:p,"global-statements":b,number:r,"numeric-constant":i,punctuation:l,string:c}},"proc-args":{pattern:RegExp(/(^proc\s+\w+\s+)(?!\s)(?:[^;"']|)+;/.source.replace(//g,function(){return n}),"im"),lookbehind:!0,inside:d},"macro-keyword":o,"macro-variable":a,"macro-string-functions":{pattern:/((?:^|\s|=))%(?:NRBQUOTE|NRQUOTE|NRSTR|BQUOTE|QUOTE|STR)\(.*?(?:[^%]\))/i,lookbehind:!0,inside:{function:{pattern:/%(?:NRBQUOTE|NRQUOTE|NRSTR|BQUOTE|QUOTE|STR)/i,alias:"keyword"},"macro-keyword":o,"macro-variable":a,"escaped-char":{pattern:/%['"()<>=¬^~;,#]/i},punctuation:l}},"macro-declaration":{pattern:/^%macro[^;]+(?=;)/im,inside:{keyword:/%macro/i}},"macro-end":{pattern:/^%mend[^;]+(?=;)/im,inside:{keyword:/%mend/i}},macro:{pattern:/%_\w+(?=\()/,alias:"keyword"},input:{pattern:/\binput\s[-\w\s/*.$&]+;/i,inside:{input:{alias:"keyword",pattern:/^input/i},comment:u,number:r,"numeric-constant":i}},"options-args":{pattern:/(^options)[-'"|/\\<>*+=:()\w\s]*(?=;)/im,lookbehind:!0,inside:d},"cas-actions":v,comment:u,function:f,format:h,altformat:p,"numeric-constant":i,datetime:{pattern:RegExp(n+"(?:dt?|t)"),alias:"number"},string:c,step:s,keyword:y,"operator-keyword":{pattern:/\b(?:eq|ne|gt|lt|ge|le|in|not)\b/i,alias:"operator"},number:r,operator:/\*\*?|\|\|?|!!?|¦¦?|<[>=]?|>[<=]?|[-+\/=&]|[~¬^]=?/i,punctuation:l}}e.exports=t,t.displayName="sas",t.aliases=[]},41720(e){"use strict";function t(e){var t,n,r;(t=e).languages.sass=t.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0}}),t.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,inside:{atrule:/(?:@[\w-]+|[+=])/m}}}),delete t.languages.sass.atrule,n=/\$[-\w]+|#\{\$[-\w]+\}/,r=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|or|not)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}],t.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,inside:{punctuation:/:/,variable:n,operator:r}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:n,operator:r,important:t.languages.sass.important}}}),delete t.languages.sass.property,delete t.languages.sass.important,t.languages.insertBefore("sass","punctuation",{selector:{pattern:/([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/,lookbehind:!0}})}e.exports=t,t.displayName="sass",t.aliases=[]},6054(e,t,n){"use strict";var r=n(15909);function i(e){e.register(r),e.languages.scala=e.languages.extend("java",{"triple-quoted-string":{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string"},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},keyword:/<-|=>|\b(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|null|object|override|package|private|protected|return|sealed|self|super|this|throw|trait|try|type|val|var|while|with|yield)\b/,number:/\b0x(?:[\da-f]*\.)?[\da-f]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e\d+)?[dfl]?/i,builtin:/\b(?:String|Int|Long|Short|Byte|Boolean|Double|Float|Char|Any|AnyRef|AnyVal|Unit|Nothing)\b/,symbol:/'[^\d\s\\]\w*/}),delete e.languages.scala["class-name"],delete e.languages.scala.function}e.exports=i,i.displayName="scala",i.aliases=[]},9997(e){"use strict";function t(e){!function(e){e.languages.scheme={comment:/;.*|#;\s*(?:\((?:[^()]|\([^()]*\))*\)|\[(?:[^\[\]]|\[[^\[\]]*\])*\])|#\|(?:[^#|]|#(?!\|)|\|(?!#)|#\|(?:[^#|]|#(?!\|)|\|(?!#))*\|#)*\|#/,string:{pattern:/"(?:[^"\\]|\\.)*"/,greedy:!0},symbol:{pattern:/'[^()\[\]#'\s]+/,greedy:!0},character:{pattern:/#\\(?:[ux][a-fA-F\d]+\b|[-a-zA-Z]+\b|[\uD800-\uDBFF][\uDC00-\uDFFF]|\S)/,greedy:!0,alias:"string"},"lambda-parameter":[{pattern:/((?:^|[^'`#])[(\[]lambda\s+)(?:[^|()\[\]'\s]+|\|(?:[^\\|]|\\.)*\|)/,lookbehind:!0},{pattern:/((?:^|[^'`#])[(\[]lambda\s+[(\[])[^()\[\]']+/,lookbehind:!0}],keyword:{pattern:/((?:^|[^'`#])[(\[])(?:begin|case(?:-lambda)?|cond(?:-expand)?|define(?:-library|-macro|-record-type|-syntax|-values)?|defmacro|delay(?:-force)?|do|else|export|except|guard|if|import|include(?:-ci|-library-declarations)?|lambda|let(?:rec)?(?:-syntax|-values|\*)?|let\*-values|only|parameterize|prefix|(?:quasi-?)?quote|rename|set!|syntax-(?:case|rules)|unless|unquote(?:-splicing)?|when)(?=[()\[\]\s]|$)/,lookbehind:!0},builtin:{pattern:/((?:^|[^'`#])[(\[])(?:abs|and|append|apply|assoc|ass[qv]|binary-port\?|boolean=?\?|bytevector(?:-append|-copy|-copy!|-length|-u8-ref|-u8-set!|\?)?|caar|cadr|call-with-(?:current-continuation|port|values)|call\/cc|car|cdar|cddr|cdr|ceiling|char(?:->integer|-ready\?|\?|<\?|<=\?|=\?|>\?|>=\?)|close-(?:input-port|output-port|port)|complex\?|cons|current-(?:error|input|output)-port|denominator|dynamic-wind|eof-object\??|eq\?|equal\?|eqv\?|error|error-object(?:-irritants|-message|\?)|eval|even\?|exact(?:-integer-sqrt|-integer\?|\?)?|expt|features|file-error\?|floor(?:-quotient|-remainder|\/)?|flush-output-port|for-each|gcd|get-output-(?:bytevector|string)|inexact\??|input-port(?:-open\?|\?)|integer(?:->char|\?)|lcm|length|list(?:->string|->vector|-copy|-ref|-set!|-tail|\?)?|make-(?:bytevector|list|parameter|string|vector)|map|max|member|memq|memv|min|modulo|negative\?|newline|not|null\?|number(?:->string|\?)|numerator|odd\?|open-(?:input|output)-(?:bytevector|string)|or|output-port(?:-open\?|\?)|pair\?|peek-char|peek-u8|port\?|positive\?|procedure\?|quotient|raise|raise-continuable|rational\?|rationalize|read-(?:bytevector|bytevector!|char|error\?|line|string|u8)|real\?|remainder|reverse|round|set-c[ad]r!|square|string(?:->list|->number|->symbol|->utf8|->vector|-append|-copy|-copy!|-fill!|-for-each|-length|-map|-ref|-set!|\?|<\?|<=\?|=\?|>\?|>=\?)?|substring|symbol(?:->string|\?|=\?)|syntax-error|textual-port\?|truncate(?:-quotient|-remainder|\/)?|u8-ready\?|utf8->string|values|vector(?:->list|->string|-append|-copy|-copy!|-fill!|-for-each|-length|-map|-ref|-set!|\?)?|with-exception-handler|write-(?:bytevector|char|string|u8)|zero\?)(?=[()\[\]\s]|$)/,lookbehind:!0},operator:{pattern:/((?:^|[^'`#])[(\[])(?:[-+*%/]|[<>]=?|=>?)(?=[()\[\]\s]|$)/,lookbehind:!0},number:{pattern:RegExp(t({"":/\d+(?:\/\d+)|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/.source,"":/[+-]?|[+-](?:inf|nan)\.0/.source,"":/[+-](?:|(?:inf|nan)\.0)?i/.source,"":/(?:@|)?|/.source,"":/(?:#d(?:#[ei])?|#[ei](?:#d)?)?/.source,"":/[0-9a-f]+(?:\/[0-9a-f]+)?/.source,"":/[+-]?|[+-](?:inf|nan)\.0/.source,"":/[+-](?:|(?:inf|nan)\.0)?i/.source,"":/(?:@|)?|/.source,"":/#[box](?:#[ei])?|(?:#[ei])?#[box]/.source,"":/(^|[()\[\]\s])(?:|)(?=[()\[\]\s]|$)/.source}),"i"),lookbehind:!0},boolean:{pattern:/(^|[()\[\]\s])#(?:[ft]|false|true)(?=[()\[\]\s]|$)/,lookbehind:!0},function:{pattern:/((?:^|[^'`#])[(\[])(?:[^|()\[\]'\s]+|\|(?:[^\\|]|\\.)*\|)(?=[()\[\]\s]|$)/,lookbehind:!0},identifier:{pattern:/(^|[()\[\]\s])\|(?:[^\\|]|\\.)*\|(?=[()\[\]\s]|$)/,lookbehind:!0,greedy:!0},punctuation:/[()\[\]']/};function t(e){for(var t in e)e[t]=e[t].replace(/<[\w\s]+>/g,function(t){return"(?:"+e[t].trim()+")"});return e[t]}}(e)}e.exports=t,t.displayName="scheme",t.aliases=[]},24296(e){"use strict";function t(e){e.languages.scss=e.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/m,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),e.languages.insertBefore("scss","atrule",{keyword:[/@(?:if|else(?: if)?|forward|for|each|while|import|use|extend|debug|warn|mixin|include|function|return|content)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),e.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),e.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|with|show|hide)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:true|false)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|or|not)(?=\s)/,lookbehind:!0}}),e.languages.scss.atrule.inside.rest=e.languages.scss}e.exports=t,t.displayName="scss",t.aliases=[]},49246(e,t,n){"use strict";var r=n(6979);function i(e){var t,n;e.register(r),n=[/"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/.source,/'[^']*'/.source,/\$'(?:[^'\\]|\\[\s\S])*'/.source,/<<-?\s*(["']?)(\w+)\1\s[\s\S]*?[\r\n]\2/.source].join("|"),(t=e).languages["shell-session"]={command:{pattern:RegExp(/^(?:[^\s@:$#*!/\\]+@[^\r\n@:$#*!/\\]+(?::[^\0-\x1F$#*?"<>:;|]+)?|[^\0-\x1F$#*?"<>@:;|]+)?/.source+/[$#]/.source+/(?:[^\\\r\n'"<$]|\\(?:[^\r]|\r\n?)|\$(?!')|<>)+/.source.replace(/<>/g,function(){return n}),"m"),greedy:!0,inside:{info:{pattern:/^[^#$]+/,alias:"punctuation",inside:{user:/^[^\s@:$#*!/\\]+@[^\r\n@:$#*!/\\]+/,punctuation:/:/,path:/[\s\S]+/}},bash:{pattern:/(^[$#]\s*)\S[\s\S]*/,lookbehind:!0,alias:"language-bash",inside:t.languages.bash},"shell-symbol":{pattern:/^[$#]/,alias:"important"}}},output:/.(?:.*(?:[\r\n]|.$))*/},t.languages["sh-session"]=t.languages.shellsession=t.languages["shell-session"]}e.exports=i,i.displayName="shellSession",i.aliases=[]},18890(e){"use strict";function t(e){e.languages.smali={comment:/#.*/,string:{pattern:/"(?:[^\r\n\\"]|\\.)*"|'(?:[^\r\n\\']|\\(?:.|u[\da-fA-F]{4}))'/,greedy:!0},"class-name":{pattern:/(^|[^L])L(?:(?:\w+|`[^`\r\n]*`)\/)*(?:[\w$]+|`[^`\r\n]*`)(?=\s*;)/,lookbehind:!0,inside:{"class-name":{pattern:/(^L|\/)(?:[\w$]+|`[^`\r\n]*`)$/,lookbehind:!0},namespace:{pattern:/^(L)(?:(?:\w+|`[^`\r\n]*`)\/)+/,lookbehind:!0,inside:{punctuation:/\//}},builtin:/^L/}},builtin:[{pattern:/([();\[])[BCDFIJSVZ]+/,lookbehind:!0},{pattern:/([\w$>]:)[BCDFIJSVZ]/,lookbehind:!0}],keyword:[{pattern:/(\.end\s+)[\w-]+/,lookbehind:!0},{pattern:/(^|[^\w.-])\.(?!\d)[\w-]+/,lookbehind:!0},{pattern:/(^|[^\w.-])(?:abstract|annotation|bridge|constructor|enum|final|interface|private|protected|public|runtime|static|synthetic|system|transient)(?![\w.-])/,lookbehind:!0}],function:{pattern:/(^|[^\w.-])(?:\w+|<[\w$-]+>)(?=\()/,lookbehind:!0},field:{pattern:/[\w$]+(?=:)/,alias:"variable"},register:{pattern:/(^|[^\w.-])[vp]\d(?![\w.-])/,lookbehind:!0,alias:"variable"},boolean:{pattern:/(^|[^\w.-])(?:true|false)(?![\w.-])/,lookbehind:!0},number:{pattern:/(^|[^/\w.-])-?(?:NAN|INFINITY|0x(?:[\dA-F]+(?:\.[\dA-F]*)?|\.[\dA-F]+)(?:p[+-]?[\dA-F]+)?|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)[dflst]?(?![\w.-])/i,lookbehind:!0},label:{pattern:/(:)\w+/,lookbehind:!0,alias:"property"},operator:/->|\.\.|[\[=]/,punctuation:/[{}(),;:]/}}e.exports=t,t.displayName="smali",t.aliases=[]},11037(e){"use strict";function t(e){e.languages.smalltalk={comment:/"(?:""|[^"])*"/,character:{pattern:/\$./,alias:"string"},string:/'(?:''|[^'])*'/,symbol:/#[\da-z]+|#(?:-|([+\/\\*~<>=@%|&?!])\1?)|#(?=\()/i,"block-arguments":{pattern:/(\[\s*):[^\[|]*\|/,lookbehind:!0,inside:{variable:/:[\da-z]+/i,punctuation:/\|/}},"temporary-variables":{pattern:/\|[^|]+\|/,inside:{variable:/[\da-z]+/i,punctuation:/\|/}},keyword:/\b(?:nil|true|false|self|super|new)\b/,number:[/\d+r-?[\dA-Z]+(?:\.[\dA-Z]+)?(?:e-?\d+)?/,/\b\d+(?:\.\d+)?(?:e-?\d+)?/],operator:/[<=]=?|:=|~[~=]|\/\/?|\\\\|>[>=]?|[!^+\-*&|,@]/,punctuation:/[.;:?\[\](){}]/}}e.exports=t,t.displayName="smalltalk",t.aliases=[]},64020(e,t,n){"use strict";var r=n(93205);function i(e){var t;e.register(r),(t=e).languages.smarty={comment:/\{\*[\s\S]*?\*\}/,delimiter:{pattern:/^\{|\}$/i,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][-+]?\d+)?/,variable:[/\$(?!\d)\w+/,/#(?!\d)\w+#/,{pattern:/(\.|->)(?!\d)\w+/,lookbehind:!0},{pattern:/(\[)(?!\d)\w+(?=\])/,lookbehind:!0}],function:[{pattern:/(\|\s*)@?(?!\d)\w+/,lookbehind:!0},/^\/?(?!\d)\w+/,/(?!\d)\w+(?=\()/],"attr-name":{pattern:/\w+\s*=\s*(?:(?!\d)\w+)?/,inside:{variable:{pattern:/(=\s*)(?!\d)\w+/,lookbehind:!0},operator:/=/}},punctuation:[/[\[\]().,:`]|->/],operator:[/[+\-*\/%]|==?=?|[!<>]=?|&&|\|\|?/,/\bis\s+(?:not\s+)?(?:div|even|odd)(?:\s+by)?\b/,/\b(?:eq|neq?|gt|lt|gt?e|lt?e|not|mod|or|and)\b/],keyword:/\b(?:false|off|on|no|true|yes)\b/},t.hooks.add("before-tokenize",function(e){var n=/\{\*[\s\S]*?\*\}|\{[\s\S]+?\}/g,r="{literal}",i="{/literal}",a=!1;t.languages["markup-templating"].buildPlaceholders(e,"smarty",n,function(e){return e===i&&(a=!1),!a&&(e===r&&(a=!0),!0)})}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"smarty")})}e.exports=i,i.displayName="smarty",i.aliases=[]},49760(e){"use strict";function t(e){var t,n;n=/\b(?:abstype|and|andalso|as|case|datatype|do|else|end|eqtype|exception|fn|fun|functor|handle|if|in|include|infix|infixr|let|local|nonfix|of|op|open|orelse|raise|rec|sharing|sig|signature|struct|structure|then|type|val|where|while|with|withtype)\b/i,(t=e).languages.sml={comment:/\(\*(?:[^*(]|\*(?!\))|\((?!\*)|\(\*(?:[^*(]|\*(?!\))|\((?!\*))*\*\))*\*\)/,string:{pattern:/#?"(?:[^"\\]|\\.)*"/,greedy:!0},"class-name":[{pattern:RegExp(/((?:^|[^:]):\s*)(?:\s*(?:(?:\*|->)\s*|,\s*(?:(?=)|(?!)\s+)))*/.source.replace(//g,function(){return/\s*(?:[*,]|->)/.source}).replace(//g,function(){return/(?:'[\w']*||\((?:[^()]|\([^()]*\))*\)|\{(?:[^{}]|\{[^{}]*\})*\})(?:\s+)*/.source}).replace(//g,function(){return/(?!)[a-z\d_][\w'.]*/.source}).replace(//g,function(){return n.source}),"i"),lookbehind:!0,greedy:!0,inside:null},{pattern:/((?:^|[^\w'])(?:datatype|exception|functor|signature|structure|type)\s+)[a-z_][\w'.]*/i,lookbehind:!0}],function:{pattern:/((?:^|[^\w'])fun\s+)[a-z_][\w'.]*/i,lookbehind:!0},keyword:n,variable:{pattern:/(^|[^\w'])'[\w']*/,lookbehind:!0},number:/~?\b(?:\d+(?:\.\d+)?(?:e~?\d+)?|0x[\da-f]+)\b/i,word:{pattern:/\b0w(?:\d+|x[\da-f]+)\b/i,alias:"constant"},boolean:/\b(?:false|true)\b/i,operator:/\.\.\.|:[>=:]|=>?|->|[<>]=?|[!+\-*/^#|@~]/,punctuation:/[(){}\[\].:,;]/},t.languages.sml["class-name"][0].inside=t.languages.sml,t.languages.smlnj=t.languages.sml}e.exports=t,t.displayName="sml",t.aliases=["smlnj"]},33351(e){"use strict";function t(e){e.languages.solidity=e.languages.extend("clike",{"class-name":{pattern:/(\b(?:contract|enum|interface|library|new|struct|using)\s+)(?!\d)[\w$]+/,lookbehind:!0},keyword:/\b(?:_|anonymous|as|assembly|assert|break|calldata|case|constant|constructor|continue|contract|default|delete|do|else|emit|enum|event|external|for|from|function|if|import|indexed|inherited|interface|internal|is|let|library|mapping|memory|modifier|new|payable|pragma|private|public|pure|require|returns?|revert|selfdestruct|solidity|storage|struct|suicide|switch|this|throw|using|var|view|while)\b/,operator:/=>|->|:=|=:|\*\*|\+\+|--|\|\||&&|<<=?|>>=?|[-+*/%^&|<>!=]=?|[~?]/}),e.languages.insertBefore("solidity","keyword",{builtin:/\b(?:address|bool|string|u?int(?:8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?|byte|bytes(?:[1-9]|[12]\d|3[0-2])?)\b/}),e.languages.insertBefore("solidity","number",{version:{pattern:/([<>]=?|\^)\d+\.\d+\.\d+\b/,lookbehind:!0,alias:"number"}}),e.languages.sol=e.languages.solidity}e.exports=t,t.displayName="solidity",t.aliases=["sol"]},13570(e){"use strict";function t(e){var t,n;n={pattern:/\{[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\}/i,alias:"constant",inside:{punctuation:/[{}]/}},(t=e).languages["solution-file"]={comment:{pattern:/#.*/,greedy:!0},string:{pattern:/"[^"\r\n]*"|'[^'\r\n]*'/,greedy:!0,inside:{guid:n}},object:{pattern:/^([ \t]*)(?:([A-Z]\w*)\b(?=.*(?:\r\n?|\n)(?:\1[ \t].*(?:\r\n?|\n))*\1End\2(?=[ \t]*$))|End[A-Z]\w*(?=[ \t]*$))/m,lookbehind:!0,greedy:!0,alias:"keyword"},property:{pattern:/^([ \t]*)(?!\s)[^\r\n"#=()]*[^\s"#=()](?=\s*=)/m,lookbehind:!0,inside:{guid:n}},guid:n,number:/\b\d+(?:\.\d+)*\b/,boolean:/\b(?:FALSE|TRUE)\b/,operator:/=/,punctuation:/[(),]/},t.languages.sln=t.languages["solution-file"]}e.exports=t,t.displayName="solutionFile",t.aliases=[]},38181(e,t,n){"use strict";var r=n(93205);function i(e){var t,n,i;e.register(r),n=/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,i=/\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b|\b0x[\dA-F]+\b/,(t=e).languages.soy={comment:[/\/\*[\s\S]*?\*\//,{pattern:/(\s)\/\/.*/,lookbehind:!0,greedy:!0}],"command-arg":{pattern:/(\{+\/?\s*(?:alias|call|delcall|delpackage|deltemplate|namespace|template)\s+)\.?[\w.]+/,lookbehind:!0,alias:"string",inside:{punctuation:/\./}},parameter:{pattern:/(\{+\/?\s*@?param\??\s+)\.?[\w.]+/,lookbehind:!0,alias:"variable"},keyword:[{pattern:/(\{+\/?[^\S\r\n]*)(?:\\[nrt]|alias|call|case|css|default|delcall|delpackage|deltemplate|else(?:if)?|fallbackmsg|for(?:each)?|if(?:empty)?|lb|let|literal|msg|namespace|nil|@?param\??|rb|sp|switch|template|xid)/,lookbehind:!0},/\b(?:any|as|attributes|bool|css|float|in|int|js|html|list|map|null|number|string|uri)\b/],delimiter:{pattern:/^\{+\/?|\/?\}+$/,alias:"punctuation"},property:/\w+(?==)/,variable:{pattern:/\$[^\W\d]\w*(?:\??(?:\.\w+|\[[^\]]+\]))*/,inside:{string:{pattern:n,greedy:!0},number:i,punctuation:/[\[\].?]/}},string:{pattern:n,greedy:!0},function:[/\w+(?=\()/,{pattern:/(\|[^\S\r\n]*)\w+/,lookbehind:!0}],boolean:/\b(?:true|false)\b/,number:i,operator:/\?:?|<=?|>=?|==?|!=|[+*/%-]|\b(?:and|not|or)\b/,punctuation:/[{}()\[\]|.,:]/},t.hooks.add("before-tokenize",function(e){var n=/\{\{.+?\}\}|\{.+?\}|\s\/\/.*|\/\*[\s\S]*?\*\//g,r="{literal}",i="{/literal}",a=!1;t.languages["markup-templating"].buildPlaceholders(e,"soy",n,function(e){return e===i&&(a=!1),!a&&(e===r&&(a=!0),!0)})}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"soy")})}e.exports=i,i.displayName="soy",i.aliases=[]},98774(e,t,n){"use strict";var r=n(24691);function i(e){e.register(r),e.languages.sparql=e.languages.extend("turtle",{boolean:/\b(?:true|false)\b/i,variable:{pattern:/[?$]\w+/,greedy:!0}}),e.languages.insertBefore("sparql","punctuation",{keyword:[/\b(?:A|ADD|ALL|AS|ASC|ASK|BNODE|BY|CLEAR|CONSTRUCT|COPY|CREATE|DATA|DEFAULT|DELETE|DESC|DESCRIBE|DISTINCT|DROP|EXISTS|FILTER|FROM|GROUP|HAVING|INSERT|INTO|LIMIT|LOAD|MINUS|MOVE|NAMED|NOT|NOW|OFFSET|OPTIONAL|ORDER|RAND|REDUCED|SELECT|SEPARATOR|SERVICE|SILENT|STRUUID|UNION|USING|UUID|VALUES|WHERE)\b/i,/\b(?:ABS|AVG|BIND|BOUND|CEIL|COALESCE|CONCAT|CONTAINS|COUNT|DATATYPE|DAY|ENCODE_FOR_URI|FLOOR|GROUP_CONCAT|HOURS|IF|IRI|isBLANK|isIRI|isLITERAL|isNUMERIC|isURI|LANG|LANGMATCHES|LCASE|MAX|MD5|MIN|MINUTES|MONTH|ROUND|REGEX|REPLACE|sameTerm|SAMPLE|SECONDS|SHA1|SHA256|SHA384|SHA512|STR|STRAFTER|STRBEFORE|STRDT|STRENDS|STRLANG|STRLEN|STRSTARTS|SUBSTR|SUM|TIMEZONE|TZ|UCASE|URI|YEAR)\b(?=\s*\()/i,/\b(?:GRAPH|BASE|PREFIX)\b/i]}),e.languages.rq=e.languages.sparql}e.exports=i,i.displayName="sparql",i.aliases=["rq"]},22855(e){"use strict";function t(e){e.languages["splunk-spl"]={comment:/`comment\("(?:\\.|[^\\"])*"\)`/,string:{pattern:/"(?:\\.|[^\\"])*"/,greedy:!0},keyword:/\b(?:abstract|accum|addcoltotals|addinfo|addtotals|analyzefields|anomalies|anomalousvalue|anomalydetection|append|appendcols|appendcsv|appendlookup|appendpipe|arules|associate|audit|autoregress|bin|bucket|bucketdir|chart|cluster|cofilter|collect|concurrency|contingency|convert|correlate|datamodel|dbinspect|dedup|delete|delta|diff|erex|eval|eventcount|eventstats|extract|fieldformat|fields|fieldsummary|filldown|fillnull|findtypes|folderize|foreach|format|from|gauge|gentimes|geom|geomfilter|geostats|head|highlight|history|iconify|input|inputcsv|inputlookup|iplocation|join|kmeans|kv|kvform|loadjob|localize|localop|lookup|makecontinuous|makemv|makeresults|map|mcollect|metadata|metasearch|meventcollect|mstats|multikv|multisearch|mvcombine|mvexpand|nomv|outlier|outputcsv|outputlookup|outputtext|overlap|pivot|predict|rangemap|rare|regex|relevancy|reltime|rename|replace|rest|return|reverse|rex|rtorder|run|savedsearch|script|scrub|search|searchtxn|selfjoin|sendemail|set|setfields|sichart|sirare|sistats|sitimechart|sitop|sort|spath|stats|strcat|streamstats|table|tags|tail|timechart|timewrap|top|transaction|transpose|trendline|tscollect|tstats|typeahead|typelearner|typer|union|uniq|untable|where|x11|xmlkv|xmlunescape|xpath|xyseries)\b/i,"operator-word":{pattern:/\b(?:and|as|by|not|or|xor)\b/i,alias:"operator"},function:/\b\w+(?=\s*\()/,property:/\b\w+(?=\s*=(?!=))/,date:{pattern:/\b\d{1,2}\/\d{1,2}\/\d{1,4}(?:(?::\d{1,2}){3})?\b/,alias:"number"},number:/\b\d+(?:\.\d+)?\b/,boolean:/\b(?:f|false|t|true)\b/i,operator:/[<>=]=?|[-+*/%|]/,punctuation:/[()[\],]/}}e.exports=t,t.displayName="splunkSpl",t.aliases=[]},29611(e){"use strict";function t(e){e.languages.sqf=e.languages.extend("clike",{string:{pattern:/"(?:(?:"")?[^"])*"(?!")|'(?:[^'])*'/,greedy:!0},keyword:/\b(?:breakOut|breakTo|call|case|catch|default|do|echo|else|execVM|execFSM|exitWith|for|forEach|forEachMember|forEachMemberAgent|forEachMemberTeam|from|goto|if|nil|preprocessFile|preprocessFileLineNumbers|private|scopeName|spawn|step|switch|then|throw|to|try|while|with)\b/i,boolean:/\b(?:true|false)\b/i,function:/\b(?:abs|accTime|acos|action|actionIDs|actionKeys|actionKeysImages|actionKeysNames|actionKeysNamesArray|actionName|actionParams|activateAddons|activatedAddons|activateKey|add3DENConnection|add3DENEventHandler|add3DENLayer|addAction|addBackpack|addBackpackCargo|addBackpackCargoGlobal|addBackpackGlobal|addCamShake|addCuratorAddons|addCuratorCameraArea|addCuratorEditableObjects|addCuratorEditingArea|addCuratorPoints|addEditorObject|addEventHandler|addForce|addForceGeneratorRTD|addGoggles|addGroupIcon|addHandgunItem|addHeadgear|addItem|addItemCargo|addItemCargoGlobal|addItemPool|addItemToBackpack|addItemToUniform|addItemToVest|addLiveStats|addMagazine|addMagazineAmmoCargo|addMagazineCargo|addMagazineCargoGlobal|addMagazineGlobal|addMagazinePool|addMagazines|addMagazineTurret|addMenu|addMenuItem|addMissionEventHandler|addMPEventHandler|addMusicEventHandler|addOwnedMine|addPlayerScores|addPrimaryWeaponItem|addPublicVariableEventHandler|addRating|addResources|addScore|addScoreSide|addSecondaryWeaponItem|addSwitchableUnit|addTeamMember|addToRemainsCollector|addTorque|addUniform|addVehicle|addVest|addWaypoint|addWeapon|addWeaponCargo|addWeaponCargoGlobal|addWeaponGlobal|addWeaponItem|addWeaponPool|addWeaponTurret|admin|agent|agents|AGLToASL|aimedAtTarget|aimPos|airDensityCurveRTD|airDensityRTD|airplaneThrottle|airportSide|AISFinishHeal|alive|all3DENEntities|allAirports|allControls|allCurators|allCutLayers|allDead|allDeadMen|allDisplays|allGroups|allMapMarkers|allMines|allMissionObjects|allow3DMode|allowCrewInImmobile|allowCuratorLogicIgnoreAreas|allowDamage|allowDammage|allowFileOperations|allowFleeing|allowGetIn|allowSprint|allPlayers|allSimpleObjects|allSites|allTurrets|allUnits|allUnitsUAV|allVariables|ammo|ammoOnPylon|animate|animateBay|animateDoor|animatePylon|animateSource|animationNames|animationPhase|animationSourcePhase|animationState|append|apply|armoryPoints|arrayIntersect|asin|ASLToAGL|ASLToATL|assert|assignAsCargo|assignAsCargoIndex|assignAsCommander|assignAsDriver|assignAsGunner|assignAsTurret|assignCurator|assignedCargo|assignedCommander|assignedDriver|assignedGunner|assignedItems|assignedTarget|assignedTeam|assignedVehicle|assignedVehicleRole|assignItem|assignTeam|assignToAirport|atan|atan2|atg|ATLToASL|attachedObject|attachedObjects|attachedTo|attachObject|attachTo|attackEnabled|backpack|backpackCargo|backpackContainer|backpackItems|backpackMagazines|backpackSpaceFor|behaviour|benchmark|binocular|blufor|boundingBox|boundingBoxReal|boundingCenter|briefingName|buildingExit|buildingPos|buldozer_EnableRoadDiag|buldozer_IsEnabledRoadDiag|buldozer_LoadNewRoads|buldozer_reloadOperMap|buttonAction|buttonSetAction|cadetMode|callExtension|camCommand|camCommit|camCommitPrepared|camCommitted|camConstuctionSetParams|camCreate|camDestroy|cameraEffect|cameraEffectEnableHUD|cameraInterest|cameraOn|cameraView|campaignConfigFile|camPreload|camPreloaded|camPrepareBank|camPrepareDir|camPrepareDive|camPrepareFocus|camPrepareFov|camPrepareFovRange|camPreparePos|camPrepareRelPos|camPrepareTarget|camSetBank|camSetDir|camSetDive|camSetFocus|camSetFov|camSetFovRange|camSetPos|camSetRelPos|camSetTarget|camTarget|camUseNVG|canAdd|canAddItemToBackpack|canAddItemToUniform|canAddItemToVest|cancelSimpleTaskDestination|canFire|canMove|canSlingLoad|canStand|canSuspend|canTriggerDynamicSimulation|canUnloadInCombat|canVehicleCargo|captive|captiveNum|cbChecked|cbSetChecked|ceil|channelEnabled|cheatsEnabled|checkAIFeature|checkVisibility|civilian|className|clear3DENAttribute|clear3DENInventory|clearAllItemsFromBackpack|clearBackpackCargo|clearBackpackCargoGlobal|clearForcesRTD|clearGroupIcons|clearItemCargo|clearItemCargoGlobal|clearItemPool|clearMagazineCargo|clearMagazineCargoGlobal|clearMagazinePool|clearOverlay|clearRadio|clearVehicleInit|clearWeaponCargo|clearWeaponCargoGlobal|clearWeaponPool|clientOwner|closeDialog|closeDisplay|closeOverlay|collapseObjectTree|collect3DENHistory|collectiveRTD|combatMode|commandArtilleryFire|commandChat|commander|commandFire|commandFollow|commandFSM|commandGetOut|commandingMenu|commandMove|commandRadio|commandStop|commandSuppressiveFire|commandTarget|commandWatch|comment|commitOverlay|compile|compileFinal|completedFSM|composeText|configClasses|configFile|configHierarchy|configName|configNull|configProperties|configSourceAddonList|configSourceMod|configSourceModList|confirmSensorTarget|connectTerminalToUAV|controlNull|controlsGroupCtrl|copyFromClipboard|copyToClipboard|copyWaypoints|cos|count|countEnemy|countFriendly|countSide|countType|countUnknown|create3DENComposition|create3DENEntity|createAgent|createCenter|createDialog|createDiaryLink|createDiaryRecord|createDiarySubject|createDisplay|createGearDialog|createGroup|createGuardedPoint|createLocation|createMarker|createMarkerLocal|createMenu|createMine|createMissionDisplay|createMPCampaignDisplay|createSimpleObject|createSimpleTask|createSite|createSoundSource|createTask|createTeam|createTrigger|createUnit|createVehicle|createVehicleCrew|createVehicleLocal|crew|ctAddHeader|ctAddRow|ctClear|ctCurSel|ctData|ctFindHeaderRows|ctFindRowHeader|ctHeaderControls|ctHeaderCount|ctRemoveHeaders|ctRemoveRows|ctrlActivate|ctrlAddEventHandler|ctrlAngle|ctrlAutoScrollDelay|ctrlAutoScrollRewind|ctrlAutoScrollSpeed|ctrlChecked|ctrlClassName|ctrlCommit|ctrlCommitted|ctrlCreate|ctrlDelete|ctrlEnable|ctrlEnabled|ctrlFade|ctrlHTMLLoaded|ctrlIDC|ctrlIDD|ctrlMapAnimAdd|ctrlMapAnimClear|ctrlMapAnimCommit|ctrlMapAnimDone|ctrlMapCursor|ctrlMapMouseOver|ctrlMapScale|ctrlMapScreenToWorld|ctrlMapWorldToScreen|ctrlModel|ctrlModelDirAndUp|ctrlModelScale|ctrlParent|ctrlParentControlsGroup|ctrlPosition|ctrlRemoveAllEventHandlers|ctrlRemoveEventHandler|ctrlScale|ctrlSetActiveColor|ctrlSetAngle|ctrlSetAutoScrollDelay|ctrlSetAutoScrollRewind|ctrlSetAutoScrollSpeed|ctrlSetBackgroundColor|ctrlSetChecked|ctrlSetDisabledColor|ctrlSetEventHandler|ctrlSetFade|ctrlSetFocus|ctrlSetFont|ctrlSetFontH1|ctrlSetFontH1B|ctrlSetFontH2|ctrlSetFontH2B|ctrlSetFontH3|ctrlSetFontH3B|ctrlSetFontH4|ctrlSetFontH4B|ctrlSetFontH5|ctrlSetFontH5B|ctrlSetFontH6|ctrlSetFontH6B|ctrlSetFontHeight|ctrlSetFontHeightH1|ctrlSetFontHeightH2|ctrlSetFontHeightH3|ctrlSetFontHeightH4|ctrlSetFontHeightH5|ctrlSetFontHeightH6|ctrlSetFontHeightSecondary|ctrlSetFontP|ctrlSetFontPB|ctrlSetFontSecondary|ctrlSetForegroundColor|ctrlSetModel|ctrlSetModelDirAndUp|ctrlSetModelScale|ctrlSetPixelPrecision|ctrlSetPosition|ctrlSetScale|ctrlSetStructuredText|ctrlSetText|ctrlSetTextColor|ctrlSetTextColorSecondary|ctrlSetTextSecondary|ctrlSetTooltip|ctrlSetTooltipColorBox|ctrlSetTooltipColorShade|ctrlSetTooltipColorText|ctrlShow|ctrlShown|ctrlText|ctrlTextHeight|ctrlTextSecondary|ctrlTextWidth|ctrlType|ctrlVisible|ctRowControls|ctRowCount|ctSetCurSel|ctSetData|ctSetHeaderTemplate|ctSetRowTemplate|ctSetValue|ctValue|curatorAddons|curatorCamera|curatorCameraArea|curatorCameraAreaCeiling|curatorCoef|curatorEditableObjects|curatorEditingArea|curatorEditingAreaType|curatorMouseOver|curatorPoints|curatorRegisteredObjects|curatorSelected|curatorWaypointCost|current3DENOperation|currentChannel|currentCommand|currentMagazine|currentMagazineDetail|currentMagazineDetailTurret|currentMagazineTurret|currentMuzzle|currentNamespace|currentTask|currentTasks|currentThrowable|currentVisionMode|currentWaypoint|currentWeapon|currentWeaponMode|currentWeaponTurret|currentZeroing|cursorObject|cursorTarget|customChat|customRadio|cutFadeOut|cutObj|cutRsc|cutText|damage|date|dateToNumber|daytime|deActivateKey|debriefingText|debugFSM|debugLog|deg|delete3DENEntities|deleteAt|deleteCenter|deleteCollection|deleteEditorObject|deleteGroup|deleteGroupWhenEmpty|deleteIdentity|deleteLocation|deleteMarker|deleteMarkerLocal|deleteRange|deleteResources|deleteSite|deleteStatus|deleteTeam|deleteVehicle|deleteVehicleCrew|deleteWaypoint|detach|detectedMines|diag_activeMissionFSMs|diag_activeScripts|diag_activeSQFScripts|diag_activeSQSScripts|diag_captureFrame|diag_captureFrameToFile|diag_captureSlowFrame|diag_codePerformance|diag_drawMode|diag_dynamicSimulationEnd|diag_enable|diag_enabled|diag_fps|diag_fpsMin|diag_frameNo|diag_lightNewLoad|diag_list|diag_log|diag_logSlowFrame|diag_mergeConfigFile|diag_recordTurretLimits|diag_setLightNew|diag_tickTime|diag_toggle|dialog|diarySubjectExists|didJIP|didJIPOwner|difficulty|difficultyEnabled|difficultyEnabledRTD|difficultyOption|direction|directSay|disableAI|disableCollisionWith|disableConversation|disableDebriefingStats|disableMapIndicators|disableNVGEquipment|disableRemoteSensors|disableSerialization|disableTIEquipment|disableUAVConnectability|disableUserInput|displayAddEventHandler|displayCtrl|displayNull|displayParent|displayRemoveAllEventHandlers|displayRemoveEventHandler|displaySetEventHandler|dissolveTeam|distance|distance2D|distanceSqr|distributionRegion|do3DENAction|doArtilleryFire|doFire|doFollow|doFSM|doGetOut|doMove|doorPhase|doStop|doSuppressiveFire|doTarget|doWatch|drawArrow|drawEllipse|drawIcon|drawIcon3D|drawLine|drawLine3D|drawLink|drawLocation|drawPolygon|drawRectangle|drawTriangle|driver|drop|dynamicSimulationDistance|dynamicSimulationDistanceCoef|dynamicSimulationEnabled|dynamicSimulationSystemEnabled|east|edit3DENMissionAttributes|editObject|editorSetEventHandler|effectiveCommander|emptyPositions|enableAI|enableAIFeature|enableAimPrecision|enableAttack|enableAudioFeature|enableAutoStartUpRTD|enableAutoTrimRTD|enableCamShake|enableCaustics|enableChannel|enableCollisionWith|enableCopilot|enableDebriefingStats|enableDiagLegend|enableDynamicSimulation|enableDynamicSimulationSystem|enableEndDialog|enableEngineArtillery|enableEnvironment|enableFatigue|enableGunLights|enableInfoPanelComponent|enableIRLasers|enableMimics|enablePersonTurret|enableRadio|enableReload|enableRopeAttach|enableSatNormalOnDetail|enableSaving|enableSentences|enableSimulation|enableSimulationGlobal|enableStamina|enableStressDamage|enableTeamSwitch|enableTraffic|enableUAVConnectability|enableUAVWaypoints|enableVehicleCargo|enableVehicleSensor|enableWeaponDisassembly|endl|endLoadingScreen|endMission|engineOn|enginesIsOnRTD|enginesPowerRTD|enginesRpmRTD|enginesTorqueRTD|entities|environmentEnabled|estimatedEndServerTime|estimatedTimeLeft|evalObjectArgument|everyBackpack|everyContainer|exec|execEditorScript|exp|expectedDestination|exportJIPMessages|eyeDirection|eyePos|face|faction|fadeMusic|fadeRadio|fadeSound|fadeSpeech|failMission|fillWeaponsFromPool|find|findCover|findDisplay|findEditorObject|findEmptyPosition|findEmptyPositionReady|findIf|findNearestEnemy|finishMissionInit|finite|fire|fireAtTarget|firstBackpack|flag|flagAnimationPhase|flagOwner|flagSide|flagTexture|fleeing|floor|flyInHeight|flyInHeightASL|fog|fogForecast|fogParams|forceAddUniform|forceAtPositionRTD|forcedMap|forceEnd|forceFlagTexture|forceFollowRoad|forceGeneratorRTD|forceMap|forceRespawn|forceSpeed|forceWalk|forceWeaponFire|forceWeatherChange|forgetTarget|format|formation|formationDirection|formationLeader|formationMembers|formationPosition|formationTask|formatText|formLeader|freeLook|fromEditor|fuel|fullCrew|gearIDCAmmoCount|gearSlotAmmoCount|gearSlotData|get3DENActionState|get3DENAttribute|get3DENCamera|get3DENConnections|get3DENEntity|get3DENEntityID|get3DENGrid|get3DENIconsVisible|get3DENLayerEntities|get3DENLinesVisible|get3DENMissionAttribute|get3DENMouseOver|get3DENSelected|getAimingCoef|getAllEnvSoundControllers|getAllHitPointsDamage|getAllOwnedMines|getAllSoundControllers|getAmmoCargo|getAnimAimPrecision|getAnimSpeedCoef|getArray|getArtilleryAmmo|getArtilleryComputerSettings|getArtilleryETA|getAssignedCuratorLogic|getAssignedCuratorUnit|getBackpackCargo|getBleedingRemaining|getBurningValue|getCameraViewDirection|getCargoIndex|getCenterOfMass|getClientState|getClientStateNumber|getCompatiblePylonMagazines|getConnectedUAV|getContainerMaxLoad|getCursorObjectParams|getCustomAimCoef|getDammage|getDescription|getDir|getDirVisual|getDLCAssetsUsage|getDLCAssetsUsageByName|getDLCs|getDLCUsageTime|getEditorCamera|getEditorMode|getEditorObjectScope|getElevationOffset|getEngineTargetRpmRTD|getEnvSoundController|getFatigue|getFieldManualStartPage|getForcedFlagTexture|getFriend|getFSMVariable|getFuelCargo|getGroupIcon|getGroupIconParams|getGroupIcons|getHideFrom|getHit|getHitIndex|getHitPointDamage|getItemCargo|getMagazineCargo|getMarkerColor|getMarkerPos|getMarkerSize|getMarkerType|getMass|getMissionConfig|getMissionConfigValue|getMissionDLCs|getMissionLayerEntities|getMissionLayers|getModelInfo|getMousePosition|getMusicPlayedTime|getNumber|getObjectArgument|getObjectChildren|getObjectDLC|getObjectMaterials|getObjectProxy|getObjectTextures|getObjectType|getObjectViewDistance|getOxygenRemaining|getPersonUsedDLCs|getPilotCameraDirection|getPilotCameraPosition|getPilotCameraRotation|getPilotCameraTarget|getPlateNumber|getPlayerChannel|getPlayerScores|getPlayerUID|getPlayerUIDOld|getPos|getPosASL|getPosASLVisual|getPosASLW|getPosATL|getPosATLVisual|getPosVisual|getPosWorld|getPylonMagazines|getRelDir|getRelPos|getRemoteSensorsDisabled|getRepairCargo|getResolution|getRotorBrakeRTD|getShadowDistance|getShotParents|getSlingLoad|getSoundController|getSoundControllerResult|getSpeed|getStamina|getStatValue|getSuppression|getTerrainGrid|getTerrainHeightASL|getText|getTotalDLCUsageTime|getTrimOffsetRTD|getUnitLoadout|getUnitTrait|getUserMFDText|getUserMFDValue|getVariable|getVehicleCargo|getWeaponCargo|getWeaponSway|getWingsOrientationRTD|getWingsPositionRTD|getWPPos|glanceAt|globalChat|globalRadio|goggles|group|groupChat|groupFromNetId|groupIconSelectable|groupIconsVisible|groupId|groupOwner|groupRadio|groupSelectedUnits|groupSelectUnit|grpNull|gunner|gusts|halt|handgunItems|handgunMagazine|handgunWeapon|handsHit|hasInterface|hasPilotCamera|hasWeapon|hcAllGroups|hcGroupParams|hcLeader|hcRemoveAllGroups|hcRemoveGroup|hcSelected|hcSelectGroup|hcSetGroup|hcShowBar|hcShownBar|headgear|hideBody|hideObject|hideObjectGlobal|hideSelection|hint|hintC|hintCadet|hintSilent|hmd|hostMission|htmlLoad|HUDMovementLevels|humidity|image|importAllGroups|importance|in|inArea|inAreaArray|incapacitatedState|independent|inflame|inflamed|infoPanel|infoPanelComponentEnabled|infoPanelComponents|infoPanels|inGameUISetEventHandler|inheritsFrom|initAmbientLife|inPolygon|inputAction|inRangeOfArtillery|insertEditorObject|intersect|is3DEN|is3DENMultiplayer|isAbleToBreathe|isAgent|isAimPrecisionEnabled|isArray|isAutoHoverOn|isAutonomous|isAutoStartUpEnabledRTD|isAutotest|isAutoTrimOnRTD|isBleeding|isBurning|isClass|isCollisionLightOn|isCopilotEnabled|isDamageAllowed|isDedicated|isDLCAvailable|isEngineOn|isEqualTo|isEqualType|isEqualTypeAll|isEqualTypeAny|isEqualTypeArray|isEqualTypeParams|isFilePatchingEnabled|isFlashlightOn|isFlatEmpty|isForcedWalk|isFormationLeader|isGroupDeletedWhenEmpty|isHidden|isInRemainsCollector|isInstructorFigureEnabled|isIRLaserOn|isKeyActive|isKindOf|isLaserOn|isLightOn|isLocalized|isManualFire|isMarkedForCollection|isMultiplayer|isMultiplayerSolo|isNil|isNull|isNumber|isObjectHidden|isObjectRTD|isOnRoad|isPipEnabled|isPlayer|isRealTime|isRemoteExecuted|isRemoteExecutedJIP|isServer|isShowing3DIcons|isSimpleObject|isSprintAllowed|isStaminaEnabled|isSteamMission|isStreamFriendlyUIEnabled|isStressDamageEnabled|isText|isTouchingGround|isTurnedOut|isTutHintsEnabled|isUAVConnectable|isUAVConnected|isUIContext|isUniformAllowed|isVehicleCargo|isVehicleRadarOn|isVehicleSensorEnabled|isWalking|isWeaponDeployed|isWeaponRested|itemCargo|items|itemsWithMagazines|join|joinAs|joinAsSilent|joinSilent|joinString|kbAddDatabase|kbAddDatabaseTargets|kbAddTopic|kbHasTopic|kbReact|kbRemoveTopic|kbTell|kbWasSaid|keyImage|keyName|knowsAbout|land|landAt|landResult|language|laserTarget|lbAdd|lbClear|lbColor|lbColorRight|lbCurSel|lbData|lbDelete|lbIsSelected|lbPicture|lbPictureRight|lbSelection|lbSetColor|lbSetColorRight|lbSetCurSel|lbSetData|lbSetPicture|lbSetPictureColor|lbSetPictureColorDisabled|lbSetPictureColorSelected|lbSetPictureRight|lbSetPictureRightColor|lbSetPictureRightColorDisabled|lbSetPictureRightColorSelected|lbSetSelectColor|lbSetSelectColorRight|lbSetSelected|lbSetText|lbSetTextRight|lbSetTooltip|lbSetValue|lbSize|lbSort|lbSortByValue|lbText|lbTextRight|lbValue|leader|leaderboardDeInit|leaderboardGetRows|leaderboardInit|leaderboardRequestRowsFriends|leaderboardRequestRowsGlobal|leaderboardRequestRowsGlobalAroundUser|leaderboardsRequestUploadScore|leaderboardsRequestUploadScoreKeepBest|leaderboardState|leaveVehicle|libraryCredits|libraryDisclaimers|lifeState|lightAttachObject|lightDetachObject|lightIsOn|lightnings|limitSpeed|linearConversion|lineBreak|lineIntersects|lineIntersectsObjs|lineIntersectsSurfaces|lineIntersectsWith|linkItem|list|listObjects|listRemoteTargets|listVehicleSensors|ln|lnbAddArray|lnbAddColumn|lnbAddRow|lnbClear|lnbColor|lnbColorRight|lnbCurSelRow|lnbData|lnbDeleteColumn|lnbDeleteRow|lnbGetColumnsPosition|lnbPicture|lnbPictureRight|lnbSetColor|lnbSetColorRight|lnbSetColumnsPos|lnbSetCurSelRow|lnbSetData|lnbSetPicture|lnbSetPictureColor|lnbSetPictureColorRight|lnbSetPictureColorSelected|lnbSetPictureColorSelectedRight|lnbSetPictureRight|lnbSetText|lnbSetTextRight|lnbSetValue|lnbSize|lnbSort|lnbSortByValue|lnbText|lnbTextRight|lnbValue|load|loadAbs|loadBackpack|loadFile|loadGame|loadIdentity|loadMagazine|loadOverlay|loadStatus|loadUniform|loadVest|local|localize|locationNull|locationPosition|lock|lockCameraTo|lockCargo|lockDriver|locked|lockedCargo|lockedDriver|lockedTurret|lockIdentity|lockTurret|lockWP|log|logEntities|logNetwork|logNetworkTerminate|lookAt|lookAtPos|magazineCargo|magazines|magazinesAllTurrets|magazinesAmmo|magazinesAmmoCargo|magazinesAmmoFull|magazinesDetail|magazinesDetailBackpack|magazinesDetailUniform|magazinesDetailVest|magazinesTurret|magazineTurretAmmo|mapAnimAdd|mapAnimClear|mapAnimCommit|mapAnimDone|mapCenterOnCamera|mapGridPosition|markAsFinishedOnSteam|markerAlpha|markerBrush|markerColor|markerDir|markerPos|markerShape|markerSize|markerText|markerType|max|members|menuAction|menuAdd|menuChecked|menuClear|menuCollapse|menuData|menuDelete|menuEnable|menuEnabled|menuExpand|menuHover|menuPicture|menuSetAction|menuSetCheck|menuSetData|menuSetPicture|menuSetValue|menuShortcut|menuShortcutText|menuSize|menuSort|menuText|menuURL|menuValue|min|mineActive|mineDetectedBy|missionConfigFile|missionDifficulty|missionName|missionNamespace|missionStart|missionVersion|modelToWorld|modelToWorldVisual|modelToWorldVisualWorld|modelToWorldWorld|modParams|moonIntensity|moonPhase|morale|move|move3DENCamera|moveInAny|moveInCargo|moveInCommander|moveInDriver|moveInGunner|moveInTurret|moveObjectToEnd|moveOut|moveTime|moveTo|moveToCompleted|moveToFailed|musicVolume|name|nameSound|nearEntities|nearestBuilding|nearestLocation|nearestLocations|nearestLocationWithDubbing|nearestObject|nearestObjects|nearestTerrainObjects|nearObjects|nearObjectsReady|nearRoads|nearSupplies|nearTargets|needReload|netId|netObjNull|newOverlay|nextMenuItemIndex|nextWeatherChange|nMenuItems|numberOfEnginesRTD|numberToDate|objectCurators|objectFromNetId|objectParent|objNull|objStatus|onBriefingGear|onBriefingGroup|onBriefingNotes|onBriefingPlan|onBriefingTeamSwitch|onCommandModeChanged|onDoubleClick|onEachFrame|onGroupIconClick|onGroupIconOverEnter|onGroupIconOverLeave|onHCGroupSelectionChanged|onMapSingleClick|onPlayerConnected|onPlayerDisconnected|onPreloadFinished|onPreloadStarted|onShowNewObject|onTeamSwitch|openCuratorInterface|openDLCPage|openDSInterface|openMap|openSteamApp|openYoutubeVideo|opfor|orderGetIn|overcast|overcastForecast|owner|param|params|parseNumber|parseSimpleArray|parseText|parsingNamespace|particlesQuality|pi|pickWeaponPool|pitch|pixelGrid|pixelGridBase|pixelGridNoUIScale|pixelH|pixelW|playableSlotsNumber|playableUnits|playAction|playActionNow|player|playerRespawnTime|playerSide|playersNumber|playGesture|playMission|playMove|playMoveNow|playMusic|playScriptedMission|playSound|playSound3D|position|positionCameraToWorld|posScreenToWorld|posWorldToScreen|ppEffectAdjust|ppEffectCommit|ppEffectCommitted|ppEffectCreate|ppEffectDestroy|ppEffectEnable|ppEffectEnabled|ppEffectForceInNVG|precision|preloadCamera|preloadObject|preloadSound|preloadTitleObj|preloadTitleRsc|primaryWeapon|primaryWeaponItems|primaryWeaponMagazine|priority|processDiaryLink|processInitCommands|productVersion|profileName|profileNamespace|profileNameSteam|progressLoadingScreen|progressPosition|progressSetPosition|publicVariable|publicVariableClient|publicVariableServer|pushBack|pushBackUnique|putWeaponPool|queryItemsPool|queryMagazinePool|queryWeaponPool|rad|radioChannelAdd|radioChannelCreate|radioChannelRemove|radioChannelSetCallSign|radioChannelSetLabel|radioVolume|rain|rainbow|random|rank|rankId|rating|rectangular|registeredTasks|registerTask|reload|reloadEnabled|remoteControl|remoteExec|remoteExecCall|remoteExecutedOwner|remove3DENConnection|remove3DENEventHandler|remove3DENLayer|removeAction|removeAll3DENEventHandlers|removeAllActions|removeAllAssignedItems|removeAllContainers|removeAllCuratorAddons|removeAllCuratorCameraAreas|removeAllCuratorEditingAreas|removeAllEventHandlers|removeAllHandgunItems|removeAllItems|removeAllItemsWithMagazines|removeAllMissionEventHandlers|removeAllMPEventHandlers|removeAllMusicEventHandlers|removeAllOwnedMines|removeAllPrimaryWeaponItems|removeAllWeapons|removeBackpack|removeBackpackGlobal|removeCuratorAddons|removeCuratorCameraArea|removeCuratorEditableObjects|removeCuratorEditingArea|removeDrawIcon|removeDrawLinks|removeEventHandler|removeFromRemainsCollector|removeGoggles|removeGroupIcon|removeHandgunItem|removeHeadgear|removeItem|removeItemFromBackpack|removeItemFromUniform|removeItemFromVest|removeItems|removeMagazine|removeMagazineGlobal|removeMagazines|removeMagazinesTurret|removeMagazineTurret|removeMenuItem|removeMissionEventHandler|removeMPEventHandler|removeMusicEventHandler|removeOwnedMine|removePrimaryWeaponItem|removeSecondaryWeaponItem|removeSimpleTask|removeSwitchableUnit|removeTeamMember|removeUniform|removeVest|removeWeapon|removeWeaponAttachmentCargo|removeWeaponCargo|removeWeaponGlobal|removeWeaponTurret|reportRemoteTarget|requiredVersion|resetCamShake|resetSubgroupDirection|resistance|resize|resources|respawnVehicle|restartEditorCamera|reveal|revealMine|reverse|reversedMouseY|roadAt|roadsConnectedTo|roleDescription|ropeAttachedObjects|ropeAttachedTo|ropeAttachEnabled|ropeAttachTo|ropeCreate|ropeCut|ropeDestroy|ropeDetach|ropeEndPosition|ropeLength|ropes|ropeUnwind|ropeUnwound|rotorsForcesRTD|rotorsRpmRTD|round|runInitScript|safeZoneH|safeZoneW|safeZoneWAbs|safeZoneX|safeZoneXAbs|safeZoneY|save3DENInventory|saveGame|saveIdentity|saveJoysticks|saveOverlay|saveProfileNamespace|saveStatus|saveVar|savingEnabled|say|say2D|say3D|score|scoreSide|screenshot|screenToWorld|scriptDone|scriptName|scriptNull|scudState|secondaryWeapon|secondaryWeaponItems|secondaryWeaponMagazine|select|selectBestPlaces|selectDiarySubject|selectedEditorObjects|selectEditorObject|selectionNames|selectionPosition|selectLeader|selectMax|selectMin|selectNoPlayer|selectPlayer|selectRandom|selectRandomWeighted|selectWeapon|selectWeaponTurret|sendAUMessage|sendSimpleCommand|sendTask|sendTaskResult|sendUDPMessage|serverCommand|serverCommandAvailable|serverCommandExecutable|serverName|serverTime|set|set3DENAttribute|set3DENAttributes|set3DENGrid|set3DENIconsVisible|set3DENLayer|set3DENLinesVisible|set3DENLogicType|set3DENMissionAttribute|set3DENMissionAttributes|set3DENModelsVisible|set3DENObjectType|set3DENSelected|setAccTime|setActualCollectiveRTD|setAirplaneThrottle|setAirportSide|setAmmo|setAmmoCargo|setAmmoOnPylon|setAnimSpeedCoef|setAperture|setApertureNew|setArmoryPoints|setAttributes|setAutonomous|setBehaviour|setBleedingRemaining|setBrakesRTD|setCameraInterest|setCamShakeDefParams|setCamShakeParams|setCamUseTI|setCaptive|setCenterOfMass|setCollisionLight|setCombatMode|setCompassOscillation|setConvoySeparation|setCuratorCameraAreaCeiling|setCuratorCoef|setCuratorEditingAreaType|setCuratorWaypointCost|setCurrentChannel|setCurrentTask|setCurrentWaypoint|setCustomAimCoef|setCustomWeightRTD|setDamage|setDammage|setDate|setDebriefingText|setDefaultCamera|setDestination|setDetailMapBlendPars|setDir|setDirection|setDrawIcon|setDriveOnPath|setDropInterval|setDynamicSimulationDistance|setDynamicSimulationDistanceCoef|setEditorMode|setEditorObjectScope|setEffectCondition|setEngineRpmRTD|setFace|setFaceAnimation|setFatigue|setFeatureType|setFlagAnimationPhase|setFlagOwner|setFlagSide|setFlagTexture|setFog|setForceGeneratorRTD|setFormation|setFormationTask|setFormDir|setFriend|setFromEditor|setFSMVariable|setFuel|setFuelCargo|setGroupIcon|setGroupIconParams|setGroupIconsSelectable|setGroupIconsVisible|setGroupId|setGroupIdGlobal|setGroupOwner|setGusts|setHideBehind|setHit|setHitIndex|setHitPointDamage|setHorizonParallaxCoef|setHUDMovementLevels|setIdentity|setImportance|setInfoPanel|setLeader|setLightAmbient|setLightAttenuation|setLightBrightness|setLightColor|setLightDayLight|setLightFlareMaxDistance|setLightFlareSize|setLightIntensity|setLightnings|setLightUseFlare|setLocalWindParams|setMagazineTurretAmmo|setMarkerAlpha|setMarkerAlphaLocal|setMarkerBrush|setMarkerBrushLocal|setMarkerColor|setMarkerColorLocal|setMarkerDir|setMarkerDirLocal|setMarkerPos|setMarkerPosLocal|setMarkerShape|setMarkerShapeLocal|setMarkerSize|setMarkerSizeLocal|setMarkerText|setMarkerTextLocal|setMarkerType|setMarkerTypeLocal|setMass|setMimic|setMousePosition|setMusicEffect|setMusicEventHandler|setName|setNameSound|setObjectArguments|setObjectMaterial|setObjectMaterialGlobal|setObjectProxy|setObjectTexture|setObjectTextureGlobal|setObjectViewDistance|setOvercast|setOwner|setOxygenRemaining|setParticleCircle|setParticleClass|setParticleFire|setParticleParams|setParticleRandom|setPilotCameraDirection|setPilotCameraRotation|setPilotCameraTarget|setPilotLight|setPiPEffect|setPitch|setPlateNumber|setPlayable|setPlayerRespawnTime|setPos|setPosASL|setPosASL2|setPosASLW|setPosATL|setPosition|setPosWorld|setPylonLoadOut|setPylonsPriority|setRadioMsg|setRain|setRainbow|setRandomLip|setRank|setRectangular|setRepairCargo|setRotorBrakeRTD|setShadowDistance|setShotParents|setSide|setSimpleTaskAlwaysVisible|setSimpleTaskCustomData|setSimpleTaskDescription|setSimpleTaskDestination|setSimpleTaskTarget|setSimpleTaskType|setSimulWeatherLayers|setSize|setSkill|setSlingLoad|setSoundEffect|setSpeaker|setSpeech|setSpeedMode|setStamina|setStaminaScheme|setStatValue|setSuppression|setSystemOfUnits|setTargetAge|setTaskMarkerOffset|setTaskResult|setTaskState|setTerrainGrid|setText|setTimeMultiplier|setTitleEffect|setToneMapping|setToneMappingParams|setTrafficDensity|setTrafficDistance|setTrafficGap|setTrafficSpeed|setTriggerActivation|setTriggerArea|setTriggerStatements|setTriggerText|setTriggerTimeout|setTriggerType|setType|setUnconscious|setUnitAbility|setUnitLoadout|setUnitPos|setUnitPosWeak|setUnitRank|setUnitRecoilCoefficient|setUnitTrait|setUnloadInCombat|setUserActionText|setUserMFDText|setUserMFDValue|setVariable|setVectorDir|setVectorDirAndUp|setVectorUp|setVehicleAmmo|setVehicleAmmoDef|setVehicleArmor|setVehicleCargo|setVehicleId|setVehicleInit|setVehicleLock|setVehiclePosition|setVehicleRadar|setVehicleReceiveRemoteTargets|setVehicleReportOwnPosition|setVehicleReportRemoteTargets|setVehicleTIPars|setVehicleVarName|setVelocity|setVelocityModelSpace|setVelocityTransformation|setViewDistance|setVisibleIfTreeCollapsed|setWantedRpmRTD|setWaves|setWaypointBehaviour|setWaypointCombatMode|setWaypointCompletionRadius|setWaypointDescription|setWaypointForceBehaviour|setWaypointFormation|setWaypointHousePosition|setWaypointLoiterRadius|setWaypointLoiterType|setWaypointName|setWaypointPosition|setWaypointScript|setWaypointSpeed|setWaypointStatements|setWaypointTimeout|setWaypointType|setWaypointVisible|setWeaponReloadingTime|setWind|setWindDir|setWindForce|setWindStr|setWingForceScaleRTD|setWPPos|show3DIcons|showChat|showCinemaBorder|showCommandingMenu|showCompass|showCuratorCompass|showGPS|showHUD|showLegend|showMap|shownArtilleryComputer|shownChat|shownCompass|shownCuratorCompass|showNewEditorObject|shownGPS|shownHUD|shownMap|shownPad|shownRadio|shownScoretable|shownUAVFeed|shownWarrant|shownWatch|showPad|showRadio|showScoretable|showSubtitles|showUAVFeed|showWarrant|showWatch|showWaypoint|showWaypoints|side|sideAmbientLife|sideChat|sideEmpty|sideEnemy|sideFriendly|sideLogic|sideRadio|sideUnknown|simpleTasks|simulationEnabled|simulCloudDensity|simulCloudOcclusion|simulInClouds|simulWeatherSync|sin|size|sizeOf|skill|skillFinal|skipTime|sleep|sliderPosition|sliderRange|sliderSetPosition|sliderSetRange|sliderSetSpeed|sliderSpeed|slingLoadAssistantShown|soldierMagazines|someAmmo|sort|soundVolume|speaker|speed|speedMode|splitString|sqrt|squadParams|stance|startLoadingScreen|stop|stopEngineRTD|stopped|str|sunOrMoon|supportInfo|suppressFor|surfaceIsWater|surfaceNormal|surfaceType|swimInDepth|switchableUnits|switchAction|switchCamera|switchGesture|switchLight|switchMove|synchronizedObjects|synchronizedTriggers|synchronizedWaypoints|synchronizeObjectsAdd|synchronizeObjectsRemove|synchronizeTrigger|synchronizeWaypoint|systemChat|systemOfUnits|tan|targetKnowledge|targets|targetsAggregate|targetsQuery|taskAlwaysVisible|taskChildren|taskCompleted|taskCustomData|taskDescription|taskDestination|taskHint|taskMarkerOffset|taskNull|taskParent|taskResult|taskState|taskType|teamMember|teamMemberNull|teamName|teams|teamSwitch|teamSwitchEnabled|teamType|terminate|terrainIntersect|terrainIntersectASL|terrainIntersectAtASL|text|textLog|textLogFormat|tg|time|timeMultiplier|titleCut|titleFadeOut|titleObj|titleRsc|titleText|toArray|toFixed|toLower|toString|toUpper|triggerActivated|triggerActivation|triggerArea|triggerAttachedVehicle|triggerAttachObject|triggerAttachVehicle|triggerDynamicSimulation|triggerStatements|triggerText|triggerTimeout|triggerTimeoutCurrent|triggerType|turretLocal|turretOwner|turretUnit|tvAdd|tvClear|tvCollapse|tvCollapseAll|tvCount|tvCurSel|tvData|tvDelete|tvExpand|tvExpandAll|tvPicture|tvPictureRight|tvSetColor|tvSetCurSel|tvSetData|tvSetPicture|tvSetPictureColor|tvSetPictureColorDisabled|tvSetPictureColorSelected|tvSetPictureRight|tvSetPictureRightColor|tvSetPictureRightColorDisabled|tvSetPictureRightColorSelected|tvSetSelectColor|tvSetText|tvSetTooltip|tvSetValue|tvSort|tvSortByValue|tvText|tvTooltip|tvValue|type|typeName|typeOf|UAVControl|uiNamespace|uiSleep|unassignCurator|unassignItem|unassignTeam|unassignVehicle|underwater|uniform|uniformContainer|uniformItems|uniformMagazines|unitAddons|unitAimPosition|unitAimPositionVisual|unitBackpack|unitIsUAV|unitPos|unitReady|unitRecoilCoefficient|units|unitsBelowHeight|unlinkItem|unlockAchievement|unregisterTask|updateDrawIcon|updateMenuItem|updateObjectTree|useAIOperMapObstructionTest|useAISteeringComponent|useAudioTimeForMoves|userInputDisabled|vectorAdd|vectorCos|vectorCrossProduct|vectorDiff|vectorDir|vectorDirVisual|vectorDistance|vectorDistanceSqr|vectorDotProduct|vectorFromTo|vectorMagnitude|vectorMagnitudeSqr|vectorModelToWorld|vectorModelToWorldVisual|vectorMultiply|vectorNormalized|vectorUp|vectorUpVisual|vectorWorldToModel|vectorWorldToModelVisual|vehicle|vehicleCargoEnabled|vehicleChat|vehicleRadio|vehicleReceiveRemoteTargets|vehicleReportOwnPosition|vehicleReportRemoteTargets|vehicles|vehicleVarName|velocity|velocityModelSpace|verifySignature|vest|vestContainer|vestItems|vestMagazines|viewDistance|visibleCompass|visibleGPS|visibleMap|visiblePosition|visiblePositionASL|visibleScoretable|visibleWatch|waitUntil|waves|waypointAttachedObject|waypointAttachedVehicle|waypointAttachObject|waypointAttachVehicle|waypointBehaviour|waypointCombatMode|waypointCompletionRadius|waypointDescription|waypointForceBehaviour|waypointFormation|waypointHousePosition|waypointLoiterRadius|waypointLoiterType|waypointName|waypointPosition|waypoints|waypointScript|waypointsEnabledUAV|waypointShow|waypointSpeed|waypointStatements|waypointTimeout|waypointTimeoutCurrent|waypointType|waypointVisible|weaponAccessories|weaponAccessoriesCargo|weaponCargo|weaponDirection|weaponInertia|weaponLowered|weapons|weaponsItems|weaponsItemsCargo|weaponState|weaponsTurret|weightRTD|west|WFSideText|wind|windDir|windRTD|windStr|wingsForcesRTD|worldName|worldSize|worldToModel|worldToModelVisual|worldToScreen)\b/i,number:/(?:\$|\b0x)[\da-f]+\b|(?:\B\.\d+|\b\d+(?:\.\d+)?)(?:e[+-]?\d+)?\b/i,operator:/##|>>|&&|\|\||[!=<>]=?|[-+*/%#^]|\b(?:and|mod|not|or)\b/i,"magic-variable":{pattern:/\b(?:_exception|_fnc_scriptName|_fnc_scriptNameParent|_forEachIndex|_this|_thisEventHandler|_thisFSM|_thisScript|_x|this|thisList|thisTrigger)\b/i,alias:"keyword"},constant:/\bDIK(?:_[a-z\d]+)+\b/i}),e.languages.insertBefore("sqf","string",{macro:{pattern:/(^[ \t]*)#[a-z](?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{directive:{pattern:/#[a-z]+\b/i,alias:"keyword"},comment:e.languages.sqf.comment}}}),delete e.languages.sqf["class-name"]}e.exports=t,t.displayName="sqf",t.aliases=[]},11114(e){"use strict";function t(e){e.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:S|ING)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:TRUE|FALSE|NULL)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|IN|ILIKE|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/}}e.exports=t,t.displayName="sql",t.aliases=[]},67386(e){"use strict";function t(e){e.languages.squirrel=e.languages.extend("clike",{comment:[e.languages.clike.comment[0],{pattern:/(^|[^\\:])(?:\/\/|#).*/,lookbehind:!0,greedy:!0}],string:[{pattern:/(^|[^\\"'@])(?:@"(?:[^"]|"")*"(?!")|"(?:[^\\\r\n"]|\\.)*")/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\"'])'(?:[^\\']|\\(?:[xuU][0-9a-fA-F]{0,8}|[\s\S]))'/,lookbehind:!0,greedy:!0}],"class-name":{pattern:/(\b(?:class|enum|extends|instanceof)\s+)\w+(?:\.\w+)*/,lookbehind:!0,inside:{punctuation:/\./}},keyword:/\b(?:base|break|case|catch|class|clone|const|constructor|continue|default|delete|else|enum|extends|for|foreach|function|if|in|instanceof|local|null|resume|return|static|switch|this|throw|try|typeof|while|yield|__LINE__|__FILE__)\b/,number:/\b(?:0x[0-9a-fA-F]+|\d+(?:\.(?:\d+|[eE][+-]?\d+))?)\b/,operator:/\+\+|--|<=>|<[-<]|>>>?|&&?|\|\|?|[-+*/%!=<>]=?|[~^]|::?/,punctuation:/[(){}\[\],;.]/}),e.languages.insertBefore("squirrel","operator",{"attribute-punctuation":{pattern:/<\/|\/>/,alias:"important"},lambda:{pattern:/@(?=\()/,alias:"operator"}})}e.exports=t,t.displayName="squirrel",t.aliases=[]},28067(e){"use strict";function t(e){e.languages.stan={comment:/\/\/.*|\/\*[\s\S]*?\*\/|#(?!include).*/,string:{pattern:/"[\x20\x21\x23-\x5B\x5D-\x7E]*"/,greedy:!0},directive:{pattern:/^([ \t]*)#include\b.*/m,lookbehind:!0,alias:"property"},"function-arg":{pattern:/(\b(?:algebra_solver|integrate_1d|integrate_ode|integrate_ode_bdf|integrate_ode_rk45|map_rect)\s*\(\s*)[a-zA-Z]\w*/,lookbehind:!0,alias:"function"},constraint:{pattern:/(\b(?:int|matrix|real|row_vector|vector)\s*)<[^<>]*>/,lookbehind:!0,inside:{expression:{pattern:/(=\s*)\S(?:\S|\s+(?!\s))*?(?=\s*(?:>$|,\s*\w+\s*=))/,lookbehind:!0,inside:null},property:/\b[a-z]\w*(?=\s*=)/i,operator:/=/,punctuation:/^<|>$|,/}},keyword:[/\b(?:break|cholesky_factor_corr|cholesky_factor_cov|continue|corr_matrix|cov_matrix|data|else|for|functions|generated|if|in|increment_log_prob|int|matrix|model|ordered|parameters|positive_ordered|print|quantities|real|reject|return|row_vector|simplex|target|transformed|unit_vector|vector|void|while)\b/,/\b(?:algebra_solver|integrate_1d|integrate_ode|integrate_ode_bdf|integrate_ode_rk45|map_rect)\b/],function:/\b[a-z]\w*(?=\s*\()/i,number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:E[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,operator:/<-|\.[*/]=?|\|\|?|&&|[!=<>+\-*/]=?|['^%~?:]/,punctuation:/[()\[\]{},;]/},e.languages.stan.constraint.inside.expression.inside=e.languages.stan}e.exports=t,t.displayName="stan",t.aliases=[]},49168(e){"use strict";function t(e){var t,n,r,i;t=e,(i={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:if|else|for|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:rgb|hsl)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:rgb|hsl)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:n={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},number:r={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:n,boolean:/\b(?:true|false)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:r,punctuation:/[{}()\[\];:,]/}).interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:i}},i.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:i}},t.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:i}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:i}},statement:{pattern:/(^[ \t]*)(?:if|else|for|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:i}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:i.interpolation}},rest:i}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:i.interpolation,comment:i.comment,punctuation:/[{},]/}},func:i.func,string:i.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:i.interpolation,punctuation:/[{}()\[\];:.]/}}e.exports=t,t.displayName="stylus",t.aliases=[]},23651(e){"use strict";function t(e){e.languages.swift=e.languages.extend("clike",{string:{pattern:/("|')(?:\\(?:\((?:[^()]|\([^)]+\))+\)|\r\n|[^(])|(?!\1)[^\\\r\n])*\1/,greedy:!0,inside:{interpolation:{pattern:/\\\((?:[^()]|\([^)]+\))+\)/,inside:{delimiter:{pattern:/^\\\(|\)$/,alias:"variable"}}}}},keyword:/\b(?:as|associativity|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic(?:Type)?|else|enum|extension|fallthrough|final|for|func|get|guard|if|import|in|infix|init|inout|internal|is|lazy|left|let|mutating|new|none|nonmutating|operator|optional|override|postfix|precedence|prefix|private|protocol|public|repeat|required|rethrows|return|right|safe|self|Self|set|some|static|struct|subscript|super|switch|throws?|try|Type|typealias|unowned|unsafe|var|weak|where|while|willSet|__(?:COLUMN__|FILE__|FUNCTION__|LINE__))\b/,number:/\b(?:[\d_]+(?:\.[\de_]+)?|0x[a-f0-9_]+(?:\.[a-f0-9p_]+)?|0b[01_]+|0o[0-7_]+)\b/i,constant:/\b(?:nil|[A-Z_]{2,}|k[A-Z][A-Za-z_]+)\b/,atrule:/@\b(?:IB(?:Outlet|Designable|Action|Inspectable)|class_protocol|exported|noreturn|NS(?:Copying|Managed)|objc|UIApplicationMain|auto_closure)\b/,builtin:/\b(?:[A-Z]\S+|abs|advance|alignof(?:Value)?|assert|contains|count(?:Elements)?|debugPrint(?:ln)?|distance|drop(?:First|Last)|dump|enumerate|equal|filter|find|first|getVaList|indices|isEmpty|join|last|lexicographicalCompare|map|max(?:Element)?|min(?:Element)?|numericCast|overlaps|partition|print(?:ln)?|reduce|reflect|reverse|sizeof(?:Value)?|sort(?:ed)?|split|startsWith|stride(?:of(?:Value)?)?|suffix|swap|toDebugString|toString|transcode|underestimateCount|unsafeBitCast|with(?:ExtendedLifetime|Unsafe(?:MutablePointers?|Pointers?)|VaList))\b/}),e.languages.swift.string.inside.interpolation.inside.rest=e.languages.swift}e.exports=t,t.displayName="swift",t.aliases=[]},32268(e,t,n){"use strict";var r=n(2329),i=n(61958);function a(e){e.register(r),e.register(i),e.languages.t4=e.languages["t4-cs"]=e.languages["t4-templating"].createT4("csharp")}e.exports=a,a.displayName="t4Cs",a.aliases=[]},2329(e){"use strict";function t(e){!function(e){function t(e,t,n){return{pattern:RegExp("<#"+e+"[\\s\\S]*?#>"),alias:"block",inside:{delimiter:{pattern:RegExp("^<#"+e+"|#>$"),alias:"important"},content:{pattern:/[\s\S]+/,inside:t,alias:n}}}}function n(n){var r=e.languages[n],i="language-"+n;return{block:{pattern:/<#[\s\S]+?#>/,inside:{directive:t("@",{"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/,inside:{punctuation:/^=|^["']|["']$/}},keyword:/\b\w+(?=\s)/,"attr-name":/\b\w+/}),expression:t("=",r,i),"class-feature":t("\\+",r,i),standard:t("",r,i)}}}}e.languages["t4-templating"]=Object.defineProperty({},"createT4",{value:n})}(e)}e.exports=t,t.displayName="t4Templating",t.aliases=[]},82996(e,t,n){"use strict";var r=n(2329),i=n(53813);function a(e){e.register(r),e.register(i),e.languages["t4-vb"]=e.languages["t4-templating"].createT4("vbnet")}e.exports=a,a.displayName="t4Vb",a.aliases=[]},17290(e,t,n){"use strict";var r=n(65039);function i(e){e.register(r),e.languages.tap={fail:/not ok[^#{\n\r]*/,pass:/ok[^#{\n\r]*/,pragma:/pragma [+-][a-z]+/,bailout:/bail out!.*/i,version:/TAP version \d+/i,plan:/\b\d+\.\.\d+(?: +#.*)?/,subtest:{pattern:/# Subtest(?:: .*)?/,greedy:!0},punctuation:/[{}]/,directive:/#.*/,yamlish:{pattern:/(^[ \t]*)---[\s\S]*?[\r\n][ \t]*\.\.\.$/m,lookbehind:!0,inside:e.languages.yaml,alias:"language-yaml"}}}e.exports=i,i.displayName="tap",i.aliases=[]},67989(e){"use strict";function t(e){e.languages.tcl={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:{pattern:/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"/,greedy:!0},variable:[{pattern:/(\$)(?:::)?(?:[a-zA-Z0-9]+::)*\w+/,lookbehind:!0},{pattern:/(\$)\{[^}]+\}/,lookbehind:!0},{pattern:/(^[\t ]*set[ \t]+)(?:::)?(?:[a-zA-Z0-9]+::)*\w+/m,lookbehind:!0}],function:{pattern:/(^[\t ]*proc[ \t]+)\S+/m,lookbehind:!0},builtin:[{pattern:/(^[\t ]*)(?:proc|return|class|error|eval|exit|for|foreach|if|switch|while|break|continue)\b/m,lookbehind:!0},/\b(?:elseif|else)\b/],scope:{pattern:/(^[\t ]*)(?:global|upvar|variable)\b/m,lookbehind:!0,alias:"constant"},keyword:{pattern:/(^[\t ]*|\[)(?:after|append|apply|array|auto_(?:execok|import|load|mkindex|qualify|reset)|automkindex_old|bgerror|binary|catch|cd|chan|clock|close|concat|dde|dict|encoding|eof|exec|expr|fblocked|fconfigure|fcopy|file(?:event|name)?|flush|gets|glob|history|http|incr|info|interp|join|lappend|lassign|lindex|linsert|list|llength|load|lrange|lrepeat|lreplace|lreverse|lsearch|lset|lsort|math(?:func|op)|memory|msgcat|namespace|open|package|parray|pid|pkg_mkIndex|platform|puts|pwd|re_syntax|read|refchan|regexp|registry|regsub|rename|Safe_Base|scan|seek|set|socket|source|split|string|subst|Tcl|tcl(?:_endOfWord|_findLibrary|startOf(?:Next|Previous)Word|wordBreak(?:After|Before)|test|vars)|tell|time|tm|trace|unknown|unload|unset|update|uplevel|vwait)\b/m,lookbehind:!0},operator:/!=?|\*\*?|==|&&?|\|\|?|<[=<]?|>[=>]?|[-+~\/%?^]|\b(?:eq|ne|in|ni)\b/,punctuation:/[{}()\[\]]/}}e.exports=t,t.displayName="tcl",t.aliases=[]},31065(e){"use strict";function t(e){!function(e){var t=/\([^|()\n]+\)|\[[^\]\n]+\]|\{[^}\n]+\}/.source,n=/\)|\((?![^|()\n]+\))/.source;function r(e,r){return RegExp(e.replace(//g,function(){return"(?:"+t+")"}).replace(//g,function(){return"(?:"+n+")"}),r||"")}var i={css:{pattern:/\{[^{}]+\}/,inside:{rest:e.languages.css}},"class-id":{pattern:/(\()[^()]+(?=\))/,lookbehind:!0,alias:"attr-value"},lang:{pattern:/(\[)[^\[\]]+(?=\])/,lookbehind:!0,alias:"attr-value"},punctuation:/[\\\/]\d+|\S/},a=e.languages.textile=e.languages.extend("markup",{phrase:{pattern:/(^|\r|\n)\S[\s\S]*?(?=$|\r?\n\r?\n|\r\r)/,lookbehind:!0,inside:{"block-tag":{pattern:r(/^[a-z]\w*(?:||[<>=])*\./.source),inside:{modifier:{pattern:r(/(^[a-z]\w*)(?:||[<>=])+(?=\.)/.source),lookbehind:!0,inside:i},tag:/^[a-z]\w*/,punctuation:/\.$/}},list:{pattern:r(/^[*#]+*\s+\S.*/.source,"m"),inside:{modifier:{pattern:r(/(^[*#]+)+/.source),lookbehind:!0,inside:i},punctuation:/^[*#]+/}},table:{pattern:r(/^(?:(?:||[<>=^~])+\.\s*)?(?:\|(?:(?:||[<>=^~_]|[\\/]\d+)+\.|(?!(?:||[<>=^~_]|[\\/]\d+)+\.))[^|]*)+\|/.source,"m"),inside:{modifier:{pattern:r(/(^|\|(?:\r?\n|\r)?)(?:||[<>=^~_]|[\\/]\d+)+(?=\.)/.source),lookbehind:!0,inside:i},punctuation:/\||^\./}},inline:{pattern:r(/(^|[^a-zA-Z\d])(\*\*|__|\?\?|[*_%@+\-^~])*.+?\2(?![a-zA-Z\d])/.source),lookbehind:!0,inside:{bold:{pattern:r(/(^(\*\*?)*).+?(?=\2)/.source),lookbehind:!0},italic:{pattern:r(/(^(__?)*).+?(?=\2)/.source),lookbehind:!0},cite:{pattern:r(/(^\?\?*).+?(?=\?\?)/.source),lookbehind:!0,alias:"string"},code:{pattern:r(/(^@*).+?(?=@)/.source),lookbehind:!0,alias:"keyword"},inserted:{pattern:r(/(^\+*).+?(?=\+)/.source),lookbehind:!0},deleted:{pattern:r(/(^-*).+?(?=-)/.source),lookbehind:!0},span:{pattern:r(/(^%*).+?(?=%)/.source),lookbehind:!0},modifier:{pattern:r(/(^\*\*|__|\?\?|[*_%@+\-^~])+/.source),lookbehind:!0,inside:i},punctuation:/[*_%?@+\-^~]+/}},"link-ref":{pattern:/^\[[^\]]+\]\S+$/m,inside:{string:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0},url:{pattern:/(^\])\S+$/,lookbehind:!0},punctuation:/[\[\]]/}},link:{pattern:r(/"*[^"]+":.+?(?=[^\w/]?(?:\s|$))/.source),inside:{text:{pattern:r(/(^"*)[^"]+(?=")/.source),lookbehind:!0},modifier:{pattern:r(/(^")+/.source),lookbehind:!0,inside:i},url:{pattern:/(:).+/,lookbehind:!0},punctuation:/[":]/}},image:{pattern:r(/!(?:||[<>=])*(?![<>=])[^!\s()]+(?:\([^)]+\))?!(?::.+?(?=[^\w/]?(?:\s|$)))?/.source),inside:{source:{pattern:r(/(^!(?:||[<>=])*)(?![<>=])[^!\s()]+(?:\([^)]+\))?(?=!)/.source),lookbehind:!0,alias:"url"},modifier:{pattern:r(/(^!)(?:||[<>=])+/.source),lookbehind:!0,inside:i},url:{pattern:/(:).+/,lookbehind:!0},punctuation:/[!:]/}},footnote:{pattern:/\b\[\d+\]/,alias:"comment",inside:{punctuation:/\[|\]/}},acronym:{pattern:/\b[A-Z\d]+\([^)]+\)/,inside:{comment:{pattern:/(\()[^()]+(?=\))/,lookbehind:!0},punctuation:/[()]/}},mark:{pattern:/\b\((?:TM|R|C)\)/,alias:"comment",inside:{punctuation:/[()]/}}}}}),o=a.phrase.inside,s={inline:o.inline,link:o.link,image:o.image,footnote:o.footnote,acronym:o.acronym,mark:o.mark};a.tag.pattern=/<\/?(?!\d)[a-z0-9]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i;var u=o.inline.inside;u.bold.inside=s,u.italic.inside=s,u.inserted.inside=s,u.deleted.inside=s,u.span.inside=s;var c=o.table.inside;c.inline=s.inline,c.link=s.link,c.image=s.image,c.footnote=s.footnote,c.acronym=s.acronym,c.mark=s.mark}(e)}e.exports=t,t.displayName="textile",t.aliases=[]},85572(e){"use strict";function t(e){!function(e){var t=/(?:[\w-]+|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*")/.source;function n(e){return e.replace(/__/g,function(){return t})}e.languages.toml={comment:{pattern:/#.*/,greedy:!0},table:{pattern:RegExp(n(/(^[\t ]*\[\s*(?:\[\s*)?)__(?:\s*\.\s*__)*(?=\s*\])/.source),"m"),lookbehind:!0,greedy:!0,alias:"class-name"},key:{pattern:RegExp(n(/(^[\t ]*|[{,]\s*)__(?:\s*\.\s*__)*(?=\s*=)/.source),"m"),lookbehind:!0,greedy:!0,alias:"property"},string:{pattern:/"""(?:\\[\s\S]|[^\\])*?"""|'''[\s\S]*?'''|'[^'\n\r]*'|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},date:[{pattern:/\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?\b/i,alias:"number"},{pattern:/\b\d{2}:\d{2}:\d{2}(?:\.\d+)?\b/,alias:"number"}],number:/(?:\b0(?:x[\da-zA-Z]+(?:_[\da-zA-Z]+)*|o[0-7]+(?:_[0-7]+)*|b[10]+(?:_[10]+)*))\b|[-+]?\b\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?\b|[-+]?\b(?:inf|nan)\b/,boolean:/\b(?:true|false)\b/,punctuation:/[.,=[\]{}]/}}(e)}e.exports=t,t.displayName="toml",t.aliases=[]},87041(e,t,n){"use strict";var r=n(96412),i=n(4979);function a(e){var t,n,a;e.register(r),e.register(i),n=(t=e).util.clone(t.languages.typescript),t.languages.tsx=t.languages.extend("jsx",n),(a=t.languages.tsx.tag).pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+a.pattern.source+")",a.pattern.flags),a.lookbehind=!0}e.exports=a,a.displayName="tsx",a.aliases=[]},61028(e,t,n){"use strict";var r=n(93205);function i(e){var t;e.register(r),(t=e).languages.tt2=t.languages.extend("clike",{comment:/#.*|\[%#[\s\S]*?%\]/,keyword:/\b(?:BLOCK|CALL|CASE|CATCH|CLEAR|DEBUG|DEFAULT|ELSE|ELSIF|END|FILTER|FINAL|FOREACH|GET|IF|IN|INCLUDE|INSERT|LAST|MACRO|META|NEXT|PERL|PROCESS|RAWPERL|RETURN|SET|STOP|TAGS|THROW|TRY|SWITCH|UNLESS|USE|WHILE|WRAPPER)\b/,punctuation:/[[\]{},()]/}),t.languages.insertBefore("tt2","number",{operator:/=[>=]?|!=?|<=?|>=?|&&|\|\|?|\b(?:and|or|not)\b/,variable:{pattern:/\b[a-z]\w*(?:\s*\.\s*(?:\d+|\$?[a-z]\w*))*\b/i}}),t.languages.insertBefore("tt2","keyword",{delimiter:{pattern:/^(?:\[%|%%)-?|-?%\]$/,alias:"punctuation"}}),t.languages.insertBefore("tt2","string",{"single-quoted-string":{pattern:/'[^\\']*(?:\\[\s\S][^\\']*)*'/,greedy:!0,alias:"string"},"double-quoted-string":{pattern:/"[^\\"]*(?:\\[\s\S][^\\"]*)*"/,greedy:!0,alias:"string",inside:{variable:{pattern:/\$(?:[a-z]\w*(?:\.(?:\d+|\$?[a-z]\w*))*)/i}}}}),delete t.languages.tt2.string,t.hooks.add("before-tokenize",function(e){var n=/\[%[\s\S]+?%\]/g;t.languages["markup-templating"].buildPlaceholders(e,"tt2",n)}),t.hooks.add("after-tokenize",function(e){t.languages["markup-templating"].tokenizePlaceholders(e,"tt2")})}e.exports=i,i.displayName="tt2",i.aliases=[]},24691(e){"use strict";function t(e){e.languages.turtle={comment:{pattern:/#.*/,greedy:!0},"multiline-string":{pattern:/"""(?:(?:""?)?(?:[^"\\]|\\.))*"""|'''(?:(?:''?)?(?:[^'\\]|\\.))*'''/,greedy:!0,alias:"string",inside:{comment:/#.*/}},string:{pattern:/"(?:[^\\"\r\n]|\\.)*"|'(?:[^\\'\r\n]|\\.)*'/,greedy:!0},url:{pattern:/<(?:[^\x00-\x20<>"{}|^`\\]|\\(?:u[\da-fA-F]{4}|U[\da-fA-F]{8}))*>/,greedy:!0,inside:{punctuation:/[<>]/}},function:{pattern:/(?:(?![-.\d\xB7])[-.\w\xB7\xC0-\uFFFD]+)?:(?:(?![-.])(?:[-.:\w\xC0-\uFFFD]|%[\da-f]{2}|\\.)+)?/i,inside:{"local-name":{pattern:/([^:]*:)[\s\S]+/,lookbehind:!0},prefix:{pattern:/[\s\S]+/,inside:{punctuation:/:/}}}},number:/[+-]?\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,punctuation:/[{}.,;()[\]]|\^\^/,boolean:/\b(?:true|false)\b/,keyword:[/(?:\ba|@prefix|@base)\b|=/,/\b(?:graph|base|prefix)\b/i],tag:{pattern:/@[a-z]+(?:-[a-z\d]+)*/i,inside:{punctuation:/@/}}},e.languages.trig=e.languages.turtle}e.exports=t,t.displayName="turtle",t.aliases=[]},19892(e){"use strict";function t(e){e.languages.twig={comment:/\{#[\s\S]*?#\}/,tag:{pattern:/\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}/,inside:{ld:{pattern:/^(?:\{\{-?|\{%-?\s*\w+)/,inside:{punctuation:/^(?:\{\{|\{%)-?/,keyword:/\w+/}},rd:{pattern:/-?(?:%\}|\}\})$/,inside:{punctuation:/.+/}},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,inside:{punctuation:/^['"]|['"]$/}},keyword:/\b(?:even|if|odd)\b/,boolean:/\b(?:true|false|null)\b/,number:/\b0x[\dA-Fa-f]+|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][-+]?\d+)?/,operator:[{pattern:/(\s)(?:and|b-and|b-xor|b-or|ends with|in|is|matches|not|or|same as|starts with)(?=\s)/,lookbehind:!0},/[=<>]=?|!=|\*\*?|\/\/?|\?:?|[-+~%|]/],property:/\b[a-zA-Z_]\w*\b/,punctuation:/[()\[\]{}:.,]/}},other:{pattern:/\S(?:[\s\S]*\S)?/,inside:e.languages.markup}}}e.exports=t,t.displayName="twig",t.aliases=[]},4979(e){"use strict";function t(e){var t,n;(t=e).languages.typescript=t.languages.extend("javascript",{"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|type)\s+)(?!keyof\b)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?:\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/}),t.languages.typescript.keyword.push(/\b(?:abstract|as|declare|implements|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)(?!\s*[^\s_${}*a-zA-Z\xA0-\uFFFF])/),delete t.languages.typescript.parameter,delete(n=t.languages.extend("typescript",{}))["class-name"],t.languages.typescript["class-name"].inside=n,t.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:n}}}}),t.languages.ts=t.languages.typescript}e.exports=t,t.displayName="typescript",t.aliases=["ts"]},23159(e){"use strict";function t(e){var t,n;n=/\b(?:ACT|ACTIFSUB|CARRAY|CASE|CLEARGIF|COA|COA_INT|CONSTANTS|CONTENT|CUR|EDITPANEL|EFFECT|EXT|FILE|FLUIDTEMPLATE|FORM|FRAME|FRAMESET|GIFBUILDER|GMENU|GMENU_FOLDOUT|GMENU_LAYERS|GP|HMENU|HRULER|HTML|IENV|IFSUB|IMAGE|IMGMENU|IMGMENUITEM|IMGTEXT|IMG_RESOURCE|INCLUDE_TYPOSCRIPT|JSMENU|JSMENUITEM|LLL|LOAD_REGISTER|NO|PAGE|RECORDS|RESTORE_REGISTER|TEMPLATE|TEXT|TMENU|TMENUITEM|TMENU_LAYERS|USER|USER_INT|_GIFBUILDER|global|globalString|globalVar)\b/,(t=e).languages.typoscript={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:= \t]|(?:^|[^= \t])[ \t]+)\/\/.*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^"'])#.*/,lookbehind:!0,greedy:!0}],function:[{pattern://,inside:{string:{pattern:/"[^"\r\n]*"|'[^'\r\n]*'/,inside:{keyword:n}},keyword:{pattern:/INCLUDE_TYPOSCRIPT/}}},{pattern:/@import\s*(?:"[^"\r\n]*"|'[^'\r\n]*')/,inside:{string:/"[^"\r\n]*"|'[^'\r\n]*'/}}],string:{pattern:/^([^=]*=[< ]?)(?:(?!\]\n).)*/,lookbehind:!0,inside:{function:/\{\$.*\}/,keyword:n,number:/^[0-9]+$/,punctuation:/[,|:]/}},keyword:n,number:{pattern:/\b[0-9]+\s*[.{=]/,inside:{operator:/[.{=]/}},tag:{pattern:/\.?[-\w\\]+\.?/,inside:{punctuation:/\./}},punctuation:/[{}[\];(),.:|]/,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/},t.languages.tsconfig=t.languages.typoscript}e.exports=t,t.displayName="typoscript",t.aliases=["tsconfig"]},34966(e){"use strict";function t(e){e.languages.unrealscript={comment:/\/\/.*|\/\*[\s\S]*?\*\//,string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},category:{pattern:/(\b(?:(?:autoexpand|hide|show)categories|var)\s*\()[^()]+(?=\))/,lookbehind:!0,greedy:!0,alias:"property"},metadata:{pattern:/(\w\s*)<\s*\w+\s*=[^<>|=\r\n]+(?:\|\s*\w+\s*=[^<>|=\r\n]+)*>/,lookbehind:!0,greedy:!0,inside:{property:/\b\w+(?=\s*=)/,operator:/=/,punctuation:/[<>|]/}},macro:{pattern:/`\w+/,alias:"property"},"class-name":{pattern:/(\b(?:class|enum|extends|interface|state(?:\(\))?|struct|within)\s+)\w+/,lookbehind:!0},keyword:/\b(?:abstract|actor|array|auto|autoexpandcategories|bool|break|byte|case|class|classgroup|client|coerce|collapsecategories|config|const|continue|default|defaultproperties|delegate|dependson|deprecated|do|dontcollapsecategories|editconst|editinlinenew|else|enum|event|exec|export|extends|final|float|for|forcescriptorder|foreach|function|goto|guid|hidecategories|hidedropdown|if|ignores|implements|inherits|input|int|interface|iterator|latent|local|material|name|native|nativereplication|noexport|nontransient|noteditinlinenew|notplaceable|operator|optional|out|pawn|perobjectconfig|perobjectlocalized|placeable|postoperator|preoperator|private|protected|reliable|replication|return|server|showcategories|simulated|singular|state|static|string|struct|structdefault|structdefaultproperties|switch|texture|transient|travel|unreliable|until|var|vector|while|within)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,boolean:/\b(?:false|true)\b/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/>>|<<|--|\+\+|\*\*|[-+*/~!=<>$@]=?|&&?|\|\|?|\^\^?|[?:%]|\b(?:Cross|Dot|ClockwiseFrom)\b/,punctuation:/[()[\]{};,.]/},e.languages.uc=e.languages.uscript=e.languages.unrealscript}e.exports=t,t.displayName="unrealscript",t.aliases=["uc","uscript"]},38521(e){"use strict";function t(e){e.languages.uri={scheme:{pattern:/^[a-z][a-z0-9+.-]*:/im,greedy:!0,inside:{"scheme-delimiter":/:$/}},fragment:{pattern:/#[\w\-.~!$&'()*+,;=%:@/?]*/,inside:{"fragment-delimiter":/^#/}},query:{pattern:/\?[\w\-.~!$&'()*+,;=%:@/?]*/,inside:{"query-delimiter":{pattern:/^\?/,greedy:!0},"pair-delimiter":/[&;]/,pair:{pattern:/^[^=][\s\S]*/,inside:{key:/^[^=]+/,value:{pattern:/(^=)[\s\S]+/,lookbehind:!0}}}}},authority:{pattern:RegExp(/^\/\//.source+/(?:[\w\-.~!$&'()*+,;=%:]*@)?/.source+("(?:"+/\[(?:[0-9a-fA-F:.]{2,48}|v[0-9a-fA-F]+\.[\w\-.~!$&'()*+,;=]+)\]/.source+"|")+/[\w\-.~!$&'()*+,;=%]*/.source+")"+/(?::\d*)?/.source,"m"),inside:{"authority-delimiter":/^\/\//,"user-info-segment":{pattern:/^[\w\-.~!$&'()*+,;=%:]*@/,inside:{"user-info-delimiter":/@$/,"user-info":/^[\w\-.~!$&'()*+,;=%:]+/}},"port-segment":{pattern:/:\d*$/,inside:{"port-delimiter":/^:/,port:/^\d+/}},host:{pattern:/[\s\S]+/,inside:{"ip-literal":{pattern:/^\[[\s\S]+\]$/,inside:{"ip-literal-delimiter":/^\[|\]$/,"ipv-future":/^v[\s\S]+/,"ipv6-address":/^[\s\S]+/}},"ipv4-address":/^(?:(?:[03-9]\d?|[12]\d{0,2})\.){3}(?:[03-9]\d?|[12]{0,2})$/}}}},path:{pattern:/^[\w\-.~!$&'()*+,;=%:@/]+/m,inside:{"path-separator":/\//}}},e.languages.url=e.languages.uri}e.exports=t,t.displayName="uri",t.aliases=["url"]},7255(e){"use strict";function t(e){var t,n;n={pattern:/[\s\S]+/,inside:null},(t=e).languages.v=t.languages.extend("clike",{string:[{pattern:/`(?:\\`|\\?[^`]{1,2})`/,alias:"rune"},{pattern:/r?(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,alias:"quoted-string",greedy:!0,inside:{interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$(?:\{[^{}]*\}|\w+(?:\.\w+(?:\([^\(\)]*\))?|\[[^\[\]]+\])*)/,lookbehind:!0,inside:{"interpolation-variable":{pattern:/^\$\w[\s\S]*$/,alias:"variable"},"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},"interpolation-expression":n}}}}],"class-name":{pattern:/(\b(?:enum|interface|struct|type)\s+)(?:C\.)?\w+/,lookbehind:!0},keyword:/(?:\b(?:as|asm|assert|atomic|break|chan|const|continue|defer|else|embed|enum|fn|for|__global|go(?:to)?|if|import|in|interface|is|lock|match|module|mut|none|or|pub|return|rlock|select|shared|sizeof|static|struct|type(?:of)?|union|unsafe)|\$(?:if|else|for)|#(?:include|flag))\b/,number:/\b(?:0x[a-f\d]+(?:_[a-f\d]+)*|0b[01]+(?:_[01]+)*|0o[0-7]+(?:_[0-7]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?)\b/i,operator:/~|\?|[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\.?/,builtin:/\b(?:any(?:_int|_float)?|bool|byte(?:ptr)?|charptr|f(?:32|64)|i(?:8|16|nt|64|128)|rune|size_t|string|u(?:16|32|64|128)|voidptr)\b/}),n.inside=t.languages.v,t.languages.insertBefore("v","operator",{attribute:{pattern:/(^[\t ]*)\[(?:deprecated|unsafe_fn|typedef|live|inline|flag|ref_only|windows_stdcall|direct_array_access)\]/m,lookbehind:!0,alias:"annotation",inside:{punctuation:/[\[\]]/,keyword:/\w+/}},generic:{pattern:/<\w+>(?=\s*[\)\{])/,inside:{punctuation:/[<>]/,"class-name":/\w+/}}}),t.languages.insertBefore("v","function",{"generic-function":{pattern:/\b\w+\s*<\w+>(?=\()/,inside:{function:/^\w+/,generic:{pattern:/<\w+>/,inside:t.languages.v.generic.inside}}}})}e.exports=t,t.displayName="v",t.aliases=[]},28173(e){"use strict";function t(e){e.languages.vala=e.languages.extend("clike",{"class-name":[{pattern:/\b[A-Z]\w*(?:\.\w+)*\b(?=(?:\?\s+|\*?\s+\*?)\w)/,inside:{punctuation:/\./}},{pattern:/(\[)[A-Z]\w*(?:\.\w+)*\b/,lookbehind:!0,inside:{punctuation:/\./}},{pattern:/(\b(?:class|interface)\s+[A-Z]\w*(?:\.\w+)*\s*:\s*)[A-Z]\w*(?:\.\w+)*\b/,lookbehind:!0,inside:{punctuation:/\./}},{pattern:/((?:\b(?:class|interface|new|struct|enum)\s+)|(?:catch\s+\())[A-Z]\w*(?:\.\w+)*\b/,lookbehind:!0,inside:{punctuation:/\./}}],keyword:/\b(?:bool|char|double|float|null|size_t|ssize_t|string|unichar|void|int|int8|int16|int32|int64|long|short|uchar|uint|uint8|uint16|uint32|uint64|ulong|ushort|class|delegate|enum|errordomain|interface|namespace|struct|break|continue|do|for|foreach|return|while|else|if|switch|assert|case|default|abstract|const|dynamic|ensures|extern|inline|internal|override|private|protected|public|requires|signal|static|virtual|volatile|weak|async|owned|unowned|try|catch|finally|throw|as|base|construct|delete|get|in|is|lock|new|out|params|ref|sizeof|set|this|throws|typeof|using|value|var|yield)\b/i,function:/\b\w+(?=\s*\()/,number:/(?:\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)(?:f|u?l?)?/i,operator:/\+\+|--|&&|\|\||<<=?|>>=?|=>|->|~|[+\-*\/%&^|=!<>]=?|\?\??|\.\.\./,punctuation:/[{}[\];(),.:]/,constant:/\b[A-Z0-9_]+\b/}),e.languages.insertBefore("vala","string",{"raw-string":{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string"},"template-string":{pattern:/@"[\s\S]*?"/,greedy:!0,inside:{interpolation:{pattern:/\$(?:\([^)]*\)|[a-zA-Z]\w*)/,inside:{delimiter:{pattern:/^\$\(?|\)$/,alias:"punctuation"},rest:e.languages.vala}},string:/[\s\S]+/}}}),e.languages.insertBefore("vala","keyword",{regex:{pattern:/\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[imsx]{0,4}(?=\s*(?:$|[\r\n,.;})\]]))/,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:e.languages.regex},"regex-delimiter":/^\//,"regex-flags":/^[a-z]+$/}}})}e.exports=t,t.displayName="vala",t.aliases=[]},53813(e,t,n){"use strict";var r=n(46241);function i(e){e.register(r),e.languages.vbnet=e.languages.extend("basic",{comment:[{pattern:/(?:!|REM\b).+/i,inside:{keyword:/^REM/i}},{pattern:/(^|[^\\:])'.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(^|[^"])"(?:""|[^"])*"(?!")/i,lookbehind:!0,greedy:!0},keyword:/(?:\b(?:ADDHANDLER|ADDRESSOF|ALIAS|AND|ANDALSO|AS|BEEP|BLOAD|BOOLEAN|BSAVE|BYREF|BYTE|BYVAL|CALL(?: ABSOLUTE)?|CASE|CATCH|CBOOL|CBYTE|CCHAR|CDATE|CDEC|CDBL|CHAIN|CHAR|CHDIR|CINT|CLASS|CLEAR|CLNG|CLOSE|CLS|COBJ|COM|COMMON|CONST|CONTINUE|CSBYTE|CSHORT|CSNG|CSTR|CTYPE|CUINT|CULNG|CUSHORT|DATA|DATE|DECIMAL|DECLARE|DEFAULT|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DELEGATE|DIM|DIRECTCAST|DO|DOUBLE|ELSE|ELSEIF|END|ENUM|ENVIRON|ERASE|ERROR|EVENT|EXIT|FALSE|FIELD|FILES|FINALLY|FOR(?: EACH)?|FRIEND|FUNCTION|GET|GETTYPE|GETXMLNAMESPACE|GLOBAL|GOSUB|GOTO|HANDLES|IF|IMPLEMENTS|IMPORTS|IN|INHERITS|INPUT|INTEGER|INTERFACE|IOCTL|IS|ISNOT|KEY|KILL|LINE INPUT|LET|LIB|LIKE|LOCATE|LOCK|LONG|LOOP|LSET|ME|MKDIR|MOD|MODULE|MUSTINHERIT|MUSTOVERRIDE|MYBASE|MYCLASS|NAME|NAMESPACE|NARROWING|NEW|NEXT|NOT|NOTHING|NOTINHERITABLE|NOTOVERRIDABLE|OBJECT|OF|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPERATOR|OPEN|OPTION(?: BASE)?|OPTIONAL|OR|ORELSE|OUT|OVERLOADS|OVERRIDABLE|OVERRIDES|PARAMARRAY|PARTIAL|POKE|PRIVATE|PROPERTY|PROTECTED|PUBLIC|PUT|RAISEEVENT|READ|READONLY|REDIM|REM|REMOVEHANDLER|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SBYTE|SELECT(?: CASE)?|SET|SHADOWS|SHARED|SHORT|SINGLE|SHELL|SLEEP|STATIC|STEP|STOP|STRING|STRUCTURE|SUB|SYNCLOCK|SWAP|SYSTEM|THEN|THROW|TIMER|TO|TROFF|TRON|TRUE|TRY|TRYCAST|TYPE|TYPEOF|UINTEGER|ULONG|UNLOCK|UNTIL|USHORT|USING|VIEW PRINT|WAIT|WEND|WHEN|WHILE|WIDENING|WITH|WITHEVENTS|WRITE|WRITEONLY|XOR)|\B(?:#CONST|#ELSE|#ELSEIF|#END|#IF))(?:\$|\b)/i,punctuation:/[,;:(){}]/})}e.exports=i,i.displayName="vbnet",i.aliases=[]},46891(e){"use strict";function t(e){var t,n;(t=e).languages.velocity=t.languages.extend("markup",{}),(n={variable:{pattern:/(^|[^\\](?:\\\\)*)\$!?(?:[a-z][\w-]*(?:\([^)]*\))?(?:\.[a-z][\w-]*(?:\([^)]*\))?|\[[^\]]+\])*|\{[^}]+\})/i,lookbehind:!0,inside:{}},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},number:/\b\d+\b/,boolean:/\b(?:true|false)\b/,operator:/[=!<>]=?|[+*/%-]|&&|\|\||\.\.|\b(?:eq|g[et]|l[et]|n(?:e|ot))\b/,punctuation:/[(){}[\]:,.]/}).variable.inside={string:n.string,function:{pattern:/([^\w-])[a-z][\w-]*(?=\()/,lookbehind:!0},number:n.number,boolean:n.boolean,punctuation:n.punctuation},t.languages.insertBefore("velocity","comment",{unparsed:{pattern:/(^|[^\\])#\[\[[\s\S]*?\]\]#/,lookbehind:!0,greedy:!0,inside:{punctuation:/^#\[\[|\]\]#$/}},"velocity-comment":[{pattern:/(^|[^\\])#\*[\s\S]*?\*#/,lookbehind:!0,greedy:!0,alias:"comment"},{pattern:/(^|[^\\])##.*/,lookbehind:!0,greedy:!0,alias:"comment"}],directive:{pattern:/(^|[^\\](?:\\\\)*)#@?(?:[a-z][\w-]*|\{[a-z][\w-]*\})(?:\s*\((?:[^()]|\([^()]*\))*\))?/i,lookbehind:!0,inside:{keyword:{pattern:/^#@?(?:[a-z][\w-]*|\{[a-z][\w-]*\})|\bin\b/,inside:{punctuation:/[{}]/}},rest:n}},variable:n.variable}),t.languages.velocity.tag.inside["attr-value"].inside.rest=t.languages.velocity}e.exports=t,t.displayName="velocity",t.aliases=[]},91824(e){"use strict";function t(e){e.languages.verilog={comment:/\/\/.*|\/\*[\s\S]*?\*\//,string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},property:/\B\$\w+\b/,constant:/\B`\w+\b/,function:/\b\w+(?=\()/,keyword:/\b(?:alias|and|assert|assign|assume|automatic|before|begin|bind|bins|binsof|bit|break|buf|bufif0|bufif1|byte|class|case|casex|casez|cell|chandle|clocking|cmos|config|const|constraint|context|continue|cover|covergroup|coverpoint|cross|deassign|default|defparam|design|disable|dist|do|edge|else|end|endcase|endclass|endclocking|endconfig|endfunction|endgenerate|endgroup|endinterface|endmodule|endpackage|endprimitive|endprogram|endproperty|endspecify|endsequence|endtable|endtask|enum|event|expect|export|extends|extern|final|first_match|for|force|foreach|forever|fork|forkjoin|function|generate|genvar|highz0|highz1|if|iff|ifnone|ignore_bins|illegal_bins|import|incdir|include|initial|inout|input|inside|instance|int|integer|interface|intersect|join|join_any|join_none|large|liblist|library|local|localparam|logic|longint|macromodule|matches|medium|modport|module|nand|negedge|new|nmos|nor|noshowcancelled|not|notif0|notif1|null|or|output|package|packed|parameter|pmos|posedge|primitive|priority|program|property|protected|pull0|pull1|pulldown|pullup|pulsestyle_onevent|pulsestyle_ondetect|pure|rand|randc|randcase|randsequence|rcmos|real|realtime|ref|reg|release|repeat|return|rnmos|rpmos|rtran|rtranif0|rtranif1|scalared|sequence|shortint|shortreal|showcancelled|signed|small|solve|specify|specparam|static|string|strong0|strong1|struct|super|supply0|supply1|table|tagged|task|this|throughout|time|timeprecision|timeunit|tran|tranif0|tranif1|tri|tri0|tri1|triand|trior|trireg|type|typedef|union|unique|unsigned|use|uwire|var|vectored|virtual|void|wait|wait_order|wand|weak0|weak1|while|wildcard|wire|with|within|wor|xnor|xor)\b/,important:/\b(?:always_latch|always_comb|always_ff|always)\b ?@?/,number:/\B##?\d+|(?:\b\d+)?'[odbh] ?[\da-fzx_?]+|\b(?:\d*[._])?\d+(?:e[-+]?\d+)?/i,operator:/[-+{}^~%*\/?=!<>&|]+/,punctuation:/[[\];(),.:]/}}e.exports=t,t.displayName="verilog",t.aliases=[]},9447(e){"use strict";function t(e){e.languages.vhdl={comment:/--.+/,"vhdl-vectors":{pattern:/\b[oxb]"[\da-f_]+"|"[01uxzwlh-]+"/i,alias:"number"},"quoted-function":{pattern:/"\S+?"(?=\()/,alias:"function"},string:/"(?:[^\\"\r\n]|\\(?:\r\n|[\s\S]))*"/,constant:/\b(?:use|library)\b/i,keyword:/\b(?:'active|'ascending|'base|'delayed|'driving|'driving_value|'event|'high|'image|'instance_name|'last_active|'last_event|'last_value|'left|'leftof|'length|'low|'path_name|'pos|'pred|'quiet|'range|'reverse_range|'right|'rightof|'simple_name|'stable|'succ|'transaction|'val|'value|access|after|alias|all|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|new|next|null|of|on|open|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|report|return|select|severity|shared|signal|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with)\b/i,boolean:/\b(?:true|false)\b/i,function:/\w+(?=\()/,number:/'[01uxzwlh-]'|\b(?:\d+#[\da-f_.]+#|\d[\d_.]*)(?:e[-+]?\d+)?/i,operator:/[<>]=?|:=|[-+*/&=]|\b(?:abs|not|mod|rem|sll|srl|sla|sra|rol|ror|and|or|nand|xnor|xor|nor)\b/i,punctuation:/[{}[\];(),.:]/}}e.exports=t,t.displayName="vhdl",t.aliases=[]},53062(e){"use strict";function t(e){e.languages.vim={string:/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\r\n]|'')*'/,comment:/".*/,function:/\b\w+(?=\()/,keyword:/\b(?:ab|abbreviate|abc|abclear|abo|aboveleft|al|all|arga|argadd|argd|argdelete|argdo|arge|argedit|argg|argglobal|argl|arglocal|ar|args|argu|argument|as|ascii|bad|badd|ba|ball|bd|bdelete|be|bel|belowright|bf|bfirst|bl|blast|bm|bmodified|bn|bnext|bN|bNext|bo|botright|bp|bprevious|brea|break|breaka|breakadd|breakd|breakdel|breakl|breaklist|br|brewind|bro|browse|bufdo|b|buffer|buffers|bun|bunload|bw|bwipeout|ca|cabbrev|cabc|cabclear|caddb|caddbuffer|cad|caddexpr|caddf|caddfile|cal|call|cat|catch|cb|cbuffer|cc|ccl|cclose|cd|ce|center|cex|cexpr|cf|cfile|cfir|cfirst|cgetb|cgetbuffer|cgete|cgetexpr|cg|cgetfile|c|change|changes|chd|chdir|che|checkpath|checkt|checktime|cla|clast|cl|clist|clo|close|cmapc|cmapclear|cnew|cnewer|cn|cnext|cN|cNext|cnf|cnfile|cNfcNfile|cnorea|cnoreabbrev|col|colder|colo|colorscheme|comc|comclear|comp|compiler|conf|confirm|con|continue|cope|copen|co|copy|cpf|cpfile|cp|cprevious|cq|cquit|cr|crewind|cuna|cunabbrev|cu|cunmap|cw|cwindow|debugg|debuggreedy|delc|delcommand|d|delete|delf|delfunction|delm|delmarks|diffg|diffget|diffoff|diffpatch|diffpu|diffput|diffsplit|diffthis|diffu|diffupdate|dig|digraphs|di|display|dj|djump|dl|dlist|dr|drop|ds|dsearch|dsp|dsplit|earlier|echoe|echoerr|echom|echomsg|echon|e|edit|el|else|elsei|elseif|em|emenu|endfo|endfor|endf|endfunction|endfun|en|endif|endt|endtry|endw|endwhile|ene|enew|ex|exi|exit|exu|exusage|f|file|files|filetype|fina|finally|fin|find|fini|finish|fir|first|fix|fixdel|fo|fold|foldc|foldclose|folddoc|folddoclosed|foldd|folddoopen|foldo|foldopen|for|fu|fun|function|go|goto|gr|grep|grepa|grepadd|ha|hardcopy|h|help|helpf|helpfind|helpg|helpgrep|helpt|helptags|hid|hide|his|history|ia|iabbrev|iabc|iabclear|if|ij|ijump|il|ilist|imapc|imapclear|in|inorea|inoreabbrev|isearch|isp|isplit|iuna|iunabbrev|iu|iunmap|j|join|ju|jumps|k|keepalt|keepj|keepjumps|kee|keepmarks|laddb|laddbuffer|lad|laddexpr|laddf|laddfile|lan|language|la|last|later|lb|lbuffer|lc|lcd|lch|lchdir|lcl|lclose|let|left|lefta|leftabove|lex|lexpr|lf|lfile|lfir|lfirst|lgetb|lgetbuffer|lgete|lgetexpr|lg|lgetfile|lgr|lgrep|lgrepa|lgrepadd|lh|lhelpgrep|l|list|ll|lla|llast|lli|llist|lmak|lmake|lm|lmap|lmapc|lmapclear|lnew|lnewer|lne|lnext|lN|lNext|lnf|lnfile|lNf|lNfile|ln|lnoremap|lo|loadview|loc|lockmarks|lockv|lockvar|lol|lolder|lop|lopen|lpf|lpfile|lp|lprevious|lr|lrewind|ls|lt|ltag|lu|lunmap|lv|lvimgrep|lvimgrepa|lvimgrepadd|lw|lwindow|mak|make|ma|mark|marks|mat|match|menut|menutranslate|mk|mkexrc|mks|mksession|mksp|mkspell|mkvie|mkview|mkv|mkvimrc|mod|mode|m|move|mzf|mzfile|mz|mzscheme|nbkey|new|n|next|N|Next|nmapc|nmapclear|noh|nohlsearch|norea|noreabbrev|nu|number|nun|nunmap|omapc|omapclear|on|only|o|open|opt|options|ou|ounmap|pc|pclose|ped|pedit|pe|perl|perld|perldo|po|pop|popu|popup|pp|ppop|pre|preserve|prev|previous|p|print|P|Print|profd|profdel|prof|profile|promptf|promptfind|promptr|promptrepl|ps|psearch|pta|ptag|ptf|ptfirst|ptj|ptjump|ptl|ptlast|ptn|ptnext|ptN|ptNext|ptp|ptprevious|ptr|ptrewind|pts|ptselect|pu|put|pw|pwd|pyf|pyfile|py|python|qa|qall|q|quit|quita|quitall|r|read|rec|recover|redi|redir|red|redo|redr|redraw|redraws|redrawstatus|reg|registers|res|resize|ret|retab|retu|return|rew|rewind|ri|right|rightb|rightbelow|rub|ruby|rubyd|rubydo|rubyf|rubyfile|ru|runtime|rv|rviminfo|sal|sall|san|sandbox|sa|sargument|sav|saveas|sba|sball|sbf|sbfirst|sbl|sblast|sbm|sbmodified|sbn|sbnext|sbN|sbNext|sbp|sbprevious|sbr|sbrewind|sb|sbuffer|scripte|scriptencoding|scrip|scriptnames|se|set|setf|setfiletype|setg|setglobal|setl|setlocal|sf|sfind|sfir|sfirst|sh|shell|sign|sil|silent|sim|simalt|sla|slast|sl|sleep|sm|smagic|smap|smapc|smapclear|sme|smenu|sn|snext|sN|sNext|sni|sniff|sno|snomagic|snor|snoremap|snoreme|snoremenu|sor|sort|so|source|spelld|spelldump|spe|spellgood|spelli|spellinfo|spellr|spellrepall|spellu|spellundo|spellw|spellwrong|sp|split|spr|sprevious|sre|srewind|sta|stag|startg|startgreplace|star|startinsert|startr|startreplace|stj|stjump|st|stop|stopi|stopinsert|sts|stselect|sun|sunhide|sunm|sunmap|sus|suspend|sv|sview|syncbind|t|tab|tabc|tabclose|tabd|tabdo|tabe|tabedit|tabf|tabfind|tabfir|tabfirst|tabl|tablast|tabm|tabmove|tabnew|tabn|tabnext|tabN|tabNext|tabo|tabonly|tabp|tabprevious|tabr|tabrewind|tabs|ta|tag|tags|tc|tcl|tcld|tcldo|tclf|tclfile|te|tearoff|tf|tfirst|th|throw|tj|tjump|tl|tlast|tm|tmenu|tn|tnext|tN|tNext|to|topleft|tp|tprevious|tr|trewind|try|ts|tselect|tu|tunmenu|una|unabbreviate|u|undo|undoj|undojoin|undol|undolist|unh|unhide|unlet|unlo|unlockvar|unm|unmap|up|update|verb|verbose|ve|version|vert|vertical|vie|view|vim|vimgrep|vimgrepa|vimgrepadd|vi|visual|viu|viusage|vmapc|vmapclear|vne|vnew|vs|vsplit|vu|vunmap|wa|wall|wh|while|winc|wincmd|windo|winp|winpos|win|winsize|wn|wnext|wN|wNext|wp|wprevious|wq|wqa|wqall|w|write|ws|wsverb|wv|wviminfo|X|xa|xall|x|xit|xm|xmap|xmapc|xmapclear|xme|xmenu|XMLent|XMLns|xn|xnoremap|xnoreme|xnoremenu|xu|xunmap|y|yank)\b/,builtin:/\b(?:autocmd|acd|ai|akm|aleph|allowrevins|altkeymap|ambiwidth|ambw|anti|antialias|arab|arabic|arabicshape|ari|arshape|autochdir|autoindent|autoread|autowrite|autowriteall|aw|awa|background|backspace|backup|backupcopy|backupdir|backupext|backupskip|balloondelay|ballooneval|balloonexpr|bdir|bdlay|beval|bex|bexpr|bg|bh|bin|binary|biosk|bioskey|bk|bkc|bomb|breakat|brk|browsedir|bs|bsdir|bsk|bt|bufhidden|buflisted|buftype|casemap|ccv|cdpath|cedit|cfu|ch|charconvert|ci|cin|cindent|cink|cinkeys|cino|cinoptions|cinw|cinwords|clipboard|cmdheight|cmdwinheight|cmp|cms|columns|com|comments|commentstring|compatible|complete|completefunc|completeopt|consk|conskey|copyindent|cot|cpo|cpoptions|cpt|cscopepathcomp|cscopeprg|cscopequickfix|cscopetag|cscopetagorder|cscopeverbose|cspc|csprg|csqf|cst|csto|csverb|cuc|cul|cursorcolumn|cursorline|cwh|debug|deco|def|define|delcombine|dex|dg|dict|dictionary|diff|diffexpr|diffopt|digraph|dip|dir|directory|dy|ea|ead|eadirection|eb|ed|edcompatible|ef|efm|ei|ek|enc|encoding|endofline|eol|ep|equalalways|equalprg|errorbells|errorfile|errorformat|esckeys|et|eventignore|expandtab|exrc|fcl|fcs|fdc|fde|fdi|fdl|fdls|fdm|fdn|fdo|fdt|fen|fenc|fencs|fex|ff|ffs|fileencoding|fileencodings|fileformat|fileformats|fillchars|fk|fkmap|flp|fml|fmr|foldcolumn|foldenable|foldexpr|foldignore|foldlevel|foldlevelstart|foldmarker|foldmethod|foldminlines|foldnestmax|foldtext|formatexpr|formatlistpat|formatoptions|formatprg|fp|fs|fsync|ft|gcr|gd|gdefault|gfm|gfn|gfs|gfw|ghr|gp|grepformat|grepprg|gtl|gtt|guicursor|guifont|guifontset|guifontwide|guiheadroom|guioptions|guipty|guitablabel|guitabtooltip|helpfile|helpheight|helplang|hf|hh|hi|hidden|highlight|hk|hkmap|hkmapp|hkp|hl|hlg|hls|hlsearch|ic|icon|iconstring|ignorecase|im|imactivatekey|imak|imc|imcmdline|imd|imdisable|imi|iminsert|ims|imsearch|inc|include|includeexpr|incsearch|inde|indentexpr|indentkeys|indk|inex|inf|infercase|insertmode|isf|isfname|isi|isident|isk|iskeyword|isprint|joinspaces|js|key|keymap|keymodel|keywordprg|km|kmp|kp|langmap|langmenu|laststatus|lazyredraw|lbr|lcs|linebreak|lines|linespace|lisp|lispwords|listchars|loadplugins|lpl|lsp|lz|macatsui|magic|makeef|makeprg|matchpairs|matchtime|maxcombine|maxfuncdepth|maxmapdepth|maxmem|maxmempattern|maxmemtot|mco|mef|menuitems|mfd|mh|mis|mkspellmem|ml|mls|mm|mmd|mmp|mmt|modeline|modelines|modifiable|modified|more|mouse|mousef|mousefocus|mousehide|mousem|mousemodel|mouses|mouseshape|mouset|mousetime|mp|mps|msm|mzq|mzquantum|nf|nrformats|numberwidth|nuw|odev|oft|ofu|omnifunc|opendevice|operatorfunc|opfunc|osfiletype|pa|para|paragraphs|paste|pastetoggle|patchexpr|patchmode|path|pdev|penc|pex|pexpr|pfn|ph|pheader|pi|pm|pmbcs|pmbfn|popt|preserveindent|previewheight|previewwindow|printdevice|printencoding|printexpr|printfont|printheader|printmbcharset|printmbfont|printoptions|prompt|pt|pumheight|pvh|pvw|qe|quoteescape|readonly|remap|report|restorescreen|revins|rightleft|rightleftcmd|rl|rlc|ro|rs|rtp|ruf|ruler|rulerformat|runtimepath|sbo|sc|scb|scr|scroll|scrollbind|scrolljump|scrolloff|scrollopt|scs|sect|sections|secure|sel|selection|selectmode|sessionoptions|sft|shcf|shellcmdflag|shellpipe|shellquote|shellredir|shellslash|shelltemp|shelltype|shellxquote|shiftround|shiftwidth|shm|shortmess|shortname|showbreak|showcmd|showfulltag|showmatch|showmode|showtabline|shq|si|sidescroll|sidescrolloff|siso|sj|slm|smartcase|smartindent|smarttab|smc|smd|softtabstop|sol|spc|spell|spellcapcheck|spellfile|spelllang|spellsuggest|spf|spl|splitbelow|splitright|sps|sr|srr|ss|ssl|ssop|stal|startofline|statusline|stl|stmp|su|sua|suffixes|suffixesadd|sw|swapfile|swapsync|swb|swf|switchbuf|sws|sxq|syn|synmaxcol|syntax|tabline|tabpagemax|tabstop|tagbsearch|taglength|tagrelative|tagstack|tal|tb|tbi|tbidi|tbis|tbs|tenc|term|termbidi|termencoding|terse|textauto|textmode|textwidth|tgst|thesaurus|tildeop|timeout|timeoutlen|title|titlelen|titleold|titlestring|toolbar|toolbariconsize|top|tpm|tsl|tsr|ttimeout|ttimeoutlen|ttm|tty|ttybuiltin|ttyfast|ttym|ttymouse|ttyscroll|ttytype|tw|tx|uc|ul|undolevels|updatecount|updatetime|ut|vb|vbs|vdir|verbosefile|vfile|viewdir|viewoptions|viminfo|virtualedit|visualbell|vop|wak|warn|wb|wc|wcm|wd|weirdinvert|wfh|wfw|whichwrap|wi|wig|wildchar|wildcharm|wildignore|wildmenu|wildmode|wildoptions|wim|winaltkeys|window|winfixheight|winfixwidth|winheight|winminheight|winminwidth|winwidth|wiv|wiw|wm|wmh|wmnu|wmw|wop|wrap|wrapmargin|wrapscan|writeany|writebackup|writedelay|ww|noacd|noai|noakm|noallowrevins|noaltkeymap|noanti|noantialias|noar|noarab|noarabic|noarabicshape|noari|noarshape|noautochdir|noautoindent|noautoread|noautowrite|noautowriteall|noaw|noawa|nobackup|noballooneval|nobeval|nobin|nobinary|nobiosk|nobioskey|nobk|nobl|nobomb|nobuflisted|nocf|noci|nocin|nocindent|nocompatible|noconfirm|noconsk|noconskey|nocopyindent|nocp|nocscopetag|nocscopeverbose|nocst|nocsverb|nocuc|nocul|nocursorcolumn|nocursorline|nodeco|nodelcombine|nodg|nodiff|nodigraph|nodisable|noea|noeb|noed|noedcompatible|noek|noendofline|noeol|noequalalways|noerrorbells|noesckeys|noet|noex|noexpandtab|noexrc|nofen|nofk|nofkmap|nofoldenable|nogd|nogdefault|noguipty|nohid|nohidden|nohk|nohkmap|nohkmapp|nohkp|nohls|noic|noicon|noignorecase|noim|noimc|noimcmdline|noimd|noincsearch|noinf|noinfercase|noinsertmode|nois|nojoinspaces|nojs|nolazyredraw|nolbr|nolinebreak|nolisp|nolist|noloadplugins|nolpl|nolz|noma|nomacatsui|nomagic|nomh|noml|nomod|nomodeline|nomodifiable|nomodified|nomore|nomousef|nomousefocus|nomousehide|nonu|nonumber|noodev|noopendevice|nopaste|nopi|nopreserveindent|nopreviewwindow|noprompt|nopvw|noreadonly|noremap|norestorescreen|norevins|nori|norightleft|norightleftcmd|norl|norlc|noro|nors|noru|noruler|nosb|nosc|noscb|noscrollbind|noscs|nosecure|nosft|noshellslash|noshelltemp|noshiftround|noshortname|noshowcmd|noshowfulltag|noshowmatch|noshowmode|nosi|nosm|nosmartcase|nosmartindent|nosmarttab|nosmd|nosn|nosol|nospell|nosplitbelow|nosplitright|nospr|nosr|nossl|nosta|nostartofline|nostmp|noswapfile|noswf|nota|notagbsearch|notagrelative|notagstack|notbi|notbidi|notbs|notermbidi|noterse|notextauto|notextmode|notf|notgst|notildeop|notimeout|notitle|noto|notop|notr|nottimeout|nottybuiltin|nottyfast|notx|novb|novisualbell|nowa|nowarn|nowb|noweirdinvert|nowfh|nowfw|nowildmenu|nowinfixheight|nowinfixwidth|nowiv|nowmnu|nowrap|nowrapscan|nowrite|nowriteany|nowritebackup|nows|invacd|invai|invakm|invallowrevins|invaltkeymap|invanti|invantialias|invar|invarab|invarabic|invarabicshape|invari|invarshape|invautochdir|invautoindent|invautoread|invautowrite|invautowriteall|invaw|invawa|invbackup|invballooneval|invbeval|invbin|invbinary|invbiosk|invbioskey|invbk|invbl|invbomb|invbuflisted|invcf|invci|invcin|invcindent|invcompatible|invconfirm|invconsk|invconskey|invcopyindent|invcp|invcscopetag|invcscopeverbose|invcst|invcsverb|invcuc|invcul|invcursorcolumn|invcursorline|invdeco|invdelcombine|invdg|invdiff|invdigraph|invdisable|invea|inveb|inved|invedcompatible|invek|invendofline|inveol|invequalalways|inverrorbells|invesckeys|invet|invex|invexpandtab|invexrc|invfen|invfk|invfkmap|invfoldenable|invgd|invgdefault|invguipty|invhid|invhidden|invhk|invhkmap|invhkmapp|invhkp|invhls|invhlsearch|invic|invicon|invignorecase|invim|invimc|invimcmdline|invimd|invincsearch|invinf|invinfercase|invinsertmode|invis|invjoinspaces|invjs|invlazyredraw|invlbr|invlinebreak|invlisp|invlist|invloadplugins|invlpl|invlz|invma|invmacatsui|invmagic|invmh|invml|invmod|invmodeline|invmodifiable|invmodified|invmore|invmousef|invmousefocus|invmousehide|invnu|invnumber|invodev|invopendevice|invpaste|invpi|invpreserveindent|invpreviewwindow|invprompt|invpvw|invreadonly|invremap|invrestorescreen|invrevins|invri|invrightleft|invrightleftcmd|invrl|invrlc|invro|invrs|invru|invruler|invsb|invsc|invscb|invscrollbind|invscs|invsecure|invsft|invshellslash|invshelltemp|invshiftround|invshortname|invshowcmd|invshowfulltag|invshowmatch|invshowmode|invsi|invsm|invsmartcase|invsmartindent|invsmarttab|invsmd|invsn|invsol|invspell|invsplitbelow|invsplitright|invspr|invsr|invssl|invsta|invstartofline|invstmp|invswapfile|invswf|invta|invtagbsearch|invtagrelative|invtagstack|invtbi|invtbidi|invtbs|invtermbidi|invterse|invtextauto|invtextmode|invtf|invtgst|invtildeop|invtimeout|invtitle|invto|invtop|invtr|invttimeout|invttybuiltin|invttyfast|invtx|invvb|invvisualbell|invwa|invwarn|invwb|invweirdinvert|invwfh|invwfw|invwildmenu|invwinfixheight|invwinfixwidth|invwiv|invwmnu|invwrap|invwrapscan|invwrite|invwriteany|invwritebackup|invws|t_AB|t_AF|t_al|t_AL|t_bc|t_cd|t_ce|t_Ce|t_cl|t_cm|t_Co|t_cs|t_Cs|t_CS|t_CV|t_da|t_db|t_dl|t_DL|t_EI|t_F1|t_F2|t_F3|t_F4|t_F5|t_F6|t_F7|t_F8|t_F9|t_fs|t_IE|t_IS|t_k1|t_K1|t_k2|t_k3|t_K3|t_k4|t_K4|t_k5|t_K5|t_k6|t_K6|t_k7|t_K7|t_k8|t_K8|t_k9|t_K9|t_KA|t_kb|t_kB|t_KB|t_KC|t_kd|t_kD|t_KD|t_ke|t_KE|t_KF|t_KG|t_kh|t_KH|t_kI|t_KI|t_KJ|t_KK|t_kl|t_KL|t_kN|t_kP|t_kr|t_ks|t_ku|t_le|t_mb|t_md|t_me|t_mr|t_ms|t_nd|t_op|t_RI|t_RV|t_Sb|t_se|t_Sf|t_SI|t_so|t_sr|t_te|t_ti|t_ts|t_ue|t_us|t_ut|t_vb|t_ve|t_vi|t_vs|t_WP|t_WS|t_xs|t_ZH|t_ZR)\b/,number:/\b(?:0x[\da-f]+|\d+(?:\.\d+)?)\b/i,operator:/\|\||&&|[-+.]=?|[=!](?:[=~][#?]?)?|[<>]=?[#?]?|[*\/%?]|\b(?:is(?:not)?)\b/,punctuation:/[{}[\](),;:]/}}e.exports=t,t.displayName="vim",t.aliases=[]},46215(e){"use strict";function t(e){e.languages["visual-basic"]={comment:{pattern:/(?:['‘’]|REM\b)(?:[^\r\n_]|_(?:\r\n?|\n)?)*/i,inside:{keyword:/^REM/i}},directive:{pattern:/#(?:Const|Else|ElseIf|End|ExternalChecksum|ExternalSource|If|Region)(?:[^\S\r\n]_[^\S\r\n]*(?:\r\n?|\n)|.)+/i,alias:"comment",greedy:!0},string:{pattern:/\$?["“”](?:["“”]{2}|[^"“”])*["“”]C?/i,greedy:!0},date:{pattern:/#[^\S\r\n]*(?:\d+([/-])\d+\1\d+(?:[^\S\r\n]+(?:\d+[^\S\r\n]*(?:AM|PM)|\d+:\d+(?::\d+)?(?:[^\S\r\n]*(?:AM|PM))?))?|\d+[^\S\r\n]*(?:AM|PM)|\d+:\d+(?::\d+)?(?:[^\S\r\n]*(?:AM|PM))?)[^\S\r\n]*#/i,alias:"builtin"},number:/(?:(?:\b\d+(?:\.\d+)?|\.\d+)(?:E[+-]?\d+)?|&[HO][\dA-F]+)(?:U?[ILS]|[FRD])?/i,boolean:/\b(?:True|False|Nothing)\b/i,keyword:/\b(?:AddHandler|AddressOf|Alias|And(?:Also)?|As|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|C(?:Bool|Byte|Char|Date|Dbl|Dec|Int|Lng|Obj|SByte|Short|Sng|Str|Type|UInt|ULng|UShort)|Char|Class|Const|Continue|Currency|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else(?:If)?|End(?:If)?|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get(?:Type|XMLNamespace)?|Global|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|IsNot|Let|Lib|Like|Long|Loop|Me|Mod|Module|Must(?:Inherit|Override)|My(?:Base|Class)|Namespace|Narrowing|New|Next|Not(?:Inheritable|Overridable)?|Object|Of|On|Operator|Option(?:al)?|Or(?:Else)?|Out|Overloads|Overridable|Overrides|ParamArray|Partial|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|SByte|Select|Set|Shadows|Shared|short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TryCast|Type|TypeOf|U(?:Integer|Long|Short)|Using|Variant|Wend|When|While|Widening|With(?:Events)?|WriteOnly|Until|Xor)\b/i,operator:[/[+\-*/\\^<=>&#@$%!]/,{pattern:/([^\S\r\n])_(?=[^\S\r\n]*[\r\n])/,lookbehind:!0}],punctuation:/[{}().,:?]/},e.languages.vb=e.languages["visual-basic"],e.languages.vba=e.languages["visual-basic"]}e.exports=t,t.displayName="visualBasic",t.aliases=[]},10784(e){"use strict";function t(e){e.languages.warpscript={comment:/#.*|\/\/.*|\/\*[\s\S]*?\*\//,string:{pattern:/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'|<'(?:[^\\']|'(?!>)|\\.)*'>/,greedy:!0},variable:/\$\S+/,macro:{pattern:/@\S+/,alias:"property"},keyword:/\b(?:BREAK|CHECKMACRO|CONTINUE|CUDF|DEFINED|DEFINEDMACRO|EVAL|FAIL|FOR|FOREACH|FORSTEP|IFT|IFTE|MSGFAIL|NRETURN|RETHROW|RETURN|SWITCH|TRY|UDF|UNTIL|WHILE)\b/,number:/[+-]?\b(?:NaN|Infinity|\d+(?:\.\d*)?(?:[Ee][+-]?\d+)?|0x[\da-fA-F]+|0b[01]+)\b/,boolean:/\b(?:false|true|F|T)\b/,punctuation:/<%|%>|[{}[\]()]/,operator:/==|&&?|\|\|?|\*\*?|>>>?|<<|[<>!~]=?|[-/%^]|\+!?|\b(?:AND|NOT|OR)\b/}}e.exports=t,t.displayName="warpscript",t.aliases=[]},17684(e){"use strict";function t(e){e.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/i,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/}}e.exports=t,t.displayName="wasm",t.aliases=[]},18191(e){"use strict";function t(e){e.languages.wiki=e.languages.extend("markup",{"block-comment":{pattern:/(^|[^\\])\/\*[\s\S]*?\*\//,lookbehind:!0,alias:"comment"},heading:{pattern:/^(=+)[^=\r\n].*?\1/m,inside:{punctuation:/^=+|=+$/,important:/.+/}},emphasis:{pattern:/('{2,5}).+?\1/,inside:{"bold-italic":{pattern:/(''''').+?(?=\1)/,lookbehind:!0,alias:["bold","italic"]},bold:{pattern:/(''')[^'](?:.*?[^'])?(?=\1)/,lookbehind:!0},italic:{pattern:/('')[^'](?:.*?[^'])?(?=\1)/,lookbehind:!0},punctuation:/^''+|''+$/}},hr:{pattern:/^-{4,}/m,alias:"punctuation"},url:[/ISBN +(?:97[89][ -]?)?(?:\d[ -]?){9}[\dx]\b|(?:RFC|PMID) +\d+/i,/\[\[.+?\]\]|\[.+?\]/],variable:[/__[A-Z]+__/,/\{{3}.+?\}{3}/,/\{\{.+?\}\}/],symbol:[/^#redirect/im,/~{3,5}/],"table-tag":{pattern:/((?:^|[|!])[|!])[^|\r\n]+\|(?!\|)/m,lookbehind:!0,inside:{"table-bar":{pattern:/\|$/,alias:"punctuation"},rest:e.languages.markup.tag.inside}},punctuation:/^(?:\{\||\|\}|\|-|[*#:;!|])|\|\||!!/m}),e.languages.insertBefore("wiki","tag",{nowiki:{pattern:/<(nowiki|pre|source)\b[^>]*>[\s\S]*?<\/\1>/i,inside:{tag:{pattern:/<(?:nowiki|pre|source)\b[^>]*>|<\/(?:nowiki|pre|source)>/i,inside:e.languages.markup.tag.inside}}}})}e.exports=t,t.displayName="wiki",t.aliases=[]},75242(e){"use strict";function t(e){e.languages.wolfram={comment:/\(\*(?:\(\*(?:[^*]|\*(?!\)))*\*\)|(?!\(\*)[\s\S])*?\*\)/,string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:Abs|AbsArg|Accuracy|Block|Do|For|Function|If|Manipulate|Module|Nest|NestList|None|Return|Switch|Table|Which|While)\b/,context:{pattern:/\w+`+\w*/,alias:"class-name"},blank:{pattern:/\b\w+_\b/,alias:"regex"},"global-variable":{pattern:/\$\w+/,alias:"variable"},boolean:/\b(?:True|False)\b/,number:/(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/\/\.|;|=\.|\^=|\^:=|:=|<<|>>|<\||\|>|:>|\|->|->|<-|@@@|@@|@|\/@|=!=|===|==|=|\+|-|\^|\[\/-+%=\]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[\|{}[\];(),.:]/},e.languages.mathematica=e.languages.wolfram,e.languages.wl=e.languages.wolfram,e.languages.nb=e.languages.wolfram}e.exports=t,t.displayName="wolfram",t.aliases=["mathematica","wl","nb"]},97202(e){"use strict";function t(e){var t;(t=e).languages.xeora=t.languages.extend("markup",{constant:{pattern:/\$(?:DomainContents|PageRenderDuration)\$/,inside:{punctuation:{pattern:/\$/}}},variable:{pattern:/\$@?(?:#+|[-+*~=^])?[\w.]+\$/,inside:{punctuation:{pattern:/[$.]/},operator:{pattern:/#+|[-+*~=^@]/}}},"function-inline":{pattern:/\$F:[-\w.]+\?[-\w.]+(?:,(?:(?:@[-#]*\w+\.[\w+.]\.*)*\|)*(?:(?:[\w+]|[-#*.~^]+[\w+]|=\S)(?:[^$=]|=+[^=])*=*|(?:@[-#]*\w+\.[\w+.]\.*)+(?:(?:[\w+]|[-#*~^][-#*.~^]*[\w+]|=\S)(?:[^$=]|=+[^=])*=*)?)?)?\$/,inside:{variable:{pattern:/(?:[,|])@?(?:#+|[-+*~=^])?[\w.]+/,inside:{punctuation:{pattern:/[,.|]/},operator:{pattern:/#+|[-+*~=^@]/}}},punctuation:{pattern:/\$\w:|[$:?.,|]/}},alias:"function"},"function-block":{pattern:/\$XF:\{[-\w.]+\?[-\w.]+(?:,(?:(?:@[-#]*\w+\.[\w+.]\.*)*\|)*(?:(?:[\w+]|[-#*.~^]+[\w+]|=\S)(?:[^$=]|=+[^=])*=*|(?:@[-#]*\w+\.[\w+.]\.*)+(?:(?:[\w+]|[-#*~^][-#*.~^]*[\w+]|=\S)(?:[^$=]|=+[^=])*=*)?)?)?\}:XF\$/,inside:{punctuation:{pattern:/[$:{}?.,|]/}},alias:"function"},"directive-inline":{pattern:/\$\w(?:#\d+\+?)?(?:\[[-\w.]+\])?:[-\/\w.]+\$/,inside:{punctuation:{pattern:/\$(?:\w:|C(?:\[|#\d))?|[:{[\]]/,inside:{tag:{pattern:/#\d/}}}},alias:"function"},"directive-block-open":{pattern:/\$\w+:\{|\$\w(?:#\d+\+?)?(?:\[[-\w.]+\])?:[-\w.]+:\{(?:![A-Z]+)?/,inside:{punctuation:{pattern:/\$(?:\w:|C(?:\[|#\d))?|[:{[\]]/,inside:{tag:{pattern:/#\d/}}},attribute:{pattern:/![A-Z]+$/,inside:{punctuation:{pattern:/!/}},alias:"keyword"}},alias:"function"},"directive-block-separator":{pattern:/\}:[-\w.]+:\{/,inside:{punctuation:{pattern:/[:{}]/}},alias:"function"},"directive-block-close":{pattern:/\}:[-\w.]+\$/,inside:{punctuation:{pattern:/[:{}$]/}},alias:"function"}}),t.languages.insertBefore("inside","punctuation",{variable:t.languages.xeora["function-inline"].inside.variable},t.languages.xeora["function-block"]),t.languages.xeoracube=t.languages.xeora}e.exports=t,t.displayName="xeora",t.aliases=["xeoracube"]},13808(e){"use strict";function t(e){!function(e){function t(t,n){e.languages[t]&&e.languages.insertBefore(t,"comment",{"doc-comment":n})}var n=e.languages.markup.tag,r={pattern:/\/\/\/.*/,greedy:!0,alias:"comment",inside:{tag:n}},i={pattern:/'''.*/,greedy:!0,alias:"comment",inside:{tag:n}};t("csharp",r),t("fsharp",r),t("vbnet",i)}(e)}e.exports=t,t.displayName="xmlDoc",t.aliases=[]},21301(e){"use strict";function t(e){e.languages.xojo={comment:{pattern:/(?:'|\/\/|Rem\b).+/i},string:{pattern:/"(?:""|[^"])*"/,greedy:!0},number:[/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:E[+-]?\d+)?/i,/&[bchou][a-z\d]+/i],symbol:/#(?:If|Else|ElseIf|Endif|Pragma)\b/i,keyword:/\b(?:AddHandler|App|Array|As(?:signs)?|Auto|By(?:Ref|Val)|Boolean|Break|Byte|Call|Case|Catch|CFStringRef|CGFloat|Class|Color|Const|Continue|CString|Currency|CurrentMethodName|Declare|Delegate|Dim|Do(?:uble|wnTo)?|Each|Else(?:If)?|End|Enumeration|Event|Exception|Exit|Extends|False|Finally|For|Function|Get|GetTypeInfo|Global|GOTO|If|Implements|In|Inherits|Int(?:erface|eger|8|16|32|64)?|Lib|Loop|Me|Module|Next|Nil|Object|Optional|OSType|ParamArray|Private|Property|Protected|PString|Ptr|Raise(?:Event)?|ReDim|RemoveHandler|Return|Select(?:or)?|Self|Set|Single|Shared|Short|Soft|Static|Step|String|Sub|Super|Text|Then|To|True|Try|Ubound|UInt(?:eger|8|16|32|64)?|Until|Using|Var(?:iant)?|Wend|While|WindowPtr|WString)\b/i,operator:/<[=>]?|>=?|[+\-*\/\\^=]|\b(?:AddressOf|And|Ctype|IsA?|Mod|New|Not|Or|Xor|WeakAddressOf)\b/i,punctuation:/[.,;:()]/}}e.exports=t,t.displayName="xojo",t.aliases=[]},20349(e){"use strict";function t(e){var t,n,r;(t=e).languages.xquery=t.languages.extend("markup",{"xquery-comment":{pattern:/\(:[\s\S]*?:\)/,greedy:!0,alias:"comment"},string:{pattern:/(["'])(?:\1\1|(?!\1)[\s\S])*\1/,greedy:!0},extension:{pattern:/\(#.+?#\)/,alias:"symbol"},variable:/\$[-\w:]+/,axis:{pattern:/(^|[^-])(?:ancestor(?:-or-self)?|attribute|child|descendant(?:-or-self)?|following(?:-sibling)?|parent|preceding(?:-sibling)?|self)(?=::)/,lookbehind:!0,alias:"operator"},"keyword-operator":{pattern:/(^|[^:-])\b(?:and|castable as|div|eq|except|ge|gt|idiv|instance of|intersect|is|le|lt|mod|ne|or|union)\b(?=$|[^:-])/,lookbehind:!0,alias:"operator"},keyword:{pattern:/(^|[^:-])\b(?:as|ascending|at|base-uri|boundary-space|case|cast as|collation|construction|copy-namespaces|declare|default|descending|else|empty (?:greatest|least)|encoding|every|external|for|function|if|import|in|inherit|lax|let|map|module|namespace|no-inherit|no-preserve|option|order(?: by|ed|ing)?|preserve|return|satisfies|schema|some|stable|strict|strip|then|to|treat as|typeswitch|unordered|validate|variable|version|where|xquery)\b(?=$|[^:-])/,lookbehind:!0},function:/[\w-]+(?::[\w-]+)*(?=\s*\()/,"xquery-element":{pattern:/(element\s+)[\w-]+(?::[\w-]+)*/,lookbehind:!0,alias:"tag"},"xquery-attribute":{pattern:/(attribute\s+)[\w-]+(?::[\w-]+)*/,lookbehind:!0,alias:"attr-name"},builtin:{pattern:/(^|[^:-])\b(?:attribute|comment|document|element|processing-instruction|text|xs:(?:anyAtomicType|anyType|anyURI|base64Binary|boolean|byte|date|dateTime|dayTimeDuration|decimal|double|duration|ENTITIES|ENTITY|float|gDay|gMonth|gMonthDay|gYear|gYearMonth|hexBinary|ID|IDREFS?|int|integer|language|long|Name|NCName|negativeInteger|NMTOKENS?|nonNegativeInteger|nonPositiveInteger|normalizedString|NOTATION|positiveInteger|QName|short|string|time|token|unsigned(?:Byte|Int|Long|Short)|untyped(?:Atomic)?|yearMonthDuration))\b(?=$|[^:-])/,lookbehind:!0},number:/\b\d+(?:\.\d+)?(?:E[+-]?\d+)?/,operator:[/[+*=?|@]|\.\.?|:=|!=|<[=<]?|>[=>]?/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}],punctuation:/[[\](){},;:/]/}),t.languages.xquery.tag.pattern=/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|\{(?!\{)(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])+\}|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,t.languages.xquery.tag.inside["attr-value"].pattern=/=(?:("|')(?:\\[\s\S]|\{(?!\{)(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])+\}|(?!\1)[^\\])*\1|[^\s'">=]+)/i,t.languages.xquery.tag.inside["attr-value"].inside.punctuation=/^="|"$/,t.languages.xquery.tag.inside["attr-value"].inside.expression={pattern:/\{(?!\{)(?:\{(?:\{[^{}]*\}|[^{}])*\}|[^{}])+\}/,inside:t.languages.xquery,alias:"language-xquery"},n=function(e){return"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(n).join("")},r=function(e){for(var i=[],a=0;a0&&i[i.length-1].tagName===n(o.content[0].content[1])&&i.pop():"/>"===o.content[o.content.length-1].content||i.push({tagName:n(o.content[0].content[1]),openedBraces:0}):!(i.length>0)||"punctuation"!==o.type||"{"!==o.content||e[a+1]&&"punctuation"===e[a+1].type&&"{"===e[a+1].content||e[a-1]&&"plain-text"===e[a-1].type&&"{"===e[a-1].content?i.length>0&&i[i.length-1].openedBraces>0&&"punctuation"===o.type&&"}"===o.content?i[i.length-1].openedBraces--:"comment"!==o.type&&(s=!0):i[i.length-1].openedBraces++),(s||"string"==typeof o)&&i.length>0&&0===i[i.length-1].openedBraces){var u=n(o);a0&&("string"==typeof e[a-1]||"plain-text"===e[a-1].type)&&(u=n(e[a-1])+u,e.splice(a-1,1),a--),/^\s+$/.test(u)?e[a]=u:e[a]=new t.Token("plain-text",u,null,u)}o.content&&"string"!=typeof o.content&&r(o.content)}},t.hooks.add("after-tokenize",function(e){"xquery"===e.language&&r(e.tokens)})}e.exports=t,t.displayName="xquery",t.aliases=[]},65039(e){"use strict";function t(e){!function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ ]+"+t.source+")?|"+t.source+"(?:[ ]+"+n.source+")?)",i=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source}),a=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function o(e,t){return t=(t||"").replace(/m/g,"")+"m",RegExp(/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,function(){return r}).replace(/<>/g,function(){return e}),t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,function(){return r})),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,function(){return r}).replace(/<>/g,function(){return"(?:"+i+"|"+a+")"})),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:o(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:o(/true|false/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:o(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:o(a),lookbehind:!0,greedy:!0},number:{pattern:o(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(e)}e.exports=t,t.displayName="yaml",t.aliases=["yml"]},80741(e){"use strict";function t(e){e.languages.yang={comment:/\/\*[\s\S]*?\*\/|\/\/.*/,string:{pattern:/"(?:[^\\"]|\\.)*"|'[^']*'/,greedy:!0},keyword:{pattern:/(^|[{};\r\n][ \t]*)[a-z_][\w.-]*/i,lookbehind:!0},namespace:{pattern:/(\s)[a-z_][\w.-]*(?=:)/i,lookbehind:!0},boolean:/\b(?:false|true)\b/,operator:/\+/,punctuation:/[{};:]/}}e.exports=t,t.displayName="yang",t.aliases=[]},86528(e){"use strict";function t(e){!function(e){function t(e){return function(){return e}}var n=/\b(?:align|allowzero|and|asm|async|await|break|cancel|catch|comptime|const|continue|defer|else|enum|errdefer|error|export|extern|fn|for|if|inline|linksection|nakedcc|noalias|null|or|orelse|packed|promise|pub|resume|return|stdcallcc|struct|suspend|switch|test|threadlocal|try|undefined|union|unreachable|usingnamespace|var|volatile|while)\b/,r="\\b(?!"+n.source+")(?!\\d)\\w+\\b",i=/align\s*\((?:[^()]|\([^()]*\))*\)/.source,a=/(?:\?|\bpromise->|(?:\[[^[\]]*\]|\*(?!\*)|\*\*)(?:\s*|\s*const\b|\s*volatile\b|\s*allowzero\b)*)/.source.replace(//g,t(i)),o=/(?:\bpromise\b|(?:\berror\.)?(?:\.)*(?!\s+))/.source.replace(//g,t(r)),s="(?!\\s)(?:!?\\s*(?:"+a+"\\s*)*"+o+")+";e.languages.zig={comment:[{pattern:/\/{3}.*/,alias:"doc-comment"},/\/{2}.*/],string:[{pattern:/(^|[^\\@])c?"(?:[^"\\\r\n]|\\.)*"/,lookbehind:!0,greedy:!0},{pattern:/([\r\n])([ \t]+c?\\{2}).*(?:(?:\r\n?|\n)\2.*)*/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\])'(?:[^'\\\r\n]|\\(?:.|x[a-fA-F\d]{2}|u\{[a-fA-F\d]{1,6}\}))'/,lookbehind:!0,greedy:!0}],builtin:/\B@(?!\d)\w+(?=\s*\()/,label:{pattern:/(\b(?:break|continue)\s*:\s*)\w+\b|\b(?!\d)\w+\b(?=\s*:\s*(?:\{|while\b))/,lookbehind:!0},"class-name":[/\b(?!\d)\w+(?=\s*=\s*(?:(?:extern|packed)\s+)?(?:enum|struct|union)\s*[({])/,{pattern:RegExp(/(:\s*)(?=\s*(?:\s*)?[=;,)])|(?=\s*(?:\s*)?\{)/.source.replace(//g,t(s)).replace(//g,t(i))),lookbehind:!0,inside:null},{pattern:RegExp(/(\)\s*)(?=\s*(?:\s*)?;)/.source.replace(//g,t(s)).replace(//g,t(i))),lookbehind:!0,inside:null}],"builtin-types":{pattern:/\b(?:anyerror|bool|c_u?(?:short|int|long|longlong)|c_longdouble|c_void|comptime_(?:float|int)|[iu](?:8|16|32|64|128|size)|f(?:16|32|64|128)|noreturn|type|void)\b/,alias:"keyword"},keyword:n,function:/\b(?!\d)\w+(?=\s*\()/,number:/\b(?:0b[01]+|0o[0-7]+|0x[a-fA-F\d]+(?:\.[a-fA-F\d]*)?(?:[pP][+-]?[a-fA-F\d]+)?|\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)\b/,boolean:/\b(?:false|true)\b/,operator:/\.[*?]|\.{2,3}|[-=]>|\*\*|\+\+|\|\||(?:<<|>>|[-+*]%|[-+*/%^&|<>!=])=?|[?~]/,punctuation:/[.:,;(){}[\]]/},e.languages.zig["class-name"].forEach(function(t){null===t.inside&&(t.inside=e.languages.zig)})}(e)}e.exports=t,t.displayName="zig",t.aliases=[]},59216(e,t,n){var r=function(e){var t=/\blang(?:uage)?-([\w-]+)\b/i,n=0,r={},i={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=f.reach));S+=E.value.length,E=E.next){var k,x=E.value;if(t.length>e.length)return;if(!(x instanceof a)){var T=1;if(v){if(!(k=o(_,S,e,g)))break;var M=k.index,O=k.index+k[0].length,A=S;for(A+=E.value.length;M>=A;)A+=(E=E.next).value.length;if(A-=E.value.length,S=A,E.value instanceof a)continue;for(var L=E;L!==t.tail&&(Af.reach&&(f.reach=N);var P=E.prev;I&&(P=c(t,P,I),S+=I.length),l(t,P,T);var R=new a(d,m?i.tokenize(C,m):C,y,C);if(E=c(t,P,R),D&&c(t,E,D),T>1){var j={cause:d+","+p,reach:N};s(e,t,n,E.prev,S,j),f&&j.reach>f.reach&&(f.reach=j.reach)}}}}}}function u(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function c(e,t,n){var r=t.next,i={value:n,prev:t,next:r};return t.next=i,r.prev=i,e.length++,i}function l(e,t,n){for(var r=t.next,i=0;i"+a.content+""},!e.document)return e.addEventListener&&(i.disableWorkerMessageHandler||e.addEventListener("message",function(t){var n=JSON.parse(t.data),r=n.language,a=n.code,o=n.immediateClose;e.postMessage(i.highlight(a,i.languages[r],r)),o&&e.close()},!1)),i;var d=i.util.currentScript();function h(){i.manual||i.highlightAll()}if(d&&(i.filename=d.src,d.hasAttribute("data-manual")&&(i.manual=!0)),!i.manual){var p=document.readyState;"loading"===p||"interactive"===p&&d&&d.defer?document.addEventListener("DOMContentLoaded",h):window.requestAnimationFrame?window.requestAnimationFrame(h):window.setTimeout(h,16)}return i}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=r),void 0!==n.g&&(n.g.Prism=r)},89509(e,t,n){/*! safe-buffer. MIT License. Feross Aboukhadijeh */ var r=n(48764),i=r.Buffer;function a(e,t){for(var n in e)t[n]=e[n]}function o(e,t,n){return i(e,t,n)}i.from&&i.alloc&&i.allocUnsafe&&i.allocUnsafeSlow?e.exports=r:(a(r,t),t.Buffer=o),o.prototype=Object.create(i.prototype),a(i,o),o.from=function(e,t,n){if("number"==typeof e)throw TypeError("Argument must not be a number");return i(e,t,n)},o.alloc=function(e,t,n){if("number"!=typeof e)throw TypeError("Argument must be a number");var r=i(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},o.allocUnsafe=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return i(e)},o.allocUnsafeSlow=function(e){if("number"!=typeof e)throw TypeError("Argument must be a number");return r.SlowBuffer(e)}},60053(e,t){"use strict";if(/** @license React v0.18.0 + * scheduler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ Object.defineProperty(t,"__esModule",{value:!0}),"undefined"==typeof window||"function"!=typeof MessageChannel){var n,r,i,a,o,s=null,u=null,c=function(){if(null!==s)try{var e=t.unstable_now();s(!0,e),s=null}catch(n){throw setTimeout(c,0),n}},l=Date.now();t.unstable_now=function(){return Date.now()-l},n=function(e){null!==s?setTimeout(n,0,e):(s=e,setTimeout(c,0))},r=function(e,t){u=setTimeout(e,t)},i=function(){clearTimeout(u)},a=function(){return!1},o=t.unstable_forceFrameRate=function(){}}else{var f=window.performance,d=window.Date,h=window.setTimeout,p=window.clearTimeout;if("undefined"!=typeof console){var b=window.cancelAnimationFrame;"function"!=typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),"function"!=typeof b&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if("object"==typeof f&&"function"==typeof f.now)t.unstable_now=function(){return f.now()};else{var m=d.now();t.unstable_now=function(){return d.now()-m}}var g=!1,v=null,y=-1,w=5,_=0;a=function(){return t.unstable_now()>=_},o=function(){},t.unstable_forceFrameRate=function(e){0>e||125M(o,n))void 0!==u&&0>M(u,o)?(e[r]=u,e[s]=n,r=s):(e[r]=o,e[a]=n,r=a);else if(void 0!==u&&0>M(u,n))e[r]=u,e[s]=n,r=s;else break a}}return t}return null}function M(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}var O=[],A=[],L=1,C=null,I=3,D=!1,N=!1,P=!1;function R(e){for(var t=x(A);null!==t;){if(null===t.callback)T(A);else if(t.startTime<=e)T(A),t.sortIndex=t.expirationTime,k(O,t);else break;t=x(A)}}function j(e){if(P=!1,R(e),!N){if(null!==x(O))N=!0,n(F);else{var t=x(A);null!==t&&r(j,t.startTime-e)}}}function F(e,n){N=!1,P&&(P=!1,i()),D=!0;var o=I;try{for(R(n),C=x(O);null!==C&&(!(C.expirationTime>n)||e&&!a());){var s=C.callback;if(null!==s){C.callback=null,I=C.priorityLevel;var u=s(C.expirationTime<=n);n=t.unstable_now(),"function"==typeof u?C.callback=u:C===x(O)&&T(O),R(n)}else T(O);C=x(O)}if(null!==C)var c=!0;else{var l=x(A);null!==l&&r(j,l.startTime-n),c=!1}return c}finally{C=null,I=o,D=!1}}function Y(e){switch(e){case 1:return -1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var B=o;t.unstable_ImmediatePriority=1,t.unstable_UserBlockingPriority=2,t.unstable_NormalPriority=3,t.unstable_IdlePriority=5,t.unstable_LowPriority=4,t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=I;I=e;try{return t()}finally{I=n}},t.unstable_next=function(e){switch(I){case 1:case 2:case 3:var t=3;break;default:t=I}var n=I;I=t;try{return e()}finally{I=n}},t.unstable_scheduleCallback=function(e,a,o){var s=t.unstable_now();if("object"==typeof o&&null!==o){var u=o.delay;u="number"==typeof u&&0s?(e.sortIndex=u,k(A,e),null===x(O)&&e===x(A)&&(P?i():P=!0,r(j,u-s))):(e.sortIndex=o,k(O,e),N||D||(N=!0,n(F))),e},t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_wrapCallback=function(e){var t=I;return function(){var n=I;I=t;try{return e.apply(this,arguments)}finally{I=n}}},t.unstable_getCurrentPriorityLevel=function(){return I},t.unstable_shouldYield=function(){var e=t.unstable_now();R(e);var n=x(O);return n!==C&&null!==C&&null!==n&&null!==n.callback&&n.startTime<=e&&n.expirationTime>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function c(e,t,n){var r=t.length-1;if(r=0?(i>0&&(e.lastNeed=i-1),i):--r=0?(i>0&&(e.lastNeed=i-2),i):--r=0?(i>0&&(2===i?i=0:e.lastNeed=i-3),i):0}function l(e,t,n){if((192&t[0])!=128)return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if((192&t[1])!=128)return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&(192&t[2])!=128)return e.lastNeed=2,"�"}}function f(e){var t=this.lastTotal-this.lastNeed,n=l(this,e,t);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):void(e.copy(this.lastChar,t,0,e.length),this.lastNeed-=e.length)}function d(e,t){var n=c(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)}function h(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"�":t}function p(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function b(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function m(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function g(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function v(e){return e.toString(this.encoding)}function y(e){return e&&e.length?this.write(e):""}t.s=s,s.prototype.write=function(e){var t,n;if(0===e.length)return"";if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return nOB});var r,i,a,o,s,u,c,l=n(67294),f=n.t(l,2),d=n(97779),h=n(47886),p=n(57209),b=n(32316),m=n(95880),g=n(17051),v=n(71381),y=n(81701),w=n(3022),_=n(60323),E=n(87591),S=n(25649),k=n(28902),x=n(71426),T=n(48884),M=n(94184),O=n.n(M),A=n(55977),L=n(73935),C=function(){if("undefined"!=typeof Map)return Map;function e(e,t){var n=-1;return e.some(function(e,r){return e[0]===t&&(n=r,!0)}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(t){var n=e(this.__entries__,t),r=this.__entries__[n];return r&&r[1]},t.prototype.set=function(t,n){var r=e(this.__entries__,t);~r?this.__entries__[r][1]=n:this.__entries__.push([t,n])},t.prototype.delete=function(t){var n=this.__entries__,r=e(n,t);~r&&n.splice(r,1)},t.prototype.has=function(t){return!!~e(this.__entries__,t)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(e,t){void 0===t&&(t=null);for(var n=0,r=this.__entries__;n0},e.prototype.connect_=function(){I&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),Y?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){I&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(e){var t=e.propertyName,n=void 0===t?"":t;F.some(function(e){return!!~n.indexOf(e)})&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),U=function(e,t){for(var n=0,r=Object.keys(t);n0},e}(),er="undefined"!=typeof WeakMap?new WeakMap:new C,ei=function(){function e(t){if(!(this instanceof e))throw TypeError("Cannot call a class as a function.");if(!arguments.length)throw TypeError("1 argument required, but only 0 present.");var n=B.getInstance(),r=new en(t,n,this);er.set(this,r)}return e}();["observe","unobserve","disconnect"].forEach(function(e){ei.prototype[e]=function(){var t;return(t=er.get(this))[e].apply(t,arguments)}});var ea=void 0!==D.ResizeObserver?D.ResizeObserver:ei;let eo=ea;var es=function(e){var t=[],n=null,r=function(){for(var r=arguments.length,i=Array(r),a=0;a=t||n<0||f&&r>=a}function g(){var e=eb();if(m(e))return v(e);s=setTimeout(g,b(e))}function v(e){return(s=void 0,d&&r)?h(e):(r=i=void 0,o)}function y(){void 0!==s&&clearTimeout(s),c=0,r=u=i=s=void 0}function w(){return void 0===s?o:v(eb())}function _(){var e=eb(),n=m(e);if(r=arguments,i=this,u=e,n){if(void 0===s)return p(u);if(f)return clearTimeout(s),s=setTimeout(g,t),h(u)}return void 0===s&&(s=setTimeout(g,t)),o}return t=ez(t)||0,ed(n)&&(l=!!n.leading,a=(f="maxWait"in n)?eW(ez(n.maxWait)||0,t):a,d="trailing"in n?!!n.trailing:d),_.cancel=y,_.flush=w,_}let eq=eV;var eZ="Expected a function";function eX(e,t,n){var r=!0,i=!0;if("function"!=typeof e)throw TypeError(eZ);return ed(n)&&(r="leading"in n?!!n.leading:r,i="trailing"in n?!!n.trailing:i),eq(e,t,{leading:r,maxWait:t,trailing:i})}let eJ=eX;var eQ={debounce:eq,throttle:eJ},e1=function(e){return eQ[e]},e0=function(e){return"function"==typeof e},e2=function(){return"undefined"==typeof window},e3=function(e){return e instanceof Element||e instanceof HTMLDocument};function e4(e){return(e4="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function e5(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function e6(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&l.createElement(tG.Z,{variant:"indeterminate",classes:r}))};tK.propTypes={fetchCount:el().number.isRequired};let tV=(0,b.withStyles)(tW)(tK);var tq=n(5536);let tZ=n.p+"ba8bbf16ebf8e1d05bef.svg";function tX(){return(tX=Object.assign||function(e){for(var t=1;t120){for(var d=Math.floor(u/80),h=u%80,p=[],b=0;b0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),null!=s&&s.stack)?(Object.defineProperty(nf(b),"stack",{value:s.stack,writable:!0,configurable:!0}),nl(b)):(Error.captureStackTrace?Error.captureStackTrace(nf(b),n):Object.defineProperty(nf(b),"stack",{value:Error().stack,writable:!0,configurable:!0}),b)}return ns(n,[{key:"toString",value:function(){return nw(this)}},{key:t4.YF,get:function(){return"Object"}}]),n}(nd(Error));function ny(e){return void 0===e||0===e.length?void 0:e}function nw(e){var t=e.message;if(e.nodes)for(var n=0,r=e.nodes;n",EOF:"",BANG:"!",DOLLAR:"$",AMP:"&",PAREN_L:"(",PAREN_R:")",SPREAD:"...",COLON:":",EQUALS:"=",AT:"@",BRACKET_L:"[",BRACKET_R:"]",BRACE_L:"{",PIPE:"|",BRACE_R:"}",NAME:"Name",INT:"Int",FLOAT:"Float",STRING:"String",BLOCK_STRING:"BlockString",COMMENT:"Comment"}),nx=n(10143),nT=Object.freeze({QUERY:"QUERY",MUTATION:"MUTATION",SUBSCRIPTION:"SUBSCRIPTION",FIELD:"FIELD",FRAGMENT_DEFINITION:"FRAGMENT_DEFINITION",FRAGMENT_SPREAD:"FRAGMENT_SPREAD",INLINE_FRAGMENT:"INLINE_FRAGMENT",VARIABLE_DEFINITION:"VARIABLE_DEFINITION",SCHEMA:"SCHEMA",SCALAR:"SCALAR",OBJECT:"OBJECT",FIELD_DEFINITION:"FIELD_DEFINITION",ARGUMENT_DEFINITION:"ARGUMENT_DEFINITION",INTERFACE:"INTERFACE",UNION:"UNION",ENUM:"ENUM",ENUM_VALUE:"ENUM_VALUE",INPUT_OBJECT:"INPUT_OBJECT",INPUT_FIELD_DEFINITION:"INPUT_FIELD_DEFINITION"}),nM=n(87392),nO=function(){function e(e){var t=new nS.WU(nk.SOF,0,0,0,0,null);this.source=e,this.lastToken=t,this.token=t,this.line=1,this.lineStart=0}var t=e.prototype;return t.advance=function(){return this.lastToken=this.token,this.token=this.lookahead()},t.lookahead=function(){var e,t=this.token;if(t.kind!==nk.EOF)do t=null!==(e=t.next)&&void 0!==e?e:t.next=nC(this,t);while(t.kind===nk.COMMENT)return t},e}();function nA(e){return e===nk.BANG||e===nk.DOLLAR||e===nk.AMP||e===nk.PAREN_L||e===nk.PAREN_R||e===nk.SPREAD||e===nk.COLON||e===nk.EQUALS||e===nk.AT||e===nk.BRACKET_L||e===nk.BRACKET_R||e===nk.BRACE_L||e===nk.PIPE||e===nk.BRACE_R}function nL(e){return isNaN(e)?nk.EOF:e<127?JSON.stringify(String.fromCharCode(e)):'"\\u'.concat(("00"+e.toString(16).toUpperCase()).slice(-4),'"')}function nC(e,t){for(var n=e.source,r=n.body,i=r.length,a=t.end;a31||9===a))return new nS.WU(nk.COMMENT,t,s,n,r,i,o.slice(t+1,s))}function nN(e,t,n,r,i,a){var o=e.body,s=n,u=t,c=!1;if(45===s&&(s=o.charCodeAt(++u)),48===s){if((s=o.charCodeAt(++u))>=48&&s<=57)throw n_(e,u,"Invalid number, unexpected digit after 0: ".concat(nL(s),"."))}else u=nP(e,u,s),s=o.charCodeAt(u);if(46===s&&(c=!0,s=o.charCodeAt(++u),u=nP(e,u,s),s=o.charCodeAt(u)),(69===s||101===s)&&(c=!0,(43===(s=o.charCodeAt(++u))||45===s)&&(s=o.charCodeAt(++u)),u=nP(e,u,s),s=o.charCodeAt(u)),46===s||nU(s))throw n_(e,u,"Invalid number, expected digit but got: ".concat(nL(s),"."));return new nS.WU(c?nk.FLOAT:nk.INT,t,u,r,i,a,o.slice(t,u))}function nP(e,t,n){var r=e.body,i=t,a=n;if(a>=48&&a<=57){do a=r.charCodeAt(++i);while(a>=48&&a<=57)return i}throw n_(e,i,"Invalid number, expected digit but got: ".concat(nL(a),"."))}function nR(e,t,n,r,i){for(var a=e.body,o=t+1,s=o,u=0,c="";o=48&&e<=57?e-48:e>=65&&e<=70?e-55:e>=97&&e<=102?e-87:-1}function nB(e,t,n,r,i){for(var a=e.body,o=a.length,s=t+1,u=0;s!==o&&!isNaN(u=a.charCodeAt(s))&&(95===u||u>=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122);)++s;return new nS.WU(nk.NAME,t,s,n,r,i,a.slice(t,s))}function nU(e){return 95===e||e>=65&&e<=90||e>=97&&e<=122}function nH(e,t){return new n$(e,t).parseDocument()}var n$=function(){function e(e,t){var n=(0,nx.T)(e)?e:new nx.H(e);this._lexer=new nO(n),this._options=t}var t=e.prototype;return t.parseName=function(){var e=this.expectToken(nk.NAME);return{kind:nE.h.NAME,value:e.value,loc:this.loc(e)}},t.parseDocument=function(){var e=this._lexer.token;return{kind:nE.h.DOCUMENT,definitions:this.many(nk.SOF,this.parseDefinition,nk.EOF),loc:this.loc(e)}},t.parseDefinition=function(){if(this.peek(nk.NAME))switch(this._lexer.token.value){case"query":case"mutation":case"subscription":return this.parseOperationDefinition();case"fragment":return this.parseFragmentDefinition();case"schema":case"scalar":case"type":case"interface":case"union":case"enum":case"input":case"directive":return this.parseTypeSystemDefinition();case"extend":return this.parseTypeSystemExtension()}else if(this.peek(nk.BRACE_L))return this.parseOperationDefinition();else if(this.peekDescription())return this.parseTypeSystemDefinition();throw this.unexpected()},t.parseOperationDefinition=function(){var e,t=this._lexer.token;if(this.peek(nk.BRACE_L))return{kind:nE.h.OPERATION_DEFINITION,operation:"query",name:void 0,variableDefinitions:[],directives:[],selectionSet:this.parseSelectionSet(),loc:this.loc(t)};var n=this.parseOperationType();return this.peek(nk.NAME)&&(e=this.parseName()),{kind:nE.h.OPERATION_DEFINITION,operation:n,name:e,variableDefinitions:this.parseVariableDefinitions(),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseOperationType=function(){var e=this.expectToken(nk.NAME);switch(e.value){case"query":return"query";case"mutation":return"mutation";case"subscription":return"subscription"}throw this.unexpected(e)},t.parseVariableDefinitions=function(){return this.optionalMany(nk.PAREN_L,this.parseVariableDefinition,nk.PAREN_R)},t.parseVariableDefinition=function(){var e=this._lexer.token;return{kind:nE.h.VARIABLE_DEFINITION,variable:this.parseVariable(),type:(this.expectToken(nk.COLON),this.parseTypeReference()),defaultValue:this.expectOptionalToken(nk.EQUALS)?this.parseValueLiteral(!0):void 0,directives:this.parseDirectives(!0),loc:this.loc(e)}},t.parseVariable=function(){var e=this._lexer.token;return this.expectToken(nk.DOLLAR),{kind:nE.h.VARIABLE,name:this.parseName(),loc:this.loc(e)}},t.parseSelectionSet=function(){var e=this._lexer.token;return{kind:nE.h.SELECTION_SET,selections:this.many(nk.BRACE_L,this.parseSelection,nk.BRACE_R),loc:this.loc(e)}},t.parseSelection=function(){return this.peek(nk.SPREAD)?this.parseFragment():this.parseField()},t.parseField=function(){var e,t,n=this._lexer.token,r=this.parseName();return this.expectOptionalToken(nk.COLON)?(e=r,t=this.parseName()):t=r,{kind:nE.h.FIELD,alias:e,name:t,arguments:this.parseArguments(!1),directives:this.parseDirectives(!1),selectionSet:this.peek(nk.BRACE_L)?this.parseSelectionSet():void 0,loc:this.loc(n)}},t.parseArguments=function(e){var t=e?this.parseConstArgument:this.parseArgument;return this.optionalMany(nk.PAREN_L,t,nk.PAREN_R)},t.parseArgument=function(){var e=this._lexer.token,t=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.ARGUMENT,name:t,value:this.parseValueLiteral(!1),loc:this.loc(e)}},t.parseConstArgument=function(){var e=this._lexer.token;return{kind:nE.h.ARGUMENT,name:this.parseName(),value:(this.expectToken(nk.COLON),this.parseValueLiteral(!0)),loc:this.loc(e)}},t.parseFragment=function(){var e=this._lexer.token;this.expectToken(nk.SPREAD);var t=this.expectOptionalKeyword("on");return!t&&this.peek(nk.NAME)?{kind:nE.h.FRAGMENT_SPREAD,name:this.parseFragmentName(),directives:this.parseDirectives(!1),loc:this.loc(e)}:{kind:nE.h.INLINE_FRAGMENT,typeCondition:t?this.parseNamedType():void 0,directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(e)}},t.parseFragmentDefinition=function(){var e,t=this._lexer.token;return(this.expectKeyword("fragment"),(null===(e=this._options)||void 0===e?void 0:e.experimentalFragmentVariables)===!0)?{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),variableDefinitions:this.parseVariableDefinitions(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}:{kind:nE.h.FRAGMENT_DEFINITION,name:this.parseFragmentName(),typeCondition:(this.expectKeyword("on"),this.parseNamedType()),directives:this.parseDirectives(!1),selectionSet:this.parseSelectionSet(),loc:this.loc(t)}},t.parseFragmentName=function(){if("on"===this._lexer.token.value)throw this.unexpected();return this.parseName()},t.parseValueLiteral=function(e){var t=this._lexer.token;switch(t.kind){case nk.BRACKET_L:return this.parseList(e);case nk.BRACE_L:return this.parseObject(e);case nk.INT:return this._lexer.advance(),{kind:nE.h.INT,value:t.value,loc:this.loc(t)};case nk.FLOAT:return this._lexer.advance(),{kind:nE.h.FLOAT,value:t.value,loc:this.loc(t)};case nk.STRING:case nk.BLOCK_STRING:return this.parseStringLiteral();case nk.NAME:switch(this._lexer.advance(),t.value){case"true":return{kind:nE.h.BOOLEAN,value:!0,loc:this.loc(t)};case"false":return{kind:nE.h.BOOLEAN,value:!1,loc:this.loc(t)};case"null":return{kind:nE.h.NULL,loc:this.loc(t)};default:return{kind:nE.h.ENUM,value:t.value,loc:this.loc(t)}}case nk.DOLLAR:if(!e)return this.parseVariable()}throw this.unexpected()},t.parseStringLiteral=function(){var e=this._lexer.token;return this._lexer.advance(),{kind:nE.h.STRING,value:e.value,block:e.kind===nk.BLOCK_STRING,loc:this.loc(e)}},t.parseList=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseValueLiteral(e)};return{kind:nE.h.LIST,values:this.any(nk.BRACKET_L,r,nk.BRACKET_R),loc:this.loc(n)}},t.parseObject=function(e){var t=this,n=this._lexer.token,r=function(){return t.parseObjectField(e)};return{kind:nE.h.OBJECT,fields:this.any(nk.BRACE_L,r,nk.BRACE_R),loc:this.loc(n)}},t.parseObjectField=function(e){var t=this._lexer.token,n=this.parseName();return this.expectToken(nk.COLON),{kind:nE.h.OBJECT_FIELD,name:n,value:this.parseValueLiteral(e),loc:this.loc(t)}},t.parseDirectives=function(e){for(var t=[];this.peek(nk.AT);)t.push(this.parseDirective(e));return t},t.parseDirective=function(e){var t=this._lexer.token;return this.expectToken(nk.AT),{kind:nE.h.DIRECTIVE,name:this.parseName(),arguments:this.parseArguments(e),loc:this.loc(t)}},t.parseTypeReference=function(){var e,t=this._lexer.token;return(this.expectOptionalToken(nk.BRACKET_L)?(e=this.parseTypeReference(),this.expectToken(nk.BRACKET_R),e={kind:nE.h.LIST_TYPE,type:e,loc:this.loc(t)}):e=this.parseNamedType(),this.expectOptionalToken(nk.BANG))?{kind:nE.h.NON_NULL_TYPE,type:e,loc:this.loc(t)}:e},t.parseNamedType=function(){var e=this._lexer.token;return{kind:nE.h.NAMED_TYPE,name:this.parseName(),loc:this.loc(e)}},t.parseTypeSystemDefinition=function(){var e=this.peekDescription()?this._lexer.lookahead():this._lexer.token;if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaDefinition();case"scalar":return this.parseScalarTypeDefinition();case"type":return this.parseObjectTypeDefinition();case"interface":return this.parseInterfaceTypeDefinition();case"union":return this.parseUnionTypeDefinition();case"enum":return this.parseEnumTypeDefinition();case"input":return this.parseInputObjectTypeDefinition();case"directive":return this.parseDirectiveDefinition()}throw this.unexpected(e)},t.peekDescription=function(){return this.peek(nk.STRING)||this.peek(nk.BLOCK_STRING)},t.parseDescription=function(){if(this.peekDescription())return this.parseStringLiteral()},t.parseSchemaDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("schema");var n=this.parseDirectives(!0),r=this.many(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);return{kind:nE.h.SCHEMA_DEFINITION,description:t,directives:n,operationTypes:r,loc:this.loc(e)}},t.parseOperationTypeDefinition=function(){var e=this._lexer.token,t=this.parseOperationType();this.expectToken(nk.COLON);var n=this.parseNamedType();return{kind:nE.h.OPERATION_TYPE_DEFINITION,operation:t,type:n,loc:this.loc(e)}},t.parseScalarTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("scalar");var n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.SCALAR_TYPE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("type");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.OBJECT_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseImplementsInterfaces=function(){var e;if(!this.expectOptionalKeyword("implements"))return[];if((null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLImplementsInterfaces)===!0){var t=[];this.expectOptionalToken(nk.AMP);do t.push(this.parseNamedType());while(this.expectOptionalToken(nk.AMP)||this.peek(nk.NAME))return t}return this.delimitedMany(nk.AMP,this.parseNamedType)},t.parseFieldsDefinition=function(){var e;return(null===(e=this._options)||void 0===e?void 0:e.allowLegacySDLEmptyFields)===!0&&this.peek(nk.BRACE_L)&&this._lexer.lookahead().kind===nk.BRACE_R?(this._lexer.advance(),this._lexer.advance(),[]):this.optionalMany(nk.BRACE_L,this.parseFieldDefinition,nk.BRACE_R)},t.parseFieldDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseArgumentDefs();this.expectToken(nk.COLON);var i=this.parseTypeReference(),a=this.parseDirectives(!0);return{kind:nE.h.FIELD_DEFINITION,description:t,name:n,arguments:r,type:i,directives:a,loc:this.loc(e)}},t.parseArgumentDefs=function(){return this.optionalMany(nk.PAREN_L,this.parseInputValueDef,nk.PAREN_R)},t.parseInputValueDef=function(){var e,t=this._lexer.token,n=this.parseDescription(),r=this.parseName();this.expectToken(nk.COLON);var i=this.parseTypeReference();this.expectOptionalToken(nk.EQUALS)&&(e=this.parseValueLiteral(!0));var a=this.parseDirectives(!0);return{kind:nE.h.INPUT_VALUE_DEFINITION,description:n,name:r,type:i,defaultValue:e,directives:a,loc:this.loc(t)}},t.parseInterfaceTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("interface");var n=this.parseName(),r=this.parseImplementsInterfaces(),i=this.parseDirectives(!0),a=this.parseFieldsDefinition();return{kind:nE.h.INTERFACE_TYPE_DEFINITION,description:t,name:n,interfaces:r,directives:i,fields:a,loc:this.loc(e)}},t.parseUnionTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("union");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseUnionMemberTypes();return{kind:nE.h.UNION_TYPE_DEFINITION,description:t,name:n,directives:r,types:i,loc:this.loc(e)}},t.parseUnionMemberTypes=function(){return this.expectOptionalToken(nk.EQUALS)?this.delimitedMany(nk.PIPE,this.parseNamedType):[]},t.parseEnumTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("enum");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseEnumValuesDefinition();return{kind:nE.h.ENUM_TYPE_DEFINITION,description:t,name:n,directives:r,values:i,loc:this.loc(e)}},t.parseEnumValuesDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseEnumValueDefinition,nk.BRACE_R)},t.parseEnumValueDefinition=function(){var e=this._lexer.token,t=this.parseDescription(),n=this.parseName(),r=this.parseDirectives(!0);return{kind:nE.h.ENUM_VALUE_DEFINITION,description:t,name:n,directives:r,loc:this.loc(e)}},t.parseInputObjectTypeDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("input");var n=this.parseName(),r=this.parseDirectives(!0),i=this.parseInputFieldsDefinition();return{kind:nE.h.INPUT_OBJECT_TYPE_DEFINITION,description:t,name:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInputFieldsDefinition=function(){return this.optionalMany(nk.BRACE_L,this.parseInputValueDef,nk.BRACE_R)},t.parseTypeSystemExtension=function(){var e=this._lexer.lookahead();if(e.kind===nk.NAME)switch(e.value){case"schema":return this.parseSchemaExtension();case"scalar":return this.parseScalarTypeExtension();case"type":return this.parseObjectTypeExtension();case"interface":return this.parseInterfaceTypeExtension();case"union":return this.parseUnionTypeExtension();case"enum":return this.parseEnumTypeExtension();case"input":return this.parseInputObjectTypeExtension()}throw this.unexpected(e)},t.parseSchemaExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("schema");var t=this.parseDirectives(!0),n=this.optionalMany(nk.BRACE_L,this.parseOperationTypeDefinition,nk.BRACE_R);if(0===t.length&&0===n.length)throw this.unexpected();return{kind:nE.h.SCHEMA_EXTENSION,directives:t,operationTypes:n,loc:this.loc(e)}},t.parseScalarTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("scalar");var t=this.parseName(),n=this.parseDirectives(!0);if(0===n.length)throw this.unexpected();return{kind:nE.h.SCALAR_TYPE_EXTENSION,name:t,directives:n,loc:this.loc(e)}},t.parseObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("type");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.OBJECT_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseInterfaceTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("interface");var t=this.parseName(),n=this.parseImplementsInterfaces(),r=this.parseDirectives(!0),i=this.parseFieldsDefinition();if(0===n.length&&0===r.length&&0===i.length)throw this.unexpected();return{kind:nE.h.INTERFACE_TYPE_EXTENSION,name:t,interfaces:n,directives:r,fields:i,loc:this.loc(e)}},t.parseUnionTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("union");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseUnionMemberTypes();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.UNION_TYPE_EXTENSION,name:t,directives:n,types:r,loc:this.loc(e)}},t.parseEnumTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("enum");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseEnumValuesDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.ENUM_TYPE_EXTENSION,name:t,directives:n,values:r,loc:this.loc(e)}},t.parseInputObjectTypeExtension=function(){var e=this._lexer.token;this.expectKeyword("extend"),this.expectKeyword("input");var t=this.parseName(),n=this.parseDirectives(!0),r=this.parseInputFieldsDefinition();if(0===n.length&&0===r.length)throw this.unexpected();return{kind:nE.h.INPUT_OBJECT_TYPE_EXTENSION,name:t,directives:n,fields:r,loc:this.loc(e)}},t.parseDirectiveDefinition=function(){var e=this._lexer.token,t=this.parseDescription();this.expectKeyword("directive"),this.expectToken(nk.AT);var n=this.parseName(),r=this.parseArgumentDefs(),i=this.expectOptionalKeyword("repeatable");this.expectKeyword("on");var a=this.parseDirectiveLocations();return{kind:nE.h.DIRECTIVE_DEFINITION,description:t,name:n,arguments:r,repeatable:i,locations:a,loc:this.loc(e)}},t.parseDirectiveLocations=function(){return this.delimitedMany(nk.PIPE,this.parseDirectiveLocation)},t.parseDirectiveLocation=function(){var e=this._lexer.token,t=this.parseName();if(void 0!==nT[t.value])return t;throw this.unexpected(e)},t.loc=function(e){var t;if((null===(t=this._options)||void 0===t?void 0:t.noLocation)!==!0)return new nS.Ye(e,this._lexer.lastToken,this._lexer.source)},t.peek=function(e){return this._lexer.token.kind===e},t.expectToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t;throw n_(this._lexer.source,t.start,"Expected ".concat(nG(e),", found ").concat(nz(t),"."))},t.expectOptionalToken=function(e){var t=this._lexer.token;if(t.kind===e)return this._lexer.advance(),t},t.expectKeyword=function(e){var t=this._lexer.token;if(t.kind===nk.NAME&&t.value===e)this._lexer.advance();else throw n_(this._lexer.source,t.start,'Expected "'.concat(e,'", found ').concat(nz(t),"."))},t.expectOptionalKeyword=function(e){var t=this._lexer.token;return t.kind===nk.NAME&&t.value===e&&(this._lexer.advance(),!0)},t.unexpected=function(e){var t=null!=e?e:this._lexer.token;return n_(this._lexer.source,t.start,"Unexpected ".concat(nz(t),"."))},t.any=function(e,t,n){this.expectToken(e);for(var r=[];!this.expectOptionalToken(n);)r.push(t.call(this));return r},t.optionalMany=function(e,t,n){if(this.expectOptionalToken(e)){var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r}return[]},t.many=function(e,t,n){this.expectToken(e);var r=[];do r.push(t.call(this));while(!this.expectOptionalToken(n))return r},t.delimitedMany=function(e,t){this.expectOptionalToken(e);var n=[];do n.push(t.call(this));while(this.expectOptionalToken(e))return n},e}();function nz(e){var t=e.value;return nG(e.kind)+(null!=t?' "'.concat(t,'"'):"")}function nG(e){return nA(e)?'"'.concat(e,'"'):e}var nW=new Map,nK=new Map,nV=!0,nq=!1;function nZ(e){return e.replace(/[\s,]+/g," ").trim()}function nX(e){return nZ(e.source.body.substring(e.start,e.end))}function nJ(e){var t=new Set,n=[];return e.definitions.forEach(function(e){if("FragmentDefinition"===e.kind){var r=e.name.value,i=nX(e.loc),a=nK.get(r);a&&!a.has(i)?nV&&console.warn("Warning: fragment with name "+r+" already exists.\ngraphql-tag enforces all fragment names across your application to be unique; read more about\nthis in the docs: http://dev.apollodata.com/core/fragments.html#unique-names"):a||nK.set(r,a=new Set),a.add(i),t.has(i)||(t.add(i),n.push(e))}else n.push(e)}),(0,t0.pi)((0,t0.pi)({},e),{definitions:n})}function nQ(e){var t=new Set(e.definitions);t.forEach(function(e){e.loc&&delete e.loc,Object.keys(e).forEach(function(n){var r=e[n];r&&"object"==typeof r&&t.add(r)})});var n=e.loc;return n&&(delete n.startToken,delete n.endToken),e}function n1(e){var t=nZ(e);if(!nW.has(t)){var n=nH(e,{experimentalFragmentVariables:nq,allowLegacyFragmentVariables:nq});if(!n||"Document"!==n.kind)throw Error("Not a valid GraphQL document.");nW.set(t,nQ(nJ(n)))}return nW.get(t)}function n0(e){for(var t=[],n=1;n, or pass an ApolloClient instance in via options.'):(0,n7.kG)(!!n,32),n}var rb=n(10542),rm=n(53712),rg=n(21436),rv=Object.prototype.hasOwnProperty;function ry(e,t){return void 0===t&&(t=Object.create(null)),rw(rp(t.client),e).useQuery(t)}function rw(e,t){var n=(0,l.useRef)();n.current&&e===n.current.client&&t===n.current.query||(n.current=new r_(e,t,n.current));var r=n.current,i=(0,l.useState)(0),a=(i[0],i[1]);return r.forceUpdate=function(){a(function(e){return e+1})},r}var r_=function(){function e(e,t,n){this.client=e,this.query=t,this.ssrDisabledResult=(0,rb.J)({loading:!0,data:void 0,error:void 0,networkStatus:rc.I.loading}),this.skipStandbyResult=(0,rb.J)({loading:!1,data:void 0,error:void 0,networkStatus:rc.I.ready}),this.toQueryResultCache=new(re.mr?WeakMap:Map),rh(t,r.Query);var i=n&&n.result,a=i&&i.data;a&&(this.previousData=a)}return e.prototype.forceUpdate=function(){__DEV__&&n7.kG.warn("Calling default no-op implementation of InternalState#forceUpdate")},e.prototype.executeQuery=function(e){var t,n=this;e.query&&Object.assign(this,{query:e.query}),this.watchQueryOptions=this.createWatchQueryOptions(this.queryHookOptions=e);var r=this.observable.reobserveAsConcast(this.getObsQueryOptions());return this.previousData=(null===(t=this.result)||void 0===t?void 0:t.data)||this.previousData,this.result=void 0,this.forceUpdate(),new Promise(function(e){var t;r.subscribe({next:function(e){t=e},error:function(){e(n.toQueryResult(n.observable.getCurrentResult()))},complete:function(){e(n.toQueryResult(t))}})})},e.prototype.useQuery=function(e){var t=this;this.renderPromises=(0,l.useContext)((0,rs.K)()).renderPromises,this.useOptions(e);var n=this.useObservableQuery(),r=rn((0,l.useCallback)(function(){if(t.renderPromises)return function(){};var e=function(){var e=t.result,r=n.getCurrentResult();!(e&&e.loading===r.loading&&e.networkStatus===r.networkStatus&&(0,ra.D)(e.data,r.data))&&t.setResult(r)},r=function(a){var o=n.last;i.unsubscribe();try{n.resetLastResults(),i=n.subscribe(e,r)}finally{n.last=o}if(!rv.call(a,"graphQLErrors"))throw a;var s=t.result;(!s||s&&s.loading||!(0,ra.D)(a,s.error))&&t.setResult({data:s&&s.data,error:a,loading:!1,networkStatus:rc.I.error})},i=n.subscribe(e,r);return function(){return setTimeout(function(){return i.unsubscribe()})}},[n,this.renderPromises,this.client.disableNetworkFetches,]),function(){return t.getCurrentResult()},function(){return t.getCurrentResult()});return this.unsafeHandlePartialRefetch(r),this.toQueryResult(r)},e.prototype.useOptions=function(t){var n,r=this.createWatchQueryOptions(this.queryHookOptions=t),i=this.watchQueryOptions;!(0,ra.D)(r,i)&&(this.watchQueryOptions=r,i&&this.observable&&(this.observable.reobserve(this.getObsQueryOptions()),this.previousData=(null===(n=this.result)||void 0===n?void 0:n.data)||this.previousData,this.result=void 0)),this.onCompleted=t.onCompleted||e.prototype.onCompleted,this.onError=t.onError||e.prototype.onError,(this.renderPromises||this.client.disableNetworkFetches)&&!1===this.queryHookOptions.ssr&&!this.queryHookOptions.skip?this.result=this.ssrDisabledResult:this.queryHookOptions.skip||"standby"===this.watchQueryOptions.fetchPolicy?this.result=this.skipStandbyResult:(this.result===this.ssrDisabledResult||this.result===this.skipStandbyResult)&&(this.result=void 0)},e.prototype.getObsQueryOptions=function(){var e=[],t=this.client.defaultOptions.watchQuery;return t&&e.push(t),this.queryHookOptions.defaultOptions&&e.push(this.queryHookOptions.defaultOptions),e.push((0,rm.o)(this.observable&&this.observable.options,this.watchQueryOptions)),e.reduce(ro.J)},e.prototype.createWatchQueryOptions=function(e){void 0===e&&(e={});var t,n=e.skip,r=Object.assign((e.ssr,e.onCompleted,e.onError,e.defaultOptions,(0,n8._T)(e,["skip","ssr","onCompleted","onError","defaultOptions"])),{query:this.query});if(this.renderPromises&&("network-only"===r.fetchPolicy||"cache-and-network"===r.fetchPolicy)&&(r.fetchPolicy="cache-first"),r.variables||(r.variables={}),n){var i=r.fetchPolicy,a=void 0===i?this.getDefaultFetchPolicy():i,o=r.initialFetchPolicy;Object.assign(r,{initialFetchPolicy:void 0===o?a:o,fetchPolicy:"standby"})}else r.fetchPolicy||(r.fetchPolicy=(null===(t=this.observable)||void 0===t?void 0:t.options.initialFetchPolicy)||this.getDefaultFetchPolicy());return r},e.prototype.getDefaultFetchPolicy=function(){var e,t;return(null===(e=this.queryHookOptions.defaultOptions)||void 0===e?void 0:e.fetchPolicy)||(null===(t=this.client.defaultOptions.watchQuery)||void 0===t?void 0:t.fetchPolicy)||"cache-first"},e.prototype.onCompleted=function(e){},e.prototype.onError=function(e){},e.prototype.useObservableQuery=function(){var e=this.observable=this.renderPromises&&this.renderPromises.getSSRObservable(this.watchQueryOptions)||this.observable||this.client.watchQuery(this.getObsQueryOptions());this.obsQueryFields=(0,l.useMemo)(function(){return{refetch:e.refetch.bind(e),reobserve:e.reobserve.bind(e),fetchMore:e.fetchMore.bind(e),updateQuery:e.updateQuery.bind(e),startPolling:e.startPolling.bind(e),stopPolling:e.stopPolling.bind(e),subscribeToMore:e.subscribeToMore.bind(e)}},[e]);var t=!(!1===this.queryHookOptions.ssr||this.queryHookOptions.skip);return this.renderPromises&&t&&(this.renderPromises.registerSSRObservable(e),e.getCurrentResult().loading&&this.renderPromises.addObservableQueryPromise(e)),e},e.prototype.setResult=function(e){var t=this.result;t&&t.data&&(this.previousData=t.data),this.result=e,this.forceUpdate(),this.handleErrorOrCompleted(e)},e.prototype.handleErrorOrCompleted=function(e){var t=this;if(!e.loading){var n=this.toApolloError(e);Promise.resolve().then(function(){n?t.onError(n):e.data&&t.onCompleted(e.data)}).catch(function(e){__DEV__&&n7.kG.warn(e)})}},e.prototype.toApolloError=function(e){return(0,rg.O)(e.errors)?new ru.cA({graphQLErrors:e.errors}):e.error},e.prototype.getCurrentResult=function(){return this.result||this.handleErrorOrCompleted(this.result=this.observable.getCurrentResult()),this.result},e.prototype.toQueryResult=function(e){var t=this.toQueryResultCache.get(e);if(t)return t;var n=e.data,r=(e.partial,(0,n8._T)(e,["data","partial"]));return this.toQueryResultCache.set(e,t=(0,n8.pi)((0,n8.pi)((0,n8.pi)({data:n},r),this.obsQueryFields),{client:this.client,observable:this.observable,variables:this.observable.variables,called:!this.queryHookOptions.skip,previousData:this.previousData})),!t.error&&(0,rg.O)(e.errors)&&(t.error=new ru.cA({graphQLErrors:e.errors})),t},e.prototype.unsafeHandlePartialRefetch=function(e){e.partial&&this.queryHookOptions.partialRefetch&&!e.loading&&(!e.data||0===Object.keys(e.data).length)&&"cache-only"!==this.observable.options.fetchPolicy&&(Object.assign(e,{loading:!0,networkStatus:rc.I.refetch}),this.observable.refetch())},e}();function rE(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:{};return ry(i$,e)},iG=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"50",10),r=iz({variables:{offset:(t-1)*n,limit:n},fetchPolicy:"network-only"}),i=r.data,a=r.loading,o=r.error;return a?l.createElement(ij,null):o?l.createElement(iN,{error:o}):i?l.createElement(iD,{chains:i.chains.results,page:t,pageSize:n,total:i.chains.metadata.total}):null},iW=n(67932),iK=n(8126),iV="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function iq(e){if(iZ())return Intl.DateTimeFormat.supportedLocalesOf(e)[0]}function iZ(){return("undefined"==typeof Intl?"undefined":iV(Intl))==="object"&&"function"==typeof Intl.DateTimeFormat}var iX="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},iJ=function(){function e(e,t){for(var n=0;n=i.length)break;s=i[o++]}else{if((o=i.next()).done)break;s=o.value}var s,u=s;if((void 0===e?"undefined":iX(e))!=="object")return;e=e[u]}return e}},{key:"put",value:function(){for(var e=arguments.length,t=Array(e),n=0;n=o.length)break;c=o[u++]}else{if((u=o.next()).done)break;c=u.value}var c,l=c;"object"!==iX(a[l])&&(a[l]={}),a=a[l]}return a[i]=r}}]),e}();let i0=i1;var i2=new i0;function i3(e,t){if(!iZ())return function(e){return e.toString()};var n=i5(e),r=JSON.stringify(t),i=i2.get(String(n),r)||i2.put(String(n),r,new Intl.DateTimeFormat(n,t));return function(e){return i.format(e)}}var i4={};function i5(e){var t=e.toString();return i4[t]?i4[t]:i4[t]=iq(e)}var i6="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function i9(e){return i8(e)?e:new Date(e)}function i8(e){return e instanceof Date||i7(e)}function i7(e){return(void 0===e?"undefined":i6(e))==="object"&&"function"==typeof e.getTime}var ae=n(54087),at=n.n(ae);function an(e,t){if(0===e.length)return 0;for(var n=0,r=e.length-1,i=void 0;n<=r;){var a=t(e[i=Math.floor((r+n)/2)]);if(0===a)return i;if(a<0){if((n=i+1)>r)return n}else if((r=i-1)=t.nextUpdateTime)ao(t,this.instances);else break}},scheduleNextTick:function(){var e=this;this.scheduledTick=at()(function(){e.tick(),e.scheduleNextTick()})},start:function(){this.scheduleNextTick()},stop:function(){at().cancel(this.scheduledTick)}};function aa(e){var t=ar(e.getNextValue(),2),n=t[0],r=t[1];e.setValue(n),e.nextUpdateTime=r}function ao(e,t){aa(e),au(t,e),as(t,e)}function as(e,t){var n=ac(e,t);e.splice(n,0,t)}function au(e,t){var n=e.indexOf(t);e.splice(n,1)}function ac(e,t){var n=t.nextUpdateTime;return an(e,function(e){return e.nextUpdateTime===n?0:e.nextUpdateTime>n?1:-1})}var al=(0,ec.oneOfType)([(0,ec.shape)({minTime:ec.number,formatAs:ec.string.isRequired}),(0,ec.shape)({test:ec.func,formatAs:ec.string.isRequired}),(0,ec.shape)({minTime:ec.number,format:ec.func.isRequired}),(0,ec.shape)({test:ec.func,format:ec.func.isRequired})]),af=(0,ec.oneOfType)([ec.string,(0,ec.shape)({steps:(0,ec.arrayOf)(al).isRequired,labels:(0,ec.oneOfType)([ec.string,(0,ec.arrayOf)(ec.string)]).isRequired,round:ec.string})]),ad=Object.assign||function(e){for(var t=1;t=0)&&Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function ab(e){var t=e.date,n=e.future,r=e.timeStyle,i=e.round,a=e.minTimeLeft,o=e.tooltip,s=e.component,u=e.container,c=e.wrapperComponent,f=e.wrapperProps,d=e.locale,h=e.locales,p=e.formatVerboseDate,b=e.verboseDateFormat,m=e.updateInterval,g=e.tick,v=ap(e,["date","future","timeStyle","round","minTimeLeft","tooltip","component","container","wrapperComponent","wrapperProps","locale","locales","formatVerboseDate","verboseDateFormat","updateInterval","tick"]),y=(0,l.useMemo)(function(){return d&&(h=[d]),h.concat(iK.Z.getDefaultLocale())},[d,h]),w=(0,l.useMemo)(function(){return new iK.Z(y)},[y]);t=(0,l.useMemo)(function(){return i9(t)},[t]);var _=(0,l.useCallback)(function(){var e=Date.now(),o=void 0;if(n&&e>=t.getTime()&&(e=t.getTime(),o=!0),void 0!==a){var s=t.getTime()-1e3*a;e>s&&(e=s,o=!0)}var u=w.format(t,r,{getTimeToNextUpdate:!0,now:e,future:n,round:i}),c=ah(u,2),l=c[0],f=c[1];return f=o?av:m||f||6e4,[l,e+f]},[t,n,r,m,i,a,w]),E=(0,l.useRef)();E.current=_;var S=(0,l.useMemo)(_,[]),k=ah(S,2),x=k[0],T=k[1],M=(0,l.useState)(x),O=ah(M,2),A=O[0],L=O[1],C=ah((0,l.useState)(),2),I=C[0],D=C[1],N=(0,l.useRef)();(0,l.useEffect)(function(){if(g)return N.current=ai.add({getNextValue:function(){return E.current()},setValue:L,nextUpdateTime:T}),function(){return N.current.stop()}},[g]),(0,l.useEffect)(function(){if(N.current)N.current.forceUpdate();else{var e=_(),t=ah(e,1)[0];L(t)}},[_]),(0,l.useEffect)(function(){D(!0)},[]);var P=(0,l.useMemo)(function(){if("undefined"!=typeof window)return i3(y,b)},[y,b]),R=(0,l.useMemo)(function(){if("undefined"!=typeof window)return p?p(t):P(t)},[t,p,P]),j=l.createElement(s,ad({date:t,verboseDate:I?R:void 0,tooltip:o},v),A),F=c||u;return F?l.createElement(F,ad({},f,{verboseDate:I?R:void 0}),j):j}ab.propTypes={date:el().oneOfType([el().instanceOf(Date),el().number]).isRequired,locale:el().string,locales:el().arrayOf(el().string),future:el().bool,timeStyle:af,round:el().string,minTimeLeft:el().number,component:el().elementType.isRequired,tooltip:el().bool.isRequired,formatVerboseDate:el().func,verboseDateFormat:el().object,updateInterval:el().oneOfType([el().number,el().arrayOf(el().shape({threshold:el().number,interval:el().number.isRequired}))]),tick:el().bool,wrapperComponent:el().func,wrapperProps:el().object},ab.defaultProps={locales:[],component:ay,tooltip:!0,verboseDateFormat:{weekday:"long",day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"},tick:!0},ab=l.memo(ab);let am=ab;var ag,av=31536e9;function ay(e){var t=e.date,n=e.verboseDate,r=e.tooltip,i=e.children,a=ap(e,["date","verboseDate","tooltip","children"]),o=(0,l.useMemo)(function(){return t.toISOString()},[t]);return l.createElement("time",ad({},a,{dateTime:o,title:r?n:void 0}),i)}ay.propTypes={date:el().instanceOf(Date).isRequired,verboseDate:el().string,tooltip:el().bool.isRequired,children:el().string.isRequired};var aw=n(30381),a_=n.n(aw),aE=n(31657);function aS(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ak(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?new ru.cA({graphQLErrors:i}):void 0;if(u===s.current.mutationId&&!c.ignoreResults){var f={called:!0,loading:!1,data:r,error:l,client:a};s.current.isMounted&&!(0,ra.D)(s.current.result,f)&&o(s.current.result=f)}var d=e.onCompleted||(null===(n=s.current.options)||void 0===n?void 0:n.onCompleted);return null==d||d(t.data,c),t}).catch(function(t){if(u===s.current.mutationId&&s.current.isMounted){var n,r={loading:!1,error:t,data:void 0,called:!0,client:a};(0,ra.D)(s.current.result,r)||o(s.current.result=r)}var i=e.onError||(null===(n=s.current.options)||void 0===n?void 0:n.onError);if(i)return i(t,c),{data:void 0,errors:t};throw t})},[]),c=(0,l.useCallback)(function(){s.current.isMounted&&o({called:!1,loading:!1,client:n})},[]);return(0,l.useEffect)(function(){return s.current.isMounted=!0,function(){s.current.isMounted=!1}},[]),[u,(0,n8.pi)({reset:c},a)]}var ou=n(59067),oc=n(28428),ol=n(11186),of=n(78513);function od(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var oh=function(e){return(0,b.createStyles)({paper:{display:"flex",margin:"".concat(2.5*e.spacing.unit,"px 0"),padding:"".concat(3*e.spacing.unit,"px ").concat(3.5*e.spacing.unit,"px")},content:{flex:1,width:"100%"},actions:od({marginTop:-(1.5*e.spacing.unit),marginLeft:-(4*e.spacing.unit)},e.breakpoints.up("sm"),{marginLeft:0,marginRight:-(1.5*e.spacing.unit)}),itemBlock:{border:"1px solid rgba(224, 224, 224, 1)",borderRadius:e.shape.borderRadius,padding:2*e.spacing.unit,marginTop:e.spacing.unit},itemBlockText:{overflowWrap:"anywhere"}})},op=(0,b.withStyles)(oh)(function(e){var t=e.actions,n=e.children,r=e.classes;return l.createElement(ia.default,{className:r.paper},l.createElement("div",{className:r.content},n),t&&l.createElement("div",{className:r.actions},t))}),ob=function(e){var t=e.title;return l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},t)},om=function(e){var t=e.children,n=e.value;return l.createElement(x.default,{variant:"body1",noWrap:!0},t||n)},og=(0,b.withStyles)(oh)(function(e){var t=e.children,n=e.classes,r=e.value;return l.createElement("div",{className:n.itemBlock},l.createElement(x.default,{variant:"body1",className:n.itemBlockText},t||r))});function ov(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]-1}let sZ=sq;function sX(e,t){var n=this.__data__,r=s$(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}let sJ=sX;function sQ(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e-1&&e%1==0&&e<=cI}let cN=cD;var cP="[object Arguments]",cR="[object Array]",cj="[object Boolean]",cF="[object Date]",cY="[object Error]",cB="[object Function]",cU="[object Map]",cH="[object Number]",c$="[object Object]",cz="[object RegExp]",cG="[object Set]",cW="[object String]",cK="[object WeakMap]",cV="[object ArrayBuffer]",cq="[object DataView]",cZ="[object Float64Array]",cX="[object Int8Array]",cJ="[object Int16Array]",cQ="[object Int32Array]",c1="[object Uint8Array]",c0="[object Uint8ClampedArray]",c2="[object Uint16Array]",c3="[object Uint32Array]",c4={};function c5(e){return eD(e)&&cN(e.length)&&!!c4[eC(e)]}c4["[object Float32Array]"]=c4[cZ]=c4[cX]=c4[cJ]=c4[cQ]=c4[c1]=c4[c0]=c4[c2]=c4[c3]=!0,c4[cP]=c4[cR]=c4[cV]=c4[cj]=c4[cq]=c4[cF]=c4[cY]=c4[cB]=c4[cU]=c4[cH]=c4[c$]=c4[cz]=c4[cG]=c4[cW]=c4[cK]=!1;let c6=c5;function c9(e){return function(t){return e(t)}}let c8=c9;var c7=n(79730),le=c7.Z&&c7.Z.isTypedArray,lt=le?c8(le):c6;let ln=lt;var lr=Object.prototype.hasOwnProperty;function li(e,t){var n=cT(e),r=!n&&ck(e),i=!n&&!r&&(0,cM.Z)(e),a=!n&&!r&&!i&&ln(e),o=n||r||i||a,s=o?cm(e.length,String):[],u=s.length;for(var c in e)(t||lr.call(e,c))&&!(o&&("length"==c||i&&("offset"==c||"parent"==c)||a&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||cC(c,u)))&&s.push(c);return s}let la=li;var lo=Object.prototype;function ls(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||lo)}let lu=ls;var lc=sM(Object.keys,Object);let ll=lc;var lf=Object.prototype.hasOwnProperty;function ld(e){if(!lu(e))return ll(e);var t=[];for(var n in Object(e))lf.call(e,n)&&"constructor"!=n&&t.push(n);return t}let lh=ld;function lp(e){return null!=e&&cN(e.length)&&!ui(e)}let lb=lp;function lm(e){return lb(e)?la(e):lh(e)}let lg=lm;function lv(e,t){return e&&cp(t,lg(t),e)}let ly=lv;function lw(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}let l_=lw;var lE=Object.prototype.hasOwnProperty;function lS(e){if(!ed(e))return l_(e);var t=lu(e),n=[];for(var r in e)"constructor"==r&&(t||!lE.call(e,r))||n.push(r);return n}let lk=lS;function lx(e){return lb(e)?la(e,!0):lk(e)}let lT=lx;function lM(e,t){return e&&cp(t,lT(t),e)}let lO=lM;var lA=n(42896);function lL(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n=0||(i[n]=e[n]);return i}function hc(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}var hl=function(e){return Array.isArray(e)&&0===e.length},hf=function(e){return"function"==typeof e},hd=function(e){return null!==e&&"object"==typeof e},hh=function(e){return String(Math.floor(Number(e)))===e},hp=function(e){return"[object String]"===Object.prototype.toString.call(e)},hb=function(e){return 0===l.Children.count(e)},hm=function(e){return hd(e)&&hf(e.then)};function hg(e,t,n,r){void 0===r&&(r=0);for(var i=d8(t);e&&r=0?[]:{}}}return(0===a?e:i)[o[a]]===n?e:(void 0===n?delete i[o[a]]:i[o[a]]=n,0===a&&void 0===n&&delete r[o[a]],r)}function hy(e,t,n,r){void 0===n&&(n=new WeakMap),void 0===r&&(r={});for(var i=0,a=Object.keys(e);i0?t.map(function(t){return x(t,hg(e,t))}):[Promise.resolve("DO_NOT_DELETE_YOU_WILL_BE_FIRED")]).then(function(e){return e.reduce(function(e,n,r){return"DO_NOT_DELETE_YOU_WILL_BE_FIRED"===n||n&&(e=hv(e,t[r],n)),e},{})})},[x]),M=(0,l.useCallback)(function(e){return Promise.all([T(e),h.validationSchema?k(e):{},h.validate?S(e):{}]).then(function(e){var t=e[0],n=e[1],r=e[2];return sx.all([t,n,r],{arrayMerge:hC})})},[h.validate,h.validationSchema,T,S,k]),O=hP(function(e){return void 0===e&&(e=_.values),E({type:"SET_ISVALIDATING",payload:!0}),M(e).then(function(e){return v.current&&(E({type:"SET_ISVALIDATING",payload:!1}),sh()(_.errors,e)||E({type:"SET_ERRORS",payload:e})),e})});(0,l.useEffect)(function(){o&&!0===v.current&&sh()(p.current,h.initialValues)&&O(p.current)},[o,O]);var A=(0,l.useCallback)(function(e){var t=e&&e.values?e.values:p.current,n=e&&e.errors?e.errors:b.current?b.current:h.initialErrors||{},r=e&&e.touched?e.touched:m.current?m.current:h.initialTouched||{},i=e&&e.status?e.status:g.current?g.current:h.initialStatus;p.current=t,b.current=n,m.current=r,g.current=i;var a=function(){E({type:"RESET_FORM",payload:{isSubmitting:!!e&&!!e.isSubmitting,errors:n,touched:r,status:i,values:t,isValidating:!!e&&!!e.isValidating,submitCount:e&&e.submitCount&&"number"==typeof e.submitCount?e.submitCount:0}})};if(h.onReset){var o=h.onReset(_.values,V);hm(o)?o.then(a):a()}else a()},[h.initialErrors,h.initialStatus,h.initialTouched]);(0,l.useEffect)(function(){!0===v.current&&!sh()(p.current,h.initialValues)&&(c&&(p.current=h.initialValues,A()),o&&O(p.current))},[c,h.initialValues,A,o,O]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(b.current,h.initialErrors)&&(b.current=h.initialErrors||hk,E({type:"SET_ERRORS",payload:h.initialErrors||hk}))},[c,h.initialErrors]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(m.current,h.initialTouched)&&(m.current=h.initialTouched||hx,E({type:"SET_TOUCHED",payload:h.initialTouched||hx}))},[c,h.initialTouched]),(0,l.useEffect)(function(){c&&!0===v.current&&!sh()(g.current,h.initialStatus)&&(g.current=h.initialStatus,E({type:"SET_STATUS",payload:h.initialStatus}))},[c,h.initialStatus,h.initialTouched]);var L=hP(function(e){if(y.current[e]&&hf(y.current[e].validate)){var t=hg(_.values,e),n=y.current[e].validate(t);return hm(n)?(E({type:"SET_ISVALIDATING",payload:!0}),n.then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}}),E({type:"SET_ISVALIDATING",payload:!1})})):(E({type:"SET_FIELD_ERROR",payload:{field:e,value:n}}),Promise.resolve(n))}return h.validationSchema?(E({type:"SET_ISVALIDATING",payload:!0}),k(_.values,e).then(function(e){return e}).then(function(t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t[e]}}),E({type:"SET_ISVALIDATING",payload:!1})})):Promise.resolve()}),C=(0,l.useCallback)(function(e,t){var n=t.validate;y.current[e]={validate:n}},[]),I=(0,l.useCallback)(function(e){delete y.current[e]},[]),D=hP(function(e,t){return E({type:"SET_TOUCHED",payload:e}),(void 0===t?i:t)?O(_.values):Promise.resolve()}),N=(0,l.useCallback)(function(e){E({type:"SET_ERRORS",payload:e})},[]),P=hP(function(e,t){var r=hf(e)?e(_.values):e;return E({type:"SET_VALUES",payload:r}),(void 0===t?n:t)?O(r):Promise.resolve()}),R=(0,l.useCallback)(function(e,t){E({type:"SET_FIELD_ERROR",payload:{field:e,value:t}})},[]),j=hP(function(e,t,r){return E({type:"SET_FIELD_VALUE",payload:{field:e,value:t}}),(void 0===r?n:r)?O(hv(_.values,e,t)):Promise.resolve()}),F=(0,l.useCallback)(function(e,t){var n,r=t,i=e;if(!hp(e)){e.persist&&e.persist();var a=e.target?e.target:e.currentTarget,o=a.type,s=a.name,u=a.id,c=a.value,l=a.checked,f=(a.outerHTML,a.options),d=a.multiple;r=t||s||u,i=/number|range/.test(o)?(n=parseFloat(c),isNaN(n)?"":n):/checkbox/.test(o)?hD(hg(_.values,r),l,c):d?hI(f):c}r&&j(r,i)},[j,_.values]),Y=hP(function(e){if(hp(e))return function(t){return F(t,e)};F(e)}),B=hP(function(e,t,n){return void 0===t&&(t=!0),E({type:"SET_FIELD_TOUCHED",payload:{field:e,value:t}}),(void 0===n?i:n)?O(_.values):Promise.resolve()}),U=(0,l.useCallback)(function(e,t){e.persist&&e.persist();var n,r=e.target,i=r.name,a=r.id;r.outerHTML,B(t||i||a,!0)},[B]),H=hP(function(e){if(hp(e))return function(t){return U(t,e)};U(e)}),$=(0,l.useCallback)(function(e){hf(e)?E({type:"SET_FORMIK_STATE",payload:e}):E({type:"SET_FORMIK_STATE",payload:function(){return e}})},[]),z=(0,l.useCallback)(function(e){E({type:"SET_STATUS",payload:e})},[]),G=(0,l.useCallback)(function(e){E({type:"SET_ISSUBMITTING",payload:e})},[]),W=hP(function(){return E({type:"SUBMIT_ATTEMPT"}),O().then(function(e){var t,n=e instanceof Error;if(!n&&0===Object.keys(e).length){try{if(void 0===(t=q()))return}catch(r){throw r}return Promise.resolve(t).then(function(e){return v.current&&E({type:"SUBMIT_SUCCESS"}),e}).catch(function(e){if(v.current)throw E({type:"SUBMIT_FAILURE"}),e})}if(v.current&&(E({type:"SUBMIT_FAILURE"}),n))throw e})}),K=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(e.stopPropagation)&&e.stopPropagation(),W().catch(function(e){console.warn("Warning: An unhandled error was caught from submitForm()",e)})}),V={resetForm:A,validateForm:O,validateField:L,setErrors:N,setFieldError:R,setFieldTouched:B,setFieldValue:j,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,setFormikState:$,submitForm:W},q=hP(function(){return f(_.values,V)}),Z=hP(function(e){e&&e.preventDefault&&hf(e.preventDefault)&&e.preventDefault(),e&&e.stopPropagation&&hf(e.stopPropagation)&&e.stopPropagation(),A()}),X=(0,l.useCallback)(function(e){return{value:hg(_.values,e),error:hg(_.errors,e),touched:!!hg(_.touched,e),initialValue:hg(p.current,e),initialTouched:!!hg(m.current,e),initialError:hg(b.current,e)}},[_.errors,_.touched,_.values]),J=(0,l.useCallback)(function(e){return{setValue:function(t,n){return j(e,t,n)},setTouched:function(t,n){return B(e,t,n)},setError:function(t){return R(e,t)}}},[j,B,R]),Q=(0,l.useCallback)(function(e){var t=hd(e),n=t?e.name:e,r=hg(_.values,n),i={name:n,value:r,onChange:Y,onBlur:H};if(t){var a=e.type,o=e.value,s=e.as,u=e.multiple;"checkbox"===a?void 0===o?i.checked=!!r:(i.checked=!!(Array.isArray(r)&&~r.indexOf(o)),i.value=o):"radio"===a?(i.checked=r===o,i.value=o):"select"===s&&u&&(i.value=i.value||[],i.multiple=!0)}return i},[H,Y,_.values]),ee=(0,l.useMemo)(function(){return!sh()(p.current,_.values)},[p.current,_.values]),et=(0,l.useMemo)(function(){return void 0!==s?ee?_.errors&&0===Object.keys(_.errors).length:!1!==s&&hf(s)?s(h):s:_.errors&&0===Object.keys(_.errors).length},[s,ee,_.errors,h]);return ho({},_,{initialValues:p.current,initialErrors:b.current,initialTouched:m.current,initialStatus:g.current,handleBlur:H,handleChange:Y,handleReset:Z,handleSubmit:K,resetForm:A,setErrors:N,setFormikState:$,setFieldTouched:B,setFieldValue:j,setFieldError:R,setStatus:z,setSubmitting:G,setTouched:D,setValues:P,submitForm:W,validateForm:O,validateField:L,isValid:et,dirty:ee,unregisterField:I,registerField:C,getFieldProps:Q,getFieldMeta:X,getFieldHelpers:J,validateOnBlur:i,validateOnChange:n,validateOnMount:o})}function hM(e){var t=hT(e),n=e.component,r=e.children,i=e.render,a=e.innerRef;return(0,l.useImperativeHandle)(a,function(){return t}),(0,l.createElement)(h_,{value:t},n?(0,l.createElement)(n,t):i?i(t):r?hf(r)?r(t):hb(r)?null:l.Children.only(r):null)}function hO(e){var t={};if(e.inner){if(0===e.inner.length)return hv(t,e.path,e.message);for(var n=e.inner,r=Array.isArray(n),i=0,n=r?n:n[Symbol.iterator]();;){if(r){if(i>=n.length)break;a=n[i++]}else{if((i=n.next()).done)break;a=i.value}var a,o=a;hg(t,o.path)||(t=hv(t,o.path,o.message))}}return t}function hA(e,t,n,r){void 0===n&&(n=!1),void 0===r&&(r={});var i=hL(e);return t[n?"validateSync":"validate"](i,{abortEarly:!1,context:r})}function hL(e){var t=Array.isArray(e)?[]:{};for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=String(n);!0===Array.isArray(e[r])?t[r]=e[r].map(function(e){return!0===Array.isArray(e)||sj(e)?hL(e):""!==e?e:void 0}):sj(e[r])?t[r]=hL(e[r]):t[r]=""!==e[r]?e[r]:void 0}return t}function hC(e,t,n){var r=e.slice();return t.forEach(function(t,i){if(void 0===r[i]){var a=!1!==n.clone&&n.isMergeableObject(t);r[i]=a?sx(Array.isArray(t)?[]:{},t,n):t}else n.isMergeableObject(t)?r[i]=sx(e[i],t,n):-1===e.indexOf(t)&&r.push(t)}),r}function hI(e){return Array.from(e).filter(function(e){return e.selected}).map(function(e){return e.value})}function hD(e,t,n){if("boolean"==typeof e)return Boolean(t);var r=[],i=!1,a=-1;if(Array.isArray(e))r=e,i=(a=e.indexOf(n))>=0;else if(!n||"true"==n||"false"==n)return Boolean(t);return t&&n&&!i?r.concat(n):i?r.slice(0,a).concat(r.slice(a+1)):r}var hN="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement?l.useLayoutEffect:l.useEffect;function hP(e){var t=(0,l.useRef)(e);return hN(function(){t.current=e}),(0,l.useCallback)(function(){for(var e=arguments.length,n=Array(e),r=0;re?t:e},0);return Array.from(ho({},e,{length:t+1}))};(function(e){function t(t){var n;return(n=e.call(this,t)||this).updateArrayField=function(e,t,r){var i=n.props,a=i.name;(0,i.formik.setFormikState)(function(n){var i="function"==typeof r?r:e,o="function"==typeof t?t:e,s=hv(n.values,a,e(hg(n.values,a))),u=r?i(hg(n.errors,a)):void 0,c=t?o(hg(n.touched,a)):void 0;return hl(u)&&(u=void 0),hl(c)&&(c=void 0),ho({},n,{values:s,errors:r?hv(n.errors,a,u):n.errors,touched:t?hv(n.touched,a,c):n.touched})})},n.push=function(e){return n.updateArrayField(function(t){return[].concat(hH(t),[ha(e)])},!1,!1)},n.handlePush=function(e){return function(){return n.push(e)}},n.swap=function(e,t){return n.updateArrayField(function(n){return hY(n,e,t)},!0,!0)},n.handleSwap=function(e,t){return function(){return n.swap(e,t)}},n.move=function(e,t){return n.updateArrayField(function(n){return hF(n,e,t)},!0,!0)},n.handleMove=function(e,t){return function(){return n.move(e,t)}},n.insert=function(e,t){return n.updateArrayField(function(n){return hB(n,e,t)},function(t){return hB(t,e,null)},function(t){return hB(t,e,null)})},n.handleInsert=function(e,t){return function(){return n.insert(e,t)}},n.replace=function(e,t){return n.updateArrayField(function(n){return hU(n,e,t)},!1,!1)},n.handleReplace=function(e,t){return function(){return n.replace(e,t)}},n.unshift=function(e){var t=-1;return n.updateArrayField(function(n){var r=n?[e].concat(n):[e];return t<0&&(t=r.length),r},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n},function(e){var n=e?[null].concat(e):[null];return t<0&&(t=n.length),n}),t},n.handleUnshift=function(e){return function(){return n.unshift(e)}},n.handleRemove=function(e){return function(){return n.remove(e)}},n.handlePop=function(){return function(){return n.pop()}},n.remove=n.remove.bind(hc(n)),n.pop=n.pop.bind(hc(n)),n}hs(t,e);var n=t.prototype;return n.componentDidUpdate=function(e){this.props.validateOnChange&&this.props.formik.validateOnChange&&!sh()(hg(e.formik.values,e.name),hg(this.props.formik.values,this.props.name))&&this.props.formik.validateForm(this.props.formik.values)},n.remove=function(e){var t;return this.updateArrayField(function(n){var r=n?hH(n):[];return t||(t=r[e]),hf(r.splice)&&r.splice(e,1),r},!0,!0),t},n.pop=function(){var e;return this.updateArrayField(function(t){var n=t;return e||(e=n&&n.pop&&n.pop()),n},!0,!0),e},n.render=function(){var e={push:this.push,pop:this.pop,swap:this.swap,move:this.move,insert:this.insert,replace:this.replace,unshift:this.unshift,remove:this.remove,handlePush:this.handlePush,handlePop:this.handlePop,handleSwap:this.handleSwap,handleMove:this.handleMove,handleInsert:this.handleInsert,handleReplace:this.handleReplace,handleUnshift:this.handleUnshift,handleRemove:this.handleRemove},t=this.props,n=t.component,r=t.render,i=t.children,a=t.name,o=hu(t.formik,["validate","validationSchema"]),s=ho({},e,{form:o,name:a});return n?(0,l.createElement)(n,s):r?r(s):i?"function"==typeof i?i(s):hb(i)?null:l.Children.only(i):null},t})(l.Component).defaultProps={validateOnChange:!0},l.Component,l.Component;var h$=n(24802),hz=n(71209),hG=n(91750),hW=n(11970),hK=n(4689),hV=n(67598),hq=function(){return(hq=Object.assign||function(e){for(var t,n=1,r=arguments.length;nt.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var i=0,r=Object.getOwnPropertySymbols(e);it.indexOf(r[i])&&(n[r[i]]=e[r[i]]);return n}function hX(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form,o=a.isSubmitting,s=a.touched,u=a.errors,c=e.onBlur,l=e.helperText,f=hZ(e,["disabled","field","form","onBlur","helperText"]),d=hg(u,i.name),h=hg(s,i.name)&&!!d;return hq(hq({variant:f.variant,error:h,helperText:h?d:l,disabled:null!=t?t:o,onBlur:null!=c?c:function(e){r(null!=e?e:i.name)}},i),f)}function hJ(e){var t=e.children,n=hZ(e,["children"]);return(0,l.createElement)(i_.Z,hq({},hX(n)),t)}function hQ(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=(e.type,e.onBlur),s=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h1(e){return(0,l.createElement)(h$.Z,hq({},hQ(e)))}function h0(e){var t,n=e.disabled,r=e.field,i=r.onBlur,a=hZ(r,["onBlur"]),o=e.form.isSubmitting,s=(e.type,e.onBlur),u=hZ(e,["disabled","field","form","type","onBlur"]);return hq(hq({disabled:null!=n?n:o,indeterminate:!Array.isArray(a.value)&&null==a.value,onBlur:null!=s?s:function(e){i(null!=e?e:a.name)}},a),u)}function h2(e){return(0,l.createElement)(hz.Z,hq({},h0(e)))}function h3(e){var t=e.Label,n=hZ(e,["Label"]);return(0,l.createElement)(hG.Z,hq({control:(0,l.createElement)(hz.Z,hq({},h0(n)))},t))}function h4(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h5(e){return(0,l.createElement)(hW.default,hq({},h4(e)))}function h6(e){var t=e.field,n=t.onBlur,r=hZ(t,["onBlur"]),i=(e.form,e.onBlur),a=hZ(e,["field","form","onBlur"]);return hq(hq({onBlur:null!=i?i:function(e){n(null!=e?e:r.name)}},r),a)}function h9(e){return(0,l.createElement)(hK.Z,hq({},h6(e)))}function h8(e){var t=e.disabled,n=e.field,r=n.onBlur,i=hZ(n,["onBlur"]),a=e.form.isSubmitting,o=e.onBlur,s=hZ(e,["disabled","field","form","onBlur"]);return hq(hq({disabled:null!=t?t:a,onBlur:null!=o?o:function(e){r(null!=e?e:i.name)}},i),s)}function h7(e){return(0,l.createElement)(hV.default,hq({},h8(e)))}hJ.displayName="FormikMaterialUITextField",h1.displayName="FormikMaterialUISwitch",h2.displayName="FormikMaterialUICheckbox",h3.displayName="FormikMaterialUICheckboxWithLabel",h5.displayName="FormikMaterialUISelect",h9.displayName="FormikMaterialUIRadioGroup",h7.displayName="FormikMaterialUIInputBase";try{a=Map}catch(pe){}try{o=Set}catch(pt){}function pn(e,t,n){if(!e||"object"!=typeof e||"function"==typeof e)return e;if(e.nodeType&&"cloneNode"in e)return e.cloneNode(!0);if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return RegExp(e);if(Array.isArray(e))return e.map(pr);if(a&&e instanceof a)return new Map(Array.from(e.entries()));if(o&&e instanceof o)return new Set(Array.from(e.values()));if(e instanceof Object){t.push(e);var r=Object.create(e);for(var i in n.push(r),e){var s=t.findIndex(function(t){return t===e[i]});r[i]=s>-1?n[s]:pn(e[i],t,n)}return r}return e}function pr(e){return pn(e,[],[])}let pi=Object.prototype.toString,pa=Error.prototype.toString,po=RegExp.prototype.toString,ps="undefined"!=typeof Symbol?Symbol.prototype.toString:()=>"",pu=/^Symbol\((.*)\)(.*)$/;function pc(e){if(e!=+e)return"NaN";let t=0===e&&1/e<0;return t?"-0":""+e}function pl(e,t=!1){if(null==e||!0===e||!1===e)return""+e;let n=typeof e;if("number"===n)return pc(e);if("string"===n)return t?`"${e}"`:e;if("function"===n)return"[Function "+(e.name||"anonymous")+"]";if("symbol"===n)return ps.call(e).replace(pu,"Symbol($1)");let r=pi.call(e).slice(8,-1);return"Date"===r?isNaN(e.getTime())?""+e:e.toISOString(e):"Error"===r||e instanceof Error?"["+pa.call(e)+"]":"RegExp"===r?po.call(e):null}function pf(e,t){let n=pl(e,t);return null!==n?n:JSON.stringify(e,function(e,n){let r=pl(this[e],t);return null!==r?r:n},2)}let pd={default:"${path} is invalid",required:"${path} is a required field",oneOf:"${path} must be one of the following values: ${values}",notOneOf:"${path} must not be one of the following values: ${values}",notType({path:e,type:t,value:n,originalValue:r}){let i=null!=r&&r!==n,a=`${e} must be a \`${t}\` type, but the final value was: \`${pf(n,!0)}\``+(i?` (cast from the value \`${pf(r,!0)}\`).`:".");return null===n&&(a+='\n If "null" is intended as an empty value be sure to mark the schema as `.nullable()`'),a},defined:"${path} must be defined"},ph={length:"${path} must be exactly ${length} characters",min:"${path} must be at least ${min} characters",max:"${path} must be at most ${max} characters",matches:'${path} must match the following: "${regex}"',email:"${path} must be a valid email",url:"${path} must be a valid URL",uuid:"${path} must be a valid UUID",trim:"${path} must be a trimmed string",lowercase:"${path} must be a lowercase string",uppercase:"${path} must be a upper case string"},pp={min:"${path} must be greater than or equal to ${min}",max:"${path} must be less than or equal to ${max}",lessThan:"${path} must be less than ${less}",moreThan:"${path} must be greater than ${more}",positive:"${path} must be a positive number",negative:"${path} must be a negative number",integer:"${path} must be an integer"},pb={min:"${path} field must be later than ${min}",max:"${path} field must be at earlier than ${max}"},pm={isValue:"${path} field must be ${value}"},pg={noUnknown:"${path} field has unspecified keys: ${unknown}"},pv={min:"${path} field must have at least ${min} items",max:"${path} field must have less than or equal to ${max} items",length:"${path} must be have ${length} items"};Object.assign(Object.create(null),{mixed:pd,string:ph,number:pp,date:pb,object:pg,array:pv,boolean:pm});var py=n(18721),pw=n.n(py);let p_=e=>e&&e.__isYupSchema__;class pE{constructor(e,t){if(this.refs=e,this.refs=e,"function"==typeof t){this.fn=t;return}if(!pw()(t,"is"))throw TypeError("`is:` is required for `when()` conditions");if(!t.then&&!t.otherwise)throw TypeError("either `then:` or `otherwise:` is required for `when()` conditions");let{is:n,then:r,otherwise:i}=t,a="function"==typeof n?n:(...e)=>e.every(e=>e===n);this.fn=function(...e){let t=e.pop(),n=e.pop(),o=a(...e)?r:i;if(o)return"function"==typeof o?o(n):n.concat(o.resolve(t))}}resolve(e,t){let n=this.refs.map(e=>e.getValue(null==t?void 0:t.value,null==t?void 0:t.parent,null==t?void 0:t.context)),r=this.fn.apply(e,n.concat(e,t));if(void 0===r||r===e)return e;if(!p_(r))throw TypeError("conditions must return a schema object");return r.resolve(t)}}let pS=pE;function pk(e){return null==e?[]:[].concat(e)}function px(){return(px=Object.assign||function(e){for(var t=1;tpf(t[n])):"function"==typeof e?e(t):e}static isError(e){return e&&"ValidationError"===e.name}constructor(e,t,n,r){super(),this.name="ValidationError",this.value=t,this.path=n,this.type=r,this.errors=[],this.inner=[],pk(e).forEach(e=>{pM.isError(e)?(this.errors.push(...e.errors),this.inner=this.inner.concat(e.inner.length?e.inner:e)):this.errors.push(e)}),this.message=this.errors.length>1?`${this.errors.length} errors occurred`:this.errors[0],Error.captureStackTrace&&Error.captureStackTrace(this,pM)}}let pO=e=>{let t=!1;return(...n)=>{t||(t=!0,e(...n))}};function pA(e,t){let{endEarly:n,tests:r,args:i,value:a,errors:o,sort:s,path:u}=e,c=pO(t),l=r.length,f=[];if(o=o||[],!l)return o.length?c(new pM(o,a,u)):c(null,a);for(let d=0;d=0||(i[n]=e[n]);return i}function pj(e){function t(t,n){let{value:r,path:i="",label:a,options:o,originalValue:s,sync:u}=t,c=pR(t,["value","path","label","options","originalValue","sync"]),{name:l,test:f,params:d,message:h}=e,{parent:p,context:b}=o;function m(e){return pN.isRef(e)?e.getValue(r,p,b):e}function g(e={}){let t=pC()(pP({value:r,originalValue:s,label:a,path:e.path||i},d,e.params),m),n=new pM(pM.formatError(e.message||h,t),r,t.path,e.type||l);return n.params=t,n}let v=pP({path:i,parent:p,type:l,createError:g,resolve:m,options:o,originalValue:s},c);if(!u){try{Promise.resolve(f.call(v,r,v)).then(e=>{pM.isError(e)?n(e):e?n(null,e):n(g())})}catch(y){n(y)}return}let w;try{var _;if(w=f.call(v,r,v),"function"==typeof(null==(_=w)?void 0:_.then))throw Error(`Validation test of type: "${v.type}" returned a Promise during a synchronous validate. This test will finish after the validate call has returned`)}catch(E){n(E);return}pM.isError(w)?n(w):w?n(null,w):n(g())}return t.OPTIONS=e,t}pN.prototype.__isYupRef=!0;let pF=e=>e.substr(0,e.length-1).substr(1);function pY(e,t,n,r=n){let i,a,o;return t?((0,pI.forEach)(t,(s,u,c)=>{let l=u?pF(s):s;if((e=e.resolve({context:r,parent:i,value:n})).innerType){let f=c?parseInt(l,10):0;if(n&&f>=n.length)throw Error(`Yup.reach cannot resolve an array item at index: ${s}, in the path: ${t}. because there is no value at that index. `);i=n,n=n&&n[f],e=e.innerType}if(!c){if(!e.fields||!e.fields[l])throw Error(`The schema does not contain the path: ${t}. (failed at: ${o} which is a type: "${e._type}")`);i=n,n=n&&n[l],e=e.fields[l]}a=l,o=u?"["+s+"]":"."+s}),{schema:e,parent:i,parentPath:a}):{parent:i,parentPath:t,schema:e}}class pB{constructor(){this.list=new Set,this.refs=new Map}get size(){return this.list.size+this.refs.size}describe(){let e=[];for(let t of this.list)e.push(t);for(let[,n]of this.refs)e.push(n.describe());return e}toArray(){return Array.from(this.list).concat(Array.from(this.refs.values()))}add(e){pN.isRef(e)?this.refs.set(e.key,e):this.list.add(e)}delete(e){pN.isRef(e)?this.refs.delete(e.key):this.list.delete(e)}has(e,t){if(this.list.has(e))return!0;let n,r=this.refs.values();for(;!(n=r.next()).done;)if(t(n.value)===e)return!0;return!1}clone(){let e=new pB;return e.list=new Set(this.list),e.refs=new Map(this.refs),e}merge(e,t){let n=this.clone();return e.list.forEach(e=>n.add(e)),e.refs.forEach(e=>n.add(e)),t.list.forEach(e=>n.delete(e)),t.refs.forEach(e=>n.delete(e)),n}}function pU(){return(pU=Object.assign||function(e){for(var t=1;t{this.typeError(pd.notType)}),this.type=(null==e?void 0:e.type)||"mixed",this.spec=pU({strip:!1,strict:!1,abortEarly:!0,recursive:!0,nullable:!1,presence:"optional"},null==e?void 0:e.spec)}get _type(){return this.type}_typeCheck(e){return!0}clone(e){if(this._mutate)return e&&Object.assign(this.spec,e),this;let t=Object.create(Object.getPrototypeOf(this));return t.type=this.type,t._typeError=this._typeError,t._whitelistError=this._whitelistError,t._blacklistError=this._blacklistError,t._whitelist=this._whitelist.clone(),t._blacklist=this._blacklist.clone(),t.exclusiveTests=pU({},this.exclusiveTests),t.deps=[...this.deps],t.conditions=[...this.conditions],t.tests=[...this.tests],t.transforms=[...this.transforms],t.spec=pr(pU({},this.spec,e)),t}label(e){var t=this.clone();return t.spec.label=e,t}meta(...e){if(0===e.length)return this.spec.meta;let t=this.clone();return t.spec.meta=Object.assign(t.spec.meta||{},e[0]),t}withMutation(e){let t=this._mutate;this._mutate=!0;let n=e(this);return this._mutate=t,n}concat(e){if(!e||e===this)return this;if(e.type!==this.type&&"mixed"!==this.type)throw TypeError(`You cannot \`concat()\` schema's of different types: ${this.type} and ${e.type}`);let t=this,n=e.clone(),r=pU({},t.spec,n.spec);return n.spec=r,n._typeError||(n._typeError=t._typeError),n._whitelistError||(n._whitelistError=t._whitelistError),n._blacklistError||(n._blacklistError=t._blacklistError),n._whitelist=t._whitelist.merge(e._whitelist,e._blacklist),n._blacklist=t._blacklist.merge(e._blacklist,e._whitelist),n.tests=t.tests,n.exclusiveTests=t.exclusiveTests,n.withMutation(t=>{e.tests.forEach(e=>{t.test(e.OPTIONS)})}),n}isType(e){return!!this.spec.nullable&&null===e||this._typeCheck(e)}resolve(e){let t=this;if(t.conditions.length){let n=t.conditions;(t=t.clone()).conditions=[],t=(t=n.reduce((t,n)=>n.resolve(t,e),t)).resolve(e)}return t}cast(e,t={}){let n=this.resolve(pU({value:e},t)),r=n._cast(e,t);if(void 0!==e&&!1!==t.assert&&!0!==n.isType(r)){let i=pf(e),a=pf(r);throw TypeError(`The value of ${t.path||"field"} could not be cast to a value that satisfies the schema type: "${n._type}". + +attempted value: ${i} +`+(a!==i?`result of cast: ${a}`:""))}return r}_cast(e,t){let n=void 0===e?e:this.transforms.reduce((t,n)=>n.call(this,t,e,this),e);return void 0===n&&(n=this.getDefault()),n}_validate(e,t={},n){let{sync:r,path:i,from:a=[],originalValue:o=e,strict:s=this.spec.strict,abortEarly:u=this.spec.abortEarly}=t,c=e;s||(c=this._cast(c,pU({assert:!1},t)));let l={value:c,path:i,options:t,originalValue:o,schema:this,label:this.spec.label,sync:r,from:a},f=[];this._typeError&&f.push(this._typeError),this._whitelistError&&f.push(this._whitelistError),this._blacklistError&&f.push(this._blacklistError),pA({args:l,value:c,path:i,sync:r,tests:f,endEarly:u},e=>{if(e)return void n(e,c);pA({tests:this.tests,args:l,path:i,sync:r,value:c,endEarly:u},n)})}validate(e,t,n){let r=this.resolve(pU({},t,{value:e}));return"function"==typeof n?r._validate(e,t,n):new Promise((n,i)=>r._validate(e,t,(e,t)=>{e?i(e):n(t)}))}validateSync(e,t){let n;return this.resolve(pU({},t,{value:e}))._validate(e,pU({},t,{sync:!0}),(e,t)=>{if(e)throw e;n=t}),n}isValid(e,t){return this.validate(e,t).then(()=>!0,e=>{if(pM.isError(e))return!1;throw e})}isValidSync(e,t){try{return this.validateSync(e,t),!0}catch(n){if(pM.isError(n))return!1;throw n}}_getDefault(){let e=this.spec.default;return null==e?e:"function"==typeof e?e.call(this):pr(e)}getDefault(e){return this.resolve(e||{})._getDefault()}default(e){return 0===arguments.length?this._getDefault():this.clone({default:e})}strict(e=!0){var t=this.clone();return t.spec.strict=e,t}_isPresent(e){return null!=e}defined(e=pd.defined){return this.test({message:e,name:"defined",exclusive:!0,test:e=>void 0!==e})}required(e=pd.required){return this.clone({presence:"required"}).withMutation(t=>t.test({message:e,name:"required",exclusive:!0,test(e){return this.schema._isPresent(e)}}))}notRequired(){var e=this.clone({presence:"optional"});return e.tests=e.tests.filter(e=>"required"!==e.OPTIONS.name),e}nullable(e=!0){return this.clone({nullable:!1!==e})}transform(e){var t=this.clone();return t.transforms.push(e),t}test(...e){let t;if(void 0===(t=1===e.length?"function"==typeof e[0]?{test:e[0]}:e[0]:2===e.length?{name:e[0],test:e[1]}:{name:e[0],message:e[1],test:e[2]}).message&&(t.message=pd.default),"function"!=typeof t.test)throw TypeError("`test` is a required parameters");let n=this.clone(),r=pj(t),i=t.exclusive||t.name&&!0===n.exclusiveTests[t.name];if(t.exclusive&&!t.name)throw TypeError("Exclusive tests must provide a unique `name` identifying the test");return t.name&&(n.exclusiveTests[t.name]=!!t.exclusive),n.tests=n.tests.filter(e=>e.OPTIONS.name!==t.name||!i&&e.OPTIONS.test!==r.OPTIONS.test),n.tests.push(r),n}when(e,t){Array.isArray(e)||"string"==typeof e||(t=e,e=".");let n=this.clone(),r=pk(e).map(e=>new pN(e));return r.forEach(e=>{e.isSibling&&n.deps.push(e.key)}),n.conditions.push(new pS(r,t)),n}typeError(e){var t=this.clone();return t._typeError=pj({message:e,name:"typeError",test(e){return!!(void 0===e||this.schema.isType(e))||this.createError({params:{type:this.schema._type}})}}),t}oneOf(e,t=pd.oneOf){var n=this.clone();return e.forEach(e=>{n._whitelist.add(e),n._blacklist.delete(e)}),n._whitelistError=pj({message:t,name:"oneOf",test(e){if(void 0===e)return!0;let t=this.schema._whitelist;return!!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}notOneOf(e,t=pd.notOneOf){var n=this.clone();return e.forEach(e=>{n._blacklist.add(e),n._whitelist.delete(e)}),n._blacklistError=pj({message:t,name:"notOneOf",test(e){let t=this.schema._blacklist;return!t.has(e,this.resolve)||this.createError({params:{values:t.toArray().join(", ")}})}}),n}strip(e=!0){let t=this.clone();return t.spec.strip=e,t}describe(){let e=this.clone(),{label:t,meta:n}=e.spec,r={meta:n,label:t,type:e.type,oneOf:e._whitelist.describe(),notOneOf:e._blacklist.describe(),tests:e.tests.map(e=>({name:e.OPTIONS.name,params:e.OPTIONS.params})).filter((e,t,n)=>n.findIndex(t=>t.name===e.name)===t)};return r}}for(let p$ of(pH.prototype.__isYupSchema__=!0,["validate","validateSync"]))pH.prototype[`${p$}At`]=function(e,t,n={}){let{parent:r,parentPath:i,schema:a}=pY(this,e,t,n.context);return a[p$](r&&r[i],pU({},n,{parent:r,path:e}))};for(let pz of["equals","is"])pH.prototype[pz]=pH.prototype.oneOf;for(let pG of["not","nope"])pH.prototype[pG]=pH.prototype.notOneOf;pH.prototype.optional=pH.prototype.notRequired;let pW=pH;function pK(){return new pW}pK.prototype=pW.prototype;let pV=e=>null==e;function pq(){return new pZ}class pZ extends pH{constructor(){super({type:"boolean"}),this.withMutation(()=>{this.transform(function(e){if(!this.isType(e)){if(/^(true|1)$/i.test(String(e)))return!0;if(/^(false|0)$/i.test(String(e)))return!1}return e})})}_typeCheck(e){return e instanceof Boolean&&(e=e.valueOf()),"boolean"==typeof e}isTrue(e=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"true"},test:e=>pV(e)||!0===e})}isFalse(e=pm.isValue){return this.test({message:e,name:"is-value",exclusive:!0,params:{value:"false"},test:e=>pV(e)||!1===e})}}pq.prototype=pZ.prototype;let pX=/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,pJ=/^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,pQ=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,p1=e=>pV(e)||e===e.trim(),p0=({}).toString();function p2(){return new p3}class p3 extends pH{constructor(){super({type:"string"}),this.withMutation(()=>{this.transform(function(e){if(this.isType(e)||Array.isArray(e))return e;let t=null!=e&&e.toString?e.toString():e;return t===p0?e:t})})}_typeCheck(e){return e instanceof String&&(e=e.valueOf()),"string"==typeof e}_isPresent(e){return super._isPresent(e)&&!!e.length}length(e,t=ph.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t=ph.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t=ph.max){return this.test({name:"max",exclusive:!0,message:t,params:{max:e},test(t){return pV(t)||t.length<=this.resolve(e)}})}matches(e,t){let n=!1,r,i;return t&&("object"==typeof t?{excludeEmptyString:n=!1,message:r,name:i}=t:r=t),this.test({name:i||"matches",message:r||ph.matches,params:{regex:e},test:t=>pV(t)||""===t&&n||-1!==t.search(e)})}email(e=ph.email){return this.matches(pX,{name:"email",message:e,excludeEmptyString:!0})}url(e=ph.url){return this.matches(pJ,{name:"url",message:e,excludeEmptyString:!0})}uuid(e=ph.uuid){return this.matches(pQ,{name:"uuid",message:e,excludeEmptyString:!1})}ensure(){return this.default("").transform(e=>null===e?"":e)}trim(e=ph.trim){return this.transform(e=>null!=e?e.trim():e).test({message:e,name:"trim",test:p1})}lowercase(e=ph.lowercase){return this.transform(e=>pV(e)?e:e.toLowerCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toLowerCase()})}uppercase(e=ph.uppercase){return this.transform(e=>pV(e)?e:e.toUpperCase()).test({message:e,name:"string_case",exclusive:!0,test:e=>pV(e)||e===e.toUpperCase()})}}p2.prototype=p3.prototype;let p4=e=>e!=+e;function p5(){return new p6}class p6 extends pH{constructor(){super({type:"number"}),this.withMutation(()=>{this.transform(function(e){let t=e;if("string"==typeof t){if(""===(t=t.replace(/\s/g,"")))return NaN;t=+t}return this.isType(t)?t:parseFloat(t)})})}_typeCheck(e){return e instanceof Number&&(e=e.valueOf()),"number"==typeof e&&!p4(e)}min(e,t=pp.min){return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t>=this.resolve(e)}})}max(e,t=pp.max){return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(t)||t<=this.resolve(e)}})}lessThan(e,t=pp.lessThan){return this.test({message:t,name:"max",exclusive:!0,params:{less:e},test(t){return pV(t)||tthis.resolve(e)}})}positive(e=pp.positive){return this.moreThan(0,e)}negative(e=pp.negative){return this.lessThan(0,e)}integer(e=pp.integer){return this.test({name:"integer",message:e,test:e=>pV(e)||Number.isInteger(e)})}truncate(){return this.transform(e=>pV(e)?e:0|e)}round(e){var t,n=["ceil","floor","round","trunc"];if("trunc"===(e=(null==(t=e)?void 0:t.toLowerCase())||"round"))return this.truncate();if(-1===n.indexOf(e.toLowerCase()))throw TypeError("Only valid options for round() are: "+n.join(", "));return this.transform(t=>pV(t)?t:Math[e](t))}}p5.prototype=p6.prototype;var p9=/^(\d{4}|[+\-]\d{6})(?:-?(\d{2})(?:-?(\d{2}))?)?(?:[ T]?(\d{2}):?(\d{2})(?::?(\d{2})(?:[,\.](\d{1,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?)?)?$/;function p8(e){var t,n,r=[1,4,5,6,7,10,11],i=0;if(n=p9.exec(e)){for(var a,o=0;a=r[o];++o)n[a]=+n[a]||0;n[2]=(+n[2]||1)-1,n[3]=+n[3]||1,n[7]=n[7]?String(n[7]).substr(0,3):0,(void 0===n[8]||""===n[8])&&(void 0===n[9]||""===n[9])?t=+new Date(n[1],n[2],n[3],n[4],n[5],n[6],n[7]):("Z"!==n[8]&&void 0!==n[9]&&(i=60*n[10]+n[11],"+"===n[9]&&(i=0-i)),t=Date.UTC(n[1],n[2],n[3],n[4],n[5]+i,n[6],n[7]))}else t=Date.parse?Date.parse(e):NaN;return t}let p7=new Date(""),be=e=>"[object Date]"===Object.prototype.toString.call(e);function bt(){return new bn}class bn extends pH{constructor(){super({type:"date"}),this.withMutation(()=>{this.transform(function(e){return this.isType(e)?e:(e=p8(e),isNaN(e)?p7:new Date(e))})})}_typeCheck(e){return be(e)&&!isNaN(e.getTime())}prepareParam(e,t){let n;if(pN.isRef(e))n=e;else{let r=this.cast(e);if(!this._typeCheck(r))throw TypeError(`\`${t}\` must be a Date or a value that can be \`cast()\` to a Date`);n=r}return n}min(e,t=pb.min){let n=this.prepareParam(e,"min");return this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(e){return pV(e)||e>=this.resolve(n)}})}max(e,t=pb.max){var n=this.prepareParam(e,"max");return this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(e){return pV(e)||e<=this.resolve(n)}})}}bn.INVALID_DATE=p7,bt.prototype=bn.prototype,bt.INVALID_DATE=p7;var br=n(11865),bi=n.n(br),ba=n(68929),bo=n.n(ba),bs=n(67523),bu=n.n(bs),bc=n(94633),bl=n.n(bc);function bf(e,t=[]){let n=[],r=[];function i(e,i){var a=(0,pI.split)(e)[0];~r.indexOf(a)||r.push(a),~t.indexOf(`${i}-${a}`)||n.push([i,a])}for(let a in e)if(pw()(e,a)){let o=e[a];~r.indexOf(a)||r.push(a),pN.isRef(o)&&o.isSibling?i(o.path,a):p_(o)&&"deps"in o&&o.deps.forEach(e=>i(e,a))}return bl().array(r,n).reverse()}function bd(e,t){let n=1/0;return e.some((e,r)=>{var i;if((null==(i=t.path)?void 0:i.indexOf(e))!==-1)return n=r,!0}),n}function bh(e){return(t,n)=>bd(e,t)-bd(e,n)}function bp(){return(bp=Object.assign||function(e){for(var t=1;t"[object Object]"===Object.prototype.toString.call(e);function bm(e,t){let n=Object.keys(e.fields);return Object.keys(t).filter(e=>-1===n.indexOf(e))}let bg=bh([]);class bv extends pH{constructor(e){super({type:"object"}),this.fields=Object.create(null),this._sortErrors=bg,this._nodes=[],this._excludedEdges=[],this.withMutation(()=>{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null}),e&&this.shape(e)})}_typeCheck(e){return bb(e)||"function"==typeof e}_cast(e,t={}){var n;let r=super._cast(e,t);if(void 0===r)return this.getDefault();if(!this._typeCheck(r))return r;let i=this.fields,a=null!=(n=t.stripUnknown)?n:this.spec.noUnknown,o=this._nodes.concat(Object.keys(r).filter(e=>-1===this._nodes.indexOf(e))),s={},u=bp({},t,{parent:s,__validating:t.__validating||!1}),c=!1;for(let l of o){let f=i[l],d=pw()(r,l);if(f){let h,p=r[l];u.path=(t.path?`${t.path}.`:"")+l;let b="spec"in(f=f.resolve({value:p,context:t.context,parent:s}))?f.spec:void 0,m=null==b?void 0:b.strict;if(null==b?void 0:b.strip){c=c||l in r;continue}void 0!==(h=t.__validating&&m?r[l]:f.cast(r[l],u))&&(s[l]=h)}else d&&!a&&(s[l]=r[l]);s[l]!==r[l]&&(c=!0)}return c?s:r}_validate(e,t={},n){let r=[],{sync:i,from:a=[],originalValue:o=e,abortEarly:s=this.spec.abortEarly,recursive:u=this.spec.recursive}=t;a=[{schema:this,value:o},...a],t.__validating=!0,t.originalValue=o,t.from=a,super._validate(e,t,(e,c)=>{if(e){if(!pM.isError(e)||s)return void n(e,c);r.push(e)}if(!u||!bb(c)){n(r[0]||null,c);return}o=o||c;let l=this._nodes.map(e=>(n,r)=>{let i=-1===e.indexOf(".")?(t.path?`${t.path}.`:"")+e:`${t.path||""}["${e}"]`,s=this.fields[e];if(s&&"validate"in s){s.validate(c[e],bp({},t,{path:i,from:a,strict:!0,parent:c,originalValue:o[e]}),r);return}r(null)});pA({sync:i,tests:l,value:c,errors:r,endEarly:s,sort:this._sortErrors,path:t.path},n)})}clone(e){let t=super.clone(e);return t.fields=bp({},this.fields),t._nodes=this._nodes,t._excludedEdges=this._excludedEdges,t._sortErrors=this._sortErrors,t}concat(e){let t=super.concat(e),n=t.fields;for(let[r,i]of Object.entries(this.fields)){let a=n[r];void 0===a?n[r]=i:a instanceof pH&&i instanceof pH&&(n[r]=i.concat(a))}return t.withMutation(()=>t.shape(n))}getDefaultFromShape(){let e={};return this._nodes.forEach(t=>{let n=this.fields[t];e[t]="default"in n?n.getDefault():void 0}),e}_getDefault(){return"default"in this.spec?super._getDefault():this._nodes.length?this.getDefaultFromShape():void 0}shape(e,t=[]){let n=this.clone(),r=Object.assign(n.fields,e);if(n.fields=r,n._sortErrors=bh(Object.keys(r)),t.length){Array.isArray(t[0])||(t=[t]);let i=t.map(([e,t])=>`${e}-${t}`);n._excludedEdges=n._excludedEdges.concat(i)}return n._nodes=bf(r,n._excludedEdges),n}pick(e){let t={};for(let n of e)this.fields[n]&&(t[n]=this.fields[n]);return this.clone().withMutation(e=>(e.fields={},e.shape(t)))}omit(e){let t=this.clone(),n=t.fields;for(let r of(t.fields={},e))delete n[r];return t.withMutation(()=>t.shape(n))}from(e,t,n){let r=(0,pI.getter)(e,!0);return this.transform(i=>{if(null==i)return i;let a=i;return pw()(i,e)&&(a=bp({},i),n||delete a[e],a[t]=r(i)),a})}noUnknown(e=!0,t=pg.noUnknown){"string"==typeof e&&(t=e,e=!0);let n=this.test({name:"noUnknown",exclusive:!0,message:t,test(t){if(null==t)return!0;let n=bm(this.schema,t);return!e||0===n.length||this.createError({params:{unknown:n.join(", ")}})}});return n.spec.noUnknown=e,n}unknown(e=!0,t=pg.noUnknown){return this.noUnknown(!e,t)}transformKeys(e){return this.transform(t=>t&&bu()(t,(t,n)=>e(n)))}camelCase(){return this.transformKeys(bo())}snakeCase(){return this.transformKeys(bi())}constantCase(){return this.transformKeys(e=>bi()(e).toUpperCase())}describe(){let e=super.describe();return e.fields=pC()(this.fields,e=>e.describe()),e}}function by(e){return new bv(e)}function bw(){return(bw=Object.assign||function(e){for(var t=1;t{this.transform(function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(t){e=null}return this.isType(e)?e:null})})}_typeCheck(e){return Array.isArray(e)}get _subType(){return this.innerType}_cast(e,t){let n=super._cast(e,t);if(!this._typeCheck(n)||!this.innerType)return n;let r=!1,i=n.map((e,n)=>{let i=this.innerType.cast(e,bw({},t,{path:`${t.path||""}[${n}]`}));return i!==e&&(r=!0),i});return r?i:n}_validate(e,t={},n){var r,i;let a=[],o=t.sync,s=t.path,u=this.innerType,c=null!=(r=t.abortEarly)?r:this.spec.abortEarly,l=null!=(i=t.recursive)?i:this.spec.recursive,f=null!=t.originalValue?t.originalValue:e;super._validate(e,t,(e,r)=>{if(e){if(!pM.isError(e)||c)return void n(e,r);a.push(e)}if(!l||!u||!this._typeCheck(r)){n(a[0]||null,r);return}f=f||r;let i=Array(r.length);for(let d=0;du.validate(h,b,t)}pA({sync:o,path:s,value:r,errors:a,endEarly:c,tests:i},n)})}clone(e){let t=super.clone(e);return t.innerType=this.innerType,t}concat(e){let t=super.concat(e);return t.innerType=this.innerType,e.innerType&&(t.innerType=t.innerType?t.innerType.concat(e.innerType):e.innerType),t}of(e){let t=this.clone();if(!p_(e))throw TypeError("`array.of()` sub-schema must be a valid yup schema not: "+pf(e));return t.innerType=e,t}length(e,t=pv.length){return this.test({message:t,name:"length",exclusive:!0,params:{length:e},test(t){return pV(t)||t.length===this.resolve(e)}})}min(e,t){return t=t||pv.min,this.test({message:t,name:"min",exclusive:!0,params:{min:e},test(t){return pV(t)||t.length>=this.resolve(e)}})}max(e,t){return t=t||pv.max,this.test({message:t,name:"max",exclusive:!0,params:{max:e},test(t){return pV(t)||t.length<=this.resolve(e)}})}ensure(){return this.default(()=>[]).transform((e,t)=>this._typeCheck(e)?e:null==t?[]:[].concat(t))}compact(e){let t=e?(t,n,r)=>!e(t,n,r):e=>!!e;return this.transform(e=>null!=e?e.filter(t):e)}describe(){let e=super.describe();return this.innerType&&(e.innerType=this.innerType.describe()),e}nullable(e=!0){return super.nullable(e)}defined(){return super.defined()}required(e){return super.required(e)}}b_.prototype=bE.prototype;var bS=by().shape({name:p2().required("Required"),url:p2().required("Required")}),bk=function(e){var t=e.initialValues,n=e.onSubmit,r=e.submitButtonText,i=e.nameDisabled,a=void 0!==i&&i;return l.createElement(hM,{initialValues:t,validationSchema:bS,onSubmit:n},function(e){var t=e.isSubmitting;return l.createElement(l.Fragment,null,l.createElement(hj,{"data-testid":"bridge-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hR,{component:hJ,id:"name",name:"name",label:"Name",disabled:a,required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(hR,{component:hJ,id:"url",name:"url",label:"Bridge URL",placeholder:"https://",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"url-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:7},l.createElement(hR,{component:hJ,id:"minimumContractPayment",name:"minimumContractPayment",label:"Minimum Contract Payment",placeholder:"0",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"minimumContractPayment-helper-text"}})),l.createElement(d.Z,{item:!0,xs:7},l.createElement(hR,{component:hJ,id:"confirmations",name:"confirmations",label:"Confirmations",placeholder:"0",type:"number",fullWidth:!0,inputProps:{min:0},FormHelperTextProps:{"data-testid":"confirmations-helper-text"}})))),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},r)))))})},bx=function(e){var t=e.bridge,n=e.onSubmit,r={name:t.name,url:t.url,minimumContractPayment:t.minimumContractPayment,confirmations:t.confirmations};return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:40},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Edit Bridge",action:l.createElement(aL.Z,{component:tz,href:"/bridges/".concat(t.id)},"Cancel")}),l.createElement(aK.Z,null,l.createElement(bk,{nameDisabled:!0,initialValues:r,onSubmit:n,submitButtonText:"Save Bridge"}))))))};function bT(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]&&arguments[0],t=e?function(){return l.createElement(x.default,{variant:"body1"},"Loading...")}:function(){return null};return{isLoading:e,LoadingPlaceholder:t}},ml=n(76023);function mf(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=0||(i[n]=e[n]);return i}function mB(e,t){if(null==e)return{};var n,r,i=mY(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function mU(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0}var mX={};function mJ(e){if(0===e.length||1===e.length)return e;var t=e.join(".");return mX[t]||(mX[t]=mZ(e)),mX[t]}function mQ(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return mJ(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return mV({},e,n[t])},t)}function m1(e){return e.join(" ")}function m0(e,t){var n=0;return function(r){return n+=1,r.map(function(r,i){return m2({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(i)})})}}function m2(e){var t=e.node,n=e.stylesheet,r=e.style,i=void 0===r?{}:r,a=e.useInlineStyles,o=e.key,s=t.properties,u=t.type,c=t.tagName,f=t.value;if("text"===u)return f;if(c){var d,h=m0(n,a);if(a){var p=Object.keys(n).reduce(function(e,t){return t.split(".").forEach(function(t){e.includes(t)||e.push(t)}),e},[]),b=s.className&&s.className.includes("token")?["token"]:[],m=s.className&&b.concat(s.className.filter(function(e){return!p.includes(e)}));d=mV({},s,{className:m1(m)||void 0,style:mQ(s.className,Object.assign({},s.style,i),n)})}else d=mV({},s,{className:m1(s.className)});var g=h(t.children);return l.createElement(c,mq({key:o},d),g)}}let m3=function(e,t){return -1!==e.listLanguages().indexOf(t)};var m4=/\n/g;function m5(e){return e.match(m4)}function m6(e){var t=e.lines,n=e.startingLineNumber,r=e.style;return t.map(function(e,t){var i=t+n;return l.createElement("span",{key:"line-".concat(t),className:"react-syntax-highlighter-line-number",style:"function"==typeof r?r(i):r},"".concat(i,"\n"))})}function m9(e){var t=e.codeString,n=e.codeStyle,r=e.containerStyle,i=void 0===r?{float:"left",paddingRight:"10px"}:r,a=e.numberStyle,o=void 0===a?{}:a,s=e.startingLineNumber;return l.createElement("code",{style:Object.assign({},n,i)},m6({lines:t.replace(/\n$/,"").split("\n"),style:o,startingLineNumber:s}))}function m8(e){return"".concat(e.toString().length,".25em")}function m7(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function ge(e,t,n){var r,i={display:"inline-block",minWidth:m8(n),paddingRight:"1em",textAlign:"right",userSelect:"none"};return mV({},i,"function"==typeof e?e(t):e)}function gt(e){var t=e.children,n=e.lineNumber,r=e.lineNumberStyle,i=e.largestLineNumber,a=e.showInlineLineNumbers,o=e.lineProps,s=void 0===o?{}:o,u=e.className,c=void 0===u?[]:u,l=e.showLineNumbers,f=e.wrapLongLines,d="function"==typeof s?s(n):s;if(d.className=c,n&&a){var h=ge(r,n,i);t.unshift(m7(n,h))}return f&l&&(d.style=mV({},d.style,{display:"flex"})),{type:"element",tagName:"span",properties:d,children:t}}function gn(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return gt({children:e,lineNumber:t,lineNumberStyle:s,largestLineNumber:o,showInlineLineNumbers:i,lineProps:n,className:a,showLineNumbers:r,wrapLongLines:u})}function b(e,t){if(r&&t&&i){var n=ge(s,t,o);e.unshift(m7(t,n))}return e}function m(e,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return t||r.length>0?p(e,n,r):b(e,n)}for(var g=function(){var e=l[h],t=e.children[0].value;if(m5(t)){var n=t.split("\n");n.forEach(function(t,i){var o=r&&f.length+a,s={type:"text",value:"".concat(t,"\n")};if(0===i){var u=l.slice(d+1,h).concat(gt({children:[s],className:e.properties.className})),c=m(u,o);f.push(c)}else if(i===n.length-1){if(l[h+1]&&l[h+1].children&&l[h+1].children[0]){var p={type:"text",value:"".concat(t)},b=gt({children:[p],className:e.properties.className});l.splice(h+1,0,b)}else{var g=[s],v=m(g,o,e.properties.className);f.push(v)}}else{var y=[s],w=m(y,o,e.properties.className);f.push(w)}}),d=h}h++};h code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}};var gc=n(98695),gl=n.n(gc);let gf=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apl","applescript","aql","arduino","arff","asciidoc","asm6502","aspnet","autohotkey","autoit","bash","basic","batch","bbcode","birb","bison","bnf","brainfuck","brightscript","bro","bsl","c","cil","clike","clojure","cmake","coffeescript","concurnas","cpp","crystal","csharp","csp","css-extras","css","cypher","d","dart","dax","dhall","diff","django","dns-zone-file","docker","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","firestore-security-rules","flow","fortran","fsharp","ftl","gcode","gdscript","gedcom","gherkin","git","glsl","gml","go","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hpkp","hsts","http","ichigojam","icon","iecst","ignore","inform7","ini","io","j","java","javadoc","javadoclike","javascript","javastacktrace","jolie","jq","js-extras","js-templates","jsdoc","json","json5","jsonp","jsstacktrace","jsx","julia","keyman","kotlin","latex","latte","less","lilypond","liquid","lisp","livescript","llvm","lolcode","lua","makefile","markdown","markup-templating","markup","matlab","mel","mizar","mongodb","monkey","moonscript","n1ql","n4js","nand2tetris-hdl","naniscript","nasm","neon","nginx","nim","nix","nsis","objectivec","ocaml","opencl","oz","parigp","parser","pascal","pascaligo","pcaxis","peoplecode","perl","php-extras","php","phpdoc","plsql","powerquery","powershell","processing","prolog","properties","protobuf","pug","puppet","pure","purebasic","purescript","python","q","qml","qore","r","racket","reason","regex","renpy","rest","rip","roboconf","robotframework","ruby","rust","sas","sass","scala","scheme","scss","shell-session","smali","smalltalk","smarty","sml","solidity","solution-file","soy","sparql","splunk-spl","sqf","sql","stan","stylus","swift","t4-cs","t4-templating","t4-vb","tap","tcl","textile","toml","tsx","tt2","turtle","twig","typescript","typoscript","unrealscript","vala","vbnet","velocity","verilog","vhdl","vim","visual-basic","warpscript","wasm","wiki","xeora","xml-doc","xojo","xquery","yaml","yang","zig"];var gd=gs(gl(),gu);gd.supportedLanguages=gf;let gh=gd;var gp=n(64566),gb=n(68239);function gm(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function gg(){var e=gm(["\n query FetchConfigV2 {\n configv2 {\n user\n effective\n }\n }\n"]);return gg=function(){return e},e}var gv=function(){var e="[[TelemetryIngress.Endpoints]] \nNetwork = '...' # e.g. EVM. Solana, Starknet, Cosmos \nChainID = '...' # e.g. 1, 5, devnet, mainnet-beta URL\nURL = '...'\nServerPubKey = '...'";return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Deprecation warning"}),l.createElement(aK.Z,null,l.createElement(x.default,{variant:"h5",gutterBottom:!0},"Starting in ",l.createElement("code",null,"v2.9.0"),":"),l.createElement(w.default,{dense:!0},l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"TelemetryIngress.URL")," and"," ",l.createElement("code",null,"TelemetryIngress.ServerPubKey")," will no longer be allowed. Please switch to ",l.createElement("code",null,"TelemetryIngress.Endpoints"),":",l.createElement(gh,{language:"toml",style:gu},e))),l.createElement(_.default,null,l.createElement(ol.Z,null,l.createElement(gb.Z,null)),l.createElement(x.default,{variant:"subtitle2",gutterBottom:!0},l.createElement("code",null,"P2P.V1")," will no longer be supported and must not be set in TOML configuration in order to boot. Use"," ",l.createElement("code",null,"P2P.V2")," instead. If you are using both,"," ",l.createElement("code",null,"V1")," can simply be removed.")))))},gy=n0(gg()),gw=function(e){var t=e.children;return l.createElement(ii.Z,null,l.createElement(ie.default,{component:"th",scope:"row",colSpan:3},t))},g_=function(){return l.createElement(gw,null,"...")},gE=function(e){var t=e.children;return l.createElement(gw,null,t)},gS=function(e){var t=e.loading,n=e.toml,r=e.error,i=void 0===r?"":r,a=e.title,o=e.expanded;if(i)return l.createElement(gE,null,i);if(t)return l.createElement(g_,null);a||(a="TOML");var s={display:"block"};return l.createElement(x.default,null,l.createElement(mR.Z,{defaultExpanded:o},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},a),l.createElement(mF.Z,{style:s},l.createElement(gh,{language:"toml",style:gu},n))))},gk=function(){var e=ry(gy,{fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return(null==t?void 0:t.configv2.effective)=="N/A"?l.createElement(l.Fragment,null,l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{title:"V2 config dump:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0})))):l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gv,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"TOML Configuration"}),l.createElement(gS,{title:"User specified:",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.user,showHead:!0,expanded:!0}),l.createElement(gS,{title:"Effective (with defaults):",error:null==r?void 0:r.message,loading:n,toml:null==t?void 0:t.configv2.effective,showHead:!0})))))},gx=n(34823),gT=function(e){return(0,b.createStyles)({cell:{paddingTop:1.5*e.spacing.unit,paddingBottom:1.5*e.spacing.unit}})},gM=(0,b.withStyles)(gT)(function(e){var t=e.classes,n=(0,A.I0)();(0,l.useEffect)(function(){n((0,ty.DQ)())});var r=(0,A.v9)(gx.N,A.wU);return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Node"}),l.createElement(r8.Z,null,l.createElement(r7.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"Version"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.version))),l.createElement(ii.Z,null,l.createElement(ie.default,{className:t.cell},l.createElement(x.default,null,"SHA"),l.createElement(x.default,{variant:"subtitle1",color:"textSecondary"},r.commitSHA))))))}),gO=function(){return l.createElement(iv,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,sm:12,md:8},l.createElement(d.Z,{container:!0},l.createElement(gk,null))),l.createElement(d.Z,{item:!0,sm:12,md:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gM,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mP,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(mS,null))))))},gA=function(){return l.createElement(gO,null)},gL=function(){return l.createElement(gA,null)},gC=n(44431),gI=1e18,gD=function(e){return new gC.BigNumber(e).dividedBy(gI).toFixed(8)},gN=function(e){var t=e.keys,n=e.chainID,r=e.hideHeaderTitle;return l.createElement(l.Fragment,null,l.createElement(sf.Z,{title:!r&&"Account Balances",subheader:"Chain ID "+n}),l.createElement(aK.Z,null,l.createElement(w.default,{dense:!1,disablePadding:!0},t&&t.map(function(e,r){return l.createElement(l.Fragment,null,l.createElement(_.default,{disableGutters:!0,key:["acc-balance",n.toString(),r.toString()].join("-")},l.createElement(E.Z,{primary:l.createElement(l.Fragment,null,l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ob,{title:"Address"}),l.createElement(om,{value:e.address})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"Native Token Balance"}),l.createElement(om,{value:e.ethBalance||"--"})),l.createElement(d.Z,{item:!0,xs:6},l.createElement(ob,{title:"LINK Balance"}),l.createElement(om,{value:e.linkBalance?gD(e.linkBalance):"--"}))))})),r+1s&&l.createElement(g$.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,{className:r.footer},l.createElement(aL.Z,{href:"/runs",component:tz},"View More"))))))});function vi(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function va(){var e=vi(["\n ","\n query FetchRecentJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...RecentJobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return va=function(){return e},e}var vo=5,vs=n0(va(),vt),vu=function(){var e=ry(vs,{variables:{offset:0,limit:vo},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vr,{data:t,errorMsg:null==r?void 0:r.message,loading:n,maxRunsSize:vo})},vc=function(e){return(0,b.createStyles)({style:{textAlign:"center",padding:2.5*e.spacing.unit,position:"fixed",left:"0",bottom:"0",width:"100%",borderRadius:0},bareAnchor:{color:e.palette.common.black,textDecoration:"none"}})},vl=(0,b.withStyles)(vc)(function(e){var t=e.classes,n=(0,A.v9)(gx.N,A.wU),r=(0,A.I0)();return(0,l.useEffect)(function(){r((0,ty.DQ)())}),l.createElement(ia.default,{className:t.style},l.createElement(x.default,null,"Chainlink Node ",n.version," at commit"," ",l.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://github.com/smartcontractkit/chainlink/commit/".concat(n.commitSHA),className:t.bareAnchor},n.commitSHA)))}),vf=function(e){return(0,b.createStyles)({cell:{borderColor:e.palette.divider,borderTop:"1px solid",borderBottom:"none",paddingTop:2*e.spacing.unit,paddingBottom:2*e.spacing.unit,paddingLeft:2*e.spacing.unit},block:{display:"block"},overflowEllipsis:{textOverflow:"ellipsis",overflow:"hidden"}})},vd=(0,b.withStyles)(vf)(function(e){var t=e.classes,n=e.job;return l.createElement(ii.Z,null,l.createElement(ie.default,{scope:"row",className:t.cell},l.createElement(d.Z,{container:!0,spacing:0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ip,{href:"/jobs/".concat(n.id),classes:{linkContent:t.block}},l.createElement(x.default,{className:t.overflowEllipsis,variant:"body1",component:"span",color:"primary"},n.name||n.id))),l.createElement(d.Z,{item:!0,xs:12},l.createElement(x.default,{variant:"body1",color:"textSecondary"},"Created ",l.createElement(aA,{tooltip:!0},n.createdAt))))))});function vh(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vp(){var e=vh(["\n fragment RecentJobsPayload_ResultsFields on Job {\n id\n name\n createdAt\n }\n"]);return vp=function(){return e},e}var vb=n0(vp()),vm=function(){return(0,b.createStyles)({cardHeader:{borderBottom:0},table:{tableLayout:"fixed"}})},vg=(0,b.withStyles)(vm)(function(e){var t,n,r=e.classes,i=e.data,a=e.errorMsg,o=e.loading;return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Recent Jobs",className:r.cardHeader}),l.createElement(r8.Z,{className:r.table},l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(t=null==i?void 0:i.jobs.results)||void 0===t?void 0:t.length)===0},"No recently created jobs"),l.createElement(gz,{msg:a}),null===(n=null==i?void 0:i.jobs.results)||void 0===n?void 0:n.map(function(e,t){return l.createElement(vd,{job:e,key:t})}))))});function vv(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function vy(){var e=vv(["\n ","\n query FetchRecentJobs($offset: Int, $limit: Int) {\n jobs(offset: $offset, limit: $limit) {\n results {\n ...RecentJobsPayload_ResultsFields\n }\n }\n }\n"]);return vy=function(){return e},e}var vw=5,v_=n0(vy(),vb),vE=function(){var e=ry(v_,{variables:{offset:0,limit:vw},fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error;return l.createElement(vg,{data:t,errorMsg:null==r?void 0:r.message,loading:n})},vS=function(){return l.createElement(iv,null,l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:8},l.createElement(vu,null)),l.createElement(d.Z,{item:!0,xs:4},l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12},l.createElement(gH,null)),l.createElement(d.Z,{item:!0,xs:12},l.createElement(vE,null))))),l.createElement(vl,null))},vk=function(){return l.createElement(vS,null)},vx=function(){return l.createElement(vk,null)},vT=n(87239),vM=function(e){switch(e){case"DirectRequestSpec":return"Direct Request";case"FluxMonitorSpec":return"Flux Monitor";default:return e.replace(/Spec$/,"")}},vO=n(5022),vA=n(78718),vL=n.n(vA);function vC(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1?t-1:0),r=1;r1?t-1:0),r=1;re.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&n.map(function(e){return l.createElement(ii.Z,{key:e.id,style:{cursor:"pointer"},onClick:function(){return r.push("/runs/".concat(e.id))}},l.createElement(ie.default,{className:t.idCell,scope:"row"},l.createElement("div",{className:t.runDetails},l.createElement(x.default,{variant:"h5",color:"primary",component:"span"},e.id))),l.createElement(ie.default,{className:t.stampCell},l.createElement(x.default,{variant:"body1",color:"textSecondary",className:t.stamp},"Created ",l.createElement(aA,{tooltip:!0},e.createdAt))),l.createElement(ie.default,{className:t.statusCell,scope:"row"},l.createElement(x.default,{variant:"body1",className:O()(t.status,ym(t,e.status))},e.status.toLowerCase())))})))}),yv=n(16839),yy=n.n(yv);function yw(e){var t=e.replace(/\w+\s*=\s*<([^>]|[\r\n])*>/g,""),n=yy().read(t),r=n.edges();return n.nodes().map(function(e){var t={id:e,parentIds:r.filter(function(t){return t.w===e}).map(function(e){return e.v})};return Object.keys(n.node(e)).length>0&&(t.attributes=n.node(e)),t})}var y_=n(94164),yE=function(e){var t=e.data,n=[];return(null==t?void 0:t.attributes)&&Object.keys(t.attributes).forEach(function(e){var r;n.push(l.createElement("div",{key:e},l.createElement(x.default,{variant:"body1",color:"textSecondary",component:"div"},l.createElement("b",null,e,":")," ",null===(r=t.attributes)||void 0===r?void 0:r[e])))}),l.createElement("div",null,t&&l.createElement(x.default,{variant:"body1",color:"textPrimary"},l.createElement("b",null,t.id)),n)},yS=n(73343),yk=n(3379),yx=n.n(yk);function yT(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nwindow.innerWidth?u-r.getBoundingClientRect().width-a:u+a,n=c+r.getBoundingClientRect().height+i>window.innerHeight?c-r.getBoundingClientRect().height-a:c+a,r.style.opacity=String(1),r.style.top="".concat(n,"px"),r.style.left="".concat(t,"px"),r.style.zIndex=String(1)}},h=function(e){var t=document.getElementById("tooltip-d3-chart-".concat(e));t&&(t.style.opacity=String(0),t.style.zIndex=String(-1))};return l.createElement("div",{style:{fontFamily:"sans-serif",fontWeight:"normal"}},l.createElement(y_.kJ,{id:"task-list-graph-d3",data:i,config:s,onMouseOverNode:d,onMouseOutNode:h},"D3 chart"),n.map(function(e){return l.createElement("div",{key:"d3-tooltip-key-".concat(e.id),id:"tooltip-d3-chart-".concat(e.id),style:{position:"absolute",opacity:"0",border:"1px solid rgba(0, 0, 0, 0.1)",padding:yS.r.spacing.unit,background:"white",borderRadius:5,zIndex:-1,inlineSize:"min-content"}},l.createElement(yE,{data:e}))}))};function yD(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);nyH&&l.createElement("div",{className:t.runDetails},l.createElement(aL.Z,{href:"/jobs/".concat(n.id,"/runs"),component:tz},"View more")))),l.createElement(d.Z,{item:!0,xs:12,sm:6},l.createElement(yU,{observationSource:n.observationSource})))});function yG(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:"";try{return vO.parse(e),!0}catch(t){return!1}})}),wq=function(e){var t=e.initialValues,n=e.onSubmit,r=e.onTOMLChange;return l.createElement(hM,{initialValues:t,validationSchema:wV,onSubmit:n},function(e){var t=e.isSubmitting,n=e.values;return r&&r(n.toml),l.createElement(hj,{"data-testid":"job-form",noValidate:!0},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12},l.createElement(hR,{component:hJ,id:"toml",name:"toml",label:"Job Spec (TOML)",required:!0,fullWidth:!0,multiline:!0,rows:10,rowsMax:25,variant:"outlined",autoComplete:"off",FormHelperTextProps:{"data-testid":"toml-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:7},l.createElement(ox.default,{variant:"contained",color:"primary",type:"submit",disabled:t,size:"large"},"Create Job"))))})},wZ=n(50109),wX="persistSpec";function wJ(e){var t=e.query,n=new URLSearchParams(t).get("definition");return n?(wZ.t8(wX,n),{toml:n}):{toml:wZ.U2(wX)||""}}var wQ=function(e){var t=e.onSubmit,n=e.onTOMLChange,r=wJ({query:(0,h.TH)().search}),i=function(e){var t=e.replace(/[\u200B-\u200D\uFEFF]/g,"");wZ.t8("".concat(wX),t),n&&n(t)};return l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"New Job"}),l.createElement(aK.Z,null,l.createElement(wq,{initialValues:r,onSubmit:t,onTOMLChange:i})))};function w1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=t.start,r=void 0===n?6:n,i=t.end,a=void 0===i?4:i;return e.substring(0,r)+"..."+e.substring(e.length-a)}function _L(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return ry(_q,e)},_X=function(){var e=_Z({fetchPolicy:"cache-and-network"}),t=e.data,n=e.loading,r=e.error,i=e.refetch;return l.createElement(_z,{loading:n,data:t,errorMsg:null==r?void 0:r.message,refetch:i})},_J=function(e){var t=e.csaKey;return l.createElement(ii.Z,{hover:!0},l.createElement(ie.default,null,l.createElement(x.default,{variant:"body1"},t.publicKey," ",l.createElement(_O,{data:t.publicKey}))))};function _Q(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function _1(){var e=_Q(["\n fragment CSAKeysPayload_ResultsFields on CSAKey {\n id\n publicKey\n }\n"]);return _1=function(){return e},e}var _0=n0(_1()),_2=function(e){var t,n,r,i=e.data,a=e.errorMsg,o=e.loading,s=e.onCreate;return l.createElement(r9.Z,null,l.createElement(sf.Z,{action:(null===(t=null==i?void 0:i.csaKeys.results)||void 0===t?void 0:t.length)===0&&l.createElement(ox.default,{variant:"outlined",color:"primary",onClick:s},"New CSA Key"),title:"CSA Key",subheader:"Manage your CSA Key"}),l.createElement(r8.Z,null,l.createElement(it.Z,null,l.createElement(ii.Z,null,l.createElement(ie.default,null,"Public Key"))),l.createElement(r7.Z,null,l.createElement(gW,{visible:o}),l.createElement(gK,{visible:(null===(n=null==i?void 0:i.csaKeys.results)||void 0===n?void 0:n.length)===0}),l.createElement(gz,{msg:a}),null===(r=null==i?void 0:i.csaKeys.results)||void 0===r?void 0:r.map(function(e,t){return l.createElement(_J,{csaKey:e,key:t})}))))};function _3(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return ry(EL,e)};function EI(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return ry(E0,e)},E6=function(){return os(E2)},E9=function(){return os(E3)},E8=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return ry(E4,e)};function E7(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return ry(SZ,e)};function SJ(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);n=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function kX(e,t){if(null==e)return{};var n,r,i={},a=Object.keys(e);for(r=0;r=0||(i[n]=e[n]);return i}var kJ=function(e){var t=e.run,n=l.useMemo(function(){var e=t.inputs,n=t.outputs,r=t.taskRuns,i=kZ(t,["inputs","outputs","taskRuns"]),a={};try{a=JSON.parse(e)}catch(o){a={}}return kq(kK({},i),{inputs:a,outputs:n,taskRuns:r})},[t]);return l.createElement(r9.Z,null,l.createElement(aK.Z,null,l.createElement(kG,{object:n})))};function kQ(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function k1(e){for(var t=1;t0&&l.createElement(ko,{errors:t.allErrors})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(h.rs,null,l.createElement(h.AW,{path:"".concat(n,"/json")},l.createElement(kJ,{run:t})),l.createElement(h.AW,{path:n},t.taskRuns.length>0&&l.createElement(kj,{taskRuns:t.taskRuns,observationSource:t.job.observationSource}))))))))};function k7(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xe(){var e=k7(["\n ","\n query FetchJobRun($id: ID!) {\n jobRun(id: $id) {\n __typename\n ... on JobRun {\n ...JobRunPayload_Fields\n }\n ... on NotFoundError {\n message\n }\n }\n }\n"]);return xe=function(){return e},e}var xt=n0(xe(),k9),xn=function(){var e=ry(xt,{variables:{id:(0,h.UO)().id}}),t=e.data,n=e.loading,r=e.error;if(n)return l.createElement(ij,null);if(r)return l.createElement(iN,{error:r});var i=null==t?void 0:t.jobRun;switch(null==i?void 0:i.__typename){case"JobRun":return l.createElement(k8,{run:i});case"NotFoundError":return l.createElement(oo,null);default:return null}};function xr(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xi(){var e=xr(["\n fragment JobRunsPayload_ResultsFields on JobRun {\n id\n allErrors\n createdAt\n finishedAt\n status\n job {\n id\n }\n }\n"]);return xi=function(){return e},e}var xa=n0(xi()),xo=function(e){var t=e.loading,n=e.data,r=e.page,i=e.pageSize,a=(0,h.k6)(),o=l.useMemo(function(){return null==n?void 0:n.jobRuns.results.map(function(e){var t,n=e.allErrors,r=e.id,i=e.createdAt;return{id:r,createdAt:i,errors:n,finishedAt:e.finishedAt,status:e.status}})},[n]);return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(iw,null,"Job Runs")),t&&l.createElement(ij,null),n&&o&&l.createElement(d.Z,{item:!0,xs:12},l.createElement(r9.Z,null,l.createElement(yg,{runs:o}),l.createElement(ir.Z,{component:"div",count:n.jobRuns.metadata.total,rowsPerPage:i,rowsPerPageOptions:[i],page:r-1,onChangePage:function(e,t){a.push("/runs?page=".concat(t+1,"&per=").concat(i))},onChangeRowsPerPage:function(){},backIconButtonProps:{"aria-label":"prev-page"},nextIconButtonProps:{"aria-label":"next-page"}})))))};function xs(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xu(){var e=xs(["\n ","\n query FetchJobRuns($offset: Int, $limit: Int) {\n jobRuns(offset: $offset, limit: $limit) {\n results {\n ...JobRunsPayload_ResultsFields\n }\n metadata {\n total\n }\n }\n }\n"]);return xu=function(){return e},e}var xc=n0(xu(),xa),xl=function(){var e=iF(),t=parseInt(e.get("page")||"1",10),n=parseInt(e.get("per")||"25",10),r=ry(xc,{variables:{offset:(t-1)*n,limit:n},fetchPolicy:"cache-and-network"}),i=r.data,a=r.loading,o=r.error;return o?l.createElement(iN,{error:o}):l.createElement(xo,{loading:a,data:i,page:t,pageSize:n})},xf=function(){var e=(0,h.$B)().path;return l.createElement(h.rs,null,l.createElement(h.AW,{exact:!0,path:e},l.createElement(xl,null)),l.createElement(h.AW,{path:"".concat(e,"/:id")},l.createElement(xn,null)))},xd=by().shape({name:p2().required("Required"),uri:p2().required("Required"),publicKey:p2().required("Required")}),xh=function(e){var t=e.initialValues,n=e.onSubmit;return l.createElement(hM,{initialValues:t,validationSchema:xd,onSubmit:n},function(e){var t=e.isSubmitting,n=e.submitForm;return l.createElement(hj,{"data-testid":"feeds-manager-form"},l.createElement(d.Z,{container:!0,spacing:16},l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"name",name:"name",label:"Name",required:!0,fullWidth:!0,FormHelperTextProps:{"data-testid":"name-helper-text"}})),l.createElement(d.Z,{item:!0,xs:!1,md:6}),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"uri",name:"uri",label:"URI",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"uri-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12,md:6},l.createElement(hR,{component:hJ,id:"publicKey",name:"publicKey",label:"Public Key",required:!0,fullWidth:!0,helperText:"Provided by the Feeds Manager operator",FormHelperTextProps:{"data-testid":"publicKey-helper-text"}})),l.createElement(d.Z,{item:!0,xs:12},l.createElement(ox.default,{variant:"contained",color:"primary",disabled:t,onClick:n},"Submit"))))})},xp=function(e){var t=e.data,n=e.onSubmit,r={name:t.name,uri:t.uri,publicKey:t.publicKey};return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Edit Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:r,onSubmit:n})))))};function xb(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function xm(){var e=xb(["\n query FetchFeedsManagers {\n feedsManagers {\n results {\n __typename\n id\n name\n uri\n publicKey\n isConnectionActive\n createdAt\n }\n }\n }\n"]);return xm=function(){return e},e}var xg=n0(xm()),xv=function(){return ry(xg)};function xy(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&void 0!==arguments[0]?arguments[0]:{};return ry(xJ,e)};function x1(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);n0?n.feedsManagers.results[0]:void 0;return n&&a?l.createElement(Tz,{manager:a}):l.createElement(h.l_,{to:{pathname:"/feeds_manager/new",state:{from:e}}})},TW={name:"Chainlink Feeds Manager",uri:"",publicKey:""},TK=function(e){var t=e.onSubmit;return l.createElement(d.Z,{container:!0},l.createElement(d.Z,{item:!0,xs:12,md:11,lg:9},l.createElement(r9.Z,null,l.createElement(sf.Z,{title:"Register Feeds Manager"}),l.createElement(aK.Z,null,l.createElement(xh,{initialValues:TW,onSubmit:t})))))};function TV(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);nt.version?e:t})},[o]),g=l.useMemo(function(){return Mm(o).sort(function(e,t){return t.version-e.version})},[o]),v=function(e,t,n){switch(e){case"PENDING":return l.createElement(l.Fragment,null,l.createElement(ox.default,{variant:"text",color:"secondary",onClick:function(){return b("reject",t)}},"Reject"),m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status&&l.createElement(ox.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve"),m.id===t&&"DELETED"===n.status&&n.pendingUpdate&&l.createElement(l.Fragment,null,l.createElement(ox.default,{variant:"contained",color:"primary",onClick:function(){return b("cancel",t)}},"Cancel"),l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs")));case"APPROVED":return l.createElement(l.Fragment,null,l.createElement(ox.default,{variant:"contained",onClick:function(){return b("cancel",t)}},"Cancel"),"DELETED"===n.status&&n.pendingUpdate&&l.createElement(x.default,{color:"error"},"This proposal was deleted. Cancel the spec to delete any running jobs"));case"CANCELLED":if(m.id===t&&"DELETED"!==n.status&&"REVOKED"!==n.status)return l.createElement(ox.default,{variant:"contained",color:"primary",onClick:function(){return b("approve",t)}},"Approve");return null;default:return null}};return l.createElement("div",null,g.map(function(e,n){return l.createElement(mR.Z,{defaultExpanded:0===n,key:n},l.createElement(mj.Z,{expandIcon:l.createElement(gp.Z,null)},l.createElement(x.default,{className:t.versionText},"Version ",e.version),l.createElement(El.Z,{label:e.status,color:"APPROVED"===e.status?"primary":"default",variant:"REJECTED"===e.status||"CANCELLED"===e.status?"outlined":"default"}),l.createElement("div",{className:t.proposedAtContainer},l.createElement(x.default,null,"Proposed ",l.createElement(aA,{tooltip:!0},e.createdAt)))),l.createElement(mF.Z,{className:t.expansionPanelDetails},l.createElement("div",{className:t.actions},l.createElement("div",{className:t.editContainer},0===n&&("PENDING"===e.status||"CANCELLED"===e.status)&&"DELETED"!==s.status&&"REVOKED"!==s.status&&l.createElement(ox.default,{variant:"contained",onClick:function(){return p(!0)}},"Edit")),l.createElement("div",{className:t.actionsContainer},v(e.status,e.id,s))),l.createElement(gh,{language:"toml",style:gu,"data-testid":"codeblock"},e.definition)))}),l.createElement(oI,{open:null!=c,title:c?M_[c.action].title:"",body:c?M_[c.action].body:"",onConfirm:function(){if(c){switch(c.action){case"approve":n(c.id);break;case"cancel":r(c.id);break;case"reject":i(c.id)}f(null)}},cancelButtonText:"Cancel",onCancel:function(){return f(null)}}),l.createElement(Mo,{open:h,onClose:function(){return p(!1)},initialValues:{definition:m.definition,id:m.id},onSubmit:a}))});function MS(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Mk(){var e=MS(["\n ","\n fragment JobProposalPayloadFields on JobProposal {\n id\n externalJobID\n remoteUUID\n jobID\n specs {\n ...JobProposal_SpecsFields\n }\n status\n pendingUpdate\n }\n"]);return Mk=function(){return e},e}var Mx=n0(Mk(),My),MT=function(e){var t=e.onApprove,n=e.onCancel,r=e.onReject,i=e.onUpdateSpec,a=e.proposal;return l.createElement(iv,null,l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(iw,null,"Job Proposal #",a.id))),l.createElement(Me,{proposal:a}),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:9},l.createElement(T$,null,"Specs"))),l.createElement(d.Z,{container:!0,spacing:32},l.createElement(d.Z,{item:!0,xs:12},l.createElement(ME,{proposal:a,specs:a.specs,onReject:r,onApprove:t,onCancel:n,onUpdateSpec:i}))))};function MM(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]e.length)&&(t=e.length);for(var n=0,r=Array(t);ne.length)&&(t=e.length);for(var n=0,r=Array(t);nU,tA:()=>$,KL:()=>H,Iw:()=>V,DQ:()=>W,cB:()=>T,LO:()=>M,t5:()=>k,qt:()=>x,Jc:()=>C,L7:()=>Y,EO:()=>B});var r,i,a=n(66289),o=n(41800),s=n.n(o),u=n(67932);(i=r||(r={})).IN_PROGRESS="in_progress",i.PENDING_INCOMING_CONFIRMATIONS="pending_incoming_confirmations",i.PENDING_CONNECTION="pending_connection",i.PENDING_BRIDGE="pending_bridge",i.PENDING_SLEEP="pending_sleep",i.ERRORED="errored",i.COMPLETED="completed";var c=n(87013),l=n(19084),f=n(34823);function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]j,v2:()=>F});var r=n(66289);function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var a="/sessions",o="/sessions",s=function e(t){var n=this;i(this,e),this.api=t,this.createSession=function(e){return n.create(e)},this.destroySession=function(){return n.destroy()},this.create=this.api.createResource(a),this.destroy=this.api.deleteResource(o)};function u(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var c="/v2/bulk_delete_runs",l=function e(t){var n=this;u(this,e),this.api=t,this.bulkDeleteJobRuns=function(e){return n.destroy(e)},this.destroy=this.api.deleteResource(c)};function f(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var d="/v2/chains/evm",h="".concat(d,"/:id"),p=function e(t){var n=this;f(this,e),this.api=t,this.getChains=function(){return n.index()},this.createChain=function(e){return n.create(e)},this.destroyChain=function(e){return n.destroy(void 0,{id:e})},this.updateChain=function(e,t){return n.update(t,{id:e})},this.index=this.api.fetchResource(d),this.create=this.api.createResource(d),this.destroy=this.api.deleteResource(h),this.update=this.api.updateResource(h)};function b(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var m="/v2/keys/evm/chain",g=function e(t){var n=this;b(this,e),this.api=t,this.chain=function(e){var t=new URLSearchParams;t.append("address",e.address),t.append("evmChainID",e.evmChainID),null!==e.nextNonce&&t.append("nextNonce",e.nextNonce),null!==e.abandon&&t.append("abandon",String(e.abandon)),null!==e.enabled&&t.append("enabled",String(e.enabled));var r=m+"?"+t.toString();return n.api.createResource(r)()}};function v(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var y="/v2/jobs",w="".concat(y,"/:specId/runs"),_=function e(t){var n=this;v(this,e),this.api=t,this.createJobRunV2=function(e,t){return n.post(t,{specId:e})},this.post=this.api.createResource(w,!0)};function E(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var S="/v2/log",k=function e(t){var n=this;E(this,e),this.api=t,this.getLogConfig=function(){return n.show()},this.updateLogConfig=function(e){return n.update(e)},this.show=this.api.fetchResource(S),this.update=this.api.updateResource(S)};function x(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var T="/v2/nodes",M=function e(t){var n=this;x(this,e),this.api=t,this.getNodes=function(){return n.index()},this.createNode=function(e){return n.create(e)},this.index=this.api.fetchResource(T),this.create=this.api.createResource(T)};function O(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var A="/v2/enroll_webauthn",L=function e(t){var n=this;O(this,e),this.api=t,this.beginKeyRegistration=function(e){return n.create(e)},this.finishKeyRegistration=function(e){return n.put(e)},this.create=this.api.fetchResource(A),this.put=this.api.createResource(A)};function C(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var I="/v2/build_info",D=function e(t){var n=this;C(this,e),this.api=t,this.show=function(){return n.api.GET(I)()}};function N(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}var P=function e(t){N(this,e),this.api=t,this.buildInfo=new D(this.api),this.bulkDeleteRuns=new l(this.api),this.chains=new p(this.api),this.logConfig=new k(this.api),this.nodes=new M(this.api),this.jobs=new _(this.api),this.webauthn=new L(this.api),this.evmKeys=new g(this.api)},R=new r.V0({base:void 0}),j=new s(R),F=new P(R)},1398(e,t,n){"use strict";n.d(t,{Z:()=>d});var r=n(67294),i=n(32316),a=n(83638),o=n(94184),s=n.n(o);function u(){return(u=Object.assign||function(e){for(var t=1;tc});var r=n(67294),i=n(32316);function a(){return(a=Object.assign||function(e){for(var t=1;tx,jK:()=>v});var r=n(67294),i=n(55977),a=n(45697),o=n.n(a),s=n(82204),u=n(71426),c=n(94184),l=n.n(c),f=n(32316),d=function(e){var t=e.palette.success||{},n=e.palette.warning||{};return{base:{paddingLeft:5*e.spacing.unit,paddingRight:5*e.spacing.unit},success:{backgroundColor:t.main,color:t.contrastText},error:{backgroundColor:e.palette.error.dark,color:e.palette.error.contrastText},warning:{backgroundColor:n.contrastText,color:n.main}}},h=function(e){var t,n=e.success,r=e.error,i=e.warning,a=e.classes,o=e.className;return n?t=a.success:r?t=a.error:i&&(t=a.warning),l()(a.base,o,t)},p=function(e){return r.createElement(s.Z,{className:h(e),square:!0},r.createElement(u.default,{variant:"body2",color:"inherit",component:"div"},e.children))};p.defaultProps={success:!1,error:!1,warning:!1},p.propTypes={success:o().bool,error:o().bool,warning:o().bool};let b=(0,f.withStyles)(d)(p);var m=function(){return r.createElement(r.Fragment,null,"Unhandled error. Please help us by opening a"," ",r.createElement("a",{href:"https://github.com/smartcontractkit/chainlink/issues/new"},"bug report"))};let g=m;function v(e){return"string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null)}function y(e,t){var n;return n="string"==typeof e?e:e.component?e.component(e.props):r.createElement(g,null),r.createElement("p",{key:t},n)}var w=function(e){var t=e.notifications;return r.createElement(b,{error:!0},t.map(y))},_=function(e){var t=e.notifications;return r.createElement(b,{success:!0},t.map(y))},E=function(e){var t=e.errors,n=e.successes;return r.createElement("div",null,(null==t?void 0:t.length)>0&&r.createElement(w,{notifications:t}),n.length>0&&r.createElement(_,{notifications:n}))},S=function(e){return{errors:e.notifications.errors,successes:e.notifications.successes}},k=(0,i.$j)(S)(E);let x=k},9409(e,t,n){"use strict";n.d(t,{ZP:()=>j});var r=n(67294),i=n(55977),a=n(47886),o=n(32316),s=n(1398),u=n(82204),c=n(30060),l=n(71426),f=n(60520),d=n(97779),h=n(57209),p=n(26842),b=n(3950),m=n(5536),g=n(45697),v=n.n(g);let y=n.p+"9f6d832ef97e8493764e.svg";function w(){return(w=Object.assign||function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&_.map(function(e,t){return r.createElement(d.Z,{item:!0,xs:12,key:t},r.createElement(u.Z,{raised:!1,className:v.error},r.createElement(c.Z,null,r.createElement(l.default,{variant:"body1",className:v.errorText},(0,b.jK)(e)))))}),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"email",label:"Email",margin:"normal",value:n,onChange:m("email"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(f.Z,{id:"password",label:"Password",type:"password",autoComplete:"password",margin:"normal",value:h,onChange:m("password"),error:_.length>0,variant:"outlined",fullWidth:!0})),r.createElement(d.Z,{item:!0,xs:12},r.createElement(d.Z,{container:!0,spacing:0,justify:"center"},r.createElement(d.Z,{item:!0},r.createElement(s.Z,{type:"submit",variant:"primary"},"Access Account")))),y&&r.createElement(l.default,{variant:"body1",color:"textSecondary"},"Signing in...")))))))},P=function(e){return{fetching:e.authentication.fetching,authenticated:e.authentication.allowed,errors:e.notifications.errors}},R=(0,i.$j)(P,x({submitSignIn:p.L7}))(N);let j=(0,h.wU)(e)((0,o.withStyles)(D)(R))},16353(e,t,n){"use strict";n.d(t,{ZP:()=>H,rH:()=>U});var r,i=n(55977),a=n(15857),o=n(9541),s=n(19084);function u(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:h,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.Mk.RECEIVE_SIGNOUT_SUCCESS:case s.Mk.RECEIVE_SIGNIN_SUCCESS:var n={allowed:t.authenticated};return o.Ks(n),f(c({},e,n),{errors:[]});case s.Mk.RECEIVE_SIGNIN_FAIL:var r={allowed:!1};return o.Ks(r),f(c({},e,r),{errors:[]});case s.Mk.RECEIVE_SIGNIN_ERROR:case s.Mk.RECEIVE_SIGNOUT_ERROR:var i={allowed:!1};return o.Ks(i),f(c({},e,i),{errors:t.errors||[]});default:return e}};let b=p;function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function g(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:_,t=arguments.length>1?arguments[1]:void 0;return t.type?t.type.startsWith(r.REQUEST)?y(g({},e),{count:e.count+1}):t.type.startsWith(r.RECEIVE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type.startsWith(r.RESPONSE)?y(g({},e),{count:Math.max(e.count-1,0)}):t.type===s.di.REDIRECT?y(g({},e),{count:0}):e:e};let S=E;function k(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function x(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:O,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.MATCH_ROUTE:return M(x({},O),{currentUrl:t.pathname});case s.Ih.NOTIFY_SUCCESS:var n={component:t.component,props:t.props};return M(x({},e),{successes:[n],errors:[]});case s.Ih.NOTIFY_SUCCESS_MSG:return M(x({},e),{successes:[t.msg],errors:[]});case s.Ih.NOTIFY_ERROR:var r=t.error.errors,i=null==r?void 0:r.map(function(e){return L(t,e)});return M(x({},e),{successes:[],errors:i});case s.Ih.NOTIFY_ERROR_MSG:return M(x({},e),{successes:[],errors:[t.msg]});case s.Mk.RECEIVE_SIGNIN_FAIL:return M(x({},e),{successes:[],errors:["Your email or password is incorrect. Please try again"]});default:return e}};function L(e,t){return{component:e.component,props:{msg:t.detail}}}let C=A;function I(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function D(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:R,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.di.REDIRECT:return P(D({},e),{to:t.to});case s.di.MATCH_ROUTE:return P(D({},e),{to:void 0});default:return e}};let F=j;var Y=n(87013),B=(0,a.UY)({authentication:b,fetching:S,notifications:C,redirect:F,buildInfo:Y.Z});B(void 0,{type:"INITIAL_STATE"});var U=i.v9;let H=B},19084(e,t,n){"use strict";var r,i,a,o,s,u,c,l,f,d;n.d(t,{Ih:()=>i,Mk:()=>a,Y0:()=>s,di:()=>r,jp:()=>o}),n(67294),(u=r||(r={})).REDIRECT="REDIRECT",u.MATCH_ROUTE="MATCH_ROUTE",(c=i||(i={})).NOTIFY_SUCCESS="NOTIFY_SUCCESS",c.NOTIFY_SUCCESS_MSG="NOTIFY_SUCCESS_MSG",c.NOTIFY_ERROR="NOTIFY_ERROR",c.NOTIFY_ERROR_MSG="NOTIFY_ERROR_MSG",(l=a||(a={})).REQUEST_SIGNIN="REQUEST_SIGNIN",l.RECEIVE_SIGNIN_SUCCESS="RECEIVE_SIGNIN_SUCCESS",l.RECEIVE_SIGNIN_FAIL="RECEIVE_SIGNIN_FAIL",l.RECEIVE_SIGNIN_ERROR="RECEIVE_SIGNIN_ERROR",l.RECEIVE_SIGNOUT_SUCCESS="RECEIVE_SIGNOUT_SUCCESS",l.RECEIVE_SIGNOUT_ERROR="RECEIVE_SIGNOUT_ERROR",(f=o||(o={})).RECEIVE_CREATE_ERROR="RECEIVE_CREATE_ERROR",f.RECEIVE_CREATE_SUCCESS="RECEIVE_CREATE_SUCCESS",f.RECEIVE_DELETE_ERROR="RECEIVE_DELETE_ERROR",f.RECEIVE_DELETE_SUCCESS="RECEIVE_DELETE_SUCCESS",f.RECEIVE_UPDATE_ERROR="RECEIVE_UPDATE_ERROR",f.RECEIVE_UPDATE_SUCCESS="RECEIVE_UPDATE_SUCCESS",f.REQUEST_CREATE="REQUEST_CREATE",f.REQUEST_DELETE="REQUEST_DELETE",f.REQUEST_UPDATE="REQUEST_UPDATE",f.UPSERT_CONFIGURATION="UPSERT_CONFIGURATION",f.UPSERT_JOB_RUN="UPSERT_JOB_RUN",f.UPSERT_JOB_RUNS="UPSERT_JOB_RUNS",f.UPSERT_TRANSACTION="UPSERT_TRANSACTION",f.UPSERT_TRANSACTIONS="UPSERT_TRANSACTIONS",f.UPSERT_BUILD_INFO="UPSERT_BUILD_INFO",(d=s||(s={})).FETCH_BUILD_INFO_REQUESTED="FETCH_BUILD_INFO_REQUESTED",d.FETCH_BUILD_INFO_SUCCEEDED="FETCH_BUILD_INFO_SUCCEEDED",d.FETCH_BUILD_INFO_FAILED="FETCH_BUILD_INFO_FAILED"},87013(e,t,n){"use strict";n.d(t,{Y:()=>o,Z:()=>u});var r=n(19084);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:o,t=arguments.length>1?arguments[1]:void 0;return t.type===r.Y0.FETCH_BUILD_INFO_SUCCEEDED?a({},t.buildInfo):e};let u=s},34823(e,t,n){"use strict";n.d(t,{N:()=>r});var r=function(e){return e.buildInfo}},73343(e,t,n){"use strict";n.d(t,{r:()=>u});var r=n(19350),i=n(32316),a=n(59114),o=n(5324),s={props:{MuiGrid:{spacing:3*o.default.unit},MuiCardHeader:{titleTypographyProps:{color:"secondary"}}},palette:{action:{hoverOpacity:.3},primary:{light:"#E5F1FF",main:"#3c40c6",contrastText:"#fff"},secondary:{main:"#3d5170"},success:{light:"#e8faf1",main:r.ek.A700,dark:r.ek[700],contrastText:r.y0.white},warning:{light:"#FFFBF1",main:"#fff6b6",contrastText:"#fad27a"},error:{light:"#ffdada",main:"#f44336",dark:"#d32f2f",contrastText:"#fff"},background:{default:"#f5f6f8",appBar:"#3c40c6"},text:{primary:(0,a.darken)(r.BA.A700,.7),secondary:"#818ea3"},listPendingStatus:{background:"#fef7e5",color:"#fecb4c"},listCompletedStatus:{background:"#e9faf2",color:"#4ed495"}},shape:{borderRadius:o.default.unit},overrides:{MuiButton:{root:{borderRadius:o.default.unit/2,textTransform:"none"},sizeLarge:{padding:void 0,fontSize:void 0,paddingTop:o.default.unit,paddingBottom:o.default.unit,paddingLeft:5*o.default.unit,paddingRight:5*o.default.unit}},MuiTableCell:{body:{fontSize:"1rem"},head:{fontSize:"1rem",fontWeight:400}},MuiCardHeader:{root:{borderBottom:"1px solid rgba(0, 0, 0, 0.12)"},action:{marginTop:-2,marginRight:0,"& >*":{marginLeft:2*o.default.unit}},subheader:{marginTop:.5*o.default.unit}}},typography:{useNextVariants:!0,fontFamily:"-apple-system,BlinkMacSystemFont,Roboto,Helvetica,Arial,sans-serif",button:{textTransform:"none",fontSize:"1.2em"},body1:{fontSize:"1.0rem",fontWeight:400,lineHeight:"1.46429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body2:{fontSize:"1.0rem",fontWeight:500,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},body1Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"1rem",lineHeight:1.5,letterSpacing:-.4},body2Next:{color:"rgb(29, 29, 29)",fontWeight:400,fontSize:"0.875rem",lineHeight:1.5,letterSpacing:-.4},display1:{color:"#818ea3",fontSize:"2.125rem",fontWeight:400,lineHeight:"1.20588em",letterSpacing:-.4},display2:{color:"#818ea3",fontSize:"2.8125rem",fontWeight:400,lineHeight:"1.13333em",marginLeft:"-.02em",letterSpacing:-.4},display3:{color:"#818ea3",fontSize:"3.5rem",fontWeight:400,lineHeight:"1.30357em",marginLeft:"-.02em",letterSpacing:-.4},display4:{fontSize:14,fontWeightLight:300,fontWeightMedium:500,fontWeightRegular:400,letterSpacing:-.4},h1:{color:"rgb(29, 29, 29)",fontSize:"6rem",fontWeight:300,lineHeight:1},h2:{color:"rgb(29, 29, 29)",fontSize:"3.75rem",fontWeight:300,lineHeight:1},h3:{color:"rgb(29, 29, 29)",fontSize:"3rem",fontWeight:400,lineHeight:1.04},h4:{color:"rgb(29, 29, 29)",fontSize:"2.125rem",fontWeight:400,lineHeight:1.17},h5:{color:"rgb(29, 29, 29)",fontSize:"1.5rem",fontWeight:400,lineHeight:1.33,letterSpacing:-.4},h6:{fontSize:"0.8rem",fontWeight:450,lineHeight:"1.71429em",color:"rgba(0, 0, 0, 0.87)",letterSpacing:-.4},subheading:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:"1.5em",letterSpacing:-.4},subtitle1:{color:"rgb(29, 29, 29)",fontSize:"1rem",fontWeight:400,lineHeight:1.75,letterSpacing:-.4},subtitle2:{color:"rgb(29, 29, 29)",fontSize:"0.875rem",fontWeight:500,lineHeight:1.57,letterSpacing:-.4}},shadows:["none","0px 1px 3px 0px rgba(0, 0, 0, 0.1),0px 1px 1px 0px rgba(0, 0, 0, 0.04),0px 2px 1px -1px rgba(0, 0, 0, 0.02)","0px 1px 5px 0px rgba(0, 0, 0, 0.1),0px 2px 2px 0px rgba(0, 0, 0, 0.04),0px 3px 1px -2px rgba(0, 0, 0, 0.02)","0px 1px 8px 0px rgba(0, 0, 0, 0.1),0px 3px 4px 0px rgba(0, 0, 0, 0.04),0px 3px 3px -2px rgba(0, 0, 0, 0.02)","0px 2px 4px -1px rgba(0, 0, 0, 0.1),0px 4px 5px 0px rgba(0, 0, 0, 0.04),0px 1px 10px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 5px 8px 0px rgba(0, 0, 0, 0.04),0px 1px 14px 0px rgba(0, 0, 0, 0.02)","0px 3px 5px -1px rgba(0, 0, 0, 0.1),0px 6px 10px 0px rgba(0, 0, 0, 0.04),0px 1px 18px 0px rgba(0, 0, 0, 0.02)","0px 4px 5px -2px rgba(0, 0, 0, 0.1),0px 7px 10px 1px rgba(0, 0, 0, 0.04),0px 2px 16px 1px rgba(0, 0, 0, 0.02)","0px 5px 5px -3px rgba(0, 0, 0, 0.1),0px 8px 10px 1px rgba(0, 0, 0, 0.04),0px 3px 14px 2px rgba(0, 0, 0, 0.02)","0px 5px 6px -3px rgba(0, 0, 0, 0.1),0px 9px 12px 1px rgba(0, 0, 0, 0.04),0px 3px 16px 2px rgba(0, 0, 0, 0.02)","0px 6px 6px -3px rgba(0, 0, 0, 0.1),0px 10px 14px 1px rgba(0, 0, 0, 0.04),0px 4px 18px 3px rgba(0, 0, 0, 0.02)","0px 6px 7px -4px rgba(0, 0, 0, 0.1),0px 11px 15px 1px rgba(0, 0, 0, 0.04),0px 4px 20px 3px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 12px 17px 2px rgba(0, 0, 0, 0.04),0px 5px 22px 4px rgba(0, 0, 0, 0.02)","0px 7px 8px -4px rgba(0, 0, 0, 0.1),0px 13px 19px 2px rgba(0, 0, 0, 0.04),0px 5px 24px 4px rgba(0, 0, 0, 0.02)","0px 7px 9px -4px rgba(0, 0, 0, 0.1),0px 14px 21px 2px rgba(0, 0, 0, 0.04),0px 5px 26px 4px rgba(0, 0, 0, 0.02)","0px 8px 9px -5px rgba(0, 0, 0, 0.1),0px 15px 22px 2px rgba(0, 0, 0, 0.04),0px 6px 28px 5px rgba(0, 0, 0, 0.02)","0px 8px 10px -5px rgba(0, 0, 0, 0.1),0px 16px 24px 2px rgba(0, 0, 0, 0.04),0px 6px 30px 5px rgba(0, 0, 0, 0.02)","0px 8px 11px -5px rgba(0, 0, 0, 0.1),0px 17px 26px 2px rgba(0, 0, 0, 0.04),0px 6px 32px 5px rgba(0, 0, 0, 0.02)","0px 9px 11px -5px rgba(0, 0, 0, 0.1),0px 18px 28px 2px rgba(0, 0, 0, 0.04),0px 7px 34px 6px rgba(0, 0, 0, 0.02)","0px 9px 12px -6px rgba(0, 0, 0, 0.1),0px 19px 29px 2px rgba(0, 0, 0, 0.04),0px 7px 36px 6px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 20px 31px 3px rgba(0, 0, 0, 0.04),0px 8px 38px 7px rgba(0, 0, 0, 0.02)","0px 10px 13px -6px rgba(0, 0, 0, 0.1),0px 21px 33px 3px rgba(0, 0, 0, 0.04),0px 8px 40px 7px rgba(0, 0, 0, 0.02)","0px 10px 14px -6px rgba(0, 0, 0, 0.1),0px 22px 35px 3px rgba(0, 0, 0, 0.04),0px 8px 42px 7px rgba(0, 0, 0, 0.02)","0px 11px 14px -7px rgba(0, 0, 0, 0.1),0px 23px 36px 3px rgba(0, 0, 0, 0.04),0px 9px 44px 8px rgba(0, 0, 0, 0.02)","0px 11px 15px -7px rgba(0, 0, 0, 0.1),0px 24px 38px 3px rgba(0, 0, 0, 0.04),0px 9px 46px 8px rgba(0, 0, 0, 0.02)",]},u=(0,i.createMuiTheme)(s)},66289(e,t,n){"use strict";function r(e){if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){if(!(e instanceof t))throw TypeError("Cannot call a class as a function")}function a(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}function o(e,t,n){return(o=a()?Reflect.construct:function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&f(i,n.prototype),i}).apply(null,arguments)}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&f(e,t)}function c(e){return -1!==Function.toString.call(e).indexOf("[native code]")}function l(e,t){return t&&("object"===p(t)||"function"==typeof t)?t:r(e)}function f(e,t){return(f=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}n.d(t,{V0:()=>B,_7:()=>v});var d,h,p=function(e){return e&&"undefined"!=typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};function b(e){var t="function"==typeof Map?new Map:void 0;return(b=function(e){if(null===e||!c(e))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return o(e,arguments,s(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),f(n,e)})(e)}function m(){if("undefined"==typeof Reflect||!Reflect.construct||Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}function g(e){var t=m();return function(){var n,r=s(e);if(t){var i=s(this).constructor;n=Reflect.construct(r,arguments,i)}else n=r.apply(this,arguments);return l(this,n)}}var v=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"AuthenticationError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e},],r}return n}(b(Error)),y=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"BadRequestError")).errors=a,r}return n}(b(Error)),w=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnprocessableEntityError")).errors=e,r}return n}(b(Error)),_=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"ServerError")).errors=e,r}return n}(b(Error)),E=function(e){u(n,e);var t=g(n);function n(e){var r,a=e.errors;return i(this,n),(r=t.call(this,"ConflictError")).errors=a,r}return n}(b(Error)),S=function(e){u(n,e);var t=g(n);function n(e){var r;return i(this,n),(r=t.call(this,"UnknownResponseError(".concat(e.statusText,")"))).errors=[{status:e.status,detail:e.statusText},],r}return n}(b(Error));function k(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e4;return Promise.race([fetch(e,t),new Promise(function(e,t){return setTimeout(function(){return t(Error("timeout"))},n)}),])}function x(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&i[i.length-1])&&(6===a[0]||2===a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=200&&e.status<300))return[3,2];return[2,e.json()];case 2:if(400!==e.status)return[3,3];return[2,e.json().then(function(e){throw new y(e)})];case 3:if(401!==e.status)return[3,4];throw new v(e);case 4:if(422!==e.status)return[3,6];return[4,$(e)];case 5:throw n=i.sent(),new w(n);case 6:if(409!==e.status)return[3,7];return[2,e.json().then(function(e){throw new E(e)})];case 7:if(!(e.status>=500))return[3,9];return[4,$(e)];case 8:throw r=i.sent(),new _(r);case 9:throw new S(e);case 10:return[2]}})})).apply(this,arguments)}function $(e){return z.apply(this,arguments)}function z(){return(z=j(function(e){return Y(this,function(t){return[2,e.json().then(function(t){return t.errors?t.errors.map(function(t){return{status:e.status,detail:t.detail}}):G(e)}).catch(function(){return G(e)})]})})).apply(this,arguments)}function G(e){return[{status:e.status,detail:e.statusText},]}},50109(e,t,n){"use strict";n.d(t,{LK:()=>o,U2:()=>i,eT:()=>s,t8:()=>a});var r=n(12795);function i(e){return r.ZP.getItem("chainlink.".concat(e))}function a(e,t){r.ZP.setItem("chainlink.".concat(e),t)}function o(e){var t=i(e),n={};if(t)try{return JSON.parse(t)}catch(r){}return n}function s(e,t){a(e,JSON.stringify(t))}},9541(e,t,n){"use strict";n.d(t,{Ks:()=>u,Tp:()=>a,iR:()=>o,pm:()=>s});var r=n(50109),i="persistURL";function a(){return r.U2(i)||""}function o(e){r.t8(i,e)}function s(){return r.LK("authentication")}function u(e){r.eT("authentication",e)}},67121(e,t,n){"use strict";function r(e){var t,n=e.Symbol;return"function"==typeof n?n.observable?t=n.observable:(t=n("observable"),n.observable=t):t="@@observable",t}n.r(t),n.d(t,{default:()=>o}),e=n.hmd(e),i="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n.g?n.g:e;var i,a=r(i);let o=a},2177(e,t,n){"use strict";n.d(t,{Z:()=>o});var r=!0,i="Invariant failed";function a(e,t){if(!e){if(r)throw Error(i);throw Error(i+": "+(t||""))}}let o=a},11742(e){e.exports=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var t=document.activeElement,n=[],r=0;ri,pi:()=>a});var r=function(e,t){return(r=Object.setPrototypeOf||({__proto__:[]})instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(e,t)};function i(e,t){if("function"!=typeof t&&null!==t)throw TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var a=function(){return(a=Object.assign||function(e){for(var t,n=1,r=arguments.length;nr})},94927(e,t,n){function r(e,t){if(i("noDeprecation"))return e;var n=!1;function r(){if(!n){if(i("throwDeprecation"))throw Error(t);i("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}return r}function i(e){try{if(!n.g.localStorage)return!1}catch(t){return!1}var r=n.g.localStorage[e];return null!=r&&"true"===String(r).toLowerCase()}e.exports=r},42473(e){"use strict";var t=function(){};e.exports=t},84763(e){e.exports=Worker},47529(e){e.exports=n;var t=Object.prototype.hasOwnProperty;function n(){for(var e={},n=0;nr,O:()=>a}),(i=r||(r={}))[i.loading=1]="loading",i[i.setVariables=2]="setVariables",i[i.fetchMore=3]="fetchMore",i[i.refetch=4]="refetch",i[i.poll=6]="poll",i[i.ready=7]="ready",i[i.error=8]="error"},30990(e,t,n){"use strict";n.d(t,{MS:()=>s,YG:()=>a,cA:()=>c,ls:()=>o});var r=n(23564);n(83952);var i=n(13154),a=Symbol();function o(e){return!!e.extensions&&Array.isArray(e.extensions[a])}function s(e){return e.hasOwnProperty("graphQLErrors")}var u=function(e){var t=(0,r.ev)((0,r.ev)((0,r.ev)([],e.graphQLErrors,!0),e.clientErrors,!0),e.protocolErrors,!0);return e.networkError&&t.push(e.networkError),t.map(function(e){return(0,i.s)(e)&&e.message||"Error message not found."}).join("\n")},c=function(e){function t(n){var r=n.graphQLErrors,i=n.protocolErrors,a=n.clientErrors,o=n.networkError,s=n.errorMessage,c=n.extraInfo,l=e.call(this,s)||this;return l.name="ApolloError",l.graphQLErrors=r||[],l.protocolErrors=i||[],l.clientErrors=a||[],l.networkError=o||null,l.message=s||u(l),l.extraInfo=c,l.__proto__=t.prototype,l}return(0,r.ZT)(t,e),t}(Error)},85317(e,t,n){"use strict";n.d(t,{K:()=>a});var r=n(67294),i=n(30320).aS?Symbol.for("__APOLLO_CONTEXT__"):"__APOLLO_CONTEXT__";function a(){var e=r.createContext[i];return e||(Object.defineProperty(r.createContext,i,{value:e=r.createContext({}),enumerable:!1,writable:!1,configurable:!0}),e.displayName="ApolloContext"),e}},21436(e,t,n){"use strict";n.d(t,{O:()=>i,k:()=>r});var r=Array.isArray;function i(e){return Array.isArray(e)&&e.length>0}},30320(e,t,n){"use strict";n.d(t,{DN:()=>s,JC:()=>l,aS:()=>o,mr:()=>i,sy:()=>a});var r=n(83952),i="function"==typeof WeakMap&&"ReactNative"!==(0,r.wY)(function(){return navigator.product}),a="function"==typeof WeakSet,o="function"==typeof Symbol&&"function"==typeof Symbol.for,s=o&&Symbol.asyncIterator,u="function"==typeof(0,r.wY)(function(){return window.document.createElement}),c=(0,r.wY)(function(){return navigator.userAgent.indexOf("jsdom")>=0})||!1,l=u&&!c},53712(e,t,n){"use strict";function r(){for(var e=[],t=0;tr})},10542(e,t,n){"use strict";n.d(t,{J:()=>o}),n(83952);var r=n(13154);function i(e){var t=new Set([e]);return t.forEach(function(e){(0,r.s)(e)&&a(e)===e&&Object.getOwnPropertyNames(e).forEach(function(n){(0,r.s)(e[n])&&t.add(e[n])})}),e}function a(e){if(__DEV__&&!Object.isFrozen(e))try{Object.freeze(e)}catch(t){if(t instanceof TypeError)return null;throw t}return e}function o(e){return __DEV__&&i(e),e}},14012(e,t,n){"use strict";n.d(t,{J:()=>a});var r=n(23564),i=n(53712);function a(e,t){return(0,i.o)(e,t,t.variables&&{variables:(0,r.pi)((0,r.pi)({},e&&e.variables),t.variables)})}},13154(e,t,n){"use strict";function r(e){return null!==e&&"object"==typeof e}n.d(t,{s:()=>r})},83952(e,t,n){"use strict";n.d(t,{ej:()=>u,kG:()=>c,wY:()=>h});var r,i=n(70655),a="Invariant Violation",o=Object.setPrototypeOf,s=void 0===o?function(e,t){return e.__proto__=t,e}:o,u=function(e){function t(n){void 0===n&&(n=a);var r=e.call(this,"number"==typeof n?a+": "+n+" (see https://github.com/apollographql/invariant-packages)":n)||this;return r.framesToPop=1,r.name=a,s(r,t.prototype),r}return(0,i.ZT)(t,e),t}(Error);function c(e,t){if(!e)throw new u(t)}var l=["debug","log","warn","error","silent"],f=l.indexOf("log");function d(e){return function(){if(l.indexOf(e)>=f)return(console[e]||console.log).apply(console,arguments)}}function h(e){try{return e()}catch(t){}}(r=c||(c={})).debug=d("debug"),r.log=d("log"),r.warn=d("warn"),r.error=d("error");let p=h(function(){return globalThis})||h(function(){return window})||h(function(){return self})||h(function(){return global})||h(function(){return h.constructor("return this")()});var b="__",m=[b,b].join("DEV");function g(){try{return Boolean(__DEV__)}catch(e){return Object.defineProperty(p,m,{value:"production"!==h(function(){return"production"}),enumerable:!1,configurable:!0,writable:!0}),p[m]}}let v=g();function y(e){try{return e()}catch(t){}}var w=y(function(){return globalThis})||y(function(){return window})||y(function(){return self})||y(function(){return global})||y(function(){return y.constructor("return this")()}),_=!1;function E(){!w||y(function(){return"production"})||y(function(){return process})||(Object.defineProperty(w,"process",{value:{env:{NODE_ENV:"production"}},configurable:!0,enumerable:!1,writable:!0}),_=!0)}function S(){_&&(delete w.process,_=!1)}E();var k=n(10143);function x(){return k.H,S()}function T(){__DEV__?c("boolean"==typeof v,v):c("boolean"==typeof v,39)}x(),T()},87462(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;tr})},25821(e,t,n){"use strict";n.d(t,{Z:()=>s});var r=n(45695);function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var a=10,o=2;function s(e){return u(e,[])}function u(e,t){switch(i(e)){case"string":return JSON.stringify(e);case"function":return e.name?"[function ".concat(e.name,"]"):"[function]";case"object":if(null===e)return"null";return c(e,t);default:return String(e)}}function c(e,t){if(-1!==t.indexOf(e))return"[Circular]";var n=[].concat(t,[e]),r=d(e);if(void 0!==r){var i=r.call(e);if(i!==e)return"string"==typeof i?i:u(i,n)}else if(Array.isArray(e))return f(e,n);return l(e,n)}function l(e,t){var n=Object.keys(e);return 0===n.length?"{}":t.length>o?"["+h(e)+"]":"{ "+n.map(function(n){var r=u(e[n],t);return n+": "+r}).join(", ")+" }"}function f(e,t){if(0===e.length)return"[]";if(t.length>o)return"[Array]";for(var n=Math.min(a,e.length),r=e.length-n,i=[],s=0;s1&&i.push("... ".concat(r," more items")),"["+i.join(", ")+"]"}function d(e){var t=e[String(r.Z)];return"function"==typeof t?t:"function"==typeof e.inspect?e.inspect:void 0}function h(e){var t=Object.prototype.toString.call(e).replace(/^\[object /,"").replace(/]$/,"");if("Object"===t&&"function"==typeof e.constructor){var n=e.constructor.name;if("string"==typeof n&&""!==n)return n}return t}},45695(e,t,n){"use strict";n.d(t,{Z:()=>i});var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):void 0;let i=r},25217(e,t,n){"use strict";function r(e,t){if(!Boolean(e))throw Error(null!=t?t:"Unexpected invariant triggered.")}n.d(t,{Ye:()=>o,WU:()=>s,UG:()=>u});var i=n(45695);function a(e){var t=e.prototype.toJSON;"function"==typeof t||r(0),e.prototype.inspect=t,i.Z&&(e.prototype[i.Z]=t)}var o=function(){function e(e,t,n){this.start=e.start,this.end=t.end,this.startToken=e,this.endToken=t,this.source=n}return e.prototype.toJSON=function(){return{start:this.start,end:this.end}},e}();a(o);var s=function(){function e(e,t,n,r,i,a,o){this.kind=e,this.start=t,this.end=n,this.line=r,this.column=i,this.value=o,this.prev=a,this.next=null}return e.prototype.toJSON=function(){return{kind:this.kind,value:this.value,line:this.line,column:this.column}},e}();function u(e){return null!=e&&"string"==typeof e.kind}a(s)},87392(e,t,n){"use strict";function r(e){var t=e.split(/\r\n|[\n\r]/g),n=a(e);if(0!==n)for(var r=1;ro&&i(t[s-1]);)--s;return t.slice(o,s).join("\n")}function i(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=-1===e.indexOf("\n"),i=" "===e[0]||" "===e[0],a='"'===e[e.length-1],o="\\"===e[e.length-1],s=!r||a||o||n,u="";return s&&!(r&&i)&&(u+="\n"+t),u+=t?e.replace(/\n/g,"\n"+t):e,s&&(u+="\n"),'"""'+u.replace(/"""/g,'\\"""')+'"""'}n.d(t,{LZ:()=>o,W7:()=>r})},97359(e,t,n){"use strict";n.d(t,{h:()=>r});var r=Object.freeze({NAME:"Name",DOCUMENT:"Document",OPERATION_DEFINITION:"OperationDefinition",VARIABLE_DEFINITION:"VariableDefinition",SELECTION_SET:"SelectionSet",FIELD:"Field",ARGUMENT:"Argument",FRAGMENT_SPREAD:"FragmentSpread",INLINE_FRAGMENT:"InlineFragment",FRAGMENT_DEFINITION:"FragmentDefinition",VARIABLE:"Variable",INT:"IntValue",FLOAT:"FloatValue",STRING:"StringValue",BOOLEAN:"BooleanValue",NULL:"NullValue",ENUM:"EnumValue",LIST:"ListValue",OBJECT:"ObjectValue",OBJECT_FIELD:"ObjectField",DIRECTIVE:"Directive",NAMED_TYPE:"NamedType",LIST_TYPE:"ListType",NON_NULL_TYPE:"NonNullType",SCHEMA_DEFINITION:"SchemaDefinition",OPERATION_TYPE_DEFINITION:"OperationTypeDefinition",SCALAR_TYPE_DEFINITION:"ScalarTypeDefinition",OBJECT_TYPE_DEFINITION:"ObjectTypeDefinition",FIELD_DEFINITION:"FieldDefinition",INPUT_VALUE_DEFINITION:"InputValueDefinition",INTERFACE_TYPE_DEFINITION:"InterfaceTypeDefinition",UNION_TYPE_DEFINITION:"UnionTypeDefinition",ENUM_TYPE_DEFINITION:"EnumTypeDefinition",ENUM_VALUE_DEFINITION:"EnumValueDefinition",INPUT_OBJECT_TYPE_DEFINITION:"InputObjectTypeDefinition",DIRECTIVE_DEFINITION:"DirectiveDefinition",SCHEMA_EXTENSION:"SchemaExtension",SCALAR_TYPE_EXTENSION:"ScalarTypeExtension",OBJECT_TYPE_EXTENSION:"ObjectTypeExtension",INTERFACE_TYPE_EXTENSION:"InterfaceTypeExtension",UNION_TYPE_EXTENSION:"UnionTypeExtension",ENUM_TYPE_EXTENSION:"EnumTypeExtension",INPUT_OBJECT_TYPE_EXTENSION:"InputObjectTypeExtension"})},10143(e,t,n){"use strict";n.d(t,{H:()=>c,T:()=>l});var r=n(99763),i=n(25821);function a(e,t){if(!Boolean(e))throw Error(t)}let o=function(e,t){return e instanceof t};function s(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"GraphQL request",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{line:1,column:1};"string"==typeof e||a(0,"Body must be a string. Received: ".concat((0,i.Z)(e),".")),this.body=e,this.name=t,this.locationOffset=n,this.locationOffset.line>0||a(0,"line in locationOffset is 1-indexed and must be positive."),this.locationOffset.column>0||a(0,"column in locationOffset is 1-indexed and must be positive.")}return u(e,[{key:r.YF,get:function(){return"Source"}}]),e}();function l(e){return o(e,c)}},99763(e,t,n){"use strict";n.d(t,{YF:()=>r});var r="function"==typeof Symbol&&null!=Symbol.toStringTag?Symbol.toStringTag:"@@toStringTag"},37452(e){"use strict";e.exports=JSON.parse('{"AElig":"\xc6","AMP":"&","Aacute":"\xc1","Acirc":"\xc2","Agrave":"\xc0","Aring":"\xc5","Atilde":"\xc3","Auml":"\xc4","COPY":"\xa9","Ccedil":"\xc7","ETH":"\xd0","Eacute":"\xc9","Ecirc":"\xca","Egrave":"\xc8","Euml":"\xcb","GT":">","Iacute":"\xcd","Icirc":"\xce","Igrave":"\xcc","Iuml":"\xcf","LT":"<","Ntilde":"\xd1","Oacute":"\xd3","Ocirc":"\xd4","Ograve":"\xd2","Oslash":"\xd8","Otilde":"\xd5","Ouml":"\xd6","QUOT":"\\"","REG":"\xae","THORN":"\xde","Uacute":"\xda","Ucirc":"\xdb","Ugrave":"\xd9","Uuml":"\xdc","Yacute":"\xdd","aacute":"\xe1","acirc":"\xe2","acute":"\xb4","aelig":"\xe6","agrave":"\xe0","amp":"&","aring":"\xe5","atilde":"\xe3","auml":"\xe4","brvbar":"\xa6","ccedil":"\xe7","cedil":"\xb8","cent":"\xa2","copy":"\xa9","curren":"\xa4","deg":"\xb0","divide":"\xf7","eacute":"\xe9","ecirc":"\xea","egrave":"\xe8","eth":"\xf0","euml":"\xeb","frac12":"\xbd","frac14":"\xbc","frac34":"\xbe","gt":">","iacute":"\xed","icirc":"\xee","iexcl":"\xa1","igrave":"\xec","iquest":"\xbf","iuml":"\xef","laquo":"\xab","lt":"<","macr":"\xaf","micro":"\xb5","middot":"\xb7","nbsp":"\xa0","not":"\xac","ntilde":"\xf1","oacute":"\xf3","ocirc":"\xf4","ograve":"\xf2","ordf":"\xaa","ordm":"\xba","oslash":"\xf8","otilde":"\xf5","ouml":"\xf6","para":"\xb6","plusmn":"\xb1","pound":"\xa3","quot":"\\"","raquo":"\xbb","reg":"\xae","sect":"\xa7","shy":"\xad","sup1":"\xb9","sup2":"\xb2","sup3":"\xb3","szlig":"\xdf","thorn":"\xfe","times":"\xd7","uacute":"\xfa","ucirc":"\xfb","ugrave":"\xf9","uml":"\xa8","uuml":"\xfc","yacute":"\xfd","yen":"\xa5","yuml":"\xff"}')},93580(e){"use strict";e.exports=JSON.parse('{"0":"�","128":"€","130":"‚","131":"ƒ","132":"„","133":"…","134":"†","135":"‡","136":"ˆ","137":"‰","138":"Š","139":"‹","140":"Œ","142":"Ž","145":"‘","146":"’","147":"“","148":"”","149":"•","150":"–","151":"—","152":"˜","153":"™","154":"š","155":"›","156":"œ","158":"ž","159":"Ÿ"}')},67946(e){"use strict";e.exports=JSON.parse('{"locale":"en","long":{"year":{"previous":"last year","current":"this year","next":"next year","past":{"one":"{0} year ago","other":"{0} years ago"},"future":{"one":"in {0} year","other":"in {0} years"}},"quarter":{"previous":"last quarter","current":"this quarter","next":"next quarter","past":{"one":"{0} quarter ago","other":"{0} quarters ago"},"future":{"one":"in {0} quarter","other":"in {0} quarters"}},"month":{"previous":"last month","current":"this month","next":"next month","past":{"one":"{0} month ago","other":"{0} months ago"},"future":{"one":"in {0} month","other":"in {0} months"}},"week":{"previous":"last week","current":"this week","next":"next week","past":{"one":"{0} week ago","other":"{0} weeks ago"},"future":{"one":"in {0} week","other":"in {0} weeks"}},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":{"one":"{0} hour ago","other":"{0} hours ago"},"future":{"one":"in {0} hour","other":"in {0} hours"}},"minute":{"current":"this minute","past":{"one":"{0} minute ago","other":"{0} minutes ago"},"future":{"one":"in {0} minute","other":"in {0} minutes"}},"second":{"current":"now","past":{"one":"{0} second ago","other":"{0} seconds ago"},"future":{"one":"in {0} second","other":"in {0} seconds"}}},"short":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"narrow":{"year":{"previous":"last yr.","current":"this yr.","next":"next yr.","past":"{0} yr. ago","future":"in {0} yr."},"quarter":{"previous":"last qtr.","current":"this qtr.","next":"next qtr.","past":{"one":"{0} qtr. ago","other":"{0} qtrs. ago"},"future":{"one":"in {0} qtr.","other":"in {0} qtrs."}},"month":{"previous":"last mo.","current":"this mo.","next":"next mo.","past":"{0} mo. ago","future":"in {0} mo."},"week":{"previous":"last wk.","current":"this wk.","next":"next wk.","past":"{0} wk. ago","future":"in {0} wk."},"day":{"previous":"yesterday","current":"today","next":"tomorrow","past":{"one":"{0} day ago","other":"{0} days ago"},"future":{"one":"in {0} day","other":"in {0} days"}},"hour":{"current":"this hour","past":"{0} hr. ago","future":"in {0} hr."},"minute":{"current":"this minute","past":"{0} min. ago","future":"in {0} min."},"second":{"current":"now","past":"{0} sec. ago","future":"in {0} sec."}},"now":{"now":{"current":"now","future":"in a moment","past":"just now"}},"mini":{"year":"{0}yr","month":"{0}mo","week":"{0}wk","day":"{0}d","hour":"{0}h","minute":"{0}m","second":"{0}s","now":"now"},"short-time":{"year":"{0} yr.","month":"{0} mo.","week":"{0} wk.","day":{"one":"{0} day","other":"{0} days"},"hour":"{0} hr.","minute":"{0} min.","second":"{0} sec."},"long-time":{"year":{"one":"{0} year","other":"{0} years"},"month":{"one":"{0} month","other":"{0} months"},"week":{"one":"{0} week","other":"{0} weeks"},"day":{"one":"{0} day","other":"{0} days"},"hour":{"one":"{0} hour","other":"{0} hours"},"minute":{"one":"{0} minute","other":"{0} minutes"},"second":{"one":"{0} second","other":"{0} seconds"}}}')}},__webpack_module_cache__={};function __webpack_require__(e){var t=__webpack_module_cache__[e];if(void 0!==t)return t.exports;var n=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e].call(n.exports,n,n.exports,__webpack_require__),n.loaded=!0,n.exports}__webpack_require__.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;__webpack_require__.t=function(n,r){if(1&r&&(n=this(n)),8&r||"object"==typeof n&&n&&(4&r&&n.__esModule||16&r&&"function"==typeof n.then))return n;var i=Object.create(null);__webpack_require__.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var o=2&r&&n;"object"==typeof o&&!~e.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach(e=>a[e]=()=>n[e]);return a.default=()=>n,__webpack_require__.d(i,a),i}})(),__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set(){throw Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},__webpack_require__.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),__webpack_require__.p="/assets/",__webpack_require__.nc=void 0;var __webpack_exports__={};(()=>{"use strict";var e,t,n,r,i=__webpack_require__(32316),a=__webpack_require__(8126),o=__webpack_require__(5690),s=__webpack_require__(30381),u=__webpack_require__.n(s),c=__webpack_require__(67294),l=__webpack_require__(73935),f=__webpack_require__.n(l),d=__webpack_require__(57209),h=__webpack_require__(55977),p=__webpack_require__(15857),b=__webpack_require__(28500);function m(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(i){return"function"==typeof i?i(n,r,e):t(i)}}}}var g=m();g.withExtraArgument=m;let v=g;var y=__webpack_require__(76489);function w(e){return function(t){return function(n){return function(r){n(r);var i=e||document&&document.cookie||"",a=t.getState();if("MATCH_ROUTE"===r.type&&"/signin"!==a.notifications.currentUrl){var o=(0,y.Q)(i);if(o.explorer)try{var s=JSON.parse(o.explorer);if("error"===s.status){var u=_(s.url);n({type:"NOTIFY_ERROR_MSG",msg:u})}}catch(c){n({type:"NOTIFY_ERROR_MSG",msg:"Invalid explorer status"})}}}}}}function _(e){var t="Can't connect to explorer: ".concat(e);return e.match(/^wss?:.+/)?t:"".concat(t,". You must use a websocket.")}var E=__webpack_require__(16353);function S(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function ei(e,t){if(e){if("string"==typeof e)return ea(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if("Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return ea(e,t)}}function ea(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n1,i=!1,a=arguments[1],o=a;return new n(function(n){return t.subscribe({next:function(t){var a=!i;if(i=!0,!a||r)try{o=e(o,t)}catch(s){return n.error(s)}else o=t},error:function(e){n.error(e)},complete:function(){if(!i&&!r)return n.error(TypeError("Cannot reduce an empty sequence"));n.next(o),n.complete()}})})},t.concat=function(){for(var e=this,t=arguments.length,n=Array(t),r=0;r=0&&i.splice(e,1),o()}});i.push(s)},error:function(e){r.error(e)},complete:function(){o()}});function o(){a.closed&&0===i.length&&r.complete()}return function(){i.forEach(function(e){return e.unsubscribe()}),a.unsubscribe()}})},t[ed]=function(){return this},e.from=function(t){var n="function"==typeof this?this:e;if(null==t)throw TypeError(t+" is not an object");var r=ep(t,ed);if(r){var i=r.call(t);if(Object(i)!==i)throw TypeError(i+" is not an object");return em(i)&&i.constructor===n?i:new n(function(e){return i.subscribe(e)})}if(ec("iterator")&&(r=ep(t,ef)))return new n(function(e){ev(function(){if(!e.closed){for(var n,i=er(r.call(t));!(n=i()).done;){var a=n.value;if(e.next(a),e.closed)return}e.complete()}})});if(Array.isArray(t))return new n(function(e){ev(function(){if(!e.closed){for(var n=0;n0))return n.connection.key;var r=n.connection.filter?n.connection.filter:[];r.sort();var i={};return r.forEach(function(e){i[e]=t[e]}),"".concat(n.connection.key,"(").concat(eV(i),")")}var a=e;if(t){var o=eV(t);a+="(".concat(o,")")}return n&&Object.keys(n).forEach(function(e){-1===eW.indexOf(e)&&(n[e]&&Object.keys(n[e]).length?a+="@".concat(e,"(").concat(eV(n[e]),")"):a+="@".concat(e))}),a},{setStringify:function(e){var t=eV;return eV=e,t}}),eV=function(e){return JSON.stringify(e,eq)};function eq(e,t){return(0,eO.s)(t)&&!Array.isArray(t)&&(t=Object.keys(t).sort().reduce(function(e,n){return e[n]=t[n],e},{})),t}function eZ(e,t){if(e.arguments&&e.arguments.length){var n={};return e.arguments.forEach(function(e){var r;return ez(n,e.name,e.value,t)}),n}return null}function eX(e){return e.alias?e.alias.value:e.name.value}function eJ(e,t,n){for(var r,i=0,a=t.selections;it.indexOf(i))throw __DEV__?new Q.ej("illegal argument: ".concat(i)):new Q.ej(27)}return e}function tt(e,t){return t?t(e):eT.of()}function tn(e){return"function"==typeof e?new ta(e):e}function tr(e){return e.request.length<=1}var ti=function(e){function t(t,n){var r=e.call(this,t)||this;return r.link=n,r}return(0,en.ZT)(t,e),t}(Error),ta=function(){function e(e){e&&(this.request=e)}return e.empty=function(){return new e(function(){return eT.of()})},e.from=function(t){return 0===t.length?e.empty():t.map(tn).reduce(function(e,t){return e.concat(t)})},e.split=function(t,n,r){var i=tn(n),a=tn(r||new e(tt));return new e(tr(i)&&tr(a)?function(e){return t(e)?i.request(e)||eT.of():a.request(e)||eT.of()}:function(e,n){return t(e)?i.request(e,n)||eT.of():a.request(e,n)||eT.of()})},e.execute=function(e,t){return e.request(eM(t.context,e7(te(t))))||eT.of()},e.concat=function(t,n){var r=tn(t);if(tr(r))return __DEV__&&Q.kG.warn(new ti("You are calling concat on a terminating link, which will have no effect",r)),r;var i=tn(n);return new e(tr(i)?function(e){return r.request(e,function(e){return i.request(e)||eT.of()})||eT.of()}:function(e,t){return r.request(e,function(e){return i.request(e,t)||eT.of()})||eT.of()})},e.prototype.split=function(t,n,r){return this.concat(e.split(t,n,r||new e(tt)))},e.prototype.concat=function(t){return e.concat(this,t)},e.prototype.request=function(e,t){throw __DEV__?new Q.ej("request is not implemented"):new Q.ej(22)},e.prototype.onError=function(e,t){if(t&&t.error)return t.error(e),!1;throw e},e.prototype.setOnError=function(e){return this.onError=e,this},e}(),to=__webpack_require__(25821),ts=__webpack_require__(25217),tu={Name:[],Document:["definitions"],OperationDefinition:["name","variableDefinitions","directives","selectionSet"],VariableDefinition:["variable","type","defaultValue","directives"],Variable:["name"],SelectionSet:["selections"],Field:["alias","name","arguments","directives","selectionSet"],Argument:["name","value"],FragmentSpread:["name","directives"],InlineFragment:["typeCondition","directives","selectionSet"],FragmentDefinition:["name","variableDefinitions","typeCondition","directives","selectionSet"],IntValue:[],FloatValue:[],StringValue:[],BooleanValue:[],NullValue:[],EnumValue:[],ListValue:["values"],ObjectValue:["fields"],ObjectField:["name","value"],Directive:["name","arguments"],NamedType:["name"],ListType:["type"],NonNullType:["type"],SchemaDefinition:["description","directives","operationTypes"],OperationTypeDefinition:["type"],ScalarTypeDefinition:["description","name","directives"],ObjectTypeDefinition:["description","name","interfaces","directives","fields"],FieldDefinition:["description","name","arguments","type","directives"],InputValueDefinition:["description","name","type","defaultValue","directives"],InterfaceTypeDefinition:["description","name","interfaces","directives","fields"],UnionTypeDefinition:["description","name","directives","types"],EnumTypeDefinition:["description","name","directives","values"],EnumValueDefinition:["description","name","directives"],InputObjectTypeDefinition:["description","name","directives","fields"],DirectiveDefinition:["description","name","arguments","locations"],SchemaExtension:["directives","operationTypes"],ScalarTypeExtension:["name","directives"],ObjectTypeExtension:["name","interfaces","directives","fields"],InterfaceTypeExtension:["name","interfaces","directives","fields"],UnionTypeExtension:["name","directives","types"],EnumTypeExtension:["name","directives","values"],InputObjectTypeExtension:["name","directives","fields"]},tc=Object.freeze({});function tl(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:tu,r=void 0,i=Array.isArray(e),a=[e],o=-1,s=[],u=void 0,c=void 0,l=void 0,f=[],d=[],h=e;do{var p,b=++o===a.length,m=b&&0!==s.length;if(b){if(c=0===d.length?void 0:f[f.length-1],u=l,l=d.pop(),m){if(i)u=u.slice();else{for(var g={},v=0,y=Object.keys(u);v1)for(var r=new tB,i=1;i=0;--a){var o=i[a],s=isNaN(+o)?{}:[];s[o]=t,t=s}n=r.merge(n,t)}),n}var tW=Object.prototype.hasOwnProperty;function tK(e,t){var n,r,i,a,o;return(0,en.mG)(this,void 0,void 0,function(){var s,u,c,l,f,d,h,p,b,m,g,v,y,w,_,E,S,k,x,T,M,O,A;return(0,en.Jh)(this,function(L){switch(L.label){case 0:if(void 0===TextDecoder)throw Error("TextDecoder must be defined in the environment: please import a polyfill.");s=new TextDecoder("utf-8"),u=null===(n=e.headers)||void 0===n?void 0:n.get("content-type"),c="boundary=",l=(null==u?void 0:u.includes(c))?null==u?void 0:u.substring((null==u?void 0:u.indexOf(c))+c.length).replace(/['"]/g,"").replace(/\;(.*)/gm,"").trim():"-",f="\r\n--".concat(l),d="",h=tI(e),p=!0,L.label=1;case 1:if(!p)return[3,3];return[4,h.next()];case 2:for(m=(b=L.sent()).value,g=b.done,v="string"==typeof m?m:s.decode(m),y=d.length-f.length+1,p=!g,d+=v,w=d.indexOf(f,y);w>-1;){if(_=void 0,_=(O=[d.slice(0,w),d.slice(w+f.length),])[0],d=O[1],E=_.indexOf("\r\n\r\n"),(k=(S=tV(_.slice(0,E)))["content-type"])&&-1===k.toLowerCase().indexOf("application/json"))throw Error("Unsupported patch content type: application/json is required.");if(x=_.slice(E))try{T=tq(e,x),Object.keys(T).length>1||"data"in T||"incremental"in T||"errors"in T||"payload"in T?tz(T)?(M={},"payload"in T&&(M=(0,en.pi)({},T.payload)),"errors"in T&&(M=(0,en.pi)((0,en.pi)({},M),{extensions:(0,en.pi)((0,en.pi)({},"extensions"in M?M.extensions:null),((A={})[tN.YG]=T.errors,A))})),null===(r=t.next)||void 0===r||r.call(t,M)):null===(i=t.next)||void 0===i||i.call(t,T):1===Object.keys(T).length&&"hasNext"in T&&!T.hasNext&&(null===(a=t.complete)||void 0===a||a.call(t))}catch(C){tZ(C,t)}w=d.indexOf(f)}return[3,1];case 3:return null===(o=t.complete)||void 0===o||o.call(t),[2]}})})}function tV(e){var t={};return e.split("\n").forEach(function(e){var n=e.indexOf(":");if(n>-1){var r=e.slice(0,n).trim().toLowerCase(),i=e.slice(n+1).trim();t[r]=i}}),t}function tq(e,t){e.status>=300&&tD(e,function(){try{return JSON.parse(t)}catch(e){return t}}(),"Response not successful: Received status code ".concat(e.status));try{return JSON.parse(t)}catch(n){var r=n;throw r.name="ServerParseError",r.response=e,r.statusCode=e.status,r.bodyText=t,r}}function tZ(e,t){var n,r;"AbortError"!==e.name&&(e.result&&e.result.errors&&e.result.data&&(null===(n=t.next)||void 0===n||n.call(t,e.result)),null===(r=t.error)||void 0===r||r.call(t,e))}function tX(e,t,n){tJ(t)(e).then(function(e){var t,r;null===(t=n.next)||void 0===t||t.call(n,e),null===(r=n.complete)||void 0===r||r.call(n)}).catch(function(e){return tZ(e,n)})}function tJ(e){return function(t){return t.text().then(function(e){return tq(t,e)}).then(function(n){return t.status>=300&&tD(t,n,"Response not successful: Received status code ".concat(t.status)),Array.isArray(n)||tW.call(n,"data")||tW.call(n,"errors")||tD(t,n,"Server response was missing for query '".concat(Array.isArray(e)?e.map(function(e){return e.operationName}):e.operationName,"'.")),n})}}var tQ=function(e){if(!e&&"undefined"==typeof fetch)throw __DEV__?new Q.ej("\n\"fetch\" has not been found globally and no fetcher has been configured. To fix this, install a fetch package (like https://www.npmjs.com/package/cross-fetch), instantiate the fetcher, and pass it into your HttpLink constructor. For example:\n\nimport fetch from 'cross-fetch';\nimport { ApolloClient, HttpLink } from '@apollo/client';\nconst client = new ApolloClient({\n link: new HttpLink({ uri: '/graphql', fetch })\n});\n "):new Q.ej(23)},t1=__webpack_require__(87392);function t0(e){return tl(e,{leave:t3})}var t2=80,t3={Name:function(e){return e.value},Variable:function(e){return"$"+e.name},Document:function(e){return t5(e.definitions,"\n\n")+"\n"},OperationDefinition:function(e){var t=e.operation,n=e.name,r=t9("(",t5(e.variableDefinitions,", "),")"),i=t5(e.directives," "),a=e.selectionSet;return n||i||r||"query"!==t?t5([t,t5([n,r]),i,a]," "):a},VariableDefinition:function(e){var t=e.variable,n=e.type,r=e.defaultValue,i=e.directives;return t+": "+n+t9(" = ",r)+t9(" ",t5(i," "))},SelectionSet:function(e){return t6(e.selections)},Field:function(e){var t=e.alias,n=e.name,r=e.arguments,i=e.directives,a=e.selectionSet,o=t9("",t,": ")+n,s=o+t9("(",t5(r,", "),")");return s.length>t2&&(s=o+t9("(\n",t8(t5(r,"\n")),"\n)")),t5([s,t5(i," "),a]," ")},Argument:function(e){var t;return e.name+": "+e.value},FragmentSpread:function(e){var t;return"..."+e.name+t9(" ",t5(e.directives," "))},InlineFragment:function(e){var t=e.typeCondition,n=e.directives,r=e.selectionSet;return t5(["...",t9("on ",t),t5(n," "),r]," ")},FragmentDefinition:function(e){var t=e.name,n=e.typeCondition,r=e.variableDefinitions,i=e.directives,a=e.selectionSet;return"fragment ".concat(t).concat(t9("(",t5(r,", "),")")," ")+"on ".concat(n," ").concat(t9("",t5(i," ")," "))+a},IntValue:function(e){return e.value},FloatValue:function(e){return e.value},StringValue:function(e,t){var n=e.value;return e.block?(0,t1.LZ)(n,"description"===t?"":" "):JSON.stringify(n)},BooleanValue:function(e){return e.value?"true":"false"},NullValue:function(){return"null"},EnumValue:function(e){return e.value},ListValue:function(e){return"["+t5(e.values,", ")+"]"},ObjectValue:function(e){return"{"+t5(e.fields,", ")+"}"},ObjectField:function(e){var t;return e.name+": "+e.value},Directive:function(e){var t;return"@"+e.name+t9("(",t5(e.arguments,", "),")")},NamedType:function(e){return e.name},ListType:function(e){return"["+e.type+"]"},NonNullType:function(e){return e.type+"!"},SchemaDefinition:t4(function(e){var t=e.directives,n=e.operationTypes;return t5(["schema",t5(t," "),t6(n)]," ")}),OperationTypeDefinition:function(e){var t;return e.operation+": "+e.type},ScalarTypeDefinition:t4(function(e){var t;return t5(["scalar",e.name,t5(e.directives," ")]," ")}),ObjectTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),FieldDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.type,i=e.directives;return t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+": "+r+t9(" ",t5(i," "))}),InputValueDefinition:t4(function(e){var t=e.name,n=e.type,r=e.defaultValue,i=e.directives;return t5([t+": "+n,t9("= ",r),t5(i," ")]," ")}),InterfaceTypeDefinition:t4(function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")}),UnionTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.types;return t5(["union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")}),EnumTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.values;return t5(["enum",t,t5(n," "),t6(r)]," ")}),EnumValueDefinition:t4(function(e){var t;return t5([e.name,t5(e.directives," ")]," ")}),InputObjectTypeDefinition:t4(function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["input",t,t5(n," "),t6(r)]," ")}),DirectiveDefinition:t4(function(e){var t=e.name,n=e.arguments,r=e.repeatable,i=e.locations;return"directive @"+t+(ne(n)?t9("(\n",t8(t5(n,"\n")),"\n)"):t9("(",t5(n,", "),")"))+(r?" repeatable":"")+" on "+t5(i," | ")}),SchemaExtension:function(e){var t=e.directives,n=e.operationTypes;return t5(["extend schema",t5(t," "),t6(n)]," ")},ScalarTypeExtension:function(e){var t;return t5(["extend scalar",e.name,t5(e.directives," ")]," ")},ObjectTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend type",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},InterfaceTypeExtension:function(e){var t=e.name,n=e.interfaces,r=e.directives,i=e.fields;return t5(["extend interface",t,t9("implements ",t5(n," & ")),t5(r," "),t6(i)]," ")},UnionTypeExtension:function(e){var t=e.name,n=e.directives,r=e.types;return t5(["extend union",t,t5(n," "),r&&0!==r.length?"= "+t5(r," | "):""]," ")},EnumTypeExtension:function(e){var t=e.name,n=e.directives,r=e.values;return t5(["extend enum",t,t5(n," "),t6(r)]," ")},InputObjectTypeExtension:function(e){var t=e.name,n=e.directives,r=e.fields;return t5(["extend input",t,t5(n," "),t6(r)]," ")}};function t4(e){return function(t){return t5([t.description,e(t)],"\n")}}function t5(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return null!==(t=null==e?void 0:e.filter(function(e){return e}).join(n))&&void 0!==t?t:""}function t6(e){return t9("{\n",t8(t5(e,"\n")),"\n}")}function t9(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return null!=t&&""!==t?e+t+n:""}function t8(e){return t9(" ",e.replace(/\n/g,"\n "))}function t7(e){return -1!==e.indexOf("\n")}function ne(e){return null!=e&&e.some(t7)}var nt,nn,nr,ni={http:{includeQuery:!0,includeExtensions:!1,preserveHeaderCase:!1},headers:{accept:"*/*","content-type":"application/json"},options:{method:"POST"}},na=function(e,t){return t(e)};function no(e,t){for(var n=[],r=2;rObject.create(null),{forEach:nv,slice:ny}=Array.prototype,{hasOwnProperty:nw}=Object.prototype;class n_{constructor(e=!0,t=ng){this.weakness=e,this.makeData=t}lookup(...e){return this.lookupArray(e)}lookupArray(e){let t=this;return nv.call(e,e=>t=t.getChildTrie(e)),nw.call(t,"data")?t.data:t.data=this.makeData(ny.call(e))}peek(...e){return this.peekArray(e)}peekArray(e){let t=this;for(let n=0,r=e.length;t&&n=0;--o)t.definitions[o].kind===nL.h.OPERATION_DEFINITION&&++a;var s=nN(e),u=e.some(function(e){return e.remove}),c=function(e){return u&&e&&e.some(s)},l=new Map,f=!1,d={enter:function(e){if(c(e.directives))return f=!0,null}},h=tl(t,{Field:d,InlineFragment:d,VariableDefinition:{enter:function(){return!1}},Variable:{enter:function(e,t,n,r,a){var o=i(a);o&&o.variables.add(e.name.value)}},FragmentSpread:{enter:function(e,t,n,r,a){if(c(e.directives))return f=!0,null;var o=i(a);o&&o.fragmentSpreads.add(e.name.value)}},FragmentDefinition:{enter:function(e,t,n,r){l.set(JSON.stringify(r),e)},leave:function(e,t,n,i){return e===l.get(JSON.stringify(i))?e:a>0&&e.selectionSet.selections.every(function(e){return e.kind===nL.h.FIELD&&"__typename"===e.name.value})?(r(e.name.value).removed=!0,f=!0,null):void 0}},Directive:{leave:function(e){if(s(e))return f=!0,null}}});if(!f)return t;var p=function(e){return e.transitiveVars||(e.transitiveVars=new Set(e.variables),e.removed||e.fragmentSpreads.forEach(function(t){p(r(t)).transitiveVars.forEach(function(t){e.transitiveVars.add(t)})})),e},b=new Set;h.definitions.forEach(function(e){e.kind===nL.h.OPERATION_DEFINITION?p(n(e.name&&e.name.value)).fragmentSpreads.forEach(function(e){b.add(e)}):e.kind!==nL.h.FRAGMENT_DEFINITION||0!==a||r(e.name.value).removed||b.add(e.name.value)}),b.forEach(function(e){p(r(e)).fragmentSpreads.forEach(function(e){b.add(e)})});var m=function(e){return!!(!b.has(e)||r(e).removed)},g={enter:function(e){if(m(e.name.value))return null}};return nD(tl(h,{FragmentSpread:g,FragmentDefinition:g,OperationDefinition:{leave:function(e){if(e.variableDefinitions){var t=p(n(e.name&&e.name.value)).transitiveVars;if(t.size0},t.prototype.tearDownQuery=function(){this.isTornDown||(this.concast&&this.observer&&(this.concast.removeObserver(this.observer),delete this.concast,delete this.observer),this.stopPolling(),this.subscriptions.forEach(function(e){return e.unsubscribe()}),this.subscriptions.clear(),this.queryManager.stopQuery(this.queryId),this.observers.clear(),this.isTornDown=!0)},t}(eT);function n4(e){var t=e.options,n=t.fetchPolicy,r=t.nextFetchPolicy;return"cache-and-network"===n||"network-only"===n?e.reobserve({fetchPolicy:"cache-first",nextFetchPolicy:function(){return(this.nextFetchPolicy=r,"function"==typeof r)?r.apply(this,arguments):n}}):e.reobserve()}function n5(e){__DEV__&&Q.kG.error("Unhandled error",e.message,e.stack)}function n6(e){__DEV__&&e&&__DEV__&&Q.kG.debug("Missing cache result fields: ".concat(JSON.stringify(e)),e)}function n9(e){return"network-only"===e||"no-cache"===e||"standby"===e}nK(n3);function n8(e){return e.kind===nL.h.FIELD||e.kind===nL.h.FRAGMENT_SPREAD||e.kind===nL.h.INLINE_FRAGMENT}function n7(e){return e.kind===Kind.SCALAR_TYPE_DEFINITION||e.kind===Kind.OBJECT_TYPE_DEFINITION||e.kind===Kind.INTERFACE_TYPE_DEFINITION||e.kind===Kind.UNION_TYPE_DEFINITION||e.kind===Kind.ENUM_TYPE_DEFINITION||e.kind===Kind.INPUT_OBJECT_TYPE_DEFINITION}function re(e){return e.kind===Kind.SCALAR_TYPE_EXTENSION||e.kind===Kind.OBJECT_TYPE_EXTENSION||e.kind===Kind.INTERFACE_TYPE_EXTENSION||e.kind===Kind.UNION_TYPE_EXTENSION||e.kind===Kind.ENUM_TYPE_EXTENSION||e.kind===Kind.INPUT_OBJECT_TYPE_EXTENSION}var rt=function(){return Object.create(null)},rn=Array.prototype,rr=rn.forEach,ri=rn.slice,ra=function(){function e(e,t){void 0===e&&(e=!0),void 0===t&&(t=rt),this.weakness=e,this.makeData=t}return e.prototype.lookup=function(){for(var e=[],t=0;tclass{constructor(){this.id=["slot",rc++,Date.now(),Math.random().toString(36).slice(2),].join(":")}hasValue(){for(let e=rs;e;e=e.parent)if(this.id in e.slots){let t=e.slots[this.id];if(t===ru)break;return e!==rs&&(rs.slots[this.id]=t),!0}return rs&&(rs.slots[this.id]=ru),!1}getValue(){if(this.hasValue())return rs.slots[this.id]}withValue(e,t,n,r){let i={__proto__:null,[this.id]:e},a=rs;rs={parent:a,slots:i};try{return t.apply(r,n)}finally{rs=a}}static bind(e){let t=rs;return function(){let n=rs;try{return rs=t,e.apply(this,arguments)}finally{rs=n}}}static noContext(e,t,n){if(!rs)return e.apply(n,t);{let r=rs;try{return rs=null,e.apply(n,t)}finally{rs=r}}}};function rf(e){try{return e()}catch(t){}}let rd="@wry/context:Slot",rh=rf(()=>globalThis)||rf(()=>global)||Object.create(null),rp=rh,rb=rp[rd]||Array[rd]||function(e){try{Object.defineProperty(rp,rd,{value:e,enumerable:!1,writable:!1,configurable:!0})}finally{return e}}(rl()),{bind:rm,noContext:rg}=rb;function rv(){}var ry=function(){function e(e,t){void 0===e&&(e=1/0),void 0===t&&(t=rv),this.max=e,this.dispose=t,this.map=new Map,this.newest=null,this.oldest=null}return e.prototype.has=function(e){return this.map.has(e)},e.prototype.get=function(e){var t=this.getNode(e);return t&&t.value},e.prototype.getNode=function(e){var t=this.map.get(e);if(t&&t!==this.newest){var n=t.older,r=t.newer;r&&(r.older=n),n&&(n.newer=r),t.older=this.newest,t.older.newer=t,t.newer=null,this.newest=t,t===this.oldest&&(this.oldest=r)}return t},e.prototype.set=function(e,t){var n=this.getNode(e);return n?n.value=t:(n={key:e,value:t,newer:null,older:this.newest},this.newest&&(this.newest.newer=n),this.newest=n,this.oldest=this.oldest||n,this.map.set(e,n),n.value)},e.prototype.clean=function(){for(;this.oldest&&this.map.size>this.max;)this.delete(this.oldest.key)},e.prototype.delete=function(e){var t=this.map.get(e);return!!t&&(t===this.newest&&(this.newest=t.older),t===this.oldest&&(this.oldest=t.newer),t.newer&&(t.newer.older=t.older),t.older&&(t.older.newer=t.newer),this.map.delete(e),this.dispose(t.value,e),!0)},e}(),rw=new rb,r_=Object.prototype.hasOwnProperty,rE=void 0===(n=Array.from)?function(e){var t=[];return e.forEach(function(e){return t.push(e)}),t}:n;function rS(e){var t=e.unsubscribe;"function"==typeof t&&(e.unsubscribe=void 0,t())}var rk=[],rx=100;function rT(e,t){if(!e)throw Error(t||"assertion failure")}function rM(e,t){var n=e.length;return n>0&&n===t.length&&e[n-1]===t[n-1]}function rO(e){switch(e.length){case 0:throw Error("unknown value");case 1:return e[0];case 2:throw e[1]}}function rA(e){return e.slice(0)}var rL=function(){function e(t){this.fn=t,this.parents=new Set,this.childValues=new Map,this.dirtyChildren=null,this.dirty=!0,this.recomputing=!1,this.value=[],this.deps=null,++e.count}return e.prototype.peek=function(){if(1===this.value.length&&!rN(this))return rC(this),this.value[0]},e.prototype.recompute=function(e){return rT(!this.recomputing,"already recomputing"),rC(this),rN(this)?rI(this,e):rO(this.value)},e.prototype.setDirty=function(){this.dirty||(this.dirty=!0,this.value.length=0,rR(this),rS(this))},e.prototype.dispose=function(){var e=this;this.setDirty(),rH(this),rF(this,function(t,n){t.setDirty(),r$(t,e)})},e.prototype.forget=function(){this.dispose()},e.prototype.dependOn=function(e){e.add(this),this.deps||(this.deps=rk.pop()||new Set),this.deps.add(e)},e.prototype.forgetDeps=function(){var e=this;this.deps&&(rE(this.deps).forEach(function(t){return t.delete(e)}),this.deps.clear(),rk.push(this.deps),this.deps=null)},e.count=0,e}();function rC(e){var t=rw.getValue();if(t)return e.parents.add(t),t.childValues.has(e)||t.childValues.set(e,[]),rN(e)?rY(t,e):rB(t,e),t}function rI(e,t){return rH(e),rw.withValue(e,rD,[e,t]),rz(e,t)&&rP(e),rO(e.value)}function rD(e,t){e.recomputing=!0,e.value.length=0;try{e.value[0]=e.fn.apply(null,t)}catch(n){e.value[1]=n}e.recomputing=!1}function rN(e){return e.dirty||!!(e.dirtyChildren&&e.dirtyChildren.size)}function rP(e){e.dirty=!1,!rN(e)&&rj(e)}function rR(e){rF(e,rY)}function rj(e){rF(e,rB)}function rF(e,t){var n=e.parents.size;if(n)for(var r=rE(e.parents),i=0;i0&&e.childValues.forEach(function(t,n){r$(e,n)}),e.forgetDeps(),rT(null===e.dirtyChildren)}function r$(e,t){t.parents.delete(e),e.childValues.delete(t),rU(e,t)}function rz(e,t){if("function"==typeof e.subscribe)try{rS(e),e.unsubscribe=e.subscribe.apply(null,t)}catch(n){return e.setDirty(),!1}return!0}var rG={setDirty:!0,dispose:!0,forget:!0};function rW(e){var t=new Map,n=e&&e.subscribe;function r(e){var r=rw.getValue();if(r){var i=t.get(e);i||t.set(e,i=new Set),r.dependOn(i),"function"==typeof n&&(rS(i),i.unsubscribe=n(e))}}return r.dirty=function(e,n){var r=t.get(e);if(r){var i=n&&r_.call(rG,n)?n:"setDirty";rE(r).forEach(function(e){return e[i]()}),t.delete(e),rS(r)}},r}function rK(){var e=new ra("function"==typeof WeakMap);return function(){return e.lookupArray(arguments)}}var rV=rK(),rq=new Set;function rZ(e,t){void 0===t&&(t=Object.create(null));var n=new ry(t.max||65536,function(e){return e.dispose()}),r=t.keyArgs,i=t.makeCacheKey||rK(),a=function(){var a=i.apply(null,r?r.apply(null,arguments):arguments);if(void 0===a)return e.apply(null,arguments);var o=n.get(a);o||(n.set(a,o=new rL(e)),o.subscribe=t.subscribe,o.forget=function(){return n.delete(a)});var s=o.recompute(Array.prototype.slice.call(arguments));return n.set(a,o),rq.add(n),rw.hasValue()||(rq.forEach(function(e){return e.clean()}),rq.clear()),s};function o(e){var t=n.get(e);t&&t.setDirty()}function s(e){var t=n.get(e);if(t)return t.peek()}function u(e){return n.delete(e)}return Object.defineProperty(a,"size",{get:function(){return n.map.size},configurable:!1,enumerable:!1}),a.dirtyKey=o,a.dirty=function(){o(i.apply(null,arguments))},a.peekKey=s,a.peek=function(){return s(i.apply(null,arguments))},a.forgetKey=u,a.forget=function(){return u(i.apply(null,arguments))},a.makeCacheKey=i,a.getKey=r?function(){return i.apply(null,r.apply(null,arguments))}:i,Object.freeze(a)}var rX=new rb,rJ=new WeakMap;function rQ(e){var t=rJ.get(e);return t||rJ.set(e,t={vars:new Set,dep:rW()}),t}function r1(e){rQ(e).vars.forEach(function(t){return t.forgetCache(e)})}function r0(e){rQ(e).vars.forEach(function(t){return t.attachCache(e)})}function r2(e){var t=new Set,n=new Set,r=function(a){if(arguments.length>0){if(e!==a){e=a,t.forEach(function(e){rQ(e).dep.dirty(r),r3(e)});var o=Array.from(n);n.clear(),o.forEach(function(t){return t(e)})}}else{var s=rX.getValue();s&&(i(s),rQ(s).dep(r))}return e};r.onNextChange=function(e){return n.add(e),function(){n.delete(e)}};var i=r.attachCache=function(e){return t.add(e),rQ(e).vars.add(r),r};return r.forgetCache=function(e){return t.delete(e)},r}function r3(e){e.broadcastWatches&&e.broadcastWatches()}var r4=function(){function e(e){var t=e.cache,n=e.client,r=e.resolvers,i=e.fragmentMatcher;this.selectionsToResolveCache=new WeakMap,this.cache=t,n&&(this.client=n),r&&this.addResolvers(r),i&&this.setFragmentMatcher(i)}return e.prototype.addResolvers=function(e){var t=this;this.resolvers=this.resolvers||{},Array.isArray(e)?e.forEach(function(e){t.resolvers=tj(t.resolvers,e)}):this.resolvers=tj(this.resolvers,e)},e.prototype.setResolvers=function(e){this.resolvers={},this.addResolvers(e)},e.prototype.getResolvers=function(){return this.resolvers||{}},e.prototype.runResolvers=function(e){var t=e.document,n=e.remoteResult,r=e.context,i=e.variables,a=e.onlyRunForcedResolvers,o=void 0!==a&&a;return(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(e){return t?[2,this.resolveDocument(t,n.data,r,i,this.fragmentMatcher,o).then(function(e){return(0,en.pi)((0,en.pi)({},n),{data:e.result})})]:[2,n]})})},e.prototype.setFragmentMatcher=function(e){this.fragmentMatcher=e},e.prototype.getFragmentMatcher=function(){return this.fragmentMatcher},e.prototype.clientQuery=function(e){return tb(["client"],e)&&this.resolvers?e:null},e.prototype.serverQuery=function(e){return n$(e)},e.prototype.prepareContext=function(e){var t=this.cache;return(0,en.pi)((0,en.pi)({},e),{cache:t,getCacheKey:function(e){return t.identify(e)}})},e.prototype.addExportedVariables=function(e,t,n){return void 0===t&&(t={}),void 0===n&&(n={}),(0,en.mG)(this,void 0,void 0,function(){return(0,en.Jh)(this,function(r){return e?[2,this.resolveDocument(e,this.buildRootValueFromCache(e,t)||{},this.prepareContext(n),t).then(function(e){return(0,en.pi)((0,en.pi)({},t),e.exportedVariables)})]:[2,(0,en.pi)({},t)]})})},e.prototype.shouldForceResolvers=function(e){var t=!1;return tl(e,{Directive:{enter:function(e){if("client"===e.name.value&&e.arguments&&(t=e.arguments.some(function(e){return"always"===e.name.value&&"BooleanValue"===e.value.kind&&!0===e.value.value})))return tc}}}),t},e.prototype.buildRootValueFromCache=function(e,t){return this.cache.diff({query:nH(e),variables:t,returnPartialData:!0,optimistic:!1}).result},e.prototype.resolveDocument=function(e,t,n,r,i,a){return void 0===n&&(n={}),void 0===r&&(r={}),void 0===i&&(i=function(){return!0}),void 0===a&&(a=!1),(0,en.mG)(this,void 0,void 0,function(){var o,s,u,c,l,f,d,h,p,b,m;return(0,en.Jh)(this,function(g){return o=e9(e),s=e4(e),u=eL(s),c=this.collectSelectionsToResolve(o,u),f=(l=o.operation)?l.charAt(0).toUpperCase()+l.slice(1):"Query",d=this,h=d.cache,p=d.client,b={fragmentMap:u,context:(0,en.pi)((0,en.pi)({},n),{cache:h,client:p}),variables:r,fragmentMatcher:i,defaultOperationType:f,exportedVariables:{},selectionsToResolve:c,onlyRunForcedResolvers:a},m=!1,[2,this.resolveSelectionSet(o.selectionSet,m,t,b).then(function(e){return{result:e,exportedVariables:b.exportedVariables}})]})})},e.prototype.resolveSelectionSet=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c=this;return(0,en.Jh)(this,function(l){return i=r.fragmentMap,a=r.context,o=r.variables,s=[n],u=function(e){return(0,en.mG)(c,void 0,void 0,function(){var u,c;return(0,en.Jh)(this,function(l){return(t||r.selectionsToResolve.has(e))&&td(e,o)?eQ(e)?[2,this.resolveField(e,t,n,r).then(function(t){var n;void 0!==t&&s.push(((n={})[eX(e)]=t,n))})]:(e1(e)?u=e:(u=i[e.name.value],__DEV__?(0,Q.kG)(u,"No fragment named ".concat(e.name.value)):(0,Q.kG)(u,11)),u&&u.typeCondition&&(c=u.typeCondition.name.value,r.fragmentMatcher(n,c,a)))?[2,this.resolveSelectionSet(u.selectionSet,t,n,r).then(function(e){s.push(e)})]:[2]:[2]})})},[2,Promise.all(e.selections.map(u)).then(function(){return tF(s)})]})})},e.prototype.resolveField=function(e,t,n,r){return(0,en.mG)(this,void 0,void 0,function(){var i,a,o,s,u,c,l,f,d,h=this;return(0,en.Jh)(this,function(p){return n?(i=r.variables,a=e.name.value,o=eX(e),s=a!==o,c=Promise.resolve(u=n[o]||n[a]),(!r.onlyRunForcedResolvers||this.shouldForceResolvers(e))&&(l=n.__typename||r.defaultOperationType,(f=this.resolvers&&this.resolvers[l])&&(d=f[s?a:o])&&(c=Promise.resolve(rX.withValue(this.cache,d,[n,eZ(e,i),r.context,{field:e,fragmentMap:r.fragmentMap},])))),[2,c.then(function(n){if(void 0===n&&(n=u),e.directives&&e.directives.forEach(function(e){"export"===e.name.value&&e.arguments&&e.arguments.forEach(function(e){"as"===e.name.value&&"StringValue"===e.value.kind&&(r.exportedVariables[e.value.value]=n)})}),!e.selectionSet||null==n)return n;var i,a,o=null!==(a=null===(i=e.directives)||void 0===i?void 0:i.some(function(e){return"client"===e.name.value}))&&void 0!==a&&a;return Array.isArray(n)?h.resolveSubSelectedArray(e,t||o,n,r):e.selectionSet?h.resolveSelectionSet(e.selectionSet,t||o,n,r):void 0})]):[2,null]})})},e.prototype.resolveSubSelectedArray=function(e,t,n,r){var i=this;return Promise.all(n.map(function(n){return null===n?null:Array.isArray(n)?i.resolveSubSelectedArray(e,t,n,r):e.selectionSet?i.resolveSelectionSet(e.selectionSet,t,n,r):void 0}))},e.prototype.collectSelectionsToResolve=function(e,t){var n=function(e){return!Array.isArray(e)},r=this.selectionsToResolveCache;function i(e){if(!r.has(e)){var a=new Set;r.set(e,a),tl(e,{Directive:function(e,t,r,i,o){"client"===e.name.value&&o.forEach(function(e){n(e)&&n8(e)&&a.add(e)})},FragmentSpread:function(e,r,o,s,u){var c=t[e.name.value];__DEV__?(0,Q.kG)(c,"No fragment named ".concat(e.name.value)):(0,Q.kG)(c,12);var l=i(c);l.size>0&&(u.forEach(function(e){n(e)&&n8(e)&&a.add(e)}),a.add(e),l.forEach(function(e){a.add(e)}))}})}return r.get(e)}return i(e)},e}(),r5=new(t_.mr?WeakMap:Map);function r6(e,t){var n=e[t];"function"==typeof n&&(e[t]=function(){return r5.set(e,(r5.get(e)+1)%1e15),n.apply(this,arguments)})}function r9(e){e.notifyTimeout&&(clearTimeout(e.notifyTimeout),e.notifyTimeout=void 0)}var r8=function(){function e(e,t){void 0===t&&(t=e.generateQueryId()),this.queryId=t,this.listeners=new Set,this.document=null,this.lastRequestId=1,this.subscriptions=new Set,this.stopped=!1,this.dirty=!1,this.observableQuery=null;var n=this.cache=e.cache;r5.has(n)||(r5.set(n,0),r6(n,"evict"),r6(n,"modify"),r6(n,"reset"))}return e.prototype.init=function(e){var t=e.networkStatus||nZ.I.loading;return this.variables&&this.networkStatus!==nZ.I.loading&&!(0,nm.D)(this.variables,e.variables)&&(t=nZ.I.setVariables),(0,nm.D)(e.variables,this.variables)||(this.lastDiff=void 0),Object.assign(this,{document:e.document,variables:e.variables,networkError:null,graphQLErrors:this.graphQLErrors||[],networkStatus:t}),e.observableQuery&&this.setObservableQuery(e.observableQuery),e.lastRequestId&&(this.lastRequestId=e.lastRequestId),this},e.prototype.reset=function(){r9(this),this.dirty=!1},e.prototype.getDiff=function(e){void 0===e&&(e=this.variables);var t=this.getDiffOptions(e);if(this.lastDiff&&(0,nm.D)(t,this.lastDiff.options))return this.lastDiff.diff;this.updateWatch(this.variables=e);var n=this.observableQuery;if(n&&"no-cache"===n.options.fetchPolicy)return{complete:!1};var r=this.cache.diff(t);return this.updateLastDiff(r,t),r},e.prototype.updateLastDiff=function(e,t){this.lastDiff=e?{diff:e,options:t||this.getDiffOptions()}:void 0},e.prototype.getDiffOptions=function(e){var t;return void 0===e&&(e=this.variables),{query:this.document,variables:e,returnPartialData:!0,optimistic:!0,canonizeResults:null===(t=this.observableQuery)||void 0===t?void 0:t.options.canonizeResults}},e.prototype.setDiff=function(e){var t=this,n=this.lastDiff&&this.lastDiff.diff;this.updateLastDiff(e),this.dirty||(0,nm.D)(n&&n.result,e&&e.result)||(this.dirty=!0,this.notifyTimeout||(this.notifyTimeout=setTimeout(function(){return t.notify()},0)))},e.prototype.setObservableQuery=function(e){var t=this;e!==this.observableQuery&&(this.oqListener&&this.listeners.delete(this.oqListener),this.observableQuery=e,e?(e.queryInfo=this,this.listeners.add(this.oqListener=function(){t.getDiff().fromOptimisticTransaction?e.observe():n4(e)})):delete this.oqListener)},e.prototype.notify=function(){var e=this;r9(this),this.shouldNotify()&&this.listeners.forEach(function(t){return t(e)}),this.dirty=!1},e.prototype.shouldNotify=function(){if(!this.dirty||!this.listeners.size)return!1;if((0,nZ.O)(this.networkStatus)&&this.observableQuery){var e=this.observableQuery.options.fetchPolicy;if("cache-only"!==e&&"cache-and-network"!==e)return!1}return!0},e.prototype.stop=function(){if(!this.stopped){this.stopped=!0,this.reset(),this.cancel(),this.cancel=e.prototype.cancel,this.subscriptions.forEach(function(e){return e.unsubscribe()});var t=this.observableQuery;t&&t.stopPolling()}},e.prototype.cancel=function(){},e.prototype.updateWatch=function(e){var t=this;void 0===e&&(e=this.variables);var n=this.observableQuery;if(!n||"no-cache"!==n.options.fetchPolicy){var r=(0,en.pi)((0,en.pi)({},this.getDiffOptions(e)),{watcher:this,callback:function(e){return t.setDiff(e)}});this.lastWatch&&(0,nm.D)(r,this.lastWatch)||(this.cancel(),this.cancel=this.cache.watch(this.lastWatch=r))}},e.prototype.resetLastWrite=function(){this.lastWrite=void 0},e.prototype.shouldWrite=function(e,t){var n=this.lastWrite;return!(n&&n.dmCount===r5.get(this.cache)&&(0,nm.D)(t,n.variables)&&(0,nm.D)(e.data,n.result.data))},e.prototype.markResult=function(e,t,n,r){var i=this,a=new tB,o=(0,tP.O)(e.errors)?e.errors.slice(0):[];if(this.reset(),"incremental"in e&&(0,tP.O)(e.incremental)){var s=tG(this.getDiff().result,e);e.data=s}else if("hasNext"in e&&e.hasNext){var u=this.getDiff();e.data=a.merge(u.result,e.data)}this.graphQLErrors=o,"no-cache"===n.fetchPolicy?this.updateLastDiff({result:e.data,complete:!0},this.getDiffOptions(n.variables)):0!==r&&(r7(e,n.errorPolicy)?this.cache.performTransaction(function(a){if(i.shouldWrite(e,n.variables))a.writeQuery({query:t,data:e.data,variables:n.variables,overwrite:1===r}),i.lastWrite={result:e,variables:n.variables,dmCount:r5.get(i.cache)};else if(i.lastDiff&&i.lastDiff.diff.complete){e.data=i.lastDiff.diff.result;return}var o=i.getDiffOptions(n.variables),s=a.diff(o);i.stopped||i.updateWatch(n.variables),i.updateLastDiff(s,o),s.complete&&(e.data=s.result)}):this.lastWrite=void 0)},e.prototype.markReady=function(){return this.networkError=null,this.networkStatus=nZ.I.ready},e.prototype.markError=function(e){return this.networkStatus=nZ.I.error,this.lastWrite=void 0,this.reset(),e.graphQLErrors&&(this.graphQLErrors=e.graphQLErrors),e.networkError&&(this.networkError=e.networkError),e},e}();function r7(e,t){void 0===t&&(t="none");var n="ignore"===t||"all"===t,r=!nO(e);return!r&&n&&e.data&&(r=!0),r}var ie=Object.prototype.hasOwnProperty,it=function(){function e(e){var t=e.cache,n=e.link,r=e.defaultOptions,i=e.queryDeduplication,a=void 0!==i&&i,o=e.onBroadcast,s=e.ssrMode,u=void 0!==s&&s,c=e.clientAwareness,l=void 0===c?{}:c,f=e.localState,d=e.assumeImmutableResults;this.clientAwareness={},this.queries=new Map,this.fetchCancelFns=new Map,this.transformCache=new(t_.mr?WeakMap:Map),this.queryIdCounter=1,this.requestIdCounter=1,this.mutationIdCounter=1,this.inFlightLinkObservables=new Map,this.cache=t,this.link=n,this.defaultOptions=r||Object.create(null),this.queryDeduplication=a,this.clientAwareness=l,this.localState=f||new r4({cache:t}),this.ssrMode=u,this.assumeImmutableResults=!!d,(this.onBroadcast=o)&&(this.mutationStore=Object.create(null))}return e.prototype.stop=function(){var e=this;this.queries.forEach(function(t,n){e.stopQueryNoBroadcast(n)}),this.cancelPendingFetches(__DEV__?new Q.ej("QueryManager stopped while query was in flight"):new Q.ej(14))},e.prototype.cancelPendingFetches=function(e){this.fetchCancelFns.forEach(function(t){return t(e)}),this.fetchCancelFns.clear()},e.prototype.mutate=function(e){var t,n,r=e.mutation,i=e.variables,a=e.optimisticResponse,o=e.updateQueries,s=e.refetchQueries,u=void 0===s?[]:s,c=e.awaitRefetchQueries,l=void 0!==c&&c,f=e.update,d=e.onQueryUpdated,h=e.fetchPolicy,p=void 0===h?(null===(t=this.defaultOptions.mutate)||void 0===t?void 0:t.fetchPolicy)||"network-only":h,b=e.errorPolicy,m=void 0===b?(null===(n=this.defaultOptions.mutate)||void 0===n?void 0:n.errorPolicy)||"none":b,g=e.keepRootFields,v=e.context;return(0,en.mG)(this,void 0,void 0,function(){var e,t,n,s,c,h;return(0,en.Jh)(this,function(b){switch(b.label){case 0:if(__DEV__?(0,Q.kG)(r,"mutation option is required. You must specify your GraphQL document in the mutation option."):(0,Q.kG)(r,15),__DEV__?(0,Q.kG)("network-only"===p||"no-cache"===p,"Mutations support only 'network-only' or 'no-cache' fetchPolicy strings. The default `network-only` behavior automatically writes mutation results to the cache. Passing `no-cache` skips the cache write."):(0,Q.kG)("network-only"===p||"no-cache"===p,16),e=this.generateMutationId(),n=(t=this.transform(r)).document,s=t.hasClientExports,r=this.cache.transformForLink(n),i=this.getVariables(r,i),!s)return[3,2];return[4,this.localState.addExportedVariables(r,i,v)];case 1:i=b.sent(),b.label=2;case 2:return c=this.mutationStore&&(this.mutationStore[e]={mutation:r,variables:i,loading:!0,error:null}),a&&this.markMutationOptimistic(a,{mutationId:e,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,updateQueries:o,update:f,keepRootFields:g}),this.broadcastQueries(),h=this,[2,new Promise(function(t,n){return nM(h.getObservableFromLink(r,(0,en.pi)((0,en.pi)({},v),{optimisticResponse:a}),i,!1),function(t){if(nO(t)&&"none"===m)throw new tN.cA({graphQLErrors:nA(t)});c&&(c.loading=!1,c.error=null);var n=(0,en.pi)({},t);return"function"==typeof u&&(u=u(n)),"ignore"===m&&nO(n)&&delete n.errors,h.markMutationResult({mutationId:e,result:n,document:r,variables:i,fetchPolicy:p,errorPolicy:m,context:v,update:f,updateQueries:o,awaitRefetchQueries:l,refetchQueries:u,removeOptimistic:a?e:void 0,onQueryUpdated:d,keepRootFields:g})}).subscribe({next:function(e){h.broadcastQueries(),"hasNext"in e&&!1!==e.hasNext||t(e)},error:function(t){c&&(c.loading=!1,c.error=t),a&&h.cache.removeOptimistic(e),h.broadcastQueries(),n(t instanceof tN.cA?t:new tN.cA({networkError:t}))}})})]}})})},e.prototype.markMutationResult=function(e,t){var n=this;void 0===t&&(t=this.cache);var r=e.result,i=[],a="no-cache"===e.fetchPolicy;if(!a&&r7(r,e.errorPolicy)){if(tU(r)||i.push({result:r.data,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}),tU(r)&&(0,tP.O)(r.incremental)){var o=t.diff({id:"ROOT_MUTATION",query:this.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0}),s=void 0;o.result&&(s=tG(o.result,r)),void 0!==s&&(r.data=s,i.push({result:s,dataId:"ROOT_MUTATION",query:e.document,variables:e.variables}))}var u=e.updateQueries;u&&this.queries.forEach(function(e,a){var o=e.observableQuery,s=o&&o.queryName;if(s&&ie.call(u,s)){var c,l=u[s],f=n.queries.get(a),d=f.document,h=f.variables,p=t.diff({query:d,variables:h,returnPartialData:!0,optimistic:!1}),b=p.result;if(p.complete&&b){var m=l(b,{mutationResult:r,queryName:d&&e3(d)||void 0,queryVariables:h});m&&i.push({result:m,dataId:"ROOT_QUERY",query:d,variables:h})}}})}if(i.length>0||e.refetchQueries||e.update||e.onQueryUpdated||e.removeOptimistic){var c=[];if(this.refetchQueries({updateCache:function(t){a||i.forEach(function(e){return t.write(e)});var o=e.update,s=!t$(r)||tU(r)&&!r.hasNext;if(o){if(!a){var u=t.diff({id:"ROOT_MUTATION",query:n.transform(e.document).asQuery,variables:e.variables,optimistic:!1,returnPartialData:!0});u.complete&&("incremental"in(r=(0,en.pi)((0,en.pi)({},r),{data:u.result}))&&delete r.incremental,"hasNext"in r&&delete r.hasNext)}s&&o(t,r,{context:e.context,variables:e.variables})}a||e.keepRootFields||!s||t.modify({id:"ROOT_MUTATION",fields:function(e,t){var n=t.fieldName,r=t.DELETE;return"__typename"===n?e:r}})},include:e.refetchQueries,optimistic:!1,removeOptimistic:e.removeOptimistic,onQueryUpdated:e.onQueryUpdated||null}).forEach(function(e){return c.push(e)}),e.awaitRefetchQueries||e.onQueryUpdated)return Promise.all(c).then(function(){return r})}return Promise.resolve(r)},e.prototype.markMutationOptimistic=function(e,t){var n=this,r="function"==typeof e?e(t.variables):e;return this.cache.recordOptimisticTransaction(function(e){try{n.markMutationResult((0,en.pi)((0,en.pi)({},t),{result:{data:r}}),e)}catch(i){__DEV__&&Q.kG.error(i)}},t.mutationId)},e.prototype.fetchQuery=function(e,t,n){return this.fetchQueryObservable(e,t,n).promise},e.prototype.getQueryStore=function(){var e=Object.create(null);return this.queries.forEach(function(t,n){e[n]={variables:t.variables,networkStatus:t.networkStatus,networkError:t.networkError,graphQLErrors:t.graphQLErrors}}),e},e.prototype.resetErrors=function(e){var t=this.queries.get(e);t&&(t.networkError=void 0,t.graphQLErrors=[])},e.prototype.transform=function(e){var t=this.transformCache;if(!t.has(e)){var n=this.cache.transformDocument(e),r=nY(n),i=this.localState.clientQuery(n),a=r&&this.localState.serverQuery(r),o={document:n,hasClientExports:tm(n),hasForcedResolvers:this.localState.shouldForceResolvers(n),clientQuery:i,serverQuery:a,defaultVars:e8(e2(n)),asQuery:(0,en.pi)((0,en.pi)({},n),{definitions:n.definitions.map(function(e){return"OperationDefinition"===e.kind&&"query"!==e.operation?(0,en.pi)((0,en.pi)({},e),{operation:"query"}):e})})},s=function(e){e&&!t.has(e)&&t.set(e,o)};s(e),s(n),s(i),s(a)}return t.get(e)},e.prototype.getVariables=function(e,t){return(0,en.pi)((0,en.pi)({},this.transform(e).defaultVars),t)},e.prototype.watchQuery=function(e){void 0===(e=(0,en.pi)((0,en.pi)({},e),{variables:this.getVariables(e.query,e.variables)})).notifyOnNetworkStatusChange&&(e.notifyOnNetworkStatusChange=!1);var t=new r8(this),n=new n3({queryManager:this,queryInfo:t,options:e});return this.queries.set(n.queryId,t),t.init({document:n.query,observableQuery:n,variables:n.variables}),n},e.prototype.query=function(e,t){var n=this;return void 0===t&&(t=this.generateQueryId()),__DEV__?(0,Q.kG)(e.query,"query option is required. You must specify your GraphQL document in the query option."):(0,Q.kG)(e.query,17),__DEV__?(0,Q.kG)("Document"===e.query.kind,'You must wrap the query string in a "gql" tag.'):(0,Q.kG)("Document"===e.query.kind,18),__DEV__?(0,Q.kG)(!e.returnPartialData,"returnPartialData option only supported on watchQuery."):(0,Q.kG)(!e.returnPartialData,19),__DEV__?(0,Q.kG)(!e.pollInterval,"pollInterval option only supported on watchQuery."):(0,Q.kG)(!e.pollInterval,20),this.fetchQuery(t,e).finally(function(){return n.stopQuery(t)})},e.prototype.generateQueryId=function(){return String(this.queryIdCounter++)},e.prototype.generateRequestId=function(){return this.requestIdCounter++},e.prototype.generateMutationId=function(){return String(this.mutationIdCounter++)},e.prototype.stopQueryInStore=function(e){this.stopQueryInStoreNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryInStoreNoBroadcast=function(e){var t=this.queries.get(e);t&&t.stop()},e.prototype.clearStore=function(e){return void 0===e&&(e={discardWatches:!0}),this.cancelPendingFetches(__DEV__?new Q.ej("Store reset while query was in flight (not completed in link chain)"):new Q.ej(21)),this.queries.forEach(function(e){e.observableQuery?e.networkStatus=nZ.I.loading:e.stop()}),this.mutationStore&&(this.mutationStore=Object.create(null)),this.cache.reset(e)},e.prototype.getObservableQueries=function(e){var t=this;void 0===e&&(e="active");var n=new Map,r=new Map,i=new Set;return Array.isArray(e)&&e.forEach(function(e){"string"==typeof e?r.set(e,!1):eN(e)?r.set(t.transform(e).document,!1):(0,eO.s)(e)&&e.query&&i.add(e)}),this.queries.forEach(function(t,i){var a=t.observableQuery,o=t.document;if(a){if("all"===e){n.set(i,a);return}var s=a.queryName;if("standby"===a.options.fetchPolicy||"active"===e&&!a.hasObservers())return;("active"===e||s&&r.has(s)||o&&r.has(o))&&(n.set(i,a),s&&r.set(s,!0),o&&r.set(o,!0))}}),i.size&&i.forEach(function(e){var r=nG("legacyOneTimeQuery"),i=t.getQuery(r).init({document:e.query,variables:e.variables}),a=new n3({queryManager:t,queryInfo:i,options:(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"network-only"})});(0,Q.kG)(a.queryId===r),i.setObservableQuery(a),n.set(r,a)}),__DEV__&&r.size&&r.forEach(function(e,t){!e&&__DEV__&&Q.kG.warn("Unknown query ".concat("string"==typeof t?"named ":"").concat(JSON.stringify(t,null,2)," requested in refetchQueries options.include array"))}),n},e.prototype.reFetchObservableQueries=function(e){var t=this;void 0===e&&(e=!1);var n=[];return this.getObservableQueries(e?"all":"active").forEach(function(r,i){var a=r.options.fetchPolicy;r.resetLastResults(),(e||"standby"!==a&&"cache-only"!==a)&&n.push(r.refetch()),t.getQuery(i).setDiff(null)}),this.broadcastQueries(),Promise.all(n)},e.prototype.setObservableQuery=function(e){this.getQuery(e.queryId).setObservableQuery(e)},e.prototype.startGraphQLSubscription=function(e){var t=this,n=e.query,r=e.fetchPolicy,i=e.errorPolicy,a=e.variables,o=e.context,s=void 0===o?{}:o;n=this.transform(n).document,a=this.getVariables(n,a);var u=function(e){return t.getObservableFromLink(n,s,e).map(function(a){"no-cache"!==r&&(r7(a,i)&&t.cache.write({query:n,result:a.data,dataId:"ROOT_SUBSCRIPTION",variables:e}),t.broadcastQueries());var o=nO(a),s=(0,tN.ls)(a);if(o||s){var u={};throw o&&(u.graphQLErrors=a.errors),s&&(u.protocolErrors=a.extensions[tN.YG]),new tN.cA(u)}return a})};if(this.transform(n).hasClientExports){var c=this.localState.addExportedVariables(n,a,s).then(u);return new eT(function(e){var t=null;return c.then(function(n){return t=n.subscribe(e)},e.error),function(){return t&&t.unsubscribe()}})}return u(a)},e.prototype.stopQuery=function(e){this.stopQueryNoBroadcast(e),this.broadcastQueries()},e.prototype.stopQueryNoBroadcast=function(e){this.stopQueryInStoreNoBroadcast(e),this.removeQuery(e)},e.prototype.removeQuery=function(e){this.fetchCancelFns.delete(e),this.queries.has(e)&&(this.getQuery(e).stop(),this.queries.delete(e))},e.prototype.broadcastQueries=function(){this.onBroadcast&&this.onBroadcast(),this.queries.forEach(function(e){return e.notify()})},e.prototype.getLocalState=function(){return this.localState},e.prototype.getObservableFromLink=function(e,t,n,r){var i,a,o=this;void 0===r&&(r=null!==(i=null==t?void 0:t.queryDeduplication)&&void 0!==i?i:this.queryDeduplication);var s=this.transform(e).serverQuery;if(s){var u=this,c=u.inFlightLinkObservables,l=u.link,f={query:s,variables:n,operationName:e3(s)||void 0,context:this.prepareContext((0,en.pi)((0,en.pi)({},t),{forceFetch:!r}))};if(t=f.context,r){var d=c.get(s)||new Map;c.set(s,d);var h=nx(n);if(!(a=d.get(h))){var p=new nq([np(l,f)]);d.set(h,a=p),p.beforeNext(function(){d.delete(h)&&d.size<1&&c.delete(s)})}}else a=new nq([np(l,f)])}else a=new nq([eT.of({data:{}})]),t=this.prepareContext(t);var b=this.transform(e).clientQuery;return b&&(a=nM(a,function(e){return o.localState.runResolvers({document:b,remoteResult:e,context:t,variables:n})})),a},e.prototype.getResultsFromLink=function(e,t,n){var r=e.lastRequestId=this.generateRequestId(),i=this.cache.transformForLink(this.transform(e.document).document);return nM(this.getObservableFromLink(i,n.context,n.variables),function(a){var o=nA(a),s=o.length>0;if(r>=e.lastRequestId){if(s&&"none"===n.errorPolicy)throw e.markError(new tN.cA({graphQLErrors:o}));e.markResult(a,i,n,t),e.markReady()}var u={data:a.data,loading:!1,networkStatus:nZ.I.ready};return s&&"ignore"!==n.errorPolicy&&(u.errors=o,u.networkStatus=nZ.I.error),u},function(t){var n=(0,tN.MS)(t)?t:new tN.cA({networkError:t});throw r>=e.lastRequestId&&e.markError(n),n})},e.prototype.fetchQueryObservable=function(e,t,n){return this.fetchConcastWithInfo(e,t,n).concast},e.prototype.fetchConcastWithInfo=function(e,t,n){var r,i,a=this;void 0===n&&(n=nZ.I.loading);var o=this.transform(t.query).document,s=this.getVariables(o,t.variables),u=this.getQuery(e),c=this.defaultOptions.watchQuery,l=t.fetchPolicy,f=void 0===l?c&&c.fetchPolicy||"cache-first":l,d=t.errorPolicy,h=void 0===d?c&&c.errorPolicy||"none":d,p=t.returnPartialData,b=void 0!==p&&p,m=t.notifyOnNetworkStatusChange,g=void 0!==m&&m,v=t.context,y=void 0===v?{}:v,w=Object.assign({},t,{query:o,variables:s,fetchPolicy:f,errorPolicy:h,returnPartialData:b,notifyOnNetworkStatusChange:g,context:y}),_=function(e){w.variables=e;var r=a.fetchQueryByPolicy(u,w,n);return"standby"!==w.fetchPolicy&&r.sources.length>0&&u.observableQuery&&u.observableQuery.applyNextFetchPolicy("after-fetch",t),r},E=function(){return a.fetchCancelFns.delete(e)};if(this.fetchCancelFns.set(e,function(e){E(),setTimeout(function(){return r.cancel(e)})}),this.transform(w.query).hasClientExports)r=new nq(this.localState.addExportedVariables(w.query,w.variables,w.context).then(_).then(function(e){return e.sources})),i=!0;else{var S=_(w.variables);i=S.fromLink,r=new nq(S.sources)}return r.promise.then(E,E),{concast:r,fromLink:i}},e.prototype.refetchQueries=function(e){var t=this,n=e.updateCache,r=e.include,i=e.optimistic,a=void 0!==i&&i,o=e.removeOptimistic,s=void 0===o?a?nG("refetchQueries"):void 0:o,u=e.onQueryUpdated,c=new Map;r&&this.getObservableQueries(r).forEach(function(e,n){c.set(n,{oq:e,lastDiff:t.getQuery(n).getDiff()})});var l=new Map;return n&&this.cache.batch({update:n,optimistic:a&&s||!1,removeOptimistic:s,onWatchUpdated:function(e,t,n){var r=e.watcher instanceof r8&&e.watcher.observableQuery;if(r){if(u){c.delete(r.queryId);var i=u(r,t,n);return!0===i&&(i=r.refetch()),!1!==i&&l.set(r,i),i}null!==u&&c.set(r.queryId,{oq:r,lastDiff:n,diff:t})}}}),c.size&&c.forEach(function(e,n){var r,i=e.oq,a=e.lastDiff,o=e.diff;if(u){if(!o){var s=i.queryInfo;s.reset(),o=s.getDiff()}r=u(i,o,a)}u&&!0!==r||(r=i.refetch()),!1!==r&&l.set(i,r),n.indexOf("legacyOneTimeQuery")>=0&&t.stopQueryNoBroadcast(n)}),s&&this.cache.removeOptimistic(s),l},e.prototype.fetchQueryByPolicy=function(e,t,n){var r=this,i=t.query,a=t.variables,o=t.fetchPolicy,s=t.refetchWritePolicy,u=t.errorPolicy,c=t.returnPartialData,l=t.context,f=t.notifyOnNetworkStatusChange,d=e.networkStatus;e.init({document:this.transform(i).document,variables:a,networkStatus:n});var h=function(){return e.getDiff(a)},p=function(t,n){void 0===n&&(n=e.networkStatus||nZ.I.loading);var o=t.result;!__DEV__||c||(0,nm.D)(o,{})||n6(t.missing);var s=function(e){return eT.of((0,en.pi)({data:e,loading:(0,nZ.O)(n),networkStatus:n},t.complete?null:{partial:!0}))};return o&&r.transform(i).hasForcedResolvers?r.localState.runResolvers({document:i,remoteResult:{data:o},context:l,variables:a,onlyRunForcedResolvers:!0}).then(function(e){return s(e.data||void 0)}):"none"===u&&n===nZ.I.refetch&&Array.isArray(t.missing)?s(void 0):s(o)},b="no-cache"===o?0:n===nZ.I.refetch&&"merge"!==s?1:2,m=function(){return r.getResultsFromLink(e,b,{variables:a,context:l,fetchPolicy:o,errorPolicy:u})},g=f&&"number"==typeof d&&d!==n&&(0,nZ.O)(n);switch(o){default:case"cache-first":var v=h();if(v.complete)return{fromLink:!1,sources:[p(v,e.markReady())]};if(c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-and-network":var v=h();if(v.complete||c||g)return{fromLink:!0,sources:[p(v),m()]};return{fromLink:!0,sources:[m()]};case"cache-only":return{fromLink:!1,sources:[p(h(),e.markReady())]};case"network-only":if(g)return{fromLink:!0,sources:[p(h()),m()]};return{fromLink:!0,sources:[m()]};case"no-cache":if(g)return{fromLink:!0,sources:[p(e.getDiff()),m(),]};return{fromLink:!0,sources:[m()]};case"standby":return{fromLink:!1,sources:[]}}},e.prototype.getQuery=function(e){return e&&!this.queries.has(e)&&this.queries.set(e,new r8(this,e)),this.queries.get(e)},e.prototype.prepareContext=function(e){void 0===e&&(e={});var t=this.localState.prepareContext(e);return(0,en.pi)((0,en.pi)({},t),{clientAwareness:this.clientAwareness})},e}(),ir=__webpack_require__(14012),ii=!1,ia=function(){function e(e){var t=this;this.resetStoreCallbacks=[],this.clearStoreCallbacks=[];var n=e.uri,r=e.credentials,i=e.headers,a=e.cache,o=e.ssrMode,s=void 0!==o&&o,u=e.ssrForceFetchDelay,c=void 0===u?0:u,l=e.connectToDevTools,f=void 0===l?"object"==typeof window&&!window.__APOLLO_CLIENT__&&__DEV__:l,d=e.queryDeduplication,h=void 0===d||d,p=e.defaultOptions,b=e.assumeImmutableResults,m=void 0!==b&&b,g=e.resolvers,v=e.typeDefs,y=e.fragmentMatcher,w=e.name,_=e.version,E=e.link;if(E||(E=n?new nh({uri:n,credentials:r,headers:i}):ta.empty()),!a)throw __DEV__?new Q.ej("To initialize Apollo Client, you must specify a 'cache' property in the options object. \nFor more information, please visit: https://go.apollo.dev/c/docs"):new Q.ej(9);if(this.link=E,this.cache=a,this.disableNetworkFetches=s||c>0,this.queryDeduplication=h,this.defaultOptions=p||Object.create(null),this.typeDefs=v,c&&setTimeout(function(){return t.disableNetworkFetches=!1},c),this.watchQuery=this.watchQuery.bind(this),this.query=this.query.bind(this),this.mutate=this.mutate.bind(this),this.resetStore=this.resetStore.bind(this),this.reFetchObservableQueries=this.reFetchObservableQueries.bind(this),f&&"object"==typeof window&&(window.__APOLLO_CLIENT__=this),!ii&&f&&__DEV__&&(ii=!0,"undefined"!=typeof window&&window.document&&window.top===window.self&&!window.__APOLLO_DEVTOOLS_GLOBAL_HOOK__)){var S=window.navigator,k=S&&S.userAgent,x=void 0;"string"==typeof k&&(k.indexOf("Chrome/")>-1?x="https://chrome.google.com/webstore/detail/apollo-client-developer-t/jdkknkkbebbapilgoeccciglkfbmbnfm":k.indexOf("Firefox/")>-1&&(x="https://addons.mozilla.org/en-US/firefox/addon/apollo-developer-tools/")),x&&__DEV__&&Q.kG.log("Download the Apollo DevTools for a better development experience: "+x)}this.version=nb,this.localState=new r4({cache:a,client:this,resolvers:g,fragmentMatcher:y}),this.queryManager=new it({cache:this.cache,link:this.link,defaultOptions:this.defaultOptions,queryDeduplication:h,ssrMode:s,clientAwareness:{name:w,version:_},localState:this.localState,assumeImmutableResults:m,onBroadcast:f?function(){t.devToolsHookCb&&t.devToolsHookCb({action:{},state:{queries:t.queryManager.getQueryStore(),mutations:t.queryManager.mutationStore||{}},dataWithOptimisticResults:t.cache.extract(!0)})}:void 0})}return e.prototype.stop=function(){this.queryManager.stop()},e.prototype.watchQuery=function(e){return this.defaultOptions.watchQuery&&(e=(0,ir.J)(this.defaultOptions.watchQuery,e)),this.disableNetworkFetches&&("network-only"===e.fetchPolicy||"cache-and-network"===e.fetchPolicy)&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.watchQuery(e)},e.prototype.query=function(e){return this.defaultOptions.query&&(e=(0,ir.J)(this.defaultOptions.query,e)),__DEV__?(0,Q.kG)("cache-and-network"!==e.fetchPolicy,"The cache-and-network fetchPolicy does not work with client.query, because client.query can only return a single result. Please use client.watchQuery to receive multiple results from the cache and the network, or consider using a different fetchPolicy, such as cache-first or network-only."):(0,Q.kG)("cache-and-network"!==e.fetchPolicy,10),this.disableNetworkFetches&&"network-only"===e.fetchPolicy&&(e=(0,en.pi)((0,en.pi)({},e),{fetchPolicy:"cache-first"})),this.queryManager.query(e)},e.prototype.mutate=function(e){return this.defaultOptions.mutate&&(e=(0,ir.J)(this.defaultOptions.mutate,e)),this.queryManager.mutate(e)},e.prototype.subscribe=function(e){return this.queryManager.startGraphQLSubscription(e)},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!1),this.cache.readQuery(e,t)},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!1),this.cache.readFragment(e,t)},e.prototype.writeQuery=function(e){var t=this.cache.writeQuery(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.writeFragment=function(e){var t=this.cache.writeFragment(e);return!1!==e.broadcast&&this.queryManager.broadcastQueries(),t},e.prototype.__actionHookForDevTools=function(e){this.devToolsHookCb=e},e.prototype.__requestRaw=function(e){return np(this.link,e)},e.prototype.resetStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!1})}).then(function(){return Promise.all(e.resetStoreCallbacks.map(function(e){return e()}))}).then(function(){return e.reFetchObservableQueries()})},e.prototype.clearStore=function(){var e=this;return Promise.resolve().then(function(){return e.queryManager.clearStore({discardWatches:!0})}).then(function(){return Promise.all(e.clearStoreCallbacks.map(function(e){return e()}))})},e.prototype.onResetStore=function(e){var t=this;return this.resetStoreCallbacks.push(e),function(){t.resetStoreCallbacks=t.resetStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.onClearStore=function(e){var t=this;return this.clearStoreCallbacks.push(e),function(){t.clearStoreCallbacks=t.clearStoreCallbacks.filter(function(t){return t!==e})}},e.prototype.reFetchObservableQueries=function(e){return this.queryManager.reFetchObservableQueries(e)},e.prototype.refetchQueries=function(e){var t=this.queryManager.refetchQueries(e),n=[],r=[];t.forEach(function(e,t){n.push(t),r.push(e)});var i=Promise.all(r);return i.queries=n,i.results=r,i.catch(function(e){__DEV__&&Q.kG.debug("In client.refetchQueries, Promise.all promise rejected with error ".concat(e))}),i},e.prototype.getObservableQueries=function(e){return void 0===e&&(e="active"),this.queryManager.getObservableQueries(e)},e.prototype.extract=function(e){return this.cache.extract(e)},e.prototype.restore=function(e){return this.cache.restore(e)},e.prototype.addResolvers=function(e){this.localState.addResolvers(e)},e.prototype.setResolvers=function(e){this.localState.setResolvers(e)},e.prototype.getResolvers=function(){return this.localState.getResolvers()},e.prototype.setLocalStateFragmentMatcher=function(e){this.localState.setFragmentMatcher(e)},e.prototype.setLink=function(e){this.link=this.queryManager.link=e},e}(),io=function(){function e(){this.getFragmentDoc=rZ(eA)}return e.prototype.batch=function(e){var t,n=this,r="string"==typeof e.optimistic?e.optimistic:!1===e.optimistic?null:void 0;return this.performTransaction(function(){return t=e.update(n)},r),t},e.prototype.recordOptimisticTransaction=function(e,t){this.performTransaction(e,t)},e.prototype.transformDocument=function(e){return e},e.prototype.transformForLink=function(e){return e},e.prototype.identify=function(e){},e.prototype.gc=function(){return[]},e.prototype.modify=function(e){return!1},e.prototype.readQuery=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{rootId:e.id||"ROOT_QUERY",optimistic:t}))},e.prototype.readFragment=function(e,t){return void 0===t&&(t=!!e.optimistic),this.read((0,en.pi)((0,en.pi)({},e),{query:this.getFragmentDoc(e.fragment,e.fragmentName),rootId:e.id,optimistic:t}))},e.prototype.writeQuery=function(e){var t=e.id,n=e.data,r=(0,en._T)(e,["id","data"]);return this.write(Object.assign(r,{dataId:t||"ROOT_QUERY",result:n}))},e.prototype.writeFragment=function(e){var t=e.id,n=e.data,r=e.fragment,i=e.fragmentName,a=(0,en._T)(e,["id","data","fragment","fragmentName"]);return this.write(Object.assign(a,{query:this.getFragmentDoc(r,i),dataId:t,result:n}))},e.prototype.updateQuery=function(e,t){return this.batch({update:function(n){var r=n.readQuery(e),i=t(r);return null==i?r:(n.writeQuery((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e.prototype.updateFragment=function(e,t){return this.batch({update:function(n){var r=n.readFragment(e),i=t(r);return null==i?r:(n.writeFragment((0,en.pi)((0,en.pi)({},e),{data:i})),i)}})},e}(),is=function(e){function t(n,r,i,a){var o,s=e.call(this,n)||this;if(s.message=n,s.path=r,s.query=i,s.variables=a,Array.isArray(s.path)){s.missing=s.message;for(var u=s.path.length-1;u>=0;--u)s.missing=((o={})[s.path[u]]=s.missing,o)}else s.missing=s.path;return s.__proto__=t.prototype,s}return(0,en.ZT)(t,e),t}(Error),iu=__webpack_require__(10542),ic=Object.prototype.hasOwnProperty;function il(e){return null==e}function id(e,t){var n=e.__typename,r=e.id,i=e._id;if("string"==typeof n&&(t&&(t.keyObject=il(r)?il(i)?void 0:{_id:i}:{id:r}),il(r)&&!il(i)&&(r=i),!il(r)))return"".concat(n,":").concat("number"==typeof r||"string"==typeof r?r:JSON.stringify(r))}var ih={dataIdFromObject:id,addTypename:!0,resultCaching:!0,canonizeResults:!1};function ip(e){return(0,n1.o)(ih,e)}function ib(e){var t=e.canonizeResults;return void 0===t?ih.canonizeResults:t}function im(e,t){return eD(t)?e.get(t.__ref,"__typename"):t&&t.__typename}var ig=/^[_a-z][_0-9a-z]*/i;function iv(e){var t=e.match(ig);return t?t[0]:e}function iy(e,t,n){return!!(0,eO.s)(t)&&((0,tP.k)(t)?t.every(function(t){return iy(e,t,n)}):e.selections.every(function(e){if(eQ(e)&&td(e,n)){var r=eX(e);return ic.call(t,r)&&(!e.selectionSet||iy(e.selectionSet,t[r],n))}return!0}))}function iw(e){return(0,eO.s)(e)&&!eD(e)&&!(0,tP.k)(e)}function i_(){return new tB}function iE(e,t){var n=eL(e4(e));return{fragmentMap:n,lookupFragment:function(e){var r=n[e];return!r&&t&&(r=t.lookup(e)),r||null}}}var iS=Object.create(null),ik=function(){return iS},ix=Object.create(null),iT=function(){function e(e,t){var n=this;this.policies=e,this.group=t,this.data=Object.create(null),this.rootIds=Object.create(null),this.refs=Object.create(null),this.getFieldValue=function(e,t){return(0,iu.J)(eD(e)?n.get(e.__ref,t):e&&e[t])},this.canRead=function(e){return eD(e)?n.has(e.__ref):"object"==typeof e},this.toReference=function(e,t){if("string"==typeof e)return eI(e);if(eD(e))return e;var r=n.policies.identify(e)[0];if(r){var i=eI(r);return t&&n.merge(r,e),i}}}return e.prototype.toObject=function(){return(0,en.pi)({},this.data)},e.prototype.has=function(e){return void 0!==this.lookup(e,!0)},e.prototype.get=function(e,t){if(this.group.depend(e,t),ic.call(this.data,e)){var n=this.data[e];if(n&&ic.call(n,t))return n[t]}return"__typename"===t&&ic.call(this.policies.rootTypenamesById,e)?this.policies.rootTypenamesById[e]:this instanceof iL?this.parent.get(e,t):void 0},e.prototype.lookup=function(e,t){return(t&&this.group.depend(e,"__exists"),ic.call(this.data,e))?this.data[e]:this instanceof iL?this.parent.lookup(e,t):this.policies.rootTypenamesById[e]?Object.create(null):void 0},e.prototype.merge=function(e,t){var n,r=this;eD(e)&&(e=e.__ref),eD(t)&&(t=t.__ref);var i="string"==typeof e?this.lookup(n=e):e,a="string"==typeof t?this.lookup(n=t):t;if(a){__DEV__?(0,Q.kG)("string"==typeof n,"store.merge expects a string ID"):(0,Q.kG)("string"==typeof n,1);var o=new tB(iI).merge(i,a);if(this.data[n]=o,o!==i&&(delete this.refs[n],this.group.caching)){var s=Object.create(null);i||(s.__exists=1),Object.keys(a).forEach(function(e){if(!i||i[e]!==o[e]){s[e]=1;var t=iv(e);t===e||r.policies.hasKeyArgs(o.__typename,t)||(s[t]=1),void 0!==o[e]||r instanceof iL||delete o[e]}}),s.__typename&&!(i&&i.__typename)&&this.policies.rootTypenamesById[n]===o.__typename&&delete s.__typename,Object.keys(s).forEach(function(e){return r.group.dirty(n,e)})}}},e.prototype.modify=function(e,t){var n=this,r=this.lookup(e);if(r){var i=Object.create(null),a=!1,o=!0,s={DELETE:iS,INVALIDATE:ix,isReference:eD,toReference:this.toReference,canRead:this.canRead,readField:function(t,r){return n.policies.readField("string"==typeof t?{fieldName:t,from:r||eI(e)}:t,{store:n})}};if(Object.keys(r).forEach(function(u){var c=iv(u),l=r[u];if(void 0!==l){var f="function"==typeof t?t:t[u]||t[c];if(f){var d=f===ik?iS:f((0,iu.J)(l),(0,en.pi)((0,en.pi)({},s),{fieldName:c,storeFieldName:u,storage:n.getStorage(e,u)}));d===ix?n.group.dirty(e,u):(d===iS&&(d=void 0),d!==l&&(i[u]=d,a=!0,l=d))}void 0!==l&&(o=!1)}}),a)return this.merge(e,i),o&&(this instanceof iL?this.data[e]=void 0:delete this.data[e],this.group.dirty(e,"__exists")),!0}return!1},e.prototype.delete=function(e,t,n){var r,i=this.lookup(e);if(i){var a=this.getFieldValue(i,"__typename"),o=t&&n?this.policies.getStoreFieldName({typename:a,fieldName:t,args:n}):t;return this.modify(e,o?((r={})[o]=ik,r):ik)}return!1},e.prototype.evict=function(e,t){var n=!1;return e.id&&(ic.call(this.data,e.id)&&(n=this.delete(e.id,e.fieldName,e.args)),this instanceof iL&&this!==t&&(n=this.parent.evict(e,t)||n),(e.fieldName||n)&&this.group.dirty(e.id,e.fieldName||"__exists")),n},e.prototype.clear=function(){this.replace(null)},e.prototype.extract=function(){var e=this,t=this.toObject(),n=[];return this.getRootIdSet().forEach(function(t){ic.call(e.policies.rootTypenamesById,t)||n.push(t)}),n.length&&(t.__META={extraRootIds:n.sort()}),t},e.prototype.replace=function(e){var t=this;if(Object.keys(this.data).forEach(function(n){e&&ic.call(e,n)||t.delete(n)}),e){var n=e.__META,r=(0,en._T)(e,["__META"]);Object.keys(r).forEach(function(e){t.merge(e,r[e])}),n&&n.extraRootIds.forEach(this.retain,this)}},e.prototype.retain=function(e){return this.rootIds[e]=(this.rootIds[e]||0)+1},e.prototype.release=function(e){if(this.rootIds[e]>0){var t=--this.rootIds[e];return t||delete this.rootIds[e],t}return 0},e.prototype.getRootIdSet=function(e){return void 0===e&&(e=new Set),Object.keys(this.rootIds).forEach(e.add,e),this instanceof iL?this.parent.getRootIdSet(e):Object.keys(this.policies.rootTypenamesById).forEach(e.add,e),e},e.prototype.gc=function(){var e=this,t=this.getRootIdSet(),n=this.toObject();t.forEach(function(r){ic.call(n,r)&&(Object.keys(e.findChildRefIds(r)).forEach(t.add,t),delete n[r])});var r=Object.keys(n);if(r.length){for(var i=this;i instanceof iL;)i=i.parent;r.forEach(function(e){return i.delete(e)})}return r},e.prototype.findChildRefIds=function(e){if(!ic.call(this.refs,e)){var t=this.refs[e]=Object.create(null),n=this.data[e];if(!n)return t;var r=new Set([n]);r.forEach(function(e){eD(e)&&(t[e.__ref]=!0),(0,eO.s)(e)&&Object.keys(e).forEach(function(t){var n=e[t];(0,eO.s)(n)&&r.add(n)})})}return this.refs[e]},e.prototype.makeCacheKey=function(){return this.group.keyMaker.lookupArray(arguments)},e}(),iM=function(){function e(e,t){void 0===t&&(t=null),this.caching=e,this.parent=t,this.d=null,this.resetCaching()}return e.prototype.resetCaching=function(){this.d=this.caching?rW():null,this.keyMaker=new n_(t_.mr)},e.prototype.depend=function(e,t){if(this.d){this.d(iO(e,t));var n=iv(t);n!==t&&this.d(iO(e,n)),this.parent&&this.parent.depend(e,t)}},e.prototype.dirty=function(e,t){this.d&&this.d.dirty(iO(e,t),"__exists"===t?"forget":"setDirty")},e}();function iO(e,t){return t+"#"+e}function iA(e,t){iD(e)&&e.group.depend(t,"__exists")}!function(e){var t=function(e){function t(t){var n=t.policies,r=t.resultCaching,i=void 0===r||r,a=t.seed,o=e.call(this,n,new iM(i))||this;return o.stump=new iC(o),o.storageTrie=new n_(t_.mr),a&&o.replace(a),o}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,t){return this.stump.addLayer(e,t)},t.prototype.removeLayer=function(){return this},t.prototype.getStorage=function(){return this.storageTrie.lookupArray(arguments)},t}(e);e.Root=t}(iT||(iT={}));var iL=function(e){function t(t,n,r,i){var a=e.call(this,n.policies,i)||this;return a.id=t,a.parent=n,a.replay=r,a.group=i,r(a),a}return(0,en.ZT)(t,e),t.prototype.addLayer=function(e,n){return new t(e,this,n,this.group)},t.prototype.removeLayer=function(e){var t=this,n=this.parent.removeLayer(e);return e===this.id?(this.group.caching&&Object.keys(this.data).forEach(function(e){var r=t.data[e],i=n.lookup(e);i?r?r!==i&&Object.keys(r).forEach(function(n){(0,nm.D)(r[n],i[n])||t.group.dirty(e,n)}):(t.group.dirty(e,"__exists"),Object.keys(i).forEach(function(n){t.group.dirty(e,n)})):t.delete(e)}),n):n===this.parent?this:n.addLayer(this.id,this.replay)},t.prototype.toObject=function(){return(0,en.pi)((0,en.pi)({},this.parent.toObject()),this.data)},t.prototype.findChildRefIds=function(t){var n=this.parent.findChildRefIds(t);return ic.call(this.data,t)?(0,en.pi)((0,en.pi)({},n),e.prototype.findChildRefIds.call(this,t)):n},t.prototype.getStorage=function(){for(var e=this.parent;e.parent;)e=e.parent;return e.getStorage.apply(e,arguments)},t}(iT),iC=function(e){function t(t){return e.call(this,"EntityStore.Stump",t,function(){},new iM(t.group.caching,t.group))||this}return(0,en.ZT)(t,e),t.prototype.removeLayer=function(){return this},t.prototype.merge=function(){return this.parent.merge.apply(this.parent,arguments)},t}(iL);function iI(e,t,n){var r=e[n],i=t[n];return(0,nm.D)(r,i)?r:i}function iD(e){return!!(e instanceof iT&&e.group.caching)}function iN(e){return[e.selectionSet,e.objectOrReference,e.context,e.context.canonizeResults,]}var iP=function(){function e(e){var t=this;this.knownResults=new(t_.mr?WeakMap:Map),this.config=(0,n1.o)(e,{addTypename:!1!==e.addTypename,canonizeResults:ib(e)}),this.canon=e.canon||new nk,this.executeSelectionSet=rZ(function(e){var n,r=e.context.canonizeResults,i=iN(e);i[3]=!r;var a=(n=t.executeSelectionSet).peek.apply(n,i);return a?r?(0,en.pi)((0,en.pi)({},a),{result:t.canon.admit(a.result)}):a:(iA(e.context.store,e.enclosingRef.__ref),t.execSelectionSetImpl(e))},{max:this.config.resultCacheMaxSize,keyArgs:iN,makeCacheKey:function(e,t,n,r){if(iD(n.store))return n.store.makeCacheKey(e,eD(t)?t.__ref:t,n.varString,r)}}),this.executeSubSelectedArray=rZ(function(e){return iA(e.context.store,e.enclosingRef.__ref),t.execSubSelectedArrayImpl(e)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var t=e.field,n=e.array,r=e.context;if(iD(r.store))return r.store.makeCacheKey(t,n,r.varString)}})}return e.prototype.resetCanon=function(){this.canon=new nk},e.prototype.diffQueryAgainstStore=function(e){var t,n=e.store,r=e.query,i=e.rootId,a=void 0===i?"ROOT_QUERY":i,o=e.variables,s=e.returnPartialData,u=void 0===s||s,c=e.canonizeResults,l=void 0===c?this.config.canonizeResults:c,f=this.config.cache.policies;o=(0,en.pi)((0,en.pi)({},e8(e5(r))),o);var d=eI(a),h=this.executeSelectionSet({selectionSet:e9(r).selectionSet,objectOrReference:d,enclosingRef:d,context:(0,en.pi)({store:n,query:r,policies:f,variables:o,varString:nx(o),canonizeResults:l},iE(r,this.config.fragments))});if(h.missing&&(t=[new is(iR(h.missing),h.missing,r,o)],!u))throw t[0];return{result:h.result,complete:!t,missing:t}},e.prototype.isFresh=function(e,t,n,r){if(iD(r.store)&&this.knownResults.get(e)===n){var i=this.executeSelectionSet.peek(n,t,r,this.canon.isKnown(e));if(i&&e===i.result)return!0}return!1},e.prototype.execSelectionSetImpl=function(e){var t,n=this,r=e.selectionSet,i=e.objectOrReference,a=e.enclosingRef,o=e.context;if(eD(i)&&!o.policies.rootTypenamesById[i.__ref]&&!o.store.has(i.__ref))return{result:this.canon.empty,missing:"Dangling reference to missing ".concat(i.__ref," object")};var s=o.variables,u=o.policies,c=o.store.getFieldValue(i,"__typename"),l=[],f=new tB;function d(e,n){var r;return e.missing&&(t=f.merge(t,((r={})[n]=e.missing,r))),e.result}this.config.addTypename&&"string"==typeof c&&!u.rootIdsByTypename[c]&&l.push({__typename:c});var h=new Set(r.selections);h.forEach(function(e){var r,p;if(td(e,s)){if(eQ(e)){var b=u.readField({fieldName:e.name.value,field:e,variables:o.variables,from:i},o),m=eX(e);void 0===b?nj.added(e)||(t=f.merge(t,((r={})[m]="Can't find field '".concat(e.name.value,"' on ").concat(eD(i)?i.__ref+" object":"object "+JSON.stringify(i,null,2)),r))):(0,tP.k)(b)?b=d(n.executeSubSelectedArray({field:e,array:b,enclosingRef:a,context:o}),m):e.selectionSet?null!=b&&(b=d(n.executeSelectionSet({selectionSet:e.selectionSet,objectOrReference:b,enclosingRef:eD(b)?b:a,context:o}),m)):o.canonizeResults&&(b=n.canon.pass(b)),void 0!==b&&l.push(((p={})[m]=b,p))}else{var g=eC(e,o.lookupFragment);if(!g&&e.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(e.name.value)):new Q.ej(5);g&&u.fragmentMatches(g,c)&&g.selectionSet.selections.forEach(h.add,h)}}});var p={result:tF(l),missing:t},b=o.canonizeResults?this.canon.admit(p):(0,iu.J)(p);return b.result&&this.knownResults.set(b.result,r),b},e.prototype.execSubSelectedArrayImpl=function(e){var t,n=this,r=e.field,i=e.array,a=e.enclosingRef,o=e.context,s=new tB;function u(e,n){var r;return e.missing&&(t=s.merge(t,((r={})[n]=e.missing,r))),e.result}return r.selectionSet&&(i=i.filter(o.store.canRead)),i=i.map(function(e,t){return null===e?null:(0,tP.k)(e)?u(n.executeSubSelectedArray({field:r,array:e,enclosingRef:a,context:o}),t):r.selectionSet?u(n.executeSelectionSet({selectionSet:r.selectionSet,objectOrReference:e,enclosingRef:eD(e)?e:a,context:o}),t):(__DEV__&&ij(o.store,r,e),e)}),{result:o.canonizeResults?this.canon.admit(i):i,missing:t}},e}();function iR(e){try{JSON.stringify(e,function(e,t){if("string"==typeof t)throw t;return t})}catch(t){return t}}function ij(e,t,n){if(!t.selectionSet){var r=new Set([n]);r.forEach(function(n){(0,eO.s)(n)&&(__DEV__?(0,Q.kG)(!eD(n),"Missing selection set for object of type ".concat(im(e,n)," returned for query field ").concat(t.name.value)):(0,Q.kG)(!eD(n),6),Object.values(n).forEach(r.add,r))})}}function iF(e){var t=nG("stringifyForDisplay");return JSON.stringify(e,function(e,n){return void 0===n?t:n}).split(JSON.stringify(t)).join("")}var iY=Object.create(null);function iB(e){var t=JSON.stringify(e);return iY[t]||(iY[t]=Object.create(null))}function iU(e){var t=iB(e);return t.keyFieldsFn||(t.keyFieldsFn=function(t,n){var r=function(e,t){return n.readField(t,e)},i=n.keyObject=i$(e,function(e){var i=iW(n.storeObject,e,r);return void 0===i&&t!==n.storeObject&&ic.call(t,e[0])&&(i=iW(t,e,iG)),__DEV__?(0,Q.kG)(void 0!==i,"Missing field '".concat(e.join("."),"' while extracting keyFields from ").concat(JSON.stringify(t))):(0,Q.kG)(void 0!==i,2),i});return"".concat(n.typename,":").concat(JSON.stringify(i))})}function iH(e){var t=iB(e);return t.keyArgsFn||(t.keyArgsFn=function(t,n){var r=n.field,i=n.variables,a=n.fieldName,o=JSON.stringify(i$(e,function(e){var n=e[0],a=n.charAt(0);if("@"===a){if(r&&(0,tP.O)(r.directives)){var o=n.slice(1),s=r.directives.find(function(e){return e.name.value===o}),u=s&&eZ(s,i);return u&&iW(u,e.slice(1))}return}if("$"===a){var c=n.slice(1);if(i&&ic.call(i,c)){var l=e.slice(0);return l[0]=c,iW(i,l)}return}if(t)return iW(t,e)}));return(t||"{}"!==o)&&(a+=":"+o),a})}function i$(e,t){var n=new tB;return iz(e).reduce(function(e,r){var i,a=t(r);if(void 0!==a){for(var o=r.length-1;o>=0;--o)a=((i={})[r[o]]=a,i);e=n.merge(e,a)}return e},Object.create(null))}function iz(e){var t=iB(e);if(!t.paths){var n=t.paths=[],r=[];e.forEach(function(t,i){(0,tP.k)(t)?(iz(t).forEach(function(e){return n.push(r.concat(e))}),r.length=0):(r.push(t),(0,tP.k)(e[i+1])||(n.push(r.slice(0)),r.length=0))})}return t.paths}function iG(e,t){return e[t]}function iW(e,t,n){return n=n||iG,iK(t.reduce(function e(t,r){return(0,tP.k)(t)?t.map(function(t){return e(t,r)}):t&&n(t,r)},e))}function iK(e){return(0,eO.s)(e)?(0,tP.k)(e)?e.map(iK):i$(Object.keys(e).sort(),function(t){return iW(e,t)}):e}function iV(e){return void 0!==e.args?e.args:e.field?eZ(e.field,e.variables):null}eK.setStringify(nx);var iq=function(){},iZ=function(e,t){return t.fieldName},iX=function(e,t,n){return(0,n.mergeObjects)(e,t)},iJ=function(e,t){return t},iQ=function(){function e(e){this.config=e,this.typePolicies=Object.create(null),this.toBeAdded=Object.create(null),this.supertypeMap=new Map,this.fuzzySubtypes=new Map,this.rootIdsByTypename=Object.create(null),this.rootTypenamesById=Object.create(null),this.usingPossibleTypes=!1,this.config=(0,en.pi)({dataIdFromObject:id},e),this.cache=this.config.cache,this.setRootTypename("Query"),this.setRootTypename("Mutation"),this.setRootTypename("Subscription"),e.possibleTypes&&this.addPossibleTypes(e.possibleTypes),e.typePolicies&&this.addTypePolicies(e.typePolicies)}return e.prototype.identify=function(e,t){var n,r,i=this,a=t&&(t.typename||(null===(n=t.storeObject)||void 0===n?void 0:n.__typename))||e.__typename;if(a===this.rootTypenamesById.ROOT_QUERY)return["ROOT_QUERY"];for(var o=t&&t.storeObject||e,s=(0,en.pi)((0,en.pi)({},t),{typename:a,storeObject:o,readField:t&&t.readField||function(){var e=i0(arguments,o);return i.readField(e,{store:i.cache.data,variables:e.variables})}}),u=a&&this.getTypePolicy(a),c=u&&u.keyFn||this.config.dataIdFromObject;c;){var l=c((0,en.pi)((0,en.pi)({},e),o),s);if((0,tP.k)(l))c=iU(l);else{r=l;break}}return r=r?String(r):void 0,s.keyObject?[r,s.keyObject]:[r]},e.prototype.addTypePolicies=function(e){var t=this;Object.keys(e).forEach(function(n){var r=e[n],i=r.queryType,a=r.mutationType,o=r.subscriptionType,s=(0,en._T)(r,["queryType","mutationType","subscriptionType"]);i&&t.setRootTypename("Query",n),a&&t.setRootTypename("Mutation",n),o&&t.setRootTypename("Subscription",n),ic.call(t.toBeAdded,n)?t.toBeAdded[n].push(s):t.toBeAdded[n]=[s]})},e.prototype.updateTypePolicy=function(e,t){var n=this,r=this.getTypePolicy(e),i=t.keyFields,a=t.fields;function o(e,t){e.merge="function"==typeof t?t:!0===t?iX:!1===t?iJ:e.merge}o(r,t.merge),r.keyFn=!1===i?iq:(0,tP.k)(i)?iU(i):"function"==typeof i?i:r.keyFn,a&&Object.keys(a).forEach(function(t){var r=n.getFieldPolicy(e,t,!0),i=a[t];if("function"==typeof i)r.read=i;else{var s=i.keyArgs,u=i.read,c=i.merge;r.keyFn=!1===s?iZ:(0,tP.k)(s)?iH(s):"function"==typeof s?s:r.keyFn,"function"==typeof u&&(r.read=u),o(r,c)}r.read&&r.merge&&(r.keyFn=r.keyFn||iZ)})},e.prototype.setRootTypename=function(e,t){void 0===t&&(t=e);var n="ROOT_"+e.toUpperCase(),r=this.rootTypenamesById[n];t!==r&&(__DEV__?(0,Q.kG)(!r||r===e,"Cannot change root ".concat(e," __typename more than once")):(0,Q.kG)(!r||r===e,3),r&&delete this.rootIdsByTypename[r],this.rootIdsByTypename[t]=n,this.rootTypenamesById[n]=t)},e.prototype.addPossibleTypes=function(e){var t=this;this.usingPossibleTypes=!0,Object.keys(e).forEach(function(n){t.getSupertypeSet(n,!0),e[n].forEach(function(e){t.getSupertypeSet(e,!0).add(n);var r=e.match(ig);r&&r[0]===e||t.fuzzySubtypes.set(e,RegExp(e))})})},e.prototype.getTypePolicy=function(e){var t=this;if(!ic.call(this.typePolicies,e)){var n=this.typePolicies[e]=Object.create(null);n.fields=Object.create(null);var r=this.supertypeMap.get(e);r&&r.size&&r.forEach(function(e){var r=t.getTypePolicy(e),i=r.fields;Object.assign(n,(0,en._T)(r,["fields"])),Object.assign(n.fields,i)})}var i=this.toBeAdded[e];return i&&i.length&&i.splice(0).forEach(function(n){t.updateTypePolicy(e,n)}),this.typePolicies[e]},e.prototype.getFieldPolicy=function(e,t,n){if(e){var r=this.getTypePolicy(e).fields;return r[t]||n&&(r[t]=Object.create(null))}},e.prototype.getSupertypeSet=function(e,t){var n=this.supertypeMap.get(e);return!n&&t&&this.supertypeMap.set(e,n=new Set),n},e.prototype.fragmentMatches=function(e,t,n,r){var i=this;if(!e.typeCondition)return!0;if(!t)return!1;var a=e.typeCondition.name.value;if(t===a)return!0;if(this.usingPossibleTypes&&this.supertypeMap.has(a))for(var o=this.getSupertypeSet(t,!0),s=[o],u=function(e){var t=i.getSupertypeSet(e,!1);t&&t.size&&0>s.indexOf(t)&&s.push(t)},c=!!(n&&this.fuzzySubtypes.size),l=!1,f=0;f1?a:t}:(r=(0,en.pi)({},i),ic.call(r,"from")||(r.from=t)),__DEV__&&void 0===r.from&&__DEV__&&Q.kG.warn("Undefined 'from' passed to readField with arguments ".concat(iF(Array.from(e)))),void 0===r.variables&&(r.variables=n),r}function i2(e){return function(t,n){if((0,tP.k)(t)||(0,tP.k)(n))throw __DEV__?new Q.ej("Cannot automatically merge arrays"):new Q.ej(4);if((0,eO.s)(t)&&(0,eO.s)(n)){var r=e.getFieldValue(t,"__typename"),i=e.getFieldValue(n,"__typename");if(r&&i&&r!==i)return n;if(eD(t)&&iw(n))return e.merge(t.__ref,n),t;if(iw(t)&&eD(n))return e.merge(t,n.__ref),n;if(iw(t)&&iw(n))return(0,en.pi)((0,en.pi)({},t),n)}return n}}function i3(e,t,n){var r="".concat(t).concat(n),i=e.flavors.get(r);return i||e.flavors.set(r,i=e.clientOnly===t&&e.deferred===n?e:(0,en.pi)((0,en.pi)({},e),{clientOnly:t,deferred:n})),i}var i4=function(){function e(e,t,n){this.cache=e,this.reader=t,this.fragments=n}return e.prototype.writeToStore=function(e,t){var n=this,r=t.query,i=t.result,a=t.dataId,o=t.variables,s=t.overwrite,u=e2(r),c=i_();o=(0,en.pi)((0,en.pi)({},e8(u)),o);var l=(0,en.pi)((0,en.pi)({store:e,written:Object.create(null),merge:function(e,t){return c.merge(e,t)},variables:o,varString:nx(o)},iE(r,this.fragments)),{overwrite:!!s,incomingById:new Map,clientOnly:!1,deferred:!1,flavors:new Map}),f=this.processSelectionSet({result:i||Object.create(null),dataId:a,selectionSet:u.selectionSet,mergeTree:{map:new Map},context:l});if(!eD(f))throw __DEV__?new Q.ej("Could not identify object ".concat(JSON.stringify(i))):new Q.ej(7);return l.incomingById.forEach(function(t,r){var i=t.storeObject,a=t.mergeTree,o=t.fieldNodeSet,s=eI(r);if(a&&a.map.size){var u=n.applyMerges(a,s,i,l);if(eD(u))return;i=u}if(__DEV__&&!l.overwrite){var c=Object.create(null);o.forEach(function(e){e.selectionSet&&(c[e.name.value]=!0)});var f=function(e){return!0===c[iv(e)]},d=function(e){var t=a&&a.map.get(e);return Boolean(t&&t.info&&t.info.merge)};Object.keys(i).forEach(function(e){f(e)&&!d(e)&&at(s,i,e,l.store)})}e.merge(r,i)}),e.retain(f.__ref),f},e.prototype.processSelectionSet=function(e){var t=this,n=e.dataId,r=e.result,i=e.selectionSet,a=e.context,o=e.mergeTree,s=this.cache.policies,u=Object.create(null),c=n&&s.rootTypenamesById[n]||eJ(r,i,a.fragmentMap)||n&&a.store.get(n,"__typename");"string"==typeof c&&(u.__typename=c);var l=function(){var e=i0(arguments,u,a.variables);if(eD(e.from)){var t=a.incomingById.get(e.from.__ref);if(t){var n=s.readField((0,en.pi)((0,en.pi)({},e),{from:t.storeObject}),a);if(void 0!==n)return n}}return s.readField(e,a)},f=new Set;this.flattenFields(i,r,a,c).forEach(function(e,n){var i,a=r[eX(n)];if(f.add(n),void 0!==a){var d=s.getStoreFieldName({typename:c,fieldName:n.name.value,field:n,variables:e.variables}),h=i6(o,d),p=t.processFieldValue(a,n,n.selectionSet?i3(e,!1,!1):e,h),b=void 0;n.selectionSet&&(eD(p)||iw(p))&&(b=l("__typename",p));var m=s.getMergeFunction(c,n.name.value,b);m?h.info={field:n,typename:c,merge:m}:i7(o,d),u=e.merge(u,((i={})[d]=p,i))}else __DEV__&&!e.clientOnly&&!e.deferred&&!nj.added(n)&&!s.getReadFunction(c,n.name.value)&&__DEV__&&Q.kG.error("Missing field '".concat(eX(n),"' while writing result ").concat(JSON.stringify(r,null,2)).substring(0,1e3))});try{var d=s.identify(r,{typename:c,selectionSet:i,fragmentMap:a.fragmentMap,storeObject:u,readField:l}),h=d[0],p=d[1];n=n||h,p&&(u=a.merge(u,p))}catch(b){if(!n)throw b}if("string"==typeof n){var m=eI(n),g=a.written[n]||(a.written[n]=[]);if(g.indexOf(i)>=0||(g.push(i),this.reader&&this.reader.isFresh(r,m,i,a)))return m;var v=a.incomingById.get(n);return v?(v.storeObject=a.merge(v.storeObject,u),v.mergeTree=i9(v.mergeTree,o),f.forEach(function(e){return v.fieldNodeSet.add(e)})):a.incomingById.set(n,{storeObject:u,mergeTree:i8(o)?void 0:o,fieldNodeSet:f}),m}return u},e.prototype.processFieldValue=function(e,t,n,r){var i=this;return t.selectionSet&&null!==e?(0,tP.k)(e)?e.map(function(e,a){var o=i.processFieldValue(e,t,n,i6(r,a));return i7(r,a),o}):this.processSelectionSet({result:e,selectionSet:t.selectionSet,context:n,mergeTree:r}):__DEV__?nJ(e):e},e.prototype.flattenFields=function(e,t,n,r){void 0===r&&(r=eJ(t,e,n.fragmentMap));var i=new Map,a=this.cache.policies,o=new n_(!1);return function e(s,u){var c=o.lookup(s,u.clientOnly,u.deferred);c.visited||(c.visited=!0,s.selections.forEach(function(o){if(td(o,n.variables)){var s=u.clientOnly,c=u.deferred;if(!(s&&c)&&(0,tP.O)(o.directives)&&o.directives.forEach(function(e){var t=e.name.value;if("client"===t&&(s=!0),"defer"===t){var r=eZ(e,n.variables);r&&!1===r.if||(c=!0)}}),eQ(o)){var l=i.get(o);l&&(s=s&&l.clientOnly,c=c&&l.deferred),i.set(o,i3(n,s,c))}else{var f=eC(o,n.lookupFragment);if(!f&&o.kind===nL.h.FRAGMENT_SPREAD)throw __DEV__?new Q.ej("No fragment named ".concat(o.name.value)):new Q.ej(8);f&&a.fragmentMatches(f,r,t,n.variables)&&e(f.selectionSet,i3(n,s,c))}}}))}(e,n),i},e.prototype.applyMerges=function(e,t,n,r,i){var a=this;if(e.map.size&&!eD(n)){var o,s,u=!(0,tP.k)(n)&&(eD(t)||iw(t))?t:void 0,c=n;u&&!i&&(i=[eD(u)?u.__ref:u]);var l=function(e,t){return(0,tP.k)(e)?"number"==typeof t?e[t]:void 0:r.store.getFieldValue(e,String(t))};e.map.forEach(function(e,t){var n=l(u,t),o=l(c,t);if(void 0!==o){i&&i.push(t);var f=a.applyMerges(e,n,o,r,i);f!==o&&(s=s||new Map).set(t,f),i&&(0,Q.kG)(i.pop()===t)}}),s&&(n=(0,tP.k)(c)?c.slice(0):(0,en.pi)({},c),s.forEach(function(e,t){n[t]=e}))}return e.info?this.cache.policies.runMergeFunction(t,n,e.info,r,i&&(o=r.store).getStorage.apply(o,i)):n},e}(),i5=[];function i6(e,t){var n=e.map;return n.has(t)||n.set(t,i5.pop()||{map:new Map}),n.get(t)}function i9(e,t){if(e===t||!t||i8(t))return e;if(!e||i8(e))return t;var n=e.info&&t.info?(0,en.pi)((0,en.pi)({},e.info),t.info):e.info||t.info,r=e.map.size&&t.map.size,i=r?new Map:e.map.size?e.map:t.map,a={info:n,map:i};if(r){var o=new Set(t.map.keys());e.map.forEach(function(e,n){a.map.set(n,i9(e,t.map.get(n))),o.delete(n)}),o.forEach(function(n){a.map.set(n,i9(t.map.get(n),e.map.get(n)))})}return a}function i8(e){return!e||!(e.info||e.map.size)}function i7(e,t){var n=e.map,r=n.get(t);r&&i8(r)&&(i5.push(r),n.delete(t))}var ae=new Set;function at(e,t,n,r){var i=function(e){var t=r.getFieldValue(e,n);return"object"==typeof t&&t},a=i(e);if(a){var o=i(t);if(!(!o||eD(a)||(0,nm.D)(a,o)||Object.keys(a).every(function(e){return void 0!==r.getFieldValue(o,e)}))){var s=r.getFieldValue(e,"__typename")||r.getFieldValue(t,"__typename"),u=iv(n),c="".concat(s,".").concat(u);if(!ae.has(c)){ae.add(c);var l=[];(0,tP.k)(a)||(0,tP.k)(o)||[a,o].forEach(function(e){var t=r.getFieldValue(e,"__typename");"string"!=typeof t||l.includes(t)||l.push(t)}),__DEV__&&Q.kG.warn("Cache data may be lost when replacing the ".concat(u," field of a ").concat(s," object.\n\nThis could cause additional (usually avoidable) network requests to fetch data that were otherwise cached.\n\nTo address this problem (which is not a bug in Apollo Client), ").concat(l.length?"either ensure all objects of type "+l.join(" and ")+" have an ID or a custom merge function, or ":"","define a custom merge function for the ").concat(c," field, so InMemoryCache can safely merge these objects:\n\n existing: ").concat(JSON.stringify(a).slice(0,1e3),"\n incoming: ").concat(JSON.stringify(o).slice(0,1e3),"\n\nFor more information about these options, please refer to the documentation:\n\n * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers\n * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects\n"))}}}}var an=function(e){function t(t){void 0===t&&(t={});var n=e.call(this)||this;return n.watches=new Set,n.typenameDocumentCache=new Map,n.makeVar=r2,n.txCount=0,n.config=ip(t),n.addTypename=!!n.config.addTypename,n.policies=new iQ({cache:n,dataIdFromObject:n.config.dataIdFromObject,possibleTypes:n.config.possibleTypes,typePolicies:n.config.typePolicies}),n.init(),n}return(0,en.ZT)(t,e),t.prototype.init=function(){var e=this.data=new iT.Root({policies:this.policies,resultCaching:this.config.resultCaching});this.optimisticData=e.stump,this.resetResultCache()},t.prototype.resetResultCache=function(e){var t=this,n=this.storeReader,r=this.config.fragments;this.storeWriter=new i4(this,this.storeReader=new iP({cache:this,addTypename:this.addTypename,resultCacheMaxSize:this.config.resultCacheMaxSize,canonizeResults:ib(this.config),canon:e?void 0:n&&n.canon,fragments:r}),r),this.maybeBroadcastWatch=rZ(function(e,n){return t.broadcastWatch(e,n)},{max:this.config.resultCacheMaxSize,makeCacheKey:function(e){var n=e.optimistic?t.optimisticData:t.data;if(iD(n)){var r=e.optimistic,i=e.id,a=e.variables;return n.makeCacheKey(e.query,e.callback,nx({optimistic:r,id:i,variables:a}))}}}),new Set([this.data.group,this.optimisticData.group,]).forEach(function(e){return e.resetCaching()})},t.prototype.restore=function(e){return this.init(),e&&this.data.replace(e),this},t.prototype.extract=function(e){return void 0===e&&(e=!1),(e?this.optimisticData:this.data).extract()},t.prototype.read=function(e){var t=e.returnPartialData,n=void 0!==t&&t;try{return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,config:this.config,returnPartialData:n})).result||null}catch(r){if(r instanceof is)return null;throw r}},t.prototype.write=function(e){try{return++this.txCount,this.storeWriter.writeToStore(this.data,e)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.modify=function(e){if(ic.call(e,"id")&&!e.id)return!1;var t=e.optimistic?this.optimisticData:this.data;try{return++this.txCount,t.modify(e.id||"ROOT_QUERY",e.fields)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.diff=function(e){return this.storeReader.diffQueryAgainstStore((0,en.pi)((0,en.pi)({},e),{store:e.optimistic?this.optimisticData:this.data,rootId:e.id||"ROOT_QUERY",config:this.config}))},t.prototype.watch=function(e){var t=this;return this.watches.size||r0(this),this.watches.add(e),e.immediate&&this.maybeBroadcastWatch(e),function(){t.watches.delete(e)&&!t.watches.size&&r1(t),t.maybeBroadcastWatch.forget(e)}},t.prototype.gc=function(e){nx.reset();var t=this.optimisticData.gc();return e&&!this.txCount&&(e.resetResultCache?this.resetResultCache(e.resetResultIdentities):e.resetResultIdentities&&this.storeReader.resetCanon()),t},t.prototype.retain=function(e,t){return(t?this.optimisticData:this.data).retain(e)},t.prototype.release=function(e,t){return(t?this.optimisticData:this.data).release(e)},t.prototype.identify=function(e){if(eD(e))return e.__ref;try{return this.policies.identify(e)[0]}catch(t){__DEV__&&Q.kG.warn(t)}},t.prototype.evict=function(e){if(!e.id){if(ic.call(e,"id"))return!1;e=(0,en.pi)((0,en.pi)({},e),{id:"ROOT_QUERY"})}try{return++this.txCount,this.optimisticData.evict(e,this.data)}finally{--this.txCount||!1===e.broadcast||this.broadcastWatches()}},t.prototype.reset=function(e){var t=this;return this.init(),nx.reset(),e&&e.discardWatches?(this.watches.forEach(function(e){return t.maybeBroadcastWatch.forget(e)}),this.watches.clear(),r1(this)):this.broadcastWatches(),Promise.resolve()},t.prototype.removeOptimistic=function(e){var t=this.optimisticData.removeLayer(e);t!==this.optimisticData&&(this.optimisticData=t,this.broadcastWatches())},t.prototype.batch=function(e){var t,n=this,r=e.update,i=e.optimistic,a=void 0===i||i,o=e.removeOptimistic,s=e.onWatchUpdated,u=function(e){var i=n,a=i.data,o=i.optimisticData;++n.txCount,e&&(n.data=n.optimisticData=e);try{return t=r(n)}finally{--n.txCount,n.data=a,n.optimisticData=o}},c=new Set;return s&&!this.txCount&&this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e){return c.add(e),!1}})),"string"==typeof a?this.optimisticData=this.optimisticData.addLayer(a,u):!1===a?u(this.data):u(),"string"==typeof o&&(this.optimisticData=this.optimisticData.removeLayer(o)),s&&c.size?(this.broadcastWatches((0,en.pi)((0,en.pi)({},e),{onWatchUpdated:function(e,t){var n=s.call(this,e,t);return!1!==n&&c.delete(e),n}})),c.size&&c.forEach(function(e){return n.maybeBroadcastWatch.dirty(e)})):this.broadcastWatches(e),t},t.prototype.performTransaction=function(e,t){return this.batch({update:e,optimistic:t||null!==t})},t.prototype.transformDocument=function(e){if(this.addTypename){var t=this.typenameDocumentCache.get(e);return t||(t=nj(e),this.typenameDocumentCache.set(e,t),this.typenameDocumentCache.set(t,t)),t}return e},t.prototype.transformForLink=function(e){var t=this.config.fragments;return t?t.transform(e):e},t.prototype.broadcastWatches=function(e){var t=this;this.txCount||this.watches.forEach(function(n){return t.maybeBroadcastWatch(n,e)})},t.prototype.broadcastWatch=function(e,t){var n=e.lastDiff,r=this.diff(e);(!t||(e.optimistic&&"string"==typeof t.optimistic&&(r.fromOptimisticTransaction=!0),!t.onWatchUpdated||!1!==t.onWatchUpdated.call(this,e,r,n)))&&(n&&(0,nm.D)(n.result,r.result)||e.callback(e.lastDiff=r,n))},t}(io),ar={possibleTypes:{ApproveJobProposalSpecPayload:["ApproveJobProposalSpecSuccess","JobAlreadyExistsError","NotFoundError"],BridgePayload:["Bridge","NotFoundError"],CancelJobProposalSpecPayload:["CancelJobProposalSpecSuccess","NotFoundError"],ChainPayload:["Chain","NotFoundError"],CreateAPITokenPayload:["CreateAPITokenSuccess","InputErrors"],CreateBridgePayload:["CreateBridgeSuccess"],CreateCSAKeyPayload:["CSAKeyExistsError","CreateCSAKeySuccess"],CreateFeedsManagerChainConfigPayload:["CreateFeedsManagerChainConfigSuccess","InputErrors","NotFoundError"],CreateFeedsManagerPayload:["CreateFeedsManagerSuccess","InputErrors","NotFoundError","SingleFeedsManagerError"],CreateJobPayload:["CreateJobSuccess","InputErrors"],CreateOCR2KeyBundlePayload:["CreateOCR2KeyBundleSuccess"],CreateOCRKeyBundlePayload:["CreateOCRKeyBundleSuccess"],CreateP2PKeyPayload:["CreateP2PKeySuccess"],DeleteAPITokenPayload:["DeleteAPITokenSuccess","InputErrors"],DeleteBridgePayload:["DeleteBridgeConflictError","DeleteBridgeInvalidNameError","DeleteBridgeSuccess","NotFoundError"],DeleteCSAKeyPayload:["DeleteCSAKeySuccess","NotFoundError"],DeleteFeedsManagerChainConfigPayload:["DeleteFeedsManagerChainConfigSuccess","NotFoundError"],DeleteJobPayload:["DeleteJobSuccess","NotFoundError"],DeleteOCR2KeyBundlePayload:["DeleteOCR2KeyBundleSuccess","NotFoundError"],DeleteOCRKeyBundlePayload:["DeleteOCRKeyBundleSuccess","NotFoundError"],DeleteP2PKeyPayload:["DeleteP2PKeySuccess","NotFoundError"],DeleteVRFKeyPayload:["DeleteVRFKeySuccess","NotFoundError"],DismissJobErrorPayload:["DismissJobErrorSuccess","NotFoundError"],Error:["CSAKeyExistsError","DeleteBridgeConflictError","DeleteBridgeInvalidNameError","InputError","JobAlreadyExistsError","NotFoundError","RunJobCannotRunError","SingleFeedsManagerError"],EthTransactionPayload:["EthTransaction","NotFoundError"],FeaturesPayload:["Features"],FeedsManagerPayload:["FeedsManager","NotFoundError"],GetSQLLoggingPayload:["SQLLogging"],GlobalLogLevelPayload:["GlobalLogLevel"],JobPayload:["Job","NotFoundError"],JobProposalPayload:["JobProposal","NotFoundError"],JobRunPayload:["JobRun","NotFoundError"],JobSpec:["BlockHeaderFeederSpec","BlockhashStoreSpec","BootstrapSpec","CronSpec","DirectRequestSpec","FluxMonitorSpec","GatewaySpec","KeeperSpec","OCR2Spec","OCRSpec","VRFSpec","WebhookSpec"],NodePayload:["Node","NotFoundError"],PaginatedPayload:["BridgesPayload","ChainsPayload","EthTransactionAttemptsPayload","EthTransactionsPayload","JobRunsPayload","JobsPayload","NodesPayload"],RejectJobProposalSpecPayload:["NotFoundError","RejectJobProposalSpecSuccess"],RunJobPayload:["NotFoundError","RunJobCannotRunError","RunJobSuccess"],SetGlobalLogLevelPayload:["InputErrors","SetGlobalLogLevelSuccess"],SetSQLLoggingPayload:["SetSQLLoggingSuccess"],SetServicesLogLevelsPayload:["InputErrors","SetServicesLogLevelsSuccess"],UpdateBridgePayload:["NotFoundError","UpdateBridgeSuccess"],UpdateFeedsManagerChainConfigPayload:["InputErrors","NotFoundError","UpdateFeedsManagerChainConfigSuccess"],UpdateFeedsManagerPayload:["InputErrors","NotFoundError","UpdateFeedsManagerSuccess"],UpdateJobProposalSpecDefinitionPayload:["NotFoundError","UpdateJobProposalSpecDefinitionSuccess"],UpdatePasswordPayload:["InputErrors","UpdatePasswordSuccess"],VRFKeyPayload:["NotFoundError","VRFKeySuccess"]}};let ai=ar;var aa=(r=void 0,location.origin),ao=new nh({uri:"".concat(aa,"/query"),credentials:"include"}),as=new ia({cache:new an({possibleTypes:ai.possibleTypes}),link:ao});if(a.Z.locale(o),u().defaultFormat="YYYY-MM-DD h:mm:ss A","undefined"!=typeof document){var au,ac,al=f().hydrate;ac=X,al(c.createElement(et,{client:as},c.createElement(d.zj,null,c.createElement(i.MuiThemeProvider,{theme:J.r},c.createElement(ac,null)))),document.getElementById("root"))}})()})(); \ No newline at end of file diff --git a/core/web/assets/main.b0b6f79f7f4a94560e37.js.gz b/core/web/assets/main.b0b6f79f7f4a94560e37.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..c505ea9eeade9a8933c1cc213f6e6d36bc873d85 GIT binary patch literal 1190421 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%0ATK8*z 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

q-69bB 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⋘9WypiZQs1-~Ge>)B|BoKah9Mm~o6rU-4 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�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 z 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

@-7&N=W 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 literal 0 HcmV?d00001 diff --git a/core/web/middleware.go b/core/web/middleware.go index 91c618cecbd..17bd7e65eba 100644 --- a/core/web/middleware.go +++ b/core/web/middleware.go @@ -21,6 +21,7 @@ import ( // inside this module. To achieve this, we direct webpack to output all of the compiled assets // in this module's folder under the "assets" directory. +//go:generate ../../operator_ui/install.sh //go:embed "assets" var uiEmbedFs embed.FS From bde6a2aea2321f2dc26773f445d1cb19e2bc98e7 Mon Sep 17 00:00:00 2001 From: Patrick Date: Wed, 22 Nov 2023 12:58:59 -0500 Subject: [PATCH 192/271] Adding TLS connection between node and OTEL collector for traces (#11278) * feature/tracing-credentials: adding TLS connection between node and OTEL collector for traces * feature/tracing-credentials: adding Tracing.Mode to configure insecure credentials explicitly * feature/tracing-credentials: relaxing conditions for Tracing.Mode and Tracing.TLSCertPath * feature/tracing-credentials: moving make file changes to a separate PR * feature/tracing-credentials: bumping relay and lint * feature/tracing-credentials: 'secure' --> 'tls' and 'insecure' --> 'unencrypted' * feature/tracing-credentials: minor docs refactoring * feature/tracing-credentials: adding changelog --- .github/tracing/README.md | 9 +- core/config/docs/core.toml | 10 +- core/config/toml/types.go | 63 ++++- core/config/toml/types_test.go | 230 +++++++++++++++++- core/config/tracing_config.go | 4 +- core/services/chainlink/config.go | 21 ++ core/services/chainlink/config_general.go | 2 +- core/services/chainlink/config_test.go | 82 ++++++- core/services/chainlink/config_tracing.go | 16 +- .../services/chainlink/config_tracing_test.go | 8 + .../testdata/config-empty-effective.toml | 2 + .../chainlink/testdata/config-full.toml | 2 + .../config-multi-chain-effective.toml | 2 + .../testdata/config-empty-effective.toml | 2 + core/web/resolver/testdata/config-full.toml | 2 + .../config-multi-chain-effective.toml | 2 + docs/CHANGELOG.md | 1 + docs/CONFIG.md | 26 +- integration-tests/types/config/node/core.go | 5 +- plugins/loop_registry.go | 3 +- plugins/loop_registry_test.go | 11 +- testdata/scripts/node/db/help.txtar | 2 +- testdata/scripts/node/validate/default.txtar | 2 + .../disk-based-logging-disabled.txtar | 2 + .../validate/disk-based-logging-no-dir.txtar | 2 + .../node/validate/disk-based-logging.txtar | 2 + testdata/scripts/node/validate/invalid.txtar | 2 + testdata/scripts/node/validate/valid.txtar | 2 + testdata/scripts/node/validate/warnings.txtar | 21 +- 29 files changed, 497 insertions(+), 41 deletions(-) diff --git a/.github/tracing/README.md b/.github/tracing/README.md index eb757384295..feba31feb65 100644 --- a/.github/tracing/README.md +++ b/.github/tracing/README.md @@ -30,7 +30,14 @@ Another way to generate traces is by enabling traces for PRs. This will instrume 8. Run `sh replay.sh` to replay those traces to the otel-collector container that was spun up in the last step. 9. navigate to `localhost:3000/explore` in a web browser to query for traces -The artifact is not json encoded - each individual line is a well formed and complete json object. +The artifact is not json encoded - each individual line is a well formed and complete json object. + + +## Production and NOPs environments + +In a production environment, we suggest coupling the lifecycle of nodes and otel-collectors. A best practice is to deploy the otel-collector alongside your node, using infrastructure as code (IAC) to automate deployments and certificate lifecycles. While there are valid use cases for using `Tracing.Mode = unencrypted`, we have set the default encryption setting to `Tracing.Mode = tls`. Externally deployed otel-collectors can not be used with `Tracing.Mode = unencrypted`. i.e. If `Tracing.Mode = unencrypted` and an external URI is detected for `Tracing.CollectorTarget` node configuration will fail to validate and the node will not boot. The node requires a valid encryption mode and collector target to send traces. + +Once traces reach the otel-collector, the rest of the observability pipeline is flexible. We recommend deploying (through automation) centrally managed Grafana Tempo and Grafana UI instances to receive from one or many otel-collector instances. Always use networking best practices and encrypt trace data, especially at network boundaries. ## Configuration This folder contains the following config files: diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 148de90cd95..79801c2c52b 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -587,13 +587,17 @@ DisableRateLimiting = false # Default # Enabled turns trace collection on or off. On requires an OTEL Tracing Collector. Enabled = false # Default # CollectorTarget is the logical address of the OTEL Tracing Collector. -CollectorTarget = "localhost:4317" # Example +CollectorTarget = 'localhost:4317' # Example # NodeID is an unique name for this node relative to any other node traces are collected for. -NodeID = "NodeID" # Example +NodeID = 'NodeID' # Example # SamplingRatio is the ratio of traces to sample for this node. SamplingRatio = 1.0 # Example +# Mode is a string value. `tls` or `unencrypted` are the only values allowed. If set to `unencrypted`, `TLSCertPath` can be unset, meaning traces will be sent over plaintext to the collector. +Mode = 'tls' # Default +# TLSCertPath is the file path to the TLS certificate used for secure communication with an OTEL Tracing Collector. +TLSCertPath = '/path/to/cert.pem' # Example # Tracing.Attributes are user specified key-value pairs to associate in the context of the traces [Tracing.Attributes] # env is an example user specified key-value pair -env = "test" # Example +env = 'test' # Example diff --git a/core/config/toml/types.go b/core/config/toml/types.go index 61962d43e5f..31076c1f1de 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -1455,6 +1455,8 @@ type Tracing struct { CollectorTarget *string NodeID *string SamplingRatio *float64 + Mode *string + TLSCertPath *string Attributes map[string]string `toml:",omitempty"` } @@ -1474,6 +1476,12 @@ func (t *Tracing) setFrom(f *Tracing) { if v := f.SamplingRatio; v != nil { t.SamplingRatio = f.SamplingRatio } + if v := f.Mode; v != nil { + t.Mode = f.Mode + } + if v := f.TLSCertPath; v != nil { + t.TLSCertPath = f.TLSCertPath + } } func (t *Tracing) ValidateConfig() (err error) { @@ -1487,10 +1495,39 @@ func (t *Tracing) ValidateConfig() (err error) { } } - if t.CollectorTarget != nil { - ok := isValidURI(*t.CollectorTarget) - if !ok { - err = multierr.Append(err, configutils.ErrInvalid{Name: "CollectorTarget", Value: *t.CollectorTarget, Msg: "must be a valid URI"}) + if t.Mode != nil { + switch *t.Mode { + case "tls": + // TLSCertPath must be set + if t.TLSCertPath == nil { + err = multierr.Append(err, configutils.ErrMissing{Name: "TLSCertPath", Msg: "must be set when Tracing.Mode is tls"}) + } else { + ok := isValidFilePath(*t.TLSCertPath) + if !ok { + err = multierr.Append(err, configutils.ErrInvalid{Name: "TLSCertPath", Value: *t.TLSCertPath, Msg: "must be a valid file path"}) + } + } + case "unencrypted": + // no-op + default: + // Mode must be either "tls" or "unencrypted" + err = multierr.Append(err, configutils.ErrInvalid{Name: "Mode", Value: *t.Mode, Msg: "must be either 'tls' or 'unencrypted'"}) + } + } + + if t.CollectorTarget != nil && t.Mode != nil { + switch *t.Mode { + case "tls": + if !isValidURI(*t.CollectorTarget) { + err = multierr.Append(err, configutils.ErrInvalid{Name: "CollectorTarget", Value: *t.CollectorTarget, Msg: "must be a valid URI"}) + } + case "unencrypted": + // Unencrypted traces can not be sent to external networks + if !isValidLocalURI(*t.CollectorTarget) { + err = multierr.Append(err, configutils.ErrInvalid{Name: "CollectorTarget", Value: *t.CollectorTarget, Msg: "must be a valid local URI"}) + } + default: + // no-op } } @@ -1499,15 +1536,19 @@ func (t *Tracing) ValidateConfig() (err error) { var hostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*$`) +// Validates uri is valid external or local URI func isValidURI(uri string) bool { if strings.Contains(uri, "://") { - // Standard URI check - _, _ = url.ParseRequestURI(uri) - // TODO: BCF-2703. Handle error. All external addresses currently fail validation until we have secure transport to external networks. - return false + _, err := url.ParseRequestURI(uri) + return err == nil } - // For URIs like "otel-collector:4317" + return isValidLocalURI(uri) +} + +// isValidLocalURI returns true if uri is a valid local URI +// External URIs (e.g. http://) are not valid local URIs, and will return false. +func isValidLocalURI(uri string) bool { parts := strings.Split(uri, ":") if len(parts) == 2 { host, port := parts[0], parts[1] @@ -1530,3 +1571,7 @@ func isValidURI(uri string) bool { func isValidHostname(hostname string) bool { return hostnameRegex.MatchString(hostname) } + +func isValidFilePath(path string) bool { + return len(path) > 0 && len(path) < 4096 +} diff --git a/core/config/toml/types_test.go b/core/config/toml/types_test.go index 92aaa024304..e16d3a864da 100644 --- a/core/config/toml/types_test.go +++ b/core/config/toml/types_test.go @@ -185,55 +185,115 @@ func TestTracing_ValidateCollectorTarget(t *testing.T) { tests := []struct { name string collectorTarget *string + mode *string wantErr bool errMsg string }{ { - name: "valid http address", + name: "valid http address in tls mode", + collectorTarget: ptr("https://testing.collector.dev"), + mode: ptr("tls"), + wantErr: false, + }, + { + name: "valid http address in unencrypted mode", collectorTarget: ptr("https://localhost:4317"), - // TODO: BCF-2703. Re-enable when we have secure transport to otel collectors in external networks - wantErr: true, - errMsg: "CollectorTarget: invalid value (https://localhost:4317): must be a valid URI", + mode: ptr("unencrypted"), + wantErr: true, + errMsg: "CollectorTarget: invalid value (https://localhost:4317): must be a valid local URI", }, + // Tracing.Mode = 'tls' { name: "valid localhost address", collectorTarget: ptr("localhost:4317"), + mode: ptr("tls"), wantErr: false, }, { name: "valid docker address", collectorTarget: ptr("otel-collector:4317"), + mode: ptr("tls"), wantErr: false, }, { name: "valid IP address", collectorTarget: ptr("192.168.1.1:4317"), + mode: ptr("tls"), wantErr: false, }, { name: "invalid port", collectorTarget: ptr("localhost:invalid"), wantErr: true, + mode: ptr("tls"), errMsg: "CollectorTarget: invalid value (localhost:invalid): must be a valid URI", }, { name: "invalid address", collectorTarget: ptr("invalid address"), wantErr: true, + mode: ptr("tls"), errMsg: "CollectorTarget: invalid value (invalid address): must be a valid URI", }, { name: "nil CollectorTarget", collectorTarget: ptr(""), wantErr: true, + mode: ptr("tls"), errMsg: "CollectorTarget: invalid value (): must be a valid URI", }, + // Tracing.Mode = 'unencrypted' + { + name: "valid localhost address", + collectorTarget: ptr("localhost:4317"), + mode: ptr("unencrypted"), + wantErr: false, + }, + { + name: "valid docker address", + collectorTarget: ptr("otel-collector:4317"), + mode: ptr("unencrypted"), + wantErr: false, + }, + { + name: "valid IP address", + collectorTarget: ptr("192.168.1.1:4317"), + mode: ptr("unencrypted"), + wantErr: false, + }, + { + name: "invalid port", + collectorTarget: ptr("localhost:invalid"), + wantErr: true, + mode: ptr("unencrypted"), + errMsg: "CollectorTarget: invalid value (localhost:invalid): must be a valid local URI", + }, + { + name: "invalid address", + collectorTarget: ptr("invalid address"), + wantErr: true, + mode: ptr("unencrypted"), + errMsg: "CollectorTarget: invalid value (invalid address): must be a valid local URI", + }, + { + name: "nil CollectorTarget", + collectorTarget: ptr(""), + wantErr: true, + mode: ptr("unencrypted"), + errMsg: "CollectorTarget: invalid value (): must be a valid local URI", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + var tlsCertPath string + if *tt.mode == "tls" { + tlsCertPath = "/path/to/cert.pem" + } tracing := &Tracing{ Enabled: ptr(true), + TLSCertPath: &tlsCertPath, + Mode: tt.mode, CollectorTarget: tt.collectorTarget, } @@ -309,5 +369,167 @@ func TestTracing_ValidateSamplingRatio(t *testing.T) { } } +func TestTracing_ValidateTLSCertPath(t *testing.T) { + // tests for Tracing.Mode = 'tls' + tls_tests := []struct { + name string + tlsCertPath *string + wantErr bool + errMsg string + }{ + { + name: "valid file path", + tlsCertPath: ptr("/etc/ssl/certs/cert.pem"), + wantErr: false, + }, + { + name: "relative file path", + tlsCertPath: ptr("certs/cert.pem"), + wantErr: false, + }, + { + name: "excessively long file path", + tlsCertPath: ptr(strings.Repeat("z", 4097)), + wantErr: true, + errMsg: "TLSCertPath: invalid value (" + strings.Repeat("z", 4097) + "): must be a valid file path", + }, + { + name: "empty file path", + tlsCertPath: ptr(""), + wantErr: true, + errMsg: "TLSCertPath: invalid value (): must be a valid file path", + }, + } + + // tests for Tracing.Mode = 'unencrypted' + unencrypted_tests := []struct { + name string + tlsCertPath *string + wantErr bool + errMsg string + }{ + { + name: "valid file path", + tlsCertPath: ptr("/etc/ssl/certs/cert.pem"), + wantErr: false, + }, + { + name: "relative file path", + tlsCertPath: ptr("certs/cert.pem"), + wantErr: false, + }, + { + name: "excessively long file path", + tlsCertPath: ptr(strings.Repeat("z", 4097)), + wantErr: false, + }, + { + name: "empty file path", + tlsCertPath: ptr(""), + wantErr: false, + }, + } + + for _, tt := range tls_tests { + t.Run(tt.name, func(t *testing.T) { + tracing := &Tracing{ + Mode: ptr("tls"), + TLSCertPath: tt.tlsCertPath, + Enabled: ptr(true), + } + + err := tracing.ValidateConfig() + + if tt.wantErr { + assert.Error(t, err) + assert.Equal(t, tt.errMsg, err.Error()) + } else { + assert.NoError(t, err) + } + }) + } + + for _, tt := range unencrypted_tests { + t.Run(tt.name, func(t *testing.T) { + tracing := &Tracing{ + Mode: ptr("unencrypted"), + TLSCertPath: tt.tlsCertPath, + Enabled: ptr(true), + } + + err := tracing.ValidateConfig() + + if tt.wantErr { + assert.Error(t, err) + assert.Equal(t, tt.errMsg, err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestTracing_ValidateMode(t *testing.T) { + tests := []struct { + name string + mode *string + tlsCertPath *string + wantErr bool + errMsg string + }{ + { + name: "tls mode with valid TLS path", + mode: ptr("tls"), + tlsCertPath: ptr("/path/to/cert.pem"), + wantErr: false, + }, + { + name: "tls mode without TLS path", + mode: ptr("tls"), + tlsCertPath: nil, + wantErr: true, + errMsg: "TLSCertPath: missing: must be set when Tracing.Mode is tls", + }, + { + name: "unencrypted mode with TLS path", + mode: ptr("unencrypted"), + tlsCertPath: ptr("/path/to/cert.pem"), + wantErr: false, + }, + { + name: "unencrypted mode without TLS path", + mode: ptr("unencrypted"), + tlsCertPath: nil, + wantErr: false, + }, + { + name: "invalid mode", + mode: ptr("unknown"), + tlsCertPath: nil, + wantErr: true, + errMsg: "Mode: invalid value (unknown): must be either 'tls' or 'unencrypted'", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tracing := &Tracing{ + Enabled: ptr(true), + Mode: tt.mode, + TLSCertPath: tt.tlsCertPath, + } + + err := tracing.ValidateConfig() + + if tt.wantErr { + assert.Error(t, err) + assert.Equal(t, tt.errMsg, err.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} + // ptr is a utility function for converting a value to a pointer to the value. func ptr[T any](t T) *T { return &t } diff --git a/core/config/tracing_config.go b/core/config/tracing_config.go index 28584a9cde4..307a010c486 100644 --- a/core/config/tracing_config.go +++ b/core/config/tracing_config.go @@ -4,6 +4,8 @@ type Tracing interface { Enabled() bool CollectorTarget() string NodeID() string - Attributes() map[string]string SamplingRatio() float64 + TLSCertPath() string + Mode() string + Attributes() map[string]string } diff --git a/core/services/chainlink/config.go b/core/services/chainlink/config.go index 10598718f97..5d8b1019e8c 100644 --- a/core/services/chainlink/config.go +++ b/core/services/chainlink/config.go @@ -52,6 +52,27 @@ func (c *Config) TOMLString() (string, error) { return string(b), nil } +// warnings aggregates warnings from valueWarnings and deprecationWarnings +func (c *Config) warnings() (err error) { + deprecationErr := c.deprecationWarnings() + warningErr := c.valueWarnings() + err = multierr.Append(deprecationErr, warningErr) + _, list := utils.MultiErrorList(err) + return list +} + +// valueWarnings returns an error if the Config contains values that hint at misconfiguration before defaults are applied. +func (c *Config) valueWarnings() (err error) { + if c.Tracing.Enabled != nil && *c.Tracing.Enabled { + if c.Tracing.Mode != nil && *c.Tracing.Mode == "unencrypted" { + if c.Tracing.TLSCertPath != nil { + err = multierr.Append(err, config.ErrInvalid{Name: "Tracing.TLSCertPath", Value: *c.Tracing.TLSCertPath, Msg: "must be empty when Tracing.Mode is 'unencrypted'"}) + } + } + } + return +} + // deprecationWarnings returns an error if the Config contains deprecated fields. // This is typically used before defaults have been applied, with input from the user. func (c *Config) deprecationWarnings() (err error) { diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index 6a835e09c89..fff7822a814 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -137,7 +137,7 @@ func (o GeneralConfigOpts) New() (GeneralConfig, error) { return nil, err } - _, warning := utils.MultiErrorList(o.Config.deprecationWarnings()) + _, warning := utils.MultiErrorList(o.Config.warnings()) o.Config.setDefaults() if !o.SkipEnv { diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 891c0a490fb..33fda221285 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -227,11 +227,13 @@ func TestConfig_Marshal(t *testing.T) { Enabled: ptr(true), CollectorTarget: ptr("localhost:4317"), NodeID: ptr("clc-ocr-sol-devnet-node-1"), + SamplingRatio: ptr(1.0), + Mode: ptr("tls"), + TLSCertPath: ptr("/path/to/cert.pem"), Attributes: map[string]string{ "test": "load", "env": "dev", }, - SamplingRatio: ptr(1.0), }, }, } @@ -688,6 +690,8 @@ Enabled = true CollectorTarget = 'localhost:4317' NodeID = 'clc-ocr-sol-devnet-node-1' SamplingRatio = 1.0 +Mode = 'tls' +TLSCertPath = '/path/to/cert.pem' [Tracing.Attributes] env = 'dev' @@ -1537,4 +1541,80 @@ func TestConfig_SetFrom(t *testing.T) { } } +func TestConfig_Warnings(t *testing.T) { + tests := []struct { + name string + config Config + expectedErrors []string + }{ + { + name: "No warnings", + config: Config{}, + expectedErrors: nil, + }, + { + name: "Value warning - unencrypted mode with TLS path set", + config: Config{ + Core: toml.Core{ + Tracing: toml.Tracing{ + Enabled: ptr(true), + Mode: ptr("unencrypted"), + TLSCertPath: ptr("/path/to/cert.pem"), + }, + }, + }, + expectedErrors: []string{"Tracing.TLSCertPath: invalid value (/path/to/cert.pem): must be empty when Tracing.Mode is 'unencrypted'"}, + }, + { + name: "Deprecation warning - P2P.V1 fields set", + config: Config{ + Core: toml.Core{ + P2P: toml.P2P{ + V1: toml.P2PV1{ + Enabled: ptr(true), + }, + }, + }, + }, + expectedErrors: []string{ + "P2P.V1: is deprecated and will be removed in a future version", + }, + }, + { + name: "Value warning and deprecation warning", + config: Config{ + Core: toml.Core{ + P2P: toml.P2P{ + V1: toml.P2PV1{ + Enabled: ptr(true), + }, + }, + Tracing: toml.Tracing{ + Enabled: ptr(true), + Mode: ptr("unencrypted"), + TLSCertPath: ptr("/path/to/cert.pem"), + }, + }, + }, + expectedErrors: []string{ + "Tracing.TLSCertPath: invalid value (/path/to/cert.pem): must be empty when Tracing.Mode is 'unencrypted'", + "P2P.V1: is deprecated and will be removed in a future version", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.config.warnings() + if len(tt.expectedErrors) == 0 { + assert.NoError(t, err) + } else { + for _, expectedErr := range tt.expectedErrors { + assert.Contains(t, err.Error(), expectedErr) + } + } + }) + } +} + func ptr[T any](t T) *T { return &t } diff --git a/core/services/chainlink/config_tracing.go b/core/services/chainlink/config_tracing.go index 390645974f1..36d09ae0564 100644 --- a/core/services/chainlink/config_tracing.go +++ b/core/services/chainlink/config_tracing.go @@ -18,10 +18,18 @@ func (t tracingConfig) NodeID() string { return *t.s.NodeID } -func (t tracingConfig) Attributes() map[string]string { - return t.s.Attributes -} - func (t tracingConfig) SamplingRatio() float64 { return *t.s.SamplingRatio } + +func (t tracingConfig) Mode() string { + return *t.s.Mode +} + +func (t tracingConfig) TLSCertPath() string { + return *t.s.TLSCertPath +} + +func (t tracingConfig) Attributes() map[string]string { + return t.s.Attributes +} diff --git a/core/services/chainlink/config_tracing_test.go b/core/services/chainlink/config_tracing_test.go index 5968bc306a2..37653729cf3 100644 --- a/core/services/chainlink/config_tracing_test.go +++ b/core/services/chainlink/config_tracing_test.go @@ -14,12 +14,16 @@ func TestTracing_Config(t *testing.T) { collectorTarget := "http://localhost:9000" nodeID := "Node1" samplingRatio := 0.5 + mode := "tls" + tlsCertPath := "/path/to/cert.pem" attributes := map[string]string{"key": "value"} tracing := toml.Tracing{ Enabled: &enabled, CollectorTarget: &collectorTarget, NodeID: &nodeID, SamplingRatio: &samplingRatio, + Mode: &mode, + TLSCertPath: &tlsCertPath, Attributes: attributes, } tConfig := tracingConfig{s: tracing} @@ -28,6 +32,8 @@ func TestTracing_Config(t *testing.T) { assert.Equal(t, "http://localhost:9000", tConfig.CollectorTarget()) assert.Equal(t, "Node1", tConfig.NodeID()) assert.Equal(t, 0.5, tConfig.SamplingRatio()) + assert.Equal(t, "tls", tConfig.Mode()) + assert.Equal(t, "/path/to/cert.pem", tConfig.TLSCertPath()) assert.Equal(t, map[string]string{"key": "value"}, tConfig.Attributes()) // Test when all fields are nil @@ -38,5 +44,7 @@ func TestTracing_Config(t *testing.T) { assert.Panics(t, func() { nilConfig.CollectorTarget() }) assert.Panics(t, func() { nilConfig.NodeID() }) assert.Panics(t, func() { nilConfig.SamplingRatio() }) + assert.Panics(t, func() { nilConfig.Mode() }) + assert.Panics(t, func() { nilConfig.TLSCertPath() }) assert.Nil(t, nilConfig.Attributes()) } diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index b897fba7f10..8f3135b34e4 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -232,3 +232,5 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 531c98d7344..eca5f6f96d2 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -238,6 +238,8 @@ Enabled = true CollectorTarget = 'localhost:4317' NodeID = 'clc-ocr-sol-devnet-node-1' SamplingRatio = 1.0 +Mode = 'tls' +TLSCertPath = '/path/to/cert.pem' [Tracing.Attributes] env = 'dev' diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index c743601ced8..6a60dfd419a 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -232,6 +232,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index b897fba7f10..8f3135b34e4 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -232,3 +232,5 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 6cd6eaabc3c..7e9872e9554 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -238,6 +238,8 @@ Enabled = false CollectorTarget = 'localhost:4317' NodeID = 'NodeID' SamplingRatio = 1.0 +Mode = 'tls' +TLSCertPath = '/path/to/cert.pem' [Tracing.Attributes] env = 'dev' diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index c743601ced8..6a60dfd419a 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -232,6 +232,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8e016347c43..265222c8bec 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added distributed tracing in the OpenTelemetry trace format to the node, currently focused at the LOOPP Plugin development effort. This includes a new set of `Tracing` TOML configurations. The default for collecting traces is off - you must explicitly enable traces and setup a valid OpenTelemetry collector. Refer to `.github/tracing/README.md` for more details. - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. - New prom metrics for mercury: `mercury_transmit_queue_delete_error_count` diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 5b93c7061e8..63c20bdf4a5 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1595,9 +1595,11 @@ DisableRateLimiting skips ratelimiting on asset requests. ```toml [Tracing] Enabled = false # Default -CollectorTarget = "localhost:4317" # Example -NodeID = "NodeID" # Example +CollectorTarget = 'localhost:4317' # Example +NodeID = 'NodeID' # Example SamplingRatio = 1.0 # Example +Mode = 'tls' # Default +TLSCertPath = '/path/to/cert.pem' # Example ``` @@ -1609,13 +1611,13 @@ Enabled turns trace collection on or off. On requires an OTEL Tracing Collector. ### CollectorTarget ```toml -CollectorTarget = "localhost:4317" # Example +CollectorTarget = 'localhost:4317' # Example ``` CollectorTarget is the logical address of the OTEL Tracing Collector. ### NodeID ```toml -NodeID = "NodeID" # Example +NodeID = 'NodeID' # Example ``` NodeID is an unique name for this node relative to any other node traces are collected for. @@ -1625,16 +1627,28 @@ SamplingRatio = 1.0 # Example ``` SamplingRatio is the ratio of traces to sample for this node. +### Mode +```toml +Mode = 'tls' # Default +``` +Mode is a string value. `tls` or `unencrypted` are the only values allowed. If set to `unencrypted`, `TLSCertPath` can be unset, meaning traces will be sent over plaintext to the collector. + +### TLSCertPath +```toml +TLSCertPath = '/path/to/cert.pem' # Example +``` +TLSCertPath is the file path to the TLS certificate used for secure communication with an OTEL Tracing Collector. + ## Tracing.Attributes ```toml [Tracing.Attributes] -env = "test" # Example +env = 'test' # Example ``` Tracing.Attributes are user specified key-value pairs to associate in the context of the traces ### env ```toml -env = "test" # Example +env = 'test' # Example ``` env is an example user specified key-value pair diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index ad85506a04c..b7f2b316aa7 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -138,11 +138,12 @@ func WithTracing() NodeConfigOpt { Enabled: ptr.Ptr(true), CollectorTarget: ptr.Ptr("otel-collector:4317"), // ksortable unique id - NodeID: ptr.Ptr(ksuid.New().String()), + NodeID: ptr.Ptr(ksuid.New().String()), + SamplingRatio: ptr.Ptr(1.0), + Mode: ptr.Ptr("unencrypted"), Attributes: map[string]string{ "env": "smoke", }, - SamplingRatio: ptr.Ptr(1.0), } } } diff --git a/plugins/loop_registry.go b/plugins/loop_registry.go index 17ad7cba5ad..7a5274803d6 100644 --- a/plugins/loop_registry.go +++ b/plugins/loop_registry.go @@ -55,8 +55,9 @@ func (m *LoopRegistry) Register(id string) (*RegisteredLoop, error) { if m.cfgTracing != nil { envCfg.TracingEnabled = m.cfgTracing.Enabled() envCfg.TracingCollectorTarget = m.cfgTracing.CollectorTarget() - envCfg.TracingAttributes = m.cfgTracing.Attributes() envCfg.TracingSamplingRatio = m.cfgTracing.SamplingRatio() + envCfg.TracingTLSCertPath = m.cfgTracing.TLSCertPath() + envCfg.TracingAttributes = m.cfgTracing.Attributes() } m.registry[id] = &RegisteredLoop{Name: id, EnvCfg: envCfg} diff --git a/plugins/loop_registry_test.go b/plugins/loop_registry_test.go index b5da9154b68..b307469e09b 100644 --- a/plugins/loop_registry_test.go +++ b/plugins/loop_registry_test.go @@ -29,13 +29,15 @@ func TestPluginPortManager(t *testing.T) { // Mock tracing config type MockCfgTracing struct{} -func (m *MockCfgTracing) Enabled() bool { return true } -func (m *MockCfgTracing) CollectorTarget() string { return "http://localhost:9000" } func (m *MockCfgTracing) Attributes() map[string]string { return map[string]string{"attribute": "value"} } -func (m *MockCfgTracing) SamplingRatio() float64 { return 0.1 } -func (m *MockCfgTracing) NodeID() string { return "" } +func (m *MockCfgTracing) Enabled() bool { return true } +func (m *MockCfgTracing) NodeID() string { return "" } +func (m *MockCfgTracing) CollectorTarget() string { return "http://localhost:9000" } +func (m *MockCfgTracing) SamplingRatio() float64 { return 0.1 } +func (m *MockCfgTracing) TLSCertPath() string { return "/path/to/cert.pem" } +func (m *MockCfgTracing) Mode() string { return "tls" } func TestLoopRegistry_Register(t *testing.T) { mockCfgTracing := &MockCfgTracing{} @@ -56,4 +58,5 @@ func TestLoopRegistry_Register(t *testing.T) { require.Equal(t, "http://localhost:9000", registeredLoop.EnvCfg.TracingCollectorTarget) require.Equal(t, map[string]string{"attribute": "value"}, registeredLoop.EnvCfg.TracingAttributes) require.Equal(t, 0.1, registeredLoop.EnvCfg.TracingSamplingRatio) + require.Equal(t, "/path/to/cert.pem", registeredLoop.EnvCfg.TracingTLSCertPath) } diff --git a/testdata/scripts/node/db/help.txtar b/testdata/scripts/node/db/help.txtar index ccdf3431786..4f2fe3e0767 100644 --- a/testdata/scripts/node/db/help.txtar +++ b/testdata/scripts/node/db/help.txtar @@ -20,4 +20,4 @@ COMMANDS: OPTIONS: --help, -h show help - + \ No newline at end of file diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 01e96ac944d..267a760f08c 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -244,6 +244,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' Invalid configuration: invalid secrets: 2 errors: - Database.URL: empty: must be provided and non-empty diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 1f6901e9ffd..e6281e8d221 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -288,6 +288,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 4c1a1c75fc3..65d832aa82e 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -288,6 +288,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 536b7d8ac08..6b9e3d56ce6 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -288,6 +288,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 89f59574fcc..aa2036413c7 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -278,6 +278,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 2d32b39a644..4ceb9d5df35 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -285,6 +285,8 @@ Enabled = false CollectorTarget = '' NodeID = '' SamplingRatio = 0.0 +Mode = 'tls' +TLSCertPath = '' [[EVM]] ChainID = '1' diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index e478203e00e..e4ff2aa35ea 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -15,6 +15,12 @@ ListenPort = 0 NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' +[Tracing] +Enabled = true +CollectorTarget = 'otel-collector:4317' +TLSCertPath = 'something' +Mode = 'unencrypted' + -- secrets.toml -- [Database] URL = 'postgresql://user:pass1234567890abcd@localhost:5432/dbname?sslmode=disable' @@ -46,6 +52,12 @@ ListenPort = 0 NewStreamTimeout = '10s' PeerstoreWriteInterval = '5m0s' +[Tracing] +Enabled = true +CollectorTarget = 'otel-collector:4317' +Mode = 'unencrypted' +TLSCertPath = 'something' + # Effective Configuration, with defaults applied: InsecureFastScrypt = false RootDir = '~/.chainlink' @@ -277,13 +289,15 @@ InfiniteDepthQueries = false DisableRateLimiting = false [Tracing] -Enabled = false -CollectorTarget = '' +Enabled = true +CollectorTarget = 'otel-collector:4317' NodeID = '' SamplingRatio = 0.0 +Mode = 'unencrypted' +TLSCertPath = 'something' # Configuration warning: -2 errors: +3 errors: - P2P.V1: is deprecated and will be removed in a future version - P2P.V1: 10 errors: - AnnounceIP: is deprecated and will be removed in a future version @@ -296,4 +310,5 @@ SamplingRatio = 0.0 - ListenPort: is deprecated and will be removed in a future version - NewStreamTimeout: is deprecated and will be removed in a future version - PeerstoreWriteInterval: is deprecated and will be removed in a future version + - Tracing.TLSCertPath: invalid value (something): must be empty when Tracing.Mode is 'unencrypted' Valid configuration. From e67a76c69689f5fa03477ba8cce39e0b2662062b Mon Sep 17 00:00:00 2001 From: Lukasz <120112546+lukaszcl@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:12:54 +0100 Subject: [PATCH 193/271] Bump CTF to fix Killgrave issue (#11365) * Bump CTF to fix Killgrave issue * disable enforcing ctf version * disable enforcing ctf version 2 * Use CTF v1.19.5 * Enable enforcing CTF version * Remove old assets --- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index ba90493d299..2338c0caa58 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 - github.com/smartcontractkit/chainlink-testing-framework v1.19.4 + github.com/smartcontractkit/chainlink-testing-framework v1.19.5 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 849df2afcab..21be8c1f480 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2380,8 +2380,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= -github.com/smartcontractkit/chainlink-testing-framework v1.19.4 h1:a5zG5k3MNbDBWiBdxF2cBLaMyB+WD0ROWPiZ0DVJQMc= -github.com/smartcontractkit/chainlink-testing-framework v1.19.4/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= +github.com/smartcontractkit/chainlink-testing-framework v1.19.5 h1:Iq1L7wCA8IkBBtP3p6W2ttu8v9cJp95spusnozW1UrA= +github.com/smartcontractkit/chainlink-testing-framework v1.19.5/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= From 59485ffebdc8c57a0e3629fed244eb326cdb6295 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:55:12 +0400 Subject: [PATCH 194/271] [AUTO-7448] add standard actions for v2.X tests (#11363) * add standard actions for v2.X tests * remove duplicated lines * fix AddAutomationJobs --- .../actions/automationv2/actions.go | 699 ++++++++++++++++++ .../contracts/contract_deployer.go | 25 + 2 files changed, 724 insertions(+) create mode 100644 integration-tests/actions/automationv2/actions.go diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go new file mode 100644 index 00000000000..f97c17bb94f --- /dev/null +++ b/integration-tests/actions/automationv2/actions.go @@ -0,0 +1,699 @@ +package automationv2 + +import ( + "crypto/ed25519" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/lib/pq" + "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + ocr2 "github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper" + ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" + "gopkg.in/guregu/null.v4" + + ocr2keepers20config "github.com/smartcontractkit/chainlink-automation/pkg/v2/config" + ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registrar_wrapper2_0" + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/store/models" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +type NodeDetails struct { + P2PId string + TransmitterAddresses []string + OCR2ConfigPublicKey string + OCR2OffchainPublicKey string + OCR2OnChainPublicKey string + OCR2Id string +} + +type AutomationTest struct { + ChainClient blockchain.EVMClient + Deployer contracts.ContractDeployer + + LinkToken contracts.LinkToken + Transcoder contracts.UpkeepTranscoder + EthLinkFeed contracts.MockETHLINKFeed + GasFeed contracts.MockGasFeed + Registry contracts.KeeperRegistry + Registrar contracts.KeeperRegistrar + + RegistrySettings contracts.KeeperRegistrySettings + RegistrarSettings contracts.KeeperRegistrarSettings + PluginConfig ocr2keepers30config.OffchainConfig + PublicConfig ocr3.PublicConfig + UpkeepPrivilegeManager common.Address + UpkeepIDs []*big.Int + + IsOnk8s bool + + ChainlinkNodesk8s []*client.ChainlinkK8sClient + ChainlinkNodes []*client.ChainlinkClient + + NodeDetails []NodeDetails + DefaultP2Pv2Bootstrapper string + MercuryCredentialName string + TransmitterKeyIndex int +} + +type UpkeepConfig struct { + UpkeepName string + EncryptedEmail []byte + UpkeepContract common.Address + GasLimit uint32 + AdminAddress common.Address + TriggerType uint8 + CheckData []byte + TriggerConfig []byte + OffchainConfig []byte + FundingAmount *big.Int +} + +func NewAutomationTestK8s( + chainClient blockchain.EVMClient, + deployer contracts.ContractDeployer, + chainlinkNodes []*client.ChainlinkK8sClient, +) *AutomationTest { + return &AutomationTest{ + ChainClient: chainClient, + Deployer: deployer, + ChainlinkNodesk8s: chainlinkNodes, + IsOnk8s: true, + TransmitterKeyIndex: 0, + UpkeepPrivilegeManager: common.HexToAddress(chainClient.GetDefaultWallet().Address()), + } +} + +func NewAutomationTestDocker( + chainClient blockchain.EVMClient, + deployer contracts.ContractDeployer, + chainlinkNodes []*client.ChainlinkClient, +) *AutomationTest { + return &AutomationTest{ + ChainClient: chainClient, + Deployer: deployer, + ChainlinkNodes: chainlinkNodes, + IsOnk8s: false, + TransmitterKeyIndex: 0, + UpkeepPrivilegeManager: common.HexToAddress(chainClient.GetDefaultWallet().Address()), + } +} + +func (a *AutomationTest) SetIsOnk8s(flag bool) { + a.IsOnk8s = flag +} + +func (a *AutomationTest) SetMercuryCredentialName(name string) { + a.MercuryCredentialName = name +} + +func (a *AutomationTest) SetTransmitterKeyIndex(index int) { + a.TransmitterKeyIndex = index +} + +func (a *AutomationTest) SetUpkeepPrivilegeManager(address string) { + a.UpkeepPrivilegeManager = common.HexToAddress(address) +} + +func (a *AutomationTest) DeployLINK() error { + linkToken, err := a.Deployer.DeployLinkTokenContract() + if err != nil { + return err + } + a.LinkToken = linkToken + err = a.ChainClient.WaitForEvents() + if err != nil { + return errors.Join(err, fmt.Errorf("failed waiting for link token contract to deploy")) + } + return nil +} + +func (a *AutomationTest) LoadLINK(address string) error { + linkToken, err := a.Deployer.LoadLinkToken(common.HexToAddress(address)) + if err != nil { + return err + } + a.LinkToken = linkToken + return nil +} + +func (a *AutomationTest) DeployTranscoder() error { + transcoder, err := a.Deployer.DeployUpkeepTranscoder() + if err != nil { + return err + } + a.Transcoder = transcoder + err = a.ChainClient.WaitForEvents() + if err != nil { + return errors.Join(err, fmt.Errorf("failed waiting for transcoder contract to deploy")) + } + return nil +} + +func (a *AutomationTest) LoadTranscoder(address string) error { + transcoder, err := a.Deployer.LoadUpkeepTranscoder(common.HexToAddress(address)) + if err != nil { + return err + } + a.Transcoder = transcoder + return nil +} + +func (a *AutomationTest) DeployEthLinkFeed() error { + ethLinkFeed, err := a.Deployer.DeployMockETHLINKFeed(a.RegistrySettings.FallbackLinkPrice) + if err != nil { + return err + } + a.EthLinkFeed = ethLinkFeed + err = a.ChainClient.WaitForEvents() + if err != nil { + return errors.Join(err, fmt.Errorf("failed waiting for Mock ETH LINK feed to deploy")) + } + return nil +} + +func (a *AutomationTest) LoadEthLinkFeed(address string) error { + ethLinkFeed, err := a.Deployer.LoadETHLINKFeed(common.HexToAddress(address)) + if err != nil { + return err + } + a.EthLinkFeed = ethLinkFeed + return nil +} + +func (a *AutomationTest) DeployGasFeed() error { + gasFeed, err := a.Deployer.DeployMockGasFeed(a.RegistrySettings.FallbackGasPrice) + if err != nil { + return err + } + a.GasFeed = gasFeed + err = a.ChainClient.WaitForEvents() + if err != nil { + return errors.Join(err, fmt.Errorf("failed waiting for mock gas feed to deploy")) + } + return nil +} + +func (a *AutomationTest) LoadEthGasFeed(address string) error { + gasFeed, err := a.Deployer.LoadGasFeed(common.HexToAddress(address)) + if err != nil { + return err + } + a.GasFeed = gasFeed + return nil +} + +func (a *AutomationTest) DeployRegistry() error { + registryOpts := &contracts.KeeperRegistryOpts{ + RegistryVersion: a.RegistrySettings.RegistryVersion, + LinkAddr: a.LinkToken.Address(), + ETHFeedAddr: a.EthLinkFeed.Address(), + GasFeedAddr: a.GasFeed.Address(), + TranscoderAddr: a.Transcoder.Address(), + RegistrarAddr: utils.ZeroAddress.Hex(), + Settings: a.RegistrySettings, + } + registry, err := a.Deployer.DeployKeeperRegistry(registryOpts) + if err != nil { + return err + } + a.Registry = registry + err = a.ChainClient.WaitForEvents() + if err != nil { + return errors.Join(err, fmt.Errorf("failed waiting for registry contract to deploy")) + } + return nil +} + +func (a *AutomationTest) LoadRegistry(address string) error { + registry, err := a.Deployer.LoadKeeperRegistry(common.HexToAddress(address), a.RegistrySettings.RegistryVersion) + if err != nil { + return err + } + a.Registry = registry + return nil +} + +func (a *AutomationTest) DeployRegistrar() error { + if a.Registry == nil { + return fmt.Errorf("registry must be deployed or loaded before registrar") + } + a.RegistrarSettings.RegistryAddr = a.Registry.Address() + registrar, err := a.Deployer.DeployKeeperRegistrar(a.RegistrySettings.RegistryVersion, a.LinkToken.Address(), a.RegistrarSettings) + if err != nil { + return err + } + a.Registrar = registrar + err = a.ChainClient.WaitForEvents() + if err != nil { + return errors.Join(err, fmt.Errorf("failed waiting for registrar contract to deploy")) + } + return nil +} + +func (a *AutomationTest) LoadRegistrar(address string) error { + if a.Registry == nil { + return fmt.Errorf("registry must be deployed or loaded before registrar") + } + a.RegistrarSettings.RegistryAddr = a.Registry.Address() + registrar, err := a.Deployer.LoadKeeperRegistrar(common.HexToAddress(address), a.RegistrySettings.RegistryVersion) + if err != nil { + return err + } + a.Registrar = registrar + return nil +} + +func (a *AutomationTest) CollectNodeDetails() error { + var ( + nodes []*client.ChainlinkClient + ) + if a.IsOnk8s { + for _, node := range a.ChainlinkNodesk8s[:] { + nodes = append(nodes, node.ChainlinkClient) + } + a.ChainlinkNodes = nodes + } else { + nodes = a.ChainlinkNodes[:] + } + + nodeDetails := make([]NodeDetails, 0) + + for i, node := range nodes { + nodeDetail := NodeDetails{} + P2PIds, err := node.MustReadP2PKeys() + if err != nil { + return errors.Join(err, fmt.Errorf("failed to read P2P keys from node %d", i)) + } + nodeDetail.P2PId = P2PIds.Data[0].Attributes.PeerID + + OCR2Keys, err := node.MustReadOCR2Keys() + if err != nil { + return errors.Join(err, fmt.Errorf("failed to read OCR2 keys from node %d", i)) + } + for _, key := range OCR2Keys.Data { + if key.Attributes.ChainType == string(chaintype.EVM) { + nodeDetail.OCR2ConfigPublicKey = key.Attributes.ConfigPublicKey + nodeDetail.OCR2OffchainPublicKey = key.Attributes.OffChainPublicKey + nodeDetail.OCR2OnChainPublicKey = key.Attributes.OnChainPublicKey + nodeDetail.OCR2Id = key.ID + break + } + } + + TransmitterKeys, err := node.EthAddressesForChain(a.ChainClient.GetChainID().String()) + nodeDetail.TransmitterAddresses = make([]string, 0) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to read Transmitter keys from node %d", i)) + } + nodeDetail.TransmitterAddresses = append(nodeDetail.TransmitterAddresses, TransmitterKeys...) + nodeDetails = append(nodeDetails, nodeDetail) + } + a.NodeDetails = nodeDetails + + if a.IsOnk8s { + a.DefaultP2Pv2Bootstrapper = fmt.Sprintf("%s@%s-node-1:%d", a.NodeDetails[0].P2PId, a.ChainlinkNodesk8s[0].Name(), 6690) + } else { + a.DefaultP2Pv2Bootstrapper = fmt.Sprintf("%s@%s:%d", a.NodeDetails[0].P2PId, a.ChainlinkNodes[0].InternalIP(), 6690) + } + return nil +} + +func (a *AutomationTest) AddBootstrapJob() error { + bootstrapSpec := &client.OCR2TaskJobSpec{ + Name: "ocr2 bootstrap node " + a.Registry.Address(), + JobType: "bootstrap", + OCR2OracleSpec: job.OCR2OracleSpec{ + ContractID: a.Registry.Address(), + Relay: "evm", + RelayConfig: map[string]interface{}{ + "chainID": int(a.ChainClient.GetChainID().Int64()), + }, + ContractConfigTrackerPollInterval: *models.NewInterval(time.Second * 15), + }, + } + _, err := a.ChainlinkNodes[0].MustCreateJob(bootstrapSpec) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to create bootstrap job on bootstrap node")) + } + return nil +} + +func (a *AutomationTest) AddAutomationJobs() error { + var contractVersion string + if a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_1 { + contractVersion = "v2.1" + } else if a.RegistrySettings.RegistryVersion == ethereum.RegistryVersion_2_0 { + contractVersion = "v2.0" + } else { + return fmt.Errorf("v2.0 and v2.1 are the only supported versions") + } + for i := 1; i < len(a.ChainlinkNodes); i++ { + autoOCR2JobSpec := client.OCR2TaskJobSpec{ + Name: "automation-" + contractVersion + "-" + a.Registry.Address(), + JobType: "offchainreporting2", + OCR2OracleSpec: job.OCR2OracleSpec{ + PluginType: "ocr2automation", + ContractID: a.Registry.Address(), + Relay: "evm", + RelayConfig: map[string]interface{}{ + "chainID": int(a.ChainClient.GetChainID().Int64()), + }, + PluginConfig: map[string]interface{}{ + "mercuryCredentialName": "\"" + a.MercuryCredentialName + "\"", + "contractVersion": "\"" + contractVersion + "\"", + }, + ContractConfigTrackerPollInterval: *models.NewInterval(time.Second * 15), + TransmitterID: null.StringFrom(a.NodeDetails[i].TransmitterAddresses[a.TransmitterKeyIndex]), + P2PV2Bootstrappers: pq.StringArray{a.DefaultP2Pv2Bootstrapper}, + OCRKeyBundleID: null.StringFrom(a.NodeDetails[i].OCR2Id), + }, + } + _, err := a.ChainlinkNodes[i].MustCreateJob(&autoOCR2JobSpec) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to create OCR2 job on node %d", i+1)) + } + } + return nil +} + +func (a *AutomationTest) SetConfigOnRegistry() error { + donNodes := a.NodeDetails[1:] + S := make([]int, len(donNodes)) + oracleIdentities := make([]confighelper.OracleIdentityExtra, len(donNodes)) + var offC []byte + var signerOnchainPublicKeys []types.OnchainPublicKey + var transmitterAccounts []types.Account + var f uint8 + var offchainConfigVersion uint64 + var offchainConfig []byte + sharedSecretEncryptionPublicKeys := make([]types.ConfigEncryptionPublicKey, len(donNodes)) + eg := &errgroup.Group{} + for i, donNode := range donNodes { + index, chainlinkNode := i, donNode + eg.Go(func() error { + offchainPkBytes, err := hex.DecodeString(strings.TrimPrefix(chainlinkNode.OCR2OffchainPublicKey, "ocr2off_evm_")) + if err != nil { + return err + } + + offchainPkBytesFixed := [ed25519.PublicKeySize]byte{} + n := copy(offchainPkBytesFixed[:], offchainPkBytes) + if n != ed25519.PublicKeySize { + return fmt.Errorf("wrong number of elements copied") + } + + configPkBytes, err := hex.DecodeString(strings.TrimPrefix(chainlinkNode.OCR2ConfigPublicKey, "ocr2cfg_evm_")) + if err != nil { + return err + } + + configPkBytesFixed := [ed25519.PublicKeySize]byte{} + n = copy(configPkBytesFixed[:], configPkBytes) + if n != ed25519.PublicKeySize { + return fmt.Errorf("wrong number of elements copied") + } + + onchainPkBytes, err := hex.DecodeString(strings.TrimPrefix(chainlinkNode.OCR2OnChainPublicKey, "ocr2on_evm_")) + if err != nil { + return err + } + + sharedSecretEncryptionPublicKeys[index] = configPkBytesFixed + oracleIdentities[index] = confighelper.OracleIdentityExtra{ + OracleIdentity: confighelper.OracleIdentity{ + OnchainPublicKey: onchainPkBytes, + OffchainPublicKey: offchainPkBytesFixed, + PeerID: chainlinkNode.P2PId, + TransmitAccount: types.Account(chainlinkNode.TransmitterAddresses[a.TransmitterKeyIndex]), + }, + ConfigEncryptionPublicKey: configPkBytesFixed, + } + S[index] = 1 + return nil + }) + } + err := eg.Wait() + if err != nil { + return errors.Join(err, fmt.Errorf("failed to build oracle identities")) + } + + switch a.RegistrySettings.RegistryVersion { + case ethereum.RegistryVersion_2_0: + offC, err = json.Marshal(ocr2keepers20config.OffchainConfig{ + TargetProbability: a.PluginConfig.TargetProbability, + TargetInRounds: a.PluginConfig.TargetInRounds, + PerformLockoutWindow: a.PluginConfig.PerformLockoutWindow, + GasLimitPerReport: a.PluginConfig.GasLimitPerReport, + GasOverheadPerUpkeep: a.PluginConfig.GasOverheadPerUpkeep, + MinConfirmations: a.PluginConfig.MinConfirmations, + MaxUpkeepBatchSize: a.PluginConfig.MaxUpkeepBatchSize, + }) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to marshal plugin config")) + } + + signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = ocr2.ContractSetConfigArgsForTests( + a.PublicConfig.DeltaProgress, a.PublicConfig.DeltaResend, + a.PublicConfig.DeltaRound, a.PublicConfig.DeltaGrace, + a.PublicConfig.DeltaStage, uint8(a.PublicConfig.RMax), + S, oracleIdentities, offC, + a.PublicConfig.MaxDurationQuery, a.PublicConfig.MaxDurationObservation, + 1200*time.Millisecond, + a.PublicConfig.MaxDurationShouldAcceptAttestedReport, + a.PublicConfig.MaxDurationShouldTransmitAcceptedReport, + a.PublicConfig.F, a.PublicConfig.OnchainConfig, + ) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to build config args")) + } + + case ethereum.RegistryVersion_2_1: + offC, err = json.Marshal(ocr2keepers30config.OffchainConfig{ + TargetProbability: a.PluginConfig.TargetProbability, + TargetInRounds: a.PluginConfig.TargetInRounds, + PerformLockoutWindow: a.PluginConfig.PerformLockoutWindow, + GasLimitPerReport: a.PluginConfig.GasLimitPerReport, + GasOverheadPerUpkeep: a.PluginConfig.GasOverheadPerUpkeep, + MinConfirmations: a.PluginConfig.MinConfirmations, + MaxUpkeepBatchSize: a.PluginConfig.MaxUpkeepBatchSize, + }) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to marshal plugin config")) + } + + signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err = ocr3.ContractSetConfigArgsForTests( + a.PublicConfig.DeltaProgress, a.PublicConfig.DeltaResend, a.PublicConfig.DeltaInitial, + a.PublicConfig.DeltaRound, a.PublicConfig.DeltaGrace, a.PublicConfig.DeltaCertifiedCommitRequest, + a.PublicConfig.DeltaStage, a.PublicConfig.RMax, + S, oracleIdentities, offC, + a.PublicConfig.MaxDurationQuery, a.PublicConfig.MaxDurationObservation, + a.PublicConfig.MaxDurationShouldAcceptAttestedReport, + a.PublicConfig.MaxDurationShouldTransmitAcceptedReport, + a.PublicConfig.F, a.PublicConfig.OnchainConfig, + ) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to build config args")) + } + default: + return fmt.Errorf("v2.0 and v2.1 are the only supported versions") + } + + var signers []common.Address + for _, signer := range signerOnchainPublicKeys { + if len(signer) != 20 { + return fmt.Errorf("OnChainPublicKey '%v' has wrong length for address", signer) + } + signers = append(signers, common.BytesToAddress(signer)) + } + + var transmitters []common.Address + for _, transmitter := range transmitterAccounts { + if !common.IsHexAddress(string(transmitter)) { + return fmt.Errorf("TransmitAccount '%s' is not a valid Ethereum address", string(transmitter)) + } + transmitters = append(transmitters, common.HexToAddress(string(transmitter))) + } + + onchainConfig, err := a.RegistrySettings.EncodeOnChainConfig(a.Registrar.Address(), a.UpkeepPrivilegeManager) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to encode onchain config")) + } + + ocrConfig := contracts.OCRv2Config{ + Signers: signers, + Transmitters: transmitters, + F: f, + OnchainConfig: onchainConfig, + OffchainConfigVersion: offchainConfigVersion, + OffchainConfig: offchainConfig, + } + + err = a.Registry.SetConfig(a.RegistrySettings, ocrConfig) + if err != nil { + return errors.Join(err, fmt.Errorf("failed to set config on registry")) + } + return nil +} + +func (a *AutomationTest) RegisterUpkeeps(upkeepConfigs []UpkeepConfig) ([]common.Hash, error) { + var registrarABI *abi.ABI + var err error + var registrationRequest []byte + registrationTxHashes := make([]common.Hash, 0) + + for _, upkeepConfig := range upkeepConfigs { + switch a.RegistrySettings.RegistryVersion { + case ethereum.RegistryVersion_2_0: + registrarABI, err = keeper_registrar_wrapper2_0.KeeperRegistrarMetaData.GetAbi() + if err != nil { + return nil, errors.Join(err, fmt.Errorf("failed to get registrar abi")) + } + registrationRequest, err = registrarABI.Pack( + "register", upkeepConfig.UpkeepName, upkeepConfig.EncryptedEmail, + upkeepConfig.UpkeepContract, upkeepConfig.GasLimit, upkeepConfig.AdminAddress, + upkeepConfig.CheckData, + upkeepConfig.OffchainConfig, upkeepConfig.FundingAmount, + common.HexToAddress(a.ChainClient.GetDefaultWallet().Address())) + if err != nil { + return nil, errors.Join(err, fmt.Errorf("failed to pack registrar request")) + } + case ethereum.RegistryVersion_2_1: + registrarABI, err = automation_registrar_wrapper2_1.AutomationRegistrarMetaData.GetAbi() + if err != nil { + return nil, errors.Join(err, fmt.Errorf("failed to get registrar abi")) + } + registrationRequest, err = registrarABI.Pack( + "register", upkeepConfig.UpkeepName, upkeepConfig.EncryptedEmail, + upkeepConfig.UpkeepContract, upkeepConfig.GasLimit, upkeepConfig.AdminAddress, + upkeepConfig.TriggerType, upkeepConfig.CheckData, upkeepConfig.TriggerConfig, + upkeepConfig.OffchainConfig, upkeepConfig.FundingAmount, + common.HexToAddress(a.ChainClient.GetDefaultWallet().Address())) + if err != nil { + return nil, errors.Join(err, fmt.Errorf("failed to pack registrar request")) + } + default: + return nil, fmt.Errorf("v2.0 and v2.1 are the only supported versions") + } + tx, err := a.LinkToken.TransferAndCall(a.Registrar.Address(), upkeepConfig.FundingAmount, registrationRequest) + if err != nil { + return nil, errors.Join(err, fmt.Errorf("failed to register upkeep")) + } + registrationTxHashes = append(registrationTxHashes, tx.Hash()) + } + return registrationTxHashes, nil +} + +func (a *AutomationTest) ConfirmUpkeepsRegistered(registrationTxHashes []common.Hash) ([]*big.Int, error) { + upkeepIds := make([]*big.Int, 0) + for _, txHash := range registrationTxHashes { + receipt, err := a.ChainClient.GetTxReceipt(txHash) + if err != nil { + return nil, errors.Join(err, fmt.Errorf("failed to confirm upkeep registration")) + } + var upkeepId *big.Int + for _, rawLog := range receipt.Logs { + parsedUpkeepId, err := a.Registry.ParseUpkeepIdFromRegisteredLog(rawLog) + if err == nil { + upkeepId = parsedUpkeepId + break + } + } + if upkeepId == nil { + return nil, fmt.Errorf("failed to parse upkeep id from registration receipt") + } + upkeepIds = append(upkeepIds, upkeepId) + } + a.UpkeepIDs = upkeepIds + return upkeepIds, nil +} + +func (a *AutomationTest) AddJobsAndSetConfig(t *testing.T) { + l := logging.GetTestLogger(t) + err := a.AddBootstrapJob() + require.NoError(t, err, "Error adding bootstrap job") + err = a.AddAutomationJobs() + require.NoError(t, err, "Error adding automation jobs") + + l.Debug(). + Interface("Plugin Config", a.PluginConfig). + Interface("Public Config", a.PublicConfig). + Interface("Registry Settings", a.RegistrySettings). + Interface("Registrar Settings", a.RegistrarSettings). + Msg("Configuring registry") + err = a.SetConfigOnRegistry() + require.NoError(t, err, "Error setting config on registry") + l.Info().Str("Registry Address", a.Registry.Address()).Msg("Successfully setConfig on registry") +} + +func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { + l := logging.GetTestLogger(t) + err := a.CollectNodeDetails() + require.NoError(t, err, "Error collecting node details") + l.Info().Msg("Collected Node Details") + l.Debug().Interface("Node Details", a.NodeDetails).Msg("Node Details") + + err = a.DeployLINK() + require.NoError(t, err, "Error deploying link token contract") + + err = a.DeployEthLinkFeed() + require.NoError(t, err, "Error deploying eth link feed contract") + err = a.DeployGasFeed() + require.NoError(t, err, "Error deploying gas feed contract") + + err = a.DeployTranscoder() + require.NoError(t, err, "Error deploying transcoder contract") + + err = a.DeployRegistry() + require.NoError(t, err, "Error deploying registry contract") + err = a.DeployRegistrar() + require.NoError(t, err, "Error deploying registrar contract") + + a.AddJobsAndSetConfig(t) +} + +func (a *AutomationTest) LoadAutomationDeployment(t *testing.T, linkTokenAddress, + ethLinkFeedAddress, gasFeedAddress, transcoderAddress, registryAddress, registrarAddress string) { + l := logging.GetTestLogger(t) + err := a.CollectNodeDetails() + require.NoError(t, err, "Error collecting node details") + l.Info().Msg("Collected Node Details") + l.Debug().Interface("Node Details", a.NodeDetails).Msg("Node Details") + + err = a.LoadLINK(linkTokenAddress) + require.NoError(t, err, "Error loading link token contract") + + err = a.LoadEthLinkFeed(ethLinkFeedAddress) + require.NoError(t, err, "Error loading eth link feed contract") + err = a.LoadEthGasFeed(gasFeedAddress) + require.NoError(t, err, "Error loading gas feed contract") + err = a.LoadTranscoder(transcoderAddress) + require.NoError(t, err, "Error loading transcoder contract") + err = a.LoadRegistry(registryAddress) + require.NoError(t, err, "Error loading registry contract") + err = a.LoadRegistrar(registrarAddress) + require.NoError(t, err, "Error loading registrar contract") + + a.AddJobsAndSetConfig(t) + +} diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go index 000fe7b2b81..528f07ec68e 100644 --- a/integration-tests/contracts/contract_deployer.go +++ b/integration-tests/contracts/contract_deployer.go @@ -89,6 +89,7 @@ type ContractDeployer interface { DeployKeeperRegistrar(registryVersion eth_contracts.KeeperRegistryVersion, linkAddr string, registrarSettings KeeperRegistrarSettings) (KeeperRegistrar, error) LoadKeeperRegistrar(address common.Address, registryVersion eth_contracts.KeeperRegistryVersion) (KeeperRegistrar, error) DeployUpkeepTranscoder() (UpkeepTranscoder, error) + LoadUpkeepTranscoder(address common.Address) (UpkeepTranscoder, error) DeployKeeperRegistry(opts *KeeperRegistryOpts) (KeeperRegistry, error) LoadKeeperRegistry(address common.Address, registryVersion eth_contracts.KeeperRegistryVersion) (KeeperRegistry, error) DeployKeeperConsumer(updateInterval *big.Int) (KeeperConsumer, error) @@ -763,6 +764,25 @@ func (e *EthereumContractDeployer) DeployUpkeepTranscoder() (UpkeepTranscoder, e }, err } +func (e *EthereumContractDeployer) LoadUpkeepTranscoder(address common.Address) (UpkeepTranscoder, error) { + instance, err := e.client.LoadContract("UpkeepTranscoder", address, func( + address common.Address, + backend bind.ContractBackend, + ) (interface{}, error) { + return upkeep_transcoder.NewUpkeepTranscoder(address, backend) + }) + + if err != nil { + return nil, err + } + + return &EthereumUpkeepTranscoder{ + client: e.client, + transcoder: instance.(*upkeep_transcoder.UpkeepTranscoder), + address: &address, + }, err +} + func (e *EthereumContractDeployer) DeployKeeperRegistrar(registryVersion eth_contracts.KeeperRegistryVersion, linkAddr string, registrarSettings KeeperRegistrarSettings) (KeeperRegistrar, error) { @@ -1192,6 +1212,7 @@ func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, re address: &address, client: e.client, registry1_1: instance.(*keeper_registry_wrapper1_1.KeeperRegistry), + version: registryVersion, }, err case eth_contracts.RegistryVersion_1_2: instance, err := e.client.LoadContract("KeeperRegistry", address, func( @@ -1207,6 +1228,7 @@ func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, re address: &address, client: e.client, registry1_2: instance.(*keeper_registry_wrapper1_2.KeeperRegistry), + version: registryVersion, }, err case eth_contracts.RegistryVersion_1_3: instance, err := e.client.LoadContract("KeeperRegistry", address, func( @@ -1222,6 +1244,7 @@ func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, re address: &address, client: e.client, registry1_3: instance.(*keeper_registry_wrapper1_3.KeeperRegistry), + version: registryVersion, }, err case eth_contracts.RegistryVersion_2_0: instance, err := e.client.LoadContract("KeeperRegistry", address, func( @@ -1237,6 +1260,7 @@ func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, re address: &address, client: e.client, registry2_0: instance.(*keeper_registry_wrapper2_0.KeeperRegistry), + version: registryVersion, }, err case eth_contracts.RegistryVersion_2_1: instance, err := e.client.LoadContract("KeeperRegistry", address, func( @@ -1252,6 +1276,7 @@ func (e *EthereumContractDeployer) LoadKeeperRegistry(address common.Address, re address: &address, client: e.client, registry2_1: instance.(*iregistry21.IKeeperRegistryMaster), + version: registryVersion, }, err default: return nil, fmt.Errorf("keeper registry version %d is not supported", registryVersion) From d5b9c44fe6d61eb39ebfb29f24afbfd2915ad3a0 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Thu, 23 Nov 2023 19:46:04 +0200 Subject: [PATCH 195/271] VRF-745: refactor VRF v2 tests to match VRF v2 Plus setup (#11257) * VRF-745: refactor VRF v2 tests to match VRF v2 Plus setup * VRF-745: removing decoding tx output * VRF-745: fixing test * VRF-745: fixing Owner Canceling Sub test * VRF-745: adding VRF V2 GH Actions workflow for WASP load test * VRF-745: adding ARBITRUM_SEPOLIA to VRF Load test GH Action workflows * VRF-745: fund CL sending keys if needed in WASP test * VRF-699: downgrading wasp version * polishing up and refactoring minor issues * reverting package name rename * fixing import * fixing lint issues * fixing lint issues * removing unnecesary parameter * fixing lint --- .../on-demand-vrfv2-performance-test.yml | 136 ++++ .../on-demand-vrfv2plus-performance-test.yml | 7 +- .../vrfv2_actions/vrfv2_config/config.go | 54 ++ .../vrfv2_constants/constants.go | 34 - .../actions/vrfv2_actions/vrfv2_models.go | 20 +- .../actions/vrfv2_actions/vrfv2_steps.go | 620 ++++++++++++++---- .../vrfv2plus/vrfv2plus_config/config.go | 1 + .../actions/vrfv2plus/vrfv2plus_steps.go | 323 +++++---- .../contracts/contract_loader.go | 40 ++ .../contracts/contract_vrf_models.go | 8 +- .../contracts/ethereum_vrfv2_contracts.go | 110 +++- integration-tests/load/vrfv2/config.go | 138 +++- integration-tests/load/vrfv2/config.toml | 72 +- integration-tests/load/vrfv2/gun.go | 66 +- .../load/vrfv2/onchain_monitoring.go | 53 +- integration-tests/load/vrfv2/vrfv2_test.go | 384 +++++++++-- integration-tests/load/vrfv2/vu.go | 94 --- integration-tests/smoke/vrfv2_test.go | 156 +++-- integration-tests/smoke/vrfv2plus_test.go | 18 +- integration-tests/testreporters/vrfv2.go | 201 ++---- integration-tests/testreporters/vrfv2plus.go | 4 +- integration-tests/testsetups/vrfv2.go | 204 ------ integration-tests/types/config/node/core.go | 5 +- 23 files changed, 1738 insertions(+), 1010 deletions(-) create mode 100644 .github/workflows/on-demand-vrfv2-performance-test.yml create mode 100644 integration-tests/actions/vrfv2_actions/vrfv2_config/config.go delete mode 100644 integration-tests/actions/vrfv2_actions/vrfv2_constants/constants.go delete mode 100644 integration-tests/load/vrfv2/vu.go delete mode 100644 integration-tests/testsetups/vrfv2.go diff --git a/.github/workflows/on-demand-vrfv2-performance-test.yml b/.github/workflows/on-demand-vrfv2-performance-test.yml new file mode 100644 index 00000000000..100fdf73e61 --- /dev/null +++ b/.github/workflows/on-demand-vrfv2-performance-test.yml @@ -0,0 +1,136 @@ +name: On Demand VRFV2 Performance Test +on: + workflow_dispatch: + inputs: + network: + description: Network to run tests on + type: choice + options: + - "ETHEREUM_MAINNET" + - "SIMULATED" + - "SEPOLIA" + - "OPTIMISM_MAINNET" + - "OPTIMISM_GOERLI" + - "ARBITRUM_MAINNET" + - "ARBITRUM_GOERLI" + - "ARBITRUM_SEPOLIA" + - "BSC_MAINNET" + - "BSC_TESTNET" + - "POLYGON_MAINNET" + - "POLYGON_MUMBAI" + - "AVALANCHE_FUJI" + - "AVALANCHE_MAINNET" + fundingPrivateKey: + description: Private funding key (Skip for Simulated) + required: false + type: string + wsURL: + description: WS URL for the network (Skip for Simulated) + required: false + type: string + httpURL: + description: HTTP URL for the network (Skip for Simulated) + required: false + type: string + chainlinkImage: + description: Container image location for the Chainlink nodes + required: true + default: public.ecr.aws/chainlink/chainlink + chainlinkVersion: + description: Container image version for the Chainlink nodes + required: true + default: "2.6.0" + performanceTestType: + description: Performance Test Type of test to run + type: choice + options: + - "Soak" + - "Load" + - "Stress" + - "Spike" + testDuration: + description: Duration of the test (time string) + required: true + default: 5m + useExistingEnv: + description: Set `true` to use existing environment or `false` to deploy CL node and all contracts + required: false + default: false + configBase64: + description: TOML config in base64 (Needed when overriding config or providing contract addresses for existing env) + required: false +jobs: + vrfv2_performance_test: + name: ${{ inputs.network }} VRFV2 Performance Test + environment: integration + runs-on: ubuntu20.04-8cores-32GB + permissions: + checks: write + pull-requests: write + id-token: write + contents: read + env: + LOKI_URL: ${{ secrets.LOKI_URL }} + LOKI_TOKEN: ${{ secrets.LOKI_TOKEN }} + SELECTED_NETWORKS: ${{ inputs.network }} + TEST_TYPE: ${{ inputs.performanceTestType }} + VRFV2_TEST_DURATION: ${{ inputs.testDuration }} + VRFV2_USE_EXISTING_ENV: ${{ inputs.useExistingEnv }} + CONFIG: ${{ inputs.configBase64 }} + TEST_LOG_LEVEL: debug + REF_NAME: ${{ github.head_ref || github.ref_name }} + CHAINLINK_IMAGE: ${{ inputs.chainlinkImage }} + CHAINLINK_VERSION: ${{ inputs.chainlinkVersion }} + SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }} + SLACK_CHANNEL: ${{ secrets.QA_VRF_SLACK_CHANNEL }} + WASP_LOG_LEVEL: info + steps: + - name: Collect Metrics + 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: ${{ inputs.network }} VRFV2 Performance Test + continue-on-error: true + - name: Setup Push Tag + shell: bash + run: | + echo "### chainlink image used for this test run :link:" >>$GITHUB_STEP_SUMMARY + echo "\`${{ inputs.chainlinkVersion }}\`" >>$GITHUB_STEP_SUMMARY + echo "### chainlink-tests image tag for this test run :ship:" >>$GITHUB_STEP_SUMMARY + echo "\`${GITHUB_SHA}\`" >>$GITHUB_STEP_SUMMARY + - name: Get Inputs + run: | + EVM_URLS=$(jq -r '.inputs.wsURL' $GITHUB_EVENT_PATH) + EVM_HTTP_URLS=$(jq -r '.inputs.httpURL' $GITHUB_EVENT_PATH) + EVM_KEYS=$(jq -r '.inputs.fundingPrivateKey' $GITHUB_EVENT_PATH) + + echo ::add-mask::$EVM_URLS + echo ::add-mask::$EVM_HTTP_URLS + echo ::add-mask::$EVM_KEYS + + echo EVM_URLS=$EVM_URLS >> $GITHUB_ENV + echo EVM_HTTP_URLS=$EVM_HTTP_URLS >> $GITHUB_ENV + echo EVM_KEYS=$EVM_KEYS >> $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + - name: Run Tests + uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@7d541cbbca52d45b8a718257af86d9cf49774d1f # v2.2.15 + with: + test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 24h -run TestVRFV2Performance/vrfv2_performance_test ./load/vrfv2 + test_download_vendor_packages_command: cd ./integration-tests && go mod download + cl_repo: ${{ inputs.chainlinkImage }} + cl_image_tag: ${{ inputs.chainlinkVersion }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + artifacts_name: vrf-test-logs + artifacts_location: ./integration-tests/load/vrfv2/logs/ + token: ${{ secrets.GITHUB_TOKEN }} + go_mod_path: ./integration-tests/go.mod + should_cleanup: false + QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} + QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} diff --git a/.github/workflows/on-demand-vrfv2plus-performance-test.yml b/.github/workflows/on-demand-vrfv2plus-performance-test.yml index 2e765ab79bd..238f40de882 100644 --- a/.github/workflows/on-demand-vrfv2plus-performance-test.yml +++ b/.github/workflows/on-demand-vrfv2plus-performance-test.yml @@ -13,10 +13,11 @@ on: - "OPTIMISM_GOERLI" - "ARBITRUM_MAINNET" - "ARBITRUM_GOERLI" + - "ARBITRUM_SEPOLIA" - "BSC_MAINNET" - "BSC_TESTNET" - "POLYGON_MAINNET" - - "MUMBAI" + - "POLYGON_MUMBAI" - "AVALANCHE_FUJI" - "AVALANCHE_MAINNET" fundingPrivateKey: @@ -120,13 +121,13 @@ jobs: - name: Run Tests uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: - test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 6h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus + test_command_to_run: cd ./integration-tests && go test -v -count=1 -timeout 24h -run TestVRFV2PlusPerformance/vrfv2plus_performance_test ./load/vrfv2plus test_download_vendor_packages_command: cd ./integration-tests && go mod download cl_repo: ${{ inputs.chainlinkImage }} cl_image_tag: ${{ inputs.chainlinkVersion }} aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} artifacts_name: vrf-test-logs - artifacts_location: ./integration-tests/load/logs/ + artifacts_location: ./integration-tests/load/vrfv2plus/logs/ token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod should_cleanup: false diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go b/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go new file mode 100644 index 00000000000..aa2ac7f59ea --- /dev/null +++ b/integration-tests/actions/vrfv2_actions/vrfv2_config/config.go @@ -0,0 +1,54 @@ +package vrfv2_config + +import "time" + +type VRFV2Config struct { + ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with + CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"1000"` // Max gas price in GWei for the chainlink node + IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token + LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed + MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator + SubscriptionFundingAmountLink float64 `envconfig:"SUBSCRIPTION_FUNDING_AMOUNT_LINK" default:"5"` // Amount of LINK to fund the subscription with + NumberOfWords uint32 `envconfig:"NUMBER_OF_WORDS" default:"3"` // Number of words to request + CallbackGasLimit uint32 `envconfig:"CALLBACK_GAS_LIMIT" default:"1000000"` // Gas limit for the callback + MaxGasLimitCoordinatorConfig uint32 `envconfig:"MAX_GAS_LIMIT_COORDINATOR_CONFIG" default:"2500000"` // Max gas limit for the VRF Coordinator config + FallbackWeiPerUnitLink int64 `envconfig:"FALLBACK_WEI_PER_UNIT_LINK" default:"60000000000000000"` // Fallback wei per unit LINK for the VRF Coordinator config + StalenessSeconds uint32 `envconfig:"STALENESS_SECONDS" default:"86400"` // Staleness in seconds for the VRF Coordinator config + GasAfterPaymentCalculation uint32 `envconfig:"GAS_AFTER_PAYMENT_CALCULATION" default:"33825"` // Gas after payment calculation for the VRF Coordinator config + FulfillmentFlatFeeLinkPPMTier1 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_1" default:"500"` + FulfillmentFlatFeeLinkPPMTier2 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_2" default:"500"` + FulfillmentFlatFeeLinkPPMTier3 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_3" default:"500"` + FulfillmentFlatFeeLinkPPMTier4 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_4" default:"500"` + FulfillmentFlatFeeLinkPPMTier5 uint32 `envconfig:"FULFILLMENT_FLAT_FEE_LINK_PPM_TIER_5" default:"500"` + ReqsForTier2 int64 `envconfig:"REQS_FOR_TIER_2" default:"0"` + ReqsForTier3 int64 `envconfig:"REQS_FOR_TIER_3" default:"0"` + ReqsForTier4 int64 `envconfig:"REQS_FOR_TIER_4" default:"0"` + ReqsForTier5 int64 `envconfig:"REQS_FOR_TIER_5" default:"0"` + + NumberOfSubToCreate int `envconfig:"NUMBER_OF_SUB_TO_CREATE" default:"1"` // Number of subscriptions to create + + RandomnessRequestCountPerRequest uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` // How many randomness requests to send per request + RandomnessRequestCountPerRequestDeviation uint16 `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST_DEVIATION" default:"0"` // How many randomness requests to send per request + + RandomWordsFulfilledEventTimeout time.Duration `envconfig:"RANDOM_WORDS_FULFILLED_EVENT_TIMEOUT" default:"2m"` // How long to wait for the RandomWordsFulfilled event to be emitted + + //Wrapper Config + WrapperGasOverhead uint32 `envconfig:"WRAPPER_GAS_OVERHEAD" default:"50000"` + CoordinatorGasOverhead uint32 `envconfig:"COORDINATOR_GAS_OVERHEAD" default:"52000"` + WrapperPremiumPercentage uint8 `envconfig:"WRAPPER_PREMIUM_PERCENTAGE" default:"25"` + WrapperMaxNumberOfWords uint8 `envconfig:"WRAPPER_MAX_NUMBER_OF_WORDS" default:"10"` + WrapperConsumerFundingAmountNativeToken float64 `envconfig:"WRAPPER_CONSUMER_FUNDING_AMOUNT_NATIVE_TOKEN" default:"1"` + WrapperConsumerFundingAmountLink int64 `envconfig:"WRAPPER_CONSUMER_FUNDING_AMOUNT_LINK" default:"10"` + + //LOAD/SOAK Test Config + TestDuration time.Duration `envconfig:"TEST_DURATION" default:"3m"` // How long to run the test for + RPS int64 `envconfig:"RPS" default:"1"` // How many requests per second to send + RateLimitUnitDuration time.Duration `envconfig:"RATE_LIMIT_UNIT_DURATION" default:"1m"` + //Using existing environment and contracts + UseExistingEnv bool `envconfig:"USE_EXISTING_ENV" default:"false"` // Whether to use an existing environment or create a new one + CoordinatorAddress string `envconfig:"COORDINATOR_ADDRESS" default:""` // Coordinator address + ConsumerAddress string `envconfig:"CONSUMER_ADDRESS" default:""` // Consumer address + LinkAddress string `envconfig:"LINK_ADDRESS" default:""` // Link address + SubID uint64 `envconfig:"SUB_ID" default:""` // Subscription ID + KeyHash string `envconfig:"KEY_HASH" default:""` +} diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_constants/constants.go b/integration-tests/actions/vrfv2_actions/vrfv2_constants/constants.go deleted file mode 100644 index 2ec993e4e6e..00000000000 --- a/integration-tests/actions/vrfv2_actions/vrfv2_constants/constants.go +++ /dev/null @@ -1,34 +0,0 @@ -package vrfv2_constants - -import ( - "math/big" - - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" -) - -var ( - LinkEthFeedResponse = big.NewInt(1e18) - MinimumConfirmations = uint16(3) - RandomnessRequestCountPerRequest = uint16(1) - //todo - get Sub id when creating subscription - need to listen for SubscriptionCreated Log - SubID = uint64(1) - VRFSubscriptionFundingAmountLink = big.NewInt(100) - ChainlinkNodeFundingAmountEth = big.NewFloat(0.1) - NumberOfWords = uint32(3) - MaxGasPriceGWei = 1000 - CallbackGasLimit = uint32(1000000) - MaxGasLimitVRFCoordinatorConfig = uint32(2.5e6) - StalenessSeconds = uint32(86400) - GasAfterPaymentCalculation = uint32(33825) - - VRFCoordinatorV2FeeConfig = vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ - FulfillmentFlatFeeLinkPPMTier1: 500, - FulfillmentFlatFeeLinkPPMTier2: 500, - FulfillmentFlatFeeLinkPPMTier3: 500, - FulfillmentFlatFeeLinkPPMTier4: 500, - FulfillmentFlatFeeLinkPPMTier5: 500, - ReqsForTier2: big.NewInt(0), - ReqsForTier3: big.NewInt(0), - ReqsForTier4: big.NewInt(0), - ReqsForTier5: big.NewInt(0)} -) diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_models.go b/integration-tests/actions/vrfv2_actions/vrfv2_models.go index 71d119e1dbb..0576d6f7d6e 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_models.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_models.go @@ -18,7 +18,21 @@ type VRFV2JobInfo struct { } type VRFV2Contracts struct { - Coordinator contracts.VRFCoordinatorV2 - BHS contracts.BlockHashStore - LoadTestConsumer contracts.VRFv2LoadTestConsumer + Coordinator contracts.VRFCoordinatorV2 + BHS contracts.BlockHashStore + LoadTestConsumers []contracts.VRFv2LoadTestConsumer +} + +// VRFV2PlusKeyData defines a jobs into and proving key info +type VRFV2KeyData struct { + VRFKey *client.VRFKey + EncodedProvingKey VRFV2EncodedProvingKey + KeyHash [32]byte +} + +type VRFV2Data struct { + VRFV2KeyData + VRFJob *client.Job + PrimaryEthAddress string + ChainID *big.Int } diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index a832d020b0f..f565af26d93 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -4,6 +4,18 @@ import ( "context" "fmt" "math/big" + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/rs/zerolog" + + commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/google/uuid" @@ -11,27 +23,37 @@ import ( chainlinkutils "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/integration-tests/actions" - vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" ) var ( - ErrNodePrimaryKey = "error getting node's primary ETH key" - ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" - ErrCreatingProvingKey = "error creating a keyHash from the proving key" - ErrRegisterProvingKey = "error registering proving keys" - ErrEncodingProvingKey = "error encoding proving key" - ErrCreatingVRFv2Key = "error creating VRFv2 key" - ErrDeployBlockHashStore = "error deploying blockhash store" - ErrDeployCoordinator = "error deploying VRFv2 CoordinatorV2" - ErrAdvancedConsumer = "error deploying VRFv2 Advanced Consumer" - ErrABIEncodingFunding = "error Abi encoding subscriptionID" - ErrSendingLinkToken = "error sending Link token" - ErrCreatingVRFv2Job = "error creating VRFv2 job" - ErrParseJob = "error parsing job definition" + ErrNodePrimaryKey = "error getting node's primary ETH key" + ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" + ErrRegisteringProvingKey = "error registering a proving key on Coordinator contract" + ErrRegisterProvingKey = "error registering proving keys" + ErrEncodingProvingKey = "error encoding proving key" + ErrCreatingVRFv2Key = "error creating VRFv2 key" + ErrDeployBlockHashStore = "error deploying blockhash store" + ErrDeployCoordinator = "error deploying VRF CoordinatorV2" + ErrAdvancedConsumer = "error deploying VRFv2 Advanced Consumer" + ErrABIEncodingFunding = "error Abi encoding subscriptionID" + ErrSendingLinkToken = "error sending Link token" + ErrCreatingVRFv2Job = "error creating VRFv2 job" + ErrParseJob = "error parsing job definition" + ErrDeployVRFV2Contracts = "error deploying VRFV2 contracts" + ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract" + ErrCreateVRFSubscription = "error creating VRF Subscription" + ErrAddConsumerToSub = "error adding consumer to VRF Subscription" + ErrFundSubWithLinkToken = "error funding subscription with Link tokens" + ErrCreateVRFV2Jobs = "error creating VRF V2 Jobs" + ErrGetPrimaryKey = "error getting primary ETH key address" + ErrRestartCLNode = "error restarting CL node" + ErrWaitTXsComplete = "error waiting for TXs to complete" + ErrRequestRandomness = "error requesting randomness" + + ErrWaitRandomWordsRequestedEvent = "error waiting for RandomWordsRequested event" + ErrWaitRandomWordsFulfilledEvent = "error waiting for RandomWordsFulfilled event" ) func DeployVRFV2Contracts( @@ -39,82 +61,80 @@ func DeployVRFV2Contracts( chainClient blockchain.EVMClient, linkTokenContract contracts.LinkToken, linkEthFeedContract contracts.MockETHLINKFeed, + consumerContractsAmount int, ) (*VRFV2Contracts, error) { bhs, err := contractDeployer.DeployBlockhashStore() if err != nil { return nil, fmt.Errorf("%s, err %w", ErrDeployBlockHashStore, err) } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } coordinator, err := contractDeployer.DeployVRFCoordinatorV2(linkTokenContract.Address(), bhs.Address(), linkEthFeedContract.Address()) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrDeployCoordinator, err) } - loadTestConsumer, err := contractDeployer.DeployVRFv2LoadTestConsumer(coordinator.Address()) + err = chainClient.WaitForEvents() if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = chainClient.WaitForEvents() + consumers, err := DeployVRFV2Consumers(contractDeployer, coordinator, consumerContractsAmount) if err != nil { return nil, err } - return &VRFV2Contracts{coordinator, bhs, loadTestConsumer}, nil + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return &VRFV2Contracts{coordinator, bhs, consumers}, nil } -func CreateVRFV2Jobs( - chainlinkNodes []*client.ChainlinkClient, - coordinator contracts.VRFCoordinatorV2, - c blockchain.EVMClient, - minIncomingConfirmations uint16, -) ([]VRFV2JobInfo, error) { - jobInfo := make([]VRFV2JobInfo, 0) - for _, chainlinkNode := range chainlinkNodes { - vrfKey, err := chainlinkNode.MustCreateVRFKey() +func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinator contracts.VRFCoordinatorV2, consumerContractsAmount int) ([]contracts.VRFv2LoadTestConsumer, error) { + var consumers []contracts.VRFv2LoadTestConsumer + for i := 1; i <= consumerContractsAmount; i++ { + loadTestConsumer, err := contractDeployer.DeployVRFv2LoadTestConsumer(coordinator.Address()) if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Key, err) - } - pubKeyCompressed := vrfKey.Data.ID - jobUUID := uuid.New() - os := &client.VRFV2TxPipelineSpec{ - Address: coordinator.Address(), + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) } - ost, err := os.String() - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) - } - nativeTokenPrimaryKeyAddress, err := chainlinkNode.PrimaryEthAddress() - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) - } - job, err := chainlinkNode.MustCreateJob(&client.VRFV2JobSpec{ - Name: fmt.Sprintf("vrf-%s", jobUUID), - CoordinatorAddress: coordinator.Address(), - FromAddresses: []string{nativeTokenPrimaryKeyAddress}, - EVMChainID: c.GetChainID().String(), - MinIncomingConfirmations: int(minIncomingConfirmations), - PublicKey: pubKeyCompressed, - ExternalJobID: jobUUID.String(), - ObservationSource: ost, - BatchFulfillmentEnabled: false, - }) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) - } - provingKey, err := VRFV2RegisterProvingKey(vrfKey, nativeTokenPrimaryKeyAddress, coordinator) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKey, err) - } - keyHash, err := coordinator.HashOfKey(context.Background(), provingKey) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKeyHash, err) - } - ji := VRFV2JobInfo{ - Job: job, - VRFKey: vrfKey, - EncodedProvingKey: provingKey, - KeyHash: keyHash, - } - jobInfo = append(jobInfo, ji) + consumers = append(consumers, loadTestConsumer) + } + return consumers, nil +} + +func CreateVRFV2Job( + chainlinkNode *client.ChainlinkClient, + coordinatorAddress string, + nativeTokenPrimaryKeyAddress string, + pubKeyCompressed string, + chainID string, + minIncomingConfirmations uint16, +) (*client.Job, error) { + jobUUID := uuid.New() + os := &client.VRFV2TxPipelineSpec{ + Address: coordinatorAddress, + } + ost, err := os.String() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrParseJob, err) + } + + job, err := chainlinkNode.MustCreateJob(&client.VRFV2JobSpec{ + Name: fmt.Sprintf("vrf-v2-%s", jobUUID), + CoordinatorAddress: coordinatorAddress, + FromAddresses: []string{nativeTokenPrimaryKeyAddress}, + EVMChainID: chainID, + MinIncomingConfirmations: int(minIncomingConfirmations), + PublicKey: pubKeyCompressed, + ExternalJobID: jobUUID.String(), + ObservationSource: ost, + BatchFulfillmentEnabled: false, + }) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Job, err) } - return jobInfo, nil + + return job, nil } func VRFV2RegisterProvingKey( @@ -136,98 +156,452 @@ func VRFV2RegisterProvingKey( return provingKey, nil } -func FundVRFCoordinatorV2Subscription(linkToken contracts.LinkToken, coordinator contracts.VRFCoordinatorV2, chainClient blockchain.EVMClient, subscriptionID uint64, linkFundingAmount *big.Int) error { +func FundVRFCoordinatorV2Subscription( + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2, + chainClient blockchain.EVMClient, + subscriptionID uint64, + linkFundingAmountJuels *big.Int, +) error { encodedSubId, err := chainlinkutils.ABIEncode(`[{"type":"uint64"}]`, subscriptionID) if err != nil { return fmt.Errorf("%s, err %w", ErrABIEncodingFunding, err) } - _, err = linkToken.TransferAndCall(coordinator.Address(), big.NewInt(0).Mul(linkFundingAmount, big.NewInt(1e18)), encodedSubId) + _, err = linkToken.TransferAndCall(coordinator.Address(), linkFundingAmountJuels, encodedSubId) if err != nil { return fmt.Errorf("%s, err %w", ErrSendingLinkToken, err) } return chainClient.WaitForEvents() } -/* setup for load tests */ +// SetupVRFV2Environment will create specified number of subscriptions and add the same conumer/s to each of them +func SetupVRFV2Environment( + env *test_env.CLClusterTestEnv, + vrfv2Config vrfv2_config.VRFV2Config, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + registerProvingKeyAgainstAddress string, + numberOfConsumers int, + numberOfSubToCreate int, + l zerolog.Logger, +) (*VRFV2Contracts, []uint64, *VRFV2Data, error) { + l.Info().Msg("Starting VRFV2 environment setup") + l.Info().Msg("Deploying VRFV2 contracts") + vrfv2Contracts, err := DeployVRFV2Contracts( + env.ContractDeployer, + env.EVMClient, + linkToken, + mockNativeLINKFeed, + numberOfConsumers, + ) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrDeployVRFV2Contracts, err) + } + vrfCoordinatorV2FeeConfig := vrf_coordinator_v2.VRFCoordinatorV2FeeConfig{ + FulfillmentFlatFeeLinkPPMTier1: vrfv2Config.FulfillmentFlatFeeLinkPPMTier1, + FulfillmentFlatFeeLinkPPMTier2: vrfv2Config.FulfillmentFlatFeeLinkPPMTier2, + FulfillmentFlatFeeLinkPPMTier3: vrfv2Config.FulfillmentFlatFeeLinkPPMTier3, + FulfillmentFlatFeeLinkPPMTier4: vrfv2Config.FulfillmentFlatFeeLinkPPMTier4, + FulfillmentFlatFeeLinkPPMTier5: vrfv2Config.FulfillmentFlatFeeLinkPPMTier5, + ReqsForTier2: big.NewInt(vrfv2Config.ReqsForTier2), + ReqsForTier3: big.NewInt(vrfv2Config.ReqsForTier3), + ReqsForTier4: big.NewInt(vrfv2Config.ReqsForTier4), + ReqsForTier5: big.NewInt(vrfv2Config.ReqsForTier5)} -func SetupLocalLoadTestEnv(nodesFunding *big.Float, subFundingLINK *big.Int) (*test_env.CLClusterTestEnv, *VRFV2Contracts, [32]byte, error) { - env, err := test_env.NewCLTestEnvBuilder(). - WithGeth(). - WithLogWatcher(). - WithMockAdapter(). - WithCLNodes(1). - WithFunding(nodesFunding). - WithLogWatcher(). - Build() + l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Setting Coordinator Config") + err = vrfv2Contracts.Coordinator.SetConfig( + vrfv2Config.MinimumConfirmations, + vrfv2Config.CallbackGasLimit, + vrfv2Config.StalenessSeconds, + vrfv2Config.GasAfterPaymentCalculation, + big.NewInt(vrfv2Config.LinkNativeFeedResponse), + vrfCoordinatorV2FeeConfig, + ) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrSetVRFCoordinatorConfig, err) + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + l.Info(). + Str("Coordinator", vrfv2Contracts.Coordinator.Address()). + Int("Number of Subs to create", numberOfSubToCreate). + Msg("Creating and funding subscriptions, adding consumers") + subIDs, err := CreateFundSubsAndAddConsumers( + env, + vrfv2Config, + linkToken, + vrfv2Contracts.Coordinator, vrfv2Contracts.LoadTestConsumers, numberOfSubToCreate) if err != nil { - return nil, nil, [32]byte{}, err + return nil, nil, nil, err } - env.ParallelTransactions(true) + l.Info().Str("Node URL", env.ClCluster.NodeAPIs()[0].URL()).Msg("Creating VRF Key on the Node") + vrfKey, err := env.ClCluster.NodeAPIs()[0].MustCreateVRFKey() + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2Key, err) + } + pubKeyCompressed := vrfKey.Data.ID - mockFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, vrfConst.LinkEthFeedResponse) + l.Info().Str("Coordinator", vrfv2Contracts.Coordinator.Address()).Msg("Registering Proving Key") + provingKey, err := VRFV2RegisterProvingKey(vrfKey, registerProvingKeyAgainstAddress, vrfv2Contracts.Coordinator) if err != nil { - return nil, nil, [32]byte{}, err + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRegisteringProvingKey, err) } - lt, err := actions.DeployLINKToken(env.ContractDeployer) + keyHash, err := vrfv2Contracts.Coordinator.HashOfKey(context.Background(), provingKey) if err != nil { - return nil, nil, [32]byte{}, err + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreatingProvingKeyHash, err) } - vrfv2Contracts, err := DeployVRFV2Contracts(env.ContractDeployer, env.EVMClient, lt, mockFeed) + + chainID := env.EVMClient.GetChainID() + + nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() if err != nil { - return nil, nil, [32]byte{}, err + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } - err = env.EVMClient.WaitForEvents() + + l.Info().Msg("Creating VRFV2 Job") + vrfV2job, err := CreateVRFV2Job( + env.ClCluster.NodeAPIs()[0], + vrfv2Contracts.Coordinator.Address(), + nativeTokenPrimaryKeyAddress, + pubKeyCompressed, + chainID.String(), + vrfv2Config.MinimumConfirmations, + ) if err != nil { - return nil, nil, [32]byte{}, err + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrCreateVRFV2Jobs, err) } - err = vrfv2Contracts.Coordinator.SetConfig( - vrfConst.MinimumConfirmations, - vrfConst.MaxGasLimitVRFCoordinatorConfig, - vrfConst.StalenessSeconds, - vrfConst.GasAfterPaymentCalculation, - vrfConst.LinkEthFeedResponse, - vrfConst.VRFCoordinatorV2FeeConfig, + + // this part is here because VRFv2 can work with only a specific key + // [[EVM.KeySpecific]] + // Key = '...' + addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) + } + nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, + node.WithVRFv2EVMEstimator(addr, vrfv2Config.CLNodeMaxGasPriceGWei), ) + l.Info().Msg("Restarting Node with new sending key PriceMax configuration") + err = env.ClCluster.Nodes[0].Restart(nodeConfig) + if err != nil { + return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRestartCLNode, err) + } + + vrfv2KeyData := VRFV2KeyData{ + VRFKey: vrfKey, + EncodedProvingKey: provingKey, + KeyHash: keyHash, + } + + data := VRFV2Data{ + vrfv2KeyData, + vrfV2job, + nativeTokenPrimaryKeyAddress, + chainID, + } + + l.Info().Msg("VRFV2 environment setup is finished") + return vrfv2Contracts, subIDs, &data, nil +} + +func CreateFundSubsAndAddConsumers( + env *test_env.CLClusterTestEnv, + vrfv2Config vrfv2_config.VRFV2Config, + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2, + consumers []contracts.VRFv2LoadTestConsumer, + numberOfSubToCreate int, +) ([]uint64, error) { + subIDs, err := CreateSubsAndFund(env, vrfv2Config, linkToken, coordinator, numberOfSubToCreate) if err != nil { - return nil, nil, [32]byte{}, err + return nil, err + } + subToConsumersMap := map[uint64][]contracts.VRFv2LoadTestConsumer{} + + //each subscription will have the same consumers + for _, subID := range subIDs { + subToConsumersMap[subID] = consumers } + + err = AddConsumersToSubs( + subToConsumersMap, + coordinator, + ) + if err != nil { + return nil, err + } + err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, [32]byte{}, err + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = vrfv2Contracts.Coordinator.CreateSubscription() + return subIDs, nil +} + +func CreateSubsAndFund( + env *test_env.CLClusterTestEnv, + vrfv2Config vrfv2_config.VRFV2Config, + linkToken contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2, + subAmountToCreate int, +) ([]uint64, error) { + subs, err := CreateSubs(env, coordinator, subAmountToCreate) if err != nil { - return nil, nil, [32]byte{}, err + return nil, err } err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, [32]byte{}, err + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - err = vrfv2Contracts.Coordinator.AddConsumer(vrfConst.SubID, vrfv2Contracts.LoadTestConsumer.Address()) + err = FundSubscriptions(env, vrfv2Config, linkToken, coordinator, subs) if err != nil { - return nil, nil, [32]byte{}, err + return nil, err + } + return subs, nil +} + +func CreateSubs( + env *test_env.CLClusterTestEnv, + coordinator contracts.VRFCoordinatorV2, + subAmountToCreate int, +) ([]uint64, error) { + var subIDArr []uint64 + + for i := 0; i < subAmountToCreate; i++ { + subID, err := CreateSubAndFindSubID(env, coordinator) + if err != nil { + return nil, err + } + subIDArr = append(subIDArr, subID) + } + return subIDArr, nil +} + +func AddConsumersToSubs( + subToConsumerMap map[uint64][]contracts.VRFv2LoadTestConsumer, + coordinator contracts.VRFCoordinatorV2, +) error { + for subID, consumers := range subToConsumerMap { + for _, consumer := range consumers { + err := coordinator.AddConsumer(subID, consumer.Address()) + if err != nil { + return fmt.Errorf("%s, err %w", ErrAddConsumerToSub, err) + } + } } - err = FundVRFCoordinatorV2Subscription(lt, vrfv2Contracts.Coordinator, env.EVMClient, vrfConst.SubID, subFundingLINK) + return nil +} + +func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2) (uint64, error) { + tx, err := coordinator.CreateSubscription() if err != nil { - return nil, nil, [32]byte{}, err + return 0, fmt.Errorf("%s, err %w", ErrCreateVRFSubscription, err) } - jobs, err := CreateVRFV2Jobs(env.ClCluster.NodeAPIs(), vrfv2Contracts.Coordinator, env.EVMClient, vrfConst.MinimumConfirmations) + err = env.EVMClient.WaitForEvents() if err != nil { - return nil, nil, [32]byte{}, err + return 0, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - // this part is here because VRFv2 can work with only a specific key - // [[EVM.KeySpecific]] - // Key = '...' - addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() + + receipt, err := env.EVMClient.GetTxReceipt(tx.Hash()) if err != nil { - return nil, nil, [32]byte{}, err + return 0, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr), + + //SubscriptionsCreated Log should be emitted with the subscription ID + subID := receipt.Logs[0].Topics[1].Big().Uint64() + + return subID, nil +} + +func FundSubscriptions( + env *test_env.CLClusterTestEnv, + vrfv2Config vrfv2_config.VRFV2Config, + linkAddress contracts.LinkToken, + coordinator contracts.VRFCoordinatorV2, + subIDs []uint64, +) error { + for _, subID := range subIDs { + //Link Billing + amountJuels := conversions.EtherToWei(big.NewFloat(vrfv2Config.SubscriptionFundingAmountLink)) + err := FundVRFCoordinatorV2Subscription(linkAddress, coordinator, env.EVMClient, subID, amountJuels) + if err != nil { + return fmt.Errorf("%s, err %w", ErrFundSubWithLinkToken, err) + } + } + err := env.EVMClient.WaitForEvents() + if err != nil { + return fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return nil +} + +func RequestRandomnessAndWaitForFulfillment( + consumer contracts.VRFv2LoadTestConsumer, + coordinator contracts.VRFCoordinatorV2, + vrfv2Data *VRFV2Data, + subID uint64, + randomnessRequestCountPerRequest uint16, + vrfv2Config vrfv2_config.VRFV2Config, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { + logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l) + err := consumer.RequestRandomness( + vrfv2Data.KeyHash, + subID, + vrfv2Config.MinimumConfirmations, + vrfv2Config.CallbackGasLimit, + vrfv2Config.NumberOfWords, + randomnessRequestCountPerRequest, ) - err = env.ClCluster.Nodes[0].Restart(nodeConfig) if err != nil { - return nil, nil, [32]byte{}, err + return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) + } + + return WaitForRequestAndFulfillmentEvents( + consumer.Address(), + coordinator, + vrfv2Data, + subID, + randomWordsFulfilledEventTimeout, + l, + ) +} + +func WaitForRequestAndFulfillmentEvents( + consumerAddress string, + coordinator contracts.VRFCoordinatorV2, + vrfv2Data *VRFV2Data, + subID uint64, + randomWordsFulfilledEventTimeout time.Duration, + l zerolog.Logger, +) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { + randomWordsRequestedEvent, err := coordinator.WaitForRandomWordsRequestedEvent( + [][32]byte{vrfv2Data.KeyHash}, + []uint64{subID}, + []common.Address{common.HexToAddress(consumerAddress)}, + time.Minute*1, + ) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsRequestedEvent, err) + } + + LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) + + randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( + []*big.Int{randomWordsRequestedEvent.RequestId}, + randomWordsFulfilledEventTimeout, + ) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitRandomWordsFulfilledEvent, err) + } + + LogRandomWordsFulfilledEvent(l, coordinator, randomWordsFulfilledEvent) + return randomWordsFulfilledEvent, err +} + +func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2LoadTestConsumer, timeout time.Duration, wg *sync.WaitGroup) (*big.Int, *big.Int, error) { + metricsChannel := make(chan *contracts.VRFLoadTestMetrics) + metricsErrorChannel := make(chan error) + + testContext, testCancel := context.WithTimeout(context.Background(), timeout) + defer testCancel() + + ticker := time.NewTicker(time.Second * 1) + var metrics *contracts.VRFLoadTestMetrics + for { + select { + case <-testContext.Done(): + ticker.Stop() + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, + fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", + metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) + case <-ticker.C: + go retrieveLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) + case metrics = <-metricsChannel: + if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { + ticker.Stop() + wg.Done() + return metrics.RequestCount, metrics.FulfilmentCount, nil + } + case err := <-metricsErrorChannel: + ticker.Stop() + wg.Done() + return nil, nil, err + } + } +} + +func retrieveLoadTestMetrics( + consumer contracts.VRFv2LoadTestConsumer, + metricsChannel chan *contracts.VRFLoadTestMetrics, + metricsErrorChannel chan error, +) { + metrics, err := consumer.GetLoadTestMetrics(context.Background()) + if err != nil { + metricsErrorChannel <- err } - return env, vrfv2Contracts, jobs[0].KeyHash, nil + metricsChannel <- metrics +} + +func LogSubDetails(l zerolog.Logger, subscription vrf_coordinator_v2.GetSubscription, subID uint64, coordinator contracts.VRFCoordinatorV2) { + l.Debug(). + Str("Coordinator", coordinator.Address()). + Str("Link Balance", (*commonassets.Link)(subscription.Balance).Link()). + Uint64("Subscription ID", subID). + Str("Subscription Owner", subscription.Owner.String()). + Interface("Subscription Consumers", subscription.Consumers). + Msg("Subscription Data") +} + +func LogRandomnessRequestedEvent( + l zerolog.Logger, + coordinator contracts.VRFCoordinatorV2, + randomWordsRequestedEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, +) { + l.Debug(). + Str("Coordinator", coordinator.Address()). + Str("Request ID", randomWordsRequestedEvent.RequestId.String()). + Uint64("Subscription ID", randomWordsRequestedEvent.SubId). + Str("Sender Address", randomWordsRequestedEvent.Sender.String()). + Interface("Keyhash", randomWordsRequestedEvent.KeyHash). + Uint32("Callback Gas Limit", randomWordsRequestedEvent.CallbackGasLimit). + Uint32("Number of Words", randomWordsRequestedEvent.NumWords). + Uint16("Minimum Request Confirmations", randomWordsRequestedEvent.MinimumRequestConfirmations). + Msg("RandomnessRequested Event") +} + +func LogRandomWordsFulfilledEvent( + l zerolog.Logger, + coordinator contracts.VRFCoordinatorV2, + randomWordsFulfilledEvent *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, +) { + l.Debug(). + Str("Coordinator", coordinator.Address()). + Str("Total Payment", randomWordsFulfilledEvent.Payment.String()). + Str("TX Hash", randomWordsFulfilledEvent.Raw.TxHash.String()). + Str("Request ID", randomWordsFulfilledEvent.RequestId.String()). + Bool("Success", randomWordsFulfilledEvent.Success). + Msg("RandomWordsFulfilled Event (TX metadata)") +} + +func logRandRequest( + consumer string, + coordinator string, + subID uint64, + vrfv2Config vrfv2_config.VRFV2Config, + l zerolog.Logger, +) { + l.Debug(). + Str("Consumer", consumer). + Str("Coordinator", coordinator). + Uint64("SubID", subID). + Uint16("MinimumConfirmations", vrfv2Config.MinimumConfirmations). + Uint32("CallbackGasLimit", vrfv2Config.CallbackGasLimit). + Uint16("RandomnessRequestCountPerRequest", vrfv2Config.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2Config.RandomnessRequestCountPerRequestDeviation). + Msg("Requesting randomness") } diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go index a47103a8a18..8cd6e0a8ce8 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_config/config.go @@ -4,6 +4,7 @@ import "time" type VRFV2PlusConfig struct { ChainlinkNodeFunding float64 `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of native currency to fund each chainlink node with + CLNodeMaxGasPriceGWei int64 `envconfig:"MAX_GAS_PRICE_GWEI" default:"1000"` // Max gas price in GWei for the chainlink node IsNativePayment bool `envconfig:"IS_NATIVE_PAYMENT" default:"false"` // Whether to use native payment or LINK token LinkNativeFeedResponse int64 `envconfig:"LINK_NATIVE_FEED_RESPONSE" default:"1000000000000000000"` // Response of the LINK/ETH feed MinimumConfirmations uint16 `envconfig:"MINIMUM_CONFIRMATIONS" default:"3"` // Minimum number of confirmations for the VRF Coordinator diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index fd15d51af4e..d0a33948e31 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -46,7 +46,6 @@ var ( ErrDeployVRFV2_5Contracts = "error deploying VRFV2_5 contracts" ErrSetVRFCoordinatorConfig = "error setting config for VRF Coordinator contract" ErrCreateVRFSubscription = "error creating VRF Subscription" - ErrFindSubID = "error finding created subscription ID" ErrAddConsumerToSub = "error adding consumer to VRF Subscription" ErrFundSubWithNativeToken = "error funding subscription with native token" ErrSetLinkNativeLinkFeed = "error setting Link and ETH/LINK feed for VRF Coordinator contract" @@ -98,35 +97,6 @@ func DeployVRFV2_5Contracts( return &VRFV2_5Contracts{coordinator, bhs, consumers}, nil } -func DeployVRFV2PlusDirectFundingContracts( - contractDeployer contracts.ContractDeployer, - chainClient blockchain.EVMClient, - linkTokenAddress string, - linkEthFeedAddress string, - coordinator contracts.VRFCoordinatorV2_5, - consumerContractsAmount int, -) (*VRFV2PlusWrapperContracts, error) { - - vrfv2PlusWrapper, err := contractDeployer.DeployVRFV2PlusWrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) - } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - - consumers, err := DeployVRFV2PlusWrapperConsumers(contractDeployer, linkTokenAddress, vrfv2PlusWrapper, consumerContractsAmount) - if err != nil { - return nil, err - } - err = chainClient.WaitForEvents() - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - return &VRFV2PlusWrapperContracts{vrfv2PlusWrapper, consumers}, nil -} - func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coordinator contracts.VRFCoordinatorV2_5, consumerContractsAmount int) ([]contracts.VRFv2PlusLoadTestConsumer, error) { var consumers []contracts.VRFv2PlusLoadTestConsumer for i := 1; i <= consumerContractsAmount; i++ { @@ -139,18 +109,6 @@ func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coord return consumers, nil } -func DeployVRFV2PlusWrapperConsumers(contractDeployer contracts.ContractDeployer, linkTokenAddress string, vrfV2PlusWrapper contracts.VRFV2PlusWrapper, consumerContractsAmount int) ([]contracts.VRFv2PlusWrapperLoadTestConsumer, error) { - var consumers []contracts.VRFv2PlusWrapperLoadTestConsumer - for i := 1; i <= consumerContractsAmount; i++ { - loadTestConsumer, err := contractDeployer.DeployVRFV2PlusWrapperLoadTestConsumer(linkTokenAddress, vrfV2PlusWrapper.Address()) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) - } - consumers = append(consumers, loadTestConsumer) - } - return consumers, nil -} - func CreateVRFV2PlusJob( chainlinkNode *client.ChainlinkClient, coordinatorAddress string, @@ -285,7 +243,10 @@ func SetupVRFV2_5Environment( if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) } - l.Info().Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()).Int("Number of Subs to create", numberOfSubToCreate).Msg("Creating and funding subscriptions, adding consumers") + l.Info(). + Str("Coordinator", vrfv2_5Contracts.Coordinator.Address()). + Int("Number of Subs to create", numberOfSubToCreate). + Msg("Creating and funding subscriptions, adding consumers") subIDs, err := CreateFundSubsAndAddConsumers( env, vrfv2PlusConfig, @@ -339,7 +300,7 @@ func SetupVRFV2_5Environment( return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) } nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr), + node.WithVRFv2EVMEstimator(addr, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -452,93 +413,6 @@ func AddConsumersToSubs( return nil } -func SetupVRFV2PlusWrapperEnvironment( - env *test_env.CLClusterTestEnv, - vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, - linkToken contracts.LinkToken, - mockNativeLINKFeed contracts.MockETHLINKFeed, - coordinator contracts.VRFCoordinatorV2_5, - keyHash [32]byte, - wrapperConsumerContractsAmount int, -) (*VRFV2PlusWrapperContracts, *big.Int, error) { - - wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( - env.ContractDeployer, - env.EVMClient, - linkToken.Address(), - mockNativeLINKFeed.Address(), - coordinator, - wrapperConsumerContractsAmount, - ) - if err != nil { - return nil, nil, err - } - - err = env.EVMClient.WaitForEvents() - - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - err = wrapperContracts.VRFV2PlusWrapper.SetConfig( - vrfv2PlusConfig.WrapperGasOverhead, - vrfv2PlusConfig.CoordinatorGasOverhead, - vrfv2PlusConfig.WrapperPremiumPercentage, - keyHash, - vrfv2PlusConfig.WrapperMaxNumberOfWords, - vrfv2PlusConfig.StalenessSeconds, - big.NewInt(vrfv2PlusConfig.FallbackWeiPerUnitLink), - vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, - vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, - ) - if err != nil { - return nil, nil, err - } - - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - - //fund sub - wrapperSubID, err := wrapperContracts.VRFV2PlusWrapper.GetSubID(context.Background()) - if err != nil { - return nil, nil, err - } - - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - - err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, []*big.Int{wrapperSubID}) - if err != nil { - return nil, nil, err - } - - //fund consumer with Link - err = linkToken.Transfer( - wrapperContracts.LoadTestConsumers[0].Address(), - big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), - ) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - - //fund consumer with Eth - err = wrapperContracts.LoadTestConsumers[0].Fund(big.NewFloat(vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)) - if err != nil { - return nil, nil, err - } - err = env.EVMClient.WaitForEvents() - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) - } - return wrapperContracts, wrapperSubID, nil -} func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts.VRFCoordinatorV2_5) (*big.Int, error) { tx, err := coordinator.CreateSubscription() if err != nil { @@ -557,39 +431,9 @@ func CreateSubAndFindSubID(env *test_env.CLClusterTestEnv, coordinator contracts //SubscriptionsCreated Log should be emitted with the subscription ID subID := receipt.Logs[0].Topics[1].Big() - //verify that the subscription was created - _, err = coordinator.FindSubscriptionID(subID) - if err != nil { - return nil, fmt.Errorf("%s, err %w", ErrFindSubID, err) - } - return subID, nil } -func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { - linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) - } - nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) - } - return -} - -func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { - linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) - } - nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) - if err != nil { - return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) - } - return -} - func FundSubscriptions( env *test_env.CLClusterTestEnv, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, @@ -621,6 +465,30 @@ func FundSubscriptions( return nil } +func GetUpgradedCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2PlusUpgradedVersion) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { + linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) + } + nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) + } + return +} + +func GetCoordinatorTotalBalance(coordinator contracts.VRFCoordinatorV2_5) (linkTotalBalance *big.Int, nativeTokenTotalBalance *big.Int, err error) { + linkTotalBalance, err = coordinator.GetLinkTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrLinkTotalBalance, err) + } + nativeTokenTotalBalance, err = coordinator.GetNativeTokenTotalBalance(context.Background()) + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrNativeTokenBalance, err) + } + return +} + func RequestRandomnessAndWaitForFulfillment( consumer contracts.VRFv2PlusLoadTestConsumer, coordinator contracts.VRFCoordinatorV2_5, @@ -705,6 +573,135 @@ func RequestRandomnessAndWaitForFulfillmentUpgraded( return randomWordsFulfilledEvent, err } +func SetupVRFV2PlusWrapperEnvironment( + env *test_env.CLClusterTestEnv, + vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, + linkToken contracts.LinkToken, + mockNativeLINKFeed contracts.MockETHLINKFeed, + coordinator contracts.VRFCoordinatorV2_5, + keyHash [32]byte, + wrapperConsumerContractsAmount int, +) (*VRFV2PlusWrapperContracts, *big.Int, error) { + + wrapperContracts, err := DeployVRFV2PlusDirectFundingContracts( + env.ContractDeployer, + env.EVMClient, + linkToken.Address(), + mockNativeLINKFeed.Address(), + coordinator, + wrapperConsumerContractsAmount, + ) + if err != nil { + return nil, nil, err + } + + err = env.EVMClient.WaitForEvents() + + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + err = wrapperContracts.VRFV2PlusWrapper.SetConfig( + vrfv2PlusConfig.WrapperGasOverhead, + vrfv2PlusConfig.CoordinatorGasOverhead, + vrfv2PlusConfig.WrapperPremiumPercentage, + keyHash, + vrfv2PlusConfig.WrapperMaxNumberOfWords, + vrfv2PlusConfig.StalenessSeconds, + big.NewInt(vrfv2PlusConfig.FallbackWeiPerUnitLink), + vrfv2PlusConfig.FulfillmentFlatFeeLinkPPM, + vrfv2PlusConfig.FulfillmentFlatFeeNativePPM, + ) + if err != nil { + return nil, nil, err + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + //fund sub + wrapperSubID, err := wrapperContracts.VRFV2PlusWrapper.GetSubID(context.Background()) + if err != nil { + return nil, nil, err + } + + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + err = FundSubscriptions(env, vrfv2PlusConfig, linkToken, coordinator, []*big.Int{wrapperSubID}) + if err != nil { + return nil, nil, err + } + + //fund consumer with Link + err = linkToken.Transfer( + wrapperContracts.LoadTestConsumers[0].Address(), + big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(vrfv2PlusConfig.WrapperConsumerFundingAmountLink)), + ) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + //fund consumer with Eth + err = wrapperContracts.LoadTestConsumers[0].Fund(big.NewFloat(vrfv2PlusConfig.WrapperConsumerFundingAmountNativeToken)) + if err != nil { + return nil, nil, err + } + err = env.EVMClient.WaitForEvents() + if err != nil { + return nil, nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return wrapperContracts, wrapperSubID, nil +} + +func DeployVRFV2PlusWrapperConsumers(contractDeployer contracts.ContractDeployer, linkTokenAddress string, vrfV2PlusWrapper contracts.VRFV2PlusWrapper, consumerContractsAmount int) ([]contracts.VRFv2PlusWrapperLoadTestConsumer, error) { + var consumers []contracts.VRFv2PlusWrapperLoadTestConsumer + for i := 1; i <= consumerContractsAmount; i++ { + loadTestConsumer, err := contractDeployer.DeployVRFV2PlusWrapperLoadTestConsumer(linkTokenAddress, vrfV2PlusWrapper.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrAdvancedConsumer, err) + } + consumers = append(consumers, loadTestConsumer) + } + return consumers, nil +} + +func DeployVRFV2PlusDirectFundingContracts( + contractDeployer contracts.ContractDeployer, + chainClient blockchain.EVMClient, + linkTokenAddress string, + linkEthFeedAddress string, + coordinator contracts.VRFCoordinatorV2_5, + consumerContractsAmount int, +) (*VRFV2PlusWrapperContracts, error) { + + vrfv2PlusWrapper, err := contractDeployer.DeployVRFV2PlusWrapper(linkTokenAddress, linkEthFeedAddress, coordinator.Address()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrDeployWrapper, err) + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + + consumers, err := DeployVRFV2PlusWrapperConsumers(contractDeployer, linkTokenAddress, vrfv2PlusWrapper, consumerContractsAmount) + if err != nil { + return nil, err + } + err = chainClient.WaitForEvents() + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrWaitTXsComplete, err) + } + return &VRFV2PlusWrapperContracts{vrfv2PlusWrapper, consumers}, nil +} + func DirectFundingRequestRandomnessAndWaitForFulfillment( consumer contracts.VRFv2PlusWrapperLoadTestConsumer, coordinator contracts.VRFCoordinatorV2_5, @@ -804,7 +801,7 @@ func WaitForRequestCountEqualToFulfilmentCount(consumer contracts.VRFv2PlusLoadT fmt.Errorf("timeout waiting for rand request and fulfilments to be equal AFTER performance test was executed. Request Count: %d, Fulfilment Count: %d", metrics.RequestCount.Uint64(), metrics.FulfilmentCount.Uint64()) case <-ticker.C: - go retreiveLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) + go retrieveLoadTestMetrics(consumer, metricsChannel, metricsErrorChannel) case metrics = <-metricsChannel: if metrics.RequestCount.Cmp(metrics.FulfilmentCount) == 0 { ticker.Stop() @@ -854,7 +851,7 @@ func ReturnFundsForFulfilledRequests(client blockchain.EVMClient, coordinator co return nil } -func retreiveLoadTestMetrics( +func retrieveLoadTestMetrics( consumer contracts.VRFv2PlusLoadTestConsumer, metricsChannel chan *contracts.VRFLoadTestMetrics, metricsErrorChannel chan error, diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go index 9a2f20226d3..6136e78b367 100644 --- a/integration-tests/contracts/contract_loader.go +++ b/integration-tests/contracts/contract_loader.go @@ -17,6 +17,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_load_test_with_metrics" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/fee_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/reward_manager" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" @@ -44,6 +46,8 @@ type ContractLoader interface { LoadWERC20Mock(addr common.Address) (WERC20Mock, error) // VRF + LoadVRFCoordinatorV2(addr string) (VRFCoordinatorV2, error) + LoadVRFv2LoadTestConsumer(addr string) (VRFv2LoadTestConsumer, error) LoadVRFCoordinatorV2_5(addr string) (VRFCoordinatorV2_5, error) LoadVRFv2PlusLoadTestConsumer(addr string) (VRFv2PlusLoadTestConsumer, error) } @@ -356,3 +360,39 @@ func (e *EthereumContractLoader) LoadVRFv2PlusLoadTestConsumer(addr string) (VRF address: &address, }, err } + +func (e *EthereumContractLoader) LoadVRFCoordinatorV2(addr string) (VRFCoordinatorV2, error) { + address := common.HexToAddress(addr) + instance, err := e.client.LoadContract("VRFCoordinatorV2", address, func( + address common.Address, + backend bind.ContractBackend, + ) (interface{}, error) { + return vrf_coordinator_v2.NewVRFCoordinatorV2(address, backend) + }) + if err != nil { + return nil, err + } + return &EthereumVRFCoordinatorV2{ + address: &address, + client: e.client, + coordinator: instance.(*vrf_coordinator_v2.VRFCoordinatorV2), + }, err +} + +func (e *EthereumContractLoader) LoadVRFv2LoadTestConsumer(addr string) (VRFv2LoadTestConsumer, error) { + address := common.HexToAddress(addr) + instance, err := e.client.LoadContract("VRFV2LoadTestWithMetrics", address, func( + address common.Address, + backend bind.ContractBackend, + ) (interface{}, error) { + return vrf_load_test_with_metrics.NewVRFV2LoadTestWithMetrics(address, backend) + }) + if err != nil { + return nil, err + } + return &EthereumVRFv2LoadTestConsumer{ + client: e.client, + consumer: instance.(*vrf_load_test_with_metrics.VRFV2LoadTestWithMetrics), + address: &address, + }, err +} diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index baee2ccd929..5f850fce1a7 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -49,10 +49,15 @@ type VRFCoordinatorV2 interface { publicProvingKey [2]*big.Int, ) error HashOfKey(ctx context.Context, pubKey [2]*big.Int) ([32]byte, error) - CreateSubscription() error + CreateSubscription() (*types.Transaction, error) AddConsumer(subId uint64, consumerAddress string) error Address() string GetSubscription(ctx context.Context, subID uint64) (vrf_coordinator_v2.GetSubscription, error) + PendingRequestsExist(ctx context.Context, subID uint64) (bool, error) + CancelSubscription(subID uint64, to common.Address) (*types.Transaction, error) + FindSubscriptionID(subID uint64) (uint64, error) + WaitForRandomWordsFulfilledEvent(requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) + WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) } type VRFCoordinatorV2_5 interface { @@ -169,6 +174,7 @@ type VRFv2LoadTestConsumer interface { GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_load_test_with_metrics.GetRequestStatus, error) GetLastRequestId(ctx context.Context) (*big.Int, error) GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) + ResetMetrics() error } type VRFv2PlusLoadTestConsumer interface { diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index 9c7e628dbd9..ac3926fa746 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -3,7 +3,9 @@ package contracts import ( "context" "encoding/hex" + "fmt" "math/big" + "time" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -182,16 +184,16 @@ func (v *EthereumVRFCoordinatorV2) RegisterProvingKey( return v.client.ProcessTransaction(tx) } -func (v *EthereumVRFCoordinatorV2) CreateSubscription() error { +func (v *EthereumVRFCoordinatorV2) CreateSubscription() (*types.Transaction, error) { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { - return err + return nil, err } tx, err := v.coordinator.CreateSubscription(opts) if err != nil { - return err + return nil, err } - return v.client.ProcessTransaction(tx) + return tx, v.client.ProcessTransaction(tx) } func (v *EthereumVRFCoordinatorV2) AddConsumer(subId uint64, consumerAddress string) error { @@ -210,6 +212,94 @@ func (v *EthereumVRFCoordinatorV2) AddConsumer(subId uint64, consumerAddress str return v.client.ProcessTransaction(tx) } +func (v *EthereumVRFCoordinatorV2) PendingRequestsExist(ctx context.Context, subID uint64) (bool, error) { + opts := &bind.CallOpts{ + From: common.HexToAddress(v.client.GetDefaultWallet().Address()), + Context: ctx, + } + pendingRequestExists, err := v.coordinator.PendingRequestExists(opts, subID) + if err != nil { + return false, err + } + return pendingRequestExists, nil +} + +// CancelSubscription cancels subscription by Sub owner, +// return funds to specified address, +// checks if pending requests for a sub exist +func (v *EthereumVRFCoordinatorV2) CancelSubscription(subID uint64, to common.Address) (*types.Transaction, error) { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return nil, err + } + tx, err := v.coordinator.CancelSubscription( + opts, + subID, + to, + ) + if err != nil { + return nil, err + } + return tx, v.client.ProcessTransaction(tx) +} + +func (v *EthereumVRFCoordinatorV2) FindSubscriptionID(subID uint64) (uint64, error) { + owner := v.client.GetDefaultWallet().Address() + subscriptionIterator, err := v.coordinator.FilterSubscriptionCreated( + nil, + []uint64{subID}, + ) + if err != nil { + return 0, err + } + + if !subscriptionIterator.Next() { + return 0, fmt.Errorf("expected at least 1 subID for the given owner %s", owner) + } + + return subscriptionIterator.Event.SubId, nil +} + +func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsFulfilledEvent(requestID []*big.Int, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { + randomWordsFulfilledEventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled) + subscription, err := v.coordinator.WatchRandomWordsFulfilled(nil, randomWordsFulfilledEventsChannel, requestID) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for RandomWordsFulfilled event") + case randomWordsFulfilledEvent := <-randomWordsFulfilledEventsChannel: + return randomWordsFulfilledEvent, nil + } + } +} + +func (v *EthereumVRFCoordinatorV2) WaitForRandomWordsRequestedEvent(keyHash [][32]byte, subID []uint64, sender []common.Address, timeout time.Duration) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested, error) { + randomWordsFulfilledEventsChannel := make(chan *vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested) + subscription, err := v.coordinator.WatchRandomWordsRequested(nil, randomWordsFulfilledEventsChannel, keyHash, subID, sender) + if err != nil { + return nil, err + } + defer subscription.Unsubscribe() + + for { + select { + case err := <-subscription.Err(): + return nil, err + case <-time.After(timeout): + return nil, fmt.Errorf("timeout waiting for RandomWordsRequested event") + case randomWordsFulfilledEvent := <-randomWordsFulfilledEventsChannel: + return randomWordsFulfilledEvent, nil + } + } +} + // GetAllRandomWords get all VRFv2 randomness output words func (v *EthereumVRFConsumerV2) GetAllRandomWords(ctx context.Context, num int) ([]*big.Int, error) { words := make([]*big.Int, 0) @@ -392,6 +482,18 @@ func (v *EthereumVRFv2LoadTestConsumer) GetLastRequestId(ctx context.Context) (* }) } +func (v *EthereumVRFv2LoadTestConsumer) ResetMetrics() error { + opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) + if err != nil { + return err + } + tx, err := v.consumer.Reset(opts) + if err != nil { + return err + } + return v.client.ProcessTransaction(tx) +} + func (v *EthereumVRFv2LoadTestConsumer) GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) { requestCount, err := v.consumer.SRequestCount(&bind.CallOpts{ From: common.HexToAddress(v.client.GetDefaultWallet().Address()), diff --git a/integration-tests/load/vrfv2/config.go b/integration-tests/load/vrfv2/config.go index 0a595f753c2..e23294e839b 100644 --- a/integration-tests/load/vrfv2/config.go +++ b/integration-tests/load/vrfv2/config.go @@ -1,10 +1,12 @@ package loadvrfv2 import ( + "encoding/base64" "fmt" - "math/big" "os" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/pelletier/go-toml/v2" "github.com/rs/zerolog/log" @@ -13,64 +15,136 @@ import ( const ( DefaultConfigFilename = "config.toml" + SoakTestType = "Soak" + LoadTestType = "Load" + StressTestType = "Stress" + SpikeTestType = "Spike" - ErrReadPerfConfig = "failed to read TOML config for performance tests" - ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" + ErrReadPerfConfig = "failed to read TOML config for performance tests" + ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" + ErrDeviationShouldBeLessThanOriginal = "`RandomnessRequestCountPerRequestDeviation` should be less than `RandomnessRequestCountPerRequest`" ) type PerformanceConfig struct { - Soak *Soak `toml:"Soak"` - Load *Load `toml:"Load"` - SoakVolume *SoakVolume `toml:"SoakVolume"` - LoadVolume *LoadVolume `toml:"LoadVolume"` - Common *Common `toml:"Common"` + Soak *Soak `toml:"Soak"` + Load *Load `toml:"Load"` + Stress *Stress `toml:"Stress"` + Spike *Spike `toml:"Spike"` + + Common *Common `toml:"Common"` + ExistingEnvConfig *ExistingEnvConfig `toml:"ExistingEnvConfig"` + NewEnvConfig *NewEnvConfig `toml:"NewEnvConfig"` } -type Common struct { +type ExistingEnvConfig struct { + CoordinatorAddress string `toml:"coordinator_address"` + ConsumerAddress string `toml:"consumer_address"` + LinkAddress string `toml:"link_address"` + SubID uint64 `toml:"sub_id"` + KeyHash string `toml:"key_hash"` + Funding + CreateFundSubsAndAddConsumers bool `toml:"create_fund_subs_and_add_consumers"` + NodeSendingKeys []string `toml:"node_sending_keys"` +} + +type NewEnvConfig struct { Funding } +type Common struct { + MinimumConfirmations uint16 `toml:"minimum_confirmations"` +} + type Funding struct { - NodeFunds *big.Float `toml:"node_funds"` - SubFunds *big.Int `toml:"sub_funds"` + SubFunding + NodeSendingKeyFunding float64 `toml:"node_sending_key_funding"` + NodeSendingKeyFundingMin float64 `toml:"node_sending_key_funding_min"` } -type Soak struct { - RPS int64 `toml:"rps"` - Duration *models.Duration `toml:"duration"` +type SubFunding struct { + SubFundsLink float64 `toml:"sub_funds_link"` } -type SoakVolume struct { - Products int64 `toml:"products"` - Pace *models.Duration `toml:"pace"` - Duration *models.Duration `toml:"duration"` +type Soak struct { + PerformanceTestConfig } type Load struct { - RPSFrom int64 `toml:"rps_from"` - RPSIncrease int64 `toml:"rps_increase"` - RPSSteps int `toml:"rps_steps"` - Duration *models.Duration `toml:"duration"` + PerformanceTestConfig } -type LoadVolume struct { - ProductsFrom int64 `toml:"products_from"` - ProductsIncrease int64 `toml:"products_increase"` - ProductsSteps int `toml:"products_steps"` - Pace *models.Duration `toml:"pace"` - Duration *models.Duration `toml:"duration"` +type Stress struct { + PerformanceTestConfig +} + +type Spike struct { + PerformanceTestConfig +} + +type PerformanceTestConfig struct { + NumberOfSubToCreate int `toml:"number_of_sub_to_create"` + + RPS int64 `toml:"rps"` + //Duration *models.Duration `toml:"duration"` + RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` + RandomnessRequestCountPerRequest uint16 `toml:"randomness_request_count_per_request"` + RandomnessRequestCountPerRequestDeviation uint16 `toml:"randomness_request_count_per_request_deviation"` } func ReadConfig() (*PerformanceConfig, error) { var cfg *PerformanceConfig - d, err := os.ReadFile(DefaultConfigFilename) - if err != nil { - return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) + rawConfig := os.Getenv("CONFIG") + var d []byte + var err error + if rawConfig == "" { + d, err = os.ReadFile(DefaultConfigFilename) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) + } + } else { + d, err = base64.StdEncoding.DecodeString(rawConfig) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) + } } err = toml.Unmarshal(d, &cfg) if err != nil { return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) } - log.Debug().Interface("PerformanceConfig", cfg).Msg("Parsed performance config") + + if cfg.Soak.RandomnessRequestCountPerRequest <= cfg.Soak.RandomnessRequestCountPerRequestDeviation { + return nil, fmt.Errorf("%s, err: %w", ErrDeviationShouldBeLessThanOriginal, err) + } + + log.Debug().Interface("Config", cfg).Msg("Parsed config") return cfg, nil } + +func SetPerformanceTestConfig(testType string, vrfv2Config *vrfv2_config.VRFV2Config, cfg *PerformanceConfig) { + switch testType { + case SoakTestType: + vrfv2Config.NumberOfSubToCreate = cfg.Soak.NumberOfSubToCreate + vrfv2Config.RPS = cfg.Soak.RPS + vrfv2Config.RateLimitUnitDuration = cfg.Soak.RateLimitUnitDuration.Duration() + vrfv2Config.RandomnessRequestCountPerRequest = cfg.Soak.RandomnessRequestCountPerRequest + vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Soak.RandomnessRequestCountPerRequestDeviation + case LoadTestType: + vrfv2Config.NumberOfSubToCreate = cfg.Load.NumberOfSubToCreate + vrfv2Config.RPS = cfg.Load.RPS + vrfv2Config.RateLimitUnitDuration = cfg.Load.RateLimitUnitDuration.Duration() + vrfv2Config.RandomnessRequestCountPerRequest = cfg.Load.RandomnessRequestCountPerRequest + vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Load.RandomnessRequestCountPerRequestDeviation + case StressTestType: + vrfv2Config.NumberOfSubToCreate = cfg.Stress.NumberOfSubToCreate + vrfv2Config.RPS = cfg.Stress.RPS + vrfv2Config.RateLimitUnitDuration = cfg.Stress.RateLimitUnitDuration.Duration() + vrfv2Config.RandomnessRequestCountPerRequest = cfg.Stress.RandomnessRequestCountPerRequest + vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Stress.RandomnessRequestCountPerRequestDeviation + case SpikeTestType: + vrfv2Config.NumberOfSubToCreate = cfg.Spike.NumberOfSubToCreate + vrfv2Config.RPS = cfg.Spike.RPS + vrfv2Config.RateLimitUnitDuration = cfg.Spike.RateLimitUnitDuration.Duration() + vrfv2Config.RandomnessRequestCountPerRequest = cfg.Spike.RandomnessRequestCountPerRequest + vrfv2Config.RandomnessRequestCountPerRequestDeviation = cfg.Spike.RandomnessRequestCountPerRequestDeviation + } +} diff --git a/integration-tests/load/vrfv2/config.toml b/integration-tests/load/vrfv2/config.toml index 8917db88cc2..4e82ec7aeb6 100644 --- a/integration-tests/load/vrfv2/config.toml +++ b/integration-tests/load/vrfv2/config.toml @@ -1,27 +1,57 @@ -# testing one product (jobs + contracts) by varying RPS + +[Common] +minimum_confirmations = 3 + +[NewEnvConfig] +sub_funds_link = 1 +node_sending_key_funding = 10 + +[ExistingEnvConfig] +coordinator_address = "0x50d47e4142598E3411aA864e08a44284e471AC6f" +#consumer_address = "0x087F232165D9bA1A602f148025e5D0666953F64a" +#sub_id = "52116875585187328970776211988181422347535732407068188096422095950800466618218" +key_hash = "0x027f94ff1465b3525f9fc03e9ff7d6d2c0953482246dd6ae07570c45d6631414" +create_fund_subs_and_add_consumers = true +link_address = "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E" +sub_funds_link = 3 +node_sending_key_funding_min = 2 +node_sending_keys = [ + "", + "", + "", + "", + "", + "", +] + +# 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds [Soak] +rate_limit_unit_duration = "6s" rps = 1 -duration = "3m" +randomness_request_count_per_request = 1 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 +# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds [Load] -rps_from = 1 -rps_increase = 1 -rps_steps = 10 -duration = "3m" - -# testing multiple products (jobs + contracts) by varying instances, deploying more of the same type with stable RPS for each product -[SoakVolume] -products = 5 -pace = "1s" -duration = "3m" +rate_limit_unit_duration = "3s" +rps = 1 +randomness_request_count_per_request = 3 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 2 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 -[LoadVolume] -products_from = 1 -products_increase = 1 -products_steps = 10 -pace = "1s" -duration = "3m" +# approx 540 RPM - 3 tx requests per second with 4 rand requests in each tx +[Stress] +rate_limit_unit_duration = "1s" +rps = 3 +randomness_request_count_per_request = 4 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 -[Common] -node_funds = 10 -sub_funds = 100 \ No newline at end of file +# approx 150 RPM - 1 tx request with 150 rand requests in each tx every 60 seconds +[Spike] +rate_limit_unit_duration = "1m" +rps = 1 +randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request +randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting +number_of_sub_to_create = 1 diff --git a/integration-tests/load/vrfv2/gun.go b/integration-tests/load/vrfv2/gun.go index 8100baaa7f7..8a5eb3c66de 100644 --- a/integration-tests/load/vrfv2/gun.go +++ b/integration-tests/load/vrfv2/gun.go @@ -1,38 +1,78 @@ package loadvrfv2 import ( + "math/rand" + + "github.com/rs/zerolog" + "github.com/smartcontractkit/wasp" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" ) /* SingleHashGun is a gun that constantly requests randomness for one feed */ type SingleHashGun struct { - contracts *vrfv2_actions.VRFV2Contracts - keyHash [32]byte + contracts *vrfv2_actions.VRFV2Contracts + keyHash [32]byte + subIDs []uint64 + vrfv2Config vrfv2_config.VRFV2Config + logger zerolog.Logger } -func SingleFeedGun(contracts *vrfv2_actions.VRFV2Contracts, keyHash [32]byte) *SingleHashGun { +func NewSingleHashGun( + contracts *vrfv2_actions.VRFV2Contracts, + keyHash [32]byte, + subIDs []uint64, + vrfv2Config vrfv2_config.VRFV2Config, + logger zerolog.Logger, +) *SingleHashGun { return &SingleHashGun{ - contracts: contracts, - keyHash: keyHash, + contracts: contracts, + keyHash: keyHash, + subIDs: subIDs, + vrfv2Config: vrfv2Config, + logger: logger, } } // Call implements example gun call, assertions on response bodies should be done here func (m *SingleHashGun) Call(_ *wasp.Generator) *wasp.CallResult { - err := m.contracts.LoadTestConsumer.RequestRandomness( - m.keyHash, - vrfConst.SubID, - vrfConst.MinimumConfirmations, - vrfConst.CallbackGasLimit, - vrfConst.NumberOfWords, - vrfConst.RandomnessRequestCountPerRequest, + //todo - should work with multiple consumers and consumers having different keyhashes and wallets + + //randomly increase/decrease randomness request count per TX + randomnessRequestCountPerRequest := deviateValue(m.vrfv2Config.RandomnessRequestCountPerRequest, m.vrfv2Config.RandomnessRequestCountPerRequestDeviation) + _, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + //the same consumer is used for all requests and in all subs + m.contracts.LoadTestConsumers[0], + m.contracts.Coordinator, + &vrfv2_actions.VRFV2Data{VRFV2KeyData: vrfv2_actions.VRFV2KeyData{KeyHash: m.keyHash}}, + //randomly pick a subID from pool of subIDs + m.subIDs[randInRange(0, len(m.subIDs)-1)], + randomnessRequestCountPerRequest, + m.vrfv2Config, + m.vrfv2Config.RandomWordsFulfilledEventTimeout, + m.logger, ) if err != nil { return &wasp.CallResult{Error: err.Error(), Failed: true} } return &wasp.CallResult{} } + +func deviateValue(requestCountPerTX uint16, deviation uint16) uint16 { + if randBool() && requestCountPerTX > deviation { + requestCountPerTX -= uint16(randInRange(0, int(deviation))) + } else { + requestCountPerTX += uint16(randInRange(0, int(deviation))) + } + return requestCountPerTX +} + +func randBool() bool { + return rand.Intn(2) == 1 +} +func randInRange(min int, max int) int { + return rand.Intn(max-min+1) + min +} diff --git a/integration-tests/load/vrfv2/onchain_monitoring.go b/integration-tests/load/vrfv2/onchain_monitoring.go index 66af1807acf..55975a7e42f 100644 --- a/integration-tests/load/vrfv2/onchain_monitoring.go +++ b/integration-tests/load/vrfv2/onchain_monitoring.go @@ -1,15 +1,14 @@ package loadvrfv2 import ( + "context" "testing" "time" "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" ) /* Monitors on-chain stats of LoadConsumer and pushes them to Loki every second */ @@ -21,29 +20,37 @@ const ( ErrLokiPush = "failed to push monitoring metrics to Loki" ) -func MonitorLoadStats(t *testing.T, vrfv2Contracts *vrfv2_actions.VRFV2Contracts, labels map[string]string) { +func MonitorLoadStats(lc *wasp.LokiClient, consumer contracts.VRFv2LoadTestConsumer, labels map[string]string) { go func() { - updatedLabels := make(map[string]string) - for k, v := range labels { - updatedLabels[k] = v - } - updatedLabels["type"] = LokiTypeLabel - updatedLabels["go_test_name"] = t.Name() - updatedLabels["gen_name"] = "performance" - lc, err := wasp.NewLokiClient(wasp.NewEnvLokiConfig()) - if err != nil { - log.Error().Err(err).Msg(ErrLokiClient) - return - } for { time.Sleep(1 * time.Second) - metrics, err := vrfv2Contracts.LoadTestConsumer.GetLoadTestMetrics(testcontext.Get(t)) - if err != nil { - log.Error().Err(err).Msg(ErrMetrics) - } - if err := lc.HandleStruct(wasp.LabelsMapToModel(updatedLabels), time.Now(), metrics); err != nil { - log.Error().Err(err).Msg(ErrLokiPush) - } + metrics := GetLoadTestMetrics(consumer) + SendMetricsToLoki(metrics, lc, labels) } }() } + +func UpdateLabels(labels map[string]string, t *testing.T) map[string]string { + updatedLabels := make(map[string]string) + for k, v := range labels { + updatedLabels[k] = v + } + updatedLabels["type"] = LokiTypeLabel + updatedLabels["go_test_name"] = t.Name() + updatedLabels["gen_name"] = "performance" + return updatedLabels +} + +func SendMetricsToLoki(metrics *contracts.VRFLoadTestMetrics, lc *wasp.LokiClient, updatedLabels map[string]string) { + if err := lc.HandleStruct(wasp.LabelsMapToModel(updatedLabels), time.Now(), metrics); err != nil { + log.Error().Err(err).Msg(ErrLokiPush) + } +} + +func GetLoadTestMetrics(consumer contracts.VRFv2LoadTestConsumer) *contracts.VRFLoadTestMetrics { + metrics, err := consumer.GetLoadTestMetrics(context.Background()) + if err != nil { + log.Error().Err(err).Msg(ErrMetrics) + } + return metrics +} diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 44325965bd7..37a70442895 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -1,94 +1,358 @@ package loadvrfv2 import ( + "context" + "math/big" + "os" + "sync" "testing" + "time" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/kelseyhightower/envconfig" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/testreporters" + "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" ) -func TestVRFV2Load(t *testing.T) { +var ( + env *test_env.CLClusterTestEnv + vrfv2Contracts *vrfv2_actions.VRFV2Contracts + vrfv2Data *vrfv2_actions.VRFV2Data + subIDs []uint64 + eoaWalletAddress string + + labels = map[string]string{ + "branch": "vrfv2_healthcheck", + "commit": "vrfv2_healthcheck", + } + + testType = os.Getenv("TEST_TYPE") +) + +func TestVRFV2Performance(t *testing.T) { cfg, err := ReadConfig() require.NoError(t, err) - env, vrfv2Contracts, key, err := vrfv2_actions.SetupLocalLoadTestEnv(cfg.Common.NodeFunds, cfg.Common.SubFunds) + var vrfv2Config vrfv2_config.VRFV2Config + err = envconfig.Process("VRFV2", &vrfv2Config) require.NoError(t, err) - labels := map[string]string{ - "branch": "vrfv2_healthcheck", - "commit": "vrfv2_healthcheck", + testReporter := &testreporters.VRFV2TestReporter{} + + SetPerformanceTestConfig(testType, &vrfv2Config, cfg) + + l := logging.GetTestLogger(t) + //todo: temporary solution with envconfig and toml config until VRF-662 is implemented + vrfv2Config.MinimumConfirmations = cfg.Common.MinimumConfirmations + + lokiConfig := wasp.NewEnvLokiConfig() + lc, err := wasp.NewLokiClient(lokiConfig) + if err != nil { + l.Error().Err(err).Msg(ErrLokiClient) + return } - singleFeedConfig := &wasp.Config{ - T: t, - LoadType: wasp.RPS, - GenName: "gun", - Gun: SingleFeedGun(vrfv2Contracts, key), - Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + updatedLabels := UpdateLabels(labels, t) + + l.Info(). + Str("Test Type", testType). + Str("Test Duration", vrfv2Config.TestDuration.Truncate(time.Second).String()). + Int64("RPS", vrfv2Config.RPS). + Str("RateLimitUnitDuration", vrfv2Config.RateLimitUnitDuration.String()). + Uint16("RandomnessRequestCountPerRequest", vrfv2Config.RandomnessRequestCountPerRequest). + Uint16("RandomnessRequestCountPerRequestDeviation", vrfv2Config.RandomnessRequestCountPerRequestDeviation). + Bool("UseExistingEnv", vrfv2Config.UseExistingEnv). + Msg("Performance Test Configuration") + + if vrfv2Config.UseExistingEnv { + //todo: temporary solution with envconfig and toml config until VRF-662 is implemented + vrfv2Config.CoordinatorAddress = cfg.ExistingEnvConfig.CoordinatorAddress + vrfv2Config.ConsumerAddress = cfg.ExistingEnvConfig.ConsumerAddress + vrfv2Config.LinkAddress = cfg.ExistingEnvConfig.LinkAddress + vrfv2Config.SubscriptionFundingAmountLink = cfg.ExistingEnvConfig.SubFunding.SubFundsLink + vrfv2Config.SubID = cfg.ExistingEnvConfig.SubID + vrfv2Config.KeyHash = cfg.ExistingEnvConfig.KeyHash + + env, err = test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithCustomCleanup( + func() { + teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2Config) + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + //cancel subs and return funds to sub owner + for _, subID := range subIDs { + l.Info(). + Uint64("Returning funds from SubID", subID). + Str("Returning funds to", eoaWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(context.Background(), subID) + if err != nil { + l.Error().Err(err).Msg("Error checking if pending requests exist") + } + if !pendingRequestsExist { + _, err := vrfv2Contracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } else { + l.Error().Uint64("Sub ID", subID).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") + } + + } + } + }). + Build() + + require.NoError(t, err, "error creating test env") + + coordinator, err := env.ContractLoader.LoadVRFCoordinatorV2(vrfv2Config.CoordinatorAddress) + require.NoError(t, err) + + var consumers []contracts.VRFv2LoadTestConsumer + if cfg.ExistingEnvConfig.CreateFundSubsAndAddConsumers { + linkToken, err := env.ContractLoader.LoadLINKToken(vrfv2Config.LinkAddress) + require.NoError(t, err) + consumers, err = vrfv2_actions.DeployVRFV2Consumers(env.ContractDeployer, coordinator, 1) + require.NoError(t, err) + subIDs, err = vrfv2_actions.CreateFundSubsAndAddConsumers( + env, + vrfv2Config, + linkToken, + coordinator, + consumers, + vrfv2Config.NumberOfSubToCreate, + ) + require.NoError(t, err) + } else { + consumer, err := env.ContractLoader.LoadVRFv2LoadTestConsumer(vrfv2Config.ConsumerAddress) + require.NoError(t, err) + consumers = append(consumers, consumer) + subIDs = append(subIDs, vrfv2Config.SubID) + } + + err = FundNodesIfNeeded(cfg, env.EVMClient, l) + require.NoError(t, err) + + vrfv2Contracts = &vrfv2_actions.VRFV2Contracts{ + Coordinator: coordinator, + LoadTestConsumers: consumers, + BHS: nil, + } + + vrfv2Data = &vrfv2_actions.VRFV2Data{ + VRFV2KeyData: vrfv2_actions.VRFV2KeyData{ + VRFKey: nil, + EncodedProvingKey: [2]*big.Int{}, + KeyHash: common.HexToHash(vrfv2Config.KeyHash), + }, + VRFJob: nil, + PrimaryEthAddress: "", + ChainID: nil, + } + + } else { + //todo: temporary solution with envconfig and toml config until VRF-662 is implemented + vrfv2Config.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding + vrfv2Config.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink + env, err = test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(1). + WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithCustomCleanup( + func() { + teardown(t, vrfv2Contracts.LoadTestConsumers[0], lc, updatedLabels, testReporter, testType, vrfv2Config) + + if env.EVMClient.NetworkSimulated() { + l.Info(). + Str("Network Name", env.EVMClient.GetNetworkName()). + Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") + } else { + for _, subID := range subIDs { + l.Info(). + Uint64("Returning funds from SubID", subID). + Str("Returning funds to", eoaWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + _, err := vrfv2Contracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } + //err = vrfv2.ReturnFundsForFulfilledRequests(env.EVMClient, vrfv2Contracts.Coordinator, l) + //l.Error().Err(err).Msg("Error returning funds for fulfilled requests") + } + if err := env.Cleanup(); err != nil { + l.Error().Err(err).Msg("Error cleaning up test environment") + } + }). + WithLogWatcher(). + Build() + + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + require.NoError(t, err, "error deploying mock ETH/LINK feed") + + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err, "error deploying LINK contract") + + vrfv2Contracts, subIDs, vrfv2Data, err = vrfv2_actions.SetupVRFV2Environment( + env, + vrfv2Config, + linkToken, + mockETHLinkFeed, + //register proving key against EOA address in order to return funds to this address + env.EVMClient.GetDefaultWallet().Address(), + 1, + vrfv2Config.NumberOfSubToCreate, + l, + ) + require.NoError(t, err, "error setting up VRF v2 env") } + eoaWalletAddress = env.EVMClient.GetDefaultWallet().Address() - multiFeedConfig := &wasp.Config{ - T: t, - LoadType: wasp.VU, - GenName: "vu", - VU: NewJobVolumeVU(cfg.SoakVolume.Pace.Duration(), 1, env.ClCluster.NodeAPIs(), env.EVMClient, vrfv2Contracts), - Labels: labels, - LokiConfig: wasp.NewEnvLokiConfig(), + l.Debug().Int("Number of Subs", len(subIDs)).Msg("Subs involved in the test") + for _, subID := range subIDs { + subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information for subscription %d", subID) + vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) } - MonitorLoadStats(t, vrfv2Contracts, labels) + singleFeedConfig := &wasp.Config{ + T: t, + LoadType: wasp.RPS, + GenName: "gun", + RateLimitUnitDuration: vrfv2Config.RateLimitUnitDuration, + Gun: NewSingleHashGun( + vrfv2Contracts, + vrfv2Data.KeyHash, + subIDs, + vrfv2Config, + l, + ), + Labels: labels, + LokiConfig: lokiConfig, + CallTimeout: 2 * time.Minute, + } + require.Len(t, vrfv2Contracts.LoadTestConsumers, 1, "only one consumer should be created for Load Test") + consumer := vrfv2Contracts.LoadTestConsumers[0] + err = consumer.ResetMetrics() + require.NoError(t, err) + MonitorLoadStats(lc, consumer, updatedLabels) // is our "job" stable at all, no memory leaks, no flaking performance under some RPS? - t.Run("vrfv2 soak test", func(t *testing.T) { - singleFeedConfig.Schedule = wasp.Plain( - cfg.Soak.RPS, - cfg.Soak.Duration.Duration(), - ) - _, err := wasp.NewProfile(). - Add(wasp.NewGenerator(singleFeedConfig)). - Run(true) - require.NoError(t, err) - }) + t.Run("vrfv2 performance test", func(t *testing.T) { - // what are the limits for one "job", figuring out the max/optimal performance params by increasing RPS and varying configuration - t.Run("vrfv2 load test", func(t *testing.T) { - singleFeedConfig.Schedule = wasp.Steps( - cfg.Load.RPSFrom, - cfg.Load.RPSIncrease, - cfg.Load.RPSSteps, - cfg.Load.Duration.Duration(), + singleFeedConfig.Schedule = wasp.Plain( + vrfv2Config.RPS, + vrfv2Config.TestDuration, ) _, err = wasp.NewProfile(). Add(wasp.NewGenerator(singleFeedConfig)). Run(true) require.NoError(t, err) - }) - // how many "jobs" of the same type we can run at once at a stable load with optimal configuration? - t.Run("vrfv2 volume soak test", func(t *testing.T) { - multiFeedConfig.Schedule = wasp.Plain( - cfg.SoakVolume.Products, - cfg.SoakVolume.Duration.Duration(), - ) - _, err = wasp.NewProfile(). - Add(wasp.NewGenerator(multiFeedConfig)). - Run(true) + var wg sync.WaitGroup + wg.Add(1) + //todo - timeout should be configurable depending on the perf test type + requestCount, fulfilmentCount, err := vrfv2_actions.WaitForRequestCountEqualToFulfilmentCount(consumer, 2*time.Minute, &wg) require.NoError(t, err) - }) + wg.Wait() - // what are the limits if we add more and more "jobs/products" of the same type, each "job" have a stable RPS we vary only amount of jobs - t.Run("vrfv2 volume load test", func(t *testing.T) { - multiFeedConfig.Schedule = wasp.Steps( - cfg.LoadVolume.ProductsFrom, - cfg.LoadVolume.ProductsIncrease, - cfg.LoadVolume.ProductsSteps, - cfg.LoadVolume.Duration.Duration(), - ) - _, err = wasp.NewProfile(). - Add(wasp.NewGenerator(multiFeedConfig)). - Run(true) - require.NoError(t, err) + l.Info(). + Interface("Request Count", requestCount). + Interface("Fulfilment Count", fulfilmentCount). + Msg("Final Request/Fulfilment Stats") }) + +} + +func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l zerolog.Logger) error { + if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { + for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { + address := common.HexToAddress(sendingKey) + sendingKeyBalance, err := client.BalanceAt(context.Background(), address) + if err != nil { + return err + } + fundingAtLeast := conversions.EtherToWei(big.NewFloat(cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) + fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) + fundingToSendEth := conversions.WeiToEther(fundingToSendWei) + if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { + l.Info(). + Str("Sending Key", sendingKey). + Str("Sending Key Current Balance", sendingKeyBalance.String()). + Str("Should have at least", fundingAtLeast.String()). + Str("Funding Amount in ETH", fundingToSendEth.String()). + Msg("Funding Node's Sending Key") + gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ + To: &address, + }) + if err != nil { + return err + } + err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + if err != nil { + return err + } + } else { + l.Info(). + Str("Sending Key", sendingKey). + Str("Sending Key Current Balance", sendingKeyBalance.String()). + Str("Should have at least", fundingAtLeast.String()). + Msg("Skipping Node's Sending Key funding as it has enough funds") + } + } + } + return nil +} + +func teardown( + t *testing.T, + consumer contracts.VRFv2LoadTestConsumer, + lc *wasp.LokiClient, + updatedLabels map[string]string, + testReporter *testreporters.VRFV2TestReporter, + testType string, + vrfv2Config vrfv2_config.VRFV2Config, +) { + //send final results to Loki + metrics := GetLoadTestMetrics(consumer) + SendMetricsToLoki(metrics, lc, updatedLabels) + //set report data for Slack notification + testReporter.SetReportData( + testType, + metrics.RequestCount, + metrics.FulfilmentCount, + metrics.AverageFulfillmentInMillions, + metrics.SlowestFulfillment, + metrics.FastestFulfillment, + vrfv2Config, + ) + + // send Slack notification + err := testReporter.SendSlackNotification(t, nil) + if err != nil { + log.Warn().Err(err).Msg("Error sending Slack notification") + } } diff --git a/integration-tests/load/vrfv2/vu.go b/integration-tests/load/vrfv2/vu.go deleted file mode 100644 index 7eb02ae330f..00000000000 --- a/integration-tests/load/vrfv2/vu.go +++ /dev/null @@ -1,94 +0,0 @@ -package loadvrfv2 - -import ( - "fmt" - "time" - - "github.com/smartcontractkit/wasp" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" - "github.com/smartcontractkit/chainlink/integration-tests/client" -) - -/* JobVolumeVU is a "virtual user" that creates a VRFv2 job and constantly requesting new randomness only for this job instance */ - -type JobVolumeVU struct { - pace time.Duration - minIncomingConfirmations uint16 - nodes []*client.ChainlinkClient - bc blockchain.EVMClient - contracts *vrfv2_actions.VRFV2Contracts - jobs []vrfv2_actions.VRFV2JobInfo - keyHash [32]byte - stop chan struct{} -} - -func NewJobVolumeVU( - pace time.Duration, - confirmations uint16, - nodes []*client.ChainlinkClient, - bc blockchain.EVMClient, - contracts *vrfv2_actions.VRFV2Contracts, -) *JobVolumeVU { - return &JobVolumeVU{ - pace: pace, - minIncomingConfirmations: confirmations, - nodes: nodes, - bc: bc, - contracts: contracts, - stop: make(chan struct{}, 1), - } -} - -func (m *JobVolumeVU) Clone(_ *wasp.Generator) wasp.VirtualUser { - return &JobVolumeVU{ - pace: m.pace, - minIncomingConfirmations: m.minIncomingConfirmations, - nodes: m.nodes, - bc: m.bc, - contracts: m.contracts, - stop: make(chan struct{}, 1), - } -} - -func (m *JobVolumeVU) Setup(_ *wasp.Generator) error { - jobs, err := vrfv2_actions.CreateVRFV2Jobs(m.nodes, m.contracts.Coordinator, m.bc, m.minIncomingConfirmations) - if err != nil { - return fmt.Errorf("failed to create VRFv2 jobs in setup: %w", err) - } - m.jobs = jobs - m.keyHash = jobs[0].KeyHash - return nil -} - -func (m *JobVolumeVU) Teardown(_ *wasp.Generator) error { - return nil -} - -func (m *JobVolumeVU) Call(l *wasp.Generator) { - time.Sleep(m.pace) - tn := time.Now() - err := m.contracts.LoadTestConsumer.RequestRandomness( - m.keyHash, - vrfConst.SubID, - vrfConst.MinimumConfirmations, - vrfConst.CallbackGasLimit, - vrfConst.NumberOfWords, - vrfConst.RandomnessRequestCountPerRequest, - ) - if err != nil { - l.ResponsesChan <- &wasp.CallResult{Duration: time.Since(tn), Error: err.Error(), Failed: true} - return - } - l.ResponsesChan <- &wasp.CallResult{Duration: time.Since(tn)} -} - -func (m *JobVolumeVU) Stop(_ *wasp.Generator) { - m.stop <- struct{}{} -} - -func (m *JobVolumeVU) StopChan() chan struct{} { - return m.stop -} diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index 09024b28ba7..c7b48fc3a35 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -1,115 +1,107 @@ package smoke import ( + "context" "math/big" "testing" - "time" - "github.com/onsi/gomega" + "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" - vrfConst "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" ) func TestVRFv2Basic(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) + var vrfv2Config vrfv2_config.VRFV2Config + err := envconfig.Process("VRFV2", &vrfv2Config) + require.NoError(t, err) + env, err := test_env.NewCLTestEnvBuilder(). WithTestLogger(t). WithGeth(). WithCLNodes(1). - WithFunding(vrfConst.ChainlinkNodeFundingAmountEth). + WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). WithStandardCleanup(). Build() - require.NoError(t, err) - env.ParallelTransactions(true) + require.NoError(t, err, "error creating test env") - mockFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, vrfConst.LinkEthFeedResponse) - require.NoError(t, err) - lt, err := actions.DeployLINKToken(env.ContractDeployer) - require.NoError(t, err) - vrfv2Contracts, err := vrfv2_actions.DeployVRFV2Contracts(env.ContractDeployer, env.EVMClient, lt, mockFeed) - require.NoError(t, err) - - err = env.EVMClient.WaitForEvents() - require.NoError(t, err) - - err = vrfv2Contracts.Coordinator.SetConfig( - vrfConst.MinimumConfirmations, - vrfConst.MaxGasLimitVRFCoordinatorConfig, - vrfConst.StalenessSeconds, - vrfConst.GasAfterPaymentCalculation, - vrfConst.LinkEthFeedResponse, - vrfConst.VRFCoordinatorV2FeeConfig, - ) - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err) - - err = vrfv2Contracts.Coordinator.CreateSubscription() - require.NoError(t, err) - err = env.EVMClient.WaitForEvents() - require.NoError(t, err) - - err = vrfv2Contracts.Coordinator.AddConsumer(vrfConst.SubID, vrfv2Contracts.LoadTestConsumer.Address()) - require.NoError(t, err) - - err = vrfv2_actions.FundVRFCoordinatorV2Subscription(lt, vrfv2Contracts.Coordinator, env.EVMClient, vrfConst.SubID, vrfConst.VRFSubscriptionFundingAmountLink) - require.NoError(t, err) + env.ParallelTransactions(true) - vrfV2jobs, err := vrfv2_actions.CreateVRFV2Jobs(env.ClCluster.NodeAPIs(), vrfv2Contracts.Coordinator, env.EVMClient, vrfConst.MinimumConfirmations) + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) require.NoError(t, err) - - // this part is here because VRFv2 can work with only a specific key - // [[EVM.KeySpecific]] - // Key = '...' - addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() - require.NoError(t, err) - nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr), - ) - err = env.ClCluster.Nodes[0].Restart(nodeConfig) + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) require.NoError(t, err) - // test and assert - err = vrfv2Contracts.LoadTestConsumer.RequestRandomness( - vrfV2jobs[0].KeyHash, - vrfConst.SubID, - vrfConst.MinimumConfirmations, - vrfConst.CallbackGasLimit, - vrfConst.NumberOfWords, - vrfConst.RandomnessRequestCountPerRequest, + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + env, + vrfv2Config, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + 1, + 1, + l, ) - require.NoError(t, err) - - gom := gomega.NewGomegaWithT(t) - timeout := time.Minute * 2 - var lastRequestID *big.Int - gom.Eventually(func(g gomega.Gomega) { - jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfV2jobs[0].Job.Data.ID) - g.Expect(err).ShouldNot(gomega.HaveOccurred()) - g.Expect(len(jobRuns.Data)).Should(gomega.BeNumerically("==", 1)) - lastRequestID, err = vrfv2Contracts.LoadTestConsumer.GetLastRequestId(testcontext.Get(t)) - l.Debug().Interface("Last Request ID", lastRequestID).Msg("Last Request ID Received") - - g.Expect(err).ShouldNot(gomega.HaveOccurred()) - status, err := vrfv2Contracts.LoadTestConsumer.GetRequestStatus(testcontext.Get(t), lastRequestID) - g.Expect(err).ShouldNot(gomega.HaveOccurred()) - g.Expect(status.Fulfilled).Should(gomega.BeTrue()) - l.Debug().Interface("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") - - g.Expect(err).ShouldNot(gomega.HaveOccurred()) + require.NoError(t, err, "error setting up VRF v2 env") + + subID := subIDs[0] + + subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + + t.Run("Request Randomness", func(t *testing.T) { + testConfig := vrfv2Config + subBalanceBeforeRequest := subscription.Balance + + jobRunsBeforeTest, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2Data.VRFJob.Data.ID) + require.NoError(t, err, "error reading job runs") + + // test and assert + randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + vrfv2Contracts.LoadTestConsumers[0], + vrfv2Contracts.Coordinator, + vrfv2Data, + subID, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + expectedSubBalanceJuels := new(big.Int).Sub(subBalanceBeforeRequest, randomWordsFulfilledEvent.Payment) + subscription, err = vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + subBalanceAfterRequest := subscription.Balance + require.Equal(t, expectedSubBalanceJuels, subBalanceAfterRequest) + + jobRuns, err := env.ClCluster.Nodes[0].API.MustReadRunsByJob(vrfv2Data.VRFJob.Data.ID) + require.NoError(t, err, "error reading job runs") + require.Equal(t, len(jobRunsBeforeTest.Data)+1, len(jobRuns.Data)) + + status, err := vrfv2Contracts.LoadTestConsumers[0].GetRequestStatus(context.Background(), randomWordsFulfilledEvent.RequestId) + require.NoError(t, err, "error getting rand request status") + require.True(t, status.Fulfilled) + l.Debug().Bool("Fulfilment Status", status.Fulfilled).Msg("Random Words Request Fulfilment Status") + + require.Equal(t, testConfig.NumberOfWords, uint32(len(status.RandomWords))) for _, w := range status.RandomWords { - l.Info().Uint64("Output", w.Uint64()).Msg("Randomness fulfilled") - g.Expect(w.Uint64()).Should(gomega.BeNumerically(">", 0), "Expected the VRF job give an answer bigger than 0") + l.Info().Str("Output", w.String()).Msg("Randomness fulfilled") + require.Equal(t, 1, w.Cmp(big.NewInt(0)), "Expected the VRF job give an answer bigger than 0") } - }, timeout, "1s").Should(gomega.Succeed()) + }) } diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index b171ea65f99..9ce9f216995 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -49,7 +49,16 @@ func TestVRFv2Plus(t *testing.T) { // register proving key against oracle address (sending key) in order to test oracleWithdraw defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, vrfv2PlusConfig, linkToken, mockETHLinkFeed, defaultWalletAddress, 1, 1, l) + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + env, + vrfv2PlusConfig, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + 1, + 1, + l, + ) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] @@ -380,6 +389,7 @@ func TestVRFv2Plus(t *testing.T) { require.NoError(t, err) require.False(t, pendingRequestsExist, "Pending requests should not exist") + randomWordsFulfilledEventTimeout := 5 * time.Second _, err = vrfv2plus.RequestRandomnessAndWaitForFulfillment( vrfv2PlusContracts.LoadTestConsumers[0], vrfv2PlusContracts.Coordinator, @@ -388,7 +398,7 @@ func TestVRFv2Plus(t *testing.T) { false, testConfig.RandomnessRequestCountPerRequest, testConfig, - 5*time.Second, + randomWordsFulfilledEventTimeout, l, ) @@ -402,7 +412,7 @@ func TestVRFv2Plus(t *testing.T) { true, testConfig.RandomnessRequestCountPerRequest, testConfig, - testConfig.RandomWordsFulfilledEventTimeout, + randomWordsFulfilledEventTimeout, l, ) @@ -486,7 +496,7 @@ func TestVRFv2Plus(t *testing.T) { Msg("Sub funds returned") //todo - need to use different wallet for each test to verify exact amount of Native/LINK returned - //todo - as defaultWallet is used in other tests in parallel which might affect the balance + //todo - as defaultWallet is used in other tests in parallel which might affect the balance - TT-684 //require.Equal(t, 1, walletBalanceNativeAfterSubCancelling.Cmp(walletBalanceNativeBeforeSubCancelling), "Native funds were not returned after sub cancellation") //todo - this fails on SIMULATED env as tx cost is calculated different as for testnets and it's not receipt.EffectiveGasPrice*receipt.GasUsed diff --git a/integration-tests/testreporters/vrfv2.go b/integration-tests/testreporters/vrfv2.go index c1ab816d4e8..2a72e4d91d0 100644 --- a/integration-tests/testreporters/vrfv2.go +++ b/integration-tests/testreporters/vrfv2.go @@ -1,173 +1,92 @@ package testreporters import ( - "encoding/csv" "fmt" + "math/big" "os" - "path/filepath" "testing" "time" - "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" + "github.com/slack-go/slack" "github.com/smartcontractkit/chainlink-testing-framework/testreporters" ) -type VRFV2SoakTestReporter struct { - Reports map[string]*VRFV2SoakTestReport // contractAddress: Report - namespace string - csvLocation string -} - -type VRFV2SoakTestReport struct { - ContractAddress string - TotalRounds uint - - averageRoundTime time.Duration - LongestRoundTime time.Duration - ShortestRoundTime time.Duration - totalRoundTimes time.Duration - - averageRoundBlocks uint - LongestRoundBlocks uint - ShortestRoundBlocks uint - totalBlockLengths uint -} - -// SetNamespace sets the namespace of the report for clean reports -func (o *VRFV2SoakTestReporter) SetNamespace(namespace string) { - o.namespace = namespace +type VRFV2TestReporter struct { + TestType string + RequestCount *big.Int + FulfilmentCount *big.Int + AverageFulfillmentInMillions *big.Int + SlowestFulfillment *big.Int + FastestFulfillment *big.Int + Vrfv2Config *vrfv2_config.VRFV2Config } -// WriteReport writes VRFV2 Soak test report to logs -func (o *VRFV2SoakTestReporter) WriteReport(folderLocation string) error { - for _, report := range o.Reports { - report.averageRoundBlocks = report.totalBlockLengths / report.TotalRounds - report.averageRoundTime = time.Duration(report.totalRoundTimes.Nanoseconds() / int64(report.TotalRounds)) - } - if err := o.writeCSV(folderLocation); err != nil { - return err - } - - log.Info().Msg("VRFV2 Soak Test Report") - log.Info().Msg("--------------------") - for contractAddress, report := range o.Reports { - log.Info(). - Str("Contract Address", report.ContractAddress). - Uint("Total Rounds Processed", report.TotalRounds). - Str("Average Round Time", fmt.Sprint(report.averageRoundTime)). - Str("Longest Round Time", fmt.Sprint(report.LongestRoundTime)). - Str("Shortest Round Time", fmt.Sprint(report.ShortestRoundTime)). - Uint("Average Round Blocks", report.averageRoundBlocks). - Uint("Longest Round Blocks", report.LongestRoundBlocks). - Uint("Shortest Round Blocks", report.ShortestRoundBlocks). - Msg(contractAddress) - } - log.Info().Msg("--------------------") - return nil +func (o *VRFV2TestReporter) SetReportData( + testType string, + RequestCount *big.Int, + FulfilmentCount *big.Int, + AverageFulfillmentInMillions *big.Int, + SlowestFulfillment *big.Int, + FastestFulfillment *big.Int, + vrfv2Config vrfv2_config.VRFV2Config, +) { + o.TestType = testType + o.RequestCount = RequestCount + o.FulfilmentCount = FulfilmentCount + o.AverageFulfillmentInMillions = AverageFulfillmentInMillions + o.SlowestFulfillment = SlowestFulfillment + o.FastestFulfillment = FastestFulfillment + o.Vrfv2Config = &vrfv2Config } -// SendNotification sends a slack message to a slack webhook and uploads test artifacts -func (o *VRFV2SoakTestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { +// SendSlackNotification sends a slack message to a slack webhook +func (o *VRFV2TestReporter) SendSlackNotification(t *testing.T, slackClient *slack.Client) error { if slackClient == nil { slackClient = slack.New(testreporters.SlackAPIKey) } testFailed := t.Failed() - headerText := ":white_check_mark: VRFV2 Soak Test PASSED :white_check_mark:" + headerText := fmt.Sprintf(":white_check_mark: VRF V2 %s Test PASSED :white_check_mark:", o.TestType) if testFailed { - headerText = ":x: VRFV2 Soak Test FAILED :x:" - } - messageBlocks := testreporters.CommonSlackNotificationBlocks( - headerText, o.namespace, o.csvLocation, - ) - ts, err := testreporters.SendSlackMessage(slackClient, slack.MsgOptionBlocks(messageBlocks...)) - if err != nil { - return err + headerText = fmt.Sprintf(":x: VRF V2 %s Test FAILED :x:", o.TestType) } - return testreporters.UploadSlackFile(slackClient, slack.FileUploadParameters{ - Title: fmt.Sprintf("VRFV2 Soak Test Report %s", o.namespace), - Filetype: "csv", - Filename: fmt.Sprintf("vrfv2_soak_%s.csv", o.namespace), - File: o.csvLocation, - InitialComment: fmt.Sprintf("VRFV2 Soak Test Report %s.", o.namespace), - Channels: []string{testreporters.SlackChannel}, - ThreadTimestamp: ts, + messageBlocks := testreporters.SlackNotifyBlocks(headerText, os.Getenv("SELECTED_NETWORKS"), []string{ + fmt.Sprintf( + "Summary\n"+ + "Perf Test Type: %s\n"+ + "Test Duration set in parameters: %s\n"+ + "Use Existing Env: %t\n"+ + "Request Count: %s\n"+ + "Fulfilment Count: %s\n"+ + "AverageFulfillmentInMillions: %s\n"+ + "Slowest Fulfillment: %s\n"+ + "Fastest Fulfillment: %s \n"+ + "RPS: %d\n"+ + "RateLimitUnitDuration: %s\n"+ + "RandomnessRequestCountPerRequest: %d\n"+ + "RandomnessRequestCountPerRequestDeviation: %d\n", + o.TestType, + o.Vrfv2Config.TestDuration.Truncate(time.Second).String(), + o.Vrfv2Config.UseExistingEnv, + o.RequestCount.String(), + o.FulfilmentCount.String(), + o.AverageFulfillmentInMillions.String(), + o.SlowestFulfillment.String(), + o.FastestFulfillment.String(), + o.Vrfv2Config.RPS, + o.Vrfv2Config.RateLimitUnitDuration.String(), + o.Vrfv2Config.RandomnessRequestCountPerRequest, + o.Vrfv2Config.RandomnessRequestCountPerRequestDeviation, + ), }) -} - -// UpdateReport updates the report based on the latest info -func (o *VRFV2SoakTestReport) UpdateReport(roundTime time.Duration, blockLength uint) { - // Updates min values from default 0 - if o.ShortestRoundBlocks == 0 { - o.ShortestRoundBlocks = blockLength - } - if o.ShortestRoundTime == 0 { - o.ShortestRoundTime = roundTime - } - o.TotalRounds++ - o.totalRoundTimes += roundTime - o.totalBlockLengths += blockLength - if roundTime >= o.LongestRoundTime { - o.LongestRoundTime = roundTime - } - if roundTime <= o.ShortestRoundTime { - o.ShortestRoundTime = roundTime - } - if blockLength >= o.LongestRoundBlocks { - o.LongestRoundBlocks = blockLength - } - if blockLength <= o.ShortestRoundBlocks { - o.ShortestRoundBlocks = blockLength - } -} - -// writes a CSV report on the test runner -func (o *VRFV2SoakTestReporter) writeCSV(folderLocation string) error { - reportLocation := filepath.Join(folderLocation, "./vrfv2_soak_report.csv") - log.Debug().Str("Location", reportLocation).Msg("Writing VRFV2 report") - o.csvLocation = reportLocation - vrfv2ReportFile, err := os.Create(reportLocation) - if err != nil { - return err - } - defer vrfv2ReportFile.Close() - vrfv2ReportWriter := csv.NewWriter(vrfv2ReportFile) - err = vrfv2ReportWriter.Write([]string{ - "Contract Index", - "Contract Address", - "Total Rounds Processed", - "Average Round Time", - "Longest Round Time", - "Shortest Round Time", - "Average Round Blocks", - "Longest Round Blocks", - "Shortest Round Blocks", - }) + _, err := testreporters.SendSlackMessage(slackClient, slack.MsgOptionBlocks(messageBlocks...)) if err != nil { return err } - for contractIndex, report := range o.Reports { - err = vrfv2ReportWriter.Write([]string{ - fmt.Sprint(contractIndex), - report.ContractAddress, - fmt.Sprint(report.TotalRounds), - fmt.Sprint(report.averageRoundTime), - fmt.Sprint(report.LongestRoundTime), - fmt.Sprint(report.ShortestRoundTime), - fmt.Sprint(report.averageRoundBlocks), - fmt.Sprint(report.LongestRoundBlocks), - fmt.Sprint(report.ShortestRoundBlocks), - }) - if err != nil { - return err - } - } - vrfv2ReportWriter.Flush() - - log.Info().Str("Location", reportLocation).Msg("Wrote CSV file") return nil } diff --git a/integration-tests/testreporters/vrfv2plus.go b/integration-tests/testreporters/vrfv2plus.go index 38220ca8821..21e4e52695a 100644 --- a/integration-tests/testreporters/vrfv2plus.go +++ b/integration-tests/testreporters/vrfv2plus.go @@ -49,9 +49,9 @@ func (o *VRFV2PlusTestReporter) SendSlackNotification(t *testing.T, slackClient } testFailed := t.Failed() - headerText := fmt.Sprintf(":white_check_mark: VRF %s Test PASSED :white_check_mark:", o.TestType) + headerText := fmt.Sprintf(":white_check_mark: VRF V2 Plus %s Test PASSED :white_check_mark:", o.TestType) if testFailed { - headerText = fmt.Sprintf(":x: VRF %s Test FAILED :x:", o.TestType) + headerText = fmt.Sprintf(":x: VRF V2 Plus %s Test FAILED :x:", o.TestType) } messageBlocks := testreporters.SlackNotifyBlocks(headerText, os.Getenv("SELECTED_NETWORKS"), []string{ diff --git a/integration-tests/testsetups/vrfv2.go b/integration-tests/testsetups/vrfv2.go deleted file mode 100644 index ef18bd267de..00000000000 --- a/integration-tests/testsetups/vrfv2.go +++ /dev/null @@ -1,204 +0,0 @@ -package testsetups - -//revive:disable:dot-imports -import ( - "context" - "fmt" - "math/big" - "os" - "strconv" - "strings" - "testing" - "time" - - "github.com/rs/zerolog/log" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - reportModel "github.com/smartcontractkit/chainlink-testing-framework/testreporters" - "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" - - "github.com/smartcontractkit/chainlink/integration-tests/client" - "github.com/smartcontractkit/chainlink/integration-tests/contracts" - "github.com/smartcontractkit/chainlink/integration-tests/testreporters" -) - -// VRFV2SoakTest defines a typical VRFV2 soak test -type VRFV2SoakTest struct { - Inputs *VRFV2SoakTestInputs - - TestReporter testreporters.VRFV2SoakTestReporter - - testEnvironment *environment.Environment - namespace string - ChainlinkNodes []*client.ChainlinkK8sClient - chainClient blockchain.EVMClient - DefaultNetwork blockchain.EVMClient - - NumberOfRandRequests int - - ErrorOccurred error - ErrorCount int -} - -// VRFV2SoakTestTestFunc function type for the request and validation you want done on each iteration -type VRFV2SoakTestTestFunc func(t *VRFV2SoakTest, requestNumber int) error - -// VRFV2SoakTestInputs define required inputs to run a vrfv2 soak test -type VRFV2SoakTestInputs struct { - BlockchainClient blockchain.EVMClient // Client for the test to connect to the blockchain with - TestDuration time.Duration `envconfig:"TEST_DURATION" default:"15m"` // How long to run the test for (assuming things pass) - ChainlinkNodeFunding *big.Float `envconfig:"CHAINLINK_NODE_FUNDING" default:".1"` // Amount of ETH to fund each chainlink node with - SubscriptionFunding *big.Int `envconfig:"SUBSCRIPTION_FUNDING" default:"100"` // Amount of Link to fund VRF Coordinator subscription - StopTestOnError bool // Do we want the test to stop after any error or just continue on - - RequestsPerMinute int `envconfig:"REQUESTS_PER_MINUTE" default:"10"` // Number of requests for randomness per minute - RandomnessRequestCountPerRequest int `envconfig:"RANDOMNESS_REQUEST_COUNT_PER_REQUEST" default:"1"` - ConsumerContract contracts.VRFv2LoadTestConsumer - TestFunc VRFV2SoakTestTestFunc // The function that makes the request and validations wanted -} - -// NewVRFV2SoakTest creates a new vrfv2 soak test to setup and run -func NewVRFV2SoakTest(inputs *VRFV2SoakTestInputs, chainlinkNodes []*client.ChainlinkK8sClient) *VRFV2SoakTest { - return &VRFV2SoakTest{ - Inputs: inputs, - TestReporter: testreporters.VRFV2SoakTestReporter{ - Reports: make(map[string]*testreporters.VRFV2SoakTestReport), - }, - ChainlinkNodes: chainlinkNodes, - } -} - -// Setup sets up the test environment -func (v *VRFV2SoakTest) Setup(t *testing.T, env *environment.Environment) { - v.ensureInputValues(t) - v.testEnvironment = env - v.namespace = v.testEnvironment.Cfg.Namespace - v.chainClient.ParallelTransactions(true) -} - -// Run starts the VRFV2 soak test -func (v *VRFV2SoakTest) Run(t *testing.T) { - l := logging.GetTestLogger(t) - l.Info(). - Str("Test Duration", v.Inputs.TestDuration.Truncate(time.Second).String()). - Int("Max number of requests per minute wanted", v.Inputs.RequestsPerMinute). - Msg("Starting VRFV2 Soak Test") - - // set the requests to only run for a certain amount of time - ctx := testcontext.Get(t) - testContext, testCancel := context.WithTimeout(ctx, v.Inputs.TestDuration) - defer testCancel() - - v.NumberOfRandRequests = 0 - - // variables dealing with how often to tick and how to stop the ticker - stop := false - startTime := time.Now() - ticker := time.NewTicker(time.Minute / time.Duration(v.Inputs.RequestsPerMinute)) - - for { - // start the loop by checking to see if any of the TestFunc responses have returned an error - if v.Inputs.StopTestOnError { - require.NoError(t, v.ErrorOccurred, "Found error") - } - select { - case <-testContext.Done(): - // stop making requests - stop = true - ticker.Stop() - break // breaks the select block - case <-ticker.C: - // make the next request - v.NumberOfRandRequests++ - go requestAndValidate(v, v.NumberOfRandRequests) - } - if stop { - break // breaks the for loop and stops the test - } - } - - err := v.chainClient.WaitForEvents() - if err != nil { - l.Error().Err(err).Msg("Error Occurred waiting for On chain events") - } - //wait some buffer time for requests to be fulfilled - //todo - need to find better way for this - time.Sleep(1 * time.Minute) - - loadTestMetrics, err := v.Inputs.ConsumerContract.GetLoadTestMetrics(ctx) - if err != nil { - l.Error().Err(err).Msg("Error Occurred when getting Load Test Metrics from Consumer contract") - } - - averageFulfillmentInBlockTime := new(big.Float).Quo(new(big.Float).SetInt(loadTestMetrics.AverageFulfillmentInMillions), big.NewFloat(1e6)) - - l.Info().Int("Requests", v.NumberOfRandRequests).Msg("Total Completed Requests calculated from Test") - l.Info().Uint64("Requests", loadTestMetrics.RequestCount.Uint64()).Msg("Total Completed Requests calculated from Contract") - l.Info().Uint64("Fulfilments", loadTestMetrics.FulfilmentCount.Uint64()).Msg("Total Completed Fulfilments") - l.Info().Uint64("Fastest Fulfilment", loadTestMetrics.FastestFulfillment.Uint64()).Msg("Fastest Fulfilment") - l.Info().Uint64("Slowest Fulfilment", loadTestMetrics.SlowestFulfillment.Uint64()).Msg("Slowest Fulfilment") - l.Info().Interface("Average Fulfillment", averageFulfillmentInBlockTime).Msg("Average Fulfillment In Block Time") - - //todo - need to calculate 95th percentile response time in Block time and calculate how many requests breached 256 block time requirement - - l.Info().Str("Run Time", time.Since(startTime).String()).Msg("Finished VRFV2 Soak Test Requests") - require.Equal(t, 0, v.ErrorCount, "Expected 0 errors") - require.Equal(t, loadTestMetrics.RequestCount.Uint64(), loadTestMetrics.FulfilmentCount.Uint64(), "Number of Rand Requests should be equal to Number of Fulfillments") -} - -func requestAndValidate(t *VRFV2SoakTest, requestNumber int) { - - log.Info().Int("Request Number", requestNumber).Msg("Making a Request") - err := t.Inputs.TestFunc(t, requestNumber) - - // only set the error to be checked if err is not nil so we avoid race conditions with passing requests - if err != nil { - t.ErrorOccurred = err - log.Error().Err(err).Msg("Error Occurred during test") - t.ErrorCount++ - } -} - -// Networks returns the networks that the test is running on -func (v *VRFV2SoakTest) TearDownVals(t *testing.T) ( - *testing.T, - string, - []*client.ChainlinkK8sClient, - reportModel.TestReporter, - blockchain.EVMClient, -) { - return t, v.namespace, v.ChainlinkNodes, &v.TestReporter, v.chainClient -} - -// ensureValues ensures that all values needed to run the test are present -func (v *VRFV2SoakTest) ensureInputValues(t *testing.T) { - inputs := v.Inputs - require.NotNil(t, inputs.BlockchainClient, "Need a valid blockchain client for the test") - v.chainClient = inputs.BlockchainClient - require.GreaterOrEqual(t, inputs.RequestsPerMinute, 1, "Expecting at least 1 request per minute") - chainlinkNodeFunding, _ := inputs.ChainlinkNodeFunding.Float64() - subscriptionFunding := inputs.SubscriptionFunding.Int64() - require.Greater(t, chainlinkNodeFunding, float64(0), "Need some amount of funding for Chainlink nodes") - require.Greater(t, subscriptionFunding, int64(0), "Need some amount of funding for VRF V2 Coordinator Subscription nodes") - require.GreaterOrEqual(t, inputs.TestDuration, time.Minute, "Test duration should be longer than 1 minute") - require.NotNil(t, inputs.TestFunc, "Expected there to be test to run") -} - -func (i VRFV2SoakTestInputs) SetForRemoteRunner() { - os.Setenv("TEST_VRFV2_TEST_DURATION", i.TestDuration.String()) - os.Setenv("TEST_VRFV2_CHAINLINK_NODE_FUNDING", i.ChainlinkNodeFunding.String()) - os.Setenv("TEST_VRFV2_SUBSCRIPTION_FUNDING", i.SubscriptionFunding.String()) - os.Setenv("TEST_VRFV2_REQUESTS_PER_MINUTE", strconv.Itoa(i.RequestsPerMinute)) - os.Setenv("TEST_VRFV2_RANDOMNESS_REQUEST_COUNT_PER_REQUEST", strconv.Itoa(i.RandomnessRequestCountPerRequest)) - - selectedNetworks := strings.Split(os.Getenv("SELECTED_NETWORKS"), ",") - for _, networkPrefix := range selectedNetworks { - urlEnv := fmt.Sprintf("%s_URLS", networkPrefix) - httpEnv := fmt.Sprintf("%s_HTTP_URLS", networkPrefix) - os.Setenv(fmt.Sprintf("TEST_%s", urlEnv), os.Getenv(urlEnv)) - os.Setenv(fmt.Sprintf("TEST_%s", httpEnv), os.Getenv(httpEnv)) - } -} diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index b7f2b316aa7..47bc8e8cc1e 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -23,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/config" - "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_constants" it_utils "github.com/smartcontractkit/chainlink/integration-tests/utils" ) @@ -224,8 +223,8 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { } } -func WithVRFv2EVMEstimator(addr string) NodeConfigOpt { - est := assets.GWei(vrfv2_constants.MaxGasPriceGWei) +func WithVRFv2EVMEstimator(addr string, maxGasPriceGWei int64) NodeConfigOpt { + est := assets.GWei(maxGasPriceGWei) return func(c *chainlink.Config) { c.EVM[0].KeySpecific = evmcfg.KeySpecificConfig{ { From 853a6170a9cb4ef8cc81e09b4ae6d3b4f6068731 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:20:24 +0100 Subject: [PATCH 196/271] BCF-2666 Add randomisation to testdb sequenced table columns (#11312) * Add randomisation to testdb sequenced table columns * Fix wrong import in pgtest.go * Move testdb sequence randomisation to shell testdb prepare * Use crypto random instead of math rand in test db alter sequences * Satisfy linter * Fix randomiseTestDBSequences err messages * Change randomiseTestDBSequences err msg * Add Error suffix to failedToRandomiseTestDBSequences custom err * Exclude unrelated tables from test db seq randomisation just in case * Rename randomiseTestDBSequences to randomizeTestDBSequences --- core/cmd/shell_local.go | 52 ++++++++++++++++++++++-- core/internal/testutils/pgtest/pgtest.go | 1 - 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index cbd0feccbf1..ada70736f49 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -2,6 +2,7 @@ package cmd import ( "context" + crand "crypto/rand" "database/sql" "fmt" "log" @@ -778,11 +779,13 @@ func (s *Shell) PrepareTestDatabase(c *cli.Context) error { if userOnly { fixturePath = "../store/fixtures/users_only_fixture.sql" } - if err := insertFixtures(dbUrl, fixturePath); err != nil { + if err = insertFixtures(dbUrl, fixturePath); err != nil { return s.errorOut(err) } - - return s.errorOut(dropDanglingTestDBs(s.Logger, db)) + if err = dropDanglingTestDBs(s.Logger, db); err != nil { + return s.errorOut(err) + } + return s.errorOut(randomizeTestDBSequences(db)) } func dropDanglingTestDBs(lggr logger.Logger, db *sqlx.DB) (err error) { @@ -822,6 +825,49 @@ func dropDanglingTestDBs(lggr logger.Logger, db *sqlx.DB) (err error) { return } +type failedToRandomizeTestDBSequencesError struct{} + +func (m *failedToRandomizeTestDBSequencesError) Error() string { + return "failed to randomize test db sequences" +} + +// randomizeTestDBSequences randomizes sequenced table columns sequence +// This is necessary as to avoid false positives in some test cases. +func randomizeTestDBSequences(db *sqlx.DB) error { + seqRows, err := db.Query(`SELECT sequence_schema, sequence_name FROM information_schema.sequences WHERE sequence_schema = $1`, "public") + if err != nil { + return fmt.Errorf("%s: error fetching sequences: %s", failedToRandomizeTestDBSequencesError{}, err) + } + + defer seqRows.Close() + for seqRows.Next() { + var sequenceSchema, sequenceName string + if err = seqRows.Scan(&sequenceSchema, &sequenceName); err != nil { + return fmt.Errorf("%s: failed scanning sequence rows: %s", failedToRandomizeTestDBSequencesError{}, err) + } + + if sequenceName == "goose_migrations_id_seq" || sequenceName == "configurations_id_seq" { + continue + } + + var randNum *big.Int + randNum, err = crand.Int(crand.Reader, utils.NewBigI(10000).ToInt()) + if err != nil { + return fmt.Errorf("%s: failed to generate random number", failedToRandomizeTestDBSequencesError{}) + } + + if _, err = db.Exec(fmt.Sprintf("ALTER SEQUENCE %s.%s RESTART WITH %d", sequenceSchema, sequenceName, randNum)); err != nil { + return fmt.Errorf("%s: failed to alter and restart %s sequence: %w", failedToRandomizeTestDBSequencesError{}, sequenceName, err) + } + } + + if err = seqRows.Err(); err != nil { + return fmt.Errorf("%s: failed to iterate through sequences: %w", failedToRandomizeTestDBSequencesError{}, err) + } + + return nil +} + // PrepareTestDatabaseUserOnly calls ResetDatabase then loads only user fixtures required for local // testing against testnets. Does not include fake chain fixtures. func (s *Shell) PrepareTestDatabaseUserOnly(c *cli.Context) error { diff --git a/core/internal/testutils/pgtest/pgtest.go b/core/internal/testutils/pgtest/pgtest.go index 1900fcc62b3..01024b39c37 100644 --- a/core/internal/testutils/pgtest/pgtest.go +++ b/core/internal/testutils/pgtest/pgtest.go @@ -34,7 +34,6 @@ func NewSqlxDB(t testing.TB) *sqlx.DB { db, err := sqlx.Open(string(dialects.TransactionWrappedPostgres), uuid.New().String()) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, db.Close()) }) - db.MapperFunc(reflectx.CamelToSnakeASCII) return db From ec7c8c9fa7e6865651db624d73f399592ad03592 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Fri, 24 Nov 2023 17:48:52 +0200 Subject: [PATCH 197/271] VRF-765: make optional to cancel subs after test run for VRF WASP tests (#11379) --- integration-tests/load/vrfv2/config.go | 3 +- integration-tests/load/vrfv2/config.toml | 19 +-- integration-tests/load/vrfv2/vrfv2_test.go | 57 +++++---- integration-tests/load/vrfv2plus/config.go | 11 +- integration-tests/load/vrfv2plus/config.toml | 28 +++-- .../load/vrfv2plus/vrfv2plus_test.go | 108 +++++++++++++----- 6 files changed, 142 insertions(+), 84 deletions(-) diff --git a/integration-tests/load/vrfv2/config.go b/integration-tests/load/vrfv2/config.go index e23294e839b..d5e94bb75fb 100644 --- a/integration-tests/load/vrfv2/config.go +++ b/integration-tests/load/vrfv2/config.go @@ -52,7 +52,8 @@ type NewEnvConfig struct { } type Common struct { - MinimumConfirmations uint16 `toml:"minimum_confirmations"` + MinimumConfirmations uint16 `toml:"minimum_confirmations"` + CancelSubsAfterTestRun bool `toml:"cancel_subs_after_test_run"` } type Funding struct { diff --git a/integration-tests/load/vrfv2/config.toml b/integration-tests/load/vrfv2/config.toml index 4e82ec7aeb6..6f54d1ec998 100644 --- a/integration-tests/load/vrfv2/config.toml +++ b/integration-tests/load/vrfv2/config.toml @@ -1,20 +1,21 @@ [Common] minimum_confirmations = 3 +cancel_subs_after_test_run = true [NewEnvConfig] -sub_funds_link = 1 -node_sending_key_funding = 10 +sub_funds_link = 1000 +node_sending_key_funding = 1000 [ExistingEnvConfig] -coordinator_address = "0x50d47e4142598E3411aA864e08a44284e471AC6f" -#consumer_address = "0x087F232165D9bA1A602f148025e5D0666953F64a" -#sub_id = "52116875585187328970776211988181422347535732407068188096422095950800466618218" -key_hash = "0x027f94ff1465b3525f9fc03e9ff7d6d2c0953482246dd6ae07570c45d6631414" +coordinator_address = "" +consumer_address = "" +sub_id = 1 +key_hash = "" create_fund_subs_and_add_consumers = true -link_address = "0xb1D4538B4571d411F07960EF2838Ce337FE1E80E" -sub_funds_link = 3 -node_sending_key_funding_min = 2 +link_address = "" +sub_funds_link = 10 +node_sending_key_funding_min = 1 node_sending_keys = [ "", "", diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 37a70442895..8e74e3aa901 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -97,25 +97,9 @@ func TestVRFV2Performance(t *testing.T) { Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - //cancel subs and return funds to sub owner - for _, subID := range subIDs { - l.Info(). - Uint64("Returning funds from SubID", subID). - Str("Returning funds to", eoaWalletAddress). - Msg("Canceling subscription and returning funds to subscription owner") - pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(context.Background(), subID) - if err != nil { - l.Error().Err(err).Msg("Error checking if pending requests exist") - } - if !pendingRequestsExist { - _, err := vrfv2Contracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) - if err != nil { - l.Error().Err(err).Msg("Error canceling subscription") - } - } else { - l.Error().Uint64("Sub ID", subID).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") - } - + if cfg.Common.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + cancelSubsAndReturnFunds(subIDs, l) } } }). @@ -186,18 +170,10 @@ func TestVRFV2Performance(t *testing.T) { Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - for _, subID := range subIDs { - l.Info(). - Uint64("Returning funds from SubID", subID). - Str("Returning funds to", eoaWalletAddress). - Msg("Canceling subscription and returning funds to subscription owner") - _, err := vrfv2Contracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) - if err != nil { - l.Error().Err(err).Msg("Error canceling subscription") - } + if cfg.Common.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + cancelSubsAndReturnFunds(subIDs, l) } - //err = vrfv2.ReturnFundsForFulfilledRequests(env.EVMClient, vrfv2Contracts.Coordinator, l) - //l.Error().Err(err).Msg("Error returning funds for fulfilled requests") } if err := env.Cleanup(); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") @@ -287,6 +263,27 @@ func TestVRFV2Performance(t *testing.T) { } +func cancelSubsAndReturnFunds(subIDs []uint64, l zerolog.Logger) { + for _, subID := range subIDs { + l.Info(). + Uint64("Returning funds from SubID", subID). + Str("Returning funds to", eoaWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + pendingRequestsExist, err := vrfv2Contracts.Coordinator.PendingRequestsExist(context.Background(), subID) + if err != nil { + l.Error().Err(err).Msg("Error checking if pending requests exist") + } + if !pendingRequestsExist { + _, err := vrfv2Contracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } else { + l.Error().Uint64("Sub ID", subID).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") + } + } +} + func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l zerolog.Logger) error { if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { diff --git a/integration-tests/load/vrfv2plus/config.go b/integration-tests/load/vrfv2plus/config.go index 96dbf99c6b2..43bcf44d044 100644 --- a/integration-tests/load/vrfv2plus/config.go +++ b/integration-tests/load/vrfv2plus/config.go @@ -41,8 +41,9 @@ type ExistingEnvConfig struct { LinkAddress string `toml:"link_address"` SubID string `toml:"sub_id"` KeyHash string `toml:"key_hash"` - SubFunding - CreateFundSubsAndAddConsumers bool `toml:"create_fund_subs_and_add_consumers"` + Funding + CreateFundSubsAndAddConsumers bool `toml:"create_fund_subs_and_add_consumers"` + NodeSendingKeys []string `toml:"node_sending_keys"` } type NewEnvConfig struct { @@ -50,12 +51,14 @@ type NewEnvConfig struct { } type Common struct { - MinimumConfirmations uint16 `toml:"minimum_confirmations"` + MinimumConfirmations uint16 `toml:"minimum_confirmations"` + CancelSubsAfterTestRun bool `toml:"cancel_subs_after_test_run"` } type Funding struct { - NodeFunds float64 `toml:"node_funds"` SubFunding + NodeSendingKeyFunding float64 `toml:"node_sending_key_funding"` + NodeSendingKeyFundingMin float64 `toml:"node_sending_key_funding_min"` } type SubFunding struct { diff --git a/integration-tests/load/vrfv2plus/config.toml b/integration-tests/load/vrfv2plus/config.toml index e3200fafe22..8ccf60c6d80 100644 --- a/integration-tests/load/vrfv2plus/config.toml +++ b/integration-tests/load/vrfv2plus/config.toml @@ -1,21 +1,31 @@ [Common] minimum_confirmations = 3 +cancel_subs_after_test_run = true [NewEnvConfig] sub_funds_link = 1 sub_funds_native = 1 node_funds = 10 +node_sending_key_funding = 1000 [ExistingEnvConfig] -coordinator_address = "0x27b61f155F772b291D1d9B478BeAd37B2Ae447b0" -#consumer_address = "0x087F232165D9bA1A602f148025e5D0666953F64a" -#sub_id = "52116875585187328970776211988181422347535732407068188096422095950800466618218" -key_hash = "0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae" +coordinator_address = "" +consumer_address = "" +sub_id = 1 +key_hash = "" create_fund_subs_and_add_consumers = true -link_address = "0x779877A7B0D9E8603169DdbD7836e478b4624789" -sub_funds_link = 3 -sub_funds_native = 1 +link_address = "" +sub_funds_link = 10 +node_sending_key_funding_min = 1 +node_sending_keys = [ + "", + "", + "", + "", + "", + "", +] # 10 RPM - 1 tx request with 1 rand request in each tx every 6 seconds [Soak] @@ -25,7 +35,7 @@ randomness_request_count_per_request = 1 # amount of randomness requests to make randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting number_of_sub_to_create = 1 -# approx 60 RPM - 1 tx request with 4 rand requests in each tx every 3 seconds +# approx 60 RPM - 1 tx request with 3 rand requests in each tx every 3 seconds [Load] rate_limit_unit_duration = "3s" rps = 1 @@ -47,4 +57,4 @@ rate_limit_unit_duration = "1m" rps = 1 randomness_request_count_per_request = 150 # amount of randomness requests to make per one TX request randomness_request_count_per_request_deviation = 0 #NOTE - deviation should be less than randomness_request_count_per_request setting -number_of_sub_to_create = 1 \ No newline at end of file +number_of_sub_to_create = 1 diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index 11e160297f0..ac22b5256f6 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -1,19 +1,24 @@ package loadvrfv2plus import ( + "context" "math/big" "os" "sync" "testing" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/smartcontractkit/wasp" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/integration-tests/testreporters" @@ -93,25 +98,9 @@ func TestVRFV2PlusPerformance(t *testing.T) { Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - //cancel subs and return funds to sub owner - for _, subID := range subIDs { - l.Info(). - Str("Returning funds from SubID", subID.String()). - Str("Returning funds to", eoaWalletAddress). - Msg("Canceling subscription and returning funds to subscription owner") - pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(testcontext.Get(t), subID) - if err != nil { - l.Error().Err(err).Msg("Error checking if pending requests exist") - } - if !pendingRequestsExist { - _, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) - if err != nil { - l.Error().Err(err).Msg("Error canceling subscription") - } - } else { - l.Error().Str("Sub ID", subID.String()).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") - } - + if cfg.Common.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + cancelSubsAndReturnFunds(subIDs, l) } } }). @@ -147,6 +136,9 @@ func TestVRFV2PlusPerformance(t *testing.T) { subIDs = append(subIDs, subID) } + err = FundNodesIfNeeded(cfg, env.EVMClient, l) + require.NoError(t, err) + vrfv2PlusContracts = &vrfv2plus.VRFV2_5Contracts{ Coordinator: coordinator, LoadTestConsumers: consumers, @@ -166,7 +158,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { } else { //todo: temporary solution with envconfig and toml config until VRF-662 is implemented - vrfv2PlusConfig.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeFunds + vrfv2PlusConfig.ChainlinkNodeFunding = cfg.NewEnvConfig.NodeSendingKeyFunding vrfv2PlusConfig.SubscriptionFundingAmountLink = cfg.NewEnvConfig.Funding.SubFundsLink vrfv2PlusConfig.SubscriptionFundingAmountNative = cfg.NewEnvConfig.Funding.SubFundsNative env, err = test_env.NewCLTestEnvBuilder(). @@ -183,18 +175,10 @@ func TestVRFV2PlusPerformance(t *testing.T) { Str("Network Name", env.EVMClient.GetNetworkName()). Msg("Network is a simulated network. Skipping fund return for Coordinator Subscriptions.") } else { - for _, subID := range subIDs { - l.Info(). - Str("Returning funds from SubID", subID.String()). - Str("Returning funds to", eoaWalletAddress). - Msg("Canceling subscription and returning funds to subscription owner") - _, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) - if err != nil { - l.Error().Err(err).Msg("Error canceling subscription") - } + if cfg.Common.CancelSubsAfterTestRun { + //cancel subs and return funds to sub owner + cancelSubsAndReturnFunds(subIDs, l) } - //err = vrfv2plus.ReturnFundsForFulfilledRequests(env.EVMClient, vrfv2PlusContracts.Coordinator, l) - //l.Error().Err(err).Msg("Error returning funds for fulfilled requests") } if err := env.Cleanup(); err != nil { l.Error().Err(err).Msg("Error cleaning up test environment") @@ -281,6 +265,68 @@ func TestVRFV2PlusPerformance(t *testing.T) { Interface("Fulfilment Count", fulfilmentCount). Msg("Final Request/Fulfilment Stats") }) + +} + +func cancelSubsAndReturnFunds(subIDs []*big.Int, l zerolog.Logger) { + for _, subID := range subIDs { + l.Info(). + Str("Returning funds from SubID", subID.String()). + Str("Returning funds to", eoaWalletAddress). + Msg("Canceling subscription and returning funds to subscription owner") + pendingRequestsExist, err := vrfv2PlusContracts.Coordinator.PendingRequestsExist(context.Background(), subID) + if err != nil { + l.Error().Err(err).Msg("Error checking if pending requests exist") + } + if !pendingRequestsExist { + _, err := vrfv2PlusContracts.Coordinator.CancelSubscription(subID, common.HexToAddress(eoaWalletAddress)) + if err != nil { + l.Error().Err(err).Msg("Error canceling subscription") + } + } else { + l.Error().Str("Sub ID", subID.String()).Msg("Pending requests exist for subscription, cannot cancel subscription and return funds") + } + } +} + +func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l zerolog.Logger) error { + if cfg.ExistingEnvConfig.NodeSendingKeyFundingMin > 0 { + for _, sendingKey := range cfg.ExistingEnvConfig.NodeSendingKeys { + address := common.HexToAddress(sendingKey) + sendingKeyBalance, err := client.BalanceAt(context.Background(), address) + if err != nil { + return err + } + fundingAtLeast := conversions.EtherToWei(big.NewFloat(cfg.ExistingEnvConfig.NodeSendingKeyFundingMin)) + fundingToSendWei := new(big.Int).Sub(fundingAtLeast, sendingKeyBalance) + fundingToSendEth := conversions.WeiToEther(fundingToSendWei) + if fundingToSendWei.Cmp(big.NewInt(0)) == 1 { + l.Info(). + Str("Sending Key", sendingKey). + Str("Sending Key Current Balance", sendingKeyBalance.String()). + Str("Should have at least", fundingAtLeast.String()). + Str("Funding Amount in ETH", fundingToSendEth.String()). + Msg("Funding Node's Sending Key") + gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ + To: &address, + }) + if err != nil { + return err + } + err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + if err != nil { + return err + } + } else { + l.Info(). + Str("Sending Key", sendingKey). + Str("Sending Key Current Balance", sendingKeyBalance.String()). + Str("Should have at least", fundingAtLeast.String()). + Msg("Skipping Node's Sending Key funding as it has enough funds") + } + } + } + return nil } func teardown( From b87f21eff26d1ee4f7f709ecc41580e83edd1de0 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:12:58 +0100 Subject: [PATCH 198/271] Call proper loop for outOfSyncLoop exist on close test (#11384) --- core/chains/evm/client/node_lifecycle_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/chains/evm/client/node_lifecycle_test.go b/core/chains/evm/client/node_lifecycle_test.go index 9fdd9bb64e1..05b8af13ec5 100644 --- a/core/chains/evm/client/node_lifecycle_test.go +++ b/core/chains/evm/client/node_lifecycle_test.go @@ -521,7 +521,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { n.wg.Add(1) go func() { defer close(ch) - n.aliveLoop() + n.outOfSyncLoop(func(num int64, td *utils.Big) bool { return false }) }() assert.NoError(t, n.Close()) testutils.WaitWithTimeout(t, ch, "expected outOfSyncLoop to exit") From 9686592f6876890545841f6e740a875eec8d1338 Mon Sep 17 00:00:00 2001 From: Ilja Pavlovs Date: Mon, 27 Nov 2023 15:58:36 +0200 Subject: [PATCH 199/271] =?UTF-8?q?VRF-543:=20add=20test=20for=20verifying?= =?UTF-8?q?=20round=20robin=20for=20multiple=20sending=20keys=E2=80=A6=20(?= =?UTF-8?q?#11382)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * VRF-543: add test for verifying round robin for multiple sending keys in VRF v2 and VRF V2 Plus * VRF-543: fixing lint issue --- integration-tests/actions/actions.go | 23 ++++ .../actions/vrfv2_actions/vrfv2_steps.go | 45 +++++-- .../actions/vrfv2plus/vrfv2plus_steps.go | 42 +++++-- .../contracts/contract_vrf_models.go | 2 +- .../contracts/ethereum_vrfv2_contracts.go | 15 ++- integration-tests/go.mod | 2 +- integration-tests/load/vrfv2/vrfv2_test.go | 10 +- .../load/vrfv2plus/vrfv2plus_test.go | 1 + integration-tests/smoke/vrfv2_test.go | 93 +++++++++++++++ integration-tests/smoke/vrfv2plus_test.go | 111 +++++++++++++++++- integration-tests/types/config/node/core.go | 22 ++-- 11 files changed, 318 insertions(+), 48 deletions(-) diff --git a/integration-tests/actions/actions.go b/integration-tests/actions/actions.go index 438c36a9f0e..624df39c479 100644 --- a/integration-tests/actions/actions.go +++ b/integration-tests/actions/actions.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum" @@ -458,3 +459,25 @@ func GenerateWallet() (common.Address, error) { } return crypto.PubkeyToAddress(*publicKeyECDSA), nil } + +// todo - move to CTF +func FundAddress(client blockchain.EVMClient, sendingKey string, fundingToSendEth *big.Float) error { + address := common.HexToAddress(sendingKey) + gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ + To: &address, + }) + if err != nil { + return err + } + err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + if err != nil { + return err + } + return nil +} + +// todo - move to CTF +func GetTxFromAddress(tx *types.Transaction) (string, error) { + from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) + return from.String(), err +} diff --git a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go index f565af26d93..4ea4a4a8534 100644 --- a/integration-tests/actions/vrfv2_actions/vrfv2_steps.go +++ b/integration-tests/actions/vrfv2_actions/vrfv2_steps.go @@ -29,6 +29,7 @@ import ( var ( ErrNodePrimaryKey = "error getting node's primary ETH key" + ErrNodeNewTxKey = "error creating node's EVM transaction key" ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" ErrRegisteringProvingKey = "error registering a proving key on Coordinator contract" ErrRegisterProvingKey = "error registering proving keys" @@ -105,7 +106,7 @@ func DeployVRFV2Consumers(contractDeployer contracts.ContractDeployer, coordinat func CreateVRFV2Job( chainlinkNode *client.ChainlinkClient, coordinatorAddress string, - nativeTokenPrimaryKeyAddress string, + nativeTokenKeyAddresses []string, pubKeyCompressed string, chainID string, minIncomingConfirmations uint16, @@ -122,7 +123,7 @@ func CreateVRFV2Job( job, err := chainlinkNode.MustCreateJob(&client.VRFV2JobSpec{ Name: fmt.Sprintf("vrf-v2-%s", jobUUID), CoordinatorAddress: coordinatorAddress, - FromAddresses: []string{nativeTokenPrimaryKeyAddress}, + FromAddresses: nativeTokenKeyAddresses, EVMChainID: chainID, MinIncomingConfirmations: int(minIncomingConfirmations), PublicKey: pubKeyCompressed, @@ -181,6 +182,7 @@ func SetupVRFV2Environment( linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, registerProvingKeyAgainstAddress string, + numberOfTxKeysToCreate int, numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, @@ -254,17 +256,21 @@ func SetupVRFV2Environment( } chainID := env.EVMClient.GetChainID() - + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2Config, numberOfTxKeysToCreate, chainID) + if err != nil { + return nil, nil, nil, err + } nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } + allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) l.Info().Msg("Creating VRFV2 Job") vrfV2job, err := CreateVRFV2Job( env.ClCluster.NodeAPIs()[0], vrfv2Contracts.Coordinator.Address(), - nativeTokenPrimaryKeyAddress, + allNativeTokenKeyAddresses, pubKeyCompressed, chainID.String(), vrfv2Config.MinimumConfirmations, @@ -276,12 +282,8 @@ func SetupVRFV2Environment( // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' - addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() - if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) - } nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr, vrfv2Config.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2Config.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -306,6 +308,25 @@ func SetupVRFV2Environment( return vrfv2Contracts, subIDs, &data, nil } +func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { + var newNativeTokenKeyAddresses []string + for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { + newTxKey, response, err := env.ClCluster.NodeAPIs()[0].CreateTxKey("evm", chainID.String()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrNodeNewTxKey, err) + } + if response.StatusCode != 200 { + return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) + } + newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.ID) + err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(vrfv2Config.ChainlinkNodeFunding)) + if err != nil { + return nil, err + } + } + return newNativeTokenKeyAddresses, nil +} + func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, vrfv2Config vrfv2_config.VRFV2Config, @@ -448,7 +469,7 @@ func RequestRandomnessAndWaitForFulfillment( l zerolog.Logger, ) (*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled, error) { logRandRequest(consumer.Address(), coordinator.Address(), subID, vrfv2Config, l) - err := consumer.RequestRandomness( + _, err := consumer.RequestRandomness( vrfv2Data.KeyHash, subID, vrfv2Config.MinimumConfirmations, @@ -460,7 +481,7 @@ func RequestRandomnessAndWaitForFulfillment( return nil, fmt.Errorf("%s, err %w", ErrRequestRandomness, err) } - return WaitForRequestAndFulfillmentEvents( + fulfillmentEvents, err := WaitForRequestAndFulfillmentEvents( consumer.Address(), coordinator, vrfv2Data, @@ -468,6 +489,7 @@ func RequestRandomnessAndWaitForFulfillment( randomWordsFulfilledEventTimeout, l, ) + return fulfillmentEvents, err } func WaitForRequestAndFulfillmentEvents( @@ -489,7 +511,6 @@ func WaitForRequestAndFulfillmentEvents( } LogRandomnessRequestedEvent(l, coordinator, randomWordsRequestedEvent) - randomWordsFulfilledEvent, err := coordinator.WaitForRandomWordsFulfilledEvent( []*big.Int{randomWordsRequestedEvent.RequestId}, randomWordsFulfilledEventTimeout, diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index d0a33948e31..0e3c8096f0c 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -7,9 +7,8 @@ import ( "sync" "time" - "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" - commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-testing-framework/utils/conversions" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrfv2plus_wrapper_load_test_consumer" @@ -31,6 +30,7 @@ import ( var ( ErrNodePrimaryKey = "error getting node's primary ETH key" + ErrNodeNewTxKey = "error creating node's EVM transaction key" ErrCreatingProvingKeyHash = "error creating a keyHash from the proving key" ErrRegisteringProvingKey = "error registering a proving key on Coordinator contract" ErrRegisterProvingKey = "error registering proving keys" @@ -112,7 +112,7 @@ func DeployVRFV2PlusConsumers(contractDeployer contracts.ContractDeployer, coord func CreateVRFV2PlusJob( chainlinkNode *client.ChainlinkClient, coordinatorAddress string, - nativeTokenPrimaryKeyAddress string, + nativeTokenKeyAddresses []string, pubKeyCompressed string, chainID string, minIncomingConfirmations uint16, @@ -129,7 +129,7 @@ func CreateVRFV2PlusJob( job, err := chainlinkNode.MustCreateJob(&client.VRFV2PlusJobSpec{ Name: fmt.Sprintf("vrf-v2-plus-%s", jobUUID), CoordinatorAddress: coordinatorAddress, - FromAddresses: []string{nativeTokenPrimaryKeyAddress}, + FromAddresses: nativeTokenKeyAddresses, EVMChainID: chainID, MinIncomingConfirmations: int(minIncomingConfirmations), PublicKey: pubKeyCompressed, @@ -207,6 +207,7 @@ func SetupVRFV2_5Environment( linkToken contracts.LinkToken, mockNativeLINKFeed contracts.MockETHLINKFeed, registerProvingKeyAgainstAddress string, + numberOfTxKeysToCreate int, numberOfConsumers int, numberOfSubToCreate int, l zerolog.Logger, @@ -273,17 +274,21 @@ func SetupVRFV2_5Environment( } chainID := env.EVMClient.GetChainID() - + newNativeTokenKeyAddresses, err := CreateAndFundSendingKeys(env, vrfv2PlusConfig, numberOfTxKeysToCreate, chainID) + if err != nil { + return nil, nil, nil, err + } nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrNodePrimaryKey, err) } + allNativeTokenKeyAddresses := append(newNativeTokenKeyAddresses, nativeTokenPrimaryKeyAddress) l.Info().Msg("Creating VRFV2 Plus Job") job, err := CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], vrfv2_5Contracts.Coordinator.Address(), - nativeTokenPrimaryKeyAddress, + allNativeTokenKeyAddresses, pubKeyCompressed, chainID.String(), vrfv2PlusConfig.MinimumConfirmations, @@ -295,12 +300,8 @@ func SetupVRFV2_5Environment( // this part is here because VRFv2 can work with only a specific key // [[EVM.KeySpecific]] // Key = '...' - addr, err := env.ClCluster.Nodes[0].API.PrimaryEthAddress() - if err != nil { - return nil, nil, nil, fmt.Errorf("%s, err %w", ErrGetPrimaryKey, err) - } nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, - node.WithVRFv2EVMEstimator(addr, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), + node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), ) l.Info().Msg("Restarting Node with new sending key PriceMax configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) @@ -325,6 +326,25 @@ func SetupVRFV2_5Environment( return vrfv2_5Contracts, subIDs, &data, nil } +func CreateAndFundSendingKeys(env *test_env.CLClusterTestEnv, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, numberOfNativeTokenAddressesToCreate int, chainID *big.Int) ([]string, error) { + var newNativeTokenKeyAddresses []string + for i := 0; i < numberOfNativeTokenAddressesToCreate; i++ { + newTxKey, response, err := env.ClCluster.NodeAPIs()[0].CreateTxKey("evm", chainID.String()) + if err != nil { + return nil, fmt.Errorf("%s, err %w", ErrNodeNewTxKey, err) + } + if response.StatusCode != 200 { + return nil, fmt.Errorf("error creating transaction key - response code, err %d", response.StatusCode) + } + newNativeTokenKeyAddresses = append(newNativeTokenKeyAddresses, newTxKey.Data.ID) + err = actions.FundAddress(env.EVMClient, newTxKey.Data.ID, big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)) + if err != nil { + return nil, err + } + } + return newNativeTokenKeyAddresses, nil +} + func CreateFundSubsAndAddConsumers( env *test_env.CLClusterTestEnv, vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig, diff --git a/integration-tests/contracts/contract_vrf_models.go b/integration-tests/contracts/contract_vrf_models.go index 5f850fce1a7..548cac252b1 100644 --- a/integration-tests/contracts/contract_vrf_models.go +++ b/integration-tests/contracts/contract_vrf_models.go @@ -170,7 +170,7 @@ type VRFv2Consumer interface { type VRFv2LoadTestConsumer interface { Address() string - RequestRandomness(hash [32]byte, subID uint64, confs uint16, gasLimit uint32, numWords uint32, requestCount uint16) error + RequestRandomness(hash [32]byte, subID uint64, confs uint16, gasLimit uint32, numWords uint32, requestCount uint16) (*types.Transaction, error) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_load_test_with_metrics.GetRequestStatus, error) GetLastRequestId(ctx context.Context) (*big.Int, error) GetLoadTestMetrics(ctx context.Context) (*VRFLoadTestMetrics, error) diff --git a/integration-tests/contracts/ethereum_vrfv2_contracts.go b/integration-tests/contracts/ethereum_vrfv2_contracts.go index ac3926fa746..d1637c93c87 100644 --- a/integration-tests/contracts/ethereum_vrfv2_contracts.go +++ b/integration-tests/contracts/ethereum_vrfv2_contracts.go @@ -441,17 +441,24 @@ func (v *EthereumVRFv2LoadTestConsumer) Address() string { return v.address.Hex() } -func (v *EthereumVRFv2LoadTestConsumer) RequestRandomness(keyHash [32]byte, subID uint64, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, requestCount uint16) error { +func (v *EthereumVRFv2LoadTestConsumer) RequestRandomness( + keyHash [32]byte, + subID uint64, + requestConfirmations uint16, + callbackGasLimit uint32, + numWords uint32, + requestCount uint16, +) (*types.Transaction, error) { opts, err := v.client.TransactionOpts(v.client.GetDefaultWallet()) if err != nil { - return err + return nil, err } tx, err := v.consumer.RequestRandomWords(opts, subID, requestConfirmations, keyHash, callbackGasLimit, numWords, requestCount) if err != nil { - return err + return nil, err } - return v.client.ProcessTransaction(tx) + return tx, v.client.ProcessTransaction(tx) } func (v *EthereumVRFv2Consumer) GetRequestStatus(ctx context.Context, requestID *big.Int) (vrf_v2_consumer_wrapper.GetRequestStatus, error) { diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 2338c0caa58..d0ca2346636 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -11,6 +11,7 @@ require ( github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.12.0 github.com/go-resty/resty/v2 v2.7.0 + github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.1 github.com/jmoiron/sqlx v1.3.5 github.com/kelseyhightower/envconfig v1.4.0 @@ -191,7 +192,6 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect diff --git a/integration-tests/load/vrfv2/vrfv2_test.go b/integration-tests/load/vrfv2/vrfv2_test.go index 8e74e3aa901..ae109f75e28 100644 --- a/integration-tests/load/vrfv2/vrfv2_test.go +++ b/integration-tests/load/vrfv2/vrfv2_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/kelseyhightower/envconfig" "github.com/rs/zerolog" @@ -199,6 +198,7 @@ func TestVRFV2Performance(t *testing.T) { mockETHLinkFeed, //register proving key against EOA address in order to return funds to this address env.EVMClient.GetDefaultWallet().Address(), + 0, 1, vrfv2Config.NumberOfSubToCreate, l, @@ -302,13 +302,7 @@ func FundNodesIfNeeded(cfg *PerformanceConfig, client blockchain.EVMClient, l ze Str("Should have at least", fundingAtLeast.String()). Str("Funding Amount in ETH", fundingToSendEth.String()). Msg("Funding Node's Sending Key") - gasEstimates, err := client.EstimateGas(ethereum.CallMsg{ - To: &address, - }) - if err != nil { - return err - } - err = client.Fund(sendingKey, fundingToSendEth, gasEstimates) + err := actions.FundAddress(client, sendingKey, fundingToSendEth) if err != nil { return err } diff --git a/integration-tests/load/vrfv2plus/vrfv2plus_test.go b/integration-tests/load/vrfv2plus/vrfv2plus_test.go index ac22b5256f6..4b6728440b3 100644 --- a/integration-tests/load/vrfv2plus/vrfv2plus_test.go +++ b/integration-tests/load/vrfv2plus/vrfv2plus_test.go @@ -204,6 +204,7 @@ func TestVRFV2PlusPerformance(t *testing.T) { mockETHLinkFeed, //register proving key against EOA address in order to return funds to this address env.EVMClient.GetDefaultWallet().Address(), + 0, 1, vrfv2PlusConfig.NumberOfSubToCreate, l, diff --git a/integration-tests/smoke/vrfv2_test.go b/integration-tests/smoke/vrfv2_test.go index c7b48fc3a35..4edb92e5df6 100644 --- a/integration-tests/smoke/vrfv2_test.go +++ b/integration-tests/smoke/vrfv2_test.go @@ -5,9 +5,12 @@ import ( "math/big" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions" "github.com/smartcontractkit/chainlink/integration-tests/actions/vrfv2_actions/vrfv2_config" @@ -44,12 +47,14 @@ func TestVRFv2Basic(t *testing.T) { // register proving key against oracle address (sending key) in order to test oracleWithdraw defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + numberOfTxKeysToCreate := 1 vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( env, vrfv2Config, linkToken, mockETHLinkFeed, defaultWalletAddress, + numberOfTxKeysToCreate, 1, 1, l, @@ -105,3 +110,91 @@ func TestVRFv2Basic(t *testing.T) { } }) } + +func TestVRFv2MultipleSendingKeys(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + var vrfv2Config vrfv2_config.VRFV2Config + err := envconfig.Process("VRFV2", &vrfv2Config) + require.NoError(t, err) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(1). + WithFunding(big.NewFloat(vrfv2Config.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2Config.LinkNativeFeedResponse)) + require.NoError(t, err) + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err) + + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + numberOfTxKeysToCreate := 2 + vrfv2Contracts, subIDs, vrfv2Data, err := vrfv2_actions.SetupVRFV2Environment( + env, + vrfv2Config, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + numberOfTxKeysToCreate, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up VRF v2 env") + + subID := subIDs[0] + + subscription, err := vrfv2Contracts.Coordinator.GetSubscription(context.Background(), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2_actions.LogSubDetails(l, subscription, subID, vrfv2Contracts.Coordinator) + + t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { + testConfig := vrfv2Config + txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") + require.NoError(t, err, "error reading tx keys") + + require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) + + var fulfillmentTxFromAddresses []string + for i := 0; i < numberOfTxKeysToCreate+1; i++ { + randomWordsFulfilledEvent, err := vrfv2_actions.RequestRandomnessAndWaitForFulfillment( + vrfv2Contracts.LoadTestConsumers[0], + vrfv2Contracts.Coordinator, + vrfv2Data, + subID, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + //todo - move TransactionByHash to EVMClient in CTF + fulfillmentTx, _, err := env.EVMClient.(*blockchain.EthereumMultinodeClient).DefaultClient.(*blockchain.EthereumClient). + Client.TransactionByHash(context.Background(), randomWordsFulfilledEvent.Raw.TxHash) + require.NoError(t, err, "error getting tx from hash") + fulfillmentTxFromAddress, err := actions.GetTxFromAddress(fulfillmentTx) + require.NoError(t, err, "error getting tx from address") + fulfillmentTxFromAddresses = append(fulfillmentTxFromAddresses, fulfillmentTxFromAddress) + } + require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) + var txKeyAddresses []string + for _, txKey := range txKeys.Data { + txKeyAddresses = append(txKeyAddresses, txKey.ID) + } + less := func(a, b string) bool { return a < b } + equalIgnoreOrder := cmp.Diff(txKeyAddresses, fulfillmentTxFromAddresses, cmpopts.SortSlices(less)) == "" + require.True(t, equalIgnoreOrder) + }) +} diff --git a/integration-tests/smoke/vrfv2plus_test.go b/integration-tests/smoke/vrfv2plus_test.go index 9ce9f216995..f4f52b6ee01 100644 --- a/integration-tests/smoke/vrfv2plus_test.go +++ b/integration-tests/smoke/vrfv2plus_test.go @@ -1,15 +1,19 @@ package smoke import ( + "context" "fmt" "math/big" "testing" "time" "github.com/ethereum/go-ethereum/common" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/kelseyhightower/envconfig" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_v2plus_upgraded_version" @@ -49,12 +53,14 @@ func TestVRFv2Plus(t *testing.T) { // register proving key against oracle address (sending key) in order to test oracleWithdraw defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + numberOfTxKeysToCreate := 2 vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( env, vrfv2PlusConfig, linkToken, mockETHLinkFeed, defaultWalletAddress, + numberOfTxKeysToCreate, 1, 1, l, @@ -596,6 +602,97 @@ func TestVRFv2Plus(t *testing.T) { }) } +func TestVRFv2PlusMultipleSendingKeys(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + var vrfv2PlusConfig vrfv2plus_config.VRFV2PlusConfig + err := envconfig.Process("VRFV2PLUS", &vrfv2PlusConfig) + require.NoError(t, err) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithCLNodes(1). + WithFunding(big.NewFloat(vrfv2PlusConfig.ChainlinkNodeFunding)). + WithStandardCleanup(). + Build() + require.NoError(t, err, "error creating test env") + + env.ParallelTransactions(true) + + mockETHLinkFeed, err := actions.DeployMockETHLinkFeed(env.ContractDeployer, big.NewInt(vrfv2PlusConfig.LinkNativeFeedResponse)) + require.NoError(t, err, "error deploying mock ETH/LINK feed") + + linkToken, err := actions.DeployLINKToken(env.ContractDeployer) + require.NoError(t, err, "error deploying LINK contract") + + // register proving key against oracle address (sending key) in order to test oracleWithdraw + defaultWalletAddress := env.EVMClient.GetDefaultWallet().Address() + + numberOfTxKeysToCreate := 2 + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + env, + vrfv2PlusConfig, + linkToken, + mockETHLinkFeed, + defaultWalletAddress, + numberOfTxKeysToCreate, + 1, + 1, + l, + ) + require.NoError(t, err, "error setting up VRF v2_5 env") + + subID := subIDs[0] + + subscription, err := vrfv2PlusContracts.Coordinator.GetSubscription(testcontext.Get(t), subID) + require.NoError(t, err, "error getting subscription information") + + vrfv2plus.LogSubDetails(l, subscription, subID, vrfv2PlusContracts.Coordinator) + + t.Run("Request Randomness with multiple sending keys", func(t *testing.T) { + testConfig := vrfv2PlusConfig + var isNativeBilling = false + txKeys, _, err := env.ClCluster.Nodes[0].API.ReadTxKeys("evm") + require.NoError(t, err, "error reading tx keys") + + require.Equal(t, numberOfTxKeysToCreate+1, len(txKeys.Data)) + + var fulfillmentTxFromAddresses []string + for i := 0; i < numberOfTxKeysToCreate+1; i++ { + randomWordsFulfilledEvent, err := vrfv2plus.RequestRandomnessAndWaitForFulfillment( + vrfv2PlusContracts.LoadTestConsumers[0], + vrfv2PlusContracts.Coordinator, + vrfv2PlusData, + subID, + isNativeBilling, + testConfig.RandomnessRequestCountPerRequest, + testConfig, + testConfig.RandomWordsFulfilledEventTimeout, + l, + ) + require.NoError(t, err, "error requesting randomness and waiting for fulfilment") + + //todo - move TransactionByHash to EVMClient in CTF + fulfillmentTx, _, err := env.EVMClient.(*blockchain.EthereumMultinodeClient).DefaultClient.(*blockchain.EthereumClient). + Client.TransactionByHash(context.Background(), randomWordsFulfilledEvent.Raw.TxHash) + require.NoError(t, err, "error getting tx from hash") + fulfillmentTxFromAddress, err := actions.GetTxFromAddress(fulfillmentTx) + require.NoError(t, err, "error getting tx from address") + fulfillmentTxFromAddresses = append(fulfillmentTxFromAddresses, fulfillmentTxFromAddress) + } + require.Equal(t, numberOfTxKeysToCreate+1, len(fulfillmentTxFromAddresses)) + var txKeyAddresses []string + for _, txKey := range txKeys.Data { + txKeyAddresses = append(txKeyAddresses, txKey.ID) + } + less := func(a, b string) bool { return a < b } + equalIgnoreOrder := cmp.Diff(txKeyAddresses, fulfillmentTxFromAddresses, cmpopts.SortSlices(less)) == "" + require.True(t, equalIgnoreOrder) + }) +} + func TestVRFv2PlusMigration(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) @@ -622,7 +719,17 @@ func TestVRFv2PlusMigration(t *testing.T) { nativeTokenPrimaryKeyAddress, err := env.ClCluster.NodeAPIs()[0].PrimaryEthAddress() require.NoError(t, err, "error getting primary eth address") - vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment(env, vrfv2PlusConfig, linkAddress, mockETHLinkFeedAddress, nativeTokenPrimaryKeyAddress, 2, 1, l) + vrfv2PlusContracts, subIDs, vrfv2PlusData, err := vrfv2plus.SetupVRFV2_5Environment( + env, + vrfv2PlusConfig, + linkAddress, + mockETHLinkFeedAddress, + nativeTokenPrimaryKeyAddress, + 0, + 2, + 1, + l, + ) require.NoError(t, err, "error setting up VRF v2_5 env") subID := subIDs[0] @@ -671,7 +778,7 @@ func TestVRFv2PlusMigration(t *testing.T) { _, err = vrfv2plus.CreateVRFV2PlusJob( env.ClCluster.NodeAPIs()[0], newCoordinator.Address(), - vrfv2PlusData.PrimaryEthAddress, + []string{vrfv2PlusData.PrimaryEthAddress}, vrfv2PlusData.VRFKey.Data.ID, vrfv2PlusData.ChainID.String(), vrfv2PlusConfig.MinimumConfirmations, diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 47bc8e8cc1e..5934809bace 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -223,22 +223,26 @@ func WithPrivateEVMs(networks []blockchain.EVMNetwork) NodeConfigOpt { } } -func WithVRFv2EVMEstimator(addr string, maxGasPriceGWei int64) NodeConfigOpt { +func WithVRFv2EVMEstimator(addresses []string, maxGasPriceGWei int64) NodeConfigOpt { est := assets.GWei(maxGasPriceGWei) - return func(c *chainlink.Config) { - c.EVM[0].KeySpecific = evmcfg.KeySpecificConfig{ - { - Key: ptr.Ptr(ethkey.EIP55Address(addr)), - GasEstimator: evmcfg.KeySpecificGasEstimator{ - PriceMax: est, - }, + + var keySpecicifArr []evmcfg.KeySpecific + for _, addr := range addresses { + keySpecicifArr = append(keySpecicifArr, evmcfg.KeySpecific{ + Key: ptr.Ptr(ethkey.EIP55Address(addr)), + GasEstimator: evmcfg.KeySpecificGasEstimator{ + PriceMax: est, }, - } + }) + } + return func(c *chainlink.Config) { + c.EVM[0].KeySpecific = keySpecicifArr c.EVM[0].Chain.GasEstimator = evmcfg.GasEstimator{ LimitDefault: ptr.Ptr[uint32](3500000), } c.EVM[0].Chain.Transactions = evmcfg.Transactions{ MaxQueued: ptr.Ptr[uint32](10000), } + } } From 9f2e0d5356c13cb4a09c5baf22b816c7423fb695 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 27 Nov 2023 08:04:00 -0600 Subject: [PATCH 200/271] core/internal/cltest: simplify (#11383) --- core/chains/evm/client/client_test.go | 77 +++-- core/chains/evm/client/pool_test.go | 4 +- core/chains/evm/txmgr/broadcaster_test.go | 74 ++--- core/chains/evm/txmgr/confirmer_test.go | 156 ++++++----- core/chains/evm/txmgr/evm_tx_store_test.go | 126 ++++----- core/chains/evm/txmgr/reaper_test.go | 4 +- core/chains/evm/txmgr/txmgr_test.go | 265 +++++++++++++++++- core/cmd/admin_commands_test.go | 8 +- core/cmd/blocks_commands_test.go | 3 +- core/cmd/bridge_commands_test.go | 6 +- core/cmd/cosmos_keys_commands_test.go | 8 +- core/cmd/cosmos_transaction_commands_test.go | 3 +- core/cmd/csa_keys_commands_test.go | 6 +- core/cmd/dkgencrypt_keys_commands_test.go | 8 +- core/cmd/dkgsign_keys_commands_test.go | 8 +- core/cmd/eth_keys_commands_test.go | 28 +- core/cmd/evm_transaction_commands_test.go | 14 +- core/cmd/forwarders_commands_test.go | 8 +- core/cmd/jobs_commands_test.go | 12 +- core/cmd/ocr2_keys_commands_test.go | 10 +- core/cmd/ocr_keys_commands_test.go | 8 +- core/cmd/p2p_keys_commands_test.go | 8 +- core/cmd/shell_local_test.go | 10 +- core/cmd/shell_remote_test.go | 38 +-- core/cmd/shell_test.go | 49 ++++ core/cmd/solana_keys_commands_test.go | 8 +- core/cmd/solana_transaction_commands_test.go | 3 +- core/cmd/starknet_keys_commands_test.go | 8 +- core/cmd/vrf_keys_commands_test.go | 8 +- core/internal/cltest/cltest.go | 123 -------- .../internal/cltest/event_websocket_server.go | 154 ---------- core/internal/cltest/factories.go | 246 +--------------- core/internal/features/features_test.go | 24 +- .../ocrcommon/discoverer_database_test.go | 21 +- core/services/pg/event_broadcaster_test.go | 10 +- 35 files changed, 680 insertions(+), 866 deletions(-) delete mode 100644 core/internal/cltest/event_websocket_server.go diff --git a/core/chains/evm/client/client_test.go b/core/chains/evm/client/client_test.go index 673fe044afe..631b5722dec 100644 --- a/core/chains/evm/client/client_test.go +++ b/core/chains/evm/client/client_test.go @@ -89,7 +89,7 @@ func TestEthClient_TransactionReceipt(t *testing.T) { t.Run("happy path", func(t *testing.T) { result := mustReadResult(t, "../../../testdata/jsonrpc/getTransactionReceipt.json") - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -104,7 +104,7 @@ func TestEthClient_TransactionReceipt(t *testing.T) { resp.Result = string(result) } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -121,7 +121,7 @@ func TestEthClient_TransactionReceipt(t *testing.T) { t.Run("no tx hash, returns ethereum.NotFound", func(t *testing.T) { result := mustReadResult(t, "../../../testdata/jsonrpc/getTransactionReceipt_notFound.json") - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -136,7 +136,7 @@ func TestEthClient_TransactionReceipt(t *testing.T) { resp.Result = string(result) } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -155,7 +155,7 @@ func TestEthClient_PendingNonceAt(t *testing.T) { address := testutils.NewAddress() - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -174,7 +174,7 @@ func TestEthClient_PendingNonceAt(t *testing.T) { resp.Result = `"0x100"` } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -206,7 +206,7 @@ func TestEthClient_BalanceAt(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -221,7 +221,7 @@ func TestEthClient_BalanceAt(t *testing.T) { resp.Result = `"` + hexutil.EncodeBig(test.balance) + `"` } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -239,7 +239,7 @@ func TestEthClient_BalanceAt(t *testing.T) { func TestEthClient_LatestBlockHeight(t *testing.T) { t.Parallel() - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -254,7 +254,7 @@ func TestEthClient_LatestBlockHeight(t *testing.T) { } resp.Result = `"0x100"` return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -289,7 +289,7 @@ func TestEthClient_GetERC20Balance(t *testing.T) { functionSelector := evmtypes.HexToFunctionSelector(client.BALANCE_OF_ADDRESS_FUNCTION_SELECTOR) // balanceOf(address) txData := utils.ConcatBytes(functionSelector.Bytes(), common.LeftPadBytes(userAddress.Bytes(), utils.EVMWordByteLen)) - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -312,8 +312,7 @@ func TestEthClient_GetERC20Balance(t *testing.T) { resp.Result = `"` + hexutil.EncodeBig(test.balance) + `"` } return - - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -370,7 +369,7 @@ func TestEthClient_HeaderByNumber(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -392,7 +391,7 @@ func TestEthClient_HeaderByNumber(t *testing.T) { resp.Result = test.rpcResp } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -420,7 +419,7 @@ func TestEthClient_SendTransaction_NoSecondaryURL(t *testing.T) { tx := types.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -435,7 +434,7 @@ func TestEthClient_SendTransaction_NoSecondaryURL(t *testing.T) { } resp.Result = `"` + tx.Hash().Hex() + `"` return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -452,7 +451,7 @@ func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { tx := types.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -465,7 +464,7 @@ func TestEthClient_SendTransaction_WithSecondaryURLs(t *testing.T) { resp.Result = `"` + tx.Hash().Hex() + `"` } return - }) + }).WSURL().String() rpcSrv := rpc.NewServer() t.Cleanup(rpcSrv.Stop) @@ -498,7 +497,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { tx := types.NewTransaction(uint64(42), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) t.Run("returns Fatal error type when error message is fatal", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -512,7 +511,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "invalid sender" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -526,7 +525,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns TransactionAlreadyKnown error type when error message is nonce too low", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -540,7 +539,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "nonce too low" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -554,7 +553,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns Successful error type when there is no error message", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -567,7 +566,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Result = `"` + tx.Hash().Hex() + `"` } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -581,7 +580,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns Underpriced error type when transaction is terminally underpriced", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -595,7 +594,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "transaction underpriced" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -609,7 +608,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns Unsupported error type when error message is queue full", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -623,7 +622,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "queue full" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -637,7 +636,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns Retryable error type when there is a transaction gap", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -651,7 +650,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "NonceGap" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -665,7 +664,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns InsufficientFunds error type when the sender address doesn't have enough funds", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -679,7 +678,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "insufficient funds for transfer" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -693,7 +692,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns ExceedsFeeCap error type when gas price is too high for the node", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -707,7 +706,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "Transaction fee cap exceeded" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -721,7 +720,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { }) t.Run("returns Unknown error type when the error can't be categorized", func(t *testing.T) { - wsURL := cltest.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -735,7 +734,7 @@ func TestEthClient_SendTransactionReturnCode(t *testing.T) { resp.Error.Message = "some random error" } return - }) + }).WSURL().String() clients := mustNewClients(t, wsURL) for _, ethClient := range clients { @@ -770,7 +769,7 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { defer cancel() chainId := big.NewInt(123456) - wsURL := cltest.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + wsURL := testutils.NewWSServer(t, chainId, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { if method == "eth_unsubscribe" { resp.Result = "true" return @@ -781,7 +780,7 @@ func TestEthClient_SubscribeNewHead(t *testing.T) { resp.Notify = headResult } return - }) + }).WSURL().String() clients := mustNewClientsWithChainID(t, wsURL, chainId) for _, ethClient := range clients { diff --git a/core/chains/evm/client/pool_test.go b/core/chains/evm/client/pool_test.go index 15a6484756d..75d38d01a4f 100644 --- a/core/chains/evm/client/pool_test.go +++ b/core/chains/evm/client/pool_test.go @@ -211,7 +211,7 @@ type chainIDResps struct { } func (r *chainIDResps) newNode(t *testing.T, nodeChainID int64) evmclient.Node { - ws := cltest.NewWSServer(t, big.NewInt(r.ws.chainID), func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { + ws := testutils.NewWSServer(t, big.NewInt(r.ws.chainID), func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { switch method { case "eth_subscribe": resp.Result = `"0x00"` @@ -223,7 +223,7 @@ func (r *chainIDResps) newNode(t *testing.T, nodeChainID int64) evmclient.Node { } t.Errorf("Unexpected method call: %s(%s)", method, params) return - }) + }).WSURL().String() wsURL, err := url.Parse(ws) require.NoError(t, err) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index d1e26c6c969..43f2fdb8cac 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -195,7 +195,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { }) t.Run("eth_txes exist for a different from address", func(t *testing.T) { - cltest.MustCreateUnstartedTx(t, txStore, otherAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, otherAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) assert.False(t, retryable) @@ -383,7 +383,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { return tx.Nonce() == uint64(343) && tx.Value().Cmp(big.NewInt(242)) == 0 }), fromAddress).Return(commonclient.Successful, nil).Once() - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(242)), &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, []byte{42, 42, 0}, gasLimit, big.Int(assets.NewEthValue(242)), &cltest.FixtureChainID) // Do the thing { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -448,7 +448,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { return false }), "latest").Return(nil).Once() - ethTx := cltest.MustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, &cltest.FixtureChainID) + ethTx := mustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, &cltest.FixtureChainID) { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -470,9 +470,9 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { return fmt.Sprintf("%s", callarg["value"]) == "0x21e" // 542 }), "latest").Return(errors.New("this is not a revert, something unexpected went wrong")).Once() - ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, - cltest.EvmTxRequestWithChecker(checker), - cltest.EvmTxRequestWithValue(big.Int(assets.NewEthValue(542)))) + ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, + txRequestWithChecker(checker), + txRequestWithValue(big.Int(assets.NewEthValue(542)))) { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) @@ -495,9 +495,9 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { return fmt.Sprintf("%s", callarg["value"]) == "0x282" // 642 }), "latest").Return(&jerr).Once() - ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, - cltest.EvmTxRequestWithChecker(checker), - cltest.EvmTxRequestWithValue(big.Int(assets.NewEthValue(642)))) + ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, + txRequestWithChecker(checker), + txRequestWithValue(big.Int(assets.NewEthValue(642)))) { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -537,9 +537,9 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { return tx.Nonce() == 0 && tx.Value().Cmp(big.NewInt(442)) == 0 }), fromAddress).Return(commonclient.Successful, nil).Once() - ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, - cltest.EvmTxRequestWithValue(big.Int(assets.NewEthValue(442))), - cltest.EvmTxRequestWithChecker(checker)) + ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, + txRequestWithValue(big.Int(assets.NewEthValue(442))), + txRequestWithChecker(checker)) { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -560,9 +560,9 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { return tx.Nonce() == 1 && tx.Value().Cmp(big.NewInt(442)) == 0 }), fromAddress).Return(commonclient.Successful, nil).Once() - ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, - cltest.EvmTxRequestWithValue(big.Int(assets.NewEthValue(442))), - cltest.EvmTxRequestWithChecker(checker)) + ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, + txRequestWithValue(big.Int(assets.NewEthValue(442))), + txRequestWithChecker(checker)) { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -579,7 +579,7 @@ func TestEthBroadcaster_TransmitChecking(t *testing.T) { // Checker will return a fatal error checkerFactory.err = errors.New("fatal checker error") - ethTx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, cltest.EvmTxRequestWithChecker(checker)) + ethTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithChecker(checker)) { retryable, err := eb.ProcessUnstartedTxs(testutils.Context(t), fromAddress) assert.NoError(t, err) @@ -635,7 +635,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi require.NoError(t, eb.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, eb.Close()) }) - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) go func() { select { @@ -688,7 +688,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Success_WithMultiplier(t *testing FeeLimit: 1231, Strategy: txmgrcommon.NewSendEveryStrategy(), } - cltest.MustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, &cltest.FixtureChainID) + mustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, &cltest.FixtureChainID) // Do the thing { @@ -758,7 +758,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { // Crashed right after we commit the database transaction that saved // the nonce to the eth_tx so evm.key_states.next_nonce has not been // incremented yet - inProgressEthTx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) + inProgressEthTx := mustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) @@ -794,7 +794,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) // Crashed right after we commit the database transaction that saved the nonce to the eth_tx - inProgressEthTx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) + inProgressEthTx := mustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) @@ -830,7 +830,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) // Crashed right after we commit the database transaction that saved the nonce to the eth_tx - inProgressEthTx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) + inProgressEthTx := mustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) @@ -865,7 +865,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) // Crashed right after we commit the database transaction that saved the nonce to the eth_tx - inProgressEthTx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) + inProgressEthTx := mustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) @@ -902,7 +902,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) // Crashed right after we commit the database transaction that saved the nonce to the eth_tx - inProgressEthTx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) + inProgressEthTx := mustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(firstNonce) @@ -943,7 +943,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_ResumingFromCrash(t *testing.T) { eb := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg, &testCheckerFactory{}, false) // Crashed right after we commit the database transaction that saved the nonce to the eth_tx - inProgressEthTx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) + inProgressEthTx := mustInsertInProgressEthTxWithAttempt(t, txStore, firstNonce, fromAddress) require.Len(t, inProgressEthTx.TxAttempts, 1) attempt := inProgressEthTx.TxAttempts[0] @@ -1009,7 +1009,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { require.NoError(t, utils.JustError(db.Exec(`SET CONSTRAINTS pipeline_runs_pipeline_spec_id_fkey DEFERRED`))) t.Run("if external wallet sent a transaction from the account and now the nonce is one higher than it should be and we got replacement underpriced then we assume a previous transaction of ours was the one that succeeded, and hand off to EthConfirmer", func(t *testing.T) { - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) // First send, replacement underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(0) @@ -1047,7 +1047,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce := getLocalNextNonce(t, eb, fromAddress) t.Run("without callback", func(t *testing.T) { - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Fatal, errors.New(fatalErrorExample)).Once() @@ -1151,7 +1151,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Run("geth Client fails with error indicating that the transaction was too expensive", func(t *testing.T) { TxFeeExceedsCapError := "tx fee (1.10 ether) exceeds the configured cap (1.00 ether)" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.ExceedsMaxFee, errors.New(TxFeeExceedsCapError)).Twice() @@ -1209,7 +1209,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Run("eth Client call fails with an unexpected random error, and transaction was not accepted into mempool", func(t *testing.T) { retryableErrorExample := "some unknown error" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(localNextNonce) }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() @@ -1261,7 +1261,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Run("eth client call fails with an unexpected random error, and the nonce check also subsequently fails", func(t *testing.T) { retryableErrorExample := "some unknown error" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == uint64(localNextNonce) }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() @@ -1313,7 +1313,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Run("eth Client call fails with an unexpected random error, and transaction was accepted into mempool", func(t *testing.T) { retryableErrorExample := "some strange RPC returns an unexpected thing" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Unknown, errors.New(retryableErrorExample)).Once() @@ -1345,7 +1345,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // This is a configuration error by the node operator, since it means they set the base gas level too low. underpricedError := "transaction underpriced" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) // First was underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { @@ -1462,7 +1462,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { })) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() eb2 := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg2, &testCheckerFactory{}, false) - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) // First was underpriced ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { @@ -1482,7 +1482,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Run("eth tx is left in progress if eth node returns insufficient eth", func(t *testing.T) { insufficientEthError := "insufficient funds for transfer" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.InsufficientFunds, errors.New(insufficientEthError)).Once() @@ -1512,7 +1512,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Run("eth tx is left in progress if nonce is too high", func(t *testing.T) { localNextNonce := getLocalNextNonce(t, eb, fromAddress) nonceGapError := "NonceGap, Future nonce. Expected nonce: " + strconv.FormatUint(localNextNonce, 10) - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { return tx.Nonce() == localNextNonce }), fromAddress).Return(commonclient.Retryable, errors.New(nonceGapError)).Once() @@ -1554,7 +1554,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { localNextNonce := getLocalNextNonce(t, eb, fromAddress) ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(localNextNonce), nil).Once() eb2 := NewTestEthBroadcaster(t, txStore, ethClient, ethKeyStore, evmcfg2, &testCheckerFactory{}, false) - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) underpricedError := "transaction underpriced" localNextNonce = getLocalNextNonce(t, eb, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *gethTypes.Transaction) bool { @@ -1576,7 +1576,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // This is a configuration error by the node operator, since it means they set the base gas level too low. underpricedError := "transaction underpriced" localNextNonce := getLocalNextNonce(t, eb, fromAddress) - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) // Check gas tip cap verification evmcfg2 := evmtest.NewChainScopedConfig(t, configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { @@ -1650,7 +1650,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_KeystoreErrors(t *testing.T) { require.NoError(t, err) t.Run("tx signing fails", func(t *testing.T) { - etx := cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) + etx := mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, gasLimit, value, &cltest.FixtureChainID) tx := *gethTypes.NewTx(&gethTypes.LegacyTx{}) kst.On("SignTx", fromAddress, diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index 60d0648a541..f5889b06649 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -38,6 +38,7 @@ import ( "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/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -84,7 +85,7 @@ func newInProgressLegacyEthTxAttempt(t *testing.T, etxID int64, gasPrice ...int6 } func mustInsertInProgressEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress gethCommon.Address) txmgr.Tx { - etx := cltest.NewEthTx(t, fromAddress) + etx := cltest.NewEthTx(fromAddress) etx.State = txmgrcommon.TxInProgress n := evmtypes.Nonce(nonce) etx.Sequence = &n @@ -94,7 +95,7 @@ func mustInsertInProgressEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, nonce } func mustInsertConfirmedEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress gethCommon.Address) txmgr.Tx { - etx := cltest.NewEthTx(t, fromAddress) + etx := cltest.NewEthTx(fromAddress) etx.State = txmgrcommon.TxConfirmed n := evmtypes.Nonce(nonce) etx.Sequence = &n @@ -111,7 +112,7 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { db := pgtest.NewSqlxDB(t) config := newTestChainScopedConfig(t) - txStore := cltest.NewTxStore(t, db, config.Database()) + txStore := newTxStore(t, db, config.Database()) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethKeyStore := cltest.NewKeyStore(t, db, config.Database()).Eth() @@ -187,21 +188,21 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) nonce := int64(0) ctx := testutils.Context(t) blockNum := int64(0) t.Run("only finds eth_txes in unconfirmed state with at least one broadcast attempt", func(t *testing.T) { - cltest.MustInsertFatalErrorEthTx(t, txStore, fromAddress) + mustInsertFatalErrorEthTx(t, txStore, fromAddress) mustInsertInProgressEthTx(t, txStore, nonce, fromAddress) nonce++ cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, nonce, 1, fromAddress) nonce++ - cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) + mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) nonce++ - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, config.EVM().ChainID()) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, config.EVM().ChainID()) // Do the thing require.NoError(t, ec.CheckForReceipts(ctx, blockNum)) @@ -450,7 +451,7 @@ func TestEthConfirmer_CheckForReceipts(t *testing.T) { require.Len(t, attempt3_1.Receipts, 0) }) t.Run("handles case where eth_receipt already exists somehow", func(t *testing.T) { - ethReceipt := cltest.MustInsertEthReceipt(t, txStore, 42, utils.NewHash(), attempt3_1.Hash) + ethReceipt := mustInsertEthReceipt(t, txStore, 42, utils.NewHash(), attempt3_1.Hash) txmReceipt := evmtypes.Receipt{ TxHash: attempt3_1.Hash, BlockHash: ethReceipt.BlockHash, @@ -604,7 +605,7 @@ func TestEthConfirmer_CheckForReceipts_batching(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 0, fromAddress) @@ -664,7 +665,7 @@ func TestEthConfirmer_CheckForReceipts_HandlesNonFwdTxsWithForwardingEnabled(t * evmcfg := evmtest.NewChainScopedConfig(t, cfg) _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) // tx is not forwarded and doesn't have meta set. EthConfirmer should handle nil meta values etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 0, fromAddress) @@ -717,7 +718,7 @@ func TestEthConfirmer_CheckForReceipts_only_likely_confirmed(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) var attempts []txmgr.TxAttempt @@ -771,7 +772,7 @@ func TestEthConfirmer_CheckForReceipts_should_not_check_for_likely_unconfirmed(t ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) ctx := testutils.Context(t) etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 1, fromAddress) @@ -802,7 +803,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt_scoped_to_key(t ethClient.On("SequenceAt", mock.Anything, mock.Anything, mock.Anything).Return(evmtypes.Nonce(20), nil) evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) // STATE @@ -869,7 +870,7 @@ func TestEthConfirmer_CheckForReceipts_confirmed_missing_receipt(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) // STATE @@ -1129,7 +1130,7 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt(t *testing.T) { evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) // STATE @@ -1137,18 +1138,18 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt(t *testing.T) { // eth_txes with nonce 1 has two attempts, the later attempt with higher gas fees // eth_txes with nonce 2 has one attempt originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) attempt0_2 := newBroadcastLegacyEthTxAttempt(t, etx0.ID, int64(2)) require.NoError(t, txStore.InsertTxAttempt(&attempt0_2)) - etx1 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx1 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 1, 1, originalBroadcastAt, fromAddress) attempt1_2 := newBroadcastLegacyEthTxAttempt(t, etx1.ID, int64(2)) require.NoError(t, txStore.InsertTxAttempt(&attempt1_2)) - etx2 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx2 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 2, 1, originalBroadcastAt, fromAddress) attempt2_1 := etx2.TxAttempts[0] - etx3 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx3 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 3, 1, originalBroadcastAt, fromAddress) attempt3_1 := etx3.TxAttempts[0] @@ -1208,7 +1209,7 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt_batchSendTransactions_fails(t evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) // STATE @@ -1216,15 +1217,15 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt_batchSendTransactions_fails(t // eth_txes with nonce 1 has two attempts, the later attempt with higher gas fees // eth_txes with nonce 2 has one attempt originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) attempt0_2 := newBroadcastLegacyEthTxAttempt(t, etx0.ID, int64(2)) require.NoError(t, txStore.InsertTxAttempt(&attempt0_2)) - etx1 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx1 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 1, 1, originalBroadcastAt, fromAddress) attempt1_2 := newBroadcastLegacyEthTxAttempt(t, etx1.ID, int64(2)) require.NoError(t, txStore.InsertTxAttempt(&attempt1_2)) - etx2 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx2 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 2, 1, originalBroadcastAt, fromAddress) attempt2_1 := etx2.TxAttempts[0] @@ -1272,7 +1273,7 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt_smallEvmRPCBatchSize_middleBa evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) ctx := testutils.Context(t) // STATE @@ -1280,15 +1281,15 @@ func TestEthConfirmer_CheckConfirmedMissingReceipt_smallEvmRPCBatchSize_middleBa // eth_txes with nonce 1 has two attempts, the later attempt with higher gas fees // eth_txes with nonce 2 has one attempt originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) attempt0_2 := newBroadcastLegacyEthTxAttempt(t, etx0.ID, int64(2)) require.NoError(t, txStore.InsertTxAttempt(&attempt0_2)) - etx1 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx1 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 1, 1, originalBroadcastAt, fromAddress) attempt1_2 := newBroadcastLegacyEthTxAttempt(t, etx1.ID, int64(2)) require.NoError(t, txStore.InsertTxAttempt(&attempt1_2)) - etx2 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx2 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 2, 1, originalBroadcastAt, fromAddress) // Expect eth_sendRawTransaction in 3 batches. First batch will pass, 2nd will fail, 3rd never attempted. @@ -1354,7 +1355,7 @@ func TestEthConfirmer_FindTxsRequiringRebroadcast(t *testing.T) { lggr := logger.TestLogger(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) t.Run("returns nothing when there are no transactions", func(t *testing.T) { etxs, err := ec.FindTxsRequiringRebroadcast(testutils.Context(t), lggr, evmFromAddress, currentHead, gasBumpThreshold, 10, 0, &cltest.FixtureChainID) @@ -1416,7 +1417,7 @@ func TestEthConfirmer_FindTxsRequiringRebroadcast(t *testing.T) { assert.Len(t, etxs, 0) }) - etxWithoutAttempts := cltest.NewEthTx(t, fromAddress) + etxWithoutAttempts := cltest.NewEthTx(fromAddress) { n := evmtypes.Nonce(nonce) etxWithoutAttempts.Sequence = &n @@ -1577,13 +1578,13 @@ func TestEthConfirmer_FindTxsRequiringRebroadcast(t *testing.T) { attempt4_2.TxFee = gas.EvmFee{Legacy: assets.NewWeiI(40000)} require.NoError(t, txStore.InsertTxAttempt(&attempt4_2)) - etx5 := cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) + etx5 := mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) nonce++ // This etx has one attempt that is too new, which would exclude it from // the gas bumping query, but it should still be caught by the insufficient // eth query - etx6 := cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) + etx6 := mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) attempt6_2 := newBroadcastLegacyEthTxAttempt(t, etx3.ID) attempt6_2.BroadcastBeforeBlockNum = &tooNew attempt6_2.TxFee = gas.EvmFee{Legacy: assets.NewWeiI(30001)} @@ -1696,7 +1697,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing nonce := int64(0) originalBroadcastAt := time.Unix(1616509100, 0) - etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, nonce, fromAddress, originalBroadcastAt) + etx := mustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, nonce, fromAddress, originalBroadcastAt) attempt1 := etx.TxAttempts[0] var dbAttempt txmgr.DbEthTxAttempt dbAttempt.FromTxAttempt(&attempt1) @@ -1735,7 +1736,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { addresses := []gethCommon.Address{fromAddress} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addresses, nil).Maybe() // Use a mock keystore for this test - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) currentHead := int64(30) oldEnough := int64(19) nonce := int64(0) @@ -2205,7 +2206,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { c.EVM[0].GasEstimator.PriceMax = assets.NewWeiI(60500000000) }) newCfg := evmtest.NewChainScopedConfig(t, gcfg) - ec2 := cltest.NewEthConfirmer(t, txStore, ethClient, newCfg, ethKeyStore, nil) + ec2 := newEthConfirmer(t, txStore, ethClient, newCfg, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && gasPrice.Cmp(tx.GasPrice()) == 0 @@ -2235,7 +2236,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { c.EVM[0].GasEstimator.PriceMax = assets.NewWeiI(60480000000) }) newCfg := evmtest.NewChainScopedConfig(t, gcfg) - ec2 := cltest.NewEthConfirmer(t, txStore, ethClient, newCfg, ethKeyStore, nil) + ec2 := newEthConfirmer(t, txStore, ethClient, newCfg, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return evmtypes.Nonce(tx.Nonce()) == *etx3.Sequence && gasPrice.Cmp(tx.GasPrice()) == 0 @@ -2256,7 +2257,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { }) // The EIP-1559 etx and attempt - etx4 := cltest.MustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, nonce, fromAddress) + etx4 := mustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, nonce, fromAddress) attempt4_1 := etx4.TxAttempts[0] require.NoError(t, db.Get(&dbAttempt, `UPDATE evm.tx_attempts SET broadcast_before_block_num=$1, gas_tip_cap=$2, gas_fee_cap=$3 WHERE id=$4 RETURNING *`, oldEnough, assets.GWei(35), assets.GWei(100), attempt4_1.ID)) @@ -2303,7 +2304,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary(t *testing.T) { c.EVM[0].GasEstimator.PriceMax = assets.GWei(1000) }) newCfg := evmtest.NewChainScopedConfig(t, gcfg) - ec2 := cltest.NewEthConfirmer(t, txStore, ethClient, newCfg, ethKeyStore, nil) + ec2 := newEthConfirmer(t, txStore, ethClient, newCfg, ethKeyStore, nil) // Third attempt failed to bump, resubmits old one instead ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { @@ -2389,10 +2390,10 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh t.Run("terminally underpriced transaction with in_progress attempt is retried with more gas", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) originalBroadcastAt := time.Unix(1616509100, 0) - etx := cltest.MustInsertUnconfirmedEthTxWithAttemptState(t, txStore, nonce, fromAddress, txmgrtypes.TxAttemptInProgress, originalBroadcastAt) + etx := mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, nonce, fromAddress, txmgrtypes.TxAttemptInProgress, originalBroadcastAt) require.Equal(t, originalBroadcastAt, *etx.BroadcastAt) nonce++ attempt := etx.TxAttempts[0] @@ -2413,7 +2414,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh t.Run("multiple gas bumps with existing broadcast attempts are retried with more gas until success in legacy mode", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, nonce, fromAddress) nonce++ @@ -2445,9 +2446,9 @@ func TestEthConfirmer_RebroadcastWhereNecessary_TerminallyUnderpriced_ThenGoesTh t.Run("multiple gas bumps with existing broadcast attempts are retried with more gas until success in EIP-1559 mode", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, kst, nil) - etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, nonce, fromAddress) + etx := mustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, nonce, fromAddress) nonce++ dxFeeAttempt := etx.TxAttempts[0] var dbAttempt txmgr.DbEthTxAttempt @@ -2510,7 +2511,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { insufficientEthError := errors.New("insufficient funds for gas * price + value") t.Run("saves attempt with state 'insufficient_eth' if eth node returns this error", func(t *testing.T) { - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) expectedBumpedGasPrice := big.NewInt(20000000000) require.Greater(t, expectedBumpedGasPrice.Int64(), attempt1_1.TxFee.Legacy.ToInt().Int64()) @@ -2536,7 +2537,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { }) t.Run("does not bump gas when previous error was 'out of eth', instead resubmits existing transaction", func(t *testing.T) { - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) expectedBumpedGasPrice := big.NewInt(20000000000) require.Greater(t, expectedBumpedGasPrice.Int64(), attempt1_1.TxFee.Legacy.ToInt().Int64()) @@ -2561,7 +2562,7 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { }) t.Run("saves the attempt as broadcast after node wallet has been topped up with sufficient balance", func(t *testing.T) { - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) expectedBumpedGasPrice := big.NewInt(20000000000) require.Greater(t, expectedBumpedGasPrice.Int64(), attempt1_1.TxFee.Legacy.ToInt().Int64()) @@ -2593,11 +2594,11 @@ func TestEthConfirmer_RebroadcastWhereNecessary_WhenOutOfEth(t *testing.T) { c.EVM[0].GasEstimator.BumpTxDepth = ptr(uint32(depth)) }) evmcfg := evmtest.NewChainScopedConfig(t, cfg) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) for i := 0; i < etxCount; i++ { n := nonce - cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) + mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, nonce, fromAddress) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(n) }), fromAddress).Return(commonclient.Successful, nil).Once() @@ -2628,7 +2629,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) config := newTestChainScopedConfig(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) head := evmtypes.Head{ Hash: utils.NewHash(), @@ -2661,7 +2662,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { t.Run("does nothing to confirmed transactions with receipts within head height of the chain and included in the chain", func(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 2, 1, fromAddress) - cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, etx.TxAttempts[0].Hash) + mustInsertEthReceipt(t, txStore, head.Number, head.Hash, etx.TxAttempts[0].Hash) // Do the thing require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(testutils.Context(t), &head)) @@ -2674,7 +2675,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { t.Run("does nothing to confirmed transactions that only have receipts older than the start of the chain", func(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 3, 1, fromAddress) // Add receipt that is older than the lowest block of the chain - cltest.MustInsertEthReceipt(t, txStore, head.Parent.Parent.Number-1, utils.NewHash(), etx.TxAttempts[0].Hash) + mustInsertEthReceipt(t, txStore, head.Parent.Parent.Number-1, utils.NewHash(), etx.TxAttempts[0].Hash) // Do the thing require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(testutils.Context(t), &head)) @@ -2688,7 +2689,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 4, 1, fromAddress) attempt := etx.TxAttempts[0] // Include one within head height but a different block hash - cltest.MustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attempt.Hash) + mustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attempt.Hash) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { atx, err := txmgr.GetGethSignedTx(attempt.SignedRawTx) @@ -2713,9 +2714,9 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { attempt := etx.TxAttempts[0] attemptHash := attempt.Hash // Add receipt that is older than the lowest block of the chain - cltest.MustInsertEthReceipt(t, txStore, head.Parent.Parent.Number-1, utils.NewHash(), attemptHash) + mustInsertEthReceipt(t, txStore, head.Parent.Parent.Number-1, utils.NewHash(), attemptHash) // Include one within head height but a different block hash - cltest.MustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attemptHash) + mustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attemptHash) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.Anything, fromAddress).Return( commonclient.Successful, nil).Once() @@ -2745,9 +2746,9 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { require.NoError(t, txStore.InsertTxAttempt(&attempt3)) // Receipt is within head height but a different block hash - cltest.MustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attempt2.Hash) + mustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attempt2.Hash) // Receipt is within head height but a different block hash - cltest.MustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attempt3.Hash) + mustInsertEthReceipt(t, txStore, head.Parent.Number, utils.NewHash(), attempt3.Hash) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { s, err := txmgr.GetGethSignedTx(attempt3.SignedRawTx) @@ -2774,7 +2775,7 @@ func TestEthConfirmer_EnsureConfirmedTransactionsInLongestChain(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 7, 1, fromAddress) attempt := etx.TxAttempts[0] // Add receipt that is higher than head - cltest.MustInsertEthReceipt(t, txStore, head.Number+1, utils.NewHash(), attempt.Hash) + mustInsertEthReceipt(t, txStore, head.Number+1, utils.NewHash(), attempt.Hash) require.NoError(t, ec.EnsureConfirmedTransactionsInLongestChain(testutils.Context(t), &head)) @@ -2799,7 +2800,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) config := newTestChainScopedConfig(t) - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, config.EVM().ChainID()) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, config.EVM().ChainID()) mustInsertInProgressEthTx(t, txStore, 0, fromAddress) etx1 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress) etx2 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) @@ -2809,7 +2810,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { t.Run("rebroadcasts one eth_tx if it falls within in nonce range", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx1.Sequence) && @@ -2824,7 +2825,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { t.Run("uses default gas limit if overrideGasLimit is 0", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx1.Sequence) && @@ -2839,7 +2840,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { t.Run("rebroadcasts several eth_txes in nonce range", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(*etx1.Sequence) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && tx.Gas() == uint64(overrideGasLimit) @@ -2853,7 +2854,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { t.Run("broadcasts zero transactions if eth_tx doesn't exist for that nonce", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(1) @@ -2879,7 +2880,7 @@ func TestEthConfirmer_ForceRebroadcast(t *testing.T) { t.Run("zero transactions use default gas limit if override wasn't specified", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) + ec := newEthConfirmer(t, txStore, ethClient, config, ethKeyStore, nil) ethClient.On("SendTransactionReturnCode", mock.Anything, mock.MatchedBy(func(tx *types.Transaction) bool { return tx.Nonce() == uint64(0) && tx.GasPrice().Int64() == gasPriceWei.Legacy.Int64() && uint32(tx.Gas()) == config.EVM().GasEstimator().LimitDefault() @@ -2923,7 +2924,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { pgtest.MustExec(t, db, `SET CONSTRAINTS pipeline_runs_pipeline_spec_id_fkey DEFERRED`) t.Run("doesn't process task runs that are not suspended (possibly already previously resumed)", func(t *testing.T) { - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { t.Fatal("No value expected") return nil }) @@ -2932,7 +2933,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 1, 1, fromAddress) - cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) + mustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) // Setting both signal_callback and callback_completed to TRUE to simulate a completed pipeline task // It would only be in a state past suspended if the resume callback was called and callback_completed was set to TRUE pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE, callback_completed = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) @@ -2942,7 +2943,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { }) t.Run("doesn't process task runs where the receipt is younger than minConfirmations", func(t *testing.T) { - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { t.Fatal("No value expected") return nil }) @@ -2951,7 +2952,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 2, 1, fromAddress) - cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, etx.TxAttempts[0].Hash) + mustInsertEthReceipt(t, txStore, head.Number, head.Hash, etx.TxAttempts[0].Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) @@ -2963,7 +2964,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { ch := make(chan interface{}) nonce := evmtypes.Nonce(3) var err error - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { err = thisErr ch <- value return nil @@ -2975,7 +2976,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, int64(nonce), 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": true}'`) - receipt := cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) + receipt := mustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) @@ -3007,7 +3008,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { ch := make(chan interface{}) nonce := evmtypes.Nonce(4) var err error - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(id uuid.UUID, value interface{}, thisErr error) error { err = thisErr ch <- value return nil @@ -3021,7 +3022,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": true}'`) // receipt is not passed through as a value since it reverted and caused an error - cltest.MustInsertRevertedEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) + mustInsertRevertedEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) @@ -3049,7 +3050,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { t.Run("does not mark callback complete if callback fails", func(t *testing.T) { nonce := evmtypes.Nonce(5) - ec := cltest.NewEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { + ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, func(uuid.UUID, interface{}, error) error { return errors.New("error") }) @@ -3057,7 +3058,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { tr := cltest.MustInsertUnfinishedPipelineTaskRun(t, db, run.ID) etx := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, int64(nonce), 1, fromAddress) - cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) + mustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, etx.TxAttempts[0].Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr.ID, minConfirmations, etx.ID) err := ec.ResumePendingTaskRuns(testutils.Context(t), &head) @@ -3071,3 +3072,14 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { } func ptr[T any](t T) *T { return &t } + +func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Client, config evmconfig.ChainScopedConfig, ks keystore.Eth, fn txmgrcommon.ResumeCallback) *txmgr.Confirmer { + lggr := logger.TestLogger(t) + ge := config.EVM().GasEstimator() + estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(ge, ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) + txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) + ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), config.Database(), ks, txBuilder, lggr) + ec.SetResumeCallback(fn) + require.NoError(t, ec.Start(testutils.Context(t))) + return ec +} diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 73bfc6fc85a..15417a43096 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -52,7 +52,7 @@ func TestORM_TransactionsWithAttempts(t *testing.T) { require.NoError(t, txStore.InsertTxAttempt(&attempt)) // tx 3 has no attempts - cltest.MustCreateUnstartedGeneratedTx(t, txStore, from, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, from, &cltest.FixtureChainID) var count int err := db.Get(&count, `SELECT count(*) FROM evm.txes`) @@ -97,7 +97,7 @@ func TestORM_Transactions(t *testing.T) { require.NoError(t, txStore.InsertTxAttempt(&attempt)) // tx 3 has no attempts - cltest.MustCreateUnstartedGeneratedTx(t, txStore, from, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, from, &cltest.FixtureChainID) var count int err := db.Get(&count, `SELECT count(*) FROM evm.txes`) @@ -125,7 +125,7 @@ func TestORM(t *testing.T) { var etx txmgr.Tx t.Run("InsertTx", func(t *testing.T) { - etx = cltest.NewEthTx(t, fromAddress) + etx = cltest.NewEthTx(fromAddress) require.NoError(t, orm.InsertTx(&etx)) assert.Greater(t, int(etx.ID), 0) cltest.AssertCount(t, db, "evm.txes", 1) @@ -147,7 +147,7 @@ func TestORM(t *testing.T) { }) var r txmgr.Receipt t.Run("InsertReceipt", func(t *testing.T) { - r = cltest.NewEthReceipt(t, 42, utils.NewHash(), attemptD.Hash, 0x1) + r = newEthReceipt(42, utils.NewHash(), attemptD.Hash, 0x1) id, err := orm.InsertReceipt(&r.Receipt) r.ID = id require.NoError(t, err) @@ -203,12 +203,12 @@ func TestORM_FindTxAttemptConfirmedByTxIDs(t *testing.T) { require.NoError(t, orm.InsertTxAttempt(&attempt)) // add receipt for the second attempt - r := cltest.NewEthReceipt(t, 4, utils.NewHash(), attempt.Hash, 0x1) + r := newEthReceipt(4, utils.NewHash(), attempt.Hash, 0x1) _, err := orm.InsertReceipt(&r.Receipt) require.NoError(t, err) // tx 3 has no attempts - cltest.MustCreateUnstartedGeneratedTx(t, orm, from, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, orm, from, &cltest.FixtureChainID) cltest.MustInsertUnconfirmedEthTx(t, orm, 3, from) // tx4 cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, orm, 4, from) // tx5 @@ -252,7 +252,7 @@ func TestORM_FindTxAttemptsRequiringResend(t *testing.T) { // Mix up the insert order to assure that they come out sorted by nonce not implicitly or by ID e1 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress, time.Unix(1616509200, 0)) - e3 := cltest.MustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, 3, fromAddress, time.Unix(1616509400, 0)) + e3 := mustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t, txStore, 3, fromAddress, time.Unix(1616509400, 0)) e0 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress, time.Unix(1616509100, 0)) e2 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress, time.Unix(1616509300, 0)) @@ -331,7 +331,7 @@ func TestORM_UpdateBroadcastAts(t *testing.T) { t.Run("does not update when broadcast_at is NULL", func(t *testing.T) { t.Parallel() - etx := cltest.MustCreateUnstartedGeneratedTx(t, orm, fromAddress, &cltest.FixtureChainID) + etx := mustCreateUnstartedGeneratedTx(t, orm, fromAddress, &cltest.FixtureChainID) var nullTime *time.Time assert.Equal(t, nullTime, etx.BroadcastAt) @@ -349,7 +349,7 @@ func TestORM_UpdateBroadcastAts(t *testing.T) { t.Parallel() time1 := time.Now() - etx := cltest.NewEthTx(t, fromAddress) + etx := cltest.NewEthTx(fromAddress) etx.Sequence = new(evmtypes.Nonce) etx.State = txmgrcommon.TxUnconfirmed etx.BroadcastAt = &time1 @@ -446,7 +446,7 @@ func TestORM_FindTxAttemptsConfirmedMissingReceipt(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) attempts, err := txStore.FindTxAttemptsConfirmedMissingReceipt(testutils.Context(t), ethClient.ConfiguredChainID()) @@ -468,7 +468,7 @@ func TestORM_UpdateTxsUnconfirmed(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) assert.Equal(t, etx0.State, txmgrcommon.TxConfirmedMissingReceipt) require.NoError(t, txStore.UpdateTxsUnconfirmed(testutils.Context(t), []int64{etx0.ID})) @@ -489,7 +489,7 @@ func TestORM_FindTxAttemptsRequiringReceiptFetch(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) attempts, err := txStore.FindTxAttemptsRequiringReceiptFetch(testutils.Context(t), ethClient.ConfiguredChainID()) @@ -510,7 +510,7 @@ func TestORM_SaveFetchedReceipts(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) originalBroadcastAt := time.Unix(1616509100, 0) - etx0 := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + etx0 := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( t, txStore, 0, 1, originalBroadcastAt, fromAddress) require.Len(t, etx0.TxAttempts, 1) @@ -552,7 +552,7 @@ func TestORM_MarkAllConfirmedMissingReceipt(t *testing.T) { assert.Equal(t, txmgrcommon.TxUnconfirmed, etx0.State) // create transaction 1 (nonce 1) that is confirmed (block 77) - etx1 := cltest.MustInsertConfirmedEthTxBySaveFetchedReceipts(t, txStore, fromAddress, int64(1), int64(77), *ethClient.ConfiguredChainID()) + etx1 := mustInsertConfirmedEthTxBySaveFetchedReceipts(t, txStore, fromAddress, int64(1), int64(77), *ethClient.ConfiguredChainID()) assert.Equal(t, etx1.State, txmgrcommon.TxConfirmed) // mark transaction 0 confirmed_missing_receipt @@ -608,7 +608,7 @@ func TestORM_GetInProgressTxAttempts(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) // insert etx with attempt - etx := cltest.MustInsertUnconfirmedEthTxWithAttemptState(t, txStore, int64(7), fromAddress, txmgrtypes.TxAttemptInProgress) + etx := mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, int64(7), fromAddress, txmgrtypes.TxAttemptInProgress) // fetch attempt attempts, err := txStore.GetInProgressTxAttempts(testutils.Context(t), fromAddress, ethClient.ConfiguredChainID()) @@ -653,7 +653,7 @@ func TestORM_FindTxesPendingCallback(t *testing.T) { etx1 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 3, 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": true}'`) attempt1 := etx1.TxAttempts[0] - cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt1.Hash) + mustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt1.Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr1.ID, minConfirmations, etx1.ID) // Callback to pipeline service completed. Should be ignored @@ -662,7 +662,7 @@ func TestORM_FindTxesPendingCallback(t *testing.T) { etx2 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 4, 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": false}'`) attempt2 := etx2.TxAttempts[0] - cltest.MustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt2.Hash) + mustInsertEthReceipt(t, txStore, head.Number-minConfirmations, head.Hash, attempt2.Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE, callback_completed = TRUE WHERE id = $3`, &tr2.ID, minConfirmations, etx2.ID) // Suspended run younger than minConfirmations. Should be ignored @@ -672,13 +672,13 @@ func TestORM_FindTxesPendingCallback(t *testing.T) { etx3 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 5, 1, fromAddress) pgtest.MustExec(t, db, `UPDATE evm.txes SET meta='{"FailOnRevert": false}'`) attempt3 := etx3.TxAttempts[0] - cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt3.Hash) + mustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt3.Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET pipeline_task_run_id = $1, min_confirmations = $2, signal_callback = TRUE WHERE id = $3`, &tr3.ID, minConfirmations, etx3.ID) // Tx not marked for callback. Should be ignore etx4 := cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 6, 1, fromAddress) attempt4 := etx4.TxAttempts[0] - cltest.MustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt4.Hash) + mustInsertEthReceipt(t, txStore, head.Number, head.Hash, attempt4.Hash) pgtest.MustExec(t, db, `UPDATE evm.txes SET min_confirmations = $1 WHERE id = $2`, minConfirmations, etx4.ID) // Unconfirmed Tx without receipts. Should be ignored @@ -710,8 +710,8 @@ func Test_FindTxWithIdempotencyKey(t *testing.T) { t.Run("returns transaction if it exists", func(t *testing.T) { idempotencyKey := "777" cfg.EVM().ChainID() - etx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, big.NewInt(0), - cltest.EvmTxRequestWithIdempotencyKey(idempotencyKey)) + etx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, big.NewInt(0), + txRequestWithIdempotencyKey(idempotencyKey)) require.Equal(t, idempotencyKey, *etx.IdempotencyKey) res, err := txStore.FindTxWithIdempotencyKey(testutils.Context(t), idempotencyKey, big.NewInt(0)) @@ -756,7 +756,7 @@ func TestORM_UpdateTxForRebroadcast(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("delete all receipts for eth transaction", func(t *testing.T) { - etx := cltest.MustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 777, 1) + etx := mustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 777, 1) etx, err := txStore.FindTxWithAttempts(etx.ID) assert.NoError(t, err) // assert attempt state @@ -811,8 +811,8 @@ func TestORM_FindTransactionsConfirmedInBlockRange(t *testing.T) { } t.Run("find all transactions confirmed in range", func(t *testing.T) { - etx_8 := cltest.MustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 700, 8) - etx_9 := cltest.MustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 777, 9) + etx_8 := mustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 700, 8) + etx_9 := mustInsertConfirmedEthTxWithReceipt(t, txStore, fromAddress, 777, 9) etxes, err := txStore.FindTransactionsConfirmedInBlockRange(testutils.Context(t), head.Number, 8, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -834,7 +834,7 @@ func TestORM_SaveInsufficientEthAttempt(t *testing.T) { require.NoError(t, err) t.Run("updates attempt state", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress) now := time.Now() err = txStore.SaveInsufficientFundsAttempt(testutils.Context(t), defaultDuration, &etx.TxAttempts[0], now) @@ -858,7 +858,7 @@ func TestORM_SaveSentAttempt(t *testing.T) { require.NoError(t, err) t.Run("updates attempt state to 'broadcast'", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress) require.Nil(t, etx.BroadcastAt) now := time.Now() @@ -883,7 +883,7 @@ func TestORM_SaveConfirmedMissingReceiptAttempt(t *testing.T) { require.NoError(t, err) t.Run("updates attempt to 'broadcast' and transaction to 'confirm_missing_receipt'", func(t *testing.T) { - etx := cltest.MustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 1, fromAddress, txmgrtypes.TxAttemptInProgress) + etx := mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 1, fromAddress, txmgrtypes.TxAttemptInProgress) now := time.Now() err = txStore.SaveConfirmedMissingReceiptAttempt(testutils.Context(t), defaultDuration, &etx.TxAttempts[0], now) @@ -906,7 +906,7 @@ func TestORM_DeleteInProgressAttempt(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("deletes in_progress attempt", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 1, fromAddress) attempt := etx.TxAttempts[0] err := txStore.DeleteInProgressAttempt(testutils.Context(t), etx.TxAttempts[0]) @@ -942,7 +942,7 @@ func TestORM_SaveInProgressAttempt(t *testing.T) { }) t.Run("updates old attempt to in_progress when insufficient_eth", func(t *testing.T) { - etx := cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 23, fromAddress) + etx := mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 23, fromAddress) attempt := etx.TxAttempts[0] require.Equal(t, txmgrtypes.TxAttemptInsufficientFunds, attempt.State) require.NotEqual(t, 0, attempt.ID) @@ -972,7 +972,7 @@ func TestORM_FindTxsRequiringGasBump(t *testing.T) { currentBlockNum := int64(10) t.Run("gets txs requiring gas bump", func(t *testing.T) { - etx := cltest.MustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 1, fromAddress, txmgrtypes.TxAttemptBroadcast) + etx := mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 1, fromAddress, txmgrtypes.TxAttemptBroadcast) err := txStore.SetBroadcastBeforeBlockNum(testutils.Context(t), currentBlockNum, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -985,7 +985,7 @@ func TestORM_FindTxsRequiringGasBump(t *testing.T) { assert.Equal(t, currentBlockNum, *attempts[0].BroadcastBeforeBlockNum) // this tx will not require gas bump - cltest.MustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 2, fromAddress, txmgrtypes.TxAttemptBroadcast) + mustInsertUnconfirmedEthTxWithAttemptState(t, txStore, 2, fromAddress, txmgrtypes.TxAttemptBroadcast) err = txStore.SetBroadcastBeforeBlockNum(testutils.Context(t), currentBlockNum+1, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -1012,18 +1012,18 @@ func TestEthConfirmer_FindTxsRequiringResubmissionDueToInsufficientEth(t *testin _, otherAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) // Insert order is mixed up to test sorting - etx2 := cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 1, fromAddress) + etx2 := mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 1, fromAddress) etx3 := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) attempt3_2 := cltest.NewLegacyEthTxAttempt(t, etx3.ID) attempt3_2.State = txmgrtypes.TxAttemptInsufficientFunds attempt3_2.TxFee.Legacy = assets.NewWeiI(100) require.NoError(t, txStore.InsertTxAttempt(&attempt3_2)) - etx1 := cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, fromAddress) + etx1 := mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, fromAddress) // These should never be returned cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 3, fromAddress) cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, 4, 100, fromAddress) - cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, otherAddress) + mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, otherAddress) t.Run("returns all eth_txes with at least one attempt that is in insufficient_eth state", func(t *testing.T) { etxs, err := txStore.FindTxsRequiringResubmissionDueToInsufficientFunds(testutils.Context(t), fromAddress, &cltest.FixtureChainID) @@ -1073,7 +1073,7 @@ func TestORM_MarkOldTxesMissingReceiptAsErrored(t *testing.T) { // tx state should be confirmed missing receipt // attempt should be broadcast before cutoff time t.Run("successfully mark errored transactions", func(t *testing.T) { - etx := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) + etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) err := txStore.MarkOldTxesMissingReceiptAsErrored(testutils.Context(t), 10, 2, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -1084,7 +1084,7 @@ func TestORM_MarkOldTxesMissingReceiptAsErrored(t *testing.T) { }) t.Run("successfully mark errored transactions w/ qopt passing in sql.Tx", func(t *testing.T) { - etx := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) + etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) err := txStore.MarkOldTxesMissingReceiptAsErrored(testutils.Context(t), 10, 2, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -1105,7 +1105,7 @@ func TestORM_LoadEthTxesAttempts(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("load eth tx attempt", func(t *testing.T) { - etx := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) + etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 1, 7, time.Now(), fromAddress) etx.TxAttempts = []txmgr.TxAttempt{} err := txStore.LoadTxesAttempts([]*txmgr.Tx{&etx}) @@ -1114,7 +1114,7 @@ func TestORM_LoadEthTxesAttempts(t *testing.T) { }) t.Run("load new attempt inserted in current postgres transaction", func(t *testing.T) { - etx := cltest.MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 3, 9, time.Now(), fromAddress) + etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 3, 9, time.Now(), fromAddress) etx.TxAttempts = []txmgr.TxAttempt{} q := pg.NewQ(db, logger.TestLogger(t), cfg.Database()) @@ -1154,7 +1154,7 @@ func TestORM_SaveReplacementInProgressAttempt(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("replace eth tx attempt", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) oldAttempt := etx.TxAttempts[0] newAttempt := cltest.NewDynamicFeeEthTxAttempt(t, etx.ID) @@ -1180,7 +1180,7 @@ func TestORM_FindNextUnstartedTransactionFromAddress(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("cannot find unstarted tx", func(t *testing.T) { - cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) + mustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) resultEtx := new(txmgr.Tx) err := txStore.FindNextUnstartedTransactionFromAddress(testutils.Context(t), resultEtx, fromAddress, ethClient.ConfiguredChainID()) @@ -1188,7 +1188,7 @@ func TestORM_FindNextUnstartedTransactionFromAddress(t *testing.T) { }) t.Run("finds unstarted tx", func(t *testing.T) { - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) resultEtx := new(txmgr.Tx) err := txStore.FindNextUnstartedTransactionFromAddress(testutils.Context(t), resultEtx, fromAddress, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -1206,7 +1206,7 @@ func TestORM_UpdateTxFatalError(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("update successful", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) etxPretendError := null.StringFrom("no more toilet paper") etx.Error = etxPretendError @@ -1229,7 +1229,7 @@ func TestORM_UpdateTxAttemptInProgressToBroadcast(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) t.Run("update successful", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 13, fromAddress) attempt := etx.TxAttempts[0] require.Equal(t, txmgrtypes.TxAttemptInProgress, attempt.State) @@ -1262,7 +1262,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { nonce := evmtypes.Nonce(123) t.Run("update successful", func(t *testing.T) { - etx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + etx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) etx.Sequence = &nonce attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) @@ -1276,7 +1276,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { }) t.Run("update fails because tx is removed", func(t *testing.T) { - etx := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + etx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) etx.Sequence = &nonce attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) @@ -1296,7 +1296,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { q = pg.NewQ(db, logger.TestLogger(t), cfg.Database()) t.Run("update replaces abandoned tx with same hash", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, nonce, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, nonce, fromAddress) require.Len(t, etx.TxAttempts, 1) zero := models.MustNewDuration(time.Duration(0)) @@ -1314,7 +1314,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { err := txMgr.XXXTestAbandon(fromAddress) // mark transaction as abandoned require.NoError(t, err) - etx2 := cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + etx2 := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) etx2.Sequence = &nonce attempt2 := cltest.NewLegacyEthTxAttempt(t, etx2.ID) attempt2.Hash = etx.TxAttempts[0].Hash @@ -1329,7 +1329,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { // Same flow as previous test, but without calling txMgr.Abandon() t.Run("duplicate tx hash disallowed in tx_eth_attempts", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, nonce, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, nonce, fromAddress) require.Len(t, etx.TxAttempts, 1) etx.State = txmgrcommon.TxUnstarted @@ -1357,7 +1357,7 @@ func TestORM_GetTxInProgress(t *testing.T) { }) t.Run("get 1 in progress eth transaction", func(t *testing.T) { - etx := cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) + etx := mustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) etxResult, err := txStore.GetTxInProgress(testutils.Context(t), fromAddress) require.NoError(t, err) @@ -1382,7 +1382,7 @@ func TestORM_HasInProgressTransaction(t *testing.T) { }) t.Run("has in progress eth transaction", func(t *testing.T) { - cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) + mustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) exists, err := txStore.HasInProgressTransaction(testutils.Context(t), fromAddress, ethClient.ConfiguredChainID()) require.NoError(t, err) @@ -1422,9 +1422,9 @@ func TestORM_CountUnstartedTransactions(t *testing.T) { _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) _, otherAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) - cltest.MustCreateUnstartedGeneratedTx(t, txStore, otherAddress, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID) + mustCreateUnstartedGeneratedTx(t, txStore, otherAddress, &cltest.FixtureChainID) cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress) count, err := txStore.CountUnstartedTransactions(testutils.Context(t), fromAddress, &cltest.FixtureChainID) @@ -1456,7 +1456,7 @@ func TestORM_CheckTxQueueCapacity(t *testing.T) { // deliberately one extra to exceed limit for i := 0; i <= int(maxUnconfirmedTransactions); i++ { - cltest.MustCreateUnstartedTx(t, txStore, otherAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, otherAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) } t.Run("with eth_txes from another address returns nil", func(t *testing.T) { @@ -1465,7 +1465,7 @@ func TestORM_CheckTxQueueCapacity(t *testing.T) { }) for i := 0; i <= int(maxUnconfirmedTransactions); i++ { - cltest.MustInsertFatalErrorEthTx(t, txStore, otherAddress) + mustInsertFatalErrorEthTx(t, txStore, otherAddress) } t.Run("ignores fatally_errored transactions", func(t *testing.T) { @@ -1474,7 +1474,7 @@ func TestORM_CheckTxQueueCapacity(t *testing.T) { }) var n int64 - cltest.MustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(n), fromAddress) + mustInsertInProgressEthTxWithAttempt(t, txStore, evmtypes.Nonce(n), fromAddress) n++ cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, n, fromAddress) n++ @@ -1496,7 +1496,7 @@ func TestORM_CheckTxQueueCapacity(t *testing.T) { }) for i := 0; i < int(maxUnconfirmedTransactions)-1; i++ { - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) } t.Run("with fewer unstarted eth_txes than limit returns nil", func(t *testing.T) { @@ -1504,14 +1504,14 @@ func TestORM_CheckTxQueueCapacity(t *testing.T) { require.NoError(t, err) }) - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) t.Run("with equal or more unstarted eth_txes than limit returns error", func(t *testing.T) { err := txStore.CheckTxQueueCapacity(testutils.Context(t), fromAddress, maxUnconfirmedTransactions, &cltest.FixtureChainID) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("cannot create transaction; too many unstarted transactions in the queue (2/%d). WARNING: Hitting EVM.Transactions.MaxQueued", maxUnconfirmedTransactions)) - cltest.MustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) + mustCreateUnstartedTx(t, txStore, fromAddress, toAddress, encodedPayload, feeLimit, value, &cltest.FixtureChainID) err = txStore.CheckTxQueueCapacity(testutils.Context(t), fromAddress, maxUnconfirmedTransactions, &cltest.FixtureChainID) require.Error(t, err) require.Contains(t, err.Error(), fmt.Sprintf("cannot create transaction; too many unstarted transactions in the queue (3/%d). WARNING: Hitting EVM.Transactions.MaxQueued", maxUnconfirmedTransactions)) @@ -1533,7 +1533,7 @@ func TestORM_CreateTransaction(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - txStore := cltest.NewTxStore(t, db, cfg.Database()) + txStore := newTxStore(t, db, cfg.Database()) kst := cltest.NewKeyStore(t, db, cfg.Database()) _, fromAddress := cltest.MustInsertRandomKey(t, kst.Eth()) @@ -1635,7 +1635,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := newTestChainScopedConfig(t) - txStore := cltest.NewTxStore(t, db, cfg.Database()) + txStore := newTxStore(t, db, cfg.Database()) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() evmtest.NewEthClientMockWithDefaultChain(t) _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) @@ -1644,7 +1644,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { subject1 := uuid.New() strategy1 := txmgrcommon.NewDropOldestStrategy(subject1, uint32(5), cfg.Database().DefaultQueryTimeout()) for i := 0; i < 5; i++ { - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, cltest.EvmTxRequestWithStrategy(strategy1)) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithStrategy(strategy1)) } testutils.AssertCountPerSubject(t, db, int64(5), subject1) }) @@ -1653,7 +1653,7 @@ func TestORM_PruneUnstartedTxQueue(t *testing.T) { subject2 := uuid.New() strategy2 := txmgrcommon.NewDropOldestStrategy(subject2, uint32(3), cfg.Database().DefaultQueryTimeout()) for i := 0; i < 5; i++ { - cltest.MustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, cltest.EvmTxRequestWithStrategy(strategy2)) + mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, &cltest.FixtureChainID, txRequestWithStrategy(strategy2)) } testutils.AssertCountPerSubject(t, db, int64(3), subject2) }) diff --git a/core/chains/evm/txmgr/reaper_test.go b/core/chains/evm/txmgr/reaper_test.go index 20cc27a675f..67216c9fd15 100644 --- a/core/chains/evm/txmgr/reaper_test.go +++ b/core/chains/evm/txmgr/reaper_test.go @@ -64,7 +64,7 @@ func TestReaper_ReapTxes(t *testing.T) { }) // Confirmed in block number 5 - cltest.MustInsertConfirmedEthTxWithReceipt(t, txStore, from, nonce, 5) + mustInsertConfirmedEthTxWithReceipt(t, txStore, from, nonce, 5) t.Run("skips if threshold=0", func(t *testing.T) { config := txmgrmocks.NewReaperConfig(t) @@ -119,7 +119,7 @@ func TestReaper_ReapTxes(t *testing.T) { cltest.AssertCount(t, db, "evm.txes", 0) }) - cltest.MustInsertFatalErrorEthTx(t, txStore, from) + mustInsertFatalErrorEthTx(t, txStore, from) t.Run("deletes errored evm.txes that exceed the age threshold", func(t *testing.T) { config := txmgrmocks.NewReaperConfig(t) diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index e27cea137b5..bab18f445bf 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -1,6 +1,7 @@ package txmgr_test import ( + "bytes" "context" "encoding/json" "fmt" @@ -8,16 +9,19 @@ import ( "testing" "time" - gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "gopkg.in/guregu/null.v4" "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -26,6 +30,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -34,6 +39,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -145,7 +151,7 @@ func TestTxm_CreateTransaction(t *testing.T) { assert.Equal(t, subject, etx.Subject.UUID) }) - cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, fromAddress) + mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, fromAddress) t.Run("with queue at capacity does not insert eth_tx", func(t *testing.T) { evmConfig.MaxQueued = uint64(1) @@ -246,8 +252,8 @@ func TestTxm_CreateTransaction(t *testing.T) { // max uint256 is 1.1579209e+77 testDefaultGlobalSubID := crypto.Keccak256Hash([]byte("sub id")).String() jobID := int32(25) - requestID := gethcommon.HexToHash("abcd") - requestTxHash := gethcommon.HexToHash("dcba") + requestID := common.HexToHash("abcd") + requestTxHash := common.HexToHash("dcba") meta := &txmgr.TxMeta{ JobID: &jobID, RequestID: &requestID, @@ -393,7 +399,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { payload := cltest.MustRandomBytes(t, 100) evmConfig.MaxQueued = uint64(1) - cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, otherKey.Address) + mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, otherKey.Address) strategy := newMockTxStrategy(t) strategy.On("Subject").Return(uuid.NullUUID{}) strategy.On("PruneQueue", mock.Anything, mock.Anything).Return(int64(0), nil) @@ -417,7 +423,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { payload := cltest.MustRandomBytes(t, 100) evmConfig.MaxQueued = uint64(1) - cltest.MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, thisKey.Address) + mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t, txStore, 0, thisKey.Address) strategy := newMockTxStrategy(t) strategy.On("Subject").Return(uuid.NullUUID{}) strategy.On("PruneQueue", mock.Anything, mock.Anything).Return(int64(0), nil) @@ -471,7 +477,7 @@ func TestTxm_Lifecycle(t *testing.T) { evmConfig.ReaperThreshold = 1 * time.Hour evmConfig.ReaperInterval = 1 * time.Hour - kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return([]gethcommon.Address{}, nil) + kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return([]common.Address{}, nil) keyChangeCh := make(chan struct{}) unsub := cltest.NewAwaiter() @@ -495,9 +501,9 @@ func TestTxm_Lifecycle(t *testing.T) { keyState := cltest.MustGenerateRandomKeyState(t) - addr := []gethcommon.Address{keyState.Address.Address()} + addr := []common.Address{keyState.Address.Address()} kst.On("EnabledAddressesForChain", &cltest.FixtureChainID).Return(addr, nil) - ethClient.On("PendingNonceAt", mock.AnythingOfType("*context.cancelCtx"), gethcommon.Address{}).Return(uint64(0), nil).Maybe() + ethClient.On("PendingNonceAt", mock.AnythingOfType("*context.cancelCtx"), common.Address{}).Return(uint64(0), nil).Maybe() keyChangeCh <- struct{}{} require.NoError(t, txm.Close()) @@ -568,3 +574,244 @@ func TestTxm_Reset(t *testing.T) { assert.Equal(t, 0, count) }) } + +func newTxStore(t *testing.T, db *sqlx.DB, cfg pg.QConfig) txmgr.EvmTxStore { + return txmgr.NewTxStore(db, logger.TestLogger(t), cfg) +} + +func newEthReceipt(blockNumber int64, blockHash common.Hash, txHash common.Hash, status uint64) txmgr.Receipt { + transactionIndex := uint(cltest.NewRandomPositiveInt64()) + + receipt := evmtypes.Receipt{ + BlockNumber: big.NewInt(blockNumber), + BlockHash: blockHash, + TxHash: txHash, + TransactionIndex: transactionIndex, + Status: status, + } + + r := txmgr.Receipt{ + BlockNumber: blockNumber, + BlockHash: blockHash, + TxHash: txHash, + TransactionIndex: transactionIndex, + Receipt: receipt, + } + return r +} + +func mustInsertEthReceipt(t *testing.T, txStore txmgr.TestEvmTxStore, blockNumber int64, blockHash common.Hash, txHash common.Hash) txmgr.Receipt { + r := newEthReceipt(blockNumber, blockHash, txHash, 0x1) + id, err := txStore.InsertReceipt(&r.Receipt) + require.NoError(t, err) + r.ID = id + return r +} + +func mustInsertRevertedEthReceipt(t *testing.T, txStore txmgr.TestEvmTxStore, blockNumber int64, blockHash common.Hash, txHash common.Hash) txmgr.Receipt { + r := newEthReceipt(blockNumber, blockHash, txHash, 0x0) + id, err := txStore.InsertReceipt(&r.Receipt) + require.NoError(t, err) + r.ID = id + return r +} + +// Inserts into evm.receipts but does not update evm.txes or evm.tx_attempts +func mustInsertConfirmedEthTxWithReceipt(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address, nonce, blockNum int64) (etx txmgr.Tx) { + etx = cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, nonce, blockNum, fromAddress) + mustInsertEthReceipt(t, txStore, blockNum, utils.NewHash(), etx.TxAttempts[0].Hash) + return etx +} + +func mustInsertConfirmedEthTxBySaveFetchedReceipts(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address, nonce int64, blockNum int64, chainID big.Int) (etx txmgr.Tx) { + etx = cltest.MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, nonce, blockNum, fromAddress) + receipt := evmtypes.Receipt{ + TxHash: etx.TxAttempts[0].Hash, + BlockHash: utils.NewHash(), + BlockNumber: big.NewInt(nonce), + TransactionIndex: uint(1), + } + err := txStore.SaveFetchedReceipts(testutils.Context(t), []*evmtypes.Receipt{&receipt}, &chainID) + require.NoError(t, err) + return etx +} + +func mustInsertFatalErrorEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address) txmgr.Tx { + etx := cltest.NewEthTx(fromAddress) + etx.Error = null.StringFrom("something exploded") + etx.State = txmgrcommon.TxFatalError + + require.NoError(t, txStore.InsertTx(&etx)) + return etx +} + +func mustInsertUnconfirmedEthTxWithAttemptState(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, txAttemptState txmgrtypes.TxAttemptState, opts ...interface{}) txmgr.Tx { + etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress, opts...) + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + + tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + rlp := new(bytes.Buffer) + require.NoError(t, tx.EncodeRLP(rlp)) + attempt.SignedRawTx = rlp.Bytes() + + attempt.State = txAttemptState + require.NoError(t, txStore.InsertTxAttempt(&attempt)) + etx, err := txStore.FindTxWithAttempts(etx.ID) + require.NoError(t, err) + return etx +} + +func mustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, opts ...interface{}) txmgr.Tx { + etx := cltest.MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress, opts...) + attempt := cltest.NewDynamicFeeEthTxAttempt(t, etx.ID) + + addr := testutils.NewAddress() + dtx := types.DynamicFeeTx{ + ChainID: big.NewInt(0), + Nonce: uint64(nonce), + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(1), + Gas: 242, + To: &addr, + Value: big.NewInt(342), + Data: []byte{2, 3, 4}, + } + tx := types.NewTx(&dtx) + rlp := new(bytes.Buffer) + require.NoError(t, tx.EncodeRLP(rlp)) + attempt.SignedRawTx = rlp.Bytes() + + attempt.State = txmgrtypes.TxAttemptBroadcast + require.NoError(t, txStore.InsertTxAttempt(&attempt)) + etx, err := txStore.FindTxWithAttempts(etx.ID) + require.NoError(t, err) + return etx +} + +func mustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address) txmgr.Tx { + timeNow := time.Now() + etx := cltest.NewEthTx(fromAddress) + + etx.BroadcastAt = &timeNow + etx.InitialBroadcastAt = &timeNow + n := evmtypes.Nonce(nonce) + etx.Sequence = &n + etx.State = txmgrcommon.TxUnconfirmed + require.NoError(t, txStore.InsertTx(&etx)) + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + + tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + rlp := new(bytes.Buffer) + require.NoError(t, tx.EncodeRLP(rlp)) + attempt.SignedRawTx = rlp.Bytes() + + attempt.State = txmgrtypes.TxAttemptInsufficientFunds + require.NoError(t, txStore.InsertTxAttempt(&attempt)) + etx, err := txStore.FindTxWithAttempts(etx.ID) + require.NoError(t, err) + return etx +} + +func mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( + t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, broadcastBeforeBlockNum int64, + broadcastAt time.Time, fromAddress common.Address) txmgr.Tx { + etx := cltest.NewEthTx(fromAddress) + + etx.BroadcastAt = &broadcastAt + etx.InitialBroadcastAt = &broadcastAt + n := evmtypes.Nonce(nonce) + etx.Sequence = &n + etx.State = txmgrcommon.TxConfirmedMissingReceipt + require.NoError(t, txStore.InsertTx(&etx)) + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + attempt.BroadcastBeforeBlockNum = &broadcastBeforeBlockNum + attempt.State = txmgrtypes.TxAttemptBroadcast + require.NoError(t, txStore.InsertTxAttempt(&attempt)) + etx.TxAttempts = append(etx.TxAttempts, attempt) + return etx +} + +func mustInsertInProgressEthTxWithAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce evmtypes.Nonce, fromAddress common.Address) txmgr.Tx { + etx := cltest.NewEthTx(fromAddress) + + etx.Sequence = &nonce + etx.State = txmgrcommon.TxInProgress + require.NoError(t, txStore.InsertTx(&etx)) + attempt := cltest.NewLegacyEthTxAttempt(t, etx.ID) + tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) + rlp := new(bytes.Buffer) + require.NoError(t, tx.EncodeRLP(rlp)) + attempt.SignedRawTx = rlp.Bytes() + attempt.State = txmgrtypes.TxAttemptInProgress + require.NoError(t, txStore.InsertTxAttempt(&attempt)) + etx, err := txStore.FindTxWithAttempts(etx.ID) + require.NoError(t, err) + return etx +} + +func mustCreateUnstartedGeneratedTx(t testing.TB, txStore txmgr.EvmTxStore, fromAddress common.Address, chainID *big.Int, opts ...func(*txmgr.TxRequest)) (tx txmgr.Tx) { + txRequest := txmgr.TxRequest{ + FromAddress: fromAddress, + } + + // Apply the default options + withDefaults()(&txRequest) + // Apply the optional parameters + for _, opt := range opts { + opt(&txRequest) + } + return mustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, chainID) +} + +func withDefaults() func(*txmgr.TxRequest) { + return func(tx *txmgr.TxRequest) { + tx.ToAddress = testutils.NewAddress() + tx.EncodedPayload = []byte{1, 2, 3} + tx.Value = big.Int(assets.NewEthValue(142)) + tx.FeeLimit = uint32(1000000000) + tx.Strategy = txmgrcommon.NewSendEveryStrategy() + // Set default values for other fields if needed + } +} + +func mustCreateUnstartedTx(t testing.TB, txStore txmgr.EvmTxStore, fromAddress common.Address, toAddress common.Address, encodedPayload []byte, gasLimit uint32, value big.Int, chainID *big.Int, opts ...interface{}) (tx txmgr.Tx) { + txRequest := txmgr.TxRequest{ + FromAddress: fromAddress, + ToAddress: toAddress, + EncodedPayload: encodedPayload, + Value: value, + FeeLimit: gasLimit, + Strategy: txmgrcommon.NewSendEveryStrategy(), + } + + return mustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, chainID) +} + +func mustCreateUnstartedTxFromEvmTxRequest(t testing.TB, txStore txmgr.EvmTxStore, txRequest txmgr.TxRequest, chainID *big.Int) (tx txmgr.Tx) { + tx, err := txStore.CreateTransaction(testutils.Context(t), txRequest, chainID) + require.NoError(t, err) + return tx +} + +func txRequestWithStrategy(strategy txmgrtypes.TxStrategy) func(*txmgr.TxRequest) { + return func(tx *txmgr.TxRequest) { + tx.Strategy = strategy + } +} + +func txRequestWithChecker(checker txmgr.TransmitCheckerSpec) func(*txmgr.TxRequest) { + return func(tx *txmgr.TxRequest) { + tx.Checker = checker + } +} +func txRequestWithValue(value big.Int) func(*txmgr.TxRequest) { + return func(tx *txmgr.TxRequest) { + tx.Value = value + } +} + +func txRequestWithIdempotencyKey(idempotencyKey string) func(*txmgr.TxRequest) { + return func(tx *txmgr.TxRequest) { + tx.IdempotencyKey = &idempotencyKey + } +} diff --git a/core/cmd/admin_commands_test.go b/core/cmd/admin_commands_test.go index 954e3577d3d..fc4c1b7e959 100644 --- a/core/cmd/admin_commands_test.go +++ b/core/cmd/admin_commands_test.go @@ -43,7 +43,7 @@ func TestShell_CreateUser(t *testing.T) { test := tt t.Run(test.name, func(t *testing.T) { set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.CreateUser, set, "") + flagSetApplyFromAction(client.CreateUser, set, "") require.NoError(t, set.Set("email", test.email)) require.NoError(t, set.Set("role", test.role)) @@ -83,7 +83,7 @@ func TestShell_ChangeRole(t *testing.T) { test := tt t.Run(test.name, func(t *testing.T) { set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ChangeRole, set, "") + flagSetApplyFromAction(client.ChangeRole, set, "") require.NoError(t, set.Set("email", test.email)) require.NoError(t, set.Set("new-role", test.role)) @@ -118,7 +118,7 @@ func TestShell_DeleteUser(t *testing.T) { test := tt t.Run(test.name, func(t *testing.T) { set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteUser, set, "") + flagSetApplyFromAction(client.DeleteUser, set, "") require.NoError(t, set.Set("email", test.email)) c := cli.NewContext(nil, set, nil) @@ -138,7 +138,7 @@ func TestShell_ListUsers(t *testing.T) { require.NoError(t, app.AuthenticationProvider().CreateUser(&user)) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ListUsers, set, "") + flagSetApplyFromAction(client.ListUsers, set, "") c := cli.NewContext(nil, set, nil) buffer := bytes.NewBufferString("") diff --git a/core/cmd/blocks_commands_test.go b/core/cmd/blocks_commands_test.go index a972df67d64..d0c0e118f9d 100644 --- a/core/cmd/blocks_commands_test.go +++ b/core/cmd/blocks_commands_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/require" "github.com/urfave/cli" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -24,7 +23,7 @@ func Test_ReplayFromBlock(t *testing.T) { client, _ := app.NewShellAndRenderer() set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ReplayFromBlock, set, "") + flagSetApplyFromAction(client.ReplayFromBlock, set, "") //Incorrect block number require.NoError(t, set.Set("block-number", "0")) diff --git a/core/cmd/bridge_commands_test.go b/core/cmd/bridge_commands_test.go index 4f043ff87e8..fae5d68e678 100644 --- a/core/cmd/bridge_commands_test.go +++ b/core/cmd/bridge_commands_test.go @@ -111,7 +111,7 @@ func TestShell_ShowBridge(t *testing.T) { require.NoError(t, app.BridgeORM().CreateBridgeType(bt)) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ShowBridge, set, "") + flagSetApplyFromAction(client.ShowBridge, set, "") require.NoError(t, set.Parse([]string{bt.Name.String()})) @@ -148,7 +148,7 @@ func TestShell_CreateBridge(t *testing.T) { test := tt t.Run(test.name, func(t *testing.T) { set := flag.NewFlagSet("bridge", 0) - cltest.FlagSetApplyFromAction(client.CreateBridge, set, "") + flagSetApplyFromAction(client.CreateBridge, set, "") require.NoError(t, set.Parse([]string{test.param})) @@ -177,7 +177,7 @@ func TestShell_RemoveBridge(t *testing.T) { require.NoError(t, err) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoveBridge, set, "") + flagSetApplyFromAction(client.RemoveBridge, set, "") require.NoError(t, set.Parse([]string{bt.Name.String()})) diff --git a/core/cmd/cosmos_keys_commands_test.go b/core/cmd/cosmos_keys_commands_test.go index 05a26fe84d7..2cab11379d0 100644 --- a/core/cmd/cosmos_keys_commands_test.go +++ b/core/cmd/cosmos_keys_commands_test.go @@ -95,7 +95,7 @@ func TestShell_CosmosKeys(t *testing.T) { require.NoError(t, err) requireCosmosKeyCount(t, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(cmd.NewCosmosKeysClient(client).DeleteKey, set, "cosmos") + flagSetApplyFromAction(cmd.NewCosmosKeysClient(client).DeleteKey, set, "cosmos") strID := key.ID() require.NoError(tt, set.Set("yes", "true")) @@ -121,7 +121,7 @@ func TestShell_CosmosKeys(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test Cosmos export", 0) - cltest.FlagSetApplyFromAction(cmd.NewCosmosKeysClient(client).ExportKey, set, "cosmos") + flagSetApplyFromAction(cmd.NewCosmosKeysClient(client).ExportKey, set, "cosmos") require.NoError(tt, set.Parse([]string{"0"})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -135,7 +135,7 @@ func TestShell_CosmosKeys(t *testing.T) { // Export test set = flag.NewFlagSet("test Cosmos export", 0) - cltest.FlagSetApplyFromAction(cmd.NewCosmosKeysClient(client).ExportKey, set, "cosmos") + flagSetApplyFromAction(cmd.NewCosmosKeysClient(client).ExportKey, set, "cosmos") require.NoError(tt, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -150,7 +150,7 @@ func TestShell_CosmosKeys(t *testing.T) { requireCosmosKeyCount(t, app, 0) set = flag.NewFlagSet("test Cosmos import", 0) - cltest.FlagSetApplyFromAction(cmd.NewCosmosKeysClient(client).ImportKey, set, "cosmos") + flagSetApplyFromAction(cmd.NewCosmosKeysClient(client).ImportKey, set, "cosmos") require.NoError(tt, set.Parse([]string{keyName})) require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/cosmos_transaction_commands_test.go b/core/cmd/cosmos_transaction_commands_test.go index f54ccaf4a68..5b5454eed44 100644 --- a/core/cmd/cosmos_transaction_commands_test.go +++ b/core/cmd/cosmos_transaction_commands_test.go @@ -21,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/params" "github.com/smartcontractkit/chainlink/v2/core/cmd" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/cosmostest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/cosmoskey" @@ -93,7 +92,7 @@ func TestShell_SendCosmosCoins(t *testing.T) { require.NoError(t, err) set := flag.NewFlagSet("sendcosmoscoins", 0) - cltest.FlagSetApplyFromAction(client.CosmosSendNativeToken, set, "cosmos") + flagSetApplyFromAction(client.CosmosSendNativeToken, set, "cosmos") require.NoError(t, set.Set("id", chainID)) require.NoError(t, set.Parse([]string{nativeToken, tt.amount, from.Address.String(), to.Address.String()})) diff --git a/core/cmd/csa_keys_commands_test.go b/core/cmd/csa_keys_commands_test.go index 23749476ac8..869608fa72b 100644 --- a/core/cmd/csa_keys_commands_test.go +++ b/core/cmd/csa_keys_commands_test.go @@ -98,7 +98,7 @@ func TestShell_ImportExportCsaKey(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test CSA export", 0) - cltest.FlagSetApplyFromAction(client.ExportCSAKey, set, "") + flagSetApplyFromAction(client.ExportCSAKey, set, "") require.NoError(t, set.Parse([]string{"0"})) require.NoError(t, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -111,7 +111,7 @@ func TestShell_ImportExportCsaKey(t *testing.T) { // Export test set = flag.NewFlagSet("test CSA export", 0) - cltest.FlagSetApplyFromAction(client.ExportCSAKey, set, "") + flagSetApplyFromAction(client.ExportCSAKey, set, "") require.NoError(t, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(t, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -127,7 +127,7 @@ func TestShell_ImportExportCsaKey(t *testing.T) { //Import test set = flag.NewFlagSet("test CSA import", 0) - cltest.FlagSetApplyFromAction(client.ImportCSAKey, set, "") + flagSetApplyFromAction(client.ImportCSAKey, set, "") require.NoError(t, set.Parse([]string{keyName})) require.NoError(t, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/dkgencrypt_keys_commands_test.go b/core/cmd/dkgencrypt_keys_commands_test.go index 61e343569cb..a7505ce46dc 100644 --- a/core/cmd/dkgencrypt_keys_commands_test.go +++ b/core/cmd/dkgencrypt_keys_commands_test.go @@ -94,7 +94,7 @@ func TestShell_DKGEncryptKeys(t *testing.T) { assert.NoError(tt, err) requireDKGEncryptKeyCount(tt, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).DeleteKey, set, "") + flagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).DeleteKey, set, "") require.NoError(tt, set.Set("yes", "true")) @@ -122,7 +122,7 @@ func TestShell_DKGEncryptKeys(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test DKGEncrypt export", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).ExportKey, set, "") + flagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).ExportKey, set, "") require.NoError(tt, set.Parse([]string{"0"})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -135,7 +135,7 @@ func TestShell_DKGEncryptKeys(t *testing.T) { // Export test set = flag.NewFlagSet("test DKGEncrypt export", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).ExportKey, set, "") + flagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).ExportKey, set, "") require.NoError(tt, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -151,7 +151,7 @@ func TestShell_DKGEncryptKeys(t *testing.T) { //Import test set = flag.NewFlagSet("test DKGEncrypt import", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).ImportKey, set, "") + flagSetApplyFromAction(cmd.NewDKGEncryptKeysClient(client).ImportKey, set, "") require.NoError(tt, set.Parse([]string{keyName})) require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/dkgsign_keys_commands_test.go b/core/cmd/dkgsign_keys_commands_test.go index 27413e6ae7b..1948800d677 100644 --- a/core/cmd/dkgsign_keys_commands_test.go +++ b/core/cmd/dkgsign_keys_commands_test.go @@ -94,7 +94,7 @@ func TestShell_DKGSignKeys(t *testing.T) { assert.NoError(tt, err) requireDKGSignKeyCount(tt, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).DeleteKey, set, "") + flagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).DeleteKey, set, "") require.NoError(tt, set.Set("yes", "true")) strID := key.ID() @@ -121,7 +121,7 @@ func TestShell_DKGSignKeys(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test DKGSign export", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).ExportKey, set, "") + flagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).ExportKey, set, "") require.NoError(tt, set.Parse([]string{"0"})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -134,7 +134,7 @@ func TestShell_DKGSignKeys(t *testing.T) { // Export test set = flag.NewFlagSet("test DKGSign export", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).ExportKey, set, "") + flagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).ExportKey, set, "") require.NoError(tt, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -149,7 +149,7 @@ func TestShell_DKGSignKeys(t *testing.T) { requireDKGSignKeyCount(tt, app, 0) set = flag.NewFlagSet("test DKGSign import", 0) - cltest.FlagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).ImportKey, set, "") + flagSetApplyFromAction(cmd.NewDKGSignKeysClient(client).ImportKey, set, "") require.NoError(tt, set.Parse([]string{keyName})) require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/eth_keys_commands_test.go b/core/cmd/eth_keys_commands_test.go index 293a2d3f6da..3eb45e27bd0 100644 --- a/core/cmd/eth_keys_commands_test.go +++ b/core/cmd/eth_keys_commands_test.go @@ -191,7 +191,7 @@ func TestShell_CreateETHKey(t *testing.T) { id := big.NewInt(0) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.CreateETHKey, set, "") + flagSetApplyFromAction(client.CreateETHKey, set, "") require.NoError(t, set.Set("evm-chain-id", testutils.FixtureChainID.String())) @@ -224,7 +224,7 @@ func TestShell_DeleteETHKey(t *testing.T) { // Delete the key set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteETHKey, set, "") + flagSetApplyFromAction(client.DeleteETHKey, set, "") require.NoError(t, set.Set("yes", "true")) require.NoError(t, set.Parse([]string{key.Address.Hex()})) @@ -257,7 +257,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { ethKeyStore := app.GetKeyStore().Eth() set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("file", "internal/fixtures/apicredentials")) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -281,7 +281,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { defer os.RemoveAll(testdir) keyfilepath := filepath.Join(testdir, "key") set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ExportETHKey, set, "") + flagSetApplyFromAction(client.ExportETHKey, set, "") require.NoError(t, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) require.NoError(t, set.Set("output", keyfilepath)) @@ -293,7 +293,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { // Delete the key set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteETHKey, set, "") + flagSetApplyFromAction(client.DeleteETHKey, set, "") require.NoError(t, set.Set("yes", "true")) require.NoError(t, set.Parse([]string{address})) @@ -308,7 +308,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { // Import the key set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ImportETHKey, set, "") + flagSetApplyFromAction(client.ImportETHKey, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) @@ -321,7 +321,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { r.Renders = nil set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ListETHKeys, set, "") + flagSetApplyFromAction(client.ListETHKeys, set, "") c = cli.NewContext(nil, set, nil) err = client.ListETHKeys(c) require.NoError(t, err) @@ -332,7 +332,7 @@ func TestShell_ImportExportETHKey_NoChains(t *testing.T) { // Export test invalid id keyName := keyNameForTest(t) set = flag.NewFlagSet("test Eth export invalid id", 0) - cltest.FlagSetApplyFromAction(client.ExportETHKey, set, "") + flagSetApplyFromAction(client.ExportETHKey, set, "") require.NoError(t, set.Parse([]string{"999"})) require.NoError(t, set.Set("new-password", "../internal/fixtures/apicredentials")) @@ -365,7 +365,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { ethClient.On("LINKBalance", mock.Anything, mock.Anything, mock.Anything).Return(commonassets.NewLinkFromJuels(42), nil) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("file", "internal/fixtures/apicredentials")) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -389,7 +389,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { defer os.RemoveAll(testdir) keyfilepath := filepath.Join(testdir, "key") set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ExportETHKey, set, "") + flagSetApplyFromAction(client.ExportETHKey, set, "") require.NoError(t, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) require.NoError(t, set.Set("output", keyfilepath)) @@ -401,7 +401,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { // Delete the key set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteETHKey, set, "") + flagSetApplyFromAction(client.DeleteETHKey, set, "") require.NoError(t, set.Set("yes", "true")) require.NoError(t, set.Parse([]string{address})) @@ -414,7 +414,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { // Import the key set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ImportETHKey, set, "") + flagSetApplyFromAction(client.ImportETHKey, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) @@ -428,7 +428,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { r.Renders = nil set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.ListETHKeys, set, "") + flagSetApplyFromAction(client.ListETHKeys, set, "") c = cli.NewContext(nil, set, nil) err = client.ListETHKeys(c) require.NoError(t, err) @@ -439,7 +439,7 @@ func TestShell_ImportExportETHKey_WithChains(t *testing.T) { // Export test invalid id keyName := keyNameForTest(t) set = flag.NewFlagSet("test Eth export invalid id", 0) - cltest.FlagSetApplyFromAction(client.ExportETHKey, set, "") + flagSetApplyFromAction(client.ExportETHKey, set, "") require.NoError(t, set.Parse([]string{"999"})) require.NoError(t, set.Set("new-password", "../internal/fixtures/apicredentials")) diff --git a/core/cmd/evm_transaction_commands_test.go b/core/cmd/evm_transaction_commands_test.go index e071d875f03..6c079a12495 100644 --- a/core/cmd/evm_transaction_commands_test.go +++ b/core/cmd/evm_transaction_commands_test.go @@ -37,7 +37,7 @@ func TestShell_IndexTransactions(t *testing.T) { // page 1 set := flag.NewFlagSet("test transactions", 0) - cltest.FlagSetApplyFromAction(client.IndexTransactions, set, "") + flagSetApplyFromAction(client.IndexTransactions, set, "") require.NoError(t, set.Set("page", "1")) @@ -51,7 +51,7 @@ func TestShell_IndexTransactions(t *testing.T) { // page 2 which doesn't exist set = flag.NewFlagSet("test txattempts", 0) - cltest.FlagSetApplyFromAction(client.IndexTransactions, set, "") + flagSetApplyFromAction(client.IndexTransactions, set, "") require.NoError(t, set.Set("page", "2")) @@ -77,7 +77,7 @@ func TestShell_ShowTransaction(t *testing.T) { attempt := tx.TxAttempts[0] set := flag.NewFlagSet("test get tx", 0) - cltest.FlagSetApplyFromAction(client.ShowTransaction, set, "") + flagSetApplyFromAction(client.ShowTransaction, set, "") require.NoError(t, set.Parse([]string{attempt.Hash.String()})) @@ -101,7 +101,7 @@ func TestShell_IndexTxAttempts(t *testing.T) { // page 1 set := flag.NewFlagSet("test txattempts", 0) - cltest.FlagSetApplyFromAction(client.IndexTxAttempts, set, "") + flagSetApplyFromAction(client.IndexTxAttempts, set, "") require.NoError(t, set.Set("page", "1")) @@ -115,7 +115,7 @@ func TestShell_IndexTxAttempts(t *testing.T) { // page 2 which doesn't exist set = flag.NewFlagSet("test transactions", 0) - cltest.FlagSetApplyFromAction(client.IndexTxAttempts, set, "") + flagSetApplyFromAction(client.IndexTxAttempts, set, "") require.NoError(t, set.Set("page", "2")) @@ -158,7 +158,7 @@ func TestShell_SendEther_From_Txm(t *testing.T) { db := app.GetSqlxDB() set := flag.NewFlagSet("sendether", 0) - cltest.FlagSetApplyFromAction(client.SendEther, set, "") + flagSetApplyFromAction(client.SendEther, set, "") amount := "100.5" to := "0x342156c8d3bA54Abc67920d35ba1d1e67201aC9C" @@ -218,7 +218,7 @@ func TestShell_SendEther_From_Txm_WEI(t *testing.T) { db := app.GetSqlxDB() set := flag.NewFlagSet("sendether", 0) - cltest.FlagSetApplyFromAction(client.SendEther, set, "") + flagSetApplyFromAction(client.SendEther, set, "") require.NoError(t, set.Set("id", testutils.FixtureChainID.String())) require.NoError(t, set.Set("wei", "false")) diff --git a/core/cmd/forwarders_commands_test.go b/core/cmd/forwarders_commands_test.go index b08d94f64dc..179216b8e41 100644 --- a/core/cmd/forwarders_commands_test.go +++ b/core/cmd/forwarders_commands_test.go @@ -73,7 +73,7 @@ func TestShell_TrackEVMForwarder(t *testing.T) { // Create the fwdr set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.TrackForwarder, set, "") + flagSetApplyFromAction(client.TrackForwarder, set, "") require.NoError(t, set.Set("address", utils.RandomAddress().Hex())) require.NoError(t, set.Set("evm-chain-id", id.String())) @@ -92,7 +92,7 @@ func TestShell_TrackEVMForwarder(t *testing.T) { // Delete fwdr set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteForwarder, set, "") + flagSetApplyFromAction(client.DeleteForwarder, set, "") require.NoError(t, set.Parse([]string{createOutput.ID})) @@ -118,7 +118,7 @@ func TestShell_TrackEVMForwarder_BadAddress(t *testing.T) { // Create the fwdr set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.TrackForwarder, set, "") + flagSetApplyFromAction(client.TrackForwarder, set, "") require.NoError(t, set.Set("address", "0xWrongFormatAddress")) require.NoError(t, set.Set("evm-chain-id", id.String())) @@ -137,7 +137,7 @@ func TestShell_DeleteEVMForwarders_MissingFwdId(t *testing.T) { // Delete fwdr without id set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteForwarder, set, "") + flagSetApplyFromAction(client.DeleteForwarder, set, "") c := cli.NewContext(nil, set, nil) require.Equal(t, "must pass the forwarder id to be archived", client.DeleteForwarder(c).Error()) diff --git a/core/cmd/jobs_commands_test.go b/core/cmd/jobs_commands_test.go index b83b17f0be6..04908e18ff3 100644 --- a/core/cmd/jobs_commands_test.go +++ b/core/cmd/jobs_commands_test.go @@ -313,7 +313,7 @@ func TestShell_ListFindJobs(t *testing.T) { // Create the job fs := flag.NewFlagSet("", flag.ExitOnError) - cltest.FlagSetApplyFromAction(client.CreateJob, fs, "") + flagSetApplyFromAction(client.CreateJob, fs, "") require.NoError(t, fs.Parse([]string{getDirectRequestSpec()})) @@ -339,7 +339,7 @@ func TestShell_ShowJob(t *testing.T) { // Create the job fs := flag.NewFlagSet("", flag.ExitOnError) - cltest.FlagSetApplyFromAction(client.CreateJob, fs, "") + flagSetApplyFromAction(client.CreateJob, fs, "") require.NoError(t, fs.Parse([]string{getDirectRequestSpec()})) @@ -382,7 +382,7 @@ func TestShell_CreateJobV2(t *testing.T) { requireJobsCount(t, app.JobORM(), 0) fs := flag.NewFlagSet("", flag.ExitOnError) - cltest.FlagSetApplyFromAction(client.CreateJob, fs, "") + flagSetApplyFromAction(client.CreateJob, fs, "") nameAndExternalJobID := uuid.New() spec := fmt.Sprintf(ocrBootstrapSpec, nameAndExternalJobID, nameAndExternalJobID) @@ -413,7 +413,7 @@ func TestShell_DeleteJob(t *testing.T) { // Create the job fs := flag.NewFlagSet("", flag.ExitOnError) - cltest.FlagSetApplyFromAction(client.CreateJob, fs, "") + flagSetApplyFromAction(client.CreateJob, fs, "") require.NoError(t, fs.Parse([]string{getDirectRequestSpec()})) @@ -432,12 +432,12 @@ func TestShell_DeleteJob(t *testing.T) { // Must supply job id set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteJob, set, "") + flagSetApplyFromAction(client.DeleteJob, set, "") c := cli.NewContext(nil, set, nil) require.Equal(t, "must pass the job id to be archived", client.DeleteJob(c).Error()) set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteJob, set, "") + flagSetApplyFromAction(client.DeleteJob, set, "") require.NoError(t, set.Parse([]string{output.ID})) diff --git a/core/cmd/ocr2_keys_commands_test.go b/core/cmd/ocr2_keys_commands_test.go index 88ea426747e..dd2ac2544da 100644 --- a/core/cmd/ocr2_keys_commands_test.go +++ b/core/cmd/ocr2_keys_commands_test.go @@ -97,7 +97,7 @@ func TestShell_OCR2Keys(t *testing.T) { client, r := app.NewShellAndRenderer() set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.CreateOCR2KeyBundle, set, "") + flagSetApplyFromAction(client.CreateOCR2KeyBundle, set, "") require.NoError(tt, set.Parse([]string{"evm"})) @@ -119,7 +119,7 @@ func TestShell_OCR2Keys(t *testing.T) { require.NoError(t, err) requireOCR2KeyCount(t, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteOCR2KeyBundle, set, "") + flagSetApplyFromAction(client.DeleteOCR2KeyBundle, set, "") require.NoError(tt, set.Parse([]string{key.ID()})) require.NoError(tt, set.Set("yes", "true")) @@ -147,7 +147,7 @@ func TestShell_OCR2Keys(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test OCR2 export", 0) - cltest.FlagSetApplyFromAction(client.ExportOCR2Key, set, "") + flagSetApplyFromAction(client.ExportOCR2Key, set, "") require.NoError(tt, set.Parse([]string{"0"})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/new_password.txt")) @@ -160,7 +160,7 @@ func TestShell_OCR2Keys(t *testing.T) { // Export set = flag.NewFlagSet("test OCR2 export", 0) - cltest.FlagSetApplyFromAction(client.ExportOCR2Key, set, "") + flagSetApplyFromAction(client.ExportOCR2Key, set, "") require.NoError(tt, set.Parse([]string{key.ID()})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/new_password.txt")) @@ -175,7 +175,7 @@ func TestShell_OCR2Keys(t *testing.T) { requireOCR2KeyCount(t, app, 0) set = flag.NewFlagSet("test OCR2 import", 0) - cltest.FlagSetApplyFromAction(client.ImportOCR2Key, set, "") + flagSetApplyFromAction(client.ImportOCR2Key, set, "") require.NoError(tt, set.Parse([]string{keyName})) require.NoError(tt, set.Set("old-password", "../internal/fixtures/new_password.txt")) diff --git a/core/cmd/ocr_keys_commands_test.go b/core/cmd/ocr_keys_commands_test.go index 2ae765d4d2a..aeda9006610 100644 --- a/core/cmd/ocr_keys_commands_test.go +++ b/core/cmd/ocr_keys_commands_test.go @@ -111,7 +111,7 @@ func TestShell_DeleteOCRKeyBundle(t *testing.T) { requireOCRKeyCount(t, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteOCRKeyBundle, set, "") + flagSetApplyFromAction(client.DeleteOCRKeyBundle, set, "") require.NoError(t, set.Parse([]string{key.ID()})) require.NoError(t, set.Set("yes", "true")) @@ -140,7 +140,7 @@ func TestShell_ImportExportOCRKey(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test OCR export", 0) - cltest.FlagSetApplyFromAction(client.ExportOCRKey, set, "") + flagSetApplyFromAction(client.ExportOCRKey, set, "") require.NoError(t, set.Parse([]string{"0"})) require.NoError(t, set.Set("new-password", "../internal/fixtures/new_password.txt")) @@ -153,7 +153,7 @@ func TestShell_ImportExportOCRKey(t *testing.T) { // Export set = flag.NewFlagSet("test OCR export", 0) - cltest.FlagSetApplyFromAction(client.ExportOCRKey, set, "") + flagSetApplyFromAction(client.ExportOCRKey, set, "") require.NoError(t, set.Parse([]string{key.ID()})) require.NoError(t, set.Set("new-password", "../internal/fixtures/new_password.txt")) @@ -168,7 +168,7 @@ func TestShell_ImportExportOCRKey(t *testing.T) { requireOCRKeyCount(t, app, 0) set = flag.NewFlagSet("test OCR import", 0) - cltest.FlagSetApplyFromAction(client.ImportOCRKey, set, "") + flagSetApplyFromAction(client.ImportOCRKey, set, "") require.NoError(t, set.Parse([]string{keyName})) require.NoError(t, set.Set("old-password", "../internal/fixtures/new_password.txt")) diff --git a/core/cmd/p2p_keys_commands_test.go b/core/cmd/p2p_keys_commands_test.go index 0407c924575..683129fafa7 100644 --- a/core/cmd/p2p_keys_commands_test.go +++ b/core/cmd/p2p_keys_commands_test.go @@ -102,7 +102,7 @@ func TestShell_DeleteP2PKey(t *testing.T) { requireP2PKeyCount(t, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteP2PKey, set, "") + flagSetApplyFromAction(client.DeleteP2PKey, set, "") require.NoError(t, set.Set("yes", "true")) @@ -132,7 +132,7 @@ func TestShell_ImportExportP2PKeyBundle(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test P2P export", 0) - cltest.FlagSetApplyFromAction(client.ExportP2PKey, set, "") + flagSetApplyFromAction(client.ExportP2PKey, set, "") require.NoError(t, set.Parse([]string{"0"})) require.NoError(t, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -145,7 +145,7 @@ func TestShell_ImportExportP2PKeyBundle(t *testing.T) { // Export test set = flag.NewFlagSet("test P2P export", 0) - cltest.FlagSetApplyFromAction(client.ExportP2PKey, set, "") + flagSetApplyFromAction(client.ExportP2PKey, set, "") require.NoError(t, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(t, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -160,7 +160,7 @@ func TestShell_ImportExportP2PKeyBundle(t *testing.T) { requireP2PKeyCount(t, app, 0) set = flag.NewFlagSet("test P2P import", 0) - cltest.FlagSetApplyFromAction(client.ImportP2PKey, set, "") + flagSetApplyFromAction(client.ImportP2PKey, set, "") require.NoError(t, set.Parse([]string{keyName})) require.NoError(t, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 4d906214ef4..2dc182944d3 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -124,7 +124,7 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { } set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RunNode, set, "") + flagSetApplyFromAction(client.RunNode, set, "") require.NoError(t, set.Set("password", test.pwdfile)) @@ -221,7 +221,7 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { } set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RunNode, set, "") + flagSetApplyFromAction(client.RunNode, set, "") require.NoError(t, set.Set("api", test.apiFile)) @@ -318,7 +318,7 @@ func TestShell_RebroadcastTransactions_Txm(t *testing.T) { beginningNonce := uint64(7) endingNonce := uint64(10) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(c.RebroadcastTransactions, set, "") + flagSetApplyFromAction(c.RebroadcastTransactions, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("beginningNonce", strconv.FormatUint(beginningNonce, 10))) @@ -397,7 +397,7 @@ func TestShell_RebroadcastTransactions_OutsideRange_Txm(t *testing.T) { } set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(c.RebroadcastTransactions, set, "") + flagSetApplyFromAction(c.RebroadcastTransactions, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("beginningNonce", strconv.FormatUint(uint64(beginningNonce), 10))) @@ -477,7 +477,7 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) { } set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RebroadcastTransactions, set, "") + flagSetApplyFromAction(client.RebroadcastTransactions, set, "") require.NoError(t, set.Set("evmChainID", testutils.FixtureChainID.String())) require.NoError(t, set.Set("address", fromAddress.Hex())) diff --git a/core/cmd/shell_remote_test.go b/core/cmd/shell_remote_test.go index 91b56ee53a4..6143d678a90 100644 --- a/core/cmd/shell_remote_test.go +++ b/core/cmd/shell_remote_test.go @@ -123,7 +123,7 @@ func TestShell_ReplayBlocks(t *testing.T) { client, _ := app.NewShellAndRenderer() set := flag.NewFlagSet("flagset", 0) - cltest.FlagSetApplyFromAction(client.ReplayFromBlock, set, "") + flagSetApplyFromAction(client.ReplayFromBlock, set, "") require.NoError(t, set.Set("block-number", "42")) require.NoError(t, set.Set("evm-chain-id", "12345678")) @@ -156,7 +156,7 @@ func TestShell_CreateExternalInitiator(t *testing.T) { client, _ := app.NewShellAndRenderer() set := flag.NewFlagSet("create", 0) - cltest.FlagSetApplyFromAction(client.CreateExternalInitiator, set, "") + flagSetApplyFromAction(client.CreateExternalInitiator, set, "") assert.NoError(t, set.Parse(test.args)) c := cli.NewContext(nil, set, nil) @@ -197,7 +197,7 @@ func TestShell_CreateExternalInitiator_Errors(t *testing.T) { initialExis := len(cltest.AllExternalInitiators(t, app.GetSqlxDB())) set := flag.NewFlagSet("create", 0) - cltest.FlagSetApplyFromAction(client.CreateExternalInitiator, set, "") + flagSetApplyFromAction(client.CreateExternalInitiator, set, "") assert.NoError(t, set.Parse(test.args)) c := cli.NewContext(nil, set, nil) @@ -228,7 +228,7 @@ func TestShell_DestroyExternalInitiator(t *testing.T) { require.NoError(t, err) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteExternalInitiator, set, "") + flagSetApplyFromAction(client.DeleteExternalInitiator, set, "") require.NoError(t, set.Parse([]string{exi.Name})) @@ -246,7 +246,7 @@ func TestShell_DestroyExternalInitiator_NotFound(t *testing.T) { client, r := app.NewShellAndRenderer() set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteExternalInitiator, set, "") + flagSetApplyFromAction(client.DeleteExternalInitiator, set, "") require.NoError(t, set.Parse([]string{"bogus-ID"})) @@ -280,7 +280,7 @@ func TestShell_RemoteLogin(t *testing.T) { client := app.NewAuthenticatingShell(prompter) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("file", test.file)) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -318,7 +318,7 @@ func TestShell_RemoteBuildCompatibility(t *testing.T) { // Fails without bypass set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("bypass-version-check", "false")) @@ -329,7 +329,7 @@ func TestShell_RemoteBuildCompatibility(t *testing.T) { // Defaults to false set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") c = cli.NewContext(nil, set, nil) err = client.RemoteLogin(c) assert.Error(t, err) @@ -425,7 +425,7 @@ func TestShell_ChangePassword(t *testing.T) { otherClient := app.NewAuthenticatingShell(prompter) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("file", "../internal/fixtures/apicredentials")) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -473,7 +473,7 @@ func TestShell_Profile_InvalidSecondsParam(t *testing.T) { client := app.NewAuthenticatingShell(prompter) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("file", "../internal/fixtures/apicredentials")) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -504,7 +504,7 @@ func TestShell_Profile(t *testing.T) { client := app.NewAuthenticatingShell(prompter) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("file", "../internal/fixtures/apicredentials")) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -595,7 +595,7 @@ func TestShell_RunOCRJob_HappyPath(t *testing.T) { require.NoError(t, err) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("bypass-version-check", "true")) require.NoError(t, set.Parse([]string{strconv.FormatInt(int64(jb.ID), 10)})) @@ -613,7 +613,7 @@ func TestShell_RunOCRJob_MissingJobID(t *testing.T) { client, _ := app.NewShellAndRenderer() set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Set("bypass-version-check", "true")) @@ -630,7 +630,7 @@ func TestShell_RunOCRJob_JobNotFound(t *testing.T) { client, _ := app.NewShellAndRenderer() set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.RemoteLogin, set, "") + flagSetApplyFromAction(client.RemoteLogin, set, "") require.NoError(t, set.Parse([]string{"1"})) require.NoError(t, set.Set("bypass-version-check", "true")) @@ -659,7 +659,7 @@ func TestShell_AutoLogin(t *testing.T) { client.HTTP = cmd.NewAuthenticatedHTTPClient(app.Logger, app.NewClientOpts(), client.CookieAuthenticator, sr) fs := flag.NewFlagSet("", flag.ExitOnError) - cltest.FlagSetApplyFromAction(client.ListJobs, fs, "") + flagSetApplyFromAction(client.ListJobs, fs, "") err := client.ListJobs(cli.NewContext(nil, fs, nil)) require.NoError(t, err) @@ -687,7 +687,7 @@ func TestShell_AutoLogin_AuthFails(t *testing.T) { client.HTTP = cmd.NewAuthenticatedHTTPClient(app.Logger, app.NewClientOpts(), client.CookieAuthenticator, sr) fs := flag.NewFlagSet("", flag.ExitOnError) - cltest.FlagSetApplyFromAction(client.ListJobs, fs, "") + flagSetApplyFromAction(client.ListJobs, fs, "") err := client.ListJobs(cli.NewContext(nil, fs, nil)) require.Error(t, err) } @@ -716,7 +716,7 @@ func TestShell_SetLogConfig(t *testing.T) { logLevel := "warn" set := flag.NewFlagSet("loglevel", 0) - cltest.FlagSetApplyFromAction(client.SetLogLevel, set, "") + flagSetApplyFromAction(client.SetLogLevel, set, "") require.NoError(t, set.Set("level", logLevel)) @@ -728,7 +728,7 @@ func TestShell_SetLogConfig(t *testing.T) { sqlEnabled := true set = flag.NewFlagSet("logsql", 0) - cltest.FlagSetApplyFromAction(client.SetLogSQL, set, "") + flagSetApplyFromAction(client.SetLogSQL, set, "") require.NoError(t, set.Set("enable", strconv.FormatBool(sqlEnabled))) c = cli.NewContext(nil, set, nil) @@ -739,7 +739,7 @@ func TestShell_SetLogConfig(t *testing.T) { sqlEnabled = false set = flag.NewFlagSet("logsql", 0) - cltest.FlagSetApplyFromAction(client.SetLogSQL, set, "") + flagSetApplyFromAction(client.SetLogSQL, set, "") require.NoError(t, set.Set("disable", "true")) c = cli.NewContext(nil, set, nil) diff --git a/core/cmd/shell_test.go b/core/cmd/shell_test.go index 2a8c2c55861..ade14aa0d8a 100644 --- a/core/cmd/shell_test.go +++ b/core/cmd/shell_test.go @@ -2,15 +2,20 @@ package cmd_test import ( "crypto/rand" + "flag" "fmt" "math/big" "os" "path/filepath" + "reflect" + "runtime" + "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/urfave/cli" "github.com/smartcontractkit/chainlink-solana/pkg/solana" solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" @@ -514,3 +519,47 @@ func TestSetupStarkNetRelayer(t *testing.T) { require.Error(t, err) }) } + +// flagSetApplyFromAction applies the flags from action to the flagSet. +// `parentCommand` will filter the app commands and only applies the flags if the command/subcommand has a parent with that name, if left empty no filtering is done +func flagSetApplyFromAction(action interface{}, flagSet *flag.FlagSet, parentCommand string) { + cliApp := cmd.Shell{} + app := cmd.NewApp(&cliApp) + + foundName := parentCommand == "" + actionFuncName := getFuncName(action) + + for _, command := range app.Commands { + flags := recursiveFindFlagsWithName(actionFuncName, command, parentCommand, foundName) + + for _, flag := range flags { + flag.Apply(flagSet) + } + } + +} + +func recursiveFindFlagsWithName(actionFuncName string, command cli.Command, parent string, foundName bool) []cli.Flag { + + if command.Action != nil { + if actionFuncName == getFuncName(command.Action) && foundName { + return command.Flags + } + } + + for _, subcommand := range command.Subcommands { + if !foundName { + foundName = strings.EqualFold(subcommand.Name, parent) + } + + found := recursiveFindFlagsWithName(actionFuncName, subcommand, parent, foundName) + if found != nil { + return found + } + } + return nil +} + +func getFuncName(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} diff --git a/core/cmd/solana_keys_commands_test.go b/core/cmd/solana_keys_commands_test.go index 32b44fc8947..e72343be45c 100644 --- a/core/cmd/solana_keys_commands_test.go +++ b/core/cmd/solana_keys_commands_test.go @@ -95,7 +95,7 @@ func TestShell_SolanaKeys(t *testing.T) { require.NoError(t, err) requireSolanaKeyCount(t, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(cmd.NewSolanaKeysClient(client).DeleteKey, set, "solana") + flagSetApplyFromAction(cmd.NewSolanaKeysClient(client).DeleteKey, set, "solana") require.NoError(tt, set.Set("yes", "true")) @@ -122,7 +122,7 @@ func TestShell_SolanaKeys(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test Solana export", 0) - cltest.FlagSetApplyFromAction(cmd.NewSolanaKeysClient(client).ExportKey, set, "solana") + flagSetApplyFromAction(cmd.NewSolanaKeysClient(client).ExportKey, set, "solana") require.NoError(tt, set.Parse([]string{"0"})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -135,7 +135,7 @@ func TestShell_SolanaKeys(t *testing.T) { // Export test set = flag.NewFlagSet("test Solana export", 0) - cltest.FlagSetApplyFromAction(cmd.NewSolanaKeysClient(client).ExportKey, set, "solana") + flagSetApplyFromAction(cmd.NewSolanaKeysClient(client).ExportKey, set, "solana") require.NoError(tt, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -150,7 +150,7 @@ func TestShell_SolanaKeys(t *testing.T) { requireSolanaKeyCount(t, app, 0) set = flag.NewFlagSet("test Solana import", 0) - cltest.FlagSetApplyFromAction(cmd.NewSolanaKeysClient(client).ImportKey, set, "solana") + flagSetApplyFromAction(cmd.NewSolanaKeysClient(client).ImportKey, set, "solana") require.NoError(tt, set.Parse([]string{keyName})) require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/solana_transaction_commands_test.go b/core/cmd/solana_transaction_commands_test.go index f019616cb85..b190caec51b 100644 --- a/core/cmd/solana_transaction_commands_test.go +++ b/core/cmd/solana_transaction_commands_test.go @@ -21,7 +21,6 @@ import ( solcfg "github.com/smartcontractkit/chainlink-solana/pkg/solana/config" "github.com/smartcontractkit/chainlink/v2/core/cmd" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" ) func TestShell_SolanaSendSol(t *testing.T) { @@ -69,7 +68,7 @@ func TestShell_SolanaSendSol(t *testing.T) { require.NoError(t, err) set := flag.NewFlagSet("sendsolcoins", 0) - cltest.FlagSetApplyFromAction(client.SolanaSendSol, set, "solana") + flagSetApplyFromAction(client.SolanaSendSol, set, "solana") require.NoError(t, set.Set("id", chainID)) require.NoError(t, set.Parse([]string{tt.amount, from.PublicKey().String(), to.PublicKey().String()})) diff --git a/core/cmd/starknet_keys_commands_test.go b/core/cmd/starknet_keys_commands_test.go index 17584eefe05..b1fa4d88f05 100644 --- a/core/cmd/starknet_keys_commands_test.go +++ b/core/cmd/starknet_keys_commands_test.go @@ -94,7 +94,7 @@ func TestShell_StarkNetKeys(t *testing.T) { require.NoError(t, err) requireStarkNetKeyCount(t, app, 1) set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).DeleteKey, set, "starknet") + flagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).DeleteKey, set, "starknet") require.NoError(tt, set.Set("yes", "true")) @@ -121,7 +121,7 @@ func TestShell_StarkNetKeys(t *testing.T) { // Export test invalid id set := flag.NewFlagSet("test StarkNet export", 0) - cltest.FlagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).ExportKey, set, "starknet") + flagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).ExportKey, set, "starknet") require.NoError(tt, set.Parse([]string{"0"})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -134,7 +134,7 @@ func TestShell_StarkNetKeys(t *testing.T) { // Export test set = flag.NewFlagSet("test StarkNet export", 0) - cltest.FlagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).ExportKey, set, "starknet") + flagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).ExportKey, set, "starknet") require.NoError(tt, set.Parse([]string{fmt.Sprint(key.ID())})) require.NoError(tt, set.Set("new-password", "../internal/fixtures/incorrect_password.txt")) @@ -149,7 +149,7 @@ func TestShell_StarkNetKeys(t *testing.T) { requireStarkNetKeyCount(t, app, 0) set = flag.NewFlagSet("test StarkNet import", 0) - cltest.FlagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).ImportKey, set, "starknet") + flagSetApplyFromAction(cmd.NewStarkNetKeysClient(client).ImportKey, set, "starknet") require.NoError(tt, set.Parse([]string{keyName})) require.NoError(tt, set.Set("old-password", "../internal/fixtures/incorrect_password.txt")) diff --git a/core/cmd/vrf_keys_commands_test.go b/core/cmd/vrf_keys_commands_test.go index 20552604446..a061067771d 100644 --- a/core/cmd/vrf_keys_commands_test.go +++ b/core/cmd/vrf_keys_commands_test.go @@ -105,7 +105,7 @@ func TestShellVRF_CRUD(t *testing.T) { // Now do a hard delete and ensure its completely removes the key set := flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteVRFKey, set, "") + flagSetApplyFromAction(client.DeleteVRFKey, set, "") require.NoError(t, set.Parse([]string{k2.Compressed})) require.NoError(t, set.Set("hard", "true")) @@ -141,7 +141,7 @@ func TestVRF_ImportExport(t *testing.T) { // Export it, encrypted with cltest.Password instead keyName := "vrfkey1" set := flag.NewFlagSet("test VRF export", 0) - cltest.FlagSetApplyFromAction(client.ExportVRFKey, set, "") + flagSetApplyFromAction(client.ExportVRFKey, set, "") require.NoError(t, set.Parse([]string{k1.Compressed})) // Arguments require.NoError(t, set.Set("new-password", "../internal/fixtures/correct_password.txt")) @@ -157,7 +157,7 @@ func TestVRF_ImportExport(t *testing.T) { // Should error if we try to import a duplicate key importSet := flag.NewFlagSet("test VRF import", 0) - cltest.FlagSetApplyFromAction(client.ImportVRFKey, importSet, "") + flagSetApplyFromAction(client.ImportVRFKey, importSet, "") require.NoError(t, importSet.Parse([]string{keyName})) require.NoError(t, importSet.Set("old-password", "../internal/fixtures/correct_password.txt")) @@ -167,7 +167,7 @@ func TestVRF_ImportExport(t *testing.T) { // Lets delete the key and import it set = flag.NewFlagSet("test", 0) - cltest.FlagSetApplyFromAction(client.DeleteVRFKey, set, "") + flagSetApplyFromAction(client.DeleteVRFKey, set, "") require.NoError(t, set.Parse([]string{k1.Compressed})) require.NoError(t, set.Set("hard", "true")) diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index cf3f4f5c073..b1431bf0599 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -6,7 +6,6 @@ import ( crand "crypto/rand" "encoding/json" "errors" - "flag" "fmt" "io" "math/big" @@ -16,8 +15,6 @@ import ( "net/url" "os" "reflect" - "runtime" - "strings" "testing" "time" @@ -29,7 +26,6 @@ import ( "github.com/google/uuid" "github.com/gorilla/securecookie" "github.com/gorilla/sessions" - cryptop2p "github.com/libp2p/go-libp2p-core/crypto" p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/manyminds/api2go/jsonapi" "github.com/onsi/gomega" @@ -37,7 +33,6 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" - "github.com/urfave/cli" "github.com/jmoiron/sqlx" @@ -46,7 +41,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/common/client" - txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" @@ -55,7 +49,6 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -120,7 +113,6 @@ const ( var ( DefaultP2PPeerID p2pkey.PeerID FixtureChainID = *testutils.FixtureChainID - source rand.Source DefaultCosmosKey = cosmoskey.MustNewInsecure(keystest.NewRandReaderFromSeed(KeyBigIntSeed)) DefaultCSAKey = csakey.MustNewV2XXXTestingOnly(big.NewInt(KeyBigIntSeed)) @@ -147,13 +139,6 @@ func init() { fmt.Printf("[gin] %-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers) } - // Seed the random number generator, otherwise separate modules will take - // the same advisory locks when tested with `go test -p N` for N > 1 - seed := time.Now().UTC().UnixNano() - fmt.Printf("cltest random seed: %v\n", seed) - - // Also seed the local source - source = rand.NewSource(seed) defaultP2PPeerID, err := p2ppeer.Decode(configtest.DefaultPeerID) if err != nil { panic(err) @@ -201,22 +186,6 @@ func NewJobPipelineV2(t testing.TB, cfg pipeline.BridgeConfig, jpcfg JobPipeline } } -func NewEventBroadcaster(t testing.TB, dbURL url.URL) pg.EventBroadcaster { - lggr := logger.TestLogger(t) - return pg.NewEventBroadcaster(dbURL, 0, 0, lggr, uuid.New()) -} - -func NewEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient evmclient.Client, config evmconfig.ChainScopedConfig, ks keystore.Eth, fn txmgrcommon.ResumeCallback) *txmgr.Confirmer { - lggr := logger.TestLogger(t) - ge := config.EVM().GasEstimator() - estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(ge, ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) - txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) - ec := txmgr.NewEvmConfirmer(txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTxmConfig(config.EVM()), txmgr.NewEvmTxmFeeConfig(ge), config.EVM().Transactions(), config.Database(), ks, txBuilder, lggr) - ec.SetResumeCallback(fn) - require.NoError(t, ec.Start(testutils.Context(t))) - return ec -} - // TestApplication holds the test application and test servers type TestApplication struct { t testing.TB @@ -228,13 +197,6 @@ type TestApplication struct { Keys []ethkey.KeyV2 } -// NewWSServer starts a websocket server which invokes callback for each message received. -// If chainID is set, then eth_chainId calls will be automatically handled. -func NewWSServer(t *testing.T, chainID *big.Int, callback testutils.JSONRPCHandler) string { - server := testutils.NewWSServer(t, chainID, callback) - return server.WSURL().String() -} - // NewApplicationEVMDisabled creates a new application with default config but EVM disabled // Useful for testing controllers func NewApplicationEVMDisabled(t *testing.T) *TestApplication { @@ -1578,58 +1540,17 @@ func AssertRecordEventually(t *testing.T, db *sqlx.DB, model interface{}, stmt s }, testutils.WaitTimeout(t), DBPollingInterval).Should(gomega.BeTrue()) } -func MustSendingKeyStates(t *testing.T, ethKeyStore keystore.Eth, chainID *big.Int) []ethkey.State { - keys, err := ethKeyStore.EnabledKeysForChain(chainID) - require.NoError(t, err) - states, err := ethKeyStore.GetStatesForKeys(keys) - require.NoError(t, err) - return states -} - -func MustRandomP2PPeerID(t *testing.T) p2ppeer.ID { - reader := rand.New(source) - p2pPrivkey, _, err := cryptop2p.GenerateEd25519Key(reader) - require.NoError(t, err) - id, err := p2ppeer.IDFromPrivateKey(p2pPrivkey) - require.NoError(t, err) - return id -} - func MustWebURL(t *testing.T, s string) *models.WebURL { uri, err := url.Parse(s) require.NoError(t, err) return (*models.WebURL)(uri) } -func AssertPipelineTaskRunsSuccessful(t testing.TB, runs []pipeline.TaskRun) { - t.Helper() - for i, run := range runs { - require.True(t, run.Error.IsZero(), fmt.Sprintf("pipeline.Task run failed (idx: %v, dotID: %v, error: '%v')", i, run.GetDotID(), run.Error.ValueOrZero())) - } -} - -func AssertPipelineTaskRunsErrored(t testing.TB, runs []pipeline.TaskRun) { - t.Helper() - for i, run := range runs { - require.False(t, run.Error.IsZero(), fmt.Sprintf("expected pipeline.Task run to have failed, but it succeeded (idx: %v, dotID: %v, output: '%v')", i, run.GetDotID(), run.Output)) - } -} - func NewTestChainScopedConfig(t testing.TB) evmconfig.ChainScopedConfig { cfg := configtest.NewGeneralConfig(t, nil) return evmtest.NewChainScopedConfig(t, cfg) } -func MustGetStateForKey(t testing.TB, kst keystore.Eth, key ethkey.KeyV2) ethkey.State { - state, err := kst.GetStateForKey(key) - require.NoError(t, err) - return state -} - -func NewTxStore(t *testing.T, db *sqlx.DB, cfg pg.QConfig) txmgr.EvmTxStore { - return txmgr.NewTxStore(db, logger.TestLogger(t), cfg) -} - func NewTestTxStore(t *testing.T, db *sqlx.DB, cfg pg.QConfig) txmgr.TestEvmTxStore { return txmgr.NewTxStore(db, logger.TestLogger(t), cfg) } @@ -1647,47 +1568,3 @@ func ClearDBTables(t *testing.T, db *sqlx.DB, tables ...string) { err = tx.Commit() require.NoError(t, err) } - -// FlagSetApplyFromAction applies the flags from action to the flagSet. -// `parentCommand` will filter the app commands and only applies the flags if the command/subcommand has a parent with that name, if left empty no filtering is done -func FlagSetApplyFromAction(action interface{}, flagSet *flag.FlagSet, parentCommand string) { - cliApp := cmd.Shell{} - app := cmd.NewApp(&cliApp) - - foundName := parentCommand == "" - actionFuncName := getFuncName(action) - - for _, command := range app.Commands { - flags := recursiveFindFlagsWithName(actionFuncName, command, parentCommand, foundName) - - for _, flag := range flags { - flag.Apply(flagSet) - } - } - -} - -func recursiveFindFlagsWithName(actionFuncName string, command cli.Command, parent string, foundName bool) []cli.Flag { - - if command.Action != nil { - if actionFuncName == getFuncName(command.Action) && foundName { - return command.Flags - } - } - - for _, subcommand := range command.Subcommands { - if !foundName { - foundName = strings.EqualFold(subcommand.Name, parent) - } - - found := recursiveFindFlagsWithName(actionFuncName, subcommand, parent, foundName) - if found != nil { - return found - } - } - return nil -} - -func getFuncName(i interface{}) string { - return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() -} diff --git a/core/internal/cltest/event_websocket_server.go b/core/internal/cltest/event_websocket_server.go deleted file mode 100644 index 64b3e90cd81..00000000000 --- a/core/internal/cltest/event_websocket_server.go +++ /dev/null @@ -1,154 +0,0 @@ -package cltest - -import ( - "net/http" - "net/http/httptest" - "net/url" - "sync" - "testing" - - "github.com/pkg/errors" - - "github.com/gorilla/websocket" -) - -// EventWebSocketServer is a web socket server designed specifically for testing -type EventWebSocketServer struct { - *httptest.Server - mutex *sync.RWMutex // shared mutex for safe access to arrays/maps. - t *testing.T - connections []*websocket.Conn - Connected chan struct{} - Disconnected chan struct{} - ReceivedText chan string - ReceivedBinary chan []byte - URL *url.URL -} - -// NewEventWebSocketServer returns a new EventWebSocketServer -func NewEventWebSocketServer(t *testing.T) (*EventWebSocketServer, func()) { - server := &EventWebSocketServer{ - mutex: &sync.RWMutex{}, - t: t, - Connected: make(chan struct{}, 1), // have buffer of one for easier assertions after the event - Disconnected: make(chan struct{}, 1), // have buffer of one for easier assertions after the event - ReceivedText: make(chan string, 100), - ReceivedBinary: make(chan []byte, 100), - } - - server.Server = httptest.NewServer(http.HandlerFunc(server.handler)) - u, err := url.Parse(server.Server.URL) - if err != nil { - t.Fatal("EventWebSocketServer: ", err) - } - u.Scheme = "ws" - server.URL = u - return server, func() { - server.Close() - } -} - -func (wss EventWebSocketServer) ConnectionsCount() int { - wss.mutex.RLock() - defer wss.mutex.RUnlock() - return len(wss.connections) -} - -// Broadcast sends a message to every web socket client connected to the EventWebSocketServer -func (wss *EventWebSocketServer) Broadcast(message string) error { - wss.mutex.RLock() - defer wss.mutex.RUnlock() - for _, connection := range wss.connections { - err := connection.WriteMessage(websocket.TextMessage, []byte(message)) - if err != nil { - return errors.Wrap(err, "error writing message to connection") - } - } - - return nil -} - -// WriteCloseMessage tells connected clients to disconnect. -// Useful to emulate that the websocket server is shutting down without -// actually shutting down. -// This overcomes httptest.Server's inability to restart on the same URL:port. -func (wss *EventWebSocketServer) WriteCloseMessage() { - wss.mutex.RLock() - for _, connection := range wss.connections { - err := connection.WriteMessage( - websocket.CloseMessage, - websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - if err != nil { - wss.t.Error(err) - } - } - wss.mutex.RUnlock() -} - -var ( - upgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { return true }, - } - - closeCodes = []int{websocket.CloseNormalClosure, websocket.CloseAbnormalClosure} -) - -func (wss *EventWebSocketServer) handler(w http.ResponseWriter, r *http.Request) { - var err error - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - wss.t.Fatal("EventWebSocketServer Upgrade: ", err) - } - - wss.addConnection(conn) - for { - messageType, payload, err := conn.ReadMessage() // we only read - if websocket.IsCloseError(err, closeCodes...) { - wss.removeConnection(conn) - return - } - if err != nil { - wss.t.Fatal("EventWebSocketServer ReadMessage: ", err) - } - - if messageType == websocket.TextMessage { - select { - case wss.ReceivedText <- string(payload): - default: - } - } else if messageType == websocket.BinaryMessage { - select { - case wss.ReceivedBinary <- payload: - default: - } - } else { - wss.t.Fatal("EventWebSocketServer UnsupportedMessageType: ", messageType) - } - } -} - -func (wss *EventWebSocketServer) addConnection(conn *websocket.Conn) { - wss.mutex.Lock() - wss.connections = append(wss.connections, conn) - wss.mutex.Unlock() - select { // broadcast connected event - case wss.Connected <- struct{}{}: - default: - } -} - -func (wss *EventWebSocketServer) removeConnection(conn *websocket.Conn) { - newc := []*websocket.Conn{} - wss.mutex.Lock() - for _, connection := range wss.connections { - if connection != conn { - newc = append(newc, connection) - } - } - wss.connections = newc - wss.mutex.Unlock() - select { // broadcast disconnected event - case wss.Disconnected <- struct{}{}: - default: - } -} diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index 741afe828f9..46014c4e04f 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -134,7 +134,7 @@ func EmptyCLIContext() *cli.Context { return cli.NewContext(nil, set, nil) } -func NewEthTx(t *testing.T, fromAddress common.Address) txmgr.Tx { +func NewEthTx(fromAddress common.Address) txmgr.Tx { return txmgr.Tx{ FromAddress: fromAddress, ToAddress: testutils.NewAddress(), @@ -156,7 +156,7 @@ func MustInsertUnconfirmedEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, nonc chainID = v } } - etx := NewEthTx(t, fromAddress) + etx := NewEthTx(fromAddress) etx.BroadcastAt = &broadcastAt etx.InitialBroadcastAt = &broadcastAt @@ -184,95 +184,9 @@ func MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t *testing.T, txStore return etx } -func MustInsertUnconfirmedEthTxWithAttemptState(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, txAttemptState txmgrtypes.TxAttemptState, opts ...interface{}) txmgr.Tx { - etx := MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress, opts...) - attempt := NewLegacyEthTxAttempt(t, etx.ID) - - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) - rlp := new(bytes.Buffer) - require.NoError(t, tx.EncodeRLP(rlp)) - attempt.SignedRawTx = rlp.Bytes() - - attempt.State = txAttemptState - require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) - require.NoError(t, err) - return etx -} - -func MustInsertUnconfirmedEthTxWithBroadcastDynamicFeeAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address, opts ...interface{}) txmgr.Tx { - etx := MustInsertUnconfirmedEthTx(t, txStore, nonce, fromAddress, opts...) - attempt := NewDynamicFeeEthTxAttempt(t, etx.ID) - - addr := testutils.NewAddress() - dtx := types.DynamicFeeTx{ - ChainID: big.NewInt(0), - Nonce: uint64(nonce), - GasTipCap: big.NewInt(1), - GasFeeCap: big.NewInt(1), - Gas: 242, - To: &addr, - Value: big.NewInt(342), - Data: []byte{2, 3, 4}, - } - tx := types.NewTx(&dtx) - rlp := new(bytes.Buffer) - require.NoError(t, tx.EncodeRLP(rlp)) - attempt.SignedRawTx = rlp.Bytes() - - attempt.State = txmgrtypes.TxAttemptBroadcast - require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) - require.NoError(t, err) - return etx -} - -func MustInsertUnconfirmedEthTxWithInsufficientEthAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, fromAddress common.Address) txmgr.Tx { - timeNow := time.Now() - etx := NewEthTx(t, fromAddress) - - etx.BroadcastAt = &timeNow - etx.InitialBroadcastAt = &timeNow - n := evmtypes.Nonce(nonce) - etx.Sequence = &n - etx.State = txmgrcommon.TxUnconfirmed - require.NoError(t, txStore.InsertTx(&etx)) - attempt := NewLegacyEthTxAttempt(t, etx.ID) - - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) - rlp := new(bytes.Buffer) - require.NoError(t, tx.EncodeRLP(rlp)) - attempt.SignedRawTx = rlp.Bytes() - - attempt.State = txmgrtypes.TxAttemptInsufficientFunds - require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) - require.NoError(t, err) - return etx -} - -func MustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt( - t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, broadcastBeforeBlockNum int64, - broadcastAt time.Time, fromAddress common.Address) txmgr.Tx { - etx := NewEthTx(t, fromAddress) - - etx.BroadcastAt = &broadcastAt - etx.InitialBroadcastAt = &broadcastAt - n := evmtypes.Nonce(nonce) - etx.Sequence = &n - etx.State = txmgrcommon.TxConfirmedMissingReceipt - require.NoError(t, txStore.InsertTx(&etx)) - attempt := NewLegacyEthTxAttempt(t, etx.ID) - attempt.BroadcastBeforeBlockNum = &broadcastBeforeBlockNum - attempt.State = txmgrtypes.TxAttemptBroadcast - require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx.TxAttempts = append(etx.TxAttempts, attempt) - return etx -} - func MustInsertConfirmedEthTxWithLegacyAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce int64, broadcastBeforeBlockNum int64, fromAddress common.Address) txmgr.Tx { timeNow := time.Now() - etx := NewEthTx(t, fromAddress) + etx := NewEthTx(fromAddress) etx.BroadcastAt = &timeNow etx.InitialBroadcastAt = &timeNow @@ -288,91 +202,6 @@ func MustInsertConfirmedEthTxWithLegacyAttempt(t *testing.T, txStore txmgr.TestE return etx } -func MustInsertInProgressEthTxWithAttempt(t *testing.T, txStore txmgr.TestEvmTxStore, nonce evmtypes.Nonce, fromAddress common.Address) txmgr.Tx { - etx := NewEthTx(t, fromAddress) - - etx.Sequence = &nonce - etx.State = txmgrcommon.TxInProgress - require.NoError(t, txStore.InsertTx(&etx)) - attempt := NewLegacyEthTxAttempt(t, etx.ID) - tx := types.NewTransaction(uint64(nonce), testutils.NewAddress(), big.NewInt(142), 242, big.NewInt(342), []byte{1, 2, 3}) - rlp := new(bytes.Buffer) - require.NoError(t, tx.EncodeRLP(rlp)) - attempt.SignedRawTx = rlp.Bytes() - attempt.State = txmgrtypes.TxAttemptInProgress - require.NoError(t, txStore.InsertTxAttempt(&attempt)) - etx, err := txStore.FindTxWithAttempts(etx.ID) - require.NoError(t, err) - return etx -} - -func MustCreateUnstartedGeneratedTx(t testing.TB, txStore txmgr.EvmTxStore, fromAddress common.Address, chainID *big.Int, opts ...func(*txmgr.TxRequest)) (tx txmgr.Tx) { - txRequest := txmgr.TxRequest{ - FromAddress: fromAddress, - } - - // Apply the default options - WithDefaults()(&txRequest) - // Apply the optional parameters - for _, opt := range opts { - opt(&txRequest) - } - return MustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, chainID) -} - -func WithDefaults() func(*txmgr.TxRequest) { - return func(tx *txmgr.TxRequest) { - tx.ToAddress = testutils.NewAddress() - tx.EncodedPayload = []byte{1, 2, 3} - tx.Value = big.Int(assets.NewEthValue(142)) - tx.FeeLimit = uint32(1000000000) - tx.Strategy = txmgrcommon.NewSendEveryStrategy() - // Set default values for other fields if needed - } -} - -func EvmTxRequestWithStrategy(strategy txmgrtypes.TxStrategy) func(*txmgr.TxRequest) { - return func(tx *txmgr.TxRequest) { - tx.Strategy = strategy - } -} - -func EvmTxRequestWithChecker(checker txmgr.TransmitCheckerSpec) func(*txmgr.TxRequest) { - return func(tx *txmgr.TxRequest) { - tx.Checker = checker - } -} -func EvmTxRequestWithValue(value big.Int) func(*txmgr.TxRequest) { - return func(tx *txmgr.TxRequest) { - tx.Value = value - } -} - -func EvmTxRequestWithIdempotencyKey(idempotencyKey string) func(*txmgr.TxRequest) { - return func(tx *txmgr.TxRequest) { - tx.IdempotencyKey = &idempotencyKey - } -} - -func MustCreateUnstartedTx(t testing.TB, txStore txmgr.EvmTxStore, fromAddress common.Address, toAddress common.Address, encodedPayload []byte, gasLimit uint32, value big.Int, chainID *big.Int, opts ...interface{}) (tx txmgr.Tx) { - txRequest := txmgr.TxRequest{ - FromAddress: fromAddress, - ToAddress: toAddress, - EncodedPayload: encodedPayload, - Value: value, - FeeLimit: gasLimit, - Strategy: txmgrcommon.NewSendEveryStrategy(), - } - - return MustCreateUnstartedTxFromEvmTxRequest(t, txStore, txRequest, chainID) -} - -func MustCreateUnstartedTxFromEvmTxRequest(t testing.TB, txStore txmgr.EvmTxStore, txRequest txmgr.TxRequest, chainID *big.Int) (tx txmgr.Tx) { - tx, err := txStore.CreateTransaction(testutils.Context(t), txRequest, chainID) - require.NoError(t, err) - return tx -} - func NewLegacyEthTxAttempt(t *testing.T, etxID int64) txmgr.TxAttempt { gasPrice := assets.NewWeiI(1) return txmgr.TxAttempt{ @@ -406,72 +235,6 @@ func NewDynamicFeeEthTxAttempt(t *testing.T, etxID int64) txmgr.TxAttempt { } } -func NewEthReceipt(t *testing.T, blockNumber int64, blockHash common.Hash, txHash common.Hash, status uint64) txmgr.Receipt { - transactionIndex := uint(NewRandomPositiveInt64()) - - receipt := evmtypes.Receipt{ - BlockNumber: big.NewInt(blockNumber), - BlockHash: blockHash, - TxHash: txHash, - TransactionIndex: transactionIndex, - Status: status, - } - - r := txmgr.Receipt{ - BlockNumber: blockNumber, - BlockHash: blockHash, - TxHash: txHash, - TransactionIndex: transactionIndex, - Receipt: receipt, - } - return r -} - -func MustInsertEthReceipt(t *testing.T, txStore txmgr.TestEvmTxStore, blockNumber int64, blockHash common.Hash, txHash common.Hash) txmgr.Receipt { - r := NewEthReceipt(t, blockNumber, blockHash, txHash, 0x1) - id, err := txStore.InsertReceipt(&r.Receipt) - require.NoError(t, err) - r.ID = id - return r -} - -func MustInsertRevertedEthReceipt(t *testing.T, txStore txmgr.TestEvmTxStore, blockNumber int64, blockHash common.Hash, txHash common.Hash) txmgr.Receipt { - r := NewEthReceipt(t, blockNumber, blockHash, txHash, 0x0) - id, err := txStore.InsertReceipt(&r.Receipt) - require.NoError(t, err) - r.ID = id - return r -} - -// Inserts into evm.receipts but does not update evm.txes or evm.tx_attempts -func MustInsertConfirmedEthTxWithReceipt(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address, nonce, blockNum int64) (etx txmgr.Tx) { - etx = MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, nonce, blockNum, fromAddress) - MustInsertEthReceipt(t, txStore, blockNum, utils.NewHash(), etx.TxAttempts[0].Hash) - return etx -} - -func MustInsertConfirmedEthTxBySaveFetchedReceipts(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address, nonce int64, blockNum int64, chainID big.Int) (etx txmgr.Tx) { - etx = MustInsertConfirmedEthTxWithLegacyAttempt(t, txStore, nonce, blockNum, fromAddress) - receipt := evmtypes.Receipt{ - TxHash: etx.TxAttempts[0].Hash, - BlockHash: utils.NewHash(), - BlockNumber: big.NewInt(nonce), - TransactionIndex: uint(1), - } - err := txStore.SaveFetchedReceipts(testutils.Context(t), []*evmtypes.Receipt{&receipt}, &chainID) - require.NoError(t, err) - return etx -} - -func MustInsertFatalErrorEthTx(t *testing.T, txStore txmgr.TestEvmTxStore, fromAddress common.Address) txmgr.Tx { - etx := NewEthTx(t, fromAddress) - etx.Error = null.StringFrom("something exploded") - etx.State = txmgrcommon.TxFatalError - - require.NoError(t, txStore.InsertTx(&etx)) - return etx -} - type RandomKey struct { Nonce int64 Disabled bool @@ -500,7 +263,8 @@ func (r RandomKey) MustInsert(t testing.TB, keystore keystore.Eth) (ethkey.KeyV2 func (r RandomKey) MustInsertWithState(t testing.TB, keystore keystore.Eth) (ethkey.State, common.Address) { k, address := r.MustInsert(t, keystore) - state := MustGetStateForKey(t, keystore, k) + state, err := keystore.GetStateForKey(k) + require.NoError(t, err) return state, address } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index 7749b173c55..35fd3a31ff8 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -429,7 +429,7 @@ func TestIntegration_DirectRequest(t *testing.T) { pipelineRuns := cltest.WaitForPipelineComplete(t, 0, j.ID, 1, 14, app.JobORM(), testutils.WaitTimeout(t)/2, time.Second) pipelineRun := pipelineRuns[0] - cltest.AssertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) + assertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) assertPricesUint256(t, big.NewInt(61464), big.NewInt(50707), big.NewInt(6381886), operatorContracts.multiWord) nameAndExternalJobID = uuid.New() @@ -454,7 +454,7 @@ func TestIntegration_DirectRequest(t *testing.T) { pipelineRuns = cltest.WaitForPipelineComplete(t, 0, jobSingleWord.ID, 1, 8, app.JobORM(), testutils.WaitTimeout(t), time.Second) pipelineRun = pipelineRuns[0] - cltest.AssertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) + assertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) v, err := operatorContracts.singleWord.CurrentPriceInt(nil) require.NoError(t, err) assert.Equal(t, big.NewInt(61464), v) @@ -535,7 +535,7 @@ observationSource = """ // The run should have succeeded but with the receipt detailing the reverted transaction pipelineRun := pipelineRuns[0] - cltest.AssertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) + assertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) outputs := pipelineRun.Outputs.Val.([]interface{}) require.Len(t, outputs, 1) @@ -581,7 +581,7 @@ observationSource = """ // The run should have failed as a revert pipelineRun := pipelineRuns[0] - cltest.AssertPipelineTaskRunsErrored(t, pipelineRun.PipelineTaskRuns) + assertPipelineTaskRunsErrored(t, pipelineRun.PipelineTaskRuns) }) t.Run("with FailOnRevert disabled, run succeeds with output being reverted receipt", func(t *testing.T) { @@ -619,7 +619,7 @@ observationSource = """ // The run should have succeeded but with the receipt detailing the reverted transaction pipelineRun := pipelineRuns[0] - cltest.AssertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) + assertPipelineTaskRunsSuccessful(t, pipelineRun.PipelineTaskRuns) outputs := pipelineRun.Outputs.Val.([]interface{}) require.Len(t, outputs, 1) @@ -1454,3 +1454,17 @@ func assertPricesUint256(t *testing.T, usd, eur, jpy *big.Int, consumer *multiwo } func ptr[T any](v T) *T { return &v } + +func assertPipelineTaskRunsSuccessful(t testing.TB, runs []pipeline.TaskRun) { + t.Helper() + for i, run := range runs { + require.True(t, run.Error.IsZero(), fmt.Sprintf("pipeline.Task run failed (idx: %v, dotID: %v, error: '%v')", i, run.GetDotID(), run.Error.ValueOrZero())) + } +} + +func assertPipelineTaskRunsErrored(t testing.TB, runs []pipeline.TaskRun) { + t.Helper() + for i, run := range runs { + require.False(t, run.Error.IsZero(), fmt.Sprintf("expected pipeline.Task run to have failed, but it succeeded (idx: %v, dotID: %v, output: '%v')", i, run.GetDotID(), run.Output)) + } +} diff --git a/core/services/ocrcommon/discoverer_database_test.go b/core/services/ocrcommon/discoverer_database_test.go index 2300316092d..ff1a931b017 100644 --- a/core/services/ocrcommon/discoverer_database_test.go +++ b/core/services/ocrcommon/discoverer_database_test.go @@ -1,23 +1,24 @@ package ocrcommon_test import ( + "crypto/rand" "testing" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" - + cryptop2p "github.com/libp2p/go-libp2p-core/crypto" + p2ppeer "github.com/libp2p/go-libp2p-core/peer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" + "github.com/smartcontractkit/chainlink/v2/core/services/ocrcommon" ) func Test_DiscovererDatabase(t *testing.T) { db := pgtest.NewSqlDB(t) - localPeerID1 := cltest.MustRandomP2PPeerID(t) - localPeerID2 := cltest.MustRandomP2PPeerID(t) + localPeerID1 := mustRandomP2PPeerID(t) + localPeerID2 := mustRandomP2PPeerID(t) dd1 := ocrcommon.NewDiscovererDatabase(db, localPeerID1) dd2 := ocrcommon.NewDiscovererDatabase(db, localPeerID2) @@ -81,3 +82,11 @@ func Test_DiscovererDatabase(t *testing.T) { }) } + +func mustRandomP2PPeerID(t *testing.T) p2ppeer.ID { + p2pPrivkey, _, err := cryptop2p.GenerateEd25519Key(rand.Reader) + require.NoError(t, err) + id, err := p2ppeer.IDFromPrivateKey(p2pPrivkey) + require.NoError(t, err) + return id +} diff --git a/core/services/pg/event_broadcaster_test.go b/core/services/pg/event_broadcaster_test.go index a82e26e0589..41dcbb0176f 100644 --- a/core/services/pg/event_broadcaster_test.go +++ b/core/services/pg/event_broadcaster_test.go @@ -5,20 +5,20 @@ import ( "testing" "time" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/pg" - + "github.com/google/uuid" "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) func TestEventBroadcaster(t *testing.T) { config, _ := heavyweight.FullTestDBNoFixturesV2(t, nil) - eventBroadcaster := cltest.NewEventBroadcaster(t, config.Database().URL()) + eventBroadcaster := pg.NewEventBroadcaster(config.Database().URL(), 0, 0, logger.TestLogger(t), uuid.New()) require.NoError(t, eventBroadcaster.Start(testutils.Context(t))) t.Cleanup(func() { require.NoError(t, eventBroadcaster.Close()) }) From 47604e288446c10f7ab5a012b6619b7436620a74 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Mon, 27 Nov 2023 10:58:28 -0600 Subject: [PATCH 201/271] bump various deps (#11357) --- core/scripts/go.mod | 71 ++++++------ core/scripts/go.sum | 226 +++++++++++++++++++++++--------------- go.mod | 69 ++++++------ go.sum | 228 ++++++++++++++++++++++++--------------- integration-tests/go.mod | 70 ++++++------ integration-tests/go.sum | 160 +++++++++++++-------------- 6 files changed, 468 insertions(+), 356 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index e9fa432d4cd..1a0775bb837 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -8,11 +8,11 @@ replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( github.com/ava-labs/coreth v0.12.1 github.com/avast/retry-go v3.0.0+incompatible - github.com/docker/docker v24.0.6+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 - github.com/google/go-cmp v0.5.9 - github.com/google/uuid v1.3.1 + github.com/google/go-cmp v0.6.0 + github.com/google/uuid v1.4.0 github.com/jmoiron/sqlx v1.3.5 github.com/joho/godotenv v1.4.0 github.com/manyminds/api2go v0.0.0-20171030193247-e7b693844a6f @@ -56,7 +56,7 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/ava-labs/avalanchego v1.10.1 // indirect - github.com/avast/retry-go/v4 v4.5.0 // indirect + github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect @@ -103,7 +103,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/esote/minmaxheap v1.0.0 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fxamacker/cbor/v2 v2.5.0 // indirect @@ -125,21 +125,21 @@ require ( github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/webauthn v0.8.6 // indirect + github.com/go-webauthn/webauthn v0.9.1 // indirect github.com/go-webauthn/x v0.1.4 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang-jwt/jwt/v5 v5.1.0 // indirect + github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -151,9 +151,9 @@ require ( github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect github.com/gorilla/context v1.1.1 // indirect - github.com/gorilla/securecookie v1.1.1 // indirect - github.com/gorilla/sessions v1.2.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.2.2 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/grafana/pyroscope-go v1.0.4 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.4 // indirect github.com/graph-gophers/dataloader v5.0.0+incompatible // indirect @@ -201,7 +201,7 @@ require ( github.com/jmhodges/levigo v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/koron/go-ssdp v0.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -251,7 +251,7 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect @@ -285,7 +285,7 @@ require ( github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // 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.15.1 // indirect + github.com/pressly/goose/v3 v3.16.0 // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect @@ -299,8 +299,9 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/scylladb/go-reflectx v1.0.1 // indirect + github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 // indirect @@ -344,33 +345,33 @@ require ( go.dedis.ch/fixbuf v1.0.3 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/sdk v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/sdk v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.15.0 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect - google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/grpc v1.58.3 // indirect + google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/guregu/null.v2 v2.1.2 // indirect gopkg.in/guregu/null.v4 v4.0.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 3d9962474b9..ef7aa735bb9 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -18,23 +18,23 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= +cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.22.0 h1:cB8R6FtUtT1TYGl5R3xuxnW6OUIc/DrT2aiR16TTG7Y= -cloud.google.com/go/compute v1.22.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= +cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.4 h1:K6n/GZHFTtEoKT5aUG3l9diPi0VduZNQ1PfdnpkkIFk= +cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -87,6 +87,10 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= +github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0 h1:G0hTKyO8fXXR1bGnZ0DY3vTG01xYfOGW76zgjg5tmC4= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0/go.mod h1:kXt1SRq0PIRa6aKZD7TnFnY9PQKmc2b13sHtOYcK6cQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/CosmWasm/wasmd v0.40.1 h1:LxbO78t/6S8TkeQlUrJ0m5O87HtAwLx4RGHq3rdrOEU= @@ -133,6 +137,8 @@ github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKS github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4= github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= @@ -148,8 +154,8 @@ github.com/ava-labs/coreth v0.12.1 h1:EWSkFGHGVUxmu1pnSK/2pdcxaAVHbGspHqO3Ag+i7s github.com/ava-labs/coreth v0.12.1/go.mod h1:/5x54QlIKjlPebkdzTA5ic9wXdejbWOnQosztkv9jxo= 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.0 h1:QoRAZZ90cj5oni2Lsgl2GW8mNTnUCnmpx/iKpwVisHg= -github.com/avast/retry-go/v4 v4.5.0/go.mod h1:7hLEXp0oku2Nir2xBAsg0PTphp9z71bN5Aq1fboC3+I= +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= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.302 h1:ST3ko6GrJKn3Xi+nAvxjG3uk/V1pW8KC52WLeIxqqNk= @@ -246,8 +252,8 @@ github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0 github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= +github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -335,10 +341,12 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= +github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= -github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -349,6 +357,10 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/go-sysinfo v1.11.1 h1:g9mwl05njS4r69TisC+vwHWTSKywZFYYUu3so3T/Lao= +github.com/elastic/go-sysinfo v1.11.1/go.mod h1:6KQb31j0QeWBDF88jIdWSxE8cwoOB9tO4Y4osN7Q70E= +github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= +github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -373,8 +385,8 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -434,6 +446,10 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclK github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= +github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -452,8 +468,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= @@ -478,8 +494,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= -github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo= +github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI= github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -503,11 +519,11 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= +github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -565,8 +581,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -601,11 +618,13 @@ github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYa github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -621,14 +640,14 @@ github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= github.com/grafana/pyroscope-go v1.0.4/go.mod h1:0d7ftwSMBV/Awm7CCiYmHQEG8Y44Ma3YSjt+nWcWztY= github.com/grafana/pyroscope-go/godeltaprof v0.1.4 h1:mDsJ3ngul7UfrHibGQpV66PbZ3q1T8glz/tK3bQKKEk= @@ -732,6 +751,8 @@ github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3 github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= @@ -832,10 +853,15 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= +github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -860,9 +886,13 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -891,8 +921,8 @@ github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= @@ -1168,8 +1198,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -1326,25 +1356,29 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= -github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= +github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= +github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s= +github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -1354,6 +1388,8 @@ github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1368,8 +1404,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.15.1 h1:dKaJ1SdLvS/+HtS8PzFT0KBEtICC1jewLXM+b3emlv8= -github.com/pressly/goose/v3 v3.15.1/go.mod h1:0E3Yg/+EwYzO6Rz2P98MlClFgIcoujbVRs575yi3iIM= +github.com/pressly/goose/v3 v3.16.0 h1:xMJUsZdHLqSnCqESyKSqEfcYVYsUuup1nrOhaEFftQg= +github.com/pressly/goose/v3 v3.16.0/go.mod h1:JwdKVnmCRhnF6XLQs2mHEQtucFD49cQBdRM4UiwkxsM= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -1443,11 +1479,15 @@ github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtm github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= +github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= +github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1605,6 +1645,8 @@ github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7E github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= +github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= @@ -1617,13 +1659,21 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7V github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd h1:dzWP1Lu+A40W883dK/Mr3xyDSM/2MggS8GtHT0qgAnE= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= +github.com/ydb-platform/ydb-go-sdk/v3 v3.54.2 h1:E0yUuuX7UmPxXm92+yQCjMveLFO3zfvYFIJVuAqsVRA= +github.com/ydb-platform/ydb-go-sdk/v3 v3.54.2/go.mod h1:fjBLQ2TdQNl4bMjuWl9adoTGBypwUTPoGC+EqYqiIcU= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= @@ -1662,20 +1712,20 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1687,8 +1737,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1748,8 +1798,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1789,8 +1839,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1850,8 +1900,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1877,8 +1927,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1971,17 +2021,17 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1993,8 +2043,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2070,8 +2120,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2159,12 +2209,12 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 h1:+VoAg+OKmWaommL56xmZSE2sUK8A7m6SUO7X89F2tbw= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753/go.mod h1:iqkVr8IRpZ53gx1dEnWlCUIEwDWqWARWrbzpasaTNYM= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 h1:XUODHrpzJEUeWmVo/jfNTLj0YyVveOo28oE6vkFbkO4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -2189,8 +2239,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2265,22 +2315,24 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= +howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= -modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= -modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= +modernc.org/libc v1.32.0 h1:yXatHTrACp3WaKNRCoZwUK7qj5V8ep1XyY0ka4oYcNc= +modernc.org/libc v1.32.0/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.26.0 h1:SocQdLRSYlA8W99V8YH0NES75thx19d9sB/aFc4R8Lw= -modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= +modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= +modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/go.mod b/go.mod index e6bb7347ffd..e396ba5ede9 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,14 @@ require ( github.com/Depado/ginprom v1.7.11 github.com/Masterminds/semver/v3 v3.2.1 github.com/Masterminds/sprig/v3 v3.2.3 - github.com/avast/retry-go/v4 v4.5.0 + github.com/avast/retry-go/v4 v4.5.1 github.com/btcsuite/btcd v0.23.4 github.com/cometbft/cometbft v0.37.2 github.com/cosmos/cosmos-sdk v0.47.4 github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e github.com/esote/minmaxheap v1.0.0 github.com/ethereum/go-ethereum v1.12.0 - github.com/fatih/color v1.15.0 + github.com/fatih/color v1.16.0 github.com/fxamacker/cbor/v2 v2.5.0 github.com/gagliardetto/solana-go v1.4.1-0.20220428092759-5250b4abbb27 github.com/getsentry/sentry-go v0.19.0 @@ -22,12 +22,12 @@ require ( github.com/gin-contrib/sessions v0.0.5 github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 - github.com/go-webauthn/webauthn v0.8.6 + github.com/go-webauthn/webauthn v0.9.1 github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 - github.com/google/uuid v1.3.1 - github.com/gorilla/securecookie v1.1.1 - github.com/gorilla/sessions v1.2.1 - github.com/gorilla/websocket v1.5.0 + github.com/google/uuid v1.4.0 + github.com/gorilla/securecookie v1.1.2 + github.com/gorilla/sessions v1.2.2 + github.com/gorilla/websocket v1.5.1 github.com/grafana/pyroscope-go v1.0.4 github.com/graph-gophers/dataloader v5.0.0+incompatible github.com/graph-gophers/graphql-go v1.3.0 @@ -49,12 +49,12 @@ require ( github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multiaddr v0.3.3 github.com/olekukonko/tablewriter v0.0.5 - github.com/onsi/gomega v1.27.8 + github.com/onsi/gomega v1.30.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pelletier/go-toml v1.9.5 github.com/pelletier/go-toml/v2 v2.1.0 github.com/pkg/errors v0.9.1 - github.com/pressly/goose/v3 v3.15.1 + github.com/pressly/goose/v3 v3.16.0 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.5.0 github.com/prometheus/common v0.45.0 @@ -62,7 +62,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/go-internal v1.11.0 github.com/scylladb/go-reflectx v1.0.1 - github.com/shirou/gopsutil/v3 v3.23.9 + github.com/shirou/gopsutil/v3 v3.23.10 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 @@ -88,15 +88,15 @@ require ( go.dedis.ch/kyber/v3 v3.1.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.15.0 golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 - golang.org/x/sync v0.4.0 - golang.org/x/term v0.13.0 - golang.org/x/text v0.13.0 + golang.org/x/sync v0.5.0 + golang.org/x/term v0.14.0 + golang.org/x/text v0.14.0 golang.org/x/time v0.3.0 - golang.org/x/tools v0.14.0 + golang.org/x/tools v0.15.0 gonum.org/v1/gonum v0.14.0 - google.golang.org/grpc v1.58.3 + google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 gopkg.in/guregu/null.v2 v2.1.2 gopkg.in/guregu/null.v4 v4.0.0 @@ -174,7 +174,7 @@ require ( github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -187,13 +187,13 @@ require ( github.com/gofrs/flock v0.8.1 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang-jwt/jwt/v5 v5.1.0 // indirect + github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -239,7 +239,7 @@ require ( github.com/jmhodges/levigo v1.0.0 // indirect github.com/jmoiron/sqlx v1.3.5 github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/koron/go-ssdp v0.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -284,7 +284,7 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect @@ -318,6 +318,7 @@ require ( github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect + github.com/sethvargo/go-retry v0.2.4 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect @@ -352,21 +353,21 @@ require ( go.dedis.ch/protobuf v1.0.11 // indirect go.etcd.io/bbolt v1.3.7 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/sdk v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/sdk v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 24d662390d0..0c72ac7e639 100644 --- a/go.sum +++ b/go.sum @@ -18,23 +18,23 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= +cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.22.0 h1:cB8R6FtUtT1TYGl5R3xuxnW6OUIc/DrT2aiR16TTG7Y= -cloud.google.com/go/compute v1.22.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= +cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.4 h1:K6n/GZHFTtEoKT5aUG3l9diPi0VduZNQ1PfdnpkkIFk= +cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -87,6 +87,10 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0= +github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0 h1:G0hTKyO8fXXR1bGnZ0DY3vTG01xYfOGW76zgjg5tmC4= +github.com/ClickHouse/clickhouse-go/v2 v2.15.0/go.mod h1:kXt1SRq0PIRa6aKZD7TnFnY9PQKmc2b13sHtOYcK6cQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/CosmWasm/wasmd v0.40.1 h1:LxbO78t/6S8TkeQlUrJ0m5O87HtAwLx4RGHq3rdrOEU= @@ -138,6 +142,8 @@ github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKS github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4= github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw= @@ -147,8 +153,8 @@ 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/v4 v4.5.0 h1:QoRAZZ90cj5oni2Lsgl2GW8mNTnUCnmpx/iKpwVisHg= -github.com/avast/retry-go/v4 v4.5.0/go.mod h1:7hLEXp0oku2Nir2xBAsg0PTphp9z71bN5Aq1fboC3+I= +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= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.302 h1:ST3ko6GrJKn3Xi+nAvxjG3uk/V1pW8KC52WLeIxqqNk= @@ -245,8 +251,8 @@ github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0 github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/confio/ics23/go v0.9.0 h1:cWs+wdbS2KRPZezoaaj+qBleXgUk5WOQFMP3CQFGTr4= github.com/confio/ics23/go v0.9.0/go.mod h1:4LPZ2NYqnYIVRklaozjNR1FScgDJ2s5Xrp+e/mYVRak= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= +github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -332,8 +338,12 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= +github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -344,6 +354,10 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/go-sysinfo v1.11.1 h1:g9mwl05njS4r69TisC+vwHWTSKywZFYYUu3so3T/Lao= +github.com/elastic/go-sysinfo v1.11.1/go.mod h1:6KQb31j0QeWBDF88jIdWSxE8cwoOB9tO4Y4osN7Q70E= +github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= +github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -368,8 +382,8 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -429,6 +443,10 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclK github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI= +github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -447,8 +465,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= @@ -475,8 +493,8 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= -github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo= +github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI= github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -500,11 +518,11 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= +github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -562,8 +580,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -598,11 +617,13 @@ github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYa github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -618,14 +639,14 @@ github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= github.com/grafana/pyroscope-go v1.0.4/go.mod h1:0d7ftwSMBV/Awm7CCiYmHQEG8Y44Ma3YSjt+nWcWztY= github.com/grafana/pyroscope-go/godeltaprof v0.1.4 h1:mDsJ3ngul7UfrHibGQpV66PbZ3q1T8glz/tK3bQKKEk= @@ -833,10 +854,15 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= +github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -861,9 +887,13 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= +github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -892,8 +922,8 @@ github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= @@ -1169,8 +1199,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -1222,6 +1252,8 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1319,8 +1351,8 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1329,25 +1361,29 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= -github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= +github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= +github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= +github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s= +github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -1357,6 +1393,8 @@ github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1371,8 +1409,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.15.1 h1:dKaJ1SdLvS/+HtS8PzFT0KBEtICC1jewLXM+b3emlv8= -github.com/pressly/goose/v3 v3.15.1/go.mod h1:0E3Yg/+EwYzO6Rz2P98MlClFgIcoujbVRs575yi3iIM= +github.com/pressly/goose/v3 v3.16.0 h1:xMJUsZdHLqSnCqESyKSqEfcYVYsUuup1nrOhaEFftQg= +github.com/pressly/goose/v3 v3.16.0/go.mod h1:JwdKVnmCRhnF6XLQs2mHEQtucFD49cQBdRM4UiwkxsM= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -1444,11 +1482,15 @@ github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtm github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= +github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= +github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -1607,6 +1649,8 @@ github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7E github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw= +github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= @@ -1619,13 +1663,21 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7V github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd h1:dzWP1Lu+A40W883dK/Mr3xyDSM/2MggS8GtHT0qgAnE= +github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= +github.com/ydb-platform/ydb-go-sdk/v3 v3.54.2 h1:E0yUuuX7UmPxXm92+yQCjMveLFO3zfvYFIJVuAqsVRA= +github.com/ydb-platform/ydb-go-sdk/v3 v3.54.2/go.mod h1:fjBLQ2TdQNl4bMjuWl9adoTGBypwUTPoGC+EqYqiIcU= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= @@ -1664,20 +1716,20 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1689,8 +1741,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -1751,8 +1803,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1792,8 +1844,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1854,8 +1906,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1881,8 +1933,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1975,9 +2027,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1985,8 +2037,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1999,8 +2051,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2075,8 +2127,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2164,12 +2216,12 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 h1:+VoAg+OKmWaommL56xmZSE2sUK8A7m6SUO7X89F2tbw= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753/go.mod h1:iqkVr8IRpZ53gx1dEnWlCUIEwDWqWARWrbzpasaTNYM= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 h1:XUODHrpzJEUeWmVo/jfNTLj0YyVveOo28oE6vkFbkO4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -2194,8 +2246,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2268,22 +2320,24 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= +howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= -modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= -modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= +modernc.org/libc v1.32.0 h1:yXatHTrACp3WaKNRCoZwUK7qj5V8ep1XyY0ka4oYcNc= +modernc.org/libc v1.32.0/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.26.0 h1:SocQdLRSYlA8W99V8YH0NES75thx19d9sB/aFc4R8Lw= -modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= +modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= +modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index d0ca2346636..fd167f81a9b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -11,13 +11,13 @@ require ( github.com/cli/go-gh/v2 v2.0.0 github.com/ethereum/go-ethereum v1.12.0 github.com/go-resty/resty/v2 v2.7.0 - github.com/google/go-cmp v0.5.9 - github.com/google/uuid v1.3.1 + github.com/google/go-cmp v0.6.0 + github.com/google/uuid v1.4.0 github.com/jmoiron/sqlx v1.3.5 github.com/kelseyhightower/envconfig v1.4.0 github.com/lib/pq v1.10.9 github.com/manifoldco/promptui v0.9.0 - github.com/onsi/gomega v1.27.8 + github.com/onsi/gomega v1.30.0 github.com/pelletier/go-toml/v2 v2.1.0 github.com/rs/zerolog v1.30.0 github.com/scylladb/go-reflectx v1.0.1 @@ -37,7 +37,7 @@ require ( github.com/umbracle/ethgo v0.1.3 go.dedis.ch/kyber/v3 v3.1.0 go.uber.org/zap v1.26.0 - golang.org/x/sync v0.4.0 + golang.org/x/sync v0.5.0 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -73,7 +73,7 @@ require ( github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/avast/retry-go/v4 v4.5.0 // indirect + github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aws/aws-sdk-go v1.44.302 // indirect github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect github.com/aws/jsii-runtime-go v1.75.0 // indirect @@ -140,7 +140,7 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/fatih/camelcase v1.0.0 // indirect - github.com/fatih/color v1.15.0 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -161,7 +161,7 @@ require ( github.com/go-kit/log v0.2.1 // indirect github.com/go-ldap/ldap/v3 v3.4.5 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/analysis v0.21.4 // indirect @@ -177,7 +177,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-stack/stack v1.8.1 // indirect - github.com/go-webauthn/webauthn v0.8.6 // indirect + github.com/go-webauthn/webauthn v0.9.1 // indirect github.com/go-webauthn/x v0.1.4 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect @@ -185,8 +185,8 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect github.com/gogo/status v1.1.1 // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect - github.com/golang/glog v1.1.0 // indirect + github.com/golang-jwt/jwt/v5 v5.1.0 // indirect + github.com/golang/glog v1.1.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect @@ -200,9 +200,9 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/securecookie v1.1.1 // indirect - github.com/gorilla/sessions v1.2.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.2.2 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 // indirect @@ -265,7 +265,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/koron/go-ssdp v0.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -315,7 +315,7 @@ require ( github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.55 // indirect @@ -385,7 +385,7 @@ require ( github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sercand/kuberesolver/v4 v4.0.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect @@ -438,38 +438,38 @@ require ( go.etcd.io/etcd/client/v3 v3.5.7 // indirect go.mongodb.org/mongo-driver v1.12.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 // indirect - go.opentelemetry.io/otel/metric v1.19.0 // indirect - go.opentelemetry.io/otel/sdk v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/sdk v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/goleak v1.2.1 // indirect + go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.15.0 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect golang.org/x/oauth2 v0.12.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 // indirect - google.golang.org/grpc v1.58.3 // indirect + google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/guregu/null.v2 v2.1.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 21be8c1f480..0ee9d7848a9 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -37,8 +37,8 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= -cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= +cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -149,8 +149,8 @@ cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARy cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk= -cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= +cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -272,8 +272,8 @@ cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQE cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/iam v1.1.4 h1:K6n/GZHFTtEoKT5aUG3l9diPi0VduZNQ1PfdnpkkIFk= +cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= @@ -666,8 +666,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/avast/retry-go/v4 v4.5.0 h1:QoRAZZ90cj5oni2Lsgl2GW8mNTnUCnmpx/iKpwVisHg= -github.com/avast/retry-go/v4 v4.5.0/go.mod h1:7hLEXp0oku2Nir2xBAsg0PTphp9z71bN5Aq1fboC3+I= +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= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -972,8 +972,8 @@ github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwo github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= @@ -1064,8 +1064,8 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= @@ -1136,8 +1136,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-webauthn/webauthn v0.8.6 h1:bKMtL1qzd2WTFkf1mFTVbreYrwn7dsYmEPjTq6QN90E= -github.com/go-webauthn/webauthn v0.8.6/go.mod h1:emwVLMCI5yx9evTTvr0r+aOZCdWJqMfbRhF0MufyUog= +github.com/go-webauthn/webauthn v0.9.1 h1:KuZjvUX9JTuFjB2n7kZhM6n76BClLUFbFM8SLKnrXpo= +github.com/go-webauthn/webauthn v0.9.1/go.mod h1:m315kRGbUljOytw8b9FGWG9QzErjI5v02pNFCF3lwpI= github.com/go-webauthn/x v0.1.4 h1:sGmIFhcY70l6k7JIDfnjVBiAAFEssga5lXIUXe0GtAs= github.com/go-webauthn/x v0.1.4/go.mod h1:75Ug0oK6KYpANh5hDOanfDI+dvPWHk788naJVG/37H8= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= @@ -1195,13 +1195,14 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= +github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1266,8 +1267,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -1315,8 +1317,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -1348,14 +1350,14 @@ github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= @@ -1678,8 +1680,8 @@ github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1975,8 +1977,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -2157,8 +2159,8 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -2167,8 +2169,8 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= @@ -2231,8 +2233,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pressly/goose/v3 v3.15.1 h1:dKaJ1SdLvS/+HtS8PzFT0KBEtICC1jewLXM+b3emlv8= -github.com/pressly/goose/v3 v3.15.1/go.mod h1:0E3Yg/+EwYzO6Rz2P98MlClFgIcoujbVRs575yi3iIM= +github.com/pressly/goose/v3 v3.16.0 h1:xMJUsZdHLqSnCqESyKSqEfcYVYsUuup1nrOhaEFftQg= +github.com/pressly/goose/v3 v3.16.0/go.mod h1:JwdKVnmCRhnF6XLQs2mHEQtucFD49cQBdRM4UiwkxsM= github.com/prometheus/alertmanager v0.25.1 h1:LGBNMspOfv8h7brb+LWj2wnwBCg2ZuuKWTh6CAVw2/Y= github.com/prometheus/alertmanager v0.25.1/go.mod h1:MEZ3rFVHqKZsw7IcNS/m4AWZeXThmJhumpiWR4eHU/w= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -2348,10 +2350,12 @@ github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYM github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec= +github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= +github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= @@ -2620,22 +2624,22 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= -go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= -go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= -go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -2653,8 +2657,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -2719,8 +2723,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2780,8 +2784,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2866,8 +2870,8 @@ golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2916,8 +2920,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3046,9 +3050,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3059,8 +3063,8 @@ golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3076,8 +3080,8 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3169,8 +3173,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3394,12 +3398,12 @@ google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753 h1:+VoAg+OKmWaommL56xmZSE2sUK8A7m6SUO7X89F2tbw= -google.golang.org/genproto v0.0.0-20230717213848-3f92550aa753/go.mod h1:iqkVr8IRpZ53gx1dEnWlCUIEwDWqWARWrbzpasaTNYM= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE= -google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753 h1:XUODHrpzJEUeWmVo/jfNTLj0YyVveOo28oE6vkFbkO4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230717213848-3f92550aa753/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -3444,8 +3448,8 @@ google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsA google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= From ab14bdc80e63b3dec61a2b0d8f983e5774f46581 Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Mon, 27 Nov 2023 12:33:27 -0500 Subject: [PATCH 202/271] Upkeep Balance Monitor (#11180) * write UpkeepBalanceMonWithBuffer * Update AutomationRegistryInterface2_0.sol Support getMinBalanceForUpkeep * reformat * remove modifier * add typeAndVersion check in constructor * restore AutomationRegistryInterface2_0.sol function * update import paths * update comments * cleanup * get rid of redundant REGISTRY variable * cleanup * rename * cleanup * remove target, min wait period, switch to min/target percentages * refactor getUnderfundedUpkeeps() to return top up amounts * refactor topUp() function * switch to max batch size * add max top up amount * add maxTopUpAmount * whitelist performUpkeep to forwarder * cleanup * rename * bring topUp() back * write initial test suite * fix solhint errors * update test for getUnderfundedUpkeeps() * add tests for owner only functions and events * add topUp and performUpkeep tests * rearrange functions on contract * add support for multiple registries * cleanup * change topUpAmounts to uint96[] * move pausable to topUp(); add length check to topUp() --------- Co-authored-by: De Clercq Wentzel <10665586+wentzeld@users.noreply.github.com> --- .../upkeeps/UpkeepBalanceMonitor.sol | 258 +++++++++++ .../automation/UpkeepBalanceMonitor.test.ts | 399 ++++++++++++++++++ 2 files changed, 657 insertions(+) create mode 100644 contracts/src/v0.8/automation/upkeeps/UpkeepBalanceMonitor.sol create mode 100644 contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts diff --git a/contracts/src/v0.8/automation/upkeeps/UpkeepBalanceMonitor.sol b/contracts/src/v0.8/automation/upkeeps/UpkeepBalanceMonitor.sol new file mode 100644 index 00000000000..dae17da7293 --- /dev/null +++ b/contracts/src/v0.8/automation/upkeeps/UpkeepBalanceMonitor.sol @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.19; + +import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol"; +import {IAutomationRegistryConsumer} from "../interfaces/IAutomationRegistryConsumer.sol"; +import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; +import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +/// @title The UpkeepBalanceMonitor contract +/// @notice A keeper-compatible contract that monitors and funds Chainlink Automation upkeeps. +contract UpkeepBalanceMonitor is ConfirmedOwner, Pausable { + using EnumerableSet for EnumerableSet.AddressSet; + + event ConfigSet(Config config); + event ForwarderSet(address forwarderAddress); + event FundsWithdrawn(uint256 amountWithdrawn, address payee); + event TopUpFailed(uint256 indexed upkeepId); + event TopUpSucceeded(uint256 indexed upkeepId, uint96 amount); + event WatchListSet(address registryAddress); + + error InvalidConfig(); + error InvalidTopUpData(); + error OnlyForwarderOrOwner(); + + /// @member maxBatchSize is the maximum number of upkeeps to fund in a single transaction + /// @member minPercentage is the percentage of the upkeep's minBalance at which top-up occurs + /// @member targetPercentage is the percentage of the upkeep's minBalance to top-up to + /// @member maxTopUpAmount is the maximum amount of LINK to top-up an upkeep with + struct Config { + uint8 maxBatchSize; + uint24 minPercentage; + uint24 targetPercentage; + uint96 maxTopUpAmount; + } + + // ================================================================ + // | STORAGE | + // ================================================================ + + LinkTokenInterface private immutable LINK_TOKEN; + + mapping(address => uint256[]) s_registryWatchLists; + EnumerableSet.AddressSet s_registries; + Config private s_config; + address private s_forwarderAddress; + + // ================================================================ + // | CONSTRUCTOR | + // ================================================================ + + /// @param linkToken the Link token address + /// @param config the initial config for the contract + constructor(LinkTokenInterface linkToken, Config memory config) ConfirmedOwner(msg.sender) { + require(address(linkToken) != address(0)); + LINK_TOKEN = linkToken; + setConfig(config); + } + + // ================================================================ + // | CORE FUNCTIONALITY | + // ================================================================ + + /// @notice Gets a list of upkeeps that are underfunded + /// @return needsFunding list of underfunded upkeepIDs + /// @return registryAddresses list of registries that the upkeepIDs belong to + /// @return topUpAmounts amount to top up each upkeep + function getUnderfundedUpkeeps() public view returns (uint256[] memory, address[] memory, uint96[] memory) { + Config memory config = s_config; + uint256[] memory needsFunding = new uint256[](config.maxBatchSize); + address[] memory registryAddresses = new address[](config.maxBatchSize); + uint96[] memory topUpAmounts = new uint96[](config.maxBatchSize); + uint256 availableFunds = LINK_TOKEN.balanceOf(address(this)); + uint256 count; + for (uint256 i = 0; i < s_registries.length(); i++) { + IAutomationRegistryConsumer registry = IAutomationRegistryConsumer(s_registries.at(i)); + for (uint256 j = 0; j < s_registryWatchLists[address(registry)].length; j++) { + uint256 upkeepID = s_registryWatchLists[address(registry)][j]; + uint96 upkeepBalance = registry.getBalance(upkeepID); + uint96 minBalance = registry.getMinBalance(upkeepID); + uint96 topUpThreshold = (minBalance * config.minPercentage) / 100; + uint96 topUpAmount = ((minBalance * config.targetPercentage) / 100) - upkeepBalance; + if (topUpAmount > config.maxTopUpAmount) { + topUpAmount = config.maxTopUpAmount; + } + if (upkeepBalance <= topUpThreshold && availableFunds >= topUpAmount) { + needsFunding[count] = upkeepID; + topUpAmounts[count] = topUpAmount; + registryAddresses[count] = address(registry); + count++; + availableFunds -= topUpAmount; + } + if (count == config.maxBatchSize) { + break; + } + } + if (count == config.maxBatchSize) { + break; + } + } + if (count < config.maxBatchSize) { + assembly { + mstore(needsFunding, count) + mstore(registryAddresses, count) + mstore(topUpAmounts, count) + } + } + return (needsFunding, registryAddresses, topUpAmounts); + } + + /// @notice Called by the keeper/owner to send funds to underfunded upkeeps + /// @param upkeepIDs the list of upkeep ids to fund + /// @param registryAddresses the list of registries that the upkeepIDs belong to + /// @param topUpAmounts the list of amounts to fund each upkeep with + /// @dev We explicitly choose not to verify that input upkeepIDs are included in the watchlist. We also + /// explicity permit any amount to be sent via topUpAmounts; it does not have to meet the criteria + /// specified in getUnderfundedUpkeeps(). Here, we are relying on the security of automation's OCR to + /// secure the output of getUnderfundedUpkeeps() as the input to topUp(), and we are treating the owner + /// as a privileged user that can perform arbitrary top-ups to any upkeepID. + function topUp( + uint256[] memory upkeepIDs, + address[] memory registryAddresses, + uint96[] memory topUpAmounts + ) public whenNotPaused { + if (msg.sender != address(s_forwarderAddress) && msg.sender != owner()) revert OnlyForwarderOrOwner(); + if (upkeepIDs.length != registryAddresses.length || upkeepIDs.length != topUpAmounts.length) + revert InvalidTopUpData(); + for (uint256 i = 0; i < upkeepIDs.length; i++) { + try LINK_TOKEN.transferAndCall(registryAddresses[i], topUpAmounts[i], abi.encode(upkeepIDs[i])) returns ( + bool success + ) { + if (success) { + emit TopUpSucceeded(upkeepIDs[i], topUpAmounts[i]); + continue; + } + } catch {} + emit TopUpFailed(upkeepIDs[i]); + } + } + + // ================================================================ + // | AUTOMATION COMPATIBLE | + // ================================================================ + + /// @notice Gets list of upkeeps ids that are underfunded and returns a keeper-compatible payload. + /// @return upkeepNeeded signals if upkeep is needed, performData is an abi encoded list of subscription ids that need funds + function checkUpkeep(bytes calldata) external view returns (bool upkeepNeeded, bytes memory performData) { + ( + uint256[] memory needsFunding, + address[] memory registryAddresses, + uint96[] memory topUpAmounts + ) = getUnderfundedUpkeeps(); + upkeepNeeded = needsFunding.length > 0; + if (upkeepNeeded) { + performData = abi.encode(needsFunding, registryAddresses, topUpAmounts); + } + return (upkeepNeeded, performData); + } + + /// @notice Called by the keeper to send funds to underfunded addresses. + /// @param performData the abi encoded list of addresses to fund + function performUpkeep(bytes calldata performData) external { + (uint256[] memory upkeepIDs, address[] memory registryAddresses, uint96[] memory topUpAmounts) = abi.decode( + performData, + (uint256[], address[], uint96[]) + ); + topUp(upkeepIDs, registryAddresses, topUpAmounts); + } + + // ================================================================ + // | ADMIN | + // ================================================================ + + /// @notice Withdraws the contract balance in LINK. + /// @param amount the amount of LINK (in juels) to withdraw + /// @param payee the address to pay + function withdraw(uint256 amount, address payee) external onlyOwner { + require(payee != address(0)); + LINK_TOKEN.transfer(payee, amount); + emit FundsWithdrawn(amount, payee); + } + + /// @notice Pause the contract, which prevents executing performUpkeep. + function pause() external onlyOwner { + _pause(); + } + + /// @notice Unpause the contract. + function unpause() external onlyOwner { + _unpause(); + } + + // ================================================================ + // | SETTERS | + // ================================================================ + + /// @notice Sets the list of upkeeps to watch + /// @param registryAddress the registry that this watchlist applies to + /// @param watchlist the list of UpkeepIDs to watch + function setWatchList(address registryAddress, uint256[] calldata watchlist) external onlyOwner { + if (watchlist.length == 0) { + s_registries.remove(registryAddress); + delete s_registryWatchLists[registryAddress]; + } else { + s_registries.add(registryAddress); + s_registryWatchLists[registryAddress] = watchlist; + } + emit WatchListSet(registryAddress); + } + + /// @notice Sets the contract config + /// @param config the new config + function setConfig(Config memory config) public onlyOwner { + if ( + config.maxBatchSize == 0 || + config.minPercentage < 100 || + config.targetPercentage <= config.minPercentage || + config.maxTopUpAmount == 0 + ) { + revert InvalidConfig(); + } + s_config = config; + emit ConfigSet(config); + } + + /// @notice Sets the upkeep's forwarder contract + /// @param forwarderAddress the new forwarder + /// @dev this should only need to be called once, after registering the contract with the registry + function setForwarder(address forwarderAddress) external onlyOwner { + s_forwarderAddress = forwarderAddress; + emit ForwarderSet(forwarderAddress); + } + + // ================================================================ + // | GETTERS | + // ================================================================ + + /// @notice Gets the list of upkeeps ids being monitored + function getWatchList() external view returns (address[] memory, uint256[][] memory) { + address[] memory registryAddresses = s_registries.values(); + uint256[][] memory upkeepIDs = new uint256[][](registryAddresses.length); + for (uint256 i = 0; i < registryAddresses.length; i++) { + upkeepIDs[i] = s_registryWatchLists[registryAddresses[i]]; + } + return (registryAddresses, upkeepIDs); + } + + /// @notice Gets the contract config + function getConfig() external view returns (Config memory) { + return s_config; + } + + /// @notice Gets the upkeep's forwarder contract + function getForwarder() external view returns (address) { + return s_forwarderAddress; + } +} diff --git a/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts b/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts new file mode 100644 index 00000000000..259a9c3b9f8 --- /dev/null +++ b/contracts/test/v0.8/automation/UpkeepBalanceMonitor.test.ts @@ -0,0 +1,399 @@ +import { ethers } from 'hardhat' +import { expect } from 'chai' +import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { randomAddress } from '../../test-helpers/helpers' +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers' +import { IKeeperRegistryMaster__factory as RegistryFactory } from '../../../typechain/factories/IKeeperRegistryMaster__factory' +import { IAutomationForwarder__factory as ForwarderFactory } from '../../../typechain/factories/IAutomationForwarder__factory' +import { UpkeepBalanceMonitor } from '../../../typechain/UpkeepBalanceMonitor' +import { LinkToken } from '../../../typechain/LinkToken' +import { BigNumber } from 'ethers' +import { + deployMockContract, + MockContract, +} from '@ethereum-waffle/mock-contract' + +let owner: SignerWithAddress +let stranger: SignerWithAddress +let registry: MockContract +let registry2: MockContract +let forwarder: MockContract +let linkToken: LinkToken +let upkeepBalanceMonitor: UpkeepBalanceMonitor + +const setup = async () => { + const accounts = await ethers.getSigners() + owner = accounts[0] + stranger = accounts[1] + + const ltFactory = await ethers.getContractFactory( + 'src/v0.4/LinkToken.sol:LinkToken', + owner, + ) + linkToken = (await ltFactory.deploy()) as LinkToken + const bmFactory = await ethers.getContractFactory( + 'UpkeepBalanceMonitor', + owner, + ) + upkeepBalanceMonitor = await bmFactory.deploy(linkToken.address, { + maxBatchSize: 10, + minPercentage: 120, + targetPercentage: 300, + maxTopUpAmount: ethers.utils.parseEther('100'), + }) + registry = await deployMockContract(owner, RegistryFactory.abi) + registry2 = await deployMockContract(owner, RegistryFactory.abi) + forwarder = await deployMockContract(owner, ForwarderFactory.abi) + await forwarder.mock.getRegistry.returns(registry.address) + await upkeepBalanceMonitor.setForwarder(forwarder.address) + await linkToken + .connect(owner) + .transfer(upkeepBalanceMonitor.address, ethers.utils.parseEther('10000')) + await upkeepBalanceMonitor + .connect(owner) + .setWatchList(registry.address, [0, 1, 2, 3, 4, 5, 6, 7, 8]) + await upkeepBalanceMonitor + .connect(owner) + .setWatchList(registry2.address, [9, 10, 11]) + for (let i = 0; i < 9; i++) { + await registry.mock.getMinBalance.withArgs(i).returns(100) + await registry.mock.getBalance.withArgs(i).returns(121) // all upkeeps are sufficiently funded + } + for (let i = 9; i < 12; i++) { + await registry2.mock.getMinBalance.withArgs(i).returns(100) + await registry2.mock.getBalance.withArgs(i).returns(121) // all upkeeps are sufficiently funded + } +} + +describe('UpkeepBalanceMonitor', () => { + beforeEach(async () => { + await loadFixture(setup) + }) + + describe('constructor()', () => { + it('should set the initial values correctly', async () => { + const config = await upkeepBalanceMonitor.getConfig() + expect(config.maxBatchSize).to.equal(10) + expect(config.minPercentage).to.equal(120) + expect(config.targetPercentage).to.equal(300) + expect(config.maxTopUpAmount).to.equal(ethers.utils.parseEther('100')) + }) + }) + + describe('setConfig()', () => { + const newConfig = { + maxBatchSize: 100, + minPercentage: 150, + targetPercentage: 500, + maxTopUpAmount: 1, + } + + it('should set config correctly', async () => { + await upkeepBalanceMonitor.connect(owner).setConfig(newConfig) + const config = await upkeepBalanceMonitor.getConfig() + expect(config.maxBatchSize).to.equal(newConfig.maxBatchSize) + expect(config.minPercentage).to.equal(newConfig.minPercentage) + expect(config.targetPercentage).to.equal(newConfig.targetPercentage) + expect(config.maxTopUpAmount).to.equal(newConfig.maxTopUpAmount) + }) + + it('cannot be called by a non-owner', async () => { + await expect( + upkeepBalanceMonitor.connect(stranger).setConfig(newConfig), + ).to.be.revertedWith('Only callable by owner') + }) + + it('should emit an event', async () => { + await expect( + upkeepBalanceMonitor.connect(owner).setConfig(newConfig), + ).to.emit(upkeepBalanceMonitor, 'ConfigSet') + }) + }) + + describe('setForwarder()', () => { + const newForwarder = randomAddress() + + it('should set the forwarder correctly', async () => { + await upkeepBalanceMonitor.connect(owner).setForwarder(newForwarder) + const forwarderAddress = await upkeepBalanceMonitor.getForwarder() + expect(forwarderAddress).to.equal(newForwarder) + }) + + it('cannot be called by a non-owner', async () => { + await expect( + upkeepBalanceMonitor.connect(stranger).setForwarder(randomAddress()), + ).to.be.revertedWith('Only callable by owner') + }) + + it('should emit an event', async () => { + await expect( + upkeepBalanceMonitor.connect(owner).setForwarder(newForwarder), + ) + .to.emit(upkeepBalanceMonitor, 'ForwarderSet') + .withArgs(newForwarder) + }) + }) + + describe('setWatchList()', () => { + const newWatchList = [ + BigNumber.from(1), + BigNumber.from(2), + BigNumber.from(10), + ] + + it('should add addresses to the watchlist', async () => { + await upkeepBalanceMonitor + .connect(owner) + .setWatchList(registry.address, newWatchList) + const [_, upkeepIDs] = await upkeepBalanceMonitor.getWatchList() + expect(upkeepIDs[0]).to.deep.equal(newWatchList) + }) + + it('cannot be called by a non-owner', async () => { + await expect( + upkeepBalanceMonitor + .connect(stranger) + .setWatchList(registry.address, [1, 2, 3]), + ).to.be.revertedWith('Only callable by owner') + }) + + it('should emit an event', async () => { + await expect( + upkeepBalanceMonitor + .connect(owner) + .setWatchList(registry.address, newWatchList), + ) + .to.emit(upkeepBalanceMonitor, 'WatchListSet') + .withArgs(registry.address) + }) + }) + + describe('withdraw()', () => { + const payee = randomAddress() + const withdrawAmount = 100 + + it('should withdraw funds to a payee', async () => { + const initialBalance = await linkToken.balanceOf( + upkeepBalanceMonitor.address, + ) + await upkeepBalanceMonitor.connect(owner).withdraw(withdrawAmount, payee) + const finalBalance = await linkToken.balanceOf( + upkeepBalanceMonitor.address, + ) + const payeeBalance = await linkToken.balanceOf(payee) + expect(finalBalance).to.equal(initialBalance.sub(withdrawAmount)) + expect(payeeBalance).to.equal(withdrawAmount) + }) + + it('cannot be called by a non-owner', async () => { + await expect( + upkeepBalanceMonitor.connect(stranger).withdraw(withdrawAmount, payee), + ).to.be.revertedWith('Only callable by owner') + }) + + it('should emit an event', async () => { + await expect( + upkeepBalanceMonitor.connect(owner).withdraw(withdrawAmount, payee), + ) + .to.emit(upkeepBalanceMonitor, 'FundsWithdrawn') + .withArgs(100, payee) + }) + }) + + describe('pause() and unpause()', () => { + it('should pause and unpause the contract', async () => { + await upkeepBalanceMonitor.connect(owner).pause() + expect(await upkeepBalanceMonitor.paused()).to.be.true + await upkeepBalanceMonitor.connect(owner).unpause() + expect(await upkeepBalanceMonitor.paused()).to.be.false + }) + + it('cannot be called by a non-owner', async () => { + await expect( + upkeepBalanceMonitor.connect(stranger).pause(), + ).to.be.revertedWith('Only callable by owner') + await upkeepBalanceMonitor.connect(owner).pause() + await expect( + upkeepBalanceMonitor.connect(stranger).unpause(), + ).to.be.revertedWith('Only callable by owner') + }) + }) + + describe('checkUpkeep() / getUnderfundedUpkeeps()', () => { + it('should find the underfunded upkeeps', async () => { + let [upkeepIDs, registries, topUpAmounts] = + await upkeepBalanceMonitor.getUnderfundedUpkeeps() + expect(upkeepIDs.length).to.equal(0) + expect(registries.length).to.equal(0) + expect(topUpAmounts.length).to.equal(0) + let [upkeepNeeded, performData] = + await upkeepBalanceMonitor.checkUpkeep('0x') + expect(upkeepNeeded).to.be.false + expect(performData).to.equal('0x') + // update the balance for some upkeeps + await registry.mock.getBalance.withArgs(2).returns(120) + await registry.mock.getBalance.withArgs(4).returns(15) + await registry.mock.getBalance.withArgs(5).returns(0) + ;[upkeepIDs, registries, topUpAmounts] = + await upkeepBalanceMonitor.getUnderfundedUpkeeps() + expect(upkeepIDs.map((v) => v.toNumber())).to.deep.equal([2, 4, 5]) + expect(registries).to.deep.equal([ + registry.address, + registry.address, + registry.address, + ]) + expect(topUpAmounts.map((v) => v.toNumber())).to.deep.equal([ + 180, 285, 300, + ]) + ;[upkeepNeeded, performData] = + await upkeepBalanceMonitor.checkUpkeep('0x') + expect(upkeepNeeded).to.be.true + expect(performData).to.equal( + ethers.utils.defaultAbiCoder.encode( + ['uint256[]', 'address[]', 'uint256[]'], + [ + [2, 4, 5], + [registry.address, registry.address, registry.address], + [180, 285, 300], + ], + ), + ) + // update all to need funding + for (let i = 0; i < 9; i++) { + await registry.mock.getBalance.withArgs(i).returns(0) + } + for (let i = 9; i < 12; i++) { + await registry2.mock.getBalance.withArgs(i).returns(0) + } + // only the max batch size are included in the list + ;[upkeepIDs, registries, topUpAmounts] = + await upkeepBalanceMonitor.getUnderfundedUpkeeps() + expect(upkeepIDs.length).to.equal(10) + expect(topUpAmounts.length).to.equal(10) + expect(upkeepIDs.map((v) => v.toNumber())).to.deep.equal([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + ]) + expect(registries).to.deep.equal([ + ...Array(9).fill(registry.address), + registry2.address, + ]) + expect(topUpAmounts.map((v) => v.toNumber())).to.deep.equal([ + ...Array(10).fill(300), + ]) + // update the balance for some upkeeps + await registry.mock.getBalance.withArgs(0).returns(300) + await registry.mock.getBalance.withArgs(5).returns(300) + ;[upkeepIDs, registries, topUpAmounts] = + await upkeepBalanceMonitor.getUnderfundedUpkeeps() + expect(upkeepIDs.length).to.equal(10) + expect(topUpAmounts.length).to.equal(10) + expect(upkeepIDs.map((v) => v.toNumber())).to.deep.equal([ + 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, + ]) + expect(registries).to.deep.equal([ + ...Array(7).fill(registry.address), + ...Array(3).fill(registry2.address), + ]) + expect(topUpAmounts.map((v) => v.toNumber())).to.deep.equal([ + ...Array(10).fill(300), + ]) + }) + }) + + describe('topUp()', () => { + beforeEach(async () => { + await registry.mock.onTokenTransfer + .withArgs( + upkeepBalanceMonitor.address, + 100, + ethers.utils.defaultAbiCoder.encode(['uint256'], [1]), + ) + .returns() + await registry.mock.onTokenTransfer + .withArgs( + upkeepBalanceMonitor.address, + 50, + ethers.utils.defaultAbiCoder.encode(['uint256'], [7]), + ) + .returns() + }) + + it('cannot be called by a non-owner', async () => { + await expect( + upkeepBalanceMonitor.connect(stranger).topUp([], [], []), + ).to.be.revertedWith('OnlyForwarderOrOwner()') + }) + + it('should revert if the contract is paused', async () => { + await upkeepBalanceMonitor.connect(owner).pause() + await expect( + upkeepBalanceMonitor.connect(owner).topUp([], [], []), + ).to.be.revertedWith('Pausable: paused') + }) + + it('tops up the upkeeps by the amounts provided', async () => { + const initialBalance = await linkToken.balanceOf(registry.address) + const tx = await upkeepBalanceMonitor + .connect(owner) + .topUp([1, 7], [registry.address, registry.address], [100, 50]) + const finalBalance = await linkToken.balanceOf(registry.address) + expect(finalBalance).to.equal(initialBalance.add(150)) + await expect(tx) + .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded') + .withArgs(1, 100) + await expect(tx) + .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded') + .withArgs(7, 50) + }) + + it('does not abort if one top-up fails', async () => { + const initialBalance = await linkToken.balanceOf(registry.address) + const tx = await upkeepBalanceMonitor + .connect(owner) + .topUp( + [1, 7, 100], + [registry.address, registry.address, registry.address], + [100, 50, 100], + ) + const finalBalance = await linkToken.balanceOf(registry.address) + expect(finalBalance).to.equal(initialBalance.add(150)) + await expect(tx) + .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded') + .withArgs(1, 100) + await expect(tx) + .to.emit(upkeepBalanceMonitor, 'TopUpSucceeded') + .withArgs(7, 50) + await expect(tx) + .to.emit(upkeepBalanceMonitor, 'TopUpFailed') + .withArgs(100) + }) + }) + + describe('checkUpkeep() / performUpkeep()', () => { + it('works round-trip', async () => { + await registry.mock.getBalance.withArgs(1).returns(100) // needs 200 + await registry.mock.getBalance.withArgs(7).returns(0) // needs 300 + await registry.mock.onTokenTransfer + .withArgs( + upkeepBalanceMonitor.address, + 200, + ethers.utils.defaultAbiCoder.encode(['uint256'], [1]), + ) + .returns() + await registry.mock.onTokenTransfer + .withArgs( + upkeepBalanceMonitor.address, + 300, + ethers.utils.defaultAbiCoder.encode(['uint256'], [7]), + ) + .returns() + const [upkeepNeeded, performData] = + await upkeepBalanceMonitor.checkUpkeep('0x') + expect(upkeepNeeded).to.be.true + const initialBalance = await linkToken.balanceOf(registry.address) + await upkeepBalanceMonitor.connect(owner).performUpkeep(performData) + const finalBalance = await linkToken.balanceOf(registry.address) + expect(finalBalance).to.equal(initialBalance.add(500)) + }) + }) +}) From a5e1873afedef375957e05a660b3bb4ca296fb93 Mon Sep 17 00:00:00 2001 From: Amir Y <83904651+amirylm@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:06:22 -0300 Subject: [PATCH 203/271] Bump chainlink-automation version (#11380) * bump chainlink-automation version NOTE: this is just for testing, referring to an open branch DO NOT MERGE before we import a proper version of chainlink-automation * go tidy * update version to 2.7.1 * go mod tidy * Update to cut tag * revert version change --------- Co-authored-by: anirudhwarrier <12178754+anirudhwarrier@users.noreply.github.com> Co-authored-by: Akshay Aggarwal --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 1a0775bb837..43840f93ce8 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -21,7 +21,7 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 github.com/pkg/errors v0.9.1 github.com/shopspring/decimal v1.3.1 - github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 + github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/core/scripts/go.sum b/core/scripts/go.sum index ef7aa735bb9..d8e682c742d 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1502,8 +1502,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= -github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= +github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= +github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= diff --git a/go.mod b/go.mod index e396ba5ede9..0f75e120fe6 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 - github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 + github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 diff --git a/go.sum b/go.sum index 0c72ac7e639..fc8cab806a9 100644 --- a/go.sum +++ b/go.sum @@ -1505,8 +1505,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= -github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= +github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= +github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index fd167f81a9b..ca77d3730e2 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -23,7 +23,7 @@ require ( github.com/scylladb/go-reflectx v1.0.1 github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 + github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 github.com/smartcontractkit/chainlink-testing-framework v1.19.5 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0ee9d7848a9..f117bf73e81 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2374,8 +2374,8 @@ github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumvbfM1u/etVq42Afwq/jtNSBSOA8n5jntnNPo= github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= -github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= -github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= +github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= +github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= From 54563c01b188289c4d8aa3381bd2f6905c402161 Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:37:43 -0800 Subject: [PATCH 204/271] [Functions] Heartbeat request support in Gateway handlers (#11345) 1. Functions Handler - add a new method "heartbeat" - add a configurable list of allowed heartbeat senders - collect results from first F+1 nodes and send back in raw form 2. Connector Handler - asynchronously forward requests to Listener and cache results - run a loop to collect OCR reports from Offchain Transmitter 3. Listener - add Timestampi field and validate it --- core/services/functions/connector_handler.go | 182 ++++++++++++++++-- .../functions/connector_handler_test.go | 109 ++++++++++- core/services/functions/listener.go | 3 + core/services/functions/listener_test.go | 8 + core/services/functions/request.go | 17 +- .../gateway/handlers/functions/api.go | 1 + .../handlers/functions/handler.functions.go | 104 +++++++--- .../functions/handler.functions_test.go | 54 +++++- .../services/ocr2/plugins/functions/plugin.go | 6 +- .../ocr2/plugins/functions/plugin_test.go | 9 +- 10 files changed, 432 insertions(+), 61 deletions(-) diff --git a/core/services/functions/connector_handler.go b/core/services/functions/connector_handler.go index 76608b8ada3..5496bbdefc1 100644 --- a/core/services/functions/connector_handler.go +++ b/core/services/functions/connector_handler.go @@ -1,14 +1,18 @@ package functions import ( + "bytes" "context" "crypto/ecdsa" "encoding/json" "fmt" + "sync" + "time" "go.uber.org/multierr" ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -25,35 +29,56 @@ import ( type functionsConnectorHandler struct { services.StateMachine - connector connector.GatewayConnector - signerKey *ecdsa.PrivateKey - nodeAddress string - storage s4.Storage - allowlist functions.OnchainAllowlist - rateLimiter *hc.RateLimiter - subscriptions functions.OnchainSubscriptions - minimumBalance assets.Link - lggr logger.Logger + connector connector.GatewayConnector + signerKey *ecdsa.PrivateKey + nodeAddress string + storage s4.Storage + allowlist functions.OnchainAllowlist + rateLimiter *hc.RateLimiter + subscriptions functions.OnchainSubscriptions + minimumBalance assets.Link + listener FunctionsListener + offchainTransmitter OffchainTransmitter + heartbeatRequests map[RequestID]*HeartbeatResponse + orderedRequests []RequestID + mu sync.Mutex + chStop services.StopChan + shutdownWaitGroup sync.WaitGroup + lggr logger.Logger } +const ( + HeartbeatRequestTimeoutSec = 240 + HeartbeatCacheSize = 1000 +) + var ( _ connector.Signer = &functionsConnectorHandler{} _ connector.GatewayConnectorHandler = &functionsConnectorHandler{} ) -func NewFunctionsConnectorHandler(nodeAddress string, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions functions.OnchainSubscriptions, minimumBalance assets.Link, lggr logger.Logger) (*functionsConnectorHandler, error) { - if signerKey == nil || storage == nil || allowlist == nil || rateLimiter == nil || subscriptions == nil { - return nil, fmt.Errorf("signerKey, storage, allowlist, rateLimiter and subscriptions must be non-nil") +// internal request ID is a hash of (sender, requestID) +func InternalId(sender []byte, requestId []byte) RequestID { + return RequestID(crypto.Keccak256Hash(append(sender, requestId...)).Bytes()) +} + +func NewFunctionsConnectorHandler(nodeAddress string, signerKey *ecdsa.PrivateKey, storage s4.Storage, allowlist functions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions functions.OnchainSubscriptions, listener FunctionsListener, offchainTransmitter OffchainTransmitter, minimumBalance assets.Link, lggr logger.Logger) (*functionsConnectorHandler, error) { + if signerKey == nil || storage == nil || allowlist == nil || rateLimiter == nil || subscriptions == nil || listener == nil || offchainTransmitter == nil { + return nil, fmt.Errorf("all dependencies must be non-nil") } return &functionsConnectorHandler{ - nodeAddress: nodeAddress, - signerKey: signerKey, - storage: storage, - allowlist: allowlist, - rateLimiter: rateLimiter, - subscriptions: subscriptions, - minimumBalance: minimumBalance, - lggr: lggr.Named("FunctionsConnectorHandler"), + nodeAddress: nodeAddress, + signerKey: signerKey, + storage: storage, + allowlist: allowlist, + rateLimiter: rateLimiter, + subscriptions: subscriptions, + minimumBalance: minimumBalance, + listener: listener, + offchainTransmitter: offchainTransmitter, + heartbeatRequests: make(map[RequestID]*HeartbeatResponse), + chStop: make(services.StopChan), + lggr: lggr.Named("FunctionsConnectorHandler"), }, nil } @@ -92,6 +117,8 @@ func (h *functionsConnectorHandler) HandleGatewayMessage(ctx context.Context, ga return } h.handleSecretsSet(ctx, gatewayId, body, fromAddr) + case functions.MethodHeartbeat: + h.handleHeartbeat(ctx, gatewayId, body, fromAddr) default: h.lggr.Errorw("unsupported method", "id", gatewayId, "method", body.Method) } @@ -102,14 +129,21 @@ func (h *functionsConnectorHandler) Start(ctx context.Context) error { if err := h.allowlist.Start(ctx); err != nil { return err } - return h.subscriptions.Start(ctx) + if err := h.subscriptions.Start(ctx); err != nil { + return err + } + h.shutdownWaitGroup.Add(1) + go h.reportLoop() + return nil }) } func (h *functionsConnectorHandler) Close() error { return h.StopOnce("FunctionsConnectorHandler", func() (err error) { + close(h.chStop) err = multierr.Combine(err, h.allowlist.Close()) err = multierr.Combine(err, h.subscriptions.Close()) + h.shutdownWaitGroup.Wait() return }) } @@ -160,6 +194,112 @@ func (h *functionsConnectorHandler) handleSecretsSet(ctx context.Context, gatewa h.sendResponseAndLog(ctx, gatewayId, body, response) } +func (h *functionsConnectorHandler) handleHeartbeat(ctx context.Context, gatewayId string, requestBody *api.MessageBody, fromAddr ethCommon.Address) { + var request *OffchainRequest + err := json.Unmarshal(requestBody.Payload, &request) + if err != nil { + h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse(fmt.Sprintf("failed to unmarshal request: %v", err))) + return + } + if !bytes.Equal(request.RequestInitiator, fromAddr.Bytes()) { + h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse("RequestInitiator doesn't match sender")) + return + } + if !bytes.Equal(request.SubscriptionOwner, fromAddr.Bytes()) { + h.sendResponseAndLog(ctx, gatewayId, requestBody, internalErrorResponse("SubscriptionOwner doesn't match sender")) + return + } + + internalId := InternalId(fromAddr.Bytes(), request.RequestId) + request.RequestId = internalId[:] + h.lggr.Infow("handling offchain heartbeat", "messageId", requestBody.MessageId, "internalId", internalId, "sender", requestBody.Sender) + h.mu.Lock() + response, ok := h.heartbeatRequests[internalId] + if !ok { // new request + response = &HeartbeatResponse{ + Status: RequestStatePending, + ReceivedTs: uint64(time.Now().Unix()), + } + h.cacheNewRequestLocked(internalId, response) + h.shutdownWaitGroup.Add(1) + go h.handleOffchainRequest(request) + } + responseToSend := *response + h.mu.Unlock() + requestBody.Receiver = requestBody.Sender + h.sendResponseAndLog(ctx, gatewayId, requestBody, responseToSend) +} + +func internalErrorResponse(internalError string) HeartbeatResponse { + return HeartbeatResponse{ + Status: RequestStateInternalError, + InternalError: internalError, + } +} + +func (h *functionsConnectorHandler) handleOffchainRequest(request *OffchainRequest) { + defer h.shutdownWaitGroup.Done() + stopCtx, _ := h.chStop.NewCtx() + ctx, cancel := context.WithTimeout(stopCtx, time.Duration(HeartbeatRequestTimeoutSec)*time.Second) + defer cancel() + err := h.listener.HandleOffchainRequest(ctx, request) + if err != nil { + h.lggr.Errorw("internal error while processing", "id", request.RequestId, "error", err) + h.mu.Lock() + defer h.mu.Unlock() + state, ok := h.heartbeatRequests[RequestID(request.RequestId)] + if !ok { + h.lggr.Errorw("request unexpectedly disappeared from local cache", "id", request.RequestId) + return + } + state.CompletedTs = uint64(time.Now().Unix()) + state.Status = RequestStateInternalError + state.InternalError = err.Error() + } else { + // no error - results will be sent to OCR aggregation and returned via reportLoop() + h.lggr.Infow("request processed successfully, waiting for aggregation ...", "id", request.RequestId) + } +} + +// Listen to OCR reports passed from the plugin and process them against a local cache of requests. +func (h *functionsConnectorHandler) reportLoop() { + defer h.shutdownWaitGroup.Done() + for { + select { + case report := <-h.offchainTransmitter.ReportChannel(): + h.lggr.Infow("received report", "requestId", report.RequestId, "resultLen", len(report.Result), "errorLen", len(report.Error)) + if len(report.RequestId) != RequestIDLength { + h.lggr.Errorw("report has invalid requestId", "requestId", report.RequestId) + continue + } + h.mu.Lock() + cachedResponse, ok := h.heartbeatRequests[RequestID(report.RequestId)] + if !ok { + h.lggr.Infow("received report for unknown request, caching it", "id", report.RequestId) + cachedResponse = &HeartbeatResponse{} + h.cacheNewRequestLocked(RequestID(report.RequestId), cachedResponse) + } + cachedResponse.CompletedTs = uint64(time.Now().Unix()) + cachedResponse.Status = RequestStateComplete + cachedResponse.Response = report + h.mu.Unlock() + case <-h.chStop: + h.lggr.Info("exiting reportLoop") + return + } + } +} + +func (h *functionsConnectorHandler) cacheNewRequestLocked(requestId RequestID, response *HeartbeatResponse) { + // remove oldest requests + for len(h.orderedRequests) >= HeartbeatCacheSize { + delete(h.heartbeatRequests, h.orderedRequests[0]) + h.orderedRequests = h.orderedRequests[1:] + } + h.heartbeatRequests[requestId] = response + h.orderedRequests = append(h.orderedRequests, requestId) +} + func (h *functionsConnectorHandler) sendResponseAndLog(ctx context.Context, gatewayId string, requestBody *api.MessageBody, payload any) { err := h.sendResponse(ctx, gatewayId, requestBody, payload) if err != nil { diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index 82c3dab3afc..fe1a1baa6fc 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -1,16 +1,20 @@ package functions_test import ( + "crypto/rand" "encoding/base64" "encoding/json" "errors" "math/big" "testing" + geth_common "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/functions" + sfmocks "github.com/smartcontractkit/chainlink/v2/core/services/functions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/common" gcmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector/mocks" @@ -24,6 +28,31 @@ import ( "github.com/stretchr/testify/require" ) +func newOffchainRequest(t *testing.T, sender []byte) (*api.Message, functions.RequestID) { + requestId := make([]byte, 32) + _, err := rand.Read(requestId) + require.NoError(t, err) + request := &functions.OffchainRequest{ + RequestId: requestId, + RequestInitiator: sender, + SubscriptionId: 1, + SubscriptionOwner: sender, + } + + internalId := functions.InternalId(request.RequestInitiator, request.RequestId) + req, err := json.Marshal(request) + require.NoError(t, err) + msg := &api.Message{ + Body: api.MessageBody{ + DonId: "fun4", + MessageId: "1", + Method: "heartbeat", + Payload: req, + }, + } + return msg, internalId +} + func TestFunctionsConnectorHandler(t *testing.T) { t.Parallel() @@ -34,12 +63,16 @@ func TestFunctionsConnectorHandler(t *testing.T) { allowlist := gfmocks.NewOnchainAllowlist(t) rateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) subscriptions := gfmocks.NewOnchainSubscriptions(t) + reportCh := make(chan *functions.OffchainResponse) + offchainTransmitter := sfmocks.NewOffchainTransmitter(t) + offchainTransmitter.On("ReportChannel", mock.Anything).Return(reportCh) + listener := sfmocks.NewFunctionsListener(t) require.NoError(t, err) allowlist.On("Start", mock.Anything).Return(nil) allowlist.On("Close", mock.Anything).Return(nil) subscriptions.On("Start", mock.Anything).Return(nil) subscriptions.On("Close", mock.Anything).Return(nil) - handler, err := functions.NewFunctionsConnectorHandler(addr.Hex(), privateKey, storage, allowlist, rateLimiter, subscriptions, *assets.NewLinkFromJuels(100), logger) + handler, err := functions.NewFunctionsConnectorHandler(addr.Hex(), privateKey, storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, *assets.NewLinkFromJuels(100), logger) require.NoError(t, err) handler.SetConnector(connector) @@ -219,4 +252,78 @@ func TestFunctionsConnectorHandler(t *testing.T) { handler.HandleGatewayMessage(testutils.Context(t), "gw1", &msg) }) }) + + t.Run("heartbeat success", func(t *testing.T) { + ctx := testutils.Context(t) + msg, internalId := newOffchainRequest(t, addr.Bytes()) + require.NoError(t, msg.Sign(privateKey)) + + // first call to trigger the request + var response functions.HeartbeatResponse + allowlist.On("Allow", addr).Return(true).Once() + listener.On("HandleOffchainRequest", mock.Anything, mock.Anything).Return(nil).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { + respMsg, ok := args[2].(*api.Message) + require.True(t, ok) + require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) + require.Equal(t, functions.RequestStatePending, response.Status) + }).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + + // async response computation + reportCh <- &functions.OffchainResponse{ + RequestId: internalId[:], + Result: []byte("ok!"), + } + reportCh <- &functions.OffchainResponse{} // sending second item to make sure the first one got processed + + // second call to collect the response + allowlist.On("Allow", addr).Return(true).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { + respMsg, ok := args[2].(*api.Message) + require.True(t, ok) + require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) + require.Equal(t, functions.RequestStateComplete, response.Status) + }).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + }) + + t.Run("heartbeat internal error", func(t *testing.T) { + ctx := testutils.Context(t) + msg, _ := newOffchainRequest(t, addr.Bytes()) + require.NoError(t, msg.Sign(privateKey)) + + // first call to trigger the request + var response functions.HeartbeatResponse + allowlist.On("Allow", addr).Return(true).Once() + listener.On("HandleOffchainRequest", mock.Anything, mock.Anything).Return(errors.New("boom")).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + + // second call to collect the response + allowlist.On("Allow", addr).Return(true).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { + respMsg, ok := args[2].(*api.Message) + require.True(t, ok) + require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) + require.Equal(t, functions.RequestStateInternalError, response.Status) + }).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + }) + + t.Run("heartbeat sender address doesn't match", func(t *testing.T) { + ctx := testutils.Context(t) + msg, _ := newOffchainRequest(t, geth_common.BytesToAddress([]byte("0x1234")).Bytes()) + require.NoError(t, msg.Sign(privateKey)) + + var response functions.HeartbeatResponse + allowlist.On("Allow", addr).Return(true).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { + respMsg, ok := args[2].(*api.Message) + require.True(t, ok) + require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) + require.Equal(t, functions.RequestStateInternalError, response.Status) + }).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + }) } diff --git a/core/services/functions/listener.go b/core/services/functions/listener.go index 3a308431807..65c364adb7c 100644 --- a/core/services/functions/listener.go +++ b/core/services/functions/listener.go @@ -300,6 +300,9 @@ func (l *functionsListener) HandleOffchainRequest(ctx context.Context, request * if len(request.SubscriptionOwner) != common.AddressLength || len(request.RequestInitiator) != common.AddressLength { return fmt.Errorf("HandleOffchainRequest: SubscriptionOwner and RequestInitiator must be set to valid addresses") } + if request.Timestamp < uint64(time.Now().Unix()-int64(l.pluginConfig.RequestTimeoutSec)) { + return fmt.Errorf("HandleOffchainRequest: request timestamp is too old") + } var requestId RequestID copy(requestId[:], request.RequestId[:32]) diff --git a/core/services/functions/listener_test.go b/core/services/functions/listener_test.go index ecad9e4cceb..0fcc9c65599 100644 --- a/core/services/functions/listener_test.go +++ b/core/services/functions/listener_test.go @@ -7,6 +7,7 @@ import ( "math/big" "sync" "testing" + "time" "github.com/ethereum/go-ethereum/common" "github.com/fxamacker/cbor/v2" @@ -195,6 +196,7 @@ func TestFunctionsListener_HandleOffchainRequest_Success(t *testing.T) { RequestInitiator: SubscriptionOwner.Bytes(), SubscriptionId: uint64(SubscriptionID), SubscriptionOwner: SubscriptionOwner.Bytes(), + Timestamp: uint64(time.Now().Unix()), Data: functions_service.RequestData{}, } require.NoError(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) @@ -210,6 +212,7 @@ func TestFunctionsListener_HandleOffchainRequest_Invalid(t *testing.T) { RequestInitiator: []byte("invalid_address"), SubscriptionId: uint64(SubscriptionID), SubscriptionOwner: SubscriptionOwner.Bytes(), + Timestamp: uint64(time.Now().Unix()), Data: functions_service.RequestData{}, } require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) @@ -217,6 +220,10 @@ func TestFunctionsListener_HandleOffchainRequest_Invalid(t *testing.T) { request.RequestInitiator = SubscriptionOwner.Bytes() request.SubscriptionOwner = []byte("invalid_address") require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) + + request.SubscriptionOwner = SubscriptionOwner.Bytes() + request.Timestamp = 1 + require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) } func TestFunctionsListener_HandleOffchainRequest_InternalError(t *testing.T) { @@ -233,6 +240,7 @@ func TestFunctionsListener_HandleOffchainRequest_InternalError(t *testing.T) { RequestInitiator: SubscriptionOwner.Bytes(), SubscriptionId: uint64(SubscriptionID), SubscriptionOwner: SubscriptionOwner.Bytes(), + Timestamp: uint64(time.Now().Unix()), Data: functions_service.RequestData{}, } require.Error(t, uni.service.HandleOffchainRequest(testutils.Context(t), request)) diff --git a/core/services/functions/request.go b/core/services/functions/request.go index 14c0b0d0e5a..eaa92fc8088 100644 --- a/core/services/functions/request.go +++ b/core/services/functions/request.go @@ -5,6 +5,10 @@ const ( LocationRemote = 1 LocationDONHosted = 2 LanguageJavaScript = 0 + + RequestStatePending = 1 + RequestStateComplete = 2 + RequestStateInternalError = 3 ) type RequestFlags [32]byte @@ -14,6 +18,7 @@ type OffchainRequest struct { RequestInitiator []byte `json:"requestInitiator"` SubscriptionId uint64 `json:"subscriptionId"` SubscriptionOwner []byte `json:"subscriptionOwner"` + Timestamp uint64 `json:"timestamp"` Data RequestData `json:"data"` } @@ -30,8 +35,16 @@ type RequestData struct { // NOTE: to be extended with raw report and signatures when needed type OffchainResponse struct { RequestId []byte `json:"requestId"` - Result []byte `json:"result"` - Error []byte `json:"error"` + Result []byte `json:"result,omitempty"` + Error []byte `json:"error,omitempty"` +} + +type HeartbeatResponse struct { + Status int `json:"status"` + InternalError string `json:"internalError,omitempty"` + ReceivedTs uint64 `json:"receivedTs"` + CompletedTs uint64 `json:"completedTs"` + Response *OffchainResponse `json:"response,omitempty"` } type DONHostedSecrets struct { diff --git a/core/services/gateway/handlers/functions/api.go b/core/services/gateway/handlers/functions/api.go index 202fa99e414..36db1943931 100644 --- a/core/services/gateway/handlers/functions/api.go +++ b/core/services/gateway/handlers/functions/api.go @@ -5,6 +5,7 @@ import "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" const ( MethodSecretsSet = "secrets_set" MethodSecretsList = "secrets_list" + MethodHeartbeat = "heartbeat" ) type SecretsSetRequest struct { diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index 3269caa2d6a..b52c866a131 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "math/big" + "strings" "time" "github.com/ethereum/go-ethereum/common" @@ -62,26 +63,28 @@ type FunctionsHandlerConfig struct { OnchainSubscriptions *OnchainSubscriptionsConfig `json:"onchainSubscriptions"` MinimumSubscriptionBalance *assets.Link `json:"minimumSubscriptionBalance"` // Not specifying RateLimiter config disables rate limiting - UserRateLimiter *hc.RateLimiterConfig `json:"userRateLimiter"` - NodeRateLimiter *hc.RateLimiterConfig `json:"nodeRateLimiter"` - MaxPendingRequests uint32 `json:"maxPendingRequests"` - RequestTimeoutMillis int64 `json:"requestTimeoutMillis"` + UserRateLimiter *hc.RateLimiterConfig `json:"userRateLimiter"` + NodeRateLimiter *hc.RateLimiterConfig `json:"nodeRateLimiter"` + MaxPendingRequests uint32 `json:"maxPendingRequests"` + RequestTimeoutMillis int64 `json:"requestTimeoutMillis"` + AllowedHeartbeatInitiators []string `json:"allowedHeartbeatInitiators"` } type functionsHandler struct { services.StateMachine - handlerConfig FunctionsHandlerConfig - donConfig *config.DONConfig - don handlers.DON - pendingRequests hc.RequestCache[PendingRequest] - allowlist OnchainAllowlist - subscriptions OnchainSubscriptions - minimumBalance *assets.Link - userRateLimiter *hc.RateLimiter - nodeRateLimiter *hc.RateLimiter - chStop services.StopChan - lggr logger.Logger + handlerConfig FunctionsHandlerConfig + donConfig *config.DONConfig + don handlers.DON + pendingRequests hc.RequestCache[PendingRequest] + allowlist OnchainAllowlist + subscriptions OnchainSubscriptions + minimumBalance *assets.Link + userRateLimiter *hc.RateLimiter + nodeRateLimiter *hc.RateLimiter + allowedHeartbeatInitiators map[string]struct{} + chStop services.StopChan + lggr logger.Logger } type PendingRequest struct { @@ -135,8 +138,12 @@ func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *con return nil, err2 } } + allowedHeartbeatInitiators := make(map[string]struct{}) + for _, initiator := range cfg.AllowedHeartbeatInitiators { + allowedHeartbeatInitiators[strings.ToLower(initiator)] = struct{}{} + } pendingRequestsCache := hc.NewRequestCache[PendingRequest](time.Millisecond*time.Duration(cfg.RequestTimeoutMillis), cfg.MaxPendingRequests) - return NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, cfg.MinimumSubscriptionBalance, userRateLimiter, nodeRateLimiter, lggr), nil + return NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, cfg.MinimumSubscriptionBalance, userRateLimiter, nodeRateLimiter, allowedHeartbeatInitiators, lggr), nil } func NewFunctionsHandler( @@ -149,19 +156,21 @@ func NewFunctionsHandler( minimumBalance *assets.Link, userRateLimiter *hc.RateLimiter, nodeRateLimiter *hc.RateLimiter, + allowedHeartbeatInitiators map[string]struct{}, lggr logger.Logger) handlers.Handler { return &functionsHandler{ - handlerConfig: cfg, - donConfig: donConfig, - don: don, - pendingRequests: pendingRequestsCache, - allowlist: allowlist, - subscriptions: subscriptions, - minimumBalance: minimumBalance, - userRateLimiter: userRateLimiter, - nodeRateLimiter: nodeRateLimiter, - chStop: make(services.StopChan), - lggr: lggr, + handlerConfig: cfg, + donConfig: donConfig, + don: don, + pendingRequests: pendingRequestsCache, + allowlist: allowlist, + subscriptions: subscriptions, + minimumBalance: minimumBalance, + userRateLimiter: userRateLimiter, + nodeRateLimiter: nodeRateLimiter, + allowedHeartbeatInitiators: allowedHeartbeatInitiators, + chStop: make(services.StopChan), + lggr: lggr, } } @@ -193,6 +202,13 @@ func (h *functionsHandler) HandleUserMessage(ctx context.Context, msg *api.Messa switch msg.Body.Method { case MethodSecretsSet, MethodSecretsList: return h.handleRequest(ctx, msg, callbackCh) + case MethodHeartbeat: + if _, ok := h.allowedHeartbeatInitiators[msg.Body.Sender]; !ok { + h.lggr.Debugw("received heartbeat request from a non-allowed sender", "sender", msg.Body.Sender) + promHandlerError.WithLabelValues(h.donConfig.DonId, ErrNotAllowlisted.Error()).Inc() + return ErrUnsupportedMethod + } + return h.handleRequest(ctx, msg, callbackCh) default: h.lggr.Debugw("unsupported method", "method", msg.Body.Method) promHandlerError.WithLabelValues(h.donConfig.DonId, ErrUnsupportedMethod.Error()).Inc() @@ -227,6 +243,8 @@ func (h *functionsHandler) HandleNodeMessage(ctx context.Context, msg *api.Messa switch msg.Body.Method { case MethodSecretsSet, MethodSecretsList: return h.pendingRequests.ProcessResponse(msg, h.processSecretsResponse) + case MethodHeartbeat: + return h.pendingRequests.ProcessResponse(msg, h.processHeartbeatResponse) default: h.lggr.Debugw("unsupported method", "method", msg.Body.Method) return ErrUnsupportedMethod @@ -295,6 +313,38 @@ func newSecretsResponse(request *api.Message, success bool, responses []*api.Mes return &handlers.UserCallbackPayload{Msg: &userResponse, ErrCode: api.NoError, ErrMsg: ""}, nil } +// Conforms to ResponseProcessor[*PendingRequest] +func (h *functionsHandler) processHeartbeatResponse(response *api.Message, responseData *PendingRequest) (*handlers.UserCallbackPayload, *PendingRequest, error) { + if _, exists := responseData.responses[response.Body.Sender]; exists { + return nil, nil, errors.New("duplicate response") + } + if response.Body.Method != responseData.request.Body.Method { + return nil, responseData, errors.New("invalid method") + } + responseData.responses[response.Body.Sender] = response + + // user response is ready with F+1 node responses + if len(responseData.responses) >= h.donConfig.F+1 { + var responseList []*api.Message + for _, response := range responseData.responses { + responseList = append(responseList, response) + } + userResponse := *responseData.request + userResponse.Body.Receiver = responseData.request.Body.Sender + // success = true only means that we got F+1 responses + // it's up to the heartbeat sender to validate computation results + payload := CombinedResponse{ResponseBase: ResponseBase{Success: true}, NodeResponses: responseList} + payloadJson, err := json.Marshal(payload) + if err != nil { + return &handlers.UserCallbackPayload{Msg: &userResponse, ErrCode: api.NodeReponseEncodingError, ErrMsg: ""}, nil, nil + } + userResponse.Body.Payload = payloadJson + return &handlers.UserCallbackPayload{Msg: &userResponse, ErrCode: api.NoError, ErrMsg: ""}, nil, nil + } + // not ready to be processed yet + return nil, responseData, nil +} + func (h *functionsHandler) Start(ctx context.Context) error { return h.StartOnce("FunctionsHandler", func() error { h.lggr.Info("starting FunctionsHandler") diff --git a/core/services/gateway/handlers/functions/handler.functions_test.go b/core/services/gateway/handlers/functions/handler.functions_test.go index 402823df173..f36b64709a2 100644 --- a/core/services/gateway/handlers/functions/handler.functions_test.go +++ b/core/services/gateway/handlers/functions/handler.functions_test.go @@ -25,7 +25,7 @@ import ( handlers_mocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/mocks" ) -func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTimeout time.Duration) (handlers.Handler, *handlers_mocks.DON, *functions_mocks.OnchainAllowlist, *functions_mocks.OnchainSubscriptions) { +func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTimeout time.Duration, heartbeatSender string) (handlers.Handler, *handlers_mocks.DON, *functions_mocks.OnchainAllowlist, *functions_mocks.OnchainSubscriptions) { cfg := functions.FunctionsHandlerConfig{} donConfig := &config.DONConfig{ Members: []config.NodeConfig{}, @@ -48,7 +48,8 @@ func newFunctionsHandlerForATestDON(t *testing.T, nodes []gc.TestNode, requestTi nodeRateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) pendingRequestsCache := hc.NewRequestCache[functions.PendingRequest](requestTimeout, 1000) - handler := functions.NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, minBalance, userRateLimiter, nodeRateLimiter, logger.TestLogger(t)) + allowedHeartbeatInititors := map[string]struct{}{heartbeatSender: {}} + handler := functions.NewFunctionsHandler(cfg, donConfig, don, pendingRequestsCache, allowlist, subscriptions, minBalance, userRateLimiter, nodeRateLimiter, allowedHeartbeatInititors, logger.TestLogger(t)) return handler, don, allowlist, subscriptions } @@ -117,7 +118,7 @@ func TestFunctionsHandler_HandleUserMessage_SecretsSet(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { nodes, user := gc.NewTestNodes(t, 4), gc.NewTestNodes(t, 1)[0] - handler, don, allowlist, subscriptions := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24) + handler, don, allowlist, subscriptions := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24, user.Address) userRequestMsg := newSignedMessage(t, "1234", "secrets_set", "don_id", user.PrivateKey) callbachCh := make(chan handlers.UserCallbackPayload) @@ -144,11 +145,54 @@ func TestFunctionsHandler_HandleUserMessage_SecretsSet(t *testing.T) { } } +func TestFunctionsHandler_HandleUserMessage_Heartbeat(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + nodeResults []bool + expectedGatewayResult bool + expectedNodeMessageCount int + }{ + {"three successful", []bool{true, true, true, false}, true, 2}, + {"two successful", []bool{false, true, false, true}, true, 2}, + {"one successful", []bool{false, true, false, false}, true, 2}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + nodes, user := gc.NewTestNodes(t, 4), gc.NewTestNodes(t, 1)[0] + handler, don, allowlist, _ := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24, user.Address) + userRequestMsg := newSignedMessage(t, "1234", "heartbeat", "don_id", user.PrivateKey) + + callbachCh := make(chan handlers.UserCallbackPayload) + done := make(chan struct{}) + go func() { + defer close(done) + // wait on a response from Gateway to the user + response := <-callbachCh + require.Equal(t, api.NoError, response.ErrCode) + require.Equal(t, userRequestMsg.Body.MessageId, response.Msg.Body.MessageId) + var payload functions.CombinedResponse + require.NoError(t, json.Unmarshal(response.Msg.Body.Payload, &payload)) + require.Equal(t, test.expectedGatewayResult, payload.Success) + require.Equal(t, test.expectedNodeMessageCount, len(payload.NodeResponses)) + }() + + allowlist.On("Allow", common.HexToAddress(user.Address)).Return(true, nil) + don.On("SendToNode", mock.Anything, mock.Anything, mock.Anything).Return(nil) + require.NoError(t, handler.HandleUserMessage(testutils.Context(t), &userRequestMsg, callbachCh)) + sendNodeReponses(t, handler, userRequestMsg, nodes, test.nodeResults) + <-done + }) + } +} + func TestFunctionsHandler_HandleUserMessage_InvalidMethod(t *testing.T) { t.Parallel() nodes, user := gc.NewTestNodes(t, 4), gc.NewTestNodes(t, 1)[0] - handler, _, allowlist, _ := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24) + handler, _, allowlist, _ := newFunctionsHandlerForATestDON(t, nodes, time.Hour*24, user.Address) userRequestMsg := newSignedMessage(t, "1234", "secrets_reveal_all_please", "don_id", user.PrivateKey) allowlist.On("Allow", common.HexToAddress(user.Address)).Return(true, nil) @@ -160,7 +204,7 @@ func TestFunctionsHandler_HandleUserMessage_Timeout(t *testing.T) { t.Parallel() nodes, user := gc.NewTestNodes(t, 4), gc.NewTestNodes(t, 1)[0] - handler, don, allowlist, subscriptions := newFunctionsHandlerForATestDON(t, nodes, time.Millisecond*10) + handler, don, allowlist, subscriptions := newFunctionsHandlerForATestDON(t, nodes, time.Millisecond*10, user.Address) userRequestMsg := newSignedMessage(t, "1234", "secrets_set", "don_id", user.PrivateKey) callbachCh := make(chan handlers.UserCallbackPayload) diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 475cf0a2af7..82280f527cd 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -146,7 +146,7 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs return nil, errors.Wrap(err, "failed to create a OnchainSubscriptions") } connectorLogger := conf.Logger.Named("GatewayConnector").With("jobName", conf.Job.PipelineSpec.JobName) - connector, err2 := NewConnector(pluginConfig.GatewayConnectorConfig, conf.EthKeystore, conf.Chain.ID(), s4Storage, allowlist, rateLimiter, subscriptions, pluginConfig.MinimumSubscriptionBalance, connectorLogger) + connector, err2 := NewConnector(pluginConfig.GatewayConnectorConfig, conf.EthKeystore, conf.Chain.ID(), s4Storage, allowlist, rateLimiter, subscriptions, functionsListener, offchainTransmitter, pluginConfig.MinimumSubscriptionBalance, connectorLogger) if err2 != nil { return nil, errors.Wrap(err, "failed to create a GatewayConnector") } @@ -173,7 +173,7 @@ func NewFunctionsServices(functionsOracleArgs, thresholdOracleArgs, s4OracleArgs return allServices, nil } -func NewConnector(gwcCfg *connector.ConnectorConfig, ethKeystore keystore.Eth, chainID *big.Int, s4Storage s4.Storage, allowlist gwFunctions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions gwFunctions.OnchainSubscriptions, minimumBalance assets.Link, lggr logger.Logger) (connector.GatewayConnector, error) { +func NewConnector(gwcCfg *connector.ConnectorConfig, ethKeystore keystore.Eth, chainID *big.Int, s4Storage s4.Storage, allowlist gwFunctions.OnchainAllowlist, rateLimiter *hc.RateLimiter, subscriptions gwFunctions.OnchainSubscriptions, listener functions.FunctionsListener, offchainTransmitter functions.OffchainTransmitter, minimumBalance assets.Link, lggr logger.Logger) (connector.GatewayConnector, error) { enabledKeys, err := ethKeystore.EnabledKeysForChain(chainID) if err != nil { return nil, err @@ -186,7 +186,7 @@ func NewConnector(gwcCfg *connector.ConnectorConfig, ethKeystore keystore.Eth, c signerKey := enabledKeys[idx].ToEcdsaPrivKey() nodeAddress := enabledKeys[idx].ID() - handler, err := functions.NewFunctionsConnectorHandler(nodeAddress, signerKey, s4Storage, allowlist, rateLimiter, subscriptions, minimumBalance, lggr) + handler, err := functions.NewFunctionsConnectorHandler(nodeAddress, signerKey, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, minimumBalance, lggr) if err != nil { return nil, err } diff --git a/core/services/ocr2/plugins/functions/plugin_test.go b/core/services/ocr2/plugins/functions/plugin_test.go index 453d4b67aa8..d77fabcc437 100644 --- a/core/services/ocr2/plugins/functions/plugin_test.go +++ b/core/services/ocr2/plugins/functions/plugin_test.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/logger" + sfmocks "github.com/smartcontractkit/chainlink/v2/core/services/functions/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" hc "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/common" gfmocks "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers/functions/mocks" @@ -35,8 +36,10 @@ func TestNewConnector_Success(t *testing.T) { subscriptions := gfmocks.NewOnchainSubscriptions(t) rateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) + listener := sfmocks.NewFunctionsListener(t) + offchainTransmitter := sfmocks.NewOffchainTransmitter(t) ethKeystore.On("EnabledKeysForChain", mock.Anything).Return([]ethkey.KeyV2{keyV2}, nil) - _, err = functions.NewConnector(gwcCfg, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, *assets.NewLinkFromJuels(0), logger.TestLogger(t)) + _, err = functions.NewConnector(gwcCfg, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, *assets.NewLinkFromJuels(0), logger.TestLogger(t)) require.NoError(t, err) } @@ -58,7 +61,9 @@ func TestNewConnector_NoKeyForConfiguredAddress(t *testing.T) { subscriptions := gfmocks.NewOnchainSubscriptions(t) rateLimiter, err := hc.NewRateLimiter(hc.RateLimiterConfig{GlobalRPS: 100.0, GlobalBurst: 100, PerSenderRPS: 100.0, PerSenderBurst: 100}) require.NoError(t, err) + listener := sfmocks.NewFunctionsListener(t) + offchainTransmitter := sfmocks.NewOffchainTransmitter(t) ethKeystore.On("EnabledKeysForChain", mock.Anything).Return([]ethkey.KeyV2{{Address: common.HexToAddress(addresses[1])}}, nil) - _, err = functions.NewConnector(gwcCfg, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, *assets.NewLinkFromJuels(0), logger.TestLogger(t)) + _, err = functions.NewConnector(gwcCfg, ethKeystore, chainID, s4Storage, allowlist, rateLimiter, subscriptions, listener, offchainTransmitter, *assets.NewLinkFromJuels(0), logger.TestLogger(t)) require.Error(t, err) } From bd7e233d3a489e8dacae0a8c8539802b3ae10832 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Tue, 28 Nov 2023 01:52:02 +0200 Subject: [PATCH 205/271] Remove core logger from common (#11366) * Remove core logger from common * Remove core logger from evm * Move legacy config to fix logger dependencies * Fix lint * Revert multinode trace removal * Fix lint * Fix testcase * Fix arbgas command logging * Fix imports * Fix sendonly node logger * Fix lint again * Minor fixes * bump chainlink-common --------- Co-authored-by: Jordan Krage --- common/client/multi_node.go | 37 +++++------ common/client/multi_node_test.go | 20 +++--- common/client/node.go | 7 ++- common/client/node_lifecycle.go | 32 +++++----- common/client/node_lifecycle_test.go | 44 ++++++------- common/client/node_test.go | 4 +- common/client/send_only_node.go | 5 +- common/client/send_only_node_test.go | 14 ++--- common/fee/models.go | 2 +- common/headtracker/head_broadcaster.go | 5 +- common/headtracker/head_listener.go | 4 +- common/headtracker/head_tracker.go | 8 +-- common/txmgr/broadcaster.go | 55 ++++++++-------- common/txmgr/confirmer.go | 17 ++--- common/txmgr/reaper.go | 4 +- common/txmgr/resender.go | 5 +- common/txmgr/txmgr.go | 8 +-- common/txmgr/types/client.go | 2 +- .../txmgr/types/mocks/tx_attempt_builder.go | 2 +- common/txmgr/types/tx.go | 24 +++---- common/txmgr/types/tx_attempt_builder.go | 2 +- core/chains/evm/client/chain_client.go | 3 +- core/chains/evm/client/client.go | 3 +- core/chains/evm/client/errors.go | 8 +-- core/chains/evm/client/helpers_test.go | 8 +-- core/chains/evm/client/node.go | 62 ++++++++++--------- core/chains/evm/client/node_fsm_test.go | 4 +- core/chains/evm/client/node_lifecycle.go | 32 +++++----- core/chains/evm/client/node_lifecycle_test.go | 30 ++++----- core/chains/evm/client/null_client.go | 6 +- core/chains/evm/client/null_client_test.go | 8 +-- core/chains/evm/client/pool.go | 15 ++--- core/chains/evm/client/pool_test.go | 14 ++--- core/chains/evm/client/rpc_client.go | 62 ++++++++++--------- core/chains/evm/client/send_only_node.go | 7 ++- core/chains/evm/client/send_only_node_test.go | 18 +++--- .../evm/client/simulated_backend_client.go | 5 +- core/chains/evm/config/chain_scoped.go | 3 +- .../evm/forwarders/forwarder_manager.go | 4 +- .../evm/forwarders/forwarder_manager_test.go | 10 +-- core/chains/evm/forwarders/orm.go | 2 +- core/chains/evm/forwarders/orm_test.go | 4 +- core/chains/evm/gas/arbitrum_estimator.go | 4 +- .../chains/evm/gas/arbitrum_estimator_test.go | 18 +++--- .../chains/evm/gas/block_history_estimator.go | 21 ++++--- .../evm/gas/block_history_estimator_test.go | 44 ++++++------- core/chains/evm/gas/cmd/arbgas/main.go | 10 +-- core/chains/evm/gas/fixed_price_estimator.go | 4 +- .../evm/gas/fixed_price_estimator_test.go | 14 ++--- core/chains/evm/gas/gas_test.go | 14 ++--- core/chains/evm/gas/models.go | 2 +- .../evm/gas/rollups/l1_gas_price_oracle.go | 7 ++- .../gas/rollups/l1_gas_price_oracle_test.go | 14 ++--- .../evm/gas/suggested_price_estimator.go | 5 +- .../evm/gas/suggested_price_estimator_test.go | 14 ++--- .../evm/headtracker/head_broadcaster.go | 2 +- .../evm/headtracker/head_broadcaster_test.go | 8 +-- core/chains/evm/headtracker/head_listener.go | 2 +- .../evm/headtracker/head_listener_test.go | 8 +-- core/chains/evm/headtracker/head_saver.go | 4 +- .../chains/evm/headtracker/head_saver_test.go | 4 +- core/chains/evm/headtracker/head_tracker.go | 2 +- .../evm/headtracker/head_tracker_test.go | 46 +++++++------- core/chains/evm/headtracker/orm.go | 4 +- core/chains/evm/headtracker/orm_test.go | 12 ++-- core/chains/evm/log/broadcaster.go | 6 +- core/chains/evm/log/eth_subscriber.go | 6 +- core/chains/evm/log/helpers_internal_test.go | 2 +- core/chains/evm/log/helpers_test.go | 8 +-- core/chains/evm/log/integration_test.go | 16 ++--- core/chains/evm/log/orm.go | 2 +- core/chains/evm/log/orm_test.go | 10 +-- core/chains/evm/log/pool.go | 4 +- core/chains/evm/log/pool_test.go | 18 +++--- core/chains/evm/log/registrations.go | 22 +++---- core/chains/evm/log/registrations_test.go | 4 +- core/chains/evm/logpoller/helper_test.go | 4 +- core/chains/evm/logpoller/log_poller.go | 8 +-- .../evm/logpoller/log_poller_internal_test.go | 15 ++--- core/chains/evm/logpoller/log_poller_test.go | 14 ++--- core/chains/evm/logpoller/observability.go | 2 +- .../evm/logpoller/observability_test.go | 4 +- core/chains/evm/logpoller/orm.go | 4 +- core/chains/evm/logpoller/orm_test.go | 6 +- core/chains/evm/monitor/balance.go | 9 +-- core/chains/evm/monitor/balance_test.go | 14 ++--- core/chains/evm/txmgr/attempts.go | 2 +- core/chains/evm/txmgr/attempts_test.go | 10 +-- core/chains/evm/txmgr/broadcaster_test.go | 15 ++--- core/chains/evm/txmgr/builder.go | 2 +- core/chains/evm/txmgr/client.go | 6 +- core/chains/evm/txmgr/common.go | 2 +- core/chains/evm/txmgr/confirmer_test.go | 10 +-- core/chains/evm/txmgr/evm_tx_store.go | 6 +- core/chains/evm/txmgr/evm_tx_store_test.go | 10 +-- core/chains/evm/txmgr/nonce_syncer.go | 4 +- core/chains/evm/txmgr/nonce_syncer_test.go | 10 +-- core/chains/evm/txmgr/reaper_test.go | 4 +- core/chains/evm/txmgr/resender_test.go | 8 +-- core/chains/evm/txmgr/transmitchecker.go | 4 +- core/chains/evm/txmgr/transmitchecker_test.go | 5 +- core/chains/evm/txmgr/txmgr_test.go | 18 +++--- core/chains/{evm => legacyevm}/chain.go | 2 +- core/chains/{evm => legacyevm}/chain_test.go | 16 ++--- core/chains/{evm => legacyevm}/evm_txm.go | 2 +- core/chains/{evm => legacyevm}/mocks/chain.go | 0 .../mocks/legacy_chain_container.go | 30 ++++----- core/cmd/shell.go | 4 +- core/cmd/shell_local_test.go | 12 ++-- core/internal/cltest/cltest.go | 6 +- core/internal/cltest/mocks.go | 12 ++-- core/internal/testutils/evmtest/evmtest.go | 14 ++--- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- core/services/blockhashstore/delegate.go | 6 +- core/services/blockhashstore/delegate_test.go | 4 +- core/services/blockheaderfeeder/delegate.go | 6 +- .../mocks/relayer_chain_interoperators.go | 6 +- .../chainlink/relayer_chain_interoperators.go | 12 ++-- .../relayer_chain_interoperators_test.go | 6 +- core/services/chainlink/relayer_factory.go | 8 +-- core/services/directrequest/delegate.go | 6 +- core/services/feeds/service.go | 6 +- core/services/feeds/service_test.go | 4 +- core/services/fluxmonitorv2/delegate.go | 6 +- core/services/gateway/delegate.go | 6 +- core/services/gateway/handler_factory.go | 6 +- .../handlers/functions/handler.functions.go | 4 +- core/services/keeper/delegate.go | 6 +- core/services/keeper/upkeep_executer_test.go | 4 +- core/services/ocr/delegate.go | 8 +-- core/services/ocr/validate.go | 4 +- core/services/ocr2/delegate.go | 8 +-- .../services/ocr2/plugins/functions/plugin.go | 4 +- .../ocr2/plugins/ocr2keeper/evm20/registry.go | 4 +- .../ocr2/plugins/ocr2keeper/evm21/registry.go | 4 +- .../ocr2/plugins/ocr2keeper/evm21/services.go | 4 +- core/services/ocr2/plugins/ocr2keeper/util.go | 8 +-- core/services/pipeline/helpers_test.go | 6 +- core/services/pipeline/runner.go | 6 +- core/services/pipeline/task.estimategas.go | 4 +- core/services/pipeline/task.eth_call.go | 4 +- core/services/pipeline/task.eth_call_test.go | 4 +- core/services/pipeline/task.eth_tx.go | 4 +- core/services/relay/evm/evm.go | 12 ++-- core/services/relay/evm/functions.go | 6 +- core/services/relay/evm/loop_impl.go | 6 +- core/services/relay/evm/median.go | 4 +- .../relay/evm/mocks/loop_relay_adapter.go | 11 ++-- core/services/relay/evm/ocr2keeper.go | 8 +-- core/services/relay/evm/ocr2vrf.go | 8 +-- core/services/relay/evm/relayer_extender.go | 29 +++++---- core/services/vrf/delegate.go | 6 +- core/services/vrf/delegate_test.go | 4 +- core/services/vrf/v1/listener_v1.go | 4 +- core/services/vrf/v2/integration_v2_test.go | 4 +- core/services/vrf/v2/listener_v2.go | 6 +- core/services/vrf/v2/listener_v2_test.go | 6 +- core/web/common.go | 4 +- core/web/eth_keys_controller.go | 4 +- core/web/evm_transfer_controller.go | 4 +- core/web/resolver/eth_key.go | 4 +- core/web/resolver/eth_key_test.go | 6 +- core/web/resolver/resolver_test.go | 9 +-- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 168 files changed, 812 insertions(+), 783 deletions(-) rename core/chains/{evm => legacyevm}/chain.go (99%) rename core/chains/{evm => legacyevm}/chain_test.go (80%) rename core/chains/{evm => legacyevm}/evm_txm.go (99%) rename core/chains/{evm => legacyevm}/mocks/chain.go (100%) rename core/chains/{evm => legacyevm}/mocks/legacy_chain_container.go (73%) diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 48a4d37ad8c..3f72d04124f 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -12,12 +12,12 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -87,7 +87,7 @@ type multiNode[ sendonlys []SendOnlyNode[CHAIN_ID, RPC_CLIENT] chainID CHAIN_ID chainType config.ChainType - logger logger.Logger + lggr logger.Logger selectionMode string noNewHeadsThreshold time.Duration nodeSelector NodeSelector[CHAIN_ID, HEAD, RPC_CLIENT] @@ -119,7 +119,7 @@ func NewMultiNode[ HEAD types.Head[BLOCK_HASH], RPC_CLIENT RPC[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD], ]( - logger logger.Logger, + l logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, @@ -132,7 +132,8 @@ func NewMultiNode[ ) MultiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT] { nodeSelector := newNodeSelector(selectionMode, nodes) - lggr := logger.Named("MultiNode").With("chainID", chainID.String()) + lggr := logger.Named(l, "MultiNode") + lggr = logger.With(lggr, "chainID", chainID.String()) // Prometheus' default interval is 15s, set this to under 7.5s to avoid // aliasing (see: https://en.wikipedia.org/wiki/Nyquist_frequency) @@ -142,7 +143,7 @@ func NewMultiNode[ sendonlys: sendonlys, chainID: chainID, chainType: chainType, - logger: lggr, + lggr: lggr, selectionMode: selectionMode, noNewHeadsThreshold: noNewHeadsThreshold, nodeSelector: nodeSelector, @@ -153,7 +154,7 @@ func NewMultiNode[ reportInterval: reportInterval, } - c.logger.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) + c.lggr.Debugf("The MultiNode is configured to use NodeSelectionMode: %s", selectionMode) return c } @@ -197,11 +198,11 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP go c.runLoop() if c.leaseDuration.Seconds() > 0 && c.selectionMode != NodeSelectionModeRoundRobin { - c.logger.Infof("The MultiNode will switch to best node every %s", c.leaseDuration.String()) + c.lggr.Infof("The MultiNode will switch to best node every %s", c.leaseDuration.String()) c.wg.Add(1) go c.checkLeaseLoop() } else { - c.logger.Info("Best node switching is disabled") + c.lggr.Info("Best node switching is disabled") } return nil @@ -249,7 +250,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP c.activeNode = c.nodeSelector.Select() if c.activeNode == nil { - c.logger.Criticalw("No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name()) + logger.Criticalw(c.lggr, "No live RPC nodes available", "NodeSelectionMode", c.nodeSelector.Name()) errmsg := fmt.Errorf("no live nodes available for chain %s", c.chainID.String()) c.SvcErrBuffer.Append(errmsg) err = ErroringNodeError @@ -282,7 +283,7 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // Terminate client subscriptions. Services are responsible for reconnecting, which will be routed to the new // best node. Only terminate connections with more than 1 subscription to account for the aliveLoop subscription if n.State() == nodeStateAlive && n != bestNode && n.SubscribersCount() > 1 { - c.logger.Infof("Switching to best node from %q to %q", n.String(), bestNode.String()) + c.lggr.Infof("Switching to best node from %q to %q", n.String(), bestNode.String()) n.UnsubscribeAllExceptAliveLoop() } } @@ -351,13 +352,13 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP } live := total - dead - c.logger.Tracew(fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + logger.Tracew(c.lggr, fmt.Sprintf("MultiNode state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) if total == dead { rerr := fmt.Errorf("no primary nodes available: 0/%d nodes are alive", total) - c.logger.Criticalw(rerr.Error(), "nodeStates", nodeStates) + logger.Criticalw(c.lggr, rerr.Error(), "nodeStates", nodeStates) c.SvcErrBuffer.Append(rerr) } else if dead > 0 { - c.logger.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + c.lggr.Errorw(fmt.Sprintf("At least one primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) } } @@ -403,9 +404,9 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP defer wg.Done() err := n.RPC().BatchCallContext(ctx, b) if err != nil { - c.logger.Debugw("Secondary node BatchCallContext failed", "err", err) + c.lggr.Debugw("Secondary node BatchCallContext failed", "err", err) } else { - c.logger.Trace("Secondary node BatchCallContext success") + logger.Trace(c.lggr, "Secondary node BatchCallContext success") } }(n) } @@ -572,15 +573,15 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP defer c.wg.Done() txErr := n.RPC().SendTransaction(ctx, tx) - c.logger.Debugw("Sendonly node sent transaction", "name", n.String(), "tx", tx, "err", txErr) + c.lggr.Debugw("Sendonly node sent transaction", "name", n.String(), "tx", tx, "err", txErr) sendOnlyError := c.sendOnlyErrorParser(txErr) if sendOnlyError != Successful { - c.logger.Warnw("RPC returned error", "name", n.String(), "tx", tx, "err", txErr) + c.lggr.Warnw("RPC returned error", "name", n.String(), "tx", tx, "err", txErr) } }(n) }) if !ok { - c.logger.Debug("Cannot send transaction on sendonly node; MultiNode is stopped", "node", n.String()) + c.lggr.Debug("Cannot send transaction on sendonly node; MultiNode is stopped", "node", n.String()) } } if nodeError != nil { diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 4c0ebb1db93..45ffb745370 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -12,11 +12,11 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -43,7 +43,7 @@ type multiNodeOpts struct { func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { if opts.logger == nil { - opts.logger = logger.TestLogger(t) + opts.logger = logger.Test(t) } result := NewMultiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, @@ -211,7 +211,7 @@ func TestMultiNode_Report(t *testing.T) { chainID := types.RandomID() node1 := newHealthyNode(t, chainID) node2 := newNodeWithState(t, chainID, nodeStateOutOfSync) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, @@ -228,7 +228,7 @@ func TestMultiNode_Report(t *testing.T) { t.Parallel() chainID := types.RandomID() node := newNodeWithState(t, chainID, nodeStateOutOfSync) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, @@ -252,7 +252,7 @@ func TestMultiNode_CheckLease(t *testing.T) { t.Parallel() chainID := types.RandomID() node := newHealthyNode(t, chainID) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, @@ -268,7 +268,7 @@ func TestMultiNode_CheckLease(t *testing.T) { t.Parallel() chainID := types.RandomID() node := newHealthyNode(t, chainID) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeHighestHead, chainID: chainID, @@ -290,7 +290,7 @@ func TestMultiNode_CheckLease(t *testing.T) { bestNode := newHealthyNode(t, chainID) nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) nodeSelector.On("Select").Return(bestNode) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeHighestHead, chainID: chainID, @@ -402,7 +402,7 @@ func TestMultiNode_selectNode(t *testing.T) { t.Run("No active nodes - reports critical error", func(t *testing.T) { t.Parallel() chainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: chainID, @@ -541,7 +541,7 @@ func TestMultiNode_BatchCallContextAll(t *testing.T) { mainNode.On("RPC").Return(okRPC) nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) nodeSelector.On("Select").Return(mainNode).Once() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: types.RandomID(), @@ -610,7 +610,7 @@ func TestMultiNode_SendTransaction(t *testing.T) { mainNode.On("RPC").Return(okRPC) nodeSelector := newMockNodeSelector[types.ID, types.Head[Hashable], multiNodeRPCClient](t) nodeSelector.On("Select").Return(mainNode).Once() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) mn := newTestMultiNode(t, multiNodeOpts{ selectionMode: NodeSelectionModeRoundRobin, chainID: types.RandomID(), diff --git a/common/client/node.go b/common/client/node.go index 5faaa5da627..b2d3a33a5b8 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -11,10 +11,10 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -135,14 +135,15 @@ func NewNode[ n.http = httpuri } n.nodeCtx, n.cancelNodeCtx = context.WithCancel(context.Background()) - lggr = lggr.Named("Node").With( + lggr = logger.Named(lggr, "Node") + lggr = logger.With(lggr, "nodeTier", Primary.String(), "nodeName", name, "node", n.String(), "chainID", chainID, "nodeOrder", n.order, ) - n.lfcLog = lggr.Named("Lifecycle") + n.lfcLog = logger.Named(lggr, "Lifecycle") n.stateLatestBlockNumber = -1 n.rpc = rpc n.chainFamily = chainFamily diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 4193560e296..9b47412766b 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -88,8 +89,9 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold() pollInterval := n.nodePoolCfg.PollInterval() - lggr := n.lfcLog.Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) - lggr.Tracew("Alive loop starting", "nodeState", n.State()) + lggr := logger.Named(n.lfcLog, "Alive") + lggr = logger.With(lggr, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) + logger.Tracew(lggr, "Alive loop starting", "nodeState", n.State()) headsC := make(chan HEAD) sub, err := n.rpc.Subscribe(n.nodeCtx, headsC, rpcSubscriptionMethodNewHeads) @@ -140,7 +142,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { case <-pollCh: var version string promPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + logger.Tracew(lggr, "Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) ctx, cancel := context.WithTimeout(n.nodeCtx, pollInterval) version, err := n.RPC().ClientVersion(ctx) cancel() @@ -160,7 +162,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.State()) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) + logger.Criticalf(lggr, "RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) continue } } @@ -172,7 +174,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { // note: there must be another live node for us to be out of sync lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) if liveNodes < 2 { - lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) + logger.Criticalf(lggr, "RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) continue } n.declareOutOfSync(n.isOutOfSync) @@ -185,13 +187,13 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { return } promPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Got head", "head", bh) + logger.Tracew(lggr, "Got head", "head", bh) if bh.BlockNumber() > highestReceivedBlockNumber { promPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.BlockNumber())) - lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + logger.Tracew(lggr, "Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) highestReceivedBlockNumber = bh.BlockNumber() } else { - lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) + logger.Tracew(lggr, "Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.BlockNumber(), "nodeState", n.State()) } if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) @@ -207,7 +209,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) + logger.Criticalf(lggr, "RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) // We don't necessarily want to wait the full timeout to check again, we should // check regularly and log noisily in this state outOfSyncT.Reset(zombieNodeCheckInterval(n.noNewHeadsThreshold)) @@ -273,7 +275,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td outOfSyncAt := time.Now() - lggr := n.lfcLog.Named("OutOfSync") + lggr := logger.Named(n.lfcLog, "OutOfSync") lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) // Need to redial since out-of-sync nodes are automatically disconnected @@ -290,7 +292,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td return } - lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + logger.Tracew(lggr, "Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) ch := make(chan HEAD) sub, err := n.rpc.Subscribe(n.nodeCtx, ch, rpcSubscriptionMethodNewHeads) @@ -322,7 +324,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td case <-time.After(zombieNodeCheckInterval(n.noNewHeadsThreshold)): if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 1 { - lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") + logger.Critical(lggr, "RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") n.declareInSync() return } @@ -352,7 +354,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { unreachableAt := time.Now() - lggr := n.lfcLog.Named("Unreachable") + lggr := logger.Named(n.lfcLog, "Unreachable") lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) dialRetryBackoff := utils.NewRedialBackoff() @@ -362,7 +364,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) unreachableLoop() { case <-n.nodeCtx.Done(): return case <-time.After(dialRetryBackoff.Duration()): - lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) + logger.Tracew(lggr, "Trying to re-dial RPC node", "nodeState", n.State()) err := n.rpc.Dial(n.nodeCtx) if err != nil { @@ -408,7 +410,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) invalidChainIDLoop() { invalidAt := time.Now() - lggr := n.lfcLog.Named("InvalidChainID") + lggr := logger.Named(n.lfcLog, "InvalidChainID") lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) chainIDRecheckBackoff := utils.NewRedialBackoff() diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 0dffe935fe0..51b44ac516d 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -11,11 +11,11 @@ import ( "github.com/stretchr/testify/mock" "go.uber.org/zap" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/common/types/mocks" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -60,7 +60,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) node := newDialedNode(t, testNodeOpts{ rpc: rpc, lggr: lggr, @@ -94,7 +94,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("Stays alive and waits for signal", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, rpc: rpc, @@ -109,7 +109,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("stays alive while below pollFailureThreshold and resets counter on success", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -151,7 +151,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("with threshold poll failures, transitions to unreachable", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -177,7 +177,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("with threshold poll failures, but we are the last node alive, forcibly keeps it alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const pollFailureThreshold = 3 node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -200,7 +200,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("when behind more than SyncThreshold, transitions to out of sync", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const syncThreshold = 10 node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -231,7 +231,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) const syncThreshold = 10 node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ @@ -254,7 +254,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("when behind but SyncThreshold=0, stay alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{ pollInterval: tests.TestInterval, @@ -302,7 +302,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newSubscribedNode(t, testNodeOpts{ config: testNodeConfig{}, lggr: lggr, @@ -329,7 +329,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { close(ch) }).Return(sub, nil).Once() rpc.On("SetAliveLoopSub", sub).Once() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newDialedNode(t, testNodeOpts{ lggr: lggr, config: testNodeConfig{}, @@ -426,7 +426,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -531,7 +531,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newAliveNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -560,7 +560,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) node := newAliveNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -591,7 +591,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -627,7 +627,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ noNewHeadsThreshold: tests.TestInterval, rpc: rpc, @@ -682,7 +682,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -698,7 +698,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newAliveNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -775,7 +775,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -797,7 +797,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.NewIDFromInt(10) rpcChainID := types.NewIDFromInt(11) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newDialedNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -846,7 +846,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, @@ -868,7 +868,7 @@ func TestUnit_NodeLifecycle_start(t *testing.T) { t.Parallel() rpc := newMockNodeClient[types.ID, Head](t) nodeChainID := types.RandomID() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) node := newNode(t, testNodeOpts{ rpc: rpc, chainID: nodeChainID, diff --git a/common/client/node_test.go b/common/client/node_test.go index 0438e11e612..7b6a38e3951 100644 --- a/common/client/node_test.go +++ b/common/client/node_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type testNodeConfig struct { @@ -52,7 +52,7 @@ type testNodeOpts struct { func newTestNode(t *testing.T, opts testNodeOpts) testNode { if opts.lggr == nil { - opts.lggr = logger.TestLogger(t) + opts.lggr = logger.Test(t) } if opts.name == "" { diff --git a/common/client/send_only_node.go b/common/client/send_only_node.go index 904916122f1..b63e93b703d 100644 --- a/common/client/send_only_node.go +++ b/common/client/send_only_node.go @@ -6,10 +6,10 @@ import ( "net/url" "sync" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) //go:generate mockery --quiet --name sendOnlyClient --structname mockSendOnlyClient --filename "mock_send_only_client_test.go" --inpackage --case=underscore @@ -75,7 +75,8 @@ func NewSendOnlyNode[ ) SendOnlyNode[CHAIN_ID, RPC] { s := new(sendOnlyNode[CHAIN_ID, RPC]) s.name = name - s.log = lggr.Named("SendOnlyNode").Named(name).With( + s.log = logger.Named(logger.Named(lggr, "SendOnlyNode"), name) + s.log = logger.With(s.log, "nodeTier", "sendonly", ) s.rpc = rpc diff --git a/common/client/send_only_node_test.go b/common/client/send_only_node_test.go index 3034b3f0a11..459f923cba8 100644 --- a/common/client/send_only_node_test.go +++ b/common/client/send_only_node_test.go @@ -11,10 +11,10 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestNewSendOnlyNode(t *testing.T) { @@ -25,7 +25,7 @@ func TestNewSendOnlyNode(t *testing.T) { u, err := url.Parse(fmt.Sprintf(urlFormat, password)) require.NoError(t, err) redacted := fmt.Sprintf(urlFormat, "xxxxx") - lggr := logger.TestLogger(t) + lggr := logger.Test(t) name := "TestNewSendOnlyNode" chainID := types.RandomID() client := newMockSendOnlyClient[types.ID](t) @@ -42,7 +42,7 @@ func TestStartSendOnlyNode(t *testing.T) { t.Parallel() t.Run("becomes unusable if initial dial fails", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() expectedError := errors.New("some http error") @@ -58,7 +58,7 @@ func TestStartSendOnlyNode(t *testing.T) { }) t.Run("Default ChainID(0) produces warn and skips checks", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() client.On("DialHTTP").Return(nil).Once() @@ -73,7 +73,7 @@ func TestStartSendOnlyNode(t *testing.T) { }) t.Run("Can recover from chainID verification failure", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() client.On("DialHTTP").Return(nil) @@ -97,7 +97,7 @@ func TestStartSendOnlyNode(t *testing.T) { }) t.Run("Can recover from chainID mismatch", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() client.On("DialHTTP").Return(nil).Once() @@ -120,7 +120,7 @@ func TestStartSendOnlyNode(t *testing.T) { }) t.Run("Start with Random ChainID", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) client := newMockSendOnlyClient[types.ID](t) client.On("Close").Once() client.On("DialHTTP").Return(nil).Once() diff --git a/common/fee/models.go b/common/fee/models.go index f980c73fc1b..1fcb55c5309 100644 --- a/common/fee/models.go +++ b/common/fee/models.go @@ -5,8 +5,8 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/chains/label" - "github.com/smartcontractkit/chainlink/v2/core/logger" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/common/headtracker/head_broadcaster.go b/common/headtracker/head_broadcaster.go index 62b2f47b68a..0e676f864fa 100644 --- a/common/headtracker/head_broadcaster.go +++ b/common/headtracker/head_broadcaster.go @@ -7,9 +7,10 @@ import ( "sync" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -45,7 +46,7 @@ func NewHeadBroadcaster[ lggr logger.Logger, ) *HeadBroadcaster[H, BLOCK_HASH] { return &HeadBroadcaster[H, BLOCK_HASH]{ - logger: lggr.Named("HeadBroadcaster"), + logger: logger.Named(lggr, "HeadBroadcaster"), callbacks: make(callbackSet[H, BLOCK_HASH]), mailbox: utils.NewSingleMailbox[H](), chClose: make(chan struct{}), diff --git a/common/headtracker/head_listener.go b/common/headtracker/head_listener.go index a3f262f4b73..2013895d0b8 100644 --- a/common/headtracker/head_listener.go +++ b/common/headtracker/head_listener.go @@ -9,11 +9,11 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -59,7 +59,7 @@ func NewHeadListener[ return &HeadListener[HTH, S, ID, BLOCK_HASH]{ config: config, client: client, - logger: lggr.Named("HeadListener"), + logger: logger.Named(lggr, "HeadListener"), chStop: chStop, } } diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 810e749a2dc..34a319e3c1c 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -10,12 +10,12 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/config" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -75,7 +75,7 @@ func NewHeadTracker[ getNilHead func() HTH, ) types.HeadTracker[HTH, BLOCK_HASH] { chStop := make(chan struct{}) - lggr = lggr.Named("HeadTracker") + lggr = logger.Named(lggr, "HeadTracker") return &HeadTracker[HTH, S, ID, BLOCK_HASH]{ headBroadcaster: headBroadcaster, client: client, @@ -229,7 +229,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context prevUnFinalizedHead := prevHead.BlockNumber() - int64(ht.config.FinalityDepth()) if head.BlockNumber() < prevUnFinalizedHead { promOldHead.WithLabelValues(ht.chainID.String()).Inc() - ht.log.Criticalf("Got very old block with number %d (highest seen was %d). This is a problem and either means a very deep re-org occurred, one of the RPC nodes has gotten far out of sync, or the chain went backwards in block numbers. This node may not function correctly without manual intervention.", head.BlockNumber(), prevHead.BlockNumber()) + logger.Criticalf(ht.log, "Got very old block with number %d (highest seen was %d). This is a problem and either means a very deep re-org occurred, one of the RPC nodes has gotten far out of sync, or the chain went backwards in block numbers. This node may not function correctly without manual intervention.", head.BlockNumber(), prevHead.BlockNumber()) ht.SvcErrBuffer.Append(errors.New("got very old block")) } } @@ -312,7 +312,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) backfill(ctx context.Context, hea } mark := time.Now() fetched := 0 - l := ht.log.With("blockNumber", headBlockNumber, + l := logger.With(ht.log, "blockNumber", headBlockNumber, "n", headBlockNumber-baseHeight, "fromBlockHeight", baseHeight, "toBlockHeight", headBlockNumber-1) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index d9a72e367ac..cff5746a9e0 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -16,12 +16,13 @@ import ( "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/chains/label" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -107,7 +108,7 @@ type Broadcaster[ FEE feetypes.Fee, ] struct { services.StateMachine - logger logger.Logger + lggr logger.Logger txStore txmgrtypes.TransactionStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, SEQ, FEE] client txmgrtypes.TransactionClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] @@ -164,14 +165,14 @@ func NewBroadcaster[ keystore txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ], txAttemptBuilder txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], sequenceSyncer SequenceSyncer[ADDR, TX_HASH, BLOCK_HASH, SEQ], - logger logger.Logger, + lggr logger.Logger, checkerFactory TransmitCheckerFactory[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], autoSyncSequence bool, generateNextSequence types.GenerateNextSequenceFunc[SEQ], ) *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { - logger = logger.Named("Broadcaster") + lggr = logger.Named(lggr, "Broadcaster") b := &Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{ - logger: logger, + lggr: lggr, txStore: txStore, client: client, TxAttemptBuilder: txAttemptBuilder, @@ -213,9 +214,9 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) star } if len(eb.enabledAddresses) > 0 { - eb.logger.Debugw(fmt.Sprintf("Booting with %d keys", len(eb.enabledAddresses)), "keys", eb.enabledAddresses) + eb.lggr.Debugw(fmt.Sprintf("Booting with %d keys", len(eb.enabledAddresses)), "keys", eb.enabledAddresses) } else { - eb.logger.Warnf("Chain %s does not have any keys, no transactions will be sent on this chain", eb.chainID.String()) + eb.lggr.Warnf("Chain %s does not have any keys, no transactions will be sent on this chain", eb.chainID.String()) } eb.chStop = make(chan struct{}) eb.wg = sync.WaitGroup{} @@ -259,7 +260,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) SetR } func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Name() string { - return eb.logger.Name() + return eb.lggr.Name() } func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) HealthReport() map[string]error { @@ -280,7 +281,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Trig default: } } else { - eb.logger.Debugf("Unstarted; ignoring trigger for %s", addr) + eb.lggr.Debugf("Unstarted; ignoring trigger for %s", addr) } } @@ -314,7 +315,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) getS if err == nil { return seq, nil } - eb.logger.Criticalw("failed to retrieve next sequence from on-chain for address: ", "address", address.String()) + logger.Criticalw(eb.lggr, "failed to retrieve next sequence from on-chain for address: ", "address", address.String()) return seq, err } @@ -342,13 +343,13 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) moni defer cancel() if eb.autoSyncSequence { - eb.logger.Debugw("Auto-syncing sequence", "address", addr.String()) + eb.lggr.Debugw("Auto-syncing sequence", "address", addr.String()) eb.SyncSequence(ctx, addr) if ctx.Err() != nil { return } } else { - eb.logger.Debugw("Skipping sequence auto-sync", "address", addr.String()) + eb.lggr.Debugw("Skipping sequence auto-sync", "address", addr.String()) } // errorRetryCh allows retry on exponential backoff in case of timeout or @@ -361,7 +362,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) moni retryable, err := eb.processUnstartedTxsImpl(ctx, addr) if err != nil { - eb.logger.Errorw("Error occurred while handling tx queue in ProcessUnstartedTxs", "err", err) + eb.lggr.Errorw("Error occurred while handling tx queue in ProcessUnstartedTxs", "err", err) } // On retryable errors we implement exponential backoff retries. This // handles intermittent connectivity, remote RPC races, timing issues etc @@ -402,7 +403,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Sync localSequence, err := eb.GetNextSequence(ctx, addr) // Address not found in map so skip sync if err != nil { - eb.logger.Criticalw("Failed to retrieve local next sequence for address", "address", addr.String(), "err", err) + logger.Criticalw(eb.lggr, "Failed to retrieve local next sequence for address", "address", addr.String(), "err", err) return } @@ -417,16 +418,16 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Sync newNextSequence, err := eb.sequenceSyncer.Sync(ctx, addr, localSequence) if err != nil { if attempt > 5 { - eb.logger.Criticalw("Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) + logger.Criticalw(eb.lggr, "Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) eb.SvcErrBuffer.Append(err) } else { - eb.logger.Warnw("Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) + eb.lggr.Warnw("Failed to sync with on-chain sequence", "address", addr.String(), "attempt", attempt, "err", err) } continue } // Found new sequence to use from on-chain if localSequence.String() != newNextSequence.String() { - eb.logger.Infow("Fast-forward sequence", "address", addr, "newNextSequence", newNextSequence, "oldNextSequence", localSequence) + eb.lggr.Infow("Fast-forward sequence", "address", addr, "newNextSequence", newNextSequence, "oldNextSequence", localSequence) // Set new sequence in the map eb.SetNextSequence(addr, newNextSequence) } @@ -451,7 +452,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc mark := time.Now() defer func() { if n > 0 { - eb.logger.Debugw("Finished processUnstartedTxs", "address", fromAddress, "time", time.Since(mark), "n", n, "id", "broadcaster") + eb.lggr.Debugw("Finished processUnstartedTxs", "address", fromAddress, "time", time.Since(mark), "n", n, "id", "broadcaster") } }() @@ -471,7 +472,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc if err != nil { return true, errors.Wrap(err, "CountUnstartedTransactions failed") } - eb.logger.Warnw(fmt.Sprintf(`Transaction throttling; %d transactions in-flight and %d unstarted transactions pending (maximum number of in-flight transactions is %d per key). %s`, nUnconfirmed, nUnstarted, maxInFlightTransactions, label.MaxInFlightTransactionsWarning), "maxInFlightTransactions", maxInFlightTransactions, "nUnconfirmed", nUnconfirmed, "nUnstarted", nUnstarted) + eb.lggr.Warnw(fmt.Sprintf(`Transaction throttling; %d transactions in-flight and %d unstarted transactions pending (maximum number of in-flight transactions is %d per key). %s`, nUnconfirmed, nUnstarted, maxInFlightTransactions, label.MaxInFlightTransactionsWarning), "maxInFlightTransactions", maxInFlightTransactions, "nUnconfirmed", nUnconfirmed, "nUnstarted", nUnstarted) select { case <-time.After(InFlightTransactionRecheckInterval): case <-ctx.Done(): @@ -490,13 +491,13 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) proc n++ var a txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] var retryable bool - a, _, _, retryable, err = eb.NewTxAttempt(ctx, *etx, eb.logger) + a, _, _, retryable, err = eb.NewTxAttempt(ctx, *etx, eb.lggr) if err != nil { return retryable, errors.Wrap(err, "processUnstartedTxs failed on NewAttempt") } if err := eb.txStore.UpdateTxUnstartedToInProgress(ctx, etx, &a); errors.Is(err, ErrTxRemoved) { - eb.logger.Debugw("tx removed", "txID", etx.ID, "subject", etx.Subject) + eb.lggr.Debugw("tx removed", "txID", etx.ID, "subject", etx.Subject) continue } else if err != nil { return true, errors.Wrap(err, "processUnstartedTxs failed on UpdateTxUnstartedToInProgress") @@ -540,7 +541,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand return errors.Wrap(err, "building transmit checker"), false } - lgr := etx.GetLogger(eb.logger.With("fee", attempt.TxFee)) + lgr := etx.GetLogger(logger.With(eb.lggr, "fee", attempt.TxFee)) // If the transmit check does not complete within the timeout, the transaction will be sent // anyway. @@ -650,14 +651,14 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) hand // If there is only one RPC node, or all RPC nodes have the same // configured cap, this transaction will get stuck and keep repeating // forever until the issue is resolved. - lgr.Criticalw(`RPC node rejected this tx as outside Fee Cap`) + logger.Criticalw(lgr, `RPC node rejected this tx as outside Fee Cap`) fallthrough default: // Every error that doesn't fall under one of the above categories will be treated as Unknown. fallthrough case client.Unknown: eb.SvcErrBuffer.Append(err) - lgr.Criticalw(`Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+ + logger.Criticalw(lgr, `Unknown error occurred while handling tx queue in ProcessUnstartedTxs. This chain/RPC client may not be supported. `+ `Urgent resolution required, Chainlink is currently operating in a degraded state and may miss transactions`, "err", err, "etx", etx, "attempt", attempt) nextSequence, e := eb.client.PendingSequenceAt(ctx, etx.FromAddress) if e != nil { @@ -717,7 +718,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) next } func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryAgainBumpingGas(ctx context.Context, lgr logger.Logger, txError error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time) (err error, retryable bool) { - lgr.With( + logger.With(lgr, "sendError", txError, "attemptFee", attempt.TxFee, "maxGasPriceConfig", eb.feeConfig.MaxFeePrice(), @@ -738,7 +739,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryA func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) tryAgainWithNewEstimation(ctx context.Context, lgr logger.Logger, txError error, etx txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], initialBroadcastAt time.Time) (err error, retryable bool) { if attempt.TxType == 0x2 { err = errors.Errorf("re-estimation is not supported for EIP-1559 transactions. Node returned error: %v. This is a bug", txError.Error()) - logger.Sugared(eb.logger).AssumptionViolation(err.Error()) + logger.Sugared(eb.lggr).AssumptionViolation(err.Error()) return err, false } @@ -807,7 +808,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetN return seq, nil } - eb.logger.Infow("address not found in local next sequence map. Attempting to search and populate sequence.", "address", address.String()) + eb.lggr.Infow("address not found in local next sequence map. Attempting to search and populate sequence.", "address", address.String()) // Check if address is in the enabled address list if !slices.Contains(eb.enabledAddresses, address) { return seq, fmt.Errorf("address disabled: %s", address) diff --git a/common/txmgr/confirmer.go b/common/txmgr/confirmer.go index bf356115828..a56768ce206 100644 --- a/common/txmgr/confirmer.go +++ b/common/txmgr/confirmer.go @@ -15,13 +15,14 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-common/pkg/chains/label" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -160,7 +161,7 @@ func NewConfirmer[ lggr logger.Logger, isReceiptNil func(R) bool, ) *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { - lggr = lggr.Named("Confirmer") + lggr = logger.Named(lggr, "Confirmer") return &Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ txStore: txStore, lggr: lggr, @@ -517,7 +518,8 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat } } - lggr := ec.lggr.Named("BatchFetchReceipts").With("blockNum", blockNum) + lggr := logger.Named(ec.lggr, "BatchFetchReceipts") + lggr = logger.With(lggr, "blockNum", blockNum) txReceipts, txErrs, err := ec.client.BatchGetReceipts(ctx, attempts) if err != nil { @@ -529,8 +531,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat receipt := txReceipts[i] err := txErrs[i] - l := logger.Sugared(attempt.Tx.GetLogger(lggr).With( - "txHash", attempt.Hash.String(), "txAttemptID", attempt.ID, + l := logger.Sugared(logger.With(attempt.Tx.GetLogger(lggr), "txHash", attempt.Hash.String(), "txAttemptID", attempt.ID, "txID", attempt.TxID, "err", err, "sequence", attempt.Tx.Sequence, )) @@ -551,7 +552,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) bat continue } - l = logger.Sugared(l.With("blockHash", receipt.GetBlockHash().String(), "status", receipt.GetStatus(), "transactionIndex", receipt.GetTransactionIndex())) + l = logger.Sugared(logger.With(l, "blockHash", receipt.GetBlockHash().String(), "status", receipt.GetStatus(), "transactionIndex", receipt.GetTransactionIndex())) if receipt.IsUnmined() { l.Debug("Got receipt for transaction but it's still in the mempool and not included in a block yet") @@ -843,7 +844,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han return errors.Wrap(err, "could not bump gas for terminally underpriced transaction") } promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() - lggr.With( + logger.With(lggr, "sendError", sendError, "maxGasPriceConfig", ec.feeConfig.MaxFeePrice(), "previousAttempt", attempt, @@ -864,7 +865,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) han // Should NEVER be fatal this is an invariant violation. The // Broadcaster can never create a TxAttempt that will // fatally error. - lggr.Criticalw("Invariant violation: fatal error while re-attempting transaction", + logger.Criticalw(lggr, "Invariant violation: fatal error while re-attempting transaction", "err", sendError, "fee", attempt.TxFee, "feeLimit", etx.FeeLimit, diff --git a/common/txmgr/reaper.go b/common/txmgr/reaper.go index 7286efa3a80..385a9a17c3d 100644 --- a/common/txmgr/reaper.go +++ b/common/txmgr/reaper.go @@ -5,11 +5,11 @@ import ( "sync/atomic" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -33,7 +33,7 @@ func NewReaper[CHAIN_ID types.ID](lggr logger.Logger, store txmgrtypes.TxHistory config, txConfig, chainID, - lggr.Named("Reaper"), + logger.Named(lggr, "Reaper"), atomic.Int64{}, make(chan struct{}, 1), make(services.StopChan), diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index e604a960bf8..ce770055609 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -7,11 +7,12 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/chains/label" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -87,7 +88,7 @@ func NewResender[ pollInterval, config, txConfig, - lggr.Named("Resender"), + logger.Named(lggr, "Resender"), make(map[string]time.Time), ctx, cancel, diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index b49c2b72f15..63bf039d8f7 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -11,12 +11,12 @@ import ( "github.com/google/uuid" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -331,7 +331,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() select { case <-time.After(backoff.Duration()): if err := b.broadcaster.startInternal(); err != nil { - b.logger.Criticalw("Failed to start Broadcaster", "err", err) + logger.Criticalw(b.logger, "Failed to start Broadcaster", "err", err) b.SvcErrBuffer.Append(err) continue } @@ -350,7 +350,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() select { case <-time.After(backoff.Duration()): if err := b.confirmer.startInternal(); err != nil { - b.logger.Criticalw("Failed to start Confirmer", "err", err) + logger.Criticalw(b.logger, "Failed to start Confirmer", "err", err) b.SvcErrBuffer.Append(err) continue } @@ -408,7 +408,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() } enabledAddresses, err := b.keyStore.EnabledAddressesForChain(b.chainID) if err != nil { - b.logger.Criticalf("Failed to reload key states after key change") + logger.Criticalf(b.logger, "Failed to reload key states after key change") b.SvcErrBuffer.Append(err) continue } diff --git a/common/txmgr/types/client.go b/common/txmgr/types/client.go index 58c1b6f6ad2..b44c41e4176 100644 --- a/common/txmgr/types/client.go +++ b/common/txmgr/types/client.go @@ -6,10 +6,10 @@ import ( "math/big" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/client" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // TxmClient is a superset of all the methods needed for the txm diff --git a/common/txmgr/types/mocks/tx_attempt_builder.go b/common/txmgr/types/mocks/tx_attempt_builder.go index f49b0025ae7..0f3d3e3fba1 100644 --- a/common/txmgr/types/mocks/tx_attempt_builder.go +++ b/common/txmgr/types/mocks/tx_attempt_builder.go @@ -5,8 +5,8 @@ package mocks import ( context "context" + logger "github.com/smartcontractkit/chainlink-common/pkg/logger" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" - logger "github.com/smartcontractkit/chainlink/v2/core/logger" mock "github.com/stretchr/testify/mock" diff --git a/common/txmgr/types/tx.go b/common/txmgr/types/tx.go index 78f6fba4592..b8a16561d88 100644 --- a/common/txmgr/types/tx.go +++ b/common/txmgr/types/tx.go @@ -13,10 +13,10 @@ import ( "github.com/pkg/errors" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" clnull "github.com/smartcontractkit/chainlink/v2/core/null" ) @@ -250,7 +250,7 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetMeta() (*TxMeta[A // GetLogger returns a new logger with metadata fields. func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger.Logger) logger.Logger { - lgr = lgr.With( + lgr = logger.With(lgr, "txID", e.ID, "sequence", e.Sequence, "checker", e.TransmitChecker, @@ -264,15 +264,15 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger } if meta != nil { - lgr = lgr.With("jobID", meta.JobID) + lgr = logger.With(lgr, "jobID", meta.JobID) if meta.RequestTxHash != nil { - lgr = lgr.With("requestTxHash", *meta.RequestTxHash) + lgr = logger.With(lgr, "requestTxHash", *meta.RequestTxHash) } if meta.RequestID != nil { id := *meta.RequestID - lgr = lgr.With("requestID", new(big.Int).SetBytes(id.Bytes()).String()) + lgr = logger.With(lgr, "requestID", new(big.Int).SetBytes(id.Bytes()).String()) } if len(meta.RequestIDs) != 0 { @@ -280,33 +280,33 @@ func (e *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) GetLogger(lgr logger for _, id := range meta.RequestIDs { ids = append(ids, new(big.Int).SetBytes(id.Bytes()).String()) } - lgr = lgr.With("requestIDs", strings.Join(ids, ",")) + lgr = logger.With(lgr, "requestIDs", strings.Join(ids, ",")) } if meta.UpkeepID != nil { - lgr = lgr.With("upkeepID", *meta.UpkeepID) + lgr = logger.With(lgr, "upkeepID", *meta.UpkeepID) } if meta.SubID != nil { - lgr = lgr.With("subID", *meta.SubID) + lgr = logger.With(lgr, "subID", *meta.SubID) } if meta.MaxLink != nil { - lgr = lgr.With("maxLink", *meta.MaxLink) + lgr = logger.With(lgr, "maxLink", *meta.MaxLink) } if meta.FwdrDestAddress != nil { - lgr = lgr.With("FwdrDestAddress", *meta.FwdrDestAddress) + lgr = logger.With(lgr, "FwdrDestAddress", *meta.FwdrDestAddress) } if len(meta.MessageIDs) > 0 { for _, mid := range meta.MessageIDs { - lgr = lgr.With("messageID", mid) + lgr = logger.With(lgr, "messageID", mid) } } if len(meta.SeqNumbers) > 0 { - lgr = lgr.With("SeqNumbers", meta.SeqNumbers) + lgr = logger.With(lgr, "SeqNumbers", meta.SeqNumbers) } } diff --git a/common/txmgr/types/tx_attempt_builder.go b/common/txmgr/types/tx_attempt_builder.go index 75712fc0c37..383b6d862f0 100644 --- a/common/txmgr/types/tx_attempt_builder.go +++ b/common/txmgr/types/tx_attempt_builder.go @@ -3,10 +3,10 @@ package types import ( "context" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // TxAttemptBuilder takes the base unsigned transaction + optional parameters (tx type, gas parameters) diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index 0f15b35ee9e..d17c55f2e4f 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -11,11 +11,12 @@ import ( "github.com/ethereum/go-ethereum/rpc" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) var _ Client = (*chainClient)(nil) diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index 5263c74de15..91c8659d8c5 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -7,11 +7,12 @@ import ( "time" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/common/config" htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum" diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go index 4cb505dc9eb..143a5f8806f 100644 --- a/core/chains/evm/client/errors.go +++ b/core/chains/evm/client/errors.go @@ -10,9 +10,9 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // fatal means this transaction can never be accepted even with a different nonce or higher gas price @@ -419,7 +419,7 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro return commonclient.Successful, err } if sendError.Fatal() { - lggr.Criticalw("Fatal error sending transaction", "err", sendError, "etx", tx) + logger.Criticalw(lggr, "Fatal error sending transaction", "err", sendError, "etx", tx) // Attempt is thrown away in this case; we don't need it since it never got accepted by a node return commonclient.Fatal, err } @@ -462,7 +462,7 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro return commonclient.Retryable, err } if sendError.IsInsufficientEth() { - lggr.Criticalw(fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+ + logger.Criticalw(lggr, fmt.Sprintf("Tx %x with type 0x%d was rejected due to insufficient eth: %s\n"+ "ACTION REQUIRED: Chainlink wallet with address 0x%x is OUT OF FUNDS", tx.Hash(), tx.Type(), sendError.Error(), fromAddress, ), "err", sendError) @@ -472,7 +472,7 @@ func ClassifySendError(err error, lggr logger.Logger, tx *types.Transaction, fro return commonclient.Retryable, errors.Wrapf(sendError, "timeout while sending transaction %s", tx.Hash().Hex()) } if sendError.IsTxFeeExceedsCap() { - lggr.Criticalw(fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), + logger.Criticalw(lggr, fmt.Sprintf("Sending transaction failed: %s", label.RPCTxFeeCapConfiguredIncorrectlyWarning), "etx", tx, "err", sendError, "id", "RPCTxFeeCapExceeded", diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index b1d477b1a29..dce825f4f71 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -9,11 +9,11 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -43,7 +43,7 @@ func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeads return nil, errors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) } - lggr := logger.TestLogger(t) + lggr := logger.Test(t) n := NewNode(nodePoolCfg, noNewHeadsThreshold, lggr, *parsed, rpcHTTPURL, "eth-primary-0", id, chainID, 1) n.(*node).setLatestReceived(0, utils.NewBigI(0)) primaries := []Node{n} @@ -87,7 +87,7 @@ func NewChainClientWithTestNode( return nil, errors.Errorf("ethereum url scheme must be websocket: %s", parsed.String()) } - lggr := logger.TestLogger(t) + lggr := logger.Test(t) rpc := NewRPCClient(lggr, *parsed, rpcHTTPURL, "eth-primary-rpc-0", id, chainID, commonclient.Primary) n := commonclient.NewNode[*big.Int, *evmtypes.Head, RPCCLient]( @@ -120,7 +120,7 @@ func NewChainClientWithEmptyNode( chainID *big.Int, ) Client { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) var chainType commonconfig.ChainType c := NewChainClient(lggr, selectionMode, leaseDuration, noNewHeadsThreshold, nil, nil, chainID, chainType) diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index b3ce489cf50..8bae566e273 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -19,10 +19,11 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -192,7 +193,8 @@ func NewNode(nodeCfg config.NodePool, noNewHeadsThreshold time.Duration, lggr lo } n.chStopInFlight = make(chan struct{}) n.nodeCtx, n.cancelNodeCtx = context.WithCancel(context.Background()) - lggr = lggr.Named("Node").With( + lggr = logger.Named(lggr, "Node") + lggr = logger.With(lggr, "nodeTier", "primary", "nodeName", name, "node", n.String(), @@ -200,8 +202,8 @@ func NewNode(nodeCfg config.NodePool, noNewHeadsThreshold time.Duration, lggr lo "nodeOrder", n.order, "mode", n.getNodeMode(), ) - n.lfcLog = lggr.Named("Lifecycle") - n.rpcLog = lggr.Named("RPC") + n.lfcLog = logger.Named(lggr, "Lifecycle") + n.rpcLog = logger.Named(lggr, "RPC") n.stateLatestBlockNumber = -1 return n @@ -259,9 +261,9 @@ func (n *node) dial(callerCtx context.Context) error { defer cancel() promEVMPoolRPCNodeDials.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr := n.lfcLog.With("wsuri", n.ws.uri.Redacted()) + lggr := logger.With(n.lfcLog, "wsuri", n.ws.uri.Redacted()) if n.http != nil { - lggr = lggr.With("httpuri", n.http.uri.Redacted()) + lggr = logger.With(lggr, "httpuri", n.http.uri.Redacted()) } lggr.Debugw("RPC dial: evmclient.Client#dial") @@ -448,7 +450,7 @@ func (n *node) CallContext(ctx context.Context, result interface{}, method strin return err } defer cancel() - lggr := n.newRqLggr().With( + lggr := logger.With(n.newRqLggr(), "method", method, "args", args, ) @@ -473,9 +475,9 @@ func (n *node) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error { return err } defer cancel() - lggr := n.newRqLggr().With("nBatchElems", len(b), "batchElems", b) + lggr := logger.With(n.newRqLggr(), "nBatchElems", len(b), "batchElems", b) - lggr.Trace("RPC call: evmclient.Client#BatchCallContext") + logger.Trace(lggr, "RPC call: evmclient.Client#BatchCallContext") start := time.Now() if http != nil { err = n.wrapHTTP(http.rpc.BatchCallContext(ctx, b)) @@ -495,7 +497,7 @@ func (n *node) EthSubscribe(ctx context.Context, channel chan<- *evmtypes.Head, return nil, err } defer cancel() - lggr := n.newRqLggr().With("args", args) + lggr := logger.With(n.newRqLggr(), "args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() @@ -518,7 +520,7 @@ func (n *node) TransactionReceipt(ctx context.Context, txHash common.Hash) (rece return nil, err } defer cancel() - lggr := n.newRqLggr().With("txHash", txHash) + lggr := logger.With(n.newRqLggr(), "txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionReceipt") @@ -545,7 +547,7 @@ func (n *node) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *t return nil, err } defer cancel() - lggr := n.newRqLggr().With("txHash", txHash) + lggr := logger.With(n.newRqLggr(), "txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionByHash") @@ -572,7 +574,7 @@ func (n *node) HeaderByNumber(ctx context.Context, number *big.Int) (header *typ return nil, err } defer cancel() - lggr := n.newRqLggr().With("number", number) + lggr := logger.With(n.newRqLggr(), "number", number) lggr.Debug("RPC call: evmclient.Client#HeaderByNumber") start := time.Now() @@ -596,7 +598,7 @@ func (n *node) HeaderByHash(ctx context.Context, hash common.Hash) (header *type return nil, err } defer cancel() - lggr := n.newRqLggr().With("hash", hash) + lggr := logger.With(n.newRqLggr(), "hash", hash) lggr.Debug("RPC call: evmclient.Client#HeaderByHash") start := time.Now() @@ -622,7 +624,7 @@ func (n *node) SendTransaction(ctx context.Context, tx *types.Transaction) error return err } defer cancel() - lggr := n.newRqLggr().With("tx", tx) + lggr := logger.With(n.newRqLggr(), "tx", tx) lggr.Debug("RPC call: evmclient.Client#SendTransaction") start := time.Now() @@ -645,7 +647,7 @@ func (n *node) PendingNonceAt(ctx context.Context, account common.Address) (nonc return 0, err } defer cancel() - lggr := n.newRqLggr().With("account", account) + lggr := logger.With(n.newRqLggr(), "account", account) lggr.Debug("RPC call: evmclient.Client#PendingNonceAt") start := time.Now() @@ -674,7 +676,7 @@ func (n *node) NonceAt(ctx context.Context, account common.Address, blockNumber return 0, err } defer cancel() - lggr := n.newRqLggr().With("account", account, "blockNumber", blockNumber) + lggr := logger.With(n.newRqLggr(), "account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#NonceAt") start := time.Now() @@ -700,7 +702,7 @@ func (n *node) PendingCodeAt(ctx context.Context, account common.Address) (code return nil, err } defer cancel() - lggr := n.newRqLggr().With("account", account) + lggr := logger.With(n.newRqLggr(), "account", account) lggr.Debug("RPC call: evmclient.Client#PendingCodeAt") start := time.Now() @@ -726,7 +728,7 @@ func (n *node) CodeAt(ctx context.Context, account common.Address, blockNumber * return nil, err } defer cancel() - lggr := n.newRqLggr().With("account", account, "blockNumber", blockNumber) + lggr := logger.With(n.newRqLggr(), "account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#CodeAt") start := time.Now() @@ -752,7 +754,7 @@ func (n *node) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint return 0, err } defer cancel() - lggr := n.newRqLggr().With("call", call) + lggr := logger.With(n.newRqLggr(), "call", call) lggr.Debug("RPC call: evmclient.Client#EstimateGas") start := time.Now() @@ -804,7 +806,7 @@ func (n *node) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumb return nil, err } defer cancel() - lggr := n.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) + lggr := logger.With(n.newRqLggr(), "callMsg", msg, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#CallContract") start := time.Now() @@ -831,7 +833,7 @@ func (n *node) BlockByNumber(ctx context.Context, number *big.Int) (b *types.Blo return nil, err } defer cancel() - lggr := n.newRqLggr().With("number", number) + lggr := logger.With(n.newRqLggr(), "number", number) lggr.Debug("RPC call: evmclient.Client#BlockByNumber") start := time.Now() @@ -857,7 +859,7 @@ func (n *node) BlockByHash(ctx context.Context, hash common.Hash) (b *types.Bloc return nil, err } defer cancel() - lggr := n.newRqLggr().With("hash", hash) + lggr := logger.With(n.newRqLggr(), "hash", hash) lggr.Debug("RPC call: evmclient.Client#BlockByHash") start := time.Now() @@ -909,7 +911,7 @@ func (n *node) BalanceAt(ctx context.Context, account common.Address, blockNumbe return nil, err } defer cancel() - lggr := n.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) + lggr := logger.With(n.newRqLggr(), "account", account.Hex(), "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#BalanceAt") start := time.Now() @@ -935,7 +937,7 @@ func (n *node) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l []type return nil, err } defer cancel() - lggr := n.newRqLggr().With("q", q) + lggr := logger.With(n.newRqLggr(), "q", q) lggr.Debug("RPC call: evmclient.Client#FilterLogs") start := time.Now() @@ -961,7 +963,7 @@ func (n *node) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, return nil, err } defer cancel() - lggr := n.newRqLggr().With("q", q) + lggr := logger.With(n.newRqLggr(), "q", q) lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") start := time.Now() @@ -1007,7 +1009,7 @@ func (n *node) ChainID() (chainID *big.Int) { return n.chainID } // newRqLggr generates a new logger with a unique request ID func (n *node) newRqLggr() logger.Logger { - return n.rpcLog.With( + return logger.With(n.rpcLog, "requestID", uuid.New(), ) } @@ -1020,11 +1022,11 @@ func (n *node) logResult( callName string, results ...interface{}, ) { - lggr = lggr.With("duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) + lggr = logger.With(lggr, "duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) promEVMPoolRPCNodeCalls.WithLabelValues(n.chainID.String(), n.name).Inc() if err == nil { promEVMPoolRPCNodeCallsSuccess.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew( + logger.Tracew(lggr, fmt.Sprintf("evmclient.Client#%s RPC call success", callName), results..., ) @@ -1057,7 +1059,7 @@ func (n *node) wrapHTTP(err error) error { if err != nil { n.rpcLog.Debugw("Call failed", "err", err) } else { - n.rpcLog.Trace("Call succeeded") + logger.Trace(n.rpcLog, "Call succeeded") } return err } diff --git a/core/chains/evm/client/node_fsm_test.go b/core/chains/evm/client/node_fsm_test.go index ce63a62a8bd..321bbc7a309 100644 --- a/core/chains/evm/client/node_fsm_test.go +++ b/core/chains/evm/client/node_fsm_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type fnMock struct{ calls int } @@ -43,7 +43,7 @@ func TestUnit_Node_StateTransitions(t *testing.T) { t.Parallel() s := testutils.NewWSServer(t, testutils.FixtureChainID, nil) - iN := NewNode(TestNodePoolConfig{}, time.Second*0, logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, nil, 1) + iN := NewNode(TestNodePoolConfig{}, time.Second*0, logger.Test(t), *s.WSURL(), nil, "test node", 42, nil, 1) n := iN.(*node) assert.Equal(t, NodeStateUndialed, n.State()) diff --git a/core/chains/evm/client/node_lifecycle.go b/core/chains/evm/client/node_lifecycle.go index 609d1522ea5..11d03d97dd5 100644 --- a/core/chains/evm/client/node_lifecycle.go +++ b/core/chains/evm/client/node_lifecycle.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -87,8 +88,9 @@ func (n *node) aliveLoop() { pollFailureThreshold := n.nodePoolCfg.PollFailureThreshold() pollInterval := n.nodePoolCfg.PollInterval() - lggr := n.lfcLog.Named("Alive").With("noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) - lggr.Tracew("Alive loop starting", "nodeState", n.State()) + lggr := logger.Named(n.lfcLog, "Alive") + lggr = logger.With(lggr, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold, "pollInterval", pollInterval, "pollFailureThreshold", pollFailureThreshold) + logger.Tracew(lggr, "Alive loop starting", "nodeState", n.State()) headsC := make(chan *evmtypes.Head) sub, err := n.EthSubscribe(n.nodeCtx, headsC, "newHeads") @@ -137,7 +139,7 @@ func (n *node) aliveLoop() { case <-pollCh: var version string promEVMPoolRPCNodePolls.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) + logger.Tracew(lggr, "Polling for version", "nodeState", n.State(), "pollFailures", pollFailures) ctx, cancel := context.WithTimeout(n.nodeCtx, pollInterval) ctx, cancel2 := n.makeQueryCtx(ctx) err := n.CallContext(ctx, &version, "web3_clientVersion") @@ -159,7 +161,7 @@ func (n *node) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailures), "pollFailures", pollFailures, "nodeState", n.State()) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - lggr.Criticalf("RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) + logger.Criticalf(lggr, "RPC endpoint failed to respond to polls; %s %s", msgCannotDisable, msgDegradedState) continue } } @@ -171,7 +173,7 @@ func (n *node) aliveLoop() { // note: there must be another live node for us to be out of sync lggr.Errorw("RPC endpoint has fallen behind", "blockNumber", num, "totalDifficulty", td, "nodeState", n.State()) if liveNodes < 2 { - lggr.Criticalf("RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) + logger.Criticalf(lggr, "RPC endpoint has fallen behind; %s %s", msgCannotDisable, msgDegradedState) continue } n.declareOutOfSync(n.isOutOfSync) @@ -184,13 +186,13 @@ func (n *node) aliveLoop() { return } promEVMPoolRPCNodeNumSeenBlocks.WithLabelValues(n.chainID.String(), n.name).Inc() - lggr.Tracew("Got head", "head", bh) + logger.Tracew(lggr, "Got head", "head", bh) if bh.Number > highestReceivedBlockNumber { promEVMPoolRPCNodeHighestSeenBlock.WithLabelValues(n.chainID.String(), n.name).Set(float64(bh.Number)) - lggr.Tracew("Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) + logger.Tracew(lggr, "Got higher block number, resetting timer", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) highestReceivedBlockNumber = bh.Number } else { - lggr.Tracew("Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) + logger.Tracew(lggr, "Ignoring previously seen block number", "latestReceivedBlockNumber", highestReceivedBlockNumber, "blockNumber", bh.Number, "nodeState", n.State()) } if outOfSyncT != nil { outOfSyncT.Reset(noNewHeadsTimeoutThreshold) @@ -206,7 +208,7 @@ func (n *node) aliveLoop() { lggr.Errorw(fmt.Sprintf("RPC endpoint detected out of sync; no new heads received for %s (last head received was %v)", noNewHeadsTimeoutThreshold, highestReceivedBlockNumber), "nodeState", n.State(), "latestReceivedBlockNumber", highestReceivedBlockNumber, "noNewHeadsTimeoutThreshold", noNewHeadsTimeoutThreshold) if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 2 { - lggr.Criticalf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) + logger.Criticalf(lggr, "RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState) // We don't necessarily want to wait the full timeout to check again, we should // check regularly and log noisily in this state outOfSyncT.Reset(zombieNodeCheckInterval(n.noNewHeadsThreshold)) @@ -272,7 +274,7 @@ func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *utils.Big) bool) { outOfSyncAt := time.Now() - lggr := n.lfcLog.Named("OutOfSync") + lggr := logger.Named(n.lfcLog, "OutOfSync") lggr.Debugw("Trying to revive out-of-sync RPC node", "nodeState", n.State()) // Need to redial since out-of-sync nodes are automatically disconnected @@ -289,7 +291,7 @@ func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *utils.Big) bool) { return } - lggr.Tracew("Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) + logger.Tracew(lggr, "Successfully subscribed to heads feed on out-of-sync RPC node", "nodeState", n.State()) ch := make(chan *evmtypes.Head) subCtx, cancel := n.makeQueryCtx(n.nodeCtx) @@ -324,7 +326,7 @@ func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *utils.Big) bool) { case <-time.After(zombieNodeCheckInterval(n.noNewHeadsThreshold)): if n.nLiveNodes != nil { if l, _, _ := n.nLiveNodes(); l < 1 { - lggr.Critical("RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") + logger.Critical(lggr, "RPC endpoint is still out of sync, but there are no other available nodes. This RPC node will be forcibly moved back into the live pool in a degraded state") n.declareInSync() return } @@ -354,7 +356,7 @@ func (n *node) unreachableLoop() { unreachableAt := time.Now() - lggr := n.lfcLog.Named("Unreachable") + lggr := logger.Named(n.lfcLog, "Unreachable") lggr.Debugw("Trying to revive unreachable RPC node", "nodeState", n.State()) dialRetryBackoff := utils.NewRedialBackoff() @@ -364,7 +366,7 @@ func (n *node) unreachableLoop() { case <-n.nodeCtx.Done(): return case <-time.After(dialRetryBackoff.Duration()): - lggr.Tracew("Trying to re-dial RPC node", "nodeState", n.State()) + logger.Tracew(lggr, "Trying to re-dial RPC node", "nodeState", n.State()) err := n.dial(n.nodeCtx) if err != nil { @@ -410,7 +412,7 @@ func (n *node) invalidChainIDLoop() { invalidAt := time.Now() - lggr := n.lfcLog.Named("InvalidChainID") + lggr := logger.Named(n.lfcLog, "InvalidChainID") lggr.Debugw(fmt.Sprintf("Periodically re-checking RPC node %s with invalid chain ID", n.String()), "nodeState", n.State()) chainIDRecheckBackoff := utils.NewRedialBackoff() diff --git a/core/chains/evm/client/node_lifecycle_test.go b/core/chains/evm/client/node_lifecycle_test.go index 05b8af13ec5..42f813cc2b5 100644 --- a/core/chains/evm/client/node_lifecycle_test.go +++ b/core/chains/evm/client/node_lifecycle_test.go @@ -12,10 +12,10 @@ import ( "github.com/tidwall/gjson" "go.uber.org/zap" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -34,7 +34,7 @@ func newTestNode(t *testing.T, cfg config.NodePool, noNewHeadsThresholds time.Du func newTestNodeWithCallback(t *testing.T, cfg config.NodePool, noNewHeadsThreshold time.Duration, callback testutils.JSONRPCHandler) *node { s := testutils.NewWSServer(t, testutils.FixtureChainID, callback) - iN := NewNode(cfg, noNewHeadsThreshold, logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, noNewHeadsThreshold, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) return n } @@ -222,7 +222,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { return }) - iN := NewNode(cfg, testutils.WaitTimeout(t), logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, testutils.WaitTimeout(t), logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) dial(t, n) @@ -268,7 +268,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { return }) - iN := NewNode(cfg, 1*time.Second, logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, 1*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) dial(t, n) @@ -288,7 +288,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when no new heads received for threshold but we are the last live node, forcibly stays alive", func(t *testing.T) { - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) pollDisabledCfg := TestNodePoolConfig{} s := testutils.NewWSServer(t, testutils.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { @@ -351,7 +351,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { return }) - iN := NewNode(cfg, 0*time.Second, logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, 0*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { return 2, highestHead.Load(), nil @@ -413,7 +413,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { return }) - iN := NewNode(cfg, 0*time.Second, logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, 0*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { return 2, highestHead.Load(), nil @@ -439,7 +439,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) t.Run("when behind more than SyncThreshold but we are the last live node, forcibly stays alive", func(t *testing.T) { - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) cfg := TestNodePoolConfig{NodeSyncThreshold: 5, NodePollFailureThreshold: 2, NodePollInterval: 100 * time.Millisecond, NodeSelectionMode: NodeSelectionMode_HighestHead} chSubbed := make(chan struct{}, 1) var highestHead atomic.Int64 @@ -557,7 +557,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { return }) - iN := NewNode(cfg, time.Duration(time.Second), logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, time.Duration(time.Second), logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) dial(t, n) @@ -584,7 +584,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { // NoNewHeadsThreshold needs to be positive but must be very large so // we don't time out waiting for a new head before we have a chance to // handle the server disconnect - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) cfg := TestNodePoolConfig{} chSubbed := make(chan struct{}, 1) s := testutils.NewWSServer(t, testutils.FixtureChainID, @@ -638,7 +638,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }) t.Run("transitions to alive if back in-sync", func(t *testing.T) { - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) cfg := TestNodePoolConfig{NodeSyncThreshold: 5, NodeSelectionMode: NodeSelectionMode_HighestHead} chSubbed := make(chan struct{}, 1) const stall = 42 @@ -715,7 +715,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { return }) - iN := NewNode(cfg, testutils.TestInterval, logger.TestLogger(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) + iN := NewNode(cfg, testutils.TestInterval, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) n.nLiveNodes = func() (int, int64, *utils.Big) { return 0, 0, nil } @@ -771,7 +771,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { t.Run("on successful redial but failed verify, transitions to invalid chain ID", func(t *testing.T) { cfg := TestNodePoolConfig{} s := testutils.NewWSServer(t, testutils.FixtureChainID, standardHandler) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, big.NewInt(42), 1) n := iN.(*node) defer func() { assert.NoError(t, n.Close()) }() @@ -790,7 +790,7 @@ func TestUnit_NodeLifecycle_unreachableLoop(t *testing.T) { t.Run("on failed redial, keeps trying to redial", func(t *testing.T) { cfg := TestNodePoolConfig{} - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) iN := NewNode(cfg, time.Second*0, lggr, *testutils.MustParseURL(t, "ws://test.invalid"), nil, "test node", 0, big.NewInt(42), 1) n := iN.(*node) defer func() { assert.NoError(t, n.Close()) }() @@ -842,7 +842,7 @@ func TestUnit_NodeLifecycle_invalidChainIDLoop(t *testing.T) { t.Run("on failed verify, keeps checking", func(t *testing.T) { cfg := TestNodePoolConfig{} s := testutils.NewWSServer(t, testutils.FixtureChainID, standardHandler) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, big.NewInt(42), 1) n := iN.(*node) defer func() { assert.NoError(t, n.Close()) }() diff --git a/core/chains/evm/client/null_client.go b/core/chains/evm/client/null_client.go index e25fed6d964..e3bb1defd0d 100644 --- a/core/chains/evm/client/null_client.go +++ b/core/chains/evm/client/null_client.go @@ -10,9 +10,9 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // NullClient satisfies the Client but has no side effects @@ -22,7 +22,7 @@ type NullClient struct { } func NewNullClient(cid *big.Int, lggr logger.Logger) *NullClient { - return &NullClient{cid: cid, lggr: lggr.Named("NullClient")} + return &NullClient{cid: cid, lggr: logger.Named(lggr, "NullClient")} } // NullClientChainID the ChainID that nullclient will return @@ -72,7 +72,7 @@ type nullSubscription struct { } func newNullSubscription(lggr logger.Logger) *nullSubscription { - return &nullSubscription{lggr: lggr.Named("NullSubscription")} + return &nullSubscription{lggr: logger.Named(lggr, "NullSubscription")} } func (ns *nullSubscription) Unsubscribe() { diff --git a/core/chains/evm/client/null_client_test.go b/core/chains/evm/client/null_client_test.go index 6b0f6e3d1b9..8f4ebd91c97 100644 --- a/core/chains/evm/client/null_client_test.go +++ b/core/chains/evm/client/null_client_test.go @@ -11,17 +11,17 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestNullClient(t *testing.T) { t.Parallel() t.Run("chain id", func(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) cid := big.NewInt(123) nc := client.NewNullClient(cid, lggr) require.Equal(t, cid, nc.ConfiguredChainID()) @@ -31,7 +31,7 @@ func TestNullClient(t *testing.T) { }) t.Run("CL client methods", func(t *testing.T) { - lggr, logs := logger.TestLoggerObserved(t, zapcore.DebugLevel) + lggr, logs := logger.TestObserved(t, zapcore.DebugLevel) nc := client.NewNullClient(nil, lggr) ctx := testutils.Context(t) @@ -77,7 +77,7 @@ func TestNullClient(t *testing.T) { }) t.Run("Geth client methods", func(t *testing.T) { - lggr, logs := logger.TestLoggerObserved(t, zapcore.DebugLevel) + lggr, logs := logger.TestObserved(t, zapcore.DebugLevel) nc := client.NewNullClient(nil, lggr) ctx := testutils.Context(t) diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 289a402a1c6..ab190f8c6aa 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -15,11 +15,11 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -76,7 +76,7 @@ type Pool struct { wg sync.WaitGroup } -func NewPool(logger logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsTreshold time.Duration, nodes []Node, sendonlys []SendOnlyNode, chainID *big.Int, chainType config.ChainType) *Pool { +func NewPool(lggr logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsTreshold time.Duration, nodes []Node, sendonlys []SendOnlyNode, chainID *big.Int, chainType config.ChainType) *Pool { if chainID == nil { panic("chainID is required") } @@ -96,7 +96,8 @@ func NewPool(logger logger.Logger, selectionMode string, leaseDuration time.Dura } }() - lggr := logger.Named("Pool").With("evmChainID", chainID.String()) + lggr = logger.Named(lggr, "Pool") + lggr = logger.With(lggr, "evmChainID", chainID.String()) p := &Pool{ nodes: nodes, @@ -262,10 +263,10 @@ func (p *Pool) report() { } live := total - dead - p.logger.Tracew(fmt.Sprintf("Pool state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) + logger.Tracew(p.logger, fmt.Sprintf("Pool state: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) if total == dead { rerr := fmt.Errorf("no EVM primary nodes available: 0/%d nodes are alive", total) - p.logger.Criticalw(rerr.Error(), "nodeStates", nodeStates) + logger.Criticalw(p.logger, rerr.Error(), "nodeStates", nodeStates) p.SvcErrBuffer.Append(rerr) } else if dead > 0 { p.logger.Errorw(fmt.Sprintf("At least one EVM primary node is dead: %d/%d nodes are alive", live, total), "nodeStates", nodeStates) @@ -310,7 +311,7 @@ func (p *Pool) selectNode() (node Node) { p.activeNode = p.nodeSelector.Select() if p.activeNode == nil { - p.logger.Criticalw("No live RPC nodes available", "NodeSelectionMode", p.nodeSelector.Name()) + logger.Criticalw(p.logger, "No live RPC nodes available", "NodeSelectionMode", p.nodeSelector.Name()) errmsg := fmt.Errorf("no live nodes available for chain %s", p.chainID.String()) p.SvcErrBuffer.Append(errmsg) return &erroringNode{errMsg: errmsg.Error()} @@ -357,7 +358,7 @@ func (p *Pool) BatchCallContextAll(ctx context.Context, b []rpc.BatchElem) error if err != nil { p.logger.Debugw("Secondary node BatchCallContext failed", "err", err) } else { - p.logger.Trace("Secondary node BatchCallContext success") + logger.Trace(p.logger, "Secondary node BatchCallContext success") } }(n) } diff --git a/core/chains/evm/client/pool_test.go b/core/chains/evm/client/pool_test.go index 75d38d01a4f..462aeed43ee 100644 --- a/core/chains/evm/client/pool_test.go +++ b/core/chains/evm/client/pool_test.go @@ -18,11 +18,11 @@ import ( "github.com/tidwall/gjson" "go.uber.org/zap" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type poolConfig struct { @@ -164,7 +164,7 @@ func TestPool_Dial(t *testing.T) { for i, n := range test.sendNodes { sendNodes[i] = n.newSendOnlyNode(t, test.sendNodeChainID) } - p := evmclient.NewPool(logger.TestLogger(t), defaultConfig.NodeSelectionMode(), defaultConfig.LeaseDuration(), time.Second*0, nodes, sendNodes, test.poolChainID, "") + p := evmclient.NewPool(logger.Test(t), defaultConfig.NodeSelectionMode(), defaultConfig.LeaseDuration(), time.Second*0, nodes, sendNodes, test.poolChainID, "") err := p.Dial(ctx) if err == nil { t.Cleanup(func() { assert.NoError(t, p.Close()) }) @@ -188,7 +188,7 @@ type chainIDResp struct { func (r *chainIDResp) newSendOnlyNode(t *testing.T, nodeChainID int64) evmclient.SendOnlyNode { httpURL := r.newHTTPServer(t) - return evmclient.NewSendOnlyNode(logger.TestLogger(t), *httpURL, t.Name(), big.NewInt(nodeChainID)) + return evmclient.NewSendOnlyNode(logger.Test(t), *httpURL, t.Name(), big.NewInt(nodeChainID)) } func (r *chainIDResp) newHTTPServer(t *testing.T) *url.URL { @@ -234,7 +234,7 @@ func (r *chainIDResps) newNode(t *testing.T, nodeChainID int64) evmclient.Node { } defer func() { r.id++ }() - return evmclient.NewNode(evmclient.TestNodePoolConfig{}, time.Second*0, logger.TestLogger(t), *wsURL, httpURL, t.Name(), r.id, big.NewInt(nodeChainID), 0) + return evmclient.NewNode(evmclient.TestNodePoolConfig{}, time.Second*0, logger.Test(t), *wsURL, httpURL, t.Name(), r.id, big.NewInt(nodeChainID), 0) } type chainIDService struct { @@ -256,7 +256,7 @@ func TestUnit_Pool_RunLoop(t *testing.T) { n3 := evmmocks.NewNode(t) nodes := []evmclient.Node{n1, n2, n3} - lggr, observedLogs := logger.TestLoggerObserved(t, zap.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zap.ErrorLevel) p := evmclient.NewPool(lggr, defaultConfig.NodeSelectionMode(), defaultConfig.LeaseDuration(), time.Second*0, nodes, []evmclient.SendOnlyNode{}, &cltest.FixtureChainID, "") n1.On("String").Maybe().Return("n1") @@ -331,7 +331,7 @@ func TestUnit_Pool_BatchCallContextAll(t *testing.T) { sendonlys = append(sendonlys, s) } - p := evmclient.NewPool(logger.TestLogger(t), defaultConfig.NodeSelectionMode(), defaultConfig.LeaseDuration(), time.Second*0, nodes, sendonlys, &cltest.FixtureChainID, "") + p := evmclient.NewPool(logger.Test(t), defaultConfig.NodeSelectionMode(), defaultConfig.LeaseDuration(), time.Second*0, nodes, sendonlys, &cltest.FixtureChainID, "") assert.True(t, p.ChainType().IsValid()) assert.False(t, p.ChainType().IsL2()) @@ -378,7 +378,7 @@ func TestUnit_Pool_LeaseDuration(t *testing.T) { n2.On("Order").Return(int32(2)) n2.On("ChainID").Return(testutils.FixtureChainID).Once() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.InfoLevel) + lggr, observedLogs := logger.TestObserved(t, zap.InfoLevel) p := evmclient.NewPool(lggr, "PriorityLevel", time.Second*2, time.Second*0, nodes, []evmclient.SendOnlyNode{}, &cltest.FixtureChainID, "") require.NoError(t, p.Dial(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, p.Close()) }) diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index 785acbb2b7d..fc9c348a572 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -18,11 +18,12 @@ import ( "github.com/pkg/errors" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -97,13 +98,14 @@ func NewRPCClient( r.http = &rawclient{uri: *httpuri} } r.chStopInFlight = make(chan struct{}) - lggr = lggr.Named("Client").With( + lggr = logger.Named(lggr, "Client") + lggr = logger.With(lggr, "clientTier", tier.String(), "clientName", name, "client", r.String(), "evmChainID", chainID, ) - r.rpcLog = lggr.Named("RPC") + r.rpcLog = logger.Named(lggr, "RPC") return r } @@ -114,9 +116,9 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { defer cancel() promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() - lggr := r.rpcLog.With("wsuri", r.ws.uri.Redacted()) + lggr := logger.With(r.rpcLog, "wsuri", r.ws.uri.Redacted()) if r.http != nil { - lggr = lggr.With("httpuri", r.http.uri.Redacted()) + lggr = logger.With(lggr, "httpuri", r.http.uri.Redacted()) } lggr.Debugw("RPC dial: evmclient.Client#dial") @@ -145,7 +147,7 @@ func (r *rpcClient) Dial(callerCtx context.Context) error { // It can only return error if the URL is malformed. func (r *rpcClient) DialHTTP() error { promEVMPoolRPCNodeDials.WithLabelValues(r.chainID.String(), r.name).Inc() - lggr := r.rpcLog.With("httpuri", r.ws.uri.Redacted()) + lggr := logger.With(r.rpcLog, "httpuri", r.ws.uri.Redacted()) lggr.Debugw("RPC dial: evmclient.Client#dial") var httprpc *rpc.Client @@ -199,11 +201,11 @@ func (r *rpcClient) logResult( callName string, results ...interface{}, ) { - lggr = lggr.With("duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) + lggr = logger.With(lggr, "duration", callDuration, "rpcDomain", rpcDomain, "callName", callName) promEVMPoolRPCNodeCalls.WithLabelValues(r.chainID.String(), r.name).Inc() if err == nil { promEVMPoolRPCNodeCallsSuccess.WithLabelValues(r.chainID.String(), r.name).Inc() - lggr.Tracew( + logger.Tracew(lggr, fmt.Sprintf("evmclient.Client#%s RPC call success", callName), results..., ) @@ -296,7 +298,7 @@ func (r *rpcClient) CallContext(ctx context.Context, result interface{}, method return err } defer cancel() - lggr := r.newRqLggr().With( + lggr := logger.With(r.newRqLggr(), "method", method, "args", args, ) @@ -325,9 +327,9 @@ func (r *rpcClient) BatchCallContext(ctx context.Context, b []any) error { batch[i] = arg.(rpc.BatchElem) } defer cancel() - lggr := r.newRqLggr().With("nBatchElems", len(b), "batchElems", b) + lggr := logger.With(r.newRqLggr(), "nBatchElems", len(b), "batchElems", b) - lggr.Trace("RPC call: evmclient.Client#BatchCallContext") + logger.Trace(lggr, "RPC call: evmclient.Client#BatchCallContext") start := time.Now() if http != nil { err = r.wrapHTTP(http.rpc.BatchCallContext(ctx, batch)) @@ -347,7 +349,7 @@ func (r *rpcClient) Subscribe(ctx context.Context, channel chan<- *evmtypes.Head return nil, err } defer cancel() - lggr := r.newRqLggr().With("args", args) + lggr := logger.With(r.newRqLggr(), "args", args) lggr.Debug("RPC call: evmclient.Client#EthSubscribe") start := time.Now() @@ -382,7 +384,7 @@ func (r *rpcClient) TransactionReceiptGeth(ctx context.Context, txHash common.Ha return nil, err } defer cancel() - lggr := r.newRqLggr().With("txHash", txHash) + lggr := logger.With(r.newRqLggr(), "txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionReceipt") @@ -408,7 +410,7 @@ func (r *rpcClient) TransactionByHash(ctx context.Context, txHash common.Hash) ( return nil, err } defer cancel() - lggr := r.newRqLggr().With("txHash", txHash) + lggr := logger.With(r.newRqLggr(), "txHash", txHash) lggr.Debug("RPC call: evmclient.Client#TransactionByHash") @@ -435,7 +437,7 @@ func (r *rpcClient) HeaderByNumber(ctx context.Context, number *big.Int) (header return nil, err } defer cancel() - lggr := r.newRqLggr().With("number", number) + lggr := logger.With(r.newRqLggr(), "number", number) lggr.Debug("RPC call: evmclient.Client#HeaderByNumber") start := time.Now() @@ -459,7 +461,7 @@ func (r *rpcClient) HeaderByHash(ctx context.Context, hash common.Hash) (header return nil, err } defer cancel() - lggr := r.newRqLggr().With("hash", hash) + lggr := logger.With(r.newRqLggr(), "hash", hash) lggr.Debug("RPC call: evmclient.Client#HeaderByHash") start := time.Now() @@ -512,7 +514,7 @@ func (r *rpcClient) BlockByHashGeth(ctx context.Context, hash common.Hash) (bloc return nil, err } defer cancel() - lggr := r.newRqLggr().With("hash", hash) + lggr := logger.With(r.newRqLggr(), "hash", hash) lggr.Debug("RPC call: evmclient.Client#BlockByHash") start := time.Now() @@ -538,7 +540,7 @@ func (r *rpcClient) BlockByNumberGeth(ctx context.Context, number *big.Int) (blo return nil, err } defer cancel() - lggr := r.newRqLggr().With("number", number) + lggr := logger.With(r.newRqLggr(), "number", number) lggr.Debug("RPC call: evmclient.Client#BlockByNumber") start := time.Now() @@ -564,7 +566,7 @@ func (r *rpcClient) SendTransaction(ctx context.Context, tx *types.Transaction) return err } defer cancel() - lggr := r.newRqLggr().With("tx", tx) + lggr := logger.With(r.newRqLggr(), "tx", tx) lggr.Debug("RPC call: evmclient.Client#SendTransaction") start := time.Now() @@ -604,7 +606,7 @@ func (r *rpcClient) PendingSequenceAt(ctx context.Context, account common.Addres return 0, err } defer cancel() - lggr := r.newRqLggr().With("account", account) + lggr := logger.With(r.newRqLggr(), "account", account) lggr.Debug("RPC call: evmclient.Client#PendingNonceAt") start := time.Now() @@ -636,7 +638,7 @@ func (r *rpcClient) SequenceAt(ctx context.Context, account common.Address, bloc return 0, err } defer cancel() - lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) + lggr := logger.With(r.newRqLggr(), "account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#NonceAt") start := time.Now() @@ -665,7 +667,7 @@ func (r *rpcClient) PendingCodeAt(ctx context.Context, account common.Address) ( return nil, err } defer cancel() - lggr := r.newRqLggr().With("account", account) + lggr := logger.With(r.newRqLggr(), "account", account) lggr.Debug("RPC call: evmclient.Client#PendingCodeAt") start := time.Now() @@ -691,7 +693,7 @@ func (r *rpcClient) CodeAt(ctx context.Context, account common.Address, blockNum return nil, err } defer cancel() - lggr := r.newRqLggr().With("account", account, "blockNumber", blockNumber) + lggr := logger.With(r.newRqLggr(), "account", account, "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#CodeAt") start := time.Now() @@ -718,7 +720,7 @@ func (r *rpcClient) EstimateGas(ctx context.Context, c interface{}) (gas uint64, } defer cancel() call := c.(ethereum.CallMsg) - lggr := r.newRqLggr().With("call", call) + lggr := logger.With(r.newRqLggr(), "call", call) lggr.Debug("RPC call: evmclient.Client#EstimateGas") start := time.Now() @@ -770,7 +772,7 @@ func (r *rpcClient) CallContract(ctx context.Context, msg interface{}, blockNumb return nil, err } defer cancel() - lggr := r.newRqLggr().With("callMsg", msg, "blockNumber", blockNumber) + lggr := logger.With(r.newRqLggr(), "callMsg", msg, "blockNumber", blockNumber) message := msg.(ethereum.CallMsg) lggr.Debug("RPC call: evmclient.Client#CallContract") @@ -830,7 +832,7 @@ func (r *rpcClient) BalanceAt(ctx context.Context, account common.Address, block return nil, err } defer cancel() - lggr := r.newRqLggr().With("account", account.Hex(), "blockNumber", blockNumber) + lggr := logger.With(r.newRqLggr(), "account", account.Hex(), "blockNumber", blockNumber) lggr.Debug("RPC call: evmclient.Client#BalanceAt") start := time.Now() @@ -887,7 +889,7 @@ func (r *rpcClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l [ return nil, err } defer cancel() - lggr := r.newRqLggr().With("q", q) + lggr := logger.With(r.newRqLggr(), "q", q) lggr.Debug("RPC call: evmclient.Client#FilterLogs") start := time.Now() @@ -918,7 +920,7 @@ func (r *rpcClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQu return nil, err } defer cancel() - lggr := r.newRqLggr().With("q", q) + lggr := logger.With(r.newRqLggr(), "q", q) lggr.Debug("RPC call: evmclient.Client#SubscribeFilterLogs") start := time.Now() @@ -979,7 +981,7 @@ func (r *rpcClient) ChainID(ctx context.Context) (chainID *big.Int, err error) { // newRqLggr generates a new logger with a unique request ID func (r *rpcClient) newRqLggr() logger.Logger { - return r.rpcLog.With( + return logger.With(r.rpcLog, "requestID", uuid.New(), ) } @@ -1004,7 +1006,7 @@ func (r *rpcClient) wrapHTTP(err error) error { if err != nil { r.rpcLog.Debugw("Call failed", "err", err) } else { - r.rpcLog.Trace("Call succeeded") + logger.Trace(r.rpcLog, "Call succeeded") } return err } diff --git a/core/chains/evm/client/send_only_node.go b/core/chains/evm/client/send_only_node.go index 02f04881c44..62a22ee1937 100644 --- a/core/chains/evm/client/send_only_node.go +++ b/core/chains/evm/client/send_only_node.go @@ -14,8 +14,8 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) //go:generate mockery --quiet --name SendOnlyNode --output ../mocks/ --case=underscore @@ -76,7 +76,8 @@ type sendOnlyNode struct { func NewSendOnlyNode(lggr logger.Logger, httpuri url.URL, name string, chainID *big.Int) SendOnlyNode { s := new(sendOnlyNode) s.name = name - s.log = lggr.Named("SendOnlyNode").Named(name).With( + s.log = logger.Named(logger.Named(lggr, "SendOnlyNode"), name) + s.log = logger.With(s.log, "nodeTier", "sendonly", ) s.uri = httpuri @@ -206,7 +207,7 @@ func (s *sendOnlyNode) SendTransaction(parentCtx context.Context, tx *types.Tran func (s *sendOnlyNode) BatchCallContext(parentCtx context.Context, b []rpc.BatchElem) (err error) { defer func(start time.Time) { - s.logTiming(s.log.With("nBatchElems", len(b)), time.Since(start), err, "BatchCallContext") + s.logTiming(logger.With(s.log, "nBatchElems", len(b)), time.Since(start), err, "BatchCallContext") }(time.Now()) ctx, cancel := s.makeQueryCtx(parentCtx) diff --git a/core/chains/evm/client/send_only_node_test.go b/core/chains/evm/client/send_only_node_test.go index 876ae9bc4da..760f7f4d3eb 100644 --- a/core/chains/evm/client/send_only_node_test.go +++ b/core/chains/evm/client/send_only_node_test.go @@ -16,13 +16,13 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestNewSendOnlyNode(t *testing.T) { @@ -32,7 +32,7 @@ func TestNewSendOnlyNode(t *testing.T) { password := "pass" url := testutils.MustParseURL(t, fmt.Sprintf(urlFormat, password)) redacted := fmt.Sprintf(urlFormat, "xxxxx") - lggr := logger.TestLogger(t) + lggr := logger.Test(t) name := "TestNewSendOnlyNode" chainID := testutils.NewRandomEVMChainID() @@ -52,7 +52,7 @@ func TestStartSendOnlyNode(t *testing.T) { chainID := testutils.NewRandomEVMChainID() r := chainIDResp{chainID.Int64(), nil} url := r.newHTTPServer(t) - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) s := evmclient.NewSendOnlyNode(lggr, *url, t.Name(), chainID) defer func() { assert.NoError(t, s.Close()) }() err := s.Start(testutils.Context(t)) @@ -62,7 +62,7 @@ func TestStartSendOnlyNode(t *testing.T) { t.Run("Start with ChainID=0", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) chainID := testutils.FixtureChainID r := chainIDResp{chainID.Int64(), nil} url := r.newHTTPServer(t) @@ -77,7 +77,7 @@ func TestStartSendOnlyNode(t *testing.T) { t.Run("becomes unusable (and remains undialed) if initial dial fails", func(t *testing.T) { t.Parallel() - lggr, observedLogs := logger.TestLoggerObserved(t, zap.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zap.WarnLevel) invalidURL := url.URL{Scheme: "some rubbish", Host: "not a valid host"} s := evmclient.NewSendOnlyNode(lggr, invalidURL, t.Name(), testutils.FixtureChainID) @@ -109,7 +109,7 @@ func TestSendTransaction(t *testing.T) { t.Parallel() chainID := testutils.FixtureChainID - lggr, observedLogs := logger.TestLoggerObserved(t, zap.DebugLevel) + lggr, observedLogs := logger.TestObserved(t, zap.DebugLevel) url := testutils.MustParseURL(t, "http://place.holder") s := evmclient.NewSendOnlyNode(lggr, *url, @@ -135,7 +135,7 @@ func TestSendTransaction(t *testing.T) { func TestBatchCallContext(t *testing.T) { t.Parallel() - lggr := logger.TestLogger(t) + lggr := logger.Test(t) chainID := testutils.FixtureChainID url := testutils.MustParseURL(t, "http://place.holder") s := evmclient.NewSendOnlyNode( diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index e922715eb9c..10b2aae502a 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -19,9 +19,10 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -347,7 +348,7 @@ func (c *SimulatedBackendClient) SendTransactionReturnCode(ctx context.Context, func (c *SimulatedBackendClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { sender, err := types.Sender(types.NewLondonSigner(c.chainId), tx) if err != nil { - logger.TestLogger(c.t).Panic(fmt.Errorf("invalid transaction: %v (tx: %#v)", err, tx)) + logger.Test(c.t).Panic(fmt.Errorf("invalid transaction: %v (tx: %#v)", err, tx)) } pendingNonce, err := c.b.PendingNonceAt(ctx, sender) if err != nil { diff --git a/core/chains/evm/config/chain_scoped.go b/core/chains/evm/config/chain_scoped.go index 804c354e0ef..fb6df26b1ad 100644 --- a/core/chains/evm/config/chain_scoped.go +++ b/core/chains/evm/config/chain_scoped.go @@ -10,10 +10,11 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/assets" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/config" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func NewTOMLChainScopedConfig(appCfg config.AppConfig, tomlConfig *toml.EVMConfig, lggr logger.Logger) *ChainScoped { diff --git a/core/chains/evm/forwarders/forwarder_manager.go b/core/chains/evm/forwarders/forwarder_manager.go index 819fb31951e..eaf0c32afe3 100644 --- a/core/chains/evm/forwarders/forwarder_manager.go +++ b/core/chains/evm/forwarders/forwarder_manager.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -20,7 +21,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/authorized_receiver" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/offchain_aggregator_wrapper" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -56,7 +56,7 @@ type FwdMgr struct { } func NewFwdMgr(db *sqlx.DB, client evmclient.Client, logpoller evmlogpoller.LogPoller, l logger.Logger, cfg Config, dbConfig pg.QConfig) *FwdMgr { - lggr := logger.Sugared(l.Named("EVMForwarderManager")) + lggr := logger.Sugared(logger.Named(l, "EVMForwarderManager")) fwdMgr := FwdMgr{ logger: lggr, cfg: cfg, diff --git a/core/chains/evm/forwarders/forwarder_manager_test.go b/core/chains/evm/forwarders/forwarder_manager_test.go index 082d329e385..1da638e743d 100644 --- a/core/chains/evm/forwarders/forwarder_manager_test.go +++ b/core/chains/evm/forwarders/forwarder_manager_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/forwarders" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" @@ -22,7 +23,6 @@ import ( "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/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -32,7 +32,7 @@ var GetAuthorisedSendersABI = evmtypes.MustGetABI(authorized_receiver.Authorized var SimpleOracleCallABI = evmtypes.MustGetABI(operator_wrapper.OperatorABI).Methods["getChainlinkToken"] func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) db := pgtest.NewSqlxDB(t) cfg := configtest.NewTestGeneralConfig(t) evmcfg := evmtest.NewChainScopedConfig(t, cfg) @@ -60,7 +60,7 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { evmClient := client.NewSimulatedBackendClient(t, ec, testutils.FixtureChainID) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) - fwdMgr.ORM = forwarders.NewORM(db, logger.TestLogger(t), cfg.Database()) + fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) fwd, err := fwdMgr.ORM.CreateForwarder(forwarderAddr, utils.Big(*testutils.FixtureChainID)) require.NoError(t, err) @@ -91,7 +91,7 @@ func TestFwdMgr_MaybeForwardTransaction(t *testing.T) { } func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) db := pgtest.NewSqlxDB(t) cfg := configtest.NewTestGeneralConfig(t) evmcfg := evmtest.NewChainScopedConfig(t, cfg) @@ -113,7 +113,7 @@ func TestFwdMgr_AccountUnauthorizedToForward_SkipsForwarding(t *testing.T) { evmClient := client.NewSimulatedBackendClient(t, ec, testutils.FixtureChainID) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), evmClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) fwdMgr := forwarders.NewFwdMgr(db, evmClient, lp, lggr, evmcfg.EVM(), evmcfg.Database()) - fwdMgr.ORM = forwarders.NewORM(db, logger.TestLogger(t), cfg.Database()) + fwdMgr.ORM = forwarders.NewORM(db, logger.Test(t), cfg.Database()) _, err = fwdMgr.ORM.CreateForwarder(forwarderAddr, utils.Big(*testutils.FixtureChainID)) require.NoError(t, err) diff --git a/core/chains/evm/forwarders/orm.go b/core/chains/evm/forwarders/orm.go index df89dbe29e9..104e2574252 100644 --- a/core/chains/evm/forwarders/orm.go +++ b/core/chains/evm/forwarders/orm.go @@ -7,7 +7,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/forwarders/orm_test.go b/core/chains/evm/forwarders/orm_test.go index f6d63dc574f..ba9664c196a 100644 --- a/core/chains/evm/forwarders/orm_test.go +++ b/core/chains/evm/forwarders/orm_test.go @@ -9,9 +9,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -28,7 +28,7 @@ func setupORM(t *testing.T) *TestORM { var ( db = pgtest.NewSqlxDB(t) - lggr = logger.TestLogger(t) + lggr = logger.Test(t) orm = NewORM(db, lggr, pgtest.NewQConfig(true)) ) diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index 480abfe721d..ee020f67002 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -13,12 +13,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -53,7 +53,7 @@ type arbitrumEstimator struct { } func NewArbitrumEstimator(lggr logger.Logger, cfg ArbConfig, rpcClient rpcClient, ethClient ethClient) EvmEstimator { - lggr = lggr.Named("ArbitrumEstimator") + lggr = logger.Named(lggr, "ArbitrumEstimator") return &arbitrumEstimator{ cfg: cfg, EvmEstimator: NewSuggestedPriceEstimator(lggr, rpcClient), diff --git a/core/chains/evm/gas/arbitrum_estimator_test.go b/core/chains/evm/gas/arbitrum_estimator_test.go index 894b531dc97..53f81617988 100644 --- a/core/chains/evm/gas/arbitrum_estimator_test.go +++ b/core/chains/evm/gas/arbitrum_estimator_test.go @@ -14,11 +14,11 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type arbConfig struct { @@ -41,7 +41,7 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("calling GetLegacyGas on unstarted estimator returns error", func(t *testing.T) { rpcClient := mocks.NewRPCClient(t) ethClient := mocks.NewETHClient(t) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{}, rpcClient, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, rpcClient, ethClient) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "estimator is not started") }) @@ -65,7 +65,7 @@ func TestArbitrumEstimator(t *testing.T) { assert.Equal(t, big.NewInt(-1), blockNumber) }).Return(zeros.Bytes(), nil) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) require.NoError(t, o.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, o.Close()) }) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) @@ -78,7 +78,7 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("gas price is lower than user specified max gas price", func(t *testing.T) { client := mocks.NewRPCClient(t) ethClient := mocks.NewETHClient(t) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{}, client, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, client, ethClient) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -104,7 +104,7 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("gas price is lower than global max gas price", func(t *testing.T) { ethClient := mocks.NewETHClient(t) client := mocks.NewRPCClient(t) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{}, client, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, client, ethClient) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -129,7 +129,7 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("calling BumpLegacyGas always returns error", func(t *testing.T) { rpcClient := mocks.NewRPCClient(t) ethClient := mocks.NewETHClient(t) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{}, rpcClient, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, rpcClient, ethClient) _, _, err := o.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), gasLimit, assets.NewWeiI(10), nil) assert.EqualError(t, err, "bump gas is not supported for this chain") }) @@ -137,7 +137,7 @@ func TestArbitrumEstimator(t *testing.T) { t.Run("calling GetLegacyGas on started estimator if initial call failed returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) ethClient := mocks.NewETHClient(t) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{}, client, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{}, client, ethClient) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) ethClient.On("CallContract", mock.Anything, mock.IsType(ethereum.CallMsg{}), mock.IsType(&big.Int{})).Run(func(args mock.Arguments) { @@ -180,7 +180,7 @@ func TestArbitrumEstimator(t *testing.T) { assert.Equal(t, big.NewInt(-1), blockNumber) }).Return(b.Bytes(), nil) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) require.NoError(t, o.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, o.Close()) }) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) @@ -215,7 +215,7 @@ func TestArbitrumEstimator(t *testing.T) { assert.Equal(t, big.NewInt(-1), blockNumber) }).Return(b.Bytes(), nil) - o := gas.NewArbitrumEstimator(logger.TestLogger(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) + o := gas.NewArbitrumEstimator(logger.Test(t), &arbConfig{v: maxGasLimit}, rpcClient, ethClient) require.NoError(t, o.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, o.Close()) }) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index a3f3520b365..64dc331f657 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -15,14 +15,15 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" ) @@ -142,7 +143,7 @@ func NewBlockHistoryEstimator(lggr logger.Logger, ethClient evmclient.Client, cf wg: new(sync.WaitGroup), ctx: ctx, ctxCancel: cancel, - logger: logger.Sugared(lggr.Named("BlockHistoryEstimator")), + logger: logger.Sugared(logger.Named(lggr, "BlockHistoryEstimator")), } return b @@ -197,7 +198,7 @@ func (b *BlockHistoryEstimator) getBlocks() []evmtypes.Block { // The provided context can be used to terminate Start sequence. func (b *BlockHistoryEstimator) Start(ctx context.Context) error { return b.StartOnce("BlockHistoryEstimator", func() error { - b.logger.Trace("Starting") + logger.Trace(b.logger, "Starting") if b.bhConfig.CheckInclusionBlocks() > 0 { b.logger.Infof("Inclusion checking enabled, bumping will be prevented on transactions that have been priced above the %d percentile for %d blocks", b.bhConfig.CheckInclusionPercentile(), b.bhConfig.CheckInclusionBlocks()) @@ -227,7 +228,7 @@ func (b *BlockHistoryEstimator) Start(ctx context.Context) error { b.wg.Add(1) go b.runLoop() - b.logger.Trace("Started") + logger.Trace(b.logger, "Started") return nil }) } @@ -290,7 +291,7 @@ func (b *BlockHistoryEstimator) BumpLegacyGas(_ context.Context, originalGasPric if b.bhConfig.CheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { if errors.Is(err, commonfee.ErrConnectivity) { - b.logger.Criticalw(BumpingHaltedLabel, "err", err) + logger.Criticalw(b.logger, BumpingHaltedLabel, "err", err) b.SvcErrBuffer.Append(err) promBlockHistoryEstimatorConnectivityFailureCount.WithLabelValues(b.chainID.String(), "legacy").Inc() } @@ -466,7 +467,7 @@ func (b *BlockHistoryEstimator) BumpDynamicFee(_ context.Context, originalFee Dy if b.bhConfig.CheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { if errors.Is(err, commonfee.ErrConnectivity) { - b.logger.Criticalw(BumpingHaltedLabel, "err", err) + logger.Criticalw(b.logger, BumpingHaltedLabel, "err", err) b.SvcErrBuffer.Append(err) promBlockHistoryEstimatorConnectivityFailureCount.WithLabelValues(b.chainID.String(), "eip1559").Inc() } @@ -507,7 +508,7 @@ func (b *BlockHistoryEstimator) FetchBlocksAndRecalculate(ctx context.Context, h func (b *BlockHistoryEstimator) Recalculate(head *evmtypes.Head) { percentile := int(b.bhConfig.TransactionPercentile()) - lggr := b.logger.With("head", head) + lggr := logger.With(b.logger, "head", head) blockHistory := b.getBlocks() if len(blockHistory) == 0 { @@ -629,9 +630,9 @@ func (b *BlockHistoryEstimator) FetchBlocks(ctx context.Context, head *evmtypes. reqs = append(reqs, req) } - lggr := b.logger.With("head", head) + lggr := logger.With(b.logger, "head", head) - lggr.Tracew(fmt.Sprintf("Fetching %v blocks (%v in local history)", len(reqs), len(blocks)), "n", len(reqs), "inHistory", len(blocks), "blockNum", head.Number) + logger.Tracew(lggr, fmt.Sprintf("Fetching %v blocks (%v in local history)", len(reqs), len(blocks)), "n", len(reqs), "inHistory", len(blocks), "blockNum", head.Number) if err := b.batchFetch(ctx, reqs); err != nil { return err } @@ -712,7 +713,7 @@ func (b *BlockHistoryEstimator) batchFetch(ctx context.Context, reqs []rpc.Batch j = len(reqs) } - b.logger.Tracew(fmt.Sprintf("Batch fetching blocks %v thru %v", HexToInt64(reqs[i].Args[0]), HexToInt64(reqs[j-1].Args[0]))) + logger.Tracew(b.logger, fmt.Sprintf("Batch fetching blocks %v thru %v", HexToInt64(reqs[i].Args[0]), HexToInt64(reqs[j-1].Args[0]))) err := b.ethClient.BatchCallContext(ctx, reqs[i:j]) if errors.Is(err, context.DeadlineExceeded) { diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index 2747ea067d6..d3edf212b6a 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -27,7 +28,6 @@ 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/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -42,7 +42,7 @@ func newBlockHistoryConfig() *gas.MockBlockHistoryConfig { } func newBlockHistoryEstimatorWithChainID(t *testing.T, c evmclient.Client, cfg gas.Config, gCfg gas.GasEstimatorConfig, bhCfg gas.BlockHistoryConfig, cid big.Int) gas.EvmEstimator { - return gas.NewBlockHistoryEstimator(logger.TestLogger(t), c, cfg, gCfg, bhCfg, cid) + return gas.NewBlockHistoryEstimator(logger.Test(t), c, cfg, gCfg, bhCfg, cid) } func newBlockHistoryEstimator(t *testing.T, c evmclient.Client, cfg gas.Config, gCfg gas.GasEstimatorConfig, bhCfg gas.BlockHistoryConfig) *gas.BlockHistoryEstimator { @@ -1300,69 +1300,69 @@ func TestBlockHistoryEstimator_IsUsable(t *testing.T) { } t.Run("returns false if transaction has 0 gas limit", func(t *testing.T) { tx := evmtypes.Transaction{Type: 0x0, GasPrice: assets.NewWeiI(10), GasLimit: 0, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) t.Run("returns false if transaction gas limit is nil and tx type is 0x0", func(t *testing.T) { tx := evmtypes.Transaction{Type: 0x0, GasPrice: nil, GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) t.Run("returns false if transaction is of type 0x7e only on Optimism", func(t *testing.T) { cfg.ChainTypeF = "optimismBedrock" tx := evmtypes.Transaction{Type: 0x7e, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) cfg.ChainTypeF = "" - assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) t.Run("returns false if transaction is of type 0x7c or 0x7b only on Celo", func(t *testing.T) { cfg.ChainTypeF = "celo" tx := evmtypes.Transaction{Type: 0x7c, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) tx2 := evmtypes.Transaction{Type: 0x7b, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) cfg.ChainTypeF = "" - assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) - assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) + assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) t.Run("returns false if transaction is of type 0x16 only on WeMix", func(t *testing.T) { cfg.ChainTypeF = "wemix" tx := evmtypes.Transaction{Type: 0x16, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) t.Run("returns false if transaction has base fee higher than the gas price only on Celo", func(t *testing.T) { cfg.ChainTypeF = "celo" tx := evmtypes.Transaction{Type: 0x0, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) tx2 := evmtypes.Transaction{Type: 0x2, MaxPriorityFeePerGas: assets.NewWeiI(200), MaxFeePerGas: assets.NewWeiI(250), GasPrice: assets.NewWeiI(50), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) cfg.ChainTypeF = "" - assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) - assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) + assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) t.Run("returns false if transaction is of type 0x71 or 0xff only on zkSync", func(t *testing.T) { cfg.ChainTypeF = string(config.ChainZkSync) tx := evmtypes.Transaction{Type: 0x71, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()} - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) tx.Type = 0x02 - assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) tx.Type = 0xff - assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) cfg.ChainTypeF = "" - assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t))) + assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.Test(t))) }) } @@ -2040,7 +2040,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { cfg := gas.NewMockConfig() bhCfg := newBlockHistoryConfig() bhCfg.CheckInclusionBlocksF = uint16(4) - lggr, obs := logger.TestLoggerObserved(t, zapcore.DebugLevel) + lggr, obs := logger.TestObserved(t, zapcore.DebugLevel) geCfg := &gas.MockGasEstimatorConfig{} geCfg.EIP1559DynamicFeesF = false @@ -2381,7 +2381,7 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { gasPrice, gasLimit, err := bhe.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), 100000, maxGasPrice, nil) require.NoError(t, err) - expectedGasPrice, expectedGasLimit, err := gas.BumpLegacyGasPriceOnly(geCfg, logger.TestLogger(t), nil, assets.NewWeiI(42), 100000, maxGasPrice) + expectedGasPrice, expectedGasLimit, err := gas.BumpLegacyGasPriceOnly(geCfg, logger.TestSugared(t), nil, assets.NewWeiI(42), 100000, maxGasPrice) require.NoError(t, err) assert.Equal(t, expectedGasLimit, gasLimit) @@ -2395,7 +2395,7 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { massive := assets.NewWeiI(100000000000000) gas.SetGasPrice(bhe, massive) - expectedGasPrice, expectedGasLimit, err := gas.BumpLegacyGasPriceOnly(geCfg, logger.TestLogger(t), massive, assets.NewWeiI(42), 100000, maxGasPrice) + expectedGasPrice, expectedGasLimit, err := gas.BumpLegacyGasPriceOnly(geCfg, logger.TestSugared(t), massive, assets.NewWeiI(42), 100000, maxGasPrice) require.NoError(t, err) assert.Equal(t, expectedGasLimit, gasLimit) diff --git a/core/chains/evm/gas/cmd/arbgas/main.go b/core/chains/evm/gas/cmd/arbgas/main.go index 6b79ff2f130..e4fb1b4e882 100644 --- a/core/chains/evm/gas/cmd/arbgas/main.go +++ b/core/chains/evm/gas/cmd/arbgas/main.go @@ -9,12 +9,11 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/rpc" - "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func main() { @@ -22,9 +21,10 @@ func main() { log.Fatal("Expected one URL argument but got", l-1) } url := os.Args[1] - lggr, sync := logger.NewLogger() - defer func() { _ = sync() }() - lggr.SetLogLevel(zapcore.DebugLevel) + lggr, err := logger.New() + if err != nil { + log.Fatal("Failed to create logger:", err) + } ctx := context.Background() withEstimator(ctx, logger.Sugared(lggr), url, func(e gas.EvmEstimator) { diff --git a/core/chains/evm/gas/fixed_price_estimator.go b/core/chains/evm/gas/fixed_price_estimator.go index 7eb7454dad9..4d9f45a1bd4 100644 --- a/core/chains/evm/gas/fixed_price_estimator.go +++ b/core/chains/evm/gas/fixed_price_estimator.go @@ -5,11 +5,11 @@ import ( "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) var _ EvmEstimator = (*fixedPriceEstimator)(nil) @@ -45,7 +45,7 @@ type fixedPriceEstimatorBlockHistoryConfig interface { // NewFixedPriceEstimator returns a new "FixedPrice" estimator which will // always use the config default values for gas prices and limits func NewFixedPriceEstimator(cfg fixedPriceEstimatorConfig, bhCfg fixedPriceEstimatorBlockHistoryConfig, lggr logger.Logger) EvmEstimator { - return &fixedPriceEstimator{cfg, bhCfg, logger.Sugared(lggr.Named("FixedPriceEstimator"))} + return &fixedPriceEstimator{cfg, bhCfg, logger.Sugared(logger.Named(lggr, "FixedPriceEstimator"))} } func (f *fixedPriceEstimator) Start(context.Context) error { diff --git a/core/chains/evm/gas/fixed_price_estimator_test.go b/core/chains/evm/gas/fixed_price_estimator_test.go index 9fa0997c103..968275ace48 100644 --- a/core/chains/evm/gas/fixed_price_estimator_test.go +++ b/core/chains/evm/gas/fixed_price_estimator_test.go @@ -6,10 +6,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type blockHistoryConfig struct { @@ -26,7 +26,7 @@ func Test_FixedPriceEstimator(t *testing.T) { t.Run("GetLegacyGas returns EvmGasPriceDefault from config, with multiplier applied", func(t *testing.T) { config := &gas.MockGasEstimatorConfig{} - f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, logger.TestLogger(t)) + f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, logger.Test(t)) config.PriceDefaultF = assets.NewWeiI(42) config.LimitMultiplierF = float32(1.1) @@ -43,7 +43,7 @@ func Test_FixedPriceEstimator(t *testing.T) { config.PriceDefaultF = assets.NewWeiI(42) config.LimitMultiplierF = float32(1.1) config.PriceMaxF = assets.NewWeiI(35) - f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, logger.TestLogger(t)) + f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, logger.Test(t)) gasPrice, gasLimit, err := f.GetLegacyGas(testutils.Context(t), nil, 100000, assets.NewWeiI(30)) require.NoError(t, err) @@ -57,7 +57,7 @@ func Test_FixedPriceEstimator(t *testing.T) { config.LimitMultiplierF = float32(1.1) config.PriceMaxF = assets.NewWeiI(20) - f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, logger.TestLogger(t)) + f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, logger.Test(t)) gasPrice, gasLimit, err := f.GetLegacyGas(testutils.Context(t), nil, 100000, assets.NewWeiI(30)) require.NoError(t, err) assert.Equal(t, 110000, int(gasLimit)) @@ -72,7 +72,7 @@ func Test_FixedPriceEstimator(t *testing.T) { config.BumpPercentF = uint16(10) config.BumpMinF = assets.NewWeiI(150) - lggr := logger.TestLogger(t) + lggr := logger.TestSugared(t) f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, lggr) gasPrice, gasLimit, err := f.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), 100000, maxGasPrice, nil) @@ -93,7 +93,7 @@ func Test_FixedPriceEstimator(t *testing.T) { config.FeeCapDefaultF = assets.NewWeiI(100) config.BumpThresholdF = uint64(3) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, lggr) fee, gasLimit, err := f.GetDynamicFee(testutils.Context(t), 100000, maxGasPrice) @@ -130,7 +130,7 @@ func Test_FixedPriceEstimator(t *testing.T) { config.BumpMinF = assets.NewWeiI(150) config.BumpPercentF = uint16(10) - lggr := logger.TestLogger(t) + lggr := logger.TestSugared(t) f := gas.NewFixedPriceEstimator(config, &blockHistoryConfig{}, lggr) originalFee := gas.DynamicFee{FeeCap: assets.NewWeiI(100), TipCap: assets.NewWeiI(25)} diff --git a/core/chains/evm/gas/gas_test.go b/core/chains/evm/gas/gas_test.go index a3f7224a094..355d39b6ce8 100644 --- a/core/chains/evm/gas/gas_test.go +++ b/core/chains/evm/gas/gas_test.go @@ -8,9 +8,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func Test_BumpLegacyGasPriceOnly(t *testing.T) { @@ -95,7 +95,7 @@ func Test_BumpLegacyGasPriceOnly(t *testing.T) { cfg.BumpMinF = test.bumpMin cfg.PriceMaxF = test.priceMax cfg.LimitMultiplierF = test.limitMultiplierPercent - actual, limit, err := gas.BumpLegacyGasPriceOnly(cfg, logger.TestLogger(t), test.currentGasPrice, test.originalGasPrice, test.originalLimit, test.priceMax) + actual, limit, err := gas.BumpLegacyGasPriceOnly(cfg, logger.TestSugared(t), test.currentGasPrice, test.originalGasPrice, test.originalLimit, test.priceMax) require.NoError(t, err) if actual.Cmp(test.expectedGasPrice) != 0 { t.Fatalf("Expected %s but got %s", test.expectedGasPrice.String(), actual.String()) @@ -115,7 +115,7 @@ func Test_BumpLegacyGasPriceOnly_HitsMaxError(t *testing.T) { cfg.PriceMaxF = priceMax originalGasPrice := toWei("3e10") // 30 GWei - _, _, err := gas.BumpLegacyGasPriceOnly(cfg, logger.TestLogger(t), nil, originalGasPrice, 42, priceMax) + _, _, err := gas.BumpLegacyGasPriceOnly(cfg, logger.TestSugared(t), nil, originalGasPrice, 42, priceMax) require.Error(t, err) require.Contains(t, err.Error(), "bumped gas price of 45 gwei would exceed configured max gas price of 40 gwei (original price was 30 gwei)") } @@ -124,7 +124,7 @@ func Test_BumpLegacyGasPriceOnly_NoBumpError(t *testing.T) { t.Parallel() priceMax := assets.GWei(40) - lggr := logger.TestLogger(t) + lggr := logger.TestSugared(t) cfg := &gas.MockGasEstimatorConfig{} cfg.BumpPercentF = uint16(0) @@ -298,7 +298,7 @@ func Test_BumpDynamicFeeOnly(t *testing.T) { cfg.LimitMultiplierF = test.limitMultiplierPercent bufferBlocks := uint16(4) - actual, limit, err := gas.BumpDynamicFeeOnly(cfg, bufferBlocks, logger.TestLogger(t), test.currentTipCap, test.currentBaseFee, test.originalFee, test.originalLimit, test.priceMax) + actual, limit, err := gas.BumpDynamicFeeOnly(cfg, bufferBlocks, logger.TestSugared(t), test.currentTipCap, test.currentBaseFee, test.originalFee, test.originalLimit, test.priceMax) require.NoError(t, err) if actual.TipCap.Cmp(test.expectedFee.TipCap) != 0 { t.Fatalf("TipCap not equal, expected %s but got %s", test.expectedFee.TipCap.String(), actual.TipCap.String()) @@ -324,14 +324,14 @@ func Test_BumpDynamicFeeOnly_HitsMaxError(t *testing.T) { t.Run("tip cap hits max", func(t *testing.T) { originalFee := gas.DynamicFee{TipCap: assets.GWei(30), FeeCap: assets.GWei(100)} - _, _, err := gas.BumpDynamicFeeOnly(cfg, 0, logger.TestLogger(t), nil, nil, originalFee, 42, priceMax) + _, _, err := gas.BumpDynamicFeeOnly(cfg, 0, logger.TestSugared(t), nil, nil, originalFee, 42, priceMax) require.Error(t, err) require.Contains(t, err.Error(), "bumped tip cap of 45 gwei would exceed configured max gas price of 40 gwei (original fee: tip cap 30 gwei, fee cap 100 gwei)") }) t.Run("fee cap hits max", func(t *testing.T) { originalFee := gas.DynamicFee{TipCap: assets.GWei(10), FeeCap: assets.GWei(100)} - _, _, err := gas.BumpDynamicFeeOnly(cfg, 0, logger.TestLogger(t), nil, nil, originalFee, 42, priceMax) + _, _, err := gas.BumpDynamicFeeOnly(cfg, 0, logger.TestSugared(t), nil, nil, originalFee, 42, priceMax) require.Error(t, err) require.Contains(t, err.Error(), "bumped fee cap of 150 gwei would exceed configured max gas price of 40 gwei (original fee: tip cap 10 gwei, fee cap 100 gwei)") }) diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index b6f34ab87a5..4f9a5419702 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/config" @@ -21,7 +22,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go index 1a0fe8b8b24..6a384fa9c54 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle.go @@ -12,11 +12,12 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -93,7 +94,7 @@ func NewL1GasPriceOracle(lggr logger.Logger, ethClient ethClient, chainType conf return &l1GasPriceOracle{ client: ethClient, pollPeriod: PollPeriod, - logger: lggr.Named(fmt.Sprintf("L1GasPriceOracle(%s)", chainType)), + logger: logger.Named(lggr, fmt.Sprintf("L1GasPriceOracle(%s)", chainType)), address: address, callArgs: callArgs, chInitialised: make(chan struct{}), @@ -158,7 +159,7 @@ func (o *l1GasPriceOracle) refresh() (t *time.Timer) { } if len(b) != 32 { // returns uint256; - o.logger.Criticalf("return data length (%d) different than expected (%d)", len(b), 32) + logger.Criticalf(o.logger, "return data length (%d) different than expected (%d)", len(b), 32) return } price := new(big.Int).SetBytes(b) diff --git a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go index 801d72919e7..2defedd6b47 100644 --- a/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go +++ b/core/chains/evm/gas/rollups/l1_gas_price_oracle_test.go @@ -11,12 +11,12 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestL1GasPriceOracle(t *testing.T) { @@ -25,13 +25,13 @@ func TestL1GasPriceOracle(t *testing.T) { t.Run("Unsupported ChainType returns nil", func(t *testing.T) { ethClient := mocks.NewETHClient(t) - assert.Panicsf(t, func() { NewL1GasPriceOracle(logger.TestLogger(t), ethClient, config.ChainCelo) }, "Received unspported chaintype %s", config.ChainCelo) + assert.Panicsf(t, func() { NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainCelo) }, "Received unspported chaintype %s", config.ChainCelo) }) t.Run("Calling L1GasPrice on unstarted L1Oracle returns error", func(t *testing.T) { ethClient := mocks.NewETHClient(t) - oracle := NewL1GasPriceOracle(logger.TestLogger(t), ethClient, config.ChainOptimismBedrock) + oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainOptimismBedrock) _, err := oracle.GasPrice(testutils.Context(t)) assert.EqualError(t, err, "L1GasPriceOracle is not started; cannot estimate gas") @@ -49,7 +49,7 @@ func TestL1GasPriceOracle(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := NewL1GasPriceOracle(logger.TestLogger(t), ethClient, config.ChainArbitrum) + oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainArbitrum) require.NoError(t, oracle.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) @@ -71,7 +71,7 @@ func TestL1GasPriceOracle(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := NewL1GasPriceOracle(logger.TestLogger(t), ethClient, config.ChainKroma) + oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainKroma) require.NoError(t, oracle.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) @@ -93,7 +93,7 @@ func TestL1GasPriceOracle(t *testing.T) { assert.Nil(t, blockNumber) }).Return(common.BigToHash(l1BaseFee).Bytes(), nil) - oracle := NewL1GasPriceOracle(logger.TestLogger(t), ethClient, config.ChainOptimismBedrock) + oracle := NewL1GasPriceOracle(logger.Test(t), ethClient, config.ChainOptimismBedrock) require.NoError(t, oracle.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, oracle.Close()) }) diff --git a/core/chains/evm/gas/suggested_price_estimator.go b/core/chains/evm/gas/suggested_price_estimator.go index cd5acbc6942..2e0d32bfc94 100644 --- a/core/chains/evm/gas/suggested_price_estimator.go +++ b/core/chains/evm/gas/suggested_price_estimator.go @@ -9,12 +9,13 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -49,7 +50,7 @@ func NewSuggestedPriceEstimator(lggr logger.Logger, client rpcClient) EvmEstimat return &SuggestedPriceEstimator{ client: client, pollPeriod: 10 * time.Second, - logger: lggr.Named("SuggestedPriceEstimator"), + logger: logger.Named(lggr, "SuggestedPriceEstimator"), chForceRefetch: make(chan (chan struct{})), chInitialised: make(chan struct{}), chStop: make(chan struct{}), diff --git a/core/chains/evm/gas/suggested_price_estimator_test.go b/core/chains/evm/gas/suggested_price_estimator_test.go index 3b6b8184e3e..304e5359107 100644 --- a/core/chains/evm/gas/suggested_price_estimator_test.go +++ b/core/chains/evm/gas/suggested_price_estimator_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestSuggestedPriceEstimator(t *testing.T) { @@ -27,7 +27,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { t.Run("calling GetLegacyGas on unstarted estimator returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) _, _, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) assert.EqualError(t, err, "estimator is not started") }) @@ -39,7 +39,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { (*big.Int)(res).SetInt64(42) }) - o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) require.NoError(t, o.Start(testutils.Context(t))) t.Cleanup(func() { assert.NoError(t, o.Close()) }) gasPrice, chainSpecificGasLimit, err := o.GetLegacyGas(testutils.Context(t), calldata, gasLimit, maxGasPrice) @@ -50,7 +50,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { t.Run("gas price is lower than user specified max gas price", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -68,7 +68,7 @@ func TestSuggestedPriceEstimator(t *testing.T) { t.Run("gas price is lower than global max gas price", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(nil).Run(func(args mock.Arguments) { res := args.Get(1).(*hexutil.Big) @@ -85,14 +85,14 @@ func TestSuggestedPriceEstimator(t *testing.T) { t.Run("calling BumpLegacyGas always returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) _, _, err := o.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), gasLimit, assets.NewWeiI(10), nil) assert.EqualError(t, err, "bump gas is not supported for this chain") }) t.Run("calling GetLegacyGas on started estimator if initial call failed returns error", func(t *testing.T) { client := mocks.NewRPCClient(t) - o := gas.NewSuggestedPriceEstimator(logger.TestLogger(t), client) + o := gas.NewSuggestedPriceEstimator(logger.Test(t), client) client.On("CallContext", mock.Anything, mock.Anything, "eth_gasPrice").Return(errors.New("kaboom")) diff --git a/core/chains/evm/headtracker/head_broadcaster.go b/core/chains/evm/headtracker/head_broadcaster.go index b47fbc2726f..9929646441a 100644 --- a/core/chains/evm/headtracker/head_broadcaster.go +++ b/core/chains/evm/headtracker/head_broadcaster.go @@ -3,10 +3,10 @@ package headtracker import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/headtracker" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type headBroadcaster = headtracker.HeadBroadcaster[*evmtypes.Head, common.Hash] diff --git a/core/chains/evm/headtracker/head_broadcaster_test.go b/core/chains/evm/headtracker/head_broadcaster_test.go index 5c2423f328a..6fb151bfe6c 100644 --- a/core/chains/evm/headtracker/head_broadcaster_test.go +++ b/core/chains/evm/headtracker/head_broadcaster_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonhtrk "github.com/smartcontractkit/chainlink/v2/common/headtracker" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" @@ -20,7 +21,6 @@ import ( "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" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -48,7 +48,7 @@ func TestHeadBroadcaster_Subscribe(t *testing.T) { }) evmCfg := evmtest.NewChainScopedConfig(t, cfg) db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) sub := commonmocks.NewSubscription(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -101,7 +101,7 @@ func TestHeadBroadcaster_BroadcastNewLongestChain(t *testing.T) { t.Parallel() g := gomega.NewWithT(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) broadcaster := headtracker.NewHeadBroadcaster(lggr) err := broadcaster.Start(testutils.Context(t)) @@ -143,7 +143,7 @@ func TestHeadBroadcaster_BroadcastNewLongestChain(t *testing.T) { func TestHeadBroadcaster_TrackableCallbackTimeout(t *testing.T) { t.Parallel() - lggr := logger.TestLogger(t) + lggr := logger.Test(t) broadcaster := headtracker.NewHeadBroadcaster(lggr) err := broadcaster.Start(testutils.Context(t)) diff --git a/core/chains/evm/headtracker/head_listener.go b/core/chains/evm/headtracker/head_listener.go index 3c81c8895ea..242b59e9a82 100644 --- a/core/chains/evm/headtracker/head_listener.go +++ b/core/chains/evm/headtracker/head_listener.go @@ -6,11 +6,11 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/headtracker" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type headListener = headtracker.HeadListener[*evmtypes.Head, ethereum.Subscription, *big.Int, common.Hash] diff --git a/core/chains/evm/headtracker/head_listener_test.go b/core/chains/evm/headtracker/head_listener_test.go index dff97f58436..8bb761bdfaa 100644 --- a/core/chains/evm/headtracker/head_listener_test.go +++ b/core/chains/evm/headtracker/head_listener_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -19,7 +20,6 @@ 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/store/models" ) @@ -35,7 +35,7 @@ func Test_HeadListener_HappyPath(t *testing.T) { // - 3 heads is passed to callback // - ethClient methods are invoked - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { // no need to test head timeouts here @@ -96,7 +96,7 @@ func Test_HeadListener_NotReceivingHeads(t *testing.T) { // - send one head, make sure ReceivingHeads() is true // - do not send any heads within BlockEmissionIdleWarningThreshold and check ReceivingHeads() is false - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { @@ -162,7 +162,7 @@ func Test_HeadListener_SubscriptionErr(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - l := logger.TestLogger(t) + l := logger.Test(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) cfg := configtest.NewGeneralConfig(t, nil) evmcfg := evmtest.NewChainScopedConfig(t, cfg) diff --git a/core/chains/evm/headtracker/head_saver.go b/core/chains/evm/headtracker/head_saver.go index 6b4b20c89c5..92eedaf153e 100644 --- a/core/chains/evm/headtracker/head_saver.go +++ b/core/chains/evm/headtracker/head_saver.go @@ -5,10 +5,10 @@ import ( "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type headSaver struct { @@ -26,7 +26,7 @@ func NewHeadSaver(lggr logger.Logger, orm ORM, config Config, htConfig HeadTrack orm: orm, config: config, htConfig: htConfig, - logger: lggr.Named("HeadSaver"), + logger: logger.Named(lggr, "HeadSaver"), heads: NewHeads(), } } diff --git a/core/chains/evm/headtracker/head_saver_test.go b/core/chains/evm/headtracker/head_saver_test.go index 5ed85adc598..f541330bc98 100644 --- a/core/chains/evm/headtracker/head_saver_test.go +++ b/core/chains/evm/headtracker/head_saver_test.go @@ -6,13 +6,13 @@ import ( "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "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/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type headTrackerConfig struct { @@ -43,7 +43,7 @@ func (c *config) BlockEmissionIdleWarningThreshold() time.Duration { func configureSaver(t *testing.T) (httypes.HeadSaver, headtracker.ORM) { db := pgtest.NewSqlxDB(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) cfg := configtest.NewGeneralConfig(t, nil) htCfg := &config{finalityDepth: uint32(1)} orm := headtracker.NewORM(db, lggr, cfg.Database(), cltest.FixtureChainID) diff --git a/core/chains/evm/headtracker/head_tracker.go b/core/chains/evm/headtracker/head_tracker.go index dd94f96383b..b86a6b5fe22 100644 --- a/core/chains/evm/headtracker/head_tracker.go +++ b/core/chains/evm/headtracker/head_tracker.go @@ -8,12 +8,12 @@ import ( "github.com/ethereum/go-ethereum/common" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/headtracker" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/headtracker/head_tracker_test.go b/core/chains/evm/headtracker/head_tracker_test.go index d734b230e1b..4d3cebd24e2 100644 --- a/core/chains/evm/headtracker/head_tracker_test.go +++ b/core/chains/evm/headtracker/head_tracker_test.go @@ -20,6 +20,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" @@ -32,7 +33,6 @@ import ( "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/utils" @@ -49,7 +49,7 @@ func TestHeadTracker_New(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := configtest.NewGeneralConfig(t, nil) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(cltest.Head(0), nil) @@ -73,7 +73,7 @@ func TestHeadTracker_Save_InsertsAndTrimsTable(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -118,7 +118,7 @@ func TestHeadTracker_Get(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(db, logger, config.Database(), cltest.FixtureChainID) @@ -165,7 +165,7 @@ func TestHeadTracker_Start_NewHeads(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(db, logger, config.Database(), cltest.FixtureChainID) @@ -190,7 +190,7 @@ func TestHeadTracker_Start_CancelContext(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(db, logger, config.Database(), cltest.FixtureChainID) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -230,7 +230,7 @@ func TestHeadTracker_CallsHeadTrackableCallbacks(t *testing.T) { g := gomega.NewWithT(t) db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(db, logger, config.Database(), cltest.FixtureChainID) @@ -269,7 +269,7 @@ func TestHeadTracker_ReconnectOnError(t *testing.T) { g := gomega.NewWithT(t) db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(db, logger, config.Database(), cltest.FixtureChainID) @@ -305,7 +305,7 @@ func TestHeadTracker_ResubscribeOnSubscriptionError(t *testing.T) { g := gomega.NewWithT(t) db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) orm := headtracker.NewORM(db, logger, config.Database(), cltest.FixtureChainID) @@ -352,7 +352,7 @@ func TestHeadTracker_Start_LoadsLatestChain(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := cltest.NewTestChainScopedConfig(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -417,7 +417,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingEnabled(t *testing.T) t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FinalityDepth = ptr[uint32](50) @@ -545,7 +545,7 @@ func TestHeadTracker_SwitchesToLongestChainWithHeadSamplingDisabled(t *testing.T t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) config := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FinalityDepth = ptr[uint32](50) @@ -773,7 +773,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("does nothing if all the heads are in database", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) for i := range heads { require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &heads[i])) @@ -790,7 +790,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("fetches a missing head", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) for i := range heads { require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &heads[i])) @@ -826,7 +826,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("fetches only heads that are missing", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) for i := range heads { require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &heads[i])) @@ -859,7 +859,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("does not backfill if chain length is already greater than or equal to depth", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) for i := range heads { require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &heads[i])) @@ -880,7 +880,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("only backfills to height 0 if chain length would otherwise cause it to try and fetch a negative head", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) ethClient := evmtest.NewEthClientMock(t) @@ -905,7 +905,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("abandons backfill and returns error if the eth node returns not found", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) for i := range heads { require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &heads[i])) @@ -936,7 +936,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("abandons backfill and returns error if the context time budget is exceeded", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) for i := range heads { require.NoError(t, orm.IdempotentInsertHead(testutils.Context(t), &heads[i])) @@ -965,7 +965,7 @@ func TestHeadTracker_Backfill(t *testing.T) { t.Run("abandons backfill and returns error when fetching a block by hash fails, indicating a reorg", func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - logger := logger.TestLogger(t) + logger := logger.Test(t) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) ethClient := evmtest.NewEthClientMock(t) ethClient.On("ConfiguredChainID", mock.Anything).Return(evmtest.MustGetDefaultChainID(t, cfg.EVMConfigs()), nil) @@ -989,7 +989,7 @@ func TestHeadTracker_Backfill(t *testing.T) { } func createHeadTracker(t *testing.T, ethClient evmclient.Client, config headtracker.Config, htConfig headtracker.HeadTrackerConfig, orm headtracker.ORM) *headTrackerUniverse { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) mailMon := utils.NewMailboxMonitor(t.Name()) @@ -1004,7 +1004,7 @@ func createHeadTracker(t *testing.T, ethClient evmclient.Client, config headtrac func createHeadTrackerWithNeverSleeper(t *testing.T, ethClient evmclient.Client, cfg chainlink.GeneralConfig, orm headtracker.ORM) *headTrackerUniverse { evmcfg := evmtest.NewChainScopedConfig(t, cfg) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, evmcfg.EVM(), evmcfg.EVM().HeadTracker()) mailMon := utils.NewMailboxMonitor(t.Name()) @@ -1021,7 +1021,7 @@ func createHeadTrackerWithNeverSleeper(t *testing.T, ethClient evmclient.Client, } func createHeadTrackerWithChecker(t *testing.T, ethClient evmclient.Client, config headtracker.Config, htConfig headtracker.HeadTrackerConfig, orm headtracker.ORM, checker httypes.HeadTrackable) *headTrackerUniverse { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) hb := headtracker.NewHeadBroadcaster(lggr) hs := headtracker.NewHeadSaver(lggr, orm, config, htConfig) hb.Subscribe(checker) diff --git a/core/chains/evm/headtracker/orm.go b/core/chains/evm/headtracker/orm.go index 34f46ce44de..88d569b9a21 100644 --- a/core/chains/evm/headtracker/orm.go +++ b/core/chains/evm/headtracker/orm.go @@ -10,8 +10,8 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -36,7 +36,7 @@ type orm struct { } func NewORM(db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig, chainID big.Int) ORM { - return &orm{pg.NewQ(db, lggr.Named("HeadTrackerORM"), cfg), utils.Big(chainID)} + return &orm{pg.NewQ(db, logger.Named(lggr, "HeadTrackerORM"), cfg), utils.Big(chainID)} } func (orm *orm) IdempotentInsertHead(ctx context.Context, head *evmtypes.Head) error { diff --git a/core/chains/evm/headtracker/orm_test.go b/core/chains/evm/headtracker/orm_test.go index 123478ff902..c9a2146daf2 100644 --- a/core/chains/evm/headtracker/orm_test.go +++ b/core/chains/evm/headtracker/orm_test.go @@ -11,17 +11,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) func TestORM_IdempotentInsertHead(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) cfg := configtest.NewGeneralConfig(t, nil) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) @@ -47,7 +47,7 @@ func TestORM_TrimOldHeads(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) cfg := configtest.NewGeneralConfig(t, nil) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) @@ -72,7 +72,7 @@ func TestORM_HeadByHash(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) cfg := configtest.NewGeneralConfig(t, nil) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) @@ -95,7 +95,7 @@ func TestORM_HeadByHash_NotFound(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) cfg := configtest.NewGeneralConfig(t, nil) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) @@ -110,7 +110,7 @@ func TestORM_LatestHeads_NoRows(t *testing.T) { t.Parallel() db := pgtest.NewSqlxDB(t) - logger := logger.TestLogger(t) + logger := logger.Test(t) cfg := configtest.NewGeneralConfig(t, nil) orm := headtracker.NewORM(db, logger, cfg.Database(), cltest.FixtureChainID) diff --git a/core/chains/evm/log/broadcaster.go b/core/chains/evm/log/broadcaster.go index d69fd696fd7..f4528396093 100644 --- a/core/chains/evm/log/broadcaster.go +++ b/core/chains/evm/log/broadcaster.go @@ -12,13 +12,13 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -167,7 +167,7 @@ var _ Broadcaster = (*broadcaster)(nil) // NewBroadcaster creates a new instance of the broadcaster func NewBroadcaster(orm ORM, ethClient evmclient.Client, config Config, lggr logger.Logger, highestSavedHead *evmtypes.Head, mailMon *utils.MailboxMonitor) *broadcaster { chStop := make(chan struct{}) - lggr = lggr.Named("LogBroadcaster") + lggr = logger.Named(lggr, "LogBroadcaster") chainId := ethClient.ConfiguredChainID() return &broadcaster{ orm: orm, @@ -443,7 +443,7 @@ func (b *broadcaster) eventLoop(chRawLogs <-chan types.Log, chErr <-chan error) // Do we have logs in the pool? // They are are invalid, since we may have missed 'removed' logs. if blockNum := b.invalidatePool(); blockNum > 0 { - lggr = lggr.With("blockNumber", blockNum) + lggr = logger.With(lggr, "blockNumber", blockNum) } lggr.Debugw("Subscription terminated. Backfilling after resubscribing") return true, err diff --git a/core/chains/evm/log/eth_subscriber.go b/core/chains/evm/log/eth_subscriber.go index b4d386140e7..96a7a8248a6 100644 --- a/core/chains/evm/log/eth_subscriber.go +++ b/core/chains/evm/log/eth_subscriber.go @@ -10,10 +10,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -27,11 +27,11 @@ type ( } ) -func newEthSubscriber(ethClient evmclient.Client, config Config, logger logger.Logger, chStop chan struct{}) *ethSubscriber { +func newEthSubscriber(ethClient evmclient.Client, config Config, lggr logger.Logger, chStop chan struct{}) *ethSubscriber { return ðSubscriber{ ethClient: ethClient, config: config, - logger: logger.Named("EthSubscriber"), + logger: logger.Named(lggr, "EthSubscriber"), chStop: chStop, } } diff --git a/core/chains/evm/log/helpers_internal_test.go b/core/chains/evm/log/helpers_internal_test.go index b3b4890e360..38f40bd329e 100644 --- a/core/chains/evm/log/helpers_internal_test.go +++ b/core/chains/evm/log/helpers_internal_test.go @@ -3,9 +3,9 @@ package log import ( "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/log/helpers_test.go b/core/chains/evm/log/helpers_test.go index f787002578e..ac7eb863e62 100644 --- a/core/chains/evm/log/helpers_test.go +++ b/core/chains/evm/log/helpers_test.go @@ -20,6 +20,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -34,7 +35,6 @@ import ( "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/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -88,7 +88,7 @@ func newBroadcasterHelperWithEthClient(t *testing.T, ethClient evmclient.Client, } }) config := evmtest.NewChainScopedConfig(t, globalConfig) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) db := pgtest.NewSqlxDB(t) @@ -265,7 +265,7 @@ func (helper *broadcasterHelper) newLogListenerWithJob(name string) *simpleLogLi var rec received return &simpleLogListener{ db: db, - lggr: logger.TestLogger(t), + lggr: logger.Test(t), cfg: helper.config.Database(), name: name, received: &rec, @@ -281,7 +281,7 @@ func (listener *simpleLogListener) SkipMarkingConsumed(skip bool) { func (listener *simpleLogListener) HandleLog(lb log.Broadcast) { listener.received.Lock() defer listener.received.Unlock() - listener.lggr.Tracef("Listener %v HandleLog for block %v %v received at %v %v", listener.name, lb.RawLog().BlockNumber, lb.RawLog().BlockHash, lb.LatestBlockNumber(), lb.LatestBlockHash()) + logger.Tracef(listener.lggr, "Listener %v HandleLog for block %v %v received at %v %v", listener.name, lb.RawLog().BlockNumber, lb.RawLog().BlockHash, lb.LatestBlockNumber(), lb.LatestBlockHash()) listener.received.logs = append(listener.received.logs, lb.RawLog()) listener.received.broadcasts = append(listener.received.broadcasts, lb) diff --git a/core/chains/evm/log/integration_test.go b/core/chains/evm/log/integration_test.go index 137b4c7292a..edc04a4ada4 100644 --- a/core/chains/evm/log/integration_test.go +++ b/core/chains/evm/log/integration_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" logmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" @@ -25,7 +26,6 @@ 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/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/srvctest" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -266,7 +266,7 @@ func TestBroadcaster_BackfillUnconsumedAfterCrash(t *testing.T) { helper := newBroadcasterHelper(t, 0, 1, logs, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FinalityDepth = ptr[uint32](confs) }) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) orm := log.NewORM(helper.db, lggr, helper.config.Database(), cltest.FixtureChainID) listener := helper.newLogListenerWithJob("one") @@ -292,7 +292,7 @@ func TestBroadcaster_BackfillUnconsumedAfterCrash(t *testing.T) { helper := newBroadcasterHelper(t, 2, 1, logs, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FinalityDepth = ptr[uint32](confs) }) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) orm := log.NewORM(helper.db, lggr, helper.config.Database(), cltest.FixtureChainID) listener := helper.newLogListenerWithJob("one") @@ -317,7 +317,7 @@ func TestBroadcaster_BackfillUnconsumedAfterCrash(t *testing.T) { helper := newBroadcasterHelper(t, 4, 1, logs, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FinalityDepth = ptr[uint32](confs) }) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) orm := log.NewORM(helper.db, lggr, helper.config.Database(), cltest.FixtureChainID) listener := helper.newLogListenerWithJob("one") @@ -342,7 +342,7 @@ func TestBroadcaster_BackfillUnconsumedAfterCrash(t *testing.T) { helper := newBroadcasterHelper(t, 7, 1, logs[1:], func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].FinalityDepth = ptr[uint32](confs) }) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) orm := log.NewORM(helper.db, lggr, helper.config.Database(), cltest.FixtureChainID) listener := helper.newLogListenerWithJob("one") listener2 := helper.newLogListenerWithJob("two") @@ -468,7 +468,7 @@ func TestBroadcaster_BackfillInBatches(t *testing.T) { var backfillCount atomic.Int64 - lggr := logger.TestLogger(t) + lggr := logger.Test(t) backfillStart := lastStoredBlockHeight - numConfirmations - int64(blockBackfillDepth) // the first backfill should start from before the last stored head mockEth.CheckFilterLogs = func(fromBlock int64, toBlock int64) { @@ -539,7 +539,7 @@ func TestBroadcaster_BackfillALargeNumberOfLogs(t *testing.T) { var backfillCount atomic.Int64 - lggr := logger.TestLogger(t) + lggr := logger.Test(t) mockEth.CheckFilterLogs = func(fromBlock int64, toBlock int64) { times := backfillCount.Add(1) - 1 lggr.Warnf("Log Batch: --------- times %v - %v, %v", times, fromBlock, toBlock) @@ -1325,7 +1325,7 @@ func TestBroadcaster_AppendLogChannel(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) mailMon := srvctest.Start(t, utils.NewMailboxMonitor(t.Name())) - lb := log.NewBroadcaster(nil, ethClient, nil, logger.TestLogger(t), nil, mailMon) + lb := log.NewBroadcaster(nil, ethClient, nil, logger.Test(t), nil, mailMon) chCombined := lb.ExportedAppendLogChannel(ch1, ch2) chCombined = lb.ExportedAppendLogChannel(chCombined, ch3) diff --git a/core/chains/evm/log/orm.go b/core/chains/evm/log/orm.go index d383419d728..a2bcab6e785 100644 --- a/core/chains/evm/log/orm.go +++ b/core/chains/evm/log/orm.go @@ -11,7 +11,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/chains/evm/log/orm_test.go b/core/chains/evm/log/orm_test.go index 48524896cf4..758cfa1d690 100644 --- a/core/chains/evm/log/orm_test.go +++ b/core/chains/evm/log/orm_test.go @@ -10,18 +10,18 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) func TestORM_broadcasts(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() orm := log.NewORM(db, lggr, cfg.Database(), cltest.FixtureChainID) @@ -134,7 +134,7 @@ func TestORM_broadcasts(t *testing.T) { func TestORM_pending(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) orm := log.NewORM(db, lggr, cfg.Database(), cltest.FixtureChainID) num, err := orm.GetPendingMinBlock() @@ -160,7 +160,7 @@ func TestORM_pending(t *testing.T) { func TestORM_MarkUnconsumed(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() orm := log.NewORM(db, lggr, cfg.Database(), cltest.FixtureChainID) @@ -259,7 +259,7 @@ func TestORM_Reinitialize(t *testing.T) { t.Run(tt.name, func(t *testing.T) { db := pgtest.NewSqlxDB(t) cfg := configtest.NewGeneralConfig(t, nil) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) orm := log.NewORM(db, lggr, cfg.Database(), cltest.FixtureChainID) jobID := cltest.MustInsertV2JobSpec(t, db, common.BigToAddress(big.NewInt(rand.Int63()))).ID diff --git a/core/chains/evm/log/pool.go b/core/chains/evm/log/pool.go index 4511c26eb60..7d534ff4103 100644 --- a/core/chains/evm/log/pool.go +++ b/core/chains/evm/log/pool.go @@ -8,7 +8,7 @@ import ( heaps "github.com/theodesp/go-heaps" pairingHeap "github.com/theodesp/go-heaps/pairing" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" ) // The Log Pool interface. @@ -59,7 +59,7 @@ func newLogPool(lggr logger.Logger) *logPool { hashesByBlockNumbers: make(map[uint64]map[common.Hash]struct{}), logsByBlockHash: make(map[common.Hash]map[uint]map[uint]types.Log), heap: pairingHeap.New(), - logger: lggr.Named("LogPool"), + logger: logger.Named(lggr, "LogPool"), } } diff --git a/core/chains/evm/log/pool_test.go b/core/chains/evm/log/pool_test.go index 9e760f57262..c4789f5acd4 100644 --- a/core/chains/evm/log/pool_test.go +++ b/core/chains/evm/log/pool_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -49,7 +49,7 @@ var ( func TestUnit_AddLog(t *testing.T) { t.Parallel() - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) blockHash := common.BigToHash(big.NewInt(1)) l1 := types.Log{ @@ -105,7 +105,7 @@ func TestUnit_AddLog(t *testing.T) { func TestUnit_GetAndDeleteAll(t *testing.T) { t.Parallel() - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) p.addLog(L1) p.addLog(L1) // duplicate an add p.addLog(L21) @@ -139,7 +139,7 @@ func TestUnit_GetAndDeleteAll(t *testing.T) { func TestUnit_GetLogsToSendWhenEmptyPool(t *testing.T) { t.Parallel() - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) logsOnBlocks, minBlockNumToSend := p.getLogsToSend(1) assert.Equal(t, int64(0), minBlockNumToSend) assert.ElementsMatch(t, []logsOnBlock{}, logsOnBlocks) @@ -206,7 +206,7 @@ func TestUnit_GetLogsToSend(t *testing.T) { }, } - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) p.addLog(L1) p.addLog(L21) p.addLog(L3) @@ -222,7 +222,7 @@ func TestUnit_GetLogsToSend(t *testing.T) { func TestUnit_DeleteOlderLogsWhenEmptyPool(t *testing.T) { t.Parallel() - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) keptDepth := p.deleteOlderLogs(1) var expectedKeptDepth *int64 require.Equal(t, expectedKeptDepth, keptDepth) @@ -286,7 +286,7 @@ func TestUnit_DeleteOlderLogs(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) p.addLog(L1) p.addLog(L21) p.addLog(L3) @@ -302,7 +302,7 @@ func TestUnit_DeleteOlderLogs(t *testing.T) { func TestUnit_RemoveBlockWhenEmptyPool(t *testing.T) { t.Parallel() - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) p.removeBlock(L1.BlockHash, L1.BlockNumber) } @@ -343,7 +343,7 @@ func TestUnit_RemoveBlock(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - var p iLogPool = newLogPool(logger.TestLogger(t)) + var p iLogPool = newLogPool(logger.Test(t)) p.addLog(L21) p.addLog(L22) p.addLog(L23) diff --git a/core/chains/evm/log/registrations.go b/core/chains/evm/log/registrations.go index f9cc933d0d0..73f197a6ab6 100644 --- a/core/chains/evm/log/registrations.go +++ b/core/chains/evm/log/registrations.go @@ -9,10 +9,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -70,13 +70,13 @@ type ( subscribers map[*subscriber][][]Topic ) -func newRegistrations(logger logger.Logger, evmChainID big.Int) *registrations { +func newRegistrations(lggr logger.Logger, evmChainID big.Int) *registrations { return ®istrations{ registeredSubs: make(map[*subscriber]struct{}), jobIDAddrs: make(map[int32]map[common.Address]struct{}), handlersByConfs: make(map[uint32]*handler), evmChainID: evmChainID, - logger: logger.Named("Registrations"), + logger: logger.Named(lggr, "Registrations"), } } @@ -85,7 +85,7 @@ func (r *registrations) addSubscriber(sub *subscriber) (needsResubscribe bool) { r.logger.Panicw(err.Error(), "err", err, "addr", sub.opts.Contract.Hex(), "jobID", sub.listener.JobID()) } - r.logger.Tracef("Added subscription %p with job ID %v", sub, sub.listener.JobID()) + logger.Tracef(r.logger, "Added subscription %p with job ID %v", sub, sub.listener.JobID()) handler, exists := r.handlersByConfs[sub.opts.MinIncomingConfirmations] if !exists { @@ -142,7 +142,7 @@ func (r *registrations) removeSubscriber(sub *subscriber) (needsResubscribe bool if err := r.checkRemoveSubscriber(sub); err != nil { r.logger.Panicw(err.Error(), "err", err, "addr", sub.opts.Contract.Hex(), "jobID", sub.listener.JobID()) } - r.logger.Tracef("Removed subscription %p with job ID %v", sub, sub.listener.JobID()) + logger.Tracef(r.logger, "Removed subscription %p with job ID %v", sub, sub.listener.JobID()) handlers, exists := r.handlersByConfs[sub.opts.MinIncomingConfirmations] if !exists { @@ -284,7 +284,7 @@ func (r *handler) addSubscriber(sub *subscriber, handlersWithGreaterConfs []*han for topic, topicValueFilters := range sub.opts.LogsWithTopics { if _, exists := r.lookupSubs[addr][topic]; !exists { - r.logger.Tracef("No existing sub for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + logger.Tracef(r.logger, "No existing sub for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) r.lookupSubs[addr][topic] = make(subscribers) func() { @@ -295,11 +295,11 @@ func (r *handler) addSubscriber(sub *subscriber, handlersWithGreaterConfs []*han // again since even the worst case lookback is already covered for _, existingHandler := range handlersWithGreaterConfs { if _, exists := existingHandler.lookupSubs[addr][topic]; exists { - r.logger.Tracef("Sub already exists for addr %s and topic %s at greater than this MinIncomingConfirmations of %v. Resubscribe is not required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + logger.Tracef(r.logger, "Sub already exists for addr %s and topic %s at greater than this MinIncomingConfirmations of %v. Resubscribe is not required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) return } } - r.logger.Tracef("No sub exists for addr %s and topic %s at this or greater MinIncomingConfirmations of %v. Resubscribe is required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + logger.Tracef(r.logger, "No sub exists for addr %s and topic %s at this or greater MinIncomingConfirmations of %v. Resubscribe is required", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) needsResubscribe = true } }() @@ -332,7 +332,7 @@ func (r *handler) removeSubscriber(sub *subscriber, allHandlers map[uint32]*hand // cleanup and resubscribe if necessary if len(topicMap) == 0 { - r.logger.Tracef("No subs left for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) + logger.Tracef(r.logger, "No subs left for addr %s and topic %s at this MinIncomingConfirmations of %v", addr.Hex(), topic.Hex(), sub.opts.MinIncomingConfirmations) func() { if !needsResubscribe { @@ -344,12 +344,12 @@ func (r *handler) removeSubscriber(sub *subscriber, allHandlers map[uint32]*hand continue } if _, exists := otherHandler.lookupSubs[addr][topic]; exists { - r.logger.Tracef("Sub still exists for addr %s and topic %s. Resubscribe will not be performed", addr.Hex(), topic.Hex()) + logger.Tracef(r.logger, "Sub still exists for addr %s and topic %s. Resubscribe will not be performed", addr.Hex(), topic.Hex()) return } } - r.logger.Tracef("No sub exists for addr %s and topic %s. Resubscribe will be performed", addr.Hex(), topic.Hex()) + logger.Tracef(r.logger, "No sub exists for addr %s and topic %s. Resubscribe will be performed", addr.Hex(), topic.Hex()) needsResubscribe = true } }() diff --git a/core/chains/evm/log/registrations_test.go b/core/chains/evm/log/registrations_test.go index c9d4eba5898..0682564fe72 100644 --- a/core/chains/evm/log/registrations_test.go +++ b/core/chains/evm/log/registrations_test.go @@ -7,8 +7,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -26,7 +26,7 @@ func newTestListener(t *testing.T, jobID int32) testListener { } func newTestRegistrations(t *testing.T) *registrations { - return newRegistrations(logger.TestLogger(t), *testutils.FixtureChainID) + return newRegistrations(logger.Test(t), *testutils.FixtureChainID) } func newTopic() Topic { diff --git a/core/chains/evm/logpoller/helper_test.go b/core/chains/evm/logpoller/helper_test.go index c61d3d5fad6..9e48690a249 100644 --- a/core/chains/evm/logpoller/helper_test.go +++ b/core/chains/evm/logpoller/helper_test.go @@ -19,12 +19,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -46,7 +46,7 @@ type TestHarness struct { } func SetupTH(t testing.TB, useFinalityTag bool, finalityDepth, backfillBatchSize, rpcBatchSize, keepFinalizedBlocksDepth int64) TestHarness { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) chainID := testutils.NewRandomEVMChainID() chainID2 := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index bb93db40378..7c4ea66cec7 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -20,11 +20,11 @@ import ( "github.com/pkg/errors" "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" @@ -136,7 +136,7 @@ func NewLogPoller(orm ORM, ec Client, lggr logger.Logger, pollPeriod time.Durati cancel: cancel, ec: ec, orm: orm, - lggr: lggr.Named("LogPoller"), + lggr: logger.Named(lggr, "LogPoller"), replayStart: make(chan int64), replayComplete: make(chan error), pollPeriod: pollPeriod, @@ -661,7 +661,7 @@ func (lp *logPoller) backfill(ctx context.Context, start, end int64) error { } } if batchSize == 1 { - lp.lggr.Criticalw("Too many log results in a single block, failed to retrieve logs! Node may be running in a degraded state.", "err", err, "from", from, "to", to, "LogBackfillBatchSize", lp.backfillBatchSize) + logger.Criticalw(lp.lggr, "Too many log results in a single block, failed to retrieve logs! Node may be running in a degraded state.", "err", err, "from", from, "to", to, "LogBackfillBatchSize", lp.backfillBatchSize) return err } batchSize /= 2 @@ -916,7 +916,7 @@ func (lp *logPoller) findBlockAfterLCA(ctx context.Context, current *evmtypes.He return nil, err } } - lp.lggr.Criticalw("Reorg greater than finality depth detected", "finalityTag", lp.useFinalityTag, "current", current.Number, "latestFinalized", latestFinalizedBlockNumber) + logger.Criticalw(lp.lggr, "Reorg greater than finality depth detected", "finalityTag", lp.useFinalityTag, "current", current.Number, "latestFinalized", latestFinalizedBlockNumber) rerr := errors.New("Reorg greater than finality depth") lp.SvcErrBuffer.Append(rerr) return nil, rerr diff --git a/core/chains/evm/logpoller/log_poller_internal_test.go b/core/chains/evm/logpoller/log_poller_internal_test.go index 2ef276802ba..e3ba8b655e8 100644 --- a/core/chains/evm/logpoller/log_poller_internal_test.go +++ b/core/chains/evm/logpoller/log_poller_internal_test.go @@ -20,13 +20,14 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -55,7 +56,7 @@ func TestLogPoller_RegisterFilter(t *testing.T) { a1 := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbb") a2 := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbc") - lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zapcore.ErrorLevel) chainID := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) @@ -137,7 +138,7 @@ func TestLogPoller_RegisterFilter(t *testing.T) { func TestLogPoller_ConvertLogs(t *testing.T) { t.Parallel() - lggr := logger.TestLogger(t) + lggr := logger.Test(t) topics := []common.Hash{EmitterABI.Events["Log1"].ID} @@ -192,7 +193,7 @@ func TestFilterName(t *testing.T) { func TestLogPoller_BackupPollerStartup(t *testing.T) { addr := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbc") - lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zapcore.WarnLevel) chainID := testutils.FixtureChainID db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) @@ -235,7 +236,7 @@ func TestLogPoller_Replay(t *testing.T) { t.Parallel() addr := common.HexToAddress("0x2ab9a2dc53736b361b72d900cdf9f78f9406fbbc") - lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.ErrorLevel) + lggr, observedLogs := logger.TestObserved(t, zapcore.ErrorLevel) chainID := testutils.FixtureChainID db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) @@ -437,7 +438,7 @@ func (lp *logPoller) reset() { } func Test_latestBlockAndFinalityDepth(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) chainID := testutils.FixtureChainID db := pgtest.NewSqlxDB(t) orm := NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) @@ -507,7 +508,7 @@ func Test_latestBlockAndFinalityDepth(t *testing.T) { } func benchmarkFilter(b *testing.B, nFilters, nAddresses, nEvents int) { - lggr := logger.TestLogger(b) + lggr := logger.Test(b) lp := NewLogPoller(nil, nil, lggr, 1*time.Hour, false, 2, 3, 2, 1000) for i := 0; i < nFilters; i++ { var addresses []common.Address diff --git a/core/chains/evm/logpoller/log_poller_test.go b/core/chains/evm/logpoller/log_poller_test.go index 94589f505a6..82447bdb5f4 100644 --- a/core/chains/evm/logpoller/log_poller_test.go +++ b/core/chains/evm/logpoller/log_poller_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -34,7 +35,6 @@ import ( "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/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -86,7 +86,7 @@ func populateDatabase(t testing.TB, o *logpoller.DbORM, chainID *big.Int) (commo func BenchmarkSelectLogsCreatedAfter(b *testing.B) { chainId := big.NewInt(137) _, db := heavyweight.FullTestDBV2(b, nil) - o := logpoller.NewORM(chainId, db, logger.TestLogger(b), pgtest.NewQConfig(false)) + o := logpoller.NewORM(chainId, db, logger.Test(b), pgtest.NewQConfig(false)) event, address, _ := populateDatabase(b, o, chainId) // Setting searchDate to pick around 5k logs @@ -106,7 +106,7 @@ func TestPopulateLoadedDB(t *testing.T) { _, db := heavyweight.FullTestDBV2(t, nil) chainID := big.NewInt(137) - o := logpoller.NewORM(big.NewInt(137), db, logger.TestLogger(t), pgtest.NewQConfig(true)) + o := logpoller.NewORM(big.NewInt(137), db, logger.Test(t), pgtest.NewQConfig(true)) event1, address1, address2 := populateDatabase(t, o, chainID) func() { @@ -651,7 +651,7 @@ func TestLogPoller_SynchronizedWithGeth(t *testing.T) { p := gopter.NewProperties(testParams) numChainInserts := 3 finalityDepth := 5 - lggr := logger.TestLogger(t) + lggr := logger.Test(t) db := pgtest.NewSqlxDB(t) owner := testutils.MustNewSimTransactor(t) @@ -1264,7 +1264,7 @@ func TestGetReplayFromBlock(t *testing.T) { func TestLogPoller_DBErrorHandling(t *testing.T) { t.Parallel() ctx := testutils.Context(t) - lggr, observedLogs := logger.TestLoggerObserved(t, zapcore.WarnLevel) + lggr, observedLogs := logger.TestObserved(t, zapcore.WarnLevel) chainID1 := testutils.NewRandomEVMChainID() chainID2 := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) @@ -1332,7 +1332,7 @@ func TestNotifyAfterInsert(t *testing.T) { dbURL = s.Database.URL.URL().String() }) - lggr, _ := logger.TestLoggerObserved(t, zapcore.WarnLevel) + lggr, _ := logger.TestObserved(t, zapcore.WarnLevel) chainID := big.NewInt(1337) o := logpoller.NewORM(chainID, sqlxDB, lggr, pgtest.NewQConfig(true)) @@ -1386,7 +1386,7 @@ type getLogErrData struct { func TestTooManyLogResults(t *testing.T) { ctx := testutils.Context(t) ec := evmtest.NewEthClientMockWithDefaultChain(t) - lggr, obs := logger.TestLoggerObserved(t, zapcore.DebugLevel) + lggr, obs := logger.TestObserved(t, zapcore.DebugLevel) chainID := testutils.NewRandomEVMChainID() db := pgtest.NewSqlxDB(t) o := logpoller.NewORM(chainID, db, lggr, pgtest.NewQConfig(true)) diff --git a/core/chains/evm/logpoller/observability.go b/core/chains/evm/logpoller/observability.go index 03f4b77be25..9826d503b9f 100644 --- a/core/chains/evm/logpoller/observability.go +++ b/core/chains/evm/logpoller/observability.go @@ -9,7 +9,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) diff --git a/core/chains/evm/logpoller/observability_test.go b/core/chains/evm/logpoller/observability_test.go index ded3d7854dd..83bd60a5564 100644 --- a/core/chains/evm/logpoller/observability_test.go +++ b/core/chains/evm/logpoller/observability_test.go @@ -14,9 +14,9 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -97,7 +97,7 @@ func TestMetricsAreProperlyPopulatedForWrites(t *testing.T) { } func createObservedORM(t *testing.T, chainId int64) *ObservedORM { - lggr, _ := logger.TestLoggerObserved(t, zapcore.ErrorLevel) + lggr, _ := logger.TestObserved(t, zapcore.ErrorLevel) db := pgtest.NewSqlxDB(t) return NewObservedORM( big.NewInt(chainId), db, lggr, pgtest.NewQConfig(true), diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index a1b86d2cb2c..c6134ed4b69 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -11,7 +11,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -61,7 +61,7 @@ type DbORM struct { // NewORM creates a DbORM scoped to chainID. func NewORM(chainID *big.Int, db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) *DbORM { - namedLogger := lggr.Named("Configs") + namedLogger := logger.Named(lggr, "Configs") q := pg.NewQ(db, namedLogger, cfg) return &DbORM{ chainID: chainID, diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index 887984055ef..e55ebeccecf 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -14,12 +14,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -1314,7 +1314,7 @@ func TestInsertLogsWithBlock(t *testing.T) { // Using pgtest.NewSqlxDB(t) will run all tests in TXs which is not desired for this type of test // (inner tx rollback will rollback outer tx, blocking rest of execution) _, db := heavyweight.FullTestDBV2(t, nil) - o := logpoller.NewORM(chainID, db, logger.TestLogger(t), pgtest.NewQConfig(true)) + o := logpoller.NewORM(chainID, db, logger.Test(t), pgtest.NewQConfig(true)) correctLog := GenLog(chainID, 1, 1, utils.RandomAddress().String(), event[:], address) invalidLog := GenLog(chainID, -10, -10, utils.RandomAddress().String(), event[:], address) @@ -1390,7 +1390,7 @@ func TestInsertLogsInTx(t *testing.T) { // We need full db here, because we want to test transaction rollbacks. _, db := heavyweight.FullTestDBV2(t, nil) - o := logpoller.NewORM(chainID, db, logger.TestLogger(t), pgtest.NewQConfig(true)) + o := logpoller.NewORM(chainID, db, logger.Test(t), pgtest.NewQConfig(true)) logs := make([]logpoller.Log, maxLogsSize, maxLogsSize+1) for i := 0; i < maxLogsSize; i++ { diff --git a/core/chains/evm/monitor/balance.go b/core/chains/evm/monitor/balance.go index b12346ac008..c3b9a49c7af 100644 --- a/core/chains/evm/monitor/balance.go +++ b/core/chains/evm/monitor/balance.go @@ -13,13 +13,13 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -51,11 +51,11 @@ type ( var _ BalanceMonitor = (*balanceMonitor)(nil) // NewBalanceMonitor returns a new balanceMonitor -func NewBalanceMonitor(ethClient evmclient.Client, ethKeyStore keystore.Eth, logger logger.Logger) *balanceMonitor { +func NewBalanceMonitor(ethClient evmclient.Client, ethKeyStore keystore.Eth, lggr logger.Logger) *balanceMonitor { chainId := ethClient.ConfiguredChainID() bm := &balanceMonitor{ services.StateMachine{}, - logger.Named("BalanceMonitor"), + logger.Named(lggr, "BalanceMonitor"), ethClient, chainId, chainId.String(), @@ -119,7 +119,8 @@ func (bm *balanceMonitor) updateBalance(ethBal assets.Eth, address gethCommon.Ad bm.ethBalances[address] = ðBal bm.ethBalancesMtx.Unlock() - lgr := bm.logger.Named("BalanceLog").With( + lgr := logger.Named(bm.logger, "BalanceLog") + lgr = logger.With(lgr, "address", address.Hex(), "ethBalance", ethBal.String(), "weiBalance", ethBal.ToInt()) diff --git a/core/chains/evm/monitor/balance_test.go b/core/chains/evm/monitor/balance_test.go index 6a62549e493..c2c976e78da 100644 --- a/core/chains/evm/monitor/balance_test.go +++ b/core/chains/evm/monitor/balance_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/monitor" @@ -20,7 +21,6 @@ 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/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) var nilBigInt *big.Int @@ -43,7 +43,7 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k1Addr := cltest.MustInsertRandomKey(t, ethKeyStore) _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) - bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.TestLogger(t)) + bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) defer func() { assert.NoError(t, bm.Close()) }() k0bal := big.NewInt(42) @@ -71,7 +71,7 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) - bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.TestLogger(t)) + bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) defer func() { assert.NoError(t, bm.Close()) }() k0bal := big.NewInt(42) @@ -91,7 +91,7 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) - bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.TestLogger(t)) + bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) defer func() { assert.NoError(t, bm.Close()) }() ctxCancelledAwaiter := cltest.NewAwaiter() @@ -121,7 +121,7 @@ func TestBalanceMonitor_Start(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) - bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.TestLogger(t)) + bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) defer func() { assert.NoError(t, bm.Close()) }() ethClient.On("BalanceAt", mock.Anything, k0Addr, nilBigInt). @@ -149,7 +149,7 @@ func TestBalanceMonitor_OnNewLongestChain_UpdatesBalance(t *testing.T) { _, k0Addr := cltest.MustInsertRandomKey(t, ethKeyStore) _, k1Addr := cltest.MustInsertRandomKey(t, ethKeyStore) - bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.TestLogger(t)) + bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) k0bal := big.NewInt(42) // Deliberately larger than a 64 bit unsigned integer to test overflow k1bal := big.NewInt(0) @@ -201,7 +201,7 @@ func TestBalanceMonitor_FewerRPCCallsWhenBehind(t *testing.T) { ethClient := newEthClientMock(t) - bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.TestLogger(t)) + bm := monitor.NewBalanceMonitor(ethClient, ethKeyStore, logger.Test(t)) ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything). Once(). Return(big.NewInt(1), nil) diff --git a/core/chains/evm/txmgr/attempts.go b/core/chains/evm/txmgr/attempts.go index 06c1126b869..37626f4550e 100644 --- a/core/chains/evm/txmgr/attempts.go +++ b/core/chains/evm/txmgr/attempts.go @@ -9,13 +9,13 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" commontypes "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) type TxAttemptSigner[ADDR commontypes.Hashable] interface { diff --git a/core/chains/evm/txmgr/attempts_test.go b/core/chains/evm/txmgr/attempts_test.go index 11304384ab8..131115e6fae 100644 --- a/core/chains/evm/txmgr/attempts_test.go +++ b/core/chains/evm/txmgr/attempts_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/mocks" @@ -21,7 +22,6 @@ 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" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" ) @@ -93,7 +93,7 @@ func TestTxm_NewDynamicFeeTx(t *testing.T) { kst := ksmocks.NewEth(t) kst.On("SignTx", addr, mock.Anything, big.NewInt(1)).Return(tx, nil) var n evmtypes.Nonce - lggr := logger.TestLogger(t) + lggr := logger.Test(t) t.Run("creates attempt with fields", func(t *testing.T) { feeCfg := newFeeConfig() @@ -165,7 +165,7 @@ func TestTxm_NewLegacyAttempt(t *testing.T) { gc.priceMin = assets.NewWeiI(10) gc.priceMax = assets.NewWeiI(50) cks := txmgr.NewEvmTxAttemptBuilder(*big.NewInt(1), gc, kst, nil) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) t.Run("creates attempt with fields", func(t *testing.T) { var n evmtypes.Nonce @@ -189,7 +189,7 @@ func TestTxm_NewCustomTxAttempt_NonRetryableErrors(t *testing.T) { t.Parallel() kst := ksmocks.NewEth(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) cks := txmgr.NewEvmTxAttemptBuilder(*big.NewInt(1), newFeeConfig(), kst, nil) dynamicFee := gas.DynamicFee{TipCap: assets.GWei(100), FeeCap: assets.GWei(200)} @@ -222,7 +222,7 @@ func TestTxm_EvmTxAttemptBuilder_RetryableEstimatorError(t *testing.T) { est.On("BumpFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(gas.EvmFee{}, uint32(0), errors.New("fail")) kst := ksmocks.NewEth(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ctx := testutils.Context(t) cks := txmgr.NewEvmTxAttemptBuilder(*big.NewInt(1), &feeConfig{eip1559DynamicFees: true}, kst, est) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 43f2fdb8cac..93b1093e795 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -22,7 +22,9 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -40,7 +42,6 @@ import ( "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/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" @@ -60,7 +61,7 @@ func NewTestEthBroadcaster( t.Helper() ctx := testutils.Context(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ge := config.EVM().GasEstimator() estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(config.EVM().GasEstimator(), ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, keyStore, estimator) @@ -94,7 +95,7 @@ func TestEthBroadcaster_Lifecycle(t *testing.T) { ethKeyStore, txBuilder, nil, - logger.TestLogger(t), + logger.Test(t), &testCheckerFactory{}, false, ) @@ -152,7 +153,7 @@ func TestEthBroadcaster_LoadNextSequenceMapFailure_StartupSuccess(t *testing.T) ethKeyStore, txBuilder, nil, - logger.TestLogger(t), + logger.Test(t), &testCheckerFactory{}, false, ) @@ -625,7 +626,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi ethKeyStore, txBuilder, nil, - logger.TestLogger(t), + logger.Test(t), &testCheckerFactory{}, false, ) @@ -1132,7 +1133,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { // same as the parent test, but callback is set by ctor t.Run("callback set by ctor", func(t *testing.T) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg.EVM().GasEstimator(), evmcfg.EVM().GasEstimator().BlockHistory(), lggr), evmcfg.EVM().GasEstimator().EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), evmcfg.EVM().GasEstimator(), ethKeyStore, estimator) localNextNonce = getLocalNextNonce(t, eb, fromAddress) @@ -1744,7 +1745,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { db := pgtest.NewSqlxDB(t) ctx := testutils.Context(t) - lggr, observed := logger.TestLoggerObserved(t, zapcore.DebugLevel) + lggr, observed := logger.TestObserved(t, zapcore.DebugLevel) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.EVM[0].NonceAutoSync = ptr(true) }) diff --git a/core/chains/evm/txmgr/builder.go b/core/chains/evm/txmgr/builder.go index 5e3d61301ca..84aa21e4de2 100644 --- a/core/chains/evm/txmgr/builder.go +++ b/core/chains/evm/txmgr/builder.go @@ -6,6 +6,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -14,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" ) diff --git a/core/chains/evm/txmgr/client.go b/core/chains/evm/txmgr/client.go index 8789f5f173e..d08274f74b6 100644 --- a/core/chains/evm/txmgr/client.go +++ b/core/chains/evm/txmgr/client.go @@ -13,11 +13,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -62,7 +62,7 @@ func (c *evmTxmClient) BatchSendTransactions( if len(reqs) != len(attempts) { lenErr := fmt.Errorf("Returned request data length (%d) != number of tx attempts (%d)", len(reqs), len(attempts)) err = errors.Join(err, lenErr) - lggr.Criticalw("Mismatched length", "err", err) + logger.Criticalw(lggr, "Mismatched length", "err", err) return } @@ -91,7 +91,7 @@ func (c *evmTxmClient) BatchSendTransactions( func (c *evmTxmClient) SendTransactionReturnCode(ctx context.Context, etx Tx, attempt TxAttempt, lggr logger.Logger) (commonclient.SendTxReturnCode, error) { signedTx, err := GetGethSignedTx(attempt.SignedRawTx) if err != nil { - lggr.Criticalw("Fatal error signing transaction", "err", err, "etx", etx) + logger.Criticalw(lggr, "Fatal error signing transaction", "err", err, "etx", etx) return commonclient.Fatal, err } return c.client.SendTransactionReturnCode(ctx, signedTx, etx.FromAddress) diff --git a/core/chains/evm/txmgr/common.go b/core/chains/evm/txmgr/common.go index 37cc89dd7ac..1956476f8dd 100644 --- a/core/chains/evm/txmgr/common.go +++ b/core/chains/evm/txmgr/common.go @@ -10,8 +10,8 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // Tries to send transactions in batches. Even if some batch(es) fail to get sent, it tries all remaining batches, diff --git a/core/chains/evm/txmgr/confirmer_test.go b/core/chains/evm/txmgr/confirmer_test.go index f5889b06649..84c42cd00f1 100644 --- a/core/chains/evm/txmgr/confirmer_test.go +++ b/core/chains/evm/txmgr/confirmer_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" @@ -36,7 +37,6 @@ import ( "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/services/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" @@ -121,7 +121,7 @@ func TestEthConfirmer_Lifecycle(t *testing.T) { cltest.MustInsertRandomKey(t, ethKeyStore) cltest.MustInsertRandomKey(t, ethKeyStore) estimator := gasmocks.NewEvmEstimator(t) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ge := config.EVM().GasEstimator() feeEstimator := gas.NewWrappedEvmEstimator(estimator, ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ethKeyStore, feeEstimator) @@ -1353,7 +1353,7 @@ func TestEthConfirmer_FindTxsRequiringRebroadcast(t *testing.T) { _, otherAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) evmOtherAddress := otherAddress - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ec := newEthConfirmer(t, txStore, ethClient, evmcfg, ethKeyStore, nil) @@ -1619,7 +1619,7 @@ func TestEthConfirmer_FindTxsRequiringRebroadcast(t *testing.T) { func TestEthConfirmer_RebroadcastWhereNecessary_WithConnectivityCheck(t *testing.T) { t.Parallel() - lggr := logger.TestLogger(t) + lggr := logger.Test(t) db := pgtest.NewSqlxDB(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) @@ -3074,7 +3074,7 @@ func TestEthConfirmer_ResumePendingRuns(t *testing.T) { func ptr[T any](t T) *T { return &t } func newEthConfirmer(t testing.TB, txStore txmgr.EvmTxStore, ethClient client.Client, config evmconfig.ChainScopedConfig, ks keystore.Eth, fn txmgrcommon.ResumeCallback) *txmgr.Confirmer { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ge := config.EVM().GasEstimator() estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(ge, ge.BlockHistory(), lggr), ge.EIP1559DynamicFees(), nil) txBuilder := txmgr.NewEvmTxAttemptBuilder(*ethClient.ConfiguredChainID(), ge, ks, estimator) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 84fe9a02c6a..2788c2fd1c9 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -18,6 +18,7 @@ import ( pkgerrors "github.com/pkg/errors" nullv4 "gopkg.in/guregu/null.v4" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -25,7 +26,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -333,7 +333,7 @@ func NewTxStore( lggr logger.Logger, cfg pg.QConfig, ) *evmTxStore { - namedLogger := lggr.Named("TxmStore") + namedLogger := logger.Named(lggr, "TxmStore") ctx, cancel := context.WithCancel(context.Background()) q := pg.NewQ(db, namedLogger, cfg, pg.WithParentCtx(ctx)) return &evmTxStore{ @@ -1371,7 +1371,7 @@ GROUP BY e.id txHashesHex[i] = common.BytesToAddress(r.TxHashes[i]) } - o.logger.Criticalw(fmt.Sprintf("eth_tx with ID %v expired without ever getting a receipt for any of our attempts. "+ + logger.Criticalw(o.logger, fmt.Sprintf("eth_tx with ID %v expired without ever getting a receipt for any of our attempts. "+ "Current block height is %v, transaction was broadcast before block height %v. This transaction may not have not been sent and will be marked as fatally errored. "+ "This can happen if there is another instance of chainlink running that is using the same private key, or if "+ "an external wallet has been used to send a transaction from account %s with nonce %v."+ diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index 15417a43096..d2cafcb8efa 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink-common/pkg/logger" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" @@ -18,7 +19,6 @@ import ( "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/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -1117,7 +1117,7 @@ func TestORM_LoadEthTxesAttempts(t *testing.T) { etx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 3, 9, time.Now(), fromAddress) etx.TxAttempts = []txmgr.TxAttempt{} - q := pg.NewQ(db, logger.TestLogger(t), cfg.Database()) + q := pg.NewQ(db, logger.Test(t), cfg.Database()) newAttempt := cltest.NewDynamicFeeEthTxAttempt(t, etx.ID) var dbAttempt txmgr.DbEthTxAttempt @@ -1258,7 +1258,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { txStore := cltest.NewTestTxStore(t, db, cfg.Database()) ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) - q := pg.NewQ(db, logger.TestLogger(t), cfg.Database()) + q := pg.NewQ(db, logger.Test(t), cfg.Database()) nonce := evmtypes.Nonce(123) t.Run("update successful", func(t *testing.T) { @@ -1293,7 +1293,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { txStore = cltest.NewTestTxStore(t, db, cfg.Database()) ethKeyStore = cltest.NewKeyStore(t, db, cfg.Database()).Eth() _, fromAddress = cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) - q = pg.NewQ(db, logger.TestLogger(t), cfg.Database()) + q = pg.NewQ(db, logger.Test(t), cfg.Database()) t.Run("update replaces abandoned tx with same hash", func(t *testing.T) { etx := mustInsertInProgressEthTxWithAttempt(t, txStore, nonce, fromAddress) @@ -1309,7 +1309,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { ccfg := evmtest.NewChainScopedConfig(t, evmCfg) evmTxmCfg := txmgr.NewEvmTxmConfig(ccfg.EVM()) ec := evmtest.NewEthClientMockWithDefaultChain(t) - txMgr := txmgr.NewEvmTxm(ec.ConfiguredChainID(), evmTxmCfg, ccfg.EVM().Transactions(), nil, logger.TestLogger(t), nil, nil, + txMgr := txmgr.NewEvmTxm(ec.ConfiguredChainID(), evmTxmCfg, ccfg.EVM().Transactions(), nil, logger.Test(t), nil, nil, nil, txStore, nil, nil, nil, nil) err := txMgr.XXXTestAbandon(fromAddress) // mark transaction as abandoned require.NoError(t, err) diff --git a/core/chains/evm/txmgr/nonce_syncer.go b/core/chains/evm/txmgr/nonce_syncer.go index dc0d27e6414..2936736b3b3 100644 --- a/core/chains/evm/txmgr/nonce_syncer.go +++ b/core/chains/evm/txmgr/nonce_syncer.go @@ -8,10 +8,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/txmgr" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/logger" ) // NonceSyncer manages the delicate task of syncing the local nonce with the @@ -61,7 +61,7 @@ func NewNonceSyncer( lggr logger.Logger, ethClient evmclient.Client, ) NonceSyncer { - lggr = lggr.Named("NonceSyncer") + lggr = logger.Named(lggr, "NonceSyncer") return &nonceSyncerImpl{ txStore: txStore, client: NewEvmTxmClient(ethClient), diff --git a/core/chains/evm/txmgr/nonce_syncer_test.go b/core/chains/evm/txmgr/nonce_syncer_test.go index f6480b4c305..f757b8863d1 100644 --- a/core/chains/evm/txmgr/nonce_syncer_test.go +++ b/core/chains/evm/txmgr/nonce_syncer_test.go @@ -3,6 +3,7 @@ package txmgr_test import ( "testing" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -10,7 +11,6 @@ import ( "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/pkg/errors" "github.com/stretchr/testify/assert" @@ -30,7 +30,7 @@ func Test_NonceSyncer_Sync(t *testing.T) { _, from := cltest.MustInsertRandomKey(t, ethKeyStore) - ns := txmgr.NewNonceSyncer(txStore, logger.TestLogger(t), ethClient) + ns := txmgr.NewNonceSyncer(txStore, logger.Test(t), ethClient) ethClient.On("PendingNonceAt", mock.Anything, from).Return(uint64(0), errors.New("something exploded")) _, err := ns.Sync(testutils.Context(t), from, types.Nonce(0)) @@ -50,7 +50,7 @@ func Test_NonceSyncer_Sync(t *testing.T) { _, from := cltest.MustInsertRandomKey(t, ethKeyStore) - ns := txmgr.NewNonceSyncer(txStore, logger.TestLogger(t), ethClient) + ns := txmgr.NewNonceSyncer(txStore, logger.Test(t), ethClient) ethClient.On("PendingNonceAt", mock.Anything, from).Return(uint64(0), nil) @@ -72,7 +72,7 @@ func Test_NonceSyncer_Sync(t *testing.T) { _, fromAddress := cltest.RandomKey{Nonce: 32}.MustInsert(t, ks) - ns := txmgr.NewNonceSyncer(txStore, logger.TestLogger(t), ethClient) + ns := txmgr.NewNonceSyncer(txStore, logger.Test(t), ethClient) // Used to mock the chain nonce ethClient.On("PendingNonceAt", mock.Anything, fromAddress).Return(uint64(5), nil) @@ -97,7 +97,7 @@ func Test_NonceSyncer_Sync(t *testing.T) { key1LocalNonce := types.Nonce(0) key2LocalNonce := types.Nonce(32) - ns := txmgr.NewNonceSyncer(txStore, logger.TestLogger(t), ethClient) + ns := txmgr.NewNonceSyncer(txStore, logger.Test(t), ethClient) // Used to mock the chain nonce ethClient.On("PendingNonceAt", mock.Anything, key1).Return(uint64(5), nil).Once() diff --git a/core/chains/evm/txmgr/reaper_test.go b/core/chains/evm/txmgr/reaper_test.go index 67216c9fd15..2da8e7a93c8 100644 --- a/core/chains/evm/txmgr/reaper_test.go +++ b/core/chains/evm/txmgr/reaper_test.go @@ -8,18 +8,18 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/logger" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" txmgrmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) func newReaperWithChainID(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], cfg txmgrtypes.ReaperChainConfig, txConfig txmgrtypes.ReaperTransactionsConfig, cid *big.Int) *txmgr.Reaper { - return txmgr.NewEvmReaper(logger.TestLogger(t), db, cfg, txConfig, cid) + return txmgr.NewEvmReaper(logger.Test(t), db, cfg, txConfig, cid) } func newReaper(t *testing.T, db txmgrtypes.TxHistoryReaper[*big.Int], cfg txmgrtypes.ReaperChainConfig, txConfig txmgrtypes.ReaperTransactionsConfig) *txmgr.Reaper { diff --git a/core/chains/evm/txmgr/resender_test.go b/core/chains/evm/txmgr/resender_test.go index cc94511e3b0..d2eefdece59 100644 --- a/core/chains/evm/txmgr/resender_test.go +++ b/core/chains/evm/txmgr/resender_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -20,7 +21,6 @@ import ( "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/utils" @@ -31,7 +31,7 @@ func Test_EthResender_resendUnconfirmed(t *testing.T) { db := pgtest.NewSqlxDB(t) logCfg := pgtest.NewQConfig(true) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) ethKeyStore := cltest.NewKeyStore(t, db, logCfg).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) {}) @@ -102,7 +102,7 @@ func Test_EthResender_alertUnconfirmed(t *testing.T) { db := pgtest.NewSqlxDB(t) logCfg := pgtest.NewQConfig(true) - lggr, o := logger.TestLoggerObserved(t, zapcore.DebugLevel) + lggr, o := logger.TestObserved(t, zapcore.DebugLevel) ethKeyStore := cltest.NewKeyStore(t, db, logCfg).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) // Set this to the smallest non-zero value possible for the attempt to be eligible for resend @@ -152,7 +152,7 @@ func Test_EthResender_Start(t *testing.T) { ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() ccfg := evmtest.NewChainScopedConfig(t, cfg) _, fromAddress := cltest.MustInsertRandomKey(t, ethKeyStore) - lggr := logger.TestLogger(t) + lggr := logger.Test(t) t.Run("resends transactions that have been languishing unconfirmed for too long", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go index eb6edd3f587..f210934adb1 100644 --- a/core/chains/evm/txmgr/transmitchecker.go +++ b/core/chains/evm/txmgr/transmitchecker.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/pkg/errors" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -20,7 +21,6 @@ import ( v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" v2 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" - "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) @@ -147,7 +147,7 @@ func (s *SimulateChecker) Check( err := s.Client.CallContext(ctx, &b, "eth_call", callArg, evmclient.ToBlockNumArg(nil)) if err != nil { if jErr := evmclient.ExtractRPCErrorOrNil(err); jErr != nil { - l.Criticalw("Transaction reverted during simulation", + logger.Criticalw(l, "Transaction reverted during simulation", "ethTxAttemptID", a.ID, "txHash", a.Hash, "err", err, "rpcErr", jErr.String(), "returnValue", b.String()) return errors.Errorf("transaction reverted during simulation: %s", jErr.String()) } diff --git a/core/chains/evm/txmgr/transmitchecker_test.go b/core/chains/evm/txmgr/transmitchecker_test.go index 5ebf5f32e38..6dd4edd91c6 100644 --- a/core/chains/evm/txmgr/transmitchecker_test.go +++ b/core/chains/evm/txmgr/transmitchecker_test.go @@ -15,6 +15,8 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/mock" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -29,7 +31,6 @@ 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/logger" ) func TestFactory(t *testing.T) { @@ -105,7 +106,7 @@ func TestFactory(t *testing.T) { func TestTransmitCheckers(t *testing.T) { client := evmtest.NewEthClientMockWithDefaultChain(t) - log := logger.TestLogger(t) + log := logger.Test(t) ctx := testutils.Context(t) t.Run("no checker", func(t *testing.T) { diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index bab18f445bf..f82bc991d52 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -20,6 +20,7 @@ import ( "github.com/jmoiron/sqlx" + "github.com/smartcontractkit/chainlink-common/pkg/logger" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" commontxmmocks "github.com/smartcontractkit/chainlink/v2/common/txmgr/types/mocks" @@ -36,7 +37,6 @@ import ( "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/keystore" ksmocks "github.com/smartcontractkit/chainlink/v2/core/services/keystore/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -45,7 +45,7 @@ import ( func makeTestEvmTxm( t *testing.T, db *sqlx.DB, ethClient evmclient.Client, estimator gas.EvmFeeEstimator, ccfg txmgr.ChainConfig, fcfg txmgr.FeeConfig, txConfig evmconfig.Transactions, dbConfig txmgr.DatabaseConfig, listenerConfig txmgr.ListenerConfig, keyStore keystore.Eth) (txmgr.TxManager, error) { - lggr := logger.TestLogger(t) + lggr := logger.Test(t) lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) // logic for building components (from evm/evm_txm.go) ------- @@ -83,7 +83,7 @@ func TestTxm_SendNativeToken_DoesNotSendToZero(t *testing.T) { keyStore := cltest.NewKeyStore(t, db, dbConfig).Eth() ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), keyStore) require.NoError(t, err) @@ -109,7 +109,7 @@ func TestTxm_CreateTransaction(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst.Eth()) require.NoError(t, err) @@ -298,7 +298,7 @@ func TestTxm_CreateTransaction(t *testing.T) { evmConfig.MaxQueued = uint64(1) // Create mock forwarder, mock authorizedsenders call. - form := forwarders.NewORM(db, logger.TestLogger(t), cfg.Database()) + form := forwarders.NewORM(db, logger.Test(t), cfg.Database()) fwdrAddr := testutils.NewAddress() fwdr, err := form.CreateForwarder(fwdrAddr, utils.Big(cltest.FixtureChainID)) require.NoError(t, err) @@ -391,7 +391,7 @@ func TestTxm_CreateTransaction_OutOfEth(t *testing.T) { config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), etKeyStore) require.NoError(t, err) @@ -482,7 +482,7 @@ func TestTxm_Lifecycle(t *testing.T) { keyChangeCh := make(chan struct{}) unsub := cltest.NewAwaiter() kst.On("SubscribeToKeyChanges").Return(keyChangeCh, unsub.ItHappened) - estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + estimator := gas.NewEstimator(logger.Test(t), ethClient, config, evmConfig.GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, evmConfig, evmConfig.GasEstimator(), evmConfig.Transactions(), dbConfig, dbConfig.Listener(), kst) require.NoError(t, err) @@ -535,7 +535,7 @@ func TestTxm_Reset(t *testing.T) { ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, nil) ethClient.On("BatchCallContextAll", mock.Anything, mock.Anything).Return(nil).Maybe() - estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) + estimator := gas.NewEstimator(logger.Test(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), cfg.Database(), cfg.Database().Listener(), kst.Eth()) require.NoError(t, err) @@ -576,7 +576,7 @@ func TestTxm_Reset(t *testing.T) { } func newTxStore(t *testing.T, db *sqlx.DB, cfg pg.QConfig) txmgr.EvmTxStore { - return txmgr.NewTxStore(db, logger.TestLogger(t), cfg) + return txmgr.NewTxStore(db, logger.Test(t), cfg) } func newEthReceipt(blockNumber int64, blockHash common.Hash, txHash common.Hash, status uint64) txmgr.Receipt { diff --git a/core/chains/evm/chain.go b/core/chains/legacyevm/chain.go similarity index 99% rename from core/chains/evm/chain.go rename to core/chains/legacyevm/chain.go index 1e52bed5cb6..0c35d929fc2 100644 --- a/core/chains/evm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -1,4 +1,4 @@ -package evm +package legacyevm import ( "context" diff --git a/core/chains/evm/chain_test.go b/core/chains/legacyevm/chain_test.go similarity index 80% rename from core/chains/evm/chain_test.go rename to core/chains/legacyevm/chain_test.go index f25af87a35b..4fcd51c39d9 100644 --- a/core/chains/evm/chain_test.go +++ b/core/chains/legacyevm/chain_test.go @@ -1,4 +1,4 @@ -package evm_test +package legacyevm_test import ( "math/big" @@ -8,8 +8,8 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -17,13 +17,13 @@ import ( ) func TestLegacyChains(t *testing.T) { - evmCfg := configtest.NewGeneralConfig(t, nil) + legacyevmCfg := configtest.NewGeneralConfig(t, nil) c := mocks.NewChain(t) c.On("ID").Return(big.NewInt(7)) - m := map[string]evm.Chain{c.ID().String(): c} + m := map[string]legacyevm.Chain{c.ID().String(): c} - l := evm.NewLegacyChains(m, evmCfg.EVMConfigs()) + l := legacyevm.NewLegacyChains(m, legacyevmCfg.EVMConfigs()) assert.NotNil(t, l.ChainNodeConfigs()) got, err := l.Get(c.ID().String()) assert.NoError(t, err) @@ -33,7 +33,7 @@ func TestLegacyChains(t *testing.T) { func TestChainOpts_Validate(t *testing.T) { type fields struct { - AppConfig evm.AppConfig + AppConfig legacyevm.AppConfig EventBroadcaster pg.EventBroadcaster MailMon *utils.MailboxMonitor DB *sqlx.DB @@ -65,7 +65,7 @@ func TestChainOpts_Validate(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - o := evm.ChainOpts{ + o := legacyevm.ChainOpts{ AppConfig: tt.fields.AppConfig, EventBroadcaster: tt.fields.EventBroadcaster, MailMon: tt.fields.MailMon, diff --git a/core/chains/evm/evm_txm.go b/core/chains/legacyevm/evm_txm.go similarity index 99% rename from core/chains/evm/evm_txm.go rename to core/chains/legacyevm/evm_txm.go index bfc0f6378bf..1606ea1b244 100644 --- a/core/chains/evm/evm_txm.go +++ b/core/chains/legacyevm/evm_txm.go @@ -1,4 +1,4 @@ -package evm +package legacyevm import ( "fmt" diff --git a/core/chains/evm/mocks/chain.go b/core/chains/legacyevm/mocks/chain.go similarity index 100% rename from core/chains/evm/mocks/chain.go rename to core/chains/legacyevm/mocks/chain.go diff --git a/core/chains/evm/mocks/legacy_chain_container.go b/core/chains/legacyevm/mocks/legacy_chain_container.go similarity index 73% rename from core/chains/evm/mocks/legacy_chain_container.go rename to core/chains/legacyevm/mocks/legacy_chain_container.go index 9180a8a2fc5..9ebacb890aa 100644 --- a/core/chains/evm/mocks/legacy_chain_container.go +++ b/core/chains/legacyevm/mocks/legacy_chain_container.go @@ -3,7 +3,7 @@ package mocks import ( - evm "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + legacyevm "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" mock "github.com/stretchr/testify/mock" types "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" @@ -31,19 +31,19 @@ func (_m *LegacyChainContainer) ChainNodeConfigs() types.Configs { } // Get provides a mock function with given fields: id -func (_m *LegacyChainContainer) Get(id string) (evm.Chain, error) { +func (_m *LegacyChainContainer) Get(id string) (legacyevm.Chain, error) { ret := _m.Called(id) - var r0 evm.Chain + var r0 legacyevm.Chain var r1 error - if rf, ok := ret.Get(0).(func(string) (evm.Chain, error)); ok { + if rf, ok := ret.Get(0).(func(string) (legacyevm.Chain, error)); ok { return rf(id) } - if rf, ok := ret.Get(0).(func(string) evm.Chain); ok { + if rf, ok := ret.Get(0).(func(string) legacyevm.Chain); ok { r0 = rf(id) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(evm.Chain) + r0 = ret.Get(0).(legacyevm.Chain) } } @@ -71,7 +71,7 @@ func (_m *LegacyChainContainer) Len() int { } // List provides a mock function with given fields: ids -func (_m *LegacyChainContainer) List(ids ...string) ([]evm.Chain, error) { +func (_m *LegacyChainContainer) List(ids ...string) ([]legacyevm.Chain, error) { _va := make([]interface{}, len(ids)) for _i := range ids { _va[_i] = ids[_i] @@ -80,16 +80,16 @@ func (_m *LegacyChainContainer) List(ids ...string) ([]evm.Chain, error) { _ca = append(_ca, _va...) ret := _m.Called(_ca...) - var r0 []evm.Chain + var r0 []legacyevm.Chain var r1 error - if rf, ok := ret.Get(0).(func(...string) ([]evm.Chain, error)); ok { + if rf, ok := ret.Get(0).(func(...string) ([]legacyevm.Chain, error)); ok { return rf(ids...) } - if rf, ok := ret.Get(0).(func(...string) []evm.Chain); ok { + if rf, ok := ret.Get(0).(func(...string) []legacyevm.Chain); ok { r0 = rf(ids...) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]evm.Chain) + r0 = ret.Get(0).([]legacyevm.Chain) } } @@ -103,15 +103,15 @@ func (_m *LegacyChainContainer) List(ids ...string) ([]evm.Chain, error) { } // Slice provides a mock function with given fields: -func (_m *LegacyChainContainer) Slice() []evm.Chain { +func (_m *LegacyChainContainer) Slice() []legacyevm.Chain { ret := _m.Called() - var r0 []evm.Chain - if rf, ok := ret.Get(0).(func() []evm.Chain); ok { + var r0 []legacyevm.Chain + if rf, ok := ret.Get(0).(func() []legacyevm.Chain); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]evm.Chain) + r0 = ret.Get(0).([]legacyevm.Chain) } } diff --git a/core/cmd/shell.go b/core/cmd/shell.go index b82bd85e3ed..35659aa7797 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -34,7 +34,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink/v2/core/build" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" @@ -166,7 +166,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G evmFactoryCfg := chainlink.EVMFactoryConfig{ CSAETHKeystore: keyStore, - ChainOpts: evm.ChainOpts{AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon, DB: db}, + ChainOpts: legacyevm.ChainOpts{AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon, DB: db}, } // evm always enabled for backward compatibility // TODO BCF-2510 this needs to change in order to clear the path for EVM extraction diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 2dc182944d3..1e030fa9e9e 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/smartcontractkit/chainlink/v2/common/client" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" cmdMocks "github.com/smartcontractkit/chainlink/v2/core/cmd/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" @@ -39,7 +39,7 @@ import ( "github.com/urfave/cli" ) -func genTestEVMRelayers(t *testing.T, opts evm.ChainRelayExtenderConfig, ks evmrelayer.CSAETHKeystore) *chainlink.CoreRelayerChainInteroperators { +func genTestEVMRelayers(t *testing.T, opts legacyevm.ChainRelayExtenderConfig, ks evmrelayer.CSAETHKeystore) *chainlink.CoreRelayerChainInteroperators { f := chainlink.RelayerFactory{ Logger: opts.Logger, LoopRegistry: plugins.NewLoopRegistry(opts.Logger, opts.AppConfig.Tracing()), @@ -83,10 +83,10 @@ func TestShell_RunNodeWithPasswords(t *testing.T) { lggr := logger.TestLogger(t) - opts := evm.ChainRelayExtenderConfig{ + opts := legacyevm.ChainRelayExtenderConfig{ Logger: lggr, KeyStore: keyStore.Eth(), - ChainOpts: evm.ChainOpts{ + ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, @@ -188,10 +188,10 @@ func TestShell_RunNodeWithAPICredentialsFile(t *testing.T) { ethClient.On("BalanceAt", mock.Anything, mock.Anything, mock.Anything).Return(big.NewInt(10), nil).Maybe() lggr := logger.TestLogger(t) - opts := evm.ChainRelayExtenderConfig{ + opts := legacyevm.ChainRelayExtenderConfig{ Logger: lggr, KeyStore: keyStore.Eth(), - ChainOpts: evm.ChainOpts{ + ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index b1431bf0599..17a1d4fadd4 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -44,7 +44,6 @@ import ( commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/auth" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" @@ -52,6 +51,7 @@ import ( httypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -173,7 +173,7 @@ type JobPipelineConfig interface { MaxSuccessfulRuns() uint64 } -func NewJobPipelineV2(t testing.TB, cfg pipeline.BridgeConfig, jpcfg JobPipelineConfig, dbCfg pg.QConfig, legacyChains evm.LegacyChainContainer, db *sqlx.DB, keyStore keystore.Master, restrictedHTTPClient, unrestrictedHTTPClient *http.Client) JobPipelineV2TestHelper { +func NewJobPipelineV2(t testing.TB, cfg pipeline.BridgeConfig, jpcfg JobPipelineConfig, dbCfg pg.QConfig, legacyChains legacyevm.LegacyChainContainer, db *sqlx.DB, keyStore keystore.Master, restrictedHTTPClient, unrestrictedHTTPClient *http.Client) JobPipelineV2TestHelper { lggr := logger.TestLogger(t) prm := pipeline.NewORM(db, lggr, dbCfg, jpcfg.MaxSuccessfulRuns()) btORM := bridges.NewORM(db, lggr, dbCfg) @@ -349,7 +349,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn } evmOpts := chainlink.EVMFactoryConfig{ - ChainOpts: evm.ChainOpts{ + ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: eventBroadcaster, MailMon: mailMon, diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index 540924d7f02..d49e94557e3 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -13,10 +13,10 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" - evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "github.com/smartcontractkit/chainlink/v2/core/cmd" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -397,7 +397,7 @@ func (m MockPasswordPrompter) Prompt() string { return m.Password } -func NewLegacyChainsWithMockChain(t testing.TB, ethClient evmclient.Client, cfg evm.AppConfig) evm.LegacyChainContainer { +func NewLegacyChainsWithMockChain(t testing.TB, ethClient evmclient.Client, cfg legacyevm.AppConfig) legacyevm.LegacyChainContainer { ch := new(evmmocks.Chain) ch.On("Client").Return(ethClient) ch.On("Logger").Return(logger.TestLogger(t)) @@ -409,7 +409,7 @@ func NewLegacyChainsWithMockChain(t testing.TB, ethClient evmclient.Client, cfg } -func NewLegacyChainsWithChain(ch evm.Chain, cfg evm.AppConfig) evm.LegacyChainContainer { - m := map[string]evm.Chain{ch.ID().String(): ch} - return evm.NewLegacyChains(m, cfg.EVMConfigs()) +func NewLegacyChainsWithChain(ch legacyevm.Chain, cfg legacyevm.AppConfig) legacyevm.LegacyChainContainer { + m := map[string]legacyevm.Chain{ch.ID().String(): ch} + return legacyevm.NewLegacyChains(m, cfg.EVMConfigs()) } diff --git a/core/internal/testutils/evmtest/evmtest.go b/core/internal/testutils/evmtest/evmtest.go index 7674650c010..423615145e5 100644 --- a/core/internal/testutils/evmtest/evmtest.go +++ b/core/internal/testutils/evmtest/evmtest.go @@ -19,7 +19,6 @@ import ( commonmocks "github.com/smartcontractkit/chainlink/v2/common/types/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" @@ -30,6 +29,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -40,7 +40,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) -func NewChainScopedConfig(t testing.TB, cfg evm.AppConfig) evmconfig.ChainScopedConfig { +func NewChainScopedConfig(t testing.TB, cfg legacyevm.AppConfig) evmconfig.ChainScopedConfig { var evmCfg *evmtoml.EVMConfig if len(cfg.EVMConfigs()) > 0 { evmCfg = cfg.EVMConfigs()[0] @@ -60,7 +60,7 @@ type TestChainOpts struct { Client evmclient.Client LogBroadcaster log.Broadcaster LogPoller logpoller.LogPoller - GeneralConfig evm.AppConfig + GeneralConfig legacyevm.AppConfig HeadTracker httypes.HeadTracker DB *sqlx.DB TxManager txmgr.TxManager @@ -78,12 +78,12 @@ func NewChainRelayExtenders(t testing.TB, testopts TestChainOpts) *evmrelay.Chai return cc } -func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) evm.ChainRelayExtenderConfig { +func NewChainRelayExtOpts(t testing.TB, testopts TestChainOpts) legacyevm.ChainRelayExtenderConfig { require.NotNil(t, testopts.KeyStore) - opts := evm.ChainRelayExtenderConfig{ + opts := legacyevm.ChainRelayExtenderConfig{ Logger: logger.TestLogger(t), KeyStore: testopts.KeyStore, - ChainOpts: evm.ChainOpts{ + ChainOpts: legacyevm.ChainOpts{ AppConfig: testopts.GeneralConfig, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: testopts.MailMon, @@ -138,7 +138,7 @@ func MustGetDefaultChainID(t testing.TB, evmCfgs evmtoml.EVMConfigs) *big.Int { } // Deprecated, this is a replacement function for tests for now removed default chain logic -func MustGetDefaultChain(t testing.TB, cc evm.LegacyChainContainer) evm.Chain { +func MustGetDefaultChain(t testing.TB, cc legacyevm.LegacyChainContainer) legacyevm.Chain { if len(cc.Slice()) == 0 { t.Fatalf("at least one evm chain container must be defined") } diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 43840f93ce8..d1795bbc846 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index d8e682c742d..cd7adf6bee3 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1504,8 +1504,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 h1:HJCPvJ+ZjU9TSX4YD5rxBJqGAvqhDfzoJgI3WmfeWeI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= diff --git a/core/services/blockhashstore/delegate.go b/core/services/blockhashstore/delegate.go index 1a84323b6f0..d6c27acd0b5 100644 --- a/core/services/blockhashstore/delegate.go +++ b/core/services/blockhashstore/delegate.go @@ -9,7 +9,7 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/trusted_blockhash_store" @@ -28,14 +28,14 @@ var _ job.ServiceCtx = &service{} // Delegate creates BlockhashStore feeder jobs. type Delegate struct { logger logger.Logger - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer ks keystore.Eth } // NewDelegate creates a new Delegate. func NewDelegate( logger logger.Logger, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, ks keystore.Eth, ) *Delegate { return &Delegate{ diff --git a/core/services/blockhashstore/delegate_test.go b/core/services/blockhashstore/delegate_test.go index 011ab87ad6b..0096ac5ca9e 100644 --- a/core/services/blockhashstore/delegate_test.go +++ b/core/services/blockhashstore/delegate_test.go @@ -10,10 +10,10 @@ import ( "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" mocklp "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -41,7 +41,7 @@ func TestDelegate_JobType(t *testing.T) { type testData struct { ethClient *mocks.Client ethKeyStore keystore.Eth - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer sendingKey ethkey.KeyV2 logs *observer.ObservedLogs } diff --git a/core/services/blockheaderfeeder/delegate.go b/core/services/blockheaderfeeder/delegate.go index 3de42d7a9e9..53f514cee27 100644 --- a/core/services/blockheaderfeeder/delegate.go +++ b/core/services/blockheaderfeeder/delegate.go @@ -9,7 +9,7 @@ import ( "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" v1 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" @@ -27,13 +27,13 @@ var _ job.ServiceCtx = &service{} type Delegate struct { logger logger.Logger - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer ks keystore.Eth } func NewDelegate( logger logger.Logger, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, ks keystore.Eth, ) *Delegate { return &Delegate{ diff --git a/core/services/chainlink/mocks/relayer_chain_interoperators.go b/core/services/chainlink/mocks/relayer_chain_interoperators.go index f778f61b0cb..74dc9dc1d45 100644 --- a/core/services/chainlink/mocks/relayer_chain_interoperators.go +++ b/core/services/chainlink/mocks/relayer_chain_interoperators.go @@ -7,7 +7,7 @@ import ( services2 "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink-common/pkg/loop" @@ -19,12 +19,12 @@ import ( // FakeRelayerChainInteroperators is a fake chainlink.RelayerChainInteroperators. // This exists because mockery generation doesn't understand how to produce an alias instead of the underlying type (which is not exported in this case). type FakeRelayerChainInteroperators struct { - EVMChains evm.LegacyChainContainer + EVMChains legacyevm.LegacyChainContainer Nodes []types.NodeStatus NodesErr error } -func (f *FakeRelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainContainer { +func (f *FakeRelayerChainInteroperators) LegacyEVMChains() legacyevm.LegacyChainContainer { return f.EVMChains } diff --git a/core/services/chainlink/relayer_chain_interoperators.go b/core/services/chainlink/relayer_chain_interoperators.go index 3be1395694d..673f71e3c65 100644 --- a/core/services/chainlink/relayer_chain_interoperators.go +++ b/core/services/chainlink/relayer_chain_interoperators.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters" "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2" "github.com/smartcontractkit/chainlink/v2/core/services/relay" @@ -46,7 +46,7 @@ type LoopRelayerStorer interface { // This will be deprecated/removed when products depend only // on the relayer interface. type LegacyChainer interface { - LegacyEVMChains() evm.LegacyChainContainer + LegacyEVMChains() legacyevm.LegacyChainContainer LegacyCosmosChains() LegacyCosmosContainer } @@ -106,14 +106,14 @@ func InitEVM(ctx context.Context, factory RelayerFactory, config EVMFactoryConfi return fmt.Errorf("failed to setup EVM relayer: %w", err2) } - legacyMap := make(map[string]evm.Chain) + legacyMap := make(map[string]legacyevm.Chain) for id, a := range adapters { // adapter is a service op.srvs = append(op.srvs, a) op.loopRelayers[id] = a legacyMap[id.ChainID] = a.Chain() } - op.legacyChains.EVMChains = evm.NewLegacyChains(legacyMap, config.AppConfig.EVMConfigs()) + op.legacyChains.EVMChains = legacyevm.NewLegacyChains(legacyMap, config.AppConfig.EVMConfigs()) return nil } } @@ -185,7 +185,7 @@ func (rs *CoreRelayerChainInteroperators) Get(id relay.ID) (loop.Relayer, error) // LegacyEVMChains returns a container with all the evm chains // TODO BCF-2511 -func (rs *CoreRelayerChainInteroperators) LegacyEVMChains() evm.LegacyChainContainer { +func (rs *CoreRelayerChainInteroperators) LegacyEVMChains() legacyevm.LegacyChainContainer { rs.mu.Lock() defer rs.mu.Unlock() return rs.legacyChains.EVMChains @@ -351,7 +351,7 @@ func (rs *CoreRelayerChainInteroperators) Services() (s []services.ServiceCtx) { // legacyChains encapsulates the chain-specific dependencies. Will be // deprecated when chain-specific logic is removed from products. type legacyChains struct { - EVMChains evm.LegacyChainContainer + EVMChains legacyevm.LegacyChainContainer CosmosChains LegacyCosmosContainer } diff --git a/core/services/chainlink/relayer_chain_interoperators_test.go b/core/services/chainlink/relayer_chain_interoperators_test.go index da1246c7bfe..6a5445d9f21 100644 --- a/core/services/chainlink/relayer_chain_interoperators_test.go +++ b/core/services/chainlink/relayer_chain_interoperators_test.go @@ -16,7 +16,7 @@ import ( stkcfg "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" "github.com/smartcontractkit/chainlink-solana/pkg/solana" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" @@ -203,7 +203,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { {name: "2 evm chains with 3 nodes", initFuncs: []chainlink.CoreRelayerChainInitFunc{ chainlink.InitEVM(testctx, factory, chainlink.EVMFactoryConfig{ - ChainOpts: evm.ChainOpts{ + ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, @@ -277,7 +277,7 @@ func TestCoreRelayerChainInteroperators(t *testing.T) { Keystore: keyStore.Solana(), TOMLConfigs: cfg.SolanaConfigs()}), chainlink.InitEVM(testctx, factory, chainlink.EVMFactoryConfig{ - ChainOpts: evm.ChainOpts{ + ChainOpts: legacyevm.ChainOpts{ AppConfig: cfg, EventBroadcaster: pg.NewNullEventBroadcaster(), MailMon: &utils.MailboxMonitor{}, diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index 6376839c700..c15643551e9 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -18,7 +18,7 @@ import ( starkchain "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/chain" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -35,7 +35,7 @@ type RelayerFactory struct { } type EVMFactoryConfig struct { - evm.ChainOpts + legacyevm.ChainOpts evmrelay.CSAETHKeystore } @@ -45,7 +45,7 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m relayers := make(map[relay.ID]evmrelay.LoopRelayAdapter) // override some common opts with the factory values. this seems weird... maybe other signatures should change, or this should take a different type... - ccOpts := evm.ChainRelayExtenderConfig{ + ccOpts := legacyevm.ChainRelayExtenderConfig{ Logger: r.Logger.Named("EVM"), KeyStore: config.CSAETHKeystore.Eth(), ChainOpts: config.ChainOpts, @@ -69,7 +69,7 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m CSAETHKeystore: config.CSAETHKeystore, EventBroadcaster: ccOpts.EventBroadcaster, } - relayer, err2 := evmrelay.NewRelayer(ccOpts.Logger.Named(relayID.ChainID), chain, relayerOpts) + relayer, err2 := evmrelay.NewRelayer(r.Logger.Named("EVM").Named(relayID.ChainID), chain, relayerOpts) if err2 != nil { err = errors.Join(err, err2) continue diff --git a/core/services/directrequest/delegate.go b/core/services/directrequest/delegate.go index 687e1cea675..a21029ea177 100644 --- a/core/services/directrequest/delegate.go +++ b/core/services/directrequest/delegate.go @@ -12,9 +12,9 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/operator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -30,7 +30,7 @@ type ( pipelineRunner pipeline.Runner pipelineORM pipeline.ORM chHeads chan *evmtypes.Head - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer mailMon *utils.MailboxMonitor } @@ -46,7 +46,7 @@ func NewDelegate( logger logger.Logger, pipelineRunner pipeline.Runner, pipelineORM pipeline.ORM, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, mailMon *utils.MailboxMonitor, ) *Delegate { return &Delegate{ diff --git a/core/services/feeds/service.go b/core/services/feeds/service.go index 32a8432f876..ea6e6cae5ab 100644 --- a/core/services/feeds/service.go +++ b/core/services/feeds/service.go @@ -19,7 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" pb "github.com/smartcontractkit/chainlink/v2/core/services/feeds/proto" "github.com/smartcontractkit/chainlink/v2/core/services/fluxmonitorv2" @@ -115,7 +115,7 @@ type service struct { ocrCfg OCRConfig ocr2cfg OCR2Config connMgr ConnectionsManager - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer lggr logger.Logger version string } @@ -132,7 +132,7 @@ func NewService( ocrCfg OCRConfig, ocr2Cfg OCR2Config, dbCfg pg.QConfig, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, version string, ) *service { diff --git a/core/services/feeds/service_test.go b/core/services/feeds/service_test.go index c94a75b3dd5..d811a4461fd 100644 --- a/core/services/feeds/service_test.go +++ b/core/services/feeds/service_test.go @@ -11,8 +11,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -149,7 +149,7 @@ type TestService struct { p2pKeystore *ksmocks.P2P ocr1Keystore *ksmocks.OCR ocr2Keystore *ksmocks.OCR2 - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer } func setupTestService(t *testing.T) *TestService { diff --git a/core/services/fluxmonitorv2/delegate.go b/core/services/fluxmonitorv2/delegate.go index e63f3556726..99e2b688f5d 100644 --- a/core/services/fluxmonitorv2/delegate.go +++ b/core/services/fluxmonitorv2/delegate.go @@ -6,8 +6,8 @@ import ( "github.com/jmoiron/sqlx" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -22,7 +22,7 @@ type Delegate struct { jobORM job.ORM pipelineORM pipeline.ORM pipelineRunner pipeline.Runner - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer lggr logger.Logger } @@ -35,7 +35,7 @@ func NewDelegate( pipelineORM pipeline.ORM, pipelineRunner pipeline.Runner, db *sqlx.DB, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, ) *Delegate { return &Delegate{ diff --git a/core/services/gateway/delegate.go b/core/services/gateway/delegate.go index 909ca21ad73..d4180184aca 100644 --- a/core/services/gateway/delegate.go +++ b/core/services/gateway/delegate.go @@ -7,7 +7,7 @@ import ( "github.com/pelletier/go-toml" "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -16,14 +16,14 @@ import ( ) type Delegate struct { - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer ks keystore.Eth lggr logger.Logger } var _ job.Delegate = (*Delegate)(nil) -func NewDelegate(legacyChains evm.LegacyChainContainer, ks keystore.Eth, lggr logger.Logger) *Delegate { +func NewDelegate(legacyChains legacyevm.LegacyChainContainer, ks keystore.Eth, lggr logger.Logger) *Delegate { return &Delegate{legacyChains: legacyChains, ks: ks, lggr: lggr} } diff --git a/core/services/gateway/handler_factory.go b/core/services/gateway/handler_factory.go index 519de608a0f..368bc64c872 100644 --- a/core/services/gateway/handler_factory.go +++ b/core/services/gateway/handler_factory.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/handlers" @@ -17,13 +17,13 @@ const ( ) type handlerFactory struct { - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer lggr logger.Logger } var _ HandlerFactory = (*handlerFactory)(nil) -func NewHandlerFactory(legacyChains evm.LegacyChainContainer, lggr logger.Logger) HandlerFactory { +func NewHandlerFactory(legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger) HandlerFactory { return &handlerFactory{legacyChains, lggr} } diff --git a/core/services/gateway/handlers/functions/handler.functions.go b/core/services/gateway/handlers/functions/handler.functions.go index b52c866a131..5277f9789d6 100644 --- a/core/services/gateway/handlers/functions/handler.functions.go +++ b/core/services/gateway/handlers/functions/handler.functions.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/api" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/config" @@ -96,7 +96,7 @@ type PendingRequest struct { var _ handlers.Handler = (*functionsHandler)(nil) -func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *config.DONConfig, don handlers.DON, legacyChains evm.LegacyChainContainer, lggr logger.Logger) (handlers.Handler, error) { +func NewFunctionsHandlerFromConfig(handlerConfig json.RawMessage, donConfig *config.DONConfig, don handlers.DON, legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger) (handlers.Handler, error) { var cfg FunctionsHandlerConfig err := json.Unmarshal(handlerConfig, &cfg) if err != nil { diff --git a/core/services/keeper/delegate.go b/core/services/keeper/delegate.go index 6d413624969..0dbf584c56f 100644 --- a/core/services/keeper/delegate.go +++ b/core/services/keeper/delegate.go @@ -5,7 +5,7 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -21,7 +21,7 @@ type Delegate struct { db *sqlx.DB jrm job.ORM pr pipeline.Runner - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer mailMon *utils.MailboxMonitor } @@ -31,7 +31,7 @@ func NewDelegate( jrm job.ORM, pr pipeline.Runner, logger logger.Logger, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, mailMon *utils.MailboxMonitor, ) *Delegate { return &Delegate{ diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index a064c3ed4b6..7bbecafa22d 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -15,7 +15,6 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" @@ -23,6 +22,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/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/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -62,7 +62,7 @@ func setup(t *testing.T, estimator gas.EvmFeeEstimator, overrideFn func(c *chain cltest.JobPipelineV2TestHelper, *txmmocks.MockEvmTxManager, keystore.Master, - evm.Chain, + legacyevm.Chain, keeper.ORM, ) { cfg := configtest.NewGeneralConfig(t, func(c *chainlink.Config, s *chainlink.Secrets) { diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index c8de3ec33c4..aa058d64979 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -19,8 +19,8 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/offchain_aggregator_wrapper" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -41,7 +41,7 @@ type Delegate struct { pipelineRunner pipeline.Runner peerWrapper *ocrcommon.SingletonPeerWrapper monitoringEndpointGen telemetry.MonitoringEndpointGenerator - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer lggr logger.Logger cfg Config mailMon *utils.MailboxMonitor @@ -58,7 +58,7 @@ func NewDelegate( pipelineRunner pipeline.Runner, peerWrapper *ocrcommon.SingletonPeerWrapper, monitoringEndpointGen telemetry.MonitoringEndpointGenerator, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, cfg Config, mailMon *utils.MailboxMonitor, @@ -346,7 +346,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e return services, nil } -func (d *Delegate) maybeCreateConfigOverrider(logger logger.Logger, chain evm.Chain, contractAddress ethkey.EIP55Address) (*ConfigOverriderImpl, error) { +func (d *Delegate) maybeCreateConfigOverrider(logger logger.Logger, chain legacyevm.Chain, contractAddress ethkey.EIP55Address) (*ConfigOverriderImpl, error) { flagsContractAddress := chain.Config().EVM().FlagsContractAddress() if flagsContractAddress != "" { flags, err := NewFlags(flagsContractAddress, chain.Client()) diff --git a/core/services/ocr/validate.go b/core/services/ocr/validate.go index 145bb7597ec..a780ebb0821 100644 --- a/core/services/ocr/validate.go +++ b/core/services/ocr/validate.go @@ -11,8 +11,8 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting" "github.com/smartcontractkit/chainlink/v2/common/config" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmconfig "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "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/ocrcommon" @@ -37,7 +37,7 @@ type insecureConfig interface { } // ValidatedOracleSpecToml validates an oracle spec that came from TOML -func ValidatedOracleSpecToml(legacyChains evm.LegacyChainContainer, tomlString string) (job.Job, error) { +func ValidatedOracleSpecToml(legacyChains legacyevm.LegacyChainContainer, tomlString string) (job.Job, error) { return ValidatedOracleSpecTomlCfg(func(id *big.Int) (evmconfig.ChainScopedConfig, error) { c, err := legacyChains.Get(id.String()) if err != nil { diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 7a90e3b07e4..45ee4e0f8fb 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -36,7 +36,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" @@ -118,7 +118,7 @@ type Delegate struct { isNewlyCreatedJob bool // Set to true if this is a new job freshly added, false if job was present already on node boot. mailMon *utils.MailboxMonitor - legacyChains evm.LegacyChainContainer // legacy: use relayers instead + legacyChains legacyevm.LegacyChainContainer // legacy: use relayers instead } type DelegateConfig interface { @@ -217,7 +217,7 @@ func NewDelegate( pipelineRunner pipeline.Runner, peerWrapper *ocrcommon.SingletonPeerWrapper, monitoringEndpointGen telemetry.MonitoringEndpointGenerator, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, cfg DelegateConfig, ks keystore.OCR2, @@ -462,7 +462,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { } } -func GetEVMEffectiveTransmitterID(jb *job.Job, chain evm.Chain, lggr logger.SugaredLogger) (string, error) { +func GetEVMEffectiveTransmitterID(jb *job.Job, chain legacyevm.Chain, lggr logger.SugaredLogger) (string, error) { spec := jb.OCR2OracleSpec if spec.PluginType == types.Mercury { return spec.TransmitterID.String, nil diff --git a/core/services/ocr2/plugins/functions/plugin.go b/core/services/ocr2/plugins/functions/plugin.go index 82280f527cd..2ebd7e30805 100644 --- a/core/services/ocr2/plugins/functions/plugin.go +++ b/core/services/ocr2/plugins/functions/plugin.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/functions" "github.com/smartcontractkit/chainlink/v2/core/services/gateway/connector" @@ -39,7 +39,7 @@ type FunctionsServicesConfig struct { BridgeORM bridges.ORM QConfig pg.QConfig DB *sqlx.DB - Chain evm.Chain + Chain legacyevm.Chain ContractID string Logger logger.Logger MailMon *utils.MailboxMonitor diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go index 3cb91ab8dcb..682ebb6e2a3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go @@ -20,9 +20,9 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -71,7 +71,7 @@ type LatestBlockGetter interface { LatestBlock() int64 } -func NewEVMRegistryService(addr common.Address, client evm.Chain, lggr logger.Logger) (*EvmRegistry, error) { +func NewEVMRegistryService(addr common.Address, client legacyevm.Chain, lggr logger.Logger) (*EvmRegistry, error) { keeperRegistryABI, err := abi.JSON(strings.NewReader(keeper_registry_wrapper2_0.KeeperRegistryABI)) if err != nil { return nil, fmt.Errorf("%w: %s", ErrABINotParsable, err) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go index 18795eb0736..e6db3e66e69 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go @@ -21,9 +21,9 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -79,7 +79,7 @@ type HttpClient interface { func NewEvmRegistry( lggr logger.Logger, addr common.Address, - client evm.Chain, + client legacyevm.Chain, registry *iregistry21.IKeeperRegistryMaster, mc *models.MercuryCredentials, al ActiveUpkeepList, diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go b/core/services/ocr2/plugins/ocr2keeper/evm21/services.go index 18c9ed6d39d..35d2f82942b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go +++ b/core/services/ocr2/plugins/ocr2keeper/evm21/services.go @@ -12,7 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-automation/pkg/v3/plugin" ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" 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" @@ -36,7 +36,7 @@ type AutomationServices interface { Keyring() ocr3types.OnchainKeyring[plugin.AutomationReportInfo] } -func New(addr common.Address, client evm.Chain, mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, lggr logger.Logger, db *sqlx.DB, dbCfg pg.QConfig) (AutomationServices, error) { +func New(addr common.Address, client legacyevm.Chain, mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, lggr logger.Logger, db *sqlx.DB, dbCfg pg.QConfig) (AutomationServices, error) { registryContract, err := iregistry21.NewIKeeperRegistryMaster(addr, client.Client()) if err != nil { return nil, fmt.Errorf("%w: failed to create caller for address and backend", ErrInitializationFailure) diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 2c477e7a6a1..0c81b16f0fa 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -44,7 +44,7 @@ var ( ErrNoChainFromSpec = fmt.Errorf("could not create chain from spec") ) -func EVMProvider(db *sqlx.DB, chain evm.Chain, lggr logger.Logger, spec job.Job, pr pipeline.Runner) (evmrelay.OCR2KeeperProvider, error) { +func EVMProvider(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, spec job.Job, pr pipeline.Runner) (evmrelay.OCR2KeeperProvider, error) { oSpec := spec.OCR2OracleSpec ocr2keeperRelayer := evmrelay.NewOCR2KeeperRelayer(db, chain, pr, spec, lggr.Named("OCR2KeeperRelayer")) @@ -71,7 +71,7 @@ func EVMDependencies20( spec job.Job, db *sqlx.DB, lggr logger.Logger, - chain evm.Chain, + chain legacyevm.Chain, pr pipeline.Runner, ) (evmrelay.OCR2KeeperProvider, *kevm20.EvmRegistry, Encoder20, *kevm20.LogProvider, error) { var err error @@ -112,7 +112,7 @@ func EVMDependencies21( spec job.Job, db *sqlx.DB, lggr logger.Logger, - chain evm.Chain, + chain legacyevm.Chain, pr pipeline.Runner, mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, diff --git a/core/services/pipeline/helpers_test.go b/core/services/pipeline/helpers_test.go index 974ff29d11c..9ee2dc693f2 100644 --- a/core/services/pipeline/helpers_test.go +++ b/core/services/pipeline/helpers_test.go @@ -6,7 +6,7 @@ import ( "github.com/google/uuid" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" ) const ( @@ -50,14 +50,14 @@ func (t *HTTPTask) HelperSetDependencies(config Config, restrictedHTTPClient, un t.unrestrictedHTTPClient = unrestrictedHTTPClient } -func (t *ETHCallTask) HelperSetDependencies(legacyChains evm.LegacyChainContainer, config Config, specGasLimit *uint32, jobType string) { +func (t *ETHCallTask) HelperSetDependencies(legacyChains legacyevm.LegacyChainContainer, config Config, specGasLimit *uint32, jobType string) { t.legacyChains = legacyChains t.config = config t.specGasLimit = specGasLimit t.jobType = jobType } -func (t *ETHTxTask) HelperSetDependencies(legacyChains evm.LegacyChainContainer, keyStore ETHKeyStore, specGasLimit *uint32, jobType string) { +func (t *ETHTxTask) HelperSetDependencies(legacyChains legacyevm.LegacyChainContainer, keyStore ETHKeyStore, specGasLimit *uint32, jobType string) { t.legacyChains = legacyChains t.keyStore = keyStore t.specGasLimit = specGasLimit diff --git a/core/services/pipeline/runner.go b/core/services/pipeline/runner.go index 388f7358ef3..768f163f9b1 100644 --- a/core/services/pipeline/runner.go +++ b/core/services/pipeline/runner.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -57,7 +57,7 @@ type runner struct { btORM bridges.ORM config Config bridgeConfig BridgeConfig - legacyEVMChains evm.LegacyChainContainer + legacyEVMChains legacyevm.LegacyChainContainer ethKeyStore ETHKeyStore vrfKeyStore VRFKeyStore runReaperWorker utils.SleeperTask @@ -102,7 +102,7 @@ var ( ) ) -func NewRunner(orm ORM, btORM bridges.ORM, cfg Config, bridgeCfg BridgeConfig, legacyChains evm.LegacyChainContainer, ethks ETHKeyStore, vrfks VRFKeyStore, lggr logger.Logger, httpClient, unrestrictedHTTPClient *http.Client) *runner { +func NewRunner(orm ORM, btORM bridges.ORM, cfg Config, bridgeCfg BridgeConfig, legacyChains legacyevm.LegacyChainContainer, ethks ETHKeyStore, vrfks VRFKeyStore, lggr logger.Logger, httpClient, unrestrictedHTTPClient *http.Client) *runner { r := &runner{ orm: orm, btORM: btORM, diff --git a/core/services/pipeline/task.estimategas.go b/core/services/pipeline/task.estimategas.go index 88c6f6facc3..1c0159819b4 100644 --- a/core/services/pipeline/task.estimategas.go +++ b/core/services/pipeline/task.estimategas.go @@ -12,7 +12,7 @@ import ( "github.com/shopspring/decimal" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -30,7 +30,7 @@ type EstimateGasLimitTask struct { EVMChainID string `json:"evmChainID" mapstructure:"evmChainID"` specGasLimit *uint32 - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer jobType string } diff --git a/core/services/pipeline/task.eth_call.go b/core/services/pipeline/task.eth_call.go index e877e1e90f4..3862ea10301 100644 --- a/core/services/pipeline/task.eth_call.go +++ b/core/services/pipeline/task.eth_call.go @@ -12,8 +12,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -35,7 +35,7 @@ type ETHCallTask struct { EVMChainID string `json:"evmChainID" mapstructure:"evmChainID"` specGasLimit *uint32 - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer config Config jobType string } diff --git a/core/services/pipeline/task.eth_call_test.go b/core/services/pipeline/task.eth_call_test.go index 8fe8bec16c0..cb58a03a9df 100644 --- a/core/services/pipeline/task.eth_call_test.go +++ b/core/services/pipeline/task.eth_call_test.go @@ -12,9 +12,9 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/chains" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" txmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" @@ -257,7 +257,7 @@ func TestETHCallTask(t *testing.T) { txManager := txmmocks.NewMockEvmTxManager(t) db := pgtest.NewSqlxDB(t) - var legacyChains evm.LegacyChainContainer + var legacyChains legacyevm.LegacyChainContainer if test.expectedErrorCause != nil || test.expectedErrorContains != "" { exts := evmtest.NewChainRelayExtenders(t, evmtest.TestChainOpts{DB: db, GeneralConfig: cfg, TxManager: txManager, KeyStore: keyStore}) legacyChains = evmrelay.NewLegacyChainsFromRelayerExtenders(exts) diff --git a/core/services/pipeline/task.eth_tx.go b/core/services/pipeline/task.eth_tx.go index 384c86446e7..c421b340c91 100644 --- a/core/services/pipeline/task.eth_tx.go +++ b/core/services/pipeline/task.eth_tx.go @@ -14,8 +14,8 @@ import ( "gopkg.in/guregu/null.v4" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" clnull "github.com/smartcontractkit/chainlink/v2/core/null" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -42,7 +42,7 @@ type ETHTxTask struct { forwardingAllowed bool specGasLimit *uint32 keyStore ETHKeyStore - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer jobType string } diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index e8267c9a842..952c1869bfa 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -24,8 +24,8 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -48,7 +48,7 @@ var _ commontypes.Relayer = &Relayer{} //nolint:staticcheck type Relayer struct { db *sqlx.DB - chain evm.Chain + chain legacyevm.Chain lggr logger.Logger ks CSAETHKeystore mercuryPool wsrpc.Pool @@ -89,7 +89,7 @@ func (c RelayerOpts) Validate() error { return err } -func NewRelayer(lggr logger.Logger, chain evm.Chain, opts RelayerOpts) (*Relayer, error) { +func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*Relayer, error) { err := opts.Validate() if err != nil { return nil, fmt.Errorf("cannot create evm relayer: %w", err) @@ -244,7 +244,7 @@ type configWatcher struct { contractABI abi.ABI offchainDigester ocrtypes.OffchainConfigDigester configPoller types.ConfigPoller - chain evm.Chain + chain legacyevm.Chain runReplay bool fromBlock uint64 replayCtx context.Context @@ -257,7 +257,7 @@ func newConfigWatcher(lggr logger.Logger, contractABI abi.ABI, offchainDigester ocrtypes.OffchainConfigDigester, configPoller types.ConfigPoller, - chain evm.Chain, + chain legacyevm.Chain, fromBlock uint64, runReplay bool, ) *configWatcher { @@ -321,7 +321,7 @@ func (c *configWatcher) ContractConfigTracker() ocrtypes.ContractConfigTracker { return c.configPoller } -func newConfigProvider(lggr logger.Logger, chain evm.Chain, opts *types.RelayOpts, eventBroadcaster pg.EventBroadcaster) (*configWatcher, error) { +func newConfigProvider(lggr logger.Logger, chain legacyevm.Chain, opts *types.RelayOpts, eventBroadcaster pg.EventBroadcaster) (*configWatcher, error) { if !common.IsHexAddress(opts.ContractID) { return nil, pkgerrors.Errorf("invalid contractID, expected hex address") } diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index 88f9d22099e..10e5d543b1a 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -19,8 +19,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" txm "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/functions/config" @@ -85,7 +85,7 @@ func (p *functionsProvider) Name() string { return p.configWatcher.Name() } -func NewFunctionsProvider(chain evm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { +func NewFunctionsProvider(chain legacyevm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { relayOpts := evmRelayTypes.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() if err != nil { @@ -130,7 +130,7 @@ func NewFunctionsProvider(chain evm.Chain, rargs commontypes.RelayArgs, pargs co }, nil } -func newFunctionsConfigProvider(pluginType functionsRelay.FunctionsPluginType, chain evm.Chain, args commontypes.RelayArgs, fromBlock uint64, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (*configWatcher, error) { +func newFunctionsConfigProvider(pluginType functionsRelay.FunctionsPluginType, chain legacyevm.Chain, args commontypes.RelayArgs, fromBlock uint64, logPollerWrapper evmRelayTypes.LogPollerWrapper, lggr logger.Logger) (*configWatcher, error) { if !common.IsHexAddress(args.ContractID) { return nil, errors.Errorf("invalid contractID, expected hex address") } diff --git a/core/services/relay/evm/loop_impl.go b/core/services/relay/evm/loop_impl.go index 309b5e24f62..57a09dd49ae 100644 --- a/core/services/relay/evm/loop_impl.go +++ b/core/services/relay/evm/loop_impl.go @@ -3,14 +3,14 @@ package evm import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) //go:generate mockery --quiet --name LoopRelayAdapter --output ./mocks/ --case=underscore type LoopRelayAdapter interface { loop.Relayer - Chain() evm.Chain + Chain() legacyevm.Chain } type LoopRelayer struct { loop.Relayer @@ -27,6 +27,6 @@ func NewLoopRelayServerAdapter(r *Relayer, cs EVMChainRelayerExtender) *LoopRela } } -func (la *LoopRelayer) Chain() evm.Chain { +func (la *LoopRelayer) Chain() legacyevm.Chain { return la.ext.Chain() } diff --git a/core/services/relay/evm/median.go b/core/services/relay/evm/median.go index 5184326cf25..db521a97208 100644 --- a/core/services/relay/evm/median.go +++ b/core/services/relay/evm/median.go @@ -14,7 +14,7 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -27,7 +27,7 @@ type medianContract struct { requestRoundTracker *RequestRoundTracker } -func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain evm.Chain, specID int32, db *sqlx.DB, lggr logger.Logger) (*medianContract, error) { +func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain legacyevm.Chain, specID int32, db *sqlx.DB, lggr logger.Logger) (*medianContract, error) { contract, err := offchain_aggregator_wrapper.NewOffchainAggregator(contractAddress, chain.Client()) if err != nil { return nil, errors.Wrap(err, "could not instantiate NewOffchainAggregator") diff --git a/core/services/relay/evm/mocks/loop_relay_adapter.go b/core/services/relay/evm/mocks/loop_relay_adapter.go index 13a73036a1d..0376c9f27a4 100644 --- a/core/services/relay/evm/mocks/loop_relay_adapter.go +++ b/core/services/relay/evm/mocks/loop_relay_adapter.go @@ -6,7 +6,8 @@ import ( context "context" big "math/big" - evm "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + legacyevm "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + mock "github.com/stretchr/testify/mock" types "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -18,15 +19,15 @@ type LoopRelayAdapter struct { } // Chain provides a mock function with given fields: -func (_m *LoopRelayAdapter) Chain() evm.Chain { +func (_m *LoopRelayAdapter) Chain() legacyevm.Chain { ret := _m.Called() - var r0 evm.Chain - if rf, ok := ret.Get(0).(func() evm.Chain); ok { + var r0 legacyevm.Chain + if rf, ok := ret.Get(0).(func() legacyevm.Chain); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(evm.Chain) + r0 = ret.Get(0).(legacyevm.Chain) } } diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 2b484edb798..3b3bfeb652d 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -20,7 +20,7 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" @@ -52,14 +52,14 @@ type OCR2KeeperRelayer interface { // ocr2keeperRelayer is the relayer with added DKG and OCR2Keeper provider functions. type ocr2keeperRelayer struct { db *sqlx.DB - chain evm.Chain + chain legacyevm.Chain pr pipeline.Runner spec job.Job lggr logger.Logger } // NewOCR2KeeperRelayer is the constructor of ocr2keeperRelayer -func NewOCR2KeeperRelayer(db *sqlx.DB, chain evm.Chain, pr pipeline.Runner, spec job.Job, lggr logger.Logger) OCR2KeeperRelayer { +func NewOCR2KeeperRelayer(db *sqlx.DB, chain legacyevm.Chain, pr pipeline.Runner, spec job.Job, lggr logger.Logger) OCR2KeeperRelayer { return &ocr2keeperRelayer{ db: db, chain: chain, @@ -130,7 +130,7 @@ func (c *ocr2keeperProvider) ContractTransmitter() ocrtypes.ContractTransmitter return c.contractTransmitter } -func newOCR2KeeperConfigProvider(lggr logger.Logger, chain evm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { +func newOCR2KeeperConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) if err != nil { diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index 66ce42b7d8d..b7a2220588b 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -16,7 +16,7 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/dkg/config" @@ -48,12 +48,12 @@ var ( // Relayer with added DKG and OCR2VRF provider functions. type ocr2vrfRelayer struct { db *sqlx.DB - chain evm.Chain + chain legacyevm.Chain lggr logger.Logger ethKeystore keystore.Eth } -func NewOCR2VRFRelayer(db *sqlx.DB, chain evm.Chain, lggr logger.Logger, ethKeystore keystore.Eth) OCR2VRFRelayer { +func NewOCR2VRFRelayer(db *sqlx.DB, chain legacyevm.Chain, lggr logger.Logger, ethKeystore keystore.Eth) OCR2VRFRelayer { return &ocr2vrfRelayer{ db: db, chain: chain, @@ -119,7 +119,7 @@ func (c *ocr2vrfProvider) ContractTransmitter() ocrtypes.ContractTransmitter { return c.contractTransmitter } -func newOCR2VRFConfigProvider(lggr logger.Logger, chain evm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { +func newOCR2VRFConfigProvider(lggr logger.Logger, chain legacyevm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) if err != nil { diff --git a/core/services/relay/evm/relayer_extender.go b/core/services/relay/evm/relayer_extender.go index 43626037a17..83f03b47f9e 100644 --- a/core/services/relay/evm/relayer_extender.go +++ b/core/services/relay/evm/relayer_extender.go @@ -11,9 +11,8 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" - evmchain "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" ) // ErrNoChains indicates that no EVM chains have been started @@ -21,31 +20,31 @@ var ErrNoChains = errors.New("no EVM chains loaded") type EVMChainRelayerExtender interface { loop.RelayerExt - Chain() evmchain.Chain + Chain() legacyevm.Chain } type EVMChainRelayerExtenderSlicer interface { Slice() []EVMChainRelayerExtender Len() int - AppConfig() evmchain.AppConfig + AppConfig() legacyevm.AppConfig } type ChainRelayerExtenders struct { exts []EVMChainRelayerExtender - cfg evmchain.AppConfig + cfg legacyevm.AppConfig } var _ EVMChainRelayerExtenderSlicer = &ChainRelayerExtenders{} -func NewLegacyChainsFromRelayerExtenders(exts EVMChainRelayerExtenderSlicer) *evmchain.LegacyChains { - m := make(map[string]evmchain.Chain) +func NewLegacyChainsFromRelayerExtenders(exts EVMChainRelayerExtenderSlicer) *legacyevm.LegacyChains { + m := make(map[string]legacyevm.Chain) for _, r := range exts.Slice() { m[r.Chain().ID().String()] = r.Chain() } - return evmchain.NewLegacyChains(m, exts.AppConfig().EVMConfigs()) + return legacyevm.NewLegacyChains(m, exts.AppConfig().EVMConfigs()) } -func newChainRelayerExtsFromSlice(exts []*ChainRelayerExt, appConfig evm.AppConfig) *ChainRelayerExtenders { +func newChainRelayerExtsFromSlice(exts []*ChainRelayerExt, appConfig legacyevm.AppConfig) *ChainRelayerExtenders { temp := make([]EVMChainRelayerExtender, len(exts)) for i := range exts { temp[i] = exts[i] @@ -56,7 +55,7 @@ func newChainRelayerExtsFromSlice(exts []*ChainRelayerExt, appConfig evm.AppConf } } -func (c *ChainRelayerExtenders) AppConfig() evmchain.AppConfig { +func (c *ChainRelayerExtenders) AppConfig() legacyevm.AppConfig { return c.cfg } @@ -70,7 +69,7 @@ func (c *ChainRelayerExtenders) Len() int { // implements OneChain type ChainRelayerExt struct { - chain evmchain.Chain + chain legacyevm.Chain } var _ EVMChainRelayerExtender = &ChainRelayerExt{} @@ -91,7 +90,7 @@ func (s *ChainRelayerExt) ID() string { return s.chain.ID().String() } -func (s *ChainRelayerExt) Chain() evmchain.Chain { +func (s *ChainRelayerExt) Chain() legacyevm.Chain { return s.chain } @@ -117,7 +116,7 @@ func (s *ChainRelayerExt) Ready() (err error) { return s.chain.Ready() } -func NewChainRelayerExtenders(ctx context.Context, opts evmchain.ChainRelayExtenderConfig) (*ChainRelayerExtenders, error) { +func NewChainRelayerExtenders(ctx context.Context, opts legacyevm.ChainRelayExtenderConfig) (*ChainRelayerExtenders, error) { if err := opts.Validate(); err != nil { return nil, err } @@ -142,14 +141,14 @@ func NewChainRelayerExtenders(ctx context.Context, opts evmchain.ChainRelayExten for i := range enabled { cid := enabled[i].ChainID.String() - privOpts := evmchain.ChainRelayExtenderConfig{ + privOpts := legacyevm.ChainRelayExtenderConfig{ Logger: opts.Logger.Named(cid), ChainOpts: opts.ChainOpts, KeyStore: opts.KeyStore, } privOpts.Logger.Infow(fmt.Sprintf("Loading chain %s", cid), "evmChainID", cid) - chain, err2 := evmchain.NewTOMLChain(ctx, enabled[i], privOpts) + chain, err2 := legacyevm.NewTOMLChain(ctx, enabled[i], privOpts) if err2 != nil { err = multierr.Combine(err, fmt.Errorf("failed to create chain %s: %w", cid, err2)) continue diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index 3b91f783b4a..f739d112ab9 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -12,9 +12,9 @@ import ( "github.com/jmoiron/sqlx" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" @@ -37,7 +37,7 @@ type Delegate struct { pr pipeline.Runner porm pipeline.ORM ks keystore.Master - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer lggr logger.Logger mailMon *utils.MailboxMonitor } @@ -47,7 +47,7 @@ func NewDelegate( ks keystore.Master, pr pipeline.Runner, porm pipeline.ORM, - legacyChains evm.LegacyChainContainer, + legacyChains legacyevm.LegacyChainContainer, lggr logger.Logger, cfg pg.QConfig, mailMon *utils.MailboxMonitor) *Delegate { diff --git a/core/services/vrf/delegate_test.go b/core/services/vrf/delegate_test.go index 3d544027d40..3c297026004 100644 --- a/core/services/vrf/delegate_test.go +++ b/core/services/vrf/delegate_test.go @@ -9,7 +9,6 @@ import ( "github.com/jmoiron/sqlx" "github.com/smartcontractkit/chainlink/v2/core/bridges" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker" @@ -18,6 +17,7 @@ import ( log_mocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -60,7 +60,7 @@ type vrfUniverse struct { submitter common.Address txm *txmgr.TxManager hb httypes.HeadBroadcaster - legacyChains evm.LegacyChainContainer + legacyChains legacyevm.LegacyChainContainer cid big.Int } diff --git a/core/services/vrf/v1/listener_v1.go b/core/services/vrf/v1/listener_v1.go index 3e958801cdf..494847797fa 100644 --- a/core/services/vrf/v1/listener_v1.go +++ b/core/services/vrf/v1/listener_v1.go @@ -17,9 +17,9 @@ import ( "github.com/theodesp/go-heaps/pairing" "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/solidity_vrf_coordinator_interface" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/recovery" @@ -63,7 +63,7 @@ type Listener struct { NewHead chan struct{} LatestHead uint64 LatestHeadMu sync.RWMutex - Chain evm.Chain + Chain legacyevm.Chain // We can keep these pending logs in memory because we // only mark them confirmed once we send a corresponding fulfillment transaction. diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index f57cb640f67..c1376eafebb 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -33,7 +33,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" evmclimocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" @@ -41,6 +40,7 @@ import ( evmlogger "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_blockhash_store" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/blockhash_store" @@ -1604,7 +1604,7 @@ func TestIntegrationVRFV2(t *testing.T) { require.Zero(t, key.Cmp(keys[0])) require.NoError(t, app.Start(testutils.Context(t))) - var chain evm.Chain + var chain legacyevm.Chain chain, err = app.GetRelayers().LegacyEVMChains().Get(testutils.SimulatedChainID.String()) require.NoError(t, err) listenerV2 := v22.MakeTestListenerV2(chain) diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index 7f23e022771..e0f8ff9a5bf 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -28,11 +28,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/batch_vrf_coordinator_v2plus" @@ -98,7 +98,7 @@ func New( cfg vrfcommon.Config, feeCfg vrfcommon.FeeConfig, l logger.Logger, - chain evm.Chain, + chain legacyevm.Chain, chainID *big.Int, q pg.Q, coordinator CoordinatorV2_X, @@ -170,7 +170,7 @@ type listenerV2 struct { cfg vrfcommon.Config feeCfg vrfcommon.FeeConfig l logger.SugaredLogger - chain evm.Chain + chain legacyevm.Chain chainID *big.Int mailMon *utils.MailboxMonitor diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index 513aa01ef65..8a7b9ce3fef 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -22,13 +22,13 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" - evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" + evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" "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" @@ -49,7 +49,7 @@ func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.M return txm } -func MakeTestListenerV2(chain evm.Chain) *listenerV2 { +func MakeTestListenerV2(chain legacyevm.Chain) *listenerV2 { return &listenerV2{chainID: chain.Client().ConfiguredChainID(), chain: chain} } diff --git a/core/web/common.go b/core/web/common.go index ae2158d153b..66159d8b60a 100644 --- a/core/web/common.go +++ b/core/web/common.go @@ -5,7 +5,7 @@ import ( "github.com/pkg/errors" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" ) var ( @@ -15,7 +15,7 @@ var ( ErrMultipleChains = errors.New("more than one chain available, you must specify chain id parameter") ) -func getChain(legacyChains evm.LegacyChainContainer, chainIDstr string) (chain evm.Chain, err error) { +func getChain(legacyChains legacyevm.LegacyChainContainer, chainIDstr string) (chain legacyevm.Chain, err error) { if chainIDstr != "" && chainIDstr != "" { // evm keys are expected to be parsable as a big int diff --git a/core/web/eth_keys_controller.go b/core/web/eth_keys_controller.go index b202d90f21e..d99992c0f56 100644 --- a/core/web/eth_keys_controller.go +++ b/core/web/eth_keys_controller.go @@ -10,8 +10,8 @@ import ( "strings" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" @@ -412,7 +412,7 @@ func (ekc *ETHKeysController) getKeyMaxGasPriceWei(state ethkey.State, keyAddres // getChain is a convenience wrapper to retrieve a chain for a given request // and call the corresponding API response error function for 400, 404 and 500 results -func (ekc *ETHKeysController) getChain(c *gin.Context, chainIDstr string) (chain evm.Chain, ok bool) { +func (ekc *ETHKeysController) getChain(c *gin.Context, chainIDstr string) (chain legacyevm.Chain, ok bool) { chain, err := getChain(ekc.app.GetRelayers().LegacyEVMChains(), chainIDstr) if err != nil { if errors.Is(err, ErrInvalidChainID) || errors.Is(err, ErrMultipleChains) { diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index 7f09bc9fdc6..6ab621661a6 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -11,9 +11,9 @@ import ( "github.com/pkg/errors" commontxmgr "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger/audit" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -92,7 +92,7 @@ func (tc *EVMTransfersController) Create(c *gin.Context) { } // ValidateEthBalanceForTransfer validates that the current balance can cover the transaction amount -func ValidateEthBalanceForTransfer(c *gin.Context, chain evm.Chain, fromAddr common.Address, amount assets.Eth) error { +func ValidateEthBalanceForTransfer(c *gin.Context, chain legacyevm.Chain, fromAddr common.Address, amount assets.Eth) error { var err error var balance *big.Int diff --git a/core/web/resolver/eth_key.go b/core/web/resolver/eth_key.go index 868d53565dd..a9c060ef0c1 100644 --- a/core/web/resolver/eth_key.go +++ b/core/web/resolver/eth_key.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/graph-gophers/graphql-go" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/web/loader" ) @@ -14,7 +14,7 @@ import ( type ETHKey struct { state ethkey.State addr ethkey.EIP55Address - chain evm.Chain + chain legacyevm.Chain } type ETHKeyResolver struct { diff --git a/core/web/resolver/eth_key_test.go b/core/web/resolver/eth_key_test.go index d75282e0fbd..ea106a4b30c 100644 --- a/core/web/resolver/eth_key_test.go +++ b/core/web/resolver/eth_key_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/mock" commonassets "github.com/smartcontractkit/chainlink-common/pkg/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" mocks2 "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/configtest" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" @@ -90,8 +90,8 @@ func TestResolver_ETHKeys(t *testing.T) { linkAddr := common.HexToAddress("0x5431F5F973781809D18643b87B44921b11355d81") cfg := configtest.NewGeneralConfig(t, nil) - m := map[string]evm.Chain{states[0].EVMChainID.String(): f.Mocks.chain} - legacyEVMChains := evm.NewLegacyChains(m, cfg.EVMConfigs()) + m := map[string]legacyevm.Chain{states[0].EVMChainID.String(): f.Mocks.chain} + legacyEVMChains := legacyevm.NewLegacyChains(m, cfg.EVMConfigs()) f.Mocks.ethKs.On("GetStatesForKeys", keys).Return(states, nil) f.Mocks.ethKs.On("Get", keys[0].Address.Hex()).Return(keys[0], nil) diff --git a/core/web/resolver/resolver_test.go b/core/web/resolver/resolver_test.go index 85c495faaae..d5906d99aff 100644 --- a/core/web/resolver/resolver_test.go +++ b/core/web/resolver/resolver_test.go @@ -15,6 +15,7 @@ import ( evmConfigMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/mocks" evmORMMocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" evmtxmgrmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr/mocks" + legacyEvmORMMocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks" coremocks "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest" @@ -50,8 +51,8 @@ type mocks struct { p2p *keystoreMocks.P2P vrf *keystoreMocks.VRF solana *keystoreMocks.Solana - chain *evmORMMocks.Chain - legacyEVMChains *evmORMMocks.LegacyChainContainer + chain *legacyEvmORMMocks.Chain + legacyEVMChains *legacyEvmORMMocks.LegacyChainContainer relayerChainInterops *chainlinkMocks.FakeRelayerChainInteroperators ethClient *evmClientMocks.Client eIMgr *webhookmocks.ExternalInitiatorManager @@ -109,8 +110,8 @@ func setupFramework(t *testing.T) *gqlTestFramework { p2p: keystoreMocks.NewP2P(t), vrf: keystoreMocks.NewVRF(t), solana: keystoreMocks.NewSolana(t), - chain: evmORMMocks.NewChain(t), - legacyEVMChains: evmORMMocks.NewLegacyChainContainer(t), + chain: legacyEvmORMMocks.NewChain(t), + legacyEVMChains: legacyEvmORMMocks.NewLegacyChainContainer(t), relayerChainInterops: &chainlinkMocks.FakeRelayerChainInteroperators{}, ethClient: evmClientMocks.NewClient(t), eIMgr: webhookmocks.NewExternalInitiatorManager(t), diff --git a/go.mod b/go.mod index 0f75e120fe6..b2afd2e2111 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 diff --git a/go.sum b/go.sum index fc8cab806a9..98371af2e2f 100644 --- a/go.sum +++ b/go.sum @@ -1507,8 +1507,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 h1:HJCPvJ+ZjU9TSX4YD5rxBJqGAvqhDfzoJgI3WmfeWeI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index ca77d3730e2..4cf6a502c49 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 github.com/smartcontractkit/chainlink-testing-framework v1.19.5 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index f117bf73e81..ef0a5b66ffb 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3 h1:cyA1aW1PYrOLZAMaSmuH7U99QBTfrF59s+6uDxQgOr0= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231121180428-d7f28e91ccc3/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 h1:HJCPvJ+ZjU9TSX4YD5rxBJqGAvqhDfzoJgI3WmfeWeI= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= From 05ccd282d98151e75a0171be2da3862d55f2fb97 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Tue, 28 Nov 2023 13:51:25 +0200 Subject: [PATCH 206/271] Remove FriendlyNumber function (#11376) --- common/headtracker/head_tracker.go | 6 ++---- core/config/presenters.go | 14 -------------- 2 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 core/config/presenters.go diff --git a/common/headtracker/head_tracker.go b/common/headtracker/head_tracker.go index 34a319e3c1c..6e379776c0f 100644 --- a/common/headtracker/head_tracker.go +++ b/common/headtracker/head_tracker.go @@ -15,7 +15,6 @@ import ( htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -103,7 +102,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) Start(ctx context.Context) error } if latestChain.IsValid() { ht.log.Debugw( - fmt.Sprintf("HeadTracker: Tracking logs from last block %v with hash %s", config.FriendlyNumber(latestChain.BlockNumber()), latestChain.BlockHash()), + fmt.Sprintf("HeadTracker: Tracking logs from last block %v with hash %s", latestChain.BlockNumber(), latestChain.BlockHash()), "blockNumber", latestChain.BlockNumber(), "blockHash", latestChain.BlockHash(), ) @@ -193,8 +192,7 @@ func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) getInitialHead(ctx context.Contex func (ht *HeadTracker[HTH, S, ID, BLOCK_HASH]) handleNewHead(ctx context.Context, head HTH) error { prevHead := ht.headSaver.LatestChain() - ht.log.Debugw(fmt.Sprintf("Received new head %v", config.FriendlyNumber(head.BlockNumber())), - "blockHeight", head.BlockNumber(), + ht.log.Debugw(fmt.Sprintf("Received new head %v", head.BlockNumber()), "blockHash", head.BlockHash(), "parentHeadHash", head.GetParentHash(), "blockTs", head.GetTimestamp(), diff --git a/core/config/presenters.go b/core/config/presenters.go deleted file mode 100644 index f4da66fd151..00000000000 --- a/core/config/presenters.go +++ /dev/null @@ -1,14 +0,0 @@ -package config - -import ( - "fmt" - "math/big" - - "golang.org/x/exp/constraints" -) - -// FriendlyNumber returns a string printing the integer or big.Int in both -// decimal and hexadecimal formats. -func FriendlyNumber[N constraints.Integer | *big.Int](n N) string { - return fmt.Sprintf("#%[1]v (0x%[1]x)", n) -} From 524eafcfc37599acda0ad7ebef01a6fb2f8434ce Mon Sep 17 00:00:00 2001 From: Makram Date: Tue, 28 Nov 2023 15:05:06 +0200 Subject: [PATCH 207/271] feat: log poller for vrf v2/v2+ (#11174) * feat: log poller for vrf v2/+ * chore: address PR comments * fix: RLock() instead of Lock() * fix: test * fix: tests still flakey * fix: lint * fix: build * chore: PR comments * chore: time out old requests * chore: skip EOA tests these tests have questionable value; we should probably remove them or find a better way to test this particular functionality. Perhaps this should even be removed. * remove concurrency in processing seems unnecessary * fix build * fix build * fix lint * fix test * fix tests --- core/services/vrf/delegate.go | 57 +- core/services/vrf/v2/bhs_feeder_test.go | 26 +- .../vrf/v2/coordinator_v2x_interface.go | 37 + .../vrf/v2/integration_helpers_test.go | 35 +- .../vrf/v2/integration_v2_plus_test.go | 16 +- core/services/vrf/v2/integration_v2_test.go | 13 +- core/services/vrf/v2/listener_v2.go | 1608 +---------------- core/services/vrf/v2/listener_v2_helpers.go | 103 ++ .../vrf/v2/listener_v2_log_listener.go | 451 +++++ .../vrf/v2/listener_v2_log_processor.go | 1214 +++++++++++++ core/services/vrf/v2/listener_v2_test.go | 79 - core/services/vrf/v2/listener_v2_types.go | 114 +- .../services/vrf/v2/listener_v2_types_test.go | 5 - core/services/vrf/vrfcommon/inflight_cache.go | 85 + .../services/vrf/vrfcommon/log_dedupe_test.go | 22 +- core/services/vrf/vrftesthelpers/helpers.go | 2 +- core/testdata/testspecs/v2_specs.go | 10 +- .../actions/vrfv2plus/vrfv2plus_steps.go | 4 +- integration-tests/client/chainlink_models.go | 1 + integration-tests/types/config/node/core.go | 6 + 20 files changed, 2140 insertions(+), 1748 deletions(-) create mode 100644 core/services/vrf/v2/listener_v2_helpers.go create mode 100644 core/services/vrf/v2/listener_v2_log_listener.go create mode 100644 core/services/vrf/v2/listener_v2_log_processor.go create mode 100644 core/services/vrf/vrfcommon/inflight_cache.go diff --git a/core/services/vrf/delegate.go b/core/services/vrf/delegate.go index f739d112ab9..a13df71d9a3 100644 --- a/core/services/vrf/delegate.go +++ b/core/services/vrf/delegate.go @@ -66,10 +66,10 @@ func (d *Delegate) JobType() job.Type { return job.VRF } -func (d *Delegate) BeforeJobCreated(spec job.Job) {} -func (d *Delegate) AfterJobCreated(spec job.Job) {} -func (d *Delegate) BeforeJobDeleted(spec job.Job) {} -func (d *Delegate) OnDeleteJob(spec job.Job, q pg.Queryer) error { return nil } +func (d *Delegate) BeforeJobCreated(job.Job) {} +func (d *Delegate) AfterJobCreated(job.Job) {} +func (d *Delegate) BeforeJobDeleted(job.Job) {} +func (d *Delegate) OnDeleteJob(job.Job, pg.Queryer) error { return nil } // ServicesForSpec satisfies the job.Delegate interface. func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { @@ -160,24 +160,28 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { return nil, errors.Wrap(err2, "NewAggregatorV3Interface") } - return []job.ServiceCtx{v2.New( - chain.Config().EVM(), - chain.Config().EVM().GasEstimator(), - lV2Plus, - chain, - chain.ID(), - d.q, - v2.NewCoordinatorV2_5(coordinatorV2Plus), - batchCoordinatorV2, - vrfOwner, - aggregator, - d.pr, - d.ks.Eth(), - jb, - d.mailMon, - utils.NewHighCapacityMailbox[log.Broadcast](), - func() {}, - vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())))}, nil + return []job.ServiceCtx{ + v2.New( + chain.Config().EVM(), + chain.Config().EVM().GasEstimator(), + lV2Plus, + chain, + chain.ID(), + d.q, + v2.NewCoordinatorV2_5(coordinatorV2Plus), + batchCoordinatorV2, + vrfOwner, + aggregator, + d.pr, + d.ks.Eth(), + jb, + func() {}, + // the lookback in the deduper must be >= the lookback specified for the log poller + // otherwise we will end up re-delivering logs that were already delivered. + vrfcommon.NewInflightCache(int(chain.Config().EVM().FinalityDepth())), + vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())), + ), + }, nil } if _, ok := task.(*pipeline.VRFTaskV2); ok { if err2 := CheckFromAddressesExist(jb, d.ks.Eth()); err != nil { @@ -225,10 +229,13 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { d.pr, d.ks.Eth(), jb, - d.mailMon, - utils.NewHighCapacityMailbox[log.Broadcast](), func() {}, - vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())))}, nil + // the lookback in the deduper must be >= the lookback specified for the log poller + // otherwise we will end up re-delivering logs that were already delivered. + vrfcommon.NewInflightCache(int(chain.Config().EVM().FinalityDepth())), + vrfcommon.NewLogDeduper(int(chain.Config().EVM().FinalityDepth())), + ), + }, nil } if _, ok := task.(*pipeline.VRFTask); ok { return []job.ServiceCtx{&v1.Listener{ diff --git a/core/services/vrf/v2/bhs_feeder_test.go b/core/services/vrf/v2/bhs_feeder_test.go index f388f80f561..31a4ff815a9 100644 --- a/core/services/vrf/v2/bhs_feeder_test.go +++ b/core/services/vrf/v2/bhs_feeder_test.go @@ -10,7 +10,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" "github.com/smartcontractkit/chainlink/v2/core/store/models" @@ -63,26 +62,12 @@ func TestStartHeartbeats(t *testing.T) { heartbeatPeriod := 5 * time.Second t.Run("bhs_feeder_startheartbeats_happy_path", func(tt *testing.T) { - coordinatorAddress := uni.rootContractAddress - vrfVersion := vrfcommon.V2 - app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, keys...) require.NoError(t, app.Start(testutils.Context(t))) - var ( - v2CoordinatorAddress string - v2PlusCoordinatorAddress string - ) - - if vrfVersion == vrfcommon.V2 { - v2CoordinatorAddress = coordinatorAddress.String() - } else if vrfVersion == vrfcommon.V2Plus { - v2PlusCoordinatorAddress = coordinatorAddress.String() - } - _ = vrftesthelpers.CreateAndStartBHSJob( t, bhsKeyAddresses, app, uni.bhsContractAddress.String(), "", - v2CoordinatorAddress, v2PlusCoordinatorAddress, "", 0, 200, heartbeatPeriod, 100) + uni.rootContractAddress.String(), "", "", 0, 200, heartbeatPeriod, 100) // Ensure log poller is ready and has all logs. require.NoError(t, app.GetRelayers().LegacyEVMChains().Slice()[0].LogPoller().Ready()) @@ -97,9 +82,10 @@ func TestStartHeartbeats(t *testing.T) { t.Logf("Sleeping %.2f seconds before checking blockhash in BHS added by BHS_Heartbeats_Service\n", diff.Seconds()) time.Sleep(diff) // storeEarliest in BHS contract stores blocktip - 256 in the Blockhash Store (BHS) - // before the initTxns:260 txns sent by the loop above, 18 txns are sent by - // newVRFCoordinatorV2Universe method. block tip is initTxns + 18 - blockTip := initTxns + 18 - verifyBlockhashStored(t, uni.coordinatorV2UniverseCommon, uint64(blockTip-256)) + tipHeader, err := uni.backend.HeaderByNumber(testutils.Context(t), nil) + require.NoError(t, err) + // the storeEarliest transaction will end up in a new block, hence the + 1 below. + blockNumberStored := tipHeader.Number.Uint64() - 256 + 1 + verifyBlockhashStored(t, uni.coordinatorV2UniverseCommon, blockNumberStored) }) } diff --git a/core/services/vrf/v2/coordinator_v2x_interface.go b/core/services/vrf/v2/coordinator_v2x_interface.go index b090c4ad5af..e20500cca89 100644 --- a/core/services/vrf/v2/coordinator_v2x_interface.go +++ b/core/services/vrf/v2/coordinator_v2x_interface.go @@ -28,6 +28,7 @@ var ( type CoordinatorV2_X interface { Address() common.Address ParseRandomWordsRequested(log types.Log) (RandomWordsRequested, error) + ParseRandomWordsFulfilled(log types.Log) (RandomWordsFulfilled, error) RequestRandomWords(opts *bind.TransactOpts, keyHash [32]byte, subID *big.Int, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, payInEth bool) (*types.Transaction, error) AddConsumer(opts *bind.TransactOpts, subID *big.Int, consumer common.Address) (*types.Transaction, error) CreateSubscription(opts *bind.TransactOpts) (*types.Transaction, error) @@ -47,6 +48,10 @@ type CoordinatorV2_X interface { GetCommitment(opts *bind.CallOpts, requestID *big.Int) ([32]byte, error) Migrate(opts *bind.TransactOpts, subID *big.Int, newCoordinator common.Address) (*types.Transaction, error) FundSubscriptionWithNative(opts *bind.TransactOpts, subID *big.Int, amount *big.Int) (*types.Transaction, error) + // RandomWordsRequestedTopic returns the log topic of the RandomWordsRequested log + RandomWordsRequestedTopic() common.Hash + // RandomWordsFulfilledTopic returns the log topic of the RandomWordsFulfilled log + RandomWordsFulfilledTopic() common.Hash } type coordinatorV2 struct { @@ -61,6 +66,14 @@ func NewCoordinatorV2(c *vrf_coordinator_v2.VRFCoordinatorV2) CoordinatorV2_X { } } +func (c *coordinatorV2) RandomWordsRequestedTopic() common.Hash { + return vrf_coordinator_v2.VRFCoordinatorV2RandomWordsRequested{}.Topic() +} + +func (c *coordinatorV2) RandomWordsFulfilledTopic() common.Hash { + return vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled{}.Topic() +} + func (c *coordinatorV2) Address() common.Address { return c.coordinator.Address() } @@ -73,6 +86,14 @@ func (c *coordinatorV2) ParseRandomWordsRequested(log types.Log) (RandomWordsReq return NewV2RandomWordsRequested(parsed), nil } +func (c *coordinatorV2) ParseRandomWordsFulfilled(log types.Log) (RandomWordsFulfilled, error) { + parsed, err := c.coordinator.ParseRandomWordsFulfilled(log) + if err != nil { + return nil, err + } + return NewV2RandomWordsFulfilled(parsed), nil +} + func (c *coordinatorV2) RequestRandomWords(opts *bind.TransactOpts, keyHash [32]byte, subID *big.Int, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, payInEth bool) (*types.Transaction, error) { return c.coordinator.RequestRandomWords(opts, keyHash, subID.Uint64(), requestConfirmations, callbackGasLimit, numWords) } @@ -187,6 +208,14 @@ func NewCoordinatorV2_5(c vrf_coordinator_v2_5.VRFCoordinatorV25Interface) Coord } } +func (c *coordinatorV2_5) RandomWordsRequestedTopic() common.Hash { + return vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalRandomWordsRequested{}.Topic() +} + +func (c *coordinatorV2_5) RandomWordsFulfilledTopic() common.Hash { + return vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalRandomWordsFulfilled{}.Topic() +} + func (c *coordinatorV2_5) Address() common.Address { return c.coordinator.Address() } @@ -199,6 +228,14 @@ func (c *coordinatorV2_5) ParseRandomWordsRequested(log types.Log) (RandomWordsR return NewV2_5RandomWordsRequested(parsed), nil } +func (c *coordinatorV2_5) ParseRandomWordsFulfilled(log types.Log) (RandomWordsFulfilled, error) { + parsed, err := c.coordinator.ParseRandomWordsFulfilled(log) + if err != nil { + return nil, err + } + return NewV2_5RandomWordsFulfilled(parsed), nil +} + func (c *coordinatorV2_5) RequestRandomWords(opts *bind.TransactOpts, keyHash [32]byte, subID *big.Int, requestConfirmations uint16, callbackGasLimit uint32, numWords uint32, payInEth bool) (*types.Transaction, error) { extraArgs, err := extraargs.ExtraArgsV1(payInEth) if err != nil { diff --git a/core/services/vrf/v2/integration_helpers_test.go b/core/services/vrf/v2/integration_helpers_test.go index a5737371919..03d96cadf20 100644 --- a/core/services/vrf/v2/integration_helpers_test.go +++ b/core/services/vrf/v2/integration_helpers_test.go @@ -72,6 +72,8 @@ func testSingleConsumerHappyPath( GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1, key2) @@ -205,8 +207,8 @@ func testMultipleConsumersNeedBHS( simulatedOverrides(t, assets.GWei(10), keySpecificOverrides...)(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) c.EVM[0].FinalityDepth = ptr[uint32](2) - c.EVM[0].LogPollInterval = models.MustNewDuration(time.Second) }) keys = append(keys, ownerKey, vrfKey) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, keys...) @@ -353,8 +355,8 @@ func testMultipleConsumersNeedTrustedBHS( c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(5_000_000)) c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) c.EVM[0].FinalityDepth = ptr[uint32](2) - c.EVM[0].LogPollInterval = models.MustNewDuration(time.Second) }) keys = append(keys, ownerKey, vrfKey) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, keys...) @@ -539,6 +541,8 @@ func testSingleConsumerHappyPathBatchFulfillment( c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) @@ -641,6 +645,8 @@ func testSingleConsumerNeedsTopUp( GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key) @@ -746,8 +752,8 @@ func testBlockHeaderFeeder( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) c.EVM[0].FinalityDepth = ptr[uint32](2) - c.EVM[0].LogPollInterval = models.MustNewDuration(time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, vrfKey, bhfKey) require.NoError(t, app.Start(testutils.Context(t))) @@ -904,6 +910,8 @@ func testSingleConsumerForcedFulfillment( GasEstimator: toml.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1, key2) @@ -1053,10 +1061,7 @@ func testSingleConsumerEIP150( vrfVersion vrfcommon.Version, nativePayment bool, ) { - callBackGasLimit := int64(2_500_000) // base callback gas. - eip150Fee := callBackGasLimit / 64 // premium needed for callWithExactGas - coordinatorFulfillmentOverhead := int64(90_000) // fixed gas used in coordinator fulfillment - gasLimit := callBackGasLimit + eip150Fee + coordinatorFulfillmentOverhead + callBackGasLimit := int64(2_500_000) // base callback gas. key1 := cltest.MustGenerateRandomKey(t) gasLanePriceWei := assets.GWei(10) @@ -1066,8 +1071,10 @@ func testSingleConsumerEIP150( Key: ptr(key1.EIP55Address), GasEstimator: v2.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) - c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(gasLimit)) + c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(3.5e6)) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) consumer := uni.vrfConsumers[0] @@ -1136,6 +1143,8 @@ func testSingleConsumerEIP150Revert( })(c, s) c.EVM[0].GasEstimator.LimitDefault = ptr(uint32(gasLimit)) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) consumer := uni.vrfConsumers[0] @@ -1199,6 +1208,8 @@ func testSingleConsumerBigGasCallbackSandwich( })(c, s) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) consumer := uni.vrfConsumers[0] @@ -1319,6 +1330,8 @@ func testSingleConsumerMultipleGasLanes( })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, cheapKey, expensiveKey) @@ -1434,6 +1447,8 @@ func testSingleConsumerAlwaysRevertingCallbackStillFulfilled( GasEstimator: v2.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key) consumer := uni.reverter @@ -1505,6 +1520,8 @@ func testConsumerProxyHappyPath( GasEstimator: v2.KeySpecificGasEstimator{PriceMax: gasLanePriceWei}, })(c, s) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1, key2) consumerOwner := uni.neil @@ -1629,6 +1646,8 @@ func testMaliciousConsumer( c.EVM[0].GasEstimator.PriceDefault = assets.GWei(1) c.EVM[0].GasEstimator.FeeCapDefault = assets.GWei(1) c.EVM[0].ChainID = (*utils.Big)(testutils.SimulatedChainID) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) carol := uni.vrfConsumers[0] diff --git a/core/services/vrf/v2/integration_v2_plus_test.go b/core/services/vrf/v2/integration_v2_plus_test.go index e45e650dc8d..1564f0f6343 100644 --- a/core/services/vrf/v2/integration_v2_plus_test.go +++ b/core/services/vrf/v2/integration_v2_plus_test.go @@ -50,6 +50,7 @@ import ( v22 "github.com/smartcontractkit/chainlink/v2/core/services/vrf/v2" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrftesthelpers" + "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -107,6 +108,10 @@ func newVRFCoordinatorV2PlusUniverse(t *testing.T, key ethkey.KeyV2, numConsumer vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalABI)) require.NoError(t, err) backend := cltest.NewSimulatedBackend(t, genesisData, gasLimit) + blockTime := time.UnixMilli(int64(backend.Blockchain().CurrentHeader().Time)) + err = backend.AdjustTime(time.Since(blockTime) - 24*time.Hour) + require.NoError(t, err) + backend.Commit() // Deploy link linkAddress, _, linkContract, err := link_token_interface.DeployLinkToken( sergey, backend) @@ -259,6 +264,10 @@ func newVRFCoordinatorV2PlusUniverse(t *testing.T, key ethkey.KeyV2, numConsumer require.NoError(t, err, "failed to set coordinator configuration") backend.Commit() + for i := 0; i < 200; i++ { + backend.Commit() + } + return coordinatorV2PlusUniverse{ coordinatorV2UniverseCommon: coordinatorV2UniverseCommon{ vrfConsumers: vrfConsumers, @@ -304,7 +313,6 @@ func newVRFCoordinatorV2PlusUniverse(t *testing.T, key ethkey.KeyV2, numConsumer } func TestVRFV2PlusIntegration_SingleConsumer_HappyPath_BatchFulfillment(t *testing.T) { - testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-2745") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) @@ -457,7 +465,7 @@ func TestVRFV2PlusIntegration_SingleConsumer_HappyPath(t *testing.T) { } func TestVRFV2PlusIntegration_SingleConsumer_EOA_Request(t *testing.T) { - testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/BCF-2744") + t.Skip("questionable value of this test") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) @@ -473,6 +481,7 @@ func TestVRFV2PlusIntegration_SingleConsumer_EOA_Request(t *testing.T) { } func TestVRFV2PlusIntegration_SingleConsumer_EOA_Request_Batching_Enabled(t *testing.T) { + t.Skip("questionable value of this test") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) @@ -488,7 +497,6 @@ func TestVRFV2PlusIntegration_SingleConsumer_EOA_Request_Batching_Enabled(t *tes } func TestVRFV2PlusIntegration_SingleConsumer_EIP150_HappyPath(t *testing.T) { - testutils.SkipFlakey(t, "https://smartcontract-it.atlassian.net/browse/VRF-589") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2PlusUniverse(t, ownerKey, 1, false) @@ -1149,6 +1157,8 @@ func TestVRFV2PlusIntegration_Migration(t *testing.T) { })(c, s) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](5_000_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) app := cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, uni.backend, ownerKey, key1) diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index c1376eafebb..74d923ce09f 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -183,6 +183,10 @@ func newVRFCoordinatorV2Universe(t *testing.T, key ethkey.KeyV2, numConsumers in vrf_coordinator_v2.VRFCoordinatorV2ABI)) require.NoError(t, err) backend := cltest.NewSimulatedBackend(t, genesisData, gasLimit) + blockTime := time.UnixMilli(int64(backend.Blockchain().CurrentHeader().Time)) + err = backend.AdjustTime(time.Since(blockTime) - 24*time.Hour) + require.NoError(t, err) + backend.Commit() // Deploy link linkAddress, _, linkContract, err := link_token_interface.DeployLinkToken( sergey, backend) @@ -925,6 +929,7 @@ func TestVRFV2Integration_SingleConsumer_HappyPath(t *testing.T) { } func TestVRFV2Integration_SingleConsumer_EOA_Request(t *testing.T) { + t.Skip("questionable value of this test") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2Universe(t, ownerKey, 1) @@ -940,6 +945,7 @@ func TestVRFV2Integration_SingleConsumer_EOA_Request(t *testing.T) { } func TestVRFV2Integration_SingleConsumer_EOA_Request_Batching_Enabled(t *testing.T) { + t.Skip("questionable value of this test") t.Parallel() ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2Universe(t, ownerKey, 1) @@ -1217,6 +1223,8 @@ func TestVRFV2Integration_Wrapper_High_Gas(t *testing.T) { })(c, s) c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](3_500_000) c.EVM[0].MinIncomingConfirmations = ptr[uint32](2) + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) }) ownerKey := cltest.MustGenerateRandomKey(t) uni := newVRFCoordinatorV2Universe(t, ownerKey, 1) @@ -1452,7 +1460,10 @@ func simulatedOverrides(t *testing.T, defaultGasPrice *assets.Wei, ks ...toml.Ke if defaultGasPrice != nil { c.EVM[0].GasEstimator.PriceDefault = defaultGasPrice } - c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](2_000_000) + c.EVM[0].GasEstimator.LimitDefault = ptr[uint32](3_500_000) + + c.Feature.LogPoller = ptr(true) + c.EVM[0].LogPollInterval = models.MustNewDuration(1 * time.Second) c.EVM[0].HeadTracker.MaxBufferSize = ptr[uint32](100) c.EVM[0].HeadTracker.SamplingInterval = models.MustNewDuration(0) // Head sampling disabled diff --git a/core/services/vrf/v2/listener_v2.go b/core/services/vrf/v2/listener_v2.go index e0f8ff9a5bf..5878bf54763 100644 --- a/core/services/vrf/v2/listener_v2.go +++ b/core/services/vrf/v2/listener_v2.go @@ -1,36 +1,22 @@ package v2 import ( - "cmp" "context" - "database/sql" "encoding/hex" - "fmt" - "math" "math/big" - "slices" "strings" "sync" "time" "github.com/avast/retry-go/v4" - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" - "github.com/google/uuid" "github.com/pkg/errors" - heaps "github.com/theodesp/go-heaps" "github.com/theodesp/go-heaps/pairing" - "go.uber.org/multierr" "github.com/smartcontractkit/chainlink-common/pkg/services" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/aggregator_v3_interface" @@ -45,12 +31,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" - "github.com/smartcontractkit/chainlink/v2/core/utils" - bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) var ( - _ log.Listener = &listenerV2{} _ job.ServiceCtx = &listenerV2{} coordinatorV2ABI = evmtypes.MustGetABI(vrf_coordinator_v2.VRFCoordinatorV2ABI) coordinatorV2PlusABI = evmtypes.MustGetABI(vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalABI) @@ -78,22 +61,8 @@ const ( txMetaFieldSubId = "SubId" txMetaGlobalSubId = "GlobalSubId" - - CouldNotDetermineIfLogConsumedMsg = "Could not determine if log was already consumed" ) -type errPossiblyInsufficientFunds struct{} - -func (errPossiblyInsufficientFunds) Error() string { - return "Simulation errored, possibly insufficient funds. Request will remain unprocessed until funds are available" -} - -type errBlockhashNotInStore struct{} - -func (errBlockhashNotInStore) Error() string { - return "Blockhash not in store" -} - func New( cfg vrfcommon.Config, feeCfg vrfcommon.FeeConfig, @@ -108,63 +77,34 @@ func New( pipelineRunner pipeline.Runner, gethks keystore.Eth, job job.Job, - mailMon *utils.MailboxMonitor, - reqLogs *utils.Mailbox[log.Broadcast], reqAdded func(), - deduper *vrfcommon.LogDeduper, + inflightCache vrfcommon.InflightCache, + fulfillmentDeduper *vrfcommon.LogDeduper, ) job.ServiceCtx { return &listenerV2{ - cfg: cfg, - feeCfg: feeCfg, - l: logger.Sugared(l), - chain: chain, - chainID: chainID, - mailMon: mailMon, - coordinator: coordinator, - batchCoordinator: batchCoordinator, - vrfOwner: vrfOwner, - pipelineRunner: pipelineRunner, - job: job, - q: q, - gethks: gethks, - reqLogs: reqLogs, - chStop: make(services.StopChan), - reqAdded: reqAdded, - blockNumberToReqID: pairing.New(), - latestHeadMu: sync.RWMutex{}, - wg: &sync.WaitGroup{}, - aggregator: aggregator, - deduper: deduper, + cfg: cfg, + feeCfg: feeCfg, + l: logger.Sugared(l), + chain: chain, + chainID: chainID, + coordinator: coordinator, + batchCoordinator: batchCoordinator, + vrfOwner: vrfOwner, + pipelineRunner: pipelineRunner, + job: job, + q: q, + gethks: gethks, + chStop: make(chan struct{}), + reqAdded: reqAdded, + blockNumberToReqID: pairing.New(), + latestHeadMu: sync.RWMutex{}, + wg: &sync.WaitGroup{}, + aggregator: aggregator, + inflightCache: inflightCache, + fulfillmentLogDeduper: fulfillmentDeduper, } } -type pendingRequest struct { - confirmedAtBlock uint64 - req RandomWordsRequested - lb log.Broadcast - utcTimestamp time.Time - - // used for exponential backoff when retrying - attempts int - lastTry time.Time -} - -type vrfPipelineResult struct { - err error - // maxFee indicates how much juels (link) or wei (ether) would be paid for the VRF request - // if it were to be fulfilled at the maximum gas price (i.e gas lane gas price). - maxFee *big.Int - // fundsNeeded indicates a "minimum balance" in juels or wei that must be held in the - // subscription's account in order to fulfill the request. - fundsNeeded *big.Int - run *pipeline.Run - payload string - gasLimit uint32 - req pendingRequest - proof VRFProof - reqCommitment RequestCommitment -} - type listenerV2 struct { services.StateMachine cfg vrfcommon.Config @@ -172,7 +112,6 @@ type listenerV2 struct { l logger.SugaredLogger chain legacyevm.Chain chainID *big.Int - mailMon *utils.MailboxMonitor coordinator CoordinatorV2_X batchCoordinator batch_vrf_coordinator_v2.BatchVRFCoordinatorV2Interface @@ -182,19 +121,13 @@ type listenerV2 struct { job job.Job q pg.Q gethks keystore.Eth - reqLogs *utils.Mailbox[log.Broadcast] chStop services.StopChan - // We can keep these pending logs in memory because we - // only mark them confirmed once we send a corresponding fulfillment transaction. - // So on node restart in the middle of processing, the lb will resend them. - reqsMu sync.Mutex // Both the log listener and the request handler write to reqs - reqs []pendingRequest + reqAdded func() // A simple debug helper // Data structures for reorg attack protection // We want a map so we can do an O(1) count update every fulfillment log we get. - respCountMu sync.Mutex - respCount map[string]uint64 + respCount map[string]uint64 // This auxiliary heap is used when we need to purge the // respCount map - we repeatedly want to remove the minimum log. // You could use a sorted list if the completed logs arrive in order, but they may not. @@ -210,8 +143,14 @@ type listenerV2 struct { // aggregator client to get link/eth feed prices from chain. Can be nil for VRF V2 plus aggregator aggregator_v3_interface.AggregatorV3InterfaceInterface - // deduper prevents processing duplicate requests from the log broadcaster. - deduper *vrfcommon.LogDeduper + // fulfillmentLogDeduper prevents re-processing fulfillment logs. + // fulfillment logs are used to increment counts in the respCount map + // and to update the blockNumberToReqID heap. + fulfillmentLogDeduper *vrfcommon.LogDeduper + + // inflightCache is a cache of in-flight requests, used to prevent + // re-processing of requests that are in-flight or already fulfilled. + inflightCache vrfcommon.InflightCache } func (lsn *listenerV2) HealthReport() map[string]error { @@ -234,7 +173,7 @@ func (lsn *listenerV2) Start(ctx context.Context) error { } if err != nil { lsn.l.Criticalw("Error getting coordinator config for gas limit check, starting anyway.", "err", err) - } else if conf.MaxGasLimit()+(GasProofVerification*2) > uint32(gasLimit) { + } else if conf.MaxGasLimit()+(GasProofVerification*2) > gasLimit { lsn.l.Criticalw("Node gas limit setting may not be high enough to fulfill all requests; it should be increased. Starting anyway.", "currentGasLimit", gasLimit, "neededGasLimit", conf.MaxGasLimit()+(GasProofVerification*2), @@ -244,24 +183,6 @@ func (lsn *listenerV2) Start(ctx context.Context) error { spec := job.LoadDefaultVRFPollPeriod(*lsn.job.VRFSpec) - unsubscribeLogs := lsn.chain.LogBroadcaster().Register(lsn, log.ListenerOpts{ - Contract: lsn.coordinator.Address(), - ParseLog: lsn.coordinator.ParseLog, - LogsWithTopics: lsn.coordinator.LogsWithTopics(spec.PublicKey.MustHash()), - // Specify a min incoming confirmations of 1 so that we can receive a request log - // right away. We set the real number of confirmations on a per-request basis in - // the getConfirmedAt method. - MinIncomingConfirmations: 1, - ReplayStartedCallback: lsn.ReplayStartedCallback, - }) - - latestHead, unsubscribeHeadBroadcaster := lsn.chain.HeadBroadcaster().Subscribe(lsn) - if latestHead != nil { - lsn.setLatestHead(latestHead) - } - - lsn.respCountMu.Lock() - defer lsn.respCountMu.Unlock() var respCount map[string]uint64 respCount, err = lsn.GetStartingResponseCountsV2(ctx) if err != nil { @@ -269,19 +190,13 @@ func (lsn *listenerV2) Start(ctx context.Context) error { } lsn.respCount = respCount - // Log listener gathers request logs - lsn.wg.Add(1) - go func() { - lsn.runLogListener([]func(){unsubscribeLogs, unsubscribeHeadBroadcaster}, spec.MinIncomingConfirmations, lsn.wg) - }() - - // Request handler periodically computes a set of logs which can be fulfilled. + // Log listener gathers request logs and processes them lsn.wg.Add(1) go func() { - lsn.runRequestHandler(spec.PollPeriod, lsn.wg) + defer lsn.wg.Done() + lsn.runLogListener(spec.PollPeriod, spec.MinIncomingConfirmations) }() - lsn.mailMon.Monitor(lsn.reqLogs, "VRFListenerV2", "RequestLogs", fmt.Sprint(lsn.job.ID)) return nil }) } @@ -326,1466 +241,27 @@ func (lsn *listenerV2) GetStartingResponseCountsV2(ctx context.Context) (respCou return respCounts, nil } -func (lsn *listenerV2) setLatestHead(head *evmtypes.Head) { +func (lsn *listenerV2) setLatestHead(head logpoller.LogPollerBlock) { lsn.latestHeadMu.Lock() defer lsn.latestHeadMu.Unlock() - num := uint64(head.Number) + num := uint64(head.BlockNumber) if num > lsn.latestHeadNumber { lsn.latestHeadNumber = num } } -// OnNewLongestChain is called by the head broadcaster when a new head is available. -func (lsn *listenerV2) OnNewLongestChain(ctx context.Context, head *evmtypes.Head) { - lsn.setLatestHead(head) -} - func (lsn *listenerV2) getLatestHead() uint64 { lsn.latestHeadMu.RLock() defer lsn.latestHeadMu.RUnlock() - return uint64(lsn.latestHeadNumber) -} - -// Returns all the confirmed logs from -// the pending queue by subscription -func (lsn *listenerV2) getAndRemoveConfirmedLogsBySub(latestHead uint64) map[string][]pendingRequest { - lsn.reqsMu.Lock() - defer lsn.reqsMu.Unlock() - vrfcommon.UpdateQueueSize(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), uniqueReqs(lsn.reqs)) - var toProcess = make(map[string][]pendingRequest) - var toKeep []pendingRequest - for i := 0; i < len(lsn.reqs); i++ { - if r := lsn.reqs[i]; lsn.ready(r, latestHead) { - toProcess[r.req.SubID().String()] = append(toProcess[r.req.SubID().String()], r) - } else { - toKeep = append(toKeep, lsn.reqs[i]) - } - } - lsn.reqs = toKeep - return toProcess -} - -func (lsn *listenerV2) ready(req pendingRequest, latestHead uint64) bool { - // Request is not eligible for fulfillment yet - if req.confirmedAtBlock > latestHead { - return false - } - - if lsn.job.VRFSpec.BackoffInitialDelay == 0 || req.attempts == 0 { - // Backoff is disabled, or this is the first try - return true - } - - return time.Now().UTC().After( - nextTry( - req.attempts, - lsn.job.VRFSpec.BackoffInitialDelay, - lsn.job.VRFSpec.BackoffMaxDelay, - req.lastTry)) -} - -func nextTry(retries int, initial, max time.Duration, last time.Time) time.Time { - expBackoffFactor := math.Pow(backoffFactor, float64(retries-1)) - - var delay time.Duration - if expBackoffFactor > float64(max/initial) { - delay = max - } else { - delay = time.Duration(float64(initial) * expBackoffFactor) - } - return last.Add(delay) -} - -// Remove all entries 10000 blocks or older -// to avoid a memory leak. -func (lsn *listenerV2) pruneConfirmedRequestCounts() { - lsn.respCountMu.Lock() - defer lsn.respCountMu.Unlock() - min := lsn.blockNumberToReqID.FindMin() - for min != nil { - m := min.(fulfilledReqV2) - if m.blockNumber > (lsn.getLatestHead() - 10000) { - break - } - delete(lsn.respCount, m.reqID) - lsn.blockNumberToReqID.DeleteMin() - min = lsn.blockNumberToReqID.FindMin() - } -} - -// Determine a set of logs that are confirmed -// and the subscription has sufficient balance to fulfill, -// given a eth call with the max gas price. -// Note we have to consider the pending reqs already in the txm as already "spent" link or native, -// using a max link or max native consumed in their metadata. -// A user will need a minBalance capable of fulfilling a single req at the max gas price or nothing will happen. -// This is acceptable as users can choose different keyhashes which have different max gas prices. -// Other variables which can change the bill amount between our eth call simulation and tx execution: -// - Link/eth price fluctuation -// - Falling back to BHS -// However the likelihood is vanishingly small as -// 1) the window between simulation and tx execution is tiny. -// 2) the max gas price provides a very large buffer most of the time. -// Its easier to optimistically assume it will go though and in the rare case of a reversion -// we simply retry TODO: follow up where if we see a fulfillment revert, return log to the queue. -func (lsn *listenerV2) processPendingVRFRequests(ctx context.Context) { - confirmed := lsn.getAndRemoveConfirmedLogsBySub(lsn.getLatestHead()) - processed := make(map[string]struct{}) - start := time.Now() - - // Add any unprocessed requests back to lsn.reqs after request processing is complete. - defer func() { - var toKeep []pendingRequest - for _, subReqs := range confirmed { - for _, req := range subReqs { - if _, ok := processed[req.req.RequestID().String()]; !ok { - req.attempts++ - req.lastTry = time.Now().UTC() - toKeep = append(toKeep, req) - if lsn.job.VRFSpec.BackoffInitialDelay != 0 { - lsn.l.Infow("Request failed, next retry will be delayed.", - "reqID", req.req.RequestID().String(), - "subID", req.req.SubID(), - "attempts", req.attempts, - "lastTry", req.lastTry.String(), - "nextTry", nextTry( - req.attempts, - lsn.job.VRFSpec.BackoffInitialDelay, - lsn.job.VRFSpec.BackoffMaxDelay, - req.lastTry)) - } - } else { - lsn.markLogAsConsumed(req.lb) - } - } - } - // There could be logs accumulated to this slice while request processor is running, - // so we merged the new ones with the ones that need to be requeued. - lsn.reqsMu.Lock() - lsn.reqs = append(lsn.reqs, toKeep...) - lsn.l.Infow("Finished processing pending requests", - "totalProcessed", len(processed), - "totalFailed", len(toKeep), - "total", len(lsn.reqs), - "time", time.Since(start).String()) - lsn.reqsMu.Unlock() // unlock here since len(lsn.reqs) is a read, to avoid a data race. - }() - - if len(confirmed) == 0 { - lsn.l.Infow("No pending requests ready for processing") - return - } - for subID, reqs := range confirmed { - l := lsn.l.With("subID", subID, "startTime", time.Now(), "numReqsForSub", len(reqs)) - // Get the balance of the subscription and also it's active status. - // The reason we need both is that we cannot determine if a subscription - // is active solely by it's balance, since an active subscription could legitimately - // have a zero balance. - var ( - startLinkBalance *big.Int - startEthBalance *big.Int - subIsActive bool - ) - sID, ok := new(big.Int).SetString(subID, 10) - if !ok { - l.Criticalw("Unable to convert %s to Int", subID) - continue - } - sub, err := lsn.coordinator.GetSubscription(&bind.CallOpts{ - Context: ctx}, sID) - - if err != nil { - if strings.Contains(err.Error(), "execution reverted") { - // "execution reverted" indicates that the subscription no longer exists. - // We can no longer just mark these as processed and continue, - // since it could be that the subscription was canceled while there - // were still unfulfilled requests. - // The simplest approach to handle this is to enter the processRequestsPerSub - // loop rather than create a bunch of largely duplicated code - // to handle this specific situation, since we need to run the pipeline to get - // the VRF proof, abi-encode it, etc. - l.Warnw("Subscription not found - setting start balance to zero", "subID", subID, "err", err) - startLinkBalance = big.NewInt(0) - } else { - // Most likely this is an RPC error, so we re-try later. - l.Errorw("Unable to read subscription balance", "err", err) - continue - } - } else { - // Happy path - sub is active. - startLinkBalance = sub.Balance() - if sub.Version() == vrfcommon.V2Plus { - startEthBalance = sub.NativeBalance() - } - subIsActive = true - } - - // Sort requests in ascending order by CallbackGasLimit - // so that we process the "cheapest" requests for each subscription - // first. This allows us to break out of the processing loop as early as possible - // in the event that a subscription is too underfunded to have it's - // requests processed. - slices.SortFunc(reqs, func(a, b pendingRequest) int { - return cmp.Compare(a.req.CallbackGasLimit(), b.req.CallbackGasLimit()) - }) - - p := lsn.processRequestsPerSub(ctx, sID, startLinkBalance, startEthBalance, reqs, subIsActive) - for reqID := range p { - processed[reqID] = struct{}{} - } - } - lsn.pruneConfirmedRequestCounts() -} - -// MaybeSubtractReservedLink figures out how much LINK is reserved for other VRF requests that -// have not been fully confirmed yet on-chain, and subtracts that from the given startBalance, -// and returns that value if there are no errors. -func (lsn *listenerV2) MaybeSubtractReservedLink(ctx context.Context, startBalance *big.Int, chainID *big.Int, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { - var metaField string - if vrfVersion == vrfcommon.V2Plus { - metaField = txMetaGlobalSubId - } else if vrfVersion == vrfcommon.V2 { - metaField = txMetaFieldSubId - } else { - return nil, errors.Errorf("unsupported vrf version %s", vrfVersion) - } - - txes, err := lsn.chain.TxManager().FindTxesByMetaFieldAndStates(ctx, metaField, subID.String(), reserveEthLinkQueryStates, chainID) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, errors.Wrap(err, "TXM FindTxesByMetaFieldAndStates failed") - } - - reservedLinkSum := big.NewInt(0) - // Aggregate non-null MaxLink from all txes returned - for _, tx := range txes { - var meta *txmgrtypes.TxMeta[common.Address, common.Hash] - meta, err = tx.GetMeta() - if err != nil { - return nil, errors.Wrap(err, "GetMeta for Tx failed") - } - if meta != nil && meta.MaxLink != nil { - txMaxLink, success := new(big.Int).SetString(*meta.MaxLink, 10) - if !success { - return nil, fmt.Errorf("converting reserved LINK %s", *meta.MaxLink) - } - - reservedLinkSum.Add(reservedLinkSum, txMaxLink) - } - } - - return new(big.Int).Sub(startBalance, reservedLinkSum), nil -} - -// MaybeSubtractReservedEth figures out how much ether is reserved for other VRF requests that -// have not been fully confirmed yet on-chain, and subtracts that from the given startBalance, -// and returns that value if there are no errors. -func (lsn *listenerV2) MaybeSubtractReservedEth(ctx context.Context, startBalance *big.Int, chainID *big.Int, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { - var metaField string - if vrfVersion == vrfcommon.V2Plus { - metaField = txMetaGlobalSubId - } else if vrfVersion == vrfcommon.V2 { - // native payment is not supported for v2, so returning 0 reserved ETH - return big.NewInt(0), nil - } else { - return nil, errors.Errorf("unsupported vrf version %s", vrfVersion) - } - txes, err := lsn.chain.TxManager().FindTxesByMetaFieldAndStates(ctx, metaField, subID.String(), reserveEthLinkQueryStates, chainID) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - return nil, errors.Wrap(err, "TXM FindTxesByMetaFieldAndStates failed") - } - - reservedEthSum := big.NewInt(0) - // Aggregate non-null MaxEth from all txes returned - for _, tx := range txes { - var meta *txmgrtypes.TxMeta[common.Address, common.Hash] - meta, err = tx.GetMeta() - if err != nil { - return nil, errors.Wrap(err, "GetMeta for Tx failed") - } - if meta != nil && meta.MaxEth != nil { - txMaxEth, success := new(big.Int).SetString(*meta.MaxEth, 10) - if !success { - return nil, fmt.Errorf("converting reserved ETH %s", *meta.MaxEth) - } - - reservedEthSum.Add(reservedEthSum, txMaxEth) - } - } - - if startBalance != nil { - return new(big.Int).Sub(startBalance, reservedEthSum), nil - } - return big.NewInt(0), nil -} - -type fulfilledReqV2 struct { - blockNumber uint64 - reqID string -} - -func (a fulfilledReqV2) Compare(b heaps.Item) int { - a1 := a - a2 := b.(fulfilledReqV2) - switch { - case a1.blockNumber > a2.blockNumber: - return 1 - case a1.blockNumber < a2.blockNumber: - return -1 - default: - return 0 - } -} - -func (lsn *listenerV2) processRequestsPerSubBatchHelper( - ctx context.Context, - subID *big.Int, - startBalance *big.Int, - startBalanceNoReserved *big.Int, - reqs []pendingRequest, - subIsActive bool, - nativePayment bool, -) (processed map[string]struct{}) { - start := time.Now() - processed = make(map[string]struct{}) - - // Base the max gas for a batch on the max gas limit for a single callback. - // Since the max gas limit for a single callback is usually quite large already, - // we probably don't want to exceed it too much so that we can reliably get - // batch fulfillments included, while also making sure that the biggest gas guzzler - // callbacks are included. - config, err := lsn.coordinator.GetConfig(&bind.CallOpts{ - Context: ctx, - }) - if err != nil { - lsn.l.Errorw("Couldn't get config from coordinator", "err", err) - return processed - } - - // Add very conservative upper bound estimate on verification costs. - batchMaxGas := uint32(config.MaxGasLimit() + 400_000) - - l := lsn.l.With( - "subID", subID, - "eligibleSubReqs", len(reqs), - "startBalance", startBalance.String(), - "startBalanceNoReserved", startBalanceNoReserved.String(), - "batchMaxGas", batchMaxGas, - "subIsActive", subIsActive, - "nativePayment", nativePayment, - ) - - defer func() { - l.Infow("Finished processing for sub", - "endBalance", startBalanceNoReserved.String(), - "totalProcessed", len(processed), - "totalUnique", uniqueReqs(reqs), - "time", time.Since(start).String()) - }() - - l.Infow("Processing requests for subscription with batching") - - // Check for already consumed or expired reqs - unconsumed, processedReqs := lsn.getUnconsumed(l, reqs) - for _, reqID := range processedReqs { - processed[reqID] = struct{}{} - } - - // Process requests in chunks in order to kick off as many jobs - // as configured in parallel. Then we can combine into fulfillment - // batches afterwards. - for chunkStart := 0; chunkStart < len(unconsumed); chunkStart += int(lsn.job.VRFSpec.ChunkSize) { - chunkEnd := chunkStart + int(lsn.job.VRFSpec.ChunkSize) - if chunkEnd > len(unconsumed) { - chunkEnd = len(unconsumed) - } - chunk := unconsumed[chunkStart:chunkEnd] - - var unfulfilled []pendingRequest - alreadyFulfilled, err := lsn.checkReqsFulfilled(ctx, l, chunk) - if errors.Is(err, context.Canceled) { - l.Infow("Context canceled, stopping request processing", "err", err) - return processed - } else if err != nil { - l.Errorw("Error checking for already fulfilled requests, proceeding anyway", "err", err) - } - for i, a := range alreadyFulfilled { - if a { - processed[chunk[i].req.RequestID().String()] = struct{}{} - } else { - unfulfilled = append(unfulfilled, chunk[i]) - } - } - - // All fromAddresses passed to the VRFv2 job have the same KeySpecific-MaxPrice value. - fromAddresses := lsn.fromAddresses() - maxGasPriceWei := lsn.feeCfg.PriceMaxKey(fromAddresses[0]) - - // Cases: - // 1. Never simulated: in this case, we want to observe the time until simulated - // on the utcTimestamp field of the pending request. - // 2. Simulated before: in this case, lastTry will be set to a non-zero time value, - // in which case we'd want to use that as a relative point from when we last tried - // the request. - observeRequestSimDuration(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), unfulfilled) - - pipelines := lsn.runPipelines(ctx, l, maxGasPriceWei, unfulfilled) - batches := newBatchFulfillments(batchMaxGas, lsn.coordinator.Version()) - outOfBalance := false - for _, p := range pipelines { - ll := l.With("reqID", p.req.req.RequestID().String(), - "txHash", p.req.req.Raw().TxHash, - "maxGasPrice", maxGasPriceWei.String(), - "fundsNeeded", p.fundsNeeded.String(), - "maxFee", p.maxFee.String(), - "gasLimit", p.gasLimit, - "attempts", p.req.attempts, - "remainingBalance", startBalanceNoReserved.String(), - "consumerAddress", p.req.req.Sender(), - "blockNumber", p.req.req.Raw().BlockNumber, - "blockHash", p.req.req.Raw().BlockHash, - ) - fromAddresses := lsn.fromAddresses() - fromAddress, err := lsn.gethks.GetRoundRobinAddress(lsn.chainID, fromAddresses...) - if err != nil { - l.Errorw("Couldn't get next from address", "err", err) - continue - } - ll = ll.With("fromAddress", fromAddress) - - if p.err != nil { - if errors.Is(p.err, errBlockhashNotInStore{}) { - // Running the blockhash store feeder in backwards mode will be required to - // resolve this. - ll.Criticalw("Pipeline error", "err", p.err) - } else { - ll.Errorw("Pipeline error", "err", p.err) - if !subIsActive { - ll.Warnw("Force-fulfilling a request with insufficient funds on a cancelled sub") - etx, err := lsn.enqueueForceFulfillment(ctx, p, fromAddress) - if err != nil { - ll.Errorw("Error enqueuing force-fulfillment, re-queueing request", "err", err) - continue - } - ll.Infow("Successfully enqueued force-fulfillment", "ethTxID", etx.ID) - processed[p.req.req.RequestID().String()] = struct{}{} - - // Need to put a continue here, otherwise the next if statement will be hit - // and we'd break out of the loop prematurely. - // If a sub is canceled, we want to force-fulfill ALL of it's pending requests - // before saying we're done with it. - continue - } - - if startBalanceNoReserved.Cmp(p.fundsNeeded) < 0 && errors.Is(p.err, errPossiblyInsufficientFunds{}) { - ll.Infow("Insufficient balance to fulfill a request based on estimate, breaking", "err", p.err) - outOfBalance = true - - // break out of this inner loop to process the currently constructed batch - break - } - - // Ensure consumer is valid, otherwise drop the request. - if !lsn.isConsumerValidAfterFinalityDepthElapsed(ctx, p.req) { - lsn.l.Infow( - "Dropping request that was made by an invalid consumer.", - "consumerAddress", p.req.req.Sender(), - "reqID", p.req.req.RequestID(), - "blockNumber", p.req.req.Raw().BlockNumber, - "blockHash", p.req.req.Raw().BlockHash, - ) - lsn.markLogAsConsumed(p.req.lb) - } - } - continue - } - - if startBalanceNoReserved.Cmp(p.maxFee) < 0 { - // Insufficient funds, have to wait for a user top up. - // Break out of the loop now and process what we are able to process - // in the constructed batches. - ll.Infow("Insufficient balance to fulfill a request, breaking") - break - } - - batches.addRun(p, fromAddress) - - startBalanceNoReserved.Sub(startBalanceNoReserved, p.maxFee) - } - - var processedRequestIDs []string - for _, batch := range batches.fulfillments { - l.Debugw("Processing batch", "batchSize", len(batch.proofs)) - p := lsn.processBatch(l, subID, startBalanceNoReserved, batchMaxGas, batch, batch.fromAddress) - processedRequestIDs = append(processedRequestIDs, p...) - } - - for _, reqID := range processedRequestIDs { - processed[reqID] = struct{}{} - } - - // outOfBalance is set to true if the current sub we are processing - // has run out of funds to process any remaining requests. After enqueueing - // this constructed batch, we break out of this outer loop in order to - // avoid unnecessarily processing the remaining requests. - if outOfBalance { - break - } - } - - return -} - -func (lsn *listenerV2) processRequestsPerSubBatch( - ctx context.Context, - subID *big.Int, - startLinkBalance *big.Int, - startEthBalance *big.Int, - reqs []pendingRequest, - subIsActive bool, -) map[string]struct{} { - var processed = make(map[string]struct{}) - startBalanceNoReserveLink, err := lsn.MaybeSubtractReservedLink( - ctx, startLinkBalance, lsn.chainID, subID, lsn.coordinator.Version()) - if err != nil { - lsn.l.Errorw("Couldn't get reserved LINK for subscription", "sub", reqs[0].req.SubID(), "err", err) - return processed - } - startBalanceNoReserveEth, err := lsn.MaybeSubtractReservedEth( - ctx, startEthBalance, lsn.chainID, subID, lsn.coordinator.Version()) - if err != nil { - lsn.l.Errorw("Couldn't get reserved ether for subscription", "sub", reqs[0].req.SubID(), "err", err) - return processed - } - - // Split the requests into native and LINK requests. - var ( - nativeRequests []pendingRequest - linkRequests []pendingRequest - ) - for _, req := range reqs { - if req.req.NativePayment() { - nativeRequests = append(nativeRequests, req) - } else { - linkRequests = append(linkRequests, req) - } - } - // process the native and link requests in parallel - var wg sync.WaitGroup - var nativeProcessed, linkProcessed map[string]struct{} - wg.Add(2) - go func() { - defer wg.Done() - nativeProcessed = lsn.processRequestsPerSubBatchHelper(ctx, subID, startEthBalance, startBalanceNoReserveEth, nativeRequests, subIsActive, true) - }() - go func() { - defer wg.Done() - linkProcessed = lsn.processRequestsPerSubBatchHelper(ctx, subID, startLinkBalance, startBalanceNoReserveLink, linkRequests, subIsActive, false) - }() - wg.Wait() - // combine the processed link and native requests into the processed map - for k, v := range nativeProcessed { - processed[k] = v - } - for k, v := range linkProcessed { - processed[k] = v - } - - return processed -} - -// enqueueForceFulfillment enqueues a forced fulfillment through the -// VRFOwner contract. It estimates gas again on the transaction due -// to the extra steps taken within VRFOwner.fulfillRandomWords. -func (lsn *listenerV2) enqueueForceFulfillment( - ctx context.Context, - p vrfPipelineResult, - fromAddress common.Address, -) (etx txmgr.Tx, err error) { - if lsn.job.VRFSpec.VRFOwnerAddress == nil { - err = errors.New("vrf owner address not set in job spec, recreate job and provide it to force-fulfill") - return - } - - if p.payload == "" { - // should probably never happen - // a critical log will be logged if this is the case in simulateFulfillment - err = errors.New("empty payload in vrfPipelineResult") - return - } - - // fulfill the request through the VRF owner - err = lsn.q.Transaction(func(tx pg.Queryer) error { - if err = lsn.chain.LogBroadcaster().MarkConsumed(p.req.lb, pg.WithQueryer(tx)); err != nil { - return err - } - - lsn.l.Infow("VRFOwner.fulfillRandomWords vs. VRFCoordinatorV2.fulfillRandomWords", - "vrf_owner.fulfillRandomWords", hexutil.Encode(vrfOwnerABI.Methods["fulfillRandomWords"].ID), - "vrf_coordinator_v2.fulfillRandomWords", hexutil.Encode(coordinatorV2ABI.Methods["fulfillRandomWords"].ID), - ) - - vrfOwnerAddress1 := lsn.vrfOwner.Address() - vrfOwnerAddressSpec := lsn.job.VRFSpec.VRFOwnerAddress.Address() - lsn.l.Infow("addresses diff", "wrapper_address", vrfOwnerAddress1, "spec_address", vrfOwnerAddressSpec) - - lsn.l.Infow("fulfillRandomWords payload", "proof", p.proof, "commitment", p.reqCommitment.Get(), "payload", p.payload) - txData := hexutil.MustDecode(p.payload) - if err != nil { - return errors.Wrap(err, "abi pack VRFOwner.fulfillRandomWords") - } - estimateGasLimit, err := lsn.chain.Client().EstimateGas(ctx, ethereum.CallMsg{ - From: fromAddress, - To: &vrfOwnerAddressSpec, - Data: txData, - }) - if err != nil { - return errors.Wrap(err, "failed to estimate gas on VRFOwner.fulfillRandomWords") - } - - lsn.l.Infow("Estimated gas limit on force fulfillment", - "estimateGasLimit", estimateGasLimit, "pipelineGasLimit", p.gasLimit) - if estimateGasLimit < uint64(p.gasLimit) { - estimateGasLimit = uint64(p.gasLimit) - } - - requestID := common.BytesToHash(p.req.req.RequestID().Bytes()) - subID := p.req.req.SubID() - requestTxHash := p.req.req.Raw().TxHash - etx, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ - FromAddress: fromAddress, - ToAddress: lsn.vrfOwner.Address(), - EncodedPayload: txData, - FeeLimit: uint32(estimateGasLimit), - Strategy: txmgrcommon.NewSendEveryStrategy(), - Meta: &txmgr.TxMeta{ - RequestID: &requestID, - SubID: ptr(subID.Uint64()), - RequestTxHash: &requestTxHash, - // No max link since simulation failed - }, - }) - return err - }) - return -} - -// For an errored pipeline run, wait until the finality depth of the chain to have elapsed, -// then check if the failing request is being called by an invalid sender. Return false if this is the case, -// otherwise true. -func (lsn *listenerV2) isConsumerValidAfterFinalityDepthElapsed(ctx context.Context, req pendingRequest) bool { - latestHead := lsn.getLatestHead() - if latestHead-req.req.Raw().BlockNumber > uint64(lsn.cfg.FinalityDepth()) { - code, err := lsn.chain.Client().CodeAt(ctx, req.req.Sender(), big.NewInt(int64(latestHead))) - if err != nil { - lsn.l.Warnw("Failed to fetch contract code", "err", err) - return true // error fetching code, give the benefit of doubt to the consumer - } - if len(code) == 0 { - return false // invalid consumer - } - } - - return true // valid consumer, or finality depth has not elapsed -} - -// processRequestsPerSubHelper processes a set of pending requests for the provided sub id. -// It returns a set of request IDs that were processed. -// Note that the provided startBalanceNoReserve is the balance of the subscription -// minus any pending requests that have already been processed and not yet fulfilled onchain. -func (lsn *listenerV2) processRequestsPerSubHelper( - ctx context.Context, - subID *big.Int, - startBalance *big.Int, - startBalanceNoReserved *big.Int, - reqs []pendingRequest, - subIsActive bool, - nativePayment bool, -) (processed map[string]struct{}) { - start := time.Now() - processed = make(map[string]struct{}) - - l := lsn.l.With( - "subID", subID, - "eligibleSubReqs", len(reqs), - "startBalance", startBalance.String(), - "startBalanceNoReserved", startBalanceNoReserved.String(), - "subIsActive", subIsActive, - "nativePayment", nativePayment, - ) - - defer func() { - l.Infow("Finished processing for sub", - "endBalance", startBalanceNoReserved.String(), - "totalProcessed", len(processed), - "totalUnique", uniqueReqs(reqs), - "time", time.Since(start).String()) - }() - - l.Infow("Processing requests for subscription") - - // Check for already consumed or expired reqs - unconsumed, processedReqs := lsn.getUnconsumed(l, reqs) - for _, reqID := range processedReqs { - processed[reqID] = struct{}{} - } - - // Process requests in chunks - for chunkStart := 0; chunkStart < len(unconsumed); chunkStart += int(lsn.job.VRFSpec.ChunkSize) { - chunkEnd := chunkStart + int(lsn.job.VRFSpec.ChunkSize) - if chunkEnd > len(unconsumed) { - chunkEnd = len(unconsumed) - } - chunk := unconsumed[chunkStart:chunkEnd] - - var unfulfilled []pendingRequest - alreadyFulfilled, err := lsn.checkReqsFulfilled(ctx, l, chunk) - if errors.Is(err, context.Canceled) { - l.Infow("Context canceled, stopping request processing", "err", err) - return processed - } else if err != nil { - l.Errorw("Error checking for already fulfilled requests, proceeding anyway", "err", err) - } - for i, a := range alreadyFulfilled { - if a { - processed[chunk[i].req.RequestID().String()] = struct{}{} - } else { - unfulfilled = append(unfulfilled, chunk[i]) - } - } - - // All fromAddresses passed to the VRFv2 job have the same KeySpecific-MaxPrice value. - fromAddresses := lsn.fromAddresses() - maxGasPriceWei := lsn.feeCfg.PriceMaxKey(fromAddresses[0]) - observeRequestSimDuration(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), unfulfilled) - pipelines := lsn.runPipelines(ctx, l, maxGasPriceWei, unfulfilled) - for _, p := range pipelines { - ll := l.With("reqID", p.req.req.RequestID().String(), - "txHash", p.req.req.Raw().TxHash, - "maxGasPrice", maxGasPriceWei.String(), - "fundsNeeded", p.fundsNeeded.String(), - "maxFee", p.maxFee.String(), - "gasLimit", p.gasLimit, - "attempts", p.req.attempts, - "remainingBalance", startBalanceNoReserved.String(), - "consumerAddress", p.req.req.Sender(), - "blockNumber", p.req.req.Raw().BlockNumber, - "blockHash", p.req.req.Raw().BlockHash, - ) - fromAddress, err := lsn.gethks.GetRoundRobinAddress(lsn.chainID, fromAddresses...) - if err != nil { - l.Errorw("Couldn't get next from address", "err", err) - continue - } - ll = ll.With("fromAddress", fromAddress) - - if p.err != nil { - if errors.Is(p.err, errBlockhashNotInStore{}) { - // Running the blockhash store feeder in backwards mode will be required to - // resolve this. - ll.Criticalw("Pipeline error", "err", p.err) - } else { - ll.Errorw("Pipeline error", "err", p.err) - - if !subIsActive { - lsn.l.Warnw("Force-fulfilling a request with insufficient funds on a cancelled sub") - etx, err2 := lsn.enqueueForceFulfillment(ctx, p, fromAddress) - if err2 != nil { - ll.Errorw("Error enqueuing force-fulfillment, re-queueing request", "err", err2) - continue - } - ll.Infow("Enqueued force-fulfillment", "ethTxID", etx.ID) - processed[p.req.req.RequestID().String()] = struct{}{} - - // Need to put a continue here, otherwise the next if statement will be hit - // and we'd break out of the loop prematurely. - // If a sub is canceled, we want to force-fulfill ALL of it's pending requests - // before saying we're done with it. - continue - } - - if startBalanceNoReserved.Cmp(p.fundsNeeded) < 0 { - ll.Infow("Insufficient balance to fulfill a request based on estimate, returning", "err", p.err) - return processed - } - - // Ensure consumer is valid, otherwise drop the request. - if !lsn.isConsumerValidAfterFinalityDepthElapsed(ctx, p.req) { - lsn.l.Infow( - "Dropping request that was made by an invalid consumer.", - "consumerAddress", p.req.req.Sender(), - "reqID", p.req.req.RequestID(), - "blockNumber", p.req.req.Raw().BlockNumber, - "blockHash", p.req.req.Raw().BlockHash, - ) - lsn.markLogAsConsumed(p.req.lb) - } - } - continue - } - - if startBalanceNoReserved.Cmp(p.maxFee) < 0 { - // Insufficient funds, have to wait for a user top up. Leave it unprocessed for now - ll.Infow("Insufficient balance to fulfill a request, returning") - return processed - } - - ll.Infow("Enqueuing fulfillment") - var transaction txmgr.Tx - err = lsn.q.Transaction(func(tx pg.Queryer) error { - if err = lsn.pipelineRunner.InsertFinishedRun(p.run, true, pg.WithQueryer(tx)); err != nil { - return err - } - if err = lsn.chain.LogBroadcaster().MarkConsumed(p.req.lb, pg.WithQueryer(tx)); err != nil { - return err - } - - var maxLink, maxEth *string - tmp := p.maxFee.String() - if p.reqCommitment.NativePayment() { - maxEth = &tmp - } else { - maxLink = &tmp - } - var ( - txMetaSubID *uint64 - txMetaGlobalSubID *string - ) - if lsn.coordinator.Version() == vrfcommon.V2Plus { - txMetaGlobalSubID = ptr(p.req.req.SubID().String()) - } else if lsn.coordinator.Version() == vrfcommon.V2 { - txMetaSubID = ptr(p.req.req.SubID().Uint64()) - } - requestID := common.BytesToHash(p.req.req.RequestID().Bytes()) - coordinatorAddress := lsn.coordinator.Address() - requestTxHash := p.req.req.Raw().TxHash - transaction, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ - FromAddress: fromAddress, - ToAddress: lsn.coordinator.Address(), - EncodedPayload: hexutil.MustDecode(p.payload), - FeeLimit: p.gasLimit, - Meta: &txmgr.TxMeta{ - RequestID: &requestID, - MaxLink: maxLink, - MaxEth: maxEth, - SubID: txMetaSubID, - GlobalSubID: txMetaGlobalSubID, - RequestTxHash: &requestTxHash, - }, - Strategy: txmgrcommon.NewSendEveryStrategy(), - Checker: txmgr.TransmitCheckerSpec{ - CheckerType: lsn.transmitCheckerType(), - VRFCoordinatorAddress: &coordinatorAddress, - VRFRequestBlockNumber: new(big.Int).SetUint64(p.req.req.Raw().BlockNumber), - }, - }) - return err - }) - if err != nil { - ll.Errorw("Error enqueuing fulfillment, requeuing request", "err", err) - continue - } - ll.Infow("Enqueued fulfillment", "ethTxID", transaction.GetID()) - - // If we successfully enqueued for the txm, subtract that balance - // And loop to attempt to enqueue another fulfillment - startBalanceNoReserved.Sub(startBalanceNoReserved, p.maxFee) - processed[p.req.req.RequestID().String()] = struct{}{} - vrfcommon.IncProcessedReqs(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version()) - } - } - - return -} - -func (lsn *listenerV2) transmitCheckerType() txmgrtypes.TransmitCheckerType { - if lsn.coordinator.Version() == vrfcommon.V2 { - return txmgr.TransmitCheckerTypeVRFV2 - } - return txmgr.TransmitCheckerTypeVRFV2Plus -} - -func (lsn *listenerV2) processRequestsPerSub( - ctx context.Context, - subID *big.Int, - startLinkBalance *big.Int, - startEthBalance *big.Int, - reqs []pendingRequest, - subIsActive bool, -) map[string]struct{} { - if lsn.job.VRFSpec.BatchFulfillmentEnabled && lsn.batchCoordinator != nil { - return lsn.processRequestsPerSubBatch(ctx, subID, startLinkBalance, startEthBalance, reqs, subIsActive) - } - - var processed = make(map[string]struct{}) - chainId := lsn.chain.Client().ConfiguredChainID() - startBalanceNoReserveLink, err := lsn.MaybeSubtractReservedLink( - ctx, startLinkBalance, chainId, subID, lsn.coordinator.Version()) - if err != nil { - lsn.l.Errorw("Couldn't get reserved LINK for subscription", "sub", reqs[0].req.SubID(), "err", err) - return processed - } - startBalanceNoReserveEth, err := lsn.MaybeSubtractReservedEth( - ctx, startEthBalance, lsn.chainID, subID, lsn.coordinator.Version()) - if err != nil { - lsn.l.Errorw("Couldn't get reserved ETH for subscription", "sub", reqs[0].req.SubID(), "err", err) - return processed - } - - // Split the requests into native and LINK requests. - var ( - nativeRequests []pendingRequest - linkRequests []pendingRequest - ) - for _, req := range reqs { - if req.req.NativePayment() { - nativeRequests = append(nativeRequests, req) - } else { - linkRequests = append(linkRequests, req) - } - } - // process the native and link requests in parallel - var ( - wg sync.WaitGroup - nativeProcessed, linkProcessed map[string]struct{} - ) - wg.Add(2) - go func() { - defer wg.Done() - nativeProcessed = lsn.processRequestsPerSubHelper( - ctx, - subID, - startEthBalance, - startBalanceNoReserveEth, - nativeRequests, - subIsActive, - true) - }() - go func() { - defer wg.Done() - linkProcessed = lsn.processRequestsPerSubHelper( - ctx, - subID, - startLinkBalance, - startBalanceNoReserveLink, - linkRequests, - subIsActive, - false) - }() - wg.Wait() - // combine the native and link processed requests into the processed map - for k, v := range nativeProcessed { - processed[k] = v - } - for k, v := range linkProcessed { - processed[k] = v - } - - return processed -} - -func (lsn *listenerV2) requestCommitmentPayload(requestID *big.Int) (payload []byte, err error) { - if lsn.coordinator.Version() == vrfcommon.V2Plus { - return coordinatorV2PlusABI.Pack("s_requestCommitments", requestID) - } else if lsn.coordinator.Version() == vrfcommon.V2 { - return coordinatorV2ABI.Pack("getCommitment", requestID) - } - return nil, errors.Errorf("unsupported coordinator version: %s", lsn.coordinator.Version()) -} - -// checkReqsFulfilled returns a bool slice the same size of the given reqs slice -// where each slice element indicates whether that request was already fulfilled -// or not. -func (lsn *listenerV2) checkReqsFulfilled(ctx context.Context, l logger.Logger, reqs []pendingRequest) ([]bool, error) { - var ( - start = time.Now() - calls = make([]rpc.BatchElem, len(reqs)) - fulfilled = make([]bool, len(reqs)) - ) - - for i, req := range reqs { - payload, err := lsn.requestCommitmentPayload(req.req.RequestID()) - if err != nil { - // This shouldn't happen - return fulfilled, errors.Wrap(err, "creating getCommitment payload") - } - - reqBlockNumber := new(big.Int).SetUint64(req.req.Raw().BlockNumber) - - // Subtract 5 since the newest block likely isn't indexed yet and will cause "header not - // found" errors. - currBlock := new(big.Int).SetUint64(lsn.getLatestHead() - 5) - m := bigmath.Max(reqBlockNumber, currBlock) - - var result string - calls[i] = rpc.BatchElem{ - Method: "eth_call", - Args: []interface{}{ - map[string]interface{}{ - "to": lsn.coordinator.Address(), - "data": hexutil.Bytes(payload), - }, - // The block at which we want to make the call - hexutil.EncodeBig(m), - }, - Result: &result, - } - } - - err := lsn.chain.Client().BatchCallContext(ctx, calls) - if err != nil { - return fulfilled, errors.Wrap(err, "making batch call") - } - - var errs error - for i, call := range calls { - if call.Error != nil { - errs = multierr.Append(errs, fmt.Errorf("checking request %s with hash %s: %w", - reqs[i].req.RequestID().String(), reqs[i].req.Raw().TxHash.String(), call.Error)) - continue - } - - rString, ok := call.Result.(*string) - if !ok { - errs = multierr.Append(errs, - fmt.Errorf("unexpected result %+v on request %s with hash %s", - call.Result, reqs[i].req.RequestID().String(), reqs[i].req.Raw().TxHash.String())) - continue - } - result, err := hexutil.Decode(*rString) - if err != nil { - errs = multierr.Append(errs, - fmt.Errorf("decoding batch call result %+v %s request %s with hash %s: %w", - call.Result, *rString, reqs[i].req.RequestID().String(), reqs[i].req.Raw().TxHash.String(), err)) - continue - } - - if utils.IsEmpty(result) { - l.Infow("Request already fulfilled", - "reqID", reqs[i].req.RequestID().String(), - "attempts", reqs[i].attempts, - "txHash", reqs[i].req.Raw().TxHash) - fulfilled[i] = true - } - } - - l.Debugw("Done checking fulfillment status", - "numChecked", len(reqs), "time", time.Since(start).String()) - return fulfilled, errs -} - -func (lsn *listenerV2) runPipelines( - ctx context.Context, - l logger.Logger, - maxGasPriceWei *assets.Wei, - reqs []pendingRequest, -) []vrfPipelineResult { - var ( - start = time.Now() - results = make([]vrfPipelineResult, len(reqs)) - wg = sync.WaitGroup{} - ) - - for i, req := range reqs { - wg.Add(1) - go func(i int, req pendingRequest) { - defer wg.Done() - results[i] = lsn.simulateFulfillment(ctx, maxGasPriceWei, req, l) - }(i, req) - } - wg.Wait() - - l.Debugw("Finished running pipelines", - "count", len(reqs), "time", time.Since(start).String()) - return results -} - -func (lsn *listenerV2) estimateFee( - ctx context.Context, - req RandomWordsRequested, - maxGasPriceWei *assets.Wei, -) (*big.Int, error) { - // NativePayment() returns true if and only if the version is V2+ and the - // request was made in ETH. - if req.NativePayment() { - return EstimateFeeWei(req.CallbackGasLimit(), maxGasPriceWei.ToInt()) - } - - // In the event we are using LINK we need to estimate the fee in juels - // Don't use up too much time to get this info, it's not critical for operating vrf. - callCtx, cancel := context.WithTimeout(ctx, 1*time.Second) - defer cancel() - roundData, err := lsn.aggregator.LatestRoundData(&bind.CallOpts{Context: callCtx}) - if err != nil { - return nil, errors.Wrap(err, "get aggregator latestAnswer") - } - - return EstimateFeeJuels( - req.CallbackGasLimit(), - maxGasPriceWei.ToInt(), - roundData.Answer, - ) -} - -// Here we use the pipeline to parse the log, generate a vrf response -// then simulate the transaction at the max gas price to determine its maximum link cost. -func (lsn *listenerV2) simulateFulfillment( - ctx context.Context, - maxGasPriceWei *assets.Wei, - req pendingRequest, - lg logger.Logger, -) vrfPipelineResult { - var ( - res = vrfPipelineResult{req: req} - err error - ) - // estimate how much funds are needed so that we can log it if the simulation fails. - res.fundsNeeded, err = lsn.estimateFee(ctx, req.req, maxGasPriceWei) - if err != nil { - // not critical, just log and continue - lg.Warnw("unable to estimate funds needed for request, continuing anyway", - "reqID", req.req.RequestID(), - "err", err) - res.fundsNeeded = big.NewInt(0) - } - - vars := pipeline.NewVarsFrom(map[string]interface{}{ - "jobSpec": map[string]interface{}{ - "databaseID": lsn.job.ID, - "externalJobID": lsn.job.ExternalJobID, - "name": lsn.job.Name.ValueOrZero(), - "publicKey": lsn.job.VRFSpec.PublicKey[:], - "maxGasPrice": maxGasPriceWei.ToInt().String(), - "evmChainID": lsn.job.VRFSpec.EVMChainID.String(), - }, - "jobRun": map[string]interface{}{ - "logBlockHash": req.req.Raw().BlockHash.Bytes(), - "logBlockNumber": req.req.Raw().BlockNumber, - "logTxHash": req.req.Raw().TxHash, - "logTopics": req.req.Raw().Topics, - "logData": req.req.Raw().Data, - }, - }) - var trrs pipeline.TaskRunResults - res.run, trrs, err = lsn.pipelineRunner.ExecuteRun(ctx, *lsn.job.PipelineSpec, vars, lg) - if err != nil { - res.err = errors.Wrap(err, "executing run") - return res - } - // The call task will fail if there are insufficient funds - if res.run.AllErrors.HasError() { - res.err = errors.WithStack(res.run.AllErrors.ToError()) - - if strings.Contains(res.err.Error(), "blockhash not found in store") { - res.err = multierr.Combine(res.err, errBlockhashNotInStore{}) - } else if strings.Contains(res.err.Error(), "execution reverted") { - // Even if the simulation fails, we want to get the - // txData for the fulfillRandomWords call, in case - // we need to force fulfill. - for _, trr := range trrs { - if trr.Task.Type() == pipeline.TaskTypeVRFV2 { - if trr.Result.Error != nil { - // error in VRF proof generation - // this means that we won't be able to force-fulfill in the event of a - // canceled sub and active requests. - // since this would be an extraordinary situation, - // we can log loudly here. - lg.Criticalw("failed to generate VRF proof", "err", trr.Result.Error) - break - } - - // extract the abi-encoded tx data to fulfillRandomWords from the VRF task. - // that's all we need in the event of a force-fulfillment. - m := trr.Result.Value.(map[string]any) - res.payload = m["output"].(string) - res.proof = FromV2Proof(m["proof"].(vrf_coordinator_v2.VRFProof)) - res.reqCommitment = NewRequestCommitment(m["requestCommitment"]) - } - } - res.err = multierr.Combine(res.err, errPossiblyInsufficientFunds{}) - } - - return res - } - finalResult := trrs.FinalResult(lg) - if len(finalResult.Values) != 1 { - res.err = errors.Errorf("unexpected number of outputs, expected 1, was %d", len(finalResult.Values)) - return res - } - - // Run succeeded, we expect a byte array representing the billing amount - b, ok := finalResult.Values[0].([]uint8) - if !ok { - res.err = errors.New("expected []uint8 final result") - return res - } - res.maxFee = utils.HexToBig(hexutil.Encode(b)[2:]) - for _, trr := range trrs { - if trr.Task.Type() == pipeline.TaskTypeVRFV2 { - m := trr.Result.Value.(map[string]interface{}) - res.payload = m["output"].(string) - res.proof = FromV2Proof(m["proof"].(vrf_coordinator_v2.VRFProof)) - res.reqCommitment = NewRequestCommitment(m["requestCommitment"]) - } - - if trr.Task.Type() == pipeline.TaskTypeVRFV2Plus { - m := trr.Result.Value.(map[string]interface{}) - res.payload = m["output"].(string) - res.proof = FromV2PlusProof(m["proof"].(vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalProof)) - res.reqCommitment = NewRequestCommitment(m["requestCommitment"]) - } - - if trr.Task.Type() == pipeline.TaskTypeEstimateGasLimit { - res.gasLimit = trr.Result.Value.(uint32) - } - } - return res -} - -func (lsn *listenerV2) runRequestHandler(pollPeriod time.Duration, wg *sync.WaitGroup) { - defer wg.Done() - tick := time.NewTicker(pollPeriod) - defer tick.Stop() - ctx, cancel := lsn.chStop.NewCtx() - defer cancel() - for { - select { - case <-lsn.chStop: - return - case <-tick.C: - lsn.processPendingVRFRequests(ctx) - } - } -} - -func (lsn *listenerV2) runLogListener(unsubscribes []func(), minConfs uint32, wg *sync.WaitGroup) { - defer wg.Done() - lsn.l.Infow("Listening for run requests", - "minConfs", minConfs) - for { - select { - case <-lsn.chStop: - for _, f := range unsubscribes { - f() - } - return - case <-lsn.reqLogs.Notify(): - // Process all the logs in the queue if one is added - for { - lb, exists := lsn.reqLogs.Retrieve() - if !exists { - break - } - lsn.handleLog(lb, minConfs) - } - } - } -} - -func (lsn *listenerV2) getConfirmedAt(req RandomWordsRequested, nodeMinConfs uint32) uint64 { - lsn.respCountMu.Lock() - defer lsn.respCountMu.Unlock() - // Take the max(nodeMinConfs, requestedConfs + requestedConfsDelay). - // Add the requested confs delay if provided in the jobspec so that we avoid an edge case - // where the primary and backup VRF v2 nodes submit a proof at the same time. - minConfs := nodeMinConfs - if uint32(req.MinimumRequestConfirmations())+uint32(lsn.job.VRFSpec.RequestedConfsDelay) > nodeMinConfs { - minConfs = uint32(req.MinimumRequestConfirmations()) + uint32(lsn.job.VRFSpec.RequestedConfsDelay) - } - newConfs := uint64(minConfs) * (1 << lsn.respCount[req.RequestID().String()]) - // We cap this at 200 because solidity only supports the most recent 256 blocks - // in the contract so if it was older than that, fulfillments would start failing - // without the blockhash store feeder. We use 200 to give the node plenty of time - // to fulfill even on fast chains. - if newConfs > 200 { - newConfs = 200 - } - if lsn.respCount[req.RequestID().String()] > 0 { - lsn.l.Warnw("Duplicate request found after fulfillment, doubling incoming confirmations", - "txHash", req.Raw().TxHash, - "blockNumber", req.Raw().BlockNumber, - "blockHash", req.Raw().BlockHash, - "reqID", req.RequestID().String(), - "newConfs", newConfs) - vrfcommon.IncDupeReqs(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version()) - } - return req.Raw().BlockNumber + newConfs -} - -func (lsn *listenerV2) handleLog(lb log.Broadcast, minConfs uint32) { - if v, ok := lb.DecodedLog().(*vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled); ok { - lsn.l.Debugw("Received fulfilled log", "reqID", v.RequestId, "success", v.Success) - consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(lb) - if err != nil { - lsn.l.Errorw(CouldNotDetermineIfLogConsumedMsg, "err", err, "txHash", lb.RawLog().TxHash) - return - } else if consumed { - return - } - lsn.respCountMu.Lock() - lsn.respCount[v.RequestId.String()]++ - lsn.respCountMu.Unlock() - lsn.blockNumberToReqID.Insert(fulfilledReqV2{ - blockNumber: v.Raw.BlockNumber, - reqID: v.RequestId.String(), - }) - lsn.markLogAsConsumed(lb) - return - } - - if v, ok := lb.DecodedLog().(*vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalRandomWordsFulfilled); ok { - lsn.l.Debugw("Received fulfilled log", "reqID", v.RequestId, "success", v.Success) - consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(lb) - if err != nil { - lsn.l.Errorw(CouldNotDetermineIfLogConsumedMsg, "err", err, "txHash", lb.RawLog().TxHash) - return - } else if consumed { - return - } - lsn.respCountMu.Lock() - lsn.respCount[v.RequestId.String()]++ - lsn.respCountMu.Unlock() - lsn.blockNumberToReqID.Insert(fulfilledReqV2{ - blockNumber: v.Raw.BlockNumber, - reqID: v.RequestId.String(), - }) - lsn.markLogAsConsumed(lb) - return - } - - req, err := lsn.coordinator.ParseRandomWordsRequested(lb.RawLog()) - if err != nil { - lsn.l.Errorw("Failed to parse log", "err", err, "txHash", lb.RawLog().TxHash) - consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(lb) - if err != nil { - lsn.l.Errorw(CouldNotDetermineIfLogConsumedMsg, "err", err, "txHash", lb.RawLog().TxHash) - return - } else if consumed { - return - } - lsn.markLogAsConsumed(lb) - return - } - - confirmedAt := lsn.getConfirmedAt(req, minConfs) - lsn.l.Infow("VRFListenerV2: Received log request", "reqID", req.RequestID(), "confirmedAt", confirmedAt, "subID", req.SubID(), "sender", req.Sender()) - lsn.reqsMu.Lock() - lsn.reqs = append(lsn.reqs, pendingRequest{ - confirmedAtBlock: confirmedAt, - req: req, - lb: lb, - utcTimestamp: time.Now().UTC(), - }) - lsn.reqAdded() - lsn.reqsMu.Unlock() -} - -func (lsn *listenerV2) markLogAsConsumed(lb log.Broadcast) { - err := lsn.chain.LogBroadcaster().MarkConsumed(lb) - lsn.l.ErrorIf(err, fmt.Sprintf("Unable to mark log %v as consumed", lb.String())) + return lsn.latestHeadNumber } // Close complies with job.Service func (lsn *listenerV2) Close() error { return lsn.StopOnce("VRFListenerV2", func() error { close(lsn.chStop) - // wait on the request handler, log listener, and head listener to stop + // wait on the request handler, log listener lsn.wg.Wait() - return lsn.reqLogs.Close() + return nil }) } - -func (lsn *listenerV2) HandleLog(lb log.Broadcast) { - if !lsn.deduper.ShouldDeliver(lb.RawLog()) { - lsn.l.Tracew("skipping duplicate log broadcast", "log", lb.RawLog()) - return - } - - wasOverCapacity := lsn.reqLogs.Deliver(lb) - if wasOverCapacity { - lsn.l.Error("Log mailbox is over capacity - dropped the oldest log") - vrfcommon.IncDroppedReqs(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), vrfcommon.ReasonMailboxSize) - } -} - -// JobID complies with log.Listener -func (lsn *listenerV2) JobID() int32 { - return lsn.job.ID -} - -// ReplayStartedCallback is called by the log broadcaster when a replay is about to start. -func (lsn *listenerV2) ReplayStartedCallback() { - // Clear the log deduper cache so that we don't incorrectly ignore logs that have been sent that - // are already in the cache. - lsn.deduper.Clear() -} - -func (lsn *listenerV2) fromAddresses() []common.Address { - var addresses []common.Address - for _, a := range lsn.job.VRFSpec.FromAddresses { - addresses = append(addresses, a.Address()) - } - return addresses -} - -func uniqueReqs(reqs []pendingRequest) int { - s := map[string]struct{}{} - for _, r := range reqs { - s[r.req.RequestID().String()] = struct{}{} - } - return len(s) -} - -// GasProofVerification is an upper limit on the gas used for verifying the VRF proof on-chain. -// It can be used to estimate the amount of LINK or native needed to fulfill a request. -const GasProofVerification uint32 = 200_000 - -// EstimateFeeJuels estimates the amount of link needed to fulfill a request -// given the callback gas limit, the gas price, and the wei per unit link. -// An error is returned if the wei per unit link provided is zero. -func EstimateFeeJuels(callbackGasLimit uint32, maxGasPriceWei, weiPerUnitLink *big.Int) (*big.Int, error) { - if weiPerUnitLink.Cmp(big.NewInt(0)) == 0 { - return nil, errors.New("wei per unit link is zero") - } - maxGasUsed := big.NewInt(int64(callbackGasLimit + GasProofVerification)) - costWei := maxGasUsed.Mul(maxGasUsed, maxGasPriceWei) - // Multiply by 1e18 first so that we don't lose a ton of digits due to truncation when we divide - // by weiPerUnitLink - numerator := costWei.Mul(costWei, big.NewInt(1e18)) - costJuels := numerator.Quo(numerator, weiPerUnitLink) - return costJuels, nil -} - -// EstimateFeeWei estimates the amount of wei needed to fulfill a request -func EstimateFeeWei(callbackGasLimit uint32, maxGasPriceWei *big.Int) (*big.Int, error) { - maxGasUsed := big.NewInt(int64(callbackGasLimit + GasProofVerification)) - costWei := maxGasUsed.Mul(maxGasUsed, maxGasPriceWei) - return costWei, nil -} - -// observeRequestSimDuration records the time between the given requests simulations or -// the time until it's first simulation, whichever is applicable. -// Cases: -// 1. Never simulated: in this case, we want to observe the time until simulated -// on the utcTimestamp field of the pending request. -// 2. Simulated before: in this case, lastTry will be set to a non-zero time value, -// in which case we'd want to use that as a relative point from when we last tried -// the request. -func observeRequestSimDuration(jobName string, extJobID uuid.UUID, vrfVersion vrfcommon.Version, pendingReqs []pendingRequest) { - now := time.Now().UTC() - for _, request := range pendingReqs { - // First time around lastTry will be zero because the request has not been - // simulated yet. It will be updated every time the request is simulated (in the event - // the request is simulated multiple times, due to it being underfunded). - if request.lastTry.IsZero() { - vrfcommon.MetricTimeUntilInitialSim. - WithLabelValues(jobName, extJobID.String(), string(vrfVersion)). - Observe(float64(now.Sub(request.utcTimestamp))) - } else { - vrfcommon.MetricTimeBetweenSims. - WithLabelValues(jobName, extJobID.String(), string(vrfVersion)). - Observe(float64(now.Sub(request.lastTry))) - } - } -} - -func ptr[T any](t T) *T { return &t } diff --git a/core/services/vrf/v2/listener_v2_helpers.go b/core/services/vrf/v2/listener_v2_helpers.go new file mode 100644 index 00000000000..b3a3675e296 --- /dev/null +++ b/core/services/vrf/v2/listener_v2_helpers.go @@ -0,0 +1,103 @@ +package v2 + +import ( + "math/big" + "strings" + "time" + + "github.com/google/uuid" + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" +) + +func uniqueReqs(reqs []pendingRequest) int { + s := map[string]struct{}{} + for _, r := range reqs { + s[r.req.RequestID().String()] = struct{}{} + } + return len(s) +} + +// GasProofVerification is an upper limit on the gas used for verifying the VRF proof on-chain. +// It can be used to estimate the amount of LINK or native needed to fulfill a request. +const GasProofVerification uint32 = 200_000 + +// EstimateFeeJuels estimates the amount of link needed to fulfill a request +// given the callback gas limit, the gas price, and the wei per unit link. +// An error is returned if the wei per unit link provided is zero. +func EstimateFeeJuels(callbackGasLimit uint32, maxGasPriceWei, weiPerUnitLink *big.Int) (*big.Int, error) { + if weiPerUnitLink.Cmp(big.NewInt(0)) == 0 { + return nil, errors.New("wei per unit link is zero") + } + maxGasUsed := big.NewInt(int64(callbackGasLimit + GasProofVerification)) + costWei := maxGasUsed.Mul(maxGasUsed, maxGasPriceWei) + // Multiply by 1e18 first so that we don't lose a ton of digits due to truncation when we divide + // by weiPerUnitLink + numerator := costWei.Mul(costWei, big.NewInt(1e18)) + costJuels := numerator.Quo(numerator, weiPerUnitLink) + return costJuels, nil +} + +// EstimateFeeWei estimates the amount of wei needed to fulfill a request +func EstimateFeeWei(callbackGasLimit uint32, maxGasPriceWei *big.Int) (*big.Int, error) { + maxGasUsed := big.NewInt(int64(callbackGasLimit + GasProofVerification)) + costWei := maxGasUsed.Mul(maxGasUsed, maxGasPriceWei) + return costWei, nil +} + +// observeRequestSimDuration records the time between the given requests simulations or +// the time until it's first simulation, whichever is applicable. +// Cases: +// 1. Never simulated: in this case, we want to observe the time until simulated +// on the utcTimestamp field of the pending request. +// 2. Simulated before: in this case, lastTry will be set to a non-zero time value, +// in which case we'd want to use that as a relative point from when we last tried +// the request. +func observeRequestSimDuration(jobName string, extJobID uuid.UUID, vrfVersion vrfcommon.Version, pendingReqs []pendingRequest) { + now := time.Now().UTC() + for _, request := range pendingReqs { + // First time around lastTry will be zero because the request has not been + // simulated yet. It will be updated every time the request is simulated (in the event + // the request is simulated multiple times, due to it being underfunded). + if request.lastTry.IsZero() { + vrfcommon.MetricTimeUntilInitialSim. + WithLabelValues(jobName, extJobID.String(), string(vrfVersion)). + Observe(float64(now.Sub(request.utcTimestamp))) + } else { + vrfcommon.MetricTimeBetweenSims. + WithLabelValues(jobName, extJobID.String(), string(vrfVersion)). + Observe(float64(now.Sub(request.lastTry))) + } + } +} + +func ptr[T any](t T) *T { return &t } + +func isProofVerificationError(errMsg string) bool { + // See VRF.sol for all these messages + // NOTE: it's unclear which of these errors are impossible and which + // may actually happen, so including them all to be safe. + errMessages := []string{ + "invalid x-ordinate", + "invalid y-ordinate", + "zero scalar", + "invZ must be inverse of z", + "bad witness", + "points in sum must be distinct", + "First mul check failed", + "Second mul check failed", + "public key is not on curve", + "gamma is not on curve", + "cGammaWitness is not on curve", + "sHashWitness is not on curve", + "addr(c*pk+s*g)!=_uWitness", + "invalid proof", + } + for _, msg := range errMessages { + if strings.Contains(errMsg, msg) { + return true + } + } + return false +} diff --git a/core/services/vrf/v2/listener_v2_log_listener.go b/core/services/vrf/v2/listener_v2_log_listener.go new file mode 100644 index 00000000000..b35593bd1ca --- /dev/null +++ b/core/services/vrf/v2/listener_v2_log_listener.go @@ -0,0 +1,451 @@ +package v2 + +import ( + "bytes" + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "go.uber.org/multierr" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" + "github.com/smartcontractkit/chainlink/v2/core/utils/mathutil" +) + +func (lsn *listenerV2) runLogListener( + pollPeriod time.Duration, + minConfs uint32, +) { + lsn.l.Infow("Listening for run requests via log poller", + "minConfs", minConfs) + ticker := time.NewTicker(pollPeriod) + defer ticker.Stop() + var ( + lastProcessedBlock int64 + startingUp = true + ) + ctx, cancel := lsn.chStop.NewCtx() + defer cancel() + for { + select { + case <-lsn.chStop: + return + case <-ticker.C: + start := time.Now() + lsn.l.Debugw("log listener loop") + // Filter registration is idempotent, so we can just call it every time + // and retry on errors using the ticker. + err := lsn.chain.LogPoller().RegisterFilter(logpoller.Filter{ + Name: logpoller.FilterName( + "VRFListener", + "version", lsn.coordinator.Version(), + "keyhash", lsn.job.VRFSpec.PublicKey.MustHash(), + "coordinatorAddress", lsn.coordinator.Address()), + EventSigs: evmtypes.HashArray{ + lsn.coordinator.RandomWordsFulfilledTopic(), + lsn.coordinator.RandomWordsRequestedTopic(), + }, + Addresses: evmtypes.AddressArray{ + lsn.coordinator.Address(), + }, + }) + if err != nil { + lsn.l.Errorw("error registering filter in log poller, retrying", + "err", err, + "elapsed", time.Since(start)) + continue + } + + // on startup we want to initialize the last processed block + if startingUp { + lsn.l.Debugw("initializing last processed block on startup") + lastProcessedBlock, err = lsn.initializeLastProcessedBlock(ctx) + if err != nil { + lsn.l.Errorw("error initializing last processed block, retrying", + "err", err, + "elapsed", time.Since(start)) + continue + } + startingUp = false + lsn.l.Debugw("initialized last processed block", "lastProcessedBlock", lastProcessedBlock) + } + + pending, err := lsn.pollLogs(ctx, minConfs, lastProcessedBlock) + if err != nil { + lsn.l.Errorw("error polling vrf logs, retrying", + "err", err, + "elapsed", time.Since(start)) + continue + } + + // process pending requests and insert any fulfillments into the inflight cache + lsn.processPendingVRFRequests(ctx, pending) + + lastProcessedBlock, err = lsn.updateLastProcessedBlock(ctx, lastProcessedBlock) + if err != nil { + lsn.l.Errorw("error updating last processed block, continuing anyway", "err", err) + } else { + lsn.l.Debugw("updated last processed block", "lastProcessedBlock", lastProcessedBlock) + } + lsn.l.Debugw("log listener loop done", "elapsed", time.Since(start)) + } + } +} + +// initializeLastProcessedBlock returns the earliest block number that we need to +// process requests for. This is the block number of the earliest unfulfilled request +// or the latest finalized block, if there are no unfulfilled requests. +// TODO: add tests +func (lsn *listenerV2) initializeLastProcessedBlock(ctx context.Context) (lastProcessedBlock int64, err error) { + lp := lsn.chain.LogPoller() + start := time.Now() + + // will retry on error in the runLogListener loop + latestBlock, err := lp.LatestBlock() + if err != nil { + return 0, fmt.Errorf("LogPoller.LatestBlock(): %w", err) + } + fromTimestamp := time.Now().UTC().Add(-lsn.job.VRFSpec.RequestTimeout) + ll := lsn.l.With( + "latestFinalizedBlock", latestBlock.FinalizedBlockNumber, + "latestBlock", latestBlock.BlockNumber, + "fromTimestamp", fromTimestamp) + ll.Debugw("Initializing last processed block") + defer func() { + ll.Debugw("Done initializing last processed block", "elapsed", time.Since(start)) + }() + + numBlocksToReplay := numReplayBlocks(lsn.job.VRFSpec.RequestTimeout, lsn.chain.ID()) + ll.Debugw("running replay on log poller") + err = lp.Replay(ctx, mathutil.Max(latestBlock.FinalizedBlockNumber-numBlocksToReplay, 1)) + if err != nil { + return 0, fmt.Errorf("LogPoller.Replay: %w", err) + } + + // get randomness requested logs with the appropriate keyhash + // keyhash is specified in topic1 + requests, err := lp.IndexedLogsCreatedAfter( + lsn.coordinator.RandomWordsRequestedTopic(), // event sig + lsn.coordinator.Address(), // address + 1, // topic index + []common.Hash{lsn.job.VRFSpec.PublicKey.MustHash()}, // topic values + fromTimestamp, // from time + logpoller.Finalized, // confs + ) + if err != nil { + return 0, fmt.Errorf("LogPoller.LogsCreatedAfter RandomWordsRequested logs: %w", err) + } + + // fulfillments don't have keyhash indexed, we'll have to get all of them + // TODO: can we instead write a single query that joins on request id's somehow? + fulfillments, err := lp.LogsCreatedAfter( + lsn.coordinator.RandomWordsFulfilledTopic(), // event sig + lsn.coordinator.Address(), // address + fromTimestamp, // from time + logpoller.Finalized, // confs + ) + if err != nil { + return 0, fmt.Errorf("LogPoller.LogsCreatedAfter RandomWordsFulfilled logs: %w", err) + } + + unfulfilled, _, _ := lsn.getUnfulfilled(append(requests, fulfillments...), ll) + // find request block of earliest unfulfilled request + // even if this block is > latest finalized, we use latest finalized as earliest unprocessed + // because re-orgs can occur on any unfinalized block. + var earliestUnfulfilledBlock = latestBlock.FinalizedBlockNumber + for _, req := range unfulfilled { + if req.Raw().BlockNumber < uint64(earliestUnfulfilledBlock) { + earliestUnfulfilledBlock = int64(req.Raw().BlockNumber) + } + } + + return earliestUnfulfilledBlock, nil +} + +func (lsn *listenerV2) updateLastProcessedBlock(ctx context.Context, currLastProcessedBlock int64) (lastProcessedBlock int64, err error) { + lp := lsn.chain.LogPoller() + start := time.Now() + + latestBlock, err := lp.LatestBlock(pg.WithParentCtx(ctx)) + if err != nil { + lsn.l.Errorw("error getting latest block", "err", err) + return 0, fmt.Errorf("LogPoller.LatestBlock(): %w", err) + } + ll := lsn.l.With( + "currLastProcessedBlock", currLastProcessedBlock, + "latestBlock", latestBlock.BlockNumber, + "latestFinalizedBlock", latestBlock.FinalizedBlockNumber) + ll.Debugw("updating last processed block") + defer func() { + ll.Debugw("done updating last processed block", "elapsed", time.Since(start)) + }() + + logs, err := lp.LogsWithSigs( + currLastProcessedBlock, + latestBlock.FinalizedBlockNumber, + []common.Hash{lsn.coordinator.RandomWordsFulfilledTopic(), lsn.coordinator.RandomWordsRequestedTopic()}, + lsn.coordinator.Address(), + pg.WithParentCtx(ctx), + ) + if err != nil { + return currLastProcessedBlock, fmt.Errorf("LogPoller.LogsWithSigs: %w", err) + } + + unfulfilled, unfulfilledLP, _ := lsn.getUnfulfilled(logs, ll) + // find request block of earliest unfulfilled request + // even if this block is > latest finalized, we use latest finalized as earliest unprocessed + // because re-orgs can occur on any unfinalized block. + var earliestUnprocessedRequestBlock = latestBlock.FinalizedBlockNumber + for i, req := range unfulfilled { + // need to drop requests that have timed out otherwise the earliestUnprocessedRequestBlock + // will be unnecessarily far back and our queries will be slower. + if unfulfilledLP[i].CreatedAt.Before(time.Now().UTC().Add(-lsn.job.VRFSpec.RequestTimeout)) { + // request timed out, don't process + lsn.l.Debugw("request timed out, skipping", + "reqID", req.RequestID(), + ) + continue + } + if req.Raw().BlockNumber < uint64(earliestUnprocessedRequestBlock) { + earliestUnprocessedRequestBlock = int64(req.Raw().BlockNumber) + } + } + + return earliestUnprocessedRequestBlock, nil +} + +// pollLogs uses the log poller to poll for the latest VRF logs +func (lsn *listenerV2) pollLogs(ctx context.Context, minConfs uint32, lastProcessedBlock int64) (pending []pendingRequest, err error) { + start := time.Now() + lp := lsn.chain.LogPoller() + + // latest unfinalized block used on purpose to get bleeding edge logs + // we don't really have the luxury to wait for finalization on most chains + // if we want to fulfill on time. + latestBlock, err := lp.LatestBlock() + if err != nil { + return nil, fmt.Errorf("LogPoller.LatestBlock(): %w", err) + } + lsn.setLatestHead(latestBlock) + ll := lsn.l.With( + "lastProcessedBlock", lastProcessedBlock, + "minConfs", minConfs, + "latestBlock", latestBlock.BlockNumber, + "latestFinalizedBlock", latestBlock.FinalizedBlockNumber) + ll.Debugw("polling for logs") + defer func() { + ll.Debugw("done polling for logs", "elapsed", time.Since(start)) + }() + + // We don't specify confs because each request can have a different conf above + // the minimum. So we do all conf handling in getConfirmedAt. + logs, err := lp.LogsWithSigs( + lastProcessedBlock, + latestBlock.BlockNumber, + []common.Hash{lsn.coordinator.RandomWordsFulfilledTopic(), lsn.coordinator.RandomWordsRequestedTopic()}, + lsn.coordinator.Address(), + pg.WithParentCtx(ctx), + ) + if err != nil { + return nil, fmt.Errorf("LogPoller.LogsWithSigs: %w", err) + } + + unfulfilled, unfulfilledLP, fulfilled := lsn.getUnfulfilled(logs, ll) + if len(unfulfilled) > 0 { + ll.Debugw("found unfulfilled logs", "unfulfilled", len(unfulfilled)) + } else { + ll.Debugw("no unfulfilled logs found") + } + + lsn.handleFulfilled(fulfilled) + + return lsn.handleRequested(unfulfilled, unfulfilledLP, minConfs), nil +} + +func (lsn *listenerV2) getUnfulfilled(logs []logpoller.Log, ll logger.Logger) (unfulfilled []RandomWordsRequested, unfulfilledLP []logpoller.Log, fulfilled map[string]RandomWordsFulfilled) { + var ( + requested = make(map[string]RandomWordsRequested) + requestedLP = make(map[string]logpoller.Log) + errs error + expectedKeyHash = lsn.job.VRFSpec.PublicKey.MustHash() + ) + fulfilled = make(map[string]RandomWordsFulfilled) + for _, l := range logs { + if l.EventSig == lsn.coordinator.RandomWordsFulfilledTopic() { + parsed, err2 := lsn.coordinator.ParseRandomWordsFulfilled(l.ToGethLog()) + if err2 != nil { + // should never happen + errs = multierr.Append(errs, err2) + continue + } + fulfilled[parsed.RequestID().String()] = parsed + } else if l.EventSig == lsn.coordinator.RandomWordsRequestedTopic() { + parsed, err2 := lsn.coordinator.ParseRandomWordsRequested(l.ToGethLog()) + if err2 != nil { + // should never happen + errs = multierr.Append(errs, err2) + continue + } + keyHash := parsed.KeyHash() + if !bytes.Equal(keyHash[:], expectedKeyHash[:]) { + // wrong keyhash, can ignore + continue + } + requested[parsed.RequestID().String()] = parsed + requestedLP[parsed.RequestID().String()] = l + } + } + // should never happen, unsure if recoverable + // may be worth a panic + if errs != nil { + ll.Errorw("encountered parse errors", "err", errs) + } + + if len(fulfilled) > 0 || len(requested) > 0 { + ll.Infow("found logs", "fulfilled", len(fulfilled), "requested", len(requested)) + } else { + ll.Debugw("no logs found") + } + + // find unfulfilled requests by comparing requested events with the fulfilled events + for reqID, req := range requested { + if _, isFulfilled := fulfilled[reqID]; !isFulfilled { + unfulfilled = append(unfulfilled, req) + unfulfilledLP = append(unfulfilledLP, requestedLP[reqID]) + } + } + + return unfulfilled, unfulfilledLP, fulfilled +} + +func (lsn *listenerV2) getConfirmedAt(req RandomWordsRequested, nodeMinConfs uint32) uint64 { + // Take the max(nodeMinConfs, requestedConfs + requestedConfsDelay). + // Add the requested confs delay if provided in the jobspec so that we avoid an edge case + // where the primary and backup VRF v2 nodes submit a proof at the same time. + minConfs := nodeMinConfs + if uint32(req.MinimumRequestConfirmations())+uint32(lsn.job.VRFSpec.RequestedConfsDelay) > nodeMinConfs { + minConfs = uint32(req.MinimumRequestConfirmations()) + uint32(lsn.job.VRFSpec.RequestedConfsDelay) + } + newConfs := uint64(minConfs) * (1 << lsn.respCount[req.RequestID().String()]) + // We cap this at 200 because solidity only supports the most recent 256 blocks + // in the contract so if it was older than that, fulfillments would start failing + // without the blockhash store feeder. We use 200 to give the node plenty of time + // to fulfill even on fast chains. + if newConfs > 200 { + newConfs = 200 + } + if lsn.respCount[req.RequestID().String()] > 0 { + lsn.l.Warnw("Duplicate request found after fulfillment, doubling incoming confirmations", + "txHash", req.Raw().TxHash, + "blockNumber", req.Raw().BlockNumber, + "blockHash", req.Raw().BlockHash, + "reqID", req.RequestID().String(), + "newConfs", newConfs) + vrfcommon.IncDupeReqs(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version()) + } + return req.Raw().BlockNumber + newConfs +} + +func (lsn *listenerV2) handleFulfilled(fulfilled map[string]RandomWordsFulfilled) { + for _, v := range fulfilled { + // don't process same log over again + // log key includes block number and blockhash, so on re-orgs it would return true + // and we would re-process the re-orged request. + if !lsn.fulfillmentLogDeduper.ShouldDeliver(v.Raw()) { + continue + } + lsn.l.Debugw("Received fulfilled log", "reqID", v.RequestID(), "success", v.Success()) + lsn.respCount[v.RequestID().String()]++ + lsn.blockNumberToReqID.Insert(fulfilledReqV2{ + blockNumber: v.Raw().BlockNumber, + reqID: v.RequestID().String(), + }) + } +} + +func (lsn *listenerV2) handleRequested(requested []RandomWordsRequested, requestedLP []logpoller.Log, minConfs uint32) (pendingRequests []pendingRequest) { + for i, req := range requested { + // don't process same log over again + // log key includes block number and blockhash, so on re-orgs it would return true + // and we would re-process the re-orged request. + if lsn.inflightCache.Contains(req.Raw()) { + continue + } + + confirmedAt := lsn.getConfirmedAt(req, minConfs) + lsn.l.Debugw("VRFListenerV2: Received log request", + "reqID", req.RequestID(), + "reqBlockNumber", req.Raw().BlockNumber, + "reqBlockHash", req.Raw().BlockHash, + "reqTxHash", req.Raw().TxHash, + "confirmedAt", confirmedAt, + "subID", req.SubID(), + "sender", req.Sender()) + pendingRequests = append(pendingRequests, pendingRequest{ + confirmedAtBlock: confirmedAt, + req: req, + utcTimestamp: requestedLP[i].CreatedAt.UTC(), + }) + lsn.reqAdded() + } + + return pendingRequests +} + +// numReplayBlocks returns the number of blocks to replay on startup +// given the request timeout and the chain ID. +// if the chain ID is not recognized it assumes a block time of 1 second +// and returns the number of blocks in a day. +func numReplayBlocks(requestTimeout time.Duration, chainID *big.Int) int64 { + var timeoutSeconds = int64(requestTimeout.Seconds()) + switch chainID.String() { + case "1": // eth mainnet + case "3": // eth ropsten + case "4": // eth rinkeby + case "5": // eth goerli + case "11155111": // eth sepolia + // block time is 12s + return timeoutSeconds / 12 + case "137": // polygon mainnet + case "80001": // polygon mumbai + // block time is 2s + return timeoutSeconds / 2 + case "56": // bsc mainnet + case "97": // bsc testnet + // block time is 2s + return timeoutSeconds / 2 + case "43114": // avalanche mainnet + case "43113": // avalanche fuji + // block time is 1s + return timeoutSeconds + case "250": // fantom mainnet + case "4002": // fantom testnet + // block time is 1s + return timeoutSeconds + case "42161": // arbitrum mainnet + case "421613": // arbitrum goerli + case "421614": // arbitrum sepolia + // block time is 0.25s in the worst case + return timeoutSeconds * 4 + case "10": // optimism mainnet + case "69": // optimism kovan + case "420": // optimism goerli + case "11155420": // optimism sepolia + case "8453": // base mainnet + case "84531": // base goerli + case "84532": // base sepolia + // block time is 2s + return timeoutSeconds / 2 + default: + // assume block time of 1s + return timeoutSeconds + } + // assume block time of 1s + return timeoutSeconds +} diff --git a/core/services/vrf/v2/listener_v2_log_processor.go b/core/services/vrf/v2/listener_v2_log_processor.go new file mode 100644 index 00000000000..004ab4c4905 --- /dev/null +++ b/core/services/vrf/v2/listener_v2_log_processor.go @@ -0,0 +1,1214 @@ +package v2 + +import ( + "cmp" + "context" + "database/sql" + "fmt" + "math" + "math/big" + "slices" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" + "go.uber.org/multierr" + + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" + "github.com/smartcontractkit/chainlink/v2/core/utils" + bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" +) + +// Returns all the confirmed logs from the provided pending queue by subscription +func (lsn *listenerV2) getConfirmedLogsBySub(latestHead uint64, pendingRequests []pendingRequest) map[string][]pendingRequest { + vrfcommon.UpdateQueueSize(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), uniqueReqs(pendingRequests)) + var toProcess = make(map[string][]pendingRequest) + for _, request := range pendingRequests { + if lsn.ready(request, latestHead) { + toProcess[request.req.SubID().String()] = append(toProcess[request.req.SubID().String()], request) + } + } + return toProcess +} + +func (lsn *listenerV2) ready(req pendingRequest, latestHead uint64) bool { + // Request is not eligible for fulfillment yet + if req.confirmedAtBlock > latestHead { + return false + } + + if lsn.job.VRFSpec.BackoffInitialDelay == 0 || req.attempts == 0 { + // Backoff is disabled, or this is the first try + return true + } + + return time.Now().UTC().After( + nextTry( + req.attempts, + lsn.job.VRFSpec.BackoffInitialDelay, + lsn.job.VRFSpec.BackoffMaxDelay, + req.lastTry)) +} + +func nextTry(retries int, initial, max time.Duration, last time.Time) time.Time { + expBackoffFactor := math.Pow(backoffFactor, float64(retries-1)) + + var delay time.Duration + if expBackoffFactor > float64(max/initial) { + delay = max + } else { + delay = time.Duration(float64(initial) * expBackoffFactor) + } + return last.Add(delay) +} + +// Remove all entries 10000 blocks or older +// to avoid a memory leak. +func (lsn *listenerV2) pruneConfirmedRequestCounts() { + min := lsn.blockNumberToReqID.FindMin() + for min != nil { + m := min.(fulfilledReqV2) + if m.blockNumber > (lsn.getLatestHead() - 10000) { + break + } + delete(lsn.respCount, m.reqID) + lsn.blockNumberToReqID.DeleteMin() + min = lsn.blockNumberToReqID.FindMin() + } +} + +// Determine a set of logs that are confirmed +// and the subscription has sufficient balance to fulfill, +// given a eth call with the max gas price. +// Note we have to consider the pending reqs already in the txm as already "spent" link or native, +// using a max link or max native consumed in their metadata. +// A user will need a minBalance capable of fulfilling a single req at the max gas price or nothing will happen. +// This is acceptable as users can choose different keyhashes which have different max gas prices. +// Other variables which can change the bill amount between our eth call simulation and tx execution: +// - Link/eth price fluctuation +// - Falling back to BHS +// However the likelihood is vanishingly small as +// 1) the window between simulation and tx execution is tiny. +// 2) the max gas price provides a very large buffer most of the time. +// Its easier to optimistically assume it will go though and in the rare case of a reversion +// we simply retry TODO: follow up where if we see a fulfillment revert, return log to the queue. +func (lsn *listenerV2) processPendingVRFRequests(ctx context.Context, pendingRequests []pendingRequest) { + confirmed := lsn.getConfirmedLogsBySub(lsn.getLatestHead(), pendingRequests) + var processedMu sync.Mutex + processed := make(map[string]struct{}) + start := time.Now() + + defer func() { + for _, subReqs := range confirmed { + for _, req := range subReqs { + if _, ok := processed[req.req.RequestID().String()]; ok { + // add to the inflight cache so that we don't re-process this request + lsn.inflightCache.Add(req.req.Raw()) + } + } + } + lsn.l.Infow("Finished processing pending requests", + "totalProcessed", len(processed), + "totalFailed", len(pendingRequests)-len(processed), + "total", len(pendingRequests), + "time", time.Since(start).String(), + "inflightCacheSize", lsn.inflightCache.Size()) + }() + + if len(confirmed) == 0 { + lsn.l.Infow("No pending requests ready for processing") + return + } + for subID, reqs := range confirmed { + l := lsn.l.With("subID", subID, "startTime", time.Now(), "numReqsForSub", len(reqs)) + // Get the balance of the subscription and also it's active status. + // The reason we need both is that we cannot determine if a subscription + // is active solely by it's balance, since an active subscription could legitimately + // have a zero balance. + var ( + startLinkBalance *big.Int + startEthBalance *big.Int + subIsActive bool + ) + sID, ok := new(big.Int).SetString(subID, 10) + if !ok { + l.Criticalw("Unable to convert %s to Int", subID) + return + } + sub, err := lsn.coordinator.GetSubscription(&bind.CallOpts{ + Context: ctx}, sID) + + if err != nil { + if strings.Contains(err.Error(), "execution reverted") { + // "execution reverted" indicates that the subscription no longer exists. + // We can no longer just mark these as processed and continue, + // since it could be that the subscription was canceled while there + // were still unfulfilled requests. + // The simplest approach to handle this is to enter the processRequestsPerSub + // loop rather than create a bunch of largely duplicated code + // to handle this specific situation, since we need to run the pipeline to get + // the VRF proof, abi-encode it, etc. + l.Warnw("Subscription not found - setting start balance to zero", "subID", subID, "err", err) + startLinkBalance = big.NewInt(0) + } else { + // Most likely this is an RPC error, so we re-try later. + l.Errorw("Unable to read subscription balance", "err", err) + return + } + } else { + // Happy path - sub is active. + startLinkBalance = sub.Balance() + if sub.Version() == vrfcommon.V2Plus { + startEthBalance = sub.NativeBalance() + } + subIsActive = true + } + + // Sort requests in ascending order by CallbackGasLimit + // so that we process the "cheapest" requests for each subscription + // first. This allows us to break out of the processing loop as early as possible + // in the event that a subscription is too underfunded to have it's + // requests processed. + slices.SortFunc(reqs, func(a, b pendingRequest) int { + return cmp.Compare(a.req.CallbackGasLimit(), b.req.CallbackGasLimit()) + }) + + p := lsn.processRequestsPerSub(ctx, sID, startLinkBalance, startEthBalance, reqs, subIsActive) + processedMu.Lock() + for reqID := range p { + processed[reqID] = struct{}{} + } + processedMu.Unlock() + } + lsn.pruneConfirmedRequestCounts() +} + +// MaybeSubtractReservedLink figures out how much LINK is reserved for other VRF requests that +// have not been fully confirmed yet on-chain, and subtracts that from the given startBalance, +// and returns that value if there are no errors. +func (lsn *listenerV2) MaybeSubtractReservedLink(ctx context.Context, startBalance *big.Int, chainID *big.Int, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { + var metaField string + if vrfVersion == vrfcommon.V2Plus { + metaField = txMetaGlobalSubId + } else if vrfVersion == vrfcommon.V2 { + metaField = txMetaFieldSubId + } else { + return nil, errors.Errorf("unsupported vrf version %s", vrfVersion) + } + + txes, err := lsn.chain.TxManager().FindTxesByMetaFieldAndStates(ctx, metaField, subID.String(), reserveEthLinkQueryStates, chainID) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("TXM FindTxesByMetaFieldAndStates failed: %w", err) + } + + reservedLinkSum := big.NewInt(0) + // Aggregate non-null MaxLink from all txes returned + for _, tx := range txes { + var meta *txmgrtypes.TxMeta[common.Address, common.Hash] + meta, err = tx.GetMeta() + if err != nil { + return nil, fmt.Errorf("GetMeta for Tx failed: %w", err) + } + if meta != nil && meta.MaxLink != nil { + txMaxLink, success := new(big.Int).SetString(*meta.MaxLink, 10) + if !success { + return nil, fmt.Errorf("converting reserved LINK %s", *meta.MaxLink) + } + + reservedLinkSum.Add(reservedLinkSum, txMaxLink) + } + } + + return new(big.Int).Sub(startBalance, reservedLinkSum), nil +} + +// MaybeSubtractReservedEth figures out how much ether is reserved for other VRF requests that +// have not been fully confirmed yet on-chain, and subtracts that from the given startBalance, +// and returns that value if there are no errors. +func (lsn *listenerV2) MaybeSubtractReservedEth(ctx context.Context, startBalance *big.Int, chainID *big.Int, subID *big.Int, vrfVersion vrfcommon.Version) (*big.Int, error) { + var metaField string + if vrfVersion == vrfcommon.V2Plus { + metaField = txMetaGlobalSubId + } else if vrfVersion == vrfcommon.V2 { + // native payment is not supported for v2, so returning 0 reserved ETH + return big.NewInt(0), nil + } else { + return nil, errors.Errorf("unsupported vrf version %s", vrfVersion) + } + txes, err := lsn.chain.TxManager().FindTxesByMetaFieldAndStates(ctx, metaField, subID.String(), reserveEthLinkQueryStates, chainID) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("TXM FindTxesByMetaFieldAndStates failed: %w", err) + } + + reservedEthSum := big.NewInt(0) + // Aggregate non-null MaxEth from all txes returned + for _, tx := range txes { + var meta *txmgrtypes.TxMeta[common.Address, common.Hash] + meta, err = tx.GetMeta() + if err != nil { + return nil, fmt.Errorf("GetMeta for Tx failed: %w", err) + } + if meta != nil && meta.MaxEth != nil { + txMaxEth, success := new(big.Int).SetString(*meta.MaxEth, 10) + if !success { + return nil, fmt.Errorf("converting reserved ETH %s", *meta.MaxEth) + } + + reservedEthSum.Add(reservedEthSum, txMaxEth) + } + } + + if startBalance != nil { + return new(big.Int).Sub(startBalance, reservedEthSum), nil + } + return big.NewInt(0), nil +} + +func (lsn *listenerV2) processRequestsPerSubBatchHelper( + ctx context.Context, + subID *big.Int, + startBalance *big.Int, + startBalanceNoReserved *big.Int, + reqs []pendingRequest, + subIsActive bool, + nativePayment bool, +) (processed map[string]struct{}) { + start := time.Now() + processed = make(map[string]struct{}) + + // Base the max gas for a batch on the max gas limit for a single callback. + // Since the max gas limit for a single callback is usually quite large already, + // we probably don't want to exceed it too much so that we can reliably get + // batch fulfillments included, while also making sure that the biggest gas guzzler + // callbacks are included. + config, err := lsn.coordinator.GetConfig(&bind.CallOpts{ + Context: ctx, + }) + if err != nil { + lsn.l.Errorw("Couldn't get config from coordinator", "err", err) + return processed + } + + // Add very conservative upper bound estimate on verification costs. + batchMaxGas := config.MaxGasLimit() + 400_000 + + l := lsn.l.With( + "subID", subID, + "eligibleSubReqs", len(reqs), + "startBalance", startBalance.String(), + "startBalanceNoReserved", startBalanceNoReserved.String(), + "batchMaxGas", batchMaxGas, + "subIsActive", subIsActive, + "nativePayment", nativePayment, + ) + + defer func() { + l.Infow("Finished processing for sub", + "endBalance", startBalanceNoReserved.String(), + "totalProcessed", len(processed), + "totalUnique", uniqueReqs(reqs), + "time", time.Since(start).String()) + }() + + l.Infow("Processing requests for subscription with batching") + + ready, expired := lsn.getReadyAndExpired(l, reqs) + for _, reqID := range expired { + processed[reqID] = struct{}{} + } + + // Process requests in chunks in order to kick off as many jobs + // as configured in parallel. Then we can combine into fulfillment + // batches afterwards. + for chunkStart := 0; chunkStart < len(ready); chunkStart += int(lsn.job.VRFSpec.ChunkSize) { + chunkEnd := chunkStart + int(lsn.job.VRFSpec.ChunkSize) + if chunkEnd > len(ready) { + chunkEnd = len(ready) + } + chunk := ready[chunkStart:chunkEnd] + + var unfulfilled []pendingRequest + alreadyFulfilled, err := lsn.checkReqsFulfilled(ctx, l, chunk) + if errors.Is(err, context.Canceled) { + l.Infow("Context canceled, stopping request processing", "err", err) + return processed + } else if err != nil { + l.Errorw("Error checking for already fulfilled requests, proceeding anyway", "err", err) + } + for i, a := range alreadyFulfilled { + if a { + processed[chunk[i].req.RequestID().String()] = struct{}{} + } else { + unfulfilled = append(unfulfilled, chunk[i]) + } + } + + // All fromAddresses passed to the VRFv2 job have the same KeySpecific-MaxPrice value. + fromAddresses := lsn.fromAddresses() + maxGasPriceWei := lsn.feeCfg.PriceMaxKey(fromAddresses[0]) + + // Cases: + // 1. Never simulated: in this case, we want to observe the time until simulated + // on the utcTimestamp field of the pending request. + // 2. Simulated before: in this case, lastTry will be set to a non-zero time value, + // in which case we'd want to use that as a relative point from when we last tried + // the request. + observeRequestSimDuration(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), unfulfilled) + + pipelines := lsn.runPipelines(ctx, l, maxGasPriceWei, unfulfilled) + batches := newBatchFulfillments(batchMaxGas, lsn.coordinator.Version()) + outOfBalance := false + for _, p := range pipelines { + ll := l.With("reqID", p.req.req.RequestID().String(), + "txHash", p.req.req.Raw().TxHash, + "maxGasPrice", maxGasPriceWei.String(), + "fundsNeeded", p.fundsNeeded.String(), + "maxFee", p.maxFee.String(), + "gasLimit", p.gasLimit, + "attempts", p.req.attempts, + "remainingBalance", startBalanceNoReserved.String(), + "consumerAddress", p.req.req.Sender(), + "blockNumber", p.req.req.Raw().BlockNumber, + "blockHash", p.req.req.Raw().BlockHash, + ) + fromAddresses := lsn.fromAddresses() + fromAddress, err := lsn.gethks.GetRoundRobinAddress(lsn.chainID, fromAddresses...) + if err != nil { + l.Errorw("Couldn't get next from address", "err", err) + continue + } + ll = ll.With("fromAddress", fromAddress) + + if p.err != nil { + if errors.Is(p.err, errBlockhashNotInStore{}) { + // Running the blockhash store feeder in backwards mode will be required to + // resolve this. + ll.Criticalw("Pipeline error", "err", p.err) + } else if errors.Is(p.err, errProofVerificationFailed{}) { + // This occurs when the proof reverts in the simulation + // This is almost always (if not always) due to a proof generated with an out-of-date + // blockhash + // we can simply mark as processed and move on, since we will eventually + // process the request with the right blockhash + ll.Infow("proof reverted in simulation, likely stale blockhash") + processed[p.req.req.RequestID().String()] = struct{}{} + } else { + ll.Errorw("Pipeline error", "err", p.err) + if !subIsActive { + ll.Warnw("Force-fulfilling a request with insufficient funds on a cancelled sub") + etx, err := lsn.enqueueForceFulfillment(ctx, p, fromAddress) + if err != nil { + ll.Errorw("Error enqueuing force-fulfillment, re-queueing request", "err", err) + continue + } + ll.Infow("Successfully enqueued force-fulfillment", "ethTxID", etx.ID) + processed[p.req.req.RequestID().String()] = struct{}{} + + // Need to put a continue here, otherwise the next if statement will be hit + // and we'd break out of the loop prematurely. + // If a sub is canceled, we want to force-fulfill ALL of it's pending requests + // before saying we're done with it. + continue + } + + if startBalanceNoReserved.Cmp(p.fundsNeeded) < 0 && errors.Is(p.err, errPossiblyInsufficientFunds{}) { + ll.Infow("Insufficient balance to fulfill a request based on estimate, breaking", "err", p.err) + outOfBalance = true + + // break out of this inner loop to process the currently constructed batch + break + } + + // Ensure consumer is valid, otherwise drop the request. + if !lsn.isConsumerValidAfterFinalityDepthElapsed(ctx, p.req) { + lsn.l.Infow( + "Dropping request that was made by an invalid consumer.", + "consumerAddress", p.req.req.Sender(), + "reqID", p.req.req.RequestID(), + "blockNumber", p.req.req.Raw().BlockNumber, + "blockHash", p.req.req.Raw().BlockHash, + ) + processed[p.req.req.RequestID().String()] = struct{}{} + continue + } + } + continue + } + + if startBalanceNoReserved.Cmp(p.maxFee) < 0 { + // Insufficient funds, have to wait for a user top up. + // Break out of the loop now and process what we are able to process + // in the constructed batches. + ll.Infow("Insufficient balance to fulfill a request, breaking") + break + } + + batches.addRun(p, fromAddress) + + startBalanceNoReserved.Sub(startBalanceNoReserved, p.maxFee) + } + + var processedRequestIDs []string + for _, batch := range batches.fulfillments { + l.Debugw("Processing batch", "batchSize", len(batch.proofs)) + p := lsn.processBatch(l, subID, startBalanceNoReserved, batchMaxGas, batch, batch.fromAddress) + processedRequestIDs = append(processedRequestIDs, p...) + } + + for _, reqID := range processedRequestIDs { + processed[reqID] = struct{}{} + } + + // outOfBalance is set to true if the current sub we are processing + // has run out of funds to process any remaining requests. After enqueueing + // this constructed batch, we break out of this outer loop in order to + // avoid unnecessarily processing the remaining requests. + if outOfBalance { + break + } + } + + return +} + +func (lsn *listenerV2) processRequestsPerSubBatch( + ctx context.Context, + subID *big.Int, + startLinkBalance *big.Int, + startEthBalance *big.Int, + reqs []pendingRequest, + subIsActive bool, +) map[string]struct{} { + var processed = make(map[string]struct{}) + startBalanceNoReserveLink, err := lsn.MaybeSubtractReservedLink( + ctx, startLinkBalance, lsn.chainID, subID, lsn.coordinator.Version()) + if err != nil { + lsn.l.Errorw("Couldn't get reserved LINK for subscription", "sub", reqs[0].req.SubID(), "err", err) + return processed + } + startBalanceNoReserveEth, err := lsn.MaybeSubtractReservedEth( + ctx, startEthBalance, lsn.chainID, subID, lsn.coordinator.Version()) + if err != nil { + lsn.l.Errorw("Couldn't get reserved ether for subscription", "sub", reqs[0].req.SubID(), "err", err) + return processed + } + + // Split the requests into native and LINK requests. + var ( + nativeRequests []pendingRequest + linkRequests []pendingRequest + ) + for _, req := range reqs { + if req.req.NativePayment() { + nativeRequests = append(nativeRequests, req) + } else { + linkRequests = append(linkRequests, req) + } + } + // process the native and link requests in parallel + var wg sync.WaitGroup + var nativeProcessed, linkProcessed map[string]struct{} + wg.Add(2) + go func() { + defer wg.Done() + nativeProcessed = lsn.processRequestsPerSubBatchHelper(ctx, subID, startEthBalance, startBalanceNoReserveEth, nativeRequests, subIsActive, true) + }() + go func() { + defer wg.Done() + linkProcessed = lsn.processRequestsPerSubBatchHelper(ctx, subID, startLinkBalance, startBalanceNoReserveLink, linkRequests, subIsActive, false) + }() + wg.Wait() + // combine the processed link and native requests into the processed map + for k, v := range nativeProcessed { + processed[k] = v + } + for k, v := range linkProcessed { + processed[k] = v + } + + return processed +} + +// enqueueForceFulfillment enqueues a forced fulfillment through the +// VRFOwner contract. It estimates gas again on the transaction due +// to the extra steps taken within VRFOwner.fulfillRandomWords. +func (lsn *listenerV2) enqueueForceFulfillment( + ctx context.Context, + p vrfPipelineResult, + fromAddress common.Address, +) (etx txmgr.Tx, err error) { + if lsn.job.VRFSpec.VRFOwnerAddress == nil { + err = errors.New("vrf owner address not set in job spec, recreate job and provide it to force-fulfill") + return + } + + if p.payload == "" { + // should probably never happen + // a critical log will be logged if this is the case in simulateFulfillment + err = errors.New("empty payload in vrfPipelineResult") + return + } + + // fulfill the request through the VRF owner + err = lsn.q.Transaction(func(tx pg.Queryer) error { + lsn.l.Infow("VRFOwner.fulfillRandomWords vs. VRFCoordinatorV2.fulfillRandomWords", + "vrf_owner.fulfillRandomWords", hexutil.Encode(vrfOwnerABI.Methods["fulfillRandomWords"].ID), + "vrf_coordinator_v2.fulfillRandomWords", hexutil.Encode(coordinatorV2ABI.Methods["fulfillRandomWords"].ID), + ) + + vrfOwnerAddress1 := lsn.vrfOwner.Address() + vrfOwnerAddressSpec := lsn.job.VRFSpec.VRFOwnerAddress.Address() + lsn.l.Infow("addresses diff", "wrapper_address", vrfOwnerAddress1, "spec_address", vrfOwnerAddressSpec) + + lsn.l.Infow("fulfillRandomWords payload", "proof", p.proof, "commitment", p.reqCommitment.Get(), "payload", p.payload) + txData := hexutil.MustDecode(p.payload) + if err != nil { + return fmt.Errorf("abi pack VRFOwner.fulfillRandomWords: %w", err) + } + estimateGasLimit, err := lsn.chain.Client().EstimateGas(ctx, ethereum.CallMsg{ + From: fromAddress, + To: &vrfOwnerAddressSpec, + Data: txData, + }) + if err != nil { + return fmt.Errorf("failed to estimate gas on VRFOwner.fulfillRandomWords: %w", err) + } + + lsn.l.Infow("Estimated gas limit on force fulfillment", + "estimateGasLimit", estimateGasLimit, "pipelineGasLimit", p.gasLimit) + if estimateGasLimit < uint64(p.gasLimit) { + estimateGasLimit = uint64(p.gasLimit) + } + + requestID := common.BytesToHash(p.req.req.RequestID().Bytes()) + subID := p.req.req.SubID() + requestTxHash := p.req.req.Raw().TxHash + etx, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ + FromAddress: fromAddress, + ToAddress: lsn.vrfOwner.Address(), + EncodedPayload: txData, + FeeLimit: uint32(estimateGasLimit), + Strategy: txmgrcommon.NewSendEveryStrategy(), + Meta: &txmgr.TxMeta{ + RequestID: &requestID, + SubID: ptr(subID.Uint64()), + RequestTxHash: &requestTxHash, + // No max link since simulation failed + }, + }) + return err + }) + return +} + +// For an errored pipeline run, wait until the finality depth of the chain to have elapsed, +// then check if the failing request is being called by an invalid sender. Return false if this is the case, +// otherwise true. +func (lsn *listenerV2) isConsumerValidAfterFinalityDepthElapsed(ctx context.Context, req pendingRequest) bool { + latestHead := lsn.getLatestHead() + if latestHead-req.req.Raw().BlockNumber > uint64(lsn.cfg.FinalityDepth()) { + code, err := lsn.chain.Client().CodeAt(ctx, req.req.Sender(), big.NewInt(int64(latestHead))) + if err != nil { + lsn.l.Warnw("Failed to fetch contract code", "err", err) + return true // error fetching code, give the benefit of doubt to the consumer + } + if len(code) == 0 { + return false // invalid consumer + } + } + + return true // valid consumer, or finality depth has not elapsed +} + +// processRequestsPerSubHelper processes a set of pending requests for the provided sub id. +// It returns a set of request IDs that were processed. +// Note that the provided startBalanceNoReserve is the balance of the subscription +// minus any pending requests that have already been processed and not yet fulfilled onchain. +func (lsn *listenerV2) processRequestsPerSubHelper( + ctx context.Context, + subID *big.Int, + startBalance *big.Int, + startBalanceNoReserved *big.Int, + reqs []pendingRequest, + subIsActive bool, + nativePayment bool, +) (processed map[string]struct{}) { + start := time.Now() + processed = make(map[string]struct{}) + + l := lsn.l.With( + "subID", subID, + "eligibleSubReqs", len(reqs), + "startBalance", startBalance.String(), + "startBalanceNoReserved", startBalanceNoReserved.String(), + "subIsActive", subIsActive, + "nativePayment", nativePayment, + ) + + defer func() { + l.Infow("Finished processing for sub", + "endBalance", startBalanceNoReserved.String(), + "totalProcessed", len(processed), + "totalUnique", uniqueReqs(reqs), + "time", time.Since(start).String()) + }() + + l.Infow("Processing requests for subscription") + + ready, expired := lsn.getReadyAndExpired(l, reqs) + for _, reqID := range expired { + processed[reqID] = struct{}{} + } + + // Process requests in chunks + for chunkStart := 0; chunkStart < len(ready); chunkStart += int(lsn.job.VRFSpec.ChunkSize) { + chunkEnd := chunkStart + int(lsn.job.VRFSpec.ChunkSize) + if chunkEnd > len(ready) { + chunkEnd = len(ready) + } + chunk := ready[chunkStart:chunkEnd] + + var unfulfilled []pendingRequest + alreadyFulfilled, err := lsn.checkReqsFulfilled(ctx, l, chunk) + if errors.Is(err, context.Canceled) { + l.Infow("Context canceled, stopping request processing", "err", err) + return processed + } else if err != nil { + l.Errorw("Error checking for already fulfilled requests, proceeding anyway", "err", err) + } + for i, a := range alreadyFulfilled { + if a { + processed[chunk[i].req.RequestID().String()] = struct{}{} + } else { + unfulfilled = append(unfulfilled, chunk[i]) + } + } + + // All fromAddresses passed to the VRFv2 job have the same KeySpecific-MaxPrice value. + fromAddresses := lsn.fromAddresses() + maxGasPriceWei := lsn.feeCfg.PriceMaxKey(fromAddresses[0]) + observeRequestSimDuration(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version(), unfulfilled) + pipelines := lsn.runPipelines(ctx, l, maxGasPriceWei, unfulfilled) + for _, p := range pipelines { + ll := l.With("reqID", p.req.req.RequestID().String(), + "txHash", p.req.req.Raw().TxHash, + "maxGasPrice", maxGasPriceWei.String(), + "fundsNeeded", p.fundsNeeded.String(), + "maxFee", p.maxFee.String(), + "gasLimit", p.gasLimit, + "attempts", p.req.attempts, + "remainingBalance", startBalanceNoReserved.String(), + "consumerAddress", p.req.req.Sender(), + "blockNumber", p.req.req.Raw().BlockNumber, + "blockHash", p.req.req.Raw().BlockHash, + ) + fromAddress, err := lsn.gethks.GetRoundRobinAddress(lsn.chainID, fromAddresses...) + if err != nil { + l.Errorw("Couldn't get next from address", "err", err) + continue + } + ll = ll.With("fromAddress", fromAddress) + + if p.err != nil { + if errors.Is(p.err, errBlockhashNotInStore{}) { + // Running the blockhash store feeder in backwards mode will be required to + // resolve this. + ll.Criticalw("Pipeline error", "err", p.err) + } else if errors.Is(p.err, errProofVerificationFailed{}) { + // This occurs when the proof reverts in the simulation + // This is almost always (if not always) due to a proof generated with an out-of-date + // blockhash + // we can simply mark as processed and move on, since we will eventually + // process the request with the right blockhash + ll.Infow("proof reverted in simulation, likely stale blockhash") + processed[p.req.req.RequestID().String()] = struct{}{} + } else { + ll.Errorw("Pipeline error", "err", p.err) + + if !subIsActive { + lsn.l.Warnw("Force-fulfilling a request with insufficient funds on a cancelled sub") + etx, err2 := lsn.enqueueForceFulfillment(ctx, p, fromAddress) + if err2 != nil { + ll.Errorw("Error enqueuing force-fulfillment, re-queueing request", "err", err2) + continue + } + ll.Infow("Enqueued force-fulfillment", "ethTxID", etx.ID) + processed[p.req.req.RequestID().String()] = struct{}{} + + // Need to put a continue here, otherwise the next if statement will be hit + // and we'd break out of the loop prematurely. + // If a sub is canceled, we want to force-fulfill ALL of it's pending requests + // before saying we're done with it. + continue + } + + if startBalanceNoReserved.Cmp(p.fundsNeeded) < 0 { + ll.Infow("Insufficient balance to fulfill a request based on estimate, returning", "err", p.err) + return processed + } + + // Ensure consumer is valid, otherwise drop the request. + if !lsn.isConsumerValidAfterFinalityDepthElapsed(ctx, p.req) { + lsn.l.Infow( + "Dropping request that was made by an invalid consumer.", + "consumerAddress", p.req.req.Sender(), + "reqID", p.req.req.RequestID(), + "blockNumber", p.req.req.Raw().BlockNumber, + "blockHash", p.req.req.Raw().BlockHash, + ) + processed[p.req.req.RequestID().String()] = struct{}{} + continue + } + } + continue + } + + if startBalanceNoReserved.Cmp(p.maxFee) < 0 { + // Insufficient funds, have to wait for a user top up. Leave it unprocessed for now + ll.Infow("Insufficient balance to fulfill a request, returning") + return processed + } + + ll.Infow("Enqueuing fulfillment") + var transaction txmgr.Tx + err = lsn.q.Transaction(func(tx pg.Queryer) error { + if err = lsn.pipelineRunner.InsertFinishedRun(p.run, true, pg.WithQueryer(tx)); err != nil { + return err + } + + var maxLink, maxEth *string + tmp := p.maxFee.String() + if p.reqCommitment.NativePayment() { + maxEth = &tmp + } else { + maxLink = &tmp + } + var ( + txMetaSubID *uint64 + txMetaGlobalSubID *string + ) + if lsn.coordinator.Version() == vrfcommon.V2Plus { + txMetaGlobalSubID = ptr(p.req.req.SubID().String()) + } else if lsn.coordinator.Version() == vrfcommon.V2 { + txMetaSubID = ptr(p.req.req.SubID().Uint64()) + } + requestID := common.BytesToHash(p.req.req.RequestID().Bytes()) + coordinatorAddress := lsn.coordinator.Address() + requestTxHash := p.req.req.Raw().TxHash + transaction, err = lsn.chain.TxManager().CreateTransaction(ctx, txmgr.TxRequest{ + FromAddress: fromAddress, + ToAddress: lsn.coordinator.Address(), + EncodedPayload: hexutil.MustDecode(p.payload), + FeeLimit: p.gasLimit, + Meta: &txmgr.TxMeta{ + RequestID: &requestID, + MaxLink: maxLink, + MaxEth: maxEth, + SubID: txMetaSubID, + GlobalSubID: txMetaGlobalSubID, + RequestTxHash: &requestTxHash, + }, + Strategy: txmgrcommon.NewSendEveryStrategy(), + Checker: txmgr.TransmitCheckerSpec{ + CheckerType: lsn.transmitCheckerType(), + VRFCoordinatorAddress: &coordinatorAddress, + VRFRequestBlockNumber: new(big.Int).SetUint64(p.req.req.Raw().BlockNumber), + }, + }) + return err + }) + if err != nil { + ll.Errorw("Error enqueuing fulfillment, requeuing request", "err", err) + continue + } + ll.Infow("Enqueued fulfillment", "ethTxID", transaction.GetID()) + + // If we successfully enqueued for the txm, subtract that balance + // And loop to attempt to enqueue another fulfillment + startBalanceNoReserved.Sub(startBalanceNoReserved, p.maxFee) + processed[p.req.req.RequestID().String()] = struct{}{} + vrfcommon.IncProcessedReqs(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, lsn.coordinator.Version()) + } + } + + return +} + +func (lsn *listenerV2) transmitCheckerType() txmgrtypes.TransmitCheckerType { + if lsn.coordinator.Version() == vrfcommon.V2 { + return txmgr.TransmitCheckerTypeVRFV2 + } + return txmgr.TransmitCheckerTypeVRFV2Plus +} + +func (lsn *listenerV2) processRequestsPerSub( + ctx context.Context, + subID *big.Int, + startLinkBalance *big.Int, + startEthBalance *big.Int, + reqs []pendingRequest, + subIsActive bool, +) map[string]struct{} { + if lsn.job.VRFSpec.BatchFulfillmentEnabled && lsn.batchCoordinator != nil { + return lsn.processRequestsPerSubBatch(ctx, subID, startLinkBalance, startEthBalance, reqs, subIsActive) + } + + var processed = make(map[string]struct{}) + chainId := lsn.chain.Client().ConfiguredChainID() + startBalanceNoReserveLink, err := lsn.MaybeSubtractReservedLink( + ctx, startLinkBalance, chainId, subID, lsn.coordinator.Version()) + if err != nil { + lsn.l.Errorw("Couldn't get reserved LINK for subscription", "sub", reqs[0].req.SubID(), "err", err) + return processed + } + startBalanceNoReserveEth, err := lsn.MaybeSubtractReservedEth( + ctx, startEthBalance, lsn.chainID, subID, lsn.coordinator.Version()) + if err != nil { + lsn.l.Errorw("Couldn't get reserved ETH for subscription", "sub", reqs[0].req.SubID(), "err", err) + return processed + } + + // Split the requests into native and LINK requests. + var ( + nativeRequests []pendingRequest + linkRequests []pendingRequest + ) + for _, req := range reqs { + if req.req.NativePayment() { + if !lsn.inflightCache.Contains(req.req.Raw()) { + nativeRequests = append(nativeRequests, req) + } else { + lsn.l.Debugw("Skipping native request because it is already inflight", + "reqID", req.req.RequestID()) + } + } else { + if !lsn.inflightCache.Contains(req.req.Raw()) { + linkRequests = append(linkRequests, req) + } else { + lsn.l.Debugw("Skipping link request because it is already inflight", + "reqID", req.req.RequestID()) + } + } + } + // process the native and link requests in parallel + var ( + wg sync.WaitGroup + nativeProcessed, linkProcessed map[string]struct{} + ) + wg.Add(2) + go func() { + defer wg.Done() + nativeProcessed = lsn.processRequestsPerSubHelper( + ctx, + subID, + startEthBalance, + startBalanceNoReserveEth, + nativeRequests, + subIsActive, + true) + }() + go func() { + defer wg.Done() + linkProcessed = lsn.processRequestsPerSubHelper( + ctx, + subID, + startLinkBalance, + startBalanceNoReserveLink, + linkRequests, + subIsActive, + false) + }() + wg.Wait() + // combine the native and link processed requests into the processed map + for k, v := range nativeProcessed { + processed[k] = v + } + for k, v := range linkProcessed { + processed[k] = v + } + + return processed +} + +func (lsn *listenerV2) requestCommitmentPayload(requestID *big.Int) (payload []byte, err error) { + if lsn.coordinator.Version() == vrfcommon.V2Plus { + return coordinatorV2PlusABI.Pack("s_requestCommitments", requestID) + } else if lsn.coordinator.Version() == vrfcommon.V2 { + return coordinatorV2ABI.Pack("getCommitment", requestID) + } + return nil, errors.Errorf("unsupported coordinator version: %s", lsn.coordinator.Version()) +} + +// checkReqsFulfilled returns a bool slice the same size of the given reqs slice +// where each slice element indicates whether that request was already fulfilled +// or not. +func (lsn *listenerV2) checkReqsFulfilled(ctx context.Context, l logger.Logger, reqs []pendingRequest) ([]bool, error) { + var ( + start = time.Now() + calls = make([]rpc.BatchElem, len(reqs)) + fulfilled = make([]bool, len(reqs)) + ) + + for i, req := range reqs { + payload, err := lsn.requestCommitmentPayload(req.req.RequestID()) + if err != nil { + // This shouldn't happen + return fulfilled, fmt.Errorf("creating getCommitment payload: %w", err) + } + + reqBlockNumber := new(big.Int).SetUint64(req.req.Raw().BlockNumber) + + // Subtract 5 since the newest block likely isn't indexed yet and will cause "header not + // found" errors. + currBlock := new(big.Int).SetUint64(lsn.getLatestHead() - 5) + m := bigmath.Max(reqBlockNumber, currBlock) + + var result string + calls[i] = rpc.BatchElem{ + Method: "eth_call", + Args: []interface{}{ + map[string]interface{}{ + "to": lsn.coordinator.Address(), + "data": hexutil.Bytes(payload), + }, + // The block at which we want to make the call + hexutil.EncodeBig(m), + }, + Result: &result, + } + } + + err := lsn.chain.Client().BatchCallContext(ctx, calls) + if err != nil { + return fulfilled, fmt.Errorf("making batch call: %w", err) + } + + var errs error + for i, call := range calls { + if call.Error != nil { + errs = multierr.Append(errs, fmt.Errorf("checking request %s with hash %s: %w", + reqs[i].req.RequestID().String(), reqs[i].req.Raw().TxHash.String(), call.Error)) + continue + } + + rString, ok := call.Result.(*string) + if !ok { + errs = multierr.Append(errs, + fmt.Errorf("unexpected result %+v on request %s with hash %s", + call.Result, reqs[i].req.RequestID().String(), reqs[i].req.Raw().TxHash.String())) + continue + } + result, err := hexutil.Decode(*rString) + if err != nil { + errs = multierr.Append(errs, + fmt.Errorf("decoding batch call result %+v %s request %s with hash %s: %w", + call.Result, *rString, reqs[i].req.RequestID().String(), reqs[i].req.Raw().TxHash.String(), err)) + continue + } + + if utils.IsEmpty(result) { + l.Infow("Request already fulfilled", + "reqID", reqs[i].req.RequestID().String(), + "attempts", reqs[i].attempts, + "txHash", reqs[i].req.Raw().TxHash) + fulfilled[i] = true + } + } + + l.Debugw("Done checking fulfillment status", + "numChecked", len(reqs), "time", time.Since(start).String()) + return fulfilled, errs +} + +func (lsn *listenerV2) runPipelines( + ctx context.Context, + l logger.Logger, + maxGasPriceWei *assets.Wei, + reqs []pendingRequest, +) []vrfPipelineResult { + var ( + start = time.Now() + results = make([]vrfPipelineResult, len(reqs)) + wg = sync.WaitGroup{} + ) + + for i, req := range reqs { + wg.Add(1) + go func(i int, req pendingRequest) { + defer wg.Done() + results[i] = lsn.simulateFulfillment(ctx, maxGasPriceWei, req, l) + }(i, req) + } + wg.Wait() + + l.Debugw("Finished running pipelines", + "count", len(reqs), "time", time.Since(start).String()) + return results +} + +func (lsn *listenerV2) estimateFee( + ctx context.Context, + req RandomWordsRequested, + maxGasPriceWei *assets.Wei, +) (*big.Int, error) { + // NativePayment() returns true if and only if the version is V2+ and the + // request was made in ETH. + if req.NativePayment() { + return EstimateFeeWei(req.CallbackGasLimit(), maxGasPriceWei.ToInt()) + } + + // In the event we are using LINK we need to estimate the fee in juels + // Don't use up too much time to get this info, it's not critical for operating vrf. + callCtx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + roundData, err := lsn.aggregator.LatestRoundData(&bind.CallOpts{Context: callCtx}) + if err != nil { + return nil, fmt.Errorf("get aggregator latestAnswer: %w", err) + } + + return EstimateFeeJuels( + req.CallbackGasLimit(), + maxGasPriceWei.ToInt(), + roundData.Answer, + ) +} + +// Here we use the pipeline to parse the log, generate a vrf response +// then simulate the transaction at the max gas price to determine its maximum link cost. +func (lsn *listenerV2) simulateFulfillment( + ctx context.Context, + maxGasPriceWei *assets.Wei, + req pendingRequest, + lg logger.Logger, +) vrfPipelineResult { + var ( + res = vrfPipelineResult{req: req} + err error + ) + // estimate how much funds are needed so that we can log it if the simulation fails. + res.fundsNeeded, err = lsn.estimateFee(ctx, req.req, maxGasPriceWei) + if err != nil { + // not critical, just log and continue + lg.Warnw("unable to estimate funds needed for request, continuing anyway", + "reqID", req.req.RequestID(), + "err", err) + res.fundsNeeded = big.NewInt(0) + } + + vars := pipeline.NewVarsFrom(map[string]interface{}{ + "jobSpec": map[string]interface{}{ + "databaseID": lsn.job.ID, + "externalJobID": lsn.job.ExternalJobID, + "name": lsn.job.Name.ValueOrZero(), + "publicKey": lsn.job.VRFSpec.PublicKey[:], + "maxGasPrice": maxGasPriceWei.ToInt().String(), + "evmChainID": lsn.job.VRFSpec.EVMChainID.String(), + }, + "jobRun": map[string]interface{}{ + "logBlockHash": req.req.Raw().BlockHash.Bytes(), + "logBlockNumber": req.req.Raw().BlockNumber, + "logTxHash": req.req.Raw().TxHash, + "logTopics": req.req.Raw().Topics, + "logData": req.req.Raw().Data, + }, + }) + var trrs pipeline.TaskRunResults + res.run, trrs, err = lsn.pipelineRunner.ExecuteRun(ctx, *lsn.job.PipelineSpec, vars, lg) + if err != nil { + res.err = fmt.Errorf("executing run: %w", err) + return res + } + // The call task will fail if there are insufficient funds + if res.run.AllErrors.HasError() { + res.err = errors.WithStack(res.run.AllErrors.ToError()) + + if strings.Contains(res.err.Error(), "blockhash not found in store") { + res.err = multierr.Combine(res.err, errBlockhashNotInStore{}) + } else if isProofVerificationError(res.err.Error()) { + res.err = multierr.Combine(res.err, errProofVerificationFailed{}) + } else if strings.Contains(res.err.Error(), "execution reverted") { + // Even if the simulation fails, we want to get the + // txData for the fulfillRandomWords call, in case + // we need to force fulfill. + for _, trr := range trrs { + if trr.Task.Type() == pipeline.TaskTypeVRFV2 { + if trr.Result.Error != nil { + // error in VRF proof generation + // this means that we won't be able to force-fulfill in the event of a + // canceled sub and active requests. + // since this would be an extraordinary situation, + // we can log loudly here. + lg.Criticalw("failed to generate VRF proof", "err", trr.Result.Error) + break + } + + // extract the abi-encoded tx data to fulfillRandomWords from the VRF task. + // that's all we need in the event of a force-fulfillment. + m := trr.Result.Value.(map[string]any) + res.payload = m["output"].(string) + res.proof = FromV2Proof(m["proof"].(vrf_coordinator_v2.VRFProof)) + res.reqCommitment = NewRequestCommitment(m["requestCommitment"]) + } + } + res.err = multierr.Combine(res.err, errPossiblyInsufficientFunds{}) + } + + return res + } + finalResult := trrs.FinalResult(lg) + if len(finalResult.Values) != 1 { + res.err = errors.Errorf("unexpected number of outputs, expected 1, was %d", len(finalResult.Values)) + return res + } + + // Run succeeded, we expect a byte array representing the billing amount + b, ok := finalResult.Values[0].([]uint8) + if !ok { + res.err = errors.New("expected []uint8 final result") + return res + } + res.maxFee = utils.HexToBig(hexutil.Encode(b)[2:]) + for _, trr := range trrs { + if trr.Task.Type() == pipeline.TaskTypeVRFV2 { + m := trr.Result.Value.(map[string]interface{}) + res.payload = m["output"].(string) + res.proof = FromV2Proof(m["proof"].(vrf_coordinator_v2.VRFProof)) + res.reqCommitment = NewRequestCommitment(m["requestCommitment"]) + } + + if trr.Task.Type() == pipeline.TaskTypeVRFV2Plus { + m := trr.Result.Value.(map[string]interface{}) + res.payload = m["output"].(string) + res.proof = FromV2PlusProof(m["proof"].(vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalProof)) + res.reqCommitment = NewRequestCommitment(m["requestCommitment"]) + } + + if trr.Task.Type() == pipeline.TaskTypeEstimateGasLimit { + res.gasLimit = trr.Result.Value.(uint32) + } + } + return res +} + +func (lsn *listenerV2) fromAddresses() []common.Address { + var addresses []common.Address + for _, a := range lsn.job.VRFSpec.FromAddresses { + addresses = append(addresses, a.Address()) + } + return addresses +} diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index 8a7b9ce3fef..bcc85b3700d 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -3,7 +3,6 @@ package v2 import ( "encoding/json" "math/big" - "sync" "testing" "time" @@ -12,10 +11,8 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/theodesp/go-heaps/pairing" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" @@ -23,8 +20,6 @@ import ( txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" @@ -502,77 +497,3 @@ func TestListener_Backoff(t *testing.T) { }) } } - -func TestListener_handleLog(tt *testing.T) { - lb := mocks.NewBroadcaster(tt) - chainID := int64(2) - minConfs := uint32(3) - blockNumber := uint64(5) - requestID := int64(6) - tt.Run("v2", func(t *testing.T) { - j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ - VRFVersion: vrfcommon.V2, - RequestedConfsDelay: 10, - FromAddresses: []string{"0xF2982b7Ef6E3D8BB738f8Ea20502229781f6Ad97"}, - }).Toml()) - require.NoError(t, err) - fulfilledLog := vrf_coordinator_v2.VRFCoordinatorV2RandomWordsFulfilled{ - RequestId: big.NewInt(requestID), - Raw: types.Log{BlockNumber: blockNumber}, - } - log := log.NewLogBroadcast(types.Log{}, *big.NewInt(chainID), &fulfilledLog) - lb.On("WasAlreadyConsumed", log).Return(false, nil).Once() - lb.On("MarkConsumed", log).Return(nil).Once() - defer lb.AssertExpectations(t) - chain := evmmocks.NewChain(t) - chain.On("LogBroadcaster").Return(lb) - listener := &listenerV2{ - respCount: map[string]uint64{}, - job: j, - blockNumberToReqID: pairing.New(), - latestHeadMu: sync.RWMutex{}, - chain: chain, - l: logger.TestLogger(t), - } - listener.handleLog(log, minConfs) - require.Equal(t, listener.respCount[fulfilledLog.RequestId.String()], uint64(1)) - req, ok := listener.blockNumberToReqID.FindMin().(fulfilledReqV2) - require.True(t, ok) - require.Equal(t, req.blockNumber, blockNumber) - require.Equal(t, req.reqID, "6") - }) - - tt.Run("v2 plus", func(t *testing.T) { - j, err := vrfcommon.ValidatedVRFSpec(testspecs.GenerateVRFSpec(testspecs.VRFSpecParams{ - VRFVersion: vrfcommon.V2Plus, - RequestedConfsDelay: 10, - FromAddresses: []string{"0xF2982b7Ef6E3D8BB738f8Ea20502229781f6Ad97"}, - }).Toml()) - require.NoError(t, err) - fulfilledLog := vrf_coordinator_v2plus_interface.IVRFCoordinatorV2PlusInternalRandomWordsFulfilled{ - RequestId: big.NewInt(requestID), - Raw: types.Log{BlockNumber: blockNumber}, - } - log := log.NewLogBroadcast(types.Log{}, *big.NewInt(chainID), &fulfilledLog) - lb.On("WasAlreadyConsumed", log).Return(false, nil).Once() - lb.On("MarkConsumed", log).Return(nil).Once() - defer lb.AssertExpectations(t) - chain := evmmocks.NewChain(t) - chain.On("LogBroadcaster").Return(lb) - listener := &listenerV2{ - respCount: map[string]uint64{}, - job: j, - blockNumberToReqID: pairing.New(), - latestHeadMu: sync.RWMutex{}, - chain: chain, - l: logger.TestLogger(t), - } - listener.handleLog(log, minConfs) - require.Equal(t, listener.respCount[fulfilledLog.RequestId.String()], uint64(1)) - req, ok := listener.blockNumberToReqID.FindMin().(fulfilledReqV2) - require.True(t, ok) - require.Equal(t, req.blockNumber, blockNumber) - require.Equal(t, req.reqID, "6") - }) - -} diff --git a/core/services/vrf/v2/listener_v2_types.go b/core/services/vrf/v2/listener_v2_types.go index 5ad44c31a8b..b3cbbff4713 100644 --- a/core/services/vrf/v2/listener_v2_types.go +++ b/core/services/vrf/v2/listener_v2_types.go @@ -1,14 +1,14 @@ package v2 import ( + "fmt" "math/big" "time" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" + heaps "github.com/theodesp/go-heaps" txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/pg" @@ -16,6 +16,68 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/vrf/vrfcommon" ) +type errPossiblyInsufficientFunds struct{} + +func (errPossiblyInsufficientFunds) Error() string { + return "Simulation errored, possibly insufficient funds. Request will remain unprocessed until funds are available" +} + +type errBlockhashNotInStore struct{} + +func (errBlockhashNotInStore) Error() string { + return "Blockhash not in store" +} + +type errProofVerificationFailed struct{} + +func (errProofVerificationFailed) Error() string { + return "Proof verification failed" +} + +type fulfilledReqV2 struct { + blockNumber uint64 + reqID string +} + +func (a fulfilledReqV2) Compare(b heaps.Item) int { + a1 := a + a2 := b.(fulfilledReqV2) + switch { + case a1.blockNumber > a2.blockNumber: + return 1 + case a1.blockNumber < a2.blockNumber: + return -1 + default: + return 0 + } +} + +type pendingRequest struct { + confirmedAtBlock uint64 + req RandomWordsRequested + utcTimestamp time.Time + + // used for exponential backoff when retrying + attempts int + lastTry time.Time +} + +type vrfPipelineResult struct { + err error + // maxFee indicates how much juels (link) or wei (ether) would be paid for the VRF request + // if it were to be fulfilled at the maximum gas price (i.e gas lane gas price). + maxFee *big.Int + // fundsNeeded indicates a "minimum balance" in juels or wei that must be held in the + // subscription's account in order to fulfill the request. + fundsNeeded *big.Int + run *pipeline.Run + payload string + gasLimit uint32 + req pendingRequest + proof VRFProof + reqCommitment RequestCommitment +} + // batchFulfillment contains all the information needed in order to // perform a batch fulfillment operation on the batch VRF coordinator. type batchFulfillment struct { @@ -24,7 +86,6 @@ type batchFulfillment struct { totalGasLimit uint32 runs []*pipeline.Run reqIDs []*big.Int - lbs []log.Broadcast maxFees []*big.Int txHashes []common.Hash fromAddress common.Address @@ -46,9 +107,6 @@ func newBatchFulfillment(result vrfPipelineResult, fromAddress common.Address, v reqIDs: []*big.Int{ result.req.req.RequestID(), }, - lbs: []log.Broadcast{ - result.req.lb, - }, maxFees: []*big.Int{ result.maxFee, }, @@ -97,7 +155,6 @@ func (b *batchFulfillments) addRun(result vrfPipelineResult, fromAddress common. currBatch.totalGasLimit += result.gasLimit currBatch.runs = append(currBatch.runs, result.run) currBatch.reqIDs = append(currBatch.reqIDs, result.req.req.RequestID()) - currBatch.lbs = append(currBatch.lbs, result.req.lb) currBatch.maxFees = append(currBatch.maxFees, result.maxFee) currBatch.txHashes = append(currBatch.txHashes, result.req.req.Raw().TxHash) } @@ -167,17 +224,15 @@ func (lsn *listenerV2) processBatch( var ethTX txmgr.Tx err = lsn.q.Transaction(func(tx pg.Queryer) error { if err = lsn.pipelineRunner.InsertFinishedRuns(batch.runs, true, pg.WithQueryer(tx)); err != nil { - return errors.Wrap(err, "inserting finished pipeline runs") - } - - if err = lsn.chain.LogBroadcaster().MarkManyConsumed(batch.lbs, pg.WithQueryer(tx)); err != nil { - return errors.Wrap(err, "mark logs consumed") + return fmt.Errorf("inserting finished pipeline runs: %w", err) } maxLink, maxEth := accumulateMaxLinkAndMaxEth(batch) - txHashes := []common.Hash{} + var ( + txHashes []common.Hash + reqIDHashes []common.Hash + ) copy(txHashes, batch.txHashes) - reqIDHashes := []common.Hash{} for _, reqID := range batch.reqIDs { reqIDHashes = append(reqIDHashes, common.BytesToHash(reqID.Bytes())) } @@ -196,8 +251,11 @@ func (lsn *listenerV2) processBatch( RequestTxHashes: txHashes, }, }) + if err != nil { + return fmt.Errorf("create batch fulfillment eth transaction: %w", err) + } - return errors.Wrap(err, "create batch fulfillment eth transaction") + return nil }) if err != nil { ll.Errorw("Error enqueuing batch fulfillments, requeuing requests", "err", err) @@ -217,35 +275,21 @@ func (lsn *listenerV2) processBatch( return } -// getUnconsumed returns the requests in the given slice that are not expired -// and not marked consumed in the log broadcaster. -func (lsn *listenerV2) getUnconsumed(l logger.Logger, reqs []pendingRequest) (unconsumed []pendingRequest, processed []string) { +// getReadyAndExpired filters out requests that are expired from the given pendingRequest slice +// and returns requests that are ready for processing. +func (lsn *listenerV2) getReadyAndExpired(l logger.Logger, reqs []pendingRequest) (ready []pendingRequest, expired []string) { for _, req := range reqs { // Check if we can ignore the request due to its age. if time.Now().UTC().Sub(req.utcTimestamp) >= lsn.job.VRFSpec.RequestTimeout { l.Infow("Request too old, dropping it", "reqID", req.req.RequestID().String(), "txHash", req.req.Raw().TxHash) - lsn.markLogAsConsumed(req.lb) - processed = append(processed, req.req.RequestID().String()) + expired = append(expired, req.req.RequestID().String()) vrfcommon.IncDroppedReqs(lsn.job.Name.ValueOrZero(), lsn.job.ExternalJobID, vrfcommon.V2, vrfcommon.ReasonAge) continue } - - // This check to see if the log was consumed needs to be in the same - // goroutine as the mark consumed to avoid processing duplicates. - consumed, err := lsn.chain.LogBroadcaster().WasAlreadyConsumed(req.lb) - if err != nil { - // Do not process for now, retry on next iteration. - l.Errorw("Could not determine if log was already consumed", - "reqID", req.req.RequestID().String(), - "txHash", req.req.Raw().TxHash, - "error", err) - } else if consumed { - processed = append(processed, req.req.RequestID().String()) - } else { - unconsumed = append(unconsumed, req) - } + // we always check if the requests are already fulfilled prior to trying to fulfill them again + ready = append(ready, req) } return } diff --git a/core/services/vrf/v2/listener_v2_types_test.go b/core/services/vrf/v2/listener_v2_types_test.go index 5391e18589b..c66270210b9 100644 --- a/core/services/vrf/v2/listener_v2_types_test.go +++ b/core/services/vrf/v2/listener_v2_types_test.go @@ -8,7 +8,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink/v2/core/chains/evm/log/mocks" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2_5" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -30,7 +29,6 @@ func Test_BatchFulfillments_AddRun(t *testing.T) { TxHash: common.HexToHash("0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65"), }, }), - lb: mocks.NewBroadcast(t), }, run: pipeline.NewRun(pipeline.Spec{}, pipeline.Vars{}), }, fromAddress) @@ -49,7 +47,6 @@ func Test_BatchFulfillments_AddRun(t *testing.T) { TxHash: common.HexToHash("0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65"), }, }), - lb: mocks.NewBroadcast(t), }, run: pipeline.NewRun(pipeline.Spec{}, pipeline.Vars{}), }, fromAddress) @@ -70,7 +67,6 @@ func Test_BatchFulfillments_AddRun_V2Plus(t *testing.T) { TxHash: common.HexToHash("0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65"), }, }), - lb: mocks.NewBroadcast(t), }, run: pipeline.NewRun(pipeline.Spec{}, pipeline.Vars{}), }, fromAddress) @@ -89,7 +85,6 @@ func Test_BatchFulfillments_AddRun_V2Plus(t *testing.T) { TxHash: common.HexToHash("0xd8d7ecc4800d25fa53ce0372f13a416d98907a7ef3d8d3bdd79cf4fe75529c65"), }, }), - lb: mocks.NewBroadcast(t), }, run: pipeline.NewRun(pipeline.Spec{}, pipeline.Vars{}), }, fromAddress) diff --git a/core/services/vrf/vrfcommon/inflight_cache.go b/core/services/vrf/vrfcommon/inflight_cache.go new file mode 100644 index 00000000000..e5bd0e8870c --- /dev/null +++ b/core/services/vrf/vrfcommon/inflight_cache.go @@ -0,0 +1,85 @@ +package vrfcommon + +import ( + "sync" + + "github.com/ethereum/go-ethereum/core/types" +) + +type InflightCache interface { + Add(lg types.Log) + Contains(lg types.Log) bool + Size() int +} + +var _ InflightCache = (*inflightCache)(nil) + +const cachePruneInterval = 1000 + +type inflightCache struct { + // cache stores the logs whose fulfillments are currently in flight or already fulfilled. + cache map[logKey]struct{} + + // lookback defines how long state should be kept for. Logs included in blocks older than + // lookback may or may not be redelivered. + lookback int + + // lastPruneHeight is the blockheight at which logs were last pruned. + lastPruneHeight uint64 + + // mu synchronizes access to the delivered map. + mu sync.RWMutex +} + +func NewInflightCache(lookback int) InflightCache { + return &inflightCache{ + cache: make(map[logKey]struct{}), + lookback: lookback, + mu: sync.RWMutex{}, + } +} + +func (c *inflightCache) Size() int { + c.mu.RLock() + defer c.mu.RUnlock() + return len(c.cache) +} + +func (c *inflightCache) Add(lg types.Log) { + c.mu.Lock() + defer c.mu.Unlock() // unlock in the last defer, so that we hold the lock when pruning. + defer c.prune(lg.BlockNumber) + + c.cache[logKey{ + blockHash: lg.BlockHash, + blockNumber: lg.BlockNumber, + logIndex: lg.Index, + }] = struct{}{} +} + +func (c *inflightCache) Contains(lg types.Log) bool { + c.mu.RLock() + defer c.mu.RUnlock() + + _, ok := c.cache[logKey{ + blockHash: lg.BlockHash, + blockNumber: lg.BlockNumber, + logIndex: lg.Index, + }] + return ok +} + +func (c *inflightCache) prune(logBlock uint64) { + // Only prune every pruneInterval blocks + if int(logBlock)-int(c.lastPruneHeight) < cachePruneInterval { + return + } + + for key := range c.cache { + if int(key.blockNumber) < int(logBlock)-c.lookback { + delete(c.cache, key) + } + } + + c.lastPruneHeight = logBlock +} diff --git a/core/services/vrf/vrfcommon/log_dedupe_test.go b/core/services/vrf/vrfcommon/log_dedupe_test.go index 757842a3f65..f606b95e2a4 100644 --- a/core/services/vrf/vrfcommon/log_dedupe_test.go +++ b/core/services/vrf/vrfcommon/log_dedupe_test.go @@ -30,6 +30,22 @@ func TestLogDeduper(t *testing.T) { }, results: []bool{true, false}, }, + { + name: "different block number", + logs: []types.Log{ + { + BlockNumber: 10, + BlockHash: common.Hash{0x1}, + Index: 3, + }, + { + BlockNumber: 11, + BlockHash: common.Hash{0x2}, + Index: 3, + }, + }, + results: []bool{true, true}, + }, { name: "same block number different hash", logs: []types.Log{ @@ -128,11 +144,11 @@ func TestLogDeduper(t *testing.T) { Index: 11, }, { - BlockNumber: 115, + BlockNumber: 1015, BlockHash: common.Hash{0x1, 0x1, 0x5}, Index: 0, }, - // Now the logs at blocks 10 and 11 should be pruned, and therefor redelivered. + // Now the logs at blocks 10 and 11 should be pruned, and therefore redelivered. // The log at block 115 should not be redelivered. { BlockNumber: 10, @@ -145,7 +161,7 @@ func TestLogDeduper(t *testing.T) { Index: 11, }, { - BlockNumber: 115, + BlockNumber: 1015, BlockHash: common.Hash{0x1, 0x1, 0x5}, Index: 0, }, diff --git a/core/services/vrf/vrftesthelpers/helpers.go b/core/services/vrf/vrftesthelpers/helpers.go index 2f269fbff03..77d3f33a653 100644 --- a/core/services/vrf/vrftesthelpers/helpers.go +++ b/core/services/vrf/vrftesthelpers/helpers.go @@ -67,7 +67,7 @@ func CreateAndStartBHSJob( TrustedBlockhashStoreAddress: trustedBlockhashStoreAddress, TrustedBlockhashStoreBatchSize: trustedBlockhashStoreBatchSize, PollPeriod: time.Second, - RunTimeout: 100 * time.Millisecond, + RunTimeout: 10 * time.Second, EVMChainID: 1337, FromAddresses: fromAddresses, }) diff --git a/core/testdata/testspecs/v2_specs.go b/core/testdata/testspecs/v2_specs.go index 0155dc0a53a..0ecb85f1e49 100644 --- a/core/testdata/testspecs/v2_specs.go +++ b/core/testdata/testspecs/v2_specs.go @@ -247,6 +247,7 @@ type VRFSpecParams struct { BackoffInitialDelay time.Duration BackoffMaxDelay time.Duration GasLanePrice *assets.Wei + PollPeriod time.Duration } type VRFSpec struct { @@ -283,6 +284,10 @@ func GenerateVRFSpec(params VRFSpecParams) VRFSpec { if params.VRFOwnerAddress != "" && vrfVersion == vrfcommon.V2 { vrfOwnerAddress = params.VRFOwnerAddress } + pollPeriod := 5 * time.Second + if params.PollPeriod > 0 && (vrfVersion == vrfcommon.V2 || vrfVersion == vrfcommon.V2Plus) { + pollPeriod = params.PollPeriod + } batchFulfillmentGasMultiplier := 1.0 if params.BatchFulfillmentGasMultiplier >= 1.0 { batchFulfillmentGasMultiplier = params.BatchFulfillmentGasMultiplier @@ -406,6 +411,7 @@ chunkSize = %d backoffInitialDelay = "%s" backoffMaxDelay = "%s" gasLanePrice = "%s" +pollPeriod = "%s" observationSource = """ %s """ @@ -414,7 +420,8 @@ observationSource = """ jobID, name, coordinatorAddress, params.EVMChainID, batchCoordinatorAddress, params.BatchFulfillmentEnabled, strconv.FormatFloat(batchFulfillmentGasMultiplier, 'f', 2, 64), confirmations, params.RequestedConfsDelay, requestTimeout.String(), publicKey, chunkSize, - params.BackoffInitialDelay.String(), params.BackoffMaxDelay.String(), gasLanePrice.String(), observationSource) + params.BackoffInitialDelay.String(), params.BackoffMaxDelay.String(), gasLanePrice.String(), + pollPeriod.String(), observationSource) if len(params.FromAddresses) != 0 { var addresses []string for _, address := range params.FromAddresses { @@ -443,6 +450,7 @@ observationSource = """ BackoffMaxDelay: params.BackoffMaxDelay, VRFOwnerAddress: vrfOwnerAddress, VRFVersion: vrfVersion, + PollPeriod: pollPeriod, }, toml: toml} } diff --git a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go index 0e3c8096f0c..70320530aa8 100644 --- a/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go +++ b/integration-tests/actions/vrfv2plus/vrfv2plus_steps.go @@ -136,6 +136,7 @@ func CreateVRFV2PlusJob( ExternalJobID: jobUUID.String(), ObservationSource: ost, BatchFulfillmentEnabled: false, + PollPeriod: time.Second, }) if err != nil { return nil, fmt.Errorf("%s, err %w", ErrCreatingVRFv2PlusJob, err) @@ -301,9 +302,10 @@ func SetupVRFV2_5Environment( // [[EVM.KeySpecific]] // Key = '...' nodeConfig := node.NewConfig(env.ClCluster.Nodes[0].NodeConfig, + node.WithLogPollInterval(1*time.Second), node.WithVRFv2EVMEstimator(allNativeTokenKeyAddresses, vrfv2PlusConfig.CLNodeMaxGasPriceGWei), ) - l.Info().Msg("Restarting Node with new sending key PriceMax configuration") + l.Info().Msg("Restarting Node with new sending key PriceMax configuration and log poll period configuration") err = env.ClCluster.Nodes[0].Restart(nodeConfig) if err != nil { return nil, nil, nil, fmt.Errorf("%s, err %w", ErrRestartCLNode, err) diff --git a/integration-tests/client/chainlink_models.go b/integration-tests/client/chainlink_models.go index 8ff8494155b..abc6ef30e41 100644 --- a/integration-tests/client/chainlink_models.go +++ b/integration-tests/client/chainlink_models.go @@ -1128,6 +1128,7 @@ type VRFV2PlusJobSpec struct { BatchFulfillmentEnabled bool `toml:"batchFulfillmentEnabled"` BackOffInitialDelay time.Duration `toml:"backOffInitialDelay"` BackOffMaxDelay time.Duration `toml:"backOffMaxDelay"` + PollPeriod time.Duration `toml:"pollPeriod"` } // Type returns the type of the job diff --git a/integration-tests/types/config/node/core.go b/integration-tests/types/config/node/core.go index 5934809bace..ccbbce87a2e 100644 --- a/integration-tests/types/config/node/core.go +++ b/integration-tests/types/config/node/core.go @@ -246,3 +246,9 @@ func WithVRFv2EVMEstimator(addresses []string, maxGasPriceGWei int64) NodeConfig } } + +func WithLogPollInterval(interval time.Duration) NodeConfigOpt { + return func(c *chainlink.Config) { + c.EVM[0].Chain.LogPollInterval = models.MustNewDuration(interval) + } +} From 1788c04f3e93f6d1478345b0ae92ace9f437d3d2 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 28 Nov 2023 08:43:12 -0600 Subject: [PATCH 208/271] plugins/cmd/chainlink-median: move to chainlink-feeds (#11270) --- .github/workflows/integration-tests.yml | 4 ++ GNUmakefile | 4 -- core/scripts/go.mod | 3 +- core/scripts/go.sum | 2 + core/services/ocr2/plugins/median/plugin.go | 67 ------------------- core/services/ocr2/plugins/median/services.go | 3 +- go.mod | 3 +- go.sum | 2 + integration-tests/go.mod | 3 +- integration-tests/go.sum | 2 + plugins/chainlink.Dockerfile | 12 ++-- plugins/cmd/chainlink-median/main.go | 41 ------------ 12 files changed, 26 insertions(+), 120 deletions(-) delete mode 100644 core/services/ocr2/plugins/median/plugin.go delete mode 100644 plugins/cmd/chainlink-median/main.go diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9550be83ced..855e4ea9365 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -615,6 +615,10 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} + - name: Setup Go + uses: ./.github/actions/setup-go + with: + only-modules: "true" - name: Get the sha from go mod id: getshortsha run: | diff --git a/GNUmakefile b/GNUmakefile index cb665498a3a..6cd5ab7143e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -57,10 +57,6 @@ chainlink-test: operator-ui ## Build a test build of chainlink binary. chainlink-local-start: ./chainlink -c /etc/node-secrets-volume/default.toml -c /etc/node-secrets-volume/overrides.toml -secrets /etc/node-secrets-volume/secrets.toml node start -d -p /etc/node-secrets-volume/node-password -a /etc/node-secrets-volume/apicredentials --vrfpassword=/etc/node-secrets-volume/apicredentials -.PHONY: install-median -install-median: ## Build & install the chainlink-median binary. - go install $(GOFLAGS) ./plugins/cmd/chainlink-median - .PHONY: install-medianpoc install-medianpoc: ## Build & install the chainlink-medianpoc binary. go install $(GOFLAGS) ./plugins/cmd/chainlink-medianpoc diff --git a/core/scripts/go.mod b/core/scripts/go.mod index d1795bbc846..7fa6055b161 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -1,6 +1,6 @@ module github.com/smartcontractkit/chainlink/core/scripts -go 1.21 +go 1.21.3 // Make sure we're working with the latest chainlink libs replace github.com/smartcontractkit/chainlink/v2 => ../../ @@ -306,6 +306,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index cd7adf6bee3..7706fa15f19 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1508,6 +1508,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f924 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= diff --git a/core/services/ocr2/plugins/median/plugin.go b/core/services/ocr2/plugins/median/plugin.go deleted file mode 100644 index 11197f09175..00000000000 --- a/core/services/ocr2/plugins/median/plugin.go +++ /dev/null @@ -1,67 +0,0 @@ -package median - -import ( - "context" - - "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" - ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/loop" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types" -) - -type Plugin struct { - loop.Plugin - stop services.StopChan -} - -func NewPlugin(lggr logger.Logger) *Plugin { - return &Plugin{Plugin: loop.Plugin{Logger: lggr}, stop: make(services.StopChan)} -} - -func (p *Plugin) NewMedianFactory(ctx context.Context, provider types.MedianProvider, dataSource, juelsPerFeeCoin median.DataSource, errorLog loop.ErrorLog) (loop.ReportingPluginFactory, error) { - var ctxVals loop.ContextValues - ctxVals.SetValues(ctx) - lggr := logger.With(p.Logger, ctxVals.Args()...) - factory := median.NumericalMedianFactory{ - ContractTransmitter: provider.MedianContract(), - DataSource: dataSource, - JuelsPerFeeCoinDataSource: juelsPerFeeCoin, - Logger: logger.NewOCRWrapper(lggr, true, func(msg string) { - ctx, cancelFn := p.stop.NewCtx() - defer cancelFn() - if err := errorLog.SaveError(ctx, msg); err != nil { - lggr.Errorw("Unable to save error", "err", msg) - } - }), - OnchainConfigCodec: provider.OnchainConfigCodec(), - ReportCodec: provider.ReportCodec(), - } - s := &reportingPluginFactoryService{lggr: logger.Named(lggr, "ReportingPluginFactory"), ReportingPluginFactory: factory} - - p.SubService(s) - - return s, nil -} - -type reportingPluginFactoryService struct { - services.StateMachine - lggr logger.Logger - ocrtypes.ReportingPluginFactory -} - -func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() } - -func (r *reportingPluginFactoryService) Start(ctx context.Context) error { - return r.StartOnce("ReportingPluginFactory", func() error { return nil }) -} - -func (r *reportingPluginFactoryService) Close() error { - return r.StopOnce("ReportingPluginFactory", func() error { return nil }) -} - -func (r *reportingPluginFactoryService) HealthReport() map[string]error { - return map[string]error{r.Name(): r.Healthy()} -} diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 9d65921ef2b..896bbacb419 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink-feeds/median" "github.com/smartcontractkit/chainlink/v2/core/config/env" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -125,7 +126,7 @@ func NewMedianServices(ctx context.Context, argsNoPlugin.ReportingPluginFactory = median srvs = append(srvs, median) } else { - argsNoPlugin.ReportingPluginFactory, err = NewPlugin(lggr).NewMedianFactory(ctx, medianProvider, dataSource, juelsPerFeeCoinSource, errorLog) + argsNoPlugin.ReportingPluginFactory, err = median.NewPlugin(lggr).NewMedianFactory(ctx, medianProvider, dataSource, juelsPerFeeCoinSource, errorLog) if err != nil { err = fmt.Errorf("failed to create median factory: %w", err) abort() diff --git a/go.mod b/go.mod index b2afd2e2111..0133011813e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/smartcontractkit/chainlink/v2 -go 1.21 +go 1.21.3 require ( github.com/Depado/ginprom v1.7.11 @@ -68,6 +68,7 @@ require ( github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 + github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 diff --git a/go.sum b/go.sum index 98371af2e2f..504f2ba8073 100644 --- a/go.sum +++ b/go.sum @@ -1511,6 +1511,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f924 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 4cf6a502c49..63bf5e9e55f 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -1,6 +1,6 @@ module github.com/smartcontractkit/chainlink/integration-tests -go 1.21 +go 1.21.3 // Make sure we're working with the latest chainlink libs replace github.com/smartcontractkit/chainlink/v2 => ../ @@ -390,6 +390,7 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect + github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ef0a5b66ffb..a1a272c0b8b 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2380,6 +2380,8 @@ github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f924 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= +github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= diff --git a/plugins/chainlink.Dockerfile b/plugins/chainlink.Dockerfile index f07fab48122..2f3e46e9ce1 100644 --- a/plugins/chainlink.Dockerfile +++ b/plugins/chainlink.Dockerfile @@ -16,12 +16,12 @@ COPY . . # Build the golang binaries RUN make install-chainlink -# Build LOOP Plugins -RUN make install-median # Install medianpoc binary RUN make install-medianpoc +# Link LOOP Plugin source dirs with simple names +RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-feeds | xargs -I % ln -s % /chainlink-feeds RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-solana | xargs -I % ln -s % /chainlink-solana RUN mkdir /chainlink-starknet RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-starknet/relayer | xargs -I % ln -s % /chainlink-starknet/relayer @@ -30,6 +30,10 @@ RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-starknet/rela FROM golang:1.21-bullseye as buildplugins RUN go version +WORKDIR /chainlink-feeds +COPY --from=buildgo /chainlink-feeds . +RUN go install ./cmd/chainlink-feeds + WORKDIR /chainlink-solana COPY --from=buildgo /chainlink-solana . RUN go install ./pkg/solana/cmd/chainlink-solana @@ -53,9 +57,9 @@ RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ COPY --from=buildgo /go/bin/chainlink /usr/local/bin/ COPY --from=buildgo /go/bin/chainlink-medianpoc /usr/local/bin/ -COPY --from=buildgo /go/bin/chainlink-median /usr/local/bin/ -ENV CL_MEDIAN_CMD chainlink-median +COPY --from=buildplugins /go/bin/chainlink-feeds /usr/local/bin/ +ENV CL_MEDIAN_CMD chainlink-feeds COPY --from=buildplugins /go/bin/chainlink-solana /usr/local/bin/ ENV CL_SOLANA_CMD chainlink-solana COPY --from=buildplugins /go/bin/chainlink-starknet /usr/local/bin/ diff --git a/plugins/cmd/chainlink-median/main.go b/plugins/cmd/chainlink-median/main.go deleted file mode 100644 index e95bfbb221b..00000000000 --- a/plugins/cmd/chainlink-median/main.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "github.com/hashicorp/go-plugin" - - "github.com/smartcontractkit/chainlink-common/pkg/loop" - - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" -) - -const ( - loggerName = "PluginMedian" -) - -func main() { - s := loop.MustNewStartedServer(loggerName) - defer s.Stop() - - p := median.NewPlugin(s.Logger) - defer s.Logger.ErrorIfFn(p.Close, "Failed to close") - - s.MustRegister(p) - - stop := make(chan struct{}) - defer close(stop) - - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: loop.PluginMedianHandshakeConfig(), - Plugins: map[string]plugin.Plugin{ - loop.PluginMedianName: &loop.GRPCPluginMedian{ - PluginServer: p, - BrokerConfig: loop.BrokerConfig{ - StopCh: stop, - Logger: s.Logger, - GRPCOpts: s.GRPCOpts, - }, - }, - }, - GRPCServer: s.GRPCOpts.NewServer, - }) -} From 25deefa0054d7a0dd2d729b61489e7107ee41d62 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko <34754799+dhaidashenko@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:07:18 +0100 Subject: [PATCH 209/271] Soft migration to Generalize Multinode client for EVM BCI-2286 (#11369) * Allow switching to Generalized MultiNode EVM client via feature flag * test how linter reacts to deprecation * deprecate evm.client * address review comments * revert adding feature flag * create new client * fix call context * unwrapp CallContext args * deprecation msg improvements --- core/chains/evm/client/chain_client.go | 2 +- core/chains/evm/client/client.go | 2 + core/chains/evm/client/node.go | 4 ++ core/chains/evm/client/node_fsm.go | 2 + .../evm/client/node_selector_highest_head.go | 1 + .../client/node_selector_priority_level.go | 1 + .../evm/client/node_selector_round_robin.go | 1 + .../client/node_selector_total_difficulty.go | 1 + core/chains/evm/client/pool.go | 9 +++ core/chains/evm/client/send_only_node.go | 4 ++ core/chains/legacyevm/chain.go | 60 ++++++------------- 11 files changed, 45 insertions(+), 42 deletions(-) diff --git a/core/chains/evm/client/chain_client.go b/core/chains/evm/client/chain_client.go index d17c55f2e4f..3efc5645e22 100644 --- a/core/chains/evm/client/chain_client.go +++ b/core/chains/evm/client/chain_client.go @@ -120,7 +120,7 @@ func (c *chainClient) BlockByNumber(ctx context.Context, number *big.Int) (b *ty } func (c *chainClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { - return c.multiNode.CallContext(ctx, result, method) + return c.multiNode.CallContext(ctx, result, method, args...) } func (c *chainClient) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index 91c8659d8c5..cccda9914fc 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -109,6 +109,8 @@ var _ htrktypes.Client[*evmtypes.Head, ethereum.Subscription, *big.Int, common.H // NewClientWithNodes instantiates a client from a list of nodes // Currently only supports one primary +// +// Deprecated: use [NewChainClient] func NewClientWithNodes(logger logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsThreshold time.Duration, primaryNodes []Node, sendOnlyNodes []SendOnlyNode, chainID *big.Int, chainType config.ChainType) (*client, error) { pool := NewPool(logger, selectionMode, leaseDuration, noNewHeadsThreshold, primaryNodes, sendOnlyNodes, chainID, chainType) return &client{ diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index 8bae566e273..e2da78b502a 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -84,6 +84,8 @@ var ( //go:generate mockery --quiet --name Node --output ../mocks/ --case=underscore // Node represents a client that connects to an ethereum-compatible RPC node +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.Node] type Node interface { Start(ctx context.Context) error Close() error @@ -179,6 +181,8 @@ type node struct { } // NewNode returns a new *node as Node +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewNode] func NewNode(nodeCfg config.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, wsuri url.URL, httpuri *url.URL, name string, id int32, chainID *big.Int, nodeOrder int32) Node { n := new(node) n.name = name diff --git a/core/chains/evm/client/node_fsm.go b/core/chains/evm/client/node_fsm.go index 2d74512eac9..10694eb7fc4 100644 --- a/core/chains/evm/client/node_fsm.go +++ b/core/chains/evm/client/node_fsm.go @@ -38,6 +38,8 @@ var ( // NodeState represents the current state of the node // Node is a FSM (finite state machine) +// +// Deprecated: to be removed. It is now internal in common/client type NodeState int func (n NodeState) String() string { diff --git a/core/chains/evm/client/node_selector_highest_head.go b/core/chains/evm/client/node_selector_highest_head.go index 9e88674fac5..2ed41486cff 100644 --- a/core/chains/evm/client/node_selector_highest_head.go +++ b/core/chains/evm/client/node_selector_highest_head.go @@ -6,6 +6,7 @@ import ( type highestHeadNodeSelector []Node +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewHighestHeadNodeSelector] func NewHighestHeadNodeSelector(nodes []Node) NodeSelector { return highestHeadNodeSelector(nodes) } diff --git a/core/chains/evm/client/node_selector_priority_level.go b/core/chains/evm/client/node_selector_priority_level.go index 7a5516dcd42..fba6d403327 100644 --- a/core/chains/evm/client/node_selector_priority_level.go +++ b/core/chains/evm/client/node_selector_priority_level.go @@ -16,6 +16,7 @@ type nodeWithPriority struct { priority int32 } +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewPriorityLevelNodeSelector] func NewPriorityLevelNodeSelector(nodes []Node) NodeSelector { return &priorityLevelNodeSelector{ nodes: nodes, diff --git a/core/chains/evm/client/node_selector_round_robin.go b/core/chains/evm/client/node_selector_round_robin.go index 9b2fa7508a7..3bd19f0ede4 100644 --- a/core/chains/evm/client/node_selector_round_robin.go +++ b/core/chains/evm/client/node_selector_round_robin.go @@ -7,6 +7,7 @@ type roundRobinSelector struct { roundRobinCount atomic.Uint32 } +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewRoundRobinSelector] func NewRoundRobinSelector(nodes []Node) NodeSelector { return &roundRobinSelector{ nodes: nodes, diff --git a/core/chains/evm/client/node_selector_total_difficulty.go b/core/chains/evm/client/node_selector_total_difficulty.go index a368422e49f..99a1c89dd4f 100644 --- a/core/chains/evm/client/node_selector_total_difficulty.go +++ b/core/chains/evm/client/node_selector_total_difficulty.go @@ -6,6 +6,7 @@ import ( type totalDifficultyNodeSelector []Node +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewTotalDifficultyNodeSelector] func NewTotalDifficultyNodeSelector(nodes []Node) NodeSelector { return totalDifficultyNodeSelector(nodes) } diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index ab190f8c6aa..9217f633dc3 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -39,6 +39,8 @@ const ( ) // NodeSelector represents a strategy to select the next node from the pool. +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NodeSelector] type NodeSelector interface { // Select returns a Node, or nil if none can be selected. // Implementation must be thread-safe. @@ -48,6 +50,8 @@ type NodeSelector interface { } // PoolConfig represents settings for the Pool +// +// Deprecated: to be removed type PoolConfig interface { NodeSelectionMode() string NodeNoNewHeadsThreshold() time.Duration @@ -56,6 +60,8 @@ type PoolConfig interface { // Pool represents an abstraction over one or more primary nodes // It is responsible for liveness checking and balancing queries across live nodes +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.MultiNode] type Pool struct { services.StateMachine nodes []Node @@ -76,6 +82,9 @@ type Pool struct { wg sync.WaitGroup } +// NewPool - creates new instance of [Pool] +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewMultiNode] func NewPool(lggr logger.Logger, selectionMode string, leaseDuration time.Duration, noNewHeadsTreshold time.Duration, nodes []Node, sendonlys []SendOnlyNode, chainID *big.Int, chainType config.ChainType) *Pool { if chainID == nil { panic("chainID is required") diff --git a/core/chains/evm/client/send_only_node.go b/core/chains/evm/client/send_only_node.go index 62a22ee1937..b6ad26696fc 100644 --- a/core/chains/evm/client/send_only_node.go +++ b/core/chains/evm/client/send_only_node.go @@ -21,6 +21,8 @@ import ( //go:generate mockery --quiet --name SendOnlyNode --output ../mocks/ --case=underscore // SendOnlyNode represents one ethereum node used as a sendonly +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.SendOnlyNode] type SendOnlyNode interface { // Start may attempt to connect to the node, but should only return error for misconfiguration - never for temporary errors. Start(context.Context) error @@ -73,6 +75,8 @@ type sendOnlyNode struct { } // NewSendOnlyNode returns a new sendonly node +// +// Deprecated: use [pkg/github.com/smartcontractkit/chainlink/v2/common/client.NewSendOnlyNode] func NewSendOnlyNode(lggr logger.Logger, httpuri url.URL, name string, chainID *big.Int) SendOnlyNode { s := new(sendOnlyNode) s.name = name diff --git a/core/chains/legacyevm/chain.go b/core/chains/legacyevm/chain.go index 0c35d929fc2..4b4c69f1ab6 100644 --- a/core/chains/legacyevm/chain.go +++ b/core/chains/legacyevm/chain.go @@ -17,6 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" + commonclient "github.com/smartcontractkit/chainlink/v2/common/client" commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -220,11 +221,7 @@ func newChain(ctx context.Context, cfg *evmconfig.ChainScoped, nodes []*toml.Nod if !cfg.EVMRPCEnabled() { client = evmclient.NewNullClient(chainID, l) } else if opts.GenEthClient == nil { - var err2 error - client, err2 = newEthClientFromChain(cfg.EVM().NodePool(), cfg.EVM().NodeNoNewHeadsThreshold(), l, chainID, chainType, nodes) - if err2 != nil { - return nil, fmt.Errorf("failed to instantiate eth client for chain with ID %s: %w", cfg.EVM().ChainID().String(), err2) - } + client = newEthClientFromCfg(cfg.EVM().NodePool(), cfg.EVM().NodeNoNewHeadsThreshold(), l, chainID, chainType, nodes) } else { client = opts.GenEthClient(chainID) } @@ -474,46 +471,27 @@ func (c *chain) Logger() logger.Logger { return c.logger } func (c *chain) BalanceMonitor() monitor.BalanceMonitor { return c.balanceMonitor } func (c *chain) GasEstimator() gas.EvmFeeEstimator { return c.gasEstimator } -func newEthClientFromChain(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType commonconfig.ChainType, nodes []*toml.Node) (evmclient.Client, error) { - var primaries []evmclient.Node - var sendonlys []evmclient.SendOnlyNode +func newEthClientFromCfg(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType commonconfig.ChainType, nodes []*toml.Node) evmclient.Client { + var empty url.URL + var primaries []commonclient.Node[*big.Int, *evmtypes.Head, evmclient.RPCCLient] + var sendonlys []commonclient.SendOnlyNode[*big.Int, evmclient.RPCCLient] for i, node := range nodes { if node.SendOnly != nil && *node.SendOnly { - sendonly := evmclient.NewSendOnlyNode(lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID) + name := fmt.Sprintf("eth-sendonly-rpc-%d", i) + rpc := evmclient.NewRPCClient(lggr, empty, (*url.URL)(node.HTTPURL), name, int32(i), chainID, + commonclient.Secondary) + sendonly := commonclient.NewSendOnlyNode[*big.Int, evmclient.RPCCLient](lggr, (url.URL)(*node.HTTPURL), + *node.Name, chainID, rpc) sendonlys = append(sendonlys, sendonly) } else { - primary, err := newPrimary(cfg, noNewHeadsThreshold, lggr, node, int32(i), chainID) - if err != nil { - return nil, err - } - primaries = append(primaries, primary) + name := fmt.Sprintf("eth-primary-rpc-%d", i) + rpc := evmclient.NewRPCClient(lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), name, int32(i), + chainID, commonclient.Primary) + primaryNode := commonclient.NewNode[*big.Int, *evmtypes.Head, evmclient.RPCCLient](cfg, noNewHeadsThreshold, + lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, *node.Order, + rpc, "EVM") + primaries = append(primaries, primaryNode) } } - return evmclient.NewClientWithNodes(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), noNewHeadsThreshold, primaries, sendonlys, chainID, chainType) + return evmclient.NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), noNewHeadsThreshold, primaries, sendonlys, chainID, chainType) } - -func newPrimary(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, n *toml.Node, id int32, chainID *big.Int) (evmclient.Node, error) { - if n.SendOnly != nil && *n.SendOnly { - return nil, errors.New("cannot cast send-only node to primary") - } - - return evmclient.NewNode(cfg, noNewHeadsThreshold, lggr, (url.URL)(*n.WSURL), (*url.URL)(n.HTTPURL), *n.Name, id, chainID, *n.Order), nil -} - -// TODO-1663: replace newEthClientFromChain with the function below once client.go is deprecated. -//func newEthClientFromChain(cfg evmconfig.NodePool, noNewHeadsThreshold time.Duration, lggr logger.Logger, chainID *big.Int, chainType config.ChainType, nodes []*toml.Node) evmclient.Client { -// var empty url.URL -// var primaries []commonclient.Node[*big.Int, *evmtypes.Head, evmclient.RPCCLient] -// var sendonlys []commonclient.SendOnlyNode[*big.Int, evmclient.RPCCLient] -// for i, node := range nodes { -// if node.SendOnly != nil && *node.SendOnly { -// rpc := evmclient.NewRPCClient(lggr, empty, (*url.URL)(node.HTTPURL), fmt.Sprintf("eth-sendonly-rpc-%d", i), int32(i), chainID, commontypes.Primary) -// sendonly := commonclient.NewSendOnlyNode[*big.Int, evmclient.RPCCLient](lggr, (url.URL)(*node.HTTPURL), *node.Name, chainID, rpc) -// sendonlys = append(sendonlys, sendonly) -// } else { -// rpc := evmclient.NewRPCClient(lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), fmt.Sprintf("eth-sendonly-rpc-%d", i), int32(i), chainID, commontypes.Primary) -// primaries = append(primaries, commonclient.NewNode[*big.Int, *evmtypes.Head, evmclient.RPCCLient](cfg, noNewHeadsThreshold, lggr, (url.URL)(*node.WSURL), (*url.URL)(node.HTTPURL), *node.Name, int32(i), chainID, *node.Order, rpc, "EVM")) -// } -// } -// return evmclient.NewChainClient(lggr, cfg.SelectionMode(), cfg.LeaseDuration(), noNewHeadsThreshold, primaries, sendonlys, chainID, chainType) -//} From 10588c7a68f45db03012b9c1cff1d2bc70fe749c Mon Sep 17 00:00:00 2001 From: Tate Date: Tue, 28 Nov 2023 09:37:38 -0700 Subject: [PATCH 210/271] Add loud error for failure to get the solana sha (#11400) --- .github/workflows/integration-tests.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 855e4ea9365..5a505ce01ca 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -623,6 +623,10 @@ jobs: id: getshortsha run: | sol_ver=$(go list -m -json github.com/smartcontractkit/chainlink-solana | jq -r .Version) + if [ -z "${sol_ver}" ]; then + echo "Error: could not get the solana version from the go.mod file, look above for error(s)" + exit 1 + fi short_sha="${sol_ver##*-}" echo "short sha is: ${short_sha}" echo "short_sha=${short_sha}" >> "$GITHUB_OUTPUT" @@ -638,6 +642,10 @@ jobs: run: | cd solanapath full_sha=$(git rev-parse ${{steps.getshortsha.outputs.short_sha}}) + if [ -z "${full_sha}" ]; then + echo "Error: could not get the full sha from the short sha using git, look above for error(s)" + exit 1 + fi echo "sha is: ${full_sha}" echo "sha=${full_sha}" >> "$GITHUB_OUTPUT" From f47e29094b8093c1beef0a4654eee92d026e7980 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 28 Nov 2023 11:45:47 -0500 Subject: [PATCH 211/271] Feed LatestPrice query cache (#11326) * Price retrieval cache for Mercury Problem: Core nodes are querying LatestReport a lot (~70rps). This is not only wasteful, it potentially increases latency if Mercury server falls behind and is also completely unnecessary. We do not need such up-to-the-millisecond accuracy for billing, Proposed solution: Introduce a price cache for mercury with a TTL e.g. of 1 minute or whatever tolerance we have for billing price and only fetch from the server when the cache becomes stale. * fix * Some PR comments * Fix more PR comments * PR comments * lint --- core/cmd/shell.go | 11 + core/config/docs/core.toml | 23 + core/config/mercury_config.go | 13 +- core/config/toml/types.go | 28 ++ core/internal/cltest/cltest.go | 11 + core/services/chainlink/application.go | 8 + core/services/chainlink/config_general.go | 2 +- core/services/chainlink/config_mercury.go | 24 ++ core/services/chainlink/config_test.go | 13 + core/services/chainlink/relayer_factory.go | 3 + .../testdata/config-empty-effective.toml | 6 + .../chainlink/testdata/config-full.toml | 6 + .../config-multi-chain-effective.toml | 6 + core/services/ocr2/delegate.go | 1 + core/services/relay/evm/evm.go | 8 +- .../services/relay/evm/mercury/utils/feeds.go | 4 + .../relay/evm/mercury/wsrpc/cache/cache.go | 395 ++++++++++++++++++ .../evm/mercury/wsrpc/cache/cache_set.go | 120 ++++++ .../evm/mercury/wsrpc/cache/cache_set_test.go | 48 +++ .../evm/mercury/wsrpc/cache/cache_test.go | 199 +++++++++ .../evm/mercury/wsrpc/cache/helpers_test.go | 38 ++ .../relay/evm/mercury/wsrpc/client.go | 49 ++- .../relay/evm/mercury/wsrpc/client_test.go | 144 ++++++- .../relay/evm/mercury/wsrpc/mocks/mocks.go | 3 + core/services/relay/evm/mercury/wsrpc/pool.go | 15 +- .../relay/evm/mercury/wsrpc/pool_test.go | 16 +- core/utils/hash.go | 20 + .../testdata/config-empty-effective.toml | 6 + core/web/resolver/testdata/config-full.toml | 6 + .../config-multi-chain-effective.toml | 6 + docs/CHANGELOG.md | 33 +- docs/CONFIG.md | 45 ++ testdata/scripts/node/validate/default.txtar | 6 + .../disk-based-logging-disabled.txtar | 6 + .../validate/disk-based-logging-no-dir.txtar | 6 + .../node/validate/disk-based-logging.txtar | 6 + testdata/scripts/node/validate/invalid.txtar | 6 + testdata/scripts/node/validate/valid.txtar | 6 + testdata/scripts/node/validate/warnings.txtar | 6 + 39 files changed, 1322 insertions(+), 30 deletions(-) create mode 100644 core/services/relay/evm/mercury/wsrpc/cache/cache.go create mode 100644 core/services/relay/evm/mercury/wsrpc/cache/cache_set.go create mode 100644 core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go create mode 100644 core/services/relay/evm/mercury/wsrpc/cache/cache_test.go create mode 100644 core/services/relay/evm/mercury/wsrpc/cache/helpers_test.go diff --git a/core/cmd/shell.go b/core/cmd/shell.go index 35659aa7797..daf936b36c8 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -43,6 +43,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore" "github.com/smartcontractkit/chainlink/v2/core/services/periodicbackup" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/versioning" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" "github.com/smartcontractkit/chainlink/v2/core/sessions" @@ -157,11 +159,19 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G eventBroadcaster := pg.NewEventBroadcaster(cfg.Database().URL(), dbListener.MinReconnectInterval(), dbListener.MaxReconnectDuration(), appLggr, cfg.AppID()) loopRegistry := plugins.NewLoopRegistry(appLggr, cfg.Tracing()) + mercuryPool := wsrpc.NewPool(appLggr, cache.Config{ + Logger: appLggr, + LatestReportTTL: cfg.Mercury().Cache().LatestReportTTL(), + MaxStaleAge: cfg.Mercury().Cache().MaxStaleAge(), + LatestReportDeadline: cfg.Mercury().Cache().LatestReportDeadline(), + }) + // create the relayer-chain interoperators from application configuration relayerFactory := chainlink.RelayerFactory{ Logger: appLggr, LoopRegistry: loopRegistry, GRPCOpts: grpcOpts, + MercuryPool: mercuryPool, } evmFactoryCfg := chainlink.EVMFactoryConfig{ @@ -227,6 +237,7 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G SecretGenerator: chainlink.FilePersistedSecretGenerator{}, LoopRegistry: loopRegistry, GRPCOpts: grpcOpts, + MercuryPool: mercuryPool, }) } diff --git a/core/config/docs/core.toml b/core/config/docs/core.toml index 79801c2c52b..e438f4553fe 100644 --- a/core/config/docs/core.toml +++ b/core/config/docs/core.toml @@ -601,3 +601,26 @@ TLSCertPath = '/path/to/cert.pem' # Example [Tracing.Attributes] # env is an example user specified key-value pair env = 'test' # Example + +[Mercury] + +# Mercury.Cache controls settings for the price retrieval cache querying a mercury server +[Mercury.Cache] +# LatestReportTTL controls how "stale" we will allow a price to be e.g. if +# set to 1s, a new price will always be fetched if the last result was +# from 1 second ago or older. +# +# Another way of looking at it is such: the cache will _never_ return a +# price that was queried from now-LatestReportTTL or before. +# +# Setting to zero disables caching entirely. +LatestReportTTL = "1s" # Default +# MaxStaleAge is that maximum amount of time that a value can be stale +# before it is deleted from the cache (a form of garbage collection). +# +# This should generally be set to something much larger than +# LatestReportTTL. Setting to zero disables garbage collection. +MaxStaleAge = "1h" # Default +# LatestReportDeadline controls how long to wait for a response from the +# mercury server before retrying. Setting this to zero will wait indefinitely. +LatestReportDeadline = "5s" # Default diff --git a/core/config/mercury_config.go b/core/config/mercury_config.go index d7e985d14e6..e530af6338f 100644 --- a/core/config/mercury_config.go +++ b/core/config/mercury_config.go @@ -1,7 +1,18 @@ package config -import ocr2models "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" +import ( + "time" + + ocr2models "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" +) + +type MercuryCache interface { + LatestReportTTL() time.Duration + MaxStaleAge() time.Duration + LatestReportDeadline() time.Duration +} type Mercury interface { Credentials(credName string) *ocr2models.MercuryCredentials + Cache() MercuryCache } diff --git a/core/config/toml/types.go b/core/config/toml/types.go index 31076c1f1de..c420d7f3f47 100644 --- a/core/config/toml/types.go +++ b/core/config/toml/types.go @@ -54,6 +54,7 @@ type Core struct { Sentry Sentry `toml:",omitempty"` Insecure Insecure `toml:",omitempty"` Tracing Tracing `toml:",omitempty"` + Mercury Mercury `toml:",omitempty"` } // SetFrom updates c with any non-nil values from f. (currently TOML field only!) @@ -82,6 +83,7 @@ func (c *Core) SetFrom(f *Core) { c.OCR.setFrom(&f.OCR) c.P2P.setFrom(&f.P2P) c.Keeper.setFrom(&f.Keeper) + c.Mercury.setFrom(&f.Mercury) c.AutoPprof.setFrom(&f.AutoPprof) c.Pyroscope.setFrom(&f.Pyroscope) @@ -1358,6 +1360,32 @@ func (ins *Insecure) setFrom(f *Insecure) { } } +type MercuryCache struct { + LatestReportTTL *models.Duration + MaxStaleAge *models.Duration + LatestReportDeadline *models.Duration +} + +func (mc *MercuryCache) setFrom(f *MercuryCache) { + if v := f.LatestReportTTL; v != nil { + mc.LatestReportTTL = v + } + if v := f.MaxStaleAge; v != nil { + mc.MaxStaleAge = v + } + if v := f.LatestReportDeadline; v != nil { + mc.LatestReportDeadline = v + } +} + +type Mercury struct { + Cache MercuryCache `toml:",omitempty"` +} + +func (m *Mercury) setFrom(f *Mercury) { + m.Cache.setFrom(&f.Cache) +} + type MercuryCredentials struct { // LegacyURL is the legacy base URL for mercury v0.2 API LegacyURL *models.SecretURL diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index 17a1d4fadd4..fc648486a03 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -76,6 +76,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/vrfkey" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" clsessions "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/smartcontractkit/chainlink/v2/core/static" @@ -342,10 +344,18 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn mailMon := utils.NewMailboxMonitor(cfg.AppID().String()) loopRegistry := plugins.NewLoopRegistry(lggr, nil) + mercuryPool := wsrpc.NewPool(lggr, cache.Config{ + Logger: lggr, + LatestReportTTL: cfg.Mercury().Cache().LatestReportTTL(), + MaxStaleAge: cfg.Mercury().Cache().MaxStaleAge(), + LatestReportDeadline: cfg.Mercury().Cache().LatestReportDeadline(), + }) + relayerFactory := chainlink.RelayerFactory{ Logger: lggr, LoopRegistry: loopRegistry, GRPCOpts: loop.GRPCOpts{}, + MercuryPool: mercuryPool, } evmOpts := chainlink.EVMFactoryConfig{ @@ -419,6 +429,7 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn UnrestrictedHTTPClient: c, SecretGenerator: MockSecretGenerator{}, LoopRegistry: plugins.NewLoopRegistry(lggr, nil), + MercuryPool: mercuryPool, }) require.NoError(t, err) app := appInstance.(*chainlink.ChainlinkApplication) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 29679ee92fb..1ecf95d3a5e 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -48,6 +48,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/promreporter" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/core/services/telemetry" "github.com/smartcontractkit/chainlink/v2/core/services/vrf" "github.com/smartcontractkit/chainlink/v2/core/services/webhook" @@ -160,6 +161,7 @@ type ApplicationOpts struct { SecretGenerator SecretGenerator LoopRegistry *plugins.LoopRegistry GRPCOpts loop.GRPCOpts + MercuryPool wsrpc.Pool } // NewApplication initializes a new store if one is not already @@ -241,6 +243,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { promReporter := promreporter.NewPromReporter(db.DB, globalLogger) srvcs = append(srvcs, promReporter) + // pool must be started before all relayers and stopped after them + srvcs = append(srvcs, opts.MercuryPool) + // EVM chains are used all over the place. This will need to change for fully EVM extraction // TODO: BCF-2510, BCF-2511 @@ -457,6 +462,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { } for _, s := range srvcs { + if s == nil { + panic("service unexpectedly nil") + } if err := healthChecker.Register(s); err != nil { return nil, err } diff --git a/core/services/chainlink/config_general.go b/core/services/chainlink/config_general.go index fff7822a814..c7d9cc6ce5d 100644 --- a/core/services/chainlink/config_general.go +++ b/core/services/chainlink/config_general.go @@ -507,7 +507,7 @@ func (g *generalConfig) Prometheus() coreconfig.Prometheus { } func (g *generalConfig) Mercury() coreconfig.Mercury { - return &mercuryConfig{s: g.secrets.Mercury} + return &mercuryConfig{c: g.c.Mercury, s: g.secrets.Mercury} } func (g *generalConfig) Threshold() coreconfig.Threshold { diff --git a/core/services/chainlink/config_mercury.go b/core/services/chainlink/config_mercury.go index 1a20dd069d8..61e48d340e4 100644 --- a/core/services/chainlink/config_mercury.go +++ b/core/services/chainlink/config_mercury.go @@ -1,11 +1,31 @@ package chainlink import ( + "time" + + "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" ) +var _ config.MercuryCache = (*mercuryCacheConfig)(nil) + +type mercuryCacheConfig struct { + c toml.MercuryCache +} + +func (m *mercuryCacheConfig) LatestReportTTL() time.Duration { + return m.c.LatestReportTTL.Duration() +} +func (m *mercuryCacheConfig) MaxStaleAge() time.Duration { + return m.c.MaxStaleAge.Duration() +} +func (m *mercuryCacheConfig) LatestReportDeadline() time.Duration { + return m.c.LatestReportDeadline.Duration() +} + type mercuryConfig struct { + c toml.Mercury s toml.MercurySecrets } @@ -23,3 +43,7 @@ func (m *mercuryConfig) Credentials(credName string) *models.MercuryCredentials } return nil } + +func (m *mercuryConfig) Cache() config.MercuryCache { + return &mercuryCacheConfig{c: m.c.Cache} +} diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index 33fda221285..d777e34abf7 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -668,6 +668,13 @@ func TestConfig_Marshal(t *testing.T) { }, }, } + full.Mercury = toml.Mercury{ + Cache: toml.MercuryCache{ + LatestReportTTL: models.MustNewDuration(100 * time.Second), + MaxStaleAge: models.MustNewDuration(101 * time.Second), + LatestReportDeadline: models.MustNewDuration(102 * time.Second), + }, + } for _, tt := range []struct { name string @@ -1103,6 +1110,12 @@ ConfirmationPoll = '42s' [[Starknet.Nodes]] Name = 'primary' URL = 'http://stark.node' +`}, + {"Mercury", Config{Core: toml.Core{Mercury: full.Mercury}}, `[Mercury] +[Mercury.Cache] +LatestReportTTL = '1m40s' +MaxStaleAge = '1m41s' +LatestReportDeadline = '1m42s' `}, {"full", full, fullTOML}, {"multi-chain", multiChain, multiChainTOML}, diff --git a/core/services/chainlink/relayer_factory.go b/core/services/chainlink/relayer_factory.go index c15643551e9..8b8749013fc 100644 --- a/core/services/chainlink/relayer_factory.go +++ b/core/services/chainlink/relayer_factory.go @@ -25,6 +25,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/services/relay" evmrelay "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc" "github.com/smartcontractkit/chainlink/v2/plugins" ) @@ -32,6 +33,7 @@ type RelayerFactory struct { logger.Logger *plugins.LoopRegistry loop.GRPCOpts + MercuryPool wsrpc.Pool } type EVMFactoryConfig struct { @@ -68,6 +70,7 @@ func (r *RelayerFactory) NewEVM(ctx context.Context, config EVMFactoryConfig) (m QConfig: ccOpts.AppConfig.Database(), CSAETHKeystore: config.CSAETHKeystore, EventBroadcaster: ccOpts.EventBroadcaster, + MercuryPool: r.MercuryPool, } relayer, err2 := evmrelay.NewRelayer(r.Logger.Named("EVM").Named(relayID.ChainID), chain, relayerOpts) if err2 != nil { diff --git a/core/services/chainlink/testdata/config-empty-effective.toml b/core/services/chainlink/testdata/config-empty-effective.toml index 8f3135b34e4..2531e7c281d 100644 --- a/core/services/chainlink/testdata/config-empty-effective.toml +++ b/core/services/chainlink/testdata/config-empty-effective.toml @@ -234,3 +234,9 @@ NodeID = '' SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' + +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index eca5f6f96d2..8036165d6e8 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -245,6 +245,12 @@ TLSCertPath = '/path/to/cert.pem' env = 'dev' test = 'load' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1m40s' +MaxStaleAge = '1m41s' +LatestReportDeadline = '1m42s' + [[EVM]] ChainID = '1' Enabled = false diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 6a60dfd419a..371cc50a170 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -235,6 +235,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 45ee4e0f8fb..eebc95903ed 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -189,6 +189,7 @@ type jobPipelineConfig interface { type mercuryConfig interface { Credentials(credName string) *models.MercuryCredentials + Cache() coreconfig.MercuryCache } type thresholdConfig interface { diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 952c1869bfa..088a69a2582 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -66,6 +66,7 @@ type RelayerOpts struct { pg.QConfig CSAETHKeystore pg.EventBroadcaster + MercuryPool wsrpc.Pool } func (c RelayerOpts) Validate() error { @@ -100,7 +101,7 @@ func NewRelayer(lggr logger.Logger, chain legacyevm.Chain, opts RelayerOpts) (*R chain: chain, lggr: lggr, ks: opts.CSAETHKeystore, - mercuryPool: wsrpc.NewPool(lggr), + mercuryPool: opts.MercuryPool, eventBroadcaster: opts.EventBroadcaster, pgCfg: opts.QConfig, }, nil @@ -116,17 +117,16 @@ func (r *Relayer) Start(context.Context) error { } func (r *Relayer) Close() error { - return r.mercuryPool.Close() + return nil } // Ready does noop: always ready func (r *Relayer) Ready() error { - return r.mercuryPool.Ready() + return nil } func (r *Relayer) HealthReport() (report map[string]error) { report = make(map[string]error) - services.CopyHealth(report, r.mercuryPool.HealthReport()) return } diff --git a/core/services/relay/evm/mercury/utils/feeds.go b/core/services/relay/evm/mercury/utils/feeds.go index b1a20618ef6..6f8978bbf0d 100644 --- a/core/services/relay/evm/mercury/utils/feeds.go +++ b/core/services/relay/evm/mercury/utils/feeds.go @@ -88,6 +88,10 @@ const ( type FeedID [32]byte +func BytesToFeedID(b []byte) FeedID { + return (FeedID)(utils.BytesToHash(b)) +} + func (f FeedID) Hex() string { return (utils.Hash)(f).Hex() } func (f FeedID) String() string { return (utils.Hash)(f).String() } diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache.go b/core/services/relay/evm/mercury/wsrpc/cache/cache.go new file mode 100644 index 00000000000..4962210d58f --- /dev/null +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache.go @@ -0,0 +1,395 @@ +package cache + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "github.com/jpillora/backoff" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +var ( + promFetchFailedCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_cache_fetch_failure_count", + Help: "Number of times we tried to call LatestReport from the mercury server, but some kind of error occurred", + }, + []string{"serverURL", "feedID"}, + ) + promCacheHitCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_cache_hit_count", + Help: "Running count of cache hits", + }, + []string{"serverURL", "feedID"}, + ) + promCacheWaitCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_cache_wait_count", + Help: "Running count of times that we had to wait for a fetch to complete before reading from cache", + }, + []string{"serverURL", "feedID"}, + ) + promCacheMissCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "mercury_cache_miss_count", + Help: "Running count of cache misses", + }, + []string{"serverURL", "feedID"}, + ) +) + +type Fetcher interface { + LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) +} + +type Client interface { + Fetcher + ServerURL() string + RawClient() pb.MercuryClient +} + +// Cache is scoped to one particular mercury server +// Use CacheSet to hold lookups for multiple servers +type Cache interface { + Fetcher + services.Service +} + +type Config struct { + Logger logger.Logger + // LatestReportTTL controls how "stale" we will allow a price to be e.g. if + // set to 1s, a new price will always be fetched if the last result was + // from more than 1 second ago. + // + // Another way of looking at it is such: the cache will _never_ return a + // price that was queried from before now-LatestReportTTL. + // + // Setting to zero disables caching entirely. + LatestReportTTL time.Duration + // MaxStaleAge is that maximum amount of time that a value can be stale + // before it is deleted from the cache (a form of garbage collection). + // + // This should generally be set to something much larger than + // LatestReportTTL. Setting to zero disables garbage collection. + MaxStaleAge time.Duration + // LatestReportDeadline controls how long to wait for a response before + // retrying. Setting this to zero will wait indefinitely. + LatestReportDeadline time.Duration +} + +func NewCache(client Client, cfg Config) Cache { + return newMemCache(client, cfg) +} + +type cacheVal struct { + sync.RWMutex + + fetching bool + fetchCh chan (struct{}) + + val *pb.LatestReportResponse + err error + + expiresAt time.Time +} + +func (v *cacheVal) read() (*pb.LatestReportResponse, error) { + v.RLock() + defer v.RUnlock() + return v.val, v.err +} + +// caller expected to hold lock +func (v *cacheVal) initiateFetch() <-chan struct{} { + if v.fetching { + panic("cannot initiateFetch on cache val that is already fetching") + } + v.fetching = true + v.fetchCh = make(chan struct{}) + return v.fetchCh +} + +func (v *cacheVal) setError(err error) { + v.Lock() + defer v.Unlock() + v.err = err +} + +func (v *cacheVal) completeFetch(val *pb.LatestReportResponse, err error, expiresAt time.Time) { + v.Lock() + defer v.Unlock() + if !v.fetching { + panic("can only completeFetch on cache val that is fetching") + } + v.val = val + v.err = err + if err == nil { + v.expiresAt = expiresAt + } + close(v.fetchCh) + v.fetchCh = nil + v.fetching = false +} + +func (v *cacheVal) abandonFetch(err error) { + v.completeFetch(nil, err, time.Now()) +} + +func (v *cacheVal) waitForResult(ctx context.Context, chResult <-chan struct{}, chStop <-chan struct{}) (*pb.LatestReportResponse, error) { + select { + case <-ctx.Done(): + _, err := v.read() + return nil, errors.Join(err, ctx.Err()) + case <-chStop: + return nil, errors.New("stopped") + case <-chResult: + return v.read() + } +} + +// memCache stores values in memory +// it will never return a stale value older than latestPriceTTL, instead +// waiting for a successful fetch or caller context cancels, whichever comes +// first +type memCache struct { + services.StateMachine + lggr logger.Logger + + client Client + + latestPriceTTL time.Duration + maxStaleAge time.Duration + latestReportDeadline time.Duration + + cache sync.Map + + wg sync.WaitGroup + chStop services.StopChan +} + +func newMemCache(client Client, cfg Config) *memCache { + return &memCache{ + services.StateMachine{}, + cfg.Logger.Named("MercuryMemCache"), + client, + cfg.LatestReportTTL, + cfg.MaxStaleAge, + cfg.LatestReportDeadline, + sync.Map{}, + sync.WaitGroup{}, + make(chan (struct{})), + } +} + +// LatestReport +// NOTE: This will actually block on all types of errors, even non-timeouts. +// Context should be set carefully and timed to be the maximum time we are +// willing to wait for a result, the background thread will keep re-querying +// until it gets one even on networking errors etc. +func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) { + if req == nil { + return nil, errors.New("req must not be nil") + } + if m.latestPriceTTL <= 0 { + return m.client.RawClient().LatestReport(ctx, req) + } + vi, _ := m.cache.LoadOrStore(req, &cacheVal{ + sync.RWMutex{}, + false, + nil, + nil, + nil, + time.Now(), // first result is always "expired" and requires fetch + }) + v := vi.(*cacheVal) + + // HOT PATH + v.RLock() + if time.Now().Before(v.expiresAt) { + // CACHE HIT + promCacheHitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + + defer v.RUnlock() + return v.val, nil + } else if v.fetching { + // CACHE WAIT + promCacheWaitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + // if someone else is fetching then wait for the fetch to complete + ch := v.fetchCh + v.RUnlock() + return v.waitForResult(ctx, ch, m.chStop) + } + // CACHE MISS + promCacheMissCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + // fallthrough to cold path and fetch + v.RUnlock() + + // COLD PATH + v.Lock() + if time.Now().Before(v.expiresAt) { + // CACHE HIT + promCacheHitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + defer v.RUnlock() + return v.val, nil + } else if v.fetching { + // CACHE WAIT + promCacheWaitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + // if someone else is fetching then wait for the fetch to complete + ch := v.fetchCh + v.Unlock() + return v.waitForResult(ctx, ch, m.chStop) + } + // CACHE MISS + promCacheMissCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + // initiate the fetch and wait for result + ch := v.initiateFetch() + v.Unlock() + + ok := m.IfStarted(func() { + m.wg.Add(1) + go m.fetch(req, v) + }) + if !ok { + err := fmt.Errorf("memCache must be started, but is: %v", m.State()) + v.abandonFetch(err) + return nil, err + } + return v.waitForResult(ctx, ch, m.chStop) +} + +const minBackoffRetryInterval = 50 * time.Millisecond + +// newBackoff creates a backoff for retrying +func (m *memCache) newBackoff() backoff.Backoff { + min := minBackoffRetryInterval + max := m.latestPriceTTL / 2 + if min > max { + // avoid setting a min that is greater than max + min = max + } + return backoff.Backoff{ + Min: min, + Max: max, + Factor: 2, + Jitter: true, + } +} + +// fetch continually tries to call FetchLatestReport and write the result to v +// it writes errors as they come up +func (m *memCache) fetch(req *pb.LatestReportRequest, v *cacheVal) { + defer m.wg.Done() + b := m.newBackoff() + memcacheCtx, cancel := m.chStop.NewCtx() + defer cancel() + var t time.Time + var val *pb.LatestReportResponse + var err error + defer func() { + v.completeFetch(val, err, t.Add(m.latestPriceTTL)) + }() + + for { + t = time.Now() + + ctx := memcacheCtx + cancel := func() {} + if m.latestReportDeadline > 0 { + ctx, cancel = context.WithTimeoutCause(memcacheCtx, m.latestReportDeadline, errors.New("latest report fetch deadline exceeded")) + } + + // NOTE: must drop down to RawClient here otherwise we enter an + // infinite loop of calling a client that calls back to this same cache + // and on and on + val, err = m.client.RawClient().LatestReport(ctx, req) + cancel() + v.setError(err) + if memcacheCtx.Err() != nil { + // stopped + return + } else if err != nil { + m.lggr.Warnw("FetchLatestReport failed", "err", err) + promFetchFailedCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + select { + case <-m.chStop: + return + case <-time.After(b.Duration()): + continue + } + } + return + } +} + +func (m *memCache) Start(context.Context) error { + return m.StartOnce(m.Name(), func() error { + m.wg.Add(1) + go m.runloop() + return nil + }) +} + +func (m *memCache) runloop() { + defer m.wg.Done() + + if m.maxStaleAge == 0 { + return + } + t := time.NewTicker(utils.WithJitter(m.maxStaleAge)) + + for { + select { + case <-t.C: + m.cleanup() + t.Reset(utils.WithJitter(m.maxStaleAge)) + case <-m.chStop: + return + } + } +} + +// remove anything that has been stale for longer than maxStaleAge so that +// cache doesn't grow forever and cause memory leaks +// +// NOTE: This should be concurrent-safe with LatestReport. The only time they +// can race is if the cache item has expired past the stale age between +// creation of the cache item and start of fetch. This is unlikely, and even if +// it does occur, the worst case is that we discard a cache item early and +// double fetch, which isn't bad at all. +func (m *memCache) cleanup() { + m.cache.Range(func(k, vi any) bool { + v := vi.(*cacheVal) + v.RLock() + defer v.RUnlock() + if v.fetching { + // skip cleanup if fetching + return true + } + if time.Now().After(v.expiresAt.Add(m.maxStaleAge)) { + // garbage collection + m.cache.Delete(k) + } + return true + }) +} + +func (m *memCache) Close() error { + return m.StopOnce(m.Name(), func() error { + close(m.chStop) + m.wg.Wait() + return nil + }) +} +func (m *memCache) HealthReport() map[string]error { + return map[string]error{ + m.Name(): m.Ready(), + } +} +func (m *memCache) Name() string { return m.lggr.Name() } diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go new file mode 100644 index 00000000000..f06f1137fb3 --- /dev/null +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go @@ -0,0 +1,120 @@ +package cache + +import ( + "context" + "fmt" + "sync" + "time" + + "golang.org/x/exp/maps" + + "github.com/smartcontractkit/chainlink-common/pkg/services" + + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// CacheSet holds a set of mercury caches keyed by server URL +type CacheSet interface { + services.Service + Get(ctx context.Context, client Client) (Fetcher, error) +} + +var _ CacheSet = (*cacheSet)(nil) + +type cacheSet struct { + sync.RWMutex + services.StateMachine + + lggr logger.Logger + caches map[string]Cache + + latestPriceTTL time.Duration + maxStaleAge time.Duration +} + +func NewCacheSet(cfg Config) CacheSet { + return newCacheSet(cfg) +} + +func newCacheSet(cfg Config) *cacheSet { + return &cacheSet{ + sync.RWMutex{}, + services.StateMachine{}, + cfg.Logger.Named("CacheSet"), + make(map[string]Cache), + cfg.LatestReportTTL, + cfg.MaxStaleAge, + } +} + +func (cs *cacheSet) Start(context.Context) error { + return cs.StartOnce("CacheSet", func() error { + return nil + }) +} + +func (cs *cacheSet) Close() error { + return cs.StopOnce("CacheSet", func() error { + cs.Lock() + defer cs.Unlock() + caches := maps.Values(cs.caches) + if err := services.MultiCloser(caches).Close(); err != nil { + return err + } + cs.caches = nil + return nil + }) +} + +func (cs *cacheSet) Get(ctx context.Context, client Client) (f Fetcher, err error) { + ok := cs.IfStarted(func() { + f, err = cs.get(ctx, client) + }) + if !ok { + return nil, fmt.Errorf("cacheSet must be started, but is: %v", cs.State()) + } + return +} + +func (cs *cacheSet) get(ctx context.Context, client Client) (Fetcher, error) { + sURL := client.ServerURL() + // HOT PATH + cs.RLock() + c, exists := cs.caches[sURL] + cs.RUnlock() + if exists { + return c, nil + } + + // COLD PATH + cs.Lock() + defer cs.Unlock() + c, exists = cs.caches[sURL] + if exists { + return c, nil + } + cfg := Config{ + Logger: cs.lggr.With("serverURL", sURL), + LatestReportTTL: cs.latestPriceTTL, + MaxStaleAge: cs.maxStaleAge, + } + c = newMemCache(client, cfg) + if err := c.Start(ctx); err != nil { + return nil, err + } + cs.caches[sURL] = c + return c, nil +} + +func (cs *cacheSet) HealthReport() map[string]error { + report := map[string]error{ + cs.Name(): cs.Ready(), + } + cs.RLock() + defer cs.RUnlock() + for _, c := range cs.caches { + services.CopyHealth(report, c.HealthReport()) + } + return report +} +func (cs *cacheSet) Name() string { return cs.lggr.Name() } diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go new file mode 100644 index 00000000000..9262fb52d31 --- /dev/null +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go @@ -0,0 +1,48 @@ +package cache + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_CacheSet(t *testing.T) { + lggr := logger.TestLogger(t) + cs := newCacheSet(Config{Logger: lggr}) + ctx := testutils.Context(t) + require.NoError(t, cs.Start(ctx)) + t.Cleanup(func() { + assert.NoError(t, cs.Close()) + }) + + t.Run("Get", func(t *testing.T) { + c := &mockClient{} + + var err error + var f Fetcher + t.Run("with virgin cacheset, makes new entry and returns it", func(t *testing.T) { + assert.Len(t, cs.caches, 0) + + f, err = cs.Get(ctx, c) + require.NoError(t, err) + + assert.IsType(t, f, &memCache{}) + assert.Len(t, cs.caches, 1) + }) + t.Run("with existing cache for value, returns that", func(t *testing.T) { + var f2 Fetcher + assert.Len(t, cs.caches, 1) + + f2, err = cs.Get(ctx, c) + require.NoError(t, err) + + assert.IsType(t, f, &memCache{}) + assert.Equal(t, f, f2) + assert.Len(t, cs.caches, 1) + }) + }) +} diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go new file mode 100644 index 00000000000..71f74b3b3cb --- /dev/null +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go @@ -0,0 +1,199 @@ +package cache + +import ( + "context" + "errors" + "strconv" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/logger" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" +) + +const neverExpireTTL = 1000 * time.Hour // some massive value that will never expire during a test + +func Test_Cache(t *testing.T) { + client := &mockClient{} + cfg := Config{ + Logger: logger.TestLogger(t), + } + ctx := testutils.Context(t) + + req1 := &pb.LatestReportRequest{FeedId: []byte{1}} + req2 := &pb.LatestReportRequest{FeedId: []byte{2}} + req3 := &pb.LatestReportRequest{FeedId: []byte{3}} + + t.Run("errors with nil req", func(t *testing.T) { + c := newMemCache(client, cfg) + + _, err := c.LatestReport(ctx, nil) + assert.EqualError(t, err, "req must not be nil") + }) + + t.Run("with LatestReportTTL=0 does no caching", func(t *testing.T) { + c := newMemCache(client, cfg) + + req := &pb.LatestReportRequest{} + for i := 0; i < 5; i++ { + client.resp = &pb.LatestReportResponse{Report: &pb.Report{Price: []byte(strconv.Itoa(i))}} + + resp, err := c.LatestReport(ctx, req) + require.NoError(t, err) + assert.Equal(t, client.resp, resp) + } + + client.resp = nil + client.err = errors.New("something exploded") + + resp, err := c.LatestReport(ctx, req) + assert.EqualError(t, err, "something exploded") + assert.Nil(t, resp) + }) + + t.Run("caches repeated calls to LatestReport, keyed by request", func(t *testing.T) { + cfg.LatestReportTTL = neverExpireTTL + client.err = nil + c := newMemCache(client, cfg) + + t.Run("if cache is unstarted, returns error", func(t *testing.T) { + // starting the cache is required for state management if we + // actually cache results, since fetches are initiated async and + // need to be cleaned up properly on close + _, err := c.LatestReport(ctx, &pb.LatestReportRequest{}) + assert.EqualError(t, err, "memCache must be started, but is: Unstarted") + }) + + err := c.StartOnce("test start", func() error { return nil }) + require.NoError(t, err) + + t.Run("returns cached value for key", func(t *testing.T) { + var firstResp *pb.LatestReportResponse + for i := 0; i < 5; i++ { + client.resp = &pb.LatestReportResponse{Report: &pb.Report{Price: []byte(strconv.Itoa(i))}} + if firstResp == nil { + firstResp = client.resp + } + + resp, err := c.LatestReport(ctx, req1) + require.NoError(t, err) + assert.Equal(t, firstResp, resp) + } + }) + + t.Run("cache keys do not conflict", func(t *testing.T) { + var firstResp1 *pb.LatestReportResponse + for i := 5; i < 10; i++ { + client.resp = &pb.LatestReportResponse{Report: &pb.Report{Price: []byte(strconv.Itoa(i))}} + if firstResp1 == nil { + firstResp1 = client.resp + } + + resp, err := c.LatestReport(ctx, req2) + require.NoError(t, err) + assert.Equal(t, firstResp1, resp) + } + + var firstResp2 *pb.LatestReportResponse + for i := 10; i < 15; i++ { + client.resp = &pb.LatestReportResponse{Report: &pb.Report{Price: []byte(strconv.Itoa(i))}} + if firstResp2 == nil { + firstResp2 = client.resp + } + + resp, err := c.LatestReport(ctx, req3) + require.NoError(t, err) + assert.Equal(t, firstResp2, resp) + } + + // req1 key still has same value + resp, err := c.LatestReport(ctx, req1) + require.NoError(t, err) + assert.Equal(t, []byte(strconv.Itoa(0)), resp.Report.Price) + + // req2 key still has same value + resp, err = c.LatestReport(ctx, req2) + require.NoError(t, err) + assert.Equal(t, []byte(strconv.Itoa(5)), resp.Report.Price) + }) + + t.Run("re-queries when a cache item has expired", func(t *testing.T) { + vi, exists := c.cache.Load(req1) + assert.True(t, exists) + v := vi.(*cacheVal) + v.expiresAt = time.Now().Add(-1 * time.Second) + + client.resp = &pb.LatestReportResponse{Report: &pb.Report{Price: []byte(strconv.Itoa(15))}} + + resp, err := c.LatestReport(ctx, req1) + require.NoError(t, err) + assert.Equal(t, client.resp, resp) + + // querying again yields the same cached item + resp, err = c.LatestReport(ctx, req1) + require.NoError(t, err) + assert.Equal(t, client.resp, resp) + }) + }) + + t.Run("complete fetch", func(t *testing.T) { + t.Run("does not change expiry if fetch returns error", func(t *testing.T) { + expires := time.Now().Add(-1 * time.Second) + v := &cacheVal{ + fetching: true, + fetchCh: make(chan (struct{})), + val: nil, + err: nil, + expiresAt: expires, + } + v.completeFetch(nil, errors.New("foo"), time.Now().Add(neverExpireTTL)) + assert.Equal(t, expires, v.expiresAt) + + v = &cacheVal{ + fetching: true, + fetchCh: make(chan (struct{})), + val: nil, + err: nil, + expiresAt: expires, + } + expires = time.Now().Add(neverExpireTTL) + v.completeFetch(nil, nil, expires) + assert.Equal(t, expires, v.expiresAt) + }) + }) + + t.Run("timeouts", func(t *testing.T) { + c := newMemCache(client, cfg) + // simulate fetch already executing in background + v := &cacheVal{ + fetching: true, + fetchCh: make(chan (struct{})), + val: nil, + err: nil, + expiresAt: time.Now().Add(-1 * time.Second), + } + c.cache.Store(req1, v) + + canceledCtx, cancel := context.WithCancel(testutils.Context(t)) + cancel() + + t.Run("returns context deadline exceeded error if fetch takes too long", func(t *testing.T) { + _, err := c.LatestReport(canceledCtx, req1) + require.Error(t, err) + assert.True(t, errors.Is(err, context.Canceled)) + assert.EqualError(t, err, "context canceled") + }) + t.Run("returns wrapped context deadline exceeded error if fetch has errored and is in the retry loop", func(t *testing.T) { + v.err = errors.New("some background fetch error") + + _, err := c.LatestReport(canceledCtx, req1) + require.Error(t, err) + assert.True(t, errors.Is(err, context.Canceled)) + assert.EqualError(t, err, "some background fetch error\ncontext canceled") + }) + }) +} diff --git a/core/services/relay/evm/mercury/wsrpc/cache/helpers_test.go b/core/services/relay/evm/mercury/wsrpc/cache/helpers_test.go new file mode 100644 index 00000000000..4cc08bdd52e --- /dev/null +++ b/core/services/relay/evm/mercury/wsrpc/cache/helpers_test.go @@ -0,0 +1,38 @@ +package cache + +import ( + "context" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" +) + +var _ Client = &mockClient{} + +type mockClient struct { + resp *pb.LatestReportResponse + err error +} + +func (m *mockClient) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) { + return m.resp, m.err +} + +func (m *mockClient) ServerURL() string { + return "mock client url" +} + +func (m *mockClient) RawClient() pb.MercuryClient { + return &mockRawClient{m.resp, m.err} +} + +type mockRawClient struct { + resp *pb.LatestReportResponse + err error +} + +func (m *mockRawClient) Transmit(ctx context.Context, in *pb.TransmitRequest) (*pb.TransmitResponse, error) { + return nil, nil +} +func (m *mockRawClient) LatestReport(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { + return m.resp, m.err +} diff --git a/core/services/relay/evm/mercury/wsrpc/client.go b/core/services/relay/evm/mercury/wsrpc/client.go index c04c00074a2..5cdf1f44e96 100644 --- a/core/services/relay/evm/mercury/wsrpc/client.go +++ b/core/services/relay/evm/mercury/wsrpc/client.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -63,6 +64,8 @@ var ( type Client interface { services.Service pb.MercuryClient + ServerURL() string + RawClient() pb.MercuryClient } type Conn interface { @@ -78,15 +81,18 @@ type client struct { serverPubKey []byte serverURL string - logger logger.Logger - conn Conn - client pb.MercuryClient + logger logger.Logger + conn Conn + rawClient pb.MercuryClient consecutiveTimeoutCnt atomic.Int32 wg sync.WaitGroup chStop services.StopChan chResetTransport chan struct{} + cacheSet cache.CacheSet + cache cache.Fetcher + timeoutCountMetric prometheus.Counter dialCountMetric prometheus.Counter dialSuccessCountMetric prometheus.Counter @@ -95,17 +101,18 @@ type client struct { } // Consumers of wsrpc package should not usually call NewClient directly, but instead use the Pool -func NewClient(lggr logger.Logger, clientPrivKey csakey.KeyV2, serverPubKey []byte, serverURL string) Client { - return newClient(lggr, clientPrivKey, serverPubKey, serverURL) +func NewClient(lggr logger.Logger, clientPrivKey csakey.KeyV2, serverPubKey []byte, serverURL string, cacheSet cache.CacheSet) Client { + return newClient(lggr, clientPrivKey, serverPubKey, serverURL, cacheSet) } -func newClient(lggr logger.Logger, clientPrivKey csakey.KeyV2, serverPubKey []byte, serverURL string) *client { +func newClient(lggr logger.Logger, clientPrivKey csakey.KeyV2, serverPubKey []byte, serverURL string, cacheSet cache.CacheSet) *client { return &client{ csaKey: clientPrivKey, serverPubKey: serverPubKey, serverURL: serverURL, logger: lggr.Named("WSRPC").With("mercuryServerURL", serverURL), chResetTransport: make(chan struct{}, 1), + cacheSet: cacheSet, chStop: make(services.StopChan), timeoutCountMetric: timeoutCount.WithLabelValues(serverURL), dialCountMetric: dialCount.WithLabelValues(serverURL), @@ -115,9 +122,15 @@ func newClient(lggr logger.Logger, clientPrivKey csakey.KeyV2, serverPubKey []by } } -func (w *client) Start(_ context.Context) error { - return w.StartOnce("WSRPC Client", func() error { - if err := w.dial(context.Background()); err != nil { +func (w *client) Start(ctx context.Context) error { + return w.StartOnce("WSRPC Client", func() (err error) { + // NOTE: This is not a mistake, dial is non-blocking so it should use a + // background context, not the Start context + if err = w.dial(context.Background()); err != nil { + return err + } + w.cache, err = w.cacheSet.Get(ctx, w) + if err != nil { return err } w.wg.Add(1) @@ -148,7 +161,7 @@ func (w *client) dial(ctx context.Context, opts ...wsrpc.DialOption) error { w.dialSuccessCountMetric.Inc() setLivenessMetric(true) w.conn = conn - w.client = pb.NewMercuryClient(conn) + w.rawClient = pb.NewMercuryClient(conn) return nil } @@ -242,7 +255,7 @@ func (w *client) Transmit(ctx context.Context, req *pb.TransmitRequest) (resp *p if err = w.waitForReady(ctx); err != nil { return nil, errors.Wrap(err, "Transmit call failed") } - resp, err = w.client.Transmit(ctx, req) + resp, err = w.rawClient.Transmit(ctx, req) if errors.Is(err, context.DeadlineExceeded) { w.timeoutCountMetric.Inc() cnt := w.consecutiveTimeoutCnt.Add(1) @@ -290,7 +303,11 @@ func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) if err = w.waitForReady(ctx); err != nil { return nil, errors.Wrap(err, "LatestReport failed") } - resp, err = w.client.LatestReport(ctx, req) + if w.cache == nil { + resp, err = w.rawClient.LatestReport(ctx, req) + } else { + resp, err = w.cache.LatestReport(ctx, req) + } if err != nil { lggr.Errorw("LatestReport failed", "err", err, "resp", resp) } else if resp.Error != "" { @@ -300,3 +317,11 @@ func (w *client) LatestReport(ctx context.Context, req *pb.LatestReportRequest) } return } + +func (w *client) ServerURL() string { + return w.serverURL +} + +func (w *client) RawClient() pb.MercuryClient { + return w.rawClient +} diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index 2410cbc52f1..d5d1b1b5ee5 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -3,6 +3,7 @@ package wsrpc import ( "context" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -10,15 +11,48 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" mocks "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) +var _ cache.CacheSet = &mockCacheSet{} + +type mockCacheSet struct{} + +func (m *mockCacheSet) Get(ctx context.Context, client cache.Client) (cache.Fetcher, error) { + return nil, nil +} +func (m *mockCacheSet) Start(context.Context) error { return nil } +func (m *mockCacheSet) Ready() error { return nil } +func (m *mockCacheSet) HealthReport() map[string]error { return nil } +func (m *mockCacheSet) Name() string { return "" } +func (m *mockCacheSet) Close() error { return nil } + +var _ cache.Cache = &mockCache{} + +type mockCache struct{} + +func (m *mockCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) { + return nil, nil +} +func (m *mockCache) Start(context.Context) error { return nil } +func (m *mockCache) Ready() error { return nil } +func (m *mockCache) HealthReport() map[string]error { return nil } +func (m *mockCache) Name() string { return "" } +func (m *mockCache) Close() error { return nil } + +func newNoopCacheSet() cache.CacheSet { + return &mockCacheSet{} +} + func Test_Client_Transmit(t *testing.T) { lggr := logger.TestLogger(t) ctx := testutils.Context(t) req := &pb.TransmitRequest{} + noopCacheSet := newNoopCacheSet() + t.Run("sends on reset channel after MaxConsecutiveTransmitFailures timed out transmits", func(t *testing.T) { calls := 0 transmitErr := context.DeadlineExceeded @@ -31,9 +65,9 @@ func Test_Client_Transmit(t *testing.T) { conn := &mocks.MockConn{ Ready: true, } - c := newClient(lggr, csakey.KeyV2{}, nil, "") + c := newClient(lggr, csakey.KeyV2{}, nil, "", noopCacheSet) c.conn = conn - c.client = wsrpcClient + c.rawClient = wsrpcClient require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) for i := 1; i < MaxConsecutiveTransmitFailures; i++ { _, err := c.Transmit(ctx, req) @@ -73,3 +107,109 @@ func Test_Client_Transmit(t *testing.T) { }) }) } + +func Test_Client_LatestReport(t *testing.T) { + lggr := logger.TestLogger(t) + ctx := testutils.Context(t) + + t.Run("with nil cache", func(t *testing.T) { + req := &pb.LatestReportRequest{} + noopCacheSet := newNoopCacheSet() + resp := &pb.LatestReportResponse{} + + wsrpcClient := &mocks.MockWSRPCClient{ + LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { + assert.Equal(t, req, in) + return resp, nil + }, + } + + conn := &mocks.MockConn{ + Ready: true, + } + c := newClient(lggr, csakey.KeyV2{}, nil, "", noopCacheSet) + c.conn = conn + c.rawClient = wsrpcClient + require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) + + r, err := c.LatestReport(ctx, req) + + require.NoError(t, err) + assert.Equal(t, resp, r) + }) + + t.Run("with cache disabled", func(t *testing.T) { + req := &pb.LatestReportRequest{} + cacheSet := cache.NewCacheSet(cache.Config{LatestReportTTL: 0, Logger: lggr}) + resp := &pb.LatestReportResponse{} + + var calls int + wsrpcClient := &mocks.MockWSRPCClient{ + LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { + calls++ + assert.Equal(t, req, in) + return resp, nil + }, + } + + conn := &mocks.MockConn{ + Ready: true, + } + c := newClient(lggr, csakey.KeyV2{}, nil, "", cacheSet) + c.conn = conn + c.rawClient = wsrpcClient + + // simulate start without dialling + require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) + var err error + require.NoError(t, cacheSet.Start(ctx)) + c.cache, err = cacheSet.Get(ctx, c) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + r, err := c.LatestReport(ctx, req) + + require.NoError(t, err) + assert.Equal(t, resp, r) + } + assert.Equal(t, 5, calls, "expected 5 calls to LatestReport but it was called %d times", calls) + }) + + t.Run("with caching", func(t *testing.T) { + req := &pb.LatestReportRequest{} + const neverExpireTTL = 1000 * time.Hour // some massive value that will never expire during a test + cacheSet := cache.NewCacheSet(cache.Config{LatestReportTTL: neverExpireTTL, Logger: lggr}) + resp := &pb.LatestReportResponse{} + + var calls int + wsrpcClient := &mocks.MockWSRPCClient{ + LatestReportF: func(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { + calls++ + assert.Equal(t, req, in) + return resp, nil + }, + } + + conn := &mocks.MockConn{ + Ready: true, + } + c := newClient(lggr, csakey.KeyV2{}, nil, "", cacheSet) + c.conn = conn + c.rawClient = wsrpcClient + + // simulate start without dialling + require.NoError(t, c.StartOnce("Mock WSRPC Client", func() error { return nil })) + var err error + require.NoError(t, cacheSet.Start(ctx)) + c.cache, err = cacheSet.Get(ctx, c) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + r, err := c.LatestReport(ctx, req) + + require.NoError(t, err) + assert.Equal(t, resp, r) + } + assert.Equal(t, 1, calls, "expected only 1 call to LatestReport but it was called %d times", calls) + }) +} diff --git a/core/services/relay/evm/mercury/wsrpc/mocks/mocks.go b/core/services/relay/evm/mercury/wsrpc/mocks/mocks.go index d764143c5fc..c0caf0dee12 100644 --- a/core/services/relay/evm/mercury/wsrpc/mocks/mocks.go +++ b/core/services/relay/evm/mercury/wsrpc/mocks/mocks.go @@ -24,6 +24,9 @@ func (m MockWSRPCClient) Transmit(ctx context.Context, in *pb.TransmitRequest) ( func (m MockWSRPCClient) LatestReport(ctx context.Context, in *pb.LatestReportRequest) (*pb.LatestReportResponse, error) { return m.LatestReportF(ctx, in) } +func (m MockWSRPCClient) ServerURL() string { return "mock server url" } + +func (m MockWSRPCClient) RawClient() pb.MercuryClient { return nil } type MockConn struct { State connectivity.State diff --git a/core/services/relay/evm/mercury/wsrpc/pool.go b/core/services/relay/evm/mercury/wsrpc/pool.go index 6630a78437e..6c525741ddc 100644 --- a/core/services/relay/evm/mercury/wsrpc/pool.go +++ b/core/services/relay/evm/mercury/wsrpc/pool.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -58,7 +59,7 @@ func (conn *connection) checkout(ctx context.Context) (cco *clientCheckout, err // not thread-safe, access must be serialized func (conn *connection) ensureStartedClient(ctx context.Context) error { if len(conn.checkouts) == 0 { - conn.Client = conn.pool.newClient(conn.lggr, conn.clientPrivKey, conn.serverPubKey, conn.serverURL) + conn.Client = conn.pool.newClient(conn.lggr, conn.clientPrivKey, conn.serverPubKey, conn.serverURL, conn.pool.cacheSet) return conn.Client.Start(ctx) } return nil @@ -119,16 +120,19 @@ type pool struct { connections map[string]map[credentials.StaticSizedPublicKey]*connection // embedding newClient makes testing/mocking easier - newClient func(lggr logger.Logger, privKey csakey.KeyV2, serverPubKey []byte, serverURL string) Client + newClient func(lggr logger.Logger, privKey csakey.KeyV2, serverPubKey []byte, serverURL string, cacheSet cache.CacheSet) Client mu sync.RWMutex + cacheSet cache.CacheSet + closed bool } -func NewPool(lggr logger.Logger) Pool { +func NewPool(lggr logger.Logger, cacheCfg cache.Config) Pool { p := newPool(lggr.Named("Mercury.WSRPCPool")) p.newClient = NewClient + p.cacheSet = cache.NewCacheSet(cacheCfg) return p } @@ -188,7 +192,9 @@ func (p *pool) newConnection(lggr logger.Logger, clientPrivKey csakey.KeyV2, ser } } -func (p *pool) Start(_ context.Context) error { return nil } +func (p *pool) Start(ctx context.Context) error { + return p.cacheSet.Start(ctx) +} func (p *pool) Close() (merr error) { p.mu.Lock() @@ -199,6 +205,7 @@ func (p *pool) Close() (merr error) { merr = errors.Join(merr, conn.forceCloseAll()) } } + merr = errors.Join(merr, p.cacheSet.Close()) return } diff --git a/core/services/relay/evm/mercury/wsrpc/pool_test.go b/core/services/relay/evm/mercury/wsrpc/pool_test.go index 980b9f4d742..3d418d39d87 100644 --- a/core/services/relay/evm/mercury/wsrpc/pool_test.go +++ b/core/services/relay/evm/mercury/wsrpc/pool_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/csakey" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/cache" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -19,8 +20,9 @@ import ( var _ Client = &mockClient{} type mockClient struct { - started bool - closed bool + started bool + closed bool + rawClient pb.MercuryClient } func (c *mockClient) Transmit(ctx context.Context, in *pb.TransmitRequest) (out *pb.TransmitResponse, err error) { @@ -40,6 +42,8 @@ func (c *mockClient) Close() error { func (c *mockClient) Name() string { return "mock client" } func (c *mockClient) Ready() error { return nil } func (c *mockClient) HealthReport() map[string]error { return nil } +func (c *mockClient) ServerURL() string { return "mock client url" } +func (c *mockClient) RawClient() pb.MercuryClient { return c.rawClient } func newMockClient(lggr logger.Logger) *mockClient { return &mockClient{} @@ -52,6 +56,7 @@ func Test_Pool(t *testing.T) { t.Run("Checkout", func(t *testing.T) { p := newPool(lggr) + p.cacheSet = &mockCacheSet{} t.Run("checks out one started client", func(t *testing.T) { clientPrivKey := csakey.MustNewV2XXXTestingOnly(big.NewInt(rand.Int63())) @@ -59,7 +64,7 @@ func Test_Pool(t *testing.T) { serverURL := "example.com:443/ws" client := newMockClient(lggr) - p.newClient = func(lggr logger.Logger, cprivk csakey.KeyV2, spubk []byte, surl string) Client { + p.newClient = func(lggr logger.Logger, cprivk csakey.KeyV2, spubk []byte, surl string, cs cache.CacheSet) Client { assert.Equal(t, clientPrivKey, cprivk) assert.Equal(t, serverPubKey, spubk) assert.Equal(t, serverURL, surl) @@ -105,7 +110,7 @@ func Test_Pool(t *testing.T) { "example.invalid:8000/ws", } - p.newClient = func(lggr logger.Logger, cprivk csakey.KeyV2, spubk []byte, surl string) Client { + p.newClient = func(lggr logger.Logger, cprivk csakey.KeyV2, spubk []byte, surl string, cs cache.CacheSet) Client { return newMockClient(lggr) } @@ -199,6 +204,7 @@ func Test_Pool(t *testing.T) { }) p := newPool(lggr) + p.cacheSet = &mockCacheSet{} t.Run("Name", func(t *testing.T) { assert.Equal(t, "PoolTestLogger", p.Name()) @@ -220,7 +226,7 @@ func Test_Pool(t *testing.T) { } var clients []*mockClient - p.newClient = func(lggr logger.Logger, cprivk csakey.KeyV2, spubk []byte, surl string) Client { + p.newClient = func(lggr logger.Logger, cprivk csakey.KeyV2, spubk []byte, surl string, cs cache.CacheSet) Client { c := newMockClient(lggr) clients = append(clients, c) return c diff --git a/core/utils/hash.go b/core/utils/hash.go index bcae823770e..b0a32454e2f 100644 --- a/core/utils/hash.go +++ b/core/utils/hash.go @@ -8,12 +8,32 @@ import ( "github.com/pkg/errors" ) +const HashLength = 32 + // Hash is a simplified version of go-ethereum's common.Hash to avoid // go-ethereum dependency // It represents a 32 byte fixed size array that marshals/unmarshals assuming a // 0x prefix type Hash [32]byte +// BytesToHash sets b to hash. +// If b is larger than len(h), b will be cropped from the left. +func BytesToHash(b []byte) Hash { + var h Hash + h.SetBytes(b) + return h +} + +// SetBytes sets the hash to the value of b. +// If b is larger than len(h), b will be cropped from the left. +func (h *Hash) SetBytes(b []byte) { + if len(b) > len(h) { + b = b[len(b)-HashLength:] + } + + copy(h[HashLength-len(b):], b) +} + // Hex converts a hash to a hex string. func (h Hash) Hex() string { return fmt.Sprintf("0x%s", hex.EncodeToString(h[:])) } diff --git a/core/web/resolver/testdata/config-empty-effective.toml b/core/web/resolver/testdata/config-empty-effective.toml index 8f3135b34e4..2531e7c281d 100644 --- a/core/web/resolver/testdata/config-empty-effective.toml +++ b/core/web/resolver/testdata/config-empty-effective.toml @@ -234,3 +234,9 @@ NodeID = '' SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' + +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index 7e9872e9554..cd0bce3cc73 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -245,6 +245,12 @@ TLSCertPath = '/path/to/cert.pem' env = 'dev' test = 'load' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1m40s' +MaxStaleAge = '1m41s' +LatestReportDeadline = '1m42s' + [[EVM]] ChainID = '1' Enabled = false diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 6a60dfd419a..371cc50a170 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -235,6 +235,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 265222c8bec..d9a785ff518 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,11 +13,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added distributed tracing in the OpenTelemetry trace format to the node, currently focused at the LOOPP Plugin development effort. This includes a new set of `Tracing` TOML configurations. The default for collecting traces is off - you must explicitly enable traces and setup a valid OpenTelemetry collector. Refer to `.github/tracing/README.md` for more details. - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. -- New prom metrics for mercury: +- New prom metrics for mercury transmit queue: `mercury_transmit_queue_delete_error_count` `mercury_transmit_queue_insert_error_count` `mercury_transmit_queue_push_error_count` Nops should consider alerting on these. +- Mercury now implements a local cache for fetching prices for fees, which ought to reduce latency and load on the mercury server, as well as increasing performance. It is enabled by default and can be configured with the following new config variables: +``` +[Mercury] + +# Mercury.Cache controls settings for the price retrieval cache querying a mercury server +[Mercury.Cache] +# LatestReportTTL controls how "stale" we will allow a price to be e.g. if +# set to 1s, a new price will always be fetched if the last result was +# from 1 second ago or older. +# +# Another way of looking at it is such: the cache will _never_ return a +# price that was queried from now-LatestReportTTL or before. +# +# Setting to zero disables caching entirely. +LatestReportTTL = "1s" # Default +# MaxStaleAge is that maximum amount of time that a value can be stale +# before it is deleted from the cache (a form of garbage collection). +# +# This should generally be set to something much larger than +# LatestReportTTL. Setting to zero disables garbage collection. +MaxStaleAge = "1h" # Default +# LatestReportDeadline controls how long to wait for a response from the +# mercury server before retrying. Setting this to zero will wait indefinitely. +LatestReportDeadline = "5s" # Default +``` +- New prom metrics for the mercury cache: + `mercury_cache_fetch_failure_count` + `mercury_cache_hit_count` + `mercury_cache_wait_count` + `mercury_cache_miss_count` + ### Changed diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 63c20bdf4a5..61d079fa4a6 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1652,6 +1652,51 @@ env = 'test' # Example ``` env is an example user specified key-value pair +## Mercury +```toml +[Mercury] +``` + + +## Mercury.Cache +```toml +[Mercury.Cache] +LatestReportTTL = "1s" # Default +MaxStaleAge = "1h" # Default +LatestReportDeadline = "5s" # Default +``` +Mercury.Cache controls settings for the price retrieval cache querying a mercury server + +### LatestReportTTL +```toml +LatestReportTTL = "1s" # Default +``` +LatestReportTTL controls how "stale" we will allow a price to be e.g. if +set to 1s, a new price will always be fetched if the last result was +from 1 second ago or older. + +Another way of looking at it is such: the cache will _never_ return a +price that was queried from now-LatestReportTTL or before. + +Setting to zero disables caching entirely. + +### MaxStaleAge +```toml +MaxStaleAge = "1h" # Default +``` +MaxStaleAge is that maximum amount of time that a value can be stale +before it is deleted from the cache (a form of garbage collection). + +This should generally be set to something much larger than +LatestReportTTL. Setting to zero disables garbage collection. + +### LatestReportDeadline +```toml +LatestReportDeadline = "5s" # Default +``` +LatestReportDeadline controls how long to wait for a response from the +mercury server before retrying. Setting this to zero will wait indefinitely. + ## EVM EVM defaults depend on ChainID: diff --git a/testdata/scripts/node/validate/default.txtar b/testdata/scripts/node/validate/default.txtar index 267a760f08c..0b841f694be 100644 --- a/testdata/scripts/node/validate/default.txtar +++ b/testdata/scripts/node/validate/default.txtar @@ -247,6 +247,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + Invalid configuration: invalid secrets: 2 errors: - Database.URL: empty: must be provided and non-empty - Password.Keystore: empty: must be provided and non-empty diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index e6281e8d221..45b08f0e52f 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -291,6 +291,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 65d832aa82e..2869af3e2de 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -291,6 +291,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index 6b9e3d56ce6..fb705819fc2 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -291,6 +291,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index aa2036413c7..7b82d3323b1 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -281,6 +281,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 4ceb9d5df35..91fe0952dd8 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -288,6 +288,12 @@ SamplingRatio = 0.0 Mode = 'tls' TLSCertPath = '' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + [[EVM]] ChainID = '1' AutoCreateKey = true diff --git a/testdata/scripts/node/validate/warnings.txtar b/testdata/scripts/node/validate/warnings.txtar index e4ff2aa35ea..a10d6df537c 100644 --- a/testdata/scripts/node/validate/warnings.txtar +++ b/testdata/scripts/node/validate/warnings.txtar @@ -296,6 +296,12 @@ SamplingRatio = 0.0 Mode = 'unencrypted' TLSCertPath = 'something' +[Mercury] +[Mercury.Cache] +LatestReportTTL = '1s' +MaxStaleAge = '1h0m0s' +LatestReportDeadline = '5s' + # Configuration warning: 3 errors: - P2P.V1: is deprecated and will be removed in a future version From 7280c405a8cce1750bd9a64964a214bae2a792d4 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:29:47 -0500 Subject: [PATCH 212/271] Make job names less dynamic for required checks (#11403) --- .github/workflows/solidity-foundry.yml | 2 +- .github/workflows/solidity.yml | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/solidity-foundry.yml b/.github/workflows/solidity-foundry.yml index 90d18ecac2e..7f6fa4f482e 100644 --- a/.github/workflows/solidity-foundry.yml +++ b/.github/workflows/solidity-foundry.yml @@ -34,7 +34,7 @@ jobs: matrix: product: [vrf, automation, llo-feeds, functions, shared] needs: [changes] - name: Foundry Tests ${{ matrix.product }} ${{ fromJSON('["(skipped)", ""]')[needs.changes.outputs.changes == 'true'] }} + name: Foundry Tests ${{ matrix.product }} # See https://github.com/foundry-rs/foundry/issues/3827 runs-on: ubuntu-22.04 diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index 5699657fa5d..90429a8c526 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -32,7 +32,6 @@ jobs: - 'contracts/src/v0.6/**/*' - 'contracts/src/v0.7/**/*' - - name: Fail if read-only files have changed if: ${{ steps.changes.outputs.old_sol == 'true' }} run: | @@ -42,8 +41,6 @@ jobs: done exit 1 - - prepublish-test: needs: [changes] if: needs.changes.outputs.changes == 'true' @@ -117,7 +114,7 @@ jobs: run: working-directory: contracts needs: [changes] - name: Lint ${{ fromJSON('["(skipped)", ""]')[needs.changes.outputs.changes == 'true'] }} + name: Solidity Lint runs-on: ubuntu-latest steps: - name: Checkout the repo From 41ab6becb26c96f95d87eeae139db9a04e1c906b Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 28 Nov 2023 14:39:28 -0600 Subject: [PATCH 213/271] core/logger: sanitize escape chars in console logs (#11402) --- .../internal/colortest/prettyconsole_test.go | 6 ++++ core/logger/prettyconsole.go | 29 +++++++++++++++++-- docs/CHANGELOG.md | 1 + 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/core/logger/internal/colortest/prettyconsole_test.go b/core/logger/internal/colortest/prettyconsole_test.go index fd2ea3f0b17..125f5a2ea50 100644 --- a/core/logger/internal/colortest/prettyconsole_test.go +++ b/core/logger/internal/colortest/prettyconsole_test.go @@ -61,6 +61,12 @@ func TestPrettyConsole_Write(t *testing.T) { "2018-04-12T12:55:28Z \x1b[91m[FATAL] \x1b[0mtop level \x1b[34m\x1b[0m \n", false, }, + { + "control", + `{"ts":1523537728, "level":"fatal", "msg":"\u0008\t\n\r\u000b\u000c\ufffd\ufffd", "hash":"nuances"}`, + "2018-04-12T12:55:28Z \x1b[91m[FATAL] \x1b[0m\\b\t\n\r\\v\\f�� \x1b[34m\x1b[0m \n", + false, + }, {"broken", `{"broken":}`, `{}`, true}, } diff --git a/core/logger/prettyconsole.go b/core/logger/prettyconsole.go index 69427f74715..5150f1f3d69 100644 --- a/core/logger/prettyconsole.go +++ b/core/logger/prettyconsole.go @@ -6,8 +6,10 @@ import ( "net/url" "os" "sort" + "strconv" "strings" "time" + "unicode" "github.com/fatih/color" "github.com/tidwall/gjson" @@ -72,7 +74,7 @@ func generateHeadline(js gjson.Result) string { tsStr, " ", coloredLevel(js.Get("level")), - fmt.Sprintf("%-50s", js.Get("msg")), + fmt.Sprintf("%-50s", sanitized(js.Get("msg").String())), " ", fmt.Sprintf("%-32s", blue(js.Get("caller"))), } @@ -105,7 +107,7 @@ func generateDetails(js gjson.Result) string { var details strings.Builder for _, v := range keys { - details.WriteString(fmt.Sprintf("%s=%v ", green(v), data[v])) + details.WriteString(fmt.Sprintf("%s=%v ", green(sanitized(v)), sanitized(data[v].String()))) } return details.String() @@ -129,3 +131,26 @@ func prettyConsoleSink(s zap.Sink) func(*url.URL) (zap.Sink, error) { return PrettyConsole{s}, nil } } + +type sanitized string + +// String replaces control characters with Go escape sequences, except for newlines and tabs. +// See strconv.QuoteRune. +func (s sanitized) String() string { + var out string + for _, r := range s { + switch r { + case '\n', '\r', '\t': + // allowed + default: + // escape others + if unicode.IsControl(r) { + q := strconv.QuoteRune(r) + out += q[1 : len(q)-1] // trim quotes + continue + } + } + out += string(r) + } + return out +} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d9a785ff518..64f9472319f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -54,6 +54,7 @@ LatestReportDeadline = "5s" # Default ### Changed - `L2Suggested` mode is now called `SuggestedPrice` +- Console logs will now escape (non-whitespace) control characters ### Removed From 9efb47d17de338146c614d4dc82c8b217af080f3 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 28 Nov 2023 15:35:25 -0600 Subject: [PATCH 214/271] core/config: add EVM.OCR DeltaCOverride and DeltaCJitterOverride (#11250) * core/config: add EVM.OCR OverrideDeltaC and OverrideDeltaCJitter * update changelog --- core/chains/evm/config/chain_scoped_ocr.go | 8 ++ core/chains/evm/config/config.go | 2 + core/chains/evm/config/toml/config.go | 8 ++ .../evm/config/toml/defaults/fallback.toml | 2 + core/config/docs/chains-evm.toml | 8 ++ core/services/chainlink/config_test.go | 4 + .../chainlink/testdata/config-full.toml | 2 + .../config-multi-chain-effective.toml | 6 + core/services/ocr/config_overrider.go | 12 +- core/services/ocr/config_overrider_test.go | 21 +++- core/services/ocr/delegate.go | 2 +- core/web/resolver/testdata/config-full.toml | 2 + .../config-multi-chain-effective.toml | 6 + docs/CHANGELOG.md | 50 ++++---- docs/CONFIG.md | 112 ++++++++++++++++++ .../disk-based-logging-disabled.txtar | 2 + .../validate/disk-based-logging-no-dir.txtar | 2 + .../node/validate/disk-based-logging.txtar | 2 + testdata/scripts/node/validate/invalid.txtar | 2 + testdata/scripts/node/validate/valid.txtar | 2 + 20 files changed, 221 insertions(+), 34 deletions(-) diff --git a/core/chains/evm/config/chain_scoped_ocr.go b/core/chains/evm/config/chain_scoped_ocr.go index 0cdec34e388..f8e171cf632 100644 --- a/core/chains/evm/config/chain_scoped_ocr.go +++ b/core/chains/evm/config/chain_scoped_ocr.go @@ -25,3 +25,11 @@ func (o *ocrConfig) ObservationGracePeriod() time.Duration { func (o *ocrConfig) DatabaseTimeout() time.Duration { return o.c.DatabaseTimeout.Duration() } + +func (o *ocrConfig) DeltaCOverride() time.Duration { + return o.c.DeltaCOverride.Duration() +} + +func (o *ocrConfig) DeltaCJitterOverride() time.Duration { + return o.c.DeltaCJitterOverride.Duration() +} diff --git a/core/chains/evm/config/config.go b/core/chains/evm/config/config.go index 2dd2d4704c3..33e2c85eee5 100644 --- a/core/chains/evm/config/config.go +++ b/core/chains/evm/config/config.go @@ -50,6 +50,8 @@ type OCR interface { ContractTransmitterTransmitTimeout() time.Duration ObservationGracePeriod() time.Duration DatabaseTimeout() time.Duration + DeltaCOverride() time.Duration + DeltaCJitterOverride() time.Duration } type OCR2 interface { diff --git a/core/chains/evm/config/toml/config.go b/core/chains/evm/config/toml/config.go index 26587cd3b0e..9e51d5be790 100644 --- a/core/chains/evm/config/toml/config.go +++ b/core/chains/evm/config/toml/config.go @@ -715,6 +715,8 @@ type OCR struct { ContractConfirmations *uint16 ContractTransmitterTransmitTimeout *models.Duration DatabaseTimeout *models.Duration + DeltaCOverride *models.Duration + DeltaCJitterOverride *models.Duration ObservationGracePeriod *models.Duration } @@ -728,6 +730,12 @@ func (o *OCR) setFrom(f *OCR) { if v := f.DatabaseTimeout; v != nil { o.DatabaseTimeout = v } + if v := f.DeltaCOverride; v != nil { + o.DeltaCOverride = v + } + if v := f.DeltaCJitterOverride; v != nil { + o.DeltaCJitterOverride = v + } if v := f.ObservationGracePeriod; v != nil { o.ObservationGracePeriod = v } diff --git a/core/chains/evm/config/toml/defaults/fallback.toml b/core/chains/evm/config/toml/defaults/fallback.toml index a75cfa0bf3b..b19423fd13a 100644 --- a/core/chains/evm/config/toml/defaults/fallback.toml +++ b/core/chains/evm/config/toml/defaults/fallback.toml @@ -64,6 +64,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h' +DeltaCJitterOverride = '1h' ObservationGracePeriod = '1s' [OCR2.Automation] diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml index 381ab794d60..711889b3fa5 100644 --- a/core/config/docs/chains-evm.toml +++ b/core/config/docs/chains-evm.toml @@ -335,6 +335,14 @@ ContractConfirmations = 4 # Default ContractTransmitterTransmitTimeout = '10s' # Default # DatabaseTimeout sets `OCR.DatabaseTimeout` for this EVM chain. DatabaseTimeout = '10s' # Default +# **ADVANCED** +# DeltaCOverride (and `DeltaCJitterOverride`) determine the config override DeltaC. +# DeltaC is the maximum age of the latest report in the contract. If the maximum age is exceeded, a new report will be +# created by the report generation protocol. +DeltaCOverride = "168h" # Default +# **ADVANCED** +# DeltaCJitterOverride is the range for jitter to add to `DeltaCOverride`. +DeltaCJitterOverride = "1h" # Default # ObservationGracePeriod sets `OCR.ObservationGracePeriod` for this EVM chain. ObservationGracePeriod = '1s' # Default diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go index d777e34abf7..2966a896902 100644 --- a/core/services/chainlink/config_test.go +++ b/core/services/chainlink/config_test.go @@ -574,6 +574,8 @@ func TestConfig_Marshal(t *testing.T) { ContractConfirmations: ptr[uint16](11), ContractTransmitterTransmitTimeout: &minute, DatabaseTimeout: &second, + DeltaCOverride: models.MustNewDuration(time.Hour), + DeltaCJitterOverride: models.MustNewDuration(time.Second), ObservationGracePeriod: &second, }, OCR2: evmcfg.OCR2{ @@ -1019,6 +1021,8 @@ LeaseDuration = '0s' ContractConfirmations = 11 ContractTransmitterTransmitTimeout = '1m0s' DatabaseTimeout = '1s' +DeltaCOverride = '1h0m0s' +DeltaCJitterOverride = '1s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/core/services/chainlink/testdata/config-full.toml b/core/services/chainlink/testdata/config-full.toml index 8036165d6e8..46d9dc2c239 100644 --- a/core/services/chainlink/testdata/config-full.toml +++ b/core/services/chainlink/testdata/config-full.toml @@ -340,6 +340,8 @@ LeaseDuration = '0s' ContractConfirmations = 11 ContractTransmitterTransmitTimeout = '1m0s' DatabaseTimeout = '1s' +DeltaCOverride = '1h0m0s' +DeltaCJitterOverride = '1s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/core/services/chainlink/testdata/config-multi-chain-effective.toml b/core/services/chainlink/testdata/config-multi-chain-effective.toml index 371cc50a170..74d83035cd5 100644 --- a/core/services/chainlink/testdata/config-multi-chain-effective.toml +++ b/core/services/chainlink/testdata/config-multi-chain-effective.toml @@ -311,6 +311,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] @@ -396,6 +398,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] @@ -475,6 +479,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/core/services/ocr/config_overrider.go b/core/services/ocr/config_overrider.go index b1acf9a7d73..ac87d0e3924 100644 --- a/core/services/ocr/config_overrider.go +++ b/core/services/ocr/config_overrider.go @@ -13,6 +13,7 @@ import ( ocrtypes "github.com/smartcontractkit/libocr/offchainreporting/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/v2/core/utils" @@ -40,8 +41,14 @@ type ConfigOverriderImpl struct { // InitialHibernationStatus - hibernation state set until the first successful update from the chain const InitialHibernationStatus = false +type DeltaCConfig interface { + DeltaCOverride() time.Duration + DeltaCJitterOverride() time.Duration +} + func NewConfigOverriderImpl( logger logger.Logger, + cfg DeltaCConfig, contractAddress ethkey.EIP55Address, flags *ContractFlags, pollTicker utils.TickerBase, @@ -53,8 +60,9 @@ func NewConfigOverriderImpl( } addressBig := contractAddress.Big() - addressSeconds := addressBig.Mod(addressBig, big.NewInt(3600)).Uint64() - deltaC := 23*time.Hour + time.Duration(addressSeconds)*time.Second + jitterSeconds := int64(cfg.DeltaCJitterOverride() / time.Second) + addressSeconds := addressBig.Mod(addressBig, big.NewInt(jitterSeconds)).Uint64() + deltaC := cfg.DeltaCOverride() + time.Duration(addressSeconds)*time.Second ctx, cancel := context.WithCancel(context.Background()) co := ConfigOverriderImpl{ diff --git a/core/services/ocr/config_overrider_test.go b/core/services/ocr/config_overrider_test.go index bb680189b10..245d6348765 100644 --- a/core/services/ocr/config_overrider_test.go +++ b/core/services/ocr/config_overrider_test.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -27,6 +28,12 @@ type configOverriderUni struct { contractAddress ethkey.EIP55Address } +type deltaCConfig struct{} + +func (d deltaCConfig) DeltaCOverride() time.Duration { return time.Hour * 24 * 7 } + +func (d deltaCConfig) DeltaCJitterOverride() time.Duration { return time.Hour } + func newConfigOverriderUni(t *testing.T, pollITicker utils.TickerBase, flagsContract *mocks.Flags) (uni configOverriderUni) { var testLogger = logger.TestLogger(t) contractAddress := cltest.NewEIP55Address() @@ -35,6 +42,7 @@ func newConfigOverriderUni(t *testing.T, pollITicker utils.TickerBase, flagsCont var err error uni.overrider, err = ocr.NewConfigOverriderImpl( testLogger, + deltaCConfig{}, contractAddress, flags, pollITicker, @@ -141,6 +149,7 @@ func Test_OCRConfigOverrider(t *testing.T) { flags := &ocr.ContractFlags{FlagsInterface: nil} _, err := ocr.NewConfigOverriderImpl( testLogger, + deltaCConfig{}, contractAddress, flags, nil, @@ -160,18 +169,18 @@ func Test_OCRConfigOverrider(t *testing.T) { address2, err := ethkey.NewEIP55Address(common.BigToAddress(big.NewInt(1234567890)).Hex()) require.NoError(t, err) - overrider1a, err := ocr.NewConfigOverriderImpl(testLogger, address1, flags, nil) + overrider1a, err := ocr.NewConfigOverriderImpl(testLogger, deltaCConfig{}, address1, flags, nil) require.NoError(t, err) - overrider1b, err := ocr.NewConfigOverriderImpl(testLogger, address1, flags, nil) + overrider1b, err := ocr.NewConfigOverriderImpl(testLogger, deltaCConfig{}, address1, flags, nil) require.NoError(t, err) - overrider2, err := ocr.NewConfigOverriderImpl(testLogger, address2, flags, nil) + overrider2, err := ocr.NewConfigOverriderImpl(testLogger, deltaCConfig{}, address2, flags, nil) require.NoError(t, err) - require.Equal(t, overrider1a.DeltaCFromAddress, time.Duration(85600000000000)) - require.Equal(t, overrider1b.DeltaCFromAddress, time.Duration(85600000000000)) - require.Equal(t, overrider2.DeltaCFromAddress, time.Duration(84690000000000)) + assert.Equal(t, cltest.MustParseDuration(t, "168h46m40s"), overrider1a.DeltaCFromAddress) + assert.Equal(t, cltest.MustParseDuration(t, "168h46m40s"), overrider1b.DeltaCFromAddress) + assert.Equal(t, cltest.MustParseDuration(t, "168h31m30s"), overrider2.DeltaCFromAddress) }) } diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index aa058d64979..ac78002d450 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -358,7 +358,7 @@ func (d *Delegate) maybeCreateConfigOverrider(logger logger.Logger, chain legacy } ticker := utils.NewPausableTicker(ConfigOverriderPollInterval) - return NewConfigOverriderImpl(logger, contractAddress, flags, &ticker) + return NewConfigOverriderImpl(logger, chain.Config().EVM().OCR(), contractAddress, flags, &ticker) } return nil, nil } diff --git a/core/web/resolver/testdata/config-full.toml b/core/web/resolver/testdata/config-full.toml index cd0bce3cc73..e98f8602a0c 100644 --- a/core/web/resolver/testdata/config-full.toml +++ b/core/web/resolver/testdata/config-full.toml @@ -339,6 +339,8 @@ LeaseDuration = '0s' ContractConfirmations = 11 ContractTransmitterTransmitTimeout = '1m0s' DatabaseTimeout = '1s' +DeltaCOverride = '1h0m0s' +DeltaCJitterOverride = '1s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/core/web/resolver/testdata/config-multi-chain-effective.toml b/core/web/resolver/testdata/config-multi-chain-effective.toml index 371cc50a170..74d83035cd5 100644 --- a/core/web/resolver/testdata/config-multi-chain-effective.toml +++ b/core/web/resolver/testdata/config-multi-chain-effective.toml @@ -311,6 +311,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] @@ -396,6 +398,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] @@ -475,6 +479,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 64f9472319f..727f5ad30dd 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,36 +19,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `mercury_transmit_queue_push_error_count` Nops should consider alerting on these. - Mercury now implements a local cache for fetching prices for fees, which ought to reduce latency and load on the mercury server, as well as increasing performance. It is enabled by default and can be configured with the following new config variables: -``` -[Mercury] - -# Mercury.Cache controls settings for the price retrieval cache querying a mercury server -[Mercury.Cache] -# LatestReportTTL controls how "stale" we will allow a price to be e.g. if -# set to 1s, a new price will always be fetched if the last result was -# from 1 second ago or older. -# -# Another way of looking at it is such: the cache will _never_ return a -# price that was queried from now-LatestReportTTL or before. -# -# Setting to zero disables caching entirely. -LatestReportTTL = "1s" # Default -# MaxStaleAge is that maximum amount of time that a value can be stale -# before it is deleted from the cache (a form of garbage collection). -# -# This should generally be set to something much larger than -# LatestReportTTL. Setting to zero disables garbage collection. -MaxStaleAge = "1h" # Default -# LatestReportDeadline controls how long to wait for a response from the -# mercury server before retrying. Setting this to zero will wait indefinitely. -LatestReportDeadline = "5s" # Default -``` + ``` + [Mercury] + + # Mercury.Cache controls settings for the price retrieval cache querying a mercury server + [Mercury.Cache] + # LatestReportTTL controls how "stale" we will allow a price to be e.g. if + # set to 1s, a new price will always be fetched if the last result was + # from 1 second ago or older. + # + # Another way of looking at it is such: the cache will _never_ return a + # price that was queried from now-LatestReportTTL or before. + # + # Setting to zero disables caching entirely. + LatestReportTTL = "1s" # Default + # MaxStaleAge is that maximum amount of time that a value can be stale + # before it is deleted from the cache (a form of garbage collection). + # + # This should generally be set to something much larger than + # LatestReportTTL. Setting to zero disables garbage collection. + MaxStaleAge = "1h" # Default + # LatestReportDeadline controls how long to wait for a response from the + # mercury server before retrying. Setting this to zero will wait indefinitely. + LatestReportDeadline = "5s" # Default + ``` - New prom metrics for the mercury cache: `mercury_cache_fetch_failure_count` `mercury_cache_hit_count` `mercury_cache_wait_count` `mercury_cache_miss_count` - +- Added new `EVM.OCR` TOML config fields `DeltaCOverride` and `DeltaCJitterOverride` for overriding the config DeltaC. ### Changed diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 61d079fa4a6..38aac7085e8 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -1771,6 +1771,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -1850,6 +1852,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -1929,6 +1933,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2008,6 +2014,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2088,6 +2096,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2167,6 +2177,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2246,6 +2258,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2326,6 +2340,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2405,6 +2421,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '2s' DatabaseTimeout = '2s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '500ms' [OCR2] @@ -2483,6 +2501,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2561,6 +2581,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2640,6 +2662,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '2s' DatabaseTimeout = '2s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '500ms' [OCR2] @@ -2720,6 +2744,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2799,6 +2825,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '2s' DatabaseTimeout = '2s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '500ms' [OCR2] @@ -2878,6 +2906,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -2957,6 +2987,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3036,6 +3068,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3115,6 +3149,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3194,6 +3230,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3274,6 +3312,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3353,6 +3393,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3431,6 +3473,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3510,6 +3554,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3589,6 +3635,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3668,6 +3716,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3746,6 +3796,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3825,6 +3877,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3904,6 +3958,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -3982,6 +4038,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4061,6 +4119,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4141,6 +4201,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4220,6 +4282,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4299,6 +4363,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4378,6 +4444,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4457,6 +4525,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4535,6 +4605,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4613,6 +4685,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4692,6 +4766,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4771,6 +4847,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4851,6 +4929,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -4931,6 +5011,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -5010,6 +5092,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -5088,6 +5172,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -5166,6 +5252,8 @@ LeaseDuration = '0s' ContractConfirmations = 1 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -5245,6 +5333,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -5324,6 +5414,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -5403,6 +5495,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [OCR2] @@ -6077,6 +6171,8 @@ Set to '0s' to disable ContractConfirmations = 4 # Default ContractTransmitterTransmitTimeout = '10s' # Default DatabaseTimeout = '10s' # Default +DeltaCOverride = "168h" # Default +DeltaCJitterOverride = "1h" # Default ObservationGracePeriod = '1s' # Default ``` @@ -6099,6 +6195,22 @@ DatabaseTimeout = '10s' # Default ``` DatabaseTimeout sets `OCR.DatabaseTimeout` for this EVM chain. +### DeltaCOverride +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +DeltaCOverride = "168h" # Default +``` +DeltaCOverride (and `DeltaCJitterOverride`) determine the config override DeltaC. +DeltaC is the maximum age of the latest report in the contract. If the maximum age is exceeded, a new report will be +created by the report generation protocol. + +### DeltaCJitterOverride +:warning: **_ADVANCED_**: _Do not change this setting unless you know what you are doing._ +```toml +DeltaCJitterOverride = "1h" # Default +``` +DeltaCJitterOverride is the range for jitter to add to `DeltaCOverride`. + ### ObservationGracePeriod ```toml ObservationGracePeriod = '1s' # Default diff --git a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar index 45b08f0e52f..c1ac26c8d02 100644 --- a/testdata/scripts/node/validate/disk-based-logging-disabled.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-disabled.txtar @@ -367,6 +367,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar index 2869af3e2de..5ae75ffca68 100644 --- a/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar +++ b/testdata/scripts/node/validate/disk-based-logging-no-dir.txtar @@ -367,6 +367,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/testdata/scripts/node/validate/disk-based-logging.txtar b/testdata/scripts/node/validate/disk-based-logging.txtar index fb705819fc2..c8b3eb4b98b 100644 --- a/testdata/scripts/node/validate/disk-based-logging.txtar +++ b/testdata/scripts/node/validate/disk-based-logging.txtar @@ -367,6 +367,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/testdata/scripts/node/validate/invalid.txtar b/testdata/scripts/node/validate/invalid.txtar index 7b82d3323b1..fd591212d08 100644 --- a/testdata/scripts/node/validate/invalid.txtar +++ b/testdata/scripts/node/validate/invalid.txtar @@ -357,6 +357,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] diff --git a/testdata/scripts/node/validate/valid.txtar b/testdata/scripts/node/validate/valid.txtar index 91fe0952dd8..020e66da527 100644 --- a/testdata/scripts/node/validate/valid.txtar +++ b/testdata/scripts/node/validate/valid.txtar @@ -364,6 +364,8 @@ LeaseDuration = '0s' ContractConfirmations = 4 ContractTransmitterTransmitTimeout = '10s' DatabaseTimeout = '10s' +DeltaCOverride = '168h0m0s' +DeltaCJitterOverride = '1h0m0s' ObservationGracePeriod = '1s' [EVM.OCR2] From 4fbb56e8a586c48422670eb16396c27b1303d57c Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:41:52 -0800 Subject: [PATCH 215/271] release/2.7.1 -> develop (#11370) * Bump version and update CHANGELOG for core v2.7.0 * core: log a warning when deprecated P2P.V1 config is set in TOML (#11073) * core: log a warning when deprecated P2P.V1 config is set in TOML * change default P2P from V1 to V2 * bump operator ui (cherry picked from commit 5808e734cf024a5e8c5450e939e1f2d5b3cba546) * operator-ui deprecation warnings (#11104) Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Co-authored-by: github-merge-queue[bot] Co-authored-by: Jordan Krage * chore: bump sigstore/cosign-installer from 2.1.0 to 3.1.2 * Finalize date on changelog for 2.7.0 * Bump version and update CHANGELOG for core v2.7.1 Signed-off-by: Sneha Agnihotri * [Hotfix] Update loading next sequence map to avoid startup failure (#11319) * Finalize date on changelog for 2.7.1 * fix failing CI tests for 2.7.1 * bump solana version in tests built --------- Signed-off-by: Sneha Agnihotri Co-authored-by: Jordan Krage Co-authored-by: chainchad <96362174+chainchad@users.noreply.github.com> Co-authored-by: george-dorin <120329946+george-dorin@users.noreply.github.com> Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com> Co-authored-by: github-merge-queue[bot] Co-authored-by: Erik Burton Co-authored-by: amit-momin <108959691+amit-momin@users.noreply.github.com> Co-authored-by: anirudhwarrier <12178754+anirudhwarrier@users.noreply.github.com> Co-authored-by: Tate --- VERSION | 2 +- docs/CHANGELOG.md | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 24ba9a38de6..860487ca19c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.0 +2.7.1 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 727f5ad30dd..c5f243626d2 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -50,7 +50,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `mercury_cache_miss_count` - Added new `EVM.OCR` TOML config fields `DeltaCOverride` and `DeltaCJitterOverride` for overriding the config DeltaC. - ### Changed - `L2Suggested` mode is now called `SuggestedPrice` @@ -71,13 +70,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 -## 2.7.1 - UNRELEASED +## 2.7.1 - 2023-11-21 ### Fixed - Fixed a bug that causes the node to shutdown if all configured RPC's are unreachable during startup. -## 2.7.0 - UNRELEASED +## 2.7.0 - 2023-11-14 ### Added From c8ca97e27222d115b55f6dcd858b36e5cb4bc1a3 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 28 Nov 2023 19:32:15 -0500 Subject: [PATCH 216/271] [BCI-2235] Abandoned Transaction Tracking (#11143) * Create Abandoned Tracker * Handle abandoned transactions * Update abandoned_tracker.go * fix tabbing * Query for non-finalized txes * fix tracker * Resend abandoned txes * Add evm_tx_store test * Resend abandoned transactions * add tracker testing * presubmit * tidy * generate * fix test * lint * tidy * fix txmgr tests * generate * Get tx by ID * generate * ensure tx attemps * Confirm abandoned txes * tracker reset * lint * Update tracker.go * Get Tx by ID * Update text * Make tracker its own component * check finality_depth * remove TxAbandoned state * generate * Fix race conditions * Update db functions * Add state machine to tracker * Delete coverage.txt * lint * count receipts in query * add context * update cltest * Add tracker description * filter chainID * update logger * generate * Update CHANGELOG.md * Test unstarted txes * lint * undo imports --------- Co-authored-by: amit-momin <108959691+amit-momin@users.noreply.github.com> --- common/txmgr/resender.go | 27 +- common/txmgr/test_helpers.go | 11 +- common/txmgr/tracker.go | 336 ++++++++++++++++++ common/txmgr/txmgr.go | 19 +- common/txmgr/types/mocks/tx_store.go | 76 ++++ common/txmgr/types/tx_store.go | 3 + core/chains/evm/txmgr/builder.go | 30 +- core/chains/evm/txmgr/evm_tx_store.go | 85 +++++ core/chains/evm/txmgr/evm_tx_store_test.go | 102 +++++- core/chains/evm/txmgr/mocks/evm_tx_store.go | 76 ++++ core/chains/evm/txmgr/models.go | 3 +- core/chains/evm/txmgr/resender_test.go | 6 +- core/chains/evm/txmgr/tracker_test.go | 161 +++++++++ core/internal/cltest/factories.go | 1 + core/scripts/common/vrf/model/model.go | 3 +- core/services/vrf/v2/integration_v2_test.go | 2 +- core/services/vrf/v2/listener_v2_test.go | 2 +- ...10_remove_evm_key_states_fk_constraint.sql | 4 + docs/CHANGELOG.md | 1 + 19 files changed, 920 insertions(+), 28 deletions(-) create mode 100644 common/txmgr/tracker.go create mode 100644 core/chains/evm/txmgr/tracker_test.go create mode 100644 core/store/migrate/migrations/0210_remove_evm_key_states_fk_constraint.sql diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index ce770055609..06c466e1730 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -41,11 +41,13 @@ type Resender[ ADDR types.Hashable, TX_HASH types.Hashable, BLOCK_HASH types.Hashable, + R txmgrtypes.ChainReceipt[TX_HASH, BLOCK_HASH], SEQ types.Sequence, FEE feetypes.Fee, ] struct { txStore txmgrtypes.TransactionStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, SEQ, FEE] client txmgrtypes.TransactionClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] ks txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ] chainID CHAIN_ID interval time.Duration @@ -64,25 +66,28 @@ func NewResender[ ADDR types.Hashable, TX_HASH types.Hashable, BLOCK_HASH types.Hashable, + R txmgrtypes.ChainReceipt[TX_HASH, BLOCK_HASH], SEQ types.Sequence, FEE feetypes.Fee, ]( lggr logger.Logger, txStore txmgrtypes.TransactionStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, SEQ, FEE], client txmgrtypes.TransactionClient[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], + tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], ks txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ], pollInterval time.Duration, config txmgrtypes.ResenderChainConfig, txConfig txmgrtypes.ResenderTransactionsConfig, -) *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] { +) *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { if txConfig.ResendAfterThreshold() == 0 { panic("Resender requires a non-zero threshold") } // todo: add context to txStore https://smartcontract-it.atlassian.net/browse/BCI-1585 ctx, cancel := context.WithCancel(context.Background()) - return &Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]{ + return &Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ txStore, client, + tracker, ks, client.ConfiguredChainID(), pollInterval, @@ -97,18 +102,18 @@ func NewResender[ } // Start is a comment which satisfies the linter -func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Start() { +func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start() { er.logger.Debugf("Enabled with poll interval of %s and age threshold of %s", er.interval, er.txConfig.ResendAfterThreshold()) go er.runLoop() } // Stop is a comment which satisfies the linter -func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Stop() { +func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Stop() { er.cancel() <-er.chDone } -func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) runLoop() { +func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() { defer close(er.chDone) if err := er.resendUnconfirmed(); err != nil { @@ -129,16 +134,20 @@ func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) runLoop() { } } -func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) resendUnconfirmed() error { - enabledAddresses, err := er.ks.EnabledAddressesForChain(er.chainID) +func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) resendUnconfirmed() error { + resendAddresses, err := er.ks.EnabledAddressesForChain(er.chainID) if err != nil { return fmt.Errorf("Resender failed getting enabled keys for chain %s: %w", er.chainID.String(), err) } + + resendAddresses = append(resendAddresses, er.tracker.GetAbandonedAddresses()...) + ageThreshold := er.txConfig.ResendAfterThreshold() maxInFlightTransactions := er.txConfig.MaxInFlight() olderThan := time.Now().Add(-ageThreshold) var allAttempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] - for _, k := range enabledAddresses { + + for _, k := range resendAddresses { var attempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] attempts, err = er.txStore.FindTxAttemptsRequiringResend(er.ctx, olderThan, maxInFlightTransactions, er.chainID, k) if err != nil { @@ -189,7 +198,7 @@ func logResendResult(lggr logger.Logger, codes []client.SendTxReturnCode) { lggr.Debugw("Completed", "n", len(codes), "nNew", nNew, "nFatal", nFatal) } -func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) logStuckAttempts(attempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], fromAddress ADDR) { +func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) logStuckAttempts(attempts []txmgrtypes.TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], fromAddress ADDR) { if time.Since(er.lastAlertTimestamps[fromAddress.String()]) >= unconfirmedTxAlertLogFrequency { oldestAttempt, exists := findOldestUnconfirmedAttempt(attempts) if exists { diff --git a/common/txmgr/test_helpers.go b/common/txmgr/test_helpers.go index 95d08c2e953..0f128a23af4 100644 --- a/common/txmgr/test_helpers.go +++ b/common/txmgr/test_helpers.go @@ -2,6 +2,7 @@ package txmgr import ( "context" + "time" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" ) @@ -13,6 +14,14 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) XXX ec.client = client } +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) XXXTestSetTTL(ttl time.Duration) { + tr.ttl = ttl +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) XXXDeliverBlock(blockHeight int64) { + tr.mb.Deliver(blockHeight) +} + func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) XXXTestStartInternal() error { return eb.startInternal() } @@ -33,7 +42,7 @@ func (ec *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) XXX return ec.closeInternal() } -func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) XXXTestResendUnconfirmed() error { +func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) XXXTestResendUnconfirmed() error { return er.resendUnconfirmed() } diff --git a/common/txmgr/tracker.go b/common/txmgr/tracker.go new file mode 100644 index 00000000000..1a24dd5b5fe --- /dev/null +++ b/common/txmgr/tracker.go @@ -0,0 +1,336 @@ +package txmgr + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-common/pkg/services" + feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" + txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" + "github.com/smartcontractkit/chainlink/v2/common/types" + + "github.com/smartcontractkit/chainlink/v2/core/utils" +) + +const ( + // defaultTTL is the default time to live for abandoned transactions + // After this TTL, the TXM stops tracking abandoned Txs. + defaultTTL = 6 * time.Hour + // handleTxesTimeout represents a sanity limit on how long handleTxesByState + // should take to complete + handleTxesTimeout = 10 * time.Minute +) + +// AbandonedTx is a transaction who's 'FromAddress' was removed from the KeyStore(by the Node Operator). +// Thus, any new attempts for this Tx can't be signed/created. This means no fee bumping can be done. +// However, the Tx may still have live attempts in the chain's mempool, and could get confirmed on the +// chain as-is. Thus, the TXM should not directly discard this Tx. +type AbandonedTx[ADDR types.Hashable] struct { + id int64 + fromAddress ADDR +} + +// Tracker tracks all transactions which have abandoned fromAddresses. +// The fromAddresses can be deleted by Node Operators from the KeyStore. In such cases, +// existing in-flight transactions for these fromAddresses are considered abandoned too. +// Since such Txs can still have attempts on chain's mempool, these could still be confirmed. +// This tracker just tracks such Txs for some time, in case they get confirmed as-is. +type Tracker[ + CHAIN_ID types.ID, + ADDR types.Hashable, + TX_HASH types.Hashable, + BLOCK_HASH types.Hashable, + R txmgrtypes.ChainReceipt[TX_HASH, BLOCK_HASH], + SEQ types.Sequence, + FEE feetypes.Fee, +] struct { + services.StateMachine + txStore txmgrtypes.TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE] + keyStore txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ] + chainID CHAIN_ID + lggr logger.Logger + enabledAddrs map[ADDR]bool + txCache map[int64]AbandonedTx[ADDR] + ttl time.Duration + lock sync.Mutex + mb *utils.Mailbox[int64] + wg sync.WaitGroup + isStarted bool + ctx context.Context + ctxCancel context.CancelFunc +} + +func NewTracker[ + CHAIN_ID types.ID, + ADDR types.Hashable, + TX_HASH types.Hashable, + BLOCK_HASH types.Hashable, + R txmgrtypes.ChainReceipt[TX_HASH, BLOCK_HASH], + SEQ types.Sequence, + FEE feetypes.Fee, +]( + txStore txmgrtypes.TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE], + keyStore txmgrtypes.KeyStore[ADDR, CHAIN_ID, SEQ], + chainID CHAIN_ID, + lggr logger.Logger, +) *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { + return &Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ + txStore: txStore, + keyStore: keyStore, + chainID: chainID, + lggr: logger.Named(lggr, "TxMgrTracker"), + enabledAddrs: map[ADDR]bool{}, + txCache: map[int64]AbandonedTx[ADDR]{}, + ttl: defaultTTL, + mb: utils.NewSingleMailbox[int64](), + lock: sync.Mutex{}, + wg: sync.WaitGroup{}, + } +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(_ context.Context) (err error) { + tr.lock.Lock() + defer tr.lock.Unlock() + return tr.StartOnce("Tracker", func() error { + return tr.startInternal() + }) +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) startInternal() (err error) { + tr.ctx, tr.ctxCancel = context.WithCancel(context.Background()) + + if err := tr.setEnabledAddresses(); err != nil { + return fmt.Errorf("failed to set enabled addresses: %w", err) + } + + if err := tr.trackAbandonedTxes(tr.ctx); err != nil { + return fmt.Errorf("failed to track abandoned txes: %w", err) + } + + tr.isStarted = true + if len(tr.txCache) == 0 { + tr.lggr.Infow("no abandoned txes found, skipping runLoop") + return nil + } + tr.wg.Add(1) + go tr.runLoop() + return nil +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() error { + tr.lock.Lock() + defer tr.lock.Unlock() + return tr.StopOnce("Tracker", func() error { + return tr.closeInternal() + }) +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) closeInternal() error { + tr.lggr.Infow("stopping tracker") + if !tr.isStarted { + return fmt.Errorf("tracker not started") + } + tr.ctxCancel() + tr.wg.Wait() + tr.isStarted = false + return nil +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() { + defer tr.wg.Done() + ttlExceeded := time.NewTicker(tr.ttl) + defer ttlExceeded.Stop() + for { + select { + case <-tr.mb.Notify(): + for { + if tr.ctx.Err() != nil { + return + } + blockHeight, exists := tr.mb.Retrieve() + if !exists { + break + } + if err := tr.HandleTxesByState(tr.ctx, blockHeight); err != nil { + tr.lggr.Errorw(fmt.Errorf("failed to handle txes by state: %w", err).Error()) + } + } + case <-ttlExceeded.C: + tr.lggr.Infow("ttl exceeded") + tr.MarkAllTxesFatal(tr.ctx) + return + case <-tr.ctx.Done(): + return + } + } +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetAbandonedAddresses() []ADDR { + tr.lock.Lock() + defer tr.lock.Unlock() + + if !tr.isStarted { + return []ADDR{} + } + + abandonedAddrs := make([]ADDR, len(tr.txCache)) + for _, atx := range tr.txCache { + abandonedAddrs = append(abandonedAddrs, atx.fromAddress) + } + return abandonedAddrs +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) IsStarted() bool { + tr.lock.Lock() + defer tr.lock.Unlock() + return tr.isStarted +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) setEnabledAddresses() error { + enabledAddrs, err := tr.keyStore.EnabledAddressesForChain(tr.chainID) + if err != nil { + return fmt.Errorf("failed to get enabled addresses for chain: %w", err) + } + + if len(enabledAddrs) == 0 { + tr.lggr.Warnf("enabled address list is empty") + } + + for _, addr := range enabledAddrs { + tr.enabledAddrs[addr] = true + } + return nil +} + +// trackAbandonedTxes called once to find and insert all abandoned txes into the tracker. +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) trackAbandonedTxes(ctx context.Context) (err error) { + if tr.isStarted { + return fmt.Errorf("tracker already started") + } + + nonFatalTxes, err := tr.txStore.GetNonFatalTransactions(ctx, tr.chainID) + if err != nil { + return fmt.Errorf("failed to get non fatal txes from txStore: %w", err) + } + + // insert abandoned txes + for _, tx := range nonFatalTxes { + if !tr.enabledAddrs[tx.FromAddress] { + tr.insertTx(tx) + } + } + + if err := tr.handleTxesByState(ctx, 0); err != nil { + return fmt.Errorf("failed to handle txes by state: %w", err) + } + + return nil +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HandleTxesByState(ctx context.Context, blockHeight int64) error { + tr.lock.Lock() + defer tr.lock.Unlock() + tr.ctx, tr.ctxCancel = context.WithTimeout(ctx, handleTxesTimeout) + defer tr.ctxCancel() + return tr.handleTxesByState(ctx, blockHeight) +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleTxesByState(ctx context.Context, blockHeight int64) error { + for id, atx := range tr.txCache { + tx, err := tr.txStore.GetTxByID(ctx, atx.id) + if err != nil { + return fmt.Errorf("failed to get tx by ID: %w", err) + } + + switch tx.State { + case TxConfirmed: + if err := tr.handleConfirmedTx(tx, blockHeight); err != nil { + return fmt.Errorf("failed to handle confirmed txes: %w", err) + } + case TxConfirmedMissingReceipt, TxUnconfirmed: + // Keep tracking tx + case TxInProgress, TxUnstarted: + // Tx could never be sent on chain even once. That means that we need to sign + // an attempt to even broadcast this Tx to the chain. Since the fromAddress + // is deleted, we can't sign it. + errMsg := "The FromAddress for this Tx was deleted before this Tx could be broadcast to the chain." + if err := tr.markTxFatal(ctx, tx, errMsg); err != nil { + return fmt.Errorf("failed to mark tx as fatal: %w", err) + } + delete(tr.txCache, id) + case TxFatalError: + delete(tr.txCache, id) + default: + tr.lggr.Errorw(fmt.Sprintf("unhandled transaction state: %v", tx.State)) + } + } + + return nil +} + +// handleConfirmedTx removes a transaction from the tracker if it's been finalized on chain +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleConfirmedTx( + tx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], + blockHeight int64, +) error { + finalized, err := tr.txStore.IsTxFinalized(tr.ctx, blockHeight, tx.ID, tr.chainID) + if err != nil { + return fmt.Errorf("failed to check if tx is finalized: %w", err) + } + + if finalized { + delete(tr.txCache, tx.ID) + } + + return nil +} + +// insertTx inserts a transaction into the tracker as an AbandonedTx +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) insertTx( + tx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) { + if _, contains := tr.txCache[tx.ID]; contains { + return + } + + tr.txCache[tx.ID] = AbandonedTx[ADDR]{ + id: tx.ID, + fromAddress: tx.FromAddress, + } + tr.lggr.Debugw(fmt.Sprintf("inserted tx %v", tx.ID)) +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) markTxFatal(ctx context.Context, + tx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], + errMsg string) error { + tx.Error.SetValid(errMsg) + + // Set state to TxInProgress so the tracker can attempt to mark it as fatal + tx.State = TxInProgress + if err := tr.txStore.UpdateTxFatalError(ctx, tx); err != nil { + return fmt.Errorf("failed to mark tx %v as abandoned: %w", tx.ID, err) + } + return nil +} + +func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) MarkAllTxesFatal(ctx context.Context) { + tr.lock.Lock() + defer tr.lock.Unlock() + errMsg := fmt.Sprintf( + "fromAddress for this Tx was deleted, and existing attempts onchain didn't finalize within %d hours, thus this Tx was abandoned.", + int(tr.ttl.Hours())) + + for _, atx := range tr.txCache { + tx, err := tr.txStore.GetTxByID(ctx, atx.id) + if err != nil { + tr.lggr.Errorw(fmt.Errorf("failed to get tx by ID: %w", err).Error()) + continue + } + + if err := tr.markTxFatal(ctx, tx, errMsg); err != nil { + tr.lggr.Errorw(fmt.Errorf("failed to mark tx as abandoned: %w", err).Error()) + } + } +} diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 63bf039d8f7..3aac88e2f4b 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -95,9 +95,10 @@ type Txm[ wg sync.WaitGroup reaper *Reaper[CHAIN_ID] - resender *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + resender *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] broadcaster *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] confirmer *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] + tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] fwdMgr txmgrtypes.ForwarderManager[ADDR] txAttemptBuilder txmgrtypes.TxAttemptBuilder[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] sequenceSyncer SequenceSyncer[ADDR, TX_HASH, BLOCK_HASH, SEQ] @@ -132,7 +133,8 @@ func NewTxm[ sequenceSyncer SequenceSyncer[ADDR, TX_HASH, BLOCK_HASH, SEQ], broadcaster *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], confirmer *Confirmer[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], - resender *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], + resender *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], + tracker *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE], ) *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE] { b := Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]{ logger: lggr, @@ -153,6 +155,7 @@ func NewTxm[ broadcaster: broadcaster, confirmer: confirmer, resender: resender, + tracker: tracker, } if txCfg.ResendAfterThreshold() <= 0 { @@ -183,6 +186,10 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return fmt.Errorf("Txm: Estimator failed to start: %w", err) } + if err := ms.Start(ctx, b.tracker); err != nil { + return fmt.Errorf("Txm: Tracker failed to start: %w", err) + } + b.wg.Add(1) go b.runLoop() <-b.chSubbed @@ -260,6 +267,10 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() (m merr = errors.Join(merr, fmt.Errorf("Txm: failed to close TxAttemptBuilder: %w", err)) } + if err := b.tracker.Close(); err != nil { + merr = errors.Join(merr, fmt.Errorf("Txm: failed to close Tracker: %w", err)) + } + return nil }) } @@ -371,6 +382,7 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() b.broadcaster.Trigger(address) case head := <-b.chHeads: b.confirmer.mb.Deliver(head) + b.tracker.mb.Deliver(head.BlockNumber()) case reset := <-b.reset: // This check prevents the weird edge-case where you can select // into this block after chStop has already been closed and the @@ -396,6 +408,9 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() if err := utils.EnsureClosed(b.confirmer); err != nil { b.logger.Panicw(fmt.Sprintf("Failed to Close Confirmer: %v", err), "err", err) } + if err := utils.EnsureClosed(b.tracker); err != nil { + b.logger.Panicw(fmt.Sprintf("Failed to Close Tracker: %v", err), "err", err) + } return case <-keysChanged: // This check prevents the weird edge-case where you can select diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index 0e344b9b6f9..0a7738fd68a 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -544,6 +544,58 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetInProgre return r0, r1 } +// GetNonFatalTransactions provides a mock function with given fields: ctx, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetNonFatalTransactions(ctx context.Context, chainID CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, chainID) + + var r0 []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, CHAIN_ID) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetTxByID provides a mock function with given fields: ctx, id +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTxByID(ctx context.Context, id int64) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { + ret := _m.Called(ctx, id) + + var r0 *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetTxInProgress provides a mock function with given fields: ctx, fromAddress func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) GetTxInProgress(ctx context.Context, fromAddress ADDR) (*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, fromAddress) @@ -594,6 +646,30 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HasInProgre return r0, r1 } +// IsTxFinalized provides a mock function with given fields: ctx, blockHeight, txID, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID CHAIN_ID) (bool, error) { + ret := _m.Called(ctx, blockHeight, txID, chainID) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) (bool, error)); ok { + return rf(ctx, blockHeight, txID, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, int64, CHAIN_ID) bool); ok { + r0 = rf(ctx, blockHeight, txID, chainID) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, int64, CHAIN_ID) error); ok { + r1 = rf(ctx, blockHeight, txID, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // LoadTxAttempts provides a mock function with given fields: ctx, etx func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) LoadTxAttempts(ctx context.Context, etx *txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error { ret := _m.Called(ctx, etx) diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index f731031f926..251135795fd 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -81,6 +81,8 @@ type TransactionStore[ FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber, lowBlockNumber int64, chainID CHAIN_ID) (etxs []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) GetTxInProgress(ctx context.Context, fromAddress ADDR) (etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) GetInProgressTxAttempts(ctx context.Context, address ADDR, chainID CHAIN_ID) (attempts []TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + GetNonFatalTransactions(ctx context.Context, chainID CHAIN_ID) (txs []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + GetTxByID(ctx context.Context, id int64) (tx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) HasInProgressTransaction(ctx context.Context, account ADDR, chainID CHAIN_ID) (exists bool, err error) LoadTxAttempts(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error MarkAllConfirmedMissingReceipt(ctx context.Context, chainID CHAIN_ID) (err error) @@ -100,6 +102,7 @@ type TransactionStore[ UpdateTxUnstartedToInProgress(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], attempt *TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error UpdateTxFatalError(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error UpdateTxForRebroadcast(ctx context.Context, etx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], etxAttempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error + IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID CHAIN_ID) (finalized bool, err error) } type TxHistoryReaper[CHAIN_ID types.ID] interface { diff --git a/core/chains/evm/txmgr/builder.go b/core/chains/evm/txmgr/builder.go index 84aa21e4de2..f0cbcbf8d92 100644 --- a/core/chains/evm/txmgr/builder.go +++ b/core/chains/evm/txmgr/builder.go @@ -50,13 +50,15 @@ func NewTxm( txmCfg := NewEvmTxmConfig(chainConfig) // wrap Evm specific config feeCfg := NewEvmTxmFeeConfig(fCfg) // wrap Evm specific config txmClient := NewEvmTxmClient(client) // wrap Evm specific client - ethBroadcaster := NewEvmBroadcaster(txStore, txmClient, txmCfg, feeCfg, txConfig, listenerConfig, keyStore, txAttemptBuilder, txNonceSyncer, lggr, checker, chainConfig.NonceAutoSync()) - ethConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr) - var ethResender *Resender + chainID := txmClient.ConfiguredChainID() + evmBroadcaster := NewEvmBroadcaster(txStore, txmClient, txmCfg, feeCfg, txConfig, listenerConfig, keyStore, txAttemptBuilder, txNonceSyncer, lggr, checker, chainConfig.NonceAutoSync()) + evmTracker := NewEvmTracker(txStore, keyStore, chainID, lggr) + evmConfirmer := NewEvmConfirmer(txStore, txmClient, txmCfg, feeCfg, txConfig, dbConfig, keyStore, txAttemptBuilder, lggr) + var evmResender *Resender if txConfig.ResendAfterThreshold() > 0 { - ethResender = NewEvmResender(lggr, txStore, txmClient, keyStore, txmgr.DefaultResenderPollInterval, chainConfig, txConfig) + evmResender = NewEvmResender(lggr, txStore, txmClient, evmTracker, keyStore, txmgr.DefaultResenderPollInterval, chainConfig, txConfig) } - txm = NewEvmTxm(txmClient.ConfiguredChainID(), txmCfg, txConfig, keyStore, lggr, checker, fwdMgr, txAttemptBuilder, txStore, txNonceSyncer, ethBroadcaster, ethConfirmer, ethResender) + txm = NewEvmTxm(chainID, txmCfg, txConfig, keyStore, lggr, checker, fwdMgr, txAttemptBuilder, txStore, txNonceSyncer, evmBroadcaster, evmConfirmer, evmResender, evmTracker) return txm, nil } @@ -75,21 +77,23 @@ func NewEvmTxm( broadcaster *Broadcaster, confirmer *Confirmer, resender *Resender, + tracker *Tracker, ) *Txm { - return txmgr.NewTxm(chainId, cfg, txCfg, keyStore, lggr, checkerFactory, fwdMgr, txAttemptBuilder, txStore, nonceSyncer, broadcaster, confirmer, resender) + return txmgr.NewTxm(chainId, cfg, txCfg, keyStore, lggr, checkerFactory, fwdMgr, txAttemptBuilder, txStore, nonceSyncer, broadcaster, confirmer, resender, tracker) } -// NewEvnResender creates a new concrete EvmResender +// NewEvmResender creates a new concrete EvmResender func NewEvmResender( lggr logger.Logger, txStore TransactionStore, client TransactionClient, + tracker *Tracker, ks KeyStore, pollInterval time.Duration, config EvmResenderConfig, txConfig txmgrtypes.ResenderTransactionsConfig, ) *Resender { - return txmgr.NewResender(lggr, txStore, client, ks, pollInterval, config, txConfig) + return txmgr.NewResender(lggr, txStore, client, tracker, ks, pollInterval, config, txConfig) } // NewEvmReaper instantiates a new EVM-specific reaper object @@ -112,6 +116,16 @@ func NewEvmConfirmer( return txmgr.NewConfirmer(txStore, client, chainConfig, feeConfig, txConfig, dbConfig, keystore, txAttemptBuilder, lggr, func(r *evmtypes.Receipt) bool { return r == nil }) } +// NewEvmTracker instantiates a new EVM tracker for abandoned transactions +func NewEvmTracker( + txStore TxStore, + keyStore KeyStore, + chainID *big.Int, + lggr logger.Logger, +) *Tracker { + return txmgr.NewTracker(txStore, keyStore, chainID, lggr) +} + // NewEvmBroadcaster returns a new concrete EvmBroadcaster func NewEvmBroadcaster( txStore TransactionStore, diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 2788c2fd1c9..51c9f98e884 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -69,6 +69,7 @@ type TestEvmTxStore interface { FindTxAttemptsByTxIDs(ids []int64) ([]TxAttempt, error) InsertTxAttempt(attempt *TxAttempt) error LoadTxesAttempts(etxs []*Tx, qopts ...pg.QOpt) error + GetFatalTransactions(ctx context.Context) (txes []*Tx, err error) } type evmTxStore struct { @@ -552,6 +553,26 @@ func (o *evmTxStore) InsertReceipt(receipt *evmtypes.Receipt) (int64, error) { return r.ID, pkgerrors.Wrap(err, "InsertReceipt failed") } +func (o *evmTxStore) GetFatalTransactions(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)) + err = qq.Transaction(func(tx pg.Queryer) error { + stmt := `SELECT * FROM evm.txes WHERE state = 'fatal_error'` + var dbEtxs []DbEthTx + if err = tx.Select(&dbEtxs, stmt); err != nil { + return fmt.Errorf("failed to load evm.txes: %w", err) + } + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + err = o.LoadTxesAttempts(txes, pg.WithParentCtx(ctx), pg.WithQueryer(tx)) + return fmt.Errorf("failed to load evm.tx_attempts: %w", err) + }, pg.OptReadOnlyTx()) + + return txes, nil +} + // FindTxWithAttempts finds the Tx with its attempts and receipts preloaded func (o *evmTxStore) FindTxWithAttempts(etxID int64) (etx Tx, err error) { err = o.q.Transaction(func(tx pg.Queryer) error { @@ -1107,6 +1128,25 @@ ORDER BY nonce ASC return etxs, pkgerrors.Wrap(err, "FindTransactionsConfirmedInBlockRange failed") } +func (o *evmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int) (finalized bool, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + + var count int32 + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + err = qq.GetContext(ctx, &count, ` + SELECT COUNT(evm.receipts.receipt) FROM evm.txes + INNER JOIN evm.tx_attempts ON evm.txes.id = evm.tx_attempts.eth_tx_id + INNER JOIN evm.receipts ON evm.tx_attempts.hash = evm.receipts.tx_hash + WHERE evm.receipts.block_number <= ($1 - evm.txes.min_confirmations) + AND evm.txes.id = $2 AND evm.txes.evm_chain_id = $3`, blockHeight, txID, chainID.String()) + if err != nil { + return false, fmt.Errorf("failed to retrieve transaction reciepts: %w", err) + } + return count > 0, nil +} + func saveAttemptWithNewState(ctx context.Context, q pg.Queryer, logger logger.Logger, attempt TxAttempt, broadcastAt time.Time) error { var dbAttempt DbEthTxAttempt dbAttempt.FromTxAttempt(&attempt) @@ -1223,6 +1263,51 @@ func (o *evmTxStore) SaveInProgressAttempt(ctx context.Context, attempt *TxAttem return nil } +func (o *evmTxStore) GetNonFatalTransactions(ctx context.Context, chainID *big.Int) (txes []*Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + err = qq.Transaction(func(tx pg.Queryer) error { + stmt := `SELECT * FROM evm.txes WHERE state <> 'fatal_error' AND evm_chain_id = $1` + var dbEtxs []DbEthTx + if err = tx.Select(&dbEtxs, stmt, chainID.String()); err != nil { + return fmt.Errorf("failed to load evm.txes: %w", err) + } + txes = make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + err = o.LoadTxesAttempts(txes, pg.WithParentCtx(ctx), pg.WithQueryer(tx)) + return fmt.Errorf("failed to load evm.txes: %w", err) + }, pg.OptReadOnlyTx()) + + return txes, nil +} + +func (o *evmTxStore) GetTxByID(ctx context.Context, id int64) (txe *Tx, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + + err = qq.Transaction(func(tx pg.Queryer) error { + stmt := `SELECT * FROM evm.txes WHERE id = $1` + var dbEtxs []DbEthTx + if err = tx.Select(&dbEtxs, stmt, id); err != nil { + return fmt.Errorf("failed to load evm.txes: %w", err) + } + txes := make([]*Tx, len(dbEtxs)) + dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) + if len(txes) != 1 { + return fmt.Errorf("failed to get tx with id %v", id) + } + txe = txes[0] + err = o.LoadTxesAttempts(txes, pg.WithParentCtx(ctx), pg.WithQueryer(tx)) + return fmt.Errorf("failed to load evm.tx_attempts: %w", err) + }, pg.OptReadOnlyTx()) + + return txe, nil +} + // FindTxsRequiringGasBump returns transactions that have all // attempts which are unconfirmed for at least gasBumpThreshold blocks, // limited by limit pending transactions diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index d2cafcb8efa..e68641735ee 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -786,6 +786,31 @@ func TestORM_UpdateTxForRebroadcast(t *testing.T) { }) } +func TestORM_IsTxFinalized(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + + t.Run("confirmed tx not past finality_depth", func(t *testing.T) { + confirmedAddr := cltest.MustGenerateRandomKey(t).Address + tx := mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) + finalized, err := txStore.IsTxFinalized(testutils.Context(t), 2, tx.ID, ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.False(t, finalized) + }) + + t.Run("confirmed tx past finality_depth", func(t *testing.T) { + confirmedAddr := cltest.MustGenerateRandomKey(t).Address + tx := mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) + finalized, err := txStore.IsTxFinalized(testutils.Context(t), 10, tx.ID, ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.True(t, finalized) + }) +} + func TestORM_FindTransactionsConfirmedInBlockRange(t *testing.T) { t.Parallel() @@ -1310,7 +1335,7 @@ func TestORM_UpdateTxUnstartedToInProgress(t *testing.T) { evmTxmCfg := txmgr.NewEvmTxmConfig(ccfg.EVM()) ec := evmtest.NewEthClientMockWithDefaultChain(t) txMgr := txmgr.NewEvmTxm(ec.ConfiguredChainID(), evmTxmCfg, ccfg.EVM().Transactions(), nil, logger.Test(t), nil, nil, - nil, txStore, nil, nil, nil, nil) + nil, txStore, nil, nil, nil, nil, nil) err := txMgr.XXXTestAbandon(fromAddress) // mark transaction as abandoned require.NoError(t, err) @@ -1365,6 +1390,81 @@ func TestORM_GetTxInProgress(t *testing.T) { }) } +func TestORM_GetNonFatalTransactions(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + + t.Run("gets 0 non finalized eth transaction", func(t *testing.T) { + txes, err := txStore.GetNonFatalTransactions(testutils.Context(t), ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.Empty(t, txes) + }) + + t.Run("get in progress, unstarted, and unconfirmed eth transactions", func(t *testing.T) { + inProgressTx := mustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) + unstartedTx := mustCreateUnstartedGeneratedTx(t, txStore, fromAddress, ethClient.ConfiguredChainID()) + + txes, err := txStore.GetNonFatalTransactions(testutils.Context(t), ethClient.ConfiguredChainID()) + require.NoError(t, err) + + for _, tx := range txes { + require.True(t, tx.ID == inProgressTx.ID || tx.ID == unstartedTx.ID) + } + }) +} + +func TestORM_GetTxByID(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + + t.Run("no transaction", func(t *testing.T) { + tx, err := txStore.GetTxByID(testutils.Context(t), int64(0)) + require.NoError(t, err) + require.Nil(t, tx) + }) + + t.Run("get transaction by ID", func(t *testing.T) { + insertedTx := mustInsertInProgressEthTxWithAttempt(t, txStore, 123, fromAddress) + tx, err := txStore.GetTxByID(testutils.Context(t), insertedTx.ID) + require.NoError(t, err) + require.NotNil(t, tx) + }) +} + +func TestORM_GetFatalTransactions(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + + t.Run("gets 0 fatal eth transactions", func(t *testing.T) { + txes, err := txStore.GetFatalTransactions(testutils.Context(t)) + require.NoError(t, err) + require.Empty(t, txes) + }) + + t.Run("get fatal transactions", func(t *testing.T) { + fatalTx := mustInsertFatalErrorEthTx(t, txStore, fromAddress) + txes, err := txStore.GetFatalTransactions(testutils.Context(t)) + require.NoError(t, err) + require.Equal(t, txes[0].ID, fatalTx.ID) + }) +} + func TestORM_HasInProgressTransaction(t *testing.T) { t.Parallel() diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index f491bda40bb..00efc1add98 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -649,6 +649,58 @@ func (_m *EvmTxStore) GetInProgressTxAttempts(ctx context.Context, address commo return r0, r1 } +// GetNonFatalTransactions provides a mock function with given fields: ctx, chainID +func (_m *EvmTxStore) GetNonFatalTransactions(ctx context.Context, chainID *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, chainID) + + var r0 []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) ([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) []*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, chainID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetTxByID provides a mock function with given fields: ctx, id +func (_m *EvmTxStore) GetTxByID(ctx context.Context, id int64) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { + ret := _m.Called(ctx, id) + + var r0 *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, int64) *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetTxInProgress provides a mock function with given fields: ctx, fromAddress func (_m *EvmTxStore) GetTxInProgress(ctx context.Context, fromAddress common.Address) (*types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee], error) { ret := _m.Called(ctx, fromAddress) @@ -699,6 +751,30 @@ func (_m *EvmTxStore) HasInProgressTransaction(ctx context.Context, account comm return r0, r1 } +// IsTxFinalized provides a mock function with given fields: ctx, blockHeight, txID, chainID +func (_m *EvmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int) (bool, error) { + ret := _m.Called(ctx, blockHeight, txID, chainID) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) (bool, error)); ok { + return rf(ctx, blockHeight, txID, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, int64, int64, *big.Int) bool); ok { + r0 = rf(ctx, blockHeight, txID, chainID) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, int64, int64, *big.Int) error); ok { + r1 = rf(ctx, blockHeight, txID, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // LoadTxAttempts provides a mock function with given fields: ctx, etx func (_m *EvmTxStore) LoadTxAttempts(ctx context.Context, etx *types.Tx[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee]) error { ret := _m.Called(ctx, etx) diff --git a/core/chains/evm/txmgr/models.go b/core/chains/evm/txmgr/models.go index 9044c52c9ae..4c622ec945a 100644 --- a/core/chains/evm/txmgr/models.go +++ b/core/chains/evm/txmgr/models.go @@ -19,7 +19,8 @@ import ( type ( Confirmer = txmgr.Confirmer[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] Broadcaster = txmgr.Broadcaster[*big.Int, *evmtypes.Head, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] - Resender = txmgr.Resender[*big.Int, common.Address, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] + Resender = txmgr.Resender[*big.Int, common.Address, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] + Tracker = txmgr.Tracker[*big.Int, common.Address, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] Reaper = txmgr.Reaper[*big.Int] TxStore = txmgrtypes.TxStore[common.Address, *big.Int, common.Hash, common.Hash, *evmtypes.Receipt, evmtypes.Nonce, gas.EvmFee] TransactionStore = txmgrtypes.TransactionStore[common.Address, *big.Int, common.Hash, common.Hash, evmtypes.Nonce, gas.EvmFee] diff --git a/core/chains/evm/txmgr/resender_test.go b/core/chains/evm/txmgr/resender_test.go index d2eefdece59..0e86c0d4f8c 100644 --- a/core/chains/evm/txmgr/resender_test.go +++ b/core/chains/evm/txmgr/resender_test.go @@ -65,7 +65,7 @@ func Test_EthResender_resendUnconfirmed(t *testing.T) { addr3TxesRawHex = append(addr3TxesRawHex, hexutil.Encode(etx.TxAttempts[0].SignedRawTx)) } - er := txmgr.NewEvmResender(lggr, txStore, txmgr.NewEvmTxmClient(ethClient), ethKeyStore, 100*time.Millisecond, ccfg.EVM(), ccfg.EVM().Transactions()) + er := txmgr.NewEvmResender(lggr, txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTracker(txStore, ethKeyStore, big.NewInt(0), lggr), ethKeyStore, 100*time.Millisecond, ccfg.EVM(), ccfg.EVM().Transactions()) var resentHex = make(map[string]struct{}) ethClient.On("BatchCallContextAll", mock.Anything, mock.MatchedBy(func(elems []rpc.BatchElem) bool { @@ -121,7 +121,7 @@ func Test_EthResender_alertUnconfirmed(t *testing.T) { txStore := cltest.NewTestTxStore(t, db, logCfg) originalBroadcastAt := time.Unix(1616509100, 0) - er := txmgr.NewEvmResender(lggr, txStore, txmgr.NewEvmTxmClient(ethClient), ethKeyStore, 100*time.Millisecond, ccfg.EVM(), ccfg.EVM().Transactions()) + er := txmgr.NewEvmResender(lggr, txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTracker(txStore, ethKeyStore, big.NewInt(0), lggr), ethKeyStore, 100*time.Millisecond, ccfg.EVM(), ccfg.EVM().Transactions()) t.Run("alerts only once for unconfirmed transaction attempt within the unconfirmedTxAlertDelay duration", func(t *testing.T) { _ = cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, int64(1), fromAddress, originalBroadcastAt) @@ -157,7 +157,7 @@ func Test_EthResender_Start(t *testing.T) { t.Run("resends transactions that have been languishing unconfirmed for too long", func(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) - er := txmgr.NewEvmResender(lggr, txStore, txmgr.NewEvmTxmClient(ethClient), ethKeyStore, 100*time.Millisecond, ccfg.EVM(), ccfg.EVM().Transactions()) + er := txmgr.NewEvmResender(lggr, txStore, txmgr.NewEvmTxmClient(ethClient), txmgr.NewEvmTracker(txStore, ethKeyStore, big.NewInt(0), lggr), ethKeyStore, 100*time.Millisecond, ccfg.EVM(), ccfg.EVM().Transactions()) originalBroadcastAt := time.Unix(1616509100, 0) etx := cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress, originalBroadcastAt) diff --git a/core/chains/evm/txmgr/tracker_test.go b/core/chains/evm/txmgr/tracker_test.go new file mode 100644 index 00000000000..a31187f04e8 --- /dev/null +++ b/core/chains/evm/txmgr/tracker_test.go @@ -0,0 +1,161 @@ +package txmgr_test + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" + "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/keystore" + "github.com/smartcontractkit/chainlink/v2/core/utils" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +const waitTime = 5 * time.Millisecond + +func newTestEvmTrackerSetup(t *testing.T) (*txmgr.Tracker, txmgr.TestEvmTxStore, keystore.Eth, []common.Address) { + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + chainID := big.NewInt(0) + enabledAddresses := generateEnabledAddresses(t, ethKeyStore, chainID) + lggr := logger.TestLogger(t) + return txmgr.NewEvmTracker(txStore, ethKeyStore, chainID, lggr), txStore, ethKeyStore, enabledAddresses +} + +func generateEnabledAddresses(t *testing.T, keyStore keystore.Eth, chainID *big.Int) []common.Address { + var enabledAddresses []common.Address + _, addr1 := cltest.MustInsertRandomKey(t, keyStore, *utils.NewBigI(chainID.Int64())) + _, addr2 := cltest.MustInsertRandomKey(t, keyStore, *utils.NewBigI(chainID.Int64())) + enabledAddresses = append(enabledAddresses, addr1, addr2) + return enabledAddresses +} + +func containsID(txes []*txmgr.Tx, id int64) bool { + for _, tx := range txes { + if tx.ID == id { + return true + } + } + return false +} + +func TestEvmTracker_Initialization(t *testing.T) { + t.Parallel() + + tracker, _, _, _ := newTestEvmTrackerSetup(t) + + err := tracker.Start(context.Background()) + require.NoError(t, err) + require.True(t, tracker.IsStarted()) + + t.Run("stop tracker", func(t *testing.T) { + err := tracker.Close() + require.NoError(t, err) + require.False(t, tracker.IsStarted()) + }) +} + +func TestEvmTracker_AddressTracking(t *testing.T) { + t.Parallel() + + t.Run("track abandoned addresses", func(t *testing.T) { + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + tracker, txStore, _, _ := newTestEvmTrackerSetup(t) + inProgressAddr := cltest.MustGenerateRandomKey(t).Address + unstartedAddr := cltest.MustGenerateRandomKey(t).Address + unconfirmedAddr := cltest.MustGenerateRandomKey(t).Address + confirmedAddr := cltest.MustGenerateRandomKey(t).Address + _ = mustInsertInProgressEthTxWithAttempt(t, txStore, 123, inProgressAddr) + _ = cltest.MustInsertUnconfirmedEthTx(t, txStore, 123, unconfirmedAddr) + _ = mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) + _ = mustCreateUnstartedTx(t, txStore, unstartedAddr, cltest.MustGenerateRandomKey(t).Address, []byte{}, 0, big.Int{}, ethClient.ConfiguredChainID()) + + err := tracker.Start(context.Background()) + require.NoError(t, err) + defer func(tracker *txmgr.Tracker) { + err = tracker.Close() + require.NoError(t, err) + }(tracker) + + addrs := tracker.GetAbandonedAddresses() + require.NotContains(t, addrs, inProgressAddr) + require.NotContains(t, addrs, unstartedAddr) + require.Contains(t, addrs, confirmedAddr) + require.Contains(t, addrs, unconfirmedAddr) + }) + + t.Run("stop tracking finalized tx", func(t *testing.T) { + tracker, txStore, _, _ := newTestEvmTrackerSetup(t) + confirmedAddr := cltest.MustGenerateRandomKey(t).Address + _ = mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) + + err := tracker.Start(context.Background()) + require.NoError(t, err) + defer func(tracker *txmgr.Tracker) { + err = tracker.Close() + require.NoError(t, err) + }(tracker) + + addrs := tracker.GetAbandonedAddresses() + require.Contains(t, addrs, confirmedAddr) + + // deliver block past minConfirmations to finalize tx + tracker.XXXDeliverBlock(10) + time.Sleep(waitTime) + + addrs = tracker.GetAbandonedAddresses() + require.NotContains(t, addrs, confirmedAddr) + }) +} + +func TestEvmTracker_ExceedingTTL(t *testing.T) { + t.Parallel() + + t.Run("confirmed but unfinalized transaction still tracked", func(t *testing.T) { + tracker, txStore, _, _ := newTestEvmTrackerSetup(t) + addr1 := cltest.MustGenerateRandomKey(t).Address + _ = mustInsertConfirmedEthTxWithReceipt(t, txStore, addr1, 123, 1) + + err := tracker.Start(context.Background()) + require.NoError(t, err) + defer func(tracker *txmgr.Tracker) { + err = tracker.Close() + require.NoError(t, err) + }(tracker) + + require.Contains(t, tracker.GetAbandonedAddresses(), addr1) + }) + + t.Run("exceeding ttl", func(t *testing.T) { + tracker, txStore, _, _ := newTestEvmTrackerSetup(t) + addr1 := cltest.MustGenerateRandomKey(t).Address + addr2 := cltest.MustGenerateRandomKey(t).Address + tx1 := mustInsertInProgressEthTxWithAttempt(t, txStore, 123, addr1) + tx2 := cltest.MustInsertUnconfirmedEthTx(t, txStore, 123, addr2) + + tracker.XXXTestSetTTL(time.Nanosecond) + err := tracker.Start(context.Background()) + require.NoError(t, err) + defer func(tracker *txmgr.Tracker) { + err = tracker.Close() + require.NoError(t, err) + }(tracker) + + time.Sleep(waitTime) + require.NotContains(t, tracker.GetAbandonedAddresses(), addr1, addr2) + + fatalTxes, err := txStore.GetFatalTransactions(context.Background()) + require.NoError(t, err) + require.True(t, containsID(fatalTxes, tx1.ID)) + require.True(t, containsID(fatalTxes, tx2.ID)) + }) +} diff --git a/core/internal/cltest/factories.go b/core/internal/cltest/factories.go index 46014c4e04f..f0ce8c4ff66 100644 --- a/core/internal/cltest/factories.go +++ b/core/internal/cltest/factories.go @@ -193,6 +193,7 @@ func MustInsertConfirmedEthTxWithLegacyAttempt(t *testing.T, txStore txmgr.TestE n := evmtypes.Nonce(nonce) etx.Sequence = &n etx.State = txmgrcommon.TxConfirmed + etx.MinConfirmations.SetValid(6) require.NoError(t, txStore.InsertTx(&etx)) attempt := NewLegacyEthTxAttempt(t, etx.ID) attempt.BroadcastBeforeBlockNum = &broadcastBeforeBlockNum diff --git a/core/scripts/common/vrf/model/model.go b/core/scripts/common/vrf/model/model.go index 42deb424536..0972c47e618 100644 --- a/core/scripts/common/vrf/model/model.go +++ b/core/scripts/common/vrf/model/model.go @@ -1,8 +1,9 @@ package model import ( - "github.com/ethereum/go-ethereum/common" "math/big" + + "github.com/ethereum/go-ethereum/common" ) var ( diff --git a/core/services/vrf/v2/integration_v2_test.go b/core/services/vrf/v2/integration_v2_test.go index 74d923ce09f..fa95b694f98 100644 --- a/core/services/vrf/v2/integration_v2_test.go +++ b/core/services/vrf/v2/integration_v2_test.go @@ -137,7 +137,7 @@ func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.M _, _, evmConfig := txmgr.MakeTestConfigs(t) txmConfig := txmgr.NewEvmTxmConfig(evmConfig) txm := txmgr.NewEvmTxm(ec.ConfiguredChainID(), txmConfig, evmConfig.Transactions(), keyStore.Eth(), logger.TestLogger(t), nil, nil, - nil, txStore, nil, nil, nil, nil) + nil, txStore, nil, nil, nil, nil, nil) return txm } diff --git a/core/services/vrf/v2/listener_v2_test.go b/core/services/vrf/v2/listener_v2_test.go index bcc85b3700d..6192db95dfe 100644 --- a/core/services/vrf/v2/listener_v2_test.go +++ b/core/services/vrf/v2/listener_v2_test.go @@ -39,7 +39,7 @@ func makeTestTxm(t *testing.T, txStore txmgr.TestEvmTxStore, keyStore keystore.M ec := evmtest.NewEthClientMockWithDefaultChain(t) txmConfig := txmgr.NewEvmTxmConfig(evmConfig) txm := txmgr.NewEvmTxm(ec.ConfiguredChainID(), txmConfig, evmConfig.Transactions(), keyStore.Eth(), logger.TestLogger(t), nil, nil, - nil, txStore, nil, nil, nil, nil) + nil, txStore, nil, nil, nil, nil, nil) return txm } diff --git a/core/store/migrate/migrations/0210_remove_evm_key_states_fk_constraint.sql b/core/store/migrate/migrations/0210_remove_evm_key_states_fk_constraint.sql new file mode 100644 index 00000000000..119de9d260e --- /dev/null +++ b/core/store/migrate/migrations/0210_remove_evm_key_states_fk_constraint.sql @@ -0,0 +1,4 @@ +-- +goose Up +ALTER TABLE evm.txes DROP CONSTRAINT eth_txes_evm_chain_id_from_address_fkey; +-- +goose Down +ALTER TABLE evm.txes ADD CONSTRAINT eth_txes_evm_chain_id_from_address_fkey FOREIGN KEY (evm_chain_id, from_address) REFERENCES evm.key_states(evm_chain_id, address) ON DELETE CASCADE DEFERRABLE INITIALLY IMMEDIATE NOT VALID; \ No newline at end of file diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c5f243626d2..e8013d3617a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added a tracker component to the txmgr for tracking and gracefully handling abandoned transactions. Abandoned transactions occur when a fromAddress is removed from the keystore by a node operator. The tracker gives abandoned transactions a chance to be finalized on chain, or marks them as fatal_error if they are not finalized within a specified time to live (default 6hrs). - Added distributed tracing in the OpenTelemetry trace format to the node, currently focused at the LOOPP Plugin development effort. This includes a new set of `Tracing` TOML configurations. The default for collecting traces is off - you must explicitly enable traces and setup a valid OpenTelemetry collector. Refer to `.github/tracing/README.md` for more details. - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. - New prom metrics for mercury transmit queue: From 2ed60e4f0a057779ef3e23002cbe4bbb76da88d6 Mon Sep 17 00:00:00 2001 From: Domino Valdano <2644901+reductionista@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:01:55 -0800 Subject: [PATCH 217/271] Change UseFinalityTags to FinalityTagEnabled in CHANGELOG.md to avoid confusion (#11404) The name of the param LogPoller accepts is useFinalityTag, but the externally-facing name of the toml config param it's set based on is FinalityTagEnabled. There is also a toml config param named UseFinalityTag but it's only used for integration tests, there is no such thing in the main production toml config. Also, adding EVM prefix for clarity. --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e8013d3617a..e282d047892 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -93,7 +93,7 @@ ServerPubKey = '...' These will eventually replace `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey`. Setting `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` alongside `[[TelemetryIngress.Endpoints]]` will prevent the node from booting. Only one way of configuring telemetry endpoints is supported. - Added bridge_name label to `pipeline_tasks_total_finished` prometheus metric. This should make it easier to see directly what bridge was failing out from the CL NODE perspective. -- LogPoller will now use finality tags to dynamically determine finality on evm chains if `UseFinalityTags=true`, rather than the fixed `FinalityDepth` specified in toml config +- LogPoller will now use finality tags to dynamically determine finality on evm chains if `EVM.FinalityTagEnabled=true`, rather than the fixed `EVM.FinalityDepth` specified in toml config ### Changed From de6c45e89fc63dcda3e3cfc67566aa5fd3bd1f5c Mon Sep 17 00:00:00 2001 From: Rens Rooimans Date: Wed, 29 Nov 2023 12:57:35 +0100 Subject: [PATCH 218/271] Add callWithExactGas lib (#11009) * add callWithExactGas lib * add fuzz test * dup code to make it more safe * add _callWithExactGasEvenIfTargetIsNoContract * make args order consistent * add callWithExactGasEvenIfTargetIsNoContract tests * improve tests * update snapshot * properly name fuzz test * fix comments * improve testing logic * return gas used in _callWithExactGasSafeReturnData (#11106) * fix function selector blacklist in fuzz test * Update shared.gas-snapshot * use constant revert reasons * fix solhint * fix ref * style nits and let all return success * fix solhint --- contracts/GNUmakefile | 5 + contracts/gas-snapshots/shared.gas-snapshot | 16 + contracts/package.json | 2 +- .../src/v0.8/shared/call/CallWithExactGas.sol | 162 +++++++ .../shared/test/call/CallWithExactGas.t.sol | 459 ++++++++++++++++++ .../test/call/CallWithExactGasHelper.sol | 35 ++ .../test/testhelpers/GenericReceiver.sol | 29 ++ 7 files changed, 707 insertions(+), 1 deletion(-) create mode 100644 contracts/src/v0.8/shared/call/CallWithExactGas.sol create mode 100644 contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol create mode 100644 contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol create mode 100644 contracts/src/v0.8/shared/test/testhelpers/GenericReceiver.sol diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile index e8808138673..f5be193249c 100644 --- a/contracts/GNUmakefile +++ b/contracts/GNUmakefile @@ -18,6 +18,11 @@ ALL_FOUNDRY_PRODUCTS = llo-feeds functions shared snapshot: ## Make a snapshot for a specific product. export FOUNDRY_PROFILE=$(FOUNDRY_PROFILE) && forge snapshot --nmt "testFuzz_\w{1,}?" --snap gas-snapshots/$(FOUNDRY_PROFILE).gas-snapshot +.PHONY: snapshot-diff +snapshot-diff: ## Make a snapshot for a specific product. + export FOUNDRY_PROFILE=$(FOUNDRY_PROFILE) && forge snapshot --nmt "testFuzz_\w{1,}?" --diff gas-snapshots/$(FOUNDRY_PROFILE).gas-snapshot + + .PHONY: snapshot-all snapshot-all: ## Make a snapshot for all products. for foundry_profile in $(ALL_FOUNDRY_PRODUCTS) ; do \ diff --git a/contracts/gas-snapshots/shared.gas-snapshot b/contracts/gas-snapshots/shared.gas-snapshot index cf003c5a26d..6f307d257f5 100644 --- a/contracts/gas-snapshots/shared.gas-snapshot +++ b/contracts/gas-snapshots/shared.gas-snapshot @@ -25,6 +25,22 @@ BurnMintERC677_mint:testSenderNotMinterReverts() (gas: 11195) BurnMintERC677_supportsInterface:testConstructorSuccess() (gas: 8685) BurnMintERC677_transfer:testInvalidAddressReverts() (gas: 10639) BurnMintERC677_transfer:testTransferSuccess() (gas: 39462) +CallWithExactGas__callWithExactGas:test_CallWithExactGasReceiverErrorSuccess() (gas: 66918) +CallWithExactGas__callWithExactGas:test_CallWithExactGasSafeReturnDataExactGas() (gas: 22615) +CallWithExactGas__callWithExactGas:test_NoContractReverts() (gas: 11559) +CallWithExactGas__callWithExactGas:test_NoGasForCallExactCheckReverts() (gas: 12908) +CallWithExactGas__callWithExactGas:test_NotEnoughGasForCallReverts() (gas: 13361) +CallWithExactGas__callWithExactGas:test_callWithExactGasSuccess(bytes,bytes4) (runs: 256, μ: 15477, ~: 15418) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractExactGasSuccess() (gas: 19147) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractReceiverErrorSuccess() (gas: 67096) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes,bytes4) (runs: 256, μ: 15675, ~: 15616) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoContractSuccess() (gas: 9816) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NoGasForCallExactCheckReturnFalseSuccess() (gas: 9578) +CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract:test_NotEnoughGasForCallReturnsFalseSuccess() (gas: 9890) +CallWithExactGas__callWithExactGasSafeReturnData:test_CallWithExactGasSafeReturnDataExactGas() (gas: 19017) +CallWithExactGas__callWithExactGasSafeReturnData:test_NoContractReverts() (gas: 13949) +CallWithExactGas__callWithExactGasSafeReturnData:test_NoGasForCallExactCheckReverts() (gas: 13239) +CallWithExactGas__callWithExactGasSafeReturnData:test_NotEnoughGasForCallReverts() (gas: 13670) OpStackBurnMintERC677_constructor:testConstructorSuccess() (gas: 1739317) OpStackBurnMintERC677_interfaceCompatibility:testBurnCompatibility() (gas: 263373) OpStackBurnMintERC677_interfaceCompatibility:testMintCompatibility() (gas: 137957) diff --git a/contracts/package.json b/contracts/package.json index 2ceb1602dca..1503c822ea4 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -18,7 +18,7 @@ "prepublishOnly": "pnpm compile && ./scripts/prepublish_generate_abi_folder", "publish-beta": "pnpm publish --tag beta", "publish-prod": "npm dist-tag add @chainlink/contracts@0.8.0 latest", - "solhint": "solhint --max-warnings 369 \"./src/v0.8/**/*.sol\"" + "solhint": "solhint --max-warnings 376 \"./src/v0.8/**/*.sol\"" }, "files": [ "src/v0.8", diff --git a/contracts/src/v0.8/shared/call/CallWithExactGas.sol b/contracts/src/v0.8/shared/call/CallWithExactGas.sol new file mode 100644 index 00000000000..6716dc15953 --- /dev/null +++ b/contracts/src/v0.8/shared/call/CallWithExactGas.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/// @notice This library contains various callWithExactGas functions. All of them are +/// safe from gas bomb attacks. +/// @dev There is code duplication in this library. This is done to not leave the assembly +/// the blocks. +library CallWithExactGas { + error NoContract(); + error NoGasForCallExactCheck(); + error NotEnoughGasForCall(); + + bytes4 internal constant NO_CONTRACT_SIG = 0x0c3b563c; + bytes4 internal constant NO_GAS_FOR_CALL_EXACT_CHECK_SIG = 0xafa32a2c; + bytes4 internal constant NOT_ENOUGH_GAS_FOR_CALL_SIG = 0x37c3be29; + + /// @notice calls target address with exactly gasAmount gas and payload as calldata. + /// Accounts for gasForCallExactCheck gas that will be used by this function. Will revert + /// if the target is not a contact. Will revert when there is not enough gas to call the + /// target with gasAmount gas. + /// @dev Ignores the return data, which makes it immune to gas bomb attacks. + /// @return success whether the call succeeded + function _callWithExactGas( + bytes memory payload, + address target, + uint256 gasLimit, + uint16 gasForCallExactCheck + ) internal returns (bool success) { + assembly { + // solidity calls check that a contract actually exists at the destination, so we do the same + // Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion") + // doesn't need to account for it. + if iszero(extcodesize(target)) { + mstore(0x0, NO_CONTRACT_SIG) + revert(0x0, 0x4) + } + + let g := gas() + // Compute g -= gasForCallExactCheck and check for underflow + // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. gasForCallExactCheck ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, gasForCallExactCheck) { + mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG) + revert(0x0, 0x4) + } + g := sub(g, gasForCallExactCheck) + // if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150 + if iszero(gt(sub(g, div(g, 64)), gasLimit)) { + mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG) + revert(0x0, 0x4) + } + + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0) + } + return success; + } + + /// @notice calls target address with exactly gasAmount gas and payload as calldata. + /// Account for gasForCallExactCheck gas that will be used by this function. Will revert + /// if the target is not a contact. Will revert when there is not enough gas to call the + /// target with gasAmount gas. + /// @dev Caps the return data length, which makes it immune to gas bomb attacks. + /// @dev Return data cap logic borrowed from + /// https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol. + /// @return success whether the call succeeded + /// @return retData the return data from the call, capped at maxReturnBytes bytes + /// @return gasUsed the gas used by the external call. Does not include the overhead of this function. + function _callWithExactGasSafeReturnData( + bytes memory payload, + address target, + uint256 gasLimit, + uint16 gasForCallExactCheck, + uint16 maxReturnBytes + ) internal returns (bool success, bytes memory retData, uint256 gasUsed) { + // allocate retData memory ahead of time + retData = new bytes(maxReturnBytes); + + assembly { + // solidity calls check that a contract actually exists at the destination, so we do the same + // Note we do this check prior to measuring gas so gasForCallExactCheck (our "cushion") + // doesn't need to account for it. + if iszero(extcodesize(target)) { + mstore(0x0, NO_CONTRACT_SIG) + revert(0x0, 0x4) + } + + let g := gas() + // Compute g -= gasForCallExactCheck and check for underflow + // The gas actually passed to the callee is _min(gasAmount, 63//64*gas available). + // We want to ensure that we revert if gasAmount > 63//64*gas available + // as we do not want to provide them with less, however that check itself costs + // gas. gasForCallExactCheck ensures we have at least enough gas to be able + // to revert if gasAmount > 63//64*gas available. + if lt(g, gasForCallExactCheck) { + mstore(0x0, NO_GAS_FOR_CALL_EXACT_CHECK_SIG) + revert(0x0, 0x4) + } + g := sub(g, gasForCallExactCheck) + // if g - g//64 <= gasAmount, revert. We subtract g//64 because of EIP-150 + if iszero(gt(sub(g, div(g, 64)), gasLimit)) { + mstore(0x0, NOT_ENOUGH_GAS_FOR_CALL_SIG) + revert(0x0, 0x4) + } + + // We save the gas before the call so we can calculate how much gas the call used + let gasBeforeCall := gas() + // call and return whether we succeeded. ignore return data + // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) + success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0) + gasUsed := sub(gasBeforeCall, gas()) + + // limit our copy to maxReturnBytes bytes + let toCopy := returndatasize() + if gt(toCopy, maxReturnBytes) { + toCopy := maxReturnBytes + } + // Store the length of the copied bytes + mstore(retData, toCopy) + // copy the bytes from retData[0:_toCopy] + returndatacopy(add(retData, 0x20), 0x0, toCopy) + } + return (success, retData, gasUsed); + } + + /// @notice Calls target address with exactly gasAmount gas and payload as calldata + /// or reverts if at least gasLimit gas is not available. + /// @dev Does not check if target is a contract. If it is not a contract, the low-level + /// call will still be made and it will succeed. + /// @dev Ignores the return data, which makes it immune to gas bomb attacks. + /// @return success whether the call succeeded + /// @return sufficientGas Whether there was enough gas to make the call + function _callWithExactGasEvenIfTargetIsNoContract( + bytes memory payload, + address target, + uint256 gasLimit, + uint16 gasForCallExactCheck + ) internal returns (bool success, bool sufficientGas) { + assembly { + let g := gas() + // Compute g -= CALL_WITH_EXACT_GAS_CUSHION and check for underflow. We + // need the cushion since the logic following the above call to gas also + // costs gas which we cannot account for exactly. So cushion is a + // conservative upper bound for the cost of this logic. + if iszero(lt(g, gasForCallExactCheck)) { + g := sub(g, gasForCallExactCheck) + // If g - g//64 <= gasAmount, we don't have enough gas. We subtract g//64 because of EIP-150. + if gt(sub(g, div(g, 64)), gasLimit) { + // Call and ignore success/return data. Note that we did not check + // whether a contract actually exists at the target address. + success := call(gasLimit, target, 0, add(payload, 0x20), mload(payload), 0x0, 0x0) + sufficientGas := true + } + } + } + return (success, sufficientGas); + } +} diff --git a/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol b/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol new file mode 100644 index 00000000000..e5c90b172cd --- /dev/null +++ b/contracts/src/v0.8/shared/test/call/CallWithExactGas.t.sol @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {CallWithExactGas} from "../../call/CallWithExactGas.sol"; +import {CallWithExactGasHelper} from "./CallWithExactGasHelper.sol"; +import {BaseTest} from "../BaseTest.t.sol"; +import {GenericReceiver} from "../testhelpers/GenericReceiver.sol"; + +contract CallWithExactGasSetup is BaseTest { + GenericReceiver internal s_receiver; + CallWithExactGasHelper internal s_caller; + uint256 internal constant DEFAULT_GAS_LIMIT = 20_000; + uint16 internal constant DEFAULT_GAS_FOR_CALL_EXACT_CHECK = 5000; + uint256 internal constant EXTCODESIZE_GAS_COST = 2600; + + function setUp() public virtual override { + BaseTest.setUp(); + + s_receiver = new GenericReceiver(false); + s_caller = new CallWithExactGasHelper(); + } +} + +contract CallWithExactGas__callWithExactGas is CallWithExactGasSetup { + function test_callWithExactGasSuccess(bytes memory payload, bytes4 funcSelector) public { + vm.pauseGasMetering(); + + bytes memory data = abi.encodeWithSelector(funcSelector, payload); + vm.assume( + funcSelector != GenericReceiver.setRevert.selector && + funcSelector != GenericReceiver.setErr.selector && + funcSelector != 0x5100fc21 // s_toRevert(), which is public and therefore has a function selector + ); + + vm.expectCall(address(s_receiver), data); + vm.resumeGasMetering(); + + bool success = s_caller.callWithExactGas( + data, + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + assertTrue(success); + } + + function test_CallWithExactGasSafeReturnDataExactGas() public { + // The calculated overhead for otherwise unaccounted for gas usage + uint256 overheadForCallWithExactGas = 364; + + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGas.selector, + "", + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + // Since only 63/64th of the gas gets passed, we compensate + uint256 allowedGas = (DEFAULT_GAS_LIMIT + (DEFAULT_GAS_LIMIT / 64)); + + allowedGas += EXTCODESIZE_GAS_COST + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + overheadForCallWithExactGas; + + // Due to EIP-150 we expect to lose 1/64, so we compensate for this + allowedGas = (allowedGas * 64) / 63; + + (bool success, bytes memory retData) = address(s_caller).call{gas: allowedGas}(payload); + + assertTrue(success); + assertEq(abi.encode(true), retData); + } + + function test_CallWithExactGasReceiverErrorSuccess() public { + bytes memory data = abi.encode("0x52656E73"); + + bytes memory errorData = new bytes(20); + for (uint256 i = 0; i < errorData.length; ++i) { + errorData[i] = 0x01; + } + s_receiver.setErr(errorData); + s_receiver.setRevert(true); + + vm.expectCall(address(s_receiver), data); + + bool success = s_caller.callWithExactGas( + data, + address(s_receiver), + DEFAULT_GAS_LIMIT * 10, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + assertFalse(success); + } + + function test_NoContractReverts() public { + address addressWithoutContract = address(1337); + + vm.expectRevert(CallWithExactGas.NoContract.selector); + + s_caller.callWithExactGas( + "", // empty payload as it will revert well before needing it + addressWithoutContract, + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + } + + function test_NoGasForCallExactCheckReverts() public { + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGas.selector, + "", // empty payload as it will revert well before needing it + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + (bool success, bytes memory retData) = address(s_caller).call{gas: DEFAULT_GAS_FOR_CALL_EXACT_CHECK - 1}(payload); + assertFalse(success); + assertEq(retData.length, CallWithExactGas.NoGasForCallExactCheck.selector.length); + assertEq(abi.encodeWithSelector(CallWithExactGas.NoGasForCallExactCheck.selector), retData); + } + + function test_NotEnoughGasForCallReverts() public { + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGas.selector, + "", // empty payload as it will revert well before needing it + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + // Supply enough gas for the final call, the DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + // the extcodesize and account for EIP-150. This doesn't account for any other gas + // usage, and will therefore fail because the checks and memory stored/loads + // also cost gas. + uint256 allowedGas = (DEFAULT_GAS_LIMIT + (DEFAULT_GAS_LIMIT / 64)) + DEFAULT_GAS_FOR_CALL_EXACT_CHECK; + // extcodesize gas cost + allowedGas += EXTCODESIZE_GAS_COST; + // EIP-150 + allowedGas = (allowedGas * 64) / 63; + + // Expect this call to fail due to not having enough gas for the final call + (bool success, bytes memory retData) = address(s_caller).call{gas: allowedGas}(payload); + + assertFalse(success); + assertEq(retData.length, CallWithExactGas.NotEnoughGasForCall.selector.length); + assertEq(abi.encodeWithSelector(CallWithExactGas.NotEnoughGasForCall.selector), retData); + } +} + +contract CallWithExactGas__callWithExactGasSafeReturnData is CallWithExactGasSetup { + function testFuzz_CallWithExactGasSafeReturnDataSuccess(bytes memory payload, bytes4 funcSelector) public { + vm.pauseGasMetering(); + bytes memory data = abi.encodeWithSelector(funcSelector, payload); + vm.assume( + funcSelector != GenericReceiver.setRevert.selector && + funcSelector != GenericReceiver.setErr.selector && + funcSelector != 0x5100fc21 // s_toRevert(), which is public and therefore has a function selector + ); + uint16 maxRetBytes = 0; + + vm.expectCall(address(s_receiver), data); + vm.resumeGasMetering(); + + (bool success, bytes memory retData, uint256 gasUsed) = s_caller.callWithExactGasSafeReturnData( + data, + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + maxRetBytes + ); + + assertTrue(success); + assertEq(retData.length, 0); + assertGt(gasUsed, 500); + } + + function test_CallWithExactGasSafeReturnDataExactGas() public { + // The gas cost for `extcodesize` + uint256 extcodesizeGas = EXTCODESIZE_GAS_COST; + // The calculated overhead for retData initialization + uint256 overheadForRetDataInit = 114; + // The calculated overhead for otherwise unaccounted for gas usage + uint256 overheadForCallWithExactGas = 486; + + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGasSafeReturnData.selector, + "", + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + 0 + ); + + // Since only 63/64th of the gas gets passed, we compensate + uint256 allowedGas = (DEFAULT_GAS_LIMIT + (DEFAULT_GAS_LIMIT / 64)); + + allowedGas += + extcodesizeGas + + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + + overheadForRetDataInit + + overheadForCallWithExactGas; + + // Due to EIP-150 we expect to lose 1/64, so we compensate for this + allowedGas = (allowedGas * 64) / 63; + + vm.expectCall(address(s_receiver), ""); + (bool success, bytes memory retData) = address(s_caller).call{gas: allowedGas}(payload); + + assertTrue(success); + (bool innerSuccess, bytes memory innerRetData, uint256 gasUsed) = abi.decode(retData, (bool, bytes, uint256)); + + assertTrue(innerSuccess); + assertEq(innerRetData.length, 0); + assertGt(gasUsed, 500); + } + + function testFuzz_CallWithExactGasReceiverErrorSuccess(uint16 testRetBytes) public { + uint16 maxReturnBytes = 500; + // Bound with upper limit, otherwise the test runs out of gas. + testRetBytes = uint16(bound(testRetBytes, 0, maxReturnBytes * 10)); + + bytes memory data = abi.encode("0x52656E73"); + + bytes memory errorData = new bytes(testRetBytes); + for (uint256 i = 0; i < errorData.length; ++i) { + errorData[i] = 0x01; + } + s_receiver.setErr(errorData); + s_receiver.setRevert(true); + + vm.expectCall(address(s_receiver), data); + + (bool success, bytes memory retData, uint256 gasUsed) = s_caller.callWithExactGasSafeReturnData( + data, + address(s_receiver), + DEFAULT_GAS_LIMIT * 10, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + maxReturnBytes + ); + + assertFalse(success); + + bytes memory expectedReturnData = errorData; + + // If expected return data is longer than MAX_RET_BYTES, truncate it to MAX_RET_BYTES + if (expectedReturnData.length > maxReturnBytes) { + expectedReturnData = new bytes(maxReturnBytes); + for (uint256 i = 0; i < maxReturnBytes; ++i) { + expectedReturnData[i] = errorData[i]; + } + } + assertEq(expectedReturnData, retData); + assertGt(gasUsed, 500); + } + + function test_NoContractReverts() public { + address addressWithoutContract = address(1337); + + vm.expectRevert(CallWithExactGas.NoContract.selector); + + s_caller.callWithExactGasSafeReturnData( + "", // empty payload as it will revert well before needing it + addressWithoutContract, + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + 0 + ); + } + + function test_NoGasForCallExactCheckReverts() public { + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGasSafeReturnData.selector, + "", // empty payload as it will revert well before needing it + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + 0 + ); + + (bool success, bytes memory retData) = address(s_caller).call{gas: DEFAULT_GAS_FOR_CALL_EXACT_CHECK - 1}(payload); + assertFalse(success); + assertEq(retData.length, CallWithExactGas.NoGasForCallExactCheck.selector.length); + assertEq(abi.encodeWithSelector(CallWithExactGas.NoGasForCallExactCheck.selector), retData); + } + + function test_NotEnoughGasForCallReverts() public { + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGasSafeReturnData.selector, + "", // empty payload as it will revert well before needing it + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + 0 + ); + + // Supply enough gas for the final call, the DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + // the extcodesize and account for EIP-150. This doesn't account for any other gas + // usage, and will therefore fail because the checks and memory stored/loads + // also cost gas. + uint256 allowedGas = (DEFAULT_GAS_LIMIT + (DEFAULT_GAS_LIMIT / 64)) + DEFAULT_GAS_FOR_CALL_EXACT_CHECK; + // extcodesize gas cost + allowedGas += EXTCODESIZE_GAS_COST; + // EIP-150 + allowedGas = (allowedGas * 64) / 63; + + // Expect this call to fail due to not having enough gas for the final call + (bool success, bytes memory retData) = address(s_caller).call{gas: allowedGas}(payload); + + assertFalse(success); + assertEq(retData.length, CallWithExactGas.NotEnoughGasForCall.selector.length); + assertEq(abi.encodeWithSelector(CallWithExactGas.NotEnoughGasForCall.selector), retData); + } +} + +contract CallWithExactGas__callWithExactGasEvenIfTargetIsNoContract is CallWithExactGasSetup { + function test_CallWithExactGasEvenIfTargetIsNoContractSuccess(bytes memory payload, bytes4 funcSelector) public { + vm.pauseGasMetering(); + bytes memory data = abi.encodeWithSelector(funcSelector, payload); + vm.assume( + funcSelector != GenericReceiver.setRevert.selector && + funcSelector != GenericReceiver.setErr.selector && + funcSelector != 0x5100fc21 // s_toRevert(), which is public and therefore has a function selector + ); + vm.expectCall(address(s_receiver), data); + vm.resumeGasMetering(); + + (bool success, bool sufficientGas) = s_caller.callWithExactGasEvenIfTargetIsNoContract( + data, + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + assertTrue(success); + assertTrue(sufficientGas); + } + + function test_CallWithExactGasEvenIfTargetIsNoContractExactGasSuccess() public { + // The calculated overhead for otherwise unaccounted for gas usage + uint256 overheadForCallWithExactGas = 446; + + bytes memory data = abi.encode("0x52656E73"); + + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGasEvenIfTargetIsNoContract.selector, + data, + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + // Since only 63/64th of the gas gets passed, we compensate + uint256 allowedGas = (DEFAULT_GAS_LIMIT + (DEFAULT_GAS_LIMIT / 64)); + + allowedGas += DEFAULT_GAS_FOR_CALL_EXACT_CHECK + overheadForCallWithExactGas; + + // Due to EIP-150 we expect to lose 1/64, so we compensate for this + allowedGas = (allowedGas * 64) / 63; + + vm.expectCall(address(s_receiver), data); + (bool outerCallSuccess, bytes memory SuccessAndSufficientGas) = address(s_caller).call{gas: allowedGas}(payload); + + // The call succeeds + assertTrue(outerCallSuccess); + + (bool success, bool sufficientGas) = abi.decode(SuccessAndSufficientGas, (bool, bool)); + assertTrue(success); + assertTrue(sufficientGas); + } + + function test_CallWithExactGasEvenIfTargetIsNoContractReceiverErrorSuccess() public { + bytes memory data = abi.encode("0x52656E73"); + + bytes memory errorData = new bytes(20); + for (uint256 i = 0; i < errorData.length; ++i) { + errorData[i] = 0x01; + } + s_receiver.setErr(errorData); + s_receiver.setRevert(true); + + vm.expectCall(address(s_receiver), data); + + (bool success, bool sufficientGas) = s_caller.callWithExactGasEvenIfTargetIsNoContract( + data, + address(s_receiver), + DEFAULT_GAS_LIMIT * 10, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + // We don't care if it reverts, we only care if we have enough gas + assertFalse(success); + assertTrue(sufficientGas); + } + + function test_NoContractSuccess() public { + bytes memory data = abi.encode("0x52656E73"); + address addressWithoutContract = address(1337); + + (bool success, bool sufficientGas) = s_caller.callWithExactGasEvenIfTargetIsNoContract( + data, + addressWithoutContract, + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + assertTrue(success); + assertTrue(sufficientGas); + } + + function test_NoGasForCallExactCheckReturnFalseSuccess() public { + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGasEvenIfTargetIsNoContract.selector, + "", // empty payload as it will revert well before needing it + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + (bool outerCallSuccess, bytes memory SuccessAndSufficientGas) = address(s_caller).call{ + gas: DEFAULT_GAS_FOR_CALL_EXACT_CHECK - 1 + }(payload); + + // The call succeeds + assertTrue(outerCallSuccess); + + (bool success, bool sufficientGas) = abi.decode(SuccessAndSufficientGas, (bool, bool)); + assertFalse(success); + assertFalse(sufficientGas); + } + + function test_NotEnoughGasForCallReturnsFalseSuccess() public { + bytes memory payload = abi.encodeWithSelector( + s_caller.callWithExactGasEvenIfTargetIsNoContract.selector, + "", // empty payload as it will revert well before needing it + address(s_receiver), + DEFAULT_GAS_LIMIT, + DEFAULT_GAS_FOR_CALL_EXACT_CHECK + ); + + // Supply enough gas for the final call, the DEFAULT_GAS_FOR_CALL_EXACT_CHECK, + // and account for EIP-150. This doesn't account for any other gas usage, and + // will therefore fail because the checks and memory stored/loads also cost gas. + uint256 allowedGas = (DEFAULT_GAS_LIMIT + (DEFAULT_GAS_LIMIT / 64)) + DEFAULT_GAS_FOR_CALL_EXACT_CHECK; + // EIP-150 + allowedGas = (allowedGas * 64) / 63; + + // Expect this call to fail due to not having enough gas for the final call + (bool outerCallSuccess, bytes memory SuccessAndSufficientGas) = address(s_caller).call{gas: allowedGas}(payload); + + // The call succeeds + assertTrue(outerCallSuccess); + + (bool success, bool sufficientGas) = abi.decode(SuccessAndSufficientGas, (bool, bool)); + assertFalse(success); + assertFalse(sufficientGas); + } +} diff --git a/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol b/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol new file mode 100644 index 00000000000..932315639b9 --- /dev/null +++ b/contracts/src/v0.8/shared/test/call/CallWithExactGasHelper.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {CallWithExactGas} from "../../call/CallWithExactGas.sol"; + +contract CallWithExactGasHelper { + function callWithExactGas( + bytes memory payload, + address target, + uint256 gasLimit, + uint16 gasForCallExactCheck + ) public returns (bool success) { + return CallWithExactGas._callWithExactGas(payload, target, gasLimit, gasForCallExactCheck); + } + + function callWithExactGasSafeReturnData( + bytes memory payload, + address target, + uint256 gasLimit, + uint16 gasForCallExactCheck, + uint16 maxReturnBytes + ) public returns (bool success, bytes memory retData, uint256 gasUsed) { + return + CallWithExactGas._callWithExactGasSafeReturnData(payload, target, gasLimit, gasForCallExactCheck, maxReturnBytes); + } + + function callWithExactGasEvenIfTargetIsNoContract( + bytes memory payload, + address target, + uint256 gasLimit, + uint16 gasForCallExactCheck + ) public returns (bool success, bool sufficientGas) { + return CallWithExactGas._callWithExactGasEvenIfTargetIsNoContract(payload, target, gasLimit, gasForCallExactCheck); + } +} diff --git a/contracts/src/v0.8/shared/test/testhelpers/GenericReceiver.sol b/contracts/src/v0.8/shared/test/testhelpers/GenericReceiver.sol new file mode 100644 index 00000000000..2c058012dfd --- /dev/null +++ b/contracts/src/v0.8/shared/test/testhelpers/GenericReceiver.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract GenericReceiver { + bool public s_toRevert; + bytes private s_err; + + constructor(bool toRevert) { + s_toRevert = toRevert; + } + + function setRevert(bool toRevert) external { + s_toRevert = toRevert; + } + + function setErr(bytes memory err) external { + s_err = err; + } + + // solhint-disable-next-line payable-fallback + fallback() external { + if (s_toRevert) { + bytes memory reason = s_err; + assembly { + revert(add(32, reason), mload(reason)) + } + } + } +} From 99071cd7d8a8b2f13c31afb933af7e3a5615775f Mon Sep 17 00:00:00 2001 From: Mateusz Sekara Date: Wed, 29 Nov 2023 15:10:39 +0100 Subject: [PATCH 219/271] CCIP-1336 LogPoller - Bunch of minor improvements (#11348) * Tracking number of logs inserted by LP * Adding read query to LP * Index for fourth word in data * Regenerating mocks * Filter by address in SelectIndexedLogsByTxHash * Fix * Post review fixes * Post merge fixes --- core/chains/evm/logpoller/disabled.go | 6 +- core/chains/evm/logpoller/log_poller.go | 20 ++- core/chains/evm/logpoller/mocks/log_poller.go | 51 +++++-- core/chains/evm/logpoller/observability.go | 91 +++++++++--- .../evm/logpoller/observability_test.go | 72 ++++++++-- core/chains/evm/logpoller/orm.go | 35 ++++- core/chains/evm/logpoller/orm_test.go | 132 +++++++++++++++++- core/chains/evm/logpoller/query.go | 12 ++ .../0211_log_poller_word_indexes.sql | 6 + 9 files changed, 373 insertions(+), 52 deletions(-) create mode 100644 core/store/migrate/migrations/0211_log_poller_word_indexes.sql diff --git a/core/chains/evm/logpoller/disabled.go b/core/chains/evm/logpoller/disabled.go index b54d4e6fc84..05d591042f4 100644 --- a/core/chains/evm/logpoller/disabled.go +++ b/core/chains/evm/logpoller/disabled.go @@ -71,7 +71,7 @@ func (disabled) IndexedLogsByBlockRange(start, end int64, eventSig common.Hash, return nil, ErrDisabled } -func (d disabled) IndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { +func (d disabled) IndexedLogsByTxHash(eventSig common.Hash, address common.Address, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { return nil, ErrDisabled } @@ -106,3 +106,7 @@ func (d disabled) IndexedLogsCreatedAfter(eventSig common.Hash, address common.A func (d disabled) LatestBlockByEventSigsAddrsWithConfs(fromBlock int64, eventSigs []common.Hash, addresses []common.Address, confs Confirmations, qopts ...pg.QOpt) (int64, error) { return 0, ErrDisabled } + +func (d disabled) LogsDataWordBetween(eventSig common.Hash, address common.Address, wordIndexMin, wordIndexMax int, wordValue common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) { + return nil, ErrDisabled +} diff --git a/core/chains/evm/logpoller/log_poller.go b/core/chains/evm/logpoller/log_poller.go index 7c4ea66cec7..de1999da260 100644 --- a/core/chains/evm/logpoller/log_poller.go +++ b/core/chains/evm/logpoller/log_poller.go @@ -53,12 +53,13 @@ type LogPoller interface { IndexedLogs(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) IndexedLogsByBlockRange(start, end int64, eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, qopts ...pg.QOpt) ([]Log, error) IndexedLogsCreatedAfter(eventSig common.Hash, address common.Address, topicIndex int, topicValues []common.Hash, after time.Time, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) - IndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) + IndexedLogsByTxHash(eventSig common.Hash, address common.Address, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) IndexedLogsTopicGreaterThan(eventSig common.Hash, address common.Address, topicIndex int, topicValueMin common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) IndexedLogsTopicRange(eventSig common.Hash, address common.Address, topicIndex int, topicValueMin common.Hash, topicValueMax common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) IndexedLogsWithSigsExcluding(address common.Address, eventSigA, eventSigB common.Hash, topicIndex int, fromBlock, toBlock int64, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) LogsDataWordRange(eventSig common.Hash, address common.Address, wordIndex int, wordValueMin, wordValueMax common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) LogsDataWordGreaterThan(eventSig common.Hash, address common.Address, wordIndex int, wordValueMin common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) + LogsDataWordBetween(eventSig common.Hash, address common.Address, wordIndexMin, wordIndexMax int, wordValue common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) } type Confirmations int @@ -969,8 +970,8 @@ func (lp *logPoller) IndexedLogsCreatedAfter(eventSig common.Hash, address commo return lp.orm.SelectIndexedLogsCreatedAfter(address, eventSig, topicIndex, topicValues, after, confs, qopts...) } -func (lp *logPoller) IndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { - return lp.orm.SelectIndexedLogsByTxHash(eventSig, txHash, qopts...) +func (lp *logPoller) IndexedLogsByTxHash(eventSig common.Hash, address common.Address, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { + return lp.orm.SelectIndexedLogsByTxHash(address, eventSig, txHash, qopts...) } // LogsDataWordGreaterThan note index is 0 based. @@ -1021,6 +1022,19 @@ func (lp *logPoller) LatestBlockByEventSigsAddrsWithConfs(fromBlock int64, event return lp.orm.SelectLatestBlockByEventSigsAddrsWithConfs(fromBlock, eventSigs, addresses, confs, qopts...) } +// LogsDataWordBetween retrieves a slice of Log records that match specific criteria. +// Besides generic filters like eventSig, address and confs, it also verifies data content against wordValue +// data[wordIndexMin] <= wordValue <= data[wordIndexMax]. +// +// Passing the same value for wordIndexMin and wordIndexMax will check the equality of the wordValue at that index. +// Leading to returning logs matching: data[wordIndexMin] == wordValue. +// +// This function is particularly useful for filtering logs by data word values and their positions within the event data. +// It returns an empty slice if no logs match the provided criteria. +func (lp *logPoller) LogsDataWordBetween(eventSig common.Hash, address common.Address, wordIndexMin, wordIndexMax int, wordValue common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) { + return lp.orm.SelectLogsDataWordBetween(address, eventSig, wordIndexMin, wordIndexMax, wordValue, confs, qopts...) +} + // GetBlocksRange tries to get the specified block numbers from the log pollers // blocks table. It falls back to the RPC for any unfulfilled requested blocks. func (lp *logPoller) GetBlocksRange(ctx context.Context, numbers []uint64, qopts ...pg.QOpt) ([]LogPollerBlock, error) { diff --git a/core/chains/evm/logpoller/mocks/log_poller.go b/core/chains/evm/logpoller/mocks/log_poller.go index 01be5f7ba55..fe4ccc965cc 100644 --- a/core/chains/evm/logpoller/mocks/log_poller.go +++ b/core/chains/evm/logpoller/mocks/log_poller.go @@ -164,32 +164,32 @@ func (_m *LogPoller) IndexedLogsByBlockRange(start int64, end int64, eventSig co return r0, r1 } -// IndexedLogsByTxHash provides a mock function with given fields: eventSig, txHash, qopts -func (_m *LogPoller) IndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]logpoller.Log, error) { +// IndexedLogsByTxHash provides a mock function with given fields: eventSig, address, txHash, qopts +func (_m *LogPoller) IndexedLogsByTxHash(eventSig common.Hash, address common.Address, txHash common.Hash, qopts ...pg.QOpt) ([]logpoller.Log, error) { _va := make([]interface{}, len(qopts)) for _i := range qopts { _va[_i] = qopts[_i] } var _ca []interface{} - _ca = append(_ca, eventSig, txHash) + _ca = append(_ca, eventSig, address, txHash) _ca = append(_ca, _va...) ret := _m.Called(_ca...) var r0 []logpoller.Log var r1 error - if rf, ok := ret.Get(0).(func(common.Hash, common.Hash, ...pg.QOpt) ([]logpoller.Log, error)); ok { - return rf(eventSig, txHash, qopts...) + if rf, ok := ret.Get(0).(func(common.Hash, common.Address, common.Hash, ...pg.QOpt) ([]logpoller.Log, error)); ok { + return rf(eventSig, address, txHash, qopts...) } - if rf, ok := ret.Get(0).(func(common.Hash, common.Hash, ...pg.QOpt) []logpoller.Log); ok { - r0 = rf(eventSig, txHash, qopts...) + if rf, ok := ret.Get(0).(func(common.Hash, common.Address, common.Hash, ...pg.QOpt) []logpoller.Log); ok { + r0 = rf(eventSig, address, txHash, qopts...) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]logpoller.Log) } } - if rf, ok := ret.Get(1).(func(common.Hash, common.Hash, ...pg.QOpt) error); ok { - r1 = rf(eventSig, txHash, qopts...) + if rf, ok := ret.Get(1).(func(common.Hash, common.Address, common.Hash, ...pg.QOpt) error); ok { + r1 = rf(eventSig, address, txHash, qopts...) } else { r1 = ret.Error(1) } @@ -522,6 +522,39 @@ func (_m *LogPoller) LogsCreatedAfter(eventSig common.Hash, address common.Addre return r0, r1 } +// LogsDataWordBetween provides a mock function with given fields: eventSig, address, wordIndexMin, wordIndexMax, wordValue, confs, qopts +func (_m *LogPoller) LogsDataWordBetween(eventSig common.Hash, address common.Address, wordIndexMin int, wordIndexMax int, wordValue common.Hash, confs logpoller.Confirmations, qopts ...pg.QOpt) ([]logpoller.Log, error) { + _va := make([]interface{}, len(qopts)) + for _i := range qopts { + _va[_i] = qopts[_i] + } + var _ca []interface{} + _ca = append(_ca, eventSig, address, wordIndexMin, wordIndexMax, wordValue, confs) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 []logpoller.Log + var r1 error + if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, int, common.Hash, logpoller.Confirmations, ...pg.QOpt) ([]logpoller.Log, error)); ok { + return rf(eventSig, address, wordIndexMin, wordIndexMax, wordValue, confs, qopts...) + } + if rf, ok := ret.Get(0).(func(common.Hash, common.Address, int, int, common.Hash, logpoller.Confirmations, ...pg.QOpt) []logpoller.Log); ok { + r0 = rf(eventSig, address, wordIndexMin, wordIndexMax, wordValue, confs, qopts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]logpoller.Log) + } + } + + if rf, ok := ret.Get(1).(func(common.Hash, common.Address, int, int, common.Hash, logpoller.Confirmations, ...pg.QOpt) error); ok { + r1 = rf(eventSig, address, wordIndexMin, wordIndexMax, wordValue, confs, qopts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // LogsDataWordGreaterThan provides a mock function with given fields: eventSig, address, wordIndex, wordValueMin, confs, qopts func (_m *LogPoller) LogsDataWordGreaterThan(eventSig common.Hash, address common.Address, wordIndex int, wordValueMin common.Hash, confs logpoller.Confirmations, qopts ...pg.QOpt) ([]logpoller.Log, error) { _va := make([]interface{}, len(qopts)) diff --git a/core/chains/evm/logpoller/observability.go b/core/chains/evm/logpoller/observability.go index 9826d503b9f..a7a0d3c03d5 100644 --- a/core/chains/evm/logpoller/observability.go +++ b/core/chains/evm/logpoller/observability.go @@ -14,6 +14,14 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) +type queryType string + +const ( + create queryType = "create" + read queryType = "read" + del queryType = "delete" +) + var ( sqlLatencyBuckets = []float64{ float64(1 * time.Millisecond), @@ -41,47 +49,63 @@ var ( Name: "log_poller_query_duration", Help: "Measures duration of Log Poller's queries fetching logs", Buckets: sqlLatencyBuckets, - }, []string{"evmChainID", "query"}) + }, []string{"evmChainID", "query", "type"}) lpQueryDataSets = promauto.NewGaugeVec(prometheus.GaugeOpts{ Name: "log_poller_query_dataset_size", Help: "Measures size of the datasets returned by Log Poller's queries", - }, []string{"evmChainID", "query"}) + }, []string{"evmChainID", "query", "type"}) + lpLogsInserted = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "log_poller_logs_inserted", + Help: "Counter to track number of logs inserted by Log Poller", + }, []string{"evmChainID"}) + lpBlockInserted = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "log_poller_blocks_inserted", + Help: "Counter to track number of blocks inserted by Log Poller", + }, []string{"evmChainID"}) ) // ObservedORM is a decorator layer for ORM used by LogPoller, responsible for pushing Prometheus metrics reporting duration and size of result set for the queries. // It doesn't change internal logic, because all calls are delegated to the origin ORM type ObservedORM struct { ORM - queryDuration *prometheus.HistogramVec - datasetSize *prometheus.GaugeVec - chainId string + queryDuration *prometheus.HistogramVec + datasetSize *prometheus.GaugeVec + logsInserted *prometheus.CounterVec + blocksInserted *prometheus.CounterVec + chainId string } // NewObservedORM creates an observed version of log poller's ORM created by NewORM // Please see ObservedLogPoller for more details on how latencies are measured func NewObservedORM(chainID *big.Int, db *sqlx.DB, lggr logger.Logger, cfg pg.QConfig) *ObservedORM { return &ObservedORM{ - ORM: NewORM(chainID, db, lggr, cfg), - queryDuration: lpQueryDuration, - datasetSize: lpQueryDataSets, - chainId: chainID.String(), + ORM: NewORM(chainID, db, lggr, cfg), + queryDuration: lpQueryDuration, + datasetSize: lpQueryDataSets, + logsInserted: lpLogsInserted, + blocksInserted: lpBlockInserted, + chainId: chainID.String(), } } func (o *ObservedORM) InsertLogs(logs []Log, qopts ...pg.QOpt) error { - return withObservedExec(o, "InsertLogs", func() error { + err := withObservedExec(o, "InsertLogs", create, func() error { return o.ORM.InsertLogs(logs, qopts...) }) + trackInsertedLogsAndBlock(o, logs, nil, err) + return err } func (o *ObservedORM) InsertLogsWithBlock(logs []Log, block LogPollerBlock, qopts ...pg.QOpt) error { - return withObservedExec(o, "InsertLogsWithBlock", func() error { + err := withObservedExec(o, "InsertLogsWithBlock", create, func() error { return o.ORM.InsertLogsWithBlock(logs, block, qopts...) }) + trackInsertedLogsAndBlock(o, logs, &block, err) + return err } func (o *ObservedORM) InsertFilter(filter Filter, qopts ...pg.QOpt) error { - return withObservedExec(o, "InsertFilter", func() error { + return withObservedExec(o, "InsertFilter", create, func() error { return o.ORM.InsertFilter(filter, qopts...) }) } @@ -93,25 +117,25 @@ func (o *ObservedORM) LoadFilters(qopts ...pg.QOpt) (map[string]Filter, error) { } func (o *ObservedORM) DeleteFilter(name string, qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteFilter", func() error { + return withObservedExec(o, "DeleteFilter", del, func() error { return o.ORM.DeleteFilter(name, qopts...) }) } func (o *ObservedORM) DeleteBlocksBefore(end int64, qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteBlocksBefore", func() error { + return withObservedExec(o, "DeleteBlocksBefore", del, func() error { return o.ORM.DeleteBlocksBefore(end, qopts...) }) } func (o *ObservedORM) DeleteLogsAndBlocksAfter(start int64, qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteLogsAndBlocksAfter", func() error { + return withObservedExec(o, "DeleteLogsAndBlocksAfter", del, func() error { return o.ORM.DeleteLogsAndBlocksAfter(start, qopts...) }) } func (o *ObservedORM) DeleteExpiredLogs(qopts ...pg.QOpt) error { - return withObservedExec(o, "DeleteExpiredLogs", func() error { + return withObservedExec(o, "DeleteExpiredLogs", del, func() error { return o.ORM.DeleteExpiredLogs(qopts...) }) } @@ -176,9 +200,9 @@ func (o *ObservedORM) SelectLogs(start, end int64, address common.Address, event }) } -func (o *ObservedORM) IndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { - return withObservedQueryAndResults(o, "IndexedLogsByTxHash", func() ([]Log, error) { - return o.ORM.SelectIndexedLogsByTxHash(eventSig, txHash, qopts...) +func (o *ObservedORM) SelectIndexedLogsByTxHash(address common.Address, eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { + return withObservedQueryAndResults(o, "SelectIndexedLogsByTxHash", func() ([]Log, error) { + return o.ORM.SelectIndexedLogsByTxHash(address, eventSig, txHash, qopts...) }) } @@ -212,6 +236,12 @@ func (o *ObservedORM) SelectLogsDataWordGreaterThan(address common.Address, even }) } +func (o *ObservedORM) SelectLogsDataWordBetween(address common.Address, eventSig common.Hash, wordIndexMin int, wordIndexMax int, wordValue common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) { + return withObservedQueryAndResults(o, "SelectLogsDataWordBetween", func() ([]Log, error) { + return o.ORM.SelectLogsDataWordBetween(address, eventSig, wordIndexMin, wordIndexMax, wordValue, confs, qopts...) + }) +} + func (o *ObservedORM) SelectIndexedLogsTopicGreaterThan(address common.Address, eventSig common.Hash, topicIndex int, topicValueMin common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) { return withObservedQueryAndResults(o, "SelectIndexedLogsTopicGreaterThan", func() ([]Log, error) { return o.ORM.SelectIndexedLogsTopicGreaterThan(address, eventSig, topicIndex, topicValueMin, confs, qopts...) @@ -228,7 +258,7 @@ func withObservedQueryAndResults[T any](o *ObservedORM, queryName string, query results, err := withObservedQuery(o, queryName, query) if err == nil { o.datasetSize. - WithLabelValues(o.chainId, queryName). + WithLabelValues(o.chainId, queryName, string(read)). Set(float64(len(results))) } return results, err @@ -238,18 +268,33 @@ func withObservedQuery[T any](o *ObservedORM, queryName string, query func() (T, queryStarted := time.Now() defer func() { o.queryDuration. - WithLabelValues(o.chainId, queryName). + WithLabelValues(o.chainId, queryName, string(read)). Observe(float64(time.Since(queryStarted))) }() return query() } -func withObservedExec(o *ObservedORM, query string, exec func() error) error { +func withObservedExec(o *ObservedORM, query string, queryType queryType, exec func() error) error { queryStarted := time.Now() defer func() { o.queryDuration. - WithLabelValues(o.chainId, query). + WithLabelValues(o.chainId, query, string(queryType)). Observe(float64(time.Since(queryStarted))) }() return exec() } + +func trackInsertedLogsAndBlock(o *ObservedORM, logs []Log, block *LogPollerBlock, err error) { + if err != nil { + return + } + o.logsInserted. + WithLabelValues(o.chainId). + Add(float64(len(logs))) + + if block != nil { + o.blocksInserted. + WithLabelValues(o.chainId). + Inc() + } +} diff --git a/core/chains/evm/logpoller/observability_test.go b/core/chains/evm/logpoller/observability_test.go index 83bd60a5564..76575f46ca4 100644 --- a/core/chains/evm/logpoller/observability_test.go +++ b/core/chains/evm/logpoller/observability_test.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/prometheus/client_golang/prometheus" io_prometheus_client "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -18,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/services/pg" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestMultipleMetricsArePublished(t *testing.T) { @@ -54,7 +56,7 @@ func TestShouldPublishDurationInCaseOfError(t *testing.T) { require.Error(t, err) require.Equal(t, 1, testutil.CollectAndCount(orm.queryDuration)) - require.Equal(t, 1, counterFromHistogramByLabels(t, orm.queryDuration, "200", "SelectLatestLogByEventSigWithConfs")) + require.Equal(t, 1, counterFromHistogramByLabels(t, orm.queryDuration, "200", "SelectLatestLogByEventSigWithConfs", "read")) } func TestMetricsAreProperlyPopulatedWithLabels(t *testing.T) { @@ -68,14 +70,14 @@ func TestMetricsAreProperlyPopulatedWithLabels(t *testing.T) { require.NoError(t, err) } - require.Equal(t, expectedCount, counterFromHistogramByLabels(t, orm.queryDuration, "420", "query")) - require.Equal(t, expectedSize, counterFromGaugeByLabels(orm.datasetSize, "420", "query")) + require.Equal(t, expectedCount, counterFromHistogramByLabels(t, orm.queryDuration, "420", "query", "read")) + require.Equal(t, expectedSize, counterFromGaugeByLabels(orm.datasetSize, "420", "query", "read")) - require.Equal(t, 0, counterFromHistogramByLabels(t, orm.queryDuration, "420", "other_query")) - require.Equal(t, 0, counterFromHistogramByLabels(t, orm.queryDuration, "5", "query")) + require.Equal(t, 0, counterFromHistogramByLabels(t, orm.queryDuration, "420", "other_query", "read")) + require.Equal(t, 0, counterFromHistogramByLabels(t, orm.queryDuration, "5", "query", "read")) - require.Equal(t, 0, counterFromGaugeByLabels(orm.datasetSize, "420", "other_query")) - require.Equal(t, 0, counterFromGaugeByLabels(orm.datasetSize, "5", "query")) + require.Equal(t, 0, counterFromGaugeByLabels(orm.datasetSize, "420", "other_query", "read")) + require.Equal(t, 0, counterFromGaugeByLabels(orm.datasetSize, "5", "query", "read")) } func TestNotPublishingDatasetSizeInCaseOfError(t *testing.T) { @@ -84,16 +86,60 @@ func TestNotPublishingDatasetSizeInCaseOfError(t *testing.T) { _, err := withObservedQueryAndResults(orm, "errorQuery", func() ([]string, error) { return nil, fmt.Errorf("error") }) require.Error(t, err) - require.Equal(t, 1, counterFromHistogramByLabels(t, orm.queryDuration, "420", "errorQuery")) - require.Equal(t, 0, counterFromGaugeByLabels(orm.datasetSize, "420", "errorQuery")) + require.Equal(t, 1, counterFromHistogramByLabels(t, orm.queryDuration, "420", "errorQuery", "read")) + require.Equal(t, 0, counterFromGaugeByLabels(orm.datasetSize, "420", "errorQuery", "read")) } func TestMetricsAreProperlyPopulatedForWrites(t *testing.T) { orm := createObservedORM(t, 420) - require.NoError(t, withObservedExec(orm, "execQuery", func() error { return nil })) - require.Error(t, withObservedExec(orm, "execQuery", func() error { return fmt.Errorf("error") })) + require.NoError(t, withObservedExec(orm, "execQuery", create, func() error { return nil })) + require.Error(t, withObservedExec(orm, "execQuery", create, func() error { return fmt.Errorf("error") })) - require.Equal(t, 2, counterFromHistogramByLabels(t, orm.queryDuration, "420", "execQuery")) + require.Equal(t, 2, counterFromHistogramByLabels(t, orm.queryDuration, "420", "execQuery", "create")) +} + +func TestCountersAreProperlyPopulatedForWrites(t *testing.T) { + orm := createObservedORM(t, 420) + logs := generateRandomLogs(420, 20) + + // First insert 10 logs + require.NoError(t, orm.InsertLogs(logs[:10])) + assert.Equal(t, float64(10), testutil.ToFloat64(orm.logsInserted.WithLabelValues("420"))) + + // Insert 5 more logs with block + require.NoError(t, orm.InsertLogsWithBlock(logs[10:15], NewLogPollerBlock(utils.RandomBytes32(), 10, time.Now(), 5))) + assert.Equal(t, float64(15), testutil.ToFloat64(orm.logsInserted.WithLabelValues("420"))) + assert.Equal(t, float64(1), testutil.ToFloat64(orm.blocksInserted.WithLabelValues("420"))) + + // Insert 5 more logs with block + require.NoError(t, orm.InsertLogsWithBlock(logs[15:], NewLogPollerBlock(utils.RandomBytes32(), 15, time.Now(), 5))) + assert.Equal(t, float64(20), testutil.ToFloat64(orm.logsInserted.WithLabelValues("420"))) + assert.Equal(t, float64(2), testutil.ToFloat64(orm.blocksInserted.WithLabelValues("420"))) + + // Don't update counters in case of an error + require.Error(t, orm.InsertLogsWithBlock(logs, NewLogPollerBlock(utils.RandomBytes32(), 0, time.Now(), 0))) + assert.Equal(t, float64(20), testutil.ToFloat64(orm.logsInserted.WithLabelValues("420"))) + assert.Equal(t, float64(2), testutil.ToFloat64(orm.blocksInserted.WithLabelValues("420"))) +} + +func generateRandomLogs(chainId, count int) []Log { + logs := make([]Log, count) + for i := range logs { + logs[i] = Log{ + EvmChainId: utils.NewBigI(int64(chainId)), + LogIndex: int64(i + 1), + BlockHash: utils.RandomBytes32(), + BlockNumber: int64(i + 1), + BlockTimestamp: time.Now(), + Topics: [][]byte{}, + EventSig: utils.RandomBytes32(), + Address: utils.RandomAddress(), + TxHash: utils.RandomBytes32(), + Data: []byte{}, + CreatedAt: time.Now(), + } + } + return logs } func createObservedORM(t *testing.T, chainId int64) *ObservedORM { @@ -107,6 +153,8 @@ func createObservedORM(t *testing.T, chainId int64) *ObservedORM { func resetMetrics(lp ObservedORM) { lp.queryDuration.Reset() lp.datasetSize.Reset() + lp.logsInserted.Reset() + lp.blocksInserted.Reset() } func counterFromGaugeByLabels(gaugeVec *prometheus.GaugeVec, labels ...string) int { diff --git a/core/chains/evm/logpoller/orm.go b/core/chains/evm/logpoller/orm.go index c6134ed4b69..c24d423b253 100644 --- a/core/chains/evm/logpoller/orm.go +++ b/core/chains/evm/logpoller/orm.go @@ -48,9 +48,10 @@ type ORM interface { SelectIndexedLogsTopicGreaterThan(address common.Address, eventSig common.Hash, topicIndex int, topicValueMin common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) SelectIndexedLogsTopicRange(address common.Address, eventSig common.Hash, topicIndex int, topicValueMin, topicValueMax common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) SelectIndexedLogsWithSigsExcluding(sigA, sigB common.Hash, topicIndex int, address common.Address, startBlock, endBlock int64, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) - SelectIndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) + SelectIndexedLogsByTxHash(address common.Address, eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) SelectLogsDataWordRange(address common.Address, eventSig common.Hash, wordIndex int, wordValueMin, wordValueMax common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) SelectLogsDataWordGreaterThan(address common.Address, eventSig common.Hash, wordIndex int, wordValueMin common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) + SelectLogsDataWordBetween(address common.Address, eventSig common.Hash, wordIndexMin int, wordIndexMax int, wordValue common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) } type DbORM struct { @@ -535,6 +536,32 @@ func (o *DbORM) SelectLogsDataWordGreaterThan(address common.Address, eventSig c return logs, nil } +func (o *DbORM) SelectLogsDataWordBetween(address common.Address, eventSig common.Hash, wordIndexMin int, wordIndexMax int, wordValue common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) { + args, err := newQueryArgsForEvent(o.chainID, address, eventSig). + withWordIndexMin(wordIndexMin). + withWordIndexMax(wordIndexMax). + withWordValue(wordValue). + withConfs(confs). + toArgs() + if err != nil { + return nil, err + } + query := fmt.Sprintf(` + SELECT * FROM evm.logs + WHERE evm_chain_id = :evm_chain_id + AND address = :address + AND event_sig = :event_sig + AND substring(data from 32*:word_index_min+1 for 32) <= :word_value + AND substring(data from 32*:word_index_max+1 for 32) >= :word_value + AND block_number <= %s + ORDER BY (block_number, log_index)`, nestedBlockNumberQuery(confs)) + var logs []Log + if err = o.q.WithOpts(qopts...).SelectNamed(&logs, query, args); err != nil { + return nil, err + } + return logs, nil +} + func (o *DbORM) SelectIndexedLogsTopicGreaterThan(address common.Address, eventSig common.Hash, topicIndex int, topicValueMin common.Hash, confs Confirmations, qopts ...pg.QOpt) ([]Log, error) { args, err := newQueryArgsForEvent(o.chainID, address, eventSig). withTopicIndex(topicIndex). @@ -664,9 +691,10 @@ func (o *DbORM) SelectIndexedLogsCreatedAfter(address common.Address, eventSig c return logs, nil } -func (o *DbORM) SelectIndexedLogsByTxHash(eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { +func (o *DbORM) SelectIndexedLogsByTxHash(address common.Address, eventSig common.Hash, txHash common.Hash, qopts ...pg.QOpt) ([]Log, error) { args, err := newQueryArgs(o.chainID). withTxHash(txHash). + withAddress(address). withEventSig(eventSig). toArgs() if err != nil { @@ -676,8 +704,9 @@ func (o *DbORM) SelectIndexedLogsByTxHash(eventSig common.Hash, txHash common.Ha err = o.q.WithOpts(qopts...).SelectNamed(&logs, ` SELECT * FROM evm.logs WHERE evm_chain_id = :evm_chain_id + AND address = :address + AND event_sig = :event_sig AND tx_hash = :tx_hash - AND event_sig = :event_sig ORDER BY (block_number, log_index)`, args) if err != nil { return nil, err diff --git a/core/chains/evm/logpoller/orm_test.go b/core/chains/evm/logpoller/orm_test.go index e55ebeccecf..2a6ebb2c04e 100644 --- a/core/chains/evm/logpoller/orm_test.go +++ b/core/chains/evm/logpoller/orm_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest/heavyweight" @@ -49,6 +50,21 @@ func GenLogWithTimestamp(chainID *big.Int, logIndex int64, blockNum int64, block } } +func GenLogWithData(chainID *big.Int, address common.Address, eventSig common.Hash, logIndex int64, blockNum int64, data []byte) logpoller.Log { + return logpoller.Log{ + EvmChainId: utils.NewBig(chainID), + LogIndex: logIndex, + BlockHash: utils.RandomBytes32(), + BlockNumber: blockNum, + EventSig: eventSig, + Topics: [][]byte{}, + Address: address, + TxHash: utils.RandomBytes32(), + Data: data, + BlockTimestamp: time.Now(), + } +} + func TestLogPoller_Batching(t *testing.T) { t.Parallel() th := SetupTH(t, false, 2, 3, 2, 1000) @@ -568,7 +584,7 @@ func TestORM_SelectIndexedLogsByTxHash(t *testing.T) { } require.NoError(t, o1.InsertLogs(logs)) - retrievedLogs, err := o1.SelectIndexedLogsByTxHash(eventSig, txHash) + retrievedLogs, err := o1.SelectIndexedLogsByTxHash(addr, eventSig, txHash) require.NoError(t, err) require.Equal(t, 2, len(retrievedLogs)) @@ -1434,3 +1450,117 @@ func TestInsertLogsInTx(t *testing.T) { }) } } + +func TestSelectLogsDataWordBetween(t *testing.T) { + address := utils.RandomAddress() + eventSig := utils.RandomBytes32() + th := SetupTH(t, false, 2, 3, 2, 1000) + + firstLogData := make([]byte, 0, 64) + firstLogData = append(firstLogData, logpoller.EvmWord(1).Bytes()...) + firstLogData = append(firstLogData, logpoller.EvmWord(10).Bytes()...) + + secondLogData := make([]byte, 0, 64) + secondLogData = append(secondLogData, logpoller.EvmWord(5).Bytes()...) + secondLogData = append(secondLogData, logpoller.EvmWord(20).Bytes()...) + + err := th.ORM.InsertLogsWithBlock( + []logpoller.Log{ + GenLogWithData(th.ChainID, address, eventSig, 1, 1, firstLogData), + GenLogWithData(th.ChainID, address, eventSig, 2, 2, secondLogData), + }, + logpoller.NewLogPollerBlock(utils.RandomBytes32(), 10, time.Now(), 1), + ) + require.NoError(t, err) + + tests := []struct { + name string + wordValue uint64 + expectedLogs []int64 + }{ + { + name: "returns only first log", + wordValue: 2, + expectedLogs: []int64{1}, + }, + { + name: "returns only second log", + wordValue: 11, + expectedLogs: []int64{2}, + }, + { + name: "returns both logs if word value is between", + wordValue: 5, + expectedLogs: []int64{1, 2}, + }, + { + name: "returns no logs if word value is outside of the range", + wordValue: 21, + expectedLogs: []int64{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + logs, err1 := th.ORM.SelectLogsDataWordBetween(address, eventSig, 0, 1, logpoller.EvmWord(tt.wordValue), logpoller.Unconfirmed) + assert.NoError(t, err1) + assert.Len(t, logs, len(tt.expectedLogs)) + + for index := range logs { + assert.Equal(t, tt.expectedLogs[index], logs[index].BlockNumber) + } + }) + } +} + +func Benchmark_LogsDataWordBetween(b *testing.B) { + chainId := big.NewInt(137) + _, db := heavyweight.FullTestDBV2(b, nil) + o := logpoller.NewORM(chainId, db, logger.Test(b), pgtest.NewQConfig(false)) + + numberOfReports := 100_000 + numberOfMessagesPerReport := 256 + + commitStoreAddress := utils.RandomAddress() + commitReportAccepted := utils.RandomBytes32() + + var dbLogs []logpoller.Log + for i := 0; i < numberOfReports; i++ { + data := make([]byte, 64) + // MinSeqNr + data = append(data, logpoller.EvmWord(uint64(numberOfMessagesPerReport*i+1)).Bytes()...) + // MaxSeqNr + data = append(data, logpoller.EvmWord(uint64(numberOfMessagesPerReport*(i+1))).Bytes()...) + + dbLogs = append(dbLogs, logpoller.Log{ + EvmChainId: utils.NewBig(chainId), + LogIndex: int64(i + 1), + BlockHash: utils.RandomBytes32(), + BlockNumber: int64(i + 1), + BlockTimestamp: time.Now(), + EventSig: commitReportAccepted, + Topics: [][]byte{}, + Address: commitStoreAddress, + TxHash: utils.RandomAddress().Hash(), + Data: data, + CreatedAt: time.Now(), + }) + } + require.NoError(b, o.InsertBlock(utils.RandomAddress().Hash(), int64(numberOfReports*numberOfMessagesPerReport), time.Now(), int64(numberOfReports*numberOfMessagesPerReport))) + require.NoError(b, o.InsertLogs(dbLogs)) + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + logs, err := o.SelectLogsDataWordBetween( + commitStoreAddress, + commitReportAccepted, + 2, + 3, + logpoller.EvmWord(uint64(numberOfReports*numberOfMessagesPerReport/2)), // Pick the middle report + logpoller.Unconfirmed, + ) + assert.NoError(b, err) + assert.Len(b, logs, 1) + } +} diff --git a/core/chains/evm/logpoller/query.go b/core/chains/evm/logpoller/query.go index 7443a860a85..b7fbfade680 100644 --- a/core/chains/evm/logpoller/query.go +++ b/core/chains/evm/logpoller/query.go @@ -82,6 +82,18 @@ func (q *queryArgs) withWordValueMax(wordValueMax common.Hash) *queryArgs { return q.withCustomHashArg("word_value_max", wordValueMax) } +func (q *queryArgs) withWordIndexMin(wordIndex int) *queryArgs { + return q.withCustomArg("word_index_min", wordIndex) +} + +func (q *queryArgs) withWordIndexMax(wordIndex int) *queryArgs { + return q.withCustomArg("word_index_max", wordIndex) +} + +func (q *queryArgs) withWordValue(wordValue common.Hash) *queryArgs { + return q.withCustomHashArg("word_value", wordValue) +} + func (q *queryArgs) withConfs(confs Confirmations) *queryArgs { return q.withCustomArg("confs", confs) } diff --git a/core/store/migrate/migrations/0211_log_poller_word_indexes.sql b/core/store/migrate/migrations/0211_log_poller_word_indexes.sql new file mode 100644 index 00000000000..3d2e8bf8a4e --- /dev/null +++ b/core/store/migrate/migrations/0211_log_poller_word_indexes.sql @@ -0,0 +1,6 @@ +-- +goose Up +CREATE INDEX evm_logs_idx_data_word_four ON evm.logs (substring(data from 97 for 32)); + + +-- +goose Down +DROP INDEX IF EXISTS evm.evm_logs_idx_data_word_four; From e4f6d9906cb1aa010827b913b23886ac11c68324 Mon Sep 17 00:00:00 2001 From: Tate Date: Wed, 29 Nov 2023 08:06:02 -0700 Subject: [PATCH 220/271] Only enforce ctf version on merge_group push to develop (#11389) * Only enforce ctf version on merge_group push to develop * add debug echo to see what was missed * Updated with refernences to merge_group payload instead of pull_request payload * use output and test that we can indeed do this in a fail situation so we can switch it back and know it will work for a true since we already validated that we do get true in the correct place * The test worked, flip back to enforcing only for merge groups to develop --- .github/workflows/integration-tests.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 5a505ce01ca..dd696f7b2df 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -28,7 +28,23 @@ jobs: steps: - name: Checkout the repo uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - name: Check Merge Group Condition + id: condition-check + run: | + echo "Checking event condition..." + SHOULD_ENFORCE="false" + if [[ "$GITHUB_EVENT_NAME" == "merge_group" ]]; then + echo "We are in a merge_group event, now check if we are on the develop branch" + target_branch=$(cat $GITHUB_EVENT_PATH | jq -r .merge_group.base_ref) + if [[ "$target_branch" == "refs/heads/develop" ]]; then + echo "We are on the develop branch, we should enforce ctf version" + SHOULD_ENFORCE="true" + fi + fi + echo "should we enforce ctf version = $SHOULD_ENFORCE" + echo "should-enforce=$SHOULD_ENFORCE" >> $GITHUB_OUTPUT - name: Enforce CTF Version + if: steps.condition-check.outputs.should-enforce == 'true' uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/mod-version@e865e376b8c2d594028c8d645dd6c47169b72974 # v2.2.16 with: go-project-path: ./integration-tests From 7e7b84bb1b8a03d17c9b4dd49932854b0ea36c11 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 29 Nov 2023 10:38:08 -0600 Subject: [PATCH 221/271] core/services/chainlink: skip P2P Peer Wrapper when unused (#11411) --- core/services/chainlink/application.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 1ecf95d3a5e..3d06789e7bc 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -360,7 +360,9 @@ func NewApplication(opts ApplicationOpts) (Application, error) { } var peerWrapper *ocrcommon.SingletonPeerWrapper - if cfg.P2P().Enabled() { + if !cfg.OCR().Enabled() && !cfg.OCR2().Enabled() { + globalLogger.Debug("P2P stack not needed") + } else if cfg.P2P().Enabled() { if err := ocrcommon.ValidatePeerWrapperConfig(cfg.P2P()); err != nil { return nil, err } From e546a7ee4fbe07a27198c644a906f7f199ea8e68 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:49:11 +0100 Subject: [PATCH 222/271] Update CleanupChainTables shell cmd to specify schema (#11409) * Update CleanupChainTables shell cmd to specify schema * Update testdb naming checks to match * Add test that checks if cleanup chain table shell cmd works * Fix shell cmd test db checks to match CI * Adjust CleanupChainTables test to handle heavyweight db testdb naming * Change CleanupChainTables to account for duplicate table names --- core/cmd/shell_local.go | 27 ++++++++++++++++++++------- core/cmd/shell_local_test.go | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index ada70736f49..dc0d44f3189 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -996,23 +996,36 @@ func (s *Shell) CleanupChainTables(c *cli.Context) error { if err != nil { return s.errorOut(errors.Wrap(err, "error connecting to the database")) } - defer db.Close() - tablesToDeleteFromQuery := `SELECT table_name FROM information_schema.columns WHERE "column_name"=$1;` + // some tables with evm_chain_id (mostly job specs) are in public schema + tablesToDeleteFromQuery := `SELECT table_name, table_schema FROM information_schema.columns WHERE "column_name"=$1;` // Delete rows from each table based on the chain_id. if strings.EqualFold("EVM", c.String("type")) { - var tables []string - if err = db.Select(&tables, tablesToDeleteFromQuery, "evm_chain_id"); err != nil { + rows, err := db.Query(tablesToDeleteFromQuery, "evm_chain_id") + if err != nil { return err + } else if rows.Err() != nil { + return rows.Err() } - for _, tableName := range tables { + + var tablesToDeleteFrom []string + for rows.Next() { + var name string + var schema string + if err = rows.Scan(&name, &schema); err != nil { + return err + } + tablesToDeleteFrom = append(tablesToDeleteFrom, schema+"."+name) + } + + for _, tableName := range tablesToDeleteFrom { query := fmt.Sprintf(`DELETE FROM %s WHERE "evm_chain_id"=$1;`, tableName) _, err = db.Exec(query, c.String("id")) if err != nil { - fmt.Printf("Error deleting rows from %s: %v\n", tableName, err) + fmt.Printf("Error deleting rows containing evm_chain_id from %s: %v\n", tableName, err) } else { - fmt.Printf("Rows with chain_id %s deleted from %s.\n", c.String("id"), tableName) + fmt.Printf("Rows with evm_chain_id %s deleted from %s.\n", c.String("id"), tableName) } } } else { diff --git a/core/cmd/shell_local_test.go b/core/cmd/shell_local_test.go index 1e030fa9e9e..fac2d7f040b 100644 --- a/core/cmd/shell_local_test.go +++ b/core/cmd/shell_local_test.go @@ -492,3 +492,23 @@ func TestShell_RebroadcastTransactions_AddressCheck(t *testing.T) { }) } } + +func TestShell_CleanupChainTables(t *testing.T) { + // Just check if it doesn't error, command itself shouldn't be changed unless major schema changes were made. + // It would be really hard to write a test that accounts for schema changes, so this should be enough to alarm us that something broke. + config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { c.Database.Dialect = dialects.Postgres }) + client := cmd.Shell{ + Config: config, + Logger: logger.TestLogger(t), + } + + set := flag.NewFlagSet("test", 0) + flagSetApplyFromAction(client.CleanupChainTables, set, "") + require.NoError(t, set.Set("id", testutils.FixtureChainID.String())) + require.NoError(t, set.Set("type", "EVM")) + // heavyweight creates test db named chainlink_test_uid, while usual naming is chainlink_test + // CleanupChainTables handles test db name with chainlink_test, but because of heavyweight test db naming we have to set danger flag + require.NoError(t, set.Set("danger", "true")) + c := cli.NewContext(nil, set, nil) + require.NoError(t, client.CleanupChainTables(c)) +} From 3876d9e077734deb284ee48e21b4be19efd92e7c Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 29 Nov 2023 13:40:17 -0600 Subject: [PATCH 223/271] embed Solana & Feeds LOOPPs; adjust CI (#11401) --- .github/workflows/integration-tests.yml | 39 +++---------------- core/chainlink.Dockerfile | 20 ++++++++++ core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- integration-tests/docker/test_env/test_env.go | 7 ++-- .../docker/test_env/test_env_builder.go | 10 ++++- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 10 files changed, 45 insertions(+), 49 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index dd696f7b2df..9e2cce02122 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -781,7 +781,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: smartcontractkit/chainlink-solana - ref: 23816fcf7d380a30c87b6d87e4fb0ca94419b259 # swtich back to this after the next solana release${{ needs.get_solana_sha.outputs.sha }} + ref: ${{ needs.get_solana_sha.outputs.sha }} - name: Build Test Image if: needs.changes.outputs.src == 'true' && needs.solana-test-image-exists.outputs.exists == 'false' uses: ./.github/actions/build-test-image @@ -794,7 +794,7 @@ jobs: - run: echo "this exists so we don't have to run anything else if the build is skipped" if: needs.changes.outputs.src == 'false' || needs.solana-test-image-exists.outputs.exists == 'true' - solana-smoke-tests-matrix: + solana-smoke-tests: if: ${{ !contains(join(github.event.pull_request.labels.*.name, ' '), 'skip-smoke-tests') }} environment: integration permissions: @@ -802,14 +802,7 @@ jobs: pull-requests: write id-token: write contents: read - strategy: - matrix: - image: - - name: (legacy) - tag-suffix: "" - - name: (plugins) - tag-suffix: -plugins - name: Solana Smoke Tests ${{ matrix.image.name }} + name: Solana Smoke Tests runs-on: ubuntu20.04-16cores-64GB needs: [ @@ -832,7 +825,7 @@ jobs: with: basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} hostname: ${{ secrets.GRAFANA_CLOUD_HOST }} - this-job-name: Solana Smoke Tests ${{ matrix.image.name }} + this-job-name: Solana Smoke Tests test-results-file: '{"testType":"go","filePath":"/tmp/gotest.log"}' continue-on-error: true - name: Checkout the repo @@ -875,7 +868,7 @@ jobs: with: test_command_to_run: export ENV_JOB_IMAGE=${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-solana-tests:${{ needs.get_solana_sha.outputs.sha }} && make test_smoke cl_repo: ${{ env.CHAINLINK_IMAGE }} - cl_image_tag: ${{ github.sha }}${{ matrix.image.tag-suffix }} + cl_image_tag: ${{ github.sha }} artifacts_location: /home/runner/work/chainlink-solana/chainlink-solana/integration-tests/logs publish_check_name: Solana Smoke Test Results go_mod_path: ./integration-tests/go.mod @@ -894,25 +887,3 @@ jobs: path: /tmp/gotest.log retention-days: 7 continue-on-error: true - - ### Used to check the required checks box when the matrix completes - solana-smoke-tests: - if: always() - runs-on: ubuntu-latest - name: Solana Smoke Tests - needs: [solana-smoke-tests-matrix] - steps: - - name: Check smoke test matrix status - if: needs.solana-smoke-tests-matrix.result != 'success' - run: exit 1 - - 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: Solana Smoke Tests - matrix-aggregator-status: ${{ needs.solana-smoke-tests-matrix.result }} - continue-on-error: true - ### End Solana Section diff --git a/core/chainlink.Dockerfile b/core/chainlink.Dockerfile index be1fdb9053b..22e65c0aa7a 100644 --- a/core/chainlink.Dockerfile +++ b/core/chainlink.Dockerfile @@ -17,6 +17,22 @@ COPY . . # Build the golang binary RUN make install-chainlink +# Link LOOP Plugin source dirs with simple names +RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-feeds | xargs -I % ln -s % /chainlink-feeds +RUN go list -m -f "{{.Dir}}" github.com/smartcontractkit/chainlink-solana | xargs -I % ln -s % /chainlink-solana + +# Build image: Plugins +FROM golang:1.21-bullseye as buildplugins +RUN go version + +WORKDIR /chainlink-feeds +COPY --from=buildgo /chainlink-feeds . +RUN go install ./cmd/chainlink-feeds + +WORKDIR /chainlink-solana +COPY --from=buildgo /chainlink-solana . +RUN go install ./pkg/solana/cmd/chainlink-solana + # Final image: ubuntu with chainlink binary FROM ubuntu:20.04 @@ -32,6 +48,10 @@ RUN curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ COPY --from=buildgo /go/bin/chainlink /usr/local/bin/ +# Install (but don't enable) LOOP Plugins +COPY --from=buildplugins /go/bin/chainlink-feeds /usr/local/bin/ +COPY --from=buildplugins /go/bin/chainlink-solana /usr/local/bin/ + # Dependency of CosmWasm/wasmd COPY --from=buildgo /go/pkg/mod/github.com/\!cosm\!wasm/wasmvm@v*/internal/api/libwasmvm.*.so /usr/lib/ RUN chmod 755 /usr/lib/libwasmvm.*.so diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 7fa6055b161..1eb186931f8 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -307,7 +307,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 7706fa15f19..2a77f2066db 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1510,8 +1510,8 @@ github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a454 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= diff --git a/go.mod b/go.mod index 0133011813e..c105c1c6000 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/go.sum b/go.sum index 504f2ba8073..18d78d4815e 100644 --- a/go.sum +++ b/go.sum @@ -1513,8 +1513,8 @@ github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a454 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 6278ec13987..4b0cefddd55 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -151,15 +151,14 @@ func (te *CLClusterTestEnv) StartMockAdapter() error { return te.MockAdapter.StartContainer() } -func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count int, secretsConfig string) error { +func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count int, secretsConfig string, opts ...ClNodeOption) error { if te.Cfg != nil && te.Cfg.ClCluster != nil { te.ClCluster = te.Cfg.ClCluster } else { + opts = append(opts, WithSecrets(secretsConfig)) te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { - ocrNode := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, - WithSecrets(secretsConfig), - ) + ocrNode := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, opts...) te.ClCluster.Nodes = append(te.ClCluster.Nodes, ocrNode) } } diff --git a/integration-tests/docker/test_env/test_env_builder.go b/integration-tests/docker/test_env/test_env_builder.go index 32f84991eef..685ea5338ae 100644 --- a/integration-tests/docker/test_env/test_env_builder.go +++ b/integration-tests/docker/test_env/test_env_builder.go @@ -40,6 +40,7 @@ type CLTestEnvBuilder struct { secretsConfig string nonDevGethNetworks []blockchain.EVMNetwork clNodesCount int + clNodesOpts []func(*ClNode) customNodeCsaKeys []string defaultNodeCsaKeys []string l zerolog.Logger @@ -110,6 +111,11 @@ func (b *CLTestEnvBuilder) WithCLNodes(clNodesCount int) *CLTestEnvBuilder { return b } +func (b *CLTestEnvBuilder) WithCLNodeOptions(opt ...ClNodeOption) *CLTestEnvBuilder { + b.clNodesOpts = append(b.clNodesOpts, opt...) + return b +} + func (b *CLTestEnvBuilder) WithForwarders() *CLTestEnvBuilder { b.hasForwarders = true return b @@ -267,7 +273,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { return nil, fmt.Errorf("cannot create nodes with custom config without nonDevNetworks") } - err = b.te.StartClCluster(b.clNodeConfig, b.clNodesCount, b.secretsConfig) + err = b.te.StartClCluster(b.clNodeConfig, b.clNodesCount, b.secretsConfig, b.clNodesOpts...) if err != nil { return nil, err } @@ -351,7 +357,7 @@ func (b *CLTestEnvBuilder) Build() (*CLClusterTestEnv, error) { } } - err := b.te.StartClCluster(cfg, b.clNodesCount, b.secretsConfig) + err := b.te.StartClCluster(cfg, b.clNodesCount, b.secretsConfig, b.clNodesOpts...) if err != nil { return nil, err } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 63bf5e9e55f..5f250af6fa8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -391,7 +391,7 @@ require ( github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index a1a272c0b8b..855d97d5e40 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2382,8 +2382,8 @@ github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a454 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d/go.mod h1:YPAfLNowdBwiKiYOwgwtbJHi8AJWbcxkbOY0ItAvkfc= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1 h1:D7yb4kgNGVAiD5lFYqm/LW8d5jU66TXyYvSskDiW9yg= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231117191230-aa6640f2edd1/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 h1:DudPr8ZNMEVgDwHBvnrpvK96JrGcGCG+LLBnlqaPGnE= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= github.com/smartcontractkit/chainlink-testing-framework v1.19.5 h1:Iq1L7wCA8IkBBtP3p6W2ttu8v9cJp95spusnozW1UrA= From f77100049fa4f39274763e1d0323041397c69ad7 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Wed, 29 Nov 2023 22:13:42 +0200 Subject: [PATCH 224/271] Change difficulty from Big to BigInt (#11388) * Change difficulty from Big to BigInt * Fix headtracker mock head * Remove EsnureClosed * Fix mock heads * Fix Tracker close on txm --- common/client/mock_head_test.go | 11 ++-- common/client/mock_node_test.go | 14 ++--- common/client/multi_node.go | 6 +- common/client/multi_node_test.go | 28 +++++----- common/client/node.go | 8 +-- common/client/node_fsm.go | 7 +-- common/client/node_lifecycle.go | 17 +++--- common/client/node_lifecycle_test.go | 55 ++++++++++--------- .../client/node_selector_total_difficulty.go | 4 +- .../node_selector_total_difficulty_test.go | 34 ++++++------ common/client/types.go | 3 +- common/fee/models.go | 2 +- common/headtracker/types/mocks/head.go | 14 ++--- common/txmgr/broadcaster.go | 2 +- common/txmgr/txmgr.go | 17 +++--- common/types/head.go | 5 +- common/types/mocks/head.go | 11 ++-- core/chains/evm/assets/wei.go | 2 +- core/chains/evm/client/erroring_node.go | 3 +- core/chains/evm/client/helpers_test.go | 3 +- core/chains/evm/client/node.go | 7 +-- core/chains/evm/client/node_fsm.go | 7 +-- core/chains/evm/client/node_lifecycle.go | 17 +++--- core/chains/evm/client/node_lifecycle_test.go | 25 ++++----- .../client/node_selector_total_difficulty.go | 6 +- .../node_selector_total_difficulty_test.go | 34 ++++++------ core/chains/evm/client/pool.go | 4 +- .../evm/client/simulated_backend_client.go | 2 +- core/chains/evm/gas/models.go | 2 +- core/chains/evm/mocks/node.go | 12 ++-- core/chains/evm/txmgr/transmitchecker.go | 3 +- core/chains/evm/types/models.go | 10 ++-- core/utils/big.go | 2 +- core/utils/utils.go | 11 ---- 34 files changed, 188 insertions(+), 200 deletions(-) diff --git a/common/client/mock_head_test.go b/common/client/mock_head_test.go index b9cf0a5866f..1b69eedf438 100644 --- a/common/client/mock_head_test.go +++ b/common/client/mock_head_test.go @@ -3,7 +3,8 @@ package client import ( - utils "github.com/smartcontractkit/chainlink/v2/core/utils" + big "math/big" + mock "github.com/stretchr/testify/mock" ) @@ -13,15 +14,15 @@ type mockHead struct { } // BlockDifficulty provides a mock function with given fields: -func (_m *mockHead) BlockDifficulty() *utils.Big { +func (_m *mockHead) BlockDifficulty() *big.Int { ret := _m.Called() - var r0 *utils.Big - if rf, ok := ret.Get(0).(func() *utils.Big); ok { + var r0 *big.Int + if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*utils.Big) + r0 = ret.Get(0).(*big.Int) } } diff --git a/common/client/mock_node_test.go b/common/client/mock_node_test.go index bd704cd2c6f..ea0e7d1a120 100644 --- a/common/client/mock_node_test.go +++ b/common/client/mock_node_test.go @@ -4,11 +4,11 @@ package client import ( context "context" + big "math/big" - types "github.com/smartcontractkit/chainlink/v2/common/types" mock "github.com/stretchr/testify/mock" - utils "github.com/smartcontractkit/chainlink/v2/core/utils" + types "github.com/smartcontractkit/chainlink/v2/common/types" ) // mockNode is an autogenerated mock type for the Node type @@ -115,13 +115,13 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) State() nodeState { } // StateAndLatest provides a mock function with given fields: -func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *utils.Big) { +func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *big.Int) { ret := _m.Called() var r0 nodeState var r1 int64 - var r2 *utils.Big - if rf, ok := ret.Get(0).(func() (nodeState, int64, *utils.Big)); ok { + var r2 *big.Int + if rf, ok := ret.Get(0).(func() (nodeState, int64, *big.Int)); ok { return rf() } if rf, ok := ret.Get(0).(func() nodeState); ok { @@ -136,11 +136,11 @@ func (_m *mockNode[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *ut r1 = ret.Get(1).(int64) } - if rf, ok := ret.Get(2).(func() *utils.Big); ok { + if rf, ok := ret.Get(2).(func() *big.Int); ok { r2 = rf() } else { if ret.Get(2) != nil { - r2 = ret.Get(2).(*utils.Big) + r2 = ret.Get(2).(*big.Int) } } diff --git a/common/client/multi_node.go b/common/client/multi_node.go index 3f72d04124f..db5380e91f5 100644 --- a/common/client/multi_node.go +++ b/common/client/multi_node.go @@ -14,11 +14,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink-common/pkg/utils" "github.com/smartcontractkit/chainlink/v2/common/config" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -261,8 +261,8 @@ func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OP // nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. // totalDifficulty will be 0 if all nodes return nil. -func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *utils.Big) { - totalDifficulty = utils.NewBigI(0) +func (c *multiNode[CHAIN_ID, SEQ, ADDR, BLOCK_HASH, TX, TX_HASH, EVENT, EVENT_OPS, TX_RECEIPT, FEE, HEAD, RPC_CLIENT]) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { + totalDifficulty = big.NewInt(0) for _, n := range c.nodes { if s, num, td := n.StateAndLatest(); s == nodeStateAlive { nLiveNodes++ diff --git a/common/client/multi_node_test.go b/common/client/multi_node_test.go index 45ffb745370..229f1320a14 100644 --- a/common/client/multi_node_test.go +++ b/common/client/multi_node_test.go @@ -2,6 +2,7 @@ package client import ( "fmt" + big "math/big" "math/rand" "testing" "time" @@ -17,14 +18,13 @@ import ( "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) -type multiNodeRPCClient RPC[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, +type multiNodeRPCClient RPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] type testMultiNode struct { - *multiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + *multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient] } @@ -46,19 +46,19 @@ func newTestMultiNode(t *testing.T, opts multiNodeOpts) testMultiNode { opts.logger = logger.Test(t) } - result := NewMultiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + result := NewMultiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient](opts.logger, opts.selectionMode, opts.leaseDuration, opts.noNewHeadsThreshold, opts.nodes, opts.sendonlys, opts.chainID, opts.chainType, opts.chainFamily, opts.sendOnlyErrorParser) return testMultiNode{ - result.(*multiNode[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + result.(*multiNode[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable], multiNodeRPCClient]), } } -func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, +func newMultiNodeRPCClient(t *testing.T) *mockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]] { - return newMockRPC[types.ID, *utils.Big, Hashable, Hashable, any, Hashable, any, any, + return newMockRPC[types.ID, *big.Int, Hashable, Hashable, any, Hashable, any, any, types.Receipt[Hashable, Hashable], Hashable, types.Head[Hashable]](t) } @@ -424,40 +424,40 @@ func TestMultiNode_nLiveNodes(t *testing.T) { t.Parallel() type nodeParams struct { BlockNumber int64 - TotalDifficulty *utils.Big + TotalDifficulty *big.Int State nodeState } testCases := []struct { Name string ExpectedNLiveNodes int ExpectedBlockNumber int64 - ExpectedTotalDifficulty *utils.Big + ExpectedTotalDifficulty *big.Int NodeParams []nodeParams }{ { Name: "no nodes", - ExpectedTotalDifficulty: utils.NewBigI(0), + ExpectedTotalDifficulty: big.NewInt(0), }, { Name: "Best node is not healthy", - ExpectedTotalDifficulty: utils.NewBigI(10), + ExpectedTotalDifficulty: big.NewInt(10), ExpectedBlockNumber: 20, ExpectedNLiveNodes: 3, NodeParams: []nodeParams{ { State: nodeStateOutOfSync, BlockNumber: 1000, - TotalDifficulty: utils.NewBigI(2000), + TotalDifficulty: big.NewInt(2000), }, { State: nodeStateAlive, BlockNumber: 20, - TotalDifficulty: utils.NewBigI(9), + TotalDifficulty: big.NewInt(9), }, { State: nodeStateAlive, BlockNumber: 19, - TotalDifficulty: utils.NewBigI(10), + TotalDifficulty: big.NewInt(10), }, { State: nodeStateAlive, diff --git a/common/client/node.go b/common/client/node.go index b2d3a33a5b8..4fad18b42cf 100644 --- a/common/client/node.go +++ b/common/client/node.go @@ -3,6 +3,7 @@ package client import ( "context" "fmt" + "math/big" "net/url" "sync" "time" @@ -15,7 +16,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) const QueryTimeout = 10 * time.Second @@ -53,7 +53,7 @@ type Node[ // State returns nodeState State() nodeState // StateAndLatest returns nodeState with the latest received block number & total difficulty. - StateAndLatest() (nodeState, int64, *utils.Big) + StateAndLatest() (nodeState, int64, *big.Int) // Name is a unique identifier for this node. Name() string String() string @@ -90,7 +90,7 @@ type node[ state nodeState // Each node is tracking the last received head number and total difficulty stateLatestBlockNumber int64 - stateLatestTotalDifficulty *utils.Big + stateLatestTotalDifficulty *big.Int // nodeCtx is the node lifetime's context nodeCtx context.Context @@ -103,7 +103,7 @@ type node[ // 1. see how many live nodes there are in total, so we can prevent the last alive node in a pool from being // moved to out-of-sync state. It is better to have one out-of-sync node than no nodes at all. // 2. compare against the highest head (by number or difficulty) to ensure we don't fall behind too far. - nLiveNodes func() (count int, blockNumber int64, totalDifficulty *utils.Big) + nLiveNodes func() (count int, blockNumber int64, totalDifficulty *big.Int) } func NewNode[ diff --git a/common/client/node_fsm.go b/common/client/node_fsm.go index d4fc19140e9..74a5814f773 100644 --- a/common/client/node_fsm.go +++ b/common/client/node_fsm.go @@ -2,11 +2,10 @@ package client import ( "fmt" + "math/big" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -110,7 +109,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) State() nodeState { return n.state } -func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *utils.Big) { +func (n *node[CHAIN_ID, HEAD, RPC]) StateAndLatest() (nodeState, int64, *big.Int) { n.stateMu.RLock() defer n.stateMu.RUnlock() return n.state, n.stateLatestBlockNumber, n.stateLatestTotalDifficulty @@ -182,7 +181,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) transitionToInSync(fn func()) { // declareOutOfSync puts a node into OutOfSync state, disconnecting all current // clients and making it unavailable for use until back in-sync. -func (n *node[CHAIN_ID, HEAD, RPC]) declareOutOfSync(isOutOfSync func(num int64, td *utils.Big) bool) { +func (n *node[CHAIN_ID, HEAD, RPC]) declareOutOfSync(isOutOfSync func(num int64, td *big.Int) bool) { n.transitionToOutOfSync(func() { n.lfcLog.Errorw("RPC Node is out of sync", "nodeState", n.state) n.wg.Add(1) diff --git a/common/client/node_lifecycle.go b/common/client/node_lifecycle.go index 9b47412766b..59a59691c83 100644 --- a/common/client/node_lifecycle.go +++ b/common/client/node_lifecycle.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "math/big" "time" "github.com/pkg/errors" @@ -11,6 +12,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-common/pkg/logger" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" + "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -49,7 +52,7 @@ func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration { return utils.WithJitter(interval) } -func (n *node[CHAIN_ID, HEAD, RPC]) setLatestReceived(blockNumber int64, totalDifficulty *utils.Big) { +func (n *node[CHAIN_ID, HEAD, RPC]) setLatestReceived(blockNumber int64, totalDifficulty *big.Int) { n.stateMu.Lock() defer n.stateMu.Unlock() n.stateLatestBlockNumber = blockNumber @@ -216,13 +219,13 @@ func (n *node[CHAIN_ID, HEAD, RPC]) aliveLoop() { continue } } - n.declareOutOfSync(func(num int64, td *utils.Big) bool { return num < highestReceivedBlockNumber }) + n.declareOutOfSync(func(num int64, td *big.Int) bool { return num < highestReceivedBlockNumber }) return } } } -func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSync(num int64, td *utils.Big) (outOfSync bool) { +func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSync(num int64, td *big.Int) (outOfSync bool) { outOfSync, _ = n.syncStatus(num, td) return } @@ -230,7 +233,7 @@ func (n *node[CHAIN_ID, HEAD, RPC]) isOutOfSync(num int64, td *utils.Big) (outOf // syncStatus returns outOfSync true if num or td is more than SyncThresold behind the best node. // Always returns outOfSync false for SyncThreshold 0. // liveNodes is only included when outOfSync is true. -func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *utils.Big) (outOfSync bool, liveNodes int) { +func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *big.Int) (outOfSync bool, liveNodes int) { if n.nLiveNodes == nil { return // skip for tests } @@ -245,8 +248,8 @@ func (n *node[CHAIN_ID, HEAD, RPC]) syncStatus(num int64, td *utils.Big) (outOfS case NodeSelectionModeHighestHead, NodeSelectionModeRoundRobin, NodeSelectionModePriorityLevel: return num < highest-int64(threshold), ln case NodeSelectionModeTotalDifficulty: - bigThreshold := utils.NewBigI(int64(threshold)) - return td.Cmp(greatest.Sub(bigThreshold)) < 0, ln + bigThreshold := big.NewInt(int64(threshold)) + return td.Cmp(bigmath.Sub(greatest, bigThreshold)) < 0, ln default: panic("unrecognized NodeSelectionMode: " + mode) } @@ -258,7 +261,7 @@ const ( ) // outOfSyncLoop takes an OutOfSync node and waits until isOutOfSync returns false to go back to live status -func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td *utils.Big) bool) { +func (n *node[CHAIN_ID, HEAD, RPC]) outOfSyncLoop(isOutOfSync func(num int64, td *big.Int) bool) { defer n.wg.Done() { diff --git a/common/client/node_lifecycle_test.go b/common/client/node_lifecycle_test.go index 51b44ac516d..224b79d8378 100644 --- a/common/client/node_lifecycle_test.go +++ b/common/client/node_lifecycle_test.go @@ -2,6 +2,7 @@ package client import ( "fmt" + big "math/big" "sync/atomic" "testing" @@ -12,11 +13,11 @@ import ( "go.uber.org/zap" "github.com/smartcontractkit/chainlink-common/pkg/logger" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink/v2/common/types" "github.com/smartcontractkit/chainlink/v2/common/types/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { @@ -188,8 +189,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { - return 1, 20, utils.NewBigI(10) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { + return 1, 20, big.NewInt(10) } pollError := errors.New("failed to get ClientVersion") rpc.On("ClientVersion", mock.Anything).Return("", pollError) @@ -213,8 +214,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { - return 10, syncThreshold + node.stateLatestBlockNumber + 1, utils.NewBigI(10) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { + return 10, syncThreshold + node.stateLatestBlockNumber + 1, big.NewInt(10) } rpc.On("ClientVersion", mock.Anything).Return("", nil) // tries to redial in outOfSync @@ -244,8 +245,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { - return 1, syncThreshold + node.stateLatestBlockNumber + 1, utils.NewBigI(10) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { + return 1, syncThreshold + node.stateLatestBlockNumber + 1, big.NewInt(10) } rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() @@ -266,8 +267,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { }) defer func() { assert.NoError(t, node.close()) }() node.stateLatestBlockNumber = 20 - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { - return 1, node.stateLatestBlockNumber + 100, utils.NewBigI(10) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { + return 1, node.stateLatestBlockNumber + 100, big.NewInt(10) } rpc.On("ClientVersion", mock.Anything).Return("", nil) node.declareAlive() @@ -310,8 +311,8 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { rpc: rpc, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { - return 1, 20, utils.NewBigI(10) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { + return 1, 20, big.NewInt(10) } node.declareAlive() tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint detected out of sync; %s %s", msgCannotDisable, msgDegradedState)) @@ -353,7 +354,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { sub.On("Err").Return((<-chan error)(nil)) sub.On("Unsubscribe").Once() expectedBlockNumber := rand.Int64() - expectedDiff := utils.NewBigI(rand.Int64()) + expectedDiff := big.NewInt(rand.Int64()) rpc.On("Subscribe", mock.Anything, mock.Anything, rpcSubscriptionMethodNewHeads).Run(func(args mock.Arguments) { ch := args.Get(1).(chan<- Head) go writeHeads(t, ch, head{BlockNumber: expectedBlockNumber, BlockDifficulty: expectedDiff}) @@ -367,14 +368,14 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { node.declareAlive() tests.AssertEventually(t, func() bool { state, block, diff := node.StateAndLatest() - return state == nodeStateAlive && block == expectedBlockNumber == diff.Equal(expectedDiff) + return state == nodeStateAlive && block == expectedBlockNumber == bigmath.Equal(diff, expectedDiff) }) }) } type head struct { BlockNumber int64 - BlockDifficulty *utils.Big + BlockDifficulty *big.Int } func writeHeads(t *testing.T, ch chan<- Head, heads ...head) { @@ -411,7 +412,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { return node } - stubIsOutOfSync := func(num int64, td *utils.Big) bool { + stubIsOutOfSync := func(num int64, td *big.Int) bool { return false } @@ -448,7 +449,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { }).Return(outOfSyncSubscription, nil).Once() rpc.On("Dial", mock.Anything).Return(errors.New("failed to redial")).Maybe() - node.declareOutOfSync(func(num int64, td *utils.Big) bool { + node.declareOutOfSync(func(num int64, td *big.Int) bool { return true }) tests.AssertLogCountEventually(t, observedLogs, msgReceivedBlock, len(heads)) @@ -614,7 +615,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { setupRPCForAliveLoop(t, rpc) - node.declareOutOfSync(func(num int64, td *utils.Big) bool { + node.declareOutOfSync(func(num int64, td *big.Int) bool { return num < highestBlock }) tests.AssertLogEventually(t, observedLogs, msgReceivedBlock) @@ -635,8 +636,8 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { lggr: lggr, }) defer func() { assert.NoError(t, node.close()) }() - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { - return 0, 100, utils.NewBigI(200) + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { + return 0, 100, big.NewInt(200) } rpc.On("Dial", mock.Anything).Return(nil).Once() @@ -943,7 +944,7 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { }) t.Run("skip if syncThreshold is not configured", func(t *testing.T) { node := newTestNode(t, testNodeOpts{}) - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { return } outOfSync, liveNodes := node.syncStatus(0, nil) @@ -954,7 +955,7 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { node := newTestNode(t, testNodeOpts{ config: testNodeConfig{syncThreshold: 1}, }) - node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + node.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { return } assert.Panics(t, func() { @@ -1000,13 +1001,13 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { selectionMode: selectionMode, }, }) - node.nLiveNodes = func() (int, int64, *utils.Big) { - return nodesNum, highestBlock, utils.NewBigI(totalDifficulty) + node.nLiveNodes = func() (int, int64, *big.Int) { + return nodesNum, highestBlock, big.NewInt(totalDifficulty) } for _, td := range []int64{totalDifficulty - syncThreshold - 1, totalDifficulty - syncThreshold, totalDifficulty, totalDifficulty + 1} { for _, testCase := range testCases { t.Run(fmt.Sprintf("%s: selectionMode: %s: total difficulty: %d", testCase.name, selectionMode, td), func(t *testing.T) { - outOfSync, liveNodes := node.syncStatus(testCase.blockNumber, utils.NewBigI(td)) + outOfSync, liveNodes := node.syncStatus(testCase.blockNumber, big.NewInt(td)) assert.Equal(t, nodesNum, liveNodes) assert.Equal(t, testCase.outOfSync, outOfSync) }) @@ -1053,13 +1054,13 @@ func TestUnit_NodeLifecycle_syncStatus(t *testing.T) { selectionMode: NodeSelectionModeTotalDifficulty, }, }) - node.nLiveNodes = func() (int, int64, *utils.Big) { - return nodesNum, highestBlock, utils.NewBigI(totalDifficulty) + node.nLiveNodes = func() (int, int64, *big.Int) { + return nodesNum, highestBlock, big.NewInt(totalDifficulty) } for _, hb := range []int64{highestBlock - syncThreshold - 1, highestBlock - syncThreshold, highestBlock, highestBlock + 1} { for _, testCase := range testCases { t.Run(fmt.Sprintf("%s: selectionMode: %s: highest block: %d", testCase.name, NodeSelectionModeTotalDifficulty, hb), func(t *testing.T) { - outOfSync, liveNodes := node.syncStatus(hb, utils.NewBigI(testCase.totalDifficulty)) + outOfSync, liveNodes := node.syncStatus(hb, big.NewInt(testCase.totalDifficulty)) assert.Equal(t, nodesNum, liveNodes) assert.Equal(t, testCase.outOfSync, outOfSync) }) diff --git a/common/client/node_selector_total_difficulty.go b/common/client/node_selector_total_difficulty.go index 9b29642d033..35491503bcc 100644 --- a/common/client/node_selector_total_difficulty.go +++ b/common/client/node_selector_total_difficulty.go @@ -1,7 +1,7 @@ package client import ( - "github.com/smartcontractkit/chainlink/v2/core/utils" + "math/big" "github.com/smartcontractkit/chainlink/v2/common/types" ) @@ -22,7 +22,7 @@ func NewTotalDifficultyNodeSelector[ func (s totalDifficultyNodeSelector[CHAIN_ID, HEAD, RPC]) Select() Node[CHAIN_ID, HEAD, RPC] { // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil - var highestTD *utils.Big + var highestTD *big.Int var nodes []Node[CHAIN_ID, HEAD, RPC] var aliveNodes []Node[CHAIN_ID, HEAD, RPC] diff --git a/common/client/node_selector_total_difficulty_test.go b/common/client/node_selector_total_difficulty_test.go index 4eecb859db9..5c43cdd8472 100644 --- a/common/client/node_selector_total_difficulty_test.go +++ b/common/client/node_selector_total_difficulty_test.go @@ -1,10 +1,10 @@ package client import ( + big "math/big" "testing" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" ) @@ -27,10 +27,10 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) } else if i == 1 { // second node is alive - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(7)) + node.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(7)) } else { // third node is alive and best - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), utils.NewBigI(8)) + node.On("StateAndLatest").Return(nodeStateAlive, int64(2), big.NewInt(8)) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fourth node is alive (same as 3rd) - node.On("StateAndLatest").Return(nodeStateAlive, int64(2), utils.NewBigI(8)) + node.On("StateAndLatest").Return(nodeStateAlive, int64(2), big.NewInt(8)) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := newMockNode[types.ID, Head, nodeClient](t) // fifth node is alive (better than 3rd and 4th) - node.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(11)) + node.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(11)) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -88,7 +88,7 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { node.On("StateAndLatest").Return(nodeStateOutOfSync, int64(-1), nil) } else { // others are unreachable - node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), utils.NewBigI(7)) + node.On("StateAndLatest").Return(nodeStateUnreachable, int64(1), big.NewInt(7)) } nodes = append(nodes, node) } @@ -106,7 +106,7 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := newMockNode[types.ID, Head, nodeClient](t) - node.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(10)) + node.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(10)) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -117,15 +117,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td but different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(10)) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) node1.On("Order").Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(10)) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) node2.On("Order").Return(int32(1)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), utils.NewBigI(10)) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(3), big.NewInt(10)) node3.On("Order").Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -136,15 +136,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different td but same order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(10)) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(10)) node1.On("Order").Maybe().Return(int32(3)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(11)) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(11)) node2.On("Order").Maybe().Return(int32(3)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(12)) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(12)) node3.On("Order").Return(int32(3)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3} @@ -155,19 +155,19 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := newMockNode[types.ID, Head, nodeClient](t) - node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(100)) + node1.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(100)) node1.On("Order").Maybe().Return(int32(4)) node2 := newMockNode[types.ID, Head, nodeClient](t) - node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(110)) + node2.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(110)) node2.On("Order").Maybe().Return(int32(5)) node3 := newMockNode[types.ID, Head, nodeClient](t) - node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(110)) + node3.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(110)) node3.On("Order").Maybe().Return(int32(1)) node4 := newMockNode[types.ID, Head, nodeClient](t) - node4.On("StateAndLatest").Return(nodeStateAlive, int64(1), utils.NewBigI(105)) + node4.On("StateAndLatest").Return(nodeStateAlive, int64(1), big.NewInt(105)) node4.On("Order").Maybe().Return(int32(2)) nodes := []Node[types.ID, Head, nodeClient]{node1, node2, node3, node4} diff --git a/common/client/types.go b/common/client/types.go index 6d3a22cee77..32d4da98b50 100644 --- a/common/client/types.go +++ b/common/client/types.go @@ -7,7 +7,6 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/assets" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // RPC includes all the necessary methods for a multi-node client to interact directly with any RPC endpoint. @@ -51,7 +50,7 @@ type RPC[ //go:generate mockery --quiet --name Head --structname mockHead --filename "mock_head_test.go" --inpackage --case=underscore type Head interface { BlockNumber() int64 - BlockDifficulty() *utils.Big + BlockDifficulty() *big.Int } // NodeClient includes all the necessary RPC methods required by a node. diff --git a/common/fee/models.go b/common/fee/models.go index 1fcb55c5309..b843cc3f055 100644 --- a/common/fee/models.go +++ b/common/fee/models.go @@ -6,8 +6,8 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink/v2/common/chains/label" - bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) var ( diff --git a/common/headtracker/types/mocks/head.go b/common/headtracker/types/mocks/head.go index 1de1f78de8c..79c483c9978 100644 --- a/common/headtracker/types/mocks/head.go +++ b/common/headtracker/types/mocks/head.go @@ -3,13 +3,13 @@ package mocks import ( - time "time" + big "math/big" mock "github.com/stretchr/testify/mock" - types "github.com/smartcontractkit/chainlink/v2/common/types" + time "time" - utils "github.com/smartcontractkit/chainlink/v2/core/utils" + types "github.com/smartcontractkit/chainlink/v2/common/types" ) // Head is an autogenerated mock type for the Head type @@ -18,15 +18,15 @@ type Head[BLOCK_HASH types.Hashable, CHAIN_ID types.ID] struct { } // BlockDifficulty provides a mock function with given fields: -func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockDifficulty() *utils.Big { +func (_m *Head[BLOCK_HASH, CHAIN_ID]) BlockDifficulty() *big.Int { ret := _m.Called() - var r0 *utils.Big - if rf, ok := ret.Get(0).(func() *utils.Big); ok { + var r0 *big.Int + if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*utils.Big) + r0 = ret.Get(0).(*big.Int) } } diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index cff5746a9e0..ab620c51be3 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -247,7 +247,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) clos eb.initSync.Lock() defer eb.initSync.Unlock() if !eb.isStarted { - return errors.Wrap(utils.ErrAlreadyStopped, "Broadcaster is not started") + return errors.Wrap(services.ErrAlreadyStopped, "Broadcaster is not started") } close(eb.chStop) eb.wg.Wait() diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 3aac88e2f4b..0a50bb66385 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -401,15 +401,18 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() // be in an Unstarted state here, if execReset exited early. // // In this case, we don't care about stopping them since they are - // already "stopped", hence the usage of utils.EnsureClosed. - if err := utils.EnsureClosed(b.broadcaster); err != nil { - b.logger.Panicw(fmt.Sprintf("Failed to Close Broadcaster: %v", err), "err", err) + // already "stopped". + err := b.broadcaster.Close() + if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { + b.logger.Errorw(fmt.Sprintf("Failed to Close Broadcaster: %v", err), "err", err) } - if err := utils.EnsureClosed(b.confirmer); err != nil { - b.logger.Panicw(fmt.Sprintf("Failed to Close Confirmer: %v", err), "err", err) + err = b.confirmer.Close() + if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { + b.logger.Errorw(fmt.Sprintf("Failed to Close Confirmer: %v", err), "err", err) } - if err := utils.EnsureClosed(b.tracker); err != nil { - b.logger.Panicw(fmt.Sprintf("Failed to Close Tracker: %v", err), "err", err) + err = b.tracker.Close() + if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { + b.logger.Errorw(fmt.Sprintf("Failed to Close Tracker: %v", err), "err", err) } return case <-keysChanged: diff --git a/common/types/head.go b/common/types/head.go index 000bad2390e..c363fd5d0f2 100644 --- a/common/types/head.go +++ b/common/types/head.go @@ -1,9 +1,8 @@ package types import ( + "math/big" "time" - - "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Head provides access to a chain's head, as needed by the TxManager. @@ -36,5 +35,5 @@ type Head[BLOCK_HASH Hashable] interface { // Returns the total difficulty of the block. For chains who do not have a concept of block // difficulty, return 0. - BlockDifficulty() *utils.Big + BlockDifficulty() *big.Int } diff --git a/common/types/mocks/head.go b/common/types/mocks/head.go index 82fd910a08b..99d2a265b44 100644 --- a/common/types/mocks/head.go +++ b/common/types/mocks/head.go @@ -3,13 +3,12 @@ package mocks import ( + big "math/big" time "time" mock "github.com/stretchr/testify/mock" types "github.com/smartcontractkit/chainlink/v2/common/types" - - utils "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Head is an autogenerated mock type for the Head type @@ -18,15 +17,15 @@ type Head[BLOCK_HASH types.Hashable] struct { } // BlockDifficulty provides a mock function with given fields: -func (_m *Head[BLOCK_HASH]) BlockDifficulty() *utils.Big { +func (_m *Head[BLOCK_HASH]) BlockDifficulty() *big.Int { ret := _m.Called() - var r0 *utils.Big - if rf, ok := ret.Get(0).(func() *utils.Big); ok { + var r0 *big.Int + if rf, ok := ret.Get(0).(func() *big.Int); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*utils.Big) + r0 = ret.Get(0).(*big.Int) } } diff --git a/core/chains/evm/assets/wei.go b/core/chains/evm/assets/wei.go index 7d1240d384c..be0143b3a86 100644 --- a/core/chains/evm/assets/wei.go +++ b/core/chains/evm/assets/wei.go @@ -10,8 +10,8 @@ import ( "github.com/shopspring/decimal" "golang.org/x/exp/constraints" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink/v2/core/utils" - bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) const ( diff --git a/core/chains/evm/client/erroring_node.go b/core/chains/evm/client/erroring_node.go index 21c4d269ea4..c33891728a7 100644 --- a/core/chains/evm/client/erroring_node.go +++ b/core/chains/evm/client/erroring_node.go @@ -5,7 +5,6 @@ import ( "math/big" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -132,7 +131,7 @@ func (e *erroringNode) State() NodeState { return NodeStateUnreachable } -func (e *erroringNode) StateAndLatest() (NodeState, int64, *utils.Big) { +func (e *erroringNode) StateAndLatest() (NodeState, int64, *big.Int) { return NodeStateUnreachable, -1, nil } diff --git a/core/chains/evm/client/helpers_test.go b/core/chains/evm/client/helpers_test.go index dce825f4f71..27b335534da 100644 --- a/core/chains/evm/client/helpers_test.go +++ b/core/chains/evm/client/helpers_test.go @@ -14,7 +14,6 @@ import ( commonconfig "github.com/smartcontractkit/chainlink/v2/common/config" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) type TestNodePoolConfig struct { @@ -45,7 +44,7 @@ func NewClientWithTestNode(t *testing.T, nodePoolCfg config.NodePool, noNewHeads lggr := logger.Test(t) n := NewNode(nodePoolCfg, noNewHeadsThreshold, lggr, *parsed, rpcHTTPURL, "eth-primary-0", id, chainID, 1) - n.(*node).setLatestReceived(0, utils.NewBigI(0)) + n.(*node).setLatestReceived(0, big.NewInt(0)) primaries := []Node{n} var sendonlys []SendOnlyNode diff --git a/core/chains/evm/client/node.go b/core/chains/evm/client/node.go index e2da78b502a..a2c8b807ba2 100644 --- a/core/chains/evm/client/node.go +++ b/core/chains/evm/client/node.go @@ -24,7 +24,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -93,7 +92,7 @@ type Node interface { // State returns NodeState State() NodeState // StateAndLatest returns NodeState with the latest received block number & total difficulty. - StateAndLatest() (state NodeState, blockNum int64, totalDifficulty *utils.Big) + StateAndLatest() (state NodeState, blockNum int64, totalDifficulty *big.Int) // Name is a unique identifier for this node. Name() string ChainID() *big.Int @@ -153,7 +152,7 @@ type node struct { state NodeState // Each node is tracking the last received head number and total difficulty stateLatestBlockNumber int64 - stateLatestTotalDifficulty *utils.Big + stateLatestTotalDifficulty *big.Int // Need to track subscriptions because closing the RPC does not (always?) // close the underlying subscription @@ -177,7 +176,7 @@ type node struct { // 1. see how many live nodes there are in total, so we can prevent the last alive node in a pool from being // moved to out-of-sync state. It is better to have one out-of-sync node than no nodes at all. // 2. compare against the highest head (by number or difficulty) to ensure we don't fall behind too far. - nLiveNodes func() (count int, blockNumber int64, totalDifficulty *utils.Big) + nLiveNodes func() (count int, blockNumber int64, totalDifficulty *big.Int) } // NewNode returns a new *node as Node diff --git a/core/chains/evm/client/node_fsm.go b/core/chains/evm/client/node_fsm.go index 10694eb7fc4..c92af3b4120 100644 --- a/core/chains/evm/client/node_fsm.go +++ b/core/chains/evm/client/node_fsm.go @@ -2,11 +2,10 @@ package client import ( "fmt" + "math/big" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - - "github.com/smartcontractkit/chainlink/v2/core/utils" ) var ( @@ -112,7 +111,7 @@ func (n *node) State() NodeState { return n.state } -func (n *node) StateAndLatest() (NodeState, int64, *utils.Big) { +func (n *node) StateAndLatest() (NodeState, int64, *big.Int) { n.stateMu.RLock() defer n.stateMu.RUnlock() return n.state, n.stateLatestBlockNumber, n.stateLatestTotalDifficulty @@ -184,7 +183,7 @@ func (n *node) transitionToInSync(fn func()) { // declareOutOfSync puts a node into OutOfSync state, disconnecting all current // clients and making it unavailable for use until back in-sync. -func (n *node) declareOutOfSync(isOutOfSync func(num int64, td *utils.Big) bool) { +func (n *node) declareOutOfSync(isOutOfSync func(num int64, td *big.Int) bool) { n.transitionToOutOfSync(func() { n.lfcLog.Errorw("RPC Node is out of sync", "nodeState", n.state) n.wg.Add(1) diff --git a/core/chains/evm/client/node_lifecycle.go b/core/chains/evm/client/node_lifecycle.go index 11d03d97dd5..f838325a646 100644 --- a/core/chains/evm/client/node_lifecycle.go +++ b/core/chains/evm/client/node_lifecycle.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "math/big" "time" "github.com/pkg/errors" @@ -11,6 +12,8 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "github.com/smartcontractkit/chainlink-common/pkg/logger" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" + evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -50,7 +53,7 @@ func zombieNodeCheckInterval(noNewHeadsThreshold time.Duration) time.Duration { return utils.WithJitter(interval) } -func (n *node) setLatestReceived(blockNumber int64, totalDifficulty *utils.Big) { +func (n *node) setLatestReceived(blockNumber int64, totalDifficulty *big.Int) { n.stateMu.Lock() defer n.stateMu.Unlock() n.stateLatestBlockNumber = blockNumber @@ -215,13 +218,13 @@ func (n *node) aliveLoop() { continue } } - n.declareOutOfSync(func(num int64, td *utils.Big) bool { return num < highestReceivedBlockNumber }) + n.declareOutOfSync(func(num int64, td *big.Int) bool { return num < highestReceivedBlockNumber }) return } } } -func (n *node) isOutOfSync(num int64, td *utils.Big) (outOfSync bool) { +func (n *node) isOutOfSync(num int64, td *big.Int) (outOfSync bool) { outOfSync, _ = n.syncStatus(num, td) return } @@ -229,7 +232,7 @@ func (n *node) isOutOfSync(num int64, td *utils.Big) (outOfSync bool) { // syncStatus returns outOfSync true if num or td is more than SyncThresold behind the best node. // Always returns outOfSync false for SyncThreshold 0. // liveNodes is only included when outOfSync is true. -func (n *node) syncStatus(num int64, td *utils.Big) (outOfSync bool, liveNodes int) { +func (n *node) syncStatus(num int64, td *big.Int) (outOfSync bool, liveNodes int) { if n.nLiveNodes == nil { return // skip for tests } @@ -244,8 +247,8 @@ func (n *node) syncStatus(num int64, td *utils.Big) (outOfSync bool, liveNodes i case NodeSelectionMode_HighestHead, NodeSelectionMode_RoundRobin, NodeSelectionMode_PriorityLevel: return num < highest-int64(threshold), ln case NodeSelectionMode_TotalDifficulty: - bigThreshold := utils.NewBigI(int64(threshold)) - return td.Cmp(greatest.Sub(bigThreshold)) < 0, ln + bigThreshold := big.NewInt(int64(threshold)) + return td.Cmp(bigmath.Sub(greatest, bigThreshold)) < 0, ln default: panic("unrecognized NodeSelectionMode: " + mode) } @@ -257,7 +260,7 @@ const ( ) // outOfSyncLoop takes an OutOfSync node and waits until isOutOfSync returns false to go back to live status -func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *utils.Big) bool) { +func (n *node) outOfSyncLoop(isOutOfSync func(num int64, td *big.Int) bool) { defer n.wg.Done() { diff --git a/core/chains/evm/client/node_lifecycle_test.go b/core/chains/evm/client/node_lifecycle_test.go index 42f813cc2b5..f097c2bc078 100644 --- a/core/chains/evm/client/node_lifecycle_test.go +++ b/core/chains/evm/client/node_lifecycle_test.go @@ -16,7 +16,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/config" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/utils" ) func standardHandler(method string, _ gjson.Result) (resp testutils.JSONRPCResponse) { @@ -161,7 +160,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { } return }) - n.nLiveNodes = func() (int, int64, *utils.Big) { return 1, 0, nil } + n.nLiveNodes = func() (int, int64, *big.Int) { return 1, 0, nil } dial(t, n) defer func() { assert.NoError(t, n.Close()) }() @@ -308,7 +307,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { iN := NewNode(pollDisabledCfg, testutils.TestInterval, lggr, *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) - n.nLiveNodes = func() (int, int64, *utils.Big) { return 1, 0, nil } + n.nLiveNodes = func() (int, int64, *big.Int) { return 1, 0, nil } dial(t, n) defer func() { assert.NoError(t, n.Close()) }() @@ -353,7 +352,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { iN := NewNode(cfg, 0*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { return 2, highestHead.Load(), nil } @@ -415,7 +414,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { iN := NewNode(cfg, 0*time.Second, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { return 2, highestHead.Load(), nil } @@ -474,7 +473,7 @@ func TestUnit_NodeLifecycle_aliveLoop(t *testing.T) { iN := NewNode(cfg, 0*time.Second, lggr, *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { return 1, highestHead.Load(), nil } @@ -521,7 +520,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { n.wg.Add(1) go func() { defer close(ch) - n.outOfSyncLoop(func(num int64, td *utils.Big) bool { return false }) + n.outOfSyncLoop(func(num int64, td *big.Int) bool { return false }) }() assert.NoError(t, n.Close()) testutils.WaitWithTimeout(t, ch, "expected outOfSyncLoop to exit") @@ -536,7 +535,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { n.wg.Add(1) - n.outOfSyncLoop(func(num int64, td *utils.Big) bool { return num == 0 }) + n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num == 0 }) assert.Equal(t, NodeStateUnreachable, n.State()) }) @@ -565,7 +564,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, n.Close()) }() n.wg.Add(1) - go n.outOfSyncLoop(func(num int64, td *utils.Big) bool { return num == 0 }) + go n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num == 0 }) testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") @@ -612,7 +611,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { defer func() { assert.NoError(t, n.Close()) }() n.wg.Add(1) - go n.outOfSyncLoop(func(num int64, td *utils.Big) bool { return num < 43 }) + go n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num < 43 }) testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") @@ -661,7 +660,7 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { iN := NewNode(cfg, time.Second*0, lggr, *s.WSURL(), nil, "test node", 0, testutils.FixtureChainID, 1) n := iN.(*node) - n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *utils.Big) { + n.nLiveNodes = func() (count int, blockNumber int64, totalDifficulty *big.Int) { return 2, stall + int64(cfg.SyncThreshold()), nil } @@ -717,14 +716,14 @@ func TestUnit_NodeLifecycle_outOfSyncLoop(t *testing.T) { iN := NewNode(cfg, testutils.TestInterval, logger.Test(t), *s.WSURL(), nil, "test node", 42, testutils.FixtureChainID, 1) n := iN.(*node) - n.nLiveNodes = func() (int, int64, *utils.Big) { return 0, 0, nil } + n.nLiveNodes = func() (int, int64, *big.Int) { return 0, 0, nil } dial(t, n) n.setState(NodeStateOutOfSync) defer func() { assert.NoError(t, n.Close()) }() n.wg.Add(1) - go n.outOfSyncLoop(func(num int64, td *utils.Big) bool { return num == 0 }) + go n.outOfSyncLoop(func(num int64, td *big.Int) bool { return num == 0 }) testutils.WaitWithTimeout(t, chSubbed, "timed out waiting for initial subscription") diff --git a/core/chains/evm/client/node_selector_total_difficulty.go b/core/chains/evm/client/node_selector_total_difficulty.go index 99a1c89dd4f..27d888947d9 100644 --- a/core/chains/evm/client/node_selector_total_difficulty.go +++ b/core/chains/evm/client/node_selector_total_difficulty.go @@ -1,8 +1,6 @@ package client -import ( - "github.com/smartcontractkit/chainlink/v2/core/utils" -) +import "math/big" type totalDifficultyNodeSelector []Node @@ -13,7 +11,7 @@ func NewTotalDifficultyNodeSelector(nodes []Node) NodeSelector { func (s totalDifficultyNodeSelector) Select() Node { // NodeNoNewHeadsThreshold may not be enabled, in this case all nodes have td == nil - var highestTD *utils.Big + var highestTD *big.Int var nodes []Node var aliveNodes []Node diff --git a/core/chains/evm/client/node_selector_total_difficulty_test.go b/core/chains/evm/client/node_selector_total_difficulty_test.go index d42a7461dee..486a421477e 100644 --- a/core/chains/evm/client/node_selector_total_difficulty_test.go +++ b/core/chains/evm/client/node_selector_total_difficulty_test.go @@ -1,11 +1,11 @@ package client_test import ( + "math/big" "testing" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" - "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" ) @@ -27,10 +27,10 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { node.On("StateAndLatest").Return(evmclient.NodeStateOutOfSync, int64(-1), nil) } else if i == 1 { // second node is alive - node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(7)) + node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(7)) } else { // third node is alive and best - node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(2), utils.NewBigI(8)) + node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(2), big.NewInt(8)) } node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -42,7 +42,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("stick to the same node", func(t *testing.T) { node := evmmocks.NewNode(t) // fourth node is alive (same as 3rd) - node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(2), utils.NewBigI(8)) + node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(2), big.NewInt(8)) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -53,7 +53,7 @@ func TestTotalDifficultyNodeSelector(t *testing.T) { t.Run("another best node", func(t *testing.T) { node := evmmocks.NewNode(t) // fifth node is alive (better than 3rd and 4th) - node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), utils.NewBigI(11)) + node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), big.NewInt(11)) node.On("Order").Maybe().Return(int32(1)) nodes = append(nodes, node) @@ -87,7 +87,7 @@ func TestTotalDifficultyNodeSelector_None(t *testing.T) { node.On("StateAndLatest").Return(evmclient.NodeStateOutOfSync, int64(-1), nil) } else { // others are unreachable - node.On("StateAndLatest").Return(evmclient.NodeStateUnreachable, int64(1), utils.NewBigI(7)) + node.On("StateAndLatest").Return(evmclient.NodeStateUnreachable, int64(1), big.NewInt(7)) } nodes = append(nodes, node) } @@ -104,7 +104,7 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td and order", func(t *testing.T) { for i := 0; i < 3; i++ { node := evmmocks.NewNode(t) - node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(10)) + node.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(10)) node.On("Order").Return(int32(2)) nodes = append(nodes, node) } @@ -115,15 +115,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("same td but different order", func(t *testing.T) { node1 := evmmocks.NewNode(t) - node1.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), utils.NewBigI(10)) + node1.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), big.NewInt(10)) node1.On("Order").Return(int32(3)) node2 := evmmocks.NewNode(t) - node2.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), utils.NewBigI(10)) + node2.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), big.NewInt(10)) node2.On("Order").Return(int32(1)) node3 := evmmocks.NewNode(t) - node3.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), utils.NewBigI(10)) + node3.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(3), big.NewInt(10)) node3.On("Order").Return(int32(2)) nodes := []evmclient.Node{node1, node2, node3} @@ -134,15 +134,15 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different td but same order", func(t *testing.T) { node1 := evmmocks.NewNode(t) - node1.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(10)) + node1.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(10)) node1.On("Order").Maybe().Return(int32(3)) node2 := evmmocks.NewNode(t) - node2.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(11)) + node2.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(11)) node2.On("Order").Maybe().Return(int32(3)) node3 := evmmocks.NewNode(t) - node3.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(12)) + node3.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(12)) node3.On("Order").Return(int32(3)) nodes := []evmclient.Node{node1, node2, node3} @@ -153,19 +153,19 @@ func TestTotalDifficultyNodeSelectorWithOrder(t *testing.T) { t.Run("different head and different order", func(t *testing.T) { node1 := evmmocks.NewNode(t) - node1.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(100)) + node1.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(100)) node1.On("Order").Maybe().Return(int32(4)) node2 := evmmocks.NewNode(t) - node2.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(110)) + node2.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(110)) node2.On("Order").Maybe().Return(int32(5)) node3 := evmmocks.NewNode(t) - node3.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(110)) + node3.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(110)) node3.On("Order").Maybe().Return(int32(1)) node4 := evmmocks.NewNode(t) - node4.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), utils.NewBigI(105)) + node4.On("StateAndLatest").Return(evmclient.NodeStateAlive, int64(1), big.NewInt(105)) node4.On("Order").Maybe().Return(int32(2)) nodes := []evmclient.Node{node1, node2, node3, node4} diff --git a/core/chains/evm/client/pool.go b/core/chains/evm/client/pool.go index 9217f633dc3..6c36cc3e987 100644 --- a/core/chains/evm/client/pool.go +++ b/core/chains/evm/client/pool.go @@ -178,8 +178,8 @@ func (p *Pool) Dial(ctx context.Context) error { // nLiveNodes returns the number of currently alive nodes, as well as the highest block number and greatest total difficulty. // totalDifficulty will be 0 if all nodes return nil. -func (p *Pool) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *utils.Big) { - totalDifficulty = utils.NewBigI(0) +func (p *Pool) nLiveNodes() (nLiveNodes int, blockNumber int64, totalDifficulty *big.Int) { + totalDifficulty = big.NewInt(0) for _, n := range p.nodes { if s, num, td := n.StateAndLatest(); s == NodeStateAlive { nLiveNodes++ diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go index 10b2aae502a..293bf64badc 100644 --- a/core/chains/evm/client/simulated_backend_client.go +++ b/core/chains/evm/client/simulated_backend_client.go @@ -302,7 +302,7 @@ func (c *SimulatedBackendClient) SubscribeNewHead( case h := <-ch: var head *evmtypes.Head if h != nil { - head = &evmtypes.Head{Difficulty: (*utils.Big)(h.Difficulty), Timestamp: time.Unix(int64(h.Time), 0), Number: h.Number.Int64(), Hash: h.Hash(), ParentHash: h.ParentHash, Parent: lastHead, EVMChainID: utils.NewBig(c.chainId)} + head = &evmtypes.Head{Difficulty: h.Difficulty, Timestamp: time.Unix(int64(h.Time), 0), Number: h.Number.Int64(), Hash: h.Hash(), ParentHash: h.ParentHash, Parent: lastHead, EVMChainID: utils.NewBig(c.chainId)} lastHead = head } select { diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 4f9a5419702..c7476d58ba4 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink/v2/common/config" commonfee "github.com/smartcontractkit/chainlink/v2/common/fee" @@ -22,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas/rollups" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/label" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" - bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) // EvmFeeEstimator provides a unified interface that wraps EvmEstimator and can determine if legacy or dynamic fee estimation should be used diff --git a/core/chains/evm/mocks/node.go b/core/chains/evm/mocks/node.go index 85db2e0ca0f..69020d411f4 100644 --- a/core/chains/evm/mocks/node.go +++ b/core/chains/evm/mocks/node.go @@ -19,8 +19,6 @@ import ( rpc "github.com/ethereum/go-ethereum/rpc" types "github.com/ethereum/go-ethereum/core/types" - - utils "github.com/smartcontractkit/chainlink/v2/core/utils" ) // Node is an autogenerated mock type for the Node type @@ -519,13 +517,13 @@ func (_m *Node) State() client.NodeState { } // StateAndLatest provides a mock function with given fields: -func (_m *Node) StateAndLatest() (client.NodeState, int64, *utils.Big) { +func (_m *Node) StateAndLatest() (client.NodeState, int64, *big.Int) { ret := _m.Called() var r0 client.NodeState var r1 int64 - var r2 *utils.Big - if rf, ok := ret.Get(0).(func() (client.NodeState, int64, *utils.Big)); ok { + var r2 *big.Int + if rf, ok := ret.Get(0).(func() (client.NodeState, int64, *big.Int)); ok { return rf() } if rf, ok := ret.Get(0).(func() client.NodeState); ok { @@ -540,11 +538,11 @@ func (_m *Node) StateAndLatest() (client.NodeState, int64, *utils.Big) { r1 = ret.Get(1).(int64) } - if rf, ok := ret.Get(2).(func() *utils.Big); ok { + if rf, ok := ret.Get(2).(func() *big.Int); ok { r2 = rf() } else { if ret.Get(2) != nil { - r2 = ret.Get(2).(*utils.Big) + r2 = ret.Get(2).(*big.Int) } } diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go index f210934adb1..76dfcb9d51c 100644 --- a/core/chains/evm/txmgr/transmitchecker.go +++ b/core/chains/evm/txmgr/transmitchecker.go @@ -12,6 +12,8 @@ import ( "github.com/pkg/errors" "github.com/smartcontractkit/chainlink-common/pkg/logger" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" + "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -22,7 +24,6 @@ import ( v2 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2plus_interface" "github.com/smartcontractkit/chainlink/v2/core/utils" - bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) type ( diff --git a/core/chains/evm/types/models.go b/core/chains/evm/types/models.go index 83b90b4d539..314180a7e98 100644 --- a/core/chains/evm/types/models.go +++ b/core/chains/evm/types/models.go @@ -39,8 +39,8 @@ type Head struct { ReceiptsRoot common.Hash TransactionsRoot common.Hash StateRoot common.Hash - Difficulty *utils.Big - TotalDifficulty *utils.Big + Difficulty *big.Int + TotalDifficulty *big.Int } var _ commontypes.Head[common.Hash] = &Head{} @@ -80,7 +80,7 @@ func (h *Head) GetTimestamp() time.Time { return h.Timestamp } -func (h *Head) BlockDifficulty() *utils.Big { +func (h *Head) BlockDifficulty() *big.Int { return h.Difficulty } @@ -283,8 +283,8 @@ func (h *Head) UnmarshalJSON(bs []byte) error { h.ReceiptsRoot = jsonHead.ReceiptsRoot h.TransactionsRoot = jsonHead.TransactionsRoot h.StateRoot = jsonHead.StateRoot - h.Difficulty = utils.NewBig(jsonHead.Difficulty.ToInt()) - h.TotalDifficulty = utils.NewBig(jsonHead.TotalDifficulty.ToInt()) + h.Difficulty = jsonHead.Difficulty.ToInt() + h.TotalDifficulty = jsonHead.TotalDifficulty.ToInt() return nil } diff --git a/core/utils/big.go b/core/utils/big.go index 22cd8e64e55..69fab223de7 100644 --- a/core/utils/big.go +++ b/core/utils/big.go @@ -8,8 +8,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" + bigmath "github.com/smartcontractkit/chainlink-common/pkg/utils/big_math" "github.com/smartcontractkit/chainlink-common/pkg/utils/bytes" - bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) const base10 = 10 diff --git a/core/utils/utils.go b/core/utils/utils.go index 69597fb9e4a..0df280775b5 100644 --- a/core/utils/utils.go +++ b/core/utils/utils.go @@ -9,7 +9,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "math" "math/big" mrand "math/rand" @@ -771,16 +770,6 @@ var ( // Deprecated: use services.StateMachine type StartStopOnce = services.StateMachine -// EnsureClosed closes the io.Closer, returning nil if it was already -// closed or not started yet -func EnsureClosed(c io.Closer) error { - err := c.Close() - if errors.Is(err, ErrAlreadyStopped) || errors.Is(err, ErrCannotStopUnstarted) { - return nil - } - return err -} - // WithJitter adds +/- 10% to a duration func WithJitter(d time.Duration) time.Duration { // #nosec From c36340ca688ba991a2433171f870d6146eecc528 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 29 Nov 2023 14:18:50 -0600 Subject: [PATCH 225/271] core/chains/evm/txmgr: nil check errs before wrapping (#11412) --- core/chains/evm/txmgr/evm_tx_store.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index 51c9f98e884..e36ca611e16 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -567,7 +567,10 @@ func (o *evmTxStore) GetFatalTransactions(ctx context.Context) (txes []*Tx, err txes = make([]*Tx, len(dbEtxs)) dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) err = o.LoadTxesAttempts(txes, pg.WithParentCtx(ctx), pg.WithQueryer(tx)) - return fmt.Errorf("failed to load evm.tx_attempts: %w", err) + if err != nil { + return fmt.Errorf("failed to load evm.tx_attempts: %w", err) + } + return nil }, pg.OptReadOnlyTx()) return txes, nil @@ -1277,7 +1280,10 @@ func (o *evmTxStore) GetNonFatalTransactions(ctx context.Context, chainID *big.I txes = make([]*Tx, len(dbEtxs)) dbEthTxsToEvmEthTxPtrs(dbEtxs, txes) err = o.LoadTxesAttempts(txes, pg.WithParentCtx(ctx), pg.WithQueryer(tx)) - return fmt.Errorf("failed to load evm.txes: %w", err) + if err != nil { + return fmt.Errorf("failed to load evm.txes: %w", err) + } + return nil }, pg.OptReadOnlyTx()) return txes, nil @@ -1302,7 +1308,10 @@ func (o *evmTxStore) GetTxByID(ctx context.Context, id int64) (txe *Tx, err erro } txe = txes[0] err = o.LoadTxesAttempts(txes, pg.WithParentCtx(ctx), pg.WithQueryer(tx)) - return fmt.Errorf("failed to load evm.tx_attempts: %w", err) + if err != nil { + return fmt.Errorf("failed to load evm.tx_attempts: %w", err) + } + return nil }, pg.OptReadOnlyTx()) return txe, nil From 80054164e663b3f7418829bcae3b7401a678da87 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 29 Nov 2023 14:31:39 -0600 Subject: [PATCH 226/271] .github/workflows: notify slack for ci-core develop failues (#11408) --- .github/workflows/ci-core.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index e69fc64cc7b..fd8dfed88ab 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -29,7 +29,7 @@ jobs: gc-basic-auth: ${{ secrets.GRAFANA_CLOUD_BASIC_AUTH }} gc-host: ${{ secrets.GRAFANA_CLOUD_HOST }} - name: Notify Slack - if: ${{ failure() && github.event.schedule != '' }} + if: ${{ failure() && (github.event_name == 'merge_group' || github.event.branch == 'develop')}} uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} @@ -107,7 +107,7 @@ jobs: ./coverage.txt ./postgres_logs.txt - name: Notify Slack - if: ${{ failure() && matrix.cmd == 'go_core_race_tests' && github.event.schedule != '' }} + if: ${{ failure() && matrix.cmd == 'go_core_race_tests' && (github.event_name == 'merge_group' || github.event.branch == 'develop') }} uses: slackapi/slack-github-action@e28cf165c92ffef168d23c5c9000cffc8a25e117 # v1.24.0 env: SLACK_BOT_TOKEN: ${{ secrets.QA_SLACK_API_KEY }} From b8199c52e22c0c1a875713e0d01df21466a91567 Mon Sep 17 00:00:00 2001 From: Dimitris Grigoriou Date: Wed, 29 Nov 2023 23:54:32 +0200 Subject: [PATCH 227/271] Add PendingSequenceAt to fix txm test case (#11416) * Add PendingSequenceAt nonce * Fix return values --- core/chains/evm/txmgr/txmgr_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index f82bc991d52..524c85b108b 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -519,8 +519,8 @@ func TestTxm_Reset(t *testing.T) { cfg := evmtest.NewChainScopedConfig(t, gcfg) kst := cltest.NewKeyStore(t, db, cfg.Database()) - _, addr := cltest.RandomKey{Nonce: 5}.MustInsert(t, kst.Eth()) - _, addr2 := cltest.RandomKey{Nonce: 3}.MustInsert(t, kst.Eth()) + _, addr := cltest.RandomKey{}.MustInsert(t, kst.Eth()) + _, addr2 := cltest.RandomKey{}.MustInsert(t, kst.Eth()) txStore := cltest.NewTestTxStore(t, db, cfg.Database()) // 4 confirmed tx from addr1 for i := int64(0); i < 4; i++ { @@ -534,6 +534,8 @@ func TestTxm_Reset(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, nil) ethClient.On("BatchCallContextAll", mock.Anything, mock.Anything).Return(nil).Maybe() + ethClient.On("PendingSequenceAt", mock.Anything, addr).Return(128, nil).Maybe() + ethClient.On("PendingSequenceAt", mock.Anything, addr2).Return(44, nil).Maybe() estimator := gas.NewEstimator(logger.Test(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), cfg.Database(), cfg.Database().Listener(), kst.Eth()) From 7e83c3477860130531486c877b89ce857bbb05cb Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Wed, 29 Nov 2023 20:06:46 -0600 Subject: [PATCH 228/271] core/chains/evm/txmgr: fix TestTxm_Reset mocks (#11420) --- core/chains/evm/txmgr/broadcaster_test.go | 1 + core/chains/evm/txmgr/txmgr_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 93b1093e795..26e78344b93 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -161,6 +161,7 @@ func TestEthBroadcaster_LoadNextSequenceMapFailure_StartupSuccess(t *testing.T) // Instance starts without error even if loading next sequence map fails err := eb.Start(testutils.Context(t)) require.NoError(t, err) + t.Cleanup(func() { assert.NoError(t, eb.Close()) }) } func TestEthBroadcaster_ProcessUnstartedEthTxs_Success(t *testing.T) { diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 524c85b108b..745623ed77e 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -534,8 +534,8 @@ func TestTxm_Reset(t *testing.T) { ethClient := evmtest.NewEthClientMockWithDefaultChain(t) ethClient.On("HeadByNumber", mock.Anything, (*big.Int)(nil)).Return(nil, nil) ethClient.On("BatchCallContextAll", mock.Anything, mock.Anything).Return(nil).Maybe() - ethClient.On("PendingSequenceAt", mock.Anything, addr).Return(128, nil).Maybe() - ethClient.On("PendingSequenceAt", mock.Anything, addr2).Return(44, nil).Maybe() + ethClient.On("PendingNonceAt", mock.Anything, addr).Return(uint64(128), nil).Maybe() + ethClient.On("PendingNonceAt", mock.Anything, addr2).Return(uint64(44), nil).Maybe() estimator := gas.NewEstimator(logger.Test(t), ethClient, cfg.EVM(), cfg.EVM().GasEstimator()) txm, err := makeTestEvmTxm(t, db, ethClient, estimator, cfg.EVM(), cfg.EVM().GasEstimator(), cfg.EVM().Transactions(), cfg.Database(), cfg.Database().Listener(), kst.Eth()) From cee0df125ba105d97d2d3ba99d51ac0ce16fef86 Mon Sep 17 00:00:00 2001 From: Andrei Smirnov Date: Thu, 30 Nov 2023 10:43:12 +0300 Subject: [PATCH 229/271] Gateway client script: added readme and json pretty print (#11361) * Gateway client script: added readme and json pretty print * Reading S4 payload from the file --- core/scripts/gateway/client/README.md | 20 ++++++++++++++++++ core/scripts/gateway/client/send_request.go | 23 +++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 core/scripts/gateway/client/README.md diff --git a/core/scripts/gateway/client/README.md b/core/scripts/gateway/client/README.md new file mode 100644 index 00000000000..8c257753e6a --- /dev/null +++ b/core/scripts/gateway/client/README.md @@ -0,0 +1,20 @@ +# A gateway client script + +This script is used to connect to a gateway server and send commands to it. + +## Usage + +All requests have to be signed on behalf of a user, you need to provide your private key in .env file, e.g. + +``` +PRIVATE_KEY=1a2b3c... +``` + +The script will automatically sign the message using the provided private key. +Run the script without arguments to get the list of available commands. + +## Example + +``` +go run . -gateway_url https://01.functions-gateway.chain.link -don_id fun-avalanche-mainnet-2 -method secrets_list -message_id 123 +``` diff --git a/core/scripts/gateway/client/send_request.go b/core/scripts/gateway/client/send_request.go index 94ff6fb17da..8ab4e8bce79 100644 --- a/core/scripts/gateway/client/send_request.go +++ b/core/scripts/gateway/client/send_request.go @@ -28,7 +28,7 @@ func main() { s4SetSlotId := flag.Uint("s4_set_slot_id", 0, "S4 set slot ID") s4SetVersion := flag.Uint64("s4_set_version", 0, "S4 set version") s4SetExpirationPeriod := flag.Int64("s4_set_expiration_period", 60*60*1000, "S4 how long until the entry expires from now (in milliseconds)") - s4SetPayload := flag.String("s4_set_payload", "", "S4 set payload") + s4SetPayloadFile := flag.String("s4_set_payload_file", "", "S4 payload file to set secret") repeat := flag.Bool("repeat", false, "Repeat sending the request every 10 seconds") flag.Parse() @@ -50,6 +50,16 @@ func main() { } address := crypto.PubkeyToAddress(key.PublicKey) + var s4SetPayload []byte + if *methodName == functions.MethodSecretsSet { + var err error + s4SetPayload, err = os.ReadFile(*s4SetPayloadFile) + if err != nil { + fmt.Println("error reading S4 payload file", err) + return + } + } + // build payload (if relevant) var payloadJSON []byte if *methodName == functions.MethodSecretsSet { @@ -57,7 +67,7 @@ func main() { Address: address.Bytes(), SlotID: *s4SetSlotId, Version: *s4SetVersion, - Payload: []byte(*s4SetPayload), + Payload: s4SetPayload, Expiration: time.Now().UnixMilli() + *s4SetExpirationPeriod, } signature, err := envelope.Sign(key) @@ -70,7 +80,7 @@ func main() { SlotID: envelope.SlotID, Version: envelope.Version, Expiration: envelope.Expiration, - Payload: []byte(*s4SetPayload), + Payload: s4SetPayload, Signature: signature, } @@ -131,7 +141,12 @@ func main() { return } - fmt.Println(string(body)) + var prettyJSON bytes.Buffer + if err := json.Indent(&prettyJSON, body, "", " "); err != nil { + fmt.Println(string(body)) + } else { + fmt.Println(prettyJSON.String()) + } } sendRequest() From 0ae82791595dd02675f18cedb23e18cedee6fe5f Mon Sep 17 00:00:00 2001 From: Cedric Date: Thu, 30 Nov 2023 09:07:58 +0000 Subject: [PATCH 230/271] [BCF-2779] Better formatting of the generic plugin config (#11406) --- core/services/ocr2/delegate.go | 41 +++++++++++------- core/services/ocr2/validate/validate.go | 37 +++++++++++++--- core/services/ocr2/validate/validate_test.go | 44 +++++++++++++++++++- plugins/medianpoc/plugin.go | 14 +++++-- plugins/medianpoc/plugin_test.go | 2 +- 5 files changed, 109 insertions(+), 29 deletions(-) diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index eebc95903ed..af56c1e7a99 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -525,29 +525,33 @@ func (d *Delegate) newServicesGenericPlugin( ) (srvs []job.ServiceCtx, err error) { spec := jb.OCR2OracleSpec + // NOTE: we don't need to validate this config, since that happens as part of creating the job. + // See: validate/validate.go's `validateSpec`. p := validate.OCR2GenericPluginConfig{} err = json.Unmarshal(spec.PluginConfig.Bytes(), &p) if err != nil { return nil, err } - cconf := p.CoreConfig - command := cconf.Command + command := p.Command if command == "" { - command = defaultPathFromPluginName(cconf.PluginName) + command = defaultPathFromPluginName(p.PluginName) } - // NOTE: we don't need to validate this config, since that happens as part of creating the job. - // See: validate/validate.go's `validateSpec`. + // Add the default pipeline to the pluginConfig + p.Pipelines = append( + p.Pipelines, + validate.PipelineSpec{Name: "__DEFAULT_PIPELINE__", Spec: jb.Pipeline.Source}, + ) rid, err := spec.RelayID() if err != nil { - return nil, ErrJobSpecNoRelayer{PluginName: cconf.PluginName, Err: err} + return nil, ErrJobSpecNoRelayer{PluginName: p.PluginName, Err: err} } relayer, err := d.RelayGetter.Get(rid) if err != nil { - return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: p.CoreConfig.PluginName} + return nil, ErrRelayNotEnabled{Err: err, Relay: spec.Relay, PluginName: p.PluginName} } provider, err := relayer.NewPluginProvider(ctx, types.RelayArgs{ @@ -556,7 +560,7 @@ func (d *Delegate) newServicesGenericPlugin( ContractID: spec.ContractID, New: d.isNewlyCreatedJob, RelayConfig: spec.RelayConfig.Bytes(), - ProviderType: cconf.ProviderType, + ProviderType: p.ProviderType, }, types.PluginArgs{ TransmitterID: spec.TransmitterID.String, PluginConfig: spec.PluginConfig.Bytes(), @@ -570,7 +574,7 @@ func (d *Delegate) newServicesGenericPlugin( rid.Network, rid.ChainID, spec.ContractID, - synchronization.TelemetryType(cconf.TelemetryType), + synchronization.TelemetryType(p.TelemetryType), ) oracleArgs := libocr2.OCR2OracleArgs{ BinaryNetworkEndpointFactory: d.peerWrapper.Peer2, @@ -586,8 +590,8 @@ func (d *Delegate) newServicesGenericPlugin( OffchainConfigDigester: provider.OffchainConfigDigester(), } - pluginLggr := lggr.Named(cconf.PluginName).Named(spec.ContractID).Named(spec.GetID()) - cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(fmt.Sprintf("%s-%s-%s", cconf.PluginName, spec.ContractID, spec.GetID()), command) + pluginLggr := lggr.Named(p.PluginName).Named(spec.ContractID).Named(spec.GetID()) + cmdFn, grpcOpts, err := d.cfg.RegisterLOOP(fmt.Sprintf("%s-%s-%s", p.PluginName, spec.ContractID, spec.GetID()), command) if err != nil { return nil, fmt.Errorf("failed to register loop: %w", err) } @@ -604,7 +608,7 @@ func (d *Delegate) newServicesGenericPlugin( //TODO: remove this workaround when the EVM relayer is running inside of an LOOPP d.lggr.Info("provider is not a LOOPP provider, switching to provider server") - ps, err2 := relay.NewProviderServer(provider, types.OCR2PluginType(cconf.ProviderType), d.lggr) + ps, err2 := relay.NewProviderServer(provider, types.OCR2PluginType(p.ProviderType), d.lggr) if err2 != nil { return nil, fmt.Errorf("cannot start EVM provider server: %s", err) } @@ -615,12 +619,17 @@ func (d *Delegate) newServicesGenericPlugin( srvs = append(srvs, ps) } + pc, err := json.Marshal(p.Config) + if err != nil { + return nil, fmt.Errorf("cannot dump plugin config to string before sending to plugin: %s", err) + } + pluginConfig := types.ReportingPluginServiceConfig{ - PluginName: cconf.PluginName, + PluginName: p.PluginName, Command: command, - ProviderType: cconf.ProviderType, - TelemetryType: cconf.TelemetryType, - PluginConfig: p.PluginConfig, + ProviderType: p.ProviderType, + TelemetryType: p.TelemetryType, + PluginConfig: string(pc), } pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner) diff --git a/core/services/ocr2/validate/validate.go b/core/services/ocr2/validate/validate.go index c97d23dca09..bb9bb03a8ac 100644 --- a/core/services/ocr2/validate/validate.go +++ b/core/services/ocr2/validate/validate.go @@ -125,16 +125,42 @@ func validateSpec(tree *toml.Tree, spec job.Job) error { return nil } -type coreConfig struct { +type PipelineSpec struct { + Name string `json:"name"` + Spec string `json:"spec"` +} + +type Config struct { + Pipelines []PipelineSpec `json:"pipelines"` + PluginConfig map[string]any `json:"pluginConfig"` +} + +type innerConfig struct { Command string `json:"command"` ProviderType string `json:"providerType"` PluginName string `json:"pluginName"` TelemetryType string `json:"telemetryType"` + Config } type OCR2GenericPluginConfig struct { - CoreConfig coreConfig `json:"coreConfig"` - PluginConfig string + innerConfig +} + +func (o *OCR2GenericPluginConfig) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &o.innerConfig) + if err != nil { + return nil + } + + m := map[string]any{} + err = json.Unmarshal(data, &m) + if err != nil { + return err + } + + o.PluginConfig = m + return nil } func validateOCR2GenericPluginSpec(jsonConfig job.JSONConfig) error { @@ -144,12 +170,11 @@ func validateOCR2GenericPluginSpec(jsonConfig job.JSONConfig) error { return err } - cc := p.CoreConfig - if cc.PluginName == "" { + if p.PluginName == "" { return errors.New("generic config invalid: must provide plugin name") } - if cc.TelemetryType == "" { + if p.TelemetryType == "" { return errors.New("generic config invalid: must provide telemetry type") } diff --git a/core/services/ocr2/validate/validate_test.go b/core/services/ocr2/validate/validate_test.go index 5b40224a4bf..b03f08f6b08 100644 --- a/core/services/ocr2/validate/validate_test.go +++ b/core/services/ocr2/validate/validate_test.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/manyminds/api2go/jsonapi" + "github.com/pelletier/go-toml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -627,7 +628,7 @@ transmitterID = "0x74103Cf8b436465870b26aa9Fa2F62AD62b22E35" [relayConfig] chainID = 4 -[pluginConfig.coreConfig] +[pluginConfig] pluginName = "median" `, assertion: func(t *testing.T, os job.Job, err error) { @@ -655,7 +656,7 @@ transmitterID = "0x74103Cf8b436465870b26aa9Fa2F62AD62b22E35" [relayConfig] chainID = 4 -[pluginConfig.coreConfig] +[pluginConfig] pluginName = "median" telemetryType = "median" `, @@ -678,3 +679,42 @@ telemetryType = "median" }) } } + +type envelope struct { + PluginConfig *validate.OCR2GenericPluginConfig +} + +func TestOCR2GenericPluginConfig_Unmarshal(t *testing.T) { + payload := ` +[pluginConfig] +pluginName = "median" +telemetryType = "median" +foo = "bar" + +[[pluginConfig.pipelines]] +name = "default" +spec = "a spec" +` + tree, err := toml.Load(payload) + require.NoError(t, err) + + // Load the toml how we load it in the plugin, i.e. convert to + // map[string]any first, then treat as JSON + o := map[string]any{} + err = tree.Unmarshal(&o) + require.NoError(t, err) + + b, err := json.Marshal(o) + require.NoError(t, err) + + e := &envelope{} + err = json.Unmarshal(b, e) + require.NoError(t, err) + + pc := e.PluginConfig + assert.Equal(t, "bar", pc.PluginConfig["foo"]) + assert.Len(t, pc.Pipelines, 1) + assert.Equal(t, validate.PipelineSpec{Name: "default", Spec: "a spec"}, pc.Pipelines[0]) + assert.Equal(t, "median", pc.PluginName) + assert.Equal(t, "median", pc.TelemetryType) +} diff --git a/plugins/medianpoc/plugin.go b/plugins/medianpoc/plugin.go index af4ec41ab8f..fdf409b588f 100644 --- a/plugins/medianpoc/plugin.go +++ b/plugins/medianpoc/plugin.go @@ -30,8 +30,13 @@ type Plugin struct { reportingplugins.MedianProviderServer } +type pipelineSpec struct { + Name string `json:"name"` + Spec string `json:"spec"` +} + type jsonConfig struct { - Pipelines map[string]string `json:"pipelines"` + Pipelines []pipelineSpec `json:"pipelines"` } func (j jsonConfig) defaultPipeline() (string, error) { @@ -39,9 +44,10 @@ func (j jsonConfig) defaultPipeline() (string, error) { } func (j jsonConfig) getPipeline(key string) (string, error) { - v, ok := j.Pipelines[key] - if ok { - return v, nil + for _, v := range j.Pipelines { + if v.Name == key { + return v.Spec, nil + } } return "", fmt.Errorf("no pipeline found for %s", key) } diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index bc6af7ae5d3..0d6c0360e43 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -80,7 +80,7 @@ func TestNewPlugin(t *testing.T) { juelsPerFeeCoinSpec := "jpfc-spec" config := types.ReportingPluginServiceConfig{ PluginConfig: fmt.Sprintf( - `{"pipelines": {"__DEFAULT_PIPELINE__": "%s", "juelsPerFeeCoinPipeline": "%s"}}`, + `{"pipelines": [{"name": "__DEFAULT_PIPELINE__", "spec": "%s"},{"name": "juelsPerFeeCoinPipeline", "spec": "%s"}]}`, defaultSpec, juelsPerFeeCoinSpec, ), From 3a58f94dc6586df60eae5754d457e1f712180501 Mon Sep 17 00:00:00 2001 From: Sri Kidambi <1702865+kidambisrinivas@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:50:01 +0000 Subject: [PATCH 231/271] Add VRFOwner contract support to VRF V2 Super Script (#11414) * Deploy VRFOwner contract and transfer ownership of coordinator * Minor changes * Execute vrfOwner.SetAuthorizedSenders for all sending keys --------- Co-authored-by: Ilja Pavlovs --- core/scripts/common/arbitrum.go | 7 ++- .../vrfv2/testnet/v2scripts/super_scripts.go | 55 +++++++++++++++++-- core/scripts/vrfv2/testnet/v2scripts/util.go | 1 - 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/core/scripts/common/arbitrum.go b/core/scripts/common/arbitrum.go index a3a2a24fcd8..251f9b7609c 100644 --- a/core/scripts/common/arbitrum.go +++ b/core/scripts/common/arbitrum.go @@ -1,12 +1,13 @@ package common const ( - ArbitrumGoerliChainID int64 = 421613 - ArbitrumOneChainID int64 = 42161 + ArbitrumGoerliChainID int64 = 421613 + ArbitrumOneChainID int64 = 42161 + ArbitrumSepoliaChainID int64 = 421614 ) // IsArbitrumChainID returns true if and only if the given chain ID corresponds // to an Arbitrum chain (testnet or mainnet). func IsArbitrumChainID(chainID int64) bool { - return chainID == ArbitrumGoerliChainID || chainID == ArbitrumOneChainID + return chainID == ArbitrumGoerliChainID || chainID == ArbitrumOneChainID || chainID == ArbitrumSepoliaChainID } diff --git a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go index b623ae63084..4a1c1fcec41 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go +++ b/core/scripts/vrfv2/testnet/v2scripts/super_scripts.go @@ -19,9 +19,11 @@ import ( "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/model" "github.com/smartcontractkit/chainlink/core/scripts/common/vrf/util" + evmtypes "github.com/ethereum/go-ethereum/core/types" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/link_token_interface" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_coordinator_v2" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/vrf_owner" "github.com/smartcontractkit/chainlink/v2/core/services/signatures/secp256k1" ) @@ -50,6 +52,8 @@ func DeployUniverseViaCLI(e helpers.Environment) { batchFulfillmentEnabled := deployCmd.Bool("batch-fulfillment-enabled", constants.BatchFulfillmentEnabled, "whether send randomness fulfillments in batches inside one tx from CL node") + deployVRFOwner := deployCmd.Bool("deploy-vrf-owner", false, "whether to deploy VRF owner contracts") + // optional flags fallbackWeiPerUnitLinkString := deployCmd.String("fallback-wei-per-unit-link", constants.FallbackWeiPerUnitLink.String(), "fallback wei/link ratio") registerVRFKeyUncompressedPubKey := deployCmd.String("uncompressed-pub-key", "", "uncompressed public key") @@ -98,10 +102,7 @@ func DeployUniverseViaCLI(e helpers.Environment) { nodesMap := make(map[string]model.Node) - fundingAmount, ok := new(big.Int).SetString(*nodeSendingKeyFundingAmount, 10) - if !ok { - panic(fmt.Sprintf("failed to parse node sending key funding amount '%s'", *nodeSendingKeyFundingAmount)) - } + fundingAmount := decimal.RequireFromString(*nodeSendingKeyFundingAmount).BigInt() nodesMap[model.VRFPrimaryNodeName] = model.Node{ SendingKeys: util.MapToSendingKeyArr(vrfPrimaryNodeSendingKeys), SendingKeyFundingAmount: fundingAmount, @@ -143,6 +144,7 @@ func DeployUniverseViaCLI(e helpers.Environment) { coordinatorConfig, *batchFulfillmentEnabled, nodesMap, + *deployVRFOwner, ) vrfPrimaryNode := nodesMap[model.VRFPrimaryNodeName] @@ -160,6 +162,7 @@ func VRFV2DeployUniverse( coordinatorConfig CoordinatorConfigV2, batchFulfillmentEnabled bool, nodesMap map[string]model.Node, + deployVRFOwner bool, ) model.JobSpecs { var compressedPkHex string var keyHash common.Hash @@ -216,6 +219,15 @@ func VRFV2DeployUniverse( coordinator, err := vrf_coordinator_v2.NewVRFCoordinatorV2(contractAddresses.CoordinatorAddress, e.Ec) helpers.PanicErr(err) + var vrfOwnerAddress common.Address + if deployVRFOwner { + var tx *evmtypes.Transaction + fmt.Printf("\nDeploying VRF Owner for coordinator %v\n", contractAddresses.CoordinatorAddress) + vrfOwnerAddress, tx, _, err = vrf_owner.DeployVRFOwner(e.Owner, e.Ec, contractAddresses.CoordinatorAddress) + helpers.PanicErr(err) + helpers.ConfirmContractDeployed(context.Background(), e.Ec, tx, e.ChainID) + } + if contractAddresses.BatchCoordinatorAddress.String() == "0x0000000000000000000000000000000000000000" { fmt.Println("\nDeploying Batch Coordinator...") contractAddresses.BatchCoordinatorAddress = DeployBatchCoordinatorV2(e, contractAddresses.CoordinatorAddress) @@ -273,6 +285,28 @@ func VRFV2DeployUniverse( helpers.PanicErr(err) fmt.Printf("Subscription %+v\n", s) + if deployVRFOwner { + // VRF Owner + vrfOwner, err := vrf_owner.NewVRFOwner(vrfOwnerAddress, e.Ec) + helpers.PanicErr(err) + var authorizedSendersSlice []common.Address + for _, s := range nodesMap[model.VRFPrimaryNodeName].SendingKeys { + authorizedSendersSlice = append(authorizedSendersSlice, common.HexToAddress(s.Address)) + } + fmt.Printf("\nSetting authorised senders for VRF Owner: %v, Authorised senders %v\n", vrfOwnerAddress.String(), authorizedSendersSlice) + tx, err := vrfOwner.SetAuthorizedSenders(e.Owner, authorizedSendersSlice) + helpers.PanicErr(err) + helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "vrf owner set authorized senders") + fmt.Printf("\nTransfering ownership of coordinator: %v, VRF Owner %v\n", contractAddresses.CoordinatorAddress, vrfOwnerAddress.String()) + tx, err = coordinator.TransferOwnership(e.Owner, vrfOwnerAddress) + helpers.PanicErr(err) + helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "transfer ownership to", vrfOwnerAddress.String()) + fmt.Printf("\nAccepting ownership of coordinator: %v, VRF Owner %v\n", contractAddresses.CoordinatorAddress, vrfOwnerAddress.String()) + tx, err = vrfOwner.AcceptVRFOwnership(e.Owner) + helpers.PanicErr(err) + helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, "vrf owner accepting vrf ownership") + } + formattedVrfPrimaryJobSpec := fmt.Sprintf( jobs.VRFV2JobFormatted, contractAddresses.CoordinatorAddress, //coordinatorAddress @@ -293,6 +327,12 @@ func VRFV2DeployUniverse( contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, ) + if deployVRFOwner { + formattedVrfPrimaryJobSpec = strings.Replace(formattedVrfPrimaryJobSpec, + "minIncomingConfirmations", + fmt.Sprintf("vrfOwnerAddress = \"%s\"\nminIncomingConfirmations", vrfOwnerAddress.Hex()), + 1) + } formattedVrfBackupJobSpec := fmt.Sprintf( jobs.VRFV2JobFormatted, @@ -314,6 +354,12 @@ func VRFV2DeployUniverse( contractAddresses.CoordinatorAddress, contractAddresses.CoordinatorAddress, ) + if deployVRFOwner { + formattedVrfBackupJobSpec = strings.Replace(formattedVrfBackupJobSpec, + "minIncomingConfirmations", + fmt.Sprintf("vrfOwnerAddress = \"%s\"\nminIncomingConfirmations", vrfOwnerAddress.Hex()), + 1) + } formattedBHSJobSpec := fmt.Sprintf( jobs.BHSJobFormatted, @@ -353,6 +399,7 @@ func VRFV2DeployUniverse( "\nVRF Coordinator Address:", contractAddresses.CoordinatorAddress, "\nBatch VRF Coordinator Address:", contractAddresses.BatchCoordinatorAddress, "\nVRF Consumer Address:", consumerAddress, + "\nVRF Owner Address:", vrfOwnerAddress, "\nVRF Subscription Id:", subID, "\nVRF Subscription Balance:", *subscriptionBalanceJuels, "\nPossible VRF Request command: ", diff --git a/core/scripts/vrfv2/testnet/v2scripts/util.go b/core/scripts/vrfv2/testnet/v2scripts/util.go index 0e348e9c01c..94e381378bb 100644 --- a/core/scripts/vrfv2/testnet/v2scripts/util.go +++ b/core/scripts/vrfv2/testnet/v2scripts/util.go @@ -89,7 +89,6 @@ func EoaFundSubscription(e helpers.Environment, coordinator vrf_coordinator_v2.V fmt.Println("Initial account balance:", bal, e.Owner.From.String(), "Funding amount:", amount.String()) b, err := utils.ABIEncode(`[{"type":"uint64"}]`, subID) helpers.PanicErr(err) - e.Owner.GasLimit = 500000 tx, err := linkToken.TransferAndCall(e.Owner, coordinator.Address(), amount, b) helpers.PanicErr(err) helpers.ConfirmTXMined(context.Background(), e.Ec, tx, e.ChainID, fmt.Sprintf("sub ID: %d", subID)) From 44659b1e8a0ad2b221f60f3934e147fe428a640c Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Thu, 30 Nov 2023 15:52:20 +0300 Subject: [PATCH 232/271] RE-2081 All core metrics dashboard for CRIB (#11396) * complete core metrics * all core metrics * go mod tidy * revert go mod * extend wasp dashboard for load tests * run OCRv1 on devspace (#11417) * run OCRv1 on devspace * fix go.mod * fix goimports * remove external charts * dynamic nodes connection in config * try the same replaces from integration-tests * fix go.sum --- charts/chainlink-cluster/README.md | 19 +- charts/chainlink-cluster/connect.toml | 12 + .../dashboard/cmd/dashboard_deploy.go | 13 +- .../chainlink-cluster/dashboard/dashboard.go | 672 +++++- charts/chainlink-cluster/devspace.yaml | 8 +- charts/chainlink-cluster/go.mod | 177 +- charts/chainlink-cluster/go.sum | 2116 ++++++++++++++++- .../templates/chainlink-cm.yaml | 12 +- .../templates/chainlink-service.yaml | 2 +- .../templates/mockserver-service.yaml | 14 + .../templates/mockserver.yaml | 3 +- charts/chainlink-cluster/values-raw-helm.yaml | 19 +- integration-tests/go.mod | 2 +- integration-tests/k8s/connect.go | 103 + integration-tests/load/ocr/README.md | 28 + integration-tests/load/ocr/config.go | 72 + integration-tests/load/ocr/config.toml | 20 + integration-tests/load/ocr/gun.go | 55 + integration-tests/load/ocr/helper.go | 68 + integration-tests/load/ocr/ocr_test.go | 71 + integration-tests/load/ocr/vu.go | 128 + 21 files changed, 3555 insertions(+), 59 deletions(-) create mode 100644 charts/chainlink-cluster/connect.toml create mode 100644 charts/chainlink-cluster/templates/mockserver-service.yaml create mode 100644 integration-tests/k8s/connect.go create mode 100644 integration-tests/load/ocr/README.md create mode 100644 integration-tests/load/ocr/config.go create mode 100644 integration-tests/load/ocr/config.toml create mode 100644 integration-tests/load/ocr/gun.go create mode 100644 integration-tests/load/ocr/helper.go create mode 100644 integration-tests/load/ocr/ocr_test.go create mode 100644 integration-tests/load/ocr/vu.go diff --git a/charts/chainlink-cluster/README.md b/charts/chainlink-cluster/README.md index 46c337dc724..e3cec129a91 100644 --- a/charts/chainlink-cluster/README.md +++ b/charts/chainlink-cluster/README.md @@ -55,10 +55,8 @@ Destroy the cluster devspace purge ``` -If you need to run some system level tests inside k8s use `runner` profile: -``` -devspace dev -p runner -``` +## Running load tests +Check this [doc](../../integration-tests/load/ocr/README.md) If you used `devspace dev ...` always use `devspace reset pods` to switch the pods back @@ -66,8 +64,6 @@ If you used `devspace dev ...` always use `devspace reset pods` to switch the po If you need to debug CL node that is already deployed change `dev.app.container` and `dev.app.labelSelector` in [devspace.yaml](devspace.yaml) if they are not default and run: ``` devspace dev -p node -or -devspace dev -p runner ``` ## Automatic file sync @@ -85,7 +81,7 @@ helm install -f values-raw-helm.yaml cl-cluster . ``` Forward all apps (in another terminal) ``` -sudo kubefwd svc +sudo kubefwd svc -n cl-cluster ``` Then you can connect and run your tests @@ -126,14 +122,9 @@ export GRAFANA_URL=... export GRAFANA_TOKEN=... export LOKI_DATA_SOURCE_NAME=Loki export PROMETHEUS_DATA_SOURCE_NAME=Thanos -export DASHBOARD_FOLDER=CLClusterEphemeralDevspace +export DASHBOARD_FOLDER=CRIB export DASHBOARD_NAME=ChainlinkCluster cd dashboard/cmd && go run dashboard_deploy.go ``` -Open Grafana folder `CLClusterEphemeralDevspace` and find dashboard `ChainlinkCluster` - -If you'd like to add more metrics or verify that all of them are added you can have the full list using IDE search or `ripgrep`: -``` -rg -U ".*promauto.*\n.*Name: \"(.*)\"" ../.. > metrics.txt -``` \ No newline at end of file +Open Grafana folder `CRIB` and find dashboard `ChainlinkCluster` \ No newline at end of file diff --git a/charts/chainlink-cluster/connect.toml b/charts/chainlink-cluster/connect.toml new file mode 100644 index 00000000000..f0a74d4c144 --- /dev/null +++ b/charts/chainlink-cluster/connect.toml @@ -0,0 +1,12 @@ +namespace = "cl-cluster" +network_name = "geth" +network_chain_id = 1337 +network_private_key = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +network_ws_url = "ws://geth:8546" +network_http_url = "http://geth:8544" +cl_nodes_num = 6 +cl_node_url_template = "http://app-node-%d:6688" +cl_node_internal_dns_record_template = "app-node-%d" +cl_node_user = "notreal@fakeemail.ch" +cl_node_password = "fj293fbBnlQ!f9vNs" +mockserver_url = "http://app-mockserver:1080" \ No newline at end of file diff --git a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go index 170ffa02883..93619fe6148 100644 --- a/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go +++ b/charts/chainlink-cluster/dashboard/cmd/dashboard_deploy.go @@ -1,14 +1,13 @@ package main import ( - "context" "os" "github.com/smartcontractkit/chainlink/v2/dashboard/dashboard" + "github.com/smartcontractkit/wasp" ) func main() { - ctx := context.Background() name := os.Getenv("DASHBOARD_NAME") if name == "" { panic("DASHBOARD_NAME must be provided") @@ -17,6 +16,7 @@ func main() { if ldsn == "" { panic("DATA_SOURCE_NAME must be provided") } + os.Setenv("DATA_SOURCE_NAME", ldsn) pdsn := os.Getenv("PROMETHEUS_DATA_SOURCE_NAME") if ldsn == "" { panic("DATA_SOURCE_NAME must be provided") @@ -34,11 +34,16 @@ func main() { panic("GRAFANA_TOKEN must be provided") } // if you'll use this dashboard base in other projects, you can add your own opts here to extend it - db, err := dashboard.NewCLClusterDashboard(name, ldsn, pdsn, dbf, grafanaURL, grafanaToken, nil) + db, err := dashboard.NewCLClusterDashboard(6, name, ldsn, pdsn, dbf, grafanaURL, grafanaToken, nil) if err != nil { panic(err) } - if err := db.Deploy(ctx); err != nil { + // here we are extending load testing dashboard with core metrics, for example + wdb, err := wasp.NewDashboard(nil, db.Opts()) + if err != nil { + panic(err) + } + if _, err := wdb.Deploy(); err != nil { panic(err) } } diff --git a/charts/chainlink-cluster/dashboard/dashboard.go b/charts/chainlink-cluster/dashboard/dashboard.go index 293cded2b0c..b29140c0405 100644 --- a/charts/chainlink-cluster/dashboard/dashboard.go +++ b/charts/chainlink-cluster/dashboard/dashboard.go @@ -13,11 +13,178 @@ import ( "github.com/K-Phoen/grabana/target/prometheus" "github.com/K-Phoen/grabana/timeseries" "github.com/K-Phoen/grabana/timeseries/axis" - "github.com/K-Phoen/grabana/variable/interval" "github.com/K-Phoen/grabana/variable/query" "github.com/pkg/errors" ) +/* +Use ripgrep to get the full list +rg -oU ".*promauto.*\n.*Name: \"(.*)\"" -r '$1' > metrics.txt + +duplicates? + +common/client/node.go:pool_rpc_node_verifies +common/client/node.go:pool_rpc_node_verifies_failed +common/client/node.go:pool_rpc_node_verifies_success +common/client/node_fsm.go:pool_rpc_node_num_transitions_to_alive +common/client/node_fsm.go:pool_rpc_node_num_transitions_to_in_sync +common/client/node_fsm.go:pool_rpc_node_num_transitions_to_out_of_sync +common/client/node_fsm.go:pool_rpc_node_num_transitions_to_unreachable +common/client/node_fsm.go:pool_rpc_node_num_transitions_to_invalid_chain_id +common/client/node_fsm.go:pool_rpc_node_num_transitions_to_unusable +common/client/node_lifecycle.go:pool_rpc_node_highest_seen_block +common/client/node_lifecycle.go:pool_rpc_node_num_seen_blocks +common/client/node_lifecycle.go:pool_rpc_node_polls_total +common/client/node_lifecycle.go:pool_rpc_node_polls_failed +common/client/node_lifecycle.go:pool_rpc_node_polls_success + +covered + +core/logger/prometheus.go:log_warn_count +core/logger/prometheus.go:log_error_count +core/logger/prometheus.go:log_critical_count +core/logger/prometheus.go:log_panic_count +core/logger/prometheus.go:log_fatal_count +common/client/multi_node.go:multi_node_states +common/txmgr/broadcaster.go:tx_manager_time_until_tx_broadcast +common/txmgr/confirmer.go:tx_manager_num_gas_bumps +common/txmgr/confirmer.go:tx_manager_gas_bump_exceeds_limit +common/txmgr/confirmer.go:tx_manager_num_confirmed_transactions +common/txmgr/confirmer.go:tx_manager_num_successful_transactions +common/txmgr/confirmer.go:tx_manager_num_tx_reverted +common/txmgr/confirmer.go:tx_manager_fwd_tx_count +common/txmgr/confirmer.go:tx_manager_tx_attempt_count +common/txmgr/confirmer.go:tx_manager_time_until_tx_confirmed +common/txmgr/confirmer.go:tx_manager_blocks_until_tx_confirmed +common/headtracker/head_tracker.go:head_tracker_current_head +common/headtracker/head_tracker.go:head_tracker_very_old_head +common/headtracker/head_listener.go:head_tracker_heads_received +common/headtracker/head_listener.go:head_tracker_connection_errors +core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_alive +core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_in_sync +core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_out_of_sync +core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_unreachable +core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_invalid_chain_id +core/chains/evm/client/node_fsm.go:evm_pool_rpc_node_num_transitions_to_unusable +core/services/promreporter/prom_reporter.go:unconfirmed_transactions +core/services/promreporter/prom_reporter.go:max_unconfirmed_tx_age +core/services/promreporter/prom_reporter.go:max_unconfirmed_blocks +core/services/promreporter/prom_reporter.go:pipeline_runs_queued +core/services/promreporter/prom_reporter.go:pipeline_task_runs_queued +core/services/pipeline/task.bridge.go:bridge_latency_seconds +core/services/pipeline/task.bridge.go:bridge_errors_total +core/services/pipeline/task.bridge.go:bridge_cache_hits_total +core/services/pipeline/task.bridge.go:bridge_cache_errors_total +core/services/pipeline/task.http.go:pipeline_task_http_fetch_time +core/services/pipeline/task.http.go:pipeline_task_http_response_body_size +core/services/pipeline/task.eth_call.go:pipeline_task_eth_call_execution_time +core/services/pipeline/runner.go:pipeline_task_execution_time +core/services/pipeline/runner.go:pipeline_run_errors +core/services/pipeline/runner.go:pipeline_run_total_time_to_completion +core/services/pipeline/runner.go:pipeline_tasks_total_finished +core/chains/evm/client/node.go:evm_pool_rpc_node_dials_total +core/chains/evm/client/node.go:evm_pool_rpc_node_dials_failed +core/chains/evm/client/node.go:evm_pool_rpc_node_dials_success +core/chains/evm/client/node.go:evm_pool_rpc_node_verifies +core/chains/evm/client/node.go:evm_pool_rpc_node_verifies_failed +core/chains/evm/client/node.go:evm_pool_rpc_node_verifies_success +core/chains/evm/client/node.go:evm_pool_rpc_node_calls_total +core/chains/evm/client/node.go:evm_pool_rpc_node_calls_failed +core/chains/evm/client/node.go:evm_pool_rpc_node_calls_success +core/chains/evm/client/node.go:evm_pool_rpc_node_rpc_call_time +core/chains/evm/client/pool.go:evm_pool_rpc_node_states +core/utils/mailbox_prom.go:mailbox_load_percent +core/services/pg/stats.go:db_conns_max +core/services/pg/stats.go:db_conns_open +core/services/pg/stats.go:db_conns_used +core/services/pg/stats.go:db_wait_count +core/services/pg/stats.go:db_wait_time_seconds +core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_highest_seen_block +core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_num_seen_blocks +core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_polls_total +core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_polls_failed +core/chains/evm/client/node_lifecycle.go:evm_pool_rpc_node_polls_success +core/services/relay/evm/config_poller.go:ocr2_failed_rpc_contract_calls +core/services/feeds/service.go:feeds_job_proposal_requests +core/services/feeds/service.go:feeds_job_proposal_count +core/services/ocrcommon/prom.go:bridge_json_parse_values +core/services/ocrcommon/prom.go:ocr_median_values +core/chains/evm/logpoller/observability.go:log_poller_query_dataset_size + +not-covered and product specific (definitions/usage should be moved to plugins) + +mercury + +core/services/relay/evm/mercury/types/types.go:mercury_price_feed_missing +core/services/relay/evm/mercury/types/types.go:mercury_price_feed_errors +core/services/relay/evm/mercury/queue.go:mercury_transmit_queue_load +core/services/relay/evm/mercury/v1/data_source.go:mercury_insufficient_blocks_count +core/services/relay/evm/mercury/v1/data_source.go:mercury_zero_blocks_count +core/services/relay/evm/mercury/wsrpc/client.go:mercury_transmit_timeout_count +core/services/relay/evm/mercury/wsrpc/client.go:mercury_dial_count +core/services/relay/evm/mercury/wsrpc/client.go:mercury_dial_success_count +core/services/relay/evm/mercury/wsrpc/client.go:mercury_dial_error_count +core/services/relay/evm/mercury/wsrpc/client.go:mercury_connection_reset_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_success_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_duplicate_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_connection_error_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_queue_delete_error_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_queue_insert_error_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_queue_push_error_count +core/services/relay/evm/mercury/transmitter.go:mercury_transmit_server_error_count + +functions + +core/services/gateway/connectionmanager.go:gateway_heartbeats_sent +core/services/gateway/gateway.go:gateway_request +core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_handler_error +core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_set_success +core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_set_failure +core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_list_success +core/services/gateway/handlers/functions/handler.functions.go:gateway_functions_secrets_list_failure +core/services/functions/external_adapter_client.go:functions_external_adapter_client_latency +core/services/functions/external_adapter_client.go:functions_external_adapter_client_errors_total +core/services/functions/listener.go:functions_request_received +core/services/functions/listener.go:functions_request_internal_error +core/services/functions/listener.go:functions_request_computation_error +core/services/functions/listener.go:functions_request_computation_success +core/services/functions/listener.go:functions_request_timeout +core/services/functions/listener.go:functions_request_confirmed +core/services/functions/listener.go:functions_request_computation_result_size +core/services/functions/listener.go:functions_request_computation_error_size +core/services/functions/listener.go:functions_request_computation_duration +core/services/functions/listener.go:functions_request_pruned +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_restarts +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_query +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_observation +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_report +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_report_num_observations +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_accept +core/services/ocr2/plugins/functions/reporting.go:functions_reporting_plugin_transmit +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_query +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_observation +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_report +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_accept +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_query_byte_size +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_query_rows_count +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_observation_rows_count +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_report_rows_count +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_wrong_sig_count +core/services/ocr2/plugins/s4/prometheus.go:s4_reporting_plugin_expired_rows + +vrf + +core/services/vrf/vrfcommon/metrics.go:vrf_request_queue_size +core/services/vrf/vrfcommon/metrics.go:vrf_processed_request_count +core/services/vrf/vrfcommon/metrics.go:vrf_dropped_request_count +core/services/vrf/vrfcommon/metrics.go:vrf_duplicate_requests +core/services/vrf/vrfcommon/metrics.go:vrf_request_time_between_sims +core/services/vrf/vrfcommon/metrics.go:vrf_request_time_until_initial_sim + +keeper +core/services/keeper/upkeep_executer.go:keeper_check_upkeep_execution_time +*/ + const ( ErrFailedToCreateDashboard = "failed to create dashboard" ErrFailedToCreateFolder = "failed to create folder" @@ -25,6 +192,7 @@ const ( // CLClusterDashboard is a dashboard for a Chainlink cluster type CLClusterDashboard struct { + Nodes int Name string LokiDataSourceName string PrometheusDataSourceName string @@ -37,8 +205,9 @@ type CLClusterDashboard struct { } // NewCLClusterDashboard returns a new dashboard for a Chainlink cluster, can be used as a base for more complex plugin based dashboards -func NewCLClusterDashboard(name, ldsn, pdsn, dbf, grafanaURL, grafanaToken string, opts []dashboard.Option) (*CLClusterDashboard, error) { +func NewCLClusterDashboard(nodes int, name, ldsn, pdsn, dbf, grafanaURL, grafanaToken string, opts []dashboard.Option) (*CLClusterDashboard, error) { db := &CLClusterDashboard{ + Nodes: nodes, Name: name, Folder: dbf, LokiDataSourceName: ldsn, @@ -58,19 +227,32 @@ func (m *CLClusterDashboard) Opts() []dashboard.Option { } // logsRowOption returns a row option for a node's logs with name and instance selector -func (m *CLClusterDashboard) logsRowOption(name, instanceSelector string) row.Option { +func (m *CLClusterDashboard) logsRowOption(name, q string) row.Option { return row.WithLogs( name, logs.DataSource(m.LokiDataSourceName), logs.Span(12), logs.Height("300px"), logs.Transparent(), - logs.WithLokiTarget(fmt.Sprintf(` - {namespace="${namespace}", app="app", instance="%s", container="node"} - `, instanceSelector)), + logs.WithLokiTarget(q), ) } +func (m *CLClusterDashboard) logsRowOptionsForNodes(nodes int) []row.Option { + opts := make([]row.Option, 0) + for i := 1; i <= nodes; i++ { + opts = append(opts, row.WithLogs( + fmt.Sprintf("Node %d", i), + logs.DataSource(m.LokiDataSourceName), + logs.Span(12), + logs.Height("300px"), + logs.Transparent(), + logs.WithLokiTarget(fmt.Sprintf(`{namespace="${namespace}", app="app", instance="node-%d", container="node"}`, i)), + )) + } + return opts +} + // timeseriesRowOption returns a row option for a timeseries with name, axis unit, query and legend template func (m *CLClusterDashboard) timeseriesRowOption(name, axisUnit, query, legendTemplate string) row.Option { var tsq timeseries.Option @@ -129,12 +311,9 @@ func (m *CLClusterDashboard) generate() error { query.Request(fmt.Sprintf("label_values(%s)", "namespace")), query.Sort(query.NumericalAsc), ), - dashboard.VariableAsInterval( - "interval", - interval.Values([]string{"30s", "1m", "5m", "10m", "30m", "1h", "6h", "12h"}), - ), dashboard.Row( "Cluster health", + row.Collapse(), m.statRowOption( "App Version", `version{namespace="${namespace}"}`, @@ -160,6 +339,21 @@ func (m *CLClusterDashboard) generate() error { prometheus.Legend("{{pod}} - {{service_id}}"), ), ), + row.WithTimeSeries( + "ETH Balance", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `eth_balance{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - {{account}}"), + ), + ), + ), + // logs + dashboard.Row( + "Logs", + row.Collapse(), row.WithTimeSeries( "Log Counters", timeseries.Span(12), @@ -177,30 +371,244 @@ func (m *CLClusterDashboard) generate() error { `log_critical_count{namespace="${namespace}"}`, prometheus.Legend("{{pod}} - critical"), ), + timeseries.WithPrometheusTarget( + `log_warn_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - warn"), + ), timeseries.WithPrometheusTarget( `log_error_count{namespace="${namespace}"}`, prometheus.Legend("{{pod}} - error"), ), ), - row.WithTimeSeries( - "ETH Balance", - timeseries.Span(12), - timeseries.Height("200px"), - timeseries.DataSource(m.PrometheusDataSourceName), - timeseries.WithPrometheusTarget( - `eth_balance{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - {{account}}"), - ), + m.logsRowOption("All errors", ` + {namespace="${namespace}", app="app", container="node"} + | json + | level="error" + | line_format "{{ .instance }} {{ .level }} {{ .ts }} {{ .logger }} {{ .caller }} {{ .msg }} {{ .version }} {{ .nodeTier }} {{ .nodeName }} {{ .node }} {{ .evmChainID }} {{ .nodeOrder }} {{ .mode }} {{ .nodeState }} {{ .sentryEventID }} {{ .stacktrace }}"`), + m.logsRowOption("Node 1", `{namespace="${namespace}", app="app", instance="node-1", container="node"}`), + m.logsRowOption("Node 2", `{namespace="${namespace}", app="app", instance="node-2", container="node"}`), + m.logsRowOption("Node 3", `{namespace="${namespace}", app="app", instance="node-3", container="node"}`), + m.logsRowOption("Node 4", `{namespace="${namespace}", app="app", instance="node-4", container="node"}`), + m.logsRowOption("Node 5", `{namespace="${namespace}", app="app", instance="node-5", container="node"}`), + m.logsRowOption("Node 6", `{namespace="${namespace}", app="app", instance="node-6", container="node"}`), + ), + // HeadTracker + dashboard.Row("Head tracker", + row.Collapse(), + m.timeseriesRowOption( + "Head tracker current head", + "Block", + `head_tracker_current_head{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Head tracker very old head", + "Block", + `head_tracker_very_old_head{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Head tracker heads received", + "Block", + `head_tracker_heads_received{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Head tracker connection errors", + "Errors", + `head_tracker_connection_errors{namespace="${namespace}"}`, + "{{ pod }}", ), ), - // logs - dashboard.Row( - "Logs", + dashboard.Row("LogPoller", + row.Collapse(), + m.timeseriesRowOption( + "LogPoller Query Dataset Size", + "", + `log_poller_query_dataset_size{namespace="${namespace}"}`, + "{{ pod }}", + ), + ), + dashboard.Row("OCRCommon", + row.Collapse(), + m.timeseriesRowOption( + "Bridge JSON Parse Values", + "", + `bridge_json_parse_values{namespace="${namespace}"}`, + "{{ pod }} JobID: {{ job_id }}", + ), + m.timeseriesRowOption( + "OCR Median Values", + "", + `ocr_median_values{namespace="${namespace}"}`, + "{{pod}} JobID: {{ job_id }}", + ), + ), + dashboard.Row("Relay Config Poller", + row.Collapse(), + m.timeseriesRowOption( + "Relay Config Poller RPC Contract Calls", + "", + `ocr2_failed_rpc_contract_calls{namespace="${namespace}"}`, + "{{ pod }}", + ), + ), + dashboard.Row("Feeds Jobs", + row.Collapse(), + m.timeseriesRowOption( + "Feeds Job Proposal Requests", + "", + `feeds_job_proposal_requests{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Feeds Job Proposal Count", + "", + `feeds_job_proposal_count{namespace="${namespace}"}`, + "{{ pod }}", + ), + ), + dashboard.Row("Mailbox", + row.Collapse(), + m.timeseriesRowOption( + "Mailbox Load Percent", + "", + `mailbox_load_percent{namespace="${namespace}"}`, + "{{ pod }} {{ name }}", + ), + ), + dashboard.Row("Multi Node States", + row.Collapse(), + m.timeseriesRowOption( + "Multi Node States", + "", + `multi_node_states{namespace="${namespace}"}`, + "{{ pod }}", + ), + ), + dashboard.Row("Block History Estimator", + row.Collapse(), + m.timeseriesRowOption( + "Gas Updater All Gas Price Percentiles", + "", + `gas_updater_all_gas_price_percentiles{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Gas Updater All Tip Cap Percentiles", + "", + `gas_updater_all_tip_cap_percentiles{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Gas Updater Set Gas Price", + "", + `gas_updater_set_gas_price{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Gas Updater Set Tip Cap", + "", + `gas_updater_set_tip_cap{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Gas Updater Current Base Fee", + "", + `gas_updater_current_base_fee{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Block History Estimator Connectivity Failure Count", + "", + `block_history_estimator_connectivity_failure_count{namespace="${namespace}"}`, + "{{ pod }}", + ), + ), + // PromReporter + dashboard.Row("Prom Reporter", row.Collapse(), - m.logsRowOption("Node 1", "node-1"), - m.logsRowOption("Node 2", "node-2"), - m.logsRowOption("Node 3", "node-3"), - m.logsRowOption("Node 4", "node-4"), + m.timeseriesRowOption( + "Unconfirmed Transactions", + "Tx", + `unconfirmed_transactions{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Unconfirmed TX Age", + "Sec", + `max_unconfirmed_tx_age{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "Unconfirmed TX Blocks", + "Blocks", + `max_unconfirmed_blocks{namespace="${namespace}"}`, + "{{ pod }}", + ), + ), + dashboard.Row("TX Manager", + row.Collapse(), + m.timeseriesRowOption( + "TX Manager Time Until TX Broadcast", + "", + `tx_manager_time_until_tx_broadcast{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Gas Bumps", + "", + `tx_manager_num_gas_bumps{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Gas Bumps Exceeds Limit", + "", + `tx_manager_gas_bump_exceeds_limit{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Confirmed Transactions", + "", + `tx_manager_num_confirmed_transactions{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Successful Transactions", + "", + `tx_manager_num_successful_transactions{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Reverted Transactions", + "", + `tx_manager_num_tx_reverted{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Fwd Transactions", + "", + `tx_manager_fwd_tx_count{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Num Transactions Attempts", + "", + `tx_manager_tx_attempt_count{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Time Until TX Confirmed", + "", + `tx_manager_time_until_tx_confirmed{namespace="${namespace}"}`, + "{{ pod }}", + ), + m.timeseriesRowOption( + "TX Manager Block Until TX Confirmed", + "", + `tx_manager_blocks_until_tx_confirmed{namespace="${namespace}"}`, + "{{ pod }}", + ), ), // DON report metrics dashboard.Row("DON Report metrics", @@ -236,6 +644,40 @@ func (m *CLClusterDashboard) generate() error { "", ), ), + dashboard.Row( + "EVM Pool Lifecycle", + row.Collapse(), + m.timeseriesRowOption( + "EVM Pool Highest Seen Block", + "Block", + `evm_pool_rpc_node_highest_seen_block{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool Num Seen Blocks", + "Block", + `evm_pool_rpc_node_num_seen_blocks{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool Node Polls Total", + "Block", + `evm_pool_rpc_node_polls_total{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool Node Polls Failed", + "Block", + `evm_pool_rpc_node_polls_failed{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool Node Polls Success", + "Block", + `evm_pool_rpc_node_polls_success{namespace="${namespace}"}`, + "{{pod}}", + ), + ), dashboard.Row( "DB Connection Metrics (App)", row.Collapse(), @@ -263,6 +705,12 @@ func (m *CLClusterDashboard) generate() error { `db_conns_wait{namespace="${namespace}"}`, "{{pod}}", ), + m.timeseriesRowOption( + "DB Wait Count", + "", + `db_wait_count{namespace="${namespace}"}`, + "{{pod}}", + ), m.timeseriesRowOption( "DB Wait time", "Sec", @@ -291,6 +739,12 @@ func (m *CLClusterDashboard) generate() error { `evm_pool_rpc_node_dials_success{namespace="${namespace}"}`, "{{pod}}", ), + m.timeseriesRowOption( + "EVM Pool RPC Node Dials Failed", + "", + `evm_pool_rpc_node_dials_failed{namespace="${namespace}"}`, + "{{pod}}", + ), m.timeseriesRowOption( "EVM Pool RPC Node Dials Total", "", @@ -298,9 +752,9 @@ func (m *CLClusterDashboard) generate() error { "{{pod}}", ), m.timeseriesRowOption( - "EVM Pool RPC Highest Seen Block", + "EVM Pool RPC Node Dials Failed", "", - `evm_pool_rpc_node_highest_seen_block{namespace="${namespace}"}`, + `evm_pool_rpc_node_dials_failed{namespace="${namespace}"}`, "{{pod}}", ), m.timeseriesRowOption( @@ -309,6 +763,36 @@ func (m *CLClusterDashboard) generate() error { `evm_pool_rpc_node_num_transitions_to_alive{namespace="${namespace}"}`, "{{pod}}", ), + m.timeseriesRowOption( + "EVM Pool RPC Total Transitions to In Sync", + "", + `evm_pool_rpc_node_num_transitions_to_in_sync{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Total Transitions to Out of Sync", + "", + `evm_pool_rpc_node_num_transitions_to_out_of_sync{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Total Transitions to Unreachable", + "", + `evm_pool_rpc_node_num_transitions_to_unreachable{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Total Transitions to invalid Chain ID", + "", + `evm_pool_rpc_node_num_transitions_to_invalid_chain_id{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "EVM Pool RPC Total Transitions to unusable", + "", + `evm_pool_rpc_node_num_transitions_to_unusable{namespace="${namespace}"}`, + "{{pod}}", + ), m.timeseriesRowOption( "EVM Pool RPC Node Polls Success", "", @@ -339,6 +823,12 @@ func (m *CLClusterDashboard) generate() error { `evm_pool_rpc_node_verifies_success{namespace="${namespace}"}`, "{{pod}} - {{evmChainID}}", ), + m.timeseriesRowOption( + "EVM Pool RPC Node Verifies Failed", + "", + `evm_pool_rpc_node_verifies_failed{namespace="${namespace}"}`, + "{{pod}} - {{evmChainID}}", + ), ), dashboard.Row( "EVM Pool RPC Node Latencies (App)", @@ -351,7 +841,89 @@ func (m *CLClusterDashboard) generate() error { ), ), dashboard.Row( - "Pipeline Tasks Metrics (App)", + "Pipeline Metrics (Runner)", + row.Collapse(), + m.timeseriesRowOption( + "Pipeline Task Execution Time", + "Sec", + `pipeline_task_execution_time{namespace="${namespace}"} / 1e6`, + "{{ pod }} JobID: {{ job_id }}", + ), + m.timeseriesRowOption( + "Pipeline Run Errors", + "", + `pipeline_run_errors{namespace="${namespace}"}`, + "{{ pod }} JobID: {{ job_id }}", + ), + m.timeseriesRowOption( + "Pipeline Run Total Time to Completion", + "Sec", + `pipeline_run_total_time_to_completion{namespace="${namespace}"} / 1e6`, + "{{ pod }} JobID: {{ job_id }}", + ), + m.timeseriesRowOption( + "Pipeline Tasks Total Finished", + "", + `pipeline_tasks_total_finished{namespace="${namespace}"}`, + "{{ pod }} JobID: {{ job_id }}", + ), + ), + dashboard.Row( + "Pipeline Metrics (ETHCall)", + row.Collapse(), + m.timeseriesRowOption( + "Pipeline Task ETH Call Execution Time", + "Sec", + `pipeline_task_eth_call_execution_time{namespace="${namespace}"}`, + "{{pod}}", + ), + ), + dashboard.Row( + "Pipeline Metrics (HTTP)", + row.Collapse(), + m.timeseriesRowOption( + "Pipeline Task HTTP Fetch Time", + "Sec", + `pipeline_task_http_fetch_time{namespace="${namespace}"} / 1e6`, + "{{pod}}", + ), + m.timeseriesRowOption( + "Pipeline Task HTTP Response Body Size", + "Bytes", + `pipeline_task_http_response_body_size{namespace="${namespace}"}`, + "{{pod}}", + ), + ), + dashboard.Row( + "Pipeline Metrics (Bridge)", + row.Collapse(), + m.timeseriesRowOption( + "Pipeline Bridge Latency", + "Sec", + `bridge_latency_seconds{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "Pipeline Bridge Errors Total", + "", + `bridge_errors_total{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "Pipeline Bridge Cache Hits Total", + "", + `bridge_cache_hits_total{namespace="${namespace}"}`, + "{{pod}}", + ), + m.timeseriesRowOption( + "Pipeline Bridge Cache Errors Total", + "", + `bridge_cache_errors_total{namespace="${namespace}"}`, + "{{pod}}", + ), + ), + dashboard.Row( + "Pipeline Metrics", row.Collapse(), m.timeseriesRowOption( "Pipeline Runs Queued", @@ -367,6 +939,48 @@ func (m *CLClusterDashboard) generate() error { ), ), } + logOptsFinal := make([]row.Option, 0) + logOptsFinal = append( + logOptsFinal, + row.Collapse(), + row.WithTimeSeries( + "Log Counters", + timeseries.Span(12), + timeseries.Height("200px"), + timeseries.DataSource(m.PrometheusDataSourceName), + timeseries.WithPrometheusTarget( + `log_panic_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - panic"), + ), + timeseries.WithPrometheusTarget( + `log_fatal_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - fatal"), + ), + timeseries.WithPrometheusTarget( + `log_critical_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - critical"), + ), + timeseries.WithPrometheusTarget( + `log_warn_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - warn"), + ), + timeseries.WithPrometheusTarget( + `log_error_count{namespace="${namespace}"}`, + prometheus.Legend("{{pod}} - error"), + ), + ), + m.logsRowOption("All errors", ` + {namespace="${namespace}", app="app", container="node"} + | json + | level="error" + | line_format "{{ .instance }} {{ .level }} {{ .ts }} {{ .logger }} {{ .caller }} {{ .msg }} {{ .version }} {{ .nodeTier }} {{ .nodeName }} {{ .node }} {{ .evmChainID }} {{ .nodeOrder }} {{ .mode }} {{ .nodeState }} {{ .sentryEventID }} {{ .stacktrace }}"`), + ) + logOptsFinal = append(logOptsFinal, m.logsRowOptionsForNodes(m.Nodes)...) + logRowOpts := dashboard.Row( + "Logs", + logOptsFinal..., + ) + opts = append(opts, logRowOpts) opts = append(opts, m.extendedOpts...) builder, err := dashboard.New( "Chainlink Cluster Dashboard", diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index 688660d918e..4f7cf8641a3 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -55,7 +55,7 @@ deployments: stateful: false chainlink: web_port: 6688 - p2p_port: 8090 + p2p_port: 6690 nodes: - name: node-1 image: ${DEVSPACE_IMAGE} @@ -69,6 +69,12 @@ deployments: - name: node-4 image: ${DEVSPACE_IMAGE} version: latest + - name: node-5 + image: ${DEVSPACE_IMAGE} + version: latest + - name: node-6 + image: ${DEVSPACE_IMAGE} + version: latest prometheusMonitor: "true" podAnnotations: { } nodeSelector: { } diff --git a/charts/chainlink-cluster/go.mod b/charts/chainlink-cluster/go.mod index 990ebbf713e..951fb1d2e3c 100644 --- a/charts/chainlink-cluster/go.mod +++ b/charts/chainlink-cluster/go.mod @@ -5,11 +5,186 @@ go 1.21 require ( github.com/K-Phoen/grabana v0.21.19 github.com/pkg/errors v0.9.1 + github.com/smartcontractkit/wasp v0.3.6 ) require ( github.com/K-Phoen/sdk v0.12.3 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/aws/aws-sdk-go v1.44.217 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee // indirect + github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dennwc/varint v1.0.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.1 // indirect + github.com/fatih/color v1.14.1 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.8.1 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.3 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/spec v0.20.8 // indirect + github.com/go-openapi/strfmt v0.21.3 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.11.1 // indirect + github.com/go-resty/resty/v2 v2.7.0 // indirect + github.com/goccy/go-json v0.9.11 // indirect + github.com/gogo/googleapis v1.4.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/gogo/status v1.1.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect - github.com/prometheus/common v0.39.0 // indirect + github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 // indirect + github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 // indirect + github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 // indirect + github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect + github.com/hashicorp/consul/api v1.20.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.4.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/hashicorp/memberlist v0.5.0 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/klauspost/compress v1.16.3 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/miekg/dns v1.1.51 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect + github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/alertmanager v0.25.0 // indirect + github.com/prometheus/client_golang v1.15.1 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/common/sigv4 v0.1.0 // indirect + github.com/prometheus/exporter-toolkit v0.9.1 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 // indirect + github.com/rs/zerolog v1.29.0 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/sercand/kuberesolver/v4 v4.0.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/testify v1.8.3 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/ugorji/go/codec v1.2.7 // indirect + github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 // indirect + github.com/weaveworks/promrus v1.2.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/v3 v3.5.7 // indirect + go.mongodb.org/mongo-driver v1.11.2 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/metric v0.37.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/goleak v1.2.1 // indirect + go.uber.org/multierr v1.9.0 // indirect + go.uber.org/ratelimit v0.2.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.7.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect + google.golang.org/grpc v1.57.2 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.26.2 // indirect + k8s.io/apimachinery v0.26.2 // indirect + k8s.io/client-go v0.26.2 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d // indirect + k8s.io/utils v0.0.0-20230308161112-d77c459e9343 // indirect + nhooyr.io/websocket v1.8.7 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +replace ( + // Fixes go mod tidy issue for ambiguous imports from go-ethereum + // See https://github.com/ugorji/go/issues/279 + github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1 + + github.com/go-kit/log => github.com/go-kit/log v0.2.1 + + // replicating the replace directive on cosmos SDK + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + + // until merged upstream: https://github.com/hashicorp/go-plugin/pull/257 + github.com/hashicorp/go-plugin => github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 + + // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 + github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f + + github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 + github.com/sercand/kuberesolver/v4 => github.com/sercand/kuberesolver/v5 v5.1.1 ) diff --git a/charts/chainlink-cluster/go.sum b/charts/chainlink-cluster/go.sum index 093a42d6081..4a7704cac49 100644 --- a/charts/chainlink-cluster/go.sum +++ b/charts/chainlink-cluster/go.sum @@ -1,20 +1,2126 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= +github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= +github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/K-Phoen/grabana v0.21.19 h1:tJjRO8nN9JrFjLoQGtOB9P5ILoqENZZGAtt3nK+Ry2Y= github.com/K-Phoen/grabana v0.21.19/go.mod h1:B7gxVxacQUgHWmgqduf4WPZoKYHO1mvZnRVCoyQiwdw= github.com/K-Phoen/sdk v0.12.3 h1:ScutEQASc9VEKJCm3OjIMD82BIS9B2XtNg3gEf6Gs+M= github.com/K-Phoen/sdk v0.12.3/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +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/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.44.217 h1:FcWC56MRl+k756aH3qeMQTylSdeJ58WN0iFz3fkyRz0= +github.com/aws/aws-sdk-go v1.44.217/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I= +github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= +github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= +github.com/digitalocean/godo v1.97.0 h1:p9w1yCcWMZcxFSLPToNGXA96WfUVLXqoHti6GzVomL4= +github.com/digitalocean/godo v1.97.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= +github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= +github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= +github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= +github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc= +github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.8 h1:ubHmXNY3FCIOinT8RNrrPfGc9t7I1qhPtdOGoG2AxRU= +github.com/go-openapi/spec v0.20.8/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc= +github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= +github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gophercloud/gophercloud v1.2.0 h1:1oXyj4g54KBg/kFtCdMM6jtxSzeIyg8wv4z1HoGPp1E= +github.com/gophercloud/gophercloud v1.2.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 h1:IOks+FXJ6iO/pfbaVEf4efNw+YzYBYNCkCabyrbkFTM= +github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2/go.mod h1:zj+5BNZAVmQafV583uLTAOzRr963KPdEm4d6NPmtbwg= +github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 h1:V5PspEXlSlNh22sMyGkgfSOVVLTsSmhbmsp1VPt8Fdc= +github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6/go.mod h1:+aWr7OBDuZMT+p0rKmLfW5saO2m3YOGBnt++IlgLhVk= +github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 h1:VXitROTlmZtLzvokNe8ZbUKpmwldM4Hy1zdNRO32jKU= +github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765/go.mod h1:DhJMrd2QInI/1CNtTN43BZuTmkccdizW1jZ+F6aHkhY= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= +github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= +github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= +github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= +github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/nomad/api v0.0.0-20230308192510-48e7d70fcd4b h1:EkuSTU8c/63q4LMayj8ilgg/4I5PXDFVcnqKfs9qcwI= +github.com/hashicorp/nomad/api v0.0.0-20230308192510-48e7d70fcd4b/go.mod h1:bKUb1ytds5KwUioHdvdq9jmrDqCThv95si0Ub7iNeBg= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hetznercloud/hcloud-go v1.41.0 h1:KJGFRRc68QiVu4PrEP5BmCQVveCP2CM26UGQUKGpIUs= +github.com/hetznercloud/hcloud-go v1.41.0/go.mod h1:NaHg47L6C77mngZhwBG652dTAztYrsZ2/iITJKhQkHA= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ionos-cloud/sdk-go/v6 v6.1.4 h1:BJHhFA8Q1SZC7VOXqKKr2BV2ysQ2/4hlk1e4hZte7GY= +github.com/ionos-cloud/sdk-go/v6 v6.1.4/go.mod h1:Ox3W0iiEz0GHnfY9e5LmAxwklsxguuNFEUSu0gVRTME= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/linode/linodego v1.14.1 h1:uGxQyy0BidoEpLGdvfi4cPgEW+0YUFsEGrLEhcTfjNc= +github.com/linode/linodego v1.14.1/go.mod h1:NJlzvlNtdMRRkXb0oN6UWzUkj6t+IBsyveHgZ5Ppjyk= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= +github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= +github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= +github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/ovh/go-ovh v1.3.0 h1:mvZaddk4E4kLcXhzb+cxBsMPYp2pHqiQpWYkInsuZPQ= +github.com/ovh/go-ovh v1.3.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/alertmanager v0.25.0 h1:vbXKUR6PYRiZPRIKfmXaG+dmCKG52RtPL4Btl8hQGvg= +github.com/prometheus/alertmanager v0.25.0/go.mod h1:MEZ3rFVHqKZsw7IcNS/m4AWZeXThmJhumpiWR4eHU/w= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= +github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= +github.com/prometheus/exporter-toolkit v0.8.2/go.mod h1:00shzmJL7KxcsabLWcONwpyNEuWhREOnFqZW7vadFS0= +github.com/prometheus/exporter-toolkit v0.9.1 h1:cNkC01riqiOS+kh3zdnNwRsbe/Blh0WwK3ij5rPJ9Sw= +github.com/prometheus/exporter-toolkit v0.9.1/go.mod h1:iFlTmFISCix0vyuyBmm0UqOUCTao9+RsAsKJP3YM9ec= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 h1:i5hmbBzR+VeL5pPl1ZncsJ1bpg3SO66bwkE1msJBsMA= +github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2/go.mod h1:Mm42Acga98xgA+u5yTaC3ki3i0rJEJWFpbdHN7q2trk= +github.com/pyroscope-io/client v0.6.0 h1:rcUFgcnfmuyVYDYT+4d0zfqc8YedOyruHSsUb9ImaBw= +github.com/pyroscope-io/client v0.6.0/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= +github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= +github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= +github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.14 h1:yFl3jyaSVLNYXlnNYM5z2pagEk1dYQhfr1p20T1NyKY= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.14/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= +github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartcontractkit/wasp v0.3.6 h1:1TLWfrTzqZwNvyyoKzPZ8FLQat2lNz640eM+mMh2YxM= +github.com/smartcontractkit/wasp v0.3.6/go.mod h1:L/cyUGfpaWxy/2twOVJLRt2mySJEIqGrFj9nyvRLpSo= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 h1:gjb7t9LCnRu14LHubyLIgrE+EYlAaREiPn/VknV7R3s= +github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205/go.mod h1:O9wmSPNVSuqxzUZPFlHnPQ8xnyvx0qBnKGFfGbj95uY= +github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= +github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNHCw= +go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 h1:lE9EJyw3/JhrjWH/hEy9FptnalDQgj7vpbgC2KCCCxE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0/go.mod h1:pcQ3MM3SWvrA71U4GDqv9UFDJ3HQsW7y5ZO3tDTlUdI= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/metric v0.37.0 h1:pHDQuLQOZwYD+Km0eb657A25NaRzy0a+eLyKfDXedEs= +go.opentelemetry.io/otel/metric v0.37.0/go.mod h1:DmdaHfGt54iV6UKxsV9slj2bBRJcKC1B1uvDLIioc1s= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= +go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= +google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.57.2 h1:uw37EN34aMFFXB2QPW7Tq6tdTbind1GpRxw5aOX3a5k= +google.golang.org/grpc v1.57.2/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= +k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= +k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= +k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= +k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d h1:VcFq5n7wCJB2FQMCIHfC+f+jNcGgNMar1uKd6rVlifU= +k8s.io/kube-openapi v0.0.0-20230303024457-afdc3dddf62d/go.mod h1:y5VtZWM9sHHc2ZodIH/6SHzXj+TPU5USoA8lcIeKEKY= +k8s.io/utils v0.0.0-20230308161112-d77c459e9343 h1:m7tbIjXGcGIAtpmQr7/NAi7RsWoW3E7Zcm4jI1HicTc= +k8s.io/utils v0.0.0-20230308161112-d77c459e9343/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/charts/chainlink-cluster/templates/chainlink-cm.yaml b/charts/chainlink-cluster/templates/chainlink-cm.yaml index 736a3322048..b33e29df4b5 100644 --- a/charts/chainlink-cluster/templates/chainlink-cm.yaml +++ b/charts/chainlink-cluster/templates/chainlink-cm.yaml @@ -26,15 +26,25 @@ data: AllowOrigins = '*' SecureCookies = false SessionTimeout = '999h0m0s' + [Feature] + FeedsManager = true + LogPoller = true + UICSAKeys = true [OCR] Enabled = true + DefaultTransactionQueueDepth = 0 [P2P] [P2P.V2] Enabled = true - ListenAddresses = ["0.0.0.0:6690"] + ListenAddresses = ['0.0.0.0:6690'] + AnnounceAddresses = ['0.0.0.0:6690'] + DeltaDial = '500ms' + DeltaReconcile = '5s' [[EVM]] ChainID = '1337' MinContractPayment = '0' + AutoCreateKey = true + FinalityDepth = 1 [[EVM.Nodes]] Name = 'node-0' WSURL = 'ws://geth:8546' diff --git a/charts/chainlink-cluster/templates/chainlink-service.yaml b/charts/chainlink-cluster/templates/chainlink-service.yaml index 24c96c909ea..7a3b70efb4f 100644 --- a/charts/chainlink-cluster/templates/chainlink-service.yaml +++ b/charts/chainlink-cluster/templates/chainlink-service.yaml @@ -12,7 +12,7 @@ spec: port: {{ $.Values.chainlink.p2p_port }} targetPort: {{ $.Values.chainlink.p2p_port }} selector: - app: {{ $.Release.Name }} + instance: {{ $cfg.name }} type: ClusterIP --- {{- end }} \ No newline at end of file diff --git a/charts/chainlink-cluster/templates/mockserver-service.yaml b/charts/chainlink-cluster/templates/mockserver-service.yaml new file mode 100644 index 00000000000..f8ab78a84b5 --- /dev/null +++ b/charts/chainlink-cluster/templates/mockserver-service.yaml @@ -0,0 +1,14 @@ +{{ if (hasKey .Values "mockserver") }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-mockserver +spec: + selector: + app: {{ .Release.Name }}-mockserver + ports: + - name: serviceport + port: {{ default "1080" $.Values.mockserver.port}} + targetPort: serviceport + type: ClusterIP +{{ end }} \ No newline at end of file diff --git a/charts/chainlink-cluster/templates/mockserver.yaml b/charts/chainlink-cluster/templates/mockserver.yaml index 96f9582435f..14c05d0acd5 100755 --- a/charts/chainlink-cluster/templates/mockserver.yaml +++ b/charts/chainlink-cluster/templates/mockserver.yaml @@ -1,3 +1,4 @@ +{{ if (hasKey .Values "mockserver") }} apiVersion: apps/v1 kind: Deployment metadata: @@ -5,7 +6,6 @@ metadata: labels: app: {{ .Release.Name }}-mockserver spec: - replicas: {{ .Values.replicaCount }} selector: matchLabels: app: {{ .Release.Name }}-mockserver @@ -57,4 +57,5 @@ spec: tolerations: {{ toYaml . | indent 8 }} {{- end }} +{{- end }} --- \ No newline at end of file diff --git a/charts/chainlink-cluster/values-raw-helm.yaml b/charts/chainlink-cluster/values-raw-helm.yaml index 006515f0a33..726a5347119 100644 --- a/charts/chainlink-cluster/values-raw-helm.yaml +++ b/charts/chainlink-cluster/values-raw-helm.yaml @@ -11,7 +11,7 @@ # version: stable chainlink: web_port: 6688 - p2p_port: 8090 + p2p_port: 6690 nodes: - name: node-1 image: "public.ecr.aws/chainlink/chainlink:latest" @@ -45,9 +45,13 @@ chainlink: # HTTPURL = 'http://geth:8544' # [WebServer.TLS] # HTTPSPort = 0 +# or use overridesToml to override some part of configuration +# overridesToml: | - name: node-2 - name: node-3 - name: node-4 + - name: node-5 + - name: node-6 resources: requests: cpu: 350m @@ -106,6 +110,19 @@ runner: limits: cpu: 1 memory: 512Mi + affinity: { } + tolerations: [ ] + nodeSelector: { } + ingress: + enabled: false + className: "" + hosts: [ ] + tls: [ ] + annotations: { } + service: + type: NodePort + port: 8080 + # monitoring.coreos.com/v1 PodMonitor for each node prometheusMonitor: false diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 5f250af6fa8..e801e25de5f 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -36,6 +36,7 @@ require ( github.com/testcontainers/testcontainers-go v0.23.0 github.com/umbracle/ethgo v0.1.3 go.dedis.ch/kyber/v3 v3.1.0 + go.uber.org/ratelimit v0.2.0 go.uber.org/zap v1.26.0 golang.org/x/sync v0.5.0 gopkg.in/guregu/null.v4 v4.0.0 @@ -452,7 +453,6 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/ratelimit v0.2.0 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.15.0 // indirect golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect diff --git a/integration-tests/k8s/connect.go b/integration-tests/k8s/connect.go new file mode 100644 index 00000000000..34eafc42e24 --- /dev/null +++ b/integration-tests/k8s/connect.go @@ -0,0 +1,103 @@ +package k8s + +import ( + "fmt" + "os" + "time" + + "github.com/pelletier/go-toml/v2" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + client2 "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +const ( + DefaultConfigFilePath = "../../../charts/chainlink-cluster/connect.toml" + ErrReadConnectionConfig = "failed to read TOML environment connection config" + ErrUnmarshalConnectionConfig = "failed to unmarshal TOML environment connection config" +) + +type ConnectionVars struct { + Namespace string `toml:"namespace"` + NetworkName string `toml:"network_name"` + NetworkChainID int64 `toml:"network_chain_id"` + NetworkPrivateKey string `toml:"network_private_key"` + NetworkWSURL string `toml:"network_ws_url"` + NetworkHTTPURL string `toml:"network_http_url"` + CLNodesNum int `toml:"cl_nodes_num"` + CLNodeURLTemplate string `toml:"cl_node_url_template"` + CLNodeInternalDNSRecordTemplate string `toml:"cl_node_internal_dns_record_template"` + CLNodeUser string `toml:"cl_node_user"` + CLNodePassword string `toml:"cl_node_password"` + MockServerURL string `toml:"mockserver_url"` +} + +// ConnectRemote connects to a local environment, see charts/chainlink-cluster +func ConnectRemote(l zerolog.Logger) (blockchain.EVMClient, *client2.MockserverClient, contracts.ContractDeployer, *client.ChainlinkK8sClient, []*client.ChainlinkK8sClient, error) { + cfg, err := ReadConfig() + if err != nil { + return nil, nil, nil, nil, nil, err + } + net := &blockchain.EVMNetwork{ + Name: cfg.NetworkName, + Simulated: true, + SupportsEIP1559: true, + ClientImplementation: blockchain.EthereumClientImplementation, + ChainID: 1337, + PrivateKeys: []string{ + cfg.NetworkPrivateKey, + }, + URLs: []string{cfg.NetworkWSURL}, + HTTPURLs: []string{cfg.NetworkHTTPURL}, + ChainlinkTransactionLimit: 500000, + Timeout: blockchain.JSONStrDuration{Duration: 2 * time.Minute}, + MinimumConfirmations: 1, + GasEstimationBuffer: 10000, + } + cc, err := blockchain.NewEVMClientFromNetwork(*net, l) + if err != nil { + return nil, nil, nil, nil, nil, err + } + cd, err := contracts.NewContractDeployer(cc, l) + if err != nil { + return nil, nil, nil, nil, nil, err + } + clClients := make([]*client.ChainlinkK8sClient, 0) + for i := 1; i <= cfg.CLNodesNum; i++ { + c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ + URL: fmt.Sprintf(cfg.CLNodeURLTemplate, i), + Email: cfg.CLNodeUser, + InternalIP: fmt.Sprintf(cfg.CLNodeInternalDNSRecordTemplate, i), + Password: cfg.CLNodePassword, + }, fmt.Sprintf(cfg.CLNodeInternalDNSRecordTemplate, i), cfg.Namespace) + if err != nil { + return nil, nil, nil, nil, nil, err + } + clClients = append(clClients, c) + } + msClient := client2.NewMockserverClient(&client2.MockserverConfig{ + LocalURL: cfg.MockServerURL, + ClusterURL: cfg.MockServerURL, + }) + return cc, msClient, cd, clClients[0], clClients[1:], nil +} + +func ReadConfig() (*ConnectionVars, error) { + var cfg *ConnectionVars + var d []byte + var err error + d, err = os.ReadFile(DefaultConfigFilePath) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrReadConnectionConfig, err) + } + err = toml.Unmarshal(d, &cfg) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalConnectionConfig, err) + } + log.Info().Interface("Config", cfg).Msg("Connecting to environment from config") + return cfg, nil +} diff --git a/integration-tests/load/ocr/README.md b/integration-tests/load/ocr/README.md new file mode 100644 index 00000000000..20446992dc2 --- /dev/null +++ b/integration-tests/load/ocr/README.md @@ -0,0 +1,28 @@ +### OCR Load tests + +## Setup +These tests can connect to any cluster create with [chainlink-cluster](../../../charts/chainlink-cluster/README.md) + +Create your cluster +``` +kubectl create ns my-cluster +devspace use namespace my-cluster +devspace deploy +sudo kubefwd svc -n my-cluster +``` + +Change environment connection configuration [here](connection.toml) + +If you haven't changed anything in [devspace.yaml](../../../charts/chainlink-cluster/devspace.yaml) then default connection configuration will work + +## Usage + +``` +export LOKI_TOKEN=... +export LOKI_URL=... + +go test -v -run TestOCRLoad +go test -v -run TestOCRVolume +``` + +Check test configuration [here](config.toml) \ No newline at end of file diff --git a/integration-tests/load/ocr/config.go b/integration-tests/load/ocr/config.go new file mode 100644 index 00000000000..2991df3774a --- /dev/null +++ b/integration-tests/load/ocr/config.go @@ -0,0 +1,72 @@ +package ocr + +import ( + "encoding/base64" + "fmt" + "os" + + "github.com/pelletier/go-toml/v2" + "github.com/rs/zerolog/log" + + "github.com/smartcontractkit/chainlink/v2/core/store/models" +) + +const ( + DefaultConfigFilename = "config.toml" + ErrReadPerfConfig = "failed to read TOML config for performance tests" + ErrUnmarshalPerfConfig = "failed to unmarshal TOML config for performance tests" +) + +type PerformanceConfig struct { + Load *Load `toml:"Load"` + Volume *Volume `toml:"Volume"` + Common *Common `toml:"Common"` +} + +type Common struct { + ETHFunds int `toml:"eth_funds"` +} + +type Load struct { + TestDuration *models.Duration `toml:"test_duration"` + Rate int64 `toml:"rate"` + RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` + VerificationInterval *models.Duration `toml:"verification_interval"` + VerificationTimeout *models.Duration `toml:"verification_timeout"` + EAChangeInterval *models.Duration `toml:"ea_change_interval"` +} + +type Volume struct { + TestDuration *models.Duration `toml:"test_duration"` + Rate int64 `toml:"rate"` + VURequestsPerUnit int `toml:"vu_requests_per_unit"` + RateLimitUnitDuration *models.Duration `toml:"rate_limit_unit_duration"` + VerificationInterval *models.Duration `toml:"verification_interval"` + VerificationTimeout *models.Duration `toml:"verification_timeout"` + EAChangeInterval *models.Duration `toml:"ea_change_interval"` +} + +func ReadConfig() (*PerformanceConfig, error) { + var cfg *PerformanceConfig + rawConfig := os.Getenv("CONFIG") + var d []byte + var err error + if rawConfig == "" { + d, err = os.ReadFile(DefaultConfigFilename) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) + } + } else { + d, err = base64.StdEncoding.DecodeString(rawConfig) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrReadPerfConfig, err) + } + } + err = toml.Unmarshal(d, &cfg) + if err != nil { + return nil, fmt.Errorf("%s, err: %w", ErrUnmarshalPerfConfig, err) + } + + log.Debug().Interface("Config", cfg).Msg("Parsed config") + return cfg, nil +} diff --git a/integration-tests/load/ocr/config.toml b/integration-tests/load/ocr/config.toml new file mode 100644 index 00000000000..df8364b3ee4 --- /dev/null +++ b/integration-tests/load/ocr/config.toml @@ -0,0 +1,20 @@ +[Load] +test_duration = "3m" +rate_limit_unit_duration = "1m" +rate = 3 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +[Volume] +test_duration = "3m" +rate_limit_unit_duration = "1m" +vu_requests_per_unit = 10 +rate = 1 +verification_interval = "5s" +verification_timeout = "3m" + +ea_change_interval = "5s" + +[Common] +eth_funds = 3 \ No newline at end of file diff --git a/integration-tests/load/ocr/gun.go b/integration-tests/load/ocr/gun.go new file mode 100644 index 00000000000..a2eb1ff2200 --- /dev/null +++ b/integration-tests/load/ocr/gun.go @@ -0,0 +1,55 @@ +package ocr + +import ( + "context" + "sync/atomic" + "time" + + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" + + "github.com/smartcontractkit/wasp" +) + +// Gun is a gun for the OCR load test +// it triggers new rounds for provided feed(aggregator) contract +type Gun struct { + roundNum atomic.Int64 + ocrInstances []contracts.OffchainAggregator + cc blockchain.EVMClient + l zerolog.Logger +} + +func NewGun(l zerolog.Logger, cc blockchain.EVMClient, ocrInstances []contracts.OffchainAggregator) *Gun { + return &Gun{ + l: l, + cc: cc, + ocrInstances: ocrInstances, + } +} + +func (m *Gun) Call(_ *wasp.Generator) *wasp.CallResult { + m.roundNum.Add(1) + requestedRound := m.roundNum.Load() + m.l.Info(). + Int64("RoundNum", requestedRound). + Str("FeedID", m.ocrInstances[0].Address()). + Msg("starting new round") + err := m.ocrInstances[0].RequestNewRound() + if err != nil { + return &wasp.CallResult{Error: err.Error(), Failed: true} + } + for { + time.Sleep(5 * time.Second) + lr, err := m.ocrInstances[0].GetLatestRound(context.Background()) + if err != nil { + return &wasp.CallResult{Error: err.Error(), Failed: true} + } + m.l.Info().Interface("LatestRound", lr).Msg("latest round") + if lr.RoundId.Int64() >= requestedRound { + return &wasp.CallResult{} + } + } +} diff --git a/integration-tests/load/ocr/helper.go b/integration-tests/load/ocr/helper.go new file mode 100644 index 00000000000..c35dc384d17 --- /dev/null +++ b/integration-tests/load/ocr/helper.go @@ -0,0 +1,68 @@ +package ocr + +import ( + "math/big" + "math/rand" + "time" + + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + + client2 "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +func SetupCluster( + cc blockchain.EVMClient, + cd contracts.ContractDeployer, + workerNodes []*client.ChainlinkK8sClient, +) (contracts.LinkToken, error) { + err := actions.FundChainlinkNodes(workerNodes, cc, big.NewFloat(3)) + if err != nil { + return nil, err + } + lt, err := cd.DeployLinkTokenContract() + if err != nil { + return nil, err + } + return lt, nil +} + +func SetupFeed( + cc blockchain.EVMClient, + msClient *client2.MockserverClient, + cd contracts.ContractDeployer, + bootstrapNode *client.ChainlinkK8sClient, + workerNodes []*client.ChainlinkK8sClient, + lt contracts.LinkToken, +) ([]contracts.OffchainAggregator, error) { + ocrInstances, err := actions.DeployOCRContracts(1, lt, cd, workerNodes, cc) + if err != nil { + return nil, err + } + err = actions.CreateOCRJobs(ocrInstances, bootstrapNode, workerNodes, 5, msClient, cc.GetChainID().String()) + if err != nil { + return nil, err + } + return ocrInstances, nil +} + +func SimulateEAActivity( + l zerolog.Logger, + eaChangeInterval time.Duration, + ocrInstances []contracts.OffchainAggregator, + workerNodes []*client.ChainlinkK8sClient, + msClient *client2.MockserverClient, +) { + go func() { + for { + time.Sleep(eaChangeInterval) + if err := actions.SetAllAdapterResponsesToTheSameValue(rand.Intn(1000), ocrInstances, workerNodes, msClient); err != nil { + l.Error().Err(err).Msg("failed to update mockserver responses") + } + } + }() +} diff --git a/integration-tests/load/ocr/ocr_test.go b/integration-tests/load/ocr/ocr_test.go new file mode 100644 index 00000000000..6bf1487125d --- /dev/null +++ b/integration-tests/load/ocr/ocr_test.go @@ -0,0 +1,71 @@ +package ocr + +import ( + "testing" + + "github.com/smartcontractkit/chainlink/integration-tests/k8s" + + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/logging" +) + +var ( + CommonTestLabels = map[string]string{ + "branch": "ocr_healthcheck_local", + "commit": "ocr_healthcheck_local", + } +) + +func TestOCRPerformance(t *testing.T) { + l := logging.GetTestLogger(t) + cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) + require.NoError(t, err) + lt, err := SetupCluster(cc, cd, workerNodes) + require.NoError(t, err) + ocrInstances, err := SetupFeed(cc, msClient, cd, bootstrapNode, workerNodes, lt) + require.NoError(t, err) + cfg, err := ReadConfig() + require.NoError(t, err) + SimulateEAActivity(l, cfg.Load.EAChangeInterval.Duration(), ocrInstances, workerNodes, msClient) + + p := wasp.NewProfile() + p.Add(wasp.NewGenerator(&wasp.Config{ + T: t, + GenName: "ocr", + LoadType: wasp.RPS, + CallTimeout: cfg.Load.VerificationTimeout.Duration(), + RateLimitUnitDuration: cfg.Load.RateLimitUnitDuration.Duration(), + Schedule: wasp.Plain(cfg.Load.Rate, cfg.Load.TestDuration.Duration()), + Gun: NewGun(l, cc, ocrInstances), + Labels: CommonTestLabels, + LokiConfig: wasp.NewEnvLokiConfig(), + })) + _, err = p.Run(true) + require.NoError(t, err) +} + +func TestOCRCapacity(t *testing.T) { + l := logging.GetTestLogger(t) + cc, msClient, cd, bootstrapNode, workerNodes, err := k8s.ConnectRemote(l) + require.NoError(t, err) + lt, err := SetupCluster(cc, cd, workerNodes) + require.NoError(t, err) + cfg, err := ReadConfig() + require.NoError(t, err) + + p := wasp.NewProfile() + p.Add(wasp.NewGenerator(&wasp.Config{ + T: t, + GenName: "ocr", + LoadType: wasp.VU, + CallTimeout: cfg.Volume.VerificationTimeout.Duration(), + Schedule: wasp.Plain(cfg.Volume.Rate, cfg.Volume.TestDuration.Duration()), + VU: NewVU(l, cfg.Volume.VURequestsPerUnit, cfg.Volume.RateLimitUnitDuration.Duration(), cc, lt, cd, bootstrapNode, workerNodes, msClient), + Labels: CommonTestLabels, + LokiConfig: wasp.NewEnvLokiConfig(), + })) + _, err = p.Run(true) + require.NoError(t, err) +} diff --git a/integration-tests/load/ocr/vu.go b/integration-tests/load/ocr/vu.go new file mode 100644 index 00000000000..a905ec011df --- /dev/null +++ b/integration-tests/load/ocr/vu.go @@ -0,0 +1,128 @@ +package ocr + +import ( + "context" + "sync/atomic" + "time" + + "github.com/rs/zerolog" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" + + "github.com/smartcontractkit/wasp" + "go.uber.org/ratelimit" + + client2 "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/contracts" +) + +// VU is a virtual user for the OCR load test +// it creates a feed and triggers new rounds +type VU struct { + rl ratelimit.Limiter + rate int + rateUnit time.Duration + roundNum atomic.Int64 + cc blockchain.EVMClient + lt contracts.LinkToken + cd contracts.ContractDeployer + bootstrapNode *client.ChainlinkK8sClient + workerNodes []*client.ChainlinkK8sClient + msClient *client2.MockserverClient + l zerolog.Logger + ocrInstances []contracts.OffchainAggregator + stop chan struct{} +} + +func NewVU( + l zerolog.Logger, + rate int, + rateUnit time.Duration, + cc blockchain.EVMClient, + lt contracts.LinkToken, + cd contracts.ContractDeployer, + bootstrapNode *client.ChainlinkK8sClient, + workerNodes []*client.ChainlinkK8sClient, + msClient *client2.MockserverClient, +) *VU { + return &VU{ + rl: ratelimit.New(rate, ratelimit.Per(rateUnit)), + rate: rate, + rateUnit: rateUnit, + l: l, + cc: cc, + lt: lt, + cd: cd, + msClient: msClient, + bootstrapNode: bootstrapNode, + workerNodes: workerNodes, + } +} + +func (m *VU) Clone(_ *wasp.Generator) wasp.VirtualUser { + return &VU{ + stop: make(chan struct{}, 1), + rl: ratelimit.New(m.rate, ratelimit.Per(m.rateUnit)), + rate: m.rate, + rateUnit: m.rateUnit, + l: m.l, + cc: m.cc, + lt: m.lt, + cd: m.cd, + msClient: m.msClient, + bootstrapNode: m.bootstrapNode, + workerNodes: m.workerNodes, + } +} + +func (m *VU) Setup(_ *wasp.Generator) error { + ocrInstances, err := actions.DeployOCRContracts(1, m.lt, m.cd, m.workerNodes, m.cc) + if err != nil { + return err + } + err = actions.CreateOCRJobs(ocrInstances, m.bootstrapNode, m.workerNodes, 5, m.msClient, m.cc.GetChainID().String()) + if err != nil { + return err + } + m.ocrInstances = ocrInstances + return nil +} + +func (m *VU) Teardown(_ *wasp.Generator) error { + return nil +} + +func (m *VU) Call(l *wasp.Generator) { + m.rl.Take() + m.roundNum.Add(1) + requestedRound := m.roundNum.Load() + m.l.Info(). + Int64("RoundNum", requestedRound). + Str("FeedID", m.ocrInstances[0].Address()). + Msg("starting new round") + err := m.ocrInstances[0].RequestNewRound() + if err != nil { + l.ResponsesChan <- &wasp.CallResult{Error: err.Error(), Failed: true} + } + for { + time.Sleep(5 * time.Second) + lr, err := m.ocrInstances[0].GetLatestRound(context.Background()) + if err != nil { + l.ResponsesChan <- &wasp.CallResult{Error: err.Error(), Failed: true} + } + m.l.Info().Interface("LatestRound", lr).Msg("latest round") + if lr.RoundId.Int64() >= requestedRound { + l.ResponsesChan <- &wasp.CallResult{} + } + } +} + +func (m *VU) Stop(_ *wasp.Generator) { + m.stop <- struct{}{} +} + +func (m *VU) StopChan() chan struct{} { + return m.stop +} From 1f5182b61941f975aac92d9706aedd018065787a Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Thu, 30 Nov 2023 09:17:06 -0500 Subject: [PATCH 233/271] Update Test Go and CTF Version (#11413) --- integration-tests/go.mod | 4 ++-- integration-tests/go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/go.mod b/integration-tests/go.mod index e801e25de5f..e83859f2f46 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -1,6 +1,6 @@ module github.com/smartcontractkit/chainlink/integration-tests -go 1.21.3 +go 1.21.4 // Make sure we're working with the latest chainlink libs replace github.com/smartcontractkit/chainlink/v2 => ../ @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 - github.com/smartcontractkit/chainlink-testing-framework v1.19.5 + github.com/smartcontractkit/chainlink-testing-framework v1.19.7 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 855d97d5e40..9a647daaffb 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2386,8 +2386,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= -github.com/smartcontractkit/chainlink-testing-framework v1.19.5 h1:Iq1L7wCA8IkBBtP3p6W2ttu8v9cJp95spusnozW1UrA= -github.com/smartcontractkit/chainlink-testing-framework v1.19.5/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= +github.com/smartcontractkit/chainlink-testing-framework v1.19.7 h1:8oEduPXC5kBwnNwE5qdK+h/NZ2qOlxK0hgtedS+/St0= +github.com/smartcontractkit/chainlink-testing-framework v1.19.7/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= From b29c327b97f2ee713dfafb94d603489acf040c55 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 30 Nov 2023 09:42:02 -0600 Subject: [PATCH 234/271] core/chains/evm/config/toml/defaults: fix sepolia link address (#11426) --- core/chains/evm/client/client.go | 5 ++++- core/chains/evm/client/rpc_client.go | 4 +++- core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml | 2 +- docs/CHANGELOG.md | 4 ++++ docs/CONFIG.md | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/chains/evm/client/client.go b/core/chains/evm/client/client.go index cccda9914fc..688cc3c9bea 100644 --- a/core/chains/evm/client/client.go +++ b/core/chains/evm/client/client.go @@ -2,6 +2,7 @@ package client import ( "context" + "fmt" "math/big" "strings" "time" @@ -166,7 +167,9 @@ func (client *client) TokenBalance(ctx context.Context, address common.Address, if err != nil { return numLinkBigInt, err } - numLinkBigInt.SetString(result, 0) + if _, ok := numLinkBigInt.SetString(result, 0); !ok { + return nil, fmt.Errorf("failed to parse int: %s", result) + } return numLinkBigInt, nil } diff --git a/core/chains/evm/client/rpc_client.go b/core/chains/evm/client/rpc_client.go index fc9c348a572..01851c4ae90 100644 --- a/core/chains/evm/client/rpc_client.go +++ b/core/chains/evm/client/rpc_client.go @@ -866,7 +866,9 @@ func (r *rpcClient) TokenBalance(ctx context.Context, address common.Address, co if err != nil { return numLinkBigInt, err } - numLinkBigInt.SetString(result, 0) + if _, ok := numLinkBigInt.SetString(result, 0); !ok { + return nil, fmt.Errorf("failed to parse int: %s", result) + } return numLinkBigInt, nil } diff --git a/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml b/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml index 7b979c2fd8b..27dda602962 100644 --- a/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml +++ b/core/chains/evm/config/toml/defaults/Ethereum_Sepolia.toml @@ -1,5 +1,5 @@ ChainID = '11155111' -LinkContractAddress = '0xb227f007804c16546Bd054dfED2E7A1fD5437678' +LinkContractAddress = '0x779877A7B0D9E8603169DdbD7836e478b4624789' MinContractPayment = '0.1 link' [GasEstimator] diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e282d047892..dc2f774cd31 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -67,6 +67,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `mercury_insufficient_blocks_count` - `mercury_zero_blocks_count` +### Fixed + +- Corrected Ethereum Sepolia `LinkContractAddress` to `0x779877A7B0D9E8603169DdbD7836e478b4624789` + ... diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 38aac7085e8..3568abe7a0e 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -5271,7 +5271,7 @@ BlockBackfillDepth = 10 BlockBackfillSkip = false FinalityDepth = 50 FinalityTagEnabled = false -LinkContractAddress = '0xb227f007804c16546Bd054dfED2E7A1fD5437678' +LinkContractAddress = '0x779877A7B0D9E8603169DdbD7836e478b4624789' LogBackfillBatchSize = 1000 LogPollInterval = '15s' LogKeepBlocksDepth = 100000 From 31bf23accd80bec9e7aaffc7739328be2cc0778a Mon Sep 17 00:00:00 2001 From: george-dorin <120329946+george-dorin@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:16:19 +0200 Subject: [PATCH 235/271] Fix telemetry manager health report (#11397) * Fix incorrect health report * Add network and chainID to logger and service names * Update helpers_test * Update services name * Remove unused * Add changelog * Implement feedback --- core/services/synchronization/helpers_test.go | 4 ++-- .../telemetry_ingress_batch_client.go | 4 ++-- .../synchronization/telemetry_ingress_client.go | 4 ++-- core/services/telemetry/manager.go | 13 +++++-------- core/services/telemetry/manager_test.go | 8 +++----- docs/CHANGELOG.md | 3 +++ 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/core/services/synchronization/helpers_test.go b/core/services/synchronization/helpers_test.go index 14aaf5a7a02..7bb2dde7633 100644 --- a/core/services/synchronization/helpers_test.go +++ b/core/services/synchronization/helpers_test.go @@ -12,14 +12,14 @@ import ( // NewTestTelemetryIngressClient calls NewTelemetryIngressClient and injects telemClient. func NewTestTelemetryIngressClient(t *testing.T, url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, telemClient telemPb.TelemClient) TelemetryService { - tc := NewTelemetryIngressClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100) + tc := NewTelemetryIngressClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100, "test", "test") tc.(*telemetryIngressClient).telemClient = telemClient return tc } // NewTestTelemetryIngressBatchClient calls NewTelemetryIngressBatchClient and injects telemClient. func NewTestTelemetryIngressBatchClient(t *testing.T, url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, telemClient telemPb.TelemClient, sendInterval time.Duration, uniconn bool) TelemetryService { - tc := NewTelemetryIngressBatchClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100, 50, sendInterval, time.Second, uniconn) + tc := NewTelemetryIngressBatchClient(url, serverPubKeyHex, ks, logging, logger.TestLogger(t), 100, 50, sendInterval, time.Second, uniconn, "test", "test") tc.(*telemetryIngressBatchClient).close = func() error { return nil } tc.(*telemetryIngressBatchClient).telemClient = telemClient return tc diff --git a/core/services/synchronization/telemetry_ingress_batch_client.go b/core/services/synchronization/telemetry_ingress_batch_client.go index c551ac85b3e..5f286f1617b 100644 --- a/core/services/synchronization/telemetry_ingress_batch_client.go +++ b/core/services/synchronization/telemetry_ingress_batch_client.go @@ -66,7 +66,7 @@ type telemetryIngressBatchClient struct { // NewTelemetryIngressBatchClient returns a client backed by wsrpc that // can send telemetry to the telemetry ingress server -func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint, telemMaxBatchSize uint, telemSendInterval time.Duration, telemSendTimeout time.Duration, useUniconn bool) TelemetryService { +func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint, telemMaxBatchSize uint, telemSendInterval time.Duration, telemSendTimeout time.Duration, useUniconn bool, network string, chainID string) TelemetryService { return &telemetryIngressBatchClient{ telemBufferSize: telemBufferSize, telemMaxBatchSize: telemMaxBatchSize, @@ -77,7 +77,7 @@ func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks key serverPubKeyHex: serverPubKeyHex, globalLogger: lggr, logging: logging, - lggr: lggr.Named("TelemetryIngressBatchClient"), + lggr: lggr.Named("TelemetryIngressBatchClient").Named(network).Named(chainID), chDone: make(chan struct{}), workers: make(map[string]*telemetryIngressBatchWorker), useUniConn: useUniconn, diff --git a/core/services/synchronization/telemetry_ingress_client.go b/core/services/synchronization/telemetry_ingress_client.go index b566199fdc0..bcad01622ea 100644 --- a/core/services/synchronization/telemetry_ingress_client.go +++ b/core/services/synchronization/telemetry_ingress_client.go @@ -52,13 +52,13 @@ type telemetryIngressClient struct { // NewTelemetryIngressClient returns a client backed by wsrpc that // can send telemetry to the telemetry ingress server -func NewTelemetryIngressClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint) TelemetryService { +func NewTelemetryIngressClient(url *url.URL, serverPubKeyHex string, ks keystore.CSA, logging bool, lggr logger.Logger, telemBufferSize uint, network string, chainID string) TelemetryService { return &telemetryIngressClient{ url: url, ks: ks, serverPubKeyHex: serverPubKeyHex, logging: logging, - lggr: lggr.Named("TelemetryIngressClient"), + lggr: lggr.Named("TelemetryIngressClient").Named(network).Named(chainID), chTelemetry: make(chan TelemPayload, telemBufferSize), chDone: make(services.StopChan), } diff --git a/core/services/telemetry/manager.go b/core/services/telemetry/manager.go index d760d13b2b0..c457aca5bf8 100644 --- a/core/services/telemetry/manager.go +++ b/core/services/telemetry/manager.go @@ -2,7 +2,6 @@ package telemetry import ( "context" - "fmt" "net/url" "strings" "time" @@ -68,7 +67,6 @@ func (l *legacyEndpointConfig) URL() *url.URL { } type telemetryEndpoint struct { - services.StateMachine ChainID string Network string URL *url.URL @@ -140,11 +138,10 @@ func (m *Manager) Name() string { } func (m *Manager) HealthReport() map[string]error { - hr := make(map[string]error) - hr[m.lggr.Name()] = m.Healthy() + hr := map[string]error{m.Name(): m.Healthy()} + for _, e := range m.endpoints { - name := fmt.Sprintf("%s.%s.%s", m.lggr.Name(), e.Network, e.ChainID) - hr[name] = e.Healthy() + services.CopyHealth(hr, e.client.HealthReport()) } return hr } @@ -190,9 +187,9 @@ func (m *Manager) addEndpoint(e config.TelemetryIngressEndpoint) error { var tClient synchronization.TelemetryService if m.useBatchSend { - tClient = synchronization.NewTelemetryIngressBatchClient(e.URL(), e.ServerPubKey(), m.ks, m.logging, m.lggr, m.bufferSize, m.maxBatchSize, m.sendInterval, m.sendTimeout, m.uniConn) + tClient = synchronization.NewTelemetryIngressBatchClient(e.URL(), e.ServerPubKey(), m.ks, m.logging, m.lggr, m.bufferSize, m.maxBatchSize, m.sendInterval, m.sendTimeout, m.uniConn, e.Network(), e.ChainID()) } else { - tClient = synchronization.NewTelemetryIngressClient(e.URL(), e.ServerPubKey(), m.ks, m.logging, m.lggr, m.bufferSize) + tClient = synchronization.NewTelemetryIngressClient(e.URL(), e.ServerPubKey(), m.ks, m.logging, m.lggr, m.bufferSize, e.Network(), e.ChainID()) } te := telemetryEndpoint{ diff --git a/core/services/telemetry/manager_test.go b/core/services/telemetry/manager_test.go index f09e4d3482f..8564be8466f 100644 --- a/core/services/telemetry/manager_test.go +++ b/core/services/telemetry/manager_test.go @@ -14,7 +14,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" - "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/config/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -246,10 +245,9 @@ func TestCorrectEndpointRouting(t *testing.T) { }) tm.endpoints[i] = &telemetryEndpoint{ - StateMachine: services.StateMachine{}, - ChainID: e.chainID, - Network: e.network, - client: clientMock, + ChainID: e.chainID, + Network: e.network, + client: clientMock, } } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index dc2f774cd31..929fec5a3e6 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] +### Fixed +- Fixed a bug that caused the Telemetry Manager to report incorrect health + ### Added - Added a tracker component to the txmgr for tracking and gracefully handling abandoned transactions. Abandoned transactions occur when a fromAddress is removed from the keystore by a node operator. The tracker gives abandoned transactions a chance to be finalized on chain, or marks them as fatal_error if they are not finalized within a specified time to live (default 6hrs). From 82ed297d4aed41024a2e10f7b8cbf086d44180d2 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 30 Nov 2023 12:22:14 -0500 Subject: [PATCH 236/271] Increase blocks number for mercury v1 to 10 (#11410) * Increase blocks number for mercury v1 to 10 * Fix test --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 +- .../relay/evm/mercury/v1/data_source_test.go | 42 ++++++------------- go.mod | 2 +- go.sum | 4 +- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 +- 7 files changed, 21 insertions(+), 39 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 1eb186931f8..c1ed59097cc 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 2a77f2066db..de86a457218 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1504,8 +1504,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 h1:HJCPvJ+ZjU9TSX4YD5rxBJqGAvqhDfzoJgI3WmfeWeI= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde h1:yulX+cnJbiS/iACjOdJfFtD5bD9M6TnXy7Og/PBafew= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 6eb9f430c60..45a99f29196 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -356,49 +356,31 @@ func TestMercury_Observe(t *testing.T) { ht2.AssertExpectations(t) }) t.Run("when chain is long enough", func(t *testing.T) { - h1 := &evmtypes.Head{ - Number: 1, - } - h2 := &evmtypes.Head{ - Number: 2, - Parent: h1, - } - h3 := &evmtypes.Head{ - Number: 3, - Parent: h2, - } - h4 := &evmtypes.Head{ - Number: 4, - Parent: h3, - } - h5 := &evmtypes.Head{ - Number: 5, - Parent: h4, - } - h6 := &evmtypes.Head{ - Number: 6, - Parent: h5, + heads := make([]*evmtypes.Head, nBlocksObservation+5) + for i := range heads { + heads[i] = &evmtypes.Head{Number: int64(i)} + if i > 0 { + heads[i].Parent = heads[i-1] + } } ht2 := commonmocks.NewHeadTracker[*evmtypes.Head, common.Hash](t) - ht2.On("LatestChain").Return(h6) + ht2.On("LatestChain").Return(heads[len(heads)-1]) ds.chainReader = evm.NewChainReader(ht2) obs, err := ds.Observe(ctx, repts, true) assert.NoError(t, err) - assert.Len(t, obs.LatestBlocks, 5) - assert.Equal(t, 6, int(obs.LatestBlocks[0].Num)) - assert.Equal(t, 5, int(obs.LatestBlocks[1].Num)) - assert.Equal(t, 4, int(obs.LatestBlocks[2].Num)) - assert.Equal(t, 3, int(obs.LatestBlocks[3].Num)) - assert.Equal(t, 2, int(obs.LatestBlocks[4].Num)) + assert.Len(t, obs.LatestBlocks, nBlocksObservation) + highestBlockNum := heads[len(heads)-1].Number + for i := range obs.LatestBlocks { + assert.Equal(t, int(highestBlockNum)-i, int(obs.LatestBlocks[i].Num)) + } ht2.AssertExpectations(t) }) t.Run("when chain reader returns an error", func(t *testing.T) { - ds.chainReader = &mockChainReader{ err: io.EOF, obs: nil, diff --git a/go.mod b/go.mod index c105c1c6000..688f5810529 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 diff --git a/go.sum b/go.sum index 18d78d4815e..d522aabb08a 100644 --- a/go.sum +++ b/go.sum @@ -1507,8 +1507,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 h1:HJCPvJ+ZjU9TSX4YD5rxBJqGAvqhDfzoJgI3WmfeWeI= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde h1:yulX+cnJbiS/iACjOdJfFtD5bD9M6TnXy7Og/PBafew= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index e83859f2f46..b03819e2066 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde github.com/smartcontractkit/chainlink-testing-framework v1.19.7 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 9a647daaffb..74fe9e4dc4d 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248 h1:HJCPvJ+ZjU9TSX4YD5rxBJqGAvqhDfzoJgI3WmfeWeI= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231127213516-5b67a57f9248/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde h1:yulX+cnJbiS/iACjOdJfFtD5bD9M6TnXy7Og/PBafew= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= From c21f4ffa9dcf7a836630baa5ddadd61510e57e5f Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Thu, 30 Nov 2023 15:20:49 -0500 Subject: [PATCH 237/271] [BCI-2151] Refactor prom reporter db API (#11394) * Refactor prom reporter db API * Add test case * lint * Use Txm from LegacyChainContainer * Use legacyevm * lint * Use TXM chainID * lint --------- Co-authored-by: Prashant Yadav <34992934+prashantkumar1982@users.noreply.github.com> --- common/txmgr/mocks/tx_manager.go | 74 ++++++++++++++++++ common/txmgr/txmgr.go | 29 ++++++- common/txmgr/types/mocks/tx_store.go | 74 ++++++++++++++++++ common/txmgr/types/tx_store.go | 4 + core/chains/evm/txmgr/evm_tx_store.go | 47 ++++++++++++ core/chains/evm/txmgr/evm_tx_store_test.go | 76 +++++++++++++++++++ core/chains/evm/txmgr/mocks/evm_tx_store.go | 74 ++++++++++++++++++ core/internal/cltest/mocks.go | 14 ++++ core/services/chainlink/application.go | 10 +-- core/services/promreporter/prom_reporter.go | 63 ++++++++++----- .../promreporter/prom_reporter_test.go | 41 ++++++++-- docs/CHANGELOG.md | 1 + 12 files changed, 476 insertions(+), 31 deletions(-) diff --git a/common/txmgr/mocks/tx_manager.go b/common/txmgr/mocks/tx_manager.go index 89abf1dea51..27077218f6e 100644 --- a/common/txmgr/mocks/tx_manager.go +++ b/common/txmgr/mocks/tx_manager.go @@ -9,6 +9,8 @@ import ( feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" mock "github.com/stretchr/testify/mock" + null "gopkg.in/guregu/null.v4" + txmgr "github.com/smartcontractkit/chainlink/v2/common/txmgr" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -35,6 +37,30 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Close( return r0 } +// CountTransactionsByState provides a mock function with given fields: ctx, state +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState) (uint32, error) { + ret := _m.Called(ctx, state) + + var r0 uint32 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxState) (uint32, error)); ok { + return rf(ctx, state) + } + if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxState) uint32); ok { + r0 = rf(ctx, state) + } else { + r0 = ret.Get(0).(uint32) + } + + if rf, ok := ret.Get(1).(func(context.Context, txmgrtypes.TxState) error); ok { + r1 = rf(ctx, state) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CreateTransaction provides a mock function with given fields: ctx, txRequest func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) CreateTransaction(ctx context.Context, txRequest txmgrtypes.TxRequest[ADDR, TX_HASH]) (txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, txRequest) @@ -59,6 +85,54 @@ func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Create return r0, r1 } +// FindEarliestUnconfirmedBroadcastTime provides a mock function with given fields: ctx +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEarliestUnconfirmedBroadcastTime(ctx context.Context) (null.Time, error) { + ret := _m.Called(ctx) + + var r0 null.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (null.Time, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) null.Time); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(null.Time) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindEarliestUnconfirmedTxAttemptBlock provides a mock function with given fields: ctx +func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context) (null.Int, error) { + ret := _m.Called(ctx) + + var r0 null.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (null.Int, error)); ok { + return rf(ctx) + } + if rf, ok := ret.Get(0).(func(context.Context) null.Int); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(null.Int) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindTxesByMetaFieldAndStates provides a mock function with given fields: ctx, metaField, metaValue, states, chainID func (_m *TxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesByMetaFieldAndStates(ctx context.Context, metaField string, metaValue string, states []txmgrtypes.TxState, chainID *big.Int) ([]*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], error) { ret := _m.Called(ctx, metaField, metaValue, states, chainID) diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 0a50bb66385..3fbdb852f8b 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -10,10 +10,10 @@ import ( "time" "github.com/google/uuid" + nullv4 "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" - feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" "github.com/smartcontractkit/chainlink/v2/common/types" @@ -55,6 +55,9 @@ type TxManager[ FindTxesWithMetaFieldByReceiptBlockNum(ctx context.Context, metaField string, blockNum int64, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) // Find transactions loaded with transaction attempts and receipts by transaction IDs and states FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + FindEarliestUnconfirmedBroadcastTime(ctx context.Context) (nullv4.Time, error) + FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context) (nullv4.Int, error) + CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState) (count uint32, err error) } type reset struct { @@ -576,6 +579,18 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindTxesWi return } +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarliestUnconfirmedBroadcastTime(ctx context.Context) (nullv4.Time, error) { + return b.txStore.FindEarliestUnconfirmedBroadcastTime(ctx, b.chainID) +} + +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context) (nullv4.Int, error) { + return b.txStore.FindEarliestUnconfirmedTxAttemptBlock(ctx, b.chainID) +} + +func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState) (count uint32, err error) { + return b.txStore.CountTransactionsByState(ctx, state, b.chainID) +} + type NullTxManager[ CHAIN_ID types.ID, HEAD types.Head[BLOCK_HASH], @@ -642,3 +657,15 @@ func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Fin func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindTxesWithAttemptsAndReceiptsByIdsAndState(ctx context.Context, ids []big.Int, states []txmgrtypes.TxState, chainID *big.Int) (txes []*txmgrtypes.Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) { return txes, errors.New(n.ErrMsg) } + +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEarliestUnconfirmedBroadcastTime(ctx context.Context) (nullv4.Time, error) { + return nullv4.Time{}, errors.New(n.ErrMsg) +} + +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context) (nullv4.Int, error) { + return nullv4.Int{}, errors.New(n.ErrMsg) +} + +func (n *NullTxManager[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState) (count uint32, err error) { + return count, errors.New(n.ErrMsg) +} diff --git a/common/txmgr/types/mocks/tx_store.go b/common/txmgr/types/mocks/tx_store.go index 0a7738fd68a..df1528a4c24 100644 --- a/common/txmgr/types/mocks/tx_store.go +++ b/common/txmgr/types/mocks/tx_store.go @@ -9,6 +9,8 @@ import ( feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" mock "github.com/stretchr/testify/mock" + null "gopkg.in/guregu/null.v4" + time "time" txmgrtypes "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -56,6 +58,30 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() { _m.Called() } +// CountTransactionsByState provides a mock function with given fields: ctx, state, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState, chainID CHAIN_ID) (uint32, error) { + ret := _m.Called(ctx, state, chainID) + + var r0 uint32 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxState, CHAIN_ID) (uint32, error)); ok { + return rf(ctx, state, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, txmgrtypes.TxState, CHAIN_ID) uint32); ok { + r0 = rf(ctx, state, chainID) + } else { + r0 = ret.Get(0).(uint32) + } + + if rf, ok := ret.Get(1).(func(context.Context, txmgrtypes.TxState, CHAIN_ID) error); ok { + r1 = rf(ctx, state, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CountUnconfirmedTransactions provides a mock function with given fields: ctx, fromAddress, chainID func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) CountUnconfirmedTransactions(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (uint32, error) { ret := _m.Called(ctx, fromAddress, chainID) @@ -142,6 +168,54 @@ func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) DeleteInPro return r0 } +// FindEarliestUnconfirmedBroadcastTime provides a mock function with given fields: ctx, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID CHAIN_ID) (null.Time, error) { + ret := _m.Called(ctx, chainID) + + var r0 null.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) (null.Time, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) null.Time); ok { + r0 = rf(ctx, chainID) + } else { + r0 = ret.Get(0).(null.Time) + } + + if rf, ok := ret.Get(1).(func(context.Context, CHAIN_ID) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindEarliestUnconfirmedTxAttemptBlock provides a mock function with given fields: ctx, chainID +func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID CHAIN_ID) (null.Int, error) { + ret := _m.Called(ctx, chainID) + + var r0 null.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) (null.Int, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, CHAIN_ID) null.Int); ok { + r0 = rf(ctx, chainID) + } else { + r0 = ret.Get(0).(null.Int) + } + + if rf, ok := ret.Get(1).(func(context.Context, CHAIN_ID) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindLatestSequence provides a mock function with given fields: ctx, fromAddress, chainId func (_m *TxStore[ADDR, CHAIN_ID, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) FindLatestSequence(ctx context.Context, fromAddress ADDR, chainId CHAIN_ID) (SEQ, error) { ret := _m.Called(ctx, fromAddress, chainId) diff --git a/common/txmgr/types/tx_store.go b/common/txmgr/types/tx_store.go index 251135795fd..57ecf28d589 100644 --- a/common/txmgr/types/tx_store.go +++ b/common/txmgr/types/tx_store.go @@ -6,6 +6,7 @@ import ( "time" "github.com/google/uuid" + "gopkg.in/guregu/null.v4" feetypes "github.com/smartcontractkit/chainlink/v2/common/fee/types" "github.com/smartcontractkit/chainlink/v2/common/types" @@ -64,6 +65,7 @@ type TransactionStore[ FEE feetypes.Fee, ] interface { CountUnconfirmedTransactions(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (count uint32, err error) + CountTransactionsByState(ctx context.Context, state TxState, chainID CHAIN_ID) (count uint32, err error) CountUnstartedTransactions(ctx context.Context, fromAddress ADDR, chainID CHAIN_ID) (count uint32, err error) CreateTransaction(ctx context.Context, txRequest TxRequest[ADDR, TX_HASH], chainID CHAIN_ID) (tx Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) DeleteInProgressAttempt(ctx context.Context, attempt TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) error @@ -79,6 +81,8 @@ type TransactionStore[ FindTxWithSequence(ctx context.Context, fromAddress ADDR, seq SEQ) (etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) FindNextUnstartedTransactionFromAddress(ctx context.Context, etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], fromAddress ADDR, chainID CHAIN_ID) error FindTransactionsConfirmedInBlockRange(ctx context.Context, highBlockNumber, lowBlockNumber int64, chainID CHAIN_ID) (etxs []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) + FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID CHAIN_ID) (null.Time, error) + FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID CHAIN_ID) (null.Int, error) GetTxInProgress(ctx context.Context, fromAddress ADDR) (etx *Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) GetInProgressTxAttempts(ctx context.Context, address ADDR, chainID CHAIN_ID) (attempts []TxAttempt[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) GetNonFatalTransactions(ctx context.Context, chainID CHAIN_ID) (txs []*Tx[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE], err error) diff --git a/core/chains/evm/txmgr/evm_tx_store.go b/core/chains/evm/txmgr/evm_tx_store.go index e36ca611e16..730809e8dda 100644 --- a/core/chains/evm/txmgr/evm_tx_store.go +++ b/core/chains/evm/txmgr/evm_tx_store.go @@ -1131,6 +1131,39 @@ ORDER BY nonce ASC return etxs, pkgerrors.Wrap(err, "FindTransactionsConfirmedInBlockRange failed") } +func (o *evmTxStore) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID *big.Int) (broadcastAt nullv4.Time, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + err = qq.Transaction(func(tx pg.Queryer) error { + if err = qq.QueryRowContext(ctx, `SELECT min(initial_broadcast_at) FROM evm.txes WHERE state = 'unconfirmed' AND evm_chain_id = $1`, chainID.String()).Scan(&broadcastAt); err != nil { + return fmt.Errorf("failed to query for unconfirmed eth_tx count: %w", err) + } + return nil + }, pg.OptReadOnlyTx()) + return broadcastAt, err +} + +func (o *evmTxStore) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID *big.Int) (earliestUnconfirmedTxBlock nullv4.Int, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + err = qq.Transaction(func(tx pg.Queryer) error { + err = qq.QueryRowContext(ctx, ` +SELECT MIN(broadcast_before_block_num) FROM evm.tx_attempts +JOIN evm.txes ON evm.txes.id = evm.tx_attempts.eth_tx_id +WHERE evm.txes.state = 'unconfirmed' +AND evm_chain_id = $1`, chainID.String()).Scan(&earliestUnconfirmedTxBlock) + if err != nil { + return fmt.Errorf("failed to query for earliest unconfirmed tx block: %w", err) + } + return nil + }, pg.OptReadOnlyTx()) + return earliestUnconfirmedTxBlock, err +} + func (o *evmTxStore) IsTxFinalized(ctx context.Context, blockHeight int64, txID int64, chainID *big.Int) (finalized bool, err error) { var cancel context.CancelFunc ctx, cancel = o.mergeContexts(ctx) @@ -1733,6 +1766,20 @@ func (o *evmTxStore) CountUnconfirmedTransactions(ctx context.Context, fromAddre return o.countTransactionsWithState(ctx, fromAddress, txmgr.TxUnconfirmed, chainID) } +// CountTransactionsByState returns the number of transactions with any fromAddress in the given state +func (o *evmTxStore) CountTransactionsByState(ctx context.Context, state txmgrtypes.TxState, chainID *big.Int) (count uint32, err error) { + var cancel context.CancelFunc + ctx, cancel = o.mergeContexts(ctx) + defer cancel() + qq := o.q.WithOpts(pg.WithParentCtx(ctx)) + err = qq.Get(&count, `SELECT count(*) FROM evm.txes WHERE state = $1 AND evm_chain_id = $2`, + state, chainID.String()) + if err != nil { + return 0, fmt.Errorf("failed to CountTransactionsByState: %w", err) + } + return count, nil +} + // CountUnstartedTransactions returns the number of unconfirmed transactions func (o *evmTxStore) CountUnstartedTransactions(ctx context.Context, fromAddress common.Address, chainID *big.Int) (count uint32, err error) { return o.countTransactionsWithState(ctx, fromAddress, txmgr.TxUnstarted, chainID) diff --git a/core/chains/evm/txmgr/evm_tx_store_test.go b/core/chains/evm/txmgr/evm_tx_store_test.go index e68641735ee..0b4287b6f6c 100644 --- a/core/chains/evm/txmgr/evm_tx_store_test.go +++ b/core/chains/evm/txmgr/evm_tx_store_test.go @@ -847,6 +847,61 @@ func TestORM_FindTransactionsConfirmedInBlockRange(t *testing.T) { }) } +func TestORM_FindEarliestUnconfirmedBroadcastTime(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + + t.Run("no unconfirmed eth txes", func(t *testing.T) { + broadcastAt, err := txStore.FindEarliestUnconfirmedBroadcastTime(testutils.Context(t), ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.False(t, broadcastAt.Valid) + }) + + t.Run("verify broadcast time", func(t *testing.T) { + tx := cltest.MustInsertUnconfirmedEthTx(t, txStore, 123, fromAddress) + broadcastAt, err := txStore.FindEarliestUnconfirmedBroadcastTime(testutils.Context(t), ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.True(t, broadcastAt.Ptr().Equal(*tx.BroadcastAt)) + }) +} + +func TestORM_FindEarliestUnconfirmedTxAttemptBlock(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := newTestChainScopedConfig(t) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + _, fromAddress := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + _, fromAddress2 := cltest.MustInsertRandomKeyReturningState(t, ethKeyStore) + + t.Run("no earliest unconfirmed tx block", func(t *testing.T) { + earliestBlock, err := txStore.FindEarliestUnconfirmedTxAttemptBlock(testutils.Context(t), ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.False(t, earliestBlock.Valid) + }) + + t.Run("verify earliest unconfirmed tx block", func(t *testing.T) { + var blockNum int64 = 2 + tx := mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 123, blockNum, time.Now(), fromAddress) + _ = mustInsertConfirmedMissingReceiptEthTxWithLegacyAttempt(t, txStore, 123, blockNum, time.Now().Add(time.Minute), fromAddress2) + err := txStore.UpdateTxsUnconfirmed(testutils.Context(t), []int64{tx.ID}) + require.NoError(t, err) + + earliestBlock, err := txStore.FindEarliestUnconfirmedTxAttemptBlock(testutils.Context(t), ethClient.ConfiguredChainID()) + require.NoError(t, err) + require.True(t, earliestBlock.Valid) + require.Equal(t, blockNum, earliestBlock.Int64) + }) +} + func TestORM_SaveInsufficientEthAttempt(t *testing.T) { t.Parallel() @@ -1511,6 +1566,27 @@ func TestORM_CountUnconfirmedTransactions(t *testing.T) { assert.Equal(t, int(count), 3) } +func TestORM_CountTransactionsByState(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewGeneralConfig(t, nil) + txStore := cltest.NewTestTxStore(t, db, cfg.Database()) + ethKeyStore := cltest.NewKeyStore(t, db, cfg.Database()).Eth() + + _, fromAddress1 := cltest.MustInsertRandomKey(t, ethKeyStore) + _, fromAddress2 := cltest.MustInsertRandomKey(t, ethKeyStore) + _, fromAddress3 := cltest.MustInsertRandomKey(t, ethKeyStore) + + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 0, fromAddress1) + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 1, fromAddress2) + cltest.MustInsertUnconfirmedEthTxWithBroadcastLegacyAttempt(t, txStore, 2, fromAddress3) + + count, err := txStore.CountTransactionsByState(testutils.Context(t), txmgrcommon.TxUnconfirmed, &cltest.FixtureChainID) + require.NoError(t, err) + assert.Equal(t, int(count), 3) +} + func TestORM_CountUnstartedTransactions(t *testing.T) { t.Parallel() diff --git a/core/chains/evm/txmgr/mocks/evm_tx_store.go b/core/chains/evm/txmgr/mocks/evm_tx_store.go index 00efc1add98..bf3088b2aa1 100644 --- a/core/chains/evm/txmgr/mocks/evm_tx_store.go +++ b/core/chains/evm/txmgr/mocks/evm_tx_store.go @@ -14,6 +14,8 @@ import ( mock "github.com/stretchr/testify/mock" + null "gopkg.in/guregu/null.v4" + time "time" types "github.com/smartcontractkit/chainlink/v2/common/txmgr/types" @@ -59,6 +61,30 @@ func (_m *EvmTxStore) Close() { _m.Called() } +// CountTransactionsByState provides a mock function with given fields: ctx, state, chainID +func (_m *EvmTxStore) CountTransactionsByState(ctx context.Context, state types.TxState, chainID *big.Int) (uint32, error) { + ret := _m.Called(ctx, state, chainID) + + var r0 uint32 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, types.TxState, *big.Int) (uint32, error)); ok { + return rf(ctx, state, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, types.TxState, *big.Int) uint32); ok { + r0 = rf(ctx, state, chainID) + } else { + r0 = ret.Get(0).(uint32) + } + + if rf, ok := ret.Get(1).(func(context.Context, types.TxState, *big.Int) error); ok { + r1 = rf(ctx, state, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CountUnconfirmedTransactions provides a mock function with given fields: ctx, fromAddress, chainID func (_m *EvmTxStore) CountUnconfirmedTransactions(ctx context.Context, fromAddress common.Address, chainID *big.Int) (uint32, error) { ret := _m.Called(ctx, fromAddress, chainID) @@ -145,6 +171,54 @@ func (_m *EvmTxStore) DeleteInProgressAttempt(ctx context.Context, attempt types return r0 } +// FindEarliestUnconfirmedBroadcastTime provides a mock function with given fields: ctx, chainID +func (_m *EvmTxStore) FindEarliestUnconfirmedBroadcastTime(ctx context.Context, chainID *big.Int) (null.Time, error) { + ret := _m.Called(ctx, chainID) + + var r0 null.Time + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (null.Time, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) null.Time); ok { + r0 = rf(ctx, chainID) + } else { + r0 = ret.Get(0).(null.Time) + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindEarliestUnconfirmedTxAttemptBlock provides a mock function with given fields: ctx, chainID +func (_m *EvmTxStore) FindEarliestUnconfirmedTxAttemptBlock(ctx context.Context, chainID *big.Int) (null.Int, error) { + ret := _m.Called(ctx, chainID) + + var r0 null.Int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) (null.Int, error)); ok { + return rf(ctx, chainID) + } + if rf, ok := ret.Get(0).(func(context.Context, *big.Int) null.Int); ok { + r0 = rf(ctx, chainID) + } else { + r0 = ret.Get(0).(null.Int) + } + + if rf, ok := ret.Get(1).(func(context.Context, *big.Int) error); ok { + r1 = rf(ctx, chainID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindLatestSequence provides a mock function with given fields: ctx, fromAddress, chainId func (_m *EvmTxStore) FindLatestSequence(ctx context.Context, fromAddress common.Address, chainId *big.Int) (evmtypes.Nonce, error) { ret := _m.Called(ctx, fromAddress, chainId) diff --git a/core/internal/cltest/mocks.go b/core/internal/cltest/mocks.go index d49e94557e3..073b3ba246c 100644 --- a/core/internal/cltest/mocks.go +++ b/core/internal/cltest/mocks.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/jmoiron/sqlx" evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" @@ -409,6 +411,18 @@ func NewLegacyChainsWithMockChain(t testing.TB, ethClient evmclient.Client, cfg } +func NewLegacyChainsWithMockChainAndTxManager(t testing.TB, ethClient evmclient.Client, cfg legacyevm.AppConfig, txm txmgr.TxManager) legacyevm.LegacyChainContainer { + ch := new(evmmocks.Chain) + ch.On("Client").Return(ethClient) + ch.On("Logger").Return(logger.TestLogger(t)) + scopedCfg := evmtest.NewChainScopedConfig(t, cfg) + ch.On("ID").Return(scopedCfg.EVM().ChainID()) + ch.On("Config").Return(scopedCfg) + ch.On("TxManager").Return(txm) + + return NewLegacyChainsWithChain(ch, cfg) +} + func NewLegacyChainsWithChain(ch legacyevm.Chain, cfg legacyevm.AppConfig) legacyevm.LegacyChainContainer { m := map[string]legacyevm.Chain{ch.ID().String(): ch} return legacyevm.NewLegacyChains(m, cfg.EVMConfigs()) diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 3d06789e7bc..1c9cd106ba9 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -238,11 +238,6 @@ func NewApplication(opts ApplicationOpts) (Application, error) { globalLogger.Info("DatabaseBackup: periodic database backups are disabled. To enable automatic backups, set Database.Backup.Mode=lite or Database.Backup.Mode=full") } - srvcs = append(srvcs, eventBroadcaster, mailMon) - srvcs = append(srvcs, relayerChainInterops.Services()...) - promReporter := promreporter.NewPromReporter(db.DB, globalLogger) - srvcs = append(srvcs, promReporter) - // pool must be started before all relayers and stopped after them srvcs = append(srvcs, opts.MercuryPool) @@ -254,6 +249,11 @@ func NewApplication(opts ApplicationOpts) (Application, error) { return nil, fmt.Errorf("no evm chains found") } + srvcs = append(srvcs, eventBroadcaster, mailMon) + srvcs = append(srvcs, relayerChainInterops.Services()...) + promReporter := promreporter.NewPromReporter(db.DB, legacyEVMChains, globalLogger) + srvcs = append(srvcs, promReporter) + // Initialize Local Users ORM and Authentication Provider specified in config // BasicAdminUsersORM is initialized and required regardless of separate Authentication Provider localAdminUsersORM := localauth.NewORM(db, cfg.WebServer().SessionTimeout().Duration(), globalLogger, cfg.Database(), auditLogger) diff --git a/core/services/promreporter/prom_reporter.go b/core/services/promreporter/prom_reporter.go index d115b1022c1..3e1444a6da1 100644 --- a/core/services/promreporter/prom_reporter.go +++ b/core/services/promreporter/prom_reporter.go @@ -3,18 +3,22 @@ package promreporter import ( "context" "database/sql" + "fmt" "math/big" "sync" "time" + txmgrcommon "github.com/smartcontractkit/chainlink/v2/common/txmgr" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" + "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" - "gopkg.in/guregu/null.v4" "github.com/smartcontractkit/chainlink-common/pkg/services" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -24,6 +28,7 @@ type ( promReporter struct { services.StateMachine db *sql.DB + chains legacyevm.LegacyChainContainer lggr logger.Logger backend PrometheusBackend newHeads *utils.Mailbox[*evmtypes.Head] @@ -86,7 +91,7 @@ func (defaultBackend) SetPipelineTaskRunsQueued(n int) { promPipelineRunsQueued.Set(float64(n)) } -func NewPromReporter(db *sql.DB, lggr logger.Logger, opts ...interface{}) *promReporter { +func NewPromReporter(db *sql.DB, chainContainer legacyevm.LegacyChainContainer, lggr logger.Logger, opts ...interface{}) *promReporter { var backend PrometheusBackend = defaultBackend{} period := 15 * time.Second for _, opt := range opts { @@ -101,6 +106,7 @@ func NewPromReporter(db *sql.DB, lggr logger.Logger, opts ...interface{}) *promR chStop := make(chan struct{}) return &promReporter{ db: db, + chains: chainContainer, lggr: lggr.Named("PromReporter"), backend: backend, newHeads: utils.NewSingleMailbox[*evmtypes.Head](), @@ -161,6 +167,14 @@ func (pr *promReporter) eventLoop() { } } +func (pr *promReporter) getTxm(evmChainID *big.Int) (txmgr.TxManager, error) { + chain, err := pr.chains.Get(evmChainID.String()) + if err != nil { + return nil, fmt.Errorf("failed to get chain: %w", err) + } + return chain.TxManager(), nil +} + func (pr *promReporter) reportHeadMetrics(ctx context.Context, head *evmtypes.Head) { evmChainID := head.EVMChainID.ToInt() err := multierr.Combine( @@ -175,40 +189,49 @@ func (pr *promReporter) reportHeadMetrics(ctx context.Context, head *evmtypes.He } func (pr *promReporter) reportPendingEthTxes(ctx context.Context, evmChainID *big.Int) (err error) { - var unconfirmed int64 - if err := pr.db.QueryRowContext(ctx, `SELECT count(*) FROM evm.txes WHERE state = 'unconfirmed' AND evm_chain_id = $1`, evmChainID.String()).Scan(&unconfirmed); err != nil { - return errors.Wrap(err, "failed to query for unconfirmed eth_tx count") + txm, err := pr.getTxm(evmChainID) + if err != nil { + return fmt.Errorf("failed to get txm: %w", err) + } + + unconfirmed, err := txm.CountTransactionsByState(ctx, txmgrcommon.TxUnconfirmed) + if err != nil { + return fmt.Errorf("failed to query for unconfirmed eth_tx count: %w", err) } - pr.backend.SetUnconfirmedTransactions(evmChainID, unconfirmed) + pr.backend.SetUnconfirmedTransactions(evmChainID, int64(unconfirmed)) return nil } func (pr *promReporter) reportMaxUnconfirmedAge(ctx context.Context, evmChainID *big.Int) (err error) { - var broadcastAt null.Time - now := time.Now() - if err := pr.db.QueryRowContext(ctx, `SELECT min(initial_broadcast_at) FROM evm.txes WHERE state = 'unconfirmed' AND evm_chain_id = $1`, evmChainID.String()).Scan(&broadcastAt); err != nil { - return errors.Wrap(err, "failed to query for unconfirmed eth_tx count") + txm, err := pr.getTxm(evmChainID) + if err != nil { + return fmt.Errorf("failed to get txm: %w", err) } + + broadcastAt, err := txm.FindEarliestUnconfirmedBroadcastTime(ctx) + if err != nil { + return fmt.Errorf("failed to query for min broadcast time: %w", err) + } + var seconds float64 if broadcastAt.Valid { - nanos := now.Sub(broadcastAt.ValueOrZero()) - seconds = float64(nanos) / 1000000000 + seconds = time.Since(broadcastAt.ValueOrZero()).Seconds() } pr.backend.SetMaxUnconfirmedAge(evmChainID, seconds) return nil } func (pr *promReporter) reportMaxUnconfirmedBlocks(ctx context.Context, head *evmtypes.Head) (err error) { - var earliestUnconfirmedTxBlock null.Int - err = pr.db.QueryRowContext(ctx, ` -SELECT MIN(broadcast_before_block_num) FROM evm.tx_attempts -JOIN evm.txes ON evm.txes.id = evm.tx_attempts.eth_tx_id -WHERE evm.txes.state = 'unconfirmed' -AND evm_chain_id = $1 -AND evm.txes.state = 'unconfirmed'`, head.EVMChainID.String()).Scan(&earliestUnconfirmedTxBlock) + txm, err := pr.getTxm(head.EVMChainID.ToInt()) if err != nil { - return errors.Wrap(err, "failed to query for min broadcast_before_block_num") + return fmt.Errorf("failed to get txm: %w", err) } + + earliestUnconfirmedTxBlock, err := txm.FindEarliestUnconfirmedTxAttemptBlock(ctx) + if err != nil { + return fmt.Errorf("failed to query for earliest unconfirmed tx block: %w", err) + } + var blocksUnconfirmed int64 if !earliestUnconfirmedTxBlock.IsZero() { blocksUnconfirmed = head.Number - earliestUnconfirmedTxBlock.ValueOrZero() diff --git a/core/services/promreporter/prom_reporter_test.go b/core/services/promreporter/prom_reporter_test.go index 1f15d94418d..54e3a5d3fab 100644 --- a/core/services/promreporter/prom_reporter_test.go +++ b/core/services/promreporter/prom_reporter_test.go @@ -6,15 +6,21 @@ import ( "testing" "time" + "github.com/jmoiron/sqlx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" "github.com/smartcontractkit/chainlink/v2/core/internal/mocks" "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/promreporter" @@ -25,12 +31,38 @@ func newHead() evmtypes.Head { return evmtypes.Head{Number: 42, EVMChainID: utils.NewBigI(0)} } +func newLegacyChainContainer(t *testing.T, db *sqlx.DB) legacyevm.LegacyChainContainer { + config, dbConfig, evmConfig := txmgr.MakeTestConfigs(t) + keyStore := cltest.NewKeyStore(t, db, dbConfig).Eth() + ethClient := evmtest.NewEthClientMockWithDefaultChain(t) + estimator := gas.NewEstimator(logger.TestLogger(t), ethClient, config, evmConfig.GasEstimator()) + lggr := logger.TestLogger(t) + lp := logpoller.NewLogPoller(logpoller.NewORM(testutils.FixtureChainID, db, lggr, pgtest.NewQConfig(true)), ethClient, lggr, 100*time.Millisecond, false, 2, 3, 2, 1000) + + txm, err := txmgr.NewTxm( + db, + evmConfig, + evmConfig.GasEstimator(), + evmConfig.Transactions(), + dbConfig, + dbConfig.Listener(), + ethClient, + lggr, + lp, + keyStore, + estimator) + require.NoError(t, err) + + cfg := configtest.NewGeneralConfig(t, nil) + return cltest.NewLegacyChainsWithMockChainAndTxManager(t, ethClient, cfg, txm) +} + func Test_PromReporter_OnNewLongestChain(t *testing.T) { t.Run("with nothing in the database", func(t *testing.T) { - d := pgtest.NewSqlDB(t) + db := pgtest.NewSqlxDB(t) backend := mocks.NewPrometheusBackend(t) - reporter := promreporter.NewPromReporter(d, logger.TestLogger(t), backend, 10*time.Millisecond) + reporter := promreporter.NewPromReporter(db.DB, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) var subscribeCalls atomic.Int32 @@ -74,7 +106,7 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { subscribeCalls.Add(1) }). Return() - reporter := promreporter.NewPromReporter(db.DB, logger.TestLogger(t), backend, 10*time.Millisecond) + reporter := promreporter.NewPromReporter(db.DB, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) require.NoError(t, reporter.Start(testutils.Context(t))) defer func() { assert.NoError(t, reporter.Close()) }() @@ -91,11 +123,10 @@ func Test_PromReporter_OnNewLongestChain(t *testing.T) { t.Run("with unfinished pipeline task runs", func(t *testing.T) { db := pgtest.NewSqlxDB(t) - pgtest.MustExec(t, db, `SET CONSTRAINTS pipeline_task_runs_pipeline_run_id_fkey DEFERRED`) backend := mocks.NewPrometheusBackend(t) - reporter := promreporter.NewPromReporter(db.DB, logger.TestLogger(t), backend, 10*time.Millisecond) + reporter := promreporter.NewPromReporter(db.DB, newLegacyChainContainer(t, db), logger.TestLogger(t), backend, 10*time.Millisecond) cltest.MustInsertUnfinishedPipelineTaskRun(t, db, 1) cltest.MustInsertUnfinishedPipelineTaskRun(t, db, 1) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 929fec5a3e6..6af224157a7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -56,6 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `PromReporter` no longer directly reads txm related status from the db, and instead uses the txStore API. - `L2Suggested` mode is now called `SuggestedPrice` - Console logs will now escape (non-whitespace) control characters From 55b696ede4ae36993f20f449a549a641f7c7aab7 Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Thu, 30 Nov 2023 15:54:23 -0500 Subject: [PATCH 238/271] FUN-1130: Move Functions Coordinator v1.1 to production folder (#11431) --- .../functions/v1_1_0/FunctionsBilling.sol | 391 ++++++++++++++++++ .../functions/v1_1_0/FunctionsCoordinator.sol | 184 +++++++++ .../v1_1_0/libraries/ChainSpecificUtil.sol | 78 ++++ .../functions/v1_1_0/ocr/OCR2Abstract.sol | 103 +++++ .../v0.8/functions/v1_1_0/ocr/OCR2Base.sol | 351 ++++++++++++++++ 5 files changed, 1107 insertions(+) create mode 100644 contracts/src/v0.8/functions/v1_1_0/FunctionsBilling.sol create mode 100644 contracts/src/v0.8/functions/v1_1_0/FunctionsCoordinator.sol create mode 100644 contracts/src/v0.8/functions/v1_1_0/libraries/ChainSpecificUtil.sol create mode 100644 contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Abstract.sol create mode 100644 contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol diff --git a/contracts/src/v0.8/functions/v1_1_0/FunctionsBilling.sol b/contracts/src/v0.8/functions/v1_1_0/FunctionsBilling.sol new file mode 100644 index 00000000000..ff345003741 --- /dev/null +++ b/contracts/src/v0.8/functions/v1_1_0/FunctionsBilling.sol @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IFunctionsSubscriptions} from "../v1_0_0/interfaces/IFunctionsSubscriptions.sol"; +import {AggregatorV3Interface} from "../../shared/interfaces/AggregatorV3Interface.sol"; +import {IFunctionsBilling} from "../v1_0_0/interfaces/IFunctionsBilling.sol"; + +import {Routable} from "../v1_0_0/Routable.sol"; +import {FunctionsResponse} from "../v1_0_0/libraries/FunctionsResponse.sol"; + +import {SafeCast} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/math/SafeCast.sol"; + +import {ChainSpecificUtil} from "./libraries/ChainSpecificUtil.sol"; + +/// @title Functions Billing contract +/// @notice Contract that calculates payment from users to the nodes of the Decentralized Oracle Network (DON). +abstract contract FunctionsBilling is Routable, IFunctionsBilling { + using FunctionsResponse for FunctionsResponse.RequestMeta; + using FunctionsResponse for FunctionsResponse.Commitment; + using FunctionsResponse for FunctionsResponse.FulfillResult; + + uint256 private constant REASONABLE_GAS_PRICE_CEILING = 1_000_000_000_000_000; // 1 million gwei + + event RequestBilled( + bytes32 indexed requestId, + uint96 juelsPerGas, + uint256 l1FeeShareWei, + uint96 callbackCostJuels, + uint96 totalCostJuels + ); + + // ================================================================ + // | Request Commitment state | + // ================================================================ + + mapping(bytes32 requestId => bytes32 commitmentHash) private s_requestCommitments; + + event CommitmentDeleted(bytes32 requestId); + + // ================================================================ + // | Configuration state | + // ================================================================ + + struct Config { + uint32 fulfillmentGasPriceOverEstimationBP; // ══╗ Percentage of gas price overestimation to account for changes in gas price between request and response. Held as basis points (one hundredth of 1 percentage point) + uint32 feedStalenessSeconds; // ║ How long before we consider the feed price to be stale and fallback to fallbackNativePerUnitLink. + uint32 gasOverheadBeforeCallback; // ║ Represents the average gas execution cost before the fulfillment callback. This amount is always billed for every request. + uint32 gasOverheadAfterCallback; // ║ Represents the average gas execution cost after the fulfillment callback. This amount is always billed for every request. + uint72 donFee; // ║ Additional flat fee (in Juels of LINK) that will be split between Node Operators. Max value is 2^80 - 1 == 1.2m LINK. + uint40 minimumEstimateGasPriceWei; // ║ The lowest amount of wei that will be used as the tx.gasprice when estimating the cost to fulfill the request + uint16 maxSupportedRequestDataVersion; // ═══════╝ The highest support request data version supported by the node. All lower versions should also be supported. + uint224 fallbackNativePerUnitLink; // ═══════════╗ Fallback NATIVE CURRENCY / LINK conversion rate if the data feed is stale + uint32 requestTimeoutSeconds; // ════════════════╝ How many seconds it takes before we consider a request to be timed out + } + + Config private s_config; + + event ConfigUpdated(Config config); + + error UnsupportedRequestDataVersion(); + error InsufficientBalance(); + error InvalidSubscription(); + error UnauthorizedSender(); + error MustBeSubOwner(address owner); + error InvalidLinkWeiPrice(int256 linkWei); + error PaymentTooLarge(); + error NoTransmittersSet(); + error InvalidCalldata(); + + // ================================================================ + // | Balance state | + // ================================================================ + + mapping(address transmitter => uint96 balanceJuelsLink) private s_withdrawableTokens; + // Pool together collected DON fees + // Disperse them on withdrawal or change in OCR configuration + uint96 internal s_feePool; + + AggregatorV3Interface private s_linkToNativeFeed; + + // ================================================================ + // | Initialization | + // ================================================================ + constructor(address router, Config memory config, address linkToNativeFeed) Routable(router) { + s_linkToNativeFeed = AggregatorV3Interface(linkToNativeFeed); + + updateConfig(config); + } + + // ================================================================ + // | Configuration | + // ================================================================ + + /// @notice Gets the Chainlink Coordinator's billing configuration + /// @return config + function getConfig() external view returns (Config memory) { + return s_config; + } + + /// @notice Sets the Chainlink Coordinator's billing configuration + /// @param config - See the contents of the Config struct in IFunctionsBilling.Config for more information + function updateConfig(Config memory config) public { + _onlyOwner(); + + s_config = config; + emit ConfigUpdated(config); + } + + // ================================================================ + // | Fee Calculation | + // ================================================================ + + /// @inheritdoc IFunctionsBilling + function getDONFee(bytes memory /* requestData */) public view override returns (uint72) { + return s_config.donFee; + } + + /// @inheritdoc IFunctionsBilling + function getAdminFee() public view override returns (uint72) { + return _getRouter().getAdminFee(); + } + + /// @inheritdoc IFunctionsBilling + function getWeiPerUnitLink() public view returns (uint256) { + Config memory config = s_config; + (, int256 weiPerUnitLink, , uint256 timestamp, ) = s_linkToNativeFeed.latestRoundData(); + // solhint-disable-next-line not-rely-on-time + if (config.feedStalenessSeconds < block.timestamp - timestamp && config.feedStalenessSeconds > 0) { + return config.fallbackNativePerUnitLink; + } + if (weiPerUnitLink <= 0) { + revert InvalidLinkWeiPrice(weiPerUnitLink); + } + return uint256(weiPerUnitLink); + } + + function _getJuelsFromWei(uint256 amountWei) private view returns (uint96) { + // (1e18 juels/link) * wei / (wei/link) = juels + // There are only 1e9*1e18 = 1e27 juels in existence, should not exceed uint96 (2^96 ~ 7e28) + return SafeCast.toUint96((1e18 * amountWei) / getWeiPerUnitLink()); + } + + // ================================================================ + // | Cost Estimation | + // ================================================================ + + /// @inheritdoc IFunctionsBilling + function estimateCost( + uint64 subscriptionId, + bytes calldata data, + uint32 callbackGasLimit, + uint256 gasPriceWei + ) external view override returns (uint96) { + _getRouter().isValidCallbackGasLimit(subscriptionId, callbackGasLimit); + // Reasonable ceilings to prevent integer overflows + if (gasPriceWei > REASONABLE_GAS_PRICE_CEILING) { + revert InvalidCalldata(); + } + uint72 adminFee = getAdminFee(); + uint72 donFee = getDONFee(data); + return _calculateCostEstimate(callbackGasLimit, gasPriceWei, donFee, adminFee); + } + + /// @notice Estimate the cost in Juels of LINK + // that will be charged to a subscription to fulfill a Functions request + // Gas Price can be overestimated to account for flucuations between request and response time + function _calculateCostEstimate( + uint32 callbackGasLimit, + uint256 gasPriceWei, + uint72 donFee, + uint72 adminFee + ) internal view returns (uint96) { + // If gas price is less than the minimum fulfillment gas price, override to using the minimum + if (gasPriceWei < s_config.minimumEstimateGasPriceWei) { + gasPriceWei = s_config.minimumEstimateGasPriceWei; + } + + uint256 gasPriceWithOverestimation = gasPriceWei + + ((gasPriceWei * s_config.fulfillmentGasPriceOverEstimationBP) / 10_000); + /// @NOTE: Basis Points are 1/100th of 1%, divide by 10_000 to bring back to original units + + uint256 executionGas = s_config.gasOverheadBeforeCallback + s_config.gasOverheadAfterCallback + callbackGasLimit; + uint256 l1FeeWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data); + uint96 estimatedGasReimbursementJuels = _getJuelsFromWei((gasPriceWithOverestimation * executionGas) + l1FeeWei); + + uint96 feesJuels = uint96(donFee) + uint96(adminFee); + + return estimatedGasReimbursementJuels + feesJuels; + } + + // ================================================================ + // | Billing | + // ================================================================ + + /// @notice Initiate the billing process for an Functions request + /// @dev Only callable by the Functions Router + /// @param request - Chainlink Functions request data, see FunctionsResponse.RequestMeta for the structure + /// @return commitment - The parameters of the request that must be held consistent at response time + function _startBilling( + FunctionsResponse.RequestMeta memory request + ) internal returns (FunctionsResponse.Commitment memory commitment) { + Config memory config = s_config; + + // Nodes should support all past versions of the structure + if (request.dataVersion > config.maxSupportedRequestDataVersion) { + revert UnsupportedRequestDataVersion(); + } + + uint72 donFee = getDONFee(request.data); + uint96 estimatedTotalCostJuels = _calculateCostEstimate( + request.callbackGasLimit, + tx.gasprice, + donFee, + request.adminFee + ); + + // Check that subscription can afford the estimated cost + if ((request.availableBalance) < estimatedTotalCostJuels) { + revert InsufficientBalance(); + } + + uint32 timeoutTimestamp = uint32(block.timestamp + config.requestTimeoutSeconds); + bytes32 requestId = keccak256( + abi.encode( + address(this), + request.requestingContract, + request.subscriptionId, + request.initiatedRequests + 1, + keccak256(request.data), + request.dataVersion, + request.callbackGasLimit, + estimatedTotalCostJuels, + timeoutTimestamp, + // solhint-disable-next-line avoid-tx-origin + tx.origin + ) + ); + + commitment = FunctionsResponse.Commitment({ + adminFee: request.adminFee, + coordinator: address(this), + client: request.requestingContract, + subscriptionId: request.subscriptionId, + callbackGasLimit: request.callbackGasLimit, + estimatedTotalCostJuels: estimatedTotalCostJuels, + timeoutTimestamp: timeoutTimestamp, + requestId: requestId, + donFee: donFee, + gasOverheadBeforeCallback: config.gasOverheadBeforeCallback, + gasOverheadAfterCallback: config.gasOverheadAfterCallback + }); + + s_requestCommitments[requestId] = keccak256(abi.encode(commitment)); + + return commitment; + } + + /// @notice Finalize billing process for an Functions request by sending a callback to the Client contract and then charging the subscription + /// @param requestId identifier for the request that was generated by the Registry in the beginBilling commitment + /// @param response response data from DON consensus + /// @param err error from DON consensus + /// @param reportBatchSize the number of fulfillments in the transmitter's report + /// @return result fulfillment result + /// @dev Only callable by a node that has been approved on the Coordinator + /// @dev simulated offchain to determine if sufficient balance is present to fulfill the request + function _fulfillAndBill( + bytes32 requestId, + bytes memory response, + bytes memory err, + bytes memory onchainMetadata, + bytes memory /* offchainMetadata TODO: use in getDonFee() for dynamic billing */, + uint8 reportBatchSize + ) internal returns (FunctionsResponse.FulfillResult) { + FunctionsResponse.Commitment memory commitment = abi.decode(onchainMetadata, (FunctionsResponse.Commitment)); + + uint256 gasOverheadWei = (commitment.gasOverheadBeforeCallback + commitment.gasOverheadAfterCallback) * tx.gasprice; + uint256 l1FeeShareWei = ChainSpecificUtil._getCurrentTxL1GasFees(msg.data) / reportBatchSize; + // Gas overhead without callback + uint96 gasOverheadJuels = _getJuelsFromWei(gasOverheadWei + l1FeeShareWei); + uint96 juelsPerGas = _getJuelsFromWei(tx.gasprice); + + // The Functions Router will perform the callback to the client contract + (FunctionsResponse.FulfillResult resultCode, uint96 callbackCostJuels) = _getRouter().fulfill( + response, + err, + juelsPerGas, + gasOverheadJuels + commitment.donFee, // cost without callback or admin fee, those will be added by the Router + msg.sender, + commitment + ); + + // The router will only pay the DON on successfully processing the fulfillment + // In these two fulfillment results the user has been charged + // Otherwise, the Coordinator should hold on to the request commitment + if ( + resultCode == FunctionsResponse.FulfillResult.FULFILLED || + resultCode == FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR + ) { + delete s_requestCommitments[requestId]; + // Reimburse the transmitter for the fulfillment gas cost + s_withdrawableTokens[msg.sender] = gasOverheadJuels + callbackCostJuels; + // Put donFee into the pool of fees, to be split later + // Saves on storage writes that would otherwise be charged to the user + s_feePool += commitment.donFee; + emit RequestBilled({ + requestId: requestId, + juelsPerGas: juelsPerGas, + l1FeeShareWei: l1FeeShareWei, + callbackCostJuels: callbackCostJuels, + totalCostJuels: gasOverheadJuels + callbackCostJuels + commitment.donFee + commitment.adminFee + }); + } + + return resultCode; + } + + // ================================================================ + // | Request Timeout | + // ================================================================ + + /// @inheritdoc IFunctionsBilling + /// @dev Only callable by the Router + /// @dev Used by FunctionsRouter.sol during timeout of a request + function deleteCommitment(bytes32 requestId) external override onlyRouter { + // Delete commitment + delete s_requestCommitments[requestId]; + emit CommitmentDeleted(requestId); + } + + // ================================================================ + // | Fund withdrawal | + // ================================================================ + + /// @inheritdoc IFunctionsBilling + function oracleWithdraw(address recipient, uint96 amount) external { + _disperseFeePool(); + + if (amount == 0) { + amount = s_withdrawableTokens[msg.sender]; + } else if (s_withdrawableTokens[msg.sender] < amount) { + revert InsufficientBalance(); + } + s_withdrawableTokens[msg.sender] -= amount; + IFunctionsSubscriptions(address(_getRouter())).oracleWithdraw(recipient, amount); + } + + /// @inheritdoc IFunctionsBilling + /// @dev Only callable by the Coordinator owner + function oracleWithdrawAll() external { + _onlyOwner(); + _disperseFeePool(); + + address[] memory transmitters = _getTransmitters(); + + // Bounded by "maxNumOracles" on OCR2Abstract.sol + for (uint256 i = 0; i < transmitters.length; ++i) { + uint96 balance = s_withdrawableTokens[transmitters[i]]; + if (balance > 0) { + s_withdrawableTokens[transmitters[i]] = 0; + IFunctionsSubscriptions(address(_getRouter())).oracleWithdraw(transmitters[i], balance); + } + } + } + + // Overriden in FunctionsCoordinator, which has visibility into transmitters + function _getTransmitters() internal view virtual returns (address[] memory); + + // DON fees are collected into a pool s_feePool + // When OCR configuration changes, or any oracle withdraws, this must be dispersed + function _disperseFeePool() internal { + if (s_feePool == 0) { + return; + } + // All transmitters are assumed to also be observers + // Pay out the DON fee to all transmitters + address[] memory transmitters = _getTransmitters(); + uint256 numberOfTransmitters = transmitters.length; + if (numberOfTransmitters == 0) { + revert NoTransmittersSet(); + } + uint96 feePoolShare = s_feePool / uint96(numberOfTransmitters); + // Bounded by "maxNumOracles" on OCR2Abstract.sol + for (uint256 i = 0; i < numberOfTransmitters; ++i) { + s_withdrawableTokens[transmitters[i]] += feePoolShare; + } + s_feePool -= feePoolShare * uint96(numberOfTransmitters); + } + + // Overriden in FunctionsCoordinator.sol + function _onlyOwner() internal view virtual; +} diff --git a/contracts/src/v0.8/functions/v1_1_0/FunctionsCoordinator.sol b/contracts/src/v0.8/functions/v1_1_0/FunctionsCoordinator.sol new file mode 100644 index 00000000000..0a5da643a57 --- /dev/null +++ b/contracts/src/v0.8/functions/v1_1_0/FunctionsCoordinator.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {IFunctionsCoordinator} from "../v1_0_0/interfaces/IFunctionsCoordinator.sol"; +import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; + +import {FunctionsBilling} from "./FunctionsBilling.sol"; +import {OCR2Base} from "./ocr/OCR2Base.sol"; +import {FunctionsResponse} from "../v1_0_0/libraries/FunctionsResponse.sol"; + +/// @title Functions Coordinator contract +/// @notice Contract that nodes of a Decentralized Oracle Network (DON) interact with +contract FunctionsCoordinator is OCR2Base, IFunctionsCoordinator, FunctionsBilling { + using FunctionsResponse for FunctionsResponse.RequestMeta; + using FunctionsResponse for FunctionsResponse.Commitment; + using FunctionsResponse for FunctionsResponse.FulfillResult; + + /// @inheritdoc ITypeAndVersion + // solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables + string public constant override typeAndVersion = "Functions Coordinator v1.1.0"; + + event OracleRequest( + bytes32 indexed requestId, + address indexed requestingContract, + address requestInitiator, + uint64 subscriptionId, + address subscriptionOwner, + bytes data, + uint16 dataVersion, + bytes32 flags, + uint64 callbackGasLimit, + FunctionsResponse.Commitment commitment + ); + event OracleResponse(bytes32 indexed requestId, address transmitter); + + error InconsistentReportData(); + error EmptyPublicKey(); + error UnauthorizedPublicKeyChange(); + + bytes private s_donPublicKey; + bytes private s_thresholdPublicKey; + + constructor( + address router, + Config memory config, + address linkToNativeFeed + ) OCR2Base() FunctionsBilling(router, config, linkToNativeFeed) {} + + /// @inheritdoc IFunctionsCoordinator + function getThresholdPublicKey() external view override returns (bytes memory) { + if (s_thresholdPublicKey.length == 0) { + revert EmptyPublicKey(); + } + return s_thresholdPublicKey; + } + + /// @inheritdoc IFunctionsCoordinator + function setThresholdPublicKey(bytes calldata thresholdPublicKey) external override onlyOwner { + if (thresholdPublicKey.length == 0) { + revert EmptyPublicKey(); + } + s_thresholdPublicKey = thresholdPublicKey; + } + + /// @inheritdoc IFunctionsCoordinator + function getDONPublicKey() external view override returns (bytes memory) { + if (s_donPublicKey.length == 0) { + revert EmptyPublicKey(); + } + return s_donPublicKey; + } + + /// @inheritdoc IFunctionsCoordinator + function setDONPublicKey(bytes calldata donPublicKey) external override onlyOwner { + if (donPublicKey.length == 0) { + revert EmptyPublicKey(); + } + s_donPublicKey = donPublicKey; + } + + /// @dev check if node is in current transmitter list + function _isTransmitter(address node) internal view returns (bool) { + address[] memory nodes = s_transmitters; + // Bounded by "maxNumOracles" on OCR2Abstract.sol + for (uint256 i = 0; i < nodes.length; ++i) { + if (nodes[i] == node) { + return true; + } + } + return false; + } + + /// @inheritdoc IFunctionsCoordinator + function startRequest( + FunctionsResponse.RequestMeta calldata request + ) external override onlyRouter returns (FunctionsResponse.Commitment memory commitment) { + commitment = _startBilling(request); + + emit OracleRequest( + commitment.requestId, + request.requestingContract, + // solhint-disable-next-line avoid-tx-origin + tx.origin, + request.subscriptionId, + request.subscriptionOwner, + request.data, + request.dataVersion, + request.flags, + request.callbackGasLimit, + commitment + ); + + return commitment; + } + + /// @dev DON fees are pooled together. If the OCR configuration is going to change, these need to be distributed. + function _beforeSetConfig(uint8 /* _f */, bytes memory /* _onchainConfig */) internal override { + if (_getTransmitters().length > 0) { + _disperseFeePool(); + } + } + + /// @dev Used by FunctionsBilling.sol + function _getTransmitters() internal view override returns (address[] memory) { + return s_transmitters; + } + + /// @dev Report hook called within OCR2Base.sol + function _report( + uint256 /*initialGas*/, + address /*transmitter*/, + uint8 /*signerCount*/, + address[MAX_NUM_ORACLES] memory /*signers*/, + bytes calldata report + ) internal override { + ( + bytes32[] memory requestIds, + bytes[] memory results, + bytes[] memory errors, + bytes[] memory onchainMetadata, + bytes[] memory offchainMetadata + ) = abi.decode(report, (bytes32[], bytes[], bytes[], bytes[], bytes[])); + uint256 numberOfFulfillments = uint8(requestIds.length); + + if ( + numberOfFulfillments == 0 || + numberOfFulfillments != results.length || + numberOfFulfillments != errors.length || + numberOfFulfillments != onchainMetadata.length || + numberOfFulfillments != offchainMetadata.length + ) { + revert ReportInvalid("Fields must be equal length"); + } + + // Bounded by "MaxRequestBatchSize" on the Job's ReportingPluginConfig + for (uint256 i = 0; i < numberOfFulfillments; ++i) { + FunctionsResponse.FulfillResult result = FunctionsResponse.FulfillResult( + _fulfillAndBill( + requestIds[i], + results[i], + errors[i], + onchainMetadata[i], + offchainMetadata[i], + uint8(numberOfFulfillments) // will not exceed "MaxRequestBatchSize" on the Job's ReportingPluginConfig + ) + ); + + // Emit on successfully processing the fulfillment + // In these two fulfillment results the user has been charged + // Otherwise, the DON will re-try + if ( + result == FunctionsResponse.FulfillResult.FULFILLED || + result == FunctionsResponse.FulfillResult.USER_CALLBACK_ERROR + ) { + emit OracleResponse(requestIds[i], msg.sender); + } + } + } + + /// @dev Used in FunctionsBilling.sol + function _onlyOwner() internal view override { + _validateOwnership(); + } +} diff --git a/contracts/src/v0.8/functions/v1_1_0/libraries/ChainSpecificUtil.sol b/contracts/src/v0.8/functions/v1_1_0/libraries/ChainSpecificUtil.sol new file mode 100644 index 00000000000..68d346e676d --- /dev/null +++ b/contracts/src/v0.8/functions/v1_1_0/libraries/ChainSpecificUtil.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {ArbGasInfo} from "../../../vendor/@arbitrum/nitro-contracts/src/precompiles/ArbGasInfo.sol"; +import {GasPriceOracle} from "../../../vendor/@eth-optimism/contracts-bedrock/v0.16.2/src/L2/GasPriceOracle.sol"; + +/// @dev A library that abstracts out opcodes that behave differently across chains. +/// @dev The methods below return values that are pertinent to the given chain. +library ChainSpecificUtil { + // ------------ Start Arbitrum Constants ------------ + + /// @dev ARBGAS_ADDR is the address of the ArbGasInfo precompile on Arbitrum. + /// @dev reference: https://github.com/OffchainLabs/nitro/blob/v2.0.14/contracts/src/precompiles/ArbGasInfo.sol#L10 + address private constant ARBGAS_ADDR = address(0x000000000000000000000000000000000000006C); + ArbGasInfo private constant ARBGAS = ArbGasInfo(ARBGAS_ADDR); + + uint256 private constant ARB_MAINNET_CHAIN_ID = 42161; + uint256 private constant ARB_GOERLI_TESTNET_CHAIN_ID = 421613; + uint256 private constant ARB_SEPOLIA_TESTNET_CHAIN_ID = 421614; + + // ------------ End Arbitrum Constants ------------ + + // ------------ Start Optimism Constants ------------ + /// @dev L1_FEE_DATA_PADDING includes 35 bytes for L1 data padding for Optimism + bytes internal constant L1_FEE_DATA_PADDING = + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; + /// @dev OVM_GASPRICEORACLE_ADDR is the address of the GasPriceOracle precompile on Optimism. + /// @dev reference: https://community.optimism.io/docs/developers/build/transaction-fees/#estimating-the-l1-data-fee + address private constant OVM_GASPRICEORACLE_ADDR = address(0x420000000000000000000000000000000000000F); + GasPriceOracle private constant OVM_GASPRICEORACLE = GasPriceOracle(OVM_GASPRICEORACLE_ADDR); + + uint256 private constant OP_MAINNET_CHAIN_ID = 10; + uint256 private constant OP_GOERLI_CHAIN_ID = 420; + uint256 private constant OP_SEPOLIA_CHAIN_ID = 11155420; + + /// @dev Base is a OP stack based rollup and follows the same L1 pricing logic as Optimism. + uint256 private constant BASE_MAINNET_CHAIN_ID = 8453; + uint256 private constant BASE_GOERLI_CHAIN_ID = 84531; + uint256 private constant BASE_SEPOLIA_CHAIN_ID = 84532; + + // ------------ End Optimism Constants ------------ + + /// @notice Returns the L1 fees in wei that will be paid for the current transaction, given any calldata + /// @notice for the current transaction. + /// @notice When on a known Arbitrum chain, it uses ArbGas.getCurrentTxL1GasFees to get the fees. + /// @notice On Arbitrum, the provided calldata is not used to calculate the fees. + /// @notice On Optimism, the provided calldata is passed to the GasPriceOracle predeploy + /// @notice and getL1Fee is called to get the fees. + function _getCurrentTxL1GasFees(bytes memory txCallData) internal view returns (uint256 l1FeeWei) { + uint256 chainid = block.chainid; + if (_isArbitrumChainId(chainid)) { + return ARBGAS.getCurrentTxL1GasFees(); + } else if (_isOptimismChainId(chainid)) { + return OVM_GASPRICEORACLE.getL1Fee(bytes.concat(txCallData, L1_FEE_DATA_PADDING)); + } + return 0; + } + + /// @notice Return true if and only if the provided chain ID is an Arbitrum chain ID. + function _isArbitrumChainId(uint256 chainId) internal pure returns (bool) { + return + chainId == ARB_MAINNET_CHAIN_ID || + chainId == ARB_GOERLI_TESTNET_CHAIN_ID || + chainId == ARB_SEPOLIA_TESTNET_CHAIN_ID; + } + + /// @notice Return true if and only if the provided chain ID is an Optimism (or Base) chain ID. + /// @notice Note that optimism chain id's are also OP stack chain id's. + function _isOptimismChainId(uint256 chainId) internal pure returns (bool) { + return + chainId == OP_MAINNET_CHAIN_ID || + chainId == OP_GOERLI_CHAIN_ID || + chainId == OP_SEPOLIA_CHAIN_ID || + chainId == BASE_MAINNET_CHAIN_ID || + chainId == BASE_GOERLI_CHAIN_ID || + chainId == BASE_SEPOLIA_CHAIN_ID; + } +} diff --git a/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Abstract.sol b/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Abstract.sol new file mode 100644 index 00000000000..4182227d645 --- /dev/null +++ b/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Abstract.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {ITypeAndVersion} from "../../../shared/interfaces/ITypeAndVersion.sol"; + +abstract contract OCR2Abstract is ITypeAndVersion { + // Maximum number of oracles the offchain reporting protocol is designed for + uint256 internal constant MAX_NUM_ORACLES = 31; + + /** + * @notice triggers a new run of the offchain reporting protocol + * @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis + * @param configDigest configDigest of this configuration + * @param configCount ordinal number of this config setting among all config settings over the life of this contract + * @param signers ith element is address ith oracle uses to sign a report + * @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method + * @param f maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly + * @param onchainConfig serialized configuration used by the contract (and possibly oracles) + * @param offchainConfigVersion version of the serialization format used for "offchainConfig" parameter + * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract + */ + event ConfigSet( + uint32 previousConfigBlockNumber, + bytes32 configDigest, + uint64 configCount, + address[] signers, + address[] transmitters, + uint8 f, + bytes onchainConfig, + uint64 offchainConfigVersion, + bytes offchainConfig + ); + + /** + * @notice sets offchain reporting protocol configuration incl. participating oracles + * @param signers addresses with which oracles sign the reports + * @param transmitters addresses oracles use to transmit the reports + * @param f number of faulty oracles the system can tolerate + * @param onchainConfig serialized configuration used by the contract (and possibly oracles) + * @param offchainConfigVersion version number for offchainEncoding schema + * @param offchainConfig serialized configuration used by the oracles exclusively and only passed through the contract + */ + function setConfig( + address[] memory signers, + address[] memory transmitters, + uint8 f, + bytes memory onchainConfig, + uint64 offchainConfigVersion, + bytes memory offchainConfig + ) external virtual; + + /** + * @notice information about current offchain reporting protocol configuration + * @return configCount ordinal number of current config, out of all configs applied to this contract so far + * @return blockNumber block at which this config was set + * @return configDigest domain-separation tag for current config (see _configDigestFromConfigData) + */ + function latestConfigDetails() + external + view + virtual + returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest); + + /** + * @notice optionally emited to indicate the latest configDigest and epoch for + which a report was successfully transmited. Alternatively, the contract may + use latestConfigDigestAndEpoch with scanLogs set to false. + */ + event Transmitted(bytes32 configDigest, uint32 epoch); + + /** + * @notice optionally returns the latest configDigest and epoch for which a + report was successfully transmitted. Alternatively, the contract may return + scanLogs set to true and use Transmitted events to provide this information + to offchain watchers. + * @return scanLogs indicates whether to rely on the configDigest and epoch + returned or whether to scan logs for the Transmitted event instead. + * @return configDigest + * @return epoch + */ + function latestConfigDigestAndEpoch() + external + view + virtual + returns (bool scanLogs, bytes32 configDigest, uint32 epoch); + + /** + * @notice transmit is called to post a new report to the contract + * @param report serialized report, which the signatures are signing. + * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries + * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries + * @param rawVs ith element is the the V component of the ith signature + */ + function transmit( + // NOTE: If these parameters are changed, expectedMsgDataLength and/or + // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly + bytes32[3] calldata reportContext, + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs // signatures + ) external virtual; +} diff --git a/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol b/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol new file mode 100644 index 00000000000..ea1d45ffd46 --- /dev/null +++ b/contracts/src/v0.8/functions/v1_1_0/ocr/OCR2Base.sol @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ConfirmedOwner} from "../../../shared/access/ConfirmedOwner.sol"; +import {OCR2Abstract} from "./OCR2Abstract.sol"; + +/** + * @notice Onchain verification of reports from the offchain reporting protocol + * @dev For details on its operation, see the offchain reporting protocol design + * doc, which refers to this contract as simply the "contract". + */ +abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { + error ReportInvalid(string message); + error InvalidConfig(string message); + + constructor() ConfirmedOwner(msg.sender) {} + + // incremented each time a new config is posted. This count is incorporated + // into the config digest, to prevent replay attacks. + uint32 internal s_configCount; + uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems + // to extract config from logs. + + // Storing these fields used on the hot path in a ConfigInfo variable reduces the + // retrieval of all of them to a single SLOAD. If any further fields are + // added, make sure that storage of the struct still takes at most 32 bytes. + struct ConfigInfo { + bytes32 latestConfigDigest; + uint8 f; // TODO: could be optimized by squeezing into one slot + uint8 n; + } + ConfigInfo internal s_configInfo; + + // Used for s_oracles[a].role, where a is an address, to track the purpose + // of the address, or to indicate that the address is unset. + enum Role { + // No oracle role has been set for address a + Unset, + // Signing address for the s_oracles[a].index'th oracle. I.e., report + // signatures from this oracle should ecrecover back to address a. + Signer, + // Transmission address for the s_oracles[a].index'th oracle. I.e., if a + // report is received by OCR2Aggregator.transmit in which msg.sender is + // a, it is attributed to the s_oracles[a].index'th oracle. + Transmitter + } + + struct Oracle { + uint8 index; // Index of oracle in s_signers/s_transmitters + Role role; // Role of the address which mapped to this struct + } + + mapping(address signerOrTransmitter => Oracle) internal s_oracles; + + // s_signers contains the signing address of each oracle + address[] internal s_signers; + + // s_transmitters contains the transmission address of each oracle, + // i.e. the address the oracle actually sends transactions to the contract from + address[] internal s_transmitters; + + /* + * Config logic + */ + + // Reverts transaction if config args are invalid + modifier checkConfigValid( + uint256 numSigners, + uint256 numTransmitters, + uint256 f + ) { + if (numSigners > MAX_NUM_ORACLES) revert InvalidConfig("too many signers"); + if (f == 0) revert InvalidConfig("f must be positive"); + if (numSigners != numTransmitters) revert InvalidConfig("oracle addresses out of registration"); + if (numSigners <= 3 * f) revert InvalidConfig("faulty-oracle f too high"); + _; + } + + struct SetConfigArgs { + address[] signers; + address[] transmitters; + uint8 f; + bytes onchainConfig; + uint64 offchainConfigVersion; + bytes offchainConfig; + } + + /// @inheritdoc OCR2Abstract + function latestConfigDigestAndEpoch() + external + view + virtual + override + returns (bool scanLogs, bytes32 configDigest, uint32 epoch) + { + return (true, bytes32(0), uint32(0)); + } + + /** + * @notice sets offchain reporting protocol configuration incl. participating oracles + * @param _signers addresses with which oracles sign the reports + * @param _transmitters addresses oracles use to transmit the reports + * @param _f number of faulty oracles the system can tolerate + * @param _onchainConfig encoded on-chain contract configuration + * @param _offchainConfigVersion version number for offchainEncoding schema + * @param _offchainConfig encoded off-chain oracle configuration + */ + function setConfig( + address[] memory _signers, + address[] memory _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _offchainConfigVersion, + bytes memory _offchainConfig + ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { + SetConfigArgs memory args = SetConfigArgs({ + signers: _signers, + transmitters: _transmitters, + f: _f, + onchainConfig: _onchainConfig, + offchainConfigVersion: _offchainConfigVersion, + offchainConfig: _offchainConfig + }); + + _beforeSetConfig(args.f, args.onchainConfig); + + while (s_signers.length != 0) { + // remove any old signer/transmitter addresses + uint256 lastIdx = s_signers.length - 1; + address signer = s_signers[lastIdx]; + address transmitter = s_transmitters[lastIdx]; + delete s_oracles[signer]; + delete s_oracles[transmitter]; + s_signers.pop(); + s_transmitters.pop(); + } + + // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol + for (uint256 i = 0; i < args.signers.length; i++) { + if (args.signers[i] == address(0)) revert InvalidConfig("signer must not be empty"); + if (args.transmitters[i] == address(0)) revert InvalidConfig("transmitter must not be empty"); + // add new signer/transmitter addresses + if (s_oracles[args.signers[i]].role != Role.Unset) revert InvalidConfig("repeated signer address"); + s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); + if (s_oracles[args.transmitters[i]].role != Role.Unset) revert InvalidConfig("repeated transmitter address"); + s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); + s_signers.push(args.signers[i]); + s_transmitters.push(args.transmitters[i]); + } + s_configInfo.f = args.f; + uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; + s_latestConfigBlockNumber = uint32(block.number); + s_configCount += 1; + { + s_configInfo.latestConfigDigest = _configDigestFromConfigData( + block.chainid, + address(this), + s_configCount, + args.signers, + args.transmitters, + args.f, + args.onchainConfig, + args.offchainConfigVersion, + args.offchainConfig + ); + } + s_configInfo.n = uint8(args.signers.length); + + emit ConfigSet( + previousConfigBlockNumber, + s_configInfo.latestConfigDigest, + s_configCount, + args.signers, + args.transmitters, + args.f, + args.onchainConfig, + args.offchainConfigVersion, + args.offchainConfig + ); + } + + function _configDigestFromConfigData( + uint256 _chainId, + address _contractAddress, + uint64 _configCount, + address[] memory _signers, + address[] memory _transmitters, + uint8 _f, + bytes memory _onchainConfig, + uint64 _encodedConfigVersion, + bytes memory _encodedConfig + ) internal pure returns (bytes32) { + uint256 h = uint256( + keccak256( + abi.encode( + _chainId, + _contractAddress, + _configCount, + _signers, + _transmitters, + _f, + _onchainConfig, + _encodedConfigVersion, + _encodedConfig + ) + ) + ); + uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 + uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 + return bytes32((prefix & prefixMask) | (h & ~prefixMask)); + } + + /** + * @notice information about current offchain reporting protocol configuration + * @return configCount ordinal number of current config, out of all configs applied to this contract so far + * @return blockNumber block at which this config was set + * @return configDigest domain-separation tag for current config (see __configDigestFromConfigData) + */ + function latestConfigDetails() + external + view + override + returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) + { + return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); + } + + /** + * @return list of addresses permitted to transmit reports to this contract + * @dev The list will match the order used to specify the transmitter during setConfig + */ + function transmitters() external view returns (address[] memory) { + return s_transmitters; + } + + function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; + + /** + * @dev hook called after the report has been fully validated + * for the extending contract to handle additional logic, such as oracle payment + * @param initialGas the amount of gas before validation + * @param transmitter the address of the account that submitted the report + * @param signers the addresses of all signing accounts + * @param report serialized report + */ + function _report( + uint256 initialGas, + address transmitter, + uint8 signerCount, + address[MAX_NUM_ORACLES] memory signers, + bytes calldata report + ) internal virtual; + + // The constant-length components of the msg.data sent to transmit. + // See the "If we wanted to call sam" example on for example reasoning + // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html + uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = + 4 + // function selector + 32 * + 3 + // 3 words containing reportContext + 32 + // word containing start location of abiencoded report value + 32 + // word containing location start of abiencoded rs value + 32 + // word containing start location of abiencoded ss value + 32 + // rawVs value + 32 + // word containing length of report + 32 + // word containing length rs + 32 + // word containing length of ss + 0; // placeholder + + function _requireExpectedMsgDataLength( + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss + ) private pure { + // calldata will never be big enough to make this overflow + uint256 expected = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + + report.length + // one byte pure entry in _report + rs.length * + 32 + // 32 bytes per entry in _rs + ss.length * + 32 + // 32 bytes per entry in _ss + 0; // placeholder + if (msg.data.length != expected) revert ReportInvalid("calldata length mismatch"); + } + + /** + * @notice transmit is called to post a new report to the contract + * @param report serialized report, which the signatures are signing. + * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries + * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries + * @param rawVs ith element is the the V component of the ith signature + */ + function transmit( + // NOTE: If these parameters are changed, expectedMsgDataLength and/or + // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly + bytes32[3] calldata reportContext, + bytes calldata report, + bytes32[] calldata rs, + bytes32[] calldata ss, + bytes32 rawVs // signatures + ) external override { + uint256 initialGas = gasleft(); // This line must come first + + { + // reportContext consists of: + // reportContext[0]: ConfigDigest + // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round + // reportContext[2]: ExtraHash + bytes32 configDigest = reportContext[0]; + uint32 epochAndRound = uint32(uint256(reportContext[1])); + + emit Transmitted(configDigest, uint32(epochAndRound >> 8)); + + // The following check is disabled to allow both current and proposed routes to submit reports using the same OCR config digest + // Chainlink Functions uses globally unique request IDs. Metadata about the request is stored and checked in the Coordinator and Router + // require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); + + _requireExpectedMsgDataLength(report, rs, ss); + + uint256 expectedNumSignatures = (s_configInfo.n + s_configInfo.f) / 2 + 1; + + if (rs.length != expectedNumSignatures) revert ReportInvalid("wrong number of signatures"); + if (rs.length != ss.length) revert ReportInvalid("report rs and ss must be of equal length"); + + Oracle memory transmitter = s_oracles[msg.sender]; + if (transmitter.role != Role.Transmitter && msg.sender != s_transmitters[transmitter.index]) + revert ReportInvalid("unauthorized transmitter"); + } + + address[MAX_NUM_ORACLES] memory signed; + uint8 signerCount = 0; + + { + // Verify signatures attached to report + bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); + + Oracle memory o; + // Bounded by MAX_NUM_ORACLES in OCR2Abstract.sol + for (uint256 i = 0; i < rs.length; ++i) { + address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); + o = s_oracles[signer]; + if (o.role != Role.Signer) revert ReportInvalid("address not authorized to sign"); + if (signed[o.index] != address(0)) revert ReportInvalid("non-unique signature"); + signed[o.index] = signer; + signerCount += 1; + } + } + + _report(initialGas, msg.sender, signerCount, signed, report); + } +} From 5b54684a0ef945949699ab55abfb96395cc63960 Mon Sep 17 00:00:00 2001 From: Tate Date: Thu, 30 Nov 2023 14:10:48 -0700 Subject: [PATCH 239/271] Bump chainlink-testing-framework to get postgres ecr changes (#11435) --- integration-tests/docker/test_env/cl_node.go | 9 ++++++--- integration-tests/docker/test_env/test_env.go | 5 ++++- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/integration-tests/docker/test_env/cl_node.go b/integration-tests/docker/test_env/cl_node.go index c781ddfc1b8..7dc077ad34b 100644 --- a/integration-tests/docker/test_env/cl_node.go +++ b/integration-tests/docker/test_env/cl_node.go @@ -82,10 +82,13 @@ func WithLogWatch(lw *logwatch.LogWatch) ClNodeOption { } } -func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *chainlink.Config, opts ...ClNodeOption) *ClNode { +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) - pgDb := test_env.NewPostgresDb(networks, test_env.WithPostgresDbContainerName(pgDefaultCName)) + pgDb, err := test_env.NewPostgresDb(networks, test_env.WithPostgresDbContainerName(pgDefaultCName)) + if err != nil { + return nil, err + } n := &ClNode{ EnvComponent: test_env.EnvComponent{ ContainerName: nodeDefaultCName, @@ -102,7 +105,7 @@ func NewClNode(networks []string, imageName, imageVersion string, nodeConfig *ch for _, opt := range opts { opt(n) } - return n + return n, nil } func (n *ClNode) SetTestLogger(t *testing.T) { diff --git a/integration-tests/docker/test_env/test_env.go b/integration-tests/docker/test_env/test_env.go index 4b0cefddd55..030a3ae9622 100644 --- a/integration-tests/docker/test_env/test_env.go +++ b/integration-tests/docker/test_env/test_env.go @@ -158,7 +158,10 @@ func (te *CLClusterTestEnv) StartClCluster(nodeConfig *chainlink.Config, count i opts = append(opts, WithSecrets(secretsConfig)) te.ClCluster = &ClCluster{} for i := 0; i < count; i++ { - ocrNode := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, opts...) + ocrNode, err := NewClNode([]string{te.Network.Name}, os.Getenv("CHAINLINK_IMAGE"), os.Getenv("CHAINLINK_VERSION"), nodeConfig, opts...) + if err != nil { + return err + } te.ClCluster.Nodes = append(te.ClCluster.Nodes, ocrNode) } } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index b03819e2066..68b46d8a26a 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -25,7 +25,7 @@ require ( github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde - github.com/smartcontractkit/chainlink-testing-framework v1.19.7 + github.com/smartcontractkit/chainlink-testing-framework v1.20.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 74fe9e4dc4d..46c132d6ff1 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2386,8 +2386,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3/go.mod h1:UfW7/PZKon+iDEHtrHOfvMnS5GfYOW/SdMZ6P97rPEk= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 h1:yxaHuDTtj2xxtsR8b+LJw8xDvyr6v/F6GP3InsP4wPI= github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664/go.mod h1:3Fa+HQTZ3R3fPC0hUqugvoo+NEeo8Y4J2WOnQfi7O34= -github.com/smartcontractkit/chainlink-testing-framework v1.19.7 h1:8oEduPXC5kBwnNwE5qdK+h/NZ2qOlxK0hgtedS+/St0= -github.com/smartcontractkit/chainlink-testing-framework v1.19.7/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= +github.com/smartcontractkit/chainlink-testing-framework v1.20.0 h1:gQPQRKJuMh6QTAIMkqZ7v5WkjEmbcoMIX/V6WPVrvuI= +github.com/smartcontractkit/chainlink-testing-framework v1.20.0/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= From 74e4c6819467c973e6dea77b651995d5702e0cc3 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Thu, 30 Nov 2023 22:30:40 +0100 Subject: [PATCH 240/271] Improve randomize testdb sequences (#11433) --- core/cmd/shell_local.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/cmd/shell_local.go b/core/cmd/shell_local.go index dc0d44f3189..a1f7fdb857c 100644 --- a/core/cmd/shell_local.go +++ b/core/cmd/shell_local.go @@ -22,6 +22,7 @@ import ( gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/fatih/color" + "github.com/lib/pq" "github.com/kylelemons/godebug/diff" "github.com/pkg/errors" @@ -834,7 +835,9 @@ func (m *failedToRandomizeTestDBSequencesError) Error() string { // randomizeTestDBSequences randomizes sequenced table columns sequence // This is necessary as to avoid false positives in some test cases. func randomizeTestDBSequences(db *sqlx.DB) error { - seqRows, err := db.Query(`SELECT sequence_schema, sequence_name FROM information_schema.sequences WHERE sequence_schema = $1`, "public") + // not ideal to hard code this, but also not safe to do it programmatically :( + schemas := pq.Array([]string{"public", "evm"}) + seqRows, err := db.Query(`SELECT sequence_schema, sequence_name, minimum_value FROM information_schema.sequences WHERE sequence_schema IN ($1)`, schemas) if err != nil { return fmt.Errorf("%s: error fetching sequences: %s", failedToRandomizeTestDBSequencesError{}, err) } @@ -842,7 +845,8 @@ func randomizeTestDBSequences(db *sqlx.DB) error { defer seqRows.Close() for seqRows.Next() { var sequenceSchema, sequenceName string - if err = seqRows.Scan(&sequenceSchema, &sequenceName); err != nil { + var minimumSequenceValue int64 + if err = seqRows.Scan(&sequenceSchema, &sequenceName, &minimumSequenceValue); err != nil { return fmt.Errorf("%s: failed scanning sequence rows: %s", failedToRandomizeTestDBSequencesError{}, err) } @@ -855,6 +859,7 @@ func randomizeTestDBSequences(db *sqlx.DB) error { if err != nil { return fmt.Errorf("%s: failed to generate random number", failedToRandomizeTestDBSequencesError{}) } + randNum.Add(randNum, big.NewInt(minimumSequenceValue)) if _, err = db.Exec(fmt.Sprintf("ALTER SEQUENCE %s.%s RESTART WITH %d", sequenceSchema, sequenceName, randNum)); err != nil { return fmt.Errorf("%s: failed to alter and restart %s sequence: %w", failedToRandomizeTestDBSequencesError{}, sequenceName, err) From 82eb917140a4eb27c398cbff67bf7bb57f05e7a2 Mon Sep 17 00:00:00 2001 From: ferglor <19188060+ferglor@users.noreply.github.com> Date: Thu, 30 Nov 2023 22:33:57 +0000 Subject: [PATCH 241/271] Refactor evm registry packages (#11374) --- core/scripts/chaincli/handler/debug.go | 6 ++--- core/scripts/chaincli/handler/keeper.go | 2 +- core/scripts/chaincli/handler/report.go | 2 +- core/services/ocr2/delegate.go | 4 ++-- .../plugins/ocr2keeper/custom_telemetry.go | 2 +- .../{evm20 => evmregistry/v20}/abi.go | 0 .../{evm20 => evmregistry/v20}/abi_test.go | 0 .../{evm20 => evmregistry/v20}/encoder.go | 0 .../v20}/encoder_test.go | 0 .../{evm20 => evmregistry/v20}/head.go | 0 .../v20}/log_provider.go | 1 + .../v20}/mocks/registry.go | 0 .../{evm20 => evmregistry/v20}/registry.go | 1 + .../v20}/registry_test.go | 0 .../{evm21 => evmregistry/v21}/active_list.go | 2 +- .../v21}/active_list_test.go | 2 +- .../v21}/autotelemetry21/custom_telemetry.go | 2 +- .../autotelemetry21/custom_telemetry_test.go | 2 +- .../v21}/block_subscriber.go | 0 .../v21}/block_subscriber_test.go | 0 .../{evm21 => evmregistry/v21}/core/abi.go | 0 .../v21}/core/interfaces.go | 0 .../v21}/core/mocks/upkeep_state_reader.go | 0 .../v21}/core/payload.go | 0 .../v21}/core/payload_test.go | 0 .../v21}/core/testutil.go | 0 .../v21}/core/trigger.go | 0 .../v21}/core/trigger_test.go | 0 .../{evm21 => evmregistry/v21}/core/type.go | 0 .../v21}/core/type_test.go | 0 .../{evm21 => evmregistry/v21}/core/utils.go | 0 .../v21}/core/utils_test.go | 0 .../v21}/encoding/encoder.go | 2 +- .../v21}/encoding/encoder_test.go | 2 +- .../v21}/encoding/interface.go | 3 +-- .../v21}/encoding/packer.go | 5 ++-- .../v21}/encoding/packer_test.go | 5 ++-- .../{evm21 => evmregistry/v21}/keyring.go | 0 .../v21}/keyring_test.go | 0 .../v21}/logprovider/block_time.go | 0 .../v21}/logprovider/block_time_test.go | 0 .../v21}/logprovider/buffer.go | 0 .../v21}/logprovider/buffer_test.go | 2 +- .../v21}/logprovider/factory.go | 2 +- .../v21}/logprovider/filter.go | 0 .../v21}/logprovider/filter_store.go | 0 .../v21}/logprovider/filter_store_test.go | 0 .../v21}/logprovider/filter_test.go | 0 .../v21}/logprovider/integration_test.go | 10 ++++---- .../v21}/logprovider/log_packer.go | 2 +- .../v21}/logprovider/provider.go | 3 ++- .../v21}/logprovider/provider_life_cycle.go | 0 .../logprovider/provider_life_cycle_test.go | 2 +- .../v21}/logprovider/provider_test.go | 0 .../v21}/logprovider/recoverer.go | 3 ++- .../v21}/logprovider/recoverer_test.go | 4 ++-- .../v21}/mercury/mercury.go | 0 .../v21}/mercury/mercury_test.go | 0 .../v21}/mercury/streams/streams.go | 8 +++---- .../v21}/mercury/streams/streams_test.go | 8 +++---- .../v21}/mercury/upkeep_failure_reasons.go | 0 .../v21}/mercury/upkeep_states.go | 0 .../v21}/mercury/v02/request.go | 2 +- .../v21}/mercury/v02/v02_request_test.go | 3 +-- .../v21}/mercury/v03/request.go | 2 +- .../v21}/mercury/v03/v03_request_test.go | 5 ++-- .../v21}/mocks/http_client.go | 0 .../v21}/mocks/registry.go | 0 .../v21}/payload_builder.go | 4 ++-- .../v21}/payload_builder_test.go | 4 ++-- .../{evm21 => evmregistry/v21}/registry.go | 8 +++---- .../v21}/registry_check_pipeline.go | 4 ++-- .../v21}/registry_check_pipeline_test.go | 6 ++--- .../v21}/registry_test.go | 6 ++--- .../{evm21 => evmregistry/v21}/services.go | 8 +++---- .../v21}/testdata/btc-usd.json | 0 .../v21}/testdata/eth-usd.json | 0 .../v21}/transmit/cache.go | 0 .../v21}/transmit/cache_test.go | 0 .../v21}/transmit/encoding.go | 0 .../v21}/transmit/encoding_test.go | 2 +- .../v21}/transmit/event_provider.go | 3 ++- .../v21}/transmit/event_provider_test.go | 2 +- .../v21}/upkeep_provider.go | 2 +- .../v21}/upkeep_provider_test.go | 2 +- .../v21}/upkeepstate/orm.go | 0 .../v21}/upkeepstate/orm_test.go | 0 .../v21}/upkeepstate/scanner.go | 2 +- .../v21}/upkeepstate/scanner_test.go | 0 .../v21}/upkeepstate/store.go | 3 ++- .../v21}/upkeepstate/store_test.go | 0 .../plugins/ocr2keeper/integration_21_test.go | 2 +- core/services/ocr2/plugins/ocr2keeper/util.go | 24 +++++++++---------- integration-tests/smoke/automation_test.go | 2 +- 94 files changed, 92 insertions(+), 91 deletions(-) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/abi.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/abi_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/encoder.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/encoder_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/head.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/log_provider.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/mocks/registry.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/registry.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm20 => evmregistry/v20}/registry_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/active_list.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/active_list_test.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/autotelemetry21/custom_telemetry.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/autotelemetry21/custom_telemetry_test.go (97%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/block_subscriber.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/block_subscriber_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/abi.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/interfaces.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/mocks/upkeep_state_reader.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/payload.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/payload_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/testutil.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/trigger.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/trigger_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/type.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/type_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/utils.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/core/utils_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/encoding/encoder.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/encoding/encoder_test.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/encoding/interface.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/encoding/packer.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/encoding/packer_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/keyring.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/keyring_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/block_time.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/block_time_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/buffer.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/buffer_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/factory.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/filter.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/filter_store.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/filter_store_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/filter_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/integration_test.go (97%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/log_packer.go (96%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/provider.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/provider_life_cycle.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/provider_life_cycle_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/provider_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/recoverer.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/logprovider/recoverer_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/mercury.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/mercury_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/streams/streams.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/streams/streams_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/upkeep_failure_reasons.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/upkeep_states.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/v02/request.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/v02/v02_request_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/v03/request.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mercury/v03/v03_request_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mocks/http_client.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/mocks/registry.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/payload_builder.go (95%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/payload_builder_test.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/registry.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/registry_check_pipeline.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/registry_check_pipeline_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/registry_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/services.go (96%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/testdata/btc-usd.json (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/testdata/eth-usd.json (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/transmit/cache.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/transmit/cache_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/transmit/encoding.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/transmit/encoding_test.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/transmit/event_provider.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/transmit/event_provider_test.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeep_provider.go (97%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeep_provider_test.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeepstate/orm.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeepstate/orm_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeepstate/scanner.go (98%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeepstate/scanner_test.go (100%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeepstate/store.go (99%) rename core/services/ocr2/plugins/ocr2keeper/{evm21 => evmregistry/v21}/upkeepstate/store_test.go (100%) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 4938907669f..fec8c6cd414 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -26,9 +26,9 @@ import ( "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/services/ocr2/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "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" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" ) diff --git a/core/scripts/chaincli/handler/keeper.go b/core/scripts/chaincli/handler/keeper.go index 453ef43a566..439532430ab 100644 --- a/core/scripts/chaincli/handler/keeper.go +++ b/core/scripts/chaincli/handler/keeper.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/umbracle/ethgo/abi" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" diff --git a/core/scripts/chaincli/handler/report.go b/core/scripts/chaincli/handler/report.go index 8381bbc1374..622963f1ac0 100644 --- a/core/scripts/chaincli/handler/report.go +++ b/core/scripts/chaincli/handler/report.go @@ -22,7 +22,7 @@ import ( ocr2keepers20 "github.com/smartcontractkit/chainlink-automation/pkg/v2" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" - evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm20" + evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20" ) type OCR2ReportDataElem struct { diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index af56c1e7a99..02341821375 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -50,8 +50,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/median" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21" - ocr2keeper21core "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21" + ocr2keeper21core "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ocr2vrfconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/config" ocr2coordinator "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/coordinator" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2vrf/juelsfeecoin" diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go index fd43747f52a..d250df4afe1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" + evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/abi.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/abi.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/abi.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/abi.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/abi_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/abi_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/abi_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/abi_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/encoder.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/encoder.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/encoder.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/encoder.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/encoder_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/encoder_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/encoder_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/encoder_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/head.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/head.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/head.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/head.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/log_provider.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/log_provider.go index 7ab641d2bf7..45884d2f726 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/log_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/log_provider.go @@ -16,6 +16,7 @@ import ( "github.com/smartcontractkit/chainlink-automation/pkg/v2/encoding" "github.com/smartcontractkit/chainlink-common/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" registry "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/keeper_registry_wrapper2_0" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/mocks/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/mocks/registry.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/mocks/registry.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm20/registry.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go index 682ebb6e2a3..a6a2f40f855 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm20/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry.go @@ -20,6 +20,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v2" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm20/registry_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v20/registry_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go index 9f825556331..7ea476ee773 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list.go @@ -6,7 +6,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) // ActiveUpkeepList is a list to manage active upkeep IDs diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go index 06b9979eefc..378589d1779 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/active_list_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/active_list_test.go @@ -9,7 +9,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestActiveUpkeepList(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go index 0ed4c84dbb5..e7e9728c3a0 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry.go @@ -15,7 +15,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" + evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/utils" diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry_test.go similarity index 97% rename from core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry_test.go index 4318d9aac60..5fce8718cba 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/autotelemetry21/custom_telemetry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/autotelemetry21/custom_telemetry_test.go @@ -10,7 +10,7 @@ import ( headtracker "github.com/smartcontractkit/chainlink/v2/core/chains/evm/headtracker/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" - evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" + evm "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" ) // const historySize = 4 diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/block_subscriber_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/block_subscriber_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/abi.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/abi.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/abi.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/abi.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/interfaces.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/interfaces.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/interfaces.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/interfaces.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks/upkeep_state_reader.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks/upkeep_state_reader.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/mocks/upkeep_state_reader.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/payload.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/payload_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/payload_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/payload_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/testutil.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/testutil.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/testutil.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/testutil.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/trigger_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/trigger_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/type.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/type.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/type_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/type_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/type_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/utils.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/utils.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/utils_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/core/utils_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core/utils_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go index fadf55633d0..89fcbb4a0ef 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder.go @@ -7,7 +7,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) var ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go index 63ff2650be2..1376e2a9bb3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/encoder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go @@ -9,7 +9,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestReportEncoder_EncodeExtract(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go index cf98115d76f..83ace2492f5 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -5,10 +5,9 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" - "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 ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go index 0157d1baf8e..57013a6277a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go @@ -9,10 +9,9 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "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). diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go index b52b777dbe5..42fcd40d618 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go @@ -14,11 +14,10 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" - "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/evm21/core" + "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) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/keyring.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/keyring.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/keyring.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/keyring.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/keyring_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/keyring_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/keyring_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/keyring_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/block_time.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/block_time.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/block_time_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/block_time_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/block_time_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go index a973ea6e19c..046c93a428a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/buffer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/buffer_test.go @@ -13,7 +13,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestLogEventBuffer_GetBlocksInRange(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/factory.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/factory.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go index 38342550c58..2b48fec2b37 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/factory.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/factory.go @@ -8,7 +8,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) // New creates a new log event provider and recoverer. diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter_store.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_store.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter_store.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_store.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter_store_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_store_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter_store_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_store_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/filter_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/filter_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go similarity index 97% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go index dd90b5e00bf..caad1adc9ad 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/integration_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/integration_test.go @@ -31,8 +31,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - kevmcore "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" + evmregistry21 "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/logprovider" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) @@ -150,7 +150,7 @@ func TestIntegration_LogEventProvider_UpdateConfig(t *testing.T) { require.Equal(t, 1, len(addrs)) t.Run("update filter config", func(t *testing.T) { - upkeepID := kevmcore.GenUpkeepID(ocr2keepers.LogTrigger, "111") + upkeepID := evmregistry21.GenUpkeepID(ocr2keepers.LogTrigger, "111") id := upkeepID.BigInt() cfg := newPlainLogTriggerConfig(addrs[0]) b, err := ethClient.BlockByHash(ctx, backend.Commit()) @@ -182,7 +182,7 @@ func TestIntegration_LogEventProvider_UpdateConfig(t *testing.T) { }) t.Run("register same log filter", func(t *testing.T) { - upkeepID := kevmcore.GenUpkeepID(ocr2keepers.LogTrigger, "222") + upkeepID := evmregistry21.GenUpkeepID(ocr2keepers.LogTrigger, "222") id := upkeepID.BigInt() cfg := newPlainLogTriggerConfig(addrs[0]) b, err := ethClient.BlockByHash(ctx, backend.Commit()) @@ -663,7 +663,7 @@ func setupDependencies(t *testing.T, db *sqlx.DB, backend *backends.SimulatedBac return lp, ethClient } -func setup(lggr logger.Logger, poller logpoller.LogPoller, c client.Client, stateStore kevmcore.UpkeepStateReader, filterStore logprovider.UpkeepFilterStore, opts *logprovider.LogTriggersOptions) (logprovider.LogEventProvider, logprovider.LogRecoverer) { +func setup(lggr logger.Logger, poller logpoller.LogPoller, c client.Client, stateStore evmregistry21.UpkeepStateReader, filterStore logprovider.UpkeepFilterStore, opts *logprovider.LogTriggersOptions) (logprovider.LogEventProvider, logprovider.LogRecoverer) { packer := logprovider.NewLogEventsPacker() if opts == nil { o := logprovider.NewOptions(200) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/log_packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/log_packer.go similarity index 96% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/log_packer.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/log_packer.go index 655d7dacfe6..5902af73f03 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/log_packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/log_packer.go @@ -7,7 +7,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) type LogDataPacker interface { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go index a3887cbe787..2dabcc82671 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider.go @@ -19,10 +19,11 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go index c58bd859510..278c727a06a 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_life_cycle_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_life_cycle_test.go @@ -16,7 +16,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestLogEventProvider_LifeCycle(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/provider_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/provider_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go index 98eb0b291bc..c7f6884426f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer.go @@ -20,10 +20,11 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go index 10e58bf26b3..89b19b4a819 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider/recoverer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider/recoverer_test.go @@ -23,8 +23,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core/mocks" + "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/core/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/mercury_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index f98d4ed2df4..aec23431921 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -21,10 +21,10 @@ import ( 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/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" - v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" - v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" + "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" + v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02" + v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index baa277e0357..abcc37dca18 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -26,10 +26,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "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/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" - v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02" - v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03" + "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" + v02 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02" + v03 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03" ) type MockMercuryConfigProvider struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_failure_reasons.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_failure_reasons.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_states.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/upkeep_states.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go index 4ce7893a0b6..8135617b6fd 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/request.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go index 2aecc0df771..ac945859d89 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v02/v02_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v02/v02_request_test.go @@ -18,11 +18,10 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "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/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" ) const ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go index 1c607889741..7002e2f30ac 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go @@ -17,7 +17,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go index 049448f8f76..16ccdac5d1f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/v03/v03_request_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/v03_request_test.go @@ -16,12 +16,11 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "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/evm21/mocks" + "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/mocks" "github.com/smartcontractkit/chainlink/v2/core/utils" "github.com/stretchr/testify/assert" - - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury" ) const ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mocks/http_client.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mocks/http_client.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/http_client.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/mocks/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/mocks/registry.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mocks/registry.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go similarity index 95% rename from core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go index e138f440e49..37c458ae6eb 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder.go @@ -6,8 +6,8 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" + "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/logprovider" ) type payloadBuilder struct { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go index d7c2312d193..7e55267f6d3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/payload_builder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/payload_builder_test.go @@ -12,8 +12,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" + "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/logprovider" ) func TestNewPayloadBuilder(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/registry.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go index e6db3e66e69..bb05b8029fe 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go @@ -28,10 +28,10 @@ import ( 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/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "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/logprovider" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go index b0db3f7dd13..f5a931aae45 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline.go @@ -12,8 +12,8 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" + "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 ( diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go index 4eaf334f7c6..a14aaec0c5e 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_check_pipeline_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_check_pipeline_test.go @@ -26,9 +26,9 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "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/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mocks" + "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/mocks" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go index fdc6675da85..f3e4402092c 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/registry_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry_test.go @@ -23,9 +23,9 @@ import ( "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/plugins/ocr2keeper/evm21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" + "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/logprovider" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go similarity index 96% rename from core/services/ocr2/plugins/ocr2keeper/evm21/services.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go index 35d2f82942b..71ccee872ff 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/services.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/services.go @@ -16,10 +16,10 @@ import ( 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/evm21/encoding" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/logprovider" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/transmit" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate" + "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/logprovider" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/testdata/btc-usd.json b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/testdata/btc-usd.json similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/testdata/btc-usd.json rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/testdata/btc-usd.json diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/testdata/eth-usd.json b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/testdata/eth-usd.json similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/testdata/eth-usd.json rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/testdata/eth-usd.json diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/transmit/cache_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/cache_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go index 7415b3701f8..da32376aab6 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/encoding_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/encoding_test.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" 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/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestTransmitEventLog(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go index 95f5ec0bf11..f66711c2b71 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider.go @@ -11,11 +11,12 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" 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/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go index 8d56fec22a5..950ec810402 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/transmit/event_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit/event_provider_test.go @@ -19,7 +19,7 @@ import ( iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestTransmitEventProvider_Sanity(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go similarity index 97% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go index 5b6d4ac7032..b30215e4fbf 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider.go @@ -7,7 +7,7 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) var _ ocr2keepers.ConditionalUpkeepProvider = &upkeepProvider{} diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go index 69bfabb259f..81ea3913188 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeep_provider_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeep_provider_test.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) func TestUpkeepProvider_GetActiveUpkeeps(t *testing.T) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/orm_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/orm_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/scanner.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go similarity index 98% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/scanner.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go index d70611313af..30a50977d17 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/scanner.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner.go @@ -11,7 +11,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" 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/plugins/ocr2keeper/evm21/logprovider" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/logprovider" "github.com/smartcontractkit/chainlink/v2/core/services/pg" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/scanner_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/scanner_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/scanner_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go similarity index 99% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go index 505959b8f39..4a4de5ea1ad 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store.go @@ -11,8 +11,9 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/logger" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/pg" "github.com/smartcontractkit/chainlink/v2/core/utils" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go similarity index 100% rename from core/services/ocr2/plugins/ocr2keeper/evm21/upkeepstate/store_test.go rename to core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/upkeepstate/store_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go index 01418b9f761..7ccd785a668 100644 --- a/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/integration_21_test.go @@ -54,7 +54,7 @@ import ( "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/ocr2/plugins/ocr2keeper" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/services/pipeline" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" ) diff --git a/core/services/ocr2/plugins/ocr2keeper/util.go b/core/services/ocr2/plugins/ocr2keeper/util.go index 0c81b16f0fa..02a0d033bc1 100644 --- a/core/services/ocr2/plugins/ocr2keeper/util.go +++ b/core/services/ocr2/plugins/ocr2keeper/util.go @@ -20,9 +20,9 @@ import ( "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/ocr2/models" - kevm20 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm20" - kevm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21" - kevm21transmit "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/transmit" + evmregistry20 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v20" + evmregistry21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" + evmregistry21transmit "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/transmit" "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" @@ -73,11 +73,11 @@ func EVMDependencies20( lggr logger.Logger, chain legacyevm.Chain, pr pipeline.Runner, -) (evmrelay.OCR2KeeperProvider, *kevm20.EvmRegistry, Encoder20, *kevm20.LogProvider, error) { +) (evmrelay.OCR2KeeperProvider, *evmregistry20.EvmRegistry, Encoder20, *evmregistry20.LogProvider, error) { var err error var keeperProvider evmrelay.OCR2KeeperProvider - var registry *kevm20.EvmRegistry + var registry *evmregistry20.EvmRegistry // the provider will be returned as a dependency if keeperProvider, err = EVMProvider(db, chain, lggr, spec, pr); err != nil { @@ -85,17 +85,17 @@ func EVMDependencies20( } rAddr := ethkey.MustEIP55Address(spec.OCR2OracleSpec.ContractID).Address() - if registry, err = kevm20.NewEVMRegistryService(rAddr, chain, lggr); err != nil { + if registry, err = evmregistry20.NewEVMRegistryService(rAddr, chain, lggr); err != nil { return nil, nil, nil, nil, err } - encoder := kevm20.EVMAutomationEncoder20{} + encoder := evmregistry20.EVMAutomationEncoder20{} // lookback blocks is hard coded and should provide ample time for logs // to be detected in most cases var lookbackBlocks int64 = 250 // TODO: accept a version of the registry contract and use the correct interfaces - logProvider, err := kevm20.NewLogProvider(lggr, chain.LogPoller(), rAddr, chain.Client(), lookbackBlocks) + logProvider, err := evmregistry20.NewLogProvider(lggr, chain.LogPoller(), rAddr, chain.Client(), lookbackBlocks) return keeperProvider, registry, encoder, logProvider, err } @@ -105,7 +105,7 @@ func FilterNamesFromSpec20(spec *job.OCR2OracleSpec) (names []string, err error) if err != nil { return nil, err } - return []string{kevm20.LogProviderFilterName(addr.Address()), kevm20.UpkeepFilterName(addr.Address())}, err + return []string{evmregistry20.LogProviderFilterName(addr.Address()), evmregistry20.UpkeepFilterName(addr.Address())}, err } func EVMDependencies21( @@ -117,7 +117,7 @@ func EVMDependencies21( mc *models.MercuryCredentials, keyring ocrtypes.OnchainKeyring, dbCfg pg.QConfig, -) (evmrelay.OCR2KeeperProvider, kevm21.AutomationServices, error) { +) (evmrelay.OCR2KeeperProvider, evmregistry21.AutomationServices, error) { var err error var keeperProvider evmrelay.OCR2KeeperProvider @@ -128,7 +128,7 @@ func EVMDependencies21( } rAddr := ethkey.MustEIP55Address(oSpec.ContractID).Address() - services, err := kevm21.New(rAddr, chain, mc, keyring, lggr, db, dbCfg) + services, err := evmregistry21.New(rAddr, chain, mc, keyring, lggr, db, dbCfg) if err != nil { return nil, nil, err } @@ -141,5 +141,5 @@ func FilterNamesFromSpec21(spec *job.OCR2OracleSpec) (names []string, err error) if err != nil { return nil, err } - return []string{kevm21transmit.EventProviderFilterName(addr.Address()), kevm21.RegistryUpkeepFilterName(addr.Address())}, err + return []string{evmregistry21transmit.EventProviderFilterName(addr.Address()), evmregistry21.RegistryUpkeepFilterName(addr.Address())}, err } diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index cdc8748676f..81b484f4e73 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -23,7 +23,7 @@ import ( cltypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evm21/mercury/streams" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/integration-tests/actions" From 9bd3ecb16e5c682b7dbbbb40671677ef98a8f682 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 30 Nov 2023 17:03:09 -0600 Subject: [PATCH 242/271] core/services/chainlink: pass static ver & sha to HealtChecker (#11432) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- core/services/chainlink/application.go | 3 ++- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c1ed59097cc..197716c2b28 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index de86a457218..c6f922432b4 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1504,8 +1504,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde h1:yulX+cnJbiS/iACjOdJfFtD5bD9M6TnXy7Og/PBafew= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 h1:68YZ874EBGEGwT3lcg6Y+3GZXzyNDeDDiqLHvNMBKYc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= diff --git a/core/services/chainlink/application.go b/core/services/chainlink/application.go index 1c9cd106ba9..5c204d693e9 100644 --- a/core/services/chainlink/application.go +++ b/core/services/chainlink/application.go @@ -20,6 +20,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/loop" commonservices "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/static" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/build" @@ -424,7 +425,7 @@ func NewApplication(opts ApplicationOpts) (Application, error) { globalLogger.Debug("Off-chain reporting v2 disabled") } - healthChecker := commonservices.NewChecker() + healthChecker := commonservices.NewChecker(static.Version, static.Sha) var lbs []utils.DependentAwaiter for _, c := range legacyEVMChains.Slice() { diff --git a/go.mod b/go.mod index 688f5810529..41255bc4f02 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 diff --git a/go.sum b/go.sum index d522aabb08a..cc3e1303774 100644 --- a/go.sum +++ b/go.sum @@ -1507,8 +1507,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde h1:yulX+cnJbiS/iACjOdJfFtD5bD9M6TnXy7Og/PBafew= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 h1:68YZ874EBGEGwT3lcg6Y+3GZXzyNDeDDiqLHvNMBKYc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 68b46d8a26a..220dc86b455 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 github.com/smartcontractkit/chainlink-testing-framework v1.20.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 46c132d6ff1..134168a8667 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2376,8 +2376,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde h1:yulX+cnJbiS/iACjOdJfFtD5bD9M6TnXy7Og/PBafew= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231129142348-7087174fecde/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 h1:68YZ874EBGEGwT3lcg6Y+3GZXzyNDeDDiqLHvNMBKYc= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= From 5969e1f654dee1fb90d5654ff51fbf824ff41013 Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 1 Dec 2023 10:11:40 +0000 Subject: [PATCH 243/271] [chore] Refactor ResultsRunSaver to not expose runResults (#11398) --- core/services/ocr/delegate.go | 17 ++-- core/services/ocr2/delegate.go | 85 ++++--------------- core/services/ocr2/plugins/median/services.go | 32 ++++--- core/services/ocr2/plugins/mercury/plugin.go | 11 +-- core/services/ocrcommon/data_source.go | 24 +++--- core/services/ocrcommon/data_source_test.go | 22 +++-- core/services/ocrcommon/run_saver.go | 20 +++-- core/services/ocrcommon/run_saver_test.go | 6 +- .../relay/evm/mercury/v1/data_source.go | 13 ++- .../relay/evm/mercury/v1/data_source_test.go | 23 ++--- .../relay/evm/mercury/v2/data_source.go | 13 ++- .../relay/evm/mercury/v2/data_source_test.go | 11 +++ .../relay/evm/mercury/v3/data_source.go | 13 ++- .../relay/evm/mercury/v3/data_source_test.go | 11 +++ 14 files changed, 143 insertions(+), 158 deletions(-) diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go index ac78002d450..f473c93b1f0 100644 --- a/core/services/ocr/delegate.go +++ b/core/services/ocr/delegate.go @@ -274,7 +274,12 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e effectiveTransmitterAddress, ) - runResults := make(chan *pipeline.Run, chain.Config().JobPipeline().ResultWriteQueueDepth()) + saver := ocrcommon.NewResultRunSaver( + d.pipelineRunner, + lggr, + cfg.JobPipeline().MaxSuccessfulRuns(), + cfg.JobPipeline().ResultWriteQueueDepth(), + ) var configOverrider ocrtypes.ConfigOverrider configOverriderService, err := d.maybeCreateConfigOverrider(lggr, chain, concreteSpec.ContractAddress) @@ -311,7 +316,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e jb, *jb.PipelineSpec, lggr, - runResults, + saver, enhancedTelemChan, ), LocalConfig: lc, @@ -334,13 +339,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e // RunResultSaver needs to be started first so its available // to read db writes. It is stopped last after the Oracle is shut down // so no further runs are enqueued and we can drain the queue. - services = append([]job.ServiceCtx{ocrcommon.NewResultRunSaver( - runResults, - d.pipelineRunner, - make(chan struct{}), - lggr, - cfg.JobPipeline().MaxSuccessfulRuns(), - )}, services...) + services = append([]job.ServiceCtx{saver}, services...) } return services, nil diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go index 02341821375..16f02282afb 100644 --- a/core/services/ocr2/delegate.go +++ b/core/services/ocr2/delegate.go @@ -426,24 +426,22 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { spec.CaptureEATelemetry = d.cfg.OCR2().CaptureEATelemetry() - runResults := make(chan *pipeline.Run, d.cfg.JobPipeline().ResultWriteQueueDepth()) - ctx := lggrCtx.ContextWithValues(context.Background()) switch spec.PluginType { case types.Mercury: - return d.newServicesMercury(ctx, lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + return d.newServicesMercury(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) case types.Median: - return d.newServicesMedian(ctx, lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + return d.newServicesMedian(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) case types.DKG: return d.newServicesDKG(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) case types.OCR2VRF: - return d.newServicesOCR2VRF(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc) + return d.newServicesOCR2VRF(lggr, jb, bootstrapPeers, kb, ocrDB, lc) case types.OCR2Keeper: - return d.newServicesOCR2Keepers(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc, ocrLogger) + return d.newServicesOCR2Keepers(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) case types.Functions: const ( @@ -453,7 +451,7 @@ func (d *Delegate) ServicesForSpec(jb job.Job) ([]job.ServiceCtx, error) { ) thresholdPluginDB := NewDB(d.db, spec.ID, thresholdPluginId, lggr, d.cfg.Database()) s4PluginDB := NewDB(d.db, spec.ID, s4PluginId, lggr, d.cfg.Database()) - return d.newServicesOCR2Functions(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, thresholdPluginDB, s4PluginDB, lc, ocrLogger) + return d.newServicesOCR2Functions(lggr, jb, bootstrapPeers, kb, ocrDB, thresholdPluginDB, s4PluginDB, lc, ocrLogger) case types.GenericPlugin: return d.newServicesGenericPlugin(ctx, lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger) @@ -652,7 +650,6 @@ func (d *Delegate) newServicesMercury( ctx context.Context, lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, ocrDB *db, @@ -720,7 +717,7 @@ func (d *Delegate) newServicesMercury( chEnhancedTelem := make(chan ocrcommon.EnhancedTelemetryMercuryData, 100) - mercuryServices, err2 := mercury.NewServices(jb, mercuryProvider, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, d.cfg.JobPipeline(), chEnhancedTelem, d.mercuryORM, (mercuryutils.FeedID)(*spec.FeedID)) + mercuryServices, err2 := mercury.NewServices(jb, mercuryProvider, d.pipelineRunner, lggr, oracleArgsNoPlugin, d.cfg.JobPipeline(), chEnhancedTelem, d.mercuryORM, (mercuryutils.FeedID)(*spec.FeedID)) if ocrcommon.ShouldCollectEnhancedTelemetryMercury(jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, chEnhancedTelem, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.FeedID.String(), synchronization.EnhancedEAMercury), lggr.Named("EnhancedTelemetryMercury")) @@ -736,7 +733,6 @@ func (d *Delegate) newServicesMedian( ctx context.Context, lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, ocrDB *db, @@ -762,14 +758,18 @@ func (d *Delegate) newServicesMedian( } errorLog := &errorLog{jobID: jb.ID, recordError: d.jobORM.RecordError} enhancedTelemChan := make(chan ocrcommon.EnhancedTelemetryData, 100) - mConfig := median.NewMedianConfig(d.cfg.JobPipeline().MaxSuccessfulRuns(), d.cfg) + mConfig := median.NewMedianConfig( + d.cfg.JobPipeline().MaxSuccessfulRuns(), + d.cfg.JobPipeline().ResultWriteQueueDepth(), + d.cfg, + ) relayer, err := d.RelayGetter.Get(rid) if err != nil { return nil, ErrRelayNotEnabled{Err: err, PluginName: "median", Relay: spec.Relay} } - medianServices, err2 := median.NewMedianServices(ctx, jb, d.isNewlyCreatedJob, relayer, d.pipelineRunner, runResults, lggr, oracleArgsNoPlugin, mConfig, enhancedTelemChan, errorLog) + medianServices, err2 := median.NewMedianServices(ctx, jb, d.isNewlyCreatedJob, relayer, d.pipelineRunner, lggr, oracleArgsNoPlugin, mConfig, enhancedTelemChan, errorLog) if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) { enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry")) @@ -852,7 +852,6 @@ func (d *Delegate) newServicesDKG( func (d *Delegate) newServicesOCR2VRF( lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, ocrDB *db, @@ -1019,28 +1018,16 @@ func (d *Delegate) newServicesOCR2VRF( return nil, errors.Wrap(err2, "new ocr2vrf") } - // RunResultSaver needs to be started first, so it's available - // to read odb writes. It is stopped last after the OraclePlugin is shut down - // so no further runs are enqueued, and we can drain the queue. - runResultSaver := ocrcommon.NewResultRunSaver( - runResults, - d.pipelineRunner, - make(chan struct{}), - lggr, - d.cfg.JobPipeline().MaxSuccessfulRuns(), - ) - // NOTE: we return from here with the services because the OCR2VRF oracles are defined // and exported from the ocr2vrf library. It takes care of running the DKG and OCR2VRF // oracles under the hood together. oracleCtx := job.NewServiceAdapter(oracles) - return []job.ServiceCtx{runResultSaver, vrfProvider, dkgProvider, oracleCtx}, nil + return []job.ServiceCtx{vrfProvider, dkgProvider, oracleCtx}, nil } func (d *Delegate) newServicesOCR2Keepers( lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, ocrDB *db, @@ -1059,18 +1046,17 @@ func (d *Delegate) newServicesOCR2Keepers( switch cfg.ContractVersion { case "v2.1": - return d.newServicesOCR2Keepers21(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) + return d.newServicesOCR2Keepers21(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) case "v2.0": - return d.newServicesOCR2Keepers20(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) + return d.newServicesOCR2Keepers20(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) default: - return d.newServicesOCR2Keepers20(lggr, jb, runResults, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) + return d.newServicesOCR2Keepers20(lggr, jb, bootstrapPeers, kb, ocrDB, lc, ocrLogger, cfg, spec) } } func (d *Delegate) newServicesOCR2Keepers21( lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, ocrDB *db, @@ -1162,19 +1148,7 @@ func (d *Delegate) newServicesOCR2Keepers21( return nil, errors.Wrap(err, "could not create new keepers ocr2 delegate") } - // RunResultSaver needs to be started first, so it's available - // to read odb writes. It is stopped last after the OraclePlugin is shut down - // so no further runs are enqueued, and we can drain the queue. - runResultSaver := ocrcommon.NewResultRunSaver( - runResults, - d.pipelineRunner, - make(chan struct{}), - lggr, - d.cfg.JobPipeline().MaxSuccessfulRuns(), - ) - automationServices := []job.ServiceCtx{ - runResultSaver, keeperProvider, services.Registry(), services.BlockSubscriber(), @@ -1206,7 +1180,6 @@ func (d *Delegate) newServicesOCR2Keepers21( func (d *Delegate) newServicesOCR2Keepers20( lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, ocrDB *db, @@ -1316,20 +1289,8 @@ func (d *Delegate) newServicesOCR2Keepers20( return nil, errors.Wrap(err, "could not create new keepers ocr2 delegate") } - // RunResultSaver needs to be started first, so it's available - // to read odb writes. It is stopped last after the OraclePlugin is shut down - // so no further runs are enqueued, and we can drain the queue. - runResultSaver := ocrcommon.NewResultRunSaver( - runResults, - d.pipelineRunner, - make(chan struct{}), - lggr, - d.cfg.JobPipeline().MaxSuccessfulRuns(), - ) - return []job.ServiceCtx{ job.NewServiceAdapter(runr), - runResultSaver, keeperProvider, rgstry, logProvider, @@ -1340,7 +1301,6 @@ func (d *Delegate) newServicesOCR2Keepers20( func (d *Delegate) newServicesOCR2Functions( lggr logger.SugaredLogger, jb job.Job, - runResults chan *pipeline.Run, bootstrapPeers []commontypes.BootstrapperLocator, kb ocr2key.KeyBundle, functionsOcrDB *db, @@ -1480,18 +1440,7 @@ func (d *Delegate) newServicesOCR2Functions( return nil, errors.Wrap(err, "error calling NewFunctionsServices") } - // RunResultSaver needs to be started first, so it's available - // to read odb writes. It is stopped last after the OraclePlugin is shut down - // so no further runs are enqueued, and we can drain the queue. - runResultSaver := ocrcommon.NewResultRunSaver( - runResults, - d.pipelineRunner, - make(chan struct{}), - lggr, - d.cfg.JobPipeline().MaxSuccessfulRuns(), - ) - - return append([]job.ServiceCtx{runResultSaver, functionsProvider, thresholdProvider, s4Provider}, functionsServices...), nil + return append([]job.ServiceCtx{functionsProvider, thresholdProvider, s4Provider}, functionsServices...), nil } // errorLog implements [loop.ErrorLog] diff --git a/core/services/ocr2/plugins/median/services.go b/core/services/ocr2/plugins/median/services.go index 896bbacb419..2d1ef720545 100644 --- a/core/services/ocr2/plugins/median/services.go +++ b/core/services/ocr2/plugins/median/services.go @@ -25,19 +25,22 @@ import ( type MedianConfig interface { JobPipelineMaxSuccessfulRuns() uint64 + JobPipelineResultWriteQueueDepth() uint64 plugins.RegistrarConfig } // concrete implementation of MedianConfig type medianConfig struct { - jobPipelineMaxSuccessfulRuns uint64 + jobPipelineMaxSuccessfulRuns uint64 + jobPipelineResultWriteQueueDepth uint64 plugins.RegistrarConfig } -func NewMedianConfig(jobPipelineMaxSuccessfulRuns uint64, pluginProcessCfg plugins.RegistrarConfig) MedianConfig { +func NewMedianConfig(jobPipelineMaxSuccessfulRuns uint64, jobPipelineResultWriteQueueDepth uint64, pluginProcessCfg plugins.RegistrarConfig) MedianConfig { return &medianConfig{ - jobPipelineMaxSuccessfulRuns: jobPipelineMaxSuccessfulRuns, - RegistrarConfig: pluginProcessCfg, + jobPipelineMaxSuccessfulRuns: jobPipelineMaxSuccessfulRuns, + jobPipelineResultWriteQueueDepth: jobPipelineResultWriteQueueDepth, + RegistrarConfig: pluginProcessCfg, } } @@ -45,12 +48,15 @@ func (m *medianConfig) JobPipelineMaxSuccessfulRuns() uint64 { return m.jobPipelineMaxSuccessfulRuns } +func (m *medianConfig) JobPipelineResultWriteQueueDepth() uint64 { + return m.jobPipelineResultWriteQueueDepth +} + func NewMedianServices(ctx context.Context, jb job.Job, isNewlyCreatedJob bool, relayer loop.Relayer, pipelineRunner pipeline.Runner, - runResults chan *pipeline.Run, lggr logger.Logger, argsNoPlugin libocr.OCR2OracleArgs, cfg MedianConfig, @@ -69,6 +75,13 @@ func NewMedianServices(ctx context.Context, } spec := jb.OCR2OracleSpec + runSaver := ocrcommon.NewResultRunSaver( + pipelineRunner, + lggr, + cfg.JobPipelineMaxSuccessfulRuns(), + cfg.JobPipelineResultWriteQueueDepth(), + ) + provider, err := relayer.NewPluginProvider(ctx, types.RelayArgs{ ExternalJobID: jb.ExternalJobID, JobID: jb.ID, @@ -104,7 +117,7 @@ func NewMedianServices(ctx context.Context, jb, *jb.PipelineSpec, lggr, - runResults, + runSaver, chEnhancedTelem, ), ocrcommon.NewInMemoryDataSource(pipelineRunner, jb, pipeline.Spec{ ID: jb.ID, @@ -140,13 +153,6 @@ func NewMedianServices(ctx context.Context, abort() return } - runSaver := ocrcommon.NewResultRunSaver( - runResults, - pipelineRunner, - make(chan struct{}), - lggr, - cfg.JobPipelineMaxSuccessfulRuns(), - ) srvs = append(srvs, runSaver, job.NewServiceAdapter(oracle)) if !jb.OCR2OracleSpec.CaptureEATelemetry { lggr.Infof("Enhanced EA telemetry is disabled for job %s", jb.Name.ValueOrZero()) diff --git a/core/services/ocr2/plugins/mercury/plugin.go b/core/services/ocr2/plugins/mercury/plugin.go index bd68ccd8b72..d443008334c 100644 --- a/core/services/ocr2/plugins/mercury/plugin.go +++ b/core/services/ocr2/plugins/mercury/plugin.go @@ -26,13 +26,13 @@ import ( type Config interface { MaxSuccessfulRuns() uint64 + ResultWriteQueueDepth() uint64 } func NewServices( jb job.Job, ocr2Provider commontypes.MercuryProvider, pipelineRunner pipeline.Runner, - runResults chan *pipeline.Run, lggr logger.Logger, argsNoPlugin libocr2.MercuryOracleArgs, cfg Config, @@ -55,6 +55,8 @@ func NewServices( } lggr = lggr.Named("MercuryPlugin").With("jobID", jb.ID, "jobName", jb.Name.ValueOrZero()) + saver := ocrcommon.NewResultRunSaver(pipelineRunner, lggr, cfg.MaxSuccessfulRuns(), cfg.ResultWriteQueueDepth()) + switch feedID.Version() { case 1: ds := mercuryv1.NewDataSource( @@ -63,7 +65,7 @@ func NewServices( jb, *jb.PipelineSpec, lggr, - runResults, + saver, chEnhancedTelem, ocr2Provider.ChainReader(), ocr2Provider.MercuryServerFetcher(), @@ -84,7 +86,7 @@ func NewServices( *jb.PipelineSpec, feedID, lggr, - runResults, + saver, chEnhancedTelem, ocr2Provider.MercuryServerFetcher(), *pluginConfig.LinkFeedID, @@ -104,7 +106,7 @@ func NewServices( *jb.PipelineSpec, feedID, lggr, - runResults, + saver, chEnhancedTelem, ocr2Provider.MercuryServerFetcher(), *pluginConfig.LinkFeedID, @@ -124,6 +126,5 @@ func NewServices( if err != nil { return nil, errors.WithStack(err) } - saver := ocrcommon.NewResultRunSaver(runResults, pipelineRunner, make(chan struct{}), lggr, cfg.MaxSuccessfulRuns()) return []job.ServiceCtx{ocr2Provider, saver, job.NewServiceAdapter(oracle)}, nil } diff --git a/core/services/ocrcommon/data_source.go b/core/services/ocrcommon/data_source.go index 0363a7124b6..706376fbfd1 100644 --- a/core/services/ocrcommon/data_source.go +++ b/core/services/ocrcommon/data_source.go @@ -33,9 +33,13 @@ type inMemoryDataSource struct { chEnhancedTelemetry chan<- EnhancedTelemetryData } +type Saver interface { + Save(run *pipeline.Run) +} + type dataSourceBase struct { inMemoryDataSource - runResults chan<- *pipeline.Run + saver Saver } // dataSource implements dataSourceBase with the proper Observe return type for ocr1 @@ -55,7 +59,7 @@ type ObservationTimestamp struct { ConfigDigest string } -func NewDataSourceV1(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, runResults chan<- *pipeline.Run, chEnhancedTelemetry chan EnhancedTelemetryData) ocr1types.DataSource { +func NewDataSourceV1(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s Saver, chEnhancedTelemetry chan EnhancedTelemetryData) ocr1types.DataSource { return &dataSource{ dataSourceBase: dataSourceBase{ inMemoryDataSource: inMemoryDataSource{ @@ -65,12 +69,12 @@ func NewDataSourceV1(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr lo lggr: lggr, chEnhancedTelemetry: chEnhancedTelemetry, }, - runResults: runResults, + saver: s, }, } } -func NewDataSourceV2(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, runResults chan<- *pipeline.Run, enhancedTelemChan chan EnhancedTelemetryData) median.DataSource { +func NewDataSourceV2(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s Saver, enhancedTelemChan chan EnhancedTelemetryData) median.DataSource { return &dataSourceV2{ dataSourceBase: dataSourceBase{ inMemoryDataSource: inMemoryDataSource{ @@ -80,7 +84,7 @@ func NewDataSourceV2(pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr lo lggr: lggr, chEnhancedTelemetry: enhancedTelemChan, }, - runResults: runResults, + saver: s, }, } } @@ -189,19 +193,13 @@ func (ds *dataSourceBase) observe(ctx context.Context, timestamp ObservationTime return nil, err } - // Do the database write in a non-blocking fashion + // Save() does the database write in a non-blocking fashion // so we can return the observation results immediately. // This is helpful in the case of a blocking API call, where // we reach the passed in context deadline and we want to // immediately return any result we have and do not want to have // a db write block that. - select { - case ds.runResults <- run: - default: - // If we're unable to enqueue a write, still return the value we have but warn. - ds.lggr.Warnf("unable to enqueue run save for job ID %d, buffer full", ds.inMemoryDataSource.spec.JobID) - return ds.inMemoryDataSource.parse(finalResult) - } + ds.saver.Save(run) return ds.inMemoryDataSource.parse(finalResult) } diff --git a/core/services/ocrcommon/data_source_test.go b/core/services/ocrcommon/data_source_test.go index 51a004f1f05..3ffc6b31e8c 100644 --- a/core/services/ocrcommon/data_source_test.go +++ b/core/services/ocrcommon/data_source_test.go @@ -93,8 +93,17 @@ func Test_InMemoryDataSourceWithProm(t *testing.T) { } +type mockSaver struct { + r *pipeline.Run +} + +func (ms *mockSaver) Save(r *pipeline.Run) { + ms.r = r +} + func Test_NewDataSourceV2(t *testing.T) { runner := pipelinemocks.NewRunner(t) + ms := &mockSaver{} runner.On("ExecuteRun", mock.Anything, mock.AnythingOfType("pipeline.Spec"), mock.Anything, mock.Anything). Return(&pipeline.Run{}, pipeline.TaskRunResults{ { @@ -106,16 +115,16 @@ func Test_NewDataSourceV2(t *testing.T) { }, }, nil) - resChan := make(chan *pipeline.Run, 100) - ds := ocrcommon.NewDataSourceV2(runner, job.Job{}, pipeline.Spec{}, logger.TestLogger(t), resChan, nil) + ds := ocrcommon.NewDataSourceV2(runner, job.Job{}, pipeline.Spec{}, logger.TestLogger(t), ms, nil) val, err := ds.Observe(testutils.Context(t), types.ReportTimestamp{}) require.NoError(t, err) - assert.Equal(t, mockValue, val.String()) // returns expected value after pipeline run - assert.Equal(t, &pipeline.Run{}, <-resChan) // expected data properly passed to channel + assert.Equal(t, mockValue, val.String()) // returns expected value after pipeline run + assert.Equal(t, &pipeline.Run{}, ms.r) // expected data properly passed to channel } func Test_NewDataSourceV1(t *testing.T) { runner := pipelinemocks.NewRunner(t) + ms := &mockSaver{} runner.On("ExecuteRun", mock.Anything, mock.AnythingOfType("pipeline.Spec"), mock.Anything, mock.Anything). Return(&pipeline.Run{}, pipeline.TaskRunResults{ { @@ -127,10 +136,9 @@ func Test_NewDataSourceV1(t *testing.T) { }, }, nil) - resChan := make(chan *pipeline.Run, 100) - ds := ocrcommon.NewDataSourceV1(runner, job.Job{}, pipeline.Spec{}, logger.TestLogger(t), resChan, nil) + ds := ocrcommon.NewDataSourceV1(runner, job.Job{}, pipeline.Spec{}, logger.TestLogger(t), ms, nil) val, err := ds.Observe(testutils.Context(t), ocrtypes.ReportTimestamp{}) require.NoError(t, err) assert.Equal(t, mockValue, new(big.Int).Set(val).String()) // returns expected value after pipeline run - assert.Equal(t, &pipeline.Run{}, <-resChan) // expected data properly passed to channel + assert.Equal(t, &pipeline.Run{}, ms.r) // expected data properly passed to channel } diff --git a/core/services/ocrcommon/run_saver.go b/core/services/ocrcommon/run_saver.go index 184226605fe..b1a0fc7b141 100644 --- a/core/services/ocrcommon/run_saver.go +++ b/core/services/ocrcommon/run_saver.go @@ -12,7 +12,7 @@ type RunResultSaver struct { services.StateMachine maxSuccessfulRuns uint64 - runResults <-chan *pipeline.Run + runResults chan *pipeline.Run pipelineRunner pipeline.Runner done chan struct{} logger logger.Logger @@ -24,18 +24,28 @@ func (r *RunResultSaver) HealthReport() map[string]error { func (r *RunResultSaver) Name() string { return r.logger.Name() } -func NewResultRunSaver(runResults <-chan *pipeline.Run, pipelineRunner pipeline.Runner, done chan struct{}, - logger logger.Logger, maxSuccessfulRuns uint64, +func NewResultRunSaver(pipelineRunner pipeline.Runner, + logger logger.Logger, maxSuccessfulRuns uint64, resultsWriteDepth uint64, ) *RunResultSaver { return &RunResultSaver{ maxSuccessfulRuns: maxSuccessfulRuns, - runResults: runResults, + runResults: make(chan *pipeline.Run, resultsWriteDepth), pipelineRunner: pipelineRunner, - done: done, + done: make(chan struct{}), logger: logger.Named("RunResultSaver"), } } +// Save sends the run on the internal `runResults` channel for saving. +// IMPORTANT: if the `runResults` pipeline is full, the run will be dropped. +func (r *RunResultSaver) Save(run *pipeline.Run) { + select { + case r.runResults <- run: + default: + r.logger.Warnw("RunSaver: the write queue was full, dropping run") + } +} + // Start starts RunResultSaver. func (r *RunResultSaver) Start(context.Context) error { return r.StartOnce("RunResultSaver", func() error { diff --git a/core/services/ocrcommon/run_saver_test.go b/core/services/ocrcommon/run_saver_test.go index 7d20a7a202e..73697d181bc 100644 --- a/core/services/ocrcommon/run_saver_test.go +++ b/core/services/ocrcommon/run_saver_test.go @@ -14,13 +14,11 @@ import ( func TestRunSaver(t *testing.T) { pipelineRunner := mocks.NewRunner(t) - rr := make(chan *pipeline.Run, 100) rs := NewResultRunSaver( - rr, pipelineRunner, - make(chan struct{}), logger.TestLogger(t), 1000, + 100, ) require.NoError(t, rs.Start(testutils.Context(t))) for i := 0; i < 100; i++ { @@ -31,7 +29,7 @@ func TestRunSaver(t *testing.T) { args.Get(0).(*pipeline.Run).ID = int64(d) }). Once() - rr <- &pipeline.Run{ID: int64(i)} + rs.Save(&pipeline.Run{ID: int64(i)}) } require.NoError(t, rs.Close()) } diff --git a/core/services/relay/evm/mercury/v1/data_source.go b/core/services/relay/evm/mercury/v1/data_source.go index 79eb32af441..bc94166e566 100644 --- a/core/services/relay/evm/mercury/v1/data_source.go +++ b/core/services/relay/evm/mercury/v1/data_source.go @@ -58,7 +58,7 @@ type datasource struct { jb job.Job spec pipeline.Spec lggr logger.Logger - runResults chan<- *pipeline.Run + saver ocrcommon.Saver orm types.DataSourceORM codec reportcodec.ReportCodec feedID [32]byte @@ -76,8 +76,8 @@ type datasource struct { var _ relaymercuryv1.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader relaymercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { - return &datasource{pr, jb, spec, lggr, rr, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, chainReader relaymercury.ChainReader, fetcher Fetcher, initialBlockNumber *int64, feedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, lggr, s, orm, reportcodec.ReportCodec{}, feedID, sync.RWMutex{}, enhancedTelemChan, chainReader, fetcher, initialBlockNumber, insufficientBlocksCount.WithLabelValues(feedID.String()), zeroBlocksCount.WithLabelValues(feedID.String())} } type ErrEmptyLatestReport struct { @@ -156,11 +156,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) return } - select { - case ds.runResults <- run: - default: - ds.lggr.Warnf("unable to enqueue run save for job ID %d, buffer full", ds.spec.JobID) - } + + ds.saver.Save(run) // NOTE: trrs comes back as _all_ tasks, but we only want the terminal ones // They are guaranteed to be sorted by index asc so should be in the correct order diff --git a/core/services/relay/evm/mercury/v1/data_source_test.go b/core/services/relay/evm/mercury/v1/data_source_test.go index 45a99f29196..d8d7d39dbb8 100644 --- a/core/services/relay/evm/mercury/v1/data_source_test.go +++ b/core/services/relay/evm/mercury/v1/data_source_test.go @@ -52,6 +52,14 @@ func (m *mockFetcher) LatestTimestamp(context.Context) (int64, error) { return 0, nil } +type mockSaver struct { + r *pipeline.Run +} + +func (ms *mockSaver) Save(r *pipeline.Run) { + ms.r = r +} + type mockORM struct { report []byte err error @@ -80,6 +88,9 @@ func TestMercury_Observe(t *testing.T) { fetcher := &mockFetcher{} ds.fetcher = fetcher + saver := &mockSaver{} + ds.saver = saver + trrs := []pipeline.TaskRunResult{ { // benchmark price @@ -293,24 +304,16 @@ func TestMercury_Observe(t *testing.T) { _, err := ds.Observe(ctx, repts, false) assert.EqualError(t, err, "Observe failed while parsing run results: failed to parse Bid: can't convert foo to decimal") }) - t.Run("sends run to runResults channel", func(t *testing.T) { + t.Run("saves run", func(t *testing.T) { for i := range trrs { trrs[i].Result.Value = "123" trrs[i].Result.Error = nil } - ch := make(chan *pipeline.Run, 1) - - ds.runResults = ch _, err := ds.Observe(ctx, repts, false) assert.NoError(t, err) - select { - case run := <-ch: - assert.Equal(t, int64(42), run.ID) - default: - t.Fatal("expected run on channel") - } + assert.Equal(t, int64(42), saver.r.ID) }) }) diff --git a/core/services/relay/evm/mercury/v2/data_source.go b/core/services/relay/evm/mercury/v2/data_source.go index f7b78925130..ec6cf5ad106 100644 --- a/core/services/relay/evm/mercury/v2/data_source.go +++ b/core/services/relay/evm/mercury/v2/data_source.go @@ -39,7 +39,7 @@ type datasource struct { spec pipeline.Spec feedID mercuryutils.FeedID lggr logger.Logger - runResults chan<- *pipeline.Run + saver ocrcommon.Saver orm types.DataSourceORM codec reportcodec.ReportCodec @@ -54,8 +54,8 @@ type datasource struct { var _ relaymercuryv2.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { - return &datasource{pr, jb, spec, feedID, lggr, rr, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, feedID, lggr, s, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} } func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv2.Observation, pipelineExecutionErr error) { @@ -91,11 +91,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) return } - select { - case ds.runResults <- run: - default: - ds.lggr.Warnf("unable to enqueue run save for job ID %d, buffer full", ds.spec.JobID) - } + + ds.saver.Save(run) var parsed parseOutput parsed, pipelineExecutionErr = ds.parse(trrs) diff --git a/core/services/relay/evm/mercury/v2/data_source_test.go b/core/services/relay/evm/mercury/v2/data_source_test.go index 02492031939..d1030327e10 100644 --- a/core/services/relay/evm/mercury/v2/data_source_test.go +++ b/core/services/relay/evm/mercury/v2/data_source_test.go @@ -63,6 +63,14 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg return m.report, m.err } +type mockSaver struct { + r *pipeline.Run +} + +func (ms *mockSaver) Save(r *pipeline.Run) { + ms.r = r +} + func Test_Datasource(t *testing.T) { orm := &mockORM{} ds := &datasource{orm: orm, lggr: logger.TestLogger(t)} @@ -72,6 +80,9 @@ func Test_Datasource(t *testing.T) { fetcher := &mockFetcher{} ds.fetcher = fetcher + saver := &mockSaver{} + ds.saver = saver + goodTrrs := []pipeline.TaskRunResult{ { // bp diff --git a/core/services/relay/evm/mercury/v3/data_source.go b/core/services/relay/evm/mercury/v3/data_source.go index 34dbd13be99..d5e1e5716dd 100644 --- a/core/services/relay/evm/mercury/v3/data_source.go +++ b/core/services/relay/evm/mercury/v3/data_source.go @@ -40,7 +40,7 @@ type datasource struct { spec pipeline.Spec feedID mercuryutils.FeedID lggr logger.Logger - runResults chan<- *pipeline.Run + saver ocrcommon.Saver orm types.DataSourceORM codec reportcodec.ReportCodec @@ -55,8 +55,8 @@ type datasource struct { var _ relaymercuryv3.DataSource = &datasource{} -func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, rr chan *pipeline.Run, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { - return &datasource{pr, jb, spec, feedID, lggr, rr, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} +func NewDataSource(orm types.DataSourceORM, pr pipeline.Runner, jb job.Job, spec pipeline.Spec, feedID mercuryutils.FeedID, lggr logger.Logger, s ocrcommon.Saver, enhancedTelemChan chan ocrcommon.EnhancedTelemetryMercuryData, fetcher LatestReportFetcher, linkFeedID, nativeFeedID mercuryutils.FeedID) *datasource { + return &datasource{pr, jb, spec, feedID, lggr, s, orm, reportcodec.ReportCodec{}, fetcher, linkFeedID, nativeFeedID, sync.RWMutex{}, enhancedTelemChan} } func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestamp, fetchMaxFinalizedTimestamp bool) (obs relaymercuryv3.Observation, pipelineExecutionErr error) { @@ -92,11 +92,8 @@ func (ds *datasource) Observe(ctx context.Context, repts ocrtypes.ReportTimestam pipelineExecutionErr = fmt.Errorf("Observe failed while executing run: %w", pipelineExecutionErr) return } - select { - case ds.runResults <- run: - default: - ds.lggr.Warnf("unable to enqueue run save for job ID %d, buffer full", ds.spec.JobID) - } + + ds.saver.Save(run) var parsed parseOutput parsed, pipelineExecutionErr = ds.parse(trrs) diff --git a/core/services/relay/evm/mercury/v3/data_source_test.go b/core/services/relay/evm/mercury/v3/data_source_test.go index c4b5b4c6100..e03e321d093 100644 --- a/core/services/relay/evm/mercury/v3/data_source_test.go +++ b/core/services/relay/evm/mercury/v3/data_source_test.go @@ -63,6 +63,14 @@ func (m *mockORM) LatestReport(ctx context.Context, feedID [32]byte, qopts ...pg return m.report, m.err } +type mockSaver struct { + r *pipeline.Run +} + +func (ms *mockSaver) Save(r *pipeline.Run) { + ms.r = r +} + func Test_Datasource(t *testing.T) { orm := &mockORM{} ds := &datasource{orm: orm, lggr: logger.TestLogger(t)} @@ -72,6 +80,9 @@ func Test_Datasource(t *testing.T) { fetcher := &mockFetcher{} ds.fetcher = fetcher + saver := &mockSaver{} + ds.saver = saver + goodTrrs := []pipeline.TaskRunResult{ { // bp From 15e7b79c92208feee41d8cf51ca4e5bb8843a531 Mon Sep 17 00:00:00 2001 From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:36:38 +0400 Subject: [PATCH 244/271] [AUTO-7451] Move load test and smoke test to use new actions (#11364) * move load test to use new common action * move smoke test to use common action * fix smoke tests * decrease batch size in load test report prep * add info logs in TestAutomationKeeperNodesDown * remove duplicate registry config in smoke test * use AutomationTest struct in setupAutomationTestDocker * add and use SetupMercuryMock --- .../actions/automationv2/actions.go | 23 ++ .../automationv2_1/automationv2_1_test.go | 233 ++++------- integration-tests/smoke/automation_test.go | 381 +++++++++++------- 3 files changed, 346 insertions(+), 291 deletions(-) diff --git a/integration-tests/actions/automationv2/actions.go b/integration-tests/actions/automationv2/actions.go index f97c17bb94f..febdd892150 100644 --- a/integration-tests/actions/automationv2/actions.go +++ b/integration-tests/actions/automationv2/actions.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/lib/pq" @@ -36,6 +38,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/v2/core/utils" + + ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" ) type NodeDetails struct { @@ -70,6 +74,8 @@ type AutomationTest struct { ChainlinkNodesk8s []*client.ChainlinkK8sClient ChainlinkNodes []*client.ChainlinkClient + DockerEnv *test_env.CLClusterTestEnv + NodeDetails []NodeDetails DefaultP2Pv2Bootstrapper string MercuryCredentialName string @@ -135,6 +141,10 @@ func (a *AutomationTest) SetUpkeepPrivilegeManager(address string) { a.UpkeepPrivilegeManager = common.HexToAddress(address) } +func (a *AutomationTest) SetDockerEnv(env *test_env.CLClusterTestEnv) { + a.DockerEnv = env +} + func (a *AutomationTest) DeployLINK() error { linkToken, err := a.Deployer.DeployLinkTokenContract() if err != nil { @@ -646,6 +656,19 @@ func (a *AutomationTest) AddJobsAndSetConfig(t *testing.T) { l.Info().Str("Registry Address", a.Registry.Address()).Msg("Successfully setConfig on registry") } +func (a *AutomationTest) SetupMercuryMock(t *testing.T, imposters []ctfTestEnv.KillgraveImposter) { + if a.IsOnk8s { + t.Error("mercury mock is not supported on k8s") + } + if a.DockerEnv == nil { + t.Error("docker env is not set") + } + err := a.DockerEnv.MockAdapter.AddImposter(imposters) + if err != nil { + require.NoError(t, err, "Error adding mock imposter") + } +} + func (a *AutomationTest) SetupAutomationDeployment(t *testing.T) { l := logging.GetTestLogger(t) err := a.CollectNodeDetails() diff --git a/integration-tests/load/automationv2_1/automationv2_1_test.go b/integration-tests/load/automationv2_1/automationv2_1_test.go index 1f39d5e4b0e..5fde9befba5 100644 --- a/integration-tests/load/automationv2_1/automationv2_1_test.go +++ b/integration-tests/load/automationv2_1/automationv2_1_test.go @@ -2,8 +2,8 @@ package automationv2_1 import ( "context" - "encoding/json" "fmt" + "math" "math/big" "os" "strconv" @@ -31,12 +31,12 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" - registrar21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_registrar_wrapper2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/log_emitter" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/simple_log_upkeep_counter_wrapper" "github.com/smartcontractkit/chainlink/integration-tests/actions" + "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" contractseth "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" @@ -284,31 +284,14 @@ func TestLogTrigger(t *testing.T) { chainClient.ParallelTransactions(true) - linkToken, err := contractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Error deploying link token contract") - - err = chainClient.WaitForEvents() - require.NoError(t, err, "Failed waiting for contracts to deploy") - - registry, registrar := actions.DeployAutoOCRRegistryAndRegistrar( - t, contractseth.RegistryVersion_2_1, *registrySettings, linkToken, contractDeployer, chainClient, - ) - - err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(100), 0) - require.NoError(t, err, "Error funding chainlink nodes") - - actions.CreateOCRKeeperJobs( - t, - chainlinkNodes, - registry.Address(), - chainClient.GetChainID().Int64(), - 0, - contractseth.RegistryVersion_2_1, - ) - - S, oracleIdentities, err := actions.GetOracleIdentities(chainlinkNodes) - require.NoError(t, err, "Error getting oracle identities") - offC, err := json.Marshal(ocr2keepers30config.OffchainConfig{ + a := automationv2.NewAutomationTestK8s(chainClient, contractDeployer, chainlinkNodes) + a.RegistrySettings = *registrySettings + a.RegistrarSettings = contracts.KeeperRegistrarSettings{ + AutoApproveConfigType: uint8(2), + AutoApproveMaxAllowed: math.MaxUint16, + MinLinkJuels: big.NewInt(0), + } + a.PluginConfig = ocr2keepers30config.OffchainConfig{ TargetProbability: "0.999", TargetInRounds: 1, PerformLockoutWindow: 80_000, // Copied from arbitrum mainnet prod value @@ -316,64 +299,33 @@ func TestLogTrigger(t *testing.T) { GasOverheadPerUpkeep: 300_000, MinConfirmations: 0, MaxUpkeepBatchSize: 10, - }) - require.NoError(t, err, "Error marshalling offchain config") - - signerOnchainPublicKeys, transmitterAccounts, f, _, offchainConfigVersion, offchainConfig, err := ocr3.ContractSetConfigArgsForTests( - 10*time.Second, // deltaProgress time.Duration, - 15*time.Second, // deltaResend time.Duration, - 500*time.Millisecond, // deltaInitial time.Duration, - 1000*time.Millisecond, // deltaRound time.Duration, - 200*time.Millisecond, // deltaGrace time.Duration, - 300*time.Millisecond, // deltaCertifiedCommitRequest time.Duration - 15*time.Second, // deltaStage time.Duration, - 24, // rMax uint64, - S, // s []int, - oracleIdentities, // oracles []OracleIdentityExtra, - offC, // reportingPluginConfig []byte, - 20*time.Millisecond, // maxDurationQuery time.Duration, - 20*time.Millisecond, // maxDurationObservation time.Duration, // good to here - 1200*time.Millisecond, // maxDurationShouldAcceptAttestedReport time.Duration, - 20*time.Millisecond, // maxDurationShouldTransmitAcceptedReport time.Duration, - 1, // f int, - nil, // onchainConfig []byte, - ) - require.NoError(t, err, "Error setting OCR config vars") - - var signers []common.Address - for _, signer := range signerOnchainPublicKeys { - require.Equal(t, 20, len(signer), "OnChainPublicKey '%v' has wrong length for address", signer) - signers = append(signers, common.BytesToAddress(signer)) } - - var transmitters []common.Address - for _, transmitter := range transmitterAccounts { - require.True(t, common.IsHexAddress(string(transmitter)), "TransmitAccount '%s' is not a valid Ethereum address", string(transmitter)) - transmitters = append(transmitters, common.HexToAddress(string(transmitter))) + a.PublicConfig = ocr3.PublicConfig{ + DeltaProgress: 10 * time.Second, + DeltaResend: 15 * time.Second, + DeltaInitial: 500 * time.Millisecond, + DeltaRound: 1000 * time.Millisecond, + DeltaGrace: 200 * time.Millisecond, + DeltaCertifiedCommitRequest: 300 * time.Millisecond, + DeltaStage: 15 * time.Second, + RMax: 24, + MaxDurationQuery: 20 * time.Millisecond, + MaxDurationObservation: 20 * time.Millisecond, + MaxDurationShouldAcceptAttestedReport: 1200 * time.Millisecond, + MaxDurationShouldTransmitAcceptedReport: 20 * time.Millisecond, + F: 1, } - onchainConfig, err := registrySettings.EncodeOnChainConfig(registrar.Address(), common.HexToAddress(chainClient.GetDefaultWallet().Address())) - require.NoError(t, err, "Error encoding onchain config") - l.Info().Msg("Done building OCR config") - ocrConfig := contracts.OCRv2Config{ - Signers: signers, - Transmitters: transmitters, - F: f, - OnchainConfig: onchainConfig, - OffchainConfigVersion: offchainConfigVersion, - OffchainConfig: offchainConfig, - } + a.SetupAutomationDeployment(t) - err = registry.SetConfig(*registrySettings, ocrConfig) - require.NoError(t, err, "Error setting registry config") + err = actions.FundChainlinkNodesAddress(chainlinkNodes[1:], chainClient, big.NewFloat(100), 0) + require.NoError(t, err, "Error funding chainlink nodes") consumerContracts := make([]contracts.KeeperConsumer, 0) triggerContracts := make([]contracts.LogEmitter, 0) utilsABI, err := automation_utils_2_1.AutomationUtilsMetaData.GetAbi() require.NoError(t, err, "Error getting automation utils abi") - registrarABI, err := registrar21.AutomationRegistrarMetaData.GetAbi() - require.NoError(t, err, "Error getting automation registrar abi") emitterABI, err := log_emitter.LogEmitterMetaData.GetAbi() require.NoError(t, err, "Error getting log emitter abi") consumerABI, err := simple_log_upkeep_counter_wrapper.SimpleLogUpkeepCounterMetaData.GetAbi() @@ -382,8 +334,8 @@ func TestLogTrigger(t *testing.T) { var bytes0 = [32]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, } - registrationTxHashes := make([]common.Hash, 0) - upkeepIds := make([]*big.Int, 0) + + upkeepConfigs := make([]automationv2.UpkeepConfig, 0) for i := 0; i < numberOfUpkeeps; i++ { consumerContract, err := contractDeployer.DeployAutomationSimpleLogTriggerConsumer() @@ -427,49 +379,32 @@ func TestLogTrigger(t *testing.T) { require.NoError(t, err, "Error encoding log trigger config") l.Debug().Bytes("Encoded Log Trigger Config", encodedLogTriggerConfig).Msg("Encoded Log Trigger Config") - registrationRequest, err := registrarABI.Pack( - "register", - fmt.Sprintf("LogTriggerUpkeep-%d", i), - []byte("test@mail.com"), - common.HexToAddress(consumerContract.Address()), - automationDefaultUpkeepGasLimit, - common.HexToAddress(chainClient.GetDefaultWallet().Address()), - uint8(1), - []byte("0"), - encodedLogTriggerConfig, - []byte("0"), - automationDefaultLinkFunds, - common.HexToAddress(chainClient.GetDefaultWallet().Address()), - ) - require.NoError(t, err, "Error encoding upkeep registration request") - tx, err := linkToken.TransferAndCall(registrar.Address(), automationDefaultLinkFunds, registrationRequest) - require.NoError(t, err, "Error sending upkeep registration request") - registrationTxHashes = append(registrationTxHashes, tx.Hash()) + upkeepConfig := automationv2.UpkeepConfig{ + UpkeepName: fmt.Sprintf("LogTriggerUpkeep-%d", i), + EncryptedEmail: []byte("test@mail.com"), + UpkeepContract: common.HexToAddress(consumerContract.Address()), + GasLimit: automationDefaultUpkeepGasLimit, + AdminAddress: common.HexToAddress(chainClient.GetDefaultWallet().Address()), + TriggerType: uint8(1), + CheckData: []byte("0"), + TriggerConfig: encodedLogTriggerConfig, + OffchainConfig: []byte("0"), + FundingAmount: automationDefaultLinkFunds, + } + upkeepConfigs = append(upkeepConfigs, upkeepConfig) } + registrationTxHashes, err := a.RegisterUpkeeps(upkeepConfigs) + require.NoError(t, err, "Error registering upkeeps") + err = chainClient.WaitForEvents() - require.NoError(t, err, "Failed waiting for upkeeps to be registered") - - for _, txHash := range registrationTxHashes { - receipt, err := chainClient.GetTxReceipt(txHash) - require.NoError(t, err, "Registration tx should be completed") - var upkeepId *big.Int - for _, rawLog := range receipt.Logs { - parsedUpkeepId, err := registry.ParseUpkeepIdFromRegisteredLog(rawLog) - if err == nil { - upkeepId = parsedUpkeepId - break - } - } - require.NotNil(t, upkeepId, "Upkeep ID should be found after registration") - l.Debug(). - Str("TxHash", txHash.String()). - Str("Upkeep ID", upkeepId.String()). - Msg("Found upkeepId in tx hash") - upkeepIds = append(upkeepIds, upkeepId) - } - l.Info().Msg("Successfully registered all Automation Consumer Contracts") - l.Info().Interface("Upkeep IDs", upkeepIds).Msg("Upkeep IDs") + require.NoError(t, err, "Failed waiting for upkeeps to register") + + upkeepIds, err := a.ConfirmUpkeepsRegistered(registrationTxHashes) + require.NoError(t, err, "Error confirming upkeeps registered") + + l.Info().Msg("Successfully registered all Automation Upkeeps") + l.Info().Interface("Upkeep IDs", upkeepIds).Msg("Upkeeps Registered") l.Info().Str("STARTUP_WAIT_TIME", StartupWaitTime.String()).Msg("Waiting for plugin to start") time.Sleep(StartupWaitTime) @@ -523,7 +458,7 @@ func TestLogTrigger(t *testing.T) { upkeepDelays := make([][]int64, 0) var numberOfEventsEmitted int - var batchSize = 500 + var batchSize uint64 = 500 for _, gen := range p.Generators { numberOfEventsEmitted += len(gen.GetData().OKData.Data) @@ -531,38 +466,47 @@ func TestLogTrigger(t *testing.T) { numberOfEventsEmitted = numberOfEventsEmitted * numberOfEvents l.Info().Int("Number of Events Emitted", numberOfEventsEmitted).Msg("Number of Events Emitted") - if endBlock-startBlock < uint64(batchSize) { - batchSize = int(endBlock - startBlock) + if endBlock-startBlock < batchSize { + batchSize = endBlock - startBlock } - for cIter, consumerContract := range consumerContracts { + for _, consumerContract := range consumerContracts { var ( logs []types.Log address = common.HexToAddress(consumerContract.Address()) timeout = 5 * time.Second ) - for fromBlock := startBlock; fromBlock < endBlock; fromBlock += uint64(batchSize) + 1 { - var ( - filterQuery = geth.FilterQuery{ - Addresses: []common.Address{address}, - FromBlock: big.NewInt(0).SetUint64(fromBlock), - ToBlock: big.NewInt(0).SetUint64(fromBlock + uint64(batchSize)), - Topics: [][]common.Hash{{consumerABI.Events["PerformingUpkeep"].ID}}, + for fromBlock := startBlock; fromBlock < endBlock; fromBlock += batchSize + 1 { + filterQuery := geth.FilterQuery{ + Addresses: []common.Address{address}, + FromBlock: big.NewInt(0).SetUint64(fromBlock), + ToBlock: big.NewInt(0).SetUint64(fromBlock + batchSize), + Topics: [][]common.Hash{{consumerABI.Events["PerformingUpkeep"].ID}}, + } + err = fmt.Errorf("initial error") // to ensure our for loop runs at least once + for err != nil { + var ( + logsInBatch []types.Log + ) + ctx2, cancel := context.WithTimeout(ctx, timeout) + logsInBatch, err = chainClient.FilterLogs(ctx2, filterQuery) + cancel() + if err != nil { + l.Error().Err(err). + Interface("FilterQuery", filterQuery). + Str("Contract Address", consumerContract.Address()). + Str("Timeout", timeout.String()). + Msg("Error getting logs") + timeout = time.Duration(math.Min(float64(timeout)*2, float64(2*time.Minute))) + continue } - ) - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, timeout) - logsInBatch, err := chainClient.FilterLogs(ctx, filterQuery) - cancel() - if err != nil { - l.Error().Err(err). + l.Info(). Interface("FilterQuery", filterQuery). Str("Contract Address", consumerContract.Address()). Str("Timeout", timeout.String()). - Msg("Error getting logs") + Msg("Collected logs") + logs = append(logs, logsInBatch...) } - logs = append(logs, logsInBatch...) - time.Sleep(time.Millisecond * 500) } if len(logs) > 0 { @@ -582,9 +526,6 @@ func TestLogTrigger(t *testing.T) { } upkeepDelays = append(upkeepDelays, delay) } - if (cIter+1)%batchSize == 0 { - time.Sleep(time.Millisecond * 500) - } } l.Info().Interface("Upkeep Delays", upkeepDelays).Msg("Upkeep Delays") @@ -596,6 +537,8 @@ func TestLogTrigger(t *testing.T) { } avg, median, ninetyPct, ninetyNinePct, maximum := testreporters.IntListStats(allUpkeepDelays) + eventsMissed := numberOfEventsEmitted - len(allUpkeepDelays) + percentMissed := float64(eventsMissed) / float64(numberOfEventsEmitted) * 100 l.Info(). Float64("Average", avg).Int64("Median", median). Int64("90th Percentile", ninetyPct).Int64("99th Percentile", ninetyNinePct). @@ -604,13 +547,15 @@ func TestLogTrigger(t *testing.T) { l.Info(). Int("Total Perform Count", len(allUpkeepDelays)). Int("Total Events Emitted", numberOfEventsEmitted). - Int("Total Events Missed", numberOfEventsEmitted-len(allUpkeepDelays)). + Int("Total Events Missed", eventsMissed). + Float64("Percent Missed", percentMissed). Msg("Test completed") testReport := fmt.Sprintf("Upkeep Delays in seconds\nAverage: %f\nMedian: %d\n90th Percentile: %d\n"+ - "99th Percentile: %d\nMax: %d\nTotal Perform Count: %d\n\nTotal Events Emitted: %d\nTotal Events Missed: %d\nTest Duration: %s\n", + "99th Percentile: %d\nMax: %d\nTotal Perform Count: %d\n\nTotal Events Emitted: %d\nTotal Events Missed: %d\n"+ + "Percent Missed: %f\nTest Duration: %s\n", avg, median, ninetyPct, ninetyNinePct, maximum, len(allUpkeepDelays), numberOfEventsEmitted, - numberOfEventsEmitted-len(allUpkeepDelays), testDuration.String()) + eventsMissed, percentMissed, testDuration.String()) err = sendSlackNotification("Finished", l, testEnvironment.Cfg.Namespace, strconv.Itoa(numberofNodes), strconv.FormatInt(startTime.UnixMilli(), 10), strconv.FormatInt(endTime.UnixMilli(), 10), diff --git a/integration-tests/smoke/automation_test.go b/integration-tests/smoke/automation_test.go index 81b484f4e73..f72843e77e8 100644 --- a/integration-tests/smoke/automation_test.go +++ b/integration-tests/smoke/automation_test.go @@ -10,12 +10,20 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/common" + ctfTestEnv "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + + ocr3 "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper" + + ocr2keepers30config "github.com/smartcontractkit/chainlink-automation/pkg/v3/config" + + "github.com/smartcontractkit/chainlink/integration-tests/actions/automationv2" + "github.com/kelseyhightower/envconfig" + + "github.com/ethereum/go-ethereum/common" "github.com/onsi/gomega" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/networks" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" @@ -27,7 +35,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/integration-tests/actions" - "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/contracts/ethereum" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" @@ -37,28 +44,13 @@ import ( var utilsABI = cltypes.MustGetABI(automation_utils_2_1.AutomationUtilsABI) const ( - automationDefaultUpkeepGasLimit = uint32(2500000) - automationDefaultLinkFunds = int64(9e18) - automationDefaultUpkeepsToDeploy = 10 - automationExpectedData = "abcdef" - defaultAmountOfUpkeeps = 2 + automationDefaultUpkeepGasLimit = uint32(2500000) + automationDefaultLinkFunds = int64(9e18) + automationExpectedData = "abcdef" + defaultAmountOfUpkeeps = 2 ) var ( - defaultOCRRegistryConfig = contracts.KeeperRegistrySettings{ - PaymentPremiumPPB: uint32(200000000), - FlatFeeMicroLINK: uint32(0), - BlockCountPerTurn: big.NewInt(10), - CheckGasLimit: uint32(2500000), - StalenessSeconds: big.NewInt(90000), - GasCeilingMultiplier: uint16(1), - MinUpkeepSpend: big.NewInt(0), - MaxPerformGas: uint32(5000000), - FallbackGasPrice: big.NewInt(2e11), - FallbackLinkPrice: big.NewInt(2e18), - MaxCheckDataSize: uint32(5000), - MaxPerformDataSize: uint32(5000), - } automationDefaultRegistryConfig = contracts.KeeperRegistrySettings{ PaymentPremiumPPB: uint32(200000000), FlatFeeMicroLINK: uint32(0), @@ -124,17 +116,17 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { isMercuryV03 := name == "registry_2_1_with_mercury_v03" isMercury := isMercuryV02 || isMercuryV03 - chainClient, _, contractDeployer, linkToken, registry, registrar, testEnv := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, isMercuryV02, isMercuryV03, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, isMercuryV02, isMercuryV03, ) consumers, upkeepIDs := actions.DeployConsumers( t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, @@ -155,7 +147,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { privilegeConfigBytes, _ := json.Marshal(streams.UpkeepPrivilegeConfig{ MercuryEnabled: true, }) - if err := registry.SetUpkeepPrivilegeConfig(upkeepIDs[i], privilegeConfigBytes); err != nil { + if err := a.Registry.SetUpkeepPrivilegeConfig(upkeepIDs[i], privilegeConfigBytes); err != nil { l.Error().Msg("Error when setting upkeep privilege config") return } @@ -184,7 +176,7 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { expect := 5 // Upgrade the nodes one at a time and check that the upkeeps are still being performed for i := 0; i < 5; i++ { - err = actions.UpgradeChainlinkNodeVersionsLocal(upgradeImage, upgradeVersion, testEnv.ClCluster.Nodes[i]) + err = actions.UpgradeChainlinkNodeVersionsLocal(upgradeImage, upgradeVersion, a.DockerEnv.ClCluster.Nodes[i]) require.NoError(t, err, "Error when upgrading node %d", i) time.Sleep(time.Second * 10) expect = expect + 5 @@ -203,11 +195,11 @@ func SetupAutomationBasic(t *testing.T, nodeUpgrade bool) { // Cancel all the registered upkeeps via the registry for i := 0; i < len(upkeepIDs); i++ { - err := registry.CancelUpkeep(upkeepIDs[i]) + err := a.Registry.CancelUpkeep(upkeepIDs[i]) require.NoError(t, err, "Could not cancel upkeep at index %d", i) } - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error encountered when waiting for upkeeps to be cancelled") var countersAfterCancellation = make([]*big.Int, len(upkeepIDs)) @@ -238,17 +230,17 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, ethereum.RegistryVersion_2_1, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, ethereum.RegistryVersion_2_1, automationDefaultRegistryConfig, false, false, ) consumers, upkeepIDs := actions.DeployConsumers( t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, @@ -313,11 +305,11 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { return } - err = registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) + err = a.Registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) } - err := chainClient.WaitForEvents() + err := a.ChainClient.WaitForEvents() require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") var countersAfterSetNoMatch = make([]*big.Int, len(upkeepIDs)) @@ -361,11 +353,11 @@ func TestSetUpkeepTriggerConfig(t *testing.T) { return } - err = registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) + err = a.Registry.SetUpkeepTriggerConfig(upkeepIDs[i], encodedLogTriggerConfig) require.NoError(t, err, "Could not set upkeep trigger config at index %d", i) } - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error encountered when waiting for setting trigger config for upkeeps") var countersAfterSetMatch = make([]*big.Int, len(upkeepIDs)) @@ -413,11 +405,23 @@ func TestAutomationAddFunds(t *testing.T) { registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) - consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(1), automationDefaultUpkeepGasLimit, false, false) + consumers, upkeepIDs := actions.DeployConsumers( + t, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, + defaultAmountOfUpkeeps, + big.NewInt(1), + automationDefaultUpkeepGasLimit, + false, + false, + ) gom := gomega.NewGomegaWithT(t) // Since the upkeep is currently underfunded, check that it doesn't get executed @@ -429,15 +433,15 @@ func TestAutomationAddFunds(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Grant permission to the registry to fund the upkeep - err := linkToken.Approve(registry.Address(), big.NewInt(9e18)) + err := a.LinkToken.Approve(a.Registry.Address(), big.NewInt(9e18)) require.NoError(t, err, "Could not approve permissions for the registry on the link token contract") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") // Add funds to the upkeep whose ID we know from above - err = registry.AddUpkeepFunds(upkeepIDs[0], big.NewInt(9e18)) + err = a.Registry.AddUpkeepFunds(upkeepIDs[0], big.NewInt(9e18)) require.NoError(t, err, "Unable to add upkeep") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for events") // Now the new upkeep should be performing because we added enough funds @@ -464,11 +468,23 @@ func TestAutomationPauseUnPause(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) - consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) + consumers, upkeepIDs := actions.DeployConsumers( + t, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, + defaultAmountOfUpkeeps, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + false, + false, + ) gom := gomega.NewGomegaWithT(t) gom.Eventually(func(g gomega.Gomega) { @@ -484,11 +500,11 @@ func TestAutomationPauseUnPause(t *testing.T) { // pause all the registered upkeeps via the registry for i := 0; i < len(upkeepIDs); i++ { - err := registry.PauseUpkeep(upkeepIDs[i]) + err := a.Registry.PauseUpkeep(upkeepIDs[i]) require.NoError(t, err, "Could not pause upkeep at index %d", i) } - err := chainClient.WaitForEvents() + err := a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for upkeeps to be paused") var countersAfterPause = make([]*big.Int, len(upkeepIDs)) @@ -513,11 +529,11 @@ func TestAutomationPauseUnPause(t *testing.T) { // unpause all the registered upkeeps via the registry for i := 0; i < len(upkeepIDs); i++ { - err := registry.UnpauseUpkeep(upkeepIDs[i]) + err := a.Registry.UnpauseUpkeep(upkeepIDs[i]) require.NoError(t, err, "Could not unpause upkeep at index %d", i) } - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for upkeeps to be unpaused") gom.Eventually(func(g gomega.Gomega) { @@ -547,11 +563,23 @@ func TestAutomationRegisterUpkeep(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) - consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) + consumers, upkeepIDs := actions.DeployConsumers( + t, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, + defaultAmountOfUpkeeps, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + false, + false, + ) var initialCounters = make([]*big.Int, len(upkeepIDs)) gom := gomega.NewGomegaWithT(t) @@ -571,8 +599,8 @@ func TestAutomationRegisterUpkeep(t *testing.T) { } }, "4m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~1m for performing each upkeep once, ~2m buffer - newConsumers, _ := actions.RegisterNewUpkeeps(t, contractDeployer, chainClient, linkToken, - registry, registrar, automationDefaultUpkeepGasLimit, 1) + newConsumers, _ := actions.RegisterNewUpkeeps(t, a.Deployer, a.ChainClient, a.LinkToken, + a.Registry, a.Registrar, automationDefaultUpkeepGasLimit, 1) // We know that newConsumers has size 1, so we can just use the newly registered upkeep. newUpkeep := newConsumers[0] @@ -618,11 +646,23 @@ func TestAutomationPauseRegistry(t *testing.T) { registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) - consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) + consumers, upkeepIDs := actions.DeployConsumers( + t, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, + defaultAmountOfUpkeeps, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + false, + false, + ) gom := gomega.NewGomegaWithT(t) // Observe that the upkeeps which are initially registered are performing @@ -636,9 +676,9 @@ func TestAutomationPauseRegistry(t *testing.T) { }, "4m", "1s").Should(gomega.Succeed()) // ~1m for cluster setup, ~1m for performing each upkeep once, ~2m buffer // Pause the registry - err := registry.Pause() + err := a.Registry.Pause() require.NoError(t, err, "Error pausing registry") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for registry to pause") // Store how many times each upkeep performed once the registry was successfully paused @@ -676,13 +716,25 @@ func TestAutomationKeeperNodesDown(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) - consumers, upkeepIDs := actions.DeployConsumers(t, registry, registrar, linkToken, contractDeployer, chainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, false, false) + consumers, upkeepIDs := actions.DeployConsumers( + t, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, + defaultAmountOfUpkeeps, + big.NewInt(automationDefaultLinkFunds), + automationDefaultUpkeepGasLimit, + false, + false, + ) gom := gomega.NewGomegaWithT(t) - nodesWithoutBootstrap := chainlinkNodes[1:] + nodesWithoutBootstrap := a.ChainlinkNodes[1:] var initialCounters = make([]*big.Int, len(upkeepIDs)) @@ -691,6 +743,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { for i := 0; i < len(upkeepIDs); i++ { counter, err := consumers[i].Counter(testcontext.Get(t)) initialCounters[i] = counter + l.Info().Int64("Upkeeps Performed", counter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(counter.Int64()).Should(gomega.BeNumerically(">", int64(0)), "Expected consumer counter to be greater than 0, but got %d", counter.Int64()) @@ -700,7 +753,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { // Take down 1 node. Currently, using 4 nodes so f=1 and is the max nodes that can go down. err := nodesWithoutBootstrap[0].MustDeleteJob("1") require.NoError(t, err, "Error deleting job from Chainlink node") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for blockchain events") l.Info().Msg("Successfully managed to take down the first half of the nodes") @@ -709,6 +762,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { gom.Eventually(func(g gomega.Gomega) { for i := 0; i < len(upkeepIDs); i++ { currentCounter, err := consumers[i].Counter(testcontext.Get(t)) + l.Info().Int64("Upkeeps Performed", currentCounter.Int64()).Int("Upkeep Index", i).Msg("Number of upkeeps performed") g.Expect(err).ShouldNot(gomega.HaveOccurred(), "Failed to retrieve consumer counter for upkeep at index %d", i) g.Expect(currentCounter.Int64()).Should(gomega.BeNumerically(">", initialCounters[i].Int64()), "Expected counter to have increased from initial value of %s, but got %s", @@ -721,7 +775,7 @@ func TestAutomationKeeperNodesDown(t *testing.T) { for _, nodeToTakeDown := range restOfNodesDown { err = nodeToTakeDown.MustDeleteJob("1") require.NoError(t, err, "Error deleting job from Chainlink node") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for blockchain events") } l.Info().Msg("Successfully managed to take down the second half of the nodes") @@ -761,17 +815,17 @@ func TestAutomationPerformSimulation(t *testing.T) { registryVersion := rv t.Run(name, func(t *testing.T) { t.Parallel() - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) consumersPerformance, _ := actions.DeployPerformanceConsumers( t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, @@ -797,7 +851,7 @@ func TestAutomationPerformSimulation(t *testing.T) { // Set performGas on consumer to be low, so that performUpkeep starts becoming successful err := consumerPerformance.SetPerformGasToBurn(testcontext.Get(t), big.NewInt(100000)) require.NoError(t, err, "Perform gas should be set successfully on consumer") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for set perform gas tx") // Upkeep should now start performing @@ -825,17 +879,17 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, chainlinkNodes, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) consumersPerformance, upkeepIDs := actions.DeployPerformanceConsumers( t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, @@ -846,7 +900,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { ) gom := gomega.NewGomegaWithT(t) - nodesWithoutBootstrap := chainlinkNodes[1:] + nodesWithoutBootstrap := a.ChainlinkNodes[1:] consumerPerformance := consumersPerformance[0] upkeepID := upkeepIDs[0] @@ -861,9 +915,9 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion // Increase gas limit for the upkeep, higher than the performGasBurn - err := registry.SetUpkeepGasLimit(upkeepID, uint32(4500000)) + err := a.Registry.SetUpkeepGasLimit(upkeepID, uint32(4500000)) require.NoError(t, err, "Error setting upkeep gas limit") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetUpkeepGasLimit tx") // Upkeep should now start performing @@ -878,7 +932,7 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { // Now increase the checkGasBurn on consumer, upkeep should stop performing err = consumerPerformance.SetCheckGasToBurn(testcontext.Get(t), big.NewInt(3000000)) require.NoError(t, err, "Check gas burn should be set successfully on consumer") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for SetCheckGasToBurn tx") // Get existing performed count @@ -906,12 +960,12 @@ func TestAutomationCheckPerformGasLimit(t *testing.T) { highCheckGasLimit.CheckGasLimit = uint32(5000000) highCheckGasLimit.RegistryVersion = registryVersion - ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, nodesWithoutBootstrap, highCheckGasLimit, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress()) + ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, nodesWithoutBootstrap, highCheckGasLimit, a.Registrar.Address(), 30*time.Second, a.Registry.RegistryOwnerAddress()) require.NoError(t, err, "Error building OCR config") - err = registry.SetConfig(highCheckGasLimit, ocrConfig) + err = a.Registry.SetConfig(highCheckGasLimit, ocrConfig) require.NoError(t, err, "Registry config should be set successfully!") - err = chainClient.WaitForEvents() + err = a.ChainClient.WaitForEvents() require.NoError(t, err, "Error waiting for set config tx") // Upkeep should start performing again, and it should get regularly performed @@ -939,17 +993,17 @@ func TestUpdateCheckData(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() l := logging.GetTestLogger(t) - chainClient, _, contractDeployer, linkToken, registry, registrar, _ := setupAutomationTestDocker( - t, registryVersion, defaultOCRRegistryConfig, false, false, + a := setupAutomationTestDocker( + t, registryVersion, automationDefaultRegistryConfig, false, false, ) performDataChecker, upkeepIDs := actions.DeployPerformDataCheckerConsumers( t, - registry, - registrar, - linkToken, - contractDeployer, - chainClient, + a.Registry, + a.Registrar, + a.LinkToken, + a.Deployer, + a.ChainClient, defaultAmountOfUpkeeps, big.NewInt(automationDefaultLinkFunds), automationDefaultUpkeepGasLimit, @@ -970,16 +1024,16 @@ func TestUpdateCheckData(t *testing.T) { }, "2m", "1s").Should(gomega.Succeed()) // ~1m for setup, 1m assertion for i := 0; i < len(upkeepIDs); i++ { - err := registry.UpdateCheckData(upkeepIDs[i], []byte(automationExpectedData)) + err := a.Registry.UpdateCheckData(upkeepIDs[i], []byte(automationExpectedData)) require.NoError(t, err, "Could not update check data for upkeep at index %d", i) } - err := chainClient.WaitForEvents() + err := a.ChainClient.WaitForEvents() require.NoError(t, err, "Error while waiting for check data update") // retrieve new check data for all upkeeps for i := 0; i < len(upkeepIDs); i++ { - upkeep, err := registry.GetUpkeepInfo(testcontext.Get(t), upkeepIDs[i]) + upkeep, err := a.Registry.GetUpkeepInfo(testcontext.Get(t), upkeepIDs[i]) require.NoError(t, err, "Failed to get upkeep info at index %d", i) require.Equal(t, []byte(automationExpectedData), upkeep.CheckData, "Upkeep data not as expected") } @@ -1009,15 +1063,7 @@ func setupAutomationTestDocker( registryConfig contracts.KeeperRegistrySettings, isMercuryV02 bool, isMercuryV03 bool, -) ( - blockchain.EVMClient, - []*client.ChainlinkClient, - contracts.ContractDeployer, - contracts.LinkToken, - contracts.KeeperRegistry, - contracts.KeeperRegistrar, - *test_env.CLClusterTestEnv, -) { +) automationv2.AutomationTest { require.False(t, isMercuryV02 && isMercuryV03, "Cannot run test with both Mercury V02 and V03 on") l := logging.GetTestLogger(t) @@ -1080,16 +1126,6 @@ func setupAutomationTestDocker( err = env.FundChainlinkNodes(big.NewFloat(testConfig.ChainlinkNodeFunding)) require.NoError(t, err, "Error funding CL nodes") - if isMercuryV02 { - output := `{"chainlinkBlob":"0x0001c38d71fed6c320b90e84b6f559459814d068e2a1700adc931ca9717d4fe70000000000000000000000000000000000000000000000000000000001a80b52b4bf1233f9cb71144a253a1791b202113c4ab4a92fa1b176d684b4959666ff8200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001004254432d5553442d415242495452554d2d544553544e4554000000000000000000000000000000000000000000000000000000000000000000000000645570be000000000000000000000000000000000000000000000000000002af2b818dc5000000000000000000000000000000000000000000000000000002af2426faf3000000000000000000000000000000000000000000000000000002af32dc209700000000000000000000000000000000000000000000000000000000012130f8df0a9745bb6ad5e2df605e158ba8ad8a33ef8a0acf9851f0f01668a3a3f2b68600000000000000000000000000000000000000000000000000000000012130f60000000000000000000000000000000000000000000000000000000000000002c4a7958dce105089cf5edb68dad7dcfe8618d7784eb397f97d5a5fade78c11a58275aebda478968e545f7e3657aba9dcbe8d44605e4c6fde3e24edd5e22c94270000000000000000000000000000000000000000000000000000000000000002459c12d33986018a8959566d145225f0c4a4e61a9a3f50361ccff397899314f0018162cf10cd89897635a0bb62a822355bd199d09f4abe76e4d05261bb44733d"}` - err = env.MockAdapter.SetStringValuePath("/client", []string{http.MethodGet, http.MethodPost}, map[string]string{"Content-Type": "application/json"}, output) - require.NoError(t, err) - } - if isMercuryV03 { - output := `{"reports":[{"feedID":"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000","validFromTimestamp":0,"observationsTimestamp":0,"fullReport":"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}]}` - err = env.MockAdapter.SetStringValuePath("/api/v1/reports/bulk", []string{http.MethodGet, http.MethodPost}, map[string]string{"Content-Type": "application/json"}, output) - require.NoError(t, err) - } } else { env, err = test_env.NewCLTestEnvBuilder(). WithTestLogger(t). @@ -1105,31 +1141,82 @@ func setupAutomationTestDocker( env.ParallelTransactions(true) nodeClients := env.ClCluster.NodeAPIs() - workerNodes := nodeClients[1:] - linkToken, err := env.ContractDeployer.DeployLinkTokenContract() - require.NoError(t, err, "Error deploying LINK token") + a := automationv2.NewAutomationTestDocker(env.EVMClient, env.ContractDeployer, nodeClients) + a.MercuryCredentialName = "cred1" + a.RegistrySettings = registryConfig + a.RegistrarSettings = contracts.KeeperRegistrarSettings{ + AutoApproveConfigType: uint8(2), + AutoApproveMaxAllowed: 1000, + MinLinkJuels: big.NewInt(0), + } + a.PluginConfig = ocr2keepers30config.OffchainConfig{ + TargetProbability: "0.999", + TargetInRounds: 1, + PerformLockoutWindow: 3_600_000, // Intentionally set to be higher than in prod for testing purpose + GasLimitPerReport: 10_300_000, + GasOverheadPerUpkeep: 300_000, + MinConfirmations: 0, + MaxUpkeepBatchSize: 10, + } + a.PublicConfig = ocr3.PublicConfig{ + DeltaProgress: 10 * time.Second, + DeltaResend: 15 * time.Second, + DeltaInitial: 500 * time.Millisecond, + DeltaRound: 1000 * time.Millisecond, + DeltaGrace: 200 * time.Millisecond, + DeltaCertifiedCommitRequest: 300 * time.Millisecond, + DeltaStage: 30 * time.Second, + RMax: 24, + MaxDurationQuery: 20 * time.Millisecond, + MaxDurationObservation: 20 * time.Millisecond, + MaxDurationShouldAcceptAttestedReport: 1200 * time.Millisecond, + MaxDurationShouldTransmitAcceptedReport: 20 * time.Millisecond, + F: 1, + } - registry, registrar := actions.DeployAutoOCRRegistryAndRegistrar( - t, - registryVersion, - registryConfig, - linkToken, - env.ContractDeployer, - env.EVMClient, - ) + a.SetupAutomationDeployment(t) + a.SetDockerEnv(env) - // Fund the registry with LINK - err = linkToken.Transfer(registry.Address(), big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(int64(defaultAmountOfUpkeeps)))) - require.NoError(t, err, "Funding keeper registry contract shouldn't fail") + if isMercuryV02 || isMercuryV03 { + var imposters []ctfTestEnv.KillgraveImposter + mercuryv03Mock200 := ctfTestEnv.KillgraveImposter{ + Request: ctfTestEnv.KillgraveRequest{ + Method: http.MethodGet, + Endpoint: "/api/v1/reports/bulk", + SchemaFile: nil, + Params: &map[string]string{"feedIDs": "0x00028c915d6af0fd66bba2d0fc9405226bca8d6806333121a7d9832103d1563c", "timestamp": "{[\\d+]}"}, + Headers: nil, + }, + Response: ctfTestEnv.KillgraveResponse{ + Status: 200, + Body: `{"reports":[{"feedID":"0x00028c915d6af0fd66bba2d0fc9405226bca8d6806333121a7d9832103d1563c","validFromTimestamp":0,"observationsTimestamp":0,"fullReport":"0x00066dfcd1ed2d95b18c948dbc5bd64c687afe93e4ca7d663ddec14c20090ad80000000000000000000000000000000000000000000000000000000000081401000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001204554482d5553442d415242495452554d2d544553544e455400000000000000000000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000289ad8d367000000000000000000000000000000000000000000000000000000289acf0b38000000000000000000000000000000000000000000000000000000289b3da40000000000000000000000000000000000000000000000000000000000018ae7ce74d9fa252a8983976eab600dc7590c778d04813430841bc6e765c34cd81a168d00000000000000000000000000000000000000000000000000000000018ae7cb0000000000000000000000000000000000000000000000000000000064891c98000000000000000000000000000000000000000000000000000000000000000260412b94e525ca6cedc9f544fd86f77606d52fe731a5d069dbe836a8bfc0fb8c911963b0ae7a14971f3b4621bffb802ef0605392b9a6c89c7fab1df8633a5ade00000000000000000000000000000000000000000000000000000000000000024500c2f521f83fba5efc2bf3effaaedde43d0a4adff785c1213b712a3aed0d8157642a84324db0cf9695ebd27708d4608eb0337e0dd87b0e43f0fa70c700d911"}]}`, + BodyFile: nil, + Headers: nil, + Delay: nil, + }, + } - err = actions.CreateOCRKeeperJobsLocal(l, nodeClients, registry.Address(), network.ChainID, 0, registryVersion) - require.NoError(t, err, "Error creating OCR Keeper Jobs") - ocrConfig, err := actions.BuildAutoOCR2ConfigVarsLocal(l, workerNodes, registryConfig, registrar.Address(), 30*time.Second, registry.RegistryOwnerAddress()) - require.NoError(t, err, "Error building OCR config vars") - err = registry.SetConfig(automationDefaultRegistryConfig, ocrConfig) - require.NoError(t, err, "Registry config should be set successfully") - require.NoError(t, env.EVMClient.WaitForEvents(), "Waiting for config to be set") + mercuryv02Mock200 := ctfTestEnv.KillgraveImposter{ + Request: ctfTestEnv.KillgraveRequest{ + Method: http.MethodGet, + Endpoint: "/client", + SchemaFile: nil, + Params: &map[string]string{"feedIdHex": "{0x00028c915d6af0fd66bba2d0fc9405226bca8d6806333121a7d9832103d1563c|0x4554482d5553442d415242495452554d2d544553544e45540000000000000000}", "blockNumber": "{[\\d+]}"}, + Headers: nil, + }, + Response: ctfTestEnv.KillgraveResponse{ + Status: 200, + Body: `{"chainlinkBlob":"0x0001c38d71fed6c320b90e84b6f559459814d068e2a1700adc931ca9717d4fe70000000000000000000000000000000000000000000000000000000001a80b52b4bf1233f9cb71144a253a1791b202113c4ab4a92fa1b176d684b4959666ff8200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001004254432d5553442d415242495452554d2d544553544e4554000000000000000000000000000000000000000000000000000000000000000000000000645570be000000000000000000000000000000000000000000000000000002af2b818dc5000000000000000000000000000000000000000000000000000002af2426faf3000000000000000000000000000000000000000000000000000002af32dc209700000000000000000000000000000000000000000000000000000000012130f8df0a9745bb6ad5e2df605e158ba8ad8a33ef8a0acf9851f0f01668a3a3f2b68600000000000000000000000000000000000000000000000000000000012130f60000000000000000000000000000000000000000000000000000000000000002c4a7958dce105089cf5edb68dad7dcfe8618d7784eb397f97d5a5fade78c11a58275aebda478968e545f7e3657aba9dcbe8d44605e4c6fde3e24edd5e22c94270000000000000000000000000000000000000000000000000000000000000002459c12d33986018a8959566d145225f0c4a4e61a9a3f50361ccff397899314f0018162cf10cd89897635a0bb62a822355bd199d09f4abe76e4d05261bb44733d"}`, + BodyFile: nil, + Headers: nil, + Delay: nil, + }, + } + + imposters = append(imposters, mercuryv03Mock200, mercuryv02Mock200) + a.SetupMercuryMock(t, imposters) + } - return env.EVMClient, nodeClients, env.ContractDeployer, linkToken, registry, registrar, env + return *a } From 0c010cd0e6b769d56d0a1664fb4ee4d981d77d42 Mon Sep 17 00:00:00 2001 From: ilija42 <57732589+ilija42@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:39:45 +0100 Subject: [PATCH 245/271] Fix keystore enable to upsert, now restores accidentally deleted keys (#11428) --- core/services/keystore/eth.go | 11 ++++++-- core/services/keystore/eth_test.go | 45 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/core/services/keystore/eth.go b/core/services/keystore/eth.go index a2b207044c0..cea7b6762e0 100644 --- a/core/services/keystore/eth.go +++ b/core/services/keystore/eth.go @@ -221,13 +221,18 @@ func (ks *eth) Enable(address common.Address, chainID *big.Int, qopts ...pg.QOpt func (ks *eth) enable(address common.Address, chainID *big.Int, qopts ...pg.QOpt) error { state := new(ethkey.State) q := ks.q.WithOpts(qopts...) - sql := `UPDATE evm.key_states SET disabled = false, updated_at = NOW() WHERE address = $1 AND evm_chain_id = $2 - RETURNING *;` + sql := `INSERT INTO evm.key_states as key_states ("address", "evm_chain_id", "disabled", "created_at", "updated_at") VALUES ($1, $2, false, NOW(), NOW()) + ON CONFLICT ("address", "evm_chain_id") DO UPDATE SET "disabled" = false, "updated_at" = NOW() WHERE key_states."address" = $1 AND key_states."evm_chain_id" = $2 + RETURNING *;` if err := q.Get(state, sql, address, chainID.String()); err != nil { return errors.Wrap(err, "failed to enable state") } - ks.keyStates.enable(address, chainID, state.UpdatedAt) + if state.CreatedAt.Equal(state.UpdatedAt) { + ks.keyStates.add(state) + } else { + ks.keyStates.enable(address, chainID, state.UpdatedAt) + } ks.notify() return nil } diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index 4165350300f..d9c5cf5cb89 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -541,6 +541,51 @@ func Test_EthKeyStore_SubscribeToKeyChanges(t *testing.T) { assertCountAtLeast(1) } +func Test_EthKeyStore_Enable(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewTestGeneralConfig(t) + keyStore := cltest.NewKeyStore(t, db, cfg.Database()) + ks := keyStore.Eth() + + t.Run("already existing disabled key gets enabled", func(t *testing.T) { + k, _ := cltest.MustInsertRandomKeyNoChains(t, ks) + require.NoError(t, ks.Add(k.Address, testutils.SimulatedChainID)) + require.NoError(t, ks.Disable(k.Address, testutils.SimulatedChainID)) + require.NoError(t, ks.Enable(k.Address, testutils.SimulatedChainID)) + key, err := ks.GetState(k.Address.Hex(), testutils.SimulatedChainID) + require.NoError(t, err) + require.Equal(t, key.Disabled, false) + }) + + t.Run("creates key, deletes it unsafely and then enable creates it again", func(t *testing.T) { + k, _ := cltest.MustInsertRandomKeyNoChains(t, ks) + require.NoError(t, ks.Add(k.Address, testutils.SimulatedChainID)) + _, err := db.Exec("DELETE FROM evm.key_states WHERE address = $1", k.Address) + require.NoError(t, err) + require.NoError(t, ks.Enable(k.Address, testutils.SimulatedChainID)) + key, err := ks.GetState(k.Address.Hex(), testutils.SimulatedChainID) + require.NoError(t, err) + require.Equal(t, key.Disabled, false) + }) + + t.Run("creates key and enables it if it exists in the keystore, but is missing from key states db table", func(t *testing.T) { + k, _ := cltest.MustInsertRandomKeyNoChains(t, ks) + require.NoError(t, ks.Enable(k.Address, testutils.SimulatedChainID)) + key, err := ks.GetState(k.Address.Hex(), testutils.SimulatedChainID) + require.NoError(t, err) + require.Equal(t, key.Disabled, false) + }) + + t.Run("errors if key is not present in keystore", func(t *testing.T) { + addrNotInKs := testutils.NewAddress() + require.Error(t, ks.Enable(addrNotInKs, testutils.SimulatedChainID)) + _, err := ks.GetState(addrNotInKs.Hex(), testutils.SimulatedChainID) + require.Error(t, err) + }) +} + func Test_EthKeyStore_EnsureKeys(t *testing.T) { t.Parallel() From f4a97b260a9673c7a4bbde795dea463aaa66e7bb Mon Sep 17 00:00:00 2001 From: Jim W Date: Fri, 1 Dec 2023 08:44:48 -0500 Subject: [PATCH 246/271] upgrade cosmos gogoproto (#10879) * upgrade cosmos gogoproto pkg * upgrade gogoproto for core scripts * run go mod on integrations * upgrade prometheus to 0.48 * update integrations mod * rm replace * newer grafana/loki; updated kuberesolver replace --------- Co-authored-by: Jordan Krage --- core/scripts/go.mod | 8 +- core/scripts/go.sum | 36 +- go.mod | 8 +- go.sum | 36 +- integration-tests/go.mod | 62 ++- integration-tests/go.sum | 1098 ++++---------------------------------- 6 files changed, 185 insertions(+), 1063 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 197716c2b28..2de09adab56 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -81,7 +81,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/cosmos-sdk v0.47.4 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/gogoproto v1.4.10 // indirect + github.com/cosmos/gogoproto v1.4.11 // indirect github.com/cosmos/iavl v0.20.0 // indirect github.com/cosmos/ibc-go/v7 v7.0.1 // indirect github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect @@ -149,7 +149,7 @@ require ( github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/sessions v1.2.2 // indirect @@ -290,7 +290,7 @@ require ( github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.47.2 // indirect + github.com/prometheus/prometheus v0.48.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rjeczalik/notify v0.9.3 // indirect @@ -359,7 +359,7 @@ require ( go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.15.0 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.18.0 // indirect golang.org/x/sync v0.5.0 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index c6f922432b4..9157e968763 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -158,8 +158,8 @@ github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh 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= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.44.302 h1:ST3ko6GrJKn3Xi+nAvxjG3uk/V1pW8KC52WLeIxqqNk= -github.com/aws/aws-sdk-go v1.44.302/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= @@ -275,8 +275,8 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= -github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= -github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ibc-go/v7 v7.0.1 h1:NIBNRWjlOoFvFQu1ZlgwkaSeHO5avf4C1YQiWegt8jw= @@ -613,11 +613,11 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -625,8 +625,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= +github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -1430,8 +1430,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.47.2 h1:jWcnuQHz1o1Wu3MZ6nMJDuTI0kU5yJp9pkxh8XEkNvI= -github.com/prometheus/prometheus v0.47.2/go.mod h1:J/bmOSjgH7lFxz2gZhrWEZs2i64vMS+HIuZfmYNhJ/M= +github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= +github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= @@ -1812,8 +1812,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1914,8 +1914,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2155,8 +2155,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc= -google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0= +google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= +google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/go.mod b/go.mod index 41255bc4f02..670ec435ea1 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/gin-contrib/size v0.0.0-20230212012657-e14a14094dc4 github.com/gin-gonic/gin v1.9.1 github.com/go-webauthn/webauthn v0.9.1 - github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 github.com/google/uuid v1.4.0 github.com/gorilla/securecookie v1.1.2 github.com/gorilla/sessions v1.2.2 @@ -58,7 +58,7 @@ require ( github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.5.0 github.com/prometheus/common v0.45.0 - github.com/prometheus/prometheus v0.47.2 + github.com/prometheus/prometheus v0.48.0 github.com/robfig/cron/v3 v3.0.1 github.com/rogpeppe/go-internal v1.11.0 github.com/scylladb/go-reflectx v1.0.1 @@ -90,7 +90,7 @@ require ( go.uber.org/multierr v1.11.0 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.15.0 - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/sync v0.5.0 golang.org/x/term v0.14.0 golang.org/x/text v0.14.0 @@ -144,7 +144,7 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/gogoproto v1.4.10 // indirect + github.com/cosmos/gogoproto v1.4.11 // indirect github.com/cosmos/iavl v0.20.0 // indirect github.com/cosmos/ibc-go/v7 v7.0.1 // indirect github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect diff --git a/go.sum b/go.sum index cc3e1303774..74a086d6cf4 100644 --- a/go.sum +++ b/go.sum @@ -157,8 +157,8 @@ github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh 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= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.44.302 h1:ST3ko6GrJKn3Xi+nAvxjG3uk/V1pW8KC52WLeIxqqNk= -github.com/aws/aws-sdk-go v1.44.302/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo= github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= @@ -274,8 +274,8 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= -github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= -github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ibc-go/v7 v7.0.1 h1:NIBNRWjlOoFvFQu1ZlgwkaSeHO5avf4C1YQiWegt8jw= @@ -612,11 +612,11 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -624,8 +624,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= -github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w= +github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -1435,8 +1435,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.47.2 h1:jWcnuQHz1o1Wu3MZ6nMJDuTI0kU5yJp9pkxh8XEkNvI= -github.com/prometheus/prometheus v0.47.2/go.mod h1:J/bmOSjgH7lFxz2gZhrWEZs2i64vMS+HIuZfmYNhJ/M= +github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= +github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= @@ -1817,8 +1817,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1920,8 +1920,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2162,8 +2162,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc= -google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0= +google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= +google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 220dc86b455..9bcd36470b8 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -59,8 +59,12 @@ require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect github.com/CosmWasm/wasmd v0.40.1 // indirect github.com/CosmWasm/wasmvm v1.2.4 // indirect @@ -75,7 +79,7 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/avast/retry-go/v4 v4.5.1 // indirect - github.com/aws/aws-sdk-go v1.44.302 // indirect + github.com/aws/aws-sdk-go v1.45.25 // indirect github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect github.com/aws/jsii-runtime-go v1.75.0 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect @@ -86,7 +90,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect - github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect @@ -112,7 +116,7 @@ require ( github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect github.com/cosmos/cosmos-sdk v0.47.4 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect - github.com/cosmos/gogoproto v1.4.10 // indirect + github.com/cosmos/gogoproto v1.4.11 // indirect github.com/cosmos/iavl v0.20.0 // indirect github.com/cosmos/ibc-go/v7 v7.0.1 // indirect github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect @@ -129,7 +133,7 @@ require ( github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -197,7 +201,7 @@ require ( github.com/google/go-tpm v0.9.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect + github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -206,9 +210,9 @@ require ( github.com/gorilla/websocket v1.5.1 // indirect github.com/gosimple/slug v1.13.1 // indirect github.com/gosimple/unidecode v1.0.1 // indirect - github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 // indirect - github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 // indirect - github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 // indirect + github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f // indirect + github.com/grafana/loki v1.6.2-0.20231201111602-11ef833ed3e4 // indirect + github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4 // indirect github.com/grafana/pyroscope-go v1.0.4 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.4 // indirect github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect @@ -221,7 +225,7 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect - github.com/hashicorp/consul/api v1.22.0 // indirect + github.com/hashicorp/consul/api v1.25.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect @@ -267,10 +271,11 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/klauspost/compress v1.17.2 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/koron/go-ssdp v0.0.2 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/libp2p/go-addr-util v0.0.2 // indirect @@ -319,10 +324,10 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect @@ -366,17 +371,18 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // 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/prometheus/alertmanager v0.25.1 // indirect + github.com/prometheus/alertmanager v0.26.0 // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/exporter-toolkit v0.10.0 // indirect + github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/prometheus v0.47.2 // indirect + github.com/prometheus/prometheus v0.48.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect @@ -384,7 +390,7 @@ require ( github.com/russross/blackfriday v1.6.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect - github.com/sercand/kuberesolver/v4 v4.0.0 // indirect + github.com/sercand/kuberesolver/v5 v5.1.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/shopspring/decimal v1.3.1 // indirect @@ -396,9 +402,10 @@ require ( github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231117204155-b253a2f56664 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect + github.com/soheilhy/cmux v0.1.5 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -424,8 +431,6 @@ require ( github.com/ugorji/go/codec v1.2.11 // indirect github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 // indirect github.com/valyala/fastjson v1.4.1 // indirect - github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 // indirect - github.com/weaveworks/promrus v1.2.0 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -440,8 +445,10 @@ require ( go.etcd.io/etcd/client/v3 v3.5.7 // indirect go.mongodb.org/mongo-driver v1.12.0 // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/collector/pdata v1.0.0-rcv0016 // indirect + go.opentelemetry.io/collector/semconv v0.87.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect go.opentelemetry.io/otel v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect @@ -455,10 +462,10 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.15.0 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.18.0 // indirect - golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sys v0.14.0 // indirect golang.org/x/term v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -479,14 +486,14 @@ require ( gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.27.3 // indirect + k8s.io/api v0.28.2 // indirect k8s.io/apiextensions-apiserver v0.25.3 // indirect - k8s.io/apimachinery v0.27.3 // indirect + k8s.io/apimachinery v0.28.2 // indirect k8s.io/cli-runtime v0.25.11 // indirect - k8s.io/client-go v0.27.3 // indirect + k8s.io/client-go v0.28.2 // indirect k8s.io/component-base v0.26.2 // indirect k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect k8s.io/kubectl v0.25.11 // indirect k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect nhooyr.io/websocket v1.8.7 // indirect @@ -514,7 +521,4 @@ replace ( // until merged upstream: https://github.com/mwitkow/grpc-proxy/pull/69 github.com/mwitkow/grpc-proxy => github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f - - github.com/prometheus/prometheus => github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 - github.com/sercand/kuberesolver/v4 => github.com/sercand/kuberesolver/v5 v5.1.1 ) diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 134168a8667..17ff5e3030c 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -18,507 +18,35 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= cloud.google.com/go/iam v1.1.4 h1:K6n/GZHFTtEoKT5aUG3l9diPi0VduZNQ1PfdnpkkIFk= cloud.google.com/go/iam v1.1.4/go.mod h1:l/rg8l1AaA+VFMho/HYx2Vv6xinPSLMF8qfhRPIZ0L8= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= contrib.go.opencensus.io/exporter/stackdriver v0.12.6/go.mod h1:8x999/OcIPy5ivx/wDiV7Gx4D+VUPODf0mWRGRc5kSk= contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= contrib.go.opencensus.io/exporter/stackdriver v0.13.5 h1:TNaexHK16gPUoc7uzELKOU7JULqccn1NDuqUxmxSqfo= @@ -543,40 +71,34 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI= github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE= github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= -github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 h1:9kDVnTz3vbfweTqAUmk/a/pH5pWFCHtvRpHYC0G/dcA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 h1:bWh0Z2rOEDfB/ywv/l0iHN1JgyazE6kW/aIA89+CEK0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -598,7 +120,6 @@ github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/K-Phoen/grabana v0.21.17 h1:mO/9DvJWC/qpTF/X5jQDm5eKgCBaCGypP/tEfXAvKfg= github.com/K-Phoen/grabana v0.21.17/go.mod h1:vbASQt9UiQhX4lC3/opLpJMJ8m+hsTUU2FwkQMytHK4= @@ -628,12 +149,7 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= -github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE= github.com/alecthomas/participle/v2 v2.0.0-alpha7 h1:cK4vjj0VSgb3lN1nuKA5F7dw+1s1pWBe5bx7nNCnN+c= github.com/alecthomas/participle/v2 v2.0.0-alpha7/go.mod h1:NumScqsC42o9x+dGj8/YqsIfhrIQjFEOFovxotbBirA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -650,10 +166,7 @@ github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKS github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -670,10 +183,9 @@ github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh 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= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.302 h1:ST3ko6GrJKn3Xi+nAvxjG3uk/V1pW8KC52WLeIxqqNk= -github.com/aws/aws-sdk-go v1.44.302/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/constructs-go/constructs/v10 v10.1.255 h1:5hARfEmhBqHSTQf/C3QLA3sWOxO2Dfja0iA1W7ZcI7g= github.com/aws/constructs-go/constructs/v10 v10.1.255/go.mod h1:DCdBSjN04Ck2pajCacTD4RKFqSA7Utya8d62XreYctI= github.com/aws/jsii-runtime-go v1.75.0 h1:NhpUfyiL7/wsRuUekFsz8FFBCYLfPD/l61kKg9kL/a4= @@ -694,8 +206,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsy github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= @@ -719,8 +229,8 @@ github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6r github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I= -github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= github.com/cdk8s-team/cdk8s-core-go/cdk8s/v2 v2.7.5 h1:rvc39Ol6z3MvaBzXkxFC6Nfsnixq/dRypushKDd7Nc0= @@ -730,8 +240,6 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= @@ -768,16 +276,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= @@ -797,7 +296,6 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= @@ -820,7 +318,6 @@ github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmf github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -835,8 +332,8 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= -github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoKuI= -github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= +github.com/cosmos/gogoproto v1.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= +github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= github.com/cosmos/ibc-go/v7 v7.0.1 h1:NIBNRWjlOoFvFQu1ZlgwkaSeHO5avf4C1YQiWegt8jw= @@ -864,8 +361,6 @@ github.com/cucumber/common/gherkin/go/v22 v22.0.0 h1:4K8NqptbvdOrjL9DEea6HFjSpbd github.com/cucumber/common/gherkin/go/v22 v22.0.0/go.mod h1:3mJT10B2GGn3MvVPd3FwR7m2u4tLhSRhWUqJU4KN4Fg= github.com/cucumber/common/messages/go/v17 v17.1.1 h1:RNqopvIFyLWnKv0LfATh34SWBhXeoFTJnSrgm9cT/Ts= github.com/cucumber/common/messages/go/v17 v17.1.1/go.mod h1:bpGxb57tDE385Rb2EohgUadLkAbhoC4IyCFi89u/JQI= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= @@ -908,12 +403,14 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/godo v1.97.0 h1:p9w1yCcWMZcxFSLPToNGXA96WfUVLXqoHti6GzVomL4= -github.com/digitalocean/godo v1.97.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= +github.com/digitalocean/godo v1.104.1 h1:SZNxjAsskM/su0YW9P8Wx3gU0W1Z13b6tZlYNpl5BnA= +github.com/digitalocean/godo v1.104.1/go.mod h1:VAI/L5YDzMuPRU01lEEUSQ/sp5Z//1HnnFv/RBTEdbg= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -936,16 +433,9 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/esote/minmaxheap v1.0.0 h1:rgA7StnXXpZG6qlM0S7pUmEv1KpWe32rYT4x8J8ntaA= @@ -975,7 +465,6 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= @@ -983,8 +472,6 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+ github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= @@ -1039,11 +526,6 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclK github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -1053,8 +535,6 @@ github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8= github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -1110,8 +590,6 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -1166,13 +644,15 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -1181,13 +661,11 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= -github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= @@ -1197,10 +675,7 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU= github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -1216,7 +691,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -1250,9 +724,10 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -1266,7 +741,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -1286,8 +760,6 @@ github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -1300,53 +772,33 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4= -github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud v1.2.0 h1:1oXyj4g54KBg/kFtCdMM6jtxSzeIyg8wv4z1HoGPp1E= -github.com/gophercloud/gophercloud v1.2.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= @@ -1362,12 +814,12 @@ github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2 h1:IOks+FXJ6iO/pfbaVEf4efNw+YzYBYNCkCabyrbkFTM= -github.com/grafana/dskit v0.0.0-20230201083518-528d8a7d52f2/go.mod h1:zj+5BNZAVmQafV583uLTAOzRr963KPdEm4d6NPmtbwg= -github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6 h1:V5PspEXlSlNh22sMyGkgfSOVVLTsSmhbmsp1VPt8Fdc= -github.com/grafana/loki v1.6.2-0.20231017135925-990ac685e6a6/go.mod h1:+aWr7OBDuZMT+p0rKmLfW5saO2m3YOGBnt++IlgLhVk= -github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765 h1:VXitROTlmZtLzvokNe8ZbUKpmwldM4Hy1zdNRO32jKU= -github.com/grafana/loki/pkg/push v0.0.0-20230127102416-571f88bc5765/go.mod h1:DhJMrd2QInI/1CNtTN43BZuTmkccdizW1jZ+F6aHkhY= +github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4= +github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f/go.mod h1:8dsy5tQOkeNQyjXpm5mQsbCu3H5uzeBD35MzRQFznKU= +github.com/grafana/loki v1.6.2-0.20231201111602-11ef833ed3e4 h1:YbfISHhpbiDvoFNxDPCicPOU/+9s4rdv9Fq/V3iRvTo= +github.com/grafana/loki v1.6.2-0.20231201111602-11ef833ed3e4/go.mod h1:Tx2uhmS+H0pGmtqX94/KaDkRHWhIowt4iYtJLctAbEY= +github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4 h1:wQ0FnSeebhJIBkgYOD06Mxk9HV2KhtEG0hp/7R+5RUQ= +github.com/grafana/loki/pkg/push v0.0.0-20231201111602-11ef833ed3e4/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0= github.com/grafana/pyroscope-go v1.0.4 h1:oyQX0BOkL+iARXzHuCdIF5TQ7/sRSel1YFViMHC7Bm0= github.com/grafana/pyroscope-go v1.0.4/go.mod h1:0d7ftwSMBV/Awm7CCiYmHQEG8Y44Ma3YSjt+nWcWztY= github.com/grafana/pyroscope-go/godeltaprof v0.1.4 h1:mDsJ3ngul7UfrHibGQpV66PbZ3q1T8glz/tK3bQKKEk= @@ -1391,8 +843,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= @@ -1406,13 +856,13 @@ github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIv github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.22.0 h1:ydEvDooB/A0c/xpsBd8GSt7P2/zYPBui4KrNip0xGjE= -github.com/hashicorp/consul/api v1.22.0/go.mod h1:zHpYgZ7TeYqS6zaszjwSt128OwESRpnhU9aGa6ue3Eg= +github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= -github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= -github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -1437,8 +887,8 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -1470,8 +920,8 @@ github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/nomad/api v0.0.0-20230308192510-48e7d70fcd4b h1:EkuSTU8c/63q4LMayj8ilgg/4I5PXDFVcnqKfs9qcwI= -github.com/hashicorp/nomad/api v0.0.0-20230308192510-48e7d70fcd4b/go.mod h1:bKUb1ytds5KwUioHdvdq9jmrDqCThv95si0Ub7iNeBg= +github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c h1:Nc3Mt2BAnq0/VoLEntF/nipX+K1S7pG+RgwiitSv6v0= +github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -1481,8 +931,8 @@ github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7H github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= -github.com/hetznercloud/hcloud-go v1.41.0 h1:KJGFRRc68QiVu4PrEP5BmCQVveCP2CM26UGQUKGpIUs= -github.com/hetznercloud/hcloud-go v1.41.0/go.mod h1:NaHg47L6C77mngZhwBG652dTAztYrsZ2/iITJKhQkHA= +github.com/hetznercloud/hcloud-go/v2 v2.4.0 h1:MqlAE+w125PLvJRCpAJmEwrIxoVdUdOyuFUhE/Ukbok= +github.com/hetznercloud/hcloud-go/v2 v2.4.0/go.mod h1:l7fA5xsncFBzQTyw29/dw5Yr88yEGKKdc6BHf24ONS0= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -1497,7 +947,6 @@ github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -1509,8 +958,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ionos-cloud/sdk-go/v6 v6.1.4 h1:BJHhFA8Q1SZC7VOXqKKr2BV2ysQ2/4hlk1e4hZte7GY= -github.com/ionos-cloud/sdk-go/v6 v6.1.4/go.mod h1:Ox3W0iiEz0GHnfY9e5LmAxwklsxguuNFEUSu0gVRTME= +github.com/ionos-cloud/sdk-go/v6 v6.1.9 h1:Iq3VIXzeEbc8EbButuACgfLMiY5TPVWUPNrF+Vsddo4= +github.com/ionos-cloud/sdk-go/v6 v6.1.9/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= @@ -1655,8 +1104,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= @@ -1666,27 +1113,23 @@ github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYb github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -1930,16 +1373,13 @@ github.com/libp2p/go-yamux/v2 v2.0.0 h1:vSGhAy5u6iHBq11ZDcyHH4Blcf9xlBhT4WQDoOE9 github.com/libp2p/go-yamux/v2 v2.0.0/go.mod h1:NVWira5+sVUIU6tu1JWvaRn1dRnG+cawOJiflsAM+7U= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/linode/linodego v1.14.1 h1:uGxQyy0BidoEpLGdvfi4cPgEW+0YUFsEGrLEhcTfjNc= -github.com/linode/linodego v1.14.1/go.mod h1:NJlzvlNtdMRRkXb0oN6UWzUkj6t+IBsyveHgZ5Ppjyk= +github.com/linode/linodego v1.23.0 h1:s0ReCZtuN9Z1IoUN9w1RLeYO1dMZUGPwOQ/IBFsBHtU= +github.com/linode/linodego v1.23.0/go.mod h1:0U7wj/UQOqBNbKv1FYTXiBUXueR8DY4HvIotwE0ENgg= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -1983,12 +1423,10 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= @@ -2000,15 +1438,13 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= @@ -2016,8 +1452,8 @@ github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+ github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -2177,10 +1613,8 @@ github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYB github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= -github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= -github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -2193,8 +1627,8 @@ github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/ovh/go-ovh v1.3.0 h1:mvZaddk4E4kLcXhzb+cxBsMPYp2pHqiQpWYkInsuZPQ= -github.com/ovh/go-ovh v1.3.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= +github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= +github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -2213,18 +1647,15 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 h1:hDSdbBuw3Lefr6R18ax0tZ2BJeNB3NehB3trOwYBsdU= github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -2235,25 +1666,20 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pressly/goose/v3 v3.16.0 h1:xMJUsZdHLqSnCqESyKSqEfcYVYsUuup1nrOhaEFftQg= github.com/pressly/goose/v3 v3.16.0/go.mod h1:JwdKVnmCRhnF6XLQs2mHEQtucFD49cQBdRM4UiwkxsM= -github.com/prometheus/alertmanager v0.25.1 h1:LGBNMspOfv8h7brb+LWj2wnwBCg2ZuuKWTh6CAVw2/Y= -github.com/prometheus/alertmanager v0.25.1/go.mod h1:MEZ3rFVHqKZsw7IcNS/m4AWZeXThmJhumpiWR4eHU/w= +github.com/prometheus/alertmanager v0.26.0 h1:uOMJWfIwJguc3NaM3appWNbbrh6G/OjvaHMk22aBBYc= +github.com/prometheus/alertmanager v0.26.0/go.mod h1:rVcnARltVjavgVaNnmevxK7kOn7IZavyf0KNgHkbEpU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= @@ -2263,29 +1689,22 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/exporter-toolkit v0.8.2/go.mod h1:00shzmJL7KxcsabLWcONwpyNEuWhREOnFqZW7vadFS0= -github.com/prometheus/exporter-toolkit v0.10.0 h1:yOAzZTi4M22ZzVxD+fhy1URTuNRj/36uQJJ5S8IPza8= -github.com/prometheus/exporter-toolkit v0.10.0/go.mod h1:+sVFzuvV5JDyw+Ih6p3zFxZNVnKQa3x5qPmDSiPu4ZY= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 h1:oHcfzdJnM/SFppy2aUlvomk37GI33x9vgJULihE5Dt8= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.mod h1:LoBCZeRh+5hX+fSULNyFnagYlQG/gBsyA/deNzROkq8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2 h1:i5hmbBzR+VeL5pPl1ZncsJ1bpg3SO66bwkE1msJBsMA= -github.com/prometheus/prometheus v0.43.1-0.20230327151049-211ae4f1f0a2/go.mod h1:Mm42Acga98xgA+u5yTaC3ki3i0rJEJWFpbdHN7q2trk= +github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= +github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw= github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= @@ -2299,7 +1718,6 @@ github.com/regen-network/gocuke v0.6.2 h1:pHviZ0kKAq2U2hN2q3smKNxct6hS0mGByFMHGn github.com/regen-network/gocuke v0.6.2/go.mod h1:zYaqIHZobHyd0xOrHGPQjbhGJsuZ1oElx150u2o1xuk= github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -2315,8 +1733,8 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= -github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -2329,15 +1747,13 @@ github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNl github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.14 h1:yFl3jyaSVLNYXlnNYM5z2pagEk1dYQhfr1p20T1NyKY= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.14/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 h1:yWfiTPwYxB0l5fGMhl/G+liULugVIHD9AU77iNLrURQ= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/scylladb/go-reflectx v1.0.1 h1:b917wZM7189pZdlND9PbIJ6NQxfDPfBvUaQ7cjj1iZQ= github.com/scylladb/go-reflectx v1.0.1/go.mod h1:rWnOfDIRWBGN0miMLIcoPt/Dhi2doCMZqwMCJ3KupFc= @@ -2408,6 +1824,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= @@ -2415,11 +1833,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= @@ -2503,10 +1918,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -2539,10 +1952,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205 h1:gjb7t9LCnRu14LHubyLIgrE+EYlAaREiPn/VknV7R3s= -github.com/weaveworks/common v0.0.0-20230411130259-f7d83a041205/go.mod h1:O9wmSPNVSuqxzUZPFlHnPQ8xnyvx0qBnKGFfGbj95uY= -github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M= -github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= @@ -2564,7 +1973,6 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= @@ -2581,12 +1989,9 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= @@ -2623,13 +2028,16 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0016 h1:qCPXSQCoD3qeWFb1RuIks8fw9Atxpk78bmtVdi15KhE= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0016/go.mod h1:OdN0alYOlYhHXu6BDlGehrZWgtBuiDsz/rlNeJeXiNg= +go.opentelemetry.io/collector/semconv v0.87.0 h1:BsG1jdLLRCBRlvUujk4QA86af7r/ZXnizczQpEs/gg8= +go.opentelemetry.io/collector/semconv v0.87.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= @@ -2643,8 +2051,6 @@ go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6 go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= @@ -2652,7 +2058,6 @@ go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunT go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -2720,43 +2125,26 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -2780,12 +2168,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2833,9 +2217,9 @@ golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -2844,33 +2228,17 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= @@ -2883,28 +2251,10 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= -golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2917,10 +2267,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -2991,11 +2338,8 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -3007,47 +2351,27 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -3060,9 +2384,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= @@ -3078,8 +2399,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= @@ -3088,16 +2407,12 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -3119,7 +2434,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -3157,7 +2471,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -3165,16 +2478,10 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -3183,23 +2490,13 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -3220,44 +2517,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o= -google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw= +google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= +google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -3309,97 +2570,10 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= @@ -3430,29 +2604,10 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/examples v0.0.0-20210424002626-9572fd6faeae/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -3467,9 +2622,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -3537,8 +2689,8 @@ k8s.io/api v0.25.11 h1:4mjYDfE3yp22jrytjH0knwgzjXKkxHX4D01ZCAazvZM= k8s.io/api v0.25.11/go.mod h1:bK4UvD4bthtutNlvensrfBX21PRQ/vs2cIYggHkOOAo= k8s.io/apiextensions-apiserver v0.25.3 h1:bfI4KS31w2f9WM1KLGwnwuVlW3RSRPuIsfNF/3HzR0k= k8s.io/apiextensions-apiserver v0.25.3/go.mod h1:ZJqwpCkxIx9itilmZek7JgfUAM0dnTsA48I4krPqRmo= -k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= -k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= k8s.io/cli-runtime v0.25.11 h1:GE2yNZm1tN+MJtw1SGMOLesLF7Kp7NVAVqRSTbXfu4o= k8s.io/cli-runtime v0.25.11/go.mod h1:r/nEINuHVEpgGhcd2WamU7hD1t/lMnSz8XM44Autltc= k8s.io/client-go v0.25.11 h1:DJQ141UsbNRI6wYSlcYLP5J5BW5Wq7Bgm42Ztq2SW70= @@ -3553,40 +2705,6 @@ k8s.io/kubectl v0.25.11 h1:6bsft5Gan6BCvQ7cJbDRFjTm4Zfq8GuUYpsWAdVngYE= k8s.io/kubectl v0.25.11/go.mod h1:8mIfgkFgT+yJ8/TlmPW1qoRh46H2si9q5nW8id7i9iM= k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= From 60a328e018ae437fd8d04ee92596435b407f5350 Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 1 Dec 2023 19:54:47 +0530 Subject: [PATCH 247/271] Add NOTE for enum duplication and todo to resovle it (#11444) --- .../plugins/ocr2keeper/evmregistry/v21/encoding/interface.go | 4 ++++ .../evmregistry/v21/mercury/upkeep_failure_reasons.go | 2 ++ .../ocr2keeper/evmregistry/v21/mercury/upkeep_states.go | 2 ++ 3 files changed, 8 insertions(+) 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 83ace2492f5..06a3e7b106b 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -11,6 +11,8 @@ import ( ) const ( + // NOTE: This enum should be kept in sync with evmregistry/v21/mercury/upkeep_failure_reasons.go + // TODO (AUTO-7928) Remove this duplication // upkeep failure onchain reasons UpkeepFailureReasonNone uint8 = 0 UpkeepFailureReasonUpkeepCancelled uint8 = 1 @@ -30,6 +32,8 @@ const ( UpkeepFailureReasonSimulationFailed uint8 = 35 UpkeepFailureReasonTxHashReorged uint8 = 36 + // NOTE: This enum should be kept in sync with evmregistry/v21/mercury/upkeep_states.go + // TODO (AUTO-7928) Remove this duplication // pipeline execution error NoPipelineError uint8 = 0 CheckBlockTooOld uint8 = 1 diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go index 261fc33bd46..66f8bca402f 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_failure_reasons.go @@ -2,6 +2,8 @@ package mercury type MercuryUpkeepFailureReason uint8 +// NOTE: This enum should be kept in sync with evmregistry/v21/encoding/interface.go +// TODO (AUTO-7928) Remove this duplication const ( // upkeep failure onchain reasons MercuryUpkeepFailureReasonNone MercuryUpkeepFailureReason = 0 diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go index 4e9cd14aee3..0d8276ff2de 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/upkeep_states.go @@ -2,6 +2,8 @@ package mercury type MercuryUpkeepState uint8 +// NOTE: This enum should be kept in sync with evmregistry/v21/encoding/interface.go +// TODO (AUTO-7928) Remove this duplication const ( NoPipelineError MercuryUpkeepState = 0 RpcFlakyFailure MercuryUpkeepState = 3 From a4b39124bb5015bc72c09e7d12e5f4f22ecfdf78 Mon Sep 17 00:00:00 2001 From: Patrick Date: Fri, 1 Dec 2023 09:29:51 -0500 Subject: [PATCH 248/271] Fix keystore enable to upsert, now restores accidentally deleted keys (#11438) * Fix keystore enable to upsert, now restores accidentally deleted keys * BCF-2828: adding evm.key_states insert for Disable keys evm route as well --------- Co-authored-by: ilija Co-authored-by: ilija42 <57732589+ilija42@users.noreply.github.com> --- core/services/keystore/eth.go | 13 +++++++---- core/services/keystore/eth_test.go | 35 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/core/services/keystore/eth.go b/core/services/keystore/eth.go index cea7b6762e0..0e86cccacae 100644 --- a/core/services/keystore/eth.go +++ b/core/services/keystore/eth.go @@ -250,13 +250,18 @@ func (ks *eth) Disable(address common.Address, chainID *big.Int, qopts ...pg.QOp func (ks *eth) disable(address common.Address, chainID *big.Int, qopts ...pg.QOpt) error { state := new(ethkey.State) q := ks.q.WithOpts(qopts...) - sql := `UPDATE evm.key_states SET disabled = false, updated_at = NOW() WHERE address = $1 AND evm_chain_id = $2 - RETURNING id, address, evm_chain_id, disabled, created_at, updated_at;` + sql := `INSERT INTO evm.key_states as key_states ("address", "evm_chain_id", "disabled", "created_at", "updated_at") VALUES ($1, $2, true, NOW(), NOW()) + ON CONFLICT ("address", "evm_chain_id") DO UPDATE SET "disabled" = true, "updated_at" = NOW() WHERE key_states."address" = $1 AND key_states."evm_chain_id" = $2 + RETURNING *;` if err := q.Get(state, sql, address, chainID.String()); err != nil { - return errors.Wrap(err, "failed to enable state") + return errors.Wrap(err, "failed to disable state") } - ks.keyStates.disable(address, chainID, state.UpdatedAt) + if state.CreatedAt.Equal(state.UpdatedAt) { + ks.keyStates.add(state) + } else { + ks.keyStates.disable(address, chainID, state.UpdatedAt) + } ks.notify() return nil } diff --git a/core/services/keystore/eth_test.go b/core/services/keystore/eth_test.go index d9c5cf5cb89..42d6c575376 100644 --- a/core/services/keystore/eth_test.go +++ b/core/services/keystore/eth_test.go @@ -788,3 +788,38 @@ func Test_EthKeyStore_CheckEnabled(t *testing.T) { require.Contains(t, err.Error(), fmt.Sprintf("eth key with address %s exists but is disabled for chain 1337 (enabled only for chain IDs: 0)", addr2.Hex())) }) } + +func Test_EthKeyStore_Disable(t *testing.T) { + t.Parallel() + + db := pgtest.NewSqlxDB(t) + cfg := configtest.NewTestGeneralConfig(t) + keyStore := cltest.NewKeyStore(t, db, cfg.Database()) + ks := keyStore.Eth() + + t.Run("creates key, deletes it unsafely and then enable creates it again", func(t *testing.T) { + k, _ := cltest.MustInsertRandomKeyNoChains(t, ks) + require.NoError(t, ks.Add(k.Address, testutils.SimulatedChainID)) + _, err := db.Exec("DELETE FROM evm.key_states WHERE address = $1", k.Address) + require.NoError(t, err) + require.NoError(t, ks.Disable(k.Address, testutils.SimulatedChainID)) + key, err := ks.GetState(k.Address.Hex(), testutils.SimulatedChainID) + require.NoError(t, err) + require.Equal(t, key.Disabled, true) + }) + + t.Run("creates key and enables it if it exists in the keystore, but is missing from key states db table", func(t *testing.T) { + k, _ := cltest.MustInsertRandomKeyNoChains(t, ks) + require.NoError(t, ks.Disable(k.Address, testutils.SimulatedChainID)) + key, err := ks.GetState(k.Address.Hex(), testutils.SimulatedChainID) + require.NoError(t, err) + require.Equal(t, key.Disabled, true) + }) + + t.Run("errors if key is not present in keystore", func(t *testing.T) { + addrNotInKs := testutils.NewAddress() + require.Error(t, ks.Disable(addrNotInKs, testutils.SimulatedChainID)) + _, err := ks.GetState(addrNotInKs.Hex(), testutils.SimulatedChainID) + require.Error(t, err) + }) +} From 43ed486d485a0a05242d9c578f9f740ebba8f740 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 1 Dec 2023 08:31:04 -0600 Subject: [PATCH 249/271] bump golang.org/x/... (#10449) --- core/scripts/go.mod | 16 ++++++------ core/scripts/go.sum | 47 ++++++++++++++++----------------- go.mod | 20 ++++++++------ go.sum | 56 +++++++++++++++++++++------------------- integration-tests/go.mod | 20 +++++++------- integration-tests/go.sum | 47 ++++++++++++++++----------------- 6 files changed, 107 insertions(+), 99 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 2de09adab56..c2f94a7edf2 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -357,17 +357,17 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/arch v0.6.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.18.0 // indirect + golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/term v0.14.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.16.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 9157e968763..67752858e4c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -26,8 +26,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= -cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -1761,8 +1761,8 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= -golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= +golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1800,8 +1800,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1812,8 +1812,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1902,8 +1902,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1914,8 +1914,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2024,16 +2024,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2051,8 +2051,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2122,8 +2122,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2131,8 +2131,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2164,8 +2164,9 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/go.mod b/go.mod index 670ec435ea1..a73fa5c5d16 100644 --- a/go.mod +++ b/go.mod @@ -89,13 +89,13 @@ require ( go.dedis.ch/kyber/v3 v3.1.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.15.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/crypto v0.16.0 + golang.org/x/exp v0.0.0-20231127185646-65229373498e golang.org/x/sync v0.5.0 - golang.org/x/term v0.14.0 + golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 - golang.org/x/time v0.3.0 - golang.org/x/tools v0.15.0 + golang.org/x/time v0.5.0 + golang.org/x/tools v0.16.0 gonum.org/v1/gonum v0.14.0 google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 @@ -105,6 +105,7 @@ require ( ) require ( + cloud.google.com/go/compute v1.23.3 // indirect contrib.go.opencensus.io/exporter/stackdriver v0.13.5 // indirect cosmossdk.io/api v0.3.1 // indirect cosmossdk.io/core v0.5.1 // indirect @@ -363,9 +364,12 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/arch v0.6.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect diff --git a/go.sum b/go.sum index 74a086d6cf4..0aa3ca40a0c 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= -cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -624,8 +624,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -1765,8 +1765,8 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= +golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -1805,8 +1805,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1817,8 +1817,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1908,8 +1908,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1920,8 +1920,8 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2030,8 +2030,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2039,8 +2039,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2050,6 +2050,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -2059,8 +2060,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2129,8 +2130,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2138,8 +2139,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2162,8 +2163,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= -google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= +google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2171,8 +2172,9 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 9bcd36470b8..7f524ec4928 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -460,20 +460,20 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/arch v0.6.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/term v0.14.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.16.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect gonum.org/v1/gonum v0.14.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 17ff5e3030c..d4ca22912e7 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -26,8 +26,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= -cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -2086,8 +2086,8 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= -golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= +golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -2129,8 +2129,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -2141,8 +2141,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -2240,8 +2240,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2253,8 +2253,8 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2377,8 +2377,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -2386,8 +2386,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2407,8 +2407,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2482,8 +2482,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2491,8 +2491,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= @@ -2526,8 +2526,9 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= From 97bef085e4bc8db0f8e156f552fb5661ba7697c6 Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 1 Dec 2023 21:23:47 +0530 Subject: [PATCH 250/271] Delete unused custom telemetry files (#11441) --- .../plugins/ocr2keeper/custom_telemetry.go | 159 ------------------ .../ocr2keeper/custom_telemetry_test.go | 17 -- 2 files changed, 176 deletions(-) delete mode 100644 core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go delete mode 100644 core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go deleted file mode 100644 index d250df4afe1..00000000000 --- a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry.go +++ /dev/null @@ -1,159 +0,0 @@ -package ocr2keeper - -import ( - "context" - "encoding/hex" - "time" - - "google.golang.org/protobuf/proto" - - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" - - "github.com/smartcontractkit/chainlink-common/pkg/services" - - "github.com/smartcontractkit/chainlink/v2/core/logger" - evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" - "github.com/smartcontractkit/chainlink/v2/core/services/synchronization/telem" - "github.com/smartcontractkit/chainlink/v2/core/static" - "github.com/smartcontractkit/chainlink/v2/core/utils" -) - -type AutomationCustomTelemetryService struct { - services.StateMachine - monitoringEndpoint commontypes.MonitoringEndpoint - blockSubscriber *evm21.BlockSubscriber - blockSubChanID int - threadCtrl utils.ThreadControl - lggr logger.Logger - configDigest [32]byte - contractConfigTracker types.ContractConfigTracker -} - -// NewAutomationCustomTelemetryService creates a telemetry service for new blocks and node version -func NewAutomationCustomTelemetryService(me commontypes.MonitoringEndpoint, - lggr logger.Logger, blocksub *evm21.BlockSubscriber, configTracker types.ContractConfigTracker) (*AutomationCustomTelemetryService, error) { - return &AutomationCustomTelemetryService{ - monitoringEndpoint: me, - threadCtrl: utils.NewThreadControl(), - lggr: lggr.Named("AutomationCustomTelem"), - contractConfigTracker: configTracker, - blockSubscriber: blocksub, - }, nil -} - -// Start starts Custom Telemetry Service, sends 1 NodeVersion message to endpoint at start and sends new BlockNumber messages -func (e *AutomationCustomTelemetryService) Start(ctx context.Context) error { - return e.StartOnce("AutomationCustomTelemetryService", func() error { - e.lggr.Infof("Starting: Custom Telemetry Service") - _, configDetails, err := e.contractConfigTracker.LatestConfigDetails(ctx) - if err != nil { - e.lggr.Errorf("Error occurred while getting newestConfigDetails for initialization %s", err) - } else { - e.configDigest = configDetails - e.sendNodeVersionMsg() - } - e.threadCtrl.Go(func(ctx context.Context) { - ticker := time.NewTicker(1 * time.Minute) - defer ticker.Stop() - for { - select { - case <-ticker.C: - _, newConfigDigest, err := e.contractConfigTracker.LatestConfigDetails(ctx) - if err != nil { - e.lggr.Errorf("Error occurred while getting newestConfigDetails in configDigest loop %s", err) - } - if newConfigDigest != e.configDigest { - e.configDigest = newConfigDigest - e.sendNodeVersionMsg() - } - case <-ctx.Done(): - return - } - } - }) - - chanID, blockSubscriberChan, blockSubErr := e.blockSubscriber.Subscribe() - if blockSubErr != nil { - e.lggr.Errorf("Block Subscriber Error: Subscribe(): %s", blockSubErr) - - } else { - e.blockSubChanID = chanID - e.threadCtrl.Go(func(ctx context.Context) { - e.lggr.Infof("Started: Sending BlockNumber Messages") - for { - select { - case blockHistory := <-blockSubscriberChan: - latestBlockKey, err := blockHistory.Latest() - if err != nil { - e.lggr.Errorf("BlockSubscriber BlockHistory.Latest() failed: %s", err) - continue - } - e.sendBlockNumberMsg(latestBlockKey) - case <-ctx.Done(): - return - } - } - }) - } - return nil - }) -} - -// Close stops go routines and closes channels -func (e *AutomationCustomTelemetryService) Close() error { - // use utils - return e.StopOnce("AutomationCustomTelemetryService", func() error { - e.lggr.Infof("Stopping: custom telemetry service") - e.threadCtrl.Close() - err := e.blockSubscriber.Unsubscribe(e.blockSubChanID) - if err != nil { - return err - } - e.lggr.Infof("Stopped: Custom telemetry service") - return nil - }) -} - -func (e *AutomationCustomTelemetryService) sendNodeVersionMsg() { - vMsg := &telem.NodeVersion{ - Timestamp: uint64(time.Now().UTC().UnixMilli()), - NodeVersion: static.Version, - ConfigDigest: e.configDigest[:], - } - wrappedVMsg := &telem.AutomationTelemWrapper{ - Msg: &telem.AutomationTelemWrapper_NodeVersion{ - NodeVersion: vMsg, - }, - } - bytes, err := proto.Marshal(wrappedVMsg) - if err != nil { - e.lggr.Errorf("Error occurred while marshalling the Node Version Message %s: %v", wrappedVMsg.String(), err) - } else { - e.monitoringEndpoint.SendLog(bytes) - e.lggr.Infof("NodeVersion Message Sent to Endpoint: %d", vMsg.Timestamp) - } -} - -func (e *AutomationCustomTelemetryService) sendBlockNumberMsg(blockKey ocr2keepers.BlockKey) { - blockNumMsg := &telem.BlockNumber{ - Timestamp: uint64(time.Now().UTC().UnixMilli()), - BlockNumber: uint64(blockKey.Number), - BlockHash: hex.EncodeToString(blockKey.Hash[:]), - ConfigDigest: e.configDigest[:], - } - wrappedBlockNumMsg := &telem.AutomationTelemWrapper{ - Msg: &telem.AutomationTelemWrapper_BlockNumber{ - BlockNumber: blockNumMsg, - }, - } - b, err := proto.Marshal(wrappedBlockNumMsg) - if err != nil { - e.lggr.Errorf("Error occurred while marshalling the Block Num Message %s: %v", wrappedBlockNumMsg.String(), err) - } else { - e.monitoringEndpoint.SendLog(b) - e.lggr.Infof("BlockNumber Message Sent to Endpoint: %d", blockNumMsg.Timestamp) - } -} diff --git a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go b/core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go deleted file mode 100644 index a40a3f3525d..00000000000 --- a/core/services/ocr2/plugins/ocr2keeper/custom_telemetry_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package ocr2keeper - -import ( - "testing" -) - -func TestNewAutomationCustomTelemetryService(t *testing.T) { - // me := &MockMonitoringEndpoint{} - // lggr := &MockLogger{} - // blocksub := &MockBlockSubscriber{} - // configTracker := &MockContractConfigTracker{} - - // service, err := NewAutomationCustomTelemetryService(me, lggr, blocksub, configTracker) - // if err != nil { - // t.Errorf("Expected no error, but got: %v", err) - // } -} From 7aed0973eeb6517b6b22335a49582d99699538a8 Mon Sep 17 00:00:00 2001 From: Akshay Aggarwal Date: Fri, 1 Dec 2023 21:52:28 +0530 Subject: [PATCH 251/271] Small cleanup in mercury v0.3 request code (#11442) --- .../evmregistry/v21/mercury/v03/request.go | 35 +++++++------------ 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go index 7002e2f30ac..cb3c9ef3222 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/v03/request.go @@ -3,7 +3,6 @@ package v03 import ( "context" "encoding/json" - "errors" "fmt" "io" "net/http" @@ -62,12 +61,11 @@ func NewClient(mercuryConfig mercury.MercuryConfigProvider, httpClient mercury.H } func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLookup, pluginRetryKey string) (mercury.MercuryUpkeepState, mercury.MercuryUpkeepFailureReason, [][]byte, bool, time.Duration, error) { - resultLen := len(streamsLookup.Feeds) - ch := make(chan mercury.MercuryData, resultLen) if len(streamsLookup.Feeds) == 0 { return 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", streamsLookup.FeedParamKey, streamsLookup.TimeParamKey, streamsLookup.Feeds) } - resultLen = 1 + resultLen := 1 // Only 1 multi-feed request is made for all feeds + ch := make(chan mercury.MercuryData, resultLen) c.threadCtrl.Go(func(ctx context.Context) { c.multiFeedsRequest(ctx, ch, streamsLookup) }) @@ -75,29 +73,22 @@ func (c *client) DoRequest(ctx context.Context, streamsLookup *mercury.StreamsLo var reqErr error var retryInterval time.Duration results := make([][]byte, len(streamsLookup.Feeds)) - retryable := true - allSuccess := true + retryable := false state := mercury.NoPipelineError - for i := 0; i < resultLen; i++ { - m := <-ch - if m.Error != nil { - reqErr = errors.Join(reqErr, m.Error) - retryable = retryable && m.Retryable - allSuccess = false - if m.State != mercury.NoPipelineError { - state = m.State - } - continue + m := <-ch + if m.Error != nil { + reqErr = m.Error + retryable = m.Retryable + state = m.State + if retryable { + retryInterval = mercury.CalculateRetryConfigFn(pluginRetryKey, c.mercuryConfig) } + } else { results = m.Bytes } - // only retry when not all successful AND none are not retryable - if retryable && !allSuccess { - retryInterval = mercury.CalculateRetryConfigFn(pluginRetryKey, c.mercuryConfig) - } - // only retry when not all successful AND none are not retryable - return state, mercury.MercuryUpkeepFailureReasonNone, results, retryable && !allSuccess, retryInterval, reqErr + + return state, mercury.MercuryUpkeepFailureReasonNone, results, retryable, retryInterval, reqErr } func (c *client) multiFeedsRequest(ctx context.Context, ch chan<- mercury.MercuryData, sl *mercury.StreamsLookup) { From 699088f7ce5801c7d1c8c26d27eb560529563984 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 1 Dec 2023 11:35:51 -0500 Subject: [PATCH 252/271] Adjust price scaling factor from 1e8 => 1e18 (#11447) --- core/scripts/go.mod | 2 +- core/scripts/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- integration-tests/go.mod | 2 +- integration-tests/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index c2f94a7edf2..c7af0541c12 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,7 +304,7 @@ require ( github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 // indirect github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 // indirect github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d // indirect github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 67752858e4c..7cea79eb76e 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1504,8 +1504,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 h1:68YZ874EBGEGwT3lcg6Y+3GZXzyNDeDDiqLHvNMBKYc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 h1:qau0/AHvPwMR3p6gWsFWC4qVfEtSEALtBetTOpHA2IU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= diff --git a/go.mod b/go.mod index a73fa5c5d16..0343f4e7159 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231129183458-faee879168b3 diff --git a/go.sum b/go.sum index 0aa3ca40a0c..4bd90da600d 100644 --- a/go.sum +++ b/go.sum @@ -1507,8 +1507,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 h1:68YZ874EBGEGwT3lcg6Y+3GZXzyNDeDDiqLHvNMBKYc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 h1:qau0/AHvPwMR3p6gWsFWC4qVfEtSEALtBetTOpHA2IU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 7f524ec4928..f95557b2bb1 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.1 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 github.com/smartcontractkit/chainlink-testing-framework v1.20.0 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 diff --git a/integration-tests/go.sum b/integration-tests/go.sum index d4ca22912e7..b275f6b653a 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -1792,8 +1792,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk= github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589 h1:68YZ874EBGEGwT3lcg6Y+3GZXzyNDeDDiqLHvNMBKYc= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231130184744-32b4198d2589/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4 h1:qau0/AHvPwMR3p6gWsFWC4qVfEtSEALtBetTOpHA2IU= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231201152724-d550085eb3c4/go.mod h1:Hrru9i7n+WEYyW2aIt3/YGPhxLX+HEGWnhk3yVXeDF8= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542 h1:oewYJtdRkJKUHCNDCj5C2LQe6Oq6qy975g931nfG0cc= github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231117191236-12eab01a4542/go.mod h1:EpvRoycRD+kniYlz+pCpRT5e+fmPm0mSD/vmND+0oMg= github.com/smartcontractkit/chainlink-feeds v0.0.0-20231127231053-2232d3a6766d h1:w4MsbOtNk6nD/mcXLstHWk9hB6g7QLtcAfhPjhwvOaQ= From abac315dbf1d3e165d4c956b3094b73685fbd384 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 1 Dec 2023 11:31:28 -0600 Subject: [PATCH 253/271] core/services/synchronization: track go routines to block close (#11443) --- .../telemetry_ingress_batch_client.go | 22 ++++++++++--------- .../telemetry_ingress_client.go | 12 ++++++---- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/core/services/synchronization/telemetry_ingress_batch_client.go b/core/services/synchronization/telemetry_ingress_batch_client.go index 5f286f1617b..b8dbb5e5d37 100644 --- a/core/services/synchronization/telemetry_ingress_batch_client.go +++ b/core/services/synchronization/telemetry_ingress_batch_client.go @@ -51,7 +51,7 @@ type telemetryIngressBatchClient struct { lggr logger.Logger wgDone sync.WaitGroup - chDone chan struct{} + chDone services.StopChan telemBufferSize uint telemMaxBatchSize uint @@ -78,7 +78,7 @@ func NewTelemetryIngressBatchClient(url *url.URL, serverPubKeyHex string, ks key globalLogger: lggr, logging: logging, lggr: lggr.Named("TelemetryIngressBatchClient").Named(network).Named(chainID), - chDone: make(chan struct{}), + chDone: make(services.StopChan), workers: make(map[string]*telemetryIngressBatchWorker), useUniConn: useUniconn, } @@ -103,22 +103,24 @@ func (tc *telemetryIngressBatchClient) Start(ctx context.Context) error { // This is used to call RPC methods on the server if tc.telemClient == nil { // only preset for tests if tc.useUniConn { + tc.wgDone.Add(1) go func() { - // Use background context to retry forever to connect - // Blocks until we connect - conn, err := wsrpc.DialUniWithContext(ctx, tc.lggr, tc.url.String(), clientPrivKey, serverPubKey) + defer tc.wgDone.Done() + ctx2, cancel := tc.chDone.NewCtx() + defer cancel() + conn, err := wsrpc.DialUniWithContext(ctx2, tc.lggr, tc.url.String(), clientPrivKey, serverPubKey) if err != nil { - if ctx.Err() != nil { + if ctx2.Err() != nil { tc.lggr.Warnw("gave up connecting to telemetry endpoint", "err", err) } else { tc.lggr.Criticalw("telemetry endpoint dial errored unexpectedly", "err", err) tc.SvcErrBuffer.Append(err) } - } else { - tc.telemClient = telemPb.NewTelemClient(conn) - tc.close = conn.Close - tc.connected.Store(true) + return } + tc.telemClient = telemPb.NewTelemClient(conn) + tc.close = conn.Close + tc.connected.Store(true) }() } else { // Spawns a goroutine that will eventually connect diff --git a/core/services/synchronization/telemetry_ingress_client.go b/core/services/synchronization/telemetry_ingress_client.go index bcad01622ea..75aa3106a8c 100644 --- a/core/services/synchronization/telemetry_ingress_client.go +++ b/core/services/synchronization/telemetry_ingress_client.go @@ -65,14 +65,14 @@ func NewTelemetryIngressClient(url *url.URL, serverPubKeyHex string, ks keystore } // Start connects the wsrpc client to the telemetry ingress server -func (tc *telemetryIngressClient) Start(ctx context.Context) error { +func (tc *telemetryIngressClient) Start(context.Context) error { return tc.StartOnce("TelemetryIngressClient", func() error { privkey, err := tc.getCSAPrivateKey() if err != nil { return err } - tc.connect(ctx, privkey) + tc.connect(privkey) return nil }) @@ -95,14 +95,15 @@ func (tc *telemetryIngressClient) HealthReport() map[string]error { return map[string]error{tc.Name(): tc.Healthy()} } -func (tc *telemetryIngressClient) connect(ctx context.Context, clientPrivKey []byte) { +func (tc *telemetryIngressClient) connect(clientPrivKey []byte) { tc.wgDone.Add(1) go func() { defer tc.wgDone.Done() + ctx, cancel := tc.chDone.NewCtx() + defer cancel() serverPubKey := keys.FromHex(tc.serverPubKeyHex) - conn, err := wsrpc.DialWithContext(ctx, tc.url.String(), wsrpc.WithTransportCreds(clientPrivKey, serverPubKey), wsrpc.WithLogger(tc.lggr)) if err != nil { if ctx.Err() != nil { @@ -111,6 +112,7 @@ func (tc *telemetryIngressClient) connect(ctx context.Context, clientPrivKey []b tc.lggr.Criticalw("telemetry endpoint dial errored unexpectedly", "err", err) tc.SvcErrBuffer.Append(err) } + return } defer conn.Close() @@ -130,7 +132,9 @@ func (tc *telemetryIngressClient) connect(ctx context.Context, clientPrivKey []b } func (tc *telemetryIngressClient) handleTelemetry() { + tc.wgDone.Add(1) go func() { + defer tc.wgDone.Done() ctx, cancel := tc.chDone.NewCtx() defer cancel() for { From eac8ebd2ec1dbdee1b60a4b0814a80b36e0ed7f3 Mon Sep 17 00:00:00 2001 From: Bolek <1416262+bolekk@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:41:12 -0800 Subject: [PATCH 254/271] [Functions] Fix heartbeat handler test (#11449) Response is asynchronous so we need to "await" it. --- .../functions/connector_handler_test.go | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/core/services/functions/connector_handler_test.go b/core/services/functions/connector_handler_test.go index fe1a1baa6fc..409f9cdcc56 100644 --- a/core/services/functions/connector_handler_test.go +++ b/core/services/functions/connector_handler_test.go @@ -7,8 +7,10 @@ import ( "errors" "math/big" "testing" + "time" geth_common "github.com/ethereum/go-ethereum/common" + "github.com/onsi/gomega" "github.com/smartcontractkit/chainlink-common/pkg/assets" "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" @@ -300,15 +302,19 @@ func TestFunctionsConnectorHandler(t *testing.T) { connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Return(nil).Once() handler.HandleGatewayMessage(ctx, "gw1", msg) - // second call to collect the response - allowlist.On("Allow", addr).Return(true).Once() - connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { - respMsg, ok := args[2].(*api.Message) - require.True(t, ok) - require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) - require.Equal(t, functions.RequestStateInternalError, response.Status) - }).Return(nil).Once() - handler.HandleGatewayMessage(ctx, "gw1", msg) + // collect the response - should eventually result in an internal error + gomega.NewGomegaWithT(t).Eventually(func() bool { + returnedState := 0 + allowlist.On("Allow", addr).Return(true).Once() + connector.On("SendToGateway", mock.Anything, "gw1", mock.Anything).Run(func(args mock.Arguments) { + respMsg, ok := args[2].(*api.Message) + require.True(t, ok) + require.NoError(t, json.Unmarshal(respMsg.Body.Payload, &response)) + returnedState = response.Status + }).Return(nil).Once() + handler.HandleGatewayMessage(ctx, "gw1", msg) + return returnedState == functions.RequestStateInternalError + }, testutils.WaitTimeout(t), 50*time.Millisecond).Should(gomega.BeTrue()) }) t.Run("heartbeat sender address doesn't match", func(t *testing.T) { From e14061892a0fd9dc983d752e514e37361da16e04 Mon Sep 17 00:00:00 2001 From: Sam Date: Fri, 1 Dec 2023 12:46:55 -0500 Subject: [PATCH 255/271] mercury cache fixes (#11448) * Properly pass through config * Key cache by feed ID, NOT by req req is a pointer meaning subsequent calls to LatestReport will never hit the cache * Remove logger from config --- core/cmd/shell.go | 1 - core/internal/cltest/cltest.go | 1 - .../relay/evm/mercury/wsrpc/cache/cache.go | 53 +++++++++---------- .../evm/mercury/wsrpc/cache/cache_set.go | 23 +++----- .../evm/mercury/wsrpc/cache/cache_set_test.go | 2 +- .../evm/mercury/wsrpc/cache/cache_test.go | 22 ++++---- .../relay/evm/mercury/wsrpc/client_test.go | 4 +- core/services/relay/evm/mercury/wsrpc/pool.go | 5 +- 8 files changed, 52 insertions(+), 59 deletions(-) diff --git a/core/cmd/shell.go b/core/cmd/shell.go index daf936b36c8..2e382be4ccf 100644 --- a/core/cmd/shell.go +++ b/core/cmd/shell.go @@ -160,7 +160,6 @@ func (n ChainlinkAppFactory) NewApplication(ctx context.Context, cfg chainlink.G loopRegistry := plugins.NewLoopRegistry(appLggr, cfg.Tracing()) mercuryPool := wsrpc.NewPool(appLggr, cache.Config{ - Logger: appLggr, LatestReportTTL: cfg.Mercury().Cache().LatestReportTTL(), MaxStaleAge: cfg.Mercury().Cache().MaxStaleAge(), LatestReportDeadline: cfg.Mercury().Cache().LatestReportDeadline(), diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index fc648486a03..8802d9c4b0d 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -345,7 +345,6 @@ func NewApplicationWithConfig(t testing.TB, cfg chainlink.GeneralConfig, flagsAn loopRegistry := plugins.NewLoopRegistry(lggr, nil) mercuryPool := wsrpc.NewPool(lggr, cache.Config{ - Logger: lggr, LatestReportTTL: cfg.Mercury().Cache().LatestReportTTL(), MaxStaleAge: cfg.Mercury().Cache().MaxStaleAge(), LatestReportDeadline: cfg.Mercury().Cache().LatestReportDeadline(), diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache.go b/core/services/relay/evm/mercury/wsrpc/cache/cache.go index 4962210d58f..cab5654081b 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache.go @@ -63,7 +63,6 @@ type Cache interface { } type Config struct { - Logger logger.Logger // LatestReportTTL controls how "stale" we will allow a price to be e.g. if // set to 1s, a new price will always be fetched if the last result was // from more than 1 second ago. @@ -84,8 +83,8 @@ type Config struct { LatestReportDeadline time.Duration } -func NewCache(client Client, cfg Config) Cache { - return newMemCache(client, cfg) +func NewCache(lggr logger.Logger, client Client, cfg Config) Cache { + return newMemCache(lggr, client, cfg) } type cacheVal struct { @@ -164,9 +163,7 @@ type memCache struct { client Client - latestPriceTTL time.Duration - maxStaleAge time.Duration - latestReportDeadline time.Duration + cfg Config cache sync.Map @@ -174,14 +171,12 @@ type memCache struct { chStop services.StopChan } -func newMemCache(client Client, cfg Config) *memCache { +func newMemCache(lggr logger.Logger, client Client, cfg Config) *memCache { return &memCache{ services.StateMachine{}, - cfg.Logger.Named("MercuryMemCache"), + lggr.Named("MemCache"), client, - cfg.LatestReportTTL, - cfg.MaxStaleAge, - cfg.LatestReportDeadline, + cfg, sync.Map{}, sync.WaitGroup{}, make(chan (struct{})), @@ -197,10 +192,11 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest if req == nil { return nil, errors.New("req must not be nil") } - if m.latestPriceTTL <= 0 { + feedIDHex := mercuryutils.BytesToFeedID(req.FeedId).String() + if m.cfg.LatestReportTTL <= 0 { return m.client.RawClient().LatestReport(ctx, req) } - vi, _ := m.cache.LoadOrStore(req, &cacheVal{ + vi, loaded := m.cache.LoadOrStore(feedIDHex, &cacheVal{ sync.RWMutex{}, false, nil, @@ -210,24 +206,26 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest }) v := vi.(*cacheVal) + m.lggr.Tracew("LatestReport", "feedID", feedIDHex, "loaded", loaded) + // HOT PATH v.RLock() if time.Now().Before(v.expiresAt) { // CACHE HIT - promCacheHitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + promCacheHitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() defer v.RUnlock() return v.val, nil } else if v.fetching { // CACHE WAIT - promCacheWaitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + promCacheWaitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() // if someone else is fetching then wait for the fetch to complete ch := v.fetchCh v.RUnlock() return v.waitForResult(ctx, ch, m.chStop) } // CACHE MISS - promCacheMissCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + promCacheMissCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() // fallthrough to cold path and fetch v.RUnlock() @@ -235,19 +233,19 @@ func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest v.Lock() if time.Now().Before(v.expiresAt) { // CACHE HIT - promCacheHitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + promCacheHitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() defer v.RUnlock() return v.val, nil } else if v.fetching { // CACHE WAIT - promCacheWaitCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + promCacheWaitCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() // if someone else is fetching then wait for the fetch to complete ch := v.fetchCh v.Unlock() return v.waitForResult(ctx, ch, m.chStop) } // CACHE MISS - promCacheMissCount.WithLabelValues(m.client.ServerURL(), mercuryutils.BytesToFeedID(req.FeedId).String()).Inc() + promCacheMissCount.WithLabelValues(m.client.ServerURL(), feedIDHex).Inc() // initiate the fetch and wait for result ch := v.initiateFetch() v.Unlock() @@ -269,7 +267,7 @@ const minBackoffRetryInterval = 50 * time.Millisecond // newBackoff creates a backoff for retrying func (m *memCache) newBackoff() backoff.Backoff { min := minBackoffRetryInterval - max := m.latestPriceTTL / 2 + max := m.cfg.LatestReportTTL / 2 if min > max { // avoid setting a min that is greater than max min = max @@ -293,7 +291,7 @@ func (m *memCache) fetch(req *pb.LatestReportRequest, v *cacheVal) { var val *pb.LatestReportResponse var err error defer func() { - v.completeFetch(val, err, t.Add(m.latestPriceTTL)) + v.completeFetch(val, err, t.Add(m.cfg.LatestReportTTL)) }() for { @@ -301,8 +299,8 @@ func (m *memCache) fetch(req *pb.LatestReportRequest, v *cacheVal) { ctx := memcacheCtx cancel := func() {} - if m.latestReportDeadline > 0 { - ctx, cancel = context.WithTimeoutCause(memcacheCtx, m.latestReportDeadline, errors.New("latest report fetch deadline exceeded")) + if m.cfg.LatestReportDeadline > 0 { + ctx, cancel = context.WithTimeoutCause(memcacheCtx, m.cfg.LatestReportDeadline, errors.New("latest report fetch deadline exceeded")) } // NOTE: must drop down to RawClient here otherwise we enter an @@ -330,6 +328,7 @@ func (m *memCache) fetch(req *pb.LatestReportRequest, v *cacheVal) { func (m *memCache) Start(context.Context) error { return m.StartOnce(m.Name(), func() error { + m.lggr.Debugw("MemCache starting", "config", m.cfg) m.wg.Add(1) go m.runloop() return nil @@ -339,16 +338,16 @@ func (m *memCache) Start(context.Context) error { func (m *memCache) runloop() { defer m.wg.Done() - if m.maxStaleAge == 0 { + if m.cfg.MaxStaleAge == 0 { return } - t := time.NewTicker(utils.WithJitter(m.maxStaleAge)) + t := time.NewTicker(utils.WithJitter(m.cfg.MaxStaleAge)) for { select { case <-t.C: m.cleanup() - t.Reset(utils.WithJitter(m.maxStaleAge)) + t.Reset(utils.WithJitter(m.cfg.MaxStaleAge)) case <-m.chStop: return } @@ -372,7 +371,7 @@ func (m *memCache) cleanup() { // skip cleanup if fetching return true } - if time.Now().After(v.expiresAt.Add(m.maxStaleAge)) { + if time.Now().After(v.expiresAt.Add(m.cfg.MaxStaleAge)) { // garbage collection m.cache.Delete(k) } diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go index f06f1137fb3..3171c8ab643 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "sync" - "time" "golang.org/x/exp/maps" @@ -28,27 +27,26 @@ type cacheSet struct { lggr logger.Logger caches map[string]Cache - latestPriceTTL time.Duration - maxStaleAge time.Duration + cfg Config } -func NewCacheSet(cfg Config) CacheSet { - return newCacheSet(cfg) +func NewCacheSet(lggr logger.Logger, cfg Config) CacheSet { + return newCacheSet(lggr, cfg) } -func newCacheSet(cfg Config) *cacheSet { +func newCacheSet(lggr logger.Logger, cfg Config) *cacheSet { return &cacheSet{ sync.RWMutex{}, services.StateMachine{}, - cfg.Logger.Named("CacheSet"), + lggr.Named("CacheSet"), make(map[string]Cache), - cfg.LatestReportTTL, - cfg.MaxStaleAge, + cfg, } } func (cs *cacheSet) Start(context.Context) error { return cs.StartOnce("CacheSet", func() error { + cs.lggr.Debugw("CacheSet starting", "config", cs.cfg) return nil }) } @@ -93,12 +91,7 @@ func (cs *cacheSet) get(ctx context.Context, client Client) (Fetcher, error) { if exists { return c, nil } - cfg := Config{ - Logger: cs.lggr.With("serverURL", sURL), - LatestReportTTL: cs.latestPriceTTL, - MaxStaleAge: cs.maxStaleAge, - } - c = newMemCache(client, cfg) + c = newMemCache(cs.lggr, client, cs.cfg) if err := c.Start(ctx); err != nil { return nil, err } diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go index 9262fb52d31..4e19c7b56de 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_set_test.go @@ -12,7 +12,7 @@ import ( func Test_CacheSet(t *testing.T) { lggr := logger.TestLogger(t) - cs := newCacheSet(Config{Logger: lggr}) + cs := newCacheSet(lggr, Config{}) ctx := testutils.Context(t) require.NoError(t, cs.Start(ctx)) t.Cleanup(func() { diff --git a/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go b/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go index 71f74b3b3cb..6378577d8d5 100644 --- a/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go +++ b/core/services/relay/evm/mercury/wsrpc/cache/cache_test.go @@ -12,31 +12,33 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" + mercuryutils "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/utils" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury/wsrpc/pb" ) const neverExpireTTL = 1000 * time.Hour // some massive value that will never expire during a test func Test_Cache(t *testing.T) { + lggr := logger.TestLogger(t) client := &mockClient{} - cfg := Config{ - Logger: logger.TestLogger(t), - } + cfg := Config{} ctx := testutils.Context(t) req1 := &pb.LatestReportRequest{FeedId: []byte{1}} req2 := &pb.LatestReportRequest{FeedId: []byte{2}} req3 := &pb.LatestReportRequest{FeedId: []byte{3}} + feedID1Hex := mercuryutils.BytesToFeedID(req1.FeedId).String() + t.Run("errors with nil req", func(t *testing.T) { - c := newMemCache(client, cfg) + c := newMemCache(lggr, client, cfg) _, err := c.LatestReport(ctx, nil) assert.EqualError(t, err, "req must not be nil") }) t.Run("with LatestReportTTL=0 does no caching", func(t *testing.T) { - c := newMemCache(client, cfg) + c := newMemCache(lggr, client, cfg) req := &pb.LatestReportRequest{} for i := 0; i < 5; i++ { @@ -58,7 +60,7 @@ func Test_Cache(t *testing.T) { t.Run("caches repeated calls to LatestReport, keyed by request", func(t *testing.T) { cfg.LatestReportTTL = neverExpireTTL client.err = nil - c := newMemCache(client, cfg) + c := newMemCache(lggr, client, cfg) t.Run("if cache is unstarted, returns error", func(t *testing.T) { // starting the cache is required for state management if we @@ -122,8 +124,8 @@ func Test_Cache(t *testing.T) { }) t.Run("re-queries when a cache item has expired", func(t *testing.T) { - vi, exists := c.cache.Load(req1) - assert.True(t, exists) + vi, exists := c.cache.Load(feedID1Hex) + require.True(t, exists) v := vi.(*cacheVal) v.expiresAt = time.Now().Add(-1 * time.Second) @@ -167,7 +169,7 @@ func Test_Cache(t *testing.T) { }) t.Run("timeouts", func(t *testing.T) { - c := newMemCache(client, cfg) + c := newMemCache(lggr, client, cfg) // simulate fetch already executing in background v := &cacheVal{ fetching: true, @@ -176,7 +178,7 @@ func Test_Cache(t *testing.T) { err: nil, expiresAt: time.Now().Add(-1 * time.Second), } - c.cache.Store(req1, v) + c.cache.Store(feedID1Hex, v) canceledCtx, cancel := context.WithCancel(testutils.Context(t)) cancel() diff --git a/core/services/relay/evm/mercury/wsrpc/client_test.go b/core/services/relay/evm/mercury/wsrpc/client_test.go index d5d1b1b5ee5..9b0100a3cdd 100644 --- a/core/services/relay/evm/mercury/wsrpc/client_test.go +++ b/core/services/relay/evm/mercury/wsrpc/client_test.go @@ -140,7 +140,7 @@ func Test_Client_LatestReport(t *testing.T) { t.Run("with cache disabled", func(t *testing.T) { req := &pb.LatestReportRequest{} - cacheSet := cache.NewCacheSet(cache.Config{LatestReportTTL: 0, Logger: lggr}) + cacheSet := cache.NewCacheSet(lggr, cache.Config{LatestReportTTL: 0}) resp := &pb.LatestReportResponse{} var calls int @@ -178,7 +178,7 @@ func Test_Client_LatestReport(t *testing.T) { t.Run("with caching", func(t *testing.T) { req := &pb.LatestReportRequest{} const neverExpireTTL = 1000 * time.Hour // some massive value that will never expire during a test - cacheSet := cache.NewCacheSet(cache.Config{LatestReportTTL: neverExpireTTL, Logger: lggr}) + cacheSet := cache.NewCacheSet(lggr, cache.Config{LatestReportTTL: neverExpireTTL}) resp := &pb.LatestReportResponse{} var calls int diff --git a/core/services/relay/evm/mercury/wsrpc/pool.go b/core/services/relay/evm/mercury/wsrpc/pool.go index 6c525741ddc..76f9bc808b9 100644 --- a/core/services/relay/evm/mercury/wsrpc/pool.go +++ b/core/services/relay/evm/mercury/wsrpc/pool.go @@ -130,9 +130,10 @@ type pool struct { } func NewPool(lggr logger.Logger, cacheCfg cache.Config) Pool { - p := newPool(lggr.Named("Mercury.WSRPCPool")) + lggr = lggr.Named("Mercury.WSRPCPool") + p := newPool(lggr) p.newClient = NewClient - p.cacheSet = cache.NewCacheSet(cacheCfg) + p.cacheSet = cache.NewCacheSet(lggr, cacheCfg) return p } From bb03a45e452e4fadfd0648c0197c011ff454b7e0 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 1 Dec 2023 13:10:39 -0500 Subject: [PATCH 256/271] Prep for CRIB (#11418) * Make CICD work with new ECRs (#495) * Fix ECR and publish on PRs (#496) * Remove test file * Add mockserver to helm chart dependency * Add default values file * Add security contexts * Fix ref to values * Use head SHA instead of commit SHA for image tagging * Use emptyDir mount for postgres to resolve perm issues * Fix volume mounts on postgres * Fix /tmp writes and mount /tmp emptyDir * Remove custom uid * Run as same uid as postgres * Test DOCKER_METADATA_PR_HEAD_SHA disable for root image * Upgrade docker metadata action * Do a full clone * Avoid emptyDir mounts and update gid * Use head SHA when on a PR as default input * Downgrade metata action to latest on prev major * Override sha tag with HEAD value * Remove old sha tag default * Attempt to fix sha tag * Fix syntax error * Remove DOCKER_METADATA_PR_HEAD_SHA env * Refactor shared var output * Revert "Refactor shared var output" This reverts commit 2362fe6473974d4723cf5294cccec7090ab07a0f. * Revert "Remove DOCKER_METADATA_PR_HEAD_SHA env" This reverts commit a7bd01e544ad97603147ccb33576e8006056e930. * Try to make things work again * Set the host to localhost for K8s * Add user to pg_isready * Make securityContext container specific * Create init container to create /clroot * Fix volumes * Add security context to init container * Skip the chown * Remove init container * Breakout DB into its own deployment and service * Fix typo * Update localhost to db service dns * Fix path to script * Change geth /root path to /app * Create emptyDir volume for geth devchain dir * Remove full clone * Add correct inputs to mockserver * merge with develop * cleanup & verify * Add fixes for /chainlink * Remove unused env var * Set back to hardcoded repo name * Reset values back to pre rebase changes * Remove mockserver templates in favor of subchart * fix connect.toml and rename mockserver connection * Backout triggering on PR * Create new build-publish workflow for chainlink-untrusted (from PR) * Reset workflow back * Make step name accurate --------- Co-authored-by: skudasov --- .../build-sign-publish-chainlink/action.yml | 12 +- .../workflows/automation-ondemand-tests.yml | 2 +- .github/workflows/build-publish-pr.yml | 44 +++++ charts/chainlink-cluster/Chart.yaml | 7 +- charts/chainlink-cluster/README.md | 6 +- charts/chainlink-cluster/connect.toml | 2 +- .../chainlink-cluster/dashboard/dashboard.go | 42 ---- charts/chainlink-cluster/devspace.yaml | 180 ++++++++++++++++-- ...ment.yaml => chainlink-db-deployment.yaml} | 72 ++----- .../templates/chainlink-db-service.yaml | 16 ++ .../templates/chainlink-node-deployment.yaml | 97 ++++++++++ ...rvice.yaml => chainlink-node-service.yaml} | 0 .../templates/geth-config-map.yaml | 6 +- .../templates/geth-deployment.yaml | 24 ++- .../templates/mockserver-service.yaml | 14 -- .../templates/mockserver.yaml | 61 ------ .../templates/runner-deployment.yaml | 4 + charts/chainlink-cluster/values-raw-helm.yaml | 134 ------------- charts/chainlink-cluster/values.yaml | 178 +++++++++++++++++ core/chainlink.devspace.Dockerfile | 2 +- 20 files changed, 554 insertions(+), 349 deletions(-) create mode 100644 .github/workflows/build-publish-pr.yml rename charts/chainlink-cluster/templates/{chainlink-deployment.yaml => chainlink-db-deployment.yaml} (60%) create mode 100644 charts/chainlink-cluster/templates/chainlink-db-service.yaml create mode 100644 charts/chainlink-cluster/templates/chainlink-node-deployment.yaml rename charts/chainlink-cluster/templates/{chainlink-service.yaml => chainlink-node-service.yaml} (100%) delete mode 100644 charts/chainlink-cluster/templates/mockserver-service.yaml delete mode 100755 charts/chainlink-cluster/templates/mockserver.yaml delete mode 100644 charts/chainlink-cluster/values-raw-helm.yaml create mode 100644 charts/chainlink-cluster/values.yaml diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml index 62add53092a..8c79f651afd 100644 --- a/.github/actions/build-sign-publish-chainlink/action.yml +++ b/.github/actions/build-sign-publish-chainlink/action.yml @@ -34,7 +34,7 @@ inputs: required: false git-commit-sha: description: Git commit SHA used as metadata when building the application (appears in logs) - default: ${{ github.sha }} + default: ${{ github.event.pull_request.head.sha || github.sha }} required: false aws-role-to-assume: description: The AWS role to assume as the CD user, if any. Used in configuring the docker/login-action @@ -73,7 +73,7 @@ runs: using: composite steps: - name: Set shared variables - shell: sh + shell: bash # See https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#multiline-strings run: | SHARED_IMAGES=${{ inputs.ecr-hostname }}/${{ inputs.ecr-image-name }} @@ -122,7 +122,9 @@ runs: - name: Generate docker metadata for root image id: meta-root - uses: docker/metadata-action@c4ee3adeed93b1fa6a762f209fb01608c1a22f1e # v4.4.4 + uses: docker/metadata-action@2c0bd771b40637d97bf205cbccdd294a32112176 # v4.5.0 + env: + DOCKER_METADATA_PR_HEAD_SHA: "true" with: # list of Docker images to use as base name for tags images: ${{ env.shared-images }} @@ -164,7 +166,9 @@ runs: - name: Generate docker metadata for non-root image id: meta-nonroot - uses: docker/metadata-action@c4ee3adeed93b1fa6a762f209fb01608c1a22f1e # v4.4.4 + uses: docker/metadata-action@2c0bd771b40637d97bf205cbccdd294a32112176 # v4.5.0 + env: + DOCKER_METADATA_PR_HEAD_SHA: "true" with: flavor: | latest=auto diff --git a/.github/workflows/automation-ondemand-tests.yml b/.github/workflows/automation-ondemand-tests.yml index 5cd2182ff62..016a10252be 100644 --- a/.github/workflows/automation-ondemand-tests.yml +++ b/.github/workflows/automation-ondemand-tests.yml @@ -115,7 +115,7 @@ jobs: pull-requests: write id-token: write contents: read - needs: [ build-chainlink, build-test-image ] + needs: [build-chainlink, build-test-image] env: CHAINLINK_COMMIT_SHA: ${{ github.sha }} CHAINLINK_ENV_USER: ${{ github.actor }} diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml new file mode 100644 index 00000000000..b958295cf24 --- /dev/null +++ b/.github/workflows/build-publish-pr.yml @@ -0,0 +1,44 @@ +name: "Build and Publish from PR" + +## +# This workflow builds and publishes a Docker image for Chainlink from a PR. +# It doesn't use an environment, has its own special IAM role, does not sign +# the image, and publishes to a special ECR repo. +## + +on: + pull_request: + +jobs: + build-publish-untrusted: + if: ${{ ! startsWith(github.ref_name, 'release/') }} + runs-on: ubuntu-20.04 + permissions: + id-token: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Build and publish chainlink image + uses: ./.github/actions/build-sign-publish-chainlink + with: + publish: true + aws-role-to-assume: ${{ secrets.AWS_OIDC_IAM_ROLE_PUBLISH_PR_ARN }} + aws-role-duration-seconds: ${{ secrets.AWS_ROLE_DURATION_SECONDS_DEFAULT }} + aws-region: ${{ secrets.AWS_REGION }} + sign-images: false + ecr-hostname: ${{ secrets.AWS_SDLC_ECR_HOSTNAME }} + ecr-image-name: chainlink-untrusted + dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} + + - 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: build-publish-untrusted + continue-on-error: true diff --git a/charts/chainlink-cluster/Chart.yaml b/charts/chainlink-cluster/Chart.yaml index bfea29c82ec..127f5b6e326 100644 --- a/charts/chainlink-cluster/Chart.yaml +++ b/charts/chainlink-cluster/Chart.yaml @@ -2,4 +2,9 @@ apiVersion: v1 name: chainlink-cluster description: Chainlink nodes cluster version: 0.1.3 -appVersion: '2.6.0' \ No newline at end of file +appVersion: "2.6.0" +dependencies: + - name: mockserver + version: "5.14.0" + repository: "@mockserver" + condition: mockserver.enabled \ No newline at end of file diff --git a/charts/chainlink-cluster/README.md b/charts/chainlink-cluster/README.md index e3cec129a91..5fb55536635 100644 --- a/charts/chainlink-cluster/README.md +++ b/charts/chainlink-cluster/README.md @@ -74,10 +74,10 @@ After that all the changes will be synced automatically Check `.profiles` to understand what is uploaded in profiles `runner` and `node` # Helm -If you would like to use `helm` directly, please uncomment data in `values-raw-helm.yaml` +If you would like to use `helm` directly, please uncomment data in `values.yaml` ## Install from local files ``` -helm install -f values-raw-helm.yaml cl-cluster . +helm install -f values.yaml cl-cluster . ``` Forward all apps (in another terminal) ``` @@ -99,7 +99,7 @@ kubectl config set-context --current --namespace cl-cluster Install ``` -helm install -f values-raw-helm.yaml cl-cluster chainlink-cluster/chainlink-cluster --version v0.1.2 +helm install -f values.yaml cl-cluster . ``` ## Create a new release diff --git a/charts/chainlink-cluster/connect.toml b/charts/chainlink-cluster/connect.toml index f0a74d4c144..1f49b5a6e37 100644 --- a/charts/chainlink-cluster/connect.toml +++ b/charts/chainlink-cluster/connect.toml @@ -9,4 +9,4 @@ cl_node_url_template = "http://app-node-%d:6688" cl_node_internal_dns_record_template = "app-node-%d" cl_node_user = "notreal@fakeemail.ch" cl_node_password = "fj293fbBnlQ!f9vNs" -mockserver_url = "http://app-mockserver:1080" \ No newline at end of file +mockserver_url = "http://mockserver:1080" \ No newline at end of file diff --git a/charts/chainlink-cluster/dashboard/dashboard.go b/charts/chainlink-cluster/dashboard/dashboard.go index b29140c0405..19a596b63e9 100644 --- a/charts/chainlink-cluster/dashboard/dashboard.go +++ b/charts/chainlink-cluster/dashboard/dashboard.go @@ -350,48 +350,6 @@ func (m *CLClusterDashboard) generate() error { ), ), ), - // logs - dashboard.Row( - "Logs", - row.Collapse(), - row.WithTimeSeries( - "Log Counters", - timeseries.Span(12), - timeseries.Height("200px"), - timeseries.DataSource(m.PrometheusDataSourceName), - timeseries.WithPrometheusTarget( - `log_panic_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - panic"), - ), - timeseries.WithPrometheusTarget( - `log_fatal_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - fatal"), - ), - timeseries.WithPrometheusTarget( - `log_critical_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - critical"), - ), - timeseries.WithPrometheusTarget( - `log_warn_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - warn"), - ), - timeseries.WithPrometheusTarget( - `log_error_count{namespace="${namespace}"}`, - prometheus.Legend("{{pod}} - error"), - ), - ), - m.logsRowOption("All errors", ` - {namespace="${namespace}", app="app", container="node"} - | json - | level="error" - | line_format "{{ .instance }} {{ .level }} {{ .ts }} {{ .logger }} {{ .caller }} {{ .msg }} {{ .version }} {{ .nodeTier }} {{ .nodeName }} {{ .node }} {{ .evmChainID }} {{ .nodeOrder }} {{ .mode }} {{ .nodeState }} {{ .sentryEventID }} {{ .stacktrace }}"`), - m.logsRowOption("Node 1", `{namespace="${namespace}", app="app", instance="node-1", container="node"}`), - m.logsRowOption("Node 2", `{namespace="${namespace}", app="app", instance="node-2", container="node"}`), - m.logsRowOption("Node 3", `{namespace="${namespace}", app="app", instance="node-3", container="node"}`), - m.logsRowOption("Node 4", `{namespace="${namespace}", app="app", instance="node-4", container="node"}`), - m.logsRowOption("Node 5", `{namespace="${namespace}", app="app", instance="node-5", container="node"}`), - m.logsRowOption("Node 6", `{namespace="${namespace}", app="app", instance="node-6", container="node"}`), - ), // HeadTracker dashboard.Row("Head tracker", row.Collapse(), diff --git a/charts/chainlink-cluster/devspace.yaml b/charts/chainlink-cluster/devspace.yaml index 4f7cf8641a3..cb4c8bfce49 100644 --- a/charts/chainlink-cluster/devspace.yaml +++ b/charts/chainlink-cluster/devspace.yaml @@ -40,26 +40,56 @@ deployments: # they can be defined the same way in values.yml # devspace merging this "values" and "values.yml" before deploy values: - runner: - image: ${DEVSPACE_IMAGE} - stateful: false - geth: - version: v1.12.0 - wsrpc-port: 8546 - httprpc-port: 8544 - networkid: 1337 - blocktime: 1 - mockserver: - port: 1080 - db: - stateful: false + podSecurityContext: + fsGroup: 999 + chainlink: + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 14933 + runAsGroup: 999 web_port: 6688 p2p_port: 6690 nodes: - name: node-1 image: ${DEVSPACE_IMAGE} version: latest + # override default config per node + # for example, use OCRv2 P2P setup, the whole config + # toml: | + # RootDir = './clroot' + # [Log] + # JSONConsole = true + # Level = 'debug' + # [WebServer] + # AllowOrigins = '*' + # SecureCookies = false + # SessionTimeout = '999h0m0s' + # [OCR2] + # Enabled = true + # [P2P] + # [P2P.V2] + # Enabled = false + # AnnounceAddresses = [] + # DefaultBootstrappers = [] + # DeltaDial = '15s' + # DeltaReconcile = '1m0s' + # ListenAddresses = [] + # [[EVM]] + # ChainID = '1337' + # MinContractPayment = '0' + # [[EVM.Nodes]] + # Name = 'node-0' + # WSURL = 'ws://geth:8546' + # HTTPURL = 'http://geth:8544' + # [WebServer.TLS] + # HTTPSPort = 0 + # or use overridesToml to override some part of configuration + # overridesToml: | - name: node-2 image: ${DEVSPACE_IMAGE} version: latest @@ -75,11 +105,125 @@ deployments: - name: node-6 image: ${DEVSPACE_IMAGE} version: latest - prometheusMonitor: "true" - podAnnotations: { } - nodeSelector: { } - tolerations: [ ] - affinity: { } + resources: + requests: + cpu: 350m + memory: 1024Mi + limits: + cpu: 350m + memory: 1024Mi + + # each CL node have a dedicated PostgreSQL 11.15 + # use StatefulSet by setting: + # + # stateful: true + # capacity 10Gi + # + # if you are running long tests + db: + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + stateful: false + resources: + requests: + cpu: 1 + memory: 1024Mi + limits: + cpu: 1 + memory: 1024Mi + # default cluster shipped with latest Geth ( dev mode by default ) + geth: + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + version: v1.12.0 + wsrpc-port: 8546 + httprpc-port: 8544 + networkid: 1337 + blocktime: 1 + resources: + requests: + cpu: 1 + memory: 1024Mi + limits: + cpu: 1 + memory: 1024Mi + # mockserver is https://www.mock-server.com/where/kubernetes.html + # used to stub External Adapters + mockserver: + # image: "mockserver/mockserver" + # version: "mockserver-5.15.0" + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + enabled: true + releasenameOverride: mockserver + app: + runAsUser: 999 + readOnlyRootFilesystem: false + port: 1080 + resources: + requests: + cpu: 1 + memory: 1024Mi + limits: + cpu: 1 + memory: 1024Mi + runner: + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + stateful: false + resources: + requests: + cpu: 1 + memory: 512Mi + limits: + cpu: 1 + memory: 512Mi + affinity: { } + tolerations: [ ] + nodeSelector: { } + ingress: + enabled: false + className: "" + hosts: [ ] + tls: [ ] + annotations: { } + service: + type: NodePort + port: 8080 + + + # monitoring.coreos.com/v1 PodMonitor for each node + prometheusMonitor: true + + # deployment placement, standard helm stuff + podAnnotations: + nodeSelector: + tolerations: + affinity: profiles: # this replaces only "runner" pod, usable when you'd like to run some system level tests inside k8s diff --git a/charts/chainlink-cluster/templates/chainlink-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-db-deployment.yaml similarity index 60% rename from charts/chainlink-cluster/templates/chainlink-deployment.yaml rename to charts/chainlink-cluster/templates/chainlink-db-deployment.yaml index b434c9894b0..f335130ea9f 100644 --- a/charts/chainlink-cluster/templates/chainlink-deployment.yaml +++ b/charts/chainlink-cluster/templates/chainlink-db-deployment.yaml @@ -6,10 +6,10 @@ kind: StatefulSet kind: Deployment {{ end }} metadata: - name: {{ $.Release.Name }}-{{ $cfg.name }} + name: {{ $.Release.Name }}-{{ $cfg.name }}-db spec: {{ if $.Values.db.stateful }} - serviceName: {{ $.Release.Name }}-{{ $cfg.name }}-service + serviceName: {{ $.Release.Name }}-db-${{ $cfg.name }} podManagementPolicy: Parallel volumeClaimTemplates: - metadata: @@ -23,14 +23,14 @@ spec: {{ end }} selector: matchLabels: - app: {{ $.Release.Name }} - instance: {{ $cfg.name }} + app: {{ $.Release.Name }}-db + instance: {{ $cfg.name }}-db release: {{ $.Release.Name }} template: metadata: labels: - app: {{ $.Release.Name }} - instance: {{ $cfg.name }} + app: {{ $.Release.Name }}-db + instance: {{ $cfg.name }}-db release: {{ $.Release.Name }} {{- range $key, $value := $.Values.labels }} {{ $key }}: {{ $value | quote }} @@ -44,11 +44,16 @@ spec: {{- end }} spec: volumes: + # TODO: breakout this config map into a separate one for the db. - name: {{ $.Release.Name }}-{{ $cfg.name }}-cm configMap: name: {{ $.Release.Name }}-{{ $cfg.name }}-cm + securityContext: + {{- toYaml $.Values.db.podSecurityContext | nindent 8 }} containers: - name: chainlink-db + securityContext: + {{- toYaml $.Values.db.securityContext | nindent 12 }} image: {{ default "postgres:11.15" $.Values.db.image }} command: - docker-entrypoint.sh @@ -114,60 +119,11 @@ spec: - mountPath: /docker-entrypoint-initdb.d/init.sql name: {{ $.Release.Name }}-{{ $cfg.name }}-cm subPath: init.sql - {{ if $.Values.db.stateful }} - volumeMounts: + {{ if $.Values.db.stateful }} - mountPath: /var/lib/postgresql/data name: postgres subPath: postgres-db - {{ end }} - - name: node - image: {{ default "public.ecr.aws/chainlink/chainlink" $cfg.image }} - imagePullPolicy: Always - command: ["bash", "-c", "while ! pg_isready --host 0.0.0.0 --port 5432; do echo \"waiting for database to start\"; sleep 1; done && chainlink -c /etc/node-secrets-volume/default.toml -c /etc/node-secrets-volume/overrides.toml -secrets /etc/node-secrets-volume/secrets.toml node start -d -p /etc/node-secrets-volume/node-password -a /etc/node-secrets-volume/apicredentials --vrfpassword=/etc/node-secrets-volume/apicredentials"] - ports: - - name: access - containerPort: {{ $.Values.chainlink.web_port }} - - name: p2p - containerPort: {{ $.Values.chainlink.p2p_port }} - env: - - name: CL_DATABASE_URL - value: postgresql://postgres:verylongdatabasepassword@0.0.0.0/chainlink?sslmode=disable - - name: CL_DEV - value: "false" - volumeMounts: - - name: {{ $.Release.Name }}-{{ $cfg.name }}-cm - mountPath: /etc/node-secrets-volume/ - livenessProbe: - httpGet: - path: /health - port: {{ $.Values.chainlink.web_port }} - initialDelaySeconds: 1 - periodSeconds: 5 - timeoutSeconds: 10 - readinessProbe: - httpGet: - path: /health - port: {{ $.Values.chainlink.web_port }} - initialDelaySeconds: 1 - periodSeconds: 5 - timeoutSeconds: 10 - startupProbe: - httpGet: - path: / - port: {{ $.Values.chainlink.web_port }} - initialDelaySeconds: 15 - periodSeconds: 5 - failureThreshold: 20 - {{ if (hasKey $.Values.chainlink "resources") }} - resources: - requests: - memory: {{ default "1024Mi" $.Values.chainlink.resources.requests.memory }} - cpu: {{ default "500m" $.Values.chainlink.resources.requests.cpu }} - limits: - memory: {{ default "1024Mi" $.Values.chainlink.resources.limits.memory }} - cpu: {{ default "500m" $.Values.chainlink.resources.limits.cpu }} - {{ else }} - {{ end }} + {{ end }} {{- with $.Values.nodeSelector }} nodeSelector: {{ toYaml . | indent 8 }} @@ -181,4 +137,4 @@ spec: {{ toYaml . | indent 8 }} {{- end }} --- -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/chainlink-cluster/templates/chainlink-db-service.yaml b/charts/chainlink-cluster/templates/chainlink-db-service.yaml new file mode 100644 index 00000000000..f27bd9eab20 --- /dev/null +++ b/charts/chainlink-cluster/templates/chainlink-db-service.yaml @@ -0,0 +1,16 @@ +{{- range $cfg := .Values.chainlink.nodes }} +apiVersion: v1 +kind: Service +metadata: + name: {{ $.Release.Name }}-db-{{ $cfg.name }} +spec: + selector: + app: {{ $.Release.Name }}-db + instance: {{ $cfg.name }}-db + release: {{ $.Release.Name }} + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 +--- +{{- end }} \ No newline at end of file diff --git a/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml new file mode 100644 index 00000000000..463453aff93 --- /dev/null +++ b/charts/chainlink-cluster/templates/chainlink-node-deployment.yaml @@ -0,0 +1,97 @@ +{{- range $cfg := .Values.chainlink.nodes }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $.Release.Name }}-{{ $cfg.name }} +spec: + selector: + matchLabels: + app: {{ $.Release.Name }} + instance: {{ $cfg.name }} + release: {{ $.Release.Name }} + template: + metadata: + labels: + app: {{ $.Release.Name }} + instance: {{ $cfg.name }} + release: {{ $.Release.Name }} + {{- range $key, $value := $.Values.labels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + annotations: + prometheus.io/scrape: 'true' + {{- range $key, $value := $.Values.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + spec: + volumes: + - name: {{ $.Release.Name }}-{{ $cfg.name }}-cm + configMap: + name: {{ $.Release.Name }}-{{ $cfg.name }}-cm + securityContext: + {{- toYaml $.Values.chainlink.podSecurityContext | nindent 8 }} + containers: + - name: node + securityContext: + {{- toYaml $.Values.chainlink.securityContext | nindent 12 }} + image: {{ default "public.ecr.aws/chainlink/chainlink" $cfg.image }} + imagePullPolicy: Always + command: ["bash", "-c", "while ! pg_isready -U postgres --host {{ $.Release.Name }}-db-{{ $cfg.name }} --port 5432; do echo \"waiting for database to start\"; sleep 1; done && chainlink -c /etc/node-secrets-volume/default.toml -c /etc/node-secrets-volume/overrides.toml -secrets /etc/node-secrets-volume/secrets.toml node start -d -p /etc/node-secrets-volume/node-password -a /etc/node-secrets-volume/apicredentials --vrfpassword=/etc/node-secrets-volume/apicredentials"] + ports: + - name: access + containerPort: {{ $.Values.chainlink.web_port }} + - name: p2p + containerPort: {{ $.Values.chainlink.p2p_port }} + env: + - name: CL_DATABASE_URL + value: postgresql://postgres:verylongdatabasepassword@{{ $.Release.Name }}-db-{{ $cfg.name }}/chainlink?sslmode=disable + - name: CL_DEV + value: "false" + volumeMounts: + - name: {{ $.Release.Name }}-{{ $cfg.name }}-cm + mountPath: /etc/node-secrets-volume/ + livenessProbe: + httpGet: + path: /health + port: {{ $.Values.chainlink.web_port }} + initialDelaySeconds: 1 + periodSeconds: 5 + timeoutSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: {{ $.Values.chainlink.web_port }} + initialDelaySeconds: 1 + periodSeconds: 5 + timeoutSeconds: 10 + startupProbe: + httpGet: + path: / + port: {{ $.Values.chainlink.web_port }} + initialDelaySeconds: 15 + periodSeconds: 5 + failureThreshold: 20 + {{ if (hasKey $.Values.chainlink "resources") }} + resources: + requests: + memory: {{ default "1024Mi" $.Values.chainlink.resources.requests.memory }} + cpu: {{ default "500m" $.Values.chainlink.resources.requests.cpu }} + limits: + memory: {{ default "1024Mi" $.Values.chainlink.resources.limits.memory }} + cpu: {{ default "500m" $.Values.chainlink.resources.limits.cpu }} + {{ else }} + {{ end }} +{{- with $.Values.nodeSelector }} + nodeSelector: + {{ toYaml . | indent 8 }} +{{- end }} +{{- with $.Values.affinity }} + affinity: + {{ toYaml . | indent 8 }} +{{- end }} +{{- with $.Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} +{{- end }} +--- +{{- end }} diff --git a/charts/chainlink-cluster/templates/chainlink-service.yaml b/charts/chainlink-cluster/templates/chainlink-node-service.yaml similarity index 100% rename from charts/chainlink-cluster/templates/chainlink-service.yaml rename to charts/chainlink-cluster/templates/chainlink-node-service.yaml diff --git a/charts/chainlink-cluster/templates/geth-config-map.yaml b/charts/chainlink-cluster/templates/geth-config-map.yaml index f3369ba580f..022d9f2ea61 100644 --- a/charts/chainlink-cluster/templates/geth-config-map.yaml +++ b/charts/chainlink-cluster/templates/geth-config-map.yaml @@ -50,9 +50,9 @@ data: password.txt: | init.sh: | #!/bin/bash - if [ ! -d /root/.ethereum/keystore ]; then - echo "/root/.ethereum/keystore not found, running 'geth init'..." - geth init /root/ethconfig/genesis.json + if [ ! -d /app/.ethereum/keystore ]; then + echo "/app/.ethereum/keystore not found, running 'geth init'..." + geth init /app/ethconfig/genesis.json echo "...done!" fi diff --git a/charts/chainlink-cluster/templates/geth-deployment.yaml b/charts/chainlink-cluster/templates/geth-deployment.yaml index 11fb0cbee22..6948c4df288 100644 --- a/charts/chainlink-cluster/templates/geth-deployment.yaml +++ b/charts/chainlink-cluster/templates/geth-deployment.yaml @@ -22,31 +22,39 @@ spec: - name: configmap-volume configMap: name: geth-cm + - name: devchain-volume + emptyDir: {} + securityContext: + {{- toYaml $.Values.geth.podSecurityContext | nindent 8 }} containers: - name: geth-network + securityContext: + {{- toYaml $.Values.geth.securityContext | nindent 12 }} image: "{{ default "ethereum/client-go" .Values.geth.image }}:{{ default "stable" .Values.geth.version }}" - command: [ "sh", "./root/init.sh" ] + command: [ "sh", "/app/init.sh" ] volumeMounts: + - name: devchain-volume + mountPath: /app/.ethereum/devchain - name : configmap-volume - mountPath: /root/init.sh + mountPath: /app/init.sh subPath: init.sh - name: configmap-volume - mountPath: /root/config + mountPath: /app/config - name: configmap-volume - mountPath: /root/.ethereum/devchain/keystore/key1 + mountPath: /app/.ethereum/devchain/keystore/key1 subPath: key1 - name: configmap-volume - mountPath: /root/.ethereum/devchain/keystore/key2 + mountPath: /app/.ethereum/devchain/keystore/key2 subPath: key2 - name: configmap-volume - mountPath: /root/.ethereum/devchain/keystore/key3 + mountPath: /app/.ethereum/devchain/keystore/key3 subPath: key3 args: - '--dev' - '--password' - - '/root/config/password.txt' + - '/app/config/password.txt' - '--datadir' - - '/root/.ethereum/devchain' + - '/app/.ethereum/devchain' - '--unlock' - '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' - '--unlock' diff --git a/charts/chainlink-cluster/templates/mockserver-service.yaml b/charts/chainlink-cluster/templates/mockserver-service.yaml deleted file mode 100644 index f8ab78a84b5..00000000000 --- a/charts/chainlink-cluster/templates/mockserver-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{ if (hasKey .Values "mockserver") }} -apiVersion: v1 -kind: Service -metadata: - name: {{ .Release.Name }}-mockserver -spec: - selector: - app: {{ .Release.Name }}-mockserver - ports: - - name: serviceport - port: {{ default "1080" $.Values.mockserver.port}} - targetPort: serviceport - type: ClusterIP -{{ end }} \ No newline at end of file diff --git a/charts/chainlink-cluster/templates/mockserver.yaml b/charts/chainlink-cluster/templates/mockserver.yaml deleted file mode 100755 index 14c05d0acd5..00000000000 --- a/charts/chainlink-cluster/templates/mockserver.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{ if (hasKey .Values "mockserver") }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Release.Name }}-mockserver - labels: - app: {{ .Release.Name }}-mockserver -spec: - selector: - matchLabels: - app: {{ .Release.Name }}-mockserver - template: - metadata: -{{- if .Values.podAnnotations }} - annotations: -{{ toYaml .Values.podAnnotations | indent 8 }} -{{- end }} - name: {{ .Release.Name }}-mockserver - labels: - app: {{ .Release.Name }}-mockserver - spec: - containers: - - name: {{ .Release.Name }}-mockserver - image: {{ default "mockserver/mockserver" .Values.mockserver.image }}:{{ default "mockserver-5.15.0" .Values.mockserver.version }} - imagePullPolicy: IfNotPresent - securityContext: - runAsUser: 65534 # nonroot - readOnlyRootFilesystem: false - ports: - - name: serviceport - containerPort: {{ .Values.mockserver.port }} - protocol: TCP - env: - - name: LOG_LEVEL - value: "DEBUG" - - name: SERVER_PORT - value: {{ .Values.mockserver.port | quote }} - {{ if (hasKey $.Values.chainlink "resources") }} - resources: - requests: - memory: {{ default "1024Mi" $.Values.chainlink.resources.requests.memory }} - cpu: {{ default "500m" $.Values.chainlink.resources.requests.cpu }} - limits: - memory: {{ default "1024Mi" $.Values.chainlink.resources.limits.memory }} - cpu: {{ default "500m" $.Values.chainlink.resources.limits.cpu }} - {{ else }} - {{ end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{ toYaml . | indent 8 }} - {{- end }} -{{- end }} ---- \ No newline at end of file diff --git a/charts/chainlink-cluster/templates/runner-deployment.yaml b/charts/chainlink-cluster/templates/runner-deployment.yaml index 5d9025b41c5..9d80ac1bfab 100644 --- a/charts/chainlink-cluster/templates/runner-deployment.yaml +++ b/charts/chainlink-cluster/templates/runner-deployment.yaml @@ -22,8 +22,12 @@ spec: annotations: prometheus.io/scrape: 'true' spec: + securityContext: + {{- toYaml $.Values.runner.podSecurityContext | nindent 8 }} containers: - name: runner + securityContext: + {{- toYaml $.Values.runner.securityContext | nindent 12 }} image: {{ default "public.ecr.aws/chainlink/chainlink" .Values.runner.image }} imagePullPolicy: Always command: [ "/bin/bash", "-c", "--" ] diff --git a/charts/chainlink-cluster/values-raw-helm.yaml b/charts/chainlink-cluster/values-raw-helm.yaml deleted file mode 100644 index 726a5347119..00000000000 --- a/charts/chainlink-cluster/values-raw-helm.yaml +++ /dev/null @@ -1,134 +0,0 @@ -# override resources for keys "chainlink", "db", or "geth" if needed -# resources: -# requests: -# cpu: 350m -# memory: 1024Mi -# limits: -# cpu: 350m -# memory: 1024Mi -# images can be overriden for the same keys: -# image: ethereum/client-go -# version: stable -chainlink: - web_port: 6688 - p2p_port: 6690 - nodes: - - name: node-1 - image: "public.ecr.aws/chainlink/chainlink:latest" - # override default config per node - # for example, use OCRv2 P2P setup, the whole config -# toml: | -# RootDir = './clroot' -# [Log] -# JSONConsole = true -# Level = 'debug' -# [WebServer] -# AllowOrigins = '*' -# SecureCookies = false -# SessionTimeout = '999h0m0s' -# [OCR2] -# Enabled = true -# [P2P] -# [P2P.V2] -# Enabled = false -# AnnounceAddresses = [] -# DefaultBootstrappers = [] -# DeltaDial = '15s' -# DeltaReconcile = '1m0s' -# ListenAddresses = [] -# [[EVM]] -# ChainID = '1337' -# MinContractPayment = '0' -# [[EVM.Nodes]] -# Name = 'node-0' -# WSURL = 'ws://geth:8546' -# HTTPURL = 'http://geth:8544' -# [WebServer.TLS] -# HTTPSPort = 0 -# or use overridesToml to override some part of configuration -# overridesToml: | - - name: node-2 - - name: node-3 - - name: node-4 - - name: node-5 - - name: node-6 - resources: - requests: - cpu: 350m - memory: 1024Mi - limits: - cpu: 350m - memory: 1024Mi - -# each CL node have a dedicated PostgreSQL 11.15 -# use StatefulSet by setting: -# -# stateful: true -# capacity 10Gi -# -# if you are running long tests -db: - stateful: false - resources: - requests: - cpu: 1 - memory: 1024Mi - limits: - cpu: 1 - memory: 1024Mi -# default cluster shipped with latest Geth ( dev mode by default ) -geth: - version: v1.12.0 - wsrpc-port: 8546 - httprpc-port: 8544 - networkid: 1337 - blocktime: 1 - resources: - requests: - cpu: 1 - memory: 1024Mi - limits: - cpu: 1 - memory: 1024Mi -# mockserver is https://www.mock-server.com/where/kubernetes.html -# used to stub External Adapters -mockserver: - port: 1080 - resources: - requests: - cpu: 1 - memory: 1024Mi - limits: - cpu: 1 - memory: 1024Mi -runner: - stateful: false - resources: - requests: - cpu: 1 - memory: 512Mi - limits: - cpu: 1 - memory: 512Mi - affinity: { } - tolerations: [ ] - nodeSelector: { } - ingress: - enabled: false - className: "" - hosts: [ ] - tls: [ ] - annotations: { } - service: - type: NodePort - port: 8080 - - -# monitoring.coreos.com/v1 PodMonitor for each node -prometheusMonitor: false - -# deployment placement, standard helm stuff -podAnnotations: -nodeSelector: -tolerations: -affinity: diff --git a/charts/chainlink-cluster/values.yaml b/charts/chainlink-cluster/values.yaml new file mode 100644 index 00000000000..eb93e6cefcf --- /dev/null +++ b/charts/chainlink-cluster/values.yaml @@ -0,0 +1,178 @@ +# override resources for keys "chainlink", "db", or "geth" if needed +# resources: +# requests: +# cpu: 350m +# memory: 1024Mi +# limits: +# cpu: 350m +# memory: 1024Mi +# images can be overriden for the same keys: +# image: ethereum/client-go +# version: stable +chainlink: + podSecurityContext: + fsGroup: 14933 + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 14933 + runAsGroup: 14933 + web_port: 6688 + p2p_port: 6690 + nodes: + - name: node-1 + image: "public.ecr.aws/chainlink/chainlink:latest" + # override default config per node + # for example, use OCRv2 P2P setup, the whole config + # toml: | + # RootDir = './clroot' + # [Log] + # JSONConsole = true + # Level = 'debug' + # [WebServer] + # AllowOrigins = '*' + # SecureCookies = false + # SessionTimeout = '999h0m0s' + # [OCR2] + # Enabled = true + # [P2P] + # [P2P.V2] + # Enabled = false + # AnnounceAddresses = [] + # DefaultBootstrappers = [] + # DeltaDial = '15s' + # DeltaReconcile = '1m0s' + # ListenAddresses = [] + # [[EVM]] + # ChainID = '1337' + # MinContractPayment = '0' + # [[EVM.Nodes]] + # Name = 'node-0' + # WSURL = 'ws://geth:8546' + # HTTPURL = 'http://geth:8544' + # [WebServer.TLS] + # HTTPSPort = 0 + # or use overridesToml to override some part of configuration + # overridesToml: | + - name: node-2 + - name: node-3 + - name: node-4 + - name: node-5 + - name: node-6 + resources: + requests: + cpu: 350m + memory: 1024Mi + limits: + cpu: 350m + memory: 1024Mi + +# each CL node have a dedicated PostgreSQL 11.15 +# use StatefulSet by setting: +# +# stateful: true +# capacity 10Gi +# +# if you are running long tests +db: + podSecurityContext: + fsGroup: 999 + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + stateful: false + resources: + requests: + cpu: 1 + memory: 1024Mi + limits: + cpu: 1 + memory: 1024Mi +# default cluster shipped with latest Geth ( dev mode by default ) +geth: + podSecurityContext: + fsGroup: 999 + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + version: v1.12.0 + wsrpc-port: 8546 + httprpc-port: 8544 + networkid: 1337 + blocktime: 1 + resources: + requests: + cpu: 1 + memory: 1024Mi + limits: + cpu: 1 + memory: 1024Mi +# mockserver is https://www.mock-server.com/where/kubernetes.html +# used to stub External Adapters +mockserver: + enabled: true + releasenameOverride: mockserver + app: + runAsUser: 999 + readOnlyRootFilesystem: false + port: 1080 + resources: + requests: + cpu: 1 + memory: 1024Mi + limits: + cpu: 1 + memory: 1024Mi +runner: + podSecurityContext: + fsGroup: 999 + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + stateful: false + resources: + requests: + cpu: 1 + memory: 512Mi + limits: + cpu: 1 + memory: 512Mi + affinity: {} + tolerations: [] + nodeSelector: {} + ingress: + enabled: false + className: "" + hosts: [] + tls: [] + annotations: {} + service: + type: NodePort + port: 8080 + +# monitoring.coreos.com/v1 PodMonitor for each node +prometheusMonitor: true + +# deployment placement, standard helm stuff +podAnnotations: +nodeSelector: +tolerations: +affinity: diff --git a/core/chainlink.devspace.Dockerfile b/core/chainlink.devspace.Dockerfile index 9ec061ae40d..88d3cec16ad 100644 --- a/core/chainlink.devspace.Dockerfile +++ b/core/chainlink.devspace.Dockerfile @@ -20,7 +20,7 @@ RUN make install-chainlink # Final image: ubuntu with chainlink binary FROM golang:1.21-bullseye -ARG CHAINLINK_USER=root +ARG CHAINLINK_USER=chainlink ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y ca-certificates gnupg lsb-release curl From fa0f16ad0acf417db1186728560278a049357914 Mon Sep 17 00:00:00 2001 From: Lei Date: Fri, 1 Dec 2023 11:04:54 -0800 Subject: [PATCH 257/271] make streams lookup modular (#11368) * make streams lookup modular * polish * address comment to use pointer instead of array/map * rebase * get rid of slice --- core/scripts/chaincli/handler/debug.go | 150 +++-- .../handler/mercury_lookup_handler.go | 534 ------------------ core/scripts/go.mod | 3 +- core/scripts/go.sum | 2 - .../v21/mercury/streams/streams.go | 143 ++--- .../v21/mercury/streams/streams_test.go | 20 +- 6 files changed, 186 insertions(+), 666 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 fec8c6cd414..0075862d95d 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -22,12 +22,17 @@ 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/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" "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" @@ -36,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() @@ -125,6 +125,8 @@ 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 { @@ -177,7 +179,8 @@ 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) if err != nil { @@ -229,73 +232,82 @@ 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) + mc := &models.MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, 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) 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 + _, _, _, 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) + values, err := streams.DoMercuryRequest(ctx, streamsLookup, &automationCheckResult) + + 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) + resolveIneligible("failed to DoMercuryRequest") } - callbackResult, err := keeperRegistry21.CheckCallback(triggerCallOpts, upkeepID, values, streamsLookup.extraData) + + // do checkCallback + err = streams.CheckCallback(ctx, values, streamsLookup, &automationCheckResult) 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 + 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) } @@ -317,6 +329,23 @@ 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 } @@ -370,9 +399,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, @@ -381,16 +428,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) { @@ -402,11 +440,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) { @@ -507,5 +545,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 1bd4b2e183c..00000000000 --- a/core/scripts/chaincli/handler/mercury_lookup_handler.go +++ /dev/null @@ -1,534 +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 { - // 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 { - // 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 c7af0541c12..21f00df02de 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -7,7 +7,6 @@ replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( github.com/ava-labs/coreth v0.12.1 - 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 @@ -19,7 +18,6 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.0 - 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 @@ -283,6 +281,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 7cea79eb76e..36504924e16 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -152,8 +152,6 @@ github.com/ava-labs/avalanchego v1.10.1 h1:lBeamJ1iNq+p2oKg2nAs+A65m8vhSDjkiTDbw github.com/ava-labs/avalanchego v1.10.1/go.mod h1:ZvSXWlbkUKlbk3BsWx29a+8eVHe/WBsOxh55BSGoeRk= github.com/ava-labs/coreth v0.12.1 h1:EWSkFGHGVUxmu1pnSK/2pdcxaAVHbGspHqO3Ag+i7sA= github.com/ava-labs/coreth v0.12.1/go.mod h1:/5x54QlIKjlPebkdzTA5ic9wXdejbWOnQosztkv9jxo= -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 aec23431921..cb9e2dd6752 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" @@ -92,8 +91,9 @@ func NewStreamsLookup( // Lookup looks through check upkeep results looking for any that need off chain lookup func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult { lookups := map[int]*mercury.StreamsLookup{} - for i, checkResult := range checkResults { - s.buildResult(ctx, i, checkResult, checkResults, lookups) + for _, checkResult := range checkResults { + copyCheckResult := checkResult + s.buildResult(ctx, ©CheckResult, lookups) } var wg sync.WaitGroup @@ -101,7 +101,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe wg.Add(1) func(i int, lookup *mercury.StreamsLookup) { s.threadCtrl.Go(func(ctx context.Context) { - s.doLookup(ctx, &wg, lookup, i, checkResults) + s.doLookup(ctx, &wg, lookup, &checkResults[i]) }) }(i, lookup) } @@ -112,7 +112,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe } // buildResult checks if the upkeep is allowed by Mercury and builds a streams lookup request from the check result -func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keepers.CheckResult, checkResults []ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { +func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { lookupLggr := s.lggr.With("where", "StreamsLookup") if checkResult.IneligibilityReason != uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted) { // Streams Lookup only works when upkeep target check reverts @@ -129,7 +129,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper // Try to decode the revert error into streams lookup format. User upkeeps can revert with any reason, see if they // tried to call mercury - lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResults[i].PerformData)) + lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResult.PerformData)) streamsLookupErr, err := s.packer.DecodeStreamsLookupRequest(checkResult.PerformData) if err != nil { lookupLggr.Debugf("at block %d upkeep %s DecodeStreamsLookupRequest failed: %v", block, upkeepId, err) @@ -139,7 +139,7 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper streamsLookupResponse := &mercury.StreamsLookup{StreamsLookupError: streamsLookupErr} if len(streamsLookupResponse.Feeds) == 0 { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) lookupLggr.Debugf("at block %s upkeep %s has empty feeds array", block, upkeepId) return } @@ -148,21 +148,21 @@ 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) - checkResults[i].Retryable = retryable + checkResult.PipelineExecutionState = uint8(state) + checkResult.IneligibilityReason = uint8(reason) + checkResult.Retryable = retryable return } else if !allowed { lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) + checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) return } } else if streamsLookupResponse.IsMercuryVersionUnkown() { // if mercury version cannot be determined, set failure reason lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) return } @@ -171,71 +171,103 @@ func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keeper // in the revert for mercury v0.2, which is denoted by time in the struct bc starting from v0.3, only timestamp will be supported streamsLookupResponse.Block = uint64(block.Int64()) lookupLggr.Infof("at block %d upkeep %s DecodeStreamsLookupRequest feedKey=%s timeKey=%s feeds=%v time=%s extraData=%s", block, upkeepId, streamsLookupResponse.FeedParamKey, streamsLookupResponse.TimeParamKey, streamsLookupResponse.Feeds, streamsLookupResponse.Time, hexutil.Encode(streamsLookupResponse.ExtraData)) - lookups[i] = streamsLookupResponse + lookups[len(lookups)] = streamsLookupResponse } -func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, i int, checkResults []ocr2keepers.CheckResult) { +func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, checkResult *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, checkResult) + 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, checkResult); 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, checkResult *ocr2keepers.CheckResult) 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 + s.lggr.Errorf("at block %d upkeep %s checkCallback packing err: %s", lookup.Block, lookup.UpkeepId, err.Error()) + checkResult.Retryable = false + checkResult.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 { + // 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 { 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 + checkResult.Retryable = true + checkResult.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 + checkResult.PipelineExecutionState = unpackCallBackState + return err } if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) + checkResult.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 fmt.Errorf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) + } if !needed { - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) + checkResult.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 fmt.Errorf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) } - checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) - checkResults[i].Eligible = true - checkResults[i].PerformData = performData + checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) + checkResult.Eligible = true + checkResult.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 +} + +func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) ([][]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(checkResult.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 { + 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()) + checkResult.Retryable = retryable + checkResult.RetryInterval = retryInterval + checkResult.PipelineExecutionState = uint8(state) + checkResult.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 +// 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 @@ -255,7 +287,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) } @@ -281,26 +312,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 abcc37dca18..2475244b4d0 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 @@ -126,6 +126,7 @@ func TestStreams_CheckCallback(t *testing.T) { tests := []struct { name string lookup *mercury.StreamsLookup + input []ocr2keepers.CheckResult values [][]byte statusCode int @@ -153,6 +154,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}, @@ -185,6 +189,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}, @@ -216,6 +223,9 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, + input: []ocr2keepers.CheckResult{ + {}, + }, values: values, statusCode: http.StatusOK, callbackResp: []byte{}, @@ -255,10 +265,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) }) } } @@ -434,7 +444,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 5e018cc58bcc4f773f40c9cabe4ec66d5f2ebf95 Mon Sep 17 00:00:00 2001 From: Ryan Hall Date: Fri, 1 Dec 2023 14:06:55 -0500 Subject: [PATCH 258/271] write test for backwards compatibility in automation report encoding function (#11430) --- .../evmregistry/v21/encoding/encoder_test.go | 35 +++++++++++++++++++ .../v21/fixtures/expected_encoded_report.txt | 1 + 2 files changed, 36 insertions(+) create mode 100644 core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/fixtures/expected_encoded_report.txt diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go index 1376e2a9bb3..aa549ab3ec8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/encoder_test.go @@ -1,7 +1,10 @@ package encoding import ( + "bytes" + "encoding/hex" "math/big" + "os" "testing" "github.com/ethereum/go-ethereum/common" @@ -12,6 +15,19 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" ) +var expectedEncodedReport []byte + +func init() { + b, err := os.ReadFile("../fixtures/expected_encoded_report.txt") + if err != nil { + panic(err) + } + expectedEncodedReport, err = hex.DecodeString(string(b)) + if err != nil { + panic(err) + } +} + func TestReportEncoder_EncodeExtract(t *testing.T) { encoder := reportEncoder{ packer: NewAbiPacker(), @@ -93,6 +109,25 @@ func TestReportEncoder_EncodeExtract(t *testing.T) { } } +func TestReportEncoder_BackwardsCompatibility(t *testing.T) { + encoder := reportEncoder{ + packer: NewAbiPacker(), + } + results := []ocr2keepers.CheckResult{ + newResult(1, 2, core.GenUpkeepID(ocr2keepers.LogTrigger, "10"), 5, 6), + newResult(3, 4, core.GenUpkeepID(ocr2keepers.ConditionTrigger, "20"), 7, 8), + } + encoded, err := encoder.Encode(results...) + assert.NoError(t, err) + if !bytes.Equal(encoded, expectedEncodedReport) { + assert.Fail(t, + "encoded report does not match expected encoded report; "+ + "this means a breaking change has been made to the report encoding function; "+ + "only update this test if non-backwards-compatible changes are necessary", + ) + } +} + func newResult(block int64, checkBlock ocr2keepers.BlockNumber, id ocr2keepers.UpkeepIdentifier, fastGasWei, linkNative int64) ocr2keepers.CheckResult { tp := core.GetUpkeepType(id) diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/fixtures/expected_encoded_report.txt b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/fixtures/expected_encoded_report.txt new file mode 100644 index 00000000000..3fb42146c8c --- /dev/null +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/fixtures/expected_encoded_report.txt @@ -0,0 +1 @@ +00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000131300000000000000000000000000000010000000000000000000000000000003230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a0aaaaaaaa9012345678901234567890123456789012345678901234567890123412345678901234567890123456789012345678901234567890123456789012340000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000101020304050607080102030405060708010203040506070801020304050607080000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000301020304050607080102030405060708010203040506070801020304050607080000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005646174613000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056461746130000000000000000000000000000000000000000000000000000000 \ No newline at end of file From 8ffd084314876a8ae27827b661179afde9db480c Mon Sep 17 00:00:00 2001 From: Steve Ellis Date: Fri, 1 Dec 2023 20:34:11 +0100 Subject: [PATCH 259/271] remove previous Solidity code owners (#11454) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index bd2d0419cf7..e34d3ea1bef 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -71,7 +71,7 @@ core/scripts/gateway @bolekk @pinebit /operator-ui/ @DeividasK @jkongie # Contracts -/contracts/ @se3000 @connorwstein @RensR +/contracts/ @RensR # First we match on project names to catch files like the compilation scripts, # gas snapshots and other files not places in the project directories. From b8084cb357582fdf43f64b1e08c38e1b457ba4ad Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Fri, 1 Dec 2023 14:27:44 -0600 Subject: [PATCH 260/271] common/txmgr: initialize map before goroutines race ahead (#11452) * common/txmgr: initiliaze map before goroutines race ahead * pass ctx from Start() --- common/txmgr/broadcaster.go | 18 +++++++----------- common/txmgr/test_helpers.go | 4 ++-- common/txmgr/txmgr.go | 6 ++++-- core/chains/evm/txmgr/broadcaster_test.go | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/common/txmgr/broadcaster.go b/common/txmgr/broadcaster.go index ab620c51be3..54ae653f662 100644 --- a/common/txmgr/broadcaster.go +++ b/common/txmgr/broadcaster.go @@ -194,14 +194,14 @@ func NewBroadcaster[ // Start starts Broadcaster service. // The provided context can be used to terminate Start sequence. -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Start(_ context.Context) error { +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Start(ctx context.Context) error { return eb.StartOnce("Broadcaster", func() (err error) { - return eb.startInternal() + return eb.startInternal(ctx) }) } // startInternal can be called multiple times, in conjunction with closeInternal. The TxMgr uses this functionality to reset broadcaster multiple times in its own lifetime. -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) startInternal() error { +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) startInternal(ctx context.Context) error { eb.initSync.Lock() defer eb.initSync.Unlock() if eb.isStarted { @@ -222,16 +222,15 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) star eb.wg = sync.WaitGroup{} eb.wg.Add(len(eb.enabledAddresses)) eb.triggers = make(map[ADDR]chan struct{}) + eb.sequenceLock.Lock() + eb.nextSequenceMap = eb.loadNextSequenceMap(ctx, eb.enabledAddresses) + eb.sequenceLock.Unlock() for _, addr := range eb.enabledAddresses { triggerCh := make(chan struct{}, 1) eb.triggers[addr] = triggerCh go eb.monitorTxs(addr, triggerCh) } - eb.sequenceLock.Lock() - defer eb.sequenceLock.Unlock() - eb.nextSequenceMap = eb.loadNextSequenceMap(eb.enabledAddresses) - eb.isStarted = true return nil } @@ -286,10 +285,7 @@ func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) Trig } // Load the next sequence map using the tx table or on-chain (if not found in tx table) -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) loadNextSequenceMap(addresses []ADDR) map[ADDR]SEQ { - ctx, cancel := eb.chStop.NewCtx() - defer cancel() - +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) loadNextSequenceMap(ctx context.Context, addresses []ADDR) map[ADDR]SEQ { nextSequenceMap := make(map[ADDR]SEQ) for _, address := range addresses { seq, err := eb.getSequenceForAddr(ctx, address) diff --git a/common/txmgr/test_helpers.go b/common/txmgr/test_helpers.go index 0f128a23af4..6c0c5680ea7 100644 --- a/common/txmgr/test_helpers.go +++ b/common/txmgr/test_helpers.go @@ -22,8 +22,8 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) XXXDeliverB tr.mb.Deliver(blockHeight) } -func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) XXXTestStartInternal() error { - return eb.startInternal() +func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) XXXTestStartInternal(ctx context.Context) error { + return eb.startInternal(ctx) } func (eb *Broadcaster[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, SEQ, FEE]) XXXTestCloseInternal() error { diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 3fbdb852f8b..228ab4ec8bf 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -339,18 +339,20 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() wg.Add(2) go func() { defer wg.Done() + ctx, cancel := b.chStop.NewCtx() + defer cancel() // Retry indefinitely on failure backoff := utils.NewRedialBackoff() for { select { case <-time.After(backoff.Duration()): - if err := b.broadcaster.startInternal(); err != nil { + if err := b.broadcaster.startInternal(ctx); err != nil { logger.Criticalw(b.logger, "Failed to start Broadcaster", "err", err) b.SvcErrBuffer.Append(err) continue } return - case <-b.chStop: + case <-ctx.Done(): stopOnce.Do(func() { stopped = true }) return } diff --git a/core/chains/evm/txmgr/broadcaster_test.go b/core/chains/evm/txmgr/broadcaster_test.go index 26e78344b93..c6e05b0954b 100644 --- a/core/chains/evm/txmgr/broadcaster_test.go +++ b/core/chains/evm/txmgr/broadcaster_test.go @@ -124,9 +124,9 @@ func TestEthBroadcaster_Lifecycle(t *testing.T) { require.Error(t, eb.XXXTestCloseInternal()) // Can successfully startInternal a previously closed instance - require.NoError(t, eb.XXXTestStartInternal()) + require.NoError(t, eb.XXXTestStartInternal(ctx)) // Can't startInternal already started instance - require.Error(t, eb.XXXTestStartInternal()) + require.Error(t, eb.XXXTestStartInternal(ctx)) // Can successfully closeInternal again require.NoError(t, eb.XXXTestCloseInternal()) } From a2a97cc6a8051441a4f48658fefdf160a4a6f062 Mon Sep 17 00:00:00 2001 From: chainchad <96362174+chainchad@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:17:57 -0500 Subject: [PATCH 261/271] Use correct image name prefix for repo (#11465) --- .github/workflows/build-publish-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-publish-pr.yml b/.github/workflows/build-publish-pr.yml index b958295cf24..a7186ee5a11 100644 --- a/.github/workflows/build-publish-pr.yml +++ b/.github/workflows/build-publish-pr.yml @@ -29,7 +29,7 @@ jobs: aws-region: ${{ secrets.AWS_REGION }} sign-images: false ecr-hostname: ${{ secrets.AWS_SDLC_ECR_HOSTNAME }} - ecr-image-name: chainlink-untrusted + ecr-image-name: crib-chainlink-untrusted dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }} dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }} From 0e301e8a371bbb7cf44cd876775cdf509160cea6 Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Fri, 1 Dec 2023 17:54:33 -0500 Subject: [PATCH 262/271] Rename Functions Client i_router to i_functionsRouter (#11450) --- .../src/v0.8/functions/dev/v1_X/FunctionsClient.sol | 8 ++++---- contracts/src/v0.8/functions/dev/v1_X/Routable.sol | 10 +++++----- .../tests/v1_X/testhelpers/FunctionsClientHarness.sol | 2 +- .../v1_X/testhelpers/FunctionsClientTestHelper.sol | 10 +++++----- .../v1_X/testhelpers/FunctionsClientUpgradeHelper.sol | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/contracts/src/v0.8/functions/dev/v1_X/FunctionsClient.sol b/contracts/src/v0.8/functions/dev/v1_X/FunctionsClient.sol index 6d033d4b235..4aabef01f28 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/FunctionsClient.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/FunctionsClient.sol @@ -11,7 +11,7 @@ import {FunctionsRequest} from "./libraries/FunctionsRequest.sol"; abstract contract FunctionsClient is IFunctionsClient { using FunctionsRequest for FunctionsRequest.Request; - IFunctionsRouter internal immutable i_router; + IFunctionsRouter internal immutable i_functionsRouter; event RequestSent(bytes32 indexed id); event RequestFulfilled(bytes32 indexed id); @@ -19,7 +19,7 @@ abstract contract FunctionsClient is IFunctionsClient { error OnlyRouterCanFulfill(); constructor(address router) { - i_router = IFunctionsRouter(router); + i_functionsRouter = IFunctionsRouter(router); } /// @notice Sends a Chainlink Functions request @@ -33,7 +33,7 @@ abstract contract FunctionsClient is IFunctionsClient { uint32 callbackGasLimit, bytes32 donId ) internal returns (bytes32) { - bytes32 requestId = i_router.sendRequest( + bytes32 requestId = i_functionsRouter.sendRequest( subscriptionId, data, FunctionsRequest.REQUEST_DATA_VERSION, @@ -53,7 +53,7 @@ abstract contract FunctionsClient is IFunctionsClient { /// @inheritdoc IFunctionsClient function handleOracleFulfillment(bytes32 requestId, bytes memory response, bytes memory err) external override { - if (msg.sender != address(i_router)) { + if (msg.sender != address(i_functionsRouter)) { revert OnlyRouterCanFulfill(); } _fulfillRequest(requestId, response, err); diff --git a/contracts/src/v0.8/functions/dev/v1_X/Routable.sol b/contracts/src/v0.8/functions/dev/v1_X/Routable.sol index b50b1e603f9..92e23362f9e 100644 --- a/contracts/src/v0.8/functions/dev/v1_X/Routable.sol +++ b/contracts/src/v0.8/functions/dev/v1_X/Routable.sol @@ -8,7 +8,7 @@ import {IOwnableFunctionsRouter} from "./interfaces/IOwnableFunctionsRouter.sol" /// as the destinations to a route (id=>contract) on the Router. /// It provides a Router getter and modifiers. abstract contract Routable is ITypeAndVersion { - IOwnableFunctionsRouter private immutable i_router; + IOwnableFunctionsRouter private immutable i_functionsRouter; error RouterMustBeSet(); error OnlyCallableByRouter(); @@ -19,17 +19,17 @@ abstract contract Routable is ITypeAndVersion { if (router == address(0)) { revert RouterMustBeSet(); } - i_router = IOwnableFunctionsRouter(router); + i_functionsRouter = IOwnableFunctionsRouter(router); } /// @notice Return the Router function _getRouter() internal view returns (IOwnableFunctionsRouter router) { - return i_router; + return i_functionsRouter; } /// @notice Reverts if called by anyone other than the router. modifier onlyRouter() { - if (msg.sender != address(i_router)) { + if (msg.sender != address(i_functionsRouter)) { revert OnlyCallableByRouter(); } _; @@ -37,7 +37,7 @@ abstract contract Routable is ITypeAndVersion { /// @notice Reverts if called by anyone other than the router owner. modifier onlyRouterOwner() { - if (msg.sender != i_router.owner()) { + if (msg.sender != i_functionsRouter.owner()) { revert OnlyCallableByRouterOwner(); } _; diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol index ec3b5a65fea..f0cb3965b5f 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientHarness.sol @@ -10,7 +10,7 @@ contract FunctionsClientHarness is FunctionsClientUpgradeHelper { constructor(address router) FunctionsClientUpgradeHelper(router) {} function getRouter_HARNESS() external view returns (address) { - return address(i_router); + return address(i_functionsRouter); } function sendRequest_HARNESS( diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol index bc73544205e..c300f4d2b8a 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientTestHelper.sol @@ -64,7 +64,7 @@ contract FunctionsClientTestHelper is FunctionsClient { uint32 callbackGasLimit = 20_000; request._initializeRequestForInlineJavaScript(sourceCode); bytes memory requestData = FunctionsRequest._encodeCBOR(request); - requestId = i_router.sendRequestToProposed( + requestId = i_functionsRouter.sendRequestToProposed( subscriptionId, requestData, FunctionsRequest.REQUEST_DATA_VERSION, @@ -76,13 +76,13 @@ contract FunctionsClientTestHelper is FunctionsClient { } function acceptTermsOfService(address acceptor, address recipient, bytes32 r, bytes32 s, uint8 v) external { - bytes32 allowListId = i_router.getAllowListId(); - ITermsOfServiceAllowList allowList = ITermsOfServiceAllowList(i_router.getContractById(allowListId)); + bytes32 allowListId = i_functionsRouter.getAllowListId(); + ITermsOfServiceAllowList allowList = ITermsOfServiceAllowList(i_functionsRouter.getContractById(allowListId)); allowList.acceptTermsOfService(acceptor, recipient, r, s, v); } function acceptSubscriptionOwnerTransfer(uint64 subscriptionId) external { - IFunctionsSubscriptions(address(i_router)).acceptSubscriptionOwnerTransfer(subscriptionId); + IFunctionsSubscriptions(address(i_functionsRouter)).acceptSubscriptionOwnerTransfer(subscriptionId); } function _fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override { @@ -98,7 +98,7 @@ contract FunctionsClientTestHelper is FunctionsClient { sendSimpleRequestWithJavaScript("somedata", s_subscriptionId, s_donId, 20_000); } if (s_doInvalidReentrantOperation) { - IFunctionsSubscriptions(address(i_router)).cancelSubscription(s_subscriptionId, msg.sender); + IFunctionsSubscriptions(address(i_functionsRouter)).cancelSubscription(s_subscriptionId, msg.sender); } emit FulfillRequestInvoked(requestId, response, err); } diff --git a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientUpgradeHelper.sol b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientUpgradeHelper.sol index a52c3009927..e0f636ee89f 100644 --- a/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientUpgradeHelper.sol +++ b/contracts/src/v0.8/functions/tests/v1_X/testhelpers/FunctionsClientUpgradeHelper.sol @@ -82,7 +82,7 @@ contract FunctionsClientUpgradeHelper is FunctionsClient, ConfirmedOwner { uint32 callbackGasLimit, bytes32 donId ) internal returns (bytes32) { - bytes32 requestId = i_router.sendRequestToProposed( + bytes32 requestId = i_functionsRouter.sendRequestToProposed( subscriptionId, data, FunctionsRequest.REQUEST_DATA_VERSION, From 7f5c4325bbf2ea510485f27e07ffb1725d71220f Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:51:44 -0800 Subject: [PATCH 263/271] Bump version and update CHANGELOG for core v2.8.0 (#11419) * Bump version and update CHANGELOG for core v2.8.0 * Add section for Upcoming changes * Combine Add sections * Consolidating Added section * Remove duplicate text --- VERSION | 2 +- docs/CHANGELOG.md | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 860487ca19c..834f2629538 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.1 +2.8.0 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6af224157a7..62236558c4c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,8 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [dev] -### Fixed -- Fixed a bug that caused the Telemetry Manager to report incorrect health +... + +## 2.8.0 - UNRELEASED ### Added @@ -53,7 +54,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `mercury_cache_wait_count` `mercury_cache_miss_count` - Added new `EVM.OCR` TOML config fields `DeltaCOverride` and `DeltaCJitterOverride` for overriding the config DeltaC. - +- Mercury v0.2 has improved consensus around current block that uses the most recent 5 blocks instead of only the latest one +- Two new prom metrics for mercury, nops should consider adding alerting on these: + - `mercury_insufficient_blocks_count` + - `mercury_zero_blocks_count` + ### Changed - `PromReporter` no longer directly reads txm related status from the db, and instead uses the txStore API. @@ -64,16 +69,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `Optimism2` as a supported gas estimator mode -### Added - -- Mercury v0.2 has improved consensus around current block that uses the most recent 5 blocks instead of only the latest one -- Two new prom metrics for mercury, nops should consider adding alerting on these: - - `mercury_insufficient_blocks_count` - - `mercury_zero_blocks_count` - ### Fixed - Corrected Ethereum Sepolia `LinkContractAddress` to `0x779877A7B0D9E8603169DdbD7836e478b4624789` +- Fixed a bug that caused the Telemetry Manager to report incorrect health + +### Upcoming Required Configuration Changes +Starting in `v2.9.0`: +- `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` will no longer be allowed. Any TOML configuration that sets this fields will prevent the node from booting. These fields will be replaced by `[[TelemetryIngress.Endpoints]]` +- `P2P.V1` will no longer be supported and must not be set in TOML configuration in order to boot. Use `P2P.V2` instead. If you are using both, `V1` can simply be removed. ... From 33a890f01e49da091fa8ec148b74b28f5edc5494 Mon Sep 17 00:00:00 2001 From: Lei Date: Mon, 4 Dec 2023 11:27:30 -0800 Subject: [PATCH 264/271] Revert "make streams lookup modular (#11368)" (#11482) This reverts commit fa0f16ad0acf417db1186728560278a049357914. --- core/scripts/chaincli/handler/debug.go | 150 ++--- .../handler/mercury_lookup_handler.go | 534 ++++++++++++++++++ core/scripts/go.mod | 3 +- core/scripts/go.sum | 2 + .../v21/mercury/streams/streams.go | 143 +++-- .../v21/mercury/streams/streams_test.go | 20 +- 6 files changed, 666 insertions(+), 186 deletions(-) create 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 0075862d95d..fec8c6cd414 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -22,17 +22,12 @@ 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/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" "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" @@ -41,7 +36,12 @@ import ( const ( ConditionTrigger uint8 = iota LogTrigger + + blockNumber = "blockNumber" expectedTypeAndVersion = "KeeperRegistry 2.1.0" + feedIdHex = "feedIdHex" + feedIDs = "feedIDs" + timestamp = "timestamp" ) var packer = encoding.NewAbiPacker() @@ -125,8 +125,6 @@ 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 { @@ -179,8 +177,7 @@ 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)) - trigger = mustAutomationTrigger(txHash, logIndex, blockNum, receipt.BlockHash) - workID = mustUpkeepWorkID(upkeepID, trigger) + workID := mustUpkeepWorkID(upkeepID, blockNum, receipt.BlockHash, txHash, logIndex) message(fmt.Sprintf("workID computed: %s", hex.EncodeToString(workID[:]))) hasKey, err := keeperRegistry21.HasDedupKey(latestCallOpts, workID) if err != nil { @@ -232,82 +229,73 @@ 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) { - mc := &models.MercuryCredentials{k.cfg.MercuryLegacyURL, k.cfg.MercuryURL, k.cfg.MercuryID, 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) + // 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) 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))) - - 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() { + if streamsLookupErr.FeedParamKey == feedIdHex && streamsLookupErr.TimeParamKey == blockNumber { message("using mercury lookup v0.2") - // check if upkeep is allowed to use mercury v0.2 - _, _, _, allowed, err := streams.AllowedToUseMercury(latestCallOpts, upkeepID) + // handle v0.2 + cfg, err := keeperRegistry21.GetUpkeepPrivilegeConfig(triggerCallOpts, upkeepID) if err != nil { - failUnknown("failed to check if upkeep is allowed to use mercury", err) + 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 } if !allowed { resolveIneligible("upkeep reverted with StreamsLookup but is not allowed to access streams") } - } else if streamsLookup.IsMercuryV03() { + } else if streamsLookupErr.FeedParamKey != feedIDs || streamsLookupErr.TimeParamKey != timestamp { // handle v0.3 - message("using mercury lookup v0.3") - } else { resolveIneligible("upkeep reverted with StreamsLookup but the configuration is invalid") + } else { + message("using mercury lookup v0.3") } + 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) } - - // do mercury request - automationCheckResult := mustAutomationCheckResult(upkeepID, checkResult, trigger) - values, err := streams.DoMercuryRequest(ctx, streamsLookup, &automationCheckResult) - - if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { + 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 { resolveIneligible("upkeep used invalid revert data") } - if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { + if state == InvalidMercuryRequest { resolveIneligible("the mercury request data is invalid") } if err != nil { - resolveIneligible("failed to DoMercuryRequest") + failCheckConfig("failed to do mercury request ", err) } - - // do checkCallback - err = streams.CheckCallback(ctx, values, streamsLookup, &automationCheckResult) + callbackResult, err := keeperRegistry21.CheckCallback(triggerCallOpts, upkeepID, values, streamsLookup.extraData) 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 callbackResult.UpkeepFailureReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResult.UpkeepFailureReason)) } - upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData - // do tenderly simulations for checkCallback - rawCall, err := core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) + upkeepNeeded, performData = callbackResult.UpkeepNeeded, callbackResult.PerformData + // do tenderly simulations + 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) } @@ -329,23 +317,6 @@ 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 } @@ -399,27 +370,9 @@ func packTriggerData(log *types.Log, blockTime uint64) ([]byte, error) { return b, nil } -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 { +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 trigger := ocr2keepers.Trigger{ LogTriggerExtension: &ocr2keepers.LogTriggerExtension{ TxHash: txHash, @@ -428,7 +381,16 @@ func mustAutomationTrigger(txHash [32]byte, logIndex int64, blockNum uint64, blo BlockHash: blockHash, }, } - return trigger + 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 } func message(msg string) { @@ -440,11 +402,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("✅ %s: this upkeep is not currently elligible", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently eligible", nil, 0) + exit("❌ this upkeep is currently elligible", nil, 0) } func rerun(msg string, err error) { @@ -545,3 +507,5 @@ 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 new file mode 100644 index 00000000000..1bd4b2e183c --- /dev/null +++ b/core/scripts/chaincli/handler/mercury_lookup_handler.go @@ -0,0 +1,534 @@ +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 { + // 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 { + // 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 21f00df02de..c7af0541c12 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -7,6 +7,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../../ require ( github.com/ava-labs/coreth v0.12.1 + 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,6 +19,7 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/pelletier/go-toml/v2 v2.1.0 + 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 @@ -281,7 +283,6 @@ 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 36504924e16..7cea79eb76e 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -152,6 +152,8 @@ github.com/ava-labs/avalanchego v1.10.1 h1:lBeamJ1iNq+p2oKg2nAs+A65m8vhSDjkiTDbw github.com/ava-labs/avalanchego v1.10.1/go.mod h1:ZvSXWlbkUKlbk3BsWx29a+8eVHe/WBsOxh55BSGoeRk= github.com/ava-labs/coreth v0.12.1 h1:EWSkFGHGVUxmu1pnSK/2pdcxaAVHbGspHqO3Ag+i7sA= github.com/ava-labs/coreth v0.12.1/go.mod h1:/5x54QlIKjlPebkdzTA5ic9wXdejbWOnQosztkv9jxo= +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 cb9e2dd6752..aec23431921 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,6 +16,7 @@ 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" @@ -91,9 +92,8 @@ func NewStreamsLookup( // Lookup looks through check upkeep results looking for any that need off chain lookup func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckResult) []ocr2keepers.CheckResult { lookups := map[int]*mercury.StreamsLookup{} - for _, checkResult := range checkResults { - copyCheckResult := checkResult - s.buildResult(ctx, ©CheckResult, lookups) + for i, checkResult := range checkResults { + s.buildResult(ctx, i, checkResult, checkResults, lookups) } var wg sync.WaitGroup @@ -101,7 +101,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe wg.Add(1) func(i int, lookup *mercury.StreamsLookup) { s.threadCtrl.Go(func(ctx context.Context) { - s.doLookup(ctx, &wg, lookup, &checkResults[i]) + s.doLookup(ctx, &wg, lookup, i, checkResults) }) }(i, lookup) } @@ -112,7 +112,7 @@ func (s *streams) Lookup(ctx context.Context, checkResults []ocr2keepers.CheckRe } // buildResult checks if the upkeep is allowed by Mercury and builds a streams lookup request from the check result -func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { +func (s *streams) buildResult(ctx context.Context, i int, checkResult ocr2keepers.CheckResult, checkResults []ocr2keepers.CheckResult, lookups map[int]*mercury.StreamsLookup) { lookupLggr := s.lggr.With("where", "StreamsLookup") if checkResult.IneligibilityReason != uint8(mercury.MercuryUpkeepFailureReasonTargetCheckReverted) { // Streams Lookup only works when upkeep target check reverts @@ -129,7 +129,7 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec // Try to decode the revert error into streams lookup format. User upkeeps can revert with any reason, see if they // tried to call mercury - lookupLggr.Infof("at block %d upkeep %s trying to DecodeStreamsLookupRequest performData=%s", block, upkeepId, hexutil.Encode(checkResult.PerformData)) + 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) @@ -139,7 +139,7 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec streamsLookupResponse := &mercury.StreamsLookup{StreamsLookupError: streamsLookupErr} if len(streamsLookupResponse.Feeds) == 0 { - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) lookupLggr.Debugf("at block %s upkeep %s has empty feeds array", block, upkeepId) return } @@ -148,21 +148,21 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec 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) - checkResult.PipelineExecutionState = uint8(state) - checkResult.IneligibilityReason = uint8(reason) - checkResult.Retryable = retryable + checkResults[i].PipelineExecutionState = uint8(state) + checkResults[i].IneligibilityReason = uint8(reason) + checkResults[i].Retryable = retryable return } else if !allowed { lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonMercuryAccessNotAllowed) return } } else if streamsLookupResponse.IsMercuryVersionUnkown() { // if mercury version cannot be determined, set failure reason lookupLggr.Debugf("at block %d upkeep %s NOT allowed to query Mercury server", block, upkeepId) - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) + checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) return } @@ -171,103 +171,71 @@ func (s *streams) buildResult(ctx context.Context, checkResult *ocr2keepers.Chec // in the revert for mercury v0.2, which is denoted by time in the struct bc starting from v0.3, only timestamp will be supported streamsLookupResponse.Block = uint64(block.Int64()) lookupLggr.Infof("at block %d upkeep %s DecodeStreamsLookupRequest feedKey=%s timeKey=%s feeds=%v time=%s extraData=%s", block, upkeepId, streamsLookupResponse.FeedParamKey, streamsLookupResponse.TimeParamKey, streamsLookupResponse.Feeds, streamsLookupResponse.Time, hexutil.Encode(streamsLookupResponse.ExtraData)) - lookups[len(lookups)] = streamsLookupResponse + lookups[i] = streamsLookupResponse } -func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) { +func (s *streams) doLookup(ctx context.Context, wg *sync.WaitGroup, lookup *mercury.StreamsLookup, i int, checkResults []ocr2keepers.CheckResult) { defer wg.Done() - values, err := s.DoMercuryRequest(ctx, lookup, checkResult) - 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()) - } + 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 err := s.CheckCallback(ctx, values, lookup, checkResult); err != nil { - s.lggr.Errorf("at block %d upkeep %s requested time %s CheckCallback 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) } -} -func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) error { - payload, err := s.abi.Pack("checkCallback", lookup.UpkeepId, values, lookup.ExtraData) if err != nil { - s.lggr.Errorf("at block %d upkeep %s checkCallback packing err: %s", lookup.Block, lookup.UpkeepId, err.Error()) - checkResult.Retryable = false - checkResult.PipelineExecutionState = uint8(mercury.PackUnpackDecodeFailed) - return err + 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 } - var mercuryBytes hexutil.Bytes - args := map[string]interface{}{ - "to": s.registry.Address().Hex(), - "data": hexutil.Bytes(payload), + 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)) } - // 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 { + 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()) - checkResult.Retryable = true - checkResult.PipelineExecutionState = uint8(mercury.RpcFlakyFailure) - return err + checkResults[i].Retryable = retryable + checkResults[i].PipelineExecutionState = uint8(state) + return } - 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()) - checkResult.PipelineExecutionState = unpackCallBackState - return err + checkResults[i].PipelineExecutionState = unpackCallBackState + return } if failureReason == uint8(mercury.MercuryUpkeepFailureReasonMercuryCallbackReverted) { - checkResult.IneligibilityReason = 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 fmt.Errorf("at block %d upkeep %s requested time %s mercury callback reverts", lookup.Block, lookup.UpkeepId, lookup.Time) - + return } if !needed { - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonUpkeepNotNeeded) + 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 fmt.Errorf("at block %d upkeep %s requested time %s callback reports upkeep not needed", lookup.Block, lookup.UpkeepId, lookup.Time) + return } - checkResult.IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) - checkResult.Eligible = true - checkResult.PerformData = performData + 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 -} - -func (s *streams) DoMercuryRequest(ctx context.Context, lookup *mercury.StreamsLookup, checkResult *ocr2keepers.CheckResult) ([][]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(checkResult.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 { - 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()) - checkResult.Retryable = retryable - checkResult.RetryInterval = retryInterval - checkResult.PipelineExecutionState = uint8(state) - checkResult.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 +// 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 @@ -287,6 +255,7 @@ 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) } @@ -312,6 +281,26 @@ 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 2475244b4d0..abcc37dca18 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 @@ -126,7 +126,6 @@ func TestStreams_CheckCallback(t *testing.T) { tests := []struct { name string lookup *mercury.StreamsLookup - input []ocr2keepers.CheckResult values [][]byte statusCode int @@ -154,9 +153,6 @@ 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}, @@ -189,9 +185,6 @@ 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}, @@ -223,9 +216,6 @@ func TestStreams_CheckCallback(t *testing.T) { UpkeepId: upkeepId, Block: bn, }, - input: []ocr2keepers.CheckResult{ - {}, - }, values: values, statusCode: http.StatusOK, callbackResp: []byte{}, @@ -265,10 +255,10 @@ func TestStreams_CheckCallback(t *testing.T) { }).Once() s.client = client - 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) + 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) }) } } @@ -444,7 +434,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 dde004ea5184d8789e9c099f42a6f49dd72c4a00 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Thu, 21 Dec 2023 14:32:31 -0600 Subject: [PATCH 265/271] core/services/relay/evm: start RequestRoundTracker; report full health (#11643) * core/services/relay/evm: start RequestRoundTracker; report full health * Tests round requests and implicit changes separately * Add test to CI * Fixes other OCR2 checks --------- Co-authored-by: Adam Hamrick (cherry picked from commit 7236361119339369127a488a7c238745e4c44099) --- core/services/relay/evm/evm.go | 25 +++--- core/services/relay/evm/median.go | 23 ++++- integration-tests/actions/ocr2_helpers.go | 27 +++++- .../smoke/forwarders_ocr2_test.go | 4 +- integration-tests/smoke/ocr2_test.go | 88 +++++++++++++++++-- 5 files changed, 139 insertions(+), 28 deletions(-) diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index 088a69a2582..b3e22ec5dbc 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -12,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/jmoiron/sqlx" pkgerrors "github.com/pkg/errors" - "go.uber.org/multierr" "github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator" "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median" @@ -519,6 +518,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp return nil, err } return &medianProvider{ + lggr: lggr.Named("MedianProvider"), configWatcher: configWatcher, reportCodec: reportCodec, contractTransmitter: contractTransmitter, @@ -529,6 +529,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp var _ commontypes.MedianProvider = (*medianProvider)(nil) type medianProvider struct { + lggr logger.Logger configWatcher *configWatcher contractTransmitter ContractTransmitter reportCodec median.ReportCodec @@ -537,26 +538,22 @@ type medianProvider struct { ms services.MultiStart } -func (p *medianProvider) Name() string { - return "EVM.MedianProvider" -} +func (p *medianProvider) Name() string { return p.lggr.Name() } func (p *medianProvider) Start(ctx context.Context) error { - return p.ms.Start(ctx, p.configWatcher, p.contractTransmitter) + return p.ms.Start(ctx, p.configWatcher, p.contractTransmitter, p.medianContract) } -func (p *medianProvider) Close() error { - return p.ms.Close() -} +func (p *medianProvider) Close() error { return p.ms.Close() } -func (p *medianProvider) Ready() error { - return multierr.Combine(p.configWatcher.Ready(), p.contractTransmitter.Ready()) -} +func (p *medianProvider) Ready() error { return nil } func (p *medianProvider) HealthReport() map[string]error { - report := p.configWatcher.HealthReport() - services.CopyHealth(report, p.contractTransmitter.HealthReport()) - return report + hp := map[string]error{p.Name(): p.Ready()} + services.CopyHealth(hp, p.configWatcher.HealthReport()) + services.CopyHealth(hp, p.contractTransmitter.HealthReport()) + services.CopyHealth(hp, p.medianContract.HealthReport()) + return hp } func (p *medianProvider) ContractTransmitter() ocrtypes.ContractTransmitter { diff --git a/core/services/relay/evm/median.go b/core/services/relay/evm/median.go index db521a97208..3f86d6f8233 100644 --- a/core/services/relay/evm/median.go +++ b/core/services/relay/evm/median.go @@ -14,6 +14,8 @@ import ( "github.com/smartcontractkit/libocr/offchainreporting2plus/types" ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/smartcontractkit/chainlink-common/pkg/services" + "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm" offchain_aggregator_wrapper "github.com/smartcontractkit/chainlink/v2/core/internal/gethwrappers2/generated/offchainaggregator" "github.com/smartcontractkit/chainlink/v2/core/logger" @@ -22,12 +24,15 @@ import ( var _ median.MedianContract = &medianContract{} type medianContract struct { + services.StateMachine + lggr logger.Logger configTracker types.ContractConfigTracker contractCaller *ocr2aggregator.OCR2AggregatorCaller requestRoundTracker *RequestRoundTracker } func newMedianContract(configTracker types.ContractConfigTracker, contractAddress common.Address, chain legacyevm.Chain, specID int32, db *sqlx.DB, lggr logger.Logger) (*medianContract, error) { + lggr = lggr.Named("MedianContract") contract, err := offchain_aggregator_wrapper.NewOffchainAggregator(contractAddress, chain.Client()) if err != nil { return nil, errors.Wrap(err, "could not instantiate NewOffchainAggregator") @@ -44,6 +49,7 @@ func newMedianContract(configTracker types.ContractConfigTracker, contractAddres } return &medianContract{ + lggr: lggr, configTracker: configTracker, contractCaller: contractCaller, requestRoundTracker: NewRequestRoundTracker( @@ -60,13 +66,22 @@ func newMedianContract(configTracker types.ContractConfigTracker, contractAddres ), }, nil } - -func (oc *medianContract) Start() error { - return oc.requestRoundTracker.Start() +func (oc *medianContract) Start(context.Context) error { + return oc.StartOnce("MedianContract", func() error { + return oc.requestRoundTracker.Start() + }) } func (oc *medianContract) Close() error { - return oc.requestRoundTracker.Close() + return oc.StopOnce("MedianContract", func() error { + return oc.requestRoundTracker.Close() + }) +} + +func (oc *medianContract) Name() string { return oc.lggr.Name() } + +func (oc *medianContract) HealthReport() map[string]error { + return map[string]error{oc.Name(): oc.Ready()} } func (oc *medianContract) LatestTransmissionDetails(ctx context.Context) (ocrtypes.ConfigDigest, uint32, uint8, *big.Int, time.Time, error) { diff --git a/integration-tests/actions/ocr2_helpers.go b/integration-tests/actions/ocr2_helpers.go index 02ce73e813e..7b0700c3452 100644 --- a/integration-tests/actions/ocr2_helpers.go +++ b/integration-tests/actions/ocr2_helpers.go @@ -21,12 +21,13 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" - "github.com/smartcontractkit/chainlink/v2/core/store/models" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/contracts" + + "github.com/smartcontractkit/chainlink/v2/core/services/job" + "github.com/smartcontractkit/chainlink/v2/core/services/keystore/chaintype" + "github.com/smartcontractkit/chainlink/v2/core/store/models" ) // DeployOCRv2Contracts deploys a number of OCRv2 contracts and configures them with defaults @@ -379,3 +380,23 @@ func StartNewOCR2Round( } return nil } + +// WatchNewOCR2Round is the same as StartNewOCR2Round but does NOT explicitly request a new round +// as that can cause odd behavior in tandem with changing adapter values in OCR2 +func WatchNewOCR2Round( + roundNumber int64, + ocrInstances []contracts.OffchainAggregatorV2, + client blockchain.EVMClient, + timeout time.Duration, + logger zerolog.Logger, +) error { + for i := 0; i < len(ocrInstances); i++ { + ocrRound := contracts.NewOffchainAggregatorV2RoundConfirmer(ocrInstances[i], big.NewInt(roundNumber), timeout, logger) + client.AddHeaderEventSubscription(ocrInstances[i].Address(), ocrRound) + err := client.WaitForEvents() + if err != nil { + return fmt.Errorf("failed to wait for event subscriptions of OCR instance %d: %w", i+1, err) + } + } + return nil +} diff --git a/integration-tests/smoke/forwarders_ocr2_test.go b/integration-tests/smoke/forwarders_ocr2_test.go index c9fe3cb11d9..7b775cf437f 100644 --- a/integration-tests/smoke/forwarders_ocr2_test.go +++ b/integration-tests/smoke/forwarders_ocr2_test.go @@ -89,7 +89,7 @@ func TestForwarderOCR2Basic(t *testing.T) { err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, ocrInstances) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.StartNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) + err = actions.WatchNewOCR2Round(1, ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) answer, err := ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) @@ -100,7 +100,7 @@ func TestForwarderOCR2Basic(t *testing.T) { ocrRoundVal := (5 + i) % 10 err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, ocrRoundVal) require.NoError(t, err) - err = actions.StartNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) + err = actions.WatchNewOCR2Round(int64(i), ocrInstances, env.EVMClient, time.Minute*10, l) require.NoError(t, err) answer, err = ocrInstances[0].GetLatestAnswer(testcontext.Get(t)) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 0676ed03004..9b30ab497b0 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -11,6 +11,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/logging" "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/contracts" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" @@ -70,7 +71,7 @@ func TestOCRv2Basic(t *testing.T) { err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") @@ -81,7 +82,7 @@ func TestOCRv2Basic(t *testing.T) { err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) - err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) @@ -92,6 +93,83 @@ func TestOCRv2Basic(t *testing.T) { ) } +// Tests that just calling requestNewRound() will properly induce more rounds +func TestOCRv2Request(t *testing.T) { + t.Parallel() + l := logging.GetTestLogger(t) + + env, err := test_env.NewCLTestEnvBuilder(). + WithTestLogger(t). + WithGeth(). + WithMockAdapter(). + WithCLNodeConfig(node.NewConfig(node.NewBaseConfig(), + node.WithOCR2(), + node.WithP2Pv2(), + node.WithTracing(), + )). + WithCLNodes(6). + WithFunding(big.NewFloat(.1)). + WithStandardCleanup(). + Build() + require.NoError(t, err) + + env.ParallelTransactions(true) + + nodeClients := env.ClCluster.NodeAPIs() + bootstrapNode, workerNodes := nodeClients[0], nodeClients[1:] + + linkToken, err := env.ContractDeployer.DeployLinkTokenContract() + require.NoError(t, err, "Deploying Link Token Contract shouldn't fail") + + err = actions.FundChainlinkNodesLocal(workerNodes, env.EVMClient, big.NewFloat(.05)) + require.NoError(t, err, "Error funding Chainlink nodes") + + // Gather transmitters + var transmitters []string + for _, node := range workerNodes { + addr, err := node.PrimaryEthAddress() + if err != nil { + require.NoError(t, fmt.Errorf("error getting node's primary ETH address: %w", err)) + } + transmitters = append(transmitters, addr) + } + + ocrOffchainOptions := contracts.DefaultOffChainAggregatorOptions() + aggregatorContracts, err := actions.DeployOCRv2Contracts(1, linkToken, env.ContractDeployer, transmitters, env.EVMClient, ocrOffchainOptions) + require.NoError(t, err, "Error deploying OCRv2 aggregator contracts") + + err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 5, env.EVMClient.GetChainID().Uint64(), false) + require.NoError(t, err, "Error creating OCRv2 jobs") + + ocrv2Config, err := actions.BuildMedianOCR2ConfigLocal(workerNodes, ocrOffchainOptions) + require.NoError(t, err, "Error building OCRv2 config") + + err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) + require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") + + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), roundData.Answer.Int64(), + "Expected latest answer from OCR contract to be 5 but got %d", + roundData.Answer.Int64(), + ) + + // Keep the mockserver value the same and continually request new rounds + for round := 2; round <= 4; round++ { + err = actions.StartNewOCR2Round(int64(round), aggregatorContracts, env.EVMClient, time.Minute*5, l) + require.NoError(t, err, "Error starting new OCR2 round") + roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(int64(round))) + require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") + require.Equal(t, int64(5), roundData.Answer.Int64(), + "Expected round %d answer from OCR contract to be 5 but got %d", + round, + roundData.Answer.Int64(), + ) + } +} + func TestOCRv2JobReplacement(t *testing.T) { l := logging.GetTestLogger(t) @@ -144,7 +222,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.ConfigureOCRv2AggregatorContracts(env.EVMClient, ocrv2Config, aggregatorContracts) require.NoError(t, err, "Error configuring OCRv2 aggregator contracts") - err = actions.StartNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(1, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err, "Error starting new OCR2 round") roundData, err := aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(1)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") @@ -155,7 +233,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = env.MockAdapter.SetAdapterBasedIntValuePath("ocr2", []string{http.MethodGet, http.MethodPost}, 10) require.NoError(t, err) - err = actions.StartNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) + err = actions.WatchNewOCR2Round(2, aggregatorContracts, env.EVMClient, time.Minute*5, l) require.NoError(t, err) roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(2)) @@ -174,7 +252,7 @@ func TestOCRv2JobReplacement(t *testing.T) { err = actions.CreateOCRv2JobsLocal(aggregatorContracts, bootstrapNode, workerNodes, env.MockAdapter, "ocr2", 15, env.EVMClient.GetChainID().Uint64(), false) require.NoError(t, err, "Error creating OCRv2 jobs") - err = actions.StartNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) + err = actions.WatchNewOCR2Round(3, aggregatorContracts, env.EVMClient, time.Minute*3, l) require.NoError(t, err, "Error starting new OCR2 round") roundData, err = aggregatorContracts[0].GetRound(testcontext.Get(t), big.NewInt(3)) require.NoError(t, err, "Getting latest answer from OCR contract shouldn't fail") From ca2ec35dbd70780fa4292ce433ded39834da549e Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Fri, 19 Jan 2024 12:59:59 +0100 Subject: [PATCH 266/271] Document metrics renaming --- docs/CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 62236558c4c..5b34d36ec9b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -64,6 +64,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `PromReporter` no longer directly reads txm related status from the db, and instead uses the txStore API. - `L2Suggested` mode is now called `SuggestedPrice` - Console logs will now escape (non-whitespace) control characters +- Following EVM Pool metrics were renamed: + - `evm_pool_rpc_node_states` → `multi_node_states` + - `evm_pool_rpc_node_num_transitions_to_alive` → `pool_rpc_node_num_transitions_to_alive` + - `evm_pool_rpc_node_num_transitions_to_in_sync` → `pool_rpc_node_num_transitions_to_in_sync` + - `evm_pool_rpc_node_num_transitions_to_out_of_sync` → `pool_rpc_node_num_transitions_to_out_of_sync` + - `evm_pool_rpc_node_num_transitions_to_unreachable` → `pool_rpc_node_num_transitions_to_unreachable` + - `evm_pool_rpc_node_num_transitions_to_invalid_chain_id` → `pool_rpc_node_num_transitions_to_invalid_chain_id` + - `evm_pool_rpc_node_num_transitions_to_unusable` → `pool_rpc_node_num_transitions_to_unusable` + - `evm_pool_rpc_node_highest_seen_block` → `pool_rpc_node_highest_seen_block` + - `evm_pool_rpc_node_num_seen_blocks` → `pool_rpc_node_num_seen_blocks` + - `evm_pool_rpc_node_polls_total` → `pool_rpc_node_polls_total` + - `evm_pool_rpc_node_polls_failed` → `pool_rpc_node_polls_failed` + - `evm_pool_rpc_node_polls_success` → `pool_rpc_node_polls_success` ### Removed From 0d4f23243d0235e4430410d0e35d0b4f96f1e03f Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Mon, 22 Jan 2024 11:36:59 -0500 Subject: [PATCH 267/271] Hotfix attempt for Canary : v2.8.0-rc0 upgrade fails on Polygon. (#11828) (#11839) * Remove start lock + add logging * Add loggs * Update tracker.go * Update tracker.go * Update common/txmgr/txmgr.go Co-authored-by: Jordan Krage * Update logging --------- Co-authored-by: Jordan Krage (cherry picked from commit 6133df8a2a8b527155a8a822d2924d5ca4bfd122) --- common/txmgr/tracker.go | 18 +++++++++++++----- common/txmgr/txmgr.go | 2 ++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/common/txmgr/tracker.go b/common/txmgr/tracker.go index 1a24dd5b5fe..f143f639aa8 100644 --- a/common/txmgr/tracker.go +++ b/common/txmgr/tracker.go @@ -92,19 +92,22 @@ func NewTracker[ } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(_ context.Context) (err error) { - tr.lock.Lock() - defer tr.lock.Unlock() + tr.lggr.Info("Abandoned transaction tracking enabled") return tr.StartOnce("Tracker", func() error { return tr.startInternal() }) } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) startInternal() (err error) { + tr.lock.Lock() + defer tr.lock.Unlock() + tr.ctx, tr.ctxCancel = context.WithCancel(context.Background()) if err := tr.setEnabledAddresses(); err != nil { return fmt.Errorf("failed to set enabled addresses: %w", err) } + tr.lggr.Info("Enabled addresses set") if err := tr.trackAbandonedTxes(tr.ctx); err != nil { return fmt.Errorf("failed to track abandoned txes: %w", err) @@ -112,9 +115,11 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) startIntern tr.isStarted = true if len(tr.txCache) == 0 { - tr.lggr.Infow("no abandoned txes found, skipping runLoop") + tr.lggr.Info("no abandoned txes found, skipping runLoop") return nil } + + tr.lggr.Infof("%d abandoned txes found, starting runLoop", len(tr.txCache)) tr.wg.Add(1) go tr.runLoop() return nil @@ -129,7 +134,7 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() err } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) closeInternal() error { - tr.lggr.Infow("stopping tracker") + tr.lggr.Info("stopping tracker") if !tr.isStarted { return fmt.Errorf("tracker not started") } @@ -159,7 +164,7 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() { } } case <-ttlExceeded.C: - tr.lggr.Infow("ttl exceeded") + tr.lggr.Info("ttl exceeded") tr.MarkAllTxesFatal(tr.ctx) return case <-tr.ctx.Done(): @@ -211,6 +216,7 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) trackAbando return fmt.Errorf("tracker already started") } + tr.lggr.Info("Retrieving non fatal transactions from txStore") nonFatalTxes, err := tr.txStore.GetNonFatalTransactions(ctx, tr.chainID) if err != nil { return fmt.Errorf("failed to get non fatal txes from txStore: %w", err) @@ -239,6 +245,8 @@ func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) HandleTxesB } func (tr *Tracker[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) handleTxesByState(ctx context.Context, blockHeight int64) error { + tr.lggr.Info("Handling transactions by state") + for id, atx := range tr.txCache { tx, err := tr.txStore.GetTxByID(ctx, atx.id) if err != nil { diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index 228ab4ec8bf..fb8e5fbd401 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -189,10 +189,12 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return fmt.Errorf("Txm: Estimator failed to start: %w", err) } + b.logger.Info("Txm starting tracker") if err := ms.Start(ctx, b.tracker); err != nil { return fmt.Errorf("Txm: Tracker failed to start: %w", err) } + b.logger.Info("Txm starting runLoop") b.wg.Add(1) go b.runLoop() <-b.chSubbed From 238ca388acccce41b0f6027cc3b879dd6fb1d76a Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 23 Jan 2024 12:49:37 -0500 Subject: [PATCH 268/271] Temporarily disable tracker (#11857) * Disable tracker * Remove all tracker calls (cherry picked from commit 5057899e96a1b914ca4b785eacf898827ab742fe) --- common/txmgr/resender.go | 3 ++- common/txmgr/txmgr.go | 9 ++++++++- core/chains/evm/txmgr/tracker_test.go | 4 ++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/common/txmgr/resender.go b/common/txmgr/resender.go index 06c466e1730..d93f20095f1 100644 --- a/common/txmgr/resender.go +++ b/common/txmgr/resender.go @@ -140,7 +140,8 @@ func (er *Resender[CHAIN_ID, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) resendUnco return fmt.Errorf("Resender failed getting enabled keys for chain %s: %w", er.chainID.String(), err) } - resendAddresses = append(resendAddresses, er.tracker.GetAbandonedAddresses()...) + // Tracker currently disabled for BCI-2638; refactor required + // resendAddresses = append(resendAddresses, er.tracker.GetAbandonedAddresses()...) ageThreshold := er.txConfig.ResendAfterThreshold() maxInFlightTransactions := er.txConfig.MaxInFlight() diff --git a/common/txmgr/txmgr.go b/common/txmgr/txmgr.go index fb8e5fbd401..de96ca0ff05 100644 --- a/common/txmgr/txmgr.go +++ b/common/txmgr/txmgr.go @@ -189,10 +189,12 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Start(ctx return fmt.Errorf("Txm: Estimator failed to start: %w", err) } + /* Tracker currently disabled for BCI-2638; refactor required b.logger.Info("Txm starting tracker") if err := ms.Start(ctx, b.tracker); err != nil { return fmt.Errorf("Txm: Tracker failed to start: %w", err) } + */ b.logger.Info("Txm starting runLoop") b.wg.Add(1) @@ -272,9 +274,11 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) Close() (m merr = errors.Join(merr, fmt.Errorf("Txm: failed to close TxAttemptBuilder: %w", err)) } + /* Tracker currently disabled for BCI-2638; refactor required if err := b.tracker.Close(); err != nil { merr = errors.Join(merr, fmt.Errorf("Txm: failed to close Tracker: %w", err)) } + */ return nil }) @@ -389,7 +393,8 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() b.broadcaster.Trigger(address) case head := <-b.chHeads: b.confirmer.mb.Deliver(head) - b.tracker.mb.Deliver(head.BlockNumber()) + // Tracker currently disabled for BCI-2638; refactor required + // b.tracker.mb.Deliver(head.BlockNumber()) case reset := <-b.reset: // This check prevents the weird edge-case where you can select // into this block after chStop has already been closed and the @@ -417,10 +422,12 @@ func (b *Txm[CHAIN_ID, HEAD, ADDR, TX_HASH, BLOCK_HASH, R, SEQ, FEE]) runLoop() if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { b.logger.Errorw(fmt.Sprintf("Failed to Close Confirmer: %v", err), "err", err) } + /* Tracker currently disabled for BCI-2638; refactor required err = b.tracker.Close() if err != nil && (!errors.Is(err, services.ErrAlreadyStopped) || !errors.Is(err, services.ErrCannotStopUnstarted)) { b.logger.Errorw(fmt.Sprintf("Failed to Close Tracker: %v", err), "err", err) } + */ return case <-keysChanged: // This check prevents the weird edge-case where you can select diff --git a/core/chains/evm/txmgr/tracker_test.go b/core/chains/evm/txmgr/tracker_test.go index a31187f04e8..af41aaeffa5 100644 --- a/core/chains/evm/txmgr/tracker_test.go +++ b/core/chains/evm/txmgr/tracker_test.go @@ -49,6 +49,7 @@ func containsID(txes []*txmgr.Tx, id int64) bool { } func TestEvmTracker_Initialization(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") t.Parallel() tracker, _, _, _ := newTestEvmTrackerSetup(t) @@ -65,6 +66,7 @@ func TestEvmTracker_Initialization(t *testing.T) { } func TestEvmTracker_AddressTracking(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") t.Parallel() t.Run("track abandoned addresses", func(t *testing.T) { @@ -94,6 +96,7 @@ func TestEvmTracker_AddressTracking(t *testing.T) { }) t.Run("stop tracking finalized tx", func(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") tracker, txStore, _, _ := newTestEvmTrackerSetup(t) confirmedAddr := cltest.MustGenerateRandomKey(t).Address _ = mustInsertConfirmedEthTxWithReceipt(t, txStore, confirmedAddr, 123, 1) @@ -118,6 +121,7 @@ func TestEvmTracker_AddressTracking(t *testing.T) { } func TestEvmTracker_ExceedingTTL(t *testing.T) { + t.Skip("BCI-2638 tracker disabled") t.Parallel() t.Run("confirmed but unfinalized transaction still tracked", func(t *testing.T) { From 773a2393d45148f19f13ce44efdd75abe26e4f06 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 23 Jan 2024 13:41:54 -0500 Subject: [PATCH 269/271] Update CHANGELOG.md --- docs/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5b34d36ec9b..57702efce31 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Disabled tracker component for hotfix. This feature will be re-enabled in a future release. - Added a tracker component to the txmgr for tracking and gracefully handling abandoned transactions. Abandoned transactions occur when a fromAddress is removed from the keystore by a node operator. The tracker gives abandoned transactions a chance to be finalized on chain, or marks them as fatal_error if they are not finalized within a specified time to live (default 6hrs). - Added distributed tracing in the OpenTelemetry trace format to the node, currently focused at the LOOPP Plugin development effort. This includes a new set of `Tracing` TOML configurations. The default for collecting traces is off - you must explicitly enable traces and setup a valid OpenTelemetry collector. Refer to `.github/tracing/README.md` for more details. - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. From 7deb0ce98c3903b6f841db87f5afe79778de4708 Mon Sep 17 00:00:00 2001 From: Dylan Tinianov Date: Tue, 23 Jan 2024 14:14:37 -0500 Subject: [PATCH 270/271] Update docs/CHANGELOG.md Co-authored-by: Jordan Krage --- docs/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 57702efce31..92ff603228a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,8 +15,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Disabled tracker component for hotfix. This feature will be re-enabled in a future release. -- Added a tracker component to the txmgr for tracking and gracefully handling abandoned transactions. Abandoned transactions occur when a fromAddress is removed from the keystore by a node operator. The tracker gives abandoned transactions a chance to be finalized on chain, or marks them as fatal_error if they are not finalized within a specified time to live (default 6hrs). - Added distributed tracing in the OpenTelemetry trace format to the node, currently focused at the LOOPP Plugin development effort. This includes a new set of `Tracing` TOML configurations. The default for collecting traces is off - you must explicitly enable traces and setup a valid OpenTelemetry collector. Refer to `.github/tracing/README.md` for more details. - Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`. - New prom metrics for mercury transmit queue: From 04fbe42af1f8389ba215826a4c07f2acc86d9844 Mon Sep 17 00:00:00 2001 From: Sneha Agnihotri <180277+snehaagni@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:26:57 -0800 Subject: [PATCH 271/271] Finalize date on changelog for 2.8.0 (#11881) --- docs/CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 92ff603228a..4d3a02c0eae 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,7 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ... -## 2.8.0 - UNRELEASED + + +## 2.8.0 - 2024-01-24 ### Added @@ -91,9 +93,11 @@ Starting in `v2.9.0`: - `TelemetryIngress.URL` and `TelemetryIngress.ServerPubKey` will no longer be allowed. Any TOML configuration that sets this fields will prevent the node from booting. These fields will be replaced by `[[TelemetryIngress.Endpoints]]` - `P2P.V1` will no longer be supported and must not be set in TOML configuration in order to boot. Use `P2P.V2` instead. If you are using both, `V1` can simply be removed. -... +## 2.7.2 - 2023-12-14 - +### Fixed + +- Fixed a bug that caused nodes without OCR or OCR2 enabled to fail config validation if `P2P.V2` was not explicitly disabled. With this fix, NOPs will not have to make changes to their config. ## 2.7.1 - 2023-11-21

S*n=hVDw^envM(;P<`2}eMwkS_%=_P29;Ql1+ge?#4 zPSx2e+%R3zlZxK9Z!-8m{KN>93Xq@3aiH0Se|L7OR^MwOz7Jc0xaZ1B@HJ_b{UEoQ zCaSbLNa#9tdC8Dezo9V0%gYt(TUMDUpc;k`icYb4jL+5dpGyuRP_Nf^?8bTvkt%i= zy8S@w&>RMA;3wQCqg&l-PtiY1o%LtFdf)RZ6DDFecaItTFEOB}I}K>^m;nW;X{!kk zR1c_ig-23u=~p!RvxhcG(HyR$-rhz)?N+PZUfF}sVe#OGVBM-j;fvV!fAgU~`P84V z^HF{$n_*$@Ko29099U6B?Z`?c+*ui9CWY7ceD1mk;`%l&#t__8G~xDsL`C(apf8!= z^l7O;^?f}?7`_eM)%Hoa{Pgtn^5pf|yO$@YG}s&!p^i>oyIE9@K4s76;f1Vj}o#F#ziDnoOZ1sXZEi3 zJ@vgQsQ!PhzO0a4>x%%ZTPY0_$v;48!s87lQN!8Vc8TfNak@<@w5)~=c#oO=h{ez= z#CQ!95j8ry2NFLhTJfAEnc@{AZPj{|pf!!6n`~YnIV&uqNMaeG4A37(xNQME+C*%4 zOGeMZ$NG2I!PEyXjuv$xKzEz9|9TsK`UM)E6gIpG^Kjcbt%X#tig=Nd3v;$PK1=5q z2p*=KmqlR$osj$~84#&>t<(+TV8FydPZI}Q)#lc<;`5uD04Mn(t;2#I(V)(Dmc-DE zx3i=qzufXguvzb}16Q}Y9G9B!-+BxK7jvJ*=xi*p@GpQ*Cy3GNl(6y}@ekw7a|Gq-ufS?M}1Cbu*{d ztT!0BGCfD{MHuo~kmMq_yy+CV%(Tde>au_)7Zh-l%*dE9Hi@RUsORCa1lw7O@Sr?| z;#r0{0hL2nET^C3)FhF3B`TNE8O-T>ERt5>3}kRUGnlBY|l1B@z! zvyIBZh$H;lQGd9BLm1aI#oTac3QP>>7@_Wk&YMl4HHYqlykDf)1uK)58iKIPLcC z05)}}>nNn}R!%tN=ru$gnS%Eok)+aSbGUlWzb7uJ5-8#75~UVZHwi-M;(HNRcdg6z zlwn0b!}e0LzICdSf=%q_7~q6KZQ?fyXpLc@Aat}Sh~*kD$RaE^+At!Ah#^#&ZbL2c z5m1Y1tKi_M3LH9kq@OUtkoJF=6uCdC!p^Yb6F$LD-O$l zrNpaS#meqglEgcLV`*cm^h?FRPE>B5Q$@`;^gylUScKg7k>Wc@;677y{)#-xYAj(P zXOd49ll)%c13pvEX)h%L&~tjL4fTDk@SxsGOq6%-OC1d8S65N{3-^10cm-tcN66e^5nzvf6 z>%LtqkUxJV$Ug`4@)nWROQplmqeQBsXzU+~_^Lc0IsvfleVbbsd}a-e-TXzJkF78@ z;iezvT!`S#`MV1@w)agRgTYpr!ZL?27o#l!TK61U^$RA?i(KD>ODDOyeOqDJbQKnB zx58U(KiQbAaex1c3_=C4PXsFomBaLkOWV&1lI8l9FiL|-;EypOi#SF!Wf2(*v_2@9-0CgdLNIyBwh$DPmVAadiLd0i@9 zVM+Rf_H_5dRM%Pe}z0v&$Qv_H&kQe>L zj4VsUEJv7V%IZz(e!c08$Ou$Hs7Rxzu_X1tWbB92@9nUuzJwaZ1R$ z!07!^akQcx^#3(5&DhQH1=FA(sIq95o7xzWhXSn|!Xf&cu}6Iyz6||fudotKgV+P| z=LPgqArFU3d;b?i6mm!B1`uG`zpyZqHfXBQk^d-_UYW*w!8tTxR5BRc8}X+(66aa) zMyo7@A*4>ckvH~jNqop0{0TFRrtVXnP+t#zs`xYXD3q-2o%(VN-|qP%^BC6%07(}oIfedC$5S@ubGg>5;Q&NN_b_wTXSV&`>G+b*lz_h zPN@-p$GnhS#-wUpZ}Nwdmt*9BDRl$V)O_s)pOTf)e8U42nK3b@?}nE6mWy9yGaptw zAEkMmMNr&utvJSWuq08mULwtJ97^71ayn+ct)`&LG%nPcy&z5e>BJC8kbK-|?Z<zc?X0bfg>ilrKHZTdqX0puTWIHyA~bpxpC+zdfr3v#p^OX03 zrGflKH*P^xKM*BmxWbjANWwvs7P!q84i_g6I~4H9A)kswe~Jfl1TWOls9pQ+55RR1 z1RuuMj-}#eQ%@J!os=HJzr-|yz+DrDP(;Cv#209|6@UW7o1t^?loPls2by2S&UeM2 zFKx^i{9Pdjf(i63bQAn|l)M#fV!a<(+Q_U>QvvDpJ#ed4Yo29&irn|qERgU}yEfn3 z2A-}WVryy7H2j}$SK#cp;}K%srbsH(Uo4rkuF%-@8R}}o{)!1lxd+%(;DA%_bumq# z7R*3|nwLs*DCjF|ndqjtyV#p+8mW&qcg|#b-@?a-CQL@L7|u|7_dsU&6*C$MJ@?mD zdoC5iMyNoo{Vor7lJc=+ogvA^PBr_$wvS~nw(NUECq>kFX2Lk*M~Mc$?J-zN>w$v( zh}i@;TYeU`7$Qg{Hv7b^4bD=AVpAfujObVDg|Ta0J|S#LRdZv+X3( z-WM+KJ6?O$thu-YKiud%h%>KTZ7#%8d= zs;DO$*^Z~d74el+adLYjFpYQqWJ(5igTNjL2U)_D&?OM!SNwNZl^5OCD9c-+H-zz{ z;(8kd7IH3$FvDp!$oM@_8yKRuoeH=ur`B+~f2Q%=Op&S3vXMl^VaXm@?@aQ&Bl!-8 z3Vslie3Dl?QSe{GB?rZ4KVQ_EUO0Ehi^cUWJv?XoO!K9d(s5-m=6#%6T=!cGWvUYj zfu56=ba&Ki1RyI*Wr?mZDCb}_nS@#uF1svPtlHC72cJcVIk{_QdR~PO(4E@)9XmaD z#O?Zq+wi9*o@s1*2L9A9tR>H(iI64=cazu;2Rn&z5vDYN`~uET_8#&-C21kHV*)bZ zAn2_vr7&vwIV8wa{mS$3! zm_V71fLX!>O7f((>%`7B`*W_&bWh;5qDvNLLp8ZVAagGuZK0+D>#-8D71$CBo^E!w z_f$(@P{|GXEzO`>qij)FYq7L>BKNhh7ww1WKSEX`>D>vcDHl^PSb~ECwmS944=dGs zYv(`g5VygJuQJKG@zuNa?ZIcNH#I~r<_@+)1*4rDzH|?jcB8YEziXTLT908ML~YHl zkkwd0%#2omV>j?Wjr;Yr#fgkeII;-Y#z+mewgRr8jMi?Q3Herz_lTH{5m|DHS z#H3OP!#<)MQgDfDRNKiEua9U3I926(xgMid@anaf6q7@WNMcN?Xm+}tT?nDT8}5Pm z^?<({sVp`FS>gNnt#-4yZH_aK(g!{p-G$$wOBCC1Z)~S?U2ITf#au#mr1R~bE5!Sd z*9jB>>FHpsy=W-)0VI3sovPCN^X#zRZEl4f(Izm$Pm0t@%yT3U6jsoO$)>qW)@7`j zNBK=Oh&j5qrNW(hvs>Sh;|RaN+ys}>t621u?-j{!oKH<;HgClM#(6txVqe2by~YF@ zIVMoQu!rslrn^`H(7-h$JC|oBE`bEdbi`&tsPuEIKXfN%)mjD^A~0l-_WKAgJ*?ct zL5j--EFd0;06DzEn7%}Y9(*H4#sP{9ZZyH;_iog=*P!}6L z!bGA}$*0U~MEmzstr|S1!yF}Uu{A^T&-MDozwhRR+|`zwZ_h2xHZfPR3;9=ipIBnJpu#WM>idMwQERl; z@tYn5#^AUYD>>%{dc{bCxL}G%r^%r)INs@)awb+=JbyzI8rx;NvwY8XsOHB^p>JHt z&CrS;Qn%4AE#JGnmz$z@ls*D;MqE-@uG6mL9_>A5k3#zl?$Tn9T%CNJ+c)m1HfA3Y zxpHuH{?8A|hw=Wu`CI8p`N%!~cmKS=EBg79<3sz*J@gK*>{smP4Oz)f%`?P=I9 zkuf4iV03R;UMiPkP)(vR4Z_S{!UAVh49eMT8s`SZD?`cEo^e4Z&_?_a&=00Y%A_kimhW)*UAoAQT&RKvy8ks$^S#~7dEbttr^OOZJ ztE?I@nPDJrUD^fP2W@;_Dh*2|KeuV^y3ZxJEk0+?7mFOq?Q@GZVW79840*tiw$cbi z|0*#M;olnER_LrZ<_1fk0o!-=EODr2#U|@qxoO^HK>LdYAw|Fqk7yYCpx2k~A3I+{==JwUX|OOS%^&T~ecAYQ4mj z`kX?+Jv1y(#*b4bu3WQ>Z8!FF%Qn&36{tyAGsxD6G2Aw?A>63OwjsrgDzSIxJ~y8c za6Eh~pa)o$iF`#Nbe*U!ac9`2zy@wD6q^z=vkf*NAPOQS-wq6O(6fx*t=R0jmu98C zY-zvFF#r>X7>0JS$js*$hWG#)un0^)_b-4e==Xp;@v6%r*50}!^AsIns{OZOlmA`c zdTqWve@BVyZgRx;+F%7|$>ii9FHg3;Tu;jP|)3J|F-78ZZ>%2F&JOifYbY?2r85 z){kvo3S9VC7R3J8u$ElU?e3mXqEyCf?X({P zsb;?MOu$f1huF7KLA-l%Z>W8wO=bvlb);<0Z!D!0e_~xq>J-f#=IRDB4}rip6;M3B z9s>rguiJ>U#xlWaMa=kYdgCnWuc9|r{GX<`c56c>4iHO`7A@sIsE98Y1dMFlv4I62 zo_nK%ZX*M+S^`j64}%#H)SpVQQyhPE)S7M%9T%{x{jzEELR13>7dDy3m1F@cvs`&dow@1j>dbeY=69mY2%=<@M+VqL|WPNKGGBHLlLLo$YBqWip4Hs>~IH6;EM4l`}o9!)T3`LChJQ zXBVJo6wPvworlniQ$AJMQq@BpjY$f|(4dWqF+742)xMkc&B?NW9biGg#xN ze)`v8jXTYnvwIXlqgEF*QE$}pFi(O8)3WlR=^lqov06g51^AHE0r6Uk{A~jgh zcV5jpjYg-lH7VI@V#5NmL?HY|4HqEe|NBq>*Z*TwJOn~q?$q3j0J#fxdlQq>B2WDy zbBM)+JBH~!j@>GOAH^~&07406$8IRBy{S-`GGWnfw3~nR@C}128aw#in3&b3r$ER= z!I%dJc5B_gylT@RykV8YqixlrCvz37cAdYz@1p6L)ejD~RWDbyHU?zxEWmY;eN;vS zV7aYYw0S%B-$p^0KHZv9StJzgY>R7MO-djUB{t8vI*m~oI8J^F(reITWhOvWUk!1?Ks|PU0_+)C^uCGni09+f>;B0ee$r=AUA)A;QV zv2a2`VTBY<>k*&U7>KlK^Sd~2E5S!7Mi97K=F7NuZIZ`AjJ-?2xxx@U7z=+7%*zBk zfmt|$1A*cVSJ7XG4Pv+U0Oal$pz;agrxBS?@hc<}*)&CNeO@iNiSyw#Sp{n$bsA;#+ZEo8Kk*PZi^4G%j)vpSM zQB?g^_$9fyG@uT!%Pnd$G&A99|L18 zH$kIo+3DY$YOPTQ`K*TC$-*0?@+%Z(3UY>fwVfv$V}_n$hLK{HiR6pBkj;K#f@nIL zW@C&f!d+2*QiuTZA0mn1CM{hN+a-TgzUgMCU2pu=(*c+@FgM;RcD#kV1o>68)9!2^ z4Y72ROqEamCPTcKn%ZhoJT$NB-pkaFy%c!^;zwhs)(C!8s8(Y2Bfc2fvU#~EQ{8zw zu@jylk-&s-OnRg{tg15z8zj>Q0^esJ2wZNmlx}_?F$&rLlkPmgowTdVp6P>X5yC=Y z^LNY2#Lr*xg-iF+5(~*+KHO5A%S&^`TEcbgPZV=*)!REyy;`H&ZLH0`Vwr_|`i+!M zJXL`66SbALvVX?+;O0tc9?HG2nTkieBB`f|opq~d?q114ckYmHk)hoYch7`rKy>4Y zdkKtAQRJz6OB8tnzltJHEc@fSc7Eo5B#VUo3PmpQ8T*a9zyAtlA6+?uK9^6Jhm}KA z%%2wTLAXAZ+Rs@_R5CASMv0h@f-s_(p&Lt&OsfDKUty<`Q6%=0Y?>-}hEVY2GgZpc z{$5A<;c4BiPpA6qJF#Q`3iNFtcs>nNn{V5Ob`}P>X7plhGT4{vc7_|3ADjoMu-X() zzrG00VP7VD1fR^Y`_$CIz|fL|Tk}N|S%$zY;gD5%z;>(>Ge-AE+_sR{jE^J7s1W zsQC0DL(E5PUFeR5?-MH0n*3}SJO}m)_hw*Eu+`Rb7hd-Uc$}C_y-j@>>1pSgwUC)0S*+Ure=t%1 z@bpd@`1wsXbRe;Eja#2s|6We(f4?`5d@eCzT6H)I4;W$X5vlqdsQ8UAbiU#?*ho*} zmvaIEW^jIoqm~K7`3I za6In^&sd28I(`RGACp|Z?fb>z4)?e|E*973NBhoxMhIIE2ywM`6?ylHnUpUdYf>Do zFPY-=xRP|+ttM@{aPJ4Z*m3<_-#}xUWg?@UL>Eg}!1UZ#xzGjPAZAYF+9EWacS~x) zqoiK0pj&X?C22ODGJHc^=29CIFXGrvbOyIKQ8ASJ3FktVL_VN*ICz0zHT^`JO4Yv5 z1g=pJm}B5Cvx}p{61{Kn_}?K^hS*yF`h0%j4%wC_?lkKKBATM7UxB3=n8UnWUTSde z^8tRPcfGtIpTJhLwzCzq4Nq6m+hxpMaJrq=Zt@L5^^8x#@iyDbqh#<--EnG@utn&3vcWb%}4(M4px3;LG7}4b26)T&F-;NvWC_j678|05X1)c_thXZDFbv@ple z47&RS!;!d@np*Zv8J$4MKzVAPSmitD_mlFiEbpXzFQ?1t(NOuc=r2NzZdimCte~x3 z*Jd3dSNUD@ZJ|VQl}t^871K0du_tOqjOF6Zyd+uraWxy}iu8xdZas6i1MB-ekqHCZ z2&3~=73TvgT+-*La%kgaR1^32;q3hS0^})tjGdUI64Mj+Zaygh(@@~CzJwN7-Cbvo zoL{IXxI@t=uV=K4rR^_m_{AMDLnANat0V8&U^`>76YS!Q&$k2PE2D=s?5_$pzIsY1 z49XVn3-QK>R<4OR!5|RRfxsBJ*9Xdw95q|}O2ED{lKDKYDOjQ~O}WV%hummc=|LiIOt&glmm@*;5l1 zL;Z?n-&vOY;3+zT-T*H}S?=S}rAV^&5xqafLuXL7dE#E4f4o39IfPh(=@0AX3F}K3 z3dw1zh`R(l*JLe!=ANHjpcC21OU*a- zE9?YGGvAj=&sagr477|CZS8k)(90SB*|tdw?<_+I=d z^}_pd>P50ay^xDA+twnUix?sv(dj5RZ#ecLb;#6>6%F=51qVt2YL4}3z{Ya|<4NVo zMNxYQ6pEF%rc4Ee2{1ufWWq?2X$JLnt@W4tM3ZF?AOFw2F_ubDmBle&?+hTE>Y zuZSe~t*nTgo)Tr1T+vqfIyqHTHc`vqSo#pI#tlfj=c>~^dK}XPgtbntk+-gvmKc_> zdBGBfzN?1_0LILV{w6Q6bc1IY-<>tQp-2>{j8ZksRK=(7Vgaiuo&&Ff;~_hCF4#gx zZ*ExTWZbW7j0{}Dhr^Nh)}cAHo*Y_G&SKHncMWtdQ}?K82aYAH02R_IiVt}7zI*`7 zsHppufrbBT59oI-Q{6ds;GVoy8OR%hf+gAZC9fjCNyYAWGDy})M%n3Zl-gPXTd1%%Uis#(*?0xtIFULjb z(7vTrf+Dr3B$+mStnGGf|RCX9mIqI9h-N^>+gN>er{Sb_$Dg_%(y?M6kk8?Dps zZ~#Qv!x#h%p7QVZ=6LC!zy~m28Kc~?VHv&1zQ=tb+1mk3%0Qo#m$^xaSH?n4$?q~u z%sYE*4bI0GJ$P13MWrmm#nU74ZXYwvDxw??s=ZU{laKZpl{6fTr2^L*>r&_$%pu&4 z7@1lL*y7V30_e%wp&QGIbqdS**gmyqEJO%(^czOEqWlw^R7C!o*5bVz*@{;nPQE&F zg@kqhXDkIUFYE05)y1YVKxK75ihRF&s~;!rlw>ny9sRK6rbxS0?K+lezzegJaBSchdxK5i1{nZGoj<$lb_9PMRzEy?7hGMZ zy@(~`+=^jcSZ}o_qSu_ZJ9e%+J^h&4v)vR{|L^??uYS9(<^9^R`dETi`yI&w-y*ue zgWl!_vW!cOhL#b#+pg7{&`Vx&AC9E!3Cn@yv_B&k4v%S%RABiEo|YcS-enoXrmj~x zy`K#uvXB)+r%y?UH^w|>4DSWu-c-X=S}ddlIW?_{_9VCLO)$oJylyRB!**~)QRsZD zg-a?GXJ>xzit0m;B%y=tP)U>Oo?8$<|FSuGTF5C6#?`e2%sC)Nk+{GZlE z`Kv=%#jovLcRv`q#(IQ&ZsDOJ4H9!wJx7tK=){p&)ri zw#d_Y?0aME2!q^39c2L29@gelr`D|PzUV7p2rbJ8mkx7z9&tW7bq?Ck?KD$0_sFdc z#aL$E;nR?rcbqPFYvUHAp-NvhQ=7I|3PJ2vt?u^{H-+#ZI7Z&3N>z3uU@8c+pYj?q z$o{5-75}KvSk7czDWdh0k1oUO=Z<$zRX7{s^+VSRy#7`VeV<>lPI4Grf2-SeI9@;7 z??kx%Ruzp}?_4o5_OB{G!3*WA_cNs*sCH|>o}&1~8(rI9yU&0yg7Mf74Fv7y6Uwl^ zbqzTa4f`wiHB71KEwJAJA?e}S-`p4Ey!Q?KD(AiTmi-eChCToG!v4+;IODT_1lh&- z`{|q4hRwGyf(dGj|8hS9v-}0ror(Pq_Z0sA-F*dre{jDCd4S%<jyZ~zZ1FJj&`|l$FEc}6nh+jnOA3wVe zrenmnPfDdx0R%^T-@P+`K~OnDe<=<<|3$fna(}h<(yCx21pc)K#amPVGJhiYHHoqI z6EIc#7yb~2=3`9#Po>f?_#gt{^nS^M>MJz2A27`Q&ioFVB~_8%K`2)(D11Mdk4j!1e zU-mWVbpH9*7w7Q>!Wgh40tpGB0U<`c$XvtsW)%^Pmxx)<-d!xPf4;WwO~?L(peM3` zDrPC*`*Bw97k;z`Id6TMD}`^yFi-7#m{DJ>$?>u=A`u}0tYxn1=Z)! zo-lZX#lC_HuR)V(^7|dGv;N4udI2L$-eF(J&Rl-^Uc~=xRBXn;z)M!gJL-py{qB4B~mgIJ{ ze^D+!=^HK{L~ly$I{b9V#*RQRG&*4@Da#coc+n^SFlQ7q5Ni9zKZCZdKZ&36sY);D zmxo*sK54ju7%w5~u{lucKg@LC`#tuNIQNmT;3L@0DSqiEo18Zo@}`ONk@9vBlu8Ek z?5Syf=dOp&>mt~9>pmQe->P^LO^D*i@#_*{sicgPIe9QI14>hd`<#)P-Z({C9JUz2 z;&EO}x?zVffwGz|Scj zUfVbLGq>MXiZdo8=4)P*jmg-4Aujw#X>_G6+Pf$zMvKK$=q_Bu35()oaw2Do0jwjB zF1{jJt^kJb3v|;=Nt)lmH-0uRO+U7Bo%N1Q8Ku{~UkkpCd z0g;wbhdu}>CREwIU^buntM{B`t}`}e$K2O*L$h=yJ%4JiAjtxy<*s0gc7J4V#6I=M zJAQma)@-2T{AD)waAUqx!8zdwW(4IWTKkqLv|yg#J3|hNYX8U;u%WvT%yG27>H3{1 zH75pbZ0x%~n#ffb3**z0&OUiPf2p0bn$st8#t0^;uA;>cxj~d<*EL{YnJNfW9QZ0| zH%@=eHthASfbo09Sv`UhBH+W+-Z%HhCh|w6QlrbahOdYqQ{(}+mgcA^goPlX;@nmi z`pvy{(LGFJEyqqS3 z1L{a?U0GD2K?w9*t+qEjVla42Wm;1ArS}?{ZyE(pa5obAM5jQ#mD$Wlr)_qNp1?hG z3{7pd8U^=rS`a}8H=LrU*y?lU5h6jW+AK0k0Sg{Qb7aX>qeiFo07e4lVHU6_Yy&1| zoQ^9VvbXWmIQH)QAPL7DorD-L3_bE@Iz7n{E#5FSBcDfH_tJLbx zwQB#?kMAGZ40@+f;0Uy;@9M2iNxq%x&Qw%$YMtg|dmb~*7T1_F4^eN;p#>|}tk#>4 z>{0j>thjt5q1tXdmde@tMU);0H<8_YSK(lD^@!}v&I305qDF;d=Hd0&e&_FfNpfj7 z>W$`SG~hGp{w1s3XgFPM1#!BaMjbvwNvrF%?Jz_$q3l-U&y`)T zbsDY5b~iWHJ+jByTlwmKW^Z>p&I3f!s&^2YI*<)mtJoI~@#aU?>8Q>>-In@Fk~H8^-L>)h8$IN7tU-`l3q*NL}4^!had2 zPV3K+eXFU#r{qfhvy-*c)<`o~a_7$#+-=pJ?IX!|F%-=+gMVUOXwm>}$HG)E_D`|Lcz7{z_W$u$l3 z4bTd%37t-D|FLu28(Ft3aA?2@cSKnB#|UXe6)Q5TOjjPqq-F-TsOaHQ(Zjr!_R!v+ zkIe9p=)2nYj^e)MyXm=yNx#s-1jh#l zA)bX7uHRRtAK(*>R_!zF4xL7`E8$jYUp<$ZB;WSY=C)7sDK*fkc+eiF^F($?~*GD z_HF{bizlo06hU{P%S8gk%xN=Jg-KzeOA(ra9vhhW6(Ob)w0n+#7I{i^L4Bj8JtF+Z zM-U)irx05Bk*on<`LdRlPOY)~ypb+1ayTVs(@KnQw^47^n9B<~iSUS~dbK88USL_T z@etom6(`>H3TezYlg1EaNEAVx7JGDtz!XCzI6}|HseJ{a9-QxJDRju_o^J8l&)S>CL zkp37c;|a|`Scv=n5uEm6Rkc#r|69YR<+kZG4}3euL;s-mBrVtJ`HI2!^Wa& z9hfQo;wEFRa10cH;xN1r_ZeiOAao#lK=)YL;>XPCgR)O|7K>^x+;>Ba@k{sJbl~)= zEYk;zO>qv><7%no9EL|#xQ9<2hs$MujwBuz*>yRq%SG$^h-VKB?^g8jOy(-K`k1nc zQ*mO38t^CU>zMU)3Z0R{LX%=w&|VyF)bTqu#t4V@J&J4cDSW`iM>Sz*|8}vsB6rwo z&H0RFh+{-Xif1N|V++~P!wbb3o)wIZWNaCl!!=I@6z~Y526}3kO(uS9!@+&(znM&6 zxhiK*!phe_pPiguzCC$&dGh_qtCQDfc3_&CcSnrg+2{*#kXc;F=ssXrELK0YwL-Fa zT-0*xA=|7+j&S8jqwgai2A?}xPSt8MaS1##H?g@ghjTK*FtH_uNErDtij_bfV>aNr zBkyagm#nKZwzuwTb?(@;)ykUIzRPMtcdfPCxc!%BOp(U?@=vpBv4WNs!8%l(T|~c4 zy}OF`Ktrfo1R~di-c=P^EGTagjR_x(Olqz<@C5F&|B2;{Fq6$1J#&_3^Lyk*vgCa? z!KhK?Ig!xn+^IDS3@mI3`Ew?gTGy#>DEd#Eg=)~v#YO)~yHEq>W&O*S%rdIAJGI)* zc-j&Iv=|)s@So=&#@@l?;!|z;)nNcsq^>mZ%4*uTEcBC+H}^Ne-;$_D0Pu!E@;v8< zQ&xr1L=RzXXiiL}M54nHFXJ4kL5e7}#gJ*oHN zZ28R((UF25GIhnWuW~6-77KBo*9RS(Bv^|6fkO+m&kM9L2tP zH3s*&)1Iu<6=txMmAYT4R4&Vwa9CNZRw%0#s``Z6>vOl_+|m=zvz(f^PfHA9^*Pyc zxUi(aiF&8;fPSuJKw)zk7fht;zclwrr=>vuK*Dg^Rp#KL`^YqFHP9L1*+$3NeFf2X z+28r(`OZY@TCJXQh(79+@uLKXKRmdx;0LdwaCEzG#EAa%qIBjn3ya=+SNY3z~r~aJt={Q@^yFdWWl= zFqG}4*yDS+5rq2m^?iZI>uxpguQnL^k6II%1}~eu=KRJskV+ONrR6SvL0l4AvBsA% z4>>`fkgDWsd6A_pkn*cW5{tbbA4PqOy!=-Y%0Mjh&tvp)gcVHlYYeqw1>otv>qg6h z&eZd$pZT--)C28$xHe(&`xwyy;)N<(LRfouGS8TFZbSb{=>#8vh`#TLfam%{8H@B> zdPD_W0Vh-V3P=Kv3hnR5i-p?N1L^Jj0#>-hq;~3<2I`p|;}SiYKn!|7SZw+VNj4o+ zd+7l=BnR5YqJe5{qg8tVlm5@OHT7o?H5DKEVl9 zIWdcDT*Ot-z$H8t6uM&UC(-oQHv^EuLjR+{tBMKvzLH-1p;^e8eT$H2Fq})`vU5;* zTgQiAT0MT<;n&YYp68)#`^-7wV!2##&wiSV=2ju&u`hI4jCF-V{P9~ah>J3Dpe*T` zpG1p$ViQ9pwP1M5AcgFltfATNwD};j*v^$GaTc+ek)g67Ec`s`6*s~1Zo~E@7yQF` z|HNAMCtARC25Ue@NO2zi+ACdS(QP9a15_$ydStcCUdoEdJ&TwSMKQ-C=;7RldJ_im z)@pnXX}0UtDtDQAfJ!x!D+to^Da6p@FiBdMVP@ft6s(Bd4&j-Z7lhF9g!@!T*;rU1 zS;yuE!bU|z6>MrJ2L~`La9_SEXrB}OQsg{_wj*cz zEX7Qw>(fEjx#v-251SP*jJwBrgJRO2Y zmnL{=lDjm*OGL`k)d*X6<-^u`Psti^i_*pg!_yaxd~@`6P_WeqSA=MX6(M6QLblps zGIBvF%l`DU<|61+ue^u8QFXEPPBfkWTqzb(+nN^VrGCT-hJl9`@1> zQ?}EpZ%cO2sCJs|^;xQUJh2IzsR>J1w4EmI3!s6&?bI6Fa|0URt~1vSCJkOwb~gk= zvghu24XB9RHFAx$XS%zT?j6a7MPFg)Twi2pn`uiwDm@!FlZx{FFfXatMbC%1LXJ>4 z%M@^!Q7FHqMXC=6sX_@#wSaRocBi;QLtsKMGufLxJez4WC!bFT6z= zhj-MjJ(y1ZY@XiliNV-I_=3GK+5=HN4ZP_djlTiYC_wi+DH4WKB#Z|z?|@K%fAN(^ z*3mZ;1VmYT+rHjJ`UV&EHVm zTC1*f`dwPLu{Ip9WW7R;4t^-N0M^p0=L7Hmw$;6y`z4QOG_wH+LBg^9GtAJ;QCE>R5g{zG4&V@-b z6Oybz<`Ra*PnO$QgDrU^8F0Y+Stj07qx5|49Tp$_V=BcFGgzhwYs{x zy1Fh+yQ;?ZLEl^C99sK1GrvB*=Y_c=Ak>b3+ugQ5Sk#cOLNRVV$WSJO9<kqqlF4&)=LJetdTF{v$T^<{i9z z{Pf}E@FlsoswQa!XOynkH}`hGR$46ZObF5GXIYOv|-jyg5N@s}Jl0qoY z8f(6gKa6b&%AXO#)d^8^d5^%S`=zntC8e=jdyi0a`6dXpNHUwlr$y+L$EI`{bP@r* zm74M1S7MVC4ka04kqOXVqfQruIqGj%01{>;GnnOzcPX!E+44N8C|^#jNo&bcx9PJo zO_KbYu|%XGvNd0mn=en&%qc?FH3d>a!5=W$#A7~BHGNCAl#jPf(H`fW9ag2(as{jJ zc>d)|(vQZ2?urt%gyM|c8NjJou$Xqy5YWR>b~0x(BSMQnk`VsXp3U>F};%aTbbE2bs?{lJ~=MGp~Jm2KJ;9*g#4)|LzS>Ks67; zun5YALcH;^|dlvQG^ zftg(oFe_~XVy)JpRBSt;%L&D%^9z-w^W^ri^2f}|PZ(3|?ri>BLAppH_yo@2v3%zC zc5SoyEzUEv>?Av|#ImEsHG6ikO(CQbBH}75g;6~81|_CkoCzs$@Wh21KKcxEpG0xI z>=~xf+}YfH@?Ixg!Jtph<$FCZS)2__oNa;;p>c|~TmATBb!qA=Gf>@LS!nMsOb#Z5__Q)=hb}!IDPwZXw=FSt^HMxep zy`4?PHBe~mvBrYOb!_h3I(w&-2rck=Pg;V}`SN6@96`rg)4AZiGaC2*6BG>{$hfP{HybjmZ!Wp;QIj+G~6rZc2otYl`j7?X4%{rgC*z#A=PL zNc$&av&QNqTQt4l$6vYsptf%9@4$;oK6xZo+&BMv(o-GtLCh*Fke%&sHmgsVN*L$+ zUeP=z56oj|gtvxiOuvdy06VbT_}{`t!08_Ly5v8IkI>wMBH)i3*|{Q zWjR<(Gpjgx{l440PxWLMhP8Pgsg8PrM`-SDZ8yIeFu#s_TS8?}@^1waxL|>{AjN=0 zk0~Y&hG?v$^zQ=Kvz@@@fQ=ptS-J!w#t+NoVAVY&k`w5>Rcki?Q~2jOVcq`-|NNDw z>&?w4$w_wWTbqxA)_BxK8|e5=nD96i(vqF6`Zu9XPv}S?_ajbZ#S5Z(5b@U%Y2_TmO6UZy*t^}G zohNOsPnp1M;x@OpGs(bQLn@>=G^7HJ=UYnqCUx}(-f*-?$uL0jBc!-; zt0Fa~_+pmLXr>*KQJQxXipTOZ4e z{#s~LsyIs2w#|4^4|Y5mcb){ziiBpxvi=*v34r_;z%!@_Xy$;qRo`yzF5f3u4zw&_ zc{m29OKs=OjYRR%7HjA}G>yG(;Dz@ui>;l;?t{zXzDC4S2n0PcJX}9;U$s+fNN2XF z3nMuJ8F6^*mX0~^@-BK!?qZM+-yN4rE3bH{ZdV3wLUDw9@HdMi>?0@t4}wlY6@2*k z?v-V0ZCs`1(f6+Sinmh2Or@1xz#)wo9}ixD@!?{lsQ!cXXRgkqBPbE=)@N9O!+&Vh zH+O)$1vUo3FlKWXJiVOPEE&4js@Ha!JDYGeXs%hDdhA+tfcI8?tFgJYE|SXiMsst^ zHrlZ5jjSY-wmUPrB)M72#r)0y;}mK8a+_MET@?i%b9-vnbYQyDC#ymVz|z>;TORe3 z;3Dh2ru3M>Z)EvIASJ@-T==kXrOOveSY1RUwrK#3dt+Bjx zPey1XMxj}&(Peh&O$gWnoJlwyb~qH_qnIOaq3BBe>FOD_zHtNH~GSnvH(g9=L zR(|Of;vG+5Xg6k+R_GcYaO*X^V*v+J?+Kf2l2d8Fyby-;7?z9W=n(Gk78$=GWeyWr+_v%X0epnKa(>r89| z^PU&a&qU_L?UU}c`Jo8pvgqm^`>BJyNKSF9(cJwTdNL=Oo9zb0kWs!@mp3eYL>CGq zRI{Csv1z0US*@|PylMwIPNINU+vKlx*6+L6OUIc{P(QO(7-a60q+d|7Tj*xaM*Eox z+$ggz?qtIAAw_Cv*z7`^HC$yo^u)bD;~O}NTv2FXn_atCJPXV zjz^@kY2WI1xu9F9c$#XPd;rrBFQ%qbc3k9>Gz)cF6S#@8rnlx{mt zB$tA12#eIz8Y4FOEumrP13Y3qX$9tNesvtm>N~t`ut5X!1I)04E@b3;S@sRG{YS~Z)SK(V3eDRBHMzu*bbZ3}cy3d@kPs4}`bnjc_o-WRM zDWEN62YtHj&tR7?+p&TI9Z_0~D3|7v7CcI06iZ1MldU|9dQM?zu?Ij^7L8JM ztrhR57<4Q)gO0^!(6PMFAmds|m5xQc6FFk6YHrZ2nc=_ydpR_oEVfCUvER*>iE|{} zwwenE_~M;rX5N5So_mc3anX%EL!8;%+}_?5u6K8qHq+VJ-BP|9>y52uUaYGeQW#8R zTBsEmRD1g3!pjg#QO!(qIDc?Fh=YaW(RUNGqiGh6rg4VLCn1j^q;9)xlPS~~`&k3gw-~ghGM1Td#AM+Qv3Izt1 zxgF}>b;WINsvLHzC_oD@Dv(; zB520Npz&uwv$dHi05@K|<&A@AQl!Ocy>wf*11Gz^RTKO6XhF6Ru6AVLwwRO*86 zEg$X3)MUdjTsgLk!f)Jn+wOI{kGFaOZgP3MqI(T<9_-&!4Rh+vt>q2SAEOK?H30oW zU7m-B6JQuT9u{g;WqN<8z&92Od|0JT0g{=JCeY&L*#J#71_}^k3B=J%zmI4`Bpje8wL43|OsL-%h zUD!8w_x?kW_1|i`A{FmuQI$W^^~UCQt+xFrTNMIBv8@X5bpLroGze3S0ZxE>BpD?d znhLv@fXJzdxeI71YK5(>1qFn4bF04mE#wolo*xE9@-j#;=4JY0u`DsK`i$dNq%z43 zR@y?WQUIE4*m>)gdr9P8{So(|k3km0k;RiBbUf|=(>7xpBA_!ipu!oDUpS7Xcm>25H0d$NOp8-2Gb1->*fv*pZjt z>@2-JD;QF|*&zlcN~)Wi zyE3ky3^JZm>Q}5 zREHZLfSCr%tS$*l-M*ghv?|JyS4=9cbp;MweZ6#RdRdn$*tMx+aSTcUWSYscWgRAx z$s)Hn@DF$-aS(d|8su;q(J-EBW$p(ilsr|-Y8LrJ9vODsO6a#^1_a*OZtgTuopq;? zU9h08;Lg?#S*sZ8+ z=Hw}x-`ag(TQoXv1W(@HZq^=+SSrjh`}!El83s}qWxCRZ|CSyJ#czH3 z649|-i)KP1xn#U_Pi`vRw=6%MvMuZdCrt0}vIIM$rd+?WsfXG^{T^W*+qQ{%zkaq(w<#%_G zfL0-~P9j6oEQjOKF)p765-CA?kjwVc`+K{$wYRye;=MJtrD74xg3psAKZtzi!{~&T z!z(VA0BopZeiZmHfnIW1BnDVpL!Y(iI~KiwwbLqs1^q>EMSr%om+bcLZoS@=USrAF z5xTu`bo}OUrTRy8Ll`G|h@A!ddtQOOSg-a#$R4GuIsKJ2>={;uTd`@GQ>5vS!4iMW z0?gSaX$$PBlajhnF~Ud`NdyL*1VbK;Q)evPE6G=wGC&Tzr-W6ezNvRxIDOt;Ae~f; zyO6>QX%Pi9+EXzB>##{kv5~NSqllZkHFWpjX9A2tCaUOVmUP`LzE3}sc#cSUb{i|} zWRj_2W{Y=`i9oKh_*oOug{#*$(Ogd7+?{7G=nplKI)*b(smJNe$}aKcBL{Q^PTXcU z?&aFrrJ)~xg5O_rugl{Ez8|@VIRf|IEq(g<_83;0-~Zsn-Y_Y#XYTv5;jH+rJD>&n z0{&)lmiIP$lS_DJvzPA1ALWe;_Rc+J@ZTBxP=v7WPI;vcl+6+k?ni!ccr&s}meqEC zIQ;{yk$Y#ijVa7+nU_mbyTnQt0mimsAKe9!eY+fG9-ltGd4WN?LO|}!Y$#J8<3RfU zEVKcXCw%KAE3K4J)U(;Tzjo6Q2x)RFY}u)s>WV&CVWncTkEWk1_M^LTy7~{x{_nH9 z9~w0P)bH+XX|?2(erVKTkpG~k9eA?nX&0VsdfJ1h|Hh}CLwJI+@C=Y)&x_#u`zb%0 zvQI^ycvt`?-RB?KbAa({ckT){wbiyGW6xBR(I7~z(pm`@RITOh2g*>$#`uA!Ub%c` z>j-ET-VVzmZV7EX$k8SG9d!Ogt@eJiLMorzytFA$Wnaafi} z@lp~kW-DpVQ0(}U+F683pC)pMMxyvIzeiczJ;$$vwxMgG zfufH`UJ(#!mGDtTW+7mT7)e&-TAphKkiBEYD(;aeWoO|pCG->5n@wBQDZ+{U9HIfC z<9^D!D+yBU(C6YlPJnWyQL!JQX4VG&)hJyeeHX}@v#z=7PFt-U-OJ@V*Lzfw9$y?RK9Sz ze6i-N$Qmjlzzb=J8|tNOj@%e?N>@cyjV*W`Cr(>AwydD7dUgs)ReL1IS41Lqr8WWj zFAOye3r~YHTr8-0(iWu4$J>+Tv0e$EByS>Po0p zUob6C5->^+b|iQH{F=U;o=K?a4#CLnGy_bCw!$=Hlqk?r--k?D-%I`ms7R2&_A&#M zRmH=u)~CopK8%P)>u8g&&;uA{-3JeE-y_kH#6?U52C>!}XWQKvfI>SStS;FjI2c%7 zJ5X3Nu2c|jfUw{(LsIO-wp$+FhGr-qNqi^E#XN!~QJgySH&bkJw*$Fu#SlZ-9Rs~l zw!(d76V(c#`^3MbkcGnuF+6Lf`(Y~;$QPWZ1m^>ezd$3^pi&0e?N%q|-sLnE*bpf9 z)zg{o7X@@O!r9Hue2iwZkJ4qyuihBr9EIe6)E4k&DQ$hi1p>Rw8S&&U#$JgXP&o(@ zQ}R$|?Z`uKZTIIloS?AV+%a^DwhTYBBldH~NnnB&LF9M{BXnEA>VXA3yLLh`L!T_z zAjKjgoVClqXq7+dC~1aAL&1iv;n?PSI<}BeC=A89inqx#=L+XnD6{IVtreUpPXtc? zlH>AdkJrZf#2R&??gTfSkrmn)G1OMx*x*}VoN?O1S%rSL$!7b+CXbL9RWt<n3Me{}=nqaC31}j8NnfPc-QKg&pVeIwW!D5e+xO;}oSx zFQ#}|y+|1c4YPN4n^IgL#iJxJ@G>_$krEgrnaoBgm((_QrK+D0BMdUpKra&wpp>9d z-$KyWHla}=ERYg{i%cGIMXB4(9U&}`@`!6UsR97Vx6TY8 z*rD*dL!e;C?u3r2IPFJqkWNY{G*GFD z-{(V)G6S8|kMK_LZI*!3RAMjS@cXRt8C{KSus87XN4H$5>J*e6bT`;LcjI5Rn~i{d zK+j~g$`1M^gLD7JzaTq2{V5!lJ*)hPQUXlDL-ilEKhi(KKjM||e%NsK58D5A`o|xC zoc-$#>is?UdG_mbs%~6^|0?*WQ9CQ&%_e*gC-qNQ4OMamZ?B8}`mlf&^(n5TjENS9 zlrvO?R*8xvSL#56Fo^FLbgnHpZ3c<9);)XH0!jt|bwk0qLskDIqDYY~JIImzDCC1F zR3$BzY+=tN1J|Endk-5zJ*(E(cH)3Jug8Qly-BRKhDxI*Rf?O;=f4W*vO)KgRfmX0I{ z%1{@KVT_(6vh47|bL$xkbWn#@Ymha_Ih^y{cLFGZ-L7vtyFd>V8@21YUc2rzfS_X& zZeT~SwJKPyTJ;)c_~=l+56+*qswnG3<|>nRz=N!kvZ$kl+kFs@`BZSeA@RhUYioasX~POonpfXmPs@7N zAeGgDo?|t@HdAv|qd7o<@b1p)RosyI2isZS<@D-42LeFxK~0Rg3S)+kNC@KnO=HYu z^3U}$>y%k#>!sP*Tf*^tUt#x<^dPVU*VE`)!vqC0SQqO-*~69s1A(aZ@u`320%ok^ zLpfkp!v*lgcXylUbYX4H7d_lu)h-z1fnatf5M-l+BV_&;+ofZaOt54#yd} zeq{iNwbQDRkFn^W*0K{9yTst9QaOX?K{*1#!SJC{iO$@lrJC4X%@9QC{Gwd1INAYC zOz758=0;&~-I|nd?zTKwFerWEr`}l`fQA0wQWs;A2DXtOPI}5O1qnVqo0ZE!reB>e zmrhLyTz;JqhDoQ6nV%)mT|Ci|Ck?<`I>e>uLyg3!{D%gz9l%Lhd;=e~1X^7$bF;~=6MVl;=xxgWSC-JN(Jc}a2| z#a$?r78F_)+y31hkZCUrqx3m{_S{SGtPeF{x5<;MXcA(#3gFWDMvQ0+zsp!XZNx6@%#w$;G4 z5Cc$<4o_|M@Etv1+d<)d(#Ru!S^DUWi(UX{RRibluD*&}#wzqHJ%!zKl}4P3y&9_a zislTr92Y0+hX$udSXpnQYaYLVNuXj;VKFL4-gI;MZBdi1wW_KW>FN!tO>iahG5z}V zJD}el{dVcMProDj?a=R#elO_vihd{bdriOR^m|CZ$Mkzizc=)ILcd4!`+mk72$Sj- z*~J7vlB4S(WIKR*jZk#g!Ji@g2r(FJ&kFdMCBuF3rZ8W;3Fa18Abf!njDCixMd1ps z!f7;~sN`PkUEncSz0WQV?1_0n_#8M%3=3~0eZnNzZ;10A?7Lb@XWu5*0bVvnu6H^B zHp{;Cq1F|iILP?vr4!k9x9mpw83xesvEKZ(4EcQv(hXLKcJ7v_b}Xy4-l%#38_IwP zk@VDze-UL08`OI*E!{})Vo({LTUK2pOkF!86!R%tE5m{#?gDr{{?`Tt8Sy%E3zNZAjYwNkl@l1~g251U*!yMMa?`gXCeoy}gL zI0w_O+~oA)OrPwpUY7YtwDuGA$wh}Gexj3L?gI0=D>L2h`(2k*b-+q9s=oMHZ&y> zf&O%a6zGwC7TB2U_Oq63`@X%`*k8x)Yv8!LnVUdNR0BO`YP-rhP2$g}4Xjtqfm4&X z5AD!75doq%y7^w++tdWuX_sFq5rPTU34-%wjs-GgbB}=;;FsQi-xAv48WFg7ZHQM; zxPqUr4=7GZRzxmG0rzX*oDEi_jWBku2}`ENPLGV(aq&93&TUHqUDzybf#TPi7Esc2 zXMT5<3s*|N=BEHT*W#Yy3JfjLFL?%Rn+&4iuJW#^{s#(AL1 zc2SLv#2?*JQTNYwzVZ`CV_jR8DE%Xwqqc0a?2^K-YS2t=mK^;mTX2dxlxy}V>z>Le zO8;WpQ()>agu}C9d9y2{m+@yz-U$awnnA)TX($w=j;&Z~c~TWq!?X1&UOLe2$4U?; zsTcYra(-r~?xhuEGK0|N@2wO?J{am@KEhZ?vTMqfu!t@=SXz{=CCrV`ML>2VPLWK` z0dR-b*lzjNc}-I*5kLp}nkg}v*I4qqE2Y_E$vaJ%hq0ro{h0S${z9mM#;C~^#lb}Y z9e!h^k-CE)4P5@xNY!Tm{?5#+&H(&_kxHwRf1F7XrT;WbBRj%>%+;6nMZVvSQ~@QI zKc7pJkmH}sw!8%LzE&wHb$sjcXNK7Uf8ow)J6{^w&hmgA8Ukp*!Hjta>=Pu2d6Fpw zG?Q{w=!|3w@LpLVI?QT{)M>^IaL~4VSWp`qB}S|+(*Cj=maQ1CD$20~B(4lc6`1g{ z`zzoalLbAI6Ek_R_7rtXWinJqMdsxl?FjXtBjpYPh;%uU8$OhAa5EJxB(kb@shsF$ z$1?G|Qh5vw0A$}R4M_wO?ZBNu)WN>mh7kjWV|ZqznxT-Gnb|Q&Q89KzUBM`sbt>r= zLKsF~{#9-ec3Iul(aldeL~uH?3dUa+TmWCHs20|s;SzU+BnwsD_!mI9~YM_=m(Y5?#`h2 z@Ar=hCpEkCm_tdtUvN?rCDR;ALIcki_fMPzRPCrHUgo0Er~4+@ARmzR_2T-~5;mIn zPB-zrTjUHHJ(@e56n$ak&ji@XYbuhO0j%b+#ow}9B+w53-r_F`?EWvciuh3I9^+=j zMC71(|5~x#Iz+u4W1M!;mXgXX1+_bA$x-`nY^JsWdVjgNKP~D8b_wy2@dT?<%Yq8O zyLT{sp-3rWE|SvEZED_qwDN&jaylZZAwl_#VzjW&q&KM68@2!8@$DP8E%&#d$#Zh; z<8q_6Ki&hlwBV9b1C#L!TUMk2`RCIsyTZzFDN{v1-Zz~>TT7@bal$nqt=aX2L$$&t zI z;M(KXjhUDF`^iQF1JU=__w-#Ug~{$C;^l7Y)I|_+;P($CcgrEia)c_quuUUrDW%a* z$M4^j3IY--hEOV?+Y@&!uXOV)5LY3djEy6VG;Amt)7C8~z;){-F)th^4rM_2Z zD~-l3Td7s+d-?yi?U`DIo48xE&)dGoyWM=gOFqeG;{H}|dncc%6Ga%$GM{m8&)?bG z%x9DVmlx(Mu~LbGn<=IB6hl>SW{%75#hU*LhFEH491kp^2DGt(^O;mHqUZv8IKsG* z(Qw00lD5DlSD;cEQ3)vcnJoh?sVdP{L3CHof30@a7gHF7mDyAp>vwS!rE{fzC@SN?TALS+ynY2_mJM*T{+DIcz5G%1W%s+1!hhn2v`#lyoc;$+4YONp5i%#5~}&gOHRwuiUH|nZHqbaQ>UjwISTRh%$DKn zZl~gOrOp6C-~8+T##Zk?bqQzE+~jkI}!1 z!lcs#x?rthyuILb3^kHcD~fl<2oC%KPESu4K*9^VD;Y2_gY8f@43Q+L>zE9r(Ild= zRo~eXt1Pz!1&34>;SoQ;%I0x8!RpV?d2$qW$9Tmj{eco^M|r*RAa!GDIK6RN`6D?i zTUEQgagnX**yOadQWW#}K|0xF@?nl$=^q)4c4;fWhvylyVwtDeyLWIRGN=3R=2 z?ivZIyo;eJp~f2(>03E(+1}mV*+n_Dk5Wf+MpWP3Cf6-Ky2Fv7&bP>KlWzgHugB?| zuvlB;)vSXUTUJ8U;nQgb-doWNCr~MDuB%Qo|Bc4wjMaxJ)5FOsDjIcE^i=@KsElCn zP3A&_wb|Tl?2vdLB^VlH-qIiwOA+>q1kdiIRVKfxWp;ZMeQ`>aVf3X^VsK)E#{{0< z!$Smrf90La0GM*%Q}Llvnlj(*Mn0Vgj)63i=54t{o)r zymwX~B^$ld?rHz*%*9`W{EUn-4cbz4+&ksy4all7jrSND1nV4#nQvnjmuIUFnC@~u zf2mR`7nI3Lz=W5%Ly4%0f)6$Vj}Zm=))+9D$ADxG0}{Z1Y`&96%{S>``-)=fN(Ncj z6dxETxeeB$ga-RiEi-H#b}w$R7|@!Rt=mQJ^)_H zzU0aQ?ZV`#UL>RuaL+2iLw;ksQS2%;tabs6x1Sx$b`N|_X(eWuu5G3*V<*lV@S`>3 zoQsMW7c=@}D3|4|0vDBC!KP>B5?Zly#Cu?5HctOYs%O$)99OrZ()CO*81M^kutN8< zE4W%(F~Z{XY--#P;hr$f?+L_;ox40sj&fWCuL2x~mMtwjH~w|^M^fIf=U9G$S!7tm zg$MNaJ#7yfAGkG&5Ts;oak-90Oqsz(3{#4l3ltgW{dK4pU$I3m*@NR6%_l_YvUfH{b9m%%=weQA}1=XlAcd2yQXtWp(GS63^{$#kW` zzSlzAFjN9T7b?2KTYPon-0*|!3?$P6RN0jKTkg&iwR)su5gKYLJ&T~fCqDHIWBb5( zzBO#}9gpQg9~d^yu0RI>4q!nUXA`MfrB307)xdhzeDo9PC#_ue_WhOwBw%wu=Qr$u zP4f?A5>_T0X0huTVNxbTnSEgHOqx-+8Kjpc=qRv52@~Q&DmE^oC$k;F9=Kz(BN$Iw zt0>S7o$!ek(#k6;V44|-fb`6dMw2-=E=b2y3DnFV4OW}loJbj3&na&%xs{6df;cc| z&dHfIPfIEd`=B=C$Bn#5V*pig=0Y;#*$4-N#(3q1kO+t_K4I|*16WZU2o5#|v`*WV zt9D~(SF9lAQ4&usjGmo>P{3c;A$bheof1nko?V);c5S3PMcyq3JY4~Vr^vpW3w%*l zV~U+CHivHPUC{n8Wcc_pNJ#pODdXyAN;?dVq?ZQ6PCJiu2E3)+Q*1VCdoudT-e#?? zVsUNm>@{U9s?EmswscUj+1T5&b8N!A|4`FSqR!c~3Y@^nJweFUUCzWC5|LCqCu3rr z+)%s*T|lLv7w{N}8l`wp`xUG?@pj{S;sH)Y;-P!vCh}?Gj+xwo{H$MH-QCro`n>9= z@!)4Zf$ag`I^#5r!Yp%tMkWHx4|M2V)aAvjsaWB>eT#>|=hYiFp}&)d)vUAhJnc!NXXRhZJ!FaneW#vZVcikzsZ4A!+x8M5BV z9M6pOVRXf1@>?Srnx)hgdh=12Yap||kY_cSv*HQz%t~Pt@{<0j4t}XOs`P`4{s861 zLhcWFN$@Xy3fQLb?!*!+f4H`GQS4D$$+zCfp_kZIaYp9Bs+&n`q2?Sk93iZ6!mk1@ z!9-R#SFB-7txI-{eBSqG;0)!4RBeh7Rq?!17sg8G-v#MozMpYF~zQ1&3m9zzvc0De8>)yfT^t?*FwFLecfPIg?ewP^dN`ADo z$BEIt%nAY}WLZCTn}n=clh4F}?wv> z@&3r&R?lDC7P2)x>xPv&9Qw~CwTl>M$IM6Vc>lD1MsxwtuE56{@r8tkY6FVgi9{6a z3idmJGv-YNYrDGx#D%v>#it998m`@`<~WV5rY{CDVjb6SNmJauu)&q6;UE(M#Czl( z=S-&vGLCRoxk8LarE)C3(DXVQBv5iX2Z%lmenf*WCp7%>nBFQK1rK-vpa4JyVx(-q zPWuS?GeB24mK#a+i#!F)GKy{M!Y#P$K_SGzT3v;%-q_B?l#vs!)CdqG~hlE32Ax&%h~X4*4U6^c418D`x>d6Fycruf*#KO`n2 zZt_0y$4lZ|BPPTkp2@!o_B1G9L_O1K!a`YNs5u}mb|qXpCesoC4Tr_s0(MJi6=eKY zJERgua=wgXrPcHEFcKLk#2c_E)uZUkr-aAHl*$TIUCGqU$wj0HZSi=#hsGlgk0_P$ z=sTfGzTd2vkk99*x~9NM*=!y>VcS~(9xCQoVd2O_17y+_?~0j*mtZv256jCu7SrFr zKp1h#$;--Or0Quj(h(x?#OGajV%(<4O5`2I{0d}#{Q%aK48V*}VdJVqhiA;!IJz_X}WdS7ap*%o#@MhPc zGLz+~RSKjE%cM{ycLllR+0)a>7LmHx3%d~lrEf+ z?Uw12TzVEln!fs|dO>G=Vqf^Igb-O8VZ}m1bX4LLPod%IjU;{9VHx)09_z9`5QKBo zpMjnJmJ|l!Dex1JcvAzm4E|1PaQ?&hI-CLVy-sqMzOGr6hohnJfY<7qPLVC&O%KR@ zd8d3~Z!~PX-ElqV0*=mH0GtBj*Y+2@ad9R^RQP_T0h#e*MnR9Oq%c z8EOnYe8W`cVcM_aAW+hPUnT`#&@FJXwid#lv8r=KzSN=7pe?F*%u#itmB5u>L<9-_ zSq7AN(kNTk{Qyp#mKvh%x>nb<#s_X>V?uJ^dd8@`XV^bE_?$F%#tmK;zcZ4eG=hhI z^xgUf`oqKDo?F2`UHYS(GNUJESl*$%DiJC!!roMP+|sxf6rnPDdqjgflo-Zzx#0#Z z-uLNZ18sH=dhpo8$6$W|j{|f+794cRI6n}|+5SQA?rw0Pt+KHh>Z`Fh0H1C72xyH9 zD;tJbMGQMZU0~GloDkH!-ju>hF)HF~)DUqM+qqtdJyimyrp3?`l+w%8I?RYnrIn*s zW`O>qGXJJ1A}nKO0Jo8vVydtzRsurd<}j#)Xj?=zBQCbW1AceM58UAHE84;kR2KwbMZIdgreC}QJvx4{zW!>8r|Z3m{_bK~;ZH0G;2aK@_? zd}zoLvFlZGADXfReVAq)UR%qavzQ!Nn(J$~#0tPlat8obB0B)ElGg!%rNnM(h!ho@ znQ13c=BpqH3>HP+W2uhVu;%4uMJniqT&>88QQQT(v@qq7i+3CYP0Jk4M|eaM5A`!U z`%qUO>h!^pI46g0s}Sv{ryAW9EY8@eMm7bBvoljFsimb-MjA$4t|X)YRw*ByXaECH zC42#y14pskev&PNUbYPS*)kaEWdIw@=^5-U2J%Y5Ku5qF51L(TNPASDjcmm} zgzXT?mIGvh1NuWd@C#d5ImFuGH(Yl^yES$@(z7USK|^;;14cgz7W!d@A=?Li@#?|r z1YSqtHNvlTdw(Ea6Z{IVJz*3*ROoRl+=Ey_IRcsv8>8iw6L@On2XNXvV;AfSVInYBBqKq4%cR3tig!s7B>6!nX$`r|c7aJvR`;&h=4HLZE^O8O%~I z$TA>esk1Z+Yw6)6s4UIGT6%O}iy>JAk0nV^>c(o=fZGC{XmB(%8W^Eb@p8L1Rn=gG z*_ZSC1p;CMBtK+Jl5vw2Kg?BJH!40}qT@qXlm zljj)!1k-*IX&=3A;0-9{FOhN_495eKxKZlQBK0?V`7B<3rB;v0q(sBI?u;K6z+8eq9 zc$nbJ1Q@Q)dZ#>GAGR*`N9*XquLdkZ2mW4^fopO-%eL1$HpA4ROdX2UAx15+MiuMA z;7c!nHQcyZ$LrRs8?0d+IO}z59KZ#6!EPvmI_4X7}|Hia89Zl)m^PR*-X5xqi5inN#)P$Iy{Od2KJGKQUt zF?5jj$T6q$CPcr1rRY=LDRYA50?99`?D!N{Ovajo^kg}aZiFITAg~b@PSRL}6kDoj zEQ`iiGzQWzPL>rtFNi`MSY`BsX4zORhia!sbgS$Zg-Vk&U(#AdXqVy=w6uwMIb_1( zOf8XK6{DavK?U7DL6z@NUjLuo3cE1}W>Xj?hdJ5i^sGe=aKx@`sEoD(!oC3S;$vwT zcNz$5B?Tmt_kes+aJmDg7 z2k-z}3Yx1G*QnwZ}THCp<#cPcHLE zP+fFp%+DKRH+;EBdH2m+>_u7rMVU0kg*NJJ)JlWXJB5sAMNt%I^GKFv%EWvZL`jES zn%Pio>oY9XlR`_Rrs5QZwo_$nJU5S=CY7Y;sEa=31 z)W1j7DMn>hSP&QRF*QrB(3F zx(dEo!?*>DVm%g(P=SjX?NlLt?InwvX~UemVr+NHh%` zt_6%?#$)289&A{$A2#sO1LPll)c1miyE|e}yJf5I4uG>BAT#Ns`WHONkHCXgLTqDW zv>vag@Wi_7u%q?yhiY@X>%jgdn1@jfZhyVQeBx^9Q@zqbTO;IS;fpC>C7v*rat7uB z)D`0Zz}I()J@=gzv$3t?v8l^NAH%T?1SjCpHX^x1I5(9XjDQ@Kk;>hojD)BS)9mFZ-nHhE?(S-MzlP~Kj1XCKNg}HrX(C5D$VMs#$`a&N z=33gAZ@fI$2FiF;wX8!LxcYTynJS$fHAC7;B5wo~k`WNBIJ{jg`xf4A&S(h?XRAo_ zRiso!3XFaVRSZa`$%qjtQ06!V6zeZy;(?K16egx$9l%+9XS${5K zxjqof^%0Fx7!dn=7%PjH(W~y6lnZIr>7(SCB`@@`N`DnA@)6_oCSrU4NNlv+s6STe zuh?`y(#|#|*7;LrIPw}5@3xvdn}+n@`@Ga7!{9G@feG!luP}PzuY%Hkc(ZObD;d$< zvW;4QHU+jAMXg$^;r3atsEDFK+Ugrk)H3#nv{koZ)xWb)Z^Ejv*VWf2HZDvb-#(Ge zLmK#(%GRNkyKe4bI0kY3By*u8YadVLV^Z}pzGHDzk}z+QB`Fqns(OhEVVjQMW<&|^ zAtOb4(LG`ORdy|}658tloM`8G3XZ;NRlIN{W@B3{ZuC7(;lg`2ZP=U2E^Z#{Zsb_- z1~0uE+D%X?J&Om(dBea4>VY5mB z{smH|098P$ze>dlAYoK|HL=CH_X>MOL!X+AhH*wQ2qy;B%HB{lI9)_+$+-Xc0V zJtlR~3mk)utMVISYYL1vpj%-!iHDE{h3v)=AiS4Mhtv~^R(PLA1lt>o`ku2Cg$t4D zLa9)eItoH(c{xo=avCc5lKb9dxuStAS`K8{6Cs6Is<0;udvN~ZgUAok$s?MMfW}bq zdL^0)NwRr%T@37ldfQUM|Lm&Kb`m;(!^TBnyi-g-wi|6OQQ(nPE|mgjwEid%DmO)b zqLH{AlVU}@?#7s_vMXQNxC{$A zfS+|BeasUN6AYmYgm7cbV54=tjj>P@!J2KNx+IdmVS?7u%LH{Kq2l;7%?Ye$FR8g` z*iG^pMFEU(Aa1#34t0PRUUzx`+?xvau?tTVc$zRcEL8%$O<3=hyX#%jwpj0P`1>QR z)VV*F_XDV7I4a}i2A!sQ;(nkjQ@bJ+V*u@=?`!kymaLD+$tE77dh4=t>*2--(wI0H%H9By|u3vhzV3Qo zDYdD4xy&+WP{`k-3jpBMd&u7p9?3ID_p`RhG!00AKhQig>Qf_dO*bC?>gV;wZ?=a{(6_Lw94*a zy*r(|K8iD5I-aF@US_->=|`y z(Z<>2f6j~OyjgsWa`-BbQEBE)Arf93-1O8#gAuxIMc)dQ09lFyXDUrHLVvMiS_QhMYow`uIlQ1GpHG>;s;LXDkwP z*+s7QtQ#yi6U`*OJ0>rp(-||Yg|=Puy`8I%SE(1k5K|B_v+g9jRLra=m4a+4^0y<$ zvn?_|+}hpR+Frsji|~{{{yJz{`qj@&^h%m1(n}=uq447vj`YJrG%lpHepuj(LzVQE1jxQ_45ar zBs)FSWujP>y1+Cv?4-A=>MVbfZorUa1KZ>ht+no~FMTtuTB*327kaqF%V&;cbjVDty_nv2gU+QL2|CX!x;)P*GgGtr-dh))GBr+T z!CE_?3Ry6@0IKaa(eZ+I9MsF`PFK?%IPF1(M!mj;9tqGR`EG7rJBIJ(IUm1moSRYg z_quheCzm2P9B0@E<}88p0%VgLI9lTU%RAV+iL`2gKlH0X475?M7ANeJ1l2a#M7aD|P~>ru}BcwicrX zcq0V0^a;UPYMQ+TQ(>b4o!+W7>gcaWSvXG_&dP%}#}AQIL1GdT<>a=Q7=shgbT46t zZ7gd6GFsRI7YYsb)q>81&oll=2)46{s1VH*P2j{YFhawJyJ(t-&u)`};YgrN0V~}! zb28|ZvdeICG+4SB4#EU|M_Q@jWG}Syfj>fYv4^9a^)i;)?OQ>&;UgZR1j*-9@|8fH zyKg)nq;~D>xJu~V-`Uu>zP_$rH>**6v4QapHg5Xqa8M$Bh`_x~t}dK{oQ+zowgKNt zjIuj1yTO}5XJMuI6*5ECf-Jusy~S+18+t>ctYNaSY5KZ>4x|ZbmN%sba1&Q>{L3pz z2k!1j0Y8-d0eqkhTB~o*q-Q7?liW->R*Fe*rW`AsTCs~Bx}YmJ+K;eB+7!n=y6t~) za%7>r@QTOi4BJNge5m4VDkKAx^EpPS0J8UjD*Xs#O*ek38?K<`KrgLuneY}~nC=LC zO{0f7gGnQ{VXs z`z%%hf6spUBA#xH0SPKUGoXd%ij2XqTeG`2Miv<*L=>cidV%-Dyy-6;uTbEl)ilUE zqSD7;;zrP?8$q;aJnfwcA46R5I1wB$&d*Umcz%wi7(`x5Dg_;Ip#S!q0|Lyz zM%&KQ0Rcu@p-AW?EE_o+Aar0?pbUvUJ8ZP5oeJOnl9!p0;N3UW^e_+qr){9tG1xX>xMR8&>B6nRtZ ze1)Pqvg^3JGlx!2dVPCgdp$v>=TB~mX6tk3+^N&$xmVc)*`f%K1xAzFNJg!LbXur_ zR_rfi5mVno=b=38zUT*o?qc>@bOGpo0$clY@;qj7qq{(i&2NiUVi#5gHRLGkgMFE$ z!!zZd<$S&_QH3;B5~qQFUa5MNH(N3Ypeu~rG@5%lpa!k%1W8_sVKmxSMz>rKQdo@M z4kFkg@UQS@#+gSUH%K!wp;eOAKv6uX$aK!YMVeKL&0m?o-%jDwVi@75zOjy6!1At9 zgaYY`#>Tp_4tZm6`{hV1&M_Nji!(tz5}$m@x2acSXK+e(+(wlnRV94N>Jfv48ax}p zLSQiK7SN&EgoC9804nbG;1v6fnzLgCuyIcLOgn#9_j+(x65tSSSyB6e0^V1odBdF6 z%J8&YhR*S+&4$Y9M!VyTv<-x$5!&sBpd+n?$8SZ9 zgBSn@n{I%A`fg8EHB|fUfDHgj*-ZL)^>N`UZvt>iOc3;a`M!ENE*s1h;AI^6c z+IwBG3A<)PmS*EPXNTJF>hb;s^zj&_0_X14>G7FZM8^m2B|zZPJ%&R>Yt=e*C#RP< zi{tLkj?`b}O(X?+ zVAGZ@CtLYf!MY8Wdx5MK&c|pcNfekog=-BW#9cSV@ZQ?&(o>ojup)5oLYsNvpY!OV>Z`pWWRJFb)Q~+JV1{jzA+ZHA3aF_8B%HS~yqNre1!2 z=qavk!;-#S#!EpxkeGserDrT#i4`<^M839Fsd#WYZrN~9B5&yEP-ty!^ItIrm&0DD z-Tp(A1Vk~Ai-`;|Sbpn^foenD@d7hqG8Q&MlBI_73L18|Ir-+3YdOqVV`nw3tcFv5 zcLxOK&=l6>K#{paHDorD+mDIc&+T@2Yr-&Myx#B(W`Atwxw{A`iQs8{_=^fTel>|= z)XSgV&#)_8&RIR1qQU*NU8y_#AZ_C>kl&i~&U@N zMW$k3-g9)32Z-5ml*>Hd|D(*$xc28`n58uAs0$@AKz$4eM zkEZ+$j>ymCecFQ19R^PzRzMbjP+YvlC>%3e4`u72>S8_zDo4*PMD3A5)%wDD>lEI* zstg~TcaC~~>AbN~$4_1${i$yRT7^bd#$3!;6wcXQ72}@>eU!7U`kJZEcfwIEghmUW zaxE0-cNT#1$6^3JcYYV*cZoYFEpT58iEM@_C8`X=9FmJ6A{?63_sno zO_^m(&$9o#PpN`0Sg3O~swGw`IVK(8{6Qau1P67$`I}=Eo}m8e{6tbqbAg)e8!e`4 zz(E-D`1Q%r+Z>2^cn^%SA|`}*{A3Vi6)pl$@HhbRbOD5T?C@|NM!Ew=lb#wb9=VT$ z&L9Xcq+d+q^kT>htr>4+Jes+6r7m*M%#x@^(D`%_jgy59<&H@N#n~LaAWl;1iP^%q zuoKYFFQyQ5)&i||U{F|nD@p;k%nw2~uOtKr%sgu&DX~eeWgOs@l8qtq7J30FZoZX@ zpl{|^++Tc-Q3=1IABrX|7Lv|(1v(<7quiHzEg9Fq2iZs#=#>En#r>R0(u!osPylN! zxVX=u+k!wSW|TVK%LltKzI07O|8OT!6e1TV?>P%p+th_x`j|20Fgsijr6nj%7lN?- zko)=Y1=$K|QIJf5a>XllRJd5on-bp1*X|FZkYnW*28>MSnl$1j7hM+^w8b_O#S6^g`b*%!74B7z+Mgx))ITjX~jo5ib@jB@biW}QjK&qu89g0;u<4$M5;b>kJ zVXH7I2%=+`1Hl*oiz6OvCtr|4nPU7&G;|oDx{df^hSuhXiE(qHFBm|CAmE9)a+U;UnWun9RIrRwCL)z3_=66X`6U5R&{&kUI~ zhjT`1I6Ppn7bM)_5gSC;$g_;)#iYXr@@moHUA24i5hMEG@CzKy7*_5T+W7GaYIZ98 zbNHq3kHX&ryeQm!?Ovth=R#dXXwbn#HS~%3UF+?Zr%~EO#TB!c47cb-;~b zL8ZJatUg=9EJQ?-xx3q43l~a}iEv{aTOsEX{LqYEajqY_acLssxj_r)0R<+`RbpN$ zQw`Hz5W?rlZQy#-S?%H{1hh?suGvgL+nlbMp z$O?kj(xENQBG?KSbLfj0{m^g<*oYoO@h>_frU%%9>DrzWC2xM0fZ8CCzR$+?6yaIho)fj^Fd=tG zoB;>L_<;ZTQ946Za2mMiKwYV6KCHeQ5?8=ZbG2)Ae*8d>$>RLexNGrk^{}I zt$Zl<+`(!043In0;h9lt9%pmclXDlSx$B9!Q}`o;HcOCVz0j++4||De+_p+y0BgFT zY+oH%*c`!O!8KPeiyWh9mGYiM6XynaCr7kPE7Hmv8??;J5vO@NM?Gm zoKQC?KC+SZ3)LrO%B7M6Xz7eid0x(6(Re13yXf&n*5hArbx@!q!+rz4zksbA&f?u; z3g{}sT^ALcP8q3jKBNw8dUgy7V)4d{j2z~4rKYnAvQvh-kq>u3s_~wEi;gIbtNFf= za{VI}dkf;BWsZr_E9mnU$Ix{dQE8!9h0H4qFoKim|mVKO!-wNyj5L!1!q*x7cSaUPrSVE^zuLSk9!%MW<+eg zFGlf;_%<-VB|azn3h}9He0s_A-$ur_H(>%K;{8|{Nv0j+Q`Ym?Nd3sk$U}S>vSc#s zL=x`D^6f>_y`7Ut>?=2OQ^RS}O6F_hrgS8~|NZ zZKz)SI*|$IGC?+Ys427b!v=)8v5vOmI zf0oKCWlpuMlvA@dtUsKqA9#P(;a_0WTU5CjXaX8|G2g&SIh23n*#?8T20dsHcA@!( zD8v9K|6CXbG=$y)@|Dq%Ksjm&s|@WneVj!f^WCt*(P*ZU!bvTG=dL@3=f3GoDB7>( zeb|N^gR)-n72(kXec6h$yWqfdOLh@j)!n&?N;9Xv{R5BmqqV=L?z&$YtoQ5#Z*5IV z4@T&5i*+cH-q4j^T$mZlV2I|X*=wKlh=udI)C(;u7Tyj$^lfJi%l(QqS|61=>mA$P zuo~+Z>sJ^Ip@HhglZ`73>a}sv(p|c))TujG4iS0z8oDV*2G3pF&XD;TK!`r!FNR0Q z35T(b4tmh&%*6BZ`MN&`BS;)bcjUzjpa85>LYG-&Ar1XGbn)HQRlg+;{bA<%FCjbR z%<*4>#>l)pzz7@V9viv!fB6`7y`5CX&bUI+x56b5CRE2XjV)~xTe>uszLOz>%2w$D z@bB(s(kJywAJXlaS&`KM3x_5J`zY({xSbyYSX-=f5Vg$-66-Le397@>NPv@91`4nh zoZrPc^-OeUXQFRAojk_O)I#XcIdEf9ax{k##*B>ZdE7822tJFOUO&B$);)#LmrH#~ zu&yM*g7*r%el9I%>W<=Y-yNI2=^ij05q> zZ#K)byrmDuMimaXaQpDx7`BXo8&v|@L_Cb7w>}24?UN5UpmW{*D;+iueSojSV)E=t zZylCb0Hn6dPC0BhW0u0Nf0m2?Q($65AunMYtDuKQD{6ZV)V6V9CF@CfyiS_nK2+9o zEB<(@Yb&)G4uZtT+S zt`94n^*+0Ct@HgFDuLJS^$TkR2i@cK> zE?(c$$FltROnfZMk8j!VWK<^G7GC|^R`Bk0-yk`Ar}cX4oDxw4b0;Dup6 z`W94kxjuwHD8`3P)52Z)lHITqw));?qk}851C}L5P~rqqp~Ok9#0c`=u=lp$Xb3Pb z*c+I|8}<%>_yPWYguj0RFn@%9zq?WK@BkH`yT4PTal0HBXL2rxZxKlM$?aG@>#{P! zvgHMO9q4NM6Z_u%gwT49(7+ykagVL%8#tKXA;&9vyA*FN>(2_1#V_{3RonWq?!IpW zQq|dqvipU7thirn$NJN)!MHxU)=l|@m?rB-Eb@wqywXKpmfctEUB!I`MSg@LFWq;# z2$A_;U7UxPHv8Gd#^K=?7vcQTW`DWZxsSwSep0>u%zmR1?`-xHm4JtTQi%^Xv<@wcr|Uc}^KhL9z%yRZdrI#ik^~;p=KwzoR+2~Vg@x6v<4+l@TgM-}^%`dG z6SGDY9zj*ytDqvo_agC9C8D2;>GVf{>GufN7w%h&27)8HWZwhI0l|1{$#JP9jj%7O z{!RHs6-Wbv#|bk2=MZcrZ4xw>o&KD?n1Z}7vL*%~kvKZ#aVPF*_RRhv31p#Z?jcjF~{N12>_7Fz9p z0-RqfSR+gkwj)+~tzfN@NhRXVV0aI-Ul&08^;4kz`ccpxJptMUz<#|Hu+3&avJX&^ zqrrj;ZYqv28s3nT)+p&*2_XXcc9c7w*lpOjTq;Q;OzpSZ$Cy2l86QDgODx)j@YKD4 zFaptxpiWpM!s$hnk3m>r%!2x=Xr4f2i63t5q!XSGqRT zm3k5!kEk?yu+m6OUWlqq%erpOL6|E?tvS0eb!1dE^PKHhP6YWTce;7EFa+&~g)wA5 zYA*C0aG@WF3tctY(3fcCkYhh1%kVoe0G2<>%zjtvreAjqn&Sq*5F&S|jELPH8ThVR z$$oEb4NlKwE6frMoGe{WTToHgZhJ(Ev=*7q<^kg@~v^RFygnUri8JS4E{ER!X-u%dS)|jnmaU z#x_+HP(_bHdwospin(y8Hdpj3&N+eoGl!2far$8N0nkxkr<~FVuyg)!G?t1qA}TG$ z%*uT53;{#gg*4~wpv+P|8hGoGyCOJpNW2wB&TfZPp~#hlHQ4X0{eSGe347bNvOoM) z61_P&q(#YEEH83cKiQKeY0_qC6CYpyDG8DoOQb?lc9huP{hb*AB!(hnr|r4hbGujs z1_ZIpZ!p+q?uO2k&O}K&J?Qk6sPK)sJN9QZoQpv5uToW1NX}Krszt%pJPDv_)@UD& zegeQW8Y4GV{60Azop|T)$35il;>YOu>WaM2;vH-N$CQc(Z(uOeL7iK61gMd5eSE{i zxsQ&(cR&Y*IxR}t!J+^Y^Gl#ofmG4_ip*H~u0dZWxl&~5+V0jg=5D@ig)ikh91~n0 zwM{>~m4CkFO!R8xx^1GdKR8~X{gRXfaG~D(gu5Zrx<4*Po7;YNw2)RZDjn8j&fr*_ zcmuSadIe~tSE9Rj*WW3+k$h32`^gO7A)8f0ANXR@7;UZGZ9Gt<%8LHY-I+hHJ5=bU zkHtQLavqJhhW(aXU%{7`C;oT3LlDEGM`ybGsX8h<_KFDr-7%0Fb{|FvrEK@luk>2# zpisQzsolR*`NHO)^L00#n+eUQTI0ejZYnl~9&xm0^`jG^XO3K@h zO@xALGex8E%nkGRT=D6bPobWoNcLM-o>EsV2~g~Kxi++(h_n~XMrlBOb|E~^=kl_Z zABe#4-Mj64|C6$C)jd=_+hS`XMtRzeB5)jnF^Dm;Tw#{x!WKjsxvntR9EK&KVKf^A zK#R&>Q!OF%{KWoZQt_hx=JTp`JmcMP{oRlHqpz#h>Wp`Tig(+8Cs(~NhvZ}Qg_F6( zi2~8kJK?xdkrihNgnM%J?1qw*K5EV}Hwe`tt(+omlC3hWF%_+pvgNcY-zw2)CRNi; zv&LRShvg2xtF3Z=kXm2S9P*CXp=lGu+=-{7-X*U=#r-w9fmh{j#{UBF{RFuE1pxb| zV!caQ)&foHgR)y&=$uP_Cn6<(0z8JU`&?e4^;@}5DxARMN~}Ys$(A0fmwZJWGOS8E zt4xZcF<+2h))yot^QV3=!Yg&L3#OB>prI86@7TZ z{Vw!qO_xJOlO0?{7YkWE!Cesa!5Gpj%o@v$GXm~)qIyUhD(V$(d8NonUbcE6Gm7Pd zei>&9FC4s!15EvbOh6vyUk-3rHo(o(weQDBigB+NITQZC(-NPSn7m|>MYZi8&d)Bh z#bQu?b(zT(J@5iv3quk=sx4g6wcrpYGD&bzl1_35t2n!uZLf@qM_vo7thd-8=$+yz zKM~w8vmP{UkzJ#^6?qNnMWu~3D#DP+h^dM&gKJr01W(UHJhw(g1PkWb8M?8y3=$i4 zZEtrWRqA)PL@e)NFK^gJBBd29d`}eHl&nsP><1NU@e(RxMfuP(>p^S3-EAGTd!2*M zey`(+S$;~*S#7sl?sgvB+oA|hjEf|v7_%j2o*4O!e3;U)vZB+QRdh=Xv7%zUMNV5s zThWI4hNg+&_F-_ZC+%}eE03pJ!FCT2n-?fX*ye?fk%d)OOXQD~b#c0tG?rVTx7k;%>BS2gEMYYJ5@{;b(u3{iqxQ#b6w^PnIbi{wEtuOSilzoPe9s*C#Ig5d*alz ztWlV9ojD@JIo8bC-Eq}o4Zod8cR%R-GpJXQpD6XJdf~2#eFxIddLY3|+}taiT)Sf6KmMLRwn#gG1;igg994>%zI z@r-j_(2a!ct|wzw6tj{sGKEZ;$E;KgOS2=>!1QT*SEt&Msp=8LncBj;jl1XKRcZU2 z(i`;Gn_s_Qe*HCn{pIG@AC_N#$zOkNPL>9ENzu;>sg{Zc=m$9CmSO(=Yi(cGFG(4v z)ZuOK`ZYPVI<~$h*6jmJ8HP!|w(~1Wu3X#dRIg&KC%^i?g+G(jVWh5C`6Z{fr8~Kv zUm`-F>G+k7M^Ns%`O2d1HCjwu;}fsANa3xqjBy7IHI3KR%q-4wwuQ$ZV$ZJ~{m|79 zI~AO(9(3ZBPQ2Rrt4!Ul?mS&O-2ffev+`h7PnNRF^8=}=>q~-DXP?KfFy6IrfPS^e zrDNt&#IHIPxmb$al|@#oJ(hYis%Fqtw~D%>sXM5h{;e%@&>x&`3*8XL&Gn)wu3eb_Dy>@8b2W3NB5<2f&U_#F zg`P@w)pW?5UXX@$lOfQeC#hk-j#Us zUo1y+O{h}u`HNc4X2skX?YRhn9<}KE|EIurDr9AQql!_;;9qVggG=3GYCn}yS#_eJ zA!(uT+f9T52?2yl%Mb#Yh*C(nSq_+o#l+_u0`BvF=49e)@5g2p>Y?}VonEXd(my)A z;5h!_?5b4%8!Pt})SjmH{(jZpuE|&9vbx2&dhgy~LeMMT=;h!2uS4>a8zcGiUqrXl4fI`Y+J=>JouJ;;&jCHOJEuWiJ#=vDV$k!PbON*_=sO8+9$^b_W`|~j8#~Ts15@-OzHp+gsGe*k zCbzlnm9=&MJM2*uvi<#9p^ErObQ!12XTMA@e@}Rhjqujbz^M|EGKX z*H2jg`wNc$nG@vyl>+6(4+xas4T19UuL_h8n~!&WDX#&1;K#ezZ4`BR*dYH=G2fRH zZuGp{kn?|-)5@JGxsSE&B$t;V+E{J0@HoE{HPq=jC%n8&G$F;Aa9EGG5`3c`>QnR% zlqqoQ@HckV-6VDkI&i)GId;8fZz~UyYpRw}{GZLUutwo?Cj>`McZ(N3!i7FSjFHQ) z>y6M-iyket=+Rb-9&M^t1FFf8?R z+|K4)x@F#I^VfIE^cJW0HfO(^UuD939>y*4bTR7mA>L@u%bA zbq?C<#8E(rDpvR9!hrFjAHX5p!4!s;w zsa(uhzMoNLDGvurQI{FwJ&mHCR6$)Om4`Mt7J7kULP;Eyd#}fPZ-0{}k})@dd*!U~ zT@XV>l1x-3X_+aqsuXQAMKSI=Va_h@Pi@!mmqxy;2yMrB;^74FrmZs^2KPd`iy&Q2 z#VSz%i6UtKh`g0VZhRb_00Y&1dH}8pQ&4D}L`J(uiHz>62%~)Dh(wnq%S#BkOU4Cd z8#)>L+KsTEvRupW!uAEg<#c-V#zT-0AV+#2@>|Ew7I3 zzc}$m-ogvr6K~u^(+obw9Zs8PV&#vn)VH8lGH2%IpPU`X*hSJSpC9B+=AaP+FF)cQ zVlIE(aGts;nW3+AI_e&ljXwN^yMQ6x1q_?ywem+z^JIzLFpVm;kfqM(8|1j-M~~BI zQD0yd5oBSoU0eV!F@V=%o(c(hEdo{Rq<~ypFiz-8oF#;_uug}OpmPC5Ff8MCKuFj$ zQx!?J{qRbO0K*<}mKJ48S=mZOLm&Xdqfrqh8gv<##A!=cG9dafGQ|Ou8Y_K_=cP`L zmnSY1rIEn1F1z? zki$0j1(~OnZkW*bQbfL&Qf%e;1jY;9%JLBkg%41sM!MapYS>}5v_)euiH}83`P)~FT7^`xFym7`22O?n1lTl=ga|&5ZsjH^S z>4P_*45lY1hoidh4Du}81_p}Pua<#PwXlr~GX-eH!kJPbCG$PmVhucfnmZYNT zW~RLe^fj7|3y*?0N^CNnb1Y68$ZV$f&}__x7GO}&k`z;Du^m9%b_;nHTGYj>`$7#_ zUa zRopO-(4a741z+B}R%33nA4D9+j&d8@5@Ts#VO2 zT^G8!+5T^Ic3bJVt$*)PL-WcIKWg}^dDJj#c`#395$=E02B4fML#vRZV&nu`0&@-b zO_!Hh9)g;E|8Ko>=jo=_EeyljAI0*M3v#Z!++FUQ(i?yN9C@ClkDipFVOs_wUJpKt zA#@y+1*cL5SX@_bc=#o(8cAdV)0sAU4)sLGgxm&qU*%;zdb`q<*9L*I!9!OW9+dyh z{bf}zqChTmsXIWs5>VORf3AGt2mSx#fqpv9Pb%28;K0-UsE^qolGD8oIo`D7f=d$Q zYk-We=hp~)TCBlQbQf;{7m6J!nSqDO-KM@k-4`c|q54H7Scc^fV{nZoUxD<`SQ-i32?o{meBd+t@S(zYmU0L?Bhohh2Op734hkekO6lXcgr!OIF{{ zox<%svc#!FhRKb|@HlspK_PkFkF)7RG9ikhi)WG ze&hr{MY2XT^;OTVC;SLaa>$C{3`IYCkYx$7lka`+Y;Z>dLxJ zb{NCQVt+UjL8AF3M#~+zAZv(wpy$Dup} z@b=f=-gN*m9T1y4AU4S%VIh5{#WI2eFs4_;S6nJ_>+NmfZg;K>No7sjD5o7*(ss5e zH9DHAn_Dw;V|wTrmrhBc#1-Mr30*lw^J+dSnBE+IAadfTjnF&ymyN(%`ID_l{e0^j z)!x;;k*oY8&*F(Q-;Ukwp%+ywXdj0#98SCd{+@fvc`KH6xFxm{_vES=OD~M$7BDoP z`p$f7-iWv2`WP)tL+#?N+17>YJD?XI&oe>f4ub=>5tvU z+x1GQWw$Nd$C)~B5!mszgCE;+qOAc`Bdfo48w()0`A%Lvr5dMH<7j)TQk`5C2L(pU zgoZX8dogozoi+gm>-5^K12iBGyw+_U^w2UmaNtf01~%^k6YT6Abg_BhvAd74kKvKj z0^?x8+x4O7ar41mzHI25K=+EliMdT^B32zVEg8}21vPGAmS?^L+C%e`>(uDQnpYFi z2>uRH3z)$}h{7*fF0vZB288+6I~ZVIfe3sJaJdhY`VwV)a;6_N@dpk41uF>9ucx~UBh;fOK+}XU{?MBV8B#~v<(cTeyW&1{ishN# zqzZ|}Z&KQy-f=IxSsdS*LHyyZU{womi)aW_q z#whfuvn0jU;d?%48KAf~RM_Yk^oJlXiAYxgM!UgO>Xo+KwRGfO>k#=ZbaBsRZaQU(tD*NN0c}%H!sTC(bX7E#=~eSu3*$uBJ}m$yXx`YL(f|A zqIr`e1HAoeoUOIDY9cJt70gtsTddk^3TeqA7*0)SK2giwt~nAt;Kx_;#hD6kNdlKN zU`mi}Mbdxwd%wNk+Sb0dpZVXZTsW5e(QkQq;3EWJK7)fGXb5NG0;wsq3nXjHhmZZX zF-N~sG34W60DYj)v<54-1QaME{E2Np2H5zq>p4qM(LhH5jS~610imUrz~mIIQ97xS zfjY5E8ARKD>LvJR34cJ%*-Gv~Z82-Vfeq8h&;ZL47^?vvG3y(e^$m1=3#aC^O1|}8 z)jy917-OVtj952DdR6jeAS+&r;z-ndJoxdfkB2gy#2>{&Ivt|68XnIChMd}?=O1dg z0G!kHgGvElDM)fL>_bQvMS*))GttcSEC}Xo!Vj7gkv)v(i)AK8Z!v*GM!`XBJe-EJ z5uW!$Jh_MaCoB-iFI=L_U%KPe9W<~^CfA)k4<;+kbTJE|3SP~1 zaRySSSxKEHCGfXPe|P9_@5B^IPt6l)>Vyn5R16IjTN>(@w@z)EWsCmy_Ql0T^P<~~ zlgW0c)oN|0=MyLbXhAOeT@l&+3}%!AsJ~ewkJc#SqP3a>km68r+(k~~SvbP|AJ;v) z<4`r{0c7&kZ3bxH8jY92|08s_0RNBO{xj1TgaUza_Bdpw=GAc^+%5uXQ?K0u$x^#A zwIkE5JDz?Ap@t`R;V003-eb8=Udy=`+FcGm@+U(7Z9-?26=dw2p3$(53QNzD6*%d~EGoDskyk%UsIg zSz+Bjk=&0>^!4gMXB1#YC?51zfjQNK3u<(nmov>fDUzL(s?x!%F7Kqa{8XDhMP4q~ zzEY3s@QHd-$Ncp?wK%U=OeJ9^7vgD*d?qPq!3BO`au_&Svk;S>5%`G1WkHEd3YjX7 z;Z@m9KvWMz05z&fZHhxL{ZjN2qe+r_>H$BYVj>+wNkIva%Sql!{sQ<<%R#d^UbR*zIwN6_~XLgk(fYRdLMxF6nYN{ zF8z<5&?9HByT8A0OxZs%1?RBb1(Fi_Z}hlMMJCOalpgG25EoF@Vi8#`@#jinOee8o zoWLZ<@rjc-J5QQ{r1aXC$W$Ezdy^f(Z18jbi3!G6r5~wh`@(XG=CM=)Ilm|LPQ0en| zrf_oxr0O0TI*~d8QFaGmT#SIa0d9dx?;T`z!X-It}fn1-OKw|94TcDj2# zFZi+y`n$beS3b^zB#cCVA4GF+cgG99Efbl=P8$*rL^#2B+wFtSZp#~l=@mB97tho!-H2r`z_1!ANA3#cpSJx7XSChSMO)60uBWfxEp9M8(4x2i1PN zySIO^vj>tePJ$U0VrQqb*TG;kp2Zg;k$LqF+6Vh>icZ7X8I`sNxp~7RoTo9A3Eu0r zTaeKzh*VRJg5*rLZufu!MCqdr>j7r~ zw`hz2CmcYdfrJ!krn;)L+uh&kfFK`E(=ebCwGV(LCP5gbgE)ziVSv{FACqaEW;(BK zdl!;=$Qk$&1R!UFPjT-Z*4^LRY2osJ7)03H?e0!*zt`EJs7Xw%*zH1!^Eg>i-JyUjPqANb zZy$=>@@By~%z;G$G3{;#IYY-l%cfI$uiJ&@o&^_?Ed4(4^1Rt4nEii(B)C(RJzf6`#Za;j2aOT3Ek0Aks1=6_D*O2Kz&v-CPcKlJL)S1B=+{Z zAbGniT9l(RA!e#spb!8p8J|}Zx~IL5k#m$`o&6R)Dt-Z0!mh;QG!ZpPZx;li78^Os zg0PRFN+>&bTl?NzjKbyI7zKc7_PU*pA}HweR8UKiPN$8Wtzs9;WHA!}T#(T)=E%sr zmAVIe`@jo2Iu{-Lt^K_{Xgw9bfUi&F8@oGg;0PIC2#Oux&2CGlm%>8wbZgXoV~}J) zx9!ZdZQC<#RcqRwwryL}wry+LwvDN_ZFjY8>-G0^@4NRtM7%%Wji{(xC)YkZ^F&6T zI$3AuUZ&NZP@4#1@<6qzFJI_FBbk+a_c{EXR$h39h^6X3H`BQz_p0t}Gm7_$_d6nX zeCI&o9c-0aTHl1|3KG}D#SZlSs=p)z8w%1vBI8Fzqip|shmK1D%;{cSS>6QGR|z#j zw*KOZQv?c-;yCQ#FI#mkhWp}MrTbTD0vV%oeS=U*SyYk5kK^o1rjgRLjP>mVxR=1R zD9f4SgTS04i91Tgw~}SSZ=d3c2Xt~5+O&g)Wi+pF!@_mKZnf=52Ai`>OV-W4A(-`h z_=ATN6E;{n(F~#zLp{y!KFs6E_on5fhz6I+ld+8N=Y4J#_N)R3X zOjT@e!U_pv#f~x&qzu@AU_Eu?6cu>`3cpkOz2TZz*FT5di2jK;_sVCiIT5&=8^9!arlW z;J>J|@&QCzb=DVc8}#Qvgkf-0zhAW7i`sikW$tHXUtCr@B_G#YSN-Br35maDU6^Fe zp0AIuc|17NUjZ%K-@MXhe{?PTsp{2)6yH~QuM|T;y|4A-eRO86ZZheZyUZ=#-z~Cv zXZh&viP5aNLQBI(h)&(bnb!V%EcWn+7;vm_bc~(GELs;u(17GJ$)N!|ezU*gRSEDR z`?YcSyn2GaXuaJUPjH<5rd)S zq@$z?En)83PG_)G*jZEKB)Ff$^6irn+DamX<&tIrNZFK6D5rk9s!*}Y!>{f=TK0P{ zvLCvY^g6>B<$dEsRQ)@??ZXBxluG;VXh<}WbQBCHLgkHnMN8a|#|<5865^{uVx3|D zs|gJvpKrzc3+3?!)r!Qx1d6sq$BTw&%Sv`{^J`Nb+>(?M9Z<=>p!t;-Ny;QmXTYA= z9oqqe(ZYG>{keQ)PzLwZ7IEsP35Oup*?Ro%T3AZH5%mrtfy?X9{JFM@PWUAGwzPC^}X$)9oD3XDq32c4s`08LR#W4rU*b9EoC=u&J%%;Cn3EeQ8r@ zRoaM$KW=YKlJ2hj^Tw5>q#jrm_{;(~W?a5qE0#kEcSyjVfqxI&MnnJ>I8n8wv}m@! zi4Rbk=~Gs^@N=_~dVW(jS=HUJypJX>_b&E(8qj>FAF7+v1x20ib>7RD6P+{_ty|~U zwlp{6{R(_(6?Sb=C*`L76=+V6GNVO0#wHnf;EtgZ(d*IPqI-~2`iHKzbM-jFqa_Fn z$N*o=Iqb&|_BPb;jO@R`bnV%2&ZlMtqxHBsW4Xde$=;@!x3Lb3TiXuNdd}+HJmt?2 zNXoCHt@wkobWoFI<it~JHml*S6Y<-h@HqkqU9ip-D7A?YM8AhQv{I^K=xY^iAf z&XILYoxZ7aN_3le)#uUrLFc4IC+0b>eDs*ysbY|iS=q!h)VL#3+Bhe`Z$#l(+qgcl z8)v7kRx(GIJ;B=~M5Zh{eJ)pjoIT4fIUspRx=PPcGoP$;X65_zjxe0f9R zfz`{-3WhbBwNqbD)r|-J4z)WPHi^$$Wo*$lxCYW+*;K$iAGa515HIM+0P^?hSE+~v zkHJ)GY?v#%cO}oWh}INY$w+>#lZdbsS-NM?+H3$Sx2_*OIDq4(PZOzGhxFow7)Sw~ z%AqnRvdbndyu{GFebQeTmzx#2yJ2=bXEC9A=}{mc|~hh;eX8J;z@kgnSDLbTpR z)nBWQ(92Rkc{WOk=-b9Ik!JGi2+EGHyGFgKG*hU=m?LiSWa4pi1JvuK!8sh z)RkDCj5)4bxwV`8!oprI*slgaFe-(+Fq-~)wa%(3f>Sn*kURnBi5L>}pb){VSt z*o2>%*xe#DN{l=FwrhwJ_sprk{(gz@`w9+OVg};R!Gi!foVlL4M%ho$TpdB886AaO z>u*6_3zVBOL7X(MC~ph8qmCUgc6fZ=h9J^kb%@-TjdLm(<;i5FK0{ymxV;nj2r(D3 z1(~}Nx1t>K z`9MfC`61K>qm)eu=`DdYhp4J1pE|KMiRTs|xpvhYgLjuec2M$BEl=PwEvA`rtLSoP z!Zl;g;Dxt;vUy8b-!dz;W6X^1NIGRUUd+De?%mAlX@kuAyL7i7kAYf-W7B-XpVdQV zVZM)@%$G_O*R1;bF;Hq(?qEpqJI*_gFrJ*t^*kOk90ZOWHpc@pohijNd|d;7U?efp zBDmx6F$XVa51$j?Y4PBldt`7eciZVSmp#hzr*^HY5xKPEOuiHJR#+&wDvhWyu^87HF|iaH&hy`INNP!e=O&iXmoV z$Ifwd#?Y&{trOa!w7bvPJ-@)^qUaKh5W#qy6oI&RJwsMJ1nfUAIhS zH1l)rUO_OBg4DL~LK@B4w}f;(IarH%z9t?KWVHp*QlCk2#nEoZnLYGZyIRp-B`W9Q zcCA&+AK`235>?UYJkDY1Zb7tl<>z^yNAMXDo*ojc+&7Me6$baZ{$;5I6Y2QT#y)A* ziN$8z(HoPGTRe03Yp&usHQVO=lI@O4oSnNLnhZG!&^PR^k9n(x% z=qm@yf+-iHIKUePPZ zzm(Z$HLmm>3+SG4Zl8aUuHK8q+MNt#z;#)ixu%P{ArYz zZG7)6w|M{=mjpA7SF>3J*;^jjp|hP`hLt#^;QsoJpkv%Fc;5QV1)&ljVXJMk9Nu7x zEhK;nirgf8wyRCc@hO_}PJ?IeL6j_Wr4dnxIS*t1-U6(_*AqWIxmg2+nc2v))hlHpB7&B`5jjrI&oG3M0$b=wtx_(3_ z88Wj3CAN9j_OlwLy?RzbyW;$NS zJ_@uOf%OX?{YTP5v)|2xNSy<$fH3sRbb6H*<)Y*o9;rSX=A5|s@N#&} z*q%O%vva{nEFF1!z!z?Q2_+9dlAjB4Sz{MCM*P$AaD z0eYPHZyx;>2iH!vj~qHaDhxm?^;K}Nce}#KH2=uui&JOm0Nk0L$}mC$y8I(H*c@~9 zFBJFz`reWu481v}hq3f$yI4Bhfmj=xUtRZFa9_Umm9U%AZ&f&Pw6O=Ui_zoKauK5B zC3nS(l&0XiymyjRBF{NWA>b0x5zyVRddIEZ4a8=2-x6{@^1rx}*`B`b$0Dj@8Vlg0 zaMb5HJ3s!_y<{{(#D~VnSmCzQ_~$IJwtmeJQZk3 zBKea`2ejQqdIfsnr_bMQk>NP-AL(q*W$(H+wl?su@p3TykFLJ7AI7odVG_71+HMn; zmAfknGxE^E*|M~G8Rkn6j>?Y~jMeGS}#_UC+5aqYKjHh`7?r*%M{_>qg zI3Y@&%&6vj2i}D^4Es1%l6ZLR{CaV(FLFmjlK5qjs4yW2J?s6PFCEhvbn<1N)k|G! zHsy(x$5?Ub)XG`_jk#=ia)Do7#w)(B(cP+aYo-OA<2Hr3y)$hEN23hS@M>7z&MoY7 zsbUXPDQk&QDP;P+x&tI-w{MaED8$B?$Nfbok zVVF?@Os(uzBqr68Z)N3G&yF{=d`zZKM*O6$)3kg~x$55YFKx?-m0cQ@M4!LY?~qTy z<v4Zqv}6KXDj3*9wwg8WCHPgZ3T2gJoRs&}x#!8CP$&1FGHIo@b`lXcbW=H(YK9 z7(D%N9qNlF={%@UlmX-BESR1~S^jKnFX5Usc*{tYKXQU%;rn4W3PYH`vbd_(q7|#j zregB4?9j$n*ih6r0_u$^>*HDztfH2MA?l6s(xkOUt}Emv248ED`pD#q(qB(_i|==JVJX<6|7E4c3v3O zS&Ig{9<#JA1$3{7W;B2ns85^6CGJ;E9L#eol#&iMjkI&k5;kv}*l{?L>JX)q1QoX` zI1##GLXLpM#0UD+;cnzOesT|sd;NFV3{~Z(vlbJ>Z9_Mls67-qQ|F9Kul6s93fZv<5G^_L(*gMK`6*t)InyR*(h*-ViG;VhYWFOk8be=1P&)n7(*>rQWUaSDX!@xFt z%#=|rJJzu9tzR5D0VmVXhj6aDdUisI3h(5M_iTTB zB_Oxk31WUwxDW(j@_k2lz!&&*Uw{C`-@3tyt9ywvlOwswULG*sQWSe zX+>w$G7XL+`}Rj_@9KBGY2teT=vGpnRJ4fQ?g=WD3CUvG3TSaH5lIHi|nY}H;qK0&?T$O!SaiX@v< z$l+E=(-kVLAgrSUi*?b0)3iMyWPe5cuJJo2I_3xiBNw5!6u!eiTTz3* z4f1NbI1)rJ7Bak#MD)a2`Jx^k>MVE$M%R2w6Gvccms_&fI~MnC$f8^6K|%s?D1PyV0)v;yY4u! zjh@`>xOoUfDQMijQ+nu3C{161KyqDGGaU3s&pCz3sFS)no3#+fFqL=4vOh3^OZEW- zaN}nl5ul^5?35RGF{^y#S?esCN6J94j*y1%8AmH58Zia``PbA-S}XHImF^V;XcdLf zDOMR0WB(eJ#YdZK@OkBOCrLU$>nP-WBn1k&^q|Lf7+Ffh{#W%O*I6n!az;9#$NA40 z8vZ-jERz^O(+F0bkuF6>F8qj_H&NjZM6&I4GR#oa@5B}dx}<2+*c!)eKjDJi%+;DBCjRJVzy_Ny3OeW45_%O(7Y8E)r;prM!?)t|9RMh z@ELv>enm;X8}-jw8ku7R#w=_hiI;RNF4GCkI<8}kpqubI!-mg|?fcAl72hRysmK$4 zWMci&8Hw(cPFC?L`Q5onrJ)t@^%>NdVkK3)|5l5J#0 z97)-LZ*}B&TIF{3NLgeNMlc`J52(|&!0hJ{KuC~3qdMKw>{>q~(I_T;S7Jsjt2(YY zrfL!#!Y=P?nyCg|baN zez|S9wHA=FT!T8)2TMQP^~7BVbBGY@4Gre4%>J%gS+dN_%UEo85@J{!R5vW@3NTot zhcf&MVKw7ek7eQty8a8o%l@kT9SNB8^Ezrxk2)d5x9Rdv5TD>4gqWp^rF^j+UavKL zZ@~c0qoNd35qbbu&?@!-dO+`x4;-%CHo0Cc&);>Uk5pFQXYrGPR70-gwYiwH2$~rX6OM5@LPQS}$aqRmsQT-=cdE_+8m3MI==wXt#kcN! zqX&+v8;0g}Z$OJm=WxLZRXW6UUrE`A|kK*N#WPOFFlP0lRsM~=vU);J(bg|3l`IICs z23L#{WSslU8y1QW@3&>|EymoH8OXsGbP%FxS^B-@93}7j%>bD(v%+gh_%L9{X&&0; zY*yndH4T=%3pG*VETUgNy4jj%X6o=x4NfX_*!M41{uDFq_fCzQ2_3}cDoI*SE)6Ver?==T2kOEh*>8#(H@Iy>4cgThWu()KC$-Eq_k(Vh z&@7FeI!kx4ZI21?b`4Mtz_n}mlF^>US^#p}=#Fowv1pI;Dece87rO^4^BS_%lUc5V?&}M)G>ueL zw@2he1B4%cALQrnez+jrY3V-e`=3UKaGeJZ&k_xLQc7MN%%=|Ly|@vW)M<28WlQCb z=0-Shk8UnjGLEvyS^EYhuD%IiuUEM4@~oVNJX(0mUx?a9PiNtFa1E&ba?yx=d&}tL ziQgpH9^J>fyJYE?WK-amr9mXi6N^2X-s#Gr&KG?8p6HRr{_(jQ~aZvv})P-mzL@!5}YAv(kklieY$AnET*zr)F#YYos!lW9vySHTB)AXVuyUH+e#>_h(EShKP+6WGXdiRXqIV)Ak)g2}svx zzN;K^gGP;Gv1a{q^DWZFRv9>Ak#n5Tdt(aqo@Z+NE|RzwoWK*u6VFqpE;AjUrHaj-z=k=1a)v4*kLWBdQN@A-;D+wPm&wj%B z1?Oe{NOYTrizq}?!k`sf6orWHuc_x2nUK%7-Zd!;3c-gaE+BhO^6^GEvc&{XOSY0Mvwji%2q@WoJ-NPqNrHREtD*9OHsy8Ox@HbCU(i5#4R0Bh zG_L{Na*m3Pv4IsEpUX>O`aN&QJ*g{qy{qvYCdJC58Tn5hKo6B0$J&R??J)kT_eAbU zlG)j_a8z|K3wb%js=LB*x;Ft zmSg&MxI}|QAbo0%sDpNEc6gezXnoNha-XR<4-7-&>^z?U5-!`XydzOXV5JzbT{<9} z5@XVv8f4}Ym0_3eHit{uTfddN9VHWJL>;G8o#3zQcsj)d3^e;>&RtmHJp|k$Jghz= zf;Gh)FYAIdk9`*^MPiE#9BCqK-?{-vMdu&ots&3XY=2JO3o-*OT8&u&1VUT}6ABr` zGFyfDGo!S_D`NANWnHKGKi@o-<1Bo})$pn=wtt2+UeO}ji94B@(#%oV?Mb7ACb4H^ z$uV1Vf{Xea`*#QtL8kq73(KZIPat=>@W&0LDm!w=AgCM=agXzd3>5Mr&Sp{F)Z%~n zVt5+|=c_L2`kr<@Q0{KF@+B;!05EM>3XUG~02``;H;7E+O%~F;EMG~t-{>vaOpUp% z%H#nPcHG#%nC?w-7nvE-Y9&5}u7$DFta-LRp=4+Jd?#23~fk%exi4n z)7iZ0pj}rrXMWbm<;n~Wb9hy9f*ZnQT6kg(;Qmy-9q<-?a8pnSjoc&r7T9qSItps- zw;HA#G+y{SUXST$Mo2-u_5hk@9gq!S?ElIsX7lzLCFY>TQ}RoNK?wVUU{|KRhj(=<@miHV^XJHkokV!TE9w<)5TlT$*Ctvu; zmN@B;CzsC;uPU9H9>0@U`=_hxS!0)7LTw3#0Yn}lgqffmvXtTDzDzKV zZ%Ed$Tl|2sI!JK1pKLsa76;=Uwd_6ZSL^~-3-Eb%GP6&E(?jsQ@oZt?(5VX3#|3fZ z%}a`XZ~hO-%!aDECYpg!DUsrLwWO3E8YqR!LSi$bF>T|KMPAp{Ak`T5|A}F5)&muPOW_#R?40GMJ-{Qj)1&Otw(2 z>`uncw@mU<)5llU=CTJ2=WSxIl71|e}Y(qE|l-1@xbIPgkdJA3+OPAX3@4R_5Z>K7-jPjq3UB1&uuyc~MP6?>Gm- zZQWq>B-M7S2HDQL@JSqb7aR5~w~l}f#@;2d%a)^dxJUHSFx-xRP03XE<;{ug*_HWo z;#3(EG21C@Wc&1)lcPKJtNZw)Ik~f0Lb9akNvAh9u^M z`@{|y$C!sk11X=yHkjZE7|7*fGV^!qkSf=5@nIKOo#ckR?z$}CK7>+L4SM7YX;pj4 zY5X{V91NayY^F-;#T`e+I((R5%0#Rgr#9Psh!B}TOpc408|p4f8VFXAXjGo99pJXM zB1)%`8mQC|VxlcWbp^N88;-Q<&v55llbd?=UqV(Uu%F0?=1NwWzbRmc$=he^%1~cyC?`nL9W9Wy9QH!C z$`r0GU)-|&Fk-h|`-=qESU6X7xb?~=^;6Ro8iGM}y%X>9ig%T^Yx_hYMyKzX*Mbha z;0j*J96y+xE&_}A8gYv#llNGy&3tdsosLC8r-6FVKR7FA)F@R2DZ!Pk+|4Z)^2#h| z^p?0R@rkL(ARYCJ&R)TXLYsnccTo%(dL@DYVQ+taDF&w^Qf zwFXsLB+O_~5~SlEFY1DRq3;Nb_Oy1;@fin+_SJUN=9l4*bkq`#*0j82V?xMJ(Who# zQ?cg#KBDG#_42{H6~5XXGZfP$`~fm-BArg}+xNYlSzVp(pI+C`ZwxUO8yZ#Xpf?qFZ~+4*ZpBR!o|HoXTxDOEB~+KGB!Ta_fg{9i-{>gfjfDRBjW1QEC8ynJtnGlxHebccCKM?gUJC6l5z*Xg^+zAvMjRdpzPb73RCRM+EX`E zsL4IQz5rT$1i zQC|GUNTTh|IxT6JZ1R= z!u#6;`AvVu?%|6vA22|8T#qd*H>mhGH&`Z>cucfq&V63?8g9Cegm8LKFFwIvxQ=4X z$R54mB4?0MC`vvf!9XfHwnhge3>zFKSEyv{lXVODGm@ZbOOxY_Y$r+LHmT?!t?aiB z`fkAlP3nCw!IL0;o5TniFD2y(RaN{R=v@a!xbCnxq_w0)(;`iztPsy!9hc0m+8DUo z7~L-4Y5I&FC>sQjmHSDyo8LRkZ5(1b=|_EOGSA@qCF2Ffy|mt*{d^N;mez*^A zw9z4=pDOuWWcZ$Ss#8S30t5SJ5(LeB%{tF!(OPj^{X5^C$zGJKB@~SsLAuC__cRuZ z`XXAUVeZ2TmD5P2lTq<{58CRXIB(>@;arwaA|zkYml;FIwHh8pLip?zen}dUw1)Dz3Ky{^oI# zeXG6e#_dk=vP&DzK)Ou%aI>L%Lh9W#q0#N{W1*UqEvNie|5ScoYxIuHBi8%SpFQED zwyWp)E!~Dtlt13CdT4D7ARCnucX^B!E!C>`5Fq5Xu$t$W=Iq=GKE9l$>A$+RT_^Q1 z1_2fc$@F|Eyru}scX!k{md|bJ$n>}S!5IQHGw(o z#?pJW<>7|P$;#u3^Y+_I4US&!&zu}c=b_a?0^&Rc`KpW@Q|elGQ~2T(paZ4jzy>#I z^v(F5*>^Y^cd4$gvsWD?qd|YmQD8$_(0o8nRq`$KY$b}8HWAC}WNLxqljh^|$W7mO z)p_-6e(dXH^~v&IjAQZTD7`Aa-Os~d&i_2mY>^oRAR%rk%T_O*oGMqF)hIftj*$#t z)aRNL{$LjR0Y|59HYCV`iCaGv8dOBvpqKjP0vSU7v3CgM+lELxlBgCm)6V_y*;cmQ#9?Iy4??;sAmfN8pR=UAC< z5C=ZMuf!bRz(ahuu#xe>{qyrP{6Mw9HihO zfm^A__~`!mf_7@q(QrUN5)NkYkl?K)WPE)8d?7nm=x8M1S5gi^@Q~0gY!rNQ|9oM) zROo0lpdYD(xcQA<1k_LNr9B`4;px>DJrd#N7BA9yt=_;@hW)DvemBmb?SBf`cZFyF zDvIBYGHCms0`^>G*u7HXcf$3hBi9C~1gOTJ)iTmKdE_CVrye;u^_dxdBB`p*Cd z@Vl7?ZO5+ge!qS~dvn{M9rpdlC{Ay~=*`rBLMYAYtr)$TxWm5p_}^ed1Lw$9hQn(S ze)o?-+kZOPzWb={@Ji3=or2#j8NEp{coDzjqRzGc_v$}nhW}}*hpv9^J`&6bFp*^@ z_JnRdA+qSle8&uhBSXxk#oScGe*)*|*0YDUxJvDg7{WxFRKxGllsd0(Ve0qNw}-x+ zU8IEtdXM}T`pfqmB|>s2>-G-q=@$_zil6s!OA;Xnb162ICkG3?2FVDLLx1JglmCvG zb$#zDW$iWrlA{k*+(u`;Spi)b8$eVOGgy1BQ>UrJ_|XWN&1==Pi}lKY4fld4+3eUZ5Cff?d2-T_h; zGQa(uA>x&*VOR7b+}SI64RwWhK!q;%DdJ@Ry|6EXGeSuIPQ0cB6L3g_xtN=7|L7tw z$o-hcX-9CbSI|Isj2Ao>%p>_)Me+U@wc9pqvpiZXgFEReje@tXk1F|b3YAC=WGLxr z5vHpF3Q2xUqwuy0BT@$yCqD*Jc*hPRsl9T?5Y;_~QDpy?d>#1|?+xQ??Bi?d>u&7J zRtI%!fHu`Zx}P6S(1j-G(+80TC|eWs>A}bT+RQ}tB8n>s=_(E7P1EVtlz>NXTDB~o zPeqJxYC2o})`NgYe|pl% zb9|WXbek&jq4B3pd%dReSi`wYv!*=X5VJEs9}RQ8oPOBG=@+77gWmMZeB)aA=~HdV zm{F#(33GH=J{kc*?O{zF*G>)>SJf%VUW1^s;xzcGrj4uqcqFD=lDX`Cx8XS<;#d~3 zEW<)4Z1k$2jr<^qhfhbjxGVPk2b3B8VkmFY^kIPQ94l>1G@LyzbN|v3>BJzWJ#SO! zO|3{~3?0bR3gy5sf`dr2@9AHtD~9Ap_T^;!io3}doPfi$jk_U> z$RtwVeH|kGZ0cW|+%BVj^r3!CqyCG{VKl{i2E}_R#m2ui{`B|1^Yibh#-XQDkqKEk z2XBkG8^wssY>nO5xjHMB#-Wc=k#||TYaffa55I-$cF;^Mr z$Z%cEH+hoiL!!*qO1&Pe>2TRC`KlK3u5yN7*e$1Bw~VkWMIJIcgzARj0&-R6f zn(o?%ux_B7eoMR5RxdhEqt5NB2Gk8!K;oS-B1am~LLf2IP}C`)(oi%g`1j!BJoV6j zuTAoY(pEv5e!m}#ZdK-0E2A_sA)d-GDMtt%cy#5<9J)+0BAGzoS; z`Z!%1=Dsg2WfLV&uNFh((Cvf!SfJ)OBdCb|G`q~0!5m>oRSz1bpI@kGsuJXUM`Q6)qoy|k8oFFv3;F&S7 zPZ8SW@NDvXC_z(P12pBX*FjJLlHIeb0CC$)ttqne?suE1OS8!>_K&WRM3KRqt*_a@ zonwRD_Q9F+f**#E7I07FKl1!onl?neUkT4*?bBBz-$P6A?cz6N2OdV%kEM;*4(MlZ zCma~X3V!28$$(C%sG&Wl3-&z5Ooaxm-Zo!?n{MPL^FXz zcxHUIId3E82F6n>nfJp+DVW8^CI`+OX9klwPMl{CU;$0N=9|1Y4TQx!uO?H@>~j}- zVAeih){`qAHGp7N-mgP1v?aV(DpV%B22o| zYb51l?W9cti!E@o-aH@k(t}O;p`hkjx#767Rc0^c4h@Rao0j-{4fAe900@&28)>Su zlz3*YzK-vO=}jyA?Vnt&t&O=X{)u9gS)cyi!s>Qc(DI_li$4?7(z^2(J#Q+4kRw2*B`!5 z6O^Sr9YB3Gnir;VJwTBb`FSF)0! zf#2X`_oPcG7ny|_vb|B}LnsFo$gQF(LH9h+ZkoVeDd=4G|9$14&T4y z7bSDk8}XBk`7=F(l}L7A#_*?QB@wUs%1}cjN0_w{KEG)U{ISby_e~%shOR7iv3!C$ z?~7=8pKI{Q*4Xe;=3Ur}1FY?o*Z}$*nr$|7|8E1S?*Tg%oC3x15KeK5lifMX=2`Vz zTKC)zbtcez;Zk|L2FZEwW+r4s@UoO+>`zHpIRPBBQ_ zLKSCvx3yLi+ER9}OYyFbQ8jq}n0gjfR-Gl?zv;9ftu*rX&JDA2bGUX#yL#Uz?Ov1e zZb^IfWOa6ot6flqVSq4Xg0#q+DJ{auzHo-CEPQ*QBznDP!1EGMym?Wvz@>@IBXSf@=D;s>PACrEu7g+jiDEro+{v@ zorffG`$F+tBkZHTkRov#vIqHnI$KwvMbm^j&z=-9rO zR3(~;=aL{d*Xc1zF%Cv&&OE0ej}R>*_59-RO+tM*Ir0Z;%{UU}z=e&b{$Nw$7X5JG z+uM_(44=haz36Jrhd08TNEo70a1{~-dudC&&BDbfVC?BAed1#WcmJ(bm?-J6Tc;QQ z%rZ6KN&1Ovajx1!`ss6)K2#0+JfuGpX(@iZSsr&ct8-NJ?cb{#Q@E?;efIYNm1mij zTV)$?aT1~78nNNQMSB)nWq>d_=`-{Pzvz2~nZ~>KCNpVOa>g(#ozF&N++LrjmF8cK zs6CK|UjD4!$7y2}_qx`lQTpro9cI=$GF&#GU{D;>I4^0Zu${??s-)2T59QQ`@6_JugYxb=R+HLiP=b!Bdx~z?Sz4A{~-FbdsI=y<99~ zxZ8@dds*t;n&s)nq|VJVtO@yHnXxesr`^?Gk_rTD_(qQ;fy2E`qhJ0IG`S!&t(6Z-BNs4~Qr#4Bf@ z1)eIf9z2{oMN-@I(P~ROlhbg)deA36Ni`LeJGhEL6dm!(_O7<(}-bL&CS;9wOaSgM0<;}=B zt0j7Df^;h)$^B${&J%RCw6b?^pUGpYWv}m_$xRojp4`3ST^6a_oV;c`yLgA+P7*pg zc!!0^hSjL_n_%g)J~y+<^c1aP#YB>${Rv#oZ+YB%c6L)t3dCV*c|3C;c=db>$@+A< zbN5TU)fK_e^fxQGI;b)`po?)*x?6p!tnB3MOCpd6t8{`IqTBDYuat`8+@fH zQrOj>LF}30tf;YeOyfsm?+=@IbY)OV=ZPQW5>5TL7^%W z!j2;R;fTDg>#4DBP>W=Lmhr`_0&eVCp1}2H9Q#$uor1|CoGH*#chq zJBgZfVmbajMwlF)&!f2ugUao*5!w>NDuKXi=vb@8TD&TD>#$3*g|_5fRv7>H5Gw z{u`9k0hieE47O6{BF%OEWuES8UArHj)P9F+>iU?*(Gt{pU6+SsQ8iZ|;j!wajo4YyvH3N9AmOpgV^tgLwemH+g>3TWI+|xQ z7sZ9(RxYBJ({I#47b^8@dRPTPJVuY{1xu~tzsnw`GPkpM8Ry5&;a|h|c5z7#Y^;N; zbsdD^j#)T*hFOe=N2ii~5m(U{>tT_zzzPXW0$VXpD=tululiF%f}+*XgrBF7;;w=N z$h4i2ESaE*5zXMApXst881n}~$WEvszF>7w?J0amdtbo2pW%Dej<2)FhT8eB%DcDP z7mu|C+seU)tKTEz8dW`Al*li>jaPdIT$I6C3p3c|pw3-ft(Uqt3vWOW3h)G9ls#Dr z58*jMbIs*HR$Yg}1}5l)_zLkvUr=Y4@l!EEv&|tf78Uc}#FP6SBb${_U25N@r8tuM z5evN`$B51gxm5^7=6VPEJPZk0RS1RVdc(5c?)ibd(7KzdRNRkV8iVtgs&7RN>R6X; zTD(sWYzB5<{9B6}^(dB_C_%Z<(C?`5)v%WDfq#{n`-!>drx@JmJ1pszrxYYt7_Nh= zpUQ~dj02$i+D~1AhCM@0!jb_29GHBJi*VJ7#czKKLjrE2grTdL;N#Mm7-$Id!PLs% zxw`FNyshb8E;0hXES~8>7m?EM<)LkeZ*(;EpuCZ96J5zy_zG8cqcAxnR7y$>i0)c( z6amvts;AvSy;z*Z@Nzc>c71J!bwu(*A+1-t_88lxNe_`E4Osx6L!iN;uub z9wSw7@l0Z8>CFQjT;Wdwyb3)LWGujj{{<;@4UAQ~WFLVLs`9TsZ`Tw5fvtcTd>cS= zq$f^??SKPc3Mo_vj9u3o;=+!H3Fd|nvI!uG0miB`?owc1<9oVof(xAokksmlvtpwn z1>=JYp#o!j^@ijxdAxApIbnkFp#%xR{Te)X{ves?iQnquKNb@0pVp`He(ej<*;0O( zf)1AQCyCG-V#c0<4TcXO0iKTv)x2C^;=-pv3Z(;MxCrgiIZR}zz35DkUZ&$Q)9=&1%tmZAOYV$U3^?4pLTQqNCw+~vhag$ouAAi2^T5?~}pf#-w| z#)lA+2_T^Y#_sSB@q-JE=?yKlkM3=c1GU8XT@ys1AZbs(QFF$9ii%SQ1V24)vHFbUOD;IzFZ2O=o5ske?eP9yDFQVY%+UYMjQIaZa@;dRM|Z;e z8!`lJEsYXmzz_dp%t$`n=b&>-aAKp5LEwP@7WhYd|8S%P_NPFA4Z98xUL8t^8A$Z- z1KWUo=7$Q?cinVclgG=@U@fTR3kO|0lv-?|ON9glA_s@fLqgX}5Nxx+I8K>t7>79( zv+tU$iJs|AI-yhO*Cu62-nPaFLU5G?Z*hLU9hx#G=zDcYAXAEf-Iy^l{%;)cL9Krr z0_7QZ=NMwgNvQm4&w;f>gOPDfDx|TCjd@HdSqzHSfP1*n8X6w}((6ol)~ejEFov-N zjXE>q#bVD79Ri&Fk_v4YnkbTQ&-WS&7L7Tj49E=+W)YRi?o7g~(B_4({{>(`pTDaX zl|4|*(hK-`M(&^$C9j$vQ!(lMA!t(b0W@a z%0WGOkwk7=2B)*GnmlnQ|B@$jw;K89|7c;$ifc~*PuDxnoBQ2%cJ>TQAb=wD0++?I zxiIJad$AH1Y=jf`W;Iu=&ZQzSH`Zc2q}4Tnl$(ZiayjO~TN)rgI%DUo23ufOTVN_% zUnp;=0NpNoiLhl4Fi|;)NT)XcSQ+7H6Pap9(pepkF-(0hrc& zxYhG@Csr?8p{$5ZYP9)$TM?ODZ!^S-$gIrG5WD4I<8Cv?a&MB!%@E7I0dh(hSH`N2 z;H=l=8W+YW$0WSWNBLiilWmS85w!loh2eT^=RF`5FTlg9k=KjbxPddMU z<4?l#tF$(O$o`;0{h#|B5UpXcV069;mF$Nthfjf7rM(4)J3o%HZJ2nNm8log(n#4A z?)iv@X%9p}-1WJ_%@ZuB6w}=T^X7+Xag62s+{5)7^{4VwfK}U}WFfd5XY4WqYHgn= za>}vrKZ*f@ZFc@ShP4oYBV(Nks>4tj@TGDUD65gkU^IXNiFBTu{RX^>k3l1x;+wLI;&y(A);KbWq;~ zEp5U&iD{v^ovPXH66CL)X z#0!A0Ecb(IHNobD6m+X?Ipt>RLM%XejcSZkxWqC^?gi!{bPAPu}s7{vQ2U1uI%~lQ(9j zDwEA>Xw#wa1_9>+ZRvz}Rm?|3vw;3o>WS>K{ZM(&id$}syJ9EI zZVl8Ve=GB30?I%n1{7zxRdQb@y^bm@)RCJOUcGG!<3ekhYxKRx(2a+Fv^#B@t&VVC z#k;{2N`9z;j^fg5h{3HaNA)!>$o4H)4Ev{3*rU3O`pT_4k#1cnd=OK+CFi}95kj4Ei5-Vy2M27t7Ygh5 zHo+`cOOz|hUC6WymD)RGFs&H7oUoG|{6f3;Y(I)ee#w9W_qITVLrkuGk=)^OrlQCt zj}8^$Y`QHrjgXv3OHEIjrEoxj&^DE9f$4w=zdtM;tL8$>HWUt7&^S9;&KFpTd^gjP zM~>*0^uNkLpb4+rnxk^cJSVJt3CE5So{lty6aB94&wTu~!ZDEuKaU+ovXYNJ*Jy?B7jIX%V4&L}8`t962gY-9z1r}T0gKWvK zjcGKf=&1+)kAnS&*J~F$J}x`N=(LNV*mes?5u#45^tHS4C4#d;Uiqb3=HWlf4YZ=<4 zT!cz;qxg2741voW&#A^k1n{AC%nCcd9L9)eU342{_K1K9{!S&oi*@IIz5OJbP`tKb z#rkKkhwWgF16REM47S?tvsJc(6&%>jXRzXSu%z=93ZKF94s7)qOeZ`sC!fs^I`64_ zvk7^%HS%t2Bx?s69`g7A^`MGnrl3K(zrj9&;5aeLO&> zyh^rXq|qG=c~}BP&DR^kx`cgEAYRVC5l=KKryG5VU2Q!r?rPs?WBZTd+HVJRBJZX15Z>R1lz%7E4)<$@c81XZV|v?80rdfMpNh9zBOi6Kf}PymLnV2ay53;`1gH)nzLJ^Qs7i_fYH}Hs4fzeT&YSK)ov7qo&Xu zYKKHiOjops!hcL&-7oP}jBW>u`>3t^YzB+y@^xx~xP*`Xx*rY-_NBvaN!MJ_U1*(E z#TSpZzCcgCk$b4jQF|tS)$5YM!^XnQ`Q#^y#~yFPNn^6vGbw!HUZUDP6P+}Rd?n3| z9Hq#(F(St(IrfH@F}Bgr{P84BX@b6Gq!GHvz=lr;_Iw=j^@E3wm1GX!w{Ir)_fXAo zX#6-iVLOK1?C;+O{5ap+6Y*O*;DT#v?up3gKO&ovKVRif(pEFM)U$!6@}IPDZL;OX zHT`hIEytsTujG;89B&<`+7swmy_+TXvy!%Q;j1SF(qhwn^4%BM zLLiN!BzZ_r&h#%cOA(CgxFz+@2V)2k$cfJ6d*I~5mN=Rtg)pW3qQ8IR6Y9MuSK74j zX!)jEbS7EZjAP%oKz6M6ijNFJ7m;*}2{I%d$Ix!uDmN*9-CdIwXAA^c!k^GPlNNCd zq*=nB@J*AJb4c4OnPG1a@ucM%zT9KRG|Y*xd`N7I^wyaFVg4}BEmj+3tZ^?#fwVyq zX5+DI&^M(tNN}W8(%zo=@uu?a4dZ~{NoCJ6ksDzSw5vx>lC*&m^ppz=Q80{57gGsc z&WB8sTw`A8X51yiB{y|MNmfyiiu1kOr+N@AnWa!VEr4UhNPvCL>lTv|najIJpKK2aJ^S<53(pik;G z(vj<92Z=$n#YFOPd+3ZW1i+4!_q4TzC#k^X%O+q;dcG8hz99y~Vu#Vt_wS0oAfHAu zuj~T8(WK)fFq>D&xC9NlCs7?5q~yZ7KTci6B)W>ADfc-ns@!$efiZciqL{*yC`-mS z`M62?*$%exs*{Oryz1oPCe#B(9c|-#pY7JXiXCGJ0DPR=*94+)M{Sac3`!f@W{PH_`MV!<Y_H9ch-29ZQf+A@sYLkZO)tSf0A#0T^ zQCHptBtKGl{V7cJCv+z^;*D_fUE)-HLVV4?5vL-#S(z*Uu_f-E;PP*qaZViZrslaD z>HL|uyhh0Dhx_f#13nO3{(3WRuqAFqaf8jc%9fw6K*y_W#uc|5&(mfL98i_a;z_T3 zUcX&Rr^dZX*09w)pVEM4S^!5fYoVv7mX z7CuXrD9+}{n81?ZIJ?R+vOAizQkR2s37JqJ^jJm$h^oN5MZfPHzKuQNj|eR61_$X5}ADeE|3C40JpvsM8AJyJs2Yr=N~sg zJ09q46SVDtxkjvDt9Y%260A=6VX^msz<4l zK4Ga8PdnF9d%e*$%t#xWYg43FQ98e|3!!sMjj5gD+2`F9yuY4n5BR16L+;8mc-3OnDOStz>jZeXdt@(>;zg`Qr0M%t zV5JJ$719S=^tj@2?(wCH-ksERI{eTQCaI*~CJxv{N@AAXnpTt&F68kK_hv%h5H$SB zT%1Z#tRYcD@C>CmZF~kH zPQn>Vm8@Lp1w4}z<{7pFiL6_e>r3P3DW?tu6#K(N^jae+VMT%|k~fTnPJ*vviy8PW z%8+RI)yohVXx8W*IT+S)NgPQ1ul zjvP(xWPdDroMhKtjBf8z zC)-1LJ+Fz|90ARFi(P#V$O%uP`TX3bR`V{cIn(R)ee-wwa#p|-3K=7v(1j={m-0-! zueI4aH}_dF%t-dt;QnyVa!PqS`kTt0*b~oLRz#C)kZAAyrzIU%uaE5WgTijx5j`{*rU@ z-(m6BSpJ~kJpUAv138)@Ep{rG7ipfa%lVWo|stt%Wz1S;18pghe zEP#lyZ$@a|ICsbP8?6hJ3~0g96OM-1CLveSWE!H%mBaO%rxJ2yt%AJSP!;4&tCGA) zD#>%)nJpw#afMuiQ!S4WXD&^{3?>$7wpo#8uI61EzI_u5h+dC5@>1JG);8;%<*F<( zvtmS^YK;X|a~3_b#QC_sDgwD(J=5k^gAND!wYc649(lleMjm7JxTiZf&^C3sCmoyH zjJ?%w2xwNk3H+#oq3_)$@XW`pFs=PUE9vPOn4>9r4BlQiBuKfz7_j=l+%xHU+gsc< z;KKj@T|Q0s#yykoTXLAHzZ$#j8n-U#t<87rIn358Ek8zwvAkz`2gN)~k*alTbNSOP=6lFf=Vq`4wA#1{$*CBSZ;=n z)SD{dV0^{g5wAME#fae4fT($7JbC8>Lm#Ic_Hl}<7I$;`>`6T%Fi7v`P7tQrOy%S= zMZ~=#FzDZyJI2&Z?Q=N+C^P-%N?>(xHUJ7OrLRnc?gl_10DCpIs-K%pLtS9_kBHCd zH@~9%Pqixon$kU1^2hspQMsZ8+G07f(a$MF?`mkOaU-TF8$H9|>_J;YGd`#&JLnan zn;M#tLQ_*4EgETr=QKD|z%f_O2QMPJtf8qC&i%pxClP$9fte)Ey#vl8c&&jcQ&e0& z+2|EgIudgY%{Ty!nsahGh#qR_S9Z9M9dsMf`T=CECY!=RzrwR4{c|@78u~#h@S};~ zCk;GeXR_~{Noas2PG2>2mma?x=lJCie58Tj=+JfNJPLt_@}!}`S*`eF;C#s;qV+?< zO2%6W=c8O9x}u>izu1R6)Xnpn!Ye93D6YrOr#A6&Ingh_2d^*%1^`=OZw>F&;49!2 zo5m?!(^*vry5f&+_%0Dwg|93qSH&-(lV3vTzl2VG>09O)TDUtnr)>d6U+HwLcO{S# zDYns}qFPp0cP^*Z&XA~F?|VpTuo7%(19l`r>a!3mH1NDCW5ZHKCm74?<3#0=$0X?j z^w@u{`Y8N&sFuD<`BZg1oRfy3S7~-zT|2iGkP5}|vvxVx82Z6M0~6L1+P>wA$tC93 z&b`vZsuwZP;4}s-9=h(Wg`{MmuLG2kaO12RvmAm;8hDqnUY!Z``3})F4NZv&;v?Zs zO;v<1G&p4^i21WKIJ$a5pPHW7N4a#+B}D5BFKZy$JaRuu4dF8l{>bL*v~yI7h`!O# zto`^T<9rm9aS(A0{lcccj(dJEA1sbF_%mG$oaucAE3aveH87*Reze`O69{i<@O!p) z9Jp}2vdobNkFg8U@khiYi|~R5kFvG8>Kqn4=yScfy^qW7A6P_U)%LccqYm;>mp)|ZIZm)}!%9lFc!8=`gp<%C`1 z&PRFPMYO(1Qi6jh-njDw-uR0Zhu=|_gIG9A@G~BjMN&gkro(;IIpQelPh1arjp(R`X8!iry&JFuWI55$)Zd;thh&D6+fv&n+);mf?KtN%oPL&d z4NU#*wd0)6aFShWYsnpTx1Sph7^nNCeypjty>?Fw-oZ=#;4x>r>&zwhQ$%ZX=3SGm zY3?m-o+4bEGw-Ooy>{l38D4%Z{qpOwW#`2~E>owH&%Xf@KQM`0-cDy4y+6;Kdq%k|reEEEX=&6RL8kzMg8+?0=V0|8; zTA9!e-U6Q>_)P;-&CF-_7Q^gw~oDGllTa7v3^O@-s-0?!{2lYaTkZ{Uk7)gSG7r4#__6ozh}MrdbHFRd zZUhMzhp9fPFb{lMaStL+Z_SbHN8y7mhb*Q@=r(58ifM!utnA zzi4RYgXi4yKf&9msl{0Cs1Gh`OZS`NY}?dV6y}Auo%{bD<<~}<19``s@TIc@yT>Hf zNOugF4_^6jZ&CD4N1CI27xhU$ianoF-MctLVsQ8-Z)8s`@;-$gY!6NN^}B7jF#>a+ zq<8NQzY?anJNgp(^h@ZIFQJdWf-VUyVDf2rrR3DFVY$hA_=)CBY0R*frO&YQFJOCL zz&04RH2w@b`2u#qv6IiROOBm?hHY_d?=$RB*MX6DldA^vzS4*SrOv1L@>s{WKgZ8? z{OjkRr=a7xHasn`utnq;QMA<5V`_hxHVICMp;ePk@S_d*=A%Ep>%ohj9k*-7b@baO z==xYkb5T@*#9pfkH;NUx1k06CTZmPZ#l=9AEH2eO>Tqs3_21{f@+9DEzv-{(#+GSw zkJ1CHc-N~*(x#^NQyRF;#GN1EA3HX3XUCpLc6JEdiFBuTH9C$D)28H&bgxPqSv;kJ z#^Mp->+8*ES9W0l3XoBCquJ^DZ!~XLO4`Jua1V*SSVY=_8d7>y)jX{wQB=ASzL$)Y$wiG4!l&-=_%!vQV^FvP5F~z%R14}P%ezwtbrnB>x?m| zA+i2X2}|vkOA3|0%P-g@l+#k$C8u^v2Gn^?FQqGzLa+4nE)-&)gZkVU^=AoNp(Sif zzIv@Kiy*ES;*_|SR~=kRB<(254kzrCvKyoZ%y$&7;LV^)yPu<5ET2?bZ_a@|@Elkv zvvsbP&}PTA3pdF3rYAfMPHEwWF`v2b5qXB4atXjpYo2HD4XhmM$fD zm68yOYCXrr?6S`5Z%c<83PI$YHeXmDiNHB&+WZ-n(?|ClWWS+TBJ&(H&a`=FbqMfR z4K1DUsknuxHqNR|pIM%Br#)@j*cx0GmgJ{u;1Wi~Pb!e3v1H1opDbG{6Y0x{0?ww7*x)5hVo;I!(Tc%;}EQVt1 zIb^7=<=j^_|>=?o((em#&&x zM?#U_d7Q2<<&+n$>T+RCs`MCHzMS(BuHKJ@mG&Dpf1xW(*=;eEE;%BWu+pij%c#bU zE!%xJ=R-@g-=W4j!7~ak+3?9$@bcH-dbg2kSO3?7|LmPJqat-frOY%R8cX&K@?01WUI7ayr;TB8tNKFVwaX&aHWR$B2zg6&41wTpn z!J#yxUBX{}ECO{_?^BW)Fcs*-l0avL?tAZ(vX5a4Zp0Qh41%W^wD)C~Dej%I(No3#wF@ zAYb{>Pt^#6AEsf;FjG8j7HT&o#gDRqcU*q)72}+}Zo)r%3-0WSW78IxV>cmh+K{<5 zOsYi}MyY+GuwTCS!#4=N!SR;LhT2^Q3TTwI)>*Ie&vm5t2S35hYv>V!a@;SP-h=Iy z?`?aa!%YP@eZ_;zyuSmqlaL3pd}x?|MgI=Nr;71Ql-E1TZ=;kaj^ZITslhXfKjKlY zeIF4F+TgIvxGIsE^iO7Ij5gh)qn~K&8aC#JtL;B5CRnR#@Pn)2IKBz9Dh#%hXsMdiEM(I^ESuNq6nEGuZ&^2Y7Ccphs z|J4(BV7(4>y|X^4n{18kDvi{pg2=w5@!{L*&1YKbkF>5+^|7Wo%jFUJU0z8q{aRNB zNO+3R`xr)NC7CXq>_*8Jy8ponM`%UAT$U%#`hK#%@UXWx`S>-gi?wA z1Wl9X#EYUAw#8bx@a0>End{;1(%qDIsfZ#38wusse{nxWtVT0Ui**eH=Jl9U172f zKwm4noR9EgxmPY75JdS2xbst#k2lY(t}VDAQ+&x=B^i&0zJEtGcU!838k9E}2(!My zf-zjM^;rB!E|e{G@+kLH)khh@pB0^!5kHuo_ z7`(*rZX33@x)*=q!9QygSFK~kPpp_b?%#0yK6*tR+bimLyrO$8V*UNQf!bQV z5Qc&0aMme4wi(|>e1qeopT`OIVp^o2AKxH2Vhdu1kNG@$5QOJ?XZ?iN+ql-f4d!kf z1{=hv?m_>^^-s_RdOG}6v7vwZMZBeQB2WKi^SgM7h0f2TzkkCdJyH5ukqImO?jQRl zRB8{-r1gzWT4};e|F+F6m!{-&OBpon{>M$bF07XO?`ucC)25 z?dR8BTctI0VMd@(P-_vAMzXqGXhtwe8mi88hp1=;*F0qTzwHzICy9~|TMCysHt%dgFbJfznPjYPl$6zMmw zNhS5U$%oHhx&7RPORJTGzK0 z=x(TRRxD(7jOBB3jt8DDp7Dn9%*0n#TL!$1VPa{j-mYraXsU1m!QYWCrI7;23t60^ zZxG!y+dP$?E;(cl@e>xqUvdTHH!DrMq|4qmnKarrmj8qppDpd=0L(~D$fKYbHOR8fBWkjlrt@dqChGi2 z-I+9}>X?ziD|E(1)$yjQI!?8N({#%q+`3}pDDmA4eLIg{n7>7}%uIF}^DY|JRXUCm zztM=8thKnybHRXX&dC=beV%@k3^jn>7aS_Df;io1q*;6BX49feyMGXbKNy=ReapBA zTGN_vajNfS(vRHWQZs0J3}znZGS|?Y%fm2jjhcSZoQ0?uA(> zGkwD>s#{pxyPwLZQK@~DiE>Fg=34l48TGTkg4V$?!+vwG5K87u$2C%j)>yOY7x_zD z2?I=1z4y7AQLCa7|GWBJp7=D>b*W|==6@UpSsZCcDsig)`+=kEMqq+~Uc$ABeyqSm9;Y9@pJQp#lP0~t*`Xrpix9O` zo!RWGgN#Q^9IC!tK^1Ywja$xQN{W|VCbgD_YwQrNvVFsk5Lp}Yn1OY)AZpP3%Y?85 z{E(-fewU74xD(@shC0IQ782U&M{93xo%F-q+z^9T$7`@ck{ZkBit|8gQ*S59`LPyi zt~K?Af0-nl2#h%kOoQ^^$x(t2~wP0nlk^b-B9c=ps0&DUw!3+mx zq%U<%B_0ySe$1lKzC2oTRgH5;vWjLlY9GpHQz5xZN9CEi>Nhyn;oTP7G1cBRZ=@W< z0~@a84ObA39nrdGAvfUI*%$i(+LUy`V_Y?wgOD!$|AW1Q$Y#U;4>k|hlUMm~KUF_`V?$SHhbdA`o{)J7&)2hnJ zH4Z28RR3v$I3ewBRpXnE=r7SyX*c9bS3EE2xol|q9Xv5v6@{o$0qpCGx zV`VN_DlqnGW69c`&Puze-C6hX84`h;`W;=`H0KF5iG&~R^^;9|WuIy9zKB!aV%Td5 zSn#u`}@3#X`sD5YEkp=b-Jfmzu55< z$e7H1u3h?N=@v^%Db-LC{n0yiwOQK}AQeN&2Btik%3}jET0_-{u)Sr?+Mg7_(Y5q6 z51^@u+P9t_9Lo*zkU1|kYkyLo8&&vA#g;`z>J8P;*NHauuNg)9<51l;FEhED9>7VA z9fquTiILShd~tr)xWf;QKPyX|4dGI?p3=Q(&E_(xJq=b(;T)>kw&Ge4#bZIJuQgWP zYfl{ws49!JBLc~aBDBxa`n5-2dCf>27FIPAK$|Sytg!}q?5v0A-zYYEeBxetHV=3>CjQ3E6@CZ#3;+ zv-U7nZb|b zB}SfJSp=_k=sUrQaZ+A5bBAQgCbuJBJ}M>&7a|nt%kw(Modb0qPc5g;*F$R-T+fc! zocP9&`znUq$GGC>xcU9L_Zx^IzDEeQmnRMmc1|v1T*> zJdTbw6V0aQYts%{K|8c*X*RUlK_y4<0#6*2P*A(eH?(=rPnsVAQ_HvApZi zZPMab02$lbX92x(pt%#5p2nKmPWx%*=}2^`cSA}ZS{L5<_iq9EYY^)T3*4^|v!Tzj zjh~b624i$Tc-istH~l({aL3}Z{jaQEy_@^iPkBy(Qo9!FfU*_;a_SR6SgRz**=lJx z5d><(+9_GpDe|6`v%EBh2_9H&_!}{@IxAhKti(2yN#WL8?(Z*5)znz-p4cDi{tKHo zExu^C65)+ST9lf0`9V0pwE2nNvW42bZ(O!`OBQNV51Qn>w7E;%1vSgORkr)37Ez^5yj%E0 z_xG{_HWkAuS)`R$GFNiK^%2@yF(Rn`8y zjZPf&2xXUN(haJ(HC=KxFvk1y(4XUEviOaW?&o`kr_?>#pWouzQ*I7~RsU zsS|h;du!A0RHIA**m>T7Ruuf>1f5a;l(c8My!@o-ph1gxfQm`%s(yB=B|;+%ZCvG3 zp*zEuSr+hVsoq_?*knKjd#q;kEa-E&>$j6w%;wle7Lr_$T>1}Blw7Xaos}+Gl>%-o z(PFx@3oyf17}}NN_iu-%B3jS-!cLWsppWGRPFUhh%;mbk@)DP1U-a#p`O*CTeRpoi zpL|YaVn;xJ0LscY#aY21IKdL7qi>3VZSkg?XKZ_Q`LeOKT~SW=N1d>AjY3+JyUS-z z#2}8BG%~w$B#hyiBt|!1cabme)h<(g^sJEI*RZ3E$)x7u#cV7*lNXPK4iD632P`NQ zH#*gf%B{gQrqOzu_Jqde1}@{?<{*9N@_`+)do2;G1G|e|3zNPOD)8g*11W*Jvh}) zO@+jz>B+J8JO^JfIrru%W&owvS!?||DZ>TMPsCjpddZ&R)0&JJW5B2e-_miwj{-JDh%4)>NW zhkQkBzwz6tY88syYB|aJ<_5ynUqA zOjJessNG20hpPQo#;(CS6A!0q|HYh=D$NQhhgr^OZ#cUnDMB$L;&g_pdYXDoKr(L} znc6H`Ra6;E9I>mIGtr=?-6?0sFRZ#ZZjI*o@Gokkvb;EJoe@cBIL+0d2M>+1+%{zs1l` z#~*F=gY#XgWzQ~E^lYXO?$jaH7oM}AP_@r<$nr1v6gz}1#y&MnZxn2UZhc21l#@E~ ztg*F?_l0AY2p@ZH3?}~?8KapB8w2g==mC6U%Z#qg)r}t#%~sOvG<@p$kXg=Wl>Wng zR{9UH{T8oL=0DIw!nRFvfwRmYA||>g%#8S7)wOTLwHdOGjZ)VeLOhhwPpTOYD+V_! zKTL@Rw!S6vN_G2tWqq)f+V#++)Qq+-uh|m&?YUU44!TbtB?BDI@|Ja0sVA0 zt236#@uYTRM~TF#Pqk9@#hMX@ke}$vJ7_$TclXX}M;<`;*N+`W;;KIXE2|qVDSBWp zc~}XLLJ9jp~F@0uTV)YwRzK)nL1 zc&;A%6qxXKKaNk{qONsTY%=tAdTld9Q#15a&-67M2O9csAF}gJhHamwwA8S-={vT` z%;Li*&5{<#@2L%nI=+UJz@yssH*`dF1xWLuGZPk#1L&PADwh_OJS@s}t+{1($->Fu z9tkKTODo(vH=(O8XZK}WWK$a^QRwvt1S|f8`h;Ke@89?K<}^>69eN`je+cstWbf2< zi|#p@t+O3Ji+c4Fi?(k$GxF&>n5ynQtA!|oa86(VxDR&1S6GXM;?+43M~8w#Tdf!xrxu&#Fy><8>2TL zI!+tuIBjJ9oA{flexrsTK89U1>{^ChH0=f@+mp{E*ZeVH2uOnZ_17| zo;K#PUi9mx=&V2;^4;+F2(U{!zoL*f%j)5eY|^Ldz+P{7dthzm%5(Wvff3rat%D?= zJvXh&!6wT;XH&MMQ^U?vsGLXG){<l{mQR(jpffG#n8go|Q$K$(Taq4}BDPKcba;W}j@E zHrljZn>N}!S9&e_f$(tc2gjats!lLsvx!}8qW z0Nrpb3ZZtq<(&nL7{2J;E3v0-CkGo7bRF=p9L6;@!K|i~wn!_@lr(HZx^Iex7q@l0 zxJpR&#w|qbokx+6Dc$yO=P)BI@*W0J_Wm<`%8QSAT)Xq!3wnf>c(BFQ${CCq?>8kmGE=4t&JU*48bJKb=VUbAa)6zKn^zz-f9@FGa+>2 zFfVA7BYU__eTGj=G|RQtW;iR%Y{$>+_Q3Ir;ngNj(2N*z(X|B{E*!%VRA$9vpm7R@ zq*5SzvN2MOuI2##Viap=92$gfO99o>~0RL@0Cyy&W*ZL(b%`(26IDYR20r z{zh$0-S%pGTb7lB#qG7Rc!hxJh5OV9oh2uX#7w-rjBK84*0jht@HEgY5)z4vTUAVJCd5`6j2*juP#;6WZ=< zccD}A^x(A>x;FP>%mLV4`X(Ie>NI`rym!&ckjncMt&C(=f2JAT^S$ZET^VnevV-|I1<-L@Zr#cMe77`|?0 zXm&k@Q-g(Cny1uw)S-GoCh>Z9o7~Qw+6+il9fU>oKwW3g1>=DI#Szy@YCh7x!3B** zB;dT;rC%}PhDTwQwwXQ-?U2nKyVf>v^rUHqVrlfJ6)n3Tm<2s?ZCdx1!MpsfK&%5! zT__A`QDTOjD$CGd+MV zA*wC=$({Vm`7vEDXY7v_C=V@ghNG79%;NEpANt2`5lEEh-^A-Hl6G`h;jdN6+AIE`(Oxy{orNAOAX!S{H&0dEw;0z@>R*(#InRa zJD1Npj_L6ML=e;Rzc=lZs6y8bp*nH+JG`qiPvFKtY@0^5chi0f5%Lem%FY~?S{rU_ z5PCsarDdsG%9s}BKri|6F}-wMSrpQ1{befk+Af4nte3{{l8G-7)2hFzRi=wCHQkq( zY1YshAlvc(r5*o2I6lH-w8)0dMU@??T|;XpZXe&Mldd>EMPt*H_qt%lYeR2~Zd>0t z)-h!*Uc#n$X4V-)>ojj0KRU*7l_?Qd()f-UKN(ubZ`=6PHC7jfDbKmtXU4aN*1Xz2 zVhz_=rCJtwdBjr8_|ec>G26y(PP!$2fW}L?tS^Vmn3Suj2FkYaz%^DMdnwP=lAo&= zL+k5o8(%xdF?FJs@6z~#89y6ZpJ&_n!8JZq#?r@FJ}~1OLu=)18?T%Lc4!&Tr11+g zzBjZM$+mIIJ=)5+CBJivf9IH?wQjbJ^KLp#9k5nf8rPU{)X-Zw+txY9`qcW<@^iQN z=Z+g%|7F|w&i&l#aJS;p_<$K77egt>8>`K4ERubEW@kF%7~3g5d8Rx3 zOp^?xn5Kwz{NDZ3swVQF6BY;j3O%CtbeUSmT&cV!MOfx__t?WZ87m5H|J6{h%T@KEtJrNkGqo04j~t-x zLI;+}F66S9mTX|L_+ zv3|{7e4U(m{5a;sSKRIg#RT`t))u97c<8$3DC*R(_#?w3^+xH9$Kzn0_-8%A&T%T# z@o;O1wTa@=%NO^RF=JlC#P-j6foAUtv_=q`eYmff+-xb$MyJ}o16mx2s1b_;yRA5b zq9A+yjuTs_$nFzO?xwGR%zDV$g?^!&WQ}{*ZJi8S@Vh1whKldqRu$=18$o;pa$oj) zG*rJP`ARWP6~4n`xocj7 zH&c~>WAM6dl0mi-mh(SU$)oH6H6HmE@L84GCT^FUVr~9syS~94w@^^EOFFx5c)~mD zH_u#P^kr)0WhlHKk0p*1CkID~6KefV_9EvZ`u-Q={zXH7 zbLE*a)Ta`2u9S$;${o3}jqQZO@)r|-iSJnDscDUbxW4w;2&E2i&c=B`~{30XPT zcy#^Xo7ESnYzs7XmEOsQ!gI=HT@Q3UAr)9o-NUtuUh;V_3_|Jh>gGBI>lLqNL&0BK zGqHFz=EzDezIp=OYe#y0jf?vC@70lEj{1sbVgFhVb1l3zZ)w`o>n&{oRNqOWs7VxY zu8puc=zaxc>!e0l``ngs*4Eb6mW#AH*dVpmwWs=T2CD1X$#RY^iTqVwPgf07-O_iu zeBe5#>q}X^h#_e714bMJ$uClr7Z~hvBJ31jdAs{An%3;}k;ZA=74XrT{jM?l>HKQS zrQv^$zTk8|F()9f1L@Fz{)aaMwJmW?B{5l80*QtgLxMES3XIleEec?6u6BC$+;W`o zD(A+KHPb+^B~#x0W9s0nInDK|`RiBg=-c^$vcZg1su?~wpe8k6lhe;kE`3doxXJM; zHL3cV9CMTS!6CKDZetTWVh4NI*CrPG?CY$3W7>Sa7^)hp)lxq}8Q;JXW5|lW)5~)%SPNL&q?DqwdkLsg?DuZ*|hZp3NHAvuOi+Hg8~0Cl0J1HAz1z zz!Lh&eg`5vmC9VtzLYtN^nOv_OZ?CJ9^KAsZT+g;&IkLIooZ=^ z`<<{qUB|$#l~ySEIUeI^*Q(jC?4*--nj!2>*HLT)(07^!jLP=Ym2^y^bG)u5%twe@ z8;I!V8wel-$OW6-J_lbg9dOgcHd9ZG$&jxPx0y(^S>ee44%ep=szACvWnPm?O8ox) zm+LJ3W&REI@gR}`7mwyCo&+iTsa7MQi@*M6-q!Ip%VS|OHedbM$Bf><%63xiS}Lo? z`%H9t%1Pzsrf*X}WO?se;avIYabM?&u4cwQZ4k61;n+zNvLso)(|&>ThA4xt68C+>*~g1OO*ETB49?==Q=^Z3cT`jRnv4_xQvyuVvVRA`11#QLw}^R4)_5rPr|GZ`k05E!(hX8TLSV z2}PaIuxA;nmxtp0mVMC0Vzqb?5(N-S%r|+&V8@Dk2%zINt5DlBUe7*X>gk z0;au!V3h;zQG5JG&P;X9ZOs?!${o&baZl`t-Q*rO<(t%g*)9>Q4N0#aa8dwqK#sqW z6Tn%$877vwgxD(9qgi_Ba-fq~2(Bh#0zJAiU920FZuqih7+Z*!u}=&gcr(y}*IdNU z`%(OYxACyt31bxhqz9c|FL&YjF;ldI);9lSKG5>uYnJa|`Hq%{U$cZd$xIzr*VprM z?o-PNv%JRkI%wJbn&ksnKA`2>*DOI^CNn_G)z>URXC|{kZ(z-iP#1M^mQk>LsN-{% ztc46SV`D!&N3#XH_< zO{?JDl%`JkJ0ZgA5wS;WD@07w|K7B=aqERDSin{N%XD0MkZY#am%h6` zIM}VE0c>5RXnO@B9P^GHWN0-8!`7wiR!|tBq~1!Bhb*lhK-KIitA3 zIdd=xf81Ct1Zff_V^Mtz;2HO?ji-WQSD;=r)o;}DHmF_3Cu{d(knLM% zqwS26Zk01AsZ+^VF|=md@^Hcew)eC->k06Ie*UBzzzmbt zSxg<>fI}V zJAm~GcBOGvK3Il0L(;UOm7qyOH$|)x^5SGAS8urvov`e7hU9ARZ|DFIE`j1y;pUAGRn}R=r1iagMA--|fBQ^FaPrm%>S{2> zR9e5v2g?9wj>)N@>HdUsJsw0Axx%~(o93N-8VaMUh z8A0i@m@Rn>S~v1>&qip`v2Q&0}2^^ts>vJr;#Gd$1{YVHW?wiwDQYAA2+ zA-|$J^hh!qOy9k#)qOEcN$Z?^-b6Yhuc;hbdic7$?SEZI0EsnAWBBwS2Id`ibw;ML|`8A}7{Sur?h9>+At2 zSX29vpA06X4@0?9XwAJ^;()E358FMT2R@S18Pwzp|*vb@H zAuJyIWDCK@x8k|TLryZhPuE{C9_YrJ)Y195@q%4;-`es#cOXP13S9n#>}j940< zmwiIK+GHQU;AyLve$cxrR#fgr{%VHKm|Oc)^qLR8V4n1vufAYTOKd?r3tCY&?)zvjF%%u{W{gf~9F2dYol z{y;5YR-@(@F~0P6wi0_gJ3q$O4gAym5gMQGM0fVw&;l{9dV9lzUK4VG>^b0KXI$R_ znSwLx2`Qm@%6?&hrr!WsbuScpiP^F-TwK^s;I?DhYQ&ls_(oM#n}|%lf5*(zQIvq z>f2!oVXGWW&h1{r)AIu|z@z^F16SYCXMjU8z|2?MIezFZK=mWqJYgK*6Y?NRrTMf4 ze0t_NJTb2ORn3ONP%~|R0UDBgy3q%?{^9`le1Mn!7wu#E2UvY^0IhELCH+PFxc&hi zzBs^vKEU9M10=L>Q2V0&p&R;V1X54=Gm-tEx6ctvxKV;woBh`DEn0axHCXPTkfMc@ zyHi%DI1ksEg#8oj_?18>)s2X=t_1Nggwe34@ zea~CR&)#$rvqX(g^k6x}QtXlna~h(2j00YBdMZ>i{Bt|TbWc`MJ?@R2tMtQ;qJLDK z*Dm2T8vVPS`|U*JZeXsGhA(oX8NqoS{T$eiWLu10bldy#2nB6~hF7E!$V59kd&#xo zuo0?V?3QANlmHA#W8O}DDG098BXEj`4NK^82x;&hA7J58V~I4ev$>M%Mi~}G#>ZrS zL%G{zfTbO4m+Ms<#A0xbZTy9)|G$5PeptT)U25-dC7{B#gtxpWed7WRY>p9@05njw zy8)aC`OXhoVa*e%zHWW1&GW6dJOlFDZ42uZp-|leZB^u8_*?vikL8+k$ z&Kq;FZrfDirEh#vD<6&>sgl}L&h5W_<(<+C%1>8B7AS{BO`Ha#(&$TcgsCRFfg-)Y z*-E6sB9jTnj!E_)97VDC#BqWS(cC!1on7DO86tK{Iexb^0}mPF@VD6sN~wG^>}QrP z`niuL`fXQtV5|xWy9qNKg|6zin;9BUgWZR`umzT;#6;hXA;?PLBF`ml;Q_Menb zRnN}8=DXce;`k3?S9RY*SRmhrTet9r?NotklScvtqj%lbefr>{%hwK}&(lM(yB?}5 zm5!0rK-s#t*I%`7a{C98CuG|NdH7VGuy&8h;WY3?APGf`2X5W4n`JCPdx9fKGM)=3 z(Mae1)mCD&*g8NSwQAogksu?G8fgQSUof?+L}M;o0U3=ha$6ubJ?gDudj~l+6>?S% z_Dv%h-xlpgvc}HBY_tYDJ^7hKA#?uWl&H5FW($aB4%3-_(&RSjwSl53`PMAoI{jnp zG0dOXW@hXi=h||p_(b7WOG7JN#VUghFgKSA%5EtHvaxMiwQ`$ttW8%)0u~DAmrvAU zZ?8C|8^eW(I7|wo3Vs&VaFhu)+}`~gqtd@ID(hwXKhJT+kl%F)`rM7ZJ^g~{d(zV? zJhLxXFUU2AE~01VnGhpJ*0@c3%&?b+m5Q&0KdMRv64+G`faG^f?NjSCvo<}}ml`ds zbImtOnJKEil6*Dy)!d#lLhDt7-Dbw-yeAfh<1zk4;&Hdx?U%~<@qt<^kF>RNR}-jt zE*Qo(YVmB4^~SZXFW0(T5CQU8>k@3S%jmSxNXIex6}^C-y4cTj96H12O)a!@`{XJ@ zZKgo>i`L6yMkTqmGWDW%h$NG6=}PG~%b!$9Nr{%tfI$;w?R#$@?Ew@MWlmX4B0Q;zHEw3*{yK35qh2xp1<}HyaU{PWdQNe0E#6W;C@p- zuh#-+Ia{LGQOi@)?NK`k;!%oDhI9fD@JTvqJ*uc8wf?2YB!`ti@>%9w}#M+S-V zYD5$ivF<^f{*F#@F*F^Okuwtmwqyv%;E!%R4eB<45_J$;T}lW$6iC z$mwS6=nN%YH2@?D>PiSU7;a;bf;K1>z{0JxVaU3=UQ)_q}ddY&3K1e3j zme~HIubLN&3aAEzlc2$GHb2+}*P$D@%mRs-4eVnsm&VKlWqsP%AHmETV+@4QHT|)z zYZ@jyM!I6Cmf%p$V=7HUY%Fey75Eh~>Q+3y`i)Td$_P`kus4HVskNR)E3 zup1x9aDws`8Kdc^K4_==KI?<`ING!uOB|BznEXZCk3Dwn78KGe?5gr^eJ}CDjnT9q zl+dO%Z;x-DAeIVYwVV)E*bZYg+@|p~iu7nj095K9#eNWjtG7xV@5R2m?Kh$QNw>j# z`^R@Wg!T9KH6v%_CXTERQTIukkxq9%#y`y;W4Rf@ERb|qBXce|_UH)%SVyIIK1b~$ zrF~@OBEi$cI075uf%dhx)=7QIb@D-ddx`Fx_sAFn&luhY1s$TCZ952b3=9uI;!P9{ zql=WAXkA{e`}-pH0%hXZ#WHpy@9g@C^iggkAa--kq)i_ybna%98Hd~$JGDqyOSw49 z6TVo;$AnEU1u{yPDzY3_+!er59J&dc%X|5VL9qcFrAS5@QB|13vNK9TEvUm{*c?{m zqaYtEHcFC=(x$pgM-!J;rbs|g?b)2k$E|!YBG6-=ej}#3R<0p#VHhm78Q#sO9 zjx=TZJdY_#X`<`Mp`!s6k12ZDRDPqW{6^E2d=%IyIr@zX^s~6~S!xL|NAe;9;l`$1 z0-CIq#^V(wiBU1DBbVxSs>pI9dA*IKH$IXV;E0jc9;s?KsuQG7l{5iPV3$+tm+-+s zEEHMFn@^w1O4{Dg{29GaWzAz0{2PaljzW>UzMMWE`dB~l0s4A0miSoSW^2(_TMOt5 z7ZIMF#bbY)G5TbTfp-kQ+5z+2_yFRVDg>B3qhNz5rqouFTGxDwujf!n*~0=lRF=ox zYZpo$!w_Ze@iBag2>z%;oz^$QW<;2yD*WZcL(n1_N8bA^6aQGX)|S{-^r@deo=u|gqr8)^2tTw zOyHB>zpWQe&JKw9(&N3}F3_|nEppP^!Hx4Dg~l|rbLLG*l2Ky1_ZqDDBbOMqSVupf zvv8Vfk}0d@owDLy2TFQOKSFt&fg?Tn=)>f--jyTzgxMbtd@u9eh9U10tnBVvG5T;h z!(O1pPmc+|c?&Beu0O0QC|m~$eVqH{rz#Hk;XI@s6pYjOAzi4#lhIR2V92h7#Y4h? zyf4ULz~$8A>&Ve&;d#SVo)5dC_0;%uo8tPkJfk(ykaNnO$gccq>>p1^-oie$=i`Ww z8|fdAQ8WKOpF>hy3>V^$wipUi{GnbagB&}(X3obK${6%Q-X^{FAJMQi3^@TayCJdo z8PyUTIb-Ke`rT&lP5jZA&l?|qEP87JnzSt1JTpB#25pUDHS3|c687H?g;9`L>ozzW z`%_pOI)ybXL|zTtiZnGI>uhc{eEg=G7NZadi^!eWEg745hFffYnDz92BAf{lXC#%l zPh*jpB|i5FwTdvZY-(4N#r0;SJJu5|t&6EkxizQJxt6aw^=v`Lc>I@REOh~1T?E&D z!xL$w=e5NGj9Y2jI)|AIGxTOtT23}a%dT{t_GRjKvQ3(Gyn5&;F(}^PFUbA&Nr;Ws zh$O-Jyklz9v$nS(sndYeC%2;jk+E2rY#69en8s-wIT@uVrtOSs|8BQEC4*Qh0;!BA z9?cp)!3?OrkSegIpQGtB|E{j(%&V$y%?nGN?ueFa7K#Md&7k!{U1Azoo_|8O}lzPwV-k{utCEO%JXBMlV z@*Wnwv9)`k-`!6P&y?4k{A2f+PL|W ziJbwfVK%_-MQs(t-d4<&4FieD(Vjs0o1lb>e8KrmyL#8k^z4YQ>LHX8+k8M8d5Tz1tcTdHug zQ6QKxOW`Ov>$7a9OwC4tV8$$m2d1P(*-}}XjRL`p*%cm_v>IhgiETE@9gK2^DjN22 z*-r7AjZ%V9N;ryj8*C}M%|?L+WwSDlQdgsFDWJ_pftF>nK8`Z5M%hkDn~ef}%w}mE z<=Gl#J4tOe3N$yH)sT8>9o;Rpwb>}p?re66qdeR9<90gPY?vwxQ^jE}t*^A5RyP|3 zbnItY93^30yxYlcvr$0Mezpsvw5^+ZTm5Y|3`pD0)|iE~u9z)_xY;NmaX-u8xmHc! z9{QjmSGq1MituviyqYgQ!K-vjI%83fY|K!LO?L^Mpg|=y6owa}5i~Tv426}72OZ(| z|2)E@MpZcf7W>hj09|5oWRVqqpO1gMzxIYg?uzVfGZ{f6Y*_o^@EjbHU(p|-`QEur zZrqo33$v?ZUQ4vn4qVwcAK2FheAgg(2?m~5QS!*h5ppYgA;kHh-x_-fh-X+6e zJmh*vt7BAMof8hU%S4Y)tc0(tlz4Fo{bja3u)ZJLF%B*}ScAPjp;5%QEIDSDf#Fq&Q z3&jg|g;C2*cYceqS{xdm0W)cf9aRf>`r<1nR1@)v`jJ#WimYbsW#PkdjU`U)hEy%P zA=PP!vFu-#zB6YOGC0NyWn@z{+)y^URoPvokYj(C>lS2T#WOYuwEmh z@RoDMci*s*)tbwSH8eofE5>w3UwHSqWbI!L(`K`#iIqr}HdYxUbHkB}9RlAR(^N`@ zoy3-2m^Q-!^-~57s>7g=#cRso!)oc9N9 zKsppsqfD(D>W|!!nDU0RbB$RVChb@0fytBC9?Hj5K00iq5gF;4rf3hJ+!U>H2|Lo` z?mWmxMLq`d!P3_+U7C<*`-rhSlBKI6kIh1UriJ`$3%RFQFe=o`8y$)5j-FjL3^kI- z$UW9tK5pgXQ9f8Q{N;m;RHVW^hb|q;+6ivcNPiF59(k;Zbx8_Qkf_)b1b^1JU>b$dY_$F~j> zfNS1ish?G)F-j3zPscp9dclkXxm+c{hjn{OqVovv3pzb=83=KHU}-PZz_~+N3SrlT zafbcN%C{m#{@w2)n++GB%d&KVfBlNb@dfEZLaIHxd&Wkouu;N2bULNu8}6N}4w)9S zX8yDHV{dOBy1a+L)L(OU2n(8epQU4$H!OybD(^$4F>4BjkKZ1?c~vhDsZF(yZTMN*;_cyds~Hfv$Y` z1z0fK42#B-iYSW70cpnFD2FU}htAx0n-po7Kz+Rn4K3gjJrk>eN3G$dq;DqRI+H%v zkKRuaKmF0$+Y9w$SVD$0tlr;%#piUC@@J!*s8Q~5l-}P~XFH!!`ZRaQGA^bngx-)X zVR??1G#lmdUyf3vqr7DQ8aWSoSK^ov1jobdu*qWp|>XZ|9$L~(8c=_hU(gT1eH#1_kb1aGtiUIJ+2Be&GYKJ z+cw0C-9HzQF}Y}ZG)P}=eQt8Awo_a<)ty9y1`Zb1TZ&eNWz=6DeZAk!`*cTPGsZvC zY@-J(2XV{2e`5#cNUvq6BR_3vO`>0Xt6-=YRY*>P^K&ai0{ZvSGfS<7?gpMchp4x> zYuw=qE%s0p>U-`Pcl~l6Yg75mmm5!i+Eu~nEOushohU|CfzWE@qelO7zlG@cg9?-E z^u}$m+VQV&e2H!3uKM?>67N?=&(?Pcg~`78CHfh8Vb3uCJc7zFr$s<-yfnqaze&JL z+`p3t^pd)HHL&jo5W|6NjK4RX-{~j^oMh8R*HH`sNY@Jsk& zB5`f)JvD`Q_jTGp@8!^N^3PPGqVMbjf7@p5E1>Jhls-%zCe8`s1bct<(a^zOYg z@fh>H5%mVrU&h|N?~Bz*VpXCmL}U)pzx|`{W@LthcXfY#(hK{=lEq+=fPXR0vn7wQ z>i-|dG5wEElZfFc=A7vs9}^~hete$Cyd$}-Qr^%s%l+OXqd@rOd5!`8l{%M2V`nn& zMnr4an=a(EGaN*Qjiwkb)Q>`A+~|t=4*e)lRrB0~(8uwU3cHSsorRb-<_%Ni8y{kx znvBN+(Tg4F!>K}*;}?2cK|brB=LUxNE&Y!tW2bzbspgY*fWPXzo+{@mxl}UGWBiaS z{O1|lBrdEma>mX>C7HUtzEWc>s_yI8_topW6uiR*fs&eMY?|1ynkF6_JL%lBJw)fj zc{O!?SGg|N?}}H~+93V`8qv?HFhA{~Go~vRRMfIb>$&CU)WVXkdUABawn#}^u62=J zkR=Jn4h}*g+-;D7hCVyM2H61uzr^AtSy87GT?+8IqX~S#UgH@fLn=NRRGxyy6ykSSF zVzo&W;lyt&15(4~W157;^BboL+ok0|7H&YGA1}RD=ts!Q7N&F62fOsb&K?HSO}(FX zb9CIyVB%@FW0fg16oKeD>eGRE4S#?6!OD^RB&BDAKbaBvteB%;)iK zN+@g~6&_JJZe(?#o$E%Tv~oa1{dxns?Z6n5tZQS8D*WbaaJ*{?N)d;3E7?riuEYdm zGF5{v3%V)nwSdoJN~B+(CE8u@##&_BV=XeRO1Kt+6K8fETt6nW9A2z*=92f`q!Jqw zUlNZw7{ukoV+kD1)a&S7iDM~J{`nLK=L-_%XAW^AnCj6^fb|tz`kdD- z)o+fQ3hpQ5!Ec!${%a!ah!g%N5T`&;?qv3SFY?^(RLkwVEiZbT>J10clpjP>O)=$5 zkf*WfAZ+EjMp&FvS(^R?>ALah5V~?*B~!BE{t5PV!xF+*t`Eu5yR`J{H0*{Ygs@z1 zP&q+D5_?jY{%O{W>wbVF|G;*PDbmI+t(i6D0A5DFn1!6QD)hNK>Dn zYBx+Fs^ywcEkd2DPjI#yrV!e4P0$umPU!3ykY7ROeo5v4O1w^d3}qfx)17I{W|%)Vd@%9U6ZLzb=rTOh2AiQnkCo!6sh#pyZUuH zdczcomt2!%XJ1`cpWvl8OreU&HK};^AJxwM>jd?NDNWBrkTiN+K#i#*miW?acAco% zf}bJ$fNR}^=)7sP{$3}tKiPn)!vVS0`}i)`5mFiT-oS9F=1AJgHj9Q%CN;*0^{(A1GW?X4TW?hbBGkQ!m|5GzfX+|b}_F^*{{OP*% zQ!_1TM&^}HVl!Hm@OmjNX5cCvMID&VbOeMOD?U}}M6+jkarB{O{;n63mZUE9mx^yo zqhKW>D;^d4P)^;mOnvj~g*POB8PVFO9i1o z@@hryPW)FMHlzfaxoC`cL(v>{?(sCWpDjOo-eJD-?BN3jGnBUvO$wuZf1)ApbB1Cn zRThCG3Apr!NLsEa$~KR%eBc;uSNq-=&E2X`Tz4soQm>>abu<&f@7|2jzP9hi$f-m& zdc(?G_-?Z+rlMfVZMPv>rg#LOJ`KeX0tVeqKzOA!YpkwBxmq~NZ@B%5oC3SHCOK!<25bc;U_eINg>=W6l2o4NGx!>us*>8=d4J312wrOXZBTE!rPuwj1>j%%ee&x!IS)9rGWCn<=4thOZ-X<0;y8ZgY@>R7xObdL5AGwLXTM3l z*BokFrU+vSO&GInlT;>2fofKCEYF+ao6e1V2zE!>*AS9}`__Jfp9A>0f}cx$f800l zJci3v9HEtXw{3&d+=i&24W3CGJd<_}eyWBw9n*8x(PNC5bE;5~;TZgD0oDXq6?kI7 zsyH{S>A0STj>(!Ey*C*fL=Z+r;O0b4_5rWx1TxWf7 zs{3%I^dZXXZy45edYCOe+D%w>#x_y^c>iaLe^o@(#{XCRU==Z0)7O?YTP{*5jLXDaQ2mYxwo+ z`T-G7OR-hcW!X&6Qp+|3PtjxS`CKURxErCkU?+e-`^V!w4PhlRyBWmZ34y!7P&hJ_ zGZ>)vPm<~%Z36aGe22hD;4s@fVCY(g5(YG5>=dTltfRTFShFF05C`E2N^~oSqFdm7 zB7=(iMF*GcKN#QM)<6P!fBX>rH$^>zE_(lwfDR_=*W@SL-yU-~x%4SnxAw6L1;3?& z){QJ;A*M^Wa+AoLhYyxLMhPkOYWDW60t=b!bNlP2XMRu|ZwLQ1Y|;M3A6PQ5PW!uw zE7Nhojd^D^F^q@E0f(#FBU(~)#YMkQDzR=wk74$w|0zT7$$Le5dtL>uq@xX4jj`9n z$&yunVvPYm$&9qWlj<*yq3jT2XTa>t#pKG2$GgXtMrMY-5AHCL|4h0{qn9EzrkTS0 z(>~>&T00eS50@4EG8q2A!B1RQCCDENfvBD;N~H)+3)CiXGs zMd73-qIJKIB!&xfc6Pf3gXCj?3qkwW4Mgkr*G{4f1-Yrw6zED z?d3)@Tw`qtoVt5-Ux&ISOg8Q3=prhx|6Z^%hg;R{#_`3`y6Q4)730^i?B6)syG=Ux zg%Mc`>$bP3PzM`2gNb)H7=$H=Z9A2hSF={+rXM$vt^j+ZgasQhUyWaD>p;c*xmaU9 zI}}p+tZ5^GmqoA>z7S5XNC!;`qsjdn9%+{5W3-r(U`ZNH^kGhV%d(t z3bS9*a|J`@Sd-)g-~hn2`tpFZ&E_q1W~T^_?JpVkipXw(*J?9a7QBbq{4%`7-16*f zWQ&M*L}i6|&NNGlI!1u+yQZxvk;Oa?&E*Jt3cuyPW4mLN_O2FEBhV*-yCvAD*T^#% zbiJ0aa)c1E6`&n1AXEz`WLgN#CSc@p|}Rg-6K(2 zgh`sa6P@PHG4pJ7e~fK(p}<7R*hzi6`@Wu%|!U;COl2tz?~q7po=CT z083;aD6{!c(X+gD_XEUNzBD(t^daiYD8<)<2?~7C-v(6SM-KZ2{>x<1C)k6DsKEO4 zzv=@jG;h;-v<27hJ7sr7@gW|W=18h|Gdjk1biFL3QvO$cNOfjTXF$xEse;Q+rV5q}>Bt^1e4i$gvcW7>| ztID3ABce9Uq5N;XP3F|Q`y>stVGjGv>i5L3EkOgH<)${wVS=ho396y0VSke7)G&vc zs(O>aeX2_MNuFuL9Hy-5J*s}G0_`UWR1I^Ov#NI}8yBf}_c`im!yG29>d%yxQy0o7 zsb&pxn6;|Ep}C$ai++;N)G&u>tNIHS))Z6ACkbB-bC|cPH^`j&Dn7~lYM8^sRh^pB zdg`nABn7Nt4l`HvGc?z{Rpvg&k!zU4)K#4rfKGZOKlfC@{^IEQvVsl8(epI}tPd@2 zIexw!J=fhaB(?ch{gogTjLJJ}T4LvvB?W{U$?P4SoYpSEJv=|H?(OWJA8(x{RO{;J zCe0#-zUYCC=rS=7h_a)GhM5Rc2P)z)KHkwkXv-8l#!{9L7}#+=OAJW9F3R5b;8&v| zYcA(+eX-Y`cKA@rlxZfbDQH;ad`8cQ^APG&t22)m?wmWX9S zG;@ZCvWc?9)Z$21xygK_#W**EJd@4j1%aTd&4+ZY%{${+$9N`NEHu{D`i-H+lvLYu zO${Q4bcz%&pqnPoR9{cB{EBRU1_Bq!I;mZHkv%)BzMdBQwE3K!?Q6y^M#eaxB70TO z!aI(L*AZV$2+$>Pu#Y>;U`b3CUUu7P2dfi>^3kQ9`|5x%K3%22U>d#Q@~DD}U(oKR zesIHpq}Qi^na1%~GI#a&lz?VZS`vPHMsm^!jdn(1#2D=;T*gm%IP;^pk#Nb_t0!+0 zBaFBj#cOFrY*^ej5LK)8wY+qIFc&cIe1m`Y-As1X4~+Gh)*YJn4j0iMKlFioa==ii zsmngPy;B4LQ!eJtDHd%bBXbG&HD-0-a=1INE-tj=Oq#!Xs+JUXz4_cvP+FLWn6?J) zF423R-pj@A)t9upTCTc&`%`1E;Ai`f(pdB;jY%0?)7Z5}+QMpZDvS3pKcapW5^lp_ zn+^O@h0UoC%@$&=>idMIo#TLM+R9wtaJ;ls91RgFS}e`SBXEondLiVhUZ)stnh#nh zDIzvi+*M<76n7DiynCi8OoG)Z2^Pts^vlR8IaVWV9M|t?FFq=13bSB!%7P6J`JgIz zGsh&07E$&- zw}Hdx=8N5Te7;w?eMK=P_>GvO+$KIZe1W2tTbm$hB_4!I*0Nx2ykP$LAvV24uS9(= zwG)JP&lh-KUEDn}$s^Zr-bQb*{sF-{c`n8_HJ?(XYr{29<+&Sd{J{F0Jqt_lJ=N*@ z@F&}Rl)a@V9bB*)}MIEcz;Xjx%>9BqXPgqmR*1r2sSo4)N@1L+nDKo|{J+1MdvbBqT4gC@Y z50=R)O4U5AiImrLTb{A8eB2&F9ow&Y!t#}MV4rT#{ihAeG@6)WpG?5QG53rF!hP7V z=6Xm1!r(N-vCZ}1)sAAtI(@_u!`fg{wTH6P+;|^x+dWaLBi4}~k_0z(%In*V5W@*| zvFnzsi_y-*EqcbiBt$E1*+AbcWVtJ*fJP_z%a`cu*C@LIUm>h+`v)vENw4V!yGf2e zF4MqoEUF{%V5A88e!w)_@D{0_ot9IlGH`kbMjp2wf*qA)@_sxB6QVu7*5Zew9&TK$ z@ofE92_=6snVG1Jzh*M!%#86~(!pG^Q;xlWYaxm4BrU<-g{nj|_nmR-`C`?qdT7v;{aif8Q1vv7mOO%hJmY;~Zb)-r5WGa}pjOYH8z z-+TCbrJL&L%3`u`V}$V=ZX`NE_%^+k&y^>=rdxbY-J~ecpbfYgZ@`?iCttM5A-@PQ z95(k*AtcQ;1>4cL-1il={eJ!Wx?p9d{=feT|HscbqTyQ?6qyEJZXo2Qs$qVDobpLQ=!y%Vrh-gx?wOWv4LM3bB{+70C_c?wsTYv3iZ*J$ntIB{2)*tZ>z zZl7Lk?4EC(9#xMUr(1i+JGH~jq`hsjM_rAAbM0qs^y2 zQs<+;t*SYnCmS@4PLomUfxJ7CMt_4d(lq)`MyVUJ9hXl(Pibiy-65mY7kQ^DcfY~^ zXd1mDqgQk{svg(h;6XNx9*|MOs`Xz~r`_jSMopuaWR$ulo1QwSpC=qOjXsf4>Yr?? zzTD4~H=8zZKa3+de_d3RTNL75vp|^3s#fNXI>wg;TXS5S7(oc53;2h{+&uc5i1?@& zfztl(81WKD>M7hM+t~Hz5IrO&~eCzZ}6#7WF76pd^`1#^6coE?S zL~j;`S~wa%*DY%8l$KS%S_)-3GM*2s`JLMmQ<<`rAB}@M+3LE3!Bobcu@0~JEC+_k zCEeEpx0^{`H=X@M3-ddaynbSoblr+N*ut34V?BsEuIxnEyO>VC50_gsn=ePsTWGy` za1Fg#CnfrddY6oUrC$&D>(MYc)@X@{DpXf~#i_*EP<@#e>E}U+!HzVzZB^(}Z#BN4`=0ODh$ss82E^ zL<8?baWfIcO_&=8x^R|=9#bW>TBE=+QjLOl=*H?cy>VGE(!$O@qyG0nORI#&^*dL4 zEGkxSp5iDYi59sN<@vz*-RpKm$L&sSNLm-E&9^MSQO5jx<~kMyu}=+aUJ480vukDU z!l1W@420#WoHG>#@^OaRNP9D;mmUogkWiPi67Ixev5}ms4M#RdeL^xu zf^X`@;zD+dp|d!?Mi|;M;=SFteMmQ%LddStTCy9nDhZ$ngvcCFh(M%1nbhP~)SbXg zNvy|JMe~jXS8EuMDX|TL8YI{9=q{~6CL#BMJiU5*%tFo{fdnG6WGJHc7@9n)B)R~Y z@Analp!Nz%r3JL5xQ|F^W#)~d`$Xf-bHnnnM@Db6!J2&Y@4{?$-bVL@)Hf+`*~eqe z3#Xr&A%-3F)TK`jSH(9{C2_gz{(Uwc^W!noWHYUUJgD4mVuo^OUvI@|f1ABCO&S7s7>Z&PT)mg2CJHjgLetwz?6tnVPS5v7DT;SunZ;l*+P9=K zjE(!aY7$WHQ(iapoXqx&l3Nj0=BSCd!sjq1TjL~^x8hUYOQ6#f8lU(rVbyUH3N$ST z=Wr`wOi{MtF3-$(s78YXWn)@2bjPNP(I85gmewZ9`d~f}y;1tB7E9F&$h{oKnQWNc zbP>5C-2GKSbq16VB+IH&fi_wR*D%^Ltq701qtOSvgAM3n-?@0Tt=K2OceQWAA5poB=DU%^V%G*|EeQRg$SmrT~)o5HRKZ%dq zI^Hx3@5Dv|2G1!5Yp||JpZ+PXC;crolG)v=ZgRoAe-;m(SN9H+29LDn({r7Ur&JxW zkTGDi2hnzK1Ag}4=Ma7%0`Jw}=Lmj`7HSdI^zL1~PkCPJ1!l8th`HMkAGRUZ;Oeo?KC$t=~#|m`^PC8q{&eL`@>&Y1c00FFY5JO1m&(}AeL4Y|pc*q8!ixLA@U~J= zQ{BeHpL7s)JsRtnRE#3p)}tf^pM$*x)+(ReGkCO&(%ImvgsS5aJ-6;2ESAfoG2Q4* z5|NjaXgs;SM6ged^N%x=I2K9rc|P(YmElK_n`V9hyhm)~F@~V{;|gcADm`{62OqjvDs4%bt7WxQs#H z3aeY7Ot0R+Z+z^XF{`Idb)o+FQANS=tT&*)j3maphwH2MY9&91xsghqbX0V+SH{;~ z85b~vH`niX-@pgg1k6Udx$4D4RwOzYYwfK>SOW6Up$4b^TZq2s40a;&uCo6by^OlPyu(hTYr$klkyuQMrLz<`K$sjSAbdLc`XRn;5^MqwWo} zCS(5_cuwBkZ+dlx-8yY-1T0)s%!R+nh+!JelVR?)RShV=Njqa2&XHjP9kvxG-fuF{ zn1%~vxIm>AsRaDHWHhGXA{j0c;!+icev_BRG(1O!=g4qNo#Wr5s4)%Cli_(PWvTw0 z-(;&X4VTDpiO%tYy4-$`xW+WRM245BlT8`^O%5B=@FE#rB#-i08vac>8`JOt8D5~c zLfv`4$!ud7_Jtl_=&gpOxFJpU^7ZrZ@9XeWfFJnueK-?+I1_z1-F-MS{hZ+~`B9Ry zi_rp+W6eRJy4l3!G`N6yeo`T=dbcM#i=G0Bf+Y=6-K?K9+msA2zic*7X3dS>5C7O~ zG0m)(G!;0{c^ThLn(dJA&L_;yk=c%EHb|QFNXF+AX6MPQiT?BlNwW_QwM75A z*%Fz3FwGAC*la${Y%6KDCDXN^Jhv^=Y&U845z8E(FS6cJ4ujoFNjn086LAoLQ-h{k)H068`kKV7&XX|TC1oI!MDdOCcNd)8UGf$Y*ln`jk8H z*D&f*ISLWW3)y8J*HwniD93;KfGAb?sL>EqZc4IOCxGS4TU#vW;L%+rgx>;!lK|FA zSEoxAenW5Yk{OgpNfCvr^5u<7wdFtJX%ha{@7gVVZ1?PU?Up{a`}n(d7e2Q8I@#`n zvMbW;&V6k6JUuJH`3S5#*%NXS$mf9pXKEV3Kl-F1D1zTv5j4IDFu8PV79jl*P_y!P z9GF+aeum8{Bum7?B&Wcw;?#IzN#Qfj#-yy8r9q1OTBVjc-uURan zFx>nd*CMLfU`x|Q^i6!Pe2Wy${NH}I)2l=}Hq$>$XYe9EI2nEl%w~gM7CAYi_~pR@ zhSjAniL#l$k&Zx)Oa?ujJ#S&jc92HJp-R_56VpA|5vW_m1nUcgE@X{lYb)ZycQ|(z zr#AEqn#lHtgL^A}Es@&uH$QzDJ=KY=kFF(xs3s(Lgnz8~keX(hkc%?R(uAb4;f$5& zCc{u1>$^mot zRRsj}E!{35JbglV4!FE?1~wIk2 z%2ZFK)R)+e$BxcqDM(z5#;V5`)nqZqCw!+*_|74>qnshYwt9NQa$!=;%u_Fk9g4i8 zp&O4}iG8?Rq}EgmfjP}Ok!A&$A?AsP}p0=Y90BP_@#HzRm8AAQ_W#C_2Lc`(j@Hyy-I_l(^AQWXMZJ3usU2f(Uba+)yPbp4|1oA3ol9HJmEhjS_g1>Qy;Nd zkaZ3SzF@t!qOSrya#*-mDtQ}DPO2AqJAMYFYNhBQj0I1DyMi3vap6RsjH_4di7PE4 zd+E-dkuLTL<=Hs1>@+%ty9CBcf5xlM`;eb`25igUw_>d>uDa<~K zRjyq}dzYT}_(6NT0C+%$zux>&5!Z%CQP2`rdwO296l?hu<|Poh?nF&Er>HR6gm11{ zgr9l%0iUu7@1l8aIsrw^^oGu+R2XGOS)54}Hylwsho2+(d4(TD5FEl!4Spc9FP@pE zDf#NpQOEklWbL<{vj~B6z5qdaKIfF+7KRXV2}8)DQ$)xmtmpHlWy-$#oP8}-PWLSS z{|B7!i&Qv5EcrsiW$GupwY7C3_wQ5nnz-ul?!6t4?3|jL zY$ztg{Fq##ZYA+eU=3|GXRk5o3WB`wmO?B{GV~9lKglJ@d`DU~DfIs&T`3JOrX(!G z@J}*}((pn`ZZY)!B+G2PV>8bjx_^=omWFdFDd8~qPZ=KkN#ar(&Znd!v>WFvYm3rrG`6NtoFoaFBY<*@C7?B!u{;Y5v_7Xt^ z@i~zL$)p8D3c&-5=q^sw!ZabbVJD?O7~*mI=AKic zCW1z?g_lKSuz5vf^V*KU1TN{npDn>|amQjo^bw{_{Au_$arX&kVB4`*XbTEvB6y79 zzF(XsL_3gd9Qbr>4{M|u9Kdp(NR~K+-dx3Ni$er# zBPolELRohUi2+XWBGhiJBHkfPYauBT3gwu4+@MVOlKZzj)3=Xiwk|vy|2n>Wp-)J1 z){-x3ktH#ZrkN19_^4^vc}(Hh=P6;MT3wxoFsqpV8o@1jb!2U$V1ob6|NhPWj+gy* z$HJO1wul)k{8`l1b;2aA82N5YJBagD~R8 z`_T3LXsRCuEio8)eutUaNKMfZ>oxk5(5V*T=`V=E=yz+ORAMOMmmForvyug;X`~&( zhY3n#c_e4ANY)JgQwb#(4gDdWaH^OA4 zSAI{r-HRB9t!cVbCI`-w$XVO>oDvaIAUJ)&Px=gBT3y8H0a!hH3?BemqBOqp)&AU!KtLuYB(M_wF1=EY%jaDNxw%VH6e z)d6cJB0eTEd1pfEx(_dj5`&WveOOF(dPs(`J$l~3=cl9R&-nag^!&{*N?rHi6QMV+ zxQXQwnHw8>SUWhb9ySioxr87Vcg)w3qfsNyFfx7NE!iar;}$mwpXII0NxnfUCMSp0 zjjf}t<8vu;ayvSr(Ae7C*=n5DF3$IkcamnW$t=kk3uY5J3#`o9dG%D968G`w(cb3f z;g+nTwBzCN2Ay!9tP;!S0)NPjo|O^pj_A5g(QOHAq84o*)^pc)yKe)`PWr9xu+0Z# z9IHnCM!92i7m|JYjnKN-w`9vC`}pv9=ZS{y9vLy7x7kyFJd!X4dQu(V+VU!uEYrn# zD`(IO!+^NFwvB}dhi;8U4u{?^4a2$gih`RJV>N~DzEM>u!CS(1 z$=sOXO!Hda+psBh&%OHts@4wT-u+$)HVN>e^9xkC9VE(a;ZnjX*rb1Zxc)h-MwKC4 z9Cvx@g077dEH$DEtOB^(%G=fZQ$)5wox|Tk)74!_>j721?iX~Q$B+gRW}v7E!&KCg zCyct7>dbM*dXl4=ie#jN)6d)+YnY7i?Q+hED*yYxQQ9`-MQOUaVl8uVzL6zz5 z;92-=6&#w$;AVu6W;0GS!(@m0dGP#6NR1H<-xoAV(x@W<=u%<4oIF$no82 zSdGa7MC2RX*?zp@e2S91ZCLk|`ji3o;&>pPeK&hI@s+WbcTeVTqcyd`uTwT{JeWqrR$tm_vtKf~do~(;{+JeuZ5bDw)Z?es;dWqDOH~3S#cjuCeGm|wFOt9Bq2grs8Iy5KDk6Z_A{wNzqb)9R zn2ANNJ3(<%wS;-3K*=p(q3j3qIP4&{LY8j!(~9%51txZYRr4Ce!m#+>eP6~NB#cl9 z1c4#a*RSh%@12xjj8onpUz;iC7)J(JruSC_0T&<)_%;d!zH5qPNS|?U zHq(2}v1RVHiLGo082H)n_2)2SjLR|o5`~^!=I3axTF2y)c~$rR+@e`3@H&$$ZtE6N z*@b?ILI*AOl*Rn`dwe#KRG7=~vX7ODYL!~tpc)?qul(wm)J2(I&~3gYY_>nN*`8#x zt=oJtB@�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�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